Socket
Socket
Sign inDemoInstall

@temporalio/client

Package Overview
Dependencies
Maintainers
8
Versions
80
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@temporalio/client - npm Package Compare versions

Comparing version 1.9.3 to 1.10.0

1

lib/base-client.d.ts
/// <reference types="node" />
import 'abort-controller/polyfill';
import { DataConverter, LoadedDataConverter } from '@temporalio/common';

@@ -4,0 +3,0 @@ import { ConnectionLike, Metadata } from './types';

@@ -7,4 +7,2 @@ "use strict";

exports.BaseClient = exports.defaultBaseClientOptions = void 0;
// Keep this around until we drop support for Node 14.
require("abort-controller/polyfill"); // eslint-disable-line import/no-unassigned-import
const node_os_1 = __importDefault(require("node:os"));

@@ -11,0 +9,0 @@ const internal_non_workflow_1 = require("@temporalio/common/lib/internal-non-workflow");

47

lib/connection.d.ts
/// <reference types="node" />
/// <reference types="node" />
import 'abort-controller/polyfill';
import { AsyncLocalStorage } from 'node:async_hooks';

@@ -15,5 +14,6 @@ import * as grpc from '@grpc/grpc-js';

/**
* Server hostname and optional port.
* Port defaults to 7233 if address contains only host.
* The address of the Temporal server to connect to, in `hostname:port` format.
*
* Port defaults to 7233. Raw IPv6 addresses must be wrapped in square brackets (e.g. `[ipv6]:port`).
*
* @default localhost:7233

@@ -90,2 +90,3 @@ */

* Optional mapping of gRPC metadata (HTTP headers) to send with each request to the server.
* Setting the `Authorization` header is mutually exclusive with the {@link apiKey} option.
*

@@ -96,2 +97,10 @@ * In order to dynamically set metadata, use {@link Connection.withMetadata}

