@sanity/runtime-cli 1.4.2 → 1.6.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 COMMAND
21
21
  running command...
22
22
  $ sanity (--version)
23
- @sanity/runtime-cli/1.4.2 linux-x64 node-v22.14.0
23
+ @sanity/runtime-cli/1.6.0 linux-x64 node-v22.14.0
24
24
  $ sanity --help [COMMAND]
25
25
  USAGE
26
26
  $ sanity COMMAND
@@ -65,7 +65,7 @@ EXAMPLES
65
65
  $ sanity blueprints deploy
66
66
  ```
67
67
 
68
- _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v1.4.2/src/commands/blueprints/deploy.ts)_
68
+ _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v1.6.0/src/commands/blueprints/deploy.ts)_
69
69
 
70
70
  ## `sanity blueprints info`
71
71
 
@@ -87,7 +87,7 @@ EXAMPLES
87
87
  $ sanity blueprints info --id abc123
88
88
  ```
89
89
 
90
- _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v1.4.2/src/commands/blueprints/info.ts)_
90
+ _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v1.6.0/src/commands/blueprints/info.ts)_
91
91
 
92
92
  ## `sanity blueprints logs`
93
93
 
@@ -109,7 +109,7 @@ EXAMPLES
109
109
  $ sanity blueprints logs --watch
110
110
  ```
111
111
 
112
- _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v1.4.2/src/commands/blueprints/logs.ts)_
112
+ _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v1.6.0/src/commands/blueprints/logs.ts)_
113
113
 
114
114
  ## `sanity blueprints plan`
115
115
 
@@ -126,7 +126,7 @@ EXAMPLES
126
126
  $ sanity blueprints plan
127
127
  ```
128
128
 
129
- _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v1.4.2/src/commands/blueprints/plan.ts)_
129
+ _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v1.6.0/src/commands/blueprints/plan.ts)_
130
130
 
131
131
  ## `sanity blueprints stacks`
132
132
 
@@ -143,7 +143,7 @@ EXAMPLES
143
143
  $ sanity blueprints stacks
144
144
  ```
145
145
 
146
- _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v1.4.2/src/commands/blueprints/stacks.ts)_
146
+ _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v1.6.0/src/commands/blueprints/stacks.ts)_
147
147
 
148
148
  ## `sanity functions dev`
149
149
 
@@ -163,7 +163,7 @@ EXAMPLES
163
163
  $ sanity functions dev --port 8974
164
164
  ```
165
165
 
166
- _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v1.4.2/src/commands/functions/dev.ts)_
166
+ _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v1.6.0/src/commands/functions/dev.ts)_
167
167
 
168
168
  ## `sanity functions invoke ID`
169
169
 
@@ -189,7 +189,7 @@ EXAMPLES
189
189
  $ sanity functions invoke <ID> --file 'payload.json'
190
190
  ```
191
191
 
192
- _See code: [src/commands/functions/invoke.ts](https://github.com/sanity-io/runtime-cli/blob/v1.4.2/src/commands/functions/invoke.ts)_
192
+ _See code: [src/commands/functions/invoke.ts](https://github.com/sanity-io/runtime-cli/blob/v1.6.0/src/commands/functions/invoke.ts)_
193
193
 
194
194
  ## `sanity functions logs ID`
195
195
 
@@ -209,7 +209,7 @@ EXAMPLES
209
209
  $ sanity functions logs <ID>
210
210
  ```
211
211
 
212
- _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v1.4.2/src/commands/functions/logs.ts)_
212
+ _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v1.6.0/src/commands/functions/logs.ts)_
213
213
 
214
214
  ## `sanity functions test PATH`
215
215
 
@@ -238,7 +238,7 @@ EXAMPLES
238
238
  $ sanity functions test ./test.ts --data '{ "id": 1 }' --timeout 60
