@rocketmq/core 0.1.1 → 0.1.3
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/.turbo/turbo-build.log +10 -8
- package/CHANGELOG.md +14 -0
- package/README.md +83 -1
- package/dist/index.cjs +4829 -163
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +255 -33
- package/dist/index.d.ts +255 -33
- package/dist/index.js +4825 -165
- package/dist/index.js.map +1 -0
- package/package.json +6 -5
- package/src/client.test.ts +256 -13
- package/src/client.ts +195 -81
- package/src/error-codes.ts +30 -0
- package/src/error-parser.test.ts +223 -0
- package/src/error-parser.ts +189 -0
- package/src/errors.test.ts +14 -11
- package/src/errors.ts +40 -3
- package/src/index.ts +31 -1
- package/src/queue-handle.test.ts +14 -140
- package/src/queue-handle.ts +8 -66
- package/src/schema-resolver.test.ts +112 -0
- package/src/schema-resolver.ts +99 -0
- package/tsup.config.ts +1 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for schema-resolver — tri-input proto3 resolution.
|
|
3
|
+
*
|
|
4
|
+
* Verifies that resolveProto handles Constructor (decorator), ZodSchemaInput
|
|
5
|
+
* (wrapper), and raw ZodObject paths. Also tests queueNameToMessageName and
|
|
6
|
+
* isConstructorInput discrimination.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, expect, it } from 'vitest';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import { Schema, Field } from '@rocketmq/schema';
|
|
12
|
+
import {
|
|
13
|
+
resolveProto,
|
|
14
|
+
isConstructorInput,
|
|
15
|
+
queueNameToMessageName,
|
|
16
|
+
type SchemaInput,
|
|
17
|
+
} from './schema-resolver.js';
|
|
18
|
+
|
|
19
|
+
describe('resolveProto', () => {
|
|
20
|
+
it('resolves a decorator class to proto3', () => {
|
|
21
|
+
@Schema()
|
|
22
|
+
class ResolverTest {
|
|
23
|
+
@Field()
|
|
24
|
+
id!: string;
|
|
25
|
+
}
|
|
26
|
+
new ResolverTest();
|
|
27
|
+
|
|
28
|
+
const result = resolveProto(ResolverTest, 'test-q');
|
|
29
|
+
expect(result.messageName).toBe('ResolverTest');
|
|
30
|
+
expect(result.proto).toContain('syntax = "proto3"');
|
|
31
|
+
expect(result.proto).toContain('message ResolverTest {');
|
|
32
|
+
expect(result.proto).toContain('string id = 1;');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('resolves a ZodSchemaInput wrapper to proto3', () => {
|
|
36
|
+
const zodInput = {
|
|
37
|
+
name: 'ZodOrder',
|
|
38
|
+
schema: z.object({
|
|
39
|
+
id: z.string(),
|
|
40
|
+
qty: z.number().int(),
|
|
41
|
+
}),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const result = resolveProto(zodInput, 'orders');
|
|
45
|
+
expect(result.messageName).toBe('ZodOrder');
|
|
46
|
+
expect(result.proto).toContain('message ZodOrder {');
|
|
47
|
+
expect(result.proto).toContain('string id = 1;');
|
|
48
|
+
expect(result.proto).toContain('int32 qty = 2;');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('resolves a raw ZodObject using queue name as message name', () => {
|
|
52
|
+
const schema = z.object({
|
|
53
|
+
title: z.string(),
|
|
54
|
+
count: z.number().int(),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const result = resolveProto(schema, 'my-events');
|
|
58
|
+
expect(result.messageName).toBe('MyEvents');
|
|
59
|
+
expect(result.proto).toContain('message MyEvents {');
|
|
60
|
+
expect(result.proto).toContain('string title = 1;');
|
|
61
|
+
expect(result.proto).toContain('int32 count = 2;');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('produces different proto for different Zod schemas', () => {
|
|
65
|
+
const input1 = { name: 'A', schema: z.object({ x: z.string() }) };
|
|
66
|
+
const input2 = { name: 'B', schema: z.object({ y: z.number() }) };
|
|
67
|
+
|
|
68
|
+
expect(resolveProto(input1, 'q').proto).not.toBe(resolveProto(input2, 'q').proto);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('queueNameToMessageName', () => {
|
|
73
|
+
it('converts kebab-case to PascalCase', () => {
|
|
74
|
+
expect(queueNameToMessageName('my-queue')).toBe('MyQueue');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('converts snake_case to PascalCase', () => {
|
|
78
|
+
expect(queueNameToMessageName('order_items')).toBe('OrderItems');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('converts dot-separated to PascalCase', () => {
|
|
82
|
+
expect(queueNameToMessageName('user.events')).toBe('UserEvents');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('handles single word', () => {
|
|
86
|
+
expect(queueNameToMessageName('orders')).toBe('Orders');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('handles already PascalCase', () => {
|
|
90
|
+
expect(queueNameToMessageName('Orders')).toBe('Orders');
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('isConstructorInput', () => {
|
|
95
|
+
it('returns true for a class constructor', () => {
|
|
96
|
+
class MyClass {}
|
|
97
|
+
expect(isConstructorInput(MyClass as SchemaInput)).toBe(true);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('returns false for a ZodSchemaInput wrapper', () => {
|
|
101
|
+
const zodInput = {
|
|
102
|
+
name: 'Test',
|
|
103
|
+
schema: z.object({ id: z.string() }),
|
|
104
|
+
};
|
|
105
|
+
expect(isConstructorInput(zodInput as SchemaInput)).toBe(false);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('returns false for a raw ZodObject', () => {
|
|
109
|
+
const raw = z.object({ id: z.string() });
|
|
110
|
+
expect(isConstructorInput(raw as SchemaInput)).toBe(false);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves proto3 definitions from decorator classes, Zod wrappers, or raw ZodObjects.
|
|
3
|
+
*
|
|
4
|
+
* Centralizes the tri-input logic so client.ts and queue-handle.ts stay
|
|
5
|
+
* clean — they just call `resolveProto(input, queueName)` without caring
|
|
6
|
+
* which path produced it.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* resolveProto(NotificationClass, 'q');
|
|
10
|
+
* resolveProto({ name: 'Notif', schema: zodSchema }, 'q');
|
|
11
|
+
* resolveProto(zodSchema, 'q'); // message name derived from queue name
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { toProto } from '@rocketmq/protobuf';
|
|
15
|
+
import type { Constructor } from '@rocketmq/schema';
|
|
16
|
+
import { isRawZodObject, isZodSchemaInput, zodToProto, type ZodSchemaInput } from '@rocketmq/zod';
|
|
17
|
+
import { z } from 'zod';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Union type accepted by assertQueue/consume as the schema parameter.
|
|
21
|
+
*
|
|
22
|
+
* Three shapes:
|
|
23
|
+
* - `Constructor<T>` — decorator class
|
|
24
|
+
* - `ZodSchemaInput` — `{ name, schema }` wrapper with explicit message name
|
|
25
|
+
* - `z.ZodType<T>` — bare Zod schema, message name derived from queue name
|
|
26
|
+
*
|
|
27
|
+
* WHY z.ZodType<T> instead of z.ZodObject: ZodObject<ZodRawShape> erases T,
|
|
28
|
+
* so TS can't infer the message type in consume(). ZodType<T> preserves
|
|
29
|
+
* the output type. Runtime still validates it's a ZodObject via isRawZodObject.
|
|
30
|
+
*/
|
|
31
|
+
export type SchemaInput<T = unknown> = Constructor<T> | ZodSchemaInput | z.ZodType<T>;
|
|
32
|
+
|
|
33
|
+
/** Result of resolving a SchemaInput to proto3. */
|
|
34
|
+
export interface ResolvedSchema {
|
|
35
|
+
/** Full proto3 definition string for AMQP x-schema argument. */
|
|
36
|
+
proto: string;
|
|
37
|
+
/** Message name for AMQP x-schema-message argument. */
|
|
38
|
+
messageName: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Converts a kebab/snake queue name to PascalCase for the proto message name.
|
|
43
|
+
*
|
|
44
|
+
* WHY: protobuf message names must be PascalCase identifiers.
|
|
45
|
+
* `zod-notifications` → `ZodNotifications`, `order_items` → `OrderItems`.
|
|
46
|
+
*
|
|
47
|
+
* Usage:
|
|
48
|
+
* queueNameToMessageName('my-queue') // => 'MyQueue'
|
|
49
|
+
*/
|
|
50
|
+
export function queueNameToMessageName(queueName: string): string {
|
|
51
|
+
return queueName
|
|
52
|
+
.split(/[-_.]/)
|
|
53
|
+
.map((seg) => seg.charAt(0).toUpperCase() + seg.slice(1))
|
|
54
|
+
.join('');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Produces proto3 + message name from a Constructor, ZodSchemaInput, or raw ZodObject.
|
|
59
|
+
*
|
|
60
|
+
* When a raw ZodObject is passed, `fallbackName` is converted to PascalCase
|
|
61
|
+
* and used as the protobuf message name.
|
|
62
|
+
*
|
|
63
|
+
* Usage:
|
|
64
|
+
* resolveProto(MyClass, 'orders');
|
|
65
|
+
* resolveProto({ name: 'Order', schema: zodObj }, 'orders');
|
|
66
|
+
* resolveProto(zodObj, 'orders'); // message name = "Orders"
|
|
67
|
+
*/
|
|
68
|
+
export function resolveProto(input: SchemaInput, fallbackName: string): ResolvedSchema {
|
|
69
|
+
if (isZodSchemaInput(input)) {
|
|
70
|
+
return {
|
|
71
|
+
proto: zodToProto(input.name, input.schema),
|
|
72
|
+
messageName: input.name,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (isRawZodObject(input)) {
|
|
77
|
+
const messageName = queueNameToMessageName(fallbackName);
|
|
78
|
+
return {
|
|
79
|
+
proto: zodToProto(messageName, input),
|
|
80
|
+
messageName,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const ctor = input as Constructor;
|
|
85
|
+
return {
|
|
86
|
+
proto: toProto(ctor),
|
|
87
|
+
messageName: ctor.name,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Type guard: returns true if the input is a class constructor (not Zod).
|
|
93
|
+
*
|
|
94
|
+
* Used by the client to conditionally register decorator metadata
|
|
95
|
+
* (Zod schemas don't have constructors to register).
|
|
96
|
+
*/
|
|
97
|
+
export function isConstructorInput(input: SchemaInput): input is Constructor {
|
|
98
|
+
return !isZodSchemaInput(input) && !isRawZodObject(input);
|
|
99
|
+
}
|