Skip to content

feat: flexible cookie auth (substack.sid and/or connect.sid), markdown notes, retries, note deletion#323

Open
onlyTheOnionNews wants to merge 3 commits into
jakub-k-slys:mainfrom
onlyTheOnionNews:staging
Open

feat: flexible cookie auth (substack.sid and/or connect.sid), markdown notes, retries, note deletion#323
onlyTheOnionNews wants to merge 3 commits into
jakub-k-slys:mainfrom
onlyTheOnionNews:staging

Conversation

@onlyTheOnionNews

Copy link
Copy Markdown

Supersedes #322, which was closed accidentally during a head-branch rename on the fork.

Summary

This PR contributes the substackular continuation of this library back upstream. It sits directly on top of main (4228d80) and adds the features from substack-gateway-oss that were missing from the TypeScript client, most importantly flexible session-cookie authentication:

  • substack.sid and/or connect.sid auth — some Substack accounts only carry one of the two session cookies. HttpClient now accepts { substackSid?, connectSid? }, requires at least one, and sends every cookie provided (the gateway sends both). The existing token config key is kept as a deprecated alias for substackSid, so current consumers are unaffected.
  • Markdown note publishingOwnProfile.publishNote(markdown, { attachmentUrl? }), a TypeScript port of the gateway''s converters/markdown.py that feeds NoteBuilder, so markdown gets real ProseMirror link marks and bullet/ordered list nodes instead of flattened text.
  • Note deletionNoteService.deleteNote(), Note.delete(), OwnProfile.deleteNote()DELETE /api/v1/comment/{id} (gateway parity).
  • Retry with exponential backoff on transient failures (network errors, 408/425/429/5xx) via axios-retry, mirroring the gateway''s tenacity policy; configurable with retryAttempts.
  • Typed errorsSubstackAuthError (401/403) vs SubstackApiError, both exported.
  • Dual ESM + CJS packaging via tsup (replacing the CJS-only Rollup build), with fp-ts/io-ts inlined because their missing exports maps break Node ESM resolution; consumption verified from both require() and import.

Heads-up for review

Because this began as a maintained continuation, the branch also rebrands the package to substackular (package name, @substackular/* path aliases, README/LICENSE attribution) and drops the docs/CI directories. The sample fixtures containing ? were renamed (?--) so the repo checks out on Windows. If you''d rather take the functional changes under the substack-api name without the rebrand, happy to split this into smaller PRs — just say the word.

Test plan

  • 243 unit tests + 24 integration tests passing (pnpm test), coverage thresholds kept at 80%
  • tsc -p tsconfig.test.json clean across src and all test suites
  • pnpm lint clean (0 errors)
  • Built dist smoke-tested from both CJS require() and ESM import(), including a connectSid-only client and the no-cookie error path
  • Live E2E (pnpm test:e2e) — needs SUBSTACK_SID/CONNECT_SID + SUBSTACK_HOSTNAME secrets

🤖 Generated with Claude Code

jjame21 added 3 commits June 12, 2026 11:28
Source extracted from jakub-k-slys/substack-api (MIT) at HEAD via git
archive. Changes from upstream:
- renamed path alias @substack-api -> @substackular
- replaced Rollup CJS-only build with tsup dual ESM/CJS + dts
- modernized tsconfig for TypeScript 6 (bundler resolution)
- renamed ?-containing sample fixtures to Windows-safe names (-- for ?)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- HttpClient now accepts { substackSid?, connectSid? } and sends every
  provided cookie; at least one is required. Fixes accounts that only
  have a substack.sid or only a connect.sid session cookie.
- SubstackConfig gains substackSid/connectSid/retryAttempts; token kept
  as deprecated alias for substackSid (substack-api compatibility).
- Transient failures (network errors, 408/425/429/5xx) retried with
  exponential backoff via axios-retry, mirroring substack-gateway-oss.
- Typed errors: SubstackApiError, SubstackAuthError (401/403).
- Note deletion: NoteService.deleteNote, Note.delete(),
  OwnProfile.deleteNote() -> DELETE /api/v1/comment/{id}.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- parseMarkdownNote: port of substack-gateway-oss converters/markdown.py
  emitting NoteBuilder paragraphs, so markdown gets real ProseMirror link
  marks and bullet/ordered list nodes instead of flattened text.
- OwnProfile.publishNote(markdown, { attachmentUrl? }) publishes notes
  from markdown strings, optionally with a link preview attachment.
- E2E credentials switched to SUBSTACK_SID / CONNECT_SID env vars with
  legacy SUBSTACK_API_KEY fallback; .env.example updated.
- tsup inlines fp-ts/io-ts (no exports maps) so the ESM bundle
  resolves under Node; verified require() and import() consumption.
- tsconfig.test.json fixed for standalone tsc typechecking.
- README, LICENSE attribution for the substack-api derivation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants