@railtownai/railengine-ingest 0.0.2 → 0.0.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.
Files changed (2) hide show
  1. package/README.md +56 -516
  2. package/package.json +3 -2
package/README.md CHANGED
@@ -1,516 +1,56 @@
1
- # @railtownai/railengine-ingest
2
-
3
- JavaScript/TypeScript SDK for Railtown AI Rail Engine - Ingestion package. This package provides a simple interface for publishing data to Rail Engine and handling webhook events.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install @railtownai/railengine-ingest
9
- ```
10
-
11
- ## Requirements
12
-
13
- - Node.js 20+
14
- - TypeScript 5.0+ (optional, but recommended)
15
-
16
- ## Quick Start
17
-
18
- ### Basic Usage
19
-
20
- ```typescript
21
- import { RailEngineIngest } from "@railtownai/railengine-ingest";
22
- import { Buffer } from "buffer";
23
-
24
- // Create ENGINE_TOKEN (get this from your Rail Engine dashboard)
25
- const tokenData = {
26
- IngestionUrl: "https://eng123.railtownlogs.com",
27
- IngestionApiToken: "your-auth-token",
28
- EngineId: "your-engine-id"
29
- };
30
- const engineToken = Buffer.from(JSON.stringify(tokenData)).toString("base64");
31
-
32
- // Initialize client
33
- const client = new RailEngineIngest({ engineToken });
34
-
35
- // Send data
36
- await client.upsert({
37
- EventId: "guid",
38
- EngineId: client.engineId,
39
- ProjectId: "project-id",
40
- Body: JSON.stringify({ title: "My Document", content: "..." }),
41
- CustomerKey: "doc-123" // Optional
42
- });
43
- ```
44
-
45
- ### Using Environment Variables
46
-
47
- ```typescript
48
- import { RailEngineIngest } from "@railtownai/railengine-ingest";
49
-
50
- // ENGINE_TOKEN is read from environment variable automatically
51
- const client = new RailEngineIngest();
52
-
53
- await client.upsert({
54
- EventId: "guid",
55
- EngineId: client.engineId,
56
- ProjectId: "project-id",
57
- Body: JSON.stringify({ title: "My Document" })
58
- });
59
- ```
60
-
61
- ## ENGINE_TOKEN Configuration
62
-
63
- The `ENGINE_TOKEN` is a base64-encoded JSON string containing your ingestion configuration. You can create it like this:
64
-
65
- ```typescript
66
- import { Buffer } from "buffer";
67
-
68
- const tokenData = {
69
- IngestionUrl: "https://eng123.railtownlogs.com",
70
- IngestionApiToken: "your-auth-token-here",
71
- EngineId: "your-engine-guid-here"
72
- };
73
-
74
- const engineToken = Buffer.from(JSON.stringify(tokenData)).toString("base64");
75
- ```
76
-
77
- ### Setting Environment Variables
78
-
79
- #### Windows (PowerShell)
80
-
81
- ```powershell
82
- $env:ENGINE_TOKEN="<base64-encoded-token>"
83
- ```
84
-
85
- #### Windows (Command Prompt)
86
-
87
- ```cmd
88
- set ENGINE_TOKEN=<base64-encoded-token>
89
- ```
90
-
91
- #### macOS/Linux
92
-
93
- ```bash
94
- export ENGINE_TOKEN="<base64-encoded-token>"
95
- ```
96
-
97
- #### .env file
98
-
99
- Create a `.env` file in your project root:
100
-
101
- ```
102
- ENGINE_TOKEN=<base64-encoded-token>
103
- ```
104
-
105
- Then use a package like `dotenv` to load it:
106
-
107
- ```typescript
108
- import "dotenv/config";
109
- import { RailEngineIngest } from "@railtownai/railengine-ingest";
110
-
111
- const client = new RailEngineIngest(); // Reads from process.env.ENGINE_TOKEN
112
- ```
113
-
114
- ## Zod Schema Support
115
-
116
- You can provide a Zod schema during client initialization to automatically validate ingested data:
117
-
118
- ```typescript
119
- import { z } from "zod";
120
- import { RailEngineIngest } from "@railtownai/railengine-ingest";
121
- import { Buffer } from "buffer";
122
-
123
- const FoodDiaryItemSchema = z.object({
124
- food_name: z.string(),
125
- calories: z.number(),
126
- carbs: z.number(),
127
- proteins: z.number(),
128
- fats: z.number()
129
- });
130
-
131
- type FoodDiaryItem = z.infer<typeof FoodDiaryItemSchema>;
132
-
133
- // Initialize with schema
134
- const token = Buffer.from(
135
- JSON.stringify({
136
- IngestionUrl: "https://eng123.railtownlogs.com",
137
- IngestionApiToken: "your-auth-token",
138
- EngineId: "your-engine-id"
139
- })
140
- ).toString("base64");
141
-
142
- const client = new RailEngineIngest({
143
- engineToken: token,
144
- schema: FoodDiaryItemSchema
145
- });
146
-
147
- // Ingest using validated object
148
- const item: FoodDiaryItem = {
149
- food_name: "Apple",
150
- calories: 95,
151
- carbs: 25.0,
152
- proteins: 0.5,
153
- fats: 0.3
154
- };
155
-
156
- await client.upsert(item); // Automatically validates and serializes
157
- ```
158
-
159
- ## Webhook Handling
160
-
161
- The ingestion package provides webhook handling capabilities for receiving and processing webhook events from Rail Engine's Publishing cylinder.
162
-
163
- ### Basic Webhook Handler
164
-
165
- ```typescript
166
- import { z } from "zod";
167
- import { WebhookHandler } from "@railtownai/railengine-ingest";
168
-
169
- const FoodDiaryItemSchema = z.object({
170
- food_name: z.string(),
171
- calories: z.number()
172
- });
173
-
174
- // Create handler with schema
175
- const handler = new WebhookHandler({ schema: FoodDiaryItemSchema });
176
-
177
- // Parse webhook payload (list of events)
178
- const events = handler.parse(await request.json());
179
-
180
- // Process each event
181
- for (const event of events) {
182
- console.log(`Event ID: ${event.eventId}`);
183
- console.log(`Engine ID: ${event.engineId}`);
184
- console.log(`Project ID: ${event.projectId}`);
185
-
186
- // Access deserialized body (FoodDiaryItem)
187
- const item = event.body;
188
- console.log(`Food: ${item.food_name}, Calories: ${item.calories}`);
189
- }
190
- ```
191
-
192
- ### Using Client's Webhook Handler
193
-
194
- If your client was initialized with a schema, you can get a pre-configured webhook handler:
195
-
196
- ```typescript
197
- import { z } from "zod";
198
- import { RailEngineIngest } from "@railtownai/railengine-ingest";
199
- import { Buffer } from "buffer";
200
-
201
- const FoodDiaryItemSchema = z.object({
202
- food_name: z.string(),
203
- calories: z.number()
204
- });
205
-
206
- const token = Buffer.from(
207
- JSON.stringify({
208
- IngestionUrl: "https://eng123.railtownlogs.com",
209
- IngestionApiToken: "your-auth-token",
210
- EngineId: "your-engine-id"
211
- })
212
- ).toString("base64");
213
-
214
- // Initialize client with schema
215
- const client = new RailEngineIngest({
216
- engineToken: token,
217
- schema: FoodDiaryItemSchema
218
- });
219
-
220
- // Get handler (automatically uses FoodDiaryItemSchema)
221
- const handler = client.getWebhookHandler();
222
-
223
- // Parse webhook payload
224
- const events = handler.parse(await request.json());
225
-
226
- // events[0].body is a FoodDiaryItem
227
- ```
228
-
229
- ### Express.js Integration Example
230
-
231
- ```typescript
232
- import express from "express";
233
- import { z } from "zod";
234
- import { RailEngineIngest } from "@railtownai/railengine-ingest";
235
- import { Buffer } from "buffer";
236
-
237
- const app = express();
238
-
239
- const FoodDiaryItemSchema = z.object({
240
- food_name: z.string(),
241
- calories: z.number(),
242
- carbs: z.number(),
243
- proteins: z.number(),
244
- fats: z.number()
245
- });
246
-
247
- const token = Buffer.from(
248
- JSON.stringify({
249
- IngestionUrl: "https://eng123.railtownlogs.com",
250
- IngestionApiToken: "your-auth-token",
251
- EngineId: "your-engine-id"
252
- })
253
- ).toString("base64");
254
-
255
- const client = new RailEngineIngest({
256
- engineToken: token,
257
- schema: FoodDiaryItemSchema
258
- });
259
-
260
- const handler = client.getWebhookHandler();
261
-
262
- app.post("/webhook", express.json(), async (req, res) => {
263
- try {
264
- // Parse webhook payload (list of events)
265
- const events = handler.parse(req.body);
266
-
267
- // Process each event
268
- for (const event of events) {
269
- // Access metadata
270
- console.log(`Event ID: ${event.eventId}`);
271
- console.log(`Engine ID: ${event.engineId}`);
272
- console.log(`Project ID: ${event.projectId}`);
273
-
274
- // Access deserialized body (FoodDiaryItem)
275
- const item = event.body;
276
- console.log(`Food: ${item.food_name}, Calories: ${item.calories}`);
277
- }
278
-
279
- res.json({ status: "ok" });
280
- } catch (error) {
281
- console.error("Webhook processing error:", error);
282
- res.status(400).json({ error: "Invalid webhook payload" });
283
- }
284
- });
285
-
286
- app.listen(3000);
287
- ```
288
-
289
- ### Manual Deserialization
290
-
291
- You can also deserialize webhook payloads manually:
292
-
293
- ```typescript
294
- import { WebhookPublishingPayload, WebhookPublishingPayloadHelper } from "@railtownai/railengine-ingest";
295
- import { z } from "zod";
296
-
297
- const FoodDiaryItemSchema = z.object({
298
- food_name: z.string(),
299
- calories: z.number()
300
- });
301
-
302
- // Parse raw payload
303
- const payload = req.body[0] as WebhookPublishingPayload; // First event
304
-
305
- // Deserialize body manually
306
- const item = WebhookPublishingPayloadHelper.getBodyAs(payload, FoodDiaryItemSchema);
307
- console.log(`Food: ${item.food_name}, Calories: ${item.calories}`);
308
- ```
309
-
310
- ## API Reference
311
-
312
- ### RailEngineIngest
313
-
314
- Main client class for ingesting data to Rail Engine.
315
-
316
- #### Constructor
317
-
318
- ```typescript
319
- new RailEngineIngest(options?: RailEngineIngestOptions<T>)
320
- ```
321
-
322
- **Options:**
323
-
324
- - `engineToken?: string` - Base64-encoded ENGINE_TOKEN. If not provided, reads from `ENGINE_TOKEN` environment variable.
325
- - `schema?: z.ZodSchema<T>` - Optional Zod schema for validating ingested data and deserializing webhook bodies.
326
- - `timeout?: number` - Request timeout in milliseconds. Defaults to 30000 (30 seconds).
327
-
328
- **Example:**
329
-
330
- ```typescript
331
- const client = new RailEngineIngest({
332
- engineToken: token,
333
- schema: MySchema,
334
- timeout: 60000
335
- });
336
- ```
337
-
338
- #### Properties
339
-
340
- - `ingestionUrl: string` - Logic App ingestion URL from decoded ENGINE_TOKEN
341
- - `ingestionApiToken: string` - Authentication token for x-rail-auth header
342
- - `engineId: string` - Engine identifier from decoded ENGINE_TOKEN
343
-
344
- #### Methods
345
-
346
- ##### upsert(data)
347
-
348
- Send data to Rail Engine ingestion endpoint.
349
-
350
- ```typescript
351
- await client.upsert(data: T | string | Record<string, unknown>): Promise<Response>
352
- ```
353
-
354
- **Parameters:**
355
-
356
- - `data` - Data to ingest. Can be an object, JSON string, or Zod-validated object (if schema provided).
357
-
358
- **Returns:**
359
-
360
- - `Promise<Response>` - HTTP response from the ingestion endpoint
361
-
362
- **Throws:**
363
-
364
- - `RailtownError` - If the request fails
365
- - `z.ZodError` - If schema validation fails
366
-
367
- **Example:**
368
-
369
- ```typescript
370
- await client.upsert({ name: "test", value: 123 });
371
- ```
372
-
373
- ##### getWebhookHandler()
374
-
375
- Get a webhook handler configured with this client's schema.
376
-
377
- ```typescript
378
- client.getWebhookHandler(): WebhookHandler<T>
379
- ```
380
-
381
- **Returns:**
382
-
383
- - `WebhookHandler<T>` - Webhook handler instance configured with the client's schema (if provided)
384
-
385
- **Example:**
386
-
387
- ```typescript
388
- const handler = client.getWebhookHandler();
389
- const events = handler.parse(webhookPayload);
390
- ```
391
-
392
- ### WebhookHandler
393
-
394
- Generic handler class for parsing webhook events.
395
-
396
- #### Constructor
397
-
398
- ```typescript
399
- new WebhookHandler<T>(options?: { schema?: z.ZodSchema<T> })
400
- ```
401
-
402
- **Options:**
403
-
404
- - `schema?: z.ZodSchema<T>` - Optional Zod schema for deserializing webhook bodies.
405
-
406
- #### Methods
407
-
408
- ##### parse(payload)
409
-
410
- Parse webhook payload into list of WebhookEvent objects.
411
-
412
- ```typescript
413
- handler.parse(payload: WebhookPublishingPayload[] | WebhookPublishingPayload): WebhookEvent<T>[]
414
- ```
415
-
416
- **Parameters:**
417
-
418
- - `payload` - Either a list of webhook payload objects or a single object
419
-
420
- **Returns:**
421
-
422
- - `WebhookEvent<T>[]` - List of WebhookEvent objects
423
-
424
- **Throws:**
425
-
426
- - `Error` - If no schema is provided
427
- - `z.ZodError` - If body JSON doesn't match the schema
428
-
429
- ### WebhookEvent
430
-
431
- Generic wrapper class that combines webhook payload metadata with deserialized body.
432
-
433
- #### Properties
434
-
435
- - `eventId: string` - Event identifier from payload
436
- - `engineId: string` - Engine identifier from payload
437
- - `projectId: string` - Project identifier from payload
438
- - `customerKey?: string` - Customer key from payload (if present)
439
- - `body: T` - Deserialized body as validated object
440
-
441
- ### WebhookPublishingPayload
442
-
443
- TypeScript interface representing the webhook payload structure.
444
-
445
- ```typescript
446
- interface WebhookPublishingPayload {
447
- EventId: string;
448
- EngineId: string;
449
- ProjectId: string;
450
- CustomerKey?: string;
451
- Body: string; // JSON stringified document
452
- }
453
- ```
454
-
455
- ### WebhookPublishingPayloadHelper
456
-
457
- Helper class for working with WebhookPublishingPayload.
458
-
459
- #### Methods
460
-
461
- ##### getBodyAs(payload, schema)
462
-
463
- Deserializes the Body field (JSON string) into a Zod-validated object.
464
-
465
- ```typescript
466
- WebhookPublishingPayloadHelper.getBodyAs<T>(
467
- payload: WebhookPublishingPayload,
468
- schema: z.ZodSchema<T>
469
- ): T
470
- ```
471
-
472
- **Parameters:**
473
-
474
- - `payload` - The webhook payload
475
- - `schema` - Zod schema to validate the Body against
476
-
477
- **Returns:**
478
-
479
- - `T` - Validated object
480
-
481
- **Throws:**
482
-
483
- - `z.ZodError` - If the JSON doesn't match the schema
484
-
485
- ## Error Handling
486
-
487
- The SDK uses custom exception classes for different error scenarios:
488
-
489
- - `RailtownError` - Base error class
490
- - `RailtownUnauthorizedError` - Authentication failed (401)
491
- - `RailtownNotFoundError` - Resource not found (404)
492
- - `RailtownBadRequestError` - Bad request (400)
493
- - `RailtownConflictError` - Conflict (409)
494
- - `RailtownServerError` - Server error (500+)
495
-
496
- **Example:**
497
-
498
- ```typescript
499
- import { RailEngineIngest, RailtownUnauthorizedError, RailtownBadRequestError } from "@railtownai/railengine-ingest";
500
-
501
- try {
502
- await client.upsert(data);
503
- } catch (error) {
504
- if (error instanceof RailtownUnauthorizedError) {
505
- console.error("Authentication failed:", error.message);
506
- } else if (error instanceof RailtownBadRequestError) {
507
- console.error("Invalid request:", error.message);
508
- } else {
509
- console.error("Unexpected error:", error);
510
- }
511
- }
512
- ```
513
-
514
- ## License
515
-
516
- See [LICENSE](../../LICENSE) file for details.
1
+ # @railtownai/railengine-ingest
2
+
3
+ JavaScript/TypeScript SDK for Railtown AI Rail Engine - Ingestion package. This package provides a simple interface for publishing data to Rail Engine and handling webhook events.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @railtownai/railengine-ingest
9
+ ```
10
+
11
+ ## Requirements
12
+
13
+ - Node.js 20+
14
+ - TypeScript 5.0+ (optional, but recommended)
15
+
16
+ ## Quick Start
17
+
18
+ ### Basic Usage
19
+
20
+ ```typescript
21
+ import { RailEngineIngest } from "@railtownai/railengine-ingest";
22
+
23
+ const engineToken = "[Your ENGINE_TOKEN]";
24
+
25
+ // Initialize client
26
+ const client = new RailEngineIngest({ engineToken });
27
+
28
+ // Send data
29
+ await client.upsert({
30
+ EventId: "guid",
31
+ EngineId: client.engineId,
32
+ ProjectId: "project-id",
33
+ Body: JSON.stringify({ title: "My Document", content: "..." }),
34
+ CustomerKey: "doc-123" // Optional
35
+ });
36
+ ```
37
+
38
+ ### Using Environment Variables
39
+
40
+ ```typescript
41
+ import { RailEngineIngest } from "@railtownai/railengine-ingest";
42
+
43
+ // ENGINE_TOKEN is read from environment variable automatically
44
+ const client = new RailEngineIngest();
45
+
46
+ await client.upsert({
47
+ EventId: "guid",
48
+ EngineId: client.engineId,
49
+ ProjectId: "project-id",
50
+ Body: JSON.stringify({ title: "My Document" })
51
+ });
52
+ ```
53
+
54
+ ## License
55
+
56
+ See [LICENSE](../../LICENSE) file for details.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@railtownai/railengine-ingest",
3
3
  "license": "MIT",
4
4
  "author": "Railtown AI",
5
- "version": "0.0.2",
5
+ "version": "0.0.3",
6
6
  "description": "JavaScript/TypeScript SDK for Railtown AI Rail Engine - Ingestion",
7
7
  "type": "module",
8
8
  "main": "./dist/cjs/index.js",
@@ -42,6 +42,7 @@
42
42
  "test": "vitest run --passWithNoTests",
43
43
  "format": "prettier --write src",
44
44
  "update": "npx npm-check-updates -u",
45
- "dry-run": "pnpm publish --no-git-checks --access public --registry https://registry.npmjs.org --dry-run"
45
+ "dry-run": "pnpm publish --no-git-checks --access public --registry https://registry.npmjs.org --dry-run",
46
+ "release": "pnpm publish --no-git-checks --access public --registry https://registry.npmjs.org"
46
47
  }
47
48
  }