gcf-common-lib 0.41.0 → 0.45.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.
@@ -1,39 +1,37 @@
1
- # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2
- # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
3
-
4
- name: Node.js Package
5
- on: [ push ]
6
-
7
- # on:
8
- # release:
9
- # types: [created]
10
-
11
- jobs:
12
- build:
13
- runs-on: ubuntu-latest
14
- steps:
15
- - uses: actions/checkout@v4
16
- - uses: actions/setup-node@v4
17
- with:
18
- node-version: 20
19
- - run: npm ci
20
- # - run: npm run build
21
- - run: npm test
22
-
23
- publish-npm:
24
- permissions:
25
- contents: read # This is required for actions/checkout
26
- id-token: write # This is required for publishing to npm
27
- needs: build
28
- runs-on: ubuntu-latest
29
- steps:
30
- - uses: actions/checkout@v4
31
- - uses: actions/setup-node@v4
32
- with:
33
- node-version: 20
34
- registry-url: https://registry.npmjs.org/
35
- - run: npm ci
36
- # - run: npm run build
37
- - run: npm publish --provenance
38
- # env:
39
- # NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
1
+ # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2
+ # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
3
+
4
+ name: Node.js Package
5
+ on: [ push ]
6
+
7
+ # on:
8
+ # release:
9
+ # types: [created]
10
+
11
+ jobs:
12
+ build:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v5
16
+ - uses: actions/setup-node@v6
17
+ with:
18
+ node-version: 24
19
+ - run: npm ci
20
+ # - run: npm run build
21
+ - run: npm test
22
+
23
+ publish-npm:
24
+ needs: build
25
+ runs-on: ubuntu-latest
26
+ permissions:
27
+ contents: read # This is required for actions/checkout
28
+ id-token: write # This is required for publishing to npm
29
+ steps:
30
+ - uses: actions/checkout@v5
31
+ - uses: actions/setup-node@v6
32
+ with:
33
+ node-version: '24'
34
+ registry-url: 'https://registry.npmjs.org'
35
+ - run: npm ci
36
+ # - run: npm run build
37
+ - run: npm publish
@@ -1,108 +1,108 @@
1
- # gcf-common-lib: Development Guidelines
2
-
3
- This document captures project-specific details to speed up work on this repository.
4
- It assumes an advanced Node/TypeScript developer familiar with Node 20+, npm, ESLint flat config, and the Node test runner.
5
-
6
- ## 1) Build and Configuration
7
-
8
- - Runtime/Tooling versions
9
- - Node: >= 20 (see package.json "engines"). Tested with Node 20.x.
10
- - TypeScript: ^5.3 (dev dependency). The project extends `@tsconfig/node20`.
11
- - TypeScript config
12
- - tsconfig.json extends `@tsconfig/node20`. It currently sets only `"compilerOptions.newLine": "crlf"`.
13
- - `@tsconfig/node20` typically configures strict mode and `noEmit: true`. As a result, `npm run build` performs type-checking only and does not emit JS artifacts by default.
14
- - If you need JS output, add an `outDir` and override `noEmit: false` in tsconfig.json (or create a separate tsconfig.build.json) and adjust the build script accordingly.
15
- - Build
16
- - Type-check: `npm run build` (runs `tsc`). No compiled JS will be emitted with the current config.
17
- - Entry point / module resolution
18
- - package.json sets `"main": "src/index"`. The published package is intended to be consumed by TS-aware builds, bundlers, or via transpilation. Node alone cannot `require('src')` TypeScript without a loader/transpiler.
19
- - Linting/Formatting
20
- - ESLint flat config with:
21
- - `@eslint/js` (base JS rules), `typescript-eslint` (TS rules), `eslint-plugin-unicorn`, and `eslint-plugin-promise`.
22
- - Notable overrides: several `@typescript-eslint/*` rules are off (no-explicit-any, no-unused-vars, no-empty-function, no-empty-interface). `unicorn/prevent-abbreviations` is off. Additional rules enforce `block-scoped-var`, `no-loop-func`.
23
- - Prettier is present as a dev dependency; there is no explicit npm script, run via `npx prettier --write .` if needed.
24
- - Line endings: CRLF (Windows) per tsconfig; ensure your editor respects this to avoid noisy diffs.
25
-
26
- ## 2) Testing
27
-
28
- - Framework
29
- - Uses Node’s built-in test runner (`node:test`) with `node:assert/strict`.
30
- - Default npm script: `npm test` runs `node --test ./test/`.
31
-
32
- - Important notes for this repo
33
- - Some code paths interact with external services (Google Pub/Sub via `@google-cloud/pubsub`, AMQP via `amqplib`). Avoid hitting those in unit tests unless you have proper credentials and brokers.
34
- - The repository contains `test/test.ts` (TypeScript) and a historical compiled `test/test.js`. The JS test tries to `require('../src')`, but the project does not emit JS builds by default, so Node cannot load TS sources as JS. Running the whole suite may therefore fail locally unless you:
35
- - Compile TS to JS and point Node to the emitted entry, or
36
- - Use a loader (e.g., ts-node / swc / tsx) to run TS directly, or
37
- - Write tests in JS that import only pure functions that do not require transpilation.
38
-
39
- - Recommended local workflows
40
- 1) Run a single, deterministic JS test file (no external deps):
41
- - Place a JS test under `test/` with a `.test.js` name, then run:
42
- - `node --test .\test\your.test.js`
43
- - Example (verified locally):
44
- ```js
45
- // test/sample.test.js
46
- const { describe, it } = require('node:test');
47
- const assert = require('node:assert/strict');
48
- describe('sample', () => {
49
- it('adds numbers', () => {
50
- assert.equal(1 + 1, 2);
51
- });
52
- it('async works', async () => {
53
- const v = await Promise.resolve('ok');
54
- assert.equal(v, 'ok');
55
- });
56
- });
57
- ```
58
- - Run: `node --test .\test\sample.test.js`
59
-
60
- 2) Testing library functions without network calls:
61
- - Prefer testing pure utilities from `src/utils.ts` (e.g., `ms`, `sec`, `A1` conversions, `safeJsonParse`). Create JS shims if needed or compile.
62
- - For `GcfCommon.process(...)`, avoid network I/O by passing payloads that do not contain `topic`, `exchange`, or `queue` in metadata/attributes. The `response(...)` method publishes only if those are present. Example payload for a no-network test:
63
- ```ts
64
- const payload = { event: { '@type': 'type.googleapis.com/google.pubsub.v1.PubsubMessage', json: { ok: 1 }, attributes: {} }, context: undefined };
65
- // No topic/exchange/queue -> response() becomes a no-op for network I/O
66
- ```
67
- - Alternatively, stub `GcfCommon.response` in tests:
68
- ```js
69
- const { GcfCommon } = require('../src');
70
- const original = GcfCommon.response;
71
- GcfCommon.response = async () => {}; // no-op
72
- // ... run test logic ...
73
- GcfCommon.response = original;
74
- ```
75
-
76
- 3) Running TS tests
77
- - Option A (compile): configure tsconfig to emit JS (e.g., to `dist/`), compile tests and source, and run Node against the emitted JS.
78
- - Option B (loader): use a TS loader like `tsx` or `ts-node` to run TS under Node’s test runner. Example with tsx:
79
- - Install dev dep: `npm i -D tsx`
80
- - Run: `node --test --import tsx .\test\test.ts`
81
- - Caveat: loader flags and support differ across Node versions; prefer pure JS tests if you want zero-config.
82
-
83
- - Integration tests (Pub/Sub, AMQP)
84
- - Pub/Sub requires Google Application Default Credentials (e.g., set `GOOGLE_APPLICATION_CREDENTIALS=path\to\service-account.json`).
85
- - AMQP requires a reachable broker URL; set `GcfCommon.amqpOptions.url` appropriately.
86
- - These are not required for unit testing and should be skipped or stubbed in CI unless explicitly needed.
87
-
88
- ## 3) Additional development information
89
-
90
- - Project layout
91
- - `src/`: TypeScript sources (helpers for Pub/Sub, AMQP, Mongo, utilities, types, and `GcfCommon`).
92
- - `test/`: Node test runner tests. Prefer `.test.js` files for frictionless execution without transpilation.
93
- - External services
94
- - `GcfCommon.response(...)` conditionally publishes to Pub/Sub or AMQP if `topic`, `exchange`, or `queue` are present in payload metadata/attributes. Omit these in unit tests to avoid network calls.
95
- - `GcfCommon.getOptions(...)` parses JSON options from metadata/attributes; it uses `safeJsonParse` with a fallback to avoid throwing.
96
- - Coding style
97
- - Adhere to the ESLint setup, especially around promises and unicorn rules; long-lived abbreviations are allowed (prevent-abbreviations is off).
98
- - Use CRLF line endings to match repo configuration.
99
- - CI/CD & publishing
100
- - `publishConfig.access: public`; branch is `master` in metadata. If publishing, ensure you build/type-check and validate tests that do not require external services, or segregate integration tests under a separate command.
101
-
102
- ## Quick Commands
103
-
104
- - Install deps: `npm ci`
105
- - Type-check build: `npm run build`
106
- - Run all JS tests in a file: `node --test .\test\some.test.js`
107
- - Lint (manual): `npx eslint .`
108
- - Format (manual): `npx prettier --write .`
1
+ # gcf-common-lib: Development Guidelines
2
+
3
+ This document captures project-specific details to speed up work on this repository.
4
+ It assumes an advanced Node/TypeScript developer familiar with Node 20+, npm, ESLint flat config, and the Node test runner.
5
+
6
+ ## 1) Build and Configuration
7
+
8
+ - Runtime/Tooling versions
9
+ - Node: >= 20 (see package.json "engines"). Tested with Node 20.x.
10
+ - TypeScript: ^5.3 (dev dependency). The project extends `@tsconfig/node20`.
11
+ - TypeScript config
12
+ - tsconfig.json extends `@tsconfig/node20`. It currently sets only `"compilerOptions.newLine": "crlf"`.
13
+ - `@tsconfig/node20` typically configures strict mode and `noEmit: true`. As a result, `npm run build` performs type-checking only and does not emit JS artifacts by default.
14
+ - If you need JS output, add an `outDir` and override `noEmit: false` in tsconfig.json (or create a separate tsconfig.build.json) and adjust the build script accordingly.
15
+ - Build
16
+ - Type-check: `npm run build` (runs `tsc`). No compiled JS will be emitted with the current config.
17
+ - Entry point / module resolution
18
+ - package.json sets `"main": "src/index"`. The published package is intended to be consumed by TS-aware builds, bundlers, or via transpilation. Node alone cannot `require('src')` TypeScript without a loader/transpiler.
19
+ - Linting/Formatting
20
+ - ESLint flat config with:
21
+ - `@eslint/js` (base JS rules), `typescript-eslint` (TS rules), `eslint-plugin-unicorn`, and `eslint-plugin-promise`.
22
+ - Notable overrides: several `@typescript-eslint/*` rules are off (no-explicit-any, no-unused-vars, no-empty-function, no-empty-interface). `unicorn/prevent-abbreviations` is off. Additional rules enforce `block-scoped-var`, `no-loop-func`.
23
+ - Prettier is present as a dev dependency; there is no explicit npm script, run via `npx prettier --write .` if needed.
24
+ - Line endings: CRLF (Windows) per tsconfig; ensure your editor respects this to avoid noisy diffs.
25
+
26
+ ## 2) Testing
27
+
28
+ - Framework
29
+ - Uses Node’s built-in test runner (`node:test`) with `node:assert/strict`.
30
+ - Default npm script: `npm test` runs `node --test ./test/`.
31
+
32
+ - Important notes for this repo
33
+ - Some code paths interact with external services (Google Pub/Sub via `@google-cloud/pubsub`, AMQP via `amqplib`). Avoid hitting those in unit tests unless you have proper credentials and brokers.
34
+ - The repository contains `test/test.ts` (TypeScript) and a historical compiled `test/test.js`. The JS test tries to `require('../src')`, but the project does not emit JS builds by default, so Node cannot load TS sources as JS. Running the whole suite may therefore fail locally unless you:
35
+ - Compile TS to JS and point Node to the emitted entry, or
36
+ - Use a loader (e.g., ts-node / swc / tsx) to run TS directly, or
37
+ - Write tests in JS that import only pure functions that do not require transpilation.
38
+
39
+ - Recommended local workflows
40
+ 1) Run a single, deterministic JS test file (no external deps):
41
+ - Place a JS test under `test/` with a `.test.js` name, then run:
42
+ - `node --test .\test\your.test.js`
43
+ - Example (verified locally):
44
+ ```js
45
+ // test/sample.test.js
46
+ const { describe, it } = require('node:test');
47
+ const assert = require('node:assert/strict');
48
+ describe('sample', () => {
49
+ it('adds numbers', () => {
50
+ assert.equal(1 + 1, 2);
51
+ });
52
+ it('async works', async () => {
53
+ const v = await Promise.resolve('ok');
54
+ assert.equal(v, 'ok');
55
+ });
56
+ });
57
+ ```
58
+ - Run: `node --test .\test\sample.test.js`
59
+
60
+ 2) Testing library functions without network calls:
61
+ - Prefer testing pure utilities from `src/utils.ts` (e.g., `ms`, `sec`, `A1` conversions, `safeJsonParse`). Create JS shims if needed or compile.
62
+ - For `GcfCommon.process(...)`, avoid network I/O by passing payloads that do not contain `topic`, `exchange`, or `queue` in metadata/attributes. The `response(...)` method publishes only if those are present. Example payload for a no-network test:
63
+ ```ts
64
+ const payload = { event: { '@type': 'type.googleapis.com/google.pubsub.v1.PubsubMessage', json: { ok: 1 }, attributes: {} }, context: undefined };
65
+ // No topic/exchange/queue -> response() becomes a no-op for network I/O
66
+ ```
67
+ - Alternatively, stub `GcfCommon.response` in tests:
68
+ ```js
69
+ const { GcfCommon } = require('../src');
70
+ const original = GcfCommon.response;
71
+ GcfCommon.response = async () => {}; // no-op
72
+ // ... run test logic ...
73
+ GcfCommon.response = original;
74
+ ```
75
+
76
+ 3) Running TS tests
77
+ - Option A (compile): configure tsconfig to emit JS (e.g., to `dist/`), compile tests and source, and run Node against the emitted JS.
78
+ - Option B (loader): use a TS loader like `tsx` or `ts-node` to run TS under Node’s test runner. Example with tsx:
79
+ - Install dev dep: `npm i -D tsx`
80
+ - Run: `node --test --import tsx .\test\test.ts`
81
+ - Caveat: loader flags and support differ across Node versions; prefer pure JS tests if you want zero-config.
82
+
83
+ - Integration tests (Pub/Sub, AMQP)
84
+ - Pub/Sub requires Google Application Default Credentials (e.g., set `GOOGLE_APPLICATION_CREDENTIALS=path\to\service-account.json`).
85
+ - AMQP requires a reachable broker URL; set `GcfCommon.amqpOptions.url` appropriately.
86
+ - These are not required for unit testing and should be skipped or stubbed in CI unless explicitly needed.
87
+
88
+ ## 3) Additional development information
89
+
90
+ - Project layout
91
+ - `src/`: TypeScript sources (helpers for Pub/Sub, AMQP, Mongo, utilities, types, and `GcfCommon`).
92
+ - `test/`: Node test runner tests. Prefer `.test.js` files for frictionless execution without transpilation.
93
+ - External services
94
+ - `GcfCommon.response(...)` conditionally publishes to Pub/Sub or AMQP if `topic`, `exchange`, or `queue` are present in payload metadata/attributes. Omit these in unit tests to avoid network calls.
95
+ - `GcfCommon.getOptions(...)` parses JSON options from metadata/attributes; it uses `safeJsonParse` with a fallback to avoid throwing.
96
+ - Coding style
97
+ - Adhere to the ESLint setup, especially around promises and unicorn rules; long-lived abbreviations are allowed (prevent-abbreviations is off).
98
+ - Use CRLF line endings to match repo configuration.
99
+ - CI/CD & publishing
100
+ - `publishConfig.access: public`; branch is `master` in metadata. If publishing, ensure you build/type-check and validate tests that do not require external services, or segregate integration tests under a separate command.
101
+
102
+ ## Quick Commands
103
+
104
+ - Install deps: `npm ci`
105
+ - Type-check build: `npm run build`
106
+ - Run all JS tests in a file: `node --test .\test\some.test.js`
107
+ - Lint (manual): `npx eslint .`
108
+ - Format (manual): `npx prettier --write .`
package/.prettierrc CHANGED
@@ -1,10 +1,10 @@
1
- {
2
- "printWidth": 120,
3
- "trailingComma": "all",
4
- "tabWidth": 2,
5
- "semi": true,
6
- "singleQuote": true,
7
- "arrowParens": "avoid",
8
- "bracketSameLine": true,
9
- "endOfLine": "crlf"
10
- }
1
+ {
2
+ "printWidth": 120,
3
+ "trailingComma": "all",
4
+ "tabWidth": 2,
5
+ "semi": true,
6
+ "singleQuote": true,
7
+ "arrowParens": "avoid",
8
+ "bracketSameLine": true,
9
+ "endOfLine": "crlf"
10
+ }
package/README.md CHANGED
@@ -1,110 +1,110 @@
1
- # gcf-common-lib
2
-
3
- Common helpers for Google Cloud Functions (GCF) and Node services:
4
- - Orchestrate handlers and conditional response publishing via Pub/Sub or AMQP (RabbitMQ).
5
- - Utilities: time helpers (ms/sec), Excel A1 conversions, safeJsonParse, simple delay/timeout.
6
- - Helpers for AMQP and MongoDB lifecycles.
7
-
8
- This library targets Node >= 20 and TypeScript. It is designed to be consumed by TS-aware builds or transpiled output.
9
-
10
- ## Installation
11
-
12
- ```sh
13
- npm i gcf-common-lib
14
- ```
15
-
16
- ## Quick start
17
-
18
- ### Process an event without network I/O
19
- Pass a payload without `topic`, `exchange`, or `queue` to avoid publishing in unit tests or offline flows.
20
-
21
- ```ts
22
- import { GcfCommon } from 'gcf-common-lib';
23
-
24
- export async function handler(payload: any) {
25
- return { ok: 1 };
26
- }
27
-
28
- // Example payload: Pub/Sub-like message without routing attributes
29
- const payload = {
30
- event: { '@type': 'type.googleapis.com/google.pubsub.v1.PubsubMessage', json: { hello: 'world' }, attributes: {} },
31
- context: undefined,
32
- };
33
-
34
- await GcfCommon.process(payload, handler);
35
- ```
36
-
37
- ### Publish a response to Pub/Sub
38
- Set the `topic` attribute in metadata/attributes. Response attributes are coerced to strings; common fields `request_id`, `consumer_id`, `app_id`, `env` are propagated if present.
39
-
40
- ```ts
41
- import { GcfCommon } from 'gcf-common-lib';
42
-
43
- const payload = {
44
- event: { '@type': 'type.googleapis.com/google.pubsub.v1.PubsubMessage', json: {}, attributes: { topic: 'projects/my-proj/topics/my-topic', request_id: 'r1' } },
45
- context: undefined,
46
- };
47
-
48
- await GcfCommon.process(payload, async () => ({ result: 42 }));
49
- // GcfCommon.response() will publish to the provided topic
50
- ```
51
-
52
- ### Publish a response to AMQP (RabbitMQ)
53
- Provide either `exchange` (with optional `queue` as routing key) or a `queue`.
54
-
55
- ```ts
56
- import { GcfCommon } from 'gcf-common-lib';
57
-
58
- GcfCommon.amqpOptions.url = 'amqp://guest:guest@localhost:5672';
59
-
60
- const payload = {
61
- event: { '@type': 'type.googleapis.com/google.pubsub.v1.PubsubMessage', json: {}, attributes: { exchange: 'events', queue: 'route.key', request_id: 'r1' } },
62
- context: undefined,
63
- };
64
-
65
- await GcfCommon.process(payload, async () => ({ result: 42 }));
66
- ```
67
-
68
- ## API overview
69
-
70
- ### GcfCommon
71
- - `process<TResponse, E = TEvent>(payload: TPayload<E>, handler: (p: TPayload<E>) => Promise<any> | any): Promise<any>`
72
- - Runs handler, then calls `response(...)` with handler result; on error publishes a structured error via `buildResponse(error)` and rethrows.
73
- - `response<E = TEvent>(payload: TPayload<E>, json?: TResponse, attributes?: Dict<any>): Promise<void>`
74
- - Publishes to Pub/Sub if `topic` is present; to AMQP if `exchange` or `queue` is present. Attributes are stringified.
75
- - `buildResponse(error: Error): TResponse`
76
- - `getOptions(payload: TPayload): Promise<Dict<any>>`
77
- - Parses JSON from `options` attribute with `safeJsonParse` and `{}` fallback.
78
- - `getMetadataOrAttribute<E = TEvent>(payload: TPayload<E>): TMetadataOrAttributes`
79
- - Extracts attributes from GCS finalize, Pub/Sub publish events, or HTTP request body shape.
80
- - `getRequestMessage(request: express.Request)`
81
-
82
- ### Utils
83
- - `ms({ w?, d?, h?, m?, s? }): number`
84
- - `sec({ w?, d?, h?, m?, s? }): number`
85
- - `indexToA1(idx: number): string`
86
- - `A1ToIndex(value: string): number`
87
- - `colNumToA1(columnNumber: number): string`
88
- - `A1ToColNum(value: string): number`
89
- - `safeJsonParse<T>(value: string, fallback?: T): T | undefined`
90
- - `delay(seconds: number): Promise<void>`
91
- - `timeoutAfter(seconds?: number): Promise<void>`
92
-
93
- ### AmqpHelper
94
- - `withAmqpConn(fn, url)` — acquire AMQP connection (Bluebird disposer) and run `fn`.
95
- - `withAmqpCh(fn, url, useConfirmChannel = false, prefetch = 1)` — channel lifecycle helper.
96
- - `publishAmqp(ch, exchange | undefined, routingKey, json, options?)`
97
- - `sendToQueueConfAmqp(ch: ConfirmChannel, queue, json, options?)`
98
-
99
- ### MongoHelper
100
- - `collectionExists(client, name)`
101
- - `collectionSafeGet(client, name, options?, afterCreate?)`
102
- - `withMongoClient(fn, url, options?)` — maintains a shared client in-process.
103
-
104
- ## Testing
105
- - Uses Node’s built-in test runner (`node:test`).
106
- - Prefer JS tests under `test/*.test.js` for zero-transpilation.
107
- - Avoid network I/O in unit tests by omitting `topic`, `exchange`, and `queue` attributes or by stubbing `GcfCommon.response`.
108
-
109
- ## License
1
+ # gcf-common-lib
2
+
3
+ Common helpers for Google Cloud Functions (GCF) and Node services:
4
+ - Orchestrate handlers and conditional response publishing via Pub/Sub or AMQP (RabbitMQ).
5
+ - Utilities: time helpers (ms/sec), Excel A1 conversions, safeJsonParse, simple delay/timeout.
6
+ - Helpers for AMQP and MongoDB lifecycles.
7
+
8
+ This library targets Node >= 20 and TypeScript. It is designed to be consumed by TS-aware builds or transpiled output.
9
+
10
+ ## Installation
11
+
12
+ ```sh
13
+ npm i gcf-common-lib
14
+ ```
15
+
16
+ ## Quick start
17
+
18
+ ### Process an event without network I/O
19
+ Pass a payload without `topic`, `exchange`, or `queue` to avoid publishing in unit tests or offline flows.
20
+
21
+ ```ts
22
+ import { GcfCommon } from 'gcf-common-lib';
23
+
24
+ export async function handler(payload: any) {
25
+ return { ok: 1 };
26
+ }
27
+
28
+ // Example payload: Pub/Sub-like message without routing attributes
29
+ const payload = {
30
+ event: { '@type': 'type.googleapis.com/google.pubsub.v1.PubsubMessage', json: { hello: 'world' }, attributes: {} },
31
+ context: undefined,
32
+ };
33
+
34
+ await GcfCommon.process(payload, handler);
35
+ ```
36
+
37
+ ### Publish a response to Pub/Sub
38
+ Set the `topic` attribute in metadata/attributes. Response attributes are coerced to strings; common fields `request_id`, `consumer_id`, `app_id`, `env` are propagated if present.
39
+
40
+ ```ts
41
+ import { GcfCommon } from 'gcf-common-lib';
42
+
43
+ const payload = {
44
+ event: { '@type': 'type.googleapis.com/google.pubsub.v1.PubsubMessage', json: {}, attributes: { topic: 'projects/my-proj/topics/my-topic', request_id: 'r1' } },
45
+ context: undefined,
46
+ };
47
+
48
+ await GcfCommon.process(payload, async () => ({ result: 42 }));
49
+ // GcfCommon.response() will publish to the provided topic
50
+ ```
51
+
52
+ ### Publish a response to AMQP (RabbitMQ)
53
+ Provide either `exchange` (with optional `queue` as routing key) or a `queue`.
54
+
55
+ ```ts
56
+ import { GcfCommon } from 'gcf-common-lib';
57
+
58
+ GcfCommon.amqpOptions.url = 'amqp://guest:guest@localhost:5672';
59
+
60
+ const payload = {
61
+ event: { '@type': 'type.googleapis.com/google.pubsub.v1.PubsubMessage', json: {}, attributes: { exchange: 'events', queue: 'route.key', request_id: 'r1' } },
62
+ context: undefined,
63
+ };
64
+
65
+ await GcfCommon.process(payload, async () => ({ result: 42 }));
66
+ ```
67
+
68
+ ## API overview
69
+
70
+ ### GcfCommon
71
+ - `process<TResponse, E = TEvent>(payload: TPayload<E>, handler: (p: TPayload<E>) => Promise<any> | any): Promise<any>`
72
+ - Runs handler, then calls `response(...)` with handler result; on error publishes a structured error via `buildResponse(error)` and rethrows.
73
+ - `response<E = TEvent>(payload: TPayload<E>, json?: TResponse, attributes?: Dict<any>): Promise<void>`
74
+ - Publishes to Pub/Sub if `topic` is present; to AMQP if `exchange` or `queue` is present. Attributes are stringified.
75
+ - `buildResponse(error: Error): TResponse`
76
+ - `getOptions(payload: TPayload): Promise<Dict<any>>`
77
+ - Parses JSON from `options` attribute with `safeJsonParse` and `{}` fallback.
78
+ - `getMetadataOrAttribute<E = TEvent>(payload: TPayload<E>): TMetadataOrAttributes`
79
+ - Extracts attributes from GCS finalize, Pub/Sub publish events, or HTTP request body shape.
80
+ - `getRequestMessage(request: express.Request)`
81
+
82
+ ### Utils
83
+ - `ms({ w?, d?, h?, m?, s? }): number`
84
+ - `sec({ w?, d?, h?, m?, s? }): number`
85
+ - `indexToA1(idx: number): string`
86
+ - `A1ToIndex(value: string): number`
87
+ - `colNumToA1(columnNumber: number): string`
88
+ - `A1ToColNum(value: string): number`
89
+ - `safeJsonParse<T>(value: string, fallback?: T): T | undefined`
90
+ - `delay(seconds: number): Promise<void>`
91
+ - `timeoutAfter(seconds?: number): Promise<void>`
92
+
93
+ ### AmqpHelper
94
+ - `withAmqpConn(fn, url)` — acquire AMQP connection (Bluebird disposer) and run `fn`.
95
+ - `withAmqpCh(fn, url, useConfirmChannel = false, prefetch = 1)` — channel lifecycle helper.
96
+ - `publishAmqp(ch, exchange | undefined, routingKey, json, options?)`
97
+ - `sendToQueueConfAmqp(ch: ConfirmChannel, queue, json, options?)`
98
+
99
+ ### MongoHelper
100
+ - `collectionExists(client, name)`
101
+ - `collectionSafeGet(client, name, options?, afterCreate?)`
102
+ - `withMongoClient(fn, url, options?)` — maintains a shared client in-process.
103
+
104
+ ## Testing
105
+ - Uses Node’s built-in test runner (`node:test`).
106
+ - Prefer JS tests under `test/*.test.js` for zero-transpilation.
107
+ - Avoid network I/O in unit tests by omitting `topic`, `exchange`, and `queue` attributes or by stubbing `GcfCommon.response`.
108
+
109
+ ## License
110
110
  MIT. See LICENSE.