@elevasis/core 0.23.0 → 0.24.1

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 (243) hide show
  1. package/dist/index.d.ts +4343 -2690
  2. package/dist/index.js +1101 -156
  3. package/dist/knowledge/index.d.ts +574 -210
  4. package/dist/knowledge/index.js +104 -1
  5. package/dist/organization-model/index.d.ts +4343 -2690
  6. package/dist/organization-model/index.js +1101 -156
  7. package/dist/test-utils/index.d.ts +483 -109
  8. package/dist/test-utils/index.js +904 -144
  9. package/package.json +3 -3
  10. package/src/README.md +14 -14
  11. package/src/__tests__/publish.test.ts +24 -24
  12. package/src/__tests__/template-core-compatibility.test.ts +9 -12
  13. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +2137 -2093
  14. package/src/_gen/__tests__/scaffold-contracts.test.ts +30 -30
  15. package/src/auth/multi-tenancy/credentials/__tests__/encryption.test.ts +217 -217
  16. package/src/auth/multi-tenancy/credentials/server/encryption.ts +69 -69
  17. package/src/auth/multi-tenancy/credentials/server/kek-loader.ts +37 -37
  18. package/src/auth/multi-tenancy/index.ts +26 -26
  19. package/src/auth/multi-tenancy/invitations/api-schemas.ts +104 -104
  20. package/src/auth/multi-tenancy/memberships/api-schemas.ts +143 -143
  21. package/src/auth/multi-tenancy/memberships/index.ts +26 -26
  22. package/src/auth/multi-tenancy/memberships/membership.ts +130 -130
  23. package/src/auth/multi-tenancy/organizations/__tests__/api-schemas.test.ts +194 -194
  24. package/src/auth/multi-tenancy/organizations/api-schemas.ts +136 -136
  25. package/src/auth/multi-tenancy/permissions.test.ts +42 -42
  26. package/src/auth/multi-tenancy/permissions.ts +123 -123
  27. package/src/auth/multi-tenancy/role-management/api-schemas.ts +78 -78
  28. package/src/auth/multi-tenancy/role-management/index.ts +16 -16
  29. package/src/auth/multi-tenancy/theme-presets.ts +45 -45
  30. package/src/auth/multi-tenancy/types.ts +57 -57
  31. package/src/auth/multi-tenancy/users/api-schemas.ts +165 -165
  32. package/src/business/README.md +2 -2
  33. package/src/business/acquisition/activity-events.test.ts +250 -250
  34. package/src/business/acquisition/activity-events.ts +93 -93
  35. package/src/business/acquisition/api-schemas.test.ts +1883 -1843
  36. package/src/business/acquisition/api-schemas.ts +1492 -1497
  37. package/src/business/acquisition/build-templates.test.ts +240 -240
  38. package/src/business/acquisition/build-templates.ts +98 -98
  39. package/src/business/acquisition/crm-next-action.test.ts +262 -262
  40. package/src/business/acquisition/crm-next-action.ts +220 -220
  41. package/src/business/acquisition/crm-priority.test.ts +216 -216
  42. package/src/business/acquisition/crm-priority.ts +349 -349
  43. package/src/business/acquisition/crm-state-actions.test.ts +153 -153
  44. package/src/business/acquisition/deal-ownership.test.ts +351 -351
  45. package/src/business/acquisition/deal-ownership.ts +120 -120
  46. package/src/business/acquisition/derive-actions.test.ts +129 -104
  47. package/src/business/acquisition/derive-actions.ts +74 -84
  48. package/src/business/acquisition/index.ts +171 -170
  49. package/src/business/acquisition/ontology-validation.ts +309 -0
  50. package/src/business/acquisition/stateful.ts +30 -30
  51. package/src/business/acquisition/types.ts +396 -396
  52. package/src/business/clients/api-schemas.test.ts +115 -115
  53. package/src/business/clients/api-schemas.ts +158 -158
  54. package/src/business/clients/index.ts +1 -1
  55. package/src/business/crm/api-schemas.ts +40 -40
  56. package/src/business/crm/index.ts +1 -1
  57. package/src/business/deals/api-schemas.ts +87 -87
  58. package/src/business/deals/index.ts +1 -1
  59. package/src/business/index.ts +5 -5
  60. package/src/business/projects/types.ts +144 -144
  61. package/src/commands/queue/types/task.ts +15 -15
  62. package/src/execution/core/runner-types.ts +61 -61
  63. package/src/execution/core/sse-executions.ts +7 -7
  64. package/src/execution/engine/__tests__/fixtures/test-agents.ts +10 -10
  65. package/src/execution/engine/agent/core/__tests__/agent.test.ts +16 -16
  66. package/src/execution/engine/agent/core/__tests__/error-passthrough.test.ts +4 -4
  67. package/src/execution/engine/agent/core/types.ts +25 -25
  68. package/src/execution/engine/agent/index.ts +6 -6
  69. package/src/execution/engine/agent/reasoning/__tests__/request-builder.test.ts +24 -24
  70. package/src/execution/engine/index.ts +443 -443
  71. package/src/execution/engine/tools/integration/server/adapters/apify/__tests__/apify-run-actor.integration.test.ts +298 -298
  72. package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.test.ts +55 -55
  73. package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.ts +107 -107
  74. package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.test.ts +48 -48
  75. package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.ts +99 -99
  76. package/src/execution/engine/tools/integration/server/adapters/apollo/index.ts +1 -1
  77. package/src/execution/engine/tools/integration/server/adapters/attio/__tests__/attio-crud.integration.test.ts +363 -363
  78. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/get-record/index.test.ts +162 -162
  79. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-records/index.test.ts +316 -316
  80. package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.test.ts +18 -18
  81. package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.ts +194 -194
  82. package/src/execution/engine/tools/integration/server/adapters/clickup/index.ts +7 -7
  83. package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-adapter.ts +204 -204
  84. package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-tools.ts +105 -105
  85. package/src/execution/engine/tools/integration/server/adapters/google-calendar/google-calendar-adapter.ts +428 -428
  86. package/src/execution/engine/tools/integration/server/adapters/google-calendar/index.ts +2 -2
  87. package/src/execution/engine/tools/integration/server/adapters/google-sheets/__tests__/google-sheets.integration.test.ts +261 -261
  88. package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-tools.ts +1474 -1474
  89. package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-tools.ts +103 -103
  90. package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.test.ts +88 -88
  91. package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.ts +141 -141
  92. package/src/execution/engine/tools/integration/server/adapters/resend/fetch/utils/types.ts +76 -76
  93. package/src/execution/engine/tools/integration/server/adapters/signature-api/signature-api-tools.ts +182 -182
  94. package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-tools.ts +310 -310
  95. package/src/execution/engine/tools/integration/service.test.ts +239 -239
  96. package/src/execution/engine/tools/integration/service.ts +172 -172
  97. package/src/execution/engine/tools/integration/tool.ts +255 -255
  98. package/src/execution/engine/tools/lead-service-types.ts +1005 -1005
  99. package/src/execution/engine/tools/messages.ts +43 -43
  100. package/src/execution/engine/tools/platform/acquisition/company-tools.ts +7 -7
  101. package/src/execution/engine/tools/platform/acquisition/contact-tools.ts +6 -6
  102. package/src/execution/engine/tools/platform/acquisition/list-tools.ts +6 -6
  103. package/src/execution/engine/tools/platform/acquisition/types.ts +280 -280
  104. package/src/execution/engine/tools/platform/email/types.ts +97 -97
  105. package/src/execution/engine/tools/registry.ts +704 -704
  106. package/src/execution/engine/tools/tool-maps.ts +831 -831
  107. package/src/execution/engine/tools/types.ts +234 -234
  108. package/src/execution/engine/workflow/types.ts +195 -197
  109. package/src/execution/external/__tests__/api-schemas.test.ts +127 -127
  110. package/src/execution/external/api-schemas.ts +40 -40
  111. package/src/execution/external/index.ts +1 -1
  112. package/src/index.ts +18 -18
  113. package/src/integrations/credentials/__tests__/api-schemas.test.ts +420 -420
  114. package/src/integrations/credentials/api-schemas.ts +146 -146
  115. package/src/integrations/credentials/schemas.ts +200 -200
  116. package/src/integrations/oauth/__tests__/provider-registry.test.ts +7 -7
  117. package/src/integrations/oauth/provider-registry.ts +74 -74
  118. package/src/integrations/oauth/server/credentials.ts +43 -43
  119. package/src/integrations/webhook-endpoints/__tests__/api-schemas.test.ts +327 -327
  120. package/src/integrations/webhook-endpoints/api-schemas.ts +103 -103
  121. package/src/integrations/webhook-endpoints/types.ts +58 -58
  122. package/src/knowledge/README.md +32 -32
  123. package/src/knowledge/__tests__/queries.test.ts +626 -535
  124. package/src/knowledge/format.ts +99 -99
  125. package/src/knowledge/index.ts +5 -5
  126. package/src/knowledge/published.ts +5 -5
  127. package/src/knowledge/queries.ts +269 -218
  128. package/src/operations/activities/api-schemas.ts +80 -80
  129. package/src/operations/activities/types.ts +64 -64
  130. package/src/organization-model/README.md +149 -149
  131. package/src/organization-model/__tests__/content-kinds-registry.test.ts +210 -210
  132. package/src/organization-model/__tests__/defaults.test.ts +168 -168
  133. package/src/organization-model/__tests__/domains/actions.test.ts +78 -56
  134. package/src/organization-model/__tests__/domains/customers.test.ts +299 -299
  135. package/src/organization-model/__tests__/domains/entities.test.ts +56 -56
  136. package/src/organization-model/__tests__/domains/goals.test.ts +493 -493
  137. package/src/organization-model/__tests__/domains/identity.test.ts +280 -280
  138. package/src/organization-model/__tests__/domains/navigation.test.ts +268 -268
  139. package/src/organization-model/__tests__/domains/offerings.test.ts +414 -414
  140. package/src/organization-model/__tests__/domains/policies.test.ts +323 -323
  141. package/src/organization-model/__tests__/domains/resource-mappings.test.ts +293 -293
  142. package/src/organization-model/__tests__/domains/resources.test.ts +387 -277
  143. package/src/organization-model/__tests__/domains/roles.test.ts +463 -463
  144. package/src/organization-model/__tests__/domains/statuses.test.ts +246 -246
  145. package/src/organization-model/__tests__/domains/systems.test.ts +209 -209
  146. package/src/organization-model/__tests__/domains/topology.test.ts +188 -0
  147. package/src/organization-model/__tests__/flatten-additive-merge.test.ts +362 -361
  148. package/src/organization-model/__tests__/foundation.test.ts +77 -77
  149. package/src/organization-model/__tests__/get-resources-for-system.test.ts +144 -144
  150. package/src/organization-model/__tests__/graph.test.ts +1312 -862
  151. package/src/organization-model/__tests__/icons.test.ts +10 -1
  152. package/src/organization-model/__tests__/knowledge.test.ts +251 -15
  153. package/src/organization-model/__tests__/lookup-helpers.test.ts +438 -438
  154. package/src/organization-model/__tests__/migration-helpers.test.ts +591 -591
  155. package/src/organization-model/__tests__/prospecting-ssot.test.ts +103 -103
  156. package/src/organization-model/__tests__/recursive-system-schema.test.ts +535 -506
  157. package/src/organization-model/__tests__/resolve.test.ts +274 -164
  158. package/src/organization-model/__tests__/schema.test.ts +844 -301
  159. package/src/organization-model/__tests__/surface-projection.test.ts +284 -284
  160. package/src/organization-model/catalogs/lead-gen.ts +144 -144
  161. package/src/organization-model/content-kinds/config.ts +36 -36
  162. package/src/organization-model/content-kinds/index.ts +76 -72
  163. package/src/organization-model/content-kinds/pipeline.ts +68 -68
  164. package/src/organization-model/content-kinds/registry.ts +44 -44
  165. package/src/organization-model/content-kinds/status.ts +71 -71
  166. package/src/organization-model/content-kinds/template.ts +83 -83
  167. package/src/organization-model/content-kinds/types.ts +117 -117
  168. package/src/organization-model/contracts.ts +27 -27
  169. package/src/organization-model/defaults.ts +42 -50
  170. package/src/organization-model/domains/actions.ts +333 -239
  171. package/src/organization-model/domains/customers.ts +78 -78
  172. package/src/organization-model/domains/entities.ts +144 -144
  173. package/src/organization-model/domains/goals.ts +83 -83
  174. package/src/organization-model/domains/knowledge.ts +117 -101
  175. package/src/organization-model/domains/navigation.ts +139 -139
  176. package/src/organization-model/domains/offerings.ts +71 -71
  177. package/src/organization-model/domains/policies.ts +102 -102
  178. package/src/organization-model/domains/projects.ts +14 -14
  179. package/src/organization-model/domains/prospecting.ts +395 -395
  180. package/src/organization-model/domains/resources.ts +202 -124
  181. package/src/organization-model/domains/roles.ts +96 -96
  182. package/src/organization-model/domains/sales.test.ts +218 -218
  183. package/src/organization-model/domains/sales.ts +380 -380
  184. package/src/organization-model/domains/shared.ts +63 -63
  185. package/src/organization-model/domains/statuses.ts +339 -339
  186. package/src/organization-model/domains/systems.ts +217 -172
  187. package/src/organization-model/domains/topology.ts +261 -0
  188. package/src/organization-model/foundation.ts +75 -75
  189. package/src/organization-model/graph/build.ts +1043 -867
  190. package/src/organization-model/graph/index.ts +4 -4
  191. package/src/organization-model/graph/link.ts +10 -10
  192. package/src/organization-model/graph/schema.ts +75 -68
  193. package/src/organization-model/graph/types.ts +71 -64
  194. package/src/organization-model/helpers.ts +289 -241
  195. package/src/organization-model/icons.ts +78 -66
  196. package/src/organization-model/index.ts +128 -125
  197. package/src/organization-model/migration-helpers.ts +247 -244
  198. package/src/organization-model/ontology.ts +658 -0
  199. package/src/organization-model/organization-graph.mdx +110 -90
  200. package/src/organization-model/organization-model.mdx +225 -213
  201. package/src/organization-model/published.ts +299 -222
  202. package/src/organization-model/resolve.ts +146 -91
  203. package/src/organization-model/schema.ts +818 -659
  204. package/src/organization-model/surface-projection.ts +212 -212
  205. package/src/organization-model/types.ts +179 -155
  206. package/src/platform/api/types.ts +38 -38
  207. package/src/platform/constants/versions.ts +3 -3
  208. package/src/platform/index.ts +23 -23
  209. package/src/platform/registry/__tests__/command-view.test.ts +10 -10
  210. package/src/platform/registry/__tests__/resource-link.test.ts +35 -35
  211. package/src/platform/registry/__tests__/resource-registry.integration.test.ts +20 -20
  212. package/src/platform/registry/__tests__/resource-registry.nested-systems.test.ts +245 -245
  213. package/src/platform/registry/__tests__/resource-registry.test.ts +2053 -2053
  214. package/src/platform/registry/__tests__/validation.test.ts +1444 -1259
  215. package/src/platform/registry/command-view.ts +10 -10
  216. package/src/platform/registry/index.ts +103 -103
  217. package/src/platform/registry/resource-link.ts +32 -32
  218. package/src/platform/registry/resource-registry.ts +886 -886
  219. package/src/platform/registry/serialization.ts +295 -295
  220. package/src/platform/registry/serialized-types.ts +166 -166
  221. package/src/platform/registry/stats-types.ts +68 -68
  222. package/src/platform/registry/types.ts +425 -425
  223. package/src/platform/registry/validation.ts +876 -684
  224. package/src/platform/utils/__tests__/validation.test.ts +1084 -1084
  225. package/src/platform/utils/validation.ts +425 -425
  226. package/src/projects/api-schemas.test.ts +39 -39
  227. package/src/projects/api-schemas.ts +291 -291
  228. package/src/reference/_generated/contracts.md +2136 -2093
  229. package/src/reference/glossary.md +76 -76
  230. package/src/scaffold-registry/__tests__/index.test.ts +206 -206
  231. package/src/scaffold-registry/__tests__/schema.test.ts +166 -166
  232. package/src/scaffold-registry/index.ts +392 -392
  233. package/src/scaffold-registry/schema.ts +243 -243
  234. package/src/server.ts +289 -289
  235. package/src/supabase/database.types.ts +3 -0
  236. package/src/test-utils/README.md +37 -37
  237. package/src/test-utils/entities.ts +108 -108
  238. package/src/test-utils/fixtures/memberships.ts +82 -82
  239. package/src/test-utils/index.ts +12 -12
  240. package/src/test-utils/organization-model.ts +65 -65
  241. package/src/test-utils/published.ts +6 -6
  242. package/src/test-utils/rls/RLSTestContext.ts +588 -588
  243. package/src/test-utils/test-utils.test.ts +44 -44
