@solidxai/core 0.1.11-beta.2 → 0.1.11-beta.5
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/migrate-removed-fields.command.d.ts +19 -0
- package/dist/commands/migrate-removed-fields.command.d.ts.map +1 -0
- package/dist/commands/migrate-removed-fields.command.js +77 -0
- package/dist/commands/migrate-removed-fields.command.js.map +1 -0
- package/dist/controllers/datasource-introspection.controller.d.ts +38 -1
- package/dist/controllers/datasource-introspection.controller.d.ts.map +1 -1
- package/dist/controllers/datasource-introspection.controller.js +38 -0
- package/dist/controllers/datasource-introspection.controller.js.map +1 -1
- package/dist/dtos/datasource-introspection-mapping.dto.d.ts +5 -0
- package/dist/dtos/datasource-introspection-mapping.dto.d.ts.map +1 -1
- package/dist/dtos/datasource-introspection-mapping.dto.js +24 -2
- package/dist/dtos/datasource-introspection-mapping.dto.js.map +1 -1
- package/dist/entities/{legacy-common-with-id.entity.d.ts → legacy-common-with-generated-id.entity.d.ts} +2 -2
- package/dist/entities/legacy-common-with-generated-id.entity.d.ts.map +1 -0
- package/dist/entities/{legacy-common-with-id.entity.js → legacy-common-with-generated-id.entity.js} +4 -4
- package/dist/entities/legacy-common-with-generated-id.entity.js.map +1 -0
- package/dist/entities/{legacy-common.entity.d.ts → legacy-common.entity-with-existing-id.entity.d.ts} +1 -1
- package/dist/entities/legacy-common.entity-with-existing-id.entity.d.ts.map +1 -0
- package/dist/entities/{legacy-common.entity.js → legacy-common.entity-with-existing-id.entity.js} +1 -1
- package/dist/entities/legacy-common.entity-with-existing-id.entity.js.map +1 -0
- package/dist/helpers/command.service.d.ts +2 -0
- package/dist/helpers/command.service.d.ts.map +1 -1
- package/dist/helpers/command.service.js +6 -2
- package/dist/helpers/command.service.js.map +1 -1
- package/dist/helpers/model-metadata-helper.service.js +3 -3
- package/dist/helpers/model-metadata-helper.service.js.map +1 -1
- package/dist/helpers/nodemon-heartbeat.d.ts +5 -0
- package/dist/helpers/nodemon-heartbeat.d.ts.map +1 -0
- package/dist/helpers/nodemon-heartbeat.js +67 -0
- package/dist/helpers/nodemon-heartbeat.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/interfaces/datasource-introspection-provider.interface.d.ts +1 -0
- package/dist/interfaces/datasource-introspection-provider.interface.d.ts.map +1 -1
- package/dist/interfaces/datasource-introspection-provider.interface.js.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts +7 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +101 -1
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +121 -34
- package/dist/services/api-key.service.d.ts.map +1 -1
- package/dist/services/api-key.service.js.map +1 -1
- package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.d.ts +4 -0
- package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.d.ts.map +1 -1
- package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.js +18 -1
- package/dist/services/computed-fields/entity/concat-entity-computed-field-provider.service.js.map +1 -1
- package/dist/services/crud.service.d.ts +1 -0
- package/dist/services/crud.service.d.ts.map +1 -1
- package/dist/services/crud.service.js +27 -0
- package/dist/services/crud.service.js.map +1 -1
- package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.js +12 -0
- package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.js.map +1 -1
- package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.js +2 -0
- package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.js.map +1 -1
- package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.js +28 -15
- package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.js.map +1 -1
- package/dist/services/datasource-introspection.service.d.ts +53 -2
- package/dist/services/datasource-introspection.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection.service.js +559 -174
- package/dist/services/datasource-introspection.service.js.map +1 -1
- package/dist/services/import-transaction.service.d.ts.map +1 -1
- package/dist/services/import-transaction.service.js +3 -0
- package/dist/services/import-transaction.service.js.map +1 -1
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +10 -10
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
- package/dist/services/model-metadata.service.d.ts +1 -0
- package/dist/services/model-metadata.service.d.ts.map +1 -1
- package/dist/services/model-metadata.service.js +28 -0
- package/dist/services/model-metadata.service.js.map +1 -1
- package/dist/services/module-metadata.service.d.ts.map +1 -1
- package/dist/services/module-metadata.service.js +27 -0
- package/dist/services/module-metadata.service.js.map +1 -1
- package/dist/services/removed-field-migration.service.d.ts +30 -0
- package/dist/services/removed-field-migration.service.d.ts.map +1 -0
- package/dist/services/removed-field-migration.service.js +319 -0
- package/dist/services/removed-field-migration.service.js.map +1 -0
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +4 -0
- package/dist/solid-core.module.js.map +1 -1
- package/dist/subscribers/audit.subscriber.d.ts +4 -1
- package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
- package/dist/subscribers/audit.subscriber.js +54 -6
- package/dist/subscribers/audit.subscriber.js.map +1 -1
- package/dist-tests/api/authenticate.spec.js +119 -0
- package/dist-tests/api/authenticate.spec.js.map +1 -0
- package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +97 -0
- package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +1 -0
- package/dist-tests/api/ping.spec.js +21 -0
- package/dist-tests/api/ping.spec.js.map +1 -0
- package/dist-tests/helpers/auth.js +41 -0
- package/dist-tests/helpers/auth.js.map +1 -0
- package/dist-tests/helpers/env.js +11 -0
- package/dist-tests/helpers/env.js.map +1 -0
- package/docs/agent-hub-grooming.md +301 -0
- package/docs/dashboards/AGENTIC_DASHBOARD_IMPLEMENTATION_PLAN.md +438 -0
- package/docs/dashboards/dashboard-curl-smoke-tests.txt +146 -0
- package/docs/dashboards/delete-legacy-dashboard-metadata.sql +172 -0
- package/docs/datasource-introspection-ddl-analysis.md +326 -0
- package/docs/datasource-introspection-implementation-plan.md +306 -0
- package/docs/grouping-enhancements.md +89 -0
- package/docs/java-spring/README.md +3 -0
- package/docs/java-spring/solid-core-module-deep-dive-report.md +1317 -0
- package/docs/module-package-import-handoff.md +691 -0
- package/docs/seed-changes.md +65 -0
- package/docs/test-data-workflow.md +200 -0
- package/docs/type-declaration-import-issue.md +24 -0
- package/package.json +1 -1
- package/src/commands/migrate-removed-fields.command.ts +81 -0
- package/src/controllers/datasource-introspection.controller.ts +27 -1
- package/src/dtos/datasource-introspection-mapping.dto.ts +15 -0
- package/src/entities/{legacy-common-with-id.entity.ts → legacy-common-with-generated-id.entity.ts} +1 -1
- package/src/helpers/command.service.ts +10 -2
- package/src/helpers/model-metadata-helper.service.ts +1 -1
- package/src/helpers/nodemon-heartbeat.ts +59 -0
- package/src/index.ts +3 -2
- package/src/interfaces/datasource-introspection-provider.interface.ts +1 -0
- package/src/seeders/module-metadata-seeder.service.ts +118 -0
- package/src/seeders/seed-data/solid-core-metadata.json +121 -34
- package/src/services/api-key.service.ts +36 -36
- package/src/services/computed-fields/entity/concat-entity-computed-field-provider.service.ts +15 -1
- package/src/services/crud.service.ts +36 -11
- package/src/services/datasource-introspection/mssql-datasource-introspection-provider.service.ts +12 -0
- package/src/services/datasource-introspection/mysql-datasource-introspection-provider.service.ts +2 -0
- package/src/services/datasource-introspection/postgres-datasource-introspection-provider.service.ts +28 -15
- package/src/services/datasource-introspection.service.ts +653 -150
- package/src/services/import-transaction.service.ts +5 -0
- package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +2 -2
- package/src/services/model-metadata.service.ts +45 -1
- package/src/services/module-metadata.service.ts +35 -0
- package/src/services/removed-field-migration.service.ts +334 -0
- package/src/solid-core.module.ts +4 -0
- package/src/subscribers/audit.subscriber.ts +70 -5
- package/.claude/settings.local.json +0 -15
- package/CLAUDE.md +0 -26
- package/dist/entities/legacy-common-with-id.entity.d.ts.map +0 -1
- package/dist/entities/legacy-common-with-id.entity.js.map +0 -1
- package/dist/entities/legacy-common.entity.d.ts.map +0 -1
- package/dist/entities/legacy-common.entity.js.map +0 -1
- package/src/services/1.js +0 -6
- /package/src/entities/{legacy-common.entity.ts → legacy-common.entity-with-existing-id.entity.ts} +0 -0
|
@@ -204,6 +204,11 @@ export class ImportTransactionService extends CRUDService<ImportTransaction> {
|
|
|
204
204
|
|
|
205
205
|
// Iterate through the fields and populate the standard instructions
|
|
206
206
|
for (const field of allFields) {
|
|
207
|
+
|
|
208
|
+
// Skip Computed fields
|
|
209
|
+
if (field.type === SolidFieldType.computed) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
207
212
|
// Skip system fields
|
|
208
213
|
if (systemFieldNames.includes(field.name)) {
|
|
209
214
|
continue;
|
|
@@ -2,8 +2,8 @@ import { Injectable, Logger } from "@nestjs/common";
|
|
|
2
2
|
import { ConfigService } from "@nestjs/config";
|
|
3
3
|
import { CommonEntity } from "src/entities/common.entity";
|
|
4
4
|
import { FieldMetadata } from "src/entities/field-metadata.entity";
|
|
5
|
-
import { LegacyCommonEntityWithExistingId } from "src/entities/legacy-common.entity";
|
|
6
|
-
import { LegacyCommonEntityWithGeneratedId } from "src/entities/legacy-common-with-id.entity";
|
|
5
|
+
import { LegacyCommonEntityWithExistingId } from "src/entities/legacy-common.entity-with-existing-id.entity";
|
|
6
|
+
import { LegacyCommonEntityWithGeneratedId } from "src/entities/legacy-common-with-generated-id.entity";
|
|
7
7
|
import { Media } from "src/entities/media.entity";
|
|
8
8
|
import { MediaStorageProvider } from "src/interfaces";
|
|
9
9
|
import { DiskFileService, S3FileService } from "src/services/file";
|
|
@@ -27,6 +27,7 @@ import { PermissionMetadata } from '../entities/permission-metadata.entity';
|
|
|
27
27
|
import { RoleMetadata } from '../entities/role-metadata.entity';
|
|
28
28
|
import { ViewMetadata } from '../entities/view-metadata.entity';
|
|
29
29
|
import { CommandService } from '../helpers/command.service';
|
|
30
|
+
import { startNodemonHeartbeat } from '../helpers/nodemon-heartbeat';
|
|
30
31
|
import {
|
|
31
32
|
REFRESH_MODEL_COMMAND,
|
|
32
33
|
REMOVE_FIELDS_COMMAND,
|
|
@@ -967,6 +968,26 @@ export class ModelMetadataService {
|
|
|
967
968
|
@DisallowInProduction()
|
|
968
969
|
async generateCodeViaCtl(modelId: number): Promise<string> {
|
|
969
970
|
const model = await this.findOne(modelId);
|
|
971
|
+
const isEmbedded = process.env.DEFAULT_DATABASE_DRIVER === 'pglite';
|
|
972
|
+
|
|
973
|
+
// When using an embedded PGlite database, the single-connection limit means
|
|
974
|
+
// a spawned `solid refresh-model` subprocess cannot get its own DB connection
|
|
975
|
+
// while the API server is already holding it. Fall back to in-process code
|
|
976
|
+
// generation (which reuses the existing connection).
|
|
977
|
+
if (isEmbedded) {
|
|
978
|
+
this.logger.log('Embedded database detected — generating API code in-process');
|
|
979
|
+
// Prevent nodemon from restarting the server while schematics write files.
|
|
980
|
+
const heartbeat = startNodemonHeartbeat(path.join(process.cwd(), 'src'));
|
|
981
|
+
try {
|
|
982
|
+
return await this.handleGenerateCode({
|
|
983
|
+
modelUserKey: model.singularName,
|
|
984
|
+
dryRun: false,
|
|
985
|
+
});
|
|
986
|
+
} finally {
|
|
987
|
+
heartbeat.stop();
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
|
|
970
991
|
return this.commandService.executeCommandWithArgs({
|
|
971
992
|
command: 'npx',
|
|
972
993
|
args: ['@solidxai/solidctl@latest', 'generate', 'model', `--name=${model.singularName}`],
|
|
@@ -1478,7 +1499,7 @@ export class ModelMetadataService {
|
|
|
1478
1499
|
//Filter out the fields by id
|
|
1479
1500
|
const fieldsForRemoval = model.fields.filter((field) => options.fieldIdsForRemoval.includes(+field.id));
|
|
1480
1501
|
const removeOutput = await this.executeRemoveFieldsCommand(model, fieldsForRemoval, options.dryRun);
|
|
1481
|
-
|
|
1502
|
+
const migrationOutput = await this.executeMigrationRemoveFieldsCommand(model, fieldsForRemoval, options.dryRun);
|
|
1482
1503
|
// Remove the fields from the database as well. This also checks, if the field is marked for removal
|
|
1483
1504
|
for (const field of fieldsForRemoval) {
|
|
1484
1505
|
if (field.isMarkedForRemoval) {
|
|
@@ -1515,6 +1536,13 @@ export class ModelMetadataService {
|
|
|
1515
1536
|
|
|
1516
1537
|
return removeOutput;
|
|
1517
1538
|
}
|
|
1539
|
+
// model-metadata.service.ts mein naya method add karo
|
|
1540
|
+
// async executeRemoveFieldsWithModel(model: ModelMetadata, fieldNames: string[], dryRun = false,) {
|
|
1541
|
+
// const fieldsForRemoval = model.fields.filter(
|
|
1542
|
+
// field => fieldNames.includes(field.name)
|
|
1543
|
+
// );
|
|
1544
|
+
// return this.executeRemoveFieldsCommand(model, fieldsForRemoval, dryRun);
|
|
1545
|
+
// }
|
|
1518
1546
|
|
|
1519
1547
|
async generateModelCode(options: CodeGenerationOptions): Promise<string> {
|
|
1520
1548
|
if (!options.modelId && !options.modelUserKey) {
|
|
@@ -1563,6 +1591,22 @@ export class ModelMetadataService {
|
|
|
1563
1591
|
return output;
|
|
1564
1592
|
}
|
|
1565
1593
|
|
|
1594
|
+
private async executeMigrationRemoveFieldsCommand(model: ModelMetadata, fieldsForRemoval: FieldMetadata[], dryRun: boolean = false,): Promise<string> {
|
|
1595
|
+
if (!fieldsForRemoval || fieldsForRemoval.length === 0) {
|
|
1596
|
+
return "";
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
const output = await this.commandService.executeCommandWithArgs({
|
|
1600
|
+
command: 'npx',
|
|
1601
|
+
args: ['@solidxai/solidctl@latest', 'migration', '-n', model.singularName, 'remove-field', ...(dryRun ? [] : ['--apply'])],
|
|
1602
|
+
cwd: path.join(process.cwd(), '..'),
|
|
1603
|
+
});
|
|
1604
|
+
|
|
1605
|
+
this.logger.debug(`Schematic output : ${output}`);
|
|
1606
|
+
return output;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
|
|
1566
1610
|
async updateUserKey(data: any) {
|
|
1567
1611
|
const { modelName, fieldName } = data;
|
|
1568
1612
|
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
} from '../helpers/schematic.service';
|
|
27
27
|
import { CommandService } from '../helpers/command.service';
|
|
28
28
|
import { SolidRegistry } from '../helpers/solid-registry';
|
|
29
|
+
import { startNodemonHeartbeat } from '../helpers/nodemon-heartbeat';
|
|
29
30
|
import { CodeGenerationOptions, ModuleMetadataConfiguration } from '../interfaces';
|
|
30
31
|
import { CrudHelperService } from './crud-helper.service';
|
|
31
32
|
import { ModelMetadataService } from './model-metadata.service';
|
|
@@ -534,6 +535,40 @@ export class ModuleMetadataService {
|
|
|
534
535
|
@DisallowInProduction()
|
|
535
536
|
async generateCodeViaCtl(moduleId: number): Promise<string> {
|
|
536
537
|
const module = await this.findOne(moduleId);
|
|
538
|
+
const isEmbedded = process.env.DEFAULT_DATABASE_DRIVER === 'pglite';
|
|
539
|
+
|
|
540
|
+
// When using an embedded PGlite database, the single-connection limit means
|
|
541
|
+
// a spawned `solid refresh-module` subprocess cannot get its own DB connection
|
|
542
|
+
// while the API server is already holding it. Fall back to in-process code
|
|
543
|
+
// generation (which reuses the existing connection) and only spawn solidctl
|
|
544
|
+
// for the UI module scaffold (which does not need a database connection).
|
|
545
|
+
if (isEmbedded) {
|
|
546
|
+
this.logger.log('Embedded database detected — generating API code in-process');
|
|
547
|
+
// Prevent nodemon from restarting the server while schematics write files.
|
|
548
|
+
// The heartbeat touches a watched file every 1.5 s (nodemon delay is 2 s),
|
|
549
|
+
// keeping the delay from expiring during DB queries between schematic runs.
|
|
550
|
+
const heartbeat = startNodemonHeartbeat(path.join(process.cwd(), 'src'));
|
|
551
|
+
try {
|
|
552
|
+
const apiOutput = await this.generateCode({
|
|
553
|
+
moduleUserKey: module.name,
|
|
554
|
+
dryRun: false,
|
|
555
|
+
});
|
|
556
|
+
let uiOutput = '';
|
|
557
|
+
try {
|
|
558
|
+
uiOutput = await this.commandService.executeCommandWithArgs({
|
|
559
|
+
command: 'npx',
|
|
560
|
+
args: ['@solidxai/solidctl@latest', 'generate', 'ui-module', `--name=${module.name}`],
|
|
561
|
+
cwd: path.join(process.cwd(), '..'),
|
|
562
|
+
});
|
|
563
|
+
} catch (error) {
|
|
564
|
+
this.logger.warn('UI module scaffold failed (solidctl may be outdated); API code was generated successfully.');
|
|
565
|
+
}
|
|
566
|
+
return `${apiOutput}\n${uiOutput}`;
|
|
567
|
+
} finally {
|
|
568
|
+
heartbeat.stop();
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
537
572
|
return this.commandService.executeCommandWithArgs({
|
|
538
573
|
command: 'npx',
|
|
539
574
|
args: ['@solidxai/solidctl@latest', 'generate', 'module', `--name=${module.name}`],
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import * as fs from "fs/promises";
|
|
2
|
+
import { BadRequestException, Injectable, Logger, NotFoundException } from "@nestjs/common";
|
|
3
|
+
import { ModuleRef } from "@nestjs/core";
|
|
4
|
+
import { getDataSourceToken } from "@nestjs/typeorm";
|
|
5
|
+
import { kebabCase, snakeCase } from "lodash";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import { ERROR_MESSAGES } from "src/constants/error-messages";
|
|
8
|
+
import { RelationType, SolidFieldType } from "src/dtos/create-field-metadata.dto";
|
|
9
|
+
import { FieldMetadata } from "src/entities/field-metadata.entity";
|
|
10
|
+
import { ModelMetadata } from "src/entities/model-metadata.entity";
|
|
11
|
+
import { ModuleMetadataHelperService } from "src/helpers/module-metadata-helper.service";
|
|
12
|
+
import { classify } from "src/helpers/string.helper";
|
|
13
|
+
import { FieldMetadataRepository } from "src/repository/field-metadata.repository";
|
|
14
|
+
import { ModelMetadataRepository } from "src/repository/model-metadata.repository";
|
|
15
|
+
import { DataSource, EntityMetadata, QueryRunner, Table } from "typeorm";
|
|
16
|
+
|
|
17
|
+
export interface RemovedFieldMigrationResult {
|
|
18
|
+
dryRun: boolean;
|
|
19
|
+
modelName: string;
|
|
20
|
+
operations: string[];
|
|
21
|
+
removedFieldNames: string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Injectable()
|
|
25
|
+
export class RemovedFieldMigrationService {
|
|
26
|
+
constructor(
|
|
27
|
+
private readonly modelMetadataRepo: ModelMetadataRepository,
|
|
28
|
+
private readonly fieldMetadataRepo: FieldMetadataRepository,
|
|
29
|
+
private readonly moduleMetadataHelperService: ModuleMetadataHelperService,
|
|
30
|
+
private readonly moduleRef: ModuleRef,
|
|
31
|
+
) { }
|
|
32
|
+
|
|
33
|
+
private readonly logger = new Logger(RemovedFieldMigrationService.name);
|
|
34
|
+
|
|
35
|
+
// Cleans fields marked for removal by updating schema state and metadata for a single model.
|
|
36
|
+
async migrateMarkedFields(modelUserKey: string, dryRun: boolean = false): Promise<RemovedFieldMigrationResult> {
|
|
37
|
+
if (!modelUserKey) {
|
|
38
|
+
throw new BadRequestException("Model name is required");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const model = await this.modelMetadataRepo.findOne({
|
|
42
|
+
where: { singularName: modelUserKey },
|
|
43
|
+
relations: { fields: true, module: true },
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (!model) {
|
|
47
|
+
throw new NotFoundException(ERROR_MESSAGES.MODEL_NOT_FOUND(modelUserKey));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const fieldsForRemoval = model.fields.filter((field) => field.isMarkedForRemoval);
|
|
51
|
+
const operations: string[] = [];
|
|
52
|
+
|
|
53
|
+
if (fieldsForRemoval.length === 0) {
|
|
54
|
+
const message = `No fields marked for removal were found for model "${model.singularName}".`;
|
|
55
|
+
this.logger.log(message);
|
|
56
|
+
operations.push(message);
|
|
57
|
+
return {
|
|
58
|
+
dryRun,
|
|
59
|
+
modelName: model.singularName,
|
|
60
|
+
operations,
|
|
61
|
+
removedFieldNames: [],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const dataSource = await this.resolveDataSource(model.dataSource);
|
|
66
|
+
const entityMetadata = this.resolveEntityMetadata(dataSource, model);
|
|
67
|
+
const queryRunner = dataSource.createQueryRunner();
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
await queryRunner.connect();
|
|
71
|
+
|
|
72
|
+
if (!dryRun) {
|
|
73
|
+
await queryRunner.startTransaction();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for (const field of fieldsForRemoval) {
|
|
77
|
+
await this.cleanupMarkedField({
|
|
78
|
+
field,
|
|
79
|
+
model,
|
|
80
|
+
entityMetadata,
|
|
81
|
+
queryRunner,
|
|
82
|
+
dryRun,
|
|
83
|
+
operations,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!dryRun) {
|
|
88
|
+
await queryRunner.commitTransaction();
|
|
89
|
+
}
|
|
90
|
+
} catch (error) {
|
|
91
|
+
if (!dryRun && queryRunner.isTransactionActive) {
|
|
92
|
+
await queryRunner.rollbackTransaction();
|
|
93
|
+
}
|
|
94
|
+
throw error;
|
|
95
|
+
} finally {
|
|
96
|
+
await queryRunner.release();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
dryRun,
|
|
101
|
+
modelName: model.singularName,
|
|
102
|
+
operations,
|
|
103
|
+
removedFieldNames: fieldsForRemoval.map((field) => field.name),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private async cleanupMarkedField(params: { field: FieldMetadata; model: ModelMetadata; entityMetadata?: EntityMetadata; queryRunner: QueryRunner; dryRun: boolean; operations: string[]; }): Promise<void> {
|
|
108
|
+
const { field, model, entityMetadata, queryRunner, dryRun, operations } = params;
|
|
109
|
+
const relationMetadata = entityMetadata?.relations.find((relation) => relation.propertyName === field.name);
|
|
110
|
+
const resolvedTableName = entityMetadata?.tableName || model.tableName;
|
|
111
|
+
|
|
112
|
+
if (field.type !== SolidFieldType.relation) {
|
|
113
|
+
const columnCandidates = this.buildColumnCandidates(field, entityMetadata, relationMetadata);
|
|
114
|
+
await this.dropColumnsForField(resolvedTableName, field, columnCandidates, queryRunner, dryRun, operations);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (field.relationType === RelationType.manyToOne) {
|
|
119
|
+
const columnCandidates = this.buildColumnCandidates(field, entityMetadata, relationMetadata);
|
|
120
|
+
await this.dropColumnsForField(resolvedTableName, field, columnCandidates, queryRunner, dryRun, operations);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (field.relationType === RelationType.manyTomany) {
|
|
125
|
+
const joinTableName = relationMetadata?.junctionEntityMetadata?.tableName || field.relationJoinTableName;
|
|
126
|
+
const ownsJoinTable = relationMetadata?.isOwning || field.isRelationManyToManyOwner;
|
|
127
|
+
|
|
128
|
+
if (!ownsJoinTable) {
|
|
129
|
+
operations.push(`No direct database cleanup required for inverse many-to-many field "${field.name}".`);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!joinTableName) {
|
|
134
|
+
operations.push(`Skipping join-table cleanup for "${field.name}" because no join table name could be resolved.`);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
await this.dropJoinTable(joinTableName, field, queryRunner, dryRun, operations);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
operations.push(`No direct database cleanup required for relation field "${field.name}" with type "${field.relationType}".`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private async dropColumnsForField(tableName: string, field: FieldMetadata, columnCandidates: string[], queryRunner: QueryRunner, dryRun: boolean, operations: string[],): Promise<void> {
|
|
146
|
+
if (!tableName) {
|
|
147
|
+
operations.push(`Skipping field "${field.name}" because the model table name could not be resolved.`);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (columnCandidates.length === 0) {
|
|
152
|
+
operations.push(`Skipping field "${field.name}" because no column candidates could be resolved.`);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const handledColumns = new Set<string>();
|
|
157
|
+
let droppedAnyColumn = false;
|
|
158
|
+
|
|
159
|
+
for (const columnName of columnCandidates) {
|
|
160
|
+
if (!columnName || handledColumns.has(columnName)) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
handledColumns.add(columnName);
|
|
165
|
+
const table = await this.loadTable(queryRunner, tableName);
|
|
166
|
+
const column = table?.columns.find((tableColumn) => tableColumn.name === columnName);
|
|
167
|
+
|
|
168
|
+
if (!column) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
droppedAnyColumn = true;
|
|
173
|
+
await this.dropColumnArtifacts(table, columnName, queryRunner, dryRun, operations);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (!droppedAnyColumn) {
|
|
177
|
+
operations.push(`No database column found for field "${field.name}" on table "${tableName}". Metadata cleanup will still proceed.`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private async dropColumnArtifacts(table: Table, columnName: string, queryRunner: QueryRunner, dryRun: boolean, operations: string[],): Promise<void> {
|
|
182
|
+
for (const foreignKey of table.foreignKeys.filter((item) => item.columnNames.includes(columnName))) {
|
|
183
|
+
operations.push(`Drop foreign key "${foreignKey.name}" on "${table.name}.${columnName}"`);
|
|
184
|
+
if (!dryRun) {
|
|
185
|
+
await queryRunner.dropForeignKey(table, foreignKey);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
for (const uniqueConstraint of table.uniques.filter((item) => item.columnNames.includes(columnName))) {
|
|
190
|
+
operations.push(`Drop unique constraint "${uniqueConstraint.name}" on "${table.name}.${columnName}"`);
|
|
191
|
+
if (!dryRun) {
|
|
192
|
+
await queryRunner.dropUniqueConstraint(table, uniqueConstraint);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
for (const index of table.indices.filter((item) => item.columnNames.includes(columnName))) {
|
|
197
|
+
operations.push(`Drop index "${index.name}" on "${table.name}.${columnName}"`);
|
|
198
|
+
if (!dryRun) {
|
|
199
|
+
await queryRunner.dropIndex(table, index);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
operations.push(`Drop column "${table.name}.${columnName}"`);
|
|
204
|
+
if (!dryRun) {
|
|
205
|
+
await queryRunner.dropColumn(table, columnName);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
private async dropJoinTable(joinTableName: string, field: FieldMetadata, queryRunner: QueryRunner, dryRun: boolean, operations: string[]): Promise<void> {
|
|
210
|
+
const table = await this.loadTable(queryRunner, joinTableName);
|
|
211
|
+
if (!table) {
|
|
212
|
+
operations.push(`Join table "${joinTableName}" for field "${field.name}" does not exist. Metadata cleanup will still proceed.`);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
operations.push(`Drop join table "${joinTableName}" for field "${field.name}"`);
|
|
217
|
+
if (!dryRun) {
|
|
218
|
+
await queryRunner.dropTable(joinTableName);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
private buildColumnCandidates(field: FieldMetadata, entityMetadata?: EntityMetadata, relationMetadata?: EntityMetadata["relations"][number]): string[] {
|
|
223
|
+
const columnCandidates = new Set<string>();
|
|
224
|
+
|
|
225
|
+
relationMetadata?.joinColumns?.forEach((column) => {
|
|
226
|
+
if (column.databaseName) {
|
|
227
|
+
columnCandidates.add(column.databaseName);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
entityMetadata?.columns
|
|
232
|
+
.filter((column) => column.propertyName === field.name)
|
|
233
|
+
.forEach((column) => {
|
|
234
|
+
if (column.databaseName) {
|
|
235
|
+
columnCandidates.add(column.databaseName);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
if (field.columnName) {
|
|
240
|
+
columnCandidates.add(field.columnName);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (field.relationCoModelColumnName) {
|
|
244
|
+
columnCandidates.add(field.relationCoModelColumnName);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (field.type === SolidFieldType.relation && field.relationType === RelationType.manyToOne) {
|
|
248
|
+
columnCandidates.add(`${snakeCase(field.name)}_id`);
|
|
249
|
+
} else {
|
|
250
|
+
columnCandidates.add(snakeCase(field.name));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return [...columnCandidates].filter(Boolean);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private async resolveDataSource(dataSourceName?: string): Promise<DataSource> {
|
|
257
|
+
const normalizedDataSourceName = dataSourceName && dataSourceName !== "default" ? dataSourceName : undefined;
|
|
258
|
+
const token = normalizedDataSourceName ? getDataSourceToken(normalizedDataSourceName) : getDataSourceToken();
|
|
259
|
+
let dataSource: DataSource | undefined;
|
|
260
|
+
|
|
261
|
+
try {
|
|
262
|
+
dataSource = this.moduleRef.get<DataSource>(token, { strict: false });
|
|
263
|
+
} catch (error: any) {
|
|
264
|
+
throw new NotFoundException(`Datasource "${normalizedDataSourceName ?? "default"}" could not be resolved: ${error?.message ?? error}`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (!dataSource) {
|
|
268
|
+
throw new NotFoundException(`Datasource "${normalizedDataSourceName ?? "default"}" could not be resolved.`);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (!dataSource.isInitialized) {
|
|
272
|
+
await dataSource.initialize();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return dataSource;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
private resolveEntityMetadata(dataSource: DataSource, model: ModelMetadata): EntityMetadata | undefined {
|
|
279
|
+
const candidates = [classify(model.singularName), model.singularName, model.tableName].filter(Boolean);
|
|
280
|
+
|
|
281
|
+
for (const candidate of candidates) {
|
|
282
|
+
try {
|
|
283
|
+
return dataSource.getMetadata(candidate);
|
|
284
|
+
} catch {
|
|
285
|
+
// Try the next candidate.
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return dataSource.entityMetadatas.find((metadata) => metadata.tableName === model.tableName);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
private async resolveMetadataFilePath(moduleName: string): Promise<string> {
|
|
293
|
+
const defaultPath = await this.moduleMetadataHelperService.getModuleMetadataFilePath(moduleName);
|
|
294
|
+
if (await this.fileExists(defaultPath)) {
|
|
295
|
+
return defaultPath;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const dashModuleName = kebabCase(moduleName);
|
|
299
|
+
const moduleMetadataFilePath = path.resolve(
|
|
300
|
+
process.cwd(),
|
|
301
|
+
"module-metadata",
|
|
302
|
+
dashModuleName,
|
|
303
|
+
`${dashModuleName}-metadata.json`,
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
if (await this.fileExists(moduleMetadataFilePath)) {
|
|
307
|
+
return moduleMetadataFilePath;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return defaultPath;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
private async fileExists(filePath: string): Promise<boolean> {
|
|
314
|
+
try {
|
|
315
|
+
await fs.access(filePath);
|
|
316
|
+
return true;
|
|
317
|
+
} catch {
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
private async loadTable(queryRunner: QueryRunner, tableName: string): Promise<Table | undefined> {
|
|
323
|
+
if (!tableName) {
|
|
324
|
+
return undefined;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const hasTable = await queryRunner.hasTable(tableName);
|
|
328
|
+
if (!hasTable) {
|
|
329
|
+
return undefined;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return queryRunner.getTable(tableName) ?? undefined;
|
|
333
|
+
}
|
|
334
|
+
}
|
package/src/solid-core.module.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { MulterModule } from "@nestjs/platform-express";
|
|
14
14
|
import { TypeOrmModule } from "@nestjs/typeorm";
|
|
15
15
|
import { RemoveFieldsCommand } from "./commands/remove-fields.command";
|
|
16
|
+
import { MigrateRemovedFieldsCommand } from "./commands/migrate-removed-fields.command";
|
|
16
17
|
import { FieldMetadataController } from "./controllers/field-metadata.controller";
|
|
17
18
|
import { DashboardController } from "./controllers/dashboard.controller";
|
|
18
19
|
import { MediaStorageProviderMetadataController } from "./controllers/media-storage-provider-metadata.controller";
|
|
@@ -47,6 +48,7 @@ import { ListOfValuesService } from "./services/list-of-values.service";
|
|
|
47
48
|
import { MediaStorageProviderMetadataService } from "./services/media-storage-provider-metadata.service";
|
|
48
49
|
import { MediaService } from "./services/media.service";
|
|
49
50
|
import { ModelMetadataService } from "./services/model-metadata.service";
|
|
51
|
+
import { RemovedFieldMigrationService } from "./services/removed-field-migration.service";
|
|
50
52
|
import { ModuleMetadataExplorerService } from "./services/module-metadata-explorer.service";
|
|
51
53
|
import { ModuleMetadataService } from "./services/module-metadata.service";
|
|
52
54
|
import { ModulePackageService } from "./services/module-package.service";
|
|
@@ -545,6 +547,7 @@ import { PostgresDatasourceIntrospectionProviderService } from "./services/datas
|
|
|
545
547
|
FieldMetadataService,
|
|
546
548
|
DashboardRuntimeService,
|
|
547
549
|
RemoveFieldsCommand,
|
|
550
|
+
MigrateRemovedFieldsCommand,
|
|
548
551
|
RefreshModelCommand,
|
|
549
552
|
RefreshModuleCommand,
|
|
550
553
|
InfoCommand,
|
|
@@ -791,6 +794,7 @@ import { PostgresDatasourceIntrospectionProviderService } from "./services/datas
|
|
|
791
794
|
UserViewMetadataRepository,
|
|
792
795
|
ModelMetadataRepository,
|
|
793
796
|
ModuleMetadataRepository,
|
|
797
|
+
RemovedFieldMigrationService,
|
|
794
798
|
ActionMetadataRepository,
|
|
795
799
|
MediaStorageProviderMetadataRepository,
|
|
796
800
|
FixturesService,
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { Injectable, Logger, Scope } from '@nestjs/common';
|
|
2
|
+
import { ModelMetadataHelperService } from 'src/helpers/model-metadata-helper.service';
|
|
2
3
|
import { lowerFirst } from 'src/helpers/string.helper';
|
|
3
4
|
import { SolidRegistry } from 'src/helpers/solid-registry';
|
|
4
5
|
import { DataSource, EntityMetadata, EntitySubscriberInterface, InsertEvent, RemoveEvent, UpdateEvent } from 'typeorm';
|
|
5
6
|
import { AuditQueuePayload } from 'src/interfaces';
|
|
6
7
|
import { RequestContextService } from 'src/services/request-context.service';
|
|
7
8
|
import { PublisherFactory } from 'src/services/queues/publisher-factory.service';
|
|
9
|
+
const AUDIT_BEFORE_SNAPSHOT = '__auditBeforeSnapshot';
|
|
8
10
|
|
|
9
|
-
@Injectable({scope: Scope.TRANSIENT})
|
|
11
|
+
@Injectable({ scope: Scope.TRANSIENT })
|
|
10
12
|
export class AuditSubscriber implements EntitySubscriberInterface {
|
|
11
13
|
private readonly logger = new Logger(AuditSubscriber.name);
|
|
12
14
|
private dataSource: DataSource;
|
|
@@ -14,6 +16,7 @@ export class AuditSubscriber implements EntitySubscriberInterface {
|
|
|
14
16
|
private readonly publisherFactory: PublisherFactory<AuditQueuePayload>,
|
|
15
17
|
private readonly solidRegistry: SolidRegistry,
|
|
16
18
|
private readonly requestContextService: RequestContextService,
|
|
19
|
+
private readonly modelMetadataHelperService: ModelMetadataHelperService,
|
|
17
20
|
) { }
|
|
18
21
|
|
|
19
22
|
bindToDataSource(dataSource: DataSource) {
|
|
@@ -53,20 +56,82 @@ export class AuditSubscriber implements EntitySubscriberInterface {
|
|
|
53
56
|
|
|
54
57
|
async afterUpdate(event: UpdateEvent<any>) {
|
|
55
58
|
if (!this.shouldTrackAudit(event.metadata)) return;
|
|
59
|
+
const entityId = event.entity?.id ?? event.databaseEntity?.id ?? (event as any).entityId ?? null;
|
|
60
|
+
let before = event.databaseEntity ?? null;
|
|
61
|
+
let after = event.entity ?? null;
|
|
62
|
+
const updatedColumnNames = (event.updatedColumns ?? []).map(c => c.propertyName);
|
|
63
|
+
|
|
64
|
+
const auditResult = await this.prepareManyToManyAuditUpdateSnapshot(event, entityId, updatedColumnNames);
|
|
65
|
+
|
|
66
|
+
if (auditResult) {
|
|
67
|
+
before = auditResult.before;
|
|
68
|
+
after = auditResult.after;
|
|
69
|
+
}
|
|
70
|
+
|
|
56
71
|
this.enqueue(event, {
|
|
57
72
|
eventType: 'update',
|
|
58
73
|
modelName: event.metadata.name,
|
|
59
|
-
entityId:
|
|
74
|
+
entityId: entityId,
|
|
60
75
|
occurredAt: new Date().toISOString(),
|
|
61
|
-
after:
|
|
76
|
+
after: after,
|
|
62
77
|
// databaseEntity is only populated when the entity was fetched first (save() path).
|
|
63
78
|
// QueryBuilder update() leaves this undefined; postAuditMessageOnUpdate guards for it.
|
|
64
|
-
before:
|
|
65
|
-
updatedColumnNames:
|
|
79
|
+
before: before,
|
|
80
|
+
updatedColumnNames: updatedColumnNames,
|
|
66
81
|
userId: this.activeUserId(),
|
|
67
82
|
});
|
|
68
83
|
}
|
|
69
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Resolves before/after snapshots for audit-enabled many-to-many relations
|
|
87
|
+
* and updates changed relation field names for audit tracking.
|
|
88
|
+
*/
|
|
89
|
+
private async prepareManyToManyAuditUpdateSnapshot(event: UpdateEvent<any>, entityId: number | null, updatedColumnNames: string[]): Promise<{ before: any; after: any } | null> {
|
|
90
|
+
|
|
91
|
+
const auditRelationFields = (await this.modelMetadataHelperService.loadFieldHierarchy(lowerFirst(event.metadata.name))).filter(field =>
|
|
92
|
+
field.enableAuditTracking &&
|
|
93
|
+
field.type === 'relation' &&
|
|
94
|
+
field.relationType !== 'one-to-many'
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
if (!entityId || auditRelationFields.length === 0) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const relations: Record<string, boolean> = {};
|
|
102
|
+
|
|
103
|
+
auditRelationFields.forEach(field => {
|
|
104
|
+
relations[field.name] = true;
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const relationBefore = event.entity?.[AUDIT_BEFORE_SNAPSHOT] ?? null;
|
|
108
|
+
|
|
109
|
+
const relationAfter = await event.manager.getRepository(event.metadata.target as any).findOne({
|
|
110
|
+
where: { id: entityId } as any,
|
|
111
|
+
relations: relations as any,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
if (relationBefore && relationAfter) {
|
|
115
|
+
auditRelationFields.forEach(field => {
|
|
116
|
+
const oldIds = Array.isArray(relationBefore[field.name])
|
|
117
|
+
? relationBefore[field.name].map(item => item.id).sort()
|
|
118
|
+
: [];
|
|
119
|
+
|
|
120
|
+
const newIds = Array.isArray(relationAfter[field.name])
|
|
121
|
+
? relationAfter[field.name].map(item => item.id).sort()
|
|
122
|
+
: [];
|
|
123
|
+
|
|
124
|
+
if ((oldIds.length !== newIds.length || JSON.stringify(oldIds) !== JSON.stringify(newIds))
|
|
125
|
+
&& !updatedColumnNames.includes(field.name)) {
|
|
126
|
+
updatedColumnNames.push(field.name);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
before: relationBefore ?? event.databaseEntity ?? null,
|
|
132
|
+
after: relationAfter ?? event.entity ?? null,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
70
135
|
async afterRemove(event: RemoveEvent<any>) {
|
|
71
136
|
if (!this.shouldTrackAudit(event.metadata)) return;
|
|
72
137
|
this.enqueue(event, {
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Bash(node -e:*)",
|
|
5
|
-
"WebFetch(domain:docs.solidxai.com)",
|
|
6
|
-
"WebFetch(domain:github.com)",
|
|
7
|
-
"Read(//Users/oswald/projects/Solid_Starters/solidctl/**)",
|
|
8
|
-
"Read(//Users/oswald/projects/Solid_Starters/**)",
|
|
9
|
-
"Bash(find /Users/oswald/projects/Solid_Starters/solid-core-module -type d -name migration* -o -name *database*)"
|
|
10
|
-
],
|
|
11
|
-
"additionalDirectories": [
|
|
12
|
-
"/Users/oswald/projects/Solid_Starters/solidctl"
|
|
13
|
-
]
|
|
14
|
-
}
|
|
15
|
-
}
|
package/CLAUDE.md
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# This is the SolidX API project.
|
|
2
|
-
This is included as a dependency in consuming projects.
|
|
3
|
-
|
|
4
|
-
# SolidX UI
|
|
5
|
-
# The corresponding UI project is at the below path:
|
|
6
|
-
/Users/oswald/projects/Solid_Starters/solid-core-ui
|
|
7
|
-
|
|
8
|
-
# SolidX Docs
|
|
9
|
-
# The documentation project is at the below path:
|
|
10
|
-
/Users/oswald/projects/solidxai-docs
|
|
11
|
-
|
|
12
|
-
# SolidX Agent
|
|
13
|
-
# The agent project which has the skills for helping users configure, build and use SolidX is at the below path:
|
|
14
|
-
/Users/oswald/projects/Solid_Starters/agent
|
|
15
|
-
|
|
16
|
-
# SolidX Control Plane Commands
|
|
17
|
-
# The control plane commands for managing SolidX instances are in the below path:
|
|
18
|
-
/Users/oswald/projects/Solid_Starters/solidctl
|
|
19
|
-
|
|
20
|
-
# Testing reference project
|
|
21
|
-
# This is the reference library management project that is used for testing and development of SolidX features. It is located at the below path:
|
|
22
|
-
/Users/oswald/projects/Solid_Starters/solid-library-management
|
|
23
|
-
|
|
24
|
-
# Code Builder project
|
|
25
|
-
# This is the project that contains the code builder which generates code based on user input and templates. It is located at the below path:
|
|
26
|
-
/Users/oswald/projects/Solid_Starters/solid-code-builder
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"legacy-common-with-id.entity.d.ts","sourceRoot":"","sources":["../../src/entities/legacy-common-with-id.entity.ts"],"names":[],"mappings":"AAEA,OAAO,EAA8B,gCAAgC,EAAE,MAAM,wBAAwB,CAAC;AAEtG,8BACsB,iCAAkC,SAAQ,gCAAgC;IAI5F,EAAE,EAAE,MAAM,CAAA;CACb"}
|