@rvoh/dream 2.0.0-alpha.11 → 2.0.0-alpha.13

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.
Files changed (180) hide show
  1. package/dist/cjs/src/Dream.js +24 -16
  2. package/dist/cjs/src/helpers/cli/generateDreamContent.js +167 -74
  3. package/dist/esm/src/Dream.js +24 -16
  4. package/dist/esm/src/helpers/cli/generateDreamContent.js +167 -74
  5. package/dist/types/src/Dream.d.ts +1 -0
  6. package/dist/types/src/helpers/cli/generateDreamContent.d.ts +31 -2
  7. package/docs/classes/db.DreamMigrationHelpers.html +9 -9
  8. package/docs/classes/db.KyselyQueryDriver.html +30 -30
  9. package/docs/classes/db.PostgresQueryDriver.html +31 -31
  10. package/docs/classes/db.QueryDriverBase.html +29 -29
  11. package/docs/classes/errors.CheckConstraintViolation.html +3 -3
  12. package/docs/classes/errors.ColumnOverflow.html +3 -3
  13. package/docs/classes/errors.CreateOrFindByFailedToCreateAndFind.html +3 -3
  14. package/docs/classes/errors.DataIncompatibleWithDatabaseField.html +3 -3
  15. package/docs/classes/errors.DataTypeColumnTypeMismatch.html +3 -3
  16. package/docs/classes/errors.GlobalNameNotSet.html +3 -3
  17. package/docs/classes/errors.InvalidCalendarDate.html +2 -2
  18. package/docs/classes/errors.MissingSerializersDefinition.html +3 -3
  19. package/docs/classes/errors.NonLoadedAssociation.html +3 -3
  20. package/docs/classes/errors.NotNullViolation.html +3 -3
  21. package/docs/classes/errors.RecordNotFound.html +3 -3
  22. package/docs/classes/errors.ValidationError.html +3 -3
  23. package/docs/classes/index.CalendarDate.html +2 -2
  24. package/docs/classes/index.Decorators.html +19 -19
  25. package/docs/classes/index.Dream.html +113 -113
  26. package/docs/classes/index.DreamApp.html +5 -5
  27. package/docs/classes/index.DreamTransaction.html +2 -2
  28. package/docs/classes/index.Env.html +2 -2
  29. package/docs/classes/index.Query.html +53 -53
  30. package/docs/classes/system.CliFileWriter.html +2 -2
  31. package/docs/classes/system.DreamBin.html +2 -2
  32. package/docs/classes/system.DreamCLI.html +5 -5
  33. package/docs/classes/system.DreamImporter.html +2 -2
  34. package/docs/classes/system.DreamLogos.html +2 -2
  35. package/docs/classes/system.DreamSerializerBuilder.html +8 -8
  36. package/docs/classes/system.ObjectSerializerBuilder.html +8 -8
  37. package/docs/classes/utils.Encrypt.html +2 -2
  38. package/docs/classes/utils.Range.html +2 -2
  39. package/docs/functions/db.closeAllDbConnections.html +1 -1
  40. package/docs/functions/db.dreamDbConnections.html +1 -1
  41. package/docs/functions/db.untypedDb.html +1 -1
  42. package/docs/functions/db.validateColumn.html +1 -1
  43. package/docs/functions/db.validateTable.html +1 -1
  44. package/docs/functions/errors.pgErrorType.html +1 -1
  45. package/docs/functions/index.DreamSerializer.html +1 -1
  46. package/docs/functions/index.ObjectSerializer.html +1 -1
  47. package/docs/functions/index.ReplicaSafe.html +1 -1
  48. package/docs/functions/index.STI.html +1 -1
  49. package/docs/functions/index.SoftDelete.html +1 -1
  50. package/docs/functions/utils.camelize.html +1 -1
  51. package/docs/functions/utils.capitalize.html +1 -1
  52. package/docs/functions/utils.cloneDeepSafe.html +1 -1
  53. package/docs/functions/utils.compact.html +1 -1
  54. package/docs/functions/utils.groupBy.html +1 -1
  55. package/docs/functions/utils.hyphenize.html +1 -1
  56. package/docs/functions/utils.intersection.html +1 -1
  57. package/docs/functions/utils.isEmpty.html +1 -1
  58. package/docs/functions/utils.normalizeUnicode.html +1 -1
  59. package/docs/functions/utils.pascalize.html +1 -1
  60. package/docs/functions/utils.percent.html +1 -1
  61. package/docs/functions/utils.range-1.html +1 -1
  62. package/docs/functions/utils.round.html +1 -1
  63. package/docs/functions/utils.sanitizeString.html +1 -1
  64. package/docs/functions/utils.snakeify.html +1 -1
  65. package/docs/functions/utils.sort.html +1 -1
  66. package/docs/functions/utils.sortBy.html +1 -1
  67. package/docs/functions/utils.sortObjectByKey.html +1 -1
  68. package/docs/functions/utils.sortObjectByValue.html +1 -1
  69. package/docs/functions/utils.uncapitalize.html +1 -1
  70. package/docs/functions/utils.uniq.html +1 -1
  71. package/docs/interfaces/openapi.OpenapiDescription.html +2 -2
  72. package/docs/interfaces/openapi.OpenapiSchemaProperties.html +1 -1
  73. package/docs/interfaces/openapi.OpenapiSchemaPropertiesShorthand.html +1 -1
  74. package/docs/interfaces/openapi.OpenapiTypeFieldObject.html +1 -1
  75. package/docs/interfaces/types.BelongsToStatement.html +2 -2
  76. package/docs/interfaces/types.DecoratorContext.html +2 -2
  77. package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
  78. package/docs/interfaces/types.DreamAppOpts.html +2 -2
  79. package/docs/interfaces/types.EncryptOptions.html +2 -2
  80. package/docs/interfaces/types.InternalAnyTypedSerializerRendersMany.html +2 -2
  81. package/docs/interfaces/types.InternalAnyTypedSerializerRendersOne.html +2 -2
  82. package/docs/interfaces/types.SerializerRendererOpts.html +2 -2
  83. package/docs/modules/db.html +1 -1
  84. package/docs/modules/errors.html +1 -1
  85. package/docs/modules/index.html +1 -1
  86. package/docs/modules/openapi.html +1 -1
  87. package/docs/modules/system.html +1 -1
  88. package/docs/modules/types.html +1 -1
  89. package/docs/modules/utils.html +1 -1
  90. package/docs/types/index.DateTime.html +1 -1
  91. package/docs/types/openapi.CommonOpenapiSchemaObjectFields.html +1 -1
  92. package/docs/types/openapi.OpenapiAllTypes.html +1 -1
  93. package/docs/types/openapi.OpenapiFormats.html +1 -1
  94. package/docs/types/openapi.OpenapiNumberFormats.html +1 -1
  95. package/docs/types/openapi.OpenapiPrimitiveBaseTypes.html +1 -1
  96. package/docs/types/openapi.OpenapiPrimitiveTypes.html +1 -1
  97. package/docs/types/openapi.OpenapiSchemaArray.html +1 -1
  98. package/docs/types/openapi.OpenapiSchemaArrayShorthand.html +1 -1
  99. package/docs/types/openapi.OpenapiSchemaBase.html +1 -1
  100. package/docs/types/openapi.OpenapiSchemaBody.html +1 -1
  101. package/docs/types/openapi.OpenapiSchemaBodyShorthand.html +1 -1
  102. package/docs/types/openapi.OpenapiSchemaCommonFields.html +1 -1
  103. package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +1 -1
  104. package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +1 -1
  105. package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +1 -1
  106. package/docs/types/openapi.OpenapiSchemaExpressionRef.html +1 -1
  107. package/docs/types/openapi.OpenapiSchemaExpressionRefSchemaShorthand.html +1 -1
  108. package/docs/types/openapi.OpenapiSchemaInteger.html +1 -1
  109. package/docs/types/openapi.OpenapiSchemaNull.html +1 -1
  110. package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
  111. package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
  112. package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +1 -1
  113. package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +1 -1
  114. package/docs/types/openapi.OpenapiSchemaObjectAnyOf.html +1 -1
  115. package/docs/types/openapi.OpenapiSchemaObjectAnyOfShorthand.html +1 -1
  116. package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
  117. package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.html +1 -1
  118. package/docs/types/openapi.OpenapiSchemaObjectOneOf.html +1 -1
  119. package/docs/types/openapi.OpenapiSchemaObjectOneOfShorthand.html +1 -1
  120. package/docs/types/openapi.OpenapiSchemaObjectShorthand.html +1 -1
  121. package/docs/types/openapi.OpenapiSchemaPrimitiveGeneric.html +1 -1
  122. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAllOf.html +1 -1
  123. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +1 -1
  124. package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +1 -1
  125. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializableRef.html +1 -1
  126. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializerRef.html +1 -1
  127. package/docs/types/openapi.OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
  128. package/docs/types/openapi.OpenapiSchemaString.html +1 -1
  129. package/docs/types/openapi.OpenapiShorthandAllTypes.html +1 -1
  130. package/docs/types/openapi.OpenapiShorthandPrimitiveBaseTypes.html +1 -1
  131. package/docs/types/openapi.OpenapiShorthandPrimitiveTypes.html +1 -1
  132. package/docs/types/openapi.OpenapiTypeField.html +1 -1
  133. package/docs/types/system.DreamAppAllowedPackageManagersEnum.html +1 -1
  134. package/docs/types/types.Camelized.html +1 -1
  135. package/docs/types/types.DbConnectionType.html +1 -1
  136. package/docs/types/types.DbTypes.html +1 -1
  137. package/docs/types/types.DreamAssociationMetadata.html +1 -1
  138. package/docs/types/types.DreamAttributes.html +1 -1
  139. package/docs/types/types.DreamClassAssociationAndStatement.html +1 -1
  140. package/docs/types/types.DreamClassColumn.html +1 -1
  141. package/docs/types/types.DreamColumn.html +1 -1
  142. package/docs/types/types.DreamColumnNames.html +1 -1
  143. package/docs/types/types.DreamLogLevel.html +1 -1
  144. package/docs/types/types.DreamLogger.html +1 -1
  145. package/docs/types/types.DreamModelSerializerType.html +1 -1
  146. package/docs/types/types.DreamOrViewModelClassSerializerKey.html +1 -1
  147. package/docs/types/types.DreamOrViewModelSerializerKey.html +1 -1
  148. package/docs/types/types.DreamParamSafeAttributes.html +1 -1
  149. package/docs/types/types.DreamParamSafeColumnNames.html +1 -1
  150. package/docs/types/types.DreamSerializable.html +1 -1
  151. package/docs/types/types.DreamSerializableArray.html +1 -1
  152. package/docs/types/types.DreamSerializerKey.html +1 -1
  153. package/docs/types/types.DreamSerializers.html +1 -1
  154. package/docs/types/types.DreamVirtualColumns.html +1 -1
  155. package/docs/types/types.EncryptAlgorithm.html +1 -1
  156. package/docs/types/types.HasManyStatement.html +1 -1
  157. package/docs/types/types.HasOneStatement.html +1 -1
  158. package/docs/types/types.Hyphenized.html +1 -1
  159. package/docs/types/types.Pascalized.html +1 -1
  160. package/docs/types/types.RoundingPrecision.html +1 -1
  161. package/docs/types/types.SerializerCasing.html +1 -1
  162. package/docs/types/types.SimpleObjectSerializerType.html +1 -1
  163. package/docs/types/types.Snakeified.html +1 -1
  164. package/docs/types/types.StrictInterface.html +1 -1
  165. package/docs/types/types.UpdateableAssociationProperties.html +1 -1
  166. package/docs/types/types.UpdateableProperties.html +1 -1
  167. package/docs/types/types.ValidationType.html +1 -1
  168. package/docs/types/types.ViewModel.html +1 -1
  169. package/docs/types/types.ViewModelClass.html +1 -1
  170. package/docs/types/types.WhereStatementForDream.html +1 -1
  171. package/docs/types/types.WhereStatementForDreamClass.html +1 -1
  172. package/docs/variables/index.DateTime-1.html +1 -1
  173. package/docs/variables/index.DreamConst.html +1 -1
  174. package/docs/variables/index.ops.html +1 -1
  175. package/docs/variables/openapi.openapiPrimitiveTypes-1.html +1 -1
  176. package/docs/variables/openapi.openapiShorthandPrimitiveTypes-1.html +1 -1
  177. package/docs/variables/system.DreamAppAllowedPackageManagersEnumValues.html +1 -1
  178. package/docs/variables/types.TRIGRAM_OPERATORS.html +1 -1
  179. package/docs/variables/types.primaryKeyTypes.html +1 -1
  180. package/package.json +1 -1
