@project-ajax/sdk 0.0.59 → 0.0.61

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 (156) hide show
  1. package/dist/builder.d.ts +6 -1
  2. package/dist/builder.d.ts.map +1 -1
  3. package/dist/builder.js +4 -0
  4. package/dist/capabilities/sync.d.ts +10 -3
  5. package/dist/capabilities/sync.d.ts.map +1 -1
  6. package/dist/schema.d.ts +7 -1
  7. package/dist/schema.d.ts.map +1 -1
  8. package/dist/schema.js +4 -0
  9. package/dist/types.d.ts +13 -0
  10. package/dist/types.d.ts.map +1 -1
  11. package/package.json +3 -11
  12. package/src/builder.ts +15 -1
  13. package/src/capabilities/sync.ts +18 -3
  14. package/src/schema.ts +11 -2
  15. package/src/types.ts +15 -0
  16. package/dist/cli/api/client.d.ts +0 -212
  17. package/dist/cli/api/client.d.ts.map +0 -1
  18. package/dist/cli/api/client.js +0 -330
  19. package/dist/cli/api/result.d.ts +0 -43
  20. package/dist/cli/api/result.d.ts.map +0 -1
  21. package/dist/cli/api/result.js +0 -43
  22. package/dist/cli/bin/cli.d.ts +0 -3
  23. package/dist/cli/bin/cli.d.ts.map +0 -1
  24. package/dist/cli/bin/cli.js +0 -5
  25. package/dist/cli/commands/auth.d.ts +0 -2
  26. package/dist/cli/commands/auth.d.ts.map +0 -1
  27. package/dist/cli/commands/auth.impl.d.ts +0 -5
  28. package/dist/cli/commands/auth.impl.d.ts.map +0 -1
  29. package/dist/cli/commands/auth.impl.js +0 -45
  30. package/dist/cli/commands/auth.impl.test.d.ts +0 -2
  31. package/dist/cli/commands/auth.impl.test.d.ts.map +0 -1
  32. package/dist/cli/commands/auth.js +0 -56
  33. package/dist/cli/commands/bundle.d.ts +0 -2
  34. package/dist/cli/commands/bundle.d.ts.map +0 -1
  35. package/dist/cli/commands/bundle.impl.d.ts +0 -2
  36. package/dist/cli/commands/bundle.impl.d.ts.map +0 -1
  37. package/dist/cli/commands/bundle.impl.js +0 -21
  38. package/dist/cli/commands/bundle.impl.test.d.ts +0 -2
  39. package/dist/cli/commands/bundle.impl.test.d.ts.map +0 -1
  40. package/dist/cli/commands/bundle.js +0 -23
  41. package/dist/cli/commands/capabilities.d.ts +0 -2
  42. package/dist/cli/commands/capabilities.d.ts.map +0 -1
  43. package/dist/cli/commands/capabilities.impl.d.ts +0 -3
  44. package/dist/cli/commands/capabilities.impl.d.ts.map +0 -1
  45. package/dist/cli/commands/capabilities.impl.js +0 -40
  46. package/dist/cli/commands/capabilities.js +0 -24
  47. package/dist/cli/commands/connect.d.ts +0 -2
  48. package/dist/cli/commands/connect.d.ts.map +0 -1
  49. package/dist/cli/commands/connect.impl.d.ts +0 -6
  50. package/dist/cli/commands/connect.impl.d.ts.map +0 -1
  51. package/dist/cli/commands/connect.impl.js +0 -116
  52. package/dist/cli/commands/connect.js +0 -78
  53. package/dist/cli/commands/deploy.d.ts +0 -3
  54. package/dist/cli/commands/deploy.d.ts.map +0 -1
  55. package/dist/cli/commands/deploy.impl.d.ts +0 -6
  56. package/dist/cli/commands/deploy.impl.d.ts.map +0 -1
  57. package/dist/cli/commands/deploy.impl.js +0 -60
  58. package/dist/cli/commands/deploy.impl.test.d.ts +0 -2
  59. package/dist/cli/commands/deploy.impl.test.d.ts.map +0 -1
  60. package/dist/cli/commands/deploy.js +0 -22
  61. package/dist/cli/commands/env.d.ts +0 -2
  62. package/dist/cli/commands/env.d.ts.map +0 -1
  63. package/dist/cli/commands/env.impl.d.ts +0 -11
  64. package/dist/cli/commands/env.impl.d.ts.map +0 -1
  65. package/dist/cli/commands/env.impl.js +0 -62
  66. package/dist/cli/commands/env.js +0 -39
  67. package/dist/cli/commands/exec.d.ts +0 -3
  68. package/dist/cli/commands/exec.d.ts.map +0 -1
  69. package/dist/cli/commands/exec.impl.d.ts +0 -7
  70. package/dist/cli/commands/exec.impl.d.ts.map +0 -1
  71. package/dist/cli/commands/exec.impl.js +0 -123
  72. package/dist/cli/commands/exec.js +0 -30
  73. package/dist/cli/commands/runs.d.ts +0 -2
  74. package/dist/cli/commands/runs.d.ts.map +0 -1
  75. package/dist/cli/commands/runs.impl.d.ts +0 -4
  76. package/dist/cli/commands/runs.impl.d.ts.map +0 -1
  77. package/dist/cli/commands/runs.impl.js +0 -71
  78. package/dist/cli/commands/runs.js +0 -45
  79. package/dist/cli/commands/secrets.d.ts +0 -2
  80. package/dist/cli/commands/secrets.d.ts.map +0 -1
  81. package/dist/cli/commands/secrets.impl.d.ts +0 -5
  82. package/dist/cli/commands/secrets.impl.d.ts.map +0 -1
  83. package/dist/cli/commands/secrets.impl.js +0 -99
  84. package/dist/cli/commands/secrets.js +0 -64
  85. package/dist/cli/commands/utils/testing.d.ts +0 -13
  86. package/dist/cli/commands/utils/testing.d.ts.map +0 -1
  87. package/dist/cli/commands/utils/testing.js +0 -58
  88. package/dist/cli/config.d.ts +0 -63
  89. package/dist/cli/config.d.ts.map +0 -1
  90. package/dist/cli/config.js +0 -194
  91. package/dist/cli/config.test.d.ts +0 -2
  92. package/dist/cli/config.test.d.ts.map +0 -1
  93. package/dist/cli/context.d.ts +0 -15
  94. package/dist/cli/context.d.ts.map +0 -1
  95. package/dist/cli/context.js +0 -16
  96. package/dist/cli/deploy.d.ts +0 -37
  97. package/dist/cli/deploy.d.ts.map +0 -1
  98. package/dist/cli/deploy.js +0 -100
  99. package/dist/cli/flags.d.ts +0 -21
  100. package/dist/cli/flags.d.ts.map +0 -1
  101. package/dist/cli/flags.js +0 -49
  102. package/dist/cli/handler.d.ts +0 -14
  103. package/dist/cli/handler.d.ts.map +0 -1
  104. package/dist/cli/handler.js +0 -32
  105. package/dist/cli/io.d.ts +0 -55
  106. package/dist/cli/io.d.ts.map +0 -1
  107. package/dist/cli/io.js +0 -96
  108. package/dist/cli/routes.d.ts +0 -2
  109. package/dist/cli/routes.d.ts.map +0 -1
  110. package/dist/cli/routes.js +0 -62
  111. package/dist/cli/utils/array.d.ts +0 -2
  112. package/dist/cli/utils/array.d.ts.map +0 -1
  113. package/dist/cli/utils/array.js +0 -10
  114. package/dist/cli/utils/openUrl.d.ts +0 -4
  115. package/dist/cli/utils/openUrl.d.ts.map +0 -1
  116. package/dist/cli/utils/openUrl.js +0 -43
  117. package/dist/cli/utils/string.d.ts +0 -2
  118. package/dist/cli/utils/string.d.ts.map +0 -1
  119. package/dist/cli/utils/string.js +0 -12
  120. package/src/cli/api/client.ts +0 -628
  121. package/src/cli/api/result.ts +0 -71
  122. package/src/cli/bin/cli.ts +0 -7
  123. package/src/cli/commands/.cursor/rules/testing-commands.mdc +0 -212
  124. package/src/cli/commands/auth.impl.test.ts +0 -228
  125. package/src/cli/commands/auth.impl.ts +0 -56
  126. package/src/cli/commands/auth.ts +0 -63
  127. package/src/cli/commands/bundle.impl.test.ts +0 -143
  128. package/src/cli/commands/bundle.impl.ts +0 -21
  129. package/src/cli/commands/bundle.ts +0 -23
  130. package/src/cli/commands/capabilities.impl.ts +0 -47
  131. package/src/cli/commands/capabilities.ts +0 -25
  132. package/src/cli/commands/connect.impl.ts +0 -149
  133. package/src/cli/commands/connect.ts +0 -80
  134. package/src/cli/commands/deploy.impl.test.ts +0 -254
  135. package/src/cli/commands/deploy.impl.ts +0 -73
  136. package/src/cli/commands/deploy.ts +0 -22
  137. package/src/cli/commands/env.impl.ts +0 -88
  138. package/src/cli/commands/env.ts +0 -38
  139. package/src/cli/commands/exec.impl.ts +0 -171
  140. package/src/cli/commands/exec.ts +0 -32
  141. package/src/cli/commands/runs.impl.ts +0 -87
  142. package/src/cli/commands/runs.ts +0 -49
  143. package/src/cli/commands/secrets.impl.ts +0 -130
  144. package/src/cli/commands/secrets.ts +0 -73
  145. package/src/cli/commands/utils/testing.ts +0 -66
  146. package/src/cli/config.test.ts +0 -108
  147. package/src/cli/config.ts +0 -265
  148. package/src/cli/context.ts +0 -26
  149. package/src/cli/deploy.ts +0 -190
  150. package/src/cli/flags.ts +0 -72
  151. package/src/cli/handler.ts +0 -68
  152. package/src/cli/io.ts +0 -132
  153. package/src/cli/routes.ts +0 -61
  154. package/src/cli/utils/array.ts +0 -7
  155. package/src/cli/utils/openUrl.ts +0 -53
  156. package/src/cli/utils/string.ts +0 -9
