chore: initial commit
Signed-off-by: Alan Brault <alan.brault@visus.io>
This commit is contained in:
147
.gitignore
vendored
Normal file
147
.gitignore
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
.output
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Sveltekit cache directory
|
||||
.svelte-kit/
|
||||
|
||||
# vitepress build output
|
||||
**/.vitepress/dist
|
||||
|
||||
# vitepress cache directory
|
||||
**/.vitepress/cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# Firebase cache directory
|
||||
.firebase/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v3
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
|
||||
# Vite files
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
.vite/
|
||||
|
||||
# Intellij
|
||||
.idea/
|
||||
|
||||
# VSCode
|
||||
.vscode/
|
||||
12
.prettierrc.json
Normal file
12
.prettierrc.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"arrowParens": "always",
|
||||
"bracketSpacing": true,
|
||||
"endOfLine": "lf",
|
||||
"jsxSingleQuote": false,
|
||||
"printWidth": 120,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5",
|
||||
"useTabs": false
|
||||
}
|
||||
40
README.md
Normal file
40
README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/route.ts`. The page auto-updates as you edit the file.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||
|
||||
## API Routes
|
||||
|
||||
This directory contains example API routes for the headless API app.
|
||||
|
||||
For more details, see [route.js file convention](https://nextjs.org/docs/app/api-reference/file-conventions/route).
|
||||
29
eslint.config.mts
Normal file
29
eslint.config.mts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { FlatCompat } from '@eslint/eslintrc';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: {
|
||||
plugins: [],
|
||||
rules: {},
|
||||
},
|
||||
});
|
||||
|
||||
const eslintConfig = [
|
||||
...compat.extends('next/typescript', 'eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'),
|
||||
{
|
||||
ignores: ['coverage/**', 'node_modules/**', '.next/**', 'out/**', 'build/**', 'next-env.d.ts'],
|
||||
},
|
||||
{
|
||||
files: ['**/*.{js,jsx,ts,tsx}'],
|
||||
rules: {
|
||||
// Add any API-specific rules here
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default eslintConfig;
|
||||
6
next-env.d.ts
vendored
Normal file
6
next-env.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
/// <reference path="./.next/types/routes.d.ts" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
7
next.config.ts
Normal file
7
next.config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { NextConfig } from 'next';
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
5408
package-lock.json
generated
Normal file
5408
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
33
package.json
Normal file
33
package.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "notion-pages-api",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack",
|
||||
"build": "next build --turbopack",
|
||||
"start": "next start",
|
||||
"lint": "eslint",
|
||||
"lint:fix": "eslint --fix .",
|
||||
"format": "prettier --write .",
|
||||
"check-format": "prettier --check ."
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "15.5.4",
|
||||
"zod": "^4.1.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.36.0",
|
||||
"@next/eslint-plugin-next": "^15.5.4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@typescript-eslint/eslint-plugin": "^8.45.0",
|
||||
"@typescript-eslint/parser": "^8.45.0",
|
||||
"eslint": "^9.36.0",
|
||||
"eslint-config-next": "^15.5.4",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"prettier": "^3.6.2",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
0
src/app/v1/pages/[page_id]/route.ts
Normal file
0
src/app/v1/pages/[page_id]/route.ts
Normal file
10
src/lib/schemas/index.ts
Normal file
10
src/lib/schemas/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export * from './notion/block.schema';
|
||||
export * from './notion/colors';
|
||||
export * from './notion/emoji.schema';
|
||||
export * from './notion/file.schema';
|
||||
export * from './notion/fileUpload.schema';
|
||||
export * from './notion/page.schema';
|
||||
export * from './notion/pageProperties.schema';
|
||||
export * from './notion/parent.schema';
|
||||
export * from './notion/richText.schema';
|
||||
export * from './notion/user.schema';
|
||||
129
src/lib/schemas/notion/block.schema.ts
Normal file
129
src/lib/schemas/notion/block.schema.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { z } from 'zod';
|
||||
import { emojiSchema, fileSchema, NOTION_COLORS, parentSchema, richTextSchema, userSchema } from '@/lib/schemas';
|
||||
|
||||
const headingsObject = z.object({
|
||||
rich_text: z.array(richTextSchema),
|
||||
color: z.enum(NOTION_COLORS),
|
||||
is_toggleable: z.boolean(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Schema representing a Notion block object.
|
||||
*/
|
||||
export const blockSchema = z.object({
|
||||
object: z.literal('block'),
|
||||
id: z.uuid(),
|
||||
parent: parentSchema,
|
||||
type: z.enum([
|
||||
'audio',
|
||||
'bookmark',
|
||||
'breadcrumb',
|
||||
'bulleted_list_item',
|
||||
'callout',
|
||||
'child_database',
|
||||
'child_page',
|
||||
'column',
|
||||
'column_list',
|
||||
'divider',
|
||||
'embed',
|
||||
'equation',
|
||||
'file',
|
||||
'heading_1',
|
||||
'heading_2',
|
||||
'heading_3',
|
||||
'image',
|
||||
'link_preview',
|
||||
'numbered_list_item',
|
||||
'paragraph',
|
||||
'pdf',
|
||||
'quote',
|
||||
'synced_block',
|
||||
'table',
|
||||
'table_of_contents',
|
||||
'table_row',
|
||||
'template',
|
||||
'to_do',
|
||||
'toggle',
|
||||
'unsupported',
|
||||
'video',
|
||||
]),
|
||||
created_time: z.iso.datetime(),
|
||||
created_by: userSchema,
|
||||
last_edited_time: z.iso.datetime(),
|
||||
last_edited_by: userSchema,
|
||||
archived: z.boolean(),
|
||||
in_trash: z.boolean(),
|
||||
has_children: z.boolean(),
|
||||
|
||||
// Optional properties for each type
|
||||
audio: fileSchema.optional(),
|
||||
bookmark: z
|
||||
.object({
|
||||
caption: z.array(richTextSchema),
|
||||
url: z.url(),
|
||||
})
|
||||
.optional(),
|
||||
breadcrumb: z.object({}).optional(),
|
||||
bulleted_list_item: z
|
||||
.object({
|
||||
rich_text: z.array(richTextSchema),
|
||||
color: z.enum(NOTION_COLORS),
|
||||
})
|
||||
.optional(),
|
||||
callout: z
|
||||
.object({
|
||||
rich_text: z.array(richTextSchema),
|
||||
icon: z.union([emojiSchema, fileSchema]),
|
||||
color: z.enum(NOTION_COLORS),
|
||||
})
|
||||
.optional(),
|
||||
child_database: z
|
||||
.object({
|
||||
title: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
child_page: z
|
||||
.object({
|
||||
title: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
code: z
|
||||
.object({
|
||||
caption: z.array(richTextSchema),
|
||||
rich_text: z.array(richTextSchema),
|
||||
language: z.enum([]), // TODO: Fill in with actual languages
|
||||
})
|
||||
.optional(),
|
||||
column_list: z.object({}).optional(),
|
||||
column: z
|
||||
.object({
|
||||
width_ratio: z.number().min(0).max(1).optional(),
|
||||
})
|
||||
.optional(),
|
||||
divider: z.object({}).optional(),
|
||||
embed: z
|
||||
.object({
|
||||
url: z.url(),
|
||||
})
|
||||
.optional(),
|
||||
equation: z.object({ expression: z.string() }).optional(),
|
||||
file: z
|
||||
.object({
|
||||
caption: z.array(richTextSchema),
|
||||
type: z.enum(['file', 'file_upload', 'external']),
|
||||
file: fileSchema,
|
||||
external: fileSchema,
|
||||
file_upload: fileSchema,
|
||||
name: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
heading_1: headingsObject.optional(),
|
||||
heading_2: headingsObject.optional(),
|
||||
heading_3: headingsObject.optional(),
|
||||
image: fileSchema.optional(),
|
||||
link_preview: z.object({ url: z.url() }).optional(),
|
||||
|
||||
// TODO: Continue with mention and others
|
||||
});
|
||||
|
||||
export type NotionBlock = z.infer<typeof blockSchema>;
|
||||
22
src/lib/schemas/notion/colors.ts
Normal file
22
src/lib/schemas/notion/colors.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/** Notion color options */
|
||||
export const NOTION_COLORS = [
|
||||
'blue',
|
||||
'blue_background',
|
||||
'brown',
|
||||
'brown_background',
|
||||
'default',
|
||||
'gray',
|
||||
'gray_background',
|
||||
'green',
|
||||
'green_background',
|
||||
'orange',
|
||||
'orange_background',
|
||||
'pink',
|
||||
'pink_background',
|
||||
'purple',
|
||||
'purple_background',
|
||||
'red',
|
||||
'red_background',
|
||||
'yellow',
|
||||
'yellow_background',
|
||||
];
|
||||
8
src/lib/schemas/notion/emoji.schema.ts
Normal file
8
src/lib/schemas/notion/emoji.schema.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const emojiSchema = z.object({
|
||||
type: z.literal('emoji'),
|
||||
emoji: z.string(),
|
||||
});
|
||||
|
||||
export type NotionEmoji = z.infer<typeof emojiSchema>;
|
||||
33
src/lib/schemas/notion/file.schema.ts
Normal file
33
src/lib/schemas/notion/file.schema.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
* Schema representing a Notion file object.
|
||||
*
|
||||
* This schema can represent three types of files:
|
||||
* 1. 'file': A file hosted by Notion, with a URL and an expiry time.
|
||||
* 2. 'file_upload': A file uploaded to Notion, identified by a unique ID.
|
||||
* 3. 'external': A file hosted externally, represented by a URL.
|
||||
*
|
||||
* Each type has its own structure, and only one type will be present in a valid object.
|
||||
*/
|
||||
export const fileSchema = z.object({
|
||||
type: z.enum(['file', 'file_upload', 'external']),
|
||||
file: z
|
||||
.object({
|
||||
url: z.url(),
|
||||
expiry_time: z.iso.datetime(),
|
||||
})
|
||||
.optional(),
|
||||
file_upload: z
|
||||
.object({
|
||||
id: z.uuid(),
|
||||
})
|
||||
.optional(),
|
||||
external: z
|
||||
.object({
|
||||
url: z.url(),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export type NotionFile = z.infer<typeof fileSchema>;
|
||||
22
src/lib/schemas/notion/fileUpload.schema.ts
Normal file
22
src/lib/schemas/notion/fileUpload.schema.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
* Schema representing a Notion file upload object.
|
||||
*
|
||||
* Includes details about the file upload status, URLs, and metadata.
|
||||
*/
|
||||
export const fileUploadSchema = z.object({
|
||||
object: z.literal('file_upload'),
|
||||
id: z.uuid(),
|
||||
created_time: z.iso.datetime(),
|
||||
expiry_time: z.iso.datetime().nullable(),
|
||||
status: z.enum(['pending', 'uploaded', 'expired', 'failed']),
|
||||
filename: z.string(),
|
||||
content_type: z.string().nullable(),
|
||||
content_length: z.number().nullable(),
|
||||
upload_url: z.string(),
|
||||
complete_url: z.string(),
|
||||
file_import_result: z.string(),
|
||||
});
|
||||
|
||||
export type NotionFileUpload = z.infer<typeof fileUploadSchema>;
|
||||
26
src/lib/schemas/notion/page.schema.ts
Normal file
26
src/lib/schemas/notion/page.schema.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { z } from 'zod';
|
||||
import { fileSchema, pagePropertiesSchema, parentSchema, userSchema } from '@/lib/schemas';
|
||||
|
||||
/**
|
||||
* Schema representing a Notion page object.
|
||||
*
|
||||
* Includes metadata about the page, its properties, and its parent.
|
||||
*/
|
||||
export const pageSchema = z.object({
|
||||
object: z.literal('page'),
|
||||
id: z.uuid(),
|
||||
created_time: z.iso.datetime(),
|
||||
created_by: userSchema,
|
||||
last_edited_time: z.iso.datetime(),
|
||||
last_edited_by: userSchema,
|
||||
archived: z.boolean(),
|
||||
in_trash: z.boolean(),
|
||||
icon: z.nullable(fileSchema),
|
||||
cover: z.nullable(fileSchema),
|
||||
properties: z.record(z.string(), pagePropertiesSchema),
|
||||
parent: parentSchema,
|
||||
url: z.url(),
|
||||
public_url: z.url().nullable(),
|
||||
});
|
||||
|
||||
export type NotionPage = z.infer<typeof pageSchema>;
|
||||
92
src/lib/schemas/notion/pageProperties.schema.ts
Normal file
92
src/lib/schemas/notion/pageProperties.schema.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { z } from 'zod';
|
||||
import { NOTION_COLORS, richTextSchema, userSchema } from '@/lib/schemas';
|
||||
|
||||
/**
|
||||
* Schema representing the properties of a Notion page.
|
||||
*
|
||||
* Includes various property types such as text, number, select, multi-select, date, people, files, and more.
|
||||
*/
|
||||
export const pagePropertiesSchema = z.object({
|
||||
id: z.string(),
|
||||
type: z.enum([
|
||||
'checkbox',
|
||||
'created_by',
|
||||
'created_time',
|
||||
'date',
|
||||
'email',
|
||||
'files',
|
||||
'formula',
|
||||
'last_edited_by',
|
||||
'last_edited_time',
|
||||
'multi_select',
|
||||
'number',
|
||||
'people',
|
||||
'phone_number',
|
||||
'relation',
|
||||
'rich_text',
|
||||
'rollup',
|
||||
'select',
|
||||
'status',
|
||||
'title',
|
||||
'url',
|
||||
'unique_id',
|
||||
'verification',
|
||||
]),
|
||||
|
||||
// Optional properties for each type
|
||||
checkbox: z.boolean().optional(),
|
||||
created_by: userSchema.optional(),
|
||||
created_time: z.iso.datetime().optional(),
|
||||
date: z
|
||||
.object({
|
||||
start: z.iso.datetime(),
|
||||
end: z.iso.datetime().optional(),
|
||||
})
|
||||
.optional(),
|
||||
email: z.email().optional(),
|
||||
files: z.array(z.any()).optional(),
|
||||
formula: z.any().optional(),
|
||||
last_edited_by: userSchema.optional(),
|
||||
last_edited_time: z.iso.datetime().optional(),
|
||||
multi_select: z
|
||||
.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
color: z.enum(NOTION_COLORS),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
number: z.number().optional(),
|
||||
people: z.array(userSchema).optional(),
|
||||
phone_number: z.string().optional(),
|
||||
relation: z.array(z.object({ id: z.string() })).optional(),
|
||||
rich_text: richTextSchema.optional(),
|
||||
rollup: z.any().optional(),
|
||||
select: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
color: z.enum(NOTION_COLORS),
|
||||
})
|
||||
.optional(),
|
||||
status: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
color: z.enum(NOTION_COLORS),
|
||||
})
|
||||
.optional(),
|
||||
title: richTextSchema.optional(),
|
||||
url: z.url().optional(),
|
||||
unique_id: z.string().optional(),
|
||||
verification: z
|
||||
.object({
|
||||
state: z.string(),
|
||||
verified_by: userSchema.nullable(),
|
||||
date: z.iso.datetime(),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export type NotionPageProperties = z.infer<typeof pagePropertiesSchema>;
|
||||
17
src/lib/schemas/notion/parent.schema.ts
Normal file
17
src/lib/schemas/notion/parent.schema.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
* Schema representing the parent of a Notion page.
|
||||
*
|
||||
* Includes various types of parents such as database, data source, page, workspace, or block.
|
||||
*/
|
||||
export const parentSchema = z.object({
|
||||
type: z.enum(['database_id', 'data_source_id', 'page_id', 'workspace', 'block_id']),
|
||||
database_id: z.uuid().optional(),
|
||||
data_source_id: z.uuid().optional(),
|
||||
page_id: z.uuid().optional(),
|
||||
workspace: z.boolean().optional(),
|
||||
block_id: z.uuid().optional(),
|
||||
});
|
||||
|
||||
export type NotionParent = z.infer<typeof parentSchema>;
|
||||
68
src/lib/schemas/notion/richText.schema.ts
Normal file
68
src/lib/schemas/notion/richText.schema.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { z } from 'zod';
|
||||
import { NOTION_COLORS, userSchema } from '@/lib/schemas';
|
||||
|
||||
/**
|
||||
* Schema representing a Notion rich text object.
|
||||
*
|
||||
* Includes text content, annotations, mentions, and equations.
|
||||
*/
|
||||
export const richTextSchema = z.array(
|
||||
z.object({
|
||||
type: z.literal('text'),
|
||||
text: z.object({
|
||||
content: z.string(),
|
||||
link: z.url().nullable(),
|
||||
}),
|
||||
mention: z
|
||||
.object({
|
||||
type: z.enum(['database', 'date', 'link_preview', 'page', 'template_mention', 'user']),
|
||||
database: z
|
||||
.object({
|
||||
id: z.uuid(),
|
||||
})
|
||||
.optional(),
|
||||
date: z
|
||||
.object({
|
||||
start: z.iso.datetime(),
|
||||
end: z.iso.datetime().optional(),
|
||||
})
|
||||
.optional(),
|
||||
link_preview: z
|
||||
.object({
|
||||
url: z.url(),
|
||||
})
|
||||
.optional(),
|
||||
page: z
|
||||
.object({
|
||||
id: z.uuid(),
|
||||
})
|
||||
.optional(),
|
||||
template_mention: z
|
||||
.object({
|
||||
type: z.enum(['template_mention_date', 'template_mention_user']),
|
||||
template_mention_date: z.enum(['today', 'now']).optional(),
|
||||
template_mention_user: z.literal('me').optional(),
|
||||
})
|
||||
.optional(),
|
||||
user: z.object(userSchema).optional(),
|
||||
})
|
||||
.optional(),
|
||||
equation: z
|
||||
.object({
|
||||
expression: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
annotations: z.object({
|
||||
bold: z.boolean(),
|
||||
italic: z.boolean(),
|
||||
strikethrough: z.boolean(),
|
||||
underline: z.boolean(),
|
||||
code: z.boolean(),
|
||||
color: z.enum(NOTION_COLORS),
|
||||
}),
|
||||
plain_text: z.string(),
|
||||
href: z.url().optional(),
|
||||
})
|
||||
);
|
||||
|
||||
export type NotionRichText = z.infer<typeof richTextSchema>;
|
||||
16
src/lib/schemas/notion/user.schema.ts
Normal file
16
src/lib/schemas/notion/user.schema.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
* Schema representing a Notion user object.
|
||||
*
|
||||
* Includes both person and bot user types.
|
||||
*/
|
||||
export const userSchema = z.object({
|
||||
object: z.literal('user'),
|
||||
id: z.uuid(),
|
||||
type: z.enum(['person', 'bot']).optional(),
|
||||
name: z.string().optional(),
|
||||
avatar_url: z.url().optional(),
|
||||
});
|
||||
|
||||
export type NotionUser = z.infer<typeof userSchema>;
|
||||
27
tsconfig.json
Normal file
27
tsconfig.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user