/**
* API key for Temporal. This becomes the "Authorization" HTTP header with "Bearer " prepended.
* This is mutually exclusive with the `Authorization` header in {@link ConnectionOptions.metadata}.
*
* You may provide a static string or a callback. Also see {@link Connection.withApiKey} or
* {@link Connection.setApiKey}
*/
apiKey?: string | (() => string);
/**
* Milliseconds to wait until establishing a connection with the server.

@@ -107,3 +116,3 @@ *

}
export type ConnectionOptionsWithDefaults = Required<Omit<ConnectionOptions, 'tls' | 'connectTimeout' | 'callCredentials'>> & {
export type ConnectionOptionsWithDefaults = Required<Omit<ConnectionOptions, 'tls' | 'connectTimeout' | 'callCredentials' | 'apiKey'>> & {
connectTimeoutMs: number;

@@ -118,2 +127,5 @@ };

staticMetadata: Metadata;
apiKeyFnRef: {
fn?: () => string;
};
}

@@ -138,2 +150,5 @@ export interface ConnectionCtorOptions {

readonly callContextStorage: AsyncLocalStorage<CallContext>;
readonly apiKeyFnRef: {
fn?: () => string;
};
}

@@ -175,2 +190,3 @@ /**

readonly callContextStorage: AsyncLocalStorage<CallContext>;
private readonly apiKeyFnRef;
protected static createCtorOptions(options?: ConnectionOptions): ConnectionCtorOptions;

@@ -200,4 +216,4 @@ /**

static connect(options?: ConnectionOptions): Promise<Connection>;
protected constructor({ options, client, workflowService, operatorService, healthService, callContextStorage, }: ConnectionCtorOptions);
protected static generateRPCImplementation({ serviceName, client, callContextStorage, interceptors, staticMetadata, }: RPCImplOptions): RPCImpl;
protected constructor({ options, client, workflowService, operatorService, healthService, callContextStorage, apiKeyFnRef, }: ConnectionCtorOptions);
protected static generateRPCImplementation({ serviceName, client, callContextStorage, interceptors, staticMetadata, apiKeyFnRef, }: RPCImplOptions): RPCImpl;
/**

@@ -243,2 +259,21 @@ * Set the deadline for any service requests executed in `fn`'s scope.

/**
* Set the apiKey for any service requests executed in `fn`'s scope (thus changing the `Authorization` header).
*
* @returns value returned from `fn`
*
* @example
*
* ```ts
* const workflowHandle = await conn.withApiKey('secret', () =>
* conn.withMetadata({ otherKey: 'set' }, () => client.start(options)))
* );
* ```
*/
withApiKey<ReturnType>(apiKey: string, fn: () => Promise<ReturnType>): Promise<ReturnType>;
/**
* Set the {@link ConnectionOptions.apiKey} for all subsequent requests. A static string or a
* callback function may be provided.
*/
setApiKey(apiKey: string | (() => string)): void;
/**
* Wait for successful connection to the server.

@@ -245,0 +280,0 @@ *

@@ -30,4 +30,2 @@ "use strict";

exports.Connection = exports.LOCAL_TARGET = void 0;
// Keep this around until we drop support for Node 14.
require("abort-controller/polyfill"); // eslint-disable-line import/no-unassigned-import
const node_async_hooks_1 = require("node:async_hooks");

@@ -63,10 +61,16 @@ const grpc = __importStar(require("@grpc/grpc-js"));

* - Add default port to address if port not specified
* - Set `Authorization` header based on {@link ConnectionOptions.apiKey}
*/
function normalizeGRPCConfig(options) {
const { tls: tlsFromConfig, credentials, callCredentials, ...rest } = options || {};
if (rest.apiKey) {
if (rest.metadata?.['Authorization']) {
throw new TypeError('Both `apiKey` option and `Authorization` header were provided, but only one makes sense to use at a time.');
}
if (credentials !== undefined) {
throw new TypeError('Both `apiKey` and `credentials` ConnectionOptions were provided, but only one makes sense to use at a time');
}
}
if (rest.address) {
// eslint-disable-next-line prefer-const
let [host, port] = rest.address.split(':', 2);
port = port || '7233';
rest.address = `${host}:${port}`;
rest.address = (0, internal_non_workflow_1.normalizeTemporalGrpcEndpointAddress)(rest.address);
}

@@ -107,7 +111,17 @@ const tls = (0, internal_non_workflow_1.normalizeTlsConfig)(tlsFromConfig);

static createCtorOptions(options) {
var _a, _b;
const optionsWithDefaults = addDefaults(normalizeGRPCConfig(options));
const normalizedOptions = normalizeGRPCConfig(options);
const apiKeyFnRef = {};
if (normalizedOptions.apiKey) {
if (typeof normalizedOptions.apiKey === 'string') {
const apiKey = normalizedOptions.apiKey;
apiKeyFnRef.fn = () => apiKey;
}
else {
apiKeyFnRef.fn = normalizedOptions.apiKey;
}
}
const optionsWithDefaults = addDefaults(normalizedOptions);
// Allow overriding this
(_a = optionsWithDefaults.metadata)['client-name'] ?? (_a['client-name'] = 'temporal-typescript');
(_b = optionsWithDefaults.metadata)['client-version'] ?? (_b['client-version'] = pkg_1.default.version);
optionsWithDefaults.metadata['client-name'] ??= 'temporal-typescript';
optionsWithDefaults.metadata['client-version'] ??= pkg_1.default.version;
const client = new this.Client(optionsWithDefaults.address, optionsWithDefaults.credentials, optionsWithDefaults.channelArgs);

@@ -121,2 +135,3 @@ const callContextStorage = new node_async_hooks_1.AsyncLocalStorage();

staticMetadata: optionsWithDefaults.metadata,
apiKeyFnRef,
});

@@ -130,2 +145,3 @@ const workflowService = types_1.WorkflowService.create(workflowRpcImpl, false, false);

staticMetadata: optionsWithDefaults.metadata,
apiKeyFnRef,
});

@@ -139,2 +155,3 @@ const operatorService = types_1.OperatorService.create(operatorRpcImpl, false, false);

staticMetadata: optionsWithDefaults.metadata,
apiKeyFnRef,
});

