@rvoh/dream 2.17.1 → 2.18.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/cli/index.js +17 -10
- package/dist/cjs/src/dream/Query.js +15 -2
- package/dist/cjs/src/dream/QueryDriver/Kysely.js +77 -2
- package/dist/cjs/src/dream-app/index.js +13 -16
- package/dist/cjs/src/encrypt/algorithms/aes-gcm/decryptAESGCM.js +7 -2
- package/dist/cjs/src/errors/MaxRecursiveStringCaseDepthExceeded.js +14 -0
- package/dist/cjs/src/errors/dream-app/DreamAppInitInvalidEncryptionKey.js +27 -0
- package/dist/cjs/src/helpers/cli/generateDreamContent.js +13 -6
- package/dist/cjs/src/helpers/cli/generateMigrationContent.js +18 -12
- package/dist/cjs/src/helpers/cli/validateStiChildColumns.js +7 -0
- package/dist/cjs/src/helpers/stringCasing.js +16 -4
- package/dist/esm/src/cli/index.js +17 -10
- package/dist/esm/src/dream/Query.js +15 -2
- package/dist/esm/src/dream/QueryDriver/Kysely.js +77 -2
- package/dist/esm/src/dream-app/index.js +13 -16
- package/dist/esm/src/encrypt/algorithms/aes-gcm/decryptAESGCM.js +7 -2
- package/dist/esm/src/errors/MaxRecursiveStringCaseDepthExceeded.js +14 -0
- package/dist/esm/src/errors/dream-app/DreamAppInitInvalidEncryptionKey.js +27 -0
- package/dist/esm/src/helpers/cli/generateDreamContent.js +13 -6
- package/dist/esm/src/helpers/cli/generateMigrationContent.js +18 -12
- package/dist/esm/src/helpers/cli/validateStiChildColumns.js +7 -0
- package/dist/esm/src/helpers/stringCasing.js +16 -4
- package/dist/types/src/cli/index.d.ts +2 -1
- package/dist/types/src/dream/Query.d.ts +9 -0
- package/dist/types/src/dream/QueryDriver/Kysely.d.ts +37 -0
- package/dist/types/src/dream-app/index.d.ts +4 -2
- package/dist/types/src/errors/MaxRecursiveStringCaseDepthExceeded.d.ts +5 -0
- package/dist/types/src/errors/dream-app/DreamAppInitInvalidEncryptionKey.d.ts +8 -0
- package/dist/types/src/helpers/cli/validateStiChildColumns.d.ts +1 -0
- 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 +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 +11 -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 +1 -1
- package/docs/functions/utils.camelize.html +1 -1
- package/docs/functions/utils.capitalize.html +1 -1
- package/docs/functions/utils.cloneDeepSafe.html +1 -1
- package/docs/functions/utils.compact.html +1 -1
- package/docs/functions/utils.groupBy.html +1 -1
- package/docs/functions/utils.hyphenize.html +1 -1
- package/docs/functions/utils.intersection.html +1 -1
- package/docs/functions/utils.isEmpty.html +1 -1
- package/docs/functions/utils.normalizeUnicode.html +1 -1
- package/docs/functions/utils.pascalize.html +1 -1
- package/docs/functions/utils.percent.html +1 -1
- package/docs/functions/utils.range.html +1 -1
- package/docs/functions/utils.round.html +1 -1
- package/docs/functions/utils.sanitizeString.html +1 -1
- package/docs/functions/utils.snakeify.html +1 -1
- package/docs/functions/utils.sort.html +1 -1
- package/docs/functions/utils.sortBy.html +1 -1
- package/docs/functions/utils.sortObjectByKey.html +1 -1
- package/docs/functions/utils.sortObjectByValue.html +1 -1
- package/docs/functions/utils.uncapitalize.html +1 -1
- package/docs/functions/utils.uniq.html +1 -1
- package/docs/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 +1 -1
- /package/dist/types/src/types/{types/associations → associations}/belongsTo.ts +0 -0
- /package/dist/types/src/types/{types/associations → associations}/hasMany.ts +0 -0
- /package/dist/types/src/types/{types/associations → associations}/hasOne.ts +0 -0
- /package/dist/types/src/types/{types/associations → associations}/shared.ts +0 -0
- /package/dist/types/src/types/{types/calendardate.ts → calendardate.ts} +0 -0
- /package/dist/types/src/types/{types/clocktime.ts → clocktime.ts} +0 -0
- /package/dist/types/src/types/{types/datetime.ts → datetime.ts} +0 -0
- /package/dist/types/src/types/{types/db.ts → db.ts} +0 -0
- /package/dist/types/src/types/{types/dream.ts → dream.ts} +0 -0
- /package/dist/types/src/types/{types/lifecycle.ts → lifecycle.ts} +0 -0
- /package/dist/types/src/types/{types/logger.ts → logger.ts} +0 -0
- /package/dist/types/src/types/{types/moduleDeclarations → moduleDeclarations}/luxon.d.ts +0 -0
- /package/dist/types/src/types/{types/openapi.ts → openapi.ts} +0 -0
- /package/dist/types/src/types/{types/query.ts → query.ts} +0 -0
- /package/dist/types/src/types/{types/recursiveSerialization.ts → recursiveSerialization.ts} +0 -0
- /package/dist/types/src/types/{types/serializer.ts → serializer.ts} +0 -0
- /package/dist/types/src/types/{types/utils.ts → utils.ts} +0 -0
- /package/dist/types/src/types/{types/validation.ts → validation.ts} +0 -0
- /package/dist/types/src/types/{types/variadic.ts → variadic.ts} +0 -0
|
@@ -14,6 +14,7 @@ import DreamCLI from '../../cli/index.js';
|
|
|
14
14
|
import { CHECK_VIOLATION, COLUMN_OVERFLOW, INVALID_INPUT_SYNTAX, NOT_NULL_VIOLATION, pgErrorType, } from '../../db/errors.js';
|
|
15
15
|
import syncDbTypesFiles from '../../db/helpers/syncDbTypesFiles.js';
|
|
16
16
|
import { default as _db } from '../../db/index.js';
|
|
17
|
+
import validateColumn from '../../db/validators/validateColumn.js';
|
|
17
18
|
import associationToGetterSetterProp from '../../decorators/field/association/associationToGetterSetterProp.js';
|
|
18
19
|
import PackageManager from '../../dream-app/helpers/PackageManager.js';
|
|
19
20
|
import DreamApp from '../../dream-app/index.js';
|
|
@@ -551,6 +552,7 @@ export default class KyselyQueryDriver extends QueryDriverBase {
|
|
|
551
552
|
// that can exceed 63 bytes. Short aliases like "pluck0" avoid this entirely.
|
|
552
553
|
const shortAliases = [];
|
|
553
554
|
fields.forEach((field, index) => {
|
|
555
|
+
this.validatePlainColumn(this.dreamClass, field);
|
|
554
556
|
const shortAlias = `pluck${index}`;
|
|
555
557
|
shortAliases.push(shortAlias);
|
|
556
558
|
// namespace the selection so that when plucking the same column name from
|
|
@@ -719,6 +721,7 @@ export default class KyselyQueryDriver extends QueryDriverBase {
|
|
|
719
721
|
});
|
|
720
722
|
if (this.query['orderStatements'].length && !bypassOrder) {
|
|
721
723
|
this.query['orderStatements'].forEach(orderStatement => {
|
|
724
|
+
this.validatePlainColumn(this.dreamClass, orderStatement.column);
|
|
722
725
|
kyselyQuery = kyselyQuery.orderBy(this.namespaceColumn(orderStatement.column), this.orderByDirection(orderStatement.direction));
|
|
723
726
|
});
|
|
724
727
|
}
|
|
@@ -1034,6 +1037,7 @@ export default class KyselyQueryDriver extends QueryDriverBase {
|
|
|
1034
1037
|
if (query['whereStatements'].length ||
|
|
1035
1038
|
query['whereNotStatements'].length ||
|
|
1036
1039
|
query['whereAnyStatements'].length) {
|
|
1040
|
+
this.validateBaseWhereStatementColumns(query);
|
|
1037
1041
|
kyselyQuery = kyselyQuery.where((eb) => eb.and([
|
|
1038
1042
|
...this.aliasWhereStatements(query['whereStatements'], query['baseSqlAlias']).map(whereStatement => this.whereStatementToExpressionWrapper(this.dreamClass, eb, whereStatement, {
|
|
1039
1043
|
disallowSimilarityOperator: false,
|
|
@@ -1368,6 +1372,66 @@ export default class KyselyQueryDriver extends QueryDriverBase {
|
|
|
1368
1372
|
namespaceColumn(column, alias = this.query['baseSqlAlias']) {
|
|
1369
1373
|
return namespaceColumn(column, alias);
|
|
1370
1374
|
}
|
|
1375
|
+
/**
|
|
1376
|
+
* @internal
|
|
1377
|
+
*
|
|
1378
|
+
* Defense-in-depth (DREAM-SQLI-01): when a runtime column identifier is a
|
|
1379
|
+
* plain, un-namespaced column name, validate it against the owning model's
|
|
1380
|
+
* compiled schema so an unknown identifier throws a Dream `InvalidColumnName`
|
|
1381
|
+
* error before it reaches the database (rather than surfacing as a raw
|
|
1382
|
+
* Postgres error or a silent blind-ordering side-channel).
|
|
1383
|
+
*
|
|
1384
|
+
* Only bare identifiers are validated. Namespaced / table-aliased /
|
|
1385
|
+
* association-namespaced identifiers (anything containing a `.`) are left on
|
|
1386
|
+
* their existing path, because the segment before the `.` may be an
|
|
1387
|
+
* association alias or table alias rather than a schema table name, and
|
|
1388
|
+
* validating those safely is not possible here without risking rejection of
|
|
1389
|
+
* currently-valid input.
|
|
1390
|
+
*/
|
|
1391
|
+
validatePlainColumn(dreamClass, column) {
|
|
1392
|
+
if (typeof column !== 'string')
|
|
1393
|
+
return;
|
|
1394
|
+
if (column.includes('.'))
|
|
1395
|
+
return;
|
|
1396
|
+
validateColumn(dreamClass.prototype.schema, dreamClass.table, column);
|
|
1397
|
+
}
|
|
1398
|
+
/**
|
|
1399
|
+
* @internal
|
|
1400
|
+
*
|
|
1401
|
+
* Defense-in-depth (DREAM-SQLI-01) for the base model's dynamic `where` keys.
|
|
1402
|
+
* These are the un-namespaced keys the developer passed to `where` /
|
|
1403
|
+
* `whereNot` / `whereAny` on the root query (validated here, before
|
|
1404
|
+
* `aliasWhereStatement` namespaces them with the base alias). Only plain
|
|
1405
|
+
* column keys are validated. Deliberately skips:
|
|
1406
|
+
* - namespaced keys (contain a `.`) — the developer explicitly namespaced
|
|
1407
|
+
* them (e.g. `{ 'users.email': ... }` or a joined table), and the prefix
|
|
1408
|
+
* may be an association/table alias rather than a schema table name;
|
|
1409
|
+
* - association-name keys (e.g. `where({ user: userInstance })`), which are
|
|
1410
|
+
* resolved to foreign keys, not columns;
|
|
1411
|
+
* - `DreamConst.passthrough` / `DreamConst.required` sentinel values, whose
|
|
1412
|
+
* keys are resolved elsewhere.
|
|
1413
|
+
*
|
|
1414
|
+
* Join-and / association where statements (which target other tables and are
|
|
1415
|
+
* pre-namespaced) are left on their existing path.
|
|
1416
|
+
*/
|
|
1417
|
+
validateBaseWhereStatementColumns(query) {
|
|
1418
|
+
const dreamClass = query['dreamClass'];
|
|
1419
|
+
const whereStatements = [
|
|
1420
|
+
...query['whereStatements'],
|
|
1421
|
+
...query['whereNotStatements'],
|
|
1422
|
+
...query['whereAnyStatements'].flat(),
|
|
1423
|
+
];
|
|
1424
|
+
whereStatements.forEach(whereStatement => Object.keys(whereStatement).forEach(attr => {
|
|
1425
|
+
if (attr.includes('.'))
|
|
1426
|
+
return;
|
|
1427
|
+
const val = whereStatement[attr];
|
|
1428
|
+
if (val === DreamConst.passthrough || val === DreamConst.required)
|
|
1429
|
+
return;
|
|
1430
|
+
if (dreamClass.associationNames.includes(attr))
|
|
1431
|
+
return;
|
|
1432
|
+
validateColumn(dreamClass.prototype.schema, dreamClass.table, attr);
|
|
1433
|
+
}));
|
|
1434
|
+
}
|
|
1371
1435
|
/**
|
|
1372
1436
|
* @internal
|
|
1373
1437
|
*
|
|
@@ -1924,8 +1988,19 @@ export default class KyselyQueryDriver extends QueryDriverBase {
|
|
|
1924
1988
|
scopesQuery = tempQuery;
|
|
1925
1989
|
}
|
|
1926
1990
|
}
|
|
1927
|
-
if (scopesQuery['whereStatements'].length
|
|
1928
|
-
|
|
1991
|
+
if (scopesQuery['whereStatements'].length ||
|
|
1992
|
+
scopesQuery['whereNotStatements'].length ||
|
|
1993
|
+
scopesQuery['whereAnyStatements'].length) {
|
|
1994
|
+
// Mirror the base-table clause logic in QueryDriverBase#buildCommon so that default
|
|
1995
|
+
// scopes expressed with whereNot(...)/whereAny(...) — not just where(...) — cross into
|
|
1996
|
+
// association loads (preload/join/leftJoinPreload/innerJoin). Without the whereNot/whereAny
|
|
1997
|
+
// branches below, such default scopes are silently dropped on association loads, leaking
|
|
1998
|
+
// rows the app intended to hide.
|
|
1999
|
+
join = join.on((eb) => eb.and([
|
|
2000
|
+
...scopesQuery['whereStatements'].map(whereStatement => this.whereStatementToExpressionWrapper(dreamClass, eb, this.aliasWhereStatement(whereStatement, tableNameOrAlias), { disallowSimilarityOperator: false })),
|
|
2001
|
+
...scopesQuery['whereNotStatements'].map(whereNotStatement => this.whereStatementToExpressionWrapper(dreamClass, eb, this.aliasWhereStatement(whereNotStatement, tableNameOrAlias), { negate: true })),
|
|
2002
|
+
...scopesQuery['whereAnyStatements'].map(whereAnyStatements => eb.or(whereAnyStatements.map(whereAnyStatement => this.whereStatementToExpressionWrapper(dreamClass, eb, this.aliasWhereStatement(whereAnyStatement, tableNameOrAlias))))),
|
|
2003
|
+
]));
|
|
1929
2004
|
}
|
|
1930
2005
|
return join;
|
|
1931
2006
|
}
|
|
@@ -4,6 +4,7 @@ import validateTable from '../db/validators/validateTable.js';
|
|
|
4
4
|
import Query from '../dream/Query.js';
|
|
5
5
|
import PostgresQueryDriver from '../dream/QueryDriver/Postgres.js';
|
|
6
6
|
import Encrypt from '../encrypt/index.js';
|
|
7
|
+
import DreamAppInitInvalidEncryptionKey from '../errors/dream-app/DreamAppInitInvalidEncryptionKey.js';
|
|
7
8
|
import DreamAppInitMissingCallToLoadModels from '../errors/dream-app/DreamAppInitMissingCallToLoadModels.js';
|
|
8
9
|
import DreamAppInitMissingMissingProjectRoot from '../errors/dream-app/DreamAppInitMissingMissingProjectRoot.js';
|
|
9
10
|
import DreamAppInitMissingPackageManager from '../errors/dream-app/DreamAppInitMissingPackageManager.js';
|
|
@@ -119,7 +120,9 @@ export default class DreamApp {
|
|
|
119
120
|
if (!this.loadedModels)
|
|
120
121
|
throw new DreamAppInitMissingCallToLoadModels();
|
|
121
122
|
if (this.encryption?.columns?.current)
|
|
122
|
-
DreamApp.checkKey('columns', this.encryption.columns.current.key, this.encryption.columns.current.algorithm);
|
|
123
|
+
DreamApp.checkKey('columns', 'current', this.encryption.columns.current.key, this.encryption.columns.current.algorithm);
|
|
124
|
+
if (this.encryption?.columns?.legacy)
|
|
125
|
+
DreamApp.checkKey('columns', 'legacy', this.encryption.columns.legacy.key, this.encryption.columns.legacy.algorithm);
|
|
123
126
|
if (!DreamAppAllowedPackageManagersEnumValues.includes(this.packageManager) &&
|
|
124
127
|
!EnvInternal.boolean('DREAM_BYPASS_PACKAGE_MANAGER_CHECK'))
|
|
125
128
|
throw new DreamAppInitMissingPackageManager();
|
|
@@ -148,22 +151,9 @@ export default class DreamApp {
|
|
|
148
151
|
await dbDriverClass.setDatabaseTypeParsers(connectionName);
|
|
149
152
|
}
|
|
150
153
|
}
|
|
151
|
-
static checkKey(encryptionIdentifier, key, algorithm) {
|
|
154
|
+
static checkKey(encryptionIdentifier, keyType, key, algorithm) {
|
|
152
155
|
if (!Encrypt.validateKey(key, algorithm))
|
|
153
|
-
|
|
154
|
-
console.warn(`
|
|
155
|
-
Your current key value for ${encryptionIdentifier} encryption is invalid.
|
|
156
|
-
Try setting it to something valid, like:
|
|
157
|
-
${Encrypt.generateKey(algorithm)}
|
|
158
|
-
|
|
159
|
-
This was done by calling:
|
|
160
|
-
Encrypt.generateKey('${algorithm}'
|
|
161
|
-
|
|
162
|
-
A new key can also be generated from the CLI:
|
|
163
|
-
% pnpm psy g:encryption-key
|
|
164
|
-
OR
|
|
165
|
-
% pnpm psy g:encryption-key --algorithm=aes-256-gcm
|
|
166
|
-
`);
|
|
156
|
+
throw new DreamAppInitInvalidEncryptionKey(encryptionIdentifier, keyType, algorithm);
|
|
167
157
|
}
|
|
168
158
|
/**
|
|
169
159
|
* Returns the cached dream application if it has been set.
|
|
@@ -218,6 +208,10 @@ A new key can also be generated from the CLI:
|
|
|
218
208
|
get paginationPageSize() {
|
|
219
209
|
return this._paginationPageSize;
|
|
220
210
|
}
|
|
211
|
+
_paginationMaxPageSize = 200;
|
|
212
|
+
get paginationMaxPageSize() {
|
|
213
|
+
return this._paginationMaxPageSize;
|
|
214
|
+
}
|
|
221
215
|
_primaryKeyType = 'bigint';
|
|
222
216
|
get primaryKeyType() {
|
|
223
217
|
return this._primaryKeyType;
|
|
@@ -495,6 +489,9 @@ A new key can also be generated from the CLI:
|
|
|
495
489
|
case 'paginationPageSize':
|
|
496
490
|
this._paginationPageSize = options;
|
|
497
491
|
break;
|
|
492
|
+
case 'paginationMaxPageSize':
|
|
493
|
+
this._paginationMaxPageSize = options;
|
|
494
|
+
break;
|
|
498
495
|
default: {
|
|
499
496
|
// protection so that if a new ApplyOpt is ever added, this will throw a type error at build time
|
|
500
497
|
const _never = applyOption;
|
|
@@ -23,8 +23,13 @@ export default function decryptAESGCM(algorithm, encrypted, key) {
|
|
|
23
23
|
try {
|
|
24
24
|
return JSON.parse(plaintext);
|
|
25
25
|
}
|
|
26
|
-
catch
|
|
27
|
-
|
|
26
|
+
catch {
|
|
27
|
+
// Intentionally drop the underlying SyntaxError as `cause`: its message embeds a
|
|
28
|
+
// snippet of the just-decrypted plaintext (e.g. `Unexpected token 'x', "secret..." is
|
|
29
|
+
// not valid JSON`). Carrying it would leak decrypted plaintext into any logger that
|
|
30
|
+
// serializes `error.cause`. DecryptionParseError's own message is generic and
|
|
31
|
+
// plaintext-free, which is all callers need to diagnose an encrypted-format mismatch.
|
|
32
|
+
throw new DecryptionParseError();
|
|
28
33
|
}
|
|
29
34
|
}
|
|
30
35
|
function unpackPayloadOrFail(payload) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export default class MaxRecursiveStringCaseDepthExceeded extends Error {
|
|
2
|
+
maxDepth;
|
|
3
|
+
constructor(maxDepth) {
|
|
4
|
+
super();
|
|
5
|
+
this.maxDepth = maxDepth;
|
|
6
|
+
}
|
|
7
|
+
get message() {
|
|
8
|
+
return `
|
|
9
|
+
Maximum recursion depth (${this.maxDepth}) exceeded while string-casing an object.
|
|
10
|
+
The input is nested more deeply than Dream will process, which usually means it is
|
|
11
|
+
malformed or maliciously deeply-nested rather than legitimate application data.
|
|
12
|
+
`;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import Encrypt from '../../encrypt/index.js';
|
|
2
|
+
export default class DreamAppInitInvalidEncryptionKey extends Error {
|
|
3
|
+
encryptionIdentifier;
|
|
4
|
+
keyType;
|
|
5
|
+
algorithm;
|
|
6
|
+
constructor(encryptionIdentifier, keyType, algorithm) {
|
|
7
|
+
super();
|
|
8
|
+
this.encryptionIdentifier = encryptionIdentifier;
|
|
9
|
+
this.keyType = keyType;
|
|
10
|
+
this.algorithm = algorithm;
|
|
11
|
+
}
|
|
12
|
+
get message() {
|
|
13
|
+
return `
|
|
14
|
+
Your ${this.keyType} key value for ${this.encryptionIdentifier} encryption is invalid.
|
|
15
|
+
Try setting it to something valid, like:
|
|
16
|
+
${Encrypt.generateKey(this.algorithm)}
|
|
17
|
+
|
|
18
|
+
This was done by calling:
|
|
19
|
+
Encrypt.generateKey('${this.algorithm}')
|
|
20
|
+
|
|
21
|
+
A new key can also be generated from the CLI:
|
|
22
|
+
% pnpm psy g:encryption-key
|
|
23
|
+
OR
|
|
24
|
+
% pnpm psy g:encryption-key --algorithm=aes-256-gcm
|
|
25
|
+
`;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -8,6 +8,7 @@ import snakeify from '../snakeify.js';
|
|
|
8
8
|
import standardizeFullyQualifiedModelName from '../standardizeFullyQualifiedModelName.js';
|
|
9
9
|
import uniq from '../uniq.js';
|
|
10
10
|
import parseAttribute from './parseAttribute.js';
|
|
11
|
+
import validateStiChildColumns from './validateStiChildColumns.js';
|
|
11
12
|
/**
|
|
12
13
|
* Column names that are automatically emitted by the model generator (and
|
|
13
14
|
* the migration generator). When the user passes any of these explicitly,
|
|
@@ -26,6 +27,8 @@ export function filterAutoGeneratedTimestampColumns(columnsWithTypes) {
|
|
|
26
27
|
}
|
|
27
28
|
export default function generateDreamContent(options) {
|
|
28
29
|
const config = createModelConfig(options);
|
|
30
|
+
if (config.isSTI)
|
|
31
|
+
validateStiChildColumns(options.columnsWithTypes);
|
|
29
32
|
// SoftDelete is incompatible with STI children, so it's only applied to
|
|
30
33
|
// non-STI-child models.
|
|
31
34
|
const includeSoftDelete = !config.isSTI && !!options.softDelete;
|
|
@@ -59,15 +62,19 @@ export default function generateDreamContent(options) {
|
|
|
59
62
|
includeDeletedAt: includeSoftDelete || hasExplicitDeletedAt,
|
|
60
63
|
});
|
|
61
64
|
const decoBlock = decoratorsInUse
|
|
62
|
-
? `const deco = new Decorators<typeof ${config.modelClassName}>()
|
|
63
|
-
|
|
65
|
+
? `const deco = new Decorators<typeof ${config.modelClassName}>()
|
|
66
|
+
|
|
67
|
+
`
|
|
68
|
+
: config.isSTI
|
|
69
|
+
? ''
|
|
70
|
+
: `// Uncomment when adding decorators (@deco.BelongsTo, @deco.Validates, etc.):
|
|
64
71
|
// import { Decorators } from '@rvoh/dream'
|
|
65
|
-
// const deco = new Decorators<typeof ${config.modelClassName}>()
|
|
66
|
-
return `${importSection}
|
|
72
|
+
// const deco = new Decorators<typeof ${config.modelClassName}>()
|
|
67
73
|
|
|
68
|
-
|
|
74
|
+
`;
|
|
75
|
+
return `${importSection}
|
|
69
76
|
|
|
70
|
-
${classDeclaration}
|
|
77
|
+
${decoBlock}${classDeclaration}
|
|
71
78
|
${tableMethod}${serializersMethod}${fieldsSection}
|
|
72
79
|
}
|
|
73
80
|
`.replace(/^\s*$/gm, '');
|
|
@@ -8,7 +8,8 @@ import globalClassNameFromFullyQualifiedModelName from '../globalClassNameFromFu
|
|
|
8
8
|
import snakeify from '../snakeify.js';
|
|
9
9
|
import standardizeFullyQualifiedModelName from '../standardizeFullyQualifiedModelName.js';
|
|
10
10
|
const STI_TYPE_COLUMN_NAME = 'type';
|
|
11
|
-
const
|
|
11
|
+
const DELETED_AT_COLUMN_NAME = 'deleted_at';
|
|
12
|
+
const COLUMNS_TO_INDEX = [STI_TYPE_COLUMN_NAME, DELETED_AT_COLUMN_NAME];
|
|
12
13
|
export default function generateMigrationContent({ connectionName = 'default', table, columnsWithTypes = [], primaryKeyType = 'bigserial', createOrAlter = 'create', stiChildClassName, softDelete = false, } = {}) {
|
|
13
14
|
const altering = createOrAlter === 'alter';
|
|
14
15
|
let requireCitextExtension = false;
|
|
@@ -25,8 +26,8 @@ export default function generateMigrationContent({ connectionName = 'default', t
|
|
|
25
26
|
return name !== 'created_at' && name !== 'updated_at' && name !== 'deleted_at';
|
|
26
27
|
});
|
|
27
28
|
const emitDeletedAtColumn = !altering && (softDelete || userExplicitlyPassedDeletedAt);
|
|
28
|
-
const { columnDefs, columnDrops, indexDefs
|
|
29
|
-
const { columnDefs, columnDrops, indexDefs
|
|
29
|
+
const { columnDefs, columnDrops, indexDefs } = processedColumnsWithTypes.reduce((acc, attributeDeclaration) => {
|
|
30
|
+
const { columnDefs, columnDrops, indexDefs } = acc;
|
|
30
31
|
const [rawSegmentOne, _attributeType, ...descriptors] = attributeDeclaration.split(':');
|
|
31
32
|
if (!rawSegmentOne)
|
|
32
33
|
return acc;
|
|
@@ -129,13 +130,7 @@ export default function generateMigrationContent({ connectionName = 'default', t
|
|
|
129
130
|
}
|
|
130
131
|
columnDrops.push(`.dropColumn('${attributeName}')`);
|
|
131
132
|
if (processedAttrType === 'belongsto' || COLUMNS_TO_INDEX.includes(attributeName)) {
|
|
132
|
-
|
|
133
|
-
indexDefs.push(`await db.schema
|
|
134
|
-
.createIndex('${indexName}')
|
|
135
|
-
.on('${table}')
|
|
136
|
-
.column('${attributeName}')
|
|
137
|
-
.execute()`);
|
|
138
|
-
indexDrops.push(`await db.schema.dropIndex('${indexName}').execute()`);
|
|
133
|
+
indexDefs.push(columnIndexStatement(table, attributeName));
|
|
139
134
|
}
|
|
140
135
|
if (stiChildClassName && !userWantsThisOptional && !arrayAttribute && !isBooleanColumn) {
|
|
141
136
|
checkConstraints.push(`
|
|
@@ -149,7 +144,10 @@ export default function generateMigrationContent({ connectionName = 'default', t
|
|
|
149
144
|
.execute()`);
|
|
150
145
|
}
|
|
151
146
|
return acc;
|
|
152
|
-
}, { columnDefs: [], columnDrops: [], indexDefs: []
|
|
147
|
+
}, { columnDefs: [], columnDrops: [], indexDefs: [] });
|
|
148
|
+
if (emitDeletedAtColumn) {
|
|
149
|
+
indexDefs.push(columnIndexStatement(table, DELETED_AT_COLUMN_NAME));
|
|
150
|
+
}
|
|
153
151
|
if (!table) {
|
|
154
152
|
return `\
|
|
155
153
|
import { Kysely, sql } from 'kysely'
|
|
@@ -196,7 +194,7 @@ ${citextExtension}${generateEnumStatements(columnsWithTypes)} await db.schema
|
|
|
196
194
|
|
|
197
195
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
198
196
|
export async function down(db: Kysely<any>): Promise<void> {
|
|
199
|
-
${
|
|
197
|
+
${altering
|
|
200
198
|
? `await db.schema${newlineDoubleIndent}.alterTable('${table}')${columnDropLines}.execute()`
|
|
201
199
|
: `await db.schema.dropTable('${table}').execute()`}${generateEnumDropStatements(columnsWithTypes)}
|
|
202
200
|
}\
|
|
@@ -393,6 +391,14 @@ function lookupReferencesTable(snakeAssociationName, originalAssociationName) {
|
|
|
393
391
|
function associationNameToForeignKey(associationName) {
|
|
394
392
|
return snakeify(associationName.replace(/\//g, '_').replace(/_id$/, '') + '_id');
|
|
395
393
|
}
|
|
394
|
+
function columnIndexStatement(table, attributeName) {
|
|
395
|
+
const indexName = `${table}_${attributeName}`;
|
|
396
|
+
return `await db.schema
|
|
397
|
+
.createIndex('${indexName}')
|
|
398
|
+
.on('${table}')
|
|
399
|
+
.column('${attributeName}')
|
|
400
|
+
.execute()`;
|
|
401
|
+
}
|
|
396
402
|
export function optionalFromDescriptors(descriptors) {
|
|
397
403
|
const optional = descriptors.at(-1) === 'optional';
|
|
398
404
|
if (optional)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import parseAttribute from './parseAttribute.js';
|
|
2
|
+
export default function validateStiChildColumns(columnsWithTypes) {
|
|
3
|
+
const belongsToColumns = columnsWithTypes.filter(columnWithType => parseAttribute(columnWithType)?.normalizedAttributeType === 'belongsto');
|
|
4
|
+
if (belongsToColumns.length) {
|
|
5
|
+
throw new Error(`STI children cannot declare belongs_to associations. Declare associations on the STI parent instead. Unsupported columns: ${belongsToColumns.join(', ')}`);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
@@ -1,22 +1,34 @@
|
|
|
1
1
|
import Dream from '../Dream.js';
|
|
2
|
+
import MaxRecursiveStringCaseDepthExceeded from '../errors/MaxRecursiveStringCaseDepthExceeded.js';
|
|
2
3
|
import BaseClockTime from '../utils/datetime/BaseClockTime.js';
|
|
3
4
|
import CalendarDate from '../utils/datetime/CalendarDate.js';
|
|
4
5
|
import { DateTime } from '../utils/datetime/DateTime.js';
|
|
5
6
|
import isObject from './isObject.js';
|
|
7
|
+
// Maximum object-nesting depth `recursiveStringCase` will traverse. Each nesting
|
|
8
|
+
// level recurses once, so without a cap a deeply-nested input (e.g. a malicious
|
|
9
|
+
// ~480KB JSON body under the request size limit) exhausts the call stack and
|
|
10
|
+
// surfaces as an uncontrolled `RangeError` (500). The cap sits far below the
|
|
11
|
+
// point at which this recursion overflows the stack (measured ~2,000+ levels)
|
|
12
|
+
// while remaining far above any legitimate nesting depth, so real application
|
|
13
|
+
// data — request params, serializer output, config objects — is never affected.
|
|
14
|
+
const MAX_RECURSIVE_STRING_CASE_DEPTH = 500;
|
|
6
15
|
export default function stringCase(target, stringCaser) {
|
|
7
16
|
if (typeof target === 'string')
|
|
8
17
|
return stringCaser(target);
|
|
9
|
-
return recursiveStringCase(target, stringCaser);
|
|
18
|
+
return recursiveStringCase(target, stringCaser, 0);
|
|
10
19
|
}
|
|
11
|
-
function recursiveStringCase(target, stringCaser) {
|
|
20
|
+
function recursiveStringCase(target, stringCaser, depth) {
|
|
12
21
|
if (target === null)
|
|
13
22
|
return null;
|
|
14
23
|
if (target === undefined)
|
|
15
24
|
return undefined;
|
|
16
25
|
if (typeof target === 'string')
|
|
17
26
|
return target;
|
|
27
|
+
if (depth >= MAX_RECURSIVE_STRING_CASE_DEPTH) {
|
|
28
|
+
throw new MaxRecursiveStringCaseDepthExceeded(MAX_RECURSIVE_STRING_CASE_DEPTH);
|
|
29
|
+
}
|
|
18
30
|
if (Array.isArray(target))
|
|
19
|
-
return target.map(s => recursiveStringCase(s, stringCaser));
|
|
31
|
+
return target.map(s => recursiveStringCase(s, stringCaser, depth + 1));
|
|
20
32
|
if (isObject(target)) {
|
|
21
33
|
if (target instanceof DateTime ||
|
|
22
34
|
target instanceof CalendarDate ||
|
|
@@ -25,7 +37,7 @@ function recursiveStringCase(target, stringCaser) {
|
|
|
25
37
|
return target;
|
|
26
38
|
}
|
|
27
39
|
return Object.keys(target).reduce((stringCasedObject, targetKey) => {
|
|
28
|
-
stringCasedObject[stringCaser(targetKey)] = recursiveStringCase(target[targetKey], stringCaser);
|
|
40
|
+
stringCasedObject[stringCaser(targetKey)] = recursiveStringCase(target[targetKey], stringCaser, depth + 1);
|
|
29
41
|
return stringCasedObject;
|
|
30
42
|
}, {});
|
|
31
43
|
}
|
|
@@ -14,7 +14,8 @@ export type SpawnOptions = Omit<NodeSpawnOptions, 'shell'> & {
|
|
|
14
14
|
args?: string[];
|
|
15
15
|
};
|
|
16
16
|
export declare const CLI_INDENT = " ";
|
|
17
|
-
export declare const baseColumnsWithTypesDescription = "space separated snake-case
|
|
17
|
+
export declare const baseColumnsWithTypesDescription = "space separated snake-case properties like this:\n title:citext subtitle:string body_markdown:text style:enum:post_styles:formal,informal\n \n all properties default to not nullable; null can be allowed by appending ':optional':\n subtitle:string:optional\n \n supported types:\n - uuid:\n - uuid[]:\n a column optimized for storing UUIDs\n \n - citext:\n - citext[]:\n case insensitive text (indexes and queries are automatically case insensitive)\n \n - encrypted:\n encrypted text (used in conjunction with the @deco.Encrypted decorator)\n \n - string:\n - string[]:\n varchar; allowed length defaults to 255, but may be customized, e.g.: subtitle:string:128 or subtitle:string:128:optional\n \n - text\n - text[]\n - date\n - date[]\n - datetime\n - datetime[]\n - time\n - time[]\n - timetz\n - timetz[]\n - integer\n - integer[]\n \n - decimal:\n - decimal[]:\n precision,scale is required, e.g.: volume:decimal:3,2 or volume:decimal:3,2:optional\n \n leveraging arrays, add the \"[]\" suffix, e.g.: volume:decimal[]:3,2\n \n - enum:\n - enum[]:\n include the enum name to automatically create the enum:\n type:enum:room_types:bathroom,kitchen,bedroom or type:enum:room_types:bathroom,kitchen,bedroom:optional\n \n omit the enum values to leverage an existing enum (omits the enum type creation):\n type:enum:room_types or type:enum:room_types:optional\n \n leveraging arrays, add the \"[]\" suffix, e.g.: type:enum[]:room_types:bathroom,kitchen,bedroom";
|
|
18
|
+
export declare const columnsWithTypesDescriptionForStiChild: string;
|
|
18
19
|
export default class DreamCLI {
|
|
19
20
|
/**
|
|
20
21
|
* Starts the Dream console
|
|
@@ -846,6 +846,15 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
|
|
|
846
846
|
static dbDriverClass<T extends Dream>(connectionName: string): typeof QueryDriverBase<T>;
|
|
847
847
|
dbDriverInstance(query?: Query<DreamInstance, any>): QueryDriverBase<DreamInstance>;
|
|
848
848
|
get connectionName(): any;
|
|
849
|
+
/**
|
|
850
|
+
* @internal
|
|
851
|
+
*
|
|
852
|
+
* Resolves the effective page size for a pagination request: applies the
|
|
853
|
+
* configured default when no page size is requested, and clamps the result to
|
|
854
|
+
* the configured maximum (`paginationMaxPageSize`, default 200) so that a
|
|
855
|
+
* forwarded, attacker-controlled `pageSize` cannot force a full-table load.
|
|
856
|
+
*/
|
|
857
|
+
private clampedPaginationPageSize;
|
|
849
858
|
/**
|
|
850
859
|
* Paginates the results of your query, accepting a pageSize and page argument,
|
|
851
860
|
* which it uses to segment your query into pages, leveraging limit and offset
|
|
@@ -362,6 +362,43 @@ export default class KyselyQueryDriver<DreamInstance extends Dream> extends Quer
|
|
|
362
362
|
* @returns A string
|
|
363
363
|
*/
|
|
364
364
|
private namespaceColumn;
|
|
365
|
+
/**
|
|
366
|
+
* @internal
|
|
367
|
+
*
|
|
368
|
+
* Defense-in-depth (DREAM-SQLI-01): when a runtime column identifier is a
|
|
369
|
+
* plain, un-namespaced column name, validate it against the owning model's
|
|
370
|
+
* compiled schema so an unknown identifier throws a Dream `InvalidColumnName`
|
|
371
|
+
* error before it reaches the database (rather than surfacing as a raw
|
|
372
|
+
* Postgres error or a silent blind-ordering side-channel).
|
|
373
|
+
*
|
|
374
|
+
* Only bare identifiers are validated. Namespaced / table-aliased /
|
|
375
|
+
* association-namespaced identifiers (anything containing a `.`) are left on
|
|
376
|
+
* their existing path, because the segment before the `.` may be an
|
|
377
|
+
* association alias or table alias rather than a schema table name, and
|
|
378
|
+
* validating those safely is not possible here without risking rejection of
|
|
379
|
+
* currently-valid input.
|
|
380
|
+
*/
|
|
381
|
+
private validatePlainColumn;
|
|
382
|
+
/**
|
|
383
|
+
* @internal
|
|
384
|
+
*
|
|
385
|
+
* Defense-in-depth (DREAM-SQLI-01) for the base model's dynamic `where` keys.
|
|
386
|
+
* These are the un-namespaced keys the developer passed to `where` /
|
|
387
|
+
* `whereNot` / `whereAny` on the root query (validated here, before
|
|
388
|
+
* `aliasWhereStatement` namespaces them with the base alias). Only plain
|
|
389
|
+
* column keys are validated. Deliberately skips:
|
|
390
|
+
* - namespaced keys (contain a `.`) — the developer explicitly namespaced
|
|
391
|
+
* them (e.g. `{ 'users.email': ... }` or a joined table), and the prefix
|
|
392
|
+
* may be an association/table alias rather than a schema table name;
|
|
393
|
+
* - association-name keys (e.g. `where({ user: userInstance })`), which are
|
|
394
|
+
* resolved to foreign keys, not columns;
|
|
395
|
+
* - `DreamConst.passthrough` / `DreamConst.required` sentinel values, whose
|
|
396
|
+
* keys are resolved elsewhere.
|
|
397
|
+
*
|
|
398
|
+
* Join-and / association where statements (which target other tables and are
|
|
399
|
+
* pre-namespaced) are left on their existing path.
|
|
400
|
+
*/
|
|
401
|
+
private validateBaseWhereStatementColumns;
|
|
365
402
|
/**
|
|
366
403
|
* @internal
|
|
367
404
|
*
|
|
@@ -118,6 +118,8 @@ export default class DreamApp {
|
|
|
118
118
|
get unicodeNormalization(): UnicodeNormalizationForm;
|
|
119
119
|
private _paginationPageSize;
|
|
120
120
|
get paginationPageSize(): number;
|
|
121
|
+
private _paginationMaxPageSize;
|
|
122
|
+
get paginationMaxPageSize(): number;
|
|
121
123
|
private _primaryKeyType;
|
|
122
124
|
get primaryKeyType(): LegacyCompatiblePrimaryKeyType;
|
|
123
125
|
private _projectRoot;
|
|
@@ -219,7 +221,7 @@ export default class DreamApp {
|
|
|
219
221
|
private requireTestDatabaseClaimSupport;
|
|
220
222
|
load<RT extends 'models' | 'serializers'>(resourceType: RT, resourcePath: string, importCb: (path: string) => Promise<any>): Promise<void>;
|
|
221
223
|
plugin(cb: (app: DreamApp) => void | Promise<void>): void;
|
|
222
|
-
set<ApplyOpt extends DreamAppSetOption>(applyOption: ApplyOpt, options: ApplyOpt extends 'bypassDeprecationChecks' ? boolean : ApplyOpt extends 'db' ? DreamDbCredentialOptions | string : ApplyOpt extends 'encryption' ? DreamAppEncryptionOptions : ApplyOpt extends 'primaryKeyType' ? LegacyCompatiblePrimaryKeyType : ApplyOpt extends 'importExtension' ? GeneratorImportStyle : ApplyOpt extends 'logger' ? DreamLogger : ApplyOpt extends 'projectRoot' ? string : ApplyOpt extends 'inflections' ? () => void | Promise<void> : ApplyOpt extends 'packageManager' ? DreamAppAllowedPackageManagersEnum : ApplyOpt extends 'paths' ? DreamDirectoryPaths : ApplyOpt extends 'parallelTests' ? number : ApplyOpt extends 'unicodeNormalization' ? UnicodeNormalizationForm : ApplyOpt extends 'paginationPageSize' ? number : never, secondaryOptions?: ApplyOpt extends 'db' ? DreamDbCredentialOptions : never): void;
|
|
224
|
+
set<ApplyOpt extends DreamAppSetOption>(applyOption: ApplyOpt, options: ApplyOpt extends 'bypassDeprecationChecks' ? boolean : ApplyOpt extends 'db' ? DreamDbCredentialOptions | string : ApplyOpt extends 'encryption' ? DreamAppEncryptionOptions : ApplyOpt extends 'primaryKeyType' ? LegacyCompatiblePrimaryKeyType : ApplyOpt extends 'importExtension' ? GeneratorImportStyle : ApplyOpt extends 'logger' ? DreamLogger : ApplyOpt extends 'projectRoot' ? string : ApplyOpt extends 'inflections' ? () => void | Promise<void> : ApplyOpt extends 'packageManager' ? DreamAppAllowedPackageManagersEnum : ApplyOpt extends 'paths' ? DreamDirectoryPaths : ApplyOpt extends 'parallelTests' ? number : ApplyOpt extends 'unicodeNormalization' ? UnicodeNormalizationForm : ApplyOpt extends 'paginationPageSize' ? number : ApplyOpt extends 'paginationMaxPageSize' ? number : never, secondaryOptions?: ApplyOpt extends 'db' ? DreamDbCredentialOptions : never): void;
|
|
223
225
|
on<T extends DreamHookEventType>(hookEventType: T, cb: T extends 'db:log' ? (event: KyselyLogEvent) => void : T extends 'repl:start' ? (context: Context) => void | Promise<void> : never): void;
|
|
224
226
|
}
|
|
225
227
|
export type DreamHookEventType = 'db:log' | 'repl:start';
|
|
@@ -232,7 +234,7 @@ export interface DreamAppOpts {
|
|
|
232
234
|
serializerCasing?: DreamSerializerCasing;
|
|
233
235
|
parallelTests: number | undefined;
|
|
234
236
|
}
|
|
235
|
-
export type DreamAppSetOption = 'bypassDeprecationChecks' | 'db' | 'encryption' | 'inflections' | 'importExtension' | 'logger' | 'paths' | 'primaryKeyType' | 'projectRoot' | 'serializerCasing' | 'parallelTests' | 'unicodeNormalization' | 'paginationPageSize' | 'packageManager';
|
|
237
|
+
export type DreamAppSetOption = 'bypassDeprecationChecks' | 'db' | 'encryption' | 'inflections' | 'importExtension' | 'logger' | 'paths' | 'primaryKeyType' | 'projectRoot' | 'serializerCasing' | 'parallelTests' | 'unicodeNormalization' | 'paginationPageSize' | 'paginationMaxPageSize' | 'packageManager';
|
|
236
238
|
export interface DreamDirectoryPaths {
|
|
237
239
|
models?: string;
|
|
238
240
|
serializers?: string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { EncryptAlgorithm } from '../../encrypt/index.js';
|
|
2
|
+
export default class DreamAppInitInvalidEncryptionKey extends Error {
|
|
3
|
+
private encryptionIdentifier;
|
|
4
|
+
private keyType;
|
|
5
|
+
private algorithm;
|
|
6
|
+
constructor(encryptionIdentifier: 'columns', keyType: 'current' | 'legacy', algorithm: EncryptAlgorithm);
|
|
7
|
+
get message(): string;
|
|
8
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function validateStiChildColumns(columnsWithTypes: string[]): void;
|
package/docs/assets/hierarchy.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
window.hierarchyData = "
|
|
1
|
+
window.hierarchyData = "eJyVksFOwzAMht/F5zCajqVVjmxCQogBEoLDtEPWejRamkxJOlRNe3cUdglj0OzkyPLv/7PjPVhjvAO+oIyVhFK6JGBxrbDy0mgHfA+U0hC0aBE4vHRo+5mVO7S3wiEQ2EhdA6d5SaCzCjhUSjiH7rpejU6qR41vFZBjBXDwrr4K8qtjgkDVSFVb1MAXOcuXBwI5yyP3h96h6qOuA/6/6i8hoDdZGRBCjBiejfMfFl06xRnFAEdwZeN48mmD1WZqtPNWSO3fpFEifNF/1mitsW70lzSFYZLFDEZ1rX7aoV0r85ni/EOQ4sfiTc+EF/e6Mu1WeLlS+C59E3Ir4fBOoqoTEAZ7XHQRbJyTsBRCWVkQWmST7wNhZXGC/dpv8Th9eD1K1wpfNYm858UJ6yuyScQxN37eKXXJqZxKhjwPhy/b2G8J"
|