@walkeros/server-source-express 0.0.0-next-20251219153324
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 +567 -0
- package/dist/dev.d.mts +79 -0
- package/dist/dev.d.ts +79 -0
- package/dist/dev.js +1 -0
- package/dist/dev.js.map +1 -0
- package/dist/dev.mjs +1 -0
- package/dist/dev.mjs.map +1 -0
- package/dist/index.d.mts +113 -0
- package/dist/index.d.ts +113 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
# @walkeros/server-source-express
|
|
2
|
+
|
|
3
|
+
Express server source for walkerOS - turn-key HTTP event collection server with
|
|
4
|
+
Express.js.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install @walkeros/server-source-express
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
### Standalone Server (Docker-style)
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import { startFlow } from '@walkeros/collector';
|
|
18
|
+
import { sourceExpress } from '@walkeros/server-source-express';
|
|
19
|
+
|
|
20
|
+
const { collector } = await startFlow({
|
|
21
|
+
sources: {
|
|
22
|
+
express: {
|
|
23
|
+
code: sourceExpress,
|
|
24
|
+
config: {
|
|
25
|
+
settings: {
|
|
26
|
+
port: 8080, // Start server on port 8080
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
destinations: {
|
|
32
|
+
// Your destinations here
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Server is now running!
|
|
37
|
+
// POST http://localhost:8080/collect - JSON event ingestion
|
|
38
|
+
// GET http://localhost:8080/collect - Pixel tracking
|
|
39
|
+
// GET http://localhost:8080/health - Health check
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### App-Only Mode (Custom Integration)
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
const { collector } = await startFlow({
|
|
46
|
+
sources: {
|
|
47
|
+
express: {
|
|
48
|
+
code: sourceExpress,
|
|
49
|
+
config: {
|
|
50
|
+
settings: {
|
|
51
|
+
// No port = app only, no server started
|
|
52
|
+
path: '/events',
|
|
53
|
+
cors: false, // Handle CORS with your own middleware
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Access the Express app
|
|
61
|
+
const expressSource = collector.sources.express;
|
|
62
|
+
const app = expressSource.app;
|
|
63
|
+
|
|
64
|
+
// Add custom middleware
|
|
65
|
+
app.use(yourAuthMiddleware);
|
|
66
|
+
|
|
67
|
+
// Add custom routes
|
|
68
|
+
app.get('/custom', customHandler);
|
|
69
|
+
|
|
70
|
+
// Start server manually
|
|
71
|
+
app.listen(3000);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Configuration
|
|
75
|
+
|
|
76
|
+
### Settings
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
interface Settings {
|
|
80
|
+
/**
|
|
81
|
+
* HTTP server port to listen on
|
|
82
|
+
* If not provided, server will not start (app-only mode)
|
|
83
|
+
* @optional
|
|
84
|
+
*/
|
|
85
|
+
port?: number;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Event collection endpoint path
|
|
89
|
+
* All HTTP methods (POST, GET, OPTIONS) registered on this path
|
|
90
|
+
* @default '/collect'
|
|
91
|
+
*/
|
|
92
|
+
path?: string;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* CORS configuration
|
|
96
|
+
* - false: Disabled
|
|
97
|
+
* - true: Allow all origins (default)
|
|
98
|
+
* - object: Custom CORS options
|
|
99
|
+
* @default true
|
|
100
|
+
*/
|
|
101
|
+
cors?: boolean | CorsOptions;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Enable health check endpoints
|
|
105
|
+
* - GET /health (liveness check)
|
|
106
|
+
* - GET /ready (readiness check)
|
|
107
|
+
* @default true
|
|
108
|
+
*/
|
|
109
|
+
status?: boolean;
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### CORS Options
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
interface CorsOptions {
|
|
117
|
+
/** Allowed origins (string, array, or '*') */
|
|
118
|
+
origin?: string | string[] | '*';
|
|
119
|
+
|
|
120
|
+
/** Allowed HTTP methods */
|
|
121
|
+
methods?: string[];
|
|
122
|
+
|
|
123
|
+
/** Allowed request headers */
|
|
124
|
+
headers?: string[];
|
|
125
|
+
|
|
126
|
+
/** Allow credentials (cookies, authorization) */
|
|
127
|
+
credentials?: boolean;
|
|
128
|
+
|
|
129
|
+
/** Preflight cache duration in seconds */
|
|
130
|
+
maxAge?: number;
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## HTTP Methods
|
|
135
|
+
|
|
136
|
+
### POST - Standard Event Ingestion
|
|
137
|
+
|
|
138
|
+
Send events as JSON in the request body.
|
|
139
|
+
|
|
140
|
+
**Request:**
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
curl -X POST http://localhost:8080/collect \
|
|
144
|
+
-H "Content-Type: application/json" \
|
|
145
|
+
-d '{
|
|
146
|
+
"event": "page view",
|
|
147
|
+
"data": {
|
|
148
|
+
"title": "Home Page",
|
|
149
|
+
"path": "/"
|
|
150
|
+
},
|
|
151
|
+
"user": {
|
|
152
|
+
"id": "user123"
|
|
153
|
+
}
|
|
154
|
+
}'
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Response:**
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"success": true,
|
|
162
|
+
"timestamp": 1647261462000
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### GET - Pixel Tracking
|
|
167
|
+
|
|
168
|
+
Send events as query parameters. Returns a 1x1 transparent GIF.
|
|
169
|
+
|
|
170
|
+
**Request:**
|
|
171
|
+
|
|
172
|
+
```html
|
|
173
|
+
<!-- In your HTML -->
|
|
174
|
+
<img
|
|
175
|
+
src="http://localhost:8080/collect?event=page%20view&data[title]=Home&user[id]=user123"
|
|
176
|
+
width="1"
|
|
177
|
+
height="1"
|
|
178
|
+
alt=""
|
|
179
|
+
/>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Response:**
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
Content-Type: image/gif
|
|
186
|
+
Cache-Control: no-cache, no-store, must-revalidate
|
|
187
|
+
|
|
188
|
+
[1x1 transparent GIF binary]
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### OPTIONS - CORS Preflight
|
|
192
|
+
|
|
193
|
+
Automatically handled for cross-origin requests.
|
|
194
|
+
|
|
195
|
+
**Request:**
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
curl -X OPTIONS http://localhost:8080/collect \
|
|
199
|
+
-H "Origin: https://example.com"
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Response:**
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
Access-Control-Allow-Origin: *
|
|
206
|
+
Access-Control-Allow-Methods: GET, POST, OPTIONS
|
|
207
|
+
Access-Control-Allow-Headers: Content-Type
|
|
208
|
+
|
|
209
|
+
204 No Content
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Health Checks
|
|
213
|
+
|
|
214
|
+
### GET /health - Liveness Check
|
|
215
|
+
|
|
216
|
+
Returns server status (always returns 200 if server is running).
|
|
217
|
+
|
|
218
|
+
**Response:**
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
{
|
|
222
|
+
"status": "ok",
|
|
223
|
+
"timestamp": 1647261462000,
|
|
224
|
+
"source": "express"
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### GET /ready - Readiness Check
|
|
229
|
+
|
|
230
|
+
Returns readiness status (same as health for Express source).
|
|
231
|
+
|
|
232
|
+
**Response:**
|
|
233
|
+
|
|
234
|
+
```json
|
|
235
|
+
{
|
|
236
|
+
"status": "ready",
|
|
237
|
+
"timestamp": 1647261462000,
|
|
238
|
+
"source": "express"
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Advanced Examples
|
|
243
|
+
|
|
244
|
+
### Custom CORS Configuration
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
await startFlow({
|
|
248
|
+
sources: {
|
|
249
|
+
express: {
|
|
250
|
+
code: sourceExpress,
|
|
251
|
+
config: {
|
|
252
|
+
settings: {
|
|
253
|
+
port: 8080,
|
|
254
|
+
cors: {
|
|
255
|
+
origin: ['https://app.example.com', 'https://admin.example.com'],
|
|
256
|
+
credentials: true,
|
|
257
|
+
methods: ['GET', 'POST', 'OPTIONS'],
|
|
258
|
+
headers: ['Content-Type', 'Authorization'],
|
|
259
|
+
maxAge: 86400, // 24 hours
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Disable Health Checks
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
await startFlow({
|
|
272
|
+
sources: {
|
|
273
|
+
express: {
|
|
274
|
+
code: sourceExpress,
|
|
275
|
+
config: {
|
|
276
|
+
settings: {
|
|
277
|
+
port: 8080,
|
|
278
|
+
status: false, // Disable /health and /ready endpoints
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Custom Endpoint Path
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
await startFlow({
|
|
290
|
+
sources: {
|
|
291
|
+
express: {
|
|
292
|
+
code: sourceExpress,
|
|
293
|
+
config: {
|
|
294
|
+
settings: {
|
|
295
|
+
port: 8080,
|
|
296
|
+
path: '/api/v1/events', // Custom path
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// All methods now work on /api/v1/events
|
|
304
|
+
// POST /api/v1/events
|
|
305
|
+
// GET /api/v1/events
|
|
306
|
+
// OPTIONS /api/v1/events
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Extend Express App
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
const { collector } = await startFlow({
|
|
313
|
+
sources: {
|
|
314
|
+
express: {
|
|
315
|
+
code: sourceExpress,
|
|
316
|
+
config: {
|
|
317
|
+
settings: { port: 8080 },
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// Access Express app for advanced customization
|
|
324
|
+
const expressSource = collector.sources.express;
|
|
325
|
+
const app = expressSource.app;
|
|
326
|
+
|
|
327
|
+
// Add authentication middleware
|
|
328
|
+
app.use('/collect', authMiddleware);
|
|
329
|
+
|
|
330
|
+
// Add rate limiting
|
|
331
|
+
import rateLimit from 'express-rate-limit';
|
|
332
|
+
app.use(
|
|
333
|
+
'/collect',
|
|
334
|
+
rateLimit({
|
|
335
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
336
|
+
max: 100, // Limit each IP to 100 requests per windowMs
|
|
337
|
+
}),
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
// Add custom logging
|
|
341
|
+
app.use((req, res, next) => {
|
|
342
|
+
console.log(`${req.method} ${req.path}`);
|
|
343
|
+
next();
|
|
344
|
+
});
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Event Format
|
|
348
|
+
|
|
349
|
+
### Single Event
|
|
350
|
+
|
|
351
|
+
```json
|
|
352
|
+
{
|
|
353
|
+
"event": "page view",
|
|
354
|
+
"data": {
|
|
355
|
+
"title": "Home Page",
|
|
356
|
+
"path": "/"
|
|
357
|
+
},
|
|
358
|
+
"context": {
|
|
359
|
+
"language": ["en", 0],
|
|
360
|
+
"currency": ["USD", 0]
|
|
361
|
+
},
|
|
362
|
+
"user": {
|
|
363
|
+
"id": "user123",
|
|
364
|
+
"device": "device456"
|
|
365
|
+
},
|
|
366
|
+
"globals": {
|
|
367
|
+
"appVersion": "1.0.0"
|
|
368
|
+
},
|
|
369
|
+
"consent": {
|
|
370
|
+
"functional": true,
|
|
371
|
+
"marketing": true
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Query Parameters (GET)
|
|
377
|
+
|
|
378
|
+
For pixel tracking, use nested bracket notation:
|
|
379
|
+
|
|
380
|
+
```
|
|
381
|
+
?event=page%20view
|
|
382
|
+
&data[title]=Home%20Page
|
|
383
|
+
&data[path]=/
|
|
384
|
+
&user[id]=user123
|
|
385
|
+
&consent[marketing]=true
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
This is automatically parsed by `requestToData` from `@walkeros/core`.
|
|
389
|
+
|
|
390
|
+
## Architecture
|
|
391
|
+
|
|
392
|
+
### Infrastructure Ownership
|
|
393
|
+
|
|
394
|
+
The Express source **owns its HTTP infrastructure**:
|
|
395
|
+
|
|
396
|
+
- ✅ Creates Express application
|
|
397
|
+
- ✅ Configures middleware (JSON parsing, CORS)
|
|
398
|
+
- ✅ Registers routes (POST, GET, OPTIONS)
|
|
399
|
+
- ✅ Starts HTTP server (if port configured)
|
|
400
|
+
- ✅ Handles graceful shutdown (SIGTERM, SIGINT)
|
|
401
|
+
|
|
402
|
+
This design enables:
|
|
403
|
+
|
|
404
|
+
1. **Turn-key deployment** - Just specify a port and deploy
|
|
405
|
+
2. **Docker-friendly** - Perfect for containerized environments
|
|
406
|
+
3. **Flexibility** - App-only mode for custom integrations
|
|
407
|
+
|
|
408
|
+
### Request Flow
|
|
409
|
+
|
|
410
|
+
```
|
|
411
|
+
┌─────────────────────────────────────────┐
|
|
412
|
+
│ HTTP Client (Browser, Server, etc.) │
|
|
413
|
+
└─────────────────────────────────────────┘
|
|
414
|
+
↓
|
|
415
|
+
┌─────────────────────────────────────────┐
|
|
416
|
+
│ EXPRESS SOURCE │
|
|
417
|
+
│ - Receives HTTP request │
|
|
418
|
+
│ - Parses body/query params │
|
|
419
|
+
│ - Validates request structure │
|
|
420
|
+
│ - Calls env.push() → Collector │
|
|
421
|
+
│ - Returns HTTP response │
|
|
422
|
+
└─────────────────────────────────────────┘
|
|
423
|
+
↓
|
|
424
|
+
┌─────────────────────────────────────────┐
|
|
425
|
+
│ COLLECTOR │
|
|
426
|
+
│ - Event validation & processing │
|
|
427
|
+
│ - Consent management │
|
|
428
|
+
│ - Mapping rules │
|
|
429
|
+
│ - Routes to destinations │
|
|
430
|
+
└─────────────────────────────────────────┘
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
## Deployment
|
|
434
|
+
|
|
435
|
+
### Docker
|
|
436
|
+
|
|
437
|
+
```dockerfile
|
|
438
|
+
FROM node:18-alpine
|
|
439
|
+
|
|
440
|
+
WORKDIR /app
|
|
441
|
+
|
|
442
|
+
COPY package*.json ./
|
|
443
|
+
RUN npm install
|
|
444
|
+
|
|
445
|
+
COPY . .
|
|
446
|
+
|
|
447
|
+
ENV PORT=8080
|
|
448
|
+
EXPOSE 8080
|
|
449
|
+
|
|
450
|
+
CMD ["node", "server.js"]
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
**server.js:**
|
|
454
|
+
|
|
455
|
+
```javascript
|
|
456
|
+
import { startFlow } from '@walkeros/collector';
|
|
457
|
+
import { sourceExpress } from '@walkeros/server-source-express';
|
|
458
|
+
|
|
459
|
+
await startFlow({
|
|
460
|
+
sources: {
|
|
461
|
+
express: {
|
|
462
|
+
code: sourceExpress,
|
|
463
|
+
config: {
|
|
464
|
+
settings: {
|
|
465
|
+
port: process.env.PORT || 8080,
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
},
|
|
470
|
+
// Your destinations...
|
|
471
|
+
});
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Kubernetes
|
|
475
|
+
|
|
476
|
+
```yaml
|
|
477
|
+
apiVersion: apps/v1
|
|
478
|
+
kind: Deployment
|
|
479
|
+
metadata:
|
|
480
|
+
name: walkeros-collector
|
|
481
|
+
spec:
|
|
482
|
+
replicas: 3
|
|
483
|
+
selector:
|
|
484
|
+
matchLabels:
|
|
485
|
+
app: walkeros-collector
|
|
486
|
+
template:
|
|
487
|
+
metadata:
|
|
488
|
+
labels:
|
|
489
|
+
app: walkeros-collector
|
|
490
|
+
spec:
|
|
491
|
+
containers:
|
|
492
|
+
- name: collector
|
|
493
|
+
image: your-registry/walkeros-collector:latest
|
|
494
|
+
ports:
|
|
495
|
+
- containerPort: 8080
|
|
496
|
+
livenessProbe:
|
|
497
|
+
httpGet:
|
|
498
|
+
path: /health
|
|
499
|
+
port: 8080
|
|
500
|
+
initialDelaySeconds: 10
|
|
501
|
+
periodSeconds: 5
|
|
502
|
+
readinessProbe:
|
|
503
|
+
httpGet:
|
|
504
|
+
path: /ready
|
|
505
|
+
port: 8080
|
|
506
|
+
initialDelaySeconds: 5
|
|
507
|
+
periodSeconds: 3
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
## Testing
|
|
511
|
+
|
|
512
|
+
The package includes comprehensive tests using mocked Express Request/Response
|
|
513
|
+
objects.
|
|
514
|
+
|
|
515
|
+
**Run tests:**
|
|
516
|
+
|
|
517
|
+
```bash
|
|
518
|
+
npm test
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
**Example test:**
|
|
522
|
+
|
|
523
|
+
```typescript
|
|
524
|
+
import { sourceExpress } from '@walkeros/server-source-express';
|
|
525
|
+
|
|
526
|
+
test('should process POST event', async () => {
|
|
527
|
+
const mockPush = jest.fn().mockResolvedValue({ event: { id: 'test' } });
|
|
528
|
+
|
|
529
|
+
const source = await sourceExpress(
|
|
530
|
+
{},
|
|
531
|
+
{
|
|
532
|
+
push: mockPush,
|
|
533
|
+
command: jest.fn(),
|
|
534
|
+
elb: jest.fn(),
|
|
535
|
+
},
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
const req = {
|
|
539
|
+
method: 'POST',
|
|
540
|
+
body: { event: 'page view', data: { title: 'Home' } },
|
|
541
|
+
headers: {},
|
|
542
|
+
get: () => undefined,
|
|
543
|
+
};
|
|
544
|
+
const res = {
|
|
545
|
+
status: jest.fn().returnThis(),
|
|
546
|
+
json: jest.fn(),
|
|
547
|
+
send: jest.fn(),
|
|
548
|
+
set: jest.fn(),
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
await source.push(req, res);
|
|
552
|
+
|
|
553
|
+
expect(res.status).toHaveBeenCalledWith(200);
|
|
554
|
+
expect(mockPush).toHaveBeenCalled();
|
|
555
|
+
});
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
## License
|
|
559
|
+
|
|
560
|
+
MIT
|
|
561
|
+
|
|
562
|
+
## Links
|
|
563
|
+
|
|
564
|
+
- [walkerOS Documentation](https://github.com/elbwalker/walkerOS)
|
|
565
|
+
- [Express.js](https://expressjs.com/)
|
|
566
|
+
- [GitHub Repository](https://github.com/elbwalker/walkerOS)
|
|
567
|
+
- [Report Issues](https://github.com/elbwalker/walkerOS/issues)
|
package/dist/dev.d.mts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { z } from '@walkeros/core/dev';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* HTTP methods enum
|
|
5
|
+
*/
|
|
6
|
+
declare const HttpMethod: z.ZodEnum<{
|
|
7
|
+
GET: "GET";
|
|
8
|
+
POST: "POST";
|
|
9
|
+
PUT: "PUT";
|
|
10
|
+
PATCH: "PATCH";
|
|
11
|
+
DELETE: "DELETE";
|
|
12
|
+
OPTIONS: "OPTIONS";
|
|
13
|
+
HEAD: "HEAD";
|
|
14
|
+
}>;
|
|
15
|
+
/**
|
|
16
|
+
* CORS origin configuration
|
|
17
|
+
* Accepts:
|
|
18
|
+
* - '*' for all origins
|
|
19
|
+
* - Single URL string
|
|
20
|
+
* - Array of URL strings
|
|
21
|
+
*/
|
|
22
|
+
declare const CorsOrigin: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodLiteral<"*">]>;
|
|
23
|
+
/**
|
|
24
|
+
* CORS options schema
|
|
25
|
+
* Configuration for Cross-Origin Resource Sharing
|
|
26
|
+
*/
|
|
27
|
+
declare const CorsOptionsSchema: z.ZodObject<{
|
|
28
|
+
origin: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodLiteral<"*">]>>;
|
|
29
|
+
methods: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
30
|
+
GET: "GET";
|
|
31
|
+
POST: "POST";
|
|
32
|
+
PUT: "PUT";
|
|
33
|
+
PATCH: "PATCH";
|
|
34
|
+
DELETE: "DELETE";
|
|
35
|
+
OPTIONS: "OPTIONS";
|
|
36
|
+
HEAD: "HEAD";
|
|
37
|
+
}>>>;
|
|
38
|
+
headers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
39
|
+
credentials: z.ZodOptional<z.ZodBoolean>;
|
|
40
|
+
maxAge: z.ZodOptional<z.ZodNumber>;
|
|
41
|
+
}, z.core.$strip>;
|
|
42
|
+
type CorsOptions = z.infer<typeof CorsOptionsSchema>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Express source settings schema
|
|
46
|
+
*/
|
|
47
|
+
declare const SettingsSchema: z.ZodObject<{
|
|
48
|
+
port: z.ZodOptional<z.ZodNumber>;
|
|
49
|
+
path: z.ZodDefault<z.ZodString>;
|
|
50
|
+
cors: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
|
|
51
|
+
origin: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodLiteral<"*">]>>;
|
|
52
|
+
methods: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
53
|
+
GET: "GET";
|
|
54
|
+
POST: "POST";
|
|
55
|
+
PUT: "PUT";
|
|
56
|
+
PATCH: "PATCH";
|
|
57
|
+
DELETE: "DELETE";
|
|
58
|
+
OPTIONS: "OPTIONS";
|
|
59
|
+
HEAD: "HEAD";
|
|
60
|
+
}>>>;
|
|
61
|
+
headers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
62
|
+
credentials: z.ZodOptional<z.ZodBoolean>;
|
|
63
|
+
maxAge: z.ZodOptional<z.ZodNumber>;
|
|
64
|
+
}, z.core.$strip>]>>;
|
|
65
|
+
status: z.ZodDefault<z.ZodBoolean>;
|
|
66
|
+
}, z.core.$strip>;
|
|
67
|
+
type Settings = z.infer<typeof SettingsSchema>;
|
|
68
|
+
|
|
69
|
+
type index_CorsOptions = CorsOptions;
|
|
70
|
+
declare const index_CorsOptionsSchema: typeof CorsOptionsSchema;
|
|
71
|
+
declare const index_CorsOrigin: typeof CorsOrigin;
|
|
72
|
+
declare const index_HttpMethod: typeof HttpMethod;
|
|
73
|
+
type index_Settings = Settings;
|
|
74
|
+
declare const index_SettingsSchema: typeof SettingsSchema;
|
|
75
|
+
declare namespace index {
|
|
76
|
+
export { type index_CorsOptions as CorsOptions, index_CorsOptionsSchema as CorsOptionsSchema, index_CorsOrigin as CorsOrigin, index_HttpMethod as HttpMethod, type index_Settings as Settings, index_SettingsSchema as SettingsSchema };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export { index as schemas };
|
package/dist/dev.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { z } from '@walkeros/core/dev';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* HTTP methods enum
|
|
5
|
+
*/
|
|
6
|
+
declare const HttpMethod: z.ZodEnum<{
|
|
7
|
+
GET: "GET";
|
|
8
|
+
POST: "POST";
|
|
9
|
+
PUT: "PUT";
|
|
10
|
+
PATCH: "PATCH";
|
|
11
|
+
DELETE: "DELETE";
|
|
12
|
+
OPTIONS: "OPTIONS";
|
|
13
|
+
HEAD: "HEAD";
|
|
14
|
+
}>;
|
|
15
|
+
/**
|
|
16
|
+
* CORS origin configuration
|
|
17
|
+
* Accepts:
|
|
18
|
+
* - '*' for all origins
|
|
19
|
+
* - Single URL string
|
|
20
|
+
* - Array of URL strings
|
|
21
|
+
*/
|
|
22
|
+
declare const CorsOrigin: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodLiteral<"*">]>;
|
|
23
|
+
/**
|
|
24
|
+
* CORS options schema
|
|
25
|
+
* Configuration for Cross-Origin Resource Sharing
|
|
26
|
+
*/
|
|
27
|
+
declare const CorsOptionsSchema: z.ZodObject<{
|
|
28
|
+
origin: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodLiteral<"*">]>>;
|
|
29
|
+
methods: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
30
|
+
GET: "GET";
|
|
31
|
+
POST: "POST";
|
|
32
|
+
PUT: "PUT";
|
|
33
|
+
PATCH: "PATCH";
|
|
34
|
+
DELETE: "DELETE";
|
|
35
|
+
OPTIONS: "OPTIONS";
|
|
36
|
+
HEAD: "HEAD";
|
|
37
|
+
}>>>;
|
|
38
|
+
headers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
39
|
+
credentials: z.ZodOptional<z.ZodBoolean>;
|
|
40
|
+
maxAge: z.ZodOptional<z.ZodNumber>;
|
|
41
|
+
}, z.core.$strip>;
|
|
42
|
+
type CorsOptions = z.infer<typeof CorsOptionsSchema>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Express source settings schema
|
|
46
|
+
*/
|
|
47
|
+
declare const SettingsSchema: z.ZodObject<{
|
|
48
|
+
port: z.ZodOptional<z.ZodNumber>;
|
|
49
|
+
path: z.ZodDefault<z.ZodString>;
|
|
50
|
+
cors: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
|
|
51
|
+
origin: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodLiteral<"*">]>>;
|
|
52
|
+
methods: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
53
|
+
GET: "GET";
|
|
54
|
+
POST: "POST";
|
|
55
|
+
PUT: "PUT";
|
|
56
|
+
PATCH: "PATCH";
|
|
57
|
+
DELETE: "DELETE";
|
|
58
|
+
OPTIONS: "OPTIONS";
|
|
59
|
+
HEAD: "HEAD";
|
|
60
|
+
}>>>;
|
|
61
|
+
headers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
62
|
+
credentials: z.ZodOptional<z.ZodBoolean>;
|
|
63
|
+
maxAge: z.ZodOptional<z.ZodNumber>;
|
|
64
|
+
}, z.core.$strip>]>>;
|
|
65
|
+
status: z.ZodDefault<z.ZodBoolean>;
|
|
66
|
+
}, z.core.$strip>;
|
|
67
|
+
type Settings = z.infer<typeof SettingsSchema>;
|
|
68
|
+
|
|
69
|
+
type index_CorsOptions = CorsOptions;
|
|
70
|
+
declare const index_CorsOptionsSchema: typeof CorsOptionsSchema;
|
|
71
|
+
declare const index_CorsOrigin: typeof CorsOrigin;
|
|
72
|
+
declare const index_HttpMethod: typeof HttpMethod;
|
|
73
|
+
type index_Settings = Settings;
|
|
74
|
+
declare const index_SettingsSchema: typeof SettingsSchema;
|
|
75
|
+
declare namespace index {
|
|
76
|
+
export { type index_CorsOptions as CorsOptions, index_CorsOptionsSchema as CorsOptionsSchema, index_CorsOrigin as CorsOrigin, index_HttpMethod as HttpMethod, type index_Settings as Settings, index_SettingsSchema as SettingsSchema };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export { index as schemas };
|
package/dist/dev.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var e,o=Object.defineProperty,r=Object.getOwnPropertyDescriptor,t=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,i=(e,r)=>{for(var t in r)o(e,t,{get:r[t],enumerable:!0})},n={};i(n,{schemas:()=>l}),module.exports=(e=n,((e,i,n,l)=>{if(i&&"object"==typeof i||"function"==typeof i)for(let s of t(i))a.call(e,s)||s===n||o(e,s,{get:()=>i[s],enumerable:!(l=r(i,s))||l.enumerable});return e})(o({},"__esModule",{value:!0}),e));var l={};i(l,{CorsOptionsSchema:()=>p,CorsOrigin:()=>d,HttpMethod:()=>c,SettingsSchema:()=>u});var s=require("@walkeros/core/dev"),c=s.z.enum(["GET","POST","PUT","PATCH","DELETE","OPTIONS","HEAD"]),d=s.z.union([s.z.string(),s.z.array(s.z.string()),s.z.literal("*")]),p=s.z.object({origin:d.describe("Allowed origins (* for all, URL string, or array of URLs)").optional(),methods:s.z.array(c).describe("Allowed HTTP methods").optional(),headers:s.z.array(s.z.string()).describe("Allowed request headers").optional(),credentials:s.z.boolean().describe("Allow credentials (cookies, authorization headers)").optional(),maxAge:s.z.number().int().positive().describe("Preflight cache duration in seconds").optional()}),b=require("@walkeros/core/dev"),u=b.z.object({port:b.z.number().int().min(0).max(65535).describe("HTTP server port to listen on. Use 0 for random available port. If not provided, server will not start (app only mode)").optional(),path:b.z.string().describe("Event collection endpoint path").default("/collect"),cors:b.z.union([b.z.boolean(),p]).describe("CORS configuration: false = disabled, true = allow all origins (default), object = custom configuration").default(!0),status:b.z.boolean().describe("Enable health check endpoints (/health, /ready)").default(!0)});//# sourceMappingURL=dev.js.map
|
package/dist/dev.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/dev.ts","../src/schemas/index.ts","../src/schemas/primitives.ts","../src/schemas/settings.ts"],"sourcesContent":["export * as schemas from './schemas';\n","export * from './primitives';\nexport * from './settings';\n","import { z } from '@walkeros/core/dev';\n\n/**\n * HTTP methods enum\n */\nexport const HttpMethod = z.enum([\n 'GET',\n 'POST',\n 'PUT',\n 'PATCH',\n 'DELETE',\n 'OPTIONS',\n 'HEAD',\n]);\n\n/**\n * CORS origin configuration\n * Accepts:\n * - '*' for all origins\n * - Single URL string\n * - Array of URL strings\n */\nexport const CorsOrigin = z.union([\n z.string(),\n z.array(z.string()),\n z.literal('*'),\n]);\n\n/**\n * CORS options schema\n * Configuration for Cross-Origin Resource Sharing\n */\nexport const CorsOptionsSchema = z.object({\n origin: CorsOrigin.describe(\n 'Allowed origins (* for all, URL string, or array of URLs)',\n ).optional(),\n\n methods: z.array(HttpMethod).describe('Allowed HTTP methods').optional(),\n\n headers: z.array(z.string()).describe('Allowed request headers').optional(),\n\n credentials: z\n .boolean()\n .describe('Allow credentials (cookies, authorization headers)')\n .optional(),\n\n maxAge: z\n .number()\n .int()\n .positive()\n .describe('Preflight cache duration in seconds')\n .optional(),\n});\n\nexport type CorsOptions = z.infer<typeof CorsOptionsSchema>;\n","import { z } from '@walkeros/core/dev';\nimport { CorsOptionsSchema } from './primitives';\n\n/**\n * Express source settings schema\n */\nexport const SettingsSchema = z.object({\n port: z\n .number()\n .int()\n .min(0) // 0 = random available port\n .max(65535)\n .describe(\n 'HTTP server port to listen on. Use 0 for random available port. If not provided, server will not start (app only mode)',\n )\n .optional(),\n\n path: z\n .string()\n .describe('Event collection endpoint path')\n .default('/collect'),\n\n cors: z\n .union([z.boolean(), CorsOptionsSchema])\n .describe(\n 'CORS configuration: false = disabled, true = allow all origins (default), object = custom configuration',\n )\n .default(true),\n\n status: z\n .boolean()\n .describe('Enable health check endpoints (/health, /ready)')\n .default(true),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,iBAAkB;AAKX,IAAM,aAAa,aAAE,KAAK;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AASM,IAAM,aAAa,aAAE,MAAM;AAAA,EAChC,aAAE,OAAO;AAAA,EACT,aAAE,MAAM,aAAE,OAAO,CAAC;AAAA,EAClB,aAAE,QAAQ,GAAG;AACf,CAAC;AAMM,IAAM,oBAAoB,aAAE,OAAO;AAAA,EACxC,QAAQ,WAAW;AAAA,IACjB;AAAA,EACF,EAAE,SAAS;AAAA,EAEX,SAAS,aAAE,MAAM,UAAU,EAAE,SAAS,sBAAsB,EAAE,SAAS;AAAA,EAEvE,SAAS,aAAE,MAAM,aAAE,OAAO,CAAC,EAAE,SAAS,yBAAyB,EAAE,SAAS;AAAA,EAE1E,aAAa,aACV,QAAQ,EACR,SAAS,oDAAoD,EAC7D,SAAS;AAAA,EAEZ,QAAQ,aACL,OAAO,EACP,IAAI,EACJ,SAAS,EACT,SAAS,qCAAqC,EAC9C,SAAS;AACd,CAAC;;;ACpDD,IAAAA,cAAkB;AAMX,IAAM,iBAAiB,cAAE,OAAO;AAAA,EACrC,MAAM,cACH,OAAO,EACP,IAAI,EACJ,IAAI,CAAC,EACL,IAAI,KAAK,EACT;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EAEZ,MAAM,cACH,OAAO,EACP,SAAS,gCAAgC,EACzC,QAAQ,UAAU;AAAA,EAErB,MAAM,cACH,MAAM,CAAC,cAAE,QAAQ,GAAG,iBAAiB,CAAC,EACtC;AAAA,IACC;AAAA,EACF,EACC,QAAQ,IAAI;AAAA,EAEf,QAAQ,cACL,QAAQ,EACR,SAAS,iDAAiD,EAC1D,QAAQ,IAAI;AACjB,CAAC;","names":["import_dev"]}
|
package/dist/dev.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.defineProperty,o={};((o,r)=>{for(var t in r)e(o,t,{get:r[t],enumerable:!0})})(o,{CorsOptionsSchema:()=>i,CorsOrigin:()=>a,HttpMethod:()=>t,SettingsSchema:()=>s});import{z as r}from"@walkeros/core/dev";var t=r.enum(["GET","POST","PUT","PATCH","DELETE","OPTIONS","HEAD"]),a=r.union([r.string(),r.array(r.string()),r.literal("*")]),i=r.object({origin:a.describe("Allowed origins (* for all, URL string, or array of URLs)").optional(),methods:r.array(t).describe("Allowed HTTP methods").optional(),headers:r.array(r.string()).describe("Allowed request headers").optional(),credentials:r.boolean().describe("Allow credentials (cookies, authorization headers)").optional(),maxAge:r.number().int().positive().describe("Preflight cache duration in seconds").optional()});import{z as n}from"@walkeros/core/dev";var s=n.object({port:n.number().int().min(0).max(65535).describe("HTTP server port to listen on. Use 0 for random available port. If not provided, server will not start (app only mode)").optional(),path:n.string().describe("Event collection endpoint path").default("/collect"),cors:n.union([n.boolean(),i]).describe("CORS configuration: false = disabled, true = allow all origins (default), object = custom configuration").default(!0),status:n.boolean().describe("Enable health check endpoints (/health, /ready)").default(!0)});export{o as schemas};//# sourceMappingURL=dev.mjs.map
|
package/dist/dev.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/schemas/index.ts","../src/schemas/primitives.ts","../src/schemas/settings.ts"],"sourcesContent":["export * from './primitives';\nexport * from './settings';\n","import { z } from '@walkeros/core/dev';\n\n/**\n * HTTP methods enum\n */\nexport const HttpMethod = z.enum([\n 'GET',\n 'POST',\n 'PUT',\n 'PATCH',\n 'DELETE',\n 'OPTIONS',\n 'HEAD',\n]);\n\n/**\n * CORS origin configuration\n * Accepts:\n * - '*' for all origins\n * - Single URL string\n * - Array of URL strings\n */\nexport const CorsOrigin = z.union([\n z.string(),\n z.array(z.string()),\n z.literal('*'),\n]);\n\n/**\n * CORS options schema\n * Configuration for Cross-Origin Resource Sharing\n */\nexport const CorsOptionsSchema = z.object({\n origin: CorsOrigin.describe(\n 'Allowed origins (* for all, URL string, or array of URLs)',\n ).optional(),\n\n methods: z.array(HttpMethod).describe('Allowed HTTP methods').optional(),\n\n headers: z.array(z.string()).describe('Allowed request headers').optional(),\n\n credentials: z\n .boolean()\n .describe('Allow credentials (cookies, authorization headers)')\n .optional(),\n\n maxAge: z\n .number()\n .int()\n .positive()\n .describe('Preflight cache duration in seconds')\n .optional(),\n});\n\nexport type CorsOptions = z.infer<typeof CorsOptionsSchema>;\n","import { z } from '@walkeros/core/dev';\nimport { CorsOptionsSchema } from './primitives';\n\n/**\n * Express source settings schema\n */\nexport const SettingsSchema = z.object({\n port: z\n .number()\n .int()\n .min(0) // 0 = random available port\n .max(65535)\n .describe(\n 'HTTP server port to listen on. Use 0 for random available port. If not provided, server will not start (app only mode)',\n )\n .optional(),\n\n path: z\n .string()\n .describe('Event collection endpoint path')\n .default('/collect'),\n\n cors: z\n .union([z.boolean(), CorsOptionsSchema])\n .describe(\n 'CORS configuration: false = disabled, true = allow all origins (default), object = custom configuration',\n )\n .default(true),\n\n status: z\n .boolean()\n .describe('Enable health check endpoints (/health, /ready)')\n .default(true),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n"],"mappings":";;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,SAAS,SAAS;AAKX,IAAM,aAAa,EAAE,KAAK;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AASM,IAAM,aAAa,EAAE,MAAM;AAAA,EAChC,EAAE,OAAO;AAAA,EACT,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,EAClB,EAAE,QAAQ,GAAG;AACf,CAAC;AAMM,IAAM,oBAAoB,EAAE,OAAO;AAAA,EACxC,QAAQ,WAAW;AAAA,IACjB;AAAA,EACF,EAAE,SAAS;AAAA,EAEX,SAAS,EAAE,MAAM,UAAU,EAAE,SAAS,sBAAsB,EAAE,SAAS;AAAA,EAEvE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,yBAAyB,EAAE,SAAS;AAAA,EAE1E,aAAa,EACV,QAAQ,EACR,SAAS,oDAAoD,EAC7D,SAAS;AAAA,EAEZ,QAAQ,EACL,OAAO,EACP,IAAI,EACJ,SAAS,EACT,SAAS,qCAAqC,EAC9C,SAAS;AACd,CAAC;;;ACpDD,SAAS,KAAAA,UAAS;AAMX,IAAM,iBAAiBC,GAAE,OAAO;AAAA,EACrC,MAAMA,GACH,OAAO,EACP,IAAI,EACJ,IAAI,CAAC,EACL,IAAI,KAAK,EACT;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EAEZ,MAAMA,GACH,OAAO,EACP,SAAS,gCAAgC,EACzC,QAAQ,UAAU;AAAA,EAErB,MAAMA,GACH,MAAM,CAACA,GAAE,QAAQ,GAAG,iBAAiB,CAAC,EACtC;AAAA,IACC;AAAA,EACF,EACC,QAAQ,IAAI;AAAA,EAEf,QAAQA,GACL,QAAQ,EACR,SAAS,iDAAiD,EAC1D,QAAQ,IAAI;AACjB,CAAC;","names":["z","z"]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Source, WalkerOS } from '@walkeros/core';
|
|
2
|
+
import { Request, Response, Application } from 'express';
|
|
3
|
+
import { z } from '@walkeros/core/dev';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* CORS options schema
|
|
7
|
+
* Configuration for Cross-Origin Resource Sharing
|
|
8
|
+
*/
|
|
9
|
+
declare const CorsOptionsSchema: z.ZodObject<{
|
|
10
|
+
origin: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodLiteral<"*">]>>;
|
|
11
|
+
methods: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
12
|
+
GET: "GET";
|
|
13
|
+
POST: "POST";
|
|
14
|
+
PUT: "PUT";
|
|
15
|
+
PATCH: "PATCH";
|
|
16
|
+
DELETE: "DELETE";
|
|
17
|
+
OPTIONS: "OPTIONS";
|
|
18
|
+
HEAD: "HEAD";
|
|
19
|
+
}>>>;
|
|
20
|
+
headers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
21
|
+
credentials: z.ZodOptional<z.ZodBoolean>;
|
|
22
|
+
maxAge: z.ZodOptional<z.ZodNumber>;
|
|
23
|
+
}, z.core.$strip>;
|
|
24
|
+
type CorsOptions = z.infer<typeof CorsOptionsSchema>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Express source settings schema
|
|
28
|
+
*/
|
|
29
|
+
declare const SettingsSchema: z.ZodObject<{
|
|
30
|
+
port: z.ZodOptional<z.ZodNumber>;
|
|
31
|
+
path: z.ZodDefault<z.ZodString>;
|
|
32
|
+
cors: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
|
|
33
|
+
origin: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodLiteral<"*">]>>;
|
|
34
|
+
methods: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
35
|
+
GET: "GET";
|
|
36
|
+
POST: "POST";
|
|
37
|
+
PUT: "PUT";
|
|
38
|
+
PATCH: "PATCH";
|
|
39
|
+
DELETE: "DELETE";
|
|
40
|
+
OPTIONS: "OPTIONS";
|
|
41
|
+
HEAD: "HEAD";
|
|
42
|
+
}>>>;
|
|
43
|
+
headers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
44
|
+
credentials: z.ZodOptional<z.ZodBoolean>;
|
|
45
|
+
maxAge: z.ZodOptional<z.ZodNumber>;
|
|
46
|
+
}, z.core.$strip>]>>;
|
|
47
|
+
status: z.ZodDefault<z.ZodBoolean>;
|
|
48
|
+
}, z.core.$strip>;
|
|
49
|
+
|
|
50
|
+
type Settings = z.infer<typeof SettingsSchema>;
|
|
51
|
+
type InitSettings = Partial<Settings>;
|
|
52
|
+
interface Mapping {
|
|
53
|
+
}
|
|
54
|
+
type Push = (req: Request, res: Response) => Promise<void>;
|
|
55
|
+
interface Env extends Source.Env {
|
|
56
|
+
req?: Request;
|
|
57
|
+
res?: Response;
|
|
58
|
+
}
|
|
59
|
+
type Types = Source.Types<Settings, Mapping, Push, Env, InitSettings>;
|
|
60
|
+
type Config = Source.Config<Types>;
|
|
61
|
+
type PartialConfig = Source.PartialConfig<Types>;
|
|
62
|
+
interface ExpressSource extends Omit<Source.Instance<Types>, 'push'> {
|
|
63
|
+
push: Push;
|
|
64
|
+
app: Application;
|
|
65
|
+
server?: ReturnType<Application['listen']>;
|
|
66
|
+
}
|
|
67
|
+
interface EventRequest {
|
|
68
|
+
event: string;
|
|
69
|
+
data?: WalkerOS.AnyObject;
|
|
70
|
+
context?: WalkerOS.AnyObject;
|
|
71
|
+
user?: WalkerOS.AnyObject;
|
|
72
|
+
globals?: WalkerOS.AnyObject;
|
|
73
|
+
consent?: WalkerOS.AnyObject;
|
|
74
|
+
}
|
|
75
|
+
interface EventResponse {
|
|
76
|
+
success: boolean;
|
|
77
|
+
id?: string;
|
|
78
|
+
timestamp?: number;
|
|
79
|
+
error?: string;
|
|
80
|
+
}
|
|
81
|
+
type RequestBody = EventRequest;
|
|
82
|
+
type ResponseBody = EventResponse;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Set CORS headers on response
|
|
86
|
+
*
|
|
87
|
+
* @param res Express response object
|
|
88
|
+
* @param corsConfig CORS configuration (false = disabled, true = allow all, object = custom)
|
|
89
|
+
*/
|
|
90
|
+
declare function setCorsHeaders(res: Response, corsConfig?: boolean | CorsOptions): void;
|
|
91
|
+
/**
|
|
92
|
+
* 1x1 transparent GIF for pixel tracking
|
|
93
|
+
* Base64-encoded GIF (43 bytes)
|
|
94
|
+
*/
|
|
95
|
+
declare const TRANSPARENT_GIF: Buffer<ArrayBuffer>;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Express source initialization
|
|
99
|
+
*
|
|
100
|
+
* This source OWNS its HTTP server infrastructure:
|
|
101
|
+
* - Creates Express application
|
|
102
|
+
* - Sets up middleware (JSON parsing, CORS)
|
|
103
|
+
* - Registers event collection endpoints (POST, GET, OPTIONS)
|
|
104
|
+
* - Starts HTTP server (if port configured)
|
|
105
|
+
* - Handles graceful shutdown
|
|
106
|
+
*
|
|
107
|
+
* @param config Partial source configuration
|
|
108
|
+
* @param env Source environment with push, command, elb functions
|
|
109
|
+
* @returns Express source instance with app and push handler
|
|
110
|
+
*/
|
|
111
|
+
declare const sourceExpress: (config: PartialConfig, env: Types["env"]) => Promise<ExpressSource>;
|
|
112
|
+
|
|
113
|
+
export { type Config, type Env, type EventRequest, type EventResponse, type ExpressSource, type InitSettings, type Mapping, type PartialConfig, type Push, type RequestBody, type ResponseBody, type Settings, TRANSPARENT_GIF, type Types, setCorsHeaders, sourceExpress };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Source, WalkerOS } from '@walkeros/core';
|
|
2
|
+
import { Request, Response, Application } from 'express';
|
|
3
|
+
import { z } from '@walkeros/core/dev';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* CORS options schema
|
|
7
|
+
* Configuration for Cross-Origin Resource Sharing
|
|
8
|
+
*/
|
|
9
|
+
declare const CorsOptionsSchema: z.ZodObject<{
|
|
10
|
+
origin: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodLiteral<"*">]>>;
|
|
11
|
+
methods: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
12
|
+
GET: "GET";
|
|
13
|
+
POST: "POST";
|
|
14
|
+
PUT: "PUT";
|
|
15
|
+
PATCH: "PATCH";
|
|
16
|
+
DELETE: "DELETE";
|
|
17
|
+
OPTIONS: "OPTIONS";
|
|
18
|
+
HEAD: "HEAD";
|
|
19
|
+
}>>>;
|
|
20
|
+
headers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
21
|
+
credentials: z.ZodOptional<z.ZodBoolean>;
|
|
22
|
+
maxAge: z.ZodOptional<z.ZodNumber>;
|
|
23
|
+
}, z.core.$strip>;
|
|
24
|
+
type CorsOptions = z.infer<typeof CorsOptionsSchema>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Express source settings schema
|
|
28
|
+
*/
|
|
29
|
+
declare const SettingsSchema: z.ZodObject<{
|
|
30
|
+
port: z.ZodOptional<z.ZodNumber>;
|
|
31
|
+
path: z.ZodDefault<z.ZodString>;
|
|
32
|
+
cors: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
|
|
33
|
+
origin: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodLiteral<"*">]>>;
|
|
34
|
+
methods: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
35
|
+
GET: "GET";
|
|
36
|
+
POST: "POST";
|
|
37
|
+
PUT: "PUT";
|
|
38
|
+
PATCH: "PATCH";
|
|
39
|
+
DELETE: "DELETE";
|
|
40
|
+
OPTIONS: "OPTIONS";
|
|
41
|
+
HEAD: "HEAD";
|
|
42
|
+
}>>>;
|
|
43
|
+
headers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
44
|
+
credentials: z.ZodOptional<z.ZodBoolean>;
|
|
45
|
+
maxAge: z.ZodOptional<z.ZodNumber>;
|
|
46
|
+
}, z.core.$strip>]>>;
|
|
47
|
+
status: z.ZodDefault<z.ZodBoolean>;
|
|
48
|
+
}, z.core.$strip>;
|
|
49
|
+
|
|
50
|
+
type Settings = z.infer<typeof SettingsSchema>;
|
|
51
|
+
type InitSettings = Partial<Settings>;
|
|
52
|
+
interface Mapping {
|
|
53
|
+
}
|
|
54
|
+
type Push = (req: Request, res: Response) => Promise<void>;
|
|
55
|
+
interface Env extends Source.Env {
|
|
56
|
+
req?: Request;
|
|
57
|
+
res?: Response;
|
|
58
|
+
}
|
|
59
|
+
type Types = Source.Types<Settings, Mapping, Push, Env, InitSettings>;
|
|
60
|
+
type Config = Source.Config<Types>;
|
|
61
|
+
type PartialConfig = Source.PartialConfig<Types>;
|
|
62
|
+
interface ExpressSource extends Omit<Source.Instance<Types>, 'push'> {
|
|
63
|
+
push: Push;
|
|
64
|
+
app: Application;
|
|
65
|
+
server?: ReturnType<Application['listen']>;
|
|
66
|
+
}
|
|
67
|
+
interface EventRequest {
|
|
68
|
+
event: string;
|
|
69
|
+
data?: WalkerOS.AnyObject;
|
|
70
|
+
context?: WalkerOS.AnyObject;
|
|
71
|
+
user?: WalkerOS.AnyObject;
|
|
72
|
+
globals?: WalkerOS.AnyObject;
|
|
73
|
+
consent?: WalkerOS.AnyObject;
|
|
74
|
+
}
|
|
75
|
+
interface EventResponse {
|
|
76
|
+
success: boolean;
|
|
77
|
+
id?: string;
|
|
78
|
+
timestamp?: number;
|
|
79
|
+
error?: string;
|
|
80
|
+
}
|
|
81
|
+
type RequestBody = EventRequest;
|
|
82
|
+
type ResponseBody = EventResponse;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Set CORS headers on response
|
|
86
|
+
*
|
|
87
|
+
* @param res Express response object
|
|
88
|
+
* @param corsConfig CORS configuration (false = disabled, true = allow all, object = custom)
|
|
89
|
+
*/
|
|
90
|
+
declare function setCorsHeaders(res: Response, corsConfig?: boolean | CorsOptions): void;
|
|
91
|
+
/**
|
|
92
|
+
* 1x1 transparent GIF for pixel tracking
|
|
93
|
+
* Base64-encoded GIF (43 bytes)
|
|
94
|
+
*/
|
|
95
|
+
declare const TRANSPARENT_GIF: Buffer<ArrayBuffer>;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Express source initialization
|
|
99
|
+
*
|
|
100
|
+
* This source OWNS its HTTP server infrastructure:
|
|
101
|
+
* - Creates Express application
|
|
102
|
+
* - Sets up middleware (JSON parsing, CORS)
|
|
103
|
+
* - Registers event collection endpoints (POST, GET, OPTIONS)
|
|
104
|
+
* - Starts HTTP server (if port configured)
|
|
105
|
+
* - Handles graceful shutdown
|
|
106
|
+
*
|
|
107
|
+
* @param config Partial source configuration
|
|
108
|
+
* @param env Source environment with push, command, elb functions
|
|
109
|
+
* @returns Express source instance with app and push handler
|
|
110
|
+
*/
|
|
111
|
+
declare const sourceExpress: (config: PartialConfig, env: Types["env"]) => Promise<ExpressSource>;
|
|
112
|
+
|
|
113
|
+
export { type Config, type Env, type EventRequest, type EventResponse, type ExpressSource, type InitSettings, type Mapping, type PartialConfig, type Push, type RequestBody, type ResponseBody, type Settings, TRANSPARENT_GIF, type Types, setCorsHeaders, sourceExpress };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var mod,__create=Object.create,__defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__getProtoOf=Object.getPrototypeOf,__hasOwnProp=Object.prototype.hasOwnProperty,__copyProps=(to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toESM=(mod,isNodeMode,target)=>(target=null!=mod?__create(__getProtoOf(mod)):{},__copyProps(!isNodeMode&&mod&&mod.__esModule?target:__defProp(target,"default",{value:mod,enumerable:!0}),mod)),index_exports={};((target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})})(index_exports,{TRANSPARENT_GIF:()=>TRANSPARENT_GIF,setCorsHeaders:()=>setCorsHeaders,sourceExpress:()=>sourceExpress}),module.exports=(mod=index_exports,__copyProps(__defProp({},"__esModule",{value:!0}),mod));var import_express=__toESM(require("express")),import_cors=__toESM(require("cors")),import_core=require("@walkeros/core"),import_dev=require("@walkeros/core/dev"),HttpMethod=import_dev.z.enum(["GET","POST","PUT","PATCH","DELETE","OPTIONS","HEAD"]),CorsOrigin=import_dev.z.union([import_dev.z.string(),import_dev.z.array(import_dev.z.string()),import_dev.z.literal("*")]),CorsOptionsSchema=import_dev.z.object({origin:CorsOrigin.describe("Allowed origins (* for all, URL string, or array of URLs)").optional(),methods:import_dev.z.array(HttpMethod).describe("Allowed HTTP methods").optional(),headers:import_dev.z.array(import_dev.z.string()).describe("Allowed request headers").optional(),credentials:import_dev.z.boolean().describe("Allow credentials (cookies, authorization headers)").optional(),maxAge:import_dev.z.number().int().positive().describe("Preflight cache duration in seconds").optional()}),import_dev2=require("@walkeros/core/dev"),SettingsSchema=import_dev2.z.object({port:import_dev2.z.number().int().min(0).max(65535).describe("HTTP server port to listen on. Use 0 for random available port. If not provided, server will not start (app only mode)").optional(),path:import_dev2.z.string().describe("Event collection endpoint path").default("/collect"),cors:import_dev2.z.union([import_dev2.z.boolean(),CorsOptionsSchema]).describe("CORS configuration: false = disabled, true = allow all origins (default), object = custom configuration").default(!0),status:import_dev2.z.boolean().describe("Enable health check endpoints (/health, /ready)").default(!0)});function setCorsHeaders(res,corsConfig=!0){if(!1!==corsConfig)if(!0===corsConfig)res.set("Access-Control-Allow-Origin","*"),res.set("Access-Control-Allow-Methods","GET, POST, OPTIONS"),res.set("Access-Control-Allow-Headers","Content-Type");else{if(corsConfig.origin){const origin=Array.isArray(corsConfig.origin)?corsConfig.origin.join(", "):corsConfig.origin;res.set("Access-Control-Allow-Origin",origin)}corsConfig.methods&&res.set("Access-Control-Allow-Methods",corsConfig.methods.join(", ")),corsConfig.headers&&res.set("Access-Control-Allow-Headers",corsConfig.headers.join(", ")),corsConfig.credentials&&res.set("Access-Control-Allow-Credentials","true"),corsConfig.maxAge&&res.set("Access-Control-Max-Age",String(corsConfig.maxAge))}}var TRANSPARENT_GIF=Buffer.from("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7","base64"),sourceExpress=async(config,env)=>{const settings=SettingsSchema.parse(config.settings||{}),app=(0,import_express.default)();if(app.use(import_express.default.json({limit:"1mb"})),!1!==settings.cors){const corsOptions=!0===settings.cors?{}:settings.cors;app.use((0,import_cors.default)(corsOptions))}const push=async(req,res)=>{try{if("OPTIONS"===req.method)return setCorsHeaders(res,settings.cors),void res.status(204).send();if("GET"===req.method){const parsedData=(0,import_core.requestToData)(req.url);return parsedData&&"object"==typeof parsedData&&await env.push(parsedData),res.set("Content-Type","image/gif"),res.set("Cache-Control","no-cache, no-store, must-revalidate"),void res.send(TRANSPARENT_GIF)}if("POST"===req.method){const eventData=req.body;return eventData&&"object"==typeof eventData?(await env.push(eventData),void res.json({success:!0,timestamp:Date.now()})):void res.status(400).json({success:!1,error:"Invalid event: body must be an object"})}res.status(405).json({success:!1,error:"Method not allowed. Use POST, GET, or OPTIONS."})}catch(error){res.status(500).json({success:!1,error:error instanceof Error?error.message:"Internal server error"})}};let server;if(app.post(settings.path,push),app.get(settings.path,push),app.options(settings.path,push),settings.status&&(app.get("/health",(req,res)=>{res.json({status:"ok",timestamp:Date.now(),source:"express"})}),app.get("/ready",(req,res)=>{res.json({status:"ready",timestamp:Date.now(),source:"express"})})),void 0!==settings.port){server=app.listen(settings.port,()=>{const statusRoutes=settings.status?"\n GET /health - Health check\n GET /ready - Readiness check":"";env.logger.info(`Express source listening on port ${settings.port}\n POST ${settings.path} - Event collection (JSON body)\n GET ${settings.path} - Pixel tracking (query params)\n OPTIONS ${settings.path} - CORS preflight`+statusRoutes)});const shutdownHandler=()=>{server&&server.close()};process.on("SIGTERM",shutdownHandler),process.on("SIGINT",shutdownHandler)}return{type:"express",config:{...config,settings:settings},push:push,app:app,server:server}};//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/schemas/primitives.ts","../src/schemas/settings.ts","../src/utils.ts"],"sourcesContent":["import express, { type Request, type Response } from 'express';\nimport cors from 'cors';\nimport { requestToData } from '@walkeros/core';\nimport type {\n ExpressSource,\n PartialConfig,\n Types,\n EventRequest,\n} from './types';\nimport { SettingsSchema } from './schemas';\nimport { setCorsHeaders, TRANSPARENT_GIF } from './utils';\n\n/**\n * Express source initialization\n *\n * This source OWNS its HTTP server infrastructure:\n * - Creates Express application\n * - Sets up middleware (JSON parsing, CORS)\n * - Registers event collection endpoints (POST, GET, OPTIONS)\n * - Starts HTTP server (if port configured)\n * - Handles graceful shutdown\n *\n * @param config Partial source configuration\n * @param env Source environment with push, command, elb functions\n * @returns Express source instance with app and push handler\n */\nexport const sourceExpress = async (\n config: PartialConfig,\n env: Types['env'],\n): Promise<ExpressSource> => {\n // Validate and apply default settings\n const settings = SettingsSchema.parse(config.settings || {});\n\n const app = express();\n\n // Middleware setup - JSON body parsing with 10mb default limit\n app.use(express.json({ limit: '1mb' }));\n\n // CORS middleware (enabled by default)\n if (settings.cors !== false) {\n const corsOptions = settings.cors === true ? {} : settings.cors;\n app.use(cors(corsOptions));\n }\n\n /**\n * Request handler - transforms HTTP requests into walker events\n * Supports POST (JSON body), GET (query params), and OPTIONS (CORS preflight)\n */\n const push = async (req: Request, res: Response): Promise<void> => {\n try {\n // Handle OPTIONS for CORS preflight\n if (req.method === 'OPTIONS') {\n setCorsHeaders(res, settings.cors);\n res.status(204).send();\n return;\n }\n\n // Handle GET requests (pixel tracking)\n if (req.method === 'GET') {\n // Parse query parameters to event data using requestToData\n const parsedData = requestToData(req.url);\n\n // Send to collector\n if (parsedData && typeof parsedData === 'object') {\n await env.push(parsedData);\n }\n\n // Return 1x1 transparent GIF for pixel tracking\n res.set('Content-Type', 'image/gif');\n res.set('Cache-Control', 'no-cache, no-store, must-revalidate');\n res.send(TRANSPARENT_GIF);\n return;\n }\n\n // Handle POST requests (standard event ingestion)\n if (req.method === 'POST') {\n const eventData = req.body;\n\n if (!eventData || typeof eventData !== 'object') {\n res.status(400).json({\n success: false,\n error: 'Invalid event: body must be an object',\n });\n return;\n }\n\n // Send event to collector\n await env.push(eventData);\n\n res.json({\n success: true,\n timestamp: Date.now(),\n });\n return;\n }\n\n // Unsupported method\n res.status(405).json({\n success: false,\n error: 'Method not allowed. Use POST, GET, or OPTIONS.',\n });\n } catch (error) {\n res.status(500).json({\n success: false,\n error: error instanceof Error ? error.message : 'Internal server error',\n });\n }\n };\n\n // Register event collection endpoint (handles POST, GET, OPTIONS)\n app.post(settings.path, push);\n app.get(settings.path, push);\n app.options(settings.path, push);\n\n // Health check endpoints (if enabled)\n if (settings.status) {\n app.get('/health', (req, res) => {\n res.json({\n status: 'ok',\n timestamp: Date.now(),\n source: 'express',\n });\n });\n\n app.get('/ready', (req, res) => {\n res.json({\n status: 'ready',\n timestamp: Date.now(),\n source: 'express',\n });\n });\n }\n\n // Source owns the HTTP server (if port configured)\n let server: ReturnType<typeof app.listen> | undefined;\n\n if (settings.port !== undefined) {\n server = app.listen(settings.port, () => {\n const statusRoutes = settings.status\n ? `\\n GET /health - Health check\\n GET /ready - Readiness check`\n : '';\n env.logger.info(\n `Express source listening on port ${settings.port}\\n` +\n ` POST ${settings.path} - Event collection (JSON body)\\n` +\n ` GET ${settings.path} - Pixel tracking (query params)\\n` +\n ` OPTIONS ${settings.path} - CORS preflight` +\n statusRoutes,\n );\n });\n\n // Graceful shutdown handlers\n const shutdownHandler = () => {\n if (server) {\n server.close();\n }\n };\n\n process.on('SIGTERM', shutdownHandler);\n process.on('SIGINT', shutdownHandler);\n }\n\n const instance: ExpressSource = {\n type: 'express',\n config: {\n ...config,\n settings,\n },\n push,\n app, // Expose app for advanced usage\n server, // Expose server (if started)\n };\n\n return instance;\n};\n\n// Export types (avoid re-exporting duplicates from schemas)\nexport type {\n ExpressSource,\n Config,\n PartialConfig,\n Types,\n EventRequest,\n EventResponse,\n RequestBody,\n ResponseBody,\n Push,\n Env,\n Mapping,\n InitSettings,\n Settings,\n} from './types';\n\n// Export utils\nexport { setCorsHeaders, TRANSPARENT_GIF } from './utils';\n","import { z } from '@walkeros/core/dev';\n\n/**\n * HTTP methods enum\n */\nexport const HttpMethod = z.enum([\n 'GET',\n 'POST',\n 'PUT',\n 'PATCH',\n 'DELETE',\n 'OPTIONS',\n 'HEAD',\n]);\n\n/**\n * CORS origin configuration\n * Accepts:\n * - '*' for all origins\n * - Single URL string\n * - Array of URL strings\n */\nexport const CorsOrigin = z.union([\n z.string(),\n z.array(z.string()),\n z.literal('*'),\n]);\n\n/**\n * CORS options schema\n * Configuration for Cross-Origin Resource Sharing\n */\nexport const CorsOptionsSchema = z.object({\n origin: CorsOrigin.describe(\n 'Allowed origins (* for all, URL string, or array of URLs)',\n ).optional(),\n\n methods: z.array(HttpMethod).describe('Allowed HTTP methods').optional(),\n\n headers: z.array(z.string()).describe('Allowed request headers').optional(),\n\n credentials: z\n .boolean()\n .describe('Allow credentials (cookies, authorization headers)')\n .optional(),\n\n maxAge: z\n .number()\n .int()\n .positive()\n .describe('Preflight cache duration in seconds')\n .optional(),\n});\n\nexport type CorsOptions = z.infer<typeof CorsOptionsSchema>;\n","import { z } from '@walkeros/core/dev';\nimport { CorsOptionsSchema } from './primitives';\n\n/**\n * Express source settings schema\n */\nexport const SettingsSchema = z.object({\n port: z\n .number()\n .int()\n .min(0) // 0 = random available port\n .max(65535)\n .describe(\n 'HTTP server port to listen on. Use 0 for random available port. If not provided, server will not start (app only mode)',\n )\n .optional(),\n\n path: z\n .string()\n .describe('Event collection endpoint path')\n .default('/collect'),\n\n cors: z\n .union([z.boolean(), CorsOptionsSchema])\n .describe(\n 'CORS configuration: false = disabled, true = allow all origins (default), object = custom configuration',\n )\n .default(true),\n\n status: z\n .boolean()\n .describe('Enable health check endpoints (/health, /ready)')\n .default(true),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import type { Response } from 'express';\nimport type { CorsOptions } from './schemas';\n\n/**\n * Set CORS headers on response\n *\n * @param res Express response object\n * @param corsConfig CORS configuration (false = disabled, true = allow all, object = custom)\n */\nexport function setCorsHeaders(\n res: Response,\n corsConfig: boolean | CorsOptions = true,\n): void {\n if (corsConfig === false) return;\n\n if (corsConfig === true) {\n // Simple CORS - allow all\n res.set('Access-Control-Allow-Origin', '*');\n res.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.set('Access-Control-Allow-Headers', 'Content-Type');\n } else {\n // Custom CORS configuration\n if (corsConfig.origin) {\n const origin = Array.isArray(corsConfig.origin)\n ? corsConfig.origin.join(', ')\n : corsConfig.origin;\n res.set('Access-Control-Allow-Origin', origin);\n }\n\n if (corsConfig.methods) {\n res.set('Access-Control-Allow-Methods', corsConfig.methods.join(', '));\n }\n\n if (corsConfig.headers) {\n res.set('Access-Control-Allow-Headers', corsConfig.headers.join(', '));\n }\n\n if (corsConfig.credentials) {\n res.set('Access-Control-Allow-Credentials', 'true');\n }\n\n if (corsConfig.maxAge) {\n res.set('Access-Control-Max-Age', String(corsConfig.maxAge));\n }\n }\n}\n\n/**\n * 1x1 transparent GIF for pixel tracking\n * Base64-encoded GIF (43 bytes)\n */\nexport const TRANSPARENT_GIF = Buffer.from(\n 'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',\n 'base64',\n);\n"],"mappings":"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","names":["import_dev","express","cors"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import express from"express";import cors from"cors";import{requestToData}from"@walkeros/core";import{z}from"@walkeros/core/dev";var HttpMethod=z.enum(["GET","POST","PUT","PATCH","DELETE","OPTIONS","HEAD"]),CorsOrigin=z.union([z.string(),z.array(z.string()),z.literal("*")]),CorsOptionsSchema=z.object({origin:CorsOrigin.describe("Allowed origins (* for all, URL string, or array of URLs)").optional(),methods:z.array(HttpMethod).describe("Allowed HTTP methods").optional(),headers:z.array(z.string()).describe("Allowed request headers").optional(),credentials:z.boolean().describe("Allow credentials (cookies, authorization headers)").optional(),maxAge:z.number().int().positive().describe("Preflight cache duration in seconds").optional()});import{z as z2}from"@walkeros/core/dev";var SettingsSchema=z2.object({port:z2.number().int().min(0).max(65535).describe("HTTP server port to listen on. Use 0 for random available port. If not provided, server will not start (app only mode)").optional(),path:z2.string().describe("Event collection endpoint path").default("/collect"),cors:z2.union([z2.boolean(),CorsOptionsSchema]).describe("CORS configuration: false = disabled, true = allow all origins (default), object = custom configuration").default(!0),status:z2.boolean().describe("Enable health check endpoints (/health, /ready)").default(!0)});function setCorsHeaders(res,corsConfig=!0){if(!1!==corsConfig)if(!0===corsConfig)res.set("Access-Control-Allow-Origin","*"),res.set("Access-Control-Allow-Methods","GET, POST, OPTIONS"),res.set("Access-Control-Allow-Headers","Content-Type");else{if(corsConfig.origin){const origin=Array.isArray(corsConfig.origin)?corsConfig.origin.join(", "):corsConfig.origin;res.set("Access-Control-Allow-Origin",origin)}corsConfig.methods&&res.set("Access-Control-Allow-Methods",corsConfig.methods.join(", ")),corsConfig.headers&&res.set("Access-Control-Allow-Headers",corsConfig.headers.join(", ")),corsConfig.credentials&&res.set("Access-Control-Allow-Credentials","true"),corsConfig.maxAge&&res.set("Access-Control-Max-Age",String(corsConfig.maxAge))}}var TRANSPARENT_GIF=Buffer.from("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7","base64"),sourceExpress=async(config,env)=>{const settings=SettingsSchema.parse(config.settings||{}),app=express();if(app.use(express.json({limit:"1mb"})),!1!==settings.cors){const corsOptions=!0===settings.cors?{}:settings.cors;app.use(cors(corsOptions))}const push=async(req,res)=>{try{if("OPTIONS"===req.method)return setCorsHeaders(res,settings.cors),void res.status(204).send();if("GET"===req.method){const parsedData=requestToData(req.url);return parsedData&&"object"==typeof parsedData&&await env.push(parsedData),res.set("Content-Type","image/gif"),res.set("Cache-Control","no-cache, no-store, must-revalidate"),void res.send(TRANSPARENT_GIF)}if("POST"===req.method){const eventData=req.body;return eventData&&"object"==typeof eventData?(await env.push(eventData),void res.json({success:!0,timestamp:Date.now()})):void res.status(400).json({success:!1,error:"Invalid event: body must be an object"})}res.status(405).json({success:!1,error:"Method not allowed. Use POST, GET, or OPTIONS."})}catch(error){res.status(500).json({success:!1,error:error instanceof Error?error.message:"Internal server error"})}};let server;if(app.post(settings.path,push),app.get(settings.path,push),app.options(settings.path,push),settings.status&&(app.get("/health",(req,res)=>{res.json({status:"ok",timestamp:Date.now(),source:"express"})}),app.get("/ready",(req,res)=>{res.json({status:"ready",timestamp:Date.now(),source:"express"})})),void 0!==settings.port){server=app.listen(settings.port,()=>{const statusRoutes=settings.status?"\n GET /health - Health check\n GET /ready - Readiness check":"";env.logger.info(`Express source listening on port ${settings.port}\n POST ${settings.path} - Event collection (JSON body)\n GET ${settings.path} - Pixel tracking (query params)\n OPTIONS ${settings.path} - CORS preflight`+statusRoutes)});const shutdownHandler=()=>{server&&server.close()};process.on("SIGTERM",shutdownHandler),process.on("SIGINT",shutdownHandler)}return{type:"express",config:{...config,settings:settings},push:push,app:app,server:server}};export{TRANSPARENT_GIF,setCorsHeaders,sourceExpress};//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/schemas/primitives.ts","../src/schemas/settings.ts","../src/utils.ts"],"sourcesContent":["import express, { type Request, type Response } from 'express';\nimport cors from 'cors';\nimport { requestToData } from '@walkeros/core';\nimport type {\n ExpressSource,\n PartialConfig,\n Types,\n EventRequest,\n} from './types';\nimport { SettingsSchema } from './schemas';\nimport { setCorsHeaders, TRANSPARENT_GIF } from './utils';\n\n/**\n * Express source initialization\n *\n * This source OWNS its HTTP server infrastructure:\n * - Creates Express application\n * - Sets up middleware (JSON parsing, CORS)\n * - Registers event collection endpoints (POST, GET, OPTIONS)\n * - Starts HTTP server (if port configured)\n * - Handles graceful shutdown\n *\n * @param config Partial source configuration\n * @param env Source environment with push, command, elb functions\n * @returns Express source instance with app and push handler\n */\nexport const sourceExpress = async (\n config: PartialConfig,\n env: Types['env'],\n): Promise<ExpressSource> => {\n // Validate and apply default settings\n const settings = SettingsSchema.parse(config.settings || {});\n\n const app = express();\n\n // Middleware setup - JSON body parsing with 10mb default limit\n app.use(express.json({ limit: '1mb' }));\n\n // CORS middleware (enabled by default)\n if (settings.cors !== false) {\n const corsOptions = settings.cors === true ? {} : settings.cors;\n app.use(cors(corsOptions));\n }\n\n /**\n * Request handler - transforms HTTP requests into walker events\n * Supports POST (JSON body), GET (query params), and OPTIONS (CORS preflight)\n */\n const push = async (req: Request, res: Response): Promise<void> => {\n try {\n // Handle OPTIONS for CORS preflight\n if (req.method === 'OPTIONS') {\n setCorsHeaders(res, settings.cors);\n res.status(204).send();\n return;\n }\n\n // Handle GET requests (pixel tracking)\n if (req.method === 'GET') {\n // Parse query parameters to event data using requestToData\n const parsedData = requestToData(req.url);\n\n // Send to collector\n if (parsedData && typeof parsedData === 'object') {\n await env.push(parsedData);\n }\n\n // Return 1x1 transparent GIF for pixel tracking\n res.set('Content-Type', 'image/gif');\n res.set('Cache-Control', 'no-cache, no-store, must-revalidate');\n res.send(TRANSPARENT_GIF);\n return;\n }\n\n // Handle POST requests (standard event ingestion)\n if (req.method === 'POST') {\n const eventData = req.body;\n\n if (!eventData || typeof eventData !== 'object') {\n res.status(400).json({\n success: false,\n error: 'Invalid event: body must be an object',\n });\n return;\n }\n\n // Send event to collector\n await env.push(eventData);\n\n res.json({\n success: true,\n timestamp: Date.now(),\n });\n return;\n }\n\n // Unsupported method\n res.status(405).json({\n success: false,\n error: 'Method not allowed. Use POST, GET, or OPTIONS.',\n });\n } catch (error) {\n res.status(500).json({\n success: false,\n error: error instanceof Error ? error.message : 'Internal server error',\n });\n }\n };\n\n // Register event collection endpoint (handles POST, GET, OPTIONS)\n app.post(settings.path, push);\n app.get(settings.path, push);\n app.options(settings.path, push);\n\n // Health check endpoints (if enabled)\n if (settings.status) {\n app.get('/health', (req, res) => {\n res.json({\n status: 'ok',\n timestamp: Date.now(),\n source: 'express',\n });\n });\n\n app.get('/ready', (req, res) => {\n res.json({\n status: 'ready',\n timestamp: Date.now(),\n source: 'express',\n });\n });\n }\n\n // Source owns the HTTP server (if port configured)\n let server: ReturnType<typeof app.listen> | undefined;\n\n if (settings.port !== undefined) {\n server = app.listen(settings.port, () => {\n const statusRoutes = settings.status\n ? `\\n GET /health - Health check\\n GET /ready - Readiness check`\n : '';\n env.logger.info(\n `Express source listening on port ${settings.port}\\n` +\n ` POST ${settings.path} - Event collection (JSON body)\\n` +\n ` GET ${settings.path} - Pixel tracking (query params)\\n` +\n ` OPTIONS ${settings.path} - CORS preflight` +\n statusRoutes,\n );\n });\n\n // Graceful shutdown handlers\n const shutdownHandler = () => {\n if (server) {\n server.close();\n }\n };\n\n process.on('SIGTERM', shutdownHandler);\n process.on('SIGINT', shutdownHandler);\n }\n\n const instance: ExpressSource = {\n type: 'express',\n config: {\n ...config,\n settings,\n },\n push,\n app, // Expose app for advanced usage\n server, // Expose server (if started)\n };\n\n return instance;\n};\n\n// Export types (avoid re-exporting duplicates from schemas)\nexport type {\n ExpressSource,\n Config,\n PartialConfig,\n Types,\n EventRequest,\n EventResponse,\n RequestBody,\n ResponseBody,\n Push,\n Env,\n Mapping,\n InitSettings,\n Settings,\n} from './types';\n\n// Export utils\nexport { setCorsHeaders, TRANSPARENT_GIF } from './utils';\n","import { z } from '@walkeros/core/dev';\n\n/**\n * HTTP methods enum\n */\nexport const HttpMethod = z.enum([\n 'GET',\n 'POST',\n 'PUT',\n 'PATCH',\n 'DELETE',\n 'OPTIONS',\n 'HEAD',\n]);\n\n/**\n * CORS origin configuration\n * Accepts:\n * - '*' for all origins\n * - Single URL string\n * - Array of URL strings\n */\nexport const CorsOrigin = z.union([\n z.string(),\n z.array(z.string()),\n z.literal('*'),\n]);\n\n/**\n * CORS options schema\n * Configuration for Cross-Origin Resource Sharing\n */\nexport const CorsOptionsSchema = z.object({\n origin: CorsOrigin.describe(\n 'Allowed origins (* for all, URL string, or array of URLs)',\n ).optional(),\n\n methods: z.array(HttpMethod).describe('Allowed HTTP methods').optional(),\n\n headers: z.array(z.string()).describe('Allowed request headers').optional(),\n\n credentials: z\n .boolean()\n .describe('Allow credentials (cookies, authorization headers)')\n .optional(),\n\n maxAge: z\n .number()\n .int()\n .positive()\n .describe('Preflight cache duration in seconds')\n .optional(),\n});\n\nexport type CorsOptions = z.infer<typeof CorsOptionsSchema>;\n","import { z } from '@walkeros/core/dev';\nimport { CorsOptionsSchema } from './primitives';\n\n/**\n * Express source settings schema\n */\nexport const SettingsSchema = z.object({\n port: z\n .number()\n .int()\n .min(0) // 0 = random available port\n .max(65535)\n .describe(\n 'HTTP server port to listen on. Use 0 for random available port. If not provided, server will not start (app only mode)',\n )\n .optional(),\n\n path: z\n .string()\n .describe('Event collection endpoint path')\n .default('/collect'),\n\n cors: z\n .union([z.boolean(), CorsOptionsSchema])\n .describe(\n 'CORS configuration: false = disabled, true = allow all origins (default), object = custom configuration',\n )\n .default(true),\n\n status: z\n .boolean()\n .describe('Enable health check endpoints (/health, /ready)')\n .default(true),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import type { Response } from 'express';\nimport type { CorsOptions } from './schemas';\n\n/**\n * Set CORS headers on response\n *\n * @param res Express response object\n * @param corsConfig CORS configuration (false = disabled, true = allow all, object = custom)\n */\nexport function setCorsHeaders(\n res: Response,\n corsConfig: boolean | CorsOptions = true,\n): void {\n if (corsConfig === false) return;\n\n if (corsConfig === true) {\n // Simple CORS - allow all\n res.set('Access-Control-Allow-Origin', '*');\n res.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.set('Access-Control-Allow-Headers', 'Content-Type');\n } else {\n // Custom CORS configuration\n if (corsConfig.origin) {\n const origin = Array.isArray(corsConfig.origin)\n ? corsConfig.origin.join(', ')\n : corsConfig.origin;\n res.set('Access-Control-Allow-Origin', origin);\n }\n\n if (corsConfig.methods) {\n res.set('Access-Control-Allow-Methods', corsConfig.methods.join(', '));\n }\n\n if (corsConfig.headers) {\n res.set('Access-Control-Allow-Headers', corsConfig.headers.join(', '));\n }\n\n if (corsConfig.credentials) {\n res.set('Access-Control-Allow-Credentials', 'true');\n }\n\n if (corsConfig.maxAge) {\n res.set('Access-Control-Max-Age', String(corsConfig.maxAge));\n }\n }\n}\n\n/**\n * 1x1 transparent GIF for pixel tracking\n * Base64-encoded GIF (43 bytes)\n */\nexport const TRANSPARENT_GIF = Buffer.from(\n 'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',\n 'base64',\n);\n"],"mappings":"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","names":["z","z"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@walkeros/server-source-express",
|
|
3
|
+
"description": "Express server source for walkerOS",
|
|
4
|
+
"version": "0.0.0-next-20251219153324",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/**"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup --silent",
|
|
14
|
+
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
|
|
15
|
+
"dev": "jest --watchAll --colors",
|
|
16
|
+
"lint": "tsc && eslint \"**/*.ts*\"",
|
|
17
|
+
"test": "jest",
|
|
18
|
+
"update": "npx npm-check-updates -u && npm update"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@walkeros/core": "0.0.0-next-20251219153324",
|
|
22
|
+
"express": "^5.2.1",
|
|
23
|
+
"cors": "^2.8.5"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/express": "^5.0.6",
|
|
27
|
+
"@types/cors": "^2.8.19"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"url": "git+https://github.com/elbwalker/walkerOS.git",
|
|
31
|
+
"directory": "packages/server/sources/express"
|
|
32
|
+
},
|
|
33
|
+
"author": "elbwalker <hello@elbwalker.com>",
|
|
34
|
+
"homepage": "https://github.com/elbwalker/walkerOS#readme",
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/elbwalker/walkerOS/issues"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"walker",
|
|
40
|
+
"walkerOS",
|
|
41
|
+
"source",
|
|
42
|
+
"server",
|
|
43
|
+
"express",
|
|
44
|
+
"http"
|
|
45
|
+
],
|
|
46
|
+
"funding": [
|
|
47
|
+
{
|
|
48
|
+
"type": "GitHub Sponsors",
|
|
49
|
+
"url": "https://github.com/sponsors/elbwalker"
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
"exports": {
|
|
53
|
+
".": {
|
|
54
|
+
"types": "./dist/index.d.ts",
|
|
55
|
+
"import": "./dist/index.mjs",
|
|
56
|
+
"require": "./dist/index.js"
|
|
57
|
+
},
|
|
58
|
+
"./dev": {
|
|
59
|
+
"types": "./dist/dev.d.ts",
|
|
60
|
+
"import": "./dist/dev.mjs",
|
|
61
|
+
"require": "./dist/dev.js"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|