@sanity/runtime-cli 4.1.0 → 4.3.0

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.
package/README.md CHANGED
@@ -20,7 +20,7 @@ $ npm install -g @sanity/runtime-cli
20
20
  $ sanity-run COMMAND
21
21
  running command...
22
22
  $ sanity-run (--version)
23
- @sanity/runtime-cli/4.1.0 linux-x64 node-v22.14.0
23
+ @sanity/runtime-cli/4.3.0 linux-x64 node-v22.14.0
24
24
  $ sanity-run --help [COMMAND]
25
25
  USAGE
26
26
  $ sanity-run COMMAND
@@ -40,6 +40,7 @@ USAGE
40
40
  * [`sanity-run blueprints stacks`](#sanity-run-blueprints-stacks)
41
41
  * [`sanity-run functions dev`](#sanity-run-functions-dev)
42
42
  * [`sanity-run functions env add NAME KEY VALUE`](#sanity-run-functions-env-add-name-key-value)
43
+ * [`sanity-run functions env list NAME`](#sanity-run-functions-env-list-name)
43
44
  * [`sanity-run functions env remove NAME KEY`](#sanity-run-functions-env-remove-name-key)
44
45
  * [`sanity-run functions invoke NAME`](#sanity-run-functions-invoke-name)
45
46
  * [`sanity-run functions logs NAME`](#sanity-run-functions-logs-name)
@@ -52,19 +53,28 @@ Add a resource to a Blueprint
52
53
 
53
54
  ```
54
55
  USAGE
55
- $ sanity-run blueprints add TYPE
56
+ $ sanity-run blueprints add TYPE [--function-type document-publish -n <value>]
56
57
 
57
58
  ARGUMENTS
58
59
  TYPE (function) Type of resource to add (e.g. function)
59
60
 
61
+ FLAGS
62
+ -n, --name=<value> Name of the resource to add
63
+ --function-type=<option> Type of function to add (e.g. document-publish)
64
+ <options: document-publish>
65
+
60
66
  DESCRIPTION
61
67
  Add a resource to a Blueprint
62
68
 
63
69
  EXAMPLES
64
70
  $ sanity-run blueprints add function
71
+
72
+ $ sanity-run blueprints add function --name my-function
73
+
74
+ $ sanity-run blueprints add function --name my-function --function-type document-publish
65
75
  ```
66
76
 
67
- _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v4.1.0/src/commands/blueprints/add.ts)_
77
+ _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.0/src/commands/blueprints/add.ts)_
68
78
 
69
79
  ## `sanity-run blueprints config`
70
80
 
@@ -72,10 +82,13 @@ View or edit Blueprint configuration
72
82
 
73
83
  ```
74
84
  USAGE
75
- $ sanity-run blueprints config [--edit]
85
+ $ sanity-run blueprints config [-t] [--project-id <value> -e] [--stack-id <value> ]
76
86
 
77
87
  FLAGS
78
- --edit Edit the configuration
88
+ -e, --edit Edit the configuration
89
+ -t, --test-config Validate the configuration
90
+ --project-id=<value> Update the Project ID in the configuration. Requires --edit flag
91
+ --stack-id=<value> Update the Stack ID in the configuration. Requires --edit flag
79
92
 
80
93
  DESCRIPTION
81
94
  View or edit Blueprint configuration
@@ -83,10 +96,14 @@ DESCRIPTION
83
96
  EXAMPLES
84
97
  $ sanity-run blueprints config
85
98
 
99
+ $ sanity-run blueprints config --test-config
100
+
86
101
  $ sanity-run blueprints config --edit
102
+
103
+ $ sanity-run blueprints config --edit --project-id <projectId> --stack-id <stackId>
87
104
  ```
88
105
 
89
- _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v4.1.0/src/commands/blueprints/config.ts)_
106
+ _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.0/src/commands/blueprints/config.ts)_
90
107
 
91
108
  ## `sanity-run blueprints deploy`
92
109
 
@@ -106,7 +123,7 @@ EXAMPLES
106
123
  $ sanity-run blueprints deploy
107
124
  ```
108
125
 
109
- _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v4.1.0/src/commands/blueprints/deploy.ts)_
126
+ _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.0/src/commands/blueprints/deploy.ts)_
110
127
 
111
128
  ## `sanity-run blueprints destroy`
112
129
 
@@ -129,7 +146,7 @@ EXAMPLES
129
146
  $ sanity-run blueprints destroy --id ST-a1b2c3
130
147
  ```
131
148
 
132
- _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v4.1.0/src/commands/blueprints/destroy.ts)_
149
+ _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.0/src/commands/blueprints/destroy.ts)_
133
150
 
134
151
  ## `sanity-run blueprints info`
135
152
 
@@ -151,7 +168,7 @@ EXAMPLES
151
168
  $ sanity-run blueprints info --id ST-a1b2c3
152
169
  ```
153
170
 
154
- _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v4.1.0/src/commands/blueprints/info.ts)_
171
+ _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.0/src/commands/blueprints/info.ts)_
155
172
 
156
173
  ## `sanity-run blueprints init`
157
174
 
@@ -159,16 +176,29 @@ Initialize a new Blueprint
159
176
 
160
177
  ```
161
178
  USAGE
162
- $ sanity-run blueprints init
179
+ $ sanity-run blueprints init [--blueprint-type json|js|ts] [--stack-id <value> --project-id <value>] [-n <value> ]
180
+
181
+ FLAGS
182
+ -n, --stack-name=<value> Name to use for a NEW Stack
183
+ --blueprint-type=<option> Blueprint manifest type to use for the Blueprint
184
+ <options: json|js|ts>
185
+ --project-id=<value> Sanity Project ID to use for the Blueprint
186
+ --stack-id=<value> Existing Stack ID to use for the Blueprint
163
187
 
164
188
  DESCRIPTION
165
189
  Initialize a new Blueprint
166
190
 
167
191
  EXAMPLES
168
192
  $ sanity-run blueprints init
193
+
194
+ $ sanity-run blueprints init --blueprint-type <json|js|ts>
195
+
196
+ $ sanity-run blueprints init --blueprint-type <json|js|ts> --project-id <projectId> --stack-id <stackId>
197
+
198
+ $ sanity-run blueprints init --blueprint-type <json|js|ts> --project-id <projectId> --stack-name <stackName>
169
199
  ```
170
200
 
171
- _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v4.1.0/src/commands/blueprints/init.ts)_
201
+ _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.0/src/commands/blueprints/init.ts)_
172
202
 
173
203
  ## `sanity-run blueprints logs`
174
204
 
@@ -190,7 +220,7 @@ EXAMPLES
190
220
  $ sanity-run blueprints logs --watch
191
221
  ```
192
222
 
193
- _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v4.1.0/src/commands/blueprints/logs.ts)_
223
+ _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.0/src/commands/blueprints/logs.ts)_
194
224
 
