@elumixor/notion-orm 2.0.2 → 2.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.
@@ -1,6 +1,5 @@
1
- import type { CreatePageParameters, UpdatePageParameters } from "@notionhq/client/build/src/api-endpoints";
2
1
  import type { ZodTypeAny } from "zod";
3
- import type { FindManyArgs, PaginateArgs, QueryFilter, SupportedNotionColumnType } from "./types";
2
+ import type { FindManyArgs, IconCoverResult, PaginateArgs, QueryFilter, SupportedNotionColumnType } from "./types";
4
3
  export type camelPropertyNameToNameAndTypeMapType = Record<string, {
5
4
  columnName: string;
6
5
  type: SupportedNotionColumnType;
@@ -20,100 +19,104 @@ export declare class DatabaseClient<DatabaseSchemaType extends Record<string, an
20
19
  schema: ZodTypeAny;
21
20
  });
22
21
  /** Find all matching records. When `stream` is set, returns an AsyncIterable fetching in batches of that size. */
23
- findMany(args: FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType> & {
22
+ findMany<Args extends FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType> & {
24
23
  stream: number;
25
- }): AsyncIterable<Partial<DatabaseSchemaType> & {
24
+ }>(args: Args): AsyncIterable<Partial<DatabaseSchemaType> & {
26
25
  id: string;
27
- }>;
28
- findMany<S extends Partial<Record<keyof DatabaseSchemaType, true>>>(args: Omit<FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType>, "stream" | "select"> & {
26
+ } & IconCoverResult<Args>>;
27
+ findMany<S extends Partial<Record<keyof DatabaseSchemaType, true>>, Args extends Omit<FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType>, "stream" | "select"> & {
29
28
  select: S;
30
- }): Promise<({
29
+ }>(args: Args): Promise<({
31
30
  [K in Extract<keyof S, keyof DatabaseSchemaType>]?: DatabaseSchemaType[K];
32
31
  } & {
33
32
  id: string;
34
- })[]>;
35
- findMany<O extends Partial<Record<keyof DatabaseSchemaType, true>>>(args: Omit<FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType>, "stream" | "omit"> & {
33
+ } & IconCoverResult<Args>)[]>;
34
+ findMany<O extends Partial<Record<keyof DatabaseSchemaType, true>>, Args extends Omit<FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType>, "stream" | "omit"> & {
36
35
  omit: O;
37
- }): Promise<({
36
+ }>(args: Args): Promise<({
38
37
  [K in Exclude<keyof DatabaseSchemaType, keyof O>]?: DatabaseSchemaType[K];
39
38
  } & {
40
39
  id: string;
41
- })[]>;
42
- findMany(args?: Omit<FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType>, "stream">): Promise<(Partial<DatabaseSchemaType> & {
40
+ } & IconCoverResult<Args>)[]>;
41
+ findMany<Args extends Omit<FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType>, "stream">>(args?: Args): Promise<(Partial<DatabaseSchemaType> & {
43
42
  id: string;
44
- })[]>;
43
+ } & IconCoverResult<Args>)[]>;
45
44
  /**
46
45
  * Fetch one page of results using Notion's native cursor.
47
46
  * Pass the returned `nextCursor` as `after` on the next call to get the next page.
48
47
  */
49
- paginate<S extends Partial<Record<keyof DatabaseSchemaType, true>>>(args: Omit<PaginateArgs<DatabaseSchemaType, ColumnNameToColumnType>, "select"> & {
48
+ paginate<S extends Partial<Record<keyof DatabaseSchemaType, true>>, Args extends Omit<PaginateArgs<DatabaseSchemaType, ColumnNameToColumnType>, "select"> & {
50
49
  select: S;
51
- }): Promise<{
50
+ }>(args: Args): Promise<{
52
51
  data: ({
53
52
  [K in Extract<keyof S, keyof DatabaseSchemaType>]?: DatabaseSchemaType[K];
54
53
  } & {
55
54
  id: string;
56
- })[];
55
+ } & IconCoverResult<Args>)[];
57
56
  nextCursor: string | null;
