@sanity/sdk 2.11.0 → 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.
- package/dist/_chunks-dts/utils.d.ts +171 -19
- package/dist/_chunks-es/_internal.js +41 -26
- package/dist/_chunks-es/_internal.js.map +1 -1
- package/dist/_chunks-es/createGroqSearchFilter.js +25 -9
- package/dist/_chunks-es/createGroqSearchFilter.js.map +1 -1
- package/dist/_chunks-es/telemetryManager.js +25 -19
- package/dist/_chunks-es/telemetryManager.js.map +1 -1
- package/dist/_chunks-es/version.js +1 -1
- package/dist/_exports/_internal.d.ts +27 -11
- package/dist/index.d.ts +2 -2
- package/dist/index.js +723 -418
- package/dist/index.js.map +1 -1
- package/package.json +16 -16
- package/src/_exports/index.ts +23 -2
- package/src/auth/refreshStampedToken.test.ts +2 -2
- package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +116 -0
- package/src/auth/subscribeToStateAndFetchCurrentUser.ts +27 -9
- package/src/config/sanityConfig.ts +12 -0
- package/src/document/actions.test.ts +112 -1
- package/src/document/actions.ts +148 -1
- package/src/document/applyDocumentActions.ts +4 -3
- package/src/document/documentStore.ts +7 -6
- package/src/document/events.test.ts +57 -2
- package/src/document/events.ts +43 -24
- package/src/document/permissions.ts +1 -1
- package/src/document/processActions/create.ts +135 -0
- package/src/document/processActions/delete.ts +100 -0
- package/src/document/processActions/discard.ts +63 -0
- package/src/document/processActions/edit.ts +141 -0
- package/src/document/processActions/processActions.ts +209 -0
- package/src/document/processActions/publish.ts +120 -0
- package/src/document/processActions/releaseArchive.ts +77 -0
- package/src/document/processActions/releaseCreate.ts +59 -0
- package/src/document/processActions/releaseDelete.ts +65 -0
- package/src/document/processActions/releaseEdit.ts +36 -0
- package/src/document/processActions/releasePublish.ts +45 -0
- package/src/document/processActions/releaseSchedule.ts +87 -0
- package/src/document/processActions/releaseUtil.ts +31 -0
- package/src/document/processActions/shared.ts +139 -0
- package/src/document/processActions/unpublish.ts +85 -0
- package/src/document/processActions.test.ts +424 -2
- package/src/document/reducers.ts +41 -6
- package/src/releases/getPerspectiveState.test.ts +1 -1
- package/src/releases/releasesStore.test.ts +50 -1
- package/src/releases/releasesStore.ts +41 -18
- package/src/releases/utils/sortReleases.test.ts +2 -2
- package/src/releases/utils/sortReleases.ts +1 -1
- package/src/telemetry/environment.test.ts +119 -0
- package/src/telemetry/environment.ts +92 -0
- package/src/telemetry/{__telemetry__/sdk.telemetry.ts → events.ts} +9 -9
- package/src/telemetry/initTelemetry.test.ts +240 -16
- package/src/telemetry/initTelemetry.ts +39 -16
- package/src/telemetry/telemetryManager.test.ts +129 -65
- package/src/telemetry/telemetryManager.ts +41 -29
- package/src/document/processActions.ts +0 -735
- package/src/telemetry/devMode.test.ts +0 -60
- package/src/telemetry/devMode.ts +0 -41
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
|
|
2
2
|
|
|
3
|
+
import {getTokenState} from '../auth/authStore'
|
|
3
4
|
import {createSanityInstance} from '../store/createSanityInstance'
|
|
4
|
-
import {
|
|
5
|
+
import {getTelemetryEnvironment} from './environment'
|
|
5
6
|
import {getTelemetryManager, initTelemetry, trackHookMounted} from './initTelemetry'
|
|
6
7
|
import {createTelemetryManager} from './telemetryManager'
|
|
7
8
|
|
|
8
|
-
vi.mock('./
|
|
9
|
-
|
|
9
|
+
vi.mock('./environment', () => ({
|
|
10
|
+
getTelemetryEnvironment: vi.fn(() => null),
|
|
10
11
|
}))
|
|
11
12
|
|
|
12
13
|
vi.mock('./telemetryManager', () => ({
|
|
@@ -14,7 +15,7 @@ vi.mock('./telemetryManager', () => ({
|
|
|
14
15
|
checkConsent: vi.fn(() => Promise.resolve(true)),
|
|
15
16
|
logSessionStarted: vi.fn(),
|
|
16
17
|
logHookFirstUsed: vi.fn(),
|
|
17
|
-
|
|
18
|
+
logError: vi.fn(),
|
|
18
19
|
endSession: vi.fn(),
|
|
19
20
|
dispose: vi.fn(),
|
|
20
21
|
hooksUsed: new Set(),
|
|
@@ -32,6 +33,53 @@ vi.mock('../auth/authStore', () => ({
|
|
|
32
33
|
})),
|
|
33
34
|
}))
|
|
34
35
|
|
|
36
|
+
type TokenSubscriber = (token: string | null) => void
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Mimics the real token state: a BehaviorSubject-like observable that
|
|
40
|
+
* remembers the latest value and lets the test push new ones. The real
|
|
41
|
+
* `getTokenState(instance).observable` is `shareReplay({bufferSize: 1, refCount: true})`,
|
|
42
|
+
* so we also model the option to emit synchronously on subscribe.
|
|
43
|
+
*/
|
|
44
|
+
function createControlledTokenState(
|
|
45
|
+
options: {
|
|
46
|
+
initial?: string | null
|
|
47
|
+
/** If set, the observable emits this value synchronously on subscribe. */
|
|
48
|
+
emitOnSubscribe?: string | null
|
|
49
|
+
} = {},
|
|
50
|
+
) {
|
|
51
|
+
const subscribers = new Set<TokenSubscriber>()
|
|
52
|
+
let current: string | null = options.initial ?? null
|
|
53
|
+
|
|
54
|
+
const tokenState = {
|
|
55
|
+
getCurrent: vi.fn(() => current),
|
|
56
|
+
subscribe: vi.fn(() => () => {}),
|
|
57
|
+
observable: {
|
|
58
|
+
subscribe: vi.fn((cb: TokenSubscriber) => {
|
|
59
|
+
subscribers.add(cb)
|
|
60
|
+
if (options.emitOnSubscribe !== undefined) {
|
|
61
|
+
current = options.emitOnSubscribe
|
|
62
|
+
cb(current)
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
unsubscribe: vi.fn(() => {
|
|
66
|
+
subscribers.delete(cb)
|
|
67
|
+
}),
|
|
68
|
+
}
|
|
69
|
+
}),
|
|
70
|
+
},
|
|
71
|
+
} as unknown as ReturnType<typeof getTokenState>
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
tokenState,
|
|
75
|
+
emit(token: string | null) {
|
|
76
|
+
current = token
|
|
77
|
+
for (const cb of [...subscribers]) cb(token)
|
|
78
|
+
},
|
|
79
|
+
subscriberCount: () => subscribers.size,
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
35
83
|
/**
|
|
36
84
|
* Flush the microtask queue so the dynamic imports in initTelemetry
|
|
37
85
|
* have time to resolve before assertions run.
|
|
@@ -47,8 +95,8 @@ describe('initTelemetry', () => {
|
|
|
47
95
|
vi.restoreAllMocks()
|
|
48
96
|
})
|
|
49
97
|
|
|
50
|
-
it('does nothing when
|
|
51
|
-
vi.mocked(
|
|
98
|
+
it('does nothing when the environment is not eligible', async () => {
|
|
99
|
+
vi.mocked(getTelemetryEnvironment).mockReturnValue(null)
|
|
52
100
|
|
|
53
101
|
const instance = createSanityInstance()
|
|
54
102
|
|
|
@@ -60,7 +108,7 @@ describe('initTelemetry', () => {
|
|
|
60
108
|
})
|
|
61
109
|
|
|
62
110
|
it('does nothing when no projectId is provided', async () => {
|
|
63
|
-
vi.mocked(
|
|
111
|
+
vi.mocked(getTelemetryEnvironment).mockReturnValue('development')
|
|
64
112
|
|
|
65
113
|
const instance = createSanityInstance()
|
|
66
114
|
initTelemetry(instance, '')
|
|
@@ -70,8 +118,8 @@ describe('initTelemetry', () => {
|
|
|
70
118
|
instance.dispose()
|
|
71
119
|
})
|
|
72
120
|
|
|
73
|
-
it('initializes telemetry in
|
|
74
|
-
vi.mocked(
|
|
121
|
+
it('initializes telemetry in development mode with a projectId', async () => {
|
|
122
|
+
vi.mocked(getTelemetryEnvironment).mockReturnValue('development')
|
|
75
123
|
|
|
76
124
|
const instance = createSanityInstance()
|
|
77
125
|
|
|
@@ -82,6 +130,7 @@ describe('initTelemetry', () => {
|
|
|
82
130
|
expect.objectContaining({
|
|
83
131
|
sessionId: instance.instanceId,
|
|
84
132
|
projectId: 'abc123',
|
|
133
|
+
environment: 'development',
|
|
85
134
|
}),
|
|
86
135
|
)
|
|
87
136
|
|
|
@@ -96,8 +145,27 @@ describe('initTelemetry', () => {
|
|
|
96
145
|
instance.dispose()
|
|
97
146
|
})
|
|
98
147
|
|
|
148
|
+
it('initializes telemetry in production mode on a Sanity-controlled domain', async () => {
|
|
149
|
+
vi.mocked(getTelemetryEnvironment).mockReturnValue('production')
|
|
150
|
+
|
|
151
|
+
const instance = createSanityInstance()
|
|
152
|
+
|
|
153
|
+
initTelemetry(instance, 'abc123')
|
|
154
|
+
await flushPromises()
|
|
155
|
+
|
|
156
|
+
expect(createTelemetryManager).toHaveBeenCalledWith(
|
|
157
|
+
expect.objectContaining({
|
|
158
|
+
sessionId: instance.instanceId,
|
|
159
|
+
projectId: 'abc123',
|
|
160
|
+
environment: 'production',
|
|
161
|
+
}),
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
instance.dispose()
|
|
165
|
+
})
|
|
166
|
+
|
|
99
167
|
it('registers manager in the WeakMap', async () => {
|
|
100
|
-
vi.mocked(
|
|
168
|
+
vi.mocked(getTelemetryEnvironment).mockReturnValue('development')
|
|
101
169
|
|
|
102
170
|
const instance = createSanityInstance()
|
|
103
171
|
|
|
@@ -110,7 +178,7 @@ describe('initTelemetry', () => {
|
|
|
110
178
|
})
|
|
111
179
|
|
|
112
180
|
it('does not initialize if instance is already disposed', async () => {
|
|
113
|
-
vi.mocked(
|
|
181
|
+
vi.mocked(getTelemetryEnvironment).mockReturnValue('development')
|
|
114
182
|
|
|
115
183
|
const instance = createSanityInstance()
|
|
116
184
|
|
|
@@ -125,7 +193,7 @@ describe('initTelemetry', () => {
|
|
|
125
193
|
})
|
|
126
194
|
|
|
127
195
|
it('calls endSession and removes manager on instance dispose', async () => {
|
|
128
|
-
vi.mocked(
|
|
196
|
+
vi.mocked(getTelemetryEnvironment).mockReturnValue('development')
|
|
129
197
|
|
|
130
198
|
const instance = createSanityInstance()
|
|
131
199
|
|
|
@@ -142,7 +210,7 @@ describe('initTelemetry', () => {
|
|
|
142
210
|
})
|
|
143
211
|
|
|
144
212
|
it('skips telemetry entirely when user has not opted in', async () => {
|
|
145
|
-
vi.mocked(
|
|
213
|
+
vi.mocked(getTelemetryEnvironment).mockReturnValue('development')
|
|
146
214
|
|
|
147
215
|
const instance = createSanityInstance()
|
|
148
216
|
|
|
@@ -150,7 +218,7 @@ describe('initTelemetry', () => {
|
|
|
150
218
|
checkConsent: vi.fn(() => Promise.resolve(false)),
|
|
151
219
|
logSessionStarted: vi.fn(),
|
|
152
220
|
logHookFirstUsed: vi.fn(),
|
|
153
|
-
|
|
221
|
+
logError: vi.fn(),
|
|
154
222
|
endSession: vi.fn(),
|
|
155
223
|
dispose: vi.fn(),
|
|
156
224
|
hooksUsed: new Set(),
|
|
@@ -171,7 +239,7 @@ describe('initTelemetry', () => {
|
|
|
171
239
|
})
|
|
172
240
|
|
|
173
241
|
it('uses perspective from config when available', async () => {
|
|
174
|
-
vi.mocked(
|
|
242
|
+
vi.mocked(getTelemetryEnvironment).mockReturnValue('development')
|
|
175
243
|
|
|
176
244
|
const instance = createSanityInstance({perspective: 'previewDrafts'})
|
|
177
245
|
|
|
@@ -189,7 +257,7 @@ describe('initTelemetry', () => {
|
|
|
189
257
|
})
|
|
190
258
|
|
|
191
259
|
it('flushes hooks buffered before manager is ready', async () => {
|
|
192
|
-
vi.mocked(
|
|
260
|
+
vi.mocked(getTelemetryEnvironment).mockReturnValue('development')
|
|
193
261
|
|
|
194
262
|
const instance = createSanityInstance()
|
|
195
263
|
|
|
@@ -205,4 +273,160 @@ describe('initTelemetry', () => {
|
|
|
205
273
|
|
|
206
274
|
instance.dispose()
|
|
207
275
|
})
|
|
276
|
+
|
|
277
|
+
it('does not buffer hooks when the environment is not eligible', async () => {
|
|
278
|
+
vi.mocked(getTelemetryEnvironment).mockReturnValue(null)
|
|
279
|
+
|
|
280
|
+
const instance = createSanityInstance()
|
|
281
|
+
|
|
282
|
+
trackHookMounted(instance, 'useQuery')
|
|
283
|
+
|
|
284
|
+
vi.mocked(getTelemetryEnvironment).mockReturnValue('production')
|
|
285
|
+
initTelemetry(instance, 'abc123')
|
|
286
|
+
await flushPromises()
|
|
287
|
+
|
|
288
|
+
const manager = vi.mocked(createTelemetryManager).mock.results[0].value
|
|
289
|
+
expect(manager.logHookFirstUsed).not.toHaveBeenCalled()
|
|
290
|
+
|
|
291
|
+
instance.dispose()
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
describe('auth token wait', () => {
|
|
295
|
+
beforeEach(() => {
|
|
296
|
+
vi.mocked(getTelemetryEnvironment).mockReturnValue('development')
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
it('defers initialization until the token observable emits', async () => {
|
|
300
|
+
const handle = createControlledTokenState({initial: null})
|
|
301
|
+
vi.mocked(getTokenState).mockReturnValue(handle.tokenState)
|
|
302
|
+
|
|
303
|
+
const instance = createSanityInstance()
|
|
304
|
+
initTelemetry(instance, 'abc123')
|
|
305
|
+
await flushPromises()
|
|
306
|
+
|
|
307
|
+
// No token yet — manager construction must not have happened.
|
|
308
|
+
expect(createTelemetryManager).not.toHaveBeenCalled()
|
|
309
|
+
expect(handle.subscriberCount()).toBe(1)
|
|
310
|
+
|
|
311
|
+
handle.emit('real-token')
|
|
312
|
+
await flushPromises()
|
|
313
|
+
|
|
314
|
+
expect(createTelemetryManager).toHaveBeenCalledWith(
|
|
315
|
+
expect.objectContaining({projectId: 'abc123', environment: 'development'}),
|
|
316
|
+
)
|
|
317
|
+
const manager = vi.mocked(createTelemetryManager).mock.results[0].value
|
|
318
|
+
expect(manager.logSessionStarted).toHaveBeenCalled()
|
|
319
|
+
|
|
320
|
+
instance.dispose()
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
it('ignores empty token emissions and keeps waiting', async () => {
|
|
324
|
+
const handle = createControlledTokenState({initial: null})
|
|
325
|
+
vi.mocked(getTokenState).mockReturnValue(handle.tokenState)
|
|
326
|
+
|
|
327
|
+
const instance = createSanityInstance()
|
|
328
|
+
initTelemetry(instance, 'abc123')
|
|
329
|
+
await flushPromises()
|
|
330
|
+
|
|
331
|
+
// Empty/falsy emissions should not be treated as a real token.
|
|
332
|
+
handle.emit(null)
|
|
333
|
+
handle.emit('')
|
|
334
|
+
await flushPromises()
|
|
335
|
+
|
|
336
|
+
expect(createTelemetryManager).not.toHaveBeenCalled()
|
|
337
|
+
expect(handle.subscriberCount()).toBe(1)
|
|
338
|
+
|
|
339
|
+
handle.emit('real-token')
|
|
340
|
+
await flushPromises()
|
|
341
|
+
|
|
342
|
+
expect(createTelemetryManager).toHaveBeenCalledTimes(1)
|
|
343
|
+
|
|
344
|
+
instance.dispose()
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
it('unsubscribes from the token observable once a token arrives', async () => {
|
|
348
|
+
const handle = createControlledTokenState({initial: null})
|
|
349
|
+
vi.mocked(getTokenState).mockReturnValue(handle.tokenState)
|
|
350
|
+
|
|
351
|
+
const instance = createSanityInstance()
|
|
352
|
+
initTelemetry(instance, 'abc123')
|
|
353
|
+
await flushPromises()
|
|
354
|
+
|
|
355
|
+
expect(handle.subscriberCount()).toBe(1)
|
|
356
|
+
handle.emit('real-token')
|
|
357
|
+
await flushPromises()
|
|
358
|
+
|
|
359
|
+
expect(handle.subscriberCount()).toBe(0)
|
|
360
|
+
|
|
361
|
+
instance.dispose()
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
it('aborts and unsubscribes when the instance is disposed during the wait', async () => {
|
|
365
|
+
const handle = createControlledTokenState({initial: null})
|
|
366
|
+
vi.mocked(getTokenState).mockReturnValue(handle.tokenState)
|
|
367
|
+
|
|
368
|
+
const instance = createSanityInstance()
|
|
369
|
+
initTelemetry(instance, 'abc123')
|
|
370
|
+
await flushPromises()
|
|
371
|
+
|
|
372
|
+
expect(handle.subscriberCount()).toBe(1)
|
|
373
|
+
|
|
374
|
+
instance.dispose()
|
|
375
|
+
await flushPromises()
|
|
376
|
+
|
|
377
|
+
expect(createTelemetryManager).not.toHaveBeenCalled()
|
|
378
|
+
expect(handle.subscriberCount()).toBe(0)
|
|
379
|
+
expect(getTelemetryManager(instance)).toBeUndefined()
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
it('does not re-subscribe to the token observable after a disposed-wait abort', async () => {
|
|
383
|
+
// Regression guard: if initInFlight isn't cleared on the dispose-abort
|
|
384
|
+
// branch, a follow-up initTelemetry call on the same instance would be
|
|
385
|
+
// silently dropped instead of bailing on the env/projectId check.
|
|
386
|
+
const handle = createControlledTokenState({initial: null})
|
|
387
|
+
vi.mocked(getTokenState).mockReturnValue(handle.tokenState)
|
|
388
|
+
|
|
389
|
+
const instance = createSanityInstance()
|
|
390
|
+
initTelemetry(instance, 'abc123')
|
|
391
|
+
await flushPromises()
|
|
392
|
+
|
|
393
|
+
instance.dispose()
|
|
394
|
+
await flushPromises()
|
|
395
|
+
|
|
396
|
+
// A second call against the same (disposed) instance should be a no-op
|
|
397
|
+
// and must not leave dangling subscriptions.
|
|
398
|
+
initTelemetry(instance, 'abc123')
|
|
399
|
+
await flushPromises()
|
|
400
|
+
|
|
401
|
+
expect(handle.subscriberCount()).toBe(0)
|
|
402
|
+
expect(createTelemetryManager).not.toHaveBeenCalled()
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
it('handles a synchronous token emission on subscribe without crashing', async () => {
|
|
406
|
+
// The real `getTokenState(instance).observable` is shareReplay({bufferSize: 1}),
|
|
407
|
+
// which means a subscribe call can deliver a buffered value synchronously
|
|
408
|
+
// before `subscribe()` itself returns. If `getCurrent()` saw `null` but the
|
|
409
|
+
// token landed in the buffer in the gap before `.subscribe(cb)`, the callback
|
|
410
|
+
// fires with a real token while `sub` is still in the TDZ. The init flow must
|
|
411
|
+
// not crash in that race.
|
|
412
|
+
const handle = createControlledTokenState({
|
|
413
|
+
initial: null,
|
|
414
|
+
emitOnSubscribe: 'sync-token',
|
|
415
|
+
})
|
|
416
|
+
vi.mocked(getTokenState).mockReturnValue(handle.tokenState)
|
|
417
|
+
|
|
418
|
+
const instance = createSanityInstance()
|
|
419
|
+
initTelemetry(instance, 'abc123')
|
|
420
|
+
|
|
421
|
+
// Must resolve without an unhandled rejection.
|
|
422
|
+
await flushPromises()
|
|
423
|
+
|
|
424
|
+
expect(createTelemetryManager).toHaveBeenCalledWith(
|
|
425
|
+
expect.objectContaining({projectId: 'abc123'}),
|
|
426
|
+
)
|
|
427
|
+
expect(handle.subscriberCount()).toBe(0)
|
|
428
|
+
|
|
429
|
+
instance.dispose()
|
|
430
|
+
})
|
|
431
|
+
})
|
|
208
432
|
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {type SanityInstance} from '../store/createSanityInstance'
|
|
2
2
|
import {createLogger} from '../utils/logger'
|
|
3
|
-
import {
|
|
3
|
+
import {getTelemetryEnvironment} from './environment'
|
|
4
4
|
import {type TelemetryManager} from './telemetryManager'
|
|
5
5
|
|
|
6
6
|
const DEFAULT_TELEMETRY_API_VERSION = '2024-11-12'
|
|
@@ -20,10 +20,23 @@ const pendingHooks = new WeakMap<SanityInstance, Set<string>>()
|
|
|
20
20
|
const initInFlight = new WeakSet<SanityInstance>()
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* Initializes
|
|
24
|
-
* qualifies.
|
|
25
|
-
*
|
|
26
|
-
*
|
|
23
|
+
* Initializes telemetry for a SDK instance if the runtime environment
|
|
24
|
+
* qualifies. The environment is resolved by `getTelemetryEnvironment()`:
|
|
25
|
+
*
|
|
26
|
+
* - `'development'` — local dev servers (`localhost` / `127.0.0.1`, or
|
|
27
|
+
* Node with `NODE_ENV=development`). This is the original opt-in
|
|
28
|
+
* surface.
|
|
29
|
+
* - `'production'` — apps deployed to Sanity-controlled domains
|
|
30
|
+
* (e.g. `*.sanity.studio`, the dashboard). End users are
|
|
31
|
+
* authenticated Sanity users with Populus consent records, so we
|
|
32
|
+
* apply the same consent gate as the Studio's `telemetry-sink`.
|
|
33
|
+
*
|
|
34
|
+
* Apps on customer-controlled domains return `null` and skip telemetry
|
|
35
|
+
* entirely.
|
|
36
|
+
*
|
|
37
|
+
* `telemetryManager` and `clientStore` are dynamically imported so the
|
|
38
|
+
* telemetry code path stays out of production bundles for apps that
|
|
39
|
+
* don't qualify. Only the lightweight environment check runs at boot.
|
|
27
40
|
*
|
|
28
41
|
* The `projectId` must be passed explicitly because the resource
|
|
29
42
|
* configuration is typically set by the React layer after the
|
|
@@ -32,8 +45,9 @@ const initInFlight = new WeakSet<SanityInstance>()
|
|
|
32
45
|
* @internal
|
|
33
46
|
*/
|
|
34
47
|
export function initTelemetry(instance: SanityInstance, projectId: string): void {
|
|
35
|
-
|
|
36
|
-
|
|
48
|
+
const environment = getTelemetryEnvironment()
|
|
49
|
+
if (!environment) {
|
|
50
|
+
logger.trace('initTelemetry skipped: environment not eligible', {internal: true})
|
|
37
51
|
return
|
|
38
52
|
}
|
|
39
53
|
if (!projectId) {
|
|
@@ -45,7 +59,7 @@ export function initTelemetry(instance: SanityInstance, projectId: string): void
|
|
|
45
59
|
}
|
|
46
60
|
initInFlight.add(instance)
|
|
47
61
|
|
|
48
|
-
logger.debug('initializing telemetry', {projectId})
|
|
62
|
+
logger.debug('initializing telemetry', {projectId, environment})
|
|
49
63
|
|
|
50
64
|
Promise.all([
|
|
51
65
|
import('./telemetryManager'),
|
|
@@ -77,15 +91,23 @@ export function initTelemetry(instance: SanityInstance, projectId: string): void
|
|
|
77
91
|
cleanup.unsubscribe()
|
|
78
92
|
resolve(false)
|
|
79
93
|
})
|
|
94
|
+
// The token observable is a `shareReplay({bufferSize: 1, refCount: true})`
|
|
95
|
+
// (see `createStateSourceAction`), so it can deliver a buffered value
|
|
96
|
+
// synchronously while we're still inside `.subscribe(cb)`. At that
|
|
97
|
+
// moment `sub` is in the TDZ, so the callback can't reach it. We
|
|
98
|
+
// gate on a `received` flag and let the post-subscribe block do the
|
|
99
|
+
// unsubscribe in the sync-emission case.
|
|
100
|
+
let received = false
|
|
80
101
|
const sub = getTokenState(instance).observable.subscribe((t) => {
|
|
81
|
-
if (t)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
102
|
+
if (received || !t) return
|
|
103
|
+
received = true
|
|
104
|
+
logger.debug('auth token received')
|
|
105
|
+
unsub()
|
|
106
|
+
resolve(true)
|
|
107
|
+
cleanup.unsubscribe()
|
|
87
108
|
})
|
|
88
109
|
cleanup.unsubscribe = () => sub.unsubscribe()
|
|
110
|
+
if (received) cleanup.unsubscribe()
|
|
89
111
|
})
|
|
90
112
|
if (!hasToken || instance.isDisposed()) {
|
|
91
113
|
initInFlight.delete(instance)
|
|
@@ -98,6 +120,7 @@ export function initTelemetry(instance: SanityInstance, projectId: string): void
|
|
|
98
120
|
sessionId: instance.instanceId,
|
|
99
121
|
getClient: () => getClient(instance, {apiVersion: DEFAULT_TELEMETRY_API_VERSION}),
|
|
100
122
|
projectId,
|
|
123
|
+
environment,
|
|
101
124
|
})
|
|
102
125
|
|
|
103
126
|
const consented = await manager.checkConsent()
|
|
@@ -128,7 +151,7 @@ export function initTelemetry(instance: SanityInstance, projectId: string): void
|
|
|
128
151
|
? 'studio'
|
|
129
152
|
: 'default'
|
|
130
153
|
|
|
131
|
-
logger.info('telemetry session started', {projectId, perspective, authMethod})
|
|
154
|
+
logger.info('telemetry session started', {projectId, perspective, authMethod, environment})
|
|
132
155
|
manager.logSessionStarted({
|
|
133
156
|
projectId,
|
|
134
157
|
perspective,
|
|
@@ -165,7 +188,7 @@ export function getTelemetryManager(instance: SanityInstance): TelemetryManager
|
|
|
165
188
|
* @internal
|
|
166
189
|
*/
|
|
167
190
|
export function trackHookMounted(instance: SanityInstance, hookName: string): void {
|
|
168
|
-
if (!
|
|
191
|
+
if (!getTelemetryEnvironment()) return
|
|
169
192
|
|
|
170
193
|
const manager = findManager(instance)
|
|
171
194
|
if (manager) {
|