@rvoh/dream 2.5.8 → 2.6.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 +12 -56
- 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/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/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 +9 -9
- 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
|
@@ -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 {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Dream from '../Dream.js';
|
|
2
2
|
import { PassthroughOnClause } from '../types/associations/shared.js';
|
|
3
|
-
import {
|
|
3
|
+
import { DbConnectionType } from '../types/db.js';
|
|
4
|
+
import { AllDefaultScopeNames, DreamSerializerKey, PassthroughColumnNames } from '../types/dream.js';
|
|
4
5
|
import { LoadForModifierFn } from '../types/query.js';
|
|
5
6
|
import { VariadicLoadArgs } from '../types/variadic.js';
|
|
6
7
|
import DreamTransaction from './DreamTransaction.js';
|
|
@@ -23,6 +24,52 @@ export default class LoadBuilder<DreamInstance extends Dream> {
|
|
|
23
24
|
*/
|
|
24
25
|
constructor(dream: Dream, dreamTransaction?: (DreamTransaction<any> | null) | undefined);
|
|
25
26
|
passthrough<I extends LoadBuilder<DreamInstance>, PassthroughColumns extends PassthroughColumnNames<DreamInstance>>(this: I, passthroughWhereStatement: PassthroughOnClause<PassthroughColumns>): I;
|
|
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<I extends LoadBuilder<DreamInstance>>(this: I, scopeName: AllDefaultScopeNames<DreamInstance>): I;
|
|
43
|
+
/**
|
|
44
|
+
* Prevents all default scopes from applying when
|
|
45
|
+
* loading associations
|
|
46
|
+
*
|
|
47
|
+
* ```ts
|
|
48
|
+
* const user = await User.firstOrFail()
|
|
49
|
+
* const loaded = await user
|
|
50
|
+
* .load('posts')
|
|
51
|
+
* .removeAllDefaultScopes()
|
|
52
|
+
* .execute()
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @returns The LoadBuilder instance for chaining
|
|
56
|
+
*/
|
|
57
|
+
removeAllDefaultScopes<I extends LoadBuilder<DreamInstance>>(this: I): I;
|
|
58
|
+
/**
|
|
59
|
+
* Sets the database connection to use when loading associations
|
|
60
|
+
*
|
|
61
|
+
* ```ts
|
|
62
|
+
* const user = await User.firstOrFail()
|
|
63
|
+
* const loaded = await user
|
|
64
|
+
* .load('posts')
|
|
65
|
+
* .connection('replica')
|
|
66
|
+
* .execute()
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* @param connection - The connection type ('primary' or 'replica')
|
|
70
|
+
* @returns The LoadBuilder instance for chaining
|
|
71
|
+
*/
|
|
72
|
+
connection<I extends LoadBuilder<DreamInstance>>(this: I, connection: DbConnectionType): I;
|
|
26
73
|
/**
|
|
27
74
|
* Attaches a load statement to the load builder
|
|
28
75
|
*
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export declare const primaryKeyTypes: readonly ["uuid7", "uuid4", "bigserial", "bigint", "integer"];
|
|
2
2
|
export declare const TRIGRAM_OPERATORS: readonly ["%", "<%", "<<%"];
|
|
3
|
+
export declare const RECURSIVE_SERIALIZATION_MAX_REPEATS = 4;
|
|
4
|
+
export declare const RECURSIVE_DESTROY_PRELOAD_MAX_REPEATS = 4;
|
|
3
5
|
declare class RequiredAttribute {
|
|
4
6
|
constructor();
|
|
5
7
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DreamClassAndAssociationNameTuple } from '../../types/recursiveSerialization.js';
|
|
2
|
+
export interface AssociationPathEdge<NodeType> {
|
|
3
|
+
nextNode: NodeType | null;
|
|
4
|
+
tuple: DreamClassAndAssociationNameTuple;
|
|
5
|
+
}
|
|
6
|
+
export default function buildAssociationPaths<NodeType>(rootNode: NodeType, opts: BuildAssociationPathsOptions<NodeType>): DreamClassAndAssociationNameTuple[][];
|
|
7
|
+
type BuildAssociationPathsOptions<NodeType> = {
|
|
8
|
+
getKey: (node: NodeType) => string;
|
|
9
|
+
getEdges: (node: NodeType) => AssociationPathEdge<NodeType>[];
|
|
10
|
+
maxRepeats: number;
|
|
11
|
+
};
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import Dream from '../../Dream.js';
|
|
2
|
+
import { DreamClassAndAssociationNameTuple } from '../../types/recursiveSerialization.js';
|
|
3
|
+
/**
|
|
4
|
+
* @internal
|
|
5
|
+
*
|
|
6
|
+
* Recursively walks `dependent: 'destroy'` associations starting from the given
|
|
7
|
+
* Dream class, producing an array of preload paths. Each path is an array of
|
|
8
|
+
* [DreamClass, associationName] tuples representing a chain from root to leaf.
|
|
9
|
+
*
|
|
10
|
+
* Allows the same Dream class to appear up to MAX_REPEATS times in a single
|
|
11
|
+
* path to support tree structures (e.g. a Category with `dependent: 'destroy'`
|
|
12
|
+
* on its children, which are also Categories).
|
|
13
|
+
*
|
|
14
|
+
* Used to build a preload tree so that all records in the cascade can be loaded
|
|
15
|
+
* upfront, eliminating N+1 queries during destroy.
|
|
16
|
+
*/
|
|
17
|
+
export default function buildDependentDestroyPreloadPaths(dreamClass: typeof Dream): DreamClassAndAssociationNameTuple[][];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LoadForModifierFn } from '../../types/query.js';
|
|
2
|
-
import { DreamClassAndAssociationNameTuple } from '
|
|
2
|
+
import { DreamClassAndAssociationNameTuple } from '../../types/recursiveSerialization.js';
|
|
3
3
|
export default function convertDreamClassAndAssociationNameTupleArrayToPreloadArgs(dreamClassAndAssociationNameTupleArray: DreamClassAndAssociationNameTuple[], modifierFn?: LoadForModifierFn, counter?: {
|
|
4
4
|
count: number;
|
|
5
5
|
}): (string | {
|
|
@@ -5,6 +5,10 @@ import { ReallyDestroyOptions } from './destroyDream.js';
|
|
|
5
5
|
* @internal
|
|
6
6
|
*
|
|
7
7
|
* Destroys all HasOne/HasMany associations on this
|
|
8
|
-
* dream that are marked as `dependent: 'destroy'
|
|
8
|
+
* dream that are marked as `dependent: 'destroy'`.
|
|
9
|
+
*
|
|
10
|
+
* Expects associations to be preloaded onto the dream instance
|
|
11
|
+
* via `loadDependentDestroyTree`. Iterates loaded associations
|
|
12
|
+
* directly instead of querying the database.
|
|
9
13
|
*/
|
|
10
14
|
export default function destroyAssociatedRecords<I extends Dream>(dream: I, txn: DreamTransaction<I>, options: ReallyDestroyOptions<I>): Promise<void>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import Dream from '../../Dream.js';
|
|
2
|
+
import { AllDefaultScopeNames } from '../../types/dream.js';
|
|
3
|
+
import DreamTransaction from '../DreamTransaction.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 function loadDependentDestroyTree<I extends Dream>(dream: I, txn: DreamTransaction<I>, { reallyDestroy, bypassAllDefaultScopes, defaultScopesToBypass, }: {
|
|
14
|
+
reallyDestroy: boolean;
|
|
15
|
+
bypassAllDefaultScopes: boolean;
|
|
16
|
+
defaultScopesToBypass: AllDefaultScopeNames<I>[];
|
|
17
|
+
}): Promise<I>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import Dream from '../../Dream.js';
|
|
2
|
+
import { DreamModelSerializerType, SimpleObjectSerializerType } from '../../types/serializer.js';
|
|
3
|
+
export interface ResolvedSerializerAssociationEdge {
|
|
4
|
+
associationAs: string;
|
|
5
|
+
sourceDreamClass: typeof Dream;
|
|
6
|
+
type: 'rendersOne' | 'rendersMany' | 'delegatedAttribute';
|
|
7
|
+
serializerAssociationName: string;
|
|
8
|
+
targets: {
|
|
9
|
+
dreamClass: typeof Dream;
|
|
10
|
+
serializer: DreamModelSerializerType | SimpleObjectSerializerType;
|
|
11
|
+
}[];
|
|
12
|
+
}
|
|
13
|
+
export default function resolveSerializerAssociationEdges(dreamClass: typeof Dream, serializer: DreamModelSerializerType | SimpleObjectSerializerType): ResolvedSerializerAssociationEdge[];
|
|
@@ -2,6 +2,7 @@ import Dream from '../../Dream.js';
|
|
|
2
2
|
import { DreamOrViewModelSerializerKey, DreamVirtualColumns, NonJsonDreamColumnNames, ViewModel, ViewModelClass } from '../../types/dream.js';
|
|
3
3
|
import { AutomaticSerializerAttributeOptions, AutomaticSerializerAttributeOptionsForType, InternalAnyTypedSerializerAttribute, InternalAnyTypedSerializerCustomAttribute, InternalAnyTypedSerializerDelegatedAttribute, InternalAnyTypedSerializerRendersMany, InternalAnyTypedSerializerRendersOne, NonAutomaticSerializerAttributeOptionsWithPossibleDecimalRenderOption, SerializerAttributeOptionsForVirtualColumn, SerializerType } from '../../types/serializer.js';
|
|
4
4
|
import { SerializerRendererOpts } from '../SerializerRenderer.js';
|
|
5
|
+
export type DreamPropertiesToExclude = keyof Dream | 'serializers';
|
|
5
6
|
export default class DreamSerializerBuilder<DataTypeForOpenapi extends typeof Dream, MaybeNullDataType extends Dream | null, PassthroughDataType = any, DataType extends Exclude<MaybeNullDataType, null> = Exclude<MaybeNullDataType, null>> {
|
|
6
7
|
protected $typeForOpenapi: DataTypeForOpenapi;
|
|
7
8
|
protected data: MaybeNullDataType;
|
|
@@ -53,7 +54,7 @@ export default class DreamSerializerBuilder<DataTypeForOpenapi extends typeof Dr
|
|
|
53
54
|
*/
|
|
54
55
|
attribute(name: NonJsonDreamColumnNames<DataType> & keyof DataType & 'type', options?: AutomaticSerializerAttributeOptionsForType): DreamSerializerBuilder<DataTypeForOpenapi, MaybeNullDataType, PassthroughDataType, DataType>;
|
|
55
56
|
attribute(name: DreamVirtualColumns<DataType>[number], options?: SerializerAttributeOptionsForVirtualColumn): DreamSerializerBuilder<DataTypeForOpenapi, MaybeNullDataType, PassthroughDataType, DataType>;
|
|
56
|
-
attribute<MaybeAttributeName extends NonJsonDreamColumnNames<DataType> | (keyof DataType & string), AttributeName extends MaybeAttributeName extends NonJsonDreamColumnNames<DataType> ? never : Exclude<keyof DataType,
|
|
57
|
+
attribute<MaybeAttributeName extends NonJsonDreamColumnNames<DataType> | (keyof DataType & string), AttributeName extends MaybeAttributeName extends NonJsonDreamColumnNames<DataType> ? never : Exclude<keyof DataType, DreamPropertiesToExclude> & string>(name: AttributeName, options: NonAutomaticSerializerAttributeOptionsWithPossibleDecimalRenderOption): DreamSerializerBuilder<DataTypeForOpenapi, MaybeNullDataType, PassthroughDataType, DataType>;
|
|
57
58
|
attribute<AttributeName extends NonJsonDreamColumnNames<DataType> & keyof DataType & string>(name: AttributeName, options?: AutomaticSerializerAttributeOptions): DreamSerializerBuilder<DataTypeForOpenapi, MaybeNullDataType, PassthroughDataType, DataType>;
|
|
58
59
|
/**
|
|
59
60
|
* Includes an attribute from a nested object in the serialized output.
|
|
@@ -75,6 +76,10 @@ export default class DreamSerializerBuilder<DataTypeForOpenapi extends typeof Dr
|
|
|
75
76
|
* - `default` - Value to use when the target object or its attribute is null/undefined
|
|
76
77
|
* - `openapi` - OpenAPI schema definition; required for non-Dream targets and json/jsonb
|
|
77
78
|
* columns, optional for standard Dream columns (where types are inferred)
|
|
79
|
+
* - `optional` - Set to `true` to indicate the value can be null in the OpenAPI schema
|
|
80
|
+
* (wraps the type in `anyOf: [schema, { type: 'null' }]`). For Dream models, this is
|
|
81
|
+
* auto-inferred from optional BelongsTo associations. Use this when delegating through
|
|
82
|
+
* a HasOne or other nullable association.
|
|
78
83
|
* - `precision` - Round decimal values to the specified number of decimal places (0–9)
|
|
79
84
|
* during rendering; does not affect the OpenAPI shape
|
|
80
85
|
* - `required` - Set to `false` to mark the attribute as optional in the OpenAPI schema;
|
|
@@ -99,7 +104,11 @@ export default class DreamSerializerBuilder<DataTypeForOpenapi extends typeof Dr
|
|
|
99
104
|
* })
|
|
100
105
|
* ```
|
|
101
106
|
*/
|
|
102
|
-
delegatedAttribute<ProvidedModelType = undefined, ProvidedTargetName extends ProvidedModelType extends undefined ? undefined : Exclude<keyof ProvidedModelType,
|
|
107
|
+
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?: AssociatedModelType extends Dream ? TargetAttributeName extends NonJsonDreamColumnNames<AssociatedModelType> & keyof AssociatedModelType & 'type' ? AutomaticSerializerAttributeOptionsForType & {
|
|
108
|
+
optional?: boolean;
|
|
109
|
+
} : TargetAttributeName extends DreamVirtualColumns<AssociatedModelType>[number] ? SerializerAttributeOptionsForVirtualColumn : TargetAttributeName extends NonJsonDreamColumnNames<AssociatedModelType> & keyof AssociatedModelType & string ? (AutomaticSerializerAttributeOptions & {
|
|
110
|
+
optional?: boolean;
|
|
111
|
+
}) | NonAutomaticSerializerAttributeOptionsWithPossibleDecimalRenderOption : NonAutomaticSerializerAttributeOptionsWithPossibleDecimalRenderOption : NonAutomaticSerializerAttributeOptionsWithPossibleDecimalRenderOption): this;
|
|
103
112
|
/**
|
|
104
113
|
* Includes a computed value in the serialized output.
|
|
105
114
|
*
|
|
@@ -196,7 +205,7 @@ export default class DreamSerializerBuilder<DataTypeForOpenapi extends typeof Dr
|
|
|
196
205
|
* .rendersOne('owner', { serializer: CustomOwnerSerializer })
|
|
197
206
|
* ```
|
|
198
207
|
*/
|
|
199
|
-
rendersOne<ProvidedModelType = undefined, ProvidedAttributeName extends ProvidedModelType extends undefined ? undefined : Exclude<keyof ProvidedModelType,
|
|
208
|
+
rendersOne<ProvidedModelType = undefined, ProvidedAttributeName 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, AttributeName extends ProvidedAttributeName extends undefined ? Exclude<keyof ActualDataType, DreamPropertiesToExclude> : ProvidedAttributeName & keyof ActualDataType = ProvidedAttributeName extends undefined ? Exclude<keyof ActualDataType, DreamPropertiesToExclude> : ProvidedAttributeName & keyof ActualDataType, AssociatedModelType = Exclude<ActualDataType[AttributeName], null>, SerializerOptions = AssociatedModelType extends Dream ? {
|
|
200
209
|
serializerKey?: DreamOrViewModelSerializerKey<AssociatedModelType>;
|
|
201
210
|
} | {
|
|
202
211
|
serializer?: SerializerType;
|
|
@@ -267,7 +276,7 @@ export default class DreamSerializerBuilder<DataTypeForOpenapi extends typeof Dr
|
|
|
267
276
|
* .rendersMany('rooms', { serializerKey: 'forGuests', as: 'guestRooms' })
|
|
268
277
|
* ```
|
|
269
278
|
*/
|
|
270
|
-
rendersMany<ProvidedModelType = undefined, ProvidedAttributeName extends ProvidedModelType extends undefined ? undefined : Exclude<keyof ProvidedModelType,
|
|
279
|
+
rendersMany<ProvidedModelType = undefined, ProvidedAttributeName 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, AttributeName extends ProvidedAttributeName extends undefined ? Exclude<keyof ActualDataType, DreamPropertiesToExclude> : ProvidedAttributeName & keyof ActualDataType = ProvidedAttributeName extends undefined ? Exclude<keyof ActualDataType, DreamPropertiesToExclude> : ProvidedAttributeName & keyof ActualDataType, AssociatedModelType = ActualDataType[AttributeName] extends (Dream | ViewModel)[] ? ActualDataType[AttributeName] extends (infer U)[] ? U : object : object, SerializerOptions = AssociatedModelType extends Dream ? {
|
|
271
280
|
serializerKey?: DreamOrViewModelSerializerKey<AssociatedModelType>;
|
|
272
281
|
} | {
|
|
273
282
|
serializer?: SerializerType;
|