noblog 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +4 -2
  2. package/dist/cmds/generate.js +9 -7
  3. package/dist/cmds/generate.js.map +1 -1
  4. package/dist/index.js +24 -10
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/nomd/nomd.d.ts.map +1 -1
  7. package/dist/lib/nomd/nomd.js +16 -1
  8. package/dist/lib/nomd/nomd.js.map +1 -1
  9. package/dist/lib/nosm/default.js +0 -1
  10. package/dist/lib/nosm/default.js.map +1 -1
  11. package/dist/lib/nosm/nosm.js +1 -0
  12. package/dist/lib/nosm/nosm.js.map +1 -1
  13. package/dist/lib/notion/client.d.ts +1 -0
  14. package/dist/lib/notion/client.d.ts.map +1 -1
  15. package/dist/lib/notion/client.js +16 -0
  16. package/dist/lib/notion/client.js.map +1 -1
  17. package/package.json +44 -28
  18. package/output/00097769-2aac-4b6f-8f37-d9b658d46b2e +0 -435
  19. package/output/1707063b-7c0a-4572-9526-f7db2f7bbc49 +0 -162
  20. package/output/1ed588e1-2604-4e33-9685-722b03f66374 +0 -89
  21. package/output/2b1c0d09-40f7-4151-a053-11391f775ece +0 -33
  22. package/output/336b6a5a-f383-4d6c-8fe5-54c9a918c4c0 +0 -264
  23. package/output/37b77a0a-e233-405a-93f5-a60a808eee35 +0 -72
  24. package/output/3ac660cb-9503-4e88-8939-5aee30c98351 +0 -24
  25. package/output/5bc1b19a-8350-48b3-aa19-8073561ad45b +0 -109
  26. package/output/765976b5-7790-497b-8634-4be1d4770e16 +0 -111
  27. package/output/90f2cb44-90ab-40b7-a736-02bfb83a314c +0 -1167
  28. package/output/99a7b0c5-f9de-403c-95ec-eba3d700a195 +0 -49
  29. package/output/c844b66d-0bf3-4f89-bbe1-5974f2de454b +0 -193
  30. package/output/cd6299f7-54bb-4160-8735-16e629422a57 +0 -193
  31. package/output/d462cfca-0a39-4b65-8430-4eae35edbb76 +0 -27
  32. package/output/daafaad2-2570-43da-8e9e-f95a1b29ca17 +0 -12
  33. package/output/e45ef6bf-6c30-4fb2-9efc-a1984e5dc530 +0 -84
  34. package/output/sub/05009ca9-cb39-4356-8f24-c0db39cb4661 +0 -64
  35. package/output/sub/0b903359-2337-4ad9-adef-e77b69142581 +0 -328
  36. package/output/sub/18f0fae1-b7ca-40ea-b725-4e69404429dc +0 -109
  37. package/output/sub/232ae8ce-5ec3-4737-a18e-077774b84b1e +0 -940
  38. package/output/sub/34f4d4af-812e-4e56-a6dd-deb96e7f605c +0 -3
  39. package/output/sub/44331753-a264-40a4-b86f-eafd676b3da0 +0 -509
  40. package/output/sub/4bac355b-24f0-49e7-a317-e4f1cad63c79 +0 -65
  41. package/output/sub/4d6663a2-e93a-402e-89f8-a47cccc0c8cf +0 -76
  42. package/output/sub/5771c956-25bf-4714-bc2e-65959c9156d9 +0 -52
  43. package/output/sub/6ff91a18-494e-4ffc-a33b-b9fd2c36f43f +0 -42
  44. package/output/sub/81e4a2b0-05e2-4013-bd41-14b144979d91 +0 -0
  45. package/output/sub/99ef1497-0b94-490f-b9a0-f54b0e64194d +0 -6
  46. package/output/sub/9a33b8cc-101a-4f6a-9852-115595cd166b +0 -172
  47. package/output/sub/9c060d29-39a2-432b-b06b-b0bd91989a52 +0 -1159
  48. package/output/sub/a45b383c-d894-4480-8322-9c5d028722e3 +0 -9
  49. package/output/sub/b875bfe4-3c1e-4a8f-a56c-27a48ab874ca +0 -438
  50. package/output/sub/b8cbb8f9-f702-4a43-943c-02a6e39683d5 +0 -222
  51. package/output/sub/e339a686-bbb1-41d9-b6b5-18111e72a68c +0 -1392
  52. package/output/sub/e7931a8e-45ca-444a-8d6f-e6fed9415c26 +0 -110
  53. package/output/sub/e859552b-c597-49ed-9ede-fab320fafd87 +0 -150
  54. package/output/sub/f4237e72-fdce-44a4-b62c-659dce24b1ee +0 -107
  55. package/output/sub/ff7ea97c-55ac-471c-902f-b7a7bd101ee6 +0 -0
  56. package/src/cmds/generate.ts +0 -54
  57. package/src/index.ts +0 -22
  58. package/src/lib/nomd/cvter.ts +0 -143
  59. package/src/lib/nomd/nomd.ts +0 -88
  60. package/src/lib/nosm/default.ts +0 -0
  61. package/src/lib/nosm/nosm.ts +0 -3
  62. package/src/lib/notion/client.ts +0 -59
  63. package/src/lib/notion/object.ts +0 -176
  64. package/src/lib/notion/request.ts +0 -42
  65. package/tsconfig.json +0 -35