195
225
  ## `sanity-run blueprints plan`
196
226
 
@@ -207,7 +237,7 @@ EXAMPLES
207
237
  $ sanity-run blueprints plan
208
238
  ```
209
239
 
210
- _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v4.1.0/src/commands/blueprints/plan.ts)_
240
+ _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.0/src/commands/blueprints/plan.ts)_
211
241
 
212
242
  ## `sanity-run blueprints stacks`
213
243
 
@@ -229,7 +259,7 @@ EXAMPLES
229
259
  $ sanity-run blueprints stacks --projectId a1b2c3
230
260
  ```
231
261
 
232
- _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v4.1.0/src/commands/blueprints/stacks.ts)_
262
+ _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.0/src/commands/blueprints/stacks.ts)_
233
263
 
234
264
  ## `sanity-run functions dev`
235
265
 
@@ -249,7 +279,7 @@ EXAMPLES
249
279
  $ sanity-run functions dev --port 8974
250
280
  ```
251
281
 
252
- _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v4.1.0/src/commands/functions/dev.ts)_
282
+ _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.0/src/commands/functions/dev.ts)_
253
283
 
254
284
  ## `sanity-run functions env add NAME KEY VALUE`
255
285
 
@@ -271,7 +301,27 @@ EXAMPLES
271
301
  $ sanity-run functions env add MyFunction API_URL https://api.example.com/
272
302
  ```
273
303
 
274
- _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v4.1.0/src/commands/functions/env/add.ts)_
304
+ _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.0/src/commands/functions/env/add.ts)_
305
+
306
+ ## `sanity-run functions env list NAME`
307
+
308
+ List the environment variables for a Sanity function
309
+
310
+ ```
311
+ USAGE
312
+ $ sanity-run functions env list NAME
313
+
314
+ ARGUMENTS
315
+ NAME The name of the Sanity Function
316
+
317
+ DESCRIPTION
318
+ List the environment variables for a Sanity function
319
+
320
+ EXAMPLES
321
+ $ sanity-run functions env list MyFunction
322
+ ```
323
+
324
+ _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.0/src/commands/functions/env/list.ts)_
275
325
 
276
326
  ## `sanity-run functions env remove NAME KEY`
277
327
 
@@ -292,7 +342,7 @@ EXAMPLES
292
342
  $ sanity-run functions env remove MyFunction API_URL
293
343
  ```
294
344
 
295
- _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v4.1.0/src/commands/functions/env/remove.ts)_
345
+ _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.0/src/commands/functions/env/remove.ts)_
296
346
 
297
347
  ## `sanity-run functions invoke NAME`
298
348
 
@@ -318,7 +368,7 @@ EXAMPLES
318
368
  $ sanity-run functions invoke <name> --file 'payload.json'
319
369
  ```
320
370
 
321
- _See code: [src/commands/functions/invoke.ts](https://github.com/sanity-io/runtime-cli/blob/v4.1.0/src/commands/functions/invoke.ts)_
371
+ _See code: [src/commands/functions/invoke.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.0/src/commands/functions/invoke.ts)_
322
372
 
323
373
  ## `sanity-run functions logs NAME`
324
374
 
@@ -350,7 +400,7 @@ EXAMPLES
350
400
  $ sanity-run functions logs <name> --delete
351
401
  ```
352
402
 
353
- _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v4.1.0/src/commands/functions/logs.ts)_
403
+ _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.0/src/commands/functions/logs.ts)_
354
404
 
355
405
  ## `sanity-run functions test NAME`
356
406
 
@@ -383,7 +433,7 @@ EXAMPLES
383
433
  $ sanity-run functions test <name> --data '{ "id": 1 }' --timeout 60
384
434
  ```
385
435
 
386
- _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v4.1.0/src/commands/functions/test.ts)_
436
+ _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.0/src/commands/functions/test.ts)_
387
437
 
388
438
  ## `sanity-run help [COMMAND]`
389
439
 
@@ -1,2 +1,3 @@
1
+ export * from './list.js';
1
2
  export * from './remove.js';
2
3
  export * from './update.js';
@@ -1,2 +1,3 @@
1
+ export * from './list.js';
1
2
  export * from './remove.js';
2
3
  export * from './update.js';
