Repository navigation
feat(core): app-level entity registry (ADR-059) - #679
seanspeaks wants to merge 2 commits into
Conversation
Add getEntityTypeRegistry(appDefinition), a pure function that merges the
optional appDefinition.entities map with the modules bound by integrations.
Inference stays on unless entities.$infer is false.
With no entities key nothing new can fail: two different definitions under
one moduleName keep the first and log a warning. With entities present
(even {}) the rules are strict: a key must equal moduleName and getName(),
one name is one definition (same object, or same package@version), and
$infer: false requires every bound module to be registered.
loadAppDefinition builds the registry once, at load, so errors fail fast,
and returns entityTypeRegistry and moduleDefinitions. The v1 router, v2
dependencies, health, the webhook loader and the worker entry read those;
createIntegrationCommands and createQueueWorker take moduleDefinitions and
fall back to the integration's own modules when none is given.
getModulesDefinitionFromIntegrationClasses is removed.
GET /api/v2/entities/types[/:type] lists registry-only types, looks them up
by registry key, and each description gains usedBy. app-definition.schema.json
gains entities; types/modules/index.d.ts adds the registry types.
This commit does not change which fields are encrypted:
loadModuleEncryptionSchemas keeps its existing (broken) require path, with a
comment pointing to claude/internal-mcp-03b-module-credential-encryption.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HyrF4oqcTdK2AtvcrocxE6
There was a problem hiding this comment.
If your organization's extra usage balance is empty, an organization admin can add extra usage credits at claude.ai/admin-settings/usage. If its monthly spend limit was reached, an admin can raise it on the same page. If neither applies, contact Anthropic support.
Once extra usage is available, someone with write access to this repository can comment @claude review on this pull request to trigger a review.
…cated registry wrapper #679 removed the export. ADR-059 §2 keeps it as a deprecated wrapper so adopters that import it keep working. It now returns the entity type registry's definitions (optionally including app.entities), logs a deprecation warning once per process, and is exported again from @friggframework/core and integrations/. Typed in types/modules. The "registered": false list flag of ADR-059 §4 is not built; the reference doc says so (the ADR is amended on the ADR branch). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HyrF4oqcTdK2AtvcrocxE6
…wrapper takes entities The first implementation lists, reads and deletes an entity of an unregistered type and refuses new ones, but does not emit "registered": false yet. Says so, and how such an entity answers today. getModulesDefinitionFromIntegrationClasses is kept as a deprecated wrapper (#679 now restores it) with an optional entities argument. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HyrF4oqcTdK2AtvcrocxE6
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit a2bcc2f. Configure here.
| return definition; | ||
| } | ||
|
|
||
| /** Lists the entity types (API modules) this app can authorize. */ |
There was a problem hiding this comment.
Authorize ignores registry type keys
Medium Severity
ListEntityTypes and GetEntityType now identify a type by its registry key, which is getName() when moduleName is missing. findDefinition, GetAuthorizationRequirements, and ProcessAuthorizationCallback still match only definition.moduleName. A type that list and get return is then 404 ENTITY_TYPE_NOT_FOUND on requirements and authorize, so a listed type cannot be connected.
Reviewed by Cursor Bugbot for commit a2bcc2f. Configure here.





