@sanity/runtime-cli 12.4.0 → 13.0.1

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 (113) hide show
  1. package/README.md +176 -63
  2. package/dist/actions/blueprints/assets.d.ts +3 -1
  3. package/dist/actions/blueprints/assets.js +8 -4
  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 +4 -0
  33. package/dist/baseCommands.js +8 -2
  34. package/dist/commands/blueprints/add.d.ts +1 -0
  35. package/dist/commands/blueprints/add.js +12 -8
  36. package/dist/commands/blueprints/config.d.ts +1 -0
  37. package/dist/commands/blueprints/config.js +10 -4
  38. package/dist/commands/blueprints/deploy.d.ts +1 -0
  39. package/dist/commands/blueprints/deploy.js +8 -2
  40. package/dist/commands/blueprints/destroy.d.ts +1 -0
  41. package/dist/commands/blueprints/destroy.js +8 -2
  42. package/dist/commands/blueprints/doctor.d.ts +1 -0
  43. package/dist/commands/blueprints/doctor.js +7 -2
  44. package/dist/commands/blueprints/info.d.ts +1 -0
  45. package/dist/commands/blueprints/info.js +9 -3
  46. package/dist/commands/blueprints/init.d.ts +1 -0
  47. package/dist/commands/blueprints/init.js +20 -11
  48. package/dist/commands/blueprints/logs.d.ts +1 -0
  49. package/dist/commands/blueprints/logs.js +8 -2
  50. package/dist/commands/blueprints/plan.d.ts +1 -0
  51. package/dist/commands/blueprints/plan.js +6 -2
  52. package/dist/commands/blueprints/stacks.d.ts +1 -0
  53. package/dist/commands/blueprints/stacks.js +8 -4
  54. package/dist/commands/functions/add.d.ts +1 -0
  55. package/dist/commands/functions/add.js +8 -2
  56. package/dist/commands/functions/dev.d.ts +1 -0
  57. package/dist/commands/functions/dev.js +14 -3
  58. package/dist/commands/functions/env/add.d.ts +2 -1
  59. package/dist/commands/functions/env/add.js +6 -2
  60. package/dist/commands/functions/env/list.d.ts +2 -1
  61. package/dist/commands/functions/env/list.js +6 -2
  62. package/dist/commands/functions/env/remove.d.ts +2 -1
  63. package/dist/commands/functions/env/remove.js +6 -2
  64. package/dist/commands/functions/logs.d.ts +2 -1
  65. package/dist/commands/functions/logs.js +7 -4
  66. package/dist/commands/functions/test.d.ts +2 -1
  67. package/dist/commands/functions/test.js +6 -2
  68. package/dist/cores/blueprints/config.js +9 -9
  69. package/dist/cores/blueprints/deploy.js +14 -16
  70. package/dist/cores/blueprints/destroy.js +6 -6
  71. package/dist/cores/blueprints/doctor.js +20 -27
  72. package/dist/cores/blueprints/info.js +3 -3
  73. package/dist/cores/blueprints/init.d.ts +3 -3
  74. package/dist/cores/blueprints/init.js +15 -8
  75. package/dist/cores/blueprints/logs.js +6 -7
  76. package/dist/cores/blueprints/plan.js +1 -0
  77. package/dist/cores/blueprints/stacks.js +4 -4
  78. package/dist/cores/functions/add.js +8 -3
  79. package/dist/cores/functions/dev.js +2 -2
  80. package/dist/cores/functions/env/add.js +3 -4
  81. package/dist/cores/functions/env/list.js +3 -4
  82. package/dist/cores/functions/env/remove.js +3 -4
  83. package/dist/cores/functions/index.d.ts +3 -9
  84. package/dist/cores/functions/logs.js +8 -9
  85. package/dist/cores/functions/test.js +7 -8
  86. package/dist/cores/index.d.ts +4 -7
  87. package/dist/cores/index.js +3 -3
  88. package/dist/index.d.ts +18 -2
  89. package/dist/index.js +20 -2
  90. package/dist/server/app.d.ts +2 -1
  91. package/dist/server/app.js +4 -4
  92. package/dist/server/handlers/invoke.d.ts +2 -1
  93. package/dist/server/handlers/invoke.js +2 -2
  94. package/dist/server/static/components/response-panel.js +3 -0
  95. package/dist/server/static/components/rule-panel.js +9 -1
  96. package/dist/utils/display/prompt.d.ts +5 -2
  97. package/dist/utils/display/prompt.js +5 -4
  98. package/dist/utils/functions/fetch-document.d.ts +3 -2
  99. package/dist/utils/functions/fetch-document.js +7 -6
  100. package/dist/utils/index.d.ts +2 -0
  101. package/dist/utils/index.js +2 -0
  102. package/dist/utils/logger.d.ts +13 -0
  103. package/dist/utils/logger.js +61 -0
  104. package/dist/utils/other/github.d.ts +2 -1
  105. package/dist/utils/other/github.js +4 -2
  106. package/dist/utils/other/npmjs.d.ts +2 -1
  107. package/dist/utils/other/npmjs.js +4 -2
  108. package/dist/utils/traced-fetch.d.ts +35 -0
  109. package/dist/utils/traced-fetch.js +238 -0
  110. package/dist/utils/validated-token.d.ts +3 -2
  111. package/dist/utils/validated-token.js +6 -4
  112. package/oclif.manifest.json +175 -38
  113. package/package.json +13 -5
