@headless-lms/core 0.1.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/LICENSE +21 -0
- package/dist/assets/events.d.ts +81 -0
- package/dist/assets/events.js +24 -0
- package/dist/assets/events.js.map +1 -0
- package/dist/assets/index.d.ts +7 -0
- package/dist/assets/index.js +3 -0
- package/dist/assets/model.d.ts +4 -0
- package/dist/assets/model.js +1 -0
- package/dist/assets/ports.d.ts +34 -0
- package/dist/assets/ports.js +1 -0
- package/dist/assets/service.d.ts +29 -0
- package/dist/assets/service.js +130 -0
- package/dist/assets/service.js.map +1 -0
- package/dist/automations/actions.d.ts +24 -0
- package/dist/automations/actions.js +59 -0
- package/dist/automations/actions.js.map +1 -0
- package/dist/automations/catalog.d.ts +12 -0
- package/dist/automations/catalog.js +224 -0
- package/dist/automations/catalog.js.map +1 -0
- package/dist/automations/events.d.ts +264 -0
- package/dist/automations/events.js +54 -0
- package/dist/automations/events.js.map +1 -0
- package/dist/automations/index.d.ts +9 -0
- package/dist/automations/index.js +4 -0
- package/dist/automations/model.d.ts +12 -0
- package/dist/automations/model.js +14 -0
- package/dist/automations/model.js.map +1 -0
- package/dist/automations/ports.d.ts +56 -0
- package/dist/automations/ports.js +1 -0
- package/dist/automations/service.d.ts +53 -0
- package/dist/automations/service.js +225 -0
- package/dist/automations/service.js.map +1 -0
- package/dist/automations/types.d.ts +3 -0
- package/dist/automations/types.js +1 -0
- package/dist/content/events.d.ts +410 -0
- package/dist/content/events.js +105 -0
- package/dist/content/events.js.map +1 -0
- package/dist/content/index.d.ts +8 -0
- package/dist/content/index.js +3 -0
- package/dist/content/model.d.ts +3 -0
- package/dist/content/model.js +1 -0
- package/dist/content/ports.d.ts +85 -0
- package/dist/content/ports.js +1 -0
- package/dist/content/service.d.ts +54 -0
- package/dist/content/service.js +479 -0
- package/dist/content/service.js.map +1 -0
- package/dist/content/types.d.ts +4 -0
- package/dist/content/types.js +1 -0
- package/dist/discussion/events.d.ts +101 -0
- package/dist/discussion/events.js +29 -0
- package/dist/discussion/events.js.map +1 -0
- package/dist/discussion/index.d.ts +8 -0
- package/dist/discussion/index.js +3 -0
- package/dist/discussion/model.d.ts +5 -0
- package/dist/discussion/model.js +1 -0
- package/dist/discussion/ports.d.ts +112 -0
- package/dist/discussion/ports.js +1 -0
- package/dist/discussion/service.d.ts +67 -0
- package/dist/discussion/service.js +343 -0
- package/dist/discussion/service.js.map +1 -0
- package/dist/discussion/types.d.ts +4 -0
- package/dist/discussion/types.js +1 -0
- package/dist/entitlements/events.d.ts +89 -0
- package/dist/entitlements/events.js +29 -0
- package/dist/entitlements/events.js.map +1 -0
- package/dist/entitlements/index.d.ts +7 -0
- package/dist/entitlements/index.js +3 -0
- package/dist/entitlements/model.d.ts +4 -0
- package/dist/entitlements/model.js +1 -0
- package/dist/entitlements/ports.d.ts +34 -0
- package/dist/entitlements/ports.js +1 -0
- package/dist/entitlements/service.d.ts +27 -0
- package/dist/entitlements/service.js +60 -0
- package/dist/entitlements/service.js.map +1 -0
- package/dist/identity/events.d.ts +63 -0
- package/dist/identity/events.js +24 -0
- package/dist/identity/events.js.map +1 -0
- package/dist/identity/index.d.ts +8 -0
- package/dist/identity/index.js +3 -0
- package/dist/identity/model.d.ts +3 -0
- package/dist/identity/model.js +1 -0
- package/dist/identity/ports.d.ts +58 -0
- package/dist/identity/ports.js +1 -0
- package/dist/identity/service.d.ts +38 -0
- package/dist/identity/service.js +79 -0
- package/dist/identity/service.js.map +1 -0
- package/dist/identity/types.d.ts +4 -0
- package/dist/identity/types.js +1 -0
- package/dist/integrations/events.d.ts +70 -0
- package/dist/integrations/events.js +24 -0
- package/dist/integrations/events.js.map +1 -0
- package/dist/integrations/index.d.ts +9 -0
- package/dist/integrations/index.js +6 -0
- package/dist/integrations/model.d.ts +28 -0
- package/dist/integrations/model.js +45 -0
- package/dist/integrations/model.js.map +1 -0
- package/dist/integrations/ports.d.ts +62 -0
- package/dist/integrations/ports.js +1 -0
- package/dist/integrations/registry.d.ts +7 -0
- package/dist/integrations/registry.js +16 -0
- package/dist/integrations/registry.js.map +1 -0
- package/dist/integrations/service.d.ts +47 -0
- package/dist/integrations/service.js +184 -0
- package/dist/integrations/service.js.map +1 -0
- package/dist/integrations/zod.d.ts +29 -0
- package/dist/integrations/zod.js +40 -0
- package/dist/integrations/zod.js.map +1 -0
- package/dist/organizations/events.d.ts +197 -0
- package/dist/organizations/events.js +59 -0
- package/dist/organizations/events.js.map +1 -0
- package/dist/organizations/index.d.ts +11 -0
- package/dist/organizations/index.js +5 -0
- package/dist/organizations/members.d.ts +32 -0
- package/dist/organizations/members.js +12 -0
- package/dist/organizations/members.js.map +1 -0
- package/dist/organizations/model.d.ts +3 -0
- package/dist/organizations/model.js +1 -0
- package/dist/organizations/ports.d.ts +127 -0
- package/dist/organizations/ports.js +1 -0
- package/dist/organizations/roles.d.ts +23 -0
- package/dist/organizations/roles.js +70 -0
- package/dist/organizations/roles.js.map +1 -0
- package/dist/organizations/service.d.ts +58 -0
- package/dist/organizations/service.js +359 -0
- package/dist/organizations/service.js.map +1 -0
- package/dist/organizations/types.d.ts +39 -0
- package/dist/organizations/types.js +1 -0
- package/dist/progress/events.d.ts +55 -0
- package/dist/progress/events.js +19 -0
- package/dist/progress/events.js.map +1 -0
- package/dist/progress/index.d.ts +8 -0
- package/dist/progress/index.js +3 -0
- package/dist/progress/model.d.ts +3 -0
- package/dist/progress/model.js +1 -0
- package/dist/progress/ports.d.ts +37 -0
- package/dist/progress/ports.js +1 -0
- package/dist/progress/service.d.ts +33 -0
- package/dist/progress/service.js +181 -0
- package/dist/progress/service.js.map +1 -0
- package/dist/progress/types.d.ts +4 -0
- package/dist/progress/types.js +1 -0
- package/dist/reporting/dashboard/index.d.ts +4 -0
- package/dist/reporting/dashboard/index.js +2 -0
- package/dist/reporting/dashboard/model.d.ts +14 -0
- package/dist/reporting/dashboard/model.js +1 -0
- package/dist/reporting/dashboard/ports.d.ts +13 -0
- package/dist/reporting/dashboard/ports.js +1 -0
- package/dist/reporting/dashboard/service.d.ts +19 -0
- package/dist/reporting/dashboard/service.js +20 -0
- package/dist/reporting/dashboard/service.js.map +1 -0
- package/dist/reporting/learn/index.d.ts +5 -0
- package/dist/reporting/learn/index.js +2 -0
- package/dist/reporting/learn/model.d.ts +23 -0
- package/dist/reporting/learn/model.js +1 -0
- package/dist/reporting/learn/ports.d.ts +42 -0
- package/dist/reporting/learn/ports.js +1 -0
- package/dist/reporting/learn/service.d.ts +48 -0
- package/dist/reporting/learn/service.js +111 -0
- package/dist/reporting/learn/service.js.map +1 -0
- package/dist/reporting/students/index.d.ts +4 -0
- package/dist/reporting/students/index.js +2 -0
- package/dist/reporting/students/model.d.ts +31 -0
- package/dist/reporting/students/model.js +1 -0
- package/dist/reporting/students/ports.d.ts +13 -0
- package/dist/reporting/students/ports.js +1 -0
- package/dist/reporting/students/service.d.ts +19 -0
- package/dist/reporting/students/service.js +20 -0
- package/dist/reporting/students/service.js.map +1 -0
- package/dist/shared/errors.d.ts +22 -0
- package/dist/shared/errors.js +34 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/shared/events.d.ts +31 -0
- package/dist/shared/events.js +75 -0
- package/dist/shared/events.js.map +1 -0
- package/dist/shared/id.d.ts +44 -0
- package/dist/shared/id.js +53 -0
- package/dist/shared/id.js.map +1 -0
- package/dist/shared/invite-token.d.ts +9 -0
- package/dist/shared/invite-token.js +16 -0
- package/dist/shared/invite-token.js.map +1 -0
- package/dist/shared/logger.d.ts +19 -0
- package/dist/shared/logger.js +44 -0
- package/dist/shared/logger.js.map +1 -0
- package/dist/shared/mailer.d.ts +23 -0
- package/dist/shared/mailer.js +27 -0
- package/dist/shared/mailer.js.map +1 -0
- package/dist/shared/ports.d.ts +62 -0
- package/dist/shared/ports.js +2 -0
- package/dist/shared/settings.d.ts +25 -0
- package/dist/shared/settings.js +21 -0
- package/dist/shared/settings.js.map +1 -0
- package/dist/types/assets.d.ts +2 -0
- package/dist/types/assets.js +1 -0
- package/dist/types/automations.d.ts +23 -0
- package/dist/types/automations.js +1 -0
- package/dist/types/content.d.ts +2 -0
- package/dist/types/content.js +1 -0
- package/dist/types/discussion.d.ts +36 -0
- package/dist/types/discussion.js +1 -0
- package/dist/types/email-templates.d.ts +57 -0
- package/dist/types/email-templates.js +1 -0
- package/dist/types/entitlements.d.ts +3 -0
- package/dist/types/entitlements.js +1 -0
- package/dist/types/events.d.ts +25 -0
- package/dist/types/events.js +1 -0
- package/dist/types/identity.d.ts +37 -0
- package/dist/types/identity.js +1 -0
- package/dist/types/index.d.ts +23 -0
- package/dist/types/index.js +1 -0
- package/dist/types/integrations.d.ts +45 -0
- package/dist/types/integrations.js +1 -0
- package/dist/types/organizations.d.ts +15 -0
- package/dist/types/organizations.js +1 -0
- package/dist/types/ports.d.ts +57 -0
- package/dist/types/ports.js +1 -0
- package/dist/types/progress.d.ts +6 -0
- package/dist/types/progress.js +1 -0
- package/dist/types/schemas/assets.d.ts +109 -0
- package/dist/types/schemas/assets.js +49 -0
- package/dist/types/schemas/assets.js.map +1 -0
- package/dist/types/schemas/automations.d.ts +118 -0
- package/dist/types/schemas/automations.js +73 -0
- package/dist/types/schemas/automations.js.map +1 -0
- package/dist/types/schemas/content.d.ts +216 -0
- package/dist/types/schemas/content.js +138 -0
- package/dist/types/schemas/content.js.map +1 -0
- package/dist/types/schemas/credentials.d.ts +15 -0
- package/dist/types/schemas/credentials.js +15 -0
- package/dist/types/schemas/credentials.js.map +1 -0
- package/dist/types/schemas/discussion.d.ts +192 -0
- package/dist/types/schemas/discussion.js +100 -0
- package/dist/types/schemas/discussion.js.map +1 -0
- package/dist/types/schemas/entitlements.d.ts +50 -0
- package/dist/types/schemas/entitlements.js +38 -0
- package/dist/types/schemas/entitlements.js.map +1 -0
- package/dist/types/schemas/identity.d.ts +37 -0
- package/dist/types/schemas/identity.js +34 -0
- package/dist/types/schemas/identity.js.map +1 -0
- package/dist/types/schemas/index.d.ts +14 -0
- package/dist/types/schemas/index.js +14 -0
- package/dist/types/schemas/integrations.d.ts +36 -0
- package/dist/types/schemas/integrations.js +30 -0
- package/dist/types/schemas/integrations.js.map +1 -0
- package/dist/types/schemas/organizations.d.ts +135 -0
- package/dist/types/schemas/organizations.js +90 -0
- package/dist/types/schemas/organizations.js.map +1 -0
- package/dist/types/schemas/outbox.d.ts +19 -0
- package/dist/types/schemas/outbox.js +18 -0
- package/dist/types/schemas/outbox.js.map +1 -0
- package/dist/types/schemas/progress.d.ts +53 -0
- package/dist/types/schemas/progress.js +37 -0
- package/dist/types/schemas/progress.js.map +1 -0
- package/dist/types/schemas/settings.d.ts +16 -0
- package/dist/types/schemas/settings.js +15 -0
- package/dist/types/schemas/settings.js.map +1 -0
- package/dist/types/schemas/shared.d.ts +13 -0
- package/dist/types/schemas/shared.js +28 -0
- package/dist/types/schemas/shared.js.map +1 -0
- package/dist/types/shared.d.ts +25 -0
- package/dist/types/shared.js +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ConfigureInput, ConnectInput, Connection, Validation } from "../types/schemas/integrations.js";
|
|
2
|
+
import { Action, ActionContext, Integration } from "../types/integrations.js";
|
|
3
|
+
import "../types/index.js";
|
|
4
|
+
import { CredentialStore, OutboxAppender, UnitOfWork } from "../shared/ports.js";
|
|
5
|
+
import "./model.js";
|
|
6
|
+
//#region src/integrations/ports.d.ts
|
|
7
|
+
/** The integrations declared at startup. Unknown ids are rejected by the service. */
|
|
8
|
+
interface IntegrationsRegistry {
|
|
9
|
+
get(id: string): Integration | null;
|
|
10
|
+
list(): Integration[];
|
|
11
|
+
}
|
|
12
|
+
/** A declared integration as surfaced to clients: id, config schema, actions. */
|
|
13
|
+
interface AvailableIntegration {
|
|
14
|
+
id: string;
|
|
15
|
+
configSchema: Record<string, unknown>;
|
|
16
|
+
secretsSchema: Record<string, unknown>;
|
|
17
|
+
actions: {
|
|
18
|
+
id: string;
|
|
19
|
+
description: string;
|
|
20
|
+
inputSchema: Record<string, unknown>;
|
|
21
|
+
outputSchema: Record<string, unknown>;
|
|
22
|
+
}[];
|
|
23
|
+
}
|
|
24
|
+
interface IntegrationsService {
|
|
25
|
+
/** The integrations declared in this deployment, with their config schemas. */
|
|
26
|
+
available(): AvailableIntegration[];
|
|
27
|
+
/** Establish a connection for an org; stores its credential and configuration. */
|
|
28
|
+
connect(orgId: string, input: ConnectInput): Promise<Connection>;
|
|
29
|
+
/** Replace a connection's stored secrets (re-auth / token refresh). */
|
|
30
|
+
reconnect(orgId: string, id: string, secrets: Record<string, unknown>): Promise<Connection | null>;
|
|
31
|
+
/** Set or change a connection's configuration and/or active flag. */
|
|
32
|
+
configure(orgId: string, id: string, input: ConfigureInput): Promise<Connection | null>;
|
|
33
|
+
/** Remove a connection and destroy its credential. */
|
|
34
|
+
disconnect(orgId: string, id: string): Promise<boolean>;
|
|
35
|
+
list(orgId: string): Promise<Connection[]>;
|
|
36
|
+
get(orgId: string, id: string): Promise<Connection | null>;
|
|
37
|
+
/** The connection consumers (billing, automations) resolve an integration by id. */
|
|
38
|
+
getByIntegration(orgId: string, integrationId: string): Promise<Connection | null>;
|
|
39
|
+
/** Invoke one of a connection's actions with its credential revealed at point
|
|
40
|
+
* of use (e.g. a listing action that feeds a client-side picker). */
|
|
41
|
+
invoke(orgId: string, connectionId: string, actionId: string, input: Record<string, unknown>): Promise<unknown>;
|
|
42
|
+
}
|
|
43
|
+
interface ConnectionsRepository {
|
|
44
|
+
insert(orgId: string, connection: Connection): Promise<Connection>;
|
|
45
|
+
findById(orgId: string, id: string): Promise<Connection | null>;
|
|
46
|
+
findByIntegration(orgId: string, integrationId: string): Promise<Connection | null>;
|
|
47
|
+
list(orgId: string): Promise<Connection[]>;
|
|
48
|
+
update(orgId: string, id: string, patch: Partial<Pick<Connection, 'config' | 'active' | 'updatedAt'>>): Promise<Connection | null>;
|
|
49
|
+
delete(orgId: string, id: string): Promise<boolean>;
|
|
50
|
+
}
|
|
51
|
+
/** Tx-scoped port bundle for this context's mutating use cases. Folding the
|
|
52
|
+
* credential store in makes credential + connection writes + outbox append
|
|
53
|
+
* one transaction (closes the historical orphan-credential window). */
|
|
54
|
+
interface IntegrationsTxScope {
|
|
55
|
+
connections: ConnectionsRepository;
|
|
56
|
+
credentials: CredentialStore;
|
|
57
|
+
outbox: OutboxAppender;
|
|
58
|
+
}
|
|
59
|
+
type IntegrationsUnitOfWork = UnitOfWork<IntegrationsTxScope>;
|
|
60
|
+
//#endregion
|
|
61
|
+
export { type Action, type ActionContext, AvailableIntegration, ConnectionsRepository, type Integration, IntegrationsRegistry, IntegrationsService, IntegrationsTxScope, IntegrationsUnitOfWork, type Validation };
|
|
62
|
+
//# sourceMappingURL=ports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Integration } from "../types/integrations.js";
|
|
2
|
+
import { IntegrationsRegistry } from "./ports.js";
|
|
3
|
+
//#region src/integrations/registry.d.ts
|
|
4
|
+
declare function createIntegrationsRegistry(integrations: Integration[]): IntegrationsRegistry;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { createIntegrationsRegistry };
|
|
7
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/integrations/registry.ts
|
|
2
|
+
function createIntegrationsRegistry(integrations) {
|
|
3
|
+
const byId = /* @__PURE__ */ new Map();
|
|
4
|
+
for (const integration of integrations) {
|
|
5
|
+
if (byId.has(integration.id)) throw new Error(`duplicate integration id "${integration.id}"`);
|
|
6
|
+
byId.set(integration.id, integration);
|
|
7
|
+
}
|
|
8
|
+
return {
|
|
9
|
+
get: (id) => byId.get(id) ?? null,
|
|
10
|
+
list: () => [...byId.values()]
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { createIntegrationsRegistry };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","names":[],"sources":["../../src/integrations/registry.ts"],"sourcesContent":["// integrations context — registry. The set of available integrations is\n// declared once at startup (composition passes the modules in); the service\n// consults it to reject unknown integration ids and to validate config.\nimport type { Integration, IntegrationsRegistry } from './ports.js';\n\nexport function createIntegrationsRegistry(integrations: Integration[]): IntegrationsRegistry {\n const byId = new Map<string, Integration>();\n for (const integration of integrations) {\n if (byId.has(integration.id)) {\n throw new Error(`duplicate integration id \"${integration.id}\"`);\n }\n byId.set(integration.id, integration);\n }\n return {\n get: (id) => byId.get(id) ?? null,\n list: () => [...byId.values()],\n };\n}\n"],"mappings":";AAKA,SAAgB,2BAA2B,cAAmD;CAC5F,MAAM,uBAAO,IAAI,IAAyB;CAC1C,KAAK,MAAM,eAAe,cAAc;EACtC,IAAI,KAAK,IAAI,YAAY,EAAE,GACzB,MAAM,IAAI,MAAM,6BAA6B,YAAY,GAAG,EAAE;EAEhE,KAAK,IAAI,YAAY,IAAI,WAAW;CACtC;CACA,OAAO;EACL,MAAM,OAAO,KAAK,IAAI,EAAE,KAAK;EAC7B,YAAY,CAAC,GAAG,KAAK,OAAO,CAAC;CAC/B;AACF"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Logger } from "../types/ports.js";
|
|
2
|
+
import { ConfigureInput, ConnectInput, Connection } from "../types/schemas/integrations.js";
|
|
3
|
+
import { CredentialStore } from "../shared/ports.js";
|
|
4
|
+
import "./model.js";
|
|
5
|
+
import { ConnectionsRepository, IntegrationsRegistry, IntegrationsService, IntegrationsUnitOfWork } from "./ports.js";
|
|
6
|
+
//#region src/integrations/service.d.ts
|
|
7
|
+
type IntegrationsServiceParams = {
|
|
8
|
+
registry: IntegrationsRegistry;
|
|
9
|
+
/** Read-only access (find/list) — runs outside any transaction. */
|
|
10
|
+
repo: ConnectionsRepository;
|
|
11
|
+
/** Atomic write scope: tx-bound connections repo + credential store + outbox. */
|
|
12
|
+
uow: IntegrationsUnitOfWork;
|
|
13
|
+
/** Point-of-use credential reveal for action invocation — reads only. */
|
|
14
|
+
credentials: CredentialStore;
|
|
15
|
+
logger?: Logger;
|
|
16
|
+
};
|
|
17
|
+
declare class IntegrationsServiceImpl implements IntegrationsService {
|
|
18
|
+
private readonly registry;
|
|
19
|
+
private readonly repo;
|
|
20
|
+
private readonly uow;
|
|
21
|
+
private readonly credentials;
|
|
22
|
+
private readonly logger;
|
|
23
|
+
constructor(params: IntegrationsServiceParams);
|
|
24
|
+
available(): {
|
|
25
|
+
id: string;
|
|
26
|
+
configSchema: Record<string, unknown>;
|
|
27
|
+
secretsSchema: Record<string, unknown>;
|
|
28
|
+
actions: {
|
|
29
|
+
id: string;
|
|
30
|
+
description: string;
|
|
31
|
+
inputSchema: Record<string, unknown>;
|
|
32
|
+
outputSchema: Record<string, unknown>;
|
|
33
|
+
}[];
|
|
34
|
+
}[];
|
|
35
|
+
private validate;
|
|
36
|
+
connect(orgId: string, input: ConnectInput): Promise<Connection>;
|
|
37
|
+
reconnect(orgId: string, id: string, secrets: Record<string, unknown>): Promise<Connection | null>;
|
|
38
|
+
configure(orgId: string, id: string, input: ConfigureInput): Promise<Connection | null>;
|
|
39
|
+
disconnect(orgId: string, id: string): Promise<boolean>;
|
|
40
|
+
list(orgId: string): Promise<Connection[]>;
|
|
41
|
+
get(orgId: string, id: string): Promise<Connection | null>;
|
|
42
|
+
getByIntegration(orgId: string, integrationId: string): Promise<Connection | null>;
|
|
43
|
+
invoke(orgId: string, connectionId: string, actionId: string, input: Record<string, unknown>): Promise<unknown>;
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { IntegrationsServiceImpl, IntegrationsServiceParams };
|
|
47
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { genId } from "../shared/id.js";
|
|
2
|
+
import { noopLogger } from "../shared/logger.js";
|
|
3
|
+
import { ConflictError, NotFoundError } from "../shared/errors.js";
|
|
4
|
+
import { ActionInvocationError, AlreadyConnectedError, InvalidConfigError, UnknownIntegrationError } from "./model.js";
|
|
5
|
+
import { integrationEvents } from "./events.js";
|
|
6
|
+
//#region src/integrations/service.ts
|
|
7
|
+
var IntegrationsServiceImpl = class {
|
|
8
|
+
registry;
|
|
9
|
+
repo;
|
|
10
|
+
uow;
|
|
11
|
+
credentials;
|
|
12
|
+
logger;
|
|
13
|
+
constructor(params) {
|
|
14
|
+
this.registry = params.registry;
|
|
15
|
+
this.repo = params.repo;
|
|
16
|
+
this.uow = params.uow;
|
|
17
|
+
this.credentials = params.credentials;
|
|
18
|
+
this.logger = params.logger ?? noopLogger;
|
|
19
|
+
}
|
|
20
|
+
available() {
|
|
21
|
+
return this.registry.list().map((integration) => ({
|
|
22
|
+
id: integration.id,
|
|
23
|
+
configSchema: integration.configSchema(),
|
|
24
|
+
secretsSchema: integration.secretsSchema(),
|
|
25
|
+
actions: integration.actions.map((action) => ({
|
|
26
|
+
id: action.id,
|
|
27
|
+
description: action.description,
|
|
28
|
+
inputSchema: action.inputSchema(),
|
|
29
|
+
outputSchema: action.outputSchema()
|
|
30
|
+
}))
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
validate(integrationId, config) {
|
|
34
|
+
const integration = this.registry.get(integrationId);
|
|
35
|
+
if (!integration) {
|
|
36
|
+
this.logger.warn("unknown integration rejected", { integrationId });
|
|
37
|
+
throw new UnknownIntegrationError(integrationId);
|
|
38
|
+
}
|
|
39
|
+
const result = integration.validateConfig(config);
|
|
40
|
+
if (!result.ok) {
|
|
41
|
+
this.logger.warn("invalid integration config rejected", {
|
|
42
|
+
integrationId,
|
|
43
|
+
errors: result.errors
|
|
44
|
+
});
|
|
45
|
+
throw new InvalidConfigError(integrationId, result.errors);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async connect(orgId, input) {
|
|
49
|
+
const config = input.config ?? {};
|
|
50
|
+
this.validate(input.integrationId, config);
|
|
51
|
+
if (await this.repo.findByIntegration(orgId, input.integrationId)) {
|
|
52
|
+
this.logger.warn("duplicate connection rejected", {
|
|
53
|
+
orgId,
|
|
54
|
+
integrationId: input.integrationId
|
|
55
|
+
});
|
|
56
|
+
throw new AlreadyConnectedError(input.integrationId);
|
|
57
|
+
}
|
|
58
|
+
const connection = await this.uow.run(async ({ connections, credentials, outbox }) => {
|
|
59
|
+
const credentialRef = await credentials.store(orgId, input.secrets);
|
|
60
|
+
const at = /* @__PURE__ */ new Date();
|
|
61
|
+
const created = await connections.insert(orgId, {
|
|
62
|
+
orgId,
|
|
63
|
+
id: genId("connection"),
|
|
64
|
+
integrationId: input.integrationId,
|
|
65
|
+
config,
|
|
66
|
+
active: true,
|
|
67
|
+
credentialRef,
|
|
68
|
+
createdAt: at,
|
|
69
|
+
updatedAt: at
|
|
70
|
+
});
|
|
71
|
+
await outbox.append([integrationEvents.connectionCreated.make({
|
|
72
|
+
orgId,
|
|
73
|
+
data: created
|
|
74
|
+
})]);
|
|
75
|
+
return created;
|
|
76
|
+
});
|
|
77
|
+
this.logger.info("integration connected", {
|
|
78
|
+
orgId,
|
|
79
|
+
integrationId: connection.integrationId,
|
|
80
|
+
connectionId: connection.id
|
|
81
|
+
});
|
|
82
|
+
return connection;
|
|
83
|
+
}
|
|
84
|
+
async reconnect(orgId, id, secrets) {
|
|
85
|
+
const connection = await this.repo.findById(orgId, id);
|
|
86
|
+
if (!connection) return null;
|
|
87
|
+
const updated = await this.uow.run(async ({ connections, credentials, outbox }) => {
|
|
88
|
+
await credentials.update(orgId, connection.credentialRef, secrets);
|
|
89
|
+
const result = await connections.update(orgId, id, { updatedAt: /* @__PURE__ */ new Date() });
|
|
90
|
+
if (result) await outbox.append([integrationEvents.connectionUpdated.make({
|
|
91
|
+
orgId,
|
|
92
|
+
data: result
|
|
93
|
+
})]);
|
|
94
|
+
return result;
|
|
95
|
+
});
|
|
96
|
+
this.logger.info("integration credentials rotated", {
|
|
97
|
+
orgId,
|
|
98
|
+
connectionId: id,
|
|
99
|
+
integrationId: connection.integrationId
|
|
100
|
+
});
|
|
101
|
+
return updated;
|
|
102
|
+
}
|
|
103
|
+
async configure(orgId, id, input) {
|
|
104
|
+
const connection = await this.repo.findById(orgId, id);
|
|
105
|
+
if (!connection) return null;
|
|
106
|
+
if (input.config !== void 0) this.validate(connection.integrationId, input.config);
|
|
107
|
+
const updated = await this.uow.run(async ({ connections, outbox }) => {
|
|
108
|
+
const result = await connections.update(orgId, id, {
|
|
109
|
+
...input.config !== void 0 ? { config: input.config } : {},
|
|
110
|
+
...input.active !== void 0 ? { active: input.active } : {},
|
|
111
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
112
|
+
});
|
|
113
|
+
if (result) await outbox.append([integrationEvents.connectionUpdated.make({
|
|
114
|
+
orgId,
|
|
115
|
+
data: result
|
|
116
|
+
})]);
|
|
117
|
+
return result;
|
|
118
|
+
});
|
|
119
|
+
this.logger.info("integration configured", {
|
|
120
|
+
orgId,
|
|
121
|
+
connectionId: id,
|
|
122
|
+
integrationId: connection.integrationId
|
|
123
|
+
});
|
|
124
|
+
return updated;
|
|
125
|
+
}
|
|
126
|
+
async disconnect(orgId, id) {
|
|
127
|
+
const connection = await this.repo.findById(orgId, id);
|
|
128
|
+
if (!connection) return false;
|
|
129
|
+
const deleted = await this.uow.run(async ({ connections, credentials, outbox }) => {
|
|
130
|
+
const ok = await connections.delete(orgId, id);
|
|
131
|
+
await credentials.destroy(orgId, connection.credentialRef);
|
|
132
|
+
await outbox.append([integrationEvents.connectionRemoved.make({
|
|
133
|
+
orgId,
|
|
134
|
+
data: connection
|
|
135
|
+
})]);
|
|
136
|
+
return ok;
|
|
137
|
+
});
|
|
138
|
+
this.logger.info("integration disconnected", {
|
|
139
|
+
orgId,
|
|
140
|
+
connectionId: id,
|
|
141
|
+
integrationId: connection.integrationId
|
|
142
|
+
});
|
|
143
|
+
return deleted;
|
|
144
|
+
}
|
|
145
|
+
list(orgId) {
|
|
146
|
+
return this.repo.list(orgId);
|
|
147
|
+
}
|
|
148
|
+
get(orgId, id) {
|
|
149
|
+
return this.repo.findById(orgId, id);
|
|
150
|
+
}
|
|
151
|
+
getByIntegration(orgId, integrationId) {
|
|
152
|
+
return this.repo.findByIntegration(orgId, integrationId);
|
|
153
|
+
}
|
|
154
|
+
async invoke(orgId, connectionId, actionId, input) {
|
|
155
|
+
const connection = await this.repo.findById(orgId, connectionId);
|
|
156
|
+
if (!connection) throw new NotFoundError("Connection", connectionId);
|
|
157
|
+
if (!connection.active) throw new ConflictError(`connection "${connectionId}" is inactive`);
|
|
158
|
+
const integration = this.registry.get(connection.integrationId);
|
|
159
|
+
if (!integration) throw new UnknownIntegrationError(connection.integrationId);
|
|
160
|
+
const action = integration.actions.find((a) => a.id === actionId);
|
|
161
|
+
if (!action) throw new NotFoundError("Action", actionId);
|
|
162
|
+
const secrets = await this.credentials.reveal(orgId, connection.credentialRef);
|
|
163
|
+
if (!secrets) throw new NotFoundError("Credential", connectionId);
|
|
164
|
+
try {
|
|
165
|
+
return await action.invoke({
|
|
166
|
+
secrets,
|
|
167
|
+
config: connection.config
|
|
168
|
+
}, input);
|
|
169
|
+
} catch (err) {
|
|
170
|
+
this.logger.warn("integration action failed", {
|
|
171
|
+
orgId,
|
|
172
|
+
connectionId,
|
|
173
|
+
integrationId: connection.integrationId,
|
|
174
|
+
actionId,
|
|
175
|
+
error: err instanceof Error ? err.message : String(err)
|
|
176
|
+
});
|
|
177
|
+
throw new ActionInvocationError(connection.integrationId, actionId, err);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
//#endregion
|
|
182
|
+
export { IntegrationsServiceImpl };
|
|
183
|
+
|
|
184
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","names":[],"sources":["../../src/integrations/service.ts"],"sourcesContent":["// integrations context — service implementation (inbound port). Owns the\n// connection lifecycle; credentials live in the shared secure credential store\n// (this service holds only the ref). The integrations the system supports are\n// declared at startup via the IntegrationsRegistry: connect/configure reject\n// unknown integration ids and validate config with the integration's own\n// validator. The domain never calls the external service — consumers take the\n// connection, reveal the credential at point of use, and build their own adapter.\n//\n// Mutations run inside the context's UnitOfWork: credential writes, connection\n// writes, and the outbox append commit in ONE transaction (transactional\n// outbox; this also closed the historical orphan-credential window). The\n// outbox relay — not this service — publishes to the EventBus.\nimport { genId } from '../shared/id.js';\nimport {\n ActionInvocationError,\n AlreadyConnectedError,\n InvalidConfigError,\n UnknownIntegrationError,\n type ConfigureInput,\n type ConnectInput,\n type Connection,\n} from './model.js';\nimport type {\n ConnectionsRepository,\n IntegrationsRegistry,\n IntegrationsService,\n IntegrationsUnitOfWork,\n} from './ports.js';\nimport type { CredentialStore, Logger } from '../shared/ports.js';\nimport { ConflictError, NotFoundError } from '../shared/errors.js';\nimport { noopLogger } from '../shared/logger.js';\nimport { integrationEvents } from './events.js';\n\nexport type IntegrationsServiceParams = {\n registry: IntegrationsRegistry;\n /** Read-only access (find/list) — runs outside any transaction. */\n repo: ConnectionsRepository;\n /** Atomic write scope: tx-bound connections repo + credential store + outbox. */\n uow: IntegrationsUnitOfWork;\n /** Point-of-use credential reveal for action invocation — reads only. */\n credentials: CredentialStore;\n logger?: Logger;\n};\n\nexport class IntegrationsServiceImpl implements IntegrationsService {\n private readonly registry: IntegrationsRegistry;\n private readonly repo: ConnectionsRepository;\n private readonly uow: IntegrationsUnitOfWork;\n private readonly credentials: CredentialStore;\n private readonly logger: Logger;\n\n constructor(params: IntegrationsServiceParams) {\n this.registry = params.registry;\n this.repo = params.repo;\n this.uow = params.uow;\n this.credentials = params.credentials;\n this.logger = params.logger ?? noopLogger;\n }\n\n available() {\n return this.registry.list().map((integration) => ({\n id: integration.id,\n configSchema: integration.configSchema(),\n secretsSchema: integration.secretsSchema(),\n actions: integration.actions.map((action) => ({\n id: action.id,\n description: action.description,\n inputSchema: action.inputSchema(),\n outputSchema: action.outputSchema(),\n })),\n }));\n }\n\n private validate(integrationId: string, config: Record<string, unknown>): void {\n const integration = this.registry.get(integrationId);\n if (!integration) {\n this.logger.warn('unknown integration rejected', { integrationId });\n throw new UnknownIntegrationError(integrationId);\n }\n const result = integration.validateConfig(config);\n if (!result.ok) {\n this.logger.warn('invalid integration config rejected', {\n integrationId,\n errors: result.errors,\n });\n throw new InvalidConfigError(integrationId, result.errors);\n }\n }\n\n async connect(orgId: string, input: ConnectInput): Promise<Connection> {\n const config = input.config ?? {};\n this.validate(input.integrationId, config);\n const existing = await this.repo.findByIntegration(orgId, input.integrationId);\n if (existing) {\n this.logger.warn('duplicate connection rejected', {\n orgId,\n integrationId: input.integrationId,\n });\n throw new AlreadyConnectedError(input.integrationId);\n }\n const connection = await this.uow.run(async ({ connections, credentials, outbox }) => {\n const credentialRef = await credentials.store(orgId, input.secrets);\n const at = new Date();\n const created = await connections.insert(orgId, {\n orgId,\n id: genId('connection'),\n integrationId: input.integrationId,\n config,\n active: true,\n credentialRef,\n createdAt: at,\n updatedAt: at,\n });\n await outbox.append([\n integrationEvents.connectionCreated.make({\n orgId,\n data: created,\n }),\n ]);\n return created;\n });\n this.logger.info('integration connected', {\n orgId,\n integrationId: connection.integrationId,\n connectionId: connection.id,\n });\n return connection;\n }\n\n async reconnect(\n orgId: string,\n id: string,\n secrets: Record<string, unknown>,\n ): Promise<Connection | null> {\n const connection = await this.repo.findById(orgId, id);\n if (!connection) {\n return null;\n }\n const updated = await this.uow.run(async ({ connections, credentials, outbox }) => {\n await credentials.update(orgId, connection.credentialRef, secrets);\n const result = await connections.update(orgId, id, { updatedAt: new Date() });\n if (result) {\n await outbox.append([\n integrationEvents.connectionUpdated.make({ orgId, data: result }),\n ]);\n }\n return result;\n });\n this.logger.info('integration credentials rotated', {\n orgId,\n connectionId: id,\n integrationId: connection.integrationId,\n });\n return updated;\n }\n\n async configure(orgId: string, id: string, input: ConfigureInput): Promise<Connection | null> {\n const connection = await this.repo.findById(orgId, id);\n if (!connection) {\n return null;\n }\n if (input.config !== undefined) {\n this.validate(connection.integrationId, input.config);\n }\n const updated = await this.uow.run(async ({ connections, outbox }) => {\n const result = await connections.update(orgId, id, {\n ...(input.config !== undefined ? { config: input.config } : {}),\n ...(input.active !== undefined ? { active: input.active } : {}),\n updatedAt: new Date(),\n });\n if (result) {\n await outbox.append([\n integrationEvents.connectionUpdated.make({ orgId, data: result }),\n ]);\n }\n return result;\n });\n this.logger.info('integration configured', {\n orgId,\n connectionId: id,\n integrationId: connection.integrationId,\n });\n return updated;\n }\n\n async disconnect(orgId: string, id: string): Promise<boolean> {\n const connection = await this.repo.findById(orgId, id);\n if (!connection) {\n return false;\n }\n const deleted = await this.uow.run(async ({ connections, credentials, outbox }) => {\n // Connection first: it holds the FK onto the credential row.\n const ok = await connections.delete(orgId, id);\n await credentials.destroy(orgId, connection.credentialRef);\n await outbox.append([\n integrationEvents.connectionRemoved.make({ orgId, data: connection }),\n ]);\n return ok;\n });\n this.logger.info('integration disconnected', {\n orgId,\n connectionId: id,\n integrationId: connection.integrationId,\n });\n return deleted;\n }\n\n list(orgId: string): Promise<Connection[]> {\n return this.repo.list(orgId);\n }\n\n get(orgId: string, id: string): Promise<Connection | null> {\n return this.repo.findById(orgId, id);\n }\n\n getByIntegration(orgId: string, integrationId: string): Promise<Connection | null> {\n return this.repo.findByIntegration(orgId, integrationId);\n }\n\n async invoke(\n orgId: string,\n connectionId: string,\n actionId: string,\n input: Record<string, unknown>,\n ): Promise<unknown> {\n const connection = await this.repo.findById(orgId, connectionId);\n if (!connection) {\n throw new NotFoundError('Connection', connectionId);\n }\n if (!connection.active) {\n throw new ConflictError(`connection \"${connectionId}\" is inactive`);\n }\n const integration = this.registry.get(connection.integrationId);\n if (!integration) {\n throw new UnknownIntegrationError(connection.integrationId);\n }\n const action = integration.actions.find((a) => a.id === actionId);\n if (!action) {\n throw new NotFoundError('Action', actionId);\n }\n const secrets = await this.credentials.reveal(orgId, connection.credentialRef);\n if (!secrets) {\n throw new NotFoundError('Credential', connectionId);\n }\n try {\n return await action.invoke({ secrets, config: connection.config }, input);\n } catch (err) {\n this.logger.warn('integration action failed', {\n orgId,\n connectionId,\n integrationId: connection.integrationId,\n actionId,\n error: err instanceof Error ? err.message : String(err),\n });\n throw new ActionInvocationError(connection.integrationId, actionId, err);\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Validation } from "../types/schemas/integrations.js";
|
|
2
|
+
import { Action, ActionContext } from "../types/integrations.js";
|
|
3
|
+
import "../types/index.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
//#region src/integrations/zod.d.ts
|
|
6
|
+
declare function zodConfig(schema: z.ZodType): {
|
|
7
|
+
configSchema: () => Record<string, unknown>;
|
|
8
|
+
validateConfig: (config: unknown) => Validation;
|
|
9
|
+
};
|
|
10
|
+
/** Derive the Integration contract's secretsSchema getter from a zod schema. */
|
|
11
|
+
declare function zodSecrets(schema: z.ZodType): {
|
|
12
|
+
secretsSchema: () => Record<string, unknown>;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Build an Action from zod schemas + a run function. `invoke` parses the input
|
|
16
|
+
* first (defaults applied, throws on mismatch), so `run` always sees typed,
|
|
17
|
+
* valid input. The output schema documents the resolved shape for consumers;
|
|
18
|
+
* it is not re-validated at runtime.
|
|
19
|
+
*/
|
|
20
|
+
declare function zodAction<I extends z.ZodType, O extends z.ZodType>(def: {
|
|
21
|
+
id: string;
|
|
22
|
+
description: string;
|
|
23
|
+
input: I;
|
|
24
|
+
output: O;
|
|
25
|
+
run(ctx: ActionContext, input: z.infer<I>): Promise<z.infer<O>>;
|
|
26
|
+
}): Action;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { zodAction, zodConfig, zodSecrets };
|
|
29
|
+
//# sourceMappingURL=zod.d.ts.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
//#region src/integrations/zod.ts
|
|
3
|
+
function toErrors(error) {
|
|
4
|
+
return error.issues.map((issue) => issue.path.length ? `${issue.path.join(".")}: ${issue.message}` : issue.message);
|
|
5
|
+
}
|
|
6
|
+
function zodConfig(schema) {
|
|
7
|
+
return {
|
|
8
|
+
configSchema: () => z.toJSONSchema(schema),
|
|
9
|
+
validateConfig: (config) => {
|
|
10
|
+
const result = schema.safeParse(config ?? {});
|
|
11
|
+
return result.success ? { ok: true } : {
|
|
12
|
+
ok: false,
|
|
13
|
+
errors: toErrors(result.error)
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** Derive the Integration contract's secretsSchema getter from a zod schema. */
|
|
19
|
+
function zodSecrets(schema) {
|
|
20
|
+
return { secretsSchema: () => z.toJSONSchema(schema) };
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Build an Action from zod schemas + a run function. `invoke` parses the input
|
|
24
|
+
* first (defaults applied, throws on mismatch), so `run` always sees typed,
|
|
25
|
+
* valid input. The output schema documents the resolved shape for consumers;
|
|
26
|
+
* it is not re-validated at runtime.
|
|
27
|
+
*/
|
|
28
|
+
function zodAction(def) {
|
|
29
|
+
return {
|
|
30
|
+
id: def.id,
|
|
31
|
+
description: def.description,
|
|
32
|
+
inputSchema: () => z.toJSONSchema(def.input),
|
|
33
|
+
outputSchema: () => z.toJSONSchema(def.output),
|
|
34
|
+
invoke: async (ctx, input) => def.run(ctx, def.input.parse(input ?? {}))
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { zodAction, zodConfig, zodSecrets };
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=zod.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zod.js","names":[],"sources":["../../src/integrations/zod.ts"],"sourcesContent":["// Zod helpers for integration modules. An integration declares its config and\n// each action's input/output as zod schemas once; these adapt them to the\n// contract's JSON-Schema getters and validators.\nimport { z } from 'zod';\nimport type { Action, ActionContext, Validation } from '../types/index.js';\n\nfunction toErrors(error: z.ZodError): string[] {\n return error.issues.map((issue) =>\n issue.path.length ? `${issue.path.join('.')}: ${issue.message}` : issue.message,\n );\n}\n\nexport function zodConfig(schema: z.ZodType): {\n configSchema: () => Record<string, unknown>;\n validateConfig: (config: unknown) => Validation;\n} {\n return {\n configSchema: () => z.toJSONSchema(schema) as Record<string, unknown>,\n validateConfig: (config) => {\n const result = schema.safeParse(config ?? {});\n return result.success ? { ok: true } : { ok: false, errors: toErrors(result.error) };\n },\n };\n}\n\n/** Derive the Integration contract's secretsSchema getter from a zod schema. */\nexport function zodSecrets(schema: z.ZodType): {\n secretsSchema: () => Record<string, unknown>;\n} {\n return { secretsSchema: () => z.toJSONSchema(schema) as Record<string, unknown> };\n}\n\n/**\n * Build an Action from zod schemas + a run function. `invoke` parses the input\n * first (defaults applied, throws on mismatch), so `run` always sees typed,\n * valid input. The output schema documents the resolved shape for consumers;\n * it is not re-validated at runtime.\n */\nexport function zodAction<I extends z.ZodType, O extends z.ZodType>(def: {\n id: string;\n description: string;\n input: I;\n output: O;\n run(ctx: ActionContext, input: z.infer<I>): Promise<z.infer<O>>;\n}): Action {\n return {\n id: def.id,\n description: def.description,\n inputSchema: () => z.toJSONSchema(def.input) as Record<string, unknown>,\n outputSchema: () => z.toJSONSchema(def.output) as Record<string, unknown>,\n invoke: async (ctx, input) => def.run(ctx, def.input.parse(input ?? {}) as z.infer<I>),\n };\n}\n"],"mappings":";;AAMA,SAAS,SAAS,OAA6B;CAC7C,OAAO,MAAM,OAAO,KAAK,UACvB,MAAM,KAAK,SAAS,GAAG,MAAM,KAAK,KAAK,GAAG,EAAE,IAAI,MAAM,YAAY,MAAM,OAC1E;AACF;AAEA,SAAgB,UAAU,QAGxB;CACA,OAAO;EACL,oBAAoB,EAAE,aAAa,MAAM;EACzC,iBAAiB,WAAW;GAC1B,MAAM,SAAS,OAAO,UAAU,UAAU,CAAC,CAAC;GAC5C,OAAO,OAAO,UAAU,EAAE,IAAI,KAAK,IAAI;IAAE,IAAI;IAAO,QAAQ,SAAS,OAAO,KAAK;GAAE;EACrF;CACF;AACF;;AAGA,SAAgB,WAAW,QAEzB;CACA,OAAO,EAAE,qBAAqB,EAAE,aAAa,MAAM,EAA6B;AAClF;;;;;;;AAQA,SAAgB,UAAoD,KAMzD;CACT,OAAO;EACL,IAAI,IAAI;EACR,aAAa,IAAI;EACjB,mBAAmB,EAAE,aAAa,IAAI,KAAK;EAC3C,oBAAoB,EAAE,aAAa,IAAI,MAAM;EAC7C,QAAQ,OAAO,KAAK,UAAU,IAAI,IAAI,KAAK,IAAI,MAAM,MAAM,SAAS,CAAC,CAAC,CAAe;CACvF;AACF"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { EventOf, EventOfValues, RuntimeEventDefinition, VersionedDomainEvent } from "../shared/events.js";
|
|
2
|
+
import "../shared/ports.js";
|
|
3
|
+
//#region src/organizations/events.d.ts
|
|
4
|
+
declare const organizationEvents: {
|
|
5
|
+
organizationCreated: RuntimeEventDefinition<VersionedDomainEvent<{
|
|
6
|
+
id: string;
|
|
7
|
+
externalId: string | null;
|
|
8
|
+
name: string;
|
|
9
|
+
slug: string;
|
|
10
|
+
ownerId: string;
|
|
11
|
+
createdAt: Date;
|
|
12
|
+
updatedAt: Date;
|
|
13
|
+
}, "organization.created", 1>, {
|
|
14
|
+
id: string;
|
|
15
|
+
externalId: string | null;
|
|
16
|
+
name: string;
|
|
17
|
+
slug: string;
|
|
18
|
+
ownerId: string;
|
|
19
|
+
createdAt: unknown;
|
|
20
|
+
updatedAt: unknown;
|
|
21
|
+
}>;
|
|
22
|
+
organizationUpdated: RuntimeEventDefinition<VersionedDomainEvent<{
|
|
23
|
+
id: string;
|
|
24
|
+
externalId: string | null;
|
|
25
|
+
name: string;
|
|
26
|
+
slug: string;
|
|
27
|
+
ownerId: string;
|
|
28
|
+
createdAt: Date;
|
|
29
|
+
updatedAt: Date;
|
|
30
|
+
}, "organization.updated", 1>, {
|
|
31
|
+
id: string;
|
|
32
|
+
externalId: string | null;
|
|
33
|
+
name: string;
|
|
34
|
+
slug: string;
|
|
35
|
+
ownerId: string;
|
|
36
|
+
createdAt: unknown;
|
|
37
|
+
updatedAt: unknown;
|
|
38
|
+
}>;
|
|
39
|
+
organizationDeleted: RuntimeEventDefinition<VersionedDomainEvent<{
|
|
40
|
+
id: string;
|
|
41
|
+
externalId: string | null;
|
|
42
|
+
name: string;
|
|
43
|
+
slug: string;
|
|
44
|
+
ownerId: string;
|
|
45
|
+
createdAt: Date;
|
|
46
|
+
updatedAt: Date;
|
|
47
|
+
}, "organization.deleted", 1>, {
|
|
48
|
+
id: string;
|
|
49
|
+
externalId: string | null;
|
|
50
|
+
name: string;
|
|
51
|
+
slug: string;
|
|
52
|
+
ownerId: string;
|
|
53
|
+
createdAt: unknown;
|
|
54
|
+
updatedAt: unknown;
|
|
55
|
+
}>;
|
|
56
|
+
orgUserLinked: RuntimeEventDefinition<VersionedDomainEvent<{
|
|
57
|
+
id: string;
|
|
58
|
+
orgId: string;
|
|
59
|
+
userId: string;
|
|
60
|
+
role: "owner" | "admin" | "instructor" | "student";
|
|
61
|
+
status: "active" | "invited";
|
|
62
|
+
createdAt: Date;
|
|
63
|
+
updatedAt: Date;
|
|
64
|
+
}, "organization.user.linked", 1>, {
|
|
65
|
+
id: string;
|
|
66
|
+
orgId: string;
|
|
67
|
+
userId: string;
|
|
68
|
+
role: "owner" | "admin" | "instructor" | "student";
|
|
69
|
+
status: "active" | "invited";
|
|
70
|
+
createdAt: unknown;
|
|
71
|
+
updatedAt: unknown;
|
|
72
|
+
}>;
|
|
73
|
+
orgUserDeleted: RuntimeEventDefinition<VersionedDomainEvent<{
|
|
74
|
+
id: string;
|
|
75
|
+
orgId: string;
|
|
76
|
+
userId: string;
|
|
77
|
+
role: "owner" | "admin" | "instructor" | "student";
|
|
78
|
+
status: "active" | "invited";
|
|
79
|
+
createdAt: Date;
|
|
80
|
+
updatedAt: Date;
|
|
81
|
+
}, "organization.user.deleted", 1>, {
|
|
82
|
+
id: string;
|
|
83
|
+
orgId: string;
|
|
84
|
+
userId: string;
|
|
85
|
+
role: "owner" | "admin" | "instructor" | "student";
|
|
86
|
+
status: "active" | "invited";
|
|
87
|
+
createdAt: unknown;
|
|
88
|
+
updatedAt: unknown;
|
|
89
|
+
}>;
|
|
90
|
+
inviteCreated: RuntimeEventDefinition<VersionedDomainEvent<{
|
|
91
|
+
id: string;
|
|
92
|
+
orgId: string;
|
|
93
|
+
email: string;
|
|
94
|
+
role: "admin" | "instructor" | "student";
|
|
95
|
+
status: "pending" | "accepted" | "rejected" | "canceled";
|
|
96
|
+
invitedBy: string;
|
|
97
|
+
tokenHash: string | null;
|
|
98
|
+
expiresAt: Date | null;
|
|
99
|
+
createdAt: Date;
|
|
100
|
+
}, "organization.invite.created", 1>, {
|
|
101
|
+
id: string;
|
|
102
|
+
orgId: string;
|
|
103
|
+
email: string;
|
|
104
|
+
role: "admin" | "instructor" | "student";
|
|
105
|
+
status: "pending" | "accepted" | "rejected" | "canceled";
|
|
106
|
+
invitedBy: string;
|
|
107
|
+
tokenHash: string | null;
|
|
108
|
+
expiresAt: unknown;
|
|
109
|
+
createdAt: unknown;
|
|
110
|
+
}>;
|
|
111
|
+
inviteAccepted: RuntimeEventDefinition<VersionedDomainEvent<{
|
|
112
|
+
id: string;
|
|
113
|
+
orgId: string;
|
|
114
|
+
email: string;
|
|
115
|
+
role: "admin" | "instructor" | "student";
|
|
116
|
+
status: "pending" | "accepted" | "rejected" | "canceled";
|
|
117
|
+
invitedBy: string;
|
|
118
|
+
tokenHash: string | null;
|
|
119
|
+
expiresAt: Date | null;
|
|
120
|
+
createdAt: Date;
|
|
121
|
+
}, "organization.invite.accepted", 1>, {
|
|
122
|
+
id: string;
|
|
123
|
+
orgId: string;
|
|
124
|
+
email: string;
|
|
125
|
+
role: "admin" | "instructor" | "student";
|
|
126
|
+
status: "pending" | "accepted" | "rejected" | "canceled";
|
|
127
|
+
invitedBy: string;
|
|
128
|
+
tokenHash: string | null;
|
|
129
|
+
expiresAt: unknown;
|
|
130
|
+
createdAt: unknown;
|
|
131
|
+
}>;
|
|
132
|
+
studentCreated: RuntimeEventDefinition<VersionedDomainEvent<{
|
|
133
|
+
id: string;
|
|
134
|
+
orgId: string;
|
|
135
|
+
userId: string;
|
|
136
|
+
role: "owner" | "admin" | "instructor" | "student";
|
|
137
|
+
status: "active" | "invited";
|
|
138
|
+
createdAt: Date;
|
|
139
|
+
updatedAt: Date;
|
|
140
|
+
}, "organization.student.created", 1>, {
|
|
141
|
+
id: string;
|
|
142
|
+
orgId: string;
|
|
143
|
+
userId: string;
|
|
144
|
+
role: "owner" | "admin" | "instructor" | "student";
|
|
145
|
+
status: "active" | "invited";
|
|
146
|
+
createdAt: unknown;
|
|
147
|
+
updatedAt: unknown;
|
|
148
|
+
}>;
|
|
149
|
+
studentDeleted: RuntimeEventDefinition<VersionedDomainEvent<{
|
|
150
|
+
id: string;
|
|
151
|
+
orgId: string;
|
|
152
|
+
userId: string;
|
|
153
|
+
role: "owner" | "admin" | "instructor" | "student";
|
|
154
|
+
status: "active" | "invited";
|
|
155
|
+
createdAt: Date;
|
|
156
|
+
updatedAt: Date;
|
|
157
|
+
}, "organization.student.deleted", 1>, {
|
|
158
|
+
id: string;
|
|
159
|
+
orgId: string;
|
|
160
|
+
userId: string;
|
|
161
|
+
role: "owner" | "admin" | "instructor" | "student";
|
|
162
|
+
status: "active" | "invited";
|
|
163
|
+
createdAt: unknown;
|
|
164
|
+
updatedAt: unknown;
|
|
165
|
+
}>;
|
|
166
|
+
studentLinked: RuntimeEventDefinition<VersionedDomainEvent<{
|
|
167
|
+
id: string;
|
|
168
|
+
orgId: string;
|
|
169
|
+
userId: string;
|
|
170
|
+
role: "owner" | "admin" | "instructor" | "student";
|
|
171
|
+
status: "active" | "invited";
|
|
172
|
+
createdAt: Date;
|
|
173
|
+
updatedAt: Date;
|
|
174
|
+
}, "organization.student.linked", 1>, {
|
|
175
|
+
id: string;
|
|
176
|
+
orgId: string;
|
|
177
|
+
userId: string;
|
|
178
|
+
role: "owner" | "admin" | "instructor" | "student";
|
|
179
|
+
status: "active" | "invited";
|
|
180
|
+
createdAt: unknown;
|
|
181
|
+
updatedAt: unknown;
|
|
182
|
+
}>;
|
|
183
|
+
};
|
|
184
|
+
type OrganizationCreated = EventOf<typeof organizationEvents.organizationCreated>;
|
|
185
|
+
type OrganizationUpdated = EventOf<typeof organizationEvents.organizationUpdated>;
|
|
186
|
+
type OrganizationDeleted = EventOf<typeof organizationEvents.organizationDeleted>;
|
|
187
|
+
type OrgUserLinked = EventOf<typeof organizationEvents.orgUserLinked>;
|
|
188
|
+
type OrgUserDeleted = EventOf<typeof organizationEvents.orgUserDeleted>;
|
|
189
|
+
type InviteCreated = EventOf<typeof organizationEvents.inviteCreated>;
|
|
190
|
+
type InviteAccepted = EventOf<typeof organizationEvents.inviteAccepted>;
|
|
191
|
+
type StudentCreated = EventOf<typeof organizationEvents.studentCreated>;
|
|
192
|
+
type StudentDeleted = EventOf<typeof organizationEvents.studentDeleted>;
|
|
193
|
+
type StudentLinked = EventOf<typeof organizationEvents.studentLinked>;
|
|
194
|
+
type OrganizationEvent = EventOfValues<typeof organizationEvents>;
|
|
195
|
+
//#endregion
|
|
196
|
+
export { InviteAccepted, InviteCreated, OrgUserDeleted, OrgUserLinked, OrganizationCreated, OrganizationDeleted, OrganizationEvent, OrganizationUpdated, StudentCreated, StudentDeleted, StudentLinked, organizationEvents };
|
|
197
|
+
//# sourceMappingURL=events.d.ts.map
|