@sanity/runtime-cli 12.3.0 → 13.0.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.
Files changed (137) hide show
  1. package/README.md +190 -76
  2. package/dist/actions/blueprints/assets.d.ts +3 -1
  3. package/dist/actions/blueprints/assets.js +15 -5
  4. package/dist/actions/blueprints/blueprint.d.ts +2 -1
  5. package/dist/actions/blueprints/blueprint.js +3 -1
  6. package/dist/actions/blueprints/config.d.ts +5 -2
  7. package/dist/actions/blueprints/config.js +4 -4
  8. package/dist/actions/blueprints/logs-streaming.d.ts +4 -2
  9. package/dist/actions/blueprints/logs-streaming.js +5 -2
  10. package/dist/actions/blueprints/logs.d.ts +2 -1
  11. package/dist/actions/blueprints/logs.js +4 -2
  12. package/dist/actions/blueprints/resources.d.ts +2 -1
  13. package/dist/actions/blueprints/resources.js +2 -2
  14. package/dist/actions/blueprints/stacks.d.ts +12 -6
  15. package/dist/actions/blueprints/stacks.js +18 -11
  16. package/dist/actions/functions/dev.d.ts +2 -1
  17. package/dist/actions/functions/dev.js +2 -2
  18. package/dist/actions/functions/env/list.d.ts +2 -1
  19. package/dist/actions/functions/env/list.js +4 -2
  20. package/dist/actions/functions/env/remove.d.ts +2 -1
  21. package/dist/actions/functions/env/remove.js +4 -2
  22. package/dist/actions/functions/env/update.d.ts +2 -1
  23. package/dist/actions/functions/env/update.js +4 -2
  24. package/dist/actions/functions/logs.d.ts +4 -3
  25. package/dist/actions/functions/logs.js +10 -6
  26. package/dist/actions/node.d.ts +2 -1
  27. package/dist/actions/node.js +2 -2
  28. package/dist/actions/sanity/examples.d.ts +5 -2
  29. package/dist/actions/sanity/examples.js +6 -6
  30. package/dist/actions/sanity/projects.d.ts +7 -3
  31. package/dist/actions/sanity/projects.js +11 -7
  32. package/dist/baseCommands.d.ts +47 -7
  33. package/dist/baseCommands.js +90 -12
  34. package/dist/commands/blueprints/add.d.ts +3 -2
  35. package/dist/commands/blueprints/add.js +14 -10
  36. package/dist/commands/blueprints/config.d.ts +3 -2
  37. package/dist/commands/blueprints/config.js +12 -6
  38. package/dist/commands/blueprints/deploy.d.ts +3 -2
  39. package/dist/commands/blueprints/deploy.js +10 -4
  40. package/dist/commands/blueprints/destroy.d.ts +3 -2
  41. package/dist/commands/blueprints/destroy.js +10 -4
  42. package/dist/commands/blueprints/doctor.d.ts +6 -4
  43. package/dist/commands/blueprints/doctor.js +17 -14
  44. package/dist/commands/blueprints/info.d.ts +3 -2
  45. package/dist/commands/blueprints/info.js +11 -5
  46. package/dist/commands/blueprints/init.d.ts +3 -2
  47. package/dist/commands/blueprints/init.js +26 -20
  48. package/dist/commands/blueprints/logs.d.ts +3 -2
  49. package/dist/commands/blueprints/logs.js +10 -4
  50. package/dist/commands/blueprints/plan.d.ts +3 -2
  51. package/dist/commands/blueprints/plan.js +8 -4
  52. package/dist/commands/blueprints/stacks.d.ts +3 -2
  53. package/dist/commands/blueprints/stacks.js +10 -6
  54. package/dist/commands/functions/add.d.ts +3 -2
  55. package/dist/commands/functions/add.js +10 -4
  56. package/dist/commands/functions/dev.d.ts +3 -2
  57. package/dist/commands/functions/dev.js +16 -5
  58. package/dist/commands/functions/env/add.d.ts +4 -3
  59. package/dist/commands/functions/env/add.js +8 -4
  60. package/dist/commands/functions/env/list.d.ts +4 -3
  61. package/dist/commands/functions/env/list.js +8 -4
  62. package/dist/commands/functions/env/remove.d.ts +4 -3
  63. package/dist/commands/functions/env/remove.js +8 -4
  64. package/dist/commands/functions/logs.d.ts +5 -4
  65. package/dist/commands/functions/logs.js +11 -5
  66. package/dist/commands/functions/test.d.ts +5 -4
  67. package/dist/commands/functions/test.js +13 -6
  68. package/dist/cores/blueprints/config.d.ts +2 -5
  69. package/dist/cores/blueprints/config.js +9 -9
  70. package/dist/cores/blueprints/deploy.js +14 -17
  71. package/dist/cores/blueprints/destroy.d.ts +2 -5
  72. package/dist/cores/blueprints/destroy.js +6 -6
  73. package/dist/cores/blueprints/doctor.js +32 -29
  74. package/dist/cores/blueprints/info.js +5 -5
  75. package/dist/cores/blueprints/init.d.ts +3 -3
  76. package/dist/cores/blueprints/init.js +15 -8
  77. package/dist/cores/blueprints/logs.js +6 -7
  78. package/dist/cores/blueprints/plan.js +1 -0
  79. package/dist/cores/blueprints/stacks.d.ts +2 -5
  80. package/dist/cores/blueprints/stacks.js +4 -4
  81. package/dist/cores/functions/add.js +8 -3
  82. package/dist/cores/functions/dev.js +2 -2
  83. package/dist/cores/functions/env/add.js +3 -4
  84. package/dist/cores/functions/env/list.js +3 -4
  85. package/dist/cores/functions/env/remove.js +3 -4
  86. package/dist/cores/functions/index.d.ts +3 -9
  87. package/dist/cores/functions/logs.d.ts +3 -1
  88. package/dist/cores/functions/logs.js +19 -11
  89. package/dist/cores/functions/test.d.ts +3 -1
  90. package/dist/cores/functions/test.js +18 -10
  91. package/dist/cores/index.d.ts +4 -7
  92. package/dist/cores/index.js +3 -3
  93. package/dist/index.d.ts +1 -2
  94. package/dist/index.js +1 -2
  95. package/dist/server/app.d.ts +2 -1
  96. package/dist/server/app.js +4 -4
  97. package/dist/server/handlers/invoke.d.ts +2 -1
  98. package/dist/server/handlers/invoke.js +2 -2
  99. package/dist/server/static/components/app.css +0 -116
  100. package/dist/server/static/components/clear-button.js +1 -1
  101. package/dist/server/static/components/console-panel.js +27 -6
  102. package/dist/server/static/components/fetch-button.js +1 -1
  103. package/dist/server/static/components/filter-api-version.js +39 -3
  104. package/dist/server/static/components/filter-document-id.js +39 -3
  105. package/dist/server/static/components/filter-with-token.js +27 -4
  106. package/dist/server/static/components/filters.js +127 -62
  107. package/dist/server/static/components/function-list.js +33 -13
  108. package/dist/server/static/components/network-spinner.js +6 -4
  109. package/dist/server/static/components/payload-panel.js +46 -24
  110. package/dist/server/static/components/response-panel.js +33 -6
  111. package/dist/server/static/components/rule-panel.js +13 -4
  112. package/dist/server/static/components/run-panel.js +14 -7
  113. package/dist/server/static/components/select-dropdown.js +34 -5
  114. package/dist/server/static/components/shared-styles.js +31 -0
  115. package/dist/server/static/components/toggle-switch.js +11 -2
  116. package/dist/utils/display/blueprints-formatting.d.ts +2 -2
  117. package/dist/utils/display/blueprints-formatting.js +31 -26
  118. package/dist/utils/display/prompt.d.ts +5 -2
  119. package/dist/utils/display/prompt.js +5 -4
  120. package/dist/utils/find-function.d.ts +4 -0
  121. package/dist/utils/find-function.js +6 -0
  122. package/dist/utils/functions/fetch-document.d.ts +3 -2
  123. package/dist/utils/functions/fetch-document.js +7 -6
  124. package/dist/utils/index.d.ts +2 -0
  125. package/dist/utils/index.js +2 -0
  126. package/dist/utils/logger.d.ts +13 -0
  127. package/dist/utils/logger.js +61 -0
  128. package/dist/utils/other/github.d.ts +2 -1
  129. package/dist/utils/other/github.js +4 -2
  130. package/dist/utils/other/npmjs.d.ts +2 -1
  131. package/dist/utils/other/npmjs.js +4 -2
  132. package/dist/utils/traced-fetch.d.ts +35 -0
  133. package/dist/utils/traced-fetch.js +238 -0
  134. package/dist/utils/validated-token.d.ts +3 -2
  135. package/dist/utils/validated-token.js +6 -4
  136. package/oclif.manifest.json +455 -75
  137. package/package.json +14 -6
