@notionhq/client 5.9.0 → 5.11.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.
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- var _Client_auth, _Client_logLevel, _Client_logger, _Client_prefixUrl, _Client_timeoutMs, _Client_notionVersion, _Client_fetch, _Client_agent, _Client_userAgent;
13
+ var _Client_auth, _Client_logLevel, _Client_logger, _Client_prefixUrl, _Client_timeoutMs, _Client_notionVersion, _Client_fetch, _Client_agent, _Client_userAgent, _Client_maxRetries, _Client_initialRetryDelayMs, _Client_maxRetryDelayMs;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  const logging_1 = require("./logging");
16
16
  const errors_1 = require("./errors");
@@ -19,7 +19,7 @@ const api_endpoints_1 = require("./api-endpoints");
19
19
  const package_json_1 = require("../package.json");
20
20
  class Client {
21
21
  constructor(options) {
22
- var _a, _b, _c, _d, _e, _f;
22
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
23
23
  _Client_auth.set(this, void 0);
24
24
  _Client_logLevel.set(this, void 0);
25
25
  _Client_logger.set(this, void 0);
@@ -29,6 +29,9 @@ class Client {
29
29
  _Client_fetch.set(this, void 0);
30
30
  _Client_agent.set(this, void 0);
31
31
  _Client_userAgent.set(this, void 0);
32
+ _Client_maxRetries.set(this, void 0);
33
+ _Client_initialRetryDelayMs.set(this, void 0);
34
+ _Client_maxRetryDelayMs.set(this, void 0);
32
35
  /*
33
36
  * Notion API endpoints
34
37
  */
@@ -37,6 +40,7 @@ class Client {
37
40
  * Retrieve block
38
41
  */
39
42
  retrieve: (args) => {
43
+ this.warnUnknownParams(args, api_endpoints_1.getBlock);
40
44
  return this.request({
41
45
  path: api_endpoints_1.getBlock.path(args),
42
46
  method: api_endpoints_1.getBlock.method,
@@ -49,6 +53,7 @@ class Client {
49
53
  * Update block
50
54
  */
51
55
  update: (args) => {
56
+ this.warnUnknownParams(args, api_endpoints_1.updateBlock);
52
57
  return this.request({
53
58
  path: api_endpoints_1.updateBlock.path(args),
54
59
  method: api_endpoints_1.updateBlock.method,
@@ -61,6 +66,7 @@ class Client {
61
66
  * Delete block
62
67
  */
63
68
  delete: (args) => {
69
+ this.warnUnknownParams(args, api_endpoints_1.deleteBlock);
64
70
  return this.request({
65
71
  path: api_endpoints_1.deleteBlock.path(args),
66
72
  method: api_endpoints_1.deleteBlock.method,
@@ -74,6 +80,7 @@ class Client {
74
80
  * Append block children
75
81
  */
76
82
  append: (args) => {
83
+ this.warnUnknownParams(args, api_endpoints_1.appendBlockChildren);
77
84
  return this.request({
78
85
  path: api_endpoints_1.appendBlockChildren.path(args),
79
86
  method: api_endpoints_1.appendBlockChildren.method,
@@ -86,6 +93,7 @@ class Client {
86
93
  * Retrieve block children
87
94
  */
88
95
  list: (args) => {
96
+ this.warnUnknownParams(args, api_endpoints_1.listBlockChildren);
89
97
  return this.request({
90
98
  path: api_endpoints_1.listBlockChildren.path(args),
91
99
  method: api_endpoints_1.listBlockChildren.method,
@@ -101,6 +109,7 @@ class Client {
101
109
  * Retrieve a database
102
110
  */
103
111
  retrieve: (args) => {
112
+ this.warnUnknownParams(args, api_endpoints_1.getDatabase);
104
113
  return this.request({
105
114
  path: api_endpoints_1.getDatabase.path(args),
106
115
  method: api_endpoints_1.getDatabase.method,
@@ -113,6 +122,7 @@ class Client {
113
122
  * Create a database
114
123
  */
115
124
  create: (args) => {
125
+ this.warnUnknownParams(args, api_endpoints_1.createDatabase);
116
126
  return this.request({
117
127
  path: api_endpoints_1.createDatabase.path(),
118
128
  method: api_endpoints_1.createDatabase.method,
@@ -125,6 +135,7 @@ class Client {
125
135
  * Update a database
126
136
  */
127
137
  update: (args) => {
138
+ this.warnUnknownParams(args, api_endpoints_1.updateDatabase);
128
139
  return this.request({
129
140
  path: api_endpoints_1.updateDatabase.path(args),
130
141
  method: api_endpoints_1.updateDatabase.method,
@@ -139,6 +150,7 @@ class Client {
139
150
  * Retrieve a data source
140
151
  */
141
152
  retrieve: (args) => {
153
+ this.warnUnknownParams(args, api_endpoints_1.getDataSource);
142
154
  return this.request({
143
155
  path: api_endpoints_1.getDataSource.path(args),
144
156
  method: api_endpoints_1.getDataSource.method,
@@ -151,6 +163,7 @@ class Client {
151
163
  * Query a data source
152
164
  */
153
165
  query: (args) => {
166
+ this.warnUnknownParams(args, api_endpoints_1.queryDataSource);
154
167
  return this.request({
155
168
  path: api_endpoints_1.queryDataSource.path(args),
156
169
  method: api_endpoints_1.queryDataSource.method,
@@ -163,6 +176,7 @@ class Client {
163
176
  * Create a data source
164
177
  */
165
178
  create: (args) => {
179
+ this.warnUnknownParams(args, api_endpoints_1.createDataSource);
166
180
  return this.request({
167
181
  path: api_endpoints_1.createDataSource.path(),
168
182
  method: api_endpoints_1.createDataSource.method,
@@ -175,6 +189,7 @@ class Client {
175
189
  * Update a data source
176
190
  */
177
191
  update: (args) => {
192
+ this.warnUnknownParams(args, api_endpoints_1.updateDataSource);
178
193
  return this.request({
179
194
  path: api_endpoints_1.updateDataSource.path(args),
180
195
  method: api_endpoints_1.updateDataSource.method,
@@ -187,6 +202,7 @@ class Client {
187
202
  * List page templates that are available for a data source
188
203
  */
189
204
  listTemplates: (args) => {
205
+ this.warnUnknownParams(args, api_endpoints_1.listDataSourceTemplates);
190
206
  return this.request({
191
207
  path: api_endpoints_1.listDataSourceTemplates.path(args),
192
208
  method: api_endpoints_1.listDataSourceTemplates.method,
@@ -201,6 +217,7 @@ class Client {
201
217
  * Create a page
202
218
  */
203
219
  create: (args) => {
220
+ this.warnUnknownParams(args, api_endpoints_1.createPage);
204
221
  return this.request({
205
222
  path: api_endpoints_1.createPage.path(),
206
223
  method: api_endpoints_1.createPage.method,
@@ -213,6 +230,7 @@ class Client {
213
230
  * Retrieve a page
214
231
  */
215
232
  retrieve: (args) => {
233
+ this.warnUnknownParams(args, api_endpoints_1.getPage);
216
234
  return this.request({
217
235
  path: api_endpoints_1.getPage.path(args),
218
236
  method: api_endpoints_1.getPage.method,
@@ -225,6 +243,7 @@ class Client {
225
243
  * Update page properties
226
244
  */
227
245
  update: (args) => {
246
+ this.warnUnknownParams(args, api_endpoints_1.updatePage);
228
247
  return this.request({
229
248
  path: api_endpoints_1.updatePage.path(args),
230
249
  method: api_endpoints_1.updatePage.method,
@@ -237,6 +256,7 @@ class Client {
237
256
  * Move a page
238
257
  */
239
258
  move: (args) => {
259
+ this.warnUnknownParams(args, api_endpoints_1.movePage);
240
260
  return this.request({
241
261
  path: api_endpoints_1.movePage.path(args),
242
262
  method: api_endpoints_1.movePage.method,
@@ -245,11 +265,36 @@ class Client {
245
265
  auth: args === null || args === void 0 ? void 0 : args.auth,
246
266
  });
247
267
  },
268
+ /**
269
+ * Retrieve a page as markdown
270
+ */
271
+ retrieveMarkdown: (args) => {
272
+ return this.request({
273
+ path: api_endpoints_1.getPageMarkdown.path(args),
274
+ method: api_endpoints_1.getPageMarkdown.method,
275
+ query: (0, utils_1.pick)(args, api_endpoints_1.getPageMarkdown.queryParams),
276
+ body: (0, utils_1.pick)(args, api_endpoints_1.getPageMarkdown.bodyParams),
277
+ auth: args === null || args === void 0 ? void 0 : args.auth,
278
+ });
279
+ },
280
+ /**
281
+ * Update a page's content as markdown
282
+ */
283
+ updateMarkdown: (args) => {
284
+ return this.request({
285
+ path: api_endpoints_1.updatePageMarkdown.path(args),
286
+ method: api_endpoints_1.updatePageMarkdown.method,
287
+ query: (0, utils_1.pick)(args, api_endpoints_1.updatePageMarkdown.queryParams),
288
+ body: (0, utils_1.pick)(args, api_endpoints_1.updatePageMarkdown.bodyParams),
289
+ auth: args === null || args === void 0 ? void 0 : args.auth,
290
+ });
291
+ },
248
292
  properties: {
249
293
  /**
250
294
  * Retrieve page property
251
295
  */
252
296
  retrieve: (args) => {
297
+ this.warnUnknownParams(args, api_endpoints_1.getPageProperty);
253
298
  return this.request({
254
299
  path: api_endpoints_1.getPageProperty.path(args),
255
300
  method: api_endpoints_1.getPageProperty.method,
@@ -265,6 +310,7 @@ class Client {
265
310
  * Retrieve a user
266
311
  */
267
312
  retrieve: (args) => {
313
+ this.warnUnknownParams(args, api_endpoints_1.getUser);
268
314
  return this.request({
269
315
  path: api_endpoints_1.getUser.path(args),
270
316
  method: api_endpoints_1.getUser.method,
@@ -277,6 +323,7 @@ class Client {
277
323
  * List all users
278
324
  */
279
325
  list: (args) => {
326
+ this.warnUnknownParams(args, api_endpoints_1.listUsers);
280
327
  return this.request({
281
328
  path: api_endpoints_1.listUsers.path(),
282
329
  method: api_endpoints_1.listUsers.method,
@@ -289,6 +336,7 @@ class Client {
289
336
  * Get details about bot
290
337
  */
291
338
  me: (args) => {
339
+ this.warnUnknownParams(args, api_endpoints_1.getSelf);
292
340
  return this.request({
293
341
  path: api_endpoints_1.getSelf.path(),
294
342
  method: api_endpoints_1.getSelf.method,
@@ -303,6 +351,7 @@ class Client {
303
351
  * Create a comment
304
352
  */
305
353
  create: (args) => {
354
+ this.warnUnknownParams(args, api_endpoints_1.createComment);
306
355
  return this.request({
307
356
  path: api_endpoints_1.createComment.path(),
308
357
  method: api_endpoints_1.createComment.method,
@@ -315,6 +364,7 @@ class Client {
315
364
  * List comments
316
365
  */
317
366
  list: (args) => {
367
+ this.warnUnknownParams(args, api_endpoints_1.listComments);
318
368
  return this.request({
319
369
  path: api_endpoints_1.listComments.path(),
320
370
  method: api_endpoints_1.listComments.method,
@@ -327,6 +377,7 @@ class Client {
327
377
  * Retrieve a comment
328
378
  */
329
379
  retrieve: (args) => {
380
+ this.warnUnknownParams(args, api_endpoints_1.getComment);
330
381
  return this.request({
331
382
  path: api_endpoints_1.getComment.path(args),
332
383
  method: api_endpoints_1.getComment.method,
@@ -341,6 +392,7 @@ class Client {
341
392
  * Create a file upload
342
393
  */
343
394
  create: (args) => {
395
+ this.warnUnknownParams(args, api_endpoints_1.createFileUpload);
344
396
  return this.request({
345
397
  path: api_endpoints_1.createFileUpload.path(),
346
398
  method: api_endpoints_1.createFileUpload.method,
@@ -353,6 +405,7 @@ class Client {
353
405
  * Retrieve a file upload
354
406
  */
355
407
  retrieve: (args) => {
408
+ this.warnUnknownParams(args, api_endpoints_1.getFileUpload);
356
409
  return this.request({
357
410
  path: api_endpoints_1.getFileUpload.path(args),
358
411
  method: api_endpoints_1.getFileUpload.method,
@@ -364,6 +417,7 @@ class Client {
364
417
  * List file uploads
365
418
  */
366
419
  list: (args) => {
420
+ this.warnUnknownParams(args, api_endpoints_1.listFileUploads);
367
421
  return this.request({
368
422
  path: api_endpoints_1.listFileUploads.path(),
369
423
  method: api_endpoints_1.listFileUploads.method,
@@ -386,6 +440,7 @@ class Client {
386
440
  * This endpoint sends HTTP multipart/form-data instead of JSON parameters.
387
441
  */
388
442
  send: (args) => {
443
+ this.warnUnknownParams(args, api_endpoints_1.sendFileUpload);
389
444
  return this.request({
390
445
  path: api_endpoints_1.sendFileUpload.path(args),
391
446
  method: api_endpoints_1.sendFileUpload.method,
@@ -398,6 +453,7 @@ class Client {
398
453
  * Complete a file upload
399
454
  */
400
455
  complete: (args) => {
456
+ this.warnUnknownParams(args, api_endpoints_1.completeFileUpload);
401
457
  return this.request({
402
458
  path: api_endpoints_1.completeFileUpload.path(args),
403
459
  method: api_endpoints_1.completeFileUpload.method,
@@ -410,6 +466,7 @@ class Client {
410
466
  * Search
411
467
  */
412
468
  this.search = (args) => {
469
+ this.warnUnknownParams(args, api_endpoints_1.search);
413
470
  return this.request({
414
471
  path: api_endpoints_1.search.path(),
415
472
  method: api_endpoints_1.search.method,
@@ -474,6 +531,16 @@ class Client {
474
531
  __classPrivateFieldSet(this, _Client_fetch, (_f = options === null || options === void 0 ? void 0 : options.fetch) !== null && _f !== void 0 ? _f : fetch, "f");
475
532
  __classPrivateFieldSet(this, _Client_agent, options === null || options === void 0 ? void 0 : options.agent, "f");
476
533
  __classPrivateFieldSet(this, _Client_userAgent, `notionhq-client/${package_json_1.version}`, "f");
534
+ if ((options === null || options === void 0 ? void 0 : options.retry) === false) {
535
+ __classPrivateFieldSet(this, _Client_maxRetries, 0, "f");
536
+ __classPrivateFieldSet(this, _Client_initialRetryDelayMs, 0, "f");
537
+ __classPrivateFieldSet(this, _Client_maxRetryDelayMs, 0, "f");
538
+ }
539
+ else {
540
+ __classPrivateFieldSet(this, _Client_maxRetries, (_h = (_g = options === null || options === void 0 ? void 0 : options.retry) === null || _g === void 0 ? void 0 : _g.maxRetries) !== null && _h !== void 0 ? _h : 2, "f");
541
+ __classPrivateFieldSet(this, _Client_initialRetryDelayMs, (_k = (_j = options === null || options === void 0 ? void 0 : options.retry) === null || _j === void 0 ? void 0 : _j.initialRetryDelayMs) !== null && _k !== void 0 ? _k : 1000, "f");
542
+ __classPrivateFieldSet(this, _Client_maxRetryDelayMs, (_m = (_l = options === null || options === void 0 ? void 0 : options.retry) === null || _l === void 0 ? void 0 : _l.maxRetryDelayMs) !== null && _m !== void 0 ? _m : 60000, "f");
543
+ }
477
544
  }
478
545
  /**
479
546
  * Sends a request.
@@ -482,10 +549,22 @@ class Client {
482
549
  const { path, method, query, body, formDataParams, auth } = args;
483
550
  (0, errors_1.validateRequestPath)(path);
484
551
  this.log(logging_1.LogLevel.INFO, "request start", { method, path });
485
- // If the body is empty, don't send the body in the HTTP request
486
- const bodyAsJsonString = !body || Object.entries(body).length === 0
487
- ? undefined
488
- : JSON.stringify(body);
552
+ const url = this.buildRequestUrl(path, query);
553
+ const bodyAsJsonString = this.serializeBody(body);
554
+ const headers = this.buildRequestHeaders(args.headers, auth, bodyAsJsonString);
555
+ const formData = this.buildFormData(formDataParams, headers);
556
+ return this.executeWithRetry({
557
+ url,
558
+ method,
559
+ path,
560
+ headers,
561
+ body: bodyAsJsonString !== null && bodyAsJsonString !== void 0 ? bodyAsJsonString : formData,
562
+ });
563
+ }
564
+ /**
565
+ * Builds the full URL with query parameters.
566
+ */
567
+ buildRequestUrl(path, query) {
489
568
  const url = new URL(`${__classPrivateFieldGet(this, _Client_prefixUrl, "f")}${path}`);
490
569
  if (query) {
491
570
  for (const [key, value] of Object.entries(query)) {
@@ -501,25 +580,24 @@ class Client {
501
580
  }
502
581
  }
503
582
  }
504
- // Allow both client ID / client secret based auth as well as token based auth.
505
- let authorizationHeader;
506
- if (typeof auth === "object") {
507
- // Client ID and secret based auth is **ONLY** supported when using the
508
- // `/oauth/token` endpoint. If this is the case, handle formatting the
509
- // authorization header as required by `Basic` auth.
510
- const unencodedCredential = `${auth.client_id}:${auth.client_secret}`;
511
- const encodedCredential = Buffer.from(unencodedCredential).toString("base64");
512
- authorizationHeader = { authorization: `Basic ${encodedCredential}` };
513
- }
514
- else {
515
- // Otherwise format authorization header as `Bearer` token auth.
516
- authorizationHeader = this.authAsHeaders(auth);
583
+ return url;
584
+ }
585
+ /**
586
+ * Serializes the request body to JSON string if non-empty.
587
+ */
588
+ serializeBody(body) {
589
+ if (!body || Object.entries(body).length === 0) {
590
+ return undefined;
517
591
  }
592
+ return JSON.stringify(body);
593
+ }
594
+ /**
595
+ * Builds the request headers including auth and content-type.
596
+ */
597
+ buildRequestHeaders(customHeaders, auth, bodyAsJsonString) {
598
+ const authorizationHeader = this.buildAuthHeader(auth);
518
599
  const headers = {
519
- // Request-level custom additional headers can be provided, but
520
- // don't allow them to override all other headers, e.g. the
521
- // standard user agent.
522
- ...args.headers,
600
+ ...customHeaders,
523
601
  ...authorizationHeader,
524
602
  "Notion-Version": __classPrivateFieldGet(this, _Client_notionVersion, "f"),
525
603
  "user-agent": __classPrivateFieldGet(this, _Client_userAgent, "f"),
@@ -527,61 +605,230 @@ class Client {
527
605
  if (bodyAsJsonString !== undefined) {
528
606
  headers["content-type"] = "application/json";
529
607
  }
530
- let formData;
531
- if (formDataParams) {
532
- delete headers["content-type"];
533
- formData = new FormData();
534
- for (const [key, value] of Object.entries(formDataParams)) {
535
- if (typeof value === "string") {
536
- formData.append(key, value);
608
+ return headers;
609
+ }
610
+ /**
611
+ * Builds the authorization header based on auth type.
612
+ */
613
+ buildAuthHeader(auth) {
614
+ if (typeof auth === "object") {
615
+ const unencodedCredential = `${auth.client_id}:${auth.client_secret}`;
616
+ const encodedCredential = Buffer.from(unencodedCredential).toString("base64");
617
+ return { authorization: `Basic ${encodedCredential}` };
618
+ }
619
+ return this.authAsHeaders(auth);
620
+ }
621
+ /**
622
+ * Builds FormData from form parameters if provided.
623
+ * Also removes content-type header to let fetch set the boundary.
624
+ */
625
+ buildFormData(formDataParams, headers) {
626
+ if (!formDataParams) {
627
+ return undefined;
628
+ }
629
+ delete headers["content-type"];
630
+ const formData = new FormData();
631
+ for (const [key, value] of Object.entries(formDataParams)) {
632
+ if (typeof value === "string") {
633
+ formData.append(key, value);
634
+ }
635
+ else if (typeof value === "object") {
636
+ formData.append(key, typeof value.data === "object" ? value.data : new Blob([value.data]), value.filename);
637
+ }
638
+ }
639
+ return formData;
640
+ }
641
+ /**
642
+ * Executes the request with retry logic.
643
+ */
644
+ async executeWithRetry(args) {
645
+ const { url, method, path, headers, body } = args;
646
+ let attempt = 0;
647
+ // eslint-disable-next-line no-constant-condition
648
+ while (true) {
649
+ try {
650
+ return await this.executeSingleRequest({
651
+ url,
652
+ method,
653
+ path,
654
+ headers,
655
+ body,
656
+ });
657
+ }
658
+ catch (error) {
659
+ if (!(0, errors_1.isNotionClientError)(error)) {
660
+ throw error;
537
661
  }
538
- else if (typeof value === "object") {
539
- formData.append(key, typeof value.data === "object"
540
- ? value.data
541
- : new Blob([value.data]), value.filename);
662
+ this.logRequestError(error, attempt);
663
+ if (attempt < __classPrivateFieldGet(this, _Client_maxRetries, "f") && this.canRetry(error, method)) {
664
+ const delayMs = this.calculateRetryDelay(error, attempt);
665
+ this.log(logging_1.LogLevel.INFO, "retrying request", {
666
+ method,
667
+ path,
668
+ attempt: attempt + 1,
669
+ delayMs,
670
+ });
671
+ await this.sleep(delayMs);
672
+ attempt++;
673
+ continue;
542
674
  }
675
+ throw error;
543
676
  }
544
677
  }
545
- try {
546
- const response = await errors_1.RequestTimeoutError.rejectAfterTimeout(__classPrivateFieldGet(this, _Client_fetch, "f").call(this, url.toString(), {
547
- method: method.toUpperCase(),
548
- headers,
549
- body: bodyAsJsonString !== null && bodyAsJsonString !== void 0 ? bodyAsJsonString : formData,
550
- agent: __classPrivateFieldGet(this, _Client_agent, "f"),
551
- }), __classPrivateFieldGet(this, _Client_timeoutMs, "f"));
552
- const responseText = await response.text();
553
- if (!response.ok) {
554
- throw (0, errors_1.buildRequestError)(response, responseText);
555
- }
556
- const responseJson = JSON.parse(responseText);
557
- this.log(logging_1.LogLevel.INFO, "request success", {
558
- method,
559
- path,
560
- ...("request_id" in responseJson && responseJson.request_id
561
- ? { requestId: responseJson.request_id }
562
- : {}),
678
+ }
679
+ /**
680
+ * Executes a single HTTP request (no retry).
681
+ */
682
+ async executeSingleRequest(args) {
683
+ const { url, method, path, headers, body } = args;
684
+ const response = await errors_1.RequestTimeoutError.rejectAfterTimeout(__classPrivateFieldGet(this, _Client_fetch, "f").call(this, url.toString(), {
685
+ method: method.toUpperCase(),
686
+ headers,
687
+ body,
688
+ agent: __classPrivateFieldGet(this, _Client_agent, "f"),
689
+ }), __classPrivateFieldGet(this, _Client_timeoutMs, "f"));
690
+ const responseText = await response.text();
691
+ if (!response.ok) {
692
+ throw (0, errors_1.buildRequestError)(response, responseText);
693
+ }
694
+ const responseJson = JSON.parse(responseText);
695
+ this.log(logging_1.LogLevel.INFO, "request success", {
696
+ method,
697
+ path,
698
+ ...this.extractRequestId(responseJson),
699
+ });
700
+ return responseJson;
701
+ }
702
+ /**
703
+ * Logs a request error with appropriate detail level.
704
+ */
705
+ logRequestError(error, attempt) {
706
+ this.log(logging_1.LogLevel.WARN, "request fail", {
707
+ code: error.code,
708
+ message: error.message,
709
+ attempt,
710
+ ...this.extractRequestId(error),
711
+ });
712
+ if ((0, errors_1.isHTTPResponseError)(error)) {
713
+ this.log(logging_1.LogLevel.DEBUG, "failed response body", {
714
+ body: error.body,
563
715
  });
564
- return responseJson;
565
716
  }
566
- catch (error) {
567
- if (!(0, errors_1.isNotionClientError)(error)) {
568
- throw error;
717
+ }
718
+ /**
719
+ * Extracts request_id from an object if present.
720
+ */
721
+ extractRequestId(obj) {
722
+ if (obj &&
723
+ typeof obj === "object" &&
724
+ "request_id" in obj &&
725
+ typeof obj.request_id === "string") {
726
+ return { requestId: obj.request_id };
727
+ }
728
+ return {};
729
+ }
730
+ /**
731
+ * Determines if an error can be retried based on its error code and method.
732
+ * Rate limits (429) are always retryable since the server explicitly asks us
733
+ * to retry. Server errors (500, 503) are only retried for idempotent methods
734
+ * (GET, DELETE) to avoid duplicate side effects.
735
+ */
736
+ canRetry(error, method) {
737
+ if (!errors_1.APIResponseError.isAPIResponseError(error)) {
738
+ return false;
739
+ }
740
+ // Rate limits are always retryable - server says "try again later"
741
+ if (error.code === errors_1.APIErrorCode.RateLimited) {
742
+ return true;
743
+ }
744
+ // Server errors only retry for idempotent methods
745
+ const isIdempotent = method === "get" || method === "delete";
746
+ if (isIdempotent) {
747
+ return (error.code === errors_1.APIErrorCode.InternalServerError ||
748
+ error.code === errors_1.APIErrorCode.ServiceUnavailable);
749
+ }
750
+ return false;
751
+ }
752
+ /**
753
+ * Calculates the delay before the next retry attempt.
754
+ * Uses retry-after header if present, otherwise exponential back-off with
755
+ * jitter.
756
+ */
757
+ calculateRetryDelay(error, attempt) {
758
+ // Try to get retry-after from the error headers
759
+ if (errors_1.APIResponseError.isAPIResponseError(error)) {
760
+ const retryAfterMs = this.parseRetryAfterHeader(error.headers);
761
+ if (retryAfterMs !== undefined) {
762
+ return Math.min(retryAfterMs, __classPrivateFieldGet(this, _Client_maxRetryDelayMs, "f"));
569
763
  }
570
- // Log the error if it's one of our known error types
571
- this.log(logging_1.LogLevel.WARN, "request fail", {
572
- code: error.code,
573
- message: error.message,
574
- ...("request_id" in error && error.request_id
575
- ? { requestId: error.request_id }
576
- : {}),
764
+ }
765
+ // Exponential back-off with full jitter
766
+ const baseDelay = __classPrivateFieldGet(this, _Client_initialRetryDelayMs, "f") * Math.pow(2, attempt);
767
+ const jitter = Math.random();
768
+ return Math.min(baseDelay * jitter + baseDelay / 2, __classPrivateFieldGet(this, _Client_maxRetryDelayMs, "f"));
769
+ }
770
+ /**
771
+ * Parses the retry-after header value.
772
+ * Supports both delta-seconds (e.g., "120") and HTTP-date formats.
773
+ * Returns the delay in milliseconds, or undefined if not present or invalid.
774
+ */
775
+ parseRetryAfterHeader(headers) {
776
+ var _a, _b;
777
+ if (!headers) {
778
+ return undefined;
779
+ }
780
+ let retryAfterValue = null;
781
+ // Handle Headers object (standard fetch API)
782
+ if (typeof headers === "object" && "get" in headers) {
783
+ const headersObj = headers;
784
+ retryAfterValue = headersObj.get("retry-after");
785
+ }
786
+ // Handle plain object
787
+ else if (typeof headers === "object") {
788
+ const headersRecord = headers;
789
+ retryAfterValue =
790
+ (_b = (_a = headersRecord["retry-after"]) !== null && _a !== void 0 ? _a : headersRecord["Retry-After"]) !== null && _b !== void 0 ? _b : null;
791
+ }
792
+ if (!retryAfterValue) {
793
+ return undefined;
794
+ }
795
+ // Try parsing as delta-seconds (integer)
796
+ const seconds = parseInt(retryAfterValue, 10);
797
+ if (!isNaN(seconds) && seconds >= 0) {
798
+ return seconds * 1000;
799
+ }
800
+ // Try parsing as HTTP-date
801
+ const date = Date.parse(retryAfterValue);
802
+ if (!isNaN(date)) {
803
+ const delayMs = date - Date.now();
804
+ return delayMs > 0 ? delayMs : 0;
805
+ }
806
+ return undefined;
807
+ }
808
+ sleep(ms) {
809
+ return new Promise(resolve => setTimeout(resolve, ms));
810
+ }
811
+ /**
812
+ * Logs a warning when the caller passes parameters that are not recognized
813
+ * by the endpoint definition. This helps catch typos and renamed parameters
814
+ * (e.g. `archived` vs `in_trash` for `databases.update`) that would
815
+ * otherwise be silently dropped by `pick()`.
816
+ */
817
+ warnUnknownParams(args, endpoint) {
818
+ var _a;
819
+ if (!args || typeof args !== "object")
820
+ return;
821
+ const unknownKeys = (0, utils_1.getUnknownParams)(args, endpoint);
822
+ if (unknownKeys.length > 0) {
823
+ this.log(logging_1.LogLevel.WARN, "unknown parameters were ignored", {
824
+ unknownParams: unknownKeys,
825
+ knownParams: [
826
+ ...endpoint.pathParams,
827
+ ...endpoint.queryParams,
828
+ ...endpoint.bodyParams,
829
+ ...((_a = endpoint.formDataParams) !== null && _a !== void 0 ? _a : []),
830
+ ],
577
831
  });
578
- if ((0, errors_1.isHTTPResponseError)(error)) {
579
- // The response body may contain sensitive information so it is logged separately at the DEBUG level
580
- this.log(logging_1.LogLevel.DEBUG, "failed response body", {
581
- body: error.body,
582
- });
583
- }
584
- throw error;
585
832
  }
586
833
  }
587
834
  /**
@@ -613,7 +860,7 @@ class Client {
613
860
  return headers;
614
861
  }
615
862
  }
616
- _Client_auth = new WeakMap(), _Client_logLevel = new WeakMap(), _Client_logger = new WeakMap(), _Client_prefixUrl = new WeakMap(), _Client_timeoutMs = new WeakMap(), _Client_notionVersion = new WeakMap(), _Client_fetch = new WeakMap(), _Client_agent = new WeakMap(), _Client_userAgent = new WeakMap();
863
+ _Client_auth = new WeakMap(), _Client_logLevel = new WeakMap(), _Client_logger = new WeakMap(), _Client_prefixUrl = new WeakMap(), _Client_timeoutMs = new WeakMap(), _Client_notionVersion = new WeakMap(), _Client_fetch = new WeakMap(), _Client_agent = new WeakMap(), _Client_userAgent = new WeakMap(), _Client_maxRetries = new WeakMap(), _Client_initialRetryDelayMs = new WeakMap(), _Client_maxRetryDelayMs = new WeakMap();
617
864
  Client.defaultNotionVersion = "2025-09-03";
618
865
  exports.default = Client;
619
866
  //# sourceMappingURL=Client.js.map