239
239
  ```
240
240
 
241
- _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v1.4.2/src/commands/functions/test.ts)_
241
+ _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v1.6.0/src/commands/functions/test.ts)_
242
242
 
243
243
  ## `sanity help [COMMAND]`
244
244
 
@@ -0,0 +1,26 @@
1
+ import type { Blueprint, BlueprintError, BlueprintStack } from '../../utils/types.js';
2
+ export declare function readBlueprintOnDisk({ blueprintPath, getStack, }?: {
3
+ blueprintPath?: string;
4
+ getStack?: boolean;
5
+ }): Promise<{
6
+ fileInfo: {
7
+ path: string;
8
+ fileName: string;
9
+ extension: string;
10
+ };
11
+ parsedBlueprint: Blueprint;
12
+ errors: BlueprintError[];
13
+ projectId?: string;
14
+ stackId?: string;
15
+ deployedStack?: BlueprintStack;
16
+ }>;
17
+ /** @experimental */ export declare function updateBlueprintMetadata({ blueprintPath, projectId, stackId, }: {
18
+ blueprintPath?: string;
19
+ projectId: string;
20
+ stackId: string;
21
+ }): Promise<void>;
22
+ export declare function writeBlueprintConfig({ blueprintPath, projectId, stackId, }: {
23
+ blueprintPath?: string;
24
+ projectId: string;
25
+ stackId: string;
26
+ }): void;
@@ -0,0 +1,199 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import { dirname, extname, join } from 'node:path';
4
+ import { cwd } from 'node:process';
5
+ import { BlueprintErrorType } from '../../utils/types.js';
6
+ // @ts-ignore - this is currently untyped
7
+ import blueprintParserValidator from '../../utils/vendor/parser-validator.js';
8
+ import { getStack as getStackById } from './stacks.js';
9
+ const SUPPORTED_FILE_NAMES_IN_PRIORITY_ORDER = [
10
+ 'blueprint.json',
11
+ 'blueprint.js',
12
+ 'blueprint.mjs',
13
+ 'blueprint.cjs',
14
+ 'blueprint.ts',
15
+ ];
16
+ function findBlueprintFile(blueprintPath) {
17
+ if (blueprintPath) {
18
+ if (existsSync(blueprintPath)) {
19
+ return { path: blueprintPath, fileName: blueprintPath, extension: extname(blueprintPath) };
20
+ }
21
+ throw Error(`Blueprint file not found: ${blueprintPath}`);
22
+ }
23
+ for (const fileName of SUPPORTED_FILE_NAMES_IN_PRIORITY_ORDER) {
24
+ const filePath = join(cwd(), fileName);
25
+ if (existsSync(filePath)) {
26
+ return { path: filePath, fileName, extension: extname(filePath) };
27
+ }
28
+ }
29
+ return null;
30
+ }
31
+ function readConfigFile(blueprintPath) {
32
+ if (blueprintPath) {
33
+ const blueprintDir = dirname(blueprintPath);
34
+ const configPath = join(blueprintDir, '.blueprint', 'config.json');
35
+ if (existsSync(configPath)) {
36
+ try {
37
+ const config = JSON.parse(readFileSync(configPath, 'utf8'));
38
+ return config || null;
39
+ }
40
+ catch (err) {
41
+ return null;
42
+ }
43
+ }
44
+ }
45
+ const configFilePath = join(cwd(), '.blueprint', 'config.json');
46
+ if (!existsSync(configFilePath))
47
+ return null;
48
+ try {
49
+ const config = JSON.parse(readFileSync(configFilePath, 'utf8'));
50
+ return config || null;
51
+ }
52
+ catch (err) {
53
+ return null;
54
+ }
55
+ }
56
+ export async function readBlueprintOnDisk({ blueprintPath, getStack, } = {}) {
57
+ try {
58
+ const blueprintFile = findBlueprintFile(blueprintPath);
59
+ if (!blueprintFile)
60
+ throw Error('Could not find Blueprint file! Use the init command.');
61
+ const { path, fileName, extension } = blueprintFile;
62
+ let blueprintString;
63
+ let blueprintJson;
64
+ let blueprintModule;
65
+ switch (extension) {
66
+ case '.json': {
67
+ blueprintString = readFileSync(path, 'utf8').toString();
68
+ blueprintJson = JSON.parse(blueprintString);
69
+ break;
70
+ }
71
+ case '.js':
72
+ case '.mjs': {
73
+ const module = await import(path);
74
+ blueprintModule = module.default;
75
+ break;
76
+ }
77
+ case '.cjs': {
78
+ const require = createRequire(import.meta.url);
79
+ blueprintModule = require(path);
80
+ break;
81
+ }
82
+ case '.ts': {
83
+ console.warn('\x1b[2mSupport for Typescript blueprints is experimental.\x1b[0m');
84
+ try {
85
+ const { tsImport } = await import('tsx/esm/api');
86
+ const module = await tsImport(path, dirname(path));
87
+ blueprintModule = module.default;
88
+ }
89
+ catch (err) {
90
+ if (err instanceof Error && err.message.includes('Cannot find module')) {
91
+ throw Error(`TypeScript support requires 'tsx' to be installed. Run: npm install -D tsx`);
92
+ }
93
+ throw err;
94
+ }
95
+ break;
96
+ }
97
+ default:
98
+ throw Error(`Unsupported blueprint file extension: ${extension}`);
99
+ }
100
+ if (blueprintModule) {
101
+ if (typeof blueprintModule !== 'function')
102
+ throw Error(`Blueprint ${fileName} must export a default function`);
103
+ blueprintJson = blueprintModule();
104
+ }
105
+ const parserResult = blueprintParserValidator(blueprintJson);
106
+ const { blueprint: parsedBlueprint } = parserResult;
107
+ const errors = parserResult.errors || [];
108
+ // find project and stack IDs from .blueprint/config.json first,
109
+ // then fallback to metadata in blueprint file,
110
+ // finally fallback to resources array...
111
+ let projectId;
112
+ let stackId;
113
+ const configIds = readConfigFile(blueprintPath);
114
+ const blueprintMetadata = parsedBlueprint.metadata;
115
+ // find/create project resource
116
+ if (configIds?.projectId) {
117
+ projectId = configIds.projectId;
118
+ }
119
+ else if (blueprintMetadata?.projectId) {
120
+ projectId = blueprintMetadata.projectId;
121
+ }
122
+ else {
123
+ const projectResource = parsedBlueprint.resources.find((r) => r.type === 'sanity.project');
124
+ if (projectResource)
125
+ projectId = projectResource.id;
126
+ }
127
+ // find/create stack resource
128
+ if (configIds?.stackId) {
129
+ stackId = configIds.stackId;
130
+ }
131
+ else if (blueprintMetadata?.stackId) {
132
+ stackId = blueprintMetadata.stackId;
133
+ }
134
+ else {
135
+ const stackResource = parsedBlueprint.resources.find((r) => r.type === 'sanity.blueprints.stack');
136
+ if (stackResource)
137
+ stackId = stackResource.id;
138
+ }
139
+ let deployedStack;
140
+ if (getStack && projectId && stackId) {
141
+ const { stack } = await getStackById({ stackId, projectId });
142
+ if (!stack) {
143
+ errors.push({
144
+ message: 'Stack not found',
145
+ type: BlueprintErrorType.InvalidStack,
146
+ });
147
+ }
148
+ deployedStack = stack;
149
+ }
150
+ return {
151
+ fileInfo: { path, fileName, extension },
152
+ errors,
153
+ projectId,
154
+ stackId,
155
+ deployedStack,
156
+ parsedBlueprint,
157
+ };
158
+ }
159
+ catch (err) {
160
+ throw Error(`Unable to parse Blueprint file: ${err}`);
161
+ }
162
+ }
163
+ /** @experimental */ // no, that's not a real JSDoc pragma
164
+ export async function updateBlueprintMetadata({ blueprintPath, projectId, stackId, }) {
165
+ const blueprintFile = findBlueprintFile(blueprintPath);
166
+ if (!blueprintFile)
167
+ throw Error('Could not find Blueprint file');
168
+ const { path } = blueprintFile;
169
+ const blueprintString = readFileSync(path, 'utf8').toString();
170
+ const blueprint = JSON.parse(blueprintString);
171
+ blueprint.metadata = blueprint.metadata || {};
172
+ blueprint.metadata.projectId = projectId;
173
+ blueprint.metadata.stackId = stackId;
174
+ writeFileSync(path, JSON.stringify(blueprint, null, 2));
175
+ }
176
+ export function writeBlueprintConfig({ blueprintPath, projectId, stackId, }) {
177
+ const blueprintFile = findBlueprintFile(blueprintPath);
178
+ if (!blueprintFile)
179
+ throw Error('Could not find Blueprint file');
180
+ const { path } = blueprintFile;
181
+ const blueprintDir = dirname(path);
182
+ const configDir = join(blueprintDir, '.blueprint');
183
+ const configPath = join(configDir, 'config.json');
184
+ if (!existsSync(configDir)) {
185
+ mkdirSync(configDir, { recursive: true });
186
+ }
187
+ let config = {};
188
+ if (existsSync(configPath)) {
189
+ try {
190
+ config = JSON.parse(readFileSync(configPath, 'utf8'));
191
+ }
192
+ catch (err) {
193
+ // config broken, start fresh
194
+ }
195
+ }
196
+ config.projectId = projectId;
197
+ config.stackId = stackId;
198
+ writeFileSync(configPath, JSON.stringify(config, null, 2));
199
+ }
@@ -3,7 +3,9 @@ import config from '../../config.js';
3
3
  const { blueprints } = config.server;
4
4
  export const logsUrl = `${blueprints}vX/blueprints/logs`;
5
5
  export async function getLogs(stackId, projectId, token) {
6
- const response = await fetch(`${logsUrl}?stackId=${stackId}`, {
6
+ const url = new URL(logsUrl);
7
+ url.searchParams.append('stackId', stackId);
8
+ const response = await fetch(url.toString(), {
7
9
  headers: {
8
10
  Accept: 'application/json',
9
11
  'Content-Type': 'application/json',
@@ -1,6 +1,6 @@
1
1
  import { Command } from '@oclif/core';
2
2
  import inquirer from 'inquirer';
3
- import readBlueprintOnDisk from '../../actions/blueprints/read-blueprint.js';
3
+ import { readBlueprintOnDisk, writeBlueprintConfig } from '../../actions/blueprints/blueprint.js';
4
4
  import { createStack, updateStack } from '../../actions/blueprints/stacks.js';
5
5
  import { stashAsset } from '../../actions/blueprints/stash-asset.js';
6
6
  import { bold, green, red, yellow } from '../../utils/display/colors.js';
@@ -9,17 +9,30 @@ export default class Deploy extends Command {
9
9
  static description = 'Deploy a Blueprint';
10
10
  static examples = ['<%= config.bin %> <%= command.id %>'];
11
11
  async run() {
12
- const { errors, projectResource, stackResource, parsedBlueprint: { resources }, deployedStack, } = await readBlueprintOnDisk({ getStack: true });
12
+ const { errors, projectId: configuredProjectId, stackId, parsedBlueprint: { resources }, deployedStack, } = await readBlueprintOnDisk({ getStack: true });
13
13
  if (errors.length > 0) {
14
14
  // printErrors(errors) // TODO: error printer in formatting
15
15
  this.log('Blueprint parse errors:');
16
16
  console.dir(errors, { depth: null });
17
17
  return;
18
18
  }
19
- if (!projectResource)
20
- this.error('Blueprint must contain a project resource'); // returns
21
- const { id: projectId } = projectResource;
22
- let name = stackResource?.name;
19
+ if (stackId && !deployedStack)
20
+ this.error('Stack ID defined but deployed stack not found');
21
+ let projectId = configuredProjectId;
22
+ if (!projectId) {
23
+ this.log('No Sanity Project context found in Blueprint configuration.');
24
+ const { maybeProjectId } = await inquirer.prompt([
25
+ {
26
+ type: 'input',
27
+ name: 'maybeProjectId',
28
+ message: 'Enter Sanity Project ID:',
29
+ },
30
+ ]);
31
+ projectId = maybeProjectId;
32
+ }
33
+ if (!projectId)
34
+ this.error('Sanity Project context is required');
35
+ let name = deployedStack?.name;
23
36
  if (!name) {
24
37
  const { stackName } = await inquirer.prompt([
25
38
  {
@@ -67,6 +80,10 @@ export default class Deploy extends Command {
67
80
  this.debug('STACK RESPONSE:', stack);
68
81
  if (deployOk) {
69
82
  s.stop(`${green('Success!')} Stack "${bold(stack.name)}" ${deployedStack ? 'updated' : 'created'} <${yellow(stack.id)}>`);
83
+ writeBlueprintConfig({
84
+ projectId,
85
+ stackId: stack.id,
86
+ });
70
87
  this.log('\nUse `sanity blueprints info` to check deployment status');
71
88
  }
72
89
  else {
@@ -1,5 +1,5 @@
1
1
  import { Command, Flags } from '@oclif/core';
2
- import readBlueprintOnDisk from '../../actions/blueprints/read-blueprint.js';
2
+ import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
3
3
  import { getStack } from '../../actions/blueprints/stacks.js';
4
4
  import { formatResourceTree } from '../../utils/display/blueprints-formatting.js';
5
5
  import { bold, green, red, yellow } from '../../utils/display/colors.js';
@@ -18,18 +18,18 @@ export default class Info extends Command {
18
18
  };
19
19
  async run() {
20
20
  const { flags } = await this.parse(Info);
21
- const { errors, deployedStack, projectResource } = await readBlueprintOnDisk({ getStack: true });
21
+ const { errors, deployedStack, projectId } = await readBlueprintOnDisk({ getStack: true });
22
22
  if (errors.length > 0) {
23
23
  // printErrors(errors)
24
24
  this.log('Blueprint parse errors:');
25
25
  console.dir(errors, { depth: null });
26
26
  return;
27
27
  }
28
- if (!projectResource)
28
+ if (!projectId)
29
29
  this.error('Project resource not found in blueprint');
30
30
  let stack = deployedStack;
31
31
  if (flags.id) {
32
- const { ok, stack: foundStack, error, } = await getStack({ stackId: flags.id, projectId: projectResource.id });
32
+ const { ok, stack: foundStack, error } = await getStack({ stackId: flags.id, projectId });
33
33
  if (!ok)
34
34
  this.error(error || 'Failed to get stack');
35
35
  stack = foundStack;
@@ -1,6 +1,6 @@
1
1
  import { Command, Flags } from '@oclif/core';
2
+ import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
2
3
  import { getLogs, streamLogs } from '../../actions/blueprints/logs.js';
3
- import readBlueprintOnDisk from '../../actions/blueprints/read-blueprint.js';
4
4
  import config from '../../config.js';
5
5
  import { formatTitle } from '../../utils/display/blueprints-formatting.js';
6
6
  import { blue, bold, green, red, yellow } from '../../utils/display/colors.js';
@@ -1,24 +1,22 @@
1
1
  import { Command } from '@oclif/core';
2
- import readBlueprintOnDisk from '../../actions/blueprints/read-blueprint.js';
2
+ import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
3
3
  import { formatResourceTree, formatTitle } from '../../utils/display/blueprints-formatting.js';
4
4
  export default class Plan extends Command {
5
5
  static description = 'Enumerate resources to be deployed - will not modify any resources';
6
6
  static examples = ['<%= config.bin %> <%= command.id %>'];
7
7
  async run() {
8
- const { errors, projectResource, stackResource, parsedBlueprint: { resources }, fileInfo, } = await readBlueprintOnDisk();
8
+ const { errors, projectId, stackId, parsedBlueprint: { resources }, fileInfo, } = await readBlueprintOnDisk();
9
9
  if (errors.length > 0) {
10
10
  // printErrors(errors)
11
11
  this.log('Blueprint parse errors:');
12
12
  console.dir(errors, { depth: null });
13
- return;
13
+ // return // don't return, show the plan
14
14
  }
15
- if (!projectResource)
15
+ if (!projectId)
16
16
  this.log('Blueprint must contain a project resource');
17
- if (!stackResource)
18
- this.log('Blueprint must contain a stack resource');
19
- const name = stackResource?.name || 'Unknown';
17
+ const name = stackId || 'Unknown'; // TODO: what is a name, really?
20
18
  this.log(`${formatTitle('Blueprint', name)} Plan\n`);
21
- this.log(`Blueprint document: "${name}" (${fileInfo.fileName})`);
19
+ this.log(`Blueprint document: (${fileInfo.fileName})`);
22
20
  this.log('');
23
21
  formatResourceTree(resources, this.log.bind(this));
24
22
  if (resources.length > 0) {
@@ -1,5 +1,5 @@
1
1
  import { Command } from '@oclif/core';
2
- import readBlueprintOnDisk from '../../actions/blueprints/read-blueprint.js';
2
+ import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
3
3
  import { listStacks } from '../../actions/blueprints/stacks.js';
4
4
  import { bold, boldnblue, yellow } from '../../utils/display/colors.js';
5
5
  import { formatDate } from '../../utils/display/dates.js';
@@ -7,24 +7,24 @@ export default class Stacks extends Command {
7
7
  static description = 'List all Blueprint stacks';
8
8
  static examples = ['<%= config.bin %> <%= command.id %>'];
9
9
  async run() {
10
- const { errors, projectResource, stackResource } = await readBlueprintOnDisk();
10
+ const { errors, projectId, stackId } = await readBlueprintOnDisk();
11
11
  if (errors.length > 0) {
12
12
  this.log('Blueprint parse errors:');
13
13
  console.dir(errors, { depth: null });
14
14
  return;
15
15
  }
16
- if (!projectResource)
16
+ if (!projectId)
17
17
  this.error('Project resource not found in blueprint');
18
- const { ok, stacks, error } = await listStacks({ projectId: projectResource.id });
18
+ const { ok, stacks, error } = await listStacks({ projectId });
19
19
  if (!ok)
20
20
  this.error(error || 'Failed to list stacks');
21
21
  if (!stacks || stacks.length === 0) {
22
22
  this.log('No stacks found');
23
23
  return;
24
24
  }
25
- this.log(`${bold('Project')} <${yellow(projectResource.id)}> ${bold('Stacks')} :\n`);
25
+ this.log(`${bold('Project')} <${yellow(projectId)}> ${bold('Stacks')} :\n`);
26
26
  for (const stack of stacks) {
27
- const isCurrentStack = stackResource?.name === stack.name;
27
+ const isCurrentStack = stackId === stack.id;
28
28
  const stackName = isCurrentStack ? boldnblue(stack.name) : bold(stack.name);
29
29
  this.log(`${stackName} <${yellow(stack.id)}>${isCurrentStack ? ' (current)' : ''}`);
30
30
  if (stack.createdAt) {
@@ -1,5 +1,5 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
- import readBlueprintOnDisk from '../../actions/blueprints/read-blueprint.js';
2
+ import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
3
3
  import { invoke } from '../../actions/functions/invoke.js';
4
4
  import config from '../../config.js';
5
5
  import { red } from '../../utils/display/colors.js';
@@ -1,5 +1,5 @@
1
1
  import { Args, Command } from '@oclif/core';
2
- import readBlueprintOnDisk from '../../actions/blueprints/read-blueprint.js';
2
+ import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
3
3
  import { logs } from '../../actions/functions/logs.js';
4
4
  import config from '../../config.js';
5
5
  import { bold, red, yellow } from '../../utils/display/colors.js';
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { default as mime } from 'mime-types';
3
- import readBlueprintOnDisk from '../actions/blueprints/read-blueprint.js';
3
+ import { readBlueprintOnDisk } from '../actions/blueprints/blueprint.js';
4
4
  import invoke from '../utils/invoke-local.js';
5
5
  import * as http from 'node:http';
6
6
  const host = 'localhost';
@@ -36,7 +36,7 @@ export function formatResourceTree(resources, logger) {
36
36
  logger(` └─ ${bold('Other Resources')} [${otherResources.length}]`);
37
37
  for (const [i, other] of otherResources.entries()) {
38
38
  const isLast = i === otherResources.length - 1;
39
- logger(` ${isLast ? '└─' : '├─'} "${yellow(other.displayName || other.name || other.src)}"`);
39
+ logger(` ${isLast ? '└─' : '├─'} "${yellow(other.displayName || other.name || other.src)}"`);
40
40
  }
41
41
  }
42
42
  }
@@ -75,6 +75,8 @@ export interface BlueprintError {
75
75
  export interface BlueprintLog {
76
76
  timestamp: string;
77
77
  message: string;
78
+ stackId: string;
79
+ level: string;
78
80
  }
79
81
  /** @internal */
80
82
  export interface AuthParams {
@@ -315,5 +315,5 @@
315
315
  ]
316
316
  }
317
317
  },
318
- "version": "1.4.2"
318
+ "version": "1.6.0"
319
319
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sanity/runtime-cli",
3
3
  "description": "A new CLI generated with oclif",
4
- "version": "1.4.2",
4
+ "version": "1.6.0",
5
5
  "author": "Sanity Runtime Team",
6
6
  "type": "module",
7
7
  "license": "MIT",
@@ -13,6 +13,9 @@
13
13
  "engines": {
14
14
  "node": ">=20.11.0"
15
15
  },
16
+ "optionalPeerDependencies": {
17
+ "tsx": "^4.19.3"
18
+ },
16
19
  "files": [
17
20
  "./bin",
18
21
  "./dist",
@@ -64,6 +67,7 @@
64
67
  "rollup": "^4.36.0",
65
68
  "shx": "^0.4.0",
66
69
  "ts-node": "^10",
70
+ "tsx": "^4.19.3",
67
71
  "typescript": "^5",
68
72
  "vitest": "3.0.8"
69
73
  },
@@ -1,16 +0,0 @@
1
- import type { Blueprint, BlueprintError, BlueprintResource, BlueprintStack } from '../../utils/types.js';
2
- export default function readBlueprintOnDisk({ blueprintPath, getStack, }?: {
3
- blueprintPath?: string;
4
- getStack?: boolean;
5
- }): Promise<{
6
- fileInfo: {
7
- path: string;
8
- fileName: string;
9
- extension: string;
10
- };
11
- parsedBlueprint: Blueprint;
12
- errors: BlueprintError[];
13
- projectResource?: BlueprintResource;
14
- stackResource?: BlueprintResource;
15
- deployedStack?: BlueprintStack;
16
- }>;
@@ -1,83 +0,0 @@
1
- import { existsSync, readFileSync } from 'node:fs';
2
- import { extname, join } from 'node:path';
3
- import { cwd } from 'node:process';
4
- import { getStackByName } from '../../actions/blueprints/stacks.js';
5
- import { BlueprintErrorType } from '../../utils/types.js';
6
- // @ts-ignore - this is currently untyped
7
- import blueprintParserValidator from '../../utils/vendor/parser-validator.js';
8
- const SUPPORTED_FILE_NAMES_IN_PRIORITY_ORDER = [
9
- 'blueprint.json',
10
- // 'blueprint.js',
11
- // 'blueprint.mjs',
12
- // 'blueprint.cjs',
13
- // 'blueprint.ts',
14
- ];
15
- function findBlueprintFile(blueprintPath) {
16
- if (blueprintPath) {
17
- if (existsSync(blueprintPath)) {
18
- return { path: blueprintPath, fileName: blueprintPath, extension: extname(blueprintPath) };
19
- }
20
- throw Error(`Blueprint file not found: ${blueprintPath}`);
21
- }
22
- for (const fileName of SUPPORTED_FILE_NAMES_IN_PRIORITY_ORDER) {
23
- const filePath = join(cwd(), fileName);
24
- if (existsSync(filePath)) {
25
- return { path: filePath, fileName, extension: extname(filePath) };
26
- }
27
- }
28
- return null;
29
- }
30
- export default async function readBlueprintOnDisk({ blueprintPath, getStack, } = {}) {
31
- try {
32
- const blueprintFile = findBlueprintFile(blueprintPath);
33
- if (!blueprintFile)
34
- throw Error('Could not find Blueprint file');
35
- const { path, fileName, extension } = blueprintFile;
36
- let blueprintString;
37
- if (extension === '.json') {
38
- blueprintString = readFileSync(path, 'utf8').toString();
39
- // } else if (extension === '.js' || extension === '.mjs') {
40
- // const blueprintModule = require(path)
41
- // blueprintJson = blueprintModule.default
42
- }
43
- else {
44
- throw Error(`Unsupported blueprint file extension: ${extension}`);
45
- }
46
- const parsed = blueprintParserValidator(JSON.parse(blueprintString));
47
- const { blueprint: parsedBlueprint } = parsed;
48
- const errors = parsed.errors || [];
49
- const projectResource = parsedBlueprint.resources.find((r) => r.type === 'sanity.project');
50
- const stackResource = parsedBlueprint.resources.find((r) => r.type === 'sanity.blueprints.stack');
51
- if (!projectResource) {
52
- errors.push({
53
- message: 'Blueprint is missing a project resource',
54
- type: BlueprintErrorType.MissingProject,
55
- });
56
- }
57
- let deployedStack;
58
- if (getStack && projectResource && stackResource) {
59
- const { stack } = await getStackByName({
60
- name: stackResource.name,
61
- projectId: projectResource.id,
62
- });
63
- if (!stack) {
64
- errors.push({
65
- message: 'Stack not found',
66
- type: BlueprintErrorType.InvalidStack,
67
- });
68
- }
69
- deployedStack = stack;
70
- }
71
- return {
72
- fileInfo: { path, fileName, extension },
73
- errors,
74
- projectResource,
75
- stackResource,
76
- deployedStack,
77
- parsedBlueprint,
78
- };
79
- }
80
- catch (err) {
81
- throw Error(`Unable to parse Blueprint file: ${err}`);
82
- }
83
- }