@rvoh/dream 2.18.1 → 2.20.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 +186 -0
- package/dist/cjs/src/db/DreamDbConnection.js +6 -0
- package/dist/cjs/src/decorators/class/SoftDelete.js +12 -0
- package/dist/cjs/src/dream/DreamClassTransactionBuilder.js +113 -0
- package/dist/cjs/src/dream/Query.js +148 -0
- package/dist/cjs/src/dream/QueryDriver/Base.js +84 -0
- package/dist/cjs/src/dream/QueryDriver/Kysely.js +420 -81
- package/dist/cjs/src/dream/internal/filterRowToKnownColumns.js +41 -0
- package/dist/cjs/src/dream/internal/saveDream.js +6 -1
- package/dist/cjs/src/dream/internal/sqlResultToDreamInstance.js +9 -2
- package/dist/cjs/src/errors/CannotNamespaceAssociationFilterToAnotherTable.js +27 -0
- package/dist/cjs/src/errors/schema-builder/CannotIgnoreAssociationColumn.js +38 -0
- package/dist/cjs/src/errors/schema-builder/CannotIgnoreEncryptedColumn.js +20 -0
- package/dist/cjs/src/errors/schema-builder/CannotIgnorePrimaryKey.js +18 -0
- package/dist/cjs/src/errors/schema-builder/CannotIgnoreSoftDeleteColumn.js +21 -0
- package/dist/cjs/src/errors/schema-builder/CannotIgnoreSortablePositionColumn.js +20 -0
- package/dist/cjs/src/errors/schema-builder/CannotIgnoreSortableScopeColumn.js +24 -0
- package/dist/cjs/src/errors/schema-builder/CannotIgnoreStiTypeColumn.js +17 -0
- package/dist/cjs/src/errors/schema-builder/ConflictingIgnoredColumns.js +27 -0
- package/dist/cjs/src/errors/schema-builder/IgnoredColumnMustBeCamelCase.js +21 -0
- package/dist/cjs/src/helpers/cli/ASTConnectionBuilder.js +38 -1
- package/dist/cjs/src/helpers/cli/ASTKyselyCodegenEnhancer.js +60 -0
- package/dist/cjs/src/helpers/cli/generateMigrationContent.js +21 -13
- package/dist/cjs/src/helpers/cli/resolveIgnoredColumns.js +198 -0
- package/dist/esm/src/Dream.js +186 -0
- package/dist/esm/src/db/DreamDbConnection.js +6 -0
- package/dist/esm/src/decorators/class/SoftDelete.js +12 -0
- package/dist/esm/src/dream/DreamClassTransactionBuilder.js +113 -0
- package/dist/esm/src/dream/Query.js +148 -0
- package/dist/esm/src/dream/QueryDriver/Base.js +84 -0
- package/dist/esm/src/dream/QueryDriver/Kysely.js +420 -81
- package/dist/esm/src/dream/internal/filterRowToKnownColumns.js +41 -0
- package/dist/esm/src/dream/internal/saveDream.js +6 -1
- package/dist/esm/src/dream/internal/sqlResultToDreamInstance.js +9 -2
- package/dist/esm/src/errors/CannotNamespaceAssociationFilterToAnotherTable.js +27 -0
- package/dist/esm/src/errors/schema-builder/CannotIgnoreAssociationColumn.js +38 -0
- package/dist/esm/src/errors/schema-builder/CannotIgnoreEncryptedColumn.js +20 -0
- package/dist/esm/src/errors/schema-builder/CannotIgnorePrimaryKey.js +18 -0
- package/dist/esm/src/errors/schema-builder/CannotIgnoreSoftDeleteColumn.js +21 -0
- package/dist/esm/src/errors/schema-builder/CannotIgnoreSortablePositionColumn.js +20 -0
- package/dist/esm/src/errors/schema-builder/CannotIgnoreSortableScopeColumn.js +24 -0
- package/dist/esm/src/errors/schema-builder/CannotIgnoreStiTypeColumn.js +17 -0
- package/dist/esm/src/errors/schema-builder/ConflictingIgnoredColumns.js +27 -0
- package/dist/esm/src/errors/schema-builder/IgnoredColumnMustBeCamelCase.js +21 -0
- package/dist/esm/src/helpers/cli/ASTConnectionBuilder.js +38 -1
- package/dist/esm/src/helpers/cli/ASTKyselyCodegenEnhancer.js +60 -0
- package/dist/esm/src/helpers/cli/generateMigrationContent.js +21 -13
- package/dist/esm/src/helpers/cli/resolveIgnoredColumns.js +198 -0
- package/dist/types/src/Dream.d.ts +178 -4
- package/dist/types/src/decorators/class/SoftDelete.d.ts +12 -0
- package/dist/types/src/dream/DreamClassTransactionBuilder.d.ts +103 -0
- package/dist/types/src/dream/Query.d.ts +138 -0
- package/dist/types/src/dream/QueryDriver/Base.d.ts +69 -0
- package/dist/types/src/dream/QueryDriver/Kysely.d.ts +187 -8
- package/dist/types/src/dream/internal/filterRowToKnownColumns.d.ts +30 -0
- package/dist/types/src/errors/CannotNamespaceAssociationFilterToAnotherTable.d.ts +8 -0
- package/dist/types/src/errors/schema-builder/CannotIgnoreAssociationColumn.d.ts +12 -0
- package/dist/types/src/errors/schema-builder/CannotIgnoreEncryptedColumn.d.ts +7 -0
- package/dist/types/src/errors/schema-builder/CannotIgnorePrimaryKey.d.ts +6 -0
- package/dist/types/src/errors/schema-builder/CannotIgnoreSoftDeleteColumn.d.ts +7 -0
- package/dist/types/src/errors/schema-builder/CannotIgnoreSortablePositionColumn.d.ts +7 -0
- package/dist/types/src/errors/schema-builder/CannotIgnoreSortableScopeColumn.d.ts +8 -0
- package/dist/types/src/errors/schema-builder/CannotIgnoreStiTypeColumn.d.ts +6 -0
- package/dist/types/src/errors/schema-builder/ConflictingIgnoredColumns.d.ts +7 -0
- package/dist/types/src/errors/schema-builder/IgnoredColumnMustBeCamelCase.d.ts +7 -0
- package/dist/types/src/helpers/cli/ASTConnectionBuilder.d.ts +20 -0
- package/dist/types/src/helpers/cli/ASTKyselyCodegenEnhancer.d.ts +13 -0
- package/dist/types/src/helpers/cli/resolveIgnoredColumns.d.ts +41 -0
- package/dist/types/src/types/associations/shared.d.ts +4 -1
- package/dist/types/src/types/dream.d.ts +17 -0
- package/dist/types/src/types/types/associations/shared.ts +10 -1
- package/dist/types/src/types/types/dream.ts +53 -0
- package/dist/types/src/types/types/variadic.ts +179 -140
- package/dist/types/src/types/variadic.d.ts +15 -10
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/db.DreamMigrationHelpers.html +11 -11
- package/docs/classes/db.KyselyQueryDriver.html +96 -34
- package/docs/classes/db.PostgresQueryDriver.html +97 -35
- package/docs/classes/db.QueryDriverBase.html +77 -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 +247 -119
- 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 +155 -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 +12 -1
- package/docs/functions/utils.camelize.html +1 -1
- package/docs/functions/utils.capitalize.html +1 -1
- package/docs/functions/utils.cloneDeepSafe.html +1 -1
- package/docs/functions/utils.compact.html +1 -1
- package/docs/functions/utils.groupBy.html +1 -1
- package/docs/functions/utils.hyphenize.html +1 -1
- package/docs/functions/utils.intersection.html +1 -1
- package/docs/functions/utils.isEmpty.html +1 -1
- package/docs/functions/utils.normalizeUnicode.html +1 -1
- package/docs/functions/utils.pascalize.html +1 -1
- package/docs/functions/utils.percent.html +1 -1
- package/docs/functions/utils.range.html +1 -1
- package/docs/functions/utils.round.html +1 -1
- package/docs/functions/utils.sanitizeString.html +1 -1
- package/docs/functions/utils.snakeify.html +1 -1
- package/docs/functions/utils.sort.html +1 -1
- package/docs/functions/utils.sortBy.html +1 -1
- package/docs/functions/utils.sortObjectByKey.html +1 -1
- package/docs/functions/utils.sortObjectByValue.html +1 -1
- package/docs/functions/utils.uncapitalize.html +1 -1
- package/docs/functions/utils.uniq.html +1 -1
- package/docs/hierarchy.html +1 -1
- package/docs/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 +3 -3
- package/dist/cjs/src/dream/internal/associations/throughAssociationHasOptionsBesidesThroughAndSource.js +0 -11
- package/dist/cjs/src/errors/associations/ThroughAssociationConditionsIncompatibleWithThroughAssociationSource.js +0 -17
- package/dist/esm/src/dream/internal/associations/throughAssociationHasOptionsBesidesThroughAndSource.js +0 -11
- package/dist/esm/src/errors/associations/ThroughAssociationConditionsIncompatibleWithThroughAssociationSource.js +0 -17
- package/dist/types/src/dream/internal/associations/throughAssociationHasOptionsBesidesThroughAndSource.d.ts +0 -13
- package/dist/types/src/errors/associations/ThroughAssociationConditionsIncompatibleWithThroughAssociationSource.d.ts +0 -12
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import scopeArray from '../../decorators/field/sortable/helpers/scopeArray.js';
|
|
2
|
+
import CannotIgnoreAssociationColumn from '../../errors/schema-builder/CannotIgnoreAssociationColumn.js';
|
|
3
|
+
import CannotIgnoreEncryptedColumn from '../../errors/schema-builder/CannotIgnoreEncryptedColumn.js';
|
|
4
|
+
import CannotIgnorePrimaryKey from '../../errors/schema-builder/CannotIgnorePrimaryKey.js';
|
|
5
|
+
import CannotIgnoreSoftDeleteColumn from '../../errors/schema-builder/CannotIgnoreSoftDeleteColumn.js';
|
|
6
|
+
import CannotIgnoreSortablePositionColumn from '../../errors/schema-builder/CannotIgnoreSortablePositionColumn.js';
|
|
7
|
+
import CannotIgnoreSortableScopeColumn from '../../errors/schema-builder/CannotIgnoreSortableScopeColumn.js';
|
|
8
|
+
import CannotIgnoreStiTypeColumn from '../../errors/schema-builder/CannotIgnoreStiTypeColumn.js';
|
|
9
|
+
import ConflictingIgnoredColumns from '../../errors/schema-builder/ConflictingIgnoredColumns.js';
|
|
10
|
+
import IgnoredColumnMustBeCamelCase from '../../errors/schema-builder/IgnoredColumnMustBeCamelCase.js';
|
|
11
|
+
import camelize from '../camelize.js';
|
|
12
|
+
import uniq from '../uniq.js';
|
|
13
|
+
/**
|
|
14
|
+
* @internal
|
|
15
|
+
*
|
|
16
|
+
* Resolves the set of ignored columns for a table from the `ignoredColumns`
|
|
17
|
+
* declarations of every model backed by that table, validating the
|
|
18
|
+
* declarations along the way. Called while `sync` builds the generated types
|
|
19
|
+
* files (the declarations have no runtime behavior; see the `ignoredColumns`
|
|
20
|
+
* getter on Dream), so each of these guards fails the sync command loudly
|
|
21
|
+
* rather than surfacing as broken behavior at runtime:
|
|
22
|
+
*
|
|
23
|
+
* - ignored columns must be declared in camelCase, since generated column
|
|
24
|
+
* names are camelized, so any other shape can never match a generated
|
|
25
|
+
* column and would be silently inert
|
|
26
|
+
* - a model may never ignore its primary key
|
|
27
|
+
* - an STI model may never ignore the STI "type" column
|
|
28
|
+
* - a model may never ignore an @Sortable position field or a plain-column
|
|
29
|
+
* @Sortable scope, the backing column of an @Encrypted property, or (on a
|
|
30
|
+
* SoftDelete model) its deletedAtField — the framework reads and writes
|
|
31
|
+
* those columns by name
|
|
32
|
+
* - models sharing a table must agree on their ignored columns, since there
|
|
33
|
+
* is only one generated schema per table (STI children inherit the base
|
|
34
|
+
* model's getter, so agreement is automatic unless a child overrides it)
|
|
35
|
+
* - no association anywhere in the app may name an ignored column as the
|
|
36
|
+
* foreign key (or polymorphic type field) it reads and writes on this
|
|
37
|
+
* table — a BelongsTo on a model backed by this table names a foreign key
|
|
38
|
+
* on this table, and so does a HasMany/HasOne on any other model that
|
|
39
|
+
* points at a model backed by this table
|
|
40
|
+
* - no association anywhere in the app may name an ignored column as its
|
|
41
|
+
* primaryKeyOverride — the column the association's foreign key points
|
|
42
|
+
* at, which lives on the associated model's table for a BelongsTo and on
|
|
43
|
+
* the declaring model's own table for a HasMany/HasOne
|
|
44
|
+
*
|
|
45
|
+
* @param models - every model backed by the table
|
|
46
|
+
* @param tableName - the table whose ignored columns are being resolved
|
|
47
|
+
* @param allModels - every model in the app (on the table's connection);
|
|
48
|
+
* required because associations declared on other models can name foreign
|
|
49
|
+
* keys on this table
|
|
50
|
+
* @returns the set of column names to omit from the table's generated types
|
|
51
|
+
*/
|
|
52
|
+
export default function resolveIgnoredColumns(models, tableName, allModels) {
|
|
53
|
+
models.forEach(modelClass => {
|
|
54
|
+
const ignoredColumns = modelClass.prototype.ignoredColumns;
|
|
55
|
+
ignoredColumns.forEach(columnName => {
|
|
56
|
+
if (camelize(columnName) !== columnName)
|
|
57
|
+
throw new IgnoredColumnMustBeCamelCase(modelClass, columnName);
|
|
58
|
+
});
|
|
59
|
+
if (ignoredColumns.includes(modelClass.primaryKey))
|
|
60
|
+
throw new CannotIgnorePrimaryKey(modelClass);
|
|
61
|
+
if (ignoredColumns.includes('type') && (modelClass['isSTIBase'] || modelClass['isSTIChild']))
|
|
62
|
+
throw new CannotIgnoreStiTypeColumn(modelClass);
|
|
63
|
+
modelClass['sortableFields'].forEach(sortableFieldConfig => {
|
|
64
|
+
if (ignoredColumns.includes(sortableFieldConfig.positionField))
|
|
65
|
+
throw new CannotIgnoreSortablePositionColumn(modelClass, sortableFieldConfig.positionField);
|
|
66
|
+
// a Sortable scope entry names either a column or a BelongsTo
|
|
67
|
+
// association on the model (getColumnForSortableScope resolves columns
|
|
68
|
+
// first, then falls back to association metadata), so an ignored scope
|
|
69
|
+
// entry only breaks @Sortable when no association answers to the name
|
|
70
|
+
scopeArray(sortableFieldConfig.scope).forEach(scopeEntry => {
|
|
71
|
+
if (ignoredColumns.includes(scopeEntry) &&
|
|
72
|
+
modelClass['associationMetadataMap']()[scopeEntry] === undefined)
|
|
73
|
+
throw new CannotIgnoreSortableScopeColumn(modelClass, scopeEntry, sortableFieldConfig.positionField);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
// the Encrypted decorator records each backing column it manages in
|
|
77
|
+
// explicitUnsafeParamColumns (which nothing else populates)
|
|
78
|
+
modelClass['explicitUnsafeParamColumns'].forEach(columnName => {
|
|
79
|
+
if (ignoredColumns.includes(columnName))
|
|
80
|
+
throw new CannotIgnoreEncryptedColumn(modelClass, columnName);
|
|
81
|
+
});
|
|
82
|
+
if (modelClass['softDelete']) {
|
|
83
|
+
const deletedAtField = modelClass.prototype['_deletedAtField'];
|
|
84
|
+
if (ignoredColumns.includes(deletedAtField))
|
|
85
|
+
throw new CannotIgnoreSoftDeleteColumn(modelClass, deletedAtField);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
const distinctDeclarations = uniq(models.map(modelClass => JSON.stringify(uniq([...modelClass.prototype.ignoredColumns]).sort())));
|
|
89
|
+
if (distinctDeclarations.length > 1)
|
|
90
|
+
throw new ConflictingIgnoredColumns(tableName, models);
|
|
91
|
+
const ignoredColumns = new Set(models.flatMap(modelClass => [...modelClass.prototype.ignoredColumns]));
|
|
92
|
+
if (ignoredColumns.size)
|
|
93
|
+
guardAssociationColumns(ignoredColumns, tableName, allModels);
|
|
94
|
+
return ignoredColumns;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* @internal
|
|
98
|
+
*
|
|
99
|
+
* fails the sync when an ignored column of the table is the foreign key (or
|
|
100
|
+
* polymorphic type field) that any association in the app reads and writes
|
|
101
|
+
* on this table, or the primaryKeyOverride column that any association's
|
|
102
|
+
* foreign key points at on this table. A BelongsTo association names a
|
|
103
|
+
* foreign key on the declaring model's own table and a primaryKeyOverride
|
|
104
|
+
* on the associated model's table; a HasMany/HasOne association names a
|
|
105
|
+
* foreign key on the associated model's table and a primaryKeyOverride on
|
|
106
|
+
* the declaring model's own table — so the load-bearing association may be
|
|
107
|
+
* declared on any model in the app, not just the models backed by this
|
|
108
|
+
* table.
|
|
109
|
+
*/
|
|
110
|
+
function guardAssociationColumns(ignoredColumns, tableName, allModels) {
|
|
111
|
+
for (const modelClass of allModels) {
|
|
112
|
+
for (const associationName of modelClass.associationNames) {
|
|
113
|
+
const associationMetaData = modelClass['associationMetadataMap']()[associationName];
|
|
114
|
+
if (associationMetaData === undefined)
|
|
115
|
+
continue;
|
|
116
|
+
// a through association names no foreign key of its own; the source
|
|
117
|
+
// association it travels through is guarded directly
|
|
118
|
+
if (associationMetaData.through)
|
|
119
|
+
continue;
|
|
120
|
+
// unlike foreignKey(), primaryKeyOverride is a plain string on the
|
|
121
|
+
// association statement, so it can be guarded without consulting the
|
|
122
|
+
// generated schema — before the foreignKey-specific short-circuits below
|
|
123
|
+
const primaryKeyOverride = associationMetaData.primaryKeyOverride;
|
|
124
|
+
if (primaryKeyOverride &&
|
|
125
|
+
ignoredColumns.has(primaryKeyOverride) &&
|
|
126
|
+
primaryKeyOverrideTableForAssociationMatches(associationMetaData, modelClass, tableName))
|
|
127
|
+
throw new CannotIgnoreAssociationColumn(tableName, primaryKeyOverride, modelClass, associationMetaData, 'primary key override');
|
|
128
|
+
if (!foreignKeyTableForAssociationMatches(associationMetaData, modelClass, tableName))
|
|
129
|
+
continue;
|
|
130
|
+
// NOTE
|
|
131
|
+
// this try-catch mirrors getAssociationData in ASTConnectionBuilder:
|
|
132
|
+
// computing foreignKey() introspects columns via the generated schema
|
|
133
|
+
// file, so it may throw — on the first sync pass because the schema
|
|
134
|
+
// file has not been regenerated yet, and on the second pass because
|
|
135
|
+
// the regenerated schema omits the ignored column. So in orderings
|
|
136
|
+
// where foreignKey() throws on the first pass, it throws on the second
|
|
137
|
+
// pass too, and these guards never see the association's foreign key;
|
|
138
|
+
// the InvalidComputedForeignKey / ExplicitForeignKeyRequired errors
|
|
139
|
+
// swallowed here resurface at runtime as the backstop.
|
|
140
|
+
let foreignKey = null;
|
|
141
|
+
let foreignKeyTypeColumn = null;
|
|
142
|
+
try {
|
|
143
|
+
foreignKey = associationMetaData.foreignKey();
|
|
144
|
+
foreignKeyTypeColumn = associationMetaData.polymorphic
|
|
145
|
+
? associationMetaData.foreignKeyTypeField()
|
|
146
|
+
: null;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (ignoredColumns.has(foreignKey))
|
|
152
|
+
throw new CannotIgnoreAssociationColumn(tableName, foreignKey, modelClass, associationMetaData, 'foreign key');
|
|
153
|
+
if (foreignKeyTypeColumn && ignoredColumns.has(foreignKeyTypeColumn))
|
|
154
|
+
throw new CannotIgnoreAssociationColumn(tableName, foreignKeyTypeColumn, modelClass, associationMetaData, 'polymorphic type field');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* @internal
|
|
160
|
+
*
|
|
161
|
+
* returns whether the given association's foreign key physically lives on
|
|
162
|
+
* the given table: on the declaring model's own table for a BelongsTo, and
|
|
163
|
+
* on the associated model's table for a HasMany/HasOne
|
|
164
|
+
*/
|
|
165
|
+
function foreignKeyTableForAssociationMatches(associationMetaData, modelClass, tableName) {
|
|
166
|
+
if (associationMetaData.type === 'BelongsTo')
|
|
167
|
+
return modelClass.table === tableName;
|
|
168
|
+
const dreamClassOrClasses = associationMetaData.modelCB();
|
|
169
|
+
// a missing associated class raises FailedToIdentifyAssociation while the
|
|
170
|
+
// builder gathers association data; it is not this guard's concern
|
|
171
|
+
if (!dreamClassOrClasses)
|
|
172
|
+
return false;
|
|
173
|
+
return Array.isArray(dreamClassOrClasses)
|
|
174
|
+
? dreamClassOrClasses.some(dreamClass => dreamClass.table === tableName)
|
|
175
|
+
: dreamClassOrClasses.table === tableName;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* @internal
|
|
179
|
+
*
|
|
180
|
+
* returns whether the given association's primaryKeyOverride column
|
|
181
|
+
* physically lives on the given table — the opposite side from the foreign
|
|
182
|
+
* key (see associationPrimaryKeyAccessors in
|
|
183
|
+
* decorators/field/association/shared.ts): on the associated model's table
|
|
184
|
+
* for a BelongsTo, and on the declaring model's own table for a
|
|
185
|
+
* HasMany/HasOne
|
|
186
|
+
*/
|
|
187
|
+
function primaryKeyOverrideTableForAssociationMatches(associationMetaData, modelClass, tableName) {
|
|
188
|
+
if (associationMetaData.type !== 'BelongsTo')
|
|
189
|
+
return modelClass.table === tableName;
|
|
190
|
+
const dreamClassOrClasses = associationMetaData.modelCB();
|
|
191
|
+
// a missing associated class raises FailedToIdentifyAssociation while the
|
|
192
|
+
// builder gathers association data; it is not this guard's concern
|
|
193
|
+
if (!dreamClassOrClasses)
|
|
194
|
+
return false;
|
|
195
|
+
return Array.isArray(dreamClassOrClasses)
|
|
196
|
+
? dreamClassOrClasses.some(dreamClass => dreamClass.table === tableName)
|
|
197
|
+
: dreamClassOrClasses.table === tableName;
|
|
198
|
+
}
|
|
@@ -716,6 +716,23 @@ export default class Dream {
|
|
|
716
716
|
* @returns The number of records corresponding to this model
|
|
717
717
|
*/
|
|
718
718
|
static count<T extends typeof Dream>(this: T): Promise<number>;
|
|
719
|
+
/**
|
|
720
|
+
* Retrieves the number of records in each group, keyed by the
|
|
721
|
+
* value of the provided group column (a SQL `GROUP BY`).
|
|
722
|
+
*
|
|
723
|
+
* ```ts
|
|
724
|
+
* await User.countBy('name')
|
|
725
|
+
* // Map(2) { 'fred' => 2, 'zed' => 1 }
|
|
726
|
+
* ```
|
|
727
|
+
*
|
|
728
|
+
* Only groups with at least one matching row appear in the Map; seed absent
|
|
729
|
+
* groups yourself with `map.get(key) ?? 0`. When the group column is nullable,
|
|
730
|
+
* records with a `null` value are grouped under a real `null` key.
|
|
731
|
+
*
|
|
732
|
+
* @param groupColumn - the column to group by
|
|
733
|
+
* @returns A Map from each present group value to the number of records in that group
|
|
734
|
+
*/
|
|
735
|
+
static countBy<T extends typeof Dream, GroupColumnName extends DreamColumnNames<InstanceType<T>>>(this: T, groupColumn: GroupColumnName): Promise<Map<InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]], number>>;
|
|
719
736
|
/**
|
|
720
737
|
* Retrieves the max value of the specified column
|
|
721
738
|
* for this model's records.
|
|
@@ -729,6 +746,25 @@ export default class Dream {
|
|
|
729
746
|
* @returns the max value of the specified column for this model's records
|
|
730
747
|
*/
|
|
731
748
|
static max<T extends typeof Dream, ColumnName extends DreamColumnNames<InstanceType<T>>>(this: T, columnName: ColumnName): Promise<InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][ColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][ColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]]>;
|
|
749
|
+
/**
|
|
750
|
+
* Retrieves the max value of the specified column within each group, keyed by
|
|
751
|
+
* the value of the provided group column (a SQL `GROUP BY`).
|
|
752
|
+
*
|
|
753
|
+
* ```ts
|
|
754
|
+
* await CompositionAsset.maxBy('name', 'score')
|
|
755
|
+
* // Map(2) { 'primary' => 9, 'secondary' => 4 }
|
|
756
|
+
* ```
|
|
757
|
+
*
|
|
758
|
+
* Only groups with at least one matching row appear in the Map. When the group
|
|
759
|
+
* column is nullable, records with a `null` value are grouped under a real
|
|
760
|
+
* `null` key; a group whose aggregated values are all `null` yields a `null`
|
|
761
|
+
* value.
|
|
762
|
+
*
|
|
763
|
+
* @param groupColumn - the column to group by
|
|
764
|
+
* @param aggregatedColumn - the column to take the max of within each group
|
|
765
|
+
* @returns A Map from each present group value to the max of the aggregated column in that group
|
|
766
|
+
*/
|
|
767
|
+
static maxBy<T extends typeof Dream, GroupColumnName extends DreamColumnNames<InstanceType<T>>, AggregatedColumnName extends DreamColumnNames<InstanceType<T>>>(this: T, groupColumn: GroupColumnName, aggregatedColumn: AggregatedColumnName): Promise<Map<InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]], InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][AggregatedColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][AggregatedColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]]>>;
|
|
732
768
|
/**
|
|
733
769
|
* Retrieves the min value of the specified column
|
|
734
770
|
* for this model's records.
|
|
@@ -743,6 +779,25 @@ export default class Dream {
|
|
|
743
779
|
* @returns the min value of the specified column for this model's records
|
|
744
780
|
*/
|
|
745
781
|
static min<T extends typeof Dream, ColumnName extends DreamColumnNames<InstanceType<T>>>(this: T, columnName: ColumnName): Promise<InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][ColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][ColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]]>;
|
|
782
|
+
/**
|
|
783
|
+
* Retrieves the min value of the specified column within each group, keyed by
|
|
784
|
+
* the value of the provided group column (a SQL `GROUP BY`).
|
|
785
|
+
*
|
|
786
|
+
* ```ts
|
|
787
|
+
* await CompositionAsset.minBy('name', 'score')
|
|
788
|
+
* // Map(2) { 'primary' => 1, 'secondary' => 4 }
|
|
789
|
+
* ```
|
|
790
|
+
*
|
|
791
|
+
* Only groups with at least one matching row appear in the Map. When the group
|
|
792
|
+
* column is nullable, records with a `null` value are grouped under a real
|
|
793
|
+
* `null` key; a group whose aggregated values are all `null` yields a `null`
|
|
794
|
+
* value.
|
|
795
|
+
*
|
|
796
|
+
* @param groupColumn - the column to group by
|
|
797
|
+
* @param aggregatedColumn - the column to take the min of within each group
|
|
798
|
+
* @returns A Map from each present group value to the min of the aggregated column in that group
|
|
799
|
+
*/
|
|
800
|
+
static minBy<T extends typeof Dream, GroupColumnName extends DreamColumnNames<InstanceType<T>>, AggregatedColumnName extends DreamColumnNames<InstanceType<T>>>(this: T, groupColumn: GroupColumnName, aggregatedColumn: AggregatedColumnName): Promise<Map<InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]], InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][AggregatedColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][AggregatedColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]]>>;
|
|
746
801
|
/**
|
|
747
802
|
* Retrieves the sum for all values of the specified column
|
|
748
803
|
* for this model's records.
|
|
@@ -757,6 +812,25 @@ export default class Dream {
|
|
|
757
812
|
* @returns the sum for all values of the specified column for this model's records
|
|
758
813
|
*/
|
|
759
814
|
static sum<T extends typeof Dream, ColumnName extends DreamColumnNames<InstanceType<T>>>(this: T, columnName: ColumnName): Promise<InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][ColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][ColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]]>;
|
|
815
|
+
/**
|
|
816
|
+
* Retrieves the sum of the specified column within each group, keyed by
|
|
817
|
+
* the value of the provided group column (a SQL `GROUP BY`).
|
|
818
|
+
*
|
|
819
|
+
* ```ts
|
|
820
|
+
* await CompositionAsset.sumBy('name', 'score')
|
|
821
|
+
* // Map(2) { 'primary' => 10, 'secondary' => 4 }
|
|
822
|
+
* ```
|
|
823
|
+
*
|
|
824
|
+
* Only groups with at least one matching row appear in the Map. When the group
|
|
825
|
+
* column is nullable, records with a `null` value are grouped under a real
|
|
826
|
+
* `null` key; a group whose aggregated values are all `null` yields a `null`
|
|
827
|
+
* value.
|
|
828
|
+
*
|
|
829
|
+
* @param groupColumn - the column to group by
|
|
830
|
+
* @param aggregatedColumn - the column to sum within each group
|
|
831
|
+
* @returns A Map from each present group value to the sum of the aggregated column in that group
|
|
832
|
+
*/
|
|
833
|
+
static sumBy<T extends typeof Dream, GroupColumnName extends DreamColumnNames<InstanceType<T>>, AggregatedColumnName extends DreamColumnNames<InstanceType<T>>>(this: T, groupColumn: GroupColumnName, aggregatedColumn: AggregatedColumnName): Promise<Map<InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]], InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][AggregatedColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][AggregatedColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]]>>;
|
|
760
834
|
/**
|
|
761
835
|
* Retrieves the average for all values of the specified column
|
|
762
836
|
* for this model's records.
|
|
@@ -771,6 +845,25 @@ export default class Dream {
|
|
|
771
845
|
* @returns the average for all values of the specified column for this model's records
|
|
772
846
|
*/
|
|
773
847
|
static avg<T extends typeof Dream, ColumnName extends DreamColumnNames<InstanceType<T>>>(this: T, columnName: ColumnName): Promise<InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][ColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][ColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]]>;
|
|
848
|
+
/**
|
|
849
|
+
* Retrieves the average of the specified column within each group, keyed by
|
|
850
|
+
* the value of the provided group column (a SQL `GROUP BY`).
|
|
851
|
+
*
|
|
852
|
+
* ```ts
|
|
853
|
+
* await CompositionAsset.avgBy('name', 'score')
|
|
854
|
+
* // Map(2) { 'primary' => 5, 'secondary' => 4 }
|
|
855
|
+
* ```
|
|
856
|
+
*
|
|
857
|
+
* Only groups with at least one matching row appear in the Map. When the group
|
|
858
|
+
* column is nullable, records with a `null` value are grouped under a real
|
|
859
|
+
* `null` key; a group whose aggregated values are all `null` yields a `null`
|
|
860
|
+
* value.
|
|
861
|
+
*
|
|
862
|
+
* @param groupColumn - the column to group by
|
|
863
|
+
* @param aggregatedColumn - the column to average within each group
|
|
864
|
+
* @returns A Map from each present group value to the average of the aggregated column in that group
|
|
865
|
+
*/
|
|
866
|
+
static avgBy<T extends typeof Dream, GroupColumnName extends DreamColumnNames<InstanceType<T>>, AggregatedColumnName extends DreamColumnNames<InstanceType<T>>>(this: T, groupColumn: GroupColumnName, aggregatedColumn: AggregatedColumnName): Promise<Map<InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][GroupColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]], InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][AggregatedColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]["coercedType" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]][AggregatedColumnName & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]["columns" & keyof InstanceType<T>["schema"][InstanceType<T>["table"] & keyof InstanceType<T>["schema"]]]]]>>;
|
|
774
867
|
/**
|
|
775
868
|
* Persists a new record, setting the provided attributes.
|
|
776
869
|
* Automatically sets createdAt and updatedAt timestamps.
|
|
@@ -1027,6 +1120,12 @@ export default class Dream {
|
|
|
1027
1120
|
* 3. each nested association will result in an additional record which duplicates data from the outer record. E.g., given `.leftJoinPreload('a', 'b', 'c')`, if each `a` has 10 `b` and each `b` has 10 `c`, then for one `a`, 100 records will be returned, each of which has all of the columns of `a`. `.preload('a', 'b', 'c')` would perform three separate SQL queries, but the data for a single `a` would only be returned once.
|
|
1028
1121
|
* 4. the individual query becomes more complex the more associations are included
|
|
1029
1122
|
* 5. associations loading associations loading associations could result in exponential amounts of data; in those cases, `.preload(...).findEach(...)` avoids instantiating massive amounts of data at once
|
|
1123
|
+
* 6. leftJoinPreload must enumerate every compiled column of every joined
|
|
1124
|
+
* model, so unlike base-model reads, `preload`/`load`, and saves, it does
|
|
1125
|
+
* not tolerate schema/image skew from an unplanned column drop during a
|
|
1126
|
+
* rolling deploy; see {@link Query.leftJoinPreload} and the
|
|
1127
|
+
* `ignoredColumns` getter for the two-deploy process that makes a
|
|
1128
|
+
* planned drop safe.
|
|
1030
1129
|
*
|
|
1031
1130
|
* ```ts
|
|
1032
1131
|
* const user = await User.leftJoinPreload('posts', 'comments', { visibilty: 'public' }, 'replies').first()
|
|
@@ -1856,6 +1955,81 @@ export default class Dream {
|
|
|
1856
1955
|
* @returns The table name for this model
|
|
1857
1956
|
*/
|
|
1858
1957
|
get table(): AssociationTableNames<any, any>;
|
|
1958
|
+
/**
|
|
1959
|
+
* Columns that Dream should behave as though they do not exist.
|
|
1960
|
+
*
|
|
1961
|
+
* Declaring a column ignored removes it from the generated types the next
|
|
1962
|
+
* time `sync` runs: it is omitted from both the db types file (the Kysely
|
|
1963
|
+
* `DB` interface) and the dream schema file, so it disappears from
|
|
1964
|
+
* `columns()` and from every place that flows from `columns()` — select
|
|
1965
|
+
* lists built for `preload`/`load` and `leftJoinPreload`, save hydration,
|
|
1966
|
+
* attribute definition, and param safety. References to the column in
|
|
1967
|
+
* application code become type errors, which is the point: they must be
|
|
1968
|
+
* removed before the column can be dropped.
|
|
1969
|
+
*
|
|
1970
|
+
* This enables safely dropping a column under rolling deploys — the same
|
|
1971
|
+
* problem Rails solves with `ignored_columns`. The safety requirement is
|
|
1972
|
+
* that no image that can run against the post-drop schema names the
|
|
1973
|
+
* column in any SQL it generates. To satisfy it: remove all application
|
|
1974
|
+
* code that uses the column, declare it here, and run `sync`; then let
|
|
1975
|
+
* the drop migration run only once no image lacking the declaration can
|
|
1976
|
+
* run against the database — whether the migration ships in a later
|
|
1977
|
+
* deploy of its own, or together with the declaration in a pipeline that
|
|
1978
|
+
* runs migrations only after the new images have rolled out. Once the
|
|
1979
|
+
* column is dropped, remove this declaration and resync.
|
|
1980
|
+
*
|
|
1981
|
+
* Precondition: as soon as an image built with this declaration runs,
|
|
1982
|
+
* the column is never placed in INSERT column lists, so before that
|
|
1983
|
+
* image can run, the column must be nullable or carry a database
|
|
1984
|
+
* default — a live `NOT NULL` column without a default fails every
|
|
1985
|
+
* create against the table.
|
|
1986
|
+
*
|
|
1987
|
+
* Dropping the column while an image that names it can still run leaves
|
|
1988
|
+
* a window during which those containers (including a rolled-back image)
|
|
1989
|
+
* fail with `42703 column does not exist` — `leftJoinPreload` in
|
|
1990
|
+
* particular has no runtime tolerance for this, since it must enumerate
|
|
1991
|
+
* aliased columns.
|
|
1992
|
+
*
|
|
1993
|
+
* This is a mechanism for the drop window, not for permanently hiding
|
|
1994
|
+
* wide columns: the ignored column is still transferred from the
|
|
1995
|
+
* database on every `RETURNING *` / `select *` until it is actually
|
|
1996
|
+
* dropped.
|
|
1997
|
+
*
|
|
1998
|
+
* The declaration is read only while `sync` generates the types files; it
|
|
1999
|
+
* has no runtime behavior of its own. A declared-but-not-synced model is
|
|
2000
|
+
* therefore not yet protected — CI should verify that `sync` produces no
|
|
2001
|
+
* diff. `sync` will fail loudly if a declared name is not camelCase
|
|
2002
|
+
* (generated column names are camelized, so any other shape could never
|
|
2003
|
+
* match and would be silently inert), if models sharing a table declare
|
|
2004
|
+
* different ignored columns, or if a model attempts to ignore a column
|
|
2005
|
+
* the framework itself reads and writes by name: its primary key, an STI
|
|
2006
|
+
* model's `type` column, any association's foreign key, polymorphic type
|
|
2007
|
+
* field, or `primaryKeyOverride` column, an `@Sortable` position field or
|
|
2008
|
+
* plain-column `@Sortable` scope, an `@Encrypted` backing column, or a
|
|
2009
|
+
* SoftDelete model's `deletedAt` column.
|
|
2010
|
+
*
|
|
2011
|
+
* Because an ignored column vanishes from `columns()`, runtime access via
|
|
2012
|
+
* type escape hatches behaves exactly like any unknown attribute: reads
|
|
2013
|
+
* return `undefined`, and writes assign a plain instance property that is
|
|
2014
|
+
* never persisted.
|
|
2015
|
+
*
|
|
2016
|
+
* ```ts
|
|
2017
|
+
* class User extends ApplicationModel {
|
|
2018
|
+
* public override get ignoredColumns() {
|
|
2019
|
+
* return ['legacyEmail'] as const
|
|
2020
|
+
* }
|
|
2021
|
+
* }
|
|
2022
|
+
* ```
|
|
2023
|
+
*
|
|
2024
|
+
* NOTE: this getter is intentionally typed as `readonly string[]` rather
|
|
2025
|
+
* than as a union of known column names: during the deploy that declares
|
|
2026
|
+
* a column ignored, the regenerated types no longer contain the column,
|
|
2027
|
+
* so a column-name-derived type would reject the very declaration that
|
|
2028
|
+
* removed it.
|
|
2029
|
+
*
|
|
2030
|
+
* @returns The list of column names this model should ignore
|
|
2031
|
+
*/
|
|
2032
|
+
get ignoredColumns(): readonly string[];
|
|
1859
2033
|
/**
|
|
1860
2034
|
* @internal
|
|
1861
2035
|
*
|
|
@@ -2651,9 +2825,9 @@ export default class Dream {
|
|
|
2651
2825
|
[x: string]: any;
|
|
2652
2826
|
} | Partial<{
|
|
2653
2827
|
[x: string]: any;
|
|
2654
|
-
}> |
|
|
2828
|
+
}> | {
|
|
2655
2829
|
[x: string]: any;
|
|
2656
|
-
}
|
|
2830
|
+
}>>;
|
|
2657
2831
|
/**
|
|
2658
2832
|
* Takes the attributes passed in and sets their values internally,
|
|
2659
2833
|
* bypassing any custom setters defined for these attributes.
|
|
@@ -2670,9 +2844,9 @@ export default class Dream {
|
|
|
2670
2844
|
[x: string]: any;
|
|
2671
2845
|
} | Partial<{
|
|
2672
2846
|
[x: string]: any;
|
|
2673
|
-
}> |
|
|
2847
|
+
}> | {
|
|
2674
2848
|
[x: string]: any;
|
|
2675
|
-
}
|
|
2849
|
+
}>>;
|
|
2676
2850
|
private _setAttributes;
|
|
2677
2851
|
/**
|
|
2678
2852
|
* Saves the state of the current instance to the
|
|
@@ -34,5 +34,17 @@ export declare const SOFT_DELETE_SCOPE_NAME = "dream:SoftDelete";
|
|
|
34
34
|
* return 'customDatetimeField' as const
|
|
35
35
|
* }
|
|
36
36
|
* }
|
|
37
|
+
*
|
|
38
|
+
* Note on indexing: Dream deliberately does not index `deleted_at`.
|
|
39
|
+
* The default scope's `WHERE deleted_at IS NULL` matches nearly every
|
|
40
|
+
* row on a healthy table, so a plain b-tree on the column is rarely
|
|
41
|
+
* chosen by the planner while still costing index size and write
|
|
42
|
+
* amplification, and Dream itself never issues a query such an index
|
|
43
|
+
* could serve. If your app needs one, add it yourself based on your
|
|
44
|
+
* own access patterns — on Postgres, the two useful shapes are a
|
|
45
|
+
* composite partial index on your hot lookup columns with
|
|
46
|
+
* `WHERE deleted_at IS NULL` (fast scoped reads), or a partial index
|
|
47
|
+
* `WHERE deleted_at IS NOT NULL` (purge/GC sweeps over soft-deleted
|
|
48
|
+
* rows). Both are Postgres-specific syntax.
|
|
37
49
|
*/
|
|
38
50
|
export default function SoftDelete(): (target: typeof Dream) => void;
|