@friggframework/core 2.0.0--canary.654.ebad223.0 → 2.0.0--canary.651.dadf4d6.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.
- package/.eslintrc.json +1 -18
- package/CLAUDE.md +62 -14
- package/README.md +22 -19
- package/core/CLAUDE.md +12 -25
- package/core/Worker.js +37 -31
- package/core/create-handler.js +197 -96
- package/core/index.js +1 -8
- package/core/secrets-to-env.js +6 -7
- package/database/config.js +7 -0
- package/database/documentdb-encryption-service.js +6 -2
- package/database/documentdb-utils.js +2 -1
- package/database/encryption/README.md +21 -5
- package/database/encryption/encryption-schema-registry.js +0 -3
- package/database/encryption/integration-mapping-encryption.js +97 -0
- package/database/encryption/prisma-encryption-extension.js +20 -9
- package/errors/fetch-error.js +65 -48
- package/handlers/app-definition-loader.js +2 -53
- package/handlers/app-handler-helpers.js +4 -18
- package/handlers/backend-utils.js +5 -0
- package/handlers/database-migration-handler.js +1 -10
- package/handlers/routers/websocket.js +6 -26
- package/handlers/workers/db-migration.js +1 -12
- package/handlers/workers/dlq-processor.js +38 -64
- package/index.js +1 -19
- package/integrations/integration-base.js +38 -56
- package/integrations/integration-router.js +20 -21
- package/integrations/repositories/integration-mapping-query-pipeline.js +152 -0
- package/integrations/repositories/integration-mapping-query.js +193 -0
- package/integrations/repositories/integration-mapping-repository-documentdb.js +53 -0
- package/integrations/repositories/integration-mapping-repository-interface.js +48 -0
- package/integrations/repositories/integration-mapping-repository-mongo.js +72 -0
- package/integrations/repositories/integration-mapping-repository-postgres.js +118 -0
- package/integrations/use-cases/delete-integration-for-user.js +13 -24
- package/jest.config.js +0 -2
- package/logs/.eslintrc.json +1 -26
- package/logs/index.js +3 -14
- package/logs/logger.js +42 -132
- package/modules/module.js +19 -32
- package/modules/requester/oauth-2.js +22 -28
- package/modules/requester/requester.js +6 -69
- package/package.json +6 -5
- package/queues/queuer-util.js +15 -1
- package/telemetry/bind-telemetry-context.js +0 -4
- package/telemetry/instrument-handler.js +2 -19
- package/telemetry/no-op-telemetry.js +0 -4
- package/telemetry/otel-telemetry.js +6 -25
- package/telemetry/telemetry-context.js +8 -19
- package/telemetry/telemetry-runtime.js +0 -17
- package/telemetry/telemetry-service.js +0 -1
- package/types/errors/index.d.ts +10 -24
- package/types/logs/index.d.ts +2 -61
- package/user/repositories/user-repository-documentdb.js +40 -8
- package/core/invocation-scope.js +0 -224
- package/logs/__fixtures__/events.js +0 -82
- package/logs/__fixtures__/matchers.js +0 -45
- package/logs/__fixtures__/secrets.js +0 -22
- package/logs/__fixtures__/vectors.js +0 -329
- package/logs/__fixtures__/with-env.js +0 -45
- package/logs/context.js +0 -92
- package/logs/debug-shims.js +0 -46
- package/logs/jest-logger-setup.js +0 -25
- package/logs/levels.js +0 -71
- package/logs/logger-runtime.js +0 -142
- package/logs/record.js +0 -231
- package/logs/redact.js +0 -333
- package/logs/serialize.js +0 -322
- package/logs/sinks.js +0 -99
- package/logs/summarize-event.js +0 -162
package/.eslintrc.json
CHANGED
|
@@ -1,20 +1,3 @@
|
|
|
1
1
|
{
|
|
2
|
-
"extends": "@friggframework/eslint-config"
|
|
3
|
-
"rules": {
|
|
4
|
-
"no-restricted-syntax": [
|
|
5
|
-
"warn",
|
|
6
|
-
{
|
|
7
|
-
"selector": "CallExpression[callee.object.object.name='process'][callee.object.property.name=/^(stdout|stderr)$/][callee.property.name='write']",
|
|
8
|
-
"message": "Write through the logger; only logs/sinks.js writes to fd 1 (ADR-048 §14)"
|
|
9
|
-
}
|
|
10
|
-
]
|
|
11
|
-
},
|
|
12
|
-
"overrides": [
|
|
13
|
-
{
|
|
14
|
-
"files": ["*.test.js", "database/utils/prisma-runner.js"],
|
|
15
|
-
"rules": {
|
|
16
|
-
"no-restricted-syntax": "off"
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
]
|
|
2
|
+
"extends": "@friggframework/eslint-config"
|
|
20
3
|
}
|
package/CLAUDE.md
CHANGED
|
@@ -211,7 +211,56 @@ packages/core/
|
|
|
211
211
|
- `integration-repository-factory.js` - Creates database-specific repositories
|
|
212
212
|
- `integration-repository-mongo.js` - MongoDB implementation
|
|
213
213
|
- `integration-repository-postgres.js` - PostgreSQL implementation
|
|
214
|
-
- `integration-mapping-repository-*.js` - Mapping data persistence
|
|
214
|
+
- `integration-mapping-repository-*.js` - Mapping data persistence.
|
|
215
|
+
`queryMappings(integrationId, { where, orderBy, skip, take, omit })`
|
|
216
|
+
returns `{ mappings, total }`: one filtered, ordered page of an
|
|
217
|
+
integration's mappings, for callers that cannot load every row through
|
|
218
|
+
`findMappingsByIntegration`. `where` is an array of ANDed conditions
|
|
219
|
+
(at most 20); an entry may be `{ anyOf: [...] }`, ORed, one level deep.
|
|
220
|
+
Conditions are `{ path: 'mapping.<segment>...', op: 'exists' | 'notExists' }`
|
|
221
|
+
(JSON null counts as absent), `{ path: 'mapping.…', op: 'in', value: string[] }`
|
|
222
|
+
(1–500 strings) and `{ path: 'sourceId', op: 'notStartsWith', value }`
|
|
223
|
+
(a NULL sourceId matches). `orderBy` is `{ path: 'mapping.…', direction:
|
|
224
|
+
'asc' | 'desc' }`, nulls last, ties broken by id in the same direction;
|
|
225
|
+
without it rows come in id order. `take` is 1–500. `omit` lists top-level
|
|
226
|
+
mapping keys to leave out of the rows; never write such rows back. Path
|
|
227
|
+
segments must match `^[A-Za-z_][A-Za-z0-9_]*$`. The PostgreSQL, MongoDB and
|
|
228
|
+
DocumentDB adapters give the same pages and totals;
|
|
229
|
+
`integration-mapping-repository-query-parity.test.js` checks that against
|
|
230
|
+
real databases when `QUERY_MAPPINGS_PARITY_MONGO_URL` /
|
|
231
|
+
`QUERY_MAPPINGS_PARITY_POSTGRES_URL` are set. Two orderings still differ:
|
|
232
|
+
strings compare by the database collation on PostgreSQL and by code point
|
|
233
|
+
on MongoDB and DocumentDB, and arrays or objects at the sort path order
|
|
234
|
+
among themselves only on PostgreSQL (the other adapters fall back to id).
|
|
235
|
+
The legacy `IntegrationMappingRepository` inherits the port's
|
|
236
|
+
"not supported by this database adapter yet" error. Every adapter refuses
|
|
237
|
+
to run while field-level encryption still encrypts
|
|
238
|
+
`IntegrationMapping.mapping` on write (see `database/encryption/README.md`),
|
|
239
|
+
and returns rows decrypted like `findMappingsByIntegration`. PostgreSQL and
|
|
240
|
+
MongoDB decrypt through `decryptQueriedMappings`
|
|
241
|
+
(`database/encryption/integration-mapping-encryption.js`), a no-op while
|
|
242
|
+
encryption is off or lists no `IntegrationMapping` field besides `mapping`.
|
|
243
|
+
Validation lives in `integration-mapping-query.js`; a new operator is one
|
|
244
|
+
entry in its `OPERATORS` table, one in the Postgres adapter's
|
|
245
|
+
`CONDITION_SQL` and one in `CONDITION_EXPRESSIONS` in
|
|
246
|
+
`integration-mapping-query-pipeline.js`, the aggregation stages both
|
|
247
|
+
MongoDB-protocol adapters share. Those stages may only use what Amazon
|
|
248
|
+
DocumentDB 4.0 and 5.0 support (no `$facet`, `$getField`, `$set` or
|
|
249
|
+
`$unset`).
|
|
250
|
+
**Cost**: PostgreSQL answers in one SQL statement, which reads every row
|
|
251
|
+
of the integration and evaluates the JSON paths per row, because no JSON
|
|
252
|
+
index exists. With ~4 KB mappings on PostgreSQL 16 that is about 0.3 s per
|
|
253
|
+
call at 10⁴ rows per integration and 2.5–3 s at 10⁵. MongoDB answers in one
|
|
254
|
+
aggregate: the `integrationId` index narrows `$match` to the integration,
|
|
255
|
+
the `$expr` is then evaluated per document, and `$facet` returns the page
|
|
256
|
+
with its `$count`, so a page (after `omit`) must fit the 16 MB document
|
|
257
|
+
limit. DocumentDB has no `$facet`: the page and the count are two
|
|
258
|
+
concurrent aggregates, so the total does not come from the same snapshot
|
|
259
|
+
as the page. Both sort in memory on a computed key (`allowDiskUse`). With
|
|
260
|
+
~4 KB mappings on MongoDB 7 that is about 0.05–0.1 s per call at 10⁴ rows
|
|
261
|
+
per integration and 0.5–0.7 s at 10⁵ (the DocumentDB pipeline, run on
|
|
262
|
+
MongoDB, 0.6–0.8 s). On every adapter a deep offset or an empty page past
|
|
263
|
+
the end costs about the same as the first page.
|
|
215
264
|
- `process-repository-*.js` - Process (long-running job) persistence.
|
|
216
265
|
Implements `applyProcessUpdate(processId, ops)` — a race-safe alternative
|
|
217
266
|
to `update(id, patch)` that routes increments, sets, and bounded-array
|
|
@@ -437,24 +486,22 @@ if (!userId) {
|
|
|
437
486
|
|
|
438
487
|
### 9. Logging System (`/logs`)
|
|
439
488
|
|
|
440
|
-
**Purpose**:
|
|
489
|
+
**Purpose**: Structured logging with debug capabilities.
|
|
441
490
|
|
|
442
|
-
**
|
|
443
|
-
- `
|
|
444
|
-
-
|
|
445
|
-
-
|
|
446
|
-
- Log an error one time, at the boundary (`createHandler`, the express middleware, `Worker.run`, the DLQ processor). Inner code throws with `cause`.
|
|
447
|
-
- Use `statusCode`, not `status` (reserved key). `body`, `payload`, `response` are dropped at INFO and above.
|
|
448
|
-
- Tests assert on `createMemorySink()` records by `eventName`, not on console spies.
|
|
449
|
-
- `debug`, `initDebugLog`, `flushDebugLog` are deprecated shims.
|
|
491
|
+
**Functions**:
|
|
492
|
+
- `debug(message, data)` - Debug logging
|
|
493
|
+
- `initDebugLog(eventName, event)` - Initialize debug context
|
|
494
|
+
- `flushDebugLog(error)` - Flush logs on error
|
|
450
495
|
|
|
451
496
|
**Usage**:
|
|
452
497
|
|
|
453
498
|
```javascript
|
|
454
|
-
const {
|
|
455
|
-
const log = getLogger('frigg.core.sync');
|
|
499
|
+
const { debug, initDebugLog, flushDebugLog } = require('@friggframework/core');
|
|
456
500
|
|
|
457
|
-
|
|
501
|
+
initDebugLog('MyIntegration', event);
|
|
502
|
+
debug('Processing request', { userId, action });
|
|
503
|
+
// ... your code ...
|
|
504
|
+
flushDebugLog(); // On error
|
|
458
505
|
```
|
|
459
506
|
|
|
460
507
|
### 10. Lambda Utilities (`/lambda`)
|
|
@@ -662,7 +709,8 @@ Use test doubles from `@friggframework/test` package for consistent mocking.
|
|
|
662
709
|
### Optional
|
|
663
710
|
|
|
664
711
|
- `SECRET_ARN` - AWS Secrets Manager ARN for auto-injection
|
|
665
|
-
- `
|
|
712
|
+
- `DEBUG` - Debug logging pattern
|
|
713
|
+
- `LOG_LEVEL` - Logging level (debug, info, warn, error)
|
|
666
714
|
|
|
667
715
|
## Version Information
|
|
668
716
|
|
package/README.md
CHANGED
|
@@ -107,6 +107,10 @@ FRIGG_ENCRYPTION_KEY=your-256-bit-encryption-key
|
|
|
107
107
|
AWS_REGION=us-east-1
|
|
108
108
|
AWS_ACCESS_KEY_ID=your-access-key
|
|
109
109
|
AWS_SECRET_ACCESS_KEY=your-secret-key
|
|
110
|
+
|
|
111
|
+
# Logging
|
|
112
|
+
DEBUG=frigg:*
|
|
113
|
+
LOG_LEVEL=info
|
|
110
114
|
```
|
|
111
115
|
|
|
112
116
|
## Core Components
|
|
@@ -251,12 +255,10 @@ const {
|
|
|
251
255
|
// Custom business logic error
|
|
252
256
|
throw new RequiredPropertyError('userId is required');
|
|
253
257
|
|
|
254
|
-
// API communication error
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
init: { method: 'GET' },
|
|
259
|
-
response,
|
|
258
|
+
// API communication error
|
|
259
|
+
throw new FetchError('Failed to fetch data from external API', {
|
|
260
|
+
statusCode: 404,
|
|
261
|
+
response: errorResponse
|
|
260
262
|
});
|
|
261
263
|
|
|
262
264
|
// Base error with custom properties
|
|
@@ -268,25 +270,25 @@ throw new BaseError('Integration failed', {
|
|
|
268
270
|
|
|
269
271
|
### 6. Logging (`/logs`)
|
|
270
272
|
|
|
271
|
-
|
|
272
|
-
ADR-011. See the [Logging guide](../../docs/guides/LOGGING.md).
|
|
273
|
+
Structured logging with debug capabilities.
|
|
273
274
|
|
|
274
275
|
**Usage:**
|
|
275
276
|
```javascript
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
277
|
+
const { debug, initDebugLog, flushDebugLog } = require('@friggframework/core');
|
|
278
|
+
|
|
279
|
+
// Initialize debug logging
|
|
280
|
+
initDebugLog('integration:slack');
|
|
281
|
+
|
|
282
|
+
// Log debug information
|
|
283
|
+
debug('Processing webhook payload', {
|
|
284
|
+
eventType: 'contact.created',
|
|
285
|
+
payload: webhookData
|
|
280
286
|
});
|
|
281
287
|
|
|
282
|
-
//
|
|
283
|
-
|
|
284
|
-
const log = getLogger('frigg.core.sync');
|
|
285
|
-
log.warn('Sync skipped', { eventName: 'frigg.core.sync.skipped', processId });
|
|
288
|
+
// Flush logs (useful in serverless environments)
|
|
289
|
+
await flushDebugLog();
|
|
286
290
|
```
|
|
287
291
|
|
|
288
|
-
`debug`, `initDebugLog` and `flushDebugLog` are deprecated shims.
|
|
289
|
-
|
|
290
292
|
### 7. User Management (`/user`)
|
|
291
293
|
|
|
292
294
|
Comprehensive user authentication and authorization system supporting both individual and organizational users.
|
|
@@ -1011,7 +1013,8 @@ const {
|
|
|
1011
1013
|
| `MONGO_URI` | Yes | MongoDB connection string |
|
|
1012
1014
|
| `FRIGG_ENCRYPTION_KEY` | Yes | 256-bit encryption key |
|
|
1013
1015
|
| `AWS_REGION` | No | AWS region for services |
|
|
1014
|
-
| `
|
|
1016
|
+
| `DEBUG` | No | Debug logging pattern |
|
|
1017
|
+
| `LOG_LEVEL` | No | Logging level (debug, info, warn, error) |
|
|
1015
1018
|
|
|
1016
1019
|
## License
|
|
1017
1020
|
|
package/core/CLAUDE.md
CHANGED
|
@@ -21,13 +21,13 @@ This file provides guidance to Claude Code when working with the Frigg Framework
|
|
|
21
21
|
- **Database Connection Management**: Automatic MongoDB connection with pooling
|
|
22
22
|
- **Secrets Management**: AWS Secrets Manager integration via `SECRET_ARN` env var
|
|
23
23
|
- **Error Sanitization**: Prevents internal details from leaking to end users
|
|
24
|
-
- **
|
|
24
|
+
- **Debug Logging**: Request/response logging with structured debug info
|
|
25
25
|
- **Connection Optimization**: `context.callbackWaitsForEmptyEventLoop = false` for reuse
|
|
26
26
|
|
|
27
27
|
**Handler Configuration Options**:
|
|
28
28
|
```javascript
|
|
29
29
|
const handler = createHandler({
|
|
30
|
-
eventName: 'MyIntegration', //
|
|
30
|
+
eventName: 'MyIntegration', // For logging/debugging
|
|
31
31
|
isUserFacingResponse: true, // true = sanitize errors, false = pass through
|
|
32
32
|
method: async (event, context) => {}, // Your Lambda function logic
|
|
33
33
|
shouldUseDatabase: true // false = skip MongoDB connection
|
|
@@ -129,8 +129,7 @@ class MyIntegration extends Delegate {
|
|
|
129
129
|
### Lambda Handler Lifecycle
|
|
130
130
|
1. **Pre-Execution Setup**:
|
|
131
131
|
```javascript
|
|
132
|
-
|
|
133
|
-
log.info('Handler invoked', { eventName: 'frigg.handler.invoked' });
|
|
132
|
+
initDebugLog(eventName, event); // Debug logging setup
|
|
134
133
|
await secretsToEnv(); // Secrets Manager injection
|
|
135
134
|
await parametersToEnv(); // SSM Parameter Store fetch (only when SSM_PARAMETER_PREFIX + FRIGG_SSM_OFFLOADED_KEYS are set)
|
|
136
135
|
context.callbackWaitsForEmptyEventLoop = false; // Connection pooling
|
|
@@ -150,20 +149,12 @@ class MyIntegration extends Delegate {
|
|
|
150
149
|
|
|
151
150
|
4. **Error Handling & Cleanup**:
|
|
152
151
|
```javascript
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
// Sanitized error response for user-facing endpoints; server-to-server
|
|
156
|
-
// errors are rethrown as a sanitized surrogate (toSanitizedSurrogate).
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
5. **Flush** (in `runInvocationScope`'s `finally`):
|
|
160
|
-
```javascript
|
|
161
|
-
// Usage rollup first (unbounded), then telemetry + log sinks in parallel
|
|
162
|
-
// under one deadline: min(flushTimeoutMs, remaining time - 50 ms).
|
|
152
|
+
flushDebugLog(error); // Debug info flush on error
|
|
153
|
+
// Sanitized error response for user-facing endpoints
|
|
163
154
|
```
|
|
164
155
|
|
|
165
156
|
### SQS Job Processing Lifecycle
|
|
166
|
-
1. **Batch Processing**: Process all records in `event.Records` sequentially
|
|
157
|
+
1. **Batch Processing**: Process all records in `event.Records` sequentially
|
|
167
158
|
2. **Message Parsing**: JSON.parse message body for parameters
|
|
168
159
|
3. **Validation**: Run custom validation on parsed parameters
|
|
169
160
|
4. **Execution**: Call `_run()` method with validated parameters
|
|
@@ -214,9 +205,7 @@ const handler = createHandler({
|
|
|
214
205
|
- Logs full error details internally
|
|
215
206
|
|
|
216
207
|
2. **Server-to-Server Errors**: `isUserFacingResponse: false`
|
|
217
|
-
-
|
|
218
|
-
a fresh `Error` with the sanitized `name`, `message`, `stack`, plus
|
|
219
|
-
`statusCode` and `code`. `instanceof` checks and custom properties are gone
|
|
208
|
+
- Re-throws original error for AWS handling
|
|
220
209
|
- Used for SQS, SNS, and internal API calls
|
|
221
210
|
- Enables proper retry mechanisms
|
|
222
211
|
|
|
@@ -225,14 +214,12 @@ const handler = createHandler({
|
|
|
225
214
|
- Prevents infinite retries for known issues
|
|
226
215
|
- Used for graceful degradation scenarios
|
|
227
216
|
|
|
228
|
-
### Logging Strategy
|
|
217
|
+
### Debug Logging Strategy
|
|
229
218
|
```javascript
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
// Throw with cause; the boundary logs the error one time.
|
|
219
|
+
initDebugLog(eventName, event); // Start logging context
|
|
220
|
+
// ... your code ...
|
|
221
|
+
flushDebugLog(error); // Flush on error (includes full context)
|
|
234
222
|
```
|
|
235
|
-
See `docs/guides/LOGGING.md`.
|
|
236
223
|
|
|
237
224
|
## Integration Development Patterns
|
|
238
225
|
|
|
@@ -659,7 +646,7 @@ describe('Health Handler', () => {
|
|
|
659
646
|
const { createHandler } = require('@friggframework/core/core');
|
|
660
647
|
|
|
661
648
|
const testHandler = createHandler({
|
|
662
|
-
isUserFacingResponse: false, //
|
|
649
|
+
isUserFacingResponse: false, // Get full errors in tests
|
|
663
650
|
shouldUseDatabase: false, // Mock/skip DB in tests
|
|
664
651
|
method: yourTestMethod
|
|
665
652
|
});
|
package/core/Worker.js
CHANGED
|
@@ -2,11 +2,8 @@ const { SQSClient, GetQueueUrlCommand, SendMessageCommand } = require('@aws-sdk/
|
|
|
2
2
|
const _ = require('lodash');
|
|
3
3
|
const { RequiredPropertyError } = require('../errors');
|
|
4
4
|
const { get } = require('../assertions');
|
|
5
|
-
const { runMessageScope } = require('./invocation-scope');
|
|
6
|
-
const { getLogger } = require('../logs');
|
|
7
5
|
|
|
8
6
|
const sqs = new SQSClient({ region: process.env.AWS_REGION });
|
|
9
|
-
const log = getLogger('frigg.worker');
|
|
10
7
|
|
|
11
8
|
class Worker {
|
|
12
9
|
async getQueueURL(params) {
|
|
@@ -28,36 +25,45 @@ class Worker {
|
|
|
28
25
|
);
|
|
29
26
|
|
|
30
27
|
for (const record of records) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
28
|
+
// Log record entry with SQS-provided attributes useful for tracing
|
|
29
|
+
// delivery history (ApproximateReceiveCount for retries, etc.).
|
|
30
|
+
let parsedEvent;
|
|
31
|
+
try {
|
|
32
|
+
parsedEvent = JSON.parse(record.body)?.event;
|
|
33
|
+
} catch {
|
|
34
|
+
parsedEvent = undefined;
|
|
35
|
+
}
|
|
36
|
+
console.log(`[Worker] record begin`, {
|
|
37
|
+
messageId: record.messageId,
|
|
38
|
+
event: parsedEvent,
|
|
39
|
+
receiveCount: record.attributes?.ApproximateReceiveCount,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const runParams = JSON.parse(record.body);
|
|
44
|
+
this._validateParams(runParams);
|
|
45
|
+
await this._run(runParams, context);
|
|
46
|
+
console.log(`[Worker] record success`, {
|
|
47
|
+
messageId: record.messageId,
|
|
48
|
+
event: runParams?.event,
|
|
49
|
+
});
|
|
50
|
+
} catch (error) {
|
|
51
|
+
if (error.isHaltError) {
|
|
52
|
+
// HaltError means "discard this message, don't retry".
|
|
53
|
+
// Treat as success so SQS deletes it from the queue.
|
|
54
|
+
// Logged explicitly — silent discards made prod debugging
|
|
55
|
+
// extremely hard; keep this visible.
|
|
56
|
+
console.warn(`[Worker] record halted (discarded, no retry)`, {
|
|
57
|
+
messageId: record.messageId,
|
|
58
|
+
event: parsedEvent,
|
|
59
|
+
reason: error.message,
|
|
60
|
+
statusCode: error.statusCode,
|
|
57
61
|
});
|
|
58
|
-
|
|
62
|
+
continue;
|
|
59
63
|
}
|
|
60
|
-
|
|
64
|
+
console.error(`[Worker] Failed to process record ${record.messageId}:`, error);
|
|
65
|
+
batchItemFailures.push({ itemIdentifier: record.messageId });
|
|
66
|
+
}
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
if (batchItemFailures.length > 0) {
|