pulse-sdk 0.0.3 → 0.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 +19 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.js +5 -0
- package/dist/consumer.d.ts +2 -0
- package/dist/consumer.js +72 -11
- package/dist/consumerManager.d.ts +4 -0
- package/dist/consumerManager.js +191 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/message.d.ts +24 -0
- package/dist/message.js +77 -0
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -149,6 +149,25 @@ The tests in `tests/` start a lightweight gRPC test server automatically; run th
|
|
|
149
149
|
- Can I use decorators for handlers? Yes — TypeScript supports decorators, but this SDK does not use them by default. We can add decorator helpers later if desired.
|
|
150
150
|
- How do I enable TLS / secure credentials? The client factory currently uses `createInsecure()` by default. We can expose a `credentials` option on `PulseConfig` to accept `grpc.credentials.createSsl(...)` or other credential objects.
|
|
151
151
|
|
|
152
|
+
**Grouped consumption**
|
|
153
|
+
|
|
154
|
+
- **What it does:** When `grouped` is `true` (the default) the SDK coalesces consumers inside the same process that use the same `consumerName` into a single streaming connection. Messages are distributed (round-robin) among the registered handlers so each message is delivered to only one handler in the group (1x per client id).
|
|
155
|
+
- **Why:** This mirrors the Python SDK behavior where handlers registered with `grouped=True` share a single consumer stream and avoid duplicate processing inside the same client.
|
|
156
|
+
- **How to configure:**
|
|
157
|
+
- `pulse.yml` (recommended): set `grouped: true` or `grouped: false` under top-level config.
|
|
158
|
+
- Environment variable: `PULSE_GROUPED=true|false`.
|
|
159
|
+
- Programmatically: pass `grouped` in the `PulseConfig` passed to `Producer`/`Consumer`.
|
|
160
|
+
- **Default:** `grouped` defaults to `true`.
|
|
161
|
+
- **Grouped=false behavior:** When `grouped` is `false`, the SDK will ensure consumers use unique consumer IDs (if you pass the default client name), so multiple consumers in the same process each receive all messages independently (useful for testing or when you want duplicate consumption).
|
|
162
|
+
- **Example `pulse.yml` entry:**
|
|
163
|
+
|
|
164
|
+
```yaml
|
|
165
|
+
grpcUrl: localhost:5556
|
|
166
|
+
grouped: true
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
The test-suite includes integration tests that validate both `grouped=true` and `grouped=false` behaviour.
|
|
170
|
+
|
|
152
171
|
## Contributing
|
|
153
172
|
|
|
154
173
|
Please open a PR with tests. The existing tests validate basic Producer/Consumer behaviour.
|
package/dist/config.d.ts
CHANGED
package/dist/config.js
CHANGED
|
@@ -13,6 +13,7 @@ const client_1 = require("./proto/client");
|
|
|
13
13
|
const DEFAULTS = {
|
|
14
14
|
grpcUrl: 'localhost:50052',
|
|
15
15
|
eventTypes: ['events'],
|
|
16
|
+
grouped: true,
|
|
16
17
|
};
|
|
17
18
|
function loadConfig(configPath) {
|
|
18
19
|
const candidates = [
|
|
@@ -37,10 +38,14 @@ function loadConfig(configPath) {
|
|
|
37
38
|
envCfg.eventTypes = process.env.PULSE_EVENT_TYPES.split(',');
|
|
38
39
|
if (process.env.PULSE_CONSUMER_NAME)
|
|
39
40
|
envCfg.consumerName = process.env.PULSE_CONSUMER_NAME;
|
|
41
|
+
if (process.env.PULSE_GROUPED)
|
|
42
|
+
envCfg.grouped = process.env.PULSE_GROUPED === 'true';
|
|
40
43
|
const merged = Object.assign({}, DEFAULTS, fileCfg, envCfg);
|
|
41
44
|
// Ensure eventTypes array exists
|
|
42
45
|
if (!merged.eventTypes)
|
|
43
46
|
merged.eventTypes = DEFAULTS.eventTypes;
|
|
47
|
+
if (merged.grouped === undefined)
|
|
48
|
+
merged.grouped = true;
|
|
44
49
|
return merged;
|
|
45
50
|
}
|
|
46
51
|
// Initialize topics from config using gRPC CreateTopic RPC.
|
package/dist/consumer.d.ts
CHANGED
|
@@ -4,7 +4,9 @@ export declare class Consumer {
|
|
|
4
4
|
private config;
|
|
5
5
|
private handlers;
|
|
6
6
|
private client;
|
|
7
|
+
private unregisterFns;
|
|
7
8
|
constructor(config: PulseConfig);
|
|
8
9
|
on(eventType: string, handler: EventHandler): void;
|
|
9
10
|
start(topic?: string, consumerName?: string): Promise<void>;
|
|
11
|
+
close(): void;
|
|
10
12
|
}
|
package/dist/consumer.js
CHANGED
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Consumer = void 0;
|
|
4
4
|
const client_1 = require("./proto/client");
|
|
5
|
+
const consumerManager_1 = require("./consumerManager");
|
|
6
|
+
const crypto_1 = require("crypto");
|
|
7
|
+
const message_1 = require("./message");
|
|
5
8
|
class Consumer {
|
|
6
9
|
constructor(config) {
|
|
7
10
|
this.config = config;
|
|
8
11
|
this.handlers = {};
|
|
12
|
+
this.unregisterFns = [];
|
|
9
13
|
this.client = (0, client_1.createClient)(config.grpcUrl);
|
|
10
14
|
}
|
|
11
15
|
on(eventType, handler) {
|
|
@@ -15,24 +19,81 @@ class Consumer {
|
|
|
15
19
|
this.handlers[eventType].push(handler);
|
|
16
20
|
}
|
|
17
21
|
async start(topic, consumerName = 'default') {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
stream
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const topicName = topic || this.config.eventTypes[0];
|
|
23
|
+
// If grouped is enabled (default true) use shared in-process stream,
|
|
24
|
+
// otherwise create a dedicated stream (each consumer gets all messages).
|
|
25
|
+
const grouped = this.config.grouped !== false;
|
|
26
|
+
if (grouped) {
|
|
27
|
+
// register handlers for this topic to shared stream
|
|
28
|
+
const handlers = this.handlers[topicName] || [];
|
|
29
|
+
for (const h of handlers) {
|
|
30
|
+
const unregister = (0, consumerManager_1.registerSharedHandler)(this.config.grpcUrl, topicName, consumerName, (msg, stub, offset) => {
|
|
31
|
+
// run handler with context so commit() works
|
|
32
|
+
// debug: console.log('consumer.wrapper.invoke', consumerName, topicName);
|
|
33
|
+
(0, message_1.runWithContext)({ stub: stub || this.client, topic: topicName, consumerName, offset: offset ?? msg.offset }, () => {
|
|
34
|
+
try {
|
|
35
|
+
h(msg);
|
|
36
|
+
}
|
|
37
|
+
catch (e) { /* ignore */ }
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
this.unregisterFns.push(unregister);
|
|
25
41
|
}
|
|
26
|
-
|
|
27
|
-
|
|
42
|
+
// Return a promise that never resolves (stream runs until process exits)
|
|
43
|
+
return new Promise(() => { });
|
|
44
|
+
}
|
|
45
|
+
// If grouped is explicitly false, and the consumerName equals the configured
|
|
46
|
+
// client name or the default literal, generate a unique consumer id so each
|
|
47
|
+
// consumer receives messages independently (mirrors Python behaviour).
|
|
48
|
+
if (!grouped) {
|
|
49
|
+
const base = this.config.consumerName || 'default';
|
|
50
|
+
if (consumerName === base || consumerName === 'default') {
|
|
51
|
+
consumerName = `${base}-${(0, crypto_1.randomUUID)().replace(/-/g, '')}`;
|
|
28
52
|
}
|
|
53
|
+
}
|
|
54
|
+
const req = { topic: topicName, consumer_name: consumerName, offset: 0 };
|
|
55
|
+
let stream = null;
|
|
56
|
+
try {
|
|
57
|
+
stream = this.client.Consume(req);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
// If the client throws synchronously (e.g. CANCELLED), return a promise
|
|
61
|
+
// that is rejected but handled to avoid an unhandled rejection when
|
|
62
|
+
// callers don't await `start()` (tests call start() without awaiting).
|
|
63
|
+
const p = Promise.reject(err);
|
|
64
|
+
p.catch(() => { }); // swallow to avoid uncaught rejection
|
|
65
|
+
return p;
|
|
66
|
+
}
|
|
67
|
+
stream.on('data', (msg) => {
|
|
68
|
+
const message = new message_1.Message(msg);
|
|
29
69
|
const handlers = this.handlers[req.topic] || [];
|
|
30
|
-
|
|
70
|
+
for (const h of handlers) {
|
|
71
|
+
// run handler within AsyncLocalStorage context so commit() can access stub and offset
|
|
72
|
+
(0, message_1.runWithContext)({ stub: this.client, topic: req.topic, consumerName, offset: message.offset }, () => {
|
|
73
|
+
try {
|
|
74
|
+
h(message);
|
|
75
|
+
}
|
|
76
|
+
catch (e) { /* handler error ignored here */ }
|
|
77
|
+
});
|
|
78
|
+
}
|
|
31
79
|
});
|
|
32
|
-
|
|
80
|
+
const p = new Promise((resolve, reject) => {
|
|
33
81
|
stream.on('end', () => resolve());
|
|
34
82
|
stream.on('error', (e) => reject(e));
|
|
35
83
|
});
|
|
84
|
+
// prevent unhandled rejections when callers don't await the returned promise
|
|
85
|
+
p.catch(() => { });
|
|
86
|
+
return p;
|
|
87
|
+
}
|
|
88
|
+
// unregister any shared handlers when this consumer is discarded
|
|
89
|
+
close() {
|
|
90
|
+
for (const u of this.unregisterFns) {
|
|
91
|
+
try {
|
|
92
|
+
u();
|
|
93
|
+
}
|
|
94
|
+
catch (e) { /* ignore */ }
|
|
95
|
+
}
|
|
96
|
+
this.unregisterFns = [];
|
|
36
97
|
}
|
|
37
98
|
}
|
|
38
99
|
exports.Consumer = Consumer;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.shutdownAll = shutdownAll;
|
|
4
|
+
exports.registerSharedHandler = registerSharedHandler;
|
|
5
|
+
const client_1 = require("./proto/client");
|
|
6
|
+
const message_1 = require("./message");
|
|
7
|
+
let suppressStreamWarnings = false;
|
|
8
|
+
async function shutdownAll() {
|
|
9
|
+
// When shutting down tests/teardown, suppress stream warnings so Jest doesn't
|
|
10
|
+
// complain about logs after tests are finished.
|
|
11
|
+
suppressStreamWarnings = true;
|
|
12
|
+
for (const [k, entry] of Array.from(registry.entries())) {
|
|
13
|
+
try {
|
|
14
|
+
if (entry.stream) {
|
|
15
|
+
try {
|
|
16
|
+
entry.stream.removeAllListeners();
|
|
17
|
+
}
|
|
18
|
+
catch (_) { }
|
|
19
|
+
try {
|
|
20
|
+
if (entry.stream.cancel)
|
|
21
|
+
entry.stream.cancel();
|
|
22
|
+
}
|
|
23
|
+
catch (_) { }
|
|
24
|
+
entry.stream = null;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
entry.handlers.clear();
|
|
28
|
+
}
|
|
29
|
+
catch (_) { }
|
|
30
|
+
}
|
|
31
|
+
catch (_) {
|
|
32
|
+
// ignore individual errors
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
if (entry.client && typeof entry.client.close === 'function')
|
|
36
|
+
entry.client.close();
|
|
37
|
+
}
|
|
38
|
+
catch (_) { }
|
|
39
|
+
try {
|
|
40
|
+
registry.delete(k);
|
|
41
|
+
}
|
|
42
|
+
catch (_) { }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const registry = new Map();
|
|
46
|
+
function keyFor(grpcUrl, topic, consumerName) {
|
|
47
|
+
return `${grpcUrl}::${topic}::${consumerName}`;
|
|
48
|
+
}
|
|
49
|
+
function registerSharedHandler(grpcUrl, topic, consumerName, handler) {
|
|
50
|
+
const k = keyFor(grpcUrl, topic, consumerName);
|
|
51
|
+
let entry = registry.get(k);
|
|
52
|
+
if (!entry) {
|
|
53
|
+
const client = (0, client_1.createClient)(grpcUrl);
|
|
54
|
+
entry = { topic, consumerName, client, stream: null, handlers: new Set(), nextIndex: 0, grpcUrl };
|
|
55
|
+
// add handler before starting the stream to avoid losing early messages
|
|
56
|
+
entry.handlers.add(handler);
|
|
57
|
+
registry.set(k, entry);
|
|
58
|
+
startStream(entry);
|
|
59
|
+
return () => {
|
|
60
|
+
// unregister
|
|
61
|
+
const e = registry.get(k);
|
|
62
|
+
if (!e)
|
|
63
|
+
return;
|
|
64
|
+
e.handlers.delete(handler);
|
|
65
|
+
if (e.handlers.size === 0) {
|
|
66
|
+
try {
|
|
67
|
+
if (e.stream && e.stream.cancel)
|
|
68
|
+
e.stream.cancel();
|
|
69
|
+
}
|
|
70
|
+
catch (err) { }
|
|
71
|
+
registry.delete(k);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
entry.handlers.add(handler);
|
|
76
|
+
return () => {
|
|
77
|
+
// unregister
|
|
78
|
+
const e = registry.get(k);
|
|
79
|
+
if (!e)
|
|
80
|
+
return;
|
|
81
|
+
e.handlers.delete(handler);
|
|
82
|
+
if (e.handlers.size === 0) {
|
|
83
|
+
// cleanup stream
|
|
84
|
+
try {
|
|
85
|
+
if (e.stream && e.stream.cancel)
|
|
86
|
+
e.stream.cancel();
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
// ignore
|
|
90
|
+
}
|
|
91
|
+
registry.delete(k);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function startStream(entry) {
|
|
96
|
+
const req = { topic: entry.topic, consumer_name: entry.consumerName, offset: 0 };
|
|
97
|
+
const stream = entry.client.Consume(req);
|
|
98
|
+
entry.stream = stream;
|
|
99
|
+
// debug: console.log('consumerManager.startStream', entry.grpcUrl, entry.topic, entry.consumerName);
|
|
100
|
+
stream.on('data', (msg) => {
|
|
101
|
+
const message = new message_1.Message(msg);
|
|
102
|
+
const handlers = Array.from(entry.handlers);
|
|
103
|
+
if (handlers.length === 0)
|
|
104
|
+
return;
|
|
105
|
+
if (entry.nextIndex === undefined)
|
|
106
|
+
entry.nextIndex = 0;
|
|
107
|
+
const h = handlers[entry.nextIndex % handlers.length];
|
|
108
|
+
entry.nextIndex = (entry.nextIndex + 1) % handlers.length;
|
|
109
|
+
try {
|
|
110
|
+
// run handler inside context providing the stub for commit()
|
|
111
|
+
// debug: console.log('consumerManager.dispatch', entry.topic, 'offset', message.offset, 'handlerIndex', entry.nextIndex);
|
|
112
|
+
(0, message_1.runWithContext)({ stub: entry.client, topic: entry.topic, consumerName: entry.consumerName, offset: message.offset }, () => {
|
|
113
|
+
h(message);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
console.warn('handler error', e);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
stream.on('error', (e) => {
|
|
121
|
+
// Log once and clean up the registry entry to avoid reconnect storms and test leaks
|
|
122
|
+
if (!suppressStreamWarnings) {
|
|
123
|
+
// Ignore normal client-side cancellations which happen during unregister
|
|
124
|
+
// and shutdown; only log unexpected stream errors.
|
|
125
|
+
try {
|
|
126
|
+
const code = e && typeof e.code !== 'undefined' ? e.code : null;
|
|
127
|
+
if (code !== 1) {
|
|
128
|
+
console.warn('shared consumer stream error for', entry.topic, e);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch (_) {
|
|
132
|
+
// if anything goes wrong determining code, log the error
|
|
133
|
+
console.warn('shared consumer stream error for', entry.topic, e);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
if (entry.stream) {
|
|
138
|
+
try {
|
|
139
|
+
entry.stream.removeAllListeners();
|
|
140
|
+
}
|
|
141
|
+
catch (_) { }
|
|
142
|
+
try {
|
|
143
|
+
if (entry.stream.cancel)
|
|
144
|
+
entry.stream.cancel();
|
|
145
|
+
}
|
|
146
|
+
catch (_) { }
|
|
147
|
+
entry.stream = null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
// ignore
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
try {
|
|
155
|
+
if (entry.client && typeof entry.client.close === 'function')
|
|
156
|
+
entry.client.close();
|
|
157
|
+
}
|
|
158
|
+
catch (_) { }
|
|
159
|
+
registry.delete(keyFor(entry.grpcUrl || '', entry.topic, entry.consumerName));
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
// ignore
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
stream.on('end', () => {
|
|
166
|
+
// stream ended; clean up entry
|
|
167
|
+
try {
|
|
168
|
+
if (entry.stream) {
|
|
169
|
+
try {
|
|
170
|
+
entry.stream.removeAllListeners();
|
|
171
|
+
}
|
|
172
|
+
catch (_) { }
|
|
173
|
+
try {
|
|
174
|
+
if (entry.stream.cancel)
|
|
175
|
+
entry.stream.cancel();
|
|
176
|
+
}
|
|
177
|
+
catch (_) { }
|
|
178
|
+
entry.stream = null;
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
if (entry.client && typeof entry.client.close === 'function')
|
|
182
|
+
entry.client.close();
|
|
183
|
+
}
|
|
184
|
+
catch (_) { }
|
|
185
|
+
registry.delete(keyFor(entry.grpcUrl || '', entry.topic, entry.consumerName));
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
// ignore
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -17,3 +17,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./producer"), exports);
|
|
18
18
|
__exportStar(require("./consumer"), exports);
|
|
19
19
|
__exportStar(require("./config"), exports);
|
|
20
|
+
__exportStar(require("./message"), exports);
|
|
21
|
+
__exportStar(require("./consumerManager"), exports);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface IMessage {
|
|
2
|
+
offset: number;
|
|
3
|
+
timestamp: number;
|
|
4
|
+
payload: any;
|
|
5
|
+
headers: Record<string, string>;
|
|
6
|
+
}
|
|
7
|
+
interface MessageContext {
|
|
8
|
+
stub: any;
|
|
9
|
+
topic: string;
|
|
10
|
+
consumerName: string;
|
|
11
|
+
offset: number;
|
|
12
|
+
committed?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare function runWithContext(ctx: MessageContext, fn: () => void): void;
|
|
15
|
+
export declare function getContext(): MessageContext | undefined;
|
|
16
|
+
export declare function commit(): Promise<void>;
|
|
17
|
+
export declare class Message implements IMessage {
|
|
18
|
+
offset: number;
|
|
19
|
+
timestamp: number;
|
|
20
|
+
payload: any;
|
|
21
|
+
headers: Record<string, string>;
|
|
22
|
+
constructor(protoMsg: any);
|
|
23
|
+
}
|
|
24
|
+
export {};
|
package/dist/message.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Message = void 0;
|
|
4
|
+
exports.runWithContext = runWithContext;
|
|
5
|
+
exports.getContext = getContext;
|
|
6
|
+
exports.commit = commit;
|
|
7
|
+
const async_hooks_1 = require("async_hooks");
|
|
8
|
+
const storage = new async_hooks_1.AsyncLocalStorage();
|
|
9
|
+
function runWithContext(ctx, fn) {
|
|
10
|
+
storage.run(ctx, fn);
|
|
11
|
+
}
|
|
12
|
+
function getContext() {
|
|
13
|
+
return storage.getStore();
|
|
14
|
+
}
|
|
15
|
+
async function commit() {
|
|
16
|
+
const ctx = storage.getStore();
|
|
17
|
+
if (!ctx)
|
|
18
|
+
throw new Error('commit() called outside of a consumer handler');
|
|
19
|
+
if (ctx.committed)
|
|
20
|
+
return;
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
try {
|
|
23
|
+
ctx.stub.CommitOffset({ topic: ctx.topic, consumer_name: ctx.consumerName, offset: ctx.offset + 1 }, (err, res) => {
|
|
24
|
+
if (err)
|
|
25
|
+
return reject(err);
|
|
26
|
+
ctx.committed = true;
|
|
27
|
+
resolve();
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
reject(e);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
class Message {
|
|
36
|
+
constructor(protoMsg) {
|
|
37
|
+
this.offset = protoMsg.offset;
|
|
38
|
+
this.timestamp = protoMsg.timestamp;
|
|
39
|
+
this.headers = {};
|
|
40
|
+
try {
|
|
41
|
+
this.headers = Object.assign({}, protoMsg.headers);
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
this.headers = {};
|
|
45
|
+
}
|
|
46
|
+
const buf = protoMsg.payload;
|
|
47
|
+
const ptype = this.headers['payload-type'];
|
|
48
|
+
if (ptype === 'json') {
|
|
49
|
+
try {
|
|
50
|
+
this.payload = JSON.parse(buf.toString());
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
this.payload = buf;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else if (ptype === 'string') {
|
|
57
|
+
try {
|
|
58
|
+
this.payload = buf.toString('utf8');
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
this.payload = buf;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else if (ptype === 'bytes') {
|
|
65
|
+
this.payload = buf;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
try {
|
|
69
|
+
this.payload = JSON.parse(buf.toString());
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
this.payload = buf;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.Message = Message;
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pulse-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Pulse SDK for Node.js/TypeScript",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "tsc",
|
|
9
9
|
"postbuild": "node scripts/copy-proto.js",
|
|
10
|
-
"test": "jest"
|
|
10
|
+
"test": "npm run build && jest"
|
|
11
11
|
},
|
|
12
12
|
"author": "",
|
|
13
13
|
"license": "MIT",
|
|
@@ -22,21 +22,21 @@
|
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"typescript": "^5.0.0",
|
|
24
24
|
"jest": "^29.0.0",
|
|
25
|
-
"ts-jest": "^29.0.0"
|
|
26
|
-
"@types/jest": "^29.0.0",
|
|
25
|
+
"ts-jest": "^29.0.0"
|
|
26
|
+
,"@types/jest": "^29.0.0",
|
|
27
27
|
"@types/js-yaml": "^4.0.5"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@grpc/grpc-js": "^1.8.0",
|
|
31
31
|
"protobufjs": "^7.2.0",
|
|
32
|
-
"@grpc/proto-loader": "^0.7.0"
|
|
33
|
-
"js-yaml": "^4.1.0"
|
|
34
|
-
}
|
|
32
|
+
"@grpc/proto-loader": "^0.7.0"
|
|
33
|
+
,"js-yaml": "^4.1.0"
|
|
34
|
+
}
|
|
35
|
+
,
|
|
35
36
|
"jest": {
|
|
36
37
|
"preset": "ts-jest",
|
|
37
38
|
"testEnvironment": "node",
|
|
38
|
-
"testMatch": [
|
|
39
|
-
|
|
40
|
-
]
|
|
39
|
+
"testMatch": ["**/tests/**/*.test.ts"],
|
|
40
|
+
"globalTeardown": "<rootDir>/tests/globalTeardown.js"
|
|
41
41
|
}
|
|
42
42
|
}
|