@visus-io/notion-sdk-ts 3.1.1 → 3.2.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/README.md CHANGED
@@ -19,7 +19,7 @@ A type-safe TypeScript SDK for the Notion API with Zod validation, OOP models, a
19
19
  - **Automatic pagination** `paginate()`, `paginateIterator()`, and `paginateWithMetadata()` helpers automatically fetch all pages, plus `iterateAllDataSourceRows()`/`collectAllDataSourceRows()` to work around the 10,000-result query cap
20
20
  - **Automatic rate limiting** Respects `Retry-After` header with exponential backoff fallback (configurable)
21
21
  - **Client-side size validation** Enforces Notion API size limits before sending requests
22
- - **Zero bloat** Single runtime dependency (`zod`); uses built-in `fetch` (Node 18+)
22
+ - **Zero bloat** Single runtime dependency (`zod`); uses built-in `fetch` (Node 22+)
23
23
 
24
24
  ## Installation
25
25
 
@@ -29,7 +29,7 @@ npm install @visus-io/notion-sdk-ts
29
29
  bun add @visus-io/notion-sdk-ts
30
30
  ```
31
31
 
32
- **Requirements:** Node.js 18+ or Bun 1.3.10+ (uses native `fetch`)
32
+ **Requirements:** Node.js 22+ or Bun 1.4.0+ (uses native `fetch`)
33
33
 
34
34
  ## Quick Start
35
35
 
@@ -118,7 +118,7 @@ bun run docs:dev # Run the docs site locally
118
118
  bun run docs:build # Build the docs site
119
119
  ```
120
120
 
121
- > **Note:** While this project uses Bun for development, the published package works with both Node.js 18+ and Bun 1.3.10+.
121
+ > **Note:** While this project uses Bun for development, the published package works with both Node.js 22+ and Bun 1.4.0+.
122
122
 
123
123
  See [**ARCHITECTURE.md**](./ARCHITECTURE.md) for project structure and architecture.
124
124
 
@@ -917,6 +917,11 @@ export declare class DataSourcesAPI extends BaseAPI<NotionDataSource, DataSource
917
917
  public_url: import("zod").ZodUnion<readonly [import("zod").ZodURL, import("zod").ZodNull]>;
918
918
  is_inline: import("zod").ZodBoolean;
919
919
  in_trash: import("zod").ZodBoolean;
920
+ database_type: import("zod").ZodOptional<import("zod").ZodEnum<{
921
+ tasks: "tasks";
922
+ projects: "projects";
923
+ skills: "skills";
924
+ }>>;
920
925
  }, import("zod/v4/core").$strip>;
921
926
  ModelClass: typeof DataSource;
922
927
  listType: "data_source";
@@ -1,5 +1,5 @@
1
1
  import type { NotionClient } from '../client';
2
- import { type NotionDatabase, type PaginatedList, type PaginationParameters } from '../schemas';
2
+ import { type DatabaseType, type NotionDatabase, type PaginatedList, type PaginationParameters } from '../schemas';
3
3
  import { Database, Page } from '../models';
4
4
  import { BaseAPI } from './base.api';
5
5
  /**
@@ -52,7 +52,9 @@ export type CreateDatabaseParent = {
52
52
  };
53
53
  /**
54
54
  * Initial data source configuration for creating a database.
55
- * As of API version 2025-09-03, databases are created with an initial data source.
55
+ * Provide this to create a database with a custom properties schema. To create a database
56
+ * from one of Notion's canonical schemas instead, use `database_type` on
57
+ * {@link CreateDatabaseOptions}.
56
58
  */
