@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.
Files changed (57) 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 +25 -9
  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 +723 -418
  12. package/dist/index.js.map +1 -1
  13. package/package.json +16 -16
  14. package/src/_exports/index.ts +23 -2
  15. package/src/auth/refreshStampedToken.test.ts +2 -2
  16. package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +116 -0
  17. package/src/auth/subscribeToStateAndFetchCurrentUser.ts +27 -9
  18. package/src/config/sanityConfig.ts +12 -0
  19. package/src/document/actions.test.ts +112 -1
  20. package/src/document/actions.ts +148 -1
  21. package/src/document/applyDocumentActions.ts +4 -3
  22. package/src/document/documentStore.ts +7 -6
  23. package/src/document/events.test.ts +57 -2
  24. package/src/document/events.ts +43 -24
  25. package/src/document/permissions.ts +1 -1
  26. package/src/document/processActions/create.ts +135 -0
  27. package/src/document/processActions/delete.ts +100 -0
  28. package/src/document/processActions/discard.ts +63 -0
  29. package/src/document/processActions/edit.ts +141 -0
  30. package/src/document/processActions/processActions.ts +209 -0
  31. package/src/document/processActions/publish.ts +120 -0
  32. package/src/document/processActions/releaseArchive.ts +77 -0
  33. package/src/document/processActions/releaseCreate.ts +59 -0
  34. package/src/document/processActions/releaseDelete.ts +65 -0
  35. package/src/document/processActions/releaseEdit.ts +36 -0
  36. package/src/document/processActions/releasePublish.ts +45 -0
  37. package/src/document/processActions/releaseSchedule.ts +87 -0
  38. package/src/document/processActions/releaseUtil.ts +31 -0
  39. package/src/document/processActions/shared.ts +139 -0
  40. package/src/document/processActions/unpublish.ts +85 -0
  41. package/src/document/processActions.test.ts +424 -2
  42. package/src/document/reducers.ts +41 -6
  43. package/src/releases/getPerspectiveState.test.ts +1 -1
  44. package/src/releases/releasesStore.test.ts +50 -1
  45. package/src/releases/releasesStore.ts +41 -18
  46. package/src/releases/utils/sortReleases.test.ts +2 -2
  47. package/src/releases/utils/sortReleases.ts +1 -1
  48. package/src/telemetry/environment.test.ts +119 -0
  49. package/src/telemetry/environment.ts +92 -0
  50. package/src/telemetry/{__telemetry__/sdk.telemetry.ts → events.ts} +9 -9
  51. package/src/telemetry/initTelemetry.test.ts +240 -16
  52. package/src/telemetry/initTelemetry.ts +39 -16
  53. package/src/telemetry/telemetryManager.test.ts +129 -65
  54. package/src/telemetry/telemetryManager.ts +41 -29
  55. package/src/document/processActions.ts +0 -735
  56. package/src/telemetry/devMode.test.ts +0 -60
  57. package/src/telemetry/devMode.ts +0 -41
@@ -1,5 +1,5 @@
1
1
  import * as _sanity_client4 from "@sanity/client";
2
- import { ClientConfig, ClientError, ClientPerspective, ListenEvent, MultipleMutationResult, ResponseQueryOptions, SanityClient, SanityDocument, SanityProject, StackablePerspective } from "@sanity/client";
2
+ import { ClientConfig, ClientError, ClientPerspective, ListenEvent, MultipleMutationResult, ReleaseDocument, ReleaseDocument as ReleaseDocument$1, ResponseQueryOptions, SanityClient, SanityDocument, SanityProject, StackablePerspective } from "@sanity/client";
3
3
  import { CurrentUser, CurrentUser as CurrentUser$1, Mutation, PatchOperations, Role, SanityDocument as SanityDocument$1, SanityDocument as SanityDocument$3, SanityDocumentLike } from "@sanity/types";
4
4
  import { Observable, Subject } from "rxjs";
5
5
  import * as _sanity_comlink3 from "@sanity/comlink";
@@ -179,6 +179,17 @@ interface DocumentTypeHandle<TDocumentType extends string = string, TDataset ext
179
179
  interface DocumentHandle<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string> extends DocumentTypeHandle<TDocumentType, TDataset, TProjectId> {
180
180
  documentId: string;
181
181
  }
182
+ /**
183
+ * Identifies a release within a Sanity dataset and project. `releaseId` is the
184
+ * `name` parameter on the release document (e.g. `{name: 'r41035a4'}`).
185
+ * The underlying release document ID is `_.releases.<releaseId>`.
186
+ * It's also the `id` parameter sent to the Actions API.
187
+ * (This type doesn't need to have ProjectId / Dataset generics since it's always the same shape)
188
+ * @beta
189
+ */
190
+ interface ReleaseHandle extends DatasetHandle {
191
+ releaseId: string;
192
+ }
182
193
  /**
183
194
  * Represents the complete configuration for a Sanity SDK instance
184
195
  * @public
@@ -1061,6 +1072,12 @@ interface DiscardDocumentAction<TDocumentType extends string = string, TDataset
1061
1072
  * @beta
1062
1073
  */
1063
1074
  type DocumentAction<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string> = CreateDocumentAction<TDocumentType, TDataset, TProjectId> | DeleteDocumentAction<TDocumentType, TDataset, TProjectId> | EditDocumentAction<TDocumentType, TDataset, TProjectId> | PublishDocumentAction<TDocumentType, TDataset, TProjectId> | UnpublishDocumentAction<TDocumentType, TDataset, TProjectId> | DiscardDocumentAction<TDocumentType, TDataset, TProjectId>;
1075
+ /**
1076
+ * Union of every action accepted by `applyDocumentActions` — both document-
1077
+ * level actions and release-lifecycle actions.
1078
+ * @beta
1079
+ */
1080
+ type Action<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string> = DocumentAction<TDocumentType, TDataset, TProjectId> | ReleaseAction;
1064
1081
  /**
1065
1082
  * Creates a `CreateDocumentAction` object.
1066
1083
  * @param doc - A handle identifying the document type, dataset, and project. An optional `documentId` can be provided.
@@ -1116,6 +1133,93 @@ declare function unpublishDocument<TDocumentType extends string = string, TDatas
1116
1133
  * @beta
1117
1134
  */
1118
1135
  declare function discardDocument<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string>(doc: DocumentHandle<TDocumentType, TDataset, TProjectId>): DiscardDocumentAction<TDocumentType, TDataset, TProjectId>;
