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.
- package/.github/workflows/npm-publish.yml +37 -39
- package/.junie/guidelines.md +108 -108
- package/.prettierrc +10 -10
- package/README.md +109 -109
- package/docs/plan.md +211 -211
- package/docs/tasks.md +88 -88
- package/eslint.config.mjs +31 -31
- package/package.json +51 -61
- package/src/amqp-helper.ts.old +52 -52
- package/src/index.js +191 -191
- package/src/index.ts +162 -181
- package/src/mongo-helper.js +42 -42
- package/src/mongo-helper.ts +52 -51
- package/src/{mongo-lock.ts → mongo-lock.ts.old} +99 -99
- package/src/types.js +2 -2
- package/src/types.ts +81 -84
- package/src/utils.js +105 -105
- package/src/utils.ts +110 -110
- package/test/test.js +30 -30
- package/test/test.ts +32 -32
- package/tsconfig.json +7 -7
- package/CODE_OF_CONDUCT.md +0 -13
- package/CONTRIBUTING.md +0 -60
- package/LICENSE +0 -21
- package/src/mongo-lock.js +0 -86
- package/test/gcf-common.process.test.js +0 -63
- package/test/gcf-common.process.test.ts +0 -65
- package/test/metadata.test.js +0 -77
- package/test/metadata.test.ts +0 -78
- package/test/sample.test.js +0 -12
- package/test/ts-tests.test.js +0 -8
- package/test/utils.test.js +0 -43
- package/test/utils.test.ts +0 -47
|
@@ -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@
|
|
16
|
-
- uses: actions/setup-node@
|
|
17
|
-
with:
|
|
18
|
-
node-version:
|
|
19
|
-
- run: npm ci
|
|
20
|
-
# - run: npm run build
|
|
21
|
-
- run: npm test
|
|
22
|
-
|
|
23
|
-
publish-npm:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
steps:
|
|
30
|
-
- uses: actions/checkout@
|
|
31
|
-
- uses: actions/setup-node@
|
|
32
|
-
with:
|
|
33
|
-
node-version:
|
|
34
|
-
registry-url: https://registry.npmjs.org
|
|
35
|
-
- run: npm ci
|
|
36
|
-
# - run: npm run build
|
|
37
|
-
- run: npm publish
|
|
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
|
package/.junie/guidelines.md
CHANGED
|
@@ -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.
|