diff --git a/packages/notion-client/package.json b/packages/notion-client/package.json index 40af8d7fa..cf6a458bd 100644 --- a/packages/notion-client/package.json +++ b/packages/notion-client/package.json @@ -22,10 +22,11 @@ "test": "ava" }, "dependencies": { - "got": "^11.8.1", + "node-fetch": "^3.2.6", "notion-types": "^6.12.6", "notion-utils": "^6.12.9", - "p-map": "^5.3.0" + "p-map": "^5.3.0", + "undici": "^5.5.1" }, "ava": { "snapshotDir": ".snapshots", diff --git a/packages/notion-client/src/notion-api.ts b/packages/notion-client/src/notion-api.ts index 74e23efc7..a57e4ef71 100644 --- a/packages/notion-client/src/notion-api.ts +++ b/packages/notion-client/src/notion-api.ts @@ -1,5 +1,5 @@ // import { promises as fs } from 'fs' -import got, { OptionsOfJSONResponseBody } from 'got' + import pMap from 'p-map' import { @@ -12,6 +12,8 @@ import * as notion from 'notion-types' import * as types from './types' +type Fetch = typeof fetch | typeof import('node-fetch').default + /** * Main Notion API client. */ @@ -20,6 +22,7 @@ export class NotionAPI { private readonly _authToken?: string private readonly _activeUser?: string private readonly _userTimeZone: string + private fetchImplementation: Fetch constructor({ apiBaseUrl = 'https://www.notion.so/api/v3', @@ -48,7 +51,7 @@ export class NotionAPI { signFileUrls = true, chunkLimit = 100, chunkNumber = 0, - gotOptions + fetchOptions }: { concurrency?: number fetchMissingBlocks?: boolean @@ -56,13 +59,13 @@ export class NotionAPI { signFileUrls?: boolean chunkLimit?: number chunkNumber?: number - gotOptions?: OptionsOfJSONResponseBody + fetchOptions?: RequestInit } = {} ): Promise<notion.ExtendedRecordMap> { const page = await this.getPageRaw(pageId, { chunkLimit, chunkNumber, - gotOptions + fetchOptions }) const recordMap = page?.recordMap as notion.ExtendedRecordMap @@ -94,7 +97,7 @@ export class NotionAPI { const newBlocks = await this.getBlocks( pendingBlockIds, - gotOptions + fetchOptions ).then((res) => res.recordMap.block) recordMap.block = { ...recordMap.block, ...newBlocks } @@ -144,7 +147,7 @@ export class NotionAPI { collectionViewId, collectionView, { - gotOptions + fetchOptions } ) @@ -195,7 +198,7 @@ export class NotionAPI { // because it is preferable for many use cases as opposed to making these API calls // lazily from the client-side. if (signFileUrls) { - await this.addSignedUrls({ recordMap, contentBlockIds, gotOptions }) + await this.addSignedUrls({ recordMap, contentBlockIds, fetchOptions }) } return recordMap @@ -204,11 +207,11 @@ export class NotionAPI { public async addSignedUrls({ recordMap, contentBlockIds, - gotOptions = {} + fetchOptions = {} }: { recordMap: notion.ExtendedRecordMap contentBlockIds?: string[] - gotOptions?: OptionsOfJSONResponseBody + fetchOptions?: RequestInit }) { recordMap.signed_urls = {} @@ -256,7 +259,7 @@ export class NotionAPI { try { const { signedUrls } = await this.getSignedFileUrls( allFileInstances, - gotOptions + fetchOptions ) if (signedUrls.length === allFileInstances.length) { @@ -276,13 +279,13 @@ export class NotionAPI { public async getPageRaw( pageId: string, { - gotOptions, + fetchOptions, chunkLimit = 100, chunkNumber = 0 }: { chunkLimit?: number chunkNumber?: number - gotOptions?: OptionsOfJSONResponseBody + fetchOptions?: RequestInit } = {} ) { const parsedPageId = parsePageId(pageId) @@ -302,7 +305,7 @@ export class NotionAPI { return this.fetch<notion.PageChunk>({ endpoint: 'loadPageChunk', body, - gotOptions + fetchOptions: fetchOptions }) } @@ -315,7 +318,7 @@ export class NotionAPI { searchQuery = '', userTimeZone = this._userTimeZone, loadContentCover = true, - gotOptions + fetchOptions }: { type?: notion.CollectionViewType limit?: number @@ -323,7 +326,7 @@ export class NotionAPI { userTimeZone?: string userLocale?: string loadContentCover?: boolean - gotOptions?: OptionsOfJSONResponseBody + fetchOptions?: RequestInit } = {} ) { const type = collectionView?.type @@ -484,27 +487,21 @@ export class NotionAPI { }, loader }, - gotOptions + fetchOptions: fetchOptions }) } - public async getUsers( - userIds: string[], - gotOptions?: OptionsOfJSONResponseBody - ) { + public async getUsers(userIds: string[], fetchOptions?: RequestInit) { return this.fetch<notion.RecordValues<notion.User>>({ endpoint: 'getRecordValues', body: { requests: userIds.map((id) => ({ id, table: 'notion_user' })) }, - gotOptions + fetchOptions: fetchOptions }) } - public async getBlocks( - blockIds: string[], - gotOptions?: OptionsOfJSONResponseBody - ) { + public async getBlocks(blockIds: string[], fetchOptions?: RequestInit) { return this.fetch<notion.PageChunk>({ endpoint: 'syncRecordValues', body: { @@ -515,27 +512,24 @@ export class NotionAPI { version: -1 })) }, - gotOptions + fetchOptions: fetchOptions }) } public async getSignedFileUrls( urls: types.SignedUrlRequest[], - gotOptions?: OptionsOfJSONResponseBody + fetchOptions?: RequestInit ) { return this.fetch<types.SignedUrlResponse>({ endpoint: 'getSignedFileUrls', body: { urls }, - gotOptions + fetchOptions: fetchOptions }) } - public async search( - params: notion.SearchParams, - gotOptions?: OptionsOfJSONResponseBody - ) { + public async search(params: notion.SearchParams, fetchOptions?: RequestInit) { const body = { type: 'BlocksInAncestor', source: 'quick_find_public', @@ -560,24 +554,24 @@ export class NotionAPI { return this.fetch<notion.SearchResults>({ endpoint: 'search', body, - gotOptions + fetchOptions: fetchOptions }) } public async fetch<T>({ endpoint, body, - gotOptions, + fetchOptions: fetchOptions, headers: clientHeaders }: { endpoint: string body: object - gotOptions?: OptionsOfJSONResponseBody + fetchOptions?: RequestInit headers?: any }): Promise<T> { const headers: any = { ...clientHeaders, - ...gotOptions?.headers, + ...fetchOptions?.headers, 'Content-Type': 'application/json' } @@ -591,13 +585,27 @@ export class NotionAPI { const url = `${this._apiBaseUrl}/${endpoint}` - return got - .post(url, { - ...gotOptions, - json: body, - headers - }) - .json() + if (!this.fetchImplementation) { + this.fetchImplementation = + typeof fetch === 'function' + ? fetch + : (await import('node-fetch')).default + } + + return await this.fetchImplementation(url, { + ...fetchOptions, + body: JSON.stringify(body), + method: 'POST', + headers + }).then(async (r) => { + const text = await r.text() + try { + const json = JSON.parse(text) + return json + } catch (e) { + throw new Error(`Cannot parse notion returned json:\n${text}`) + } + }) // return fetch(url, { // method: 'post', diff --git a/yarn.lock b/yarn.lock index 4e8dd0874..c77b1be1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2659,7 +2659,7 @@ resolved "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.1.tgz" integrity sha512-BUyKJGdDWqvWC5GEhyOiUrGNi9iJUr4CU0O2WxJL6QJhHeeA/NVBalH+FeK0r/x/W0rPymXt5s78TDS7d6lCwg== -"@sindresorhus/is@^4.0.0", "@sindresorhus/is@^4.6.0": +"@sindresorhus/is@^4.6.0": version "4.6.0" resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== @@ -2791,13 +2791,6 @@ "@svgr/plugin-svgo" "^5.5.0" loader-utils "^2.0.0" -"@szmarczak/http-timer@^4.0.5": - version "4.0.5" - resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz" - integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ== - dependencies: - defer-to-connect "^2.0.0" - "@szmarczak/http-timer@^5.0.1": version "5.0.1" resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz" @@ -2883,7 +2876,7 @@ dependencies: "@types/node" "*" -"@types/cacheable-request@^6.0.1", "@types/cacheable-request@^6.0.2": +"@types/cacheable-request@^6.0.2": version "6.0.2" resolved "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz" integrity sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA== @@ -4324,11 +4317,6 @@ cacache@^15.0.5, cacache@^15.2.0: tar "^6.0.2" unique-filename "^1.1.1" -cacheable-lookup@^5.0.3: - version "5.0.4" - resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz" - integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== - cacheable-lookup@^6.0.4: version "6.0.4" resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.0.4.tgz" @@ -5288,6 +5276,11 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +data-uri-to-buffer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz#b5db46aea50f6176428ac05b73be39a57701a64b" + integrity sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA== + data-urls@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz" @@ -5423,7 +5416,7 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -defer-to-connect@^2.0.0, defer-to-connect@^2.0.1: +defer-to-connect@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== @@ -6469,6 +6462,14 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fetch-blob@^3.1.2, fetch-blob@^3.1.4: + version "3.1.5" + resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.1.5.tgz#0077bf5f3fcdbd9d75a0b5362f77dbb743489863" + integrity sha512-N64ZpKqoLejlrwkIAnb9iLSA3Vx/kjgzpcDhygcqJ2KKjky8nCgUQ+dzXtbrLaWZGZNmNfQTsiQ0weZ1svglHg== + dependencies: + node-domexception "^1.0.0" + web-streams-polyfill "^3.0.3" + figures@^3.0.0: version "3.2.0" resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz" @@ -6648,6 +6649,13 @@ format-number@^3.0.0: resolved "https://registry.npmjs.org/format-number/-/format-number-3.0.0.tgz" integrity sha512-RWcbtINcRZ2DWCo4EcJgOJUYIwtsY5LKlTtL5OX1vfGsxEEK5mKaGxZC0p4Mgy63vXR12rut3lnjwCQ8YIlRMw== +formdata-polyfill@^4.0.10: + version "4.0.10" + resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" + integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== + dependencies: + fetch-blob "^3.1.2" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" @@ -6964,23 +6972,6 @@ globby@^13.1.1: merge2 "^1.4.1" slash "^4.0.0" -got@^11.8.1: - version "11.8.3" - resolved "https://registry.yarnpkg.com/got/-/got-11.8.3.tgz#f496c8fdda5d729a90b4905d2b07dbd148170770" - integrity sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg== - dependencies: - "@sindresorhus/is" "^4.0.0" - "@szmarczak/http-timer" "^4.0.5" - "@types/cacheable-request" "^6.0.1" - "@types/responselike" "^1.0.0" - cacheable-lookup "^5.0.3" - cacheable-request "^7.0.2" - decompress-response "^6.0.0" - http2-wrapper "^1.0.0-beta.5.2" - lowercase-keys "^2.0.0" - p-cancelable "^2.0.0" - responselike "^2.0.0" - got@^12.0.2: version "12.0.2" resolved "https://registry.npmjs.org/got/-/got-12.0.2.tgz" @@ -7258,14 +7249,6 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -http2-wrapper@^1.0.0-beta.5.2: - version "1.0.0-beta.5.2" - resolved "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz" - integrity sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ== - dependencies: - quick-lru "^5.1.1" - resolve-alpn "^1.0.0" - http2-wrapper@^2.1.10: version "2.1.10" resolved "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.1.10.tgz" @@ -9397,6 +9380,11 @@ node-addon-api@^3.2.0: resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== +node-domexception@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + node-fetch@^2.6.1: version "2.6.7" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz" @@ -9404,6 +9392,15 @@ node-fetch@^2.6.1: dependencies: whatwg-url "^5.0.0" +node-fetch@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.2.6.tgz#6d4627181697a9d9674aae0d61548e0d629b31b9" + integrity sha512-LAy/HZnLADOVkVPubaxHDft29booGglPFDr2Hw0J1AercRh01UiVFm++KMDnJeH9sHgNB4hsXPii7Sgym/sTbw== + dependencies: + data-uri-to-buffer "^4.0.0" + fetch-blob "^3.1.4" + formdata-polyfill "^4.0.10" + node-forge@^1: version "1.3.1" resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz" @@ -9834,11 +9831,6 @@ osenv@^0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" -p-cancelable@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz" - integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg== - p-cancelable@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz" @@ -11579,7 +11571,7 @@ resize-observer-polyfill@^1.5.1: resolved "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== -resolve-alpn@^1.0.0, resolve-alpn@^1.2.0: +resolve-alpn@^1.2.0: version "1.2.1" resolved "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz" integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== @@ -13191,6 +13183,11 @@ unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" +undici@^5.5.1: + version "5.5.1" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.5.1.tgz#baaf25844a99eaa0b22e1ef8d205bffe587c8f43" + integrity sha512-MEvryPLf18HvlCbLSzCW0U00IMftKGI5udnjrQbC5D4P0Hodwffhv+iGfWuJwg16Y/TK11ZFK8i+BPVW2z/eAw== + unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz" @@ -13442,6 +13439,11 @@ wcwidth@^1.0.0: dependencies: defaults "^1.0.3" +web-streams-polyfill@^3.0.3: + version "3.2.1" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" + integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz"