@visus-io/notion-sdk-ts 3.0.1 → 3.0.2

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.
@@ -35,18 +35,6 @@ export declare abstract class BaseAPI<TResponse, TModel> {
35
35
  * return this.deleteResource(`/pages/${pageId}`);
36
36
  */
37
37
  protected deleteResource(resourcePath: string): Promise<TModel>;
38
- /**
39
- * Build filter_properties body parameter from an array of property names.
40
- *
41
- * @param filterProperties - Optional array of property names to include in the response
42
- * @returns Body object with filter_properties parameter for API requests
43
- *
44
- * @throws {NotionValidationError} If array exceeds LIMITS.ARRAY_ELEMENTS
45
- * @example
46
- * const body = this.buildFilterPropertiesBody(['Name', 'Status']);
47
- * // body will be: { filter_properties: ['Name', 'Status'] }
48
- */
49
- protected buildFilterPropertiesBody(filterProperties?: string[]): Record<string, unknown>;
50
38
  /**
51
39
  * Build pagination body parameters from PaginationParameters.
52
40
  *
@@ -61,6 +49,9 @@ export declare abstract class BaseAPI<TResponse, TModel> {
61
49
  /**
62
50
  * Build filter_properties query parameter from an array of property names.
63
51
  *
52
+ * Notion sends this as a repeated query key (`filter_properties=a&filter_properties=b`),
53
+ * not a single comma-joined value, so the array is preserved for `NotionClient` to expand.
54
+ *
64
55
  * @param filterProperties - Optional array of property names to include in the response
65
56
  * @returns Query object with filter_properties parameter for API requests
66
57
  *
@@ -68,9 +59,9 @@ export declare abstract class BaseAPI<TResponse, TModel> {
68
59
  *
69
60
  * @example
70
61
  * const query = this.buildFilterPropertiesQuery(['Name', 'Status']);
71
- * // query will be: { filter_properties: 'Name,Status' }
62
+ * // query will be: { filter_properties: ['Name', 'Status'] }
72
63
  */
73
- protected buildFilterPropertiesQuery(filterProperties?: string[]): Record<string, string>;
64
+ protected buildFilterPropertiesQuery(filterProperties?: string[]): Record<string, string[]>;
74
65
  /**
75
66
  * Build pagination query parameters from PaginationParameters.
76
67
  *
@@ -106,7 +97,7 @@ export declare abstract class BaseAPI<TResponse, TModel> {
106
97
  * @example
107
98
  * return this.retrieveResource(`/pages/${pageId}`, query);
108
99
  */
109
- protected retrieveResource(resourcePath: string, query?: Record<string, string>): Promise<TModel>;
100
+ protected retrieveResource(resourcePath: string, query?: Record<string, string | string[]>): Promise<TModel>;
110
101
  /**
111
102
  * Retrieve a paginated list of resources via GET request.
112
103
  *
@@ -120,7 +111,7 @@ export declare abstract class BaseAPI<TResponse, TModel> {
120
111
  * { page_size: '50' },
121
112
  * );
122
113
  */
123
- protected listResources(resourcePath: string, query?: Record<string, string>): Promise<PaginatedList<TModel>>;
114
+ protected listResources(resourcePath: string, query?: Record<string, string | string[]>): Promise<PaginatedList<TModel>>;
124
115
  /**
125
116
  * Update an existing resource via PATCH request.
126
117
  *
@@ -29,25 +29,6 @@ class BaseAPI {
29
29
  });
30
30
  return this.parseAndWrap(response);
31
31
  }
32
- /**
33
- * Build filter_properties body parameter from an array of property names.
34
- *
35
- * @param filterProperties - Optional array of property names to include in the response
36
- * @returns Body object with filter_properties parameter for API requests
37
- *
38
- * @throws {NotionValidationError} If array exceeds LIMITS.ARRAY_ELEMENTS
39
- * @example
40
- * const body = this.buildFilterPropertiesBody(['Name', 'Status']);
41
- * // body will be: { filter_properties: ['Name', 'Status'] }
42
- */
43
- buildFilterPropertiesBody(filterProperties) {
44
- const body = {};
45
- if (filterProperties) {
46
- (0, validation_1.validateArrayLength)(filterProperties, validation_1.LIMITS.ARRAY_ELEMENTS, 'filter_properties');
47
- body.filter_properties = filterProperties;
48
- }
49
- return body;
50
- }
51
32
  /**
52
33
  * Build pagination body parameters from PaginationParameters.
53
34
  *
@@ -71,6 +52,9 @@ class BaseAPI {
71
52
  /**
72
53
  * Build filter_properties query parameter from an array of property names.
73
54
  *
55
+ * Notion sends this as a repeated query key (`filter_properties=a&filter_properties=b`),
56
+ * not a single comma-joined value, so the array is preserved for `NotionClient` to expand.
57
+ *
74
58
  * @param filterProperties - Optional array of property names to include in the response
75
59
  * @returns Query object with filter_properties parameter for API requests
76
60
  *
@@ -78,13 +62,13 @@ class BaseAPI {
78
62
  *
79
63
  * @example
80
64
  * const query = this.buildFilterPropertiesQuery(['Name', 'Status']);
81
- * // query will be: { filter_properties: 'Name,Status' }
65
+ * // query will be: { filter_properties: ['Name', 'Status'] }
82
66
  */
83
67
  buildFilterPropertiesQuery(filterProperties) {
84
68
  const query = {};
85
69
  if (filterProperties) {
86
70
  (0, validation_1.validateArrayLength)(filterProperties, validation_1.LIMITS.ARRAY_ELEMENTS, 'filter_properties');
87
- query.filter_properties = filterProperties.join(',');
71
+ query.filter_properties = filterProperties;
88
72
  }
89
73
  return query;
90
74
  }
@@ -77,7 +77,14 @@ export interface QueryDataSourceOptions extends PaginationParameters {
77
77
  filter_properties?: string[];
78
78
  /** Filter by result type (for wikis) */
79
79
  result_type?: 'page' | 'data_source';
80
- /** Filter by trash status */
80
+ /** Whether to return only archived pages (true) or only non-archived pages (false, default) */
81
+ is_archived?: boolean;
82
+ /**
83
+ * Whether to return only trashed pages (true) or only non-trashed pages (false).
84
+ *
85
+ * @deprecated Use `is_archived` instead. Kept as an alias forwarded into
86
+ * `is_archived`; if both are provided, `is_archived` takes precedence.
87
+ */
81
88
  in_trash?: boolean;
82
89
  }
83
90
  /**
@@ -61,19 +61,21 @@ class DataSourcesAPI extends base_api_1.BaseAPI {
61
61
  if (options?.start_cursor) {
62
62
  body.start_cursor = options.start_cursor;
63
63
  }
64
- if (options?.filter_properties) {
65
- (0, validation_1.validateArrayLength)(options.filter_properties, validation_1.LIMITS.ARRAY_ELEMENTS, 'filter_properties');
66
- body.filter_properties = options.filter_properties;
64
+ if (options?.in_trash !== undefined && options?.is_archived === undefined) {
65
+ console.warn('[notion-sdk-ts] QueryDataSourceOptions.in_trash is deprecated, use is_archived instead.');
67
66
  }
68
- if (options?.in_trash !== undefined) {
69
- body.in_trash = options.in_trash;
67
+ const isArchived = options?.is_archived ?? options?.in_trash;
68
+ if (isArchived !== undefined) {
69
+ body.is_archived = isArchived;
70
70
  }
71
71
  if (options?.result_type) {
72
72
  body.result_type = options.result_type;
73
73
  }
74
+ const query = this.buildFilterPropertiesQuery(options?.filter_properties);
74
75
  const response = await this.client.request({
75
76
  method: 'POST',
76
77
  path: `/data_sources/${dataSourceId}/query`,
78
+ query: Object.keys(query).length > 0 ? query : undefined,
77
79
  body: Object.keys(body).length > 0 ? body : undefined,
78
80
  });
79
81
  const listSchema = (0, schemas_1.paginatedListSchema)(schemas_1.pageSchema);
@@ -48,11 +48,12 @@ class DatabasesAPI extends base_api_1.BaseAPI {
48
48
  ...(options?.filter ? { filter: options.filter } : {}),
49
49
  ...(options?.sorts ? { sorts: options.sorts } : {}),
50
50
  ...this.buildPaginationBody(options),
51
- ...this.buildFilterPropertiesBody(options?.filter_properties),
52
51
  };
52
+ const query = this.buildFilterPropertiesQuery(options?.filter_properties);
53
53
  const response = await this.client.request({
54
54
  method: 'POST',
55
55
  path: `/databases/${databaseId}/query`,
56
+ query: Object.keys(query).length > 0 ? query : undefined,
56
57
  body: Object.keys(body).length > 0 ? body : undefined,
57
58
  });
58
59
  const listSchema = (0, schemas_1.paginatedListSchema)(schemas_1.pageSchema);
@@ -73,6 +73,12 @@ export declare class FileUploadsAPI extends BaseAPI<NotionFileUpload, FileUpload
73
73
  * @see https://developers.notion.com/reference/complete-a-file-upload
74
74
  */
75
75
  complete(completeUrl: string): Promise<FileUpload>;
76
+ /**
77
+ * Extracts the request path from a complete URL, accepting both absolute
78
+ * URLs (e.g. `https://api.notion.com/v1/file_uploads/.../complete`) and
79
+ * relative paths (e.g. `/v1/file_uploads/.../complete` or the path alone).
80
+ */
81
+ private static toRequestPath;
76
82
  /**
77
83
  * Helper method to upload a file in one call.
78
84
  * This combines initiate, upload, and complete steps.
@@ -66,12 +66,27 @@ class FileUploadsAPI extends base_api_1.BaseAPI {
66
66
  async complete(completeUrl) {
67
67
  const response = await this.client.request({
68
68
  method: 'POST',
69
- path: completeUrl.replace(/^https:\/\/api\.notion\.com\/v1/, ''),
69
+ path: FileUploadsAPI.toRequestPath(completeUrl),
70
70
  body: {},
71
71
  });
72
72
  const parsed = schemas_1.fileUploadSchema.parse(response);
73
73
  return new models_1.FileUpload(parsed);
74
74
  }
75
+ /**
76
+ * Extracts the request path from a complete URL, accepting both absolute
77
+ * URLs (e.g. `https://api.notion.com/v1/file_uploads/.../complete`) and
78
+ * relative paths (e.g. `/v1/file_uploads/.../complete` or the path alone).
79
+ */
80
+ static toRequestPath(completeUrl) {
81
+ let path;
82
+ try {
83
+ path = new URL(completeUrl).pathname;
84
+ }
85
+ catch {
86
+ path = completeUrl;
87
+ }
88
+ return path.replace(/^\/v1/, '');
89
+ }
75
90
  /**
76
91
  * Helper method to upload a file in one call.
77
92
  * This combines initiate, upload, and complete steps.
package/dist/client.d.ts CHANGED
@@ -26,7 +26,7 @@ export interface NotionClientOptions {
26
26
  export interface RequestOptions {
27
27
  method: 'GET' | 'POST' | 'PATCH' | 'DELETE';
28
28
  path: string;
29
- query?: Record<string, string | number | boolean | undefined>;
29
+ query?: Record<string, string | number | boolean | string[] | undefined>;
30
30
  body?: unknown;
31
31
  }
32
32
  /**
package/dist/client.js CHANGED
@@ -110,7 +110,13 @@ class NotionClient {
110
110
  const url = new URL(`${this.baseUrl}/v1${path}`);
111
111
  if (query) {
112
112
  Object.entries(query).forEach(([key, value]) => {
113
- if (value !== undefined) {
113
+ if (value === undefined) {
114
+ return;
115
+ }
116
+ if (Array.isArray(value)) {
117
+ value.forEach((item) => url.searchParams.append(key, item));
118
+ }
119
+ else {
114
120
  url.searchParams.append(key, String(value));
115
121
  }
116
122
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@visus-io/notion-sdk-ts",
3
- "version": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "private": false,
5
5
  "description": "TypeScript SDK for the Notion API",
6
6
  "keywords": [
@@ -37,7 +37,7 @@
37
37
  "LICENSE"
38
38
  ],
39
39
  "scripts": {
40
- "build": "tsc",
40
+ "build": "tsc -p tsconfig.build.json",
41
41
  "format": "prettier --write .",
42
42
  "format:check": "prettier --check .",
43
43
  "lint": "eslint .",
@@ -68,9 +68,10 @@
68
68
  "@types/node": "^25.2.1",
69
69
  "@vitest/coverage-v8": "^4.0.18",
70
70
  "eslint": "^10.0.0",
71
- "eslint-plugin-zod": "4.7.0",
71
+ "eslint-plugin-zod": "4.8.0",
72
72
  "husky": "^9.1.7",
73
73
  "lint-staged": "^16.2.7",
74
+ "msw": "2.15.0",
74
75
  "prettier": "^3.8.1",
75
76
  "prettier-plugin-packagejson": "^3.0.0",
76
77
  "typescript": "^6.0.0",