@@ -1,10 +1,12 @@
1
1
  /* globals customElements */
2
2
  import {ApiBaseElement} from './api-base.js'
3
+ import {getSharedStyleSheets} from './shared-styles.js'
3
4
 
4
- const dropdownTemplate = ({label}) => `
5
+ const template = document.createElement('template')
6
+ template.innerHTML = `
5
7
  <fieldset class="mar-t-2">
6
8
  <label class="slab-text">
7
- <span class="block mar-b-1">${label}</span>
9
+ <span class="block mar-b-1"><slot name="label"></slot></span>
8
10
  <div data-ui="Select" class="relative inline-block w-100">
9
11
  <select
10
12
  data-ui="Select"
@@ -37,7 +39,31 @@ const dropdownTemplate = ({label}) => `
37
39
  `
38
40
 
39
41
  class SelectDropdown extends ApiBaseElement {
40
- connectedCallback() {
42
+ constructor() {
43
+ super()
44
+ this.attachShadow({mode: 'open'}).appendChild(template.content.cloneNode(true))
45
+ }
46
+
47
+ async connectedCallback() {
48
+ const sheets = await getSharedStyleSheets()
49
+
50
+ // Create a component-specific stylesheet that will be applied after shared styles
51
+ const componentSheet = new CSSStyleSheet()
52
+ await componentSheet.replace(`
53
+ :host {
54
+ align-self: flex-end;
55
+ }
56
+ fieldset {
57
+ margin-top: 0 !important;
58
+ }
59
+ select {
60
+ background: transparent !important;
61
+ color: light-dark(var(--gray-950), var(--gray-300)) !important;
62
+ }
63
+ `)
64
+
65
+ this.shadowRoot.adoptedStyleSheets = [...sheets, componentSheet]
66
+
41
67
  this.label = this.getAttribute('label') || 'Select'
42
68
  this.storeKey = this.getAttribute('store-key')
43
69
  this.selectedKey = this.getAttribute('selected-key')
@@ -45,8 +71,11 @@ class SelectDropdown extends ApiBaseElement {
45
71
  this.labelProp = this.getAttribute('label-prop') || 'displayName'
46
72
  this.subscribeTo = this.getAttribute('subscribe-to')
47
73
 
48
- this.innerHTML = dropdownTemplate({label: this.label})
49
- this.select = this.querySelector('select')
74
+ // Set label text in slot
75
+ const labelSlot = this.shadowRoot.querySelector('slot[name="label"]')
76
+ labelSlot.textContent = this.label
77
+
78
+ this.select = this.shadowRoot.querySelector('select')
50
79
  this.select.addEventListener('change', this.handleSelect)
51
80
 
52
81
  this.api.subscribe(this.renderOptions, [this.storeKey])
@@ -0,0 +1,31 @@
1
+ /* globals fetch */
2
+
3
+ let cachedStyleSheets = null
4
+
5
+ export async function getSharedStyleSheets() {
6
+ if (cachedStyleSheets) {
7
+ return cachedStyleSheets
8
+ }
9
+
10
+ try {
11
+ // Fetch both stylesheets
12
+ const [mCssResponse, appCssResponse] = await Promise.all([
13
+ fetch('./vendor/m-.css'),
14
+ fetch('./components/app.css'),
15
+ ])
16
+
17
+ const [mCssText, appCssText] = await Promise.all([mCssResponse.text(), appCssResponse.text()])
18
+
19
+ // Create CSSStyleSheet objects
20
+ const mSheet = new CSSStyleSheet()
21
+ const appSheet = new CSSStyleSheet()
22
+
23
+ await Promise.all([mSheet.replace(mCssText), appSheet.replace(appCssText)])
24
+
25
+ cachedStyleSheets = [mSheet, appSheet]
26
+ return cachedStyleSheets
27
+ } catch (error) {
28
+ console.error('Failed to load shared stylesheets:', error)
29
+ return []
30
+ }
31
+ }
@@ -6,6 +6,7 @@ template.innerHTML = `
6
6
  <style>
7
7
  :host {
8
8
  display: inline-block;
9
+ cursor: pointer;
9
10
  }
10
11
 
11
12
  .switch-container {
@@ -14,9 +15,11 @@ template.innerHTML = `
14
15
  }
15
16
 
16
17
  [part="track"] {
18
+ padding: 0.125em;
19
+ border-radius: 1em;
20
+ background-color: hsl(0, 0%, 67%);
17
21
  width: 1.5rem;
18
22
  height: 0.75rem;
19
- background-color: #dddddd;
20
23
  text-align: left;
21
24
  border-radius: 10px;
22
25
  overflow: hidden;
@@ -24,9 +27,11 @@ template.innerHTML = `
24
27
  }
25
28
 
26
29
  [part="slider"] {
30
+ border-radius: 1em;
31
+ background-color: hsl(0, 0%, 100%);
32
+ box-shadow: 0.0625em 0.0625em 0.125em hsla(0, 0%, 0%, 0.25);
27
33
  width: 50%;
28
34
  height: 100%;
29
- background-color: #777777;
30
35
  transition: all 256ms;
31
36
  position: absolute;
32
37
  top: 0;
@@ -37,6 +42,10 @@ template.innerHTML = `
37
42
  :host([checked]) [part="slider"] {
38
43
  transform: translateX(100%);
39
44
  }
45
+
46
+ :host([checked])::part(track) {
47
+ background-color: var(--color-interactive-primary-active-bg);
48
+ }
40
49
  </style>
41
50
 
42
51
 
@@ -1,8 +1,8 @@
1
1
  import type { Blueprint, Resource } from '@sanity/blueprints-parser';
2
2
  import { type DeployedResource, type Stack } from '../types.js';
3
3
  export declare function formatTitle(title: string, name: string): string;
4
- export declare function formatDeployedResourceTree(resources: DeployedResource[] | undefined): string;
5
- export declare function formatResourceTree(resources: Resource[] | undefined): string;
4
+ export declare function formatDeployedResourceTree(resources: DeployedResource[] | undefined, verbose?: boolean): string;
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;
8
8
  export declare function stackDeployDiff(localBlueprint: Blueprint, deployedStack: Stack): string | null;
@@ -47,28 +47,35 @@ const categoryByLabel = Object.values(RESOURCE_CATEGORIES).reduce((acc, curr) =>
47
47
  return acc;
48
48
  }, {});
49
49
  function resourceName(res, displayNameAttribute) {
50
+ const nameParts = [chalk.bold.green(res.name)];
50
51
  const displayName = displayNameAttribute &&
51
52
  displayNameAttribute in res &&
52
53
  typeof res[displayNameAttribute] === 'string' &&
53
54
  res[displayNameAttribute];
54
- const name = displayName || res.name || 'unnamed';
55
- return chalk.bold.green(name);
55
+ if (displayName && displayName !== res.name)
56
+ nameParts.push(chalk.green(`"${displayName}"`));
57
+ return nameParts.join(' ');
56
58
  }
57
- function deployedResourceName(res, displayNameAttribute) {
59
+ function deployedResourceName(res, displayNameAttribute, _verbose = false) {
60
+ const nameParts = [chalk.bold.green(res.name)];
58
61
  const displayName = displayNameAttribute &&
59
62
  typeof res.parameters[displayNameAttribute] === 'string' &&
60
63
  res.parameters[displayNameAttribute];
61
- const name = displayName || res.name || 'unnamed';
62
- const ids = idList(res).join(' ');
63
- return `${chalk.bold.green(name)} ${ids}`;
64
+ if (displayName && displayName !== res.name)
65
+ nameParts.push(chalk.green(`"${displayName}"`));
66
+ // if (verbose)
67
+ nameParts.push(idList(res));
68
+ return nameParts.join(' ');
64
69
  }
65
70
  function idList(res) {
66
- return [
67
- 'id' in res && typeof res.id === 'string' && res.id ? `${niceId(res.id)}` : '',
68
- 'externalId' in res && typeof res.externalId === 'string' && res.externalId
69
- ? `<${niceId(res.externalId)}>`
70
- : '',
71
- ];
71
+ const ids = [];
72
+ if ('id' in res && typeof res.id === 'string' && res.id) {
73
+ ids.push(niceId(res.id));
74
+ }
75
+ if ('externalId' in res && typeof res.externalId === 'string' && res.externalId) {
76
+ ids.push(`<${niceId(res.externalId)}>`);
77
+ }
78
+ return ids.length > 0 ? ids.join(' ') : '';
72
79
  }
73
80
  export function formatTitle(title, name) {
74
81
  return `${chalk.bold.blue(title)} ${chalk.bold(`"${name}"`)}`;
@@ -93,46 +100,44 @@ function categorizeResources(resources) {
93
100
  }
94
101
  return categorized;
95
102
  }
96
- function buildOutputTree(resources, name, mapToResource) {
103
+ function buildOutputTree(resources, createName, mapToResource, verbose = false) {
97
104
  const output = [`${chalk.bold.underline('Resources')} [${resources.length}]`];
105
+ const children = [];
98
106
  const categorized = categorizeResources(resources);
99
107
  for (const category of Object.values(categoryByLabel)) {
100
108
  const catResources = categorized[category.label];
101
109
  if (catResources && catResources.length > 0) {
102
- const catOutput = [`${chalk.bold(category.label)} [${catResources.length}]`];
110
+ children.push(`${chalk.bold(category.label)} [${catResources.length}]`);
103
111
  const details = [];
104
112
  for (const res of catResources) {
105
- details.push(name(category, res));
113
+ details.push(createName(category, res, verbose));
106
114
  if (category.formatDetails) {
107
115
  details.push(category.formatDetails(mapToResource(res)));
108
116
  }
109
117
  }
110
- catOutput.push(details);
111
- output.push(catOutput);
118
+ children.push(details);
112
119
  }
113
120
  }
114
121
  if (categorized['Other Resources'] && categorized['Other Resources'].length > 0) {
115
- const otherOutput = [
116
- `${chalk.bold('Other Resources')} [${categorized['Other Resources'].length}]`,
117
- ];
122
+ children.push(`${chalk.bold('Other Resources')} [${categorized['Other Resources'].length}]`);
118
123
  const otherResourcesOutput = categorized['Other Resources'].map((other) => {
119
124
  return `${chalk.yellow(other.name ?? 'unnamed')} ${chalk.dim(other.type)}`;
120
125
  });
121
- otherOutput.push(otherResourcesOutput);
122
- output.push(otherOutput);
126
+ children.push(otherResourcesOutput);
123
127
  }
128
+ output.push(children);
124
129
  return output;
125
130
  }
126
- export function formatDeployedResourceTree(resources) {
131
+ export function formatDeployedResourceTree(resources, verbose = false) {
127
132
  if (!resources || resources.length === 0)
128
133
  return ' Zero deployed resources';
129
- const output = buildOutputTree(resources, (category, res) => deployedResourceName(res, category.displayNameAttribute), (res) => ({ name: res.name, type: res.type, ...res.parameters }));
134
+ const output = buildOutputTree(resources, (category, res, v) => deployedResourceName(res, category.displayNameAttribute, v), (res) => ({ name: res.name, type: res.type, ...res.parameters }), verbose);
130
135
  return `${treeify(output)}\n`;
131
136
  }
132
- export function formatResourceTree(resources) {
137
+ export function formatResourceTree(resources, verbose = false) {
133
138
  if (!resources || resources.length === 0)
134
139
  return ' Zero resources';
135
- const output = buildOutputTree(resources, (category, res) => resourceName(res, category.displayNameAttribute), (res) => res);
140
+ const output = buildOutputTree(resources, (category, res) => resourceName(res, category.displayNameAttribute), (res) => res, verbose);
136
141
  return `${treeify(output)}\n`;
137
142
  }
138
143
  export function formatStackInfo(stack, isCurrentStack = false) {
@@ -1,3 +1,4 @@
1
+ import type { Logger } from '../logger.js';
1
2
  export declare function promptForBlueprintType(): Promise<string>;
2
3
  /**
3
4
  * Prompt the user for a Project after selecting an Organization.
@@ -5,10 +6,11 @@ export declare function promptForBlueprintType(): Promise<string>;
5
6
  * @returns The selected project, with the projectId and displayName
6
7
  * @throws {Error} If the user does not have any projects or if the API call fails
7
8
  */
8
- export declare function promptForProject({ token, knownOrganizationId, knownProjectId, }: {
9
+ export declare function promptForProject({ token, knownOrganizationId, knownProjectId, logger, }: {
9
10
  token: string;
10
11
  knownOrganizationId?: string;
11
12
  knownProjectId?: string;
13
+ logger: ReturnType<typeof Logger>;
12
14
  }): Promise<{
13
15
  projectId: string;
14
16
  displayName: string;
@@ -21,9 +23,10 @@ export declare function promptForProject({ token, knownOrganizationId, knownProj
21
23
  * @returns The selected Stack ID
22
24
  * @throws {Error} If the user does not have any Stacks or if the API call fails
23
25
  */
24
- export declare function promptForStack({ projectId, token, }: {
26
+ export declare function promptForStack({ projectId, token, logger, }: {
25
27
  projectId: string;
26
28
  token: string;
29
+ logger: ReturnType<typeof Logger>;
27
30
  }): Promise<{
28
31
  stackId: string;
29
32
  name: string;
@@ -20,8 +20,8 @@ export async function promptForBlueprintType() {
20
20
  * @returns The selected project, with the projectId and displayName
21
21
  * @throws {Error} If the user does not have any projects or if the API call fails
22
22
  */
23
- export async function promptForProject({ token, knownOrganizationId, knownProjectId, }) {
24
- const { ok, error, organizations } = await groupProjectsByOrganization({ token });
23
+ export async function promptForProject({ token, knownOrganizationId, knownProjectId, logger, }) {
24
+ const { ok, error, organizations } = await groupProjectsByOrganization({ token, logger });
25
25
  if (!ok) {
26
26
  throw new Error(error ?? 'Unknown error listing projects');
27
27
  }
@@ -78,8 +78,8 @@ export async function promptForProject({ token, knownOrganizationId, knownProjec
78
78
  * @returns The selected Stack ID
79
79
  * @throws {Error} If the user does not have any Stacks or if the API call fails
80
80
  */
81
- export async function promptForStack({ projectId, token, }) {
82
- const { ok: stacksOk, error: stacksErr, stacks, } = await listStacks({ token, scopeType: 'project', scopeId: projectId });
81
+ export async function promptForStack({ projectId, token, logger, }) {
82
+ const { ok: stacksOk, error: stacksErr, stacks, } = await listStacks({ token, scopeType: 'project', scopeId: projectId }, logger);
83
83
  if (!stacksOk) {
84
84
  throw new Error(stacksErr || 'Failed to list Stacks');
85
85
  }
@@ -110,6 +110,7 @@ export async function promptForStack({ projectId, token, }) {
110
110
  scopeType: 'project',
111
111
  scopeId: projectId,
112
112
  name: stackName,
113
+ logger,
113
114
  });
114
115
  return { stackId: stack.id, name: stackName };
115
116
  }
@@ -1,4 +1,8 @@
1
1
  import type { Blueprint } from '@sanity/blueprints-parser';
2
2
  import type { DeployedResource, FunctionResource, Stack } from './types.js';
3
+ export declare function getFunctionNames(resources: Array<{
4
+ type?: string;
5
+ name?: string;
6
+ }> | undefined): string[];
3
7
  export declare function findFunctionInBlueprint(blueprint: Blueprint, name: string): FunctionResource;
4
8
  export declare function findFunctionInStack(stack: Stack, name: string): DeployedResource;
@@ -1,3 +1,9 @@
1
+ export function getFunctionNames(resources) {
2
+ return (resources
3
+ ?.filter((r) => r?.type?.startsWith('sanity.function.'))
4
+ .map((r) => r.name)
5
+ .filter((name) => typeof name === 'string') ?? []);
6
+ }
1
7
  export function findFunctionInBlueprint(blueprint, name) {
2
8
  const func = blueprint?.resources?.find((r) => r?.type?.startsWith('sanity.function.') && r.name === name);
3
9
  if (!func)
@@ -1,4 +1,5 @@
1
1
  import { type ClientConfig } from '@sanity/client';
2
+ import type { Logger } from '../logger.js';
2
3
  import type { FetchConfig } from '../types.js';
3
- export declare function fetchDocument(documentId: string, { projectId, dataset, useCdn, apiVersion, apiHost, token }: ClientConfig): Promise<Record<string, unknown>>;
4
- export declare function fetchAsset(documentId: string, { mediaLibraryId, apiVersion, apiHost, token }: FetchConfig): Promise<Record<string, unknown>>;
4
+ export declare function fetchDocument(documentId: string, { projectId, dataset, useCdn, apiVersion, apiHost, token }: ClientConfig, logger: ReturnType<typeof Logger>): Promise<Record<string, unknown>>;
5
+ export declare function fetchAsset(documentId: string, { mediaLibraryId, apiVersion, apiHost, token }: FetchConfig, logger: ReturnType<typeof Logger>): Promise<Record<string, unknown>>;
@@ -1,7 +1,7 @@
1
1
  import { createClient } from '@sanity/client';
2
- import ora from 'ora';
3
- export async function fetchDocument(documentId, { projectId, dataset, useCdn = true, apiVersion = '2025-02-06', apiHost, token }) {
4
- const spinner = ora(`Fetching document ID ${documentId}...`).start();
2
+ import { createTracedFetch } from '../traced-fetch.js';
3
+ export async function fetchDocument(documentId, { projectId, dataset, useCdn = true, apiVersion = '2025-02-06', apiHost, token }, logger) {
4
+ const spinner = logger.ora(`Fetching document ID ${documentId}...`).start();
5
5
  const client = createClient({ projectId, dataset, useCdn, apiVersion, apiHost, token });
6
6
  const data = await client.fetch(`*[_id == "${documentId}"]`);
7
7
  spinner.stop();
@@ -10,10 +10,11 @@ export async function fetchDocument(documentId, { projectId, dataset, useCdn = t
10
10
  }
11
11
  return data[0];
12
12
  }
13
- export async function fetchAsset(documentId, { mediaLibraryId, apiVersion = '2025-03-24', apiHost, token }) {
14
- const spinner = ora(`Fetching document ID ${documentId}...`).start();
13
+ export async function fetchAsset(documentId, { mediaLibraryId, apiVersion = '2025-03-24', apiHost, token }, logger) {
14
+ const spinner = logger.ora(`Fetching document ID ${documentId}...`).start();
15
+ const fetchFn = createTracedFetch(logger);
15
16
  const url = `${apiHost}/v${apiVersion}/media-libraries/${mediaLibraryId}/doc/${documentId}`;
16
- const response = await fetch(url, {
17
+ const response = await fetchFn(url, {
17
18
  headers: {
18
19
  Authorization: `Bearer ${token}`,
19
20
  },
@@ -1,6 +1,8 @@
1
1
  export * as display from './display/index.js';
2
2
  export * as findFunction from './find-function.js';
3
3
  export * as invokeLocal from './invoke-local.js';
4
+ export * as logger from './logger.js';
5
+ export * as tracedFetch from './traced-fetch.js';
4
6
  export * as types from './types.js';
5
7
  export * as validate from './validate/index.js';
6
8
  export * as validatedToken from './validated-token.js';
@@ -1,6 +1,8 @@
1
1
  export * as display from './display/index.js';
2
2
  export * as findFunction from './find-function.js';
3
3
  export * as invokeLocal from './invoke-local.js';
4
+ export * as logger from './logger.js';
5
+ export * as tracedFetch from './traced-fetch.js';
4
6
  export * as types from './types.js';
5
7
  export * as validate from './validate/index.js';
6
8
  export * as validatedToken from './validated-token.js';
@@ -0,0 +1,13 @@
1
+ import ora from 'ora';
2
+ export declare function Logger(log: (msg: string) => void, flags?: {
3
+ verbose?: boolean;
4
+ trace?: boolean;
5
+ }): {
6
+ (msg: string): void;
7
+ trace(formatter: unknown, ...args: unknown[]): false | void;
8
+ verbose(formatter: unknown, ...args: unknown[]): false | void;
9
+ info(formatter: unknown, ...args: unknown[]): false | void;
10
+ warn(formatter: unknown, ...args: unknown[]): false | void;
11
+ error(formatter: unknown, ...args: unknown[]): false | void;
12
+ ora: typeof ora;
13
+ };
@@ -0,0 +1,61 @@
1
+ import { format } from 'node:util';
2
+ import ora from 'ora';
3
+ var LogLevel;
4
+ (function (LogLevel) {
5
+ LogLevel[LogLevel["TRACE"] = 0] = "TRACE";
6
+ LogLevel[LogLevel["VERBOSE"] = 1] = "VERBOSE";
7
+ LogLevel[LogLevel["INFO"] = 2] = "INFO";
8
+ LogLevel[LogLevel["WARN"] = 3] = "WARN";
9
+ LogLevel[LogLevel["ERROR"] = 4] = "ERROR";
10
+ })(LogLevel || (LogLevel = {}));
11
+ export function Logger(log, flags = {}) {
12
+ const logger = (msg) => {
13
+ log(msg);
14
+ };
15
+ const level = flags.verbose ? LogLevel.VERBOSE : flags.trace ? LogLevel.TRACE : LogLevel.INFO;
16
+ logger.trace = (formatter, ...args) => level <= LogLevel.TRACE && logger(format(formatter, ...args));
17
+ logger.verbose = (formatter, ...args) => level <= LogLevel.VERBOSE && logger(format(formatter, ...args));
18
+ logger.info = (formatter, ...args) => level <= LogLevel.INFO && logger(format(formatter, ...args));
19
+ logger.warn = (formatter, ...args) => level <= LogLevel.WARN && logger(format(formatter, ...args));
20
+ logger.error = (formatter, ...args) => level <= LogLevel.ERROR && logger(format(formatter, ...args));
21
+ const oraWrapper = (opts) => {
22
+ if (level >= LogLevel.INFO)
23
+ return ora(opts);
24
+ return createOraLineLoggingWrapper(opts, logger);
25
+ };
26
+ logger.ora = oraWrapper;
27
+ return logger;
28
+ }
29
+ /**
30
+ * An ora-like wrapper that uses standard line output logging instead of rendering a spinner; used in verbose and trace modes.
31
+ */
32
+ function createOraLineLoggingWrapper(opts, logger) {
33
+ const text = typeof opts === 'string' ? opts : (typeof opts === 'object' ? opts.text : '') || '';
34
+ const wrapper = {
35
+ clear() {
36
+ logger('');
37
+ return wrapper;
38
+ },
39
+ fail(subtext) {
40
+ logger.error(subtext);
41
+ return wrapper;
42
+ },
43
+ info(subtext) {
44
+ logger(subtext || wrapper.text);
45
+ return wrapper;
46
+ },
47
+ start(subtext) {
48
+ logger(subtext || wrapper.text);
49
+ return wrapper;
50
+ },
51
+ stop() {
52
+ return wrapper;
53
+ },
54
+ succeed(subtext) {
55
+ logger(subtext || wrapper.text);
56
+ return wrapper;
57
+ },
58
+ text,
59
+ };
60
+ return wrapper;
61
+ }
@@ -1,2 +1,3 @@
1
+ import type { Logger } from '../logger.js';
1
2
  export declare const GITHUB_API_URL = "https://api.github.com";
2
- export declare function gitHubRequest(path: string): Promise<Response>;
3
+ export declare function gitHubRequest(path: string, logger: ReturnType<typeof Logger>): Promise<Response>;
@@ -1,8 +1,10 @@
1
1
  // ! Making requests to the GitHub API will be rate limited at 60 requests per hour per IP address
2
2
  // https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#primary-rate-limit-for-unauthenticated-users
3
+ import { createTracedFetch } from '../traced-fetch.js';
3
4
  export const GITHUB_API_URL = 'https://api.github.com';
4
- export async function gitHubRequest(path) {
5
- const response = await fetch(`${GITHUB_API_URL}${path}`, {
5
+ export async function gitHubRequest(path, logger) {
6
+ const fetchFn = createTracedFetch(logger);
7
+ const response = await fetchFn(`${GITHUB_API_URL}${path}`, {
6
8
  headers: { Accept: 'application/vnd.github.v3+json' },
7
9
  });
8
10
  if (response.ok) {
@@ -1 +1,2 @@
1
- export declare function getLatestNpmVersion(pkg: string): Promise<string>;
1
+ import type { Logger } from '../logger.js';
2
+ export declare function getLatestNpmVersion(pkg: string, logger: ReturnType<typeof Logger>): Promise<string>;
@@ -1,7 +1,9 @@
1
- export async function getLatestNpmVersion(pkg) {
1
+ import { createTracedFetch } from '../traced-fetch.js';
2
+ export async function getLatestNpmVersion(pkg, logger) {
3
+ const fetchFn = createTracedFetch(logger);
2
4
  const url = `https://registry.npmjs.org/${pkg}/latest`;
3
5
  try {
4
- const res = await fetch(url);
6
+ const res = await fetchFn(url);
5
7
  if (!res.ok)
6
8
  throw new Error(`Failed to fetch version for ${pkg}`);
7
9
  const data = await res.json();
@@ -0,0 +1,35 @@
1
+ import type { Logger } from './logger.js';
2
+ /**
3
+ * Configuration options for traced fetch
4
+ */
5
+ export interface TracedFetchOptions {
6
+ /** Whether to log request headers. Default: true */
7
+ logRequestHeaders?: boolean;
8
+ /** Whether to log response headers. Default: true */
9
+ logResponseHeaders?: boolean;
10
+ /** Whether to log request body. Default: true */
11
+ logRequestBody?: boolean;
12
+ /** Whether to log response body. Default: true */
13
+ logResponseBody?: boolean;
14
+ /** Maximum length for body preview. Default: 500 */
15
+ maxBodyLength?: number;
16
+ /** Headers to redact (case-insensitive). Default: ['authorization', 'cookie', 'set-cookie', 'x-api-key', 'x-auth-token', 'proxy-authorization'] */
17
+ redactedHeaders?: string[];
18
+ }
19
+ /**
20
+ * Creates a traced fetch function with HTTP request/response introspection.
21
+ * Logs HTTP details to the provided logger when set to TRACE log level.
22
+ * Includes request timing, headers, and optional body previews.
23
+ * Headers containing sensitive data are redacted by default.
24
+ * @param logger - Logger instance from Logger() factory
25
+ * @param options - Configuration options for logging behavior
26
+ * @returns A fetch-compatible function with tracing capabilities
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * const logger = Logger(console.log, {trace: true})
31
+ * const tracedFetch = createTracedFetch(logger)
32
+ * const response = await tracedFetch('https://api.example.com/data')
33
+ * ```
34
+ */
35
+ export declare function createTracedFetch(logger: ReturnType<typeof Logger>, options?: TracedFetchOptions): typeof fetch;