@sanity/runtime-cli 11.1.1 → 11.1.3

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/11.1.1 linux-x64 node-v24.11.0
23
+ @sanity/runtime-cli/11.1.3 linux-x64 node-v24.11.0
24
24
  $ sanity-run --help [COMMAND]
25
25
  USAGE
26
26
  $ sanity-run COMMAND
@@ -92,7 +92,7 @@ EXAMPLES
92
92
  $ sanity-run blueprints add function --name my-function --fn-type document-create --fn-type document-update --lang js
93
93
  ```
94
94
 
95
- _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.1/src/commands/blueprints/add.ts)_
95
+ _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.3/src/commands/blueprints/add.ts)_
96
96
 
97
97
  ## `sanity-run blueprints config`
98
98
 
@@ -124,7 +124,7 @@ EXAMPLES
124
124
  $ sanity-run blueprints config --edit --project-id <projectId> --stack-id <stackId>
125
125
  ```
126
126
 
127
- _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.1/src/commands/blueprints/config.ts)_
127
+ _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.3/src/commands/blueprints/config.ts)_
128
128
 
129
129
  ## `sanity-run blueprints deploy`
130
130
 
@@ -146,7 +146,7 @@ EXAMPLES
146
146
  $ sanity-run blueprints deploy --no-wait
147
147
  ```
148
148
 
149
- _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.1/src/commands/blueprints/deploy.ts)_
149
+ _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.3/src/commands/blueprints/deploy.ts)_
150
150
 
151
151
  ## `sanity-run blueprints destroy`
152
152
 
@@ -173,7 +173,7 @@ EXAMPLES
173
173
  $ sanity-run blueprints destroy --stack-id <stackId> --project-id <projectId> --force --no-wait
174
174
  ```
175
175
 
176
- _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.1/src/commands/blueprints/destroy.ts)_
176
+ _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.3/src/commands/blueprints/destroy.ts)_
177
177
 
178
178
  ## `sanity-run blueprints doctor`
179
179
 
@@ -191,7 +191,7 @@ DESCRIPTION
191
191
  Diagnose potential issues with Blueprint configuration
192
192
  ```
193
193
 
194
- _See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.1/src/commands/blueprints/doctor.ts)_
194
+ _See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.3/src/commands/blueprints/doctor.ts)_
195
195
 
196
196
  ## `sanity-run blueprints info`
197
197
 
@@ -213,7 +213,7 @@ EXAMPLES
213
213
  $ sanity-run blueprints info --stack-id <stackId>
214
214
  ```
215
215
 
216
- _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.1/src/commands/blueprints/info.ts)_
216
+ _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.3/src/commands/blueprints/info.ts)_
217
217
 
218
218
  ## `sanity-run blueprints init [DIR]`
219
219
 
@@ -252,7 +252,7 @@ EXAMPLES
252
252
  $ sanity-run blueprints init --blueprint-type <json|js|ts> --stack-name <stackName>
253
253
  ```
254
254
 
255
- _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.1/src/commands/blueprints/init.ts)_
255
+ _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.3/src/commands/blueprints/init.ts)_
256
256
 
257
257
  ## `sanity-run blueprints logs`
258
258
 
@@ -274,7 +274,7 @@ EXAMPLES
274
274
  $ sanity-run blueprints logs --watch
275
275
  ```
276
276
 
277
- _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.1/src/commands/blueprints/logs.ts)_
277
+ _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.3/src/commands/blueprints/logs.ts)_
278
278
 
279
279
  ## `sanity-run blueprints plan`
280
280
 
@@ -291,7 +291,7 @@ EXAMPLES
291
291
  $ sanity-run blueprints plan
292
292
  ```
293
293
 
294
- _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.1/src/commands/blueprints/plan.ts)_
294
+ _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.3/src/commands/blueprints/plan.ts)_
295
295
 
296
296
  ## `sanity-run blueprints stacks`
297
297
 
@@ -316,7 +316,7 @@ EXAMPLES
316
316
  $ sanity-run blueprints stacks --organization-id <organizationId>
317
317
  ```
318
318
 
319
- _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.1/src/commands/blueprints/stacks.ts)_
319
+ _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.3/src/commands/blueprints/stacks.ts)_
320
320
 
321
321
  ## `sanity-run functions add`
322
322
 