58
57
  hasMore: boolean;
59
58
  }>;
60
- paginate<O extends Partial<Record<keyof DatabaseSchemaType, true>>>(args: Omit<PaginateArgs<DatabaseSchemaType, ColumnNameToColumnType>, "omit"> & {
59
+ paginate<O extends Partial<Record<keyof DatabaseSchemaType, true>>, Args extends Omit<PaginateArgs<DatabaseSchemaType, ColumnNameToColumnType>, "omit"> & {
61
60
  omit: O;
62
- }): Promise<{
61
+ }>(args: Args): Promise<{
63
62
  data: ({
64
63
  [K in Exclude<keyof DatabaseSchemaType, keyof O>]?: DatabaseSchemaType[K];
65
64
  } & {
66
65
  id: string;
67
- })[];
66
+ } & IconCoverResult<Args>)[];
68
67
  nextCursor: string | null;
69
68
  hasMore: boolean;
70
69
  }>;
71
- paginate(args?: PaginateArgs<DatabaseSchemaType, ColumnNameToColumnType>): Promise<{
70
+ paginate<Args extends PaginateArgs<DatabaseSchemaType, ColumnNameToColumnType>>(args?: Args): Promise<{
72
71
  data: (Partial<DatabaseSchemaType> & {
73
72
  id: string;
74
- })[];
73
+ } & IconCoverResult<Args>)[];
75
74
  nextCursor: string | null;
76
75
  hasMore: boolean;
77
76
  }>;
78
77
  /** Find the first matching record, or null if none found. */
79
- findFirst<S extends Partial<Record<keyof DatabaseSchemaType, true>>>(args: Omit<FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType>, "stream" | "select"> & {
78
+ findFirst<S extends Partial<Record<keyof DatabaseSchemaType, true>>, Args extends Omit<FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType>, "stream" | "select"> & {
80
79
  select: S;
81
- }): Promise<({
80
+ }>(args: Args): Promise<({
82
81
  [K in Extract<keyof S, keyof DatabaseSchemaType>]?: DatabaseSchemaType[K];
83
82
  } & {
84
83
  id: string;
85
- }) | null>;
86
- findFirst<O extends Partial<Record<keyof DatabaseSchemaType, true>>>(args: Omit<FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType>, "stream" | "omit"> & {
84
+ } & IconCoverResult<Args>) | null>;
85
+ findFirst<O extends Partial<Record<keyof DatabaseSchemaType, true>>, Args extends Omit<FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType>, "stream" | "omit"> & {
87
86
  omit: O;
88
- }): Promise<({
87
+ }>(args: Args): Promise<({
89
88
  [K in Exclude<keyof DatabaseSchemaType, keyof O>]?: DatabaseSchemaType[K];
90
89
  } & {
91
90
  id: string;
92
- }) | null>;
93
- findFirst(args?: Omit<FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType>, "stream">): Promise<(Partial<DatabaseSchemaType> & {
91
+ } & IconCoverResult<Args>) | null>;
92
+ findFirst<Args extends Omit<FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType>, "stream">>(args?: Args): Promise<(Partial<DatabaseSchemaType> & {
94
93
  id: string;
95
- }) | null>;
94
+ } & IconCoverResult<Args>) | null>;
96
95
  /** Find a record by its Notion page ID. Returns null if not found. */
97
- findUnique(args: {
96
+ findUnique<Args extends {
98
97
  where: {
99
98
  id: string;
100
99
  };
101
100
  select?: Partial<Record<keyof DatabaseSchemaType, true>>;
102
101
  omit?: Partial<Record<keyof DatabaseSchemaType, true>>;
103
- }): Promise<(Partial<DatabaseSchemaType> & {
102
+ $icon?: true;
103
+ $cover?: true;
104
+ }>(args: Args): Promise<(Partial<DatabaseSchemaType> & {
104
105
  id: string;
105
- }) | null>;
106
+ } & IconCoverResult<Args>) | null>;
106
107
  /** Create a new record and return it. */