57
59
  export interface InitialDataSource {
58
60
  /** Data source properties schema */
@@ -62,14 +64,17 @@ export interface InitialDataSource {
62
64
  }
63
65
  /**
64
66
  * Options for creating a database.
65
- * As of API version 2025-09-03, databases require an `initial_data_source` object.
66
- * This object holds the properties schema. It replaces top-level properties.
67
+ * Provide exactly one of `database_type` or `initial_data_source`. `database_type` builds
68
+ * the database from one of Notion's canonical schemas. `initial_data_source` holds a custom
69
+ * properties schema. It replaces top-level properties.
67
70
  */
68
71
  export interface CreateDatabaseOptions {
69
72
  /** The parent object (page or workspace) */
70
73
  parent: CreateDatabaseParent;
74
+ /** Build the database from a canonical Notion schema instead of a custom one */
75
+ database_type?: DatabaseType;
71
76
  /** Initial data source configuration (contains properties schema) */
72
- initial_data_source: InitialDataSource;
77
+ initial_data_source?: InitialDataSource;
73
78
  /** Database title as rich text array */
74
79
  title?: unknown[];
75
80
  /** Database icon (emoji, file, or external) */
@@ -621,6 +626,11 @@ export declare class DatabasesAPI extends BaseAPI<NotionDatabase, Database> {
621
626
  is_inline: import("zod").ZodBoolean;
622
627
  is_locked: import("zod").ZodOptional<import("zod").ZodBoolean>;
623
628
  public_url: import("zod").ZodNullable<import("zod").ZodURL>;
629
+ database_type: import("zod").ZodOptional<import("zod").ZodEnum<{
630
+ tasks: "tasks";
631
+ projects: "projects";
632
+ skills: "skills";
633
+ }>>;
624
634
  }, import("zod/v4/core").$strip>;
625
635
  ModelClass: typeof Database;
626
636
  listType: "database";
@@ -652,6 +662,7 @@ export declare class DatabasesAPI extends BaseAPI<NotionDatabase, Database> {
652
662
  *
653
663
  * @param options - Options for creating the database
654
664
  * @returns The created database wrapped in a Database model
665
+ * @throws {NotionValidationError} If not exactly one of `database_type`/`initial_data_source` is provided
655
666
  *
656
667
  * @see https://developers.notion.com/reference/create-a-database
657
668
  */
@@ -74,19 +74,20 @@ class DatabasesAPI extends base_api_1.BaseAPI {
74
74
  *
75
75
  * @param options - Options for creating the database
76
76
  * @returns The created database wrapped in a Database model
77
+ * @throws {NotionValidationError} If not exactly one of `database_type`/`initial_data_source` is provided
77
78
  *
78
79
  * @see https://developers.notion.com/reference/create-a-database
79
80
  */
80
81
  async create(options) {
82
+ if (Boolean(options.database_type) === Boolean(options.initial_data_source)) {
83
+ throw new validation_1.NotionValidationError('Exactly one of database_type or initial_data_source must be provided');
84
+ }
81
85
  if (options.title) {
82
86
  (0, validation_1.validateArrayLength)(options.title, validation_1.LIMITS.ARRAY_ELEMENTS, 'title');
83
87
  }
84
88
  if (options.initial_data_source?.title) {
85
89
  (0, validation_1.validateArrayLength)(options.initial_data_source.title, validation_1.LIMITS.ARRAY_ELEMENTS, 'initial_data_source.title');
86
90
  }
87
- if (options.initial_data_source.title) {
88
- (0, validation_1.validateArrayLength)(options.initial_data_source.title, validation_1.LIMITS.ARRAY_ELEMENTS, 'initial_data_source.title');
89
- }
90
91
  return this.createResource('/databases', options);
91
92
  }
92
93
  /**
@@ -380,6 +380,9 @@ export declare class PagesAPI extends BaseAPI<NotionPage, Page> {
380
380
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
381
381
  type: import("zod").ZodLiteral<"string">;
382
382
  string: import("zod").ZodNullable<import("zod").ZodString>;
383
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
384
+ type: import("zod").ZodLiteral<"unsupported">;
385
+ unsupported: import("zod").ZodObject<{}, import("zod/v4/core").$strip>;
383
386
  }, import("zod/v4/core").$strip>], "type">;
384
387
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
385
388
  id: import("zod").ZodString;
package/dist/errors.d.ts CHANGED
@@ -14,6 +14,8 @@ export interface NotionErrorResponse {
14
14
  status: number;
15
15
  code: NotionErrorCode;
16
16
  message: string;
17
+ /** Extra machine-readable context for some error codes, for example `restricted_resource`. */
18
+ additional_data?: Record<string, unknown>;
17
19
  }
18
20
  /**
19
21
  * Thrown when the Notion API returns an error response.
@@ -42,6 +44,11 @@ export declare class NotionAPIError extends Error {
42
44
  * Check if the API could not find the requested object.
43
45
  */
44
46
  isNotFound(): boolean;
47
+ /**
48
+ * Check if a workspace restriction blocked the request.
49
+ * The Free workspace block limit is one example.
50
+ */
51
+ isRestrictedResource(): boolean;
45
52
  /**
46
53
  * Check if the error is a validation error.
47
54
  */
package/dist/errors.js CHANGED
@@ -43,6 +43,13 @@ class NotionAPIError extends Error {
43
43
  isNotFound() {
44
44
  return this.code === 'object_not_found';
45
45
  }
46
+ /**
47
+ * Check if a workspace restriction blocked the request.
48
+ * The Free workspace block limit is one example.
49
+ */
50
+ isRestrictedResource() {
51
+ return this.code === 'restricted_resource';
52
+ }
46
53
  /**
47
54
  * Check if the error is a validation error.
48
55
  */
@@ -1,5 +1,5 @@
1
1
  import { BaseModel, type TRUSTED } from './base.model';
2
- import { type NotionDataSource, type NotionFile, type NotionIcon, type NotionParent, type NotionPropertiesObject, type NotionPropertyObject, type NotionRichText, type NotionUser } from '../schemas';
2
+ import { type DatabaseType, type NotionDataSource, type NotionFile, type NotionIcon, type NotionParent, type NotionPropertiesObject, type NotionPropertyObject, type NotionRichText, type NotionUser } from '../schemas';
3
3
  /**
4
4
  * Data Source model class with helper methods.
5
5
  *
@@ -77,6 +77,11 @@ export declare class DataSource extends BaseModel<NotionDataSource> {
77
77
  * Returns whether the data source is in trash.
78
78
  */
79
79
  get inTrash(): boolean;
80
+ /**
81
+ * Returns the canonical database type this data source's parent database was built from,
82
+ * if any.
83
+ */
84
+ get databaseType(): DatabaseType | undefined;
80
85
  /**
81
86
  * Extracts the plain text title from the data source.
82
87
  */
@@ -116,6 +116,13 @@ class DataSource extends base_model_1.BaseModel {
116
116
  get inTrash() {
117
117
  return this.data.in_trash;
118
118
  }
119
+ /**
120
+ * Returns the canonical database type this data source's parent database was built from,
121
+ * if any.
122
+ */
123
+ get databaseType() {
124
+ return this.data.database_type;
125
+ }
119
126
  /**
120
127
  * Extracts the plain text title from the data source.
121
128
  */
@@ -1,5 +1,5 @@
1
1
  import { BaseModel, type TRUSTED } from './base.model';
2
- import { type DataSourceRef, type NotionDatabase, type NotionFile, type NotionIcon, type NotionParent, type NotionRichText, type NotionUser } from '../schemas';
2
+ import { type DatabaseType, type DataSourceRef, type NotionDatabase, type NotionFile, type NotionIcon, type NotionParent, type NotionRichText, type NotionUser } from '../schemas';
3
3
  /**
4
4
  * Database model class with helper methods.
5
5
  *
@@ -77,6 +77,10 @@ export declare class Database extends BaseModel<NotionDatabase> {
77
77
  * Returns the public URL if published, otherwise null.
78
78
  */
79
79
  get publicUrl(): string | null;
80
+ /**
81
+ * Returns the canonical database type this database was built from, if any.
82
+ */
83
+ get databaseType(): DatabaseType | undefined;
80
84
  /**
81
85
  * Extracts the plain text title from the database.
82
86
  */
@@ -116,6 +116,12 @@ class Database extends base_model_1.BaseModel {
116
116
  get publicUrl() {
117
117
  return this.data.public_url;
118
118
  }
119
+ /**
120
+ * Returns the canonical database type this database was built from, if any.
121
+ */
122
+ get databaseType() {
123
+ return this.data.database_type;
124
+ }
119
125
  /**
120
126
  * Extracts the plain text title from the database.
121
127
  */
@@ -28,6 +28,11 @@ export declare class User extends BaseModel<NotionUser> {
28
28
  * Get email if this is a person user.
29
29
  */
30
30
  getEmail(): string | undefined;
31
+ /**
32
+ * Get the person's email verification status.
33
+ * Returns `undefined` when the user is not a person.
34
+ */
35
+ getEmailVerified(): boolean | undefined;
31
36
  /**
32
37
  * Get bot information if this is a bot user.
33
38
  */
@@ -48,6 +48,16 @@ class User extends base_model_1.BaseModel {
48
48
  }
49
49
  return undefined;
50
50
  }
51
+ /**
52
+ * Get the person's email verification status.
53
+ * Returns `undefined` when the user is not a person.
54
+ */
55
+ getEmailVerified() {
56
+ if (this.isPerson()) {
57
+ return this.data.person.email_verified;
58
+ }
59
+ return undefined;
60
+ }
51
61
  /**
52
62
  * Get bot information if this is a bot user.
53
63
  */
@@ -822,6 +822,11 @@ export declare const dataSourceSchema: z.ZodObject<{
822
822
  public_url: z.ZodUnion<readonly [z.ZodURL, z.ZodNull]>;
823
823
  is_inline: z.ZodBoolean;
824
824
  in_trash: z.ZodBoolean;
825
+ database_type: z.ZodOptional<z.ZodEnum<{
826
+ tasks: "tasks";
827
+ projects: "projects";
828
+ skills: "skills";
829
+ }>>;
825
830
  }, z.core.$strip>;
826
831
  /**
827
832
  * @category Databases & Data Sources
@@ -42,6 +42,7 @@ const richText_schema_1 = require("./richText.schema");
42
42
  const shared_schema_1 = require("./shared.schema");
43
43
  const user_schema_1 = require("./user.schema");
44
44
  const propertyObjects_schema_1 = require("./propertyObjects.schema");
45
+ const database_schema_1 = require("./database.schema");
45
46
  /**
46
47
  * Notion data source object schema.
47
48
  *
@@ -88,6 +89,8 @@ exports.dataSourceSchema = z.object({
88
89
  is_inline: z.boolean(),
89
90
  /** Whether the data source is in the trash */
90
91
  in_trash: z.boolean(),
92
+ /** Canonical database type this data source's parent database was built from, if any */
93
+ database_type: z.enum(database_schema_1.DATABASE_TYPES).optional(),
91
94
  });
92
95
  /**
93
96
  * A single data source template reference. The List data source templates endpoint
@@ -20,6 +20,19 @@ export declare const dataSourceRefSchema: z.ZodObject<{
20
20
  * @category Databases & Data Sources
21
21
  */
22
22
  export type DataSourceRef = z.infer<typeof dataSourceRefSchema>;
23
+ /**
24
+ * A canonical database type.
25
+ *
26
+ * Pass one of these values as `database_type` when you create a database. Notion builds the
27
+ * database from its schema for that type, not a custom schema.
28
+ *
29
+ * @category Databases & Data Sources
30
+ */
31
+ export declare const DATABASE_TYPES: readonly ["tasks", "projects", "skills"];
32
+ /**
33
+ * @category Databases & Data Sources
34
+ */
35
+ export type DatabaseType = (typeof DATABASE_TYPES)[number];
23
36
  /**
24
37
  * @category Databases & Data Sources
25
38
  */
@@ -536,6 +549,11 @@ export declare const databaseSchema: z.ZodObject<{
536
549
  is_inline: z.ZodBoolean;
537
550
  is_locked: z.ZodOptional<z.ZodBoolean>;
538
551
  public_url: z.ZodNullable<z.ZodURL>;
552
+ database_type: z.ZodOptional<z.ZodEnum<{
553
+ tasks: "tasks";
554
+ projects: "projects";
555
+ skills: "skills";
556
+ }>>;
539
557
  }, z.core.$strip>;
540
558
  /**
541
559
  * @category Databases & Data Sources
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.databaseSchema = exports.dataSourceRefSchema = void 0;
36
+ exports.databaseSchema = exports.DATABASE_TYPES = exports.dataSourceRefSchema = void 0;
37
37
  const z = __importStar(require("zod"));
38
38
  const file_schema_1 = require("./file.schema");
39
39
  const icon_schema_1 = require("./icon.schema");
@@ -58,6 +58,15 @@ exports.dataSourceRefSchema = z.object({
58
58
  id: z.uuid(),
59
59
  name: z.string().trim(),
60
60
  });
61
+ /**
62
+ * A canonical database type.
63
+ *
64
+ * Pass one of these values as `database_type` when you create a database. Notion builds the
65
+ * database from its schema for that type, not a custom schema.
66
+ *
67
+ * @category Databases & Data Sources
68
+ */
69
+ exports.DATABASE_TYPES = ['tasks', 'projects', 'skills'];
61
70
  /**
62
71
  * @category Databases & Data Sources
63
72
  */
@@ -79,4 +88,5 @@ exports.databaseSchema = z.object({
79
88
  is_inline: z.boolean(),
80
89
  is_locked: z.boolean().optional(),
81
90
  public_url: z.nullable(z.url()),
91
+ database_type: z.enum(exports.DATABASE_TYPES).optional(),
82
92
  });
@@ -241,6 +241,9 @@ export declare const pageSchema: z.ZodObject<{
241
241
  }, z.core.$strip>, z.ZodObject<{
242
242
  type: z.ZodLiteral<"string">;
243
243
  string: z.ZodNullable<z.ZodString>;
244
+ }, z.core.$strip>, z.ZodObject<{
245
+ type: z.ZodLiteral<"unsupported">;
246
+ unsupported: z.ZodObject<{}, z.core.$strip>;
244
247
  }, z.core.$strip>], "type">;
245
248
  }, z.core.$strip>, z.ZodObject<{
246
249
  id: z.ZodString;
@@ -118,6 +118,9 @@ declare const formulaPropertySchema: z.ZodObject<{
118
118
  }, z.core.$strip>, z.ZodObject<{
119
119
  type: z.ZodLiteral<"string">;
120
120
  string: z.ZodNullable<z.ZodString>;
121
+ }, z.core.$strip>, z.ZodObject<{
122
+ type: z.ZodLiteral<"unsupported">;
123
+ unsupported: z.ZodObject<{}, z.core.$strip>;
121
124
  }, z.core.$strip>], "type">;
122
125
  }, z.core.$strip>;
123
126
  /** Last edited by property. */
@@ -885,6 +888,9 @@ export declare const pagePropertiesSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
885
888
  }, z.core.$strip>, z.ZodObject<{
886
889
  type: z.ZodLiteral<"string">;
887
890
  string: z.ZodNullable<z.ZodString>;
891
+ }, z.core.$strip>, z.ZodObject<{
892
+ type: z.ZodLiteral<"unsupported">;
893
+ unsupported: z.ZodObject<{}, z.core.$strip>;
888
894
  }, z.core.$strip>], "type">;
