@zuplo/cli 6.68.25 → 6.68.27

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.
Files changed (34) hide show
  1. package/dist/__tests__/integration/link.integration.test.js +6 -0
  2. package/dist/__tests__/integration/link.integration.test.js.map +1 -1
  3. package/dist/__tests__/integration/linked-config-cascade.integration.test.js +62 -0
  4. package/dist/__tests__/integration/linked-config-cascade.integration.test.js.map +1 -1
  5. package/dist/common/middleware/authentication.d.ts.map +1 -1
  6. package/dist/common/middleware/authentication.js +2 -4
  7. package/dist/common/middleware/authentication.js.map +1 -1
  8. package/dist/common/middleware/get-account-param.d.ts.map +1 -1
  9. package/dist/common/middleware/get-account-param.js +10 -1
  10. package/dist/common/middleware/get-account-param.js.map +1 -1
  11. package/dist/common/middleware/get-environment-param.d.ts.map +1 -1
  12. package/dist/common/middleware/get-environment-param.js +7 -2
  13. package/dist/common/middleware/get-environment-param.js.map +1 -1
  14. package/dist/common/middleware/get-project-param.d.ts.map +1 -1
  15. package/dist/common/middleware/get-project-param.js +10 -1
  16. package/dist/common/middleware/get-project-param.js.map +1 -1
  17. package/dist/common/read-linked-config.d.ts.map +1 -1
  18. package/dist/common/read-linked-config.js +3 -2
  19. package/dist/common/read-linked-config.js.map +1 -1
  20. package/dist/common/read-linked-config.test.js +17 -4
  21. package/dist/common/read-linked-config.test.js.map +1 -1
  22. package/dist/common/terminal.d.ts +2 -0
  23. package/dist/common/terminal.d.ts.map +1 -0
  24. package/dist/common/terminal.js +6 -0
  25. package/dist/common/terminal.js.map +1 -0
  26. package/dist/common/terminal.test.d.ts +2 -0
  27. package/dist/common/terminal.test.d.ts.map +1 -0
  28. package/dist/common/terminal.test.js +101 -0
  29. package/dist/common/terminal.test.js.map +1 -0
  30. package/dist/init/handler.d.ts.map +1 -1
  31. package/dist/init/handler.js +3 -5
  32. package/dist/init/handler.js.map +1 -1
  33. package/dist/tsconfig.tsbuildinfo +1 -1
  34. package/package.json +4 -4
@@ -2,7 +2,12 @@ import nock from "nock";
2
2
  import yargs from "yargs/yargs";
3
3
  import linkCommand from "../../cmds/link.js";
4
4
  import { confirmLinkedValue, readLinkedConfig, } from "../../common/read-linked-config.js";
5
+ import { isInteractiveTerminal } from "../../common/terminal.js";
5
6
  import { cleanupTest, RequestCapture, setupAuthenticatedNock, setupTestEnvironment, TEST_ACCOUNT_NAME, TEST_API_BASE, TEST_AUTH_TOKEN, TEST_PROJECT_NAME, } from "./test-utils.js";