@@ -1,212 +0,0 @@
1
- ---
2
- description: Testing patterns for CLI command implementations
3
- globs:
4
- - "**/*.impl.test.ts"
5
- alwaysApply: false
6
- ---
7
-
8
- # Testing CLI Commands
9
-
10
- ## Understanding buildHandler
11
-
12
- Commands are wrapped with `buildHandler` which:
13
- 1. Returns a function that accepts `(flags, ...args)`
14
- 2. Internally loads config using `Config.load()`
15
- 3. Requires a `this` context with `process`, `fs`, `os`, `path`, and `writer`
16
-
17
- ## Test Structure Pattern
18
-
19
- ```typescript
20
- describe("commandName", () => {
21
- let stderrSpy: Mock<typeof process.stderr.write>; // or stdoutSpy
22
-
23
- beforeEach(() => {
24
- stderrSpy = vi
25
- .spyOn(process.stderr, "write")
26
- .mockImplementation(() => true);
27
- });
28
-
29
- it("does something", async () => {
30
- // 1. Create a mock config with the desired state
31
- const mockConfig = await createTestConfig({
32
- token: "test-token",
33
- workerId: null,
34
- environment: "local",
35
- baseUrl: "http://localhost:3000",
36
- });
37
-
38
- // 2. Spy on Config.load to return your mock config
39
- vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
40
-
41
- // 3. Spy on config methods to verify they're called
42
- const setTokenSpy = vi.spyOn(mockConfig, "setToken");
43
-
44
- // 4. Create a base context
45
- const context = createBaseContext();
46
-
47
- // 5. Call the handler with .call(context, flags, ...args)
48
- await commandHandler.call(context, baseFlags, "arg1", "arg2");
49
-
50
- // 6. Assert on spies and output
51
- expect(setTokenSpy).toHaveBeenCalledWith("expected-value");
52
- expect(stderrSpy).toHaveBeenCalledWith(
53
- expect.stringContaining("Expected message"),
54
- );
55
- });
56
- });
57
- ```
58
-
59
- ## Key Testing Patterns
60
-
61
- ### 1. Mocking Config.load
62
-
63
- Always mock `Config.load` to return your test config. The handler loads config internally, so you can't pass it directly.
64
-
65
- ```typescript
66
- const mockConfig = await createTestConfig({
67
- token: null,
68
- workerId: null,
69
- environment: "local",
70
- baseUrl: "http://localhost:3000",
71
- });
72
-
73
- vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
74
- ```
75
-
76
- ### 2. Creating Context
77
-
78
- Handlers expect a context with specific properties:
79
-
80
- ```typescript
81
- function createBaseContext() {
82
- return {
83
- writer: new Writer({ debugEnabled: false }),
84
- process,
85
- fs,
86
- os,
87
- path,
88
- };
89
- }
90
- ```
91
-
92
- ### 3. Calling Handlers
93
-
94
- Use `.call()` to provide the context:
95
-
96
- ```typescript
97
- await handler.call(context, flags, ...args);
98
- ```
99
-
100
- **Don't** call the handler directly without context - it will fail because `this.process` will be undefined.
101
-
102
- ### 4. Verifying Config Updates
103
-
104
- Spy on config methods to verify they were called, rather than checking state directly:
105
-
106
- ```typescript
107
- const setTokenSpy = vi.spyOn(mockConfig, "setToken");
108
- await handler.call(context, flags);
109
- expect(setTokenSpy).toHaveBeenCalledWith(expectedValue);
110
- ```
111
-
112
- Config methods like `setToken()`, `setEnvironment()`, and `setWorkerId()` write to the file asynchronously, so checking the config state immediately won't work.
113
-
114
- ### 5. Mocking External Dependencies
115
-
116
- Mock external modules before importing the implementation:
117
-
118
- ```typescript
119
- vi.mock("../utils/openUrl.js", () => ({
120
- openNotionUrl: vi.fn().mockResolvedValue(undefined),
121
- }));
122
-
123
- import { commandHandler } from "./command.impl.js";
124
- import { openNotionUrl } from "../utils/openUrl.js";
125
-
126
- // In your test:
127
- expect(openNotionUrl).toHaveBeenCalledWith("local", "http://...");
128
-
129
- // For one-time failures:
130
- vi.mocked(openNotionUrl).mockRejectedValueOnce(new Error("Failed"));
131
- ```
132
-
133
- ### 6. Capturing stdout/stderr
134
-
135
- When testing output, spy on the write methods and check the content:
136
-
137
- ```typescript
138
- let stderrSpy: Mock<typeof process.stderr.write>;
139
-
140
- beforeEach(() => {
141
- stderrSpy = vi
142
- .spyOn(process.stderr, "write")
143
- .mockImplementation(() => true);
144
- });
145
-
146
- // For single messages:
147
- expect(stderrSpy).toHaveBeenCalledWith(
148
- expect.stringContaining("Expected message"),
149
- );
150
-
151
- // For multiple messages (concatenate all calls):
152
- const allCalls = stderrSpy.mock.calls.map((call) => call[0]).join("");
153
- expect(allCalls).toContain("Expected message");
154
- ```
155
-
156
- ## Helper Functions
157
-
158
- Place helper functions at the bottom of test files:
159
-
160
- ```typescript
161
- // Test helpers
162
-
163
- type ConfigFileContents = {
164
- token: string | null;
165
- workerId: string | null;
166
- environment: Environment;
167
- baseUrl: string;
168
- };
169
-
170
- const tmpDirectories: string[] = [];
171
-
172
- const baseFlags: GlobalFlags = {
173
- debug: false,
174
- };
175
-
176
- async function createConfigFile(contents: ConfigFileContents) {
177
- const dir = await mkdtemp(path.join(tmpdir(), "test-prefix-"));
178
- tmpDirectories.push(dir);
179
- const configFilePath = path.join(dir, "config.json");
180
- await writeFile(configFilePath, JSON.stringify(contents, null, 2), "utf-8");
181
- return configFilePath;
182
- }
183
-
184
- async function createTestConfig(
185
- configFileContents: ConfigFileContents,
186
- ): Promise<Config> {
187
- const configFilePath = await createConfigFile(configFileContents);
188
- return Config.load({
189
- configFilePath,
190
- processEnv: {} as NodeJS.ProcessEnv,
191
- flags: { debug: false },
192
- });
193
- }
194
-
195
- function createBaseContext() {
196
- return {
197
- writer: new Writer({ debugEnabled: false }),
198
- process,
199
- fs,
200
- os,
201
- path,
202
- };
203
- }
204
- ```
205
-
206
- ## Common Mistakes to Avoid
207
-
208
- 1. **Don't call handlers directly without context**: Always use `.call(context, ...)`
209
- 2. **Don't check config state directly**: Spy on the setter methods instead
210
- 3. **Don't forget to mock Config.load**: The handler loads config internally
211
- 4. **Don't forget to create unique temp directories**: Use `mkdtemp` with unique prefixes
212
- 5. **Don't forget to clean up temp files**: Use `afterEach` to remove temp directories
@@ -1,228 +0,0 @@
1
- import {
2
- afterEach,
3
- beforeEach,
4
- describe,
5
- expect,
6
- it,
7
- type Mock,
8
- vi,
9
- } from "vitest";
10
- import { Config } from "../config.js";
11
- import {
12
- baseFlags,
13
- cleanupTmpDirectories,
14
- createAndLoadConfig,
15
- createBaseContext,
16
- } from "./utils/testing.js";
17
-
18
- // Mock the openUrl module before importing the implementation
19
- vi.mock("../utils/openUrl.js", () => ({
20
- openNotionUrl: vi.fn().mockResolvedValue(undefined),
21
- }));
22
-
23
- import { openNotionUrl } from "../utils/openUrl.js";
24
- import { login, logout, show } from "./auth.impl.js";
25
-
26
- afterEach(cleanupTmpDirectories);
27
-
28
- describe("login", () => {
29
- let stderrSpy: Mock<typeof process.stderr.write>;
30
-
31
- beforeEach(() => {
32
- stderrSpy = vi
33
- .spyOn(process.stderr, "write")
34
- .mockImplementation(() => true);
35
- });
36
-
37
- it("opens browser and displays instructions when no token provided", async () => {
38
- const [mockConfig] = await createAndLoadConfig({
39
- configFile: {
40
- token: null,
41
- workerId: null,
42
- environment: "local",
43
- baseUrl: "http://localhost:3000",
44
- },
45
- });
46
-
47
- vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
48
-
49
- const context = createBaseContext();
50
-
51
- await login.call(context, baseFlags);
52
-
53
- expect(openNotionUrl).toHaveBeenCalledWith(
54
- "local",
55
- "http://localhost:3000/__workers__?createToken=true",
56
- );
57
-
58
- const allCalls = stderrSpy.mock.calls.map((call) => call[0]).join("");
59
- expect(allCalls).toContain("Opening browser to create a token...");
60
- expect(allCalls).toContain(
61
- "http://localhost:3000/__workers__?createToken=true",
62
- );
63
- expect(allCalls).toContain("After creating a token, run:");
64
- expect(allCalls).toContain("npx workers auth login <token>");
65
- });
66
-
67
- it("shows error message when browser fails to open", async () => {
68
- const [mockConfig] = await createAndLoadConfig({
69
- configFile: {
70
- token: null,
71
- workerId: null,
72
- environment: "local",
73
- baseUrl: "http://localhost:3000",
74
- },
75
- });
76
-
77
- vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
78
- vi.mocked(openNotionUrl).mockRejectedValueOnce(new Error("Failed to open"));
79
-
80
- const context = createBaseContext();
81
-
82
- await login.call(context, baseFlags);
83
-
84
- const allCalls = stderrSpy.mock.calls.map((call) => call[0]).join("");
85
- expect(allCalls).toContain("Failed to open browser automatically");
86
- expect(allCalls).toContain(
87
- "http://localhost:3000/__workers__?createToken=true",
88
- );
89
- });
90
-
91
- it("saves token and clears workerId when token is provided", async () => {
92
- const [mockConfig] = await createAndLoadConfig({
93
- configFile: {
94
- token: null,
95
- workerId: null,
96
- environment: "local",
97
- baseUrl: "http://localhost:3000",
98
- },
99
- });
100
-
101
- // Spy on Config.load to return our mock config
102
- vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
103
-
104
- const testToken =
105
- "1.2.eyJzcGFjZUlkIjoic3BhY2UxIiwidXNlcklkIjoidXNlcjEiLCJjZWxsSWQiOiJjZWxsMSJ9.sig";
106
-
107
- const updateSpy = vi.spyOn(mockConfig, "update");
108
-
109
- const context = createBaseContext();
110
-
111
- await login.call(context, baseFlags, testToken);
112
-
113
- expect(updateSpy).toHaveBeenCalledWith({
114
- environment: "local",
115
- token: testToken,
116
- workerId: null,
117
- });
118
- expect(stderrSpy).toHaveBeenCalledWith(
119
- expect.stringContaining("Successfully logged in!"),
120
- );
121
- });
122
-
123
- it("saves environment from config when logging in", async () => {
124
- const [mockConfig] = await createAndLoadConfig({
125
- configFile: {
126
- token: null,
127
- workerId: null,
128
- baseUrl: "https://www.notion.so",
129
- },
130
- flags: {
131
- env: "local",
132
- },
133
- });
134
-
135
- vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
136
-
137
- const testToken =
138
- "1.2.eyJzcGFjZUlkIjoic3BhY2UxIiwidXNlcklkIjoidXNlcjEiLCJjZWxsSWQiOiJjZWxsMSJ9.sig";
139
-
140
- const updateSpy = vi.spyOn(mockConfig, "update");
141
-
142
- const context = createBaseContext();
143
-
144
- await login.call(context, baseFlags, testToken);
145
-
146
- expect(updateSpy).toHaveBeenCalledWith({
147
- token: testToken,
148
- workerId: null,
149
- environment: "local",
150
- });
151
- });
152
- });
153
-
154
- describe("show", () => {
155
- let stdoutSpy: Mock<typeof process.stdout.write>;
156
-
157
- beforeEach(() => {
158
- stdoutSpy = vi
159
- .spyOn(process.stdout, "write")
160
- .mockImplementation(() => true);
161
- });
162
-
163
- it("writes token to stdout when token exists", async () => {
164
- const testToken =
165
- "1.2.eyJzcGFjZUlkIjoic3BhY2UxIiwidXNlcklkIjoidXNlcjEiLCJjZWxsSWQiOiJjZWxsMSJ9.sig";
166
-
167
- const [mockConfig] = await createAndLoadConfig({
168
- configFile: {
169
- token: testToken,
170
- workerId: "worker-1",
171
- environment: "local",
172
- baseUrl: "http://localhost:3000",
173
- },
174
- });
175
-
176
- vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
177
-
178
- const context = createBaseContext();
179
-
180
- await show.call(context, baseFlags);
181
-
182
- expect(stdoutSpy).toHaveBeenCalledWith(`${testToken}\n`);
183
- });
184
-
185
- it("writes empty string to stdout when no token exists", async () => {
186
- const [mockConfig] = await createAndLoadConfig({
187
- configFile: {
188
- token: null,
189
- workerId: null,
190
- environment: "local",
191
- baseUrl: "http://localhost:3000",
192
- },
193
- });
194
-
195
- vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
196
-
197
- const context = createBaseContext();
198
-
199
- await show.call(context, baseFlags);
200
-
201
- expect(stdoutSpy).toHaveBeenCalledWith("\n");
202
- });
203
- });
204
-
205
- describe("logout", () => {
206
- it("calls setToken with null", async () => {
207
- const [mockConfig] = await createAndLoadConfig({
208
- configFile: {
209
- token: "existing-token",
210
- workerId: "worker-1",
211
- environment: "local",
212
- baseUrl: "http://localhost:3000",
213
- },
214
- });
215
-
216
- vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
217
-
218
- const updateSpy = vi.spyOn(mockConfig, "update");
219
-
220
- const context = createBaseContext();
221
-
222
- await logout.call(context, baseFlags);
223
-
224
- expect(updateSpy).toHaveBeenCalledWith({
225
- token: null,
226
- });
227
- });
228
- });
@@ -1,56 +0,0 @@
1
- import type { ConfigMap } from "../config.js";
2
- import type { GlobalFlags } from "../flags.js";
3
- import { buildHandler, type HandlerContext } from "../handler.js";
4
- import { openNotionUrl } from "../utils/openUrl.js";
5
-
6
- export const login = buildHandler(async function (
7
- this: HandlerContext,
8
- _: GlobalFlags,
9
- token?: string,
10
- ) {
11
- const environment = this.config.environment;
12
-
13
- // If no token provided, open the browser to get one
14
- if (!token) {
15
- const url = `${this.config.baseUrl}/__workers__?createToken=true`;
16
-
17
- this.io.writeErr(`Opening browser to create a token...\n${url}\n`);
18
- this.io.writeErr("After creating a token, run:");
19
- this.io.writeErr("\tnpx workers auth login <token>");
20
-
21
- try {
22
- await openNotionUrl(environment, url);
23
- } catch (_error) {
24
- this.io.writeErr(
25
- `Failed to open browser automatically. Please visit:\n ${url}`,
26
- );
27
- }
28
-
29
- return;
30
- }
31
-
32
- const update: Partial<ConfigMap> = {
33
- token,
34
- workerId: null,
35
- };
36
-
37
- console.log("environment", environment);
38
-
39
- if (environment !== "prod") {
40
- update.environment = environment;
41
- }
42
-
43
- await this.config.update(update);
44
-
45
- this.io.writeErr("Successfully logged in!");
46
- });
47
-
48
- export const show = buildHandler(function (this: HandlerContext) {
49
- this.io.writeOut(`${this.config.token ?? ""}`);
50
- });
51
-
52
- export const logout = buildHandler(async function (this: HandlerContext) {
53
- await this.config.update({
54
- token: null,
55
- });
56
- });
@@ -1,63 +0,0 @@
1
- import { buildCommand, buildRouteMap } from "@stricli/core";
2
- import { globalFlags } from "../flags.js";
3
-
4
- export const authCommands = buildRouteMap({
5
- docs: {
6
- brief: "Commands for managing the CLI context",
7
- },
8
- routes: {
9
- login: buildCommand({
10
- docs: {
11
- brief:
12
- "Login to the Project Ajax platform using a Workers API token; will clear existing token, environment, and worker ID",
13
- },
14
-
15
- parameters: {
16
- positional: {
17
- kind: "tuple",
18
- parameters: [
19
- {
20
- brief: "A Workers API token",
21
- parse: String,
22
- placeholder: "api-token",
23
- optional: true,
24
- },
25
- ],
26
- },
27
-
28
- flags: {
29
- ...globalFlags,
30
- },
31
- },
32
-
33
- loader: () => import("./auth.impl.js").then((m) => m.login),
34
- }),
35
-
36
- show: buildCommand({
37
- docs: {
38
- brief: "Get the token for the CLI",
39
- },
40
- parameters: {
41
- flags: {
42
- ...globalFlags,
43
- },
44
- },
45
-
46
- loader: () => import("./auth.impl.js").then((m) => m.show),
47
- }),
48
-
49
- logout: buildCommand({
50
- docs: {
51
- brief: "Unset the token for the CLI",
52
- },
53
-
54
- parameters: {
55
- flags: {
56
- ...globalFlags,
57
- },
58
- },
59
-
60
- loader: () => import("./auth.impl.js").then((m) => m.logout),
61
- }),
62
- },
63
- });
@@ -1,143 +0,0 @@
1
- import {
2
- afterEach,
3
- beforeEach,
4
- describe,
5
- expect,
6
- it,
7
- type Mock,
8
- vi,
9
- } from "vitest";
10
- import { ApiClient } from "../api/client.js";
11
- import { Result } from "../api/result.js";
12
- import { Config } from "../config.js";
13
- import { downloadBundle } from "./bundle.impl.js";
14
- import {
15
- baseFlags,
16
- cleanupTmpDirectories,
17
- createAndLoadConfig,
18
- createBaseContext,
19
- } from "./utils/testing.js";
20
-
21
- afterEach(cleanupTmpDirectories);
22
-
23
- describe("downloadBundle", () => {
24
- let stderrSpy: Mock<typeof process.stderr.write>;
25
- let stdoutSpy: Mock<typeof process.stdout.write>;
26
-
27
- beforeEach(() => {
28
- stderrSpy = vi
29
- .spyOn(process.stderr, "write")
30
- .mockImplementation(() => true);
31
- stdoutSpy = vi
32
- .spyOn(process.stdout, "write")
33
- .mockImplementation(() => true);
34
- });
35
-
36
- it("downloads and pipes bundle to stdout when workerId is set", async () => {
37
- const [mockConfig] = await createAndLoadConfig({
38
- configFile: {
39
- token:
40
- "1.2.eyJzcGFjZUlkIjoic3BhY2UxIiwidXNlcklkIjoidXNlcjEiLCJjZWxsSWQiOiJjZWxsMSJ9.sig",
41
- workerId: "worker-123",
42
- environment: "local",
43
- baseUrl: "http://localhost:3000",
44
- },
45
- });
46
-
47
- vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
48
-
49
- // Create a mock web stream
50
- const mockWebStream = new ReadableStream({
51
- start(controller) {
52
- controller.enqueue(new TextEncoder().encode("bundle content"));
53
- controller.close();
54
- },
55
- });
56
-
57
- // This allows us to wait for the pipe to happen.
58
- let resolveWrite: () => void;
59
- const writePromise = new Promise<void>((resolve) => {
60
- resolveWrite = resolve;
61
- });
62
- stdoutSpy = vi.spyOn(process.stdout, "write").mockImplementation(() => {
63
- resolveWrite();
64
- return true;
65
- });
66
-
67
- const mockDownloadWorkerBundle = vi
68
- .spyOn(ApiClient.prototype, "downloadWorkerBundle")
69
- .mockResolvedValue(Result.success(mockWebStream));
70
-
71
- const context = createBaseContext();
72
-
73
- await downloadBundle.call(context, baseFlags);
74
-
75
- expect(mockDownloadWorkerBundle).toHaveBeenCalledWith("worker-123");
76
- await writePromise;
77
- expect(stdoutSpy).toHaveBeenCalledWith(Buffer.from("bundle content"));
78
- });
79
-
80
- it("throws error when workerId is not set", async () => {
81
- const [mockConfig] = await createAndLoadConfig({
82
- configFile: {
83
- token:
84
- "1.2.eyJzcGFjZUlkIjoic3BhY2UxIiwidXNlcklkIjoidXNlcjEiLCJjZWxsSWQiOiJjZWxsMSJ9.sig",
85
- workerId: null,
86
- environment: "local",
87
- baseUrl: "http://localhost:3000",
88
- },
89
- });
90
-
91
- vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
92
-
93
- const mockDownloadWorkerBundle = vi.spyOn(
94
- ApiClient.prototype,
95
- "downloadWorkerBundle",
96
- );
97
-
98
- const context = createBaseContext();
99
-
100
- await expect(downloadBundle.call(context, baseFlags)).rejects.toThrow(
101
- "No worker configured. Run 'workers deploy' first to create a worker.",
102
- );
103
-
104
- expect(mockDownloadWorkerBundle).not.toHaveBeenCalled();
105
- });
106
-
107
- it("handles download failure gracefully", async () => {
108
- const [mockConfig] = await createAndLoadConfig({
109
- configFile: {
110
- token:
111
- "1.2.eyJzcGFjZUlkIjoic3BhY2UxIiwidXNlcklkIjoidXNlcjEiLCJjZWxsSWQiOiJjZWxsMSJ9.sig",
112
- workerId: "worker-123",
113
- environment: "local",
114
- baseUrl: "http://localhost:3000",
115
- },
116
- });
117
-
118
- vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
119
-
120
- const mockDownloadWorkerBundle = vi
121
- .spyOn(ApiClient.prototype, "downloadWorkerBundle")
122
- .mockResolvedValue(
123
- Result.fail({
124
- status: 404,
125
- statusText: "Not Found",
126
- message: "Worker bundle not found",
127
- }),
128
- );
129
-
130
- const context = createBaseContext();
131
-
132
- await downloadBundle.call(context, baseFlags);
133
-
134
- expect(mockDownloadWorkerBundle).toHaveBeenCalledWith("worker-123");
135
-
136
- const allCalls = stderrSpy.mock.calls.map((call) => call[0]).join("");
137
- expect(allCalls).toContain("✗ Failed to download bundle");
138
- expect(allCalls).toContain("✗ Worker bundle not found");
139
-
140
- // Should not write anything to stdout on error
141
- expect(stdoutSpy).not.toHaveBeenCalled();
142
- });
143
- });