@valon-technologies/gestalt 0.0.1-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +160 -0
- package/gen/v1/auth_pb.ts +212 -0
- package/gen/v1/cache_pb.ts +357 -0
- package/gen/v1/datastore_pb.ts +922 -0
- package/gen/v1/plugin_pb.ts +772 -0
- package/gen/v1/runtime_pb.ts +216 -0
- package/gen/v1/s3_pb.ts +640 -0
- package/gen/v1/secrets_pb.ts +63 -0
- package/package.json +55 -0
- package/src/api.ts +98 -0
- package/src/auth.ts +103 -0
- package/src/build.ts +181 -0
- package/src/cache.ts +304 -0
- package/src/catalog.ts +188 -0
- package/src/index.ts +182 -0
- package/src/indexeddb.ts +740 -0
- package/src/plugin.ts +402 -0
- package/src/provider.ts +133 -0
- package/src/runtime.ts +871 -0
- package/src/s3.ts +1128 -0
- package/src/schema.ts +219 -0
- package/src/secrets.ts +36 -0
- package/src/target.ts +192 -0
- package/tsconfig.json +22 -0
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Catalog,
|
|
3
|
+
type CatalogOperation,
|
|
4
|
+
type CatalogSchema,
|
|
5
|
+
catalogToJson,
|
|
6
|
+
schemaToCatalogSchema,
|
|
7
|
+
schemaToParameters,
|
|
8
|
+
writeCatalogYaml,
|
|
9
|
+
} from "./catalog.ts";
|
|
10
|
+
import {
|
|
11
|
+
errorMessage,
|
|
12
|
+
type MaybePromise,
|
|
13
|
+
type OperationResult,
|
|
14
|
+
type Request,
|
|
15
|
+
responseBrand,
|
|
16
|
+
type Response,
|
|
17
|
+
} from "./api.ts";
|
|
18
|
+
import { RuntimeProvider, type RuntimeProviderOptions } from "./provider.ts";
|
|
19
|
+
import type { Schema } from "./schema.ts";
|
|
20
|
+
|
|
21
|
+
export type ConnectionMode =
|
|
22
|
+
| "unspecified"
|
|
23
|
+
| "none"
|
|
24
|
+
| "user"
|
|
25
|
+
| "identity"
|
|
26
|
+
| "either";
|
|
27
|
+
|
|
28
|
+
export interface ConnectionParamDefinition {
|
|
29
|
+
required?: boolean;
|
|
30
|
+
description?: string;
|
|
31
|
+
defaultValue?: string;
|
|
32
|
+
from?: string;
|
|
33
|
+
field?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface OperationOptions<In, Out> {
|
|
37
|
+
id: string;
|
|
38
|
+
method?: string;
|
|
39
|
+
title?: string;
|
|
40
|
+
description?: string;
|
|
41
|
+
allowedRoles?: string[];
|
|
42
|
+
tags?: string[];
|
|
43
|
+
readOnly?: boolean;
|
|
44
|
+
visible?: boolean;
|
|
45
|
+
input?: Schema<In>;
|
|
46
|
+
output?: Schema<Out>;
|
|
47
|
+
handler: (input: In, request: Request) => MaybePromise<Out | Response<Out>>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface OperationDefinition<In, Out> extends OperationOptions<
|
|
51
|
+
In,
|
|
52
|
+
Out
|
|
53
|
+
> {}
|
|
54
|
+
|
|
55
|
+
export type SessionCatalog = Catalog | Record<string, unknown>;
|
|
56
|
+
export type SessionCatalogHandler = (
|
|
57
|
+
request: Request,
|
|
58
|
+
) => MaybePromise<SessionCatalog | null | undefined>;
|
|
59
|
+
|
|
60
|
+
export interface PluginDefinitionOptions extends RuntimeProviderOptions {
|
|
61
|
+
connectionMode?: ConnectionMode;
|
|
62
|
+
authTypes?: string[];
|
|
63
|
+
connectionParams?: Record<string, ConnectionParamDefinition>;
|
|
64
|
+
iconSvg?: string;
|
|
65
|
+
operations: Array<OperationDefinition<any, any>>;
|
|
66
|
+
sessionCatalog?: SessionCatalogHandler;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type IntegrationProviderOptions = PluginDefinitionOptions;
|
|
70
|
+
|
|
71
|
+
export function operation<In, Out>(
|
|
72
|
+
options: OperationOptions<In, Out>,
|
|
73
|
+
): OperationDefinition<In, Out> {
|
|
74
|
+
return {
|
|
75
|
+
...options,
|
|
76
|
+
id: options.id.trim(),
|
|
77
|
+
method: normalizeMethod(options.method),
|
|
78
|
+
title: options.title?.trim() ?? "",
|
|
79
|
+
description: options.description?.trim() ?? "",
|
|
80
|
+
allowedRoles: normalizeAllowedRoles(options.allowedRoles),
|
|
81
|
+
tags: [...(options.tags ?? [])],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export class IntegrationProvider extends RuntimeProvider {
|
|
86
|
+
readonly kind = "integration" as const;
|
|
87
|
+
readonly iconSvg: string;
|
|
88
|
+
readonly connectionMode: ConnectionMode;
|
|
89
|
+
readonly authTypes: string[];
|
|
90
|
+
readonly connectionParams: Record<string, ConnectionParamDefinition>;
|
|
91
|
+
|
|
92
|
+
private readonly sessionCatalogHandler: SessionCatalogHandler | undefined;
|
|
93
|
+
private readonly operations = new Map<
|
|
94
|
+
string,
|
|
95
|
+
OperationDefinition<any, any>
|
|
96
|
+
>();
|
|
97
|
+
|
|
98
|
+
constructor(options: PluginDefinitionOptions) {
|
|
99
|
+
super(options);
|
|
100
|
+
this.iconSvg = options.iconSvg?.trim() ?? "";
|
|
101
|
+
this.connectionMode = options.connectionMode ?? "unspecified";
|
|
102
|
+
this.authTypes = [...(options.authTypes ?? [])];
|
|
103
|
+
this.connectionParams = normalizeConnectionParams(options.connectionParams);
|
|
104
|
+
this.sessionCatalogHandler = options.sessionCatalog;
|
|
105
|
+
|
|
106
|
+
for (const entry of options.operations) {
|
|
107
|
+
const id = entry.id.trim();
|
|
108
|
+
if (!id) {
|
|
109
|
+
throw new Error("operation id is required");
|
|
110
|
+
}
|
|
111
|
+
if (this.operations.has(id)) {
|
|
112
|
+
throw new Error(`duplicate operation id ${JSON.stringify(id)}`);
|
|
113
|
+
}
|
|
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
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
supportsSessionCatalog(): boolean {
|
|
127
|
+
return this.sessionCatalogHandler !== undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async catalogForRequest(
|
|
131
|
+
request: Request,
|
|
132
|
+
): Promise<SessionCatalog | null | undefined> {
|
|
133
|
+
return await this.sessionCatalogHandler?.(request);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
staticCatalog(): Catalog {
|
|
137
|
+
const catalog: Catalog = {
|
|
138
|
+
operations: [...this.operations.values()].map<CatalogOperation>(
|
|
139
|
+
(entry) => {
|
|
140
|
+
const operationCatalog: CatalogOperation = {
|
|
141
|
+
id: entry.id,
|
|
142
|
+
method: normalizeMethod(entry.method),
|
|
143
|
+
};
|
|
144
|
+
if (entry.title) {
|
|
145
|
+
operationCatalog.title = entry.title;
|
|
146
|
+
}
|
|
147
|
+
if (entry.description) {
|
|
148
|
+
operationCatalog.description = entry.description;
|
|
149
|
+
}
|
|
150
|
+
const parameters = schemaToParameters(
|
|
151
|
+
entry.input as Schema<unknown> | undefined,
|
|
152
|
+
);
|
|
153
|
+
if (parameters.length > 0) {
|
|
154
|
+
operationCatalog.parameters = parameters;
|
|
155
|
+
}
|
|
156
|
+
const inputSchema = schemaToCatalogSchema(
|
|
157
|
+
entry.input as Schema<unknown> | undefined,
|
|
158
|
+
);
|
|
159
|
+
if (inputSchema !== undefined) {
|
|
160
|
+
operationCatalog.inputSchema = inputSchema;
|
|
161
|
+
}
|
|
162
|
+
const outputSchema = schemaToCatalogSchema(
|
|
163
|
+
entry.output as Schema<unknown> | undefined,
|
|
164
|
+
);
|
|
165
|
+
if (outputSchema !== undefined) {
|
|
166
|
+
operationCatalog.outputSchema = outputSchema;
|
|
167
|
+
}
|
|
168
|
+
if (entry.tags && entry.tags.length > 0) {
|
|
169
|
+
operationCatalog.tags = [...entry.tags];
|
|
170
|
+
}
|
|
171
|
+
if (entry.allowedRoles && entry.allowedRoles.length > 0) {
|
|
172
|
+
operationCatalog.allowedRoles = [...entry.allowedRoles];
|
|
173
|
+
}
|
|
174
|
+
if (entry.readOnly !== undefined) {
|
|
175
|
+
operationCatalog.readOnly = entry.readOnly;
|
|
176
|
+
}
|
|
177
|
+
if (entry.visible !== undefined) {
|
|
178
|
+
operationCatalog.visible = entry.visible;
|
|
179
|
+
}
|
|
180
|
+
return operationCatalog;
|
|
181
|
+
},
|
|
182
|
+
),
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
if (this.name) {
|
|
186
|
+
catalog.name = this.name;
|
|
187
|
+
}
|
|
188
|
+
if (this.displayName) {
|
|
189
|
+
catalog.displayName = this.displayName;
|
|
190
|
+
}
|
|
191
|
+
if (this.description) {
|
|
192
|
+
catalog.description = this.description;
|
|
193
|
+
}
|
|
194
|
+
if (this.iconSvg) {
|
|
195
|
+
catalog.iconSvg = this.iconSvg;
|
|
196
|
+
}
|
|
197
|
+
return catalog;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
writeCatalog(path: string): void {
|
|
201
|
+
writeCatalogYaml(path, this.staticCatalog());
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
catalogJson(): string {
|
|
205
|
+
return catalogToJson(this.staticCatalog());
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async execute(
|
|
209
|
+
operationId: string,
|
|
210
|
+
params: Record<string, unknown>,
|
|
211
|
+
request: Request,
|
|
212
|
+
): Promise<OperationResult> {
|
|
213
|
+
const entry = this.operations.get(operationId);
|
|
214
|
+
if (!entry) {
|
|
215
|
+
return errorResult(404, "unknown operation");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
let input: unknown = undefined;
|
|
219
|
+
try {
|
|
220
|
+
if (entry.input) {
|
|
221
|
+
input = entry.input.parse(
|
|
222
|
+
normalizeOperationInput(entry.input, params),
|
|
223
|
+
"$",
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
} catch (error) {
|
|
227
|
+
return errorResult(400, errorMessage(error));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
const raw = await entry.handler(input, request);
|
|
232
|
+
const response = isResponse(raw) ? raw : { status: 200, body: raw };
|
|
233
|
+
const body = entry.output
|
|
234
|
+
? entry.output.parse(response.body, "$response")
|
|
235
|
+
: response.body;
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
status: response.status ?? 200,
|
|
239
|
+
body: JSON.stringify(body),
|
|
240
|
+
};
|
|
241
|
+
} catch (error) {
|
|
242
|
+
return errorResult(500, errorMessage(error));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export const Plugin = IntegrationProvider;
|
|
248
|
+
|
|
249
|
+
export function defineIntegrationProvider(
|
|
250
|
+
options: PluginDefinitionOptions,
|
|
251
|
+
): IntegrationProvider {
|
|
252
|
+
return new IntegrationProvider(options);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export function definePlugin(
|
|
256
|
+
options: PluginDefinitionOptions,
|
|
257
|
+
): IntegrationProvider {
|
|
258
|
+
return new IntegrationProvider(options);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function isIntegrationProvider(
|
|
262
|
+
value: unknown,
|
|
263
|
+
): value is IntegrationProvider {
|
|
264
|
+
return (
|
|
265
|
+
value instanceof IntegrationProvider ||
|
|
266
|
+
(typeof value === "object" &&
|
|
267
|
+
value !== null &&
|
|
268
|
+
"kind" in value &&
|
|
269
|
+
(value as { kind?: unknown }).kind === "integration" &&
|
|
270
|
+
"staticCatalog" in value &&
|
|
271
|
+
"execute" in value)
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function normalizeConnectionParams(
|
|
276
|
+
input: Record<string, ConnectionParamDefinition> | undefined,
|
|
277
|
+
): Record<string, ConnectionParamDefinition> {
|
|
278
|
+
const output: Record<string, ConnectionParamDefinition> = {};
|
|
279
|
+
for (const [key, value] of Object.entries(input ?? {})) {
|
|
280
|
+
const entry: ConnectionParamDefinition = {};
|
|
281
|
+
if (value.required !== undefined) {
|
|
282
|
+
entry.required = value.required;
|
|
283
|
+
}
|
|
284
|
+
if (value.description?.trim()) {
|
|
285
|
+
entry.description = value.description.trim();
|
|
286
|
+
}
|
|
287
|
+
if (value.defaultValue !== undefined) {
|
|
288
|
+
entry.defaultValue = value.defaultValue;
|
|
289
|
+
}
|
|
290
|
+
if (value.from?.trim()) {
|
|
291
|
+
entry.from = value.from.trim();
|
|
292
|
+
}
|
|
293
|
+
if (value.field?.trim()) {
|
|
294
|
+
entry.field = value.field.trim();
|
|
295
|
+
}
|
|
296
|
+
output[key] = entry;
|
|
297
|
+
}
|
|
298
|
+
return output;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function isResponse(value: unknown): value is Response<unknown> {
|
|
302
|
+
if (typeof value !== "object" || value === null) {
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
if (!(responseBrand in value)) {
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
const status = (value as { status?: unknown }).status;
|
|
309
|
+
return (
|
|
310
|
+
status === undefined ||
|
|
311
|
+
(typeof status === "number" && Number.isInteger(status))
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function normalizeMethod(value: string | undefined): string {
|
|
316
|
+
return (value?.trim() || "POST").toUpperCase();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function normalizeAllowedRoles(value: string[] | undefined): string[] {
|
|
320
|
+
const normalized: string[] = [];
|
|
321
|
+
const seen = new Set<string>();
|
|
322
|
+
for (const role of value ?? []) {
|
|
323
|
+
const trimmed = role.trim();
|
|
324
|
+
if (!trimmed || seen.has(trimmed)) {
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
seen.add(trimmed);
|
|
328
|
+
normalized.push(trimmed);
|
|
329
|
+
}
|
|
330
|
+
return normalized;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function normalizeOperationInput(
|
|
334
|
+
schema: Schema<unknown>,
|
|
335
|
+
params: Record<string, unknown>,
|
|
336
|
+
): unknown {
|
|
337
|
+
if (schema.fields) {
|
|
338
|
+
return params ?? {};
|
|
339
|
+
}
|
|
340
|
+
const entries = Object.entries(params ?? {});
|
|
341
|
+
if (entries.length === 1) {
|
|
342
|
+
return entries[0]?.[1];
|
|
343
|
+
}
|
|
344
|
+
return params;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function errorResult(status: number, message: string): OperationResult {
|
|
348
|
+
return {
|
|
349
|
+
status,
|
|
350
|
+
body: JSON.stringify({
|
|
351
|
+
error: message,
|
|
352
|
+
}),
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export function connectionModeToProtoValue(mode: ConnectionMode): number {
|
|
357
|
+
switch (mode) {
|
|
358
|
+
case "none":
|
|
359
|
+
return 1;
|
|
360
|
+
case "user":
|
|
361
|
+
return 2;
|
|
362
|
+
case "identity":
|
|
363
|
+
return 3;
|
|
364
|
+
case "either":
|
|
365
|
+
return 4;
|
|
366
|
+
case "unspecified":
|
|
367
|
+
default:
|
|
368
|
+
return 0;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
export function connectionParamToProto(value: ConnectionParamDefinition): {
|
|
373
|
+
required?: boolean;
|
|
374
|
+
description?: string;
|
|
375
|
+
defaultValue?: string;
|
|
376
|
+
from?: string;
|
|
377
|
+
field?: string;
|
|
378
|
+
} {
|
|
379
|
+
const output: {
|
|
380
|
+
required?: boolean;
|
|
381
|
+
description?: string;
|
|
382
|
+
defaultValue?: string;
|
|
383
|
+
from?: string;
|
|
384
|
+
field?: string;
|
|
385
|
+
} = {};
|
|
386
|
+
if (value.required !== undefined) {
|
|
387
|
+
output.required = value.required;
|
|
388
|
+
}
|
|
389
|
+
if (value.description !== undefined) {
|
|
390
|
+
output.description = value.description;
|
|
391
|
+
}
|
|
392
|
+
if (value.defaultValue !== undefined) {
|
|
393
|
+
output.defaultValue = value.defaultValue;
|
|
394
|
+
}
|
|
395
|
+
if (value.from !== undefined) {
|
|
396
|
+
output.from = value.from;
|
|
397
|
+
}
|
|
398
|
+
if (value.field !== undefined) {
|
|
399
|
+
output.field = value.field;
|
|
400
|
+
}
|
|
401
|
+
return output;
|
|
402
|
+
}
|
package/src/provider.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { MaybePromise } from "./api.ts";
|
|
2
|
+
|
|
3
|
+
export type ProviderKind =
|
|
4
|
+
| "integration"
|
|
5
|
+
| "auth"
|
|
6
|
+
| "cache"
|
|
7
|
+
| "secrets"
|
|
8
|
+
| "s3"
|
|
9
|
+
| "telemetry";
|
|
10
|
+
|
|
11
|
+
export type ProviderMetadata = {
|
|
12
|
+
kind?: ProviderKind;
|
|
13
|
+
name?: string;
|
|
14
|
+
displayName?: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
version?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type ConfigureHandler = (
|
|
20
|
+
name: string,
|
|
21
|
+
config: Record<string, unknown>,
|
|
22
|
+
) => MaybePromise<void>;
|
|
23
|
+
|
|
24
|
+
export type HealthCheckHandler = () => MaybePromise<void>;
|
|
25
|
+
|
|
26
|
+
export type WarningsHandler = () => MaybePromise<string[]>;
|
|
27
|
+
|
|
28
|
+
export type CloseHandler = () => MaybePromise<void>;
|
|
29
|
+
|
|
30
|
+
export interface RuntimeProviderOptions {
|
|
31
|
+
name?: string;
|
|
32
|
+
displayName?: string;
|
|
33
|
+
description?: string;
|
|
34
|
+
version?: string;
|
|
35
|
+
configure?: ConfigureHandler;
|
|
36
|
+
healthCheck?: HealthCheckHandler;
|
|
37
|
+
warnings?: string[] | WarningsHandler;
|
|
38
|
+
close?: CloseHandler;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export abstract class RuntimeProvider {
|
|
42
|
+
abstract readonly kind: ProviderKind;
|
|
43
|
+
|
|
44
|
+
name: string;
|
|
45
|
+
readonly displayName: string;
|
|
46
|
+
readonly description: string;
|
|
47
|
+
readonly version: string;
|
|
48
|
+
|
|
49
|
+
private readonly configureHandler: ConfigureHandler | undefined;
|
|
50
|
+
private readonly healthCheckHandler: HealthCheckHandler | undefined;
|
|
51
|
+
private readonly warningsSource: string[] | WarningsHandler | undefined;
|
|
52
|
+
private readonly closeHandler: CloseHandler | undefined;
|
|
53
|
+
|
|
54
|
+
protected constructor(options: RuntimeProviderOptions) {
|
|
55
|
+
this.name = slugName(options.name ?? "");
|
|
56
|
+
this.displayName = options.displayName?.trim() ?? "";
|
|
57
|
+
this.description = options.description?.trim() ?? "";
|
|
58
|
+
this.version = options.version?.trim() ?? "";
|
|
59
|
+
this.configureHandler = options.configure;
|
|
60
|
+
this.healthCheckHandler = options.healthCheck;
|
|
61
|
+
this.warningsSource = Array.isArray(options.warnings)
|
|
62
|
+
? [...options.warnings]
|
|
63
|
+
: options.warnings;
|
|
64
|
+
this.closeHandler = options.close;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
resolveName(fallback: string): void {
|
|
68
|
+
if (!this.name) {
|
|
69
|
+
this.name = slugName(fallback);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
runtimeMetadata(): ProviderMetadata {
|
|
74
|
+
const metadata: ProviderMetadata = {
|
|
75
|
+
kind: this.kind,
|
|
76
|
+
};
|
|
77
|
+
if (this.name) {
|
|
78
|
+
metadata.name = this.name;
|
|
79
|
+
}
|
|
80
|
+
if (this.displayName) {
|
|
81
|
+
metadata.displayName = this.displayName;
|
|
82
|
+
}
|
|
83
|
+
if (this.description) {
|
|
84
|
+
metadata.description = this.description;
|
|
85
|
+
}
|
|
86
|
+
if (this.version) {
|
|
87
|
+
metadata.version = this.version;
|
|
88
|
+
}
|
|
89
|
+
return metadata;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async configureProvider(name: string, config: Record<string, unknown>): Promise<void> {
|
|
93
|
+
await this.configureHandler?.(name, config);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
supportsHealthCheck(): boolean {
|
|
97
|
+
return this.healthCheckHandler !== undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async healthCheck(): Promise<void> {
|
|
101
|
+
await this.healthCheckHandler?.();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async warnings(): Promise<string[]> {
|
|
105
|
+
if (!this.warningsSource) {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
if (Array.isArray(this.warningsSource)) {
|
|
109
|
+
return [...this.warningsSource];
|
|
110
|
+
}
|
|
111
|
+
return [...(await this.warningsSource())];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async closeProvider(): Promise<void> {
|
|
115
|
+
await this.closeHandler?.();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function isRuntimeProvider(value: unknown): value is RuntimeProvider {
|
|
120
|
+
return (
|
|
121
|
+
value instanceof RuntimeProvider ||
|
|
122
|
+
(typeof value === "object" &&
|
|
123
|
+
value !== null &&
|
|
124
|
+
"kind" in value &&
|
|
125
|
+
"resolveName" in value &&
|
|
126
|
+
"configureProvider" in value)
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function slugName(value: string): string {
|
|
131
|
+
const normalized = value.trim().replace(/^@[^/]+\//, "");
|
|
132
|
+
return normalized.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
133
|
+
}
|