@@ -0,0 +1,6 @@
1
+ import type { AuthParams } from '../../../utils/types.js';
2
+ export declare function list(id: string, auth: AuthParams): Promise<{
3
+ ok: boolean;
4
+ envvars: any;
5
+ error: any;
6
+ }>;
@@ -0,0 +1,15 @@
1
+ import config from '../../../config.js';
2
+ import getHeaders from '../../../utils/get-headers.js';
3
+ const { functions } = config.server;
4
+ export async function list(id, auth) {
5
+ const response = await fetch(`${functions}vX/functions/${id}/envvars`, {
6
+ headers: getHeaders(auth),
7
+ method: 'GET',
8
+ });
9
+ const json = await response.json();
10
+ return {
11
+ ok: response.ok,
12
+ envvars: json.envvars,
13
+ error: response.ok ? null : json?.error?.message,
14
+ };
15
+ }
@@ -5,6 +5,15 @@ export default class Add extends Command {
5
5
  static args: {
6
6
  type: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
7
  };
8
+ static flags: {
9
+ name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ 'function-type': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ };
8
12
  run(): Promise<void>;
9
- private addFunction;
13
+ promptForFunctionName(): Promise<string>;
14
+ promptForFunctionType(): Promise<string>;
15
+ addFunction({ name, type }: {
16
+ name?: string;
17
+ type?: string;
18
+ }): Promise<void>;
10
19
  }
@@ -1,11 +1,16 @@
1
- import { Args, Command } from '@oclif/core';
1
+ import { Args, Command, Flags } from '@oclif/core';
2
2
  import highlight from 'color-json';
3
3
  import inquirer from 'inquirer';
4
4
  import { findBlueprintFile } from '../../actions/blueprints/blueprint.js';
5
5
  import { createFunctionResource } from '../../actions/blueprints/resources.js';
