@solidxai/core 0.1.11-beta.12 → 0.1.11-beta.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/.claude/settings.local.json +16 -0
  2. package/CLAUDE.md +26 -0
  3. package/dist/controllers/media.controller.js +0 -2
  4. package/dist/controllers/media.controller.js.map +1 -1
  5. package/dist/helpers/environment.helper.d.ts +0 -1
  6. package/dist/helpers/environment.helper.d.ts.map +1 -1
  7. package/dist/helpers/environment.helper.js +0 -4
  8. package/dist/helpers/environment.helper.js.map +1 -1
  9. package/dist/helpers/model-metadata-helper.service.d.ts +1 -2
  10. package/dist/helpers/model-metadata-helper.service.d.ts.map +1 -1
  11. package/dist/helpers/model-metadata-helper.service.js +2 -6
  12. package/dist/helpers/model-metadata-helper.service.js.map +1 -1
  13. package/dist/helpers/solid-registry.d.ts +1 -2
  14. package/dist/helpers/solid-registry.d.ts.map +1 -1
  15. package/dist/helpers/solid-registry.js.map +1 -1
  16. package/dist/index.d.ts +0 -1
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +0 -1
  19. package/dist/index.js.map +1 -1
  20. package/dist/repository/scheduled-job.repository.d.ts +2 -2
  21. package/dist/repository/scheduled-job.repository.d.ts.map +1 -1
  22. package/dist/repository/scheduled-job.repository.js +3 -6
  23. package/dist/repository/scheduled-job.repository.js.map +1 -1
  24. package/dist/repository/security-rule.repository.d.ts +2 -2
  25. package/dist/repository/security-rule.repository.d.ts.map +1 -1
  26. package/dist/repository/security-rule.repository.js +3 -6
  27. package/dist/repository/security-rule.repository.js.map +1 -1
  28. package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.d.ts.map +1 -1
  29. package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.js +5 -7
  30. package/dist/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.js.map +1 -1
  31. package/dist/services/crud-helper.service.d.ts.map +1 -1
  32. package/dist/services/crud-helper.service.js +7 -0
  33. package/dist/services/crud-helper.service.js.map +1 -1
  34. package/dist/services/model-metadata.service.d.ts.map +1 -1
  35. package/dist/services/model-metadata.service.js +0 -16
  36. package/dist/services/model-metadata.service.js.map +1 -1
  37. package/dist/services/module-metadata.service.d.ts.map +1 -1
  38. package/dist/services/module-metadata.service.js +0 -28
  39. package/dist/services/module-metadata.service.js.map +1 -1
  40. package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
  41. package/dist/subscribers/audit.subscriber.js +2 -8
  42. package/dist/subscribers/audit.subscriber.js.map +1 -1
  43. package/dist/subscribers/computed-entity-field.subscriber.d.ts +0 -8
  44. package/dist/subscribers/computed-entity-field.subscriber.d.ts.map +1 -1
  45. package/dist/subscribers/computed-entity-field.subscriber.js +3 -36
  46. package/dist/subscribers/computed-entity-field.subscriber.js.map +1 -1
  47. package/dist/subscribers/created-by-updated-by.subscriber.d.ts.map +1 -1
  48. package/dist/subscribers/created-by-updated-by.subscriber.js.map +1 -1
  49. package/dist/subscribers/field-metadata.subscriber.d.ts.map +1 -1
  50. package/dist/subscribers/field-metadata.subscriber.js.map +1 -1
  51. package/dist/subscribers/scheduled-job.subscriber.d.ts.map +1 -1
  52. package/dist/subscribers/scheduled-job.subscriber.js +2 -3
  53. package/dist/subscribers/scheduled-job.subscriber.js.map +1 -1
  54. package/dist/subscribers/security-rule.subscriber.d.ts.map +1 -1
  55. package/dist/subscribers/security-rule.subscriber.js +3 -5
  56. package/dist/subscribers/security-rule.subscriber.js.map +1 -1
  57. package/package.json +1 -1
  58. package/src/controllers/media.controller.ts +1 -1
  59. package/src/helpers/environment.helper.ts +0 -11
  60. package/src/helpers/model-metadata-helper.service.ts +2 -7
  61. package/src/helpers/solid-registry.ts +1 -7
  62. package/src/index.ts +0 -1
  63. package/src/repository/scheduled-job.repository.ts +4 -10
  64. package/src/repository/security-rule.repository.ts +4 -10
  65. package/src/services/1.js +6 -0
  66. package/src/services/computed-fields/entity/alpha-num-external-id-computed-field-provider.ts +5 -11
  67. package/src/services/crud-helper.service.ts +7 -0
  68. package/src/services/model-metadata.service.ts +0 -22
  69. package/src/services/module-metadata.service.ts +0 -36
  70. package/src/subscribers/audit.subscriber.ts +2 -11
  71. package/src/subscribers/computed-entity-field.subscriber.ts +6 -63
  72. package/src/subscribers/created-by-updated-by.subscriber.ts +0 -3
  73. package/src/subscribers/field-metadata.subscriber.ts +0 -3
  74. package/src/subscribers/scheduled-job.subscriber.ts +2 -3
  75. package/src/subscribers/security-rule.subscriber.ts +3 -8
  76. package/dist/helpers/nodemon-heartbeat.d.ts +0 -5
  77. package/dist/helpers/nodemon-heartbeat.d.ts.map +0 -1
  78. package/dist/helpers/nodemon-heartbeat.js +0 -67
  79. package/dist/helpers/nodemon-heartbeat.js.map +0 -1
  80. package/dist-tests/api/authenticate.spec.js +0 -119
  81. package/dist-tests/api/authenticate.spec.js.map +0 -1
  82. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +0 -97
  83. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +0 -1
  84. package/dist-tests/api/ping.spec.js +0 -21
  85. package/dist-tests/api/ping.spec.js.map +0 -1
  86. package/dist-tests/helpers/auth.js +0 -41
  87. package/dist-tests/helpers/auth.js.map +0 -1
  88. package/dist-tests/helpers/env.js +0 -11
  89. package/dist-tests/helpers/env.js.map +0 -1
  90. package/docs/agent-hub-grooming.md +0 -301
  91. package/docs/dashboards/AGENTIC_DASHBOARD_IMPLEMENTATION_PLAN.md +0 -438
  92. package/docs/dashboards/dashboard-curl-smoke-tests.txt +0 -146
  93. package/docs/dashboards/delete-legacy-dashboard-metadata.sql +0 -172
  94. package/docs/datasource-introspection-ddl-analysis.md +0 -326
  95. package/docs/datasource-introspection-implementation-plan.md +0 -306
  96. package/docs/grouping-enhancements.md +0 -89
  97. package/docs/java-spring/README.md +0 -3
  98. package/docs/java-spring/solid-core-module-deep-dive-report.md +0 -1317
  99. package/docs/module-package-import-handoff.md +0 -691
  100. package/docs/seed-changes.md +0 -65
  101. package/docs/test-data-workflow.md +0 -200
  102. package/docs/type-declaration-import-issue.md +0 -24
  103. package/src/helpers/nodemon-heartbeat.ts +0 -59
