mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
feat: Introduce Google Cloud Platform as external secrets provider (#10146)
This commit is contained in:
parent
af695ebf93
commit
3ccb9df2f9
|
@ -83,6 +83,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@azure/identity": "^4.3.0",
|
"@azure/identity": "^4.3.0",
|
||||||
"@azure/keyvault-secrets": "^4.8.0",
|
"@azure/keyvault-secrets": "^4.8.0",
|
||||||
|
"@google-cloud/secret-manager": "^5.6.0",
|
||||||
"@n8n/client-oauth2": "workspace:*",
|
"@n8n/client-oauth2": "workspace:*",
|
||||||
"@n8n/config": "workspace:*",
|
"@n8n/config": "workspace:*",
|
||||||
"@n8n/localtunnel": "2.1.0",
|
"@n8n/localtunnel": "2.1.0",
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { InfisicalProvider } from './providers/infisical';
|
||||||
import { VaultProvider } from './providers/vault';
|
import { VaultProvider } from './providers/vault';
|
||||||
import { AwsSecretsManager } from './providers/aws-secrets/aws-secrets-manager';
|
import { AwsSecretsManager } from './providers/aws-secrets/aws-secrets-manager';
|
||||||
import { AzureKeyVault } from './providers/azure-key-vault/azure-key-vault';
|
import { AzureKeyVault } from './providers/azure-key-vault/azure-key-vault';
|
||||||
|
import { GcpSecretsManager } from './providers/gcp-secrets-manager/gcp-secrets-manager';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class ExternalSecretsProviders {
|
export class ExternalSecretsProviders {
|
||||||
|
@ -12,6 +13,7 @@ export class ExternalSecretsProviders {
|
||||||
infisical: InfisicalProvider,
|
infisical: InfisicalProvider,
|
||||||
vault: VaultProvider,
|
vault: VaultProvider,
|
||||||
azureKeyVault: AzureKeyVault,
|
azureKeyVault: AzureKeyVault,
|
||||||
|
gcpSecretsManager: GcpSecretsManager,
|
||||||
};
|
};
|
||||||
|
|
||||||
getProvider(name: string): { new (): SecretsProvider } | null {
|
getProvider(name: string): { new (): SecretsProvider } | null {
|
||||||
|
|
|
@ -37,7 +37,7 @@ describe('AzureKeyVault', () => {
|
||||||
yield { name: 'secret1' };
|
yield { name: 'secret1' };
|
||||||
yield { name: 'secret2' };
|
yield { name: 'secret2' };
|
||||||
yield { name: 'secret3' }; // no value
|
yield { name: 'secret3' }; // no value
|
||||||
yield { name: '#@&' }; // invalid name
|
yield { name: '#@&' }; // unsupported name
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -65,6 +65,6 @@ describe('AzureKeyVault', () => {
|
||||||
expect(azureKeyVault.getSecret('secret1')).toBe('value1');
|
expect(azureKeyVault.getSecret('secret1')).toBe('value1');
|
||||||
expect(azureKeyVault.getSecret('secret2')).toBe('value2');
|
expect(azureKeyVault.getSecret('secret2')).toBe('value2');
|
||||||
expect(azureKeyVault.getSecret('secret3')).toBeUndefined(); // no value
|
expect(azureKeyVault.getSecret('secret3')).toBeUndefined(); // no value
|
||||||
expect(azureKeyVault.getSecret('#@&')).toBeUndefined(); // invalid name
|
expect(azureKeyVault.getSecret('#@&')).toBeUndefined(); // unsupported name
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
import { mock } from 'jest-mock-extended';
|
||||||
|
import { GcpSecretsManager } from '../gcp-secrets-manager/gcp-secrets-manager';
|
||||||
|
import type { GcpSecretsManagerContext } from '../gcp-secrets-manager/types';
|
||||||
|
import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
|
||||||
|
import type { google } from '@google-cloud/secret-manager/build/protos/protos';
|
||||||
|
|
||||||
|
jest.mock('@google-cloud/secret-manager');
|
||||||
|
|
||||||
|
type GcpSecretVersionResponse = google.cloud.secretmanager.v1.IAccessSecretVersionResponse;
|
||||||
|
|
||||||
|
describe('GCP Secrets Manager', () => {
|
||||||
|
const gcpSecretsManager = new GcpSecretsManager();
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update cached secrets', async () => {
|
||||||
|
/**
|
||||||
|
* Arrange
|
||||||
|
*/
|
||||||
|
const PROJECT_ID = 'my-project-id';
|
||||||
|
|
||||||
|
const SECRETS: Record<string, string> = {
|
||||||
|
secret1: 'value1',
|
||||||
|
secret2: 'value2',
|
||||||
|
secret3: '', // no value
|
||||||
|
'#@&': 'value', // unsupported name
|
||||||
|
};
|
||||||
|
|
||||||
|
await gcpSecretsManager.init(
|
||||||
|
mock<GcpSecretsManagerContext>({
|
||||||
|
settings: { serviceAccountKey: `{ "project_id": "${PROJECT_ID}" }` },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const listSpy = jest
|
||||||
|
.spyOn(SecretManagerServiceClient.prototype, 'listSecrets')
|
||||||
|
// @ts-expect-error Partial mock
|
||||||
|
.mockResolvedValue([
|
||||||
|
[
|
||||||
|
{ name: `projects/${PROJECT_ID}/secrets/secret1` },
|
||||||
|
{ name: `projects/${PROJECT_ID}/secrets/secret2` },
|
||||||
|
{ name: `projects/${PROJECT_ID}/secrets/secret3` },
|
||||||
|
{ name: `projects/${PROJECT_ID}/secrets/#@&` },
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
const getSpy = jest
|
||||||
|
.spyOn(SecretManagerServiceClient.prototype, 'accessSecretVersion')
|
||||||
|
.mockImplementation(async ({ name }: { name: string }) => {
|
||||||
|
const secretName = name.split('/')[3];
|
||||||
|
return [
|
||||||
|
{ payload: { data: Buffer.from(SECRETS[secretName]) } },
|
||||||
|
] as GcpSecretVersionResponse[];
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Act
|
||||||
|
*/
|
||||||
|
await gcpSecretsManager.connect();
|
||||||
|
await gcpSecretsManager.update();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert
|
||||||
|
*/
|
||||||
|
expect(listSpy).toHaveBeenCalled();
|
||||||
|
|
||||||
|
expect(getSpy).toHaveBeenCalledWith({
|
||||||
|
name: `projects/${PROJECT_ID}/secrets/secret1/versions/latest`,
|
||||||
|
});
|
||||||
|
expect(getSpy).toHaveBeenCalledWith({
|
||||||
|
name: `projects/${PROJECT_ID}/secrets/secret2/versions/latest`,
|
||||||
|
});
|
||||||
|
expect(getSpy).toHaveBeenCalledWith({
|
||||||
|
name: `projects/${PROJECT_ID}/secrets/secret3/versions/latest`,
|
||||||
|
});
|
||||||
|
expect(getSpy).not.toHaveBeenCalledWith({
|
||||||
|
name: `projects/${PROJECT_ID}/secrets/#@&/versions/latest`,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(gcpSecretsManager.getSecret('secret1')).toBe('value1');
|
||||||
|
expect(gcpSecretsManager.getSecret('secret2')).toBe('value2');
|
||||||
|
expect(gcpSecretsManager.getSecret('secret3')).toBeUndefined(); // no value
|
||||||
|
expect(gcpSecretsManager.getSecret('#@&')).toBeUndefined(); // unsupported name
|
||||||
|
});
|
||||||
|
});
|
|
@ -74,7 +74,7 @@ export class AzureKeyVault implements SecretsProvider {
|
||||||
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
|
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
|
||||||
this.client = new SecretClient(`https://${vaultName}.vault.azure.net/`, credential);
|
this.client = new SecretClient(`https://${vaultName}.vault.azure.net/`, credential);
|
||||||
this.state = 'connected';
|
this.state = 'connected';
|
||||||
} catch (error) {
|
} catch {
|
||||||
this.state = 'error';
|
this.state = 'error';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ export class AzureKeyVault implements SecretsProvider {
|
||||||
await this.client.listPropertiesOfSecrets().next();
|
await this.client.listPropertiesOfSecrets().next();
|
||||||
return [true];
|
return [true];
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
return [false, error instanceof Error ? error.message : 'unknown error'];
|
return [false, error instanceof Error ? error.message : 'Unknown error'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
import { SecretManagerServiceClient as GcpClient } from '@google-cloud/secret-manager';
|
||||||
|
import { DOCS_HELP_NOTICE, EXTERNAL_SECRETS_NAME_REGEX } from '@/ExternalSecrets/constants';
|
||||||
|
import type { SecretsProvider, SecretsProviderState } from '@/Interfaces';
|
||||||
|
import { jsonParse, type INodeProperties } from 'n8n-workflow';
|
||||||
|
import type {
|
||||||
|
GcpSecretsManagerContext,
|
||||||
|
GcpSecretAccountKey,
|
||||||
|
RawGcpSecretAccountKey,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
export class GcpSecretsManager implements SecretsProvider {
|
||||||
|
name = 'gcpSecretsManager';
|
||||||
|
|
||||||
|
displayName = 'GCP Secrets Manager';
|
||||||
|
|
||||||
|
state: SecretsProviderState = 'initializing';
|
||||||
|
|
||||||
|
properties: INodeProperties[] = [
|
||||||
|
DOCS_HELP_NOTICE,
|
||||||
|
{
|
||||||
|
displayName: 'Service Account Key',
|
||||||
|
name: 'serviceAccountKey',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
typeOptions: { password: true },
|
||||||
|
placeholder: 'e.g. { "type": "service_account", "project_id": "gcp-secrets-store", ... }',
|
||||||
|
hint: 'Content of JSON file downloaded from Google Cloud Console.',
|
||||||
|
noDataExpression: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
private cachedSecrets: Record<string, string> = {};
|
||||||
|
|
||||||
|
private client: GcpClient;
|
||||||
|
|
||||||
|
private settings: GcpSecretAccountKey;
|
||||||
|
|
||||||
|
async init(context: GcpSecretsManagerContext) {
|
||||||
|
this.settings = this.parseSecretAccountKey(context.settings.serviceAccountKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
async connect() {
|
||||||
|
const { projectId, privateKey, clientEmail } = this.settings;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.client = new GcpClient({
|
||||||
|
credentials: { client_email: clientEmail, private_key: privateKey },
|
||||||
|
projectId,
|
||||||
|
});
|
||||||
|
this.state = 'connected';
|
||||||
|
} catch {
|
||||||
|
this.state = 'error';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async test(): Promise<[boolean] | [boolean, string]> {
|
||||||
|
if (!this.client) return [false, 'Failed to connect to GCP Secrets Manager'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.client.initialize();
|
||||||
|
return [true];
|
||||||
|
} catch (error: unknown) {
|
||||||
|
return [false, error instanceof Error ? error.message : 'Unknown error'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async disconnect() {
|
||||||
|
// unused
|
||||||
|
}
|
||||||
|
|
||||||
|
async update() {
|
||||||
|
const { projectId } = this.settings;
|
||||||
|
|
||||||
|
const [rawSecretNames] = await this.client.listSecrets({
|
||||||
|
parent: `projects/${projectId}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const secretNames = rawSecretNames.reduce<string[]>((acc, cur) => {
|
||||||
|
if (!cur.name || !EXTERNAL_SECRETS_NAME_REGEX.test(cur.name)) return acc;
|
||||||
|
|
||||||
|
const secretName = cur.name.split('/').pop();
|
||||||
|
|
||||||
|
if (secretName) acc.push(secretName);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const promises = secretNames.map(async (name) => {
|
||||||
|
const versions = await this.client.accessSecretVersion({
|
||||||
|
name: `projects/${projectId}/secrets/${name}/versions/latest`,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!Array.isArray(versions) || !versions.length) return null;
|
||||||
|
|
||||||
|
const [latestVersion] = versions;
|
||||||
|
|
||||||
|
if (!latestVersion.payload?.data) return null;
|
||||||
|
|
||||||
|
const value = latestVersion.payload.data.toString();
|
||||||
|
|
||||||
|
if (!value) return null;
|
||||||
|
|
||||||
|
return { name, value };
|
||||||
|
});
|
||||||
|
|
||||||
|
const results = await Promise.all(promises);
|
||||||
|
|
||||||
|
this.cachedSecrets = results.reduce<Record<string, string>>((acc, cur) => {
|
||||||
|
if (cur) acc[cur.name] = cur.value;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
getSecret(name: string) {
|
||||||
|
return this.cachedSecrets[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
hasSecret(name: string) {
|
||||||
|
return name in this.cachedSecrets;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSecretNames() {
|
||||||
|
return Object.keys(this.cachedSecrets);
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseSecretAccountKey(privateKey: string): GcpSecretAccountKey {
|
||||||
|
const parsed = jsonParse<RawGcpSecretAccountKey>(privateKey, { fallbackValue: {} });
|
||||||
|
|
||||||
|
return {
|
||||||
|
projectId: parsed?.project_id ?? '',
|
||||||
|
clientEmail: parsed?.client_email ?? '',
|
||||||
|
privateKey: parsed?.private_key ?? '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
import type { SecretsProviderSettings } from '@/Interfaces';
|
||||||
|
|
||||||
|
type JsonString = string;
|
||||||
|
|
||||||
|
export type GcpSecretsManagerContext = SecretsProviderSettings<{
|
||||||
|
serviceAccountKey: JsonString;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type RawGcpSecretAccountKey = {
|
||||||
|
project_id?: string;
|
||||||
|
private_key?: string;
|
||||||
|
client_email?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GcpSecretAccountKey = {
|
||||||
|
projectId: string;
|
||||||
|
clientEmail: string;
|
||||||
|
privateKey: string;
|
||||||
|
};
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g transform="translate(2.000000, 6.000000)" fill="#4285F4" fill-rule="nonzero">
|
||||||
|
<path d="M20,9.41469125e-14 L20,11.8867925 L16.0377358,11.8867925 C15.9335312,11.8867925 15.8490566,11.8023179 15.8490566,11.6981132 L15.8490566,10.5660377 C15.8490566,10.4618331 15.9335312,10.3773585 16.0377358,10.3773585 L18.4900566,10.377 L18.4900566,1.509 L16.0377358,1.50943396 C15.9335312,1.50943396 15.8490566,1.42495939 15.8490566,1.32075472 L15.8490566,0.188679245 C15.8490566,0.0844745755 15.9335312,9.41105434e-14 16.0377358,9.41469125e-14 L20,9.41469125e-14 Z M4.309101,9.41469125e-14 C4.41330567,9.3877869e-14 4.49778024,0.0844745755 4.49778024,0.188679245 L4.49778024,1.32075472 C4.49778024,1.42495939 4.41330567,1.50943396 4.309101,1.50943396 L1.509,1.509 L1.509,10.377 L4.29245283,10.3773585 C4.3966575,10.3773585 4.48113208,10.4618331 4.48113208,10.5660377 L4.48113208,11.6981132 C4.48113208,11.8023179 4.3966575,11.8867925 4.29245283,11.8867925 L2.14050999e-13,11.8867925 L2.14050999e-13,9.41469125e-14 L4.309101,9.41469125e-14 Z M15.4271098,3.86792453 L15.4271098,5.3240566 L15.4879305,5.34852941 L16.8381494,4.87130966 L17.1179245,5.69114872 L15.7555414,6.15613208 L15.719049,6.25402331 L16.6556874,7.51437292 L15.9501676,8.02830189 L15.0378575,6.76795228 L14.9527085,6.76795228 L14.0403984,8.02830189 L13.3348786,7.51437292 L14.2593529,6.25402331 L14.2350246,6.15613208 L12.8726415,5.69114872 L13.1524166,4.87130966 L14.4904714,5.34852941 L14.5634562,5.3240566 L14.5634562,3.86792453 L15.4271098,3.86792453 Z M5.19597773,3.86792453 L5.19597773,5.30963945 L5.2567984,5.33386996 L6.60701735,4.86137515 L6.88679245,5.673097 L5.52440936,6.13347656 L5.48791696,6.23039857 L6.42455533,7.47826947 L5.71903552,7.98711003 L4.80672541,6.73923913 L4.72157647,6.73923913 L3.80926637,7.98711003 L3.10374655,7.47826947 L4.02822079,6.23039857 L4.00389252,6.13347656 L2.64150943,5.673097 L2.92128453,4.86137515 L4.25933935,5.33386996 L4.33232416,5.30963945 L4.33232416,3.86792453 L5.19597773,3.86792453 Z M10.2903173,3.86792453 L10.2903173,5.30963945 L10.351138,5.33386996 L11.701357,4.86137515 L11.9811321,5.673097 L10.618749,6.13347656 L10.5822566,6.23039857 L11.518895,7.47826947 L10.8133751,7.98711003 L9.90106504,6.73923913 L9.81591609,6.73923913 L8.90360599,7.98711003 L8.19808618,7.47826947 L9.12256042,6.23039857 L9.09823215,6.13347656 L7.73584906,5.673097 L8.01562416,4.86137515 L9.35367897,5.33386996 L9.42666378,5.30963945 L9.42666378,3.86792453 L10.2903173,3.86792453 Z" >
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
|
@ -7,6 +7,7 @@ import doppler from '../assets/images/doppler.webp';
|
||||||
import vault from '../assets/images/hashicorp.webp';
|
import vault from '../assets/images/hashicorp.webp';
|
||||||
import awsSecretsManager from '../assets/images/aws-secrets-manager.svg';
|
import awsSecretsManager from '../assets/images/aws-secrets-manager.svg';
|
||||||
import azureKeyVault from '../assets/images/azure-key-vault.svg';
|
import azureKeyVault from '../assets/images/azure-key-vault.svg';
|
||||||
|
import gcpSecretsManager from '../assets/images/gcp-secrets-manager.svg';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
provider: ExternalSecretsProvider;
|
provider: ExternalSecretsProvider;
|
||||||
|
@ -20,6 +21,7 @@ const image = computed(
|
||||||
vault,
|
vault,
|
||||||
awsSecretsManager,
|
awsSecretsManager,
|
||||||
azureKeyVault,
|
azureKeyVault,
|
||||||
|
gcpSecretsManager,
|
||||||
})[props.provider.name],
|
})[props.provider.name],
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -608,6 +608,9 @@ importers:
|
||||||
'@azure/keyvault-secrets':
|
'@azure/keyvault-secrets':
|
||||||
specifier: ^4.8.0
|
specifier: ^4.8.0
|
||||||
version: 4.8.0
|
version: 4.8.0
|
||||||
|
'@google-cloud/secret-manager':
|
||||||
|
specifier: ^5.6.0
|
||||||
|
version: 5.6.0(encoding@0.1.13)
|
||||||
'@n8n/client-oauth2':
|
'@n8n/client-oauth2':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../@n8n/client-oauth2
|
version: link:../@n8n/client-oauth2
|
||||||
|
@ -3360,6 +3363,10 @@ packages:
|
||||||
resolution: {integrity: sha512-uWJJf6S2PJL7oZ4ezv16aZl9+IJqPo5GzUv1pZ3/qRiMj13p0ylEgX1+LxBpX71eEPKTwMHoJV2IBBe3EAq7Xw==}
|
resolution: {integrity: sha512-uWJJf6S2PJL7oZ4ezv16aZl9+IJqPo5GzUv1pZ3/qRiMj13p0ylEgX1+LxBpX71eEPKTwMHoJV2IBBe3EAq7Xw==}
|
||||||
engines: {node: '>=14.0.0'}
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
|
'@google-cloud/secret-manager@5.6.0':
|
||||||
|
resolution: {integrity: sha512-0daW/OXQEVc6VQKPyJTQNyD+563I/TYQ7GCQJx4dq3lB666R9FUPvqHx9b/o/qQtZ5pfuoCbGZl3krpxgTSW8Q==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
'@google-cloud/storage@6.11.0':
|
'@google-cloud/storage@6.11.0':
|
||||||
resolution: {integrity: sha512-p5VX5K2zLTrMXlKdS1CiQNkKpygyn7CBFm5ZvfhVj6+7QUsjWvYx9YDMkYXdarZ6JDt4cxiu451y9QUIH82ZTw==}
|
resolution: {integrity: sha512-p5VX5K2zLTrMXlKdS1CiQNkKpygyn7CBFm5ZvfhVj6+7QUsjWvYx9YDMkYXdarZ6JDt4cxiu451y9QUIH82ZTw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
@ -3376,11 +3383,6 @@ packages:
|
||||||
resolution: {integrity: sha512-vYVqYzHicDqyKB+NQhAc54I1QWCBLCrYG6unqOIcBTHx+7x8C9lcoLj3KVJXs2VB4lUbpWY+Kk9NipcbXYWmvg==}
|
resolution: {integrity: sha512-vYVqYzHicDqyKB+NQhAc54I1QWCBLCrYG6unqOIcBTHx+7x8C9lcoLj3KVJXs2VB4lUbpWY+Kk9NipcbXYWmvg==}
|
||||||
engines: {node: '>=12.10.0'}
|
engines: {node: '>=12.10.0'}
|
||||||
|
|
||||||
'@grpc/proto-loader@0.7.10':
|
|
||||||
resolution: {integrity: sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==}
|
|
||||||
engines: {node: '>=6'}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
'@grpc/proto-loader@0.7.13':
|
'@grpc/proto-loader@0.7.13':
|
||||||
resolution: {integrity: sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==}
|
resolution: {integrity: sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
@ -15843,6 +15845,13 @@ snapshots:
|
||||||
- encoding
|
- encoding
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
'@google-cloud/secret-manager@5.6.0(encoding@0.1.13)':
|
||||||
|
dependencies:
|
||||||
|
google-gax: 4.3.4(encoding@0.1.13)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- encoding
|
||||||
|
- supports-color
|
||||||
|
|
||||||
'@google-cloud/storage@6.11.0(encoding@0.1.13)':
|
'@google-cloud/storage@6.11.0(encoding@0.1.13)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@google-cloud/paginator': 3.0.7
|
'@google-cloud/paginator': 3.0.7
|
||||||
|
@ -15875,13 +15884,6 @@ snapshots:
|
||||||
'@grpc/proto-loader': 0.7.13
|
'@grpc/proto-loader': 0.7.13
|
||||||
'@js-sdsl/ordered-map': 4.4.2
|
'@js-sdsl/ordered-map': 4.4.2
|
||||||
|
|
||||||
'@grpc/proto-loader@0.7.10':
|
|
||||||
dependencies:
|
|
||||||
lodash.camelcase: 4.3.0
|
|
||||||
long: 5.2.3
|
|
||||||
protobufjs: 7.3.0
|
|
||||||
yargs: 17.7.2
|
|
||||||
|
|
||||||
'@grpc/proto-loader@0.7.13':
|
'@grpc/proto-loader@0.7.13':
|
||||||
dependencies:
|
dependencies:
|
||||||
lodash.camelcase: 4.3.0
|
lodash.camelcase: 4.3.0
|
||||||
|
@ -21305,7 +21307,7 @@ snapshots:
|
||||||
|
|
||||||
eslint-import-resolver-node@0.3.9:
|
eslint-import-resolver-node@0.3.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 3.2.7(supports-color@5.5.0)
|
debug: 3.2.7(supports-color@8.1.1)
|
||||||
is-core-module: 2.13.1
|
is-core-module: 2.13.1
|
||||||
resolve: 1.22.8
|
resolve: 1.22.8
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
@ -21330,7 +21332,7 @@ snapshots:
|
||||||
|
|
||||||
eslint-module-utils@2.8.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0):
|
eslint-module-utils@2.8.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 3.2.7(supports-color@5.5.0)
|
debug: 3.2.7(supports-color@8.1.1)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.5.2)
|
'@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.5.2)
|
||||||
eslint: 8.57.0
|
eslint: 8.57.0
|
||||||
|
@ -21350,7 +21352,7 @@ snapshots:
|
||||||
array.prototype.findlastindex: 1.2.3
|
array.prototype.findlastindex: 1.2.3
|
||||||
array.prototype.flat: 1.3.2
|
array.prototype.flat: 1.3.2
|
||||||
array.prototype.flatmap: 1.3.2
|
array.prototype.flatmap: 1.3.2
|
||||||
debug: 3.2.7(supports-color@5.5.0)
|
debug: 3.2.7(supports-color@8.1.1)
|
||||||
doctrine: 2.1.0
|
doctrine: 2.1.0
|
||||||
eslint: 8.57.0
|
eslint: 8.57.0
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
|
@ -21880,7 +21882,7 @@ snapshots:
|
||||||
|
|
||||||
follow-redirects@1.15.6(debug@3.2.7):
|
follow-redirects@1.15.6(debug@3.2.7):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
debug: 3.2.7(supports-color@5.5.0)
|
debug: 3.2.7(supports-color@8.1.1)
|
||||||
|
|
||||||
follow-redirects@1.15.6(debug@4.3.4):
|
follow-redirects@1.15.6(debug@4.3.4):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
|
@ -22221,7 +22223,7 @@ snapshots:
|
||||||
array-parallel: 0.1.3
|
array-parallel: 0.1.3
|
||||||
array-series: 0.1.5
|
array-series: 0.1.5
|
||||||
cross-spawn: 4.0.2
|
cross-spawn: 4.0.2
|
||||||
debug: 3.2.7(supports-color@5.5.0)
|
debug: 3.2.7(supports-color@8.1.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
@ -22255,7 +22257,7 @@ snapshots:
|
||||||
google-gax@4.3.4(encoding@0.1.13):
|
google-gax@4.3.4(encoding@0.1.13):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@grpc/grpc-js': 1.10.8
|
'@grpc/grpc-js': 1.10.8
|
||||||
'@grpc/proto-loader': 0.7.10
|
'@grpc/proto-loader': 0.7.13
|
||||||
'@types/long': 4.0.2
|
'@types/long': 4.0.2
|
||||||
abort-controller: 3.0.0
|
abort-controller: 3.0.0
|
||||||
duplexify: 4.1.2
|
duplexify: 4.1.2
|
||||||
|
@ -24914,7 +24916,7 @@ snapshots:
|
||||||
|
|
||||||
pdf-parse@1.1.1:
|
pdf-parse@1.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 3.2.7(supports-color@5.5.0)
|
debug: 3.2.7(supports-color@8.1.1)
|
||||||
node-ensure: 0.0.0
|
node-ensure: 0.0.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
@ -25826,7 +25828,7 @@ snapshots:
|
||||||
|
|
||||||
rhea@1.0.24:
|
rhea@1.0.24:
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 3.2.7(supports-color@5.5.0)
|
debug: 3.2.7(supports-color@8.1.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
@ -26200,7 +26202,7 @@ snapshots:
|
||||||
binascii: 0.0.2
|
binascii: 0.0.2
|
||||||
bn.js: 5.2.1
|
bn.js: 5.2.1
|
||||||
browser-request: 0.3.3
|
browser-request: 0.3.3
|
||||||
debug: 3.2.7(supports-color@5.5.0)
|
debug: 3.2.7(supports-color@8.1.1)
|
||||||
expand-tilde: 2.0.2
|
expand-tilde: 2.0.2
|
||||||
extend: 3.0.2
|
extend: 3.0.2
|
||||||
fast-xml-parser: 4.2.7
|
fast-xml-parser: 4.2.7
|
||||||
|
|
Loading…
Reference in a new issue