From 00e1511b7983d2f6b17601c0138e89e5e0a76d8a Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Thu, 16 Jun 2022 10:59:36 -0700 Subject: [PATCH 01/20] librdkafka 1.9.0 --- ci/librdkafka-defs-generator.js | 2 +- config.d.ts | 47 ++++++++++++++++++++++++++++++--- deps/librdkafka | 2 +- deps/windows-install.py | 2 +- errors.d.ts | 20 +++++++------- lib/error.js | 20 +++++++------- package.json | 4 +-- 7 files changed, 68 insertions(+), 29 deletions(-) diff --git a/ci/librdkafka-defs-generator.js b/ci/librdkafka-defs-generator.js index 2a1364a5..dc41bf9b 100644 --- a/ci/librdkafka-defs-generator.js +++ b/ci/librdkafka-defs-generator.js @@ -171,7 +171,7 @@ function updateErrorDefinitions(file) { // validate body const emptyCheck = body - .replace(/(( \/\*)|( ?\*)).*/g, '') + .replace(/((\s+\/\*)|( ?\*)).*/g, '') .replace(/ ERR_\w+: -?\d+,?\r?\n/g, '') .trim() if (emptyCheck !== '') { diff --git a/config.d.ts b/config.d.ts index e939f995..f140c799 100644 --- a/config.d.ts +++ b/config.d.ts @@ -1,4 +1,4 @@ -// ====== Generated from librdkafka 1.8.2 file CONFIGURATION.md ====== +// ====== Generated from librdkafka 1.9.0 file CONFIGURATION.md ====== // Code that generated this is a derivative work of the code from Nam Nguyen // https://gist.github.com/ntgn81/066c2c8ec5b4238f85d1e9168a04e3fb @@ -6,7 +6,7 @@ export interface GlobalConfig { /** * Indicates the builtin features for this build of librdkafka. An application can either query this value or attempt to set it with its list of required features to check for library support. * - * @default gzip, snappy, ssl, sasl, regex, lz4, sasl_gssapi, sasl_plain, sasl_scram, plugins, zstd, sasl_oauthbearer + * @default gzip, snappy, ssl, sasl, regex, lz4, sasl_gssapi, sasl_plain, sasl_scram, plugins, zstd, sasl_oauthbearer, http, oidc */ "builtin.features"?: string; @@ -177,6 +177,13 @@ export interface GlobalConfig { */ "broker.address.family"?: 'any' | 'v4' | 'v6'; + /** + * Maximum time allowed for broker connection setup (TCP connection setup as well SSL and SASL handshake). If the connection to the broker is not fully functional after this the connection will be closed and retried. + * + * @default 30000 + */ + "socket.connection.setup.timeout.ms"?: number; + /** * Close broker connections after the specified time of inactivity. Disable with 0. If this property is left at its default value some heuristics are performed to determine a suitable default value, this is currently limited to identifying brokers on Azure (see librdkafka issue #3109 for more info). * @@ -268,7 +275,7 @@ export interface GlobalConfig { "enable.random.seed"?: boolean; /** - * Log broker disconnects. It might be useful to turn this off when interacting with 0.9 brokers with an aggressive `connection.max.idle.ms` value. + * Log broker disconnects. It might be useful to turn this off when interacting with 0.9 brokers with an aggressive `connections.max.idle.ms` value. * * @default true */ @@ -544,10 +551,42 @@ export interface GlobalConfig { "enable.sasl.oauthbearer.unsecure.jwt"?: boolean; /** - * SASL/OAUTHBEARER token refresh callback (set with rd_kafka_conf_set_oauthbearer_token_refresh_cb(), triggered by rd_kafka_poll(), et.al. This callback will be triggered when it is time to refresh the client's OAUTHBEARER token. + * SASL/OAUTHBEARER token refresh callback (set with rd_kafka_conf_set_oauthbearer_token_refresh_cb(), triggered by rd_kafka_poll(), et.al. This callback will be triggered when it is time to refresh the client's OAUTHBEARER token. Also see `rd_kafka_conf_enable_sasl_queue()`. */ "oauthbearer_token_refresh_cb"?: any; + /** + * Set to "default" or "oidc" to control which login method to be used. If set to "oidc", the following properties must also be be specified: `sasl.oauthbearer.client.id`, `sasl.oauthbearer.client.secret`, and `sasl.oauthbearer.token.endpoint.url`. + * + * @default default + */ + "sasl.oauthbearer.method"?: 'default' | 'oidc'; + + /** + * Public identifier for the application. Must be unique across all clients that the authorization server handles. Only used when `sasl.oauthbearer.method` is set to "oidc". + */ + "sasl.oauthbearer.client.id"?: string; + + /** + * Client secret only known to the application and the authorization server. This should be a sufficiently random string that is not guessable. Only used when `sasl.oauthbearer.method` is set to "oidc". + */ + "sasl.oauthbearer.client.secret"?: string; + + /** + * Client use this to specify the scope of the access request to the broker. Only used when `sasl.oauthbearer.method` is set to "oidc". + */ + "sasl.oauthbearer.scope"?: string; + + /** + * Allow additional information to be provided to the broker. Comma-separated list of key=value pairs. E.g., "supportFeatureX=true,organizationId=sales-emea".Only used when `sasl.oauthbearer.method` is set to "oidc". + */ + "sasl.oauthbearer.extensions"?: string; + + /** + * OAuth/OIDC issuer token endpoint HTTP(S) URI used to retrieve token. Only used when `sasl.oauthbearer.method` is set to "oidc". + */ + "sasl.oauthbearer.token.endpoint.url"?: string; + /** * List of plugin libraries to load (; separated). The library search path is platform dependent (see dlopen(3) for Unix and LoadLibrary() for Windows). If no filename extension is specified the platform-specific extension (such as .dll or .so) will be appended automatically. */ diff --git a/deps/librdkafka b/deps/librdkafka index 063a9ae7..b47da0e4 160000 --- a/deps/librdkafka +++ b/deps/librdkafka @@ -1 +1 @@ -Subproject commit 063a9ae7a65cebdf1cc128da9815c05f91a2a996 +Subproject commit b47da0e4eec900d9cb091d2f524e957505dbb513 diff --git a/deps/windows-install.py b/deps/windows-install.py index 39acd8d4..c8254080 100644 --- a/deps/windows-install.py +++ b/deps/windows-install.py @@ -18,7 +18,7 @@ outputDir = 'librdkafka.redist' outputFile = outputDir + '.zip' dllPath = outputDir + '/runtimes/win{}-x64/native'.format(librdkafkaWinSufix) -libPath = outputDir + '/build/native/lib/win{}/x64/win{}-x64-Release/v140'.format(librdkafkaWinSufix, librdkafkaWinSufix) +libPath = outputDir + '/build/native/lib/win{}/x64/win{}-x64-Release/v142'.format(librdkafkaWinSufix, librdkafkaWinSufix) includePath = outputDir + '/build/native/include/librdkafka' # download librdkafka from nuget diff --git a/errors.d.ts b/errors.d.ts index 9040922b..800653fe 100644 --- a/errors.d.ts +++ b/errors.d.ts @@ -1,4 +1,4 @@ -// ====== Generated from librdkafka 1.8.2 file src-cpp/rdkafkacpp.h ====== +// ====== Generated from librdkafka 1.9.0 file src-cpp/rdkafkacpp.h ====== export const CODES: { ERRORS: { /* Internal errors to rdkafka: */ /** Begin internal error codes (**-200**) */ @@ -20,15 +20,15 @@ export const CODES: { ERRORS: { /** Produced message timed out (**-192**) */ ERR__MSG_TIMED_OUT: number, /** Reached the end of the topic+partition queue on - * the broker. Not really an error. - * This event is disabled by default, - * see the `enable.partition.eof` configuration property (**-191**) */ + * the broker. Not really an error. + * This event is disabled by default, + * see the `enable.partition.eof` configuration property (**-191**) */ ERR__PARTITION_EOF: number, /** Permanent: Partition does not exist in cluster (**-190**) */ ERR__UNKNOWN_PARTITION: number, /** File or filesystem error (**-189**) */ ERR__FS: number, - /** Permanent: Topic does not exist in cluster (**-188**) */ + /** Permanent: Topic does not exist in cluster (**-188**) */ ERR__UNKNOWN_TOPIC: number, /** All broker connections are down (**-187**) */ ERR__ALL_BROKERS_DOWN: number, @@ -50,9 +50,9 @@ export const CODES: { ERRORS: { ERR__UNKNOWN_GROUP: number, /** Operation in progress (**-178**) */ ERR__IN_PROGRESS: number, - /** Previous operation in progress, wait for it to finish (**-177**) */ + /** Previous operation in progress, wait for it to finish (**-177**) */ ERR__PREV_IN_PROGRESS: number, - /** This operation would interfere with an existing subscription (**-176**) */ + /** This operation would interfere with an existing subscription (**-176**) */ ERR__EXISTING_SUBSCRIPTION: number, /** Assigned partitions (rebalance_cb) (**-175**) */ ERR__ASSIGN_PARTITIONS: number, @@ -163,15 +163,15 @@ export const CODES: { ERRORS: { ERR_NETWORK_EXCEPTION: number, /** Coordinator load in progress (**14**) */ ERR_COORDINATOR_LOAD_IN_PROGRESS: number, - /** Group coordinator load in progress (**14**) */ +/** Group coordinator load in progress (**14**) */ ERR_GROUP_LOAD_IN_PROGRESS: number, /** Coordinator not available (**15**) */ ERR_COORDINATOR_NOT_AVAILABLE: number, - /** Group coordinator not available (**15**) */ +/** Group coordinator not available (**15**) */ ERR_GROUP_COORDINATOR_NOT_AVAILABLE: number, /** Not coordinator (**16**) */ ERR_NOT_COORDINATOR: number, - /** Not coordinator for group (**16**) */ +/** Not coordinator for group (**16**) */ ERR_NOT_COORDINATOR_FOR_GROUP: number, /** Invalid topic (**17**) */ ERR_TOPIC_EXCEPTION: number, diff --git a/lib/error.js b/lib/error.js index b63b8acf..535c4d07 100644 --- a/lib/error.js +++ b/lib/error.js @@ -27,7 +27,7 @@ LibrdKafkaError.wrap = errorWrap; * @enum {number} * @constant */ -// ====== Generated from librdkafka 1.8.2 file src-cpp/rdkafkacpp.h ====== +// ====== Generated from librdkafka 1.9.0 file src-cpp/rdkafkacpp.h ====== LibrdKafkaError.codes = { /* Internal errors to rdkafka: */ @@ -50,15 +50,15 @@ LibrdKafkaError.codes = { /** Produced message timed out*/ ERR__MSG_TIMED_OUT: -192, /** Reached the end of the topic+partition queue on - * the broker. Not really an error. - * This event is disabled by default, - * see the `enable.partition.eof` configuration property. */ + * the broker. Not really an error. + * This event is disabled by default, + * see the `enable.partition.eof` configuration property. */ ERR__PARTITION_EOF: -191, /** Permanent: Partition does not exist in cluster. */ ERR__UNKNOWN_PARTITION: -190, /** File or filesystem error */ ERR__FS: -189, - /** Permanent: Topic does not exist in cluster. */ + /** Permanent: Topic does not exist in cluster. */ ERR__UNKNOWN_TOPIC: -188, /** All broker connections are down. */ ERR__ALL_BROKERS_DOWN: -187, @@ -80,9 +80,9 @@ LibrdKafkaError.codes = { ERR__UNKNOWN_GROUP: -179, /** Operation in progress */ ERR__IN_PROGRESS: -178, - /** Previous operation in progress, wait for it to finish. */ + /** Previous operation in progress, wait for it to finish. */ ERR__PREV_IN_PROGRESS: -177, - /** This operation would interfere with an existing subscription */ + /** This operation would interfere with an existing subscription */ ERR__EXISTING_SUBSCRIPTION: -176, /** Assigned partitions (rebalance_cb) */ ERR__ASSIGN_PARTITIONS: -175, @@ -193,15 +193,15 @@ LibrdKafkaError.codes = { ERR_NETWORK_EXCEPTION: 13, /** Coordinator load in progress */ ERR_COORDINATOR_LOAD_IN_PROGRESS: 14, - /** Group coordinator load in progress */ +/** Group coordinator load in progress */ ERR_GROUP_LOAD_IN_PROGRESS: 14, /** Coordinator not available */ ERR_COORDINATOR_NOT_AVAILABLE: 15, - /** Group coordinator not available */ +/** Group coordinator not available */ ERR_GROUP_COORDINATOR_NOT_AVAILABLE: 15, /** Not coordinator */ ERR_NOT_COORDINATOR: 16, - /** Not coordinator for group */ +/** Not coordinator for group */ ERR_NOT_COORDINATOR_FOR_GROUP: 16, /** Invalid topic */ ERR_TOPIC_EXCEPTION: 17, diff --git a/package.json b/package.json index abcf5301..bba8a715 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "node-rdkafka", "version": "v2.13.0", "description": "Node.js bindings for librdkafka", - "librdkafka": "1.8.2", + "librdkafka": "1.9.0", "main": "lib/index.js", "scripts": { "configure": "node-gyp configure", @@ -46,4 +46,4 @@ "engines": { "node": ">=6.0.0" } -} +} \ No newline at end of file From e11ea06c8bbb6e5cc9938193dd9a04f3fc2e13fd Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Fri, 17 Jun 2022 12:50:56 -0700 Subject: [PATCH 02/20] revert new line removal --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bba8a715..7701daac 100644 --- a/package.json +++ b/package.json @@ -46,4 +46,4 @@ "engines": { "node": ">=6.0.0" } -} \ No newline at end of file +} From c5bb0dc5915689b802113022e47f3ceb803c83a8 Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Fri, 17 Jun 2022 12:53:50 -0700 Subject: [PATCH 03/20] update version in readme --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 51f66b83..3d826189 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ I am looking for *your* help to make this project even better! If you're interes The `node-rdkafka` library is a high-performance NodeJS client for [Apache Kafka](http://kafka.apache.org/) that wraps the native [librdkafka](https://github.com/edenhill/librdkafka) library. All the complexity of balancing writes across partitions and managing (possibly ever-changing) brokers should be encapsulated in the library. -__This library currently uses `librdkafka` version `1.8.2`.__ +__This library currently uses `librdkafka` version `1.9.0`.__ ## Reference Docs @@ -59,7 +59,7 @@ Using Alpine Linux? Check out the [docs](https://github.com/Blizzard/node-rdkafk ### Windows -Windows build **is not** compiled from `librdkafka` source but it is rather linked against the appropriate version of [NuGet librdkafka.redist](https://www.nuget.org/packages/librdkafka.redist/) static binary that gets downloaded from `https://globalcdn.nuget.org/packages/librdkafka.redist.1.8.2.nupkg` during installation. This download link can be changed using the environment variable `NODE_RDKAFKA_NUGET_BASE_URL` that defaults to `https://globalcdn.nuget.org/packages/` when it's no set. +Windows build **is not** compiled from `librdkafka` source but it is rather linked against the appropriate version of [NuGet librdkafka.redist](https://www.nuget.org/packages/librdkafka.redist/) static binary that gets downloaded from `https://globalcdn.nuget.org/packages/librdkafka.redist.1.9.0.nupkg` during installation. This download link can be changed using the environment variable `NODE_RDKAFKA_NUGET_BASE_URL` that defaults to `https://globalcdn.nuget.org/packages/` when it's no set. Requirements: * [node-gyp for Windows](https://github.com/nodejs/node-gyp#on-windows) (the easies way to get it: `npm install --global --production windows-build-tools`, if your node version is 6.x or below, pleasse use `npm install --global --production windows-build-tools@3.1.0`) @@ -96,7 +96,7 @@ var Kafka = require('node-rdkafka'); ## Configuration -You can pass many configuration options to `librdkafka`. A full list can be found in `librdkafka`'s [Configuration.md](https://github.com/edenhill/librdkafka/blob/v1.8.2/CONFIGURATION.md) +You can pass many configuration options to `librdkafka`. A full list can be found in `librdkafka`'s [Configuration.md](https://github.com/edenhill/librdkafka/blob/v1.9.0/CONFIGURATION.md) Configuration keys that have the suffix `_cb` are designated as callbacks. Some of these keys are informational and you can choose to opt-in (for example, `dr_cb`). Others are callbacks designed to @@ -131,7 +131,7 @@ You can also get the version of `librdkafka` const Kafka = require('node-rdkafka'); console.log(Kafka.librdkafkaVersion); -// #=> 1.8.2 +// #=> 1.9.0 ``` ## Sending Messages @@ -144,7 +144,7 @@ var producer = new Kafka.Producer({ }); ``` -A `Producer` requires only `metadata.broker.list` (the Kafka brokers) to be created. The values in this list are separated by commas. For other configuration options, see the [Configuration.md](https://github.com/edenhill/librdkafka/blob/v1.8.2/CONFIGURATION.md) file described previously. +A `Producer` requires only `metadata.broker.list` (the Kafka brokers) to be created. The values in this list are separated by commas. For other configuration options, see the [Configuration.md](https://github.com/edenhill/librdkafka/blob/v1.9.0/CONFIGURATION.md) file described previously. The following example illustrates a list with several `librdkafka` options set. From 52eaef757af2077e459360cbdad21e36f0788316 Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Tue, 12 Jul 2022 14:42:27 -0700 Subject: [PATCH 04/20] librdkafka 1.9.1 --- README.md | 10 +++++----- config.d.ts | 2 +- deps/librdkafka | 2 +- errors.d.ts | 2 +- lib/error.js | 2 +- package.json | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 3d826189..b6f033b7 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ I am looking for *your* help to make this project even better! If you're interes The `node-rdkafka` library is a high-performance NodeJS client for [Apache Kafka](http://kafka.apache.org/) that wraps the native [librdkafka](https://github.com/edenhill/librdkafka) library. All the complexity of balancing writes across partitions and managing (possibly ever-changing) brokers should be encapsulated in the library. -__This library currently uses `librdkafka` version `1.9.0`.__ +__This library currently uses `librdkafka` version `1.9.1`.__ ## Reference Docs @@ -59,7 +59,7 @@ Using Alpine Linux? Check out the [docs](https://github.com/Blizzard/node-rdkafk ### Windows -Windows build **is not** compiled from `librdkafka` source but it is rather linked against the appropriate version of [NuGet librdkafka.redist](https://www.nuget.org/packages/librdkafka.redist/) static binary that gets downloaded from `https://globalcdn.nuget.org/packages/librdkafka.redist.1.9.0.nupkg` during installation. This download link can be changed using the environment variable `NODE_RDKAFKA_NUGET_BASE_URL` that defaults to `https://globalcdn.nuget.org/packages/` when it's no set. +Windows build **is not** compiled from `librdkafka` source but it is rather linked against the appropriate version of [NuGet librdkafka.redist](https://www.nuget.org/packages/librdkafka.redist/) static binary that gets downloaded from `https://globalcdn.nuget.org/packages/librdkafka.redist.1.9.1.nupkg` during installation. This download link can be changed using the environment variable `NODE_RDKAFKA_NUGET_BASE_URL` that defaults to `https://globalcdn.nuget.org/packages/` when it's no set. Requirements: * [node-gyp for Windows](https://github.com/nodejs/node-gyp#on-windows) (the easies way to get it: `npm install --global --production windows-build-tools`, if your node version is 6.x or below, pleasse use `npm install --global --production windows-build-tools@3.1.0`) @@ -96,7 +96,7 @@ var Kafka = require('node-rdkafka'); ## Configuration -You can pass many configuration options to `librdkafka`. A full list can be found in `librdkafka`'s [Configuration.md](https://github.com/edenhill/librdkafka/blob/v1.9.0/CONFIGURATION.md) +You can pass many configuration options to `librdkafka`. A full list can be found in `librdkafka`'s [Configuration.md](https://github.com/edenhill/librdkafka/blob/v1.9.1/CONFIGURATION.md) Configuration keys that have the suffix `_cb` are designated as callbacks. Some of these keys are informational and you can choose to opt-in (for example, `dr_cb`). Others are callbacks designed to @@ -131,7 +131,7 @@ You can also get the version of `librdkafka` const Kafka = require('node-rdkafka'); console.log(Kafka.librdkafkaVersion); -// #=> 1.9.0 +// #=> 1.9.1 ``` ## Sending Messages @@ -144,7 +144,7 @@ var producer = new Kafka.Producer({ }); ``` -A `Producer` requires only `metadata.broker.list` (the Kafka brokers) to be created. The values in this list are separated by commas. For other configuration options, see the [Configuration.md](https://github.com/edenhill/librdkafka/blob/v1.9.0/CONFIGURATION.md) file described previously. +A `Producer` requires only `metadata.broker.list` (the Kafka brokers) to be created. The values in this list are separated by commas. For other configuration options, see the [Configuration.md](https://github.com/edenhill/librdkafka/blob/v1.9.1/CONFIGURATION.md) file described previously. The following example illustrates a list with several `librdkafka` options set. diff --git a/config.d.ts b/config.d.ts index f140c799..e748e733 100644 --- a/config.d.ts +++ b/config.d.ts @@ -1,4 +1,4 @@ -// ====== Generated from librdkafka 1.9.0 file CONFIGURATION.md ====== +// ====== Generated from librdkafka 1.9.1 file CONFIGURATION.md ====== // Code that generated this is a derivative work of the code from Nam Nguyen // https://gist.github.com/ntgn81/066c2c8ec5b4238f85d1e9168a04e3fb diff --git a/deps/librdkafka b/deps/librdkafka index b47da0e4..faacc74b 160000 --- a/deps/librdkafka +++ b/deps/librdkafka @@ -1 +1 @@ -Subproject commit b47da0e4eec900d9cb091d2f524e957505dbb513 +Subproject commit faacc74bbaa82a8d89188981c60d3140bd52d795 diff --git a/errors.d.ts b/errors.d.ts index 800653fe..7b3639bb 100644 --- a/errors.d.ts +++ b/errors.d.ts @@ -1,4 +1,4 @@ -// ====== Generated from librdkafka 1.9.0 file src-cpp/rdkafkacpp.h ====== +// ====== Generated from librdkafka 1.9.1 file src-cpp/rdkafkacpp.h ====== export const CODES: { ERRORS: { /* Internal errors to rdkafka: */ /** Begin internal error codes (**-200**) */ diff --git a/lib/error.js b/lib/error.js index 535c4d07..52ea617e 100644 --- a/lib/error.js +++ b/lib/error.js @@ -27,7 +27,7 @@ LibrdKafkaError.wrap = errorWrap; * @enum {number} * @constant */ -// ====== Generated from librdkafka 1.9.0 file src-cpp/rdkafkacpp.h ====== +// ====== Generated from librdkafka 1.9.1 file src-cpp/rdkafkacpp.h ====== LibrdKafkaError.codes = { /* Internal errors to rdkafka: */ diff --git a/package.json b/package.json index 7701daac..64543afb 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "node-rdkafka", "version": "v2.13.0", "description": "Node.js bindings for librdkafka", - "librdkafka": "1.9.0", + "librdkafka": "1.9.1", "main": "lib/index.js", "scripts": { "configure": "node-gyp configure", @@ -46,4 +46,4 @@ "engines": { "node": ">=6.0.0" } -} +} \ No newline at end of file From 4a9856e8fef8f01a95cc0b9d3fbe765b980a8eff Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Thu, 28 Jul 2022 10:53:54 -0700 Subject: [PATCH 05/20] update windows-install script to copy new libcurl dll --- deps/windows-install.py | 1 + 1 file changed, 1 insertion(+) diff --git a/deps/windows-install.py b/deps/windows-install.py index c8254080..fa114b78 100644 --- a/deps/windows-install.py +++ b/deps/windows-install.py @@ -63,6 +63,7 @@ def createdir(dir): shutil.copy2(includePath + '/rdkafkacpp.h', depsIncludeDir) shutil.copy2(dllPath + '/libcrypto-1_1-x64.dll', buildReleaseDir) +shutil.copy2(dllPath + '/libcurl.dll', buildReleaseDir) shutil.copy2(dllPath + '/librdkafka.dll', buildReleaseDir) shutil.copy2(dllPath + '/librdkafkacpp.dll', buildReleaseDir) shutil.copy2(dllPath + '/libssl-1_1-x64.dll', buildReleaseDir) From 383360ed4da9cc3d4415092eb1e01d9fee8d3788 Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Tue, 2 Aug 2022 12:59:18 -0700 Subject: [PATCH 06/20] v1.9.2 --- README.md | 6 +++--- config.d.ts | 2 +- deps/librdkafka | 2 +- errors.d.ts | 2 +- lib/error.js | 2 +- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index b6f033b7..c89f97f8 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ I am looking for *your* help to make this project even better! If you're interes The `node-rdkafka` library is a high-performance NodeJS client for [Apache Kafka](http://kafka.apache.org/) that wraps the native [librdkafka](https://github.com/edenhill/librdkafka) library. All the complexity of balancing writes across partitions and managing (possibly ever-changing) brokers should be encapsulated in the library. -__This library currently uses `librdkafka` version `1.9.1`.__ +__This library currently uses `librdkafka` version `1.9.2`.__ ## Reference Docs @@ -59,7 +59,7 @@ Using Alpine Linux? Check out the [docs](https://github.com/Blizzard/node-rdkafk ### Windows -Windows build **is not** compiled from `librdkafka` source but it is rather linked against the appropriate version of [NuGet librdkafka.redist](https://www.nuget.org/packages/librdkafka.redist/) static binary that gets downloaded from `https://globalcdn.nuget.org/packages/librdkafka.redist.1.9.1.nupkg` during installation. This download link can be changed using the environment variable `NODE_RDKAFKA_NUGET_BASE_URL` that defaults to `https://globalcdn.nuget.org/packages/` when it's no set. +Windows build **is not** compiled from `librdkafka` source but it is rather linked against the appropriate version of [NuGet librdkafka.redist](https://www.nuget.org/packages/librdkafka.redist/) static binary that gets downloaded from `https://globalcdn.nuget.org/packages/librdkafka.redist.1.9.2.nupkg` during installation. This download link can be changed using the environment variable `NODE_RDKAFKA_NUGET_BASE_URL` that defaults to `https://globalcdn.nuget.org/packages/` when it's no set. Requirements: * [node-gyp for Windows](https://github.com/nodejs/node-gyp#on-windows) (the easies way to get it: `npm install --global --production windows-build-tools`, if your node version is 6.x or below, pleasse use `npm install --global --production windows-build-tools@3.1.0`) @@ -131,7 +131,7 @@ You can also get the version of `librdkafka` const Kafka = require('node-rdkafka'); console.log(Kafka.librdkafkaVersion); -// #=> 1.9.1 +// #=> 1.9.2 ``` ## Sending Messages diff --git a/config.d.ts b/config.d.ts index e748e733..b816277e 100644 --- a/config.d.ts +++ b/config.d.ts @@ -1,4 +1,4 @@ -// ====== Generated from librdkafka 1.9.1 file CONFIGURATION.md ====== +// ====== Generated from librdkafka 1.9.2 file CONFIGURATION.md ====== // Code that generated this is a derivative work of the code from Nam Nguyen // https://gist.github.com/ntgn81/066c2c8ec5b4238f85d1e9168a04e3fb diff --git a/deps/librdkafka b/deps/librdkafka index faacc74b..9b72ca3a 160000 --- a/deps/librdkafka +++ b/deps/librdkafka @@ -1 +1 @@ -Subproject commit faacc74bbaa82a8d89188981c60d3140bd52d795 +Subproject commit 9b72ca3aa6c49f8f57eea02f70aadb1453d3ba1f diff --git a/errors.d.ts b/errors.d.ts index 7b3639bb..b300dd79 100644 --- a/errors.d.ts +++ b/errors.d.ts @@ -1,4 +1,4 @@ -// ====== Generated from librdkafka 1.9.1 file src-cpp/rdkafkacpp.h ====== +// ====== Generated from librdkafka 1.9.2 file src-cpp/rdkafkacpp.h ====== export const CODES: { ERRORS: { /* Internal errors to rdkafka: */ /** Begin internal error codes (**-200**) */ diff --git a/lib/error.js b/lib/error.js index 52ea617e..27812cf5 100644 --- a/lib/error.js +++ b/lib/error.js @@ -27,7 +27,7 @@ LibrdKafkaError.wrap = errorWrap; * @enum {number} * @constant */ -// ====== Generated from librdkafka 1.9.1 file src-cpp/rdkafkacpp.h ====== +// ====== Generated from librdkafka 1.9.2 file src-cpp/rdkafkacpp.h ====== LibrdKafkaError.codes = { /* Internal errors to rdkafka: */ diff --git a/package.json b/package.json index 64543afb..30747e8f 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "node-rdkafka", "version": "v2.13.0", "description": "Node.js bindings for librdkafka", - "librdkafka": "1.9.1", + "librdkafka": "1.9.2", "main": "lib/index.js", "scripts": { "configure": "node-gyp configure", From 102578bef1348e67817a4e20460e88a074c2f697 Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Mon, 15 Aug 2022 16:25:20 -0700 Subject: [PATCH 07/20] update readme links to 1.9.2 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c89f97f8..926d5b39 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ var Kafka = require('node-rdkafka'); ## Configuration -You can pass many configuration options to `librdkafka`. A full list can be found in `librdkafka`'s [Configuration.md](https://github.com/edenhill/librdkafka/blob/v1.9.1/CONFIGURATION.md) +You can pass many configuration options to `librdkafka`. A full list can be found in `librdkafka`'s [Configuration.md](https://github.com/edenhill/librdkafka/blob/v1.9.2/CONFIGURATION.md) Configuration keys that have the suffix `_cb` are designated as callbacks. Some of these keys are informational and you can choose to opt-in (for example, `dr_cb`). Others are callbacks designed to @@ -144,7 +144,7 @@ var producer = new Kafka.Producer({ }); ``` -A `Producer` requires only `metadata.broker.list` (the Kafka brokers) to be created. The values in this list are separated by commas. For other configuration options, see the [Configuration.md](https://github.com/edenhill/librdkafka/blob/v1.9.1/CONFIGURATION.md) file described previously. +A `Producer` requires only `metadata.broker.list` (the Kafka brokers) to be created. The values in this list are separated by commas. For other configuration options, see the [Configuration.md](https://github.com/edenhill/librdkafka/blob/v1.9.2/CONFIGURATION.md) file described previously. The following example illustrates a list with several `librdkafka` options set. From a9099d4a1b4babf4cea116a609b44203c8cd4018 Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Tue, 20 Sep 2022 14:10:25 -0700 Subject: [PATCH 08/20] separate thread for consume loop --- package.json | 4 +- src/kafka-consumer.cc | 122 +++++++++++++++++++++++++++++++++++++++++- src/kafka-consumer.h | 13 +++++ 3 files changed, 135 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 30747e8f..d7d8ce12 100644 --- a/package.json +++ b/package.json @@ -36,12 +36,12 @@ "jsdoc": "^3.4.0", "jshint": "^2.10.1", "mocha": "^5.2.0", - "node-gyp": "^8.4.1", + "node-gyp": "^9.1.0", "toolkit-jsdoc": "^1.0.0" }, "dependencies": { "bindings": "^1.3.1", - "nan": "^2.14.0" + "nan": "^2.16.0" }, "engines": { "node": ">=6.0.0" diff --git a/src/kafka-consumer.cc b/src/kafka-consumer.cc index 29911403..ffcf6f4a 100644 --- a/src/kafka-consumer.cc +++ b/src/kafka-consumer.cc @@ -7,6 +7,8 @@ * of the MIT license. See the LICENSE.txt file for details. */ +#define THREADED_CONSUME + #include #include @@ -32,11 +34,22 @@ KafkaConsumer::KafkaConsumer(Conf* gconfig, Conf* tconfig): std::string errstr; m_gconfig->set("default_topic_conf", m_tconfig, errstr); + + consume_callback = nullptr; + + uv_mutex_init(&consume_messages_lock); } KafkaConsumer::~KafkaConsumer() { // We only want to run this if it hasn't been run already Disconnect(); + + if (consume_callback != nullptr) { + consume_callback->Reset(); + consume_callback = nullptr; + } + + uv_mutex_destroy(&consume_messages_lock); } Baton KafkaConsumer::Connect() { @@ -474,6 +487,90 @@ std::string KafkaConsumer::Name() { return std::string(m_client->name()); } +void KafkaConsumer::ConsumeLoop(void *arg) { + KafkaConsumer* consumer = (KafkaConsumer*)arg; + + bool looping = true; + + while (consumer->IsConnected() && looping) { + Baton b = consumer->Consume(consumer->consume_timeout_ms); + RdKafka::ErrorCode ec = b.err(); + if (ec == RdKafka::ERR_NO_ERROR) { + RdKafka::Message *message = b.data(); + switch (message->err()) { + + case RdKafka::ERR_NO_ERROR: { + // message is deleted after it's passed to the main event loop + + scoped_mutex_lock lock(consumer->consume_messages_lock); + consumer->consume_messages.push_back(message); + + uv_async_send(&consumer->consume_async); + + break; + } + + case RdKafka::ERR__TIMED_OUT: + case RdKafka::ERR__TIMED_OUT_QUEUE: + case RdKafka::ERR__PARTITION_EOF: { + delete message; + + // no need to wait here because the next Consume() call will handle the timeout? + + break; + } + + default: + // Unknown error. We need to break out of this + // SetErrorBaton(b); + // TODO: pass error along? + + looping = false; + break; + } + + } else if (ec == RdKafka::ERR_UNKNOWN_TOPIC_OR_PART || ec == RdKafka::ERR_TOPIC_AUTHORIZATION_FAILED) { + // bus.SendWarning(ec); + + } else { + // Unknown error. We need to break out of this + // SetErrorBaton(b); + looping = false; + } + } + + uv_close((uv_handle_t*) &consumer->consume_async, NULL); +} + +void KafkaConsumer::ConsumeMessage(uv_async_t* handle) { + Nan::HandleScope scope; + + KafkaConsumer* consumer = (KafkaConsumer*)handle->data; + + std::vector message_queue; + // std::vector warning_queue; + + { + scoped_mutex_lock lock(consumer->consume_messages_lock); + // Copy the vector and empty it + consumer->consume_messages.swap(message_queue); + // m_asyncwarning.swap(warning_queue); + } + + for (unsigned int i = 0; i < message_queue.size(); i++) { + RdKafka::Message* message = message_queue[i]; + v8::Local argv[] = { + Nan::Null(), + Conversion::Message::ToV8Object(message), + Nan::Null(), + }; + + delete message; + + consumer->consume_callback->Call(3, argv); + } +} + Nan::Persistent KafkaConsumer::constructor; void KafkaConsumer::Init(v8::Local exports) { @@ -576,6 +673,12 @@ void KafkaConsumer::New(const Nan::FunctionCallbackInfo& info) { KafkaConsumer* consumer = new KafkaConsumer(gconfig, tconfig); + v8::Local context = v8::Local::Cast(info[0]); + consumer->consume_context.Reset(context); + + uv_async_init(Nan::GetCurrentEventLoop(), &consumer->consume_async, ConsumeMessage); + consumer->consume_async.data = consumer; + // Wrap it consumer->Wrap(info.This()); @@ -1086,8 +1189,22 @@ NAN_METHOD(KafkaConsumer::NodeConsumeLoop) { v8::Local cb = info[2].As(); Nan::Callback *callback = new Nan::Callback(cb); + +#ifdef THREADED_CONSUME + if (consumer->consume_callback != nullptr) { + return Nan::ThrowError("Consume was already called once"); + } + + consumer->consume_timeout_ms = timeout_ms; + consumer->consume_retry_read_ms = retry_read_ms; + consumer->consume_callback = callback; + + uv_thread_create(&consumer->consume_event_loop, KafkaConsumer::ConsumeLoop, (void*)consumer); + +#else Nan::AsyncQueueWorker( new Workers::KafkaConsumerConsumeLoop(callback, consumer, timeout_ms, retry_read_ms)); +#endif info.GetReturnValue().Set(Nan::Null()); } @@ -1141,8 +1258,9 @@ NAN_METHOD(KafkaConsumer::NodeConsume) { v8::Local cb = info[1].As(); Nan::Callback *callback = new Nan::Callback(cb); - Nan::AsyncQueueWorker( - new Workers::KafkaConsumerConsume(callback, consumer, timeout_ms)); + + Nan::AsyncQueueWorker( + new Workers::KafkaConsumerConsume(callback, consumer, timeout_ms)); } info.GetReturnValue().Set(Nan::Null()); diff --git a/src/kafka-consumer.h b/src/kafka-consumer.h index 57b92904..c62bcbc6 100644 --- a/src/kafka-consumer.h +++ b/src/kafka-consumer.h @@ -84,6 +84,9 @@ class KafkaConsumer : public Connection { void ActivateDispatchers(); void DeactivateDispatchers(); + static void ConsumeLoop(void *arg); + static void ConsumeMessage(uv_async_t* handle); + protected: static Nan::Persistent constructor; static void New(const Nan::FunctionCallbackInfo& info); @@ -98,6 +101,16 @@ class KafkaConsumer : public Connection { int m_partition_cnt; bool m_is_subscribed = false; + uv_thread_t consume_event_loop; + int consume_timeout_ms; + int consume_retry_read_ms; + Nan::Callback *consume_callback; + Nan::Persistent consume_context; + + uv_async_t consume_async; + uv_mutex_t consume_messages_lock; + std::vector consume_messages; + // Node methods static NAN_METHOD(NodeConnect); static NAN_METHOD(NodeSubscribe); From a1b450516fce55a32908e89741ff13f61af17716 Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Wed, 21 Sep 2022 12:48:05 -0700 Subject: [PATCH 09/20] change where consume_async is opened / closed --- src/kafka-consumer.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/kafka-consumer.cc b/src/kafka-consumer.cc index ffcf6f4a..aa4e0f24 100644 --- a/src/kafka-consumer.cc +++ b/src/kafka-consumer.cc @@ -37,6 +37,9 @@ KafkaConsumer::KafkaConsumer(Conf* gconfig, Conf* tconfig): consume_callback = nullptr; + uv_async_init(Nan::GetCurrentEventLoop(), &consume_async, ConsumeMessage); + consume_async.data = this; + uv_mutex_init(&consume_messages_lock); } @@ -50,6 +53,8 @@ KafkaConsumer::~KafkaConsumer() { } uv_mutex_destroy(&consume_messages_lock); + + uv_close((uv_handle_t*) &consume_async, NULL); } Baton KafkaConsumer::Connect() { @@ -497,7 +502,7 @@ void KafkaConsumer::ConsumeLoop(void *arg) { RdKafka::ErrorCode ec = b.err(); if (ec == RdKafka::ERR_NO_ERROR) { RdKafka::Message *message = b.data(); - switch (message->err()) { + switch (message->err()) { case RdKafka::ERR_NO_ERROR: { // message is deleted after it's passed to the main event loop @@ -538,8 +543,6 @@ void KafkaConsumer::ConsumeLoop(void *arg) { looping = false; } } - - uv_close((uv_handle_t*) &consumer->consume_async, NULL); } void KafkaConsumer::ConsumeMessage(uv_async_t* handle) { @@ -676,9 +679,6 @@ void KafkaConsumer::New(const Nan::FunctionCallbackInfo& info) { v8::Local context = v8::Local::Cast(info[0]); consumer->consume_context.Reset(context); - uv_async_init(Nan::GetCurrentEventLoop(), &consumer->consume_async, ConsumeMessage); - consumer->consume_async.data = consumer; - // Wrap it consumer->Wrap(info.This()); From 6fc412d0585e31699997a0e75c7312e775203a50 Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Thu, 22 Sep 2022 14:11:21 -0700 Subject: [PATCH 10/20] reuse existing classes --- lib/kafka-consumer.js | 2 +- src/kafka-consumer.cc | 136 ++++++------------------------------------ src/kafka-consumer.h | 13 +--- src/workers.cc | 49 +++++++-------- src/workers.h | 6 +- 5 files changed, 48 insertions(+), 158 deletions(-) diff --git a/lib/kafka-consumer.js b/lib/kafka-consumer.js index be3b6390..c479240f 100644 --- a/lib/kafka-consumer.js +++ b/lib/kafka-consumer.js @@ -385,7 +385,7 @@ KafkaConsumer.prototype.unsubscribe = function() { * is fetched. */ KafkaConsumer.prototype.consume = function(number, cb) { - var timeoutMs = this._consumeTimeout || DEFAULT_CONSUME_TIME_OUT; + var timeoutMs = this._consumeTimeout !== undefined ? this._consumeTimeout : DEFAULT_CONSUME_TIME_OUT; var self = this; if ((number && typeof number === 'number') || (number && cb)) { diff --git a/src/kafka-consumer.cc b/src/kafka-consumer.cc index aa4e0f24..5458391e 100644 --- a/src/kafka-consumer.cc +++ b/src/kafka-consumer.cc @@ -7,8 +7,6 @@ * of the MIT license. See the LICENSE.txt file for details. */ -#define THREADED_CONSUME - #include #include @@ -35,26 +33,12 @@ KafkaConsumer::KafkaConsumer(Conf* gconfig, Conf* tconfig): m_gconfig->set("default_topic_conf", m_tconfig, errstr); - consume_callback = nullptr; - - uv_async_init(Nan::GetCurrentEventLoop(), &consume_async, ConsumeMessage); - consume_async.data = this; - - uv_mutex_init(&consume_messages_lock); + m_consume_loop = nullptr; } KafkaConsumer::~KafkaConsumer() { // We only want to run this if it hasn't been run already Disconnect(); - - if (consume_callback != nullptr) { - consume_callback->Reset(); - consume_callback = nullptr; - } - - uv_mutex_destroy(&consume_messages_lock); - - uv_close((uv_handle_t*) &consume_async, NULL); } Baton KafkaConsumer::Connect() { @@ -108,6 +92,11 @@ Baton KafkaConsumer::Disconnect() { } } + if (m_consume_loop != nullptr) { + delete m_consume_loop; + m_consume_loop = nullptr; + } + m_is_closing = false; return Baton(err); @@ -492,88 +481,6 @@ std::string KafkaConsumer::Name() { return std::string(m_client->name()); } -void KafkaConsumer::ConsumeLoop(void *arg) { - KafkaConsumer* consumer = (KafkaConsumer*)arg; - - bool looping = true; - - while (consumer->IsConnected() && looping) { - Baton b = consumer->Consume(consumer->consume_timeout_ms); - RdKafka::ErrorCode ec = b.err(); - if (ec == RdKafka::ERR_NO_ERROR) { - RdKafka::Message *message = b.data(); - switch (message->err()) { - - case RdKafka::ERR_NO_ERROR: { - // message is deleted after it's passed to the main event loop - - scoped_mutex_lock lock(consumer->consume_messages_lock); - consumer->consume_messages.push_back(message); - - uv_async_send(&consumer->consume_async); - - break; - } - - case RdKafka::ERR__TIMED_OUT: - case RdKafka::ERR__TIMED_OUT_QUEUE: - case RdKafka::ERR__PARTITION_EOF: { - delete message; - - // no need to wait here because the next Consume() call will handle the timeout? - - break; - } - - default: - // Unknown error. We need to break out of this - // SetErrorBaton(b); - // TODO: pass error along? - - looping = false; - break; - } - - } else if (ec == RdKafka::ERR_UNKNOWN_TOPIC_OR_PART || ec == RdKafka::ERR_TOPIC_AUTHORIZATION_FAILED) { - // bus.SendWarning(ec); - - } else { - // Unknown error. We need to break out of this - // SetErrorBaton(b); - looping = false; - } - } -} - -void KafkaConsumer::ConsumeMessage(uv_async_t* handle) { - Nan::HandleScope scope; - - KafkaConsumer* consumer = (KafkaConsumer*)handle->data; - - std::vector message_queue; - // std::vector warning_queue; - - { - scoped_mutex_lock lock(consumer->consume_messages_lock); - // Copy the vector and empty it - consumer->consume_messages.swap(message_queue); - // m_asyncwarning.swap(warning_queue); - } - - for (unsigned int i = 0; i < message_queue.size(); i++) { - RdKafka::Message* message = message_queue[i]; - v8::Local argv[] = { - Nan::Null(), - Conversion::Message::ToV8Object(message), - Nan::Null(), - }; - - delete message; - - consumer->consume_callback->Call(3, argv); - } -} - Nan::Persistent KafkaConsumer::constructor; void KafkaConsumer::Init(v8::Local exports) { @@ -676,9 +583,6 @@ void KafkaConsumer::New(const Nan::FunctionCallbackInfo& info) { KafkaConsumer* consumer = new KafkaConsumer(gconfig, tconfig); - v8::Local context = v8::Local::Cast(info[0]); - consumer->consume_context.Reset(context); - // Wrap it consumer->Wrap(info.This()); @@ -1186,25 +1090,19 @@ NAN_METHOD(KafkaConsumer::NodeConsumeLoop) { KafkaConsumer* consumer = ObjectWrap::Unwrap(info.This()); - v8::Local cb = info[2].As(); - - Nan::Callback *callback = new Nan::Callback(cb); + if (consumer->m_consume_loop != nullptr) { + return Nan::ThrowError("Consume was already called"); + } -#ifdef THREADED_CONSUME - if (consumer->consume_callback != nullptr) { - return Nan::ThrowError("Consume was already called once"); + if (!consumer->IsConnected()) { + return Nan::ThrowError("Connect before calling consume"); } - consumer->consume_timeout_ms = timeout_ms; - consumer->consume_retry_read_ms = retry_read_ms; - consumer->consume_callback = callback; + v8::Local cb = info[2].As(); - uv_thread_create(&consumer->consume_event_loop, KafkaConsumer::ConsumeLoop, (void*)consumer); - -#else - Nan::AsyncQueueWorker( - new Workers::KafkaConsumerConsumeLoop(callback, consumer, timeout_ms, retry_read_ms)); -#endif + Nan::Callback *callback = new Nan::Callback(cb); + + consumer->m_consume_loop = new Workers::KafkaConsumerConsumeLoop(callback, consumer, timeout_ms, retry_read_ms); info.GetReturnValue().Set(Nan::Null()); } @@ -1259,8 +1157,8 @@ NAN_METHOD(KafkaConsumer::NodeConsume) { v8::Local cb = info[1].As(); Nan::Callback *callback = new Nan::Callback(cb); - Nan::AsyncQueueWorker( - new Workers::KafkaConsumerConsume(callback, consumer, timeout_ms)); + Nan::AsyncQueueWorker( + new Workers::KafkaConsumerConsume(callback, consumer, timeout_ms)); } info.GetReturnValue().Set(Nan::Null()); diff --git a/src/kafka-consumer.h b/src/kafka-consumer.h index c62bcbc6..de8f2181 100644 --- a/src/kafka-consumer.h +++ b/src/kafka-consumer.h @@ -84,9 +84,6 @@ class KafkaConsumer : public Connection { void ActivateDispatchers(); void DeactivateDispatchers(); - static void ConsumeLoop(void *arg); - static void ConsumeMessage(uv_async_t* handle); - protected: static Nan::Persistent constructor; static void New(const Nan::FunctionCallbackInfo& info); @@ -101,15 +98,7 @@ class KafkaConsumer : public Connection { int m_partition_cnt; bool m_is_subscribed = false; - uv_thread_t consume_event_loop; - int consume_timeout_ms; - int consume_retry_read_ms; - Nan::Callback *consume_callback; - Nan::Persistent consume_context; - - uv_async_t consume_async; - uv_mutex_t consume_messages_lock; - std::vector consume_messages; + void* m_consume_loop; // Node methods static NAN_METHOD(NodeConnect); diff --git a/src/workers.cc b/src/workers.cc index 89306366..68fb0409 100644 --- a/src/workers.cc +++ b/src/workers.cc @@ -664,51 +664,53 @@ KafkaConsumerConsumeLoop::KafkaConsumerConsumeLoop(Nan::Callback *callback, MessageWorker(callback), consumer(consumer), m_timeout_ms(timeout_ms), - m_timeout_sleep_delay_ms(timeout_sleep_delay_ms), - m_rand_seed(time(NULL)) {} + m_timeout_sleep_delay_ms(timeout_sleep_delay_ms) { + uv_thread_create(&thread_event_loop, KafkaConsumerConsumeLoop::ConsumeLoop, (void*)this); +} KafkaConsumerConsumeLoop::~KafkaConsumerConsumeLoop() {} void KafkaConsumerConsumeLoop::Execute(const ExecutionMessageBus& bus) { + // ConsumeLoop is used instead +} + +void KafkaConsumerConsumeLoop::ConsumeLoop(void *arg) { + KafkaConsumerConsumeLoop* consumerLoop = (KafkaConsumerConsumeLoop*)arg; + ExecutionMessageBus bus(consumerLoop); + KafkaConsumer* consumer = consumerLoop->consumer; + // Do one check here before we move forward bool looping = true; while (consumer->IsConnected() && looping) { - Baton b = consumer->Consume(m_timeout_ms); + Baton b = consumer->Consume(consumerLoop->m_timeout_ms); RdKafka::ErrorCode ec = b.err(); if (ec == RdKafka::ERR_NO_ERROR) { RdKafka::Message *message = b.data(); switch (message->err()) { case RdKafka::ERR__PARTITION_EOF: bus.Send(message); - // EOF means there are no more messages to read. - // We should wait a little bit for more messages to come in - // when in consume loop mode - // Randomise the wait time to avoid contention on different - // slow topics - #ifndef _WIN32 - usleep(static_cast(rand_r(&m_rand_seed) * 1000 * 1000 / RAND_MAX)); - #else - _sleep(1000); - #endif break; + case RdKafka::ERR__TIMED_OUT: case RdKafka::ERR__TIMED_OUT_QUEUE: delete message; - // If it is timed out this could just mean there were no - // new messages fetched quickly enough. This isn't really - // an error that should kill us. - #ifndef _WIN32 - usleep(m_timeout_sleep_delay_ms*1000); - #else - _sleep(m_timeout_sleep_delay_ms); - #endif + if (consumerLoop->m_timeout_sleep_delay_ms > 0) { + // If it is timed out this could just mean there were no + // new messages fetched quickly enough. This isn't really + // an error that should kill us. + #ifndef _WIN32 + usleep(consumerLoop->m_timeout_sleep_delay_ms*1000); + #else + _sleep(consumerLoop->m_timeout_sleep_delay_ms); + #endif + } break; case RdKafka::ERR_NO_ERROR: bus.Send(message); break; default: // Unknown error. We need to break out of this - SetErrorBaton(b); + consumerLoop->SetErrorBaton(b); looping = false; break; } @@ -716,7 +718,7 @@ void KafkaConsumerConsumeLoop::Execute(const ExecutionMessageBus& bus) { bus.SendWarning(ec); } else { // Unknown error. We need to break out of this - SetErrorBaton(b); + consumerLoop->SetErrorBaton(b); looping = false; } } @@ -770,7 +772,6 @@ void KafkaConsumerConsumeLoop::HandleOKCallback() { void KafkaConsumerConsumeLoop::HandleErrorCallback() { Nan::HandleScope scope; - const unsigned int argc = 1; v8::Local argv[argc] = { Nan::Error(ErrorMessage()) }; diff --git a/src/workers.h b/src/workers.h index 62a9cca0..dcea2477 100644 --- a/src/workers.h +++ b/src/workers.h @@ -121,8 +121,8 @@ class MessageWorker : public ErrorAwareWorker { void SendWarning(RdKafka::ErrorCode c) const { that_->ProduceWarning_(c); } - private: explicit ExecutionMessageBus(MessageWorker* that) : that_(that) {} + private: MessageWorker* const that_; }; @@ -372,12 +372,14 @@ class KafkaConsumerConsumeLoop : public MessageWorker { NodeKafka::KafkaConsumer*, const int &, const int &); ~KafkaConsumerConsumeLoop(); + static void ConsumeLoop(void *arg); void Execute(const ExecutionMessageBus&); void HandleOKCallback(); void HandleErrorCallback(); void HandleMessageCallback(RdKafka::Message*, RdKafka::ErrorCode); private: - NodeKafka::KafkaConsumer * consumer; + uv_thread_t thread_event_loop; + NodeKafka::KafkaConsumer* consumer; const int m_timeout_ms; unsigned int m_rand_seed; const int m_timeout_sleep_delay_ms; From 3e5732d67b708a1859a0efe57feb14e4ee90facf Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Thu, 22 Sep 2022 14:12:24 -0700 Subject: [PATCH 11/20] remove whitespace change --- src/kafka-consumer.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/kafka-consumer.cc b/src/kafka-consumer.cc index 5458391e..45bf1853 100644 --- a/src/kafka-consumer.cc +++ b/src/kafka-consumer.cc @@ -1156,7 +1156,6 @@ NAN_METHOD(KafkaConsumer::NodeConsume) { v8::Local cb = info[1].As(); Nan::Callback *callback = new Nan::Callback(cb); - Nan::AsyncQueueWorker( new Workers::KafkaConsumerConsume(callback, consumer, timeout_ms)); } From fcd0e106b9e9fa96e04bd23b0a562b62bd333ec5 Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Thu, 22 Sep 2022 14:13:48 -0700 Subject: [PATCH 12/20] update error --- src/kafka-consumer.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kafka-consumer.cc b/src/kafka-consumer.cc index 45bf1853..8346a9ce 100644 --- a/src/kafka-consumer.cc +++ b/src/kafka-consumer.cc @@ -1095,7 +1095,7 @@ NAN_METHOD(KafkaConsumer::NodeConsumeLoop) { } if (!consumer->IsConnected()) { - return Nan::ThrowError("Connect before calling consume"); + return Nan::ThrowError("Connect must be called before consume"); } v8::Local cb = info[2].As(); From 74cf21536b410703a827b8d45427510e17928d0f Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Thu, 22 Sep 2022 14:17:22 -0700 Subject: [PATCH 13/20] rename variable for clarify --- src/kafka-consumer.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/kafka-consumer.cc b/src/kafka-consumer.cc index 8346a9ce..0c4c07c2 100644 --- a/src/kafka-consumer.cc +++ b/src/kafka-consumer.cc @@ -1078,14 +1078,15 @@ NAN_METHOD(KafkaConsumer::NodeConsumeLoop) { } else { timeout_ms = static_cast(maybeTimeout.FromJust()); } - int retry_read_ms; + + int timeout_sleep_delay_ms; Nan::Maybe maybeSleep = Nan::To(info[1].As()); if (maybeSleep.IsNothing()) { - retry_read_ms = 500; + timeout_sleep_delay_ms = 500; } else { - retry_read_ms = static_cast(maybeSleep.FromJust()); + timeout_sleep_delay_ms = static_cast(maybeSleep.FromJust()); } KafkaConsumer* consumer = ObjectWrap::Unwrap(info.This()); @@ -1102,7 +1103,7 @@ NAN_METHOD(KafkaConsumer::NodeConsumeLoop) { Nan::Callback *callback = new Nan::Callback(cb); - consumer->m_consume_loop = new Workers::KafkaConsumerConsumeLoop(callback, consumer, timeout_ms, retry_read_ms); + consumer->m_consume_loop = new Workers::KafkaConsumerConsumeLoop(callback, consumer, timeout_ms, timeout_sleep_delay_ms); info.GetReturnValue().Set(Nan::Null()); } From 690607cbbe417f0c5edd93947f1e0acddea1b663 Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Thu, 29 Sep 2022 17:09:47 -0700 Subject: [PATCH 14/20] add semver dependency for update-version.js --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index d7d8ce12..ce4e54b4 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "jshint": "^2.10.1", "mocha": "^5.2.0", "node-gyp": "^9.1.0", + "semver": "7.3.7", "toolkit-jsdoc": "^1.0.0" }, "dependencies": { From a47aa80018d7735a387e93eed2dbb5cfc341cf69 Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Thu, 29 Sep 2022 17:14:35 -0700 Subject: [PATCH 15/20] test dependencies --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ce4e54b4..b2d3412c 100644 --- a/package.json +++ b/package.json @@ -37,12 +37,12 @@ "jshint": "^2.10.1", "mocha": "^5.2.0", "node-gyp": "^9.1.0", - "semver": "7.3.7", "toolkit-jsdoc": "^1.0.0" }, "dependencies": { "bindings": "^1.3.1", - "nan": "^2.16.0" + "nan": "^2.16.0", + "semver": "^7.3.7" }, "engines": { "node": ">=6.0.0" From e7ec97c99bb9481148f1e14d758a2787b8f58379 Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Thu, 29 Sep 2022 17:25:27 -0700 Subject: [PATCH 16/20] test --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b2d3412c..8c7a0fff 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "test": "make test", "test:e2e": "make e2e", "install": "node-gyp rebuild", - "prepack": "node ./ci/prepublish.js" + "prepack": "npm install && node ./ci/prepublish.js" }, "keywords": [ "kafka", From c8027f43e40ff1d904d2961631181d5f8c307850 Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Thu, 29 Sep 2022 17:33:08 -0700 Subject: [PATCH 17/20] move semver back to dev deps --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8c7a0fff..3d265a7d 100644 --- a/package.json +++ b/package.json @@ -37,12 +37,12 @@ "jshint": "^2.10.1", "mocha": "^5.2.0", "node-gyp": "^9.1.0", + "semver": "^7.3.7", "toolkit-jsdoc": "^1.0.0" }, "dependencies": { "bindings": "^1.3.1", - "nan": "^2.16.0", - "semver": "^7.3.7" + "nan": "^2.16.0" }, "engines": { "node": ">=6.0.0" From 49daafbb8a93a15b2ece7ad1e69c3e0da0de0703 Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Mon, 17 Oct 2022 09:55:16 -0700 Subject: [PATCH 18/20] cleanup --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 11089498..ab8e5cdc 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "build": "node-gyp build", "test": "make test", "install": "node-gyp rebuild", - "prepack": "npm install && node ./ci/prepublish.js" + "prepack": "node ./ci/prepublish.js" }, "keywords": [ "kafka", From 7ee9a256d73636427e2f17ddc60277e47a528296 Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Mon, 31 Oct 2022 16:00:28 -0700 Subject: [PATCH 19/20] remove semver --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index ab8e5cdc..5d2aad53 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "jshint": "^2.10.1", "mocha": "^5.2.0", "node-gyp": "^9.1.0", - "semver": "^7.3.7", "toolkit-jsdoc": "^1.0.0" }, "dependencies": { From e7dc75de42e12974531d65d87706aa85b6b6cfe8 Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Mon, 31 Oct 2022 16:22:41 -0700 Subject: [PATCH 20/20] update shrinkwrap --- npm-shrinkwrap.json | 419 +++++++++++++++++++++++++++----------------- 1 file changed, 259 insertions(+), 160 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 0473e8f2..8a90b2c8 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -11,14 +11,14 @@ "license": "MIT", "dependencies": { "bindings": "^1.3.1", - "nan": "^2.14.0" + "nan": "^2.16.0" }, "devDependencies": { "bluebird": "^3.5.3", "jsdoc": "^3.4.0", "jshint": "^2.10.1", "mocha": "^5.2.0", - "node-gyp": "^8.4.1", + "node-gyp": "^9.1.0", "toolkit-jsdoc": "^1.0.0" }, "engines": { @@ -44,35 +44,38 @@ "dev": true }, "node_modules/@npmcli/fs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", - "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", "dev": true, "dependencies": { - "@gar/promisify": "^1.0.1", + "@gar/promisify": "^1.1.3", "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", "dev": true, "dependencies": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" }, "engines": { - "node": ">=10" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true, "engines": { - "node": ">= 6" + "node": ">= 10" } }, "node_modules/@types/linkify-it": { @@ -282,32 +285,72 @@ "dev": true }, "node_modules/cacache": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", - "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "version": "16.1.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", + "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", "dev": true, "dependencies": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", "minipass-collect": "^1.0.2", "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", "p-map": "^4.0.0", "promise-inflight": "^1.0.1", "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" }, "engines": { - "node": ">= 10" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" } }, "node_modules/catharsis": { @@ -678,12 +721,12 @@ "dev": true }, "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, "dependencies": { - "@tootallnate/once": "1", + "@tootallnate/once": "2", "agent-base": "6", "debug": "4" }, @@ -938,42 +981,39 @@ "dev": true }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.0.tgz", + "integrity": "sha512-EIRtP1GrSJny0dqb50QXRUNBxHJhcpxHC++M5tD7RYbvLLn5KVWKsbyswSSqDuU15UFi3bgTQIY8nhDMeF6aDQ==", "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/make-fetch-happen": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", - "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", "dev": true, "dependencies": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.2.0", + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", + "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", + "minipass-fetch": "^2.0.3", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.2", + "negotiator": "^0.6.3", "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.0.0", - "ssri": "^8.0.0" + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" }, "engines": { - "node": ">= 10" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/markdown-it": { @@ -1072,20 +1112,20 @@ } }, "node_modules/minipass-fetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", - "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", "dev": true, "dependencies": { - "minipass": "^3.1.0", + "minipass": "^3.1.6", "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" + "minizlib": "^2.1.2" }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" }, "optionalDependencies": { - "encoding": "^0.1.12" + "encoding": "^0.1.13" } }, "node_modules/minipass-flush": { @@ -1247,16 +1287,16 @@ } }, "node_modules/node-gyp": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", - "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.3.0.tgz", + "integrity": "sha512-A6rJWfXFz7TQNjpldJ915WFb1LnhO4lIve3ANPbWreuEoLoKlFT3sxIepPBkLhM27crW8YmN+pjlgbasH6cH/Q==", "dev": true, "dependencies": { "env-paths": "^2.2.0", "glob": "^7.1.4", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^9.1.0", - "nopt": "^5.0.0", + "make-fetch-happen": "^10.0.3", + "nopt": "^6.0.0", "npmlog": "^6.0.0", "rimraf": "^3.0.2", "semver": "^7.3.5", @@ -1267,22 +1307,22 @@ "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": ">= 10.12.0" + "node": "^12.22 || ^14.13 || >=16" } }, "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", "dev": true, "dependencies": { - "abbrev": "1" + "abbrev": "^1.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": ">=6" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/npmlog": { @@ -1439,6 +1479,18 @@ "node": ">=10" } }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -1476,9 +1528,9 @@ } }, "node_modules/socks-proxy-agent": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", - "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", "dev": true, "dependencies": { "agent-base": "^6.0.2", @@ -1513,15 +1565,15 @@ "dev": true }, "node_modules/ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", "dev": true, "dependencies": { "minipass": "^3.1.1" }, "engines": { - "node": ">= 8" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/string_decoder": { @@ -1622,21 +1674,27 @@ "dev": true }, "node_modules/unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", + "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", "dev": true, "dependencies": { - "unique-slug": "^2.0.0" + "unique-slug": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", + "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", "dev": true, "dependencies": { "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/util-deprecate": { @@ -1702,19 +1760,19 @@ "dev": true }, "@npmcli/fs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", - "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", "dev": true, "requires": { - "@gar/promisify": "^1.0.1", + "@gar/promisify": "^1.1.3", "semver": "^7.3.5" } }, "@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", "dev": true, "requires": { "mkdirp": "^1.0.4", @@ -1722,9 +1780,9 @@ } }, "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true }, "@types/linkify-it": { @@ -1906,29 +1964,62 @@ "dev": true }, "cacache": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", - "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "version": "16.1.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", + "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", "dev": true, "requires": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", "minipass-collect": "^1.0.2", "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", "p-map": "^4.0.0", "promise-inflight": "^1.0.1", "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } } }, "catharsis": { @@ -2240,12 +2331,12 @@ "dev": true }, "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, "requires": { - "@tootallnate/once": "1", + "@tootallnate/once": "2", "agent-base": "6", "debug": "4" }, @@ -2457,36 +2548,33 @@ "dev": true }, "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.0.tgz", + "integrity": "sha512-EIRtP1GrSJny0dqb50QXRUNBxHJhcpxHC++M5tD7RYbvLLn5KVWKsbyswSSqDuU15UFi3bgTQIY8nhDMeF6aDQ==", + "dev": true }, "make-fetch-happen": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", - "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", "dev": true, "requires": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.2.0", + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", + "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", + "minipass-fetch": "^2.0.3", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.2", + "negotiator": "^0.6.3", "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.0.0", - "ssri": "^8.0.0" + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" } }, "markdown-it": { @@ -2563,15 +2651,15 @@ } }, "minipass-fetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", - "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", "dev": true, "requires": { - "encoding": "^0.1.12", - "minipass": "^3.1.0", + "encoding": "^0.1.13", + "minipass": "^3.1.6", "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" + "minizlib": "^2.1.2" } }, "minipass-flush": { @@ -2694,16 +2782,16 @@ "dev": true }, "node-gyp": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", - "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.3.0.tgz", + "integrity": "sha512-A6rJWfXFz7TQNjpldJ915WFb1LnhO4lIve3ANPbWreuEoLoKlFT3sxIepPBkLhM27crW8YmN+pjlgbasH6cH/Q==", "dev": true, "requires": { "env-paths": "^2.2.0", "glob": "^7.1.4", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^9.1.0", - "nopt": "^5.0.0", + "make-fetch-happen": "^10.0.3", + "nopt": "^6.0.0", "npmlog": "^6.0.0", "rimraf": "^3.0.2", "semver": "^7.3.5", @@ -2712,12 +2800,12 @@ } }, "nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", "dev": true, "requires": { - "abbrev": "1" + "abbrev": "^1.0.0" } }, "npmlog": { @@ -2828,6 +2916,17 @@ "dev": true, "requires": { "lru-cache": "^6.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + } } }, "set-blocking": { @@ -2859,9 +2958,9 @@ } }, "socks-proxy-agent": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", - "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", "dev": true, "requires": { "agent-base": "^6.0.2", @@ -2887,9 +2986,9 @@ } }, "ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", "dev": true, "requires": { "minipass": "^3.1.1" @@ -2975,18 +3074,18 @@ "dev": true }, "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", + "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", "dev": true, "requires": { - "unique-slug": "^2.0.0" + "unique-slug": "^3.0.0" } }, "unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", + "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", "dev": true, "requires": { "imurmurhash": "^0.1.4"