1136
+ /**
1137
+ * Creates a new release. The `releaseId` must be unique within the current
1138
+ * retention period.
1139
+ * @beta
1140
+ */
1141
+ interface CreateReleaseAction extends ReleaseHandle {
1142
+ type: 'release.create';
1143
+ metadata: ReleaseDocument['metadata'];
1144
+ }
1145
+ /**
1146
+ * Patches the metadata of an existing release.
1147
+ * @beta
1148
+ */
1149
+ interface EditReleaseAction extends ReleaseHandle {
1150
+ type: 'release.edit';
1151
+ patch: PatchOperations;
1152
+ }
1153
+ /**
1154
+ * Publishes all version documents in a release.
1155
+ * @beta
1156
+ */
1157
+ interface PublishReleaseAction extends ReleaseHandle {
1158
+ type: 'release.publish';
1159
+ }
1160
+ /**
1161
+ * Schedules a release to be published at the given UTC time. Locks the
1162
+ * version documents server-side until the release is unscheduled or published.
1163
+ * @beta
1164
+ */
1165
+ interface ScheduleReleaseAction extends ReleaseHandle {
1166
+ type: 'release.schedule';
1167
+ publishAt: string;
1168
+ }
1169
+ /**
1170
+ * Unschedules a release that was previously scheduled, returning it to the
1171
+ * active editable state.
1172
+ * @beta
1173
+ */
1174
+ interface UnscheduleReleaseAction extends ReleaseHandle {
1175
+ type: 'release.unschedule';
1176
+ }
1177
+ /**
1178
+ * Archives an active release. Version documents within the release are
1179
+ * removed and no longer queryable, though still recoverable through history
1180
+ * during the retention period.
1181
+ * @beta
1182
+ */
1183
+ interface ArchiveReleaseAction extends ReleaseHandle {
1184
+ type: 'release.archive';
1185
+ }
1186
+ /**
1187
+ * Restores an archived release. Only possible during the retention period.
1188
+ * @beta
1189
+ */
1190
+ interface UnarchiveReleaseAction extends ReleaseHandle {
1191
+ type: 'release.unarchive';
1192
+ }
1193
+ /**
1194
+ * Permanently deletes an archived or published release. To remove an active
1195
+ * release, use the archive action first.
1196
+ * @beta
1197
+ */
1198
+ interface DeleteReleaseAction extends ReleaseHandle {
1199
+ type: 'release.delete';
1200
+ }
1201
+ /**
1202
+ * Union of all release actions that can be dispatched alongside document
1203
+ * actions through `applyDocumentActions`.
1204
+ * @beta
1205
+ */
1206
+ type ReleaseAction = CreateReleaseAction | EditReleaseAction | PublishReleaseAction | ScheduleReleaseAction | UnscheduleReleaseAction | ArchiveReleaseAction | UnarchiveReleaseAction | DeleteReleaseAction;
1207
+ /** @beta */
1208
+ declare function createRelease(handle: ReleaseHandle, metadata?: ReleaseDocument['metadata']): CreateReleaseAction;
1209
+ /** @beta */
1210
+ declare function editRelease(handle: ReleaseHandle, patch: PatchOperations): EditReleaseAction;
1211
+ /** @beta */
1212
+ declare function publishRelease(handle: ReleaseHandle): PublishReleaseAction;
1213
+ /** @beta */
1214
+ declare function scheduleRelease(handle: ReleaseHandle, publishAt: string): ScheduleReleaseAction;
1215
+ /** @beta */
1216
+ declare function unscheduleRelease(handle: ReleaseHandle): UnscheduleReleaseAction;
1217
+ /** @beta */
1218
+ declare function archiveRelease(handle: ReleaseHandle): ArchiveReleaseAction;
1219
+ /** @beta */
1220
+ declare function unarchiveRelease(handle: ReleaseHandle): UnarchiveReleaseAction;
1221
+ /** @beta */
1222
+ declare function deleteRelease(handle: ReleaseHandle): DeleteReleaseAction;
1119
1223
  /**
1120
1224
  * Represents a reactive state source that provides synchronized access to store data
1121
1225
  *
@@ -1333,11 +1437,26 @@ type ActionMap = {
1333
1437
  delete: 'sanity.action.document.delete';
1334
1438
  edit: 'sanity.action.document.edit';
1335
1439
  publish: 'sanity.action.document.publish';
1440
+ releaseCreate: 'sanity.action.release.create';
1441
+ releaseEdit: 'sanity.action.release.edit';
1442
+ releasePublish: 'sanity.action.release.publish';
1443
+ releaseSchedule: 'sanity.action.release.schedule';
1444
+ releaseUnschedule: 'sanity.action.release.unschedule';
1445
+ releaseArchive: 'sanity.action.release.archive';
1446
+ releaseUnarchive: 'sanity.action.release.unarchive';
1447
+ releaseDelete: 'sanity.action.release.delete';
1336
1448
  };
1337
1449
  type OptimisticLock = {
1338
1450
  ifDraftRevisionId?: string;
1339
1451
  ifPublishedRevisionId?: string;
1340
1452
  };
1453
+ interface ReleaseMetadataPayload {
1454
+ title?: string;
1455
+ description?: string;
1456
+ intendedPublishAt?: string;
1457
+ releaseType?: 'asap' | 'scheduled' | 'undecided';
1458
+ cardinality?: 'one' | 'many';
1459
+ }
1341
1460
  type HttpAction = {
1342
1461
  actionType: ActionMap['create'];
1343
1462
  publishedId: string;
@@ -1363,7 +1482,34 @@ type HttpAction = {
1363
1482
  actionType: ActionMap['publish'];
1364
1483
  draftId: string;
1365
1484
  publishedId: string;
1366
- } & OptimisticLock);
1485
+ } & OptimisticLock) | {
1486
+ actionType: ActionMap['releaseCreate'];
1487
+ releaseId: string;
1488
+ metadata?: ReleaseMetadataPayload;
1489
+ } | {
1490
+ actionType: ActionMap['releaseEdit'];
1491
+ releaseId: string;
1492
+ patch: PatchOperations;
1493
+ } | {
1494
+ actionType: ActionMap['releasePublish'];
1495
+ releaseId: string;
1496
+ } | {
1497
+ actionType: ActionMap['releaseSchedule'];
1498
+ releaseId: string;
1499
+ publishAt: string;
1500
+ } | {
1501
+ actionType: ActionMap['releaseUnschedule'];
1502
+ releaseId: string;
1503
+ } | {
1504
+ actionType: ActionMap['releaseArchive'];
1505
+ releaseId: string;
1506
+ } | {
1507
+ actionType: ActionMap['releaseUnarchive'];
1508
+ releaseId: string;
1509
+ } | {
1510
+ actionType: ActionMap['releaseDelete'];
1511
+ releaseId: string;
1512
+ };
1367
1513
  /**
1368
1514
  * Represents a transaction that is queued to be applied but has not yet been
1369
1515
  * applied. A transaction will remain in a queued state until all required
@@ -1379,7 +1525,7 @@ interface QueuedTransaction {
1379
1525
  * actions don't mention draft IDs and is meant to abstract away the draft
1380
1526
  * model from users.
1381
1527
  */
1382
- actions: DocumentAction[];
1528
+ actions: Action[];
1383
1529
  /**
1384
1530
  * An optional flag set to disable this transaction from being batched with
1385
1531
  * other transactions.
@@ -1568,9 +1714,10 @@ interface ActionsResult<TDocument extends SanityDocument$2 = SanityDocument$2> {
1568
1714
  /** @beta */
