@rvoh/dream 2.5.8 → 2.6.1-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/src/Dream.js +12 -56
- package/dist/cjs/src/db/migration-helpers/DreamMigrationHelpers.js +15 -11
- package/dist/cjs/src/dream/LoadBuilder.js +55 -0
- package/dist/cjs/src/dream/Query.js +4 -5
- package/dist/cjs/src/dream/constants.js +2 -0
- package/dist/cjs/src/dream/internal/buildAssociationPaths.js +33 -0
- package/dist/cjs/src/dream/internal/buildDependentDestroyPreloadPaths.js +43 -0
- package/dist/cjs/src/dream/internal/buildSerializerPreloadPaths.js +38 -0
- package/dist/cjs/src/dream/internal/destroyAssociatedRecords.js +14 -6
- package/dist/cjs/src/dream/internal/destroyDream.js +7 -1
- package/dist/cjs/src/dream/internal/loadDependentDestroyTree.js +35 -0
- package/dist/cjs/src/dream/internal/resolveSerializerAssociationEdges.js +53 -0
- package/dist/cjs/src/serializer/SerializerRenderer.js +1 -1
- package/dist/cjs/src/serializer/builders/DreamSerializerBuilder.js +4 -0
- package/dist/cjs/src/types/recursiveSerialization.js +1 -0
- package/dist/esm/src/Dream.js +12 -56
- package/dist/esm/src/db/migration-helpers/DreamMigrationHelpers.js +15 -11
- package/dist/esm/src/dream/LoadBuilder.js +55 -0
- package/dist/esm/src/dream/Query.js +4 -5
- package/dist/esm/src/dream/constants.js +2 -0
- package/dist/esm/src/dream/internal/buildAssociationPaths.js +33 -0
- package/dist/esm/src/dream/internal/buildDependentDestroyPreloadPaths.js +43 -0
- package/dist/esm/src/dream/internal/buildSerializerPreloadPaths.js +38 -0
- package/dist/esm/src/dream/internal/destroyAssociatedRecords.js +14 -6
- package/dist/esm/src/dream/internal/destroyDream.js +7 -1
- package/dist/esm/src/dream/internal/loadDependentDestroyTree.js +35 -0
- package/dist/esm/src/dream/internal/resolveSerializerAssociationEdges.js +53 -0
- package/dist/esm/src/serializer/SerializerRenderer.js +1 -1
- package/dist/esm/src/serializer/builders/DreamSerializerBuilder.js +4 -0
- package/dist/esm/src/types/recursiveSerialization.js +1 -0
- package/dist/types/src/db/migration-helpers/DreamMigrationHelpers.d.ts +6 -7
- package/dist/types/src/dream/LoadBuilder.d.ts +48 -1
- package/dist/types/src/dream/constants.d.ts +2 -0
- package/dist/types/src/dream/internal/buildAssociationPaths.d.ts +12 -0
- package/dist/types/src/dream/internal/buildDependentDestroyPreloadPaths.d.ts +17 -0
- package/dist/types/src/dream/internal/buildSerializerPreloadPaths.d.ts +3 -0
- package/dist/types/src/dream/internal/convertDreamClassAndAssociationNameTupleArrayToPreloadArgs.d.ts +1 -1
- package/dist/types/src/dream/internal/destroyAssociatedRecords.d.ts +5 -1
- package/dist/types/src/dream/internal/loadDependentDestroyTree.d.ts +17 -0
- package/dist/types/src/dream/internal/resolveSerializerAssociationEdges.d.ts +13 -0
- package/dist/types/src/serializer/builders/DreamSerializerBuilder.d.ts +13 -4
- package/dist/types/src/serializer/builders/ObjectSerializerBuilder.d.ts +3 -2
- package/dist/types/src/types/recursiveSerialization.d.ts +8 -0
- package/dist/types/src/types/recursiveSerialization.ts +10 -0
- package/dist/types/src/types/serializer.d.ts +8 -1
- package/dist/types/src/types/serializer.ts +8 -1
- package/docs/classes/db.DreamMigrationHelpers.html +14 -15
- package/docs/classes/db.KyselyQueryDriver.html +32 -32
- package/docs/classes/db.PostgresQueryDriver.html +33 -33
- package/docs/classes/db.QueryDriverBase.html +31 -31
- 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.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 +118 -118
- package/docs/classes/index.DreamApp.html +5 -5
- package/docs/classes/index.DreamTransaction.html +2 -2
- package/docs/classes/index.Env.html +2 -2
- package/docs/classes/index.Query.html +56 -56
- package/docs/classes/system.CliFileWriter.html +4 -4
- package/docs/classes/system.DreamBin.html +2 -2
- package/docs/classes/system.DreamCLI.html +6 -6
- package/docs/classes/system.DreamImporter.html +2 -2
- package/docs/classes/system.DreamLogos.html +2 -2
- package/docs/classes/system.DreamSerializerBuilder.html +19 -15
- package/docs/classes/system.ObjectSerializerBuilder.html +10 -10
- package/docs/classes/system.PathHelpers.html +3 -3
- package/docs/classes/utils.Encrypt.html +2 -2
- 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.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
- package/dist/cjs/src/dream/internal/extractNestedPaths.js +0 -34
- package/dist/esm/src/dream/internal/extractNestedPaths.js +0 -34
- package/dist/types/src/dream/internal/extractNestedPaths.d.ts +0 -21
package/dist/cjs/src/Dream.js
CHANGED
|
@@ -5,6 +5,7 @@ import { blankAssociationsFactory } from './decorators/field/association/shared.
|
|
|
5
5
|
import { blankHooksFactory } from './decorators/field/lifecycle/shared.js';
|
|
6
6
|
import resortAllRecords from './decorators/field/sortable/helpers/resortAllRecords.js';
|
|
7
7
|
import DreamApp from './dream-app/index.js';
|
|
8
|
+
import { RECURSIVE_SERIALIZATION_MAX_REPEATS } from './dream/constants.js';
|
|
8
9
|
import DreamClassTransactionBuilder from './dream/DreamClassTransactionBuilder.js';
|
|
9
10
|
import DreamInstanceTransactionBuilder from './dream/DreamInstanceTransactionBuilder.js';
|
|
10
11
|
import associationQuery from './dream/internal/associations/associationQuery.js';
|
|
@@ -20,6 +21,7 @@ import ensureSTITypeFieldIsSet from './dream/internal/ensureSTITypeFieldIsSet.js
|
|
|
20
21
|
import findOrCreateBy from './dream/internal/findOrCreateBy.js';
|
|
21
22
|
import printSerializerHierarchyLevel from './dream/internal/printSerializerHierarchyLevel.js';
|
|
22
23
|
import reload from './dream/internal/reload.js';
|
|
24
|
+
import resolveSerializerAssociationEdges from './dream/internal/resolveSerializerAssociationEdges.js';
|
|
23
25
|
import runValidations from './dream/internal/runValidations.js';
|
|
24
26
|
import saveDream from './dream/internal/saveDream.js';
|
|
25
27
|
import { DEFAULT_BYPASS_ALL_DEFAULT_SCOPES, DEFAULT_DEFAULT_SCOPES_TO_BYPASS, DEFAULT_SKIP_HOOKS, } from './dream/internal/scopeHelpers.js';
|
|
@@ -43,14 +45,11 @@ import GlobalNameNotSet from './errors/dream-app/GlobalNameNotSet.js';
|
|
|
43
45
|
import DreamMissingRequiredOverride from './errors/DreamMissingRequiredOverride.js';
|
|
44
46
|
import NonExistentScopeProvidedToResort from './errors/NonExistentScopeProvidedToResort.js';
|
|
45
47
|
import RecordNotFound from './errors/RecordNotFound.js';
|
|
46
|
-
import MissingSerializersDefinition from './errors/serializers/MissingSerializersDefinition.js';
|
|
47
48
|
import areEqual from './helpers/areEqual.js';
|
|
48
49
|
import cloneDeepSafe from './helpers/cloneDeepSafe.js';
|
|
49
|
-
import compact from './helpers/compact.js';
|
|
50
50
|
import isJsonColumn from './helpers/db/types/isJsonColumn.js';
|
|
51
51
|
import notEqual from './helpers/notEqual.js';
|
|
52
52
|
import { inferSerializersFromDreamClassOrViewModelClass } from './serializer/helpers/inferSerializerFromDreamOrViewModel.js';
|
|
53
|
-
const RECURSIVE_SERIALIZATION_MAX_REPEATS = 4;
|
|
54
53
|
export default class Dream {
|
|
55
54
|
DB;
|
|
56
55
|
/**
|
|
@@ -568,72 +567,29 @@ export default class Dream {
|
|
|
568
567
|
if (repeatedAssociationTracker[depthTrackerId] + 1 > RECURSIVE_SERIALIZATION_MAX_REPEATS)
|
|
569
568
|
return {};
|
|
570
569
|
repeatedAssociationTracker[depthTrackerId]++;
|
|
571
|
-
const
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
serializerAssociation.name;
|
|
576
|
-
const serializerAssociationType = serializerAssociation.type;
|
|
577
|
-
const association = this['getAssociationMetadata'](serializerAssociationName);
|
|
578
|
-
if (!association)
|
|
579
|
-
return accumulator;
|
|
580
|
-
const maybeAssociatedClasses = association.modelCB();
|
|
581
|
-
if (!maybeAssociatedClasses)
|
|
582
|
-
throw new Error(`No class defined on ${serializerAssociationName} association on ${this.sanitizedName}`);
|
|
583
|
-
const associatedClasses = Array.isArray(maybeAssociatedClasses)
|
|
584
|
-
? maybeAssociatedClasses
|
|
585
|
-
: [maybeAssociatedClasses];
|
|
586
|
-
/////////////////////////////////////////////////
|
|
587
|
-
// map associated classes to their serializers //
|
|
588
|
-
/////////////////////////////////////////////////
|
|
589
|
-
const associatedClassSerializerTuples = associatedClasses.flatMap(associatedClass => {
|
|
590
|
-
/**
|
|
591
|
-
* `serializers` is an array because `associatedClass` may be an STI
|
|
592
|
-
* base, with each of its STI children having its own serializer
|
|
593
|
-
*/
|
|
594
|
-
let serializers = [];
|
|
595
|
-
try {
|
|
596
|
-
serializers = serializerAssociation.options.serializer
|
|
597
|
-
? compact([serializerAssociation.options.serializer])
|
|
598
|
-
: compact(inferSerializersFromDreamClassOrViewModelClass(associatedClass, serializerAssociation.options.serializerKey));
|
|
599
|
-
}
|
|
600
|
-
catch (error) {
|
|
601
|
-
if (!(error instanceof MissingSerializersDefinition))
|
|
602
|
-
throw error;
|
|
603
|
-
serializers = [];
|
|
604
|
-
}
|
|
605
|
-
return serializers.map(serializer => [associatedClass, serializer]);
|
|
606
|
-
});
|
|
607
|
-
/////////////////////////////////////////////////////
|
|
608
|
-
// end:map associated classes to their serializers //
|
|
609
|
-
/////////////////////////////////////////////////////
|
|
610
|
-
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
611
|
-
// reduce over all associated serializers, recursively building out their associated serializers //
|
|
612
|
-
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
613
|
-
const innerAssociationSerializerInfo = associatedClassSerializerTuples.reduce((innerAccumulator, [associatedClass, associatedSerializer]) => {
|
|
614
|
-
if (forDisplay && serializerAssociationType !== 'delegatedAttribute') {
|
|
570
|
+
const edges = resolveSerializerAssociationEdges(this, serializer);
|
|
571
|
+
const mappedAssociations = edges.reduce((accumulator, edge) => {
|
|
572
|
+
const innerAssociationSerializerInfo = edge.targets.reduce((innerAccumulator, target) => {
|
|
573
|
+
if (forDisplay && edge.type !== 'delegatedAttribute') {
|
|
615
574
|
printSerializerHierarchyLevel({
|
|
616
|
-
serializerAssociationType,
|
|
617
|
-
serializerAssociationName,
|
|
618
|
-
associationSerializer:
|
|
575
|
+
serializerAssociationType: edge.type,
|
|
576
|
+
serializerAssociationName: edge.serializerAssociationName,
|
|
577
|
+
associationSerializer: target.serializer,
|
|
619
578
|
forDisplayDepth,
|
|
620
579
|
});
|
|
621
580
|
}
|
|
622
581
|
return {
|
|
623
582
|
...innerAccumulator,
|
|
624
|
-
...
|
|
583
|
+
...target.dreamClass['recursiveSerializationMap'](target.serializer, {
|
|
625
584
|
forDisplay,
|
|
626
585
|
forDisplayDepth: forDisplayDepth + 1,
|
|
627
586
|
repeatedAssociationTracker,
|
|
628
587
|
}),
|
|
629
588
|
};
|
|
630
589
|
}, {});
|
|
631
|
-
|
|
632
|
-
// end:reduce over all associated serializers, recursively building out their associated serializers //
|
|
633
|
-
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
634
|
-
accumulator[association.as] = {
|
|
590
|
+
accumulator[edge.associationAs] = {
|
|
635
591
|
parentDreamClass: this,
|
|
636
|
-
nestedSerializerInfo:
|
|
592
|
+
nestedSerializerInfo: edge.type === 'delegatedAttribute' ? {} : innerAssociationSerializerInfo,
|
|
637
593
|
};
|
|
638
594
|
return accumulator;
|
|
639
595
|
}, {});
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { sql } from 'kysely';
|
|
2
2
|
export default class DreamMigrationHelpers {
|
|
3
3
|
/**
|
|
4
|
-
* Rename a table and its associated primary key sequence.
|
|
4
|
+
* Rename a table and its associated primary key index and sequence.
|
|
5
5
|
*
|
|
6
|
-
* This method renames
|
|
7
|
-
*
|
|
8
|
-
* it automatically creates a sequence named `{tablename}_id_seq`. If you only rename
|
|
9
|
-
* the table, the sequence keeps the old name, which can cause confusion and issues.
|
|
6
|
+
* This method renames the table, its primary key index (`{tablename}_pkey`),
|
|
7
|
+
* and its primary key sequence (`{tablename}_id_seq`) to keep them in sync.
|
|
10
8
|
*
|
|
11
|
-
*
|
|
12
|
-
* key
|
|
9
|
+
* The sequence rename is skipped for tables with UUID primary keys (which have
|
|
10
|
+
* no associated sequence). The primary key index is always renamed since
|
|
11
|
+
* PostgreSQL does not automatically rename it when the table is renamed.
|
|
13
12
|
*
|
|
14
13
|
* @param db - The Kysely database object passed into the migration up/down function
|
|
15
14
|
* @param from - The current name of the table to rename
|
|
@@ -17,10 +16,15 @@ export default class DreamMigrationHelpers {
|
|
|
17
16
|
*/
|
|
18
17
|
static async renameTable(db, from, to) {
|
|
19
18
|
await db.schema.alterTable(from).renameTo(to).execute();
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
await sql `ALTER INDEX IF EXISTS ${sql.ref(`${from}_pkey`)} RENAME TO ${sql.ref(`${to}_pkey`)}`.execute(db);
|
|
20
|
+
const sequenceExists = await sql `
|
|
21
|
+
SELECT EXISTS (
|
|
22
|
+
SELECT 1 FROM pg_class WHERE relkind = 'S' AND relname = ${`${from}_id_seq`}
|
|
23
|
+
)
|
|
24
|
+
`.execute(db);
|
|
25
|
+
if (sequenceExists.rows[0]?.exists) {
|
|
26
|
+
await sql `ALTER SEQUENCE ${sql.ref(`${from}_id_seq`)} RENAME TO ${sql.ref(`${to}_id_seq`)}`.execute(db);
|
|
27
|
+
}
|
|
24
28
|
}
|
|
25
29
|
/**
|
|
26
30
|
* Unique indexes cannot be populated by the same value even within a transaction,
|
|
@@ -24,6 +24,61 @@ export default class LoadBuilder {
|
|
|
24
24
|
this.query = this.query.passthrough(passthroughWhereStatement);
|
|
25
25
|
return this;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Prevents a specific default scope from applying when
|
|
29
|
+
* loading associations
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* const user = await User.firstOrFail()
|
|
33
|
+
* const loaded = await user
|
|
34
|
+
* .load('posts')
|
|
35
|
+
* .removeDefaultScope('dream:SoftDelete')
|
|
36
|
+
* .execute()
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @param scopeName - The default scope to bypass
|
|
40
|
+
* @returns The LoadBuilder instance for chaining
|
|
41
|
+
*/
|
|
42
|
+
removeDefaultScope(scopeName) {
|
|
43
|
+
this.query = this.query.removeDefaultScope(scopeName);
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Prevents all default scopes from applying when
|
|
48
|
+
* loading associations
|
|
49
|
+
*
|
|
50
|
+
* ```ts
|
|
51
|
+
* const user = await User.firstOrFail()
|
|
52
|
+
* const loaded = await user
|
|
53
|
+
* .load('posts')
|
|
54
|
+
* .removeAllDefaultScopes()
|
|
55
|
+
* .execute()
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @returns The LoadBuilder instance for chaining
|
|
59
|
+
*/
|
|
60
|
+
removeAllDefaultScopes() {
|
|
61
|
+
this.query = this.query.removeAllDefaultScopes();
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Sets the database connection to use when loading associations
|
|
66
|
+
*
|
|
67
|
+
* ```ts
|
|
68
|
+
* const user = await User.firstOrFail()
|
|
69
|
+
* const loaded = await user
|
|
70
|
+
* .load('posts')
|
|
71
|
+
* .connection('replica')
|
|
72
|
+
* .execute()
|
|
73
|
+
* ```
|
|
74
|
+
*
|
|
75
|
+
* @param connection - The connection type ('primary' or 'replica')
|
|
76
|
+
* @returns The LoadBuilder instance for chaining
|
|
77
|
+
*/
|
|
78
|
+
connection(connection) {
|
|
79
|
+
this.query = this.query.connection(connection);
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
27
82
|
/**
|
|
28
83
|
* Attaches a load statement to the load builder
|
|
29
84
|
*
|
|
@@ -17,10 +17,10 @@ import namespaceColumn from '../helpers/namespaceColumn.js';
|
|
|
17
17
|
import protectAgainstPollutingAssignment from '../helpers/protectAgainstPollutingAssignment.js';
|
|
18
18
|
import { toSafeObject } from '../helpers/toSafeObject.js';
|
|
19
19
|
import ops from '../ops/index.js';
|
|
20
|
+
import buildSerializerPreloadPaths from './internal/buildSerializerPreloadPaths.js';
|
|
20
21
|
import computedPaginatePage from './internal/computedPaginatePage.js';
|
|
21
22
|
import convertDreamClassAndAssociationNameTupleArrayToPreloadArgs from './internal/convertDreamClassAndAssociationNameTupleArrayToPreloadArgs.js';
|
|
22
|
-
|
|
23
|
-
// Cache for extractNestedPaths results, keyed by "${sanitizedName}:${serializerKey}"
|
|
23
|
+
// Cache for serializer preload-path results, keyed by "${globalName}:${serializerKey}"
|
|
24
24
|
const extractedNestedPathsCache = new Map();
|
|
25
25
|
export default class Query {
|
|
26
26
|
/**
|
|
@@ -526,11 +526,10 @@ export default class Query {
|
|
|
526
526
|
* @returns A Query with all serialization associations preloaded
|
|
527
527
|
*/
|
|
528
528
|
preloadFor(serializerKey, modifierFn) {
|
|
529
|
-
const cacheKey = `${this.dreamClass.
|
|
529
|
+
const cacheKey = `${this.dreamClass.globalName}:${serializerKey ?? 'default'}`;
|
|
530
530
|
let preloadArgs = extractedNestedPathsCache.get(cacheKey);
|
|
531
531
|
if (!preloadArgs) {
|
|
532
|
-
|
|
533
|
-
preloadArgs = extractNestedPaths(this.dreamClass['serializationMap'](serializerKey));
|
|
532
|
+
preloadArgs = buildSerializerPreloadPaths(this.dreamClass, serializerKey);
|
|
534
533
|
extractedNestedPathsCache.set(cacheKey, preloadArgs);
|
|
535
534
|
}
|
|
536
535
|
let query = this;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export const primaryKeyTypes = ['uuid7', 'uuid4', 'bigserial', 'bigint', 'integer'];
|
|
2
2
|
export const TRIGRAM_OPERATORS = ['%', '<%', '<<%'];
|
|
3
|
+
export const RECURSIVE_SERIALIZATION_MAX_REPEATS = 4;
|
|
4
|
+
export const RECURSIVE_DESTROY_PRELOAD_MAX_REPEATS = RECURSIVE_SERIALIZATION_MAX_REPEATS;
|
|
3
5
|
class RequiredAttribute {
|
|
4
6
|
constructor() { }
|
|
5
7
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export default function buildAssociationPaths(rootNode, opts) {
|
|
2
|
+
const paths = [];
|
|
3
|
+
traverseAssociationPaths(rootNode, [], {}, paths, opts);
|
|
4
|
+
return paths;
|
|
5
|
+
}
|
|
6
|
+
function traverseAssociationPaths(node, currentPath, depthTracker, paths, opts) {
|
|
7
|
+
const trackerId = opts.getKey(node);
|
|
8
|
+
depthTracker[trackerId] ??= 0;
|
|
9
|
+
if (depthTracker[trackerId] + 1 > opts.maxRepeats) {
|
|
10
|
+
if (currentPath.length > 0) {
|
|
11
|
+
paths.push([...currentPath]);
|
|
12
|
+
}
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
depthTracker[trackerId]++;
|
|
16
|
+
const edges = opts.getEdges(node);
|
|
17
|
+
if (edges.length === 0) {
|
|
18
|
+
if (currentPath.length > 0) {
|
|
19
|
+
paths.push([...currentPath]);
|
|
20
|
+
}
|
|
21
|
+
depthTracker[trackerId]--;
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
for (const edge of edges) {
|
|
25
|
+
const newPath = [...currentPath, edge.tuple];
|
|
26
|
+
if (!edge.nextNode) {
|
|
27
|
+
paths.push(newPath);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
traverseAssociationPaths(edge.nextNode, newPath, depthTracker, paths, opts);
|
|
31
|
+
}
|
|
32
|
+
depthTracker[trackerId]--;
|
|
33
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { RECURSIVE_DESTROY_PRELOAD_MAX_REPEATS } from '../constants.js';
|
|
2
|
+
import buildAssociationPaths from './buildAssociationPaths.js';
|
|
3
|
+
const dependentDestroyPreloadPathsCache = new Map();
|
|
4
|
+
/**
|
|
5
|
+
* @internal
|
|
6
|
+
*
|
|
7
|
+
* Recursively walks `dependent: 'destroy'` associations starting from the given
|
|
8
|
+
* Dream class, producing an array of preload paths. Each path is an array of
|
|
9
|
+
* [DreamClass, associationName] tuples representing a chain from root to leaf.
|
|
10
|
+
*
|
|
11
|
+
* Allows the same Dream class to appear up to MAX_REPEATS times in a single
|
|
12
|
+
* path to support tree structures (e.g. a Category with `dependent: 'destroy'`
|
|
13
|
+
* on its children, which are also Categories).
|
|
14
|
+
*
|
|
15
|
+
* Used to build a preload tree so that all records in the cascade can be loaded
|
|
16
|
+
* upfront, eliminating N+1 queries during destroy.
|
|
17
|
+
*/
|
|
18
|
+
export default function buildDependentDestroyPreloadPaths(dreamClass) {
|
|
19
|
+
const cacheKey = dreamClass.globalName;
|
|
20
|
+
const cached = dependentDestroyPreloadPathsCache.get(cacheKey);
|
|
21
|
+
if (cached)
|
|
22
|
+
return cached;
|
|
23
|
+
const paths = [];
|
|
24
|
+
paths.push(...buildAssociationPaths(dreamClass, {
|
|
25
|
+
getKey: currentDreamClass => currentDreamClass.globalName,
|
|
26
|
+
getEdges: currentDreamClass => {
|
|
27
|
+
const associationMap = currentDreamClass['associationMetadataMap']();
|
|
28
|
+
const dependentDestroyNames = Object.keys(associationMap).filter(key => associationMap[key].dependent === 'destroy');
|
|
29
|
+
return dependentDestroyNames.flatMap(associationName => {
|
|
30
|
+
const association = associationMap[associationName];
|
|
31
|
+
const modelCBResult = association.modelCB();
|
|
32
|
+
const targetClasses = Array.isArray(modelCBResult) ? modelCBResult : [modelCBResult];
|
|
33
|
+
return targetClasses.map(targetClass => ({
|
|
34
|
+
nextNode: targetClass,
|
|
35
|
+
tuple: [currentDreamClass, associationName],
|
|
36
|
+
}));
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
maxRepeats: RECURSIVE_DESTROY_PRELOAD_MAX_REPEATS,
|
|
40
|
+
}));
|
|
41
|
+
dependentDestroyPreloadPathsCache.set(cacheKey, paths);
|
|
42
|
+
return paths;
|
|
43
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { inferSerializersFromDreamClassOrViewModelClass } from '../../serializer/helpers/inferSerializerFromDreamOrViewModel.js';
|
|
2
|
+
import { RECURSIVE_SERIALIZATION_MAX_REPEATS } from '../constants.js';
|
|
3
|
+
import buildAssociationPaths from './buildAssociationPaths.js';
|
|
4
|
+
import resolveSerializerAssociationEdges from './resolveSerializerAssociationEdges.js';
|
|
5
|
+
export default function buildSerializerPreloadPaths(dreamClass, serializerKey) {
|
|
6
|
+
const key = serializerKey || 'default';
|
|
7
|
+
const serializer = inferSerializersFromDreamClassOrViewModelClass(dreamClass, key)[0] ?? null;
|
|
8
|
+
if (!serializer)
|
|
9
|
+
throw new Error(`unable to find serializer with key: ${key}`);
|
|
10
|
+
const paths = buildAssociationPaths({
|
|
11
|
+
dreamClass,
|
|
12
|
+
serializer,
|
|
13
|
+
}, {
|
|
14
|
+
getKey: node => node.dreamClass.globalName,
|
|
15
|
+
getEdges: serializerNodeToEdges,
|
|
16
|
+
maxRepeats: RECURSIVE_SERIALIZATION_MAX_REPEATS,
|
|
17
|
+
});
|
|
18
|
+
const dedupedPaths = new Map();
|
|
19
|
+
for (const path of paths) {
|
|
20
|
+
dedupedPaths.set(path
|
|
21
|
+
.map(([pathDreamClass, associationName]) => `${pathDreamClass.globalName}:${associationName}`)
|
|
22
|
+
.join('|'), path);
|
|
23
|
+
}
|
|
24
|
+
return [...dedupedPaths.values()];
|
|
25
|
+
}
|
|
26
|
+
function serializerNodeToEdges({ dreamClass, serializer, }) {
|
|
27
|
+
const edges = resolveSerializerAssociationEdges(dreamClass, serializer);
|
|
28
|
+
return edges.flatMap(edge => {
|
|
29
|
+
const tuple = [dreamClass, edge.associationAs];
|
|
30
|
+
if (edge.type === 'delegatedAttribute' || edge.targets.length === 0) {
|
|
31
|
+
return [{ nextNode: null, tuple }];
|
|
32
|
+
}
|
|
33
|
+
return edge.targets.map(target => ({
|
|
34
|
+
nextNode: target,
|
|
35
|
+
tuple,
|
|
36
|
+
}));
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -2,17 +2,25 @@
|
|
|
2
2
|
* @internal
|
|
3
3
|
*
|
|
4
4
|
* Destroys all HasOne/HasMany associations on this
|
|
5
|
-
* dream that are marked as `dependent: 'destroy'
|
|
5
|
+
* dream that are marked as `dependent: 'destroy'`.
|
|
6
|
+
*
|
|
7
|
+
* Expects associations to be preloaded onto the dream instance
|
|
8
|
+
* via `loadDependentDestroyTree`. Iterates loaded associations
|
|
9
|
+
* directly instead of querying the database.
|
|
6
10
|
*/
|
|
7
11
|
export default async function destroyAssociatedRecords(dream, txn, options) {
|
|
8
12
|
const dreamClass = dream.constructor;
|
|
9
13
|
const { reallyDestroy } = options;
|
|
10
14
|
for (const associationName of dreamClass['dependentDestroyAssociationNames']()) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
const loaded = dream[associationName];
|
|
16
|
+
const records = Array.isArray(loaded) ? loaded : loaded ? [loaded] : [];
|
|
17
|
+
for (const record of records) {
|
|
18
|
+
if (reallyDestroy) {
|
|
19
|
+
await record.txn(txn).reallyDestroy(options);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
await record.txn(txn).destroy(options);
|
|
23
|
+
}
|
|
16
24
|
}
|
|
17
25
|
}
|
|
18
26
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import DreamApp from '../../dream-app/index.js';
|
|
2
2
|
import destroyAssociatedRecords from './destroyAssociatedRecords.js';
|
|
3
|
+
import loadDependentDestroyTree from './loadDependentDestroyTree.js';
|
|
3
4
|
import runHooksFor from './runHooksFor.js';
|
|
4
5
|
/**
|
|
5
6
|
* @internal
|
|
@@ -30,7 +31,12 @@ export default async function destroyDream(dream, txn = null, options) {
|
|
|
30
31
|
async function destroyDreamWithTransaction(dream, txn, options) {
|
|
31
32
|
const { cascade, reallyDestroy, skipHooks } = options;
|
|
32
33
|
if (cascade) {
|
|
33
|
-
await
|
|
34
|
+
const dreamWithAssociations = await loadDependentDestroyTree(dream, txn, {
|
|
35
|
+
reallyDestroy,
|
|
36
|
+
bypassAllDefaultScopes: options.bypassAllDefaultScopes ?? false,
|
|
37
|
+
defaultScopesToBypass: options.defaultScopesToBypass ?? [],
|
|
38
|
+
});
|
|
39
|
+
await destroyAssociatedRecords(dreamWithAssociations, txn, options);
|
|
34
40
|
}
|
|
35
41
|
if (!skipHooks) {
|
|
36
42
|
await runHooksFor('beforeDestroy', dream, true, null, txn);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import LoadBuilder from '../LoadBuilder.js';
|
|
2
|
+
import buildDependentDestroyPreloadPaths from './buildDependentDestroyPreloadPaths.js';
|
|
3
|
+
import convertDreamClassAndAssociationNameTupleArrayToPreloadArgs from './convertDreamClassAndAssociationNameTupleArrayToPreloadArgs.js';
|
|
4
|
+
/**
|
|
5
|
+
* @internal
|
|
6
|
+
*
|
|
7
|
+
* Loads all `dependent: 'destroy'` associations onto the dream instance
|
|
8
|
+
* upfront, eliminating N+1 queries during cascade destruction.
|
|
9
|
+
*
|
|
10
|
+
* Returns a clone of the dream with all associations loaded.
|
|
11
|
+
* If there are no dependent-destroy associations, returns the original dream.
|
|
12
|
+
*/
|
|
13
|
+
export default async function loadDependentDestroyTree(dream, txn, { reallyDestroy, bypassAllDefaultScopes, defaultScopesToBypass, }) {
|
|
14
|
+
const dreamClass = dream.constructor;
|
|
15
|
+
const paths = buildDependentDestroyPreloadPaths(dreamClass);
|
|
16
|
+
if (paths.length === 0)
|
|
17
|
+
return dream;
|
|
18
|
+
let loadBuilder = new LoadBuilder(dream, txn);
|
|
19
|
+
for (const path of paths) {
|
|
20
|
+
const args = convertDreamClassAndAssociationNameTupleArrayToPreloadArgs(path);
|
|
21
|
+
loadBuilder = loadBuilder.load(...args);
|
|
22
|
+
}
|
|
23
|
+
if (bypassAllDefaultScopes) {
|
|
24
|
+
loadBuilder = loadBuilder.removeAllDefaultScopes();
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
for (const scopeName of defaultScopesToBypass) {
|
|
28
|
+
loadBuilder = loadBuilder.removeDefaultScope(scopeName);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (reallyDestroy) {
|
|
32
|
+
loadBuilder = loadBuilder.removeDefaultScope('dream:SoftDelete');
|
|
33
|
+
}
|
|
34
|
+
return await loadBuilder.execute();
|
|
35
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import MissingSerializersDefinition from '../../errors/serializers/MissingSerializersDefinition.js';
|
|
2
|
+
import compact from '../../helpers/compact.js';
|
|
3
|
+
import { inferSerializersFromDreamClassOrViewModelClass } from '../../serializer/helpers/inferSerializerFromDreamOrViewModel.js';
|
|
4
|
+
export default function resolveSerializerAssociationEdges(dreamClass, serializer) {
|
|
5
|
+
const serializerBuilder = serializer(undefined, undefined);
|
|
6
|
+
const serializerAssociations = serializerBuilder['attributes'].filter(attribute => ['rendersOne', 'rendersMany', 'delegatedAttribute'].includes(attribute.type));
|
|
7
|
+
return compact(serializerAssociations.map(serializerAssociation => {
|
|
8
|
+
const serializerAssociationName = serializerAssociation.targetName ??
|
|
9
|
+
serializerAssociation.name;
|
|
10
|
+
const association = dreamClass['getAssociationMetadata'](serializerAssociationName);
|
|
11
|
+
if (!association)
|
|
12
|
+
return null;
|
|
13
|
+
if (serializerAssociation.type === 'delegatedAttribute') {
|
|
14
|
+
return {
|
|
15
|
+
associationAs: association.as,
|
|
16
|
+
sourceDreamClass: dreamClass,
|
|
17
|
+
type: serializerAssociation.type,
|
|
18
|
+
serializerAssociationName,
|
|
19
|
+
targets: [],
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const maybeAssociatedClasses = association.modelCB();
|
|
23
|
+
if (!maybeAssociatedClasses)
|
|
24
|
+
throw new Error(`No class defined on ${serializerAssociationName} association on ${dreamClass.sanitizedName}`);
|
|
25
|
+
const associatedClasses = Array.isArray(maybeAssociatedClasses)
|
|
26
|
+
? maybeAssociatedClasses
|
|
27
|
+
: [maybeAssociatedClasses];
|
|
28
|
+
const targets = associatedClasses.flatMap(associatedClass => {
|
|
29
|
+
let serializers = [];
|
|
30
|
+
try {
|
|
31
|
+
serializers = serializerAssociation.options.serializer
|
|
32
|
+
? compact([serializerAssociation.options.serializer])
|
|
33
|
+
: compact(inferSerializersFromDreamClassOrViewModelClass(associatedClass, serializerAssociation.options.serializerKey));
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (!(error instanceof MissingSerializersDefinition))
|
|
37
|
+
throw error;
|
|
38
|
+
serializers = [];
|
|
39
|
+
}
|
|
40
|
+
return serializers.map(associatedSerializer => ({
|
|
41
|
+
dreamClass: associatedClass,
|
|
42
|
+
serializer: associatedSerializer,
|
|
43
|
+
}));
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
associationAs: association.as,
|
|
47
|
+
sourceDreamClass: dreamClass,
|
|
48
|
+
type: serializerAssociation.type,
|
|
49
|
+
serializerAssociationName,
|
|
50
|
+
targets,
|
|
51
|
+
};
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
@@ -158,7 +158,7 @@ export default class SerializerRenderer {
|
|
|
158
158
|
// does not pass it into the call to DreamSerializer/ObjectSerializer,
|
|
159
159
|
// then it would be lost to serializers rendered via rendersOne/Many, and SerializerRenderer
|
|
160
160
|
// handles passing its passthrough data into those
|
|
161
|
-
.render(passthroughData));
|
|
161
|
+
.render(passthroughData, this.renderOpts));
|
|
162
162
|
});
|
|
163
163
|
return accumulator;
|
|
164
164
|
}
|
|
@@ -37,6 +37,10 @@ export default class DreamSerializerBuilder {
|
|
|
37
37
|
* - `default` - Value to use when the target object or its attribute is null/undefined
|
|
38
38
|
* - `openapi` - OpenAPI schema definition; required for non-Dream targets and json/jsonb
|
|
39
39
|
* columns, optional for standard Dream columns (where types are inferred)
|
|
40
|
+
* - `optional` - Set to `true` to indicate the value can be null in the OpenAPI schema
|
|
41
|
+
* (wraps the type in `anyOf: [schema, { type: 'null' }]`). For Dream models, this is
|
|
42
|
+
* auto-inferred from optional BelongsTo associations. Use this when delegating through
|
|
43
|
+
* a HasOne or other nullable association.
|
|
40
44
|
* - `precision` - Round decimal values to the specified number of decimal places (0–9)
|
|
41
45
|
* during rendering; does not affect the OpenAPI shape
|
|
42
46
|
* - `required` - Set to `false` to mark the attribute as optional in the OpenAPI schema;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|