@sanity/runtime-cli 13.0.3 → 13.1.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/13.0.3 linux-x64 node-v24.12.0
23
+ @sanity/runtime-cli/13.1.0 linux-x64 node-v24.13.0
24
24
  $ sanity-run --help [COMMAND]
25
25
  USAGE
26
26
  $ sanity-run COMMAND
@@ -98,7 +98,7 @@ EXAMPLES
98
98
  $ sanity-run blueprints add function --name my-function --fn-type document-create --fn-type document-update --lang js
99
99
  ```
100
100
 
101
- _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v13.0.3/src/commands/blueprints/add.ts)_
101
+ _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v13.1.0/src/commands/blueprints/add.ts)_
102
102
 
103
103
  ## `sanity-run blueprints config`
104
104
 
@@ -133,7 +133,7 @@ EXAMPLES
133
133
  $ sanity-run blueprints config --edit --project-id <projectId> --stack-id <stackId>
134
134
  ```
135
135
 
136
- _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v13.0.3/src/commands/blueprints/config.ts)_
136
+ _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v13.1.0/src/commands/blueprints/config.ts)_
137
137
 
138
138
  ## `sanity-run blueprints deploy`
139
139
 
@@ -163,7 +163,7 @@ EXAMPLES
163
163
  $ sanity-run blueprints deploy --no-wait
164
164
  ```
165
165
 
166
- _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v13.0.3/src/commands/blueprints/deploy.ts)_
166
+ _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v13.1.0/src/commands/blueprints/deploy.ts)_
167
167
 
168
168
  ## `sanity-run blueprints destroy`
169
169
 
@@ -195,7 +195,7 @@ EXAMPLES
195
195
  $ sanity-run blueprints destroy --stack-id <stackId> --project-id <projectId> --force --no-wait
196
196
  ```
197
197
 
198
- _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v13.0.3/src/commands/blueprints/destroy.ts)_
198
+ _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v13.1.0/src/commands/blueprints/destroy.ts)_
199
199
 
200
200
  ## `sanity-run blueprints doctor`
201
201
 
@@ -221,7 +221,7 @@ DESCRIPTION
221
221
  issues.
222
222
  ```
223
223
 
224
- _See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v13.0.3/src/commands/blueprints/doctor.ts)_
224
+ _See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v13.1.0/src/commands/blueprints/doctor.ts)_
225
225
 
226
226
  ## `sanity-run blueprints info`
227
227
 
@@ -251,7 +251,7 @@ EXAMPLES
251
251
  $ sanity-run blueprints info --id <stackId>
252
252
  ```
253
253
 
254
- _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v13.0.3/src/commands/blueprints/info.ts)_
254
+ _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v13.1.0/src/commands/blueprints/info.ts)_
255
255
 
256
256
  ## `sanity-run blueprints init [DIR]`
257
257
 
@@ -301,7 +301,7 @@ EXAMPLES
301
301
  $ sanity-run blueprints init --blueprint-type <json|js|ts> --stack-name <stackName>
302
302
  ```
303
303
 
304
- _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v13.0.3/src/commands/blueprints/init.ts)_
304
+ _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v13.1.0/src/commands/blueprints/init.ts)_
305
305
 
306
306
  ## `sanity-run blueprints logs`
307
307
 
@@ -329,7 +329,7 @@ EXAMPLES
329
329
  $ sanity-run blueprints logs --watch
330
330
  ```
331
331
 
332
- _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v13.0.3/src/commands/blueprints/logs.ts)_
332
+ _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v13.1.0/src/commands/blueprints/logs.ts)_
333
333
 
334
334
  ## `sanity-run blueprints plan`
335
335
 
@@ -352,7 +352,7 @@ EXAMPLES
352
352
  $ sanity-run blueprints plan
353
353
  ```
354
354
 
355
- _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v13.0.3/src/commands/blueprints/plan.ts)_
355
+ _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v13.1.0/src/commands/blueprints/plan.ts)_
356
356
 
357
357
  ## `sanity-run blueprints stacks`
358
358
 
@@ -381,7 +381,7 @@ EXAMPLES
381
381
  $ sanity-run blueprints stacks --organization-id <organizationId>
382
382
  ```
383
383
 
384
- _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v13.0.3/src/commands/blueprints/stacks.ts)_
384
+ _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v13.1.0/src/commands/blueprints/stacks.ts)_
385
385
 
386
386
  ## `sanity-run functions add`
387
387
 
@@ -430,7 +430,7 @@ EXAMPLES
430
430
  $ sanity-run functions add --name my-function --type document-create --type document-update --lang js
431
431
  ```
432
432
 
433
- _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v13.0.3/src/commands/functions/add.ts)_
433
+ _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v13.1.0/src/commands/functions/add.ts)_
434
434
 
435
435
  ## `sanity-run functions dev`
436
436
 
@@ -464,7 +464,7 @@ EXAMPLES
464
464
  $ sanity-run functions dev --timeout 60
465
465
  ```
466
466
 
467
- _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v13.0.3/src/commands/functions/dev.ts)_
467
+ _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v13.1.0/src/commands/functions/dev.ts)_
468
468
 
469
469
  ## `sanity-run functions env add NAME KEY VALUE`
470
470
 
@@ -491,7 +491,7 @@ EXAMPLES
491
491
  $ sanity-run functions env add MyFunction API_URL https://api.example.com/
