@sanity/sdk 2.5.0 → 2.6.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/index.js CHANGED
@@ -17,15 +17,14 @@ import { isKeySegment, isKeyedObject } from "@sanity/types";
17
17
  import { createDocumentLoaderFromClient } from "@sanity/mutate/_unstable_store";
18
18
  import { SDK_CHANNEL_NAME, SDK_NODE_NAME } from "@sanity/message-protocol";
19
19
  import { fromUrl } from "@sanity/bifur-client";
20
- const SOURCE_ID = "__sanity_internal_sourceId";
21
- function datasetSource(projectId, dataset) {
22
- return { [SOURCE_ID]: { projectId, dataset } };
20
+ function isDatasetSource(source) {
21
+ return "projectId" in source && "dataset" in source;
23
22
  }
24
- function mediaLibrarySource(id) {
25
- return { [SOURCE_ID]: ["media-library", id] };
23
+ function isMediaLibrarySource(source) {
24
+ return "mediaLibraryId" in source;
26
25
  }
27
- function canvasSource(id) {
28
- return { [SOURCE_ID]: ["canvas", id] };
26
+ function isCanvasSource(source) {
27
+ return "canvasId" in source;
29
28
  }
30
29
  function getPublishedId(id) {
31
30
  const draftsPrefix = "drafts.";
@@ -38,13 +37,152 @@ function getDraftId(id) {
38
37
  function insecureRandomId() {
39
38
  return Array.from({ length: 16 }, () => Math.floor(Math.random() * 16).toString(16)).join("");
40
39
  }
40
+ const LOG_LEVEL_PRIORITY = {
41
+ error: 0,
42
+ warn: 1,
43
+ info: 2,
44
+ debug: 3,
45
+ trace: 4
46
+ }, DEFAULT_CONFIG = {
47
+ level: "warn",
48
+ namespaces: [],
49
+ internal: !1,
50
+ timestamps: !0,
51
+ enableInProduction: !1,
52
+ handler: {
53
+ // eslint-disable-next-line no-console
54
+ error: console.error.bind(console),
55
+ // eslint-disable-next-line no-console
56
+ warn: console.warn.bind(console),
57
+ // eslint-disable-next-line no-console
58
+ info: console.info.bind(console),
59
+ // eslint-disable-next-line no-console
60
+ debug: console.debug.bind(console),
61
+ // eslint-disable-next-line no-console
62
+ trace: console.debug.bind(console)
63
+ // trace uses console.debug
64
+ }
65
+ };
66
+ function parseDebugEnvVar() {
67
+ if (typeof process > "u" || !process.env?.DEBUG)
68
+ return null;
69
+ const debug = process.env.DEBUG;
70
+ if (!debug.includes("sanity"))
71
+ return null;
72
+ const config = {}, levelMatch = debug.match(/sanity:(trace|debug|info|warn|error):/), hasLevelSpecifier = !!levelMatch;
73
+ if (levelMatch ? config.level = levelMatch[1] : config.level = "debug", debug === "sanity")
74
+ config.namespaces = ["*"];
75
+ else if (hasLevelSpecifier && debug.match(/sanity:(trace|debug|info|warn|error):\*/))
76
+ config.namespaces = ["*"];
77
+ else if (!hasLevelSpecifier && debug.includes("sanity:*"))
78
+ config.namespaces = ["*"];
79
+ else {
80
+ const namespaces = debug.split(",").filter((s) => s.includes("sanity:")).map((s) => {
81
+ const cleaned = s.replace(/^sanity:/, "");
82
+ return hasLevelSpecifier && cleaned.match(/^(trace|debug|info|warn|error):/) ? cleaned.split(":").slice(1).join(":") : cleaned.split(":")[0];
83
+ }).filter(Boolean).filter((ns) => ns !== "*");
84
+ namespaces.length > 0 && (config.namespaces = namespaces);
85
+ }
86
+ return debug.includes(":internal") && (config.internal = !0), config;
87
+ }
88
+ const envConfig = parseDebugEnvVar();
89
+ let globalConfig = {
90
+ ...DEFAULT_CONFIG,
91
+ ...envConfig ?? {}
92
+ };
93
+ envConfig && (["info", "debug", "trace"].includes(globalConfig.level) || globalConfig.level === "warn") && console.info(
94
+ `[${(/* @__PURE__ */ new Date()).toISOString()}] [INFO] [sdk] Logging auto-configured from DEBUG environment variable`,
95
+ {
96
+ level: globalConfig.level,
97
+ namespaces: globalConfig.namespaces,
98
+ internal: globalConfig.internal,
99
+ source: "env:DEBUG",
100
+ value: typeof process < "u" ? process.env?.DEBUG : void 0
101
+ }
102
+ );
103
+ function configureLogging$1(config) {
104
+ globalConfig = {
105
+ ...globalConfig,
106
+ ...config,
107
+ handler: config.handler ?? globalConfig.handler
108
+ };
109
+ }
110
+ function isLoggingEnabled() {
111
+ return typeof process < "u" && process.env?.NODE_ENV === "production" ? globalConfig.enableInProduction : !0;
112
+ }
113
+ function isNamespaceEnabled(namespace) {
114
+ return isLoggingEnabled() ? globalConfig.namespaces.includes("*") ? !0 : globalConfig.namespaces.includes(namespace) : !1;
115
+ }
116
+ function isLevelEnabled(level) {
117
+ return isLoggingEnabled() ? LOG_LEVEL_PRIORITY[level] <= LOG_LEVEL_PRIORITY[globalConfig.level] : !1;
118
+ }
119
+ function formatMessage(namespace, level, message, context) {
120
+ const parts = [];
121
+ if (globalConfig.timestamps) {
122
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
123
+ parts.push(`[${timestamp}]`);
124
+ }
125
+ parts.push(`[${level.toUpperCase()}]`), parts.push(`[${namespace}]`);
126
+ const instanceContext = context?.instanceContext;
127
+ return instanceContext && (instanceContext.projectId && parts.push(`[project:${instanceContext.projectId}]`), instanceContext.dataset && parts.push(`[dataset:${instanceContext.dataset}]`), instanceContext.instanceId && parts.push(`[instance:${instanceContext.instanceId.slice(0, 8)}]`)), parts.push(message), [parts.join(" "), context];
128
+ }
129
+ function sanitizeContext(context) {
130
+ if (!context || Object.keys(context).length === 0) return;
131
+ const sanitized = { ...context }, sensitiveKeys = ["token", "password", "secret", "apiKey", "authorization"];
132
+ for (const key of Object.keys(sanitized))
133
+ sensitiveKeys.some((sensitive) => key.toLowerCase().includes(sensitive)) && (sanitized[key] = "[REDACTED]");
134
+ return sanitized;
135
+ }
136
+ function createLogger(namespace, baseContext) {
137
+ const logAtLevel = (level, message, context) => {
138
+ if (!isNamespaceEnabled(namespace) || !isLevelEnabled(level) || context?.internal && !globalConfig.internal) return;
139
+ const mergedContext = { ...baseContext, ...context }, sanitized = sanitizeContext(mergedContext), [formatted, finalContext] = formatMessage(namespace, level, message, sanitized);
140
+ globalConfig.handler[level](formatted, finalContext);
141
+ };
142
+ return {
143
+ namespace,
144
+ error: (message, context) => logAtLevel("error", message, context),
145
+ warn: (message, context) => logAtLevel("warn", message, context),
146
+ info: (message, context) => logAtLevel("info", message, context),
147
+ debug: (message, context) => logAtLevel("debug", message, context),
148
+ trace: (message, context) => logAtLevel("trace", message, { ...context, internal: !0 }),
149
+ isLevelEnabled: (level) => isNamespaceEnabled(namespace) && isLevelEnabled(level),
150
+ child: (childContext) => createLogger(namespace, { ...baseContext, ...childContext }),
151
+ getInstanceContext: () => baseContext?.instanceContext
152
+ };
153
+ }
41
154
  function createSanityInstance(config = {}) {
42
- const instanceId = crypto.randomUUID(), disposeListeners = /* @__PURE__ */ new Map(), disposed = { current: !1 }, instance = {
155
+ const instanceId = crypto.randomUUID(), disposeListeners = /* @__PURE__ */ new Map(), disposed = { current: !1 }, instanceContext = {
156
+ instanceId,
157
+ projectId: config.projectId,
158
+ dataset: config.dataset
159
+ }, logger = createLogger("sdk", { instanceContext });
160
+ logger.info("Sanity instance created", {
161
+ hasProjectId: !!config.projectId,
162
+ hasDataset: !!config.dataset,
163
+ hasAuth: !!config.auth,
164
+ hasPerspective: !!config.perspective
165
+ }), logger.debug("Instance configuration", {
166
+ projectId: config.projectId,
167
+ dataset: config.dataset,
168
+ perspective: config.perspective,
169
+ studioMode: config.studioMode?.enabled,
170
+ hasAuthProviders: !!config.auth?.providers,
171
+ hasAuthToken: !!config.auth?.token
172
+ });
173
+ const instance = {
43
174
  instanceId,
44
175
  config,
45
176
  isDisposed: () => disposed.current,
46
177
  dispose: () => {
47
- disposed.current || (disposed.current = !0, disposeListeners.forEach((listener) => listener()), disposeListeners.clear());
178
+ if (disposed.current) {
179
+ logger.trace("Dispose called on already disposed instance", { internal: !0 });
180
+ return;
181
+ }
182
+ logger.trace("Disposing instance", {
183
+ internal: !0,
184
+ listenerCount: disposeListeners.size
185
+ }), disposed.current = !0, disposeListeners.forEach((listener) => listener()), disposeListeners.clear(), logger.info("Instance disposed");
48
186
  },
49
187
  onDispose: (cb) => {
50
188
  const listenerId = insecureRandomId();
@@ -54,14 +192,26 @@ function createSanityInstance(config = {}) {
54
192
  },
55
193
  getParent: () => {
56
194
  },
57
- createChild: (next) => Object.assign(
58
- createSanityInstance({
59
- ...config,
60
- ...next,
61
- ...config.auth === next.auth ? config.auth : config.auth && next.auth && { auth: { ...config.auth, ...next.auth } }
62
- }),
63
- { getParent: () => instance }
64
- ),
195
+ createChild: (next) => {
196
+ logger.debug("Creating child instance", {
197
+ parentInstanceId: instanceId.slice(0, 8),
198
+ overridingProjectId: !!next.projectId,
199
+ overridingDataset: !!next.dataset,
200
+ overridingAuth: !!next.auth
201
+ });
202
+ const child = Object.assign(
203
+ createSanityInstance({
204
+ ...config,
205
+ ...next,
206
+ ...config.auth === next.auth ? config.auth : config.auth && next.auth && { auth: { ...config.auth, ...next.auth } }
207
+ }),
208
+ { getParent: () => instance }
209
+ );
210
+ return logger.trace("Child instance created", {
211
+ internal: !0,
212
+ childInstanceId: child.instanceId.slice(0, 8)
213
+ }), child;
214
+ },
65
215
  match: (targetConfig) => {
66
216
  if (Object.entries(pick(targetConfig, "auth", "projectId", "dataset")).every(
67
217
  ([key, value]) => config[key] === value
@@ -131,9 +281,9 @@ const bindActionByDataset = createActionBinder((instance, options) => {
131
281
  return { name: `${projectId}.${dataset}`, projectId, dataset };
132
282
  }), bindActionBySource = createActionBinder((instance, { source }) => {
133
283
  if (source) {
134
- const id = source[SOURCE_ID];
135
- if (!id) throw new Error("Invalid source (missing ID information)");
136
- return Array.isArray(id) ? { name: id.join(":") } : { name: `${id.projectId}.${id.dataset}` };
284
+ let id;
285
+ if (isDatasetSource(source) ? id = `${source.projectId}.${source.dataset}` : isMediaLibrarySource(source) ? id = `media-library:${source.mediaLibraryId}` : isCanvasSource(source) && (id = `canvas:${source.canvasId}`), !id) throw new Error(`Received invalid source: ${JSON.stringify(source)}`);
286
+ return { name: id };
137
287
  }
138
288
  const { projectId, dataset } = instance.config;
139
289
  if (!projectId || !dataset)
@@ -673,12 +823,12 @@ const authStore = {
673
823
  `The client options provided contains unsupported properties: ${listFormatter.format(disallowedKeys)}. Allowed keys are: ${listFormatter.format(allowedKeys)}.`
674
824
  );
675
825
  }
676
- const tokenFromState = state.get().token, { clients, authMethod } = state.get(), hasSource = !!options.source;
677
- let sourceId = options.source?.[SOURCE_ID], resource;
678
- Array.isArray(sourceId) && (resource = { type: sourceId[0], id: sourceId[1] }, sourceId = void 0);
826
+ const tokenFromState = state.get().token, { clients, authMethod } = state.get();
827
+ let resource;
828
+ options.source && (isDatasetSource(options.source) ? resource = { type: "dataset", id: `${options.source.projectId}.${options.source.dataset}` } : isMediaLibrarySource(options.source) ? resource = { type: "media-library", id: options.source.mediaLibraryId } : isCanvasSource(options.source) && (resource = { type: "canvas", id: options.source.canvasId }));
679
829
  const projectId = options.projectId ?? instance.config.projectId, dataset = options.dataset ?? instance.config.dataset, apiHost = options.apiHost ?? instance.config.auth?.apiHost, effectiveOptions = {
680
830
  ...DEFAULT_CLIENT_CONFIG,
681
- ...(options.scope === "global" || !projectId || hasSource) && { useProjectHostname: !1 },
831
+ ...(options.scope === "global" || !projectId || resource) && { useProjectHostname: !1 },
682
832
  token: authMethod === "cookie" ? void 0 : tokenFromState ?? void 0,
683
833
  ...options,
684
834
  ...projectId && { projectId },
@@ -686,7 +836,7 @@ const authStore = {
686
836
  ...apiHost && { apiHost },
687
837
  ...resource && { "~experimental_resource": resource }
688
838
  };
689
- hasSource && ((options.projectId || options.dataset) && console.warn(
839
+ resource && ((options.projectId || options.dataset) && console.warn(
690
840
  "Both source and explicit projectId/dataset are provided. The source will be used and projectId/dataset will be ignored."
691
841
  ), delete effectiveOptions.projectId, delete effectiveOptions.dataset), effectiveOptions.token === null || typeof effectiveOptions.token > "u" ? (delete effectiveOptions.token, authMethod === "cookie" && (effectiveOptions.withCredentials = !0)) : delete effectiveOptions.withCredentials;
692
842
  const key = getClientConfigKey(effectiveOptions);
@@ -1144,6 +1294,21 @@ function createProjectHandle(handle) {
1144
1294
  function createDatasetHandle(handle) {
1145
1295
  return handle;
1146
1296
  }
1297
+ function configureLogging(config) {
1298
+ configureLogging$1(config);
1299
+ const configLevel = config.level || "warn", shouldLog = ["info", "debug", "trace"].includes(configLevel) || configLevel === "warn";
1300
+ shouldLog && config.handler?.info ? config.handler.info(`[${(/* @__PURE__ */ new Date()).toISOString()}] [INFO] [sdk] Logging configured`, {
1301
+ level: configLevel,
1302
+ namespaces: config.namespaces || [],
1303
+ internal: config.internal || !1,
1304
+ source: "programmatic"
1305
+ }) : shouldLog && console.info(`[${(/* @__PURE__ */ new Date()).toISOString()}] [INFO] [sdk] Logging configured`, {
1306
+ level: configLevel,
1307
+ namespaces: config.namespaces || [],
1308
+ internal: config.internal || !1,
1309
+ source: "programmatic"
1310
+ });
1311
+ }
1147
1312
  const API_VERSION$4 = "v2025-02-19", datasets = createFetcherStore({
1148
1313
  name: "Datasets",
1149
1314
  getKey: (instance, options) => {
@@ -4122,7 +4287,7 @@ function getCorsErrorProjectId(error) {
4122
4287
  const projMatch = (error.message || "").match(/manage\/project\/([^/?#]+)/);
4123
4288
  return projMatch ? projMatch[1] : null;
4124
4289
  }
4125
- var version = "2.5.0";
4290
+ var version = "2.6.0";
4126
4291
  const CORE_SDK_VERSION = getEnv("PKG_VERSION") || `${version}-development`;
4127
4292
  export {
4128
4293
  AuthStateType,
@@ -4133,7 +4298,7 @@ export {
4133
4298
  agentTransform,
4134
4299
  agentTranslate,
4135
4300
  applyDocumentActions,
4136
- canvasSource,
4301
+ configureLogging,
4137
4302
  createDatasetHandle,
4138
4303
  createDocument,
4139
4304
  createDocumentHandle,
@@ -4141,7 +4306,6 @@ export {
4141
4306
  createGroqSearchFilter,
4142
4307
  createProjectHandle,
4143
4308
  createSanityInstance,
4144
- datasetSource,
4145
4309
  defineIntent,
4146
4310
  deleteDocument,
4147
4311
  destroyController,
@@ -4183,12 +4347,14 @@ export {
4183
4347
  getUsersKey,
4184
4348
  getUsersState,
4185
4349
  handleAuthCallback,
4350
+ isCanvasSource,
4351
+ isDatasetSource,
4352
+ isMediaLibrarySource,
4186
4353
  isProjectUserNotFoundClientError,
4187
4354
  joinPaths,
4188
4355
  jsonMatch2 as jsonMatch,
4189
4356
  loadMoreUsers,
4190
4357
  logout,
4191
- mediaLibrarySource,
4192
4358
  observeOrganizationVerificationState,
4193
4359
  parseQueryKey,
4194
4360
  parseUsersKey,