@walkeros/server-source-gcp 4.1.0-next-1778668930820 → 4.1.0

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 CHANGED
@@ -1,8 +1,19 @@
1
+ <p align="left">
2
+ <a href="https://www.walkeros.io">
3
+ <img alt="walkerOS" title="walkerOS" src="https://www.walkeros.io/img/walkerOS_logo.svg" width="256px"/>
4
+ </a>
5
+ </p>
6
+
1
7
  # @walkeros/server-source-gcp
2
8
 
3
- Google Cloud Platform server sources for walkerOS, lightweight runtime adapters
4
- for GCP services. Ships two surfaces: the Cloud Functions HTTP handler and
5
- Pub/Sub (pull subscriber and push webhook).
9
+ Google Cloud Functions source for walkerOS. A lightweight runtime adapter with
10
+ plug-and-play assignment to a Cloud Functions handler, batch processing, and
11
+ configurable CORS. The package also ships Pub/Sub sources for ingesting from
12
+ Pub/Sub topics.
13
+
14
+ [Documentation](https://www.walkeros.io/docs/sources/server/gcp) &bull;
15
+ [NPM Package](https://www.npmjs.com/package/@walkeros/server-source-gcp) &bull;
16
+ [Source Code](https://github.com/elbwalker/walkerOS/tree/main/packages/server/sources/gcp)
6
17
 
7
18
  ## Installation
8
19
 
@@ -10,469 +21,34 @@ Pub/Sub (pull subscriber and push webhook).
10
21
  npm install @walkeros/server-source-gcp @google-cloud/functions-framework
11
22
  ```
12
23
 
13
- ## Usage
14
-
15
- ```typescript
16
- import {
17
- sourceCloudFunction,
18
- type SourceCloudFunction,
19
- } from '@walkeros/server-source-gcp';
20
- import { startFlow } from '@walkeros/collector';
21
- import { http } from '@google-cloud/functions-framework';
22
-
23
- const { elb } = await startFlow<SourceCloudFunction.Push>({
24
- sources: { api: { code: sourceCloudFunction } },
25
- });
26
-
27
- http('walkerHandler', elb);
28
- ```
29
-
30
- ---
31
-
32
- ## Cloud Functions Source
33
-
34
- The Cloud Functions source provides an HTTP handler that receives walker events
35
- and forwards them to the walkerOS collector.
36
-
37
- ### Basic Usage
38
-
39
- ```typescript
40
- import {
41
- sourceCloudFunction,
42
- type SourceCloudFunction,
43
- } from '@walkeros/server-source-gcp';
44
- import { startFlow } from '@walkeros/collector';
45
- import { http } from '@google-cloud/functions-framework';
46
-
47
- // Handler singleton - reused across warm invocations
48
- let handler: SourceCloudFunction.Push;
49
-
50
- async function setup() {
51
- if (handler) return handler;
52
-
53
- const { elb } = await startFlow<SourceCloudFunction.Push>({
54
- sources: {
55
- api: {
56
- code: sourceCloudFunction,
57
- config: {
58
- settings: { cors: true },
59
- },
60
- },
61
- },
62
- destinations: {
63
- // Your destinations
64
- },
65
- });
66
-
67
- handler = elb;
68
- return handler;
69
- }
70
-
71
- // Register with Cloud Functions framework
72
- setup().then((h) => http('walkerHandler', h));
73
- ```
74
-
75
- ## Bundler Integration
76
-
77
- Use with minimal config:
78
-
79
- ```json
80
- {
81
- "sources": {
82
- "api": { "type": "cloudfunction", "cors": true }
83
- }
84
- }
85
- ```
86
-
87
- Bundler auto-generates deployable exports.
88
-
89
- ### Configuration Options
90
-
91
- ```typescript
92
- interface Settings {
93
- cors?: boolean | CorsOptions; // Enable CORS (default: true)
94
- batch?: boolean; // Enable batch processing (default: true)
95
- maxBatchSize?: number; // Max events per batch (default: 100)
96
- timeout?: number; // Request timeout (default: 30000ms)
97
- }
98
-
99
- interface CorsOptions {
100
- origin?: string | string[]; // Allowed origins
101
- methods?: string[]; // Allowed methods
102
- headers?: string[]; // Allowed headers
103
- credentials?: boolean; // Allow credentials
104
- maxAge?: number; // Preflight cache time
105
- }
106
- ```
107
-
108
- ### Ingest Metadata
109
-
110
- Extract request metadata and forward it to processors and destinations:
111
-
112
- ```typescript
113
- await startFlow({
114
- sources: {
115
- api: {
116
- code: sourceCloudFunction,
117
- config: {
118
- settings: { cors: true },
119
- ingest: {
120
- ip: 'ip',
121
- ua: 'headers.user-agent',
122
- origin: 'headers.origin',
123
- },
124
- },
125
- },
126
- },
127
- });
128
- ```
129
-
130
- **Available ingest paths:**
131
-
132
- | Path | Description |
133
- | ----------- | --------------------------------- |
134
- | `ip` | Client IP address |
135
- | `headers.*` | HTTP headers (user-agent, origin) |
136
- | `method` | HTTP method |
137
- | `hostname` | Request hostname |
138
-
139
- ### Request Format
140
-
141
- **Single Event:**
142
-
143
- ```json
144
- {
145
- "event": "page view",
146
- "data": {
147
- "title": "Home Page",
148
- "path": "/"
149
- },
150
- "context": {
151
- "stage": ["prod", 1]
152
- }
153
- }
154
- ```
155
-
156
- **Batch Events:**
157
-
158
- ```json
159
- {
160
- "events": [
161
- {
162
- "event": "page view",
163
- "data": { "title": "Page 1" }
164
- },
165
- {
166
- "event": "button click",
167
- "data": { "id": "btn1" }
168
- }
169
- ]
170
- }
171
- ```
172
-
173
- ### Response Format
174
-
175
- **Success:**
176
-
177
- ```json
178
- {
179
- "success": true,
180
- "id": "event-id-123"
181
- }
182
- ```
183
-
184
- **Batch Success:**
185
-
186
- ```json
187
- {
188
- "success": true,
189
- "processed": 2,
190
- "errors": []
191
- }
192
- ```
193
-
194
- **Error:**
24
+ ## Quick start
195
25
 
196
26
  ```json
197
27
  {
198
- "success": false,
199
- "error": "Invalid event format"
200
- }
201
- ```
202
-
203
- ### Deployment
204
-
205
- The source is designed to work with the walkerOS deployment system:
206
-
207
- ```json
208
- {
209
- "providers": [
210
- {
211
- "name": "api-endpoint",
212
- "type": "gcp-functions",
213
- "artifact": {
214
- "source": "bundler",
215
- "bundle": "api-collector"
216
- },
217
- "settings": {
218
- "functionName": "walker-collector",
219
- "runtime": "nodejs18",
220
- "memory": 256
28
+ "version": 4,
29
+ "flows": {
30
+ "default": {
31
+ "config": { "platform": "server" },
32
+ "sources": {
33
+ "gcp": { "package": "@walkeros/server-source-gcp", "config": {} }
221
34
  }
222
35
  }
223
- ]
224
- }
225
- ```
226
-
227
- ### Testing
228
-
229
- The source uses environment injection for testability:
230
-
231
- ```typescript
232
- import { sourceCloudFunction } from '@walkeros/server-source-gcp';
233
-
234
- const mockElb = jest.fn().mockResolvedValue({
235
- ok: true,
236
- event: { id: 'test-id' },
237
- });
238
-
239
- const source = await sourceCloudFunction(
240
- { settings: { cors: false } },
241
- { elb: mockElb },
242
- );
243
-
244
- // Test the handler
245
- const mockReq = { method: 'POST', body: { event: 'test' } };
246
- const mockRes = { status: jest.fn().mockReturnThis(), json: jest.fn() };
247
-
248
- await source.push(mockReq, mockRes);
249
-
250
- expect(mockElb).toHaveBeenCalledWith('test', {});
251
- expect(mockRes.status).toHaveBeenCalledWith(200);
252
- ```
253
-
254
- ## Architecture
255
-
256
- This source follows the walkerOS patterns:
257
-
258
- - **Stateless**: No collector references, communicates via elb function
259
- - **Environment Injection**: All dependencies provided through environment
260
- - **Lean Implementation**: Minimal required fields, focused on HTTP handling
261
- - **Standard Interface**: The `push` function IS the Cloud Function handler
262
- - **Plug-and-Play**: Direct assignment: `http('handler', source.push)`
263
-
264
- The source's `push` function accepts HTTP requests, transforms them into walker
265
- events, and forwards them to the collector for processing by destinations.
266
-
267
- ---
268
-
269
- ## Pub/Sub source
270
-
271
- Subscribes to a Google Cloud Pub/Sub topic and forwards each message to the
272
- walkerOS collector. Ships in two delivery models:
273
-
274
- - `sourcePubSubPull`: long-running streaming pull subscriber. Use in containers,
275
- VMs, or any process that stays alive. Highest throughput.
276
- - `sourcePubSubPush`: HTTP push webhook handler. Use in serverless deployments
277
- (Cloud Run, Cloud Functions, Lambda) where there is no long-running process to
278
- keep a streaming subscriber alive.
279
-
280
- ### Installation
281
-
282
- Pub/Sub support is part of `@walkeros/server-source-gcp`. The Pub/Sub SDK is
283
- declared as a runtime dependency:
284
-
285
- ```bash
286
- npm install @walkeros/server-source-gcp
287
- ```
288
-
289
- ### Quickstart, pull subscriber
290
-
291
- ```typescript
292
- import { sourcePubSubPull } from '@walkeros/server-source-gcp';
293
- import { startFlow } from '@walkeros/collector';
294
-
295
- await startFlow({
296
- sources: {
297
- pubsub: {
298
- code: sourcePubSubPull,
299
- config: {
300
- settings: {
301
- projectId: 'my-gcp-project',
302
- subscription: 'events-sub',
303
- // Optional: tighten flow control beyond SDK defaults
304
- flowControl: { maxMessages: 100, maxBytes: 10 * 1024 * 1024 },
305
- },
306
- },
307
- },
308
- },
309
- destinations: {
310
- // Your destinations
311
- },
312
- });
313
- ```
314
-
315
- The pull source is event-driven: `init()` opens the streaming subscription and
316
- forwards each delivered message to the collector via `env.push`. The source's
317
- `push` is a deliberate no-op stub (the framework never calls it). `destroy()`
318
- closes the subscriber gracefully, honoring `shutdownTimeoutMs` (default 30000).
319
-
320
- ### Quickstart, push webhook
321
-
322
- ```typescript
323
- import express from 'express';
324
- import { sourcePubSubPush } from '@walkeros/server-source-gcp';
325
- import { startFlow } from '@walkeros/collector';
326
-
327
- const { sources } = await startFlow({
328
- sources: {
329
- pubsub: {
330
- code: sourcePubSubPush,
331
- config: {
332
- settings: { decoder: 'json' },
333
- },
334
- },
335
- },
336
- destinations: {
337
- // Your destinations
338
- },
339
- });
340
-
341
- const app = express();
342
- app.use(express.json());
343
- app.post('/pubsub-push', sources.pubsub.push);
344
- app.listen(8080);
345
- ```
346
-
347
- Pub/Sub POSTs each message envelope to `/pubsub-push`. The source decodes the
348
- envelope, base64-decodes the data field, runs the configured decoder, and
349
- forwards to the collector. Returns 200 on success, 400 on malformed envelope,
350
- 401 when OIDC verification fails, 500 on push failure (Pub/Sub will retry per
351
- the subscription's retry policy).
352
-
353
- ### Authentication
354
-
355
- Three modes, in precedence order:
356
-
357
- 1. **Pre-configured client** (`settings.client`). When you bring your own
358
- `PubSub` instance with custom auth, this bypasses credentials resolution.
359
- 2. **Service account JSON** (`settings.credentials`). Pass an object
360
- `{ client_email, private_key }` or a JSON string the source will parse.
361
- 3. **Application Default Credentials (ADC)**. The default. Works on GCP compute
362
- (Cloud Run, GCE, GKE, Cloud Functions) and locally via
363
- `gcloud auth application-default login`.
364
-
365
- ```typescript
366
- // Service account (object form)
367
- config: {
368
- settings: {
369
- projectId: 'my-project',
370
- subscription: 'events-sub',
371
- credentials: {
372
- client_email: 'sa@my-project.iam.gserviceaccount.com',
373
- private_key: '-----BEGIN PRIVATE KEY-----\n...',
374
- },
375
- },
376
- }
377
- ```
378
-
379
- ### Setup, idempotent subscription provisioning
380
-
381
- `walkeros setup source.<id>` provisions the subscription. Idempotent: safe to
382
- re-run; `ALREADY_EXISTS` is non-fatal. Drift detection emits `setup.drift`
383
- warnings without auto-mutating.
384
-
385
- ```typescript
386
- config: {
387
- setup: {
388
- createTopic: true, // Create the topic if missing (default: false)
389
- ackDeadlineSeconds: 60,
390
- deadLetterPolicy: {
391
- deadLetterTopic: 'events-dlq',
392
- maxDeliveryAttempts: 5,
393
- createDeadLetterTopic: true,
394
- },
395
- retryPolicy: {
396
- minimumBackoff: { seconds: 10 },
397
- maximumBackoff: { seconds: 600 },
398
- },
399
- },
400
- settings: {
401
- projectId: 'my-project',
402
- subscription: 'events-sub',
403
- topic: 'events',
404
- },
405
- }
406
- ```
407
-
408
- Auto-created topics use the EU multi-region storage policy (`eu-west1`,
409
- `eu-west3`, `eu-west4`). Operators in non-EU regions should override
410
- `messageStoragePolicy` per organisation policy.
411
-
412
- The destination side (`@walkeros/server-destination-gcp`) provisions the topic
413
- itself; you can use either side's setup for the topic. Subscription provisioning
414
- is owned exclusively by this source.
415
-
416
- ### Decoders
417
-
418
- | Decoder | Behavior |
419
- | ---------------- | -------------------------------------------------------------------------- |
420
- | `json` (default) | `JSON.parse(data.toString('utf8'))`. Throws DecoderError on parse failure. |
421
- | `text` | `data.toString('utf8')`. The text becomes the event payload. |
422
- | `raw` | The raw `Buffer` is forwarded as-is. |
423
-
424
- ### Backpressure and flow control
425
-
426
- The pull subscriber has built-in flow control:
427
-
428
- - `maxMessages` (default 100): max concurrent in-flight messages.
429
- - `maxBytes` (default 10 MB): max concurrent in-flight bytes.
430
-
431
- The subscriber automatically slows down when the collector pushes back. Tune via
432
- `settings.flowControl`.
433
-
434
- ### OIDC verification (push mode)
435
-
436
- Off by default. Enable per push subscription when your endpoint is publicly
437
- reachable:
438
-
439
- ```typescript
440
- config: {
441
- settings: {
442
- verifyOidc: true,
443
- audience: 'https://my-service.example/pubsub-push',
444
- },
36
+ }
445
37
  }
446
38
  ```
447
39
 
448
- The source verifies the `Authorization: Bearer <jwt>` header against GCP public
449
- keys via `google-auth-library`. Misconfigured OIDC silently rejects all
450
- messages, so leave it off when running behind a private network and rely on
451
- network isolation instead.
40
+ ## Documentation
452
41
 
453
- ### Emulator
42
+ Full configuration, mapping, and examples live in the docs:
43
+ **https://www.walkeros.io/docs/sources/server/gcp**
454
44
 
455
- Both pull and push honor `PUBSUB_EMULATOR_HOST`. For explicit configuration, set
456
- `settings.apiEndpoint`:
45
+ ## Contribute
457
46
 
458
- ```typescript
459
- config: {
460
- settings: {
461
- projectId: 'my-project',
462
- subscription: 'events-sub',
463
- apiEndpoint: 'localhost:8085',
464
- },
465
- }
466
- ```
47
+ Feel free to contribute by submitting an
48
+ [issue](https://github.com/elbwalker/walkerOS/issues), starting a
49
+ [discussion](https://github.com/elbwalker/walkerOS/discussions), or getting in
50
+ [contact](https://calendly.com/elb-alexander/30min).
467
51
 
468
- ### Troubleshooting
52
+ ## License
469
53
 
470
- - **Subscription not found / unauthorized.** The pull source logs a canonical
471
- hint:
472
- `Pub/Sub subscription "X" not found or unauthorized in project "Y". Run "walkeros setup source.<id>" to create it.`
473
- Run setup or grant `roles/pubsub.subscriber` to the runtime service account.
474
- - **JSON decode failures.** Default `onPushError: 'nack'` redelivers; set
475
- `onPushError: 'ack'` to drop instead. Switch to `decoder: 'raw'` if your
476
- payloads are binary (Avro, Protobuf, etc.) and decode in a transformer.
477
- - **Push 401 with OIDC enabled.** Verify the audience matches your endpoint URL
478
- exactly. Pub/Sub signs tokens with the configured audience.
54
+ MIT