@solidstarters/solid-core 1.2.200 → 1.2.201
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/commands/fixtures/fixtures-setup.command.d.ts +15 -0
- package/dist/commands/fixtures/fixtures-setup.command.d.ts.map +1 -0
- package/dist/commands/fixtures/fixtures-setup.command.js +58 -0
- package/dist/commands/fixtures/fixtures-setup.command.js.map +1 -0
- package/dist/commands/fixtures/fixtures-tear-down.command.d.ts +16 -0
- package/dist/commands/fixtures/fixtures-tear-down.command.d.ts.map +1 -0
- package/dist/commands/fixtures/fixtures-tear-down.command.js +59 -0
- package/dist/commands/fixtures/fixtures-tear-down.command.js.map +1 -0
- package/dist/commands/refresh-model.command.d.ts.map +1 -1
- package/dist/commands/refresh-model.command.js +4 -0
- package/dist/commands/refresh-model.command.js.map +1 -1
- package/dist/constants/error-messages.d.ts +2 -0
- package/dist/constants/error-messages.d.ts.map +1 -1
- package/dist/constants/error-messages.js +4 -0
- package/dist/constants/error-messages.js.map +1 -1
- package/dist/controllers/model-sequence.controller.d.ts +43 -0
- package/dist/controllers/model-sequence.controller.d.ts.map +1 -0
- package/dist/controllers/model-sequence.controller.js +179 -0
- package/dist/controllers/model-sequence.controller.js.map +1 -0
- package/dist/controllers/setting.controller.d.ts +1 -0
- package/dist/controllers/setting.controller.d.ts.map +1 -1
- package/dist/controllers/setting.controller.js +15 -0
- package/dist/controllers/setting.controller.js.map +1 -1
- package/dist/dtos/basic-filters.dto.d.ts +3 -1
- package/dist/dtos/basic-filters.dto.d.ts.map +1 -1
- package/dist/dtos/basic-filters.dto.js +8 -2
- package/dist/dtos/basic-filters.dto.js.map +1 -1
- package/dist/dtos/basic-group-filters.dto.d.ts +6 -0
- package/dist/dtos/basic-group-filters.dto.d.ts.map +1 -0
- package/dist/dtos/basic-group-filters.dto.js +46 -0
- package/dist/dtos/basic-group-filters.dto.js.map +1 -0
- package/dist/dtos/create-field-metadata.dto.js +2 -2
- package/dist/dtos/create-field-metadata.dto.js.map +1 -1
- package/dist/dtos/create-model-sequence.dto.d.ts +14 -0
- package/dist/dtos/create-model-sequence.dto.d.ts.map +1 -0
- package/dist/dtos/create-model-sequence.dto.js +90 -0
- package/dist/dtos/create-model-sequence.dto.js.map +1 -0
- package/dist/dtos/create-role-metadata.dto.d.ts.map +1 -1
- package/dist/dtos/create-role-metadata.dto.js +1 -0
- package/dist/dtos/create-role-metadata.dto.js.map +1 -1
- package/dist/dtos/get-mcp-url.dto.d.ts +5 -0
- package/dist/dtos/get-mcp-url.dto.d.ts.map +1 -0
- package/dist/dtos/get-mcp-url.dto.js +31 -0
- package/dist/dtos/get-mcp-url.dto.js.map +1 -0
- package/dist/dtos/resolve-s3-url.dto.d.ts +5 -5
- package/dist/dtos/resolve-s3-url.dto.d.ts.map +1 -1
- package/dist/dtos/resolve-s3-url.dto.js +7 -7
- package/dist/dtos/resolve-s3-url.dto.js.map +1 -1
- package/dist/dtos/update-model-sequence.dto.d.ts +15 -0
- package/dist/dtos/update-model-sequence.dto.d.ts.map +1 -0
- package/dist/dtos/update-model-sequence.dto.js +94 -0
- package/dist/dtos/update-model-sequence.dto.js.map +1 -0
- package/dist/entities/common.entity.d.ts.map +1 -1
- package/dist/entities/common.entity.js +1 -0
- package/dist/entities/common.entity.js.map +1 -1
- package/dist/entities/legacy-common.entity.d.ts.map +1 -1
- package/dist/entities/legacy-common.entity.js +1 -0
- package/dist/entities/legacy-common.entity.js.map +1 -1
- package/dist/entities/model-sequence.entity.d.ts +15 -0
- package/dist/entities/model-sequence.entity.d.ts.map +1 -0
- package/dist/entities/model-sequence.entity.js +67 -0
- package/dist/entities/model-sequence.entity.js.map +1 -0
- package/dist/helpers/field-crud-managers/BigIntFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/BigIntFieldCrudManager.js +13 -2
- package/dist/helpers/field-crud-managers/BigIntFieldCrudManager.js.map +1 -1
- package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.d.ts +0 -1
- package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.js +4 -9
- package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.js.map +1 -1
- package/dist/helpers/field-crud-managers/ManyToOneRelationFieldCrudManager.d.ts +0 -1
- package/dist/helpers/field-crud-managers/ManyToOneRelationFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/ManyToOneRelationFieldCrudManager.js +7 -8
- package/dist/helpers/field-crud-managers/ManyToOneRelationFieldCrudManager.js.map +1 -1
- package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.d.ts +0 -1
- package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.js +4 -9
- package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.js.map +1 -1
- package/dist/helpers/model-metadata-helper.service.d.ts.map +1 -1
- package/dist/helpers/model-metadata-helper.service.js +6 -2
- package/dist/helpers/model-metadata-helper.service.js.map +1 -1
- package/dist/helpers/module-metadata-helper.service.d.ts +1 -0
- package/dist/helpers/module-metadata-helper.service.d.ts.map +1 -1
- package/dist/helpers/module-metadata-helper.service.js +9 -0
- package/dist/helpers/module-metadata-helper.service.js.map +1 -1
- package/dist/helpers/module.helper.d.ts +1 -0
- package/dist/helpers/module.helper.d.ts.map +1 -1
- package/dist/helpers/module.helper.js +29 -3
- package/dist/helpers/module.helper.js.map +1 -1
- package/dist/helpers/solid-registry.d.ts +11 -0
- package/dist/helpers/solid-registry.d.ts.map +1 -1
- package/dist/helpers/solid-registry.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/jobs/computed-field-evaluation-subscriber.service.d.ts +1 -0
- package/dist/jobs/computed-field-evaluation-subscriber.service.d.ts.map +1 -1
- package/dist/jobs/computed-field-evaluation-subscriber.service.js +16 -4
- package/dist/jobs/computed-field-evaluation-subscriber.service.js.map +1 -1
- package/dist/repository/media.repository.d.ts.map +1 -1
- package/dist/repository/media.repository.js +4 -0
- package/dist/repository/media.repository.js.map +1 -1
- package/dist/repository/model-sequence.repository.d.ts +14 -0
- package/dist/repository/model-sequence.repository.d.ts.map +1 -0
- package/dist/repository/model-sequence.repository.js +103 -0
- package/dist/repository/model-sequence.repository.js.map +1 -0
- package/dist/seeders/module-metadata-seeder.service.d.ts +7 -12
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +64 -26
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +343 -27
- package/dist/seeders/system-fields-seeder.service.d.ts +1 -0
- package/dist/seeders/system-fields-seeder.service.d.ts.map +1 -1
- package/dist/seeders/system-fields-seeder.service.js +11 -2
- package/dist/seeders/system-fields-seeder.service.js.map +1 -1
- package/dist/services/action-metadata.service.d.ts.map +1 -1
- package/dist/services/action-metadata.service.js +1 -0
- package/dist/services/action-metadata.service.js.map +1 -1
- package/dist/services/ai-interaction.service.d.ts.map +1 -1
- package/dist/services/ai-interaction.service.js +1 -0
- package/dist/services/ai-interaction.service.js.map +1 -1
- package/dist/services/authentication.service.d.ts.map +1 -1
- package/dist/services/authentication.service.js +22 -14
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/chatter-message-details.service.d.ts.map +1 -1
- package/dist/services/chatter-message-details.service.js +1 -0
- package/dist/services/chatter-message-details.service.js.map +1 -1
- package/dist/services/chatter-message.service.d.ts.map +1 -1
- package/dist/services/chatter-message.service.js +7 -3
- package/dist/services/chatter-message.service.js.map +1 -1
- package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.d.ts.map +1 -1
- package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.js +7 -5
- package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.js.map +1 -1
- package/dist/services/computed-fields/entity/sequence-num-computed-field-provider.d.ts +15 -0
- package/dist/services/computed-fields/entity/sequence-num-computed-field-provider.d.ts.map +1 -0
- package/dist/services/computed-fields/entity/sequence-num-computed-field-provider.js +72 -0
- package/dist/services/computed-fields/entity/sequence-num-computed-field-provider.js.map +1 -0
- package/dist/services/crud-helper.service.d.ts +23 -6
- package/dist/services/crud-helper.service.d.ts.map +1 -1
- package/dist/services/crud-helper.service.js +257 -45
- package/dist/services/crud-helper.service.js.map +1 -1
- package/dist/services/crud.service.d.ts +3 -1
- package/dist/services/crud.service.d.ts.map +1 -1
- package/dist/services/crud.service.js +53 -24
- package/dist/services/crud.service.js.map +1 -1
- package/dist/services/database/database-bootstrap.service.d.ts +12 -0
- package/dist/services/database/database-bootstrap.service.d.ts.map +1 -0
- package/dist/services/database/database-bootstrap.service.js +115 -0
- package/dist/services/database/database-bootstrap.service.js.map +1 -0
- package/dist/services/email-template.service.d.ts +7 -7
- package/dist/services/email-template.service.d.ts.map +1 -1
- package/dist/services/email-template.service.js +8 -7
- package/dist/services/email-template.service.js.map +1 -1
- package/dist/services/excel.service.d.ts +10 -0
- package/dist/services/excel.service.d.ts.map +1 -1
- package/dist/services/excel.service.js +100 -0
- package/dist/services/excel.service.js.map +1 -1
- package/dist/services/field-metadata.service.d.ts +4 -1
- package/dist/services/field-metadata.service.d.ts.map +1 -1
- package/dist/services/field-metadata.service.js +35 -30
- package/dist/services/field-metadata.service.js.map +1 -1
- package/dist/services/file.service.d.ts +1 -0
- package/dist/services/file.service.d.ts.map +1 -1
- package/dist/services/file.service.js +9 -0
- package/dist/services/file.service.js.map +1 -1
- package/dist/services/fixtures.service.d.ts +13 -0
- package/dist/services/fixtures.service.d.ts.map +1 -0
- package/dist/services/fixtures.service.js +95 -0
- package/dist/services/fixtures.service.js.map +1 -0
- package/dist/services/genai/ingest-metadata.service.d.ts.map +1 -1
- package/dist/services/genai/ingest-metadata.service.js +1 -1
- package/dist/services/genai/ingest-metadata.service.js.map +1 -1
- package/dist/services/import-transaction-error-log.service.d.ts.map +1 -1
- package/dist/services/import-transaction-error-log.service.js +1 -0
- package/dist/services/import-transaction-error-log.service.js.map +1 -1
- package/dist/services/import-transaction.service.d.ts.map +1 -1
- package/dist/services/import-transaction.service.js +7 -1
- package/dist/services/import-transaction.service.js.map +1 -1
- package/dist/services/list-of-values.service.d.ts +2 -2
- package/dist/services/list-of-values.service.d.ts.map +1 -1
- package/dist/services/list-of-values.service.js +2 -1
- package/dist/services/list-of-values.service.js.map +1 -1
- package/dist/services/locale.service.d.ts.map +1 -1
- package/dist/services/locale.service.js +1 -0
- package/dist/services/locale.service.js.map +1 -1
- package/dist/services/mail/smtp-email.service.js +0 -1
- package/dist/services/mail/smtp-email.service.js.map +1 -1
- package/dist/services/media.service.d.ts +3 -3
- package/dist/services/media.service.d.ts.map +1 -1
- package/dist/services/media.service.js +6 -4
- package/dist/services/media.service.js.map +1 -1
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts.map +1 -1
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +17 -6
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
- package/dist/services/mediaStorageProviders/file-storage-provider.d.ts.map +1 -1
- package/dist/services/mediaStorageProviders/file-storage-provider.js +0 -13
- package/dist/services/mediaStorageProviders/file-storage-provider.js.map +1 -1
- package/dist/services/menu-item-metadata.service.d.ts.map +1 -1
- package/dist/services/menu-item-metadata.service.js +4 -0
- package/dist/services/menu-item-metadata.service.js.map +1 -1
- package/dist/services/model-metadata.service.d.ts.map +1 -1
- package/dist/services/model-metadata.service.js +2 -42
- package/dist/services/model-metadata.service.js.map +1 -1
- package/dist/services/model-sequence.service.d.ts +23 -0
- package/dist/services/model-sequence.service.d.ts.map +1 -0
- package/dist/services/model-sequence.service.js +55 -0
- package/dist/services/model-sequence.service.js.map +1 -0
- package/dist/services/module-metadata.service.d.ts +1 -0
- package/dist/services/module-metadata.service.d.ts.map +1 -1
- package/dist/services/module-metadata.service.js +35 -1
- package/dist/services/module-metadata.service.js.map +1 -1
- package/dist/services/permission-metadata.service.d.ts +5 -5
- package/dist/services/permission-metadata.service.d.ts.map +1 -1
- package/dist/services/permission-metadata.service.js +6 -5
- package/dist/services/permission-metadata.service.js.map +1 -1
- package/dist/services/queues/database-subscriber.service.d.ts.map +1 -1
- package/dist/services/queues/database-subscriber.service.js +2 -1
- package/dist/services/queues/database-subscriber.service.js.map +1 -1
- package/dist/services/queues/rabbitmq-subscriber.service.d.ts.map +1 -1
- package/dist/services/queues/rabbitmq-subscriber.service.js +2 -2
- package/dist/services/queues/rabbitmq-subscriber.service.js.map +1 -1
- package/dist/services/role-metadata.service.d.ts.map +1 -1
- package/dist/services/role-metadata.service.js +1 -0
- package/dist/services/role-metadata.service.js.map +1 -1
- package/dist/services/scheduled-job.service.d.ts +6 -6
- package/dist/services/scheduled-job.service.d.ts.map +1 -1
- package/dist/services/scheduled-job.service.js +8 -8
- package/dist/services/scheduled-job.service.js.map +1 -1
- package/dist/services/scheduled-jobs/scheduler.service.d.ts.map +1 -1
- package/dist/services/scheduled-jobs/scheduler.service.js +4 -0
- package/dist/services/scheduled-jobs/scheduler.service.js.map +1 -1
- package/dist/services/security-rule.service.d.ts.map +1 -1
- package/dist/services/security-rule.service.js +1 -0
- package/dist/services/security-rule.service.js.map +1 -1
- package/dist/services/selection-providers/list-of-models-selection-provider.service.d.ts.map +1 -1
- package/dist/services/selection-providers/list-of-models-selection-provider.service.js +4 -0
- package/dist/services/selection-providers/list-of-models-selection-provider.service.js.map +1 -1
- package/dist/services/setting.service.d.ts +7 -5
- package/dist/services/setting.service.d.ts.map +1 -1
- package/dist/services/setting.service.js +26 -4
- package/dist/services/setting.service.js.map +1 -1
- package/dist/services/sms-template.service.d.ts +7 -7
- package/dist/services/sms-template.service.d.ts.map +1 -1
- package/dist/services/sms-template.service.js +8 -7
- package/dist/services/sms-template.service.js.map +1 -1
- package/dist/services/solid-introspect.service.d.ts +4 -13
- package/dist/services/solid-introspect.service.d.ts.map +1 -1
- package/dist/services/solid-introspect.service.js +4 -22
- package/dist/services/solid-introspect.service.js.map +1 -1
- package/dist/services/solid-ts-morph.service.js +2 -2
- package/dist/services/solid-ts-morph.service.js.map +1 -1
- package/dist/services/user-activity-history.service.d.ts.map +1 -1
- package/dist/services/user-activity-history.service.js +1 -0
- package/dist/services/user-activity-history.service.js.map +1 -1
- package/dist/services/user-view-metadata.service.d.ts.map +1 -1
- package/dist/services/user-view-metadata.service.js +3 -2
- package/dist/services/user-view-metadata.service.js.map +1 -1
- package/dist/services/user.service.d.ts.map +1 -1
- package/dist/services/user.service.js +1 -0
- package/dist/services/user.service.js.map +1 -1
- package/dist/services/view-metadata.service.d.ts +1 -1
- package/dist/services/view-metadata.service.d.ts.map +1 -1
- package/dist/services/view-metadata.service.js +3 -1
- package/dist/services/view-metadata.service.js.map +1 -1
- package/dist/solid-core-cli-db.module.d.ts.map +1 -1
- package/dist/solid-core-cli-db.module.js +5 -2
- package/dist/solid-core-cli-db.module.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +18 -0
- package/dist/solid-core.module.js.map +1 -1
- package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
- package/dist/subscribers/audit.subscriber.js +5 -1
- package/dist/subscribers/audit.subscriber.js.map +1 -1
- package/dist/subscribers/computed-entity-field.subscriber.d.ts +4 -2
- package/dist/subscribers/computed-entity-field.subscriber.d.ts.map +1 -1
- package/dist/subscribers/computed-entity-field.subscriber.js +53 -12
- package/dist/subscribers/computed-entity-field.subscriber.js.map +1 -1
- package/dist/transformers/typeorm/local-date-time-transformer.d.ts +5 -0
- package/dist/transformers/typeorm/local-date-time-transformer.d.ts.map +1 -0
- package/dist/transformers/typeorm/local-date-time-transformer.js +26 -0
- package/dist/transformers/typeorm/local-date-time-transformer.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/docs/grouping-enhancements.md +89 -0
- package/package.json +1 -1
- package/src/commands/fixtures/fixtures-setup.command.ts +44 -0
- package/src/commands/fixtures/fixtures-tear-down.command.ts +45 -0
- package/src/commands/refresh-model.command.ts +3 -1
- package/src/constants/error-messages.ts +7 -1
- package/src/controllers/model-sequence.controller.ts +93 -0
- package/src/controllers/setting.controller.ts +33 -21
- package/src/dtos/basic-filters.dto.ts +6 -1
- package/src/dtos/basic-group-filters.dto.ts +23 -0
- package/src/dtos/create-field-metadata.dto.ts +1 -1
- package/src/dtos/create-model-sequence.dto.ts +51 -0
- package/src/dtos/create-role-metadata.dto.ts +16 -3
- package/src/dtos/get-mcp-url.dto.ts +13 -0
- package/src/dtos/resolve-s3-url.dto.ts +9 -11
- package/src/dtos/update-model-sequence.dto.ts +53 -0
- package/src/entities/common.entity.ts +2 -2
- package/src/entities/legacy-common.entity.ts +2 -1
- package/src/entities/model-sequence.entity.ts +32 -0
- package/src/helpers/field-crud-managers/BigIntFieldCrudManager.ts +18 -5
- package/src/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.ts +9 -9
- package/src/helpers/field-crud-managers/ManyToOneRelationFieldCrudManager.ts +16 -8
- package/src/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.ts +9 -9
- package/src/helpers/model-metadata-helper.service.ts +6 -4
- package/src/helpers/module-metadata-helper.service.ts +18 -1
- package/src/helpers/module.helper.ts +40 -5
- package/src/helpers/solid-registry.ts +14 -0
- package/src/index.ts +3 -1
- package/src/jobs/computed-field-evaluation-subscriber.service.ts +15 -4
- package/src/repository/media.repository.ts +3 -2
- package/src/repository/model-sequence.repository.ts +97 -0
- package/src/seeders/module-metadata-seeder.service.ts +103 -29
- package/src/seeders/seed-data/solid-core-metadata.json +343 -27
- package/src/seeders/system-fields-seeder.service.ts +6 -2
- package/src/services/action-metadata.service.ts +3 -2
- package/src/services/ai-interaction.service.ts +2 -1
- package/src/services/authentication.service.ts +46 -14
- package/src/services/chatter-message-details.service.ts +2 -1
- package/src/services/chatter-message.service.ts +10 -4
- package/src/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.ts +8 -7
- package/src/services/computed-fields/entity/sequence-num-computed-field-provider.ts +86 -0
- package/src/services/crud-helper.service.ts +287 -49
- package/src/services/crud.service.ts +83 -32
- package/src/services/database/database-bootstrap.service.ts +91 -0
- package/src/services/email-template.service.ts +11 -13
- package/src/services/excel.service.ts +146 -3
- package/src/services/field-metadata.service.ts +102 -55
- package/src/services/file.service.ts +9 -0
- package/src/services/fixtures.service.ts +108 -0
- package/src/services/genai/ingest-metadata.service.ts +4 -3
- package/src/services/import-transaction-error-log.service.ts +2 -1
- package/src/services/import-transaction.service.ts +8 -4
- package/src/services/list-of-values.service.ts +4 -4
- package/src/services/locale.service.ts +2 -1
- package/src/services/mail/smtp-email.service.ts +1 -1
- package/src/services/media.service.ts +10 -11
- package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +22 -7
- package/src/services/mediaStorageProviders/file-storage-provider.ts +18 -13
- package/src/services/menu-item-metadata.service.ts +6 -2
- package/src/services/model-metadata.service.ts +50 -44
- package/src/services/model-sequence.service.ts +33 -0
- package/src/services/module-metadata.service.ts +49 -2
- package/src/services/permission-metadata.service.ts +8 -9
- package/src/services/queues/database-subscriber.service.ts +3 -1
- package/src/services/queues/rabbitmq-subscriber.service.ts +4 -2
- package/src/services/role-metadata.service.ts +1 -0
- package/src/services/scheduled-job.service.ts +9 -9
- package/src/services/scheduled-jobs/scheduler.service.ts +5 -0
- package/src/services/security-rule.service.ts +1 -0
- package/src/services/selection-providers/list-of-models-selection-provider.service.ts +5 -2
- package/src/services/setting.service.ts +33 -6
- package/src/services/sms-template.service.ts +11 -13
- package/src/services/solid-introspect.service.ts +6 -19
- package/src/services/solid-ts-morph.service.ts +2 -2
- package/src/services/user-activity-history.service.ts +3 -2
- package/src/services/user-view-metadata.service.ts +4 -3
- package/src/services/user.service.ts +2 -1
- package/src/services/view-metadata.service.ts +5 -4
- package/src/solid-core-cli-db.module.ts +5 -4
- package/src/solid-core.module.ts +18 -0
- package/src/subscribers/audit.subscriber.ts +3 -2
- package/src/subscribers/computed-entity-field.subscriber.ts +60 -17
- package/src/transformers/typeorm/local-date-time-transformer.ts +30 -0
- /package/sql/{mssql → default/mssql}/proc_CleanupModelMetadata.sql +0 -0
- /package/sql/{mssql → default/mssql}/proc_CleanupModuleMetadata.sql +0 -0
- /package/sql/{mssql/scratchpad.sql → default/mssql/scratchpad.sql.txt} +0 -0
- /package/sql/{postgres → default/postgres}/proc_CleanupModelMetadata.sql +0 -0
- /package/sql/{postgres → default/postgres}/proc_CleanupModuleMetadata.sql +0 -0
- /package/sql/{postgres/scratchpad.sql → default/postgres/scratchpad.sql.txt} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BadRequestException, NotFoundException } from "@nestjs/common";
|
|
1
|
+
import { BadRequestException, Inject, NotFoundException } from "@nestjs/common";
|
|
2
2
|
import { ConfigService } from "@nestjs/config";
|
|
3
3
|
import { DiscoveryService, ModuleRef } from "@nestjs/core";
|
|
4
4
|
import { isArray } from "class-validator";
|
|
@@ -33,13 +33,14 @@ import { SelectionStaticFieldCrudManager } from "../helpers/field-crud-managers/
|
|
|
33
33
|
import { ShortTextFieldCrudManager } from "../helpers/field-crud-managers/ShortTextFieldCrudManager";
|
|
34
34
|
import { UUIDFieldCrudManager } from "../helpers/field-crud-managers/UUIDFieldCrudManager";
|
|
35
35
|
import { FieldCrudManager, MediaWithFullUrl } from "../interfaces";
|
|
36
|
-
import { CrudHelperService, UserIdFields } from "./crud-helper.service";
|
|
36
|
+
import { CrudHelperService, FilterCombinator, UserIdFields } from "./crud-helper.service";
|
|
37
37
|
import { FileService } from "./file.service";
|
|
38
38
|
import { HashingService } from "./hashing.service";
|
|
39
39
|
import { getMediaStorageProvider } from "./mediaStorageProviders";
|
|
40
40
|
import { ModelMetadataService } from "./model-metadata.service";
|
|
41
41
|
import { ModuleMetadataService } from "./module-metadata.service";
|
|
42
42
|
import { RequestContextService } from "./request-context.service";
|
|
43
|
+
import { BasicGroupFilterDto } from "src/dtos/basic-group-filters.dto";
|
|
43
44
|
|
|
44
45
|
export class CRUDService<T extends CommonEntity> { // Add two generic value i.e Person,CreatePersonDto, so we get the proper types in our service
|
|
45
46
|
|
|
@@ -55,6 +56,8 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
|
|
|
55
56
|
readonly modelName: string,
|
|
56
57
|
readonly moduleName: string,
|
|
57
58
|
readonly moduleRef: ModuleRef,
|
|
59
|
+
readonly defaultEntityManager? : EntityManager
|
|
60
|
+
|
|
58
61
|
//We can just have the Model Entity here
|
|
59
62
|
) { }
|
|
60
63
|
|
|
@@ -182,7 +185,12 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
|
|
|
182
185
|
throw new BadRequestException(`Cannot update a published record for model ${this.modelName}. Unpublish it first.`
|
|
183
186
|
);
|
|
184
187
|
}
|
|
185
|
-
|
|
188
|
+
|
|
189
|
+
// // In some instances for legacy tables sometimes id is mapped as a bigint.
|
|
190
|
+
// // in these cases the update method ends up attempting to insert records due to some type orm type mismatch issue.
|
|
191
|
+
// const idFieldMetadata = model.fields.find(f => f.name === 'id');
|
|
192
|
+
// updateDto.id = idFieldMetadata?.type === 'bigint' ? BigInt(id) : id;
|
|
193
|
+
|
|
186
194
|
// This class will be extended by the generated service class i.e PersonService
|
|
187
195
|
// The data required to identify the model and module name will be passed from the generate CrudService subclass
|
|
188
196
|
//TODO: Algorithm to create the entity
|
|
@@ -455,19 +463,34 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
|
|
|
455
463
|
|
|
456
464
|
// Create above query on pincode table using query builder
|
|
457
465
|
var qb: SelectQueryBuilder<T> = await this.repo.createSecurityRuleAwareQueryBuilder(alias)
|
|
458
|
-
// qb = this.crudHelperService.buildFilterQuery(qb, basicFilterDto, alias);
|
|
459
|
-
if (internationalisation && draftPublishWorkflow) {
|
|
460
|
-
qb = this.crudHelperService.buildFilterQuery(qb, basicFilterDto, alias, internationalisation, draftPublishWorkflow, this.moduleRef);
|
|
461
|
-
}
|
|
462
|
-
else {
|
|
463
|
-
qb = this.crudHelperService.buildFilterQuery(qb, basicFilterDto, alias);
|
|
464
|
-
}
|
|
465
466
|
|
|
466
467
|
if (basicFilterDto.groupBy) {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
468
|
+
const groupFilterQb = (internationalisation && draftPublishWorkflow)
|
|
469
|
+
? this.crudHelperService.buildFilterQuery(qb, basicFilterDto, alias, internationalisation, draftPublishWorkflow, this.moduleRef, FilterCombinator.AND, false, false)
|
|
470
|
+
: this.crudHelperService.buildFilterQuery(qb, basicFilterDto, alias, undefined, undefined, undefined, FilterCombinator.AND, false, false);
|
|
471
|
+
|
|
472
|
+
const groupByFields = this.crudHelperService.normalize(basicFilterDto.groupBy);
|
|
473
|
+
if (!groupByFields.length) {
|
|
474
|
+
throw new BadRequestException(ERROR_MESSAGES.INVALID_GROUP_BY_COUNT);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (basicFilterDto.populateGroup) {
|
|
478
|
+
const hasRelationGroup = groupByFields.some(field => field.includes('.'));
|
|
479
|
+
if (hasRelationGroup) {
|
|
480
|
+
throw new BadRequestException('populateGroup is not supported when grouping on relation fields. Fetch group metadata first and retrieve records in a separate call.');
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const { aliasMap: groupAliasMap, formatMap: groupFormatMap, expressionMap: groupExpressionMap } = this.crudHelperService.applyGroupBySelections(groupFilterQb, groupByFields, alias);
|
|
485
|
+
const aggregateAliasMap = this.crudHelperService.applyAggregates(groupFilterQb, basicFilterDto.aggregates, alias);
|
|
486
|
+
const sortAliasMap = { ...groupAliasMap, ...aggregateAliasMap };
|
|
487
|
+
this.crudHelperService.applyGroupSortingAndPagination(groupFilterQb, basicFilterDto.sort, sortAliasMap, limit, offset);
|
|
488
|
+
|
|
489
|
+
const groupByResult = await groupFilterQb.getRawMany();
|
|
490
|
+
const totalGroups = await this.crudHelperService.countGroups(groupFilterQb);
|
|
491
|
+
|
|
492
|
+
const groupByFieldsOrdered = this.crudHelperService.normalize(basicFilterDto.groupBy || []);
|
|
493
|
+
const { groupMeta, groupRecords } = await this.handleGroupFind(groupByResult, groupFilter, populateGroup, alias, populateUserIdFields, populateMedia, basicFilterDto, groupAliasMap, aggregateAliasMap, groupByFieldsOrdered, groupFormatMap, groupExpressionMap);
|
|
471
494
|
|
|
472
495
|
return {
|
|
473
496
|
meta: {
|
|
@@ -478,7 +501,9 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
|
|
|
478
501
|
}
|
|
479
502
|
}
|
|
480
503
|
else {
|
|
481
|
-
|
|
504
|
+
qb = (internationalisation && draftPublishWorkflow)
|
|
505
|
+
? this.crudHelperService.buildFilterQuery(qb, basicFilterDto, alias, internationalisation, draftPublishWorkflow, this.moduleRef)
|
|
506
|
+
: this.crudHelperService.buildFilterQuery(qb, basicFilterDto, alias);
|
|
482
507
|
const { meta, records } = await this.handleNonGroupFind(qb, populateUserIdFields, populateMedia, offset, limit, alias);
|
|
483
508
|
return {
|
|
484
509
|
meta,
|
|
@@ -503,17 +528,38 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
|
|
|
503
528
|
return this.wrapFindResponse(offset, limit, count, entities);
|
|
504
529
|
}
|
|
505
530
|
|
|
506
|
-
private async handleGroupFind(
|
|
507
|
-
|
|
508
|
-
|
|
531
|
+
private async handleGroupFind(
|
|
532
|
+
groupByResult: any[],
|
|
533
|
+
groupFilter: BasicGroupFilterDto | undefined,
|
|
534
|
+
populateGroup: boolean,
|
|
535
|
+
alias: string,
|
|
536
|
+
populateUserIdFields: UserIdFields[],
|
|
537
|
+
populateMedia: string[],
|
|
538
|
+
baseFilterDto: BasicFilterDto,
|
|
539
|
+
groupAliasMap: Record<string, string>,
|
|
540
|
+
aggregateAliasMap: Record<string, string>,
|
|
541
|
+
groupByFieldsOrdered: string[],
|
|
542
|
+
groupFormatMap: Record<string, string | undefined>,
|
|
543
|
+
groupExpressionMap: Record<string, string>
|
|
544
|
+
) {
|
|
509
545
|
const groupMeta = [];
|
|
510
546
|
const groupRecords = [];
|
|
547
|
+
const aggregateAliasSet = new Set(Object.values(aggregateAliasMap));
|
|
511
548
|
// For each group, get the records and the count
|
|
512
549
|
for (const group of groupByResult) {
|
|
513
550
|
if (populateGroup) {
|
|
514
551
|
let groupByQb: SelectQueryBuilder<T> = await this.repo.createSecurityRuleAwareQueryBuilder(alias);
|
|
515
|
-
|
|
516
|
-
|
|
552
|
+
const groupFilterDto: BasicFilterDto = {
|
|
553
|
+
...baseFilterDto,
|
|
554
|
+
...groupFilter,
|
|
555
|
+
groupBy: undefined,
|
|
556
|
+
aggregates: undefined,
|
|
557
|
+
// Only use explicit groupFilter.sort for record ordering; group-level sorts can contain
|
|
558
|
+
// group expressions (e.g. createdAt:day) that are invalid on record queries.
|
|
559
|
+
sort: groupFilter?.sort,
|
|
560
|
+
};
|
|
561
|
+
groupByQb = this.crudHelperService.buildFilterQuery(groupByQb, groupFilterDto, alias);
|
|
562
|
+
groupByQb = this.crudHelperService.buildGroupByRecordsQuery(groupByQb, group, alias, groupAliasMap, aggregateAliasMap, groupExpressionMap);
|
|
517
563
|
const [entities, count] = await groupByQb.getManyAndCount();
|
|
518
564
|
|
|
519
565
|
// Populate the entity with the userId fields
|
|
@@ -525,20 +571,22 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
|
|
|
525
571
|
if (populateMedia && populateMedia.length > 0) {
|
|
526
572
|
await this.handlePopulateMedia(populateMedia, entities);
|
|
527
573
|
}
|
|
528
|
-
const groupData = this.wrapFindResponse(groupFilter
|
|
529
|
-
groupRecords.push(this.crudHelperService.createGroupRecords(group,
|
|
574
|
+
const groupData = this.wrapFindResponse(groupFilter?.offset, groupFilter?.limit, count, entities);
|
|
575
|
+
groupRecords.push(this.crudHelperService.createGroupRecords(group, aggregateAliasSet, groupData, groupByFieldsOrdered, groupAliasMap, groupFormatMap));
|
|
530
576
|
}
|
|
531
|
-
groupMeta.push(this.crudHelperService.createGroupMeta(group,
|
|
577
|
+
groupMeta.push(this.crudHelperService.createGroupMeta(group, aggregateAliasSet, groupByFieldsOrdered, groupAliasMap, groupFormatMap));
|
|
532
578
|
}
|
|
533
579
|
return { groupMeta, groupRecords };
|
|
534
580
|
}
|
|
535
581
|
|
|
536
|
-
private wrapFindResponse(offset: number, limit: number, count: number, entities: T[]) {
|
|
537
|
-
const
|
|
538
|
-
const
|
|
582
|
+
private wrapFindResponse(offset: number | undefined, limit: number | undefined, count: number, entities: T[]) {
|
|
583
|
+
const safeLimit = limit ?? count ?? 0;
|
|
584
|
+
const safeOffset = offset ?? 0;
|
|
585
|
+
const currentPage = safeLimit ? Math.floor(safeOffset / safeLimit) + 1 : 1;
|
|
586
|
+
const totalPages = safeLimit ? Math.ceil(count / safeLimit) : 1;
|
|
539
587
|
|
|
540
|
-
const nextPage = currentPage < totalPages ? currentPage + 1 : null;
|
|
541
|
-
const prevPage = currentPage > 1 ? currentPage - 1 : null;
|
|
588
|
+
const nextPage = safeLimit && currentPage < totalPages ? currentPage + 1 : null;
|
|
589
|
+
const prevPage = safeLimit && currentPage > 1 ? currentPage - 1 : null;
|
|
542
590
|
|
|
543
591
|
const r = {
|
|
544
592
|
meta: {
|
|
@@ -547,7 +595,7 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
|
|
|
547
595
|
nextPage: nextPage,
|
|
548
596
|
prevPage: prevPage,
|
|
549
597
|
totalPages: totalPages,
|
|
550
|
-
perPage: +
|
|
598
|
+
perPage: safeLimit ? +safeLimit : 0,
|
|
551
599
|
},
|
|
552
600
|
records: entities
|
|
553
601
|
};
|
|
@@ -574,7 +622,7 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
|
|
|
574
622
|
}
|
|
575
623
|
|
|
576
624
|
private async handlePopulateMedia(populateMedia: string[], entities: T[]) {
|
|
577
|
-
const model = await this.
|
|
625
|
+
const model = await this.getDefaultEntityManager().getRepository(ModelMetadata).findOne({
|
|
578
626
|
where: {
|
|
579
627
|
singularName: this.modelName,
|
|
580
628
|
},
|
|
@@ -913,7 +961,7 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
|
|
|
913
961
|
throw new BadRequestException(`Field ${field.name} does not define a relationCoModelSingularName`);
|
|
914
962
|
}
|
|
915
963
|
|
|
916
|
-
const relationCoModel = await this.
|
|
964
|
+
const relationCoModel = await this.getDefaultEntityManager().getRepository(ModelMetadata).findOne({
|
|
917
965
|
where: { singularName: field.relationCoModelSingularName },
|
|
918
966
|
relations: ['fields', 'fields.mediaStorageProvider', 'fields.model'],
|
|
919
967
|
});
|
|
@@ -1016,5 +1064,8 @@ export class CRUDService<T extends CommonEntity> { // Add two generic value i.e
|
|
|
1016
1064
|
|
|
1017
1065
|
return updatedEntity
|
|
1018
1066
|
}
|
|
1019
|
-
}
|
|
1020
1067
|
|
|
1068
|
+
private getDefaultEntityManager(){
|
|
1069
|
+
return this.defaultEntityManager ?? this.entityManager;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
|
|
2
|
+
import { DataSource } from 'typeorm';
|
|
3
|
+
import { readdir, readFile } from 'fs/promises';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { InjectDataSource } from '@nestjs/typeorm';
|
|
6
|
+
|
|
7
|
+
@Injectable()
|
|
8
|
+
export class DatabaseBootstrapService implements OnModuleInit {
|
|
9
|
+
private readonly logger = new Logger(DatabaseBootstrapService.name);
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
@InjectDataSource()
|
|
13
|
+
private readonly dataSource: DataSource,
|
|
14
|
+
) { }
|
|
15
|
+
|
|
16
|
+
async onModuleInit() {
|
|
17
|
+
if (!this.dataSource.isInitialized) {
|
|
18
|
+
this.logger.warn(`[${this.dataSource.name}] DataSource not initialized. Skipping SQL bootstrap.`);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
this.logger.debug(`[${this.dataSource.name}] Bootstrapping stored procedures...`);
|
|
23
|
+
|
|
24
|
+
await this.applyAllSqlFiles();
|
|
25
|
+
|
|
26
|
+
this.logger.debug(`[${this.dataSource.name}] SQL bootstrap completed`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private resolveSqlDirectory(): string {
|
|
30
|
+
const datasourceName = this.dataSource.name || 'default';
|
|
31
|
+
const dbType = this.dataSource.options.type;
|
|
32
|
+
|
|
33
|
+
const sqlFilePath = path.resolve(__dirname, `../../../sql/${datasourceName}/${dbType}`);
|
|
34
|
+
|
|
35
|
+
return sqlFilePath
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private async applyAllSqlFiles() {
|
|
39
|
+
const sqlDir = this.resolveSqlDirectory();
|
|
40
|
+
|
|
41
|
+
this.logger.debug(`[${this.dataSource.name}] SQL directory: ${sqlDir}`);
|
|
42
|
+
|
|
43
|
+
let files: string[];
|
|
44
|
+
try {
|
|
45
|
+
files = await readdir(sqlDir);
|
|
46
|
+
} catch {
|
|
47
|
+
this.logger.warn(
|
|
48
|
+
`[${this.dataSource.name}] No SQL directory found. Skipping.`,
|
|
49
|
+
);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const sqlFiles = files
|
|
54
|
+
.filter(file => file.endsWith('.sql'))
|
|
55
|
+
.sort();
|
|
56
|
+
|
|
57
|
+
if (!sqlFiles.length) {
|
|
58
|
+
this.logger.warn(
|
|
59
|
+
`[${this.dataSource.name}] No SQL files found`,
|
|
60
|
+
);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for (const file of sqlFiles) {
|
|
65
|
+
await this.applySqlFileSafely(
|
|
66
|
+
path.join(sqlDir, file),
|
|
67
|
+
file,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private async applySqlFileSafely(
|
|
73
|
+
filePath: string,
|
|
74
|
+
fileName: string,
|
|
75
|
+
) {
|
|
76
|
+
this.logger.debug(`[${this.dataSource.name}] Applying ${fileName}`);
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const sql = await readFile(filePath, 'utf8');
|
|
80
|
+
await this.dataSource.query(sql);
|
|
81
|
+
|
|
82
|
+
this.logger.debug(`[${this.dataSource.name}] Applied ${fileName}`);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
// DO NOT THROW — continue with next file
|
|
85
|
+
this.logger.error(
|
|
86
|
+
`[${this.dataSource.name}] Failed ${fileName}`,
|
|
87
|
+
error instanceof Error ? error.stack : String(error),
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -1,25 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { forwardRef, Inject, Injectable } from '@nestjs/common';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { ConfigService } from '@nestjs/config';
|
|
4
|
+
import { DiscoveryService, ModuleRef } from '@nestjs/core';
|
|
5
|
+
import { InjectEntityManager } from '@nestjs/typeorm';
|
|
6
|
+
import { EmailTemplateRepository } from 'src/repository/email-template.repository';
|
|
7
|
+
import { EntityManager } from 'typeorm';
|
|
6
8
|
import { EmailTemplate } from '../entities/email-template.entity';
|
|
7
|
-
import {
|
|
8
|
-
import { UpdateEmailTemplateDto } from '../dtos/update-email-template.dto';
|
|
9
|
+
import { CrudHelperService } from './crud-helper.service';
|
|
9
10
|
import { CRUDService } from './crud.service';
|
|
10
|
-
import { ModelMetadataService } from './model-metadata.service';
|
|
11
|
-
import { ModuleMetadataService } from './module-metadata.service';
|
|
12
|
-
import { MediaStorageProviderMetadataService } from './media-storage-provider-metadata.service';
|
|
13
|
-
import { ConfigService } from '@nestjs/config';
|
|
14
11
|
import { FileService } from './file.service';
|
|
12
|
+
import { MediaStorageProviderMetadataService } from './media-storage-provider-metadata.service';
|
|
15
13
|
import { MediaService } from './media.service';
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import { EmailTemplateRepository } from 'src/repository/email-template.repository';
|
|
14
|
+
import { ModelMetadataService } from './model-metadata.service';
|
|
15
|
+
import { ModuleMetadataService } from './module-metadata.service';
|
|
19
16
|
|
|
20
17
|
@Injectable()
|
|
21
18
|
export class EmailTemplateService extends CRUDService<EmailTemplate>{
|
|
22
19
|
constructor(
|
|
20
|
+
@Inject(forwardRef(() => ModelMetadataService))
|
|
23
21
|
readonly modelMetadataService: ModelMetadataService,
|
|
24
22
|
readonly moduleMetadataService: ModuleMetadataService,
|
|
25
23
|
readonly mediaStorageProviderService: MediaStorageProviderMetadataService,
|
|
@@ -3,7 +3,6 @@ import * as ExcelJS from 'exceljs';
|
|
|
3
3
|
import { ERROR_MESSAGES } from 'src/constants/error-messages';
|
|
4
4
|
import { PassThrough, Readable } from 'stream';
|
|
5
5
|
|
|
6
|
-
|
|
7
6
|
export interface ExcelReadOptions {
|
|
8
7
|
pageSize?: number; // Number of records per page
|
|
9
8
|
hasHeaderRow?: boolean; // Whether the first row contains headers
|
|
@@ -17,6 +16,11 @@ export interface ExcelReadResult {
|
|
|
17
16
|
data: Record<string, any>[]; // Data records in the current page
|
|
18
17
|
}
|
|
19
18
|
|
|
19
|
+
export interface ExcelReadAllResult {
|
|
20
|
+
headers: string[];
|
|
21
|
+
rows: Record<string, any>[];
|
|
22
|
+
}
|
|
23
|
+
|
|
20
24
|
@Injectable()
|
|
21
25
|
export class ExcelService {
|
|
22
26
|
private logger = new Logger(ExcelService.name);
|
|
@@ -59,7 +63,7 @@ export class ExcelService {
|
|
|
59
63
|
// headers.reduce((acc, header) => ({ ...acc, [header]: '' }), {})
|
|
60
64
|
// ).commit(); // Write a dummy record with headers
|
|
61
65
|
|
|
62
|
-
|
|
66
|
+
workbook.commit();
|
|
63
67
|
return passThrough;
|
|
64
68
|
}
|
|
65
69
|
|
|
@@ -86,7 +90,7 @@ export class ExcelService {
|
|
|
86
90
|
this.logger.debug(`✅ Chunk ${chunkIndex} written to Excel`);
|
|
87
91
|
}
|
|
88
92
|
|
|
89
|
-
|
|
93
|
+
workbook.commit();
|
|
90
94
|
// passThrough.end(); // ✅ Properly close the stream
|
|
91
95
|
} catch (error) {
|
|
92
96
|
this.logger.error(`❌ Error writing Excel: ${error.message}`);
|
|
@@ -159,4 +163,143 @@ export class ExcelService {
|
|
|
159
163
|
}
|
|
160
164
|
}
|
|
161
165
|
|
|
166
|
+
private cleanString(value: any): string {
|
|
167
|
+
return (value === null || value === undefined ? '' : String(value))
|
|
168
|
+
.replace(/\uFEFF/g, '') // BOM
|
|
169
|
+
.replace(/\u00A0/g, ' ') // NBSP
|
|
170
|
+
.replace(/\s+/g, ' ')
|
|
171
|
+
.trim();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private normalizeCellValue(value: any, sharedStrings?: any[]): any {
|
|
175
|
+
if (value === undefined || value === null) return null;
|
|
176
|
+
|
|
177
|
+
// ExcelJS streaming shared string ref: { sharedString: number }
|
|
178
|
+
if (typeof value === 'object' && value && 'sharedString' in value) {
|
|
179
|
+
const idx = (value as any).sharedString;
|
|
180
|
+
const resolved = sharedStrings?.[idx];
|
|
181
|
+
// sharedStrings may store objects or plain strings depending on ExcelJS internals
|
|
182
|
+
if (resolved === undefined || resolved === null) return null;
|
|
183
|
+
if (typeof resolved === 'string') return resolved;
|
|
184
|
+
if (typeof resolved === 'object') {
|
|
185
|
+
if ('text' in resolved && typeof (resolved as any).text === 'string') return (resolved as any).text;
|
|
186
|
+
if ('richText' in resolved && Array.isArray((resolved as any).richText)) {
|
|
187
|
+
return (resolved as any).richText.map((item: any) => item?.text ?? '').join('');
|
|
188
|
+
}
|
|
189
|
+
if ('value' in resolved) return (resolved as any).value;
|
|
190
|
+
}
|
|
191
|
+
return resolved;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ExcelJS can return rich objects for styled cells; unwrap to plain text/primitive
|
|
195
|
+
if (typeof value === 'object' && value) {
|
|
196
|
+
// Plain rich cell: { text: '...' }
|
|
197
|
+
if ('text' in value && typeof (value as any).text === 'string') return (value as any).text;
|
|
198
|
+
|
|
199
|
+
// Rich text: { richText: [{text:'a'}, ...] }
|
|
200
|
+
if ('richText' in value && Array.isArray((value as any).richText)) {
|
|
201
|
+
return (value as any).richText.map((item: any) => item?.text ?? '').join('');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Formula cells: { formula: '...', result: ... }
|
|
205
|
+
if ('result' in value) return (value as any).result;
|
|
206
|
+
if ('formula' in value) return (value as any).formula;
|
|
207
|
+
|
|
208
|
+
// Hyperlinks: { text: '...', hyperlink: '...' }
|
|
209
|
+
if ('hyperlink' in value && typeof (value as any).hyperlink === 'string') {
|
|
210
|
+
return (value as any).text ?? (value as any).hyperlink;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Sometimes primitive nested under .value
|
|
214
|
+
if ('value' in value) return this.normalizeCellValue((value as any).value, sharedStrings);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return value;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
public async readExcelFromStreamNonStreaming(
|
|
221
|
+
stream: Readable,
|
|
222
|
+
options?: ExcelReadOptions & { worksheetIndex?: number; maxRows?: number }
|
|
223
|
+
): Promise<ExcelReadAllResult> {
|
|
224
|
+
const {
|
|
225
|
+
hasHeaderRow = true,
|
|
226
|
+
providedHeaders = [],
|
|
227
|
+
worksheetIndex = 0, // 0-based
|
|
228
|
+
maxRows,
|
|
229
|
+
} = options || {};
|
|
230
|
+
|
|
231
|
+
// 1) Read entire stream into a Buffer
|
|
232
|
+
const chunks: Buffer[] = [];
|
|
233
|
+
for await (const chunk of stream) {
|
|
234
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
235
|
+
}
|
|
236
|
+
const buffer = Buffer.concat(chunks);
|
|
237
|
+
|
|
238
|
+
// 2) Load workbook (non-streaming)
|
|
239
|
+
const workbook = new ExcelJS.Workbook();
|
|
240
|
+
// @ts-ignore
|
|
241
|
+
await workbook.xlsx.load(buffer);
|
|
242
|
+
|
|
243
|
+
const worksheet = workbook.worksheets?.[worksheetIndex];
|
|
244
|
+
if (!worksheet) {
|
|
245
|
+
return { headers: [], rows: [] };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// 3) Determine headers
|
|
249
|
+
let headers: string[] = [];
|
|
250
|
+
|
|
251
|
+
const firstRow = worksheet.getRow(1);
|
|
252
|
+
const firstRowValues = Array.isArray(firstRow.values) ? (firstRow.values as any[]).slice(1) : [];
|
|
253
|
+
|
|
254
|
+
const normalizeNonStreamingCell = (v: any) => {
|
|
255
|
+
// In non-streaming ExcelJS, cell.value can be:
|
|
256
|
+
// - string/number/boolean/date
|
|
257
|
+
// - {richText}, {text}, {hyperlink}, {formula,result}, etc.
|
|
258
|
+
// We'll reuse your normalizeCellValue but without sharedStrings
|
|
259
|
+
return this.normalizeCellValue(v);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
if (hasHeaderRow) {
|
|
263
|
+
headers = firstRowValues.map((v) => this.cleanString(normalizeNonStreamingCell(v)));
|
|
264
|
+
} else if (providedHeaders.length) {
|
|
265
|
+
headers = providedHeaders.map((h) => this.cleanString(h));
|
|
266
|
+
} else {
|
|
267
|
+
headers = firstRowValues.map((_, idx) => `${idx}`);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// If headers are all blank and hasHeaderRow=true, treat as no headers (avoid mapping everything to "")
|
|
271
|
+
if (hasHeaderRow && headers.length > 0 && headers.every((h) => !h)) {
|
|
272
|
+
this.logger.warn(`ExcelService.readExcelFromStreamNonStreaming: header row appears blank`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// 4) Read rows
|
|
276
|
+
const rows: Record<string, any>[] = [];
|
|
277
|
+
|
|
278
|
+
const startRowNumber = hasHeaderRow ? 2 : 1;
|
|
279
|
+
const lastRowNumber = worksheet.rowCount || 0;
|
|
280
|
+
|
|
281
|
+
for (let r = startRowNumber; r <= lastRowNumber; r++) {
|
|
282
|
+
if (maxRows && rows.length >= maxRows) break;
|
|
283
|
+
|
|
284
|
+
const row = worksheet.getRow(r);
|
|
285
|
+
const rawValues = Array.isArray(row.values) ? (row.values as any[]).slice(1) : [];
|
|
286
|
+
const values = rawValues.map((v) => normalizeNonStreamingCell(v));
|
|
287
|
+
|
|
288
|
+
// Align row width to header width
|
|
289
|
+
while (values.length < headers.length) values.push(null);
|
|
290
|
+
if (values.length > headers.length) values.length = headers.length;
|
|
291
|
+
|
|
292
|
+
const record = headers.reduce((acc, key, i) => {
|
|
293
|
+
acc[key] = values[i] ?? null;
|
|
294
|
+
return acc;
|
|
295
|
+
}, {} as Record<string, any>);
|
|
296
|
+
|
|
297
|
+
// Skip fully empty rows
|
|
298
|
+
if (Object.values(record).every((v) => v === null || this.cleanString(v) === '')) continue;
|
|
299
|
+
|
|
300
|
+
rows.push(record);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return { headers, rows };
|
|
304
|
+
}
|
|
162
305
|
}
|