@slogvo/notion-client 0.1.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/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vue Notion X Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ---
24
+
25
+ This project extends MIT-licensed work from react-notion-x by Travis Fischer
26
+ and other contributors.
package/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # @slogvo/notion-client
2
+
3
+ > Robust TypeScript client for the unofficial Notion API. Works in Node.js and Browser.
4
+ >
5
+ > Client kết nối Notion API viết bằng TypeScript, hoạt động trên cả server và trình duyệt. 🚀
6
+ >
7
+ > 非公式 Notion API 向けの強力な TypeScript クライアント。 Node.js およびブラウザで動作します。 ⚡️
8
+
9
+ Part of the [vue-notion](https://github.com/slogvo/vue-notion) monorepo.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ pnpm add @slogvo/notion-client
15
+ ```
16
+
17
+ ## License
18
+
19
+ MIT
@@ -0,0 +1,79 @@
1
+ import * as notion from '@slogvo/notion-types';
2
+ import { FetchOptions } from 'ofetch';
3
+
4
+ interface SignedUrlRequest {
5
+ permissionRecord: PermissionRecord;
6
+ url: string;
7
+ }
8
+ interface PermissionRecord {
9
+ table: string;
10
+ id: notion.ID;
11
+ }
12
+ interface SignedUrlResponse {
13
+ signedUrls: string[];
14
+ }
15
+
16
+ /**
17
+ * Main Notion API client.
18
+ */
19
+ declare class NotionAPI {
20
+ private readonly _apiBaseUrl;
21
+ private readonly _authToken?;
22
+ private readonly _activeUser?;
23
+ private readonly _userTimeZone;
24
+ private readonly _ofetchOptions?;
25
+ constructor({ apiBaseUrl, authToken, activeUser, userTimeZone, ofetchOptions }?: {
26
+ apiBaseUrl?: string;
27
+ authToken?: string;
28
+ userLocale?: string;
29
+ userTimeZone?: string;
30
+ activeUser?: string;
31
+ ofetchOptions?: FetchOptions;
32
+ });
33
+ getPage(pageId: string, { concurrency, fetchMissingBlocks, fetchCollections, signFileUrls, chunkLimit, chunkNumber, throwOnCollectionErrors, collectionReducerLimit, fetchRelationPages, ofetchOptions }?: {
34
+ concurrency?: number;
35
+ fetchMissingBlocks?: boolean;
36
+ fetchCollections?: boolean;
37
+ signFileUrls?: boolean;
38
+ chunkLimit?: number;
39
+ chunkNumber?: number;
40
+ throwOnCollectionErrors?: boolean;
41
+ collectionReducerLimit?: number;
42
+ fetchRelationPages?: boolean;
43
+ ofetchOptions?: FetchOptions;
44
+ }): Promise<notion.ExtendedRecordMap>;
45
+ fetchRelationPages: (recordMap: notion.ExtendedRecordMap, ofetchOptions: FetchOptions | undefined) => Promise<notion.BlockMap>;
46
+ extractRelationPageIdsFromBlock: (blockValue: any, collectionSchema: any) => Set<string>;
47
+ addSignedUrls({ recordMap, contentBlockIds, ofetchOptions }: {
48
+ recordMap: notion.ExtendedRecordMap;
49
+ contentBlockIds?: string[];
50
+ ofetchOptions?: FetchOptions;
51
+ }): Promise<void>;
52
+ getPageRaw(pageId: string, { ofetchOptions, chunkLimit, chunkNumber }?: {
53
+ chunkLimit?: number;
54
+ chunkNumber?: number;
55
+ ofetchOptions?: FetchOptions;
56
+ }): Promise<notion.PageChunk>;
57
+ getCollectionData(collectionId: string, collectionViewId: string, collectionView?: any, { limit, searchQuery, userTimeZone, loadContentCover, spaceId, ofetchOptions }?: {
58
+ type?: notion.CollectionViewType;
59
+ limit?: number;
60
+ searchQuery?: string;
61
+ userTimeZone?: string;
62
+ userLocale?: string;
63
+ loadContentCover?: boolean;
64
+ spaceId?: string;
65
+ ofetchOptions?: FetchOptions;
66
+ }): Promise<notion.CollectionInstance>;
67
+ getUsers(userIds: string[], ofetchOptions?: FetchOptions): Promise<notion.RecordValues<notion.User>>;
68
+ getBlocks(blockIds: string[], ofetchOptions?: FetchOptions): Promise<notion.PageChunk>;
69
+ getSignedFileUrls(urls: SignedUrlRequest[], ofetchOptions?: FetchOptions): Promise<SignedUrlResponse>;
70
+ search(params: notion.SearchParams, ofetchOptions?: FetchOptions): Promise<notion.SearchResults>;
71
+ fetch<T>({ endpoint, body, ofetchOptions, headers: clientHeaders }: {
72
+ endpoint: string;
73
+ body: object;
74
+ ofetchOptions?: FetchOptions;
75
+ headers?: any;
76
+ }): Promise<T>;
77
+ }
78
+
79
+ export { NotionAPI, type PermissionRecord, type SignedUrlRequest, type SignedUrlResponse };
package/build/index.js ADDED
@@ -0,0 +1,548 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+
5
+ // src/notion-api.ts
6
+ import {
7
+ getBlockCollectionId,
8
+ getPageContentBlockIds,
9
+ parsePageId,
10
+ uuidToId
11
+ } from "@slogvo/notion-utils";
12
+ import { ofetch } from "ofetch";
13
+ import pMap from "p-map";
14
+ var NotionAPI = class {
15
+ constructor({
16
+ apiBaseUrl = "https://www.notion.so/api/v3",
17
+ authToken,
18
+ activeUser,
19
+ userTimeZone = "America/New_York",
20
+ ofetchOptions
21
+ } = {}) {
22
+ __publicField(this, "_apiBaseUrl");
23
+ __publicField(this, "_authToken");
24
+ __publicField(this, "_activeUser");
25
+ __publicField(this, "_userTimeZone");
26
+ __publicField(this, "_ofetchOptions");
27
+ __publicField(this, "fetchRelationPages", async (recordMap, ofetchOptions) => {
28
+ var _a, _b;
29
+ const maxIterations = 10;
30
+ for (let i = 0; i < maxIterations; ++i) {
31
+ const relationPageIdsThisIteration = /* @__PURE__ */ new Set();
32
+ for (const blockId of Object.keys(recordMap.block)) {
33
+ const blockValue = (_a = recordMap.block[blockId]) == null ? void 0 : _a.value;
34
+ if ((blockValue == null ? void 0 : blockValue.parent_table) === "collection" && (blockValue == null ? void 0 : blockValue.parent_id)) {
35
+ const collection = (_b = recordMap.collection[blockValue.parent_id]) == null ? void 0 : _b.value;
36
+ if (collection == null ? void 0 : collection.schema) {
37
+ const ids = this.extractRelationPageIdsFromBlock(
38
+ blockValue,
39
+ collection.schema
40
+ );
41
+ for (const id of ids) relationPageIdsThisIteration.add(id);
42
+ }
43
+ }
44
+ }
45
+ const missingRelationPageIds = Array.from(
46
+ relationPageIdsThisIteration
47
+ ).filter((id) => {
48
+ var _a2;
49
+ return !((_a2 = recordMap.block[id]) == null ? void 0 : _a2.value);
50
+ });
51
+ if (!missingRelationPageIds.length) break;
52
+ try {
53
+ const newBlocks = await this.getBlocks(
54
+ missingRelationPageIds,
55
+ ofetchOptions
56
+ ).then((res) => res.recordMap.block);
57
+ recordMap.block = { ...recordMap.block, ...newBlocks };
58
+ } catch (err) {
59
+ console.warn(
60
+ "NotionAPI getBlocks error during fetchRelationPages:",
61
+ err
62
+ );
63
+ }
64
+ }
65
+ return recordMap.block;
66
+ });
67
+ __publicField(this, "extractRelationPageIdsFromBlock", (blockValue, collectionSchema) => {
68
+ var _a;
69
+ const pageIds = /* @__PURE__ */ new Set();
70
+ for (const propertyId of Object.keys(blockValue.properties || {})) {
71
+ const schema = collectionSchema[propertyId];
72
+ if ((schema == null ? void 0 : schema.type) === "relation") {
73
+ const decorations = blockValue.properties[propertyId];
74
+ if (Array.isArray(decorations)) {
75
+ for (const decoration of decorations) {
76
+ if (Array.isArray(decoration) && decoration.length > 1 && decoration[0] === "\u2023") {
77
+ const pagePointer = (_a = decoration[1]) == null ? void 0 : _a[0];
78
+ if (Array.isArray(pagePointer) && pagePointer.length > 1 && pagePointer[0] === "p") {
79
+ pageIds.add(pagePointer[1]);
80
+ }
81
+ }
82
+ }
83
+ }
84
+ }
85
+ }
86
+ return pageIds;
87
+ });
88
+ this._apiBaseUrl = apiBaseUrl;
89
+ this._authToken = authToken;
90
+ this._activeUser = activeUser;
91
+ this._userTimeZone = userTimeZone;
92
+ this._ofetchOptions = ofetchOptions;
93
+ }
94
+ async getPage(pageId, {
95
+ concurrency = 3,
96
+ fetchMissingBlocks = true,
97
+ fetchCollections = true,
98
+ signFileUrls = true,
99
+ chunkLimit = 100,
100
+ chunkNumber = 0,
101
+ throwOnCollectionErrors = false,
102
+ collectionReducerLimit = 999,
103
+ fetchRelationPages = false,
104
+ ofetchOptions
105
+ } = {}) {
106
+ var _a, _b, _c;
107
+ const page = await this.getPageRaw(pageId, {
108
+ chunkLimit,
109
+ chunkNumber,
110
+ ofetchOptions
111
+ });
112
+ const recordMap = page == null ? void 0 : page.recordMap;
113
+ if (!(recordMap == null ? void 0 : recordMap.block)) {
114
+ throw new Error(`Notion page not found "${uuidToId(pageId)}"`);
115
+ }
116
+ recordMap.collection = (_a = recordMap.collection) != null ? _a : {};
117
+ recordMap.collection_view = (_b = recordMap.collection_view) != null ? _b : {};
118
+ recordMap.notion_user = (_c = recordMap.notion_user) != null ? _c : {};
119
+ recordMap.collection_query = {};
120
+ recordMap.signed_urls = {};
121
+ if (fetchMissingBlocks) {
122
+ while (true) {
123
+ const pendingBlockIds = getPageContentBlockIds(recordMap).filter(
124
+ (id) => !recordMap.block[id]
125
+ );
126
+ if (!pendingBlockIds.length) {
127
+ break;
128
+ }
129
+ const newBlocks = await this.getBlocks(
130
+ pendingBlockIds,
131
+ ofetchOptions
132
+ ).then((res) => res.recordMap.block);
133
+ recordMap.block = { ...recordMap.block, ...newBlocks };
134
+ }
135
+ }
136
+ const contentBlockIds = getPageContentBlockIds(recordMap);
137
+ if (fetchCollections) {
138
+ const allCollectionInstances = contentBlockIds.flatMap((blockId) => {
139
+ var _a2, _b2;
140
+ const block = (_a2 = recordMap.block[blockId]) == null ? void 0 : _a2.value;
141
+ const collectionId = block && (block.type === "collection_view" || block.type === "collection_view_page") && getBlockCollectionId(block, recordMap);
142
+ if (collectionId) {
143
+ const spaceId = block == null ? void 0 : block.space_id;
144
+ return (_b2 = block.view_ids) == null ? void 0 : _b2.map((collectionViewId) => ({
145
+ collectionId,
146
+ collectionViewId,
147
+ spaceId
148
+ }));
149
+ } else {
150
+ return [];
151
+ }
152
+ });
153
+ await pMap(
154
+ allCollectionInstances,
155
+ async (collectionInstance) => {
156
+ var _a2, _b2;
157
+ const { collectionId, collectionViewId, spaceId } = collectionInstance;
158
+ const collectionView = (_a2 = recordMap.collection_view[collectionViewId]) == null ? void 0 : _a2.value;
159
+ try {
160
+ const collectionData = await this.getCollectionData(
161
+ collectionId,
162
+ collectionViewId,
163
+ collectionView,
164
+ {
165
+ limit: collectionReducerLimit,
166
+ spaceId,
167
+ ofetchOptions
168
+ }
169
+ );
170
+ recordMap.block = {
171
+ ...recordMap.block,
172
+ ...collectionData.recordMap.block
173
+ };
174
+ recordMap.collection = {
175
+ ...recordMap.collection,
176
+ ...collectionData.recordMap.collection
177
+ };
178
+ recordMap.collection_view = {
179
+ ...recordMap.collection_view,
180
+ ...collectionData.recordMap.collection_view
181
+ };
182
+ recordMap.notion_user = {
183
+ ...recordMap.notion_user,
184
+ ...collectionData.recordMap.notion_user
185
+ };
186
+ recordMap.collection_query[collectionId] = {
187
+ ...recordMap.collection_query[collectionId],
188
+ [collectionViewId]: (_b2 = collectionData.result) == null ? void 0 : _b2.reducerResults
189
+ };
190
+ } catch (err) {
191
+ console.warn(
192
+ "NotionAPI collectionQuery error",
193
+ { pageId, collectionId, collectionViewId },
194
+ err.message
195
+ );
196
+ if (throwOnCollectionErrors) {
197
+ throw err;
198
+ } else {
199
+ console.error(err);
200
+ }
201
+ }
202
+ },
203
+ {
204
+ concurrency
205
+ }
206
+ );
207
+ }
208
+ if (signFileUrls) {
209
+ await this.addSignedUrls({ recordMap, contentBlockIds, ofetchOptions });
210
+ }
211
+ if (fetchRelationPages) {
212
+ const newBlocks = await this.fetchRelationPages(recordMap, ofetchOptions);
213
+ recordMap.block = { ...recordMap.block, ...newBlocks };
214
+ }
215
+ return recordMap;
216
+ }
217
+ async addSignedUrls({
218
+ recordMap,
219
+ contentBlockIds,
220
+ ofetchOptions = {}
221
+ }) {
222
+ recordMap.signed_urls = {};
223
+ if (!contentBlockIds) {
224
+ contentBlockIds = getPageContentBlockIds(recordMap);
225
+ }
226
+ const allFileInstances = contentBlockIds.flatMap((blockId) => {
227
+ var _a, _b, _c, _d, _e, _f;
228
+ const block = (_a = recordMap.block[blockId]) == null ? void 0 : _a.value;
229
+ if (block && (block.type === "pdf" || block.type === "audio" || block.type === "image" && ((_b = block.file_ids) == null ? void 0 : _b.length) || block.type === "video" || block.type === "file" || block.type === "page")) {
230
+ const source = block.type === "page" ? (_c = block.format) == null ? void 0 : _c.page_cover : (_f = (_e = (_d = block.properties) == null ? void 0 : _d.source) == null ? void 0 : _e[0]) == null ? void 0 : _f[0];
231
+ if (source) {
232
+ if (source.includes("secure.notion-static.com") || source.includes("prod-files-secure") || source.includes("attachment:")) {
233
+ return {
234
+ permissionRecord: {
235
+ table: "block",
236
+ id: block.id
237
+ },
238
+ url: source
239
+ };
240
+ }
241
+ return [];
242
+ }
243
+ }
244
+ return [];
245
+ });
246
+ if (allFileInstances.length > 0) {
247
+ try {
248
+ const { signedUrls } = await this.getSignedFileUrls(
249
+ allFileInstances,
250
+ ofetchOptions
251
+ );
252
+ if (signedUrls.length === allFileInstances.length) {
253
+ for (const [i, file] of allFileInstances.entries()) {
254
+ const signedUrl = signedUrls[i];
255
+ if (!signedUrl) continue;
256
+ const blockId = file.permissionRecord.id;
257
+ if (!blockId) continue;
258
+ recordMap.signed_urls[blockId] = signedUrl;
259
+ }
260
+ }
261
+ } catch (err) {
262
+ console.warn("NotionAPI getSignedfileUrls error", err);
263
+ }
264
+ }
265
+ }
266
+ async getPageRaw(pageId, {
267
+ ofetchOptions,
268
+ chunkLimit = 100,
269
+ chunkNumber = 0
270
+ } = {}) {
271
+ const parsedPageId = parsePageId(pageId);
272
+ if (!parsedPageId) {
273
+ throw new Error(`invalid notion pageId "${pageId}"`);
274
+ }
275
+ const body = {
276
+ pageId: parsedPageId,
277
+ limit: chunkLimit,
278
+ chunkNumber,
279
+ cursor: { stack: [] },
280
+ verticalColumns: false
281
+ };
282
+ return this.fetch({
283
+ endpoint: "loadPageChunk",
284
+ body,
285
+ ofetchOptions
286
+ });
287
+ }
288
+ async getCollectionData(collectionId, collectionViewId, collectionView, {
289
+ limit = 999,
290
+ searchQuery = "",
291
+ userTimeZone = this._userTimeZone,
292
+ loadContentCover = true,
293
+ spaceId,
294
+ ofetchOptions
295
+ } = {}) {
296
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
297
+ const type = collectionView == null ? void 0 : collectionView.type;
298
+ const isBoardType = type === "board";
299
+ const groupBy = isBoardType ? (_a = collectionView == null ? void 0 : collectionView.format) == null ? void 0 : _a.board_columns_by : (_b = collectionView == null ? void 0 : collectionView.format) == null ? void 0 : _b.collection_group_by;
300
+ let filters = [];
301
+ if ((_c = collectionView == null ? void 0 : collectionView.format) == null ? void 0 : _c.property_filters) {
302
+ filters = (_d = collectionView.format) == null ? void 0 : _d.property_filters.map(
303
+ (filterObj) => {
304
+ var _a2, _b2;
305
+ return {
306
+ filter: (_a2 = filterObj == null ? void 0 : filterObj.filter) == null ? void 0 : _a2.filter,
307
+ property: (_b2 = filterObj == null ? void 0 : filterObj.filter) == null ? void 0 : _b2.property
308
+ };
309
+ }
310
+ );
311
+ }
312
+ if ((_f = (_e = collectionView == null ? void 0 : collectionView.query2) == null ? void 0 : _e.filter) == null ? void 0 : _f.filters) {
313
+ filters.push(...collectionView.query2.filter.filters);
314
+ }
315
+ let loader = {
316
+ type: "reducer",
317
+ reducers: {
318
+ collection_group_results: {
319
+ type: "results",
320
+ limit,
321
+ loadContentCover
322
+ }
323
+ },
324
+ sort: [],
325
+ ...collectionView == null ? void 0 : collectionView.query2,
326
+ filter: {
327
+ filters,
328
+ operator: "and"
329
+ },
330
+ searchQuery,
331
+ userTimeZone
332
+ };
333
+ if (groupBy) {
334
+ const groups = ((_g = collectionView == null ? void 0 : collectionView.format) == null ? void 0 : _g.board_columns) || ((_h = collectionView == null ? void 0 : collectionView.format) == null ? void 0 : _h.collection_groups) || [];
335
+ const iterators = [isBoardType ? "board" : "group_aggregation", "results"];
336
+ const operators = {
337
+ checkbox: "checkbox_is",
338
+ url: "string_starts_with",
339
+ text: "string_starts_with",
340
+ select: "enum_is",
341
+ multi_select: "enum_contains",
342
+ created_time: "date_is_within",
343
+ undefined: "is_empty"
344
+ };
345
+ const reducersQuery = {};
346
+ for (const group of groups) {
347
+ const {
348
+ property,
349
+ value: { value, type: type2 }
350
+ } = group;
351
+ for (const iterator of iterators) {
352
+ const iteratorProps = iterator === "results" ? {
353
+ type: iterator,
354
+ limit
355
+ } : {
356
+ type: "aggregation",
357
+ aggregation: {
358
+ aggregator: "count"
359
+ }
360
+ };
361
+ const isUncategorizedValue = value === void 0;
362
+ const isDateValue = value == null ? void 0 : value.range;
363
+ const queryLabel = isUncategorizedValue ? "uncategorized" : isDateValue ? ((_i = value.range) == null ? void 0 : _i.start_date) || ((_j = value.range) == null ? void 0 : _j.end_date) : (value == null ? void 0 : value.value) || value;
364
+ const queryValue = !isUncategorizedValue && (isDateValue || (value == null ? void 0 : value.value) || value);
365
+ reducersQuery[`${iterator}:${type2}:${queryLabel}`] = {
366
+ ...iteratorProps,
367
+ filter: {
368
+ operator: "and",
369
+ filters: [
370
+ {
371
+ property,
372
+ filter: {
373
+ operator: !isUncategorizedValue ? operators[type2] : "is_empty",
374
+ ...!isUncategorizedValue && {
375
+ value: {
376
+ type: "exact",
377
+ value: queryValue
378
+ }
379
+ }
380
+ }
381
+ }
382
+ ]
383
+ }
384
+ };
385
+ }
386
+ }
387
+ const reducerLabel = isBoardType ? "board_columns" : `${type}_groups`;
388
+ loader = {
389
+ type: "reducer",
390
+ reducers: {
391
+ [reducerLabel]: {
392
+ type: "groups",
393
+ version: "v2",
394
+ groupBy,
395
+ ...((_k = collectionView == null ? void 0 : collectionView.query2) == null ? void 0 : _k.filter) && {
396
+ filter: (_l = collectionView == null ? void 0 : collectionView.query2) == null ? void 0 : _l.filter
397
+ },
398
+ groupSortPreference: groups.map((group) => {
399
+ var _a2, _b2;
400
+ return {
401
+ property: group == null ? void 0 : group.property,
402
+ value: {
403
+ type: (_a2 = group == null ? void 0 : group.value) == null ? void 0 : _a2.type,
404
+ value: (_b2 = group == null ? void 0 : group.value) == null ? void 0 : _b2.value
405
+ }
406
+ };
407
+ }),
408
+ limit
409
+ },
410
+ ...reducersQuery
411
+ },
412
+ ...collectionView == null ? void 0 : collectionView.query2,
413
+ searchQuery,
414
+ userTimeZone,
415
+ //TODO: add filters here
416
+ filter: {
417
+ filters,
418
+ operator: "and"
419
+ }
420
+ };
421
+ }
422
+ const headers = {};
423
+ if (spaceId) {
424
+ headers["x-notion-space-id"] = spaceId;
425
+ }
426
+ return this.fetch({
427
+ endpoint: "queryCollection",
428
+ body: {
429
+ collection: {
430
+ id: collectionId
431
+ },
432
+ collectionView: {
433
+ id: collectionViewId
434
+ },
435
+ source: {
436
+ type: "collection",
437
+ id: collectionId
438
+ },
439
+ loader
440
+ },
441
+ headers,
442
+ ofetchOptions: {
443
+ timeout: 6e4,
444
+ ...ofetchOptions,
445
+ params: {
446
+ // TODO: spread ofetchOptions?.searchParams
447
+ src: "initial_load"
448
+ }
449
+ }
450
+ });
451
+ }
452
+ async getUsers(userIds, ofetchOptions) {
453
+ return this.fetch({
454
+ endpoint: "getRecordValues",
455
+ body: {
456
+ requests: userIds.map((id) => ({ id, table: "notion_user" }))
457
+ },
458
+ ofetchOptions
459
+ });
460
+ }
461
+ async getBlocks(blockIds, ofetchOptions) {
462
+ return this.fetch({
463
+ endpoint: "syncRecordValuesMain",
464
+ body: {
465
+ requests: blockIds.map((blockId) => ({
466
+ // TODO: when to use table 'space' vs 'block'?
467
+ table: "block",
468
+ id: blockId,
469
+ version: -1
470
+ }))
471
+ },
472
+ ofetchOptions
473
+ });
474
+ }
475
+ async getSignedFileUrls(urls, ofetchOptions) {
476
+ return this.fetch({
477
+ endpoint: "getSignedFileUrls",
478
+ body: {
479
+ urls
480
+ },
481
+ ofetchOptions
482
+ });
483
+ }
484
+ async search(params, ofetchOptions) {
485
+ const body = {
486
+ type: "BlocksInAncestor",
487
+ source: "quick_find_public",
488
+ ancestorId: parsePageId(params.ancestorId),
489
+ sort: {
490
+ field: "relevance"
491
+ },
492
+ limit: params.limit || 20,
493
+ query: params.query,
494
+ filters: {
495
+ isDeletedOnly: false,
496
+ isNavigableOnly: false,
497
+ excludeTemplates: true,
498
+ requireEditPermissions: false,
499
+ includePublicPagesWithoutExplicitAccess: true,
500
+ ancestors: [],
501
+ createdBy: [],
502
+ editedBy: [],
503
+ lastEditedTime: {},
504
+ createdTime: {},
505
+ ...params.filters
506
+ }
507
+ };
508
+ return this.fetch({
509
+ endpoint: "search",
510
+ body,
511
+ ofetchOptions
512
+ });
513
+ }
514
+ async fetch({
515
+ endpoint,
516
+ body,
517
+ ofetchOptions,
518
+ headers: clientHeaders
519
+ }) {
520
+ var _a;
521
+ const headers = {
522
+ ...clientHeaders,
523
+ ...(_a = this._ofetchOptions) == null ? void 0 : _a.headers,
524
+ ...ofetchOptions == null ? void 0 : ofetchOptions.headers,
525
+ "Content-Type": "application/json"
526
+ };
527
+ if (this._authToken) {
528
+ headers.cookie = `token_v2=${this._authToken}`;
529
+ }
530
+ if (this._activeUser) {
531
+ headers["x-notion-active-user-header"] = this._activeUser;
532
+ }
533
+ const url = `${this._apiBaseUrl}/${endpoint}`;
534
+ const res = ofetch(url, {
535
+ method: "POST",
536
+ mode: "no-cors",
537
+ ...this._ofetchOptions,
538
+ ...ofetchOptions,
539
+ body,
540
+ headers
541
+ });
542
+ return res;
543
+ }
544
+ };
545
+ export {
546
+ NotionAPI
547
+ };
548
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/notion-api.ts"],"sourcesContent":["// import { promises as fs } from 'fs'\n//import ky, { type Options as OfetchOptions } from 'ky'\n\nimport type * as notion from '@slogvo/notion-types'\nimport {\n getBlockCollectionId,\n getPageContentBlockIds,\n parsePageId,\n uuidToId\n} from '@slogvo/notion-utils'\nimport { type FetchOptions as OfetchOptions, ofetch } from 'ofetch'\nimport pMap from 'p-map'\n\nimport type * as types from './types'\n\n/**\n * Main Notion API client.\n */\nexport class NotionAPI {\n private readonly _apiBaseUrl: string\n private readonly _authToken?: string\n private readonly _activeUser?: string\n private readonly _userTimeZone: string\n private readonly _ofetchOptions?: OfetchOptions\n\n constructor({\n apiBaseUrl = 'https://www.notion.so/api/v3',\n authToken,\n activeUser,\n userTimeZone = 'America/New_York',\n ofetchOptions\n }: {\n apiBaseUrl?: string\n authToken?: string\n userLocale?: string\n userTimeZone?: string\n activeUser?: string\n ofetchOptions?: OfetchOptions\n } = {}) {\n this._apiBaseUrl = apiBaseUrl\n this._authToken = authToken\n this._activeUser = activeUser\n this._userTimeZone = userTimeZone\n this._ofetchOptions = ofetchOptions\n }\n\n public async getPage(\n pageId: string,\n {\n concurrency = 3,\n fetchMissingBlocks = true,\n fetchCollections = true,\n signFileUrls = true,\n chunkLimit = 100,\n chunkNumber = 0,\n throwOnCollectionErrors = false,\n collectionReducerLimit = 999,\n fetchRelationPages = false,\n ofetchOptions\n }: {\n concurrency?: number\n fetchMissingBlocks?: boolean\n fetchCollections?: boolean\n signFileUrls?: boolean\n chunkLimit?: number\n chunkNumber?: number\n throwOnCollectionErrors?: boolean\n collectionReducerLimit?: number\n fetchRelationPages?: boolean\n ofetchOptions?: OfetchOptions\n } = {}\n ): Promise<notion.ExtendedRecordMap> {\n const page = await this.getPageRaw(pageId, {\n chunkLimit,\n chunkNumber,\n ofetchOptions\n })\n const recordMap = page?.recordMap as notion.ExtendedRecordMap\n\n if (!recordMap?.block) {\n throw new Error(`Notion page not found \"${uuidToId(pageId)}\"`)\n }\n\n // ensure that all top-level maps exist\n recordMap.collection = recordMap.collection ?? {}\n recordMap.collection_view = recordMap.collection_view ?? {}\n recordMap.notion_user = recordMap.notion_user ?? {}\n\n // additional mappings added for convenience\n // note: these are not native notion objects\n recordMap.collection_query = {}\n recordMap.signed_urls = {}\n\n if (fetchMissingBlocks) {\n while (true) {\n // fetch any missing content blocks\n const pendingBlockIds = getPageContentBlockIds(recordMap).filter(\n id => !recordMap.block[id]\n )\n\n if (!pendingBlockIds.length) {\n break\n }\n\n const newBlocks = await this.getBlocks(\n pendingBlockIds,\n ofetchOptions\n ).then(res => res.recordMap.block)\n\n recordMap.block = { ...recordMap.block, ...newBlocks }\n }\n }\n\n const contentBlockIds = getPageContentBlockIds(recordMap)\n\n // Optionally fetch all data for embedded collections and their associated views.\n // NOTE: We're eagerly fetching *all* data for each collection and all of its views.\n // This is really convenient in order to ensure that all data needed for a given\n // Notion page is readily available for use cases involving server-side rendering\n // and edge caching.\n if (fetchCollections) {\n const allCollectionInstances: Array<{\n collectionId: string\n collectionViewId: string\n spaceId?: string\n }> = contentBlockIds.flatMap(blockId => {\n const block = recordMap.block[blockId]?.value\n const collectionId =\n block &&\n (block.type === 'collection_view' ||\n block.type === 'collection_view_page') &&\n getBlockCollectionId(block, recordMap)\n\n if (collectionId) {\n const spaceId = block?.space_id\n return block.view_ids?.map(collectionViewId => ({\n collectionId,\n collectionViewId,\n spaceId\n }))\n } else {\n return []\n }\n })\n\n // fetch data for all collection view instances\n await pMap(\n allCollectionInstances,\n async collectionInstance => {\n const { collectionId, collectionViewId, spaceId } = collectionInstance\n const collectionView =\n recordMap.collection_view[collectionViewId]?.value\n\n try {\n const collectionData = await this.getCollectionData(\n collectionId,\n collectionViewId,\n collectionView,\n {\n limit: collectionReducerLimit,\n spaceId,\n ofetchOptions\n }\n )\n\n // await fs.writeFile(\n // `${collectionId}-${collectionViewId}.json`,\n // JSON.stringify(collectionData.result, null, 2)\n // )\n\n recordMap.block = {\n ...recordMap.block,\n ...collectionData.recordMap.block\n }\n\n recordMap.collection = {\n ...recordMap.collection,\n ...collectionData.recordMap.collection\n }\n\n recordMap.collection_view = {\n ...recordMap.collection_view,\n ...collectionData.recordMap.collection_view\n }\n\n recordMap.notion_user = {\n ...recordMap.notion_user,\n ...collectionData.recordMap.notion_user\n }\n\n recordMap.collection_query![collectionId] = {\n ...recordMap.collection_query![collectionId],\n [collectionViewId]: (collectionData.result as any)?.reducerResults\n }\n } catch (err: any) {\n // It's possible for public pages to link to private collections,\n // in which case Notion returns a 400 error. This may be that or it\n // may be something else.\n console.warn(\n 'NotionAPI collectionQuery error',\n { pageId, collectionId, collectionViewId },\n err.message\n )\n\n if (throwOnCollectionErrors) {\n throw err\n // throw new Error(\n // `NotionAPI error fetching collectionQuery for page \"${pageId}\" collection \"${collectionId}\" view \"${collectionViewId}\": ${err.message}`,\n // {\n // cause: err\n // }\n // )\n } else {\n console.error(err)\n }\n }\n },\n {\n concurrency\n }\n )\n }\n\n // Optionally fetch signed URLs for any embedded files.\n // NOTE: Similar to collection data, we default to eagerly fetching signed URL info\n // because it is preferable for many use cases as opposed to making these API calls\n // lazily from the client-side.\n if (signFileUrls) {\n await this.addSignedUrls({ recordMap, contentBlockIds, ofetchOptions })\n }\n\n if (fetchRelationPages) {\n const newBlocks = await this.fetchRelationPages(recordMap, ofetchOptions)\n recordMap.block = { ...recordMap.block, ...newBlocks }\n }\n\n return recordMap\n }\n\n fetchRelationPages = async (\n recordMap: notion.ExtendedRecordMap,\n ofetchOptions: OfetchOptions | undefined\n ): Promise<notion.BlockMap> => {\n const maxIterations = 10\n\n for (let i = 0; i < maxIterations; ++i) {\n const relationPageIdsThisIteration = new Set<string>()\n\n for (const blockId of Object.keys(recordMap.block)) {\n const blockValue = recordMap.block[blockId]?.value\n if (\n blockValue?.parent_table === 'collection' &&\n blockValue?.parent_id\n ) {\n const collection = recordMap.collection[blockValue.parent_id]?.value\n if (collection?.schema) {\n const ids = this.extractRelationPageIdsFromBlock(\n blockValue,\n collection.schema\n )\n for (const id of ids) relationPageIdsThisIteration.add(id)\n }\n }\n }\n\n const missingRelationPageIds = Array.from(\n relationPageIdsThisIteration\n ).filter(id => !recordMap.block[id]?.value)\n\n if (!missingRelationPageIds.length) break\n\n try {\n const newBlocks = await this.getBlocks(\n missingRelationPageIds,\n ofetchOptions\n ).then(res => res.recordMap.block)\n recordMap.block = { ...recordMap.block, ...newBlocks }\n } catch (err: any) {\n console.warn(\n 'NotionAPI getBlocks error during fetchRelationPages:',\n err\n )\n // consider break or delay/retry here\n }\n }\n\n return recordMap.block\n }\n\n extractRelationPageIdsFromBlock = (\n blockValue: any,\n collectionSchema: any\n ): Set<string> => {\n const pageIds = new Set<string>()\n\n for (const propertyId of Object.keys(blockValue.properties || {})) {\n const schema = collectionSchema[propertyId]\n if (schema?.type === 'relation') {\n const decorations = blockValue.properties[propertyId]\n if (Array.isArray(decorations)) {\n for (const decoration of decorations) {\n if (\n Array.isArray(decoration) &&\n decoration.length > 1 &&\n decoration[0] === '‣'\n ) {\n const pagePointer = decoration[1]?.[0]\n if (\n Array.isArray(pagePointer) &&\n pagePointer.length > 1 &&\n pagePointer[0] === 'p'\n ) {\n pageIds.add(pagePointer[1])\n }\n }\n }\n }\n }\n }\n return pageIds\n }\n\n public async addSignedUrls({\n recordMap,\n contentBlockIds,\n ofetchOptions = {}\n }: {\n recordMap: notion.ExtendedRecordMap\n contentBlockIds?: string[]\n ofetchOptions?: OfetchOptions\n }) {\n recordMap.signed_urls = {}\n\n if (!contentBlockIds) {\n contentBlockIds = getPageContentBlockIds(recordMap)\n }\n\n const allFileInstances = contentBlockIds.flatMap(blockId => {\n const block = recordMap.block[blockId]?.value\n\n if (\n block &&\n (block.type === 'pdf' ||\n block.type === 'audio' ||\n (block.type === 'image' && block.file_ids?.length) ||\n block.type === 'video' ||\n block.type === 'file' ||\n block.type === 'page')\n ) {\n const source =\n block.type === 'page'\n ? block.format?.page_cover\n : block.properties?.source?.[0]?.[0]\n // console.log(block, source)\n\n if (source) {\n if (\n source.includes('secure.notion-static.com') ||\n source.includes('prod-files-secure') ||\n source.includes('attachment:')\n ) {\n return {\n permissionRecord: {\n table: 'block',\n id: block.id\n },\n url: source\n }\n }\n\n return []\n }\n }\n\n return []\n })\n\n if (allFileInstances.length > 0) {\n try {\n const { signedUrls } = await this.getSignedFileUrls(\n allFileInstances,\n ofetchOptions\n )\n\n if (signedUrls.length === allFileInstances.length) {\n for (const [i, file] of allFileInstances.entries()) {\n const signedUrl = signedUrls[i]\n if (!signedUrl) continue\n\n const blockId = file.permissionRecord.id\n if (!blockId) continue\n\n recordMap.signed_urls[blockId] = signedUrl\n }\n }\n } catch (err) {\n console.warn('NotionAPI getSignedfileUrls error', err)\n }\n }\n }\n\n public async getPageRaw(\n pageId: string,\n {\n ofetchOptions,\n chunkLimit = 100,\n chunkNumber = 0\n }: {\n chunkLimit?: number\n chunkNumber?: number\n ofetchOptions?: OfetchOptions\n } = {}\n ) {\n const parsedPageId = parsePageId(pageId)\n\n if (!parsedPageId) {\n throw new Error(`invalid notion pageId \"${pageId}\"`)\n }\n\n const body = {\n pageId: parsedPageId,\n limit: chunkLimit,\n chunkNumber,\n cursor: { stack: [] },\n verticalColumns: false\n }\n\n return this.fetch<notion.PageChunk>({\n endpoint: 'loadPageChunk',\n body,\n ofetchOptions\n })\n }\n\n public async getCollectionData(\n collectionId: string,\n collectionViewId: string,\n collectionView?: any,\n {\n limit = 999,\n searchQuery = '',\n userTimeZone = this._userTimeZone,\n loadContentCover = true,\n spaceId,\n ofetchOptions\n }: {\n type?: notion.CollectionViewType\n limit?: number\n searchQuery?: string\n userTimeZone?: string\n userLocale?: string\n loadContentCover?: boolean\n spaceId?: string\n ofetchOptions?: OfetchOptions\n } = {}\n ) {\n const type = collectionView?.type\n const isBoardType = type === 'board'\n const groupBy = isBoardType\n ? collectionView?.format?.board_columns_by\n : collectionView?.format?.collection_group_by\n\n let filters = []\n if (collectionView?.format?.property_filters) {\n filters = collectionView.format?.property_filters.map(\n (filterObj: any) => {\n //get the inner filter\n return {\n filter: filterObj?.filter?.filter,\n property: filterObj?.filter?.property\n }\n }\n )\n }\n\n // Fixes formula filters from not working\n if (collectionView?.query2?.filter?.filters) {\n filters.push(...collectionView.query2.filter.filters)\n }\n\n let loader: any = {\n type: 'reducer',\n reducers: {\n collection_group_results: {\n type: 'results',\n limit,\n loadContentCover\n }\n },\n sort: [],\n ...collectionView?.query2,\n filter: {\n filters,\n operator: 'and'\n },\n searchQuery,\n userTimeZone\n }\n\n if (groupBy) {\n const groups =\n collectionView?.format?.board_columns ||\n collectionView?.format?.collection_groups ||\n []\n const iterators = [isBoardType ? 'board' : 'group_aggregation', 'results']\n const operators = {\n checkbox: 'checkbox_is',\n url: 'string_starts_with',\n text: 'string_starts_with',\n select: 'enum_is',\n multi_select: 'enum_contains',\n created_time: 'date_is_within',\n undefined: 'is_empty'\n }\n\n const reducersQuery: Record<string, any> = {}\n for (const group of groups) {\n const {\n property,\n value: { value, type }\n } = group\n\n for (const iterator of iterators) {\n const iteratorProps =\n iterator === 'results'\n ? {\n type: iterator,\n limit\n }\n : {\n type: 'aggregation',\n aggregation: {\n aggregator: 'count'\n }\n }\n\n const isUncategorizedValue = value === undefined\n const isDateValue = value?.range\n // TODO: review dates reducers\n const queryLabel = isUncategorizedValue\n ? 'uncategorized'\n : isDateValue\n ? value.range?.start_date || value.range?.end_date\n : value?.value || value\n\n const queryValue =\n !isUncategorizedValue && (isDateValue || value?.value || value)\n\n reducersQuery[`${iterator}:${type}:${queryLabel}`] = {\n ...iteratorProps,\n filter: {\n operator: 'and',\n filters: [\n {\n property,\n filter: {\n operator: !isUncategorizedValue\n ? operators[type as keyof typeof operators]\n : 'is_empty',\n ...(!isUncategorizedValue && {\n value: {\n type: 'exact',\n value: queryValue\n }\n })\n }\n }\n ]\n }\n }\n }\n }\n\n const reducerLabel = isBoardType ? 'board_columns' : `${type}_groups`\n loader = {\n type: 'reducer',\n reducers: {\n [reducerLabel]: {\n type: 'groups',\n version: 'v2',\n groupBy,\n ...(collectionView?.query2?.filter && {\n filter: collectionView?.query2?.filter\n }),\n groupSortPreference: groups.map((group: any) => ({\n property: group?.property,\n value: {\n type: group?.value?.type,\n value: group?.value?.value\n }\n })),\n limit\n },\n ...reducersQuery\n },\n ...collectionView?.query2,\n searchQuery,\n userTimeZone,\n //TODO: add filters here\n filter: {\n filters,\n operator: 'and'\n }\n }\n }\n\n // if (isBoardType) {\n // console.log(\n // JSON.stringify(\n // {\n // collectionId,\n // collectionViewId,\n // loader,\n // groupBy: groupBy || 'NONE',\n // collectionViewQuery: collectionView.query2 || 'NONE'\n // },\n // null,\n // 2\n // )\n // )\n // }\n\n const headers: any = {}\n if (spaceId) {\n headers['x-notion-space-id'] = spaceId\n }\n\n return this.fetch<notion.CollectionInstance>({\n endpoint: 'queryCollection',\n body: {\n collection: {\n id: collectionId\n },\n collectionView: {\n id: collectionViewId\n },\n source: {\n type: 'collection',\n id: collectionId\n },\n loader\n },\n headers,\n ofetchOptions: {\n timeout: 60_000,\n ...ofetchOptions,\n params: {\n // TODO: spread ofetchOptions?.searchParams\n src: 'initial_load'\n }\n }\n })\n }\n\n public async getUsers(userIds: string[], ofetchOptions?: OfetchOptions) {\n return this.fetch<notion.RecordValues<notion.User>>({\n endpoint: 'getRecordValues',\n body: {\n requests: userIds.map(id => ({ id, table: 'notion_user' }))\n },\n ofetchOptions\n })\n }\n\n public async getBlocks(blockIds: string[], ofetchOptions?: OfetchOptions) {\n return this.fetch<notion.PageChunk>({\n endpoint: 'syncRecordValuesMain',\n body: {\n requests: blockIds.map(blockId => ({\n // TODO: when to use table 'space' vs 'block'?\n table: 'block',\n id: blockId,\n version: -1\n }))\n },\n ofetchOptions\n })\n }\n\n public async getSignedFileUrls(\n urls: types.SignedUrlRequest[],\n ofetchOptions?: OfetchOptions\n ) {\n return this.fetch<types.SignedUrlResponse>({\n endpoint: 'getSignedFileUrls',\n body: {\n urls\n },\n ofetchOptions\n })\n }\n\n public async search(\n params: notion.SearchParams,\n ofetchOptions?: OfetchOptions\n ) {\n const body = {\n type: 'BlocksInAncestor',\n source: 'quick_find_public',\n ancestorId: parsePageId(params.ancestorId),\n sort: {\n field: 'relevance'\n },\n limit: params.limit || 20,\n query: params.query,\n filters: {\n isDeletedOnly: false,\n isNavigableOnly: false,\n excludeTemplates: true,\n requireEditPermissions: false,\n includePublicPagesWithoutExplicitAccess: true,\n ancestors: [],\n createdBy: [],\n editedBy: [],\n lastEditedTime: {},\n createdTime: {},\n ...params.filters\n }\n }\n\n return this.fetch<notion.SearchResults>({\n endpoint: 'search',\n body,\n ofetchOptions\n })\n }\n\n public async fetch<T>({\n endpoint,\n body,\n ofetchOptions,\n headers: clientHeaders\n }: {\n endpoint: string\n body: object\n ofetchOptions?: OfetchOptions\n headers?: any\n }): Promise<T> {\n const headers: any = {\n ...clientHeaders,\n ...this._ofetchOptions?.headers,\n ...ofetchOptions?.headers,\n 'Content-Type': 'application/json'\n }\n\n if (this._authToken) {\n headers.cookie = `token_v2=${this._authToken}`\n }\n\n if (this._activeUser) {\n headers['x-notion-active-user-header'] = this._activeUser\n }\n\n const url = `${this._apiBaseUrl}/${endpoint}`\n\n /* const res = await ky.post(url, {\n mode: 'no-cors',\n ...this._ofetchOptions,\n ...ofetchOptions,\n json: body,\n headers\n }) */\n\n // TODO: we're awaiting the first fetch and then separately awaiting\n // `res.json()` because there seems to be some weird error which repros\n // sporadically when loading collections where the body is already used.\n // No idea why, but from my testing, separating these into two separate\n // steps seems to fix the issue locally for me...\n // console.log(endpoint, { bodyUsed: res.bodyUsed })\n\n /* return res.json<T>() */\n const res = ofetch(url, {\n method: 'POST',\n mode: 'no-cors',\n ...this._ofetchOptions,\n ...ofetchOptions,\n body,\n headers\n })\n return res\n }\n}\n"],"mappings":"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","names":["_a","_b","type"]}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@slogvo/notion-client",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Robust TypeScript client for the unofficial Notion API.",
6
+ "repository": "slogvo/vue-notion",
7
+ "author": "Vue Notion Contributors",
8
+ "license": "MIT",
9
+ "keywords": [
10
+ "notion",
11
+ "notion-api",
12
+ "client",
13
+ "typescript"
14
+ ],
15
+ "publishConfig": {
16
+ "access": "public",
17
+ "registry": "https://registry.npmjs.org/"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/slogvo/vue-notion/issues"
21
+ },
22
+ "homepage": "https://github.com/slogvo/vue-notion#readme",
23
+ "main": "./build/index.js",
24
+ "module": "./build/index.js",
25
+ "types": "./build/index.d.ts",
26
+ "sideEffects": false,
27
+ "files": [
28
+ "build"
29
+ ],
30
+ "engines": {
31
+ "node": ">=20"
32
+ },
33
+ "dependencies": {
34
+ "ofetch": "^1.4.1",
35
+ "p-map": "^7.0.3",
36
+ "@slogvo/notion-types": "0.1.0",
37
+ "@slogvo/notion-utils": "0.1.0"
38
+ },
39
+ "scripts": {
40
+ "build": "tsup",
41
+ "dev": "tsup --watch",
42
+ "watch": "tsup --watch --silent --onSuccess 'echo build successful'",
43
+ "clean": "del build",
44
+ "test": "run-s test:*",
45
+ "test:lint": "eslint .",
46
+ "test:typecheck": "tsc --noEmit",
47
+ "test:unit": "vitest run"
48
+ }
49
+ }