@@ -149,2 +166,3 @@ const healthService = types_1.HealthService.create(healthRpcImpl, false, false);

options: optionsWithDefaults,
apiKeyFnRef,
};

@@ -203,3 +221,3 @@ }

}
constructor({ options, client, workflowService, operatorService, healthService, callContextStorage, }) {
constructor({ options, client, workflowService, operatorService, healthService, callContextStorage, apiKeyFnRef, }) {
this.options = options;

@@ -211,7 +229,11 @@ this.client = client;

this.callContextStorage = callContextStorage;
this.apiKeyFnRef = apiKeyFnRef;
}
static generateRPCImplementation({ serviceName, client, callContextStorage, interceptors, staticMetadata, }) {
static generateRPCImplementation({ serviceName, client, callContextStorage, interceptors, staticMetadata, apiKeyFnRef, }) {
return (method, requestData, callback) => {
const metadataContainer = new grpc.Metadata();
const { metadata, deadline, abortSignal } = callContextStorage.getStore() ?? {};
if (apiKeyFnRef.fn) {
metadataContainer.set('Authorization', `Bearer ${apiKeyFnRef.fn()}`);
}
for (const [k, v] of Object.entries(staticMetadata)) {

@@ -278,5 +300,43 @@ metadataContainer.set(k, v);

const cc = this.callContextStorage.getStore();
return await this.callContextStorage.run({ ...cc, metadata: { ...cc?.metadata, ...metadata } }, fn);
return await this.callContextStorage.run({
...cc,
metadata: { ...cc?.metadata, ...metadata },
}, fn);
}
/**
* Set the apiKey for any service requests executed in `fn`'s scope (thus changing the `Authorization` header).
*
* @returns value returned from `fn`
*
* @example
*
* ```ts
* const workflowHandle = await conn.withApiKey('secret', () =>
* conn.withMetadata({ otherKey: 'set' }, () => client.start(options)))
* );
* ```
*/
async withApiKey(apiKey, fn) {
const cc = this.callContextStorage.getStore();
return await this.callContextStorage.run({
...cc,
metadata: { ...cc?.metadata, Authorization: `Bearer ${apiKey}` },
}, fn);
}
/**
* Set the {@link ConnectionOptions.apiKey} for all subsequent requests. A static string or a
* callback function may be provided.
*/
setApiKey(apiKey) {
if (typeof apiKey === 'string') {
if (apiKey === '') {
throw new TypeError('`apiKey` must not be an empty string');
}
this.apiKeyFnRef.fn = () => apiKey;
}
else {
this.apiKeyFnRef.fn = apiKey;
}
}
/**
* Wait for successful connection to the server.

@@ -283,0 +343,0 @@ *

@@ -25,3 +25,3 @@ import { Interceptor, StatusObject } from '@grpc/grpc-js';

*
* @default 2
* @default 1.7
*/

@@ -38,3 +38,3 @@ factor: number;

*
* @default 0.1
* @default 0.2
*/

@@ -45,3 +45,3 @@ maxJitter: number;

*
* The default is 1 second for RESOURCE_EXHAUSTED errors and 20 millis for other retryable errors.
* The default is 1 second for RESOURCE_EXHAUSTED errors and 100 millis for other retryable errors.
*/

@@ -52,3 +52,3 @@ initialIntervalMs(status: StatusObject): number;

*
* The default is 10 seconds regardless of the status.
* The default is 5 seconds regardless of the status.
*/

@@ -55,0 +55,0 @@ maxIntervalMs(status: StatusObject): number;

@@ -35,7 +35,7 @@ "use strict";

maxAttempts: maxAttempts ?? 10,
factor: factor ?? 2,
maxJitter: maxJitter ?? 0.1,
factor: factor ?? 1.7,
maxJitter: maxJitter ?? 0.2,
initialIntervalMs: initialIntervalMs ?? defaultInitialIntervalMs,
maxIntervalMs() {
return 10000;
return 5000;
},

@@ -90,3 +90,3 @@ };

}
return 20;
return 100;
}

@@ -93,0 +93,0 @@ /**

@@ -167,3 +167,3 @@ "use strict";

taskQueue: {
kind: proto_1.temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED,
kind: proto_1.temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_NORMAL,
name: action.taskQueue,

@@ -170,0 +170,0 @@ },

@@ -430,3 +430,3 @@ "use strict";

taskQueue: {
kind: proto_1.temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED,
kind: proto_1.temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_NORMAL,
name: options.taskQueue,

@@ -475,3 +475,3 @@ },

taskQueue: {
kind: proto_1.temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED,
kind: proto_1.temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_NORMAL,
name: opts.taskQueue,

@@ -478,0 +478,0 @@ },

@@ -35,3 +35,2 @@ import { CommonWorkflowOptions, SignalDefinition, WithWorkflowArgs, Workflow } from '@temporalio/common';

*
* @experimental
*/

@@ -38,0 +37,0 @@ startDelay?: Duration;

{
"name": "@temporalio/client",
"version": "1.9.3",
"version": "1.10.0",
"description": "Temporal.io SDK Client sub-package",

@@ -16,5 +16,5 @@ "main": "lib/index.js",

"dependencies": {
"@grpc/grpc-js": "~1.7.3",
"@temporalio/common": "1.9.3",
"@temporalio/proto": "1.9.3",
"@grpc/grpc-js": "^1.10.6",
"@temporalio/common": "1.10.0",
"@temporalio/proto": "1.10.0",
"abort-controller": "^3.0.0",

@@ -43,3 +43,3 @@ "long": "^5.2.3",

],
"gitHead": "e7e46639c1ba23b86f367a051fb54d736c5f21ce"
"gitHead": "39d702af7ae5d7e33ee90651292aa77fa07d33ca"
}

@@ -1,3 +0,1 @@

// Keep this around until we drop support for Node 14.
import 'abort-controller/polyfill'; // eslint-disable-line import/no-unassigned-import
import os from 'node:os';

@@ -4,0 +2,0 @@ import { DataConverter, LoadedDataConverter } from '@temporalio/common';

@@ -1,7 +0,10 @@

// Keep this around until we drop support for Node 14.
import 'abort-controller/polyfill'; // eslint-disable-line import/no-unassigned-import
import { AsyncLocalStorage } from 'node:async_hooks';
import * as grpc from '@grpc/grpc-js';
import type { RPCImpl } from 'protobufjs';
import { filterNullAndUndefined, normalizeTlsConfig, TLSConfig } from '@temporalio/common/lib/internal-non-workflow';
import {
filterNullAndUndefined,
normalizeTlsConfig,
TLSConfig,
normalizeTemporalGrpcEndpointAddress,
} from '@temporalio/common/lib/internal-non-workflow';
import { Duration, msOptionalToNumber } from '@temporalio/common/lib/time';

@@ -18,5 +21,6 @@ import { isGrpcServiceError, ServiceError } from './errors';

/**
* Server hostname and optional port.
* Port defaults to 7233 if address contains only host.
* The address of the Temporal server to connect to, in `hostname:port` format.
*
* Port defaults to 7233. Raw IPv6 addresses must be wrapped in square brackets (e.g. `[ipv6]:port`).
*
* @default localhost:7233

@@ -99,2 +103,3 @@ */

* Optional mapping of gRPC metadata (HTTP headers) to send with each request to the server.
* Setting the `Authorization` header is mutually exclusive with the {@link apiKey} option.
*

@@ -106,2 +111,11 @@ * In order to dynamically set metadata, use {@link Connection.withMetadata}

/**
* API key for Temporal. This becomes the "Authorization" HTTP header with "Bearer " prepended.
* This is mutually exclusive with the `Authorization` header in {@link ConnectionOptions.metadata}.
*
* You may provide a static string or a callback. Also see {@link Connection.withApiKey} or
* {@link Connection.setApiKey}
*/
apiKey?: string | (() => string);
/**
* Milliseconds to wait until establishing a connection with the server.

@@ -119,3 +133,3 @@ *

export type ConnectionOptionsWithDefaults = Required<
Omit<ConnectionOptions, 'tls' | 'connectTimeout' | 'callCredentials'>
Omit<ConnectionOptions, 'tls' | 'connectTimeout' | 'callCredentials' | 'apiKey'>
> & {

@@ -149,10 +163,20 @@ connectTimeoutMs: number;

* - Add default port to address if port not specified
* - Set `Authorization` header based on {@link ConnectionOptions.apiKey}
*/
function normalizeGRPCConfig(options?: ConnectionOptions): ConnectionOptions {
const { tls: tlsFromConfig, credentials, callCredentials, ...rest } = options || {};
if (rest.apiKey) {
if (rest.metadata?.['Authorization']) {
throw new TypeError(
'Both `apiKey` option and `Authorization` header were provided, but only one makes sense to use at a time.'
);
}
if (credentials !== undefined) {
throw new TypeError(
'Both `apiKey` and `credentials` ConnectionOptions were provided, but only one makes sense to use at a time'
);
}
}
if (rest.address) {
// eslint-disable-next-line prefer-const
let [host, port] = rest.address.split(':', 2);
port = port || '7233';
rest.address = `${host}:${port}`;
rest.address = normalizeTemporalGrpcEndpointAddress(rest.address);
}

@@ -197,2 +221,3 @@ const tls = normalizeTlsConfig(tlsFromConfig);

staticMetadata: Metadata;
apiKeyFnRef: { fn?: () => string };
}

@@ -218,2 +243,3 @@

readonly callContextStorage: AsyncLocalStorage<CallContext>;
readonly apiKeyFnRef: { fn?: () => string };
}

