@sanity/sdk 2.11.1 → 2.12.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 (47) hide show
  1. package/dist/_chunks-dts/utils.d.ts +171 -19
  2. package/dist/_chunks-es/_internal.js +41 -26
  3. package/dist/_chunks-es/_internal.js.map +1 -1
  4. package/dist/_chunks-es/createGroqSearchFilter.js +15 -4
  5. package/dist/_chunks-es/createGroqSearchFilter.js.map +1 -1
  6. package/dist/_chunks-es/telemetryManager.js +25 -19
  7. package/dist/_chunks-es/telemetryManager.js.map +1 -1
  8. package/dist/_chunks-es/version.js +1 -1
  9. package/dist/_exports/_internal.d.ts +27 -11
  10. package/dist/index.d.ts +2 -2
  11. package/dist/index.js +355 -75
  12. package/dist/index.js.map +1 -1
  13. package/package.json +8 -8
  14. package/src/_exports/index.ts +23 -2
  15. package/src/config/sanityConfig.ts +12 -0
  16. package/src/document/actions.test.ts +112 -1
  17. package/src/document/actions.ts +148 -1
  18. package/src/document/applyDocumentActions.ts +4 -3
  19. package/src/document/documentStore.ts +6 -5
  20. package/src/document/events.test.ts +57 -2
  21. package/src/document/events.ts +43 -24
  22. package/src/document/processActions/edit.ts +9 -44
  23. package/src/document/processActions/processActions.ts +44 -3
  24. package/src/document/processActions/releaseArchive.ts +77 -0
  25. package/src/document/processActions/releaseCreate.ts +59 -0
  26. package/src/document/processActions/releaseDelete.ts +65 -0
  27. package/src/document/processActions/releaseEdit.ts +36 -0
  28. package/src/document/processActions/releasePublish.ts +45 -0
  29. package/src/document/processActions/releaseSchedule.ts +87 -0
  30. package/src/document/processActions/releaseUtil.ts +31 -0
  31. package/src/document/processActions/shared.ts +94 -2
  32. package/src/document/processActions.test.ts +423 -1
  33. package/src/document/reducers.ts +40 -5
  34. package/src/releases/getPerspectiveState.test.ts +1 -1
  35. package/src/releases/releasesStore.test.ts +50 -1
  36. package/src/releases/releasesStore.ts +41 -18
  37. package/src/releases/utils/sortReleases.test.ts +2 -2
  38. package/src/releases/utils/sortReleases.ts +1 -1
  39. package/src/telemetry/environment.test.ts +119 -0
  40. package/src/telemetry/environment.ts +92 -0
  41. package/src/telemetry/{__telemetry__/sdk.telemetry.ts → events.ts} +9 -9
  42. package/src/telemetry/initTelemetry.test.ts +240 -16
  43. package/src/telemetry/initTelemetry.ts +39 -16
  44. package/src/telemetry/telemetryManager.test.ts +129 -65
  45. package/src/telemetry/telemetryManager.ts +41 -29
  46. package/src/telemetry/devMode.test.ts +0 -60
  47. package/src/telemetry/devMode.ts +0 -41