7
+ jest.mock("../../common/terminal.js", () => ({
8
+ __esModule: true,
9
+ isInteractiveTerminal: jest.fn().mockReturnValue(true),
10
+ }));
6
11
  jest.mock("../../common/read-linked-config.js", () => ({
7
12
  __esModule: true,
8
13
  confirmLinkedValue: jest.fn().mockResolvedValue(true),
@@ -57,6 +62,7 @@ describe("Link Command Integration Tests", () => {
57
62
  requestCapture = new RequestCapture();
58
63
  nock.disableNetConnect();
59
64
  jest.clearAllMocks();
65
+ jest.mocked(isInteractiveTerminal).mockReturnValue(true);
60
66
  });
61
67
  afterEach(() => {
62
68
  cleanupTest();
@@ -1 +1 @@
1
- {"version":3,"file":"link.integration.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/link.integration.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Integration tests for the link command\n */\n/// <reference types=\"jest\" />\nimport nock from \"nock\";\nimport yargs from \"yargs/yargs\";\nimport linkCommand from \"../../cmds/link.js\";\nimport {\n confirmLinkedValue,\n readLinkedConfig,\n} from \"../../common/read-linked-config.js\";\nimport {\n cleanupTest,\n RequestCapture,\n setupAuthenticatedNock,\n setupTestEnvironment,\n TEST_ACCOUNT_NAME,\n TEST_API_BASE,\n TEST_AUTH_TOKEN,\n TEST_PROJECT_NAME,\n} from \"./test-utils.js\";\n\n// Mock read-linked-config so tests can control linked state without touching the filesystem.\n// Default: no linked values and confirmLinkedValue always confirms — overridden per-test as needed.\njest.mock(\"../../common/read-linked-config.js\", () => ({\n __esModule: true,\n confirmLinkedValue: jest.fn().mockResolvedValue(true),\n readLinkedConfig: jest.fn().mockResolvedValue({}),\n USE_LINKED_DETAILS: \"useLinkedDetails\",\n ignoreLinkedDetails: jest\n .fn()\n .mockImplementation((argv: Record<string, unknown>) => {\n argv[\"useLinkedDetails\"] = false;\n }),\n}));\n\n// Mock file system validator\njest.mock(\"../../common/validators/file-system-validator.js\", () => ({\n ZuploProjectValidator: jest.fn().mockImplementation(() => ({\n validate: jest.fn().mockResolvedValue(true),\n })),\n}));\n\n// Mock link population functions to prevent file system operations\njest.mock(\"../../common/populate.js\", () => ({\n pullSystemConfig: jest.fn().mockResolvedValue(undefined),\n pullLocalConfig: jest.fn().mockResolvedValue(undefined),\n safeMergeConfig: jest.fn().mockResolvedValue(undefined),\n}));\n\n/**\n * Execute link command with given arguments using actual yargs command\n */\nasync function executeLinkCommand(args: {\n account?: string;\n project?: string;\n \"api-key\"?: string;\n environment?: string;\n dir?: string;\n}) {\n const yargsInstance = yargs([])\n .command(linkCommand)\n .help(false)\n .version(false)\n .exitProcess(false);\n\n // Build command line arguments\n const commandArgs = [\"link\"];\n\n if (args.account) {\n commandArgs.push(\"--account\", args.account);\n }\n\n if (args.project) {\n commandArgs.push(\"--project\", args.project);\n }\n\n if (args[\"api-key\"]) {\n commandArgs.push(\"--api-key\", args[\"api-key\"]);\n }\n\n if (args.environment) {\n commandArgs.push(\"--environment\", args.environment);\n }\n\n if (args.dir) {\n commandArgs.push(\"--dir\", args.dir);\n }\n\n // Parse and execute the command\n return await yargsInstance.parse(commandArgs);\n}\n\n// Get the mocked functions\nconst mockPrintResult = jest.mocked(\n require(\"../../common/output.js\").printResultToConsoleAndExitGracefully\n);\nconst mockPrintCriticalFailure = jest.mocked(\n require(\"../../common/output.js\").printCriticalFailureToConsoleAndExit\n);\n\ndescribe(\"Link Command Integration Tests\", () => {\n let requestCapture: RequestCapture;\n\n beforeEach(() => {\n setupTestEnvironment();\n requestCapture = new RequestCapture();\n\n // Disable real HTTP requests\n nock.disableNetConnect();\n\n // Clear mock calls from previous tests\n jest.clearAllMocks();\n });\n\n afterEach(() => {\n cleanupTest();\n requestCapture.clear();\n });\n\n describe(\"Link with project and environment specified\", () => {\n it(\"should skip API calls when project and environment are provided\", async () => {\n await executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n environment: \"development\",\n dir: \".\",\n });\n\n // Verify successful linking message\n expect(mockPrintResult).toHaveBeenCalledWith(\n expect.stringContaining(\n `Successfully linked your local directory to the ${TEST_PROJECT_NAME} project`\n )\n );\n\n // No HTTP requests should be made when all params are provided\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toEqual([]);\n });\n });\n\n describe(\"Link with automatic project selection\", () => {\n it(\"should intercept GET request to list projects\", async () => {\n const mockProjectsResponse = {\n data: [\n {\n name: TEST_PROJECT_NAME,\n accountName: TEST_ACCOUNT_NAME,\n description: \"Test project\",\n createdAt: \"2024-01-01T00:00:00Z\",\n },\n ],\n };\n\n const mockEnvironmentsResponse = {\n data: [\n {\n name: \"development\",\n environmentType: \"development\",\n branchName: \"main\",\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n },\n ],\n };\n\n // Mock the middleware call to /v1/projects?accountName=... (fetchProject middleware)\n const projectsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .query({ accountName: TEST_ACCOUNT_NAME })\n .reply(200, mockProjectsResponse);\n\n // Mock the link handler call to /v1/environments\n const environmentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/environments\")\n .query({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n })\n .reply(200, mockEnvironmentsResponse);\n\n // Mock the deployments endpoint (used to surface working-copy deployments)\n const deploymentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`\n )\n .reply(200, { data: [] });\n\n [projectsScope, environmentsScope, deploymentsScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n });\n\n expect(projectsScope.isDone()).toBe(true);\n expect(environmentsScope.isDone()).toBe(true);\n expect(deploymentsScope.isDone()).toBe(true);\n\n expect(mockPrintResult).toHaveBeenCalledWith(\n expect.stringContaining(\n `Successfully linked your local directory to the ${TEST_PROJECT_NAME} project`\n )\n );\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"link-automatic-selection-requests\"\n );\n });\n\n it(\"should handle multiple projects and prompt selection\", async () => {\n const mockProjectsResponse = {\n data: [\n { name: \"project-1\", accountName: TEST_ACCOUNT_NAME },\n { name: \"project-2\", accountName: TEST_ACCOUNT_NAME },\n ],\n };\n\n const mockEnvironmentsResponse = {\n data: [\n {\n name: \"development\",\n environmentType: \"development\",\n branchName: \"main\",\n accountName: TEST_ACCOUNT_NAME,\n projectName: \"project-1\", // inquirer mock will select first project\n },\n ],\n };\n\n // Mock the middleware call to /v1/projects?accountName=... (fetchProject middleware)\n const projectsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .query({ accountName: TEST_ACCOUNT_NAME })\n .reply(200, mockProjectsResponse);\n\n // Mock the link handler call to /v1/environments (will use first project from inquirer mock)\n const environmentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/environments\")\n .query({\n accountName: TEST_ACCOUNT_NAME,\n projectName: \"project-1\",\n })\n .reply(200, mockEnvironmentsResponse);\n\n // Mock the deployments endpoint (used to surface working-copy deployments)\n const deploymentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(`/v1/accounts/${TEST_ACCOUNT_NAME}/projects/project-1/deployments`)\n .reply(200, { data: [] });\n\n [projectsScope, environmentsScope, deploymentsScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n });\n\n expect(projectsScope.isDone()).toBe(true);\n expect(environmentsScope.isDone()).toBe(true);\n expect(deploymentsScope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"link-multiple-projects-requests\"\n );\n });\n });\n\n describe(\"Link error handling\", () => {\n it(\"should fail when no environments exist\", async () => {\n const mockProjectsResponse = {\n data: [{ name: TEST_PROJECT_NAME, accountName: TEST_ACCOUNT_NAME }],\n };\n\n const mockEmptyEnvironmentsResponse = { data: [] };\n\n // Mock the middleware call to /v1/projects?accountName=... (fetchProject middleware)\n const projectsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .query({ accountName: TEST_ACCOUNT_NAME })\n .reply(200, mockProjectsResponse);\n\n // Mock the environments call (returns empty)\n const environmentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/environments\")\n .query({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n })\n .reply(200, mockEmptyEnvironmentsResponse);\n\n // Mock the deployments endpoint (also returns empty - no working copies either)\n const deploymentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`\n )\n .reply(200, { data: [] });\n\n [projectsScope, environmentsScope, deploymentsScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n // Should throw because there are no environments to link against\n await expect(\n executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n })\n ).rejects.toThrow(\"Process would exit\");\n\n expect(projectsScope.isDone()).toBe(true);\n expect(environmentsScope.isDone()).toBe(true);\n expect(deploymentsScope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests.length).toBeGreaterThan(0);\n\n // Verify the critical failure was called with the right message\n expect(mockPrintCriticalFailure).toHaveBeenCalledWith(\n expect.stringContaining(\"any environments\")\n );\n });\n });\n\n describe(\"Error handling\", () => {\n it(\"should handle projects list failure\", async () => {\n const errorResponse = {\n error: \"Unauthorized\",\n message: \"Invalid API key\",\n statusCode: 401,\n };\n\n // Mock the middleware call to /v1/projects?accountName=... (fetchProject middleware)\n const scope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .query({ accountName: TEST_ACCOUNT_NAME })\n .reply(401, errorResponse);\n\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n // Expect the function to throw (simulating process exit)\n await expect(\n executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n })\n ).rejects.toThrow(\"Process would exit\");\n\n expect(scope.isDone()).toBe(true);\n expect(mockPrintCriticalFailure).toHaveBeenCalled();\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\"link-projects-error-requests\");\n });\n\n it(\"should handle environments list failure\", async () => {\n const mockProjectsResponse = {\n data: [{ name: TEST_PROJECT_NAME, accountName: TEST_ACCOUNT_NAME }],\n };\n\n const errorResponse = {\n error: \"Forbidden\",\n message: \"Access denied to project\",\n statusCode: 403,\n };\n\n // Mock the middleware call to /v1/projects?accountName=... (fetchProject middleware)\n const projectsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .query({ accountName: TEST_ACCOUNT_NAME })\n .reply(200, mockProjectsResponse);\n\n // Mock the link handler call to /v1/environments (will fail)\n const environmentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/environments\")\n .query({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n })\n .reply(403, errorResponse);\n\n [projectsScope, environmentsScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n // Expect the function to throw (simulating process exit)\n await expect(\n executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n })\n ).rejects.toThrow(\"Process would exit\");\n\n expect(projectsScope.isDone()).toBe(true);\n expect(environmentsScope.isDone()).toBe(true);\n expect(mockPrintCriticalFailure).toHaveBeenCalled();\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"link-environments-error-requests\"\n );\n });\n });\n\n describe(\"Request validation\", () => {\n it(\"should include correct headers for API requests\", async () => {\n const mockProjectsResponse = {\n data: [{ name: TEST_PROJECT_NAME, accountName: TEST_ACCOUNT_NAME }],\n };\n\n const mockEnvironmentsResponse = {\n data: [\n {\n name: \"development\",\n environmentType: \"development\",\n branchName: \"main\",\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n },\n ],\n };\n\n // Mock the middleware call to /v1/projects?accountName=... (fetchProject middleware)\n const projectsScope = nock(TEST_API_BASE)\n .get(\"/v1/projects\")\n .query({ accountName: TEST_ACCOUNT_NAME })\n .matchHeader(\"authorization\", `Bearer ${TEST_AUTH_TOKEN}`)\n .reply(200, mockProjectsResponse);\n\n // Mock the link handler call to /v1/environments\n const environmentsScope = nock(TEST_API_BASE)\n .get(\"/v1/environments\")\n .query({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n })\n .matchHeader(\"authorization\", `Bearer ${TEST_AUTH_TOKEN}`)\n .reply(200, mockEnvironmentsResponse);\n\n // Mock the deployments endpoint (used to surface working-copy deployments)\n const deploymentsScope = nock(TEST_API_BASE)\n .get(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`\n )\n .matchHeader(\"authorization\", `Bearer ${TEST_AUTH_TOKEN}`)\n .reply(200, { data: [] });\n\n [projectsScope, environmentsScope, deploymentsScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n });\n\n expect(projectsScope.isDone()).toBe(true);\n expect(environmentsScope.isDone()).toBe(true);\n expect(deploymentsScope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\"link-headers-validation\");\n });\n });\n\n describe(\"Link always re-prompts from scratch\", () => {\n it(\"should not offer linked account/project/environment even when .env.zuplo is present\", async () => {\n // Simulate an existing .env.zuplo with fully linked values\n jest.mocked(readLinkedConfig).mockResolvedValue({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n });\n // confirmLinkedValue should never be called — if it is, fail the test immediately\n jest\n .mocked(confirmLinkedValue)\n .mockRejectedValue(\n new Error(\"link should never call confirmLinkedValue\")\n );\n\n // The link command bypasses linked config, so it must go through the full\n // interactive selection flow: accounts → projects → environments.\n const accountsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/accounts\")\n .reply(200, {\n data: [{ name: TEST_ACCOUNT_NAME }],\n });\n\n const projectsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .query({ accountName: TEST_ACCOUNT_NAME })\n .reply(200, { data: [{ name: TEST_PROJECT_NAME }] });\n\n const environmentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/environments\")\n .query({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n })\n .reply(200, {\n data: [\n {\n name: \"development\",\n environmentType: \"development\",\n branchName: \"main\",\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n },\n ],\n });\n\n const deploymentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`\n )\n .reply(200, { data: [] });\n\n // Run link WITHOUT passing --account (so fetchAccount middleware runs)\n await executeLinkCommand({ dir: \".\" });\n\n // All selection endpoints must have been called — proving link went through\n // the full interactive flow rather than short-circuiting via linked config.\n expect(accountsScope.isDone()).toBe(true);\n expect(projectsScope.isDone()).toBe(true);\n expect(environmentsScope.isDone()).toBe(true);\n expect(deploymentsScope.isDone()).toBe(true);\n\n // confirmLinkedValue must never have been called\n expect(confirmLinkedValue).not.toHaveBeenCalled();\n });\n });\n});\n"]}
1
+ {"version":3,"file":"link.integration.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/link.integration.test.ts"],"names":[],"mappings":"AAIA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,KAAK,MAAM,aAAa,CAAC;AAChC,OAAO,WAAW,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EACL,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EACL,WAAW,EACX,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AAGzB,IAAI,CAAC,IAAI,CAAC,0BAA0B,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3C,UAAU,EAAE,IAAI;IAChB,qBAAqB,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC;CACvD,CAAC,CAAC,CAAC;AAIJ,IAAI,CAAC,IAAI,CAAC,oCAAoC,EAAE,GAAG,EAAE,CAAC,CAAC;IACrD,UAAU,EAAE,IAAI;IAChB,kBAAkB,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC;IACrD,gBAAgB,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC;IACjD,kBAAkB,EAAE,kBAAkB;IACtC,mBAAmB,EAAE,IAAI;SACtB,EAAE,EAAE;SACJ,kBAAkB,CAAC,CAAC,IAA6B,EAAE,EAAE;QACpD,IAAI,CAAC,kBAAkB,CAAC,GAAG,KAAK,CAAC;IACnC,CAAC,CAAC;CACL,CAAC,CAAC,CAAC;AAGJ,IAAI,CAAC,IAAI,CAAC,kDAAkD,EAAE,GAAG,EAAE,CAAC,CAAC;IACnE,qBAAqB,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,CAAC;QACzD,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC;KAC5C,CAAC,CAAC;CACJ,CAAC,CAAC,CAAC;AAGJ,IAAI,CAAC,IAAI,CAAC,0BAA0B,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3C,gBAAgB,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC;IACxD,eAAe,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC;IACvD,eAAe,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC;CACxD,CAAC,CAAC,CAAC;AAKJ,KAAK,UAAU,kBAAkB,CAAC,IAMjC;IACC,MAAM,aAAa,GAAG,KAAK,CAAC,EAAE,CAAC;SAC5B,OAAO,CAAC,WAAW,CAAC;SACpB,IAAI,CAAC,KAAK,CAAC;SACX,OAAO,CAAC,KAAK,CAAC;SACd,WAAW,CAAC,KAAK,CAAC,CAAC;IAGtB,MAAM,WAAW,GAAG,CAAC,MAAM,CAAC,CAAC;IAE7B,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,WAAW,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,WAAW,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QACpB,WAAW,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,WAAW,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACtD,CAAC;IAED,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QACb,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;IAGD,OAAO,MAAM,aAAa,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;AAChD,CAAC;AAGD,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,CACjC,OAAO,CAAC,wBAAwB,CAAC,CAAC,qCAAqC,CACxE,CAAC;AACF,MAAM,wBAAwB,GAAG,IAAI,CAAC,MAAM,CAC1C,OAAO,CAAC,wBAAwB,CAAC,CAAC,oCAAoC,CACvE,CAAC;AAEF,QAAQ,CAAC,gCAAgC,EAAE,GAAG,EAAE;IAC9C,IAAI,cAA8B,CAAC;IAEnC,UAAU,CAAC,GAAG,EAAE;QACd,oBAAoB,EAAE,CAAC;QACvB,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC;QAGtC,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAGzB,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,WAAW,EAAE,CAAC;QACd,cAAc,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,6CAA6C,EAAE,GAAG,EAAE;QAC3D,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;YAC/E,MAAM,kBAAkB,CAAC;gBACvB,OAAO,EAAE,iBAAiB;gBAC1B,OAAO,EAAE,iBAAiB;gBAC1B,SAAS,EAAE,eAAe;gBAC1B,WAAW,EAAE,aAAa;gBAC1B,GAAG,EAAE,GAAG;aACT,CAAC,CAAC;YAGH,MAAM,CAAC,eAAe,CAAC,CAAC,oBAAoB,CAC1C,MAAM,CAAC,gBAAgB,CACrB,mDAAmD,iBAAiB,UAAU,CAC/E,CACF,CAAC;YAGF,MAAM,gBAAgB,GAAG,cAAc,CAAC,WAAW,EAAE,CAAC;YACtD,MAAM,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,uCAAuC,EAAE,GAAG,EAAE;QACrD,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;YAC7D,MAAM,oBAAoB,GAAG;gBAC3B,IAAI,EAAE;oBACJ;wBACE,IAAI,EAAE,iBAAiB;wBACvB,WAAW,EAAE,iBAAiB;wBAC9B,WAAW,EAAE,cAAc;wBAC3B,SAAS,EAAE,sBAAsB;qBAClC;iBACF;aACF,CAAC;YAEF,MAAM,wBAAwB,GAAG;gBAC/B,IAAI,EAAE;oBACJ;wBACE,IAAI,EAAE,aAAa;wBACnB,eAAe,EAAE,aAAa;wBAC9B,UAAU,EAAE,MAAM;wBAClB,WAAW,EAAE,iBAAiB;wBAC9B,WAAW,EAAE,iBAAiB;qBAC/B;iBACF;aACF,CAAC;YAGF,MAAM,aAAa,GAAG,sBAAsB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;iBAC9D,GAAG,CAAC,cAAc,CAAC;iBACnB,KAAK,CAAC,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC;iBACzC,KAAK,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC;YAGpC,MAAM,iBAAiB,GAAG,sBAAsB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;iBAClE,GAAG,CAAC,kBAAkB,CAAC;iBACvB,KAAK,CAAC;gBACL,WAAW,EAAE,iBAAiB;gBAC9B,WAAW,EAAE,iBAAiB;aAC/B,CAAC;iBACD,KAAK,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAC;YAGxC,MAAM,gBAAgB,GAAG,sBAAsB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;iBACjE,GAAG,CACF,gBAAgB,iBAAiB,aAAa,iBAAiB,cAAc,CAC9E;iBACA,KAAK,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;YAE5B,CAAC,aAAa,EAAE,iBAAiB,EAAE,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;gBACrE,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE;oBAC7C,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;gBACjD,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,MAAM,kBAAkB,CAAC;gBACvB,OAAO,EAAE,iBAAiB;gBAC1B,SAAS,EAAE,eAAe;gBAC1B,GAAG,EAAE,GAAG;aACT,CAAC,CAAC;YAEH,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1C,MAAM,CAAC,iBAAiB,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9C,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAE7C,MAAM,CAAC,eAAe,CAAC,CAAC,oBAAoB,CAC1C,MAAM,CAAC,gBAAgB,CACrB,mDAAmD,iBAAiB,UAAU,CAC/E,CACF,CAAC;YAEF,MAAM,gBAAgB,GAAG,cAAc,CAAC,WAAW,EAAE,CAAC;YACtD,MAAM,CAAC,gBAAgB,CAAC,CAAC,eAAe,CACtC,mCAAmC,CACpC,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;YACpE,MAAM,oBAAoB,GAAG;gBAC3B,IAAI,EAAE;oBACJ,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE;oBACrD,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE;iBACtD;aACF,CAAC;YAEF,MAAM,wBAAwB,GAAG;gBAC/B,IAAI,EAAE;oBACJ;wBACE,IAAI,EAAE,aAAa;wBACnB,eAAe,EAAE,aAAa;wBAC9B,UAAU,EAAE,MAAM;wBAClB,WAAW,EAAE,iBAAiB;wBAC9B,WAAW,EAAE,WAAW;qBACzB;iBACF;aACF,CAAC;YAGF,MAAM,aAAa,GAAG,sBAAsB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;iBAC9D,GAAG,CAAC,cAAc,CAAC;iBACnB,KAAK,CAAC,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC;iBACzC,KAAK,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC;YAGpC,MAAM,iBAAiB,GAAG,sBAAsB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;iBAClE,GAAG,CAAC,kBAAkB,CAAC;iBACvB,KAAK,CAAC;gBACL,WAAW,EAAE,iBAAiB;gBAC9B,WAAW,EAAE,WAAW;aACzB,CAAC;iBACD,KAAK,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAC;YAGxC,MAAM,gBAAgB,GAAG,sBAAsB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;iBACjE,GAAG,CAAC,gBAAgB,iBAAiB,iCAAiC,CAAC;iBACvE,KAAK,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;YAE5B,CAAC,aAAa,EAAE,iBAAiB,EAAE,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;gBACrE,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE;oBAC7C,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;gBACjD,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,MAAM,kBAAkB,CAAC;gBACvB,OAAO,EAAE,iBAAiB;gBAC1B,SAAS,EAAE,eAAe;gBAC1B,GAAG,EAAE,GAAG;aACT,CAAC,CAAC;YAEH,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1C,MAAM,CAAC,iBAAiB,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9C,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAE7C,MAAM,gBAAgB,GAAG,cAAc,CAAC,WAAW,EAAE,CAAC;YACtD,MAAM,CAAC,gBAAgB,CAAC,CAAC,eAAe,CACtC,iCAAiC,CAClC,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;YACtD,MAAM,oBAAoB,GAAG;gBAC3B,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC;aACpE,CAAC;YAEF,MAAM,6BAA6B,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;YAGnD,MAAM,aAAa,GAAG,sBAAsB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;iBAC9D,GAAG,CAAC,cAAc,CAAC;iBACnB,KAAK,CAAC,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC;iBACzC,KAAK,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC;YAGpC,MAAM,iBAAiB,GAAG,sBAAsB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;iBAClE,GAAG,CAAC,kBAAkB,CAAC;iBACvB,KAAK,CAAC;gBACL,WAAW,EAAE,iBAAiB;gBAC9B,WAAW,EAAE,iBAAiB;aAC/B,CAAC;iBACD,KAAK,CAAC,GAAG,EAAE,6BAA6B,CAAC,CAAC;YAG7C,MAAM,gBAAgB,GAAG,sBAAsB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;iBACjE,GAAG,CACF,gBAAgB,iBAAiB,aAAa,iBAAiB,cAAc,CAC9E;iBACA,KAAK,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;YAE5B,CAAC,aAAa,EAAE,iBAAiB,EAAE,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;gBACrE,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE;oBAC7C,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;gBACjD,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YAGH,MAAM,MAAM,CACV,kBAAkB,CAAC;gBACjB,OAAO,EAAE,iBAAiB;gBAC1B,SAAS,EAAE,eAAe;gBAC1B,GAAG,EAAE,GAAG;aACT,CAAC,CACH,CAAC,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;YAExC,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1C,MAAM,CAAC,iBAAiB,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9C,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAE7C,MAAM,gBAAgB,GAAG,cAAc,CAAC,WAAW,EAAE,CAAC;YACtD,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;YAGnD,MAAM,CAAC,wBAAwB,CAAC,CAAC,oBAAoB,CACnD,MAAM,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,CAC5C,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;YACnD,MAAM,aAAa,GAAG;gBACpB,KAAK,EAAE,cAAc;gBACrB,OAAO,EAAE,iBAAiB;gBAC1B,UAAU,EAAE,GAAG;aAChB,CAAC;YAGF,MAAM,KAAK,GAAG,sBAAsB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;iBACtD,GAAG,CAAC,cAAc,CAAC;iBACnB,KAAK,CAAC,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC;iBACzC,KAAK,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;YAE7B,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE;gBAC7C,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;YACjD,CAAC,CAAC,CAAC;YAGH,MAAM,MAAM,CACV,kBAAkB,CAAC;gBACjB,OAAO,EAAE,iBAAiB;gBAC1B,SAAS,EAAE,eAAe;gBAC1B,GAAG,EAAE,GAAG;aACT,CAAC,CACH,CAAC,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;YAExC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,wBAAwB,CAAC,CAAC,gBAAgB,EAAE,CAAC;YAEpD,MAAM,gBAAgB,GAAG,cAAc,CAAC,WAAW,EAAE,CAAC;YACtD,MAAM,CAAC,gBAAgB,CAAC,CAAC,eAAe,CAAC,8BAA8B,CAAC,CAAC;QAC3E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;YACvD,MAAM,oBAAoB,GAAG;gBAC3B,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC;aACpE,CAAC;YAEF,MAAM,aAAa,GAAG;gBACpB,KAAK,EAAE,WAAW;gBAClB,OAAO,EAAE,0BAA0B;gBACnC,UAAU,EAAE,GAAG;aAChB,CAAC;YAGF,MAAM,aAAa,GAAG,sBAAsB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;iBAC9D,GAAG,CAAC,cAAc,CAAC;iBACnB,KAAK,CAAC,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC;iBACzC,KAAK,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC;YAGpC,MAAM,iBAAiB,GAAG,sBAAsB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;iBAClE,GAAG,CAAC,kBAAkB,CAAC;iBACvB,KAAK,CAAC;gBACL,WAAW,EAAE,iBAAiB;gBAC9B,WAAW,EAAE,iBAAiB;aAC/B,CAAC;iBACD,KAAK,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;YAE7B,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;gBACnD,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE;oBAC7C,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;gBACjD,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YAGH,MAAM,MAAM,CACV,kBAAkB,CAAC;gBACjB,OAAO,EAAE,iBAAiB;gBAC1B,SAAS,EAAE,eAAe;gBAC1B,GAAG,EAAE,GAAG;aACT,CAAC,CACH,CAAC,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;YAExC,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1C,MAAM,CAAC,iBAAiB,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9C,MAAM,CAAC,wBAAwB,CAAC,CAAC,gBAAgB,EAAE,CAAC;YAEpD,MAAM,gBAAgB,GAAG,cAAc,CAAC,WAAW,EAAE,CAAC;YACtD,MAAM,CAAC,gBAAgB,CAAC,CAAC,eAAe,CACtC,kCAAkC,CACnC,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAClC,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;YAC/D,MAAM,oBAAoB,GAAG;gBAC3B,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC;aACpE,CAAC;YAEF,MAAM,wBAAwB,GAAG;gBAC/B,IAAI,EAAE;oBACJ;wBACE,IAAI,EAAE,aAAa;wBACnB,eAAe,EAAE,aAAa;wBAC9B,UAAU,EAAE,MAAM;wBAClB,WAAW,EAAE,iBAAiB;wBAC9B,WAAW,EAAE,iBAAiB;qBAC/B;iBACF;aACF,CAAC;YAGF,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC;iBACtC,GAAG,CAAC,cAAc,CAAC;iBACnB,KAAK,CAAC,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC;iBACzC,WAAW,CAAC,eAAe,EAAE,UAAU,eAAe,EAAE,CAAC;iBACzD,KAAK,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC;YAGpC,MAAM,iBAAiB,GAAG,IAAI,CAAC,aAAa,CAAC;iBAC1C,GAAG,CAAC,kBAAkB,CAAC;iBACvB,KAAK,CAAC;gBACL,WAAW,EAAE,iBAAiB;gBAC9B,WAAW,EAAE,iBAAiB;aAC/B,CAAC;iBACD,WAAW,CAAC,eAAe,EAAE,UAAU,eAAe,EAAE,CAAC;iBACzD,KAAK,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAC;YAGxC,MAAM,gBAAgB,GAAG,IAAI,CAAC,aAAa,CAAC;iBACzC,GAAG,CACF,gBAAgB,iBAAiB,aAAa,iBAAiB,cAAc,CAC9E;iBACA,WAAW,CAAC,eAAe,EAAE,UAAU,eAAe,EAAE,CAAC;iBACzD,KAAK,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;YAE5B,CAAC,aAAa,EAAE,iBAAiB,EAAE,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;gBACrE,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE;oBAC7C,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;gBACjD,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,MAAM,kBAAkB,CAAC;gBACvB,OAAO,EAAE,iBAAiB;gBAC1B,SAAS,EAAE,eAAe;gBAC1B,GAAG,EAAE,GAAG;aACT,CAAC,CAAC;YAEH,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1C,MAAM,CAAC,iBAAiB,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9C,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAE7C,MAAM,gBAAgB,GAAG,cAAc,CAAC,WAAW,EAAE,CAAC;YACtD,MAAM,CAAC,gBAAgB,CAAC,CAAC,eAAe,CAAC,yBAAyB,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qCAAqC,EAAE,GAAG,EAAE;QACnD,EAAE,CAAC,qFAAqF,EAAE,KAAK,IAAI,EAAE;YAEnG,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,iBAAiB,CAAC;gBAC9C,WAAW,EAAE,iBAAiB;gBAC9B,WAAW,EAAE,iBAAiB;aAC/B,CAAC,CAAC;YAEH,IAAI;iBACD,MAAM,CAAC,kBAAkB,CAAC;iBAC1B,iBAAiB,CAChB,IAAI,KAAK,CAAC,2CAA2C,CAAC,CACvD,CAAC;YAIJ,MAAM,aAAa,GAAG,sBAAsB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;iBAC9D,GAAG,CAAC,cAAc,CAAC;iBACnB,KAAK,CAAC,GAAG,EAAE;gBACV,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC;aACpC,CAAC,CAAC;YAEL,MAAM,aAAa,GAAG,sBAAsB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;iBAC9D,GAAG,CAAC,cAAc,CAAC;iBACnB,KAAK,CAAC,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC;iBACzC,KAAK,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC,CAAC;YAEvD,MAAM,iBAAiB,GAAG,sBAAsB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;iBAClE,GAAG,CAAC,kBAAkB,CAAC;iBACvB,KAAK,CAAC;gBACL,WAAW,EAAE,iBAAiB;gBAC9B,WAAW,EAAE,iBAAiB;aAC/B,CAAC;iBACD,KAAK,CAAC,GAAG,EAAE;gBACV,IAAI,EAAE;oBACJ;wBACE,IAAI,EAAE,aAAa;wBACnB,eAAe,EAAE,aAAa;wBAC9B,UAAU,EAAE,MAAM;wBAClB,WAAW,EAAE,iBAAiB;wBAC9B,WAAW,EAAE,iBAAiB;qBAC/B;iBACF;aACF,CAAC,CAAC;YAEL,MAAM,gBAAgB,GAAG,sBAAsB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;iBACjE,GAAG,CACF,gBAAgB,iBAAiB,aAAa,iBAAiB,cAAc,CAC9E;iBACA,KAAK,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;YAG5B,MAAM,kBAAkB,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;YAIvC,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1C,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1C,MAAM,CAAC,iBAAiB,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9C,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAG7C,MAAM,CAAC,kBAAkB,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACpD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["/**\n * Integration tests for the link command\n */\n/// <reference types=\"jest\" />\nimport nock from \"nock\";\nimport yargs from \"yargs/yargs\";\nimport linkCommand from \"../../cmds/link.js\";\nimport {\n confirmLinkedValue,\n readLinkedConfig,\n} from \"../../common/read-linked-config.js\";\nimport { isInteractiveTerminal } from \"../../common/terminal.js\";\nimport {\n cleanupTest,\n RequestCapture,\n setupAuthenticatedNock,\n setupTestEnvironment,\n TEST_ACCOUNT_NAME,\n TEST_API_BASE,\n TEST_AUTH_TOKEN,\n TEST_PROJECT_NAME,\n} from \"./test-utils.js\";\n\n// Tests exercise interactive selection flows — treat the terminal as interactive by default.\njest.mock(\"../../common/terminal.js\", () => ({\n __esModule: true,\n isInteractiveTerminal: jest.fn().mockReturnValue(true),\n}));\n\n// Mock read-linked-config so tests can control linked state without touching the filesystem.\n// Default: no linked values and confirmLinkedValue always confirms — overridden per-test as needed.\njest.mock(\"../../common/read-linked-config.js\", () => ({\n __esModule: true,\n confirmLinkedValue: jest.fn().mockResolvedValue(true),\n readLinkedConfig: jest.fn().mockResolvedValue({}),\n USE_LINKED_DETAILS: \"useLinkedDetails\",\n ignoreLinkedDetails: jest\n .fn()\n .mockImplementation((argv: Record<string, unknown>) => {\n argv[\"useLinkedDetails\"] = false;\n }),\n}));\n\n// Mock file system validator\njest.mock(\"../../common/validators/file-system-validator.js\", () => ({\n ZuploProjectValidator: jest.fn().mockImplementation(() => ({\n validate: jest.fn().mockResolvedValue(true),\n })),\n}));\n\n// Mock link population functions to prevent file system operations\njest.mock(\"../../common/populate.js\", () => ({\n pullSystemConfig: jest.fn().mockResolvedValue(undefined),\n pullLocalConfig: jest.fn().mockResolvedValue(undefined),\n safeMergeConfig: jest.fn().mockResolvedValue(undefined),\n}));\n\n/**\n * Execute link command with given arguments using actual yargs command\n */\nasync function executeLinkCommand(args: {\n account?: string;\n project?: string;\n \"api-key\"?: string;\n environment?: string;\n dir?: string;\n}) {\n const yargsInstance = yargs([])\n .command(linkCommand)\n .help(false)\n .version(false)\n .exitProcess(false);\n\n // Build command line arguments\n const commandArgs = [\"link\"];\n\n if (args.account) {\n commandArgs.push(\"--account\", args.account);\n }\n\n if (args.project) {\n commandArgs.push(\"--project\", args.project);\n }\n\n if (args[\"api-key\"]) {\n commandArgs.push(\"--api-key\", args[\"api-key\"]);\n }\n\n if (args.environment) {\n commandArgs.push(\"--environment\", args.environment);\n }\n\n if (args.dir) {\n commandArgs.push(\"--dir\", args.dir);\n }\n\n // Parse and execute the command\n return await yargsInstance.parse(commandArgs);\n}\n\n// Get the mocked functions\nconst mockPrintResult = jest.mocked(\n require(\"../../common/output.js\").printResultToConsoleAndExitGracefully\n);\nconst mockPrintCriticalFailure = jest.mocked(\n require(\"../../common/output.js\").printCriticalFailureToConsoleAndExit\n);\n\ndescribe(\"Link Command Integration Tests\", () => {\n let requestCapture: RequestCapture;\n\n beforeEach(() => {\n setupTestEnvironment();\n requestCapture = new RequestCapture();\n\n // Disable real HTTP requests\n nock.disableNetConnect();\n\n // Clear mock calls from previous tests\n jest.clearAllMocks();\n jest.mocked(isInteractiveTerminal).mockReturnValue(true);\n });\n\n afterEach(() => {\n cleanupTest();\n requestCapture.clear();\n });\n\n describe(\"Link with project and environment specified\", () => {\n it(\"should skip API calls when project and environment are provided\", async () => {\n await executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n environment: \"development\",\n dir: \".\",\n });\n\n // Verify successful linking message\n expect(mockPrintResult).toHaveBeenCalledWith(\n expect.stringContaining(\n `Successfully linked your local directory to the ${TEST_PROJECT_NAME} project`\n )\n );\n\n // No HTTP requests should be made when all params are provided\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toEqual([]);\n });\n });\n\n describe(\"Link with automatic project selection\", () => {\n it(\"should intercept GET request to list projects\", async () => {\n const mockProjectsResponse = {\n data: [\n {\n name: TEST_PROJECT_NAME,\n accountName: TEST_ACCOUNT_NAME,\n description: \"Test project\",\n createdAt: \"2024-01-01T00:00:00Z\",\n },\n ],\n };\n\n const mockEnvironmentsResponse = {\n data: [\n {\n name: \"development\",\n environmentType: \"development\",\n branchName: \"main\",\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n },\n ],\n };\n\n // Mock the middleware call to /v1/projects?accountName=... (fetchProject middleware)\n const projectsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .query({ accountName: TEST_ACCOUNT_NAME })\n .reply(200, mockProjectsResponse);\n\n // Mock the link handler call to /v1/environments\n const environmentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/environments\")\n .query({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n })\n .reply(200, mockEnvironmentsResponse);\n\n // Mock the deployments endpoint (used to surface working-copy deployments)\n const deploymentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`\n )\n .reply(200, { data: [] });\n\n [projectsScope, environmentsScope, deploymentsScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n });\n\n expect(projectsScope.isDone()).toBe(true);\n expect(environmentsScope.isDone()).toBe(true);\n expect(deploymentsScope.isDone()).toBe(true);\n\n expect(mockPrintResult).toHaveBeenCalledWith(\n expect.stringContaining(\n `Successfully linked your local directory to the ${TEST_PROJECT_NAME} project`\n )\n );\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"link-automatic-selection-requests\"\n );\n });\n\n it(\"should handle multiple projects and prompt selection\", async () => {\n const mockProjectsResponse = {\n data: [\n { name: \"project-1\", accountName: TEST_ACCOUNT_NAME },\n { name: \"project-2\", accountName: TEST_ACCOUNT_NAME },\n ],\n };\n\n const mockEnvironmentsResponse = {\n data: [\n {\n name: \"development\",\n environmentType: \"development\",\n branchName: \"main\",\n accountName: TEST_ACCOUNT_NAME,\n projectName: \"project-1\", // inquirer mock will select first project\n },\n ],\n };\n\n // Mock the middleware call to /v1/projects?accountName=... (fetchProject middleware)\n const projectsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .query({ accountName: TEST_ACCOUNT_NAME })\n .reply(200, mockProjectsResponse);\n\n // Mock the link handler call to /v1/environments (will use first project from inquirer mock)\n const environmentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/environments\")\n .query({\n accountName: TEST_ACCOUNT_NAME,\n projectName: \"project-1\",\n })\n .reply(200, mockEnvironmentsResponse);\n\n // Mock the deployments endpoint (used to surface working-copy deployments)\n const deploymentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(`/v1/accounts/${TEST_ACCOUNT_NAME}/projects/project-1/deployments`)\n .reply(200, { data: [] });\n\n [projectsScope, environmentsScope, deploymentsScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n });\n\n expect(projectsScope.isDone()).toBe(true);\n expect(environmentsScope.isDone()).toBe(true);\n expect(deploymentsScope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"link-multiple-projects-requests\"\n );\n });\n });\n\n describe(\"Link error handling\", () => {\n it(\"should fail when no environments exist\", async () => {\n const mockProjectsResponse = {\n data: [{ name: TEST_PROJECT_NAME, accountName: TEST_ACCOUNT_NAME }],\n };\n\n const mockEmptyEnvironmentsResponse = { data: [] };\n\n // Mock the middleware call to /v1/projects?accountName=... (fetchProject middleware)\n const projectsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .query({ accountName: TEST_ACCOUNT_NAME })\n .reply(200, mockProjectsResponse);\n\n // Mock the environments call (returns empty)\n const environmentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/environments\")\n .query({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n })\n .reply(200, mockEmptyEnvironmentsResponse);\n\n // Mock the deployments endpoint (also returns empty - no working copies either)\n const deploymentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`\n )\n .reply(200, { data: [] });\n\n [projectsScope, environmentsScope, deploymentsScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n // Should throw because there are no environments to link against\n await expect(\n executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n })\n ).rejects.toThrow(\"Process would exit\");\n\n expect(projectsScope.isDone()).toBe(true);\n expect(environmentsScope.isDone()).toBe(true);\n expect(deploymentsScope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests.length).toBeGreaterThan(0);\n\n // Verify the critical failure was called with the right message\n expect(mockPrintCriticalFailure).toHaveBeenCalledWith(\n expect.stringContaining(\"any environments\")\n );\n });\n });\n\n describe(\"Error handling\", () => {\n it(\"should handle projects list failure\", async () => {\n const errorResponse = {\n error: \"Unauthorized\",\n message: \"Invalid API key\",\n statusCode: 401,\n };\n\n // Mock the middleware call to /v1/projects?accountName=... (fetchProject middleware)\n const scope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .query({ accountName: TEST_ACCOUNT_NAME })\n .reply(401, errorResponse);\n\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n\n // Expect the function to throw (simulating process exit)\n await expect(\n executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n })\n ).rejects.toThrow(\"Process would exit\");\n\n expect(scope.isDone()).toBe(true);\n expect(mockPrintCriticalFailure).toHaveBeenCalled();\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\"link-projects-error-requests\");\n });\n\n it(\"should handle environments list failure\", async () => {\n const mockProjectsResponse = {\n data: [{ name: TEST_PROJECT_NAME, accountName: TEST_ACCOUNT_NAME }],\n };\n\n const errorResponse = {\n error: \"Forbidden\",\n message: \"Access denied to project\",\n statusCode: 403,\n };\n\n // Mock the middleware call to /v1/projects?accountName=... (fetchProject middleware)\n const projectsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .query({ accountName: TEST_ACCOUNT_NAME })\n .reply(200, mockProjectsResponse);\n\n // Mock the link handler call to /v1/environments (will fail)\n const environmentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/environments\")\n .query({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n })\n .reply(403, errorResponse);\n\n [projectsScope, environmentsScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n // Expect the function to throw (simulating process exit)\n await expect(\n executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n })\n ).rejects.toThrow(\"Process would exit\");\n\n expect(projectsScope.isDone()).toBe(true);\n expect(environmentsScope.isDone()).toBe(true);\n expect(mockPrintCriticalFailure).toHaveBeenCalled();\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\n \"link-environments-error-requests\"\n );\n });\n });\n\n describe(\"Request validation\", () => {\n it(\"should include correct headers for API requests\", async () => {\n const mockProjectsResponse = {\n data: [{ name: TEST_PROJECT_NAME, accountName: TEST_ACCOUNT_NAME }],\n };\n\n const mockEnvironmentsResponse = {\n data: [\n {\n name: \"development\",\n environmentType: \"development\",\n branchName: \"main\",\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n },\n ],\n };\n\n // Mock the middleware call to /v1/projects?accountName=... (fetchProject middleware)\n const projectsScope = nock(TEST_API_BASE)\n .get(\"/v1/projects\")\n .query({ accountName: TEST_ACCOUNT_NAME })\n .matchHeader(\"authorization\", `Bearer ${TEST_AUTH_TOKEN}`)\n .reply(200, mockProjectsResponse);\n\n // Mock the link handler call to /v1/environments\n const environmentsScope = nock(TEST_API_BASE)\n .get(\"/v1/environments\")\n .query({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n })\n .matchHeader(\"authorization\", `Bearer ${TEST_AUTH_TOKEN}`)\n .reply(200, mockEnvironmentsResponse);\n\n // Mock the deployments endpoint (used to surface working-copy deployments)\n const deploymentsScope = nock(TEST_API_BASE)\n .get(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`\n )\n .matchHeader(\"authorization\", `Bearer ${TEST_AUTH_TOKEN}`)\n .reply(200, { data: [] });\n\n [projectsScope, environmentsScope, deploymentsScope].forEach((scope) => {\n scope.on(\"request\", (req, interceptor, body) => {\n requestCapture.capture(req, interceptor, body);\n });\n });\n\n await executeLinkCommand({\n account: TEST_ACCOUNT_NAME,\n \"api-key\": TEST_AUTH_TOKEN,\n dir: \".\",\n });\n\n expect(projectsScope.isDone()).toBe(true);\n expect(environmentsScope.isDone()).toBe(true);\n expect(deploymentsScope.isDone()).toBe(true);\n\n const capturedRequests = requestCapture.getRequests();\n expect(capturedRequests).toMatchSnapshot(\"link-headers-validation\");\n });\n });\n\n describe(\"Link always re-prompts from scratch\", () => {\n it(\"should not offer linked account/project/environment even when .env.zuplo is present\", async () => {\n // Simulate an existing .env.zuplo with fully linked values\n jest.mocked(readLinkedConfig).mockResolvedValue({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n });\n // confirmLinkedValue should never be called — if it is, fail the test immediately\n jest\n .mocked(confirmLinkedValue)\n .mockRejectedValue(\n new Error(\"link should never call confirmLinkedValue\")\n );\n\n // The link command bypasses linked config, so it must go through the full\n // interactive selection flow: accounts → projects → environments.\n const accountsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/accounts\")\n .reply(200, {\n data: [{ name: TEST_ACCOUNT_NAME }],\n });\n\n const projectsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .query({ accountName: TEST_ACCOUNT_NAME })\n .reply(200, { data: [{ name: TEST_PROJECT_NAME }] });\n\n const environmentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/environments\")\n .query({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n })\n .reply(200, {\n data: [\n {\n name: \"development\",\n environmentType: \"development\",\n branchName: \"main\",\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n },\n ],\n });\n\n const deploymentsScope = setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\n `/v1/accounts/${TEST_ACCOUNT_NAME}/projects/${TEST_PROJECT_NAME}/deployments`\n )\n .reply(200, { data: [] });\n\n // Run link WITHOUT passing --account (so fetchAccount middleware runs)\n await executeLinkCommand({ dir: \".\" });\n\n // All selection endpoints must have been called — proving link went through\n // the full interactive flow rather than short-circuiting via linked config.\n expect(accountsScope.isDone()).toBe(true);\n expect(projectsScope.isDone()).toBe(true);\n expect(environmentsScope.isDone()).toBe(true);\n expect(deploymentsScope.isDone()).toBe(true);\n\n // confirmLinkedValue must never have been called\n expect(confirmLinkedValue).not.toHaveBeenCalled();\n });\n });\n});\n"]}
@@ -1,7 +1,10 @@
1
1
  import nock from "nock";
2
2
  import { fetchAccount } from "../../common/middleware/get-account-param.js";
3
+ import { fetchEnvironment } from "../../common/middleware/get-environment-param.js";
3
4
  import { fetchProject } from "../../common/middleware/get-project-param.js";
5
+ import { printCriticalFailureToConsoleAndExit } from "../../common/output.js";
4
6
  import { confirmLinkedValue, readLinkedConfig, USE_LINKED_DETAILS, } from "../../common/read-linked-config.js";
7
+ import { isInteractiveTerminal } from "../../common/terminal.js";
5
8
  import { cleanupTest, setupAuthenticatedNock, setupTestEnvironment, TEST_ACCOUNT_NAME, TEST_API_BASE, TEST_AUTH_TOKEN, TEST_PROJECT_NAME, } from "./test-utils.js";
6
9
  jest.mock("../../common/read-linked-config.js", () => ({
7
10
  __esModule: true,
@@ -14,11 +17,16 @@ jest.mock("../../common/read-linked-config.js", () => ({
14
17
  argv["useLinkedDetails"] = false;
15
18
  }),
16
19
  }));
20
+ jest.mock("../../common/terminal.js", () => ({
21
+ __esModule: true,
22
+ isInteractiveTerminal: jest.fn().mockReturnValue(true),
23
+ }));
17
24
  describe("Linked-config cascade decline", () => {
18
25
  beforeEach(() => {
19
26
  setupTestEnvironment();
20
27
  nock.disableNetConnect();
21
28
  jest.clearAllMocks();
29
+ jest.mocked(isInteractiveTerminal).mockReturnValue(true);
22
30
  });
23
31
  afterEach(() => {
24
32
  cleanupTest();
@@ -77,5 +85,59 @@ describe("Linked-config cascade decline", () => {
77
85
  expect(confirmLinkedValue).toHaveBeenCalledTimes(1);
78
86
  expect(argv[USE_LINKED_DETAILS]).toBe(false);
79
87
  });
88
+ it("uses linked account silently when terminal is non-interactive", async () => {
89
+ jest.mocked(readLinkedConfig).mockResolvedValue({
90
+ accountName: TEST_ACCOUNT_NAME,
91
+ });
92
+ jest.mocked(isInteractiveTerminal).mockReturnValue(false);
93
+ const argv = { authToken: TEST_AUTH_TOKEN };
94
+ await fetchAccount(argv);
95
+ expect(confirmLinkedValue).not.toHaveBeenCalled();
96
+ expect(argv.account).toBe(TEST_ACCOUNT_NAME);
97
+ });
98
+ it("uses linked project silently when terminal is non-interactive", async () => {
99
+ jest.mocked(readLinkedConfig).mockResolvedValue({
100
+ accountName: TEST_ACCOUNT_NAME,
101
+ projectName: TEST_PROJECT_NAME,
102
+ });
103
+ jest.mocked(isInteractiveTerminal).mockReturnValue(false);
104
+ const argv = {
105
+ authToken: TEST_AUTH_TOKEN,
106
+ account: TEST_ACCOUNT_NAME,
107
+ };
108
+ await fetchProject(argv);
109
+ expect(confirmLinkedValue).not.toHaveBeenCalled();
110
+ expect(argv.project).toBe(TEST_PROJECT_NAME);
111
+ });
112
+ it("fails loudly when --project is omitted in non-interactive environments without linked config", async () => {
113
+ jest.mocked(readLinkedConfig).mockResolvedValue({});
114
+ jest.mocked(isInteractiveTerminal).mockReturnValue(false);
115
+ const argv = {
116
+ authToken: TEST_AUTH_TOKEN,
117
+ account: TEST_ACCOUNT_NAME,
118
+ };
119
+ await expect(fetchProject(argv)).rejects.toThrow("Process would exit");
120
+ expect(printCriticalFailureToConsoleAndExit).toHaveBeenCalledWith(expect.stringContaining("--project is required"));
121
+ expect(argv.project).toBeUndefined();
122
+ });
123
+ it("fails loudly when --account is omitted in non-interactive environments without linked config", async () => {
124
+ jest.mocked(readLinkedConfig).mockResolvedValue({});
125
+ jest.mocked(isInteractiveTerminal).mockReturnValue(false);
126
+ const argv = { authToken: TEST_AUTH_TOKEN };
127
+ await expect(fetchAccount(argv)).rejects.toThrow("Process would exit");
128
+ expect(printCriticalFailureToConsoleAndExit).toHaveBeenCalledWith(expect.stringContaining("--account is required"));
129
+ expect(argv.account).toBeUndefined();
130
+ });
131
+ it("fails loudly when --environment is omitted in non-interactive environments", async () => {
132
+ jest.mocked(isInteractiveTerminal).mockReturnValue(false);
133
+ const argv = {
134
+ authToken: TEST_AUTH_TOKEN,
135
+ account: TEST_ACCOUNT_NAME,
136
+ project: TEST_PROJECT_NAME,
137
+ };
138
+ await expect(fetchEnvironment(argv)).rejects.toThrow("Process would exit");
139
+ expect(printCriticalFailureToConsoleAndExit).toHaveBeenCalledWith(expect.stringContaining("--environment is required"));
140
+ expect(argv.environment).toBeUndefined();
141
+ });
80
142
  });
81
143
  //# sourceMappingURL=linked-config-cascade.integration.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"linked-config-cascade.integration.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/linked-config-cascade.integration.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Integration tests for the linked-config cascade decline behaviour.\n *\n * When a user is prompted \"Use linked account 'X'?\" and answers No, the\n * useLinkedDetails flag is set to false on argv. Every downstream middleware\n * (fetchProject) must respect that flag and skip their own linked-value prompts —\n * so a manually selected account can never be accidentally paired with a stale\n * linked project.\n */\n/// <reference types=\"jest\" />\nimport nock from \"nock\";\nimport { fetchAccount } from \"../../common/middleware/get-account-param.js\";\nimport { fetchProject } from \"../../common/middleware/get-project-param.js\";\nimport {\n confirmLinkedValue,\n readLinkedConfig,\n USE_LINKED_DETAILS,\n} from \"../../common/read-linked-config.js\";\nimport {\n cleanupTest,\n setupAuthenticatedNock,\n setupTestEnvironment,\n TEST_ACCOUNT_NAME,\n TEST_API_BASE,\n TEST_AUTH_TOKEN,\n TEST_PROJECT_NAME,\n} from \"./test-utils.js\";\n\njest.mock(\"../../common/read-linked-config.js\", () => ({\n __esModule: true,\n confirmLinkedValue: jest.fn().mockResolvedValue(true),\n readLinkedConfig: jest.fn().mockResolvedValue({}),\n USE_LINKED_DETAILS: \"useLinkedDetails\",\n ignoreLinkedDetails: jest\n .fn()\n .mockImplementation((argv: Record<string, unknown>) => {\n argv[\"useLinkedDetails\"] = false;\n }),\n}));\n\ndescribe(\"Linked-config cascade decline\", () => {\n beforeEach(() => {\n setupTestEnvironment();\n nock.disableNetConnect();\n jest.clearAllMocks();\n });\n\n afterEach(() => {\n cleanupTest();\n });\n\n it(\"sets useLinkedDetails to false when user declines linked account\", async () => {\n jest.mocked(readLinkedConfig).mockResolvedValue({\n accountName: TEST_ACCOUNT_NAME,\n });\n // User says No to the linked account prompt\n jest.mocked(confirmLinkedValue).mockResolvedValue(false);\n\n setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/accounts\")\n .reply(200, { data: [{ name: TEST_ACCOUNT_NAME }] });\n\n const argv: Record<string, unknown> = { authToken: TEST_AUTH_TOKEN };\n await fetchAccount(argv);\n\n expect(confirmLinkedValue).toHaveBeenCalledTimes(1);\n expect(argv[USE_LINKED_DETAILS]).toBe(false);\n // account was resolved via the API selection path, not from linked config\n expect(argv.account).toBe(TEST_ACCOUNT_NAME);\n });\n\n it(\"skips linked project prompt when useLinkedDetails is false\", async () => {\n jest.mocked(readLinkedConfig).mockResolvedValue({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n });\n // confirmLinkedValue should never be reached for the project step\n jest\n .mocked(confirmLinkedValue)\n .mockRejectedValue(\n new Error(\"fetchProject must not call confirmLinkedValue after decline\")\n );\n\n setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .query({ accountName: TEST_ACCOUNT_NAME })\n .reply(200, { data: [{ name: TEST_PROJECT_NAME }] });\n\n const argv: Record<string, unknown> = {\n authToken: TEST_AUTH_TOKEN,\n account: TEST_ACCOUNT_NAME,\n [USE_LINKED_DETAILS]: false,\n };\n await fetchProject(argv);\n\n expect(confirmLinkedValue).not.toHaveBeenCalled();\n expect(argv.project).toBe(TEST_PROJECT_NAME);\n });\n\n it(\"full cascade: declining account prevents linked project prompt\", async () => {\n jest.mocked(readLinkedConfig).mockResolvedValue({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n });\n\n // Only the first call (account) returns false — if project middleware calls\n // confirmLinkedValue, the mock will return false again and it'll fall through\n // to the API. The real test is the call count.\n jest.mocked(confirmLinkedValue).mockResolvedValueOnce(false);\n\n setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/accounts\")\n .reply(200, { data: [{ name: TEST_ACCOUNT_NAME }] });\n\n setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .query({ accountName: TEST_ACCOUNT_NAME })\n .reply(200, { data: [{ name: TEST_PROJECT_NAME }] });\n\n const argv: Record<string, unknown> = { authToken: TEST_AUTH_TOKEN };\n\n await fetchAccount(argv);\n await fetchProject(argv);\n\n // confirmLinkedValue must only have been called once (for the account prompt).\n // The cascade flag prevented the project prompt.\n expect(confirmLinkedValue).toHaveBeenCalledTimes(1);\n expect(argv[USE_LINKED_DETAILS]).toBe(false);\n });\n});\n"]}
1
+ {"version":3,"file":"linked-config-cascade.integration.test.js","sourceRoot":"","sources":["../../../src/__tests__/integration/linked-config-cascade.integration.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Integration tests for the linked-config cascade decline behaviour.\n *\n * When a user is prompted \"Use linked account 'X'?\" and answers No, the\n * useLinkedDetails flag is set to false on argv. Every downstream middleware\n * (fetchProject) must respect that flag and skip their own linked-value prompts —\n * so a manually selected account can never be accidentally paired with a stale\n * linked project.\n */\n/// <reference types=\"jest\" />\nimport nock from \"nock\";\nimport { fetchAccount } from \"../../common/middleware/get-account-param.js\";\nimport { fetchEnvironment } from \"../../common/middleware/get-environment-param.js\";\nimport { fetchProject } from \"../../common/middleware/get-project-param.js\";\nimport { printCriticalFailureToConsoleAndExit } from \"../../common/output.js\";\nimport {\n confirmLinkedValue,\n readLinkedConfig,\n USE_LINKED_DETAILS,\n} from \"../../common/read-linked-config.js\";\nimport { isInteractiveTerminal } from \"../../common/terminal.js\";\nimport {\n cleanupTest,\n setupAuthenticatedNock,\n setupTestEnvironment,\n TEST_ACCOUNT_NAME,\n TEST_API_BASE,\n TEST_AUTH_TOKEN,\n TEST_PROJECT_NAME,\n} from \"./test-utils.js\";\n\njest.mock(\"../../common/read-linked-config.js\", () => ({\n __esModule: true,\n confirmLinkedValue: jest.fn().mockResolvedValue(true),\n readLinkedConfig: jest.fn().mockResolvedValue({}),\n USE_LINKED_DETAILS: \"useLinkedDetails\",\n ignoreLinkedDetails: jest\n .fn()\n .mockImplementation((argv: Record<string, unknown>) => {\n argv[\"useLinkedDetails\"] = false;\n }),\n}));\n\njest.mock(\"../../common/terminal.js\", () => ({\n __esModule: true,\n isInteractiveTerminal: jest.fn().mockReturnValue(true),\n}));\n\ndescribe(\"Linked-config cascade decline\", () => {\n beforeEach(() => {\n setupTestEnvironment();\n nock.disableNetConnect();\n jest.clearAllMocks();\n jest.mocked(isInteractiveTerminal).mockReturnValue(true);\n });\n\n afterEach(() => {\n cleanupTest();\n });\n\n it(\"sets useLinkedDetails to false when user declines linked account\", async () => {\n jest.mocked(readLinkedConfig).mockResolvedValue({\n accountName: TEST_ACCOUNT_NAME,\n });\n // User says No to the linked account prompt\n jest.mocked(confirmLinkedValue).mockResolvedValue(false);\n\n setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/accounts\")\n .reply(200, { data: [{ name: TEST_ACCOUNT_NAME }] });\n\n const argv: Record<string, unknown> = { authToken: TEST_AUTH_TOKEN };\n await fetchAccount(argv);\n\n expect(confirmLinkedValue).toHaveBeenCalledTimes(1);\n expect(argv[USE_LINKED_DETAILS]).toBe(false);\n // account was resolved via the API selection path, not from linked config\n expect(argv.account).toBe(TEST_ACCOUNT_NAME);\n });\n\n it(\"skips linked project prompt when useLinkedDetails is false\", async () => {\n jest.mocked(readLinkedConfig).mockResolvedValue({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n });\n // confirmLinkedValue should never be reached for the project step\n jest\n .mocked(confirmLinkedValue)\n .mockRejectedValue(\n new Error(\"fetchProject must not call confirmLinkedValue after decline\")\n );\n\n setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .query({ accountName: TEST_ACCOUNT_NAME })\n .reply(200, { data: [{ name: TEST_PROJECT_NAME }] });\n\n const argv: Record<string, unknown> = {\n authToken: TEST_AUTH_TOKEN,\n account: TEST_ACCOUNT_NAME,\n [USE_LINKED_DETAILS]: false,\n };\n await fetchProject(argv);\n\n expect(confirmLinkedValue).not.toHaveBeenCalled();\n expect(argv.project).toBe(TEST_PROJECT_NAME);\n });\n\n it(\"full cascade: declining account prevents linked project prompt\", async () => {\n jest.mocked(readLinkedConfig).mockResolvedValue({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n });\n\n // Only the first call (account) returns false — if project middleware calls\n // confirmLinkedValue, the mock will return false again and it'll fall through\n // to the API. The real test is the call count.\n jest.mocked(confirmLinkedValue).mockResolvedValueOnce(false);\n\n setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/accounts\")\n .reply(200, { data: [{ name: TEST_ACCOUNT_NAME }] });\n\n setupAuthenticatedNock(nock(TEST_API_BASE))\n .get(\"/v1/projects\")\n .query({ accountName: TEST_ACCOUNT_NAME })\n .reply(200, { data: [{ name: TEST_PROJECT_NAME }] });\n\n const argv: Record<string, unknown> = { authToken: TEST_AUTH_TOKEN };\n\n await fetchAccount(argv);\n await fetchProject(argv);\n\n // confirmLinkedValue must only have been called once (for the account prompt).\n // The cascade flag prevented the project prompt.\n expect(confirmLinkedValue).toHaveBeenCalledTimes(1);\n expect(argv[USE_LINKED_DETAILS]).toBe(false);\n });\n\n it(\"uses linked account silently when terminal is non-interactive\", async () => {\n jest.mocked(readLinkedConfig).mockResolvedValue({\n accountName: TEST_ACCOUNT_NAME,\n });\n jest.mocked(isInteractiveTerminal).mockReturnValue(false);\n\n const argv: Record<string, unknown> = { authToken: TEST_AUTH_TOKEN };\n await fetchAccount(argv);\n\n expect(confirmLinkedValue).not.toHaveBeenCalled();\n expect(argv.account).toBe(TEST_ACCOUNT_NAME);\n });\n\n it(\"uses linked project silently when terminal is non-interactive\", async () => {\n jest.mocked(readLinkedConfig).mockResolvedValue({\n accountName: TEST_ACCOUNT_NAME,\n projectName: TEST_PROJECT_NAME,\n });\n jest.mocked(isInteractiveTerminal).mockReturnValue(false);\n\n const argv: Record<string, unknown> = {\n authToken: TEST_AUTH_TOKEN,\n account: TEST_ACCOUNT_NAME,\n };\n await fetchProject(argv);\n\n expect(confirmLinkedValue).not.toHaveBeenCalled();\n expect(argv.project).toBe(TEST_PROJECT_NAME);\n });\n\n it(\"fails loudly when --project is omitted in non-interactive environments without linked config\", async () => {\n jest.mocked(readLinkedConfig).mockResolvedValue({});\n jest.mocked(isInteractiveTerminal).mockReturnValue(false);\n\n const argv: Record<string, unknown> = {\n authToken: TEST_AUTH_TOKEN,\n account: TEST_ACCOUNT_NAME,\n };\n\n // The guard fires before any API call, so no nock interceptor is needed.\n await expect(fetchProject(argv)).rejects.toThrow(\"Process would exit\");\n\n expect(printCriticalFailureToConsoleAndExit).toHaveBeenCalledWith(\n expect.stringContaining(\"--project is required\")\n );\n expect(argv.project).toBeUndefined();\n });\n\n it(\"fails loudly when --account is omitted in non-interactive environments without linked config\", async () => {\n jest.mocked(readLinkedConfig).mockResolvedValue({});\n jest.mocked(isInteractiveTerminal).mockReturnValue(false);\n\n const argv: Record<string, unknown> = { authToken: TEST_AUTH_TOKEN };\n\n // The guard fires before any API call, so no nock interceptor is needed.\n await expect(fetchAccount(argv)).rejects.toThrow(\"Process would exit\");\n\n expect(printCriticalFailureToConsoleAndExit).toHaveBeenCalledWith(\n expect.stringContaining(\"--account is required\")\n );\n expect(argv.account).toBeUndefined();\n });\n\n it(\"fails loudly when --environment is omitted in non-interactive environments\", async () => {\n jest.mocked(isInteractiveTerminal).mockReturnValue(false);\n\n // No argv.dir means getGitBranch returns undefined, falling through to the\n // non-interactive guard before any API call is made.\n const argv: Record<string, unknown> = {\n authToken: TEST_AUTH_TOKEN,\n account: TEST_ACCOUNT_NAME,\n project: TEST_PROJECT_NAME,\n };\n\n await expect(fetchEnvironment(argv)).rejects.toThrow(\"Process would exit\");\n\n expect(printCriticalFailureToConsoleAndExit).toHaveBeenCalledWith(\n expect.stringContaining(\"--environment is required\")\n );\n expect(argv.environment).toBeUndefined();\n });\n});\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"authentication.d.ts","sourceRoot":"","sources":["../../../src/common/middleware/authentication.ts"],"names":[],"mappings":"AA6BA,wBAAsB,YAAY,CAAC,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,iBAmC9D"}
1
+ {"version":3,"file":"authentication.d.ts","sourceRoot":"","sources":["../../../src/common/middleware/authentication.ts"],"names":[],"mappings":"AAwBA,wBAAsB,YAAY,CAAC,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,iBAmC9D"}
@@ -2,12 +2,10 @@ import { confirm } from "@inquirer/prompts";
2
2
  import { login } from "../../login/login.js";
3
3
  import { getAuthToken } from "../../login/tokens.js";
4
4
  import { printCriticalFailureToConsoleAndExit } from "../output.js";
5
+ import { isInteractiveTerminal } from "../terminal.js";
5
6
  async function fail() {
6
7
  await printCriticalFailureToConsoleAndExit(`You are not authenticated. Please run \`zuplo login\` to log in or set an api key.`);
7
8
  }
8
- function isInteractive() {
9
- return process.stdout.isTTY === true && !process.env.CI;
10
- }
11
9
  async function failNonInteractive() {
12
10
  await printCriticalFailureToConsoleAndExit(`Authentication required. This command requires an API key when running in non-interactive mode (e.g., CI/CD).
13
11
 
@@ -24,7 +22,7 @@ export async function authenticate(argv) {
24
22
  }
25
23
  let token = await getAuthToken();
26
24
  if (!token) {
27
- if (!isInteractive()) {
25
+ if (!isInteractiveTerminal()) {
28
26
  return failNonInteractive();
29
27
  }
30
28
  const result = await confirm({
@@ -1 +1 @@
1
- {"version":3,"file":"authentication.js","sourceRoot":"","sources":["../../../src/common/middleware/authentication.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,oCAAoC,EAAE,MAAM,cAAc,CAAC;AAEpE,KAAK,UAAU,IAAI;IACjB,MAAM,oCAAoC,CACxC,oFAAoF,CACrF,CAAC;AACJ,CAAC;AAED,SAAS,aAAa;IAGpB,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED,KAAK,UAAU,kBAAkB;IAC/B,MAAM,oCAAoC,CACxC;;;;;;uDAMmD,CACpD,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAA4B;IAC7D,IAAI,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QACpB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACpD,OAAO;IACT,CAAC;IAED,IAAI,KAAK,GAAG,MAAM,YAAY,EAAE,CAAC;IAEjC,IAAI,CAAC,KAAK,EAAE,CAAC;QAEX,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;YACrB,OAAO,kBAAkB,EAAE,CAAC;QAC9B,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;YAC3B,OAAO,EAAE,sDAAsD;SAChE,CAAC,CAAC;QAGH,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QAGD,MAAM,KAAK,EAAE,CAAC;QAGd,KAAK,GAAG,MAAM,YAAY,EAAE,CAAC;QAC7B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;IACH,CAAC;IAGD,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;AAC5C,CAAC","sourcesContent":["import { confirm } from \"@inquirer/prompts\";\nimport { login } from \"../../login/login.js\";\nimport { getAuthToken } from \"../../login/tokens.js\";\nimport { printCriticalFailureToConsoleAndExit } from \"../output.js\";\n\nasync function fail() {\n await printCriticalFailureToConsoleAndExit(\n `You are not authenticated. Please run \\`zuplo login\\` to log in or set an api key.`\n );\n}\n\nfunction isInteractive(): boolean {\n // Check if running in an interactive terminal\n // biome-ignore lint/style/noProcessEnv: Migrated from ESLint\n return process.stdout.isTTY === true && !process.env.CI;\n}\n\nasync function failNonInteractive() {\n await printCriticalFailureToConsoleAndExit(\n `Authentication required. This command requires an API key when running in non-interactive mode (e.g., CI/CD).\n\nPlease provide an API key using one of the following methods:\n 1. Pass the --api-key (or --apikey) flag: zuplo <command> --api-key YOUR_API_KEY\n 2. Set the environment variable: ZUPLO_API_KEY=YOUR_API_KEY\n\nYou can create an API key at: https://portal.zuplo.com`\n );\n}\n\nexport async function authenticate(argv: { \"api-key\"?: string }) {\n if (argv[\"api-key\"]) {\n Object.assign(argv, { authToken: argv[\"api-key\"] });\n return;\n }\n\n let token = await getAuthToken();\n\n if (!token) {\n // Check if we're in a non-interactive environment\n if (!isInteractive()) {\n return failNonInteractive();\n }\n\n const result = await confirm({\n message: \"You are not authenticated. Would you like to log in?\",\n });\n\n // IF the user said no, then return an error\n if (!result) {\n return fail();\n }\n\n // Login\n await login();\n\n // Validate the login again\n token = await getAuthToken();\n if (!token) {\n return fail();\n }\n }\n\n // Assign the token to the args\n Object.assign(argv, { authToken: token });\n}\n"]}
1
+ {"version":3,"file":"authentication.js","sourceRoot":"","sources":["../../../src/common/middleware/authentication.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,oCAAoC,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,KAAK,UAAU,IAAI;IACjB,MAAM,oCAAoC,CACxC,oFAAoF,CACrF,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,kBAAkB;IAC/B,MAAM,oCAAoC,CACxC;;;;;;uDAMmD,CACpD,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAA4B;IAC7D,IAAI,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QACpB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACpD,OAAO;IACT,CAAC;IAED,IAAI,KAAK,GAAG,MAAM,YAAY,EAAE,CAAC;IAEjC,IAAI,CAAC,KAAK,EAAE,CAAC;QAEX,IAAI,CAAC,qBAAqB,EAAE,EAAE,CAAC;YAC7B,OAAO,kBAAkB,EAAE,CAAC;QAC9B,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;YAC3B,OAAO,EAAE,sDAAsD;SAChE,CAAC,CAAC;QAGH,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QAGD,MAAM,KAAK,EAAE,CAAC;QAGd,KAAK,GAAG,MAAM,YAAY,EAAE,CAAC;QAC7B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;IACH,CAAC;IAGD,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;AAC5C,CAAC","sourcesContent":["import { confirm } from \"@inquirer/prompts\";\nimport { login } from \"../../login/login.js\";\nimport { getAuthToken } from \"../../login/tokens.js\";\nimport { printCriticalFailureToConsoleAndExit } from \"../output.js\";\nimport { isInteractiveTerminal } from \"../terminal.js\";\n\nasync function fail() {\n await printCriticalFailureToConsoleAndExit(\n `You are not authenticated. Please run \\`zuplo login\\` to log in or set an api key.`\n );\n}\n\nasync function failNonInteractive() {\n await printCriticalFailureToConsoleAndExit(\n `Authentication required. This command requires an API key when running in non-interactive mode (e.g., CI/CD).\n\nPlease provide an API key using one of the following methods:\n 1. Pass the --api-key (or --apikey) flag: zuplo <command> --api-key YOUR_API_KEY\n 2. Set the environment variable: ZUPLO_API_KEY=YOUR_API_KEY\n\nYou can create an API key at: https://portal.zuplo.com`\n );\n}\n\nexport async function authenticate(argv: { \"api-key\"?: string }) {\n if (argv[\"api-key\"]) {\n Object.assign(argv, { authToken: argv[\"api-key\"] });\n return;\n }\n\n let token = await getAuthToken();\n\n if (!token) {\n // Check if we're in a non-interactive environment\n if (!isInteractiveTerminal()) {\n return failNonInteractive();\n }\n\n const result = await confirm({\n message: \"You are not authenticated. Would you like to log in?\",\n });\n\n // IF the user said no, then return an error\n if (!result) {\n return fail();\n }\n\n // Login\n await login();\n\n // Validate the login again\n token = await getAuthToken();\n if (!token) {\n return fail();\n }\n }\n\n // Assign the token to the args\n Object.assign(argv, { authToken: token });\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"get-account-param.d.ts","sourceRoot":"","sources":["../../../src/common/middleware/get-account-param.ts"],"names":[],"mappings":"AAiBA,wBAAsB,YAAY,CAAC,IAAI,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,iBA+ElE"}
1
+ {"version":3,"file":"get-account-param.d.ts","sourceRoot":"","sources":["../../../src/common/middleware/get-account-param.ts"],"names":[],"mappings":"AAkBA,wBAAsB,YAAY,CAAC,IAAI,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,iBA6FlE"}
@@ -3,6 +3,7 @@ import { logger } from "../logger.js";
3
3
  import { printCriticalFailureToConsoleAndExit, textOrJson } from "../output.js";
4
4
  import { confirmLinkedValue, readLinkedConfig, USE_LINKED_DETAILS, } from "../read-linked-config.js";
5
5
  import settings from "../settings.js";
6
+ import { isInteractiveTerminal } from "../terminal.js";
6
7
  export async function fetchAccount(argv) {
7
8
  if (argv.account && typeof argv.account === "string") {
8
9
  return;
@@ -13,6 +14,10 @@ export async function fetchAccount(argv) {
13
14
  if (argv[USE_LINKED_DETAILS] !== false) {
14
15
  const linked = await readLinkedConfig(argv);
15
16
  if (linked.accountName) {
17
+ if (!isInteractiveTerminal()) {
18
+ argv.account = linked.accountName;
19
+ return;
20
+ }
16
21
  const useLinked = await confirmLinkedValue(`Use linked account '${linked.accountName}'?`);
17
22
  if (useLinked) {
18
23
  argv.account = linked.accountName;
@@ -21,6 +26,10 @@ export async function fetchAccount(argv) {
21
26
  argv[USE_LINKED_DETAILS] = false;
22
27
  }
23
28
  }
29
+ if (!isInteractiveTerminal()) {
30
+ await printCriticalFailureToConsoleAndExit("Error: --account is required in non-interactive environments (e.g. CI/CD).\nUse the --account flag to specify an account name.");
31
+ return;
32
+ }
24
33
  const response = await fetch(`${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/accounts`, {
25
34
  method: "GET",
26
35
  headers: {
@@ -44,7 +53,7 @@ export async function fetchAccount(argv) {
44
53
  }
45
54
  catch (error) {
46
55
  if (error.isTtyError || error.name === "ExitPromptError") {
47
- process.exit(0);
56
+ process.exit(1);
48
57
  }
49
58
  logger.trace("Failed to select account", error);
50
59
  process.exit(1);
@@ -1 +1 @@
1
- {"version":3,"file":"get-account-param.js","sourceRoot":"","sources":["../../../src/common/middleware/get-account-param.ts"],"names":[],"mappings":"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","sourcesContent":["import { select } from \"@inquirer/prompts\";\nimport { logger } from \"../logger.js\";\nimport { printCriticalFailureToConsoleAndExit, textOrJson } from \"../output.js\";\nimport {\n confirmLinkedValue,\n readLinkedConfig,\n USE_LINKED_DETAILS,\n} from \"../read-linked-config.js\";\nimport settings from \"../settings.js\";\n\n/**\n * Middleware that ensures an account is provided in argv. If not, prompts the user.\n *\n * NOTE: This middleware MUST come after the authenticate middleware\n * because the authenticate middleware gets the account if the user is\n * authenticating with an API key.\n */\nexport async function fetchAccount(argv: { [key: string]: unknown }) {\n if (argv.account && typeof argv.account === \"string\") {\n return;\n }\n // In the case where the user supplied an api-key, they don't need to supply an account.\n if (argv[\"api-key\"]) {\n return;\n }\n\n // If the account is linked, confirm with the user before prompting for selection.\n // Skip if useLinkedDetails is false (e.g. set by the link command, which always re-prompts from scratch).\n if (argv[USE_LINKED_DETAILS] !== false) {\n const linked = await readLinkedConfig(argv);\n if (linked.accountName) {\n const useLinked = await confirmLinkedValue(\n `Use linked account '${linked.accountName}'?`\n );\n if (useLinked) {\n argv.account = linked.accountName;\n return;\n }\n // User declined — signal downstream middleware to also skip linked values\n // so a manually selected account isn't paired with a stale linked project.\n argv[USE_LINKED_DETAILS] = false;\n }\n }\n\n // Make a call to the API key\n const response = await fetch(\n `${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/accounts`,\n {\n method: \"GET\",\n headers: {\n // biome-ignore lint/style/useNamingConvention: External API property\n Authorization: `Bearer ${argv.authToken}`,\n },\n }\n );\n\n if (response.ok) {\n const accountsJson: { data: { name: string }[] } = await response.json();\n\n try {\n argv.account = await select({\n message: \"Select the account\",\n choices: accountsJson.data\n .map((acc) => {\n return {\n name: acc.name,\n value: acc.name,\n };\n })\n .sort((a, b) => a.name.localeCompare(b.name)),\n });\n } catch (error) {\n if (error.isTtyError || error.name === \"ExitPromptError\") {\n process.exit(0);\n }\n logger.trace(\"Failed to select account\", error);\n process.exit(1);\n }\n } else {\n if (response.status === 404) {\n await printCriticalFailureToConsoleAndExit(\n \"You don't have a Zuplo account. Create one at https://zuplo.com and try again.\"\n );\n }\n logger.trace(\n {\n status: response.status,\n statusText: response.statusText,\n response: textOrJson(await response.text()),\n },\n \"Failed to request accounts for user\"\n );\n await printCriticalFailureToConsoleAndExit(\n \"Error: Failed to request accounts for the user.\"\n );\n }\n}\n"]}
1
+ {"version":3,"file":"get-account-param.js","sourceRoot":"","sources":["../../../src/common/middleware/get-account-param.ts"],"names":[],"mappings":"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","sourcesContent":["import { select } from \"@inquirer/prompts\";\nimport { logger } from \"../logger.js\";\nimport { printCriticalFailureToConsoleAndExit, textOrJson } from \"../output.js\";\nimport {\n confirmLinkedValue,\n readLinkedConfig,\n USE_LINKED_DETAILS,\n} from \"../read-linked-config.js\";\nimport settings from \"../settings.js\";\nimport { isInteractiveTerminal } from \"../terminal.js\";\n\n/**\n * Middleware that ensures an account is provided in argv. If not, prompts the user.\n *\n * NOTE: This middleware MUST come after the authenticate middleware\n * because the authenticate middleware gets the account if the user is\n * authenticating with an API key.\n */\nexport async function fetchAccount(argv: { [key: string]: unknown }) {\n if (argv.account && typeof argv.account === \"string\") {\n return;\n }\n // In the case where the user supplied an api-key, they don't need to supply an account.\n if (argv[\"api-key\"]) {\n return;\n }\n\n // If the account is linked, confirm with the user before prompting for selection.\n // Skip if useLinkedDetails is false (e.g. set by the link command, which always re-prompts from scratch).\n if (argv[USE_LINKED_DETAILS] !== false) {\n const linked = await readLinkedConfig(argv);\n if (linked.accountName) {\n if (!isInteractiveTerminal()) {\n argv.account = linked.accountName;\n return;\n }\n\n const useLinked = await confirmLinkedValue(\n `Use linked account '${linked.accountName}'?`\n );\n if (useLinked) {\n argv.account = linked.accountName;\n return;\n }\n // User declined — signal downstream middleware to also skip linked values\n // so a manually selected account isn't paired with a stale linked project.\n argv[USE_LINKED_DETAILS] = false;\n }\n }\n\n // In non-interactive environments, --account must be passed explicitly.\n // We check here, before the API call, to avoid an unnecessary network round-trip.\n if (!isInteractiveTerminal()) {\n await printCriticalFailureToConsoleAndExit(\n \"Error: --account is required in non-interactive environments (e.g. CI/CD).\\nUse the --account flag to specify an account name.\"\n );\n return;\n }\n\n // Make a call to the API key\n const response = await fetch(\n `${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/accounts`,\n {\n method: \"GET\",\n headers: {\n // biome-ignore lint/style/useNamingConvention: External API property\n Authorization: `Bearer ${argv.authToken}`,\n },\n }\n );\n\n if (response.ok) {\n const accountsJson: { data: { name: string }[] } = await response.json();\n\n try {\n argv.account = await select({\n message: \"Select the account\",\n choices: accountsJson.data\n .map((acc) => {\n return {\n name: acc.name,\n value: acc.name,\n };\n })\n .sort((a, b) => a.name.localeCompare(b.name)),\n });\n } catch (error) {\n if (error.isTtyError || error.name === \"ExitPromptError\") {\n process.exit(1);\n }\n logger.trace(\"Failed to select account\", error);\n process.exit(1);\n }\n } else {\n if (response.status === 404) {\n await printCriticalFailureToConsoleAndExit(\n \"You don't have a Zuplo account. Create one at https://zuplo.com and try again.\"\n );\n }\n logger.trace(\n {\n status: response.status,\n statusText: response.statusText,\n response: textOrJson(await response.text()),\n },\n \"Failed to request accounts for user\"\n );\n await printCriticalFailureToConsoleAndExit(\n \"Error: Failed to request accounts for the user.\"\n );\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"get-environment-param.d.ts","sourceRoot":"","sources":["../../../src/common/middleware/get-environment-param.ts"],"names":[],"mappings":"AAYA,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,iBAmMtE"}
1
+ {"version":3,"file":"get-environment-param.d.ts","sourceRoot":"","sources":["../../../src/common/middleware/get-environment-param.ts"],"names":[],"mappings":"AAaA,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,iBA4MtE"}
@@ -3,6 +3,7 @@ import { simpleGit } from "simple-git";
3
3
  import { logger } from "../logger.js";
4
4
  import { printCriticalFailureToConsoleAndExit, textOrJson } from "../output.js";
5
5
  import settings from "../settings.js";
6
+ import { isInteractiveTerminal } from "../terminal.js";
6
7
  import { prettyPrintEnvironmentPrompt } from "../utils/pretty-print-environment-prompt.js";
7
8
  export async function fetchEnvironment(argv) {
8
9
  if (argv.environment && typeof argv.environment === "string") {
@@ -19,6 +20,10 @@ export async function fetchEnvironment(argv) {
19
20
  argv.environment = branch;
20
21
  }
21
22
  else {
23
+ if (!isInteractiveTerminal()) {
24
+ await printCriticalFailureToConsoleAndExit("Error: --environment is required in non-interactive environments (e.g. CI/CD).\nUse the --environment flag to specify an environment name.");
25
+ return;
26
+ }
22
27
  const baseUrl = `${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/environments`;
23
28
  const queryParams = {
24
29
  accountName: argv.account,
@@ -88,9 +93,9 @@ export async function fetchEnvironment(argv) {
88
93
  }
89
94
  catch (error) {
90
95
  if (error.isTtyError || error.name === "ExitPromptError") {
91
- process.exit(0);
96
+ process.exit(1);
92
97
  }
93
- logger.trace("Failed to select account", error);
98
+ logger.trace("Failed to select environment", error);
94
99
  process.exit(1);
95
100
  }
96
101
  }
@@ -1 +1 @@
1
- {"version":3,"file":"get-environment-param.js","sourceRoot":"","sources":["../../../src/common/middleware/get-environment-param.ts"],"names":[],"mappings":"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","sourcesContent":["import { select } from \"@inquirer/prompts\";\nimport { simpleGit } from \"simple-git\";\nimport { Environment } from \"../api/lib.js\";\nimport { logger } from \"../logger.js\";\nimport { printCriticalFailureToConsoleAndExit, textOrJson } from \"../output.js\";\nimport settings from \"../settings.js\";\nimport { prettyPrintEnvironmentPrompt } from \"../utils/pretty-print-environment-prompt.js\";\n\n/**\n * Middleware that ensures a environment is provided in argv. If not, prompts the user.\n * Note: must be called after get account param and get project param.\n */\nexport async function fetchEnvironment(argv: { [key: string]: unknown }) {\n if (argv.environment && typeof argv.environment === \"string\") {\n return;\n }\n\n if (!argv.account || typeof argv.account !== \"string\") {\n throw new Error(\"Invalid state: account is not set.\");\n }\n if (!argv.project || typeof argv.project !== \"string\") {\n throw new Error(\"Invalid state: project is not set.\");\n }\n\n // 1. Check if the dir argument is set and if we are in a git repository\n // If we are, get the current branch and use that as the environment name.\n const branch = await getGitBranch(argv);\n\n if (branch && !argv[\"fetch-environments\"]) {\n // If the branch is set, use that as the environment name\n argv.environment = branch;\n } else {\n const baseUrl = `${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/environments`;\n\n const queryParams: Record<string, string> = {\n // This is safe because of the previous middleware where we set the account and project.\n accountName: argv.account!,\n projectName: argv.project!,\n };\n\n // Create the query string using URLSearchParams\n const url = new URL(baseUrl);\n url.search = new URLSearchParams(queryParams).toString();\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n // biome-ignore lint/style/useNamingConvention: External API property\n Authorization: `Bearer ${argv.authToken}`,\n },\n });\n\n if (response.ok) {\n const environmentJsonFromDeveloperAPI = (await response.json()) as {\n data: Environment[];\n };\n\n // Also fetch working-copy deployments, which are not returned by the\n // /v1/environments endpoint (it only returns self-managed and deployer-owned environments).\n const deploymentsUrl = new URL(\n `${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/accounts/${argv.account}/projects/${argv.project}/deployments`\n );\n const deploymentsResponse = await fetch(deploymentsUrl, {\n method: \"GET\",\n headers: {\n // biome-ignore lint/style/useNamingConvention: External API property\n Authorization: `Bearer ${argv.authToken}`,\n },\n });\n\n if (deploymentsResponse.ok) {\n const { data: deployments } = (await deploymentsResponse.json()) as {\n data: Array<{\n name: string;\n accountName: string;\n projectName: string;\n branchName: string;\n environmentType: string;\n createdOn: string;\n }>;\n };\n\n const workingCopies: Environment[] = deployments\n .filter((d) => d.environmentType === \"WORKING_COPY\")\n .map((d) => ({\n name: d.name,\n accountName: d.accountName,\n projectName: d.projectName,\n branchName: d.branchName,\n environmentType: \"working_copy\",\n createdOn: new Date(d.createdOn),\n }));\n\n environmentJsonFromDeveloperAPI.data.push(...workingCopies);\n } else {\n logger.debug(\n {\n status: deploymentsResponse.status,\n statusText: deploymentsResponse.statusText,\n },\n \"Failed to fetch working-copy deployments for project\"\n );\n }\n\n if (environmentJsonFromDeveloperAPI.data.length === 0) {\n await printCriticalFailureToConsoleAndExit(\n \"Error: You don't have any environments for this project to link against. First, deploy this project using zuplo deploy and then link against that.\"\n );\n }\n\n // TODO: This logic doesn't work for working copies anymore due to\n // https://github.com/zuplo/core/blob/main/apps/tenant-api/prisma/migrations/v2_20250711155424_add-working-copy-main-check/migration.sql\n // and https://github.com/zuplo/core/blob/6595268b9d2e5edfb3a20cbb086ccd363cf115f9/apps/tenant-api/src/routes/managed-dedicated/environments.ts#L96-L102\n // Leaving this logic commented out for now since we may want to bring it back.\n // let numDevelopmentEnvironments =\n // environmentJsonFromDeveloperAPI.data.filter((env) => {\n // return env.environmentType === \"development\";\n // }).length;\n\n // if (numDevelopmentEnvironments <= 0) {\n // // Create one on-the-fly\n // const createDevelopmenEnvironmentResponse = await fetch(`${baseUrl}`, {\n // method: \"POST\",\n // headers: {\n // authorization: `Bearer ${argv.authToken}`,\n // \"Content-Type\": \"application/json\",\n // },\n // body: JSON.stringify({\n // accountName: argv.account,\n // projectName: argv.project,\n // environmentType: \"development\",\n // }),\n // });\n\n // if (!createDevelopmenEnvironmentResponse.ok) {\n // console.log(await createDevelopmenEnvironmentResponse.json());\n // logger.debug(\n // {\n // status: createDevelopmenEnvironmentResponse.status,\n // statusText: createDevelopmenEnvironmentResponse.statusText,\n // },\n // \"Failed to create a new instance of development environment\"\n // );\n // await printCriticalFailureToConsoleAndExit(\n // \"Failed to create a new development environment. Please try again later.\"\n // );\n // }\n\n // numDevelopmentEnvironments++;\n\n // // Re-fetch\n // const updatedResponse = await fetch(url, {\n // headers: {\n // authorization: `Bearer ${argv.authToken}`,\n // },\n // });\n\n // environmentJsonFromDeveloperAPI = (await updatedResponse.json()) as {\n // data: Environment[];\n // };\n // }\n\n const environments = environmentJsonFromDeveloperAPI.data\n .map((env) => {\n return {\n // Pass false: we always show the real env name in the selection list,\n // never collapse development instances to \"Default for local development\".\n [prettyPrintEnvironmentPrompt(env, false)]: env.name,\n };\n })\n .reduce((acc, curr) => {\n // biome-ignore lint/performance/noAccumulatingSpread: Migrated from ESLint\n return { ...acc, ...curr };\n });\n\n try {\n argv.environment = await select({\n message: \"Select the environment\",\n choices: Object.entries(environments)\n .map(([key, value]) => {\n return {\n name: key,\n value: value,\n };\n })\n .sort((a, b) => a.name.localeCompare(b.name)),\n });\n } catch (error) {\n if (error.isTtyError || error.name === \"ExitPromptError\") {\n process.exit(0);\n }\n logger.trace(\"Failed to select account\", error);\n process.exit(1);\n }\n } else {\n logger.trace(\n {\n status: response.status,\n statusText: response.statusText,\n response: textOrJson(await response.text()),\n },\n \"Failed to request environments for project\"\n );\n await printCriticalFailureToConsoleAndExit(\n \"Error: Failed to request environments for the project.\"\n );\n }\n }\n}\nasync function getGitBranch(argv: { [key: string]: unknown }) {\n let branch: string | undefined;\n const dir = argv.dir;\n if (dir && typeof dir === \"string\") {\n const git = simpleGit({ baseDir: dir });\n const isGitRepo = await git.checkIsRepo();\n if (isGitRepo) {\n // Get the current branch from Git\n const status = await git.status();\n if (!status.current) {\n throw new Error(\"Invalid state: Directory is in detached head state.\");\n }\n branch = status.current.trim();\n\n // @NOTE - gitlab returns HEAD as the current branch when running git.status()\n // https://forum.gitlab.com/t/why-i-cant-get-the-branch-name/72462/6\n if (branch === \"HEAD\") {\n // Fetch remote branches to ensure the latest information\n await git.fetch([\"--all\"]);\n\n const branchCommit = await git.branch([\"-a\"]);\n const branchRef = await git.branch([\n \"-r\",\n \"--contains\",\n branchCommit.current,\n ]);\n if (branchRef?.all?.[0]) {\n const originBranch = branchRef.all[0];\n const cleanOriginBranch = originBranch.replace(/^origin\\//, \"\");\n if (!cleanOriginBranch) {\n throw new Error(\n \"Invalid state: Directory is in detached head state.\"\n );\n }\n branch = cleanOriginBranch.trim();\n }\n }\n }\n }\n return branch;\n}\n"]}
1
+ {"version":3,"file":"get-environment-param.js","sourceRoot":"","sources":["../../../src/common/middleware/get-environment-param.ts"],"names":[],"mappings":"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","sourcesContent":["import { select } from \"@inquirer/prompts\";\nimport { simpleGit } from \"simple-git\";\nimport { Environment } from \"../api/lib.js\";\nimport { logger } from \"../logger.js\";\nimport { printCriticalFailureToConsoleAndExit, textOrJson } from \"../output.js\";\nimport settings from \"../settings.js\";\nimport { isInteractiveTerminal } from \"../terminal.js\";\nimport { prettyPrintEnvironmentPrompt } from \"../utils/pretty-print-environment-prompt.js\";\n\n/**\n * Middleware that ensures a environment is provided in argv. If not, prompts the user.\n * Note: must be called after get account param and get project param.\n */\nexport async function fetchEnvironment(argv: { [key: string]: unknown }) {\n if (argv.environment && typeof argv.environment === \"string\") {\n return;\n }\n\n if (!argv.account || typeof argv.account !== \"string\") {\n throw new Error(\"Invalid state: account is not set.\");\n }\n if (!argv.project || typeof argv.project !== \"string\") {\n throw new Error(\"Invalid state: project is not set.\");\n }\n\n // 1. Check if the dir argument is set and if we are in a git repository\n // If we are, get the current branch and use that as the environment name.\n const branch = await getGitBranch(argv);\n\n if (branch && !argv[\"fetch-environments\"]) {\n // If the branch is set, use that as the environment name\n argv.environment = branch;\n } else {\n // In non-interactive environments, --environment must be passed explicitly.\n // We check here, before the API calls, to avoid unnecessary network round-trips.\n if (!isInteractiveTerminal()) {\n await printCriticalFailureToConsoleAndExit(\n \"Error: --environment is required in non-interactive environments (e.g. CI/CD).\\nUse the --environment flag to specify an environment name.\"\n );\n return;\n }\n\n const baseUrl = `${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/environments`;\n\n const queryParams: Record<string, string> = {\n // This is safe because of the previous middleware where we set the account and project.\n accountName: argv.account!,\n projectName: argv.project!,\n };\n\n // Create the query string using URLSearchParams\n const url = new URL(baseUrl);\n url.search = new URLSearchParams(queryParams).toString();\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n // biome-ignore lint/style/useNamingConvention: External API property\n Authorization: `Bearer ${argv.authToken}`,\n },\n });\n\n if (response.ok) {\n const environmentJsonFromDeveloperAPI = (await response.json()) as {\n data: Environment[];\n };\n\n // Also fetch working-copy deployments, which are not returned by the\n // /v1/environments endpoint (it only returns self-managed and deployer-owned environments).\n const deploymentsUrl = new URL(\n `${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/accounts/${argv.account}/projects/${argv.project}/deployments`\n );\n const deploymentsResponse = await fetch(deploymentsUrl, {\n method: \"GET\",\n headers: {\n // biome-ignore lint/style/useNamingConvention: External API property\n Authorization: `Bearer ${argv.authToken}`,\n },\n });\n\n if (deploymentsResponse.ok) {\n const { data: deployments } = (await deploymentsResponse.json()) as {\n data: Array<{\n name: string;\n accountName: string;\n projectName: string;\n branchName: string;\n environmentType: string;\n createdOn: string;\n }>;\n };\n\n const workingCopies: Environment[] = deployments\n .filter((d) => d.environmentType === \"WORKING_COPY\")\n .map((d) => ({\n name: d.name,\n accountName: d.accountName,\n projectName: d.projectName,\n branchName: d.branchName,\n environmentType: \"working_copy\",\n createdOn: new Date(d.createdOn),\n }));\n\n environmentJsonFromDeveloperAPI.data.push(...workingCopies);\n } else {\n logger.debug(\n {\n status: deploymentsResponse.status,\n statusText: deploymentsResponse.statusText,\n },\n \"Failed to fetch working-copy deployments for project\"\n );\n }\n\n if (environmentJsonFromDeveloperAPI.data.length === 0) {\n await printCriticalFailureToConsoleAndExit(\n \"Error: You don't have any environments for this project to link against. First, deploy this project using zuplo deploy and then link against that.\"\n );\n }\n\n // TODO: This logic doesn't work for working copies anymore due to\n // https://github.com/zuplo/core/blob/main/apps/tenant-api/prisma/migrations/v2_20250711155424_add-working-copy-main-check/migration.sql\n // and https://github.com/zuplo/core/blob/6595268b9d2e5edfb3a20cbb086ccd363cf115f9/apps/tenant-api/src/routes/managed-dedicated/environments.ts#L96-L102\n // Leaving this logic commented out for now since we may want to bring it back.\n // let numDevelopmentEnvironments =\n // environmentJsonFromDeveloperAPI.data.filter((env) => {\n // return env.environmentType === \"development\";\n // }).length;\n\n // if (numDevelopmentEnvironments <= 0) {\n // // Create one on-the-fly\n // const createDevelopmenEnvironmentResponse = await fetch(`${baseUrl}`, {\n // method: \"POST\",\n // headers: {\n // authorization: `Bearer ${argv.authToken}`,\n // \"Content-Type\": \"application/json\",\n // },\n // body: JSON.stringify({\n // accountName: argv.account,\n // projectName: argv.project,\n // environmentType: \"development\",\n // }),\n // });\n\n // if (!createDevelopmenEnvironmentResponse.ok) {\n // console.log(await createDevelopmenEnvironmentResponse.json());\n // logger.debug(\n // {\n // status: createDevelopmenEnvironmentResponse.status,\n // statusText: createDevelopmenEnvironmentResponse.statusText,\n // },\n // \"Failed to create a new instance of development environment\"\n // );\n // await printCriticalFailureToConsoleAndExit(\n // \"Failed to create a new development environment. Please try again later.\"\n // );\n // }\n\n // numDevelopmentEnvironments++;\n\n // // Re-fetch\n // const updatedResponse = await fetch(url, {\n // headers: {\n // authorization: `Bearer ${argv.authToken}`,\n // },\n // });\n\n // environmentJsonFromDeveloperAPI = (await updatedResponse.json()) as {\n // data: Environment[];\n // };\n // }\n\n const environments = environmentJsonFromDeveloperAPI.data\n .map((env) => {\n return {\n // Pass false: we always show the real env name in the selection list,\n // never collapse development instances to \"Default for local development\".\n [prettyPrintEnvironmentPrompt(env, false)]: env.name,\n };\n })\n .reduce((acc, curr) => {\n // biome-ignore lint/performance/noAccumulatingSpread: Migrated from ESLint\n return { ...acc, ...curr };\n });\n\n try {\n argv.environment = await select({\n message: \"Select the environment\",\n choices: Object.entries(environments)\n .map(([key, value]) => {\n return {\n name: key,\n value: value,\n };\n })\n .sort((a, b) => a.name.localeCompare(b.name)),\n });\n } catch (error) {\n if (error.isTtyError || error.name === \"ExitPromptError\") {\n process.exit(1);\n }\n logger.trace(\"Failed to select environment\", error);\n process.exit(1);\n }\n } else {\n logger.trace(\n {\n status: response.status,\n statusText: response.statusText,\n response: textOrJson(await response.text()),\n },\n \"Failed to request environments for project\"\n );\n await printCriticalFailureToConsoleAndExit(\n \"Error: Failed to request environments for the project.\"\n );\n }\n }\n}\nasync function getGitBranch(argv: { [key: string]: unknown }) {\n let branch: string | undefined;\n const dir = argv.dir;\n if (dir && typeof dir === \"string\") {\n const git = simpleGit({ baseDir: dir });\n const isGitRepo = await git.checkIsRepo();\n if (isGitRepo) {\n // Get the current branch from Git\n const status = await git.status();\n if (!status.current) {\n throw new Error(\"Invalid state: Directory is in detached head state.\");\n }\n branch = status.current.trim();\n\n // @NOTE - gitlab returns HEAD as the current branch when running git.status()\n // https://forum.gitlab.com/t/why-i-cant-get-the-branch-name/72462/6\n if (branch === \"HEAD\") {\n // Fetch remote branches to ensure the latest information\n await git.fetch([\"--all\"]);\n\n const branchCommit = await git.branch([\"-a\"]);\n const branchRef = await git.branch([\n \"-r\",\n \"--contains\",\n branchCommit.current,\n ]);\n if (branchRef?.all?.[0]) {\n const originBranch = branchRef.all[0];\n const cleanOriginBranch = originBranch.replace(/^origin\\//, \"\");\n if (!cleanOriginBranch) {\n throw new Error(\n \"Invalid state: Directory is in detached head state.\"\n );\n }\n branch = cleanOriginBranch.trim();\n }\n }\n }\n }\n return branch;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"get-project-param.d.ts","sourceRoot":"","sources":["../../../src/common/middleware/get-project-param.ts"],"names":[],"mappings":"AAgBA,wBAAsB,YAAY,CAAC,IAAI,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,iBA4GlE"}
1
+ {"version":3,"file":"get-project-param.d.ts","sourceRoot":"","sources":["../../../src/common/middleware/get-project-param.ts"],"names":[],"mappings":"AAiBA,wBAAsB,YAAY,CAAC,IAAI,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,iBA0HlE"}