@rvoh/dream 2.25.0 → 2.27.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/dist/cjs/src/Dream.js +10 -18
- package/dist/cjs/src/cli/index.js +31 -5
- package/dist/cjs/src/db/migration-helpers/DreamMigrationHelpers.js +127 -6
- package/dist/cjs/src/dream/DreamClassTransactionBuilder.js +0 -18
- package/dist/cjs/src/dream/Query.js +0 -23
- package/dist/cjs/src/dream/QueryDriver/Kysely.js +8 -8
- package/dist/cjs/src/dream/internal/printSerializerHierarchyLevel.js +4 -3
- package/dist/cjs/src/dream/internal/resolveSerializerAssociationEdges.js +14 -1
- package/dist/cjs/src/errors/DropEnumValueRetypeFailed.js +208 -0
- package/dist/cjs/src/errors/serializers/MissingSerializersDefinition.js +16 -2
- package/dist/cjs/src/errors/serializers/MissingSerializersDefinitionForKey.js +5 -2
- package/dist/cjs/src/errors/serializers/NoGlobalSerializerForSpecifiedKey.js +5 -3
- package/dist/cjs/src/errors/serializers/NoSerializersResolvedForKey.js +5 -2
- package/dist/cjs/src/errors/serializers/NonDreamSerializerDerivedFromGlobalSerializerForSpecifiedKey.js +5 -3
- package/dist/cjs/src/errors/serializers/serializerResolutionContextMessage.js +46 -0
- package/dist/cjs/src/helpers/cli/generateFactoryContent.js +7 -1
- package/dist/cjs/src/helpers/cli/generateMigrationContent.js +80 -13
- package/dist/cjs/src/helpers/cli/syncWithFailureContext.js +41 -0
- package/dist/cjs/src/serializer/SerializerRenderer.js +32 -4
- package/dist/cjs/src/serializer/helpers/inferSerializerFromDreamOrViewModel.js +28 -10
- package/dist/cjs/src/serializer/helpers/serializerForAssociatedClass.js +6 -1
- package/dist/cjs/src/serializer/helpers/serializerGlobalName.js +16 -0
- package/dist/esm/src/Dream.js +10 -18
- package/dist/esm/src/cli/index.js +31 -5
- package/dist/esm/src/db/migration-helpers/DreamMigrationHelpers.js +127 -6
- package/dist/esm/src/dream/DreamClassTransactionBuilder.js +0 -18
- package/dist/esm/src/dream/Query.js +0 -23
- package/dist/esm/src/dream/QueryDriver/Kysely.js +8 -8
- package/dist/esm/src/dream/internal/printSerializerHierarchyLevel.js +4 -3
- package/dist/esm/src/dream/internal/resolveSerializerAssociationEdges.js +14 -1
- package/dist/esm/src/errors/DropEnumValueRetypeFailed.js +208 -0
- package/dist/esm/src/errors/serializers/MissingSerializersDefinition.js +16 -2
- package/dist/esm/src/errors/serializers/MissingSerializersDefinitionForKey.js +5 -2
- package/dist/esm/src/errors/serializers/NoGlobalSerializerForSpecifiedKey.js +5 -3
- package/dist/esm/src/errors/serializers/NoSerializersResolvedForKey.js +5 -2
- package/dist/esm/src/errors/serializers/NonDreamSerializerDerivedFromGlobalSerializerForSpecifiedKey.js +5 -3
- package/dist/esm/src/errors/serializers/serializerResolutionContextMessage.js +46 -0
- package/dist/esm/src/helpers/cli/generateFactoryContent.js +7 -1
- package/dist/esm/src/helpers/cli/generateMigrationContent.js +80 -13
- package/dist/esm/src/helpers/cli/syncWithFailureContext.js +41 -0
- package/dist/esm/src/serializer/SerializerRenderer.js +32 -4
- package/dist/esm/src/serializer/helpers/inferSerializerFromDreamOrViewModel.js +28 -10
- package/dist/esm/src/serializer/helpers/serializerForAssociatedClass.js +6 -1
- package/dist/esm/src/serializer/helpers/serializerGlobalName.js +16 -0
- package/dist/types/src/Dream.d.ts +10 -3
- package/dist/types/src/cli/index.d.ts +1 -1
- package/dist/types/src/dream/DreamClassTransactionBuilder.d.ts +3 -2
- package/dist/types/src/dream/Query.d.ts +3 -2
- package/dist/types/src/errors/DropEnumValueRetypeFailed.d.ts +89 -0
- package/dist/types/src/errors/serializers/MissingSerializersDefinition.d.ts +3 -1
- package/dist/types/src/errors/serializers/MissingSerializersDefinitionForKey.d.ts +3 -1
- package/dist/types/src/errors/serializers/NoGlobalSerializerForSpecifiedKey.d.ts +3 -1
- package/dist/types/src/errors/serializers/NoSerializersResolvedForKey.d.ts +3 -1
- package/dist/types/src/errors/serializers/NonDreamSerializerDerivedFromGlobalSerializerForSpecifiedKey.d.ts +3 -1
- package/dist/types/src/errors/serializers/serializerResolutionContextMessage.d.ts +20 -0
- package/dist/types/src/helpers/cli/syncWithFailureContext.d.ts +58 -0
- package/dist/types/src/serializer/helpers/inferSerializerFromDreamOrViewModel.d.ts +11 -4
- package/dist/types/src/serializer/helpers/serializerGlobalName.d.ts +15 -0
- package/dist/types/src/types/associations/shared.d.ts +1 -1
- package/dist/types/src/types/dream.d.ts +3 -0
- package/dist/types/src/types/query.d.ts +1 -1
- package/dist/types/src/types/serializer.d.ts +38 -0
- package/dist/types/src/types/types/associations/shared.ts +8 -6
- package/dist/types/src/types/types/dream.ts +6 -0
- package/dist/types/src/types/types/query.ts +1 -1
- package/dist/types/src/types/types/serializer.ts +40 -0
- package/docs/assets/search.js +1 -1
- package/docs/classes/db.DreamMigrationHelpers.html +11 -11
- package/docs/classes/db.KyselyQueryDriver.html +40 -40
- package/docs/classes/db.PostgresQueryDriver.html +41 -41
- package/docs/classes/db.QueryDriverBase.html +39 -39
- 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.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 +19 -19
- package/docs/classes/index.Dream.html +132 -138
- package/docs/classes/index.DreamApp.html +10 -10
- package/docs/classes/index.DreamTransaction.html +2 -2
- package/docs/classes/index.Env.html +2 -2
- package/docs/classes/index.Query.html +93 -80
- package/docs/classes/system.CliFileWriter.html +4 -4
- package/docs/classes/system.DreamBin.html +2 -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 +11 -11
- package/docs/classes/system.ObjectSerializerBuilder.html +8 -8
- package/docs/classes/system.PathHelpers.html +3 -3
- package/docs/classes/utils.Encrypt.html +3 -3
- 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/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.DreamAppInitOptions.html +2 -2
- package/docs/interfaces/types.DreamAppOpts.html +2 -2
- package/docs/interfaces/types.DreamDbConfig.html +5 -5
- package/docs/interfaces/types.DurationObject.html +2 -2
- package/docs/interfaces/types.EncryptOptions.html +2 -2
- 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/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.OpenapiSchemaCommonFields.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +2 -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 +2 -2
- package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAnyOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAnyOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectOneOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectOneOfShorthand.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 +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +2 -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
|
@@ -9,33 +9,51 @@ import expandStiClasses from '../../helpers/sti/expandStiClasses.js';
|
|
|
9
9
|
import uniq from '../../helpers/uniq.js';
|
|
10
10
|
import isDreamSerializer from './isDreamSerializer.js';
|
|
11
11
|
export const DEFAULT_SERIALIZER_KEY = 'default';
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* `resolutionContext` says how resolution reached `obj` — the `rendersOne`/`rendersMany` edge that
|
|
14
|
+
* led here, and the STI base it was expanded from. It is threaded down purely so that the errors
|
|
15
|
+
* below can name the route instead of only the class, and it is optional so that a caller with no
|
|
16
|
+
* route to describe (and every existing caller outside this repo) is unaffected. See
|
|
17
|
+
* `SerializerResolutionContext`.
|
|
18
|
+
*/
|
|
19
|
+
export default function inferSerializerFromDreamOrViewModel(obj, serializerKey = DEFAULT_SERIALIZER_KEY, resolutionContext) {
|
|
13
20
|
if (!obj)
|
|
14
21
|
return null;
|
|
15
22
|
const serializers = obj.serializers;
|
|
16
23
|
if (!serializers)
|
|
17
|
-
throw new MissingSerializersDefinition(obj);
|
|
24
|
+
throw new MissingSerializersDefinition(obj, serializerKey, resolutionContext);
|
|
18
25
|
const serializerOrGlobalName = serializers[serializerKey];
|
|
19
26
|
if (!serializerOrGlobalName)
|
|
20
|
-
throw new MissingSerializersDefinitionForKey(obj, serializerKey);
|
|
27
|
+
throw new MissingSerializersDefinitionForKey(obj, serializerKey, resolutionContext);
|
|
21
28
|
if (isDreamSerializer(serializerOrGlobalName))
|
|
22
29
|
return serializerOrGlobalName;
|
|
23
30
|
const globalName = serializerOrGlobalName;
|
|
24
31
|
const dreamApp = DreamApp.getOrFail();
|
|
25
32
|
const serializer = dreamApp.serializers[globalName];
|
|
26
33
|
if (!serializer)
|
|
27
|
-
throw new NoGlobalSerializerForSpecifiedKey(obj, serializerKey, globalName);
|
|
34
|
+
throw new NoGlobalSerializerForSpecifiedKey(obj, serializerKey, globalName, resolutionContext);
|
|
28
35
|
if (!isDreamSerializer(serializer))
|
|
29
|
-
throw new NonDreamSerializerDerivedFromGlobalSerializerForSpecifiedKey(obj, serializerKey, globalName, serializer);
|
|
36
|
+
throw new NonDreamSerializerDerivedFromGlobalSerializerForSpecifiedKey(obj, serializerKey, globalName, serializer, resolutionContext);
|
|
30
37
|
return serializer;
|
|
31
38
|
}
|
|
32
|
-
export function inferSerializersFromDreamClassOrViewModelClass(classDef, serializerKey = undefined) {
|
|
39
|
+
export function inferSerializersFromDreamClassOrViewModelClass(classDef, serializerKey = undefined, resolutionContext) {
|
|
33
40
|
if (!classDef)
|
|
34
41
|
return [];
|
|
35
42
|
const classes = expandStiClasses(classDef);
|
|
36
|
-
|
|
43
|
+
// This is the one place that knows *whether* the class an error ends up naming came from STI
|
|
44
|
+
// expansion: below this line the class the caller passed is gone, replaced by one child per
|
|
45
|
+
// iteration, and every downstream error names a child. Identity rather than `isSTIBase` because
|
|
46
|
+
// `classDef` may be a ViewModelClass, which `expandStiClasses` hands straight back.
|
|
47
|
+
const expandedFromStiBase = classes.length !== 1 || classes[0] !== classDef;
|
|
48
|
+
const childResolutionContext = expandedFromStiBase
|
|
49
|
+
? { ...resolutionContext, stiBase: stiBaseName(classDef) }
|
|
50
|
+
: resolutionContext;
|
|
51
|
+
const serializers = classes.map(classDef => inferSerializerFromDreamOrViewModel(classDef.prototype, serializerKey, childResolutionContext));
|
|
37
52
|
return uniq(compact(serializers));
|
|
38
53
|
}
|
|
54
|
+
function stiBaseName(classDef) {
|
|
55
|
+
return classDef.sanitizedName ?? classDef.name;
|
|
56
|
+
}
|
|
39
57
|
/**
|
|
40
58
|
* As `inferSerializersFromDreamClassOrViewModelClass`, but guarantees a non-empty result.
|
|
41
59
|
*
|
|
@@ -51,9 +69,9 @@ export function inferSerializersFromDreamClassOrViewModelClass(classDef, seriali
|
|
|
51
69
|
* preload-path building and serialization display — so that they raise one typed error between
|
|
52
70
|
* them.
|
|
53
71
|
*/
|
|
54
|
-
export function inferSerializersFromDreamClassOrViewModelClassOrFail(classDef, serializerKey) {
|
|
55
|
-
const serializers = inferSerializersFromDreamClassOrViewModelClass(classDef, serializerKey);
|
|
72
|
+
export function inferSerializersFromDreamClassOrViewModelClassOrFail(classDef, serializerKey, resolutionContext) {
|
|
73
|
+
const serializers = inferSerializersFromDreamClassOrViewModelClass(classDef, serializerKey, resolutionContext);
|
|
56
74
|
if (serializers.length === 0)
|
|
57
|
-
throw new NoSerializersResolvedForKey(classDef, serializerKey);
|
|
75
|
+
throw new NoSerializersResolvedForKey(classDef, serializerKey, resolutionContext);
|
|
58
76
|
return serializers;
|
|
59
77
|
}
|
|
@@ -28,5 +28,10 @@ export function serializerForAssociatedClass(object, associationName, options) {
|
|
|
28
28
|
// would flatten. Unioning every child's serializer here — which is what buildSerializerPreloadPaths
|
|
29
29
|
// does for preload paths — is meaningless: exactly one set of keys can be flattened into the
|
|
30
30
|
// payload.
|
|
31
|
-
return inferSerializersFromDreamClassOrViewModelClass(associatedClass, options.serializerKey
|
|
31
|
+
return (inferSerializersFromDreamClassOrViewModelClass(associatedClass, options.serializerKey, {
|
|
32
|
+
// Reached only from SerializerRenderer's rendersOne branch, so the edge type is fixed. No
|
|
33
|
+
// declaring serializer: the renderer holds the rendered attribute, not the serializer that
|
|
34
|
+
// declared it.
|
|
35
|
+
edge: { type: 'rendersOne', associationName },
|
|
36
|
+
})[0] ?? null);
|
|
32
37
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @internal
|
|
3
|
+
*
|
|
4
|
+
* The single reader of a serializer's `globalName`.
|
|
5
|
+
*
|
|
6
|
+
* Serializers are plain functions; `globalName` is stamped onto them by `importSerializers` when the
|
|
7
|
+
* app loads them, so the property is not on any serializer type and every reader would otherwise
|
|
8
|
+
* have to invent its own cast — three different spellings of it existed before this helper.
|
|
9
|
+
*
|
|
10
|
+
* `undefined` is a legitimate answer: a serializer built inline (a spec, or a `{ serializer }`
|
|
11
|
+
* option) is never stamped. Callers decide what that means for them — the resolution-context message
|
|
12
|
+
* simply drops the declarer.
|
|
13
|
+
*/
|
|
14
|
+
export default function serializerGlobalName(serializer) {
|
|
15
|
+
return serializer.globalName;
|
|
16
|
+
}
|
package/dist/esm/src/Dream.js
CHANGED
|
@@ -49,6 +49,7 @@ import cloneDeepSafe from './helpers/cloneDeepSafe.js';
|
|
|
49
49
|
import isJsonColumn from './helpers/db/types/isJsonColumn.js';
|
|
50
50
|
import notEqual from './helpers/notEqual.js';
|
|
51
51
|
import { inferSerializersFromDreamClassOrViewModelClassOrFail } from './serializer/helpers/inferSerializerFromDreamOrViewModel.js';
|
|
52
|
+
import serializerGlobalName from './serializer/helpers/serializerGlobalName.js';
|
|
52
53
|
export default class Dream {
|
|
53
54
|
DB;
|
|
54
55
|
/**
|
|
@@ -580,8 +581,10 @@ export default class Dream {
|
|
|
580
581
|
// One line per serializer: for an STI base the displayed tree is the union across its children,
|
|
581
582
|
// so naming a single child's serializer under the base's own name would misattribute the tree.
|
|
582
583
|
serializers.forEach(serializer => {
|
|
584
|
+
// Interpolated rather than defaulted so the printed line is unchanged for a serializer with no
|
|
585
|
+
// globalName; naming inline serializers in this tree is its own change.
|
|
583
586
|
// eslint-disable-next-line no-console
|
|
584
|
-
console.log(yoctocolors.gray(serializer
|
|
587
|
+
console.log(yoctocolors.gray(`${serializerGlobalName(serializer)}`));
|
|
585
588
|
});
|
|
586
589
|
return this.recursiveSerializationMap(serializers, {
|
|
587
590
|
forDisplay: true,
|
|
@@ -667,6 +670,12 @@ export default class Dream {
|
|
|
667
670
|
* * foreign key fields for belongs to associations (these should usually be verified before being set)
|
|
668
671
|
* * type fields corresponding to polymorphic associations
|
|
669
672
|
*
|
|
673
|
+
* @internal
|
|
674
|
+
*
|
|
675
|
+
* Not part of the public API. Psychic reaches it through the bracketed
|
|
676
|
+
* back-door (`dreamClass['paramSafeColumnsOrFallback']()`), the same escape
|
|
677
|
+
* hatch used for other Dream internals such as `virtualAttributes`.
|
|
678
|
+
*
|
|
670
679
|
* @returns A subset of columns for the given dream class
|
|
671
680
|
*/
|
|
672
681
|
static paramSafeColumnsOrFallback() {
|
|
@@ -1778,23 +1787,6 @@ export default class Dream {
|
|
|
1778
1787
|
static async pluck(...columnNames) {
|
|
1779
1788
|
return (await this.query().pluck(...columnNames));
|
|
1780
1789
|
}
|
|
1781
|
-
/**
|
|
1782
|
-
* Plucks the specified fields from the given dream class table
|
|
1783
|
-
* in batches, passing each found columns into the
|
|
1784
|
-
* provided callback function
|
|
1785
|
-
*
|
|
1786
|
-
* ```ts
|
|
1787
|
-
* await User.order('id').pluckEach('id', (id) => {
|
|
1788
|
-
* console.log(id)
|
|
1789
|
-
* })
|
|
1790
|
-
* // 1
|
|
1791
|
-
* // 2
|
|
1792
|
-
* // 3
|
|
1793
|
-
* ```
|
|
1794
|
-
*
|
|
1795
|
-
* @param args - a list of fields to pluck, followed by a callback function to call for each set of found fields
|
|
1796
|
-
* @returns void
|
|
1797
|
-
*/
|
|
1798
1790
|
static async pluckEach(...args) {
|
|
1799
1791
|
return await this.query().pluckEach(...args);
|
|
1800
1792
|
}
|
|
@@ -5,6 +5,7 @@ import DreamApp from '../dream-app/index.js';
|
|
|
5
5
|
import Encrypt from '../encrypt/index.js';
|
|
6
6
|
import SspawnRequiresDevelopmentOrTest from '../errors/SspawnRequiresDevelopmentOrTest.js';
|
|
7
7
|
import generateDream from '../helpers/cli/generateDream.js';
|
|
8
|
+
import syncWithFailureContext from '../helpers/cli/syncWithFailureContext.js';
|
|
8
9
|
import EnvInternal from '../helpers/EnvInternal.js';
|
|
9
10
|
import loadRepl from '../helpers/loadRepl.js';
|
|
10
11
|
import DreamCliLogger from './logger/DreamCliLogger.js';
|
|
@@ -129,7 +130,9 @@ ${INDENT} create-many-to-many-posts-users # WRONG: selects 'many_posts_
|
|
|
129
130
|
${INDENT} # -to- inside "many-to-many" matches: with no columns
|
|
130
131
|
${INDENT} # this errors; with columns it silently scaffolds it
|
|
131
132
|
${INDENT} create-posts-users-join-table # correct: no marker match
|
|
132
|
-
${INDENT} #
|
|
133
|
+
${INDENT} # with no columns, generates empty up/down methods to
|
|
134
|
+
${INDENT} # write by hand; with columns, scaffolds
|
|
135
|
+
${INDENT} # alterTable('<table-name>') to hand-edit`;
|
|
133
136
|
export default class DreamCLI {
|
|
134
137
|
/**
|
|
135
138
|
* Starts the Dream console
|
|
@@ -184,7 +187,9 @@ ${INDENT} # Remove columns (include -from-<table_name>; columns are described w
|
|
|
184
187
|
${INDENT} # their types, same shorthand as -to-, so the rollback (down) can recreate them)
|
|
185
188
|
${INDENT} pnpm psy g:migration remove-legacy-fields-from-posts legacy_status:string
|
|
186
189
|
${INDENT}
|
|
187
|
-
${INDENT} # General schema change (no -to-/-from- marker —
|
|
190
|
+
${INDENT} # General schema change (no -to-/-from- marker — with no columns, generates
|
|
191
|
+
${INDENT} # empty up/down methods to write by hand; with columns, scaffolds
|
|
192
|
+
${INDENT} # alterTable('<table-name>') to hand-edit)
|
|
188
193
|
${INDENT} pnpm psy g:migration create-unique-index-on-invitations`)
|
|
189
194
|
.argument('<migrationName>', migrationNameArgumentDescription)
|
|
190
195
|
.option('--connection-name <connectionName>', 'the database connection to use for this migration. Only needed for multi-database setups; defaults to "default"')
|
|
@@ -345,7 +350,11 @@ ${INDENT} pnpm psy db:migrate`)
|
|
|
345
350
|
await initializeDreamApp({ bypassDreamIntegrityChecks: true });
|
|
346
351
|
await DreamBin.dbMigrate();
|
|
347
352
|
if (EnvInternal.isTest && !skipSync) {
|
|
348
|
-
await
|
|
353
|
+
await syncWithFailureContext({
|
|
354
|
+
commandName: 'db:migrate',
|
|
355
|
+
completedWork: 'Every pending migration ran successfully',
|
|
356
|
+
recoveryAdvice: 'run `sync` on its own, or re-run `db:migrate` — the migrations it already applied are not re-applied, so it goes straight back to the sync',
|
|
357
|
+
}, async () => await DreamBin.sync(onSync));
|
|
349
358
|
}
|
|
350
359
|
process.exit();
|
|
351
360
|
});
|
|
@@ -362,7 +371,13 @@ ${INDENT} pnpm psy db:rollback --steps=3 # rolls back the last 3 migrations`
|
|
|
362
371
|
await initializeDreamApp({ bypassDreamIntegrityChecks: true });
|
|
363
372
|
await DreamBin.dbRollback({ steps });
|
|
364
373
|
if (EnvInternal.isTest && !skipSync) {
|
|
365
|
-
await
|
|
374
|
+
await syncWithFailureContext({
|
|
375
|
+
commandName: 'db:rollback',
|
|
376
|
+
completedWork: 'The rollback completed successfully and is already committed',
|
|
377
|
+
// never `re-run db:rollback` here: the rollback is already
|
|
378
|
+
// committed, so a second run rolls back a further migration
|
|
379
|
+
recoveryAdvice: 'run `sync` on its own. Do not re-run `db:rollback` — the rollback it would perform is a further one, undoing a migration you did not ask to undo',
|
|
380
|
+
}, async () => await DreamBin.sync(onSync));
|
|
366
381
|
}
|
|
367
382
|
process.exit();
|
|
368
383
|
});
|
|
@@ -409,7 +424,18 @@ ${INDENT}Warning: all existing data will be lost. The seed file (db/seed.ts) wil
|
|
|
409
424
|
logPrefix: ' ',
|
|
410
425
|
logPrefixColor: 'green',
|
|
411
426
|
});
|
|
412
|
-
await
|
|
427
|
+
await syncWithFailureContext({
|
|
428
|
+
commandName: 'db:reset',
|
|
429
|
+
completedWork: 'db:drop, db:create and db:migrate all completed successfully',
|
|
430
|
+
didNotRun: 'db:seed, which db:reset runs after the sync, did not run',
|
|
431
|
+
// the two routes are not interchangeable, so the second one names
|
|
432
|
+
// the env var it needs: db:reset seeds by calling the app's seed
|
|
433
|
+
// hook directly (below), while the `db:seed` *command* refuses to
|
|
434
|
+
// seed under NODE_ENV=test unless DREAM_SEED_DB_IN_TEST=1 — and
|
|
435
|
+
// NODE_ENV=test is the only environment DreamBin.sync does any
|
|
436
|
+
// work in, so it is the only environment this advice is printed in
|
|
437
|
+
recoveryAdvice: 're-run `db:reset`, which seeds directly and so picks up the skipped seeding step, or run `sync` on its own followed by `DREAM_SEED_DB_IN_TEST=1 db:seed` — this sync only runs under `NODE_ENV=test`, and a bare `db:seed` does not seed there',
|
|
438
|
+
}, async () => await DreamBin.sync(onSync));
|
|
413
439
|
DreamCLI.logger.log(arrows, { logPrefix: ' ' });
|
|
414
440
|
DreamCLI.logger.log(colorize('db:seed', { color: 'green' }), {
|
|
415
441
|
logPrefix: ' ',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { sql } from 'kysely';
|
|
2
2
|
import InternalEncrypt from '../../encrypt/InternalEncrypt.js';
|
|
3
|
+
import DropEnumValueRetypeFailed, { quotedRelationName, } from '../../errors/DropEnumValueRetypeFailed.js';
|
|
3
4
|
export default class DreamMigrationHelpers {
|
|
4
5
|
/**
|
|
5
6
|
* Rename a table and its associated primary key index and sequence.
|
|
@@ -169,14 +170,29 @@ export default class DreamMigrationHelpers {
|
|
|
169
170
|
* @param __namedParameters.replacements - Details about which table and column to change and which value to replace the dropped value with (or remove it if the column is an array)
|
|
170
171
|
*/
|
|
171
172
|
static async dropEnumValue(db, { enumName, value, replacements }) {
|
|
173
|
+
// Read the check constraints on every replacement column up front, before anything is
|
|
174
|
+
// retyped. They are only ever *used* from the catch blocks below, but they cannot be read
|
|
175
|
+
// from there: dropEnumValue runs inside a migration transaction (runMigration.ts routes any
|
|
176
|
+
// migration mentioning it through `newTransaction`), and once a statement in a transaction
|
|
177
|
+
// block errors, Postgres aborts the block and fails every subsequent statement with
|
|
178
|
+
// `25P02 current transaction is aborted`. A pg_constraint SELECT issued from a catch block
|
|
179
|
+
// would therefore throw itself and bury the very error we are trying to explain.
|
|
180
|
+
const checkConstraintsByPosition = await checkConstraintsOnColumns(db, replacements);
|
|
172
181
|
// temporarily set all table columns depending on this enum to an acceptable alternate type
|
|
173
|
-
for (const tableAndColumnToChange of replacements) {
|
|
182
|
+
for (const [position, tableAndColumnToChange] of replacements.entries()) {
|
|
174
183
|
const tableAndColumnToChangeAsArray = tableAndColumnToChange;
|
|
175
184
|
const isArray = tableAndColumnToChangeAsArray.array || false;
|
|
176
|
-
await db.schema
|
|
185
|
+
await retypeOrExplain(() => db.schema
|
|
177
186
|
.alterTable(tableAndColumnToChange.table)
|
|
178
187
|
.alterColumn(tableAndColumnToChange.column, col => col.setDataType(computedTemporaryType(isArray)))
|
|
179
|
-
.execute()
|
|
188
|
+
.execute(), {
|
|
189
|
+
enumName,
|
|
190
|
+
value,
|
|
191
|
+
table: tableAndColumnToChange.table,
|
|
192
|
+
column: tableAndColumnToChange.column,
|
|
193
|
+
retypeDirection: 'toText',
|
|
194
|
+
...constraintContextFor(checkConstraintsByPosition, position),
|
|
195
|
+
});
|
|
180
196
|
}
|
|
181
197
|
// collect enum values before dropping type
|
|
182
198
|
const allEnumValues = await getEnumValues(db, enumName);
|
|
@@ -187,15 +203,23 @@ export default class DreamMigrationHelpers {
|
|
|
187
203
|
.createType(enumName)
|
|
188
204
|
.asEnum(allEnumValues.filter(val => val !== value))
|
|
189
205
|
.execute();
|
|
190
|
-
for (const tableAndColumnToChange of replacements) {
|
|
206
|
+
for (const [position, tableAndColumnToChange] of replacements.entries()) {
|
|
191
207
|
const isArray = tableAndColumnToChange.array || false;
|
|
208
|
+
const retypeContext = {
|
|
209
|
+
enumName,
|
|
210
|
+
value,
|
|
211
|
+
table: tableAndColumnToChange.table,
|
|
212
|
+
column: tableAndColumnToChange.column,
|
|
213
|
+
retypeDirection: 'toEnum',
|
|
214
|
+
...constraintContextFor(checkConstraintsByPosition, position),
|
|
215
|
+
};
|
|
192
216
|
if (isArray) {
|
|
193
217
|
await replaceArrayValues(db, value, tableAndColumnToChange);
|
|
194
|
-
await updateTableColumnToNewEnumArrayType(db, enumName, tableAndColumnToChange);
|
|
218
|
+
await retypeOrExplain(() => updateTableColumnToNewEnumArrayType(db, enumName, tableAndColumnToChange), retypeContext);
|
|
195
219
|
}
|
|
196
220
|
else {
|
|
197
221
|
await replaceNonArrayValues(db, value, tableAndColumnToChange);
|
|
198
|
-
await updateTableColumnToNewEnumType(db, enumName, tableAndColumnToChange);
|
|
222
|
+
await retypeOrExplain(() => updateTableColumnToNewEnumType(db, enumName, tableAndColumnToChange), retypeContext);
|
|
199
223
|
}
|
|
200
224
|
}
|
|
201
225
|
}
|
|
@@ -330,6 +354,103 @@ export default class DreamMigrationHelpers {
|
|
|
330
354
|
}
|
|
331
355
|
}
|
|
332
356
|
}
|
|
357
|
+
/**
|
|
358
|
+
* Run one of dropEnumValue's internal column retypes, and on failure re-raise it as a
|
|
359
|
+
* {@link DropEnumValueRetypeFailed} naming the table, the column and the check constraints
|
|
360
|
+
* that were read before any retype ran. The original error is kept reachable underneath.
|
|
361
|
+
*
|
|
362
|
+
* A caught throwable is normalized to an `Error` rather than cast to one: the error's
|
|
363
|
+
* message getter dereferences `originalError.message`, so a driver (or a mock) that rejects
|
|
364
|
+
* with a non-Error would otherwise blow up inside the getter and destroy the very
|
|
365
|
+
* diagnostic this wrapping exists to produce.
|
|
366
|
+
*/
|
|
367
|
+
async function retypeOrExplain(retype, context) {
|
|
368
|
+
try {
|
|
369
|
+
await retype();
|
|
370
|
+
}
|
|
371
|
+
catch (error) {
|
|
372
|
+
throw new DropEnumValueRetypeFailed({
|
|
373
|
+
...context,
|
|
374
|
+
originalError: error instanceof Error ? error : new Error(String(error)),
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* The check constraints defined on each replacement's column, indexed by the replacement's
|
|
380
|
+
* position in `replacements` (both of dropEnumValue's loops walk the same array in order, so
|
|
381
|
+
* the position identifies a replacement exactly and no key string has to be kept in sync).
|
|
382
|
+
*
|
|
383
|
+
* Position, rather than the replacement object, because the same object may legitimately
|
|
384
|
+
* appear at two positions — `const r = {...}; replacements: [r, r]` — and a Map keyed on the
|
|
385
|
+
* object would collapse those two entries onto one, reporting each column's constraints twice
|
|
386
|
+
* and emitting a duplicated dropConstraint line for every one of them.
|
|
387
|
+
*
|
|
388
|
+
* One catalog query covers every replacement: the (relation, column) pairs are passed as two
|
|
389
|
+
* arrays and unnested `WITH ORDINALITY`, so each row carries the 1-based position of the
|
|
390
|
+
* replacement it belongs to. Issuing one query per replacement would cost a round trip per
|
|
391
|
+
* column on every successful `dropEnumValue`, and Kysely serializes them all on the
|
|
392
|
+
* migration's single connection.
|
|
393
|
+
*
|
|
394
|
+
* `relationResolved` is selected separately from the constraints, because "the table has no
|
|
395
|
+
* check constraints" and "the table name did not resolve" both produce zero constraint rows
|
|
396
|
+
* and the error message must not confuse the two.
|
|
397
|
+
*
|
|
398
|
+
* `to_regclass` rather than a `::regclass` cast so that an unknown table yields no rows
|
|
399
|
+
* instead of throwing, leaving the retype itself to report a missing table exactly as it
|
|
400
|
+
* does today. The name handed to it is quoted per segment, because Kysely quotes the
|
|
401
|
+
* identifiers it emits (`alterTable('MixedCase')` -> `"MixedCase"`) while an unquoted
|
|
402
|
+
* `to_regclass` argument case-folds and would silently miss every non-lowercase table.
|
|
403
|
+
*
|
|
404
|
+
* The selected aliases are deliberately single words: a `CamelCasePlugin` on the connection
|
|
405
|
+
* rewrites result keys, and `ord`/`resolved`/`name`/`definition` survive that rewrite
|
|
406
|
+
* unchanged.
|
|
407
|
+
*/
|
|
408
|
+
async function checkConstraintsOnColumns(db, replacements) {
|
|
409
|
+
const byPosition = replacements.map(() => ({
|
|
410
|
+
relationResolved: false,
|
|
411
|
+
checkConstraints: [],
|
|
412
|
+
}));
|
|
413
|
+
if (replacements.length === 0)
|
|
414
|
+
return byPosition;
|
|
415
|
+
const relationNames = replacements.map(replacement => quotedRelationName(replacement.table));
|
|
416
|
+
const columnNames = replacements.map(replacement => replacement.column);
|
|
417
|
+
const response = await sql `
|
|
418
|
+
SELECT
|
|
419
|
+
t.ord::int AS ord,
|
|
420
|
+
(to_regclass(t.relname) IS NOT NULL) AS resolved,
|
|
421
|
+
con.name AS name,
|
|
422
|
+
con.definition AS definition
|
|
423
|
+
FROM unnest(${relationNames}::text[], ${columnNames}::text[])
|
|
424
|
+
WITH ORDINALITY AS t(relname, colname, ord)
|
|
425
|
+
LEFT JOIN LATERAL (
|
|
426
|
+
SELECT c.conname AS name, pg_get_constraintdef(c.oid) AS definition
|
|
427
|
+
FROM pg_constraint c
|
|
428
|
+
JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = ANY(c.conkey)
|
|
429
|
+
WHERE c.contype = 'c'
|
|
430
|
+
AND c.conrelid = to_regclass(t.relname)
|
|
431
|
+
AND a.attname = t.colname
|
|
432
|
+
) con ON true
|
|
433
|
+
ORDER BY t.ord, con.name
|
|
434
|
+
`.execute(db);
|
|
435
|
+
for (const row of response.rows) {
|
|
436
|
+
const entry = byPosition[Number(row.ord) - 1];
|
|
437
|
+
if (entry === undefined)
|
|
438
|
+
continue;
|
|
439
|
+
entry.relationResolved = row.resolved;
|
|
440
|
+
if (row.name !== null && row.definition !== null) {
|
|
441
|
+
entry.checkConstraints.push({ name: row.name, definition: row.definition });
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return byPosition;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* The constraint metadata for the replacement at `position`. A position with no entry (which
|
|
448
|
+
* cannot happen today, but would be a silent lie if it did) reads as "not read", never as
|
|
449
|
+
* "none defined".
|
|
450
|
+
*/
|
|
451
|
+
function constraintContextFor(byPosition, position) {
|
|
452
|
+
return byPosition[position] ?? { relationResolved: false, checkConstraints: [] };
|
|
453
|
+
}
|
|
333
454
|
async function getEnumValues(db, enumName) {
|
|
334
455
|
const response = await sql `SELECT unnest(enum_range(NULL::${sql.raw(enumName)}))`.execute(db);
|
|
335
456
|
return response.rows.map(row => row.unnest);
|
|
@@ -747,24 +747,6 @@ export default class DreamClassTransactionBuilder {
|
|
|
747
747
|
async pluck(...columnNames) {
|
|
748
748
|
return (await this.queryInstance().pluck(...columnNames));
|
|
749
749
|
}
|
|
750
|
-
/**
|
|
751
|
-
* Plucks the specified fields from the given dream class table
|
|
752
|
-
* in batches, passing each found columns into the provided callback function.
|
|
753
|
-
*
|
|
754
|
-
* ```ts
|
|
755
|
-
* await ApplicationModel.transaction(async txn => {
|
|
756
|
-
* await User.txn(txn).order('id').pluckEach('id', (id) => {
|
|
757
|
-
* console.log(id)
|
|
758
|
-
* })
|
|
759
|
-
* // 1
|
|
760
|
-
* // 2
|
|
761
|
-
* // 3
|
|
762
|
-
* })
|
|
763
|
-
* ```
|
|
764
|
-
*
|
|
765
|
-
* @param fields - A list of fields to pluck, followed by a callback function to call for each set of found fields
|
|
766
|
-
* @returns void
|
|
767
|
-
*/
|
|
768
750
|
async pluckEach(...args) {
|
|
769
751
|
await this.queryInstance().pluckEach(...args);
|
|
770
752
|
}
|
|
@@ -1338,29 +1338,6 @@ export default class Query {
|
|
|
1338
1338
|
const vals = await this.dbDriverInstance().pluck(...columnNames);
|
|
1339
1339
|
return (columnNames.length > 1 ? vals : vals.flat());
|
|
1340
1340
|
}
|
|
1341
|
-
/**
|
|
1342
|
-
* Plucks the specified column names from the given dream class table
|
|
1343
|
-
* in batches, passing each found columns into the
|
|
1344
|
-
* provided callback function
|
|
1345
|
-
*
|
|
1346
|
-
* ```ts
|
|
1347
|
-
* await User.order('id').pluckEach('id', (id) => {
|
|
1348
|
-
* console.log(id)
|
|
1349
|
-
* })
|
|
1350
|
-
* // 1
|
|
1351
|
-
* // 2
|
|
1352
|
-
* // 3
|
|
1353
|
-
* ```
|
|
1354
|
-
*
|
|
1355
|
-
* A `limit` or `offset` on the Query cannot compose with the batch windows
|
|
1356
|
-
* — each window replaces the limit with the batch size and the offset with
|
|
1357
|
-
* the running window offset, silently ignoring both — so a limit- or
|
|
1358
|
-
* offset-carrying Query throws at runtime.
|
|
1359
|
-
*
|
|
1360
|
-
* @param args - a list of column names to pluck, followed by a callback function to call for each set of found fields
|
|
1361
|
-
* @returns void
|
|
1362
|
-
* @throws BatchingIncompatibleWithLimitOrOffset if the query carries a `limit` or `offset`
|
|
1363
|
-
*/
|
|
1364
1341
|
async pluckEach(...args) {
|
|
1365
1342
|
const providedCbIndex = args.findIndex(v => typeof v === 'function');
|
|
1366
1343
|
const providedCb = args[providedCbIndex];
|
|
@@ -457,7 +457,7 @@ export default class KyselyQueryDriver extends QueryDriverBase {
|
|
|
457
457
|
bypassSelectAll: true,
|
|
458
458
|
bypassOrder: true,
|
|
459
459
|
});
|
|
460
|
-
kyselyQuery = kyselyQuery.select(max(columnName));
|
|
460
|
+
kyselyQuery = kyselyQuery.select(max(this.namespaceColumn(columnName)));
|
|
461
461
|
const data = await executeDatabaseQuery(kyselyQuery, 'executeTakeFirstOrThrow');
|
|
462
462
|
return data.max;
|
|
463
463
|
}
|
|
@@ -477,7 +477,7 @@ export default class KyselyQueryDriver extends QueryDriverBase {
|
|
|
477
477
|
async maxBy(groupColumn, aggregatedColumn) {
|
|
478
478
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
479
479
|
const { max } = this.dbFor('select').fn;
|
|
480
|
-
return this.groupedAggregate(groupColumn, max(aggregatedColumn));
|
|
480
|
+
return this.groupedAggregate(groupColumn, max(this.namespaceColumn(aggregatedColumn)));
|
|
481
481
|
}
|
|
482
482
|
/**
|
|
483
483
|
* Retrieves the min value of the specified column
|
|
@@ -498,7 +498,7 @@ export default class KyselyQueryDriver extends QueryDriverBase {
|
|
|
498
498
|
bypassSelectAll: true,
|
|
499
499
|
bypassOrder: true,
|
|
500
500
|
});
|
|
501
|
-
kyselyQuery = kyselyQuery.select(min(columnName));
|
|
501
|
+
kyselyQuery = kyselyQuery.select(min(this.namespaceColumn(columnName)));
|
|
502
502
|
const data = await executeDatabaseQuery(kyselyQuery, 'executeTakeFirstOrThrow');
|
|
503
503
|
return data.min;
|
|
504
504
|
}
|
|
@@ -518,7 +518,7 @@ export default class KyselyQueryDriver extends QueryDriverBase {
|
|
|
518
518
|
async minBy(groupColumn, aggregatedColumn) {
|
|
519
519
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
520
520
|
const { min } = this.dbFor('select').fn;
|
|
521
|
-
return this.groupedAggregate(groupColumn, min(aggregatedColumn));
|
|
521
|
+
return this.groupedAggregate(groupColumn, min(this.namespaceColumn(aggregatedColumn)));
|
|
522
522
|
}
|
|
523
523
|
/**
|
|
524
524
|
* Retrieves the sum value of the specified column
|
|
@@ -539,7 +539,7 @@ export default class KyselyQueryDriver extends QueryDriverBase {
|
|
|
539
539
|
bypassSelectAll: true,
|
|
540
540
|
bypassOrder: true,
|
|
541
541
|
});
|
|
542
|
-
kyselyQuery = kyselyQuery.select(sum(columnName));
|
|
542
|
+
kyselyQuery = kyselyQuery.select(sum(this.namespaceColumn(columnName)));
|
|
543
543
|
const data = await executeDatabaseQuery(kyselyQuery, 'executeTakeFirstOrThrow');
|
|
544
544
|
return data.sum === null ? null : parseFloat(data.sum);
|
|
545
545
|
}
|
|
@@ -559,7 +559,7 @@ export default class KyselyQueryDriver extends QueryDriverBase {
|
|
|
559
559
|
async sumBy(groupColumn, aggregatedColumn) {
|
|
560
560
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
561
561
|
const { sum } = this.dbFor('select').fn;
|
|
562
|
-
return this.groupedAggregate(groupColumn, sum(aggregatedColumn), rawSum => rawSum === null ? null : parseFloat(rawSum));
|
|
562
|
+
return this.groupedAggregate(groupColumn, sum(this.namespaceColumn(aggregatedColumn)), rawSum => rawSum === null ? null : parseFloat(rawSum));
|
|
563
563
|
}
|
|
564
564
|
/**
|
|
565
565
|
* Retrieves the average value of the specified column
|
|
@@ -580,7 +580,7 @@ export default class KyselyQueryDriver extends QueryDriverBase {
|
|
|
580
580
|
bypassSelectAll: true,
|
|
581
581
|
bypassOrder: true,
|
|
582
582
|
});
|
|
583
|
-
kyselyQuery = kyselyQuery.select(avg(columnName));
|
|
583
|
+
kyselyQuery = kyselyQuery.select(avg(this.namespaceColumn(columnName)));
|
|
584
584
|
const data = await executeDatabaseQuery(kyselyQuery, 'executeTakeFirstOrThrow');
|
|
585
585
|
return data.avg;
|
|
586
586
|
}
|
|
@@ -600,7 +600,7 @@ export default class KyselyQueryDriver extends QueryDriverBase {
|
|
|
600
600
|
async avgBy(groupColumn, aggregatedColumn) {
|
|
601
601
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
602
602
|
const { avg } = this.dbFor('select').fn;
|
|
603
|
-
return this.groupedAggregate(groupColumn, avg(aggregatedColumn));
|
|
603
|
+
return this.groupedAggregate(groupColumn, avg(this.namespaceColumn(aggregatedColumn)));
|
|
604
604
|
}
|
|
605
605
|
/**
|
|
606
606
|
* Retrieves the number of records in the database
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import yoctocolors from 'yoctocolors';
|
|
2
2
|
import { indent } from '../../helpers/indent.js';
|
|
3
|
+
import serializerGlobalName from '../../serializer/helpers/serializerGlobalName.js';
|
|
3
4
|
/**
|
|
4
5
|
* @internal
|
|
5
6
|
*
|
|
@@ -30,10 +31,10 @@ export default function printSerializerHierarchyLevel({ targetSerializers, forDi
|
|
|
30
31
|
// eslint-disable-next-line no-console
|
|
31
32
|
console.log(indentation + `${prefix}${type} ${yoctocolors.cyan(serializerAssociationName)}`);
|
|
32
33
|
serializers.forEach(serializer => {
|
|
34
|
+
// Interpolated rather than defaulted so the printed line is unchanged for a serializer with no
|
|
35
|
+
// globalName; naming inline serializers in this tree is its own change.
|
|
33
36
|
// eslint-disable-next-line no-console
|
|
34
|
-
console.log(yoctocolors.gray(indentation +
|
|
35
|
-
indent(prefix.length, { tabWidth: 1 }) +
|
|
36
|
-
serializer.globalName));
|
|
37
|
+
console.log(yoctocolors.gray(indentation + indent(prefix.length, { tabWidth: 1 }) + `${serializerGlobalName(serializer)}`));
|
|
37
38
|
});
|
|
38
39
|
});
|
|
39
40
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import compact from '../../helpers/compact.js';
|
|
2
2
|
import { inferSerializersFromDreamClassOrViewModelClass } from '../../serializer/helpers/inferSerializerFromDreamOrViewModel.js';
|
|
3
|
+
import serializerGlobalName from '../../serializer/helpers/serializerGlobalName.js';
|
|
3
4
|
/**
|
|
4
5
|
* Keyed on the two arguments. Weak on both, so an entry is collectable once either the Dream class
|
|
5
6
|
* or the serializer is. The cached arrays are handed out by reference and must be treated as
|
|
@@ -102,7 +103,19 @@ function buildSerializerAssociationEdges(dreamClass, serializer) {
|
|
|
102
103
|
// nothing wrong with it.
|
|
103
104
|
const serializers = serializerAssociation.options.serializer
|
|
104
105
|
? compact([serializerAssociation.options.serializer])
|
|
105
|
-
: compact(inferSerializersFromDreamClassOrViewModelClass(associatedClass, serializerAssociation.options.serializerKey
|
|
106
|
+
: compact(inferSerializersFromDreamClassOrViewModelClass(associatedClass, serializerAssociation.options.serializerKey,
|
|
107
|
+
// Error-only, and derived entirely from this function's two arguments, so nothing
|
|
108
|
+
// route-dependent is introduced: it never enters the memo key in
|
|
109
|
+
// `resolveSerializerAssociationEdges` and is never stored in the returned edges.
|
|
110
|
+
{
|
|
111
|
+
edge: {
|
|
112
|
+
type: serializerAssociation.type,
|
|
113
|
+
associationName: serializerAssociationName,
|
|
114
|
+
// Undefined for a serializer built inline, which is never stamped with a
|
|
115
|
+
// globalName — the message simply drops the declarer.
|
|
116
|
+
declaredBy: serializerGlobalName(serializer),
|
|
117
|
+
},
|
|
118
|
+
}));
|
|
106
119
|
return serializers.map(associatedSerializer => ({
|
|
107
120
|
dreamClass: associatedClass,
|
|
108
121
|
serializer: associatedSerializer,
|