tfrere HF Staff commited on
Commit
cad8784
·
1 Parent(s): ab55123
Files changed (37) hide show
  1. app/scripts/notion-importer/.cursorignore +1 -0
  2. app/scripts/notion-importer/.notion-to-md/media/27877f1c-9c9d-804d-9c82-f7b3905578ff_media.json +198 -0
  3. app/scripts/notion-importer/README.md +291 -0
  4. app/scripts/notion-importer/custom-code-renderer.mjs +33 -0
  5. app/scripts/notion-importer/debug-properties.mjs +87 -0
  6. app/scripts/notion-importer/env.example +2 -0
  7. app/scripts/notion-importer/index.mjs +324 -0
  8. app/scripts/notion-importer/input/pages.json +9 -0
  9. app/scripts/notion-importer/mdx-converter.mjs +551 -0
  10. app/scripts/notion-importer/notion-converter.mjs +259 -0
  11. app/scripts/notion-importer/notion-metadata-extractor.mjs +303 -0
  12. app/scripts/notion-importer/output/.temp-pages.json +9 -0
  13. app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8013-b668-f14bd1ac0ec0.png +3 -0
  14. app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8014-834f-d700b623256b.png +3 -0
  15. app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-801d-841a-e35011491566.png +3 -0
  16. app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8031-ac8d-c5678af1bdd5.png +3 -0
  17. app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8048-9b7e-db4fa7485915.png +3 -0
  18. app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-804d-bd0a-e0b1c15e504f.png +3 -0
  19. app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8075-ae2e-dc24fe9296ca.png +3 -0
  20. app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8078-b6da-c7a4c67c8f35.png +3 -0
  21. app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-808d-9c6d-fae817ac8868.png +3 -0
  22. app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-808f-b712-c7c608da3fc6.png +3 -0
  23. app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80a9-b4d0-f2129716632d.png +3 -0
  24. app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80aa-b968-c54c9fe7e5d7.png +3 -0
  25. app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80b6-be07-e8646502f82a.png +3 -0
  26. app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80b9-8cfb-f0a6aaaa8760.png +3 -0
  27. app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80e7-a500-fb79cebde7e3.png +3 -0
  28. app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80e9-b729-dbd328930bed.png +3 -0
  29. app/scripts/notion-importer/output/smol-training-guide.md +0 -0
  30. app/scripts/notion-importer/output/smol-training-guide.mdx +0 -0
  31. app/scripts/notion-importer/output/smol.md +0 -0
  32. app/scripts/notion-importer/output/smol.mdx +0 -0
  33. app/scripts/notion-importer/package-lock.json +0 -0
  34. app/scripts/notion-importer/package.json +0 -0
  35. app/scripts/notion-importer/post-processor.mjs +369 -0
  36. app/scripts/notion-importer/test-access.mjs +39 -0
  37. app/scripts/notion-importer/yarn.lock +1118 -0
