@project-ajax/cli 0.0.17 → 0.0.18

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 (48) hide show
  1. package/dist/commands/auth.d.ts.map +1 -1
  2. package/dist/commands/auth.impl.d.ts +8 -2
  3. package/dist/commands/auth.impl.d.ts.map +1 -1
  4. package/dist/commands/auth.impl.js +31 -5
  5. package/dist/commands/auth.js +7 -2
  6. package/dist/commands/capabilities.impl.d.ts +1 -1
  7. package/dist/commands/capabilities.impl.d.ts.map +1 -1
  8. package/dist/commands/capabilities.impl.js +7 -1
  9. package/dist/commands/delete.d.ts +3 -0
  10. package/dist/commands/delete.d.ts.map +1 -0
  11. package/dist/commands/delete.impl.d.ts +7 -0
  12. package/dist/commands/delete.impl.d.ts.map +1 -0
  13. package/dist/commands/delete.impl.js +26 -0
  14. package/dist/commands/delete.impl.test.d.ts +2 -0
  15. package/dist/commands/delete.impl.test.d.ts.map +1 -0
  16. package/dist/commands/delete.js +31 -0
  17. package/dist/commands/deploy.js +1 -1
  18. package/dist/commands/list.d.ts +3 -0
  19. package/dist/commands/list.d.ts.map +1 -0
  20. package/dist/commands/list.impl.d.ts +3 -0
  21. package/dist/commands/list.impl.d.ts.map +1 -0
  22. package/dist/commands/list.impl.js +41 -0
  23. package/dist/commands/list.impl.test.d.ts +2 -0
  24. package/dist/commands/list.impl.test.d.ts.map +1 -0
  25. package/dist/commands/list.js +17 -0
  26. package/dist/commands/oauth.impl.d.ts +1 -1
  27. package/dist/commands/oauth.impl.d.ts.map +1 -1
  28. package/dist/commands/oauth.impl.js +11 -3
  29. package/dist/commands/runs.impl.d.ts +1 -1
  30. package/dist/commands/runs.impl.d.ts.map +1 -1
  31. package/dist/commands/runs.impl.js +7 -1
  32. package/dist/commands/secrets.impl.d.ts +1 -1
  33. package/dist/commands/secrets.impl.d.ts.map +1 -1
  34. package/dist/commands/secrets.impl.js +7 -1
  35. package/dist/commands/utils/testing.d.ts +4 -2
  36. package/dist/commands/utils/testing.d.ts.map +1 -1
  37. package/dist/commands/utils/testing.js +23 -10
  38. package/dist/config.d.ts +121 -7
  39. package/dist/config.d.ts.map +1 -1
  40. package/dist/config.js +194 -37
  41. package/dist/flags.d.ts +5 -0
  42. package/dist/flags.d.ts.map +1 -1
  43. package/dist/flags.js +34 -1
  44. package/dist/handler.d.ts.map +1 -1
  45. package/dist/handler.js +9 -3
  46. package/dist/routes.d.ts.map +1 -1
  47. package/dist/routes.js +39 -5
  48. package/package.json +2 -2
package/dist/config.d.ts CHANGED
@@ -20,22 +20,76 @@ declare const LocalConfig: z.ZodObject<{
20
20
  workerId: z.ZodNullable<z.ZodString>;
21
21
  }, z.z.core.$strip>;
22
22
  export type LocalConfig = z.infer<typeof LocalConfig>;
23
+ declare const GlobalConfig: z.ZodObject<{
24
+ version: z.ZodLiteral<"1">;
25
+ defaultSpaceId: z.ZodNullable<z.ZodString>;
26
+ }, z.z.core.$strip>;
27
+ export type GlobalConfig = z.infer<typeof GlobalConfig>;
23
28
  export declare class NoSuitableConfigFileError extends Error {
24
29
  constructor();
25
30
  }