1569
1715
  interface ApplyDocumentActionsOptions {
1570
1716
  /**
1571
- * List of actions to apply.
1717
+ * List of actions to apply. Accepts both document actions and release
1718
+ * lifecycle actions because they share the same transaction pipeline.
1572
1719
  */
1573
- actions: DocumentAction[];
1720
+ actions: Action[];
1574
1721
  /**
1575
1722
  * The resource to which the documents being acted on belong.
1576
1723
  */
@@ -1588,6 +1735,12 @@ interface ApplyDocumentActionsOptions {
1588
1735
  declare function applyDocumentActions<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string>(instance: SanityInstance, options: ApplyDocumentActionsOptions): Promise<ActionsResult<SanityDocument$2<TDocumentType, `${TProjectId}.${TDataset}`>>>;
1589
1736
  /** @beta */
1590
1737
  declare function applyDocumentActions(instance: SanityInstance, options: ApplyDocumentActionsOptions): Promise<ActionsResult>;
1738
+ /**
1739
+ * Returns the full release document ID for the given release name.
1740
+ * e.g. `getReleaseDocumentId('my-release') === '_.releases.my-release'`
1741
+ * @beta
1742
+ */
1743
+ declare function getReleaseDocumentId(releaseId: string): string;
1591
1744
  /**
1592
1745
  * @public
1593
1746
  */
@@ -2156,22 +2309,14 @@ declare function resolveQuery<TQuery extends string = string, TDataset extends s
2156
2309
  /** @beta */
2157
2310
  declare function resolveQuery<TData>(instance: SanityInstance, queryOptions: ResolveQueryOptions): Promise<TData>;
2158
2311
  /**
2159
- * Represents a document in a Sanity dataset that represents release options.
2160
- * @internal
2312
+ * Lifecycle states a release document can be in. Mirrors the server's
2313
+ * `ReleaseState`.
2314
+ * @beta
2161
2315
  */
2162
- type ReleaseDocument = SanityDocument$1 & {
2163
- name: string;
2164
- publishAt?: string;
2165
- state: 'active' | 'scheduled';
2166
- metadata: {
2167
- title: string;
2168
- releaseType: 'asap' | 'scheduled' | 'undecided';
2169
- intendedPublishAt?: string;
2170
- description?: string;
2171
- };
2172
- };
2316
+ type ReleaseState = 'active' | 'archiving' | 'unarchiving' | 'archived' | 'published' | 'publishing' | 'scheduled' | 'scheduling';
2173
2317
  interface ReleasesStoreState {
2174
2318
  activeReleases?: ReleaseDocument[];
2319
+ allReleases?: ReleaseDocument[];
2175
2320
  error?: unknown;
2176
2321
  }
2177
2322
  /**
@@ -2181,6 +2326,13 @@ interface ReleasesStoreState {
2181
2326
  declare const getActiveReleasesState: (instance: SanityInstance, options?: {
2182
2327
  resource?: DocumentResource;
2183
2328
  }) => StateSource<ReleaseDocument[] | undefined>;
2329
+ /**
2330
+ * Get every release in the store, including archived and published.
2331
+ * @internal
2332
+ */
2333
+ declare const getAllReleasesState: (instance: SanityInstance, options?: {
2334
+ resource?: DocumentResource;
2335
+ }) => StateSource<ReleaseDocument[] | undefined>;
2184
2336
  declare const _getPerspectiveStateSelector: StoreAction<ReleasesStoreState, [_?: (PerspectiveHandle & {
2185
2337
  projectId?: string;
2186
2338
  dataset?: string;
@@ -2619,4 +2771,4 @@ declare function getClientErrorApiType(error: ClientError): string | undefined;
2619
2771
  declare function getClientErrorApiDescription(error: ClientError): string | undefined;
2620
2772
  /** @internal True if the error represents a projectUserNotFoundError. */
2621
2773
  declare function isProjectUserNotFoundClientError(error: ClientError): boolean;
2622
- export { ProjectsOptions as $, NodeState as $n, ReleasePerspective as $r, getFavoritesState as $t, isImportError as A, DocumentAction as An, AgentTranslateOptions as Ar, TransportEvent as At, loadMoreUsers as B, getDatasetsState as Bn, CanvasResource$1 as Br, UsersGroupState as Bt, getIndexForKey as C, PermissionDeniedReason as Cn, AgentGenerateResult as Cr, ProjectionValuePending as Ct, slicePath as D, CreateDocumentAction as Dn, AgentPromptResult as Dr, PresenceLocation as Dt, jsonMatch as E, StateSource as En, AgentPromptOptions as Er, DisconnectEvent as Et, createGroqSearchFilter as F, deleteDocument as Fn, agentTransform as Fr, ResolveUserOptions as Ft, getPerspectiveState as G, LogLevel as Gn, DocumentHandle as Gr, resolveOrganizations as Gt, resolveUsers as H, configureLogging as Hn, DatasetHandle as Hr, Organizations as Ht, FetcherStore as I, discardDocument as In, agentTranslate as Ir, ResolveUsersOptions as It, QueryOptions as J, LoggerConfig as Jn, DocumentTypeHandle as Jr, OrganizationMember as Jt, ReleaseDocument as K, LogNamespace as Kn, DocumentResource as Kr, Organization as Kt, FetcherStoreState as L, editDocument as Ln, SanityInstance as Lr, SanityUser as Lt, Intent as M, PublishDocumentAction as Mn, agentGenerate as Mr, GetUserOptions as Mt, IntentFilter as N, UnpublishDocumentAction as Nn, agentPatch as Nr, GetUsersOptions as Nt, stringifyPath as O, DeleteDocumentAction as On, AgentTransformOptions as Or, RollCallEvent as Ot, defineIntent as P, createDocument as Pn, agentPrompt as Pr, Membership as Pt, resolveQuery as Q, createProjectHandle as Qn, ProjectHandle as Qr, FavoriteStatusResponse as Qt, getUserState as R, publishDocument as Rn, createSanityInstance as Rr, SanityUserResponse as Rt, SanityProject$1 as S, DocumentPermissionsResult as Sn, AgentGenerateOptions as Sr, ValuePending as St, joinPaths as T, Selector as Tn, AgentPatchResult as Tr, getPresence as Tt, getUsersKey as U, InstanceContext as Un, DatasetResource as Ur, OrganizationsOptions as Ut, resolveUser as V, resolveDatasets as Vn, CanvasSource as Vr, UsersStoreState as Vt, parseUsersKey as W, LogContext as Wn, DatasetSource as Wr, getOrganizationsState as Wt, getQueryState as X, createDocumentHandle as Xn, MediaLibrarySource as Xr, getOrganizationState as Xt, getQueryKey as Y, createDatasetHandle as Yn, MediaLibraryResource as Yr, OrganizationOptions as Yt, parseQueryKey as Z, createDocumentTypeHandle as Zn, PerspectiveHandle as Zr, resolveOrganization as Zt, getTokenState as _, getDocumentSyncStatus as _n, logout as _r, getPreviewState as _t, isProjectUserNotFoundClientError as a, isDatasetResource as ai, DocumentCreatedEvent as an, destroyController as ar, ProjectBase as at, Role as b, resolvePermissions as bn, OrgVerificationResult as br, PreviewStoreState as bt, ErrorAuthState as c, isMediaLibrarySource as ci, DocumentEditedEvent as cn, releaseChannel as cr, ProjectMetadata as ct, LoggingInAuthState as d, DocumentTransactionSubmissionResult as dn, RequestNewTokenMessage as dr, resolveProject as dt, SanityConfig as ei, resolveFavoritesState as en, getNodeState as er, getProjectsState as et, getAuthState as f, DocumentUnpublishedEvent as fn, WindowMessage as fr, ResolvePreviewOptions as ft, getLoginUrlState as g, getDocumentState as gn, getClientState as gr, GetPreviewStateOptions as gt, getIsInDashboardState as h, DocumentOptions as hn, getClient as hr, PREVIEW_PROJECTION as ht, getClientErrorApiType as i, isCanvasSource as ii, ActionErrorEvent as in, ComlinkControllerState as ir, Project as it, getCorsErrorProjectId as j, EditDocumentAction as jn, AgentTranslateResult as jr, UserPresence as jt, CORE_SDK_VERSION as k, DiscardDocumentAction as kn, AgentTransformResult as kr, StateEvent as kt, LoggedInAuthState as l, AuthConfig as li, DocumentEvent as ln, FrameMessage as lr, ProjectOptions as lt, getDashboardOrganizationId as m, TransactionRevertedEvent as mn, ClientStoreState as mr, transformProjectionToPreview as mt, getClientErrorApiBody as n, TokenSource as ni, ApplyDocumentActionsOptions as nn, getOrCreateNode as nr, resolveProjection as nt, AuthState as o, isDatasetSource as oi, DocumentDeletedEvent as on, getOrCreateChannel as or, ProjectMember as ot, getCurrentUserState as p, TransactionAcceptedEvent as pn, ClientOptions as pr, resolvePreview as pt, getActiveReleasesState as q, Logger as qn, DocumentSource as qr, OrganizationBase as qt, getClientErrorApiDescription as r, isCanvasResource as ri, applyDocumentActions as rn, releaseNode as rr, getProjectionState as rt, AuthStoreState as s, isMediaLibraryResource as si, DocumentDiscardedEvent as sn, getOrCreateController as sr, ProjectMemberRole as st, ApiErrorBody as t, StudioConfig as ti, ActionsResult as tn, ComlinkNodeState as tr, resolveProjects as tt, LoggedOutAuthState as u, AuthProvider as ui, DocumentPublishedEvent as un, NewTokenResponseMessage as ur, getProjectState as ut, setAuthToken as v, getPermissionsState as vn, handleAuthCallback as vr, PreviewMedia as vt, getPathDepth as w, JsonMatch as wn, AgentPatchOptions as wr, ValidProjection as wt, SanityDocument$3 as x, subscribeDocumentEvents as xn, AuthStateType as xr, PreviewValue as xt, CurrentUser$1 as y, resolveDocument as yn, observeOrganizationVerificationState as yr, PreviewQueryResult as yt, getUsersState as z, unpublishDocument as zn, isStudioConfig as zr, UserProfile as zt };
2774
+ export { parseQueryKey as $, editDocument as $n, agentGenerate as $r, resolveOrganization as $t, CORE_SDK_VERSION as A, isMediaLibrarySource as Ai, Action as An, FrameMessage as Ar, RollCallEvent as At, getUsersState as B, PublishDocumentAction as Bn, observeOrganizationVerificationState as Br, SanityUserResponse as Bt, SanityProject$1 as C, StudioConfig as Ci, resolvePermissions as Cn, getOrCreateNode as Cr, PreviewValue as Ct, jsonMatch as D, isDatasetResource as Di, JsonMatch as Dn, getOrCreateChannel as Dr, getPresence as Dt, joinPaths as E, isCanvasSource as Ei, PermissionDeniedReason as En, destroyController as Er, ValidProjection as Et, defineIntent as F, DeleteReleaseAction as Fn, ClientStoreState as Fr, GetUsersOptions as Ft, parseUsersKey as G, UnpublishDocumentAction as Gn, AgentPatchOptions as Gr, OrganizationsOptions as Gt, resolveUser as H, ReleaseAction as Hn, AuthStateType as Hr, UsersGroupState as Ht, createGroqSearchFilter as I, DiscardDocumentAction as In, getClient as Ir, Membership as It, getActiveReleasesState as J, createDocument as Jn, AgentPromptResult as Jr, Organization as Jt, getPerspectiveState as K, UnscheduleReleaseAction as Kn, AgentPatchResult as Kr, getOrganizationsState as Kt, FetcherStore as L, DocumentAction as Ln, getClientState as Lr, ResolveUserOptions as Lt, getCorsErrorProjectId as M, AuthProvider as Mi, CreateDocumentAction as Mn, RequestNewTokenMessage as Mr, TransportEvent as Mt, Intent as N, CreateReleaseAction as Nn, WindowMessage as Nr, UserPresence as Nt, slicePath as O, isDatasetSource as Oi, Selector as On, getOrCreateController as Or, DisconnectEvent as Ot, IntentFilter as P, DeleteDocumentAction as Pn, ClientOptions as Pr, GetUserOptions as Pt, getQueryState as Q, discardDocument as Qn, AgentTranslateResult as Qr, getOrganizationState as Qt, FetcherStoreState as R, EditDocumentAction as Rn, logout as Rr, ResolveUsersOptions as Rt, SanityDocument$3 as S, SanityConfig as Si, resolveDocument as Sn, ComlinkNodeState as Sr, PreviewStoreState as St, getPathDepth as T, isCanvasResource as Ti, DocumentPermissionsResult as Tn, ComlinkControllerState as Tr, ProjectionValuePending as Tt, resolveUsers as U, ScheduleReleaseAction as Un, AgentGenerateOptions as Ur, UsersStoreState as Ut, loadMoreUsers as V, PublishReleaseAction as Vn, OrgVerificationResult as Vr, UserProfile as Vt, getUsersKey as W, UnarchiveReleaseAction as Wn, AgentGenerateResult as Wr, Organizations as Wt, QueryOptions as X, deleteDocument as Xn, AgentTransformResult as Xr, OrganizationMember as Xt, getAllReleasesState as Y, createRelease as Yn, AgentTransformOptions as Yr, OrganizationBase as Yt, getQueryKey as Z, deleteRelease as Zn, AgentTranslateOptions as Zr, OrganizationOptions as Zt, getTokenState as _, MediaLibrarySource as _i, TransactionRevertedEvent as _n, createDocumentHandle as _r, PREVIEW_PROJECTION as _t, isProjectUserNotFoundClientError as a, createSanityInstance as ai, ApplyDocumentActionsOptions as an, unpublishDocument as ar, getProjectionState as at, ReleaseDocument$1 as b, ReleaseHandle as bi, getDocumentSyncStatus as bn, NodeState as br, PreviewMedia as bt, ErrorAuthState as c, CanvasSource as ci, DocumentCreatedEvent as cn, resolveDatasets as cr, ProjectMember as ct, LoggingInAuthState as d, DatasetSource as di, DocumentEditedEvent as dn, LogContext as dr, ProjectOptions as dt, agentPatch as ei, FavoriteStatusResponse as en, editRelease as er, resolveQuery as et, getAuthState as f, DocumentHandle as fi, DocumentEvent as fn, LogLevel as fr, getProjectState as ft, getLoginUrlState as g, MediaLibraryResource as gi, TransactionAcceptedEvent as gn, createDatasetHandle as gr, transformProjectionToPreview as gt, getIsInDashboardState as h, DocumentTypeHandle as hi, DocumentUnpublishedEvent as hn, LoggerConfig as hr, resolvePreview as ht, getClientErrorApiType as i, SanityInstance as ii, ActionsResult as in, unarchiveRelease as ir, resolveProjection as it, isImportError as j, AuthConfig as ji, ArchiveReleaseAction as jn, NewTokenResponseMessage as jr, StateEvent as jt, stringifyPath as k, isMediaLibraryResource as ki, StateSource as kn, releaseChannel as kr, PresenceLocation as kt, LoggedInAuthState as l, DatasetHandle as li, DocumentDeletedEvent as ln, configureLogging as lr, ProjectMemberRole as lt, getDashboardOrganizationId as m, DocumentSource as mi, DocumentTransactionSubmissionResult as mn, Logger as mr, ResolvePreviewOptions as mt, getClientErrorApiBody as n, agentTransform as ni, resolveFavoritesState as nn, publishRelease as nr, getProjectsState as nt, AuthState as o, isStudioConfig as oi, applyDocumentActions as on, unscheduleRelease as or, Project as ot, getCurrentUserState as p, DocumentResource as pi, DocumentPublishedEvent as pn, LogNamespace as pr, resolveProject as pt, ReleaseState as q, archiveRelease as qn, AgentPromptOptions as qr, resolveOrganizations as qt, getClientErrorApiDescription as r, agentTranslate as ri, getReleaseDocumentId as rn, scheduleRelease as rr, resolveProjects as rt, AuthStoreState as s, CanvasResource$1 as si, ActionErrorEvent as sn, getDatasetsState as sr, ProjectBase as st, ApiErrorBody as t, agentPrompt as ti, getFavoritesState as tn, publishDocument as tr, ProjectsOptions as tt, LoggedOutAuthState as u, DatasetResource as ui, DocumentDiscardedEvent as un, InstanceContext as ur, ProjectMetadata as ut, setAuthToken as v, PerspectiveHandle as vi, DocumentOptions as vn, createDocumentTypeHandle as vr, GetPreviewStateOptions as vt, getIndexForKey as w, TokenSource as wi, subscribeDocumentEvents as wn, releaseNode as wr, ValuePending as wt, Role as x, ReleasePerspective as xi, getPermissionsState as xn, getNodeState as xr, PreviewQueryResult as xt, CurrentUser$1 as y, ProjectHandle as yi, getDocumentState as yn, createProjectHandle as yr, getPreviewState as yt, getUserState as z, EditReleaseAction as zn, handleAuthCallback as zr, SanityUser as zt };
@@ -1,39 +1,51 @@
1
1
  import { createLogger } from "./createGroqSearchFilter.js";
2
2
  import { defineEvent } from "@sanity/telemetry";
3
- function isLocalUrl(win) {
4
- const url = win.location?.href;
5
- return url ? url.startsWith("http://localhost") || url.startsWith("https://localhost") || url.startsWith("http://127.0.0.1") || url.startsWith("https://127.0.0.1") : !1;
3
+ const SANITY_CONTROLLED_HOST_SUFFIXES = [".sanity.studio", ".sanity.io"];
4
+ function getBrowserHostname(win) {
5
+ const hostname = win.location?.hostname;
6
+ return typeof hostname == "string" && hostname.length > 0 ? hostname.toLowerCase() : null;
6
7
  }
7
- function isDevMode() {
8
- return typeof window < "u" ? isLocalUrl(window) : typeof process < "u" && process.env?.NODE_ENV === "development";
8
+ function isLocalHostname(hostname) {
9
+ return hostname === "localhost" || hostname === "127.0.0.1";
9
10
  }
10
- const SDKDevSessionStarted = defineEvent({
11
- name: "SDK Dev Session Started",
11
+ function isSanityControlledHostname(hostname) {
12
+ return SANITY_CONTROLLED_HOST_SUFFIXES.some((suffix) => hostname.endsWith(suffix));
13
+ }
14
+ function getTelemetryEnvironment() {
15
+ if (typeof window < "u") {
16
+ const hostname = getBrowserHostname(window);
17
+ return hostname ? isLocalHostname(hostname) ? "development" : isSanityControlledHostname(hostname) ? "production" : null : null;
18
+ }
19
+ return typeof process < "u" && process.env?.NODE_ENV === "development" ? "development" : null;
20
+ }
21
+ const SDKSessionStarted = defineEvent({
22
+ name: "SDK Session Started",
12
23
  version: 1,
13
- description: "SDK instance created in development mode"
24
+ description: "SDK instance created (environment is recorded in the event context)"
14
25
  }), SDKHookMounted = defineEvent({
15
26
  name: "SDK Hook Mounted",
16
27
  version: 1,
17
28
  description: "An SDK hook was mounted for the first time in this session"
18
- }), SDKDevSessionEnded = defineEvent({
19
- name: "SDK Dev Session Ended",
29
+ }), SDKSessionEnded = defineEvent({
30
+ name: "SDK Session Ended",
20
31
  version: 1,
21
- description: "SDK instance disposed in development mode"
22
- }), SDKDevError = defineEvent({
23
- name: "SDK Dev Error",
32
+ description: "SDK instance disposed (environment is recorded in the event context)"
33
+ }), SDKError = defineEvent({
34
+ name: "SDK Error",
24
35
  version: 1,
25
- description: "Runtime error caught during SDK development"
36
+ description: "Runtime error caught in the SDK"
26
37
  }), DEFAULT_TELEMETRY_API_VERSION = "2024-11-12", logger = createLogger("telemetry"), telemetryManagers = /* @__PURE__ */ new WeakMap(), pendingHooks = /* @__PURE__ */ new WeakMap(), initInFlight = /* @__PURE__ */ new WeakSet();
27
38
  function initTelemetry(instance, projectId) {
28
- if (!isDevMode()) {
29
- logger.trace("initTelemetry skipped: not dev mode", { internal: !0 });
39
+ const environment = getTelemetryEnvironment();
40
+ if (!environment) {
41
+ logger.trace("initTelemetry skipped: environment not eligible", { internal: !0 });
30
42
  return;
31
43
  }
32
44
  if (!projectId) {
33
45
  logger.trace("initTelemetry skipped: no projectId", { internal: !0 });
34
46
  return;
35
47
  }
36
- telemetryManagers.has(instance) || initInFlight.has(instance) || (initInFlight.add(instance), logger.debug("initializing telemetry", { projectId }), Promise.all([
48
+ telemetryManagers.has(instance) || initInFlight.has(instance) || (initInFlight.add(instance), logger.debug("initializing telemetry", { projectId, environment }), Promise.all([
37
49
  import("./telemetryManager.js"),
38
50
  import("./createGroqSearchFilter.js").then(function(n) {
39
51
  return n.clientStore;
@@ -52,10 +64,12 @@ function initTelemetry(instance, projectId) {
52
64
  const cleanup = { unsubscribe: () => {
53
65
  } }, unsub = instance.onDispose(() => {
54
66
  cleanup.unsubscribe(), resolve(!1);
55
- }), sub = getTokenState(instance).observable.subscribe((t) => {
56
- t && (logger.debug("auth token received"), sub.unsubscribe(), unsub(), resolve(!0));
57
67
  });
58
- cleanup.unsubscribe = () => sub.unsubscribe();
68
+ let received = !1;
69
+ const sub = getTokenState(instance).observable.subscribe((t) => {
70
+ received || !t || (received = !0, logger.debug("auth token received"), unsub(), resolve(!0), cleanup.unsubscribe());
71
+ });
72
+ cleanup.unsubscribe = () => sub.unsubscribe(), received && cleanup.unsubscribe();
59
73
  }) || instance.isDisposed())) {
60
74
  initInFlight.delete(instance), logger.debug("telemetry skipped: no token resolved or instance disposed");
61
75
  return;
@@ -63,7 +77,8 @@ function initTelemetry(instance, projectId) {
63
77
  const manager = createTelemetryManager({
64
78
  sessionId: instance.instanceId,
65
79
  getClient: () => getClient(instance, { apiVersion: DEFAULT_TELEMETRY_API_VERSION }),
66
- projectId
80
+ projectId,
81
+ environment
67
82
  }), consented = await manager.checkConsent();
68
83
  if (logger.debug("consent check complete", { consented }), !consented || instance.isDisposed()) {
69
84
  initInFlight.delete(instance), manager.dispose();
@@ -78,7 +93,7 @@ function initTelemetry(instance, projectId) {
78
93
  pendingHooks.delete(instance);
79
94
  }
80
95
  const config = instance.config, perspective = typeof config.perspective == "string" ? config.perspective : "published", authMethod = config.auth?.token ? "token" : config.studio?.auth?.token ? "studio" : "default";
81
- logger.info("telemetry session started", { projectId, perspective, authMethod }), manager.logSessionStarted({
96
+ logger.info("telemetry session started", { projectId, perspective, authMethod, environment }), manager.logSessionStarted({
82
97
  projectId,
83
98
  perspective,
84
99
  authMethod
@@ -93,7 +108,7 @@ function getTelemetryManager(instance) {
93
108
  return telemetryManagers.get(instance);
94
109
  }
95
110
  function trackHookMounted(instance, hookName) {
96
- if (!isDevMode()) return;
111
+ if (!getTelemetryEnvironment()) return;
97
112
  const manager = findManager(instance);
98
113
  if (manager) {
99
114
  logger.trace("hook mounted (logged)", { hookName, internal: !0 }), manager.logHookFirstUsed(hookName);
@@ -107,10 +122,10 @@ function findManager(instance) {
107
122
  return telemetryManagers.get(instance);
108
123
  }
109
124
  export {
110
- SDKDevError,
111
- SDKDevSessionEnded,
112
- SDKDevSessionStarted,
125
+ SDKError,
113
126
  SDKHookMounted,
127
+ SDKSessionEnded,
128
+ SDKSessionStarted,
114
129
  getTelemetryManager,
115
130
  initTelemetry,
116
131
  trackHookMounted
@@ -1 +1 @@
1
- {"version":3,"file":"_internal.js","sources":["../../src/telemetry/devMode.ts","../../src/telemetry/__telemetry__/sdk.telemetry.ts","../../src/telemetry/initTelemetry.ts"],"sourcesContent":["/**\n * Checks whether the current URL points to a local development server.\n *\n * @param win - The window object to check\n * @returns True if running on localhost or 127.0.0.1\n * @internal\n */\nfunction isLocalUrl(win: Window): boolean {\n const url = win.location?.href\n if (!url) return false\n return (\n url.startsWith('http://localhost') ||\n url.startsWith('https://localhost') ||\n url.startsWith('http://127.0.0.1') ||\n url.startsWith('https://127.0.0.1')\n )\n}\n\n/**\n * Determines whether the SDK should enable dev-mode telemetry for the\n * SDK consumer (i.e. a developer building an app with `@sanity/sdk`).\n *\n * Browser: returns true only when the URL is `localhost` or `127.0.0.1`.\n * The URL check is the primary signal because consumer bundlers may or\n * may not forward `NODE_ENV` to the browser reliably.\n *\n * Node (scripts / non-browser): falls back to `NODE_ENV === 'development'`.\n *\n * Bracket-notation `process.env['NODE_ENV']` is used to avoid bundler\n * dead-code replacement.\n *\n * @returns True if the SDK is running in a development environment\n * @internal\n */\nexport function isDevMode(): boolean {\n if (typeof window !== 'undefined') {\n return isLocalUrl(window)\n }\n\n return typeof process !== 'undefined' && process.env?.['NODE_ENV'] === 'development'\n}\n","import {defineEvent} from '@sanity/telemetry'\n\n/** @internal */\nexport const SDKDevSessionStarted = defineEvent<{\n version: string\n projectId: string\n perspective: string\n authMethod: string\n}>({\n name: 'SDK Dev Session Started',\n version: 1,\n description: 'SDK instance created in development mode',\n})\n\n/** @internal */\nexport const SDKHookMounted = defineEvent<{\n hookName: string\n}>({\n name: 'SDK Hook Mounted',\n version: 1,\n description: 'An SDK hook was mounted for the first time in this session',\n})\n\n/** @internal */\nexport const SDKDevSessionEnded = defineEvent<{\n durationSeconds: number\n hooksUsed: string[]\n}>({\n name: 'SDK Dev Session Ended',\n version: 1,\n description: 'SDK instance disposed in development mode',\n})\n\n/** @internal */\nexport const SDKDevError = defineEvent<{\n errorType: string\n hookName: string\n}>({\n name: 'SDK Dev Error',\n version: 1,\n description: 'Runtime error caught during SDK development',\n})\n","import {type SanityInstance} from '../store/createSanityInstance'\nimport {createLogger} from '../utils/logger'\nimport {isDevMode} from './devMode'\nimport {type TelemetryManager} from './telemetryManager'\n\nconst DEFAULT_TELEMETRY_API_VERSION = '2024-11-12'\n\nconst logger = createLogger('telemetry')\n\n/**\n * Per-instance map of active telemetry managers. Allows the React\n * package (and other consumers) to look up the manager for a given\n * instance and log hook usage / errors without importing the full\n * telemetry module themselves.\n *\n * @internal\n */\nconst telemetryManagers = new WeakMap<SanityInstance, TelemetryManager>()\nconst pendingHooks = new WeakMap<SanityInstance, Set<string>>()\nconst initInFlight = new WeakSet<SanityInstance>()\n\n/**\n * Initializes dev-mode telemetry for a SDK instance if the environment\n * qualifies. Both `telemetryManager` and `clientStore` are dynamically\n * imported to avoid circular dependencies and to keep telemetry code\n * out of production bundles via code splitting.\n *\n * The `projectId` must be passed explicitly because the resource\n * configuration is typically set by the React layer after the\n * instance has already been created.\n *\n * @internal\n */\nexport function initTelemetry(instance: SanityInstance, projectId: string): void {\n if (!isDevMode()) {\n logger.trace('initTelemetry skipped: not dev mode', {internal: true})\n return\n }\n if (!projectId) {\n logger.trace('initTelemetry skipped: no projectId', {internal: true})\n return\n }\n if (telemetryManagers.has(instance) || initInFlight.has(instance)) {\n return\n }\n initInFlight.add(instance)\n\n logger.debug('initializing telemetry', {projectId})\n\n Promise.all([\n import('./telemetryManager'),\n import('../client/clientStore'),\n import('../auth/authStore'),\n ])\n .then(async ([{createTelemetryManager}, {getClient}, {getTokenState}]) => {\n if (instance.isDisposed()) {\n initInFlight.delete(instance)\n logger.debug('telemetry skipped: instance disposed before imports resolved')\n return\n }\n\n // Wait for the auth store to resolve a token. The client needs a\n // Bearer token for the consent check and batch POSTs. If the token\n // is already available (e.g. static token config), this resolves\n // immediately. For OAuth/localStorage discovery it waits for the\n // first emission. For unauthenticated apps the promise never\n // resolves, which is fine since telemetry requires auth anyway.\n const token = getTokenState(instance).getCurrent()\n logger.trace('auth token check', {tokenPresent: !!token, internal: true})\n\n if (!token) {\n logger.debug('waiting for auth token')\n const hasToken = await new Promise<boolean>((resolve) => {\n if (instance.isDisposed()) return resolve(false)\n const cleanup = {unsubscribe: () => {}}\n const unsub = instance.onDispose(() => {\n cleanup.unsubscribe()\n resolve(false)\n })\n const sub = getTokenState(instance).observable.subscribe((t) => {\n if (t) {\n logger.debug('auth token received')\n sub.unsubscribe()\n unsub()\n resolve(true)\n }\n })\n cleanup.unsubscribe = () => sub.unsubscribe()\n })\n if (!hasToken || instance.isDisposed()) {\n initInFlight.delete(instance)\n logger.debug('telemetry skipped: no token resolved or instance disposed')\n return\n }\n }\n\n const manager = createTelemetryManager({\n sessionId: instance.instanceId,\n getClient: () => getClient(instance, {apiVersion: DEFAULT_TELEMETRY_API_VERSION}),\n projectId,\n })\n\n const consented = await manager.checkConsent()\n logger.debug('consent check complete', {consented})\n if (!consented || instance.isDisposed()) {\n initInFlight.delete(instance)\n manager.dispose()\n return\n }\n\n initInFlight.delete(instance)\n telemetryManagers.set(instance, manager)\n\n const buffered = pendingHooks.get(instance)\n if (buffered) {\n logger.debug('flushing buffered hooks', {hooks: Array.from(buffered)})\n for (const hookName of buffered) {\n manager.logHookFirstUsed(hookName)\n }\n pendingHooks.delete(instance)\n }\n\n const config = instance.config\n const perspective = typeof config.perspective === 'string' ? config.perspective : 'published'\n const authMethod = config.auth?.token\n ? 'token'\n : config.studio?.auth?.token\n ? 'studio'\n : 'default'\n\n logger.info('telemetry session started', {projectId, perspective, authMethod})\n manager.logSessionStarted({\n projectId,\n perspective,\n authMethod,\n })\n\n instance.onDispose(() => {\n manager.endSession()\n telemetryManagers.delete(instance)\n logger.debug('telemetry session ended')\n })\n })\n .catch((err) => {\n initInFlight.delete(instance)\n logger.warn('telemetry init failed', {error: err})\n })\n}\n\n/**\n * Retrieves the telemetry manager for an instance, if one exists.\n * Returns undefined when telemetry is disabled or not yet initialized.\n *\n * @internal\n */\nexport function getTelemetryManager(instance: SanityInstance): TelemetryManager | undefined {\n return telemetryManagers.get(instance)\n}\n\n/**\n * Record a hook name for an instance. If the telemetry manager is\n * already initialized the event is logged immediately. Otherwise\n * the name is buffered and flushed when init completes.\n *\n * @internal\n */\nexport function trackHookMounted(instance: SanityInstance, hookName: string): void {\n if (!isDevMode()) return\n\n const manager = findManager(instance)\n if (manager) {\n logger.trace('hook mounted (logged)', {hookName, internal: true})\n manager.logHookFirstUsed(hookName)\n return\n }\n\n const root = getRootInstance(instance)\n let hooks = pendingHooks.get(root)\n if (!hooks) {\n hooks = new Set()\n pendingHooks.set(root, hooks)\n }\n if (!hooks.has(hookName)) {\n logger.trace('hook mounted (buffered)', {hookName, internal: true})\n }\n hooks.add(hookName)\n}\n\nfunction findManager(instance: SanityInstance): TelemetryManager | undefined {\n return telemetryManagers.get(instance)\n}\n\nfunction getRootInstance(instance: SanityInstance): SanityInstance {\n return instance\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"_internal.js","sources":["../../src/telemetry/environment.ts","../../src/telemetry/events.ts","../../src/telemetry/initTelemetry.ts"],"sourcesContent":["/**\n * Telemetry environment classification.\n *\n * - `'development'` — the SDK is running on the local developer's machine\n * (`localhost` / `127.0.0.1` in the browser, or `NODE_ENV=development`\n * in Node). These are the original dev-only telemetry sessions.\n *\n * - `'production'` — the SDK is running on a production Sanity-controlled\n * domain (Studio deployments on `*.sanity.studio`, the dashboard on\n * `*.sanity.io`) where end users are authenticated Sanity users with\n * Populus consent records. Production telemetry is gated to this\n * allowlist; SDK apps deployed to customer-controlled domains do not\n * emit telemetry. Staging (`*.sanity.work`) and preview\n * (`*.sanity.dev`) hosts are deliberately excluded.\n *\n * @internal\n */\nexport type TelemetryEnvironment = 'development' | 'production'\n\n/**\n * Hostname suffixes that count as Sanity-controlled for the purposes of\n * production telemetry. A user reaching one of these hosts in the\n * browser is authenticated against Sanity and has a Populus consent\n * record, so we apply the same telemetry rules as the Studio's\n * `telemetry-sink`.\n *\n * The leading `.` is required: it ensures suffix matches hit a real\n * subdomain boundary, so apex matches (`sanity.io`, `sanity.studio`)\n * and lookalikes (`evilsanity.studio`) are excluded. Only production\n * hosts are listed; staging (`*.sanity.work`) and preview\n * (`*.sanity.dev`) hosts are excluded by omission.\n *\n * @internal\n */\nconst SANITY_CONTROLLED_HOST_SUFFIXES = ['.sanity.studio', '.sanity.io'] as const\n\nfunction getBrowserHostname(win: Window): string | null {\n const hostname = win.location?.hostname\n return typeof hostname === 'string' && hostname.length > 0 ? hostname.toLowerCase() : null\n}\n\nfunction isLocalHostname(hostname: string): boolean {\n return hostname === 'localhost' || hostname === '127.0.0.1'\n}\n\nfunction isSanityControlledHostname(hostname: string): boolean {\n return SANITY_CONTROLLED_HOST_SUFFIXES.some((suffix) => hostname.endsWith(suffix))\n}\n\n/**\n * Returns the telemetry environment for the current runtime, or `null`\n * when telemetry should not run at all.\n *\n * Browser:\n * - `localhost` / `127.0.0.1` → `'development'`\n * - host matches the Sanity-controlled allowlist → `'production'`\n * - anything else (customer domains, unknown contexts) → `null`\n *\n * Node (scripts, SSR, tests):\n * - `NODE_ENV=development` → `'development'`\n * - otherwise → `null`. Production-side server runtimes don't carry the\n * browser-authenticated user/consent assumption, so we don't enable\n * them under the production gate.\n *\n * Bracket-notation `process.env['NODE_ENV']` is used to avoid bundler\n * dead-code replacement.\n *\n * @internal\n */\nexport function getTelemetryEnvironment(): TelemetryEnvironment | null {\n if (typeof window !== 'undefined') {\n const hostname = getBrowserHostname(window)\n if (!hostname) return null\n if (isLocalHostname(hostname)) return 'development'\n if (isSanityControlledHostname(hostname)) return 'production'\n return null\n }\n\n if (typeof process !== 'undefined' && process.env?.['NODE_ENV'] === 'development') {\n return 'development'\n }\n return null\n}\n\n/**\n * Convenience predicate for \"telemetry can run in this environment\".\n *\n * @internal\n */\nexport function isTelemetryEnabled(): boolean {\n return getTelemetryEnvironment() !== null\n}\n","import {defineEvent} from '@sanity/telemetry'\n\n/** @internal */\nexport const SDKSessionStarted = defineEvent<{\n version: string\n projectId: string\n perspective: string\n authMethod: string\n}>({\n name: 'SDK Session Started',\n version: 1,\n description: 'SDK instance created (environment is recorded in the event context)',\n})\n\n/** @internal */\nexport const SDKHookMounted = defineEvent<{\n hookName: string\n}>({\n name: 'SDK Hook Mounted',\n version: 1,\n description: 'An SDK hook was mounted for the first time in this session',\n})\n\n/** @internal */\nexport const SDKSessionEnded = defineEvent<{\n durationSeconds: number\n hooksUsed: string[]\n}>({\n name: 'SDK Session Ended',\n version: 1,\n description: 'SDK instance disposed (environment is recorded in the event context)',\n})\n\n/** @internal */\nexport const SDKError = defineEvent<{\n errorType: string\n hookName: string\n}>({\n name: 'SDK Error',\n version: 1,\n description: 'Runtime error caught in the SDK',\n})\n","import {type SanityInstance} from '../store/createSanityInstance'\nimport {createLogger} from '../utils/logger'\nimport {getTelemetryEnvironment} from './environment'\nimport {type TelemetryManager} from './telemetryManager'\n\nconst DEFAULT_TELEMETRY_API_VERSION = '2024-11-12'\n\nconst logger = createLogger('telemetry')\n\n/**\n * Per-instance map of active telemetry managers. Allows the React\n * package (and other consumers) to look up the manager for a given\n * instance and log hook usage / errors without importing the full\n * telemetry module themselves.\n *\n * @internal\n */\nconst telemetryManagers = new WeakMap<SanityInstance, TelemetryManager>()\nconst pendingHooks = new WeakMap<SanityInstance, Set<string>>()\nconst initInFlight = new WeakSet<SanityInstance>()\n\n/**\n * Initializes telemetry for a SDK instance if the runtime environment\n * qualifies. The environment is resolved by `getTelemetryEnvironment()`:\n *\n * - `'development'` — local dev servers (`localhost` / `127.0.0.1`, or\n * Node with `NODE_ENV=development`). This is the original opt-in\n * surface.\n * - `'production'` — apps deployed to Sanity-controlled domains\n * (e.g. `*.sanity.studio`, the dashboard). End users are\n * authenticated Sanity users with Populus consent records, so we\n * apply the same consent gate as the Studio's `telemetry-sink`.\n *\n * Apps on customer-controlled domains return `null` and skip telemetry\n * entirely.\n *\n * `telemetryManager` and `clientStore` are dynamically imported so the\n * telemetry code path stays out of production bundles for apps that\n * don't qualify. Only the lightweight environment check runs at boot.\n *\n * The `projectId` must be passed explicitly because the resource\n * configuration is typically set by the React layer after the\n * instance has already been created.\n *\n * @internal\n */\nexport function initTelemetry(instance: SanityInstance, projectId: string): void {\n const environment = getTelemetryEnvironment()\n if (!environment) {\n logger.trace('initTelemetry skipped: environment not eligible', {internal: true})\n return\n }\n if (!projectId) {\n logger.trace('initTelemetry skipped: no projectId', {internal: true})\n return\n }\n if (telemetryManagers.has(instance) || initInFlight.has(instance)) {\n return\n }\n initInFlight.add(instance)\n\n logger.debug('initializing telemetry', {projectId, environment})\n\n Promise.all([\n import('./telemetryManager'),\n import('../client/clientStore'),\n import('../auth/authStore'),\n ])\n .then(async ([{createTelemetryManager}, {getClient}, {getTokenState}]) => {\n if (instance.isDisposed()) {\n initInFlight.delete(instance)\n logger.debug('telemetry skipped: instance disposed before imports resolved')\n return\n }\n\n // Wait for the auth store to resolve a token. The client needs a\n // Bearer token for the consent check and batch POSTs. If the token\n // is already available (e.g. static token config), this resolves\n // immediately. For OAuth/localStorage discovery it waits for the\n // first emission. For unauthenticated apps the promise never\n // resolves, which is fine since telemetry requires auth anyway.\n const token = getTokenState(instance).getCurrent()\n logger.trace('auth token check', {tokenPresent: !!token, internal: true})\n\n if (!token) {\n logger.debug('waiting for auth token')\n const hasToken = await new Promise<boolean>((resolve) => {\n if (instance.isDisposed()) return resolve(false)\n const cleanup = {unsubscribe: () => {}}\n const unsub = instance.onDispose(() => {\n cleanup.unsubscribe()\n resolve(false)\n })\n // The token observable is a `shareReplay({bufferSize: 1, refCount: true})`\n // (see `createStateSourceAction`), so it can deliver a buffered value\n // synchronously while we're still inside `.subscribe(cb)`. At that\n // moment `sub` is in the TDZ, so the callback can't reach it. We\n // gate on a `received` flag and let the post-subscribe block do the\n // unsubscribe in the sync-emission case.\n let received = false\n const sub = getTokenState(instance).observable.subscribe((t) => {\n if (received || !t) return\n received = true\n logger.debug('auth token received')\n unsub()\n resolve(true)\n cleanup.unsubscribe()\n })\n cleanup.unsubscribe = () => sub.unsubscribe()\n if (received) cleanup.unsubscribe()\n })\n if (!hasToken || instance.isDisposed()) {\n initInFlight.delete(instance)\n logger.debug('telemetry skipped: no token resolved or instance disposed')\n return\n }\n }\n\n const manager = createTelemetryManager({\n sessionId: instance.instanceId,\n getClient: () => getClient(instance, {apiVersion: DEFAULT_TELEMETRY_API_VERSION}),\n projectId,\n environment,\n })\n\n const consented = await manager.checkConsent()\n logger.debug('consent check complete', {consented})\n if (!consented || instance.isDisposed()) {\n initInFlight.delete(instance)\n manager.dispose()\n return\n }\n\n initInFlight.delete(instance)\n telemetryManagers.set(instance, manager)\n\n const buffered = pendingHooks.get(instance)\n if (buffered) {\n logger.debug('flushing buffered hooks', {hooks: Array.from(buffered)})\n for (const hookName of buffered) {\n manager.logHookFirstUsed(hookName)\n }\n pendingHooks.delete(instance)\n }\n\n const config = instance.config\n const perspective = typeof config.perspective === 'string' ? config.perspective : 'published'\n const authMethod = config.auth?.token\n ? 'token'\n : config.studio?.auth?.token\n ? 'studio'\n : 'default'\n\n logger.info('telemetry session started', {projectId, perspective, authMethod, environment})\n manager.logSessionStarted({\n projectId,\n perspective,\n authMethod,\n })\n\n instance.onDispose(() => {\n manager.endSession()\n telemetryManagers.delete(instance)\n logger.debug('telemetry session ended')\n })\n })\n .catch((err) => {\n initInFlight.delete(instance)\n logger.warn('telemetry init failed', {error: err})\n })\n}\n\n/**\n * Retrieves the telemetry manager for an instance, if one exists.\n * Returns undefined when telemetry is disabled or not yet initialized.\n *\n * @internal\n */\nexport function getTelemetryManager(instance: SanityInstance): TelemetryManager | undefined {\n return telemetryManagers.get(instance)\n}\n\n/**\n * Record a hook name for an instance. If the telemetry manager is\n * already initialized the event is logged immediately. Otherwise\n * the name is buffered and flushed when init completes.\n *\n * @internal\n */\nexport function trackHookMounted(instance: SanityInstance, hookName: string): void {\n if (!getTelemetryEnvironment()) return\n\n const manager = findManager(instance)\n if (manager) {\n logger.trace('hook mounted (logged)', {hookName, internal: true})\n manager.logHookFirstUsed(hookName)\n return\n }\n\n const root = getRootInstance(instance)\n let hooks = pendingHooks.get(root)\n if (!hooks) {\n hooks = new Set()\n pendingHooks.set(root, hooks)\n }\n if (!hooks.has(hookName)) {\n logger.trace('hook mounted (buffered)', {hookName, internal: true})\n }\n hooks.add(hookName)\n}\n\nfunction findManager(instance: SanityInstance): TelemetryManager | undefined {\n return telemetryManagers.get(instance)\n}\n\nfunction getRootInstance(instance: SanityInstance): SanityInstance {\n return instance\n}\n"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { createClient, CorsOriginError } from "@sanity/client";
2
- import { Observable, map, distinctUntilChanged, shareReplay, skip, defer, finalize, filter, exhaustMap, timer, switchMap, takeWhile, from, firstValueFrom, EMPTY, fromEvent, catchError, NEVER, first, race, startWith, pairwise, mergeMap, groupBy, of, combineLatest, tap, share } from "rxjs";
2
+ import { Observable, map, distinctUntilChanged, shareReplay, skip, defer, finalize, filter, exhaustMap, timer, switchMap, takeWhile, from, firstValueFrom, catchError, EMPTY, fromEvent, NEVER, first, race, startWith, pairwise, mergeMap, groupBy, of, combineLatest, tap, share } from "rxjs";
3
3
  import { createSelector } from "reselect";
4
4
  import { createImageUrlBuilder } from "@sanity/image-url";
5
5
  import { devtools } from "zustand/middleware";
@@ -547,16 +547,21 @@ const refreshStampedToken = ({ state }) => {
547
547
  uri: "/users/me",
548
548
  method: "GET",
549
549
  tag: "users.get-current"
550
- })
550
+ }).pipe(
551
+ /**
552
+ * Catch inside switchMap so the outer subscription survives.
553
+ * Without this, a 401 terminates the subscription permanently
554
+ * and subsequent token refreshes via comlink never re-fetch /users/me.
555
+ * @see SDK-1409
556
+ */
557
+ catchError((error) => (state.set("setError", { authState: { type: AuthStateType.ERROR, error } }), EMPTY))
558
+ )
551
559
  )
552
560
  ).subscribe({
553
561
  next: (currentUser) => {
554
562
  state.set("setCurrentUser", (prev) => ({
555
563
  authState: prev.authState.type === AuthStateType.LOGGED_IN ? { ...prev.authState, currentUser } : prev.authState
556
564
  }));
557
- },
558
- error: (error) => {
559
- state.set("setError", { authState: { type: AuthStateType.ERROR, error } });
560
565
  }
561
566
  });
562
567
  };
@@ -1126,7 +1131,8 @@ function sortReleases(releases = []) {
1126
1131
  const ARCHIVED_RELEASE_STATES = ["archived", "published"], STABLE_EMPTY_RELEASES = [], releasesStore = {
1127
1132
  name: "Releases",
1128
1133
  getInitialState: () => ({
1129
- activeReleases: void 0
1134
+ activeReleases: void 0,
1135
+ allReleases: void 0
1130
1136
  }),
1131
1137
  initialize: (context) => {
1132
1138
  const subscription = subscribeToReleases(context);
@@ -1140,7 +1146,12 @@ const ARCHIVED_RELEASE_STATES = ["archived", "published"], STABLE_EMPTY_RELEASES
1140
1146
  ), getActiveReleasesState = (instance, options) => (
1141
1147
  // bindActionByResource keyFn destructures { resource } from the first param, so pass {} when no options
1142
1148
  _getActiveReleasesState(instance, options ?? {})
1143
- ), RELEASES_QUERY = "releases::all()", subscribeToReleases = ({
1149
+ ), _getAllReleasesState = bindActionByResource(
1150
+ releasesStore,
1151
+ createStateSourceAction({
1152
+ selector: ({ state }, _) => state.allReleases
1153
+ })
1154
+ ), getAllReleasesState = (instance, options) => _getAllReleasesState(instance, options ?? {}), RELEASES_QUERY = "releases::all()", subscribeToReleases = ({
1144
1155
  instance,
1145
1156
  state,
1146
1157
  key: { resource }
@@ -1153,8 +1164,12 @@ const ARCHIVED_RELEASE_STATES = ["archived", "published"], STABLE_EMPTY_RELEASES
1153
1164
  });
1154
1165
  return releases$.pipe(
1155
1166
  map((releases) => {
1156
- state.set("setActiveReleases", {
1157
- activeReleases: sortReleases(releases ?? STABLE_EMPTY_RELEASES).filter((release) => !ARCHIVED_RELEASE_STATES.includes(release.state)).reverse()
1167
+ const sorted = sortReleases(releases ?? STABLE_EMPTY_RELEASES).reverse();
1168
+ state.set("setReleases", {
1169
+ allReleases: sorted,
1170
+ activeReleases: sorted.filter(
1171
+ (release) => !ARCHIVED_RELEASE_STATES.includes(release.state)
1172
+ )
1158
1173
  });
1159
1174
  })
1160
1175
  ).subscribe({ error: (error) => state.set("setError", { error }) });
@@ -1482,6 +1497,7 @@ export {
1482
1497
  createStateSourceAction,
1483
1498
  defineStore,
1484
1499
  getActiveReleasesState,
1500
+ getAllReleasesState,
1485
1501
  getAuthCode,
1486
1502
  getAuthState,
1487
1503
  getCleanedUrl,