492
492
  ```
493
493
 
494
- _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v13.0.3/src/commands/functions/env/add.ts)_
494
+ _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v13.1.0/src/commands/functions/env/add.ts)_
495
495
 
496
496
  ## `sanity-run functions env list NAME`
497
497
 
@@ -515,7 +515,7 @@ EXAMPLES
515
515
  $ sanity-run functions env list MyFunction
516
516
  ```
517
517
 
518
- _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v13.0.3/src/commands/functions/env/list.ts)_
518
+ _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v13.1.0/src/commands/functions/env/list.ts)_
519
519
 
520
520
  ## `sanity-run functions env remove NAME KEY`
521
521
 
@@ -541,7 +541,7 @@ EXAMPLES
541
541
  $ sanity-run functions env remove MyFunction API_URL
542
542
  ```
543
543
 
544
- _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v13.0.3/src/commands/functions/env/remove.ts)_
544
+ _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v13.1.0/src/commands/functions/env/remove.ts)_
545
545
 
546
546
  ## `sanity-run functions logs [NAME]`
547
547
 
@@ -580,7 +580,7 @@ EXAMPLES
580
580
  $ sanity-run functions logs <name> --delete
581
581
  ```
582
582
 
583
- _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v13.0.3/src/commands/functions/logs.ts)_
583
+ _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v13.1.0/src/commands/functions/logs.ts)_
584
584
 
585
585
  ## `sanity-run functions test [NAME]`
586
586
 
@@ -634,7 +634,7 @@ EXAMPLES
634
634
  $ sanity-run functions test <name> --event update --data-before '{ "title": "before" }' --data-after '{ "title": "after" }'
635
635
  ```
636
636
 
637
- _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v13.0.3/src/commands/functions/test.ts)_
637
+ _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v13.1.0/src/commands/functions/test.ts)_
638
638
 
639
639
  ## `sanity-run help [COMMAND]`
640
640
 
@@ -1,6 +1,7 @@
1
1
  import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
2
2
  import { basename, dirname, extname } from 'node:path';
3
3
  import { cwd, env } from 'node:process';
4
+ import { pathToFileURL } from 'node:url';
4
5
  import blueprintParserValidator from '@sanity/blueprints-parser';
5
6
  import { findUpSync } from 'find-up';
6
7
  import { createJiti } from 'jiti';
@@ -89,7 +90,7 @@ export async function readLocalBlueprint(logger, blueprintPath) {
89
90
  }
90
91
  case '.ts': {
91
92
  const jiti = createJiti(dirname(foundFilePath));
92
- const modDefault = await jiti.import(`file://${foundFilePath}`, { default: true });
93
+ const modDefault = await jiti.import(pathToFileURL(foundFilePath).href, { default: true });
93
94
  blueprintModule = modDefault;
94
95
  break;
95
96
  }
@@ -1,3 +1,6 @@
1
1
  export declare const GITIGNORE_FOR_FUNCTIONS = "\n# Sanity Functions\nfunctions/**/.env*\nfunctions/**/.build/\nfunctions/**/node_modules/\n";
2
2
  export declare const GITIGNORE_TEMPLATE = "node_modules\n.env\n\n# Sanity Functions\nfunctions/**/.env*\nfunctions/**/.build/\nfunctions/**/node_modules/\n\n";
3
- export declare function writeGitignoreFile(nearFilePath: string): string | null;
3
+ export type GitignoreResult = {
4
+ action: 'created' | 'updated' | 'unchanged';
5
+ };
6
+ export declare function writeGitignoreFile(nearFilePath: string): GitignoreResult;
@@ -20,9 +20,9 @@ export function writeGitignoreFile(nearFilePath) {
20
20
  // append GITIGNORE_FOR_FUNCTIONS to existing .gitignore
21
21
  const existingContent = readFileSync(gitignorePath, 'utf8').toString();
22
22
  if (existingContent.includes(GITIGNORE_FOR_FUNCTIONS))
23
- return null;
24
- content = `${existingContent}\n${GITIGNORE_FOR_FUNCTIONS}`;
23
+ return { action: 'unchanged' };
24
+ content = `${existingContent.trimEnd()}\n${GITIGNORE_FOR_FUNCTIONS}`;
25
25
  }
26
26
  writeFileSync(gitignorePath, content);
27
- return content;
27
+ return { action: gitignoreExists ? 'updated' : 'created' };
28
28
  }
@@ -1,4 +1,5 @@
1
1
  export declare const SANITY_ACCESS_ROLE = "sanity.access.role";
2
+ export declare const SANITY_ACCESS_ROBOT = "sanity.access.robot";
2
3
  export declare const SANITY_FUNCTION_DOCUMENT = "sanity.function.document";
3
4
  export declare const SANITY_FUNCTION_MEDIA_LIBRARY_ASSET = "sanity.function.media-library.asset";
4
5
  export declare const SANITY_FUNCTION_SCHEDULE = "sanity.function.cron";
package/dist/constants.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export const SANITY_ACCESS_ROLE = 'sanity.access.role';
2
+ export const SANITY_ACCESS_ROBOT = 'sanity.access.robot';
2
3
  export const SANITY_FUNCTION_DOCUMENT = 'sanity.function.document';
3
4
  export const SANITY_FUNCTION_MEDIA_LIBRARY_ASSET = 'sanity.function.media-library.asset';