107
108
  create(args: {
108
109
  data: DatabaseSchemaType;
109
- icon?: CreatePageParameters["icon"];
110
+ $icon?: string;
111
+ $cover?: string;
110
112
  }): Promise<Partial<DatabaseSchemaType> & {
111
113
  id: string;
112
114
  }>;
113
115
  /** Create multiple records and return them. */
114
116
  createMany(args: {
115
117
  data: DatabaseSchemaType[];
116
- icon?: CreatePageParameters["icon"];
118
+ $icon?: string;
119
+ $cover?: string;
117
120
  }): Promise<(Partial<DatabaseSchemaType> & {
118
121
  id: string;
119
122
  })[]>;
@@ -123,13 +126,15 @@ export declare class DatabaseClient<DatabaseSchemaType extends Record<string, an
123
126
  id: string;
124
127
  };
125
128
  data: Partial<DatabaseSchemaType>;
126
- icon?: UpdatePageParameters["icon"];
129
+ $icon?: string;
130
+ $cover?: string;
127
131
  }): Promise<void>;
128
132
  /** Update all records matching the filter. Returns the count of updated records. */
129
133
  updateMany(args: {
130
134
  where?: QueryFilter<DatabaseSchemaType, ColumnNameToColumnType>;
131
135
  data: Partial<DatabaseSchemaType>;
132
- icon?: UpdatePageParameters["icon"];
136
+ $icon?: string;
137
+ $cover?: string;
133
138
  }): Promise<{
134
139
  count: number;
135
140
  }>;
@@ -138,7 +143,8 @@ export declare class DatabaseClient<DatabaseSchemaType extends Record<string, an
138
143
  where: QueryFilter<DatabaseSchemaType, ColumnNameToColumnType>;
139
144
  create: DatabaseSchemaType;
140
145
  update: Partial<DatabaseSchemaType>;
