@glubean/grpc 0.1.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 ADDED
@@ -0,0 +1,166 @@
1
+ # @glubean/grpc
2
+
3
+ gRPC plugin for [Glubean](https://glubean.dev) — workflow-level gRPC testing with built-in tracing.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @glubean/grpc @grpc/grpc-js @grpc/proto-loader
9
+ ```
10
+
11
+ `@grpc/grpc-js` and `@grpc/proto-loader` are peer dependencies — you install them alongside the plugin.
12
+
13
+ ## Quick Start
14
+
15
+ ```ts
16
+ import { test, configure } from "@glubean/sdk";
17
+ import { grpc } from "@glubean/grpc";
18
+
19
+ const { users } = configure({
20
+ plugins: {
21
+ users: grpc({
22
+ proto: "./protos/users.proto",
23
+ address: "{{USER_SERVICE_ADDR}}",
24
+ package: "acme.users.v1",
25
+ service: "UsersService",
26
+ metadata: { authorization: "Bearer {{API_TOKEN}}" },
27
+ }),
28
+ },
29
+ });
30
+
31
+ export const getUser = test("get-user", async (ctx) => {
32
+ const res = await users.call("GetUser", { id: "u_123" });
33
+ ctx.expect(res.status.code).toBe(0);
34
+ ctx.expect(res.message.user.id).toBe("u_123");
35
+ });
36
+ ```
37
+
38
+ ## Standalone (without `configure()`)
39
+
40
+ ```ts
41
+ import { createGrpcClient } from "@glubean/grpc";
42
+
43
+ const client = createGrpcClient({
44
+ proto: "./protos/billing.proto",
45
+ address: "localhost:50051",
46
+ package: "acme.billing.v1",
47
+ service: "BillingService",
48
+ });
49
+
50
+ const res = await client.call("CreateInvoice", {
51
+ customer_id: "cus_123",
52
+ amount_cents: 1200,
53
+ });
54
+
55
+ console.log(res.status.code); // 0 = OK
56
+ console.log(res.message); // decoded response
57
+ console.log(res.duration); // ms
58
+
59
+ client.close();
60
+ ```
61
+
62
+ ## API
63
+
64
+ ### `grpc(options)` — Plugin Factory
65
+
66
+ For use with `configure({ plugins })`. Supports `{{template}}` placeholders in `address` and `metadata` values, resolved from Glubean vars and secrets.
67
+
68
+ | Option | Type | Description |
69
+ |--------|------|-------------|
70
+ | `proto` | `string` | Path to `.proto` file |
71
+ | `address` | `string` | Server address (`host:port`), supports `{{VAR}}` |
72
+ | `package` | `string` | Protobuf package name |
73
+ | `service` | `string` | Service name |
74
+ | `metadata` | `Record<string, string>` | Static metadata, supports `{{VAR}}` |
75
+ | `tls` | `boolean` | Use TLS (default: `false`) |
76
+ | `deadlineMs` | `number` | Default deadline in ms (default: `30000`) |
77
+
78
+ ### `createGrpcClient(options, hooks?)` — Standalone
79
+
80
+ Same options as above (without template support). Optional `hooks` parameter for instrumentation:
81
+
82
+ ```ts
83
+ createGrpcClient(options, {
84
+ event: (ev) => { /* trace event */ },
85
+ });
86
+ ```
87
+
88
+ ### `client.call(method, request, options?)`
89
+
90
+ Make a unary RPC call.
91
+
92
+ ```ts
93
+ const res = await client.call("GetUser", { id: "u_123" }, {
94
+ deadlineMs: 5000,
95
+ metadata: { "x-request-id": "abc" },
96
+ });
97
+ ```
98
+
99
+ Returns `GrpcCallResult`:
100
+
101
+ | Field | Type | Description |
102
+ |-------|------|-------------|
103
+ | `message` | `T` | Decoded response |
104
+ | `status.code` | `number` | gRPC status code (0 = OK) |
105
+ | `status.details` | `string` | Status details |
106
+ | `duration` | `number` | Call duration in ms |
107
+ | `responseMetadata` | `Record<string, string>` | Server response metadata |
108
+
109
+ Errors don't throw — they return with a non-zero `status.code` for assertion-friendly testing.
110
+
111
+ ### `client.close()`
112
+
113
+ Close the underlying gRPC channel.
114
+
115
+ ## Tracing
116
+
117
+ Every RPC call emits a single `trace` event with the full request/response cycle:
118
+
119
+ | Field | Type | Description |
120
+ |-------|------|-------------|
121
+ | `protocol` | `"grpc"` | Protocol discriminator |
122
+ | `target` | `string` | `Service/Method` |
123
+ | `status` | `number` | gRPC status code (0 = OK) |
124
+ | `durationMs` | `number` | Call duration in ms |
125
+ | `ok` | `boolean` | `true` if status is 0 |
126
+ | `service` | `string` | Service name |
127
+ | `method` | `string` | RPC method name |
128
+ | `peer` | `string` | Server address |
129
+ | `request` | `object` | Request payload |
130
+ | `response` | `object` | Response payload (success only) |
131
+ | `metadata` | `object` | Merged request metadata (static + per-call) |
132
+
133
+ These traces share the same event channel as HTTP traces, enabling unified timeline rendering in the Glubean dashboard.
134
+
135
+ ## Auth
136
+
137
+ Static metadata (including auth tokens) is sent with every call:
138
+
139
+ ```ts
140
+ grpc({
141
+ // ...
142
+ metadata: { authorization: "Bearer {{API_TOKEN}}" },
143
+ });
144
+ ```
145
+
146
+ Per-call metadata overrides static values:
147
+
148
+ ```ts
149
+ await client.call("GetUser", { id: "u_123" }, {
150
+ metadata: { authorization: "Bearer per-call-token" },
151
+ });
152
+ ```
153
+
154
+ ## Scope
155
+
156
+ This is a v1 focused on unary RPC. Not included:
157
+
158
+ - Server / client / bidirectional streaming
159
+ - Reflection-based service discovery
160
+ - Static codegen / type generation
161
+ - grpc-web / Connect RPC
162
+ - Retry policies
163
+
164
+ ## License
165
+
166
+ MIT
@@ -0,0 +1,172 @@
1
+ /**
2
+ * gRPC plugin for Glubean tests.
3
+ *
4
+ * Provides a thin wrapper over `@grpc/grpc-js` that simplifies
5
+ * unary gRPC calls with auto-tracing via Glubean events.
6
+ *
7
+ * ## Usage
8
+ *
9
+ * ### As a plugin via `configure()` (recommended)
10
+ *
11
+ * ```ts
12
+ * import { test, configure } from "@glubean/sdk";
13
+ * import { grpc } from "@glubean/grpc";
14
+ *
15
+ * const { users } = configure({
16
+ * plugins: {
17
+ * users: grpc({
18
+ * proto: "./protos/users.proto",
19
+ * address: "{{USER_SERVICE_ADDR}}",
20
+ * package: "acme.users.v1",
21
+ * service: "UsersService",
22
+ * }),
23
+ * },
24
+ * });
25
+ *
26
+ * export const getUser = test("get-user", async (ctx) => {
27
+ * const res = await users.call("GetUser", { id: "u_123" });
28
+ * ctx.expect(res.status.code).toBe(0);
29
+ * ctx.expect(res.message.user.id).toBe("u_123");
30
+ * });
31
+ * ```
32
+ *
33
+ * ### Standalone (without `configure()`)
34
+ *
35
+ * ```ts
36
+ * import { createGrpcClient } from "@glubean/grpc";
37
+ *
38
+ * const client = createGrpcClient({
39
+ * proto: "./protos/users.proto",
40
+ * address: "localhost:50051",
41
+ * package: "acme.users.v1",
42
+ * service: "UsersService",
43
+ * });
44
+ *
45
+ * const res = await client.call("GetUser", { id: "u_123" });
46
+ * ```
47
+ *
48
+ * @module grpc
49
+ */
50
+ import type * as grpcTypes from "@grpc/grpc-js";
51
+ import type { PluginFactory } from "@glubean/sdk";
52
+ /** Options for creating a gRPC client. */
53
+ export interface GrpcClientOptions {
54
+ /** Path to the .proto file */
55
+ proto: string;
56
+ /** gRPC server address (host:port) */
57
+ address: string;
58
+ /** Protobuf package name (e.g., "acme.users.v1") */
59
+ package: string;
60
+ /** Service name within the package (e.g., "UsersService") */
61
+ service: string;
62
+ /** Static metadata sent with every call */
63
+ metadata?: Record<string, string>;
64
+ /** Use TLS credentials. Default: false (insecure) */
65
+ tls?: boolean;
66
+ /** Default deadline in milliseconds for all calls */
67
+ deadlineMs?: number;
68
+ }
69
+ /** gRPC status returned with every call. */
70
+ export interface GrpcStatus {
71
+ /** gRPC status code (0 = OK) */
72
+ code: number;
73
+ /** Human-readable status details */
74
+ details: string;
75
+ }
76
+ /** Result of a unary gRPC call. */
77
+ export interface GrpcCallResult<T = unknown> {
78
+ /** Decoded response message */
79
+ message: T;
80
+ /** gRPC status */
81
+ status: GrpcStatus;
82
+ /** Response metadata from the server */
83
+ responseMetadata: Record<string, string>;
84
+ /** Duration of the call in milliseconds */
85
+ duration: number;
86
+ }
87
+ /** Per-call options. */
88
+ export interface GrpcCallOptions {
89
+ /** Override deadline for this call (ms) */
90
+ deadlineMs?: number;
91
+ /** Additional metadata for this call only */
92
+ metadata?: Record<string, string>;
93
+ }
94
+ /** A gRPC client bound to a specific service. */
95
+ export interface GrpcClient {
96
+ /** Make a unary RPC call. */
97
+ call<T = unknown>(method: string, request: Record<string, unknown>, options?: GrpcCallOptions): Promise<GrpcCallResult<T>>;
98
+ /** Close the underlying channel. */
99
+ close(): void;
100
+ /** The underlying `@grpc/grpc-js` client for direct access (streaming, channel state, etc.). */
101
+ raw: grpcTypes.Client;
102
+ }
103
+ /** Hooks for Glubean runtime instrumentation. */
104
+ export interface GrpcHooks {
105
+ event?: (ev: {
106
+ type: string;
107
+ data: Record<string, unknown>;
108
+ }) => void;
109
+ }
110
+ /**
111
+ * Create a gRPC client bound to a specific service.
112
+ *
113
+ * @param options Client configuration
114
+ * @param hooks Optional Glubean runtime hooks for action/event/log
115
+ * @returns A bound `GrpcClient` instance
116
+ */
117
+ export declare function createGrpcClient(options: GrpcClientOptions, hooks?: GrpcHooks): GrpcClient;
118
+ /**
119
+ * Plugin options for use with `configure()`.
120
+ * `address` supports `{{VAR}}` template syntax.
121
+ * `metadata` values support `{{VAR}}` template syntax.
122
+ */
123
+ export interface GrpcPluginOptions {
124
+ /** Path to the .proto file */
125
+ proto: string;
126
+ /** Var key or `{{template}}` for the gRPC address */
127
+ address: string;
128
+ /** Protobuf package name */
129
+ package: string;
130
+ /** Service name */
131
+ service: string;
132
+ /** Metadata with `{{template}}` support */
133
+ metadata?: Record<string, string>;
134
+ /** Use TLS */
135
+ tls?: boolean;
136
+ /** Default deadline in ms */
137
+ deadlineMs?: number;
138
+ }
139
+ /**
140
+ * Create a gRPC plugin for use with `configure({ plugins })`.
141
+ *
142
+ * Resolves `{{template}}` placeholders in `address` and `metadata` using
143
+ * the Glubean runtime (vars and secrets).
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * import { test, configure } from "@glubean/sdk";
148
+ * import { grpc } from "@glubean/grpc";
149
+ *
150
+ * const { billing } = configure({
151
+ * plugins: {
152
+ * billing: grpc({
153
+ * proto: "./protos/billing.proto",
154
+ * address: "{{BILLING_ADDR}}",
155
+ * package: "acme.billing.v1",
156
+ * service: "BillingService",
157
+ * metadata: { authorization: "Bearer {{API_TOKEN}}" },
158
+ * }),
159
+ * },
160
+ * });
161
+ *
162
+ * export const createInvoice = test("create-invoice", async (ctx) => {
163
+ * const res = await billing.call("CreateInvoice", {
164
+ * customer_id: "cus_123",
165
+ * amount_cents: 1200,
166
+ * });
167
+ * ctx.expect(res.status.code).toBe(0);
168
+ * });
169
+ * ```
170
+ */
171
+ export declare function grpc(options: GrpcPluginOptions): PluginFactory<GrpcClient>;
172
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,273 @@
1
+ /**
2
+ * gRPC plugin for Glubean tests.
3
+ *
4
+ * Provides a thin wrapper over `@grpc/grpc-js` that simplifies
5
+ * unary gRPC calls with auto-tracing via Glubean events.
6
+ *
7
+ * ## Usage
8
+ *
9
+ * ### As a plugin via `configure()` (recommended)
10
+ *
11
+ * ```ts
12
+ * import { test, configure } from "@glubean/sdk";
13
+ * import { grpc } from "@glubean/grpc";
14
+ *
15
+ * const { users } = configure({
16
+ * plugins: {
17
+ * users: grpc({
18
+ * proto: "./protos/users.proto",
19
+ * address: "{{USER_SERVICE_ADDR}}",
20
+ * package: "acme.users.v1",
21
+ * service: "UsersService",
22
+ * }),
23
+ * },
24
+ * });
25
+ *
26
+ * export const getUser = test("get-user", async (ctx) => {
27
+ * const res = await users.call("GetUser", { id: "u_123" });
28
+ * ctx.expect(res.status.code).toBe(0);
29
+ * ctx.expect(res.message.user.id).toBe("u_123");
30
+ * });
31
+ * ```
32
+ *
33
+ * ### Standalone (without `configure()`)
34
+ *
35
+ * ```ts
36
+ * import { createGrpcClient } from "@glubean/grpc";
37
+ *
38
+ * const client = createGrpcClient({
39
+ * proto: "./protos/users.proto",
40
+ * address: "localhost:50051",
41
+ * package: "acme.users.v1",
42
+ * service: "UsersService",
43
+ * });
44
+ *
45
+ * const res = await client.call("GetUser", { id: "u_123" });
46
+ * ```
47
+ *
48
+ * @module grpc
49
+ */
50
+ import { definePlugin } from "@glubean/sdk/plugin";
51
+ let grpcJs;
52
+ let protoLoader;
53
+ try {
54
+ grpcJs = await import("@grpc/grpc-js");
55
+ protoLoader = await import("@grpc/proto-loader");
56
+ }
57
+ catch {
58
+ throw new Error('@glubean/grpc requires "@grpc/grpc-js" and "@grpc/proto-loader" as peer dependencies.\n' +
59
+ "Install them with:\n\n" +
60
+ " npm install @grpc/grpc-js @grpc/proto-loader\n");
61
+ }
62
+ // =============================================================================
63
+ // Client implementation
64
+ // =============================================================================
65
+ /**
66
+ * Resolve a nested package from a loaded proto definition.
67
+ *
68
+ * For example, `resolvePackage(def, "acme.users.v1")` traverses
69
+ * `def.acme.users.v1`.
70
+ */
71
+ function resolvePackage(packageDef, packageName) {
72
+ const parts = packageName.split(".");
73
+ let current = packageDef;
74
+ for (const part of parts) {
75
+ const next = current[part];
76
+ if (!next || typeof next !== "object") {
77
+ throw new Error(`Package "${packageName}" not found in proto definition (failed at "${part}")`);
78
+ }
79
+ current = next;
80
+ }
81
+ return current;
82
+ }
83
+ /**
84
+ * Build gRPC metadata from a plain object.
85
+ */
86
+ function buildMetadata(base, extra) {
87
+ const md = new grpcJs.Metadata();
88
+ if (base) {
89
+ for (const [k, v] of Object.entries(base)) {
90
+ md.set(k, v);
91
+ }
92
+ }
93
+ if (extra) {
94
+ for (const [k, v] of Object.entries(extra)) {
95
+ md.set(k, v);
96
+ }
97
+ }
98
+ return md;
99
+ }
100
+ /**
101
+ * Create a gRPC client bound to a specific service.
102
+ *
103
+ * @param options Client configuration
104
+ * @param hooks Optional Glubean runtime hooks for action/event/log
105
+ * @returns A bound `GrpcClient` instance
106
+ */
107
+ export function createGrpcClient(options, hooks) {
108
+ const packageDefinition = protoLoader.loadSync(options.proto, {
109
+ keepCase: true,
110
+ longs: String,
111
+ enums: String,
112
+ defaults: true,
113
+ oneofs: true,
114
+ });
115
+ const packageDef = grpcJs.loadPackageDefinition(packageDefinition);
116
+ const servicePkg = resolvePackage(packageDef, options.package);
117
+ const ServiceConstructor = servicePkg[options.service];
118
+ if (!ServiceConstructor || typeof ServiceConstructor !== "function") {
119
+ throw new Error(`Service "${options.service}" not found in package "${options.package}"`);
120
+ }
121
+ const credentials = options.tls
122
+ ? grpcJs.credentials.createSsl()
123
+ : grpcJs.credentials.createInsecure();
124
+ const client = new ServiceConstructor(options.address, credentials);
125
+ return {
126
+ call(method, request, callOptions) {
127
+ return new Promise((resolve, reject) => {
128
+ const fn = client[method];
129
+ if (typeof fn !== "function") {
130
+ reject(new Error(`Method "${method}" not found on service "${options.service}"`));
131
+ return;
132
+ }
133
+ const md = buildMetadata(options.metadata, callOptions?.metadata);
134
+ const deadlineMs = callOptions?.deadlineMs ?? options.deadlineMs ?? 30_000;
135
+ const deadline = new Date(Date.now() + deadlineMs);
136
+ const target = `${options.service}/${method}`;
137
+ // Merge static + per-call metadata into a plain object for tracing
138
+ const mergedMetadata = {
139
+ ...options.metadata,
140
+ ...callOptions?.metadata,
141
+ };
142
+ const start = performance.now();
143
+ let responseMetadata = {};
144
+ const call = fn.call(client, request, md, { deadline }, (err, response) => {
145
+ const durationMs = Math.round(performance.now() - start);
146
+ if (err) {
147
+ const status = {
148
+ code: err.code ?? grpcJs.status.UNKNOWN,
149
+ details: err.details ?? err.message,
150
+ };
151
+ // Extract response metadata from error if available
152
+ if (err.metadata) {
153
+ for (const [k, values] of Object.entries(err.metadata.getMap())) {
154
+ responseMetadata[k] = String(values);
155
+ }
156
+ }
157
+ hooks?.event?.({
158
+ type: "trace",
159
+ data: {
160
+ protocol: "grpc",
161
+ target,
162
+ status: status.code,
163
+ durationMs,
164
+ ok: false,
165
+ service: options.service,
166
+ method,
167
+ peer: options.address,
168
+ request,
169
+ metadata: mergedMetadata,
170
+ },
171
+ });
172
+ // Still resolve with status info instead of rejecting
173
+ // This matches Glubean's pattern of returning results for assertions
174
+ resolve({
175
+ message: (response ?? {}),
176
+ status,
177
+ responseMetadata,
178
+ duration: durationMs,
179
+ });
180
+ return;
181
+ }
182
+ hooks?.event?.({
183
+ type: "trace",
184
+ data: {
185
+ protocol: "grpc",
186
+ target,
187
+ status: grpcJs.status.OK,
188
+ durationMs,
189
+ ok: true,
190
+ service: options.service,
191
+ method,
192
+ peer: options.address,
193
+ request,
194
+ response,
195
+ metadata: mergedMetadata,
196
+ },
197
+ });
198
+ resolve({
199
+ message: response,
200
+ status: { code: grpcJs.status.OK, details: "OK" },
201
+ responseMetadata,
202
+ duration: durationMs,
203
+ });
204
+ });
205
+ // Capture response headers metadata
206
+ call.on("metadata", (md) => {
207
+ for (const [k, values] of Object.entries(md.getMap())) {
208
+ responseMetadata[k] = String(values);
209
+ }
210
+ });
211
+ });
212
+ },
213
+ close() {
214
+ client.close();
215
+ },
216
+ raw: client,
217
+ };
218
+ }
219
+ /**
220
+ * Create a gRPC plugin for use with `configure({ plugins })`.
221
+ *
222
+ * Resolves `{{template}}` placeholders in `address` and `metadata` using
223
+ * the Glubean runtime (vars and secrets).
224
+ *
225
+ * @example
226
+ * ```ts
227
+ * import { test, configure } from "@glubean/sdk";
228
+ * import { grpc } from "@glubean/grpc";
229
+ *
230
+ * const { billing } = configure({
231
+ * plugins: {
232
+ * billing: grpc({
233
+ * proto: "./protos/billing.proto",
234
+ * address: "{{BILLING_ADDR}}",
235
+ * package: "acme.billing.v1",
236
+ * service: "BillingService",
237
+ * metadata: { authorization: "Bearer {{API_TOKEN}}" },
238
+ * }),
239
+ * },
240
+ * });
241
+ *
242
+ * export const createInvoice = test("create-invoice", async (ctx) => {
243
+ * const res = await billing.call("CreateInvoice", {
244
+ * customer_id: "cus_123",
245
+ * amount_cents: 1200,
246
+ * });
247
+ * ctx.expect(res.status.code).toBe(0);
248
+ * });
249
+ * ```
250
+ */
251
+ export function grpc(options) {
252
+ return definePlugin((runtime) => {
253
+ const resolvedAddress = runtime.resolveTemplate(options.address);
254
+ const resolvedMetadata = options.metadata
255
+ ? Object.fromEntries(Object.entries(options.metadata).map(([k, v]) => [
256
+ k,
257
+ runtime.resolveTemplate(v),
258
+ ]))
259
+ : undefined;
260
+ return createGrpcClient({
261
+ proto: options.proto,
262
+ address: resolvedAddress,
263
+ package: options.package,
264
+ service: options.service,
265
+ metadata: resolvedMetadata,
266
+ tls: options.tls,
267
+ deadlineMs: options.deadlineMs,
268
+ }, {
269
+ event: (ev) => runtime.event(ev),
270
+ });
271
+ });
272
+ }
273
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@glubean/grpc",
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/index.d.ts",
8
+ "import": "./dist/index.js"
9
+ }
10
+ },
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "dependencies": {
18
+ "@glubean/sdk": "0.1.7"
19
+ },
20
+ "peerDependencies": {
21
+ "@grpc/grpc-js": "^1.12.0",
22
+ "@grpc/proto-loader": "^0.7.0"
23
+ },
24
+ "devDependencies": {
25
+ "@grpc/grpc-js": "^1.14.0",
26
+ "@grpc/proto-loader": "^0.7.0",
27
+ "vitest": "^3.2.1",
28
+ "typescript": "^5.8.3"
29
+ },
30
+ "scripts": {
31
+ "build": "tsc -p tsconfig.build.json",
32
+ "test": "vitest run"
33
+ }
34
+ }