@@ -358,7 +358,7 @@ EXAMPLES
358
358
  $ sanity-run functions add --name my-function --type document-create --type document-update --lang js
359
359
  ```
360
360
 
361
- _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.1/src/commands/functions/add.ts)_
361
+ _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.3/src/commands/functions/add.ts)_
362
362
 
363
363
  ## `sanity-run functions dev`
364
364
 
@@ -366,10 +366,11 @@ Start the Sanity Function emulator
366
366
 
367
367
  ```
368
368
  USAGE
369
- $ sanity-run functions dev [-p <value>] [-t <value>]
369
+ $ sanity-run functions dev [-h <value>] [-p <value>] [-t <value>]
370
370
 
371
371
  FLAGS
372
- -p, --port=<value> Port to start emulator on
372
+ -h, --host=<value> The local network interface at which to listen. [default: "localhost"]
373
+ -p, --port=<value> TCP port to start emulator on. [default: 8080]
373
374
  -t, --timeout=<value> Maximum execution time for all functions, in seconds. Takes precedence over function-specific
374
375
  `timeout`
375
376
 
@@ -377,10 +378,10 @@ DESCRIPTION
377
378
  Start the Sanity Function emulator
378
379
 
379
380
  EXAMPLES
380
- $ sanity-run functions dev --port 8974
381
+ $ sanity-run functions dev --host 127.0.0.1 --port 8974
381
382
  ```
382
383
 
383
- _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.1/src/commands/functions/dev.ts)_
384
+ _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.3/src/commands/functions/dev.ts)_
384
385
 
385
386
  ## `sanity-run functions env add NAME KEY VALUE`
386
387
 
@@ -402,7 +403,7 @@ EXAMPLES
402
403
  $ sanity-run functions env add MyFunction API_URL https://api.example.com/
403
404
  ```
404
405
 
405
- _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.1/src/commands/functions/env/add.ts)_
406
+ _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.3/src/commands/functions/env/add.ts)_
406
407
 
407
408
  ## `sanity-run functions env list NAME`
408
409
 
@@ -422,7 +423,7 @@ EXAMPLES
422
423
  $ sanity-run functions env list MyFunction
423
424
  ```
424
425
 
425
- _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.1/src/commands/functions/env/list.ts)_
426
+ _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.3/src/commands/functions/env/list.ts)_
426
427
 
427
428
  ## `sanity-run functions env remove NAME KEY`
428
429
 
@@ -443,7 +444,7 @@ EXAMPLES
443
444
  $ sanity-run functions env remove MyFunction API_URL
444
445
  ```
445
446
 
446
- _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.1/src/commands/functions/env/remove.ts)_
447
+ _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.3/src/commands/functions/env/remove.ts)_
447
448
 
448
449
  ## `sanity-run functions logs NAME`
449
450
 
@@ -477,7 +478,7 @@ EXAMPLES
477
478
  $ sanity-run functions logs <name> --delete
478
479
  ```
479
480
 
480
- _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.1/src/commands/functions/logs.ts)_
481
+ _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.3/src/commands/functions/logs.ts)_
481
482
 
482
483
  ## `sanity-run functions test NAME`
483
484
 
@@ -526,7 +527,7 @@ EXAMPLES
526
527
  $ sanity-run functions test <name> --event update --data-before '{ "title": "before" }' --data-after '{ "title": "after" }'
527
528
  ```
528
529
 
529
- _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.1/src/commands/functions/test.ts)_
530
+ _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.3/src/commands/functions/test.ts)_
530
531
 
531
532
  ## `sanity-run help [COMMAND]`
532
533
 
@@ -1,2 +1,2 @@
1
1
  import type { InvokeExecutionOptions } from '../../utils/types.js';
2
- export declare function dev(port: number, executionOptions?: Partial<InvokeExecutionOptions>): Promise<void>;
2
+ export declare function dev(host: string, port: number, executionOptions?: Partial<InvokeExecutionOptions>): Promise<void>;
@@ -1,4 +1,4 @@
1
1
  import { app } from '../../server/app.js';
