@elumixor/notion-orm 0.1.1 → 2.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.
- package/README.md +112 -210
- package/dist/ast/constants.d.ts +12 -13
- package/dist/cli.js +5409 -2038
- package/dist/config/helpers.d.ts +3 -1
- package/dist/db-client/add.d.ts +1 -1
- package/dist/db-client/client.d.ts +169 -0
- package/dist/db-client/query.d.ts +2 -2
- package/dist/db-client/{queryTypes.d.ts → types.d.ts} +25 -8
- package/dist/index.js +168 -94
- package/dist/public-api.d.ts +2 -2
- package/package.json +2 -1
- package/dist/db-client/DatabaseClient.d.ts +0 -47
package/dist/config/helpers.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
export type NotionConfigType = {
|
|
2
2
|
auth: string;
|
|
3
3
|
databases: Record<string, string>;
|
|
4
|
+
/** Output directory for generated files, relative to project root. Defaults to "generated/notion-orm" */
|
|
5
|
+
outputDir?: string;
|
|
4
6
|
};
|
|
5
7
|
export declare function validateConfig(): Promise<void>;
|
|
6
8
|
export declare function findConfigFile(): {
|
|
7
9
|
path: string;
|
|
8
10
|
isTS: true;
|
|
9
11
|
} | undefined;
|
|
10
|
-
export declare function initializeNotionConfigFile(): Promise<
|
|
12
|
+
export declare function initializeNotionConfigFile(): Promise<"created" | "exists">;
|
package/dist/db-client/add.d.ts
CHANGED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import type { CreatePageParameters, UpdatePageParameters } from "@notionhq/client/build/src/api-endpoints";
|
|
2
|
+
import type { ZodTypeAny } from "zod";
|
|
3
|
+
import type { FindManyArgs, PaginateArgs, QueryFilter, SupportedNotionColumnType } from "./types";
|
|
4
|
+
export type camelPropertyNameToNameAndTypeMapType = Record<string, {
|
|
5
|
+
columnName: string;
|
|
6
|
+
type: SupportedNotionColumnType;
|
|
7
|
+
}>;
|
|
8
|
+
export declare class DatabaseClient<DatabaseSchemaType extends Record<string, any>, ColumnNameToColumnType extends Record<keyof DatabaseSchemaType, SupportedNotionColumnType>> {
|
|
9
|
+
private client;
|
|
10
|
+
private id;
|
|
11
|
+
private camelPropertyNameToNameAndTypeMap;
|
|
12
|
+
private schema;
|
|
13
|
+
name: string;
|
|
14
|
+
private loggedSchemaValidationIssues;
|
|
15
|
+
constructor(args: {
|
|
16
|
+
id: string;
|
|
17
|
+
camelPropertyNameToNameAndTypeMap: camelPropertyNameToNameAndTypeMapType;
|
|
18
|
+
auth: string;
|
|
19
|
+
name: string;
|
|
20
|
+
schema: ZodTypeAny;
|
|
21
|
+
});
|
|
22
|
+
/** Find all matching records. When `stream` is set, returns an AsyncIterable fetching in batches of that size. */
|
|
23
|
+
findMany(args: FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType> & {
|
|
24
|
+
stream: number;
|
|
25
|
+
}): AsyncIterable<Partial<DatabaseSchemaType> & {
|
|
26
|
+
id: string;
|
|
27
|
+
}>;
|
|
28
|
+
findMany<S extends Partial<Record<keyof DatabaseSchemaType, true>>>(args: Omit<FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType>, "stream" | "select"> & {
|
|
29
|
+
select: S;
|
|
30
|
+
}): Promise<({
|
|
31
|
+
[K in Extract<keyof S, keyof DatabaseSchemaType>]?: DatabaseSchemaType[K];
|
|
32
|
+
} & {
|
|
33
|
+
id: string;
|
|
34
|
+
})[]>;
|
|
35
|
+
findMany<O extends Partial<Record<keyof DatabaseSchemaType, true>>>(args: Omit<FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType>, "stream" | "omit"> & {
|
|
36
|
+
omit: O;
|
|
37
|
+
}): Promise<({
|
|
38
|
+
[K in Exclude<keyof DatabaseSchemaType, keyof O>]?: DatabaseSchemaType[K];
|
|
39
|
+
} & {
|
|
40
|
+
id: string;
|
|
41
|
+
})[]>;
|
|
42
|
+
findMany(args?: Omit<FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType>, "stream">): Promise<(Partial<DatabaseSchemaType> & {
|
|
43
|
+
id: string;
|
|
44
|
+
})[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Fetch one page of results using Notion's native cursor.
|
|
47
|
+
* Pass the returned `nextCursor` as `after` on the next call to get the next page.
|
|
48
|
+
*/
|
|
49
|
+
paginate<S extends Partial<Record<keyof DatabaseSchemaType, true>>>(args: Omit<PaginateArgs<DatabaseSchemaType, ColumnNameToColumnType>, "select"> & {
|
|
50
|
+
select: S;
|
|
51
|
+
}): Promise<{
|
|
52
|
+
data: ({
|
|
53
|
+
[K in Extract<keyof S, keyof DatabaseSchemaType>]?: DatabaseSchemaType[K];
|
|
54
|
+
} & {
|
|
55
|
+
id: string;
|
|
56
|
+
})[];
|
|
57
|
+
nextCursor: string | null;
|
|
58
|
+
hasMore: boolean;
|
|
59
|
+
}>;
|
|
60
|
+
paginate<O extends Partial<Record<keyof DatabaseSchemaType, true>>>(args: Omit<PaginateArgs<DatabaseSchemaType, ColumnNameToColumnType>, "omit"> & {
|
|
61
|
+
omit: O;
|
|
62
|
+
}): Promise<{
|
|
63
|
+
data: ({
|
|
64
|
+
[K in Exclude<keyof DatabaseSchemaType, keyof O>]?: DatabaseSchemaType[K];
|
|
65
|
+
} & {
|
|
66
|
+
id: string;
|
|
67
|
+
})[];
|
|
68
|
+
nextCursor: string | null;
|
|
69
|
+
hasMore: boolean;
|
|
70
|
+
}>;
|
|
71
|
+
paginate(args?: PaginateArgs<DatabaseSchemaType, ColumnNameToColumnType>): Promise<{
|
|
72
|
+
data: (Partial<DatabaseSchemaType> & {
|
|
73
|
+
id: string;
|
|
74
|
+
})[];
|
|
75
|
+
nextCursor: string | null;
|
|
76
|
+
hasMore: boolean;
|
|
77
|
+
}>;
|
|
78
|
+
/** 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"> & {
|
|
80
|
+
select: S;
|
|
81
|
+
}): Promise<({
|
|
82
|
+
[K in Extract<keyof S, keyof DatabaseSchemaType>]?: DatabaseSchemaType[K];
|
|
83
|
+
} & {
|
|
84
|
+
id: string;
|
|
85
|
+
}) | null>;
|
|
86
|
+
findFirst<O extends Partial<Record<keyof DatabaseSchemaType, true>>>(args: Omit<FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType>, "stream" | "omit"> & {
|
|
87
|
+
omit: O;
|
|
88
|
+
}): Promise<({
|
|
89
|
+
[K in Exclude<keyof DatabaseSchemaType, keyof O>]?: DatabaseSchemaType[K];
|
|
90
|
+
} & {
|
|
91
|
+
id: string;
|
|
92
|
+
}) | null>;
|
|
93
|
+
findFirst(args?: Omit<FindManyArgs<DatabaseSchemaType, ColumnNameToColumnType>, "stream">): Promise<(Partial<DatabaseSchemaType> & {
|
|
94
|
+
id: string;
|
|
95
|
+
}) | null>;
|
|
96
|
+
/** Find a record by its Notion page ID. Returns null if not found. */
|
|
97
|
+
findUnique(args: {
|
|
98
|
+
where: {
|
|
99
|
+
id: string;
|
|
100
|
+
};
|
|
101
|
+
select?: Partial<Record<keyof DatabaseSchemaType, true>>;
|
|
102
|
+
omit?: Partial<Record<keyof DatabaseSchemaType, true>>;
|
|
103
|
+
}): Promise<(Partial<DatabaseSchemaType> & {
|
|
104
|
+
id: string;
|
|
105
|
+
}) | null>;
|
|
106
|
+
/** Create a new record and return it. */
|
|
107
|
+
create(args: {
|
|
108
|
+
data: DatabaseSchemaType;
|
|
109
|
+
icon?: CreatePageParameters["icon"];
|
|
110
|
+
}): Promise<Partial<DatabaseSchemaType> & {
|
|
111
|
+
id: string;
|
|
112
|
+
}>;
|
|
113
|
+
/** Create multiple records and return them. */
|
|
114
|
+
createMany(args: {
|
|
115
|
+
data: DatabaseSchemaType[];
|
|
116
|
+
icon?: CreatePageParameters["icon"];
|
|
117
|
+
}): Promise<(Partial<DatabaseSchemaType> & {
|
|
118
|
+
id: string;
|
|
119
|
+
})[]>;
|
|
120
|
+
/** Update a record by its Notion page ID. */
|
|
121
|
+
update(args: {
|
|
122
|
+
where: {
|
|
123
|
+
id: string;
|
|
124
|
+
};
|
|
125
|
+
data: Partial<DatabaseSchemaType>;
|
|
126
|
+
icon?: UpdatePageParameters["icon"];
|
|
127
|
+
}): Promise<void>;
|
|
128
|
+
/** Update all records matching the filter. Returns the count of updated records. */
|
|
129
|
+
updateMany(args: {
|
|
130
|
+
where?: QueryFilter<DatabaseSchemaType, ColumnNameToColumnType>;
|
|
131
|
+
data: Partial<DatabaseSchemaType>;
|
|
132
|
+
icon?: UpdatePageParameters["icon"];
|
|
133
|
+
}): Promise<{
|
|
134
|
+
count: number;
|
|
135
|
+
}>;
|
|
136
|
+
/** Create or update a record. If a record matches `where`, updates it; otherwise creates it. */
|
|
137
|
+
upsert(args: {
|
|
138
|
+
where: QueryFilter<DatabaseSchemaType, ColumnNameToColumnType>;
|
|
139
|
+
create: DatabaseSchemaType;
|
|
140
|
+
update: Partial<DatabaseSchemaType>;
|
|
141
|
+
icon?: CreatePageParameters["icon"];
|
|
142
|
+
}): Promise<{
|
|
143
|
+
created: boolean;
|
|
144
|
+
id: string;
|
|
145
|
+
}>;
|
|
146
|
+
/** Archive (soft-delete) a record by its Notion page ID. */
|
|
147
|
+
delete(args: {
|
|
148
|
+
where: {
|
|
149
|
+
id: string;
|
|
150
|
+
};
|
|
151
|
+
}): Promise<void>;
|
|
152
|
+
/** Archive all records matching the filter. Returns the count of deleted records. */
|
|
153
|
+
deleteMany(args?: {
|
|
154
|
+
where?: QueryFilter<DatabaseSchemaType, ColumnNameToColumnType>;
|
|
155
|
+
}): Promise<{
|
|
156
|
+
count: number;
|
|
157
|
+
}>;
|
|
158
|
+
/** Count records matching the filter. */
|
|
159
|
+
count(args?: {
|
|
160
|
+
where?: QueryFilter<DatabaseSchemaType, ColumnNameToColumnType>;
|
|
161
|
+
}): Promise<number>;
|
|
162
|
+
private buildCreateBody;
|
|
163
|
+
private parsePage;
|
|
164
|
+
private buildQueryCall;
|
|
165
|
+
private fetchAllPages;
|
|
166
|
+
private streamingIterable;
|
|
167
|
+
private applySelectOmit;
|
|
168
|
+
private validateDatabaseSchema;
|
|
169
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { QueryDataSourceResponse } from "@notionhq/client/build/src/api-endpoints";
|
|
2
|
-
import type { apiFilterType, QueryFilter, SupportedNotionColumnType } from "./
|
|
3
|
-
import type { camelPropertyNameToNameAndTypeMapType } from "./
|
|
2
|
+
import type { apiFilterType, QueryFilter, SupportedNotionColumnType } from "./types";
|
|
3
|
+
import type { camelPropertyNameToNameAndTypeMapType } from "./client";
|
|
4
4
|
/**
|
|
5
5
|
* Transforms Notion API query response into simplified format
|
|
6
6
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Column types' for all query options
|
|
3
3
|
*/
|
|
4
|
-
import type { DataSourceObjectResponse
|
|
4
|
+
import type { DataSourceObjectResponse } from "@notionhq/client/build/src/api-endpoints";
|
|
5
5
|
type NotionPropertyTypeToConfigMap = DataSourceObjectResponse["properties"];
|
|
6
6
|
export type DatabasePropertyType = NotionPropertyTypeToConfigMap[keyof NotionPropertyTypeToConfigMap]["type"];
|
|
7
7
|
export declare const SUPPORTED_PROPERTY_TYPES: {
|
|
@@ -111,16 +111,33 @@ export type CompoundFilters<Y extends Record<string, any>, T extends Record<keyo
|
|
|
111
111
|
or: Array<SingleFilter<Y, T> | CompoundFilters<Y, T>>;
|
|
112
112
|
};
|
|
113
113
|
export type QueryFilter<Y extends Record<string, any>, T extends Record<keyof Y, SupportedNotionColumnType>> = SingleFilter<Y, T> | CompoundFilters<Y, T>;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
/** Prisma-style orderBy: `{ fieldName: 'asc' | 'desc' }` or an array of those */
|
|
115
|
+
export type OrderByInput<Y> = {
|
|
116
|
+
[K in keyof Y]?: "asc" | "desc";
|
|
117
|
+
} | {
|
|
118
|
+
[K in keyof Y]?: "asc" | "desc";
|
|
119
|
+
}[];
|
|
120
|
+
export type FindManyArgs<Y extends Record<string, any>, T extends Record<keyof Y, SupportedNotionColumnType>> = {
|
|
121
|
+
where?: QueryFilter<Y, T>;
|
|
122
|
+
orderBy?: OrderByInput<Y>;
|
|
123
|
+
select?: Partial<Record<keyof Y, true>>;
|
|
124
|
+
omit?: Partial<Record<keyof Y, true>>;
|
|
125
|
+
take?: number;
|
|
126
|
+
skip?: number;
|
|
127
|
+
/** When set, findMany returns an AsyncIterable, fetching results in batches of this size */
|
|
128
|
+
stream?: number;
|
|
129
|
+
};
|
|
130
|
+
export type PaginateArgs<Y extends Record<string, any>, T extends Record<keyof Y, SupportedNotionColumnType>> = {
|
|
131
|
+
where?: QueryFilter<Y, T>;
|
|
132
|
+
orderBy?: OrderByInput<Y>;
|
|
117
133
|
select?: Partial<Record<keyof Y, true>>;
|
|
118
134
|
omit?: Partial<Record<keyof Y, true>>;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
};
|
|
135
|
+
take?: number;
|
|
136
|
+
/** Opaque cursor token returned by a previous paginate() call */
|
|
137
|
+
after?: string;
|
|
123
138
|
};
|
|
139
|
+
/** @deprecated Use FindManyArgs */
|
|
140
|
+
export type Query<Y extends Record<string, any>, T extends Record<keyof Y, SupportedNotionColumnType>> = FindManyArgs<Y, T>;
|
|
124
141
|
export type apiFilterQuery = {
|
|
125
142
|
filter?: apiSingleFilter | apiAndFilter | apiOrFilter;
|
|
126
143
|
};
|
package/dist/index.js
CHANGED
|
@@ -3,15 +3,29 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
function __accessProp(key) {
|
|
7
|
+
return this[key];
|
|
8
|
+
}
|
|
9
|
+
var __toESMCache_node;
|
|
10
|
+
var __toESMCache_esm;
|
|
6
11
|
var __toESM = (mod, isNodeMode, target) => {
|
|
12
|
+
var canCache = mod != null && typeof mod === "object";
|
|
13
|
+
if (canCache) {
|
|
14
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
15
|
+
var cached = cache.get(mod);
|
|
16
|
+
if (cached)
|
|
17
|
+
return cached;
|
|
18
|
+
}
|
|
7
19
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
8
20
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
9
21
|
for (let key of __getOwnPropNames(mod))
|
|
10
22
|
if (!__hasOwnProp.call(to, key))
|
|
11
23
|
__defProp(to, key, {
|
|
12
|
-
get: (
|
|
24
|
+
get: __accessProp.bind(mod, key),
|
|
13
25
|
enumerable: true
|
|
14
26
|
});
|
|
27
|
+
if (canCache)
|
|
28
|
+
cache.set(mod, to);
|
|
15
29
|
return to;
|
|
16
30
|
};
|
|
17
31
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
@@ -1327,7 +1341,7 @@ var require_src = __commonJS((exports) => {
|
|
|
1327
1341
|
} });
|
|
1328
1342
|
});
|
|
1329
1343
|
|
|
1330
|
-
// src/db-client/
|
|
1344
|
+
// src/db-client/client.ts
|
|
1331
1345
|
var import_client = __toESM(require_src(), 1);
|
|
1332
1346
|
|
|
1333
1347
|
// src/ast/constants.ts
|
|
@@ -1337,7 +1351,6 @@ var __filename2 = fileURLToPath(import.meta.url);
|
|
|
1337
1351
|
var __dirname2 = path.dirname(__filename2);
|
|
1338
1352
|
var PACKAGE_ROOT = path.resolve(__dirname2, "../../");
|
|
1339
1353
|
var IS_DEV = process.cwd() === PACKAGE_ROOT;
|
|
1340
|
-
var DATABASES_DIR = path.join(process.cwd(), "generated");
|
|
1341
1354
|
var AST_RUNTIME_CONSTANTS = {
|
|
1342
1355
|
NOTION_API_VERSION: "2025-09-03",
|
|
1343
1356
|
PACKAGE_LOG_PREFIX: "[@elumixor/notion-orm]",
|
|
@@ -1346,6 +1359,16 @@ var AST_RUNTIME_CONSTANTS = {
|
|
|
1346
1359
|
SCHEMA_DRIFT_HELP_MESSAGE: "Run `notion generate` to refresh all database schemas."
|
|
1347
1360
|
};
|
|
1348
1361
|
|
|
1362
|
+
// src/helpers.ts
|
|
1363
|
+
function camelize(str) {
|
|
1364
|
+
const cleaned = str.replace(/[^a-zA-Z0-9\s]/g, "");
|
|
1365
|
+
return cleaned.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function(match, index) {
|
|
1366
|
+
if (+match === 0)
|
|
1367
|
+
return "";
|
|
1368
|
+
return index === 0 ? match.toLowerCase() : match.toUpperCase();
|
|
1369
|
+
});
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1349
1372
|
// src/db-client/add.ts
|
|
1350
1373
|
function buildPropertyValueForAddPage(args) {
|
|
1351
1374
|
const { type, value } = args;
|
|
@@ -1441,16 +1464,6 @@ var emailCall = (args) => {
|
|
|
1441
1464
|
return { email: value };
|
|
1442
1465
|
};
|
|
1443
1466
|
|
|
1444
|
-
// src/helpers.ts
|
|
1445
|
-
function camelize(str) {
|
|
1446
|
-
const cleaned = str.replace(/[^a-zA-Z0-9\s]/g, "");
|
|
1447
|
-
return cleaned.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function(match, index) {
|
|
1448
|
-
if (+match === 0)
|
|
1449
|
-
return "";
|
|
1450
|
-
return index === 0 ? match.toLowerCase() : match.toUpperCase();
|
|
1451
|
-
});
|
|
1452
|
-
}
|
|
1453
|
-
|
|
1454
1467
|
// src/db-client/query.ts
|
|
1455
1468
|
function buildQueryResponse(res, camelPropertyNameToNameAndTypeMap, validateSchema) {
|
|
1456
1469
|
const results = res.results.map((result, index) => {
|
|
@@ -1582,7 +1595,7 @@ function recursivelyBuildFilter(queryFilter, camelPropertyNameToNameAndTypeMap)
|
|
|
1582
1595
|
}
|
|
1583
1596
|
}
|
|
1584
1597
|
|
|
1585
|
-
// src/db-client/
|
|
1598
|
+
// src/db-client/client.ts
|
|
1586
1599
|
class DatabaseClient {
|
|
1587
1600
|
client;
|
|
1588
1601
|
id;
|
|
@@ -1603,103 +1616,181 @@ class DatabaseClient {
|
|
|
1603
1616
|
this.name = args.name;
|
|
1604
1617
|
this.loggedSchemaValidationIssues = new Set;
|
|
1605
1618
|
}
|
|
1606
|
-
|
|
1607
|
-
const
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
const columnObject = buildPropertyValueForAddPage({
|
|
1619
|
-
type,
|
|
1620
|
-
value
|
|
1621
|
-
});
|
|
1622
|
-
if (callBody.properties && columnObject) {
|
|
1623
|
-
callBody.properties[columnName] = columnObject;
|
|
1624
|
-
}
|
|
1619
|
+
findMany(args = {}) {
|
|
1620
|
+
const queryCall = this.buildQueryCall(args);
|
|
1621
|
+
if (args.stream)
|
|
1622
|
+
return this.streamingIterable(queryCall, args);
|
|
1623
|
+
return this.fetchAllPages(queryCall, args);
|
|
1624
|
+
}
|
|
1625
|
+
async paginate(args) {
|
|
1626
|
+
const queryCall = this.buildQueryCall(args ?? {});
|
|
1627
|
+
const response = await this.client.dataSources.query({
|
|
1628
|
+
...queryCall,
|
|
1629
|
+
page_size: args?.take ?? 100,
|
|
1630
|
+
start_cursor: args?.after
|
|
1625
1631
|
});
|
|
1626
|
-
|
|
1632
|
+
const results = buildQueryResponse(response, this.camelPropertyNameToNameAndTypeMap, (r) => this.validateDatabaseSchema(r));
|
|
1633
|
+
return {
|
|
1634
|
+
data: this.applySelectOmit(results, args?.select, args?.omit),
|
|
1635
|
+
nextCursor: response.has_more ? response.next_cursor ?? null : null,
|
|
1636
|
+
hasMore: response.has_more
|
|
1637
|
+
};
|
|
1627
1638
|
}
|
|
1628
|
-
async
|
|
1629
|
-
const
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1639
|
+
async findFirst(args) {
|
|
1640
|
+
const queryCall = this.buildQueryCall(args ?? {});
|
|
1641
|
+
const response = await this.client.dataSources.query({ ...queryCall, page_size: 1 });
|
|
1642
|
+
const results = buildQueryResponse(response, this.camelPropertyNameToNameAndTypeMap, (r) => this.validateDatabaseSchema(r));
|
|
1643
|
+
if (results.length === 0)
|
|
1644
|
+
return null;
|
|
1645
|
+
const [item] = this.applySelectOmit(results, args?.select, args?.omit);
|
|
1646
|
+
return item ?? null;
|
|
1647
|
+
}
|
|
1648
|
+
async findUnique(args) {
|
|
1649
|
+
try {
|
|
1650
|
+
const page = await this.client.pages.retrieve({ page_id: args.where.id });
|
|
1651
|
+
if (!("properties" in page))
|
|
1652
|
+
return null;
|
|
1653
|
+
const result = this.parsePage(page);
|
|
1654
|
+
const [item] = this.applySelectOmit([result], args.select, args.omit);
|
|
1655
|
+
return item ?? null;
|
|
1656
|
+
} catch {
|
|
1657
|
+
return null;
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
async create(args) {
|
|
1661
|
+
const callBody = this.buildCreateBody(args.data, args.icon);
|
|
1662
|
+
const page = await this.client.pages.create(callBody);
|
|
1663
|
+
return this.parsePage(page);
|
|
1664
|
+
}
|
|
1665
|
+
async createMany(args) {
|
|
1666
|
+
return Promise.all(args.data.map((data) => this.create({ data, icon: args.icon })));
|
|
1667
|
+
}
|
|
1668
|
+
async update(args) {
|
|
1669
|
+
const callBody = { page_id: args.where.id, properties: {} };
|
|
1670
|
+
if (args.icon !== undefined)
|
|
1671
|
+
callBody.icon = args.icon;
|
|
1672
|
+
for (const [propertyName, value] of Object.entries(args.data)) {
|
|
1633
1673
|
const { type, columnName } = this.camelPropertyNameToNameAndTypeMap[propertyName];
|
|
1634
1674
|
const columnObject = buildPropertyValueForAddPage({ type, value });
|
|
1635
|
-
if (callBody.properties && columnObject)
|
|
1675
|
+
if (callBody.properties && columnObject)
|
|
1636
1676
|
callBody.properties[columnName] = columnObject;
|
|
1637
|
-
|
|
1638
|
-
});
|
|
1677
|
+
}
|
|
1639
1678
|
await this.client.pages.update(callBody);
|
|
1640
1679
|
}
|
|
1641
|
-
async
|
|
1642
|
-
|
|
1680
|
+
async updateMany(args) {
|
|
1681
|
+
const queryCall = this.buildQueryCall({ where: args.where });
|
|
1682
|
+
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 })));
|
|
1684
|
+
return { count: results.length };
|
|
1643
1685
|
}
|
|
1644
1686
|
async upsert(args) {
|
|
1645
|
-
const queryCall = this.buildQueryCall({
|
|
1687
|
+
const queryCall = this.buildQueryCall({ where: args.where });
|
|
1646
1688
|
const response = await this.client.dataSources.query({ ...queryCall, page_size: 1 });
|
|
1647
1689
|
if (response.results.length > 0) {
|
|
1648
1690
|
const existingId = response.results[0].id;
|
|
1649
|
-
await this.update(existingId, args.
|
|
1691
|
+
await this.update({ where: { id: existingId }, data: args.update, icon: args.icon });
|
|
1650
1692
|
return { created: false, id: existingId };
|
|
1651
1693
|
}
|
|
1652
|
-
const created = await this.
|
|
1694
|
+
const created = await this.create({ data: args.create, icon: args.icon });
|
|
1653
1695
|
return { created: true, id: created.id };
|
|
1654
1696
|
}
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1697
|
+
async delete(args) {
|
|
1698
|
+
await this.client.pages.update({ page_id: args.where.id, archived: true });
|
|
1699
|
+
}
|
|
1700
|
+
async deleteMany(args) {
|
|
1701
|
+
const queryCall = this.buildQueryCall({ where: args?.where });
|
|
1702
|
+
const results = await this.fetchAllPages(queryCall, {});
|
|
1703
|
+
await Promise.all(results.map((r) => this.delete({ where: { id: r.id } })));
|
|
1704
|
+
return { count: results.length };
|
|
1705
|
+
}
|
|
1706
|
+
async count(args) {
|
|
1707
|
+
const queryCall = this.buildQueryCall({ where: args?.where });
|
|
1708
|
+
const results = await this.fetchAllPages(queryCall, {});
|
|
1709
|
+
return results.length;
|
|
1710
|
+
}
|
|
1711
|
+
buildCreateBody(data, icon) {
|
|
1712
|
+
const callBody = {
|
|
1713
|
+
parent: { data_source_id: this.id, type: "data_source_id" },
|
|
1714
|
+
properties: {}
|
|
1715
|
+
};
|
|
1716
|
+
if (icon)
|
|
1717
|
+
callBody.icon = icon;
|
|
1718
|
+
for (const [propertyName, value] of Object.entries(data)) {
|
|
1719
|
+
const { type, columnName } = this.camelPropertyNameToNameAndTypeMap[propertyName];
|
|
1720
|
+
const columnObject = buildPropertyValueForAddPage({ type, value });
|
|
1721
|
+
if (callBody.properties && columnObject)
|
|
1722
|
+
callBody.properties[columnName] = columnObject;
|
|
1723
|
+
}
|
|
1724
|
+
return callBody;
|
|
1725
|
+
}
|
|
1726
|
+
parsePage(page) {
|
|
1727
|
+
const result = { id: page.id };
|
|
1728
|
+
for (const [columnName, value] of Object.entries(page.properties)) {
|
|
1729
|
+
const camelName = camelize(columnName);
|
|
1730
|
+
const colType = this.camelPropertyNameToNameAndTypeMap[camelName]?.type;
|
|
1731
|
+
if (colType)
|
|
1732
|
+
result[camelName] = getResponseValue(colType, value);
|
|
1733
|
+
}
|
|
1734
|
+
return result;
|
|
1660
1735
|
}
|
|
1661
|
-
buildQueryCall(
|
|
1736
|
+
buildQueryCall(args) {
|
|
1662
1737
|
const queryCall = { data_source_id: this.id };
|
|
1663
|
-
if (
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
});
|
|
1738
|
+
if (args.orderBy) {
|
|
1739
|
+
const orderByArr = Array.isArray(args.orderBy) ? args.orderBy : [args.orderBy];
|
|
1740
|
+
queryCall["sorts"] = orderByArr.flatMap((obj) => Object.entries(obj).map(([prop, dir]) => ({
|
|
1741
|
+
property: this.camelPropertyNameToNameAndTypeMap[prop]?.columnName ?? prop,
|
|
1742
|
+
direction: dir === "asc" ? "ascending" : "descending"
|
|
1743
|
+
})));
|
|
1669
1744
|
}
|
|
1670
|
-
if (
|
|
1671
|
-
queryCall["filter"] = recursivelyBuildFilter(
|
|
1745
|
+
if (args.where) {
|
|
1746
|
+
queryCall["filter"] = recursivelyBuildFilter(args.where, this.camelPropertyNameToNameAndTypeMap);
|
|
1672
1747
|
}
|
|
1673
1748
|
return queryCall;
|
|
1674
1749
|
}
|
|
1675
|
-
async fetchAllPages(queryCall,
|
|
1750
|
+
async fetchAllPages(queryCall, args) {
|
|
1676
1751
|
const allResults = [];
|
|
1677
1752
|
let cursor;
|
|
1678
1753
|
let isFirst = true;
|
|
1754
|
+
const take = args.take;
|
|
1755
|
+
const skip = args.skip ?? 0;
|
|
1756
|
+
let fetched = 0;
|
|
1679
1757
|
do {
|
|
1680
1758
|
const response = await this.client.dataSources.query({ ...queryCall, start_cursor: cursor, page_size: 100 });
|
|
1681
1759
|
const page = buildQueryResponse(response, this.camelPropertyNameToNameAndTypeMap, isFirst ? (r) => this.validateDatabaseSchema(r) : () => {});
|
|
1682
1760
|
isFirst = false;
|
|
1683
|
-
|
|
1761
|
+
for (const item of this.applySelectOmit(page, args.select, args.omit)) {
|
|
1762
|
+
fetched++;
|
|
1763
|
+
if (fetched <= skip)
|
|
1764
|
+
continue;
|
|
1765
|
+
allResults.push(item);
|
|
1766
|
+
if (take && allResults.length >= take)
|
|
1767
|
+
return allResults;
|
|
1768
|
+
}
|
|
1684
1769
|
cursor = response.has_more ? response.next_cursor ?? undefined : undefined;
|
|
1685
|
-
} while (cursor
|
|
1686
|
-
return
|
|
1770
|
+
} while (cursor);
|
|
1771
|
+
return allResults;
|
|
1687
1772
|
}
|
|
1688
|
-
|
|
1773
|
+
streamingIterable(queryCall, args) {
|
|
1689
1774
|
const self = this;
|
|
1690
|
-
const pageSize =
|
|
1775
|
+
const pageSize = args.stream;
|
|
1691
1776
|
return {
|
|
1692
1777
|
[Symbol.asyncIterator]: async function* () {
|
|
1693
1778
|
let cursor;
|
|
1694
1779
|
let isFirst = true;
|
|
1695
1780
|
let yielded = 0;
|
|
1781
|
+
let skipped = 0;
|
|
1782
|
+
const skip = args.skip ?? 0;
|
|
1696
1783
|
do {
|
|
1697
1784
|
const response = await self.client.dataSources.query({ ...queryCall, start_cursor: cursor, page_size: pageSize });
|
|
1698
1785
|
const page = buildQueryResponse(response, self.camelPropertyNameToNameAndTypeMap, isFirst ? (r) => self.validateDatabaseSchema(r) : () => {});
|
|
1699
1786
|
isFirst = false;
|
|
1700
|
-
for (const item of self.applySelectOmit(page,
|
|
1787
|
+
for (const item of self.applySelectOmit(page, args.select, args.omit)) {
|
|
1788
|
+
if (skipped < skip) {
|
|
1789
|
+
skipped++;
|
|
1790
|
+
continue;
|
|
1791
|
+
}
|
|
1701
1792
|
yield item;
|
|
1702
|
-
if (
|
|
1793
|
+
if (args.take && ++yielded >= args.take)
|
|
1703
1794
|
return;
|
|
1704
1795
|
}
|
|
1705
1796
|
cursor = response.has_more ? response.next_cursor ?? undefined : undefined;
|
|
@@ -1725,22 +1816,17 @@ class DatabaseClient {
|
|
|
1725
1816
|
});
|
|
1726
1817
|
}
|
|
1727
1818
|
validateDatabaseSchema(result) {
|
|
1728
|
-
if (!this.schema)
|
|
1819
|
+
if (!this.schema)
|
|
1729
1820
|
return;
|
|
1730
|
-
}
|
|
1731
1821
|
const schemaLabel = this.name ?? this.id;
|
|
1732
1822
|
const remoteColumnNames = new Set(Object.keys(result));
|
|
1733
1823
|
const missingProperties = [];
|
|
1734
1824
|
for (const propName in this.camelPropertyNameToNameAndTypeMap) {
|
|
1735
|
-
if (!remoteColumnNames.has(propName))
|
|
1825
|
+
if (!remoteColumnNames.has(propName))
|
|
1736
1826
|
missingProperties.push(propName);
|
|
1737
|
-
}
|
|
1738
1827
|
}
|
|
1739
1828
|
if (missingProperties.length > 0) {
|
|
1740
|
-
const issueSignature2 = JSON.stringify({
|
|
1741
|
-
type: "missing_properties",
|
|
1742
|
-
properties: missingProperties
|
|
1743
|
-
});
|
|
1829
|
+
const issueSignature2 = JSON.stringify({ type: "missing_properties", properties: missingProperties });
|
|
1744
1830
|
if (!this.loggedSchemaValidationIssues.has(issueSignature2)) {
|
|
1745
1831
|
this.loggedSchemaValidationIssues.add(issueSignature2);
|
|
1746
1832
|
console.error(`⚠️ ${AST_RUNTIME_CONSTANTS.PACKAGE_LOG_PREFIX} ${AST_RUNTIME_CONSTANTS.SCHEMA_DRIFT_PREFIX} for the following Notion database ${schemaLabel}
|
|
@@ -1756,10 +1842,7 @@ Missing properties: ${missingProperties.map((prop) => `\`${prop}\``).join(", ")}
|
|
|
1756
1842
|
if (remoteColName === "id")
|
|
1757
1843
|
continue;
|
|
1758
1844
|
if (!this.camelPropertyNameToNameAndTypeMap[remoteColName]) {
|
|
1759
|
-
const issueSignature2 = JSON.stringify({
|
|
1760
|
-
type: "unexpected_property",
|
|
1761
|
-
property: remoteColName
|
|
1762
|
-
});
|
|
1845
|
+
const issueSignature2 = JSON.stringify({ type: "unexpected_property", property: remoteColName });
|
|
1763
1846
|
if (!this.loggedSchemaValidationIssues.has(issueSignature2)) {
|
|
1764
1847
|
this.loggedSchemaValidationIssues.add(issueSignature2);
|
|
1765
1848
|
console.error(`⚠️ ${AST_RUNTIME_CONSTANTS.PACKAGE_LOG_PREFIX} ${AST_RUNTIME_CONSTANTS.SCHEMA_DRIFT_PREFIX} for the following Notion database ${schemaLabel}
|
|
@@ -1773,17 +1856,11 @@ Unexpected property found in remote data: \`${remoteColName}\`
|
|
|
1773
1856
|
}
|
|
1774
1857
|
}
|
|
1775
1858
|
const parseResult = this.schema.safeParse(result);
|
|
1776
|
-
if (parseResult.success)
|
|
1859
|
+
if (parseResult.success)
|
|
1777
1860
|
return;
|
|
1778
|
-
}
|
|
1779
|
-
|
|
1780
|
-
code: issue.code,
|
|
1781
|
-
path: issue.path,
|
|
1782
|
-
message: issue.message
|
|
1783
|
-
})));
|
|
1784
|
-
if (this.loggedSchemaValidationIssues.has(issueSignature)) {
|
|
1861
|
+
const issueSignature = JSON.stringify(parseResult.error.issues.map((issue) => ({ code: issue.code, path: issue.path, message: issue.message })));
|
|
1862
|
+
if (this.loggedSchemaValidationIssues.has(issueSignature))
|
|
1785
1863
|
return;
|
|
1786
|
-
}
|
|
1787
1864
|
this.loggedSchemaValidationIssues.add(issueSignature);
|
|
1788
1865
|
console.error(`⚠️ ${AST_RUNTIME_CONSTANTS.PACKAGE_LOG_PREFIX} ${AST_RUNTIME_CONSTANTS.SCHEMA_DRIFT_PREFIX} for the following Notion database ${schemaLabel}
|
|
1789
1866
|
|
|
@@ -1792,10 +1869,7 @@ Validation issues: ${parseResult.error.issues.map((issue) => `\`${issue.path.joi
|
|
|
1792
1869
|
|
|
1793
1870
|
✅ ${AST_RUNTIME_CONSTANTS.SCHEMA_DRIFT_HELP_MESSAGE}
|
|
1794
1871
|
`);
|
|
1795
|
-
console.log("Validation details:", {
|
|
1796
|
-
issues: parseResult.error.issues,
|
|
1797
|
-
result
|
|
1798
|
-
});
|
|
1872
|
+
console.log("Validation details:", { issues: parseResult.error.issues, result });
|
|
1799
1873
|
}
|
|
1800
1874
|
}
|
|
1801
1875
|
export {
|
package/dist/public-api.d.ts
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* Public API entry point for @elumixor/notion-orm.
|
|
3
3
|
* External users' generated files import DatabaseClient and types from here.
|
|
4
4
|
*/
|
|
5
|
-
export { DatabaseClient } from "./db-client/
|
|
6
|
-
export type { Query, QueryResult, QueryResultType, SupportedNotionColumnType } from "./db-client/
|
|
5
|
+
export { DatabaseClient } from "./db-client/client";
|
|
6
|
+
export type { FindManyArgs, PaginateArgs, Query, QueryFilter, QueryResult, QueryResultType, OrderByInput, SupportedNotionColumnType, } from "./db-client/types";
|
|
7
7
|
export type { NotionConfigType } from "./config/helpers";
|