@friggframework/core 2.0.0-next.54 → 2.0.0-next.55

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.
@@ -8,17 +8,23 @@ const {
8
8
  updateOne,
9
9
  deleteOne,
10
10
  } = require('../../database/documentdb-utils');
11
- const { ProcessRepositoryInterface } = require('./process-repository-interface');
11
+ const {
12
+ ProcessRepositoryInterface,
13
+ } = require('./process-repository-interface');
14
+ const {
15
+ DocumentDBEncryptionService,
16
+ } = require('../../database/documentdb-encryption-service');
12
17
 
13
18
  class ProcessRepositoryDocumentDB extends ProcessRepositoryInterface {
14
19
  constructor() {
15
20
  super();
16
21
  this.prisma = prisma;
22
+ this.encryptionService = new DocumentDBEncryptionService();
17
23
  }
18
24
 
19
25
  async create(processData) {
20
26
  const now = new Date();
21
- const document = {
27
+ const plainDocument = {
22
28
  userId: toObjectId(processData.userId),
23
29
  integrationId: toObjectId(processData.integrationId),
24
30
  name: processData.name,
@@ -26,45 +32,127 @@ class ProcessRepositoryDocumentDB extends ProcessRepositoryInterface {
26
32
  state: processData.state || 'INITIALIZING',
27
33
  context: processData.context || {},
28
34
  results: processData.results || {},
29
- childProcesses: (processData.childProcesses || []).map((id) => toObjectId(id)).filter(Boolean),
30
- parentProcessId: processData.parentProcessId ? toObjectId(processData.parentProcessId) : null,
35
+ childProcesses: (processData.childProcesses || [])
36
+ .map((id) => toObjectId(id))
37
+ .filter(Boolean),
38
+ parentProcessId: processData.parentProcessId
39
+ ? toObjectId(processData.parentProcessId)
40
+ : null,
31
41
  createdAt: now,
32
42
  updatedAt: now,
33
43
  };
34
- const insertedId = await insertOne(this.prisma, 'Process', document);
35
- const created = await findOne(this.prisma, 'Process', { _id: insertedId });
36
- return this._mapProcess(created);
44
+
45
+ const encryptedDocument = await this.encryptionService.encryptFields(
46
+ 'Process',
47
+ plainDocument
48
+ );
49
+
50
+ const insertedId = await insertOne(
51
+ this.prisma,
52
+ 'Process',
53
+ encryptedDocument
54
+ );
55
+
56
+ const created = await findOne(this.prisma, 'Process', {
57
+ _id: insertedId,
58
+ });
59
+ if (!created) {
60
+ console.error(
61
+ '[ProcessRepositoryDocumentDB] Process not found after insert',
62
+ {
63
+ insertedId: fromObjectId(insertedId),
64
+ processData: {
65
+ userId: processData.userId,
66
+ integrationId: processData.integrationId,
67
+ name: processData.name,
68
+ type: processData.type,
69
+ },
70
+ }
71
+ );
72
+ throw new Error(
73
+ 'Failed to create process: Document not found after insert. ' +
74
+ 'This indicates a database consistency issue.'
75
+ );
76
+ }
77
+ const decryptedProcess = await this.encryptionService.decryptFields(
78
+ 'Process',
79
+ created
80
+ );
81
+ return this._mapProcess(decryptedProcess);
37
82
  }
38
83
 
39
84
  async findById(processId) {
40
85
  const objectId = toObjectId(processId);
41
86
  if (!objectId) return null;
42
87
  const doc = await findOne(this.prisma, 'Process', { _id: objectId });
43
- return doc ? this._mapProcess(doc) : null;
88
+ if (!doc) return null;
89
+
90
+ const decryptedProcess = await this.encryptionService.decryptFields(
91
+ 'Process',
92
+ doc
93
+ );
94
+ return this._mapProcess(decryptedProcess);
44
95
  }
45
96
 
46
97
  async update(processId, updates) {
47
98
  const objectId = toObjectId(processId);
48
99
  if (!objectId) return null;
100
+
101
+ const existing = await findOne(this.prisma, 'Process', {
102
+ _id: objectId,
103
+ });
104
+ if (!existing) return null;
105
+
49
106
  const updatePayload = {};
50
107
  if (updates.state !== undefined) updatePayload.state = updates.state;
51
- if (updates.context !== undefined) updatePayload.context = updates.context;
52
- if (updates.results !== undefined) updatePayload.results = updates.results;
108
+ if (updates.context !== undefined)
109
+ updatePayload.context = updates.context;
110
+ if (updates.results !== undefined)
111
+ updatePayload.results = updates.results;
53
112
  if (updates.childProcesses !== undefined) {
54
- updatePayload.childProcesses = (updates.childProcesses || []).map((id) => toObjectId(id)).filter(Boolean);
113
+ updatePayload.childProcesses = (updates.childProcesses || [])
114
+ .map((id) => toObjectId(id))
115
+ .filter(Boolean);
55
116
  }
56
117
  if (updates.parentProcessId !== undefined) {
57
- updatePayload.parentProcessId = updates.parentProcessId ? toObjectId(updates.parentProcessId) : null;
118
+ updatePayload.parentProcessId = updates.parentProcessId
119
+ ? toObjectId(updates.parentProcessId)
120
+ : null;
58
121
  }
59
122
  updatePayload.updatedAt = new Date();
123
+
124
+ const encryptedUpdate = await this.encryptionService.encryptFields(
125
+ 'Process',
126
+ updatePayload
127
+ );
128
+
60
129
  await updateOne(
61
130
  this.prisma,
62
131
  'Process',
63
132
  { _id: objectId },
64
- { $set: updatePayload }
133
+ { $set: encryptedUpdate }
134
+ );
135
+
136
+ const updated = await findOne(this.prisma, 'Process', {
137
+ _id: objectId,
138
+ });
139
+ if (!updated) {
140
+ console.error(
141
+ '[ProcessRepositoryDocumentDB] Process not found after update',
142
+ {
143
+ processId: fromObjectId(objectId),
144
+ }
145
+ );
146
+ throw new Error(
147
+ 'Failed to update process: Document not found after update. ' +
148
+ 'This indicates a database consistency issue.'
149
+ );
150
+ }
151
+ const decryptedProcess = await this.encryptionService.decryptFields(
152
+ 'Process',
153
+ updated
65
154
  );
66
- const updated = await findOne(this.prisma, 'Process', { _id: objectId });
67
- return updated ? this._mapProcess(updated) : null;
155
+ return this._mapProcess(decryptedProcess);
68
156
  }
69
157
 
70
158
  async findByIntegrationAndType(integrationId, type) {
@@ -76,30 +164,36 @@ class ProcessRepositoryDocumentDB extends ProcessRepositoryInterface {
76
164
  const docs = await findMany(this.prisma, 'Process', filter, {
77
165
  sort: { createdAt: -1 },
78
166
  });
79
- return docs.map((doc) => this._mapProcess(doc));
167
+
168
+ const decryptedDocs = await Promise.all(
169
+ docs.map((doc) =>
170
+ this.encryptionService.decryptFields('Process', doc)
171
+ )
172
+ );
173
+
174
+ return decryptedDocs.map((doc) => this._mapProcess(doc));
80
175
  }
81
176
 
82
- async findActiveProcesses(integrationId, excludeStates = ['COMPLETED', 'ERROR']) {
177
+ async findActiveProcesses(
178
+ integrationId,
179
+ excludeStates = ['COMPLETED', 'ERROR']
180
+ ) {
83
181
  const integrationObjectId = toObjectId(integrationId);
84
- const pipeline = [
85
- {
86
- $match: {
87
- integrationId: integrationObjectId,
88
- },
89
- },
90
- {
91
- $match: {
92
- state: { $nin: excludeStates },
93
- },
94
- },
95
- { $sort: { createdAt: -1 } },
96
- ];
97
- const docs = await this.prisma.$runCommandRaw({
98
- aggregate: 'Process',
99
- pipeline,
100
- cursor: {},
101
- }).then((res) => res?.cursor?.firstBatch || []);
102
- return docs.map((doc) => this._mapProcess(doc));
182
+ const filter = {
183
+ integrationId: integrationObjectId,
184
+ state: { $nin: excludeStates },
185
+ };
186
+ const docs = await findMany(this.prisma, 'Process', filter, {
187
+ sort: { createdAt: -1 },
188
+ });
189
+
190
+ const decryptedDocs = await Promise.all(
191
+ docs.map((doc) =>
192
+ this.encryptionService.decryptFields('Process', doc)
193
+ )
194
+ );
195
+
196
+ return decryptedDocs.map((doc) => this._mapProcess(doc));
103
197
  }
104
198
 
105
199
  async findByName(name) {
@@ -109,7 +203,13 @@ class ProcessRepositoryDocumentDB extends ProcessRepositoryInterface {
109
203
  { name },
110
204
  { sort: { createdAt: -1 } }
111
205
  );
112
- return doc ? this._mapProcess(doc) : null;
206
+ if (!doc) return null;
207
+
208
+ const decryptedProcess = await this.encryptionService.decryptFields(
209
+ 'Process',
210
+ doc
211
+ );
212
+ return this._mapProcess(decryptedProcess);
113
213
  }
114
214
 
115
215
  async deleteById(processId) {
@@ -128,8 +228,12 @@ class ProcessRepositoryDocumentDB extends ProcessRepositoryInterface {
128
228
  state: doc?.state ?? null,
129
229
  context: doc?.context ?? {},
130
230
  results: doc?.results ?? {},
131
- childProcesses: (doc?.childProcesses || []).map((id) => fromObjectId(id)),
132
- parentProcessId: doc?.parentProcessId ? fromObjectId(doc.parentProcessId) : null,
231
+ childProcesses: (doc?.childProcesses || []).map((id) =>
232
+ fromObjectId(id)
233
+ ),
234
+ parentProcessId: doc?.parentProcessId
235
+ ? fromObjectId(doc.parentProcessId)
236
+ : null,
133
237
  createdAt: doc?.createdAt ? new Date(doc.createdAt) : null,
134
238
  updatedAt: doc?.updatedAt ? new Date(doc.updatedAt) : null,
135
239
  };
@@ -137,5 +241,3 @@ class ProcessRepositoryDocumentDB extends ProcessRepositoryInterface {
137
241
  }
138
242
 
139
243
  module.exports = { ProcessRepositoryDocumentDB };
140
-
141
-
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@friggframework/core",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0-next.54",
4
+ "version": "2.0.0-next.55",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-apigatewaymanagementapi": "^3.588.0",
7
7
  "@aws-sdk/client-kms": "^3.588.0",
@@ -38,9 +38,9 @@
38
38
  }
39
39
  },
40
40
  "devDependencies": {
41
- "@friggframework/eslint-config": "2.0.0-next.54",
42
- "@friggframework/prettier-config": "2.0.0-next.54",
43
- "@friggframework/test": "2.0.0-next.54",
41
+ "@friggframework/eslint-config": "2.0.0-next.55",
42
+ "@friggframework/prettier-config": "2.0.0-next.55",
43
+ "@friggframework/test": "2.0.0-next.55",
44
44
  "@prisma/client": "^6.17.0",
45
45
  "@types/lodash": "4.17.15",
46
46
  "@typescript-eslint/eslint-plugin": "^8.0.0",
@@ -80,5 +80,5 @@
80
80
  "publishConfig": {
81
81
  "access": "public"
82
82
  },
83
- "gitHead": "d72f0af6966a5701fe2a4257139d40292972f92a"
83
+ "gitHead": "7f86d4b5faaab4de74e6e3676948b62d1e1a5bb1"
84
84
  }
@@ -10,6 +10,7 @@ const {
10
10
  deleteMany,
11
11
  } = require('../../database/documentdb-utils');
12
12
  const { TokenRepositoryInterface } = require('./token-repository-interface');
13
+ const { ClientSafeError } = require('../../errors');
13
14
 
14
15
  const BCRYPT_ROUNDS = 10;
15
16
 
@@ -30,25 +31,36 @@ class TokenRepositoryDocumentDB extends TokenRepositoryInterface {
30
31
  created: now,
31
32
  };
32
33
  const insertedId = await insertOne(this.prisma, 'Token', document);
33
- const created = await findOne(this.prisma, 'Token', { _id: insertedId });
34
+ const created = await findOne(this.prisma, 'Token', {
35
+ _id: insertedId,
36
+ });
34
37
  return this._mapToken(created);
35
38
  }
36
39
 
37
40
  async validateAndGetToken(tokenObj) {
38
41
  const objectId = toObjectId(tokenObj.id);
39
42
  if (!objectId) {
40
- throw new Error('Invalid Token: Token does not exist');
43
+ throw new ClientSafeError(
44
+ 'Invalid Token: Token does not exist',
45
+ 401
46
+ );
41
47
  }
42
48
  const record = await findOne(this.prisma, 'Token', { _id: objectId });
43
49
  if (!record) {
44
- throw new Error('Invalid Token: Token does not exist');
50
+ throw new ClientSafeError(
51
+ 'Invalid Token: Token does not exist',
52
+ 401
53
+ );
45
54
  }
46
55
  const isValid = await bcrypt.compare(tokenObj.token, record.token);
47
56
  if (!isValid) {
48
- throw new Error('Invalid Token: Token does not match');
57
+ throw new ClientSafeError(
58
+ 'Invalid Token: Token does not match',
59
+ 401
60
+ );
49
61
  }
50
62
  if (record.expires && new Date(record.expires) < new Date()) {
51
- throw new Error('Invalid Token: Token is expired');
63
+ throw new ClientSafeError('Invalid Token: Token is expired', 401);
52
64
  }
53
65
  return this._mapToken(record);
54
66
  }
@@ -86,7 +98,9 @@ class TokenRepositoryDocumentDB extends TokenRepositoryInterface {
86
98
  async deleteTokensByUserId(userId) {
87
99
  const objectId = toObjectId(userId);
88
100
  if (!objectId) return { acknowledged: true, deletedCount: 0 };
89
- const result = await deleteMany(this.prisma, 'Token', { userId: objectId });
101
+ const result = await deleteMany(this.prisma, 'Token', {
102
+ userId: objectId,
103
+ });
90
104
  const deleted = result?.n ?? 0;
91
105
  return { acknowledged: true, deletedCount: deleted };
92
106
  }
@@ -121,5 +135,3 @@ class TokenRepositoryDocumentDB extends TokenRepositoryInterface {
121
135
  }
122
136
 
123
137
  module.exports = { TokenRepositoryDocumentDB };
124
-
125
-
@@ -1,6 +1,7 @@
1
1
  const { prisma } = require('../../database/prisma');
2
2
  const bcrypt = require('bcryptjs');
3
3
  const { TokenRepositoryInterface } = require('./token-repository-interface');
4
+ const { ClientSafeError } = require('../../errors');
4
5
 
5
6
  const BCRYPT_ROUNDS = 10;
6
7
 
@@ -58,7 +59,10 @@ class TokenRepositoryMongo extends TokenRepositoryInterface {
58
59
  });
59
60
 
60
61
  if (!sessionToken) {
61
- throw new Error('Invalid Token: Token does not exist');
62
+ throw new ClientSafeError(
63
+ 'Invalid Token: Token does not exist',
64
+ 401
65
+ );
62
66
  }
63
67
 
64
68
  // Verify token hash matches
@@ -67,7 +71,10 @@ class TokenRepositoryMongo extends TokenRepositoryInterface {
67
71
  sessionToken.token
68
72
  );
69
73
  if (!isValid) {
70
- throw new Error('Invalid Token: Token does not match');
74
+ throw new ClientSafeError(
75
+ 'Invalid Token: Token does not match',
76
+ 401
77
+ );
71
78
  }
72
79
 
73
80
  // Check if token is expired
@@ -75,7 +82,7 @@ class TokenRepositoryMongo extends TokenRepositoryInterface {
75
82
  sessionToken.expires &&
76
83
  new Date(sessionToken.expires) < new Date()
77
84
  ) {
78
- throw new Error('Invalid Token: Token is expired');
85
+ throw new ClientSafeError('Invalid Token: Token is expired', 401);
79
86
  }
80
87
 
81
88
  return sessionToken;
@@ -1,6 +1,7 @@
1
1
  const { prisma } = require('../../database/prisma');
2
2
  const bcrypt = require('bcryptjs');
3
3
  const { TokenRepositoryInterface } = require('./token-repository-interface');
4
+ const { ClientSafeError } = require('../../errors');
4
5
 
5
6
  const BCRYPT_ROUNDS = 10;
6
7
 
@@ -92,7 +93,10 @@ class TokenRepositoryPostgres extends TokenRepositoryInterface {
92
93
  });
93
94
 
94
95
  if (!sessionToken) {
95
- throw new Error('Invalid Token: Token does not exist');
96
+ throw new ClientSafeError(
97
+ 'Invalid Token: Token does not exist',
98
+ 401
99
+ );
96
100
  }
97
101
 
98
102
  // Verify token hash matches
@@ -101,7 +105,10 @@ class TokenRepositoryPostgres extends TokenRepositoryInterface {
101
105
  sessionToken.token
102
106
  );
103
107
  if (!isValid) {
104
- throw new Error('Invalid Token: Token does not match');
108
+ throw new ClientSafeError(
109
+ 'Invalid Token: Token does not match',
110
+ 401
111
+ );
105
112
  }
106
113
 
107
114
  // Check if token is expired
@@ -109,7 +116,7 @@ class TokenRepositoryPostgres extends TokenRepositoryInterface {
109
116
  sessionToken.expires &&
110
117
  new Date(sessionToken.expires) < new Date()
111
118
  ) {
112
- throw new Error('Invalid Token: Token is expired');
119
+ throw new ClientSafeError('Invalid Token: Token is expired', 401);
113
120
  }
114
121
 
115
122
  return this._convertTokenIds(sessionToken);