2
- export async function dev(port, executionOptions) {
3
- app(Number(port), executionOptions);
2
+ export async function dev(host, port, executionOptions) {
3
+ app(host, Number(port), executionOptions);
4
4
  }
@@ -3,6 +3,7 @@ export default class DevCommand extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static flags: {
6
+ host: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
7
  port: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
8
  timeout: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
9
  };
@@ -2,9 +2,18 @@ import { Command, Flags } from '@oclif/core';
2
2
  import { functionDevCore } from '../../cores/functions/dev.js';
3
3
  export default class DevCommand extends Command {
4
4
  static description = 'Start the Sanity Function emulator';
5
- static examples = ['<%= config.bin %> <%= command.id %> --port 8974'];
5
+ static examples = ['<%= config.bin %> <%= command.id %> --host 127.0.0.1 --port 8974'];
6
6
  static flags = {
7
- port: Flags.integer({ char: 'p', description: 'Port to start emulator on', required: false }),
7
+ host: Flags.string({
8
+ char: 'h',
9
+ description: 'The local network interface at which to listen. [default: "localhost"]',
10
+ required: false,
11
+ }),
12
+ port: Flags.integer({
13
+ char: 'p',
14
+ description: 'TCP port to start emulator on. [default: 8080]',
15
+ required: false,
16
+ }),
8
17
  timeout: Flags.integer({
9
18
  char: 't',
10
19
  description: 'Maximum execution time for all functions, in seconds. Takes precedence over function-specific `timeout`',
@@ -1,3 +1,4 @@
1
+ import type { ScopeType } from '../../utils/types.js';
1
2
  import type { CoreConfig, CoreResult } from '../index.js';
2
3
  export interface BlueprintInitOptions extends CoreConfig {
3
4
  token: string;
@@ -15,3 +16,37 @@ export interface BlueprintInitOptions extends CoreConfig {
15
16
  };
16
17
  }
17
18
  export declare function blueprintInitCore(options: BlueprintInitOptions): Promise<CoreResult>;
19
+ export declare function validateFlags(flags: {
20
+ stackId?: string;
21
+ stackName?: string;
22
+ organizationId?: string;
23
+ projectId?: string;
24
+ }): CoreResult | null;
25
+ interface ResolvedScope {
26
+ scopeType: ScopeType;
27
+ scopeId: string;
28
+ stackId: string | undefined;
29
+ }
30
+ export declare function resolveScopeAndStack(params: {
31
+ projectId: string | undefined;
32
+ organizationId: string | undefined;
33
+ stackId: string | undefined;
34
+ stackName: string | undefined;
35
+ token: string;
36
+ log: (message: string) => void;
37
+ }): Promise<ResolvedScope>;
38
+ export declare function determineBlueprintExtension(params: {
39
+ requestedType: string | undefined;
40
+ blueprintDir: string;
41
+ }): Promise<string>;
42
+ export declare function createBlueprintFiles(params: {
43
+ blueprintDir: string;
44
+ userProvidedDirName: string | undefined;
45
+ blueprintExtension: string;
46
+ scopeType: ScopeType;
47
+ scopeId: string;
48
+ stackId: string | undefined;
49
+ bin: string;
50
+ log: (message: string) => void;
51
+ }): Promise<CoreResult>;
52
+ export {};
@@ -11,149 +11,60 @@ import { verifyExampleExists, writeExample } from '../../actions/sanity/examples
11
11
  import { getProject } from '../../actions/sanity/projects.js';
12
12
  import { BLUEPRINT_CONFIG_FILE, BLUEPRINT_DIR } from '../../config.js';
13
13
  import { check, warn } from '../../utils/display/presenters.js';
14
- import { promptForProject, promptForStackId } from '../../utils/display/prompt.js';
15
- const LAUNCH_LIMIT_STACK_PER_PROJECT = true;
14
+ import { promptForProject } from '../../utils/display/prompt.js';
15
+ const SCOPE_PROJECT = 'project';
16
+ const SCOPE_ORGANIZATION = 'organization';
16
17
  export async function blueprintInitCore(options) {
17
18
  const { bin = 'sanity', log, token, args, flags } = options;
18
19
  try {
19
- const { dir: flagDir, example: flagExample, 'blueprint-type': flagBlueprintType, 'project-id': flagProjectId,
20
- // 'organization-id': flagOrganizationId,
21
- 'stack-id': flagStackId, 'stack-name': flagStackName, } = flags;
20
+ const { dir: flagDir, example: flagExample, 'blueprint-type': flagBlueprintType, 'project-id': flagProjectId, 'organization-id': flagOrganizationId, 'stack-id': flagStackId, 'stack-name': flagStackName, } = flags;
22
21
  const { dir: argDir } = args;
23
- const providedDir = argDir || flagDir;
24
- const blueprintDir = providedDir || '.';
22
+ const userProvidedDirName = argDir || flagDir;
23
+ const blueprintDir = userProvidedDirName || '.';
25
24
  const existingBlueprint = findBlueprintFile(blueprintDir);
26
25
  if (existingBlueprint) {
27
26
  return { success: false, error: 'Existing Blueprint found.' };
28
27
  }
28
+ const validationError = validateFlags({
29
+ stackId: flagStackId,
30
+ stackName: flagStackName,
31
+ organizationId: flagOrganizationId,
32
+ projectId: flagProjectId,
33
+ });
34
+ if (validationError)
35
+ return validationError;
29
36
  if (flagExample) {
30
- // ! short circuit for examples
31
- log(warn(`Example feature is experimental. Setting up "${flagExample}"...`));
32
- // we need to...
33
- // * 1. verify example exists in the recipes repo
34
- const exampleExists = await verifyExampleExists({ type: 'blueprint', name: flagExample });
35
- if (!exampleExists) {
36
- return { success: false, error: `Blueprint example "${flagExample}" does not exist.` };
37
- }
38
- // * 2. get a projectId from the user
39
- const projectId = flagProjectId || (await promptForProject({ token })).projectId;
40
- // * 3. create empty stack with name from example name
41
- const stack = await createEmptyStack({
42
- token,
43
- scopeType: 'project',
44
- scopeId: projectId,
45
- name: `example-${flagExample}`,
46
- projectBased: false,
47
- });
48
- // * 4. download and write example to disk
49
- // take into account optional providedDir
50
- const exampleDir = join(blueprintDir, providedDir ? '' : flagExample);
51
- if (existsSync(exampleDir)) {
52
- return { success: false, error: `Example directory "${exampleDir}" already exists.` };
53
- }
54
- const addedExample = await writeExample({
55
- exampleType: 'blueprint',
37
+ return handleExampleInitialization({
56
38
  exampleName: flagExample,
57
- dir: exampleDir,
58
- });
59
- if (!addedExample) {
60
- return { success: false, error: `Unable to download example "${flagExample}"` };
61
- }
62
- const { files, dir: newDir, instructions } = addedExample;
63
- for (const filePath of Object.keys(files)) {
64
- log(check(`${chalk.bold('Created:')} ${newDir}/${filePath}`));
65
- }
66
- const discoveredBlueprint = findBlueprintFile(exampleDir);
67
- if (!discoveredBlueprint) {
68
- return { success: false, error: 'Failed to find blueprint file.' };
69
- }
70
- const { blueprintFilePath } = discoveredBlueprint;
71
- // * 5. write config file
72
- writeConfigFile({ blueprintFilePath, projectId, stackId: stack.id });
73
- log(check(`${chalk.bold('Configured:')} ${exampleDir}/${BLUEPRINT_DIR}/${BLUEPRINT_CONFIG_FILE}`));
74
- // * 6. print next step
75
- log(`\n Run "${chalk.bold.magenta(`cd ${exampleDir} && npm i`)}" and check out the README`);
76
- if (instructions) {
77
- log('');
78
- log(instructions);
79
- }
80
- return { success: true };
81
- }
82
- let blueprintExtension = flagBlueprintType || (await promptForBlueprintType());
83
- if (!blueprintExtension) {
84
- return { success: false, error: 'Blueprint type is required.' };
85
- }
86
- if (blueprintExtension === 'js') {
87
- const packageJsonPath = join(blueprintDir, 'package.json');
88
- const packageExists = existsSync(packageJsonPath);
89
- if (packageExists) {
90
- // if package.json#type is not module, set blueprintExtension to mjs
91
- try {
92
- const packageJson = readFileSync(packageJsonPath, 'utf8');
93
- const packageJsonObject = JSON.parse(packageJson);
94
- if (packageJsonObject.type !== 'module') {
95
- blueprintExtension = 'mjs';
96
- }
97
- }
98
- catch { } // not our concern
99
- }
100
- }
101
- let projectId = flagProjectId;
102
- let stackId = flagStackId;
103
- if (!projectId) {
104
- const pickedProject = await promptForProject({ token });
105
- projectId = pickedProject.projectId;
106
- }
107
- log('');
108
- if (flagStackName) {
109
- // using --stack-name gets around "LAUNCH LIMIT: 1 Stack per Project"
110
- const stack = await createEmptyStack({
39
+ projectId: flagProjectId,
40
+ blueprintDir,
41
+ userProvidedDirName,
111
42
  token,
112
- scopeType: 'project',
113
- scopeId: projectId,
114
- name: flagStackName,
115
- projectBased: false,
43
+ log,
116
44
  });
117
- stackId = stack.id;
118
- }
119
- if (!stackId) {
120
- // LAUNCH LIMIT: 1 Stack per Project - do not prompt for Stack, just create one
121
- if (LAUNCH_LIMIT_STACK_PER_PROJECT) {
122
- await createProjectBasedStack({ token, scopeType: 'project', scopeId: projectId }, log);
123
- // do not set stackId, to avoid saving it to the config file
124
- }
125
- else {
126
- stackId = await promptForStackId({ projectId, token });
127
- }
128
- }
129
- const blueprintFileName = `sanity.blueprint.${blueprintExtension}`;
130
- const blueprintFilePath = join(blueprintDir, blueprintFileName);
131
- writeBlueprintToDisk({ blueprintFilePath });
132
- if (providedDir)
133
- log(check(`${chalk.bold('New folder created:')} ${providedDir}/`));
134
- log(check(`${chalk.bold('Created Blueprint:')} ${providedDir ?? '.'}/${blueprintFileName}`));
135
- writeConfigFile({ blueprintFilePath, projectId, stackId });
136
- log(check(`${chalk.bold('Added configuration:')} ${providedDir ?? '.'}/${BLUEPRINT_DIR}/${BLUEPRINT_CONFIG_FILE}`));
137
- writeGitignoreFile(blueprintFilePath);
138
- log(check(`${chalk.bold('Added .gitignore:')} ${providedDir ?? '.'}/.gitignore`));
139
- if (blueprintExtension !== 'json') {
140
- try {
141
- // check for || create package.json and add @sanity/blueprints to dependencies
142
- await writeOrUpdateNodeDependency(blueprintFilePath, '@sanity/blueprints');
143
- log(check(`${chalk.bold('Added dependency:')} @sanity/blueprints`));
144
- }
145
- catch {
146
- log(warn('Unable to add @sanity/blueprints to your project.'));
147
- }
148
45
  }
149
- const nextStepParts = [];
150
- if (providedDir)
151
- nextStepParts.push(`cd ${providedDir}`);
152
- if (blueprintExtension !== 'json')
153
- nextStepParts.push('npm install');
154
- nextStepParts.push(`${bin} blueprints --help`);
155
- log(`\n Run "${chalk.bold.magenta(nextStepParts.join(' && '))}" to get started`);
156
- return { success: true };
46
+ const { scopeType, scopeId, stackId } = await resolveScopeAndStack({
47
+ projectId: flagProjectId,
48
+ organizationId: flagOrganizationId,
49
+ stackId: flagStackId,
50
+ stackName: flagStackName,
51
+ token,
52
+ log,
53
+ });
54
+ const blueprintExtension = await determineBlueprintExtension({
55
+ requestedType: flagBlueprintType,
56
+ blueprintDir,
57
+ });
58
+ return createBlueprintFiles({
59
+ blueprintDir,
60
+ userProvidedDirName,
61
+ blueprintExtension,
62
+ scopeType,
63
+ scopeId,
64
+ stackId,
65
+ bin,
66
+ log,
67
+ });
157
68
  }
158
69
  catch (error) {
159
70
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -161,6 +72,158 @@ export async function blueprintInitCore(options) {
161
72
  return { success: false, error: errorMessage };
162
73
  }
163
74
  }
75
+ export function validateFlags(flags) {
76
+ const { stackId, stackName, organizationId, projectId } = flags;
77
+ if (stackId && stackName) {
78
+ return { success: false, error: 'Cannot specify both --stack-id and --stack-name' };
79
+ }
80
+ if (organizationId && projectId) {
81
+ return { success: false, error: 'Cannot specify both --organization-id and --project-id' };
82
+ }
83
+ return null;
84
+ }
85
+ async function handleExampleInitialization(options) {
86
+ const { exampleName, blueprintDir, userProvidedDirName, projectId, token, log } = options;
87
+ log(warn(`Example feature is experimental. Setting up "${exampleName}"...`));
88
+ const exampleExists = await verifyExampleExists({ type: 'blueprint', name: exampleName });
89
+ if (!exampleExists) {
90
+ return { success: false, error: `Blueprint example "${exampleName}" does not exist.` };
91
+ }
92
+ const resolvedProjectId = projectId || (await promptForProject({ token })).projectId;
93
+ const stack = await createEmptyStack({
94
+ token,
95
+ scopeType: SCOPE_PROJECT,
96
+ scopeId: resolvedProjectId,
97
+ name: `example-${exampleName}`,
98
+ projectBased: false,
99
+ });
100
+ const exampleDir = userProvidedDirName || join(blueprintDir, exampleName);
101
+ if (existsSync(exampleDir)) {
102
+ return { success: false, error: `Example directory "${exampleDir}" already exists.` };
103
+ }
104
+ const addedExample = await writeExample({
105
+ exampleType: 'blueprint',
106
+ exampleName,
107
+ dir: exampleDir,
108
+ });
109
+ if (!addedExample) {
110
+ return { success: false, error: `Unable to download example "${exampleName}"` };
111
+ }
112
+ const { files, dir: newDir, instructions } = addedExample;
113
+ for (const filePath of Object.keys(files)) {
114
+ log(check(`${chalk.bold('Created:')} ${newDir}/${filePath}`));
115
+ }
116
+ const discoveredBlueprint = findBlueprintFile(exampleDir);
117
+ if (!discoveredBlueprint) {
118
+ return { success: false, error: 'Failed to find blueprint file.' };
119
+ }
120
+ const { blueprintFilePath } = discoveredBlueprint;
121
+ writeConfigFile({ blueprintFilePath, projectId: resolvedProjectId, stackId: stack.id });
122
+ log(check(`${chalk.bold('Configured:')} ${exampleDir}/${BLUEPRINT_DIR}/${BLUEPRINT_CONFIG_FILE}`));
123
+ log(`\n Run "${chalk.bold.magenta(`cd ${exampleDir} && npm i`)}" and check out the README`);
124
+ if (instructions) {
125
+ log('');
126
+ log(instructions);
127
+ }
128
+ return { success: true };
129
+ }
130
+ export async function resolveScopeAndStack(params) {
131
+ const { projectId, organizationId, stackId, stackName, token, log } = params;
132
+ let scopeType = SCOPE_PROJECT;
133
+ let scopeId;
134
+ if (projectId) {
135
+ scopeType = SCOPE_PROJECT;
136
+ scopeId = projectId;
137
+ }
138
+ else if (organizationId) {
139
+ scopeType = SCOPE_ORGANIZATION;
140
+ scopeId = organizationId;
141
+ }
142
+ let resolvedStackId = stackId;
143
+ if (!resolvedStackId && stackName && scopeType && scopeId) {
144
+ // essentially the only way to create an org-scoped stack
145
+ const stack = await createEmptyStack({
146
+ token,
147
+ scopeType,
148
+ scopeId,
149
+ name: stackName,
150
+ projectBased: false,
151
+ });
152
+ resolvedStackId = stack.id;
153
+ }
154
+ if (!scopeId) {
155
+ const pickedProject = await promptForProject({ token });
156
+ scopeType = SCOPE_PROJECT;
157
+ scopeId = pickedProject.projectId;
158
+ }
159
+ if (!resolvedStackId) {
160
+ await getOrCreateProjectBasedStack({ token, projectId: scopeId, log });
161
+ }
162
+ return {
163
+ scopeType,
164
+ scopeId,
165
+ stackId: resolvedStackId,
166
+ };
167
+ }
168
+ export async function determineBlueprintExtension(params) {
169
+ const { requestedType, blueprintDir } = params;
170
+ const extension = requestedType || (await promptForBlueprintType());
171
+ if (extension === 'js') {
172
+ const packageJsonPath = join(blueprintDir, 'package.json');
173
+ const packageExists = existsSync(packageJsonPath);
174
+ if (packageExists) {
175
+ try {
176
+ const packageJson = readFileSync(packageJsonPath, 'utf8');
177
+ const packageJsonObject = JSON.parse(packageJson);
178
+ if (packageJsonObject.type !== 'module') {
179
+ return 'mjs';
180
+ }
181
+ }
182
+ catch { }
183
+ }
184
+ }
185
+ return extension;
186
+ }
187
+ export async function createBlueprintFiles(params) {
188
+ const { blueprintDir, userProvidedDirName, blueprintExtension, scopeType, scopeId, stackId, bin, log, } = params;
189
+ if (!blueprintExtension) {
190
+ return { success: false, error: 'Blueprint type is required.' };
191
+ }
192
+ const blueprintFileName = `sanity.blueprint.${blueprintExtension}`;
193
+ const blueprintFilePath = join(blueprintDir, blueprintFileName);
194
+ writeBlueprintToDisk({ blueprintFilePath });
195
+ if (userProvidedDirName) {
196
+ log(check(`${chalk.bold('New folder created:')} ${userProvidedDirName}/`));
197
+ }
198
+ const displayPath = userProvidedDirName || '.';
199
+ log(check(`${chalk.bold('Created Blueprint:')} ${displayPath}/${blueprintFileName}`));
200
+ writeConfigFile({
201
+ blueprintFilePath,
202
+ stackId,
203
+ ...(scopeType === SCOPE_ORGANIZATION ? { organizationId: scopeId } : { projectId: scopeId }),
204
+ });
205
+ log(check(`${chalk.bold('Added configuration:')} ${displayPath}/${BLUEPRINT_DIR}/${BLUEPRINT_CONFIG_FILE}`));
206
+ writeGitignoreFile(blueprintFilePath);
207
+ log(check(`${chalk.bold('Added .gitignore:')} ${displayPath}/.gitignore`));
208
+ if (blueprintExtension !== 'json') {
209
+ const blueprintsPackage = '@sanity/blueprints';
210
+ try {
211
+ await writeOrUpdateNodeDependency(blueprintFilePath, blueprintsPackage);
212
+ log(check(`${chalk.bold('Added dependency:')} ${blueprintsPackage}`));
213
+ }
214
+ catch {
215
+ log(warn(`Unable to add ${blueprintsPackage} to your project.`));
216
+ }
217
+ }
218
+ const nextStepParts = [];
219
+ if (userProvidedDirName)
220
+ nextStepParts.push(`cd ${userProvidedDirName}`);
221
+ if (blueprintExtension !== 'json')
222
+ nextStepParts.push('npm install');
223
+ nextStepParts.push(`${bin} blueprints --help`);
224
+ log(`\n Run "${chalk.bold.magenta(nextStepParts.join(' && '))}" to get started`);
225
+ return { success: true };
226
+ }
164
227
  async function promptForBlueprintType() {
165
228
  const { pickedBlueprintsType } = await inquirer.prompt([
166
229
  {
@@ -178,27 +241,24 @@ async function promptForBlueprintType() {
178
241
  return pickedBlueprintsType;
179
242
  }
180
243
  // LAUNCH LIMIT: 1 Stack per Project - create exclusive stack for project
181
- async function createProjectBasedStack(auth, log) {
182
- if (auth.scopeType !== 'project') {
183
- throw new Error('Auth must be for a project');
184
- }
185
- const { scopeId: projectId, token } = auth;
186
- // get project
244
+ async function getOrCreateProjectBasedStack(params) {
245
+ const { projectId, token, log } = params;
187
246
  const { ok: projectOk, project } = await getProject({
188
- token: auth.token,
189
- scopeType: 'project',
247
+ token,
248
+ scopeType: SCOPE_PROJECT,
190
249
  scopeId: projectId,
191
250
  });
192
251
  if (!projectOk) {
193
252
  throw new Error('Failed to find Project while creating Stack');
194
253
  }
195
- const projectDisplayName = project.displayName;
196
254
  // check if project has a stack
197
- const inferredStackId = `ST-${projectId}`;
198
- const { stack: existingStack, ok: stackOk } = await getStack({ stackId: inferredStackId, auth });
255
+ const { stack: existingStack, ok: stackOk } = await getStack({
256
+ auth: { token, scopeType: SCOPE_PROJECT, scopeId: projectId },
257
+ stackId: `ST-${projectId}`,
258
+ });
199
259
  // if existing stack, return stack
200
260
  if (stackOk && existingStack) {
201
- log(warn(`"${projectDisplayName}" has an existing deployment.`));
261
+ log(warn(`"${project.displayName}" has an existing deployment.`));
202
262
  log(warn(`Deploying an empty Blueprint ${chalk.bold.red('will override the existing deployment!')}`));
203
263
  log('');
204
264
  return existingStack;
@@ -206,9 +266,9 @@ async function createProjectBasedStack(auth, log) {
206
266
  // if not, create a stack
207
267
  const stack = await createEmptyStack({
208
268
  token,
209
- scopeType: 'project',
269
+ scopeType: SCOPE_PROJECT,
210
270
  scopeId: projectId,
211
- name: projectDisplayName,
271
+ name: project.displayName,
212
272
  });
213
273
  return stack;
214
274
  }
@@ -1,6 +1,7 @@
1
1
  import type { CoreConfig, CoreResult } from '../index.js';
2
2
  export interface FunctionDevOptions extends CoreConfig {
3
3
  flags: {
4
+ host?: string;
4
5
  port?: number;
5
6
  timeout?: number;
6
7
  };
@@ -1,14 +1,14 @@
1
1
  import { dev } from '../../actions/functions/dev.js';
2
2
  export async function functionDevCore(options) {
3
3
  const { log, flags } = options;
4
- const { port = 8080, timeout } = flags;
4
+ const { host = 'localhost', port = 8080, timeout } = flags;
5
5
  // Construct execution options only if timeout is provided
6
6
  const executionOptions = timeout
7
7
  ? { timeout }
8
8
  : undefined;
9
9
  try {
10
- await dev(Number(port), executionOptions);
11
- log(`Server is running on http://localhost:${port}\n`);
10
+ await dev(host, Number(port), executionOptions);
11
+ log(`Server is running on http://${host}:${port}\n`);
12
12
  return {
13
13
  success: true,
14
14
  // hold the line...
@@ -1,5 +1,5 @@
1
1
  import { type InvokeExecutionOptions } from '../utils/types.js';
2
- declare const app: (port: number, executionOptions?: Partial<InvokeExecutionOptions>) => void;
2
+ declare const app: (host: string, port: number, executionOptions?: Partial<InvokeExecutionOptions>) => void;
3
3
  declare function parseDocumentUrl(url: string): {
4
4
  projectId: string;
5
5
  dataset: string;
@@ -7,8 +7,7 @@ import config from '../config.js';
7
7
  import { isRecord } from '../utils/is-record.js';
8
8
  import { isEventType, } from '../utils/types.js';
9
9
  import { handleInvokeRequest } from './handlers/invoke.js';
10
- const host = 'localhost';
11
- const app = (port, executionOptions) => {
10
+ const app = (host, port, executionOptions) => {
12
11
  const requestListener = async (req, res) => {
13
12
  res.setHeader('Content-Type', 'application/json');
14
13
  switch (true) {
@@ -866,12 +866,21 @@
866
866
  "args": {},
867
867
  "description": "Start the Sanity Function emulator",
868
868
  "examples": [
869
- "<%= config.bin %> <%= command.id %> --port 8974"
869
+ "<%= config.bin %> <%= command.id %> --host 127.0.0.1 --port 8974"
870
870
  ],
871
871
  "flags": {
872
+ "host": {
873
+ "char": "h",
874
+ "description": "The local network interface at which to listen. [default: \"localhost\"]",
875
+ "name": "host",
876
+ "required": false,
877
+ "hasDynamicHelp": false,
878
+ "multiple": false,
879
+ "type": "option"
880
+ },
872
881
  "port": {
873
882
  "char": "p",
874
- "description": "Port to start emulator on",
883
+ "description": "TCP port to start emulator on. [default: 8080]",
875
884
  "name": "port",
876
885
  "required": false,
877
886
  "hasDynamicHelp": false,
@@ -1408,5 +1417,5 @@
1408
1417
  ]
1409
1418
  }
1410
1419
  },
1411
- "version": "11.1.1"
1420
+ "version": "11.1.3"
1412
1421
  }
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": "11.1.1",
4
+ "version": "11.1.3",
5
5
  "author": "Sanity Runtime Team",
6
6
  "type": "module",
7
7
  "license": "MIT",