@alienplatform/testing 3.3.10 → 3.3.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -105,7 +105,8 @@ async function setAzureSecret(resourcePrefix, vaultName, secretKey, secretValue)
105
105
  operation: "setAzureSecret",
106
106
  message: "Azure credentials are required (set AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET env vars)"
107
107
  }));
108
- const client = new SecretClient(vaultUrl, new ClientSecretCredential(tenantId, clientId, clientSecret));
108
+ const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
109
+ const client = new SecretClient(vaultUrl, credential);
109
110
  const azureSecretKey = secretKey.replace(/_/g, "-");
110
111
  await client.setSecret(azureSecretKey, secretValue);
111
112
  }
@@ -1 +1 @@
1
- {"version":3,"file":"external-secrets.js","names":[],"sources":["../src/external-secrets.ts"],"sourcesContent":["/**\n * External secrets - platform-native secret management\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport { AlienError } from \"@alienplatform/core\"\nimport { PutParameterCommand, SSMClient } from \"@aws-sdk/client-ssm\"\nimport { ClientSecretCredential } from \"@azure/identity\"\nimport { SecretClient } from \"@azure/keyvault-secrets\"\nimport { SecretManagerServiceClient } from \"@google-cloud/secret-manager\"\nimport {\n TestingOperationFailedError,\n TestingUnsupportedPlatformError,\n withTestingContext,\n} from \"./errors.js\"\nimport type { Platform } from \"./types.js\"\n\n/**\n * Set an external secret using platform-native tools\n *\n * Falls back to environment variables for cloud provider credentials.\n */\nexport async function setExternalSecret(\n platform: Platform,\n resourcePrefix: string,\n vaultName: string,\n secretKey: string,\n secretValue: string,\n _namespace?: string,\n stateDir?: string,\n deploymentId?: string,\n): Promise<void> {\n try {\n switch (platform) {\n case \"aws\":\n await setAWSSecret(resourcePrefix, vaultName, secretKey, secretValue)\n break\n\n case \"gcp\":\n await setGCPSecret(resourcePrefix, vaultName, secretKey, secretValue)\n break\n\n case \"azure\":\n await setAzureSecret(resourcePrefix, vaultName, secretKey, secretValue)\n break\n\n case \"local\":\n await setLocalSecret(vaultName, secretKey, secretValue, stateDir, deploymentId)\n break\n\n default: {\n const exhaustive: never = platform\n throw new AlienError(\n TestingUnsupportedPlatformError.create({\n platform: String(exhaustive),\n operation: \"setExternalSecret\",\n }),\n )\n }\n }\n } catch (error) {\n throw await withTestingContext(error, \"setExternalSecret\", \"Failed to set external secret\", {\n platform,\n resourcePrefix,\n vaultName,\n secretKey,\n })\n }\n}\n\n/**\n * Set AWS SSM Parameter Store secret\n *\n * Uses environment variables for credentials (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION).\n */\nasync function setAWSSecret(\n resourcePrefix: string,\n vaultName: string,\n secretKey: string,\n secretValue: string,\n): Promise<void> {\n const client = new SSMClient({\n region: process.env.AWS_REGION,\n })\n const parameterName = `/${resourcePrefix}-${vaultName}-${secretKey}`\n\n await client.send(\n new PutParameterCommand({\n Name: parameterName,\n Value: secretValue,\n Type: \"SecureString\",\n Overwrite: true,\n }),\n )\n}\n\n/**\n * Set GCP Secret Manager secret\n *\n * Uses environment variables for credentials (GOOGLE_APPLICATION_CREDENTIALS, GCP_PROJECT_ID).\n */\nasync function setGCPSecret(\n resourcePrefix: string,\n vaultName: string,\n secretKey: string,\n secretValue: string,\n): Promise<void> {\n // Falls back to GOOGLE_APPLICATION_CREDENTIALS\n const client = new SecretManagerServiceClient()\n const projectId = process.env.GCP_PROJECT_ID || process.env.GOOGLE_CLOUD_PROJECT\n\n if (!projectId) {\n throw new AlienError(\n TestingOperationFailedError.create({\n operation: \"setGCPSecret\",\n message: \"GCP project ID is required (set GCP_PROJECT_ID or GOOGLE_CLOUD_PROJECT env var)\",\n }),\n )\n }\n\n const secretName = `${resourcePrefix}-${vaultName}-${secretKey}`\n const parent = `projects/${projectId}`\n const secretPath = `${parent}/secrets/${secretName}`\n\n try {\n // Try to create the secret first\n await client.createSecret({\n parent,\n secretId: secretName,\n secret: {\n replication: {\n automatic: {},\n },\n },\n })\n } catch (error: any) {\n // Secret already exists, that's fine\n if (!error.message?.includes(\"ALREADY_EXISTS\")) {\n throw await withTestingContext(error, \"setGCPSecret\", \"Failed to create GCP secret\")\n }\n }\n\n // Add secret version\n await client.addSecretVersion({\n parent: secretPath,\n payload: {\n data: Buffer.from(secretValue, \"utf8\"),\n },\n })\n}\n\n/**\n * Set Azure Key Vault secret\n *\n * Uses environment variables for credentials (AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET).\n */\nasync function setAzureSecret(\n resourcePrefix: string,\n vaultName: string,\n secretKey: string,\n secretValue: string,\n): Promise<void> {\n const vaultNameFull = `${resourcePrefix}-${vaultName}`\n const vaultUrl = `https://${vaultNameFull}.vault.azure.net`\n\n // Fall back to environment variables\n const tenantId = process.env.AZURE_TENANT_ID\n const clientId = process.env.AZURE_CLIENT_ID\n const clientSecret = process.env.AZURE_CLIENT_SECRET\n\n if (!tenantId || !clientId || !clientSecret) {\n throw new AlienError(\n TestingOperationFailedError.create({\n operation: \"setAzureSecret\",\n message:\n \"Azure credentials are required (set AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET env vars)\",\n }),\n )\n }\n\n const credential = new ClientSecretCredential(tenantId, clientId, clientSecret)\n const client = new SecretClient(vaultUrl, credential)\n\n // Azure Key Vault requires alphanumeric names with hyphens\n const azureSecretKey = secretKey.replace(/_/g, \"-\")\n\n await client.setSecret(azureSecretKey, secretValue)\n}\n\n/**\n * Set local dev secret by writing directly to the vault's secrets.json file.\n *\n * The local vault binding (LocalVault) reads from:\n * {stateDir}/{deploymentId}/vault/{vaultName}/secrets.json\n *\n * We write to the same path so the running function can read it immediately.\n */\nasync function setLocalSecret(\n vaultName: string,\n secretKey: string,\n secretValue: string,\n stateDir?: string,\n deploymentId?: string,\n): Promise<void> {\n if (!stateDir) {\n throw new AlienError(\n TestingOperationFailedError.create({\n operation: \"setLocalSecret\",\n message: \"stateDir is required for local vault set\",\n }),\n )\n }\n\n if (!deploymentId) {\n throw new AlienError(\n TestingOperationFailedError.create({\n operation: \"setLocalSecret\",\n message: \"deploymentId is required for local vault set\",\n }),\n )\n }\n\n // Path matches what LocalVault reads: {stateDir}/{deploymentId}/vault/{vaultName}/secrets.json\n const vaultDir = join(stateDir, deploymentId, \"vault\", vaultName)\n const secretsFile = join(vaultDir, \"secrets.json\")\n\n // Read existing secrets or start fresh\n let secrets: Record<string, string> = {}\n if (existsSync(secretsFile)) {\n secrets = JSON.parse(readFileSync(secretsFile, \"utf-8\"))\n }\n\n // Set the secret\n secrets[secretKey] = secretValue\n\n // Write back\n mkdirSync(vaultDir, { recursive: true })\n writeFileSync(secretsFile, JSON.stringify(secrets, null, 2))\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"external-secrets.js","names":[],"sources":["../src/external-secrets.ts"],"sourcesContent":["/**\n * External secrets - platform-native secret management\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport { AlienError } from \"@alienplatform/core\"\nimport { PutParameterCommand, SSMClient } from \"@aws-sdk/client-ssm\"\nimport { ClientSecretCredential } from \"@azure/identity\"\nimport { SecretClient } from \"@azure/keyvault-secrets\"\nimport { SecretManagerServiceClient } from \"@google-cloud/secret-manager\"\nimport {\n TestingOperationFailedError,\n TestingUnsupportedPlatformError,\n withTestingContext,\n} from \"./errors.js\"\nimport type { Platform } from \"./types.js\"\n\n/**\n * Set an external secret using platform-native tools\n *\n * Falls back to environment variables for cloud provider credentials.\n */\nexport async function setExternalSecret(\n platform: Platform,\n resourcePrefix: string,\n vaultName: string,\n secretKey: string,\n secretValue: string,\n _namespace?: string,\n stateDir?: string,\n deploymentId?: string,\n): Promise<void> {\n try {\n switch (platform) {\n case \"aws\":\n await setAWSSecret(resourcePrefix, vaultName, secretKey, secretValue)\n break\n\n case \"gcp\":\n await setGCPSecret(resourcePrefix, vaultName, secretKey, secretValue)\n break\n\n case \"azure\":\n await setAzureSecret(resourcePrefix, vaultName, secretKey, secretValue)\n break\n\n case \"local\":\n await setLocalSecret(vaultName, secretKey, secretValue, stateDir, deploymentId)\n break\n\n default: {\n const exhaustive: never = platform\n throw new AlienError(\n TestingUnsupportedPlatformError.create({\n platform: String(exhaustive),\n operation: \"setExternalSecret\",\n }),\n )\n }\n }\n } catch (error) {\n throw await withTestingContext(error, \"setExternalSecret\", \"Failed to set external secret\", {\n platform,\n resourcePrefix,\n vaultName,\n secretKey,\n })\n }\n}\n\n/**\n * Set AWS SSM Parameter Store secret\n *\n * Uses environment variables for credentials (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION).\n */\nasync function setAWSSecret(\n resourcePrefix: string,\n vaultName: string,\n secretKey: string,\n secretValue: string,\n): Promise<void> {\n const client = new SSMClient({\n region: process.env.AWS_REGION,\n })\n const parameterName = `/${resourcePrefix}-${vaultName}-${secretKey}`\n\n await client.send(\n new PutParameterCommand({\n Name: parameterName,\n Value: secretValue,\n Type: \"SecureString\",\n Overwrite: true,\n }),\n )\n}\n\n/**\n * Set GCP Secret Manager secret\n *\n * Uses environment variables for credentials (GOOGLE_APPLICATION_CREDENTIALS, GCP_PROJECT_ID).\n */\nasync function setGCPSecret(\n resourcePrefix: string,\n vaultName: string,\n secretKey: string,\n secretValue: string,\n): Promise<void> {\n // Falls back to GOOGLE_APPLICATION_CREDENTIALS\n const client = new SecretManagerServiceClient()\n const projectId = process.env.GCP_PROJECT_ID || process.env.GOOGLE_CLOUD_PROJECT\n\n if (!projectId) {\n throw new AlienError(\n TestingOperationFailedError.create({\n operation: \"setGCPSecret\",\n message: \"GCP project ID is required (set GCP_PROJECT_ID or GOOGLE_CLOUD_PROJECT env var)\",\n }),\n )\n }\n\n const secretName = `${resourcePrefix}-${vaultName}-${secretKey}`\n const parent = `projects/${projectId}`\n const secretPath = `${parent}/secrets/${secretName}`\n\n try {\n // Try to create the secret first\n await client.createSecret({\n parent,\n secretId: secretName,\n secret: {\n replication: {\n automatic: {},\n },\n },\n })\n } catch (error: any) {\n // Secret already exists, that's fine\n if (!error.message?.includes(\"ALREADY_EXISTS\")) {\n throw await withTestingContext(error, \"setGCPSecret\", \"Failed to create GCP secret\")\n }\n }\n\n // Add secret version\n await client.addSecretVersion({\n parent: secretPath,\n payload: {\n data: Buffer.from(secretValue, \"utf8\"),\n },\n })\n}\n\n/**\n * Set Azure Key Vault secret\n *\n * Uses environment variables for credentials (AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET).\n */\nasync function setAzureSecret(\n resourcePrefix: string,\n vaultName: string,\n secretKey: string,\n secretValue: string,\n): Promise<void> {\n const vaultNameFull = `${resourcePrefix}-${vaultName}`\n const vaultUrl = `https://${vaultNameFull}.vault.azure.net`\n\n // Fall back to environment variables\n const tenantId = process.env.AZURE_TENANT_ID\n const clientId = process.env.AZURE_CLIENT_ID\n const clientSecret = process.env.AZURE_CLIENT_SECRET\n\n if (!tenantId || !clientId || !clientSecret) {\n throw new AlienError(\n TestingOperationFailedError.create({\n operation: \"setAzureSecret\",\n message:\n \"Azure credentials are required (set AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET env vars)\",\n }),\n )\n }\n\n const credential = new ClientSecretCredential(tenantId, clientId, clientSecret)\n const client = new SecretClient(vaultUrl, credential)\n\n // Azure Key Vault requires alphanumeric names with hyphens\n const azureSecretKey = secretKey.replace(/_/g, \"-\")\n\n await client.setSecret(azureSecretKey, secretValue)\n}\n\n/**\n * Set local dev secret by writing directly to the vault's secrets.json file.\n *\n * The local vault binding (LocalVault) reads from:\n * {stateDir}/{deploymentId}/vault/{vaultName}/secrets.json\n *\n * We write to the same path so the running function can read it immediately.\n */\nasync function setLocalSecret(\n vaultName: string,\n secretKey: string,\n secretValue: string,\n stateDir?: string,\n deploymentId?: string,\n): Promise<void> {\n if (!stateDir) {\n throw new AlienError(\n TestingOperationFailedError.create({\n operation: \"setLocalSecret\",\n message: \"stateDir is required for local vault set\",\n }),\n )\n }\n\n if (!deploymentId) {\n throw new AlienError(\n TestingOperationFailedError.create({\n operation: \"setLocalSecret\",\n message: \"deploymentId is required for local vault set\",\n }),\n )\n }\n\n // Path matches what LocalVault reads: {stateDir}/{deploymentId}/vault/{vaultName}/secrets.json\n const vaultDir = join(stateDir, deploymentId, \"vault\", vaultName)\n const secretsFile = join(vaultDir, \"secrets.json\")\n\n // Read existing secrets or start fresh\n let secrets: Record<string, string> = {}\n if (existsSync(secretsFile)) {\n secrets = JSON.parse(readFileSync(secretsFile, \"utf-8\"))\n }\n\n // Set the secret\n secrets[secretKey] = secretValue\n\n // Write back\n mkdirSync(vaultDir, { recursive: true })\n writeFileSync(secretsFile, JSON.stringify(secrets, null, 2))\n}\n"],"mappings":"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"}