@friggframework/core 2.0.0--canary.517.f04156f.0 → 2.0.0--canary.517.a37d697.0

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.
@@ -16,16 +16,16 @@
16
16
  */
17
17
 
18
18
  // Repository Interfaces
19
- const { ScriptExecutionRepositoryInterface } = require('./repositories/script-execution-repository-interface');
19
+ const { AdminProcessRepositoryInterface } = require('./repositories/admin-process-repository-interface');
20
20
  const { ScriptScheduleRepositoryInterface } = require('./repositories/script-schedule-repository-interface');
21
21
 
22
22
  // Repository Factories
23
23
  const {
24
- createScriptExecutionRepository,
25
- ScriptExecutionRepositoryMongo,
26
- ScriptExecutionRepositoryPostgres,
27
- ScriptExecutionRepositoryDocumentDB,
28
- } = require('./repositories/script-execution-repository-factory');
24
+ createAdminProcessRepository,
25
+ AdminProcessRepositoryMongo,
26
+ AdminProcessRepositoryPostgres,
27
+ AdminProcessRepositoryDocumentDB,
28
+ } = require('./repositories/admin-process-repository-factory');
29
29
  const {
30
30
  createScriptScheduleRepository,
31
31
  ScriptScheduleRepositoryMongo,
@@ -35,17 +35,17 @@ const {
35
35
 
36
36
  module.exports = {
37
37
  // Repository Interfaces
38
- ScriptExecutionRepositoryInterface,
38
+ AdminProcessRepositoryInterface,
39
39
  ScriptScheduleRepositoryInterface,
40
40
 
41
41
  // Repository Factories (primary exports for use cases)
42
- createScriptExecutionRepository,
42
+ createAdminProcessRepository,
43
43
  createScriptScheduleRepository,
44
44
 
45
45
  // Concrete Implementations (for testing)
46
- ScriptExecutionRepositoryMongo,
47
- ScriptExecutionRepositoryPostgres,
48
- ScriptExecutionRepositoryDocumentDB,
46
+ AdminProcessRepositoryMongo,
47
+ AdminProcessRepositoryPostgres,
48
+ AdminProcessRepositoryDocumentDB,
49
49
  ScriptScheduleRepositoryMongo,
50
50
  ScriptScheduleRepositoryPostgres,
51
51
  ScriptScheduleRepositoryDocumentDB,
@@ -1,9 +1,9 @@
1
1
  const {
2
- ScriptExecutionRepositoryMongo,
3
- } = require('./script-execution-repository-mongo');
2
+ AdminProcessRepositoryMongo,
3
+ } = require('./admin-process-repository-mongo');
4
4
 
5
5
  /**
6
- * DocumentDB Script Execution Repository Adapter
6
+ * DocumentDB Admin Process Repository Adapter
7
7
  * Extends MongoDB implementation since DocumentDB uses the same Prisma client
8
8
  *
9
9
  * DocumentDB-specific characteristics:
@@ -12,10 +12,10 @@ const {
12
12
  * - IDs are strings with ObjectId format
13
13
  * - All operations identical to MongoDB implementation
14
14
  */
15
- class ScriptExecutionRepositoryDocumentDB extends ScriptExecutionRepositoryMongo {
15
+ class AdminProcessRepositoryDocumentDB extends AdminProcessRepositoryMongo {
16
16
  constructor() {
17
17
  super();
18
18
  }
19
19
  }
20
20
 
21
- module.exports = { ScriptExecutionRepositoryDocumentDB };
21
+ module.exports = { AdminProcessRepositoryDocumentDB };
@@ -1,12 +1,12 @@
1
- const { ScriptExecutionRepositoryMongo } = require('./script-execution-repository-mongo');
2
- const { ScriptExecutionRepositoryPostgres } = require('./script-execution-repository-postgres');
1
+ const { AdminProcessRepositoryMongo } = require('./admin-process-repository-mongo');
2
+ const { AdminProcessRepositoryPostgres } = require('./admin-process-repository-postgres');
3
3
  const {
4
- ScriptExecutionRepositoryDocumentDB,
5
- } = require('./script-execution-repository-documentdb');
4
+ AdminProcessRepositoryDocumentDB,
5
+ } = require('./admin-process-repository-documentdb');
6
6
  const config = require('../../database/config');
7
7
 
8
8
  /**
9
- * Script Execution Repository Factory
9
+ * Admin Process Repository Factory
10
10
  * Creates the appropriate repository adapter based on database type
11
11
  *
12
12
  * This implements the Factory pattern for Hexagonal Architecture:
@@ -16,24 +16,24 @@ const config = require('../../database/config');
16
16
  *
17
17
  * Usage:
18
18
  * ```javascript
19
- * const repository = createScriptExecutionRepository();
19
+ * const repository = createAdminProcessRepository();
20
20
  * ```
21
21
  *
22
- * @returns {ScriptExecutionRepositoryInterface} Configured repository adapter
22
+ * @returns {AdminProcessRepositoryInterface} Configured repository adapter
23
23
  * @throws {Error} If database type is not supported
24
24
  */
25
- function createScriptExecutionRepository() {
25
+ function createAdminProcessRepository() {
26
26
  const dbType = config.DB_TYPE;
27
27
 
28
28
  switch (dbType) {
29
29
  case 'mongodb':
30
- return new ScriptExecutionRepositoryMongo();
30
+ return new AdminProcessRepositoryMongo();
31
31
 
32
32
  case 'postgresql':
33
- return new ScriptExecutionRepositoryPostgres();
33
+ return new AdminProcessRepositoryPostgres();
34
34
 
35
35
  case 'documentdb':
36
- return new ScriptExecutionRepositoryDocumentDB();
36
+ return new AdminProcessRepositoryDocumentDB();
37
37
 
38
38
  default:
39
39
  throw new Error(
@@ -43,9 +43,9 @@ function createScriptExecutionRepository() {
43
43
  }
44
44
 
45
45
  module.exports = {
46
- createScriptExecutionRepository,
46
+ createAdminProcessRepository,
47
47
  // Export adapters for direct testing
48
- ScriptExecutionRepositoryMongo,
49
- ScriptExecutionRepositoryPostgres,
50
- ScriptExecutionRepositoryDocumentDB,
48
+ AdminProcessRepositoryMongo,
49
+ AdminProcessRepositoryPostgres,
50
+ AdminProcessRepositoryDocumentDB,
51
51
  };
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Admin Process Repository Interface
3
+ * Abstract base class defining the contract for admin process persistence adapters
4
+ *
5
+ * This follows the Port in Hexagonal Architecture:
6
+ * - Domain layer depends on this abstraction
7
+ * - Concrete adapters implement this interface
8
+ * - Use cases receive repositories via dependency injection
9
+ *
10
+ * Admin processes track administrative operations including:
11
+ * - Admin script executions
12
+ * - Database migrations
13
+ * - Scheduled maintenance tasks
14
+ *
15
+ * The AdminProcess model uses a flexible JSON storage pattern:
16
+ * - context: Input parameters, trigger info, audit data, script version
17
+ * - results: Output data, logs, metrics, error details
18
+ *
19
+ * @abstract
20
+ */
21
+ class AdminProcessRepositoryInterface {
22
+ /**
23
+ * Create a new admin process record
24
+ *
25
+ * @param {Object} params - Process creation parameters
26
+ * @param {string} params.name - Name of the process (e.g., script name, migration name)
27
+ * @param {string} params.type - Type of process (e.g., 'ADMIN_SCRIPT', 'DB_MIGRATION')
28
+ * @param {Object} [params.context] - Context data (input, trigger, audit, script version)
29
+ * @param {string} [params.context.scriptVersion] - Version of the script
30
+ * @param {string} [params.context.trigger] - Trigger type ('MANUAL', 'SCHEDULED', 'QUEUE', 'WEBHOOK')
31
+ * @param {string} [params.context.mode] - Execution mode ('sync' or 'async')
32
+ * @param {Object} [params.context.input] - Input parameters
33
+ * @param {Object} [params.context.audit] - Audit information
34
+ * @param {string} [params.context.audit.apiKeyName] - Name of API key used
35
+ * @param {string} [params.context.audit.apiKeyLast4] - Last 4 chars of API key
36
+ * @param {string} [params.context.audit.ipAddress] - IP address of requester
37
+ * @returns {Promise<Object>} The created process record
38
+ * @abstract
39
+ */
40
+ async createProcess({ name, type, context }) {
41
+ throw new Error('Method createProcess must be implemented by subclass');
42
+ }
43
+
44
+ /**
45
+ * Find a process by its ID
46
+ *
47
+ * @param {string|number} id - The process ID
48
+ * @returns {Promise<Object|null>} The process record or null if not found
49
+ * @abstract
50
+ */
51
+ async findProcessById(id) {
52
+ throw new Error('Method findProcessById must be implemented by subclass');
53
+ }
54
+
55
+ /**
56
+ * Find all processes with a specific name
57
+ *
58
+ * @param {string} name - The process name to filter by
59
+ * @param {Object} [options] - Query options
60
+ * @param {number} [options.limit] - Maximum number of results
61
+ * @param {number} [options.offset] - Number of results to skip
62
+ * @param {string} [options.sortBy] - Field to sort by
63
+ * @param {string} [options.sortOrder] - Sort order ('asc' or 'desc')
64
+ * @returns {Promise<Array>} Array of process records
65
+ * @abstract
66
+ */
67
+ async findProcessesByName(name, options = {}) {
68
+ throw new Error('Method findProcessesByName must be implemented by subclass');
69
+ }
70
+
71
+ /**
72
+ * Find all processes with a specific state
73
+ *
74
+ * @param {string} state - State to filter by ('PENDING', 'RUNNING', 'COMPLETED', 'FAILED')
75
+ * @param {Object} [options] - Query options
76
+ * @param {number} [options.limit] - Maximum number of results
77
+ * @param {number} [options.offset] - Number of results to skip
78
+ * @param {string} [options.sortBy] - Field to sort by
79
+ * @param {string} [options.sortOrder] - Sort order ('asc' or 'desc')
80
+ * @returns {Promise<Array>} Array of process records
81
+ * @abstract
82
+ */
83
+ async findProcessesByState(state, options = {}) {
84
+ throw new Error('Method findProcessesByState must be implemented by subclass');
85
+ }
86
+
87
+ /**
88
+ * Update the state of a process
89
+ *
90
+ * @param {string|number} id - The process ID
91
+ * @param {string} state - New state value ('PENDING', 'RUNNING', 'COMPLETED', 'FAILED')
92
+ * @returns {Promise<Object>} Updated process record
93
+ * @abstract
94
+ */
95
+ async updateProcessState(id, state) {
96
+ throw new Error('Method updateProcessState must be implemented by subclass');
97
+ }
98
+
99
+ /**
100
+ * Update the results of a process
101
+ * Merges new results with existing results in the results JSON field
102
+ *
103
+ * @param {string|number} id - The process ID
104
+ * @param {Object} results - Results data to merge
105
+ * @param {Object} [results.output] - Output data from the process
106
+ * @param {Object} [results.error] - Error information
107
+ * @param {string} [results.error.name] - Error name/type
108
+ * @param {string} [results.error.message] - Error message
109
+ * @param {string} [results.error.stack] - Error stack trace
110
+ * @param {Object} [results.metrics] - Performance metrics
111
+ * @param {Date} [results.metrics.startTime] - Process start time
112
+ * @param {Date} [results.metrics.endTime] - Process end time
113
+ * @param {number} [results.metrics.durationMs] - Duration in milliseconds
114
+ * @returns {Promise<Object>} Updated process record
115
+ * @abstract
116
+ */
117
+ async updateProcessResults(id, results) {
118
+ throw new Error('Method updateProcessResults must be implemented by subclass');
119
+ }
120
+
121
+ /**
122
+ * Append a log entry to a process's log array in results
123
+ *
124
+ * @param {string|number} id - The process ID
125
+ * @param {Object} logEntry - Log entry to append
126
+ * @param {string} logEntry.level - Log level ('debug', 'info', 'warn', 'error')
127
+ * @param {string} logEntry.message - Log message
128
+ * @param {Object} [logEntry.data] - Additional log data
129
+ * @param {string} logEntry.timestamp - ISO timestamp
130
+ * @returns {Promise<Object>} Updated process record
131
+ * @abstract
132
+ */
133
+ async appendProcessLog(id, logEntry) {
134
+ throw new Error('Method appendProcessLog must be implemented by subclass');
135
+ }
136
+
137
+ /**
138
+ * Delete all processes older than a specific date
139
+ * Used for cleanup and retention policies
140
+ *
141
+ * @param {Date} date - Delete processes older than this date
142
+ * @returns {Promise<Object>} Deletion result with count
143
+ * @abstract
144
+ */
145
+ async deleteProcessesOlderThan(date) {
146
+ throw new Error('Method deleteProcessesOlderThan must be implemented by subclass');
147
+ }
148
+ }
149
+
150
+ module.exports = { AdminProcessRepositoryInterface };
@@ -0,0 +1,213 @@
1
+ const { prisma } = require('../../database/prisma');
2
+ const {
3
+ AdminProcessRepositoryInterface,
4
+ } = require('./admin-process-repository-interface');
5
+
6
+ /**
7
+ * MongoDB Admin Process Repository Adapter
8
+ * Handles admin process persistence using Prisma with MongoDB
9
+ *
10
+ * MongoDB-specific characteristics:
11
+ * - IDs are strings with @db.ObjectId
12
+ * - context and results are Json objects
13
+ * - Stores logs in results.logs array
14
+ */
15
+ class AdminProcessRepositoryMongo extends AdminProcessRepositoryInterface {
16
+ constructor() {
17
+ super();
18
+ this.prisma = prisma;
19
+ }
20
+
21
+ /**
22
+ * Create a new admin process record
23
+ *
24
+ * @param {Object} params - Process creation parameters
25
+ * @param {string} params.name - Name of the process
26
+ * @param {string} params.type - Type of process (e.g., 'ADMIN_SCRIPT', 'DB_MIGRATION')
27
+ * @param {Object} [params.context] - Context data
28
+ * @returns {Promise<Object>} The created process record
29
+ */
30
+ async createProcess({ name, type, context = {} }) {
31
+ const data = {
32
+ name,
33
+ type,
34
+ context,
35
+ results: { logs: [] },
36
+ };
37
+
38
+ const process = await this.prisma.adminProcess.create({
39
+ data,
40
+ });
41
+
42
+ return process;
43
+ }
44
+
45
+ /**
46
+ * Find a process by its ID
47
+ *
48
+ * @param {string} id - The process ID
49
+ * @returns {Promise<Object|null>} The process record or null if not found
50
+ */
51
+ async findProcessById(id) {
52
+ const process = await this.prisma.adminProcess.findUnique({
53
+ where: { id },
54
+ });
55
+
56
+ return process;
57
+ }
58
+
59
+ /**
60
+ * Find all processes with a specific name
61
+ *
62
+ * @param {string} name - The process name to filter by
63
+ * @param {Object} [options] - Query options
64
+ * @param {number} [options.limit] - Maximum number of results
65
+ * @param {number} [options.offset] - Number of results to skip
66
+ * @param {string} [options.sortBy] - Field to sort by
67
+ * @param {string} [options.sortOrder] - Sort order ('asc' or 'desc')
68
+ * @returns {Promise<Array>} Array of process records
69
+ */
70
+ async findProcessesByName(name, options = {}) {
71
+ const { limit, offset, sortBy = 'createdAt', sortOrder = 'desc' } = options;
72
+
73
+ const processes = await this.prisma.adminProcess.findMany({
74
+ where: { name },
75
+ orderBy: { [sortBy]: sortOrder },
76
+ take: limit,
77
+ skip: offset,
78
+ });
79
+
80
+ return processes;
81
+ }
82
+
83
+ /**
84
+ * Find all processes with a specific state
85
+ *
86
+ * @param {string} state - State to filter by
87
+ * @param {Object} [options] - Query options
88
+ * @param {number} [options.limit] - Maximum number of results
89
+ * @param {number} [options.offset] - Number of results to skip
90
+ * @param {string} [options.sortBy] - Field to sort by
91
+ * @param {string} [options.sortOrder] - Sort order ('asc' or 'desc')
92
+ * @returns {Promise<Array>} Array of process records
93
+ */
94
+ async findProcessesByState(state, options = {}) {
95
+ const { limit, offset, sortBy = 'createdAt', sortOrder = 'desc' } = options;
96
+
97
+ const processes = await this.prisma.adminProcess.findMany({
98
+ where: { state },
99
+ orderBy: { [sortBy]: sortOrder },
100
+ take: limit,
101
+ skip: offset,
102
+ });
103
+
104
+ return processes;
105
+ }
106
+
107
+ /**
108
+ * Update the state of a process
109
+ *
110
+ * @param {string} id - The process ID
111
+ * @param {string} state - New state value
112
+ * @returns {Promise<Object>} Updated process record
113
+ */
114
+ async updateProcessState(id, state) {
115
+ const process = await this.prisma.adminProcess.update({
116
+ where: { id },
117
+ data: { state },
118
+ });
119
+
120
+ return process;
121
+ }
122
+
123
+ /**
124
+ * Update the results of a process
125
+ * Merges new results with existing results
126
+ *
127
+ * @param {string} id - The process ID
128
+ * @param {Object} results - Results data to merge
129
+ * @returns {Promise<Object>} Updated process record
130
+ */
131
+ async updateProcessResults(id, results) {
132
+ // Get current process to merge results
133
+ const currentProcess = await this.prisma.adminProcess.findUnique({
134
+ where: { id },
135
+ });
136
+
137
+ if (!currentProcess) {
138
+ throw new Error(`AdminProcess ${id} not found`);
139
+ }
140
+
141
+ // Merge new results with existing results
142
+ const mergedResults = {
143
+ ...(currentProcess.results || {}),
144
+ ...results,
145
+ };
146
+
147
+ const process = await this.prisma.adminProcess.update({
148
+ where: { id },
149
+ data: { results: mergedResults },
150
+ });
151
+
152
+ return process;
153
+ }
154
+
155
+ /**
156
+ * Append a log entry to a process's log array in results
157
+ *
158
+ * @param {string} id - The process ID
159
+ * @param {Object} logEntry - Log entry to append
160
+ * @param {string} logEntry.level - Log level ('debug', 'info', 'warn', 'error')
161
+ * @param {string} logEntry.message - Log message
162
+ * @param {Object} [logEntry.data] - Additional log data
163
+ * @param {string} logEntry.timestamp - ISO timestamp
164
+ * @returns {Promise<Object>} Updated process record
165
+ */
166
+ async appendProcessLog(id, logEntry) {
167
+ // Get current process
168
+ const process = await this.prisma.adminProcess.findUnique({
169
+ where: { id },
170
+ });
171
+
172
+ if (!process) {
173
+ throw new Error(`AdminProcess ${id} not found`);
174
+ }
175
+
176
+ // Get current results and logs
177
+ const results = process.results || {};
178
+ const logs = Array.isArray(results.logs) ? [...results.logs] : [];
179
+ logs.push(logEntry);
180
+
181
+ // Update with new logs array in results
182
+ const updated = await this.prisma.adminProcess.update({
183
+ where: { id },
184
+ data: { results: { ...results, logs } },
185
+ });
186
+
187
+ return updated;
188
+ }
189
+
190
+ /**
191
+ * Delete all processes older than a specific date
192
+ * Used for cleanup and retention policies
193
+ *
194
+ * @param {Date} date - Delete processes older than this date
195
+ * @returns {Promise<Object>} Deletion result with count
196
+ */
197
+ async deleteProcessesOlderThan(date) {
198
+ const result = await this.prisma.adminProcess.deleteMany({
199
+ where: {
200
+ createdAt: {
201
+ lt: date,
202
+ },
203
+ },
204
+ });
205
+
206
+ return {
207
+ acknowledged: true,
208
+ deletedCount: result.count,
209
+ };
210
+ }
211
+ }
212
+
213
+ module.exports = { AdminProcessRepositoryMongo };