4
5
  export const SANITY_FUNCTION_SCHEDULE = 'sanity.function.cron';
@@ -2,6 +2,7 @@ import type { ScopeType } from '../../utils/types.js';
2
2
  import type { CoreConfig, CoreResult } from '../index.js';
3
3
  export interface BlueprintInitOptions extends CoreConfig {
4
4
  token: string;
5
+ knownProjectId?: string;
5
6
  args: {
6
7
  dir?: string;
7
8
  };
@@ -33,6 +34,7 @@ export declare function resolveScopeAndStack(params: {
33
34
  organizationId: string | undefined;
34
35
  stackId: string | undefined;
35
36
  stackName: string | undefined;
37
+ knownProjectId?: string;
36
38
  log: CoreConfig['log'];
37
39
  token: string;
38
40
  }): Promise<ResolvedScope>;
@@ -8,6 +8,7 @@ import { createEmptyStack } from '../../actions/blueprints/stacks.js';
8
8
  import { writeGitignoreFile } from '../../actions/git.js';
9
9
  import { writeOrUpdateNodeDependency } from '../../actions/node.js';
10
10
  import { verifyExampleExists, writeExample } from '../../actions/sanity/examples.js';
11
+ import { getProject } from '../../actions/sanity/projects.js';
11
12
  import { BLUEPRINT_CONFIG_DIR, BLUEPRINT_CONFIG_FILE } from '../../config.js';
12
13
  import { check, filePathRelativeToCwd, labeledId, warn } from '../../utils/display/presenters.js';
13
14
  import { promptForBlueprintType, promptForProject, promptForStack, } from '../../utils/display/prompt.js';
@@ -15,7 +16,7 @@ import { blueprintConfigCore } from './config.js';
15
16
  const SCOPE_PROJECT = 'project';
16
17
  const SCOPE_ORGANIZATION = 'organization';
17
18
  export async function blueprintInitCore(options) {
18
- const { bin = 'sanity', log, token, args, flags } = options;
19
+ const { bin = 'sanity', log, token, knownProjectId, args, flags } = options;
19
20
  const { dir: flagDir, example: flagExample, 'blueprint-type': flagBlueprintType, 'project-id': flagProjectId, 'organization-id': flagOrganizationId, 'stack-id': flagStackId, 'stack-name': flagStackName, verbose: v = false, } = flags;
20
21
  const { dir: argDir } = args;
21
22
  const userProvidedDirName = argDir || flagDir;
@@ -84,6 +85,7 @@ export async function blueprintInitCore(options) {
84
85
  organizationId: flagOrganizationId,
85
86
  stackId: flagStackId,
86
87
  stackName: flagStackName,
88
+ knownProjectId,
87
89
  log,
88
90
  token,
89
91
  });
@@ -169,7 +171,7 @@ async function handleExampleInitialization(options) {
169
171
  return { success: true };
170
172
  }
171
173
  export async function resolveScopeAndStack(params) {
172
- const { projectId, organizationId, stackId, stackName, log, token } = params;
174
+ const { projectId, organizationId, stackId, stackName, knownProjectId, log, token } = params;
173
175
  let scopeType = SCOPE_PROJECT;
174
176
  let scopeId;
175
177
  if (projectId) {
@@ -195,10 +197,33 @@ export async function resolveScopeAndStack(params) {
195
197
  resolvedStackId = stack.id;
196
198
  }
197
199
  if (!scopeId) {
198
- log('\nBlueprints are associated with a Sanity Project. Please select one:');
199
- const pickedProject = await promptForProject({ token, logger: log });
200
- scopeType = SCOPE_PROJECT;
201
- scopeId = pickedProject.projectId;
200
+ log('\nBlueprints are associated with a Sanity project.');
201
+ // If we have a CLI project ID, offer it as a suggestion
202
+ if (knownProjectId) {
203
+ const { ok, project } = await getProject({
204
+ token,
205
+ scopeId: knownProjectId,
206
+ scopeType: 'project',
207
+ logger: log,
208
+ });
209
+ if (ok && project) {
210
+ const useCliProject = await confirm({
211
+ message: `The CLI is configured to use "${project.displayName}" (${knownProjectId}). Use this for the blueprint?`,
212
+ default: true,
213
+ });
214
+ if (useCliProject) {
215
+ scopeType = SCOPE_PROJECT;
216
+ scopeId = knownProjectId;
217
+ }
218
+ }
219
+ }
220
+ // If still no scope (no knownProjectId, lookup failed, or user declined), prompt for selection
221
+ if (!scopeId) {
222
+ log('Select a project:');
223
+ const pickedProject = await promptForProject({ token, logger: log });
224
+ scopeType = SCOPE_PROJECT;
225
+ scopeId = pickedProject.projectId;
226
+ }
202
227
  }
203
228
  if (!resolvedStackId) {
204
229
  log('\nBlueprints are deployed to a "Stack".');
@@ -254,8 +279,13 @@ export async function createBlueprintFiles(params) {
254
279
  ...(scopeType === SCOPE_ORGANIZATION ? { organizationId: scopeId } : { projectId: scopeId }),
255
280
  });
256
281
  log(check(`${chalk.bold('Added configuration:')} ${displayPath}/${BLUEPRINT_CONFIG_DIR}/${BLUEPRINT_CONFIG_FILE}`));
257
- writeGitignoreFile(blueprintFilePath);
258
- log(check(`${chalk.bold('Added .gitignore:')} ${displayPath}/.gitignore`));
282
+ const gitignoreResult = writeGitignoreFile(blueprintFilePath);
283
+ if (gitignoreResult.action === 'created') {
284
+ log(check(`${chalk.bold('Added .gitignore:')} ${displayPath}/.gitignore`));
285
+ }
286
+ else if (gitignoreResult.action === 'updated') {
287
+ log(check(`${chalk.bold('Updated .gitignore:')} ${displayPath}/.gitignore`));
288
+ }
259
289
  if (blueprintExtension !== 'json') {
260
290
  const blueprintsPackage = '@sanity/blueprints';
261
291
  try {
@@ -6,6 +6,7 @@ import { highlight } from 'cardinal';
6
6
  import chalk from 'chalk';
7
7
  import { createFunctionResource } from '../../actions/blueprints/resources.js';
8
8
  import { verifyExampleExists, writeExample } from '../../actions/sanity/examples.js';
9
+ import { SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULE } from '../../constants.js';
9
10
  import { check, indent, warn } from '../../utils/display/presenters.js';
10
11
  import { validateFunctionName } from '../../utils/validate/resource.js';
11
12
  const generateFunctionBlueprintResourceTemplate = (fnName, eventNames) => {
@@ -71,7 +72,17 @@ export async function functionAddCore(options) {
71
72
  }
72
73
  else {
73
74
  const objectLiteral = configString.replace(/^(\s*)"([a-zA-Z_$][a-zA-Z0-9_$]*)":/gm, '$1$2:');
74
- log(indent(highlight(`defineDocumentFunction(${objectLiteral})`)));
75
+ let type = 'Document';
76
+ switch (functionConfig.type) {
77
+ case SANITY_FUNCTION_MEDIA_LIBRARY_ASSET:
78
+ type = 'MediaLibraryAsset';
79
+ break;
80
+ case SANITY_FUNCTION_SCHEDULE:
81
+ type = 'Schedule';
82
+ break;
83
+ default:
84
+ }
85
+ log(indent(highlight(`define${type}Function(${objectLiteral})`)));
75
86
  }
76
87
  }
77
88
  else {
@@ -5849,7 +5849,7 @@ class SelectionRange {
5849
5849
  Compare this range to another range.
5850
5850
  */
5851
5851
  eq(other, includeAssoc = false) {
5852
- return this.anchor == other.anchor && this.head == other.head &&
5852
+ return this.anchor == other.anchor && this.head == other.head && this.goalColumn == other.goalColumn &&
5853
5853
  (!includeAssoc || !this.empty || this.assoc == other.assoc);
5854
5854
  }
5855
5855
  /**
@@ -7338,7 +7338,8 @@ class EditorState {
7338
7338
  - Other (anything else)
7339
7339
  */
7340
7340
  charCategorizer(at) {
7341
- return makeCategorizer(this.languageDataAt("wordChars", at).join(""));
7341
+ let chars = this.languageDataAt("wordChars", at);
7342
+ return makeCategorizer(chars.length ? chars[0] : "");
7342
7343
  }
7343
7344
  /**
7344
7345
  Find the word at the given position, meaning the range
@@ -7524,6 +7525,9 @@ class RangeValue {
7524
7525
  RangeValue.prototype.startSide = RangeValue.prototype.endSide = 0;
7525
7526
  RangeValue.prototype.point = false;
7526
7527
  RangeValue.prototype.mapMode = MapMode.TrackDel;
7528
+ function cmpVal(a, b) {
7529
+ return a == b || a.constructor == b.constructor && a.eq(b);
7530
+ }
7527
7531
  /**
7528
7532
  A range associates a value with a range of positions.
7529
7533
  */
@@ -7830,7 +7834,7 @@ class RangeSet {
7830
7834
  for (;;) {
7831
7835
  if (sideA.to != sideB.to ||
7832
7836
  !sameValues(sideA.active, sideB.active) ||
7833
- sideA.point && (!sideB.point || !sideA.point.eq(sideB.point)))
7837
+ sideA.point && (!sideB.point || !cmpVal(sideA.point, sideB.point)))
7834
7838
  return false;
7835
7839
  if (sideA.to > to)
7836
7840
  return true;
@@ -8305,22 +8309,27 @@ function compare(a, startA, b, startB, length, comparator) {
8305
8309
  b.goto(startB);
8306
8310
  let endB = startB + length;
8307
8311
  let pos = startB, dPos = startB - startA;
8308
- for (;;) {
8312
+ let bounds = !!comparator.boundChange;
8313
+ for (let boundChange = false;;) {
8309
8314
  let dEnd = (a.to + dPos) - b.to, diff = dEnd || a.endSide - b.endSide;
8310
8315
  let end = diff < 0 ? a.to + dPos : b.to, clipEnd = Math.min(end, endB);
8311
- if (a.point || b.point) {
8312
- if (!(a.point && b.point && (a.point == b.point || a.point.eq(b.point)) &&
8316
+ let point = a.point || b.point;
8317
+ if (point) {
8318
+ if (!(a.point && b.point && cmpVal(a.point, b.point) &&
8313
8319
  sameValues(a.activeForPoint(a.to), b.activeForPoint(b.to))))
8314
8320
  comparator.comparePoint(pos, clipEnd, a.point, b.point);
8321
+ boundChange = false;
8315
8322
  }
8316
8323
  else {
8324
+ if (boundChange)
8325
+ comparator.boundChange(pos);
8317
8326
  if (clipEnd > pos && !sameValues(a.active, b.active))
8318
8327
  comparator.compareRange(pos, clipEnd, a.active, b.active);
8328
+ if (bounds && clipEnd < endB && (dEnd || a.openEnd(end) != b.openEnd(end)))
8329
+ boundChange = true;
8319
8330
  }
8320
8331
  if (end > endB)
8321
8332
  break;
8322
- if ((dEnd || a.openEnd != b.openEnd) && comparator.boundChange)
8323
- comparator.boundChange(end);
8324
8333
  pos = end;
8325
8334
  if (diff <= 0)
8326
8335
  a.next();
@@ -8332,7 +8341,7 @@ function sameValues(a, b) {
8332
8341
  if (a.length != b.length)
8333
8342
  return false;
8334
8343
  for (let i = 0; i < a.length; i++)
8335
- if (a[i] != b[i] && !a[i].eq(b[i]))
8344
+ if (a[i] != b[i] && !cmpVal(a[i], b[i]))
8336
8345
  return false;
8337
8346
  return true;
8338
8347
  }
@@ -2,6 +2,7 @@ import {table as renderTable} from 'node:console'
2
2
  import {existsSync, statSync} from 'node:fs'
3
3
  import {isAbsolute, join} from 'node:path'
4
4
  import process from 'node:process'
5
+ import {pathToFileURL} from 'node:url'
5
6
  import {inspect} from 'node:util'
6
7
 
7
8
  export function getFunctionSource(src) {
@@ -12,9 +13,9 @@ export function getFunctionSource(src) {
12
13
  if (!existsSync(indexPath)) {
13
14
  throw Error(`Function directory ${pathToCheck} has no index.js`)
14
15
  }
15
- return `file://${indexPath}`
16
+ return pathToFileURL(indexPath).href
16
17
  }
17
- return `file://${pathToCheck}`
18
+ return pathToFileURL(pathToCheck).href
18
19
  }
19
20
 
20
21
  // Monkey patch console menthods to have logs match server log format
@@ -1,7 +1,7 @@
1
1
  import type { Blueprint, Resource } from '@sanity/blueprints-parser';
2
- import { type DeployedResource, type Stack } from '../types.js';
2
+ import { type BlueprintResourceRecord, type Stack } from '../types.js';
3
3
  export declare function formatTitle(title: string, name: string): string;
4
- export declare function formatDeployedResourceTree(resources: DeployedResource[] | undefined, verbose?: boolean): string;
4
+ export declare function formatDeployedResourceTree(resources: BlueprintResourceRecord[] | undefined, verbose?: boolean): string;
5
5
  export declare function formatResourceTree(resources: Resource[] | undefined, verbose?: boolean): string;
6
6
  export declare function formatStackInfo(stack: Stack | Blueprint, isCurrentStack?: boolean): string;
7
7
  export declare function formatStacksListing(stacks: Stack[], currentStackId?: string): string;
@@ -1,10 +1,10 @@
1
1
  import { treeify } from 'array-treeify';
2
2
  import chalk from 'chalk';
3
- import { SANITY_ACCESS_ROLE, SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULE, SANITY_PROJECT_CORS, SANITY_PROJECT_DATASET, SANITY_PROJECT_WEBHOOK, } from '../../constants.js';
4
- import { isCorsOriginResource, isDatasetResource, isRoleResource, isWebhookResource, } from '../types.js';
3
+ import { SANITY_ACCESS_ROBOT, SANITY_ACCESS_ROLE, SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULE, SANITY_PROJECT_CORS, SANITY_PROJECT_DATASET, SANITY_PROJECT_WEBHOOK, } from '../../constants.js';
4
+ import { isCorsOriginResource, isDatasetResource, isRobotResource, isRoleResource, isWebhookResource, } from '../types.js';
5
5
  import { formatDate, formatDuration } from './dates.js';
6
6
  import { niceId } from './presenters.js';
7
- import { arrayifyCors, arrayifyDataset, arrayifyFunction, arrayifyRole, arrayifyWebhook, } from './resources-formatting.js';
7
+ import { arrayifyCors, arrayifyDataset, arrayifyFunction, arrayifyRobot, arrayifyRole, arrayifyWebhook, } from './resources-formatting.js';
8
8
  const functionCategory = {
9
9
  label: 'Functions',
10
10
  displayNameAttribute: 'displayName',
@@ -18,6 +18,13 @@ const RESOURCE_CATEGORIES = {
18
18
  return isRoleResource(res) ? arrayifyRole(res) : [];
19
19
  },
20
20
  },
21
+ [SANITY_ACCESS_ROBOT]: {
22
+ label: 'Robots',
23
+ displayNameAttribute: 'label',
24
+ formatDetails(res) {
25
+ return isRobotResource(res) ? arrayifyRobot(res) : [];
26
+ },
27
+ },
21
28
  [SANITY_FUNCTION_DOCUMENT]: functionCategory,
22
29
  [SANITY_FUNCTION_MEDIA_LIBRARY_ASSET]: functionCategory,
23
30
  [SANITY_FUNCTION_SCHEDULE]: functionCategory,
@@ -32,24 +32,25 @@ export async function promptForProject({ token, knownOrganizationId, knownProjec
32
32
  if (organizations.length > 1) {
33
33
  const orgChoices = organizations.map(({ organization, projects }) => ({
34
34
  name: `"${organization.name}" ${niceId(organization.id)}`,
35
- value: { organization, projects },
35
+ value: organization.id,
36
36
  disabled: !projects || projects.length === 0 ? '(0 Projects)' : false,
37
37
  }));
38
- const pickedOrganization = await select({
38
+ const pickedOrgId = await select({
39
39
  message: 'Which Organization would you like to use?',
40
40
  choices: orgChoices,
41
41
  default: knownOrganizationId,
42
42
  });
43
- projects = pickedOrganization.projects;
43
+ const pickedOrg = organizations.find((o) => o.organization.id === pickedOrgId);
44
+ projects = pickedOrg?.projects;
44
45
  }
45
46
  else {
46
47
  projects = organizations[0].projects;
47
48
  }
48
49
  const projectChoices = projects.map(({ displayName, id: projectId }) => ({
49
50
  name: `"${displayName}" ${niceId(projectId)}`,
50
- value: { projectId, displayName },
51
+ value: projectId,
51
52
  }));
52
- let pickedProject;
53
+ let pickedProjectId;
53
54
  if (projectChoices.length === 1) {
54
55
  const onlyProject = projectChoices[0];
55
56
  const confirmed = await confirm({
@@ -57,18 +58,21 @@ export async function promptForProject({ token, knownOrganizationId, knownProjec
57
58
  default: true,
58
59
  });
59
60
  if (confirmed)
60
- pickedProject = onlyProject.value;
61
+ pickedProjectId = onlyProject.value;
61
62
  else
62
63
  throw new Error('No project selected');
63
64
  }
64
65
  else {
65
- pickedProject = await select({
66
+ pickedProjectId = await select({
66
67
  message: 'Choose a Sanity Project:',
67
68
  choices: projectChoices,
68
69
  default: knownProjectId,
69
70
  });
70
71
  }
71
- return pickedProject;
72
+ const pickedProject = projects.find((p) => p.id === pickedProjectId);
73
+ if (!pickedProject)
74
+ throw new Error('Project not found');
75
+ return { projectId: pickedProject.id, displayName: pickedProject.displayName };
72
76
  }
73
77
  /**
74
78
  * Prompt the user for a Stack ID after selecting a Project.
@@ -83,24 +87,24 @@ export async function promptForStack({ projectId, token, logger, }) {
83
87
  if (!stacksOk) {
84
88
  throw new Error(stacksErr || 'Failed to list Stacks');
85
89
  }
86
- const newStackValue = { id: 'new', name: 'new' };
87
- let pickedStackId = newStackValue;
90
+ const NEW_STACK_ID = 'new';
91
+ let pickedStackId = NEW_STACK_ID;
88
92
  if (stacks.length > 0) {
89
93
  const stackChoices = [];
90
94
  stackChoices.push(new Separator(chalk.underline('Create a new Stack:')));
91
- stackChoices.push({ name: chalk.bold('New Stack ✨'), value: newStackValue });
95
+ stackChoices.push({ name: chalk.bold('New Stack ✨'), value: NEW_STACK_ID });
92
96
  stackChoices.push(new Separator(chalk.underline('Use an existing Stack:')));
93
97
  stackChoices.push(...stacks.map((s) => ({
94
98
  name: `"${s.name}" ${niceId(s.id)} ${chalk.dim(`(${s.resources.length} res)`)}`,
95
- value: { id: s.id, name: s.name },
99
+ value: s.id,
96
100
  })));
97
101
  pickedStackId = await select({
98
102
  message: 'Select a deployment Stack:',
99
103
  choices: stackChoices,
100
- default: newStackValue,
104
+ default: NEW_STACK_ID,
101
105
  });
102
106
  }
103
- if (pickedStackId.id === 'new') {
107
+ if (pickedStackId === NEW_STACK_ID) {
104
108
  const stackName = await input({
105
109
  message: 'Enter a name for your new Stack:',
106
110
  validate: (input) => input.length > 0 || 'Stack name is required',
@@ -114,5 +118,8 @@ export async function promptForStack({ projectId, token, logger, }) {
114
118
  });
115
119
  return { stackId: stack.id, name: stackName };
116
120
  }
117
- return { stackId: pickedStackId.id, name: pickedStackId.name };
121
+ const pickedStack = stacks.find((s) => s.id === pickedStackId);
122
+ if (!pickedStack)
123
+ throw new Error('Stack not found');
124
+ return { stackId: pickedStack.id, name: pickedStack.name };
118
125
  }
@@ -1,8 +1,9 @@
1
- import type { BlueprintCorsOriginResource, BlueprintDatasetResource, BlueprintDocumentWebhookResource, BlueprintRoleResource } from '@sanity/blueprints';
1
+ import type { BlueprintCorsOriginResource, BlueprintDatasetResource, BlueprintDocumentWebhookResource, BlueprintRobotResource, BlueprintRoleResource } from '@sanity/blueprints';
2
2
  import type { TreeInput } from 'array-treeify';
3
3
  import { type FunctionResourceBase } from '../../utils/types.js';
4
4
  export declare function arrayifyFunction(fn: FunctionResourceBase): TreeInput;
5
5
  export declare function arrayifyCors(resource: BlueprintCorsOriginResource): TreeInput;
6
+ export declare function arrayifyRobot(resource: BlueprintRobotResource): TreeInput;
6
7
  export declare function arrayifyRole(resource: BlueprintRoleResource): TreeInput;
7
8
  export declare function arrayifyDataset(resource: BlueprintDatasetResource): TreeInput;
8
9
  export declare function arrayifyWebhook(resource: BlueprintDocumentWebhookResource): TreeInput;
@@ -49,6 +49,37 @@ export function arrayifyFunction(fn) {
49
49
  export function arrayifyCors(resource) {
50
50
  return [formatLabeledValue('origin', resource.origin)];
51
51
  }
52
+ export function arrayifyRobot(resource) {
53
+ const details = [formatLabeledValue('label', resource.label)];
54
+ if (resource.memberships.length > 0) {
55
+ details.push(formatLabel('memberships'));
56
+ const memberships = [];
57
+ resource.memberships.forEach((m, i) => {
58
+ const membership = [];
59
+ membership.push(formatLabeledValue('resourceType', m.resourceType));
60
+ membership.push(formatLabeledValue('resourceId', m.resourceId));
61
+ if (m.roleNames.length > 0) {
62
+ membership.push(formatLabeledValue('roleNames', m.roleNames.join(', ')));
63
+ }
64
+ else {
65
+ membership.push(formatLabeledValue('roleNames', '[]'));
66
+ }
67
+ memberships.push(formatLabel(i.toString()));
68
+ memberships.push(membership);
69
+ });
70
+ details.push(memberships);
71
+ }
72
+ else {
73
+ details.push(formatLabeledValue('memberships', '[]'));
74
+ }
75
+ if (resource.resourceType) {
76
+ details.push(formatLabeledValue('resourceType', resource.resourceType));
77
+ }
78
+ if (resource.resourceId) {
79
+ details.push(formatLabeledValue('resourceId', resource.resourceId));
80
+ }
81
+ return details;
82
+ }
52
83
  export function arrayifyRole(resource) {
53
84
  const details = [];
54
85
  if (resource.description)
@@ -13,7 +13,12 @@ export function findFunctionInBlueprint(blueprint, name) {
13
13
  export function findFunctionInStack(stack, name) {
14
14
  const func = stack?.resources?.find((r) => r?.type?.startsWith('sanity.function.') && r.name === name);
15
15
  if (!func)
16
- throw Error(`Unable to find function ${name}`);
16
+ throw Error(`Unable to find function: "${name}"`);
17
+ if (!isDeployedResource(func))
18
+ throw Error(`Unable to find deployed function: "${name}"`);
17
19
  // return the deployed resource
18
20
  return func;
19
21
  }
22
+ function isDeployedResource(r) {
23
+ return 'externalId' in r && typeof r.externalId === 'string';
24
+ }
@@ -1,4 +1,4 @@
1
- import { type BlueprintCorsOriginResource, type BlueprintDatasetResource, type BlueprintDocumentWebhookResource, type BlueprintResource, type BlueprintRoleResource } from '@sanity/blueprints';
1
+ import { type BlueprintCorsOriginResource, type BlueprintDatasetResource, type BlueprintDocumentWebhookResource, type BlueprintResource, type BlueprintRobotResource, type BlueprintRoleResource } from '@sanity/blueprints';
2
2
  import type { Blueprint } from '@sanity/blueprints-parser';
3
3
  import { SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULE } from '../constants.js';
4
4
  export type ScopeType = 'organization' | 'project';
@@ -48,11 +48,16 @@ interface FunctionResourceScheduleExpressionEvent {
48
48
  expression: string;
49
49
  }
50
50
  type FunctionResourceScheduleEvent = FunctionResourceScheduleExplicitEvent | FunctionResourceScheduleExpressionEvent;
51
- export interface DeployedResource extends BlueprintResource {
51
+ /** The Blueprint resource as represented in the Blueprints API database */
52
+ export interface BlueprintResourceRecord extends BlueprintResource {
52
53
  id: string;
53
- externalId: string;
54
+ externalId?: string | null;
54
55
  parameters: Record<string, unknown>;
55
56
  }
57
+ /** The deployed resource; complete with external ID */
58
+ export interface DeployedResource extends BlueprintResourceRecord {
59
+ externalId: string;
60
+ }
56
61
  export declare function isLocalFunctionResource(r: BlueprintResource): r is FunctionResourceBase;
57
62
  export declare function isDocumentFunctionResource<T extends BlueprintResource>(r: T): r is T & FunctionResourceDocument;
58
63
  export declare function isMediaLibraryAssetFunctionResource<T extends BlueprintResource>(r: T): r is T & FunctionResourceMediaLibraryAsset;
@@ -60,6 +65,7 @@ export declare function isScheduleFunctionResource<T extends BlueprintResource>(
60
65
  export declare function isLocalFunctionCollection<T extends BlueprintResource>(r: T): r is T & FunctionsCollection;
61
66
  export declare function isScheduleEvent(e: unknown): e is FunctionResourceScheduleEvent;
62
67
  export declare function isCorsOriginResource(r: unknown): r is BlueprintCorsOriginResource;
68
+ export declare function isRobotResource(r: unknown): r is BlueprintRobotResource;
63
69
  export declare function isRoleResource(r: unknown): r is BlueprintRoleResource;
64
70
  export declare function isDatasetResource(r: unknown): r is BlueprintDatasetResource;
65
71
  export declare function isWebhookResource(r: unknown): r is BlueprintDocumentWebhookResource;
@@ -114,7 +120,7 @@ export interface Stack {
114
120
  displayName: string;
115
121
  scopeType: ScopeType;
116
122
  scopeId: string;
117
- resources: Array<DeployedResource>;
123
+ resources: Array<BlueprintResourceRecord>;
118
124
  createdAt?: string;
119
125
  updatedAt?: string;
120
126
  recentOperation?: StackOperation;
@@ -1,4 +1,4 @@
1
- import { validateCorsOrigin, validateDataset, validateDocumentWebhook, validateRole, } from '@sanity/blueprints';
1
+ import { validateCorsOrigin, validateDataset, validateDocumentWebhook, validateRobot, validateRole, } from '@sanity/blueprints';
2
2
  import { SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULE, } from '../constants.js';
3
3
  // type narrowing with predicate functions
4
4
  export function isLocalFunctionResource(r) {
@@ -22,6 +22,9 @@ export function isScheduleEvent(e) {
22
22
  export function isCorsOriginResource(r) {
23
23
  return validateCorsOrigin(r).length === 0;
24
24
  }
25
+ export function isRobotResource(r) {
26
+ return validateRobot(r).length === 0;
27
+ }
25
28
  export function isRoleResource(r) {
26
29
  return validateRole(r).length === 0;
27
30
  }
@@ -1814,5 +1814,5 @@
1814
1814
  ]
1815
1815
  }
1816
1816
  },
1817
- "version": "13.0.3"
1817
+ "version": "13.1.0"
1818
1818
  }
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": "13.0.3",
4
+ "version": "13.1.0",
5
5
  "author": "Sanity Runtime Team",
6
6
  "type": "module",
7
7
  "license": "MIT",
@@ -98,12 +98,12 @@
98
98
  "dependencies": {
99
99
  "@architect/hydrate": "^5.0.1",
100
100
  "@architect/inventory": "^5.0.0",
101
- "@inquirer/prompts": "^8.0.1",
101
+ "@inquirer/prompts": "^8.2.0",
102
102
  "@oclif/core": "^4.8.0",
103
103
  "@oclif/plugin-help": "^6.2.36",
104
- "@sanity/blueprints": "^0.7.0",
104
+ "@sanity/blueprints": "^0.8.0",
105
105
  "@sanity/blueprints-parser": "^0.3.0",
106
- "@sanity/client": "^7.13.0",
106
+ "@sanity/client": "^7.14.0",
107
107
  "adm-zip": "^0.5.16",
108
108
  "array-treeify": "^0.1.5",
109
109
  "cardinal": "^2.1.1",
@@ -111,25 +111,24 @@
111
111
  "eventsource": "^4.1.0",
112
112
  "find-up": "^8.0.0",
113
113
  "get-folder-size": "^5.0.0",
114
- "groq-js": "^1.21.0",
115
- "inquirer": "^12.11.1",
114
+ "groq-js": "^1.25.0",
116
115
  "jiti": "^2.6.1",
117
116
  "mime-types": "^3.0.2",
118
- "ora": "^9.0.0",
117
+ "ora": "^9.1.0",
119
118
  "tar-stream": "^3.1.7",
120
- "vite": "^7.2.4",
121
- "vite-tsconfig-paths": "^5.1.4",
122
- "ws": "^8.18.3",
119
+ "vite": "^7.3.1",
120
+ "vite-tsconfig-paths": "^6.0.4",
121
+ "ws": "^8.19.0",
123
122
  "xdg-basedir": "^5.1.0"
124
123
  },
125
124
  "devDependencies": {
126
- "@biomejs/biome": "2.3.7",
125
+ "@biomejs/biome": "2.3.11",
127
126
  "@codemirror/lang-json": "^6.0.2",
128
- "@codemirror/state": "^6.5.2",
127
+ "@codemirror/state": "^6.5.4",
129
128
  "@enhance/store": "^1.0.2",
130
129
  "@lezer/highlight": "^1.2.3",
131
130
  "@oclif/test": "^4.1.15",
132
- "@playwright/test": "^1.56.1",
131
+ "@playwright/test": "^1.57.0",
133
132
  "@rollup/plugin-node-resolve": "^16.0.3",
134
133
  "@sanity/functions": "^1.1.0",
135
134
  "@types/adm-zip": "^0.5.7",
@@ -139,15 +138,15 @@
139
138
  "@types/tar-stream": "^3.1.4",
140
139
  "@types/ws": "^8.18.1",
141
140
  "codemirror": "^6.0.2",
142
- "mentoss": "^0.12.0",
143
- "oclif": "^4.22.57",
141
+ "mentoss": "^0.13.0",
142
+ "oclif": "^4.22.67",
144
143
  "pretty-bytes": "^7.1.0",
145
144
  "pretty-ms": "^9.3.0",
146
- "rollup": "^4.53.3",
145
+ "rollup": "^4.55.3",
147
146
  "shx": "^0.4.0",
148
147
  "ts-node": "^10.9.2",
149
148
  "typescript": "^5.9.3",
150
- "vitest": "4.0.13"
149
+ "vitest": "4.0.17"
151
150
  },
152
151
  "oclif": {
153
152
  "bin": "sanity-run",