@michelangelo-ai/rpc 0.3.0-nightly.20260708 → 0.3.0-nightly.20260710
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/create-fetch-transport.ts +36 -0
- package/grpc-transcoder-error.ts +49 -0
- package/handlers.ts +19 -39
- package/index.ts +1 -1
- package/normalize-transcoder-error.ts +36 -0
- package/package.json +2 -3
- package/request.ts +6 -1
- package/runtime-config.ts +1 -0
- package/services.ts +37 -26
- package/transformations/common.ts +3 -0
- package/types.ts +50 -9
- package/vite.config.ts +1 -7
- package/normalize-connect-error.ts +0 -82
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { toTranscoderError } from './grpc-transcoder-error';
|
|
2
|
+
|
|
3
|
+
import type { JsonValue } from '@bufbuild/protobuf';
|
|
4
|
+
import type { FetchTransport, FetchTransportOptions } from './types';
|
|
5
|
+
|
|
6
|
+
export function createFetchTransport(options: FetchTransportOptions): FetchTransport {
|
|
7
|
+
const baseUrl = options.baseUrl.replace(/\/+$/, '');
|
|
8
|
+
const headers = {
|
|
9
|
+
'Content-Type': 'application/json',
|
|
10
|
+
'context-Ttl-Ms': '10000',
|
|
11
|
+
'grpc-timeout': '1000000m',
|
|
12
|
+
'Rpc-Caller': 'ma-studio',
|
|
13
|
+
'Rpc-Service': 'ma-apiserver',
|
|
14
|
+
'Rpc-Encoding': 'proto',
|
|
15
|
+
...options.headers,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
async callUnary(serviceName, methodName, request) {
|
|
20
|
+
const response = await fetch(`${baseUrl}/${serviceName}/${methodName}`, {
|
|
21
|
+
method: 'POST',
|
|
22
|
+
headers,
|
|
23
|
+
body: JSON.stringify(request),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// cast: response.json() returns Promise<any> per DOM types
|
|
27
|
+
const body = (await response.json().catch(() => null)) as JsonValue | null;
|
|
28
|
+
|
|
29
|
+
if (response.status < 200 || response.status >= 300) {
|
|
30
|
+
throw toTranscoderError(response, body);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return body;
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { GoogleRpcStatus } from './types';
|
|
2
|
+
|
|
3
|
+
export class GrpcTranscoderError extends Error {
|
|
4
|
+
readonly code: number;
|
|
5
|
+
readonly details: unknown[];
|
|
6
|
+
|
|
7
|
+
constructor(message: string, code: number, details: unknown[] = []) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = 'GrpcTranscoderError';
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.details = details;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function toTranscoderError(response: Response, body: unknown): GrpcTranscoderError {
|
|
16
|
+
if (isGoogleRpcStatus(body)) {
|
|
17
|
+
return new GrpcTranscoderError(body.message, body.code, body.details ?? []);
|
|
18
|
+
}
|
|
19
|
+
// Plain YARPC errors leave the body empty. Envoy surfaces the real code
|
|
20
|
+
// and message via grpc-status/grpc-message headers.
|
|
21
|
+
const headerStatus = response.headers.get('grpc-status');
|
|
22
|
+
const headerMessage = response.headers.get('grpc-message');
|
|
23
|
+
if (headerStatus !== null) {
|
|
24
|
+
return new GrpcTranscoderError(
|
|
25
|
+
headerMessage ? safeDecode(headerMessage) : response.statusText,
|
|
26
|
+
Number(headerStatus)
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
return new GrpcTranscoderError(
|
|
30
|
+
response.statusText || `Request failed with status ${response.status}`,
|
|
31
|
+
2
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isGoogleRpcStatus(value: unknown): value is GoogleRpcStatus {
|
|
36
|
+
if (typeof value !== 'object' || value === null) return false;
|
|
37
|
+
// cast: narrowing unknown to check shape
|
|
38
|
+
const candidate = value as GoogleRpcStatus;
|
|
39
|
+
return typeof candidate.code === 'number' && typeof candidate.message === 'string';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// grpc-message headers are percent-encoded per the gRPC HTTP/2 spec.
|
|
43
|
+
function safeDecode(value: string): string {
|
|
44
|
+
try {
|
|
45
|
+
return decodeURIComponent(value);
|
|
46
|
+
} catch {
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
}
|
package/handlers.ts
CHANGED
|
@@ -6,55 +6,35 @@ import type { ExtractUnaryRpc } from './types';
|
|
|
6
6
|
|
|
7
7
|
let handlersPromise: Promise<Awaited<ReturnType<typeof createHandlers>>> | null = null;
|
|
8
8
|
|
|
9
|
+
function unary<Fn>(fn: Fn): ExtractUnaryRpc<Fn> {
|
|
10
|
+
// cast: TS can't resolve ExtractUnaryRpc's conditional type against the unconstrained generic Fn
|
|
11
|
+
// from within this function; fn always satisfies it at call sites
|
|
12
|
+
return fn as unknown as ExtractUnaryRpc<Fn>;
|
|
13
|
+
}
|
|
14
|
+
|
|
9
15
|
async function createHandlers() {
|
|
10
16
|
const services = await getServices();
|
|
11
17
|
|
|
12
18
|
return {
|
|
13
|
-
ListDeployment: services.DeploymentService.listDeployment
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
ListProject: services.ProjectService.listProject as ExtractUnaryRpc<
|
|
26
|
-
typeof services.ProjectService.listProject
|
|
27
|
-
>,
|
|
28
|
-
GetProject: services.ProjectService.getProject as ExtractUnaryRpc<
|
|
29
|
-
typeof services.ProjectService.getProject
|
|
30
|
-
>,
|
|
31
|
-
GetPipeline: services.PipelineService.getPipeline as ExtractUnaryRpc<
|
|
32
|
-
typeof services.PipelineService.getPipeline
|
|
33
|
-
>,
|
|
34
|
-
ListPipeline: services.PipelineService.listPipeline as ExtractUnaryRpc<
|
|
35
|
-
typeof services.PipelineService.listPipeline
|
|
36
|
-
>,
|
|
37
|
-
ListPipelineRun: services.PipelineRunService.listPipelineRun as ExtractUnaryRpc<
|
|
38
|
-
typeof services.PipelineRunService.listPipelineRun
|
|
39
|
-
>,
|
|
40
|
-
GetPipelineRun: services.PipelineRunService.getPipelineRun as ExtractUnaryRpc<
|
|
41
|
-
typeof services.PipelineRunService.getPipelineRun
|
|
42
|
-
>,
|
|
43
|
-
ListTriggerRun: services.TriggerRunService.listTriggerRun as ExtractUnaryRpc<
|
|
44
|
-
typeof services.TriggerRunService.listTriggerRun
|
|
45
|
-
>,
|
|
46
|
-
GetTriggerRun: services.TriggerRunService.getTriggerRun as ExtractUnaryRpc<
|
|
47
|
-
typeof services.TriggerRunService.getTriggerRun
|
|
48
|
-
>,
|
|
19
|
+
ListDeployment: unary(services.DeploymentService.listDeployment),
|
|
20
|
+
GetDeployment: unary(services.DeploymentService.getDeployment),
|
|
21
|
+
ListInferenceServer: unary(services.InferenceServerService.listInferenceServer),
|
|
22
|
+
GetInferenceServer: unary(services.InferenceServerService.getInferenceServer),
|
|
23
|
+
ListProject: unary(services.ProjectService.listProject),
|
|
24
|
+
GetProject: unary(services.ProjectService.getProject),
|
|
25
|
+
GetPipeline: unary(services.PipelineService.getPipeline),
|
|
26
|
+
ListPipeline: unary(services.PipelineService.listPipeline),
|
|
27
|
+
ListPipelineRun: unary(services.PipelineRunService.listPipelineRun),
|
|
28
|
+
GetPipelineRun: unary(services.PipelineRunService.getPipelineRun),
|
|
29
|
+
ListTriggerRun: unary(services.TriggerRunService.listTriggerRun),
|
|
30
|
+
GetTriggerRun: unary(services.TriggerRunService.getTriggerRun),
|
|
49
31
|
UpdateTriggerRun: (record: TriggerRun) =>
|
|
50
32
|
services.TriggerRunService.updateTriggerRun({ triggerRun: record }),
|
|
51
33
|
CreatePipelineRun: (record: PipelineRun) =>
|
|
52
34
|
services.PipelineRunService.createPipelineRun({ pipelineRun: record }),
|
|
53
35
|
UpdatePipelineRun: (record: PipelineRun) =>
|
|
54
36
|
services.PipelineRunService.updatePipelineRun({ pipelineRun: record }),
|
|
55
|
-
ListModel: services.ModelService.listModel
|
|
56
|
-
typeof services.ModelService.listModel
|
|
57
|
-
>,
|
|
37
|
+
ListModel: unary(services.ModelService.listModel),
|
|
58
38
|
} as const;
|
|
59
39
|
}
|
|
60
40
|
|
package/index.ts
CHANGED
|
@@ -31,4 +31,4 @@ export * from './gen/michelangelo/api/v2/trigger_run_svc_pb';
|
|
|
31
31
|
export * from './gen/michelangelo/api/v2/user_pb';
|
|
32
32
|
|
|
33
33
|
export { request } from './request';
|
|
34
|
-
export {
|
|
34
|
+
export { normalizeTranscoderError } from './normalize-transcoder-error';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ApplicationError, GrpcStatusCode } from '@michelangelo-ai/core';
|
|
2
|
+
|
|
3
|
+
import { GrpcTranscoderError } from './grpc-transcoder-error';
|
|
4
|
+
|
|
5
|
+
import type { ErrorNormalizer } from '@michelangelo-ai/core';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Normalizes errors thrown by the fetch transport into ApplicationError format.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* <ErrorProvider normalizeError={normalizeTranscoderError}>
|
|
13
|
+
* {children}
|
|
14
|
+
* </ErrorProvider>
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export const normalizeTranscoderError: ErrorNormalizer = (
|
|
18
|
+
error: unknown
|
|
19
|
+
): ApplicationError | null => {
|
|
20
|
+
if (!(error instanceof GrpcTranscoderError)) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// cast: gRPC status codes 0-16 map directly to GrpcStatusCode enum values
|
|
25
|
+
const code = (
|
|
26
|
+
error.code in GrpcStatusCode ? error.code : GrpcStatusCode.UNKNOWN
|
|
27
|
+
) as GrpcStatusCode;
|
|
28
|
+
|
|
29
|
+
return new ApplicationError(error.message, code, {
|
|
30
|
+
source: 'grpc-transcoder',
|
|
31
|
+
meta: {
|
|
32
|
+
details: error.details,
|
|
33
|
+
},
|
|
34
|
+
cause: error,
|
|
35
|
+
});
|
|
36
|
+
};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@michelangelo-ai/rpc",
|
|
3
3
|
"license": "Apache-2.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.3.0-nightly.
|
|
5
|
+
"version": "0.3.0-nightly.20260710",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "vite build",
|
|
8
8
|
"test": "cd ../../ && yarn test:rpc"
|
|
@@ -12,8 +12,7 @@
|
|
|
12
12
|
"vite": "6.2.0"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@
|
|
16
|
-
"@connectrpc/connect-web": "^2.0.2",
|
|
15
|
+
"@bufbuild/protobuf": "^2.2.5",
|
|
17
16
|
"@michelangelo-ai/core": "*"
|
|
18
17
|
},
|
|
19
18
|
"exports": {
|
package/request.ts
CHANGED
|
@@ -24,8 +24,13 @@ export async function request<RpcId extends keyof RpcHandlerType>(
|
|
|
24
24
|
args: OmitTypeName<Parameters<RpcHandlerType[RpcId]>[0]>
|
|
25
25
|
): Promise<OmitTypeName<Awaited<ReturnType<RpcHandlerType[RpcId]>>>> {
|
|
26
26
|
const handlers = await getRpcHandlers();
|
|
27
|
+
// cast: dynamic key lookup on handlers loses the specific RPC signature; we know RpcId is a valid
|
|
28
|
+
// key with matching handler shape
|
|
27
29
|
const handler = handlers[rpcId] as (a: unknown) => Promise<unknown>;
|
|
30
|
+
// cast: handler returns unknown via dynamic dispatch; RpcId determines the concrete return type
|
|
28
31
|
const response = (await handler(args)) as Awaited<ReturnType<RpcHandlerType[RpcId]>>;
|
|
32
|
+
// cast: toPlainObject returns unknown; RpcId determines the proto shape after stripping $typeName
|
|
33
|
+
// fields
|
|
29
34
|
return toPlainObject(response) as OmitTypeName<Awaited<ReturnType<RpcHandlerType[RpcId]>>>;
|
|
30
35
|
}
|
|
31
36
|
|
|
@@ -37,7 +42,7 @@ function toPlainObject(value: unknown): unknown {
|
|
|
37
42
|
if (Array.isArray(value)) return value.map(toPlainObject);
|
|
38
43
|
|
|
39
44
|
const result: Record<string, unknown> = {};
|
|
40
|
-
for (const [key, val] of Object.entries(value
|
|
45
|
+
for (const [key, val] of Object.entries(value)) {
|
|
41
46
|
if (key === '$typeName' || key === '$unknown') continue;
|
|
42
47
|
result[key] = toPlainObject(val);
|
|
43
48
|
}
|
package/runtime-config.ts
CHANGED
|
@@ -23,6 +23,7 @@ export async function getRuntimeConfig(): Promise<RuntimeConfig> {
|
|
|
23
23
|
|
|
24
24
|
let config: RuntimeConfig;
|
|
25
25
|
try {
|
|
26
|
+
// cast: response.json() returns any; the config endpoint's shape is asserted, not validated
|
|
26
27
|
config = (await response.json()) as RuntimeConfig;
|
|
27
28
|
} catch (error) {
|
|
28
29
|
console.error(
|
package/services.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { createRegistry } from '@bufbuild/protobuf';
|
|
2
|
-
import { createClient } from '@connectrpc/connect';
|
|
3
|
-
import { createConnectTransport } from '@connectrpc/connect-web';
|
|
1
|
+
import { create, createRegistry, fromJson, toJson } from '@bufbuild/protobuf';
|
|
4
2
|
|
|
3
|
+
import { createFetchTransport } from './create-fetch-transport';
|
|
5
4
|
import { TypedStructSchema } from './gen/michelangelo/api/typed_struct_pb';
|
|
6
5
|
import { DeploymentService } from './gen/michelangelo/api/v2/deployment_svc_pb';
|
|
7
6
|
import { InferenceServerService } from './gen/michelangelo/api/v2/inference_server_svc_pb';
|
|
@@ -12,41 +11,53 @@ import { ProjectService } from './gen/michelangelo/api/v2/project_svc_pb';
|
|
|
12
11
|
import { TriggerRunService } from './gen/michelangelo/api/v2/trigger_run_svc_pb';
|
|
13
12
|
import { getRuntimeConfig } from './runtime-config';
|
|
14
13
|
|
|
14
|
+
import type { DescService } from '@bufbuild/protobuf';
|
|
15
|
+
import type { FetchTransport, ServiceClient, Services } from './types';
|
|
16
|
+
|
|
15
17
|
const typeRegistry = createRegistry(TypedStructSchema);
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Builds a service client whose methods JSON-encode the request, POST it
|
|
21
|
+
* through the fetch transport, and decode the JSON response back into a
|
|
22
|
+
* protobuf-es message. Envoy's grpc_json_transcoder handles the JSON<->binary
|
|
23
|
+
* conversion on the wire, so this client only ever sees JSON.
|
|
24
|
+
*/
|
|
25
|
+
function createServiceClient<T extends DescService>(
|
|
26
|
+
service: T,
|
|
27
|
+
transport: FetchTransport
|
|
28
|
+
): ServiceClient<T> {
|
|
29
|
+
const client: Record<string, (request: Record<string, unknown>) => Promise<unknown>> = {};
|
|
30
|
+
|
|
31
|
+
for (const method of service.methods) {
|
|
32
|
+
if (method.methodKind !== 'unary') continue;
|
|
19
33
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
34
|
+
client[method.localName] = async (request) => {
|
|
35
|
+
const message = create(method.input, request);
|
|
36
|
+
const requestJson = toJson(method.input, message, { registry: typeRegistry });
|
|
37
|
+
const responseJson = await transport.callUnary(service.typeName, method.name, requestJson);
|
|
38
|
+
return fromJson(method.output, responseJson, { registry: typeRegistry });
|
|
39
|
+
};
|
|
40
|
+
}
|
|
27
41
|
|
|
28
|
-
|
|
29
|
-
|
|
42
|
+
// cast: dynamic method construction can't be statically typed
|
|
43
|
+
return client as ServiceClient<T>;
|
|
44
|
+
}
|
|
30
45
|
|
|
31
46
|
let servicesPromise: Promise<Services> | null = null;
|
|
32
47
|
|
|
33
48
|
async function createServices(): Promise<Services> {
|
|
34
49
|
const { apiBaseUrl } = await getRuntimeConfig();
|
|
35
50
|
|
|
36
|
-
const transport =
|
|
37
|
-
baseUrl: apiBaseUrl,
|
|
38
|
-
interceptors: [callerInterceptor],
|
|
39
|
-
jsonOptions: { registry: typeRegistry },
|
|
40
|
-
});
|
|
51
|
+
const transport = createFetchTransport({ baseUrl: apiBaseUrl });
|
|
41
52
|
|
|
42
53
|
return {
|
|
43
|
-
DeploymentService:
|
|
44
|
-
InferenceServerService:
|
|
45
|
-
ProjectService:
|
|
46
|
-
PipelineService:
|
|
47
|
-
PipelineRunService:
|
|
48
|
-
TriggerRunService:
|
|
49
|
-
ModelService:
|
|
54
|
+
DeploymentService: createServiceClient(DeploymentService, transport),
|
|
55
|
+
InferenceServerService: createServiceClient(InferenceServerService, transport),
|
|
56
|
+
ProjectService: createServiceClient(ProjectService, transport),
|
|
57
|
+
PipelineService: createServiceClient(PipelineService, transport),
|
|
58
|
+
PipelineRunService: createServiceClient(PipelineRunService, transport),
|
|
59
|
+
TriggerRunService: createServiceClient(TriggerRunService, transport),
|
|
60
|
+
ModelService: createServiceClient(ModelService, transport),
|
|
50
61
|
} as const;
|
|
51
62
|
}
|
|
52
63
|
|
|
@@ -33,6 +33,9 @@ export function extractEntityFromResponse<T extends HasTypeName>(
|
|
|
33
33
|
.toLowerCase();
|
|
34
34
|
|
|
35
35
|
if (entityName in response) {
|
|
36
|
+
// cast: dynamic key access on response loses type; we've verified entityName exists in the
|
|
37
|
+
// response above (only true for Get/Create/Update prefixes; List/Delete are unstripped and
|
|
38
|
+
// unexercised — see #1461)
|
|
36
39
|
return response[entityName] as ExtractEntityFromResponse<T>;
|
|
37
40
|
}
|
|
38
41
|
|
package/types.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
DescMethodUnary,
|
|
3
|
+
DescService,
|
|
4
|
+
JsonValue,
|
|
5
|
+
Message,
|
|
6
|
+
MessageInitShape,
|
|
7
|
+
MessageShape,
|
|
8
|
+
} from '@bufbuild/protobuf';
|
|
3
9
|
import type { DeploymentService } from './gen/michelangelo/api/v2/deployment_svc_pb';
|
|
4
10
|
import type { InferenceServerService } from './gen/michelangelo/api/v2/inference_server_svc_pb';
|
|
5
11
|
import type { ModelService } from './gen/michelangelo/api/v2/model_svc_pb';
|
|
@@ -13,14 +19,49 @@ export interface RuntimeConfig {
|
|
|
13
19
|
apiBaseUrl: string;
|
|
14
20
|
}
|
|
15
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Shape of a `google.rpc.Status` error body, as produced by Envoy's
|
|
24
|
+
* grpc_json_transcoder filter when a unary RPC returns a non-OK gRPC status.
|
|
25
|
+
*/
|
|
26
|
+
export interface GoogleRpcStatus {
|
|
27
|
+
code: number;
|
|
28
|
+
message: string;
|
|
29
|
+
details?: unknown[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface FetchTransportOptions {
|
|
33
|
+
/** Base URL of the Envoy-fronted API server, e.g. `https://api.example.com`. */
|
|
34
|
+
baseUrl: string;
|
|
35
|
+
/** Additional headers to send with every request, merged over the static defaults. */
|
|
36
|
+
headers?: Record<string, string>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface FetchTransport {
|
|
40
|
+
/**
|
|
41
|
+
* Calls a unary RPC through Envoy's grpc_json_transcoder by POSTing JSON to
|
|
42
|
+
* `/{serviceName}/{methodName}` and returning the parsed JSON response.
|
|
43
|
+
*/
|
|
44
|
+
callUnary(serviceName: string, methodName: string, request: unknown): Promise<JsonValue>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Maps a service's generated method descriptors to a client object shaped
|
|
49
|
+
* like Connect's `Client<T>` — one async function per unary RPC.
|
|
50
|
+
*/
|
|
51
|
+
export type ServiceClient<T extends DescService> = {
|
|
52
|
+
[K in keyof T['method']]: T['method'][K] extends DescMethodUnary<infer I, infer O>
|
|
53
|
+
? (request: MessageInitShape<I>) => Promise<MessageShape<O>>
|
|
54
|
+
: never;
|
|
55
|
+
};
|
|
56
|
+
|
|
16
57
|
export type Services = {
|
|
17
|
-
DeploymentService:
|
|
18
|
-
InferenceServerService:
|
|
19
|
-
ProjectService:
|
|
20
|
-
PipelineService:
|
|
21
|
-
PipelineRunService:
|
|
22
|
-
TriggerRunService:
|
|
23
|
-
ModelService:
|
|
58
|
+
DeploymentService: ServiceClient<typeof DeploymentService>;
|
|
59
|
+
InferenceServerService: ServiceClient<typeof InferenceServerService>;
|
|
60
|
+
ProjectService: ServiceClient<typeof ProjectService>;
|
|
61
|
+
PipelineService: ServiceClient<typeof PipelineService>;
|
|
62
|
+
PipelineRunService: ServiceClient<typeof PipelineRunService>;
|
|
63
|
+
TriggerRunService: ServiceClient<typeof TriggerRunService>;
|
|
64
|
+
ModelService: ServiceClient<typeof ModelService>;
|
|
24
65
|
};
|
|
25
66
|
|
|
26
67
|
/**
|
package/vite.config.ts
CHANGED
|
@@ -10,13 +10,7 @@ export default defineConfig({
|
|
|
10
10
|
formats: ['es'],
|
|
11
11
|
},
|
|
12
12
|
rollupOptions: {
|
|
13
|
-
external: [
|
|
14
|
-
'react',
|
|
15
|
-
'@bufbuild/protobuf',
|
|
16
|
-
'@connectrpc/connect',
|
|
17
|
-
'@connectrpc/connect-web',
|
|
18
|
-
'@tanstack/react-query',
|
|
19
|
-
],
|
|
13
|
+
external: ['react', '@bufbuild/protobuf', '@tanstack/react-query'],
|
|
20
14
|
},
|
|
21
15
|
outDir: 'dist',
|
|
22
16
|
emptyOutDir: true,
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { ConnectError } from '@connectrpc/connect';
|
|
2
|
-
import { ApplicationError, GrpcStatusCode } from '@michelangelo-ai/core';
|
|
3
|
-
|
|
4
|
-
import type { ErrorNormalizer } from '@michelangelo-ai/core';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Normalizes Connect RPC errors to ApplicationError format
|
|
8
|
-
*
|
|
9
|
-
* @param error - The error to normalize
|
|
10
|
-
* @returns ApplicationError if it's a Connect error, null otherwise
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```ts
|
|
14
|
-
* // Usage in error provider
|
|
15
|
-
* const errorProvider = (
|
|
16
|
-
* <ErrorProvider normalizeError={normalizeConnectError}>
|
|
17
|
-
* {children}
|
|
18
|
-
* </ErrorProvider>
|
|
19
|
-
* );
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
export const normalizeConnectError: ErrorNormalizer = (error: unknown): ApplicationError | null => {
|
|
23
|
-
if (!(error instanceof ConnectError)) {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return new ApplicationError(error.message, mapConnectCodeToGrpc(error.code), {
|
|
28
|
-
source: 'connect-rpc',
|
|
29
|
-
meta: {
|
|
30
|
-
connectErrorName: error.name,
|
|
31
|
-
details: error.details,
|
|
32
|
-
metadata: error.metadata,
|
|
33
|
-
},
|
|
34
|
-
cause: error.cause ?? error,
|
|
35
|
-
});
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Maps Connect RPC status codes to gRPC status codes
|
|
40
|
-
* Connect uses the same numeric codes as gRPC
|
|
41
|
-
*/
|
|
42
|
-
function mapConnectCodeToGrpc(code: number): GrpcStatusCode {
|
|
43
|
-
// Connect uses the same numeric codes as gRPC, so we can map directly
|
|
44
|
-
switch (code) {
|
|
45
|
-
case 0:
|
|
46
|
-
return GrpcStatusCode.OK;
|
|
47
|
-
case 1:
|
|
48
|
-
return GrpcStatusCode.CANCELLED;
|
|
49
|
-
case 2:
|
|
50
|
-
return GrpcStatusCode.UNKNOWN;
|
|
51
|
-
case 3:
|
|
52
|
-
return GrpcStatusCode.INVALID_ARGUMENT;
|
|
53
|
-
case 4:
|
|
54
|
-
return GrpcStatusCode.DEADLINE_EXCEEDED;
|
|
55
|
-
case 5:
|
|
56
|
-
return GrpcStatusCode.NOT_FOUND;
|
|
57
|
-
case 6:
|
|
58
|
-
return GrpcStatusCode.ALREADY_EXISTS;
|
|
59
|
-
case 7:
|
|
60
|
-
return GrpcStatusCode.PERMISSION_DENIED;
|
|
61
|
-
case 8:
|
|
62
|
-
return GrpcStatusCode.RESOURCE_EXHAUSTED;
|
|
63
|
-
case 9:
|
|
64
|
-
return GrpcStatusCode.FAILED_PRECONDITION;
|
|
65
|
-
case 10:
|
|
66
|
-
return GrpcStatusCode.ABORTED;
|
|
67
|
-
case 11:
|
|
68
|
-
return GrpcStatusCode.OUT_OF_RANGE;
|
|
69
|
-
case 12:
|
|
70
|
-
return GrpcStatusCode.UNIMPLEMENTED;
|
|
71
|
-
case 13:
|
|
72
|
-
return GrpcStatusCode.INTERNAL;
|
|
73
|
-
case 14:
|
|
74
|
-
return GrpcStatusCode.UNAVAILABLE;
|
|
75
|
-
case 15:
|
|
76
|
-
return GrpcStatusCode.DATA_LOSS;
|
|
77
|
-
case 16:
|
|
78
|
-
return GrpcStatusCode.UNAUTHENTICATED;
|
|
79
|
-
default:
|
|
80
|
-
return GrpcStatusCode.UNKNOWN;
|
|
81
|
-
}
|
|
82
|
-
}
|