@simplysm/service-common 13.0.100 → 14.0.4

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.
Files changed (46) hide show
  1. package/README.md +214 -92
  2. package/dist/define-event.d.ts +7 -7
  3. package/dist/define-event.d.ts.map +1 -1
  4. package/dist/define-event.js +21 -10
  5. package/dist/define-event.js.map +1 -6
  6. package/dist/index.d.ts +0 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +5 -2
  9. package/dist/index.js.map +1 -6
  10. package/dist/protocol/create-service-protocol.d.ts +20 -20
  11. package/dist/protocol/create-service-protocol.d.ts.map +1 -1
  12. package/dist/protocol/create-service-protocol.js +150 -112
  13. package/dist/protocol/create-service-protocol.js.map +1 -6
  14. package/dist/protocol/protocol.types.d.ts +18 -26
  15. package/dist/protocol/protocol.types.d.ts.map +1 -1
  16. package/dist/protocol/protocol.types.js +16 -15
  17. package/dist/protocol/protocol.types.js.map +1 -6
  18. package/dist/service-types/auto-update-service.types.d.ts +5 -5
  19. package/dist/service-types/auto-update-service.types.js +2 -1
  20. package/dist/service-types/auto-update-service.types.js.map +1 -6
  21. package/dist/service-types/orm-service.types.d.ts +7 -5
  22. package/dist/service-types/orm-service.types.d.ts.map +1 -1
  23. package/dist/service-types/orm-service.types.js +2 -1
  24. package/dist/service-types/orm-service.types.js.map +1 -6
  25. package/dist/types.d.ts +5 -5
  26. package/dist/types.d.ts.map +1 -1
  27. package/dist/types.js +2 -1
  28. package/dist/types.js.map +1 -6
  29. package/package.json +9 -8
  30. package/src/define-event.ts +7 -7
  31. package/src/index.ts +4 -6
  32. package/src/protocol/create-service-protocol.ts +48 -40
  33. package/src/protocol/protocol.types.ts +35 -46
  34. package/src/service-types/auto-update-service.types.ts +5 -5
  35. package/src/service-types/orm-service.types.ts +5 -5
  36. package/src/types.ts +5 -5
  37. package/dist/service-types/smtp-client-service.types.d.ts +0 -38
  38. package/dist/service-types/smtp-client-service.types.d.ts.map +0 -1
  39. package/dist/service-types/smtp-client-service.types.js +0 -1
  40. package/dist/service-types/smtp-client-service.types.js.map +0 -6
  41. package/docs/events.md +0 -51
  42. package/docs/protocol.md +0 -252
  43. package/docs/service-types.md +0 -162
  44. package/src/service-types/smtp-client-service.types.ts +0 -41
  45. package/tests/define-event.spec.ts +0 -11
  46. package/tests/protocol/service-protocol.spec.ts +0 -251
package/docs/protocol.md DELETED
@@ -1,252 +0,0 @@
1
- # Protocol
2
-
3
- ## `PROTOCOL_CONFIG`
4
-
5
- Service protocol configuration constants.
6
-
7
- ```typescript
8
- const PROTOCOL_CONFIG = {
9
- MAX_TOTAL_SIZE: 100 * 1024 * 1024, // Max message size (100MB)
10
- SPLIT_MESSAGE_SIZE: 3 * 1024 * 1024, // Chunking threshold (3MB)
11
- CHUNK_SIZE: 300 * 1024, // Chunk size (300KB)
12
- GC_INTERVAL: 10 * 1000, // GC interval (10s)
13
- EXPIRE_TIME: 60 * 1000, // Incomplete message expiry (60s)
14
- } as const;
15
- ```
16
-
17
- ## `ServiceProtocol`
18
-
19
- Service protocol interface for encoding/decoding binary messages.
20
-
21
- ```typescript
22
- interface ServiceProtocol {
23
- encode(uuid: string, message: ServiceMessage): { chunks: Bytes[]; totalSize: number };
24
- decode<T extends ServiceMessage>(bytes: Bytes): ServiceMessageDecodeResult<T>;
25
- dispose(): void;
26
- }
27
- ```
28
-
29
- | Method | Description |
30
- |--------|-------------|
31
- | `encode()` | Encode a message (auto-split if exceeding 3MB) |
32
- | `decode()` | Decode a message (auto-reassemble chunked packets) |
33
- | `dispose()` | Release GC timer and free memory |
34
-
35
- ## `ServiceMessageDecodeResult`
36
-
37
- Message decode result type (union).
38
-
39
- ```typescript
40
- type ServiceMessageDecodeResult<TMessage extends ServiceMessage> =
41
- | { type: "complete"; uuid: string; message: TMessage }
42
- | { type: "progress"; uuid: string; totalSize: number; completedSize: number };
43
- ```
44
-
45
- ## `createServiceProtocol`
46
-
47
- Create a service protocol encoder/decoder. Binary Protocol V2: Header 28 bytes (UUID 16 + TotalSize 8 + Index 4) + JSON body. Auto chunking at 300KB when exceeding 3MB. Max 100MB.
48
-
49
- ```typescript
50
- function createServiceProtocol(): ServiceProtocol;
51
- ```
52
-
53
- ## `ServiceMessage`
54
-
55
- Union type of all service messages.
56
-
57
- ```typescript
58
- type ServiceMessage =
59
- | ServiceReloadMessage
60
- | ServiceRequestMessage
61
- | ServiceAuthMessage
62
- | ServiceProgressMessage
63
- | ServiceResponseMessage
64
- | ServiceErrorMessage
65
- | ServiceAddEventListenerMessage
66
- | ServiceRemoveEventListenerMessage
67
- | ServiceGetEventListenerInfosMessage
68
- | ServiceEmitEventMessage
69
- | ServiceEventMessage;
70
- ```
71
-
72
- ## `ServiceServerMessage`
73
-
74
- Messages sent from server to client.
75
-
76
- ```typescript
77
- type ServiceServerMessage =
78
- | ServiceReloadMessage
79
- | ServiceResponseMessage
80
- | ServiceErrorMessage
81
- | ServiceEventMessage;
82
- ```
83
-
84
- ## `ServiceServerRawMessage`
85
-
86
- Server messages including progress notifications.
87
-
88
- ```typescript
89
- type ServiceServerRawMessage = ServiceProgressMessage | ServiceServerMessage;
90
- ```
91
-
92
- ## `ServiceClientMessage`
93
-
94
- Messages sent from client to server.
95
-
96
- ```typescript
97
- type ServiceClientMessage =
98
- | ServiceRequestMessage
99
- | ServiceAuthMessage
100
- | ServiceAddEventListenerMessage
101
- | ServiceRemoveEventListenerMessage
102
- | ServiceGetEventListenerInfosMessage
103
- | ServiceEmitEventMessage;
104
- ```
105
-
106
- ## `ServiceReloadMessage`
107
-
108
- Server reload command to client.
109
-
110
- ```typescript
111
- interface ServiceReloadMessage {
112
- name: "reload";
113
- body: {
114
- clientName: string | undefined;
115
- changedFileSet: Set<string>;
116
- };
117
- }
118
- ```
119
-
120
- ## `ServiceProgressMessage`
121
-
122
- Server progress notification for chunked messages.
123
-
124
- ```typescript
125
- interface ServiceProgressMessage {
126
- name: "progress";
127
- body: {
128
- totalSize: number;
129
- completedSize: number;
130
- };
131
- }
132
- ```
133
-
134
- ## `ServiceErrorMessage`
135
-
136
- Server error notification.
137
-
138
- ```typescript
139
- interface ServiceErrorMessage {
140
- name: "error";
141
- body: {
142
- name: string;
143
- message: string;
144
- code: string;
145
- stack?: string;
146
- detail?: unknown;
147
- cause?: unknown;
148
- };
149
- }
150
- ```
151
-
152
- ## `ServiceAuthMessage`
153
-
154
- Client authentication message.
155
-
156
- ```typescript
157
- interface ServiceAuthMessage {
158
- name: "auth";
159
- body: string;
160
- }
161
- ```
162
-
163
- ## `ServiceRequestMessage`
164
-
165
- Client service method request.
166
-
167
- ```typescript
168
- interface ServiceRequestMessage {
169
- name: `${string}.${string}`;
170
- body: unknown[];
171
- }
172
- ```
173
-
174
- ## `ServiceResponseMessage`
175
-
176
- Server service method response.
177
-
178
- ```typescript
179
- interface ServiceResponseMessage {
180
- name: "response";
181
- body?: unknown;
182
- }
183
- ```
184
-
185
- ## `ServiceAddEventListenerMessage`
186
-
187
- Client add event listener request.
188
-
189
- ```typescript
190
- interface ServiceAddEventListenerMessage {
191
- name: "evt:add";
192
- body: {
193
- key: string;
194
- name: string;
195
- info: unknown;
196
- };
197
- }
198
- ```
199
-
200
- ## `ServiceRemoveEventListenerMessage`
201
-
202
- Client remove event listener request.
203
-
204
- ```typescript
205
- interface ServiceRemoveEventListenerMessage {
206
- name: "evt:remove";
207
- body: {
208
- key: string;
209
- };
210
- }
211
- ```
212
-
213
- ## `ServiceGetEventListenerInfosMessage`
214
-
215
- Client request for event listener info list.
216
-
217
- ```typescript
218
- interface ServiceGetEventListenerInfosMessage {
219
- name: "evt:gets";
220
- body: {
221
- name: string;
222
- };
223
- }
224
- ```
225
-
226
- ## `ServiceEmitEventMessage`
227
-
228
- Client emit event request.
229
-
230
- ```typescript
231
- interface ServiceEmitEventMessage {
232
- name: "evt:emit";
233
- body: {
234
- keys: string[];
235
- data: unknown;
236
- };
237
- }
238
- ```
239
-
240
- ## `ServiceEventMessage`
241
-
242
- Server event notification.
243
-
244
- ```typescript
245
- interface ServiceEventMessage {
246
- name: "evt:on";
247
- body: {
248
- keys: string[];
249
- data: unknown;
250
- };
251
- }
252
- ```
@@ -1,162 +0,0 @@
1
- # Service Types
2
-
3
- ## `OrmService`
4
-
5
- ORM service interface. Provides database connection, transaction management, and query execution over the service protocol. Supports MySQL, MSSQL, and PostgreSQL.
6
-
7
- ```typescript
8
- interface OrmService {
9
- getInfo(opt: DbConnOptions & { configName: string }): Promise<{
10
- dialect: Dialect;
11
- database?: string;
12
- schema?: string;
13
- }>;
14
- connect(opt: Record<string, unknown>): Promise<number>;
15
- close(connId: number): Promise<void>;
16
- beginTransaction(connId: number, isolationLevel?: IsolationLevel): Promise<void>;
17
- commitTransaction(connId: number): Promise<void>;
18
- rollbackTransaction(connId: number): Promise<void>;
19
- executeParametrized(connId: number, query: string, params?: unknown[]): Promise<unknown[][]>;
20
- executeDefs(connId: number, defs: QueryDef[], options?: (ResultMeta | undefined)[]): Promise<unknown[][]>;
21
- bulkInsert(connId: number, tableName: string, columnDefs: Record<string, ColumnMeta>, records: Record<string, unknown>[]): Promise<void>;
22
- }
23
- ```
24
-
25
- | Method | Description |
26
- |--------|-------------|
27
- | `getInfo()` | Get database dialect and connection info |
28
- | `connect()` | Open a new database connection, returns connection ID |
29
- | `close()` | Close a database connection |
30
- | `beginTransaction()` | Begin transaction with optional isolation level |
31
- | `commitTransaction()` | Commit transaction |
32
- | `rollbackTransaction()` | Rollback transaction |
33
- | `executeParametrized()` | Execute a parameterized SQL query |
34
- | `executeDefs()` | Execute QueryDef array |
35
- | `bulkInsert()` | Bulk insert records |
36
-
37
- ## `DbConnOptions`
38
-
39
- Database connection options.
40
-
41
- ```typescript
42
- type DbConnOptions = { configName?: string; config?: Record<string, unknown> };
43
- ```
44
-
45
- | Field | Type | Description |
46
- |-------|------|-------------|
47
- | `configName` | `string` | Configuration name to look up from server config |
48
- | `config` | `Record<string, unknown>` | Direct connection config object |
49
-
50
- ## `AutoUpdateService`
51
-
52
- Auto-update service interface. Retrieves the latest version info for client applications.
53
-
54
- ```typescript
55
- interface AutoUpdateService {
56
- getLastVersion(platform: string): Promise<
57
- | { version: string; downloadPath: string }
58
- | undefined
59
- >;
60
- }
61
- ```
62
-
63
- | Method | Description |
64
- |--------|-------------|
65
- | `getLastVersion()` | Retrieve latest version info for a platform (e.g., "win32", "darwin", "android") |
66
-
67
- ## `SmtpClientSendOption`
68
-
69
- Full SMTP send options.
70
-
71
- ```typescript
72
- interface SmtpClientSendOption {
73
- host: string;
74
- port?: number;
75
- secure?: boolean;
76
- user?: string;
77
- pass?: string;
78
- from: string;
79
- to: string;
80
- cc?: string;
81
- bcc?: string;
82
- subject: string;
83
- html: string;
84
- attachments?: SmtpClientSendAttachment[];
85
- }
86
- ```
87
-
88
- | Field | Type | Description |
89
- |-------|------|-------------|
90
- | `host` | `string` | SMTP server hostname |
91
- | `port` | `number` | SMTP server port |
92
- | `secure` | `boolean` | Use TLS |
93
- | `user` | `string` | SMTP auth username |
94
- | `pass` | `string` | SMTP auth password |
95
- | `from` | `string` | Sender email |
96
- | `to` | `string` | Recipient email |
97
- | `cc` | `string` | CC recipients |
98
- | `bcc` | `string` | BCC recipients |
99
- | `subject` | `string` | Email subject |
100
- | `html` | `string` | Email body (HTML) |
101
- | `attachments` | `SmtpClientSendAttachment[]` | File attachments |
102
-
103
- ## `SmtpClientSendByDefaultOption`
104
-
105
- SMTP send options using default server configuration.
106
-
107
- ```typescript
108
- interface SmtpClientSendByDefaultOption {
109
- to: string;
110
- cc?: string;
111
- bcc?: string;
112
- subject: string;
113
- html: string;
114
- attachments?: SmtpClientSendAttachment[];
115
- }
116
- ```
117
-
118
- ## `SmtpClientSendAttachment`
119
-
120
- Email attachment definition.
121
-
122
- ```typescript
123
- interface SmtpClientSendAttachment {
124
- filename: string;
125
- content?: string | Uint8Array;
126
- path?: any;
127
- contentType?: string;
128
- }
129
- ```
130
-
131
- | Field | Type | Description |
132
- |-------|------|-------------|
133
- | `filename` | `string` | Attachment filename |
134
- | `content` | `string \| Uint8Array` | Inline content |
135
- | `path` | `any` | File path |
136
- | `contentType` | `string` | MIME type |
137
-
138
- ## `SmtpClientDefaultOptions`
139
-
140
- Default SMTP client configuration.
141
-
142
- ```typescript
143
- interface SmtpClientDefaultOptions {
144
- senderName: string;
145
- senderEmail?: string;
146
- user?: string;
147
- pass?: string;
148
- host: string;
149
- port?: number;
150
- secure?: boolean;
151
- }
152
- ```
153
-
154
- | Field | Type | Description |
155
- |-------|------|-------------|
156
- | `senderName` | `string` | Default sender display name |
157
- | `senderEmail` | `string` | Default sender email |
158
- | `user` | `string` | SMTP auth username |
159
- | `pass` | `string` | SMTP auth password |
160
- | `host` | `string` | SMTP server hostname |
161
- | `port` | `number` | SMTP server port |
162
- | `secure` | `boolean` | Use TLS |
@@ -1,41 +0,0 @@
1
- export interface SmtpClientSendAttachment {
2
- filename: string;
3
- content?: string | Uint8Array;
4
- path?: any;
5
- contentType?: string;
6
- }
7
-
8
- export interface SmtpClientSendByDefaultOption {
9
- to: string;
10
- cc?: string;
11
- bcc?: string;
12
- subject: string;
13
- html: string;
14
- attachments?: SmtpClientSendAttachment[];
15
- }
16
-
17
- export interface SmtpClientSendOption {
18
- host: string;
19
- port?: number;
20
- secure?: boolean;
21
- user?: string;
22
- pass?: string;
23
-
24
- from: string;
25
- to: string;
26
- cc?: string;
27
- bcc?: string;
28
- subject: string;
29
- html: string;
30
- attachments?: SmtpClientSendAttachment[];
31
- }
32
-
33
- export interface SmtpClientDefaultOptions {
34
- senderName: string;
35
- senderEmail?: string;
36
- user?: string;
37
- pass?: string;
38
- host: string;
39
- port?: number;
40
- secure?: boolean;
41
- }
@@ -1,11 +0,0 @@
1
- // packages/service-common/tests/define-event.spec.ts
2
- import { describe, it, expect } from "vitest";
3
- import { defineEvent } from "@simplysm/service-common";
4
-
5
- describe("defineEvent", () => {
6
- it("create event definition with given name", () => {
7
- const evt = defineEvent<{ channel: string }, string>("OrderUpdated");
8
- expect(evt.eventName).toBe("OrderUpdated");
9
- });
10
-
11
- });
@@ -1,251 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
- import { createServiceProtocol, type ServiceProtocol } from "../../src/protocol/create-service-protocol";
3
- import type { ServiceMessage } from "../../src/protocol/protocol.types";
4
- import { Uuid } from "@simplysm/core-common";
5
-
6
- describe("ServiceProtocol", () => {
7
- let protocol: ServiceProtocol;
8
-
9
- beforeEach(() => {
10
- protocol = createServiceProtocol();
11
- });
12
-
13
- afterEach(() => {
14
- protocol.dispose();
15
- });
16
-
17
- describe("Encoding", () => {
18
- it("encode single message", () => {
19
- const uuid = Uuid.generate().toString();
20
- const message: ServiceMessage = { name: "test.method", body: [{ test: "data" }] };
21
-
22
- const result = protocol.encode(uuid, message);
23
-
24
- expect(result.chunks.length).toBe(1);
25
- expect(result.totalSize).toBeGreaterThan(0);
26
- });
27
-
28
- it("throw error when message exceeds 100MB", () => {
29
- const uuid = Uuid.generate().toString();
30
- // Generate data larger than 100MB
31
- const largeData = "x".repeat(101 * 1024 * 1024);
32
- const message: ServiceMessage = { name: "test.method", body: [largeData] };
33
-
34
- expect(() => protocol.encode(uuid, message)).toThrow("Message size exceeds the limit.");
35
- });
36
- });
37
-
38
- describe("Decoding", () => {
39
- it("decode single message", () => {
40
- const uuid = Uuid.generate().toString();
41
- const message: ServiceMessage = { name: "test.method", body: [{ value: 123 }] };
42
-
43
- const encoded = protocol.encode(uuid, message);
44
- const result = protocol.decode(encoded.chunks[0]);
45
-
46
- expect(result.type).toBe("complete");
47
- if (result.type === "complete") {
48
- expect(result.message.name).toBe("test.method");
49
- expect(result.message.body).toEqual([{ value: 123 }]);
50
- }
51
- });
52
-
53
- it("throw error when buffer size is smaller than header size", () => {
54
- const smallBytes = new Uint8Array(20);
55
-
56
- expect(() => protocol.decode(smallBytes)).toThrow("Buffer size is smaller than header size.");
57
- });
58
-
59
- it("throw error when decoded message exceeds 100MB", () => {
60
- // Manually create header with totalSize exceeding 100MB
61
- const headerBytes = new Uint8Array(28);
62
- const uuidBytes = new Uuid(Uuid.generate().toString()).toBytes();
63
- headerBytes.set(uuidBytes, 0);
64
-
65
- const headerView = new DataView(
66
- headerBytes.buffer,
67
- headerBytes.byteOffset,
68
- headerBytes.byteLength,
69
- );
70
- headerView.setBigUint64(16, BigInt(101 * 1024 * 1024), false); // 101MB
71
- headerView.setUint32(24, 0, false);
72
-
73
- expect(() => protocol.decode(headerBytes)).toThrow("Message size exceeds the limit.");
74
- });
75
- });
76
-
77
- describe("Chunking", () => {
78
- it("chunk message larger than 3MB", () => {
79
- const uuid = Uuid.generate().toString();
80
- // Create 4MB data
81
- const largeData = "x".repeat(4 * 1024 * 1024);
82
- const message: ServiceMessage = { name: "test.method", body: [largeData] };
83
-
84
- const result = protocol.encode(uuid, message);
85
-
86
- expect(result.chunks.length).toBeGreaterThan(1);
87
- });
88
-
89
- it("assemble chunked message in order", () => {
90
- const uuid = Uuid.generate().toString();
91
- // 4MB data
92
- const largeData = "x".repeat(4 * 1024 * 1024);
93
- const message: ServiceMessage = { name: "test.method", body: [largeData] };
94
-
95
- const encoded = protocol.encode(uuid, message);
96
- expect(encoded.chunks.length).toBeGreaterThan(1);
97
-
98
- // Decode chunks in order
99
- let result!: ReturnType<typeof protocol.decode>;
100
- for (let i = 0; i < encoded.chunks.length; i++) {
101
- result = protocol.decode(encoded.chunks[i]);
102
- if (i < encoded.chunks.length - 1) {
103
- expect(result.type).toBe("progress");
104
- }
105
- }
106
-
107
- expect(result.type).toBe("complete");
108
- if (result.type === "complete") {
109
- expect(result.message.body).toEqual([largeData]);
110
- }
111
- });
112
-
113
- it("assemble chunked message in reverse order", () => {
114
- const uuid = Uuid.generate().toString();
115
- // 4MB data
116
- const largeData = "x".repeat(4 * 1024 * 1024);
117
- const message: ServiceMessage = { name: "test.method", body: [largeData] };
118
-
119
- const encoded = protocol.encode(uuid, message);
120
- const reversedChunks = [...encoded.chunks].reverse();
121
-
122
- // Decode in reverse order
123
- let result!: ReturnType<typeof protocol.decode>;
124
- for (let i = 0; i < reversedChunks.length; i++) {
125
- result = protocol.decode(reversedChunks[i]);
126
- }
127
-
128
- // Should complete at the end
129
- expect(result.type).toBe("complete");
130
- if (result.type === "complete") {
131
- expect(result.message.body).toEqual([largeData]);
132
- }
133
- });
134
-
135
- it("prevent duplicate packets", () => {
136
- const uuid = Uuid.generate().toString();
137
- // 4MB data
138
- const largeData = "x".repeat(4 * 1024 * 1024);
139
- const message: ServiceMessage = { name: "test.method", body: [largeData] };
140
-
141
- const encoded = protocol.encode(uuid, message);
142
-
143
- // Send first chunk twice
144
- protocol.decode(encoded.chunks[0]);
145
- const result1 = protocol.decode(encoded.chunks[0]); // Duplicate
146
-
147
- // Should be in progress state, and completedSize should not increase from duplicate
148
- expect(result1.type).toBe("progress");
149
-
150
- // Send remaining chunks
151
- let result!: ReturnType<typeof protocol.decode>;
152
- for (let i = 1; i < encoded.chunks.length; i++) {
153
- result = protocol.decode(encoded.chunks[i]);
154
- }
155
-
156
- expect(result.type).toBe("complete");
157
- if (result.type === "complete") {
158
- expect(result.message.body).toEqual([largeData]);
159
- }
160
- });
161
- });
162
-
163
- describe("UUID interleaving", () => {
164
- it("receive chunks from multiple UUIDs in interleaved order", () => {
165
- const uuid1 = Uuid.generate().toString();
166
- const uuid2 = Uuid.generate().toString();
167
-
168
- // Each with 4MB data to trigger chunking
169
- const largeData1 = "A".repeat(4 * 1024 * 1024);
170
- const largeData2 = "B".repeat(4 * 1024 * 1024);
171
- const message1: ServiceMessage = { name: "test.method1", body: [largeData1] };
172
- const message2: ServiceMessage = { name: "test.method2", body: [largeData2] };
173
-
174
- const encoded1 = protocol.encode(uuid1, message1);
175
- const encoded2 = protocol.encode(uuid2, message2);
176
-
177
- expect(encoded1.chunks.length).toBeGreaterThan(1);
178
- expect(encoded2.chunks.length).toBeGreaterThan(1);
179
-
180
- // Decode chunks in interleaved order (uuid1[0], uuid2[0], uuid1[1], uuid2[1], ...)
181
- const maxChunks = Math.max(encoded1.chunks.length, encoded2.chunks.length);
182
- let result1!: ReturnType<typeof protocol.decode>;
183
- let result2!: ReturnType<typeof protocol.decode>;
184
-
185
- for (let i = 0; i < maxChunks; i++) {
186
- if (i < encoded1.chunks.length) {
187
- result1 = protocol.decode(encoded1.chunks[i]);
188
- }
189
- if (i < encoded2.chunks.length) {
190
- result2 = protocol.decode(encoded2.chunks[i]);
191
- }
192
- }
193
-
194
- // Both messages should complete
195
- expect(result1.type).toBe("complete");
196
- expect(result2.type).toBe("complete");
197
-
198
- if (result1.type === "complete" && result2.type === "complete") {
199
- expect(result1.message.name).toBe("test.method1");
200
- expect(result1.message.body).toEqual([largeData1]);
201
- expect(result2.message.name).toBe("test.method2");
202
- expect(result2.message.body).toEqual([largeData2]);
203
- }
204
- });
205
-
206
- });
207
-
208
- describe("Edge cases", () => {
209
- it("handle null body", () => {
210
- const uuid = Uuid.generate().toString();
211
- const message: ServiceMessage = { name: "test.method", body: [null] };
212
-
213
- const encoded = protocol.encode(uuid, message);
214
- const decoded = protocol.decode(encoded.chunks[0]);
215
-
216
- expect(decoded.type).toBe("complete");
217
- if (decoded.type === "complete") {
218
- // JsonConvert.stringify/parse converts null to undefined
219
- expect(decoded.message.body).toEqual([undefined]);
220
- }
221
- });
222
-
223
- it("handle message at exactly 3MB boundary", () => {
224
- const uuid = Uuid.generate().toString();
225
- // Exactly 3MB
226
- const data = "x".repeat(3 * 1024 * 1024 - 50); // Account for some JSON overhead
227
- const message: ServiceMessage = { name: "test.method", body: [data] };
228
-
229
- const encoded = protocol.encode(uuid, message);
230
- // Messages up to 3MB should not be chunked
231
- expect(encoded.chunks.length).toBe(1);
232
- });
233
-
234
- it("include correct information in progress response", () => {
235
- const uuid = Uuid.generate().toString();
236
- const largeData = "x".repeat(4 * 1024 * 1024);
237
- const message: ServiceMessage = { name: "test.method", body: [largeData] };
238
-
239
- const encoded = protocol.encode(uuid, message);
240
- const result = protocol.decode(encoded.chunks[0]);
241
-
242
- expect(result.type).toBe("progress");
243
- if (result.type === "progress") {
244
- expect(result.uuid).toBe(uuid);
245
- expect(result.totalSize).toBe(encoded.totalSize);
246
- expect(result.completedSize).toBeGreaterThan(0);
247
- expect(result.completedSize).toBeLessThan(result.totalSize);
248
- }
249
- });
250
- });
251
- });