pulse-sdk 0.0.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 +111 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +7 -0
- package/dist/consumer.d.ts +10 -0
- package/dist/consumer.js +38 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +19 -0
- package/dist/producer.d.ts +11 -0
- package/dist/producer.js +25 -0
- package/dist/proto/client.d.ts +2 -0
- package/dist/proto/client.js +54 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Pulse SDK — TypeScript
|
|
2
|
+
|
|
3
|
+
TypeScript client for Pulse Broker (gRPC). This README is written for end users and explains installation, configuration, and basic usage with practical examples.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Install from npm (published package):
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install pulse-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or install locally from the repository (for development):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
To compile the TypeScript sources (optional for development/CI):
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm run build
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Run the lightweight integration tests (a test gRPC server is started automatically):
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm test
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Configuration (current)
|
|
32
|
+
|
|
33
|
+
Currently the SDK supports programmatic configuration only: create and pass a `PulseConfig` object when instantiating `Producer` or `Consumer`.
|
|
34
|
+
|
|
35
|
+
The code exposes a `PulseConfig` interface for strong typing. Environment-file loading (e.g. `pulse.yml` or `PULSE_*` environment variables) is planned but not implemented in this initial version — for now, pass configuration directly in code.
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
Import from the published package and pass configuration programmatically:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { Producer, Consumer } from 'pulse-sdk';
|
|
43
|
+
|
|
44
|
+
const cfg = { grpcUrl: 'localhost:50052', eventTypes: ['events'] };
|
|
45
|
+
|
|
46
|
+
// Send a JSON-serializable event
|
|
47
|
+
const producer = new Producer(cfg);
|
|
48
|
+
await producer.send('events', { type: 'user.created', id: 123 });
|
|
49
|
+
|
|
50
|
+
// Consume events and register a handler
|
|
51
|
+
const consumer = new Consumer(cfg);
|
|
52
|
+
consumer.on('events', (msg) => {
|
|
53
|
+
console.log('received', msg.payload);
|
|
54
|
+
});
|
|
55
|
+
await consumer.start('events', 'my-consumer');
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Programmatic override
|
|
59
|
+
|
|
60
|
+
You can create and pass `PulseConfig` directly in code:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
const cfg = { grpcUrl: '10.0.0.5:50052', eventTypes: ['events'] };
|
|
64
|
+
const producer = new Producer(cfg);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Technical Details
|
|
68
|
+
|
|
69
|
+
- The SDK dynamically loads `pulse.proto` at runtime using `@grpc/proto-loader` and `@grpc/grpc-js`.
|
|
70
|
+
- The protobuf field `bytes payload` is used to carry the message body; the SDK serializes JSON payloads into that field by default.
|
|
71
|
+
- `Consumer.start()` opens a server stream (`Consume`) and dispatches incoming messages to handlers registered via `consumer.on(topic, handler)`.
|
|
72
|
+
|
|
73
|
+
## Examples
|
|
74
|
+
|
|
75
|
+
Producer (send multiple events):
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
const producer = new Producer(loadConfig());
|
|
79
|
+
await producer.send('events', { type: 'login', userId: 1 });
|
|
80
|
+
await producer.send('events', { type: 'logout', userId: 1 });
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Consumer (simple processing):
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
const cfg = loadConfig();
|
|
87
|
+
const consumer = new Consumer(cfg);
|
|
88
|
+
|
|
89
|
+
consumer.on('events', (msg) => {
|
|
90
|
+
console.log('event payload', msg.payload);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
await consumer.start('events', cfg.consumerName || 'default');
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Local Tests
|
|
97
|
+
|
|
98
|
+
The tests in `tests/` start a lightweight gRPC test server automatically; run them with `npm test`.
|
|
99
|
+
|
|
100
|
+
## FAQ (short)
|
|
101
|
+
|
|
102
|
+
- 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.
|
|
103
|
+
- 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.
|
|
104
|
+
|
|
105
|
+
## Contributing
|
|
106
|
+
|
|
107
|
+
Please open a PR with tests. The existing tests validate basic Producer/Consumer behaviour.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
If you want this README expanded to mirror the Python SDK more closely (topic-level config, manual offset commits, longer examples), tell me which sections to add and I will update it.
|
package/dist/config.d.ts
ADDED
package/dist/config.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { PulseConfig } from './config';
|
|
2
|
+
export type EventHandler = (payload: any) => void;
|
|
3
|
+
export declare class Consumer {
|
|
4
|
+
private config;
|
|
5
|
+
private handlers;
|
|
6
|
+
private client;
|
|
7
|
+
constructor(config: PulseConfig);
|
|
8
|
+
on(eventType: string, handler: EventHandler): void;
|
|
9
|
+
start(topic?: string, consumerName?: string): Promise<void>;
|
|
10
|
+
}
|
package/dist/consumer.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Consumer = void 0;
|
|
4
|
+
const client_1 = require("./proto/client");
|
|
5
|
+
class Consumer {
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.config = config;
|
|
8
|
+
this.handlers = {};
|
|
9
|
+
this.client = (0, client_1.createClient)(config.grpcUrl);
|
|
10
|
+
}
|
|
11
|
+
on(eventType, handler) {
|
|
12
|
+
if (!this.handlers[eventType]) {
|
|
13
|
+
this.handlers[eventType] = [];
|
|
14
|
+
}
|
|
15
|
+
this.handlers[eventType].push(handler);
|
|
16
|
+
}
|
|
17
|
+
async start(topic, consumerName = 'default') {
|
|
18
|
+
const req = { topic: topic || this.config.eventTypes[0], consumer_name: consumerName, offset: 0 };
|
|
19
|
+
const stream = this.client.Consume(req);
|
|
20
|
+
stream.on('data', (msg) => {
|
|
21
|
+
const payloadBuf = msg.payload;
|
|
22
|
+
let parsed = payloadBuf;
|
|
23
|
+
try {
|
|
24
|
+
parsed = JSON.parse(payloadBuf.toString());
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
// keep raw buffer if not JSON
|
|
28
|
+
}
|
|
29
|
+
const handlers = this.handlers[req.topic] || [];
|
|
30
|
+
handlers.forEach(h => h({ offset: msg.offset, timestamp: msg.timestamp, payload: parsed, headers: msg.headers }));
|
|
31
|
+
});
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
stream.on('end', () => resolve());
|
|
34
|
+
stream.on('error', (e) => reject(e));
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.Consumer = Consumer;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./producer"), exports);
|
|
18
|
+
__exportStar(require("./consumer"), exports);
|
|
19
|
+
__exportStar(require("./config"), exports);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PulseConfig } from './config';
|
|
2
|
+
export interface PublishResponse {
|
|
3
|
+
id: string;
|
|
4
|
+
offset: number | string;
|
|
5
|
+
}
|
|
6
|
+
export declare class Producer {
|
|
7
|
+
private config;
|
|
8
|
+
private client;
|
|
9
|
+
constructor(config: PulseConfig);
|
|
10
|
+
send(topic: string, payload: any, headers?: Record<string, string>): Promise<PublishResponse>;
|
|
11
|
+
}
|
package/dist/producer.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Producer = void 0;
|
|
4
|
+
const client_1 = require("./proto/client");
|
|
5
|
+
class Producer {
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.config = config;
|
|
8
|
+
this.client = (0, client_1.createClient)(config.grpcUrl);
|
|
9
|
+
}
|
|
10
|
+
async send(topic, payload, headers) {
|
|
11
|
+
const req = {
|
|
12
|
+
topic,
|
|
13
|
+
payload: Buffer.from(JSON.stringify(payload)),
|
|
14
|
+
headers: headers || {},
|
|
15
|
+
};
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
this.client.Publish(req, (err, res) => {
|
|
18
|
+
if (err)
|
|
19
|
+
return reject(err);
|
|
20
|
+
resolve({ id: res.id, offset: res.offset });
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.Producer = Producer;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.createClient = createClient;
|
|
40
|
+
const path_1 = __importDefault(require("path"));
|
|
41
|
+
const grpc = __importStar(require("@grpc/grpc-js"));
|
|
42
|
+
const protoLoader = __importStar(require("@grpc/proto-loader"));
|
|
43
|
+
const PROTO_PATH = path_1.default.resolve(__dirname, '../../../../internal/api/proto/pulse.proto');
|
|
44
|
+
const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
|
|
45
|
+
keepCase: true,
|
|
46
|
+
longs: String,
|
|
47
|
+
enums: String,
|
|
48
|
+
defaults: true,
|
|
49
|
+
oneofs: true,
|
|
50
|
+
});
|
|
51
|
+
const loaded = grpc.loadPackageDefinition(packageDefinition).pulse.v1;
|
|
52
|
+
function createClient(address) {
|
|
53
|
+
return new loaded.PulseService(address, grpc.credentials.createInsecure());
|
|
54
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pulse-sdk",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Pulse SDK for Node.js/TypeScript",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"test": "jest"
|
|
10
|
+
},
|
|
11
|
+
"author": "",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"prepare": "npm run build",
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"typescript": "^5.0.0",
|
|
23
|
+
"jest": "^29.0.0",
|
|
24
|
+
"ts-jest": "^29.0.0",
|
|
25
|
+
"@types/jest": "^29.0.0"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@grpc/grpc-js": "^1.8.0",
|
|
29
|
+
"protobufjs": "^7.2.0",
|
|
30
|
+
"@grpc/proto-loader": "^0.7.0"
|
|
31
|
+
},
|
|
32
|
+
"jest": {
|
|
33
|
+
"preset": "ts-jest",
|
|
34
|
+
"testEnvironment": "node",
|
|
35
|
+
"testMatch": [
|
|
36
|
+
"**/tests/**/*.test.ts"
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
}
|