6
+ import { validateFunctionName, validateFunctionType } from '../../utils/validate/resource.js';
6
7
  export default class Add extends Command {
7
8
  static description = 'Add a resource to a Blueprint';
8
- static examples = ['<%= config.bin %> <%= command.id %> function'];
9
+ static examples = [
10
+ '<%= config.bin %> <%= command.id %> function',
11
+ '<%= config.bin %> <%= command.id %> function --name my-function',
12
+ '<%= config.bin %> <%= command.id %> function --name my-function --function-type document-publish',
13
+ ];
9
14
  static args = {
10
15
  type: Args.string({
11
16
  description: 'Type of resource to add (e.g. function)',
@@ -13,19 +18,30 @@ export default class Add extends Command {
13
18
  required: true,
14
19
  }),
15
20
  };
21
+ static flags = {
22
+ name: Flags.string({
23
+ description: 'Name of the resource to add',
24
+ char: 'n',
25
+ }),
26
+ 'function-type': Flags.string({
27
+ description: 'Type of function to add (e.g. document-publish)',
28
+ options: ['document-publish' /*, 'document-create', 'document-delete'*/],
29
+ dependsOn: ['name'],
30
+ }),
31
+ };
16
32
  async run() {
17
- const { args } = await this.parse(Add);
33
+ const { args, flags } = await this.parse(Add);
18
34
  const existingBlueprint = findBlueprintFile();
19
35
  if (!existingBlueprint) {
20
36
  this.error('No blueprint file found. Run `sanity blueprints init` first.');
21
37
  }
22
- if (args.type === 'function') {
23
- await this.addFunction();
24
- return;
25
- }
26
- this.error(`Unsupported resource type: ${args.type}`);
38
+ if (args.type !== 'function')
39
+ this.error(`Unsupported resource type: ${args.type}`);
40
+ const resourceName = flags.name;
41
+ const functionType = flags['function-type'];
42
+ await this.addFunction({ name: resourceName, type: functionType });
27
43
  }
28
- async addFunction() {
44
+ async promptForFunctionName() {
29
45
  const { functionName } = await inquirer.prompt([
30
46
  {
31
47
  type: 'input',
@@ -34,6 +50,9 @@ export default class Add extends Command {
34
50
  validate: (input) => input.length > 0 || 'Function name is required',
35
51
  },
36
52
  ]);
53
+ return functionName;
54
+ }
55
+ async promptForFunctionType() {
37
56
  const { functionType } = await inquirer.prompt([
38
57
  {
39
58
  type: 'list',
@@ -47,6 +66,17 @@ export default class Add extends Command {
47
66
  default: 'document-publish',
48
67
  },
49
68
  ]);
69
+ return functionType;
70
+ }
71
+ async addFunction({ name, type }) {
72
+ const functionName = name || (await this.promptForFunctionName());
73
+ if (!validateFunctionName(functionName)) {
74
+ this.error('Invalid function name. Must be 6+ characters, no special characters, no spaces');
75
+ }
76
+ const functionType = type || (await this.promptForFunctionType());
77
+ if (!validateFunctionType(functionType)) {
78
+ this.error('Invalid function type. Must be one of: document-publish, document-create, document-delete');
79
+ }
50
80
  const { filePath, resourceAdded, resource } = createFunctionResource({
51
81
  name: functionName,
52
82
  type: functionType,
@@ -3,7 +3,24 @@ export default class Config extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static flags: {
6
+ 'test-config': import("@oclif/core/interfaces").BooleanFlag<boolean>;
6
7
  edit: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ 'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'stack-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
10
  };
8
11
  run(): Promise<void>;
12
+ promptForProjectId({ knownProjectId }: {
13
+ knownProjectId?: string;
14
+ }): Promise<string>;
15
+ promptForStackId({ projectId, knownStackId, }: {
16
+ projectId: string;
17
+ knownStackId?: string;
18
+ }): Promise<string | undefined>;
19
+ testConfigAndReport({ stackId, projectId }: {
20
+ stackId: string;
21
+ projectId: string;
22
+ }): Promise<{
23
+ ok: boolean;
24
+ error: string | null;
25
+ }>;
9
26
  }
@@ -1,9 +1,10 @@
1
1
  import { Command, Flags } from '@oclif/core';
2
2
  import highlight from 'color-json';
3
3
  import inquirer from 'inquirer';
4
+ import Spinner from 'yocto-spinner';
4
5
  import { readBlueprintOnDisk, writeConfigFile } from '../../actions/blueprints/blueprint.js';
5
6
  import { listProjects } from '../../actions/blueprints/projects.js';
6
- import { listStacks } from '../../actions/blueprints/stacks.js';
7
+ import { getStack, listStacks } from '../../actions/blueprints/stacks.js';
7
8
  import config from '../../config.js';
8
9
  import { bold, dim, niceId } from '../../utils/display/colors.js';
9
10
  const { token } = config;
@@ -11,27 +12,86 @@ export default class Config extends Command {
11
12
  static description = 'View or edit Blueprint configuration';
12
13
  static examples = [
13
14
  '<%= config.bin %> <%= command.id %>',
15
+ '<%= config.bin %> <%= command.id %> --test-config',
14
16
  '<%= config.bin %> <%= command.id %> --edit',
17
+ '<%= config.bin %> <%= command.id %> --edit --project-id <projectId> --stack-id <stackId>',
15
18
  ];
16
19
  static flags = {
20
+ 'test-config': Flags.boolean({
21
+ char: 't',
22
+ aliases: ['test', 'validate'],
23
+ description: 'Validate the configuration',
24
+ default: false,
25
+ }),
17
26
  edit: Flags.boolean({
27
+ char: 'e',
18
28
  description: 'Edit the configuration',
19
29
  default: false,
20
30
  }),
31
+ 'project-id': Flags.string({
32
+ description: 'Update the Project ID in the configuration. Requires --edit flag',
33
+ aliases: ['project', 'projectId'],
34
+ dependsOn: ['edit'],
35
+ }),
36
+ 'stack-id': Flags.string({
37
+ description: 'Update the Stack ID in the configuration. Requires --edit flag',
38
+ aliases: ['stack', 'stackId'],
39
+ dependsOn: ['edit'],
40
+ }),
21
41
  };
22
42
  async run() {
23
43
  const { flags } = await this.parse(Config);
44
+ const { edit: editConfig, 'project-id': editProjectId, 'stack-id': editStackId, 'test-config': testConfig, } = flags;
24
45
  const blueprint = await readBlueprintOnDisk();
25
- const { stackId: configStackId, projectId: configProjectId, configPath, fileInfo } = blueprint;
26
- const { extension: blueprintExtension, blueprintFilePath } = fileInfo;
46
+ const { stackId: configStackId, projectId: configProjectId } = blueprint;
27
47
  if (!configStackId && !configProjectId) {
28
- this.log('Project and Stack configuration is missing.');
48
+ this.error('Project and Stack configuration is missing! Use `blueprints init` to create a configuration.');
29
49
  }
30
50
  this.log(bold('Current configuration:'));
31
- this.log(`Sanity Project: ${niceId(configProjectId)}`);
32
- this.log(`Blueprint Stack: ${niceId(configStackId)}`);
33
- if (!flags.edit)
51
+ this.log(` Sanity Project: ${niceId(configProjectId)}`);
52
+ this.log(` Blueprint Stack: ${niceId(configStackId)}`);
53
+ if ((editProjectId || editStackId) && !editConfig) {
54
+ this.log('To update the configuration, use the --edit flag.');
55
+ return;
56
+ }
57
+ if (!editConfig && !testConfig)
58
+ return;
59
+ if (testConfig && !editConfig) {
60
+ if (configStackId && configProjectId) {
61
+ await this.testConfigAndReport({ stackId: configStackId, projectId: configProjectId });
62
+ }
63
+ else {
64
+ this.log('Unable to test the configuration. Both Project and Stack IDs must be set.');
65
+ }
66
+ }
67
+ if (!editConfig)
34
68
  return;
69
+ const projectId = editProjectId || (await this.promptForProjectId({ knownProjectId: configProjectId }));
70
+ if (!projectId)
71
+ this.error('Project ID is required.');
72
+ const stackId = editStackId || (await this.promptForStackId({ projectId, knownStackId: configStackId }));
73
+ if (testConfig) {
74
+ if (projectId && stackId) {
75
+ const { ok: newConfigOk } = await this.testConfigAndReport({ stackId, projectId });
76
+ if (!newConfigOk) {
77
+ this.error('Updated configuration has not been saved.');
78
+ }
79
+ }
80
+ else {
81
+ this.error('Unable to test the configuration. Both Project and Stack IDs must be set.');
82
+ }
83
+ }
84
+ try {
85
+ // update or create .blueprint/config.json
86
+ writeConfigFile({ projectId, stackId });
87
+ this.log('Configuration updated successfully.');
88
+ }
89
+ catch (error) {
90
+ this.log('Unable to dynamically update config. Use these values in your blueprint:');
91
+ this.log(highlight(JSON.stringify({ metadata: { projectId, stackId } }, null, 2)));
92
+ }
93
+ }
94
+ async promptForProjectId({ knownProjectId }) {
35
95
  const { ok, projects, error } = await listProjects({ token });
36
96
  if (!ok)
37
97
  this.error(error ?? 'Unknown error listing projects');
@@ -42,46 +102,55 @@ export default class Config extends Command {
42
102
  name: `"${displayName}" ${niceId(projectId)}`,
43
103
  value: projectId,
44
104
  }));
45
- const { projectId } = await inquirer.prompt([
105
+ const { pickedProjectId } = await inquirer.prompt([
46
106
  {
47
107
  type: 'list',
48
- name: 'projectId',
108
+ name: 'pickedProjectId',
49
109
  message: 'Select your Sanity project:',
50
110
  choices: projectChoices,
51
- default: configProjectId,
111
+ default: knownProjectId,
52
112
  },
53
113
  ]);
114
+ return pickedProjectId;
115
+ }
116
+ async promptForStackId({ projectId, knownStackId, }) {
54
117
  const auth = { token, projectId };
55
118
  // get stacks for selected project
56
119
  const { ok: stacksOk, stacks, error: stacksError } = await listStacks(auth);
57
120
  if (!stacksOk)
58
121
  this.error(stacksError ?? 'Unknown error listing stacks');
59
- let stackId;
60
122
  if (stacks.length > 0) {
61
123
  const stackChoices = stacks.map((s) => ({
62
124
  name: `"${s.name}" ${niceId(s.id)} ${dim(`(${s.resources.length} res)`)}`,
63
125
  value: s.id,
64
126
  }));
65
127
  stackChoices.push({ name: 'Unset Stack association', value: 'unset' });
66
- const { stackId: selectedStackId } = await inquirer.prompt([
128
+ const { pickedStackId } = await inquirer.prompt([
67
129
  {
68
130
  type: 'list',
69
- name: 'stackId',
131
+ name: 'pickedStackId',
70
132
  message: 'Select a stack:',
71
133
  choices: stackChoices,
72
- default: configStackId,
134
+ default: knownStackId,
73
135
  },
74
136
  ]);
75
- stackId = selectedStackId === 'unset' ? undefined : selectedStackId;
137
+ return pickedStackId === 'unset' ? undefined : pickedStackId;
76
138
  }
77
- try {
78
- // update or create .blueprint/config.json
79
- writeConfigFile({ projectId, stackId });
80
- this.log('Configuration updated successfully.');
139
+ return undefined;
140
+ }
141
+ async testConfigAndReport({ stackId, projectId }) {
142
+ const spinner = Spinner({ text: 'Testing the configuration...' }).start();
143
+ const { ok, error } = await getStack({
144
+ stackId,
145
+ auth: { token, projectId },
146
+ });
147
+ if (!ok) {
148
+ spinner.error(error ?? 'Unknown error testing the configuration');
149
+ this.log('Use the --edit flag to interactively update the configuration.');
81
150
  }
82
- catch (error) {
83
- this.log('Unable to dynamically update config. Use these values in your blueprint:');
84
- this.log(highlight(JSON.stringify({ metadata: { projectId, stackId } }, null, 2)));
151
+ else {
152
+ spinner.success('Configuration is valid.');
85
153
  }
154
+ return { ok, error };
86
155
  }
87
156
  }
@@ -1,6 +1,22 @@
1
1
  import { Command } from '@oclif/core';
2
+ import type { AuthParams, Stack, StackPayload } from '../../utils/types.js';
2
3
  export default class Init extends Command {
3
4
  static description: string;
4
5
  static examples: string[];
6
+ static flags: {
7
+ 'blueprint-type': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ 'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'stack-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ 'stack-name': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ };
5
12
  run(): Promise<void>;
13
+ promptForBlueprintType(): Promise<string>;
14
+ promptForProjectId(): Promise<string>;
15
+ promptForStackId({ projectId }: {
16
+ projectId: string;
17
+ }): Promise<string>;
18
+ createEmptyStack({ stackPayload, auth, }: {
19
+ stackPayload: StackPayload;
20
+ auth: AuthParams;
21
+ }): Promise<Stack>;
6
22
  }
@@ -1,6 +1,6 @@
1
1
  import { join } from 'node:path';
2
2
  import { cwd } from 'node:process';
3
- import { Command } from '@oclif/core';
3
+ import { Command, Flags } from '@oclif/core';
4
4
  import inquirer from 'inquirer';
5
5
  import { findBlueprintFile, writeBlueprintToDisk, writeConfigFile, } from '../../actions/blueprints/blueprint.js';
6
6
  import { listProjects } from '../../actions/blueprints/projects.js';
@@ -10,16 +10,77 @@ import { bold, dim, niceId, underline } from '../../utils/display/colors.js';
10
10
  const { token } = config;
11
11
  export default class Init extends Command {
12
12
  static description = 'Initialize a new Blueprint';
13
- static examples = ['<%= config.bin %> <%= command.id %>'];
13
+ static examples = [
14
+ '<%= config.bin %> <%= command.id %>',
15
+ '<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts>',
16
+ '<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts> --project-id <projectId> --stack-id <stackId>',
17
+ '<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts> --project-id <projectId> --stack-name <stackName>',
18
+ ];
19
+ static flags = {
20
+ 'blueprint-type': Flags.string({
21
+ description: 'Blueprint manifest type to use for the Blueprint',
22
+ options: ['json', 'js', 'ts'],
23
+ aliases: ['type'],
24
+ }),
25
+ 'project-id': Flags.string({
26
+ description: 'Sanity Project ID to use for the Blueprint',
27
+ aliases: ['project', 'projectId'],
28
+ }),
29
+ 'stack-id': Flags.string({
30
+ description: 'Existing Stack ID to use for the Blueprint',
31
+ aliases: ['stack', 'stackId'],
32
+ dependsOn: ['project-id'],
33
+ exclusive: ['stack-name'],
34
+ }),
35
+ 'stack-name': Flags.string({
36
+ char: 'n',
37
+ description: 'Name to use for a NEW Stack',
38
+ aliases: ['name'],
39
+ dependsOn: ['project-id'],
40
+ exclusive: ['stack-id'],
41
+ }),
42
+ };
14
43
  async run() {
44
+ const { flags } = await this.parse(Init);
45
+ const { 'blueprint-type': flagBlueprintType, 'project-id': flagProjectId, 'stack-id': flagStackId, 'stack-name': flagStackName, } = flags;
15
46
  const existingBlueprint = findBlueprintFile();
16
47
  if (existingBlueprint) {
17
48
  this.error(`A blueprint file already exists: ${existingBlueprint.fileName}`);
18
49
  }
19
- const { blueprintExtension } = await inquirer.prompt([
50
+ const blueprintExtension = flagBlueprintType || (await this.promptForBlueprintType());
51
+ if (!blueprintExtension)
52
+ this.error('Blueprint type is required.');
53
+ const projectId = flagProjectId || (await this.promptForProjectId());
54
+ if (!projectId)
55
+ this.error('Project ID is required.');
56
+ let stackId = flagStackId;
57
+ if (!stackId) {
58
+ if (flagStackName) {
59
+ const stack = await this.createEmptyStack({
60
+ stackPayload: { name: flagStackName, projectId, document: { resources: [] } },
61
+ auth: { token, projectId },
62
+ });
63
+ stackId = stack.id;
64
+ }
65
+ else {
66
+ stackId = await this.promptForStackId({ projectId });
67
+ }
68
+ }
69
+ const fileName = `blueprint.${blueprintExtension}`;
70
+ const filePath = join(cwd(), fileName);
71
+ writeBlueprintToDisk({ blueprintFilePath: filePath });
72
+ this.log(`Created new blueprint: ./${fileName}`);
73
+ writeConfigFile({ blueprintFilePath: filePath, projectId, stackId });
74
+ this.log(`Created new config file: ./${fileName}.config.json`);
75
+ if (blueprintExtension === 'ts') {
76
+ this.log('\nNote: TypeScript support requires "tsx" to be installed. Run: npm install -D tsx');
77
+ }
78
+ }
79
+ async promptForBlueprintType() {
80
+ const { pickedBlueprintsType } = await inquirer.prompt([
20
81
  {
21
82
  type: 'list',
22
- name: 'blueprintExtension',
83
+ name: 'pickedBlueprintsType',
23
84
  message: 'Choose a blueprint type:',
24
85
  choices: [
25
86
  { name: 'JSON (Recommended)', value: 'json' },
@@ -29,6 +90,9 @@ export default class Init extends Command {
29
90
  default: 'json',
30
91
  },
31
92
  ]);
93
+ return pickedBlueprintsType;
94
+ }
95
+ async promptForProjectId() {
32
96
  const { ok: projectsOk, error: projectsErr, projects } = await listProjects({ token });
33
97
  if (!projectsOk)
34
98
  this.error(projectsErr ?? 'Unknown error listing projects');
@@ -39,14 +103,17 @@ export default class Init extends Command {
39
103
  name: `"${displayName}" ${niceId(projectId)}`,
40
104
  value: projectId,
41
105
  }));
42
- const { projectId } = await inquirer.prompt([
106
+ const { pickedProjectId } = await inquirer.prompt([
43
107
  {
44
108
  type: 'list',
45
- name: 'projectId',
109
+ name: 'pickedProjectId',
46
110
  message: 'Select your Sanity project:',
47
111
  choices: projectChoices,
48
112
  },
49
113
  ]);
114
+ return pickedProjectId;
115
+ }
116
+ async promptForStackId({ projectId }) {
50
117
  const { ok: stacksOk, error: stacksErr, stacks } = await listStacks({ token, projectId });
51
118
  if (!stacksOk)
52
119
  this.error(stacksErr || 'Failed to list Stacks');
@@ -58,16 +125,15 @@ export default class Init extends Command {
58
125
  value: s.id,
59
126
  })));
60
127
  }
61
- let stackId;
62
- const { stackId: stackIdChoice } = await inquirer.prompt([
128
+ const { pickedStackId } = await inquirer.prompt([
63
129
  {
64
130
  type: 'list',
65
- name: 'stackId',
131
+ name: 'pickedStackId',
66
132
  message: 'Select your Blueprint Stack:',
67
133
  choices: stackChoices,
68
134
  },
69
135
  ]);
70
- if (stackIdChoice === 'new') {
136
+ if (pickedStackId === 'new') {
71
137
  const { stackName } = await inquirer.prompt([
72
138
  {
73
139
  type: 'input',
@@ -76,25 +142,18 @@ export default class Init extends Command {
76
142
  validate: (input) => input.length > 0 || 'Stack name is required',
77
143
  },
78
144
  ]);
79
- const { ok: stackOk, error: stackErr, stack, } = await createStack({
145
+ const stack = await this.createEmptyStack({
80
146
  stackPayload: { name: stackName, projectId, document: { resources: [] } },
81
147
  auth: { token, projectId },
82
148
  });
83
- if (!stackOk)
84
- this.error(stackErr || 'Failed to create Stack');
85
- stackId = stack.id;
86
- }
87
- else {
88
- stackId = stackIdChoice;
89
- }
90
- const fileName = `blueprint.${blueprintExtension}`;
91
- const filePath = join(cwd(), fileName);
92
- writeBlueprintToDisk({ blueprintFilePath: filePath });
93
- this.log(`Created new blueprint: ./${fileName}`);
94
- writeConfigFile({ blueprintFilePath: filePath, projectId, stackId });
95
- this.log(`Created new config file: ./${fileName}.config.json`);
96
- if (blueprintExtension === 'ts') {
97
- this.log('\nNote: TypeScript support requires "tsx" to be installed. Run: npm install -D tsx');
149
+ return stack.id;
98
150
  }
151
+ return pickedStackId;
152
+ }
153
+ async createEmptyStack({ stackPayload, auth, }) {
154
+ const response = await createStack({ stackPayload, auth });
155
+ if (!response.ok)
156
+ this.error(response.error || 'Failed to create Stack');
157
+ return response.stack;
99
158
  }
100
159
  }
@@ -0,0 +1,9 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class List extends Command {
3
+ static args: {
4
+ name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,30 @@
1
+ import { Args, Command } from '@oclif/core';
2
+ import { readBlueprintOnDisk } from '../../../actions/blueprints/blueprint.js';
3
+ import { list } from '../../../actions/functions/env/list.js';
4
+ import config from '../../../config.js';
5
+ import { findFunctionByName } from '../../../utils/find-function.js';
6
+ export default class List extends Command {
7
+ static args = {
8
+ name: Args.string({ description: 'The name of the Sanity Function', required: true }),
9
+ };
10
+ static description = 'List the environment variables for a Sanity function';
11
+ static examples = ['<%= config.bin %> <%= command.id %> MyFunction'];
12
+ async run() {
13
+ const { args } = await this.parse(List);
14
+ const { deployedStack } = await readBlueprintOnDisk({ getStack: true, token: config.token });
15
+ if (!deployedStack)
16
+ this.error('Stack not found. Is it deployed?'); // returns
17
+ const { projectId } = deployedStack;
18
+ const { externalId } = findFunctionByName(deployedStack, args.name);
19
+ const result = await list(externalId, {
20
+ token: config.token,
21
+ projectId,
22
+ });
23
+ if (!result.ok) {
24
+ this.error(`Error: ${result.error || 'Unknown error'}`);
25
+ }
26
+ for (const key of result.envvars) {
27
+ this.log(key);
28
+ }
29
+ }
30
+ }
@@ -1,4 +1,5 @@
1
1
  export * as display from './display/index.js';
2
2
  export * as findFunction from './find-function.js';
3
3
  export * as types from './types.js';
4
+ export * as validate from './validate/index.js';
4
5
  export * as vendor from './vendor/index.js';
@@ -1,4 +1,5 @@
1
1
  export * as display from './display/index.js';
2
2
  export * as findFunction from './find-function.js';
3
3
  export * as types from './types.js';
4
+ export * as validate from './validate/index.js';
4
5
  export * as vendor from './vendor/index.js';
@@ -0,0 +1 @@
1
+ export * as validate from './resource.js';
@@ -0,0 +1 @@
1
+ export * as validate from './resource.js';
@@ -0,0 +1,2 @@
1
+ export declare function validateFunctionName(name: string): boolean;
2
+ export declare function validateFunctionType(type: string): boolean;
@@ -0,0 +1,8 @@
1
+ export function validateFunctionName(name) {
2
+ // must be 6+ characters, no special characters, no spaces, allow _ and -
3
+ return /^[a-zA-Z0-9][a-zA-Z0-9_-]{5,}$/.test(name);
4
+ }
5
+ export function validateFunctionType(type) {
6
+ const functionType = type.replace('sanity.function.', '');
7
+ return ['document-publish', 'document-create', 'document-delete'].includes(functionType);
8
+ }
@@ -14,9 +14,33 @@
14
14
  },
15
15
  "description": "Add a resource to a Blueprint",
16
16
  "examples": [
17
- "<%= config.bin %> <%= command.id %> function"
17
+ "<%= config.bin %> <%= command.id %> function",
18
+ "<%= config.bin %> <%= command.id %> function --name my-function",
19
+ "<%= config.bin %> <%= command.id %> function --name my-function --function-type document-publish"
18
20
  ],
19
- "flags": {},
21
+ "flags": {
22
+ "name": {
23
+ "char": "n",
24
+ "description": "Name of the resource to add",
25
+ "name": "name",
26
+ "hasDynamicHelp": false,
27
+ "multiple": false,
28
+ "type": "option"
29
+ },
30
+ "function-type": {
31
+ "dependsOn": [
32
+ "name"
33
+ ],
34
+ "description": "Type of function to add (e.g. document-publish)",
35
+ "name": "function-type",
36
+ "hasDynamicHelp": false,
37
+ "multiple": false,
38
+ "options": [
39
+ "document-publish"
40
+ ],
41
+ "type": "option"
42
+ }
43
+ },
20
44
  "hasDynamicHelp": false,
21
45
  "hiddenAliases": [],
22
46
  "id": "blueprints:add",
@@ -39,14 +63,56 @@
39
63
  "description": "View or edit Blueprint configuration",
40
64
  "examples": [
41
65
  "<%= config.bin %> <%= command.id %>",
42
- "<%= config.bin %> <%= command.id %> --edit"
66
+ "<%= config.bin %> <%= command.id %> --test-config",
67
+ "<%= config.bin %> <%= command.id %> --edit",
68
+ "<%= config.bin %> <%= command.id %> --edit --project-id <projectId> --stack-id <stackId>"
43
69
  ],
44
70
  "flags": {
71
+ "test-config": {
72
+ "aliases": [
73
+ "test",
74
+ "validate"
75
+ ],
76
+ "char": "t",
77
+ "description": "Validate the configuration",
78
+ "name": "test-config",
79
+ "allowNo": false,
80
+ "type": "boolean"
81
+ },
45
82
  "edit": {
83
+ "char": "e",
46
84
  "description": "Edit the configuration",
47
85
  "name": "edit",
48
86
  "allowNo": false,
49
87
  "type": "boolean"
88
+ },
89
+ "project-id": {
90
+ "aliases": [
91
+ "project",
92
+ "projectId"
93
+ ],
94
+ "dependsOn": [
95
+ "edit"
96
+ ],
97
+ "description": "Update the Project ID in the configuration. Requires --edit flag",
98
+ "name": "project-id",
99
+ "hasDynamicHelp": false,
100
+ "multiple": false,
101
+ "type": "option"
102
+ },
103
+ "stack-id": {
104
+ "aliases": [
105
+ "stack",
106
+ "stackId"
107
+ ],
108
+ "dependsOn": [
109
+ "edit"
110
+ ],
111
+ "description": "Update the Stack ID in the configuration. Requires --edit flag",
112
+ "name": "stack-id",
113
+ "hasDynamicHelp": false,
114
+ "multiple": false,
115
+ "type": "option"
50
116
  }
51
117
  },
52
118
  "hasDynamicHelp": false,
@@ -175,9 +241,73 @@
175
241
  "args": {},
176
242
  "description": "Initialize a new Blueprint",
177
243
  "examples": [
178
- "<%= config.bin %> <%= command.id %>"
244
+ "<%= config.bin %> <%= command.id %>",
245
+ "<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts>",
246
+ "<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts> --project-id <projectId> --stack-id <stackId>",
247
+ "<%= config.bin %> <%= command.id %> --blueprint-type <json|js|ts> --project-id <projectId> --stack-name <stackName>"
179
248
  ],
180
- "flags": {},
249
+ "flags": {
250
+ "blueprint-type": {
251
+ "aliases": [
252
+ "type"
253
+ ],
254
+ "description": "Blueprint manifest type to use for the Blueprint",
255
+ "name": "blueprint-type",
256
+ "hasDynamicHelp": false,
257
+ "multiple": false,
258
+ "options": [
259
+ "json",
260
+ "js",
261
+ "ts"
262
+ ],
263
+ "type": "option"
264
+ },
265
+ "project-id": {
266
+ "aliases": [
267
+ "project",
268
+ "projectId"
269
+ ],
270
+ "description": "Sanity Project ID to use for the Blueprint",
271
+ "name": "project-id",
272
+ "hasDynamicHelp": false,
273
+ "multiple": false,
274
+ "type": "option"
275
+ },
276
+ "stack-id": {
277
+ "aliases": [
278
+ "stack",
279
+ "stackId"
280
+ ],
281
+ "dependsOn": [
282
+ "project-id"
283
+ ],
284
+ "description": "Existing Stack ID to use for the Blueprint",
285
+ "exclusive": [
286
+ "stack-name"
287
+ ],
288
+ "name": "stack-id",
289
+ "hasDynamicHelp": false,
290
+ "multiple": false,
291
+ "type": "option"
292
+ },
293
+ "stack-name": {
294
+ "aliases": [
295
+ "name"
296
+ ],
297
+ "char": "n",
298
+ "dependsOn": [
299
+ "project-id"
300
+ ],
301
+ "description": "Name to use for a NEW Stack",
302
+ "exclusive": [
303
+ "stack-id"
304
+ ],
305
+ "name": "stack-name",
306
+ "hasDynamicHelp": false,
307
+ "multiple": false,
308
+ "type": "option"
309
+ }
310
+ },
181
311
  "hasDynamicHelp": false,
182
312
  "hiddenAliases": [],
183
313
  "id": "blueprints:init",
@@ -571,6 +701,37 @@
571
701
  "add.js"
572
702
  ]
573
703
  },
704
+ "functions:env:list": {
705
+ "aliases": [],
706
+ "args": {
707
+ "name": {
708
+ "description": "The name of the Sanity Function",
709
+ "name": "name",
710
+ "required": true
711
+ }
712
+ },
713
+ "description": "List the environment variables for a Sanity function",
714
+ "examples": [
715
+ "<%= config.bin %> <%= command.id %> MyFunction"
716
+ ],
717
+ "flags": {},
718
+ "hasDynamicHelp": false,
719
+ "hiddenAliases": [],
720
+ "id": "functions:env:list",
721
+ "pluginAlias": "@sanity/runtime-cli",
722
+ "pluginName": "@sanity/runtime-cli",
723
+ "pluginType": "core",
724
+ "strict": true,
725
+ "enableJsonFlag": false,
726
+ "isESM": true,
727
+ "relativePath": [
728
+ "dist",
729
+ "commands",
730
+ "functions",
731
+ "env",
732
+ "list.js"
733
+ ]
734
+ },
574
735
  "functions:env:remove": {
575
736
  "aliases": [],
576
737
  "args": {
@@ -608,5 +769,5 @@
608
769
  ]
609
770
  }
610
771
  },
611
- "version": "4.1.0"
772
+ "version": "4.3.0"
612
773
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sanity/runtime-cli",
3
3
  "description": "Sanity's Runtime CLI for Blueprints and Functions",
4
- "version": "4.1.0",
4
+ "version": "4.3.0",
5
5
  "author": "Sanity Runtime Team",
6
6
  "type": "module",
7
7
  "license": "MIT",