@@ -1,7 +1,6 @@
1
1
  import { camelCase } from 'lodash';
2
2
  import { forwardRef, Inject, Injectable, InternalServerErrorException, Logger, Scope } from "@nestjs/common";
3
3
  import { ComputedFieldTriggerOperation } from "src/dtos/create-field-metadata.dto";
4
- import { isEmbeddedDb } from "src/helpers/environment.helper";
5
4
  import { ComputedFieldMetadata, SolidRegistry, TypeOrmEventContext } from "src/helpers/solid-registry";
6
5
  import { IEntityPreComputeFieldProvider } from "src/interfaces";
7
6
  import { PublisherFactory } from "src/services/queues/publisher-factory.service";
@@ -17,13 +16,6 @@ export class ComputedEntityFieldSubscriber implements EntitySubscriberInterface
17
16
  private readonly logger = new Logger(this.constructor.name);
18
17
  private dataSource: DataSource;
19
18
 
20
- // Per-transaction buffer for post-event evaluation jobs. Entries are
21
- // flushed in afterTransactionCommit so the publish (which uses the
22
- // default DataSource) never races the in-flight transaction's connection.
23
- // On PGlite (pool size 1), the flush is deferred via setImmediate to
24
- // ensure the transaction's connection has been released first.
25
- private perTxn = new WeakMap<any, { computedField: ComputedFieldMetadata<any>; databaseEntity: any }[]>();
26
-
27
19
  constructor(
28
20
  private readonly solidRegistry: SolidRegistry,
29
21
  @Inject(forwardRef(() => PublisherFactory))
@@ -68,18 +60,8 @@ export class ComputedEntityFieldSubscriber implements EntitySubscriberInterface
68
60
 
69
61
  //FIXME: Need to add support for beforeRemove, beforeSoftRemove, afterSoftRemove, beforeRecover, afterRecover
70
62
 
71
- // --------- post-event dispatch (pglite vs non-pglite) ----------
72
-
73
- /**
74
- * On embedded PGlite (pool size 1): buffer payloads on the queryRunner and
75
- * flush in afterTransactionCommit (deferred via setImmediate) so the publish
76
- * never tries to get a second connection while the transaction is active.
77
- *
78
- * On regular Postgres: fire-and-forget publish immediately (original behaviour),
79
- * since the pool has spare connections.
80
- */
81
63
  private handlePostEventJobs(
82
- event: InsertEvent<any> | UpdateEvent<any> | RemoveEvent<any>,
64
+ _event: InsertEvent<any> | UpdateEvent<any> | RemoveEvent<any>,
83
65
  entity: any,
84
66
  currentOperation: ComputedFieldTriggerOperation,
85
67
  modelName: string,
@@ -94,44 +76,11 @@ export class ComputedEntityFieldSubscriber implements EntitySubscriberInterface
94
76
  );
95
77
  if (computedFieldsTobeEvaluated.length === 0) return;
96
78
 
97
- if (isEmbeddedDb()) {
98
- const qr = event.queryRunner;
99
- const arr = this.perTxn.get(qr) ?? [];
100
- for (const computedField of computedFieldsTobeEvaluated) {
101
- arr.push({
102
- computedField: this.attachContext(computedField, eventContext),
103
- databaseEntity: entity,
104
- });
105
- }
106
- this.perTxn.set(qr, arr);
107
- } else {
108
- for (const computedField of computedFieldsTobeEvaluated) {
109
- this.enqueueComputedFieldEvaluationJob(
110
- this.attachContext(computedField, eventContext),
111
- entity,
112
- );
113
- }
114
- }
115
- }
116
-
117
- // --------- transaction lifecycle (pglite only) ----------
118
- async afterTransactionCommit(event: { queryRunner: any }) {
119
- const batch = this.perTxn.get(event.queryRunner) ?? [];
120
- this.perTxn.delete(event.queryRunner);
121
- if (batch.length === 0) return;
122
-
123
- // Deferred via setImmediate so the transaction's connection is released
124
- // back to the (size-1) pool before the publish tries to acquire it.
125
- setImmediate(() => void this.flushBatch(batch));
126
- }
127
-
128
- afterTransactionRollback(event: { queryRunner: any }) {
129
- this.perTxn.delete(event.queryRunner);
130
- }
131
-
132
- private async flushBatch(batch: { computedField: ComputedFieldMetadata<any>; databaseEntity: any }[]) {
133
- for (const { computedField, databaseEntity } of batch) {
134
- this.enqueueComputedFieldEvaluationJob(computedField, databaseEntity);
79
+ for (const computedField of computedFieldsTobeEvaluated) {
80
+ this.enqueueComputedFieldEvaluationJob(
81
+ this.attachContext(computedField, eventContext),
82
+ entity,
83
+ );
135
84
  }
136
85
  }
137
86
 
@@ -199,10 +148,8 @@ export class ComputedEntityFieldSubscriber implements EntitySubscriberInterface
199
148
  }