26
31
  /**
27
- * Manages configuration for the command line interface.
32
+ * Manages configuration for the Workers CLI.
33
+ *
34
+ * ## Configuration Sources
35
+ *
36
+ * The CLI uses three configuration files:
37
+ *
38
+ * 1. **Global user config** (`config.json`) - Stores user preferences like
39
+ * `defaultSpaceId`. Located in `$NOTION_HOME/`, `$XDG_CONFIG_HOME/notion/`,
40
+ * `$HOME/.config/notion/`, or `$HOME/.notion/`. This file is always created
41
+ * when the CLI runs.
42
+ *
43
+ * 2. **Global space cache** (`spaces.json`) - Stores authenticated spaces and
44
+ * their metadata. Located in the same directory as `config.json`.
45
+ *
46
+ * 3. **Local project config** (`workers.json`) - Stores project-specific settings
47
+ * like which space and worker the project is associated with. Located in the
48
+ * current working directory.
49
+ *
50
+ * ## Configuration Precedence
51
+ *
52
+ * Values are resolved in this order (highest to lowest priority):
53
+ * - Environment variables (WORKERS_ENVIRONMENT, WORKERS_BASE_URL, WORKERS_TOKEN)
54
+ * - Command-line flags (--env, --base-url, --token, --space)
55
+ * - Local config file (workers.json)
56
+ * - Global user config (config.json) for `defaultSpaceId`
57
+ * - Defaults
58
+ *
59
+ * Space ID specifically follows: --space > WORKERS_SPACE_ID > workers.json > config.json
28
60
  *
29
- * Order of precedence: Command-line flags, environment variables, local config
30
- * files, where appropriate.
61
+ * ## Lazy workers.json Creation
62
+ *
63
+ * The `workers.json` file is only written when the CLI detects it's running in
64
+ * a worker project directory. A directory is considered a worker project if it
65
+ * contains a `package.json` with any `@project-ajax/*` package in dependencies
66
+ * or devDependencies.
67
+ *
68
+ * This design allows users to run commands like `workers auth login` from their
69
+ * home directory without creating an unwanted `workers.json` file there. The
70
+ * authentication still succeeds—the token is stored in the system keychain and
71
+ * the space is cached in `spaces.json`—but no local config file is created.
72
+ *
73
+ * When running in a worker project directory, operations like `login()`,
74
+ * `switchToSpace()`, and `setWorker()` will create or update `workers.json`
75
+ * to persist the project's space and worker associations.
76
+ *
77
+ * ## Token Storage
78
+ *
79
+ * Tokens are stored in the system keychain (via the `cross-keychain` package),
80
+ * keyed by space ID. This keeps sensitive credentials out of config files and
81
+ * allows multiple spaces to be authenticated simultaneously.
31
82
  */