@@ -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;
@@ -0,0 +1,238 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { performance } from 'node:perf_hooks';
3
+ import { env } from 'node:process';
4
+ import chalk from 'chalk';
5
+ let defaultMaxLength = env.SANITY_TRACE_MAX_LENGTH
6
+ ? Number.parseInt(env.SANITY_TRACE_MAX_LENGTH, 10)
7
+ : 500;
8
+ if (Number.isNaN(defaultMaxLength))
9
+ defaultMaxLength = 500;
10
+ const DEFAULT_OPTIONS = {
11
+ logRequestHeaders: true,
12
+ logResponseHeaders: true,
13
+ logRequestBody: true,
14
+ logResponseBody: true,
15
+ maxBodyLength: defaultMaxLength,
16
+ redactedHeaders: [
17
+ 'authorization',
18
+ 'cookie',
19
+ 'set-cookie',
20
+ 'x-api-key',
21
+ 'x-auth-token',
22
+ 'proxy-authorization',
23
+ ],
24
+ };
25
+ /**
26
+ * Content types that are safe to log as text
27
+ */
28
+ const SAFE_CONTENT_TYPES = [
29
+ 'application/json',
30
+ 'application/xml',
31
+ 'application/x-www-form-urlencoded',
32
+ 'text/',
33
+ ];
34
+ /**
35
+ * Check if content type is safe to log
36
+ */
37
+ function isSafeContentType(contentType) {
38
+ const lower = contentType.toLowerCase();
39
+ return SAFE_CONTENT_TYPES.some((safe) => lower.startsWith(safe));
40
+ }
41
+ /**
42
+ * Redact sensitive headers
43
+ */
44
+ function redactHeaders(headers, redactedKeys) {
45
+ if (!headers)
46
+ return {};
47
+ const result = {};
48
+ const redactedLower = redactedKeys.map((k) => k.toLowerCase());
49
+ // Handle Headers object
50
+ if (headers instanceof Headers) {
51
+ headers.forEach((value, key) => {
52
+ result[key] = redactedLower.includes(key.toLowerCase()) ? '[REDACTED]' : value;
53
+ });
54
+ }
55
+ // Handle array of tuples
56
+ else if (Array.isArray(headers)) {
57
+ for (const [key, value] of headers) {
58
+ result[key] = redactedLower.includes(key.toLowerCase()) ? '[REDACTED]' : value;
59
+ }
60
+ }
61
+ // Handle plain object
62
+ else {
63
+ for (const [key, value] of Object.entries(headers)) {
64
+ result[key] = redactedLower.includes(key.toLowerCase()) ? '[REDACTED]' : value;
65
+ }
66
+ }
67
+ return result;
68
+ }
69
+ /**
70
+ * Create a preview of body content
71
+ */
72
+ function createBodyPreview(body, maxLength, contentType) {
73
+ if (body === null) {
74
+ return '[empty]';
75
+ }
76
+ // Handle string bodies
77
+ if (typeof body === 'string') {
78
+ if (!contentType || isSafeContentType(contentType)) {
79
+ if (body.length <= maxLength) {
80
+ return body;
81
+ }
82
+ return `${body.slice(0, maxLength)}... [truncated]`;
83
+ }
84
+ return `[non-text content, ${body.length} chars]`;
85
+ }
86
+ // Handle Buffer
87
+ if (Buffer.isBuffer(body)) {
88
+ if (contentType && isSafeContentType(contentType)) {
89
+ const text = body.toString('utf8');
90
+ if (text.length <= maxLength) {
91
+ return text;
92
+ }
93
+ return `${text.slice(0, maxLength)}... [truncated]`;
94
+ }
95
+ return `[${contentType} binary, ${body.length} bytes]`;
96
+ }
97
+ return '[unknown content type]';
98
+ }
99
+ /**
100
+ * Extract body from RequestInit if present
101
+ */
102
+ async function extractRequestBody(init) {
103
+ if (!init?.body) {
104
+ return { body: null, contentType: null };
105
+ }
106
+ const contentType = init.headers ? new Headers(init.headers).get('content-type') : null;
107
+ // init.body in node can be string, buffer, blob, formdata, url search params or a readable stream, so handle each case.
108
+ if (typeof init.body === 'string') {
109
+ return { body: init.body, contentType };
110
+ }
111
+ if (init.body instanceof FormData) {
112
+ return { body: '[FormData]', contentType };
113
+ }
114
+ if (init.body instanceof URLSearchParams) {
115
+ return { body: init.body.toString(), contentType };
116
+ }
117
+ if (init.body instanceof Blob) {
118
+ return { body: '[FormData]', contentType: contentType || init.body.type };
119
+ }
120
+ if (init.body instanceof ReadableStream) {
121
+ return { body: '[ReadableStream]', contentType };
122
+ }
123
+ if (Buffer.isBuffer(init.body)) {
124
+ return { body: init.body, contentType };
125
+ }
126
+ return { body: '[unknown body type]', contentType };
127
+ }
128
+ /**
129
+ * Extract body from Response
130
+ */
131
+ async function extractResponseBody(response, maxLength) {
132
+ const contentType = response.headers.get('content-type');
133
+ // Don't try to read body for streaming responses (event-stream)
134
+ if (contentType?.includes('text/event-stream')) {
135
+ return '[streaming response]';
136
+ }
137
+ // Clone response to avoid consuming it
138
+ const cloned = response.clone();
139
+ try {
140
+ // Try to read as text
141
+ const text = await cloned.text();
142
+ if (!contentType || isSafeContentType(contentType)) {
143
+ if (text.length <= maxLength) {
144
+ return text;
145
+ }
146
+ return `${text.slice(0, maxLength)}... [truncated]`;
147
+ }
148
+ return `[binary content, ${text.length} bytes]`;
149
+ }
150
+ catch (error) {
151
+ return `[unable to read body: ${error instanceof Error ? error.message : String(error)}]`;
152
+ }
153
+ }
154
+ /**
155
+ * Creates a traced fetch function with HTTP request/response introspection.
156
+ * Logs HTTP details to the provided logger when set to TRACE log level.
157
+ * Includes request timing, headers, and optional body previews.
158
+ * Headers containing sensitive data are redacted by default.
159
+ * @param logger - Logger instance from Logger() factory
160
+ * @param options - Configuration options for logging behavior
161
+ * @returns A fetch-compatible function with tracing capabilities
162
+ *
163
+ * @example
164
+ * ```typescript
165
+ * const logger = Logger(console.log, {trace: true})
166
+ * const tracedFetch = createTracedFetch(logger)
167
+ * const response = await tracedFetch('https://api.example.com/data')
168
+ * ```
169
+ */
170
+ export function createTracedFetch(logger, options) {
171
+ const opts = {
172
+ ...DEFAULT_OPTIONS,
173
+ ...options,
174
+ };
175
+ return async (input, init) => {
176
+ const requestId = randomUUID().slice(0, 3);
177
+ const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;
178
+ const method = init?.method || 'GET';
179
+ const startTime = performance.now();
180
+ function trace(type, msgTemplate, ...args) {
181
+ const arrow = type === 'req' ? '→' : type === 'res' ? '←' : chalk.red('✗');
182
+ logger.trace(`${chalk.dim('[%s]')} HTTP ${arrow} ${msgTemplate}`, requestId, ...args);
183
+ }
184
+ // Log request URL
185
+ trace('req', '%s %s', method, url);
186
+ // Log request headers
187
+ if (opts.logRequestHeaders && init?.headers) {
188
+ const redacted = redactHeaders(init.headers, opts.redactedHeaders);
189
+ trace('req', 'Headers: %j', redacted);
190
+ }
191
+ // Log request body
192
+ if (opts.logRequestBody && init?.body) {
193
+ try {
194
+ const { body, contentType } = await extractRequestBody(init);
195
+ const preview = createBodyPreview(body, opts.maxBodyLength, contentType);
196
+ trace('req', 'Body: %s', preview);
197
+ }
198
+ catch (error) {
199
+ trace('err', 'Body: [error reading body: %s]', error instanceof Error ? error.message : String(error));
200
+ }
201
+ }
202
+ try {
203
+ // Make the actual fetch call
204
+ const response = await fetch(input, init);
205
+ const endTime = performance.now();
206
+ const duration = Math.round(endTime - startTime);
207
+ // Log response
208
+ trace('res', '%d %s (%dms)', response.status, response.statusText, duration);
209
+ // Log response headers
210
+ if (opts.logResponseHeaders) {
211
+ const headers = {};
212
+ response.headers.forEach((value, key) => {
213
+ headers[key] = value;
214
+ });
215
+ trace('res', 'Headers: %j', headers);
216
+ }
217
+ // Log response body
218
+ if (opts.logResponseBody) {
219
+ try {
220
+ const bodyPreview = await extractResponseBody(response, opts.maxBodyLength);
221
+ trace('res', 'Body: %s', bodyPreview);
222
+ }
223
+ catch (error) {
224
+ trace('err', 'Body: [error reading body: %s]', error instanceof Error ? error.message : String(error));
225
+ }
226
+ }
227
+ return response;
228
+ }
229
+ catch (error) {
230
+ const endTime = performance.now();
231
+ const duration = Math.round(endTime - startTime);
232
+ // Log error
233
+ trace('err', '%s %s (%dms) - %s', method, url, duration, error instanceof Error ? error.message : String(error));
234
+ // Re-throw the error
235
+ throw error;
236
+ }
237
+ };
238
+ }
@@ -1,6 +1,7 @@
1
+ import type { Logger } from './logger.js';
1
2
  import type { Result } from './types.js';