@@ -1,110 +0,0 @@
1
-
2
- # char
3
-
4
-
5
- 在老牌语言中,char通常是 1 Byte 的长度,储存一个ASCII字符。但是在rust中,字符是utf-8编码的,而utf-8采用的是变长编码 (长度为 1-4 Byte)。为了内存对齐,rust中最小的char长度应当是可以是2的幂次的,大于等于4的最小长度,也就是4。
6
-
7
-
8
- # 字符串的实现
9
-
10
-
11
- 字符串储存了文本的字面量,首先要明确这一点
12
-
13
-
14
- 然后需要理解的是,所有的类型,都是固定长度的,比如`i32`就是4个字节。而字符串的长度显然是不定的,是可变的,作为 byte sequence `[u8]` 被直接储存在可执行文件的 data section 中。
15
-
16
-
17
- ```rust
18
- ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
19
- │ H │ e │ l │ l │ o │ │ W │ o │ r │ l │ d │
20
- └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
21
- ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
22
- │ 72 │ 101 │ 108 │ 108 │ 111 │ 32 │ 87 │ 111 │ 114 │ 108 │ 100 │
23
- └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
24
- ```
25
-
26
-
27
- ### C 的方法
28
-
29
-
30
- 在 C 中,`\0` 作为字符串的结束符 `append` 在字符串的末尾。为了确定字符串的长度,就需要遍历每个 byte。
31
-
32
-
33
- ### Rust 的方法
34
-
35
-
36
- ```rust
37
- let arr = [1, 2, 3, 4]; // array
38
-
39
- // 以下是slice
40
- let slice1 = &arr[..];
41
- let slice2 = &arr[1..3];
42
- let slice3 = &arr[..3];
43
- let slice4 = &arr[1..];
44
- ```
45
-
46
-
47
- rust中的切片类型就是 `&[T]`,表示连续内存序列(数组) `[T]` 的`视图`,这个指针在最底层是通过称为胖指针(`FatPtr`)的结构体来模拟的:
48
-
49
-
50
- ```rust
51
- // src/libcore/ptr/mod.rs
52
-
53
- // Repr<T> 联合体定义了不同种类的指针,用于引用数组(切片)的数据。这个联合体使用了 repr(C) 属性,这表示 Rust 应该按照 C 语言的内存布局规则来布局这个联合体,以便与其他语言进行交互。
54
-
55
- #[repr(C)]
56
- pub(crate) union Repr<T> {
57
- pub(crate) rust: *const [T],
58
- rust_mut: *mut [T],
59
- pub(crate) raw: FatPtr<T>,
60
- }
61
-
62
- #[repr(C)]
63
- pub(crate) struct FatPtr<T> {
64
- data: *const T,
65
- pub(crate) len: usize,
66
- }
67
- ```
68
-
69
-
70
- 在内存布局(memory layout)上, 切片变量和`FatPtr`类型的变量共享同一片内存空间,而FatPtr中则保存了"切片"的必要特征:
71
-
72
- - data: 指向若干同质连续数据内存首地址的指针;
73
- - len: `data`指针所指向的连续内存段中存放的元素数目;
74
-
75
- 而借助于Rust类型系统的优势,标准库在`[T]`类型上定义的方法和trait则完全封装了底层负责解释指针含义的工作(这部分解释工作需要依赖unsafe rust来实现)。
76
-
77
-
78
- str 在本质上是 `‘static str`,对应的,`s: &’static str = “foo”`。
79
-
80
-
81
- str在rust中是很特殊的一个存在,由于utf-8动态编码的性质,如果你想要获得str的第n个字符
82
-
83
-
84
- ```c++
85
- &str.chars().nth(n)
86
- ```
87
-
88
-
89
- ## 浅浅对比 String
90
-
91
-
92
- ```rust
93
- pub struct String {
94
- vec: Vec<u8>,
95
- }
96
- ```
97
-
98
-
99
- rust的String实际上就很直观了,底层就是一个vec,只不过因为是private的,必须要通过暴露的借口进行初始化。
100
-
101
-
102
- ```rust
103
- pub const fn new() -> String {
104
- String { vec: Vec::new() }
105
- }
106
- ```
107
-
108
-
109
- 同时,在rust中,str被硬编码在了程序的二进制文件中(static data),而&str正是硬编码后的str在文件中的地址。而string则是被创建在堆上的。
110
-
@@ -1,150 +0,0 @@
1
-
2
- # **Raft paper summarization**
3
-
4
-
5
- [Raft paper summarization](9a33b8cc-101a-4f6a-9852-115595cd166b.md)
6
-
7
-
8
- # **Part 2A: leader election**
9
-
10
-
11
- > Implement Raft leader election and heartbeats (`AppendEntries` RPCs with no log entries). The goal for Part 2A is for a single leader to be elected, for the leader to remain the leader if there are no failures, and for a new leader to take over if the old leader fails or if packets to/from the old leader are lost. Run `go test -run` 2A to test your 2A code.
12
-
13
- <details>
14
- <summary>**Goal**</summary>
15
-
16
- Sending and receiving RequestVote RPCs
17
-
18
-
19
- </details>
20
-
21
-
22
- ![figure2-2A.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/816ab0e2-0bbf-4fbc-a791-de833db1ae15/bf799b2f-6287-4999-b548-4974769458ec/figure2-2A.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45HZZMZUHI%2F20240727%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20240727T092513Z&X-Amz-Expires=3600&X-Amz-Signature=e96fc27813785c92bd6144647840aa67ec78e540237b7fac3cd29a2b67a87110&X-Amz-SignedHeaders=host&x-id=GetObject)
23
-
24
-
25
- Step**s:**
26
-
27
- <details>
28
- <summary>Add the state for leader election to the `Raft` struct in `raft.go`</summary>
29
-
30
- ```go
31
- // 2A
32
- currTerm int32
33
- votedFor int
34
- logEntries []logEntry
35
- ```
36
-
37
-
38
- </details>
39
-
40
- <details>
41
- <summary>define a struct to hold information about each log entry</summary>
42
-
43
- ```go
44
- type logEntry struct {
45
- command string
46
- term int32
47
- }
48
- ```
49
-
50
-
51
- </details>
52
-
53
- <details>
54
- <summary>Fill in the `RequestVoteArgs` and `RequestVoteReply` structs</summary>
55
-
56
- ```go
57
- // example RequestVote RPC arguments structure.
58
- // field names must start with capital letters!
59
- type RequestVoteArgs struct {
60
- // Your data here (2A, 2B).
61
- term int32
62
- candidateId int
63
- lastLogIndex int32
64
- lastLogTerm int32
65
- }
66
-
67
- // example RequestVote RPC reply structure.
68
- // field names must start with capital letters!
69
- type RequestVoteReply struct {
70
- // Your data here (2A).
71
- term int32
72
- voteGranted bool
73
- }
74
- ```
75
-
76
-
77
- </details>
78
-
79
- <details>
80
- <summary>Modify `Make()` to create a background goroutine that will kick off leader election periodically by sending out `RequestVote` RPCs when it hasn't heard from another peer for a while.</summary>
81
-
82
- ```go
83
-
84
- ```
85
-
86
-
87
- </details>
88
-
89
- <details>
90
- <summary>Implement the `RequestVote()` RPC handler so that servers will vote for one another</summary>
91
-
92
- </details>
93
-
94
- <details>
95
- <summary>To implement heartbeats, define an `AppendEntries` RPC struct, and have the leader send them out periodically.</summary>
96
-
97
- </details>
98
-
99
- <details>
100
- <summary>Write an `AppendEntries` RPC handler method that resets the election timeout so that other servers don't step forward as leaders when one has already been elected.</summary>
101
-
102
- </details>
103
-
104
- <details>
105
- <summary>The tester requires that the leader send heartbeat RPCs no more than ten times per second.</summary>
106
-
107
- </details>
108
-
109
-
110
- # **Part 2B: log**
111
-
112
- - [x] 从当前的Leader处收到AppendEntries RPCs或向Candidate投票的时候reset ElectionTimeout()
113
- <details>
114
- <summary>All you need to know about index</summary>
115
- 1. Leader开始,接受Client的命令,将Command append到log尾部,让Server更新状态机
116
- 2. Server更新状态机中的log并返回true的reply。注意,此时Leader和Server的commitIndex都没有发生变化,变化的只是Leader中的nextIndex和matchIndex。这里在Term相同的情况下返回false的reply,会让Leader中的nextIndex自减。
117
- 3. 每当一个AppendEntries返回true以后,触发信号,让Leader检查commitIndex是否能够更新,能更新就增大commitIndex,在下次heartbeat的时候就会更新Server中的commitIndex。
118
-
119
- </details>
120
-
121
-
122
- # **Part 2C: persistence**
123
-
124
-
125
- # **Part 2D: log compaction**
126
-
127
-
128
- 实现Snapshot,index意味着所有index之前的log都将被丢弃
129
-
130
-
131
- Follower应该使用快照来赶上整体的进度
132
-
133
-
134
- 忽略CondInstallSnapshot
135
-
136
-
137
- 可以先将index设为0,慢慢增大来尝试通过2D的第一个test
138
-
139
-
140
- 如果leader没有能让follower up-to-date的条目,则发送快照
141
-
142
-
143
- 不要实现论文中figure 13的offset
144
-
145
-
146
- 需要让GC可以access被丢弃的条目,即不存在可访问被丢弃条目的指针
147
-
148
-
149
- 保存最新快照的lastIncludedTerm和lastIncludedIndex
150
-
@@ -1,107 +0,0 @@
1
-
2
- 在仅支持进程的操作系统中,进程是拥有资源和独立调度的基本单位。在引入线程的操作系统中,**线程是独立调度的基本单位,进程是资源拥有的基本单位**。在同一进程中,线程的切换不会引起进程切换。在不同进程中进行线程切换,如从一个进程内的线程切换到另一个进程中的线程时,会引起进程切换
3
-
4
-
5
- 线程创建、管理、调度等采用的方式称为线程模型。线程模型一般分为以下三种:
6
-
7
- - 内核级线程(Kernel Level Thread)模型
8
- - 用户级线程(User Level Thread)模型
9
- - 两级线程模型,也称混合型线程模型
10
-
11
- 三大线程模型最大差异就在于用户级线程与内核调度实体KSE(KSE,Kernel Scheduling Entity)之间的对应关系。**KSE是Kernel Scheduling Entity的缩写,其是可被操作系统内核调度器调度的对象实体,是操作系统内核的最小调度单元,可以简单理解为内核级线程**。
12
-
13
-
14
- **用户级线程即协程**,由应用程序创建与管理,协程必须与内核级线程绑定之后才能执行。**线程由 CPU 调度是抢占式的,协程由用户态调度是协作式的,一个协程让出 CPU 后,才执行下一个协程**。
15
-
16
-
17
- 用户级线程(ULT)与内核级线程(KLT)比较:
18
-
19
-
20
- | 特性 | 用户级线程 | 内核级线程 |
21
- | ---------- | ------------------------------- | ------------------------------ |
22
- | 创建者 | **应用程序** | **内核** |
23
- | 操作系统是否感知存在 | 否 | 是 |
24
- | 开销成本 | **创建成本低,上下文切换成本低**,上下文切换不需要硬件支持 | **创建成本高,上下文切换成本高**,上下文切换需要硬件支持 |
25
- | 如果线程阻塞 | 整个进程将被阻塞。即不能利用多处理来发挥并发优势 | 其他线程可以继续执行,进程不会阻塞 |
26
- | 案例 | Java thread, POSIX threads | Window Solaris |
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
- ### 内核级线程模型
48
-
49
-
50
- ![ult_klt_1_1.jpg](https://static.cyub.vip/images/202008/ult_klt_1_1.jpg)
51
-
52
-
53
- **内核级线程模型中用户线程与内核线程是一对一关系(1 : 1)**。**线程的创建、销毁、切换工作都是由内核完成的**。应用程序不参与线程的管理工作,只能调用内核级线程编程接口(应用程序创建一个新线程或撤销一个已有线程时,都会进行一个系统调用)。每个用户线程都会被绑定到一个内核线程。用户线程在其生命期内都会绑定到该内核线程。一旦用户线程终止,两个线程都将离开系统。
54
-
55
-
56
- 操作系统调度器管理、调度并分派这些线程。运行时库为每个用户级线程请求一个内核级线程。操作系统的内存管理和调度子系统必须要考虑到数量巨大的用户级线程。操作系统为每个线程创建上下文。进程的每个线程在资源可用时都可以被指派到处理器内核。
57
-
58
-
59
- 内核级线程模型有如下优点:
60
-
61
- - 在多处理器系统中,内核能够并行执行同一进程内的多个线程
62
- - 如果进程中的一个线程被阻塞,不会阻塞其他线程,是能够切换同一进程内的其他线程继续执行
63
- - 当一个线程阻塞时,内核根据选择可以运行另一个进程的线程,而用户空间实现的线程中,运行时系统始终运行自己进程中的线程
64
-
65
- 缺点:
66
-
67
- - 线程的创建与删除都需要CPU参与,成本大
68
-
69
- ### 用户级线程模型
70
-
71
-
72
- ![ult_klt_n_1.jpg](https://static.cyub.vip/images/202008/ult_klt_n_1.jpg)
73
-
74
-
75
- **用户线程模型中的用户线程与内核线程KSE是多对一关系(N : 1)**。**线程的创建、销毁以及线程之间的协调、同步等工作都是在用户态完成**,具体来说就是由应用程序的线程库来完成。**内核对这些是无感知的,内核此时的调度都是基于进程的**。线程的并发处理从宏观来看,任意时刻每个进程只能够有一个线程在运行,且只有一个处理器内核会被分配给该进程。
76
-
77
-
78
- 从上图中可以看出来:库调度器从进程的多个线程中选择一个线程,然后该线程和该进程允许的一个内核线程关联起来。内核线程将被操作系统调度器指派到处理器内核。用户级线程是一种”多对一”的线程映射
79
-
80
-
81
- 用户级线程有如下优点:
82
-
83
- - 创建和销毁线程、线程切换代价等线程管理的代价比内核线程少得多, 因为保存线程状态的过程和调用程序都只是本地过程
84
- - 线程能够利用的表空间和堆栈空间比内核级线程多
85
-
86
- 缺点:
87
-
88
- - 线程发生I/O或页面故障引起的阻塞时,如果调用阻塞系统调用则内核由于不知道有多线程的存在,而会阻塞整个进程从而阻塞所有线程, 因此同一进程中只能同时有一个线程在运行
89
- - 资源调度按照进程进行,多个处理机下,同一个进程中的线程只能在同一个处理机下分时复用
90
-
91
- ### 两级线程模型
92
-
93
-
94
- ![ult_klt_n_m.jpg](https://static.cyub.vip/images/202008/ult_klt_n_m.jpg)
95
-
96
-
97
- **两级线程模型中用户线程与内核线程是一对一关系(N : M)**。两级线程模型充分吸收上面两种模型的优点,尽量规避缺点。其线程创建在用户空间中完成,线程的调度和同步也在应用程序中进行。一个应用程序中的多个用户级线程被绑定到一些(小于或等于用户级线程的数目)内核级线程上。
98
-
99
-
100
- ### Golang的线程模型
101
-
102
-
103
- **Golang在底层实现了混合型线程模型**。M即系统线程,由系统调用产生,一个M关联一个KSE,即两级线程模型中的系统线程。G为Groutine,即两级线程模型的的应用及线程。M与G的关系是N:M。
104
-
105
-
106
- ![golang_ult_klt.jpg](https://static.cyub.vip/images/202008/golang_ult_klt.jpg)
107
-
File without changes
@@ -1,54 +0,0 @@
1
- import * as path from "path"
2
- import { Logger } from "tslog";
3
-
4
- import { Noblog } from "../lib/nomd/nomd.js";
5
- import { GetAllPosts } from "../lib/notion/client.js";
6
-
7
- let v: boolean = false;
8
- let save_dir = "./content/markdown"
9
- let sub_dir = "sub"
10
- const nob = new Noblog()
11
- const logger = new Logger({ name: "noblog" });
12
-
13
- function help() {
14
- const usage = "Usage: noblog <SAVE_DIR>";
15
- const example = "Example:\n noblog .";
16
-
17
- const output = usage + "\n" + example + "\n";
18
- return output;
19
- }
20
-
21
- async function main() {
22
- const argv = process.argv.slice(2,);
23
- if (argv.length > 1) {
24
- console.error("Error: invallid arg.");
25
- console.log(help());
26
- }
27
- save_dir = argv[0] ?? save_dir;
28
- sub_dir = path.join(save_dir, sub_dir)
29
-
30
- const pages = await GetAllPosts();
31
- const futures = pages.map(page => {
32
- return nob.FromPageid(page.id)
33
- })
34
-
35
- try {
36
- await Promise.all(futures);
37
- for (const pageid of Object.keys(nob.MdCollection)) {
38
- let fpath = save_dir;
39
- if (pages.find(page => page.id !== pageid))
40
- fpath = sub_dir
41
- const log = await nob.SaveJelly(pageid, fpath, nob.MdCollection[pageid])
42
- if (v) logger.info(log);
43
- }
44
- console.log(`success: finish dump all files to "${save_dir}"`)
45
- } catch (error) {
46
- if (error instanceof Error) {
47
- console.log(`failed: consider running with "v=true" - ${error.message}`);
48
- } else {
49
- console.log('failed: an unknown error occurred');
50
- }
51
- }
52
- }
53
-
54
- main()
package/src/index.ts DELETED
@@ -1,22 +0,0 @@
1
- export * from './lib/nomd/nomd.js'
2
- export * from './lib/notion/client.js'
3
-
4
- // ---------------- test -----------------
5
- import 'dotenv/config'
6
- import { Noblog } from './lib/nomd/nomd.js'
7
- import { GetAllPosts } from './lib/notion/client.js'
8
-
9
- const nob = new Noblog();
10
-
11
- GetAllPosts().then(res => {
12
- // client.pages.retrieve({ page_id: res[0]?.id } as any).then(
13
- // res => console.log(res))
14
- //
15
- nob.FromPageid(res[5]?.id as any).then(
16
- res => {
17
- console.log(nob.MdCollection)
18
- }
19
- )
20
- // client.blocks.children.list({ block_id: res[0]?.id } as any).then(
21
- // res => console.log(res))
22
- })
@@ -1,143 +0,0 @@
1
- import fetch from "node-fetch";
2
- import { markdownTable } from "markdown-table";
3
-
4
-
5
- type CalloutIcon =
6
- | { type: "emoji"; emoji?: string }
7
- | { type: "external"; external?: { url: string } }
8
- | { type: "file"; file: { url: string; expiry_time: string } }
9
- | null;
10
-
11
- export const inlineCode = (text: string) => {
12
- return `\`${text}\``;
13
- };
14
-
15
- export const inlineEquation = (text: string) => {
16
- return `$${text}$`;
17
- };
18
-
19
- export const bold = (text: string) => {
20
- return `**${text}**`;
21
- };
22
-
23
- export const italic = (text: string) => {
24
- return `_${text}_`;
25
- };
26
-
27
- export const strikethrough = (text: string) => {
28
- return `~~${text}~~`;
29
- };
30
-
31
- export const underline = (text: string) => {
32
- return `<u>${text}</u>`;
33
- };
34
-
35
- export const link = (text: string, href: string) => {
36
- return `[${text}](${href})`;
37
- };
38
-
39
- export const codeBlock = (text: string, language?: string) => {
40
- if (language === "plain text") language = "text";
41
-
42
- return `\`\`\`${language}
43
- ${text}
44
- \`\`\``;
45
- };
46
-
47
- export const equation = (text: string) => {
48
- return `$$
49
- ${text}
50
- $$`;
51
- };
52
-
53
- export const heading1 = (text: string) => {
54
- return `# ${text}`;
55
- };
56
-
57
- export const heading2 = (text: string) => {
58
- return `## ${text}`;
59
- };
60
-
61
- export const heading3 = (text: string) => {
62
- return `### ${text}`;
63
- };
64
-
65
- export const quote = (text: string) => {
66
- // the replace is done to handle multiple lines
67
- return `> ${text.replace(/\n/g, " \n> ")}`;
68
- };
69
-
70
- export const callout = (text: string, icon?: CalloutIcon) => {
71
- let emoji: string | undefined;
72
- if (icon?.type === "emoji") {
73
- emoji = icon.emoji;
74
- }
75
-
76
- // the replace is done to handle multiple lines
77
- return `> ${emoji ? emoji + " " : ""}${text.replace(/\n/g, " \n> ")}`;
78
- };
79
-
80
- export const bullet = (text: string, count?: number) => {
81
- let renderText = text.trim();
82
- return count ? `${count}. ${renderText}` : `- ${renderText}`;
83
- };
84
-
85
- export const todo = (text: string, checked: boolean) => {
86
- return checked ? `- [x] ${text}` : `- [ ] ${text}`;
87
- };
88
-
89
- export const image = async (
90
- alt: string,
91
- href: string,
92
- convertToBase64: boolean = false
93
- ): Promise<string> => {
94
- // In case the user does not want to convert the images to Base64
95
- // or the image is already base64
96
- if (!convertToBase64 || href.startsWith("data:")) {
97
- if (href.startsWith("data:")) {
98
- // Extract base64 data, i.e. the string after 'data:mime/type;base64,'
99
- const base64 = href.split(",").pop();
100
-
101
- // This overrides incorrect data: string format to png
102
- // so that browsers can correctly render the data
103
- return `![${alt}](data:image/png;base64,${base64})`;
104
- }
105
-
106
- return `![${alt}](${href})`;
107
- } else {
108
- // Otherwise, download the image and convert it to base64
109
- const res = await fetch(href);
110
- const buf = await res.arrayBuffer();
111
-
112
- const base64 = Buffer.from(buf).toString("base64");
113
-
114
- return `![${alt}](data:image/png;base64,${base64})`;
115
- }
116
- };
117
-
118
- export const addTabSpace = (text: string, n = 0) => {
119
- const tab = " ";
120
- for (let i = 0; i < n; i++) {
121
- if (text.includes("\n")) {
122
- const multiLineText = text.split(/(?:^|\n)/).join(`\n${tab}`);
123
- text = tab + multiLineText;
124
- } else text = tab + text;
125
- }
126
- return text;
127
- };
128
-
129
- export const divider = () => {
130
- return "---";
131
- };
132
-
133
- export const toggle = (summary?: string, children?: string) => {
134
- if (!summary) return children || "";
135
- return `<details>
136
- <summary>${summary}</summary>
137
- ${children || ""}
138
- </details>\n\n`;
139
- };
140
-
141
- export const table = (cells: string[][]) => {
142
- return markdownTable(cells);
143
- };
@@ -1,88 +0,0 @@
1
- import * as fs from 'fs/promises';
2
- import * as path from 'path';
3
- import { NotionToMarkdown } from "notion-to-md";
4
-
5
- import * as cvtmd from "./cvter.js"
6
- import { client as notion_client } from "../notion/client.js";
7
-
8
- // from notion-to-md
9
- type MdBlock = {
10
- type?: string;
11
- blockId: string;
12
- parent: string;
13
- children: MdBlock[];
14
- };
15
-
16
- export type MdJelly = {
17
- content: string; // markdown content
18
- children: string[]; // child_page
19
- }
20
-
21
- export class Noblog extends NotionToMarkdown {
22
- MdCollection: Record<string, MdJelly> = {};
23
-
24
- constructor() {
25
- super({ notionClient: notion_client });
26
- this.setCustomTransformer("child_page", async (block) => {
27
- const page = block as any;
28
- if (!page?.id) return false;
29
- return `[${page.child_page.title}](${page.id}.md)`;
30
- })
31
- }
32
-
33
- async FromBlocks(blocks: MdBlock[], recursive: boolean): Promise<MdJelly> {
34
- let jelly: MdJelly = { content: "", children: [] }
35
- for (const block of blocks) {
36
- const subjelly = await this.FromBlocks(block.children, recursive);
37
-
38
- if (block.type === "toggle") {
39
- jelly.content += cvtmd.toggle(block.parent, subjelly.content);
40
- jelly.children = [...jelly.children, ...subjelly.children];
41
- continue;
42
- }
43
- let line_wrap = "";
44
- if (
45
- block.type !== "to_do" &&
46
- block.type !== "bulleted_list_item" &&
47
- block.type !== "numbered_list_item"
48
- ) {
49
- line_wrap = "\n";
50
- }
51
- jelly.content += (line_wrap + `${cvtmd.addTabSpace(block.parent, 0)}\n` + line_wrap);
52
- if (block.type === "child_page") {
53
- jelly.children = [...jelly.children, block.blockId];
54
- if (recursive) await this.FromPageid(block.blockId, recursive);
55
- } else {
56
- jelly.content += subjelly.content;
57
- jelly.children = [...jelly.children, ...subjelly.children];
58
- }
59
- }
60
- return jelly;
61
- }
62
-
63
- async FromPageid(page_id: string, recursive: boolean = true) {
64
- if (this.MdCollection.hasOwnProperty(page_id)) {
65
- return this.MdCollection[page_id];
66
- }
67
- const blocks = await this.pageToMarkdown(page_id);
68
- const jelly = await this.FromBlocks(blocks, recursive);
69
- this.MdCollection[page_id] = jelly;
70
- return jelly;
71
- }
72
-
73
- async SaveJelly(fname: string, dir: string, jelly: MdJelly | undefined): Promise<string> {
74
- if (jelly === undefined) return `failed: ${fname} has empty jelly: `
75
- try {
76
- await fs.mkdir(dir, { recursive: true });
77
- const filePath = path.join(dir, fname);
78
- await fs.writeFile(filePath, jelly.content, 'utf-8');
79
- return `success: ${fname} saved to ${filePath}`;
80
- } catch (error) {
81
- if (error instanceof Error) {
82
- return `failed: ${fname} - ${error.message}`;
83
- } else {
84
- return 'failed: an unknown error occurred';
85
- }
86
- }
87
- }
88
- }
File without changes
@@ -1,3 +0,0 @@
1
- // noblog style manager
2
-
3
-