@elevasis/core 0.22.0 → 0.24.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 (244) hide show
  1. package/dist/index.d.ts +3214 -2501
  2. package/dist/index.js +3112 -1222
  3. package/dist/knowledge/index.d.ts +1108 -1264
  4. package/dist/knowledge/index.js +112 -9
  5. package/dist/organization-model/index.d.ts +3214 -2501
  6. package/dist/organization-model/index.js +3112 -1222
  7. package/dist/test-utils/index.d.ts +985 -1103
  8. package/dist/test-utils/index.js +2464 -1165
  9. package/package.json +5 -5
  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 -80
  13. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +2389 -2121
  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 +1493 -1500
  37. package/src/business/acquisition/build-templates.test.ts +240 -240
  38. package/src/business/acquisition/build-templates.ts +83 -41
  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 -151
  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 -392
  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 +202 -202
  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 +33 -32
  123. package/src/knowledge/__tests__/queries.test.ts +633 -541
  124. package/src/knowledge/format.ts +100 -99
  125. package/src/knowledge/index.ts +5 -5
  126. package/src/knowledge/published.ts +5 -5
  127. package/src/knowledge/queries.ts +274 -222
  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 -109
  131. package/src/organization-model/__tests__/content-kinds-registry.test.ts +210 -0
  132. package/src/organization-model/__tests__/defaults.test.ts +168 -194
  133. package/src/organization-model/__tests__/domains/actions.test.ts +78 -0
  134. package/src/organization-model/__tests__/domains/customers.test.ts +48 -44
  135. package/src/organization-model/__tests__/domains/entities.test.ts +56 -0
  136. package/src/organization-model/__tests__/domains/goals.test.ts +110 -96
  137. package/src/organization-model/__tests__/domains/identity.test.ts +4 -3
  138. package/src/organization-model/__tests__/domains/navigation.test.ts +222 -166
  139. package/src/organization-model/__tests__/domains/offerings.test.ts +83 -88
  140. package/src/organization-model/__tests__/domains/policies.test.ts +323 -0
  141. package/src/organization-model/__tests__/domains/resource-mappings.test.ts +30 -30
  142. package/src/organization-model/__tests__/domains/resources.test.ts +396 -175
  143. package/src/organization-model/__tests__/domains/roles.test.ts +463 -402
  144. package/src/organization-model/__tests__/domains/statuses.test.ts +13 -10
  145. package/src/organization-model/__tests__/domains/systems.test.ts +209 -193
  146. package/src/organization-model/__tests__/flatten-additive-merge.test.ts +362 -0
  147. package/src/organization-model/__tests__/foundation.test.ts +47 -75
  148. package/src/organization-model/__tests__/get-resources-for-system.test.ts +144 -0
  149. package/src/organization-model/__tests__/graph.test.ts +1336 -149
  150. package/src/organization-model/__tests__/icons.test.ts +10 -1
  151. package/src/organization-model/__tests__/knowledge.test.ts +418 -61
  152. package/src/organization-model/__tests__/lookup-helpers.test.ts +438 -0
  153. package/src/organization-model/__tests__/migration-helpers.test.ts +591 -0
  154. package/src/organization-model/__tests__/prospecting-ssot.test.ts +103 -94
  155. package/src/organization-model/__tests__/recursive-system-schema.test.ts +549 -0
  156. package/src/organization-model/__tests__/resolve.test.ts +303 -42
  157. package/src/organization-model/__tests__/schema.test.ts +863 -153
  158. package/src/organization-model/__tests__/surface-projection.test.ts +284 -174
  159. package/src/organization-model/catalogs/lead-gen.ts +144 -0
  160. package/src/organization-model/content-kinds/config.ts +36 -0
  161. package/src/organization-model/content-kinds/index.ts +78 -0
  162. package/src/organization-model/content-kinds/pipeline.ts +68 -0
  163. package/src/organization-model/content-kinds/registry.ts +44 -0
  164. package/src/organization-model/content-kinds/status.ts +71 -0
  165. package/src/organization-model/content-kinds/template.ts +83 -0
  166. package/src/organization-model/content-kinds/types.ts +117 -0
  167. package/src/organization-model/contracts.ts +27 -17
  168. package/src/organization-model/defaults.ts +489 -107
  169. package/src/organization-model/domains/actions.ts +333 -0
  170. package/src/organization-model/domains/customers.ts +10 -7
  171. package/src/organization-model/domains/entities.ts +144 -0
  172. package/src/organization-model/domains/goals.ts +9 -6
  173. package/src/organization-model/domains/knowledge.ts +128 -54
  174. package/src/organization-model/domains/navigation.ts +139 -416
  175. package/src/organization-model/domains/offerings.ts +15 -10
  176. package/src/organization-model/domains/policies.ts +102 -0
  177. package/src/organization-model/domains/projects.ts +6 -40
  178. package/src/organization-model/domains/prospecting.ts +395 -514
  179. package/src/organization-model/domains/resources.ts +173 -81
  180. package/src/organization-model/domains/roles.ts +96 -93
  181. package/src/organization-model/domains/sales.test.ts +218 -218
  182. package/src/organization-model/domains/sales.ts +380 -589
  183. package/src/organization-model/domains/shared.ts +8 -8
  184. package/src/organization-model/domains/statuses.ts +298 -89
  185. package/src/organization-model/domains/systems.ts +240 -38
  186. package/src/organization-model/foundation.ts +35 -48
  187. package/src/organization-model/graph/build.ts +1035 -279
  188. package/src/organization-model/graph/index.ts +4 -4
  189. package/src/organization-model/graph/link.ts +10 -10
  190. package/src/organization-model/graph/schema.ts +77 -56
  191. package/src/organization-model/graph/types.ts +75 -56
  192. package/src/organization-model/helpers.ts +312 -59
  193. package/src/organization-model/icons.ts +78 -66
  194. package/src/organization-model/index.ts +129 -16
  195. package/src/organization-model/migration-helpers.ts +252 -0
  196. package/src/organization-model/ontology.ts +661 -0
  197. package/src/organization-model/organization-graph.mdx +110 -89
  198. package/src/organization-model/organization-model.mdx +226 -171
  199. package/src/organization-model/published.ts +295 -139
  200. package/src/organization-model/resolve.ts +139 -21
  201. package/src/organization-model/schema.ts +841 -301
  202. package/src/organization-model/surface-projection.ts +212 -218
  203. package/src/organization-model/types.ts +181 -90
  204. package/src/platform/api/types.ts +38 -38
  205. package/src/platform/constants/versions.ts +3 -3
  206. package/src/platform/index.ts +23 -23
  207. package/src/platform/registry/__tests__/command-view.test.ts +5 -7
  208. package/src/platform/registry/__tests__/resource-link.test.ts +35 -30
  209. package/src/platform/registry/__tests__/resource-registry.integration.test.ts +17 -32
  210. package/src/platform/registry/__tests__/resource-registry.nested-systems.test.ts +245 -0
  211. package/src/platform/registry/__tests__/resource-registry.test.ts +2053 -2051
  212. package/src/platform/registry/__tests__/validation.test.ts +1347 -1343
  213. package/src/platform/registry/command-view.ts +10 -10
  214. package/src/platform/registry/index.ts +103 -103
  215. package/src/platform/registry/resource-link.ts +32 -32
  216. package/src/platform/registry/resource-registry.ts +890 -878
  217. package/src/platform/registry/serialization.ts +295 -295
  218. package/src/platform/registry/serialized-types.ts +166 -166
  219. package/src/platform/registry/stats-types.ts +68 -68
  220. package/src/platform/registry/types.ts +425 -425
  221. package/src/platform/registry/validation.ts +745 -743
  222. package/src/platform/utils/__tests__/validation.test.ts +1084 -1084
  223. package/src/platform/utils/validation.ts +425 -425
  224. package/src/projects/api-schemas.test.ts +39 -39
  225. package/src/projects/api-schemas.ts +291 -291
  226. package/src/reference/_generated/contracts.md +2389 -2121
  227. package/src/reference/glossary.md +76 -76
  228. package/src/scaffold-registry/__tests__/index.test.ts +206 -206
  229. package/src/scaffold-registry/__tests__/schema.test.ts +166 -166
  230. package/src/scaffold-registry/index.ts +392 -392
  231. package/src/scaffold-registry/schema.ts +243 -243
  232. package/src/server.ts +289 -289
  233. package/src/supabase/database.types.ts +3153 -3093
  234. package/src/test-utils/README.md +37 -37
  235. package/src/test-utils/entities.ts +108 -108
  236. package/src/test-utils/fixtures/memberships.ts +82 -82
  237. package/src/test-utils/index.ts +12 -12
  238. package/src/test-utils/organization-model.ts +65 -65
  239. package/src/test-utils/published.ts +6 -6
  240. package/src/test-utils/rls/RLSTestContext.ts +588 -588
  241. package/src/test-utils/test-utils.test.ts +44 -49
  242. package/src/organization-model/__tests__/domains/operations.test.ts +0 -203
  243. package/src/organization-model/domains/features.ts +0 -31
  244. package/src/organization-model/domains/operations.ts +0 -85
@@ -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
+ }