2
- export declare function validToken(maybeToken?: string): Promise<string>;
3
- export declare function validTokenOrErrorMessage(maybeToken?: string): Promise<Result<string, {
3
+ export declare function validToken(logger: ReturnType<typeof Logger>, maybeToken?: string): Promise<string>;
4
+ export declare function validTokenOrErrorMessage(logger: ReturnType<typeof Logger>, maybeToken?: string): Promise<Result<string, {
4
5
  e: Error | unknown;
5
6
  message: string;
6
7
  }>>;
@@ -1,12 +1,14 @@
1
1
  import config from '../config.js';
2
- export async function validToken(maybeToken) {
2
+ import { createTracedFetch } from './traced-fetch.js';
3
+ export async function validToken(logger, maybeToken) {
3
4
  if (config.isTest)
4
5
  return maybeToken ?? 'token';
6
+ const fetchFn = createTracedFetch(logger);
5
7
  const token = maybeToken ?? config.token;
6
8
  if (!token)
7
9
  throw new Error('NO_TOKEN');
8
10
  const url = `${config.apiUrl}v2025-04-23/users/me`;
9
- const response = await fetch(url, {
11
+ const response = await fetchFn(url, {
10
12
  method: 'GET',
11
13
  headers: {
12
14
  Accept: 'application/json',
@@ -21,9 +23,9 @@ export async function validToken(maybeToken) {
21
23
  }
22
24
  throw new Error('SERVER_ERROR', { cause: response.statusText });
23
25
  }
24
- export async function validTokenOrErrorMessage(maybeToken) {
26
+ export async function validTokenOrErrorMessage(logger, maybeToken) {
25
27
  try {
26
- const token = await validToken(maybeToken);
28
+ const token = await validToken(logger, maybeToken);
27
29
  return { ok: true, value: token };
28
30
  }
29
31
  catch (e) {