32
83
  export declare class Config {
33
84
  #private;
34
85
  constructor(opts: {
86
+ globalConfigPath: string;
87
+ globalConfig: GlobalConfig;
35
88
  spaceCachePath: string;
36
89
  spaceCache: SpaceCache;
37
90
  localConfigPath: string;
38
91
  localConfig: LocalConfig;
92
+ isInWorkerDirectory: boolean;
39
93
  environment: Environment;
40
94
  baseURL: string;
41
95
  spaceId: string | null;
@@ -46,6 +100,24 @@ export declare class Config {
46
100
  get baseURL(): string;
47
101
  get spaceId(): string | null;
48
102
  get workerId(): string | null;
103
+ /**
104
+ * Get the default space ID from global user config.
105
+ *
106
+ * This is the space that will be used when no local config exists and
107
+ * no --space flag is provided.
108
+ *
109
+ * @returns The default space ID, or null if not set.
110
+ */
111
+ get defaultSpaceId(): string | null;
112
+ /**
113
+ * Set the default space ID in global user config.
114
+ *
115
+ * This updates the `config.json` file to persist the default space
116
+ * preference across CLI invocations.
117
+ *
118
+ * @param spaceId The space ID to set as default, or null to clear.
119
+ */
120
+ setDefaultSpaceId(spaceId: string | null): void;
49
121
  /**
50
122
  * Get the list of cached spaces.
51
123
  *
@@ -104,31 +176,66 @@ export declare class Config {
104
176
  * @throws Error if the space is not in the cache.
105
177
  */
106
178
  switchToSpace(spaceId: string): void;
179
+ /**
180
+ * Resolve the global config directory path.
181
+ *
182
+ * This is a shared helper used by both `resolveGlobalConfigPath` and
183
+ * `resolveSpaceCachePath` to determine the directory for global config files.
184
+ *
185
+ * Priority order:
186
+ * 1. `$NOTION_HOME` (if set and is a directory)
187
+ * 2. `$XDG_CONFIG_HOME/notion` (if XDG_CONFIG_HOME is set)
188
+ * 3. `$HOME/.config/notion` (if $HOME/.config exists)
189
+ * 4. `$HOME/.notion` (fallback)
190
+ *
191
+ * @param env The environment variables.
192
+ * @returns The directory path, or undefined if no suitable directory found.
193
+ */
194
+ static resolveGlobalDir(env: NodeJS.ProcessEnv): string | undefined;
195
+ /**
196
+ * Resolve the global config path.
197
+ *
198
+ * The global config file (`config.json`) stores user preferences like
199
+ * `defaultSpaceId`. It uses the same directory resolution as `spaces.json`.
200
+ *
201
+ * @param opts Options for resolving the global config path.
202
+ * @param opts.env The environment variables.
203
+ * @param opts.create Whether to create the file if it doesn't exist.
204
+ * @returns The path to the global config file.
205
+ */
206
+ static resolveGlobalConfigPath(opts: {
207
+ env: NodeJS.ProcessEnv;
208
+ create?: boolean;
209
+ }): string;
107
210
  /**
108
211
  * Resolve the space cache path.
109
212
  *
110
213
  * @param opts Options for resolving the space cache path.
111
214
  * @param opts.filePath The path to the space cache file.
112
215
  * @param opts.env The environment variables.
216
+ * @param opts.create Whether to create the file if it doesn't exist.
113
217
  * @returns The path to the space cache file.
114
218
  */
115
219
  static resolveSpaceCachePath(opts: {
116
220
  filePath?: string;
117
221
  env: NodeJS.ProcessEnv;
222
+ create?: boolean;
118
223
  }): string;
119
224
  /**
120
225
  * Resolve the local config path.
121
226
  *
122
227
  * @param opts Options for resolving the local config path.
123
228
  * @param opts.filePath The path to the local config file.
124
- * @returns
229
+ * @param opts.create Whether to create the file if it doesn't exist.
230
+ * @returns The path to the local config file.
125
231
  */
126
232
  static resolveLocalConfigPath(opts?: {
127
233
  filePath?: string;
234
+ create?: boolean;
128
235
  }): string;
129
236
  /**
130
- * Load config from space cache and local config files, then resolve them to
131
- * a final config for this execution of the command line interface.
237
+ * Load config from global config, space cache, and local config files, then
238
+ * resolve them to a final config for this execution of the command line interface.
132
239
  *
133
240
  * If the local config is v0, it will be upgraded to v1.
134
241
  *
@@ -137,16 +244,19 @@ export declare class Config {
137
244
  * - Environment: WORKERS_ENVIRONMENT or --env flag
138
245
  * - Token: WORKERS_TOKEN or --token flag
139
246
  * - Base URL: WORKERS_BASE_URL or --base-url flag
247
+ * - Space: --space flag > WORKERS_SPACE_ID > workers.json > config.json defaultSpaceId
140
248
  *
141
249
  * @param opts The options to load the config from.
250
+ * @param opts.globalConfigPath The path to the global config file.
142
251
  * @param opts.spaceCachePath The path to the space cache file.
143
252
  * @param opts.localPath The path to the local config file.
144
253
  * @param opts.env The process environment.
145
- * @param opts.processEnv The process environment.
146
254
  * @param opts.flags The global flags.
255
+ * @param opts.spaceOverride Optional space ID override (from --space flag).
147
256
  * @returns The resolved config.
148
257
  */
149
258
  static load(opts: {
259
+ globalConfigPath: string;
150
260
  spaceCachePath: string;
151
261
  localPath: string;
152
262
  env: NodeJS.ProcessEnv;
@@ -160,6 +270,10 @@ export declare class Config {
160
270
  * The empty space cache.
161
271
  */
162
272
  static get emptySpaceCache(): SpaceCache;
273
+ /**
274
+ * The empty global config.
275
+ */
276
+ static get emptyGlobalConfig(): GlobalConfig;
163
277
  }
164
278
  export {};
165
279
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/config.js CHANGED
@@ -30,6 +30,10 @@ const LocalConfigV0 = z.object({
30
30
  token: z.string().nullable(),
31
31
  workerId: z.string().nullable()
32
32
  }).partial();
33
+ const GlobalConfig = z.object({
34
+ version: z.literal("1"),
35
+ defaultSpaceId: z.string().nullable()
36
+ });
33
37
  class NoSuitableConfigFileError extends Error {
34
38
  constructor() {
35
39
  super("No suitable config file found");
@@ -37,20 +41,26 @@ class NoSuitableConfigFileError extends Error {
37
41
  }
38
42
  }
39
43
  class Config {
44
+ #globalConfigPath;
45
+ #globalConfig;
40
46
  #spaceCachePath;
41
47
  #spaceCache;
42
48
  #localConfigPath;
43
49
  #localConfig;
50
+ #isInWorkerDirectory;
44
51
  #baseURL;
45
52
  #environment;
46
53
  #spaceId;
47
54
  #workerId;
48
55
  #token = null;
49
56
  constructor(opts) {
57
+ this.#globalConfigPath = opts.globalConfigPath;
58
+ this.#globalConfig = opts.globalConfig;
50
59
  this.#spaceCachePath = opts.spaceCachePath;
51
60
  this.#spaceCache = opts.spaceCache;
52
61
  this.#localConfigPath = opts.localConfigPath;
53
62
  this.#localConfig = opts.localConfig;
63
+ this.#isInWorkerDirectory = opts.isInWorkerDirectory;
54
64
  this.#baseURL = opts.baseURL;
55
65
  this.#spaceId = opts.spaceId;
56
66
  this.#workerId = opts.workerId;
@@ -69,6 +79,29 @@ class Config {
69
79
  get workerId() {
70
80
  return this.#workerId;
71
81
  }
82
+ /**
83
+ * Get the default space ID from global user config.
84
+ *
85
+ * This is the space that will be used when no local config exists and
86
+ * no --space flag is provided.
87
+ *
88
+ * @returns The default space ID, or null if not set.
89
+ */
90
+ get defaultSpaceId() {
91
+ return this.#globalConfig.defaultSpaceId;
92
+ }
93
+ /**
94
+ * Set the default space ID in global user config.
95
+ *
96
+ * This updates the `config.json` file to persist the default space
97
+ * preference across CLI invocations.
98
+ *
99
+ * @param spaceId The space ID to set as default, or null to clear.
100
+ */
101
+ setDefaultSpaceId(spaceId) {
102
+ this.#globalConfig.defaultSpaceId = spaceId;
103
+ this.#writeGlobalConfig();
104
+ }
72
105
  /**
73
106
  * Get the list of cached spaces.
74
107
  *
@@ -163,31 +196,123 @@ class Config {
163
196
  this.#localConfig.spaceId = spaceId;
164
197
  this.#writeLocalConfig();
165
198
  }
199
+ /**
200
+ * Write the space cache to disk.
201
+ *
202
+ * The space cache is always written, regardless of the current directory,
203
+ * since it's a global user-level file.
204
+ */
166
205
  #writeSpaceCache() {
167
206
  fs.writeFileSync(
168
207
  this.#spaceCachePath,
169
208
  JSON.stringify(this.#spaceCache, null, 2)
170
209
  );
171
210
  }
211
+ /**
212
+ * Write the local config to disk.
213
+ *
214
+ * This is a no-op if the current directory is not a worker project (i.e.,
215
+ * does not contain a package.json with @project-ajax/* dependencies). This
216
+ * prevents creating workers.json files in unintended locations like the
217
+ * user's home directory when running `workers auth login`.
218
+ *
219
+ * @see isWorkerDirectory for the detection logic
220
+ */
172
221
  #writeLocalConfig() {
222
+ if (!this.#isInWorkerDirectory) {
223
+ return;
224
+ }
173
225
  fs.writeFileSync(
174
226
  this.#localConfigPath,
175
227
  JSON.stringify(this.#localConfig, null, 2)
176
228
  );
177
229
  }
230
+ /**
231
+ * Write the global config to disk.
232
+ *
233
+ * The global config is always written since it's a user-level file.
234
+ */
235
+ #writeGlobalConfig() {
236
+ fs.writeFileSync(
237
+ this.#globalConfigPath,
238
+ JSON.stringify(this.#globalConfig, null, 2)
239
+ );
240
+ }
241
+ /**
242
+ * Resolve the global config directory path.
243
+ *
244
+ * This is a shared helper used by both `resolveGlobalConfigPath` and
245
+ * `resolveSpaceCachePath` to determine the directory for global config files.
246
+ *
247
+ * Priority order:
248
+ * 1. `$NOTION_HOME` (if set and is a directory)
249
+ * 2. `$XDG_CONFIG_HOME/notion` (if XDG_CONFIG_HOME is set)
250
+ * 3. `$HOME/.config/notion` (if $HOME/.config exists)
251
+ * 4. `$HOME/.notion` (fallback)
252
+ *
253
+ * @param env The environment variables.
254
+ * @returns The directory path, or undefined if no suitable directory found.
255
+ */
256
+ static resolveGlobalDir(env) {
257
+ const home = env.HOME;
258
+ const xdgConfigHome = env.XDG_CONFIG_HOME;
259
+ const notionHome = env.NOTION_HOME;
260
+ if (notionHome && isDir(notionHome)) {
261
+ return notionHome;
262
+ } else if (xdgConfigHome && isDir(xdgConfigHome)) {
263
+ return path.join(xdgConfigHome, "notion");
264
+ } else if (home && isDir(path.join(home, ".config"))) {
265
+ return path.join(home, ".config", "notion");
266
+ } else if (home && isDir(home)) {
267
+ return path.join(home, ".notion");
268
+ }
269
+ return void 0;
270
+ }
271
+ /**
272
+ * Resolve the global config path.
273
+ *
274
+ * The global config file (`config.json`) stores user preferences like
275
+ * `defaultSpaceId`. It uses the same directory resolution as `spaces.json`.
276
+ *
277
+ * @param opts Options for resolving the global config path.
278
+ * @param opts.env The environment variables.
279
+ * @param opts.create Whether to create the file if it doesn't exist.
280
+ * @returns The path to the global config file.
281
+ */
282
+ static resolveGlobalConfigPath(opts) {
283
+ const configDir = Config.resolveGlobalDir(opts.env);
284
+ if (!configDir) {
285
+ throw new NoSuitableConfigFileError();
286
+ }
287
+ const configFilePath = path.join(configDir, "config.json");
288
+ debug("%o", { configDir, configFilePath });
289
+ if (opts.create && !fs.existsSync(configDir)) {
290
+ fs.mkdirSync(configDir, { recursive: true });
291
+ }
292
+ if (fs.existsSync(configDir) && !isDir(configDir)) {
293
+ throw new NoSuitableConfigFileError();
294
+ }
295
+ if (opts.create && !fs.existsSync(configFilePath)) {
296
+ fs.writeFileSync(
297
+ configFilePath,
298
+ JSON.stringify(Config.emptyGlobalConfig, null, 2)
299
+ );
300
+ }
301
+ if (fs.existsSync(configFilePath) && !isFile(configFilePath)) {
302
+ throw new NoSuitableConfigFileError();
303
+ }
304
+ return configFilePath;
305
+ }
178
306
  /**
179
307
  * Resolve the space cache path.
180
308
  *
181
309
  * @param opts Options for resolving the space cache path.
182
310
  * @param opts.filePath The path to the space cache file.
183
311
  * @param opts.env The environment variables.
312
+ * @param opts.create Whether to create the file if it doesn't exist.
184
313
  * @returns The path to the space cache file.
185
314
  */
186
315
  static resolveSpaceCachePath(opts) {
187
- const env = opts.env;
188
- const home = env.HOME;
189
- const xdgConfigHome = env.XDG_CONFIG_HOME;
190
- const notionHome = env.NOTION_HOME;
191
316
  let configFileDir;
192
317
  let configFilePath;
193
318
  if (opts.filePath) {
@@ -195,37 +320,29 @@ class Config {
195
320
  configFileDir = path.dirname(absPath);
196
321
  configFilePath = absPath;
197
322
  } else {
198
- if (notionHome && isDir(notionHome)) {
199
- configFileDir = notionHome;
200
- configFilePath = path.join(configFileDir, "spaces.json");
201
- } else if (xdgConfigHome && isDir(xdgConfigHome)) {
202
- configFileDir = path.join(xdgConfigHome, "notion");
203
- configFilePath = path.join(configFileDir, "spaces.json");
204
- } else if (home && isDir(path.join(home, ".config"))) {
205
- configFileDir = path.join(home, ".config", "notion");
206
- configFilePath = path.join(configFileDir, "spaces.json");
207
- } else if (home && isDir(home)) {
208
- configFileDir = path.join(home, ".notion");
209
- configFilePath = path.join(configFileDir, "spaces.json");
323
+ const configDir = Config.resolveGlobalDir(opts.env);
324
+ if (configDir) {
325
+ configFileDir = configDir;
326
+ configFilePath = path.join(configDir, "spaces.json");
210
327
  }
211
328
  }
212
329
  debug("%o", { configFileDir, configFilePath });
213
330
  if (!(configFileDir && configFilePath)) {
214
331
  throw new NoSuitableConfigFileError();
215
332
  }
216
- if (!fs.existsSync(configFileDir)) {
333
+ if (opts.create && !fs.existsSync(configFileDir)) {
217
334
  fs.mkdirSync(configFileDir, { recursive: true });
218
335
  }
219
- if (!isDir(configFileDir)) {
336
+ if (fs.existsSync(configFileDir) && !isDir(configFileDir)) {
220
337
  throw new NoSuitableConfigFileError();
221
338
  }
222
- if (!fs.existsSync(configFilePath)) {
339
+ if (opts.create && !fs.existsSync(configFilePath)) {
223
340
  fs.writeFileSync(
224
341
  configFilePath,
225
342
  JSON.stringify(Config.emptySpaceCache, null, 2)
226
343
  );
227
344
  }
228
- if (!isFile(configFilePath)) {
345
+ if (fs.existsSync(configFilePath) && !isFile(configFilePath)) {
229
346
  throw new NoSuitableConfigFileError();
230
347
  }
231
348
  return configFilePath;
@@ -235,27 +352,28 @@ class Config {
235
352
  *
236
353
  * @param opts Options for resolving the local config path.
237
354
  * @param opts.filePath The path to the local config file.
238
- * @returns
355
+ * @param opts.create Whether to create the file if it doesn't exist.
356
+ * @returns The path to the local config file.
239
357
  */
240
358
  static resolveLocalConfigPath(opts = {}) {
241
359
  const absPath = path.resolve(
242
360
  process.cwd(),
243
361
  opts.filePath ?? "./workers.json"
244
362
  );
245
- if (!fs.existsSync(absPath)) {
363
+ if (opts.create && !fs.existsSync(absPath)) {
246
364
  fs.writeFileSync(
247
365
  absPath,
248
366
  JSON.stringify(Config.emptyLocalConfig, null, 2)
249
367
  );
250
368
  }
251
- if (!isFile(absPath)) {
369
+ if (fs.existsSync(absPath) && !isFile(absPath)) {
252
370
  throw new NoSuitableConfigFileError();
253
371
  }
254
372
  return absPath;
255
373
  }
256
374
  /**
257
- * Load config from space cache and local config files, then resolve them to
258
- * a final config for this execution of the command line interface.
375
+ * Load config from global config, space cache, and local config files, then
376
+ * resolve them to a final config for this execution of the command line interface.
259
377
  *
260
378
  * If the local config is v0, it will be upgraded to v1.
261
379
  *
@@ -264,43 +382,60 @@ class Config {
264
382
  * - Environment: WORKERS_ENVIRONMENT or --env flag
265
383
  * - Token: WORKERS_TOKEN or --token flag
266
384
  * - Base URL: WORKERS_BASE_URL or --base-url flag
385
+ * - Space: --space flag > WORKERS_SPACE_ID > workers.json > config.json defaultSpaceId
267
386
  *
268
387
  * @param opts The options to load the config from.
388
+ * @param opts.globalConfigPath The path to the global config file.
269
389
  * @param opts.spaceCachePath The path to the space cache file.
270
390
  * @param opts.localPath The path to the local config file.
271
391
  * @param opts.env The process environment.
272
- * @param opts.processEnv The process environment.
273
392
  * @param opts.flags The global flags.
393
+ * @param opts.spaceOverride Optional space ID override (from --space flag).
274
394
  * @returns The resolved config.
275
395
  */
276
396
  static load(opts) {
277
- const { spaceCachePath, localPath, env, flags } = opts;
397
+ const { globalConfigPath, spaceCachePath, localPath, env, flags } = opts;
398
+ let globalConfig;
399
+ if (fs.existsSync(globalConfigPath)) {
400
+ const globalJson = fs.readFileSync(globalConfigPath, "utf-8");
401
+ const globalRaw = JSON.parse(globalJson);
402
+ globalConfig = GlobalConfig.parse(globalRaw);
403
+ } else {
404
+ globalConfig = Config.emptyGlobalConfig;
405
+ }
278
406
  const cacheJson = fs.readFileSync(spaceCachePath, "utf-8");
279
407
  const cacheRaw = JSON.parse(cacheJson);
280
408
  const spaceCache = SpaceCache.parse(cacheRaw);
281
- const localJson = fs.readFileSync(localPath, "utf-8");
282
- const localRaw = JSON.parse(localJson);
283
409
  let localConfig;
284
- const v1Result = LocalConfig.safeParse(localRaw);
285
- if (v1Result.success) {
286
- localConfig = v1Result.data;
410
+ if (fs.existsSync(localPath)) {
411
+ const localJson = fs.readFileSync(localPath, "utf-8");
412
+ const localRaw = JSON.parse(localJson);
413
+ const v1Result = LocalConfig.safeParse(localRaw);
414
+ if (v1Result.success) {
415
+ localConfig = v1Result.data;
416
+ } else {
417
+ const v0Config = LocalConfigV0.parse(localRaw);
418
+ localConfig = upgradeV0ToV1(v0Config, localPath);
419
+ }
287
420
  } else {
288
- const v0Config = LocalConfigV0.parse(localRaw);
289
- localConfig = upgradeV0ToV1(v0Config, localPath);
421
+ localConfig = Config.emptyLocalConfig;
290
422
  }
291
423
  const environment = Environment.parse(
292
424
  env.WORKERS_ENVIRONMENT ?? flags.env ?? localConfig.environment
293
425
  );
294
426
  const baseURLOverride = env.WORKERS_BASE_URL ?? flags["base-url"] ?? localConfig.baseURL;
295
427
  const baseURL = baseURLOverride ?? baseUrlForEnvironment(environment);
296
- const spaceId = localConfig.spaceId;
428
+ const spaceId = flags.space ?? env.WORKERS_SPACE_ID ?? localConfig.spaceId ?? globalConfig.defaultSpaceId;
297
429
  const workerId = localConfig.workerId;
298
430
  const token = env.WORKERS_TOKEN ?? flags.token ?? null;
299
431
  return new Config({
432
+ globalConfigPath,
433
+ globalConfig,
300
434
  spaceCachePath,
301
435
  spaceCache,
302
436
  localConfigPath: localPath,
303
437
  localConfig,
438
+ isInWorkerDirectory: isWorkerDirectory(path.dirname(localPath)),
304
439
  environment,
305
440
  baseURL,
306
441
  spaceId,
@@ -320,6 +455,12 @@ class Config {
320
455
  static get emptySpaceCache() {
321
456
  return { version: "1", spaces: {} };
322
457
  }
458
+ /**
459
+ * The empty global config.
460
+ */
461
+ static get emptyGlobalConfig() {
462
+ return { version: "1", defaultSpaceId: null };
463
+ }
323
464
  }
324
465
  function isDir(path2) {
325
466
  try {
@@ -328,9 +469,25 @@ function isDir(path2) {
328
469
  return false;
329
470
  }
330
471
  }
331
- function isFile(path2) {
472
+ function isFile(filePath) {
332
473
  try {
333
- return fs.statSync(path2).isFile();
474
+ return fs.statSync(filePath).isFile();
475
+ } catch {
476
+ return false;
477
+ }
478
+ }
479
+ function isWorkerDirectory(dir) {
480
+ const packageJsonPath = path.join(dir, "package.json");
481
+ if (!fs.existsSync(packageJsonPath)) {
482
+ return false;
483
+ }
484
+ try {
485
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
486
+ const allDeps = {
487
+ ...pkg.dependencies,
488
+ ...pkg.devDependencies
489
+ };
490
+ return Object.keys(allDeps).some((dep) => dep.startsWith("@project-ajax/"));
334
491
  } catch {
335
492
  return false;
336
493
  }
package/dist/flags.d.ts CHANGED
@@ -7,14 +7,19 @@ export interface GlobalFlags {
7
7
  env?: Environment;
8
8
  token?: string;
9
9
  "base-url"?: string;
10
+ space?: string;
10
11
  }
11
12
  export declare const globalFlags: {
12
13
  [K in keyof Required<GlobalFlags>]: TypedFlagParameter<GlobalFlags[K], LocalContext>;
13
14
  };
14
15
  export interface FormatFlags {
15
16
  plain: boolean;
17
+ human: boolean;
18
+ json: boolean;
16
19
  }
17
20
  export declare const formatFlags: {
18
21
  [K in keyof Required<FormatFlags>]: TypedFlagParameter<FormatFlags[K], LocalContext>;
19
22
  };
23
+ export type OutputFormat = "human" | "plain" | "json";
24
+ export declare function resolveFormat(flags: FormatFlags, process: NodeJS.Process): OutputFormat;
20
25
  //# sourceMappingURL=flags.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"flags.d.ts","sourceRoot":"","sources":["../src/flags.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,MAAM,WAAW,WAAW;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,WAAW,EAAE;KACxB,CAAC,IAAI,MAAM,QAAQ,CAAC,WAAW,CAAC,GAAG,kBAAkB,CACrD,WAAW,CAAC,CAAC,CAAC,EACd,YAAY,CACZ;CA+BD,CAAC;AAEF,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,OAAO,CAAC;CACf;AAED,eAAO,MAAM,WAAW,EAAE;KACxB,CAAC,IAAI,MAAM,QAAQ,CAAC,WAAW,CAAC,GAAG,kBAAkB,CACrD,WAAW,CAAC,CAAC,CAAC,EACd,YAAY,CACZ;CAOD,CAAC"}
1
+ {"version":3,"file":"flags.d.ts","sourceRoot":"","sources":["../src/flags.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,MAAM,WAAW,WAAW;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,WAAW,EAAE;KACxB,CAAC,IAAI,MAAM,QAAQ,CAAC,WAAW,CAAC,GAAG,kBAAkB,CACrD,WAAW,CAAC,CAAC,CAAC,EACd,YAAY,CACZ;CAsCD,CAAC;AAEF,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACd;AAED,eAAO,MAAM,WAAW,EAAE;KACxB,CAAC,IAAI,MAAM,QAAQ,CAAC,WAAW,CAAC,GAAG,kBAAkB,CACrD,WAAW,CAAC,CAAC,CAAC,EACd,YAAY,CACZ;CAiBD,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;AAEtD,wBAAgB,aAAa,CAC5B,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,MAAM,CAAC,OAAO,GACrB,YAAY,CAoBd"}
package/dist/flags.js CHANGED
@@ -28,6 +28,12 @@ const globalFlags = {
28
28
  parse: String,
29
29
  brief: "The base URL to use for all API requests",
30
30
  optional: true
31
+ },
32
+ space: {
33
+ kind: "parsed",
34
+ parse: String,
35
+ brief: "Space ID to use. Overrides local config and global default. Triggers login if not authenticated.",
36
+ optional: true
31
37
  }
32
38
  };
33
39
  const formatFlags = {
@@ -35,9 +41,36 @@ const formatFlags = {
35
41
  kind: "boolean",
36
42
  brief: "Output the results in plain text format",
37
43
  default: false
44
+ },
45
+ human: {
46
+ kind: "boolean",
47
+ brief: "Output the results in a human-friendly format",
48
+ default: false
49
+ },
50
+ json: {
51
+ kind: "boolean",
52
+ brief: "Output the results as JSON",
53
+ default: false
38
54
  }
39
55
  };
56
+ function resolveFormat(flags, process) {
57
+ const flagCount = Number(flags.plain) + Number(flags.human) + Number(flags.json);
58
+ if (flagCount > 1) {
59
+ throw new Error("Only one of --plain, --human, or --json can be used.");
60
+ }
61
+ if (flags.json) {
62
+ return "json";
63
+ }
64
+ if (flags.plain) {
65
+ return "plain";
66
+ }
67
+ if (flags.human) {
68
+ return "human";
69
+ }
70
+ return process.stdout.isTTY ? "human" : "plain";
71
+ }
40
72
  export {
41
73
  formatFlags,
42
- globalFlags
74
+ globalFlags,
75
+ resolveFormat
43
76
  };