@solidxai/core 0.1.11-beta.8 → 0.1.11
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/CHANGELOG.md +456 -0
- package/dist/controllers/datasource-introspection.controller.d.ts +8 -0
- package/dist/controllers/datasource-introspection.controller.d.ts.map +1 -1
- package/dist/controllers/media.controller.js +0 -2
- package/dist/controllers/media.controller.js.map +1 -1
- package/dist/helpers/module-metadata-helper.service.js +1 -1
- package/dist/helpers/module-metadata-helper.service.js.map +1 -1
- package/dist/helpers/solid-registry.d.ts +1 -2
- package/dist/helpers/solid-registry.d.ts.map +1 -1
- package/dist/helpers/solid-registry.js.map +1 -1
- package/dist/helpers/typeorm-db-helper.d.ts +1 -0
- package/dist/helpers/typeorm-db-helper.d.ts.map +1 -1
- package/dist/helpers/typeorm-db-helper.js +12 -0
- package/dist/helpers/typeorm-db-helper.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts +1 -0
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +62 -33
- package/dist/services/authentication.service.d.ts +2 -0
- package/dist/services/authentication.service.d.ts.map +1 -1
- package/dist/services/authentication.service.js +32 -11
- package/dist/services/authentication.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 +5 -6
- package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.js.map +1 -1
- package/dist/services/crud-helper.service.d.ts.map +1 -1
- package/dist/services/crud-helper.service.js +32 -12
- package/dist/services/crud-helper.service.js.map +1 -1
- package/dist/services/datasource-introspection.service.d.ts +10 -0
- package/dist/services/datasource-introspection.service.d.ts.map +1 -1
- package/dist/services/datasource-introspection.service.js +109 -17
- package/dist/services/datasource-introspection.service.js.map +1 -1
- package/dist/services/menu-item-metadata.service.d.ts.map +1 -1
- package/dist/services/menu-item-metadata.service.js +1 -0
- package/dist/services/menu-item-metadata.service.js.map +1 -1
- package/dist/services/model-metadata.service.d.ts +2 -0
- package/dist/services/model-metadata.service.d.ts.map +1 -1
- package/dist/services/model-metadata.service.js +49 -15
- 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 +0 -27
- package/dist/services/module-metadata.service.js.map +1 -1
- package/dist/services/settings/default-settings-provider.service.d.ts +212 -0
- package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
- package/dist/services/settings/default-settings-provider.service.js +106 -0
- package/dist/services/settings/default-settings-provider.service.js.map +1 -1
- package/dist/services/solid-ts-morph.service.d.ts +6 -0
- package/dist/services/solid-ts-morph.service.d.ts.map +1 -1
- package/dist/services/solid-ts-morph.service.js +43 -0
- package/dist/services/solid-ts-morph.service.js.map +1 -1
- package/dist/subscribers/audit.subscriber.d.ts +0 -1
- package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
- package/dist/subscribers/audit.subscriber.js +1 -10
- package/dist/subscribers/audit.subscriber.js.map +1 -1
- package/dist/subscribers/computed-entity-field.subscriber.d.ts +1 -1
- package/dist/subscribers/computed-entity-field.subscriber.d.ts.map +1 -1
- package/dist/subscribers/computed-entity-field.subscriber.js +11 -13
- package/dist/subscribers/computed-entity-field.subscriber.js.map +1 -1
- package/dist/subscribers/created-by-updated-by.subscriber.d.ts.map +1 -1
- package/dist/subscribers/created-by-updated-by.subscriber.js.map +1 -1
- package/dist/subscribers/field-metadata.subscriber.d.ts.map +1 -1
- package/dist/subscribers/field-metadata.subscriber.js.map +1 -1
- package/dist/subscribers/scheduled-job.subscriber.js +1 -1
- package/dist/subscribers/scheduled-job.subscriber.js.map +1 -1
- package/dist/subscribers/security-rule.subscriber.js +1 -1
- package/dist/subscribers/security-rule.subscriber.js.map +1 -1
- package/package.json +1 -1
- package/src/controllers/media.controller.ts +1 -1
- package/src/helpers/module-metadata-helper.service.ts +1 -1
- package/src/helpers/solid-registry.ts +1 -7
- package/src/helpers/typeorm-db-helper.ts +15 -0
- package/src/index.ts +0 -1
- package/src/interfaces.ts +1 -0
- package/src/seeders/seed-data/solid-core-metadata.json +62 -33
- package/src/services/authentication.service.ts +46 -12
- package/src/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.ts +5 -10
- package/src/services/crud-helper.service.ts +32 -12
- package/src/services/datasource-introspection.service.ts +176 -16
- package/src/services/menu-item-metadata.service.ts +2 -1
- package/src/services/model-metadata.service.ts +63 -21
- package/src/services/module-metadata.service.ts +0 -35
- package/src/services/settings/default-settings-provider.service.ts +106 -0
- package/src/services/solid-ts-morph.service.ts +61 -1
- package/src/subscribers/audit.subscriber.ts +1 -12
- package/src/subscribers/computed-entity-field.subscriber.ts +23 -29
- package/src/subscribers/created-by-updated-by.subscriber.ts +0 -3
- package/src/subscribers/field-metadata.subscriber.ts +0 -3
- package/src/subscribers/scheduled-job.subscriber.ts +1 -1
- package/src/subscribers/security-rule.subscriber.ts +1 -1
- package/dist/helpers/nodemon-heartbeat.d.ts +0 -5
- package/dist/helpers/nodemon-heartbeat.d.ts.map +0 -1
- package/dist/helpers/nodemon-heartbeat.js +0 -67
- package/dist/helpers/nodemon-heartbeat.js.map +0 -1
- package/src/helpers/nodemon-heartbeat.ts +0 -59
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Injectable, Logger } from "@nestjs/common";
|
|
3
3
|
import { join, dirname, normalize, isAbsolute, basename } from "node:path";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
-
import { Project, Node, ObjectLiteralExpression, ArrayLiteralExpression, QuoteKind, IndentationText } from "ts-morph";
|
|
5
|
+
import { Project, Node, ObjectLiteralExpression, ArrayLiteralExpression, QuoteKind, IndentationText, SyntaxKind } from "ts-morph";
|
|
6
6
|
import { MethodDeclarationStructure, StructureKind } from "ts-morph";
|
|
7
7
|
|
|
8
8
|
type Bucket = "imports" | "providers" | "exports";
|
|
@@ -468,6 +468,66 @@ export class SolidTsMorphService {
|
|
|
468
468
|
return { staged: true, overwritten: false, skipped: false };
|
|
469
469
|
}
|
|
470
470
|
|
|
471
|
+
cleanupTypeormDatasourceEntity(
|
|
472
|
+
filePath: string,
|
|
473
|
+
entityImportPath: string,
|
|
474
|
+
entityClassName: string,
|
|
475
|
+
): { staged: boolean; skipped: boolean; removedImport: boolean; removedEntity: boolean } {
|
|
476
|
+
const abs = this.resolveRepoPath(filePath);
|
|
477
|
+
if (!existsSync(abs)) {
|
|
478
|
+
this.logger.warn(`cleanupTypeormDatasourceEntity: file not found at ${filePath}, skipping.`);
|
|
479
|
+
return { staged: false, skipped: true, removedImport: false, removedEntity: false };
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const existing = this.project.getSourceFile(abs);
|
|
483
|
+
const sourceFile = existing
|
|
484
|
+
? existing
|
|
485
|
+
: this.project.createSourceFile(abs, readFileSync(abs, "utf8"), { overwrite: true });
|
|
486
|
+
|
|
487
|
+
let removedImport = false;
|
|
488
|
+
let removedEntity = false;
|
|
489
|
+
|
|
490
|
+
const importDeclaration = sourceFile.getImportDeclarations().find((declaration) =>
|
|
491
|
+
declaration.getModuleSpecifierValue().replace(/\\/g, "/") === entityImportPath.replace(/\\/g, "/"),
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
if (importDeclaration) {
|
|
495
|
+
const namedImport = importDeclaration.getNamedImports().find((named) => named.getName() === entityClassName);
|
|
496
|
+
if (namedImport) {
|
|
497
|
+
namedImport.remove();
|
|
498
|
+
removedImport = true;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (
|
|
502
|
+
importDeclaration.getNamedImports().length === 0 &&
|
|
503
|
+
!importDeclaration.getDefaultImport() &&
|
|
504
|
+
!importDeclaration.getNamespaceImport()
|
|
505
|
+
) {
|
|
506
|
+
importDeclaration.remove();
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const entitiesDeclaration = sourceFile.getVariableDeclaration("entities");
|
|
511
|
+
const entitiesArray = entitiesDeclaration?.getInitializerIfKind(SyntaxKind.ArrayLiteralExpression);
|
|
512
|
+
if (entitiesArray) {
|
|
513
|
+
const elements = entitiesArray.getElements();
|
|
514
|
+
for (let i = elements.length - 1; i >= 0; i--) {
|
|
515
|
+
if (elements[i].getText().replace(/\s+/g, "") === entityClassName) {
|
|
516
|
+
entitiesArray.removeElement(i);
|
|
517
|
+
removedEntity = true;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (!removedImport && !removedEntity) {
|
|
523
|
+
return { staged: false, skipped: true, removedImport, removedEntity };
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
this.dirtySourceFiles.add(abs);
|
|
527
|
+
this.logger.log(`Staged datasource cleanup in ${this.rel(abs)} for entity ${entityClassName}`);
|
|
528
|
+
return { staged: true, skipped: false, removedImport, removedEntity };
|
|
529
|
+
}
|
|
530
|
+
|
|
471
531
|
|
|
472
532
|
|
|
473
533
|
}
|
|
@@ -106,7 +106,7 @@ export class AuditSubscriber implements EntitySubscriberInterface {
|
|
|
106
106
|
|
|
107
107
|
const relationBefore = event.entity?.[AUDIT_BEFORE_SNAPSHOT] ?? null;
|
|
108
108
|
|
|
109
|
-
const relationAfter = await
|
|
109
|
+
const relationAfter = await this.dataSource.getRepository(event.metadata.target as any).findOne({
|
|
110
110
|
where: { id: entityId } as any,
|
|
111
111
|
relations: relations as any,
|
|
112
112
|
});
|
|
@@ -151,11 +151,6 @@ export class AuditSubscriber implements EntitySubscriberInterface {
|
|
|
151
151
|
|
|
152
152
|
if (batch.length === 0) return;
|
|
153
153
|
|
|
154
|
-
if (this.shouldDeferAuditPublish()) {
|
|
155
|
-
setImmediate(() => void this.publishAuditBatch(batch));
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
154
|
await this.publishAuditBatch(batch);
|
|
160
155
|
}
|
|
161
156
|
|
|
@@ -164,12 +159,6 @@ export class AuditSubscriber implements EntitySubscriberInterface {
|
|
|
164
159
|
this.perTxn.delete(event.queryRunner);
|
|
165
160
|
}
|
|
166
161
|
|
|
167
|
-
private shouldDeferAuditPublish(): boolean {
|
|
168
|
-
const driver = (process.env.DEFAULT_DATABASE_DRIVER ?? '').toLowerCase();
|
|
169
|
-
const poolMax = Number(process.env.DEFAULT_DATABASE_POOL_MAX ?? 20);
|
|
170
|
-
return driver === 'pglite' && Number.isFinite(poolMax) && poolMax <= 1;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
162
|
private async publishAuditBatch(batch: AuditQueuePayload[]) {
|
|
174
163
|
try {
|
|
175
164
|
// allSettled: publish in parallel; a single failure does not block the rest.
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { camelCase } from 'lodash';
|
|
2
|
-
import { Delete } from "@aws-sdk/client-s3";
|
|
3
2
|
import { forwardRef, Inject, Injectable, InternalServerErrorException, Logger, Scope } from "@nestjs/common";
|
|
4
|
-
import { model } from "mongoose";
|
|
5
3
|
import { ComputedFieldTriggerOperation } from "src/dtos/create-field-metadata.dto";
|
|
6
4
|
import { ComputedFieldMetadata, SolidRegistry, TypeOrmEventContext } from "src/helpers/solid-registry";
|
|
7
5
|
import { IEntityPreComputeFieldProvider } from "src/interfaces";
|
|
@@ -14,19 +12,15 @@ export interface ComputedFieldEvaluationPayload extends ComputedFieldMetadata {
|
|
|
14
12
|
}
|
|
15
13
|
|
|
16
14
|
@Injectable({ scope: Scope.TRANSIENT })
|
|
17
|
-
// @EventSubscriber()
|
|
18
15
|
export class ComputedEntityFieldSubscriber implements EntitySubscriberInterface {
|
|
19
16
|
private readonly logger = new Logger(this.constructor.name);
|
|
20
17
|
private dataSource: DataSource;
|
|
18
|
+
|
|
21
19
|
constructor(
|
|
22
|
-
// @InjectDataSource()
|
|
23
|
-
// private readonly dataSource: DataSource,
|
|
24
20
|
private readonly solidRegistry: SolidRegistry,
|
|
25
21
|
@Inject(forwardRef(() => PublisherFactory))
|
|
26
22
|
private readonly publisherFactory: PublisherFactory<ComputedFieldEvaluationPayload>
|
|
27
|
-
// private readonly computedFieldPublisher: ComputedFieldEvaluationPublisherDatabase,
|
|
28
23
|
) {
|
|
29
|
-
// this.dataSource.subscribers.push(this);
|
|
30
24
|
}
|
|
31
25
|
|
|
32
26
|
bindToDataSource(dataSource: DataSource) {
|
|
@@ -43,47 +37,54 @@ export class ComputedEntityFieldSubscriber implements EntitySubscriberInterface
|
|
|
43
37
|
async beforeUpdate(event: UpdateEvent<any>): Promise<any> {
|
|
44
38
|
const modelName = camelCase(event.metadata?.name ?? event.entity?.constructor?.name ?? '');
|
|
45
39
|
const eventContext = this.sanitizeEventContext(event, 'beforeUpdate');
|
|
46
|
-
// await this.handleComputedFieldEvaluation(event.databaseEntity, ComputedFieldTriggerOperation.beforeUpdate, modelName, eventContext);
|
|
47
40
|
await this.handleComputedFieldEvaluation(event.entity, ComputedFieldTriggerOperation.beforeUpdate, modelName, eventContext);
|
|
48
41
|
}
|
|
49
42
|
|
|
50
43
|
afterInsert(event: InsertEvent<any>) {
|
|
51
44
|
const modelName = camelCase(event.metadata?.name ?? event.entity?.constructor?.name ?? '');
|
|
52
45
|
const eventContext = this.sanitizeEventContext(event, 'afterInsert');
|
|
53
|
-
this.
|
|
46
|
+
this.handlePostEventJobs(event, event.entity, ComputedFieldTriggerOperation.afterInsert, modelName, eventContext);
|
|
54
47
|
}
|
|
55
48
|
|
|
56
49
|
afterUpdate(event: UpdateEvent<any>) {
|
|
57
50
|
const modelName = camelCase(event.metadata?.name ?? event.entity?.constructor?.name ?? event.databaseEntity?.constructor?.name ?? '');
|
|
58
51
|
const eventContext = this.sanitizeEventContext(event, 'afterUpdate');
|
|
59
|
-
|
|
60
|
-
this.handleComputedFieldEvaluationJob(event.entity, ComputedFieldTriggerOperation.afterUpdate, modelName, eventContext);
|
|
52
|
+
this.handlePostEventJobs(event, event.entity, ComputedFieldTriggerOperation.afterUpdate, modelName, eventContext);
|
|
61
53
|
}
|
|
62
54
|
|
|
63
55
|
afterRemove(event: RemoveEvent<any>) {
|
|
64
56
|
const modelName = camelCase(event.metadata?.name ?? event.entity?.constructor?.name ?? event.databaseEntity?.constructor?.name ?? '');
|
|
65
57
|
const eventContext = this.sanitizeEventContext(event, 'afterRemove');
|
|
66
|
-
this.
|
|
58
|
+
this.handlePostEventJobs(event, event.databaseEntity, ComputedFieldTriggerOperation.afterRemove, modelName, eventContext);
|
|
67
59
|
}
|
|
68
60
|
|
|
69
61
|
//FIXME: Need to add support for beforeRemove, beforeSoftRemove, afterSoftRemove, beforeRecover, afterRecover
|
|
70
62
|
|
|
71
|
-
private
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
63
|
+
private handlePostEventJobs(
|
|
64
|
+
_event: InsertEvent<any> | UpdateEvent<any> | RemoveEvent<any>,
|
|
65
|
+
entity: any,
|
|
66
|
+
currentOperation: ComputedFieldTriggerOperation,
|
|
67
|
+
modelName: string,
|
|
68
|
+
eventContext?: TypeOrmEventContext,
|
|
69
|
+
) {
|
|
70
|
+
if (!entity) return;
|
|
71
|
+
|
|
75
72
|
const computedFieldsTobeEvaluated = this.getComputedFieldsForEvaluation(
|
|
76
73
|
this.solidRegistry.getComputedFieldMetadata(),
|
|
77
74
|
currentOperation,
|
|
78
75
|
modelName
|
|
79
76
|
);
|
|
80
|
-
|
|
77
|
+
if (computedFieldsTobeEvaluated.length === 0) return;
|
|
78
|
+
|
|
81
79
|
for (const computedField of computedFieldsTobeEvaluated) {
|
|
82
|
-
|
|
80
|
+
this.enqueueComputedFieldEvaluationJob(
|
|
81
|
+
this.attachContext(computedField, eventContext),
|
|
82
|
+
entity,
|
|
83
|
+
);
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
private
|
|
87
|
+
private async handleComputedFieldEvaluation(entity: any, currentOperation: ComputedFieldTriggerOperation, modelName: string, eventContext?: TypeOrmEventContext): Promise<void> {
|
|
87
88
|
if (!entity) {
|
|
88
89
|
return;
|
|
89
90
|
}
|
|
@@ -92,9 +93,8 @@ export class ComputedEntityFieldSubscriber implements EntitySubscriberInterface
|
|
|
92
93
|
currentOperation,
|
|
93
94
|
modelName
|
|
94
95
|
);
|
|
95
|
-
//TODO: We can add a feature i.e dependsOn, where we can check if the computed field depends on other computed fields and evaluate them first
|
|
96
96
|
for (const computedField of computedFieldsTobeEvaluated) {
|
|
97
|
-
this.
|
|
97
|
+
await this.evaluateComputedField(this.attachContext(computedField, eventContext), entity, currentOperation);
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -147,17 +147,12 @@ export class ComputedEntityFieldSubscriber implements EntitySubscriberInterface
|
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
private enqueueComputedFieldEvaluationJob(computedField: ComputedFieldMetadata<any>, databaseEntity: any
|
|
151
|
-
const { manager: _manager, ...serializableEventContext } = computedField.eventContext ?? {};
|
|
150
|
+
private enqueueComputedFieldEvaluationJob(computedField: ComputedFieldMetadata<any>, databaseEntity: any) {
|
|
152
151
|
const payload = {
|
|
153
152
|
...computedField,
|
|
154
|
-
eventContext: serializableEventContext,
|
|
155
153
|
databaseEntity,
|
|
156
154
|
};
|
|
157
|
-
this.publisherFactory.publish({ payload }, 'ComputedFieldEvaluationPublisher')
|
|
158
|
-
// this.computedFieldPublisher.publish({
|
|
159
|
-
// payload
|
|
160
|
-
// });
|
|
155
|
+
this.publisherFactory.publish({ payload }, 'ComputedFieldEvaluationPublisher');
|
|
161
156
|
}
|
|
162
157
|
|
|
163
158
|
private attachContext<T extends ComputedFieldMetadata<any>>(computedField: T, eventContext?: any): T {
|
|
@@ -176,7 +171,6 @@ export class ComputedEntityFieldSubscriber implements EntitySubscriberInterface
|
|
|
176
171
|
const base: TypeOrmEventContext = {
|
|
177
172
|
metadataName: event.metadata?.name,
|
|
178
173
|
eventType: eventType,
|
|
179
|
-
manager: event.manager,
|
|
180
174
|
};
|
|
181
175
|
if ("entityId" in event && event.entityId) {
|
|
182
176
|
base.entityId = event.entityId;
|
|
@@ -38,9 +38,6 @@ export class CreatedByUpdatedBySubscriber implements EntitySubscriberInterface {
|
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
// NOTE: If re-enabling loadUser below, query via `event.queryRunner.manager`, NOT
|
|
42
|
-
// `this.defaultDataSource.getRepository(...)`. A second connection opened mid-transaction
|
|
43
|
-
// deadlocks single-threaded engines (PGlite).
|
|
44
41
|
// const loadedUser = await this.loadUser(activeUserOrUndefined as unknown as ActiveUserData);
|
|
45
42
|
// if (isInsert) {
|
|
46
43
|
// event.entity.createdBy = loadedUser?.id;
|
|
@@ -21,9 +21,6 @@ export class FieldMetadataSubscriber implements EntitySubscriberInterface<FieldM
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
async afterUpdate(event: UpdateEvent<FieldMetadata>): Promise<void> {
|
|
24
|
-
// NOTE: If re-enabling the block below, use `event.queryRunner.manager` (the active
|
|
25
|
-
// transaction's connection), NOT `this.dataSource.getRepository(...)`. A second
|
|
26
|
-
// connection opened here mid-transaction deadlocks single-threaded engines (PGlite).
|
|
27
24
|
// If a field of type relation.many-to-one is marked for removal, and this field had an inverse relation created
|
|
28
25
|
// It means there is a field of type relation.one-to-many in the co-model, which needs to be removed
|
|
29
26
|
// if (event.entity && event.entity.isMarkedForRemoval && event.entity.type === 'relation' && event.entity.relationType === 'many-to-one' && event.entity.relationCreateInverse === true) {
|
|
@@ -83,7 +83,7 @@ export class ScheduledJobSubscriber
|
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
const moduleMetadataRepo =
|
|
86
|
+
const moduleMetadataRepo = this.dataSource.getRepository(ModuleMetadata);
|
|
87
87
|
const populatedModuleMetadata = await moduleMetadataRepo.findOne({
|
|
88
88
|
where: { id: moduleMetadata.id },
|
|
89
89
|
});
|
|
@@ -40,7 +40,7 @@ export class SecurityRuleSubscriber implements EntitySubscriberInterface<Securit
|
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const modelMetadataRepo =
|
|
43
|
+
const modelMetadataRepo = this.dataSource.getRepository(ModelMetadata);
|
|
44
44
|
const populatedModelMetadata = await modelMetadataRepo.findOne({
|
|
45
45
|
where: {
|
|
46
46
|
id: modelMetadata.id
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"nodemon-heartbeat.d.ts","sourceRoot":"","sources":["../../src/helpers/nodemon-heartbeat.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB;AASD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,sBAAsB,CA8B5E"}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.startNodemonHeartbeat = startNodemonHeartbeat;
|
|
37
|
-
const fsSync = __importStar(require("fs"));
|
|
38
|
-
const path = __importStar(require("path"));
|
|
39
|
-
const HEARTBEAT_FILENAME = 'solidx-nodemon-heartbeat.json';
|
|
40
|
-
const HEARTBEAT_INTERVAL_MS = 1500;
|
|
41
|
-
function startNodemonHeartbeat(srcDir) {
|
|
42
|
-
const filePath = path.join(srcDir, HEARTBEAT_FILENAME);
|
|
43
|
-
let stopped = false;
|
|
44
|
-
const touch = () => {
|
|
45
|
-
try {
|
|
46
|
-
fsSync.writeFileSync(filePath, String(Date.now()));
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
touch();
|
|
52
|
-
const timer = setInterval(touch, HEARTBEAT_INTERVAL_MS);
|
|
53
|
-
return {
|
|
54
|
-
stop() {
|
|
55
|
-
if (stopped)
|
|
56
|
-
return;
|
|
57
|
-
stopped = true;
|
|
58
|
-
clearInterval(timer);
|
|
59
|
-
try {
|
|
60
|
-
fsSync.unlinkSync(filePath);
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
//# sourceMappingURL=nodemon-heartbeat.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"nodemon-heartbeat.js","sourceRoot":"","sources":["../../src/helpers/nodemon-heartbeat.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,sDA8BC;AA1DD,2CAA6B;AAC7B,2CAA6B;AAa7B,MAAM,kBAAkB,GAAG,+BAA+B,CAAC;AAC3D,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAanC,SAAgB,qBAAqB,CAAC,MAAc;IAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACvD,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,MAAM,KAAK,GAAG,GAAG,EAAE;QACjB,IAAI,CAAC;YACH,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrD,CAAC;QAAC,MAAM,CAAC;QAGT,CAAC;IACH,CAAC,CAAC;IAGF,KAAK,EAAE,CAAC;IAER,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAC;IAExD,OAAO;QACL,IAAI;YACF,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,aAAa,CAAC,KAAK,CAAC,CAAC;YACrB,IAAI,CAAC;gBACH,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAC9B,CAAC;YAAC,MAAM,CAAC;YAET,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import * as fsSync from 'fs';\nimport * as path from 'path';\n\n/**\n * The heartbeat file is placed inside the nodemon-watched src/ directory so\n * that touching it resets nodemon's restart delay. This prevents nodemon from\n * restarting the API server while in-process code generation is writing files\n * (which is especially important for embedded PGlite projects where the\n * generate-code endpoint runs schematics in-process).\n *\n * The file uses a .json extension because nodemon is configured to watch\n * ts,json files. It is placed at the src/ root (not inside any metadata\n * directory) so it is not caught by the metadata JSON ignore rule.\n */\nconst HEARTBEAT_FILENAME = 'solidx-nodemon-heartbeat.json';\nconst HEARTBEAT_INTERVAL_MS = 1500;\n\nexport interface NodemonHeartbeatHandle {\n stop: () => void;\n}\n\n/**\n * Starts a heartbeat that touches a file in the nodemon-watched directory\n * every ~1.5 s. Call `stop()` after the response has been flushed to allow\n * nodemon to restart.\n *\n * @param srcDir Absolute path to the `src/` directory nodemon watches.\n */\nexport function startNodemonHeartbeat(srcDir: string): NodemonHeartbeatHandle {\n const filePath = path.join(srcDir, HEARTBEAT_FILENAME);\n let stopped = false;\n\n const touch = () => {\n try {\n fsSync.writeFileSync(filePath, String(Date.now()));\n } catch {\n // best-effort — if the file can't be written, nodemon will simply\n // restart as usual, which is the safe fallback.\n }\n };\n\n // Write immediately so the delay resets before any schematic output lands.\n touch();\n\n const timer = setInterval(touch, HEARTBEAT_INTERVAL_MS);\n\n return {\n stop() {\n if (stopped) return;\n stopped = true;\n clearInterval(timer);\n try {\n fsSync.unlinkSync(filePath);\n } catch {\n // file may not exist — ignore\n }\n },\n };\n}\n"]}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import * as fsSync from 'fs';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* The heartbeat file is placed inside the nodemon-watched src/ directory so
|
|
6
|
-
* that touching it resets nodemon's restart delay. This prevents nodemon from
|
|
7
|
-
* restarting the API server while in-process code generation is writing files
|
|
8
|
-
* (which is especially important for embedded PGlite projects where the
|
|
9
|
-
* generate-code endpoint runs schematics in-process).
|
|
10
|
-
*
|
|
11
|
-
* The file uses a .json extension because nodemon is configured to watch
|
|
12
|
-
* ts,json files. It is placed at the src/ root (not inside any metadata
|
|
13
|
-
* directory) so it is not caught by the metadata JSON ignore rule.
|
|
14
|
-
*/
|
|
15
|
-
const HEARTBEAT_FILENAME = 'solidx-nodemon-heartbeat.json';
|
|
16
|
-
const HEARTBEAT_INTERVAL_MS = 1500;
|
|
17
|
-
|
|
18
|
-
export interface NodemonHeartbeatHandle {
|
|
19
|
-
stop: () => void;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Starts a heartbeat that touches a file in the nodemon-watched directory
|
|
24
|
-
* every ~1.5 s. Call `stop()` after the response has been flushed to allow
|
|
25
|
-
* nodemon to restart.
|
|
26
|
-
*
|
|
27
|
-
* @param srcDir Absolute path to the `src/` directory nodemon watches.
|
|
28
|
-
*/
|
|
29
|
-
export function startNodemonHeartbeat(srcDir: string): NodemonHeartbeatHandle {
|
|
30
|
-
const filePath = path.join(srcDir, HEARTBEAT_FILENAME);
|
|
31
|
-
let stopped = false;
|
|
32
|
-
|
|
33
|
-
const touch = () => {
|
|
34
|
-
try {
|
|
35
|
-
fsSync.writeFileSync(filePath, String(Date.now()));
|
|
36
|
-
} catch {
|
|
37
|
-
// best-effort — if the file can't be written, nodemon will simply
|
|
38
|
-
// restart as usual, which is the safe fallback.
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
// Write immediately so the delay resets before any schematic output lands.
|
|
43
|
-
touch();
|
|
44
|
-
|
|
45
|
-
const timer = setInterval(touch, HEARTBEAT_INTERVAL_MS);
|
|
46
|
-
|
|
47
|
-
return {
|
|
48
|
-
stop() {
|
|
49
|
-
if (stopped) return;
|
|
50
|
-
stopped = true;
|
|
51
|
-
clearInterval(timer);
|
|
52
|
-
try {
|
|
53
|
-
fsSync.unlinkSync(filePath);
|
|
54
|
-
} catch {
|
|
55
|
-
// file may not exist — ignore
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
}
|