200
149
 
201
150
  private enqueueComputedFieldEvaluationJob(computedField: ComputedFieldMetadata<any>, databaseEntity: any) {
202
- const { manager: _manager, ...serializableEventContext } = computedField.eventContext ?? {};
203
151
  const payload = {
204
152
  ...computedField,
205
- eventContext: serializableEventContext,
206
153
  databaseEntity,
207
154
  };
208
155
  this.publisherFactory.publish({ payload }, 'ComputedFieldEvaluationPublisher');
@@ -224,10 +171,6 @@ export class ComputedEntityFieldSubscriber implements EntitySubscriberInterface
224
171
  const base: TypeOrmEventContext = {
225
172
  metadataName: event.metadata?.name,
226
173
  eventType: eventType,
227
- // Only pass the transaction's EntityManager for embedded DB (PGlite),
228
- // where a second pooled connection mid-transaction would deadlock.
229
- // On regular Postgres, providers use their injected EntityManager.
230
- ...(isEmbeddedDb() ? { manager: event.manager } : {}),
231
174
  };
232
175
  if ("entityId" in event && event.entityId) {
233
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) {
@@ -4,7 +4,6 @@ import * as fs from "fs/promises";
4
4
  import { CreateScheduledJobDto } from "src/dtos/create-scheduled-job.dto";
5
5
  import { ModuleMetadata } from "src/entities/module-metadata.entity";
6
6
  import { ScheduledJob } from "src/entities/scheduled-job.entity";
7
- import { isEmbeddedDb } from "src/helpers/environment.helper";
8
7
  import { ModuleMetadataHelperService } from "src/helpers/module-metadata-helper.service";
9
8
  import { ScheduledJobRepository } from "src/repository/scheduled-job.repository";
10
9
  import {
@@ -84,7 +83,7 @@ export class ScheduledJobSubscriber
84
83
  return;
85
84
  }
86
85
 
87
- const moduleMetadataRepo = (isEmbeddedDb() ? event.queryRunner.manager : this.dataSource).getRepository(ModuleMetadata);
86
+ const moduleMetadataRepo = this.dataSource.getRepository(ModuleMetadata);
88
87
  const populatedModuleMetadata = await moduleMetadataRepo.findOne({
89
88
  where: { id: moduleMetadata.id },
90
89
  });
@@ -161,7 +160,7 @@ export class ScheduledJobSubscriber
161
160
  (job) => job.scheduleName === jobName
162
161
  );
163
162
  // Insert or update job in metadata
164
- const jobDto: CreateScheduledJobDto = await this.scheduledJobRepo.toDto(populatedScheduleJob as ScheduledJob, isEmbeddedDb() ? entityManager : undefined);
163
+ const jobDto: CreateScheduledJobDto = await this.scheduledJobRepo.toDto(populatedScheduleJob as ScheduledJob);
165
164
  // Strip out bookkeeping/runtime fields before persisting to metadata JSON.
166
165
  const {
167
166
  moduleId,
@@ -4,7 +4,6 @@ import * as fs from 'fs/promises'; // Use the Promise-based version of fs for as
4
4
  import { SOLID_CORE_MODULE_NAME } from 'src/constants';
5
5
  import { ModelMetadata } from 'src/entities/model-metadata.entity';
6
6
  import { SecurityRule } from 'src/entities/security-rule.entity';
7
- import { isEmbeddedDb } from "src/helpers/environment.helper";
8
7
  import { ModuleMetadataHelperService } from "src/helpers/module-metadata-helper.service";
9
8
  import { SecurityRuleRepository } from 'src/repository/security-rule.repository';
10
9
  import { DataSource, EntitySubscriberInterface, InsertEvent, UpdateEvent } from "typeorm";
@@ -41,11 +40,7 @@ export class SecurityRuleSubscriber implements EntitySubscriberInterface<Securit
41
40
  return;
42
41
  }
43
42
 
44
- // On embedded PGlite, use the transaction's connection to avoid a
45
- // second pooled connection mid-transaction. On regular Postgres, use
46
- // the default DataSource (original behaviour).
47
- const mgr = isEmbeddedDb() ? event.manager : this.dataSource;
48
- const modelMetadataRepo = mgr.getRepository(ModelMetadata);
43
+ const modelMetadataRepo = this.dataSource.getRepository(ModelMetadata);
49
44
  const populatedModelMetadata = await modelMetadataRepo.findOne({
50
45
  where: {
51
46
  id: modelMetadata.id
@@ -72,12 +67,12 @@ export class SecurityRuleSubscriber implements EntitySubscriberInterface<Securit
72
67
 
73
68
  if (metaData.securityRules) {
74
69
  const securityRuleIndex = metaData.securityRules?.findIndex((ruleFromFile: { name: string }) => ruleFromFile.name === securityRule.name);
75
- const { id, roleId, modelMetadataId, ...requiredDto } = await this.securityRuleRepo.toDto(securityRule, isEmbeddedDb() ? event.manager : undefined)
70
+ const { id, roleId, modelMetadataId, ...requiredDto } = await this.securityRuleRepo.toDto(securityRule)
76
71
  metaData.securityRules[securityRuleIndex] = { ...requiredDto, securityRuleConfig: JSON.parse(securityRule.securityRuleConfig) }
77
72
  }
78
73
  else {
79
74
  const securityRules = []
80
- const { id, roleId, modelMetadataId, ...requiredDto } = await this.securityRuleRepo.toDto(securityRule, isEmbeddedDb() ? event.manager : undefined)
75
+ const { id, roleId, modelMetadataId, ...requiredDto } = await this.securityRuleRepo.toDto(securityRule)
81
76
  securityRules.push({ ...requiredDto, securityRuleConfig: JSON.parse(securityRule.securityRuleConfig) })
82
77
  metaData.securityRules = securityRules
83
78
  }
@@ -1,5 +0,0 @@
1
- export interface NodemonHeartbeatHandle {
2
- stop: () => void;
3
- }
4
- export declare function startNodemonHeartbeat(srcDir: string): NodemonHeartbeatHandle;
5
- //# sourceMappingURL=nodemon-heartbeat.d.ts.map
@@ -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,119 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const test_1 = require("@playwright/test");
4
- const env_1 = require("../helpers/env");
5
- const baseURL = process.env.API_BASE_URL ?? "http://localhost:3000";
6
- const TEST_USER_EMAIL = (0, env_1.getRequiredEnv)("TEST_USER_EMAIL");
7
- const TEST_USER_PASSWORD = (0, env_1.getRequiredEnv)("TEST_USER_PASSWORD");
8
- function base64UrlDecode(input) {
9
- const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
10
- const padded = normalized.length % 4 === 0
11
- ? normalized
12
- : normalized.padEnd(normalized.length + (4 - (normalized.length % 4)), "=");
13
- return Buffer.from(padded, "base64").toString("utf-8");
14
- }
15
- function validateJwt(token) {
16
- const parts = token.split(".");
17
- if (parts.length !== 3) {
18
- throw new Error("JWT must have three dot-separated parts.");
19
- }
20
- const headerJson = JSON.parse(base64UrlDecode(parts[0]));
21
- const payloadJson = JSON.parse(base64UrlDecode(parts[1]));
22
- if (!headerJson || typeof headerJson !== "object") {
23
- throw new Error("JWT header must be a JSON object.");
24
- }
25
- if (!payloadJson || typeof payloadJson !== "object") {
26
- throw new Error("JWT payload must be a JSON object.");
27
- }
28
- if (typeof payloadJson.exp !== "number") {
29
- throw new Error("JWT payload.exp must be a number.");
30
- }
31
- return payloadJson;
32
- }
33
- (0, test_1.test)("API: authenticate succeeds with valid credentials", async () => {
34
- const api = await test_1.request.newContext({
35
- baseURL,
36
- extraHTTPHeaders: {
37
- accept: "*/*",
38
- "content-type": "application/json",
39
- },
40
- });
41
- try {
42
- const res = await api.post("/api/iam/authenticate", {
43
- data: {
44
- email: TEST_USER_EMAIL,
45
- username: "",
46
- password: TEST_USER_PASSWORD,
47
- },
48
- });
49
- (0, test_1.expect)(res.status()).toBe(200);
50
- const json = await res.json();
51
- (0, test_1.expect)(json.statusCode).toBe(200);
52
- (0, test_1.expect)(Array.isArray(json.message)).toBe(true);
53
- (0, test_1.expect)(json.message.length).toBe(0);
54
- (0, test_1.expect)(json.error).toBe("");
55
- const user = json.data?.user;
56
- (0, test_1.expect)(user, "Expected data.user to be an object.").toBeTruthy();
57
- (0, test_1.expect)(typeof user).toBe("object");
58
- const email = user?.email;
59
- (0, test_1.expect)(typeof email).toBe("string");
60
- if (email === TEST_USER_EMAIL) {
61
- (0, test_1.expect)(email).toBe(TEST_USER_EMAIL);
62
- }
63
- else {
64
- (0, test_1.expect)(email.length).toBeGreaterThan(0);
65
- }
66
- (0, test_1.expect)(typeof user?.mobile).toBe("string");
67
- (0, test_1.expect)(typeof user?.username).toBe("string");
68
- (0, test_1.expect)(typeof user?.forcePasswordChange).toBe("boolean");
69
- (0, test_1.expect)(typeof user?.id).toBe("number");
70
- const roles = user?.roles;
71
- (0, test_1.expect)(Array.isArray(roles)).toBe(true);
72
- if (Array.isArray(roles)) {
73
- (0, test_1.expect)(roles.every((role) => typeof role === "string")).toBe(true);
74
- (0, test_1.expect)(roles).toContain("Admin");
75
- }
76
- const accessToken = json.data?.accessToken;
77
- const refreshToken = json.data?.refreshToken;
78
- (0, test_1.expect)(typeof accessToken).toBe("string");
79
- (0, test_1.expect)(typeof refreshToken).toBe("string");
80
- const accessPayload = validateJwt(accessToken);
81
- const refreshPayload = validateJwt(refreshToken);
82
- (0, test_1.expect)(typeof accessPayload.exp).toBe("number");
83
- (0, test_1.expect)(typeof refreshPayload.exp).toBe("number");
84
- }
85
- finally {
86
- await api.dispose();
87
- }
88
- });
89
- (0, test_1.test)("API: authenticate fails with wrong password", async () => {
90
- const api = await test_1.request.newContext({
91
- baseURL,
92
- extraHTTPHeaders: {
93
- accept: "*/*",
94
- "content-type": "application/json",
95
- },
96
- });
97
- try {
98
- const res = await api.post("/api/iam/authenticate", {
99
- data: {
100
- email: TEST_USER_EMAIL,
101
- username: "",
102
- password: `${TEST_USER_PASSWORD}__wrong`,
103
- },
104
- });
105
- (0, test_1.expect)(res.status()).toBe(401);
106
- const json = await res.json();
107
- (0, test_1.expect)(json.statusCode).toBe(401);
108
- (0, test_1.expect)(json.statusCodeMessage).toBe("Unauthorized");
109
- (0, test_1.expect)(json.message).toBe("Invalid credentials");
110
- (0, test_1.expect)(json.error).toBe("Invalid credentials");
111
- (0, test_1.expect)(json.data?.statusCode).toBe(401);
112
- (0, test_1.expect)(json.data?.error).toBe("Unauthorized");
113
- (0, test_1.expect)(json.data?.message).toBe("Invalid credentials");
114
- }
115
- finally {
116
- await api.dispose();
117
- }
118
- });
119
- //# sourceMappingURL=authenticate.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"authenticate.spec.js","sourceRoot":"","sources":["../../tests/api/authenticate.spec.ts"],"names":[],"mappings":";;AAAA,2CAAyD;AACzD,wCAAgD;AAOhD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,uBAAuB,CAAC;AACpE,MAAM,eAAe,GAAG,IAAA,oBAAc,EAAC,iBAAiB,CAAC,CAAC;AAC1D,MAAM,kBAAkB,GAAG,IAAA,oBAAc,EAAC,oBAAoB,CAAC,CAAC;AAEhE,SAAS,eAAe,CAAC,KAAa;IACpC,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC/D,MAAM,MAAM,GACV,UAAU,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC;QACzB,CAAC,CAAC,UAAU;QACZ,CAAC,CAAC,UAAU,CAAC,MAAM,CACjB,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EACjD,GAAG,CACJ,CAAC;IACN,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAE1D,IAAI,CAAC,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,IAAI,CAAC,WAAW,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACxD,CAAC;IAED,IAAI,OAAO,WAAW,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,OAAO,WAAyB,CAAC;AACnC,CAAC;AAED,IAAA,WAAI,EAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;IACnE,MAAM,GAAG,GAAG,MAAM,cAAO,CAAC,UAAU,CAAC;QACnC,OAAO;QACP,gBAAgB,EAAE;YAChB,MAAM,EAAE,KAAK;YACb,cAAc,EAAE,kBAAkB;SACnC;KACF,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,uBAAuB,EAAE;YAClD,IAAI,EAAE;gBACJ,KAAK,EAAE,eAAe;gBACtB,QAAQ,EAAE,EAAE;gBACZ,QAAQ,EAAE,kBAAkB;aAC7B;SACF,CAAC,CAAC;QAEH,IAAA,aAAM,EAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAE9B,IAAA,aAAM,EAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClC,IAAA,aAAM,EAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAA,aAAM,EAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpC,IAAA,aAAM,EAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAE5B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,IAA2C,CAAC;QACpE,IAAA,aAAM,EAAC,IAAI,EAAE,qCAAqC,CAAC,CAAC,UAAU,EAAE,CAAC;QACjE,IAAA,aAAM,EAAC,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEnC,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC;QAC1B,IAAA,aAAM,EAAC,OAAO,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpC,IAAI,KAAK,KAAK,eAAe,EAAE,CAAC;YAC9B,IAAA,aAAM,EAAC,KAAK,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACtC,CAAC;aAAM,CAAC;YAEN,IAAA,aAAM,EAAE,KAAgB,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACtD,CAAC;QAED,IAAA,aAAM,EAAC,OAAO,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3C,IAAA,aAAM,EAAC,OAAO,IAAI,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAA,aAAM,EAAC,OAAO,IAAI,EAAE,mBAAmB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzD,IAAA,aAAM,EAAC,OAAO,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEvC,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC;QAC1B,IAAA,aAAM,EAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,IAAA,aAAM,EAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnE,IAAA,aAAM,EAAC,KAAK,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACnC,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC;QAC3C,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC;QAC7C,IAAA,aAAM,EAAC,OAAO,WAAW,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAA,aAAM,EAAC,OAAO,YAAY,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAE3C,MAAM,aAAa,GAAG,WAAW,CAAC,WAAqB,CAAC,CAAC;QACzD,MAAM,cAAc,GAAG,WAAW,CAAC,YAAsB,CAAC,CAAC;QAE3D,IAAA,aAAM,EAAC,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAChD,IAAA,aAAM,EAAC,OAAO,cAAc,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACnD,CAAC;YAAS,CAAC;QACT,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;IACtB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAA,WAAI,EAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;IAC7D,MAAM,GAAG,GAAG,MAAM,cAAO,CAAC,UAAU,CAAC;QACnC,OAAO;QACP,gBAAgB,EAAE;YAChB,MAAM,EAAE,KAAK;YACb,cAAc,EAAE,kBAAkB;SACnC;KACF,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,uBAAuB,EAAE;YAClD,IAAI,EAAE;gBACJ,KAAK,EAAE,eAAe;gBACtB,QAAQ,EAAE,EAAE;gBACZ,QAAQ,EAAE,GAAG,kBAAkB,SAAS;aACzC;SACF,CAAC,CAAC;QAEH,IAAA,aAAM,EAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAE9B,IAAA,aAAM,EAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClC,IAAA,aAAM,EAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACpD,IAAA,aAAM,EAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;QACjD,IAAA,aAAM,EAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;QAC/C,IAAA,aAAM,EAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACxC,IAAA,aAAM,EAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC9C,IAAA,aAAM,EAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IACzD,CAAC;YAAS,CAAC;QACT,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;IACtB,CAAC;AACH,CAAC,CAAC,CAAC","sourcesContent":["import { expect, request, test } from \"@playwright/test\";\nimport { getRequiredEnv } from \"../helpers/env\";\n\ntype JwtPayload = {\n exp?: number;\n [key: string]: unknown;\n};\n\nconst baseURL = process.env.API_BASE_URL ?? \"http://localhost:3000\";\nconst TEST_USER_EMAIL = getRequiredEnv(\"TEST_USER_EMAIL\");\nconst TEST_USER_PASSWORD = getRequiredEnv(\"TEST_USER_PASSWORD\");\n\nfunction base64UrlDecode(input: string): string {\n const normalized = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded =\n normalized.length % 4 === 0\n ? normalized\n : normalized.padEnd(\n normalized.length + (4 - (normalized.length % 4)),\n \"=\"\n );\n return Buffer.from(padded, \"base64\").toString(\"utf-8\");\n}\n\nfunction validateJwt(token: string): JwtPayload {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"JWT must have three dot-separated parts.\");\n }\n\n const headerJson = JSON.parse(base64UrlDecode(parts[0]));\n const payloadJson = JSON.parse(base64UrlDecode(parts[1]));\n\n if (!headerJson || typeof headerJson !== \"object\") {\n throw new Error(\"JWT header must be a JSON object.\");\n }\n\n if (!payloadJson || typeof payloadJson !== \"object\") {\n throw new Error(\"JWT payload must be a JSON object.\");\n }\n\n if (typeof payloadJson.exp !== \"number\") {\n throw new Error(\"JWT payload.exp must be a number.\");\n }\n\n return payloadJson as JwtPayload;\n}\n\ntest(\"API: authenticate succeeds with valid credentials\", async () => {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: {\n accept: \"*/*\",\n \"content-type\": \"application/json\",\n },\n });\n\n try {\n const res = await api.post(\"/api/iam/authenticate\", {\n data: {\n email: TEST_USER_EMAIL,\n username: \"\",\n password: TEST_USER_PASSWORD,\n },\n });\n\n expect(res.status()).toBe(200);\n const json = await res.json();\n\n expect(json.statusCode).toBe(200);\n expect(Array.isArray(json.message)).toBe(true);\n expect(json.message.length).toBe(0);\n expect(json.error).toBe(\"\");\n\n const user = json.data?.user as Record<string, unknown> | undefined;\n expect(user, \"Expected data.user to be an object.\").toBeTruthy();\n expect(typeof user).toBe(\"object\");\n\n const email = user?.email;\n expect(typeof email).toBe(\"string\");\n if (email === TEST_USER_EMAIL) {\n expect(email).toBe(TEST_USER_EMAIL);\n } else {\n // If your API returns a fixed system email, replace this with an exact match.\n expect((email as string).length).toBeGreaterThan(0);\n }\n\n expect(typeof user?.mobile).toBe(\"string\");\n expect(typeof user?.username).toBe(\"string\");\n expect(typeof user?.forcePasswordChange).toBe(\"boolean\");\n expect(typeof user?.id).toBe(\"number\");\n\n const roles = user?.roles;\n expect(Array.isArray(roles)).toBe(true);\n if (Array.isArray(roles)) {\n expect(roles.every((role) => typeof role === \"string\")).toBe(true);\n expect(roles).toContain(\"Admin\");\n }\n\n const accessToken = json.data?.accessToken;\n const refreshToken = json.data?.refreshToken;\n expect(typeof accessToken).toBe(\"string\");\n expect(typeof refreshToken).toBe(\"string\");\n\n const accessPayload = validateJwt(accessToken as string);\n const refreshPayload = validateJwt(refreshToken as string);\n\n expect(typeof accessPayload.exp).toBe(\"number\");\n expect(typeof refreshPayload.exp).toBe(\"number\");\n } finally {\n await api.dispose();\n }\n});\n\ntest(\"API: authenticate fails with wrong password\", async () => {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: {\n accept: \"*/*\",\n \"content-type\": \"application/json\",\n },\n });\n\n try {\n const res = await api.post(\"/api/iam/authenticate\", {\n data: {\n email: TEST_USER_EMAIL,\n username: \"\",\n password: `${TEST_USER_PASSWORD}__wrong`,\n },\n });\n\n expect(res.status()).toBe(401);\n const json = await res.json();\n\n expect(json.statusCode).toBe(401);\n expect(json.statusCodeMessage).toBe(\"Unauthorized\");\n expect(json.message).toBe(\"Invalid credentials\");\n expect(json.error).toBe(\"Invalid credentials\");\n expect(json.data?.statusCode).toBe(401);\n expect(json.data?.error).toBe(\"Unauthorized\");\n expect(json.data?.message).toBe(\"Invalid credentials\");\n } finally {\n await api.dispose();\n }\n});\n"]}
@@ -1,97 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const test_1 = require("@playwright/test");
4
- const auth_1 = require("../helpers/auth");
5
- const baseURL = process.env.API_BASE_URL ?? "http://localhost:3000";
6
- async function getCityByName(name) {
7
- const api = await test_1.request.newContext({
8
- baseURL,
9
- extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
10
- });
11
- try {
12
- const res = await api.get(`/api/city-master?filters[name][$eq]=${encodeURIComponent(name)}&limit=1&offset=0`);
13
- (0, test_1.expect)(res.status()).toBe(200);
14
- const json = await res.json();
15
- const record = json?.data?.records?.[0];
16
- (0, test_1.expect)(record, `Expected city '${name}' in testData`).toBeTruthy();
17
- (0, test_1.expect)(record.id, "Expected record to have id").toBeTruthy();
18
- return record;
19
- }
20
- finally {
21
- await api.dispose();
22
- }
23
- }
24
- test_1.test.describe("CRUDService.findOne (cityMaster)", () => {
25
- (0, test_1.test)("returns a city by id", async () => {
26
- const city = await getCityByName("Mumbai");
27
- const api = await test_1.request.newContext({
28
- baseURL,
29
- extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
30
- });
31
- try {
32
- const res = await api.get(`/api/city-master/${city.id}`);
33
- (0, test_1.expect)(res.status()).toBe(200);
34
- const json = await res.json();
35
- (0, test_1.expect)(json?.data?.id).toBe(city.id);
36
- (0, test_1.expect)(json?.data?.name).toBe("Mumbai");
37
- (0, test_1.expect)(json?.data?.description).toBe("Mumbai city");
38
- }
39
- finally {
40
- await api.dispose();
41
- }
42
- });
43
- (0, test_1.test)("supports fields[] selection", async () => {
44
- const city = await getCityByName("Pune");
45
- const api = await test_1.request.newContext({
46
- baseURL,
47
- extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
48
- });
49
- try {
50
- const res = await api.get(`/api/city-master/${city.id}?fields[]=id&fields[]=name`);
51
- (0, test_1.expect)(res.status()).toBe(200);
52
- const json = await res.json();
53
- (0, test_1.expect)(json?.data?.id).toBe(city.id);
54
- (0, test_1.expect)(json?.data?.name).toBe("Pune");
55
- (0, test_1.expect)(json?.data?.description).toBeUndefined();
56
- (0, test_1.expect)(json?.data?.state).toBeUndefined();
57
- }
58
- finally {
59
- await api.dispose();
60
- }
61
- });
62
- (0, test_1.test)("supports populate=state", async () => {
63
- const city = await getCityByName("Bengaluru");
64
- const api = await test_1.request.newContext({
65
- baseURL,
66
- extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
67
- });
68
- try {
69
- const res = await api.get(`/api/city-master/${city.id}?populate=state`);
70
- (0, test_1.expect)(res.status()).toBe(200);
71
- const json = await res.json();
72
- (0, test_1.expect)(json?.data?.name).toBe("Bengaluru");
73
- (0, test_1.expect)(json?.data?.state).toBeTruthy();
74
- (0, test_1.expect)(json?.data?.state?.name).toBe("Karnataka");
75
- }
76
- finally {
77
- await api.dispose();
78
- }
79
- });
80
- (0, test_1.test)("returns 404 for missing id", async () => {
81
- const api = await test_1.request.newContext({
82
- baseURL,
83
- extraHTTPHeaders: await (0, auth_1.getAuthHeaders)(baseURL),
84
- });
85
- try {
86
- const res = await api.get(`/api/city-master/99999999`);
87
- (0, test_1.expect)(res.status()).toBe(404);
88
- const json = await res.json();
89
- (0, test_1.expect)(String(json?.message)).toContain("cityMaster");
90
- (0, test_1.expect)(String(json?.message)).toContain("not found");
91
- }
92
- finally {
93
- await api.dispose();
94
- }
95
- });
96
- });
97
- //# sourceMappingURL=crud-service.findOne.cityMaster.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"crud-service.findOne.cityMaster.spec.js","sourceRoot":"","sources":["../../tests/api/crud-service.findOne.cityMaster.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, request, test } from \"@playwright/test\";\nimport { getAuthHeaders } from \"../helpers/auth\";\n\nconst baseURL = process.env.API_BASE_URL ?? \"http://localhost:3000\";\n\nasync function getCityByName(name: string) {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(\n `/api/city-master?filters[name][$eq]=${encodeURIComponent(name)}&limit=1&offset=0`\n );\n expect(res.status()).toBe(200);\n const json = await res.json();\n\n const record = json?.data?.records?.[0];\n expect(record, `Expected city '${name}' in testData`).toBeTruthy();\n expect(record.id, \"Expected record to have id\").toBeTruthy();\n\n return record;\n } finally {\n await api.dispose();\n }\n}\n\ntest.describe(\"CRUDService.findOne (cityMaster)\", () => {\n test(\"returns a city by id\", async () => {\n const city = await getCityByName(\"Mumbai\");\n\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(`/api/city-master/${city.id}`);\n expect(res.status()).toBe(200);\n\n const json = await res.json();\n expect(json?.data?.id).toBe(city.id);\n expect(json?.data?.name).toBe(\"Mumbai\");\n expect(json?.data?.description).toBe(\"Mumbai city\");\n } finally {\n await api.dispose();\n }\n });\n\n test(\"supports fields[] selection\", async () => {\n const city = await getCityByName(\"Pune\");\n\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(\n `/api/city-master/${city.id}?fields[]=id&fields[]=name`\n );\n expect(res.status()).toBe(200);\n\n const json = await res.json();\n expect(json?.data?.id).toBe(city.id);\n expect(json?.data?.name).toBe(\"Pune\");\n expect(json?.data?.description).toBeUndefined();\n expect(json?.data?.state).toBeUndefined();\n } finally {\n await api.dispose();\n }\n });\n\n test(\"supports populate=state\", async () => {\n const city = await getCityByName(\"Bengaluru\");\n\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(`/api/city-master/${city.id}?populate=state`);\n expect(res.status()).toBe(200);\n\n const json = await res.json();\n expect(json?.data?.name).toBe(\"Bengaluru\");\n expect(json?.data?.state).toBeTruthy();\n expect(json?.data?.state?.name).toBe(\"Karnataka\");\n } finally {\n await api.dispose();\n }\n });\n\n test(\"returns 404 for missing id\", async () => {\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: await getAuthHeaders(baseURL),\n });\n try {\n const res = await api.get(`/api/city-master/99999999`);\n expect(res.status()).toBe(404);\n\n const json = await res.json();\n expect(String(json?.message)).toContain(\"cityMaster\");\n expect(String(json?.message)).toContain(\"not found\");\n } finally {\n await api.dispose();\n }\n });\n});\n"]}
@@ -1,21 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const test_1 = require("@playwright/test");
4
- (0, test_1.test)("GET /api/ping returns pong", async () => {
5
- const baseURL = process.env.BASE_URL || "http://localhost:3000";
6
- if (!baseURL) {
7
- throw new Error("baseURL is not configured. Set API_BASE_URL or use the default.");
8
- }
9
- const api = await test_1.request.newContext({ baseURL });
10
- const res = await api.get("/api/ping");
11
- (0, test_1.expect)(res.status()).toBe(200);
12
- const body = await res.json();
13
- (0, test_1.expect)(body).toEqual({
14
- statusCode: 200,
15
- message: [],
16
- error: "",
17
- data: { pong: "v1.0.2" },
18
- });
19
- await api.dispose();
20
- });
21
- //# sourceMappingURL=ping.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ping.spec.js","sourceRoot":"","sources":["../../tests/api/ping.spec.ts"],"names":[],"mappings":";;AAAA,2CAAyD;AAEzD,IAAA,WAAI,EAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;IAC5C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,uBAAuB,CAAC;IAChE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;IACrF,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,cAAO,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAElD,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAEvC,IAAA,aAAM,EAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE/B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAA,aAAM,EAAC,IAAI,CAAC,CAAC,OAAO,CAAC;QACnB,UAAU,EAAE,GAAG;QACf,OAAO,EAAE,EAAE;QACX,KAAK,EAAE,EAAE;QACT,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KACzB,CAAC,CAAC;IAEH,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;AACtB,CAAC,CAAC,CAAC","sourcesContent":["import { expect, request, test } from \"@playwright/test\";\n\ntest(\"GET /api/ping returns pong\", async () => {\n const baseURL = process.env.BASE_URL || \"http://localhost:3000\";\n if (!baseURL) {\n throw new Error(\"baseURL is not configured. Set API_BASE_URL or use the default.\");\n }\n const api = await request.newContext({ baseURL });\n\n const res = await api.get(\"/api/ping\");\n\n expect(res.status()).toBe(200);\n\n const body = await res.json();\n expect(body).toEqual({\n statusCode: 200,\n message: [],\n error: \"\",\n data: { pong: \"v1.0.2\" },\n });\n\n await api.dispose();\n});\n"]}
@@ -1,41 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getAccessToken = getAccessToken;
4
- exports.getAuthHeaders = getAuthHeaders;
5
- const test_1 = require("@playwright/test");
6
- const env_1 = require("./env");
7
- async function getAccessToken(baseURL) {
8
- const TEST_USER_EMAIL = (0, env_1.getRequiredEnv)("TEST_USER_EMAIL");
9
- const TEST_USER_PASSWORD = (0, env_1.getRequiredEnv)("TEST_USER_PASSWORD");
10
- const api = await test_1.request.newContext({
11
- baseURL,
12
- extraHTTPHeaders: {
13
- accept: "*/*",
14
- "content-type": "application/json",
15
- },
16
- });
17
- try {
18
- const res = await api.post("/api/iam/authenticate", {
19
- data: {
20
- email: TEST_USER_EMAIL,
21
- username: "",
22
- password: TEST_USER_PASSWORD,
23
- },
24
- });
25
- (0, test_1.expect)(res.status()).toBe(200);
26
- const json = await res.json();
27
- const token = json?.data?.accessToken;
28
- (0, test_1.expect)(token, "Expected access token from authenticate endpoint.").toBeTruthy();
29
- return token;
30
- }
31
- finally {
32
- await api.dispose();
33
- }
34
- }
35
- async function getAuthHeaders(baseURL) {
36
- const token = await getAccessToken(baseURL);
37
- return {
38
- authorization: `Bearer ${token}`,
39
- };
40
- }
41
- //# sourceMappingURL=auth.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../tests/helpers/auth.ts"],"names":[],"mappings":";;AAGA,wCA6BC;AAED,wCAKC;AAvCD,2CAAmD;AACnD,+BAAuC;AAEhC,KAAK,UAAU,cAAc,CAAC,OAAe;IAClD,MAAM,eAAe,GAAG,IAAA,oBAAc,EAAC,iBAAiB,CAAC,CAAC;IAC1D,MAAM,kBAAkB,GAAG,IAAA,oBAAc,EAAC,oBAAoB,CAAC,CAAC;IAEhE,MAAM,GAAG,GAAG,MAAM,cAAO,CAAC,UAAU,CAAC;QACnC,OAAO;QACP,gBAAgB,EAAE;YAChB,MAAM,EAAE,KAAK;YACb,cAAc,EAAE,kBAAkB;SACnC;KACF,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,uBAAuB,EAAE;YAClD,IAAI,EAAE;gBACJ,KAAK,EAAE,eAAe;gBACtB,QAAQ,EAAE,EAAE;gBACZ,QAAQ,EAAE,kBAAkB;aAC7B;SACF,CAAC,CAAC;QAEH,IAAA,aAAM,EAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,IAAI,EAAE,IAAI,EAAE,WAAiC,CAAC;QAC5D,IAAA,aAAM,EAAC,KAAK,EAAE,mDAAmD,CAAC,CAAC,UAAU,EAAE,CAAC;QAChF,OAAO,KAAe,CAAC;IACzB,CAAC;YAAS,CAAC;QACT,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;IACtB,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,cAAc,CAAC,OAAe;IAClD,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC;IAC5C,OAAO;QACL,aAAa,EAAE,UAAU,KAAK,EAAE;KACjC,CAAC;AACJ,CAAC","sourcesContent":["import { expect, request } from \"@playwright/test\";\nimport { getRequiredEnv } from \"./env\";\n\nexport async function getAccessToken(baseURL: string) {\n const TEST_USER_EMAIL = getRequiredEnv(\"TEST_USER_EMAIL\");\n const TEST_USER_PASSWORD = getRequiredEnv(\"TEST_USER_PASSWORD\");\n\n const api = await request.newContext({\n baseURL,\n extraHTTPHeaders: {\n accept: \"*/*\",\n \"content-type\": \"application/json\",\n },\n });\n\n try {\n const res = await api.post(\"/api/iam/authenticate\", {\n data: {\n email: TEST_USER_EMAIL,\n username: \"\",\n password: TEST_USER_PASSWORD,\n },\n });\n\n expect(res.status()).toBe(200);\n const json = await res.json();\n const token = json?.data?.accessToken as string | undefined;\n expect(token, \"Expected access token from authenticate endpoint.\").toBeTruthy();\n return token as string;\n } finally {\n await api.dispose();\n }\n}\n\nexport async function getAuthHeaders(baseURL: string) {\n const token = await getAccessToken(baseURL);\n return {\n authorization: `Bearer ${token}`,\n };\n}\n"]}
@@ -1,11 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getRequiredEnv = getRequiredEnv;
4
- function getRequiredEnv(name) {
5
- const value = process.env[name];
6
- if (!value) {
7
- throw new Error(`Missing required env var: ${name}`);
8
- }
9
- return value;
10
- }
11
- //# sourceMappingURL=env.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"env.js","sourceRoot":"","sources":["../../tests/helpers/env.ts"],"names":[],"mappings":";;AAAA,wCAMC;AAND,SAAgB,cAAc,CAAC,IAAY;IACzC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,6BAA6B,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["export function getRequiredEnv(name: string): string {\n const value = process.env[name];\n if (!value) {\n throw new Error(`Missing required env var: ${name}`);\n }\n return value;\n}\n"]}