@valon-technologies/gestalt 0.0.1-alpha.8 → 0.0.1-alpha.9
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/README.md +8 -10
- package/gen/v1/{auth_pb.ts → authentication_pb.ts} +51 -17
- package/gen/v1/authorization_pb.ts +857 -0
- package/gen/v1/cache_pb.ts +32 -0
- package/gen/v1/datastore_pb.ts +62 -0
- package/gen/v1/plugin_pb.ts +216 -18
- package/gen/v1/runtime_pb.ts +27 -3
- package/gen/v1/s3_pb.ts +39 -0
- package/gen/v1/secrets_pb.ts +6 -0
- package/gen/v1/workflow_pb.ts +1372 -0
- package/package.json +9 -1
- package/src/api.ts +56 -0
- package/src/auth.ts +67 -16
- package/src/build.ts +37 -21
- package/src/cache.ts +32 -0
- package/src/catalog.ts +27 -0
- package/src/index.ts +87 -18
- package/src/indexeddb.ts +166 -0
- package/src/invoker.ts +124 -0
- package/src/plugin.ts +93 -38
- package/src/provider-kind.ts +107 -0
- package/src/provider.ts +32 -1
- package/src/runtime.ts +233 -218
- package/src/s3.ts +135 -20
- package/src/schema.ts +46 -0
- package/src/secrets.ts +12 -0
- package/src/target.ts +58 -60
- package/src/workflow-manager.ts +131 -0
- package/src/workflow.ts +479 -0
- package/tsconfig.json +1 -0
package/src/invoker.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { connect } from "node:net";
|
|
2
|
+
|
|
3
|
+
import type { JsonObject, JsonValue } from "@bufbuild/protobuf";
|
|
4
|
+
import { createClient, type Client } from "@connectrpc/connect";
|
|
5
|
+
import { createGrpcTransport } from "@connectrpc/connect-node";
|
|
6
|
+
|
|
7
|
+
import { PluginInvoker as PluginInvokerService } from "../gen/v1/plugin_pb.ts";
|
|
8
|
+
import type { OperationResult, Request } from "./api.ts";
|
|
9
|
+
|
|
10
|
+
export const ENV_PLUGIN_INVOKER_SOCKET = "GESTALT_PLUGIN_INVOKER_SOCKET";
|
|
11
|
+
|
|
12
|
+
export interface PluginInvokeOptions {
|
|
13
|
+
connection?: string;
|
|
14
|
+
instance?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface PluginInvocationGrant {
|
|
18
|
+
plugin: string;
|
|
19
|
+
operations: string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class PluginInvoker {
|
|
23
|
+
private readonly client: Client<typeof PluginInvokerService>;
|
|
24
|
+
private readonly invocationToken: string;
|
|
25
|
+
|
|
26
|
+
constructor(request: Request);
|
|
27
|
+
constructor(invocationToken: string);
|
|
28
|
+
constructor(requestOrToken: Request | string) {
|
|
29
|
+
this.invocationToken = normalizeInvocationToken(requestOrToken);
|
|
30
|
+
|
|
31
|
+
const socketPath = process.env[ENV_PLUGIN_INVOKER_SOCKET];
|
|
32
|
+
if (!socketPath) {
|
|
33
|
+
throw new Error(`plugin invoker: ${ENV_PLUGIN_INVOKER_SOCKET} is not set`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const transport = createGrpcTransport({
|
|
37
|
+
baseUrl: "http://localhost",
|
|
38
|
+
nodeOptions: {
|
|
39
|
+
createConnection: () => connect(socketPath),
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
this.client = createClient(PluginInvokerService, transport);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async invoke(
|
|
46
|
+
plugin: string,
|
|
47
|
+
operation: string,
|
|
48
|
+
params: Record<string, unknown> = {},
|
|
49
|
+
options?: PluginInvokeOptions,
|
|
50
|
+
): Promise<OperationResult> {
|
|
51
|
+
const response = await this.client.invoke({
|
|
52
|
+
invocationToken: this.invocationToken,
|
|
53
|
+
plugin,
|
|
54
|
+
operation,
|
|
55
|
+
params: toJsonObject(params),
|
|
56
|
+
connection: options?.connection ?? "",
|
|
57
|
+
instance: options?.instance ?? "",
|
|
58
|
+
});
|
|
59
|
+
return {
|
|
60
|
+
status: response.status,
|
|
61
|
+
body: response.body,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async exchangeInvocationToken(options?: {
|
|
66
|
+
grants?: PluginInvocationGrant[];
|
|
67
|
+
ttlSeconds?: number;
|
|
68
|
+
}): Promise<string> {
|
|
69
|
+
const response = await this.client.exchangeInvocationToken({
|
|
70
|
+
parentInvocationToken: this.invocationToken,
|
|
71
|
+
grants: (options?.grants ?? [])
|
|
72
|
+
.map((grant) => ({
|
|
73
|
+
plugin: grant.plugin.trim(),
|
|
74
|
+
operations: grant.operations
|
|
75
|
+
.map((operation) => operation.trim())
|
|
76
|
+
.filter(Boolean),
|
|
77
|
+
}))
|
|
78
|
+
.filter((grant) => grant.plugin.length > 0),
|
|
79
|
+
ttlSeconds: BigInt(Math.max(0, options?.ttlSeconds ?? 0)),
|
|
80
|
+
});
|
|
81
|
+
return response.invocationToken;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function normalizeInvocationToken(requestOrToken: Request | string): string {
|
|
86
|
+
const invocationToken =
|
|
87
|
+
typeof requestOrToken === "string"
|
|
88
|
+
? requestOrToken
|
|
89
|
+
: requestOrToken.invocationToken;
|
|
90
|
+
const trimmed = invocationToken.trim();
|
|
91
|
+
if (!trimmed) {
|
|
92
|
+
throw new Error("plugin invoker: invocation token is not available");
|
|
93
|
+
}
|
|
94
|
+
return trimmed;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function toJsonObject(params: Record<string, unknown>): JsonObject {
|
|
98
|
+
const output: JsonObject = {};
|
|
99
|
+
for (const [key, value] of Object.entries(params ?? {})) {
|
|
100
|
+
if (value === undefined) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
output[key] = toJsonValue(value);
|
|
104
|
+
}
|
|
105
|
+
return output;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function toJsonValue(value: unknown): JsonValue {
|
|
109
|
+
if (
|
|
110
|
+
value === null ||
|
|
111
|
+
typeof value === "string" ||
|
|
112
|
+
typeof value === "number" ||
|
|
113
|
+
typeof value === "boolean"
|
|
114
|
+
) {
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
if (Array.isArray(value)) {
|
|
118
|
+
return value.map((entry) => toJsonValue(entry));
|
|
119
|
+
}
|
|
120
|
+
if (typeof value === "object") {
|
|
121
|
+
return toJsonObject(value as Record<string, unknown>);
|
|
122
|
+
}
|
|
123
|
+
throw new Error("plugin invoker: params must be JSON-serializable");
|
|
124
|
+
}
|
package/src/plugin.ts
CHANGED
|
@@ -18,13 +18,18 @@ import {
|
|
|
18
18
|
import { RuntimeProvider, type RuntimeProviderOptions } from "./provider.ts";
|
|
19
19
|
import type { Schema } from "./schema.ts";
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* How a plugin provider expects to authenticate or connect.
|
|
23
|
+
*/
|
|
21
24
|
export type ConnectionMode =
|
|
22
25
|
| "unspecified"
|
|
23
26
|
| "none"
|
|
24
27
|
| "user"
|
|
25
|
-
| "identity"
|
|
26
|
-
| "either";
|
|
28
|
+
| "identity";
|
|
27
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Metadata for a single connection parameter exposed by a provider.
|
|
32
|
+
*/
|
|
28
33
|
export interface ConnectionParamDefinition {
|
|
29
34
|
required?: boolean;
|
|
30
35
|
description?: string;
|
|
@@ -33,6 +38,9 @@ export interface ConnectionParamDefinition {
|
|
|
33
38
|
field?: string;
|
|
34
39
|
}
|
|
35
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Operation definition accepted by {@link operation} and {@link definePlugin}.
|
|
43
|
+
*/
|
|
36
44
|
export interface OperationOptions<In, Out> {
|
|
37
45
|
id: string;
|
|
38
46
|
method?: string;
|
|
@@ -47,16 +55,29 @@ export interface OperationOptions<In, Out> {
|
|
|
47
55
|
handler: (input: In, request: Request) => MaybePromise<Out | Response<Out>>;
|
|
48
56
|
}
|
|
49
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Normalized plugin operation definition.
|
|
60
|
+
*/
|
|
50
61
|
export interface OperationDefinition<In, Out> extends OperationOptions<
|
|
51
62
|
In,
|
|
52
63
|
Out
|
|
53
64
|
> {}
|
|
54
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Session-specific catalog payload returned by a provider at runtime.
|
|
68
|
+
*/
|
|
55
69
|
export type SessionCatalog = Catalog | Record<string, unknown>;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Callback used to resolve a catalog for an authenticated request context.
|
|
73
|
+
*/
|
|
56
74
|
export type SessionCatalogHandler = (
|
|
57
75
|
request: Request,
|
|
58
76
|
) => MaybePromise<SessionCatalog | null | undefined>;
|
|
59
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Runtime hooks required to implement a plugin provider.
|
|
80
|
+
*/
|
|
60
81
|
export interface PluginDefinitionOptions extends RuntimeProviderOptions {
|
|
61
82
|
connectionMode?: ConnectionMode;
|
|
62
83
|
authTypes?: string[];
|
|
@@ -66,8 +87,9 @@ export interface PluginDefinitionOptions extends RuntimeProviderOptions {
|
|
|
66
87
|
sessionCatalog?: SessionCatalogHandler;
|
|
67
88
|
}
|
|
68
89
|
|
|
69
|
-
|
|
70
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Normalizes a plugin operation definition.
|
|
92
|
+
*/
|
|
71
93
|
export function operation<In, Out>(
|
|
72
94
|
options: OperationOptions<In, Out>,
|
|
73
95
|
): OperationDefinition<In, Out> {
|
|
@@ -82,7 +104,31 @@ export function operation<In, Out>(
|
|
|
82
104
|
};
|
|
83
105
|
}
|
|
84
106
|
|
|
85
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Plugin provider implementation consumed by the Gestalt runtime.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* import { definePlugin, ok, operation, s } from "@valon-technologies/gestalt";
|
|
113
|
+
*
|
|
114
|
+
* export const plugin = definePlugin({
|
|
115
|
+
* displayName: "Example Provider",
|
|
116
|
+
* operations: [
|
|
117
|
+
* operation({
|
|
118
|
+
* id: "ping",
|
|
119
|
+
* method: "GET",
|
|
120
|
+
* readOnly: true,
|
|
121
|
+
* input: s.object({ name: s.string({ default: "World" }) }),
|
|
122
|
+
* output: s.object({ message: s.string() }),
|
|
123
|
+
* async handler(input) {
|
|
124
|
+
* return ok({ message: `Hello, ${input.name}` });
|
|
125
|
+
* },
|
|
126
|
+
* }),
|
|
127
|
+
* ],
|
|
128
|
+
* });
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
export class PluginProvider extends RuntimeProvider {
|
|
86
132
|
readonly kind = "integration" as const;
|
|
87
133
|
readonly iconSvg: string;
|
|
88
134
|
readonly connectionMode: ConnectionMode;
|
|
@@ -90,10 +136,7 @@ export class IntegrationProvider extends RuntimeProvider {
|
|
|
90
136
|
readonly connectionParams: Record<string, ConnectionParamDefinition>;
|
|
91
137
|
|
|
92
138
|
private readonly sessionCatalogHandler: SessionCatalogHandler | undefined;
|
|
93
|
-
private readonly operations = new Map<
|
|
94
|
-
string,
|
|
95
|
-
OperationDefinition<any, any>
|
|
96
|
-
>();
|
|
139
|
+
private readonly operations = new Map<string, OperationDefinition<any, any>>();
|
|
97
140
|
|
|
98
141
|
constructor(options: PluginDefinitionOptions) {
|
|
99
142
|
super(options);
|
|
@@ -103,36 +146,37 @@ export class IntegrationProvider extends RuntimeProvider {
|
|
|
103
146
|
this.connectionParams = normalizeConnectionParams(options.connectionParams);
|
|
104
147
|
this.sessionCatalogHandler = options.sessionCatalog;
|
|
105
148
|
|
|
106
|
-
for (const
|
|
107
|
-
const
|
|
108
|
-
if (!id) {
|
|
149
|
+
for (const rawEntry of options.operations) {
|
|
150
|
+
const entry = operation(rawEntry);
|
|
151
|
+
if (!entry.id) {
|
|
109
152
|
throw new Error("operation id is required");
|
|
110
153
|
}
|
|
111
|
-
if (this.operations.has(id)) {
|
|
112
|
-
throw new Error(`duplicate operation id ${JSON.stringify(id)}`);
|
|
154
|
+
if (this.operations.has(entry.id)) {
|
|
155
|
+
throw new Error(`duplicate operation id ${JSON.stringify(entry.id)}`);
|
|
113
156
|
}
|
|
114
|
-
this.operations.set(id,
|
|
115
|
-
...entry,
|
|
116
|
-
id,
|
|
117
|
-
method: normalizeMethod(entry.method),
|
|
118
|
-
title: entry.title?.trim() ?? "",
|
|
119
|
-
description: entry.description?.trim() ?? "",
|
|
120
|
-
allowedRoles: normalizeAllowedRoles(entry.allowedRoles),
|
|
121
|
-
tags: [...(entry.tags ?? [])],
|
|
122
|
-
});
|
|
157
|
+
this.operations.set(entry.id, entry);
|
|
123
158
|
}
|
|
124
159
|
}
|
|
125
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Reports whether the provider exposes a session-specific catalog.
|
|
163
|
+
*/
|
|
126
164
|
supportsSessionCatalog(): boolean {
|
|
127
165
|
return this.sessionCatalogHandler !== undefined;
|
|
128
166
|
}
|
|
129
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Resolves a catalog for the current request context, if configured.
|
|
170
|
+
*/
|
|
130
171
|
async catalogForRequest(
|
|
131
172
|
request: Request,
|
|
132
173
|
): Promise<SessionCatalog | null | undefined> {
|
|
133
174
|
return await this.sessionCatalogHandler?.(request);
|
|
134
175
|
}
|
|
135
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Returns the static catalog emitted during provider startup.
|
|
179
|
+
*/
|
|
136
180
|
staticCatalog(): Catalog {
|
|
137
181
|
const catalog: Catalog = {
|
|
138
182
|
operations: [...this.operations.values()].map<CatalogOperation>(
|
|
@@ -197,14 +241,23 @@ export class IntegrationProvider extends RuntimeProvider {
|
|
|
197
241
|
return catalog;
|
|
198
242
|
}
|
|
199
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Writes the provider's static catalog to disk as YAML.
|
|
246
|
+
*/
|
|
200
247
|
writeCatalog(path: string): void {
|
|
201
248
|
writeCatalogYaml(path, this.staticCatalog());
|
|
202
249
|
}
|
|
203
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Returns the static catalog serialized as JSON.
|
|
253
|
+
*/
|
|
204
254
|
catalogJson(): string {
|
|
205
255
|
return catalogToJson(this.staticCatalog());
|
|
206
256
|
}
|
|
207
257
|
|
|
258
|
+
/**
|
|
259
|
+
* Executes an operation against validated input and request metadata.
|
|
260
|
+
*/
|
|
208
261
|
async execute(
|
|
209
262
|
operationId: string,
|
|
210
263
|
params: Record<string, unknown>,
|
|
@@ -244,25 +297,23 @@ export class IntegrationProvider extends RuntimeProvider {
|
|
|
244
297
|
}
|
|
245
298
|
}
|
|
246
299
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
options: PluginDefinitionOptions,
|
|
251
|
-
): IntegrationProvider {
|
|
252
|
-
return new IntegrationProvider(options);
|
|
253
|
-
}
|
|
254
|
-
|
|
300
|
+
/**
|
|
301
|
+
* Creates a plugin provider.
|
|
302
|
+
*/
|
|
255
303
|
export function definePlugin(
|
|
256
304
|
options: PluginDefinitionOptions,
|
|
257
|
-
):
|
|
258
|
-
return new
|
|
305
|
+
): PluginProvider {
|
|
306
|
+
return new PluginProvider(options);
|
|
259
307
|
}
|
|
260
308
|
|
|
261
|
-
|
|
309
|
+
/**
|
|
310
|
+
* Runtime type guard for plugin providers loaded from user modules.
|
|
311
|
+
*/
|
|
312
|
+
export function isPluginProvider(
|
|
262
313
|
value: unknown,
|
|
263
|
-
): value is
|
|
314
|
+
): value is PluginProvider {
|
|
264
315
|
return (
|
|
265
|
-
value instanceof
|
|
316
|
+
value instanceof PluginProvider ||
|
|
266
317
|
(typeof value === "object" &&
|
|
267
318
|
value !== null &&
|
|
268
319
|
"kind" in value &&
|
|
@@ -353,6 +404,9 @@ function errorResult(status: number, message: string): OperationResult {
|
|
|
353
404
|
};
|
|
354
405
|
}
|
|
355
406
|
|
|
407
|
+
/**
|
|
408
|
+
* Converts a connection mode into the shared protocol enum value.
|
|
409
|
+
*/
|
|
356
410
|
export function connectionModeToProtoValue(mode: ConnectionMode): number {
|
|
357
411
|
switch (mode) {
|
|
358
412
|
case "none":
|
|
@@ -361,14 +415,15 @@ export function connectionModeToProtoValue(mode: ConnectionMode): number {
|
|
|
361
415
|
return 2;
|
|
362
416
|
case "identity":
|
|
363
417
|
return 3;
|
|
364
|
-
case "either":
|
|
365
|
-
return 4;
|
|
366
418
|
case "unspecified":
|
|
367
419
|
default:
|
|
368
420
|
return 0;
|
|
369
421
|
}
|
|
370
422
|
}
|
|
371
423
|
|
|
424
|
+
/**
|
|
425
|
+
* Converts a connection parameter definition into protocol wire metadata.
|
|
426
|
+
*/
|
|
372
427
|
export function connectionParamToProto(value: ConnectionParamDefinition): {
|
|
373
428
|
required?: boolean;
|
|
374
429
|
description?: string;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { ProviderKind } from "./provider.ts";
|
|
2
|
+
|
|
3
|
+
type ProviderKindDefinition = {
|
|
4
|
+
readonly tokens: readonly string[];
|
|
5
|
+
readonly formatToken: string;
|
|
6
|
+
readonly defaultExportNames: readonly string[];
|
|
7
|
+
readonly label: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const PROVIDER_KIND_DEFINITIONS = {
|
|
11
|
+
integration: {
|
|
12
|
+
tokens: ["plugin"],
|
|
13
|
+
formatToken: "plugin",
|
|
14
|
+
defaultExportNames: ["provider", "plugin"],
|
|
15
|
+
label: "plugin provider",
|
|
16
|
+
},
|
|
17
|
+
authentication: {
|
|
18
|
+
tokens: ["authentication"],
|
|
19
|
+
formatToken: "authentication",
|
|
20
|
+
defaultExportNames: ["authentication", "provider"],
|
|
21
|
+
label: "authentication provider",
|
|
22
|
+
},
|
|
23
|
+
cache: {
|
|
24
|
+
tokens: ["cache"],
|
|
25
|
+
formatToken: "cache",
|
|
26
|
+
defaultExportNames: ["cache", "provider"],
|
|
27
|
+
label: "cache provider",
|
|
28
|
+
},
|
|
29
|
+
secrets: {
|
|
30
|
+
tokens: ["secrets"],
|
|
31
|
+
formatToken: "secrets",
|
|
32
|
+
defaultExportNames: ["secrets", "provider"],
|
|
33
|
+
label: "secrets provider",
|
|
34
|
+
},
|
|
35
|
+
s3: {
|
|
36
|
+
tokens: ["s3"],
|
|
37
|
+
formatToken: "s3",
|
|
38
|
+
defaultExportNames: ["s3", "provider"],
|
|
39
|
+
label: "s3 provider",
|
|
40
|
+
},
|
|
41
|
+
workflow: {
|
|
42
|
+
tokens: ["workflow"],
|
|
43
|
+
formatToken: "workflow",
|
|
44
|
+
defaultExportNames: ["workflow", "provider"],
|
|
45
|
+
label: "workflow provider",
|
|
46
|
+
},
|
|
47
|
+
telemetry: {
|
|
48
|
+
tokens: ["telemetry"],
|
|
49
|
+
formatToken: "telemetry",
|
|
50
|
+
defaultExportNames: ["telemetry", "provider"],
|
|
51
|
+
label: "telemetry provider",
|
|
52
|
+
},
|
|
53
|
+
} satisfies Record<ProviderKind, ProviderKindDefinition>;
|
|
54
|
+
|
|
55
|
+
const EXTERNAL_PROVIDER_KIND_TOKEN_SET = new Set<string>(
|
|
56
|
+
Object.values(PROVIDER_KIND_DEFINITIONS).flatMap(
|
|
57
|
+
(definition) => definition.tokens,
|
|
58
|
+
),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const EXTERNAL_PROVIDER_KIND_MAP = new Map<string, ProviderKind>(
|
|
62
|
+
Object.entries(PROVIDER_KIND_DEFINITIONS).flatMap(([kind, definition]) =>
|
|
63
|
+
definition.tokens.map(
|
|
64
|
+
(token) => [token, kind as ProviderKind] as const,
|
|
65
|
+
),
|
|
66
|
+
),
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
export function isExternalProviderKindToken(value: string): boolean {
|
|
70
|
+
return EXTERNAL_PROVIDER_KIND_TOKEN_SET.has(value.trim().toLowerCase());
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function parseExternalProviderKind(value: string): ProviderKind {
|
|
74
|
+
const normalized = value.trim().toLowerCase();
|
|
75
|
+
const kind = EXTERNAL_PROVIDER_KIND_MAP.get(normalized);
|
|
76
|
+
if (!kind) {
|
|
77
|
+
throw new Error(`unsupported provider kind ${JSON.stringify(value)}`);
|
|
78
|
+
}
|
|
79
|
+
return kind;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function formatExternalProviderKind(kind: ProviderKind): string {
|
|
83
|
+
return PROVIDER_KIND_DEFINITIONS[kind].formatToken;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function providerKindLabel(kind: ProviderKind): string {
|
|
87
|
+
return PROVIDER_KIND_DEFINITIONS[kind].label;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function defaultProviderExportNames(
|
|
91
|
+
kind: ProviderKind,
|
|
92
|
+
): readonly string[] {
|
|
93
|
+
return PROVIDER_KIND_DEFINITIONS[kind].defaultExportNames;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function resolveDefaultProviderExport(
|
|
97
|
+
module: Record<string, unknown>,
|
|
98
|
+
kind: ProviderKind,
|
|
99
|
+
): unknown {
|
|
100
|
+
for (const exportName of defaultProviderExportNames(kind)) {
|
|
101
|
+
const candidate = Reflect.get(module, exportName);
|
|
102
|
+
if (candidate !== undefined && candidate !== null) {
|
|
103
|
+
return candidate;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return Reflect.get(module, "default");
|
|
107
|
+
}
|
package/src/provider.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import type { MaybePromise } from "./api.ts";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Provider kinds supported by the TypeScript SDK runtime.
|
|
5
|
+
*/
|
|
3
6
|
export type ProviderKind =
|
|
4
7
|
| "integration"
|
|
5
|
-
| "
|
|
8
|
+
| "authentication"
|
|
6
9
|
| "cache"
|
|
7
10
|
| "secrets"
|
|
8
11
|
| "s3"
|
|
12
|
+
| "workflow"
|
|
9
13
|
| "telemetry";
|
|
10
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Runtime metadata reported to the Gestalt host during startup.
|
|
17
|
+
*/
|
|
11
18
|
export type ProviderMetadata = {
|
|
12
19
|
kind?: ProviderKind;
|
|
13
20
|
name?: string;
|
|
@@ -16,17 +23,32 @@ export type ProviderMetadata = {
|
|
|
16
23
|
version?: string;
|
|
17
24
|
};
|
|
18
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Optional configuration hook invoked after the host starts the provider.
|
|
28
|
+
*/
|
|
19
29
|
export type ConfigureHandler = (
|
|
20
30
|
name: string,
|
|
21
31
|
config: Record<string, unknown>,
|
|
22
32
|
) => MaybePromise<void>;
|
|
23
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Optional readiness probe invoked by the Gestalt host.
|
|
36
|
+
*/
|
|
24
37
|
export type HealthCheckHandler = () => MaybePromise<void>;
|
|
25
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Optional callback that returns non-fatal runtime warnings.
|
|
41
|
+
*/
|
|
26
42
|
export type WarningsHandler = () => MaybePromise<string[]>;
|
|
27
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Optional shutdown hook invoked when the provider process exits.
|
|
46
|
+
*/
|
|
28
47
|
export type CloseHandler = () => MaybePromise<void>;
|
|
29
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Shared runtime metadata and lifecycle hooks for authored providers.
|
|
51
|
+
*/
|
|
30
52
|
export interface RuntimeProviderOptions {
|
|
31
53
|
name?: string;
|
|
32
54
|
displayName?: string;
|
|
@@ -38,6 +60,9 @@ export interface RuntimeProviderOptions {
|
|
|
38
60
|
close?: CloseHandler;
|
|
39
61
|
}
|
|
40
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Base class shared by all TypeScript SDK provider implementations.
|
|
65
|
+
*/
|
|
41
66
|
export abstract class RuntimeProvider {
|
|
42
67
|
abstract readonly kind: ProviderKind;
|
|
43
68
|
|
|
@@ -116,6 +141,9 @@ export abstract class RuntimeProvider {
|
|
|
116
141
|
}
|
|
117
142
|
}
|
|
118
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Runtime type guard for values that implement the provider base contract.
|
|
146
|
+
*/
|
|
119
147
|
export function isRuntimeProvider(value: unknown): value is RuntimeProvider {
|
|
120
148
|
return (
|
|
121
149
|
value instanceof RuntimeProvider ||
|
|
@@ -127,6 +155,9 @@ export function isRuntimeProvider(value: unknown): value is RuntimeProvider {
|
|
|
127
155
|
);
|
|
128
156
|
}
|
|
129
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Normalizes package and provider names into Gestalt's slug format.
|
|
160
|
+
*/
|
|
130
161
|
export function slugName(value: string): string {
|
|
131
162
|
const normalized = value.trim().replace(/^@[^/]+\//, "");
|
|
132
163
|
return normalized.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|