app/scripts/notion-importer/.cursorignore ADDED
@@ -0,0 +1 @@
 
 
1
+ .env
app/scripts/notion-importer/.notion-to-md/media/27877f1c-9c9d-804d-9c82-f7b3905578ff_media.json ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "pageId": "27877f1c-9c9d-804d-9c82-f7b3905578ff",
3
+ "lastUpdated": "2025-10-08T13:00:26.065Z",
4
+ "mediaEntries": {
5
+ "27877f1c-9c9d-8078-b6da-c7a4c67c8f35": {
6
+ "mediaInfo": {
7
+ "type": "DOWNLOAD",
8
+ "originalUrl": "https://prod-files-secure.s3.us-west-2.amazonaws.com/6fe77f1c-9c9d-81b1-b174-00031f2bc702/69940e63-ba02-4d6b-bc79-81dcab16590b/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466RQAHHWD2%2F20251008%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20251008T130015Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjECQaCXVzLXdlc3QtMiJIMEYCIQCja3KUJzl59DJB4he%2BZc4EIAdSKZH6PHGkvSc5iTCA5gIhAK1O3OugR1Ap%2Fhuos6U7RbD2KxvfMHmfQXSU6K7IuEKgKogECL3%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMNjM3NDIzMTgzODA1Igw0pXUjiFqmEZtM2z8q3AMIdeJHjnKFMXfrzdsbRQMk5j3lsQc7tLMnmEPmHH%2FmX7sZrT4UjUkaKoeSyH%2Bg7wFpYmgvN77x8nGQ0nEQtZAPw2%2F3EH4nPTYF53xRmvyn7%2FMwloMmw7eBz7SIZ3Xk0aPYpbtVRoPUesefx7Jvddzmxu0Q%2FQpw0%2FrRCkLPJAnMdsR8fY3n8s8TBv7cVHHWkjpqgeaNKOD7O7Ej%2FNkVahgG%2BVi%2B8DlcuBG8FNk2EzDNroum71dNW6RzTO3ju65V5xHwLgmRkZRTKS%2FHeolCb01h51d%2BhJY03OORmJCMcOEYzPWg48LVGlFl%2FOA6OAhR%2FQ0eWs%2B1ZzKAz7HOSK6gHOhmcCj%2Bbve0%2FVaH2P708k33m5SQqjZQmfmO0JUCNn2WRPK9uYfSy64QFqpzeEXgAGiaNMQCrWjrLpO1TNcK457IQ4ofuJ9bt3be947z17Fa2pKdcescLFE7GdVtV5L4Nps5%2FoHCl0X1BxCPjpxN8zLRIQZtHFRloU8K6ebOrPFIb4bkjxfC9VidpyS0vnZ1DhFt8ssI1sOwrzRn5Kf%2FRltkKxvJPAp829yFUsrA35VhLZLllGpsdLzGVZS1GWkNqf0UxA5BZhdb9t4qPmp1WbBIufTnxcFuHKdUMxsdfTCxpZnHBjqkASugrWSTfsJxQU%2BvD6fKuut0yaNhG1Zscr6q%2FLX6mZAcKzCrMYWhsql27FMH5lEFZTsvBfNoX2h1ie3MPQw%2B67C4yNLYGIvBL%2BFPBfaw6qimvk48x5JDZBNsxN4fEMufvUM1%2FbemWiZZJSl8V5Q5h0NMxBmZFJkachxllAPWTTVPbSArY%2FYyX3vpKDGMi1fYZYT4IjNycyQDb6nSEKlqbnOjUosj&X-Amz-Signature=0e7a6d2cf89a76128b1e4ecd8e12053deb0a5e802a15e3e8c3d7979e2131fa09&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
9
+ "localPath": "/Users/thibaudfrere/Documents/work-projects/huggingface/research-article-template/app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8078-b6da-c7a4c67c8f35.png",
10
+ "sourceType": "block",
11
+ "transformedPath": "/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8078-b6da-c7a4c67c8f35.png"
12
+ },
13
+ "lastEdited": "2025-09-24T09:22:00.000Z",
14
+ "createdAt": "2025-09-24T09:36:35.892Z",
15
+ "updatedAt": "2025-10-08T13:00:25.689Z"
16
+ },
17
+ "27877f1c-9c9d-8014-834f-d700b623256b": {
18
+ "mediaInfo": {
19
+ "type": "DOWNLOAD",
20
+ "originalUrl": "https://prod-files-secure.s3.us-west-2.amazonaws.com/6fe77f1c-9c9d-81b1-b174-00031f2bc702/a11fbd23-10f8-4485-ad35-f3de9d480449/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466RQAHHWD2%2F20251008%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20251008T130015Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjECQaCXVzLXdlc3QtMiJIMEYCIQCja3KUJzl59DJB4he%2BZc4EIAdSKZH6PHGkvSc5iTCA5gIhAK1O3OugR1Ap%2Fhuos6U7RbD2KxvfMHmfQXSU6K7IuEKgKogECL3%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMNjM3NDIzMTgzODA1Igw0pXUjiFqmEZtM2z8q3AMIdeJHjnKFMXfrzdsbRQMk5j3lsQc7tLMnmEPmHH%2FmX7sZrT4UjUkaKoeSyH%2Bg7wFpYmgvN77x8nGQ0nEQtZAPw2%2F3EH4nPTYF53xRmvyn7%2FMwloMmw7eBz7SIZ3Xk0aPYpbtVRoPUesefx7Jvddzmxu0Q%2FQpw0%2FrRCkLPJAnMdsR8fY3n8s8TBv7cVHHWkjpqgeaNKOD7O7Ej%2FNkVahgG%2BVi%2B8DlcuBG8FNk2EzDNroum71dNW6RzTO3ju65V5xHwLgmRkZRTKS%2FHeolCb01h51d%2BhJY03OORmJCMcOEYzPWg48LVGlFl%2FOA6OAhR%2FQ0eWs%2B1ZzKAz7HOSK6gHOhmcCj%2Bbve0%2FVaH2P708k33m5SQqjZQmfmO0JUCNn2WRPK9uYfSy64QFqpzeEXgAGiaNMQCrWjrLpO1TNcK457IQ4ofuJ9bt3be947z17Fa2pKdcescLFE7GdVtV5L4Nps5%2FoHCl0X1BxCPjpxN8zLRIQZtHFRloU8K6ebOrPFIb4bkjxfC9VidpyS0vnZ1DhFt8ssI1sOwrzRn5Kf%2FRltkKxvJPAp829yFUsrA35VhLZLllGpsdLzGVZS1GWkNqf0UxA5BZhdb9t4qPmp1WbBIufTnxcFuHKdUMxsdfTCxpZnHBjqkASugrWSTfsJxQU%2BvD6fKuut0yaNhG1Zscr6q%2FLX6mZAcKzCrMYWhsql27FMH5lEFZTsvBfNoX2h1ie3MPQw%2B67C4yNLYGIvBL%2BFPBfaw6qimvk48x5JDZBNsxN4fEMufvUM1%2FbemWiZZJSl8V5Q5h0NMxBmZFJkachxllAPWTTVPbSArY%2FYyX3vpKDGMi1fYZYT4IjNycyQDb6nSEKlqbnOjUosj&X-Amz-Signature=9145e81d85d35d531ffb6bbabad25d5846aa559613d371bc2880f6d02529b799&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
21
+ "localPath": "/Users/thibaudfrere/Documents/work-projects/huggingface/research-article-template/app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8014-834f-d700b623256b.png",
22
+ "sourceType": "block",
23
+ "transformedPath": "/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8014-834f-d700b623256b.png"
24
+ },
25
+ "lastEdited": "2025-09-24T09:22:00.000Z",
26
+ "createdAt": "2025-09-24T09:36:35.899Z",
27
+ "updatedAt": "2025-10-08T13:00:25.703Z"
28
+ },
29
+ "27877f1c-9c9d-808d-9c6d-fae817ac8868": {
30
+ "mediaInfo": {
31
+ "type": "DOWNLOAD",
32
+ "originalUrl": "https://prod-files-secure.s3.us-west-2.amazonaws.com/6fe77f1c-9c9d-81b1-b174-00031f2bc702/9b549e26-b527-4ad5-b1ad-3ed049747090/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466X3VEST5Q%2F20251008%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20251008T130020Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjECQaCXVzLXdlc3QtMiJIMEYCIQC4Th0nobtVjj0GnPkA6aNua8I5iyMPP5wm3FPgOcNrpQIhAM6Cd1AyvJOWLCdgyh%2F9xxRIzhQ8ZEYJPtDoTzf9dVzsKogECL3%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMNjM3NDIzMTgzODA1Igwbq%2Fmpsk4PhTeoeU8q3ANUdDY8Un1V3sp1KnXAZQmxQggeyUGmjkp%2BsBgML%2Bh3jLesSMzdlQPFwlTBbqh74KOfU7NmNfHFLiHqWLrpeLClymtDa9BGRQAnfNGuT%2BUXXxp5dSoeOsbUJInqYhW4FsjUBRNw%2FSAslpIJKfTrmcN81%2FIUQNc0TRtVmiiXKPFUCNfWErOAQlec%2B6mNZCJYY1EbzUg1tPFNYYLpoiNb45%2F0t1VlsyeG6WlkA2KFXybguL2BmlWms%2BC%2BDeANHe2r%2Bq0h9JVtz%2BYBB3CzzQdeerNfGtyml%2BTz5DiBvv0h9F5FNcqJ4Uf1CbLGaRVIlCXLHam3xV3Fk%2FpYYQerVcK6t6YWWeRO0x92Uj1TyF1H4aCPO%2BuAjxDdY0WJfdLROdRvwH69zTdBYb26Lo7h2T2MnY%2FjJ4UNGPf0CWoGMVU9BJCO6%2Bm7RySlYPw1Axogb7clhfZAUYsM%2FhBI01n5OLo2mIl1dMcIxnZNc%2FFxp3njz6Jpth%2Fu0KOe8X%2FPTgM9S58jNUVTWHYd3bEm54SfyUlEIwmSVHkdHAvZw8QfhIC8DAJULbBkKHe6nRKxvGqo1UZW2OY0%2BKpOu7kPBwFl13lcqjMeSy%2F%2BvpZDgwHonNSKYiYRrlffBfL%2BUFWdiijebzDHpJnHBjqkAfpE6DXG5ducvq1TeQISVLspLOKXhLJ0Jn1auWbgWOv02xnmSsxVrzmbEGcfsyZArqlrtxdg9N9yH30onEToRB0OnUGFrpiazcXfmdl7JS0Cn3mL4FiEHdFC%2B2lUkrwFbKVX0NhXsjQRmkeUUtT7dXPTDyzpPIxoyghxvJsm6OswiSouBbg4NB6EQaFjgxRw9KEg79jjKbCgyYvpKNs56YVN9kvk&X-Amz-Signature=b9e650906f6978d3d01caebf5329c1571bfa97d53d88a3c571eaaa7b618970d2&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
33
+ "localPath": "/Users/thibaudfrere/Documents/work-projects/huggingface/research-article-template/app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-808d-9c6d-fae817ac8868.png",
34
+ "sourceType": "block",
35
+ "transformedPath": "/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-808d-9c6d-fae817ac8868.png"
36
+ },
37
+ "lastEdited": "2025-09-24T09:22:00.000Z",
38
+ "createdAt": "2025-09-24T09:36:35.943Z",
39
+ "updatedAt": "2025-10-08T13:00:25.727Z"
40
+ },
41
+ "27877f1c-9c9d-8075-ae2e-dc24fe9296ca": {
42
+ "mediaInfo": {
43
+ "type": "DOWNLOAD",
44
+ "originalUrl": "https://prod-files-secure.s3.us-west-2.amazonaws.com/6fe77f1c-9c9d-81b1-b174-00031f2bc702/943ada15-c18b-4434-ac96-cca2440119db/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466X3VEST5Q%2F20251008%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20251008T130020Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjECQaCXVzLXdlc3QtMiJIMEYCIQC4Th0nobtVjj0GnPkA6aNua8I5iyMPP5wm3FPgOcNrpQIhAM6Cd1AyvJOWLCdgyh%2F9xxRIzhQ8ZEYJPtDoTzf9dVzsKogECL3%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMNjM3NDIzMTgzODA1Igwbq%2Fmpsk4PhTeoeU8q3ANUdDY8Un1V3sp1KnXAZQmxQggeyUGmjkp%2BsBgML%2Bh3jLesSMzdlQPFwlTBbqh74KOfU7NmNfHFLiHqWLrpeLClymtDa9BGRQAnfNGuT%2BUXXxp5dSoeOsbUJInqYhW4FsjUBRNw%2FSAslpIJKfTrmcN81%2FIUQNc0TRtVmiiXKPFUCNfWErOAQlec%2B6mNZCJYY1EbzUg1tPFNYYLpoiNb45%2F0t1VlsyeG6WlkA2KFXybguL2BmlWms%2BC%2BDeANHe2r%2Bq0h9JVtz%2BYBB3CzzQdeerNfGtyml%2BTz5DiBvv0h9F5FNcqJ4Uf1CbLGaRVIlCXLHam3xV3Fk%2FpYYQerVcK6t6YWWeRO0x92Uj1TyF1H4aCPO%2BuAjxDdY0WJfdLROdRvwH69zTdBYb26Lo7h2T2MnY%2FjJ4UNGPf0CWoGMVU9BJCO6%2Bm7RySlYPw1Axogb7clhfZAUYsM%2FhBI01n5OLo2mIl1dMcIxnZNc%2FFxp3njz6Jpth%2Fu0KOe8X%2FPTgM9S58jNUVTWHYd3bEm54SfyUlEIwmSVHkdHAvZw8QfhIC8DAJULbBkKHe6nRKxvGqo1UZW2OY0%2BKpOu7kPBwFl13lcqjMeSy%2F%2BvpZDgwHonNSKYiYRrlffBfL%2BUFWdiijebzDHpJnHBjqkAfpE6DXG5ducvq1TeQISVLspLOKXhLJ0Jn1auWbgWOv02xnmSsxVrzmbEGcfsyZArqlrtxdg9N9yH30onEToRB0OnUGFrpiazcXfmdl7JS0Cn3mL4FiEHdFC%2B2lUkrwFbKVX0NhXsjQRmkeUUtT7dXPTDyzpPIxoyghxvJsm6OswiSouBbg4NB6EQaFjgxRw9KEg79jjKbCgyYvpKNs56YVN9kvk&X-Amz-Signature=f6c6aeafa758351188cf92575d7adbac6d4438d61891b377861cceebe4699c16&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
45
+ "localPath": "/Users/thibaudfrere/Documents/work-projects/huggingface/research-article-template/app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8075-ae2e-dc24fe9296ca.png",
46
+ "sourceType": "block",
47
+ "transformedPath": "/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8075-ae2e-dc24fe9296ca.png"
48
+ },
49
+ "lastEdited": "2025-09-24T09:22:00.000Z",
50
+ "createdAt": "2025-09-24T09:36:36.095Z",
51
+ "updatedAt": "2025-10-08T13:00:25.926Z"
52
+ },
53
+ "27877f1c-9c9d-801d-841a-e35011491566": {
54
+ "mediaInfo": {
55
+ "type": "DOWNLOAD",
56
+ "originalUrl": "https://prod-files-secure.s3.us-west-2.amazonaws.com/6fe77f1c-9c9d-81b1-b174-00031f2bc702/3a912fb2-01eb-46d2-9687-41ffbb5b0446/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466Y2XCJFFW%2F20251008%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20251008T130015Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjECQaCXVzLXdlc3QtMiJIMEYCIQDGr1WaO6mypVW00YGoLx4XK5HeMm7tg4Wxi4tRLT0sBQIhAJZB9sfEWXoL6QQ8WnamF77mePTGmWqiZjYqIQ1Ba5kMKogECL3%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMNjM3NDIzMTgzODA1IgyvfgauHIg52DwLqjMq3AOxwogx8uxNPXYdZuG3Giz0pcRPcVYGSzx%2FsCHi3wTJAjMOnK7oq5bOcT%2Fl2UzIP2n1RWhZZWYAbDJFjDsGnnxxjP1dA4W%2BG2JxtQjoyKDV24YvvyoKp%2B8c%2BVuzeJYc%2FAp0wja9UZjjmB6We9vy73l%2F0lzOEnuDWGvA3Wz7HN72nCAxRWfFw3VXoLc12NJA7jMXJpMTJ0kQhR54IefMuCgXQRkP9ThY944aJBcJDVVW2oPCliR0sNKWOwxmzYLfqTaCfVYnYWij0W2PFYWTl8O8Fz%2F6tJQeBzIzYZkQH%2B%2FID9QHo2H91PyVglygKK5nFfSUGmu5%2BlBHNZhF0tQ%2BkkSiJBO%2Fzpzh1l3tJ5%2FSDTS3OkCq9CgQS1LlOZ%2FWVGRa0dO%2BPKuLt2kSE3g%2BdENrh0uHMXL16JnD8tihVBQjDVDteb9ty3wYric3oz7UIdTbcFFhBvLNK2iaTTxgVVe8Iwec1Yh5%2Br70PVEtQ2RGpeJRNF%2FTCdNajTXZecTA4gfj3Dep1uEbLH6wJzKpoYff5ewJZaQ5NqIKjBbwZlP0ZsdmirA3vCFTiBuA%2FtxyQutgfCfqtxDzRYtRLdYtuXHnOJX19k0dW1qKTx%2FvJHjGhd%2BF7DDuSjbWdZfyMNBtvTCCpZnHBjqkAXWtzxhDxy981YikbANxxcBAJxQM7hXL4Lmn9JZH3hI2Z46Dd1xpYDzpq%2Bv0TjPTSsilTsyySr4C8rXZuw4BTrdyjlduhH34%2FNp16EC1u7PlP2iNue5eMeLdERldacspGGFk9eApo%2FSCPEUJdHN%2B514Zr0nBCSnGWEy9najlnY9KFSWwfrPpbEz0Z2MLkADQVvGJ4N1Aa7S4MU9m9WAxOA%2BBxMyX&X-Amz-Signature=fdbb11308e72b72824529e50a6386bf06785d0576e5c27d6cf6f609380614733&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
57
+ "localPath": "/Users/thibaudfrere/Documents/work-projects/huggingface/research-article-template/app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-801d-841a-e35011491566.png",
58
+ "sourceType": "block",
59
+ "transformedPath": "/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-801d-841a-e35011491566.png"
60
+ },
61
+ "lastEdited": "2025-09-24T09:22:00.000Z",
62
+ "createdAt": "2025-09-24T09:36:36.121Z",
63
+ "updatedAt": "2025-10-08T13:00:25.898Z"
64
+ },
65
+ "27877f1c-9c9d-8048-9b7e-db4fa7485915": {
66
+ "mediaInfo": {
67
+ "type": "DOWNLOAD",
68
+ "originalUrl": "https://prod-files-secure.s3.us-west-2.amazonaws.com/6fe77f1c-9c9d-81b1-b174-00031f2bc702/58651851-13c8-4e99-92e3-857c8e3d305b/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466X3VEST5Q%2F20251008%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20251008T130020Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjECQaCXVzLXdlc3QtMiJIMEYCIQC4Th0nobtVjj0GnPkA6aNua8I5iyMPP5wm3FPgOcNrpQIhAM6Cd1AyvJOWLCdgyh%2F9xxRIzhQ8ZEYJPtDoTzf9dVzsKogECL3%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMNjM3NDIzMTgzODA1Igwbq%2Fmpsk4PhTeoeU8q3ANUdDY8Un1V3sp1KnXAZQmxQggeyUGmjkp%2BsBgML%2Bh3jLesSMzdlQPFwlTBbqh74KOfU7NmNfHFLiHqWLrpeLClymtDa9BGRQAnfNGuT%2BUXXxp5dSoeOsbUJInqYhW4FsjUBRNw%2FSAslpIJKfTrmcN81%2FIUQNc0TRtVmiiXKPFUCNfWErOAQlec%2B6mNZCJYY1EbzUg1tPFNYYLpoiNb45%2F0t1VlsyeG6WlkA2KFXybguL2BmlWms%2BC%2BDeANHe2r%2Bq0h9JVtz%2BYBB3CzzQdeerNfGtyml%2BTz5DiBvv0h9F5FNcqJ4Uf1CbLGaRVIlCXLHam3xV3Fk%2FpYYQerVcK6t6YWWeRO0x92Uj1TyF1H4aCPO%2BuAjxDdY0WJfdLROdRvwH69zTdBYb26Lo7h2T2MnY%2FjJ4UNGPf0CWoGMVU9BJCO6%2Bm7RySlYPw1Axogb7clhfZAUYsM%2FhBI01n5OLo2mIl1dMcIxnZNc%2FFxp3njz6Jpth%2Fu0KOe8X%2FPTgM9S58jNUVTWHYd3bEm54SfyUlEIwmSVHkdHAvZw8QfhIC8DAJULbBkKHe6nRKxvGqo1UZW2OY0%2BKpOu7kPBwFl13lcqjMeSy%2F%2BvpZDgwHonNSKYiYRrlffBfL%2BUFWdiijebzDHpJnHBjqkAfpE6DXG5ducvq1TeQISVLspLOKXhLJ0Jn1auWbgWOv02xnmSsxVrzmbEGcfsyZArqlrtxdg9N9yH30onEToRB0OnUGFrpiazcXfmdl7JS0Cn3mL4FiEHdFC%2B2lUkrwFbKVX0NhXsjQRmkeUUtT7dXPTDyzpPIxoyghxvJsm6OswiSouBbg4NB6EQaFjgxRw9KEg79jjKbCgyYvpKNs56YVN9kvk&X-Amz-Signature=f13847b038a80822deb40d3e0873722dc40cb227be4a6edfdb70d27be0794729&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
69
+ "localPath": "/Users/thibaudfrere/Documents/work-projects/huggingface/research-article-template/app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8048-9b7e-db4fa7485915.png",
70
+ "sourceType": "block",
71
+ "transformedPath": "/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8048-9b7e-db4fa7485915.png"
72
+ },
73
+ "lastEdited": "2025-09-24T09:22:00.000Z",
74
+ "createdAt": "2025-09-24T09:36:36.136Z",
75
+ "updatedAt": "2025-10-08T13:00:25.917Z"
76
+ },
77
+ "27877f1c-9c9d-804d-bd0a-e0b1c15e504f": {
78
+ "mediaInfo": {
79
+ "type": "DOWNLOAD",
80
+ "originalUrl": "https://prod-files-secure.s3.us-west-2.amazonaws.com/6fe77f1c-9c9d-81b1-b174-00031f2bc702/3fbc7e6c-8ec0-4ef5-9161-7aef75930323/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466RQAHHWD2%2F20251008%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20251008T130014Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjECQaCXVzLXdlc3QtMiJIMEYCIQCja3KUJzl59DJB4he%2BZc4EIAdSKZH6PHGkvSc5iTCA5gIhAK1O3OugR1Ap%2Fhuos6U7RbD2KxvfMHmfQXSU6K7IuEKgKogECL3%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMNjM3NDIzMTgzODA1Igw0pXUjiFqmEZtM2z8q3AMIdeJHjnKFMXfrzdsbRQMk5j3lsQc7tLMnmEPmHH%2FmX7sZrT4UjUkaKoeSyH%2Bg7wFpYmgvN77x8nGQ0nEQtZAPw2%2F3EH4nPTYF53xRmvyn7%2FMwloMmw7eBz7SIZ3Xk0aPYpbtVRoPUesefx7Jvddzmxu0Q%2FQpw0%2FrRCkLPJAnMdsR8fY3n8s8TBv7cVHHWkjpqgeaNKOD7O7Ej%2FNkVahgG%2BVi%2B8DlcuBG8FNk2EzDNroum71dNW6RzTO3ju65V5xHwLgmRkZRTKS%2FHeolCb01h51d%2BhJY03OORmJCMcOEYzPWg48LVGlFl%2FOA6OAhR%2FQ0eWs%2B1ZzKAz7HOSK6gHOhmcCj%2Bbve0%2FVaH2P708k33m5SQqjZQmfmO0JUCNn2WRPK9uYfSy64QFqpzeEXgAGiaNMQCrWjrLpO1TNcK457IQ4ofuJ9bt3be947z17Fa2pKdcescLFE7GdVtV5L4Nps5%2FoHCl0X1BxCPjpxN8zLRIQZtHFRloU8K6ebOrPFIb4bkjxfC9VidpyS0vnZ1DhFt8ssI1sOwrzRn5Kf%2FRltkKxvJPAp829yFUsrA35VhLZLllGpsdLzGVZS1GWkNqf0UxA5BZhdb9t4qPmp1WbBIufTnxcFuHKdUMxsdfTCxpZnHBjqkASugrWSTfsJxQU%2BvD6fKuut0yaNhG1Zscr6q%2FLX6mZAcKzCrMYWhsql27FMH5lEFZTsvBfNoX2h1ie3MPQw%2B67C4yNLYGIvBL%2BFPBfaw6qimvk48x5JDZBNsxN4fEMufvUM1%2FbemWiZZJSl8V5Q5h0NMxBmZFJkachxllAPWTTVPbSArY%2FYyX3vpKDGMi1fYZYT4IjNycyQDb6nSEKlqbnOjUosj&X-Amz-Signature=fd7ef992dc4f3a290a5f04a0a640fe2828b5a33487682c4956913fe674c9c017&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
81
+ "localPath": "/Users/thibaudfrere/Documents/work-projects/huggingface/research-article-template/app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-804d-bd0a-e0b1c15e504f.png",
82
+ "sourceType": "block",
83
+ "transformedPath": "/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-804d-bd0a-e0b1c15e504f.png"
84
+ },
85
+ "lastEdited": "2025-09-24T09:22:00.000Z",
86
+ "createdAt": "2025-09-24T09:36:36.244Z",
87
+ "updatedAt": "2025-10-08T13:00:26.009Z"
88
+ },
89
+ "27877f1c-9c9d-80b9-8cfb-f0a6aaaa8760": {
90
+ "mediaInfo": {
91
+ "type": "DOWNLOAD",
92
+ "originalUrl": "https://prod-files-secure.s3.us-west-2.amazonaws.com/6fe77f1c-9c9d-81b1-b174-00031f2bc702/d3899f55-47ba-45b0-be0f-9abc5bb4f247/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466RQAHHWD2%2F20251008%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20251008T130015Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjECQaCXVzLXdlc3QtMiJIMEYCIQCja3KUJzl59DJB4he%2BZc4EIAdSKZH6PHGkvSc5iTCA5gIhAK1O3OugR1Ap%2Fhuos6U7RbD2KxvfMHmfQXSU6K7IuEKgKogECL3%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMNjM3NDIzMTgzODA1Igw0pXUjiFqmEZtM2z8q3AMIdeJHjnKFMXfrzdsbRQMk5j3lsQc7tLMnmEPmHH%2FmX7sZrT4UjUkaKoeSyH%2Bg7wFpYmgvN77x8nGQ0nEQtZAPw2%2F3EH4nPTYF53xRmvyn7%2FMwloMmw7eBz7SIZ3Xk0aPYpbtVRoPUesefx7Jvddzmxu0Q%2FQpw0%2FrRCkLPJAnMdsR8fY3n8s8TBv7cVHHWkjpqgeaNKOD7O7Ej%2FNkVahgG%2BVi%2B8DlcuBG8FNk2EzDNroum71dNW6RzTO3ju65V5xHwLgmRkZRTKS%2FHeolCb01h51d%2BhJY03OORmJCMcOEYzPWg48LVGlFl%2FOA6OAhR%2FQ0eWs%2B1ZzKAz7HOSK6gHOhmcCj%2Bbve0%2FVaH2P708k33m5SQqjZQmfmO0JUCNn2WRPK9uYfSy64QFqpzeEXgAGiaNMQCrWjrLpO1TNcK457IQ4ofuJ9bt3be947z17Fa2pKdcescLFE7GdVtV5L4Nps5%2FoHCl0X1BxCPjpxN8zLRIQZtHFRloU8K6ebOrPFIb4bkjxfC9VidpyS0vnZ1DhFt8ssI1sOwrzRn5Kf%2FRltkKxvJPAp829yFUsrA35VhLZLllGpsdLzGVZS1GWkNqf0UxA5BZhdb9t4qPmp1WbBIufTnxcFuHKdUMxsdfTCxpZnHBjqkASugrWSTfsJxQU%2BvD6fKuut0yaNhG1Zscr6q%2FLX6mZAcKzCrMYWhsql27FMH5lEFZTsvBfNoX2h1ie3MPQw%2B67C4yNLYGIvBL%2BFPBfaw6qimvk48x5JDZBNsxN4fEMufvUM1%2FbemWiZZJSl8V5Q5h0NMxBmZFJkachxllAPWTTVPbSArY%2FYyX3vpKDGMi1fYZYT4IjNycyQDb6nSEKlqbnOjUosj&X-Amz-Signature=0e702a4db1c4dabdd69b83b59535958cd39a3e2906332aa607e61864797bca59&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
93
+ "localPath": "/Users/thibaudfrere/Documents/work-projects/huggingface/research-article-template/app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80b9-8cfb-f0a6aaaa8760.png",
94
+ "sourceType": "block",
95
+ "transformedPath": "/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80b9-8cfb-f0a6aaaa8760.png"
96
+ },
97
+ "lastEdited": "2025-09-24T09:22:00.000Z",
98
+ "createdAt": "2025-09-24T09:36:36.247Z",
99
+ "updatedAt": "2025-10-08T13:00:26.000Z"
100
+ },
101
+ "27877f1c-9c9d-80aa-b968-c54c9fe7e5d7": {
102
+ "mediaInfo": {
103
+ "type": "DOWNLOAD",
104
+ "originalUrl": "https://prod-files-secure.s3.us-west-2.amazonaws.com/6fe77f1c-9c9d-81b1-b174-00031f2bc702/1f240ffd-b4d7-422a-9a81-0aabda1f6c16/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466Y2XCJFFW%2F20251008%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20251008T130015Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjECQaCXVzLXdlc3QtMiJIMEYCIQDGr1WaO6mypVW00YGoLx4XK5HeMm7tg4Wxi4tRLT0sBQIhAJZB9sfEWXoL6QQ8WnamF77mePTGmWqiZjYqIQ1Ba5kMKogECL3%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMNjM3NDIzMTgzODA1IgyvfgauHIg52DwLqjMq3AOxwogx8uxNPXYdZuG3Giz0pcRPcVYGSzx%2FsCHi3wTJAjMOnK7oq5bOcT%2Fl2UzIP2n1RWhZZWYAbDJFjDsGnnxxjP1dA4W%2BG2JxtQjoyKDV24YvvyoKp%2B8c%2BVuzeJYc%2FAp0wja9UZjjmB6We9vy73l%2F0lzOEnuDWGvA3Wz7HN72nCAxRWfFw3VXoLc12NJA7jMXJpMTJ0kQhR54IefMuCgXQRkP9ThY944aJBcJDVVW2oPCliR0sNKWOwxmzYLfqTaCfVYnYWij0W2PFYWTl8O8Fz%2F6tJQeBzIzYZkQH%2B%2FID9QHo2H91PyVglygKK5nFfSUGmu5%2BlBHNZhF0tQ%2BkkSiJBO%2Fzpzh1l3tJ5%2FSDTS3OkCq9CgQS1LlOZ%2FWVGRa0dO%2BPKuLt2kSE3g%2BdENrh0uHMXL16JnD8tihVBQjDVDteb9ty3wYric3oz7UIdTbcFFhBvLNK2iaTTxgVVe8Iwec1Yh5%2Br70PVEtQ2RGpeJRNF%2FTCdNajTXZecTA4gfj3Dep1uEbLH6wJzKpoYff5ewJZaQ5NqIKjBbwZlP0ZsdmirA3vCFTiBuA%2FtxyQutgfCfqtxDzRYtRLdYtuXHnOJX19k0dW1qKTx%2FvJHjGhd%2BF7DDuSjbWdZfyMNBtvTCCpZnHBjqkAXWtzxhDxy981YikbANxxcBAJxQM7hXL4Lmn9JZH3hI2Z46Dd1xpYDzpq%2Bv0TjPTSsilTsyySr4C8rXZuw4BTrdyjlduhH34%2FNp16EC1u7PlP2iNue5eMeLdERldacspGGFk9eApo%2FSCPEUJdHN%2B514Zr0nBCSnGWEy9najlnY9KFSWwfrPpbEz0Z2MLkADQVvGJ4N1Aa7S4MU9m9WAxOA%2BBxMyX&X-Amz-Signature=c353bf2bf0d4ef95c66531df5c779035a90add76e2c286e9e26d41c1a6526eb9&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
105
+ "localPath": "/Users/thibaudfrere/Documents/work-projects/huggingface/research-article-template/app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80aa-b968-c54c9fe7e5d7.png",
106
+ "sourceType": "block",
107
+ "transformedPath": "/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80aa-b968-c54c9fe7e5d7.png"
108
+ },
109
+ "lastEdited": "2025-09-24T09:22:00.000Z",
110
+ "createdAt": "2025-09-24T09:36:36.247Z",
111
+ "updatedAt": "2025-10-08T13:00:26.061Z"
112
+ },
113
+ "27877f1c-9c9d-8013-b668-f14bd1ac0ec0": {
114
+ "mediaInfo": {
115
+ "type": "DOWNLOAD",
116
+ "originalUrl": "https://prod-files-secure.s3.us-west-2.amazonaws.com/6fe77f1c-9c9d-81b1-b174-00031f2bc702/cb01b334-89a5-436f-a141-b7c310d25b57/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466Y2XCJFFW%2F20251008%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20251008T130015Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjECQaCXVzLXdlc3QtMiJIMEYCIQDGr1WaO6mypVW00YGoLx4XK5HeMm7tg4Wxi4tRLT0sBQIhAJZB9sfEWXoL6QQ8WnamF77mePTGmWqiZjYqIQ1Ba5kMKogECL3%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMNjM3NDIzMTgzODA1IgyvfgauHIg52DwLqjMq3AOxwogx8uxNPXYdZuG3Giz0pcRPcVYGSzx%2FsCHi3wTJAjMOnK7oq5bOcT%2Fl2UzIP2n1RWhZZWYAbDJFjDsGnnxxjP1dA4W%2BG2JxtQjoyKDV24YvvyoKp%2B8c%2BVuzeJYc%2FAp0wja9UZjjmB6We9vy73l%2F0lzOEnuDWGvA3Wz7HN72nCAxRWfFw3VXoLc12NJA7jMXJpMTJ0kQhR54IefMuCgXQRkP9ThY944aJBcJDVVW2oPCliR0sNKWOwxmzYLfqTaCfVYnYWij0W2PFYWTl8O8Fz%2F6tJQeBzIzYZkQH%2B%2FID9QHo2H91PyVglygKK5nFfSUGmu5%2BlBHNZhF0tQ%2BkkSiJBO%2Fzpzh1l3tJ5%2FSDTS3OkCq9CgQS1LlOZ%2FWVGRa0dO%2BPKuLt2kSE3g%2BdENrh0uHMXL16JnD8tihVBQjDVDteb9ty3wYric3oz7UIdTbcFFhBvLNK2iaTTxgVVe8Iwec1Yh5%2Br70PVEtQ2RGpeJRNF%2FTCdNajTXZecTA4gfj3Dep1uEbLH6wJzKpoYff5ewJZaQ5NqIKjBbwZlP0ZsdmirA3vCFTiBuA%2FtxyQutgfCfqtxDzRYtRLdYtuXHnOJX19k0dW1qKTx%2FvJHjGhd%2BF7DDuSjbWdZfyMNBtvTCCpZnHBjqkAXWtzxhDxy981YikbANxxcBAJxQM7hXL4Lmn9JZH3hI2Z46Dd1xpYDzpq%2Bv0TjPTSsilTsyySr4C8rXZuw4BTrdyjlduhH34%2FNp16EC1u7PlP2iNue5eMeLdERldacspGGFk9eApo%2FSCPEUJdHN%2B514Zr0nBCSnGWEy9najlnY9KFSWwfrPpbEz0Z2MLkADQVvGJ4N1Aa7S4MU9m9WAxOA%2BBxMyX&X-Amz-Signature=ff0a0599c70fd9f202d55e4a7e887de8d8727724550d0e9faf6df28e0e9b7696&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
117
+ "localPath": "/Users/thibaudfrere/Documents/work-projects/huggingface/research-article-template/app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8013-b668-f14bd1ac0ec0.png",
118
+ "sourceType": "block",
119
+ "transformedPath": "/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8013-b668-f14bd1ac0ec0.png"
120
+ },
121
+ "lastEdited": "2025-09-24T09:22:00.000Z",
122
+ "createdAt": "2025-09-24T09:36:36.250Z",
123
+ "updatedAt": "2025-10-08T13:00:26.056Z"
124
+ },
125
+ "27877f1c-9c9d-80e9-b729-dbd328930bed": {
126
+ "mediaInfo": {
127
+ "type": "DOWNLOAD",
128
+ "originalUrl": "https://prod-files-secure.s3.us-west-2.amazonaws.com/6fe77f1c-9c9d-81b1-b174-00031f2bc702/9d723e06-252f-44e3-844e-1410fa92667a/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466RQAHHWD2%2F20251008%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20251008T130015Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjECQaCXVzLXdlc3QtMiJIMEYCIQCja3KUJzl59DJB4he%2BZc4EIAdSKZH6PHGkvSc5iTCA5gIhAK1O3OugR1Ap%2Fhuos6U7RbD2KxvfMHmfQXSU6K7IuEKgKogECL3%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMNjM3NDIzMTgzODA1Igw0pXUjiFqmEZtM2z8q3AMIdeJHjnKFMXfrzdsbRQMk5j3lsQc7tLMnmEPmHH%2FmX7sZrT4UjUkaKoeSyH%2Bg7wFpYmgvN77x8nGQ0nEQtZAPw2%2F3EH4nPTYF53xRmvyn7%2FMwloMmw7eBz7SIZ3Xk0aPYpbtVRoPUesefx7Jvddzmxu0Q%2FQpw0%2FrRCkLPJAnMdsR8fY3n8s8TBv7cVHHWkjpqgeaNKOD7O7Ej%2FNkVahgG%2BVi%2B8DlcuBG8FNk2EzDNroum71dNW6RzTO3ju65V5xHwLgmRkZRTKS%2FHeolCb01h51d%2BhJY03OORmJCMcOEYzPWg48LVGlFl%2FOA6OAhR%2FQ0eWs%2B1ZzKAz7HOSK6gHOhmcCj%2Bbve0%2FVaH2P708k33m5SQqjZQmfmO0JUCNn2WRPK9uYfSy64QFqpzeEXgAGiaNMQCrWjrLpO1TNcK457IQ4ofuJ9bt3be947z17Fa2pKdcescLFE7GdVtV5L4Nps5%2FoHCl0X1BxCPjpxN8zLRIQZtHFRloU8K6ebOrPFIb4bkjxfC9VidpyS0vnZ1DhFt8ssI1sOwrzRn5Kf%2FRltkKxvJPAp829yFUsrA35VhLZLllGpsdLzGVZS1GWkNqf0UxA5BZhdb9t4qPmp1WbBIufTnxcFuHKdUMxsdfTCxpZnHBjqkASugrWSTfsJxQU%2BvD6fKuut0yaNhG1Zscr6q%2FLX6mZAcKzCrMYWhsql27FMH5lEFZTsvBfNoX2h1ie3MPQw%2B67C4yNLYGIvBL%2BFPBfaw6qimvk48x5JDZBNsxN4fEMufvUM1%2FbemWiZZJSl8V5Q5h0NMxBmZFJkachxllAPWTTVPbSArY%2FYyX3vpKDGMi1fYZYT4IjNycyQDb6nSEKlqbnOjUosj&X-Amz-Signature=4faf0d241e315ba80e5f56bb4cb410229c15e429fcab990fd1ebc2b17e2e4388&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
129
+ "localPath": "/Users/thibaudfrere/Documents/work-projects/huggingface/research-article-template/app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80e9-b729-dbd328930bed.png",
130
+ "sourceType": "block",
131
+ "transformedPath": "/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80e9-b729-dbd328930bed.png"
132
+ },
133
+ "lastEdited": "2025-09-24T09:22:00.000Z",
134
+ "createdAt": "2025-09-24T09:36:36.251Z",
135
+ "updatedAt": "2025-10-08T13:00:26.048Z"
136
+ },
137
+ "27877f1c-9c9d-80a9-b4d0-f2129716632d": {
138
+ "mediaInfo": {
139
+ "type": "DOWNLOAD",
140
+ "originalUrl": "https://prod-files-secure.s3.us-west-2.amazonaws.com/6fe77f1c-9c9d-81b1-b174-00031f2bc702/137c2c51-6f89-437f-ba45-3a844c747b7a/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466W5FHSQK6%2F20251008%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20251008T130018Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjECQaCXVzLXdlc3QtMiJGMEQCIAhuEUuJdQLuRcUIqWrlF%2FxJVRmDyl6u61kVLhtjlfZUAiBV%2Fg6BRYZGv8gjl2KUiGhGUm6oydw9R%2BGbL4YBC8orxyqIBAi9%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8BEAAaDDYzNzQyMzE4MzgwNSIM3uBXzGO9Ig0qNGY4KtwD8SFvKEBtLvytudTAIznpwZ0LlbKJs50UtNCuHXZMaMCWVzh3bh5s1YUDv9F8QzpxpmwoEHNA0zdieOQ0RTayznMP%2FQonTMVpq110QUSpjw0oiOQAObBO%2FZhOW3eVIvyERdtG8hJ%2FFdtOx68HiwShjXQNIx%2FTLP%2FyKsamFu97bNFKLLbXaKEQL%2FPeYBMfUh1FOikwzUsWtnX6xkIyDZlzyvR5XfiR93IkhPKAe3tRogdfHErVfaMhWD4CLC3CotugLqYLChLu2FEgjljFpEHc5u5od5FnVy9AUpcA9X21VVFEM5ZdQ2e3RAB6wIMQvIaYTHHCnFFfouA8nRqcEVzWysN3qwh2ilXM%2FkIfZN7fRIxbBSPLyUIVcJpZNwbzs8d5bs3qvzM9G8FkzDvtYML3JOdEzF4LuxAAJJLAqhTTQ1PhnB4lalWW%2BzGBluxdvxfidxr7w8b74x1A6sr2RRg204TCYmbv8epUSs%2BRfkjbpQb25T59MrJnDDkvR3vwM0uRiYOcx7BIJoJQ9NABbDhUdQivPKSx%2FWfUI4qzy2Qu3ylcwrudYUrkObXxW8EpCrpCr3q7hWCPkKfvLhioYojN04XKEoAr9eJNJKYu8%2BGtS2%2FHsFqf94Soxz4NYa8wsaWZxwY6pgEneyRps7aPn1nFrZvbD3DUmmNVnG7Ot90eZ9AeaSCgE0a8LPo2EBJqYduCTOFx0ZS31WSpymmuKKyjsI94sx8u%2F5Mmvyc%2F6g6F%2FSGga8Z7F5NIPVciGCTsa2iZFLblK4LGKDfvx5RgoyuAatccTNlNYca%2F5w9iQ1JXOu83l39OW1O54Xr5rX96kIiak60h5LxJgIHU3zeBdD4UeGH39LaRoJyMksdX&X-Amz-Signature=3e45baae308e0004d8e62bc03d6e459d6310e19ae4935db52491727bc24231cb&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
141
+ "localPath": "/Users/thibaudfrere/Documents/work-projects/huggingface/research-article-template/app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80a9-b4d0-f2129716632d.png",
142
+ "sourceType": "block",
143
+ "transformedPath": "/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80a9-b4d0-f2129716632d.png"
144
+ },
145
+ "lastEdited": "2025-09-24T09:22:00.000Z",
146
+ "createdAt": "2025-09-24T09:36:36.257Z",
147
+ "updatedAt": "2025-10-08T13:00:26.048Z"
148
+ },
149
+ "27877f1c-9c9d-80b6-be07-e8646502f82a": {
150
+ "mediaInfo": {
151
+ "type": "DOWNLOAD",
152
+ "originalUrl": "https://prod-files-secure.s3.us-west-2.amazonaws.com/6fe77f1c-9c9d-81b1-b174-00031f2bc702/843a4edd-435d-45ca-8212-1af31305fb3a/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466RQAHHWD2%2F20251008%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20251008T130015Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjECQaCXVzLXdlc3QtMiJIMEYCIQCja3KUJzl59DJB4he%2BZc4EIAdSKZH6PHGkvSc5iTCA5gIhAK1O3OugR1Ap%2Fhuos6U7RbD2KxvfMHmfQXSU6K7IuEKgKogECL3%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMNjM3NDIzMTgzODA1Igw0pXUjiFqmEZtM2z8q3AMIdeJHjnKFMXfrzdsbRQMk5j3lsQc7tLMnmEPmHH%2FmX7sZrT4UjUkaKoeSyH%2Bg7wFpYmgvN77x8nGQ0nEQtZAPw2%2F3EH4nPTYF53xRmvyn7%2FMwloMmw7eBz7SIZ3Xk0aPYpbtVRoPUesefx7Jvddzmxu0Q%2FQpw0%2FrRCkLPJAnMdsR8fY3n8s8TBv7cVHHWkjpqgeaNKOD7O7Ej%2FNkVahgG%2BVi%2B8DlcuBG8FNk2EzDNroum71dNW6RzTO3ju65V5xHwLgmRkZRTKS%2FHeolCb01h51d%2BhJY03OORmJCMcOEYzPWg48LVGlFl%2FOA6OAhR%2FQ0eWs%2B1ZzKAz7HOSK6gHOhmcCj%2Bbve0%2FVaH2P708k33m5SQqjZQmfmO0JUCNn2WRPK9uYfSy64QFqpzeEXgAGiaNMQCrWjrLpO1TNcK457IQ4ofuJ9bt3be947z17Fa2pKdcescLFE7GdVtV5L4Nps5%2FoHCl0X1BxCPjpxN8zLRIQZtHFRloU8K6ebOrPFIb4bkjxfC9VidpyS0vnZ1DhFt8ssI1sOwrzRn5Kf%2FRltkKxvJPAp829yFUsrA35VhLZLllGpsdLzGVZS1GWkNqf0UxA5BZhdb9t4qPmp1WbBIufTnxcFuHKdUMxsdfTCxpZnHBjqkASugrWSTfsJxQU%2BvD6fKuut0yaNhG1Zscr6q%2FLX6mZAcKzCrMYWhsql27FMH5lEFZTsvBfNoX2h1ie3MPQw%2B67C4yNLYGIvBL%2BFPBfaw6qimvk48x5JDZBNsxN4fEMufvUM1%2FbemWiZZJSl8V5Q5h0NMxBmZFJkachxllAPWTTVPbSArY%2FYyX3vpKDGMi1fYZYT4IjNycyQDb6nSEKlqbnOjUosj&X-Amz-Signature=6b41be5cc09cf3fae5304c63a3291650e24c6ad9285ba21202c90a186c8be390&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
153
+ "localPath": "/Users/thibaudfrere/Documents/work-projects/huggingface/research-article-template/app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80b6-be07-e8646502f82a.png",
154
+ "sourceType": "block",
155
+ "transformedPath": "/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80b6-be07-e8646502f82a.png"
156
+ },
157
+ "lastEdited": "2025-09-24T09:22:00.000Z",
158
+ "createdAt": "2025-09-24T09:36:36.274Z",
159
+ "updatedAt": "2025-10-08T13:00:25.995Z"
160
+ },
161
+ "27877f1c-9c9d-808f-b712-c7c608da3fc6": {
162
+ "mediaInfo": {
163
+ "type": "DOWNLOAD",
164
+ "originalUrl": "https://prod-files-secure.s3.us-west-2.amazonaws.com/6fe77f1c-9c9d-81b1-b174-00031f2bc702/33327526-41c3-4e9c-b4ee-c7cc3c23c7ca/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466Y2XCJFFW%2F20251008%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20251008T130015Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjECQaCXVzLXdlc3QtMiJIMEYCIQDGr1WaO6mypVW00YGoLx4XK5HeMm7tg4Wxi4tRLT0sBQIhAJZB9sfEWXoL6QQ8WnamF77mePTGmWqiZjYqIQ1Ba5kMKogECL3%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMNjM3NDIzMTgzODA1IgyvfgauHIg52DwLqjMq3AOxwogx8uxNPXYdZuG3Giz0pcRPcVYGSzx%2FsCHi3wTJAjMOnK7oq5bOcT%2Fl2UzIP2n1RWhZZWYAbDJFjDsGnnxxjP1dA4W%2BG2JxtQjoyKDV24YvvyoKp%2B8c%2BVuzeJYc%2FAp0wja9UZjjmB6We9vy73l%2F0lzOEnuDWGvA3Wz7HN72nCAxRWfFw3VXoLc12NJA7jMXJpMTJ0kQhR54IefMuCgXQRkP9ThY944aJBcJDVVW2oPCliR0sNKWOwxmzYLfqTaCfVYnYWij0W2PFYWTl8O8Fz%2F6tJQeBzIzYZkQH%2B%2FID9QHo2H91PyVglygKK5nFfSUGmu5%2BlBHNZhF0tQ%2BkkSiJBO%2Fzpzh1l3tJ5%2FSDTS3OkCq9CgQS1LlOZ%2FWVGRa0dO%2BPKuLt2kSE3g%2BdENrh0uHMXL16JnD8tihVBQjDVDteb9ty3wYric3oz7UIdTbcFFhBvLNK2iaTTxgVVe8Iwec1Yh5%2Br70PVEtQ2RGpeJRNF%2FTCdNajTXZecTA4gfj3Dep1uEbLH6wJzKpoYff5ewJZaQ5NqIKjBbwZlP0ZsdmirA3vCFTiBuA%2FtxyQutgfCfqtxDzRYtRLdYtuXHnOJX19k0dW1qKTx%2FvJHjGhd%2BF7DDuSjbWdZfyMNBtvTCCpZnHBjqkAXWtzxhDxy981YikbANxxcBAJxQM7hXL4Lmn9JZH3hI2Z46Dd1xpYDzpq%2Bv0TjPTSsilTsyySr4C8rXZuw4BTrdyjlduhH34%2FNp16EC1u7PlP2iNue5eMeLdERldacspGGFk9eApo%2FSCPEUJdHN%2B514Zr0nBCSnGWEy9najlnY9KFSWwfrPpbEz0Z2MLkADQVvGJ4N1Aa7S4MU9m9WAxOA%2BBxMyX&X-Amz-Signature=68518895319bebfef33cc5c5786ea73b408d55f058fe1b07a6e057f3167389dc&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
165
+ "localPath": "/Users/thibaudfrere/Documents/work-projects/huggingface/research-article-template/app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-808f-b712-c7c608da3fc6.png",
166
+ "sourceType": "block",
167
+ "transformedPath": "/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-808f-b712-c7c608da3fc6.png"
168
+ },
169
+ "lastEdited": "2025-09-24T09:22:00.000Z",
170
+ "createdAt": "2025-09-24T09:36:36.274Z",
171
+ "updatedAt": "2025-10-08T13:00:25.863Z"
172
+ },
173
+ "27877f1c-9c9d-8031-ac8d-c5678af1bdd5": {
174
+ "mediaInfo": {
175
+ "type": "DOWNLOAD",
176
+ "originalUrl": "https://prod-files-secure.s3.us-west-2.amazonaws.com/6fe77f1c-9c9d-81b1-b174-00031f2bc702/16831579-d887-4894-b36a-d4df8a134653/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466X3VEST5Q%2F20251008%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20251008T130020Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjECQaCXVzLXdlc3QtMiJIMEYCIQC4Th0nobtVjj0GnPkA6aNua8I5iyMPP5wm3FPgOcNrpQIhAM6Cd1AyvJOWLCdgyh%2F9xxRIzhQ8ZEYJPtDoTzf9dVzsKogECL3%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMNjM3NDIzMTgzODA1Igwbq%2Fmpsk4PhTeoeU8q3ANUdDY8Un1V3sp1KnXAZQmxQggeyUGmjkp%2BsBgML%2Bh3jLesSMzdlQPFwlTBbqh74KOfU7NmNfHFLiHqWLrpeLClymtDa9BGRQAnfNGuT%2BUXXxp5dSoeOsbUJInqYhW4FsjUBRNw%2FSAslpIJKfTrmcN81%2FIUQNc0TRtVmiiXKPFUCNfWErOAQlec%2B6mNZCJYY1EbzUg1tPFNYYLpoiNb45%2F0t1VlsyeG6WlkA2KFXybguL2BmlWms%2BC%2BDeANHe2r%2Bq0h9JVtz%2BYBB3CzzQdeerNfGtyml%2BTz5DiBvv0h9F5FNcqJ4Uf1CbLGaRVIlCXLHam3xV3Fk%2FpYYQerVcK6t6YWWeRO0x92Uj1TyF1H4aCPO%2BuAjxDdY0WJfdLROdRvwH69zTdBYb26Lo7h2T2MnY%2FjJ4UNGPf0CWoGMVU9BJCO6%2Bm7RySlYPw1Axogb7clhfZAUYsM%2FhBI01n5OLo2mIl1dMcIxnZNc%2FFxp3njz6Jpth%2Fu0KOe8X%2FPTgM9S58jNUVTWHYd3bEm54SfyUlEIwmSVHkdHAvZw8QfhIC8DAJULbBkKHe6nRKxvGqo1UZW2OY0%2BKpOu7kPBwFl13lcqjMeSy%2F%2BvpZDgwHonNSKYiYRrlffBfL%2BUFWdiijebzDHpJnHBjqkAfpE6DXG5ducvq1TeQISVLspLOKXhLJ0Jn1auWbgWOv02xnmSsxVrzmbEGcfsyZArqlrtxdg9N9yH30onEToRB0OnUGFrpiazcXfmdl7JS0Cn3mL4FiEHdFC%2B2lUkrwFbKVX0NhXsjQRmkeUUtT7dXPTDyzpPIxoyghxvJsm6OswiSouBbg4NB6EQaFjgxRw9KEg79jjKbCgyYvpKNs56YVN9kvk&X-Amz-Signature=0e07c27075a366f43cf237c80660a72ba43bdbad3486e7f1f9e3a12afdfcc7f0&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
177
+ "localPath": "/Users/thibaudfrere/Documents/work-projects/huggingface/research-article-template/app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8031-ac8d-c5678af1bdd5.png",
178
+ "sourceType": "block",
179
+ "transformedPath": "/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8031-ac8d-c5678af1bdd5.png"
180
+ },
181
+ "lastEdited": "2025-09-24T09:22:00.000Z",
182
+ "createdAt": "2025-09-24T09:36:36.276Z",
183
+ "updatedAt": "2025-10-08T13:00:25.920Z"
184
+ },
185
+ "27877f1c-9c9d-80e7-a500-fb79cebde7e3": {
186
+ "mediaInfo": {
187
+ "type": "DOWNLOAD",
188
+ "originalUrl": "https://prod-files-secure.s3.us-west-2.amazonaws.com/6fe77f1c-9c9d-81b1-b174-00031f2bc702/0458ed42-9805-4c8f-9d7e-c675474a626e/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466X3VEST5Q%2F20251008%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20251008T130020Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjECQaCXVzLXdlc3QtMiJIMEYCIQC4Th0nobtVjj0GnPkA6aNua8I5iyMPP5wm3FPgOcNrpQIhAM6Cd1AyvJOWLCdgyh%2F9xxRIzhQ8ZEYJPtDoTzf9dVzsKogECL3%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMNjM3NDIzMTgzODA1Igwbq%2Fmpsk4PhTeoeU8q3ANUdDY8Un1V3sp1KnXAZQmxQggeyUGmjkp%2BsBgML%2Bh3jLesSMzdlQPFwlTBbqh74KOfU7NmNfHFLiHqWLrpeLClymtDa9BGRQAnfNGuT%2BUXXxp5dSoeOsbUJInqYhW4FsjUBRNw%2FSAslpIJKfTrmcN81%2FIUQNc0TRtVmiiXKPFUCNfWErOAQlec%2B6mNZCJYY1EbzUg1tPFNYYLpoiNb45%2F0t1VlsyeG6WlkA2KFXybguL2BmlWms%2BC%2BDeANHe2r%2Bq0h9JVtz%2BYBB3CzzQdeerNfGtyml%2BTz5DiBvv0h9F5FNcqJ4Uf1CbLGaRVIlCXLHam3xV3Fk%2FpYYQerVcK6t6YWWeRO0x92Uj1TyF1H4aCPO%2BuAjxDdY0WJfdLROdRvwH69zTdBYb26Lo7h2T2MnY%2FjJ4UNGPf0CWoGMVU9BJCO6%2Bm7RySlYPw1Axogb7clhfZAUYsM%2FhBI01n5OLo2mIl1dMcIxnZNc%2FFxp3njz6Jpth%2Fu0KOe8X%2FPTgM9S58jNUVTWHYd3bEm54SfyUlEIwmSVHkdHAvZw8QfhIC8DAJULbBkKHe6nRKxvGqo1UZW2OY0%2BKpOu7kPBwFl13lcqjMeSy%2F%2BvpZDgwHonNSKYiYRrlffBfL%2BUFWdiijebzDHpJnHBjqkAfpE6DXG5ducvq1TeQISVLspLOKXhLJ0Jn1auWbgWOv02xnmSsxVrzmbEGcfsyZArqlrtxdg9N9yH30onEToRB0OnUGFrpiazcXfmdl7JS0Cn3mL4FiEHdFC%2B2lUkrwFbKVX0NhXsjQRmkeUUtT7dXPTDyzpPIxoyghxvJsm6OswiSouBbg4NB6EQaFjgxRw9KEg79jjKbCgyYvpKNs56YVN9kvk&X-Amz-Signature=e43c1f35e9930e144c0b2b6098457be4848a723301fd78f3ff27d94f87ccbebb&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
189
+ "localPath": "/Users/thibaudfrere/Documents/work-projects/huggingface/research-article-template/app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80e7-a500-fb79cebde7e3.png",
190
+ "sourceType": "block",
191
+ "transformedPath": "/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80e7-a500-fb79cebde7e3.png"
192
+ },
193
+ "lastEdited": "2025-09-24T09:22:00.000Z",
194
+ "createdAt": "2025-09-24T09:36:36.448Z",
195
+ "updatedAt": "2025-10-08T13:00:26.065Z"
196
+ }
197
+ }
198
+ }
app/scripts/notion-importer/README.md ADDED
@@ -0,0 +1,291 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Notion Importer
2
+
3
+ Complete Notion to MDX (Markdown + JSX) importer optimized for Astro with advanced media handling, interactive components, and seamless integration.
4
+
5
+ ## 🚀 Quick Start
6
+
7
+ ### Method 1: Using NOTION_PAGE_ID (Recommended)
8
+
9
+ ```bash
10
+ # Install dependencies
11
+ npm install
12
+
13
+ # Setup environment variables
14
+ cp env.example .env
15
+ # Edit .env with your Notion token and page ID
16
+
17
+ # Complete Notion → MDX conversion (fetches title/slug automatically)
18
+ NOTION_TOKEN=secret_xxx NOTION_PAGE_ID=abc123 node index.mjs
19
+
20
+ # Or use .env file
21
+ node index.mjs
22
+ ```
23
+
24
+ ### Method 2: Using pages.json (Legacy)
25
+
26
+ ```bash
27
+ # Install dependencies
28
+ npm install
29
+
30
+ # Setup environment variables
31
+ cp env.example .env
32
+ # Edit .env with your Notion token
33
+
34
+ # Configure pages in input/pages.json
35
+ # {
36
+ # "pages": [
37
+ # {
38
+ # "id": "your-page-id",
39
+ # "title": "Title",
40
+ # "slug": "slug"
41
+ # }
42
+ # ]
43
+ # }
44
+
45
+ # Complete Notion → MDX conversion
46
+ node index.mjs
47
+
48
+ # For step-by-step debugging
49
+ node notion-converter.mjs # Notion → Markdown
50
+ node mdx-converter.mjs # Markdown → MDX
51
+ ```
52
+
53
+ ## 📁 Structure
54
+
55
+ ```
56
+ notion-importer/
57
+ ├── index.mjs # Complete Notion → MDX pipeline
58
+ ├── notion-converter.mjs # Notion → Markdown with notion-to-md v4
59
+ ├── mdx-converter.mjs # Markdown → MDX with Astro components
60
+ ├── post-processor.mjs # Markdown post-processing
61
+ ├── package.json # Dependencies and scripts
62
+ ├── env.example # Environment variables template
63
+ ├── input/ # Configuration
64
+ │ └── pages.json # Notion pages to convert
65
+ └── output/ # Results
66
+ ├── *.md # Intermediate Markdown
67
+ ├── *.mdx # Final MDX for Astro
68
+ └── media/ # Downloaded media files
69
+ ```
70
+
71
+ ## ✨ Key Features
72
+
73
+ ### 🎯 **Advanced Media Handling**
74
+ - **Local download**: Automatic download of all Notion media (images, files, PDFs)
75
+ - **Path transformation**: Smart path conversion for web accessibility
76
+ - **Image components**: Automatic conversion to Astro `Image` components with zoom/download
77
+ - **Media organization**: Structured media storage by page ID
78
+
79
+ ### 🧮 **Interactive Components**
80
+ - **Callouts → Notes**: Notion callouts converted to Astro `Note` components
81
+ - **Enhanced tables**: Tables wrapped in styled containers
82
+ - **Code blocks**: Enhanced with copy functionality
83
+ - **Automatic imports**: Smart component and image import generation
84
+
85
+ ### 🎨 **Smart Formatting**
86
+ - **Link fixing**: Notion internal links converted to relative links
87
+ - **Artifact cleanup**: Removal of Notion-specific formatting artifacts
88
+ - **Frontmatter generation**: Automatic YAML frontmatter from Notion properties
89
+ - **Astro compatibility**: Full compatibility with Astro MDX processing
90
+
91
+ ### 🔧 **Robust Pipeline**
92
+ - **Notion preprocessing**: Advanced page configuration and media strategy
93
+ - **Post-processing**: Markdown cleanup and optimization
94
+ - **MDX conversion**: Final transformation with Astro components
95
+ - **Auto-copy**: Automatic copying to Astro content directory
96
+
97
+ ## 📊 Example Workflow
98
+
99
+ ```bash
100
+ # 1. Configure your Notion pages
101
+ # Edit input/pages.json with your page IDs
102
+
103
+ # 2. Complete automatic conversion
104
+ NOTION_TOKEN=your_token node index.mjs --clean
105
+
106
+ # 3. Generated results
107
+ ls output/
108
+ # → getting-started.md (Intermediate Markdown)
109
+ # → getting-started.mdx (Final MDX for Astro)
110
+ # → media/ (downloaded images and files)
111
+ ```
112
+
113
+ ### 📋 Conversion Result
114
+
115
+ The pipeline generates MDX files optimized for Astro with:
116
+
117
+ ```mdx
118
+ ---
119
+ title: "Getting Started with Notion"
120
+ published: "2024-01-15"
121
+ tableOfContentsAutoCollapse: true
122
+ ---
123
+
124
+ import Image from '../components/Image.astro';
125
+ import Note from '../components/Note.astro';
126
+ import gettingStartedImage from './media/getting-started/image1.png';
127
+
128
+ ## Introduction
129
+
130
+ Here is some content with a callout:
131
+
132
+ <Note type="info" title="Important">
133
+ This is a converted Notion callout.
134
+ </Note>
135
+
136
+ And an image:
137
+
138
+ <Figure
139
+ src={gettingStartedImage}
140
+ alt="Getting started screenshot"
141
+ zoomable
142
+ downloadable
143
+ layout="fixed"
144
+ />
145
+ ```
146
+
147
+ ## ⚙️ Required Astro Configuration
148
+
149
+ To use the generated MDX files, ensure your Astro project has the required components:
150
+
151
+ ```astro
152
+ // src/components/Figure.astro
153
+ ---
154
+ export interface Props {
155
+ src: any;
156
+ alt?: string;
157
+ caption?: string;
158
+ zoomable?: boolean;
159
+ downloadable?: boolean;
160
+ layout?: string;
161
+ id?: string;
162
+ }
163
+
164
+ const { src, alt, caption, zoomable, downloadable, layout, id } = Astro.props;
165
+ ---
166
+
167
+ <figure {id} class="figure">
168
+ <img src={src} alt={alt} />
169
+ {caption && <figcaption>{caption}</figcaption>}
170
+ </figure>
171
+ ```
172
+
173
+ ## 🛠️ Prerequisites
174
+
175
+ - **Node.js** with ESM support
176
+ - **Notion Integration**: Set up an integration in your Notion workspace
177
+ - **Notion Token**: Copy the "Internal Integration Token"
178
+ - **Shared Pages**: Share the specific Notion page(s) with your integration
179
+ - **Astro** to use the generated MDX
180
+
181
+ ## 🎯 Technical Architecture
182
+
183
+ ### 4-Stage Pipeline
184
+
185
+ 1. **Notion Preprocessing** (`notion-converter.mjs`)
186
+ - Configuration loading from `pages.json`
187
+ - Notion API client initialization
188
+ - Media download strategy configuration
189
+
190
+ 2. **Notion-to-Markdown** (notion-to-md v4)
191
+ - Page conversion with `NotionConverter`
192
+ - Media downloading with `downloadMediaTo()`
193
+ - File export with `DefaultExporter`
194
+
195
+ 3. **Markdown Post-processing** (`post-processor.mjs`)
196
+ - Notion artifact cleanup
197
+ - Link fixing and optimization
198
+ - Table and code block enhancement
199
+
200
+ 4. **MDX Conversion** (`mdx-converter.mjs`)
201
+ - Component transformation (Figure, Note)
202
+ - Automatic import generation
203
+ - Frontmatter enhancement
204
+ - Astro compatibility optimization
205
+
206
+ ## 📊 Configuration Options
207
+
208
+ ### Pages Configuration (`input/pages.json`)
209
+
210
+ ```json
211
+ {
212
+ "pages": [
213
+ {
214
+ "id": "your-notion-page-id",
215
+ "title": "Page Title",
216
+ "slug": "page-slug"
217
+ }
218
+ ]
219
+ }
220
+ ```
221
+
222
+ ### Environment Variables
223
+
224
+ Copy `env.example` to `.env` and configure:
225
+
226
+ ```bash
227
+ cp env.example .env
228
+ # Edit .env with your actual Notion token
229
+ ```
230
+
231
+ Required variables:
232
+ ```bash
233
+ NOTION_TOKEN=secret_your_notion_integration_token_here
234
+ ```
235
+
236
+ ### Command Line Options
237
+
238
+ ```bash
239
+ # Full workflow
240
+ node index.mjs --clean --token=your_token
241
+
242
+ # Notion to Markdown only
243
+ node index.mjs --notion-only
244
+
245
+ # Markdown to MDX only
246
+ node index.mjs --mdx-only
247
+
248
+ # Custom paths
249
+ node index.mjs --input=my-pages.json --output=converted/
250
+ ```
251
+
252
+ ## 📊 Conversion Statistics
253
+
254
+ For a typical Notion page:
255
+ - **Media files** automatically downloaded and organized
256
+ - **Callouts** converted to interactive Note components
257
+ - **Images** transformed to Figure components with zoom/download
258
+ - **Tables** enhanced with proper styling containers
259
+ - **Code blocks** enhanced with copy functionality
260
+ - **Links** fixed for proper internal navigation
261
+
262
+ ## ✅ Project Status
263
+
264
+ ### 🎉 **Complete Features**
265
+ - ✅ **Notion → MDX Pipeline**: Full end-to-end functional conversion
266
+ - ✅ **Media Management**: Automatic download and path transformation
267
+ - ✅ **Component Integration**: Seamless Astro component integration
268
+ - ✅ **Smart Formatting**: Intelligent cleanup and optimization
269
+ - ✅ **Robustness**: Error handling and graceful degradation
270
+ - ✅ **Flexibility**: Modular pipeline with step-by-step options
271
+
272
+ ### 🚀 **Production Ready**
273
+ The toolkit is now **100% operational** for converting Notion pages to MDX/Astro with all advanced features (media handling, component integration, smart formatting).
274
+
275
+ ## 🔗 Integration with notion-to-md v4
276
+
277
+ This toolkit leverages the powerful [notion-to-md v4](https://notionconvert.com/docs/v4/guides/) library with:
278
+
279
+ - **Advanced Media Strategies**: Download, upload, and direct media handling
280
+ - **Custom Renderers**: Block transformers and annotation transformers
281
+ - **Exporter Plugins**: File, buffer, and stdout output options
282
+ - **Database Support**: Full database property and frontmatter transformation
283
+ - **Page References**: Smart internal link handling
284
+
285
+ ## 📚 Additional Resources
286
+
287
+ - [notion-to-md v4 Documentation](https://notionconvert.com/docs/v4/guides/)
288
+ - [Notion API Documentation](https://developers.notion.com/)
289
+ - [Astro MDX Documentation](https://docs.astro.build/en/guides/integrations-guide/mdx/)
290
+ - [Media Handling Strategies](https://notionconvert.com/blog/mastering-media-handling-in-notion-to-md-v4-download-upload-and-direct-strategies/)
291
+ - [Frontmatter Transformation](https://notionconvert.com/blog/how-to-convert-notion-properties-to-frontmatter-with-notion-to-md-v4/)
app/scripts/notion-importer/custom-code-renderer.mjs ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Custom Code Block Renderer for notion-to-md
5
+ * Fixes the issue where code blocks end with "text" instead of proper closing
6
+ */
7
+
8
+ export function createCustomCodeRenderer() {
9
+ return {
10
+ name: 'custom-code-renderer',
11
+ type: 'renderer',
12
+
13
+ /**
14
+ * Custom renderer for code blocks
15
+ * @param {Object} block - Notion code block
16
+ * @returns {string} - Properly formatted markdown code block
17
+ */
18
+ code: (block) => {
19
+ const { language, rich_text } = block.code;
20
+
21
+ // Extract the actual code content from rich_text
22
+ const codeContent = rich_text
23
+ .map(text => text.plain_text)
24
+ .join('');
25
+
26
+ // Determine the language (default to empty string if not specified)
27
+ const lang = language || '';
28
+
29
+ // Return properly formatted markdown code block
30
+ return `\`\`\`${lang}\n${codeContent}\n\`\`\``;
31
+ }
32
+ };
33
+ }
app/scripts/notion-importer/debug-properties.mjs ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+
3
+ import { config } from 'dotenv';
4
+ import { Client } from '@notionhq/client';
5
+
6
+ // Load environment variables from .env file
7
+ config();
8
+
9
+ const notion = new Client({
10
+ auth: process.env.NOTION_TOKEN,
11
+ });
12
+
13
+ async function debugPageProperties() {
14
+ const pageId = '27877f1c9c9d804d9c82f7b3905578ff';
15
+
16
+ try {
17
+ console.log('🔍 Debugging page properties...');
18
+ console.log(`📄 Page ID: ${pageId}`);
19
+
20
+ const page = await notion.pages.retrieve({ page_id: pageId });
21
+
22
+ console.log('\n📋 Available properties:');
23
+ console.log('========================');
24
+
25
+ for (const [key, value] of Object.entries(page.properties)) {
26
+ console.log(`\n🔹 ${key}:`);
27
+ console.log(` Type: ${value.type}`);
28
+
29
+ switch (value.type) {
30
+ case 'title':
31
+ console.log(` Value: "${value.title.map(t => t.plain_text).join('')}"`);
32
+ break;
33
+ case 'rich_text':
34
+ console.log(` Value: "${value.rich_text.map(t => t.plain_text).join('')}"`);
35
+ break;
36
+ case 'people':
37
+ console.log(` People: ${value.people.map(p => p.name || p.id).join(', ')}`);
38
+ break;
39
+ case 'select':
40
+ console.log(` Value: ${value.select?.name || 'null'}`);
41
+ break;
42
+ case 'multi_select':
43
+ console.log(` Values: [${value.multi_select.map(s => s.name).join(', ')}]`);
44
+ break;
45
+ case 'date':
46
+ console.log(` Value: ${value.date?.start || 'null'}`);
47
+ break;
48
+ case 'checkbox':
49
+ console.log(` Value: ${value.checkbox}`);
50
+ break;
51
+ case 'url':
52
+ console.log(` Value: ${value.url || 'null'}`);
53
+ break;
54
+ case 'email':
55
+ console.log(` Value: ${value.email || 'null'}`);
56
+ break;
57
+ case 'phone_number':
58
+ console.log(` Value: ${value.phone_number || 'null'}`);
59
+ break;
60
+ case 'number':
61
+ console.log(` Value: ${value.number || 'null'}`);
62
+ break;
63
+ case 'created_time':
64
+ console.log(` Value: ${value.created_time}`);
65
+ break;
66
+ case 'created_by':
67
+ console.log(` Value: ${value.created_by?.id || 'null'}`);
68
+ break;
69
+ case 'last_edited_time':
70
+ console.log(` Value: ${value.last_edited_time}`);
71
+ break;
72
+ case 'last_edited_by':
73
+ console.log(` Value: ${value.last_edited_by?.id || 'null'}`);
74
+ break;
75
+ default:
76
+ console.log(` Value: ${JSON.stringify(value, null, 2)}`);
77
+ }
78
+ }
79
+
80
+ console.log('\n✅ Properties debug completed!');
81
+
82
+ } catch (error) {
83
+ console.error('❌ Error:', error.message);
84
+ }
85
+ }
86
+
87
+ debugPageProperties();
app/scripts/notion-importer/env.example ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ NOTION_TOKEN=ntn_xxx
2
+ NOTION_PAGE_ID=xxx
app/scripts/notion-importer/index.mjs ADDED
@@ -0,0 +1,324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+
3
+ import { config } from 'dotenv';
4
+ import { join, dirname, basename } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, statSync } from 'fs';
7
+ import { convertNotionToMarkdown } from './notion-converter.mjs';
8
+ import { convertToMdx } from './mdx-converter.mjs';
9
+ import { Client } from '@notionhq/client';
10
+
11
+ // Load environment variables from .env file (but don't override existing ones)
12
+ config({ override: false });
13
+
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = dirname(__filename);
16
+
17
+ // Default configuration
18
+ const DEFAULT_INPUT = join(__dirname, 'input', 'pages.json');
19
+ const DEFAULT_OUTPUT = join(__dirname, 'output');
20
+ const ASTRO_CONTENT_PATH = join(__dirname, '..', '..', 'src', 'content', 'article.mdx');
21
+ const ASTRO_ASSETS_PATH = join(__dirname, '..', '..', 'src', 'content', 'assets', 'image');
22
+ const ASTRO_BIB_PATH = join(__dirname, '..', '..', 'src', 'content', 'bibliography.bib');
23
+
24
+ function parseArgs() {
25
+ const args = process.argv.slice(2);
26
+ const config = {
27
+ input: DEFAULT_INPUT,
28
+ output: DEFAULT_OUTPUT,
29
+ clean: false,
30
+ notionOnly: false,
31
+ mdxOnly: false,
32
+ token: process.env.NOTION_TOKEN,
33
+ pageId: process.env.NOTION_PAGE_ID
34
+ };
35
+
36
+ for (const arg of args) {
37
+ if (arg.startsWith('--input=')) {
38
+ config.input = arg.split('=')[1];
39
+ } else if (arg.startsWith('--output=')) {
40
+ config.output = arg.split('=')[1];
41
+ } else if (arg.startsWith('--token=')) {
42
+ config.token = arg.split('=')[1];
43
+ } else if (arg.startsWith('--page-id=')) {
44
+ config.pageId = arg.split('=')[1];
45
+ } else if (arg === '--clean') {
46
+ config.clean = true;
47
+ } else if (arg === '--notion-only') {
48
+ config.notionOnly = true;
49
+ } else if (arg === '--mdx-only') {
50
+ config.mdxOnly = true;
51
+ }
52
+ }
53
+
54
+ return config;
55
+ }
56
+
57
+ function showHelp() {
58
+ console.log(`
59
+ 🚀 Notion to MDX Toolkit
60
+
61
+ Usage:
62
+ node index.mjs [options]
63
+
64
+ Options:
65
+ --input=PATH Input pages configuration file (default: input/pages.json)
66
+ --output=PATH Output directory (default: output/)
67
+ --token=TOKEN Notion API token (or set NOTION_TOKEN env var)
68
+ --clean Clean output directory before processing
69
+ --notion-only Only convert Notion to Markdown (skip MDX conversion)
70
+ --mdx-only Only convert existing Markdown to MDX
71
+ --help, -h Show this help
72
+
73
+ Environment Variables:
74
+ NOTION_TOKEN Your Notion integration token
75
+
76
+ Examples:
77
+ # Full conversion workflow
78
+ NOTION_TOKEN=your_token node index.mjs --clean
79
+
80
+ # Only convert Notion pages to Markdown
81
+ node index.mjs --notion-only --token=your_token
82
+
83
+ # Only convert existing Markdown to MDX
84
+ node index.mjs --mdx-only
85
+
86
+ # Custom paths
87
+ node index.mjs --input=my-pages.json --output=converted/ --token=your_token
88
+
89
+ Configuration File Format (pages.json):
90
+ {
91
+ "pages": [
92
+ {
93
+ "id": "your-notion-page-id",
94
+ "title": "Page Title",
95
+ "slug": "page-slug"
96
+ }
97
+ ]
98
+ }
99
+
100
+ Workflow:
101
+ 1. Notion → Markdown (with media download)
102
+ 2. Markdown → MDX (with Astro components)
103
+ 3. Copy to Astro content directory
104
+ `);
105
+ }
106
+
107
+ function ensureDirectory(dir) {
108
+ if (!existsSync(dir)) {
109
+ mkdirSync(dir, { recursive: true });
110
+ }
111
+ }
112
+
113
+ async function cleanDirectory(dir) {
114
+ if (existsSync(dir)) {
115
+ const { execSync } = await import('child_process');
116
+ execSync(`rm -rf "${dir}"/*`, { stdio: 'inherit' });
117
+ }
118
+ }
119
+
120
+ function readPagesConfig(inputFile) {
121
+ try {
122
+ const content = readFileSync(inputFile, 'utf8');
123
+ return JSON.parse(content);
124
+ } catch (error) {
125
+ console.error(`❌ Error reading pages config: ${error.message}`);
126
+ return { pages: [] };
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Create a temporary pages.json from NOTION_PAGE_ID environment variable
132
+ * Extracts title and generates slug from the Notion page
133
+ */
134
+ async function createPagesConfigFromEnv(pageId, token, outputPath) {
135
+ try {
136
+ console.log('🔍 Fetching page info from Notion API...');
137
+ const notion = new Client({ auth: token });
138
+ const page = await notion.pages.retrieve({ page_id: pageId });
139
+
140
+ // Extract title
141
+ let title = 'Article';
142
+ if (page.properties.title && page.properties.title.title && page.properties.title.title.length > 0) {
143
+ title = page.properties.title.title[0].plain_text;
144
+ } else if (page.properties.Name && page.properties.Name.title && page.properties.Name.title.length > 0) {
145
+ title = page.properties.Name.title[0].plain_text;
146
+ }
147
+
148
+ // Generate slug from title
149
+ const slug = title
150
+ .toLowerCase()
151
+ .replace(/[^\w\s-]/g, '')
152
+ .replace(/\s+/g, '-')
153
+ .replace(/-+/g, '-')
154
+ .trim();
155
+
156
+ console.log(` �� Found page: "${title}" (slug: ${slug})`);
157
+
158
+ // Create pages config
159
+ const pagesConfig = {
160
+ pages: [{
161
+ id: pageId,
162
+ title: title,
163
+ slug: slug
164
+ }]
165
+ };
166
+
167
+ // Write to temporary file
168
+ writeFileSync(outputPath, JSON.stringify(pagesConfig, null, 4));
169
+ console.log(` ✅ Created temporary pages config`);
170
+
171
+ return pagesConfig;
172
+ } catch (error) {
173
+ console.error(`❌ Error fetching page from Notion: ${error.message}`);
174
+ throw error;
175
+ }
176
+ }
177
+
178
+ function copyToAstroContent(outputDir) {
179
+ console.log('📋 Copying MDX files to Astro content directory...');
180
+
181
+ try {
182
+ // Ensure Astro directories exist
183
+ mkdirSync(dirname(ASTRO_CONTENT_PATH), { recursive: true });
184
+ mkdirSync(ASTRO_ASSETS_PATH, { recursive: true });
185
+
186
+ // Copy MDX file
187
+ const files = readdirSync(outputDir);
188
+ const mdxFiles = files.filter(file => file.endsWith('.mdx'));
189
+ if (mdxFiles.length > 0) {
190
+ const mdxFile = join(outputDir, mdxFiles[0]); // Take the first MDX file
191
+ // Read and write instead of copy to avoid EPERM issues
192
+ const mdxContent = readFileSync(mdxFile, 'utf8');
193
+ writeFileSync(ASTRO_CONTENT_PATH, mdxContent);
194
+ console.log(` ✅ Copied MDX to ${ASTRO_CONTENT_PATH}`);
195
+ }
196
+
197
+ // Copy images
198
+ const mediaDir = join(outputDir, 'media');
199
+ if (existsSync(mediaDir)) {
200
+ const imageExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.svg'];
201
+ let imageCount = 0;
202
+
203
+ function copyImagesRecursively(dir) {
204
+ const files = readdirSync(dir);
205
+ for (const file of files) {
206
+ const filePath = join(dir, file);
207
+ const stat = statSync(filePath);
208
+
209
+ if (stat.isDirectory()) {
210
+ copyImagesRecursively(filePath);
211
+ } else if (imageExtensions.some(ext => file.toLowerCase().endsWith(ext))) {
212
+ const filename = basename(filePath);
213
+ const destPath = join(ASTRO_ASSETS_PATH, filename);
214
+ copyFileSync(filePath, destPath);
215
+ imageCount++;
216
+ }
217
+ }
218
+ }
219
+
220
+ copyImagesRecursively(mediaDir);
221
+ console.log(` ✅ Copied ${imageCount} image(s) to ${ASTRO_ASSETS_PATH}`);
222
+
223
+ // Update image paths in MDX file
224
+ const mdxContent = readFileSync(ASTRO_CONTENT_PATH, 'utf8');
225
+ let updatedContent = mdxContent.replace(/\.\/media\//g, './assets/image/');
226
+ // Remove the subdirectory from image paths since we copy images directly to assets/image/
227
+ updatedContent = updatedContent.replace(/\.\/assets\/image\/[^\/]+\//g, './assets/image/');
228
+ writeFileSync(ASTRO_CONTENT_PATH, updatedContent);
229
+ console.log(` ✅ Updated image paths in MDX file`);
230
+ }
231
+
232
+ // Create empty bibliography.bib
233
+ writeFileSync(ASTRO_BIB_PATH, '');
234
+ console.log(` ✅ Created empty bibliography at ${ASTRO_BIB_PATH}`);
235
+
236
+ } catch (error) {
237
+ console.warn(` ⚠️ Failed to copy to Astro: ${error.message}`);
238
+ }
239
+ }
240
+
241
+
242
+ async function main() {
243
+ const args = process.argv.slice(2);
244
+
245
+ if (args.includes('--help') || args.includes('-h')) {
246
+ showHelp();
247
+ process.exit(0);
248
+ }
249
+
250
+ const config = parseArgs();
251
+
252
+ console.log('🚀 Notion to MDX Toolkit');
253
+ console.log('========================');
254
+
255
+ try {
256
+ // Prepare input config file
257
+ let inputConfigFile = config.input;
258
+ let pageIdFromEnv = null;
259
+
260
+ // If NOTION_PAGE_ID is provided via env var, create temporary pages.json
261
+ if (config.pageId && config.token) {
262
+ console.log('✨ Using NOTION_PAGE_ID from environment variable');
263
+ const tempConfigPath = join(config.output, '.temp-pages.json');
264
+ ensureDirectory(config.output);
265
+ await createPagesConfigFromEnv(config.pageId, config.token, tempConfigPath);
266
+ inputConfigFile = tempConfigPath;
267
+ pageIdFromEnv = config.pageId;
268
+ } else if (!existsSync(config.input)) {
269
+ console.error(`❌ No NOTION_PAGE_ID environment variable and no pages.json found at: ${config.input}`);
270
+ console.log('💡 Either set NOTION_PAGE_ID env var or create input/pages.json');
271
+ process.exit(1);
272
+ }
273
+
274
+ if (config.clean) {
275
+ console.log('🧹 Cleaning output directory...');
276
+ await cleanDirectory(config.output);
277
+ }
278
+
279
+ if (config.mdxOnly) {
280
+ // Only convert existing Markdown to MDX
281
+ console.log('📝 MDX conversion only mode');
282
+ await convertToMdx(config.output, config.output);
283
+ copyToAstroContent(config.output);
284
+
285
+ } else if (config.notionOnly) {
286
+ // Only convert Notion to Markdown
287
+ console.log('📄 Notion conversion only mode');
288
+ await convertNotionToMarkdown(inputConfigFile, config.output, config.token);
289
+
290
+ } else {
291
+ // Full workflow
292
+ console.log('🔄 Full conversion workflow');
293
+
294
+ // Step 1: Convert Notion to Markdown
295
+ console.log('\n📄 Step 1: Converting Notion pages to Markdown...');
296
+ await convertNotionToMarkdown(inputConfigFile, config.output, config.token);
297
+
298
+ // Step 2: Convert Markdown to MDX with Notion metadata
299
+ console.log('\n📝 Step 2: Converting Markdown to MDX...');
300
+ const pagesConfig = readPagesConfig(inputConfigFile);
301
+ const firstPage = pagesConfig.pages && pagesConfig.pages.length > 0 ? pagesConfig.pages[0] : null;
302
+ const pageId = pageIdFromEnv || (firstPage ? firstPage.id : null);
303
+ await convertToMdx(config.output, config.output, pageId, config.token);
304
+
305
+ // Step 3: Copy to Astro content directory
306
+ console.log('\n📋 Step 3: Copying to Astro content directory...');
307
+ copyToAstroContent(config.output);
308
+ }
309
+
310
+ console.log('\n🎉 Conversion completed successfully!');
311
+
312
+ } catch (error) {
313
+ console.error('❌ Error:', error.message);
314
+ process.exit(1);
315
+ }
316
+ }
317
+
318
+ // Export functions for use as module
319
+ export { convertNotionToMarkdown, convertToMdx };
320
+
321
+ // Run CLI if called directly
322
+ if (import.meta.url === `file://${process.argv[1]}`) {
323
+ main();
324
+ }
app/scripts/notion-importer/input/pages.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "pages": [
3
+ {
4
+ "id": "27877f1c9c9d804d9c82f7b3905578ff",
5
+ "title": "The Smol Training Guide",
6
+ "slug": "smol-training-guide"
7
+ }
8
+ ]
9
+ }
app/scripts/notion-importer/mdx-converter.mjs ADDED
@@ -0,0 +1,551 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync } from 'fs';
4
+ import { join, dirname, basename, extname } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import matter from 'gray-matter';
7
+ import { extractAndGenerateNotionFrontmatter } from './notion-metadata-extractor.mjs';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
11
+
12
+ // Configuration
13
+ const DEFAULT_INPUT = join(__dirname, 'output');
14
+ const DEFAULT_OUTPUT = join(__dirname, 'output');
15
+
16
+ function parseArgs() {
17
+ const args = process.argv.slice(2);
18
+ const config = {
19
+ input: DEFAULT_INPUT,
20
+ output: DEFAULT_OUTPUT,
21
+ };
22
+
23
+ for (const arg of args) {
24
+ if (arg.startsWith('--input=')) {
25
+ config.input = arg.substring('--input='.length);
26
+ } else if (arg.startsWith('--output=')) {
27
+ config.output = arg.substring('--output='.length);
28
+ } else if (arg === '--help' || arg === '-h') {
29
+ console.log(`
30
+ 📝 Notion Markdown to MDX Converter
31
+
32
+ Usage:
33
+ node mdx-converter.mjs [options]
34
+
35
+ Options:
36
+ --input=PATH Input directory or file (default: ${DEFAULT_INPUT})
37
+ --output=PATH Output directory (default: ${DEFAULT_OUTPUT})
38
+ --help, -h Show this help
39
+
40
+ Examples:
41
+ # Convert all markdown files in output directory
42
+ node mdx-converter.mjs
43
+
44
+ # Convert specific file
45
+ node mdx-converter.mjs --input=article.md --output=converted/
46
+
47
+ # Convert directory
48
+ node mdx-converter.mjs --input=markdown-files/ --output=mdx-files/
49
+ `);
50
+ process.exit(0);
51
+ } else if (!config.input) {
52
+ config.input = arg;
53
+ } else if (!config.output) {
54
+ config.output = arg;
55
+ }
56
+ }
57
+ return config;
58
+ }
59
+
60
+ /**
61
+ * Track which Astro components are used during transformations
62
+ */
63
+ const usedComponents = new Set();
64
+
65
+ /**
66
+ * Track individual image imports needed
67
+ */
68
+ const imageImports = new Map(); // src -> varName
69
+
70
+ /**
71
+ * Generate a variable name from image path
72
+ * @param {string} src - Image source path
73
+ * @returns {string} - Valid variable name
74
+ */
75
+ function generateImageVarName(src) {
76
+ // Extract filename without extension and make it a valid JS variable
77
+ const filename = src.split('/').pop().replace(/\.[^.]+$/, '');
78
+ return filename.replace(/[^a-zA-Z0-9]/g, '_').replace(/^[0-9]/, 'img_$&');
79
+ }
80
+
81
+ /**
82
+ * Add required component imports to the frontmatter
83
+ * @param {string} content - MDX content
84
+ * @returns {string} - Content with component imports
85
+ */
86
+ function addComponentImports(content) {
87
+ console.log(' 📦 Adding component and image imports...');
88
+
89
+ let imports = [];
90
+
91
+ // Add component imports
92
+ if (usedComponents.size > 0) {
93
+ const componentImports = Array.from(usedComponents)
94
+ .map(component => `import ${component} from '../components/${component}.astro';`);
95
+ imports.push(...componentImports);
96
+ console.log(` ✅ Importing components: ${Array.from(usedComponents).join(', ')}`);
97
+ }
98
+
99
+ // Add image imports
100
+ if (imageImports.size > 0) {
101
+ const imageImportStatements = Array.from(imageImports.entries())
102
+ .map(([src, varName]) => `import ${varName} from '${src}';`);
103
+ imports.push(...imageImportStatements);
104
+ console.log(` ✅ Importing ${imageImports.size} image(s)`);
105
+ }
106
+
107
+ if (imports.length === 0) {
108
+ console.log(' ℹ️ No imports needed');
109
+ return content;
110
+ }
111
+
112
+ const importBlock = imports.join('\n');
113
+
114
+ // Insert imports after frontmatter
115
+ const frontmatterEnd = content.indexOf('---', 3) + 3;
116
+ if (frontmatterEnd > 2) {
117
+ return content.slice(0, frontmatterEnd) + '\n\n' + importBlock + '\n' + content.slice(frontmatterEnd);
118
+ } else {
119
+ // No frontmatter, add at beginning
120
+ return importBlock + '\n\n' + content;
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Transform Notion images to Image components
126
+ * @param {string} content - MDX content
127
+ * @returns {string} - Content with Image components
128
+ */
129
+ function transformImages(content) {
130
+ console.log(' 🖼️ Transforming images to Image components...');
131
+
132
+ let hasImages = false;
133
+
134
+ // Helper function to clean source paths
135
+ const cleanSrcPath = (src) => {
136
+ // Convert Notion media paths to relative paths
137
+ return src.replace(/^\/media\//, './media/')
138
+ .replace(/^\.\/media\//, './media/');
139
+ };
140
+
141
+ // Helper to clean caption text
142
+ const cleanCaption = (caption) => {
143
+ return caption
144
+ .replace(/<[^>]*>/g, '') // Remove HTML tags
145
+ .replace(/\n/g, ' ') // Replace newlines with spaces
146
+ .replace(/\r/g, ' ') // Replace carriage returns with spaces
147
+ .replace(/\s+/g, ' ') // Replace multiple spaces with single space
148
+ .replace(/'/g, "\\'") // Escape quotes
149
+ .trim(); // Trim whitespace
150
+ };
151
+
152
+ // Helper to clean alt text
153
+ const cleanAltText = (alt, maxLength = 100) => {
154
+ const cleaned = alt
155
+ .replace(/<[^>]*>/g, '') // Remove HTML tags
156
+ .replace(/\n/g, ' ') // Replace newlines with spaces
157
+ .replace(/\r/g, ' ') // Replace carriage returns with spaces
158
+ .replace(/\s+/g, ' ') // Replace multiple spaces with single space
159
+ .trim(); // Trim whitespace
160
+
161
+ return cleaned.length > maxLength
162
+ ? cleaned.substring(0, maxLength) + '...'
163
+ : cleaned;
164
+ };
165
+
166
+ // Create Image component with import
167
+ const createImageComponent = (src, alt = '', caption = '') => {
168
+ const cleanSrc = cleanSrcPath(src);
169
+
170
+ // Skip PDF URLs and external URLs - they should remain as links only
171
+ if (cleanSrc.includes('.pdf') || cleanSrc.includes('arxiv.org/pdf') ||
172
+ (cleanSrc.startsWith('http') && !cleanSrc.includes('/media/'))) {
173
+ console.log(` ⚠️ Skipping external/PDF URL: ${cleanSrc}`);
174
+ // Return the original markdown image syntax for external URLs
175
+ return `![${alt}](${src})`;
176
+ }
177
+
178
+ const varName = generateImageVarName(cleanSrc);
179
+ imageImports.set(cleanSrc, varName);
180
+ usedComponents.add('Image');
181
+
182
+ const props = [];
183
+ props.push(`src={${varName}}`);
184
+ props.push('zoomable');
185
+ props.push('downloadable');
186
+ props.push('layout="fixed"');
187
+ if (alt) props.push(`alt="${alt}"`);
188
+ if (caption) props.push(`caption={'${caption}'}`);
189
+
190
+ return `<Image\n ${props.join('\n ')}\n/>`;
191
+ };
192
+
193
+ // Transform markdown images: ![alt](src)
194
+ content = content.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (match, alt, src) => {
195
+ const cleanSrc = cleanSrcPath(src);
196
+ const cleanAlt = cleanAltText(alt || 'Image');
197
+ hasImages = true;
198
+
199
+ return createImageComponent(cleanSrc, cleanAlt);
200
+ });
201
+
202
+ // Transform images with captions (Notion sometimes adds captions as separate text)
203
+ content = content.replace(/!\[([^\]]*)\]\(([^)]+)\)\s*\n\s*([^\n]+)/g, (match, alt, src, caption) => {
204
+ const cleanSrc = cleanSrcPath(src);
205
+ const cleanAlt = cleanAltText(alt || 'Image');
206
+ const cleanCap = cleanCaption(caption);
207
+ hasImages = true;
208
+
209
+ return createImageComponent(cleanSrc, cleanAlt, cleanCap);
210
+ });
211
+
212
+ if (hasImages) {
213
+ console.log(' ✅ Image components with imports will be created');
214
+ }
215
+
216
+ return content;
217
+ }
218
+
219
+ /**
220
+ * Transform Notion callouts to Note components
221
+ * @param {string} content - MDX content
222
+ * @returns {string} - Content with Note components
223
+ */
224
+ function transformCallouts(content) {
225
+ console.log(' 📝 Transforming callouts to Note components...');
226
+
227
+ let transformedCount = 0;
228
+
229
+ // Transform blockquotes that look like Notion callouts
230
+ content = content.replace(/^> \*\*([^*]+)\*\*\s*\n> (.+?)(?=\n> \*\*|\n\n|\n$)/gms, (match, title, content) => {
231
+ transformedCount++;
232
+ usedComponents.add('Note');
233
+
234
+ const cleanContent = content
235
+ .replace(/^> /gm, '') // Remove blockquote markers
236
+ .replace(/\n+/g, '\n') // Normalize newlines
237
+ .trim();
238
+
239
+ return `<Note type="${title.toLowerCase()}" title="${title}">\n${cleanContent}\n</Note>\n\n`;
240
+ });
241
+
242
+ if (transformedCount > 0) {
243
+ console.log(` ✅ Transformed ${transformedCount} callout(s) to Note components`);
244
+ }
245
+
246
+ return content;
247
+ }
248
+
249
+ /**
250
+ * Transform Notion databases/tables to enhanced table components
251
+ * @param {string} content - MDX content
252
+ * @returns {string} - Content with enhanced tables
253
+ */
254
+ function transformTables(content) {
255
+ console.log(' 📊 Enhancing tables...');
256
+
257
+ let enhancedCount = 0;
258
+
259
+ // Wrap tables in a container for better styling
260
+ content = content.replace(/^(\|[^|\n]+\|[\s\S]*?)(?=\n\n|\n$)/gm, (match) => {
261
+ if (match.includes('|') && match.split('\n').length > 2) {
262
+ enhancedCount++;
263
+ return `<div class="table-container">\n\n${match}\n\n</div>`;
264
+ }
265
+ return match;
266
+ });
267
+
268
+ if (enhancedCount > 0) {
269
+ console.log(` ✅ Enhanced ${enhancedCount} table(s)`);
270
+ }
271
+
272
+ return content;
273
+ }
274
+
275
+ /**
276
+ * Transform Notion code blocks to enhanced code components
277
+ * @param {string} content - MDX content
278
+ * @returns {string} - Content with enhanced code blocks
279
+ */
280
+ function transformCodeBlocks(content) {
281
+ console.log(' 💻 Enhancing code blocks...');
282
+
283
+ let enhancedCount = 0;
284
+
285
+ // Add copy functionality to code blocks
286
+ content = content.replace(/^```(\w+)\n([\s\S]*?)\n```$/gm, (match, lang, code) => {
287
+ enhancedCount++;
288
+ return `\`\`\`${lang} copy\n${code}\n\`\`\``;
289
+ });
290
+
291
+ if (enhancedCount > 0) {
292
+ console.log(` ✅ Enhanced ${enhancedCount} code block(s)`);
293
+ }
294
+
295
+ return content;
296
+ }
297
+
298
+ /**
299
+ * Fix Notion-specific formatting issues
300
+ * @param {string} content - MDX content
301
+ * @returns {string} - Content with fixed formatting
302
+ */
303
+ function fixNotionFormatting(content) {
304
+ console.log(' 🔧 Fixing Notion formatting issues...');
305
+
306
+ let fixedCount = 0;
307
+
308
+ // Fix Notion's toggle lists that don't convert well
309
+ content = content.replace(/^(\s*)•\s*(.+)$/gm, (match, indent, text) => {
310
+ fixedCount++;
311
+ return `${indent}- ${text}`;
312
+ });
313
+
314
+ // Fix Notion's numbered lists that might have issues
315
+ content = content.replace(/^(\s*)\d+\.\s*(.+)$/gm, (match, indent, text) => {
316
+ // Only fix if it's not already properly formatted
317
+ if (!text.includes('\n') || text.split('\n').length === 1) {
318
+ return match; // Keep as is
319
+ }
320
+ fixedCount++;
321
+ return `${indent}1. ${text}`;
322
+ });
323
+
324
+ // Fix Notion's bold/italic combinations
325
+ content = content.replace(/\*\*([^*]+)\*\*([^*]+)\*\*([^*]+)\*\*/g, (match, part1, part2, part3) => {
326
+ fixedCount++;
327
+ return `**${part1}${part2}${part3}**`;
328
+ });
329
+
330
+ if (fixedCount > 0) {
331
+ console.log(` ✅ Fixed ${fixedCount} formatting issue(s)`);
332
+ }
333
+
334
+ return content;
335
+ }
336
+
337
+ /**
338
+ * Ensure proper frontmatter for MDX with Notion metadata
339
+ * @param {string} content - MDX content
340
+ * @param {string} pageId - Notion page ID (optional)
341
+ * @param {string} notionToken - Notion API token (optional)
342
+ * @returns {string} - Content with proper frontmatter
343
+ */
344
+ async function ensureFrontmatter(content, pageId = null, notionToken = null) {
345
+ console.log(' 📄 Ensuring proper frontmatter...');
346
+
347
+ if (!content.startsWith('---')) {
348
+ let frontmatter;
349
+
350
+ if (pageId && notionToken) {
351
+ try {
352
+ console.log(' 🔍 Extracting Notion metadata...');
353
+ frontmatter = await extractAndGenerateNotionFrontmatter(pageId, notionToken);
354
+ console.log(' ✅ Generated rich frontmatter from Notion');
355
+ } catch (error) {
356
+ console.log(' ⚠️ Failed to extract Notion metadata, using basic frontmatter');
357
+ frontmatter = generateBasicFrontmatter();
358
+ }
359
+ } else {
360
+ frontmatter = generateBasicFrontmatter();
361
+ console.log(' ✅ Generated basic frontmatter');
362
+ }
363
+
364
+ return frontmatter + content;
365
+ }
366
+
367
+ // Parse existing frontmatter and enhance it
368
+ try {
369
+ const { data, content: body } = matter(content);
370
+
371
+ // If we have Notion metadata available, try to enhance the frontmatter
372
+ if (pageId && notionToken && (!data.notion_id || data.notion_id !== pageId)) {
373
+ try {
374
+ console.log(' 🔍 Enhancing frontmatter with Notion metadata...');
375
+ const notionFrontmatter = await extractAndGenerateNotionFrontmatter(pageId, notionToken);
376
+ const { data: notionData } = matter(notionFrontmatter);
377
+
378
+ // Merge Notion metadata with existing frontmatter
379
+ const enhancedData = { ...data, ...notionData };
380
+ const enhancedContent = matter.stringify(body, enhancedData);
381
+ console.log(' ✅ Enhanced frontmatter with Notion metadata');
382
+ return enhancedContent;
383
+ } catch (error) {
384
+ console.log(' ⚠️ Could not enhance with Notion metadata, keeping existing');
385
+ }
386
+ }
387
+
388
+ // Ensure required fields
389
+ if (!data.title) data.title = 'Notion Article';
390
+ if (!data.published) data.published = new Date().toISOString().split('T')[0];
391
+ if (!data.tableOfContentsAutoCollapse) data.tableOfContentsAutoCollapse = true;
392
+
393
+ const enhancedContent = matter.stringify(body, data);
394
+ console.log(' ✅ Enhanced existing frontmatter');
395
+ return enhancedContent;
396
+ } catch (error) {
397
+ console.log(' ⚠️ Could not parse frontmatter, keeping as is');
398
+ return content;
399
+ }
400
+ }
401
+
402
+ /**
403
+ * Generate basic frontmatter
404
+ * @returns {string} - Basic frontmatter
405
+ */
406
+ function generateBasicFrontmatter() {
407
+ const currentDate = new Date().toLocaleDateString('en-US', {
408
+ year: 'numeric',
409
+ month: 'short',
410
+ day: '2-digit'
411
+ });
412
+ return `---
413
+ title: "Notion Article"
414
+ published: "${currentDate}"
415
+ tableOfContentsAutoCollapse: true
416
+ ---
417
+
418
+ `;
419
+ }
420
+
421
+ /**
422
+ * Main MDX processing function that applies all transformations
423
+ * @param {string} content - Raw Markdown content
424
+ * @param {string} pageId - Notion page ID (optional)
425
+ * @param {string} notionToken - Notion API token (optional)
426
+ * @returns {string} - Processed MDX content compatible with Astro
427
+ */
428
+ async function processMdxContent(content, pageId = null, notionToken = null) {
429
+ console.log('🔧 Processing for Astro MDX compatibility...');
430
+
431
+ // Clear previous tracking
432
+ usedComponents.clear();
433
+ imageImports.clear();
434
+
435
+ let processedContent = content;
436
+
437
+ // Apply each transformation step sequentially
438
+ processedContent = await ensureFrontmatter(processedContent, pageId, notionToken);
439
+ processedContent = fixNotionFormatting(processedContent);
440
+ processedContent = transformCallouts(processedContent);
441
+ processedContent = transformImages(processedContent);
442
+ processedContent = transformTables(processedContent);
443
+ processedContent = transformCodeBlocks(processedContent);
444
+
445
+ // Add component imports at the end
446
+ processedContent = addComponentImports(processedContent);
447
+
448
+ return processedContent;
449
+ }
450
+
451
+ /**
452
+ * Convert a single markdown file to MDX
453
+ * @param {string} inputFile - Input markdown file
454
+ * @param {string} outputDir - Output directory
455
+ * @param {string} pageId - Notion page ID (optional)
456
+ * @param {string} notionToken - Notion API token (optional)
457
+ */
458
+ async function convertFileToMdx(inputFile, outputDir, pageId = null, notionToken = null) {
459
+ const filename = basename(inputFile, '.md');
460
+ const outputFile = join(outputDir, `${filename}.mdx`);
461
+
462
+ console.log(`📝 Converting: ${basename(inputFile)} → ${basename(outputFile)}`);
463
+
464
+ try {
465
+ const markdownContent = readFileSync(inputFile, 'utf8');
466
+ const mdxContent = await processMdxContent(markdownContent, pageId, notionToken);
467
+ writeFileSync(outputFile, mdxContent);
468
+
469
+ console.log(` ✅ Converted: ${outputFile}`);
470
+
471
+ // Show file size
472
+ const inputSize = Math.round(markdownContent.length / 1024);
473
+ const outputSize = Math.round(mdxContent.length / 1024);
474
+ console.log(` 📊 Input: ${inputSize}KB → Output: ${outputSize}KB`);
475
+
476
+ } catch (error) {
477
+ console.error(` ❌ Failed to convert ${inputFile}: ${error.message}`);
478
+ }
479
+ }
480
+
481
+ /**
482
+ * Convert all markdown files in a directory to MDX
483
+ * @param {string} inputPath - Input path (file or directory)
484
+ * @param {string} outputDir - Output directory
485
+ * @param {string} pageId - Notion page ID (optional)
486
+ * @param {string} notionToken - Notion API token (optional)
487
+ */
488
+ async function convertToMdx(inputPath, outputDir, pageId = null, notionToken = null) {
489
+ console.log('📝 Notion Markdown to Astro MDX Converter');
490
+ console.log(`📁 Input: ${inputPath}`);
491
+ console.log(`📁 Output: ${outputDir}`);
492
+
493
+ // Check if input exists
494
+ if (!existsSync(inputPath)) {
495
+ console.error(`❌ Input not found: ${inputPath}`);
496
+ process.exit(1);
497
+ }
498
+
499
+ try {
500
+ // Ensure output directory exists
501
+ if (!existsSync(outputDir)) {
502
+ mkdirSync(outputDir, { recursive: true });
503
+ }
504
+
505
+ let filesToConvert = [];
506
+
507
+ if (statSync(inputPath).isDirectory()) {
508
+ // Convert all .md files in directory
509
+ const files = readdirSync(inputPath);
510
+ filesToConvert = files
511
+ .filter(file => file.endsWith('.md'))
512
+ .map(file => join(inputPath, file));
513
+ } else if (inputPath.endsWith('.md')) {
514
+ // Convert single file
515
+ filesToConvert = [inputPath];
516
+ } else {
517
+ console.error('❌ Input must be a .md file or directory containing .md files');
518
+ process.exit(1);
519
+ }
520
+
521
+ if (filesToConvert.length === 0) {
522
+ console.log('ℹ️ No .md files found to convert');
523
+ return;
524
+ }
525
+
526
+ console.log(`🔄 Found ${filesToConvert.length} file(s) to convert`);
527
+
528
+ // Convert each file
529
+ for (const file of filesToConvert) {
530
+ await convertFileToMdx(file, outputDir, pageId, notionToken);
531
+ }
532
+
533
+ console.log(`✅ Conversion completed! ${filesToConvert.length} file(s) processed`);
534
+
535
+ } catch (error) {
536
+ console.error('❌ Conversion failed:', error.message);
537
+ process.exit(1);
538
+ }
539
+ }
540
+
541
+ export { convertToMdx };
542
+
543
+ function main() {
544
+ const config = parseArgs();
545
+ convertToMdx(config.input, config.output);
546
+ console.log('🎉 MDX conversion completed!');
547
+ }
548
+
549
+ if (import.meta.url === `file://${process.argv[1]}`) {
550
+ main();
551
+ }
app/scripts/notion-importer/notion-converter.mjs ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+
3
+ import { config } from 'dotenv';
4
+ import { Client } from '@notionhq/client';
5
+ import { NotionConverter } from 'notion-to-md';
6
+ import { DefaultExporter } from 'notion-to-md/plugins/exporter';
7
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
8
+ import { join, dirname, basename } from 'path';
9
+ import { fileURLToPath } from 'url';
10
+ import { postProcessMarkdown } from './post-processor.mjs';
11
+ import { createCustomCodeRenderer } from './custom-code-renderer.mjs';
12
+
13
+ // Load environment variables from .env file (but don't override existing ones)
14
+ config({ override: false });
15
+
16
+ const __filename = fileURLToPath(import.meta.url);
17
+ const __dirname = dirname(__filename);
18
+
19
+ // Configuration
20
+ const DEFAULT_INPUT = join(__dirname, 'input', 'pages.json');
21
+ const DEFAULT_OUTPUT = join(__dirname, 'output');
22
+
23
+ function parseArgs() {
24
+ const args = process.argv.slice(2);
25
+ const config = {
26
+ input: DEFAULT_INPUT,
27
+ output: DEFAULT_OUTPUT,
28
+ clean: false,
29
+ token: process.env.NOTION_TOKEN
30
+ };
31
+
32
+ for (const arg of args) {
33
+ if (arg.startsWith('--input=')) {
34
+ config.input = arg.split('=')[1];
35
+ } else if (arg.startsWith('--output=')) {
36
+ config.output = arg.split('=')[1];
37
+ } else if (arg.startsWith('--token=')) {
38
+ config.token = arg.split('=')[1];
39
+ } else if (arg === '--clean') {
40
+ config.clean = true;
41
+ }
42
+ }
43
+
44
+ return config;
45
+ }
46
+
47
+ function ensureDirectory(dir) {
48
+ if (!existsSync(dir)) {
49
+ mkdirSync(dir, { recursive: true });
50
+ }
51
+ }
52
+
53
+ function loadPagesConfig(configFile) {
54
+ if (!existsSync(configFile)) {
55
+ console.error(`❌ Configuration file not found: ${configFile}`);
56
+ console.log('📝 Create a pages.json file with your Notion page IDs:');
57
+ console.log(`
58
+ {
59
+ "pages": [
60
+ {
61
+ "id": "your-notion-page-id-1",
62
+ "title": "Page Title 1",
63
+ "slug": "page-1"
64
+ },
65
+ {
66
+ "id": "your-notion-page-id-2",
67
+ "title": "Page Title 2",
68
+ "slug": "page-2"
69
+ }
70
+ ]
71
+ }
72
+ `);
73
+ process.exit(1);
74
+ }
75
+
76
+ try {
77
+ const config = JSON.parse(readFileSync(configFile, 'utf8'));
78
+ return config.pages || [];
79
+ } catch (error) {
80
+ console.error(`❌ Error reading configuration: ${error.message}`);
81
+ process.exit(1);
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Convert a single Notion page to Markdown with advanced media handling
87
+ * @param {Object} notion - Notion client
88
+ * @param {string} pageId - Notion page ID
89
+ * @param {string} outputDir - Output directory
90
+ * @param {string} pageTitle - Page title for file naming
91
+ * @returns {Promise<string>} - Path to generated markdown file
92
+ */
93
+ async function convertNotionPage(notion, pageId, outputDir, pageTitle) {
94
+ console.log(`📄 Converting Notion page: ${pageTitle} (${pageId})`);
95
+
96
+ try {
97
+ // Create media directory for this page
98
+ const mediaDir = join(outputDir, 'media', pageId);
99
+ ensureDirectory(mediaDir);
100
+
101
+ // Configure the DefaultExporter to save to a file
102
+ const outputFile = join(outputDir, `${pageTitle}.md`);
103
+ const exporter = new DefaultExporter({
104
+ outputType: 'file',
105
+ outputPath: outputFile,
106
+ });
107
+
108
+ // Create the converter with media downloading strategy
109
+ const n2m = new NotionConverter(notion)
110
+ .withExporter(exporter)
111
+ // Download media to local directory with path transformation
112
+ .downloadMediaTo({
113
+ outputDir: mediaDir,
114
+ // Transform paths to be web-accessible
115
+ transformPath: (localPath) => `/media/${pageId}/${basename(localPath)}`,
116
+ });
117
+
118
+ // Convert the page
119
+ const result = await n2m.convert(pageId);
120
+
121
+ console.log(` ✅ Converted to: ${outputFile}`);
122
+ console.log(` 📊 Content length: ${result.content.length} characters`);
123
+ console.log(` 🖼️ Media saved to: ${mediaDir}`);
124
+
125
+ return outputFile;
126
+
127
+ } catch (error) {
128
+ console.error(` ❌ Failed to convert page ${pageId}: ${error.message}`);
129
+ throw error;
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Process Notion pages with advanced configuration
135
+ * @param {string} inputFile - Path to pages configuration
136
+ * @param {string} outputDir - Output directory
137
+ * @param {string} notionToken - Notion API token
138
+ */
139
+ export async function convertNotionToMarkdown(inputFile, outputDir, notionToken) {
140
+ console.log('🚀 Notion to Markdown Converter');
141
+ console.log(`📁 Input: ${inputFile}`);
142
+ console.log(`📁 Output: ${outputDir}`);
143
+
144
+ // Validate Notion token
145
+ if (!notionToken) {
146
+ console.error('❌ NOTION_TOKEN not found. Please set it as environment variable or use --token=YOUR_TOKEN');
147
+ process.exit(1);
148
+ }
149
+
150
+ // Ensure output directory exists
151
+ ensureDirectory(outputDir);
152
+
153
+ try {
154
+ // Initialize Notion client
155
+ const notion = new Client({
156
+ auth: notionToken,
157
+ });
158
+
159
+ // Load pages configuration
160
+ const pages = loadPagesConfig(inputFile);
161
+ console.log(`📋 Found ${pages.length} page(s) to convert`);
162
+
163
+ const convertedFiles = [];
164
+
165
+ // Convert each page
166
+ for (const page of pages) {
167
+ try {
168
+ const outputFile = await convertNotionPage(
169
+ notion,
170
+ page.id,
171
+ outputDir,
172
+ page.slug || page.title?.toLowerCase().replace(/\s+/g, '-') || page.id
173
+ );
174
+ convertedFiles.push(outputFile);
175
+ } catch (error) {
176
+ console.error(`❌ Failed to convert page ${page.id}: ${error.message}`);
177
+ // Continue with other pages
178
+ }
179
+ }
180
+
181
+ // Post-process all converted files
182
+ console.log('🔧 Post-processing converted files...');
183
+ for (const file of convertedFiles) {
184
+ try {
185
+ let content = readFileSync(file, 'utf8');
186
+ content = postProcessMarkdown(content);
187
+ writeFileSync(file, content);
188
+ console.log(` ✅ Post-processed: ${basename(file)}`);
189
+ } catch (error) {
190
+ console.error(` ❌ Failed to post-process ${file}: ${error.message}`);
191
+ }
192
+ }
193
+
194
+ console.log(`✅ Conversion completed! ${convertedFiles.length} file(s) generated`);
195
+
196
+ } catch (error) {
197
+ console.error('❌ Conversion failed:', error.message);
198
+ process.exit(1);
199
+ }
200
+ }
201
+
202
+ function main() {
203
+ const config = parseArgs();
204
+
205
+ if (config.clean) {
206
+ console.log('🧹 Cleaning output directory...');
207
+ // Clean output directory logic would go here
208
+ }
209
+
210
+ convertNotionToMarkdown(config.input, config.output, config.token);
211
+ console.log('🎉 Notion conversion completed!');
212
+ }
213
+
214
+ // Show help if requested
215
+ if (process.argv.includes('--help') || process.argv.includes('-h')) {
216
+ console.log(`
217
+ 🚀 Notion to Markdown Converter
218
+
219
+ Usage:
220
+ node notion-converter.mjs [options]
221
+
222
+ Options:
223
+ --input=PATH Input pages configuration file (default: input/pages.json)
224
+ --output=PATH Output directory (default: output/)
225
+ --token=TOKEN Notion API token (or set NOTION_TOKEN env var)
226
+ --clean Clean output directory before conversion
227
+ --help, -h Show this help
228
+
229
+ Environment Variables:
230
+ NOTION_TOKEN Your Notion integration token
231
+
232
+ Examples:
233
+ # Basic conversion with environment token
234
+ NOTION_TOKEN=your_token node notion-converter.mjs
235
+
236
+ # Custom paths and token
237
+ node notion-converter.mjs --input=my-pages.json --output=converted/ --token=your_token
238
+
239
+ # Clean output first
240
+ node notion-converter.mjs --clean
241
+
242
+ Configuration File Format (pages.json):
243
+ {
244
+ "pages": [
245
+ {
246
+ "id": "your-notion-page-id",
247
+ "title": "Page Title",
248
+ "slug": "page-slug"
249
+ }
250
+ ]
251
+ }
252
+ `);
253
+ process.exit(0);
254
+ }
255
+
256
+ // Run CLI if called directly
257
+ if (import.meta.url === `file://${process.argv[1]}`) {
258
+ main();
259
+ }
app/scripts/notion-importer/notion-metadata-extractor.mjs ADDED
@@ -0,0 +1,303 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+
3
+ import { Client } from '@notionhq/client';
4
+
5
+ /**
6
+ * Notion Metadata Extractor
7
+ * Extracts document metadata from Notion pages for frontmatter generation
8
+ */
9
+
10
+ /**
11
+ * Extract metadata from Notion page
12
+ * @param {string} pageId - Notion page ID
13
+ * @param {string} notionToken - Notion API token
14
+ * @returns {object} - Extracted metadata object
15
+ */
16
+ export async function extractNotionMetadata(pageId, notionToken) {
17
+ const notion = new Client({
18
+ auth: notionToken,
19
+ });
20
+
21
+ const metadata = {};
22
+
23
+ try {
24
+ // Get page information
25
+ const page = await notion.pages.retrieve({ page_id: pageId });
26
+
27
+ // Extract title from page properties
28
+ if (page.properties.title && page.properties.title.title && page.properties.title.title.length > 0) {
29
+ metadata.title = page.properties.title.title[0].plain_text;
30
+ }
31
+
32
+ // Extract creation date
33
+ if (page.created_time) {
34
+ metadata.published = new Date(page.created_time).toLocaleDateString('en-US', {
35
+ year: 'numeric',
36
+ month: 'short',
37
+ day: '2-digit'
38
+ });
39
+ metadata.created_time = page.created_time;
40
+ }
41
+
42
+ // Extract last edited date
43
+ if (page.last_edited_time) {
44
+ metadata.last_edited_time = page.last_edited_time;
45
+ }
46
+
47
+ // Extract created by
48
+ if (page.created_by && page.created_by.id) {
49
+ metadata.created_by = page.created_by.id;
50
+ }
51
+
52
+ // Extract last edited by
53
+ if (page.last_edited_by && page.last_edited_by.id) {
54
+ metadata.last_edited_by = page.last_edited_by.id;
55
+ }
56
+
57
+ // Extract page URL
58
+ metadata.notion_url = page.url;
59
+
60
+ // Extract page ID
61
+ metadata.notion_id = page.id;
62
+
63
+ // Extract parent information
64
+ if (page.parent) {
65
+ metadata.parent = {
66
+ type: page.parent.type,
67
+ id: page.parent[page.parent.type]?.id || page.parent[page.parent.type]
68
+ };
69
+ }
70
+
71
+ // Extract cover image if available
72
+ if (page.cover) {
73
+ metadata.cover = {
74
+ type: page.cover.type,
75
+ url: page.cover[page.cover.type]?.url || page.cover[page.cover.type]
76
+ };
77
+ }
78
+
79
+ // Extract icon if available
80
+ if (page.icon) {
81
+ metadata.icon = {
82
+ type: page.icon.type,
83
+ emoji: page.icon.emoji,
84
+ url: page.icon.external?.url || page.icon.file?.url
85
+ };
86
+ }
87
+
88
+ // Extract authors and custom properties
89
+ const customProperties = {};
90
+ for (const [key, value] of Object.entries(page.properties)) {
91
+ if (key !== 'title') { // Skip title as it's handled separately
92
+ const extractedValue = extractPropertyValue(value);
93
+
94
+ // Check for author-related properties
95
+ if (key.toLowerCase().includes('author') ||
96
+ key.toLowerCase().includes('writer') ||
97
+ key.toLowerCase().includes('creator') ||
98
+ value.type === 'people') {
99
+ metadata.authors = extractedValue;
100
+ } else {
101
+ customProperties[key] = extractedValue;
102
+ }
103
+ }
104
+ }
105
+
106
+ // If no authors found in properties, try to get from created_by
107
+ if (!metadata.authors && page.created_by) {
108
+ try {
109
+ const user = await notion.users.retrieve({ user_id: page.created_by.id });
110
+ metadata.authors = [{
111
+ name: user.name || user.id,
112
+ id: user.id
113
+ }];
114
+ } catch (error) {
115
+ console.log(' ⚠️ Could not fetch author from created_by:', error.message);
116
+ // Fallback to basic info
117
+ metadata.authors = [{
118
+ name: page.created_by.name || page.created_by.id,
119
+ id: page.created_by.id
120
+ }];
121
+ }
122
+ }
123
+
124
+ if (Object.keys(customProperties).length > 0) {
125
+ metadata.properties = customProperties;
126
+ }
127
+
128
+ // Try to extract description from page content (first paragraph)
129
+ try {
130
+ const blocks = await notion.blocks.children.list({ block_id: pageId });
131
+ const firstParagraph = blocks.results.find(block =>
132
+ block.type === 'paragraph' &&
133
+ block.paragraph.rich_text &&
134
+ block.paragraph.rich_text.length > 0
135
+ );
136
+
137
+ if (firstParagraph) {
138
+ const description = firstParagraph.paragraph.rich_text
139
+ .map(text => text.plain_text)
140
+ .join('')
141
+ .trim();
142
+
143
+ if (description && description.length > 0) {
144
+ metadata.description = description.substring(0, 200) + (description.length > 200 ? '...' : '');
145
+ }
146
+ }
147
+ } catch (error) {
148
+ console.log(' ⚠️ Could not extract description from page content');
149
+ }
150
+
151
+ // Generate tags from page properties
152
+ const tags = [];
153
+ for (const [key, value] of Object.entries(page.properties)) {
154
+ if (value.type === 'multi_select' && value.multi_select) {
155
+ value.multi_select.forEach(option => {
156
+ tags.push(option.name);
157
+ });
158
+ } else if (value.type === 'select' && value.select) {
159
+ tags.push(value.select.name);
160
+ }
161
+ }
162
+
163
+ if (tags.length > 0) {
164
+ metadata.tags = tags;
165
+ }
166
+
167
+ } catch (error) {
168
+ console.error('Error extracting Notion metadata:', error.message);
169
+ // Return basic metadata if extraction fails
170
+ metadata.title = "Notion Article";
171
+ metadata.published = new Date().toLocaleDateString('en-US', {
172
+ year: 'numeric',
173
+ month: 'short',
174
+ day: '2-digit'
175
+ });
176
+ }
177
+
178
+ return metadata;
179
+ }
180
+
181
+ /**
182
+ * Extract value from Notion property
183
+ * @param {object} property - Notion property object
184
+ * @returns {any} - Extracted value
185
+ */
186
+ function extractPropertyValue(property) {
187
+ switch (property.type) {
188
+ case 'rich_text':
189
+ return property.rich_text.map(text => text.plain_text).join('');
190
+ case 'title':
191
+ return property.title.map(text => text.plain_text).join('');
192
+ case 'number':
193
+ return property.number;
194
+ case 'select':
195
+ return property.select?.name || null;
196
+ case 'multi_select':
197
+ return property.multi_select.map(option => option.name);
198
+ case 'date':
199
+ return property.date?.start || null;
200
+ case 'checkbox':
201
+ return property.checkbox;
202
+ case 'url':
203
+ return property.url;
204
+ case 'email':
205
+ return property.email;
206
+ case 'phone_number':
207
+ return property.phone_number;
208
+ case 'created_time':
209
+ return property.created_time;
210
+ case 'created_by':
211
+ return property.created_by?.id || null;
212
+ case 'last_edited_time':
213
+ return property.last_edited_time;
214
+ case 'last_edited_by':
215
+ return property.last_edited_by?.id || null;
216
+ case 'people':
217
+ return property.people.map(person => ({
218
+ name: person.name || person.id,
219
+ id: person.id
220
+ }));
221
+ default:
222
+ return null;
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Generate YAML frontmatter from metadata object
228
+ * @param {object} metadata - Metadata object
229
+ * @returns {string} - YAML frontmatter string
230
+ */
231
+ export function generateNotionFrontmatter(metadata) {
232
+ let frontmatter = '---\n';
233
+
234
+ // Title
235
+ if (metadata.title) {
236
+ frontmatter += `title: "${metadata.title}"\n`;
237
+ }
238
+
239
+ // Description
240
+ if (metadata.description) {
241
+ frontmatter += `description: "${metadata.description}"\n`;
242
+ }
243
+
244
+ // Publication date
245
+ if (metadata.published) {
246
+ frontmatter += `published: "${metadata.published}"\n`;
247
+ }
248
+
249
+ // Authors
250
+ if (metadata.authors && metadata.authors.length > 0) {
251
+ frontmatter += 'authors:\n';
252
+ metadata.authors.forEach(author => {
253
+ if (typeof author === 'string') {
254
+ frontmatter += ` - name: "${author}"\n`;
255
+ } else if (author.name) {
256
+ frontmatter += ` - name: "${author.name}"\n`;
257
+ }
258
+ });
259
+ }
260
+
261
+ // Tags
262
+ if (metadata.tags && metadata.tags.length > 0) {
263
+ frontmatter += 'tags:\n';
264
+ metadata.tags.forEach(tag => {
265
+ frontmatter += ` - "${tag}"\n`;
266
+ });
267
+ }
268
+
269
+ // Notion metadata removed - keeping only standard frontmatter fields
270
+
271
+ // Cover image
272
+ if (metadata.cover && metadata.cover.url) {
273
+ frontmatter += `cover: "${metadata.cover.url}"\n`;
274
+ }
275
+
276
+ // Icon
277
+ if (metadata.icon) {
278
+ if (metadata.icon.emoji) {
279
+ frontmatter += `icon: "${metadata.icon.emoji}"\n`;
280
+ } else if (metadata.icon.url) {
281
+ frontmatter += `icon: "${metadata.icon.url}"\n`;
282
+ }
283
+ }
284
+
285
+ // Custom properties removed - keeping frontmatter clean and standard
286
+
287
+ // Default Astro configuration
288
+ frontmatter += 'tableOfContentsAutoCollapse: true\n';
289
+ frontmatter += '---\n\n';
290
+
291
+ return frontmatter;
292
+ }
293
+
294
+ /**
295
+ * Extract and generate frontmatter from Notion page
296
+ * @param {string} pageId - Notion page ID
297
+ * @param {string} notionToken - Notion API token
298
+ * @returns {string} - Complete YAML frontmatter
299
+ */
300
+ export async function extractAndGenerateNotionFrontmatter(pageId, notionToken) {
301
+ const metadata = await extractNotionMetadata(pageId, notionToken);
302
+ return generateNotionFrontmatter(metadata);
303
+ }
app/scripts/notion-importer/output/.temp-pages.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "pages": [
3
+ {
4
+ "id": "27877f1c9c9d804d9c82f7b3905578ff",
5
+ "title": "smol",
6
+ "slug": "smol"
7
+ }
8
+ ]
9
+ }
app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8013-b668-f14bd1ac0ec0.png ADDED

Git LFS Details

  • SHA256: d98d74457cf5234df8ca8adcc934352fd85f5a9e0136b6a29df9fd06085b36af
  • Pointer size: 131 Bytes
  • Size of remote file: 291 kB
app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8014-834f-d700b623256b.png ADDED

Git LFS Details

  • SHA256: b461e8e2a5b124ddb1758524277e7ed820aed6d2d2d0a620f4f570e30803b66d
  • Pointer size: 130 Bytes
  • Size of remote file: 74 kB
app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-801d-841a-e35011491566.png ADDED

Git LFS Details

  • SHA256: b01c31948030bed4175a5b2f74608a3d961af2a4f707849aa406cd9646e26e5f
  • Pointer size: 131 Bytes
  • Size of remote file: 186 kB
app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8031-ac8d-c5678af1bdd5.png ADDED

Git LFS Details

  • SHA256: c99977a98f7bb69f3afc9fd1a31c6e6b422c0becd9f3968ec580a4d8186d3182
  • Pointer size: 131 Bytes
  • Size of remote file: 180 kB
app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8048-9b7e-db4fa7485915.png ADDED

Git LFS Details

  • SHA256: d3ea46c46f2270fc88543836183bae85bd457d4c82329fb7e83a1d5227392fd6
  • Pointer size: 131 Bytes
  • Size of remote file: 163 kB
app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-804d-bd0a-e0b1c15e504f.png ADDED

Git LFS Details

  • SHA256: 7ee903889e42121d6953f0a41545f59e69431fdb33ea94808abf7d12b2760b0a
  • Pointer size: 131 Bytes
  • Size of remote file: 265 kB
app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8075-ae2e-dc24fe9296ca.png ADDED

Git LFS Details

  • SHA256: 3c8e9ec1d8288a94705ad53a789a80b89684718162ecd5194232f84f5961c938
  • Pointer size: 130 Bytes
  • Size of remote file: 80.6 kB
app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-8078-b6da-c7a4c67c8f35.png ADDED

Git LFS Details

  • SHA256: f435105ea29cdb02bfd774bf9a6b9a1e83c62c1a15b10da81d3c849f97c917a7
  • Pointer size: 130 Bytes
  • Size of remote file: 55.7 kB
app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-808d-9c6d-fae817ac8868.png ADDED

Git LFS Details

  • SHA256: 70dd1ad811ef5941d3bc1eb67ec66924782e511c78ebecce7798624716556f15
  • Pointer size: 130 Bytes
  • Size of remote file: 36.1 kB
app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-808f-b712-c7c608da3fc6.png ADDED

Git LFS Details

  • SHA256: 286dfd49b4378214990cbc1532272f00a8a2c9cb2cf6305509f5377ee90e25cc
  • Pointer size: 131 Bytes
  • Size of remote file: 187 kB
app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80a9-b4d0-f2129716632d.png ADDED

Git LFS Details

  • SHA256: 99cbea898b137e7befa3835241d170034b526d0d3f78f5dce2596fdf144f4882
  • Pointer size: 131 Bytes
  • Size of remote file: 262 kB
app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80aa-b968-c54c9fe7e5d7.png ADDED

Git LFS Details

  • SHA256: 2904c36730cd019f75a50e5f6bfe7ea0936fd741a7908c79344635e1c3237739
  • Pointer size: 131 Bytes
  • Size of remote file: 249 kB
app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80b6-be07-e8646502f82a.png ADDED

Git LFS Details

  • SHA256: 3432dabfcb5f515b65018d73e15c77923dc85208024f47cdab985fa271002a1e
  • Pointer size: 131 Bytes
  • Size of remote file: 213 kB
app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80b9-8cfb-f0a6aaaa8760.png ADDED

Git LFS Details

  • SHA256: 138e37882a394c0e4d103ea6eb46e694ece5bf7092c4c7aeef2bf72b7bf2efa9
  • Pointer size: 131 Bytes
  • Size of remote file: 272 kB
app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80e7-a500-fb79cebde7e3.png ADDED

Git LFS Details

  • SHA256: bb5141925372d43013596fd0a953e4d7b763a8837d23175f771ec78d29756d57
  • Pointer size: 131 Bytes
  • Size of remote file: 283 kB
app/scripts/notion-importer/output/media/27877f1c9c9d804d9c82f7b3905578ff/image_27877f1c-9c9d-80e9-b729-dbd328930bed.png ADDED

Git LFS Details

  • SHA256: cda03fd1680cf1e8e21b689be6e91269a64c96c629995eb20ae4cebf6ea94ec1
  • Pointer size: 131 Bytes
  • Size of remote file: 221 kB
app/scripts/notion-importer/output/smol-training-guide.md ADDED
The diff for this file is too large to render. See raw diff
 
app/scripts/notion-importer/output/smol-training-guide.mdx ADDED
The diff for this file is too large to render. See raw diff
 
app/scripts/notion-importer/output/smol.md ADDED
The diff for this file is too large to render. See raw diff
 
app/scripts/notion-importer/output/smol.mdx ADDED
The diff for this file is too large to render. See raw diff
 
app/scripts/notion-importer/package-lock.json ADDED
Binary file (89.3 kB). View file
 
app/scripts/notion-importer/package.json ADDED
Binary file (1.19 kB). View file
 
app/scripts/notion-importer/post-processor.mjs ADDED
@@ -0,0 +1,369 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
4
+ import { join, dirname, basename } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
9
+
10
+ /**
11
+ * Post-process Notion-generated Markdown for better MDX compatibility
12
+ * @param {string} content - Raw markdown content from Notion
13
+ * @returns {string} - Processed markdown content
14
+ */
15
+ export function postProcessMarkdown(content) {
16
+ console.log('🔧 Post-processing Notion Markdown for MDX compatibility...');
17
+
18
+ let processedContent = content;
19
+
20
+ // Apply each transformation step
21
+ processedContent = cleanNotionArtifacts(processedContent);
22
+ processedContent = fixNotionLinks(processedContent);
23
+ processedContent = optimizeImages(processedContent);
24
+ processedContent = shiftHeadingLevels(processedContent);
25
+ processedContent = cleanEmptyLines(processedContent);
26
+ processedContent = fixCodeBlocks(processedContent);
27
+ processedContent = fixCodeBlockEndings(processedContent);
28
+ processedContent = optimizeTables(processedContent);
29
+
30
+ return processedContent;
31
+ }
32
+
33
+ /**
34
+ * Clean Notion-specific artifacts and formatting
35
+ * @param {string} content - Markdown content
36
+ * @returns {string} - Cleaned content
37
+ */
38
+ function cleanNotionArtifacts(content) {
39
+ console.log(' 🧹 Cleaning Notion artifacts...');
40
+
41
+ let cleanedCount = 0;
42
+
43
+ // Remove Notion's internal page references that don't convert well
44
+ content = content.replace(/\[([^\]]+)\]\(https:\/\/www\.notion\.so\/[^)]+\)/g, (match, text) => {
45
+ cleanedCount++;
46
+ return text; // Keep just the text, remove the broken link
47
+ });
48
+
49
+ // Clean up Notion's callout blocks that might not render properly
50
+ content = content.replace(/^> \*\*([^*]+)\*\*\s*\n/gm, '> **$1**\n\n');
51
+
52
+ // Remove Notion's page dividers that don't have markdown equivalents
53
+ content = content.replace(/^---+\s*$/gm, '');
54
+
55
+ // Clean up empty blockquotes
56
+ content = content.replace(/^>\s*$/gm, '');
57
+
58
+ if (cleanedCount > 0) {
59
+ console.log(` ✅ Cleaned ${cleanedCount} Notion artifact(s)`);
60
+ }
61
+
62
+ return content;
63
+ }
64
+
65
+ /**
66
+ * Fix Notion internal links to be more MDX-friendly
67
+ * @param {string} content - Markdown content
68
+ * @returns {string} - Content with fixed links
69
+ */
70
+ function fixNotionLinks(content) {
71
+ console.log(' 🔗 Fixing Notion internal links...');
72
+
73
+ let fixedCount = 0;
74
+
75
+ // Convert Notion page links to relative links (assuming they'll be converted to MDX)
76
+ content = content.replace(/\[([^\]]+)\]\(https:\/\/www\.notion\.so\/[^/]+\/([^?#)]+)\)/g, (match, text, pageId) => {
77
+ fixedCount++;
78
+ // Convert to relative link - this will need to be updated based on your routing
79
+ return `[${text}](#${pageId})`;
80
+ });
81
+
82
+ // Fix broken notion.so links that might be malformed
83
+ content = content.replace(/\[([^\]]+)\]\(https:\/\/www\.notion\.so\/[^)]*\)/g, (match, text) => {
84
+ fixedCount++;
85
+ return text; // Remove broken links, keep text
86
+ });
87
+
88
+ if (fixedCount > 0) {
89
+ console.log(` ✅ Fixed ${fixedCount} Notion link(s)`);
90
+ }
91
+
92
+ return content;
93
+ }
94
+
95
+ /**
96
+ * Optimize images for better MDX compatibility
97
+ * @param {string} content - Markdown content
98
+ * @returns {string} - Content with optimized images
99
+ */
100
+ function optimizeImages(content) {
101
+ console.log(' 🖼️ Optimizing images...');
102
+
103
+ let optimizedCount = 0;
104
+
105
+ // Ensure images have proper alt text
106
+ content = content.replace(/!\[\]\(([^)]+)\)/g, (match, src) => {
107
+ optimizedCount++;
108
+ const filename = basename(src);
109
+ return `![${filename}](${src})`;
110
+ });
111
+
112
+ // Clean up image paths that might have query parameters
113
+ content = content.replace(/!\[([^\]]*)\]\(([^)]+)\?[^)]*\)/g, (match, alt, src) => {
114
+ optimizedCount++;
115
+ return `![${alt}](${src})`;
116
+ });
117
+
118
+ if (optimizedCount > 0) {
119
+ console.log(` ✅ Optimized ${optimizedCount} image(s)`);
120
+ }
121
+
122
+ return content;
123
+ }
124
+
125
+ /**
126
+ * Shift all heading levels down by one (H1 → H2, H2 → H3, etc.)
127
+ * @param {string} content - Markdown content
128
+ * @returns {string} - Content with shifted heading levels
129
+ */
130
+ function shiftHeadingLevels(content) {
131
+ console.log(' 📝 Shifting heading levels down by one...');
132
+
133
+ let shiftedCount = 0;
134
+
135
+ // Shift heading levels: H1 → H2, H2 → H3, H3 → H4, H4 → H5, H5 → H6
136
+ // Process from highest to lowest to avoid conflicts
137
+ content = content.replace(/^##### (.*$)/gim, '###### $1');
138
+ content = content.replace(/^#### (.*$)/gim, '##### $1');
139
+ content = content.replace(/^### (.*$)/gim, '#### $1');
140
+ content = content.replace(/^## (.*$)/gim, '### $1');
141
+ content = content.replace(/^# (.*$)/gim, '## $1');
142
+
143
+ // Count the number of headings shifted
144
+ const headingMatches = content.match(/^#{1,6} /gm);
145
+ if (headingMatches) {
146
+ shiftedCount = headingMatches.length;
147
+ }
148
+
149
+ console.log(` ✅ Shifted ${shiftedCount} heading level(s)`);
150
+ return content;
151
+ }
152
+
153
+ /**
154
+ * Fix code block endings that end with "text" instead of proper closing
155
+ * @param {string} content - Markdown content
156
+ * @returns {string} - Content with fixed code block endings
157
+ */
158
+ function fixCodeBlockEndings(content) {
159
+ console.log(' 💻 Fixing code block endings...');
160
+
161
+ let fixedCount = 0;
162
+
163
+ // Fix code blocks that end with ```text instead of ```
164
+ content = content.replace(/```text\n/g, '```\n');
165
+
166
+ // Count the number of fixes
167
+ const textEndingMatches = content.match(/```text\n/g);
168
+ if (textEndingMatches) {
169
+ fixedCount = textEndingMatches.length;
170
+ }
171
+
172
+ if (fixedCount > 0) {
173
+ console.log(` ✅ Fixed ${fixedCount} code block ending(s)`);
174
+ }
175
+
176
+ return content;
177
+ }
178
+
179
+ /**
180
+ * Clean up excessive empty lines
181
+ * @param {string} content - Markdown content
182
+ * @returns {string} - Content with cleaned spacing
183
+ */
184
+ function cleanEmptyLines(content) {
185
+ console.log(' 📝 Cleaning excessive empty lines...');
186
+
187
+ // Replace 3+ consecutive newlines with 2 newlines
188
+ const cleanedContent = content.replace(/\n{3,}/g, '\n\n');
189
+
190
+ const originalLines = content.split('\n').length;
191
+ const cleanedLines = cleanedContent.split('\n').length;
192
+ const removedLines = originalLines - cleanedLines;
193
+
194
+ if (removedLines > 0) {
195
+ console.log(` ✅ Removed ${removedLines} excessive empty line(s)`);
196
+ }
197
+
198
+ return cleanedContent;
199
+ }
200
+
201
+ /**
202
+ * Fix code blocks for better MDX compatibility
203
+ * @param {string} content - Markdown content
204
+ * @returns {string} - Content with fixed code blocks
205
+ */
206
+ function fixCodeBlocks(content) {
207
+ console.log(' 💻 Fixing code blocks...');
208
+
209
+ let fixedCount = 0;
210
+
211
+ // Ensure code blocks have proper language identifiers
212
+ content = content.replace(/^```\s*$/gm, '```text');
213
+
214
+ // Fix code blocks that might have Notion-specific formatting
215
+ content = content.replace(/^```(\w+)\s*\n([\s\S]*?)\n```$/gm, (match, lang, code) => {
216
+ // Clean up any Notion artifacts in code
217
+ const cleanCode = code.replace(/\u00A0/g, ' '); // Replace non-breaking spaces
218
+ return `\`\`\`${lang}\n${cleanCode}\n\`\`\``;
219
+ });
220
+
221
+ if (fixedCount > 0) {
222
+ console.log(` ✅ Fixed ${fixedCount} code block(s)`);
223
+ }
224
+
225
+ return content;
226
+ }
227
+
228
+ /**
229
+ * Optimize tables for better MDX rendering
230
+ * @param {string} content - Markdown content
231
+ * @returns {string} - Content with optimized tables
232
+ */
233
+ function optimizeTables(content) {
234
+ console.log(' 📊 Optimizing tables...');
235
+
236
+ let optimizedCount = 0;
237
+
238
+ // Fix tables that might have inconsistent column counts
239
+ content = content.replace(/^\|(.+)\|\s*$/gm, (match, row) => {
240
+ const cells = row.split('|').map(cell => cell.trim());
241
+ const cleanCells = cells.filter(cell => cell.length > 0);
242
+
243
+ if (cleanCells.length > 0) {
244
+ optimizedCount++;
245
+ return `| ${cleanCells.join(' | ')} |`;
246
+ }
247
+ return match;
248
+ });
249
+
250
+ // Ensure table headers are properly formatted
251
+ content = content.replace(/^\|(.+)\|\s*\n\|([-:\s|]+)\|\s*$/gm, (match, header, separator) => {
252
+ const headerCells = header.split('|').map(cell => cell.trim()).filter(cell => cell.length > 0);
253
+ const separatorCells = separator.split('|').map(cell => cell.trim()).filter(cell => cell.length > 0);
254
+
255
+ if (headerCells.length !== separatorCells.length) {
256
+ optimizedCount++;
257
+ const newSeparator = headerCells.map(() => '---').join(' | ');
258
+ return `| ${headerCells.join(' | ')} |\n| ${newSeparator} |`;
259
+ }
260
+ return match;
261
+ });
262
+
263
+ if (optimizedCount > 0) {
264
+ console.log(` ✅ Optimized ${optimizedCount} table(s)`);
265
+ }
266
+
267
+ return content;
268
+ }
269
+
270
+ /**
271
+ * Extract frontmatter from Notion page properties
272
+ * @param {Object} pageProperties - Notion page properties
273
+ * @returns {string} - YAML frontmatter
274
+ */
275
+ export function generateFrontmatter(pageProperties) {
276
+ console.log(' 📄 Generating frontmatter from Notion properties...');
277
+
278
+ const frontmatter = {
279
+ title: pageProperties.title || 'Untitled',
280
+ published: new Date().toISOString().split('T')[0],
281
+ tableOfContentsAutoCollapse: true
282
+ };
283
+
284
+ // Add other properties if they exist
285
+ if (pageProperties.description) {
286
+ frontmatter.description = pageProperties.description;
287
+ }
288
+ if (pageProperties.tags) {
289
+ frontmatter.tags = pageProperties.tags;
290
+ }
291
+ if (pageProperties.author) {
292
+ frontmatter.author = pageProperties.author;
293
+ }
294
+
295
+ // Convert to YAML string
296
+ const yamlLines = Object.entries(frontmatter)
297
+ .map(([key, value]) => {
298
+ if (Array.isArray(value)) {
299
+ return `${key}:\n${value.map(v => ` - ${v}`).join('\n')}`;
300
+ }
301
+ return `${key}: "${value}"`;
302
+ });
303
+
304
+ return `---\n${yamlLines.join('\n')}\n---\n\n`;
305
+ }
306
+
307
+ function main() {
308
+ const args = process.argv.slice(2);
309
+
310
+ if (args.includes('--help') || args.includes('-h')) {
311
+ console.log(`
312
+ 🔧 Notion Markdown Post-Processor
313
+
314
+ Usage:
315
+ node post-processor.mjs [options] [input-file] [output-file]
316
+
317
+ Options:
318
+ --verbose Show detailed processing information
319
+ --help, -h Show this help
320
+
321
+ Examples:
322
+ # Process a single file
323
+ node post-processor.mjs input.md output.md
324
+
325
+ # Process with verbose output
326
+ node post-processor.mjs --verbose input.md output.md
327
+ `);
328
+ process.exit(0);
329
+ }
330
+
331
+ const verbose = args.includes('--verbose');
332
+ const inputFile = args.find(arg => !arg.startsWith('--') && arg.endsWith('.md'));
333
+ const outputFile = args.find(arg => !arg.startsWith('--') && arg !== inputFile && arg.endsWith('.md'));
334
+
335
+ if (!inputFile) {
336
+ console.error('❌ Please provide an input markdown file');
337
+ process.exit(1);
338
+ }
339
+
340
+ if (!existsSync(inputFile)) {
341
+ console.error(`❌ Input file not found: ${inputFile}`);
342
+ process.exit(1);
343
+ }
344
+
345
+ try {
346
+ console.log(`📖 Reading: ${inputFile}`);
347
+ const content = readFileSync(inputFile, 'utf8');
348
+
349
+ const processedContent = postProcessMarkdown(content);
350
+
351
+ const finalOutputFile = outputFile || inputFile.replace('.md', '.processed.md');
352
+ writeFileSync(finalOutputFile, processedContent);
353
+
354
+ console.log(`✅ Processed: ${finalOutputFile}`);
355
+
356
+ if (verbose) {
357
+ console.log(`📊 Input: ${content.length} chars → Output: ${processedContent.length} chars`);
358
+ }
359
+
360
+ } catch (error) {
361
+ console.error('❌ Processing failed:', error.message);
362
+ process.exit(1);
363
+ }
364
+ }
365
+
366
+ // Run CLI if called directly
367
+ if (import.meta.url === `file://${process.argv[1]}`) {
368
+ main();
369
+ }
app/scripts/notion-importer/test-access.mjs ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+
3
+ import { config } from 'dotenv';
4
+ import { Client } from '@notionhq/client';
5
+
6
+ // Load environment variables from .env file
7
+ config();
8
+
9
+ const notion = new Client({
10
+ auth: process.env.NOTION_TOKEN,
11
+ });
12
+
13
+ async function testAccess() {
14
+ const pageId = '27877f1c9c9d804d9c82f7b3905578ff';
15
+
16
+ try {
17
+ console.log('🔍 Testing access to Notion page...');
18
+ console.log(`📄 Page ID: ${pageId}`);
19
+
20
+ const response = await notion.pages.retrieve({ page_id: pageId });
21
+
22
+ console.log('✅ Access successful!');
23
+ console.log(`📝 Page title: ${response.properties.title?.title?.[0]?.text?.content || 'No title'}`);
24
+ console.log(`📅 Created: ${response.created_time}`);
25
+ console.log(`👤 Created by: ${response.created_by.id}`);
26
+
27
+ } catch (error) {
28
+ console.error('❌ Access failed:', error.message);
29
+
30
+ if (error.code === 'unauthorized') {
31
+ console.log('\n💡 Solutions:');
32
+ console.log('1. Check that your NOTION_TOKEN is correct');
33
+ console.log('2. Make sure the page is shared with your integration');
34
+ console.log('3. Verify that the integration has the right permissions');
35
+ }
36
+ }
37
+ }
38
+
39
+ testAccess();
app/scripts/notion-importer/yarn.lock ADDED
@@ -0,0 +1,1118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2
+ # yarn lockfile v1
3
+
4
+
5
+ "@cspotcode/source-map-support@^0.8.0":
6
+ version "0.8.1"
7
+ resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz"
8
+ integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
9
+ dependencies:
10
+ "@jridgewell/trace-mapping" "0.3.9"
11
+
12
+ "@jridgewell/resolve-uri@^3.0.3":
13
+ version "3.1.2"
14
+ resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz"
15
+ integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
16
+
17
+ "@jridgewell/sourcemap-codec@^1.4.10":
18
+ version "1.5.5"
19
+ resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz"
20
+ integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
21
+
22
+ "@jridgewell/trace-mapping@0.3.9":
23
+ version "0.3.9"
24
+ resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz"
25
+ integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
26
+ dependencies:
27
+ "@jridgewell/resolve-uri" "^3.0.3"
28
+ "@jridgewell/sourcemap-codec" "^1.4.10"
29
+
30
+ "@notionhq/client@^2.0.0", "@notionhq/client@^2.2.15":
31
+ version "2.3.0"
32
+ resolved "https://registry.npmjs.org/@notionhq/client/-/client-2.3.0.tgz"
33
+ integrity sha512-l7WqTCpQqC+HibkB9chghONQTYcxNQT0/rOJemBfmuKQRTu2vuV8B3yA395iKaUdDo7HI+0KvQaz9687Xskzkw==
34
+ dependencies:
35
+ "@types/node-fetch" "^2.5.10"
36
+ node-fetch "^2.6.1"
37
+
38
+ "@tsconfig/node10@^1.0.7":
39
+ version "1.0.11"
40
+ resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz"
41
+ integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==
42
+
43
+ "@tsconfig/node12@^1.0.7":
44
+ version "1.0.11"
45
+ resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz"
46
+ integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
47
+
48
+ "@tsconfig/node14@^1.0.0":
49
+ version "1.0.3"
50
+ resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz"
51
+ integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
52
+
53
+ "@tsconfig/node16@^1.0.2":
54
+ version "1.0.4"
55
+ resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz"
56
+ integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==
57
+
58
+ "@types/debug@^4.0.0":
59
+ version "4.1.12"
60
+ resolved "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz"
61
+ integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==
62
+ dependencies:
63
+ "@types/ms" "*"
64
+
65
+ "@types/estree-jsx@^1.0.0":
66
+ version "1.0.5"
67
+ resolved "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz"
68
+ integrity sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==
69
+ dependencies:
70
+ "@types/estree" "*"
71
+
72
+ "@types/estree@*", "@types/estree@^1.0.0":
73
+ version "1.0.8"
74
+ resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz"
75
+ integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==
76
+
77
+ "@types/hast@^3.0.0":
78
+ version "3.0.4"
79
+ resolved "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz"
80
+ integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==
81
+ dependencies:
82
+ "@types/unist" "*"
83
+
84
+ "@types/mdast@^4.0.0":
85
+ version "4.0.4"
86
+ resolved "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz"
87
+ integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==
88
+ dependencies:
89
+ "@types/unist" "*"
90
+
91
+ "@types/ms@*":
92
+ version "2.1.0"
93
+ resolved "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz"
94
+ integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==
95
+
96
+ "@types/node-fetch@^2.5.10":
97
+ version "2.6.13"
98
+ resolved "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz"
99
+ integrity sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==
100
+ dependencies:
101
+ "@types/node" "*"
102
+ form-data "^4.0.4"
103
+
104
+ "@types/node@*":
105
+ version "24.5.2"
106
+ resolved "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz"
107
+ integrity sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==
108
+ dependencies:
109
+ undici-types "~7.12.0"
110
+
111
+ "@types/unist@*", "@types/unist@^3.0.0":
112
+ version "3.0.3"
113
+ resolved "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz"
114
+ integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==
115
+
116
+ "@types/unist@^2.0.0":
117
+ version "2.0.11"
118
+ resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz"
119
+ integrity sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==
120
+
121
+ acorn-jsx@^5.0.0:
122
+ version "5.3.2"
123
+ resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
124
+ integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
125
+
126
+ acorn-walk@^8.1.1:
127
+ version "8.3.4"
128
+ resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz"
129
+ integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==
130
+ dependencies:
131
+ acorn "^8.11.0"
132
+
133
+ "acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.0.0, acorn@^8.11.0, acorn@^8.4.1:
134
+ version "8.15.0"
135
+ resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz"
136
+ integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==
137
+
138
+ arg@^4.1.0:
139
+ version "4.1.3"
140
+ resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz"
141
+ integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
142
+
143
+ argparse@^1.0.7:
144
+ version "1.0.10"
145
+ resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz"
146
+ integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
147
+ dependencies:
148
+ sprintf-js "~1.0.2"
149
+
150
+ asynckit@^0.4.0:
151
+ version "0.4.0"
152
+ resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz"
153
+ integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
154
+
155
+ bail@^2.0.0:
156
+ version "2.0.2"
157
+ resolved "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz"
158
+ integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==
159
+
160
+ call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
161
+ version "1.0.2"
162
+ resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz"
163
+ integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
164
+ dependencies:
165
+ es-errors "^1.3.0"
166
+ function-bind "^1.1.2"
167
+
168
+ ccount@^2.0.0:
169
+ version "2.0.1"
170
+ resolved "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz"
171
+ integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==
172
+
173
+ character-entities-html4@^2.0.0:
174
+ version "2.1.0"
175
+ resolved "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz"
176
+ integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==
177
+
178
+ character-entities-legacy@^3.0.0:
179
+ version "3.0.0"
180
+ resolved "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz"
181
+ integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==
182
+
183
+ character-entities@^2.0.0:
184
+ version "2.0.2"
185
+ resolved "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz"
186
+ integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==
187
+
188
+ character-reference-invalid@^2.0.0:
189
+ version "2.0.1"
190
+ resolved "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz"
191
+ integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==
192
+
193
+ combined-stream@^1.0.8:
194
+ version "1.0.8"
195
+ resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz"
196
+ integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
197
+ dependencies:
198
+ delayed-stream "~1.0.0"
199
+
200
+ create-require@^1.1.0:
201
+ version "1.1.1"
202
+ resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz"
203
+ integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
204
+
205
+ data-uri-to-buffer@^4.0.0:
206
+ version "4.0.1"
207
+ resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz"
208
+ integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==
209
+
210
+ debug@^4.0.0:
211
+ version "4.4.3"
212
+ resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz"
213
+ integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
214
+ dependencies:
215
+ ms "^2.1.3"
216
+
217
+ decode-named-character-reference@^1.0.0:
218
+ version "1.2.0"
219
+ resolved "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz"
220
+ integrity sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==
221
+ dependencies:
222
+ character-entities "^2.0.0"
223
+
224
+ delayed-stream@~1.0.0:
225
+ version "1.0.0"
226
+ resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz"
227
+ integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
228
+
229
+ dequal@^2.0.0:
230
+ version "2.0.3"
231
+ resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz"
232
+ integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==
233
+
234
+ devlop@^1.0.0, devlop@^1.1.0:
235
+ version "1.1.0"
236
+ resolved "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz"
237
+ integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==
238
+ dependencies:
239
+ dequal "^2.0.0"
240
+
241
+ diff@^4.0.1:
242
+ version "4.0.2"
243
+ resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz"
244
+ integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
245
+
246
+ dotenv@^17.2.2:
247
+ version "17.2.2"
248
+ resolved "https://registry.npmjs.org/dotenv/-/dotenv-17.2.2.tgz"
249
+ integrity sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==
250
+
251
+ dunder-proto@^1.0.1:
252
+ version "1.0.1"
253
+ resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz"
254
+ integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
255
+ dependencies:
256
+ call-bind-apply-helpers "^1.0.1"
257
+ es-errors "^1.3.0"
258
+ gopd "^1.2.0"
259
+
260
+ es-define-property@^1.0.1:
261
+ version "1.0.1"
262
+ resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz"
263
+ integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
264
+
265
+ es-errors@^1.3.0:
266
+ version "1.3.0"
267
+ resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz"
268
+ integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
269
+
270
+ es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
271
+ version "1.1.1"
272
+ resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz"
273
+ integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
274
+ dependencies:
275
+ es-errors "^1.3.0"
276
+
277
+ es-set-tostringtag@^2.1.0:
278
+ version "2.1.0"
279
+ resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz"
280
+ integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==
281
+ dependencies:
282
+ es-errors "^1.3.0"
283
+ get-intrinsic "^1.2.6"
284
+ has-tostringtag "^1.0.2"
285
+ hasown "^2.0.2"
286
+
287
+ esprima@^4.0.0:
288
+ version "4.0.1"
289
+ resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz"
290
+ integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
291
+
292
+ estree-util-is-identifier-name@^3.0.0:
293
+ version "3.0.0"
294
+ resolved "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz"
295
+ integrity sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==
296
+
297
+ estree-util-visit@^2.0.0:
298
+ version "2.0.0"
299
+ resolved "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz"
300
+ integrity sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==
301
+ dependencies:
302
+ "@types/estree-jsx" "^1.0.0"
303
+ "@types/unist" "^3.0.0"
304
+
305
+ extend-shallow@^2.0.1:
306
+ version "2.0.1"
307
+ resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz"
308
+ integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==
309
+ dependencies:
310
+ is-extendable "^0.1.0"
311
+
312
+ extend@^3.0.0:
313
+ version "3.0.2"
314
+ resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz"
315
+ integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
316
+
317
+ fetch-blob@^3.1.2, fetch-blob@^3.1.4:
318
+ version "3.2.0"
319
+ resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz"
320
+ integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==
321
+ dependencies:
322
+ node-domexception "^1.0.0"
323
+ web-streams-polyfill "^3.0.3"
324
+
325
+ form-data@^4.0.4:
326
+ version "4.0.4"
327
+ resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz"
328
+ integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==
329
+ dependencies:
330
+ asynckit "^0.4.0"
331
+ combined-stream "^1.0.8"
332
+ es-set-tostringtag "^2.1.0"
333
+ hasown "^2.0.2"
334
+ mime-types "^2.1.12"
335
+
336
+ formdata-polyfill@^4.0.10:
337
+ version "4.0.10"
338
+ resolved "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz"
339
+ integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==
340
+ dependencies:
341
+ fetch-blob "^3.1.2"
342
+
343
+ function-bind@^1.1.2:
344
+ version "1.1.2"
345
+ resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
346
+ integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
347
+
348
+ get-intrinsic@^1.2.6:
349
+ version "1.3.0"
350
+ resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz"
351
+ integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
352
+ dependencies:
353
+ call-bind-apply-helpers "^1.0.2"
354
+ es-define-property "^1.0.1"
355
+ es-errors "^1.3.0"
356
+ es-object-atoms "^1.1.1"
357
+ function-bind "^1.1.2"
358
+ get-proto "^1.0.1"
359
+ gopd "^1.2.0"
360
+ has-symbols "^1.1.0"
361
+ hasown "^2.0.2"
362
+ math-intrinsics "^1.1.0"
363
+
364
+ get-proto@^1.0.1:
365
+ version "1.0.1"
366
+ resolved "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz"
367
+ integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
368
+ dependencies:
369
+ dunder-proto "^1.0.1"
370
+ es-object-atoms "^1.0.0"
371
+
372
+ gopd@^1.2.0:
373
+ version "1.2.0"
374
+ resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz"
375
+ integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
376
+
377
+ gray-matter@^4.0.3:
378
+ version "4.0.3"
379
+ resolved "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz"
380
+ integrity sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==
381
+ dependencies:
382
+ js-yaml "^3.13.1"
383
+ kind-of "^6.0.2"
384
+ section-matter "^1.0.0"
385
+ strip-bom-string "^1.0.0"
386
+
387
+ has-symbols@^1.0.3, has-symbols@^1.1.0:
388
+ version "1.1.0"
389
+ resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz"
390
+ integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
391
+
392
+ has-tostringtag@^1.0.2:
393
+ version "1.0.2"
394
+ resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz"
395
+ integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
396
+ dependencies:
397
+ has-symbols "^1.0.3"
398
+
399
+ hasown@^2.0.2:
400
+ version "2.0.2"
401
+ resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz"
402
+ integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
403
+ dependencies:
404
+ function-bind "^1.1.2"
405
+
406
+ is-alphabetical@^2.0.0:
407
+ version "2.0.1"
408
+ resolved "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz"
409
+ integrity sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==
410
+
411
+ is-alphanumerical@^2.0.0:
412
+ version "2.0.1"
413
+ resolved "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz"
414
+ integrity sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==
415
+ dependencies:
416
+ is-alphabetical "^2.0.0"
417
+ is-decimal "^2.0.0"
418
+
419
+ is-decimal@^2.0.0:
420
+ version "2.0.1"
421
+ resolved "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz"
422
+ integrity sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==
423
+
424
+ is-extendable@^0.1.0:
425
+ version "0.1.1"
426
+ resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz"
427
+ integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==
428
+
429
+ is-hexadecimal@^2.0.0:
430
+ version "2.0.1"
431
+ resolved "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz"
432
+ integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==
433
+
434
+ is-plain-obj@^4.0.0:
435
+ version "4.1.0"
436
+ resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz"
437
+ integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==
438
+
439
+ js-yaml@^3.13.1:
440
+ version "3.14.1"
441
+ resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz"
442
+ integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
443
+ dependencies:
444
+ argparse "^1.0.7"
445
+ esprima "^4.0.0"
446
+
447
+ kind-of@^6.0.0, kind-of@^6.0.2:
448
+ version "6.0.3"
449
+ resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz"
450
+ integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
451
+
452
+ longest-streak@^3.0.0:
453
+ version "3.1.0"
454
+ resolved "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz"
455
+ integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==
456
+
457
+ make-error@^1.1.1:
458
+ version "1.3.6"
459
+ resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz"
460
+ integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
461
+
462
+ math-intrinsics@^1.1.0:
463
+ version "1.1.0"
464
+ resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz"
465
+ integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
466
+
467
+ mdast-util-from-markdown@^2.0.0:
468
+ version "2.0.2"
469
+ resolved "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz"
470
+ integrity sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==
471
+ dependencies:
472
+ "@types/mdast" "^4.0.0"
473
+ "@types/unist" "^3.0.0"
474
+ decode-named-character-reference "^1.0.0"
475
+ devlop "^1.0.0"
476
+ mdast-util-to-string "^4.0.0"
477
+ micromark "^4.0.0"
478
+ micromark-util-decode-numeric-character-reference "^2.0.0"
479
+ micromark-util-decode-string "^2.0.0"
480
+ micromark-util-normalize-identifier "^2.0.0"
481
+ micromark-util-symbol "^2.0.0"
482
+ micromark-util-types "^2.0.0"
483
+ unist-util-stringify-position "^4.0.0"
484
+
485
+ mdast-util-mdx-expression@^2.0.0:
486
+ version "2.0.1"
487
+ resolved "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz"
488
+ integrity sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==
489
+ dependencies:
490
+ "@types/estree-jsx" "^1.0.0"
491
+ "@types/hast" "^3.0.0"
492
+ "@types/mdast" "^4.0.0"
493
+ devlop "^1.0.0"
494
+ mdast-util-from-markdown "^2.0.0"
495
+ mdast-util-to-markdown "^2.0.0"
496
+
497
+ mdast-util-mdx-jsx@^3.0.0:
498
+ version "3.2.0"
499
+ resolved "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz"
500
+ integrity sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==
501
+ dependencies:
502
+ "@types/estree-jsx" "^1.0.0"
503
+ "@types/hast" "^3.0.0"
504
+ "@types/mdast" "^4.0.0"
505
+ "@types/unist" "^3.0.0"
506
+ ccount "^2.0.0"
507
+ devlop "^1.1.0"
508
+ mdast-util-from-markdown "^2.0.0"
509
+ mdast-util-to-markdown "^2.0.0"
510
+ parse-entities "^4.0.0"
511
+ stringify-entities "^4.0.0"
512
+ unist-util-stringify-position "^4.0.0"
513
+ vfile-message "^4.0.0"
514
+
515
+ mdast-util-mdx@^3.0.0:
516
+ version "3.0.0"
517
+ resolved "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz"
518
+ integrity sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==
519
+ dependencies:
520
+ mdast-util-from-markdown "^2.0.0"
521
+ mdast-util-mdx-expression "^2.0.0"
522
+ mdast-util-mdx-jsx "^3.0.0"
523
+ mdast-util-mdxjs-esm "^2.0.0"
524
+ mdast-util-to-markdown "^2.0.0"
525
+
526
+ mdast-util-mdxjs-esm@^2.0.0:
527
+ version "2.0.1"
528
+ resolved "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz"
529
+ integrity sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==
530
+ dependencies:
531
+ "@types/estree-jsx" "^1.0.0"
532
+ "@types/hast" "^3.0.0"
533
+ "@types/mdast" "^4.0.0"
534
+ devlop "^1.0.0"
535
+ mdast-util-from-markdown "^2.0.0"
536
+ mdast-util-to-markdown "^2.0.0"
537
+
538
+ mdast-util-phrasing@^4.0.0:
539
+ version "4.1.0"
540
+ resolved "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz"
541
+ integrity sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==
542
+ dependencies:
543
+ "@types/mdast" "^4.0.0"
544
+ unist-util-is "^6.0.0"
545
+
546
+ mdast-util-to-markdown@^2.0.0:
547
+ version "2.1.2"
548
+ resolved "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz"
549
+ integrity sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==
550
+ dependencies:
551
+ "@types/mdast" "^4.0.0"
552
+ "@types/unist" "^3.0.0"
553
+ longest-streak "^3.0.0"
554
+ mdast-util-phrasing "^4.0.0"
555
+ mdast-util-to-string "^4.0.0"
556
+ micromark-util-classify-character "^2.0.0"
557
+ micromark-util-decode-string "^2.0.0"
558
+ unist-util-visit "^5.0.0"
559
+ zwitch "^2.0.0"
560
+
561
+ mdast-util-to-string@^4.0.0:
562
+ version "4.0.0"
563
+ resolved "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz"
564
+ integrity sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==
565
+ dependencies:
566
+ "@types/mdast" "^4.0.0"
567
+
568
+ micromark-core-commonmark@^2.0.0:
569
+ version "2.0.3"
570
+ resolved "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz"
571
+ integrity sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==
572
+ dependencies:
573
+ decode-named-character-reference "^1.0.0"
574
+ devlop "^1.0.0"
575
+ micromark-factory-destination "^2.0.0"
576
+ micromark-factory-label "^2.0.0"
577
+ micromark-factory-space "^2.0.0"
578
+ micromark-factory-title "^2.0.0"
579
+ micromark-factory-whitespace "^2.0.0"
580
+ micromark-util-character "^2.0.0"
581
+ micromark-util-chunked "^2.0.0"
582
+ micromark-util-classify-character "^2.0.0"
583
+ micromark-util-html-tag-name "^2.0.0"
584
+ micromark-util-normalize-identifier "^2.0.0"
585
+ micromark-util-resolve-all "^2.0.0"
586
+ micromark-util-subtokenize "^2.0.0"
587
+ micromark-util-symbol "^2.0.0"
588
+ micromark-util-types "^2.0.0"
589
+
590
+ micromark-extension-mdx-expression@^3.0.0:
591
+ version "3.0.1"
592
+ resolved "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz"
593
+ integrity sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==
594
+ dependencies:
595
+ "@types/estree" "^1.0.0"
596
+ devlop "^1.0.0"
597
+ micromark-factory-mdx-expression "^2.0.0"
598
+ micromark-factory-space "^2.0.0"
599
+ micromark-util-character "^2.0.0"
600
+ micromark-util-events-to-acorn "^2.0.0"
601
+ micromark-util-symbol "^2.0.0"
602
+ micromark-util-types "^2.0.0"
603
+
604
+ micromark-extension-mdx-jsx@^3.0.0:
605
+ version "3.0.2"
606
+ resolved "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz"
607
+ integrity sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==
608
+ dependencies:
609
+ "@types/estree" "^1.0.0"
610
+ devlop "^1.0.0"
611
+ estree-util-is-identifier-name "^3.0.0"
612
+ micromark-factory-mdx-expression "^2.0.0"
613
+ micromark-factory-space "^2.0.0"
614
+ micromark-util-character "^2.0.0"
615
+ micromark-util-events-to-acorn "^2.0.0"
616
+ micromark-util-symbol "^2.0.0"
617
+ micromark-util-types "^2.0.0"
618
+ vfile-message "^4.0.0"
619
+
620
+ micromark-extension-mdx-md@^2.0.0:
621
+ version "2.0.0"
622
+ resolved "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz"
623
+ integrity sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==
624
+ dependencies:
625
+ micromark-util-types "^2.0.0"
626
+
627
+ micromark-extension-mdxjs-esm@^3.0.0:
628
+ version "3.0.0"
629
+ resolved "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz"
630
+ integrity sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==
631
+ dependencies:
632
+ "@types/estree" "^1.0.0"
633
+ devlop "^1.0.0"
634
+ micromark-core-commonmark "^2.0.0"
635
+ micromark-util-character "^2.0.0"
636
+ micromark-util-events-to-acorn "^2.0.0"
637
+ micromark-util-symbol "^2.0.0"
638
+ micromark-util-types "^2.0.0"
639
+ unist-util-position-from-estree "^2.0.0"
640
+ vfile-message "^4.0.0"
641
+
642
+ micromark-extension-mdxjs@^3.0.0:
643
+ version "3.0.0"
644
+ resolved "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz"
645
+ integrity sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==
646
+ dependencies:
647
+ acorn "^8.0.0"
648
+ acorn-jsx "^5.0.0"
649
+ micromark-extension-mdx-expression "^3.0.0"
650
+ micromark-extension-mdx-jsx "^3.0.0"
651
+ micromark-extension-mdx-md "^2.0.0"
652
+ micromark-extension-mdxjs-esm "^3.0.0"
653
+ micromark-util-combine-extensions "^2.0.0"
654
+ micromark-util-types "^2.0.0"
655
+
656
+ micromark-factory-destination@^2.0.0:
657
+ version "2.0.1"
658
+ resolved "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz"
659
+ integrity sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==
660
+ dependencies:
661
+ micromark-util-character "^2.0.0"
662
+ micromark-util-symbol "^2.0.0"
663
+ micromark-util-types "^2.0.0"
664
+
665
+ micromark-factory-label@^2.0.0:
666
+ version "2.0.1"
667
+ resolved "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz"
668
+ integrity sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==
669
+ dependencies:
670
+ devlop "^1.0.0"
671
+ micromark-util-character "^2.0.0"
672
+ micromark-util-symbol "^2.0.0"
673
+ micromark-util-types "^2.0.0"
674
+
675
+ micromark-factory-mdx-expression@^2.0.0:
676
+ version "2.0.3"
677
+ resolved "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz"
678
+ integrity sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==
679
+ dependencies:
680
+ "@types/estree" "^1.0.0"
681
+ devlop "^1.0.0"
682
+ micromark-factory-space "^2.0.0"
683
+ micromark-util-character "^2.0.0"
684
+ micromark-util-events-to-acorn "^2.0.0"
685
+ micromark-util-symbol "^2.0.0"
686
+ micromark-util-types "^2.0.0"
687
+ unist-util-position-from-estree "^2.0.0"
688
+ vfile-message "^4.0.0"
689
+
690
+ micromark-factory-space@^2.0.0:
691
+ version "2.0.1"
692
+ resolved "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz"
693
+ integrity sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==
694
+ dependencies:
695
+ micromark-util-character "^2.0.0"
696
+ micromark-util-types "^2.0.0"
697
+
698
+ micromark-factory-title@^2.0.0:
699
+ version "2.0.1"
700
+ resolved "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz"
701
+ integrity sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==
702
+ dependencies:
703
+ micromark-factory-space "^2.0.0"
704
+ micromark-util-character "^2.0.0"
705
+ micromark-util-symbol "^2.0.0"
706
+ micromark-util-types "^2.0.0"
707
+
708
+ micromark-factory-whitespace@^2.0.0:
709
+ version "2.0.1"
710
+ resolved "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz"
711
+ integrity sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==
712
+ dependencies:
713
+ micromark-factory-space "^2.0.0"
714
+ micromark-util-character "^2.0.0"
715
+ micromark-util-symbol "^2.0.0"
716
+ micromark-util-types "^2.0.0"
717
+
718
+ micromark-util-character@^2.0.0:
719
+ version "2.1.1"
720
+ resolved "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz"
721
+ integrity sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==
722
+ dependencies:
723
+ micromark-util-symbol "^2.0.0"
724
+ micromark-util-types "^2.0.0"
725
+
726
+ micromark-util-chunked@^2.0.0:
727
+ version "2.0.1"
728
+ resolved "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz"
729
+ integrity sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==
730
+ dependencies:
731
+ micromark-util-symbol "^2.0.0"
732
+
733
+ micromark-util-classify-character@^2.0.0:
734
+ version "2.0.1"
735
+ resolved "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz"
736
+ integrity sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==
737
+ dependencies:
738
+ micromark-util-character "^2.0.0"
739
+ micromark-util-symbol "^2.0.0"
740
+ micromark-util-types "^2.0.0"
741
+
742
+ micromark-util-combine-extensions@^2.0.0:
743
+ version "2.0.1"
744
+ resolved "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz"
745
+ integrity sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==
746
+ dependencies:
747
+ micromark-util-chunked "^2.0.0"
748
+ micromark-util-types "^2.0.0"
749
+
750
+ micromark-util-decode-numeric-character-reference@^2.0.0:
751
+ version "2.0.2"
752
+ resolved "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz"
753
+ integrity sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==
754
+ dependencies:
755
+ micromark-util-symbol "^2.0.0"
756
+
757
+ micromark-util-decode-string@^2.0.0:
758
+ version "2.0.1"
759
+ resolved "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz"
760
+ integrity sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==
761
+ dependencies:
762
+ decode-named-character-reference "^1.0.0"
763
+ micromark-util-character "^2.0.0"
764
+ micromark-util-decode-numeric-character-reference "^2.0.0"
765
+ micromark-util-symbol "^2.0.0"
766
+
767
+ micromark-util-encode@^2.0.0:
768
+ version "2.0.1"
769
+ resolved "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz"
770
+ integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==
771
+
772
+ micromark-util-events-to-acorn@^2.0.0:
773
+ version "2.0.3"
774
+ resolved "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz"
775
+ integrity sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==
776
+ dependencies:
777
+ "@types/estree" "^1.0.0"
778
+ "@types/unist" "^3.0.0"
779
+ devlop "^1.0.0"
780
+ estree-util-visit "^2.0.0"
781
+ micromark-util-symbol "^2.0.0"
782
+ micromark-util-types "^2.0.0"
783
+ vfile-message "^4.0.0"
784
+
785
+ micromark-util-html-tag-name@^2.0.0:
786
+ version "2.0.1"
787
+ resolved "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz"
788
+ integrity sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==
789
+
790
+ micromark-util-normalize-identifier@^2.0.0:
791
+ version "2.0.1"
792
+ resolved "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz"
793
+ integrity sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==
794
+ dependencies:
795
+ micromark-util-symbol "^2.0.0"
796
+
797
+ micromark-util-resolve-all@^2.0.0:
798
+ version "2.0.1"
799
+ resolved "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz"
800
+ integrity sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==
801
+ dependencies:
802
+ micromark-util-types "^2.0.0"
803
+
804
+ micromark-util-sanitize-uri@^2.0.0:
805
+ version "2.0.1"
806
+ resolved "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz"
807
+ integrity sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==
808
+ dependencies:
809
+ micromark-util-character "^2.0.0"
810
+ micromark-util-encode "^2.0.0"
811
+ micromark-util-symbol "^2.0.0"
812
+
813
+ micromark-util-subtokenize@^2.0.0:
814
+ version "2.1.0"
815
+ resolved "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz"
816
+ integrity sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==
817
+ dependencies:
818
+ devlop "^1.0.0"
819
+ micromark-util-chunked "^2.0.0"
820
+ micromark-util-symbol "^2.0.0"
821
+ micromark-util-types "^2.0.0"
822
+
823
+ micromark-util-symbol@^2.0.0:
824
+ version "2.0.1"
825
+ resolved "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz"
826
+ integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==
827
+
828
+ micromark-util-types@^2.0.0:
829
+ version "2.0.2"
830
+ resolved "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz"
831
+ integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==
832
+
833
+ micromark@^4.0.0:
834
+ version "4.0.2"
835
+ resolved "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz"
836
+ integrity sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==
837
+ dependencies:
838
+ "@types/debug" "^4.0.0"
839
+ debug "^4.0.0"
840
+ decode-named-character-reference "^1.0.0"
841
+ devlop "^1.0.0"
842
+ micromark-core-commonmark "^2.0.0"
843
+ micromark-factory-space "^2.0.0"
844
+ micromark-util-character "^2.0.0"
845
+ micromark-util-chunked "^2.0.0"
846
+ micromark-util-combine-extensions "^2.0.0"
847
+ micromark-util-decode-numeric-character-reference "^2.0.0"
848
+ micromark-util-encode "^2.0.0"
849
+ micromark-util-normalize-identifier "^2.0.0"
850
+ micromark-util-resolve-all "^2.0.0"
851
+ micromark-util-sanitize-uri "^2.0.0"
852
+ micromark-util-subtokenize "^2.0.0"
853
+ micromark-util-symbol "^2.0.0"
854
+ micromark-util-types "^2.0.0"
855
+
856
+ mime-db@1.52.0:
857
+ version "1.52.0"
858
+ resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
859
+ integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
860
+
861
+ mime-types@^2.1.12, mime-types@^2.1.35:
862
+ version "2.1.35"
863
+ resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
864
+ integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
865
+ dependencies:
866
+ mime-db "1.52.0"
867
+
868
+ mime@^3.0.0:
869
+ version "3.0.0"
870
+ resolved "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz"
871
+ integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==
872
+
873
+ ms@^2.1.3:
874
+ version "2.1.3"
875
+ resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
876
+ integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
877
+
878
+ node-domexception@^1.0.0:
879
+ version "1.0.0"
880
+ resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz"
881
+ integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
882
+
883
+ node-fetch@^2.6.1:
884
+ version "2.7.0"
885
+ resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz"
886
+ integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
887
+ dependencies:
888
+ whatwg-url "^5.0.0"
889
+
890
+ node-fetch@^2.7.0:
891
+ version "2.7.0"
892
+ resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz"
893
+ integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
894
+ dependencies:
895
+ whatwg-url "^5.0.0"
896
+
897
+ node-fetch@^3.3.2:
898
+ version "3.3.2"
899
+ resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz"
900
+ integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==
901
+ dependencies:
902
+ data-uri-to-buffer "^4.0.0"
903
+ fetch-blob "^3.1.4"
904
+ formdata-polyfill "^4.0.10"
905
+
906
+ notion-to-md@^4.0.0-alpha:
907
+ version "4.0.0-alpha.7"
908
+ resolved "https://registry.npmjs.org/notion-to-md/-/notion-to-md-4.0.0-alpha.7.tgz"
909
+ integrity sha512-3kocKMEVcivy2ccuv2uZDJQFKXdvRmsujbN2GeOwP6yoNqhj/c/fmXroqPkk4XXRqNdJB2jzf5NPhPSWpuZkdA==
910
+ dependencies:
911
+ mime "^3.0.0"
912
+ node-fetch "^2.7.0"
913
+ ts-node "^10.9.2"
914
+
915
+ parse-entities@^4.0.0:
916
+ version "4.0.2"
917
+ resolved "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz"
918
+ integrity sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==
919
+ dependencies:
920
+ "@types/unist" "^2.0.0"
921
+ character-entities-legacy "^3.0.0"
922
+ character-reference-invalid "^2.0.0"
923
+ decode-named-character-reference "^1.0.0"
924
+ is-alphanumerical "^2.0.0"
925
+ is-decimal "^2.0.0"
926
+ is-hexadecimal "^2.0.0"
927
+
928
+ remark-mdx@^3.0.0:
929
+ version "3.1.1"
930
+ resolved "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz"
931
+ integrity sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==
932
+ dependencies:
933
+ mdast-util-mdx "^3.0.0"
934
+ micromark-extension-mdxjs "^3.0.0"
935
+
936
+ remark-parse@^11.0.0:
937
+ version "11.0.0"
938
+ resolved "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz"
939
+ integrity sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==
940
+ dependencies:
941
+ "@types/mdast" "^4.0.0"
942
+ mdast-util-from-markdown "^2.0.0"
943
+ micromark-util-types "^2.0.0"
944
+ unified "^11.0.0"
945
+
946
+ remark-stringify@^11.0.0:
947
+ version "11.0.0"
948
+ resolved "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz"
949
+ integrity sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==
950
+ dependencies:
951
+ "@types/mdast" "^4.0.0"
952
+ mdast-util-to-markdown "^2.0.0"
953
+ unified "^11.0.0"
954
+
955
+ section-matter@^1.0.0:
956
+ version "1.0.0"
957
+ resolved "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz"
958
+ integrity sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==
959
+ dependencies:
960
+ extend-shallow "^2.0.1"
961
+ kind-of "^6.0.0"
962
+
963
+ sprintf-js@~1.0.2:
964
+ version "1.0.3"
965
+ resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"
966
+ integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
967
+
968
+ stringify-entities@^4.0.0:
969
+ version "4.0.4"
970
+ resolved "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz"
971
+ integrity sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==
972
+ dependencies:
973
+ character-entities-html4 "^2.0.0"
974
+ character-entities-legacy "^3.0.0"
975
+
976
+ strip-bom-string@^1.0.0:
977
+ version "1.0.0"
978
+ resolved "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz"
979
+ integrity sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==
980
+
981
+ tr46@~0.0.3:
982
+ version "0.0.3"
983
+ resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz"
984
+ integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
985
+
986
+ trough@^2.0.0:
987
+ version "2.2.0"
988
+ resolved "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz"
989
+ integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==
990
+
991
+ ts-node@^10.9.2:
992
+ version "10.9.2"
993
+ resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz"
994
+ integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==
995
+ dependencies:
996
+ "@cspotcode/source-map-support" "^0.8.0"
997
+ "@tsconfig/node10" "^1.0.7"
998
+ "@tsconfig/node12" "^1.0.7"
999
+ "@tsconfig/node14" "^1.0.0"
1000
+ "@tsconfig/node16" "^1.0.2"
1001
+ acorn "^8.4.1"
1002
+ acorn-walk "^8.1.1"
1003
+ arg "^4.1.0"
1004
+ create-require "^1.1.0"
1005
+ diff "^4.0.1"
1006
+ make-error "^1.1.1"
1007
+ v8-compile-cache-lib "^3.0.1"
1008
+ yn "3.1.1"
1009
+
1010
+ typescript@>=2.7:
1011
+ version "5.9.2"
1012
+ resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz"
1013
+ integrity sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==
1014
+
1015
+ undici-types@~7.12.0:
1016
+ version "7.12.0"
1017
+ resolved "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz"
1018
+ integrity sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==
1019
+
1020
+ unified@^11.0.0, unified@^11.0.4:
1021
+ version "11.0.5"
1022
+ resolved "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz"
1023
+ integrity sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==
1024
+ dependencies:
1025
+ "@types/unist" "^3.0.0"
1026
+ bail "^2.0.0"
1027
+ devlop "^1.0.0"
1028
+ extend "^3.0.0"
1029
+ is-plain-obj "^4.0.0"
1030
+ trough "^2.0.0"
1031
+ vfile "^6.0.0"
1032
+
1033
+ unist-util-is@^6.0.0:
1034
+ version "6.0.0"
1035
+ resolved "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz"
1036
+ integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==
1037
+ dependencies:
1038
+ "@types/unist" "^3.0.0"
1039
+
1040
+ unist-util-position-from-estree@^2.0.0:
1041
+ version "2.0.0"
1042
+ resolved "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz"
1043
+ integrity sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==
1044
+ dependencies:
1045
+ "@types/unist" "^3.0.0"
1046
+
1047
+ unist-util-stringify-position@^4.0.0:
1048
+ version "4.0.0"
1049
+ resolved "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz"
1050
+ integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==
1051
+ dependencies:
1052
+ "@types/unist" "^3.0.0"
1053
+
1054
+ unist-util-visit-parents@^6.0.0:
1055
+ version "6.0.1"
1056
+ resolved "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz"
1057
+ integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==
1058
+ dependencies:
1059
+ "@types/unist" "^3.0.0"
1060
+ unist-util-is "^6.0.0"
1061
+
1062
+ unist-util-visit@^5.0.0:
1063
+ version "5.0.0"
1064
+ resolved "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz"
1065
+ integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==
1066
+ dependencies:
1067
+ "@types/unist" "^3.0.0"
1068
+ unist-util-is "^6.0.0"
1069
+ unist-util-visit-parents "^6.0.0"
1070
+
1071
+ v8-compile-cache-lib@^3.0.1:
1072
+ version "3.0.1"
1073
+ resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz"
1074
+ integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
1075
+
1076
+ vfile-message@^4.0.0:
1077
+ version "4.0.3"
1078
+ resolved "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz"
1079
+ integrity sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==
1080
+ dependencies:
1081
+ "@types/unist" "^3.0.0"
1082
+ unist-util-stringify-position "^4.0.0"
1083
+
1084
+ vfile@^6.0.0:
1085
+ version "6.0.3"
1086
+ resolved "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz"
1087
+ integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==
1088
+ dependencies:
1089
+ "@types/unist" "^3.0.0"
1090
+ vfile-message "^4.0.0"
1091
+
1092
+ web-streams-polyfill@^3.0.3:
1093
+ version "3.3.3"
1094
+ resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz"
1095
+ integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==
1096
+
1097
+ webidl-conversions@^3.0.0:
1098
+ version "3.0.1"
1099
+ resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz"
1100
+ integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
1101
+
1102
+ whatwg-url@^5.0.0:
1103
+ version "5.0.0"
1104
+ resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz"
1105
+ integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
1106
+ dependencies:
1107
+ tr46 "~0.0.3"
1108
+ webidl-conversions "^3.0.0"
1109
+
1110
+ yn@3.1.1:
1111
+ version "3.1.1"
1112
+ resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz"
1113
+ integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
1114
+
1115
+ zwitch@^2.0.0:
1116
+ version "2.0.4"
1117
+ resolved "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz"
1118
+ integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==