@@ -1,428 +1,428 @@
1
- import type { calendar_v3 } from '@googleapis/calendar'
2
- import type { BaseIntegrationAdapter } from '../../../base-integration-adapter'
3
- import { ToolingError } from '../../../../types'
4
- import type { ExecutionContext } from '../../../../../base/types'
5
- import { getOAuthCredentials } from '../../../../../../../integrations/oauth/server/credentials'
6
-
7
- /**
8
- * Google Calendar credentials format
9
- * Stored in credentials table, encrypted
10
- */
11
- interface GoogleCalendarCredentials {
12
- accessToken: string
13
- refreshToken?: string
14
- expiresAt?: string
15
- }
16
-
17
- /**
18
- * Date/time value from Google Calendar API
19
- */
20
- export interface CalendarDateTime {
21
- dateTime?: string // ISO 8601 datetime (when time is specified)
22
- date?: string // YYYY-MM-DD (for all-day events)
23
- timeZone?: string
24
- }
25
-
26
- /**
27
- * A Google Calendar event (plausible-use fields only)
28
- */
29
- export interface CalendarEvent {
30
- id: string
31
- summary?: string
32
- description?: string
33
- location?: string
34
- start: CalendarDateTime
35
- end: CalendarDateTime
36
- status?: 'confirmed' | 'tentative' | 'cancelled'
37
- htmlLink?: string
38
- created?: string
39
- updated?: string
40
- organizer?: { email?: string; displayName?: string }
41
- attendees?: Array<{ email?: string; displayName?: string; responseStatus?: string }>
42
- recurringEventId?: string
43
- }
44
-
45
- /**
46
- * listEvents parameters.
47
- *
48
- * `credentialName` is required when calling the adapter directly (HTTP-handler
49
- * path). It is ignored on the dispatch path, where credentials are injected
50
- * by the execution-engine pipeline.
51
- */
52
- interface ListEventsParams {
53
- organizationId: string
54
- credentialName?: string
55
- calendarId?: string
56
- timeMin?: string
57
- timeMax?: string
58
- maxResults?: number
59
- singleEvents?: boolean
60
- orderBy?: 'startTime' | 'updated'
61
- }
62
-
63
- /**
64
- * listEvents result
65
- */
66
- export interface ListEventsResult {
67
- items: CalendarEvent[]
68
- nextPageToken?: string
69
- summary?: string
70
- timeZone?: string
71
- }
72
-
73
- /**
74
- * getEvent parameters
75
- */
76
- interface GetEventParams {
77
- organizationId: string
78
- credentialName?: string
79
- calendarId?: string
80
- eventId: string
81
- }
82
-
83
- /**
84
- * Google Calendar adapter for calendar and event operations via Google Calendar API v3
85
- *
86
- * Read-only methods:
87
- * - listEvents: List events from a calendar
88
- * - getEvent: Get a single event by ID
89
- *
90
- * @example
91
- * const adapter = new GoogleCalendarAdapter()
92
- * const result = await adapter.listEvents({
93
- * organizationId: 'org-uuid',
94
- * calendarId: 'primary',
95
- * maxResults: 10
96
- * })
97
- */
98
- export class GoogleCalendarAdapter implements BaseIntegrationAdapter {
99
- readonly name = 'google-calendar'
100
-
101
- /**
102
- * Call Google Calendar API method
103
- * @param method - Method name ('listEvents' | 'getEvent')
104
- * @param params - Method parameters (must include organizationId)
105
- * @param credentials - OAuth2 access token
106
- * @param context - Execution context for logging and tracing
107
- */
108
- async call(
109
- method: string,
110
- params: unknown,
111
- credentials: Record<string, unknown>,
112
- context?: ExecutionContext
113
- ): Promise<unknown> {
114
- if (!this.validateCredentials(credentials)) {
115
- throw new ToolingError('credentials_invalid', 'Invalid Google Calendar credentials', {
116
- integration: 'google-calendar',
117
- method
118
- })
119
- }
120
- const calCreds = credentials as unknown as GoogleCalendarCredentials
121
-
122
- const client = await this.createClient(calCreds)
123
-
124
- switch (method) {
125
- case 'listEvents':
126
- return this.listEvents(client, params as ListEventsParams, context)
127
- case 'getEvent':
128
- return this.getEvent(client, params as GetEventParams, context)
129
- default:
130
- throw new ToolingError('method_not_found', `Unknown method: ${method}`, {
131
- integration: 'google-calendar',
132
- method
133
- })
134
- }
135
- }
136
-
137
- /**
138
- * List events from a calendar — callable directly (no method dispatch overhead)
139
- */
140
- async listEvents(
141
- clientOrParams: calendar_v3.Calendar | ListEventsParams,
142
- paramsOrContext?: ListEventsParams | ExecutionContext,
143
- maybeContext?: ExecutionContext
144
- ): Promise<ListEventsResult> {
145
- // Support both direct call (listEvents(params, context)) and
146
- // dispatch call (listEvents(client, params, context))
147
- let client: calendar_v3.Calendar
148
- let params: ListEventsParams
149
- let context: ExecutionContext | undefined
150
-
151
- if ('calendarId' in (clientOrParams as object) || 'organizationId' in (clientOrParams as object)) {
152
- // Called directly: listEvents(params, context?)
153
- params = clientOrParams as ListEventsParams
154
- context = paramsOrContext as ExecutionContext | undefined
155
- if (!params.credentialName) {
156
- throw new ToolingError('validation_error', 'Missing required field: credentialName', { params })
157
- }
158
- const creds = await this.getCredentialsForOrg(params.organizationId, params.credentialName)
159
- client = await this.createClient(creds)
160
- } else {
161
- // Called via dispatch: listEvents(client, params, context?)
162
- client = clientOrParams as calendar_v3.Calendar
163
- params = paramsOrContext as ListEventsParams
164
- context = maybeContext
165
- }
166
-
167
- return this._listEvents(client, params, context)
168
- }
169
-
170
- /**
171
- * Get a single event by ID — callable directly
172
- */
173
- async getEvent(
174
- clientOrParams: calendar_v3.Calendar | GetEventParams,
175
- paramsOrContext?: GetEventParams | ExecutionContext,
176
- maybeContext?: ExecutionContext
177
- ): Promise<CalendarEvent> {
178
- let client: calendar_v3.Calendar
179
- let params: GetEventParams
180
- let context: ExecutionContext | undefined
181
-
182
- if ('eventId' in (clientOrParams as object) || 'organizationId' in (clientOrParams as object)) {
183
- // Called directly: getEvent(params, context?)
184
- params = clientOrParams as GetEventParams
185
- context = paramsOrContext as ExecutionContext | undefined
186
- if (!params.credentialName) {
187
- throw new ToolingError('validation_error', 'Missing required field: credentialName', { params })
188
- }
189
- const creds = await this.getCredentialsForOrg(params.organizationId, params.credentialName)
190
- client = await this.createClient(creds)
191
- } else {
192
- // Called via dispatch: getEvent(client, params, context?)
193
- client = clientOrParams as calendar_v3.Calendar
194
- params = paramsOrContext as GetEventParams
195
- context = maybeContext
196
- }
197
-
198
- return this._getEvent(client, params, context)
199
- }
200
-
201
- /**
202
- * Create authenticated Google Calendar client (lazy loads @googleapis/calendar SDK)
203
- * CRITICAL: Use dynamic import to prevent OOM and module-level crashes
204
- */
205
- private async createClient(creds: GoogleCalendarCredentials): Promise<calendar_v3.Calendar> {
206
- const { calendar } = await import('@googleapis/calendar')
207
- const { OAuth2Client } = await import('google-auth-library')
208
-
209
- // Get client credentials from environment for token refresh
210
- const { clientId, clientSecret } = getOAuthCredentials('google-calendar')
211
-
212
- const oauth2Client = new OAuth2Client(clientId, clientSecret)
213
- oauth2Client.setCredentials({
214
- access_token: creds.accessToken,
215
- refresh_token: creds.refreshToken
216
- })
217
-
218
- return calendar({ version: 'v3', auth: oauth2Client })
219
- }
220
-
221
- /**
222
- * Retrieve credentials for an organization from the credentials table.
223
- * Used when the adapter is called directly (not via the dispatch pipeline).
224
- * The caller passes the credential name explicitly -- names are user-chosen
225
- * at OAuth-modal time, so they cannot be hardcoded.
226
- */
227
- private async getCredentialsForOrg(
228
- organizationId: string,
229
- credentialName: string
230
- ): Promise<GoogleCalendarCredentials> {
231
- // Dynamic import avoids pulling Supabase into browser bundles
232
- const { createClient } = await import('@supabase/supabase-js')
233
- const { decryptCredentialValue } = await import(
234
- '../../../../../../../auth/multi-tenancy/credentials/server/service'
235
- )
236
-
237
- const supabaseUrl = process.env.SUPABASE_URL
238
- const supabaseKey = process.env.SUPABASE_SERVICE_KEY
239
- if (!supabaseUrl || !supabaseKey) {
240
- throw new ToolingError('credentials_invalid', 'Missing Supabase environment variables', { organizationId })
241
- }
242
-
243
- const supabase = createClient(supabaseUrl, supabaseKey)
244
- const { data, error } = await supabase
245
- .from('credentials')
246
- .select('encrypted_value')
247
- .eq('organization_id', organizationId)
248
- .eq('name', credentialName)
249
- .single()
250
-
251
- if (error || !data) {
252
- throw new ToolingError('credentials_invalid', 'Google Calendar credential not found', {
253
- organizationId,
254
- credentialName
255
- })
256
- }
257
-
258
- return decryptCredentialValue(data.encrypted_value) as unknown as GoogleCalendarCredentials
259
- }
260
-
261
- /**
262
- * Internal list events implementation
263
- */
264
- private async _listEvents(
265
- client: calendar_v3.Calendar,
266
- params: ListEventsParams,
267
- context?: ExecutionContext
268
- ): Promise<ListEventsResult> {
269
- if (!params.organizationId) {
270
- throw new ToolingError('validation_error', 'Missing required field: organizationId', { params })
271
- }
272
-
273
- const calendarId = params.calendarId ?? 'primary'
274
- const maxResults = Math.min(params.maxResults ?? 10, 250)
275
- const singleEvents = params.singleEvents ?? true
276
- const orderBy = params.orderBy ?? 'startTime'
277
- const timeMin = params.timeMin ?? new Date().toISOString()
278
-
279
- try {
280
- const response = await client.events.list({
281
- calendarId,
282
- timeMin,
283
- timeMax: params.timeMax,
284
- maxResults,
285
- singleEvents,
286
- orderBy
287
- })
288
-
289
- const items = (response.data.items ?? []).map(this.transformEvent)
290
-
291
- if (context?.logger) {
292
- context.logger.info(
293
- `[GoogleCalendarAdapter] Events listed: organizationId=${context.organizationId} executionId=${context.executionId} calendarId=${calendarId} count=${items.length}`
294
- )
295
- }
296
-
297
- return {
298
- items,
299
- nextPageToken: response.data.nextPageToken ?? undefined,
300
- summary: response.data.summary ?? undefined,
301
- timeZone: response.data.timeZone ?? undefined
302
- }
303
- } catch (error: any) {
304
- throw this.handleApiError(error, calendarId)
305
- }
306
- }
307
-
308
- /**
309
- * Internal get event implementation
310
- */
311
- private async _getEvent(
312
- client: calendar_v3.Calendar,
313
- params: GetEventParams,
314
- context?: ExecutionContext
315
- ): Promise<CalendarEvent> {
316
- if (!params.organizationId) {
317
- throw new ToolingError('validation_error', 'Missing required field: organizationId', { params })
318
- }
319
- if (!params.eventId) {
320
- throw new ToolingError('validation_error', 'Missing required field: eventId', { params })
321
- }
322
-
323
- const calendarId = params.calendarId ?? 'primary'
324
-
325
- try {
326
- const response = await client.events.get({
327
- calendarId,
328
- eventId: params.eventId
329
- })
330
-
331
- const event = this.transformEvent(response.data)
332
-
333
- if (context?.logger) {
334
- context.logger.info(
335
- `[GoogleCalendarAdapter] Event retrieved: organizationId=${context.organizationId} executionId=${context.executionId} calendarId=${calendarId} eventId=${params.eventId}`
336
- )
337
- }
338
-
339
- return event
340
- } catch (error: any) {
341
- throw this.handleApiError(error, calendarId)
342
- }
343
- }
344
-
345
- /**
346
- * Transform a raw Google Calendar API event into the typed CalendarEvent shape
347
- */
348
- private transformEvent(raw: calendar_v3.Schema$Event): CalendarEvent {
349
- return {
350
- id: raw.id ?? '',
351
- summary: raw.summary ?? undefined,
352
- description: raw.description ?? undefined,
353
- location: raw.location ?? undefined,
354
- start: {
355
- dateTime: raw.start?.dateTime ?? undefined,
356
- date: raw.start?.date ?? undefined,
357
- timeZone: raw.start?.timeZone ?? undefined
358
- },
359
- end: {
360
- dateTime: raw.end?.dateTime ?? undefined,
361
- date: raw.end?.date ?? undefined,
362
- timeZone: raw.end?.timeZone ?? undefined
363
- },
364
- status: (raw.status as CalendarEvent['status']) ?? undefined,
365
- htmlLink: raw.htmlLink ?? undefined,
366
- created: raw.created ?? undefined,
367
- updated: raw.updated ?? undefined,
368
- organizer: raw.organizer
369
- ? {
370
- email: raw.organizer.email ?? undefined,
371
- displayName: raw.organizer.displayName ?? undefined
372
- }
373
- : undefined,
374
- attendees: raw.attendees?.map((a) => ({
375
- email: a.email ?? undefined,
376
- displayName: a.displayName ?? undefined,
377
- responseStatus: a.responseStatus ?? undefined
378
- })),
379
- recurringEventId: raw.recurringEventId ?? undefined
380
- }
381
- }
382
-
383
- /**
384
- * Handle Google Calendar API errors with meaningful messages
385
- */
386
- private handleApiError(error: any, calendarId: string): ToolingError {
387
- const code = error.code || error.status || error.response?.status
388
-
389
- if (code === 404) {
390
- return new ToolingError('api_error', `Calendar or event not found: ${calendarId}`, {
391
- calendarId,
392
- statusCode: 404
393
- })
394
- }
395
- if (code === 403) {
396
- return new ToolingError('permission_denied', 'Access denied. Check calendar sharing permissions.', {
397
- calendarId,
398
- statusCode: 403
399
- })
400
- }
401
- if (code === 401) {
402
- return new ToolingError('credentials_invalid', 'Authentication expired. Token refresh required.', {
403
- calendarId,
404
- statusCode: 401
405
- })
406
- }
407
- if (code === 429) {
408
- return new ToolingError('rate_limit_exceeded', 'Rate limited. Retry after delay.', {
409
- calendarId,
410
- statusCode: 429
411
- })
412
- }
413
-
414
- return new ToolingError('api_error', `Google Calendar API error: ${error.message}`, {
415
- calendarId,
416
- statusCode: code,
417
- details: error
418
- })
419
- }
420
-
421
- /**
422
- * Validate credentials structure
423
- * Required by BaseIntegrationAdapter interface
424
- */
425
- validateCredentials(creds: Record<string, unknown>): boolean {
426
- return typeof creds.accessToken === 'string' && creds.accessToken.length > 0
427
- }
428
- }
1
+ import type { calendar_v3 } from '@googleapis/calendar'
2
+ import type { BaseIntegrationAdapter } from '../../../base-integration-adapter'
3
+ import { ToolingError } from '../../../../types'
4
+ import type { ExecutionContext } from '../../../../../base/types'
5
+ import { getOAuthCredentials } from '../../../../../../../integrations/oauth/server/credentials'
6
+
7
+ /**
8
+ * Google Calendar credentials format
9
+ * Stored in credentials table, encrypted
10
+ */
11
+ interface GoogleCalendarCredentials {
12
+ accessToken: string
13
+ refreshToken?: string
14
+ expiresAt?: string
15
+ }
16
+
17
+ /**
18
+ * Date/time value from Google Calendar API
19
+ */
20
+ export interface CalendarDateTime {
21
+ dateTime?: string // ISO 8601 datetime (when time is specified)
22
+ date?: string // YYYY-MM-DD (for all-day events)
23
+ timeZone?: string
24
+ }
25
+
26
+ /**
27
+ * A Google Calendar event (plausible-use fields only)
28
+ */
29
+ export interface CalendarEvent {
30
+ id: string
31
+ summary?: string
32
+ description?: string
33
+ location?: string
34
+ start: CalendarDateTime
35
+ end: CalendarDateTime
36
+ status?: 'confirmed' | 'tentative' | 'cancelled'
37
+ htmlLink?: string
38
+ created?: string
39
+ updated?: string
40
+ organizer?: { email?: string; displayName?: string }
41
+ attendees?: Array<{ email?: string; displayName?: string; responseStatus?: string }>
42
+ recurringEventId?: string
43
+ }
44
+
45
+ /**
46
+ * listEvents parameters.
47
+ *
48
+ * `credentialName` is required when calling the adapter directly (HTTP-handler
49
+ * path). It is ignored on the dispatch path, where credentials are injected
50
+ * by the execution-engine pipeline.
51
+ */
52
+ interface ListEventsParams {
53
+ organizationId: string
54
+ credentialName?: string
55
+ calendarId?: string
56
+ timeMin?: string
57
+ timeMax?: string
58
+ maxResults?: number
59
+ singleEvents?: boolean
60
+ orderBy?: 'startTime' | 'updated'
61
+ }
62
+
63
+ /**
64
+ * listEvents result
65
+ */
66
+ export interface ListEventsResult {
67
+ items: CalendarEvent[]
68
+ nextPageToken?: string
69
+ summary?: string
70
+ timeZone?: string
71
+ }
72
+
73
+ /**
74
+ * getEvent parameters
75
+ */
76
+ interface GetEventParams {
77
+ organizationId: string
78
+ credentialName?: string
79
+ calendarId?: string
80
+ eventId: string
81
+ }
82
+
83
+ /**
84
+ * Google Calendar adapter for calendar and event operations via Google Calendar API v3
85
+ *
86
+ * Read-only methods:
87
+ * - listEvents: List events from a calendar
88
+ * - getEvent: Get a single event by ID
89
+ *
90
+ * @example
91
+ * const adapter = new GoogleCalendarAdapter()
92
+ * const result = await adapter.listEvents({
93
+ * organizationId: 'org-uuid',
94
+ * calendarId: 'primary',
95
+ * maxResults: 10
96
+ * })
97
+ */
98
+ export class GoogleCalendarAdapter implements BaseIntegrationAdapter {
99
+ readonly name = 'google-calendar'
100
+
101
+ /**
102
+ * Call Google Calendar API method
103
+ * @param method - Method name ('listEvents' | 'getEvent')
104
+ * @param params - Method parameters (must include organizationId)
105
+ * @param credentials - OAuth2 access token
106
+ * @param context - Execution context for logging and tracing
107
+ */
108
+ async call(
109
+ method: string,
110
+ params: unknown,
111
+ credentials: Record<string, unknown>,
112
+ context?: ExecutionContext
113
+ ): Promise<unknown> {
114
+ if (!this.validateCredentials(credentials)) {
115
+ throw new ToolingError('credentials_invalid', 'Invalid Google Calendar credentials', {
116
+ integration: 'google-calendar',
117
+ method
118
+ })
119
+ }
120
+ const calCreds = credentials as unknown as GoogleCalendarCredentials
121
+
122
+ const client = await this.createClient(calCreds)
123
+
124
+ switch (method) {
125
+ case 'listEvents':
126
+ return this.listEvents(client, params as ListEventsParams, context)
127
+ case 'getEvent':
128
+ return this.getEvent(client, params as GetEventParams, context)
129
+ default:
130
+ throw new ToolingError('method_not_found', `Unknown method: ${method}`, {
131
+ integration: 'google-calendar',
132
+ method
133
+ })
134
+ }
135
+ }
136
+
137
+ /**
138
+ * List events from a calendar — callable directly (no method dispatch overhead)
139
+ */
140
+ async listEvents(
141
+ clientOrParams: calendar_v3.Calendar | ListEventsParams,
142
+ paramsOrContext?: ListEventsParams | ExecutionContext,
143
+ maybeContext?: ExecutionContext
144
+ ): Promise<ListEventsResult> {
145
+ // Support both direct call (listEvents(params, context)) and
146
+ // dispatch call (listEvents(client, params, context))
147
+ let client: calendar_v3.Calendar
148
+ let params: ListEventsParams
149
+ let context: ExecutionContext | undefined
150
+
151
+ if ('calendarId' in (clientOrParams as object) || 'organizationId' in (clientOrParams as object)) {
152
+ // Called directly: listEvents(params, context?)
153
+ params = clientOrParams as ListEventsParams
154
+ context = paramsOrContext as ExecutionContext | undefined
155
+ if (!params.credentialName) {
156
+ throw new ToolingError('validation_error', 'Missing required field: credentialName', { params })
157
+ }
158
+ const creds = await this.getCredentialsForOrg(params.organizationId, params.credentialName)
159
+ client = await this.createClient(creds)
160
+ } else {
161
+ // Called via dispatch: listEvents(client, params, context?)
162
+ client = clientOrParams as calendar_v3.Calendar
163
+ params = paramsOrContext as ListEventsParams
164
+ context = maybeContext
165
+ }
166
+
167
+ return this._listEvents(client, params, context)
168
+ }
169
+
170
+ /**
171
+ * Get a single event by ID — callable directly
172
+ */
173
+ async getEvent(
174
+ clientOrParams: calendar_v3.Calendar | GetEventParams,
175
+ paramsOrContext?: GetEventParams | ExecutionContext,
176
+ maybeContext?: ExecutionContext
177
+ ): Promise<CalendarEvent> {
178
+ let client: calendar_v3.Calendar
179
+ let params: GetEventParams
180
+ let context: ExecutionContext | undefined
181
+
182
+ if ('eventId' in (clientOrParams as object) || 'organizationId' in (clientOrParams as object)) {
183
+ // Called directly: getEvent(params, context?)
184
+ params = clientOrParams as GetEventParams
185
+ context = paramsOrContext as ExecutionContext | undefined
186
+ if (!params.credentialName) {
187
+ throw new ToolingError('validation_error', 'Missing required field: credentialName', { params })
188
+ }
189
+ const creds = await this.getCredentialsForOrg(params.organizationId, params.credentialName)
190
+ client = await this.createClient(creds)
191
+ } else {
192
+ // Called via dispatch: getEvent(client, params, context?)
193
+ client = clientOrParams as calendar_v3.Calendar
194
+ params = paramsOrContext as GetEventParams
195
+ context = maybeContext
196
+ }
197
+
198
+ return this._getEvent(client, params, context)
199
+ }
200
+
201
+ /**
202
+ * Create authenticated Google Calendar client (lazy loads @googleapis/calendar SDK)
203
+ * CRITICAL: Use dynamic import to prevent OOM and module-level crashes
204
+ */
205
+ private async createClient(creds: GoogleCalendarCredentials): Promise<calendar_v3.Calendar> {
206
+ const { calendar } = await import('@googleapis/calendar')
207
+ const { OAuth2Client } = await import('google-auth-library')
208
+
209
+ // Get client credentials from environment for token refresh
210
+ const { clientId, clientSecret } = getOAuthCredentials('google-calendar')
211
+
212
+ const oauth2Client = new OAuth2Client(clientId, clientSecret)
213
+ oauth2Client.setCredentials({
214
+ access_token: creds.accessToken,
215
+ refresh_token: creds.refreshToken
216
+ })
217
+
218
+ return calendar({ version: 'v3', auth: oauth2Client })
219
+ }
220
+
221
+ /**
222
+ * Retrieve credentials for an organization from the credentials table.
223
+ * Used when the adapter is called directly (not via the dispatch pipeline).
224
+ * The caller passes the credential name explicitly -- names are user-chosen
225
+ * at OAuth-modal time, so they cannot be hardcoded.
226
+ */
227
+ private async getCredentialsForOrg(
228
+ organizationId: string,
229
+ credentialName: string
230
+ ): Promise<GoogleCalendarCredentials> {
231
+ // Dynamic import avoids pulling Supabase into browser bundles
232
+ const { createClient } = await import('@supabase/supabase-js')
233
+ const { decryptCredentialValue } = await import(
234
+ '../../../../../../../auth/multi-tenancy/credentials/server/service'
235
+ )
236
+
237
+ const supabaseUrl = process.env.SUPABASE_URL
238
+ const supabaseKey = process.env.SUPABASE_SERVICE_KEY
239
+ if (!supabaseUrl || !supabaseKey) {
240
+ throw new ToolingError('credentials_invalid', 'Missing Supabase environment variables', { organizationId })
241
+ }
242
+
243
+ const supabase = createClient(supabaseUrl, supabaseKey)
244
+ const { data, error } = await supabase
245
+ .from('credentials')
246
+ .select('encrypted_value')
247
+ .eq('organization_id', organizationId)
248
+ .eq('name', credentialName)
249
+ .single()
250
+
251
+ if (error || !data) {
252
+ throw new ToolingError('credentials_invalid', 'Google Calendar credential not found', {
253
+ organizationId,
254
+ credentialName
255
+ })
256
+ }
257
+
258
+ return decryptCredentialValue(data.encrypted_value) as unknown as GoogleCalendarCredentials
259
+ }
260
+
261
+ /**
262
+ * Internal list events implementation
263
+ */
264
+ private async _listEvents(
265
+ client: calendar_v3.Calendar,
266
+ params: ListEventsParams,
267
+ context?: ExecutionContext
268
+ ): Promise<ListEventsResult> {
269
+ if (!params.organizationId) {
270
+ throw new ToolingError('validation_error', 'Missing required field: organizationId', { params })
271
+ }
272
+
273
+ const calendarId = params.calendarId ?? 'primary'
274
+ const maxResults = Math.min(params.maxResults ?? 10, 250)
275
+ const singleEvents = params.singleEvents ?? true
276
+ const orderBy = params.orderBy ?? 'startTime'
277
+ const timeMin = params.timeMin ?? new Date().toISOString()
278
+
279
+ try {
280
+ const response = await client.events.list({
281
+ calendarId,
282
+ timeMin,
283
+ timeMax: params.timeMax,
284
+ maxResults,
285
+ singleEvents,
286
+ orderBy
287
+ })
288
+
289
+ const items = (response.data.items ?? []).map(this.transformEvent)
290
+
291
+ if (context?.logger) {
292
+ context.logger.info(
293
+ `[GoogleCalendarAdapter] Events listed: organizationId=${context.organizationId} executionId=${context.executionId} calendarId=${calendarId} count=${items.length}`
294
+ )
295
+ }
296
+
297
+ return {
298
+ items,
299
+ nextPageToken: response.data.nextPageToken ?? undefined,
300
+ summary: response.data.summary ?? undefined,
301
+ timeZone: response.data.timeZone ?? undefined
302
+ }
303
+ } catch (error: any) {
304
+ throw this.handleApiError(error, calendarId)
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Internal get event implementation
310
+ */
311
+ private async _getEvent(
312
+ client: calendar_v3.Calendar,
313
+ params: GetEventParams,
314
+ context?: ExecutionContext
315
+ ): Promise<CalendarEvent> {
316
+ if (!params.organizationId) {
317
+ throw new ToolingError('validation_error', 'Missing required field: organizationId', { params })
318
+ }
319
+ if (!params.eventId) {
320
+ throw new ToolingError('validation_error', 'Missing required field: eventId', { params })
321
+ }
322
+
323
+ const calendarId = params.calendarId ?? 'primary'
324
+
325
+ try {
326
+ const response = await client.events.get({
327
+ calendarId,
328
+ eventId: params.eventId
329
+ })
330
+
331
+ const event = this.transformEvent(response.data)
332
+
333
+ if (context?.logger) {
334
+ context.logger.info(
335
+ `[GoogleCalendarAdapter] Event retrieved: organizationId=${context.organizationId} executionId=${context.executionId} calendarId=${calendarId} eventId=${params.eventId}`
336
+ )
337
+ }
338
+
339
+ return event
340
+ } catch (error: any) {
341
+ throw this.handleApiError(error, calendarId)
342
+ }
343
+ }
344
+
345
+ /**
346
+ * Transform a raw Google Calendar API event into the typed CalendarEvent shape
347
+ */
348
+ private transformEvent(raw: calendar_v3.Schema$Event): CalendarEvent {
349
+ return {
350
+ id: raw.id ?? '',
351
+ summary: raw.summary ?? undefined,
352
+ description: raw.description ?? undefined,
353
+ location: raw.location ?? undefined,
354
+ start: {
355
+ dateTime: raw.start?.dateTime ?? undefined,
356
+ date: raw.start?.date ?? undefined,
357
+ timeZone: raw.start?.timeZone ?? undefined
358
+ },
359
+ end: {
360
+ dateTime: raw.end?.dateTime ?? undefined,
361
+ date: raw.end?.date ?? undefined,
362
+ timeZone: raw.end?.timeZone ?? undefined
363
+ },
364
+ status: (raw.status as CalendarEvent['status']) ?? undefined,
365
+ htmlLink: raw.htmlLink ?? undefined,
366
+ created: raw.created ?? undefined,
367
+ updated: raw.updated ?? undefined,
368
+ organizer: raw.organizer
369
+ ? {
370
+ email: raw.organizer.email ?? undefined,
371
+ displayName: raw.organizer.displayName ?? undefined
372
+ }
373
+ : undefined,
374
+ attendees: raw.attendees?.map((a) => ({
375
+ email: a.email ?? undefined,
376
+ displayName: a.displayName ?? undefined,
377
+ responseStatus: a.responseStatus ?? undefined
378
+ })),
379
+ recurringEventId: raw.recurringEventId ?? undefined
380
+ }
381
+ }
382
+
383
+ /**
384
+ * Handle Google Calendar API errors with meaningful messages
385
+ */
386
+ private handleApiError(error: any, calendarId: string): ToolingError {
387
+ const code = error.code || error.status || error.response?.status
388
+
389
+ if (code === 404) {
390
+ return new ToolingError('api_error', `Calendar or event not found: ${calendarId}`, {
391
+ calendarId,
392
+ statusCode: 404
393
+ })
394
+ }
395
+ if (code === 403) {
396
+ return new ToolingError('permission_denied', 'Access denied. Check calendar sharing permissions.', {
397
+ calendarId,
398
+ statusCode: 403
399
+ })
400
+ }
401
+ if (code === 401) {
402
+ return new ToolingError('credentials_invalid', 'Authentication expired. Token refresh required.', {
403
+ calendarId,
404
+ statusCode: 401
405
+ })
406
+ }
407
+ if (code === 429) {
408
+ return new ToolingError('rate_limit_exceeded', 'Rate limited. Retry after delay.', {
409
+ calendarId,
410
+ statusCode: 429
411
+ })
412
+ }
413
+
414
+ return new ToolingError('api_error', `Google Calendar API error: ${error.message}`, {
415
+ calendarId,
416
+ statusCode: code,
417
+ details: error
418
+ })
419
+ }
420
+
421
+ /**
422
+ * Validate credentials structure
423
+ * Required by BaseIntegrationAdapter interface
424
+ */
425
+ validateCredentials(creds: Record<string, unknown>): boolean {
426
+ return typeof creds.accessToken === 'string' && creds.accessToken.length > 0
427
+ }
428
+ }