@@ -370,6 +370,12 @@ export default class Dream {
370
370
  static get isSTIBase() {
371
371
  return !!this.extendedBy?.length && !this.isSTIChild;
372
372
  }
373
+ static stiSiblings() {
374
+ const stiBase = this.stiBaseClassOrOwnClass;
375
+ if (!stiBase.isSTIBase)
376
+ return [];
377
+ return stiBase.extendedBy;
378
+ }
373
379
  /**
374
380
  * @internal
375
381
  *
@@ -687,9 +693,9 @@ export default class Dream {
687
693
  return false;
688
694
  if (this.explicitUnsafeParamColumns.includes(column))
689
695
  return false;
690
- if (this.isBelongsToAssociationForeignKey(column))
696
+ if (this.isBelongsToAssociationForeignKey(column, { includeStiSiblings: true }))
691
697
  return false;
692
- if (this.isBelongsToAssociationPolymorphicTypeField(column))
698
+ if (this.isBelongsToAssociationPolymorphicTypeField(column, { includeStiSiblings: true }))
693
699
  return false;
694
700
  if ((this.isSTIChild || this.isSTIBase) && column === 'type')
695
701
  return false;
@@ -755,13 +761,15 @@ export default class Dream {
755
761
  *
756
762
  * @returns An array containing all of the associations for this dream class
757
763
  */
758
- static associationMetadataMap() {
764
+ static associationMetadataMap(opts = { includeStiSiblings: false }) {
759
765
  const allAssociations = [
760
766
  ...this.associationMetadataByType.belongsTo,
761
767
  ...this.associationMetadataByType.hasOne,
762
768
  ...this.associationMetadataByType.hasMany,
763
769
  ];
764
- const map = {};
770
+ const map = opts.includeStiSiblings
771
+ ? this.stiSiblings().reduce((map, sibling) => ({ ...map, ...sibling['associationMetadataMap']() }), {})
772
+ : {};
765
773
  for (const association of allAssociations) {
766
774
  map[association.as] = association;
767
775
  }
@@ -1837,8 +1845,8 @@ export default class Dream {
1837
1845
  * @param column - A column on this Dream class
1838
1846
  * @returns A boolean
1839
1847
  */
1840
- static isBelongsToAssociationForeignKey(column) {
1841
- return this.belongsToAssociationForeignKeys().includes(column);
1848
+ static isBelongsToAssociationForeignKey(column, opts = { includeStiSiblings: false }) {
1849
+ return this.belongsToAssociationForeignKeys(opts).includes(column);
1842
1850
  }
1843
1851
  /**
1844
1852
  * @internal
@@ -1856,8 +1864,8 @@ export default class Dream {
1856
1864
  * @param column - a column on this dream class
1857
1865
  * @returns A boolean
1858
1866
  */
1859
- static isBelongsToAssociationPolymorphicTypeField(column) {
1860
- return this.polymorphicTypeColumns().includes(column);
1867
+ static isBelongsToAssociationPolymorphicTypeField(column, opts = { includeStiSiblings: false }) {
1868
+ return this.polymorphicTypeColumns(opts).includes(column);
1861
1869
  }
1862
1870
  /**
1863
1871
  * @internal
@@ -1872,9 +1880,9 @@ export default class Dream {
1872
1880
  *
1873
1881
  * @returns An array of column names that are belongs to foreign keys on this dream class
1874
1882
  */
1875
- static belongsToAssociationForeignKeys() {
1876
- const associationMap = this.associationMetadataMap();
1877
- return this.belongsToAssociationNames().map(belongsToKey => associationMap[belongsToKey]?.foreignKey());
1883
+ static belongsToAssociationForeignKeys(opts = { includeStiSiblings: false }) {
1884
+ const associationMap = this.associationMetadataMap(opts);
1885
+ return this.belongsToAssociationNames(opts).map(belongsToKey => associationMap[belongsToKey]?.foreignKey());
1878
1886
  }
1879
1887
  /**
1880
1888
  * @internal
@@ -1888,9 +1896,9 @@ export default class Dream {
1888
1896
  *
1889
1897
  * @returns An array of column names that are polymorphic type fields on the given dream class
1890
1898
  */
1891
- static polymorphicTypeColumns() {
1892
- const associationMap = this.associationMetadataMap();
1893
- return this.belongsToAssociationNames()
1899
+ static polymorphicTypeColumns(opts = { includeStiSiblings: false }) {
1900
+ const associationMap = this.associationMetadataMap(opts);
1901
+ return this.belongsToAssociationNames(opts)
1894
1902
  .filter(key => associationMap[key]?.polymorphic)
1895
1903
  .map(belongsToKey => associationMap[belongsToKey]?.foreignKeyTypeField());
1896
1904
  }
@@ -1908,8 +1916,8 @@ export default class Dream {
1908
1916
  *
1909
1917
  * @returns An array of belongs to association names
1910
1918
  */
1911
- static belongsToAssociationNames() {
1912
- const associationMap = this.associationMetadataMap();
1919
+ static belongsToAssociationNames(opts = { includeStiSiblings: false }) {
1920
+ const associationMap = this.associationMetadataMap(opts);
1913
1921
  return Object.keys(associationMap).filter(key => associationMap[key]?.type === 'BelongsTo');
1914
1922
  }
1915
1923
  /**
@@ -7,59 +7,70 @@ import absoluteDreamPath from '../path/absoluteDreamPath.js';
7
7
  import snakeify from '../snakeify.js';
8
8
  import standardizeFullyQualifiedModelName from '../standardizeFullyQualifiedModelName.js';
9
9
  import uniq from '../uniq.js';
10
- export default function generateDreamContent({ fullyQualifiedModelName, columnsWithTypes, fullyQualifiedParentName, serializer, includeAdminSerializers, connectionName = 'default', }) {
11
- fullyQualifiedModelName = standardizeFullyQualifiedModelName(fullyQualifiedModelName);
10
+ export default function generateDreamContent(options) {
11
+ const config = createModelConfig(options);
12
+ const baseImports = createImportConfig(config, options);
13
+ const attributesResult = processAttributes(options.columnsWithTypes, config.modelClassName);
14
+ const allImports = {
15
+ ...baseImports,
16
+ modelImportStatements: [...baseImports.modelImportStatements, ...attributesResult.imports],
17
+ };
18
+ const importSection = buildImportSection(allImports);
19
+ const classDeclaration = buildClassDeclaration(config);
20
+ const tableMethod = buildTableMethod(config);
21
+ const serializersMethod = buildSerializersMethod(config, options);
22
+ const fieldsSection = buildFieldsSection(config, attributesResult);
23
+ return `${importSection}
24
+
25
+ const deco = new Decorators<typeof ${config.modelClassName}>()
26
+
27
+ ${classDeclaration}
28
+ ${tableMethod}${serializersMethod}${fieldsSection}
29
+ }
30
+ `.replace(/^\s*$/gm, '');
31
+ }
32
+ export function createModelConfig(options) {
33
+ const fullyQualifiedModelName = standardizeFullyQualifiedModelName(options.fullyQualifiedModelName);
12
34
  const modelClassName = globalClassNameFromFullyQualifiedModelName(fullyQualifiedModelName);
35
+ const isSTI = !!options.fullyQualifiedParentName;
13
36
  let parentModelClassName;
37
+ if (isSTI) {
38
+ const standardizedParentName = standardizeFullyQualifiedModelName(options.fullyQualifiedParentName);
39
+ parentModelClassName = globalClassNameFromFullyQualifiedModelName(standardizedParentName);
40
+ }
41
+ const connectionName = options.connectionName || 'default';
42
+ const applicationModelName = connectionName === 'default' ? 'ApplicationModel' : `${pascalize(connectionName)}ApplicationModel`;
43
+ const tableName = snakeify(pluralize(fullyQualifiedModelName.replace(/\//g, '_')));
44
+ return {
45
+ fullyQualifiedModelName,
46
+ modelClassName,
47
+ parentModelClassName,
48
+ applicationModelName,
49
+ isSTI,
50
+ tableName,
51
+ };
52
+ }
53
+ export function createImportConfig(config, options) {
14
54
  const dreamTypeImports = ['DreamColumn'];
15
55
  const dreamImports = ['Decorators'];
16
- if (serializer)
56
+ if (options.serializer) {
17
57
  dreamTypeImports.push('DreamSerializers');
18
- const isSTI = !!fullyQualifiedParentName;
19
- if (isSTI) {
20
- fullyQualifiedParentName = standardizeFullyQualifiedModelName(fullyQualifiedParentName);
21
- parentModelClassName = globalClassNameFromFullyQualifiedModelName(fullyQualifiedParentName);
58
+ }
59
+ if (config.isSTI) {
22
60
  dreamImports.push('STI');
23
61
  }
24
- const idTypescriptType = `DreamColumn<${modelClassName}, 'id'>`;
25
- const applicationModelName = connectionName === 'default' ? 'ApplicationModel' : `${pascalize(connectionName)}ApplicationModel`;
26
- const modelImportStatements = isSTI
27
- ? [importStatementForModel(fullyQualifiedParentName)]
28
- : [importStatementForModel(applicationModelName)];
29
- const attributeStatements = columnsWithTypes.map(attribute => {
30
- const [attributeName, attributeType, ...descriptors] = attribute.split(':');
31
- if (attributeName === undefined)
32
- return '';
33
- const fullyQualifiedAssociatedModelName = standardizeFullyQualifiedModelName(attributeName);
34
- const associationModelName = globalClassNameFromFullyQualifiedModelName(fullyQualifiedAssociatedModelName);
35
- const associationImportStatement = importStatementForModel(fullyQualifiedAssociatedModelName);
36
- if (!attributeType)
37
- throw new Error(`must pass a column type for ${attributeName} (i.e. ${attributeName}:string)`);
38
- switch (attributeType) {
39
- case 'belongs_to': {
40
- modelImportStatements.push(associationImportStatement);
41
- const associationName = camelize(fullyQualifiedAssociatedModelName.split('/').pop());
42
- const associationForeignKey = `${associationName}Id`;
43
- return `
44
- @deco.BelongsTo('${fullyQualifiedAssociatedModelName}', { on: '${associationForeignKey}'${descriptors.includes('optional') ? ', optional: true' : ''} })
45
- public ${associationName}: ${associationModelName}${descriptors.includes('optional') ? ' | null' : ''}
46
- public ${associationForeignKey}: DreamColumn<${modelClassName}, '${associationName}Id'>
47
- `;
48
- }
49
- case 'has_one':
50
- case 'has_many':
51
- return '';
52
- case 'encrypted':
53
- return `
54
- @deco.Encrypted()
55
- public ${camelize(attributeName)}: ${getAttributeType(attribute, modelClassName)}\
56
- `;
57
- default:
58
- return `
59
- public ${camelize(attributeName)}: ${getAttributeType(attribute, modelClassName)}\
60
- `;
61
- }
62
- });
62
+ const baseModelName = config.isSTI ? options.fullyQualifiedParentName : config.applicationModelName;
63
+ const modelImportStatements = [importStatementForModel(baseModelName)];
64
+ return {
65
+ dreamTypeImports,
66
+ dreamImports,
67
+ modelImportStatements,
68
+ };
69
+ }
70
+ export function processAttributes(columnsWithTypes, modelClassName) {
71
+ const attributeResults = columnsWithTypes.map(attribute => processAttribute(attribute, modelClassName));
72
+ const allImports = attributeResults.flatMap(result => result.imports);
73
+ const attributeStatements = attributeResults.map(result => result.content);
63
74
  const formattedFields = attributeStatements
64
75
  .filter(attr => !/^\n@/.test(attr))
65
76
  .map(s => s.split('\n').join('\n '))
@@ -69,42 +80,124 @@ public ${camelize(attributeName)}: ${getAttributeType(attribute, modelClassName)
69
80
  .map(s => s.split('\n').join('\n '))
70
81
  .join('\n ')
71
82
  .replace(/\n {2}$/, '');
72
- let timestamps = `
73
- public createdAt: DreamColumn<${modelClassName}, 'createdAt'>
74
- public updatedAt: DreamColumn<${modelClassName}, 'updatedAt'>
83
+ return {
84
+ content: '', // Not used in this context
85
+ imports: allImports,
86
+ formattedFields,
87
+ formattedDecorators,
88
+ };
89
+ }
90
+ export function processAttribute(attribute, modelClassName) {
91
+ const [attributeName, attributeType, ...descriptors] = attribute.split(':');
92
+ if (attributeName === undefined)
93
+ return { content: '', imports: [] };
94
+ if (!attributeType) {
95
+ throw new Error(`must pass a column type for ${attributeName} (i.e. ${attributeName}:string)`);
96
+ }
97
+ switch (attributeType) {
98
+ case 'belongs_to':
99
+ return createBelongsToAttribute(attributeName, descriptors, modelClassName);
100
+ case 'has_one':
101
+ case 'has_many':
102
+ return { content: '', imports: [] };
103
+ case 'encrypted':
104
+ return createEncryptedAttribute(attributeName, attribute, modelClassName);
105
+ default:
106
+ return createRegularAttribute(attributeName, attribute, modelClassName);
107
+ }
108
+ }
109
+ export function createBelongsToAttribute(attributeName, descriptors, modelClassName) {
110
+ const fullyQualifiedAssociatedModelName = standardizeFullyQualifiedModelName(attributeName);
111
+ const associationModelName = globalClassNameFromFullyQualifiedModelName(fullyQualifiedAssociatedModelName);
112
+ const associationImportStatement = importStatementForModel(fullyQualifiedAssociatedModelName);
113
+ const associationName = camelize(fullyQualifiedAssociatedModelName.split('/').pop());
114
+ const associationForeignKey = `${associationName}Id`;
115
+ const isOptional = descriptors.includes('optional');
116
+ const content = `
117
+ @deco.BelongsTo('${fullyQualifiedAssociatedModelName}', { on: '${associationForeignKey}'${isOptional ? ', optional: true' : ''} })
118
+ public ${associationName}: ${associationModelName}${isOptional ? ' | null' : ''}
119
+ public ${associationForeignKey}: DreamColumn<${modelClassName}, '${associationName}Id'>
75
120
  `;
76
- if (!formattedDecorators.length)
77
- timestamps = timestamps.replace(/\n$/, '');
78
- const tableName = snakeify(pluralize(fullyQualifiedModelName.replace(/\//g, '_')));
79
- return `\
80
- ${dreamImports.length ? `import { ${uniq(dreamImports).join(', ')} } from '@rvoh/dream'\n` : ''}import { ${uniq(dreamTypeImports).join(', ')} } from '@rvoh/dream/types'${uniq(modelImportStatements).join('')}
81
-
82
- const deco = new Decorators<typeof ${modelClassName}>()
83
-
84
- ${isSTI ? `\n@STI(${parentModelClassName})` : ''}
85
- export default class ${modelClassName} extends ${isSTI ? parentModelClassName : applicationModelName} {
86
- ${isSTI
87
- ? ''
88
- : ` public override get table() {
89
- return '${tableName}' as const
121
+ return {
122
+ content,
123
+ imports: [associationImportStatement],
124
+ };
125
+ }
126
+ export function createEncryptedAttribute(attributeName, attribute, modelClassName) {
127
+ const content = `
128
+ @deco.Encrypted()
129
+ public ${camelize(attributeName)}: ${getAttributeType(attribute, modelClassName)}`;
130
+ return {
131
+ content,
132
+ imports: [],
133
+ };
134
+ }
135
+ export function createRegularAttribute(attributeName, attribute, modelClassName) {
136
+ const content = `
137
+ public ${camelize(attributeName)}: ${getAttributeType(attribute, modelClassName)}`;
138
+ return {
139
+ content,
140
+ imports: [],
141
+ };
142
+ }
143
+ function buildImportSection(imports) {
144
+ const dreamImportLine = imports.dreamImports.length
145
+ ? `import { ${uniq(imports.dreamImports).join(', ')} } from '@rvoh/dream'\n`
146
+ : '';
147
+ const typeImportLine = `import { ${uniq(imports.dreamTypeImports).join(', ')} } from '@rvoh/dream/types'`;
148
+ const modelImports = uniq(imports.modelImportStatements).join('');
149
+ return `${dreamImportLine}${typeImportLine}${modelImports}`;
150
+ }
151
+ function buildClassDeclaration(config) {
152
+ const stiDecorator = config.isSTI ? `@STI(${config.parentModelClassName})\n` : '';
153
+ const extendsClause = config.isSTI ? config.parentModelClassName : config.applicationModelName;
154
+ return `${stiDecorator}export default class ${config.modelClassName} extends ${extendsClause} {`;
155
+ }
156
+ function buildTableMethod(config) {
157
+ if (config.isSTI)
158
+ return '';
159
+ return ` public override get table() {
160
+ return '${config.tableName}' as const
90
161
  }
91
162
 
92
- `}${serializer
93
- ? ` public ${isSTI ? 'override ' : ''}get serializers(): DreamSerializers<${modelClassName}> {
163
+ `;
164
+ }
165
+ function buildSerializersMethod(config, options) {
166
+ if (!options.serializer)
167
+ return '';
168
+ const overrideKeyword = config.isSTI ? 'override ' : '';
169
+ const defaultSerializer = serializerGlobalNameFromFullyQualifiedModelName(config.fullyQualifiedModelName);
170
+ const summarySerializer = serializerGlobalNameFromFullyQualifiedModelName(config.fullyQualifiedModelName, 'summary');
171
+ let adminSerializers = '';
172
+ if (options.includeAdminSerializers) {
173
+ const adminSerializer = defaultSerializer.replace(/Serializer$/, 'AdminSerializer');
174
+ const adminSummarySerializer = summarySerializer.replace(/SummarySerializer$/, 'AdminSummarySerializer');
175
+ adminSerializers = `
176
+ admin: '${adminSerializer}',
177
+ adminSummary: '${adminSummarySerializer}',`;
178
+ }
179
+ return ` public ${overrideKeyword}get serializers(): DreamSerializers<${config.modelClassName}> {
94
180
  return {
95
- default: '${serializerGlobalNameFromFullyQualifiedModelName(fullyQualifiedModelName)}',
96
- summary: '${serializerGlobalNameFromFullyQualifiedModelName(fullyQualifiedModelName, 'summary')}',${!includeAdminSerializers
97
- ? ''
98
- : `
99
- admin: '${serializerGlobalNameFromFullyQualifiedModelName(fullyQualifiedModelName).replace(/Serializer$/, 'AdminSerializer')}',
100
- adminSummary: '${serializerGlobalNameFromFullyQualifiedModelName(fullyQualifiedModelName, 'summary').replace(/SummarySerializer$/, 'AdminSummarySerializer')}',`}
181
+ default: '${defaultSerializer}',
182
+ summary: '${summarySerializer}',${adminSerializers}
101
183
  }
102
184
  }
103
185
 
104
- `
105
- : ''}${isSTI ? formattedFields : ` public id: ${idTypescriptType}${formattedFields}${timestamps}`}${formattedDecorators}
186
+ `;
106
187
  }
107
- `.replace(/^\s*$/gm, '');
188
+ function buildFieldsSection(config, attributes) {
189
+ if (config.isSTI) {
190
+ return `${attributes.formattedFields}${attributes.formattedDecorators}`;
191
+ }
192
+ const idField = ` public id: DreamColumn<${config.modelClassName}, 'id'>`;
193
+ let timestamps = `
194
+ public createdAt: DreamColumn<${config.modelClassName}, 'createdAt'>
195
+ public updatedAt: DreamColumn<${config.modelClassName}, 'updatedAt'>
196
+ `;
197
+ if (!attributes.formattedDecorators.length) {
198
+ timestamps = timestamps.replace(/\n$/, '');
199
+ }
200
+ return `${idField}${attributes.formattedFields}${timestamps}${attributes.formattedDecorators}`;
108
201
  }
109
202
  function getAttributeType(attribute, modelClassName) {
110
203
  return `DreamColumn<${modelClassName}, '${camelize(attribute.split(':')[0])}'>`;
@@ -370,6 +370,12 @@ export default class Dream {
370
370
  static get isSTIBase() {
371
371
  return !!this.extendedBy?.length && !this.isSTIChild;
372
372
  }
373
+ static stiSiblings() {
374
+ const stiBase = this.stiBaseClassOrOwnClass;
375
+ if (!stiBase.isSTIBase)
376
+ return [];
377
+ return stiBase.extendedBy;
378
+ }
373
379
  /**
374
380
  * @internal
375
381
  *
@@ -687,9 +693,9 @@ export default class Dream {
687
693
  return false;
688
694
  if (this.explicitUnsafeParamColumns.includes(column))
689
695
  return false;
690
- if (this.isBelongsToAssociationForeignKey(column))
696
+ if (this.isBelongsToAssociationForeignKey(column, { includeStiSiblings: true }))
691
697
  return false;
692
- if (this.isBelongsToAssociationPolymorphicTypeField(column))
698
+ if (this.isBelongsToAssociationPolymorphicTypeField(column, { includeStiSiblings: true }))
693
699
  return false;
694
700
  if ((this.isSTIChild || this.isSTIBase) && column === 'type')
695
701
  return false;
@@ -755,13 +761,15 @@ export default class Dream {
755
761
  *
756
762
  * @returns An array containing all of the associations for this dream class
757
763
  */
758
- static associationMetadataMap() {
764
+ static associationMetadataMap(opts = { includeStiSiblings: false }) {
759
765
  const allAssociations = [
760
766
  ...this.associationMetadataByType.belongsTo,
761
767
  ...this.associationMetadataByType.hasOne,
762
768
  ...this.associationMetadataByType.hasMany,
763
769
  ];
764
- const map = {};
770
+ const map = opts.includeStiSiblings
771
+ ? this.stiSiblings().reduce((map, sibling) => ({ ...map, ...sibling['associationMetadataMap']() }), {})
772
+ : {};
765
773
  for (const association of allAssociations) {
766
774
  map[association.as] = association;
767
775
  }
@@ -1837,8 +1845,8 @@ export default class Dream {
1837
1845
  * @param column - A column on this Dream class
1838
1846
  * @returns A boolean
1839
1847
  */
1840
- static isBelongsToAssociationForeignKey(column) {
1841
- return this.belongsToAssociationForeignKeys().includes(column);
1848
+ static isBelongsToAssociationForeignKey(column, opts = { includeStiSiblings: false }) {
1849
+ return this.belongsToAssociationForeignKeys(opts).includes(column);
1842
1850
  }
1843
1851
  /**
1844
1852
  * @internal
@@ -1856,8 +1864,8 @@ export default class Dream {
1856
1864
  * @param column - a column on this dream class
1857
1865
  * @returns A boolean
1858
1866
  */
1859
- static isBelongsToAssociationPolymorphicTypeField(column) {
1860
- return this.polymorphicTypeColumns().includes(column);
1867
+ static isBelongsToAssociationPolymorphicTypeField(column, opts = { includeStiSiblings: false }) {
1868
+ return this.polymorphicTypeColumns(opts).includes(column);
1861
1869
  }
1862
1870
  /**
1863
1871
  * @internal
@@ -1872,9 +1880,9 @@ export default class Dream {
1872
1880
  *
1873
1881
  * @returns An array of column names that are belongs to foreign keys on this dream class
1874
1882
  */
1875
- static belongsToAssociationForeignKeys() {
1876
- const associationMap = this.associationMetadataMap();
1877
- return this.belongsToAssociationNames().map(belongsToKey => associationMap[belongsToKey]?.foreignKey());
1883
+ static belongsToAssociationForeignKeys(opts = { includeStiSiblings: false }) {
1884
+ const associationMap = this.associationMetadataMap(opts);
1885
+ return this.belongsToAssociationNames(opts).map(belongsToKey => associationMap[belongsToKey]?.foreignKey());
1878
1886
  }
1879
1887
  /**
1880
1888
  * @internal
@@ -1888,9 +1896,9 @@ export default class Dream {
1888
1896
  *
1889
1897
  * @returns An array of column names that are polymorphic type fields on the given dream class
1890
1898
  */
1891
- static polymorphicTypeColumns() {
1892
- const associationMap = this.associationMetadataMap();
1893
- return this.belongsToAssociationNames()
1899
+ static polymorphicTypeColumns(opts = { includeStiSiblings: false }) {
1900
+ const associationMap = this.associationMetadataMap(opts);
1901
+ return this.belongsToAssociationNames(opts)
1894
1902
  .filter(key => associationMap[key]?.polymorphic)
1895
1903
  .map(belongsToKey => associationMap[belongsToKey]?.foreignKeyTypeField());
1896
1904
  }
@@ -1908,8 +1916,8 @@ export default class Dream {
1908
1916
  *
1909
1917
  * @returns An array of belongs to association names
1910
1918
  */
1911
- static belongsToAssociationNames() {
1912
- const associationMap = this.associationMetadataMap();
1919
+ static belongsToAssociationNames(opts = { includeStiSiblings: false }) {
1920
+ const associationMap = this.associationMetadataMap(opts);
1913
1921
  return Object.keys(associationMap).filter(key => associationMap[key]?.type === 'BelongsTo');
1914
1922
  }
1915
1923
  /**