@friggframework/core 2.0.0-next.84 → 2.0.0-next.86

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.
@@ -560,13 +560,25 @@ class IntegrationBase {
560
560
  * @returns {Promise<void>}
561
561
  */
562
562
  async receiveNotification(notifier, delegateString, object = null) {
563
- if (delegateString !== 'CREDENTIAL_INVALIDATED') return;
564
563
  if (!this.id) return;
565
- console.log(
566
- `[Frigg] Module ${notifier?.name || '?'} reported invalid credentials for integration ${this.id} — marking ERROR`
567
- );
568
- await this.updateIntegrationStatus.execute(this.id, 'ERROR');
569
- this.status = 'ERROR';
564
+
565
+ if (delegateString === 'CREDENTIAL_INVALIDATED') {
566
+ console.log(
567
+ `[Frigg] Module ${notifier?.name || '?'} reported invalid credentials for integration ${this.id} — marking ERROR`
568
+ );
569
+ await this.updateIntegrationStatus.execute(this.id, 'ERROR');
570
+ this.status = 'ERROR';
571
+ return;
572
+ }
573
+
574
+ if (delegateString === 'CREDENTIAL_VALIDATED') {
575
+ if (this.status !== 'ERROR') return;
576
+ console.log(
577
+ `[Frigg] Module ${notifier?.name || '?'} reported valid credentials for integration ${this.id} — clearing ERROR → ENABLED`
578
+ );
579
+ await this.updateIntegrationStatus.execute(this.id, 'ENABLED');
580
+ this.status = 'ENABLED';
581
+ }
570
582
  }
571
583
  }
572
584
 
@@ -28,8 +28,9 @@
28
28
  * const updateMetrics = new UpdateProcessMetrics({ processRepository, websocketService });
29
29
  * await updateMetrics.execute(processId, {
30
30
  * processed: 100,
31
- * success: 95,
31
+ * success: 92,
32
32
  * errors: 5,
33
+ * skipped: 3,
33
34
  * errorDetails: [{ contactId: 'abc', error: 'Missing email', timestamp: '...' }]
34
35
  * });
35
36
  */
@@ -54,6 +55,11 @@ class UpdateProcessMetrics {
54
55
  * @param {number} [metricsUpdate.processed=0] - Records processed in this batch
55
56
  * @param {number} [metricsUpdate.success=0] - Successful records
56
57
  * @param {number} [metricsUpdate.errors=0] - Failed records
58
+ * @param {number} [metricsUpdate.skipped=0] - Intentionally-skipped
59
+ * records (hash-match, dedupe, loop protection, etc.). Increments
60
+ * `results.aggregateData.totalSkipped`. Distinct from errors so the
61
+ * UI can show `processed = synced + failed + skipped` without
62
+ * conflating intentional skips with failures.
57
63
  * @param {Array} [metricsUpdate.errorDetails=[]] - Error details array
58
64
  * @returns {Promise<Object>} Updated process record
59
65
  * @throws {Error} If process not found or update fails
@@ -71,9 +77,11 @@ class UpdateProcessMetrics {
71
77
  const processed = metricsUpdate.processed || 0;
72
78
  const success = metricsUpdate.success || 0;
73
79
  const errors = metricsUpdate.errors || 0;
80
+ const skipped = metricsUpdate.skipped || 0;
74
81
  if (processed) increment['context.processedRecords'] = processed;
75
82
  if (success) increment['results.aggregateData.totalSynced'] = success;
76
83
  if (errors) increment['results.aggregateData.totalFailed'] = errors;
84
+ if (skipped) increment['results.aggregateData.totalSkipped'] = skipped;
77
85
 
78
86
  const pushSlice = {};
79
87
  if (
@@ -191,6 +199,7 @@ class UpdateProcessMetrics {
191
199
  total: context.totalRecords || 0,
192
200
  successCount: aggregateData.totalSynced || 0,
193
201
  errorCount: aggregateData.totalFailed || 0,
202
+ skippedCount: aggregateData.totalSkipped || 0,
194
203
  recordsPerSecond: aggregateData.recordsPerSecond || 0,
195
204
  estimatedCompletion: context.estimatedCompletion || null,
196
205
  timestamp: new Date().toISOString(),
package/modules/module.js CHANGED
@@ -41,6 +41,8 @@ class Module extends Delegate {
41
41
  // Module → parent delegate (typically IntegrationBase) events
42
42
  this.DLGT_CREDENTIAL_INVALIDATED = 'CREDENTIAL_INVALIDATED';
43
43
  this.delegateTypes.push(this.DLGT_CREDENTIAL_INVALIDATED);
44
+ this.DLGT_CREDENTIAL_VALIDATED = 'CREDENTIAL_VALIDATED';
45
+ this.delegateTypes.push(this.DLGT_CREDENTIAL_VALIDATED);
44
46
 
45
47
  Object.assign(this, this.definition.requiredAuthMethods);
46
48
 
@@ -123,6 +125,20 @@ class Module extends Delegate {
123
125
  credentialDetails
124
126
  );
125
127
  this.credential = persisted;
128
+
129
+ if (this.credential?.id) {
130
+ try {
131
+ await this.notify(this.DLGT_CREDENTIAL_VALIDATED, {
132
+ credentialId: this.credential.id,
133
+ moduleName: this.name,
134
+ });
135
+ } catch (err) {
136
+ console.error(
137
+ `[Frigg] Failed to propagate CREDENTIAL_VALIDATED for module ${this.name}:`,
138
+ err?.message || err
139
+ );
140
+ }
141
+ }
126
142
  }
127
143
 
128
144
  async receiveNotification(notifier, delegateString, object = null) {
@@ -151,9 +151,9 @@ class Requester extends Delegate {
151
151
  await new Promise((resolve) => setTimeout(resolve, delay));
152
152
  return this._request(url, options, i + 1);
153
153
  } else if (status === 401) {
154
- if (!this.isRefreshable || this.refreshCount > 0) {
154
+ if (!this.isRefreshable) {
155
155
  await this.notify(this.DLGT_INVALID_AUTH);
156
- } else {
156
+ } else if (this.refreshCount === 0) {
157
157
  this.refreshCount++;
158
158
  const refreshSucceeded = await this.refreshAuth();
159
159
  if (refreshSucceeded) {
@@ -178,6 +178,11 @@ class Requester extends Delegate {
178
178
  );
179
179
  }
180
180
 
181
+ // Successful response: reset the per-instance refresh budget so
182
+ // a later 401 in the same Requester lifetime can attempt refresh
183
+ // again instead of silently falling through.
184
+ this.refreshCount = 0;
185
+
181
186
  // parsedBody consumes the response body stream. If the server
182
187
  // stalls mid-stream the timer (still armed) aborts it.
183
188
  return options.returnFullRes
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.84",
4
+ "version": "2.0.0-next.86",
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.84",
42
- "@friggframework/prettier-config": "2.0.0-next.84",
43
- "@friggframework/test": "2.0.0-next.84",
41
+ "@friggframework/eslint-config": "2.0.0-next.86",
42
+ "@friggframework/prettier-config": "2.0.0-next.86",
43
+ "@friggframework/test": "2.0.0-next.86",
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": "c0b32d4d1f558fc8f737fd2731f0d67e84509ca2"
83
+ "gitHead": "8862a43e3aa4be518fa1b35da41b76129b617a2e"
84
84
  }