@valon-technologies/gestalt 0.0.1-alpha.1 → 0.0.1-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/runtime.ts CHANGED
@@ -4,17 +4,22 @@ import { dirname, resolve } from "node:path";
4
4
 
5
5
  import { create } from "@bufbuild/protobuf";
6
6
  import { EmptySchema } from "@bufbuild/protobuf/wkt";
7
- import { Code, ConnectError, type ServiceImpl } from "@connectrpc/connect";
7
+ import {
8
+ Code,
9
+ ConnectError,
10
+ type ConnectRouter,
11
+ type ServiceImpl,
12
+ } from "@connectrpc/connect";
8
13
  import { connectNodeAdapter } from "@connectrpc/connect-node";
9
14
 
10
15
  import {
11
- AuthProvider as AuthProviderService,
16
+ AuthenticationProvider as AuthenticationProviderService,
12
17
  AuthSessionSettingsSchema,
13
18
  AuthenticatedUserSchema,
14
19
  BeginLoginResponseSchema,
15
20
  type CompleteLoginRequest as AuthCompleteLoginRequest,
16
21
  type ValidateExternalTokenRequest,
17
- } from "../gen/v1/auth_pb.ts";
22
+ } from "../gen/v1/authentication_pb.ts";
18
23
  import {
19
24
  Cache as CacheService,
20
25
  CacheDeleteManyResponseSchema,
@@ -53,23 +58,33 @@ import {
53
58
  type ConfigureProviderRequest,
54
59
  } from "../gen/v1/runtime_pb.ts";
55
60
  import { S3 as S3Service } from "../gen/v1/s3_pb.ts";
61
+ import { WorkflowProvider as WorkflowProviderService } from "../gen/v1/workflow_pb.ts";
56
62
  import { errorMessage, type Request } from "./api.ts";
57
63
  import {
58
- AuthProvider,
59
- isAuthProvider,
64
+ AuthenticationProvider,
65
+ isAuthenticationProvider,
60
66
  type AuthenticatedUser,
61
67
  } from "./auth.ts";
62
68
  import { CacheProvider, isCacheProvider } from "./cache.ts";
63
69
  import { SecretsProvider, isSecretsProvider } from "./secrets.ts";
64
70
  import { catalogToYaml, type Catalog } from "./catalog.ts";
65
71
  import {
66
- IntegrationProvider,
72
+ PluginProvider,
67
73
  connectionModeToProtoValue,
68
74
  connectionParamToProto,
69
- isIntegrationProvider,
75
+ isPluginProvider,
70
76
  } from "./plugin.ts";
77
+ import {
78
+ providerKindLabel,
79
+ resolveDefaultProviderExport,
80
+ } from "./provider-kind.ts";
71
81
  import { type ProviderKind, slugName } from "./provider.ts";
72
82
  import { S3Provider, createS3Service, isS3Provider } from "./s3.ts";
83
+ import {
84
+ WorkflowProvider,
85
+ createWorkflowProviderService,
86
+ isWorkflowProvider,
87
+ } from "./workflow.ts";
73
88
  import {
74
89
  defaultProviderName,
75
90
  formatProviderTarget,
@@ -79,24 +94,134 @@ import {
79
94
  resolveProviderImportUrl,
80
95
  } from "./target.ts";
81
96
 
97
+ /**
98
+ * Environment variable containing the Unix socket path for a running provider.
99
+ */
82
100
  export const ENV_PROVIDER_SOCKET = "GESTALT_PLUGIN_SOCKET";
101
+ /**
102
+ * Environment variable containing the parent process ID supplied by the host.
103
+ */
83
104
  export const ENV_PROVIDER_PARENT_PID = "GESTALT_PLUGIN_PARENT_PID";
105
+ /**
106
+ * Environment variable used to request static catalog generation.
107
+ */
84
108
  export const ENV_WRITE_CATALOG = "GESTALT_PLUGIN_WRITE_CATALOG";
85
- export const CURRENT_PROTOCOL_VERSION = 2;
109
+ /**
110
+ * Environment variable used to request generated manifest metadata export.
111
+ */
112
+ export const ENV_WRITE_MANIFEST_METADATA =
113
+ "GESTALT_PLUGIN_WRITE_MANIFEST_METADATA";
114
+ /**
115
+ * Protocol version currently implemented by the TypeScript runtime.
116
+ */
117
+ export const CURRENT_PROTOCOL_VERSION = 3;
118
+ /**
119
+ * Command-line usage for the runtime entrypoint.
120
+ */
86
121
  export const USAGE = "usage: bun run runtime.ts ROOT PROVIDER_TARGET";
122
+ export { createWorkflowProviderService } from "./workflow.ts";
87
123
 
124
+ /**
125
+ * Parsed arguments for the runtime entrypoint.
126
+ */
88
127
  export type RuntimeArgs = {
89
128
  root: string;
90
129
  target: string;
91
130
  };
92
131
 
132
+ /**
133
+ * Provider implementations supported by the runtime host.
134
+ */
93
135
  export type LoadedProvider =
94
- | IntegrationProvider
95
- | AuthProvider
136
+ | PluginProvider
137
+ | AuthenticationProvider
96
138
  | CacheProvider
97
139
  | SecretsProvider
98
- | S3Provider;
140
+ | S3Provider
141
+ | WorkflowProvider;
142
+
143
+ type ProviderRuntimeEntry = {
144
+ isProvider: (value: unknown) => value is LoadedProvider;
145
+ protoKind: ProtoProviderKind;
146
+ registerService: (router: ConnectRouter, provider: LoadedProvider) => void;
147
+ };
148
+
149
+ const PROVIDER_RUNTIME_ENTRIES: Partial<
150
+ Record<ProviderKind, ProviderRuntimeEntry>
151
+ > = {
152
+ integration: {
153
+ isProvider: isPluginProvider as (value: unknown) => value is LoadedProvider,
154
+ protoKind: ProtoProviderKind.INTEGRATION,
155
+ registerService(router, provider) {
156
+ router.service(
157
+ IntegrationProviderService,
158
+ createProviderService(provider as PluginProvider),
159
+ );
160
+ },
161
+ },
162
+ authentication: {
163
+ isProvider:
164
+ isAuthenticationProvider as (value: unknown) => value is LoadedProvider,
165
+ protoKind: ProtoProviderKind.AUTHENTICATION,
166
+ registerService(router, provider) {
167
+ router.service(
168
+ AuthenticationProviderService,
169
+ createAuthenticationService(provider as AuthenticationProvider),
170
+ );
171
+ },
172
+ },
173
+ cache: {
174
+ isProvider: isCacheProvider as (value: unknown) => value is LoadedProvider,
175
+ protoKind: ProtoProviderKind.CACHE,
176
+ registerService(router, provider) {
177
+ router.service(
178
+ CacheService,
179
+ createCacheService(provider as CacheProvider),
180
+ );
181
+ },
182
+ },
183
+ secrets: {
184
+ isProvider: isSecretsProvider as (value: unknown) => value is LoadedProvider,
185
+ protoKind: ProtoProviderKind.SECRETS,
186
+ registerService(router, provider) {
187
+ router.service(
188
+ SecretsProviderService,
189
+ createSecretsService(provider as SecretsProvider),
190
+ );
191
+ },
192
+ },
193
+ s3: {
194
+ isProvider: isS3Provider as (value: unknown) => value is LoadedProvider,
195
+ protoKind: ProtoProviderKind.S3,
196
+ registerService(router, provider) {
197
+ router.service(S3Service, createS3Service(provider as S3Provider));
198
+ },
199
+ },
200
+ workflow: {
201
+ isProvider: isWorkflowProvider as (value: unknown) => value is LoadedProvider,
202
+ protoKind: ProtoProviderKind.WORKFLOW,
203
+ registerService(router, provider) {
204
+ router.service(
205
+ WorkflowProviderService,
206
+ createWorkflowProviderService(provider as WorkflowProvider),
207
+ );
208
+ },
209
+ },
210
+ };
211
+
212
+ function assertProtocolVersion(protocolVersion: number): void {
213
+ if (protocolVersion === CURRENT_PROTOCOL_VERSION) {
214
+ return;
215
+ }
216
+ throw new ConnectError(
217
+ `host requested protocol version ${protocolVersion}, provider requires ${CURRENT_PROTOCOL_VERSION}`,
218
+ Code.FailedPrecondition,
219
+ );
220
+ }
99
221
 
222
+ /**
223
+ * CLI entrypoint that loads a provider from source and starts serving it.
224
+ */
100
225
  export async function main(
101
226
  argv: string[] = process.argv.slice(2),
102
227
  ): Promise<number> {
@@ -112,6 +237,9 @@ export async function main(
112
237
  return 0;
113
238
  }
114
239
 
240
+ /**
241
+ * Parses `gestalt-ts-runtime` CLI arguments.
242
+ */
115
243
  export function parseRuntimeArgs(argv: string[]): RuntimeArgs | undefined {
116
244
  if (argv.length !== 2) {
117
245
  return undefined;
@@ -122,87 +250,35 @@ export function parseRuntimeArgs(argv: string[]): RuntimeArgs | undefined {
122
250
  };
123
251
  }
124
252
 
253
+ /**
254
+ * Loads any supported provider kind from a package root and optional target.
255
+ */
125
256
  export async function loadProviderFromTarget(
126
257
  root: string,
127
258
  rawTarget?: string,
128
259
  ): Promise<LoadedProvider> {
129
260
  const config = readPackageConfig(root);
130
- const targetValue =
131
- rawTarget?.trim() ||
132
- formatProviderTarget(
133
- config.providerTarget ?? readPackageProviderTarget(root),
134
- );
135
- const target = parseProviderTarget(targetValue);
261
+ const explicitTarget = rawTarget?.trim();
262
+ const target = explicitTarget
263
+ ? parseProviderTarget(explicitTarget)
264
+ : config.providerTarget ?? readPackageProviderTarget(root);
265
+ const targetValue = explicitTarget || formatProviderTarget(target);
136
266
  const module = await import(resolveProviderImportUrl(root, target));
137
267
  const candidate =
138
- (target.exportName ? module[target.exportName] : undefined) ??
139
- defaultProviderExport(module, target.kind);
268
+ (target.exportName ? Reflect.get(module, target.exportName) : undefined) ??
269
+ resolveDefaultProviderExport(module, target.kind);
140
270
 
141
271
  const defaultName =
142
272
  slugName(config.name ?? "") ||
143
273
  slugName(dirname(resolve(root, target.modulePath)));
144
- switch (target.kind) {
145
- case "integration": {
146
- if (!isIntegrationProvider(candidate)) {
147
- throw new Error(
148
- `${targetValue} did not resolve to a Gestalt integration provider`,
149
- );
150
- }
151
- candidate.resolveName(defaultName);
152
- return candidate;
153
- }
154
- case "auth": {
155
- if (!isAuthProvider(candidate)) {
156
- throw new Error(
157
- `${targetValue} did not resolve to a Gestalt auth provider`,
158
- );
159
- }
160
- candidate.resolveName(defaultName);
161
- return candidate;
162
- }
163
- case "cache": {
164
- if (!isCacheProvider(candidate)) {
165
- throw new Error(
166
- `${targetValue} did not resolve to a Gestalt cache provider`,
167
- );
168
- }
169
- candidate.resolveName(defaultName);
170
- return candidate;
171
- }
172
- case "secrets": {
173
- if (!isSecretsProvider(candidate)) {
174
- throw new Error(
175
- `${targetValue} did not resolve to a Gestalt secrets provider`,
176
- );
177
- }
178
- candidate.resolveName(defaultName);
179
- return candidate;
180
- }
181
- case "s3": {
182
- if (!isS3Provider(candidate)) {
183
- throw new Error(`${targetValue} did not resolve to a Gestalt s3 provider`);
184
- }
185
- candidate.resolveName(defaultName);
186
- return candidate;
187
- }
188
- default:
189
- throw new Error(
190
- `TypeScript SDK does not yet support provider kind ${JSON.stringify(target.kind)}`,
191
- );
192
- }
193
- }
194
-
195
- export async function loadPluginFromTarget(
196
- root: string,
197
- rawTarget?: string,
198
- ): Promise<IntegrationProvider> {
199
- const provider = await loadProviderFromTarget(root, rawTarget);
200
- if (!isIntegrationProvider(provider)) {
201
- throw new Error("target did not resolve to an integration provider");
202
- }
274
+ const provider = resolveLoadedProvider(candidate, target.kind, targetValue);
275
+ provider.resolveName(defaultName);
203
276
  return provider;
204
277
  }
205
278
 
279
+ /**
280
+ * Runs a provider that has already been loaded into memory.
281
+ */
206
282
  export async function runLoadedProvider(
207
283
  provider: LoadedProvider,
208
284
  options: {
@@ -217,102 +293,41 @@ export async function runLoadedProvider(
217
293
  }
218
294
 
219
295
  const catalogPath = process.env[ENV_WRITE_CATALOG];
220
- if (catalogPath) {
221
- if (!isIntegrationProvider(provider)) {
296
+ const manifestMetadataPath = process.env[ENV_WRITE_MANIFEST_METADATA];
297
+ if (catalogPath || manifestMetadataPath) {
298
+ if (!isPluginProvider(provider)) {
222
299
  throw new Error(
223
- "static catalog generation is only supported for integration providers",
300
+ "static catalog and manifest metadata generation are only supported for plugin providers",
224
301
  );
225
302
  }
226
- writeFileSync(catalogPath, pluginCatalogYaml(provider), "utf8");
303
+ if (catalogPath) {
304
+ writeFileSync(catalogPath, catalogToYaml(provider.staticCatalog()), "utf8");
305
+ }
306
+ if (manifestMetadataPath && provider.supportsManifestMetadata()) {
307
+ provider.writeManifestMetadata(manifestMetadataPath);
308
+ }
227
309
  return;
228
310
  }
229
311
 
230
312
  await serve(provider);
231
313
  }
232
314
 
233
- export async function runLoadedPlugin(
234
- plugin: IntegrationProvider,
235
- options: {
236
- root?: string;
237
- pluginName?: string;
238
- } = {},
239
- ): Promise<void> {
240
- const runtimeOptions: {
241
- root?: string;
242
- providerName?: string;
243
- } = {};
244
- if (options.root !== undefined) {
245
- runtimeOptions.root = options.root;
246
- }
247
- if (options.pluginName !== undefined) {
248
- runtimeOptions.providerName = options.pluginName;
249
- }
250
- await runLoadedProvider(plugin, runtimeOptions);
251
- }
252
-
315
+ /**
316
+ * Runs a bundled provider export after validating its provider kind.
317
+ */
253
318
  export async function runBundledProvider(
254
319
  provider: unknown,
255
320
  kind: ProviderKind,
256
321
  providerName: string,
257
322
  ): Promise<void> {
258
- let loaded: LoadedProvider;
259
- switch (kind) {
260
- case "integration":
261
- if (!isIntegrationProvider(provider)) {
262
- throw new Error(
263
- "bundled target did not resolve to a Gestalt integration provider",
264
- );
265
- }
266
- loaded = provider;
267
- break;
268
- case "auth":
269
- if (!isAuthProvider(provider)) {
270
- throw new Error(
271
- "bundled target did not resolve to a Gestalt auth provider",
272
- );
273
- }
274
- loaded = provider;
275
- break;
276
- case "cache":
277
- if (!isCacheProvider(provider)) {
278
- throw new Error(
279
- "bundled target did not resolve to a Gestalt cache provider",
280
- );
281
- }
282
- loaded = provider;
283
- break;
284
- case "secrets":
285
- if (!isSecretsProvider(provider)) {
286
- throw new Error(
287
- "bundled target did not resolve to a Gestalt secrets provider",
288
- );
289
- }
290
- loaded = provider;
291
- break;
292
- case "s3":
293
- if (!isS3Provider(provider)) {
294
- throw new Error("bundled target did not resolve to a Gestalt s3 provider");
295
- }
296
- loaded = provider;
297
- break;
298
- default:
299
- throw new Error(
300
- `TypeScript SDK does not yet support provider kind ${JSON.stringify(kind)}`,
301
- );
302
- }
303
- loaded.name = slugName(providerName);
304
- await runLoadedProvider(loaded, {
323
+ await runLoadedProvider(resolveLoadedProvider(provider, kind, "bundled target"), {
305
324
  providerName,
306
325
  });
307
326
  }
308
327
 
309
- export async function runBundledPlugin(
310
- plugin: unknown,
311
- pluginName: string,
312
- ): Promise<void> {
313
- await runBundledProvider(plugin, "integration", pluginName);
314
- }
315
-
328
+ /**
329
+ * Starts serving a provider over the Gestalt Unix socket transport.
330
+ */
316
331
  export async function serve(provider: LoadedProvider): Promise<void> {
317
332
  const socketPath = process.env[ENV_PROVIDER_SOCKET];
318
333
  if (!socketPath) {
@@ -328,20 +343,7 @@ export async function serve(provider: LoadedProvider): Promise<void> {
328
343
  connect: false,
329
344
  routes(router) {
330
345
  router.service(ProviderLifecycle, createRuntimeService(provider));
331
- if (isIntegrationProvider(provider)) {
332
- router.service(
333
- IntegrationProviderService,
334
- createProviderService(provider),
335
- );
336
- } else if (isAuthProvider(provider)) {
337
- router.service(AuthProviderService, createAuthService(provider));
338
- } else if (isCacheProvider(provider)) {
339
- router.service(CacheService, createCacheService(provider));
340
- } else if (isS3Provider(provider)) {
341
- router.service(S3Service, createS3Service(provider));
342
- } else if (isSecretsProvider(provider)) {
343
- router.service(SecretsProviderService, createSecretsService(provider));
344
- }
346
+ registerProviderService(router, provider);
345
347
  },
346
348
  });
347
349
 
@@ -392,13 +394,18 @@ export async function serve(provider: LoadedProvider): Promise<void> {
392
394
  }
393
395
  }
394
396
 
397
+ /**
398
+ * Adapts the provider lifecycle service used during startup and health checks.
399
+ *
400
+ * @internal
401
+ */
395
402
  export function createRuntimeService(
396
403
  provider: LoadedProvider,
397
404
  ): Partial<ServiceImpl<typeof ProviderLifecycle>> {
398
405
  return {
399
406
  async getProviderIdentity() {
400
407
  return create(ProviderIdentitySchema, {
401
- kind: providerKindToProto(provider.kind),
408
+ kind: providerRuntimeEntry(provider.kind).protoKind,
402
409
  name: provider.name,
403
410
  displayName: provider.displayName,
404
411
  description: provider.description,
@@ -409,12 +416,7 @@ export function createRuntimeService(
409
416
  });
410
417
  },
411
418
  async configureProvider(request: ConfigureProviderRequest) {
412
- if (request.protocolVersion !== CURRENT_PROTOCOL_VERSION) {
413
- throw new ConnectError(
414
- `host requested protocol version ${request.protocolVersion}, provider requires ${CURRENT_PROTOCOL_VERSION}`,
415
- Code.FailedPrecondition,
416
- );
417
- }
419
+ assertProtocolVersion(request.protocolVersion);
418
420
  try {
419
421
  await provider.configureProvider(
420
422
  request.name,
@@ -451,9 +453,17 @@ export function createRuntimeService(
451
453
  };
452
454
  }
453
455
 
456
+ /**
457
+ * Adapts a plugin provider to the shared protocol service implementation.
458
+ *
459
+ * @internal
460
+ */
454
461
  export function createProviderService(
455
- provider: IntegrationProvider,
462
+ provider: LoadedProvider,
456
463
  ): Partial<ServiceImpl<typeof IntegrationProviderService>> {
464
+ if (!isPluginProvider(provider)) {
465
+ throw new Error("provider is not a plugin provider");
466
+ }
457
467
  return {
458
468
  getMetadata() {
459
469
  return create(ProviderMetadataSchema, {
@@ -478,6 +488,7 @@ export function createProviderService(
478
488
  });
479
489
  },
480
490
  async startProvider(request: StartProviderRequest) {
491
+ assertProtocolVersion(request.protocolVersion);
481
492
  try {
482
493
  await provider.configureProvider(
483
494
  request.name,
@@ -503,6 +514,7 @@ export function createProviderService(
503
514
  request.token,
504
515
  request.connectionParams,
505
516
  request.context,
517
+ request.invocationToken,
506
518
  ),
507
519
  ),
508
520
  );
@@ -542,9 +554,15 @@ export function createProviderService(
542
554
  };
543
555
  }
544
556
 
545
- export function createAuthService(
546
- provider: AuthProvider,
547
- ): Partial<ServiceImpl<typeof AuthProviderService>> {
557
+ /**
558
+ * Adapts an authentication provider to the shared protocol service
559
+ * implementation.
560
+ *
561
+ * @internal
562
+ */
563
+ export function createAuthenticationService(
564
+ provider: AuthenticationProvider,
565
+ ): Partial<ServiceImpl<typeof AuthenticationProviderService>> {
548
566
  return {
549
567
  async beginLogin(request) {
550
568
  const response = await provider.beginLogin({
@@ -557,7 +575,7 @@ export function createAuthService(
557
575
  });
558
576
  if (!response) {
559
577
  throw new ConnectError(
560
- "auth provider returned nil response",
578
+ "authentication provider returned nil response",
561
579
  Code.Internal,
562
580
  );
563
581
  }
@@ -576,7 +594,7 @@ export function createAuthService(
576
594
  });
577
595
  if (!user) {
578
596
  throw new ConnectError(
579
- "auth provider returned nil user",
597
+ "authentication provider returned nil user",
580
598
  Code.Internal,
581
599
  );
582
600
  }
@@ -585,7 +603,7 @@ export function createAuthService(
585
603
  async validateExternalToken(request: ValidateExternalTokenRequest) {
586
604
  if (!provider.supportsExternalTokenValidation()) {
587
605
  throw new ConnectError(
588
- "auth provider does not support external token validation",
606
+ "authentication provider does not support external token validation",
589
607
  Code.Unimplemented,
590
608
  );
591
609
  }
@@ -598,7 +616,7 @@ export function createAuthService(
598
616
  async getSessionSettings() {
599
617
  if (!provider.supportsSessionSettings()) {
600
618
  throw new ConnectError(
601
- "auth provider does not expose session settings",
619
+ "authentication provider does not expose session settings",
602
620
  Code.Unimplemented,
603
621
  );
604
622
  }
@@ -610,6 +628,11 @@ export function createAuthService(
610
628
  };
611
629
  }
612
630
 
631
+ /**
632
+ * Adapts a cache provider to the shared protocol service implementation.
633
+ *
634
+ * @internal
635
+ */
613
636
  export function createCacheService(
614
637
  provider: CacheProvider,
615
638
  ): Partial<ServiceImpl<typeof CacheService>> {
@@ -674,6 +697,11 @@ export function createCacheService(
674
697
  };
675
698
  }
676
699
 
700
+ /**
701
+ * Adapts a secrets provider to the shared protocol service implementation.
702
+ *
703
+ * @internal
704
+ */
677
705
  export function createSecretsService(
678
706
  provider: SecretsProvider,
679
707
  ): Partial<ServiceImpl<typeof SecretsProviderService>> {
@@ -687,14 +715,11 @@ export function createSecretsService(
687
715
  };
688
716
  }
689
717
 
690
- export function pluginCatalogYaml(plugin: IntegrationProvider): string {
691
- return catalogToYaml(plugin.staticCatalog());
692
- }
693
-
694
718
  function providerRequest(
695
719
  token: string,
696
720
  connectionParams: Record<string, string>,
697
721
  requestContext?: ProtoRequestContext,
722
+ invocationToken = "",
698
723
  ): Request {
699
724
  const subject = requestContext?.subject;
700
725
  const credential = requestContext?.credential;
@@ -720,43 +745,44 @@ function providerRequest(
720
745
  policy: access?.policy ?? "",
721
746
  role: access?.role ?? "",
722
747
  },
748
+ workflow: {
749
+ ...(requestContext?.workflow ?? {}),
750
+ },
751
+ invocationToken,
723
752
  };
724
753
  }
725
754
 
726
- function providerKindToProto(kind: ProviderKind): ProtoProviderKind {
727
- switch (kind) {
728
- case "integration":
729
- return ProtoProviderKind.INTEGRATION;
730
- case "auth":
731
- return ProtoProviderKind.AUTH;
732
- case "cache":
733
- return ProtoProviderKind.CACHE;
734
- case "secrets":
735
- return ProtoProviderKind.SECRETS;
736
- case "s3":
737
- return ProtoProviderKind.S3;
738
- case "telemetry":
739
- return ProtoProviderKind.TELEMETRY;
740
- default:
741
- return ProtoProviderKind.UNSPECIFIED;
755
+ function providerRuntimeEntry(
756
+ kind: ProviderKind,
757
+ ): ProviderRuntimeEntry {
758
+ const entry = PROVIDER_RUNTIME_ENTRIES[kind];
759
+ if (!entry) {
760
+ throw new Error(
761
+ `TypeScript SDK does not yet support provider kind ${JSON.stringify(kind)}`,
762
+ );
742
763
  }
764
+ return entry;
743
765
  }
744
766
 
745
- function defaultProviderExport(module: Record<string, unknown>, kind: ProviderKind): unknown {
746
- switch (kind) {
747
- case "integration":
748
- return module.provider ?? module.plugin ?? module.default;
749
- case "auth":
750
- return module.auth ?? module.provider ?? module.default;
751
- case "cache":
752
- return module.cache ?? module.provider ?? module.default;
753
- case "secrets":
754
- return module.secrets ?? module.provider ?? module.default;
755
- case "s3":
756
- return module.s3 ?? module.provider ?? module.default;
757
- case "telemetry":
758
- return module.telemetry ?? module.provider ?? module.default;
767
+ function resolveLoadedProvider(
768
+ candidate: unknown,
769
+ kind: ProviderKind,
770
+ source: string,
771
+ ): LoadedProvider {
772
+ const entry = providerRuntimeEntry(kind);
773
+ if (!entry.isProvider(candidate)) {
774
+ throw new Error(
775
+ `${source} did not resolve to a Gestalt ${providerKindLabel(kind)}`,
776
+ );
759
777
  }
778
+ return candidate;
779
+ }
780
+
781
+ function registerProviderService(
782
+ router: ConnectRouter,
783
+ provider: LoadedProvider,
784
+ ): void {
785
+ providerRuntimeEntry(provider.kind).registerService(router, provider);
760
786
  }
761
787
 
762
788
  function objectFromUnknown(value: unknown): Record<string, unknown> {