@@ -38,6 +38,13 @@ describe('createTelemetryManager', () => {
38
38
 
39
39
  const getClient = () => mockClient as never
40
40
 
41
+ const baseOptions = {
42
+ sessionId: 'test-session-id',
43
+ getClient,
44
+ projectId: 'abc123',
45
+ environment: 'development' as const,
46
+ }
47
+
41
48
  beforeEach(() => {
42
49
  vi.clearAllMocks()
43
50
  })
@@ -47,11 +54,7 @@ describe('createTelemetryManager', () => {
47
54
  })
48
55
 
49
56
  it('creates a batched store with the given session ID', () => {
50
- createTelemetryManager({
51
- sessionId: 'test-session-id',
52
- getClient,
53
- projectId: 'abc123',
54
- })
57
+ createTelemetryManager(baseOptions)
55
58
 
56
59
  expect(createBatchedStore).toHaveBeenCalledWith(
57
60
  'test-session-id',
@@ -62,11 +65,7 @@ describe('createTelemetryManager', () => {
62
65
  })
63
66
 
64
67
  it('logs session started with SDK version and provided data', () => {
65
- const manager = createTelemetryManager({
66
- sessionId: 'test-session-id',
67
- getClient,
68
- projectId: 'abc123',
69
- })
68
+ const manager = createTelemetryManager(baseOptions)
70
69
 
71
70
  const storeInstance = vi.mocked(createBatchedStore).mock.results[0].value
72
71
  const logger = storeInstance.logger
@@ -78,7 +77,7 @@ describe('createTelemetryManager', () => {
78
77
  })
79
78
 
80
79
  expect(logger.log).toHaveBeenCalledWith(
81
- expect.objectContaining({name: 'SDK Dev Session Started'}),
80
+ expect.objectContaining({name: 'SDK Session Started'}),
82
81
  expect.objectContaining({
83
82
  version: '2.8.0-test',
84
83
  projectId: 'abc123',
@@ -89,11 +88,7 @@ describe('createTelemetryManager', () => {
89
88
  })
90
89
 
91
90
  it('deduplicates hook first-used events by name', () => {
92
- const manager = createTelemetryManager({
93
- sessionId: 'test-session-id',
94
- getClient,
95
- projectId: 'abc123',
96
- })
91
+ const manager = createTelemetryManager(baseOptions)
97
92
 
98
93
  const storeInstance = vi.mocked(createBatchedStore).mock.results[0].value
99
94
  const logger = storeInstance.logger
@@ -111,11 +106,7 @@ describe('createTelemetryManager', () => {
111
106
  })
112
107
 
113
108
  it('tracks hooksUsed set', () => {
114
- const manager = createTelemetryManager({
115
- sessionId: 'test-session-id',
116
- getClient,
117
- projectId: 'abc123',
118
- })
109
+ const manager = createTelemetryManager(baseOptions)
119
110
 
120
111
  manager.logHookFirstUsed('useQuery')
121
112
  manager.logHookFirstUsed('useDocument')
@@ -123,19 +114,15 @@ describe('createTelemetryManager', () => {
123
114
  expect(manager.hooksUsed).toEqual(new Set(['useQuery', 'useDocument']))
124
115
  })
125
116
 
126
- it('logs dev error events', () => {
127
- const manager = createTelemetryManager({
128
- sessionId: 'test-session-id',
129
- getClient,
130
- projectId: 'abc123',
131
- })
117
+ it('logs error events', () => {
118
+ const manager = createTelemetryManager(baseOptions)
132
119
 
133
120
  const storeInstance = vi.mocked(createBatchedStore).mock.results[0].value
134
121
  const logger = storeInstance.logger
135
122
 
136
- manager.logDevError('TypeError', 'documentStore')
123
+ manager.logError('TypeError', 'documentStore')
137
124
 
138
- expect(logger.log).toHaveBeenCalledWith(expect.objectContaining({name: 'SDK Dev Error'}), {
125
+ expect(logger.log).toHaveBeenCalledWith(expect.objectContaining({name: 'SDK Error'}), {
139
126
  errorType: 'TypeError',
140
127
  hookName: 'documentStore',
141
128
  })
@@ -144,11 +131,7 @@ describe('createTelemetryManager', () => {
144
131
  it('logs session ended with duration and hooksUsed on endSession', () => {
145
132
  vi.useFakeTimers()
146
133
 
147
- const manager = createTelemetryManager({
148
- sessionId: 'test-session-id',
149
- getClient,
150
- projectId: 'abc123',
151
- })
134
+ const manager = createTelemetryManager(baseOptions)
152
135
 
153
136
  const storeInstance = vi.mocked(createBatchedStore).mock.results[0].value
154
137
  const logger = storeInstance.logger
@@ -160,7 +143,7 @@ describe('createTelemetryManager', () => {
160
143
  manager.endSession()
161
144
 
162
145
  expect(logger.log).toHaveBeenCalledWith(
163
- expect.objectContaining({name: 'SDK Dev Session Ended'}),
146
+ expect.objectContaining({name: 'SDK Session Ended'}),
164
147
  expect.objectContaining({
165
148
  durationSeconds: 5,
166
149
  hooksUsed: ['useQuery'],
@@ -170,13 +153,114 @@ describe('createTelemetryManager', () => {
170
153
  vi.useRealTimers()
171
154
  })
172
155
 
156
+ describe('environment context', () => {
157
+ type EnrichedContext = {
158
+ environment?: string
159
+ version?: string
160
+ origin?: string
161
+ traceCorrelationId?: string
162
+ }
163
+ type EnrichedEvent = {
164
+ type?: string
165
+ name?: string
166
+ version?: number
167
+ traceId?: string
168
+ sessionId?: string
169
+ createdAt?: string
170
+ data?: unknown
171
+ context: EnrichedContext
172
+ }
173
+ const getRequestBody = () => {
174
+ const [args] = mockClient.request.mock.calls as unknown as Array<
175
+ [{body: {batch: EnrichedEvent[]}}]
176
+ >
177
+ return args[0].body
178
+ }
179
+
180
+ it('records "development" in the enriched batch context and preserves the event payload', async () => {
181
+ createTelemetryManager({...baseOptions, environment: 'development'})
182
+
183
+ const storeOptions = vi.mocked(createBatchedStore).mock.calls[0][1]
184
+ await storeOptions.sendEvents([
185
+ {
186
+ type: 'log',
187
+ version: 1,
188
+ name: 'SDK Session Started',
189
+ sessionId: 'test-session-id',
190
+ createdAt: '2026-01-01T00:00:00Z',
191
+ data: {projectId: 'p1'},
192
+ } as never,
193
+ ])
194
+
195
+ const [event] = getRequestBody().batch
196
+ expect(event.context.environment).toBe('development')
197
+ expect(event.type).toBe('log')
198
+ expect(event.name).toBe('SDK Session Started')
199
+ expect(event.version).toBe(1)
200
+ expect(event.sessionId).toBe('test-session-id')
201
+ expect(event.data).toEqual({projectId: 'p1'})
202
+ })
203
+
204
+ it('records "production" in the enriched batch context', async () => {
205
+ createTelemetryManager({...baseOptions, environment: 'production'})
206
+
207
+ const storeOptions = vi.mocked(createBatchedStore).mock.calls[0][1]
208
+ await storeOptions.sendEvents([
209
+ {
210
+ type: 'log',
211
+ version: 1,
212
+ name: 'SDK Session Started',
213
+ sessionId: 'test-session-id',
214
+ createdAt: '2026-01-01T00:00:00Z',
215
+ data: {},
216
+ } as never,
217
+ ])
218
+
219
+ expect(getRequestBody().batch[0].context.environment).toBe('production')
220
+ })
221
+
222
+ it('preserves pre-existing context fields from trace events and lets SDK fields take precedence on conflict', async () => {
223
+ // Trace events from `@sanity/telemetry` arrive with a top-level
224
+ // `context` (see `TelemetryTraceStartEvent`, etc.). Replacing rather
225
+ // than merging would silently drop those fields. The SDK-managed
226
+ // fields (`version`, `environment`, `origin`) should also win on key
227
+ // collision so they remain authoritative. The rest of the trace
228
+ // event (`type`, `name`, `traceId`, `sessionId`, `createdAt`) must
229
+ // survive — downstream consumers (Rudderstack via telemetry-sink)
230
+ // read those fields directly off each batch entry.
231
+ createTelemetryManager({...baseOptions, environment: 'production'})
232
+
233
+ const storeOptions = vi.mocked(createBatchedStore).mock.calls[0][1]
234
+ await storeOptions.sendEvents([
235
+ {
236
+ type: 'trace.start',
237
+ name: 'some.trace',
238
+ version: 1,
239
+ traceId: 't1',
240
+ sessionId: 'test-session-id',
241
+ createdAt: '2026-01-01T00:00:00Z',
242
+ context: {
243
+ traceCorrelationId: 'abc-123',
244
+ environment: 'should-be-overwritten',
245
+ },
246
+ } as never,
247
+ ])
248
+
249
+ const [event] = getRequestBody().batch
250
+ expect(event.context.traceCorrelationId).toBe('abc-123')
251
+ expect(event.context.environment).toBe('production')
252
+ expect(event.context.version).toBe('2.8.0-test')
253
+ expect(event.type).toBe('trace.start')
254
+ expect(event.name).toBe('some.trace')
255
+ expect(event.traceId).toBe('t1')
256
+ expect(event.sessionId).toBe('test-session-id')
257
+ expect(event.createdAt).toBe('2026-01-01T00:00:00Z')
258
+ })
259
+ })
260
+
173
261
  describe('endSession teardown', () => {
174
262
  it('always uses flush + end (no sendBeacon due to auth header limitation)', () => {
175
- const manager = createTelemetryManager({
176
- sessionId: 'test-session-id',
177
- getClient,
178
- projectId: 'abc123',
179
- })
263
+ const manager = createTelemetryManager(baseOptions)
180
264
 
181
265
  const storeInstance = vi.mocked(createBatchedStore).mock.results[0].value
182
266
 
@@ -191,11 +275,7 @@ describe('createTelemetryManager', () => {
191
275
  it('checkConsent returns true when user has opted in', async () => {
192
276
  mockClient.request.mockResolvedValue({status: 'granted'})
193
277
 
194
- const manager = createTelemetryManager({
195
- sessionId: 'test-session-id',
196
- getClient,
197
- projectId: 'abc123',
198
- })
278
+ const manager = createTelemetryManager(baseOptions)
199
279
 
200
280
  const result = await manager.checkConsent()
201
281
  expect(result).toBe(true)
@@ -207,11 +287,7 @@ describe('createTelemetryManager', () => {
207
287
  it('checkConsent returns false when user has denied telemetry', async () => {
208
288
  mockClient.request.mockResolvedValue({status: 'denied'})
209
289
 
210
- const manager = createTelemetryManager({
211
- sessionId: 'test-session-id',
212
- getClient,
213
- projectId: 'abc123',
214
- })
290
+ const manager = createTelemetryManager(baseOptions)
215
291
 
216
292
  expect(await manager.checkConsent()).toBe(false)
217
293
  })
@@ -219,11 +295,7 @@ describe('createTelemetryManager', () => {
219
295
  it('checkConsent returns false when consent is unset', async () => {
220
296
  mockClient.request.mockResolvedValue({status: 'unset'})
221
297
 
222
- const manager = createTelemetryManager({
223
- sessionId: 'test-session-id',
224
- getClient,
225
- projectId: 'abc123',
226
- })
298
+ const manager = createTelemetryManager(baseOptions)
227
299
 
228
300
  expect(await manager.checkConsent()).toBe(false)
229
301
  })
@@ -231,11 +303,7 @@ describe('createTelemetryManager', () => {
231
303
  it('checkConsent returns false on network failure', async () => {
232
304
  mockClient.request.mockRejectedValue(new Error('Network error'))
233
305
 
234
- const manager = createTelemetryManager({
235
- sessionId: 'test-session-id',
236
- getClient,
237
- projectId: 'abc123',
238
- })
306
+ const manager = createTelemetryManager(baseOptions)
239
307
 
240
308
  expect(await manager.checkConsent()).toBe(false)
241
309
  })
@@ -243,11 +311,7 @@ describe('createTelemetryManager', () => {
243
311
  it('caches consent after the first call', async () => {
244
312
  mockClient.request.mockResolvedValue({status: 'granted'})
245
313
 
246
- createTelemetryManager({
247
- sessionId: 'test-session-id',
248
- getClient,
249
- projectId: 'abc123',
250
- })
314
+ createTelemetryManager(baseOptions)
251
315
 
252
316
  const storeOptions = vi.mocked(createBatchedStore).mock.calls[0][1]
253
317
  const resolveConsent = storeOptions.resolveConsent
@@ -10,12 +10,8 @@ import {
10
10
 
11
11
  import {createLogger} from '../utils/logger'
12
12
  import {CORE_SDK_VERSION} from '../version'
13
- import {
14
- SDKDevError,
15
- SDKDevSessionEnded,
16
- SDKDevSessionStarted,
17
- SDKHookMounted,
18
- } from './__telemetry__/sdk.telemetry'
13
+ import {type TelemetryEnvironment} from './environment'
14
+ import {SDKError, SDKHookMounted, SDKSessionEnded, SDKSessionStarted} from './events'
19
15
 
20
16
  const FLUSH_INTERVAL_MS = 30_000
21
17
  const CONSENT_TAG = 'telemetry-consent.sdk'
@@ -24,10 +20,13 @@ const BATCH_TAG = 'telemetry.batch'
24
20
  const log = createLogger('telemetry')
25
21
 
26
22
  /**
27
- * Manages dev-mode telemetry for a single SDK instance.
23
+ * Manages telemetry for a single SDK instance.
28
24
  *
29
25
  * Wraps `@sanity/telemetry`'s batched store with SDK-specific concerns:
30
26
  * consent caching, session lifecycle events, and hook usage tracking.
27
+ * The `environment` is captured at construction time and recorded in
28
+ * the event context so downstream pipelines can distinguish dev and
29
+ * production sessions.
31
30
  *
32
31
  * @internal
33
32
  */
@@ -40,16 +39,16 @@ export interface TelemetryManager {
40
39
  */
41
40
  checkConsent(): Promise<boolean>
42
41
 
43
- /** Log a "SDK Dev Session Started" event */
42
+ /** Log a "SDK Session Started" event */
44
43
  logSessionStarted(data: {projectId: string; perspective: string; authMethod: string}): void
45
44
 
46
- /** Log a "SDK Hook First Used" event (deduplicated per hook name) */
45
+ /** Log a "SDK Hook Mounted" event (deduplicated per hook name) */
47
46
  logHookFirstUsed(hookName: string): void
48
47
 
49
- /** Log a "SDK Dev Error" event */
50
- logDevError(errorType: string, hookName: string): void
48
+ /** Log a "SDK Error" event */
49
+ logError(errorType: string, hookName: string): void
51
50
 
52
- /** Log a "SDK Dev Session Ended" event and tear down the store */
51
+ /** Log a "SDK Session Ended" event and tear down the store */
53
52
  endSession(): void
54
53
 
55
54
  /** Tear down the store without logging a session-end event */
@@ -63,6 +62,7 @@ interface TelemetryManagerOptions {
63
62
  sessionId: string
64
63
  getClient: () => SanityClient
65
64
  projectId: string
65
+ environment: TelemetryEnvironment
66
66
  }
67
67
 
68
68
  /**
@@ -75,7 +75,7 @@ interface TelemetryManagerOptions {
75
75
  * @internal
76
76
  */
77
77
  export function createTelemetryManager(options: TelemetryManagerOptions): TelemetryManager {
78
- const {sessionId, getClient, projectId} = options
78
+ const {sessionId, getClient, projectId, environment} = options
79
79
  const startedAt = Date.now()
80
80
  const emittedHooks = new Set<string>()
81
81
 
@@ -97,18 +97,28 @@ export function createTelemetryManager(options: TelemetryManagerOptions): Teleme
97
97
  }
98
98
 
99
99
  const enrichBatch = (batch: TelemetryEvent[]) =>
100
- batch.map((event) => ({
101
- ...event,
102
- context: {
103
- version: CORE_SDK_VERSION,
104
- environment: 'development' as const,
105
- origin: typeof window !== 'undefined' ? window.location.origin : 'node',
106
- },
107
- }))
100
+ batch.map((event) => {
101
+ // Trace events (`trace.start` / `trace.log` / `trace.error` /
102
+ // `trace.complete`) arrive with their own caller-provided `context`.
103
+ // Log / userProperties events don't. Narrow via `in` so `event`
104
+ // stays typed as `TelemetryEvent`, then merge so we don't drop the
105
+ // trace context. SDK-owned fields below win on conflict.
106
+ const existing =
107
+ 'context' in event ? (event.context as Record<string, unknown> | undefined) : undefined
108
+ return {
109
+ ...event,
110
+ context: {
111
+ ...existing,
112
+ version: CORE_SDK_VERSION,
113
+ environment,
114
+ origin: typeof window !== 'undefined' ? window.location.origin : 'node',
115
+ },
116
+ }
117
+ })
108
118
 
109
119
  const sendEvents = async (batch: TelemetryEvent[]): Promise<unknown> => {
110
120
  const client = getClient()
111
- log.debug('sending event batch', {batchSize: batch.length})
121
+ log.debug('sending event batch', {batchSize: batch.length, environment})
112
122
  return client.request({
113
123
  uri: '/intake/batch',
114
124
  method: 'POST',
@@ -135,13 +145,14 @@ export function createTelemetryManager(options: TelemetryManagerOptions): Teleme
135
145
  },
136
146
 
137
147
  logSessionStarted(data) {
138
- log.debug('event: SDK Dev Session Started', {
148
+ log.debug('event: SDK Session Started', {
139
149
  projectId: data.projectId,
140
150
  perspective: data.perspective,
141
151
  authMethod: data.authMethod,
142
152
  version: CORE_SDK_VERSION,
153
+ environment,
143
154
  })
144
- logger.log(SDKDevSessionStarted, {
155
+ logger.log(SDKSessionStarted, {
145
156
  version: CORE_SDK_VERSION,
146
157
  ...data,
147
158
  })
@@ -154,18 +165,19 @@ export function createTelemetryManager(options: TelemetryManagerOptions): Teleme
154
165
  logger.log(SDKHookMounted, {hookName})
155
166
  },
156
167
 
157
- logDevError(errorType: string, hookName: string) {
158
- log.debug('event: SDK Dev Error', {errorType, hookName})
159
- logger.log(SDKDevError, {errorType, hookName})
168
+ logError(errorType: string, hookName: string) {
169
+ log.debug('event: SDK Error', {errorType, hookName})
170
+ logger.log(SDKError, {errorType, hookName})
160
171
  },
161
172
 
162
173
  endSession() {
163
174
  const durationSeconds = Math.round((Date.now() - startedAt) / 1000)
164
- log.debug('event: SDK Dev Session Ended', {
175
+ log.debug('event: SDK Session Ended', {
165
176
  durationSeconds,
166
177
  hooksUsed: [...emittedHooks],
178
+ environment,
167
179
  })
168
- logger.log(SDKDevSessionEnded, {
180
+ logger.log(SDKSessionEnded, {
169
181
  durationSeconds,
170
182
  hooksUsed: [...emittedHooks],
171
183
  })
@@ -1,60 +0,0 @@
1
- import {afterEach, describe, expect, it, vi} from 'vitest'
2
-
3
- import {isDevMode} from './devMode'
4
-
5
- describe('isDevMode', () => {
6
- afterEach(() => {
7
- vi.unstubAllEnvs()
8
- vi.unstubAllGlobals()
9
- })
10
-
11
- it('returns false when NODE_ENV is production', () => {
12
- vi.stubEnv('NODE_ENV', 'production')
13
- vi.stubGlobal('window', undefined)
14
- expect(isDevMode()).toBe(false)
15
- })
16
-
17
- it('returns true when running on localhost', () => {
18
- vi.stubEnv('NODE_ENV', 'development')
19
- vi.stubGlobal('window', {
20
- location: {href: 'http://localhost:3000/'},
21
- })
22
- expect(isDevMode()).toBe(true)
23
- })
24
-
25
- it('returns true when running on 127.0.0.1', () => {
26
- vi.stubEnv('NODE_ENV', 'development')
27
- vi.stubGlobal('window', {
28
- location: {href: 'http://127.0.0.1:3000/'},
29
- })
30
- expect(isDevMode()).toBe(true)
31
- })
32
-
33
- it('returns true on localhost even when NODE_ENV is production', () => {
34
- vi.stubEnv('NODE_ENV', 'production')
35
- vi.stubGlobal('window', {
36
- location: {href: 'http://localhost:3000/'},
37
- })
38
- expect(isDevMode()).toBe(true)
39
- })
40
-
41
- it('returns false for a non-local URL', () => {
42
- vi.stubEnv('NODE_ENV', 'test')
43
- vi.stubGlobal('window', {
44
- location: {href: 'https://myapp.sanity.studio/'},
45
- })
46
- expect(isDevMode()).toBe(false)
47
- })
48
-
49
- it('returns true when NODE_ENV is development and no window', () => {
50
- vi.stubEnv('NODE_ENV', 'development')
51
- vi.stubGlobal('window', undefined)
52
- expect(isDevMode()).toBe(true)
53
- })
54
-
55
- it('returns false when NODE_ENV is test and no window', () => {
56
- vi.stubEnv('NODE_ENV', 'test')
57
- vi.stubGlobal('window', undefined)
58
- expect(isDevMode()).toBe(false)
59
- })
60
- })
@@ -1,41 +0,0 @@
1
- /**
2
- * Checks whether the current URL points to a local development server.
3
- *
4
- * @param win - The window object to check
5
- * @returns True if running on localhost or 127.0.0.1
6
- * @internal
7
- */
8
- function isLocalUrl(win: Window): boolean {
9
- const url = win.location?.href
10
- if (!url) return false
11
- return (
12
- url.startsWith('http://localhost') ||
13
- url.startsWith('https://localhost') ||
14
- url.startsWith('http://127.0.0.1') ||
15
- url.startsWith('https://127.0.0.1')
16
- )
17
- }
18
-
19
- /**
20
- * Determines whether the SDK should enable dev-mode telemetry for the
21
- * SDK consumer (i.e. a developer building an app with `@sanity/sdk`).
22
- *
23
- * Browser: returns true only when the URL is `localhost` or `127.0.0.1`.
24
- * The URL check is the primary signal because consumer bundlers may or
25
- * may not forward `NODE_ENV` to the browser reliably.
26
- *
27
- * Node (scripts / non-browser): falls back to `NODE_ENV === 'development'`.
28
- *
29
- * Bracket-notation `process.env['NODE_ENV']` is used to avoid bundler
30
- * dead-code replacement.
31
- *
32
- * @returns True if the SDK is running in a development environment
33
- * @internal
34
- */
35
- export function isDevMode(): boolean {
36
- if (typeof window !== 'undefined') {
37
- return isLocalUrl(window)
38
- }
39
-
40
- return typeof process !== 'undefined' && process.env?.['NODE_ENV'] === 'development'
41
- }