@friggframework/core 2.0.0--canary.711.f8b6445.0 → 2.0.0--canary.710.91495fa.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/CLAUDE.md CHANGED
@@ -224,7 +224,7 @@ packages/core/
224
224
  (`remove-entities-from-integration.js`) refuses to leave a module with no
225
225
  entity (`LAST_ENTITY_OF_MODULE`, 409). Re-adding an attached entity or
226
226
  removing a detached one changes nothing; a detached entity is not deleted.
227
- `integration-repository-entities-parity.test.js` runs every adapter against
227
+ `integration-repository-entities-parity.db.test.js` runs every adapter against
228
228
  real databases when `INTEGRATION_ENTITIES_PARITY_MONGO_URL` /
229
229
  `INTEGRATION_ENTITIES_PARITY_POSTGRES_URL` are set. The DocumentDB adapter
230
230
  writes only `Integration.entityIds` (as its `createIntegration` does), not
@@ -266,7 +266,7 @@ packages/core/
266
266
  mapping keys to leave out of the rows; never write such rows back. Path
267
267
  segments must match `^[A-Za-z_][A-Za-z0-9_]*$`. The PostgreSQL, MongoDB and
268
268
  DocumentDB adapters give the same pages and totals;
269
- `integration-mapping-repository-query-parity.test.js` checks that against
269
+ `integration-mapping-repository-query-parity.db.test.js` checks that against
270
270
  real databases when `QUERY_MAPPINGS_PARITY_MONGO_URL` /
271
271
  `QUERY_MAPPINGS_PARITY_POSTGRES_URL` are set. Two orderings still differ:
272
272
  strings compare by the database collation on PostgreSQL and by code point