889
895
  }, z.core.$strip>, z.ZodObject<{
890
896
  id: z.ZodString;
@@ -113,6 +113,7 @@ const formulaPropertySchema = z.object({
113
113
  }),
114
114
  z.object({ type: z.literal('number'), number: z.number().nullable() }),
115
115
  z.object({ type: z.literal('string'), string: z.string().trim().nullable() }),
116
+ z.object({ type: z.literal('unsupported'), unsupported: z.object({}) }),
116
117
  ]),
117
118
  });
118
119
  /** Last edited by property. */
@@ -435,6 +435,9 @@ export declare const viewQueryResponseSchema: z.ZodObject<{
435
435
  }, z.core.$strip>, z.ZodObject<{
436
436
  type: z.ZodLiteral<"string">;
437
437
  string: z.ZodNullable<z.ZodString>;
438
+ }, z.core.$strip>, z.ZodObject<{
439
+ type: z.ZodLiteral<"unsupported">;
440
+ unsupported: z.ZodObject<{}, z.core.$strip>;
438
441
  }, z.core.$strip>], "type">;
439
442
  }, z.core.$strip>, z.ZodObject<{
440
443
  id: z.ZodString;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@visus-io/notion-sdk-ts",
3
- "version": "3.1.1",
3
+ "version": "3.2.0",
4
4
  "private": false,
5
5
  "description": "TypeScript SDK for the Notion API",
6
6
  "keywords": [
@@ -63,26 +63,27 @@
63
63
  ]
64
64
  },
