@vibesdotdev/integrations 0.0.0-stage → 1.0.0-rc.2

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 (242) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +100 -2
  3. package/dist/ai/ai.cloud.plugin.d.ts +5 -0
  4. package/dist/ai/ai.cloud.plugin.d.ts.map +1 -0
  5. package/dist/ai/ai.cloud.plugin.js +67 -0
  6. package/dist/ai/ai.cloud.plugin.js.map +1 -0
  7. package/dist/ai/ai.plugin.d.ts +5 -0
  8. package/dist/ai/ai.plugin.d.ts.map +1 -0
  9. package/dist/ai/ai.plugin.js +97 -0
  10. package/dist/ai/ai.plugin.js.map +1 -0
  11. package/dist/base/base.plugin.d.ts +10 -0
  12. package/dist/base/base.plugin.d.ts.map +1 -0
  13. package/dist/base/base.plugin.js +23 -0
  14. package/dist/base/base.plugin.js.map +1 -0
  15. package/dist/base/index.d.ts +13 -0
  16. package/dist/base/index.d.ts.map +1 -0
  17. package/dist/base/index.js +15 -0
  18. package/dist/base/index.js.map +1 -0
  19. package/dist/base/resolver.d.ts +13 -0
  20. package/dist/base/resolver.d.ts.map +1 -0
  21. package/dist/base/resolver.js +30 -0
  22. package/dist/base/resolver.js.map +1 -0
  23. package/dist/base/schemas.d.ts +167 -0
  24. package/dist/base/schemas.d.ts.map +1 -0
  25. package/dist/base/schemas.js +48 -0
  26. package/dist/base/schemas.js.map +1 -0
  27. package/dist/catalog/index.d.ts +6 -0
  28. package/dist/catalog/index.d.ts.map +1 -0
  29. package/dist/catalog/index.js +6 -0
  30. package/dist/catalog/index.js.map +1 -0
  31. package/dist/catalog/integration-catalog.capability.d.ts +14 -0
  32. package/dist/catalog/integration-catalog.capability.d.ts.map +1 -0
  33. package/dist/catalog/integration-catalog.capability.js +23 -0
  34. package/dist/catalog/integration-catalog.capability.js.map +1 -0
  35. package/dist/catalog/integration-catalog.descriptor.d.ts +6 -0
  36. package/dist/catalog/integration-catalog.descriptor.d.ts.map +1 -0
  37. package/dist/catalog/integration-catalog.descriptor.js +11 -0
  38. package/dist/catalog/integration-catalog.descriptor.js.map +1 -0
  39. package/dist/catalog/integration-catalog.exposure.d.ts +62 -0
  40. package/dist/catalog/integration-catalog.exposure.d.ts.map +1 -0
  41. package/dist/catalog/integration-catalog.exposure.js +101 -0
  42. package/dist/catalog/integration-catalog.exposure.js.map +1 -0
  43. package/dist/catalog/integration-catalog.http.d.ts +9 -0
  44. package/dist/catalog/integration-catalog.http.d.ts.map +1 -0
  45. package/dist/catalog/integration-catalog.http.js +58 -0
  46. package/dist/catalog/integration-catalog.http.js.map +1 -0
  47. package/dist/catalog/integration-catalog.kind.d.ts +36 -0
  48. package/dist/catalog/integration-catalog.kind.d.ts.map +1 -0
  49. package/dist/catalog/integration-catalog.kind.js +14 -0
  50. package/dist/catalog/integration-catalog.kind.js.map +1 -0
  51. package/dist/catalog/queries.d.ts +6 -0
  52. package/dist/catalog/queries.d.ts.map +1 -0
  53. package/dist/catalog/queries.js +29 -0
  54. package/dist/catalog/queries.js.map +1 -0
  55. package/dist/client/helper.d.ts +5 -0
  56. package/dist/client/helper.d.ts.map +1 -0
  57. package/dist/client/helper.js +6 -0
  58. package/dist/client/helper.js.map +1 -0
  59. package/dist/client/index.d.ts +4 -0
  60. package/dist/client/index.d.ts.map +1 -0
  61. package/dist/client/index.js +3 -0
  62. package/dist/client/index.js.map +1 -0
  63. package/dist/client/integrations.runtime-client.browser.d.ts +10 -0
  64. package/dist/client/integrations.runtime-client.browser.d.ts.map +1 -0
  65. package/dist/client/integrations.runtime-client.browser.js +13 -0
  66. package/dist/client/integrations.runtime-client.browser.js.map +1 -0
  67. package/dist/client/integrations.runtime-client.d.ts +25 -0
  68. package/dist/client/integrations.runtime-client.d.ts.map +1 -0
  69. package/dist/client/integrations.runtime-client.descriptor.d.ts +4 -0
  70. package/dist/client/integrations.runtime-client.descriptor.d.ts.map +1 -0
  71. package/dist/client/integrations.runtime-client.descriptor.js +13 -0
  72. package/dist/client/integrations.runtime-client.descriptor.js.map +1 -0
  73. package/dist/client/integrations.runtime-client.js +41 -0
  74. package/dist/client/integrations.runtime-client.js.map +1 -0
  75. package/dist/docs/integrations.docs.descriptor.d.ts +4 -0
  76. package/dist/docs/integrations.docs.descriptor.d.ts.map +1 -0
  77. package/dist/docs/integrations.docs.descriptor.js +43 -0
  78. package/dist/docs/integrations.docs.descriptor.js.map +1 -0
  79. package/dist/docs/integrations.providers.docs.descriptor.d.ts +4 -0
  80. package/dist/docs/integrations.providers.docs.descriptor.d.ts.map +1 -0
  81. package/dist/docs/integrations.providers.docs.descriptor.js +32 -0
  82. package/dist/docs/integrations.providers.docs.descriptor.js.map +1 -0
  83. package/dist/http/fetch-with-retry.d.ts +27 -0
  84. package/dist/http/fetch-with-retry.d.ts.map +1 -0
  85. package/dist/http/fetch-with-retry.js +76 -0
  86. package/dist/http/fetch-with-retry.js.map +1 -0
  87. package/dist/index.d.ts +24 -0
  88. package/dist/index.d.ts.map +1 -0
  89. package/dist/index.js +16 -0
  90. package/dist/index.js.map +1 -0
  91. package/dist/integrations.browser.plugin.d.ts +11 -0
  92. package/dist/integrations.browser.plugin.d.ts.map +1 -0
  93. package/dist/integrations.browser.plugin.js +26 -0
  94. package/dist/integrations.browser.plugin.js.map +1 -0
  95. package/dist/integrations.consumer.plugin.d.ts +3 -0
  96. package/dist/integrations.consumer.plugin.d.ts.map +1 -0
  97. package/dist/integrations.consumer.plugin.js +12 -0
  98. package/dist/integrations.consumer.plugin.js.map +1 -0
  99. package/dist/integrations.plugin.d.ts +10 -0
  100. package/dist/integrations.plugin.d.ts.map +1 -0
  101. package/dist/integrations.plugin.js +67 -0
  102. package/dist/integrations.plugin.js.map +1 -0
  103. package/dist/kinds/integration-store.context.descriptor.d.ts +31 -0
  104. package/dist/kinds/integration-store.context.descriptor.d.ts.map +1 -0
  105. package/dist/kinds/integration-store.context.descriptor.js +8 -0
  106. package/dist/kinds/integration-store.context.descriptor.js.map +1 -0
  107. package/dist/kinds/integration.kind.d.ts +74 -0
  108. package/dist/kinds/integration.kind.d.ts.map +1 -0
  109. package/dist/kinds/integration.kind.js +542 -0
  110. package/dist/kinds/integration.kind.js.map +1 -0
  111. package/dist/kinds/integration.store.d.ts +30 -0
  112. package/dist/kinds/integration.store.d.ts.map +1 -0
  113. package/dist/kinds/integration.store.js +295 -0
  114. package/dist/kinds/integration.store.js.map +1 -0
  115. package/dist/schemas/boundary.d.ts +7 -0
  116. package/dist/schemas/boundary.d.ts.map +1 -0
  117. package/dist/schemas/boundary.js +16 -0
  118. package/dist/schemas/boundary.js.map +1 -0
  119. package/dist/schemas/catalog.d.ts +135 -0
  120. package/dist/schemas/catalog.d.ts.map +1 -0
  121. package/dist/schemas/catalog.js +13 -0
  122. package/dist/schemas/catalog.js.map +1 -0
  123. package/dist/schemas/credential-resolution.d.ts +35 -0
  124. package/dist/schemas/credential-resolution.d.ts.map +1 -0
  125. package/dist/schemas/credential-resolution.js +14 -0
  126. package/dist/schemas/credential-resolution.js.map +1 -0
  127. package/dist/schemas/http-retry.d.ts +9 -0
  128. package/dist/schemas/http-retry.d.ts.map +1 -0
  129. package/dist/schemas/http-retry.js +8 -0
  130. package/dist/schemas/http-retry.js.map +1 -0
  131. package/dist/schemas/integration-connection.d.ts +79 -0
  132. package/dist/schemas/integration-connection.d.ts.map +1 -0
  133. package/dist/schemas/integration-connection.js +55 -0
  134. package/dist/schemas/integration-connection.js.map +1 -0
  135. package/dist/schemas/integration-descriptor.d.ts +358 -0
  136. package/dist/schemas/integration-descriptor.d.ts.map +1 -0
  137. package/dist/schemas/integration-descriptor.js +153 -0
  138. package/dist/schemas/integration-descriptor.js.map +1 -0
  139. package/dist/schemas/oauth-state.d.ts +30 -0
  140. package/dist/schemas/oauth-state.d.ts.map +1 -0
  141. package/dist/schemas/oauth-state.js +26 -0
  142. package/dist/schemas/oauth-state.js.map +1 -0
  143. package/dist/schemas/settings.d.ts +9 -0
  144. package/dist/schemas/settings.d.ts.map +1 -0
  145. package/dist/schemas/settings.js +8 -0
  146. package/dist/schemas/settings.js.map +1 -0
  147. package/dist/schemas/store.d.ts +68 -0
  148. package/dist/schemas/store.d.ts.map +1 -0
  149. package/dist/schemas/store.js +31 -0
  150. package/dist/schemas/store.js.map +1 -0
  151. package/dist/server/defaults/system-defaults.kind.d.ts +8 -0
  152. package/dist/server/defaults/system-defaults.kind.d.ts.map +1 -0
  153. package/dist/server/defaults/system-defaults.kind.js +13 -0
  154. package/dist/server/defaults/system-defaults.kind.js.map +1 -0
  155. package/dist/server/integration-oauth.d.ts +44 -0
  156. package/dist/server/integration-oauth.d.ts.map +1 -0
  157. package/dist/server/integration-oauth.js +56 -0
  158. package/dist/server/integration-oauth.js.map +1 -0
  159. package/dist/server/integration-secrets.d.ts +11 -0
  160. package/dist/server/integration-secrets.d.ts.map +1 -0
  161. package/dist/server/integration-secrets.js +98 -0
  162. package/dist/server/integration-secrets.js.map +1 -0
  163. package/dist/server/oauth-state.d.ts +74 -0
  164. package/dist/server/oauth-state.d.ts.map +1 -0
  165. package/dist/server/oauth-state.js +208 -0
  166. package/dist/server/oauth-state.js.map +1 -0
  167. package/dist/server/schemas/integration-oauth.d.ts +32 -0
  168. package/dist/server/schemas/integration-oauth.d.ts.map +1 -0
  169. package/dist/server/schemas/integration-oauth.js +20 -0
  170. package/dist/server/schemas/integration-oauth.js.map +1 -0
  171. package/dist/server/schemas/integration-secrets.d.ts +165 -0
  172. package/dist/server/schemas/integration-secrets.d.ts.map +1 -0
  173. package/dist/server/schemas/integration-secrets.js +72 -0
  174. package/dist/server/schemas/integration-secrets.js.map +1 -0
  175. package/dist/server/schemas/system-defaults.d.ts +124 -0
  176. package/dist/server/schemas/system-defaults.d.ts.map +1 -0
  177. package/dist/server/schemas/system-defaults.js +48 -0
  178. package/dist/server/schemas/system-defaults.js.map +1 -0
  179. package/dist/server/system-defaults.d.ts +20 -0
  180. package/dist/server/system-defaults.d.ts.map +1 -0
  181. package/dist/server/system-defaults.js +58 -0
  182. package/dist/server/system-defaults.js.map +1 -0
  183. package/dist/settings/context.d.ts +11 -0
  184. package/dist/settings/context.d.ts.map +1 -0
  185. package/dist/settings/context.js +19 -0
  186. package/dist/settings/context.js.map +1 -0
  187. package/dist/settings/system-settings-reader.context.descriptor.d.ts +43 -0
  188. package/dist/settings/system-settings-reader.context.descriptor.d.ts.map +1 -0
  189. package/dist/settings/system-settings-reader.context.descriptor.js +23 -0
  190. package/dist/settings/system-settings-reader.context.descriptor.js.map +1 -0
  191. package/dist/status/integration-connect.availability.d.ts +16 -0
  192. package/dist/status/integration-connect.availability.d.ts.map +1 -0
  193. package/dist/status/integration-connect.availability.js +14 -0
  194. package/dist/status/integration-connect.availability.js.map +1 -0
  195. package/dist/status/integration-connection-status.d.ts +18 -0
  196. package/dist/status/integration-connection-status.d.ts.map +1 -0
  197. package/dist/status/integration-connection-status.js +17 -0
  198. package/dist/status/integration-connection-status.js.map +1 -0
  199. package/dist/status/integration-connection.capability.d.ts +19 -0
  200. package/dist/status/integration-connection.capability.d.ts.map +1 -0
  201. package/dist/status/integration-connection.capability.js +15 -0
  202. package/dist/status/integration-connection.capability.js.map +1 -0
  203. package/dist/status/integration-connection.kind.d.ts +55 -0
  204. package/dist/status/integration-connection.kind.d.ts.map +1 -0
  205. package/dist/status/integration-connection.kind.js +31 -0
  206. package/dist/status/integration-connection.kind.js.map +1 -0
  207. package/dist/status/integration-connection.queries.d.ts +25 -0
  208. package/dist/status/integration-connection.queries.d.ts.map +1 -0
  209. package/dist/status/integration-connection.queries.js +65 -0
  210. package/dist/status/integration-connection.queries.js.map +1 -0
  211. package/dist/svelte/components/IntegrationsCatalog.svelte +201 -0
  212. package/dist/svelte/components/IntegrationsCatalog.svelte.d.ts +5 -0
  213. package/dist/svelte/components/IntegrationsCatalog.svelte.d.ts.map +1 -0
  214. package/dist/svelte/components/IntegrationsLoader.svelte +46 -0
  215. package/dist/svelte/components/IntegrationsLoader.svelte.d.ts +12 -0
  216. package/dist/svelte/components/IntegrationsLoader.svelte.d.ts.map +1 -0
  217. package/dist/svelte/connect-action.d.ts +20 -0
  218. package/dist/svelte/connect-action.d.ts.map +1 -0
  219. package/dist/svelte/connect-action.js +40 -0
  220. package/dist/svelte/docs/integrations-svelte.catalog.docs.descriptor.d.ts +4 -0
  221. package/dist/svelte/docs/integrations-svelte.catalog.docs.descriptor.d.ts.map +1 -0
  222. package/dist/svelte/docs/integrations-svelte.catalog.docs.descriptor.js +30 -0
  223. package/dist/svelte/index.d.ts +12 -0
  224. package/dist/svelte/index.d.ts.map +1 -0
  225. package/dist/svelte/index.js +7 -0
  226. package/dist/svelte/integrations-svelte.plugin.d.ts +10 -0
  227. package/dist/svelte/integrations-svelte.plugin.d.ts.map +1 -0
  228. package/dist/svelte/integrations-svelte.plugin.js +16 -0
  229. package/dist/svelte/loaders.d.ts +17 -0
  230. package/dist/svelte/loaders.d.ts.map +1 -0
  231. package/dist/svelte/loaders.js +17 -0
  232. package/dist/svelte/runtime.d.ts +12 -0
  233. package/dist/svelte/runtime.d.ts.map +1 -0
  234. package/dist/svelte/runtime.js +11 -0
  235. package/dist/svelte/state.svelte.d.ts +30 -0
  236. package/dist/svelte/state.svelte.d.ts.map +1 -0
  237. package/dist/svelte/state.svelte.js +133 -0
  238. package/dist/svelte/styles.css +2 -0
  239. package/dist/svelte/types.d.ts +13 -0
  240. package/dist/svelte/types.d.ts.map +1 -0
  241. package/dist/svelte/types.js +1 -0
  242. package/package.json +223 -4
