@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.
Files changed (261) hide show
  1. package/LICENSE +21 -0
  2. package/dist/assets/events.d.ts +81 -0
  3. package/dist/assets/events.js +24 -0
  4. package/dist/assets/events.js.map +1 -0
  5. package/dist/assets/index.d.ts +7 -0
  6. package/dist/assets/index.js +3 -0
  7. package/dist/assets/model.d.ts +4 -0
  8. package/dist/assets/model.js +1 -0
  9. package/dist/assets/ports.d.ts +34 -0
  10. package/dist/assets/ports.js +1 -0
  11. package/dist/assets/service.d.ts +29 -0
  12. package/dist/assets/service.js +130 -0
  13. package/dist/assets/service.js.map +1 -0
  14. package/dist/automations/actions.d.ts +24 -0
  15. package/dist/automations/actions.js +59 -0
  16. package/dist/automations/actions.js.map +1 -0
  17. package/dist/automations/catalog.d.ts +12 -0
  18. package/dist/automations/catalog.js +224 -0
  19. package/dist/automations/catalog.js.map +1 -0
  20. package/dist/automations/events.d.ts +264 -0
  21. package/dist/automations/events.js +54 -0
  22. package/dist/automations/events.js.map +1 -0
  23. package/dist/automations/index.d.ts +9 -0
  24. package/dist/automations/index.js +4 -0
  25. package/dist/automations/model.d.ts +12 -0
  26. package/dist/automations/model.js +14 -0
  27. package/dist/automations/model.js.map +1 -0
  28. package/dist/automations/ports.d.ts +56 -0
  29. package/dist/automations/ports.js +1 -0
  30. package/dist/automations/service.d.ts +53 -0
  31. package/dist/automations/service.js +225 -0
  32. package/dist/automations/service.js.map +1 -0
  33. package/dist/automations/types.d.ts +3 -0
  34. package/dist/automations/types.js +1 -0
  35. package/dist/content/events.d.ts +410 -0
  36. package/dist/content/events.js +105 -0
  37. package/dist/content/events.js.map +1 -0
  38. package/dist/content/index.d.ts +8 -0
  39. package/dist/content/index.js +3 -0
  40. package/dist/content/model.d.ts +3 -0
  41. package/dist/content/model.js +1 -0
  42. package/dist/content/ports.d.ts +85 -0
  43. package/dist/content/ports.js +1 -0
  44. package/dist/content/service.d.ts +54 -0
  45. package/dist/content/service.js +479 -0
  46. package/dist/content/service.js.map +1 -0
  47. package/dist/content/types.d.ts +4 -0
  48. package/dist/content/types.js +1 -0
  49. package/dist/discussion/events.d.ts +101 -0
  50. package/dist/discussion/events.js +29 -0
  51. package/dist/discussion/events.js.map +1 -0
  52. package/dist/discussion/index.d.ts +8 -0
  53. package/dist/discussion/index.js +3 -0
  54. package/dist/discussion/model.d.ts +5 -0
  55. package/dist/discussion/model.js +1 -0
  56. package/dist/discussion/ports.d.ts +112 -0
  57. package/dist/discussion/ports.js +1 -0
  58. package/dist/discussion/service.d.ts +67 -0
  59. package/dist/discussion/service.js +343 -0
  60. package/dist/discussion/service.js.map +1 -0
  61. package/dist/discussion/types.d.ts +4 -0
  62. package/dist/discussion/types.js +1 -0
  63. package/dist/entitlements/events.d.ts +89 -0
  64. package/dist/entitlements/events.js +29 -0
  65. package/dist/entitlements/events.js.map +1 -0
  66. package/dist/entitlements/index.d.ts +7 -0
  67. package/dist/entitlements/index.js +3 -0
  68. package/dist/entitlements/model.d.ts +4 -0
  69. package/dist/entitlements/model.js +1 -0
  70. package/dist/entitlements/ports.d.ts +34 -0
  71. package/dist/entitlements/ports.js +1 -0
  72. package/dist/entitlements/service.d.ts +27 -0
  73. package/dist/entitlements/service.js +60 -0
  74. package/dist/entitlements/service.js.map +1 -0
  75. package/dist/identity/events.d.ts +63 -0
  76. package/dist/identity/events.js +24 -0
  77. package/dist/identity/events.js.map +1 -0
  78. package/dist/identity/index.d.ts +8 -0
  79. package/dist/identity/index.js +3 -0
  80. package/dist/identity/model.d.ts +3 -0
  81. package/dist/identity/model.js +1 -0
  82. package/dist/identity/ports.d.ts +58 -0
  83. package/dist/identity/ports.js +1 -0
  84. package/dist/identity/service.d.ts +38 -0
  85. package/dist/identity/service.js +79 -0
  86. package/dist/identity/service.js.map +1 -0
  87. package/dist/identity/types.d.ts +4 -0
  88. package/dist/identity/types.js +1 -0
  89. package/dist/integrations/events.d.ts +70 -0
  90. package/dist/integrations/events.js +24 -0
  91. package/dist/integrations/events.js.map +1 -0
  92. package/dist/integrations/index.d.ts +9 -0
  93. package/dist/integrations/index.js +6 -0
  94. package/dist/integrations/model.d.ts +28 -0
  95. package/dist/integrations/model.js +45 -0
  96. package/dist/integrations/model.js.map +1 -0
  97. package/dist/integrations/ports.d.ts +62 -0
  98. package/dist/integrations/ports.js +1 -0
  99. package/dist/integrations/registry.d.ts +7 -0
  100. package/dist/integrations/registry.js +16 -0
  101. package/dist/integrations/registry.js.map +1 -0
  102. package/dist/integrations/service.d.ts +47 -0
  103. package/dist/integrations/service.js +184 -0
  104. package/dist/integrations/service.js.map +1 -0
  105. package/dist/integrations/zod.d.ts +29 -0
  106. package/dist/integrations/zod.js +40 -0
  107. package/dist/integrations/zod.js.map +1 -0
  108. package/dist/organizations/events.d.ts +197 -0
  109. package/dist/organizations/events.js +59 -0
  110. package/dist/organizations/events.js.map +1 -0
  111. package/dist/organizations/index.d.ts +11 -0
  112. package/dist/organizations/index.js +5 -0
  113. package/dist/organizations/members.d.ts +32 -0
  114. package/dist/organizations/members.js +12 -0
  115. package/dist/organizations/members.js.map +1 -0
  116. package/dist/organizations/model.d.ts +3 -0
  117. package/dist/organizations/model.js +1 -0
  118. package/dist/organizations/ports.d.ts +127 -0
  119. package/dist/organizations/ports.js +1 -0
  120. package/dist/organizations/roles.d.ts +23 -0
  121. package/dist/organizations/roles.js +70 -0
  122. package/dist/organizations/roles.js.map +1 -0
  123. package/dist/organizations/service.d.ts +58 -0
  124. package/dist/organizations/service.js +359 -0
  125. package/dist/organizations/service.js.map +1 -0
  126. package/dist/organizations/types.d.ts +39 -0
  127. package/dist/organizations/types.js +1 -0
  128. package/dist/progress/events.d.ts +55 -0
  129. package/dist/progress/events.js +19 -0
  130. package/dist/progress/events.js.map +1 -0
  131. package/dist/progress/index.d.ts +8 -0
  132. package/dist/progress/index.js +3 -0
  133. package/dist/progress/model.d.ts +3 -0
  134. package/dist/progress/model.js +1 -0
  135. package/dist/progress/ports.d.ts +37 -0
  136. package/dist/progress/ports.js +1 -0
  137. package/dist/progress/service.d.ts +33 -0
  138. package/dist/progress/service.js +181 -0
  139. package/dist/progress/service.js.map +1 -0
  140. package/dist/progress/types.d.ts +4 -0
  141. package/dist/progress/types.js +1 -0
  142. package/dist/reporting/dashboard/index.d.ts +4 -0
  143. package/dist/reporting/dashboard/index.js +2 -0
  144. package/dist/reporting/dashboard/model.d.ts +14 -0
  145. package/dist/reporting/dashboard/model.js +1 -0
  146. package/dist/reporting/dashboard/ports.d.ts +13 -0
  147. package/dist/reporting/dashboard/ports.js +1 -0
  148. package/dist/reporting/dashboard/service.d.ts +19 -0
  149. package/dist/reporting/dashboard/service.js +20 -0
  150. package/dist/reporting/dashboard/service.js.map +1 -0
  151. package/dist/reporting/learn/index.d.ts +5 -0
  152. package/dist/reporting/learn/index.js +2 -0
  153. package/dist/reporting/learn/model.d.ts +23 -0
  154. package/dist/reporting/learn/model.js +1 -0
  155. package/dist/reporting/learn/ports.d.ts +42 -0
  156. package/dist/reporting/learn/ports.js +1 -0
  157. package/dist/reporting/learn/service.d.ts +48 -0
  158. package/dist/reporting/learn/service.js +111 -0
  159. package/dist/reporting/learn/service.js.map +1 -0
  160. package/dist/reporting/students/index.d.ts +4 -0
  161. package/dist/reporting/students/index.js +2 -0
  162. package/dist/reporting/students/model.d.ts +31 -0
  163. package/dist/reporting/students/model.js +1 -0
  164. package/dist/reporting/students/ports.d.ts +13 -0
  165. package/dist/reporting/students/ports.js +1 -0
  166. package/dist/reporting/students/service.d.ts +19 -0
  167. package/dist/reporting/students/service.js +20 -0
  168. package/dist/reporting/students/service.js.map +1 -0
  169. package/dist/shared/errors.d.ts +22 -0
  170. package/dist/shared/errors.js +34 -0
  171. package/dist/shared/errors.js.map +1 -0
  172. package/dist/shared/events.d.ts +31 -0
  173. package/dist/shared/events.js +75 -0
  174. package/dist/shared/events.js.map +1 -0
  175. package/dist/shared/id.d.ts +44 -0
  176. package/dist/shared/id.js +53 -0
  177. package/dist/shared/id.js.map +1 -0
  178. package/dist/shared/invite-token.d.ts +9 -0
  179. package/dist/shared/invite-token.js +16 -0
  180. package/dist/shared/invite-token.js.map +1 -0
  181. package/dist/shared/logger.d.ts +19 -0
  182. package/dist/shared/logger.js +44 -0
  183. package/dist/shared/logger.js.map +1 -0
  184. package/dist/shared/mailer.d.ts +23 -0
  185. package/dist/shared/mailer.js +27 -0
  186. package/dist/shared/mailer.js.map +1 -0
  187. package/dist/shared/ports.d.ts +62 -0
  188. package/dist/shared/ports.js +2 -0
  189. package/dist/shared/settings.d.ts +25 -0
  190. package/dist/shared/settings.js +21 -0
  191. package/dist/shared/settings.js.map +1 -0
  192. package/dist/types/assets.d.ts +2 -0
  193. package/dist/types/assets.js +1 -0
  194. package/dist/types/automations.d.ts +23 -0
  195. package/dist/types/automations.js +1 -0
  196. package/dist/types/content.d.ts +2 -0
  197. package/dist/types/content.js +1 -0
  198. package/dist/types/discussion.d.ts +36 -0
  199. package/dist/types/discussion.js +1 -0
  200. package/dist/types/email-templates.d.ts +57 -0
  201. package/dist/types/email-templates.js +1 -0
  202. package/dist/types/entitlements.d.ts +3 -0
  203. package/dist/types/entitlements.js +1 -0
  204. package/dist/types/events.d.ts +25 -0
  205. package/dist/types/events.js +1 -0
  206. package/dist/types/identity.d.ts +37 -0
  207. package/dist/types/identity.js +1 -0
  208. package/dist/types/index.d.ts +23 -0
  209. package/dist/types/index.js +1 -0
  210. package/dist/types/integrations.d.ts +45 -0
  211. package/dist/types/integrations.js +1 -0
  212. package/dist/types/organizations.d.ts +15 -0
  213. package/dist/types/organizations.js +1 -0
  214. package/dist/types/ports.d.ts +57 -0
  215. package/dist/types/ports.js +1 -0
  216. package/dist/types/progress.d.ts +6 -0
  217. package/dist/types/progress.js +1 -0
  218. package/dist/types/schemas/assets.d.ts +109 -0
  219. package/dist/types/schemas/assets.js +49 -0
  220. package/dist/types/schemas/assets.js.map +1 -0
  221. package/dist/types/schemas/automations.d.ts +118 -0
  222. package/dist/types/schemas/automations.js +73 -0
  223. package/dist/types/schemas/automations.js.map +1 -0
  224. package/dist/types/schemas/content.d.ts +216 -0
  225. package/dist/types/schemas/content.js +138 -0
  226. package/dist/types/schemas/content.js.map +1 -0
  227. package/dist/types/schemas/credentials.d.ts +15 -0
  228. package/dist/types/schemas/credentials.js +15 -0
  229. package/dist/types/schemas/credentials.js.map +1 -0
  230. package/dist/types/schemas/discussion.d.ts +192 -0
  231. package/dist/types/schemas/discussion.js +100 -0
  232. package/dist/types/schemas/discussion.js.map +1 -0
  233. package/dist/types/schemas/entitlements.d.ts +50 -0
  234. package/dist/types/schemas/entitlements.js +38 -0
  235. package/dist/types/schemas/entitlements.js.map +1 -0
  236. package/dist/types/schemas/identity.d.ts +37 -0
  237. package/dist/types/schemas/identity.js +34 -0
  238. package/dist/types/schemas/identity.js.map +1 -0
  239. package/dist/types/schemas/index.d.ts +14 -0
  240. package/dist/types/schemas/index.js +14 -0
  241. package/dist/types/schemas/integrations.d.ts +36 -0
  242. package/dist/types/schemas/integrations.js +30 -0
  243. package/dist/types/schemas/integrations.js.map +1 -0
  244. package/dist/types/schemas/organizations.d.ts +135 -0
  245. package/dist/types/schemas/organizations.js +90 -0
  246. package/dist/types/schemas/organizations.js.map +1 -0
  247. package/dist/types/schemas/outbox.d.ts +19 -0
  248. package/dist/types/schemas/outbox.js +18 -0
  249. package/dist/types/schemas/outbox.js.map +1 -0
  250. package/dist/types/schemas/progress.d.ts +53 -0
  251. package/dist/types/schemas/progress.js +37 -0
  252. package/dist/types/schemas/progress.js.map +1 -0
  253. package/dist/types/schemas/settings.d.ts +16 -0
  254. package/dist/types/schemas/settings.js +15 -0
  255. package/dist/types/schemas/settings.js.map +1 -0
  256. package/dist/types/schemas/shared.d.ts +13 -0
  257. package/dist/types/schemas/shared.js +28 -0
  258. package/dist/types/schemas/shared.js.map +1 -0
  259. package/dist/types/shared.d.ts +25 -0
  260. package/dist/types/shared.js +1 -0
  261. 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