@zuplo/cli 6.52.22 → 6.52.23

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.
@@ -1 +1 @@
1
- {"version":3,"file":"populate.d.ts","sourceRoot":"","sources":["../../src/link/populate.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAwB/C,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,iBAkDjE;AAED,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,KAAK,GAAG,aAAa,GAAG,WAAW,CAAC,iBAwF3D"}
1
+ {"version":3,"file":"populate.d.ts","sourceRoot":"","sources":["../../src/link/populate.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,iBAkDjE;AAED,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,KAAK,GAAG,aAAa,GAAG,WAAW,CAAC,iBAkD3D"}
@@ -7,16 +7,6 @@ import { ZUPLO_FALLBACK_JSON_FILE, ZUPLO_PREFERRED_JSON_FILE, ZUPLO_SYSTEM_ENV_V
7
7
  import { logger } from "../common/logger.js";
8
8
  import { printCriticalFailureToConsoleAndExit } from "../common/output.js";
9
9
  import settings from "../common/settings.js";
10
- const SYSTEM_KEYS = [
11
- "accountName",
12
- "projectName",
13
- "environmentType",
14
- "systemConfigurations",
15
- ];
16
- function wrapEnvValue(value) {
17
- const escapedValue = value.replace(/"/g, '\\"');
18
- return `"${escapedValue}"`;
19
- }
20
10
  export async function safeMergeConfig(dir, project) {
21
11
  const normalizedDir = join(relative(process.cwd(), dir));
22
12
  const zuploPreferredConfigFile = join(normalizedDir, ZUPLO_PREFERRED_JSON_FILE);
@@ -60,7 +50,7 @@ export async function safeMergeConfig(dir, project) {
60
50
  export async function pullSystemConfig(argv) {
61
51
  const normalizedDir = join(relative(process.cwd(), argv.dir));
62
52
  const zuploPreferredConfigFile = join(normalizedDir, ZUPLO_SYSTEM_ENV_VAR);
63
- const environmentResponseFromDeveloperAPI = await fetch(`${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/environments/${argv.environment}/local-configurations`, {
53
+ const environmentResponseFromDeveloperAPI = await fetch(`${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/environments/${argv.environment}/configurations`, {
64
54
  headers: {
65
55
  authorization: `Bearer ${argv.authToken}`,
66
56
  },
@@ -79,43 +69,16 @@ export async function pullSystemConfig(argv) {
79
69
  }
80
70
  }
81
71
  const payload = await environmentResponseFromDeveloperAPI.json();
82
- const userEnvVars = Object.keys(payload)
83
- .filter((key) => {
84
- return !key.startsWith("ZUPLO_PUBLIC_") && !SYSTEM_KEYS.includes(key);
85
- })
86
- .map((key) => {
87
- return `${key}=${wrapEnvValue(payload[key])}`;
88
- });
89
- const zuploPublicEnvVars = Object.keys(payload)
90
- .filter((key) => {
91
- return key.startsWith("ZUPLO_PUBLIC_");
92
- })
93
- .map((key) => {
94
- return `${key}=${wrapEnvValue(payload[key])}`;
95
- });
96
- let content = `# This file is auto-generated from zuplo link. Please do not edit it manually.
72
+ const content = `
73
+ # This file is auto-generated from zuplo link. Please do not edit it manually.
97
74
  # It will be auto-generated afresh the next time you run zuplo link.
98
75
  # If you wish to add your own environment variables, create a separate .env file.
99
76
 
100
77
  ZUPLO_ACCOUNT_NAME=${payload.accountName}
101
78
  ZUPLO_PROJECT_NAME=${payload.projectName}
102
79
  ZUPLO_ENVIRONMENT_TYPE=${payload.environmentType}
103
- ZUPLO_SYSTEM_CONFIGURATIONS=${payload["systemConfigurations"]}`;
104
- if (zuploPublicEnvVars.length > 0) {
105
- content += `
106
-
107
- # Public Zuplo environment variables
108
- ${zuploPublicEnvVars.join("\n")}`;
109
- }
110
- if (userEnvVars.length > 0) {
111
- content += `
112
-
113
- # Environment variables defined in the Zuplo UI for the environment
114
- # Note that " characters are escaped with a backslash and escaped double quotes
115
- # will show up as \\" in the value.
116
- ${userEnvVars.join("\n")}`;
117
- }
118
- content += "\n";
80
+ ZUPLO_SYSTEM_CONFIGURATIONS=${payload["systemConfigurations"]}
81
+ `;
119
82
  await writeFile(zuploPreferredConfigFile, content);
120
83
  }
121
84
  //# sourceMappingURL=populate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"populate.js","sourceRoot":"","sources":["../../src/link/populate.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport { applyEdits, modify } from \"jsonc-parser\";\nimport { format } from \"prettier\";\nimport {\n ZUPLO_FALLBACK_JSON_FILE,\n ZUPLO_PREFERRED_JSON_FILE,\n ZUPLO_SYSTEM_ENV_VAR,\n} from \"../common/constants.js\";\nimport { logger } from \"../common/logger.js\";\nimport { printCriticalFailureToConsoleAndExit } from \"../common/output.js\";\nimport settings from \"../common/settings.js\";\nimport { Arguments } from \"../link/handler.js\";\n\nconst SYSTEM_KEYS = [\n \"accountName\",\n \"projectName\",\n \"environmentType\",\n \"systemConfigurations\",\n];\n\n/**\n * Wraps environment variable values in single quotes for dotenv compatibility.\n * Escapes any single quotes within the value by replacing them with \\'.\n * This ensures proper parsing while preserving special characters.\n *\n * @param value The environment variable value to wrap\n * @returns The value wrapped in single quotes with escaped single quotes\n */\nfunction wrapEnvValue(value: string): string {\n // Escape single quotes by replacing them with \\'\n const escapedValue = value.replace(/\"/g, '\\\\\"');\n // Always use single quotes\n return `\"${escapedValue}\"`;\n}\n\nexport async function safeMergeConfig(dir: string, project: string) {\n const normalizedDir = join(relative(process.cwd(), dir));\n const zuploPreferredConfigFile = join(\n normalizedDir,\n ZUPLO_PREFERRED_JSON_FILE\n );\n const zuploFallbackConfigFile = join(normalizedDir, ZUPLO_FALLBACK_JSON_FILE);\n if (existsSync(zuploPreferredConfigFile)) {\n // Process the file, while respecting comments\n // Apply the edits sequentially\n const originalContents = await readFile(zuploPreferredConfigFile, \"utf-8\");\n const modifyProjectEdit = modify(originalContents, [\"project\"], project, {\n getInsertionIndex: () => 2,\n });\n const contentsPostProject = applyEdits(originalContents, modifyProjectEdit);\n const formatted = await format(contentsPostProject, {\n parser: \"json\",\n quoteProps: \"as-needed\",\n });\n await writeFile(zuploPreferredConfigFile, formatted);\n } else if (existsSync(zuploFallbackConfigFile)) {\n // Copy the file over\n const config = JSON.parse(await readFile(zuploFallbackConfigFile, \"utf-8\"));\n const formatted = await format(\n JSON.stringify({\n version: 1,\n project: project,\n compatibilityDate: config.compatibilityDate,\n }),\n {\n parser: \"json\",\n quoteProps: \"as-needed\",\n }\n );\n await writeFile(zuploPreferredConfigFile, formatted);\n } else {\n // Create new file\n const formatted = await format(\n JSON.stringify({\n version: 1,\n project: project,\n compatibilityDate: \"2023-03-14\",\n }),\n {\n parser: \"json\",\n quoteProps: \"as-needed\",\n }\n );\n await writeFile(zuploPreferredConfigFile, formatted);\n }\n}\n\nexport async function pullSystemConfig(\n argv: Pick<Arguments, \"dir\" | \"environment\" | \"authToken\">\n) {\n const normalizedDir = join(relative(process.cwd(), argv.dir));\n const zuploPreferredConfigFile = join(normalizedDir, ZUPLO_SYSTEM_ENV_VAR);\n\n const environmentResponseFromDeveloperAPI = await fetch(\n `${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/environments/${argv.environment}/local-configurations`,\n {\n headers: {\n authorization: `Bearer ${argv.authToken}`,\n },\n }\n );\n\n if (!environmentResponseFromDeveloperAPI.ok) {\n if (\n environmentResponseFromDeveloperAPI.status === 404 ||\n environmentResponseFromDeveloperAPI.status === 401\n ) {\n await printCriticalFailureToConsoleAndExit(\n \"Error: Failed to link data from the environment. The environment you specified doesn't exist or you don't have access to it.\"\n );\n } else {\n logger.error(\n {\n status: environmentResponseFromDeveloperAPI.status,\n statusText: environmentResponseFromDeveloperAPI.statusText,\n },\n `Failed to link data from ${argv.environment}`\n );\n await printCriticalFailureToConsoleAndExit(\n \"Error: Failed to link data from the environment. Please try again later.\"\n );\n }\n }\n\n const payload = await environmentResponseFromDeveloperAPI.json();\n\n // We wrap the env vars in appropriate quotes to preserve special characters\n // and handle single quotes properly for dotenv parsing.\n const userEnvVars = Object.keys(payload)\n .filter((key) => {\n return !key.startsWith(\"ZUPLO_PUBLIC_\") && !SYSTEM_KEYS.includes(key);\n })\n .map((key) => {\n return `${key}=${wrapEnvValue(payload[key])}`;\n });\n\n const zuploPublicEnvVars = Object.keys(payload)\n .filter((key) => {\n // These start with ZUPLO_PUBLIC_ and are not system variables\n return key.startsWith(\"ZUPLO_PUBLIC_\");\n })\n .map((key) => {\n return `${key}=${wrapEnvValue(payload[key])}`;\n });\n\n let content = `# This file is auto-generated from zuplo link. Please do not edit it manually.\n# It will be auto-generated afresh the next time you run zuplo link.\n# If you wish to add your own environment variables, create a separate .env file.\n\nZUPLO_ACCOUNT_NAME=${payload.accountName}\nZUPLO_PROJECT_NAME=${payload.projectName}\nZUPLO_ENVIRONMENT_TYPE=${payload.environmentType}\nZUPLO_SYSTEM_CONFIGURATIONS=${payload[\"systemConfigurations\"]}`;\n\n // Only add public environment variables section if there are any\n if (zuploPublicEnvVars.length > 0) {\n content += `\n\n# Public Zuplo environment variables\n${zuploPublicEnvVars.join(\"\\n\")}`;\n }\n\n // Only add user environment variables section if there are any\n if (userEnvVars.length > 0) {\n content += `\n\n# Environment variables defined in the Zuplo UI for the environment\n# Note that \" characters are escaped with a backslash and escaped double quotes\n# will show up as \\\\\" in the value.\n${userEnvVars.join(\"\\n\")}`;\n }\n\n // Add final newline\n content += \"\\n\";\n\n await writeFile(zuploPreferredConfigFile, content);\n}\n"]}
1
+ {"version":3,"file":"populate.js","sourceRoot":"","sources":["../../src/link/populate.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport { applyEdits, modify } from \"jsonc-parser\";\nimport { format } from \"prettier\";\nimport {\n ZUPLO_FALLBACK_JSON_FILE,\n ZUPLO_PREFERRED_JSON_FILE,\n ZUPLO_SYSTEM_ENV_VAR,\n} from \"../common/constants.js\";\nimport { logger } from \"../common/logger.js\";\nimport { printCriticalFailureToConsoleAndExit } from \"../common/output.js\";\nimport settings from \"../common/settings.js\";\nimport { Arguments } from \"../link/handler.js\";\n\nexport async function safeMergeConfig(dir: string, project: string) {\n const normalizedDir = join(relative(process.cwd(), dir));\n const zuploPreferredConfigFile = join(\n normalizedDir,\n ZUPLO_PREFERRED_JSON_FILE\n );\n const zuploFallbackConfigFile = join(normalizedDir, ZUPLO_FALLBACK_JSON_FILE);\n if (existsSync(zuploPreferredConfigFile)) {\n // Process the file, while respecting comments\n // Apply the edits sequentially\n const originalContents = await readFile(zuploPreferredConfigFile, \"utf-8\");\n const modifyProjectEdit = modify(originalContents, [\"project\"], project, {\n getInsertionIndex: () => 2,\n });\n const contentsPostProject = applyEdits(originalContents, modifyProjectEdit);\n const formatted = await format(contentsPostProject, {\n parser: \"json\",\n quoteProps: \"as-needed\",\n });\n await writeFile(zuploPreferredConfigFile, formatted);\n } else if (existsSync(zuploFallbackConfigFile)) {\n // Copy the file over\n const config = JSON.parse(await readFile(zuploFallbackConfigFile, \"utf-8\"));\n const formatted = await format(\n JSON.stringify({\n version: 1,\n project: project,\n compatibilityDate: config.compatibilityDate,\n }),\n {\n parser: \"json\",\n quoteProps: \"as-needed\",\n }\n );\n await writeFile(zuploPreferredConfigFile, formatted);\n } else {\n // Create new file\n const formatted = await format(\n JSON.stringify({\n version: 1,\n project: project,\n compatibilityDate: \"2023-03-14\",\n }),\n {\n parser: \"json\",\n quoteProps: \"as-needed\",\n }\n );\n await writeFile(zuploPreferredConfigFile, formatted);\n }\n}\n\nexport async function pullSystemConfig(\n argv: Pick<Arguments, \"dir\" | \"environment\" | \"authToken\">\n) {\n const normalizedDir = join(relative(process.cwd(), argv.dir));\n const zuploPreferredConfigFile = join(normalizedDir, ZUPLO_SYSTEM_ENV_VAR);\n\n const environmentResponseFromDeveloperAPI = await fetch(\n `${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/environments/${argv.environment}/configurations`,\n {\n headers: {\n authorization: `Bearer ${argv.authToken}`,\n },\n }\n );\n\n if (!environmentResponseFromDeveloperAPI.ok) {\n if (\n environmentResponseFromDeveloperAPI.status === 404 ||\n environmentResponseFromDeveloperAPI.status === 401\n ) {\n await printCriticalFailureToConsoleAndExit(\n \"Error: Failed to link data from the environment. The environment you specified doesn't exist or you don't have access to it.\"\n );\n } else {\n logger.error(\n {\n status: environmentResponseFromDeveloperAPI.status,\n statusText: environmentResponseFromDeveloperAPI.statusText,\n },\n `Failed to link data from ${argv.environment}`\n );\n await printCriticalFailureToConsoleAndExit(\n \"Error: Failed to link data from the environment. Please try again later.\"\n );\n }\n }\n\n const payload = await environmentResponseFromDeveloperAPI.json();\n\n const content = `\n# This file is auto-generated from zuplo link. Please do not edit it manually.\n# It will be auto-generated afresh the next time you run zuplo link.\n# If you wish to add your own environment variables, create a separate .env file.\n\nZUPLO_ACCOUNT_NAME=${payload.accountName}\nZUPLO_PROJECT_NAME=${payload.projectName}\nZUPLO_ENVIRONMENT_TYPE=${payload.environmentType}\nZUPLO_SYSTEM_CONFIGURATIONS=${payload[\"systemConfigurations\"]}\n`;\n\n await writeFile(zuploPreferredConfigFile, content);\n}\n"]}
@@ -1 +1 @@
1
- {"root":["../src/cli.ts","../src/types.d.ts","../src/__tests__/archive-utils.test.ts","../src/__tests__/engine.test.ts","../src/__tests__/import-openapi-utils.test.ts","../src/__tests__/import-openapi.test.ts","../src/__tests__/oas-test-data.ts","../src/__tests__/outdated.test.ts","../src/__tests__/populate.test.ts","../src/__tests__/tsconfig-upgrader.test.ts","../src/__tests__/integration/delete.integration.test.ts","../src/__tests__/integration/deploy.integration.test.ts","../src/__tests__/integration/jest-mocks-setup.ts","../src/__tests__/integration/jest-setup.ts","../src/__tests__/integration/link.integration.test.ts","../src/__tests__/integration/list.integration.test.ts","../src/__tests__/integration/test-utils.ts","../src/__tests__/integration/tunnel.integration.test.ts","../src/__tests__/integration/variable.integration.test.ts","../src/build/handler.ts","../src/cmds/build.ts","../src/cmds/compile.ts","../src/cmds/convert.ts","../src/cmds/delete.ts","../src/cmds/deploy.ts","../src/cmds/dev.ts","../src/cmds/editor.ts","../src/cmds/link.ts","../src/cmds/list.ts","../src/cmds/login.ts","../src/cmds/test.ts","../src/cmds/project/import-openapi.ts","../src/cmds/project/index.ts","../src/cmds/project/update.ts","../src/cmds/source/import-openapi.ts","../src/cmds/source/index.ts","../src/cmds/source/upgrade.ts","../src/cmds/tunnel/create.ts","../src/cmds/tunnel/delete.ts","../src/cmds/tunnel/describe.ts","../src/cmds/tunnel/index.ts","../src/cmds/tunnel/list.ts","../src/cmds/tunnel/rotate-token.ts","../src/cmds/tunnel/services/describe.ts","../src/cmds/tunnel/services/index.ts","../src/cmds/tunnel/services/update.ts","../src/cmds/variable/create.ts","../src/cmds/variable/index.ts","../src/cmds/variable/update.ts","../src/common/alias.ts","../src/common/args.ts","../src/common/constants.ts","../src/common/handler.ts","../src/common/logger.ts","../src/common/models.ts","../src/common/outdated.ts","../src/common/output.ts","../src/common/settings.ts","../src/common/worker-output.ts","../src/common/analytics/lib.ts","../src/common/api/lib.ts","../src/common/machine-id/lib.ts","../src/common/middleware/authentication.ts","../src/common/middleware/get-account-param.ts","../src/common/middleware/get-environment-param.ts","../src/common/middleware/get-project-param.ts","../src/common/middleware/logging.ts","../src/common/middleware/user-configuration.ts","../src/common/middleware/user-identification.ts","../src/common/upgraders/lib.ts","../src/common/upgraders/package-json-upgrader.ts","../src/common/upgraders/tsconfig-upgrader.ts","../src/common/upgraders/vscode-settings-json-upgrader.ts","../src/common/utils/box.ts","../src/common/utils/ports.ts","../src/common/utils/pretty-print-environment-prompt.ts","../src/common/utils/types.ts","../src/common/utils/urls.ts","../src/common/validators/file-system-validator.ts","../src/common/validators/lib.ts","../src/common/validators/project-name-validator.ts","../src/common/xdg/lib.ts","../src/compile/handler.ts","../src/convert/engine.ts","../src/convert/handler.ts","../src/convert/routes.legacy.ts","../src/delete/handler.ts","../src/delete/poll-deployment.ts","../src/deploy/archive.ts","../src/deploy/environments.ts","../src/deploy/file-upload.ts","../src/deploy/handler.ts","../src/deploy/poll-deployment.ts","../src/dev/handler.ts","../src/editor/handler.ts","../src/editor/assets/index-7e947de6.js","../src/editor/server/cors-plugin.ts","../src/editor/server/server.ts","../src/editor/server/xfs.ts","../src/link/handler.ts","../src/link/populate.ts","../src/list/handler.ts","../src/login/html.ts","../src/login/login.ts","../src/login/server.ts","../src/login/tokens.ts","../src/project/import-openapi/handler.ts","../src/project/import-openapi/interfaces.ts","../src/project/import-openapi/utils.ts","../src/project/update/handler.ts","../src/test/esbuild-config.ts","../src/test/handler.ts","../src/test/invoke-test.ts","../src/test/test-files.test.ts","../src/test/test-files.ts","../src/test/esbuild-plugins/node-test-prep-plugin.ts","../src/tunnel/models.ts","../src/tunnel/create/handler.ts","../src/tunnel/delete/handler.ts","../src/tunnel/delete/poll-teardown-operation.ts","../src/tunnel/describe/handler.ts","../src/tunnel/list/handler.ts","../src/tunnel/rotate-token/handler.ts","../src/tunnel/services/describe/handler.ts","../src/tunnel/services/update/handler.ts","../src/tunnel/services/update/poll-provisioning-operations.ts","../src/variable/models.ts","../src/variable/create/handler.ts","../src/variable/update/handler.ts"],"version":"5.8.2"}
1
+ {"root":["../src/cli.ts","../src/types.d.ts","../src/__tests__/archive-utils.test.ts","../src/__tests__/engine.test.ts","../src/__tests__/import-openapi-utils.test.ts","../src/__tests__/import-openapi.test.ts","../src/__tests__/oas-test-data.ts","../src/__tests__/outdated.test.ts","../src/__tests__/tsconfig-upgrader.test.ts","../src/__tests__/integration/delete.integration.test.ts","../src/__tests__/integration/deploy.integration.test.ts","../src/__tests__/integration/jest-mocks-setup.ts","../src/__tests__/integration/jest-setup.ts","../src/__tests__/integration/link.integration.test.ts","../src/__tests__/integration/list.integration.test.ts","../src/__tests__/integration/test-utils.ts","../src/__tests__/integration/tunnel.integration.test.ts","../src/__tests__/integration/variable.integration.test.ts","../src/build/handler.ts","../src/cmds/build.ts","../src/cmds/compile.ts","../src/cmds/convert.ts","../src/cmds/delete.ts","../src/cmds/deploy.ts","../src/cmds/dev.ts","../src/cmds/editor.ts","../src/cmds/link.ts","../src/cmds/list.ts","../src/cmds/login.ts","../src/cmds/test.ts","../src/cmds/project/import-openapi.ts","../src/cmds/project/index.ts","../src/cmds/project/update.ts","../src/cmds/source/import-openapi.ts","../src/cmds/source/index.ts","../src/cmds/source/upgrade.ts","../src/cmds/tunnel/create.ts","../src/cmds/tunnel/delete.ts","../src/cmds/tunnel/describe.ts","../src/cmds/tunnel/index.ts","../src/cmds/tunnel/list.ts","../src/cmds/tunnel/rotate-token.ts","../src/cmds/tunnel/services/describe.ts","../src/cmds/tunnel/services/index.ts","../src/cmds/tunnel/services/update.ts","../src/cmds/variable/create.ts","../src/cmds/variable/index.ts","../src/cmds/variable/update.ts","../src/common/alias.ts","../src/common/args.ts","../src/common/constants.ts","../src/common/handler.ts","../src/common/logger.ts","../src/common/models.ts","../src/common/outdated.ts","../src/common/output.ts","../src/common/settings.ts","../src/common/worker-output.ts","../src/common/analytics/lib.ts","../src/common/api/lib.ts","../src/common/machine-id/lib.ts","../src/common/middleware/authentication.ts","../src/common/middleware/get-account-param.ts","../src/common/middleware/get-environment-param.ts","../src/common/middleware/get-project-param.ts","../src/common/middleware/logging.ts","../src/common/middleware/user-configuration.ts","../src/common/middleware/user-identification.ts","../src/common/upgraders/lib.ts","../src/common/upgraders/package-json-upgrader.ts","../src/common/upgraders/tsconfig-upgrader.ts","../src/common/upgraders/vscode-settings-json-upgrader.ts","../src/common/utils/box.ts","../src/common/utils/ports.ts","../src/common/utils/pretty-print-environment-prompt.ts","../src/common/utils/types.ts","../src/common/utils/urls.ts","../src/common/validators/file-system-validator.ts","../src/common/validators/lib.ts","../src/common/validators/project-name-validator.ts","../src/common/xdg/lib.ts","../src/compile/handler.ts","../src/convert/engine.ts","../src/convert/handler.ts","../src/convert/routes.legacy.ts","../src/delete/handler.ts","../src/delete/poll-deployment.ts","../src/deploy/archive.ts","../src/deploy/environments.ts","../src/deploy/file-upload.ts","../src/deploy/handler.ts","../src/deploy/poll-deployment.ts","../src/dev/handler.ts","../src/editor/handler.ts","../src/editor/assets/index-7e947de6.js","../src/editor/server/cors-plugin.ts","../src/editor/server/server.ts","../src/editor/server/xfs.ts","../src/link/handler.ts","../src/link/populate.ts","../src/list/handler.ts","../src/login/html.ts","../src/login/login.ts","../src/login/server.ts","../src/login/tokens.ts","../src/project/import-openapi/handler.ts","../src/project/import-openapi/interfaces.ts","../src/project/import-openapi/utils.ts","../src/project/update/handler.ts","../src/test/esbuild-config.ts","../src/test/handler.ts","../src/test/invoke-test.ts","../src/test/test-files.test.ts","../src/test/test-files.ts","../src/test/esbuild-plugins/node-test-prep-plugin.ts","../src/tunnel/models.ts","../src/tunnel/create/handler.ts","../src/tunnel/delete/handler.ts","../src/tunnel/delete/poll-teardown-operation.ts","../src/tunnel/describe/handler.ts","../src/tunnel/list/handler.ts","../src/tunnel/rotate-token/handler.ts","../src/tunnel/services/describe/handler.ts","../src/tunnel/services/update/handler.ts","../src/tunnel/services/update/poll-provisioning-operations.ts","../src/variable/models.ts","../src/variable/create/handler.ts","../src/variable/update/handler.ts"],"version":"5.8.2"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zuplo/cli",
3
- "version": "6.52.22",
3
+ "version": "6.52.23",
4
4
  "repository": "https://github.com/zuplo/zuplo",
5
5
  "author": "Zuplo, Inc.",
6
6
  "type": "module",
@@ -29,9 +29,9 @@
29
29
  "@opentelemetry/api": "1.9.0",
30
30
  "@sentry/node": "9.22.0",
31
31
  "@swc/core": "1.10.18",
32
- "@zuplo/core": "6.52.22",
33
- "@zuplo/openapi-tools": "6.52.22",
34
- "@zuplo/runtime": "6.52.22",
32
+ "@zuplo/core": "6.52.23",
33
+ "@zuplo/openapi-tools": "6.52.23",
34
+ "@zuplo/runtime": "6.52.23",
35
35
  "as-table": "1.0.55",
36
36
  "chalk": "5.4.1",
37
37
  "chokidar": "3.5.3",
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=populate.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"populate.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/populate.test.ts"],"names":[],"mappings":""}
@@ -1,315 +0,0 @@
1
- import { beforeEach, describe, it } from "node:test";
2
- import { assert } from "chai";
3
- import { MockAgent, setGlobalDispatcher } from "undici";
4
- import { readFile, rm, mkdir } from "node:fs/promises";
5
- import { join } from "node:path";
6
- import { tmpdir } from "node:os";
7
- import { parse as dotenvParse } from "dotenv";
8
- const TEST_ENVIRONMENT = "test-env-123";
9
- const TEST_AUTH_TOKEN = "test-auth-token-456";
10
- const TEST_API_ENDPOINT = "https://dev.zuplo.com";
11
- describe("pullSystemConfig", () => {
12
- let mockAgent;
13
- let testDir;
14
- beforeEach(async () => {
15
- mockAgent = new MockAgent();
16
- mockAgent.disableNetConnect();
17
- setGlobalDispatcher(mockAgent);
18
- testDir = join(tmpdir(), `zuplo-test-${Date.now()}`);
19
- await mkdir(testDir, { recursive: true });
20
- });
21
- it("should generate correct .env.zuplo file with system variables", async () => {
22
- const mockPayload = {
23
- accountName: "test-account",
24
- projectName: "test-project",
25
- environmentType: "development",
26
- systemConfigurations: "config-123",
27
- };
28
- mockAgent
29
- .get(TEST_API_ENDPOINT)
30
- .intercept({
31
- path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,
32
- method: "GET",
33
- headers: {
34
- authorization: `Bearer ${TEST_AUTH_TOKEN}`,
35
- },
36
- })
37
- .reply(200, mockPayload);
38
- const { pullSystemConfig } = await import("../link/populate.js");
39
- await pullSystemConfig({
40
- dir: testDir,
41
- environment: TEST_ENVIRONMENT,
42
- authToken: TEST_AUTH_TOKEN,
43
- });
44
- const envFilePath = join(testDir, ".env.zuplo");
45
- const content = await readFile(envFilePath, "utf-8");
46
- assert.include(content, "# This file is auto-generated from zuplo link. Please do not edit it manually.");
47
- assert.include(content, "# It will be auto-generated afresh the next time you run zuplo link.");
48
- assert.include(content, "# If you wish to add your own environment variables, create a separate .env file.");
49
- assert.include(content, "ZUPLO_ACCOUNT_NAME=test-account");
50
- assert.include(content, "ZUPLO_PROJECT_NAME=test-project");
51
- assert.include(content, "ZUPLO_ENVIRONMENT_TYPE=development");
52
- assert.include(content, "ZUPLO_SYSTEM_CONFIGURATIONS=config-123");
53
- assert.notInclude(content, "# Public Zuplo environment variables");
54
- assert.notInclude(content, "# Environment variables defined in the Zuplo UI for the environment");
55
- await rm(testDir, { recursive: true, force: true });
56
- });
57
- it("should handle payload with public and user environment variables", async () => {
58
- const mockPayload = {
59
- accountName: "my-account",
60
- projectName: "my-project",
61
- environmentType: "production",
62
- systemConfigurations: "prod-config-456",
63
- ZUPLO_PUBLIC_API_URL: "https://api.example.com",
64
- ZUPLO_PUBLIC_DOCS_URL: "https://docs.example.com",
65
- DATABASE_URL: "postgres://localhost:5432/db",
66
- API_KEY: "secret-key-123",
67
- REDIS_URL: "redis://localhost:6379",
68
- };
69
- mockAgent
70
- .get(TEST_API_ENDPOINT)
71
- .intercept({
72
- path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,
73
- method: "GET",
74
- headers: {
75
- authorization: `Bearer ${TEST_AUTH_TOKEN}`,
76
- },
77
- })
78
- .reply(200, mockPayload);
79
- const { pullSystemConfig } = await import("../link/populate.js");
80
- await pullSystemConfig({
81
- dir: testDir,
82
- environment: TEST_ENVIRONMENT,
83
- authToken: TEST_AUTH_TOKEN,
84
- });
85
- const envFilePath = join(testDir, ".env.zuplo");
86
- const content = await readFile(envFilePath, "utf-8");
87
- assert.include(content, "ZUPLO_ACCOUNT_NAME=my-account");
88
- assert.include(content, "ZUPLO_PROJECT_NAME=my-project");
89
- assert.include(content, "ZUPLO_ENVIRONMENT_TYPE=production");
90
- assert.include(content, "ZUPLO_SYSTEM_CONFIGURATIONS=prod-config-456");
91
- const publicSectionRegex = /# Public Zuplo environment variables[\s\S]*?(?=# Environment variables defined in the Zuplo UI|$)/;
92
- const publicSection = content.match(publicSectionRegex)?.[0] || "";
93
- assert.include(publicSection, 'ZUPLO_PUBLIC_API_URL="https://api.example.com"');
94
- assert.include(publicSection, 'ZUPLO_PUBLIC_DOCS_URL="https://docs.example.com"');
95
- const userSectionRegex = /# Environment variables defined in the Zuplo UI[\s\S]*$/;
96
- const userSection = content.match(userSectionRegex)?.[0] || "";
97
- assert.include(userSection, 'DATABASE_URL="postgres://localhost:5432/db"');
98
- assert.include(userSection, 'API_KEY="secret-key-123"');
99
- assert.include(userSection, 'REDIS_URL="redis://localhost:6379"');
100
- assert.notInclude(userSection, "accountName=");
101
- assert.notInclude(userSection, "projectName=");
102
- assert.notInclude(userSection, "environmentType=");
103
- assert.notInclude(userSection, "systemConfigurations=");
104
- await rm(testDir, { recursive: true, force: true });
105
- });
106
- it("should handle empty environment variables gracefully", async () => {
107
- const mockPayload = {
108
- accountName: "test-account",
109
- projectName: "test-project",
110
- environmentType: "development",
111
- systemConfigurations: "config-789",
112
- };
113
- mockAgent
114
- .get(TEST_API_ENDPOINT)
115
- .intercept({
116
- path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,
117
- method: "GET",
118
- headers: {
119
- authorization: `Bearer ${TEST_AUTH_TOKEN}`,
120
- },
121
- })
122
- .reply(200, mockPayload);
123
- const { pullSystemConfig } = await import("../link/populate.js");
124
- await pullSystemConfig({
125
- dir: testDir,
126
- environment: TEST_ENVIRONMENT,
127
- authToken: TEST_AUTH_TOKEN,
128
- });
129
- const envFilePath = join(testDir, ".env.zuplo");
130
- const content = await readFile(envFilePath, "utf-8");
131
- assert.include(content, "ZUPLO_ACCOUNT_NAME=test-account");
132
- assert.include(content, "ZUPLO_PROJECT_NAME=test-project");
133
- assert.include(content, "ZUPLO_ENVIRONMENT_TYPE=development");
134
- assert.include(content, "ZUPLO_SYSTEM_CONFIGURATIONS=config-789");
135
- assert.notInclude(content, "# Public Zuplo environment variables");
136
- assert.notInclude(content, "# Environment variables defined in the Zuplo UI for the environment");
137
- await rm(testDir, { recursive: true, force: true });
138
- });
139
- it("should handle special characters in environment variable values", async () => {
140
- const mockPayload = {
141
- accountName: "test-account",
142
- projectName: "test-project",
143
- environmentType: "development",
144
- systemConfigurations: "config-123",
145
- DATABASE_URL: "postgres://user:p@ssw0rd!@localhost:5432/db?ssl=true",
146
- API_SECRET: "secret-with-special-chars!@#$%^&*()",
147
- JSON_CONFIG: '{"key": "value", "nested": {"array": [1, 2, 3]}}',
148
- };
149
- mockAgent
150
- .get(TEST_API_ENDPOINT)
151
- .intercept({
152
- path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,
153
- method: "GET",
154
- headers: {
155
- authorization: `Bearer ${TEST_AUTH_TOKEN}`,
156
- },
157
- })
158
- .reply(200, mockPayload);
159
- const { pullSystemConfig } = await import("../link/populate.js");
160
- await pullSystemConfig({
161
- dir: testDir,
162
- environment: TEST_ENVIRONMENT,
163
- authToken: TEST_AUTH_TOKEN,
164
- });
165
- const envFilePath = join(testDir, ".env.zuplo");
166
- const content = await readFile(envFilePath, "utf-8");
167
- assert.include(content, 'DATABASE_URL="postgres://user:p@ssw0rd!@localhost:5432/db?ssl=true"');
168
- assert.include(content, 'API_SECRET="secret-with-special-chars!@#$%^&*()"');
169
- assert.include(content, 'JSON_CONFIG="{\\"key\\": \\"value\\", \\"nested\\": {\\"array\\": [1, 2, 3]}}"');
170
- const userSectionRegex = /# Environment variables defined in the Zuplo UI[\s\S]*$/;
171
- const userSection = content.match(userSectionRegex)?.[0] || "";
172
- assert.include(userSection, 'DATABASE_URL="postgres://user:p@ssw0rd!@localhost:5432/db?ssl=true"');
173
- assert.include(userSection, 'API_SECRET="secret-with-special-chars!@#$%^&*()"');
174
- assert.include(userSection, 'JSON_CONFIG="{\\"key\\": \\"value\\", \\"nested\\": {\\"array\\": [1, 2, 3]}}"');
175
- await rm(testDir, { recursive: true, force: true });
176
- });
177
- it("should only include public variables section when public variables exist", async () => {
178
- const mockPayload = {
179
- accountName: "test-account",
180
- projectName: "test-project",
181
- environmentType: "development",
182
- systemConfigurations: "config-123",
183
- ZUPLO_PUBLIC_API_URL: "https://api.example.com",
184
- ZUPLO_PUBLIC_DOCS_URL: "https://docs.example.com",
185
- };
186
- mockAgent
187
- .get(TEST_API_ENDPOINT)
188
- .intercept({
189
- path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,
190
- method: "GET",
191
- headers: {
192
- authorization: `Bearer ${TEST_AUTH_TOKEN}`,
193
- },
194
- })
195
- .reply(200, mockPayload);
196
- const { pullSystemConfig } = await import("../link/populate.js");
197
- await pullSystemConfig({
198
- dir: testDir,
199
- environment: TEST_ENVIRONMENT,
200
- authToken: TEST_AUTH_TOKEN,
201
- });
202
- const envFilePath = join(testDir, ".env.zuplo");
203
- const content = await readFile(envFilePath, "utf-8");
204
- assert.include(content, "ZUPLO_ACCOUNT_NAME=test-account");
205
- assert.include(content, "ZUPLO_PROJECT_NAME=test-project");
206
- assert.include(content, "ZUPLO_ENVIRONMENT_TYPE=development");
207
- assert.include(content, "ZUPLO_SYSTEM_CONFIGURATIONS=config-123");
208
- assert.include(content, "# Public Zuplo environment variables");
209
- assert.include(content, 'ZUPLO_PUBLIC_API_URL="https://api.example.com"');
210
- assert.include(content, 'ZUPLO_PUBLIC_DOCS_URL="https://docs.example.com"');
211
- assert.notInclude(content, "# Environment variables defined in the Zuplo UI for the environment");
212
- await rm(testDir, { recursive: true, force: true });
213
- });
214
- it("should only include user variables section when user variables exist", async () => {
215
- const mockPayload = {
216
- accountName: "test-account",
217
- projectName: "test-project",
218
- environmentType: "development",
219
- systemConfigurations: "config-123",
220
- DATABASE_URL: "postgres://localhost:5432/db",
221
- API_KEY: "secret-key-123",
222
- };
223
- mockAgent
224
- .get(TEST_API_ENDPOINT)
225
- .intercept({
226
- path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,
227
- method: "GET",
228
- headers: {
229
- authorization: `Bearer ${TEST_AUTH_TOKEN}`,
230
- },
231
- })
232
- .reply(200, mockPayload);
233
- const { pullSystemConfig } = await import("../link/populate.js");
234
- await pullSystemConfig({
235
- dir: testDir,
236
- environment: TEST_ENVIRONMENT,
237
- authToken: TEST_AUTH_TOKEN,
238
- });
239
- const envFilePath = join(testDir, ".env.zuplo");
240
- const content = await readFile(envFilePath, "utf-8");
241
- assert.include(content, "ZUPLO_ACCOUNT_NAME=test-account");
242
- assert.include(content, "ZUPLO_PROJECT_NAME=test-project");
243
- assert.include(content, "ZUPLO_ENVIRONMENT_TYPE=development");
244
- assert.include(content, "ZUPLO_SYSTEM_CONFIGURATIONS=config-123");
245
- assert.notInclude(content, "# Public Zuplo environment variables");
246
- assert.include(content, "# Environment variables defined in the Zuplo UI for the environment");
247
- assert.include(content, 'DATABASE_URL="postgres://localhost:5432/db"');
248
- assert.include(content, 'API_KEY="secret-key-123"');
249
- await rm(testDir, { recursive: true, force: true });
250
- });
251
- it("should properly escape double quotes in environment variable values for dotenv parsing", async () => {
252
- const mockPayload = {
253
- accountName: "test-account",
254
- projectName: "test-project",
255
- environmentType: "development",
256
- systemConfigurations: "config-123",
257
- DATABASE_URL: "postgres://user:p'assw'ord@localhost:5432/db",
258
- API_KEY: "secret'with'quotes",
259
- JSON_CONFIG: '{"message": "Hello \'world\'", "nested": {"value": "test\'s"}}',
260
- ZUPLO_PUBLIC_MESSAGE: "Welcome to Zuplo's platform!",
261
- COMPLEX_VALUE: "?\\\"!##==A_special_chars'''",
262
- EDGE_CASE_1: "value with 'single' and \"double\" quotes",
263
- EDGE_CASE_2: "path\\to\\file's location",
264
- EDGE_CASE_3: "it's a $pecial @#$%^&*() value!",
265
- EDGE_CASE_4: "multi\nline\nwith'quotes",
266
- EDGE_CASE_5: "tabs\tand\tspaces and 'quotes'",
267
- };
268
- mockAgent
269
- .get(TEST_API_ENDPOINT)
270
- .intercept({
271
- path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,
272
- method: "GET",
273
- headers: {
274
- authorization: `Bearer ${TEST_AUTH_TOKEN}`,
275
- },
276
- })
277
- .reply(200, mockPayload);
278
- const { pullSystemConfig } = await import("../link/populate.js");
279
- await pullSystemConfig({
280
- dir: testDir,
281
- environment: TEST_ENVIRONMENT,
282
- authToken: TEST_AUTH_TOKEN,
283
- });
284
- const envFilePath = join(testDir, ".env.zuplo");
285
- const content = await readFile(envFilePath, "utf-8");
286
- assert.include(content, "DATABASE_URL=\"postgres://user:p'assw'ord@localhost:5432/db\"");
287
- assert.include(content, "API_KEY=\"secret'with'quotes\"");
288
- assert.include(content, 'JSON_CONFIG="{\\"message\\": \\"Hello \'world\'\\", \\"nested\\": {\\"value\\": \\"test\'s\\"}}"');
289
- assert.include(content, 'ZUPLO_PUBLIC_MESSAGE="Welcome to Zuplo\'s platform!"');
290
- assert.include(content, `COMPLEX_VALUE="?\\\\"!##==A_special_chars'''"`);
291
- assert.include(content, 'EDGE_CASE_1="value with \'single\' and \\"double\\" quotes"');
292
- assert.include(content, 'EDGE_CASE_2="path\\to\\file\'s location"');
293
- assert.include(content, 'EDGE_CASE_3="it\'s a $pecial @#$%^&*() value!"');
294
- assert.include(content, 'EDGE_CASE_4="multi\nline\nwith\'quotes"');
295
- assert.include(content, "EDGE_CASE_5=\"tabs\tand\tspaces and 'quotes'\"");
296
- const fileContent = await readFile(envFilePath, "utf-8");
297
- const parsedEnv = dotenvParse(fileContent);
298
- assert.isUndefined(parsedEnv.error, "dotenv should parse the file without errors");
299
- assert.equal(parsedEnv.DATABASE_URL, mockPayload.DATABASE_URL);
300
- assert.equal(parsedEnv.API_KEY, mockPayload.API_KEY);
301
- assert.isString(parsedEnv.JSON_CONFIG);
302
- assert.include(parsedEnv.JSON_CONFIG, '\\"message\\"');
303
- assert.equal(parsedEnv.ZUPLO_PUBLIC_MESSAGE, mockPayload.ZUPLO_PUBLIC_MESSAGE);
304
- assert.isString(parsedEnv.COMPLEX_VALUE);
305
- assert.include(parsedEnv.COMPLEX_VALUE, "special_chars");
306
- assert.isString(parsedEnv.EDGE_CASE_1);
307
- assert.include(parsedEnv.EDGE_CASE_1, "single");
308
- assert.equal(parsedEnv.EDGE_CASE_2, mockPayload.EDGE_CASE_2);
309
- assert.equal(parsedEnv.EDGE_CASE_3, mockPayload.EDGE_CASE_3);
310
- assert.equal(parsedEnv.EDGE_CASE_4, mockPayload.EDGE_CASE_4);
311
- assert.equal(parsedEnv.EDGE_CASE_5, mockPayload.EDGE_CASE_5);
312
- await rm(testDir, { recursive: true, force: true });
313
- });
314
- });
315
- //# sourceMappingURL=populate.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"populate.test.js","sourceRoot":"","sources":["../../src/__tests__/populate.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Unit tests for the pullSystemConfig function\n * Focuses on testing the parsing logic and environment file generation\n */\n\nimport { beforeEach, describe, it } from \"node:test\";\nimport { assert } from \"chai\";\nimport { MockAgent, setGlobalDispatcher } from \"undici\";\nimport { readFile, rm, mkdir } from \"node:fs/promises\";\nimport { join, parse } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport { parse as dotenvParse } from \"dotenv\";\n\n// Test constants\nconst TEST_ENVIRONMENT = \"test-env-123\";\nconst TEST_AUTH_TOKEN = \"test-auth-token-456\";\nconst TEST_API_ENDPOINT = \"https://dev.zuplo.com\";\n\ndescribe(\"pullSystemConfig\", () => {\n let mockAgent: MockAgent;\n let testDir: string;\n\n beforeEach(async () => {\n // Setup HTTP mocking\n mockAgent = new MockAgent();\n mockAgent.disableNetConnect();\n setGlobalDispatcher(mockAgent);\n\n // Create a temporary directory for testing\n testDir = join(tmpdir(), `zuplo-test-${Date.now()}`);\n await mkdir(testDir, { recursive: true });\n });\n\n it(\"should generate correct .env.zuplo file with system variables\", async () => {\n const mockPayload = {\n accountName: \"test-account\",\n projectName: \"test-project\",\n environmentType: \"development\",\n systemConfigurations: \"config-123\",\n };\n\n // Mock successful API response\n mockAgent\n .get(TEST_API_ENDPOINT)\n .intercept({\n path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,\n method: \"GET\",\n headers: {\n authorization: `Bearer ${TEST_AUTH_TOKEN}`,\n },\n })\n .reply(200, mockPayload);\n\n const { pullSystemConfig } = await import(\"../link/populate.js\");\n\n await pullSystemConfig({\n dir: testDir,\n environment: TEST_ENVIRONMENT,\n authToken: TEST_AUTH_TOKEN,\n });\n\n // Verify the .env.zuplo file was created\n const envFilePath = join(testDir, \".env.zuplo\");\n const content = await readFile(envFilePath, \"utf-8\");\n\n // Verify file header\n assert.include(\n content,\n \"# This file is auto-generated from zuplo link. Please do not edit it manually.\"\n );\n assert.include(\n content,\n \"# It will be auto-generated afresh the next time you run zuplo link.\"\n );\n assert.include(\n content,\n \"# If you wish to add your own environment variables, create a separate .env file.\"\n );\n\n // Verify system variables are present\n assert.include(content, \"ZUPLO_ACCOUNT_NAME=test-account\");\n assert.include(content, \"ZUPLO_PROJECT_NAME=test-project\");\n assert.include(content, \"ZUPLO_ENVIRONMENT_TYPE=development\");\n assert.include(content, \"ZUPLO_SYSTEM_CONFIGURATIONS=config-123\");\n\n // Verify sections are NOT present when there are no variables for them\n assert.notInclude(content, \"# Public Zuplo environment variables\");\n assert.notInclude(\n content,\n \"# Environment variables defined in the Zuplo UI for the environment\"\n );\n\n // Clean up\n await rm(testDir, { recursive: true, force: true });\n });\n\n it(\"should handle payload with public and user environment variables\", async () => {\n const mockPayload = {\n accountName: \"my-account\",\n projectName: \"my-project\",\n environmentType: \"production\",\n systemConfigurations: \"prod-config-456\",\n // Public variables (should be in public section)\n ZUPLO_PUBLIC_API_URL: \"https://api.example.com\",\n ZUPLO_PUBLIC_DOCS_URL: \"https://docs.example.com\",\n // User variables (should be in user section)\n DATABASE_URL: \"postgres://localhost:5432/db\",\n API_KEY: \"secret-key-123\",\n REDIS_URL: \"redis://localhost:6379\",\n };\n\n // Mock successful API response\n mockAgent\n .get(TEST_API_ENDPOINT)\n .intercept({\n path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,\n method: \"GET\",\n headers: {\n authorization: `Bearer ${TEST_AUTH_TOKEN}`,\n },\n })\n .reply(200, mockPayload);\n\n const { pullSystemConfig } = await import(\"../link/populate.js\");\n\n await pullSystemConfig({\n dir: testDir,\n environment: TEST_ENVIRONMENT,\n authToken: TEST_AUTH_TOKEN,\n });\n\n // Read the generated file\n const envFilePath = join(testDir, \".env.zuplo\");\n const content = await readFile(envFilePath, \"utf-8\");\n\n // Verify system variables\n assert.include(content, \"ZUPLO_ACCOUNT_NAME=my-account\");\n assert.include(content, \"ZUPLO_PROJECT_NAME=my-project\");\n assert.include(content, \"ZUPLO_ENVIRONMENT_TYPE=production\");\n assert.include(content, \"ZUPLO_SYSTEM_CONFIGURATIONS=prod-config-456\");\n\n // Verify public variables are in the public section\n const publicSectionRegex =\n /# Public Zuplo environment variables[\\s\\S]*?(?=# Environment variables defined in the Zuplo UI|$)/;\n const publicSection = content.match(publicSectionRegex)?.[0] || \"\";\n assert.include(\n publicSection,\n 'ZUPLO_PUBLIC_API_URL=\"https://api.example.com\"'\n );\n assert.include(\n publicSection,\n 'ZUPLO_PUBLIC_DOCS_URL=\"https://docs.example.com\"'\n );\n\n // Verify user variables are in the user section\n const userSectionRegex =\n /# Environment variables defined in the Zuplo UI[\\s\\S]*$/;\n const userSection = content.match(userSectionRegex)?.[0] || \"\";\n assert.include(userSection, 'DATABASE_URL=\"postgres://localhost:5432/db\"');\n assert.include(userSection, 'API_KEY=\"secret-key-123\"');\n assert.include(userSection, 'REDIS_URL=\"redis://localhost:6379\"');\n\n // Verify system variables are NOT in user section\n assert.notInclude(userSection, \"accountName=\");\n assert.notInclude(userSection, \"projectName=\");\n assert.notInclude(userSection, \"environmentType=\");\n assert.notInclude(userSection, \"systemConfigurations=\");\n\n // Clean up\n await rm(testDir, { recursive: true, force: true });\n });\n\n it(\"should handle empty environment variables gracefully\", async () => {\n const mockPayload = {\n accountName: \"test-account\",\n projectName: \"test-project\",\n environmentType: \"development\",\n systemConfigurations: \"config-789\",\n // No additional environment variables\n };\n\n // Mock successful API response\n mockAgent\n .get(TEST_API_ENDPOINT)\n .intercept({\n path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,\n method: \"GET\",\n headers: {\n authorization: `Bearer ${TEST_AUTH_TOKEN}`,\n },\n })\n .reply(200, mockPayload);\n\n const { pullSystemConfig } = await import(\"../link/populate.js\");\n\n await pullSystemConfig({\n dir: testDir,\n environment: TEST_ENVIRONMENT,\n authToken: TEST_AUTH_TOKEN,\n });\n\n // Read the generated file\n const envFilePath = join(testDir, \".env.zuplo\");\n const content = await readFile(envFilePath, \"utf-8\");\n\n // Verify system variables are still present\n assert.include(content, \"ZUPLO_ACCOUNT_NAME=test-account\");\n assert.include(content, \"ZUPLO_PROJECT_NAME=test-project\");\n assert.include(content, \"ZUPLO_ENVIRONMENT_TYPE=development\");\n assert.include(content, \"ZUPLO_SYSTEM_CONFIGURATIONS=config-789\");\n\n // Verify sections are NOT present when there are no variables for them\n assert.notInclude(content, \"# Public Zuplo environment variables\");\n assert.notInclude(\n content,\n \"# Environment variables defined in the Zuplo UI for the environment\"\n );\n\n // Clean up\n await rm(testDir, { recursive: true, force: true });\n });\n\n it(\"should handle special characters in environment variable values\", async () => {\n const mockPayload = {\n accountName: \"test-account\",\n projectName: \"test-project\",\n environmentType: \"development\",\n systemConfigurations: \"config-123\",\n DATABASE_URL: \"postgres://user:p@ssw0rd!@localhost:5432/db?ssl=true\",\n API_SECRET: \"secret-with-special-chars!@#$%^&*()\",\n JSON_CONFIG: '{\"key\": \"value\", \"nested\": {\"array\": [1, 2, 3]}}',\n };\n\n // Mock successful API response\n mockAgent\n .get(TEST_API_ENDPOINT)\n .intercept({\n path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,\n method: \"GET\",\n headers: {\n authorization: `Bearer ${TEST_AUTH_TOKEN}`,\n },\n })\n .reply(200, mockPayload);\n\n const { pullSystemConfig } = await import(\"../link/populate.js\");\n\n await pullSystemConfig({\n dir: testDir,\n environment: TEST_ENVIRONMENT,\n authToken: TEST_AUTH_TOKEN,\n });\n\n // Read the generated file\n const envFilePath = join(testDir, \".env.zuplo\");\n const content = await readFile(envFilePath, \"utf-8\");\n\n // Verify special characters are preserved in environment variable values\n assert.include(\n content,\n 'DATABASE_URL=\"postgres://user:p@ssw0rd!@localhost:5432/db?ssl=true\"'\n );\n assert.include(content, 'API_SECRET=\"secret-with-special-chars!@#$%^&*()\"');\n assert.include(\n content,\n 'JSON_CONFIG=\"{\\\\\"key\\\\\": \\\\\"value\\\\\", \\\\\"nested\\\\\": {\\\\\"array\\\\\": [1, 2, 3]}}\"'\n );\n\n // Verify these are in the user section (not public section)\n const userSectionRegex =\n /# Environment variables defined in the Zuplo UI[\\s\\S]*$/;\n const userSection = content.match(userSectionRegex)?.[0] || \"\";\n assert.include(\n userSection,\n 'DATABASE_URL=\"postgres://user:p@ssw0rd!@localhost:5432/db?ssl=true\"'\n );\n assert.include(\n userSection,\n 'API_SECRET=\"secret-with-special-chars!@#$%^&*()\"'\n );\n assert.include(\n userSection,\n 'JSON_CONFIG=\"{\\\\\"key\\\\\": \\\\\"value\\\\\", \\\\\"nested\\\\\": {\\\\\"array\\\\\": [1, 2, 3]}}\"'\n );\n\n // Clean up\n await rm(testDir, { recursive: true, force: true });\n });\n\n it(\"should only include public variables section when public variables exist\", async () => {\n const mockPayload = {\n accountName: \"test-account\",\n projectName: \"test-project\",\n environmentType: \"development\",\n systemConfigurations: \"config-123\",\n // Only public variables, no user variables\n ZUPLO_PUBLIC_API_URL: \"https://api.example.com\",\n ZUPLO_PUBLIC_DOCS_URL: \"https://docs.example.com\",\n };\n\n // Mock successful API response\n mockAgent\n .get(TEST_API_ENDPOINT)\n .intercept({\n path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,\n method: \"GET\",\n headers: {\n authorization: `Bearer ${TEST_AUTH_TOKEN}`,\n },\n })\n .reply(200, mockPayload);\n\n const { pullSystemConfig } = await import(\"../link/populate.js\");\n\n await pullSystemConfig({\n dir: testDir,\n environment: TEST_ENVIRONMENT,\n authToken: TEST_AUTH_TOKEN,\n });\n\n // Read the generated file\n const envFilePath = join(testDir, \".env.zuplo\");\n const content = await readFile(envFilePath, \"utf-8\");\n\n // Verify system variables are present\n assert.include(content, \"ZUPLO_ACCOUNT_NAME=test-account\");\n assert.include(content, \"ZUPLO_PROJECT_NAME=test-project\");\n assert.include(content, \"ZUPLO_ENVIRONMENT_TYPE=development\");\n assert.include(content, \"ZUPLO_SYSTEM_CONFIGURATIONS=config-123\");\n\n // Verify public section is present with variables\n assert.include(content, \"# Public Zuplo environment variables\");\n assert.include(content, 'ZUPLO_PUBLIC_API_URL=\"https://api.example.com\"');\n assert.include(content, 'ZUPLO_PUBLIC_DOCS_URL=\"https://docs.example.com\"');\n\n // Verify user section is NOT present since there are no user variables\n assert.notInclude(\n content,\n \"# Environment variables defined in the Zuplo UI for the environment\"\n );\n\n // Clean up\n await rm(testDir, { recursive: true, force: true });\n });\n\n it(\"should only include user variables section when user variables exist\", async () => {\n const mockPayload = {\n accountName: \"test-account\",\n projectName: \"test-project\",\n environmentType: \"development\",\n systemConfigurations: \"config-123\",\n // Only user variables, no public variables\n DATABASE_URL: \"postgres://localhost:5432/db\",\n API_KEY: \"secret-key-123\",\n };\n\n // Mock successful API response\n mockAgent\n .get(TEST_API_ENDPOINT)\n .intercept({\n path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,\n method: \"GET\",\n headers: {\n authorization: `Bearer ${TEST_AUTH_TOKEN}`,\n },\n })\n .reply(200, mockPayload);\n\n const { pullSystemConfig } = await import(\"../link/populate.js\");\n\n await pullSystemConfig({\n dir: testDir,\n environment: TEST_ENVIRONMENT,\n authToken: TEST_AUTH_TOKEN,\n });\n\n // Read the generated file\n const envFilePath = join(testDir, \".env.zuplo\");\n const content = await readFile(envFilePath, \"utf-8\");\n\n // Verify system variables are present\n assert.include(content, \"ZUPLO_ACCOUNT_NAME=test-account\");\n assert.include(content, \"ZUPLO_PROJECT_NAME=test-project\");\n assert.include(content, \"ZUPLO_ENVIRONMENT_TYPE=development\");\n assert.include(content, \"ZUPLO_SYSTEM_CONFIGURATIONS=config-123\");\n\n // Verify public section is NOT present since there are no public variables\n assert.notInclude(content, \"# Public Zuplo environment variables\");\n\n // Verify user section is present with variables\n assert.include(\n content,\n \"# Environment variables defined in the Zuplo UI for the environment\"\n );\n assert.include(content, 'DATABASE_URL=\"postgres://localhost:5432/db\"');\n assert.include(content, 'API_KEY=\"secret-key-123\"');\n\n // Clean up\n await rm(testDir, { recursive: true, force: true });\n });\n\n it(\"should properly escape double quotes in environment variable values for dotenv parsing\", async () => {\n const mockPayload = {\n accountName: \"test-account\",\n projectName: \"test-project\",\n environmentType: \"development\",\n systemConfigurations: \"config-123\",\n // Environment variables with double quotes that need escaping\n DATABASE_URL: \"postgres://user:p'assw'ord@localhost:5432/db\",\n API_KEY: \"secret'with'quotes\",\n JSON_CONFIG:\n '{\"message\": \"Hello \\'world\\'\", \"nested\": {\"value\": \"test\\'s\"}}',\n ZUPLO_PUBLIC_MESSAGE: \"Welcome to Zuplo's platform!\",\n COMPLEX_VALUE: \"?\\\\\\\"!##==A_special_chars'''\",\n // Additional special character test cases\n EDGE_CASE_1: \"value with 'single' and \\\"double\\\" quotes\",\n EDGE_CASE_2: \"path\\\\to\\\\file's location\",\n EDGE_CASE_3: \"it's a $pecial @#$%^&*() value!\",\n EDGE_CASE_4: \"multi\\nline\\nwith'quotes\",\n EDGE_CASE_5: \"tabs\\tand\\tspaces and 'quotes'\",\n };\n\n // Mock successful API response\n mockAgent\n .get(TEST_API_ENDPOINT)\n .intercept({\n path: `/v1/environments/${TEST_ENVIRONMENT}/local-configurations`,\n method: \"GET\",\n headers: {\n authorization: `Bearer ${TEST_AUTH_TOKEN}`,\n },\n })\n .reply(200, mockPayload);\n\n const { pullSystemConfig } = await import(\"../link/populate.js\");\n\n await pullSystemConfig({\n dir: testDir,\n environment: TEST_ENVIRONMENT,\n authToken: TEST_AUTH_TOKEN,\n });\n\n // Read the generated file\n const envFilePath = join(testDir, \".env.zuplo\");\n const content = await readFile(envFilePath, \"utf-8\");\n\n // Verify that all values use double quotes with escaped double quotes\n assert.include(\n content,\n \"DATABASE_URL=\\\"postgres://user:p'assw'ord@localhost:5432/db\\\"\"\n );\n assert.include(content, \"API_KEY=\\\"secret'with'quotes\\\"\");\n assert.include(\n content,\n 'JSON_CONFIG=\"{\\\\\"message\\\\\": \\\\\"Hello \\'world\\'\\\\\", \\\\\"nested\\\\\": {\\\\\"value\\\\\": \\\\\"test\\'s\\\\\"}}\"'\n );\n assert.include(\n content,\n 'ZUPLO_PUBLIC_MESSAGE=\"Welcome to Zuplo\\'s platform!\"'\n );\n assert.include(content, `COMPLEX_VALUE=\"?\\\\\\\\\"!##==A_special_chars'''\"`);\n\n // Verify additional edge cases use double quotes with escaped double quotes\n assert.include(\n content,\n 'EDGE_CASE_1=\"value with \\'single\\' and \\\\\"double\\\\\" quotes\"'\n );\n assert.include(content, 'EDGE_CASE_2=\"path\\\\to\\\\file\\'s location\"');\n assert.include(content, 'EDGE_CASE_3=\"it\\'s a $pecial @#$%^&*() value!\"');\n assert.include(content, 'EDGE_CASE_4=\"multi\\nline\\nwith\\'quotes\"');\n assert.include(content, \"EDGE_CASE_5=\\\"tabs\\tand\\tspaces and 'quotes'\\\"\");\n\n // Test that dotenv can properly parse the generated file\n const fileContent = await readFile(envFilePath, \"utf-8\");\n const parsedEnv = dotenvParse(fileContent);\n\n // Verify that dotenv successfully parsed the file without errors\n assert.isUndefined(\n parsedEnv.error,\n \"dotenv should parse the file without errors\"\n );\n\n // Verify that the parsed values match the original values from the mock payload\n // Note: dotenv may not perfectly unescape all values, but should parse without errors\n assert.equal(parsedEnv.DATABASE_URL, mockPayload.DATABASE_URL);\n assert.equal(parsedEnv.API_KEY, mockPayload.API_KEY);\n // For JSON_CONFIG, dotenv doesn't unescape the quotes, so we check that it contains the escaped version\n assert.isString(parsedEnv.JSON_CONFIG);\n assert.include(parsedEnv.JSON_CONFIG, '\\\\\"message\\\\\"');\n assert.equal(\n parsedEnv.ZUPLO_PUBLIC_MESSAGE,\n mockPayload.ZUPLO_PUBLIC_MESSAGE\n );\n // For COMPLEX_VALUE, dotenv doesn't unescape the backslash-escaped quotes properly\n assert.isString(parsedEnv.COMPLEX_VALUE);\n assert.include(parsedEnv.COMPLEX_VALUE, \"special_chars\");\n\n // Verify additional edge cases are parsed correctly\n // For values with double quotes, dotenv may not unescape them perfectly\n assert.isString(parsedEnv.EDGE_CASE_1);\n assert.include(parsedEnv.EDGE_CASE_1, \"single\");\n assert.equal(parsedEnv.EDGE_CASE_2, mockPayload.EDGE_CASE_2);\n assert.equal(parsedEnv.EDGE_CASE_3, mockPayload.EDGE_CASE_3);\n assert.equal(parsedEnv.EDGE_CASE_4, mockPayload.EDGE_CASE_4);\n assert.equal(parsedEnv.EDGE_CASE_5, mockPayload.EDGE_CASE_5);\n\n // Clean up\n await rm(testDir, { recursive: true, force: true });\n });\n});\n"]}