65
65
  "dependencies": {
66
- "zod": "^4.3.6"
66
+ "zod": "^4.4.3"
67
67
  },
68
68
  "devDependencies": {
69
- "@commitlint/cli": "^21.0.0",
70
- "@commitlint/config-conventional": "^21.0.0",
71
- "@types/node": "^25.2.1",
72
- "@vitest/coverage-v8": "^4.0.18",
73
- "eslint": "^10.0.0",
74
- "eslint-plugin-zod": "4.9.0",
69
+ "@commitlint/cli": "^21.2.2",
70
+ "@commitlint/config-conventional": "^21.2.2",
71
+ "@types/bun": "^1.4.0",
72
+ "@types/node": "^25.9.5",
73
+ "@vitest/coverage-v8": "^4.1.11",
74
+ "eslint": "^10.9.0",
75
+ "eslint-plugin-zod": "4.12.0",
75
76
  "husky": "^9.1.7",
76
- "lint-staged": "^16.2.7",
77
+ "lint-staged": "^16.4.0",
77
78
  "msw": "2.15.0",
78
- "prettier": "^3.8.1",
79
- "prettier-plugin-packagejson": "^3.0.0",
79
+ "prettier": "^3.9.6",
80
+ "prettier-plugin-packagejson": "^3.0.2",
80
81
  "typescript": "^6.0.3",
81
- "typescript-eslint": "^8.54.0",
82
- "vitest": "^4.0.18"
82
+ "typescript-eslint": "^8.67.0",
83
+ "vitest": "^4.1.11"
83
84
  },
84
85
  "engines": {
85
- "bun": ">=1.3.10",
86
- "node": ">=18.0.0"
86
+ "bun": ">=1.4.0",
87
+ "node": ">=22.0.0"
87
88
  }
88
89
  }