@@ -0,0 +1,74 @@
1
+ import type { KindContext, RuntimeKindDescriptor } from '@vibesdotdev/kernel/schemas/kind';
2
+ import type { RuntimeDescriptor } from '@vibesdotdev/kernel/schemas/descriptor';
3
+ import type { CredentialCandidates } from '@vibesdotdev/secrets/schemas';
4
+ import { type IntegrationConfig, type IntegrationConnectionSelector, type IntegrationCredentialSource, type IntegrationCredentialWriteMetadata, type IntegrationConfigValue, type IntegrationOAuthAuthorization, type IntegrationOAuthCompletion, type IntegrationOAuthStart, type IntegrationOAuthMetadata, type IntegrationOperationResult, type IntegrationProviderDescriptor, type IntegrationProviderImplementation, type IntegrationProviderResolvedConfiguration } from '../schemas/integration-descriptor.ts';
5
+ /**
6
+ * Base implementation of the integration/provider kind. Handles credential
7
+ * resolution, persistent config, OAuth lifecycle for descriptors that
8
+ * declare an `oauth` block, and operation dispatch.
9
+ *
10
+ * Provider packs (e.g. `@vibesdotdev/integrations-quickbooks`) extend this
11
+ * class to implement declared operations like `'sync'`. The runtime calls
12
+ * extended classes through plugin loaders registered on the
13
+ * `integration/provider` kind.
14
+ */
15
+ export declare class BaseIntegrationProvider implements IntegrationProviderImplementation {
16
+ readonly descriptor: IntegrationProviderDescriptor;
17
+ private readonly settingsReader;
18
+ private readonly store;
19
+ protected readonly context: KindContext;
20
+ constructor(descriptor: RuntimeDescriptor, context: KindContext);
21
+ /** Throw if the descriptor doesn't declare the named operation. */
22
+ protected requireDeclaredOperation(opId: string): void;
23
+ /** Provider policy applies to every read before Core resolves metadata or values. */
24
+ protected credentialSources(): Promise<IntegrationCredentialSource[]>;
25
+ getConfig(): Promise<IntegrationConfig>;
26
+ getCredentials(): Promise<Record<string, string>>;
27
+ getCredential(key: string): Promise<string | undefined>;
28
+ /** Return metadata references without resolving credential values in provider code. */
29
+ getCredentialCandidates(key: string): Promise<CredentialCandidates>;
30
+ private credentialCandidates;
31
+ /** Provider request configuration contains references; only Secrets reads their values. */
32
+ getCredentialUseConfiguration(selector?: Readonly<IntegrationConnectionSelector>): Promise<IntegrationProviderResolvedConfiguration>;
33
+ private loadSelectedConnection;
34
+ getResolvedConfiguration(selector?: Readonly<IntegrationConnectionSelector>): Promise<IntegrationProviderResolvedConfiguration>;
35
+ setCredential(key: string, value: string, metadata?: IntegrationCredentialWriteMetadata): Promise<void>;
36
+ unsetCredential(key: string): Promise<void>;
37
+ startAuthorization(input: IntegrationOAuthStart): Promise<IntegrationOAuthAuthorization>;
38
+ completeAuthorization(input: IntegrationOAuthCompletion): Promise<void>;
39
+ private missingStoreError;
40
+ invokeOperation<T = unknown>(opId: string, args?: IntegrationConfigValue): Promise<IntegrationOperationResult<T>>;
41
+ protected requireOAuthMetadata(): IntegrationOAuthMetadata;
42
+ private requireWriteableSource;
43
+ private scopedSource;
44
+ private resolveCredentialSource;
45
+ private resolveCredentialSet;
46
+ private readSelectedConnection;
47
+ private hostCredentialSources;
48
+ private mergeCredentialReferences;
49
+ private resolveSelectedConnection;
50
+ private protectHostEndpointConfiguration;
51
+ private resolveCredentialReference;
52
+ /**
53
+ * Build org-scoped scopeId for credential lookup.
54
+ * Combines the live request orgId with the descriptor-level scopeId.
55
+ * Format: {orgId}:{scopeId} when both present, {orgId} when only orgId.
56
+ */
57
+ private resolveScopeId;
58
+ /**
59
+ * Resolve the CALLING request's org id at call time. The integration
60
+ * provider instance is value-cached by the runtime for the isolate
61
+ * lifetime, so a constructor-snapshotted `this.context.orgId` would
62
+ * freeze to whichever org FIRST resolved the provider — every later
63
+ * request (any org) would then read and write that first org's
64
+ * credentials and config (cross-tenant bleed; the Microsoft pack fixed
65
+ * the same bug in ac19f2688). Read the live request context instead
66
+ * (`runtime.context('orgId')`, set by hosts via runWithRequestContext),
67
+ * falling back to the constructor-injected context orgId, then
68
+ * undefined (system/platform scope).
69
+ */
70
+ protected getCurrentOrgId(): Promise<string | undefined>;
71
+ private getSettingValue;
72
+ }
73
+ export declare const integrationProviderKind: RuntimeKindDescriptor<IntegrationProviderDescriptor, IntegrationProviderImplementation, KindContext>;
74
+ //# sourceMappingURL=integration.kind.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration.kind.d.ts","sourceRoot":"","sources":["../../src/kinds/integration.kind.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,542 @@
1
+ import { parseIntegrationBoundary, IntegrationContextOrgIdSchema } from "../schemas/boundary.js";
2
+ import { IntegrationCredentialSourceSchema } from "../schemas/integration-descriptor.js";
3
+ import { CapabilityError } from '@vibesdotdev/kernel';
4
+ import { CredentialReferenceSchema } from '@vibesdotdev/kernel/credentials';
5
+ import { applyIntegrationClientId, exchangeIntegrationAuthorizationCode } from "../server/integration-oauth.js";
6
+ import { IntegrationConfigValueSchema, IntegrationConfigSchema, IntegrationOperationInputSchema, IntegrationConnectionSelectorSchema, IntegrationProviderResolvedConfigurationSchema, IntegrationOAuthStartSchema, IntegrationOAuthCompletionSchema, IntegrationCredentialKeySchema, IntegrationCredentialWriteRequestSchema, IntegrationProviderDescriptorSchema } from "../schemas/integration-descriptor.js";
7
+ import { isIntegrationStoreAdapter } from "./integration.store.js";
8
+ import { integrationStoreContextId } from "./integration-store.context.descriptor.js";
9
+ import { createUnavailableSystemSettingsReader, isSystemSettingsReader, systemSettingsReaderContextId } from "../settings/context.js";
10
+ /**
11
+ * Base implementation of the integration/provider kind. Handles credential
12
+ * resolution, persistent config, OAuth lifecycle for descriptors that
13
+ * declare an `oauth` block, and operation dispatch.
14
+ *
15
+ * Provider packs (e.g. `@vibesdotdev/integrations-quickbooks`) extend this
16
+ * class to implement declared operations like `'sync'`. The runtime calls
17
+ * extended classes through plugin loaders registered on the
18
+ * `integration/provider` kind.
19
+ */
20
+ export class BaseIntegrationProvider {
21
+ descriptor;
22
+ settingsReader;
23
+ store;
24
+ context;
25
+ constructor(descriptor, context) {
26
+ this.descriptor = parseIntegrationBoundary(IntegrationProviderDescriptorSchema, descriptor);
27
+ this.context = context;
28
+ // Use a structural guard, not a nullish check: the runtime fills an
29
+ // unwired context slot with an inert placeholder that is truthy but
30
+ // has no `.get()`, which would defeat `?? createUnavailable...` and
31
+ // throw "settingsReader.get is not a function" on the first getConfig()
32
+ // of any descriptor with a configSettingKey. isSystemSettingsReader
33
+ // only accepts a real injected reader.
34
+ const injectedSettingsReader = context[systemSettingsReaderContextId];
35
+ this.settingsReader = isSystemSettingsReader(injectedSettingsReader)
36
+ ? injectedSettingsReader
37
+ : createUnavailableSystemSettingsReader(context.runtime);
38
+ const injectedStore = context[integrationStoreContextId];
39
+ this.store = isIntegrationStoreAdapter(injectedStore) ? injectedStore : undefined;
40
+ }
41
+ /** Throw if the descriptor doesn't declare the named operation. */
42
+ requireDeclaredOperation(opId) {
43
+ const declared = (this.descriptor.operations ?? []).find((op) => op.id === opId);
44
+ if (!declared) {
45
+ throw new CapabilityError('The integration operation is not declared.', 'INVALID_INPUT', { reason: 'integration-operation-undeclared', retryable: false });
46
+ }
47
+ }
48
+ /** Provider policy applies to every read before Core resolves metadata or values. */
49
+ async credentialSources() {
50
+ return this.descriptor.credentials;
51
+ }
52
+ async getConfig() {
53
+ if (this.descriptor.configurationSource === 'host')
54
+ return { ...this.descriptor.config };
55
+ let config = { ...this.descriptor.config };
56
+ const settingKey = this.descriptor.configSettingKey;
57
+ if (settingKey) {
58
+ const settingValue = await this.getSettingValue(settingKey);
59
+ if (settingValue !== undefined) {
60
+ config = { ...config, ...parseIntegrationBoundary(IntegrationConfigSchema, settingValue) };
61
+ }
62
+ }
63
+ // Scope the persisted-config read to this actor's org using the SAME
64
+ // selector the credential reads use (scope: 'org' + composite
65
+ // scopeId derived from the live request orgId). Without this,
66
+ // getConfig() returns whichever org's `core` row was updated most
67
+ // recently — a cross-tenant config bleed (org A reads org B's
68
+ // realmId etc.).
69
+ const orgId = await this.getCurrentOrgId();
70
+ const serviceConfig = await this.store?.loadConfig(this.descriptor.id, {
71
+ scope: 'org',
72
+ scopeId: this.resolveScopeId(undefined, orgId)
73
+ });
74
+ if (serviceConfig) {
75
+ config = { ...config, ...parseIntegrationBoundary(IntegrationConfigSchema, serviceConfig) };
76
+ }
77
+ return config;
78
+ }
79
+ async getCredentials() {
80
+ return (await this.resolveCredentialSet()).credentials;
81
+ }
82
+ async getCredential(key) {
83
+ const credentials = await this.getCredentials();
84
+ return credentials[key];
85
+ }
86
+ /** Return metadata references without resolving credential values in provider code. */
87
+ async getCredentialCandidates(key) {
88
+ key = parseIntegrationBoundary(IntegrationCredentialKeySchema, key);
89
+ const sources = (await this.credentialSources()).filter(source => source.key === key);
90
+ return this.credentialCandidates(sources);
91
+ }
92
+ async credentialCandidates(sources) {
93
+ const candidates = [];
94
+ for (const source of sources) {
95
+ if (source.source === 'setting') {
96
+ const value = await this.getSettingValue(source.settingKey);
97
+ if (value !== undefined)
98
+ candidates.push(parseIntegrationBoundary(CredentialReferenceSchema, value));
99
+ continue;
100
+ }
101
+ if (source.reference !== undefined && source.scope === undefined) {
102
+ candidates.push(source.reference);
103
+ continue;
104
+ }
105
+ if (this.store) {
106
+ const orgId = source.scope === undefined || source.scope === 'org' ? await this.getCurrentOrgId() : undefined;
107
+ const selected = await this.store.loadCredential(this.descriptor.id, this.scopedSource(source, orgId));
108
+ if (selected)
109
+ candidates.push(parseIntegrationBoundary(CredentialReferenceSchema, selected));
110
+ }
111
+ if (source.reference)
112
+ candidates.push(source.reference);
113
+ }
114
+ return candidates;
115
+ }
116
+ /** Provider request configuration contains references; only Secrets reads their values. */
117
+ async getCredentialUseConfiguration(selector) {
118
+ if (selector && this.descriptor.configurationSource === 'host') {
119
+ throw new CapabilityError('Host integration configuration cannot select a saved account.', 'CONFLICT', {
120
+ reason: 'integration-host-credential-selection-conflict', retryable: false
121
+ });
122
+ }
123
+ let selected;
124
+ if (selector) {
125
+ const load = this.store?.loadConnectionReferences;
126
+ if (!load)
127
+ throw new CapabilityError('Saved connection reference delivery is unavailable.', 'UNAVAILABLE', {
128
+ reason: 'integration-connection-references-unsupported', retryable: false
129
+ });
130
+ selected = await this.loadSelectedConnection(selector, load);
131
+ }
132
+ const declaredSources = await this.credentialSources();
133
+ const sources = selected ? this.hostCredentialSources(declaredSources) : declaredSources;
134
+ const entries = [];
135
+ for (const key of new Set(sources.map(source => source.key))) {
136
+ const candidates = await this.credentialCandidates(sources.filter(source => source.key === key));
137
+ if (candidates.length > 0)
138
+ entries.push([key, candidates]);
139
+ }
140
+ const references = Object.fromEntries(entries);
141
+ const hostKeys = new Set(sources
142
+ .filter(source => source.source === 'setting' || source.reference !== undefined)
143
+ .map(source => source.key));
144
+ return parseIntegrationBoundary(IntegrationProviderResolvedConfigurationSchema, {
145
+ config: this.protectHostEndpointConfiguration(selected?.config ?? await this.getConfig(), hostKeys),
146
+ credentials: {},
147
+ credentialReferences: this.mergeCredentialReferences(references, selected?.credentialReferences ?? {})
148
+ });
149
+ }
150
+ async loadSelectedConnection(selector, load) {
151
+ selector = parseIntegrationBoundary(IntegrationConnectionSelectorSchema, selector);
152
+ const runtime = this.context.runtime;
153
+ if (!runtime) {
154
+ throw new CapabilityError('Selected integration delivery requires its authenticated runtime request.', 'UNAVAILABLE', { reason: 'integration-runtime-required', retryable: false });
155
+ }
156
+ const principal = runtime.requestPrincipal();
157
+ if (principal.kind === 'service') {
158
+ const account = principal.delegation?.account;
159
+ if (!account || principal.onBehalfOf !== account.ownerId || account.integrationId !== this.descriptor.id || account.connectionId !== selector.connectionId) {
160
+ throw new CapabilityError('Selected integration delivery requires verified authority for this account.', 'PERMISSION_DENIED', { reason: 'integration-account-delegation-mismatch', retryable: false });
161
+ }
162
+ const actor = { userId: account.ownerId };
163
+ if (account.orgId !== undefined)
164
+ actor.organizationId = account.orgId;
165
+ return this.readSelectedConnection(load, selector, actor);
166
+ }
167
+ if (principal.kind !== 'user')
168
+ throw new CapabilityError('Selected integration delivery requires an authenticated user.', 'PERMISSION_DENIED', { reason: 'integration-account-principal-required', retryable: false });
169
+ const actor = { userId: principal.userId };
170
+ if (principal.orgId !== undefined)
171
+ actor.organizationId = principal.orgId;
172
+ return this.readSelectedConnection(load, selector, actor);
173
+ }
174
+ async getResolvedConfiguration(selector) {
175
+ if (selector && this.descriptor.configurationSource === 'host') {
176
+ throw new CapabilityError('Host integration configuration cannot select a saved account.', 'CONFLICT', {
177
+ reason: 'integration-host-credential-selection-conflict', retryable: false
178
+ });
179
+ }
180
+ if (selector) {
181
+ const load = this.store?.loadConnection;
182
+ if (!load)
183
+ throw new CapabilityError('Saved connection selection is unavailable.', 'UNAVAILABLE', { reason: 'integration-connection-selection-unsupported' });
184
+ return this.resolveSelectedConnection(await this.loadSelectedConnection(selector, load));
185
+ }
186
+ const [config, resolved] = await Promise.all([
187
+ this.getConfig(),
188
+ this.resolveCredentialSet()
189
+ ]);
190
+ return {
191
+ config: this.protectHostEndpointConfiguration(config, resolved.hostCredentialKeys),
192
+ credentials: resolved.credentials,
193
+ credentialReferences: resolved.credentialReferences
194
+ };
195
+ }
196
+ async setCredential(key, value, metadata) {
197
+ const request = parseIntegrationBoundary(IntegrationCredentialWriteRequestSchema, {
198
+ key,
199
+ value,
200
+ metadata
201
+ });
202
+ const source = this.requireWriteableSource(request.key);
203
+ const orgId = source.scope === undefined || source.scope === 'org'
204
+ ? await this.getCurrentOrgId()
205
+ : undefined;
206
+ const scopedSource = this.scopedSource(source, orgId);
207
+ if (!this.store)
208
+ throw this.missingStoreError();
209
+ await this.store.writeCredential(this.descriptor.id, scopedSource, request.value, request.metadata);
210
+ }
211
+ async unsetCredential(key) {
212
+ key = parseIntegrationBoundary(IntegrationCredentialKeySchema, key);
213
+ const source = this.requireWriteableSource(key);
214
+ const orgId = source.scope === undefined || source.scope === 'org'
215
+ ? await this.getCurrentOrgId()
216
+ : undefined;
217
+ const scopedSource = this.scopedSource(source, orgId);
218
+ if (!this.store)
219
+ throw this.missingStoreError();
220
+ await this.store.deleteCredential(this.descriptor.id, scopedSource);
221
+ }
222
+ async startAuthorization(input) {
223
+ input = parseIntegrationBoundary(IntegrationOAuthStartSchema, input);
224
+ const oauth = this.requireOAuthMetadata();
225
+ const clientId = await this.getCredentialCandidates('clientId');
226
+ if (clientId.length === 0) {
227
+ throw new CapabilityError('Integration OAuth requires a client identifier.', 'UNAVAILABLE', { reason: 'integration-oauth-client-id-required', retryable: false });
228
+ }
229
+ const params = new URLSearchParams({
230
+ client_id: '{clientId}',
231
+ redirect_uri: input.redirectUri,
232
+ response_type: 'code',
233
+ state: input.state
234
+ });
235
+ const scopes = input.scopes ?? oauth.scopes;
236
+ if (scopes.length > 0) {
237
+ params.set('scope', scopes.join(' '));
238
+ }
239
+ for (const [key, value] of Object.entries(oauth.authorizationParams ?? {})) {
240
+ params.set(key, value);
241
+ }
242
+ try {
243
+ return { authorizationUrl: await applyIntegrationClientId(this.context.runtime, {
244
+ authorizationUrl: `${oauth.authorizationEndpoint}?${params.toString()}`,
245
+ clientId
246
+ }) };
247
+ }
248
+ catch (error) {
249
+ if (error instanceof CapabilityError && error.reason === 'oauth-client-credential-unset') {
250
+ throw new CapabilityError('Integration OAuth requires a client identifier.', 'UNAVAILABLE', { reason: 'integration-oauth-client-id-required', retryable: false });
251
+ }
252
+ throw error;
253
+ }
254
+ }
255
+ async completeAuthorization(input) {
256
+ input = parseIntegrationBoundary(IntegrationOAuthCompletionSchema, input);
257
+ const oauth = this.requireOAuthMetadata();
258
+ const tokens = await exchangeIntegrationAuthorizationCode(this.context.runtime, {
259
+ tokenEndpoint: oauth.tokenEndpoint,
260
+ clientId: await this.getCredentialCandidates('clientId'),
261
+ clientSecret: await this.getCredentialCandidates('clientSecret'),
262
+ clientAuthentication: oauth.clientAuthentication,
263
+ bodyEncoding: oauth.bodyEncoding,
264
+ headers: oauth.headers,
265
+ code: input.code,
266
+ redirectUri: input.redirectUri
267
+ });
268
+ // Persist callback-derived config first (e.g. QuickBooks realmId,
269
+ // Microsoft tenant) so subsequent getConfig() reads see them and
270
+ // the integration_services row is up-to-date before the secrets
271
+ // land via setCredential.
272
+ const configPatch = {};
273
+ for (const param of oauth.configFromCallback) {
274
+ const value = input.params?.[param];
275
+ if (value !== undefined)
276
+ configPatch[param] = value;
277
+ }
278
+ if (Object.keys(configPatch).length > 0) {
279
+ const orgId = await this.getCurrentOrgId();
280
+ if (!this.store)
281
+ throw this.missingStoreError();
282
+ await this.store.writeConfig(this.descriptor.id, { scope: 'org', scopeId: this.resolveScopeId(undefined, orgId) }, configPatch);
283
+ }
284
+ const accessExpiresAt = tokens.expires_in
285
+ ? new Date(Date.now() + tokens.expires_in * 1000).toISOString()
286
+ : undefined;
287
+ const refreshExpiry = tokens.x_refresh_token_expires_in ?? tokens.refresh_token_expires_in;
288
+ const refreshExpiresAt = refreshExpiry
289
+ ? new Date(Date.now() + refreshExpiry * 1000).toISOString()
290
+ : undefined;
291
+ await this.setCredential('accessToken', tokens.access_token, {
292
+ expiresAt: accessExpiresAt,
293
+ tags: { tokenType: 'access' }
294
+ });
295
+ if (tokens.refresh_token) {
296
+ await this.setCredential('refreshToken', tokens.refresh_token, {
297
+ expiresAt: refreshExpiresAt,
298
+ tags: { tokenType: 'refresh' }
299
+ });
300
+ }
301
+ }
302
+ missingStoreError() {
303
+ return new CapabilityError('The integration store is not mounted.', 'UNAVAILABLE', { reason: 'integration-store-unavailable', retryable: false });
304
+ }
305
+ async invokeOperation(opId, args) {
306
+ parseIntegrationBoundary(IntegrationOperationInputSchema, args);
307
+ const declared = (this.descriptor.operations ?? []).find((op) => op.id === opId);
308
+ if (!declared) {
309
+ throw new CapabilityError('The integration operation is not declared.', 'INVALID_INPUT', { reason: 'integration-operation-undeclared', retryable: false });
310
+ }
311
+ throw new CapabilityError('The integration operation has no implementation.', 'UNAVAILABLE', { reason: 'integration-operation-unimplemented', retryable: false });
312
+ }
313
+ requireOAuthMetadata() {
314
+ const oauth = this.descriptor.oauth;
315
+ if (!oauth) {
316
+ throw new CapabilityError('Integration OAuth metadata is not declared.', 'UNAVAILABLE', { reason: 'integration-oauth-metadata-required', retryable: false });
317
+ }
318
+ return oauth;
319
+ }
320
+ requireWriteableSource(key) {
321
+ key = parseIntegrationBoundary(IntegrationCredentialKeySchema, key);
322
+ const declared = (this.descriptor.credentials ?? []).filter((source) => source.key === key);
323
+ const source = declared.find((candidate) => candidate.source === 'secret'
324
+ && (candidate.scope !== undefined || candidate.reference === undefined));
325
+ if (!source) {
326
+ if (declared.some((candidate) => candidate.reference !== undefined && candidate.scope === undefined)) {
327
+ throw new CapabilityError('Host credential references are read-only.', 'INVALID_INPUT', { reason: 'integration-host-credential-readonly', retryable: false });
328
+ }
329
+ throw new CapabilityError('The integration credential is not declared as a managed secret.', 'INVALID_INPUT', { reason: 'integration-credential-source-readonly', retryable: false });
330
+ }
331
+ return source;
332
+ }
333
+ scopedSource(source, orgId) {
334
+ const scope = source.scope ?? 'org';
335
+ let scopeId;
336
+ if (scope === 'org') {
337
+ scopeId = this.resolveScopeId(source.scopeId, orgId);
338
+ }
339
+ else if (source.scopeId !== undefined) {
340
+ scopeId = source.scopeId;
341
+ }
342
+ else {
343
+ throw new CapabilityError('An integration credential requires a complete managed scope.', 'INVALID_INPUT', { reason: 'integration-credential-scope-incomplete', retryable: false });
344
+ }
345
+ return parseIntegrationBoundary(IntegrationCredentialSourceSchema, {
346
+ ...source,
347
+ scope,
348
+ scopeId,
349
+ providerType: source.providerType ?? this.descriptor.provider
350
+ });
351
+ }
352
+ async resolveCredentialSource(source, orgId) {
353
+ if (source.source === 'setting') {
354
+ const settingKey = source.settingKey;
355
+ if (!settingKey) {
356
+ throw new CapabilityError('The credential setting key is not declared.', 'INVALID_INPUT', {
357
+ reason: 'integration-credential-setting-key-required', retryable: false
358
+ });
359
+ }
360
+ const value = await this.getSettingValue(settingKey);
361
+ if (value === undefined)
362
+ return { value: undefined, hostReference: false };
363
+ const reference = parseIntegrationBoundary(CredentialReferenceSchema, value);
364
+ const resolved = await this.resolveCredentialReference(reference, source.required);
365
+ return { value: resolved, hostReference: resolved !== undefined, reference };
366
+ }
367
+ if (source.reference !== undefined && source.scope === undefined) {
368
+ const resolved = await this.resolveCredentialReference(source.reference, source.required);
369
+ return { value: resolved, hostReference: resolved !== undefined, reference: source.reference };
370
+ }
371
+ if (!this.store) {
372
+ if (source.required)
373
+ throw this.missingStoreError();
374
+ return { value: undefined, hostReference: false };
375
+ }
376
+ const scopedSource = this.scopedSource(source, orgId);
377
+ const reference = await this.store.loadCredential(this.descriptor.id, scopedSource);
378
+ if (!reference) {
379
+ const resolved = await this.resolveCredentialReference(source.reference, source.required);
380
+ return { value: resolved, hostReference: resolved !== undefined, reference: source.reference };
381
+ }
382
+ // Metadata proved the tenant secret exists. A value lookup failure now
383
+ // propagates instead of being confused with an absent saved credential.
384
+ return {
385
+ value: await this.resolveCredentialReference(reference, true),
386
+ hostReference: false,
387
+ reference
388
+ };
389
+ }
390
+ async resolveCredentialSet(sources) {
391
+ sources ??= await this.credentialSources();
392
+ const credentials = {};
393
+ const credentialReferences = {};
394
+ const hostCredentialKeys = new Set();
395
+ const needsOrg = sources.some((source) => source.source === 'secret'
396
+ && !(source.reference !== undefined && source.scope === undefined)
397
+ && (source.scope === undefined || source.scope === 'org'));
398
+ const orgId = needsOrg ? await this.getCurrentOrgId() : undefined;
399
+ for (const source of sources) {
400
+ if (Object.hasOwn(credentials, source.key))
401
+ continue;
402
+ const resolved = await this.resolveCredentialSource(source, orgId);
403
+ if (resolved.value !== undefined) {
404
+ credentials[source.key] = resolved.value;
405
+ if (resolved.reference)
406
+ credentialReferences[source.key] = [resolved.reference];
407
+ if (resolved.hostReference)
408
+ hostCredentialKeys.add(source.key);
409
+ continue;
410
+ }
411
+ if (source.required) {
412
+ throw new CapabilityError('A required integration credential is absent.', 'UNAVAILABLE', { reason: 'integration-credential-required', retryable: false });
413
+ }
414
+ }
415
+ return { credentials, credentialReferences, hostCredentialKeys };
416
+ }
417
+ async readSelectedConnection(load, selector, actor) {
418
+ const store = this.store;
419
+ if (!store) {
420
+ throw new CapabilityError('Saved connection selection is unavailable.', 'UNAVAILABLE', {
421
+ reason: 'integration-connection-selection-unsupported', retryable: false
422
+ });
423
+ }
424
+ return parseIntegrationBoundary(IntegrationProviderResolvedConfigurationSchema, await load.call(store, this.descriptor, selector, actor));
425
+ }
426
+ hostCredentialSources(sources) {
427
+ return sources.filter((source) => source.source === 'setting'
428
+ || (source.reference !== undefined && source.scope === undefined));
429
+ }
430
+ mergeCredentialReferences(host, selected) {
431
+ return {
432
+ ...selected,
433
+ ...Object.fromEntries(Object.entries(host).map(([key, candidates]) => {
434
+ const accountCandidates = Object.hasOwn(selected, key) ? selected[key] : [];
435
+ return [key, [...candidates, ...accountCandidates]];
436
+ }))
437
+ };
438
+ }
439
+ async resolveSelectedConnection(selected) {
440
+ const hostSources = this.hostCredentialSources(await this.credentialSources());
441
+ const host = await this.resolveCredentialSet(hostSources);
442
+ // A resolved host value wins; a confirmed-absent optional host source
443
+ // advances to the selected account, as reference delivery does.
444
+ return parseIntegrationBoundary(IntegrationProviderResolvedConfigurationSchema, {
445
+ config: this.protectHostEndpointConfiguration(selected.config, host.hostCredentialKeys),
446
+ credentials: { ...selected.credentials, ...host.credentials },
447
+ credentialReferences: this.mergeCredentialReferences(host.credentialReferences, selected.credentialReferences)
448
+ });
449
+ }
450
+ protectHostEndpointConfiguration(config, hostCredentialKeys) {
451
+ if (hostCredentialKeys.size === 0)
452
+ return config;
453
+ const protectedConfig = { ...config };
454
+ for (const key of new Set(['baseUrl', ...this.descriptor.protectedConfigKeys])) {
455
+ const declared = key === 'baseUrl' ? this.descriptor.baseUrl ?? this.descriptor.config[key] : this.descriptor.config[key];
456
+ if (declared === undefined)
457
+ delete protectedConfig[key];
458
+ else
459
+ protectedConfig[key] = declared;
460
+ }
461
+ return protectedConfig;
462
+ }
463
+ async resolveCredentialReference(reference, required) {
464
+ const runtime = this.context.runtime;
465
+ if (!runtime) {
466
+ throw new CapabilityError('Integration credentials require the caller runtime.', 'UNAVAILABLE', { reason: 'integration-runtime-required', retryable: false });
467
+ }
468
+ if (!reference)
469
+ return undefined;
470
+ const result = await runtime.resolveCredential(reference);
471
+ if (result.ok)
472
+ return result.value;
473
+ if (!required && result.error.code === 'NOT_FOUND' && result.error.reason === 'credential-not-set') {
474
+ return undefined;
475
+ }
476
+ throw result.error;
477
+ }
478
+ /**
479
+ * Build org-scoped scopeId for credential lookup.
480
+ * Combines the live request orgId with the descriptor-level scopeId.
481
+ * Format: {orgId}:{scopeId} when both present, {orgId} when only orgId.
482
+ */
483
+ resolveScopeId(descriptorScopeId, orgId) {
484
+ if (!orgId) {
485
+ throw new CapabilityError('Organization identity is required for integration access.', 'PERMISSION_DENIED', {
486
+ reason: 'integration-organization-required', retryable: false
487
+ });
488
+ }
489
+ if (!descriptorScopeId)
490
+ return orgId;
491
+ return `${orgId}:${descriptorScopeId}`;
492
+ }
493
+ /**
494
+ * Resolve the CALLING request's org id at call time. The integration
495
+ * provider instance is value-cached by the runtime for the isolate
496
+ * lifetime, so a constructor-snapshotted `this.context.orgId` would
497
+ * freeze to whichever org FIRST resolved the provider — every later
498
+ * request (any org) would then read and write that first org's
499
+ * credentials and config (cross-tenant bleed; the Microsoft pack fixed
500
+ * the same bug in ac19f2688). Read the live request context instead
501
+ * (`runtime.context('orgId')`, set by hosts via runWithRequestContext),
502
+ * falling back to the constructor-injected context orgId, then
503
+ * undefined (system/platform scope).
504
+ */
505
+ async getCurrentOrgId() {
506
+ const runtime = this.context.runtime;
507
+ if (runtime?.hasContext('orgId')) {
508
+ const value = parseIntegrationBoundary(IntegrationContextOrgIdSchema, await runtime.context('orgId'));
509
+ if (value !== undefined)
510
+ return value;
511
+ }
512
+ return parseIntegrationBoundary(IntegrationContextOrgIdSchema, this.context.orgId);
513
+ }
514
+ async getSettingValue(settingKey) {
515
+ const settingValue = await this.settingsReader.get(settingKey);
516
+ if (settingValue === undefined)
517
+ return undefined;
518
+ return parseIntegrationBoundary(IntegrationConfigValueSchema, settingValue);
519
+ }
520
+ }
521
+ function resolveIntegrationProvider(candidates, scope) {
522
+ const typedCandidates = candidates.map((candidate) => parseIntegrationBoundary(IntegrationProviderDescriptorSchema, candidate));
523
+ if (typedCandidates.length === 0)
524
+ return undefined;
525
+ const enabled = typedCandidates.filter((descriptor) => descriptor.enabled !== false);
526
+ const matching = enabled.filter((descriptor) => {
527
+ if (!descriptor.hardware || descriptor.hardware.length === 0)
528
+ return true;
529
+ return scope.hardware !== undefined && descriptor.hardware.includes(scope.hardware);
530
+ });
531
+ if (matching.length === 0)
532
+ return enabled[0] ?? typedCandidates[0];
533
+ return [...matching].sort((left, right) => (right.priority ?? 0) - (left.priority ?? 0))[0];
534
+ }
535
+ export const integrationProviderKind = {
536
+ id: 'integration/provider',
537
+ descriptorSchema: IntegrationProviderDescriptorSchema,
538
+ defaultImplementation: BaseIntegrationProvider,
539
+ resolve: resolveIntegrationProvider,
540
+ contexts: [integrationStoreContextId]
541
+ };
542
+ //# sourceMappingURL=integration.kind.js.map