Summary
This implements ADR-059 (see #677). An app can now declare entity types in an optional
appDefinition.entities, keyed by entity type name. That makes a type listable and authorizable without first declaring an integration that uses it. Declared types are merged with the types inferred from integration Definitions, and$infer: falseturns inference off.Based on #671 (
feat/management-api-v2). It does not depend on #678.What it does
modules/entity-type-registry.js.getEntityTypeRegistry(appDefinition)is pure and returns a frozenMapofname -> { definition, sources: [{kind, name}], settings }. It throwsEntityRegistryError, which extendsBaseErrorwith codeENTITY_REGISTRY_INVALID.moduleNameand, when one is defined,getName(). Matching is case-sensitive.entitiesis present, even when it is{}. Without anentitieskey, two integrations that each bind a different object with the samemoduleNamekeep the first one and logfrigg.modules.entity_registry.duplicate_definition, so existing apps behave exactly as they do today.$infer: false. Any bound module that is not registered fails at load, and the error names the integrations that bind it.loadAppDefinitionbuilds the registry and fails at load if it is invalid. The v1 integration router (/api/authorize), health, the webhook loader, the queue-worker entry and the v2 dependencies all read that one registry instead of re-inferring from integration classes.usedByfield. Get looks up by registry key. This fixes a type that list returned but get answered with 404, and a sort crash for definitions that have onlygetName().createFriggCommands. They take injectedmoduleDefinitions. When none is passed, they fall back explicitly to the integration's own modules.entitiesis added toapp-definition.schema.json, andusedByto the entity-type API schema and the OpenAPI component.types/modules/index.d.tsis new, andcore-concepts.mdandapi-reference.mdare updated.Breaking (pre-release line)
getModulesDefinitionFromIntegrationClassesis removed from@friggframework/coreandintegrations/index.js. No caller in this repo uses it. An external adopter importing it should switch togetEntityTypeRegistry.Not in this PR
credentialFieldsencryption. The loader for it has a broken require path, so those fields have never been encrypted. This PR deliberately leaves that alone and adds a comment pointing to the follow-up.claude/internal-mcp-03b-module-credential-encryption, because turning encryption on can break reads of existing plaintext rows.display/scopesettings are stored, but nothing reads them yet (ADR-056 work).Test plan
packages/corerun with the no-op global setup, because Mongo and Postgres binaries can't run here: 65 tests fail, the same failing set by name as base. 2894 pass.packages/schemas: 166 pass.$infer, and each of the 8 call sites.🤖 Generated with Claude Code
https://claude.ai/code/session_01HyrF4oqcTdK2AtvcrocxE6
Generated by Claude Code
Note
Medium Risk
Touches authorization, module factory wiring, and app load behavior; misconfigured
entitiesnow stops the app at startup, but apps withoutentitieskeep prior inference behavior.Overview
Adds an app-level entity registry (ADR-059) via optional
appDefinition.entities, so entity types can be offered and authorized even when no integration binds them.loadAppDefinition()builds a frozenentityTypeRegistryandmoduleDefinitionsonce at startup and fails fast on invalid registries (EntityRegistryError); strict rules apply whenentitiesis present, with$infer: falserequiring every integration-bound module to be registered.Wiring: v1
/api/authorize, v2 authorize/entity-types, health, webhooks, queue workers, andcreateFriggCommands/createIntegrationCommandsnow consume the loaded registry (with an explicit fallback to the integration’s own modules whenmoduleDefinitionsis omitted).getModulesDefinitionFromIntegrationClassesremains as a deprecated registry wrapper.API:
GET /api/v2/entities/typesand per-type GET includeusedBy(integration names; empty for registry-only types). OpenAPI, JSON schemas, docs, and new TypeScript types for the registry are updated.Reviewed by Cursor Bugbot for commit a2bcc2f. Bugbot is set up for automated code reviews on this repo. Configure here.
📦 Published PR as canary version:
2.0.0--canary.679.a2bcc2f.0✨ Test out this PR locally via:
npm install @friggframework/admin-scripts@2.0.0--canary.679.a2bcc2f.0 npm install @friggframework/core@2.0.0--canary.679.a2bcc2f.0 npm install @friggframework/devtools@2.0.0--canary.679.a2bcc2f.0 npm install @friggframework/eslint-config@2.0.0--canary.679.a2bcc2f.0 npm install @friggframework/prettier-config@2.0.0--canary.679.a2bcc2f.0 npm install @friggframework/schemas@2.0.0--canary.679.a2bcc2f.0 npm install @friggframework/serverless-plugin@2.0.0--canary.679.a2bcc2f.0 npm install @friggframework/test@2.0.0--canary.679.a2bcc2f.0 npm install @friggframework/ui@2.0.0--canary.679.a2bcc2f.0 # or yarn add @friggframework/admin-scripts@2.0.0--canary.679.a2bcc2f.0 yarn add @friggframework/core@2.0.0--canary.679.a2bcc2f.0 yarn add @friggframework/devtools@2.0.0--canary.679.a2bcc2f.0 yarn add @friggframework/eslint-config@2.0.0--canary.679.a2bcc2f.0 yarn add @friggframework/prettier-config@2.0.0--canary.679.a2bcc2f.0 yarn add @friggframework/schemas@2.0.0--canary.679.a2bcc2f.0 yarn add @friggframework/serverless-plugin@2.0.0--canary.679.a2bcc2f.0 yarn add @friggframework/test@2.0.0--canary.679.a2bcc2f.0 yarn add @friggframework/ui@2.0.0--canary.679.a2bcc2f.0