141
- icon?: CreatePageParameters["icon"];
146
+ $icon?: string;
147
+ $cover?: string;
142
148
  }): Promise<{
143
149
  created: boolean;
144
150
  id: string;
@@ -4,7 +4,10 @@ import type { camelPropertyNameToNameAndTypeMapType } from "./client";
4
4
  /**
5
5
  * Transforms Notion API query response into simplified format
6
6
  */
7
- export declare function buildQueryResponse<DatabaseSchemaType extends Record<string, any>>(res: QueryDataSourceResponse, camelPropertyNameToNameAndTypeMap: camelPropertyNameToNameAndTypeMapType, validateSchema: (result: Partial<DatabaseSchemaType>) => void): (Partial<DatabaseSchemaType> & {
7
+ export declare function buildQueryResponse<DatabaseSchemaType extends Record<string, any>>(res: QueryDataSourceResponse, camelPropertyNameToNameAndTypeMap: camelPropertyNameToNameAndTypeMapType, validateSchema: (result: Partial<DatabaseSchemaType>) => void, meta?: {
8
+ $icon?: true;
9
+ $cover?: true;
10
+ }): (Partial<DatabaseSchemaType> & {
8
11
  id: string;
9
12
  })[];
10
13
  /**
@@ -126,6 +126,8 @@ export type FindManyArgs<Y extends Record<string, any>, T extends Record<keyof Y
126
126
  skip?: number;
127
127
  /** When set, findMany returns an AsyncIterable, fetching results in batches of this size */
128
128
  stream?: number;
129
+ $icon?: true;
130
+ $cover?: true;
129
131
  };
130
132
  export type PaginateArgs<Y extends Record<string, any>, T extends Record<keyof Y, SupportedNotionColumnType>> = {
131
133
  where?: QueryFilter<Y, T>;
@@ -135,6 +137,8 @@ export type PaginateArgs<Y extends Record<string, any>, T extends Record<keyof Y
135
137
  take?: number;
136
138
  /** Opaque cursor token returned by a previous paginate() call */
137
139
  after?: string;
140
+ $icon?: true;
141
+ $cover?: true;
138
142
  };
139
143
  /** @deprecated Use FindManyArgs */
140
144
  export type Query<Y extends Record<string, any>, T extends Record<keyof Y, SupportedNotionColumnType>> = FindManyArgs<Y, T>;
@@ -161,6 +165,15 @@ type apiOrFilter = {
161
165
  export type QueryResult<DatabaseSchema> = Partial<DatabaseSchema> & {
162
166
  id: string;
163
167
  };
168
+ export type IconCoverResult<Args> = (Args extends {
169
+ $icon: true;
170
+ } ? {
171
+ $icon: string | null;
172
+ } : unknown) & (Args extends {
173
+ $cover: true;
174
+ } ? {
175
+ $cover: string | null;
176
+ } : unknown);
164
177
  export type QueryResultType<Y, Args> = Args extends {
165
178
  select: infer S extends Record<string, unknown>;
166
179
  } ? {
package/dist/index.js CHANGED
@@ -1465,7 +1465,7 @@ var emailCall = (args) => {
1465
1465
  };
1466
1466
 
1467
1467
  // src/db-client/query.ts
1468
- function buildQueryResponse(res, camelPropertyNameToNameAndTypeMap, validateSchema) {
1468
+ function buildQueryResponse(res, camelPropertyNameToNameAndTypeMap, validateSchema, meta) {
1469
1469
  const results = res.results.map((result, index) => {
1470
1470
  if (result.object === "page" && !("properties" in result)) {
1471
1471
  console.log("Skipping this page: ", { result });
@@ -1480,6 +1480,24 @@ function buildQueryResponse(res, camelPropertyNameToNameAndTypeMap, validateSche
1480
1480
  simpleResult[camelizeColumnName] = getResponseValue(columnType, result2);
1481
1481
  }
1482
1482
  }
1483
+ if (meta?.$icon) {
1484
+ const icon = result.icon;
1485
+ if (icon?.type === "external")
1486
+ simpleResult.$icon = icon.external?.url ?? null;
1487
+ else if (icon?.type === "file")
1488
+ simpleResult.$icon = icon.file?.url ?? null;
1489
+ else
1490
+ simpleResult.$icon = null;
1491
+ }
1492
+ if (meta?.$cover) {
1493
+ const cover = result.cover;
1494
+ if (cover?.type === "external")
1495
+ simpleResult.$cover = cover.external?.url ?? null;
1496
+ else if (cover?.type === "file")
1497
+ simpleResult.$cover = cover.file?.url ?? null;
1498
+ else
1499
+ simpleResult.$cover = null;
1500
+ }
1483
1501
  if (index === 0) {
1484
1502
  validateSchema(simpleResult);
1485
1503
  }
@@ -1629,7 +1647,7 @@ class DatabaseClient {
1629
1647
  page_size: args?.take ?? 100,
1630
1648
  start_cursor: args?.after
1631
1649
  });
1632
- const results = buildQueryResponse(response, this.camelPropertyNameToNameAndTypeMap, (r) => this.validateDatabaseSchema(r));
1650
+ const results = buildQueryResponse(response, this.camelPropertyNameToNameAndTypeMap, (r) => this.validateDatabaseSchema(r), { $icon: args?.$icon, $cover: args?.$cover });
1633
1651
  return {
1634
1652
  data: this.applySelectOmit(results, args?.select, args?.omit),
1635
1653
  nextCursor: response.has_more ? response.next_cursor ?? null : null,
@@ -1639,7 +1657,7 @@ class DatabaseClient {
1639
1657
  async findFirst(args) {
1640
1658
  const queryCall = this.buildQueryCall(args ?? {});
1641
1659
  const response = await this.client.dataSources.query({ ...queryCall, page_size: 1 });
1642
- const results = buildQueryResponse(response, this.camelPropertyNameToNameAndTypeMap, (r) => this.validateDatabaseSchema(r));
1660
+ const results = buildQueryResponse(response, this.camelPropertyNameToNameAndTypeMap, (r) => this.validateDatabaseSchema(r), { $icon: args?.$icon, $cover: args?.$cover });
1643
1661
  if (results.length === 0)
1644
1662
  return null;
1645
1663
  const [item] = this.applySelectOmit(results, args?.select, args?.omit);
@@ -1650,7 +1668,7 @@ class DatabaseClient {
1650
1668
  const page = await this.client.pages.retrieve({ page_id: args.where.id });
1651
1669
  if (!("properties" in page))
1652
1670
  return null;
1653
- const result = this.parsePage(page);
1671
+ const result = this.parsePage(page, { $icon: args.$icon, $cover: args.$cover });
1654
1672
  const [item] = this.applySelectOmit([result], args.select, args.omit);
1655
1673
  return item ?? null;
1656
1674
  } catch {
@@ -1658,17 +1676,19 @@ class DatabaseClient {
1658
1676
  }
1659
1677
  }
1660
1678
  async create(args) {
1661
- const callBody = this.buildCreateBody(args.data, args.icon);
1679
+ const callBody = this.buildCreateBody(args.data, args.$icon, args.$cover);
1662
1680
  const page = await this.client.pages.create(callBody);
1663
1681
  return this.parsePage(page);
1664
1682
  }
1665
1683
  async createMany(args) {
1666
- return Promise.all(args.data.map((data) => this.create({ data, icon: args.icon })));
1684
+ return Promise.all(args.data.map((data) => this.create({ data, $icon: args.$icon, $cover: args.$cover })));
1667
1685
  }
1668
1686
  async update(args) {
1669
1687
  const callBody = { page_id: args.where.id, properties: {} };
1670
- if (args.icon !== undefined)
1671
- callBody.icon = args.icon;
1688
+ if (args.$icon !== undefined)
1689
+ callBody.icon = { type: "external", external: { url: args.$icon } };
1690
+ if (args.$cover !== undefined)
1691
+ callBody.cover = { type: "external", external: { url: args.$cover } };
1672
1692
  for (const [propertyName, value] of Object.entries(args.data)) {
1673
1693
  const { type, columnName } = this.camelPropertyNameToNameAndTypeMap[propertyName];
1674
1694
  const columnObject = buildPropertyValueForAddPage({ type, value });
@@ -1680,7 +1700,7 @@ class DatabaseClient {
1680
1700
  async updateMany(args) {
1681
1701
  const queryCall = this.buildQueryCall({ where: args.where });
1682
1702
  const results = await this.fetchAllPages(queryCall, {});
1683
- await Promise.all(results.map((r) => this.update({ where: { id: r.id }, data: args.data, icon: args.icon })));
1703
+ await Promise.all(results.map((r) => this.update({ where: { id: r.id }, data: args.data, $icon: args.$icon, $cover: args.$cover })));
1684
1704
  return { count: results.length };
1685
1705
  }
1686
1706
  async upsert(args) {
@@ -1688,10 +1708,10 @@ class DatabaseClient {
1688
1708
  const response = await this.client.dataSources.query({ ...queryCall, page_size: 1 });
1689
1709
  if (response.results.length > 0) {
1690
1710
  const existingId = response.results[0].id;
1691
- await this.update({ where: { id: existingId }, data: args.update, icon: args.icon });
1711
+ await this.update({ where: { id: existingId }, data: args.update, $icon: args.$icon, $cover: args.$cover });
1692
1712
  return { created: false, id: existingId };
1693
1713
  }
1694
- const created = await this.create({ data: args.create, icon: args.icon });
1714
+ const created = await this.create({ data: args.create, $icon: args.$icon, $cover: args.$cover });
1695
1715
  return { created: true, id: created.id };
1696
1716
  }
1697
1717
  async delete(args) {
@@ -1708,13 +1728,15 @@ class DatabaseClient {
1708
1728
  const results = await this.fetchAllPages(queryCall, {});
1709
1729
  return results.length;
1710
1730
  }
1711
- buildCreateBody(data, icon) {
1731
+ buildCreateBody(data, $icon, $cover) {
1712
1732
  const callBody = {
1713
1733
  parent: { data_source_id: this.id, type: "data_source_id" },
1714
1734
  properties: {}
1715
1735
  };
1716
- if (icon)
1717
- callBody.icon = icon;
1736
+ if ($icon)
1737
+ callBody.icon = { type: "external", external: { url: $icon } };
1738
+ if ($cover)
1739
+ callBody.cover = { type: "external", external: { url: $cover } };
1718
1740
  for (const [propertyName, value] of Object.entries(data)) {
1719
1741
  const { type, columnName } = this.camelPropertyNameToNameAndTypeMap[propertyName];
1720
1742
  const columnObject = buildPropertyValueForAddPage({ type, value });
@@ -1723,7 +1745,7 @@ class DatabaseClient {
1723
1745
  }
1724
1746
  return callBody;
1725
1747
  }
1726
- parsePage(page) {
1748
+ parsePage(page, meta) {
1727
1749
  const result = { id: page.id };
1728
1750
  for (const [columnName, value] of Object.entries(page.properties)) {
1729
1751
  const camelName = camelize(columnName);
@@ -1731,6 +1753,22 @@ class DatabaseClient {
1731
1753
  if (colType)
1732
1754
  result[camelName] = getResponseValue(colType, value);
1733
1755
  }
1756
+ if (meta?.$icon) {
1757
+ if (page.icon?.type === "external")
1758
+ result.$icon = page.icon.external?.url ?? null;
1759
+ else if (page.icon?.type === "file")
1760
+ result.$icon = page.icon.file?.url ?? null;
1761
+ else
1762
+ result.$icon = null;
1763
+ }
1764
+ if (meta?.$cover) {
1765
+ if (page.cover?.type === "external")
1766
+ result.$cover = page.cover.external?.url ?? null;
1767
+ else if (page.cover?.type === "file")
1768
+ result.$cover = page.cover.file?.url ?? null;
1769
+ else
1770
+ result.$cover = null;
1771
+ }
1734
1772
  return result;
1735
1773
  }
1736
1774
  buildQueryCall(args) {
@@ -1756,7 +1794,7 @@ class DatabaseClient {
1756
1794
  let fetched = 0;
1757
1795
  do {
1758
1796
  const response = await this.client.dataSources.query({ ...queryCall, start_cursor: cursor, page_size: 100 });
1759
- const page = buildQueryResponse(response, this.camelPropertyNameToNameAndTypeMap, isFirst ? (r) => this.validateDatabaseSchema(r) : () => {});
1797
+ const page = buildQueryResponse(response, this.camelPropertyNameToNameAndTypeMap, isFirst ? (r) => this.validateDatabaseSchema(r) : () => {}, { $icon: args.$icon, $cover: args.$cover });
1760
1798
  isFirst = false;
1761
1799
  for (const item of this.applySelectOmit(page, args.select, args.omit)) {
1762
1800
  fetched++;
@@ -1782,7 +1820,7 @@ class DatabaseClient {
1782
1820
  const skip = args.skip ?? 0;
1783
1821
  do {
1784
1822
  const response = await self.client.dataSources.query({ ...queryCall, start_cursor: cursor, page_size: pageSize });
1785
- const page = buildQueryResponse(response, self.camelPropertyNameToNameAndTypeMap, isFirst ? (r) => self.validateDatabaseSchema(r) : () => {});
1823
+ const page = buildQueryResponse(response, self.camelPropertyNameToNameAndTypeMap, isFirst ? (r) => self.validateDatabaseSchema(r) : () => {}, { $icon: args.$icon, $cover: args.$cover });
1786
1824
  isFirst = false;
1787
1825
  for (const item of self.applySelectOmit(page, args.select, args.omit)) {
1788
1826
  if (skipped < skip) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elumixor/notion-orm",
3
- "version": "2.0.2",
3
+ "version": "2.1.0",
4
4
  "type": "module",
5
5
  "description": "Bring Notion database schemas/types to TypeScript",
6
6
  "main": "dist/index.js",