@rvoh/dream 2.0.4 → 2.1.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/bin/index.js +2 -0
- package/dist/cjs/src/db/helpers/dreamSchemaTypesFilenameForConnection.js +3 -0
- package/dist/cjs/src/db/helpers/syncDbTypesFiles.js +2 -89
- package/dist/cjs/src/dream/QueryDriver/Base.js +2 -2
- package/dist/cjs/src/dream/QueryDriver/Kysely.js +9 -19
- package/dist/cjs/src/dream/QueryDriver/Postgres.js +2 -2
- package/dist/cjs/src/dream-app/index.js +15 -0
- package/dist/cjs/src/helpers/cli/ASTBuilder.js +277 -0
- package/dist/cjs/src/helpers/cli/ASTConnectionBuilder.js +284 -0
- package/dist/cjs/src/helpers/cli/ASTGlobalSchemaBuilder.js +57 -0
- package/dist/cjs/src/helpers/cli/ASTKyselyCodegenEnhancer.js +236 -0
- package/dist/cjs/src/helpers/cli/ASTSchemaBuilder.js +304 -0
- package/dist/cjs/src/helpers/cli/DBClassDeprecation.js +60 -0
- package/dist/esm/src/bin/index.js +2 -0
- package/dist/esm/src/db/helpers/dreamSchemaTypesFilenameForConnection.js +3 -0
- package/dist/esm/src/db/helpers/syncDbTypesFiles.js +2 -89
- package/dist/esm/src/dream/QueryDriver/Base.js +2 -2
- package/dist/esm/src/dream/QueryDriver/Kysely.js +9 -19
- package/dist/esm/src/dream/QueryDriver/Postgres.js +2 -2
- package/dist/esm/src/dream-app/index.js +15 -0
- package/dist/esm/src/helpers/cli/ASTBuilder.js +277 -0
- package/dist/esm/src/helpers/cli/ASTConnectionBuilder.js +284 -0
- package/dist/esm/src/helpers/cli/ASTGlobalSchemaBuilder.js +57 -0
- package/dist/esm/src/helpers/cli/ASTKyselyCodegenEnhancer.js +236 -0
- package/dist/esm/src/helpers/cli/ASTSchemaBuilder.js +304 -0
- package/dist/esm/src/helpers/cli/DBClassDeprecation.js +60 -0
- package/dist/types/src/db/helpers/dreamSchemaTypesFilenameForConnection.d.ts +1 -0
- package/dist/types/src/dream/QueryDriver/Base.d.ts +3 -3
- package/dist/types/src/dream/QueryDriver/Kysely.d.ts +2 -14
- package/dist/types/src/dream/QueryDriver/Postgres.d.ts +2 -2
- package/dist/types/src/dream-app/index.d.ts +12 -2
- package/dist/types/src/helpers/cli/ASTBuilder.d.ts +159 -0
- package/dist/types/src/helpers/cli/ASTConnectionBuilder.d.ts +104 -0
- package/dist/types/src/helpers/cli/ASTGlobalSchemaBuilder.d.ts +28 -0
- package/dist/types/src/helpers/cli/ASTKyselyCodegenEnhancer.d.ts +68 -0
- package/dist/types/src/helpers/cli/ASTSchemaBuilder.d.ts +80 -0
- package/dist/types/src/helpers/cli/DBClassDeprecation.d.ts +14 -0
- package/docs/assets/search.js +1 -1
- package/docs/classes/db.DreamMigrationHelpers.html +9 -9
- package/docs/classes/db.KyselyQueryDriver.html +31 -44
- package/docs/classes/db.PostgresQueryDriver.html +32 -45
- package/docs/classes/db.QueryDriverBase.html +30 -30
- package/docs/classes/errors.CheckConstraintViolation.html +3 -3
- package/docs/classes/errors.ColumnOverflow.html +3 -3
- package/docs/classes/errors.CreateOrFindByFailedToCreateAndFind.html +3 -3
- package/docs/classes/errors.DataIncompatibleWithDatabaseField.html +3 -3
- package/docs/classes/errors.DataTypeColumnTypeMismatch.html +3 -3
- package/docs/classes/errors.GlobalNameNotSet.html +3 -3
- package/docs/classes/errors.InvalidCalendarDate.html +2 -2
- package/docs/classes/errors.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 +2 -2
- package/docs/classes/index.Decorators.html +19 -19
- package/docs/classes/index.Dream.html +113 -113
- package/docs/classes/index.DreamApp.html +7 -6
- package/docs/classes/index.DreamTransaction.html +2 -2
- package/docs/classes/index.Env.html +2 -2
- package/docs/classes/index.Query.html +53 -53
- package/docs/classes/system.CliFileWriter.html +2 -2
- package/docs/classes/system.DreamBin.html +2 -2
- package/docs/classes/system.DreamCLI.html +5 -5
- package/docs/classes/system.DreamImporter.html +2 -2
- package/docs/classes/system.DreamLogos.html +2 -2
- package/docs/classes/system.DreamSerializerBuilder.html +8 -8
- package/docs/classes/system.ObjectSerializerBuilder.html +8 -8
- package/docs/classes/utils.Encrypt.html +2 -2
- package/docs/classes/utils.Range.html +2 -2
- package/docs/functions/db.closeAllDbConnections.html +1 -1
- package/docs/functions/db.dreamDbConnections.html +1 -1
- package/docs/functions/db.untypedDb.html +1 -1
- package/docs/functions/db.validateColumn.html +1 -1
- package/docs/functions/db.validateTable.html +1 -1
- package/docs/functions/errors.pgErrorType.html +1 -1
- package/docs/functions/index.DreamSerializer.html +1 -1
- package/docs/functions/index.ObjectSerializer.html +1 -1
- package/docs/functions/index.ReplicaSafe.html +1 -1
- package/docs/functions/index.STI.html +1 -1
- package/docs/functions/index.SoftDelete.html +1 -1
- package/docs/functions/utils.camelize.html +1 -1
- package/docs/functions/utils.capitalize.html +1 -1
- package/docs/functions/utils.cloneDeepSafe.html +1 -1
- package/docs/functions/utils.compact.html +1 -1
- package/docs/functions/utils.groupBy.html +1 -1
- package/docs/functions/utils.hyphenize.html +1 -1
- package/docs/functions/utils.intersection.html +1 -1
- package/docs/functions/utils.isEmpty.html +1 -1
- package/docs/functions/utils.normalizeUnicode.html +1 -1
- package/docs/functions/utils.pascalize.html +1 -1
- package/docs/functions/utils.percent.html +1 -1
- package/docs/functions/utils.range-1.html +1 -1
- package/docs/functions/utils.round.html +1 -1
- package/docs/functions/utils.sanitizeString.html +1 -1
- package/docs/functions/utils.snakeify.html +1 -1
- package/docs/functions/utils.sort.html +1 -1
- package/docs/functions/utils.sortBy.html +1 -1
- package/docs/functions/utils.sortObjectByKey.html +1 -1
- package/docs/functions/utils.sortObjectByValue.html +1 -1
- package/docs/functions/utils.uncapitalize.html +1 -1
- package/docs/functions/utils.uniq.html +1 -1
- package/docs/interfaces/openapi.OpenapiDescription.html +2 -2
- package/docs/interfaces/openapi.OpenapiSchemaProperties.html +1 -1
- package/docs/interfaces/openapi.OpenapiSchemaPropertiesShorthand.html +1 -1
- package/docs/interfaces/openapi.OpenapiTypeFieldObject.html +1 -1
- package/docs/interfaces/types.BelongsToStatement.html +2 -2
- package/docs/interfaces/types.DecoratorContext.html +2 -2
- package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
- package/docs/interfaces/types.DreamAppOpts.html +2 -2
- package/docs/interfaces/types.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/modules/db.html +1 -1
- package/docs/modules/errors.html +1 -1
- package/docs/modules/index.html +1 -1
- package/docs/modules/openapi.html +1 -1
- package/docs/modules/system.html +1 -1
- package/docs/modules/types.html +1 -1
- package/docs/modules/utils.html +1 -1
- package/docs/types/index.DateTime.html +1 -1
- 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 +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionRef.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionRefSchemaShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaInteger.html +1 -1
- package/docs/types/openapi.OpenapiSchemaNull.html +1 -1
- 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 +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializableRef.html +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializerRef.html +1 -1
- 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.Camelized.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 +1 -1
- 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.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.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 +1 -1
- package/docs/types/types.ViewModelClass.html +1 -1
- package/docs/types/types.WhereStatementForDream.html +1 -1
- package/docs/types/types.WhereStatementForDreamClass.html +1 -1
- package/docs/variables/index.DateTime-1.html +1 -1
- package/docs/variables/index.DreamConst.html +1 -1
- package/docs/variables/index.ops.html +1 -1
- package/docs/variables/openapi.openapiPrimitiveTypes-1.html +1 -1
- package/docs/variables/openapi.openapiShorthandPrimitiveTypes-1.html +1 -1
- package/docs/variables/system.DreamAppAllowedPackageManagersEnumValues.html +1 -1
- package/docs/variables/types.TRIGRAM_OPERATORS.html +1 -1
- package/docs/variables/types.primaryKeyTypes.html +1 -1
- package/package.json +1 -1
- package/dist/cjs/src/helpers/cli/SchemaBuilder.js +0 -408
- package/dist/esm/src/helpers/cli/SchemaBuilder.js +0 -408
- package/dist/types/src/helpers/cli/SchemaBuilder.d.ts +0 -44
|
@@ -1,408 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs/promises';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import { CliFileWriter } from '../../cli/CliFileWriter.js';
|
|
4
|
-
import dbTypesFilenameForConnection from '../../db/helpers/dbTypesFilenameForConnection.js';
|
|
5
|
-
import DreamApp from '../../dream-app/index.js';
|
|
6
|
-
import { DreamConst } from '../../dream/constants.js';
|
|
7
|
-
import Query from '../../dream/Query.js';
|
|
8
|
-
import { ExplicitForeignKeyRequired, InvalidComputedForeignKey, } from '../../errors/associations/InvalidComputedForeignKey.js';
|
|
9
|
-
import FailedToIdentifyAssociation from '../../errors/schema-builder/FailedToIdentifyAssociation.js';
|
|
10
|
-
import camelize from '../camelize.js';
|
|
11
|
-
import compact from '../compact.js';
|
|
12
|
-
import EnvInternal from '../EnvInternal.js';
|
|
13
|
-
import intersection from '../intersection.js';
|
|
14
|
-
import pascalize from '../pascalize.js';
|
|
15
|
-
import sortBy from '../sortBy.js';
|
|
16
|
-
import uniq from '../uniq.js';
|
|
17
|
-
import autogeneratedFileDisclaimer from './autoGeneratedFileDisclaimer.js';
|
|
18
|
-
export default class SchemaBuilder {
|
|
19
|
-
connectionName;
|
|
20
|
-
hasForeignKeyError = false;
|
|
21
|
-
constructor(connectionName) {
|
|
22
|
-
this.connectionName = connectionName;
|
|
23
|
-
}
|
|
24
|
-
async build() {
|
|
25
|
-
const { schemaConstContent, passthroughColumns, allDefaultScopeNames } = await this.buildSchemaContent();
|
|
26
|
-
const imports = await this.getSchemaImports(schemaConstContent);
|
|
27
|
-
const importStr = imports.length
|
|
28
|
-
? `\
|
|
29
|
-
import {
|
|
30
|
-
${imports.sort().join(',\n ')}
|
|
31
|
-
} from './db.js'`
|
|
32
|
-
: '';
|
|
33
|
-
const calendarDateImportStatement = EnvInternal.boolean('DREAM_CORE_DEVELOPMENT')
|
|
34
|
-
? "import type CalendarDate from '../../src/helpers/CalendarDate.js'\nimport { type DateTime } from '../../src/helpers/DateTime.js'"
|
|
35
|
-
: "import { type CalendarDate, type DateTime } from '@rvoh/dream'";
|
|
36
|
-
const dreamApp = DreamApp.getOrFail();
|
|
37
|
-
const newSchemaFileContents = `\
|
|
38
|
-
${autogeneratedFileDisclaimer()}
|
|
39
|
-
${calendarDateImportStatement}
|
|
40
|
-
${importStr}
|
|
41
|
-
|
|
42
|
-
${schemaConstContent}
|
|
43
|
-
|
|
44
|
-
export const connectionTypeConfig = {
|
|
45
|
-
passthroughColumns: ${stringifyArray(uniq(passthroughColumns.sort()), { indent: 4 })},
|
|
46
|
-
allDefaultScopeNames: ${stringifyArray(uniq(allDefaultScopeNames.sort()), { indent: 4 })},
|
|
47
|
-
globalNames: {
|
|
48
|
-
models: ${this.globalModelNames()},
|
|
49
|
-
},
|
|
50
|
-
} as const
|
|
51
|
-
`;
|
|
52
|
-
const schemaPath = this.connectionName === 'default'
|
|
53
|
-
? path.join(dreamApp.projectRoot, dreamApp.paths.types, 'dream.ts')
|
|
54
|
-
: path.join(dreamApp.projectRoot, dreamApp.paths.types, `dream.${camelize(this.connectionName)}.ts`);
|
|
55
|
-
await CliFileWriter.write(schemaPath, newSchemaFileContents);
|
|
56
|
-
}
|
|
57
|
-
static async buildGlobalTypes() {
|
|
58
|
-
const dreamApp = DreamApp.getOrFail();
|
|
59
|
-
const newSchemaFileContents = `\
|
|
60
|
-
export const globalTypeConfig = {
|
|
61
|
-
serializers: ${stringifyArray(Object.keys(dreamApp.serializers || {}).sort(), { indent: 6 })},
|
|
62
|
-
} as const
|
|
63
|
-
`;
|
|
64
|
-
const schemaPath = path.join(dreamApp.projectRoot, dreamApp.paths.types, 'dream.globals.ts');
|
|
65
|
-
await CliFileWriter.write(schemaPath, newSchemaFileContents);
|
|
66
|
-
}
|
|
67
|
-
globalModelNames() {
|
|
68
|
-
const dreamApp = DreamApp.getOrFail();
|
|
69
|
-
const models = dreamApp.models;
|
|
70
|
-
return `{
|
|
71
|
-
${Object.keys(models)
|
|
72
|
-
.filter(key => models[key]?.prototype?.connectionName === this.connectionName)
|
|
73
|
-
.map(key => `'${key}': '${models[key]?.prototype?.table}'`)
|
|
74
|
-
.join(',\n ')}
|
|
75
|
-
}`;
|
|
76
|
-
}
|
|
77
|
-
async buildSchemaContent() {
|
|
78
|
-
let passthroughColumns = [];
|
|
79
|
-
let allDefaultScopeNames = [];
|
|
80
|
-
const schemaData = await this.getSchemaData();
|
|
81
|
-
const fileContents = await this.loadDbSyncFile();
|
|
82
|
-
const schemaConstContent = `\
|
|
83
|
-
export const schema = {
|
|
84
|
-
${Object.keys(schemaData)
|
|
85
|
-
.map(tableName => {
|
|
86
|
-
const tableData = schemaData[tableName];
|
|
87
|
-
if (tableData === undefined)
|
|
88
|
-
return '';
|
|
89
|
-
const defaultScopeNames = tableData.scopes.default;
|
|
90
|
-
const namedScopeNames = tableData.scopes.named;
|
|
91
|
-
allDefaultScopeNames = [...allDefaultScopeNames, ...defaultScopeNames];
|
|
92
|
-
const schemaDatum = schemaData[tableName];
|
|
93
|
-
if (schemaDatum === undefined)
|
|
94
|
-
return '';
|
|
95
|
-
return `\
|
|
96
|
-
${tableName}: {
|
|
97
|
-
serializerKeys: ${stringifyArray(tableData.serializerKeys)},
|
|
98
|
-
scopes: {
|
|
99
|
-
default: ${stringifyArray(defaultScopeNames)},
|
|
100
|
-
named: ${stringifyArray(namedScopeNames)},
|
|
101
|
-
},
|
|
102
|
-
nonJsonColumnNames: ${stringifyArray(Object.keys(schemaDatum.columns).filter(columnName => !['json', 'jsonb', 'json[]', 'jsonb[]'].includes(tableData.columns[columnName].dbType)))},
|
|
103
|
-
columns: {
|
|
104
|
-
${Object.keys(schemaDatum.columns)
|
|
105
|
-
.sort()
|
|
106
|
-
.map(columnName => {
|
|
107
|
-
const columnData = tableData.columns[columnName];
|
|
108
|
-
if (columnData === undefined)
|
|
109
|
-
return '';
|
|
110
|
-
const kyselyType = this.kyselyType(tableName, columnName, fileContents);
|
|
111
|
-
if (kyselyType === undefined)
|
|
112
|
-
return '';
|
|
113
|
-
return `${columnName}: {
|
|
114
|
-
coercedType: {} as ${this.coercedType(kyselyType, columnData.dbType)},
|
|
115
|
-
enumType: ${columnData.enumType ? `{} as ${columnData.enumType}` : 'null'},
|
|
116
|
-
enumArrayType: ${columnData.enumType ? `[] as ${columnData.enumType}[]` : 'null'},
|
|
117
|
-
enumValues: ${columnData.enumValues ?? 'null'},
|
|
118
|
-
dbType: '${columnData.dbType}',
|
|
119
|
-
allowNull: ${columnData.allowNull},
|
|
120
|
-
isArray: ${columnData.isArray},
|
|
121
|
-
},`;
|
|
122
|
-
})
|
|
123
|
-
.join('\n ')}
|
|
124
|
-
},
|
|
125
|
-
virtualColumns: ${stringifyArray(schemaDatum.virtualColumns)},
|
|
126
|
-
associations: {
|
|
127
|
-
${Object.keys(schemaDatum.associations)
|
|
128
|
-
.sort()
|
|
129
|
-
.map(associationName => {
|
|
130
|
-
const associationMetadata = tableData.associations[associationName];
|
|
131
|
-
if (associationMetadata === undefined)
|
|
132
|
-
return '';
|
|
133
|
-
const andStatement = associationMetadata.and;
|
|
134
|
-
const requiredAndClauses = andStatement === null
|
|
135
|
-
? []
|
|
136
|
-
: Object.keys(andStatement).filter(column => andStatement[column] === DreamConst.required);
|
|
137
|
-
const passthroughAndClauses = andStatement === null
|
|
138
|
-
? []
|
|
139
|
-
: Object.keys(andStatement).filter(column => andStatement[column] === DreamConst.passthrough);
|
|
140
|
-
passthroughColumns = [...passthroughColumns, ...passthroughAndClauses];
|
|
141
|
-
return `${associationName}: {
|
|
142
|
-
type: '${associationMetadata.type}',
|
|
143
|
-
foreignKey: ${associationMetadata.foreignKey ? `'${associationMetadata.foreignKey}'` : 'null'},
|
|
144
|
-
foreignKeyTypeColumn: ${associationMetadata.foreignKeyTypeColumn ? `'${associationMetadata.foreignKeyTypeColumn}'` : 'null'},
|
|
145
|
-
tables: ${stringifyArray(associationMetadata.tables)},
|
|
146
|
-
optional: ${associationMetadata.optional},
|
|
147
|
-
requiredAndClauses: ${requiredAndClauses.length === 0 ? 'null' : stringifyArray(requiredAndClauses)},
|
|
148
|
-
passthroughAndClauses: ${passthroughAndClauses.length === 0 ? 'null' : stringifyArray(passthroughAndClauses)},
|
|
149
|
-
},`;
|
|
150
|
-
})
|
|
151
|
-
.join('\n ')}
|
|
152
|
-
},
|
|
153
|
-
},\
|
|
154
|
-
`;
|
|
155
|
-
})
|
|
156
|
-
.join('\n ')}
|
|
157
|
-
} as const`;
|
|
158
|
-
return { schemaConstContent, passthroughColumns, allDefaultScopeNames };
|
|
159
|
-
}
|
|
160
|
-
async getSchemaImports(schemaContent) {
|
|
161
|
-
const allExports = await this.getExportedModulesFromDbSync();
|
|
162
|
-
const schemaContentWithoutImports = schemaContent.replace(/import {[^}]*}/gm, '');
|
|
163
|
-
return allExports.filter(exportedModule => {
|
|
164
|
-
if (new RegExp(`coercedType: {} as ${exportedModule}`).test(schemaContentWithoutImports))
|
|
165
|
-
return true;
|
|
166
|
-
if (new RegExp(`coercedType: {} as ArrayType<${exportedModule}`).test(schemaContentWithoutImports))
|
|
167
|
-
return true;
|
|
168
|
-
if (new RegExp(`enumType: {} as ${exportedModule}`).test(schemaContentWithoutImports))
|
|
169
|
-
return true;
|
|
170
|
-
if (new RegExp(`enumValues: ${exportedModule}`).test(schemaContentWithoutImports))
|
|
171
|
-
return true;
|
|
172
|
-
return false;
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
async tableData(tableName) {
|
|
176
|
-
const dreamApp = DreamApp.getOrFail();
|
|
177
|
-
const models = Object.values(dreamApp.models).filter(model => model.table === tableName);
|
|
178
|
-
const maybeModel = models[0];
|
|
179
|
-
if (!maybeModel)
|
|
180
|
-
throw new Error(`
|
|
181
|
-
Could not find a Dream model with table "${tableName}".
|
|
182
|
-
|
|
183
|
-
If you recently changed the name of a table in a migration, you
|
|
184
|
-
may need to update the table getter in the corresponding Dream.
|
|
185
|
-
`);
|
|
186
|
-
const baseModel = maybeModel['stiBaseClassOrOwnClass'];
|
|
187
|
-
const associationData = this.getAssociationData(tableName);
|
|
188
|
-
const allStiChildren = models.filter(model => model['isSTIChild']);
|
|
189
|
-
const modelsToCheck = allStiChildren.length ? allStiChildren : [baseModel];
|
|
190
|
-
// If a table is STI, then we look only at the serializers attached to
|
|
191
|
-
// all STI children (not the STI base model because the base model may not have any serializers)
|
|
192
|
-
const eachModelSerializerKeys = modelsToCheck.map(model => {
|
|
193
|
-
let serializers = {};
|
|
194
|
-
try {
|
|
195
|
-
serializers = model?.prototype?.['serializers'] || {};
|
|
196
|
-
}
|
|
197
|
-
catch {
|
|
198
|
-
// no-op
|
|
199
|
-
}
|
|
200
|
-
return Object.keys(serializers);
|
|
201
|
-
});
|
|
202
|
-
const serializerKeys = intersection(...eachModelSerializerKeys);
|
|
203
|
-
return {
|
|
204
|
-
scopes: {
|
|
205
|
-
default: uniq(models.flatMap(model => model['scopes'].default.map(scopeStatement => scopeStatement.method))),
|
|
206
|
-
named: uniq(models.flatMap(model => model['scopes'].named.map(scopeStatement => scopeStatement.method))),
|
|
207
|
-
},
|
|
208
|
-
columns: await this.getColumnData(tableName, associationData),
|
|
209
|
-
virtualColumns: uniq(models.flatMap(model => model['virtualAttributes'].map(prop => prop.property) || [])),
|
|
210
|
-
associations: associationData,
|
|
211
|
-
serializerKeys,
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
async getColumnData(tableName, associationData) {
|
|
215
|
-
const dbDriverClass = Query.dbDriverClass(this.connectionName);
|
|
216
|
-
return await dbDriverClass.getColumnData(this.connectionName, tableName, associationData);
|
|
217
|
-
}
|
|
218
|
-
enumType(row) {
|
|
219
|
-
const enumName = pascalize(row.udtName.replace(/\[\]$/, ''));
|
|
220
|
-
return enumName;
|
|
221
|
-
}
|
|
222
|
-
async getSchemaData() {
|
|
223
|
-
const tables = await this.getTables();
|
|
224
|
-
const schemaData = {};
|
|
225
|
-
for (const table of tables) {
|
|
226
|
-
schemaData[table] = await this.tableData(table);
|
|
227
|
-
}
|
|
228
|
-
return schemaData;
|
|
229
|
-
}
|
|
230
|
-
getAssociationData(tableName, targetAssociationType) {
|
|
231
|
-
const dreamApp = DreamApp.getOrFail();
|
|
232
|
-
const models = sortBy(Object.values(dreamApp.models), m => m.table);
|
|
233
|
-
const tableAssociationData = {};
|
|
234
|
-
for (const model of models.filter(model => model.table === tableName)) {
|
|
235
|
-
for (const associationName of model.associationNames) {
|
|
236
|
-
const associationMetaData = model['associationMetadataMap']()[associationName];
|
|
237
|
-
if (associationMetaData === undefined)
|
|
238
|
-
continue;
|
|
239
|
-
if (targetAssociationType && associationMetaData.type !== targetAssociationType)
|
|
240
|
-
continue;
|
|
241
|
-
const dreamClassOrClasses = associationMetaData.modelCB();
|
|
242
|
-
if (!dreamClassOrClasses)
|
|
243
|
-
throw new FailedToIdentifyAssociation(model, associationMetaData.type, associationName, associationMetaData.globalAssociationNameOrNames);
|
|
244
|
-
const optional = associationMetaData.type === 'BelongsTo' ? associationMetaData.optional === true : null;
|
|
245
|
-
const where = associationMetaData.type === 'HasMany' || associationMetaData.type === 'HasOne'
|
|
246
|
-
? associationMetaData.and || null
|
|
247
|
-
: null;
|
|
248
|
-
// NOTE
|
|
249
|
-
// this try-catch is here because the SchemaBuilder currently needs to be run twice to generate foreignKey
|
|
250
|
-
// correctly. The first time will raise, since calling Dream.columns is dependant on the schema const to
|
|
251
|
-
// introspect columns during a foreign key check. This will be repaired once kysely types have been successfully
|
|
252
|
-
// split off into a separate file from the types we diliver in types/dream.ts
|
|
253
|
-
let foreignKey = null;
|
|
254
|
-
try {
|
|
255
|
-
const isThroughAssociation = associationMetaData.through;
|
|
256
|
-
if (!isThroughAssociation) {
|
|
257
|
-
const _foreignKey = associationMetaData.foreignKey();
|
|
258
|
-
foreignKey = _foreignKey;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
catch (err) {
|
|
262
|
-
this.hasForeignKeyError = true;
|
|
263
|
-
// console.error(err)
|
|
264
|
-
}
|
|
265
|
-
try {
|
|
266
|
-
tableAssociationData[associationName] ||= {
|
|
267
|
-
tables: [],
|
|
268
|
-
type: associationMetaData.type,
|
|
269
|
-
polymorphic: associationMetaData.polymorphic,
|
|
270
|
-
foreignKey,
|
|
271
|
-
foreignKeyTypeColumn: associationMetaData.polymorphic
|
|
272
|
-
? associationMetaData?.foreignKeyTypeField?.() || null
|
|
273
|
-
: null,
|
|
274
|
-
optional,
|
|
275
|
-
and: where,
|
|
276
|
-
};
|
|
277
|
-
if (foreignKey)
|
|
278
|
-
tableAssociationData[associationName]['foreignKey'] = foreignKey;
|
|
279
|
-
if (Array.isArray(dreamClassOrClasses)) {
|
|
280
|
-
const tables = dreamClassOrClasses.map(dreamClass => dreamClass.table);
|
|
281
|
-
tableAssociationData[associationName].tables = [
|
|
282
|
-
...tableAssociationData[associationName].tables,
|
|
283
|
-
...tables,
|
|
284
|
-
];
|
|
285
|
-
}
|
|
286
|
-
else {
|
|
287
|
-
tableAssociationData[associationName].tables.push(dreamClassOrClasses.table);
|
|
288
|
-
}
|
|
289
|
-
// guarantee unique
|
|
290
|
-
tableAssociationData[associationName].tables = [
|
|
291
|
-
...new Set(tableAssociationData[associationName].tables),
|
|
292
|
-
];
|
|
293
|
-
}
|
|
294
|
-
catch (error) {
|
|
295
|
-
if (!(error instanceof ExplicitForeignKeyRequired || error instanceof InvalidComputedForeignKey))
|
|
296
|
-
throw error;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
return Object.keys(tableAssociationData)
|
|
301
|
-
.sort()
|
|
302
|
-
.reduce((acc, key) => {
|
|
303
|
-
if (tableAssociationData[key] === undefined)
|
|
304
|
-
return acc;
|
|
305
|
-
acc[key] = tableAssociationData[key];
|
|
306
|
-
return acc;
|
|
307
|
-
}, {});
|
|
308
|
-
}
|
|
309
|
-
async getExportedModulesFromDbSync() {
|
|
310
|
-
const fileContents = await this.loadDbSyncFile();
|
|
311
|
-
const exportedConsts = [...fileContents.matchAll(/export\s+const\s+([a-zA-Z0-9_]+)/g)].map(res => res[1]);
|
|
312
|
-
const exportedTypes = [...fileContents.matchAll(/export\s+type\s+([a-zA-Z0-9_]+)/g)].map(res => res[1]);
|
|
313
|
-
const exportedInterfaces = [...fileContents.matchAll(/export\s+interface\s+([a-zA-Z0-9_]+)/g)].map(res => res[1]);
|
|
314
|
-
const allExports = compact([...exportedConsts, ...exportedTypes, ...exportedInterfaces]);
|
|
315
|
-
return allExports;
|
|
316
|
-
}
|
|
317
|
-
async getTables() {
|
|
318
|
-
const fileContents = await this.loadDbSyncFile();
|
|
319
|
-
const tableLines = /export interface DB {([^}]*)}/.exec(fileContents)[1];
|
|
320
|
-
if (tableLines === undefined)
|
|
321
|
-
return [];
|
|
322
|
-
const tables = tableLines
|
|
323
|
-
.split('\n')
|
|
324
|
-
.map(line => {
|
|
325
|
-
const stingArray = line.split(':');
|
|
326
|
-
const substring = stingArray[0];
|
|
327
|
-
if (substring === undefined)
|
|
328
|
-
return '';
|
|
329
|
-
return substring.replace(/\s*/, '');
|
|
330
|
-
})
|
|
331
|
-
.filter(line => !!line);
|
|
332
|
-
return tables;
|
|
333
|
-
}
|
|
334
|
-
kyselyType(tableName, columnName, fileContents) {
|
|
335
|
-
const tableLinesString = /export interface DB {([^}]*)}/.exec(fileContents)[1];
|
|
336
|
-
if (tableLinesString === undefined)
|
|
337
|
-
return '';
|
|
338
|
-
const tableLines = tableLinesString
|
|
339
|
-
.split('\n')
|
|
340
|
-
.filter(line => !!line)
|
|
341
|
-
.filter(line => new RegExp(`^ ${tableName}:`).test(line));
|
|
342
|
-
const tableLine = tableLines[0];
|
|
343
|
-
if (tableLine === undefined)
|
|
344
|
-
return '';
|
|
345
|
-
const interfaceName = tableLine.split(':')[1]?.replace(/[\s;]*/g, '');
|
|
346
|
-
const interfaceLinesString = new RegExp(`export interface ${interfaceName} {([^}]*)}`).exec(fileContents)[1];
|
|
347
|
-
if (interfaceLinesString === undefined)
|
|
348
|
-
return '';
|
|
349
|
-
const interfaceLines = interfaceLinesString
|
|
350
|
-
.split('\n')
|
|
351
|
-
.filter(line => !!line)
|
|
352
|
-
.filter(line => new RegExp(` ${columnName}:`).test(line));
|
|
353
|
-
const interfaceLine = interfaceLines[0];
|
|
354
|
-
if (interfaceLine === undefined)
|
|
355
|
-
return '';
|
|
356
|
-
const kyselyType = interfaceLine.split(':')[1]?.replace(/[\s;]*/g, '');
|
|
357
|
-
return kyselyType;
|
|
358
|
-
}
|
|
359
|
-
coercedType(kyselyType, dbType) {
|
|
360
|
-
const postfix = /\[\]$/.test(dbType) ? '[]' : '';
|
|
361
|
-
return kyselyType
|
|
362
|
-
.replace(/\s/g, '')
|
|
363
|
-
.replace(/Generated<(.*)>/g, '$1')
|
|
364
|
-
.replace(/ArrayType<(.*)>/g, '$1[]')
|
|
365
|
-
.split('|')
|
|
366
|
-
.map(individualType => {
|
|
367
|
-
switch (individualType) {
|
|
368
|
-
case 'Numeric':
|
|
369
|
-
case 'Numeric[]':
|
|
370
|
-
return `number${postfix}`;
|
|
371
|
-
case 'Timestamp':
|
|
372
|
-
case 'Timestamp[]':
|
|
373
|
-
return /^date[[\]]*$/.test(dbType) ? `CalendarDate${postfix}` : `DateTime${postfix}`;
|
|
374
|
-
case 'Int8':
|
|
375
|
-
case 'Int8[]':
|
|
376
|
-
return `string${postfix}`;
|
|
377
|
-
default:
|
|
378
|
-
return individualType;
|
|
379
|
-
}
|
|
380
|
-
})
|
|
381
|
-
.join(' | ');
|
|
382
|
-
}
|
|
383
|
-
async loadDbSyncFile() {
|
|
384
|
-
const dreamApp = DreamApp.getOrFail();
|
|
385
|
-
const dbSyncPath = path.join(dreamApp.projectRoot, dreamApp.paths.types, dbTypesFilenameForConnection(this.connectionName));
|
|
386
|
-
return (await fs.readFile(dbSyncPath)).toString();
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
function stringifyArray(arr = [], { indent } = {}) {
|
|
390
|
-
if (indent && arr.length > 3) {
|
|
391
|
-
let spaces = '';
|
|
392
|
-
for (let i = 0; i < indent; i++) {
|
|
393
|
-
spaces = `${spaces} `;
|
|
394
|
-
}
|
|
395
|
-
return `[
|
|
396
|
-
${spaces}${[...arr]
|
|
397
|
-
.sort()
|
|
398
|
-
.map(val => `'${val}'`)
|
|
399
|
-
.join(`,\n${spaces}`)}
|
|
400
|
-
${spaces.replace(/\s{2}$/, '')}]`;
|
|
401
|
-
}
|
|
402
|
-
else {
|
|
403
|
-
return `[${[...arr]
|
|
404
|
-
.sort()
|
|
405
|
-
.map(val => `'${val}'`)
|
|
406
|
-
.join(', ')}]`;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { DreamConst } from '../../dream/constants.js';
|
|
2
|
-
export default class SchemaBuilder {
|
|
3
|
-
private connectionName;
|
|
4
|
-
hasForeignKeyError: boolean;
|
|
5
|
-
constructor(connectionName: string);
|
|
6
|
-
build(): Promise<void>;
|
|
7
|
-
static buildGlobalTypes(): Promise<void>;
|
|
8
|
-
private globalModelNames;
|
|
9
|
-
private buildSchemaContent;
|
|
10
|
-
private getSchemaImports;
|
|
11
|
-
private tableData;
|
|
12
|
-
private getColumnData;
|
|
13
|
-
private enumType;
|
|
14
|
-
private getSchemaData;
|
|
15
|
-
private getAssociationData;
|
|
16
|
-
private getExportedModulesFromDbSync;
|
|
17
|
-
private getTables;
|
|
18
|
-
private kyselyType;
|
|
19
|
-
private coercedType;
|
|
20
|
-
private loadDbSyncFile;
|
|
21
|
-
}
|
|
22
|
-
export interface SchemaBuilderAssociationData {
|
|
23
|
-
tables: string[];
|
|
24
|
-
type: 'BelongsTo' | 'HasOne' | 'HasMany';
|
|
25
|
-
polymorphic: boolean;
|
|
26
|
-
optional: boolean | null;
|
|
27
|
-
foreignKey: string | null;
|
|
28
|
-
foreignKeyTypeColumn: string | null;
|
|
29
|
-
and: Record<string, string | typeof DreamConst.passthrough | typeof DreamConst.required> | null;
|
|
30
|
-
}
|
|
31
|
-
export interface SchemaBuilderColumnData {
|
|
32
|
-
dbType: string;
|
|
33
|
-
allowNull: boolean;
|
|
34
|
-
enumType: string | null;
|
|
35
|
-
enumValues: string | null;
|
|
36
|
-
foreignKey: string | null;
|
|
37
|
-
isArray: boolean;
|
|
38
|
-
}
|
|
39
|
-
export interface SchemaBuilderInformationSchemaRow {
|
|
40
|
-
columnName: string;
|
|
41
|
-
udtName: string;
|
|
42
|
-
dataType: string;
|
|
43
|
-
isNullable: 'YES' | 'NO';
|
|
44
|
-
}
|