@rvoh/dream 2.16.0 → 2.17.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/db/migration-helpers/DreamMigrationHelpers.js +6 -3
- package/dist/cjs/src/db/testDatabasePool.js +12 -3
- package/dist/cjs/src/decorators/class/STI.js +4 -32
- package/dist/cjs/src/decorators/class/SoftDelete.js +1 -1
- package/dist/cjs/src/decorators/field/association/BelongsTo.js +6 -1
- package/dist/cjs/src/decorators/field/association/HasMany.js +6 -1
- package/dist/cjs/src/decorators/field/association/HasOne.js +6 -1
- package/dist/cjs/src/decorators/field/association/shared.js +16 -0
- package/dist/cjs/src/dream/constants.js +1 -1
- package/dist/cjs/src/dream-app/helpers/importers/importModels.js +0 -2
- package/dist/cjs/src/dream-app/index.js +5 -6
- package/dist/cjs/src/helpers/cli/generateMigrationContent.js +5 -1
- package/dist/esm/src/db/migration-helpers/DreamMigrationHelpers.js +6 -3
- package/dist/esm/src/db/testDatabasePool.js +12 -3
- package/dist/esm/src/decorators/class/STI.js +4 -32
- package/dist/esm/src/decorators/class/SoftDelete.js +1 -1
- package/dist/esm/src/decorators/field/association/BelongsTo.js +6 -1
- package/dist/esm/src/decorators/field/association/HasMany.js +6 -1
- package/dist/esm/src/decorators/field/association/HasOne.js +6 -1
- package/dist/esm/src/decorators/field/association/shared.js +16 -0
- package/dist/esm/src/dream/constants.js +1 -1
- package/dist/esm/src/dream-app/helpers/importers/importModels.js +0 -2
- package/dist/esm/src/dream-app/index.js +5 -6
- package/dist/esm/src/helpers/cli/generateMigrationContent.js +5 -1
- package/dist/types/src/db/migration-helpers/DreamMigrationHelpers.d.ts +6 -3
- package/dist/types/src/db/testDatabasePool.d.ts +7 -0
- package/dist/types/src/decorators/class/STI.d.ts +1 -2
- package/dist/types/src/decorators/class/SoftDelete.d.ts +1 -1
- package/dist/types/src/decorators/field/association/shared.d.ts +4 -0
- package/dist/types/src/dream/constants.d.ts +1 -1
- package/dist/types/src/types/db.d.ts +1 -1
- package/dist/types/src/types/types/db.ts +1 -1
- package/docs/classes/db.DreamMigrationHelpers.html +17 -14
- package/docs/classes/db.KyselyQueryDriver.html +34 -34
- package/docs/classes/db.PostgresQueryDriver.html +35 -35
- package/docs/classes/db.QueryDriverBase.html +33 -33
- 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 +118 -118
- 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 +57 -57
- 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 +2 -2
- 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
|
@@ -7,9 +7,12 @@ export default class DreamMigrationHelpers {
|
|
|
7
7
|
* This method renames the table, its primary key index (`{tablename}_pkey`),
|
|
8
8
|
* and its primary key sequence (`{tablename}_id_seq`) to keep them in sync.
|
|
9
9
|
*
|
|
10
|
-
* The sequence rename
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* The sequence rename applies to both legacy `serial`/`bigserial` columns and
|
|
11
|
+
* modern `GENERATED ... AS IDENTITY` columns: identity columns are also backed
|
|
12
|
+
* by a `{tablename}_id_seq` sequence, so they need the same rename. It is
|
|
13
|
+
* skipped only for tables with UUID primary keys (which have no associated
|
|
14
|
+
* sequence). The primary key index is always renamed since PostgreSQL does not
|
|
15
|
+
* automatically rename it when the table is renamed.
|
|
13
16
|
*
|
|
14
17
|
* @param db - The Kysely database object passed into the migration up/down function
|
|
15
18
|
* @param from - The current name of the table to rename
|
|
@@ -58,11 +58,20 @@ const state = {
|
|
|
58
58
|
* probe so they always agree on the index range.
|
|
59
59
|
*/
|
|
60
60
|
export function testDatabasePoolSize(parallelTests) {
|
|
61
|
-
const n =
|
|
62
|
-
? Math.floor(parallelTests)
|
|
63
|
-
: 1;
|
|
61
|
+
const n = normalizeTestDatabaseParallelism(parallelTests);
|
|
64
62
|
return 2 * Math.max(1, n) + POOL_MARGIN;
|
|
65
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Normalize configured test parallelism to the minimum safe pool width input.
|
|
66
|
+
* Even a serial vitest run can overlap old/new worker processes, so missing,
|
|
67
|
+
* invalid, zero, or fractional-below-one values all mean "one active worker",
|
|
68
|
+
* not "disable the per-live-worker database pool".
|
|
69
|
+
*/
|
|
70
|
+
export function normalizeTestDatabaseParallelism(parallelTests) {
|
|
71
|
+
return typeof parallelTests === 'number' && Number.isFinite(parallelTests) && parallelTests > 0
|
|
72
|
+
? Math.max(1, Math.floor(parallelTests))
|
|
73
|
+
: 1;
|
|
74
|
+
}
|
|
66
75
|
/**
|
|
67
76
|
* The database name for a pool index given the un-suffixed base name. Index 1
|
|
68
77
|
* is the base itself (no suffix); higher indexes are `<base>_<index>`. This is
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import StiChildCannotDefineNewAssociations from '../../errors/sti/StiChildCannotDefineNewAssociations.js';
|
|
2
2
|
import StiChildIncompatibleWithReplicaSafeDecorator from '../../errors/sti/StiChildIncompatibleWithReplicaSafeDecorator.js';
|
|
3
3
|
import StiChildIncompatibleWithSoftDeleteDecorator from '../../errors/sti/StiChildIncompatibleWithSoftDeleteDecorator.js';
|
|
4
|
+
import { associationDeclarationNamesFromMetadata } from '../field/association/shared.js';
|
|
4
5
|
import { scopeImplementation } from '../static-method/Scope.js';
|
|
5
6
|
export const STI_SCOPE_NAME = 'dream:STI';
|
|
6
7
|
export default function STI(dreamClass) {
|
|
7
|
-
return function (stiChildClass) {
|
|
8
|
+
return function (stiChildClass, context) {
|
|
8
9
|
const baseClass = dreamClass['sti'].baseClass || dreamClass;
|
|
10
|
+
if (associationDeclarationNamesFromMetadata(context?.metadata).length)
|
|
11
|
+
throw new StiChildCannotDefineNewAssociations(baseClass, stiChildClass);
|
|
9
12
|
if (Object.getOwnPropertyDescriptor(stiChildClass, 'replicaSafe'))
|
|
10
13
|
throw new StiChildIncompatibleWithReplicaSafeDecorator(stiChildClass);
|
|
11
14
|
if (Object.getOwnPropertyDescriptor(stiChildClass, 'softDelete'))
|
|
@@ -26,34 +29,3 @@ export default function STI(dreamClass) {
|
|
|
26
29
|
scopeImplementation(stiChildClass, STI_SCOPE_NAME, { default: true });
|
|
27
30
|
};
|
|
28
31
|
}
|
|
29
|
-
export function validateStiChildAssociations(dreamClasses) {
|
|
30
|
-
dreamClasses.forEach(dreamClass => {
|
|
31
|
-
if (!dreamClass?.isDream)
|
|
32
|
-
return;
|
|
33
|
-
if (!dreamClass['sti'].active)
|
|
34
|
-
return;
|
|
35
|
-
const baseClass = dreamClass['sti'].baseClass;
|
|
36
|
-
if (!baseClass)
|
|
37
|
-
return;
|
|
38
|
-
if (hasAssociationsUnavailableOnBase(baseClass, dreamClass))
|
|
39
|
-
throw new StiChildCannotDefineNewAssociations(baseClass, dreamClass);
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
function hasAssociationsUnavailableOnBase(baseClass, stiChildClass) {
|
|
43
|
-
const baseAssociationKeys = associationKeysByTypeAndName(baseClass['associationMetadataByType']);
|
|
44
|
-
const childAssociationKeys = associationKeysByTypeAndName(stiChildClass['associationMetadataByType']);
|
|
45
|
-
for (const associationKey of childAssociationKeys) {
|
|
46
|
-
if (!baseAssociationKeys.has(associationKey))
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
function associationKeysByTypeAndName(associationMetadataByType) {
|
|
52
|
-
const res = new Set();
|
|
53
|
-
Object.entries(associationMetadataByType).forEach(([associationType, associations]) => {
|
|
54
|
-
associations.forEach((association) => {
|
|
55
|
-
res.add(`${associationType}:${association.as}`);
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
return res;
|
|
59
|
-
}
|
|
@@ -14,7 +14,7 @@ export const SOFT_DELETE_SCOPE_NAME = 'dream:SoftDelete';
|
|
|
14
14
|
* export async function up(db: Kysely<any>): Promise<void> {
|
|
15
15
|
* await db.schema
|
|
16
16
|
* .createTable('posts')
|
|
17
|
-
* .addColumn('id', '
|
|
17
|
+
* .addColumn('id', 'bigint', col => col.primaryKey().generatedByDefaultAsIdentity())
|
|
18
18
|
* .addColumn('deleted_at', 'timestamp', col => col.defaultTo(null))
|
|
19
19
|
* .addColumn('created_at', 'timestamp', col => col.notNull())
|
|
20
20
|
* .addColumn('updated_at', 'timestamp', col => col.notNull())
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import lookupModelByGlobalNameOrNames from '../../../dream-app/helpers/lookupModelByGlobalNameOrNames.js';
|
|
2
2
|
import ArrayTargetOnlyOnPolymorphicBelongsTo from '../../../errors/associations/ArrayTargetOnlyOnPolymorphicBelongsTo.js';
|
|
3
3
|
import { validatesImplementation } from '../validation/Validates.js';
|
|
4
|
-
import { applyGetterAndSetter, associationPrimaryKeyAccessors, blankAssociationsFactory, finalForeignKey, foreignKeyTypeField, } from './shared.js';
|
|
4
|
+
import { applyGetterAndSetter, associationPrimaryKeyAccessors, blankAssociationsFactory, finalForeignKey, foreignKeyTypeField, markAssociationDeclaration, } from './shared.js';
|
|
5
5
|
/**
|
|
6
6
|
* Establishes a "BelongsTo" association between the base dream
|
|
7
7
|
* and the child dream, where the base dream has a foreign key
|
|
@@ -56,6 +56,7 @@ import { applyGetterAndSetter, associationPrimaryKeyAccessors, blankAssociations
|
|
|
56
56
|
export default function BelongsTo(globalAssociationNameOrNames, opts = {}) {
|
|
57
57
|
const { on: foreignKey, optional = false, polymorphic = false, primaryKeyOverride = null, withoutDefaultScopes, } = opts;
|
|
58
58
|
return function (_, context) {
|
|
59
|
+
markAssociationDeclaration(context);
|
|
59
60
|
const key = context.name;
|
|
60
61
|
context.addInitializer(function () {
|
|
61
62
|
const target = this;
|
|
@@ -93,6 +94,10 @@ export default function BelongsTo(globalAssociationNameOrNames, opts = {}) {
|
|
|
93
94
|
return foreignKeyTypeField(foreignKey, dreamClass, partialAssociation);
|
|
94
95
|
},
|
|
95
96
|
};
|
|
97
|
+
if (dreamClass['isSTIChild']) {
|
|
98
|
+
applyGetterAndSetter(target, association, { isBelongsTo: true, foreignKeyBase: foreignKey });
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
96
101
|
if (!Object.getOwnPropertyDescriptor(dreamClass, 'associationMetadataByType'))
|
|
97
102
|
dreamClass['associationMetadataByType'] = blankAssociationsFactory(dreamClass);
|
|
98
103
|
dreamClass['associationMetadataByType']['belongsTo'].push(association);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import lookupModelByGlobalNameOrNames from '../../../dream-app/helpers/lookupModelByGlobalNameOrNames.js';
|
|
2
|
-
import { applyGetterAndSetter, associationPrimaryKeyAccessors, blankAssociationsFactory, finalForeignKey, foreignKeyTypeField, validateHasStatementArgs, } from './shared.js';
|
|
2
|
+
import { applyGetterAndSetter, associationPrimaryKeyAccessors, blankAssociationsFactory, finalForeignKey, foreignKeyTypeField, markAssociationDeclaration, validateHasStatementArgs, } from './shared.js';
|
|
3
3
|
/**
|
|
4
4
|
* Establishes a "HasMany" association between the base dream
|
|
5
5
|
* and the child dream, where the child dream has a foreign key
|
|
@@ -36,6 +36,7 @@ import { applyGetterAndSetter, associationPrimaryKeyAccessors, blankAssociations
|
|
|
36
36
|
export default function HasMany(globalAssociationNameOrNames, opts = {}) {
|
|
37
37
|
const { dependent, distinct, on: foreignKey, and, andNot, andAny, order, polymorphic = false, primaryKeyOverride = null, selfAnd, selfAndNot, source, through, withoutDefaultScopes, } = opts;
|
|
38
38
|
return function (_, context) {
|
|
39
|
+
markAssociationDeclaration(context);
|
|
39
40
|
const key = context.name;
|
|
40
41
|
context.addInitializer(function () {
|
|
41
42
|
const target = this;
|
|
@@ -86,6 +87,10 @@ export default function HasMany(globalAssociationNameOrNames, opts = {}) {
|
|
|
86
87
|
return foreignKeyTypeField(foreignKey, dreamClass, partialAssociation);
|
|
87
88
|
},
|
|
88
89
|
};
|
|
90
|
+
if (dreamClass['isSTIChild']) {
|
|
91
|
+
applyGetterAndSetter(target, association);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
89
94
|
if (!Object.getOwnPropertyDescriptor(dreamClass, 'associationMetadataByType'))
|
|
90
95
|
dreamClass['associationMetadataByType'] = blankAssociationsFactory(dreamClass);
|
|
91
96
|
dreamClass['associationMetadataByType']['hasMany'].push(association);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import lookupModelByGlobalNameOrNames from '../../../dream-app/helpers/lookupModelByGlobalNameOrNames.js';
|
|
2
|
-
import { applyGetterAndSetter, associationPrimaryKeyAccessors, blankAssociationsFactory, finalForeignKey, foreignKeyTypeField, validateHasStatementArgs, } from './shared.js';
|
|
2
|
+
import { applyGetterAndSetter, associationPrimaryKeyAccessors, blankAssociationsFactory, finalForeignKey, foreignKeyTypeField, markAssociationDeclaration, validateHasStatementArgs, } from './shared.js';
|
|
3
3
|
/**
|
|
4
4
|
* Establishes a "HasOne" association between the base dream
|
|
5
5
|
* and the child dream, where the child dream has a foreign key
|
|
@@ -34,6 +34,7 @@ import { applyGetterAndSetter, associationPrimaryKeyAccessors, blankAssociations
|
|
|
34
34
|
export default function HasOne(globalAssociationNameOrNames, opts = {}) {
|
|
35
35
|
const { dependent, on: foreignKey, and, andNot, andAny, polymorphic = false, primaryKeyOverride = null, selfAnd, selfAndNot, source, through, withoutDefaultScopes, } = opts;
|
|
36
36
|
return function (_, context) {
|
|
37
|
+
markAssociationDeclaration(context);
|
|
37
38
|
const key = context.name;
|
|
38
39
|
context.addInitializer(function () {
|
|
39
40
|
const target = this;
|
|
@@ -82,6 +83,10 @@ export default function HasOne(globalAssociationNameOrNames, opts = {}) {
|
|
|
82
83
|
return foreignKeyTypeField(foreignKey, dreamClass, partialAssociation);
|
|
83
84
|
},
|
|
84
85
|
};
|
|
86
|
+
if (dreamClass['isSTIChild']) {
|
|
87
|
+
applyGetterAndSetter(target, association);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
85
90
|
if (!Object.getOwnPropertyDescriptor(dreamClass, 'associationMetadataByType'))
|
|
86
91
|
dreamClass['associationMetadataByType'] = blankAssociationsFactory(dreamClass);
|
|
87
92
|
dreamClass['associationMetadataByType']['hasOne'].push(association);
|
|
@@ -8,6 +8,22 @@ import CannotDefineAssociationWithBothDependentAndRequiredAndClause from '../../
|
|
|
8
8
|
import camelize from '../../../helpers/camelize.js';
|
|
9
9
|
import freezeBaseClassArrayMap from '../../helpers/freezeBaseClassArrayMap.js';
|
|
10
10
|
import associationToGetterSetterProp from './associationToGetterSetterProp.js';
|
|
11
|
+
export const ASSOCIATION_DECLARATION_METADATA_KEY = Symbol('dream:associationDeclarationNames');
|
|
12
|
+
export function markAssociationDeclaration(context) {
|
|
13
|
+
const metadata = context.metadata;
|
|
14
|
+
if (!metadata)
|
|
15
|
+
return;
|
|
16
|
+
const existingDeclarationNames = Object.getOwnPropertyDescriptor(metadata, ASSOCIATION_DECLARATION_METADATA_KEY)?.value || [];
|
|
17
|
+
Object.defineProperty(metadata, ASSOCIATION_DECLARATION_METADATA_KEY, {
|
|
18
|
+
configurable: true,
|
|
19
|
+
value: [...existingDeclarationNames, context.name],
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
export function associationDeclarationNamesFromMetadata(metadata) {
|
|
23
|
+
if (!metadata)
|
|
24
|
+
return [];
|
|
25
|
+
return (Object.getOwnPropertyDescriptor(metadata, ASSOCIATION_DECLARATION_METADATA_KEY)?.value || []);
|
|
26
|
+
}
|
|
11
27
|
export function blankAssociationsFactory(dreamClass, { freeze = false, } = {}) {
|
|
12
28
|
// This pattern allows associations to be defined on a base STI class and on
|
|
13
29
|
// child STI classes. The new `associationsMap` property will be created
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const primaryKeyTypes = ['uuid7', 'uuid4', '
|
|
1
|
+
export const primaryKeyTypes = ['uuid7', 'uuid4', 'bigint', 'integer'];
|
|
2
2
|
export const TRIGRAM_OPERATORS = ['%', '<%', '<<%'];
|
|
3
3
|
export const RECURSIVE_SERIALIZATION_MAX_REPEATS = 4;
|
|
4
4
|
export const RECURSIVE_DESTROY_PRELOAD_MAX_REPEATS = RECURSIVE_SERIALIZATION_MAX_REPEATS;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import Dream from '../../../Dream.js';
|
|
2
|
-
import { validateStiChildAssociations } from '../../../decorators/class/STI.js';
|
|
3
2
|
import DreamMissingRequiredOverride from '../../../errors/DreamMissingRequiredOverride.js';
|
|
4
3
|
import DreamImporter from '../DreamImporter.js';
|
|
5
4
|
import globalModelKeyFromPath from '../globalModelKeyFromPath.js';
|
|
@@ -67,7 +66,6 @@ async function importModelsUncached(modelsPath, modelImportCb) {
|
|
|
67
66
|
}
|
|
68
67
|
}
|
|
69
68
|
}
|
|
70
|
-
validateStiChildAssociations(modelClasses.map(([, modelClass]) => modelClass));
|
|
71
69
|
_models = models;
|
|
72
70
|
return _models;
|
|
73
71
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as util from 'node:util';
|
|
2
|
-
import { claimedTestDatabaseIndexOrNull, claimTestDatabase, testDatabaseNameForIndex, } from '../db/testDatabasePool.js';
|
|
2
|
+
import { claimedTestDatabaseIndexOrNull, claimTestDatabase, normalizeTestDatabaseParallelism, testDatabaseNameForIndex, } from '../db/testDatabasePool.js';
|
|
3
3
|
import validateTable from '../db/validators/validateTable.js';
|
|
4
4
|
import Query from '../dream/Query.js';
|
|
5
5
|
import PostgresQueryDriver from '../dream/QueryDriver/Postgres.js';
|
|
@@ -208,7 +208,7 @@ A new key can also be generated from the CLI:
|
|
|
208
208
|
}
|
|
209
209
|
_parallelTests;
|
|
210
210
|
get parallelTests() {
|
|
211
|
-
return process.env.NODE_ENV === 'test' ? this._parallelTests : undefined;
|
|
211
|
+
return process.env.NODE_ENV === 'test' ? normalizeTestDatabaseParallelism(this._parallelTests) : undefined;
|
|
212
212
|
}
|
|
213
213
|
_unicodeNormalization = 'NFC';
|
|
214
214
|
get unicodeNormalization() {
|
|
@@ -218,7 +218,7 @@ A new key can also be generated from the CLI:
|
|
|
218
218
|
get paginationPageSize() {
|
|
219
219
|
return this._paginationPageSize;
|
|
220
220
|
}
|
|
221
|
-
_primaryKeyType = '
|
|
221
|
+
_primaryKeyType = 'bigint';
|
|
222
222
|
get primaryKeyType() {
|
|
223
223
|
return this._primaryKeyType;
|
|
224
224
|
}
|
|
@@ -276,7 +276,7 @@ A new key can also be generated from the CLI:
|
|
|
276
276
|
this._inflections = opts.inflections;
|
|
277
277
|
if (opts?.serializerCasing)
|
|
278
278
|
this._serializerCasing = opts.serializerCasing;
|
|
279
|
-
if (opts?.parallelTests)
|
|
279
|
+
if (opts?.parallelTests !== undefined)
|
|
280
280
|
this._parallelTests = opts.parallelTests;
|
|
281
281
|
this._paths = {
|
|
282
282
|
conf: opts?.paths?.conf || 'src/conf',
|
|
@@ -483,9 +483,8 @@ A new key can also be generated from the CLI:
|
|
|
483
483
|
};
|
|
484
484
|
break;
|
|
485
485
|
case 'parallelTests':
|
|
486
|
-
if (process.env.NODE_ENV === 'test'
|
|
486
|
+
if (process.env.NODE_ENV === 'test')
|
|
487
487
|
this._parallelTests = options;
|
|
488
|
-
}
|
|
489
488
|
break;
|
|
490
489
|
case 'unicodeNormalization':
|
|
491
490
|
this._unicodeNormalization = options;
|
|
@@ -361,8 +361,12 @@ function generateIdStr({ primaryKeyType }) {
|
|
|
361
361
|
.primaryKey()
|
|
362
362
|
.defaultTo(sql\`uuid_generate_v4()\`),
|
|
363
363
|
)`;
|
|
364
|
+
case 'integer':
|
|
365
|
+
return `.addColumn('id', 'integer', col => col.primaryKey().generatedByDefaultAsIdentity())`;
|
|
366
|
+
case 'bigint':
|
|
367
|
+
case 'bigserial':
|
|
364
368
|
default:
|
|
365
|
-
return `.addColumn('id', '
|
|
369
|
+
return `.addColumn('id', 'bigint', col => col.primaryKey().generatedByDefaultAsIdentity())`;
|
|
366
370
|
}
|
|
367
371
|
}
|
|
368
372
|
/**
|
|
@@ -7,9 +7,12 @@ export default class DreamMigrationHelpers {
|
|
|
7
7
|
* This method renames the table, its primary key index (`{tablename}_pkey`),
|
|
8
8
|
* and its primary key sequence (`{tablename}_id_seq`) to keep them in sync.
|
|
9
9
|
*
|
|
10
|
-
* The sequence rename
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* The sequence rename applies to both legacy `serial`/`bigserial` columns and
|
|
11
|
+
* modern `GENERATED ... AS IDENTITY` columns: identity columns are also backed
|
|
12
|
+
* by a `{tablename}_id_seq` sequence, so they need the same rename. It is
|
|
13
|
+
* skipped only for tables with UUID primary keys (which have no associated
|
|
14
|
+
* sequence). The primary key index is always renamed since PostgreSQL does not
|
|
15
|
+
* automatically rename it when the table is renamed.
|
|
13
16
|
*
|
|
14
17
|
* @param db - The Kysely database object passed into the migration up/down function
|
|
15
18
|
* @param from - The current name of the table to rename
|
|
@@ -58,11 +58,20 @@ const state = {
|
|
|
58
58
|
* probe so they always agree on the index range.
|
|
59
59
|
*/
|
|
60
60
|
export function testDatabasePoolSize(parallelTests) {
|
|
61
|
-
const n =
|
|
62
|
-
? Math.floor(parallelTests)
|
|
63
|
-
: 1;
|
|
61
|
+
const n = normalizeTestDatabaseParallelism(parallelTests);
|
|
64
62
|
return 2 * Math.max(1, n) + POOL_MARGIN;
|
|
65
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Normalize configured test parallelism to the minimum safe pool width input.
|
|
66
|
+
* Even a serial vitest run can overlap old/new worker processes, so missing,
|
|
67
|
+
* invalid, zero, or fractional-below-one values all mean "one active worker",
|
|
68
|
+
* not "disable the per-live-worker database pool".
|
|
69
|
+
*/
|
|
70
|
+
export function normalizeTestDatabaseParallelism(parallelTests) {
|
|
71
|
+
return typeof parallelTests === 'number' && Number.isFinite(parallelTests) && parallelTests > 0
|
|
72
|
+
? Math.max(1, Math.floor(parallelTests))
|
|
73
|
+
: 1;
|
|
74
|
+
}
|
|
66
75
|
/**
|
|
67
76
|
* The database name for a pool index given the un-suffixed base name. Index 1
|
|
68
77
|
* is the base itself (no suffix); higher indexes are `<base>_<index>`. This is
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import StiChildCannotDefineNewAssociations from '../../errors/sti/StiChildCannotDefineNewAssociations.js';
|
|
2
2
|
import StiChildIncompatibleWithReplicaSafeDecorator from '../../errors/sti/StiChildIncompatibleWithReplicaSafeDecorator.js';
|
|
3
3
|
import StiChildIncompatibleWithSoftDeleteDecorator from '../../errors/sti/StiChildIncompatibleWithSoftDeleteDecorator.js';
|
|
4
|
+
import { associationDeclarationNamesFromMetadata } from '../field/association/shared.js';
|
|
4
5
|
import { scopeImplementation } from '../static-method/Scope.js';
|
|
5
6
|
export const STI_SCOPE_NAME = 'dream:STI';
|
|
6
7
|
export default function STI(dreamClass) {
|
|
7
|
-
return function (stiChildClass) {
|
|
8
|
+
return function (stiChildClass, context) {
|
|
8
9
|
const baseClass = dreamClass['sti'].baseClass || dreamClass;
|
|
10
|
+
if (associationDeclarationNamesFromMetadata(context?.metadata).length)
|
|
11
|
+
throw new StiChildCannotDefineNewAssociations(baseClass, stiChildClass);
|
|
9
12
|
if (Object.getOwnPropertyDescriptor(stiChildClass, 'replicaSafe'))
|
|
10
13
|
throw new StiChildIncompatibleWithReplicaSafeDecorator(stiChildClass);
|
|
11
14
|
if (Object.getOwnPropertyDescriptor(stiChildClass, 'softDelete'))
|
|
@@ -26,34 +29,3 @@ export default function STI(dreamClass) {
|
|
|
26
29
|
scopeImplementation(stiChildClass, STI_SCOPE_NAME, { default: true });
|
|
27
30
|
};
|
|
28
31
|
}
|
|
29
|
-
export function validateStiChildAssociations(dreamClasses) {
|
|
30
|
-
dreamClasses.forEach(dreamClass => {
|
|
31
|
-
if (!dreamClass?.isDream)
|
|
32
|
-
return;
|
|
33
|
-
if (!dreamClass['sti'].active)
|
|
34
|
-
return;
|
|
35
|
-
const baseClass = dreamClass['sti'].baseClass;
|
|
36
|
-
if (!baseClass)
|
|
37
|
-
return;
|
|
38
|
-
if (hasAssociationsUnavailableOnBase(baseClass, dreamClass))
|
|
39
|
-
throw new StiChildCannotDefineNewAssociations(baseClass, dreamClass);
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
function hasAssociationsUnavailableOnBase(baseClass, stiChildClass) {
|
|
43
|
-
const baseAssociationKeys = associationKeysByTypeAndName(baseClass['associationMetadataByType']);
|
|
44
|
-
const childAssociationKeys = associationKeysByTypeAndName(stiChildClass['associationMetadataByType']);
|
|
45
|
-
for (const associationKey of childAssociationKeys) {
|
|
46
|
-
if (!baseAssociationKeys.has(associationKey))
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
function associationKeysByTypeAndName(associationMetadataByType) {
|
|
52
|
-
const res = new Set();
|
|
53
|
-
Object.entries(associationMetadataByType).forEach(([associationType, associations]) => {
|
|
54
|
-
associations.forEach((association) => {
|
|
55
|
-
res.add(`${associationType}:${association.as}`);
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
return res;
|
|
59
|
-
}
|
|
@@ -14,7 +14,7 @@ export const SOFT_DELETE_SCOPE_NAME = 'dream:SoftDelete';
|
|
|
14
14
|
* export async function up(db: Kysely<any>): Promise<void> {
|
|
15
15
|
* await db.schema
|
|
16
16
|
* .createTable('posts')
|
|
17
|
-
* .addColumn('id', '
|
|
17
|
+
* .addColumn('id', 'bigint', col => col.primaryKey().generatedByDefaultAsIdentity())
|
|
18
18
|
* .addColumn('deleted_at', 'timestamp', col => col.defaultTo(null))
|
|
19
19
|
* .addColumn('created_at', 'timestamp', col => col.notNull())
|
|
20
20
|
* .addColumn('updated_at', 'timestamp', col => col.notNull())
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import lookupModelByGlobalNameOrNames from '../../../dream-app/helpers/lookupModelByGlobalNameOrNames.js';
|
|
2
2
|
import ArrayTargetOnlyOnPolymorphicBelongsTo from '../../../errors/associations/ArrayTargetOnlyOnPolymorphicBelongsTo.js';
|
|
3
3
|
import { validatesImplementation } from '../validation/Validates.js';
|
|
4
|
-
import { applyGetterAndSetter, associationPrimaryKeyAccessors, blankAssociationsFactory, finalForeignKey, foreignKeyTypeField, } from './shared.js';
|
|
4
|
+
import { applyGetterAndSetter, associationPrimaryKeyAccessors, blankAssociationsFactory, finalForeignKey, foreignKeyTypeField, markAssociationDeclaration, } from './shared.js';
|
|
5
5
|
/**
|
|
6
6
|
* Establishes a "BelongsTo" association between the base dream
|
|
7
7
|
* and the child dream, where the base dream has a foreign key
|
|
@@ -56,6 +56,7 @@ import { applyGetterAndSetter, associationPrimaryKeyAccessors, blankAssociations
|
|
|
56
56
|
export default function BelongsTo(globalAssociationNameOrNames, opts = {}) {
|
|
57
57
|
const { on: foreignKey, optional = false, polymorphic = false, primaryKeyOverride = null, withoutDefaultScopes, } = opts;
|
|
58
58
|
return function (_, context) {
|
|
59
|
+
markAssociationDeclaration(context);
|
|
59
60
|
const key = context.name;
|
|
60
61
|
context.addInitializer(function () {
|
|
61
62
|
const target = this;
|
|
@@ -93,6 +94,10 @@ export default function BelongsTo(globalAssociationNameOrNames, opts = {}) {
|
|
|
93
94
|
return foreignKeyTypeField(foreignKey, dreamClass, partialAssociation);
|
|
94
95
|
},
|
|
95
96
|
};
|
|
97
|
+
if (dreamClass['isSTIChild']) {
|
|
98
|
+
applyGetterAndSetter(target, association, { isBelongsTo: true, foreignKeyBase: foreignKey });
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
96
101
|
if (!Object.getOwnPropertyDescriptor(dreamClass, 'associationMetadataByType'))
|
|
97
102
|
dreamClass['associationMetadataByType'] = blankAssociationsFactory(dreamClass);
|
|
98
103
|
dreamClass['associationMetadataByType']['belongsTo'].push(association);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import lookupModelByGlobalNameOrNames from '../../../dream-app/helpers/lookupModelByGlobalNameOrNames.js';
|
|
2
|
-
import { applyGetterAndSetter, associationPrimaryKeyAccessors, blankAssociationsFactory, finalForeignKey, foreignKeyTypeField, validateHasStatementArgs, } from './shared.js';
|
|
2
|
+
import { applyGetterAndSetter, associationPrimaryKeyAccessors, blankAssociationsFactory, finalForeignKey, foreignKeyTypeField, markAssociationDeclaration, validateHasStatementArgs, } from './shared.js';
|
|
3
3
|
/**
|
|
4
4
|
* Establishes a "HasMany" association between the base dream
|
|
5
5
|
* and the child dream, where the child dream has a foreign key
|
|
@@ -36,6 +36,7 @@ import { applyGetterAndSetter, associationPrimaryKeyAccessors, blankAssociations
|
|
|
36
36
|
export default function HasMany(globalAssociationNameOrNames, opts = {}) {
|
|
37
37
|
const { dependent, distinct, on: foreignKey, and, andNot, andAny, order, polymorphic = false, primaryKeyOverride = null, selfAnd, selfAndNot, source, through, withoutDefaultScopes, } = opts;
|
|
38
38
|
return function (_, context) {
|
|
39
|
+
markAssociationDeclaration(context);
|
|
39
40
|
const key = context.name;
|
|
40
41
|
context.addInitializer(function () {
|
|
41
42
|
const target = this;
|
|
@@ -86,6 +87,10 @@ export default function HasMany(globalAssociationNameOrNames, opts = {}) {
|
|
|
86
87
|
return foreignKeyTypeField(foreignKey, dreamClass, partialAssociation);
|
|
87
88
|
},
|
|
88
89
|
};
|
|
90
|
+
if (dreamClass['isSTIChild']) {
|
|
91
|
+
applyGetterAndSetter(target, association);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
89
94
|
if (!Object.getOwnPropertyDescriptor(dreamClass, 'associationMetadataByType'))
|
|
90
95
|
dreamClass['associationMetadataByType'] = blankAssociationsFactory(dreamClass);
|
|
91
96
|
dreamClass['associationMetadataByType']['hasMany'].push(association);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import lookupModelByGlobalNameOrNames from '../../../dream-app/helpers/lookupModelByGlobalNameOrNames.js';
|
|
2
|
-
import { applyGetterAndSetter, associationPrimaryKeyAccessors, blankAssociationsFactory, finalForeignKey, foreignKeyTypeField, validateHasStatementArgs, } from './shared.js';
|
|
2
|
+
import { applyGetterAndSetter, associationPrimaryKeyAccessors, blankAssociationsFactory, finalForeignKey, foreignKeyTypeField, markAssociationDeclaration, validateHasStatementArgs, } from './shared.js';
|
|
3
3
|
/**
|
|
4
4
|
* Establishes a "HasOne" association between the base dream
|
|
5
5
|
* and the child dream, where the child dream has a foreign key
|
|
@@ -34,6 +34,7 @@ import { applyGetterAndSetter, associationPrimaryKeyAccessors, blankAssociations
|
|
|
34
34
|
export default function HasOne(globalAssociationNameOrNames, opts = {}) {
|
|
35
35
|
const { dependent, on: foreignKey, and, andNot, andAny, polymorphic = false, primaryKeyOverride = null, selfAnd, selfAndNot, source, through, withoutDefaultScopes, } = opts;
|
|
36
36
|
return function (_, context) {
|
|
37
|
+
markAssociationDeclaration(context);
|
|
37
38
|
const key = context.name;
|
|
38
39
|
context.addInitializer(function () {
|
|
39
40
|
const target = this;
|
|
@@ -82,6 +83,10 @@ export default function HasOne(globalAssociationNameOrNames, opts = {}) {
|
|
|
82
83
|
return foreignKeyTypeField(foreignKey, dreamClass, partialAssociation);
|
|
83
84
|
},
|
|
84
85
|
};
|
|
86
|
+
if (dreamClass['isSTIChild']) {
|
|
87
|
+
applyGetterAndSetter(target, association);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
85
90
|
if (!Object.getOwnPropertyDescriptor(dreamClass, 'associationMetadataByType'))
|
|
86
91
|
dreamClass['associationMetadataByType'] = blankAssociationsFactory(dreamClass);
|
|
87
92
|
dreamClass['associationMetadataByType']['hasOne'].push(association);
|
|
@@ -8,6 +8,22 @@ import CannotDefineAssociationWithBothDependentAndRequiredAndClause from '../../
|
|
|
8
8
|
import camelize from '../../../helpers/camelize.js';
|
|
9
9
|
import freezeBaseClassArrayMap from '../../helpers/freezeBaseClassArrayMap.js';
|
|
10
10
|
import associationToGetterSetterProp from './associationToGetterSetterProp.js';
|
|
11
|
+
export const ASSOCIATION_DECLARATION_METADATA_KEY = Symbol('dream:associationDeclarationNames');
|
|
12
|
+
export function markAssociationDeclaration(context) {
|
|
13
|
+
const metadata = context.metadata;
|
|
14
|
+
if (!metadata)
|
|
15
|
+
return;
|
|
16
|
+
const existingDeclarationNames = Object.getOwnPropertyDescriptor(metadata, ASSOCIATION_DECLARATION_METADATA_KEY)?.value || [];
|
|
17
|
+
Object.defineProperty(metadata, ASSOCIATION_DECLARATION_METADATA_KEY, {
|
|
18
|
+
configurable: true,
|
|
19
|
+
value: [...existingDeclarationNames, context.name],
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
export function associationDeclarationNamesFromMetadata(metadata) {
|
|
23
|
+
if (!metadata)
|
|
24
|
+
return [];
|
|
25
|
+
return (Object.getOwnPropertyDescriptor(metadata, ASSOCIATION_DECLARATION_METADATA_KEY)?.value || []);
|
|
26
|
+
}
|
|
11
27
|
export function blankAssociationsFactory(dreamClass, { freeze = false, } = {}) {
|
|
12
28
|
// This pattern allows associations to be defined on a base STI class and on
|
|
13
29
|
// child STI classes. The new `associationsMap` property will be created
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const primaryKeyTypes = ['uuid7', 'uuid4', '
|
|
1
|
+
export const primaryKeyTypes = ['uuid7', 'uuid4', 'bigint', 'integer'];
|
|
2
2
|
export const TRIGRAM_OPERATORS = ['%', '<%', '<<%'];
|
|
3
3
|
export const RECURSIVE_SERIALIZATION_MAX_REPEATS = 4;
|
|
4
4
|
export const RECURSIVE_DESTROY_PRELOAD_MAX_REPEATS = RECURSIVE_SERIALIZATION_MAX_REPEATS;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import Dream from '../../../Dream.js';
|
|
2
|
-
import { validateStiChildAssociations } from '../../../decorators/class/STI.js';
|
|
3
2
|
import DreamMissingRequiredOverride from '../../../errors/DreamMissingRequiredOverride.js';
|
|
4
3
|
import DreamImporter from '../DreamImporter.js';
|
|
5
4
|
import globalModelKeyFromPath from '../globalModelKeyFromPath.js';
|
|
@@ -67,7 +66,6 @@ async function importModelsUncached(modelsPath, modelImportCb) {
|
|
|
67
66
|
}
|
|
68
67
|
}
|
|
69
68
|
}
|
|
70
|
-
validateStiChildAssociations(modelClasses.map(([, modelClass]) => modelClass));
|
|
71
69
|
_models = models;
|
|
72
70
|
return _models;
|
|
73
71
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as util from 'node:util';
|
|
2
|
-
import { claimedTestDatabaseIndexOrNull, claimTestDatabase, testDatabaseNameForIndex, } from '../db/testDatabasePool.js';
|
|
2
|
+
import { claimedTestDatabaseIndexOrNull, claimTestDatabase, normalizeTestDatabaseParallelism, testDatabaseNameForIndex, } from '../db/testDatabasePool.js';
|
|
3
3
|
import validateTable from '../db/validators/validateTable.js';
|
|
4
4
|
import Query from '../dream/Query.js';
|
|
5
5
|
import PostgresQueryDriver from '../dream/QueryDriver/Postgres.js';
|
|
@@ -208,7 +208,7 @@ A new key can also be generated from the CLI:
|
|
|
208
208
|
}
|
|
209
209
|
_parallelTests;
|
|
210
210
|
get parallelTests() {
|
|
211
|
-
return process.env.NODE_ENV === 'test' ? this._parallelTests : undefined;
|
|
211
|
+
return process.env.NODE_ENV === 'test' ? normalizeTestDatabaseParallelism(this._parallelTests) : undefined;
|
|
212
212
|
}
|
|
213
213
|
_unicodeNormalization = 'NFC';
|
|
214
214
|
get unicodeNormalization() {
|
|
@@ -218,7 +218,7 @@ A new key can also be generated from the CLI:
|
|
|
218
218
|
get paginationPageSize() {
|
|
219
219
|
return this._paginationPageSize;
|
|
220
220
|
}
|
|
221
|
-
_primaryKeyType = '
|
|
221
|
+
_primaryKeyType = 'bigint';
|
|
222
222
|
get primaryKeyType() {
|
|
223
223
|
return this._primaryKeyType;
|
|
224
224
|
}
|
|
@@ -276,7 +276,7 @@ A new key can also be generated from the CLI:
|
|
|
276
276
|
this._inflections = opts.inflections;
|
|
277
277
|
if (opts?.serializerCasing)
|
|
278
278
|
this._serializerCasing = opts.serializerCasing;
|
|
279
|
-
if (opts?.parallelTests)
|
|
279
|
+
if (opts?.parallelTests !== undefined)
|
|
280
280
|
this._parallelTests = opts.parallelTests;
|
|
281
281
|
this._paths = {
|
|
282
282
|
conf: opts?.paths?.conf || 'src/conf',
|
|
@@ -483,9 +483,8 @@ A new key can also be generated from the CLI:
|
|
|
483
483
|
};
|
|
484
484
|
break;
|
|
485
485
|
case 'parallelTests':
|
|
486
|
-
if (process.env.NODE_ENV === 'test'
|
|
486
|
+
if (process.env.NODE_ENV === 'test')
|
|
487
487
|
this._parallelTests = options;
|
|
488
|
-
}
|
|
489
488
|
break;
|
|
490
489
|
case 'unicodeNormalization':
|
|
491
490
|
this._unicodeNormalization = options;
|
|
@@ -361,8 +361,12 @@ function generateIdStr({ primaryKeyType }) {
|
|
|
361
361
|
.primaryKey()
|
|
362
362
|
.defaultTo(sql\`uuid_generate_v4()\`),
|
|
363
363
|
)`;
|
|
364
|
+
case 'integer':
|
|
365
|
+
return `.addColumn('id', 'integer', col => col.primaryKey().generatedByDefaultAsIdentity())`;
|
|
366
|
+
case 'bigint':
|
|
367
|
+
case 'bigserial':
|
|
364
368
|
default:
|
|
365
|
-
return `.addColumn('id', '
|
|
369
|
+
return `.addColumn('id', 'bigint', col => col.primaryKey().generatedByDefaultAsIdentity())`;
|
|
366
370
|
}
|
|
367
371
|
}
|
|
368
372
|
/**
|
|
@@ -6,9 +6,12 @@ export default class DreamMigrationHelpers {
|
|
|
6
6
|
* This method renames the table, its primary key index (`{tablename}_pkey`),
|
|
7
7
|
* and its primary key sequence (`{tablename}_id_seq`) to keep them in sync.
|
|
8
8
|
*
|
|
9
|
-
* The sequence rename
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* The sequence rename applies to both legacy `serial`/`bigserial` columns and
|
|
10
|
+
* modern `GENERATED ... AS IDENTITY` columns: identity columns are also backed
|
|
11
|
+
* by a `{tablename}_id_seq` sequence, so they need the same rename. It is
|
|
12
|
+
* skipped only for tables with UUID primary keys (which have no associated
|
|
13
|
+
* sequence). The primary key index is always renamed since PostgreSQL does not
|
|
14
|
+
* automatically rename it when the table is renamed.
|
|
12
15
|
*
|
|
13
16
|
* @param db - The Kysely database object passed into the migration up/down function
|
|
14
17
|
* @param from - The current name of the table to rename
|
|
@@ -5,6 +5,13 @@ import type DreamApp from '../dream-app/index.js';
|
|
|
5
5
|
* probe so they always agree on the index range.
|
|
6
6
|
*/
|
|
7
7
|
export declare function testDatabasePoolSize(parallelTests: number | undefined): number;
|
|
8
|
+
/**
|
|
9
|
+
* Normalize configured test parallelism to the minimum safe pool width input.
|
|
10
|
+
* Even a serial vitest run can overlap old/new worker processes, so missing,
|
|
11
|
+
* invalid, zero, or fractional-below-one values all mean "one active worker",
|
|
12
|
+
* not "disable the per-live-worker database pool".
|
|
13
|
+
*/
|
|
14
|
+
export declare function normalizeTestDatabaseParallelism(parallelTests: number | undefined): number;
|
|
8
15
|
/**
|
|
9
16
|
* The database name for a pool index given the un-suffixed base name. Index 1
|
|
10
17
|
* is the base itself (no suffix); higher indexes are `<base>_<index>`. This is
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import Dream from '../../Dream.js';
|
|
2
2
|
export declare const STI_SCOPE_NAME = "dream:STI";
|
|
3
|
-
export default function STI(dreamClass: typeof Dream): (stiChildClass: typeof Dream) => void;
|
|
4
|
-
export declare function validateStiChildAssociations(dreamClasses: (typeof Dream)[]): void;
|
|
3
|
+
export default function STI(dreamClass: typeof Dream): (stiChildClass: typeof Dream, context?: ClassDecoratorContext) => void;
|
|
@@ -13,7 +13,7 @@ export declare const SOFT_DELETE_SCOPE_NAME = "dream:SoftDelete";
|
|
|
13
13
|
* export async function up(db: Kysely<any>): Promise<void> {
|
|
14
14
|
* await db.schema
|
|
15
15
|
* .createTable('posts')
|
|
16
|
-
* .addColumn('id', '
|
|
16
|
+
* .addColumn('id', 'bigint', col => col.primaryKey().generatedByDefaultAsIdentity())
|
|
17
17
|
* .addColumn('deleted_at', 'timestamp', col => col.defaultTo(null))
|
|
18
18
|
* .addColumn('created_at', 'timestamp', col => col.notNull())
|
|
19
19
|
* .addColumn('updated_at', 'timestamp', col => col.notNull())
|