@@ -67,9 +67,10 @@ class MyIntegration extends IntegrationBase {
67
67
  async hydrateFromExternalUser(externalUserId) {
68
68
  // Find integration context by external entity ID
69
69
  const result =
70
- await this.commands.findIntegrationContextByExternalEntityId(
71
- externalUserId
72
- );
70
+ await this.commands.findIntegrationContextByExternalEntityId({
71
+ externalId: externalUserId,
72
+ type: 'my-integration', // integration config.type to match
73
+ });
73
74
 
74
75
  if (result.error) {
75
76
  return { error: result.error };
@@ -231,9 +232,10 @@ Manage integration records and load full integration contexts.
231
232
  ```javascript
232
233
  // Find integration context by external entity ID
233
234
  // Returns { context, error } where context includes record + hydrated modules
234
- const result = await commands.findIntegrationContextByExternalEntityId(
235
- 'external-user-or-workspace-id'
236
- );
235
+ const result = await commands.findIntegrationContextByExternalEntityId({
236
+ externalId: 'external-user-or-workspace-id',
237
+ type: 'my-integration', // integration config.type to match
238
+ });
237
239
 
238
240
  if (!result.error) {
239
241
  integration.setIntegrationRecord(result.context);
@@ -299,7 +301,10 @@ try {
299
301
  For integration context operations, errors are returned in the result:
300
302
 
301
303
  ```javascript
302
- const result = await commands.findIntegrationContextByExternalEntityId(userId);
304
+ const result = await commands.findIntegrationContextByExternalEntityId({
305
+ externalId: userId,
306
+ type: 'my-integration',
307
+ });
303
308
 
304
309
  if (result.error) {
305
310
  return { error: result.error };
@@ -349,9 +354,10 @@ const result =
349
354
  const { createFriggCommands } = require('@friggframework/core');
350
355
 
351
356
  this.commands = createFriggCommands({ integrationClass: MyIntegration });
352
- const result = await this.commands.findIntegrationContextByExternalEntityId(
353
- userId
354
- );
357
+ const result = await this.commands.findIntegrationContextByExternalEntityId({
358
+ externalId: userId,
359
+ type: 'my-integration',
360
+ });
355
361
  ```
356
362
 
357
363
  ## Best Practices
@@ -433,7 +439,7 @@ if (!user) {
433
439
  | | `findEntitiesByIds(ids)` | Find multiple by IDs |
434
440
  | | `updateEntity(id, updates)` | Update entity properties |
435
441
  | | `deleteEntity(id)` | Delete entity |
436
- | **Integration** | `findIntegrationContextByExternalEntityId(externalId)` | Load integration + modules by external ID |
442
+ | **Integration** | `findIntegrationContextByExternalEntityId({ externalId, type })` | Load integration + modules by external ID |
437
443
  | | `loadIntegrationContextById(integrationId)` | Load integration + modules by ID |
438
444
 
439
445
  ## Support
@@ -508,9 +508,9 @@ return entities.map((e) => ({
508
508
 
509
509
  **Verified:**
510
510
 
511
- - ✅ `postgres-relation-decryption.test.js` - Proves the bug exists
512
- - ✅ `postgres-decryption-fix-verification.test.js` - Verifies separate queries work
513
- - ✅ `mongo-decryption-fix-verification.test.js` - Verifies fix for MongoDB
511
+ - ✅ `postgres-relation-decryption.db.test.js` - Proves the bug exists
512
+ - ✅ `postgres-decryption-fix-verification.db.test.js` - Verifies separate queries work
513
+ - ✅ `mongo-decryption-fix-verification.db.test.js` - Verifies fix for MongoDB
514
514
 
515
515
  **Implementation Examples:**
516
516
 
@@ -174,26 +174,46 @@ function loadModuleEncryptionSchemas(integrations) {
174
174
  return;
175
175
  }
176
176
 
177
- const { getModulesDefinitionFromIntegrationClasses } = require('../integrations/utils/map-integration-dto');
177
+ const { getModulesDefinitionFromIntegrationClasses } = require('../../integrations/utils/map-integration-dto');
178
178
 
179
- const moduleDefinitions = getModulesDefinitionFromIntegrationClasses(integrations);
180
- const credentialFields = extractCredentialFieldsFromModules(moduleDefinitions);
179
+ // Integrations without modules contribute no credential fields.
180
+ const integrationsWithModules = integrations.filter(
181
+ (integration) => integration?.Definition?.modules
182
+ );
183
+ const moduleDefinitions = getModulesDefinitionFromIntegrationClasses(
184
+ integrationsWithModules
185
+ );
186
+
187
+ // Fields the core schema already encrypts (e.g. data.api_key) are
188
+ // covered; registering them again would fail validation and drop every
189
+ // other module field with them.
190
+ const coreCredentialFields = CORE_ENCRYPTION_SCHEMA.Credential.fields;
191
+ const credentialFields = extractCredentialFieldsFromModules(
192
+ moduleDefinitions
193
+ ).filter((field) => !coreCredentialFields.includes(field));
181
194
 
182
195
  if (credentialFields.length === 0) {
183
196
  return;
184
197
  }
185
198
 
186
- const moduleSchema = {
199
+ // Merge with whatever custom schema the app already registered rather
200
+ // than replacing it (registerCustomSchema overwrites).
201
+ const existingCredentialFields = customSchema.Credential?.fields || [];
202
+ const mergedSchema = {
203
+ ...customSchema,
187
204
  Credential: {
188
- fields: credentialFields
189
- }
205
+ ...customSchema.Credential,
206
+ fields: [
207
+ ...new Set([...existingCredentialFields, ...credentialFields]),
208
+ ],
209
+ },
190
210
  };
191
211
 
192
212
  logger.info(
193
213
  `Registering module-level encryption for ${credentialFields.length} credential fields`
194
214
  );
195
215
 
196
- registerCustomSchema(moduleSchema);
216
+ registerCustomSchema(mergedSchema);
197
217
  }
198
218
 
199
219
  /**
package/jest-setup.js CHANGED
@@ -1,2 +1,13 @@
1
- const {globalSetup} = require('@friggframework/test');
2
- module.exports = globalSetup;
1
+ // Core's unit suites must not need a database, so this global setup only
2
+ // sets the test environment. It deliberately does not start an in-memory
3
+ // MongoDB (as @friggframework/test's globalSetup does): nothing in the default
4
+ // suite talks to a live server, and a binary that failed to start used to stop
5
+ // every core suite from running. Suites that need a real database are
6
+ // `*.db.test.js` files, run by `npm run test:db` (see jest.db.config.js).
7
+ const { overrideEnvironment } = require('@friggframework/test');
8
+
9
+ module.exports = async function () {
10
+ if (!process.env.STAGE) {
11
+ overrideEnvironment({ STAGE: 'dev' });
12
+ }
13
+ };
package/jest-teardown.js CHANGED
@@ -1,2 +1,5 @@
1
- const {globalTeardown} = require('@friggframework/test');
2
- module.exports = globalTeardown;
1
+ const { restoreEnvironment } = require('@friggframework/test');
2
+
3
+ module.exports = async function () {
4
+ restoreEnvironment();
5
+ };
package/jest.config.js CHANGED
@@ -20,4 +20,8 @@ module.exports = {
20
20
  globalTeardown: './jest-teardown.js',
21
21
 
22
22
  setupFilesAfterEnv: ['./logs/jest-logger-setup.js'],
23
+
24
+ // `*.db.test.js` suites talk to a real MongoDB replica set / PostgreSQL
25
+ // and run separately via `npm run test:db` (jest.db.config.js).
26
+ testPathIgnorePatterns: ['/node_modules/', '\\.db\\.test\\.js$'],
23
27
  };
@@ -0,0 +1,23 @@
1
+ /*
2
+ * Database integration suites (`*.db.test.js`).
3
+ *
4
+ * These talk to real databases instead of mocking Prisma, so they are kept
5
+ * out of the default `npm test` run and executed with `npm run test:db`.
6
+ * They need generated Prisma clients (`npm run prisma:generate`) and:
7
+ * - a MongoDB replica set (Prisma requires one for MongoDB writes):
8
+ * FRIGG_TEST_MONGODB_URL, default
9
+ * mongodb://localhost:27017/frigg?replicaSet=rs0
10
+ * - PostgreSQL with the prisma-postgresql migrations applied:
11
+ * FRIGG_TEST_POSTGRES_URL, default
12
+ * postgresql://postgres:postgres@localhost:5432/frigg?schema=public
13
+ * The repository parity suites skip themselves unless their own URLs are
14
+ * set (INTEGRATION_ENTITIES_PARITY_* / QUERY_MAPPINGS_PARITY_*).
15
+ */
16
+ const base = require('./jest.config');
17
+
18
+ module.exports = {
19
+ ...base,
20
+ coverageThreshold: undefined,
21
+ testMatch: ['**/*.db.test.js'],
22
+ testPathIgnorePatterns: ['/node_modules/'],
23
+ };
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--canary.711.f8b6445.0",
4
+ "version": "2.0.0--canary.710.91495fa.0",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-apigatewaymanagementapi": "^3.588.0",
7
7
  "@aws-sdk/client-kms": "^3.588.0",
@@ -48,9 +48,9 @@
48
48
  }
49
49
  },
50
50
  "devDependencies": {
51
- "@friggframework/eslint-config": "2.0.0--canary.711.f8b6445.0",
52
- "@friggframework/prettier-config": "2.0.0--canary.711.f8b6445.0",
53
- "@friggframework/test": "2.0.0--canary.711.f8b6445.0",
51
+ "@friggframework/eslint-config": "2.0.0--canary.710.91495fa.0",
52
+ "@friggframework/prettier-config": "2.0.0--canary.710.91495fa.0",
53
+ "@friggframework/test": "2.0.0--canary.710.91495fa.0",
54
54
  "@prisma/client": "^6.19.3",
55
55
  "@types/lodash": "4.17.15",
56
56
  "@typescript-eslint/eslint-plugin": "^8.0.0",
@@ -68,6 +68,7 @@
68
68
  "scripts": {
69
69
  "lint:fix": "prettier --write --loglevel error . && eslint . --fix",
70
70
  "test": "jest --passWithNoTests # TODO",
71
+ "test:db": "jest --config jest.db.config.js --runInBand",
71
72
  "prisma:generate:mongo": "npx prisma generate --schema ./prisma-mongodb/schema.prisma",
72
73
  "prisma:generate:postgres": "npx prisma generate --schema ./prisma-postgresql/schema.prisma",
73
74
  "prisma:generate": "npm run prisma:generate:mongo && npm run prisma:generate:postgres",
@@ -90,5 +91,5 @@
90
91
  "publishConfig": {
91
92
  "access": "public"
92
93
  },
93
- "gitHead": "f8b64453af51d5a060c85bacb037b6cc9fc8943f"
94
+ "gitHead": "91495fa7fd4cc9e0da6709172c245e97ee06daf4"
94
95
  }
@@ -97,16 +97,17 @@ const QueuerUtil = {
97
97
  Id: uuid(),
98
98
  MessageBody: JSON.stringify(entry),
99
99
  });
100
- // Sends 10, then purges the buffer
100
+ // Sends 10, then starts a fresh buffer. Each command gets its own
101
+ // array: clearing the shared buffer in place would also empty the
102
+ // Entries of a command that was already sent.
101
103
  if (buffer.length === batchSize) {
104
+ const batch = buffer.splice(0, buffer.length);
102
105
  const command = new SendMessageBatchCommand({
103
- Entries: buffer,
106
+ Entries: batch,
104
107
  QueueUrl: queueUrl,
105
108
  });
106
109
  const result = await sqs.send(command);
107
- inspectBatchResult(result, queueUrl, buffer);
108
- // Purge the buffer
109
- buffer.splice(0, buffer.length);
110
+ inspectBatchResult(result, queueUrl, batch);
110
111
  }
111
112
  }
112
113