@@ -260,5 +286,16 @@

readonly callContextStorage: AsyncLocalStorage<CallContext>;
private readonly apiKeyFnRef: { fn?: () => string };
protected static createCtorOptions(options?: ConnectionOptions): ConnectionCtorOptions {
const optionsWithDefaults = addDefaults(normalizeGRPCConfig(options));
const normalizedOptions = normalizeGRPCConfig(options);
const apiKeyFnRef: { fn?: () => string } = {};
if (normalizedOptions.apiKey) {
if (typeof normalizedOptions.apiKey === 'string') {
const apiKey = normalizedOptions.apiKey;
apiKeyFnRef.fn = () => apiKey;
} else {
apiKeyFnRef.fn = normalizedOptions.apiKey;
}
}
const optionsWithDefaults = addDefaults(normalizedOptions);
// Allow overriding this

@@ -281,2 +318,3 @@ optionsWithDefaults.metadata['client-name'] ??= 'temporal-typescript';

staticMetadata: optionsWithDefaults.metadata,
apiKeyFnRef,
});

@@ -290,2 +328,3 @@ const workflowService = WorkflowService.create(workflowRpcImpl, false, false);

staticMetadata: optionsWithDefaults.metadata,
apiKeyFnRef,
});

@@ -299,2 +338,3 @@ const operatorService = OperatorService.create(operatorRpcImpl, false, false);

