├── .editorconfig ├── .eslintrc.json ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ └── test-and-publish.yaml ├── .gitignore ├── .husky ├── .gitignore ├── commit-msg └── pre-commit ├── .prettierrc.yaml ├── .releaserc.json ├── CONTRIBUTING.md ├── README.md ├── commitlint.config.js ├── examples └── google-pubsub │ ├── README.md │ ├── index.js │ ├── package-lock.json │ └── package.json ├── package-lock.json ├── package.json ├── src ├── EmittedMessage.ts ├── GoogleCloudPubSub │ ├── ExtendedMessage.ts │ ├── GoogleCloudPubSub.ts │ ├── index.ts │ └── lib.ts ├── PubSub.ts ├── PubSubFactory.ts └── index.ts ├── test ├── GoogleCloudPubSub.test.ts └── utils │ ├── lib.ts │ ├── test-utils.ts │ └── tools.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | indent_size = 2, 2 | indent_style = space -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@algoan/eslint-config" 3 | } -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | 6 | ## Motivation and Context 7 | 8 | 9 | 10 | ## Types of changes 11 | 12 | - [ ] Bug fix (non-breaking change which fixes an issue) 13 | - [ ] New feature (non-breaking change which adds functionality) 14 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 5 9 | versioning-strategy: increase 10 | reviewers: 11 | - LeKer29 12 | - g-ongenae 13 | ignore: 14 | - dependency-name: y18n 15 | versions: 16 | - 4.0.1 17 | - dependency-name: pino-pretty 18 | versions: 19 | - 4.4.0 20 | - 4.5.0 21 | - 4.6.0 22 | - 4.7.0 23 | - 4.7.1 24 | - dependency-name: ini 25 | versions: 26 | - 1.3.7 27 | -------------------------------------------------------------------------------- /.github/workflows/test-and-publish.yaml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Algoan PubSub Test and Publish Workflow 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | workflow_dispatch: 12 | inputs: 13 | ref: 14 | description: 'Branch, tag or commit SHA1 to build' 15 | required: false 16 | type: string 17 | default: master 18 | 19 | 20 | jobs: 21 | test: 22 | runs-on: ubuntu-latest 23 | continue-on-error: true 24 | strategy: 25 | matrix: 26 | version: [ 27 | { node: "16.x", npm: "8" }, 28 | { node: "18.x", npm: "10" }, 29 | { node: "20.x", npm: "10" } 30 | ] 31 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 32 | 33 | steps: 34 | - uses: actions/checkout@v3 35 | - name: Use Node.js ${{ matrix.version.node }} 36 | uses: actions/setup-node@v3 37 | with: 38 | node-version: ${{ matrix.version.node }} 39 | cache: 'npm' 40 | - name: Get GCloud CLI to install pubsub emulator 41 | uses: google-github-actions/setup-gcloud@v0 42 | - name: Update GCloud Components 43 | run: gcloud components update --quiet 44 | - name: Install GCloud beta 45 | run: gcloud components install beta --quiet 46 | - name: Install pubsub-emulator 47 | run: gcloud components install pubsub-emulator --quiet 48 | - run: npm i -g npm@${{ matrix.version.npm }} 49 | - run: npm i 50 | - run: npm run cover 51 | 52 | publish: 53 | runs-on: ubuntu-latest 54 | name: Publish @algoan/pubsub to NPM 55 | needs: 56 | - test 57 | if: ${{ github.event_name == 'workflow_dispatch' }} 58 | steps: 59 | - uses: actions/checkout@v3 60 | with: 61 | fetch-depth: 0 62 | ref: ${{ inputs.ref }} 63 | - name: Use Node.js 64 | uses: actions/setup-node@v3 65 | with: 66 | node-version: '20.x' 67 | cache: 'npm' 68 | - run: npm i -g npm 69 | - run: npm ci 70 | - run: npm run compile 71 | - run: npm run semantic-release 72 | name: Run Semantic Release 73 | env: 74 | GITHUB_TOKEN: ${{ secrets.GH_PAT_SEMANTIC_RELEASE }} 75 | GITHUB_NPM_CONFIG_REGISTRY: https://npm.pkg.github.com/ 76 | GITHUB_NPM_TOKEN: ${{ secrets.GITHUB_TOKEN }} 77 | NPM_NPM_CONFIG_REGISTRY: https://registry.npmjs.org/ 78 | NPM_NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .DS_Store 4 | .nyc_output/ 5 | coverage/ 6 | creds.json 7 | 8 | #editors 9 | .idea 10 | .vscode 11 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | echo $1 5 | npx commitlint --edit $1 6 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.prettierrc.yaml: -------------------------------------------------------------------------------- 1 | # .prettierrc 2 | printWidth: 120 3 | singleQuote: true 4 | trailingComma: all 5 | parser: typescript 6 | arrowParens: always -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@semantic-release/commit-analyzer", 4 | "@semantic-release/release-notes-generator", 5 | [ 6 | "@amanda-mitchell/semantic-release-npm-multiple", 7 | { 8 | "registries": { 9 | "github": {}, 10 | "npm": {} 11 | } 12 | } 13 | ], 14 | "@semantic-release/github", 15 | ["@semantic-release/git", { 16 | "assets": ["package.json", "package-lock.json"], 17 | "message": "chore(release): ${nextRelease.version} [skip ci]" 18 | }] 19 | ], 20 | "preset": "angular" 21 | } 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We would love to have your contribute, thank you for that! 🎉 4 | 5 | If you want to add missing APIs, or correct an issue, you will have to follow this list of instructions. 6 | 7 | ## Instructions 8 | 9 | - Set up your local environment by forking the repository. 10 | - When you are about to commit, [commitlint](https://github.com/conventional-changelog/commitlint) is running to check if your commit message respects [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/). 11 | - Write tests, there is a high coverage on the repository. Simply run `npm run cover` to generate a `coverage/` directory. 12 | - Respect [coding style](#code-style). Run `npm run lint` to check if there are errors. 13 | - Open a Pull Request where you describe the feature/issue you are about to publish. 14 | 15 | ## Code Style 16 | 17 | This project uses [ESLint](https://eslint.org/) to analyze the TypeScript code. Commit are linted too thanks to [commitlint](https://github.com/conventional-changelog/commitlint) and the [conventional commit format](https://conventionalcommits.org/). 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PubSub 2 | 3 | This is a generic PubSub Factory exposing a listen and a emit method. 4 | 5 | _NOTE_: Today, only [Google Cloud PubSub](https://cloud.google.com/pubsub/docs/overview) has been added. 6 | 7 | ## Installation 8 | 9 | ```bash 10 | npm install --save @algoan/pubsub 11 | ``` 12 | 13 | ## Usage 14 | 15 | ### Google Cloud PubSub 16 | 17 | #### Run tests 18 | 19 | To run tests or to try the PubSubFactory class, you need to have a google account and have installed gcloud sdk. 20 | 21 | Then, to install the [Google PubSub simulator](https://cloud.google.com/pubsub/docs/emulator), run: 22 | 23 | ```shell 24 | gcloud components install pubsub-emulator 25 | gcloud components install beta 26 | gcloud components update 27 | ``` 28 | 29 | Start tests running: 30 | 31 | ```shell 32 | npm test 33 | ``` 34 | 35 | It will launch a Google PubSub emulator thanks to the [google-pubsub-emulator](https://github.com/ert78gb/google-pubsub-emulator) library. 36 | 37 | 38 | #### Example 39 | 40 | To create a PubSub instance using Google Cloud: 41 | 42 | ```typescript 43 | import { EmittedMessage, GCPubSub, PubSubFactory, Transport } from '@algoan/pubsub' 44 | 45 | const pubsub: GCPubSub = PubSubFactory.create({ 46 | transport: Transport.GOOGLE_PUBSUB, 47 | options: { 48 | projectId: 'test', 49 | // And all other Google PubSub properties 50 | } 51 | }); 52 | const topicName: string = 'some_topic'; 53 | 54 | await pubsub.listen(topicName, { 55 | autoAck: true, 56 | onMessage: (data: EmittedMessage<{foo: string}>) => { 57 | console.log(data.parsedData); // { foo: 'bar', time: {Date.now}, _eventName: 'some_topic' } 58 | // do whatever you want. The message has already been acknowledged 59 | }, 60 | onError: (error: Error) => { 61 | // Handle error as you wish 62 | } 63 | }); 64 | 65 | await pubsub.emit(topicName, { foo: 'bar' }); 66 | ``` 67 | 68 | ### Contribution 69 | 70 | Thank you for your future contribution 😁 Please follow [these instructions](CONTRIBUTING.md) before opening a pull request! 71 | 72 | ## API 73 | 74 | ### `PubSubFactory.create({ transport, options })` 75 | 76 | The only static method from the `PubSubFactory` class. It initiates a new PubSub instance depending on the `transport`. By default, it connects to [Google Cloud PubSub](https://googleapis.dev/nodejs/pubsub/latest/index.html). 77 | 78 | - `transport`: PubSub technology to use. Only `GOOGLE_PUBSUB` is available for now. 79 | - `options`: Options related to the transport. 80 | - If `transport === Transport.GOOGLE_PUBSUB`, then have a look at the [Google Cloud PubSub config client](https://googleapis.dev/nodejs/pubsub/latest/global.html#ClientConfig). 81 | - `debug`: Display logs if it is set to true. It uses a [pino logger](https://getpino.io/#/) and [pino-pretty](https://github.com/pinojs/pino-pretty) if `NODE_ENV` is not equal to `production`. 82 | - `pinoOptions`: If `debug` is set to true, set the [pino logger options](https://getpino.io/#/docs/api?id=options). Default to `level: debug` and `prettyPrint: true` if `NODE_ENV` is not equal to `production`. 83 | - `topicsPrefix`: Add a prefix to all created topics. Example: `topicsPrefix: 'my-topic'`, all topics will begin with `my-topic+{your topic name}`. 84 | - `topicsSeparator`: Customize separator between topicsPrefix and topic name. Example: `topicsSeparator: '-'`, all topics will be `{topic prefix}-{your topic name} (default to '+')`. 85 | - `subscriptionsPrefix`: Add a prefix to all created subscriptions. Example: `subscriptionsPrefix: 'my-sub'`, all subscriptions will begin with `my-sub%{your topic name}`. 86 | - `subscriptionsSeparator`: Customize separator between subscriptionsPrefix and topic name. Example: `subscriptionsSeparator: '-'`, all subscriptions will be `{subscription prefix}-{your topic name} (default to '%')`. 87 | - `namespace`: Add a namespace property to [Message attributes](https://googleapis.dev/nodejs/pubsub/latest/google.pubsub.v1.html#.PubsubMessage) when publishing on a topic. 88 | - `environment`: Add a environment property to [Message attributes](https://googleapis.dev/nodejs/pubsub/latest/google.pubsub.v1.html#.PubsubMessage) when publishing on a topic. 89 | 90 | ### `pubsub.listen(event, opts)` 91 | 92 | Listen to a specific event. 93 | 94 | _NOTE_: It only uses the [Google Cloud subscription pull](https://cloud.google.com/pubsub/docs/pull) delivery for now. 95 | 96 | - `event`: Name of the event. 97 | - `opts`: Options related to the Listener method 98 | - `onMessage`: Method called when receiving a message 99 | - `onError`: Method called when an error occurs 100 | - `options`: Option related to the chosen transport 101 | 102 | If the chosen transport is Google Cloud PubSub, then `options` would be: 103 | 104 | - `autoAck`: Automatically ACK an event as soon as it is received (default to `true`) 105 | - `subscriptionOptions`: Options related to the created Subscription: 106 | - `name`: Custom name for the subscription. Default: `event` (also equal to the topic name) 107 | - `get`: Options applied to the `getSubscription` method (have a look at [Subscription options](https://googleapis.dev/nodejs/pubsub/latest/Subscription.html#get)) 108 | - `sub`: Options applied to the subscription instance (see also [`setOptions` method](https://googleapis.dev/nodejs/pubsub/latest/Subscription.html#setOptions)) 109 | - `create`: Options applied to the `createSubscription` method (have a look at [Create Subscription options](https://googleapis.dev/nodejs/pubsub/latest/Topic.html#createSubscription)) 110 | - `topicOptions`: Options applied to the created topic (have a look at [Topic options](https://googleapis.dev/nodejs/pubsub/latest/Topic.html#get)) 111 | - `topicName`: Set the topic name. By default, it uses the default name with a prefix. 112 | 113 | ### `pubsub.emit(event, payload, opts)` 114 | 115 | Emit a specific event with a payload. It added attributes in the message if you have added a namespace or an environment when setting the `PubSubFactory` class. It also adds an `_eventName` and a `time` property in the emitted `payload`. 116 | 117 | - `event`: Name of the event to emit. 118 | - `payload`: Payload to send. It will be buffered by Google, and then parsed by the [listen](#pubsublistenevent-options) method. 119 | - `opts`: Options related to the Emit method 120 | - `metadata`: Custom metadata added to the message 121 | - `options`: Option related to the chosen transport 122 | 123 | If the chosen transport is Google Cloud PubSub, then `options` would be: 124 | 125 | - `topicOptions`: Options applied to the created topic (have a look at [Topic options](https://googleapis.dev/nodejs/pubsub/latest/Topic.html#get)) 126 | - `publishOptions`: Publish options set to the topic after its creation. Refer to [Publish Options](https://googleapis.dev/nodejs/pubsub/latest/global.html#PublishOptions) 127 | - `messageOptions`: Additional message options added to the message. Refer to [Message Options](https://googleapis.dev/nodejs/pubsub/latest/google.pubsub.v1.IPubsubMessage.html) 128 | 129 | ### `pubsub.unsubscribe(event)` 130 | 131 | Stop the server connection for a given subscription. 132 | 133 | - `event`: Name of of the event to stop listening for. 134 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'] 3 | } -------------------------------------------------------------------------------- /examples/google-pubsub/README.md: -------------------------------------------------------------------------------- 1 | # PubSub Example: Google Cloud Pub/Sub 2 | 3 | Sample Express application in order to test the @algoan/pubsub library with Google Pub/Sub. 4 | 5 | ## Getting Started 6 | 7 | Install dependencies: 8 | 9 | ```shell 10 | npm i 11 | ``` 12 | 13 | Run a google Pub/Sub emulator using gcloud CLI. You must ensure that `beta` and `pubsub-emulator` are installed: 14 | 15 | ```shell 16 | gcloud components install pubsub-emulator 17 | gcloud components install beta 18 | gcloud components update 19 | ```` 20 | 21 | You can also run the emulator using Docker: 22 | 23 | ```shell 24 | docker pull google/cloud-sdk:emulators 25 | docker run --rm -p 8085:8085 google/cloud-sdk:emulators /bin/bash -c "gcloud beta emulators pubsub start --project=test --host-port='0.0.0.0:8085'" 26 | ``` 27 | 28 | Then, run: 29 | 30 | ```shell 31 | npm start 32 | ``` 33 | 34 | Navigate on http://localhost:3000! To emit an event, simply call GET http://localhost:3000/emit. To close the subscription server collection, call the GET http://localhost:3000/close API. -------------------------------------------------------------------------------- /examples/google-pubsub/index.js: -------------------------------------------------------------------------------- 1 | process.env.PUBSUB_EMULATOR_HOST = 'localhost:8085'; 2 | process.env.PUBSUB_PROJECT_ID = 'test'; 3 | 4 | const express = require('express'); 5 | const timers = require('timers/promises'); 6 | const app = express(); 7 | const port = 3000; 8 | const Pubsub = require('@algoan/pubsub'); 9 | const topicName = 'my_topic'; 10 | 11 | const pubsubClient = Pubsub.PubSubFactory.create({ 12 | options: { 13 | projectId: 'test', 14 | }, 15 | }); 16 | const secondPubsubClient = Pubsub.PubSubFactory.create({ 17 | options: { 18 | projectId: 'test', 19 | }, 20 | }); 21 | 22 | let pubsubCall = 0; 23 | 24 | app.get('/', (req, res) => { 25 | res.send(`PubSub calls: ${pubsubCall}`); 26 | }); 27 | 28 | app.get('/emit', async (req, res) => { 29 | secondPubsubClient.emit(topicName, {}); 30 | await timers.setTimeout(1000); 31 | res.redirect('/'); 32 | }); 33 | 34 | app.get('/close', async (req, res) => { 35 | await pubsubClient.unsubscribe(topicName); 36 | await timers.setTimeout(1000); 37 | res.redirect('/'); 38 | }); 39 | 40 | app.listen(port, async () => { 41 | await pubsubClient.listen(topicName, { 42 | options: { 43 | subscriptionOptions: { 44 | name: topicName, 45 | }, 46 | }, 47 | onMessage: () => { 48 | console.log('Received message!'); 49 | pubsubCall++; 50 | }, 51 | }); 52 | console.log(`Example app listening on port ${port}`); 53 | }); 54 | -------------------------------------------------------------------------------- /examples/google-pubsub/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google-pubsub", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "google-pubsub", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@algoan/pubsub": "^6.1.2", 13 | "express": "^4.18.2" 14 | } 15 | }, 16 | "node_modules/@algoan/pubsub": { 17 | "version": "6.1.2", 18 | "resolved": "https://registry.npmjs.org/@algoan/pubsub/-/pubsub-6.1.2.tgz", 19 | "integrity": "sha512-aMUlSXqkmBMfd3XPq6FhRAu4PP9b2XcZ2A8LEiSJvn/uDNnGlsx7RjojK6eezDXWRupevgVKXpD6jIu1HOjviQ==", 20 | "dependencies": { 21 | "@google-cloud/pubsub": "^4.0.7", 22 | "pino": "^8.16.2" 23 | }, 24 | "engines": { 25 | "node": ">= 12" 26 | } 27 | }, 28 | "node_modules/@google-cloud/paginator": { 29 | "version": "5.0.0", 30 | "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.0.tgz", 31 | "integrity": "sha512-87aeg6QQcEPxGCOthnpUjvw4xAZ57G7pL8FS0C4e/81fr3FjkpUpibf1s2v5XGyGhUVGF4Jfg7yEcxqn2iUw1w==", 32 | "dependencies": { 33 | "arrify": "^2.0.0", 34 | "extend": "^3.0.2" 35 | }, 36 | "engines": { 37 | "node": ">=14.0.0" 38 | } 39 | }, 40 | "node_modules/@google-cloud/precise-date": { 41 | "version": "4.0.0", 42 | "resolved": "https://registry.npmjs.org/@google-cloud/precise-date/-/precise-date-4.0.0.tgz", 43 | "integrity": "sha512-1TUx3KdaU3cN7nfCdNf+UVqA/PSX29Cjcox3fZZBtINlRrXVTmUkQnCKv2MbBUbCopbK4olAT1IHl76uZyCiVA==", 44 | "engines": { 45 | "node": ">=14.0.0" 46 | } 47 | }, 48 | "node_modules/@google-cloud/projectify": { 49 | "version": "4.0.0", 50 | "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz", 51 | "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==", 52 | "engines": { 53 | "node": ">=14.0.0" 54 | } 55 | }, 56 | "node_modules/@google-cloud/promisify": { 57 | "version": "4.0.0", 58 | "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz", 59 | "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==", 60 | "engines": { 61 | "node": ">=14" 62 | } 63 | }, 64 | "node_modules/@google-cloud/pubsub": { 65 | "version": "4.1.1", 66 | "resolved": "https://registry.npmjs.org/@google-cloud/pubsub/-/pubsub-4.1.1.tgz", 67 | "integrity": "sha512-YKfCFJCzp0duhjKPbjJbVJQEc+okGG5EL8gDFdBchKKLkYcupooeGgXe8wHm54hjAEQZyw3aJ0Iy5/MOLrk/FA==", 68 | "dependencies": { 69 | "@google-cloud/paginator": "^5.0.0", 70 | "@google-cloud/precise-date": "^4.0.0", 71 | "@google-cloud/projectify": "^4.0.0", 72 | "@google-cloud/promisify": "^4.0.0", 73 | "@opentelemetry/api": "^1.6.0", 74 | "@opentelemetry/semantic-conventions": "~1.19.0", 75 | "@types/duplexify": "^3.6.0", 76 | "@types/long": "^4.0.0", 77 | "arrify": "^2.0.0", 78 | "extend": "^3.0.2", 79 | "google-auth-library": "^9.0.0", 80 | "google-gax": "^4.0.4", 81 | "heap-js": "^2.2.0", 82 | "is-stream-ended": "^0.1.4", 83 | "lodash.snakecase": "^4.1.1", 84 | "p-defer": "^3.0.0" 85 | }, 86 | "engines": { 87 | "node": ">=14.0.0" 88 | } 89 | }, 90 | "node_modules/@grpc/grpc-js": { 91 | "version": "1.9.15", 92 | "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", 93 | "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==", 94 | "license": "Apache-2.0", 95 | "dependencies": { 96 | "@grpc/proto-loader": "^0.7.8", 97 | "@types/node": ">=12.12.47" 98 | }, 99 | "engines": { 100 | "node": "^8.13.0 || >=10.10.0" 101 | } 102 | }, 103 | "node_modules/@grpc/proto-loader": { 104 | "version": "0.7.10", 105 | "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz", 106 | "integrity": "sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==", 107 | "dependencies": { 108 | "lodash.camelcase": "^4.3.0", 109 | "long": "^5.0.0", 110 | "protobufjs": "^7.2.4", 111 | "yargs": "^17.7.2" 112 | }, 113 | "bin": { 114 | "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" 115 | }, 116 | "engines": { 117 | "node": ">=6" 118 | } 119 | }, 120 | "node_modules/@opentelemetry/api": { 121 | "version": "1.7.0", 122 | "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.7.0.tgz", 123 | "integrity": "sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw==", 124 | "engines": { 125 | "node": ">=8.0.0" 126 | } 127 | }, 128 | "node_modules/@opentelemetry/semantic-conventions": { 129 | "version": "1.19.0", 130 | "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.19.0.tgz", 131 | "integrity": "sha512-14jRpC8f5c0gPSwoZ7SbEJni1PqI+AhAE8m1bMz6v+RPM4OlP1PT2UHBJj5Qh/ALLPjhVU/aZUK3YyjTUqqQVg==", 132 | "engines": { 133 | "node": ">=14" 134 | } 135 | }, 136 | "node_modules/@protobufjs/aspromise": { 137 | "version": "1.1.2", 138 | "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", 139 | "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" 140 | }, 141 | "node_modules/@protobufjs/base64": { 142 | "version": "1.1.2", 143 | "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", 144 | "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" 145 | }, 146 | "node_modules/@protobufjs/codegen": { 147 | "version": "2.0.4", 148 | "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", 149 | "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" 150 | }, 151 | "node_modules/@protobufjs/eventemitter": { 152 | "version": "1.1.0", 153 | "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", 154 | "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" 155 | }, 156 | "node_modules/@protobufjs/fetch": { 157 | "version": "1.1.0", 158 | "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", 159 | "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", 160 | "dependencies": { 161 | "@protobufjs/aspromise": "^1.1.1", 162 | "@protobufjs/inquire": "^1.1.0" 163 | } 164 | }, 165 | "node_modules/@protobufjs/float": { 166 | "version": "1.0.2", 167 | "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", 168 | "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" 169 | }, 170 | "node_modules/@protobufjs/inquire": { 171 | "version": "1.1.0", 172 | "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", 173 | "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" 174 | }, 175 | "node_modules/@protobufjs/path": { 176 | "version": "1.1.2", 177 | "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", 178 | "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" 179 | }, 180 | "node_modules/@protobufjs/pool": { 181 | "version": "1.1.0", 182 | "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", 183 | "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" 184 | }, 185 | "node_modules/@protobufjs/utf8": { 186 | "version": "1.1.0", 187 | "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", 188 | "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" 189 | }, 190 | "node_modules/@tootallnate/once": { 191 | "version": "2.0.0", 192 | "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", 193 | "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", 194 | "engines": { 195 | "node": ">= 10" 196 | } 197 | }, 198 | "node_modules/@types/caseless": { 199 | "version": "0.12.5", 200 | "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", 201 | "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==" 202 | }, 203 | "node_modules/@types/duplexify": { 204 | "version": "3.6.4", 205 | "resolved": "https://registry.npmjs.org/@types/duplexify/-/duplexify-3.6.4.tgz", 206 | "integrity": "sha512-2eahVPsd+dy3CL6FugAzJcxoraWhUghZGEQJns1kTKfCXWKJ5iG/VkaB05wRVrDKHfOFKqb0X0kXh91eE99RZg==", 207 | "dependencies": { 208 | "@types/node": "*" 209 | } 210 | }, 211 | "node_modules/@types/long": { 212 | "version": "4.0.2", 213 | "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", 214 | "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" 215 | }, 216 | "node_modules/@types/node": { 217 | "version": "20.11.10", 218 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.10.tgz", 219 | "integrity": "sha512-rZEfe/hJSGYmdfX9tvcPMYeYPW2sNl50nsw4jZmRcaG0HIAb0WYEpsB05GOb53vjqpyE9GUhlDQ4jLSoB5q9kg==", 220 | "dependencies": { 221 | "undici-types": "~5.26.4" 222 | } 223 | }, 224 | "node_modules/@types/request": { 225 | "version": "2.48.12", 226 | "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", 227 | "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", 228 | "dependencies": { 229 | "@types/caseless": "*", 230 | "@types/node": "*", 231 | "@types/tough-cookie": "*", 232 | "form-data": "^2.5.0" 233 | } 234 | }, 235 | "node_modules/@types/tough-cookie": { 236 | "version": "4.0.5", 237 | "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", 238 | "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==" 239 | }, 240 | "node_modules/abort-controller": { 241 | "version": "3.0.0", 242 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 243 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 244 | "dependencies": { 245 | "event-target-shim": "^5.0.0" 246 | }, 247 | "engines": { 248 | "node": ">=6.5" 249 | } 250 | }, 251 | "node_modules/accepts": { 252 | "version": "1.3.8", 253 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 254 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 255 | "dependencies": { 256 | "mime-types": "~2.1.34", 257 | "negotiator": "0.6.3" 258 | }, 259 | "engines": { 260 | "node": ">= 0.6" 261 | } 262 | }, 263 | "node_modules/agent-base": { 264 | "version": "7.1.0", 265 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", 266 | "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", 267 | "dependencies": { 268 | "debug": "^4.3.4" 269 | }, 270 | "engines": { 271 | "node": ">= 14" 272 | } 273 | }, 274 | "node_modules/agent-base/node_modules/debug": { 275 | "version": "4.3.4", 276 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 277 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 278 | "dependencies": { 279 | "ms": "2.1.2" 280 | }, 281 | "engines": { 282 | "node": ">=6.0" 283 | }, 284 | "peerDependenciesMeta": { 285 | "supports-color": { 286 | "optional": true 287 | } 288 | } 289 | }, 290 | "node_modules/agent-base/node_modules/ms": { 291 | "version": "2.1.2", 292 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 293 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 294 | }, 295 | "node_modules/ansi-regex": { 296 | "version": "5.0.1", 297 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 298 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 299 | "engines": { 300 | "node": ">=8" 301 | } 302 | }, 303 | "node_modules/ansi-styles": { 304 | "version": "4.3.0", 305 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 306 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 307 | "dependencies": { 308 | "color-convert": "^2.0.1" 309 | }, 310 | "engines": { 311 | "node": ">=8" 312 | }, 313 | "funding": { 314 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 315 | } 316 | }, 317 | "node_modules/array-flatten": { 318 | "version": "1.1.1", 319 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 320 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" 321 | }, 322 | "node_modules/arrify": { 323 | "version": "2.0.1", 324 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", 325 | "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", 326 | "engines": { 327 | "node": ">=8" 328 | } 329 | }, 330 | "node_modules/asynckit": { 331 | "version": "0.4.0", 332 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 333 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 334 | }, 335 | "node_modules/atomic-sleep": { 336 | "version": "1.0.0", 337 | "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", 338 | "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", 339 | "engines": { 340 | "node": ">=8.0.0" 341 | } 342 | }, 343 | "node_modules/base64-js": { 344 | "version": "1.5.1", 345 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 346 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 347 | "funding": [ 348 | { 349 | "type": "github", 350 | "url": "https://github.com/sponsors/feross" 351 | }, 352 | { 353 | "type": "patreon", 354 | "url": "https://www.patreon.com/feross" 355 | }, 356 | { 357 | "type": "consulting", 358 | "url": "https://feross.org/support" 359 | } 360 | ] 361 | }, 362 | "node_modules/bignumber.js": { 363 | "version": "9.1.2", 364 | "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", 365 | "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", 366 | "engines": { 367 | "node": "*" 368 | } 369 | }, 370 | "node_modules/body-parser": { 371 | "version": "1.20.3", 372 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", 373 | "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", 374 | "license": "MIT", 375 | "dependencies": { 376 | "bytes": "3.1.2", 377 | "content-type": "~1.0.5", 378 | "debug": "2.6.9", 379 | "depd": "2.0.0", 380 | "destroy": "1.2.0", 381 | "http-errors": "2.0.0", 382 | "iconv-lite": "0.4.24", 383 | "on-finished": "2.4.1", 384 | "qs": "6.13.0", 385 | "raw-body": "2.5.2", 386 | "type-is": "~1.6.18", 387 | "unpipe": "1.0.0" 388 | }, 389 | "engines": { 390 | "node": ">= 0.8", 391 | "npm": "1.2.8000 || >= 1.4.16" 392 | } 393 | }, 394 | "node_modules/buffer": { 395 | "version": "6.0.3", 396 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", 397 | "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", 398 | "funding": [ 399 | { 400 | "type": "github", 401 | "url": "https://github.com/sponsors/feross" 402 | }, 403 | { 404 | "type": "patreon", 405 | "url": "https://www.patreon.com/feross" 406 | }, 407 | { 408 | "type": "consulting", 409 | "url": "https://feross.org/support" 410 | } 411 | ], 412 | "dependencies": { 413 | "base64-js": "^1.3.1", 414 | "ieee754": "^1.2.1" 415 | } 416 | }, 417 | "node_modules/buffer-equal-constant-time": { 418 | "version": "1.0.1", 419 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 420 | "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" 421 | }, 422 | "node_modules/bytes": { 423 | "version": "3.1.2", 424 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 425 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 426 | "license": "MIT", 427 | "engines": { 428 | "node": ">= 0.8" 429 | } 430 | }, 431 | "node_modules/call-bind-apply-helpers": { 432 | "version": "1.0.2", 433 | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", 434 | "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", 435 | "license": "MIT", 436 | "dependencies": { 437 | "es-errors": "^1.3.0", 438 | "function-bind": "^1.1.2" 439 | }, 440 | "engines": { 441 | "node": ">= 0.4" 442 | } 443 | }, 444 | "node_modules/call-bound": { 445 | "version": "1.0.3", 446 | "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", 447 | "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", 448 | "license": "MIT", 449 | "dependencies": { 450 | "call-bind-apply-helpers": "^1.0.1", 451 | "get-intrinsic": "^1.2.6" 452 | }, 453 | "engines": { 454 | "node": ">= 0.4" 455 | }, 456 | "funding": { 457 | "url": "https://github.com/sponsors/ljharb" 458 | } 459 | }, 460 | "node_modules/cliui": { 461 | "version": "8.0.1", 462 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", 463 | "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", 464 | "dependencies": { 465 | "string-width": "^4.2.0", 466 | "strip-ansi": "^6.0.1", 467 | "wrap-ansi": "^7.0.0" 468 | }, 469 | "engines": { 470 | "node": ">=12" 471 | } 472 | }, 473 | "node_modules/color-convert": { 474 | "version": "2.0.1", 475 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 476 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 477 | "dependencies": { 478 | "color-name": "~1.1.4" 479 | }, 480 | "engines": { 481 | "node": ">=7.0.0" 482 | } 483 | }, 484 | "node_modules/color-name": { 485 | "version": "1.1.4", 486 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 487 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 488 | }, 489 | "node_modules/combined-stream": { 490 | "version": "1.0.8", 491 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 492 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 493 | "dependencies": { 494 | "delayed-stream": "~1.0.0" 495 | }, 496 | "engines": { 497 | "node": ">= 0.8" 498 | } 499 | }, 500 | "node_modules/content-disposition": { 501 | "version": "0.5.4", 502 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 503 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 504 | "dependencies": { 505 | "safe-buffer": "5.2.1" 506 | }, 507 | "engines": { 508 | "node": ">= 0.6" 509 | } 510 | }, 511 | "node_modules/content-type": { 512 | "version": "1.0.5", 513 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 514 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 515 | "license": "MIT", 516 | "engines": { 517 | "node": ">= 0.6" 518 | } 519 | }, 520 | "node_modules/cookie": { 521 | "version": "0.7.1", 522 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", 523 | "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", 524 | "license": "MIT", 525 | "engines": { 526 | "node": ">= 0.6" 527 | } 528 | }, 529 | "node_modules/cookie-signature": { 530 | "version": "1.0.6", 531 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 532 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" 533 | }, 534 | "node_modules/debug": { 535 | "version": "2.6.9", 536 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 537 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 538 | "license": "MIT", 539 | "dependencies": { 540 | "ms": "2.0.0" 541 | } 542 | }, 543 | "node_modules/delayed-stream": { 544 | "version": "1.0.0", 545 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 546 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 547 | "engines": { 548 | "node": ">=0.4.0" 549 | } 550 | }, 551 | "node_modules/depd": { 552 | "version": "2.0.0", 553 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 554 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 555 | "license": "MIT", 556 | "engines": { 557 | "node": ">= 0.8" 558 | } 559 | }, 560 | "node_modules/destroy": { 561 | "version": "1.2.0", 562 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 563 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", 564 | "license": "MIT", 565 | "engines": { 566 | "node": ">= 0.8", 567 | "npm": "1.2.8000 || >= 1.4.16" 568 | } 569 | }, 570 | "node_modules/dunder-proto": { 571 | "version": "1.0.1", 572 | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", 573 | "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", 574 | "license": "MIT", 575 | "dependencies": { 576 | "call-bind-apply-helpers": "^1.0.1", 577 | "es-errors": "^1.3.0", 578 | "gopd": "^1.2.0" 579 | }, 580 | "engines": { 581 | "node": ">= 0.4" 582 | } 583 | }, 584 | "node_modules/duplexify": { 585 | "version": "4.1.2", 586 | "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", 587 | "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", 588 | "dependencies": { 589 | "end-of-stream": "^1.4.1", 590 | "inherits": "^2.0.3", 591 | "readable-stream": "^3.1.1", 592 | "stream-shift": "^1.0.0" 593 | } 594 | }, 595 | "node_modules/ecdsa-sig-formatter": { 596 | "version": "1.0.11", 597 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", 598 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", 599 | "dependencies": { 600 | "safe-buffer": "^5.0.1" 601 | } 602 | }, 603 | "node_modules/ee-first": { 604 | "version": "1.1.1", 605 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 606 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", 607 | "license": "MIT" 608 | }, 609 | "node_modules/emoji-regex": { 610 | "version": "8.0.0", 611 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 612 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 613 | }, 614 | "node_modules/encodeurl": { 615 | "version": "2.0.0", 616 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", 617 | "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", 618 | "license": "MIT", 619 | "engines": { 620 | "node": ">= 0.8" 621 | } 622 | }, 623 | "node_modules/end-of-stream": { 624 | "version": "1.4.4", 625 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 626 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 627 | "dependencies": { 628 | "once": "^1.4.0" 629 | } 630 | }, 631 | "node_modules/es-define-property": { 632 | "version": "1.0.1", 633 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", 634 | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", 635 | "license": "MIT", 636 | "engines": { 637 | "node": ">= 0.4" 638 | } 639 | }, 640 | "node_modules/es-errors": { 641 | "version": "1.3.0", 642 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 643 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 644 | "license": "MIT", 645 | "engines": { 646 | "node": ">= 0.4" 647 | } 648 | }, 649 | "node_modules/es-object-atoms": { 650 | "version": "1.1.1", 651 | "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", 652 | "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", 653 | "license": "MIT", 654 | "dependencies": { 655 | "es-errors": "^1.3.0" 656 | }, 657 | "engines": { 658 | "node": ">= 0.4" 659 | } 660 | }, 661 | "node_modules/escalade": { 662 | "version": "3.1.1", 663 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 664 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 665 | "engines": { 666 | "node": ">=6" 667 | } 668 | }, 669 | "node_modules/escape-html": { 670 | "version": "1.0.3", 671 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 672 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", 673 | "license": "MIT" 674 | }, 675 | "node_modules/etag": { 676 | "version": "1.8.1", 677 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 678 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", 679 | "license": "MIT", 680 | "engines": { 681 | "node": ">= 0.6" 682 | } 683 | }, 684 | "node_modules/event-target-shim": { 685 | "version": "5.0.1", 686 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 687 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 688 | "engines": { 689 | "node": ">=6" 690 | } 691 | }, 692 | "node_modules/events": { 693 | "version": "3.3.0", 694 | "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", 695 | "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", 696 | "engines": { 697 | "node": ">=0.8.x" 698 | } 699 | }, 700 | "node_modules/express": { 701 | "version": "4.21.2", 702 | "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", 703 | "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", 704 | "license": "MIT", 705 | "dependencies": { 706 | "accepts": "~1.3.8", 707 | "array-flatten": "1.1.1", 708 | "body-parser": "1.20.3", 709 | "content-disposition": "0.5.4", 710 | "content-type": "~1.0.4", 711 | "cookie": "0.7.1", 712 | "cookie-signature": "1.0.6", 713 | "debug": "2.6.9", 714 | "depd": "2.0.0", 715 | "encodeurl": "~2.0.0", 716 | "escape-html": "~1.0.3", 717 | "etag": "~1.8.1", 718 | "finalhandler": "1.3.1", 719 | "fresh": "0.5.2", 720 | "http-errors": "2.0.0", 721 | "merge-descriptors": "1.0.3", 722 | "methods": "~1.1.2", 723 | "on-finished": "2.4.1", 724 | "parseurl": "~1.3.3", 725 | "path-to-regexp": "0.1.12", 726 | "proxy-addr": "~2.0.7", 727 | "qs": "6.13.0", 728 | "range-parser": "~1.2.1", 729 | "safe-buffer": "5.2.1", 730 | "send": "0.19.0", 731 | "serve-static": "1.16.2", 732 | "setprototypeof": "1.2.0", 733 | "statuses": "2.0.1", 734 | "type-is": "~1.6.18", 735 | "utils-merge": "1.0.1", 736 | "vary": "~1.1.2" 737 | }, 738 | "engines": { 739 | "node": ">= 0.10.0" 740 | }, 741 | "funding": { 742 | "type": "opencollective", 743 | "url": "https://opencollective.com/express" 744 | } 745 | }, 746 | "node_modules/extend": { 747 | "version": "3.0.2", 748 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 749 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 750 | }, 751 | "node_modules/fast-redact": { 752 | "version": "3.3.0", 753 | "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.3.0.tgz", 754 | "integrity": "sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ==", 755 | "engines": { 756 | "node": ">=6" 757 | } 758 | }, 759 | "node_modules/finalhandler": { 760 | "version": "1.3.1", 761 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", 762 | "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", 763 | "license": "MIT", 764 | "dependencies": { 765 | "debug": "2.6.9", 766 | "encodeurl": "~2.0.0", 767 | "escape-html": "~1.0.3", 768 | "on-finished": "2.4.1", 769 | "parseurl": "~1.3.3", 770 | "statuses": "2.0.1", 771 | "unpipe": "~1.0.0" 772 | }, 773 | "engines": { 774 | "node": ">= 0.8" 775 | } 776 | }, 777 | "node_modules/form-data": { 778 | "version": "2.5.1", 779 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", 780 | "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", 781 | "dependencies": { 782 | "asynckit": "^0.4.0", 783 | "combined-stream": "^1.0.6", 784 | "mime-types": "^2.1.12" 785 | }, 786 | "engines": { 787 | "node": ">= 0.12" 788 | } 789 | }, 790 | "node_modules/forwarded": { 791 | "version": "0.2.0", 792 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 793 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 794 | "engines": { 795 | "node": ">= 0.6" 796 | } 797 | }, 798 | "node_modules/fresh": { 799 | "version": "0.5.2", 800 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 801 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", 802 | "license": "MIT", 803 | "engines": { 804 | "node": ">= 0.6" 805 | } 806 | }, 807 | "node_modules/function-bind": { 808 | "version": "1.1.2", 809 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 810 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 811 | "license": "MIT", 812 | "funding": { 813 | "url": "https://github.com/sponsors/ljharb" 814 | } 815 | }, 816 | "node_modules/gaxios": { 817 | "version": "6.1.1", 818 | "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.1.1.tgz", 819 | "integrity": "sha512-bw8smrX+XlAoo9o1JAksBwX+hi/RG15J+NTSxmNPIclKC3ZVK6C2afwY8OSdRvOK0+ZLecUJYtj2MmjOt3Dm0w==", 820 | "dependencies": { 821 | "extend": "^3.0.2", 822 | "https-proxy-agent": "^7.0.1", 823 | "is-stream": "^2.0.0", 824 | "node-fetch": "^2.6.9" 825 | }, 826 | "engines": { 827 | "node": ">=14" 828 | } 829 | }, 830 | "node_modules/gcp-metadata": { 831 | "version": "6.1.0", 832 | "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", 833 | "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", 834 | "dependencies": { 835 | "gaxios": "^6.0.0", 836 | "json-bigint": "^1.0.0" 837 | }, 838 | "engines": { 839 | "node": ">=14" 840 | } 841 | }, 842 | "node_modules/get-caller-file": { 843 | "version": "2.0.5", 844 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 845 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 846 | "engines": { 847 | "node": "6.* || 8.* || >= 10.*" 848 | } 849 | }, 850 | "node_modules/get-intrinsic": { 851 | "version": "1.2.7", 852 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", 853 | "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", 854 | "license": "MIT", 855 | "dependencies": { 856 | "call-bind-apply-helpers": "^1.0.1", 857 | "es-define-property": "^1.0.1", 858 | "es-errors": "^1.3.0", 859 | "es-object-atoms": "^1.0.0", 860 | "function-bind": "^1.1.2", 861 | "get-proto": "^1.0.0", 862 | "gopd": "^1.2.0", 863 | "has-symbols": "^1.1.0", 864 | "hasown": "^2.0.2", 865 | "math-intrinsics": "^1.1.0" 866 | }, 867 | "engines": { 868 | "node": ">= 0.4" 869 | }, 870 | "funding": { 871 | "url": "https://github.com/sponsors/ljharb" 872 | } 873 | }, 874 | "node_modules/get-proto": { 875 | "version": "1.0.1", 876 | "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", 877 | "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", 878 | "license": "MIT", 879 | "dependencies": { 880 | "dunder-proto": "^1.0.1", 881 | "es-object-atoms": "^1.0.0" 882 | }, 883 | "engines": { 884 | "node": ">= 0.4" 885 | } 886 | }, 887 | "node_modules/google-auth-library": { 888 | "version": "9.5.0", 889 | "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.5.0.tgz", 890 | "integrity": "sha512-OUbP509lWVlZxuMY+Cgomw49VzZFP9myIcVeYEpeBlbXJbPC4R+K4BmO9hd3ciYM5QIwm5W1PODcKjqxtkye9Q==", 891 | "dependencies": { 892 | "base64-js": "^1.3.0", 893 | "ecdsa-sig-formatter": "^1.0.11", 894 | "gaxios": "^6.1.1", 895 | "gcp-metadata": "^6.1.0", 896 | "gtoken": "^7.0.0", 897 | "jws": "^4.0.0" 898 | }, 899 | "engines": { 900 | "node": ">=14" 901 | } 902 | }, 903 | "node_modules/google-gax": { 904 | "version": "4.2.1", 905 | "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.2.1.tgz", 906 | "integrity": "sha512-Yal4oh2GMHBsFX8zunxwaRuD2bP7rrA7Oz/ooXK8uOMGnP71jNVRl6fUv8chYLkPTqEzBSij9TZw49B86SDVTg==", 907 | "dependencies": { 908 | "@grpc/grpc-js": "~1.9.6", 909 | "@grpc/proto-loader": "^0.7.0", 910 | "@types/long": "^4.0.0", 911 | "abort-controller": "^3.0.0", 912 | "duplexify": "^4.0.0", 913 | "google-auth-library": "^9.0.0", 914 | "node-fetch": "^2.6.1", 915 | "object-hash": "^3.0.0", 916 | "proto3-json-serializer": "^2.0.0", 917 | "protobufjs": "7.2.6", 918 | "retry-request": "^7.0.0", 919 | "uuid": "^9.0.1" 920 | }, 921 | "engines": { 922 | "node": ">=14" 923 | } 924 | }, 925 | "node_modules/gopd": { 926 | "version": "1.2.0", 927 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", 928 | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", 929 | "license": "MIT", 930 | "engines": { 931 | "node": ">= 0.4" 932 | }, 933 | "funding": { 934 | "url": "https://github.com/sponsors/ljharb" 935 | } 936 | }, 937 | "node_modules/gtoken": { 938 | "version": "7.0.1", 939 | "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.0.1.tgz", 940 | "integrity": "sha512-KcFVtoP1CVFtQu0aSk3AyAt2og66PFhZAlkUOuWKwzMLoulHXG5W5wE5xAnHb+yl3/wEFoqGW7/cDGMU8igDZQ==", 941 | "dependencies": { 942 | "gaxios": "^6.0.0", 943 | "jws": "^4.0.0" 944 | }, 945 | "engines": { 946 | "node": ">=14.0.0" 947 | } 948 | }, 949 | "node_modules/has-symbols": { 950 | "version": "1.1.0", 951 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", 952 | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", 953 | "license": "MIT", 954 | "engines": { 955 | "node": ">= 0.4" 956 | }, 957 | "funding": { 958 | "url": "https://github.com/sponsors/ljharb" 959 | } 960 | }, 961 | "node_modules/hasown": { 962 | "version": "2.0.2", 963 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 964 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 965 | "license": "MIT", 966 | "dependencies": { 967 | "function-bind": "^1.1.2" 968 | }, 969 | "engines": { 970 | "node": ">= 0.4" 971 | } 972 | }, 973 | "node_modules/heap-js": { 974 | "version": "2.3.0", 975 | "resolved": "https://registry.npmjs.org/heap-js/-/heap-js-2.3.0.tgz", 976 | "integrity": "sha512-E5303mzwQ+4j/n2J0rDvEPBN7GKjhis10oHiYOgjxsmxYgqG++hz9NyLLOXttzH8as/DyiBHYpUrJTZWYaMo8Q==", 977 | "engines": { 978 | "node": ">=10.0.0" 979 | } 980 | }, 981 | "node_modules/http-errors": { 982 | "version": "2.0.0", 983 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 984 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 985 | "license": "MIT", 986 | "dependencies": { 987 | "depd": "2.0.0", 988 | "inherits": "2.0.4", 989 | "setprototypeof": "1.2.0", 990 | "statuses": "2.0.1", 991 | "toidentifier": "1.0.1" 992 | }, 993 | "engines": { 994 | "node": ">= 0.8" 995 | } 996 | }, 997 | "node_modules/http-proxy-agent": { 998 | "version": "5.0.0", 999 | "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", 1000 | "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", 1001 | "dependencies": { 1002 | "@tootallnate/once": "2", 1003 | "agent-base": "6", 1004 | "debug": "4" 1005 | }, 1006 | "engines": { 1007 | "node": ">= 6" 1008 | } 1009 | }, 1010 | "node_modules/http-proxy-agent/node_modules/agent-base": { 1011 | "version": "6.0.2", 1012 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", 1013 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", 1014 | "dependencies": { 1015 | "debug": "4" 1016 | }, 1017 | "engines": { 1018 | "node": ">= 6.0.0" 1019 | } 1020 | }, 1021 | "node_modules/http-proxy-agent/node_modules/debug": { 1022 | "version": "4.3.4", 1023 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 1024 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 1025 | "dependencies": { 1026 | "ms": "2.1.2" 1027 | }, 1028 | "engines": { 1029 | "node": ">=6.0" 1030 | }, 1031 | "peerDependenciesMeta": { 1032 | "supports-color": { 1033 | "optional": true 1034 | } 1035 | } 1036 | }, 1037 | "node_modules/http-proxy-agent/node_modules/ms": { 1038 | "version": "2.1.2", 1039 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1040 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1041 | }, 1042 | "node_modules/https-proxy-agent": { 1043 | "version": "7.0.2", 1044 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", 1045 | "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", 1046 | "dependencies": { 1047 | "agent-base": "^7.0.2", 1048 | "debug": "4" 1049 | }, 1050 | "engines": { 1051 | "node": ">= 14" 1052 | } 1053 | }, 1054 | "node_modules/https-proxy-agent/node_modules/debug": { 1055 | "version": "4.3.4", 1056 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 1057 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 1058 | "dependencies": { 1059 | "ms": "2.1.2" 1060 | }, 1061 | "engines": { 1062 | "node": ">=6.0" 1063 | }, 1064 | "peerDependenciesMeta": { 1065 | "supports-color": { 1066 | "optional": true 1067 | } 1068 | } 1069 | }, 1070 | "node_modules/https-proxy-agent/node_modules/ms": { 1071 | "version": "2.1.2", 1072 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1073 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1074 | }, 1075 | "node_modules/iconv-lite": { 1076 | "version": "0.4.24", 1077 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 1078 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 1079 | "license": "MIT", 1080 | "dependencies": { 1081 | "safer-buffer": ">= 2.1.2 < 3" 1082 | }, 1083 | "engines": { 1084 | "node": ">=0.10.0" 1085 | } 1086 | }, 1087 | "node_modules/ieee754": { 1088 | "version": "1.2.1", 1089 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 1090 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 1091 | "funding": [ 1092 | { 1093 | "type": "github", 1094 | "url": "https://github.com/sponsors/feross" 1095 | }, 1096 | { 1097 | "type": "patreon", 1098 | "url": "https://www.patreon.com/feross" 1099 | }, 1100 | { 1101 | "type": "consulting", 1102 | "url": "https://feross.org/support" 1103 | } 1104 | ] 1105 | }, 1106 | "node_modules/inherits": { 1107 | "version": "2.0.4", 1108 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1109 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 1110 | }, 1111 | "node_modules/ipaddr.js": { 1112 | "version": "1.9.1", 1113 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 1114 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 1115 | "engines": { 1116 | "node": ">= 0.10" 1117 | } 1118 | }, 1119 | "node_modules/is-fullwidth-code-point": { 1120 | "version": "3.0.0", 1121 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1122 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1123 | "engines": { 1124 | "node": ">=8" 1125 | } 1126 | }, 1127 | "node_modules/is-stream": { 1128 | "version": "2.0.1", 1129 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", 1130 | "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", 1131 | "engines": { 1132 | "node": ">=8" 1133 | }, 1134 | "funding": { 1135 | "url": "https://github.com/sponsors/sindresorhus" 1136 | } 1137 | }, 1138 | "node_modules/is-stream-ended": { 1139 | "version": "0.1.4", 1140 | "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz", 1141 | "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==" 1142 | }, 1143 | "node_modules/json-bigint": { 1144 | "version": "1.0.0", 1145 | "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", 1146 | "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", 1147 | "dependencies": { 1148 | "bignumber.js": "^9.0.0" 1149 | } 1150 | }, 1151 | "node_modules/jwa": { 1152 | "version": "2.0.0", 1153 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", 1154 | "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", 1155 | "dependencies": { 1156 | "buffer-equal-constant-time": "1.0.1", 1157 | "ecdsa-sig-formatter": "1.0.11", 1158 | "safe-buffer": "^5.0.1" 1159 | } 1160 | }, 1161 | "node_modules/jws": { 1162 | "version": "4.0.0", 1163 | "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", 1164 | "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", 1165 | "dependencies": { 1166 | "jwa": "^2.0.0", 1167 | "safe-buffer": "^5.0.1" 1168 | } 1169 | }, 1170 | "node_modules/lodash.camelcase": { 1171 | "version": "4.3.0", 1172 | "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", 1173 | "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" 1174 | }, 1175 | "node_modules/lodash.snakecase": { 1176 | "version": "4.1.1", 1177 | "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", 1178 | "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" 1179 | }, 1180 | "node_modules/long": { 1181 | "version": "5.2.3", 1182 | "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", 1183 | "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" 1184 | }, 1185 | "node_modules/math-intrinsics": { 1186 | "version": "1.1.0", 1187 | "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", 1188 | "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", 1189 | "license": "MIT", 1190 | "engines": { 1191 | "node": ">= 0.4" 1192 | } 1193 | }, 1194 | "node_modules/media-typer": { 1195 | "version": "0.3.0", 1196 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 1197 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", 1198 | "license": "MIT", 1199 | "engines": { 1200 | "node": ">= 0.6" 1201 | } 1202 | }, 1203 | "node_modules/merge-descriptors": { 1204 | "version": "1.0.3", 1205 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", 1206 | "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", 1207 | "license": "MIT", 1208 | "funding": { 1209 | "url": "https://github.com/sponsors/sindresorhus" 1210 | } 1211 | }, 1212 | "node_modules/methods": { 1213 | "version": "1.1.2", 1214 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 1215 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", 1216 | "engines": { 1217 | "node": ">= 0.6" 1218 | } 1219 | }, 1220 | "node_modules/mime": { 1221 | "version": "1.6.0", 1222 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 1223 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 1224 | "license": "MIT", 1225 | "bin": { 1226 | "mime": "cli.js" 1227 | }, 1228 | "engines": { 1229 | "node": ">=4" 1230 | } 1231 | }, 1232 | "node_modules/mime-db": { 1233 | "version": "1.52.0", 1234 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 1235 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 1236 | "engines": { 1237 | "node": ">= 0.6" 1238 | } 1239 | }, 1240 | "node_modules/mime-types": { 1241 | "version": "2.1.35", 1242 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 1243 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 1244 | "dependencies": { 1245 | "mime-db": "1.52.0" 1246 | }, 1247 | "engines": { 1248 | "node": ">= 0.6" 1249 | } 1250 | }, 1251 | "node_modules/ms": { 1252 | "version": "2.0.0", 1253 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1254 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", 1255 | "license": "MIT" 1256 | }, 1257 | "node_modules/negotiator": { 1258 | "version": "0.6.3", 1259 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 1260 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 1261 | "engines": { 1262 | "node": ">= 0.6" 1263 | } 1264 | }, 1265 | "node_modules/node-fetch": { 1266 | "version": "2.7.0", 1267 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", 1268 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 1269 | "dependencies": { 1270 | "whatwg-url": "^5.0.0" 1271 | }, 1272 | "engines": { 1273 | "node": "4.x || >=6.0.0" 1274 | }, 1275 | "peerDependencies": { 1276 | "encoding": "^0.1.0" 1277 | }, 1278 | "peerDependenciesMeta": { 1279 | "encoding": { 1280 | "optional": true 1281 | } 1282 | } 1283 | }, 1284 | "node_modules/object-hash": { 1285 | "version": "3.0.0", 1286 | "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", 1287 | "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", 1288 | "engines": { 1289 | "node": ">= 6" 1290 | } 1291 | }, 1292 | "node_modules/object-inspect": { 1293 | "version": "1.13.4", 1294 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", 1295 | "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", 1296 | "license": "MIT", 1297 | "engines": { 1298 | "node": ">= 0.4" 1299 | }, 1300 | "funding": { 1301 | "url": "https://github.com/sponsors/ljharb" 1302 | } 1303 | }, 1304 | "node_modules/on-exit-leak-free": { 1305 | "version": "2.1.2", 1306 | "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", 1307 | "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", 1308 | "engines": { 1309 | "node": ">=14.0.0" 1310 | } 1311 | }, 1312 | "node_modules/on-finished": { 1313 | "version": "2.4.1", 1314 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 1315 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 1316 | "license": "MIT", 1317 | "dependencies": { 1318 | "ee-first": "1.1.1" 1319 | }, 1320 | "engines": { 1321 | "node": ">= 0.8" 1322 | } 1323 | }, 1324 | "node_modules/once": { 1325 | "version": "1.4.0", 1326 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1327 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1328 | "dependencies": { 1329 | "wrappy": "1" 1330 | } 1331 | }, 1332 | "node_modules/p-defer": { 1333 | "version": "3.0.0", 1334 | "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-3.0.0.tgz", 1335 | "integrity": "sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw==", 1336 | "engines": { 1337 | "node": ">=8" 1338 | } 1339 | }, 1340 | "node_modules/parseurl": { 1341 | "version": "1.3.3", 1342 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 1343 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 1344 | "license": "MIT", 1345 | "engines": { 1346 | "node": ">= 0.8" 1347 | } 1348 | }, 1349 | "node_modules/path-to-regexp": { 1350 | "version": "0.1.12", 1351 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", 1352 | "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", 1353 | "license": "MIT" 1354 | }, 1355 | "node_modules/pino": { 1356 | "version": "8.17.2", 1357 | "resolved": "https://registry.npmjs.org/pino/-/pino-8.17.2.tgz", 1358 | "integrity": "sha512-LA6qKgeDMLr2ux2y/YiUt47EfgQ+S9LznBWOJdN3q1dx2sv0ziDLUBeVpyVv17TEcGCBuWf0zNtg3M5m1NhhWQ==", 1359 | "dependencies": { 1360 | "atomic-sleep": "^1.0.0", 1361 | "fast-redact": "^3.1.1", 1362 | "on-exit-leak-free": "^2.1.0", 1363 | "pino-abstract-transport": "v1.1.0", 1364 | "pino-std-serializers": "^6.0.0", 1365 | "process-warning": "^3.0.0", 1366 | "quick-format-unescaped": "^4.0.3", 1367 | "real-require": "^0.2.0", 1368 | "safe-stable-stringify": "^2.3.1", 1369 | "sonic-boom": "^3.7.0", 1370 | "thread-stream": "^2.0.0" 1371 | }, 1372 | "bin": { 1373 | "pino": "bin.js" 1374 | } 1375 | }, 1376 | "node_modules/pino-abstract-transport": { 1377 | "version": "1.1.0", 1378 | "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz", 1379 | "integrity": "sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==", 1380 | "dependencies": { 1381 | "readable-stream": "^4.0.0", 1382 | "split2": "^4.0.0" 1383 | } 1384 | }, 1385 | "node_modules/pino-abstract-transport/node_modules/readable-stream": { 1386 | "version": "4.5.2", 1387 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", 1388 | "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", 1389 | "dependencies": { 1390 | "abort-controller": "^3.0.0", 1391 | "buffer": "^6.0.3", 1392 | "events": "^3.3.0", 1393 | "process": "^0.11.10", 1394 | "string_decoder": "^1.3.0" 1395 | }, 1396 | "engines": { 1397 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 1398 | } 1399 | }, 1400 | "node_modules/pino-std-serializers": { 1401 | "version": "6.2.2", 1402 | "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", 1403 | "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==" 1404 | }, 1405 | "node_modules/process": { 1406 | "version": "0.11.10", 1407 | "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", 1408 | "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", 1409 | "engines": { 1410 | "node": ">= 0.6.0" 1411 | } 1412 | }, 1413 | "node_modules/process-warning": { 1414 | "version": "3.0.0", 1415 | "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", 1416 | "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" 1417 | }, 1418 | "node_modules/proto3-json-serializer": { 1419 | "version": "2.0.1", 1420 | "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.1.tgz", 1421 | "integrity": "sha512-8awBvjO+FwkMd6gNoGFZyqkHZXCFd54CIYTb6De7dPaufGJ2XNW+QUNqbMr8MaAocMdb+KpsD4rxEOaTBDCffA==", 1422 | "dependencies": { 1423 | "protobufjs": "^7.2.5" 1424 | }, 1425 | "engines": { 1426 | "node": ">=14.0.0" 1427 | } 1428 | }, 1429 | "node_modules/protobufjs": { 1430 | "version": "7.2.6", 1431 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.6.tgz", 1432 | "integrity": "sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==", 1433 | "hasInstallScript": true, 1434 | "dependencies": { 1435 | "@protobufjs/aspromise": "^1.1.2", 1436 | "@protobufjs/base64": "^1.1.2", 1437 | "@protobufjs/codegen": "^2.0.4", 1438 | "@protobufjs/eventemitter": "^1.1.0", 1439 | "@protobufjs/fetch": "^1.1.0", 1440 | "@protobufjs/float": "^1.0.2", 1441 | "@protobufjs/inquire": "^1.1.0", 1442 | "@protobufjs/path": "^1.1.2", 1443 | "@protobufjs/pool": "^1.1.0", 1444 | "@protobufjs/utf8": "^1.1.0", 1445 | "@types/node": ">=13.7.0", 1446 | "long": "^5.0.0" 1447 | }, 1448 | "engines": { 1449 | "node": ">=12.0.0" 1450 | } 1451 | }, 1452 | "node_modules/proxy-addr": { 1453 | "version": "2.0.7", 1454 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 1455 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 1456 | "dependencies": { 1457 | "forwarded": "0.2.0", 1458 | "ipaddr.js": "1.9.1" 1459 | }, 1460 | "engines": { 1461 | "node": ">= 0.10" 1462 | } 1463 | }, 1464 | "node_modules/qs": { 1465 | "version": "6.13.0", 1466 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", 1467 | "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", 1468 | "license": "BSD-3-Clause", 1469 | "dependencies": { 1470 | "side-channel": "^1.0.6" 1471 | }, 1472 | "engines": { 1473 | "node": ">=0.6" 1474 | }, 1475 | "funding": { 1476 | "url": "https://github.com/sponsors/ljharb" 1477 | } 1478 | }, 1479 | "node_modules/quick-format-unescaped": { 1480 | "version": "4.0.4", 1481 | "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", 1482 | "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" 1483 | }, 1484 | "node_modules/range-parser": { 1485 | "version": "1.2.1", 1486 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 1487 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 1488 | "license": "MIT", 1489 | "engines": { 1490 | "node": ">= 0.6" 1491 | } 1492 | }, 1493 | "node_modules/raw-body": { 1494 | "version": "2.5.2", 1495 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", 1496 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", 1497 | "license": "MIT", 1498 | "dependencies": { 1499 | "bytes": "3.1.2", 1500 | "http-errors": "2.0.0", 1501 | "iconv-lite": "0.4.24", 1502 | "unpipe": "1.0.0" 1503 | }, 1504 | "engines": { 1505 | "node": ">= 0.8" 1506 | } 1507 | }, 1508 | "node_modules/readable-stream": { 1509 | "version": "3.6.2", 1510 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 1511 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 1512 | "dependencies": { 1513 | "inherits": "^2.0.3", 1514 | "string_decoder": "^1.1.1", 1515 | "util-deprecate": "^1.0.1" 1516 | }, 1517 | "engines": { 1518 | "node": ">= 6" 1519 | } 1520 | }, 1521 | "node_modules/real-require": { 1522 | "version": "0.2.0", 1523 | "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", 1524 | "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", 1525 | "engines": { 1526 | "node": ">= 12.13.0" 1527 | } 1528 | }, 1529 | "node_modules/require-directory": { 1530 | "version": "2.1.1", 1531 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1532 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 1533 | "engines": { 1534 | "node": ">=0.10.0" 1535 | } 1536 | }, 1537 | "node_modules/retry-request": { 1538 | "version": "7.0.2", 1539 | "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", 1540 | "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", 1541 | "dependencies": { 1542 | "@types/request": "^2.48.8", 1543 | "extend": "^3.0.2", 1544 | "teeny-request": "^9.0.0" 1545 | }, 1546 | "engines": { 1547 | "node": ">=14" 1548 | } 1549 | }, 1550 | "node_modules/safe-buffer": { 1551 | "version": "5.2.1", 1552 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1553 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1554 | "funding": [ 1555 | { 1556 | "type": "github", 1557 | "url": "https://github.com/sponsors/feross" 1558 | }, 1559 | { 1560 | "type": "patreon", 1561 | "url": "https://www.patreon.com/feross" 1562 | }, 1563 | { 1564 | "type": "consulting", 1565 | "url": "https://feross.org/support" 1566 | } 1567 | ] 1568 | }, 1569 | "node_modules/safe-stable-stringify": { 1570 | "version": "2.4.3", 1571 | "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", 1572 | "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", 1573 | "engines": { 1574 | "node": ">=10" 1575 | } 1576 | }, 1577 | "node_modules/safer-buffer": { 1578 | "version": "2.1.2", 1579 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1580 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 1581 | "license": "MIT" 1582 | }, 1583 | "node_modules/send": { 1584 | "version": "0.19.0", 1585 | "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", 1586 | "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", 1587 | "license": "MIT", 1588 | "dependencies": { 1589 | "debug": "2.6.9", 1590 | "depd": "2.0.0", 1591 | "destroy": "1.2.0", 1592 | "encodeurl": "~1.0.2", 1593 | "escape-html": "~1.0.3", 1594 | "etag": "~1.8.1", 1595 | "fresh": "0.5.2", 1596 | "http-errors": "2.0.0", 1597 | "mime": "1.6.0", 1598 | "ms": "2.1.3", 1599 | "on-finished": "2.4.1", 1600 | "range-parser": "~1.2.1", 1601 | "statuses": "2.0.1" 1602 | }, 1603 | "engines": { 1604 | "node": ">= 0.8.0" 1605 | } 1606 | }, 1607 | "node_modules/send/node_modules/encodeurl": { 1608 | "version": "1.0.2", 1609 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 1610 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", 1611 | "license": "MIT", 1612 | "engines": { 1613 | "node": ">= 0.8" 1614 | } 1615 | }, 1616 | "node_modules/send/node_modules/ms": { 1617 | "version": "2.1.3", 1618 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1619 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1620 | "license": "MIT" 1621 | }, 1622 | "node_modules/serve-static": { 1623 | "version": "1.16.2", 1624 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", 1625 | "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", 1626 | "license": "MIT", 1627 | "dependencies": { 1628 | "encodeurl": "~2.0.0", 1629 | "escape-html": "~1.0.3", 1630 | "parseurl": "~1.3.3", 1631 | "send": "0.19.0" 1632 | }, 1633 | "engines": { 1634 | "node": ">= 0.8.0" 1635 | } 1636 | }, 1637 | "node_modules/setprototypeof": { 1638 | "version": "1.2.0", 1639 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 1640 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", 1641 | "license": "ISC" 1642 | }, 1643 | "node_modules/side-channel": { 1644 | "version": "1.1.0", 1645 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", 1646 | "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", 1647 | "license": "MIT", 1648 | "dependencies": { 1649 | "es-errors": "^1.3.0", 1650 | "object-inspect": "^1.13.3", 1651 | "side-channel-list": "^1.0.0", 1652 | "side-channel-map": "^1.0.1", 1653 | "side-channel-weakmap": "^1.0.2" 1654 | }, 1655 | "engines": { 1656 | "node": ">= 0.4" 1657 | }, 1658 | "funding": { 1659 | "url": "https://github.com/sponsors/ljharb" 1660 | } 1661 | }, 1662 | "node_modules/side-channel-list": { 1663 | "version": "1.0.0", 1664 | "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", 1665 | "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", 1666 | "license": "MIT", 1667 | "dependencies": { 1668 | "es-errors": "^1.3.0", 1669 | "object-inspect": "^1.13.3" 1670 | }, 1671 | "engines": { 1672 | "node": ">= 0.4" 1673 | }, 1674 | "funding": { 1675 | "url": "https://github.com/sponsors/ljharb" 1676 | } 1677 | }, 1678 | "node_modules/side-channel-map": { 1679 | "version": "1.0.1", 1680 | "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", 1681 | "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", 1682 | "license": "MIT", 1683 | "dependencies": { 1684 | "call-bound": "^1.0.2", 1685 | "es-errors": "^1.3.0", 1686 | "get-intrinsic": "^1.2.5", 1687 | "object-inspect": "^1.13.3" 1688 | }, 1689 | "engines": { 1690 | "node": ">= 0.4" 1691 | }, 1692 | "funding": { 1693 | "url": "https://github.com/sponsors/ljharb" 1694 | } 1695 | }, 1696 | "node_modules/side-channel-weakmap": { 1697 | "version": "1.0.2", 1698 | "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", 1699 | "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", 1700 | "license": "MIT", 1701 | "dependencies": { 1702 | "call-bound": "^1.0.2", 1703 | "es-errors": "^1.3.0", 1704 | "get-intrinsic": "^1.2.5", 1705 | "object-inspect": "^1.13.3", 1706 | "side-channel-map": "^1.0.1" 1707 | }, 1708 | "engines": { 1709 | "node": ">= 0.4" 1710 | }, 1711 | "funding": { 1712 | "url": "https://github.com/sponsors/ljharb" 1713 | } 1714 | }, 1715 | "node_modules/sonic-boom": { 1716 | "version": "3.8.0", 1717 | "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.0.tgz", 1718 | "integrity": "sha512-ybz6OYOUjoQQCQ/i4LU8kaToD8ACtYP+Cj5qd2AO36bwbdewxWJ3ArmJ2cr6AvxlL2o0PqnCcPGUgkILbfkaCA==", 1719 | "dependencies": { 1720 | "atomic-sleep": "^1.0.0" 1721 | } 1722 | }, 1723 | "node_modules/split2": { 1724 | "version": "4.2.0", 1725 | "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", 1726 | "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", 1727 | "engines": { 1728 | "node": ">= 10.x" 1729 | } 1730 | }, 1731 | "node_modules/statuses": { 1732 | "version": "2.0.1", 1733 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 1734 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 1735 | "license": "MIT", 1736 | "engines": { 1737 | "node": ">= 0.8" 1738 | } 1739 | }, 1740 | "node_modules/stream-events": { 1741 | "version": "1.0.5", 1742 | "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", 1743 | "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", 1744 | "dependencies": { 1745 | "stubs": "^3.0.0" 1746 | } 1747 | }, 1748 | "node_modules/stream-shift": { 1749 | "version": "1.0.3", 1750 | "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", 1751 | "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==" 1752 | }, 1753 | "node_modules/string_decoder": { 1754 | "version": "1.3.0", 1755 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 1756 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 1757 | "dependencies": { 1758 | "safe-buffer": "~5.2.0" 1759 | } 1760 | }, 1761 | "node_modules/string-width": { 1762 | "version": "4.2.3", 1763 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1764 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1765 | "dependencies": { 1766 | "emoji-regex": "^8.0.0", 1767 | "is-fullwidth-code-point": "^3.0.0", 1768 | "strip-ansi": "^6.0.1" 1769 | }, 1770 | "engines": { 1771 | "node": ">=8" 1772 | } 1773 | }, 1774 | "node_modules/strip-ansi": { 1775 | "version": "6.0.1", 1776 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1777 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1778 | "dependencies": { 1779 | "ansi-regex": "^5.0.1" 1780 | }, 1781 | "engines": { 1782 | "node": ">=8" 1783 | } 1784 | }, 1785 | "node_modules/stubs": { 1786 | "version": "3.0.0", 1787 | "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", 1788 | "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==" 1789 | }, 1790 | "node_modules/teeny-request": { 1791 | "version": "9.0.0", 1792 | "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", 1793 | "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", 1794 | "dependencies": { 1795 | "http-proxy-agent": "^5.0.0", 1796 | "https-proxy-agent": "^5.0.0", 1797 | "node-fetch": "^2.6.9", 1798 | "stream-events": "^1.0.5", 1799 | "uuid": "^9.0.0" 1800 | }, 1801 | "engines": { 1802 | "node": ">=14" 1803 | } 1804 | }, 1805 | "node_modules/teeny-request/node_modules/agent-base": { 1806 | "version": "6.0.2", 1807 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", 1808 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", 1809 | "dependencies": { 1810 | "debug": "4" 1811 | }, 1812 | "engines": { 1813 | "node": ">= 6.0.0" 1814 | } 1815 | }, 1816 | "node_modules/teeny-request/node_modules/debug": { 1817 | "version": "4.3.4", 1818 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 1819 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 1820 | "dependencies": { 1821 | "ms": "2.1.2" 1822 | }, 1823 | "engines": { 1824 | "node": ">=6.0" 1825 | }, 1826 | "peerDependenciesMeta": { 1827 | "supports-color": { 1828 | "optional": true 1829 | } 1830 | } 1831 | }, 1832 | "node_modules/teeny-request/node_modules/https-proxy-agent": { 1833 | "version": "5.0.1", 1834 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", 1835 | "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", 1836 | "dependencies": { 1837 | "agent-base": "6", 1838 | "debug": "4" 1839 | }, 1840 | "engines": { 1841 | "node": ">= 6" 1842 | } 1843 | }, 1844 | "node_modules/teeny-request/node_modules/ms": { 1845 | "version": "2.1.2", 1846 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1847 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1848 | }, 1849 | "node_modules/thread-stream": { 1850 | "version": "2.4.1", 1851 | "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.4.1.tgz", 1852 | "integrity": "sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==", 1853 | "dependencies": { 1854 | "real-require": "^0.2.0" 1855 | } 1856 | }, 1857 | "node_modules/toidentifier": { 1858 | "version": "1.0.1", 1859 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 1860 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 1861 | "license": "MIT", 1862 | "engines": { 1863 | "node": ">=0.6" 1864 | } 1865 | }, 1866 | "node_modules/tr46": { 1867 | "version": "0.0.3", 1868 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 1869 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 1870 | }, 1871 | "node_modules/type-is": { 1872 | "version": "1.6.18", 1873 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 1874 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 1875 | "license": "MIT", 1876 | "dependencies": { 1877 | "media-typer": "0.3.0", 1878 | "mime-types": "~2.1.24" 1879 | }, 1880 | "engines": { 1881 | "node": ">= 0.6" 1882 | } 1883 | }, 1884 | "node_modules/undici-types": { 1885 | "version": "5.26.5", 1886 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 1887 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" 1888 | }, 1889 | "node_modules/unpipe": { 1890 | "version": "1.0.0", 1891 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1892 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 1893 | "license": "MIT", 1894 | "engines": { 1895 | "node": ">= 0.8" 1896 | } 1897 | }, 1898 | "node_modules/util-deprecate": { 1899 | "version": "1.0.2", 1900 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1901 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 1902 | }, 1903 | "node_modules/utils-merge": { 1904 | "version": "1.0.1", 1905 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1906 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", 1907 | "engines": { 1908 | "node": ">= 0.4.0" 1909 | } 1910 | }, 1911 | "node_modules/uuid": { 1912 | "version": "9.0.1", 1913 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", 1914 | "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", 1915 | "funding": [ 1916 | "https://github.com/sponsors/broofa", 1917 | "https://github.com/sponsors/ctavan" 1918 | ], 1919 | "bin": { 1920 | "uuid": "dist/bin/uuid" 1921 | } 1922 | }, 1923 | "node_modules/vary": { 1924 | "version": "1.1.2", 1925 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1926 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 1927 | "engines": { 1928 | "node": ">= 0.8" 1929 | } 1930 | }, 1931 | "node_modules/webidl-conversions": { 1932 | "version": "3.0.1", 1933 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 1934 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 1935 | }, 1936 | "node_modules/whatwg-url": { 1937 | "version": "5.0.0", 1938 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 1939 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 1940 | "dependencies": { 1941 | "tr46": "~0.0.3", 1942 | "webidl-conversions": "^3.0.0" 1943 | } 1944 | }, 1945 | "node_modules/wrap-ansi": { 1946 | "version": "7.0.0", 1947 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1948 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1949 | "dependencies": { 1950 | "ansi-styles": "^4.0.0", 1951 | "string-width": "^4.1.0", 1952 | "strip-ansi": "^6.0.0" 1953 | }, 1954 | "engines": { 1955 | "node": ">=10" 1956 | }, 1957 | "funding": { 1958 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 1959 | } 1960 | }, 1961 | "node_modules/wrappy": { 1962 | "version": "1.0.2", 1963 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1964 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 1965 | }, 1966 | "node_modules/y18n": { 1967 | "version": "5.0.8", 1968 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 1969 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 1970 | "engines": { 1971 | "node": ">=10" 1972 | } 1973 | }, 1974 | "node_modules/yargs": { 1975 | "version": "17.7.2", 1976 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", 1977 | "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", 1978 | "dependencies": { 1979 | "cliui": "^8.0.1", 1980 | "escalade": "^3.1.1", 1981 | "get-caller-file": "^2.0.5", 1982 | "require-directory": "^2.1.1", 1983 | "string-width": "^4.2.3", 1984 | "y18n": "^5.0.5", 1985 | "yargs-parser": "^21.1.1" 1986 | }, 1987 | "engines": { 1988 | "node": ">=12" 1989 | } 1990 | }, 1991 | "node_modules/yargs-parser": { 1992 | "version": "21.1.1", 1993 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", 1994 | "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", 1995 | "engines": { 1996 | "node": ">=12" 1997 | } 1998 | } 1999 | } 2000 | } 2001 | -------------------------------------------------------------------------------- /examples/google-pubsub/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google-pubsub", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@algoan/pubsub": "^6.1.2", 14 | "express": "^4.18.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@algoan/pubsub", 3 | "version": "6.2.0", 4 | "description": "PubSub Library for algoan", 5 | "main": "./dist/src/index.js", 6 | "typings": "./dist/src/index.d.ts", 7 | "scripts": { 8 | "clean": "rimraf ./dist ./coverage ./.nyc_output ./node_modules", 9 | "compile": "tsc -p .", 10 | "prepare": "husky install", 11 | "commit": "git-cz", 12 | "pretest": "npm run compile && npm run lint", 13 | "prettier": "prettier --write '{src,test,examples}/**/*.{js,ts}'", 14 | "lint": "eslint ./src/**/*.ts", 15 | "test": "ava --color --verbose", 16 | "cover": "nyc --reporter=html --reporter=lcov npm test", 17 | "semantic-release": "semantic-release" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/algoan/pubsub.git" 22 | }, 23 | "keywords": [ 24 | "typescript", 25 | "nodejs", 26 | "google-pubsub" 27 | ], 28 | "files": [ 29 | "dist/src/" 30 | ], 31 | "author": "algoan", 32 | "license": "ISC", 33 | "devDependencies": { 34 | "@algoan/eslint-config": "^2.1.1", 35 | "@amanda-mitchell/semantic-release-npm-multiple": "github:algoan/semantic-release-npm-multiple#fd763c631503cb88dd143ce72c5838aa1b19ace4", 36 | "@ava/typescript": "^4.1.0", 37 | "@commitlint/cli": "^19.8.1", 38 | "@commitlint/config-conventional": "^19.8.1", 39 | "@semantic-release/git": "^10.0.1", 40 | "@types/sinon": "^17.0.4", 41 | "@types/uuid": "^10.0.0", 42 | "ava": "^5.3.1", 43 | "commitizen": "^4.3.1", 44 | "cz-conventional-changelog": "^3.3.0", 45 | "eslint": "^8.57.0", 46 | "google-pubsub-emulator": "^7.1.0", 47 | "husky": "^9.1.7", 48 | "lint-staged": "^16.1.0", 49 | "nyc": "^17.1.0", 50 | "prettier": "^3.5.3", 51 | "rimraf": "^6.0.1", 52 | "semantic-release": "^24.2.5", 53 | "sinon": "^20.0.0", 54 | "typescript": "^5.8.3", 55 | "uuid": "^11.1.0" 56 | }, 57 | "dependencies": { 58 | "@google-cloud/pubsub": "^4.11.0", 59 | "pino": "^9.7.0" 60 | }, 61 | "engines": { 62 | "node": ">= 12" 63 | }, 64 | "ava": { 65 | "files": [ 66 | "test/**/*.test.ts" 67 | ], 68 | "typescript": { 69 | "rewritePaths": { 70 | "test/": "dist/test/" 71 | }, 72 | "compile": false 73 | }, 74 | "failFast": true 75 | }, 76 | "publishConfig": { 77 | "access": "public" 78 | }, 79 | "config": { 80 | "commitizen": { 81 | "path": "./node_modules/cz-conventional-changelog" 82 | } 83 | }, 84 | "lint-staged": { 85 | "{src,test}/**/*.ts": [ 86 | "prettier --list-different \"{src,test}/**/*.ts\"" 87 | ] 88 | }, 89 | "nyc": { 90 | "check-coverage": false, 91 | "per-file": true, 92 | "cache": false, 93 | "extension": [ 94 | ".ts" 95 | ] 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/EmittedMessage.ts: -------------------------------------------------------------------------------- 1 | import { Message } from '@google-cloud/pubsub'; 2 | import { ErrorPayload } from './GoogleCloudPubSub'; 3 | 4 | /** 5 | * Message emitted by the PubSub 6 | */ 7 | export interface EmittedMessage { 8 | /** Message unique identifier */ 9 | id: string; 10 | /** Payload sent */ 11 | payload: T | ErrorPayload; 12 | /** Metadata: namespace, environment etc */ 13 | metadata?: Metadata; 14 | /** Acknowledgment unique identifier */ 15 | ackId?: string; 16 | /** Counter, if message are ordered */ 17 | count?: number; 18 | /** Date of emission */ 19 | emittedAt: Date; 20 | /** Date of reception */ 21 | receivedAt: Date; 22 | /** Duration in ms */ 23 | duration: number; 24 | /** Ordering Key */ 25 | orderingKey?: string; 26 | /** Acknowledge method for pubsub providers with a manual acknowledgment */ 27 | ack(): void; 28 | /** Modify Acknowledge method for pubsub providers with a manual acknowledgment */ 29 | modAck(deadline: number): void; 30 | /** Not-Acknowledge method for pubsub providers with a manual acknowledgment */ 31 | nack(): void; 32 | /** Getter for retrieving the original message for custom use case */ 33 | getOriginalMessage(): Message; 34 | } 35 | 36 | /** 37 | * Payload metadata 38 | */ 39 | export interface Metadata { 40 | namespace?: string; 41 | environment?: string; 42 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 43 | [key: string]: any; 44 | } 45 | 46 | /** 47 | * Check if a message is in an error state 48 | * @param payload 49 | */ 50 | // eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/tslint/config 51 | export const isPayloadError = (payload: T | ErrorPayload): payload is ErrorPayload => 52 | 'code' in payload && 'err' in payload; 53 | -------------------------------------------------------------------------------- /src/GoogleCloudPubSub/ExtendedMessage.ts: -------------------------------------------------------------------------------- 1 | import { Attributes, Message } from '@google-cloud/pubsub'; 2 | 3 | import { EmittedMessage } from '..'; 4 | import { ErrorPayload } from './lib'; 5 | 6 | /** 7 | * Extends the Google PubSub message class by adding a parsed data getter 8 | */ 9 | export class ExtendedMessage implements EmittedMessage { 10 | /** Message unique identifier */ 11 | public id: string; 12 | /** Payload sent */ 13 | public payload: T | ErrorPayload; 14 | /** Metadata: namespace, environment etc */ 15 | public metadata?: Attributes; 16 | /** Acknowledgment unique identifier */ 17 | public ackId?: string; 18 | /** Counter, if message are ordered */ 19 | /** @deprecated this parameter is wrong and based on a falsy condition */ 20 | public count?: number; 21 | /** Date of emission */ 22 | public emittedAt: Date; 23 | /** Date of reception */ 24 | public receivedAt: Date; 25 | /** Duration in ms */ 26 | public duration: number; 27 | /** Ordering key if it exists */ 28 | public orderingKey?: string; 29 | /** GoogleCloud ack method, is defined if autoAck is disable */ 30 | private readonly originalMessage: Message; 31 | 32 | constructor(message: Message) { 33 | this.id = message.id; 34 | try { 35 | // eslint-disable-next-line @typescript-eslint/tslint/config 36 | this.payload = JSON.parse(message.data.toString()); 37 | } catch (err) { 38 | this.payload = { 39 | code: 'JSON_PARSE_ERROR_MESSAGE', 40 | err, 41 | }; 42 | } 43 | this.metadata = message.attributes; 44 | this.ackId = message.ackId; 45 | this.count = isNaN(Number(message.orderingKey)) ? undefined : Number(message.orderingKey); 46 | this.emittedAt = message.publishTime; 47 | this.receivedAt = new Date(message.received); 48 | this.duration = message.received - this.emittedAt.valueOf(); 49 | this.originalMessage = message; 50 | this.orderingKey = message.orderingKey; 51 | } 52 | 53 | /** 54 | * Shared ack() method. 55 | * Use it if "autoAck" is disabled 56 | */ 57 | public ack(): void { 58 | this.originalMessage.ack(); 59 | } 60 | 61 | /** 62 | * Shared modAck() method. 63 | * Use it if "autoAck" is disabled 64 | */ 65 | public modAck(deadline: number): void { 66 | this.originalMessage.modAck(deadline); 67 | } 68 | 69 | /** 70 | * Shared nack() method. 71 | * Use it if "autoAck" is disabled 72 | */ 73 | public nack(): void { 74 | this.originalMessage.nack(); 75 | } 76 | /** 77 | * Getter for retrieving the original message. 78 | * Use it if extended message is not helpful 79 | */ 80 | public getOriginalMessage(): Message { 81 | return this.originalMessage; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/GoogleCloudPubSub/GoogleCloudPubSub.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Attributes, 3 | CreateSubscriptionResponse, 4 | ExistsResponse, 5 | GetSubscriptionResponse, 6 | GetTopicOptions, 7 | GetTopicResponse, 8 | Message, 9 | PubSub as GPubSub, 10 | Subscription, 11 | Topic, 12 | PublishOptions, 13 | } from '@google-cloud/pubsub'; 14 | import { pino } from 'pino'; 15 | 16 | import { EmitOptions, ListenOptions } from '..'; 17 | import { ExtendedMessage } from './ExtendedMessage'; 18 | import { 19 | GCListenOptions, 20 | GCPubSub, 21 | GCSubscriptionOptions, 22 | GooglePubSubOptions, 23 | SubscriptionMap, 24 | TopicMap, 25 | } from './lib'; 26 | 27 | /** 28 | * Google PubSub SDK 29 | */ 30 | export class GoogleCloudPubSub implements GCPubSub { 31 | /** 32 | * Google cloud pubsub Client 33 | */ 34 | public client: GPubSub; 35 | 36 | /** 37 | * Cached topics of the emitter 38 | */ 39 | public readonly subscriptions: SubscriptionMap; 40 | 41 | /** 42 | * Subscription prefix 43 | * Example: if "subscriptionPrefix = app", 44 | * then subscription names will begin with "app%" 45 | */ 46 | private readonly subscriptionsPrefix?: string; 47 | 48 | /** 49 | * Subscription separator 50 | * Example: if "subscriptionSeparator = -", 51 | * then subscription name will begin with "-" 52 | * 53 | * @defaultValue `%` 54 | */ 55 | private readonly subscriptionsSeparator?: string; 56 | 57 | /** 58 | * Topic prefix 59 | * Example: if "topicsPrefix = algoan", 60 | * then topic names will begin with "algoan+" 61 | */ 62 | private readonly topicsPrefix?: string; 63 | 64 | /** 65 | * Topic separator 66 | * Example: if "topicsSeparator = -", 67 | * then topic names will begin with "-" 68 | * 69 | * @defaultValue '+' 70 | */ 71 | private readonly topicsSeparator?: string; 72 | 73 | /** 74 | * An optional namespace 75 | * Can be useful when emitting an event 76 | */ 77 | private readonly namespace?: string; 78 | 79 | /** 80 | * Optional environment 81 | * Can be useful to differentiate development from production 82 | */ 83 | private readonly environment?: string; 84 | 85 | /** 86 | * Cached topics of the emitter 87 | */ 88 | private readonly topics: TopicMap; 89 | 90 | /** 91 | * Logger 92 | */ 93 | private readonly logger: pino.Logger; 94 | 95 | constructor(options: GooglePubSubOptions = {}) { 96 | this.client = new GPubSub(options); 97 | this.subscriptionsPrefix = options.subscriptionsPrefix; 98 | this.subscriptionsSeparator = options.subscriptionsSeparator !== undefined ? options.subscriptionsSeparator : '%'; 99 | this.topicsPrefix = options.topicsPrefix; 100 | this.topicsSeparator = options.topicsSeparator !== undefined ? options.topicsSeparator : '+'; 101 | this.namespace = options.namespace; 102 | this.environment = options.environment; 103 | this.topics = new Map(); 104 | this.subscriptions = new Map(); 105 | this.logger = pino({ 106 | level: options.debug === true ? 'debug' : 'silent', 107 | ...options.pinoOptions, 108 | }); 109 | } 110 | 111 | /** 112 | * Listen to a Google PubSub subscription 113 | * Only pull method 114 | * @tutorial https://cloud.google.com/pubsub/docs/pull 115 | * @param event Event to subscribe to 116 | * @param opts Options related to the listen method 117 | */ 118 | public async listen( 119 | event: string, 120 | opts: ListenOptions = { options: { autoAck: true } }, 121 | ): Promise { 122 | const topicName: string = opts?.options?.topicName ?? this.getTopicName(event); 123 | const topic: Topic = await this.getOrCreateTopic(topicName, opts.options?.topicOptions); 124 | const subscription: Subscription = await this.getOrCreateSubscription( 125 | event, 126 | topic, 127 | opts.options?.subscriptionOptions, 128 | ); 129 | this.logger.debug(`Listened to topic ${topic.name} with subscription ${subscription.name}`); 130 | 131 | subscription.on('message', (message: Message): void => { 132 | const extendedMessage: ExtendedMessage = new ExtendedMessage(message); 133 | 134 | this.logger.debug( 135 | { ...extendedMessage, payload: undefined, originalMessage: undefined }, 136 | `A message has been received for Subscription ${subscription.name} after ${ 137 | message.received - message.publishTime.valueOf() 138 | } ms`, 139 | ); 140 | 141 | if (opts.options?.autoAck !== false) { 142 | extendedMessage.ack(); 143 | } 144 | 145 | if (opts.onMessage !== undefined) { 146 | opts.onMessage(extendedMessage); 147 | } 148 | }); 149 | 150 | subscription.on('error', (error: Error): void => { 151 | this.logger.error(error, `An error occurred when listening to subscription ${subscription.name}`); 152 | 153 | if (opts.onError !== undefined) { 154 | opts.onError(error); 155 | } 156 | }); 157 | } 158 | 159 | /** 160 | * Emit an event using the Google PubSub publish JSON method 161 | * @tutorial https://cloud.google.com/pubsub/docs/publisher 162 | * @param event Event name to publish 163 | * @param payload Payload to share 164 | * @param opts Emit options 165 | */ 166 | public async emit( 167 | event: string, 168 | data: Record, 169 | opts: EmitOptions = {}, 170 | ): Promise { 171 | const topic: Topic = await this.getOrCreateTopic( 172 | this.getTopicName(event), 173 | opts.options?.topicOptions, 174 | opts.options?.publishOptions, 175 | ); 176 | this.logger.debug(`Found topic ${topic.name} for event ${event}`); 177 | 178 | const attributes: Attributes = { ...opts.options?.messageOptions?.attributes }; 179 | 180 | if (this.namespace !== undefined) { 181 | attributes.namespace = this.namespace; 182 | } 183 | 184 | if (this.environment !== undefined) { 185 | attributes.environment = this.environment; 186 | } 187 | 188 | this.logger.debug( 189 | { 190 | attributes, 191 | }, 192 | `Sending payload to Topic ${topic.name}`, 193 | ); 194 | 195 | return topic.publishMessage({ 196 | json: data, 197 | attributes: { ...attributes, ...opts.metadata }, 198 | ...opts.options?.messageOptions, 199 | }); 200 | } 201 | 202 | /** 203 | * Stop listening to a specific subscription. Close the server connection. 204 | * @param event Event name 205 | */ 206 | public async unsubscribe(event: string): Promise { 207 | const subscriptionName: string = this.getSubscriptionName(event); 208 | // Cover a case where there could be a custom subscription name with a prefix. 209 | const cachedSubscription: Subscription | undefined = 210 | this.subscriptions.get(subscriptionName) || this.subscriptions.get(event); 211 | 212 | if (cachedSubscription === undefined) { 213 | return; 214 | } 215 | 216 | return cachedSubscription.close(); 217 | } 218 | 219 | /** 220 | * Get or create a topic on Google PubSub 221 | * Also fill the topic map in-memory cache 222 | * @tutorial https://github.com/googleapis/nodejs-pubsub/blob/master/samples/createTopic.js 223 | * @param name Name of the topic 224 | */ 225 | private async getOrCreateTopic( 226 | name: string, 227 | getTopicOptions?: GetTopicOptions, 228 | publishOptions?: PublishOptions, 229 | ): Promise { 230 | const cachedTopic: Topic | undefined = this.topics.get(name); 231 | const topicOptions = { autoCreate: true, ...getTopicOptions }; 232 | 233 | if (cachedTopic !== undefined) { 234 | return cachedTopic; 235 | } 236 | 237 | const [topic]: GetTopicResponse = await this.client.topic(name).get(topicOptions); 238 | 239 | if (publishOptions) { 240 | topic.setPublishOptions(publishOptions); 241 | } 242 | 243 | this.topics.set(name, topic); 244 | 245 | return topic; 246 | } 247 | 248 | /** 249 | * Get or create a subscription on GooglePubSub 250 | * @tutorial https://github.com/googleapis/nodejs-pubsub/blob/master/samples/getSubscription.js 251 | * Also fill the subscription in-memory cache 252 | * @param name Name of the subscription 253 | * @param topic Topic attached to this subscription 254 | */ 255 | private async getOrCreateSubscription( 256 | name: string, 257 | topic: Topic, 258 | subOptions?: GCSubscriptionOptions, 259 | ): Promise { 260 | const options = { 261 | ...subOptions, 262 | get: { autoCreate: true, ...subOptions?.get }, 263 | }; 264 | const subscriptionName: string = options?.name ?? this.getSubscriptionName(name); 265 | const cachedSubscription: Subscription | undefined = this.subscriptions.get(subscriptionName); 266 | 267 | if (cachedSubscription !== undefined) { 268 | return cachedSubscription; 269 | } 270 | 271 | const sub: Subscription = topic.subscription(subscriptionName, options?.sub); 272 | const [exists]: ExistsResponse = await sub.exists(); 273 | 274 | /** 275 | * If autoCreate mode is disabled, check if the subscription is attached to the topic 276 | */ 277 | if (exists && sub.metadata?.topic !== undefined && sub.metadata.topic !== topic.name) { 278 | throw new Error( 279 | `[@algoan/pubsub] The topic ${topic.name} is not attached to this subscription (expects topic ${sub.metadata.topic})`, 280 | ); 281 | } 282 | 283 | if (!exists && !(options.get.autoCreate ?? false)) { 284 | /** 285 | * If autoCreate mode is disabled then do not create the subscription 286 | */ 287 | throw new Error(`[@algoan/pubsub] The subscription ${subscriptionName} is not found in topic ${topic.name}`); 288 | } 289 | 290 | const [subscription]: GetSubscriptionResponse | CreateSubscriptionResponse = exists 291 | ? await sub.get(options?.get) 292 | : await sub.create(options?.create); 293 | 294 | this.subscriptions.set(subscriptionName, subscription); 295 | 296 | return subscription; 297 | } 298 | 299 | /** 300 | * Add a topic prefix to the event if it is defined 301 | * @param event Event name emitted 302 | */ 303 | private getTopicName(event: string): string { 304 | if (this.topicsPrefix !== undefined) { 305 | return `${this.topicsPrefix}${this.topicsSeparator}${event}`; 306 | } 307 | 308 | return event; 309 | } 310 | 311 | /** 312 | * Add a topic prefix to the event if it is defined 313 | * @param event Event name emitted 314 | */ 315 | private getSubscriptionName(event: string): string { 316 | if (this.subscriptionsPrefix !== undefined) { 317 | return `${this.subscriptionsPrefix}${this.subscriptionsSeparator}${event}`; 318 | } 319 | 320 | return event; 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /src/GoogleCloudPubSub/index.ts: -------------------------------------------------------------------------------- 1 | export * from './GoogleCloudPubSub'; 2 | export * from './lib'; 3 | export * from './ExtendedMessage'; 4 | -------------------------------------------------------------------------------- /src/GoogleCloudPubSub/lib.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CreateSubscriptionOptions, 3 | GetSubscriptionOptions, 4 | GetTopicOptions, 5 | PubSub as GPubSub, 6 | Subscription, 7 | SubscriptionOptions, 8 | Topic, 9 | } from '@google-cloud/pubsub'; 10 | import { ClientConfig } from '@google-cloud/pubsub/build/src/pubsub'; 11 | import { MessageOptions, PublishOptions } from '@google-cloud/pubsub/build/src/topic'; 12 | import { pino } from 'pino'; 13 | 14 | import { PubSub } from '..'; 15 | 16 | /** 17 | * Extends Google PubSub config 18 | */ 19 | export interface GooglePubSubOptions extends ClientConfig { 20 | topicsPrefix?: string; 21 | topicsSeparator?: string; 22 | subscriptionsPrefix?: string; 23 | subscriptionsSeparator?: string; 24 | namespace?: string; 25 | environment?: string; 26 | debug?: boolean; 27 | pinoOptions?: pino.LoggerOptions; 28 | } 29 | 30 | /** 31 | * A key-map of Topic instances (for cache) 32 | */ 33 | export type TopicMap = Map; 34 | 35 | /** 36 | * A key-map of Subscription instances (for cache) 37 | */ 38 | export type SubscriptionMap = Map; 39 | 40 | /** 41 | * PubSub SDK for Google Cloud 42 | */ 43 | export type GCPubSub = PubSub; 44 | 45 | /** 46 | * Google Cloud PubSub Listen options 47 | */ 48 | export interface GCListenOptions { 49 | /** Automatic Acknowledgment */ 50 | autoAck?: boolean; 51 | /** Google PubSub subscription options */ 52 | subscriptionOptions?: GCSubscriptionOptions; 53 | /** Google PubSub topic options */ 54 | topicOptions?: GetTopicOptions; 55 | /** Publishing message options */ 56 | messageOptions?: Omit; 57 | /** Topic Publish options */ 58 | publishOptions?: PublishOptions; 59 | /** Topic name, if you want a different name than the subscription */ 60 | topicName?: string; 61 | } 62 | 63 | /** 64 | * Mix Subscriptions Options interface 65 | */ 66 | export interface GCSubscriptionOptions { 67 | /** Subscription name. Default to topic name if not provided */ 68 | name?: string; 69 | /** Options applied to the getSubscription: https://googleapis.dev/nodejs/pubsub/latest/v1.SubscriberClient.html#getSubscription */ 70 | get?: GetSubscriptionOptions; 71 | /** Options applied to the subscription: https://googleapis.dev/nodejs/pubsub/latest/Subscription.html#setOptions */ 72 | sub?: SubscriptionOptions; 73 | /** Options applied to the createSubscription */ 74 | create?: CreateSubscriptionOptions; 75 | } 76 | 77 | /** 78 | * Payload interface if a JSON parsing error occurs 79 | */ 80 | export interface ErrorPayload { 81 | code: string; 82 | err: unknown; 83 | } 84 | -------------------------------------------------------------------------------- /src/PubSub.ts: -------------------------------------------------------------------------------- 1 | import { EmittedMessage, Metadata } from './EmittedMessage'; 2 | 3 | /** 4 | * PubSub generic interface 5 | */ 6 | export interface PubSub { 7 | /** 8 | * The exposed PubSub client 9 | * If it is Google, then PSClient = PubSub 10 | * https://github.com/googleapis/nodejs-pubsub/blob/master/src/pubsub.ts#L235 11 | */ 12 | client: PSClient; 13 | /** 14 | * Exposed cached subscriptions 15 | * If it is Google, then PSSubscription = Subscription 16 | * https://github.com/googleapis/nodejs-pubsub/blob/master/src/subscription.ts#L211 17 | */ 18 | subscriptions: Map; 19 | /** 20 | * Listen to a subscription 21 | * @param event Event to listen to 22 | * @param options Message handler 23 | */ 24 | listen(event: string, options?: ListenOptions): Promise; 25 | 26 | /** 27 | * Stop listening to a subscription 28 | */ 29 | unsubscribe(event: string): Promise; 30 | 31 | /** 32 | * Emit an event 33 | * @param event Event to emit 34 | * @param data Payload to send 35 | * @param options Options related to the emit options 36 | */ 37 | emit(event: string, data: object, options?: EmitOptions): Promise; 38 | } 39 | 40 | /** 41 | * Listen options argument 42 | */ 43 | export interface ListenOptions { 44 | /** Error handler */ 45 | onError?(error: Error): void; 46 | /** Message handler */ 47 | onMessage?(extendedMessage: EmittedMessage): void; 48 | /** Options */ 49 | options?: Options; 50 | } 51 | 52 | /** 53 | * Emit options argument 54 | */ 55 | export interface EmitOptions { 56 | metadata?: Metadata; 57 | options?: Options; 58 | } 59 | -------------------------------------------------------------------------------- /src/PubSubFactory.ts: -------------------------------------------------------------------------------- 1 | import { GCPubSub, GoogleCloudPubSub, GooglePubSubOptions } from './GoogleCloudPubSub'; 2 | 3 | /** 4 | * PubSub factory class 5 | */ 6 | // tslint:disable-next-line: no-unnecessary-class 7 | export class PubSubFactory { 8 | /** 9 | * Create a pubsub instance depending of the transport 10 | * @param params PubSub parameters 11 | */ 12 | public static create(params: FactoryParameters = { transport: Transport.GOOGLE_PUBSUB }): GCPubSub { 13 | return new GoogleCloudPubSub(params.options); 14 | } 15 | } 16 | 17 | /** 18 | * Transport to use 19 | */ 20 | export enum Transport { 21 | GOOGLE_PUBSUB = 'GOOGLE_PUBSUB', 22 | } 23 | 24 | /** 25 | * Create instance parameters 26 | */ 27 | export interface FactoryParameters { 28 | transport: Transport; 29 | options?: GooglePubSubOptions; 30 | } 31 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './PubSubFactory'; 2 | export * from './PubSub'; 3 | export * from './EmittedMessage'; 4 | export * from './GoogleCloudPubSub/lib'; 5 | -------------------------------------------------------------------------------- /test/GoogleCloudPubSub.test.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-lines */ 2 | /* eslint-disable prefer-arrow/prefer-arrow-functions */ 3 | /* eslint-disable no-void */ 4 | import test, { ExecutionContext } from 'ava'; 5 | import * as sinon from 'sinon'; 6 | 7 | import { GCPubSub, PubSubFactory, Transport } from '../src'; 8 | import { ExtendedMessage } from '../src/GoogleCloudPubSub'; 9 | 10 | import { generateRandomTopicName } from './utils/tools'; 11 | import { TestUtils } from './utils/test-utils'; 12 | 13 | const Emulator = require('google-pubsub-emulator'); 14 | 15 | const projectId: string = 'algoan-test'; 16 | let ackSpy: sinon.SinonSpy; 17 | 18 | let emulator: any; 19 | 20 | test.before(async () => { 21 | emulator = new Emulator({ 22 | project: projectId, 23 | debug: process.env.EMULATOR_DEBUG === 'true', 24 | }); 25 | 26 | ackSpy = sinon.spy(ExtendedMessage.prototype, 'ack'); 27 | 28 | return emulator.start(); 29 | }); 30 | 31 | test.afterEach(() => { 32 | ackSpy.resetHistory(); 33 | }); 34 | 35 | test.after.always(async () => { 36 | return emulator.stop(); 37 | }); 38 | 39 | test('GPS001a - should properly emit and listen', async (t: ExecutionContext): Promise => { 40 | const topicName: string = generateRandomTopicName(); 41 | const pubSub: GCPubSub = PubSubFactory.create({ 42 | transport: Transport.GOOGLE_PUBSUB, 43 | options: { 44 | projectId, 45 | }, 46 | }); 47 | const testUtils = new TestUtils(pubSub, topicName, t); 48 | 49 | await testUtils.validateListenAndEmit((message) => { 50 | t.true(ackSpy.calledOnce); 51 | t.true(ackSpy.called); 52 | t.is(message.count, 0); 53 | }); 54 | }); 55 | 56 | test('GPS001b - should properly emit and listen with ordering key', async (t: ExecutionContext): Promise => { 57 | const topicName: string = generateRandomTopicName(); 58 | const orderingKey: string = 'key1'; 59 | const pubSub: GCPubSub = PubSubFactory.create({ 60 | transport: Transport.GOOGLE_PUBSUB, 61 | options: { 62 | projectId, 63 | }, 64 | }); 65 | 66 | const testUtils = new TestUtils(pubSub, topicName, t, { 67 | options: { 68 | publishOptions: { 69 | messageOrdering: true, 70 | }, 71 | messageOptions: { 72 | orderingKey, 73 | }, 74 | }, 75 | }); 76 | 77 | await testUtils.validateListenAndEmit((message) => { 78 | t.true(ackSpy.calledOnce); 79 | t.true(ackSpy.called); 80 | t.is(message.orderingKey, orderingKey); 81 | t.is(message.count, undefined); 82 | }); 83 | }); 84 | 85 | test('GPS001c - should properly emit and listen with a prefix', async (t: ExecutionContext): Promise => { 86 | const topicName: string = generateRandomTopicName(); 87 | const topicsPrefix: string = 'pref'; 88 | const pubSub: GCPubSub = PubSubFactory.create({ 89 | transport: Transport.GOOGLE_PUBSUB, 90 | options: { 91 | projectId, 92 | topicsPrefix, 93 | }, 94 | }); 95 | 96 | const testUtils = new TestUtils(pubSub, topicName, t); 97 | 98 | await testUtils.validateListenAndEmit((message) => { 99 | t.true(ackSpy.calledOnce); 100 | t.true(ackSpy.called); 101 | t.is(message.count, 0); 102 | }); 103 | }); 104 | 105 | test('GPS001d - should properly emit, but not listen to the subscription', async (t: ExecutionContext): Promise => { 106 | const topicName: string = generateRandomTopicName(); 107 | const pubSub: GCPubSub = PubSubFactory.create({ 108 | transport: Transport.GOOGLE_PUBSUB, 109 | options: { 110 | projectId, 111 | }, 112 | }); 113 | 114 | const testUtils = new TestUtils(pubSub, topicName, t); 115 | 116 | await testUtils.validateNotListeningAndEmit(topicName); 117 | 118 | t.pass('Test succeeded, because no message was received'); 119 | }); 120 | 121 | test('GPS001e - should properly emit and listen because wrong topic name to unsubscribe', async (t: ExecutionContext): Promise => { 122 | const topicName: string = generateRandomTopicName(); 123 | const pubSub: GCPubSub = PubSubFactory.create({ 124 | transport: Transport.GOOGLE_PUBSUB, 125 | options: { 126 | projectId, 127 | }, 128 | }); 129 | 130 | const testUtils = new TestUtils(pubSub, topicName, t); 131 | 132 | await testUtils.validateNotListeningAndEmit('wrong_subscription_or_topic_name', true, (message) => { 133 | t.true(ackSpy.calledOnce); 134 | t.true(ackSpy.called); 135 | t.is(message.count, 0); 136 | }); 137 | }); 138 | 139 | test('GPS001f - should properly emit, but not listen to the subscription with a prefix', async (t: ExecutionContext): Promise => { 140 | const topicName: string = generateRandomTopicName(); 141 | const pubSub: GCPubSub = PubSubFactory.create({ 142 | transport: Transport.GOOGLE_PUBSUB, 143 | options: { 144 | projectId, 145 | subscriptionsPrefix: 'my-prefix', 146 | }, 147 | }); 148 | 149 | const testUtils = new TestUtils(pubSub, topicName, t); 150 | 151 | await testUtils.validateNotListeningAndEmit(topicName); 152 | 153 | t.pass('Test succeeded, because no message was received'); 154 | }); 155 | 156 | test('GPS001g - should properly emit, but not listen to the subscription with a custom name and a prefix', async (t: ExecutionContext): Promise => { 157 | const topicName: string = generateRandomTopicName(); 158 | const customSubscriptionName = 'completely-different-name'; 159 | const pubSub: GCPubSub = PubSubFactory.create({ 160 | transport: Transport.GOOGLE_PUBSUB, 161 | options: { 162 | projectId, 163 | subscriptionsPrefix: 'my-prefix', 164 | }, 165 | }); 166 | 167 | const testUtils = new TestUtils(pubSub, topicName, t); 168 | 169 | await testUtils.validateNotListeningAndEmit(customSubscriptionName, false, undefined, { 170 | autoAck: true, 171 | subscriptionOptions: { 172 | name: customSubscriptionName, 173 | }, 174 | }); 175 | 176 | t.pass('Test succeeded, because no message was received'); 177 | }); 178 | 179 | test('GPS002 - should properly emit but the ack method is never called - no ack', async (t: ExecutionContext): Promise => { 180 | const topicName: string = generateRandomTopicName(); 181 | const pubSub: GCPubSub = PubSubFactory.create({ 182 | transport: Transport.GOOGLE_PUBSUB, 183 | options: { 184 | projectId, 185 | namespace: 'test-app', 186 | environment: 'test', 187 | }, 188 | }); 189 | 190 | const testUtils = new TestUtils(pubSub, topicName, t); 191 | 192 | await testUtils.validateListenAndEmit( 193 | (message) => { 194 | t.false(ackSpy.called); 195 | t.deepEqual(message.metadata, { 196 | namespace: 'test-app', 197 | environment: 'test', 198 | }); 199 | t.is(message.count, 0); 200 | }, 201 | { 202 | autoAck: false, 203 | subscriptionOptions: { 204 | sub: { 205 | streamingOptions: { 206 | maxStreams: 1, 207 | }, 208 | }, 209 | }, 210 | }, 211 | ); 212 | }); 213 | 214 | test('GPS002a - should properly emit and properly ack manually', async (t: ExecutionContext): Promise => { 215 | const topicName: string = generateRandomTopicName(); 216 | const pubSub: GCPubSub = PubSubFactory.create({ 217 | transport: Transport.GOOGLE_PUBSUB, 218 | options: { 219 | projectId, 220 | namespace: 'test-app', 221 | environment: 'test', 222 | }, 223 | }); 224 | 225 | const testUtils = new TestUtils(pubSub, topicName, t); 226 | 227 | await testUtils.validateListenAndEmit( 228 | (message) => { 229 | t.false(ackSpy.called); 230 | message.ack(); 231 | t.deepEqual(message.metadata, { 232 | namespace: 'test-app', 233 | environment: 'test', 234 | }); 235 | t.is(message.count, 0); 236 | }, 237 | { 238 | autoAck: false, 239 | }, 240 | ); 241 | }); 242 | 243 | test('GPS003 - should add prefix to subscription and topic', async (t: ExecutionContext): Promise => { 244 | const topicName: string = generateRandomTopicName(); 245 | const pubsub: GCPubSub = PubSubFactory.create({ 246 | transport: Transport.GOOGLE_PUBSUB, 247 | options: { 248 | projectId, 249 | topicsPrefix: 'algoan', 250 | subscriptionsPrefix: 'test-app', 251 | }, 252 | }); 253 | 254 | await pubsub.listen(topicName); 255 | 256 | const [isTopicExisting] = await pubsub.client.topic(`algoan+${topicName}`).exists(); 257 | const [isSubcriptionExisting] = await pubsub.client.subscription(`test-app%${topicName}`).exists(); 258 | 259 | t.true(isTopicExisting); 260 | t.true(isSubcriptionExisting); 261 | }); 262 | 263 | test('GPS004 - should not add prefix to subscription and topic', async (t: ExecutionContext): Promise => { 264 | const topicName: string = generateRandomTopicName(); 265 | const pubsub: GCPubSub = PubSubFactory.create({ 266 | transport: Transport.GOOGLE_PUBSUB, 267 | options: { 268 | projectId, 269 | }, 270 | }); 271 | 272 | await pubsub.listen(topicName); 273 | 274 | const [isTopicExisting] = await pubsub.client.topic(`algoan+${topicName}`).exists(); 275 | const [isSubcriptionExisting] = await pubsub.client.subscription(`test-app%${topicName}`).exists(); 276 | const [isTopicExistingWithoutPrefix] = await pubsub.client.topic(topicName).exists(); 277 | const [isSubcriptionExistingWithoutPrefix] = await pubsub.client.subscription(topicName).exists(); 278 | 279 | t.false(isTopicExisting); 280 | t.false(isSubcriptionExisting); 281 | t.true(isTopicExistingWithoutPrefix); 282 | t.true(isSubcriptionExistingWithoutPrefix); 283 | }); 284 | 285 | test('GPS005 - should add separator to subscription and topic', async (t: ExecutionContext): Promise => { 286 | const topicName: string = generateRandomTopicName(); 287 | const pubsub: GCPubSub = PubSubFactory.create({ 288 | transport: Transport.GOOGLE_PUBSUB, 289 | options: { 290 | projectId, 291 | topicsPrefix: 'algoan', 292 | subscriptionsPrefix: 'test-app', 293 | subscriptionsSeparator: '-', 294 | }, 295 | }); 296 | 297 | await pubsub.listen(topicName); 298 | 299 | const [isTopicExisting] = await pubsub.client.topic(`algoan+${topicName}`).exists(); 300 | const [isSubcriptionExisting] = await pubsub.client.subscription(`test-app-${topicName}`).exists(); 301 | 302 | t.true(isTopicExisting); 303 | t.true(isSubcriptionExisting); 304 | }); 305 | 306 | test('GPS006 - should not add separator to subscription and topic', async (t: ExecutionContext): Promise => { 307 | const topicName: string = generateRandomTopicName(); 308 | const pubsub: GCPubSub = PubSubFactory.create({ 309 | transport: Transport.GOOGLE_PUBSUB, 310 | options: { 311 | projectId, 312 | topicsPrefix: 'algoan', 313 | subscriptionsPrefix: 'test-app', 314 | }, 315 | }); 316 | 317 | await pubsub.listen(topicName); 318 | 319 | const [isTopicExisting] = await pubsub.client.topic(`algoan+${topicName}`).exists(); 320 | const [isSubcriptionExisting] = await pubsub.client.subscription(`test-app%${topicName}`).exists(); 321 | 322 | t.true(isTopicExisting); 323 | t.true(isSubcriptionExisting); 324 | }); 325 | 326 | test('GPS007 - should add separator to topic name', async (t: ExecutionContext): Promise => { 327 | const topicName: string = generateRandomTopicName(); 328 | const pubsub: GCPubSub = PubSubFactory.create({ 329 | transport: Transport.GOOGLE_PUBSUB, 330 | options: { 331 | projectId, 332 | topicsPrefix: 'algoan', 333 | topicsSeparator: '-', 334 | }, 335 | }); 336 | 337 | await pubsub.listen(topicName); 338 | 339 | const [isTopicExisting] = await pubsub.client.topic(`algoan-${topicName}`).exists(); 340 | 341 | t.true(isTopicExisting); 342 | }); 343 | 344 | test('GPS008 - should create subscription with a different name', async (t: ExecutionContext): Promise => { 345 | const topicName: string = generateRandomTopicName(); 346 | const customSubscriptionName: string = 'custom_name'; 347 | const pubsub: GCPubSub = PubSubFactory.create({ 348 | transport: Transport.GOOGLE_PUBSUB, 349 | options: { 350 | projectId, 351 | topicsPrefix: 'algoan', 352 | topicsSeparator: '-', 353 | }, 354 | }); 355 | 356 | await pubsub.listen(topicName, { 357 | options: { 358 | subscriptionOptions: { 359 | name: customSubscriptionName, 360 | }, 361 | }, 362 | }); 363 | 364 | const [isTopicExisting] = await pubsub.client.topic(`algoan-${topicName}`).exists(); 365 | const [isSubscriptionExisting] = await pubsub.client 366 | .topic(`algoan-${topicName}`) 367 | .subscription(customSubscriptionName) 368 | .exists(); 369 | 370 | t.true(isTopicExisting); 371 | t.true(isSubscriptionExisting); 372 | }); 373 | 374 | test('GPS009 - should not create a subscription or a topic', async (t: ExecutionContext): Promise => { 375 | const topicName: string = generateRandomTopicName(); 376 | const customSubscriptionName: string = 'custom_name'; 377 | const pubsub: GCPubSub = PubSubFactory.create({ 378 | transport: Transport.GOOGLE_PUBSUB, 379 | options: { 380 | projectId, 381 | topicsPrefix: 'algoan', 382 | topicsSeparator: '-', 383 | }, 384 | }); 385 | 386 | try { 387 | await pubsub.listen(topicName, { 388 | options: { 389 | subscriptionOptions: { 390 | name: customSubscriptionName, 391 | }, 392 | topicOptions: { 393 | autoCreate: false, 394 | }, 395 | }, 396 | }); 397 | 398 | t.fail('This promise is not supposed to be resolved, since the topic does not exist!'); 399 | } catch (err) { 400 | t.is((err as any)?.details, 'Topic not found'); 401 | } 402 | 403 | const [topics] = await pubsub.client.getTopics(); 404 | const [subscriptions] = await pubsub.client.getSubscriptions(); 405 | t.is( 406 | topics.find((topic) => topic.name === `projects/${projectId}/topics/${topicName}`), 407 | undefined, 408 | ); 409 | t.is( 410 | subscriptions.find((sub) => sub.name === `projects/${projectId}/subscriptions/${customSubscriptionName}`), 411 | undefined, 412 | ); 413 | }); 414 | 415 | test('GPS010 - should use another topic name', async (t: ExecutionContext): Promise => { 416 | const subscriptionName: string = generateRandomTopicName(); 417 | const topicName: string = 'generateRandomTopicName'; 418 | const pubsub: GCPubSub = PubSubFactory.create({ 419 | transport: Transport.GOOGLE_PUBSUB, 420 | options: { 421 | projectId, 422 | topicsPrefix: 'algoan', 423 | topicsSeparator: '-', 424 | }, 425 | }); 426 | 427 | await pubsub.listen(subscriptionName, { 428 | options: { 429 | topicName, 430 | }, 431 | }); 432 | 433 | const [isTopicExisting] = await pubsub.client.topic(topicName).exists(); 434 | const [isSubscriptionExisting] = await pubsub.client.topic(topicName).subscription(subscriptionName).exists(); 435 | 436 | t.true(isTopicExisting); 437 | t.true(isSubscriptionExisting); 438 | }); 439 | 440 | test('GPS011 - should throw an error because the topic is not attached to the subscription', async (t: ExecutionContext): Promise => { 441 | const subscriptionName: string = generateRandomTopicName(); 442 | const topicName: string = generateRandomTopicName(); 443 | const pubsub: GCPubSub = PubSubFactory.create({ 444 | transport: Transport.GOOGLE_PUBSUB, 445 | options: { 446 | projectId, 447 | topicsPrefix: 'algoan', 448 | topicsSeparator: '-', 449 | }, 450 | }); 451 | 452 | const [[createdTopic], [secondTopic]] = await Promise.all([ 453 | pubsub.client.createTopic(topicName), 454 | pubsub.client.createTopic('random_topic'), 455 | ]); 456 | await createdTopic.createSubscription(subscriptionName); 457 | 458 | try { 459 | await pubsub.listen(subscriptionName, { 460 | options: { 461 | topicName: secondTopic.name, 462 | topicOptions: { 463 | autoCreate: false, 464 | }, 465 | }, 466 | }); 467 | 468 | t.fail('This promise is not supposed to be resolved, since the topic does not exist!'); 469 | } catch (err) { 470 | t.is( 471 | (err as Error).message, 472 | `[@algoan/pubsub] The topic ${secondTopic.name} is not attached to this subscription (expects topic ${createdTopic.name})`, 473 | ); 474 | } 475 | }); 476 | 477 | test('GPS012 - should properly listen to the already created subscription', async (t: ExecutionContext): Promise => { 478 | const subscriptionName: string = generateRandomTopicName(); 479 | const topicName: string = generateRandomTopicName(); 480 | const pubsub: GCPubSub = PubSubFactory.create({ 481 | transport: Transport.GOOGLE_PUBSUB, 482 | options: { 483 | projectId, 484 | topicsPrefix: 'algoan', 485 | topicsSeparator: '-', 486 | }, 487 | }); 488 | 489 | const [createdTopic] = await pubsub.client.createTopic(topicName); 490 | await createdTopic.createSubscription(subscriptionName); 491 | 492 | await pubsub.listen(subscriptionName, { 493 | options: { 494 | topicName: createdTopic.name, 495 | topicOptions: { 496 | autoCreate: false, 497 | }, 498 | }, 499 | }); 500 | 501 | const [isTopicExisting] = await pubsub.client.topic(topicName).exists(); 502 | const [isSubscriptionExisting] = await pubsub.client.topic(topicName).subscription(subscriptionName).exists(); 503 | 504 | t.true(isTopicExisting); 505 | t.true(isSubscriptionExisting); 506 | }); 507 | 508 | test('GPS013 - should throw an error because the subscription is not created', async (t: ExecutionContext): Promise => { 509 | const subscriptionName: string = generateRandomTopicName(); 510 | const topicName: string = generateRandomTopicName(); 511 | const pubsub: GCPubSub = PubSubFactory.create({ 512 | transport: Transport.GOOGLE_PUBSUB, 513 | options: { 514 | projectId, 515 | topicsPrefix: 'algoan', 516 | topicsSeparator: '-', 517 | }, 518 | }); 519 | 520 | const [[createdTopic]] = await Promise.all([pubsub.client.createTopic(topicName)]); 521 | try { 522 | await pubsub.listen(subscriptionName, { 523 | options: { 524 | topicName: createdTopic.name, 525 | topicOptions: { 526 | autoCreate: false, 527 | }, 528 | subscriptionOptions: { 529 | get: { 530 | autoCreate: false, 531 | }, 532 | }, 533 | }, 534 | }); 535 | 536 | t.fail('This promise is not supposed to be resolved, since the subscription does not exist!'); 537 | } catch (err) { 538 | t.is( 539 | (err as Error).message, 540 | `[@algoan/pubsub] The subscription ${subscriptionName} is not found in topic projects/${projectId}/topics/${topicName}`, 541 | ); 542 | } 543 | }); 544 | -------------------------------------------------------------------------------- /test/utils/lib.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Fake onMessage 3 | */ 4 | export interface OnMessage { 5 | hello: string; 6 | } 7 | -------------------------------------------------------------------------------- /test/utils/test-utils.ts: -------------------------------------------------------------------------------- 1 | import { setTimeout } from 'timers/promises'; 2 | import { ExecutionContext } from 'ava'; 3 | 4 | import { EmitOptions, EmittedMessage, GCListenOptions, GCPubSub, isPayloadError } from '../../src'; 5 | import { OnMessage } from './lib'; 6 | 7 | /** 8 | * Test utils for the Google PubSub class 9 | */ 10 | export class TestUtils { 11 | /** 12 | * Create a test util instance in order to validate common scenarios 13 | * @param pubSub Pubsub client 14 | * @param topicName Topic name 15 | * @param avaExecCtx AVAJS execution context 16 | * @param topicOptions Options related to the topic 17 | */ 18 | constructor( 19 | public pubSub: GCPubSub, 20 | public topicName: string, 21 | public avaExecCtx: ExecutionContext, 22 | public topicOptions: EmitOptions = {}, 23 | ) {} 24 | 25 | /** 26 | * Emit and Listen to a message. Assert results with the Ava execution context. 27 | * @param validateListenFn Custom validation function with the emitted message as argument 28 | * @param listenOptions Options attached to the listen method 29 | */ 30 | public async validateListenAndEmit( 31 | validateListenFn: (msg: EmittedMessage) => void, 32 | listenOptions?: GCListenOptions, 33 | ) { 34 | await new Promise((resolve, reject) => { 35 | void this.pubSub.listen(this.topicName, { 36 | onMessage: this.handleMessage.bind(this, validateListenFn, resolve), 37 | onError: this.handleError.bind(this, reject), 38 | options: listenOptions, 39 | }); 40 | 41 | void this.emitAfterDelay(); 42 | }); 43 | } 44 | 45 | /** 46 | * Validate the unsubscription by rejecting the promise if a message is listened 47 | * @param subscriptionName Name of the subscription to unsubscribe to 48 | * @param shouldListen set to true if you do not want to reject the onMessage method 49 | * @param validateListenFn Custom validation function with the emitted message as argument 50 | * @param listenOptions Listen options 51 | */ 52 | public async validateNotListeningAndEmit( 53 | subscriptionName: string, 54 | shouldListen: boolean = false, 55 | validateListenFn?: (msg: EmittedMessage) => void, 56 | listenOptions?: GCListenOptions, 57 | ) { 58 | await new Promise(async (resolve, reject) => { 59 | await this.pubSub.listen(this.topicName, { 60 | onMessage: 61 | shouldListen && validateListenFn 62 | ? this.handleMessage.bind(this, validateListenFn, resolve) 63 | : this.handleUnsubscribeMessage.bind(this, subscriptionName, reject), 64 | onError: this.handleError.bind(this, reject), 65 | options: listenOptions, 66 | }); 67 | 68 | await this.pubSub.unsubscribe(subscriptionName); 69 | 70 | void this.emitAfterDelay(); 71 | 72 | await setTimeout(2000); 73 | 74 | resolve(true); 75 | }); 76 | } 77 | 78 | /** 79 | * Handle emitted messages 80 | * @param validateListenFn Custom validation function with the emitted message as argument 81 | * @param resolve Promise resolve function 82 | * @param message Emitted message 83 | */ 84 | private handleMessage( 85 | validateListenFn: (msg: EmittedMessage) => void, 86 | resolve: (val: unknown) => void, 87 | message: EmittedMessage, 88 | ): void { 89 | try { 90 | if (isPayloadError(message.payload)) { 91 | throw new Error('Error in payload'); 92 | } 93 | 94 | const payload: OnMessage = message.payload; 95 | this.validateEmittedMessageProperties(message, payload); 96 | validateListenFn(message); 97 | resolve(true); 98 | } catch (err) { 99 | throw err; 100 | } 101 | } 102 | 103 | /** 104 | * Handle errors 105 | * @param reject Promise reject function 106 | * @param error Error object 107 | */ 108 | private handleError(reject: (reason?: any) => void, error: any): void { 109 | reject(error); 110 | } 111 | 112 | /** 113 | * Handle messages after unsubscribe 114 | * @param subscriptionName Subscription name 115 | * @param reject Promise reject function 116 | */ 117 | private handleUnsubscribeMessage(subscriptionName: string, reject: (reason?: any) => void): void { 118 | reject(`Should not listen to anything, because unsubscribed from subscription ${subscriptionName}`); 119 | } 120 | 121 | /** 122 | * Validate some properties of the Emitted Message 123 | * @param message Emitted message 124 | * @param payload Non error payload of the message 125 | */ 126 | private validateEmittedMessageProperties(message: EmittedMessage, payload: OnMessage) { 127 | const { avaExecCtx } = this; 128 | avaExecCtx.deepEqual(payload, { hello: 'world' }); 129 | ['id', 'ackId', 'emittedAt', 'receivedAt', 'duration'].forEach((property) => { 130 | avaExecCtx.truthy(message[property as keyof EmittedMessage]); 131 | }); 132 | } 133 | 134 | /** 135 | * Emit an event after a certain delay 136 | * @param delay Number of ms to wait for 137 | */ 138 | private async emitAfterDelay(delay: number = 1000): Promise { 139 | await setTimeout(delay); 140 | await this.pubSub.emit(this.topicName, { hello: 'world' }, this.topicOptions); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /test/utils/tools.ts: -------------------------------------------------------------------------------- 1 | import { v4 } from 'uuid'; 2 | 3 | /** 4 | * Generate a random topic name 5 | * @returns 6 | */ 7 | export const generateRandomTopicName = (): string => `topic_${v4()}`; 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "commonjs", 5 | "lib": ["es2015", "es6", "es7"], 6 | "allowJs": false, 7 | "checkJs": false, 8 | "declaration": true, 9 | "outDir": "dist", 10 | "removeComments": true, 11 | "noEmit": false, 12 | "importHelpers": false, 13 | "isolatedModules": false, 14 | 15 | "strict": true, 16 | "noImplicitAny": true, 17 | "strictNullChecks": true, 18 | "noImplicitThis": true, 19 | "alwaysStrict": true, 20 | 21 | "noUnusedLocals": true, 22 | "noUnusedParameters": true, 23 | "noImplicitReturns": true, 24 | "noFallthroughCasesInSwitch": true, 25 | 26 | "moduleResolution": "node", 27 | "allowSyntheticDefaultImports": false, 28 | 29 | "inlineSourceMap": false, 30 | 31 | "experimentalDecorators": true, 32 | "emitDecoratorMetadata": true 33 | }, 34 | "include": [ 35 | "./src/**/*", 36 | "./test/**/*" 37 | ], 38 | "exclude": [ 39 | "node_modules" 40 | ] 41 | } 42 | --------------------------------------------------------------------------------