@vornrun/connector-substack 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/README.md +110 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +2070 -0
- package/package.json +65 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@vornrun/connector-substack`.
|
|
4
|
+
|
|
5
|
+
## 0.1.0
|
|
6
|
+
|
|
7
|
+
First release.
|
|
8
|
+
|
|
9
|
+
Trigger a workflow from new posts on a Substack publication, and let a
|
|
10
|
+
workflow step read any publication's feed, search posts, read a post's
|
|
11
|
+
comments, save a draft from markdown, comment on a post, like or unlike a
|
|
12
|
+
comment, and delete a draft or a comment.
|
|
13
|
+
|
|
14
|
+
- **Trigger:** `newPost`, from the publication's RSS feed.
|
|
15
|
+
- **Actions:** `readFeed`, `searchPosts`, `readComments`, `createDraft`,
|
|
16
|
+
`deleteDraft`, `commentOnPost`, `setCommentLike`, `deleteComment`.
|
|
17
|
+
- **Signing in:** the first connector on the `browser` rung. Vorn opens a
|
|
18
|
+
window on a browser profile kept for the connection alone, and every
|
|
19
|
+
signed-in call runs inside that window as a request from Substack's own
|
|
20
|
+
page. No cookie or password reaches the connector.
|
|
21
|
+
|
|
22
|
+
Substack publishes no API, so the connector reads the public RSS feed and
|
|
23
|
+
asks the web endpoints Substack's pages use, each one checked from a
|
|
24
|
+
signed-in browser on 2026-09-10 against the author's own publication.
|
|
25
|
+
Reading the feed, searching and reading comments on a public post need no
|
|
26
|
+
sign-in. Writes stay on `*.substack.com`.
|
|
27
|
+
|
|
28
|
+
It never publishes or schedules, because publishing emails every
|
|
29
|
+
subscriber: any request path containing `publish` or `schedule` is refused
|
|
30
|
+
before it leaves, and `deleteDraft` refuses a published post. Draft bodies
|
|
31
|
+
are markdown converted to the editor's own document, using only the node and
|
|
32
|
+
mark names Substack's editor saved.
|
|
33
|
+
|
|
34
|
+
Needs the Vorn release that adds browser sign-in; an older Vorn does not
|
|
35
|
+
list the connector. Ships as a pack with a conformance receipt covering the
|
|
36
|
+
dedupe replay of the trigger and the mock run of every action. `marked`
|
|
37
|
+
travels inside the bundle; no runtime dependencies.
|
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# @vornrun/connector-substack
|
|
2
|
+
|
|
3
|
+
Trigger Vorn workflows from new posts on a Substack publication, and read a
|
|
4
|
+
feed, search posts, read comments, save a draft, comment, and like or delete
|
|
5
|
+
comments from a workflow step. Substack publishes no API for any of this, so
|
|
6
|
+
the connector reads the public RSS feed and asks the same web endpoints
|
|
7
|
+
Substack's own pages use, from inside a window you signed in to.
|
|
8
|
+
|
|
9
|
+
## Signing in
|
|
10
|
+
|
|
11
|
+
Connect, then sign in to Substack in the window Vorn opens. The window runs
|
|
12
|
+
on a browser profile kept for this connection alone. Vorn keeps that
|
|
13
|
+
profile, not a password and not a copied cookie, and the connection's row
|
|
14
|
+
says who is signed in. A sign-in link Substack emails opens in your default
|
|
15
|
+
browser; paste it into the connection's sign-in link field instead.
|
|
16
|
+
|
|
17
|
+
The connector never sees a cookie. Each signed-in call runs inside that
|
|
18
|
+
window as a request from Substack's own page, on `substack.com` or a
|
|
19
|
+
`*.substack.com` publication, and Vorn refuses any other address.
|
|
20
|
+
|
|
21
|
+
When Substack signs the window out, a workflow step that needed it waits as
|
|
22
|
+
"waiting for sign-in" until you sign in again, then runs again. When Vorn is
|
|
23
|
+
closed on the desktop that signed in, the step fails and says to open it.
|
|
24
|
+
|
|
25
|
+
Reading a feed, searching posts and reading the comments on a public post
|
|
26
|
+
need no sign-in.
|
|
27
|
+
|
|
28
|
+
## Settings
|
|
29
|
+
|
|
30
|
+
| Setting | Env | What it is |
|
|
31
|
+
| --- | --- | --- |
|
|
32
|
+
| `publication` | `SUBSTACK_PUBLICATION` | Your publication's substack.com subdomain, such as `novumai`. The `newPost` trigger reads its feed, and a step that names no publication uses it. When it is empty, signed-in actions use the account's primary publication. |
|
|
33
|
+
|
|
34
|
+
## It never publishes
|
|
35
|
+
|
|
36
|
+
Publishing a post emails every subscriber, so nothing here publishes or
|
|
37
|
+
schedules. `createDraft` saves a draft and returns the address to open it in
|
|
38
|
+
the editor, where you publish it yourself. Any request path containing
|
|
39
|
+
`publish` or `schedule` is refused before it leaves, and `deleteDraft` reads
|
|
40
|
+
the draft first and refuses a published post.
|
|
41
|
+
|
|
42
|
+
## Substack's terms
|
|
43
|
+
|
|
44
|
+
Substack's [terms of use](https://substack.com/tos) forbid automated crawling
|
|
45
|
+
and unattended processes. Keep the signed-in actions in workflows you start,
|
|
46
|
+
and poll the feed no more often than a reader would.
|
|
47
|
+
|
|
48
|
+
## Triggers
|
|
49
|
+
|
|
50
|
+
### `newPost` — a post appears in the publication's feed
|
|
51
|
+
|
|
52
|
+
Reads `https://<publication>/feed` over plain HTTPS without signing in, on the
|
|
53
|
+
SDK's `timestamp` dedupe from each item's `pubDate`. The feed holds about the
|
|
54
|
+
last twenty posts, so a burst larger than that between polls loses the
|
|
55
|
+
oldest. Each item carries `externalId` (the guid, which is the post's
|
|
56
|
+
address), `title`, `url`, `description` (the subtitle), `assignee` (the
|
|
57
|
+
author) and `updatedAt`, plus `subtitle`, `author`, `publishedAt` and `text`
|
|
58
|
+
for templates.
|
|
59
|
+
|
|
60
|
+
## Actions
|
|
61
|
+
|
|
62
|
+
| Action | Idempotent | Signed in | What it does |
|
|
63
|
+
| --- | --- | --- | --- |
|
|
64
|
+
| `readFeed` | yes | no | `GET https://<publication>/feed`, for any publication, a custom domain included. `limit` from 1 to 20, default 10. Returns `publication`, `count` and `posts` (`id`, `title`, `subtitle`, `url`, `author`, `publishedAt`, `html`, `text`). |
|
|
65
|
+
| `searchPosts` | yes | no | `GET substack.com/api/v1/post/search` with `query` and `page` (from 0). Returns `count`, `more` and `posts` (`id`, `title`, `subtitle`, `url`, `publishedAt`, `author`, `authorHandle`, `likes`, `comments`, `publicationId`). |
|
|
66
|
+
| `readComments` | yes | no | Looks the post up by address or slug (`GET /api/v1/posts/<slug>`) unless given `postId`, then `GET /api/v1/post/<id>/comments?all_comments=true&sort=newest_first`. Returns `postId`, `count` and `comments` (`id`, `body`, `author`, `handle`, `date`, `likes`, `parentId`, `replies`), each reply after the comment it answers. |
|
|
67
|
+
| `createDraft` | no | yes | `GET substack.com/api/v1/user/profile/self` for the byline, then `POST /api/v1/drafts` on the publication with the markdown body converted to the editor's document. Returns `id`, `title` and `editUrl`. |
|
|
68
|
+
| `deleteDraft` | no | yes | `GET`, then `DELETE /api/v1/drafts/<id>`; a published post is refused. Returns `deleted`. |
|
|
69
|
+
| `commentOnPost` | no | yes | `POST /api/v1/post/<id>/comment` with `{ body }`. Returns `id` and `postId`. |
|
|
70
|
+
| `setCommentLike` | yes | yes | `POST` to like or `DELETE` to unlike `/api/v1/comment/<id>/reaction`, with `{ reaction: "❤" }`. Returns `liked`. |
|
|
71
|
+
| `deleteComment` | no | yes | `DELETE /api/v1/comment/<id>`. Returns `deleted`. |
|
|
72
|
+
|
|
73
|
+
Writes stay on `*.substack.com`. A publication on a custom domain can be read
|
|
74
|
+
at its own address, but a comment on one of its posts is refused; write to it
|
|
75
|
+
at its substack.com address instead.
|
|
76
|
+
|
|
77
|
+
### Markdown in a draft
|
|
78
|
+
|
|
79
|
+
Headings, paragraphs, bullet and numbered lists, quotes, code blocks, rules,
|
|
80
|
+
line breaks, bold, italic, inline code, strikethrough and links carry over,
|
|
81
|
+
under the node and mark names Substack's editor saved when each was pasted
|
|
82
|
+
into a draft. A picture becomes a link to it, because uploading one is the
|
|
83
|
+
editor's job. A table becomes one line per row, and a script or data link
|
|
84
|
+
keeps its words without being clickable.
|
|
85
|
+
|
|
86
|
+
## Checks
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
packages/substack/scripts/check.sh # typecheck, tests, build, conformance receipt
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Tests make no network calls. Plain and signed-in fetches are separate stubs,
|
|
93
|
+
so each test also says which calls went through the window. There is no live
|
|
94
|
+
check: every signed-in action needs the Vorn window, which the SDK's live
|
|
95
|
+
check skips.
|
|
96
|
+
|
|
97
|
+
## Built from
|
|
98
|
+
|
|
99
|
+
Substack publishes no API reference. Every request here was made from a
|
|
100
|
+
signed-in browser on 2026-09-10 against the author's own publication, and
|
|
101
|
+
each write was undone.
|
|
102
|
+
|
|
103
|
+
- `GET substack.com/api/v1/user/profile/self` answers 200 with `id`, `name`,
|
|
104
|
+
`handle` and `publicationUsers` when signed in, and 401 when not.
|
|
105
|
+
- `/feed` is RSS 2.0 with `guid`, `pubDate`, `dc:creator` and
|
|
106
|
+
`content:encoded`.
|
|
107
|
+
- Search, comments and a post by slug answer without a sign-in.
|
|
108
|
+
- `POST /api/v1/drafts` answers 400 `draft_bylines Invalid value` without a
|
|
109
|
+
byline and 200 with one. A deleted draft then reads back 404.
|
|
110
|
+
- Commenting, liking and deleting a comment are the requests listed above.
|