staticMetadata: optionsWithDefaults.metadata,
apiKeyFnRef,
});

@@ -310,2 +350,3 @@ const healthService = HealthService.create(healthRpcImpl, false, false);

options: optionsWithDefaults,
apiKeyFnRef,
};

@@ -374,2 +415,3 @@ }

callContextStorage,
apiKeyFnRef,
}: ConnectionCtorOptions) {

@@ -382,2 +424,3 @@ this.options = options;

this.callContextStorage = callContextStorage;
this.apiKeyFnRef = apiKeyFnRef;
}

@@ -391,2 +434,3 @@

staticMetadata,
apiKeyFnRef,
}: RPCImplOptions): RPCImpl {

@@ -396,2 +440,5 @@ return (method: { name: string }, requestData: any, callback: grpc.requestCallback<any>) => {

const { metadata, deadline, abortSignal } = callContextStorage.getStore() ?? {};
if (apiKeyFnRef.fn) {
metadataContainer.set('Authorization', `Bearer ${apiKeyFnRef.fn()}`);
}
for (const [k, v] of Object.entries(staticMetadata)) {

@@ -470,6 +517,51 @@ metadataContainer.set(k, v);

const cc = this.callContextStorage.getStore();
return await this.callContextStorage.run({ ...cc, metadata: { ...cc?.metadata, ...metadata } }, fn);
return await this.callContextStorage.run(
{
...cc,
metadata: { ...cc?.metadata, ...metadata },
},
fn
);
}
/**
* Set the apiKey for any service requests executed in `fn`'s scope (thus changing the `Authorization` header).
*
* @returns value returned from `fn`
*
* @example
*
* ```ts
* const workflowHandle = await conn.withApiKey('secret', () =>
* conn.withMetadata({ otherKey: 'set' }, () => client.start(options)))
* );
* ```
*/
async withApiKey<ReturnType>(apiKey: string, fn: () => Promise<ReturnType>): Promise<ReturnType> {
const cc = this.callContextStorage.getStore();
return await this.callContextStorage.run(
{
...cc,
metadata: { ...cc?.metadata, Authorization: `Bearer ${apiKey}` },
},
fn
);
}
/**
* Set the {@link ConnectionOptions.apiKey} for all subsequent requests. A static string or a
* callback function may be provided.
*/
setApiKey(apiKey: string | (() => string)): void {
if (typeof apiKey === 'string') {
if (apiKey === '') {
throw new TypeError('`apiKey` must not be an empty string');
}
this.apiKeyFnRef.fn = () => apiKey;
} else {
this.apiKeyFnRef.fn = apiKey;
}
}
/**
* Wait for successful connection to the server.

@@ -476,0 +568,0 @@ *

@@ -29,3 +29,3 @@ import { InterceptingCall, Interceptor, ListenerBuilder, RequesterBuilder, StatusObject } from '@grpc/grpc-js';

*
* @default 2
* @default 1.7
*/

@@ -43,3 +43,3 @@ factor: number;

*
* @default 0.1
* @default 0.2
*/

@@ -50,3 +50,3 @@ maxJitter: number;

*
* The default is 1 second for RESOURCE_EXHAUSTED errors and 20 millis for other retryable errors.
* The default is 1 second for RESOURCE_EXHAUSTED errors and 100 millis for other retryable errors.
*/

@@ -58,3 +58,3 @@ initialIntervalMs(status: StatusObject): number;

*
* The default is 10 seconds regardless of the status.
* The default is 5 seconds regardless of the status.
*/

@@ -75,7 +75,7 @@ maxIntervalMs(status: StatusObject): number;

maxAttempts: maxAttempts ?? 10,
factor: factor ?? 2,
maxJitter: maxJitter ?? 0.1,
factor: factor ?? 1.7,
maxJitter: maxJitter ?? 0.2,
initialIntervalMs: initialIntervalMs ?? defaultInitialIntervalMs,
maxIntervalMs() {
return 10_000;
return 5_000;
},

@@ -133,3 +133,3 @@ };

}
return 20;
return 100;
}

@@ -136,0 +136,0 @@

@@ -270,3 +270,3 @@ import Long from 'long'; // eslint-disable-line import/no-named-as-default

taskQueue: {
kind: temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED,
kind: temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_NORMAL,
name: action.taskQueue,

@@ -273,0 +273,0 @@ },

@@ -904,3 +904,3 @@ import { status as grpcStatus } from '@grpc/grpc-js';

taskQueue: {
kind: temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED,
kind: temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_NORMAL,
name: options.taskQueue,

@@ -953,3 +953,3 @@ },

taskQueue: {
kind: temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED,
kind: temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_NORMAL,
name: opts.taskQueue,

@@ -956,0 +956,0 @@ },

@@ -41,3 +41,2 @@ import { CommonWorkflowOptions, SignalDefinition, WithWorkflowArgs, Workflow } from '@temporalio/common';

*
* @experimental
*/

@@ -44,0 +43,0 @@ startDelay?: Duration;

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc