@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.
- package/README.md +214 -92
- package/dist/define-event.d.ts +7 -7
- package/dist/define-event.d.ts.map +1 -1
- package/dist/define-event.js +21 -10
- package/dist/define-event.js.map +1 -6
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -6
- package/dist/protocol/create-service-protocol.d.ts +20 -20
- package/dist/protocol/create-service-protocol.d.ts.map +1 -1
- package/dist/protocol/create-service-protocol.js +150 -112
- package/dist/protocol/create-service-protocol.js.map +1 -6
- package/dist/protocol/protocol.types.d.ts +18 -26
- package/dist/protocol/protocol.types.d.ts.map +1 -1
- package/dist/protocol/protocol.types.js +16 -15
- package/dist/protocol/protocol.types.js.map +1 -6
- package/dist/service-types/auto-update-service.types.d.ts +5 -5
- package/dist/service-types/auto-update-service.types.js +2 -1
- package/dist/service-types/auto-update-service.types.js.map +1 -6
- package/dist/service-types/orm-service.types.d.ts +7 -5
- package/dist/service-types/orm-service.types.d.ts.map +1 -1
- package/dist/service-types/orm-service.types.js +2 -1
- package/dist/service-types/orm-service.types.js.map +1 -6
- package/dist/types.d.ts +5 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -1
- package/dist/types.js.map +1 -6
- package/package.json +9 -8
- package/src/define-event.ts +7 -7
- package/src/index.ts +4 -6
- package/src/protocol/create-service-protocol.ts +48 -40
- package/src/protocol/protocol.types.ts +35 -46
- package/src/service-types/auto-update-service.types.ts +5 -5
- package/src/service-types/orm-service.types.ts +5 -5
- package/src/types.ts +5 -5
- package/dist/service-types/smtp-client-service.types.d.ts +0 -38
- package/dist/service-types/smtp-client-service.types.d.ts.map +0 -1
- package/dist/service-types/smtp-client-service.types.js +0 -1
- package/dist/service-types/smtp-client-service.types.js.map +0 -6
- package/docs/events.md +0 -51
- package/docs/protocol.md +0 -252
- package/docs/service-types.md +0 -162
- package/src/service-types/smtp-client-service.types.ts +0 -41
- package/tests/define-event.spec.ts +0 -11
- 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
|
-
```
|
package/docs/service-types.md
DELETED
|
@@ -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
|
-
});
|