@ewanc26/supporters 0.1.5 → 0.1.7
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/README.md +4 -105
- package/dist/events.d.ts +20 -0
- package/dist/events.js +50 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -2,113 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
SvelteKit component library for displaying Ko-fi supporters, backed by an ATProto PDS.
|
|
4
4
|
|
|
5
|
-
Ko-fi's webhook pushes payment events to your endpoint. Each event is stored as a record under the `uk.ewancroft.kofi.supporter` lexicon on your PDS, with a TID rkey derived from the transaction timestamp. The component reads those records and renders them.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## How it works
|
|
10
|
-
|
|
11
|
-
1. Ko-fi POSTs a webhook event to `/webhook` on each transaction
|
|
12
|
-
2. The handler verifies the `verification_token`, respects `is_public`, and calls `appendEvent`
|
|
13
|
-
3. `appendEvent` writes a record to your PDS under `uk.ewancroft.kofi.supporter`
|
|
14
|
-
4. `readStore` fetches all records and aggregates them into `KofiSupporter` objects
|
|
15
|
-
5. Pass the result to `<KofiSupporters>` or `<LunarContributors>`
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## Setup
|
|
20
|
-
|
|
21
|
-
### 1. Environment variables
|
|
22
|
-
|
|
23
|
-
```env
|
|
24
|
-
# Required — copy from ko-fi.com/manage/webhooks → Advanced → Verification Token
|
|
25
|
-
KOFI_VERIFICATION_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
26
|
-
|
|
27
|
-
# Required — your ATProto identity and a dedicated app password
|
|
28
|
-
ATPROTO_DID=did:plc:yourdidhex
|
|
29
|
-
ATPROTO_PDS_URL=https://your-pds.example.com
|
|
30
|
-
ATPROTO_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Generate an app password at your PDS under **Settings → App Passwords**.
|
|
34
|
-
|
|
35
|
-
### 2. Register the webhook
|
|
36
|
-
|
|
37
|
-
Go to **ko-fi.com/manage/webhooks** and set your webhook URL to:
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
https://your-domain.com/webhook
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
### 3. Add the route
|
|
44
|
-
|
|
45
|
-
Copy `src/routes/webhook/+server.ts` into your SvelteKit app's routes directory.
|
|
46
|
-
|
|
47
|
-
### 4. Use the component
|
|
48
|
-
|
|
49
|
-
```ts
|
|
50
|
-
// +page.server.ts
|
|
51
|
-
import { readStore } from '@ewanc26/supporters';
|
|
52
|
-
|
|
53
|
-
export const load = async () => ({
|
|
54
|
-
supporters: await readStore()
|
|
55
|
-
});
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
```svelte
|
|
59
|
-
<!-- +page.svelte -->
|
|
60
|
-
<script lang="ts">
|
|
61
|
-
import { KofiSupporters } from '@ewanc26/supporters';
|
|
62
|
-
let { data } = $props();
|
|
63
|
-
</script>
|
|
64
|
-
|
|
65
|
-
<KofiSupporters supporters={data.supporters} />
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
## Components
|
|
71
|
-
|
|
72
|
-
### `<KofiSupporters>`
|
|
73
|
-
|
|
74
|
-
Displays all supporters with emoji type badges (☕ donation, ⭐ subscription, 🎨 commission, 🛍️ shop order).
|
|
75
|
-
|
|
76
|
-
| Prop | Type | Default |
|
|
77
|
-
|---|---|---|
|
|
78
|
-
| `supporters` | `KofiSupporter[]` | `[]` |
|
|
79
|
-
| `heading` | `string` | `'Supporters'` |
|
|
80
|
-
| `description` | `string` | `'People who support my work on Ko-fi.'` |
|
|
81
|
-
| `filter` | `KofiEventType[]` | `undefined` (show all) |
|
|
82
|
-
| `loading` | `boolean` | `false` |
|
|
83
|
-
| `error` | `string \| null` | `null` |
|
|
84
|
-
|
|
85
|
-
### `<LunarContributors>`
|
|
86
|
-
|
|
87
|
-
Convenience wrapper around `<KofiSupporters>` pre-filtered to `Subscription` events.
|
|
88
|
-
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
## Importing historical data
|
|
92
|
-
|
|
93
|
-
Export your transaction history from **ko-fi.com/manage/transactions → Export CSV**, then:
|
|
94
|
-
|
|
95
5
|
```bash
|
|
96
|
-
|
|
97
|
-
node node_modules/@ewanc26/supporters/scripts/import-history.mjs transactions.csv --dry-run
|
|
6
|
+
pnpm add @ewanc26/supporters
|
|
98
7
|
```
|
|
99
8
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
## Lexicon
|
|
9
|
+
Full documentation at **[docs.ewancroft.uk](https://docs.ewancroft.uk/projects/supporters)**.
|
|
103
10
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
```ts
|
|
107
|
-
{
|
|
108
|
-
name: string // display name from Ko-fi
|
|
109
|
-
type: string // "Donation" | "Subscription" | "Commission" | "Shop Order"
|
|
110
|
-
tier?: string // subscription tier name, if applicable
|
|
111
|
-
}
|
|
112
|
-
```
|
|
11
|
+
## Licence
|
|
113
12
|
|
|
114
|
-
|
|
13
|
+
See the root [LICENSE](../../LICENSE).
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetches Ko-fi supporter events as a chronological timeline.
|
|
3
|
+
*
|
|
4
|
+
* Unlike readStore (which aggregates per-person), this returns every
|
|
5
|
+
* individual event record ordered most-recent-first.
|
|
6
|
+
*
|
|
7
|
+
* No auth required — records are publicly readable.
|
|
8
|
+
*
|
|
9
|
+
* @param did - The ATProto DID to read records from.
|
|
10
|
+
*/
|
|
11
|
+
import type { KofiEventType } from './types.js';
|
|
12
|
+
export type { KofiEventType };
|
|
13
|
+
export interface KofiSupportEvent {
|
|
14
|
+
rkey: string;
|
|
15
|
+
name: string;
|
|
16
|
+
type: KofiEventType;
|
|
17
|
+
tier?: string;
|
|
18
|
+
date: Date;
|
|
19
|
+
}
|
|
20
|
+
export declare function fetchEvents(did: string): Promise<KofiSupportEvent[]>;
|
package/dist/events.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetches Ko-fi supporter events as a chronological timeline.
|
|
3
|
+
*
|
|
4
|
+
* Unlike readStore (which aggregates per-person), this returns every
|
|
5
|
+
* individual event record ordered most-recent-first.
|
|
6
|
+
*
|
|
7
|
+
* No auth required — records are publicly readable.
|
|
8
|
+
*
|
|
9
|
+
* @param did - The ATProto DID to read records from.
|
|
10
|
+
*/
|
|
11
|
+
import { AtpAgent } from '@atproto/api';
|
|
12
|
+
import { decodeTid } from '@ewanc26/tid';
|
|
13
|
+
const COLLECTION = 'uk.ewancroft.kofi.supporter';
|
|
14
|
+
async function resolvePdsUrl(did) {
|
|
15
|
+
const res = await fetch(`https://slingshot.microcosm.blue/xrpc/com.bad-example.identity.resolveMiniDoc?identifier=${encodeURIComponent(did)}`);
|
|
16
|
+
if (!res.ok)
|
|
17
|
+
throw new Error(`Failed to resolve PDS for ${did}: ${res.status}`);
|
|
18
|
+
const data = (await res.json());
|
|
19
|
+
if (!data.pds)
|
|
20
|
+
throw new Error(`No PDS found in identity document for ${did}`);
|
|
21
|
+
return data.pds;
|
|
22
|
+
}
|
|
23
|
+
export async function fetchEvents(did) {
|
|
24
|
+
const pdsUrl = await resolvePdsUrl(did);
|
|
25
|
+
const agent = new AtpAgent({ service: pdsUrl });
|
|
26
|
+
const events = [];
|
|
27
|
+
let cursor;
|
|
28
|
+
do {
|
|
29
|
+
const res = await agent.com.atproto.repo.listRecords({
|
|
30
|
+
repo: did,
|
|
31
|
+
collection: COLLECTION,
|
|
32
|
+
limit: 100,
|
|
33
|
+
cursor
|
|
34
|
+
});
|
|
35
|
+
for (const record of res.data.records) {
|
|
36
|
+
const value = record.value;
|
|
37
|
+
const rkey = record.uri.split('/').pop() ?? '';
|
|
38
|
+
let date;
|
|
39
|
+
try {
|
|
40
|
+
date = decodeTid(rkey).date;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
date = new Date(0);
|
|
44
|
+
}
|
|
45
|
+
events.push({ rkey, name: value.name, type: value.type, tier: value.tier, date });
|
|
46
|
+
}
|
|
47
|
+
cursor = res.data.cursor;
|
|
48
|
+
} while (cursor);
|
|
49
|
+
return events.sort((a, b) => b.date.getTime() - a.date.getTime());
|
|
50
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { default as KofiSupporters } from './KofiSupporters.svelte';
|
|
2
|
+
export { fetchEvents } from './events.js';
|
|
3
|
+
export type { KofiSupportEvent } from './events.js';
|
|
2
4
|
export { default as LunarContributors } from './LunarContributors.svelte';
|
|
3
5
|
export { readStore, appendEvent } from './store.js';
|
|
4
6
|
export type { KofiEventRecord } from './store.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { default as KofiSupporters } from './KofiSupporters.svelte';
|
|
2
|
+
export { fetchEvents } from './events.js';
|
|
2
3
|
export { default as LunarContributors } from './LunarContributors.svelte';
|
|
3
4
|
export { readStore, appendEvent } from './store.js';
|
|
4
5
|
export { parseWebhook, WebhookError } from './webhook.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ewanc26/supporters",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "SvelteKit component library for displaying Ko-fi supporters, backed by an ATProto PDS.",
|
|
5
5
|
"author": "Ewan Croft",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -32,20 +32,20 @@
|
|
|
32
32
|
"svelte": "^5.0.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@atproto/api": "^0.
|
|
36
|
-
"@sveltejs/adapter-auto": "^7.0.
|
|
37
|
-
"@sveltejs/kit": "^2.
|
|
35
|
+
"@atproto/api": "^0.19.3",
|
|
36
|
+
"@sveltejs/adapter-auto": "^7.0.1",
|
|
37
|
+
"@sveltejs/kit": "^2.55.0",
|
|
38
38
|
"@sveltejs/package": "^2.5.7",
|
|
39
39
|
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
40
40
|
"@tailwindcss/typography": "^0.5.19",
|
|
41
|
-
"@tailwindcss/vite": "^4.1
|
|
41
|
+
"@tailwindcss/vite": "^4.2.1",
|
|
42
42
|
"prettier": "^3.8.1",
|
|
43
|
-
"prettier-plugin-svelte": "^3.
|
|
43
|
+
"prettier-plugin-svelte": "^3.5.1",
|
|
44
44
|
"prettier-plugin-tailwindcss": "^0.7.2",
|
|
45
|
-
"publint": "^0.3.
|
|
46
|
-
"svelte": "^5.
|
|
47
|
-
"svelte-check": "^4.4.
|
|
48
|
-
"tailwindcss": "^4.1
|
|
45
|
+
"publint": "^0.3.18",
|
|
46
|
+
"svelte": "^5.53.11",
|
|
47
|
+
"svelte-check": "^4.4.5",
|
|
48
|
+
"tailwindcss": "^4.2.1",
|
|
49
49
|
"typescript": "^5.9.3",
|
|
50
50
|
"vite": "^7.3.1"
|
|
51
51
|
},
|