@friggframework/core 2.0.0--canary.680.6bacd26.0 → 2.0.0--canary.663.8de669c.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/CHANGELOG.md +0 -11
- package/application/commands/integration-commands.js +8 -18
- package/application/index.js +2 -6
- package/credential/repositories/credential-repository-documentdb.js +0 -34
- package/credential/repositories/credential-repository-interface.js +0 -15
- package/credential/repositories/credential-repository-mongo.js +0 -23
- package/credential/repositories/credential-repository-postgres.js +0 -28
- package/database/encryption/README.md +0 -2
- package/database/encryption/encryption-schema-registry.js +12 -41
- package/database/encryption/field-encryption-service.js +5 -69
- package/generated/prisma-mongodb/edge.js +4 -20
- package/generated/prisma-mongodb/index-browser.js +1 -17
- package/generated/prisma-mongodb/index.d.ts +95 -1543
- package/generated/prisma-mongodb/index.js +4 -20
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +0 -27
- package/generated/prisma-mongodb/wasm.js +4 -20
- package/generated/prisma-postgresql/edge.js +4 -20
- package/generated/prisma-postgresql/index-browser.js +1 -17
- package/generated/prisma-postgresql/index.d.ts +84 -1565
- package/generated/prisma-postgresql/index.js +4 -20
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +0 -26
- package/generated/prisma-postgresql/wasm.js +4 -20
- package/handlers/app-definition-loader.js +8 -19
- package/handlers/app-handler-helpers.js +3 -25
- package/handlers/backend-utils.js +25 -31
- package/handlers/middleware/admin-auth.js +1 -14
- package/handlers/routers/auth.js +3 -11
- package/handlers/routers/health.js +4 -11
- package/handlers/workers/integration-defined-workers.js +2 -5
- package/index.js +2 -4
- package/integrations/index.js +4 -0
- package/integrations/integration-router.js +23 -16
- package/integrations/utils/map-integration-dto.js +15 -1
- package/modules/index.js +0 -6
- package/modules/requester/requester.js +2 -27
- package/package.json +5 -5
- package/prisma-mongodb/schema.prisma +0 -27
- package/prisma-postgresql/schema.prisma +0 -26
- package/types/index.d.ts +0 -1
- package/credential/use-cases/credential-operations-for-user.js +0 -117
- package/credential/use-cases/reauthorize-credential-for-user.js +0 -66
- package/database/encryption/test-support/fake-ciphertext.js +0 -15
- package/integrations/use-cases/integration-operations-for-user.js +0 -225
- package/management-api/create-management-api-router.js +0 -49
- package/management-api/error-response.js +0 -116
- package/management-api/management-api-config.js +0 -82
- package/management-api/meta-router.js +0 -57
- package/management-api/openapi/build-openapi.js +0 -144
- package/management-api/openapi/components.js +0 -368
- package/management-api/openapi/v1-operations.js +0 -83
- package/management-api/openapi/v2-operations.js +0 -252
- package/management-api/route-registry.js +0 -294
- package/management-api/test-support/http-client.js +0 -39
- package/management-api/use-cases/get-management-api-meta.js +0 -57
- package/management-api/v1-deprecation.js +0 -120
- package/management-api/v2/create-v2-router.js +0 -85
- package/management-api/v2/dependencies.js +0 -256
- package/management-api/v2/dto.js +0 -123
- package/management-api/v2/handlers/authorization.js +0 -93
- package/management-api/v2/handlers/credentials.js +0 -29
- package/management-api/v2/handlers/entities.js +0 -74
- package/management-api/v2/handlers/integrations.js +0 -125
- package/management-api/v2/handlers/proxy.js +0 -34
- package/management-api/v2/request.js +0 -54
- package/management-api/v2/test-support/fake-dependencies.js +0 -103
- package/modules/domain/authorization-session.js +0 -144
- package/modules/entity-type-registry.js +0 -311
- package/modules/proxy/proxy-errors.js +0 -20
- package/modules/proxy/proxy-path.js +0 -179
- package/modules/proxy/proxy-policy.js +0 -248
- package/modules/repositories/authorization-session-repository-documentdb.js +0 -112
- package/modules/repositories/authorization-session-repository-factory.js +0 -31
- package/modules/repositories/authorization-session-repository-interface.js +0 -47
- package/modules/repositories/authorization-session-repository-prisma.js +0 -87
- package/modules/requester/proxy-request.js +0 -244
- package/modules/tests/doubles/in-memory-authorization-session-repository.js +0 -45
- package/modules/use-cases/entity-operations-for-user.js +0 -125
- package/modules/use-cases/entity-types.js +0 -115
- package/modules/use-cases/execute-entity-proxy-request.js +0 -239
- package/modules/use-cases/get-authorization-requirements.js +0 -100
- package/modules/use-cases/get-entity-module-for-user.js +0 -86
- package/modules/use-cases/multi-step-authorization.js +0 -302
- package/prisma-postgresql/migrations/20261003000000_add_authorization_session/migration.sql +0 -28
- package/types/modules/index.d.ts +0 -58
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
const Boom = require('@hapi/boom');
|
|
2
|
-
const { FetchError } = require('../../errors/fetch-error');
|
|
3
|
-
const { getLogger } = require('../../logs');
|
|
4
|
-
const { AuthorizationSession } = require('../domain/authorization-session');
|
|
5
|
-
const { findDefinition, getAuthStepCount } = require('./entity-types');
|
|
6
|
-
|
|
7
|
-
const log = getLogger('frigg.api.authorize');
|
|
8
|
-
|
|
9
|
-
const defaultCreateModule = (params) => {
|
|
10
|
-
const { Module } = require('../module');
|
|
11
|
-
return new Module(params);
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const isMultiStep = (definition) =>
|
|
15
|
-
getAuthStepCount(definition) > 1 ||
|
|
16
|
-
typeof definition.processAuthorizationStep === 'function';
|
|
17
|
-
|
|
18
|
-
const sessionNotFound = () =>
|
|
19
|
-
Boom.notFound('Authorization session not found or expired', {
|
|
20
|
-
code: 'AUTHORIZATION_SESSION_NOT_FOUND',
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Maps a failure from the module or the provider to a client-facing error.
|
|
25
|
-
* Boom errors pass through. A provider HTTP failure is a 502; a failed auth
|
|
26
|
-
* test or a ClientSafeError is a 400. Anything else stays a 500.
|
|
27
|
-
*/
|
|
28
|
-
function toAuthorizationError(error, { passMessage = false } = {}) {
|
|
29
|
-
if (error?.isBoom) return error;
|
|
30
|
-
if (error instanceof FetchError) {
|
|
31
|
-
return Boom.badGateway('The provider rejected the authorization request', {
|
|
32
|
-
code: 'UPSTREAM_ERROR',
|
|
33
|
-
expose: true,
|
|
34
|
-
details: { upstreamStatus: error.statusCode ?? null },
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
if (error?.isClientSafe || passMessage) {
|
|
38
|
-
return Boom.badRequest(error?.message || 'Authorization failed', {
|
|
39
|
-
code: 'AUTHORIZATION_FAILED',
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
if (error?.message === 'Authorization failed') {
|
|
43
|
-
return Boom.badRequest(
|
|
44
|
-
'Authorization failed: the credentials did not pass the module auth test',
|
|
45
|
-
{ code: 'AUTHORIZATION_FAILED' }
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
return error;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Loads the session a caller continues, checking it belongs to them, to the
|
|
53
|
-
* entity type and (for re-authorization) to the credential.
|
|
54
|
-
*/
|
|
55
|
-
async function loadSession(repository, sessionId, binding) {
|
|
56
|
-
if (typeof sessionId !== 'string' || !sessionId) throw sessionNotFound();
|
|
57
|
-
const session = await repository.findBySessionId(sessionId);
|
|
58
|
-
if (!session || session.isExpired() || !session.belongsTo(binding)) {
|
|
59
|
-
throw sessionNotFound();
|
|
60
|
-
}
|
|
61
|
-
if (session.completed) {
|
|
62
|
-
throw Boom.conflict('Authorization session is already complete', {
|
|
63
|
-
code: 'AUTHORIZATION_SESSION_COMPLETED',
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
return session;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async function startSession(repository, binding, maxSteps) {
|
|
70
|
-
// Expired sessions are removed opportunistically; PostgreSQL has no TTL.
|
|
71
|
-
Promise.resolve()
|
|
72
|
-
.then(() => repository.deleteExpired())
|
|
73
|
-
.catch((error) =>
|
|
74
|
-
log.warn('Expired authorization sessions were not removed', {
|
|
75
|
-
eventName: `${log.name}.session_cleanup_failed`,
|
|
76
|
-
error,
|
|
77
|
-
})
|
|
78
|
-
);
|
|
79
|
-
return repository.create(AuthorizationSession.start({ ...binding, maxSteps }));
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* GET /api/v2/authorize (and GET /api/v2/credentials/:id/reauthorize): what
|
|
84
|
-
* the caller must submit next. A multi-step flow gets a session on step 1;
|
|
85
|
-
* later steps need that sessionId and must be the session's current step.
|
|
86
|
-
*/
|
|
87
|
-
class GetAuthorizationStep {
|
|
88
|
-
/**
|
|
89
|
-
* @param {Object} params
|
|
90
|
-
* @param {Array<Object>} params.moduleDefinitions
|
|
91
|
-
* @param {import('./get-authorization-requirements').GetAuthorizationRequirements} params.getAuthorizationRequirements
|
|
92
|
-
* @param {import('../repositories/authorization-session-repository-interface').AuthorizationSessionRepositoryInterface} params.authorizationSessionRepository
|
|
93
|
-
*/
|
|
94
|
-
constructor({ moduleDefinitions, getAuthorizationRequirements, authorizationSessionRepository }) {
|
|
95
|
-
this.moduleDefinitions = moduleDefinitions || [];
|
|
96
|
-
this.getAuthorizationRequirements = getAuthorizationRequirements;
|
|
97
|
-
this.sessions = authorizationSessionRepository;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async execute({ user, entityType, step = 1, sessionId, credentialId = null, state }) {
|
|
101
|
-
const definition = findDefinition(this.moduleDefinitions, entityType);
|
|
102
|
-
const userId = user.getId();
|
|
103
|
-
const requirementsFor = (n) =>
|
|
104
|
-
this.getAuthorizationRequirements.execute({ entityType, step: n, userId, state });
|
|
105
|
-
|
|
106
|
-
if (!isMultiStep(definition)) return requirementsFor(step);
|
|
107
|
-
|
|
108
|
-
const binding = { userId, entityType, credentialId };
|
|
109
|
-
let session;
|
|
110
|
-
if (sessionId) {
|
|
111
|
-
session = await loadSession(this.sessions, sessionId, binding);
|
|
112
|
-
if (step !== session.currentStep) {
|
|
113
|
-
throw Boom.conflict(
|
|
114
|
-
`This session expects step ${session.currentStep}`,
|
|
115
|
-
{ code: 'STEP_OUT_OF_ORDER', details: { expectedStep: session.currentStep } }
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
} else {
|
|
119
|
-
if (step !== 1) {
|
|
120
|
-
throw Boom.badRequest('sessionId is required after step 1', {
|
|
121
|
-
code: 'SESSION_REQUIRED',
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
session = await startSession(this.sessions, binding, getAuthStepCount(definition));
|
|
125
|
-
}
|
|
126
|
-
return { ...(await requirementsFor(step)), sessionId: session.sessionId };
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* POST /api/v2/authorize (and POST /api/v2/credentials/:id/reauthorize).
|
|
132
|
-
*
|
|
133
|
-
* Single-step modules complete at once through ProcessAuthorizationCallback
|
|
134
|
-
* (the v1 authorize logic). Multi-step modules run
|
|
135
|
-
* definition.processAuthorizationStep(api, step, data, stepData) per step;
|
|
136
|
-
* when it reports `completed`, its `authData` goes through the same
|
|
137
|
-
* callback. Returns either
|
|
138
|
-
* `{ status: 'complete', entity, credential }` (domain records; the handler
|
|
139
|
-
* masks them) or
|
|
140
|
-
* `{ status: 'pending', step, totalSteps, sessionId, requirements, message? }`.
|
|
141
|
-
*/
|
|
142
|
-
class SubmitAuthorizationStep {
|
|
143
|
-
constructor({
|
|
144
|
-
moduleDefinitions,
|
|
145
|
-
getAuthorizationRequirements,
|
|
146
|
-
authorizationSessionRepository,
|
|
147
|
-
processAuthorizationCallback,
|
|
148
|
-
moduleRepository,
|
|
149
|
-
credentialRepository,
|
|
150
|
-
createModule,
|
|
151
|
-
}) {
|
|
152
|
-
this.moduleDefinitions = moduleDefinitions || [];
|
|
153
|
-
this.getAuthorizationRequirements = getAuthorizationRequirements;
|
|
154
|
-
this.sessions = authorizationSessionRepository;
|
|
155
|
-
this.processAuthorizationCallback = processAuthorizationCallback;
|
|
156
|
-
this.moduleRepository = moduleRepository;
|
|
157
|
-
this.credentialRepository = credentialRepository;
|
|
158
|
-
this.createModule = createModule || defaultCreateModule;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
async execute({ user, entityType, data, step = 1, sessionId, credentialId = null }) {
|
|
162
|
-
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
163
|
-
throw Boom.badRequest('data must be an object', {
|
|
164
|
-
code: 'VALIDATION_ERROR',
|
|
165
|
-
details: { field: 'data' },
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
const definition = findDefinition(this.moduleDefinitions, entityType);
|
|
169
|
-
const userId = user.getId();
|
|
170
|
-
|
|
171
|
-
if (!isMultiStep(definition)) {
|
|
172
|
-
if (step !== 1) {
|
|
173
|
-
throw Boom.badRequest(`${entityType} has a single authorization step`, {
|
|
174
|
-
code: 'INVALID_STEP',
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
return this.complete({ userId, entityType, params: data, credentialId });
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const totalSteps = getAuthStepCount(definition);
|
|
181
|
-
const binding = { userId, entityType, credentialId };
|
|
182
|
-
let session;
|
|
183
|
-
if (sessionId) {
|
|
184
|
-
session = await loadSession(this.sessions, sessionId, binding);
|
|
185
|
-
if (step === 1 && session.currentStep !== 1) {
|
|
186
|
-
session.restart();
|
|
187
|
-
} else if (step !== session.currentStep) {
|
|
188
|
-
throw Boom.conflict(`This session expects step ${session.currentStep}`, {
|
|
189
|
-
code: 'STEP_OUT_OF_ORDER',
|
|
190
|
-
details: { expectedStep: session.currentStep },
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
} else {
|
|
194
|
-
if (step !== 1) {
|
|
195
|
-
throw Boom.badRequest('sessionId is required after step 1', {
|
|
196
|
-
code: 'SESSION_REQUIRED',
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
session = await startSession(this.sessions, binding, totalSteps);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (typeof definition.processAuthorizationStep !== 'function') {
|
|
203
|
-
throw Boom.badImplementation(
|
|
204
|
-
`Module ${entityType} declares ${totalSteps} steps but no processAuthorizationStep`
|
|
205
|
-
);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const module = this.createModule({ userId, definition });
|
|
209
|
-
let result;
|
|
210
|
-
try {
|
|
211
|
-
result = await definition.processAuthorizationStep(
|
|
212
|
-
module.api,
|
|
213
|
-
step,
|
|
214
|
-
data,
|
|
215
|
-
session.stepData
|
|
216
|
-
);
|
|
217
|
-
} catch (error) {
|
|
218
|
-
// processAuthorizationStep errors are written for the end user
|
|
219
|
-
// (docs/MULTI_STEP_AUTH_MIGRATION_GUIDE.md), e.g. "Invalid code".
|
|
220
|
-
throw toAuthorizationError(error, { passMessage: true });
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
if (result?.completed) {
|
|
224
|
-
session.markComplete();
|
|
225
|
-
await this.sessions.update(session);
|
|
226
|
-
const outcome = await this.complete({
|
|
227
|
-
userId,
|
|
228
|
-
entityType,
|
|
229
|
-
params: result.authData || {},
|
|
230
|
-
credentialId,
|
|
231
|
-
});
|
|
232
|
-
// The collected data is no longer needed once the credential exists.
|
|
233
|
-
await Promise.resolve()
|
|
234
|
-
.then(() => this.sessions.deleteBySessionId(session.sessionId))
|
|
235
|
-
.catch(() => {});
|
|
236
|
-
return outcome;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const nextStep = result?.nextStep ?? step + 1;
|
|
240
|
-
try {
|
|
241
|
-
session.advanceTo(nextStep, result?.stepData);
|
|
242
|
-
} catch (error) {
|
|
243
|
-
throw Boom.badImplementation(
|
|
244
|
-
`Module ${entityType} returned an invalid next step: ${error.message}`
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
await this.sessions.update(session);
|
|
248
|
-
|
|
249
|
-
const requirements = await this.getAuthorizationRequirements.execute({
|
|
250
|
-
entityType,
|
|
251
|
-
step: nextStep,
|
|
252
|
-
userId,
|
|
253
|
-
});
|
|
254
|
-
return {
|
|
255
|
-
status: 'pending',
|
|
256
|
-
step: nextStep,
|
|
257
|
-
totalSteps,
|
|
258
|
-
sessionId: session.sessionId,
|
|
259
|
-
requirements,
|
|
260
|
-
...(result?.message && { message: String(result.message) }),
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
async complete({ userId, entityType, params, credentialId }) {
|
|
265
|
-
let callback;
|
|
266
|
-
try {
|
|
267
|
-
callback = await this.processAuthorizationCallback.execute(
|
|
268
|
-
userId,
|
|
269
|
-
entityType,
|
|
270
|
-
params
|
|
271
|
-
);
|
|
272
|
-
} catch (error) {
|
|
273
|
-
throw toAuthorizationError(error);
|
|
274
|
-
}
|
|
275
|
-
const [entity, credential] = await Promise.all([
|
|
276
|
-
this.moduleRepository.findEntityById(callback.entity_id),
|
|
277
|
-
this.credentialRepository.findCredentialById(callback.credential_id),
|
|
278
|
-
]);
|
|
279
|
-
const outcome = {
|
|
280
|
-
status: 'complete',
|
|
281
|
-
entity,
|
|
282
|
-
credential: credential && {
|
|
283
|
-
...credential,
|
|
284
|
-
type: entityType,
|
|
285
|
-
entityIds: entity ? [String(entity.id)] : [],
|
|
286
|
-
},
|
|
287
|
-
};
|
|
288
|
-
if (credentialId && String(callback.credential_id) !== String(credentialId)) {
|
|
289
|
-
// The provider account differs from the one being re-authorized,
|
|
290
|
-
// so a separate credential was stored.
|
|
291
|
-
outcome.previousCredentialId = String(credentialId);
|
|
292
|
-
}
|
|
293
|
-
return outcome;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
module.exports = {
|
|
298
|
-
GetAuthorizationStep,
|
|
299
|
-
SubmitAuthorizationStep,
|
|
300
|
-
toAuthorizationError,
|
|
301
|
-
isMultiStep,
|
|
302
|
-
};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
-- CreateTable
|
|
2
|
-
-- Multi-step authorization state for Management API v2 (/api/v2/authorize).
|
|
3
|
-
-- Short-lived rows; "stepData" is encrypted at rest by the application.
|
|
4
|
-
CREATE TABLE "AuthorizationSession" (
|
|
5
|
-
"id" SERIAL NOT NULL,
|
|
6
|
-
"sessionId" TEXT NOT NULL,
|
|
7
|
-
"userId" TEXT NOT NULL,
|
|
8
|
-
"entityType" TEXT NOT NULL,
|
|
9
|
-
"credentialId" TEXT,
|
|
10
|
-
"currentStep" INTEGER NOT NULL DEFAULT 1,
|
|
11
|
-
"maxSteps" INTEGER NOT NULL,
|
|
12
|
-
"stepData" JSONB NOT NULL DEFAULT '{}',
|
|
13
|
-
"expiresAt" TIMESTAMP(3) NOT NULL,
|
|
14
|
-
"completed" BOOLEAN NOT NULL DEFAULT false,
|
|
15
|
-
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
16
|
-
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
17
|
-
|
|
18
|
-
CONSTRAINT "AuthorizationSession_pkey" PRIMARY KEY ("id")
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
-- CreateIndex
|
|
22
|
-
CREATE UNIQUE INDEX "AuthorizationSession_sessionId_key" ON "AuthorizationSession"("sessionId");
|
|
23
|
-
|
|
24
|
-
-- CreateIndex
|
|
25
|
-
CREATE INDEX "AuthorizationSession_userId_entityType_idx" ON "AuthorizationSession"("userId", "entityType");
|
|
26
|
-
|
|
27
|
-
-- CreateIndex
|
|
28
|
-
CREATE INDEX "AuthorizationSession_expiresAt_idx" ON "AuthorizationSession"("expiresAt");
|
package/types/modules/index.d.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
declare module "@friggframework/core" {
|
|
2
|
-
/** Where an entity type comes from: the app's `entities` map or an integration binding it. */
|
|
3
|
-
export type EntityTypeSource =
|
|
4
|
-
| { kind: "app" }
|
|
5
|
-
| { kind: "integration"; name: string };
|
|
6
|
-
|
|
7
|
-
export type EntityScope = "individual" | "organization" | "global";
|
|
8
|
-
|
|
9
|
-
/** App-level settings carried from the `entities` entry; empty for an inferred-only type. */
|
|
10
|
-
export interface EntityTypeSettings {
|
|
11
|
-
scope?: EntityScope | { default?: EntityScope; allow?: EntityScope[] };
|
|
12
|
-
proxy?: Record<string, unknown>;
|
|
13
|
-
display?: Record<string, unknown>;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface EntityTypeEntry {
|
|
17
|
-
/** The API module definition. */
|
|
18
|
-
definition: any;
|
|
19
|
-
/** Frozen. `{ kind: 'app' }` first when registered, then each binding integration. */
|
|
20
|
-
sources: readonly EntityTypeSource[];
|
|
21
|
-
/** Frozen. */
|
|
22
|
-
settings: Readonly<EntityTypeSettings>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** Entity type name (the definition's moduleName) to its entry. */
|
|
26
|
-
export type EntityTypeRegistry = Map<string, EntityTypeEntry>;
|
|
27
|
-
|
|
28
|
-
/** One `appDefinition.entities` entry. The key must equal the definition's moduleName (and getName() when defined). */
|
|
29
|
-
export interface EntityRegistration extends EntityTypeSettings {
|
|
30
|
-
definition: any;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface AppDefinitionEntities {
|
|
34
|
-
/** false turns inference off: every module an integration binds must be registered. Default true. */
|
|
35
|
-
$infer?: boolean;
|
|
36
|
-
[entityType: string]: EntityRegistration | boolean | undefined;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface AppDefinition {
|
|
40
|
-
integrations: any[];
|
|
41
|
-
/** App-level entity type registry (ADR-059). */
|
|
42
|
-
entities?: AppDefinitionEntities;
|
|
43
|
-
[key: string]: unknown;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Builds the registry from `entities` plus the modules integrations bind.
|
|
48
|
-
* Throws EntityRegistryError on an invalid registry.
|
|
49
|
-
*/
|
|
50
|
-
export function getEntityTypeRegistry(
|
|
51
|
-
appDefinition?: Pick<AppDefinition, "entities" | "integrations">
|
|
52
|
-
): EntityTypeRegistry;
|
|
53
|
-
|
|
54
|
-
/** Invalid `entities` registry. `code` is always `ENTITY_REGISTRY_INVALID`. */
|
|
55
|
-
export class EntityRegistryError extends Error {
|
|
56
|
-
code: "ENTITY_REGISTRY_INVALID";
|
|
57
|
-
}
|
|
58
|
-
}
|