@ewanc26/pds-landing 1.0.0 → 2.0.2
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 +122 -14
- package/dist/components/ContactSection.svelte +59 -0
- package/dist/components/ContactSection.svelte.d.ts +17 -0
- package/dist/components/Divider.svelte +9 -0
- package/dist/components/Divider.svelte.d.ts +26 -0
- package/dist/components/KVGrid.svelte +87 -0
- package/dist/components/KVGrid.svelte.d.ts +13 -0
- package/dist/components/LinkList.svelte +60 -0
- package/dist/components/LinkList.svelte.d.ts +16 -0
- package/dist/components/PDSFooter.svelte +43 -0
- package/dist/components/PDSFooter.svelte.d.ts +9 -0
- package/dist/components/PDSPage.svelte +172 -0
- package/dist/components/PDSPage.svelte.d.ts +32 -0
- package/dist/components/PromptLine.svelte +48 -0
- package/dist/components/PromptLine.svelte.d.ts +11 -0
- package/dist/components/SectionLabel.svelte +25 -0
- package/dist/components/SectionLabel.svelte.d.ts +8 -0
- package/dist/components/StatusGrid.svelte +92 -0
- package/dist/components/StatusGrid.svelte.d.ts +10 -0
- package/dist/components/Tagline.svelte +24 -0
- package/dist/components/Tagline.svelte.d.ts +8 -0
- package/dist/components/TerminalCard.svelte +77 -0
- package/dist/components/TerminalCard.svelte.d.ts +10 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +17 -0
- package/dist/utils/fetchPDSStatus.d.ts +34 -0
- package/dist/utils/fetchPDSStatus.js +65 -0
- package/package.json +68 -31
- package/LICENSE +0 -661
- package/dist/assets/icon/android-icon-192x192.png +0 -0
- package/dist/assets/icon/apple-icon-114x114.png +0 -0
- package/dist/assets/icon/apple-icon-120x120.png +0 -0
- package/dist/assets/icon/apple-icon-144x144.png +0 -0
- package/dist/assets/icon/apple-icon-152x152.png +0 -0
- package/dist/assets/icon/apple-icon-180x180.png +0 -0
- package/dist/assets/icon/apple-icon-57x57.png +0 -0
- package/dist/assets/icon/apple-icon-60x60.png +0 -0
- package/dist/assets/icon/apple-icon-72x72.png +0 -0
- package/dist/assets/icon/apple-icon-76x76.png +0 -0
- package/dist/assets/icon/browserconfig.xml +0 -2
- package/dist/assets/icon/favicon-16x16.png +0 -0
- package/dist/assets/icon/favicon-256x256.png +0 -0
- package/dist/assets/icon/favicon-32x32.png +0 -0
- package/dist/assets/icon/favicon-96x96.png +0 -0
- package/dist/assets/icon/favicon.ico +0 -0
- package/dist/assets/icon/instructions.txt +0 -22
- package/dist/assets/icon/manifest.json +0 -41
- package/dist/assets/icon/ms-icon-144x144.png +0 -0
- package/dist/assets/icon/ms-icon-150x150.png +0 -0
- package/dist/assets/icon/ms-icon-310x310.png +0 -0
- package/dist/assets/icon/ms-icon-70x70.png +0 -0
- package/dist/assets/thumb.svg +0 -78
- package/dist/index.html +0 -231
- package/dist/script.js +0 -100
- package/dist/style.css +0 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { LinkItem } from './LinkList.svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Text shown in the terminal titlebar. */
|
|
4
|
+
cardTitle?: string;
|
|
5
|
+
promptUser?: string;
|
|
6
|
+
promptHost?: string;
|
|
7
|
+
promptPath?: string;
|
|
8
|
+
tagline?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Origin prepended to `/xrpc/…` calls.
|
|
11
|
+
* Leave empty (`''`) to use the current page's origin (default).
|
|
12
|
+
*/
|
|
13
|
+
baseUrl?: string;
|
|
14
|
+
showStatus?: boolean;
|
|
15
|
+
showEndpoints?: boolean;
|
|
16
|
+
showLinks?: boolean;
|
|
17
|
+
showContact?: boolean;
|
|
18
|
+
showFooter?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Static links always shown in the Links section.
|
|
21
|
+
* Dynamic links (privacy policy / ToS) from `describeServer` are
|
|
22
|
+
* appended automatically.
|
|
23
|
+
*/
|
|
24
|
+
staticLinks?: LinkItem[];
|
|
25
|
+
blueskyHandle?: string;
|
|
26
|
+
blueskyClientUrl?: string;
|
|
27
|
+
showNixpkg?: boolean;
|
|
28
|
+
showAtproto?: boolean;
|
|
29
|
+
}
|
|
30
|
+
declare const PDSPage: import("svelte").Component<Props, {}, "">;
|
|
31
|
+
type PDSPage = ReturnType<typeof PDSPage>;
|
|
32
|
+
export default PDSPage;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Username portion (before the @). */
|
|
4
|
+
user: string;
|
|
5
|
+
/** Hostname (after the @). */
|
|
6
|
+
host: string;
|
|
7
|
+
/** Shell path shown after the colon. Defaults to `~`. */
|
|
8
|
+
path?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let { user, host, path = '~' }: Props = $props();
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<div class="pds-prompt-line">
|
|
15
|
+
<span class="pds-user-marker">{user}@{host}</span><span class="pds-prompt-path">:{path}</span
|
|
16
|
+
><span class="pds-prompt-char" aria-hidden="true"> $</span>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<style>
|
|
20
|
+
.pds-prompt-line {
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: baseline;
|
|
23
|
+
margin-bottom: 0.3rem;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.pds-user-marker {
|
|
27
|
+
color: var(--pds-color-green);
|
|
28
|
+
font-weight: 700;
|
|
29
|
+
word-break: break-all;
|
|
30
|
+
font-size: clamp(0.95em, 4vw, 1.15em);
|
|
31
|
+
letter-spacing: -0.01em;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.pds-prompt-path {
|
|
35
|
+
color: var(--pds-color-subtext-0);
|
|
36
|
+
font-weight: 700;
|
|
37
|
+
font-size: clamp(0.95em, 4vw, 1.15em);
|
|
38
|
+
opacity: 0.6;
|
|
39
|
+
user-select: none;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.pds-prompt-char {
|
|
43
|
+
color: var(--pds-color-subtext-0);
|
|
44
|
+
font-weight: 700;
|
|
45
|
+
font-size: clamp(0.95em, 4vw, 1.15em);
|
|
46
|
+
user-select: none;
|
|
47
|
+
}
|
|
48
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** Username portion (before the @). */
|
|
3
|
+
user: string;
|
|
4
|
+
/** Hostname (after the @). */
|
|
5
|
+
host: string;
|
|
6
|
+
/** Shell path shown after the colon. Defaults to `~`. */
|
|
7
|
+
path?: string;
|
|
8
|
+
}
|
|
9
|
+
declare const PromptLine: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type PromptLine = ReturnType<typeof PromptLine>;
|
|
11
|
+
export default PromptLine;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
label?: string;
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let { label, children }: Props = $props();
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<div class="pds-section-label">
|
|
13
|
+
{#if children}{@render children()}{:else}{label ?? ''}{/if}
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<style>
|
|
17
|
+
.pds-section-label {
|
|
18
|
+
font-weight: 700;
|
|
19
|
+
text-transform: uppercase;
|
|
20
|
+
font-size: 0.72em;
|
|
21
|
+
letter-spacing: 0.12em;
|
|
22
|
+
color: var(--pds-color-green);
|
|
23
|
+
margin-bottom: 0.7rem;
|
|
24
|
+
}
|
|
25
|
+
</style>
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount } from 'svelte';
|
|
3
|
+
import KVGrid from './KVGrid.svelte';
|
|
4
|
+
import type { KVItem } from './KVGrid.svelte';
|
|
5
|
+
import { fetchPDSStatus, type PDSHealth, type PDSDescription } from '../utils/fetchPDSStatus.js';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
/**
|
|
9
|
+
* Base URL to prepend to `/xrpc/…` calls.
|
|
10
|
+
* Defaults to `''` (same origin — correct for in-browser use).
|
|
11
|
+
*/
|
|
12
|
+
baseUrl?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let { baseUrl = '' }: Props = $props();
|
|
16
|
+
|
|
17
|
+
const LOADING_ITEMS: KVItem[] = [
|
|
18
|
+
{ key: 'reachable', value: '…', status: 'loading' },
|
|
19
|
+
{ key: 'version', value: '…', status: 'loading' },
|
|
20
|
+
{ key: 'did', value: '…', status: 'loading' },
|
|
21
|
+
{ key: 'accounts', value: '…', status: 'loading' },
|
|
22
|
+
{ key: 'invite required', value: '…', status: 'loading' }
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
let items: KVItem[] = $state([...LOADING_ITEMS]);
|
|
26
|
+
|
|
27
|
+
function buildItems(
|
|
28
|
+
health: PDSHealth,
|
|
29
|
+
description: PDSDescription,
|
|
30
|
+
accountCount: number
|
|
31
|
+
): KVItem[] {
|
|
32
|
+
const result: KVItem[] = [
|
|
33
|
+
{
|
|
34
|
+
key: 'reachable',
|
|
35
|
+
value: health.reachable ? '✓ online' : '✗ unreachable',
|
|
36
|
+
status: health.reachable ? 'ok' : 'err'
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
key: 'version',
|
|
40
|
+
value: health.version ?? (health.reachable ? 'unknown' : '—'),
|
|
41
|
+
status: health.reachable ? undefined : 'err'
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
key: 'did',
|
|
45
|
+
value: description.did ?? '—'
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
key: 'accounts',
|
|
49
|
+
value: accountCount >= 0 ? accountCount.toString() : '—'
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
key: 'invite required',
|
|
53
|
+
value:
|
|
54
|
+
description.inviteCodeRequired === null
|
|
55
|
+
? '—'
|
|
56
|
+
: description.inviteCodeRequired
|
|
57
|
+
? 'yes'
|
|
58
|
+
: 'no',
|
|
59
|
+
status:
|
|
60
|
+
description.inviteCodeRequired === null
|
|
61
|
+
? undefined
|
|
62
|
+
: description.inviteCodeRequired
|
|
63
|
+
? 'warn'
|
|
64
|
+
: 'ok'
|
|
65
|
+
}
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
if (description.phoneVerificationRequired !== null) {
|
|
69
|
+
result.push({
|
|
70
|
+
key: 'phone verify',
|
|
71
|
+
value: description.phoneVerificationRequired ? 'yes' : 'no',
|
|
72
|
+
status: description.phoneVerificationRequired ? 'warn' : 'ok'
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (description.availableUserDomains.length > 0) {
|
|
77
|
+
result.push({
|
|
78
|
+
key: 'user domains',
|
|
79
|
+
value: description.availableUserDomains.join(', ')
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
onMount(async () => {
|
|
87
|
+
const { health, description, accountCount } = await fetchPDSStatus(baseUrl);
|
|
88
|
+
items = buildItems(health, description, accountCount);
|
|
89
|
+
});
|
|
90
|
+
</script>
|
|
91
|
+
|
|
92
|
+
<KVGrid {items} />
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/**
|
|
3
|
+
* Base URL to prepend to `/xrpc/…` calls.
|
|
4
|
+
* Defaults to `''` (same origin — correct for in-browser use).
|
|
5
|
+
*/
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
}
|
|
8
|
+
declare const StatusGrid: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type StatusGrid = ReturnType<typeof StatusGrid>;
|
|
10
|
+
export default StatusGrid;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
text?: string;
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let { text, children }: Props = $props();
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<p class="pds-tagline">
|
|
13
|
+
{#if children}{@render children()}{:else}{text ?? ''}{/if}
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<style>
|
|
17
|
+
.pds-tagline {
|
|
18
|
+
color: var(--pds-color-overlay-0);
|
|
19
|
+
font-size: 0.82em;
|
|
20
|
+
margin-top: 0.2rem;
|
|
21
|
+
margin-bottom: 1.4rem;
|
|
22
|
+
line-height: 1.5;
|
|
23
|
+
}
|
|
24
|
+
</style>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
/** Text shown in the titlebar after the traffic-light dots. */
|
|
6
|
+
title?: string;
|
|
7
|
+
class?: string;
|
|
8
|
+
children?: Snippet;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let { title = 'terminal', class: cls = '', children }: Props = $props();
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<div class="pds-card {cls}">
|
|
15
|
+
<div class="pds-card-titlebar">
|
|
16
|
+
<span class="pds-dot" aria-hidden="true"></span>
|
|
17
|
+
<span class="pds-dot" aria-hidden="true"></span>
|
|
18
|
+
<span class="pds-dot" aria-hidden="true"></span>
|
|
19
|
+
<span class="pds-card-title">{title}</span>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="pds-card-body">
|
|
22
|
+
{#if children}{@render children()}{/if}
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<style>
|
|
27
|
+
.pds-card {
|
|
28
|
+
width: 100%;
|
|
29
|
+
max-width: 680px;
|
|
30
|
+
overflow: hidden;
|
|
31
|
+
border-radius: 0.5rem;
|
|
32
|
+
background-color: var(--pds-color-mantle);
|
|
33
|
+
border: 1px solid var(--pds-color-surface-1);
|
|
34
|
+
box-shadow:
|
|
35
|
+
0 0 0 1px color-mix(in srgb, var(--pds-color-green) 6%, transparent),
|
|
36
|
+
0 8px 32px color-mix(in srgb, var(--pds-color-shadow) 50%, transparent);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.pds-card-titlebar {
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
gap: 0.5rem;
|
|
43
|
+
padding: 0.55rem 1rem;
|
|
44
|
+
background-color: var(--pds-color-surface-0);
|
|
45
|
+
color: var(--pds-color-green);
|
|
46
|
+
border-bottom: 1px solid color-mix(in srgb, var(--pds-color-green) 15%, transparent);
|
|
47
|
+
font-size: 0.75rem;
|
|
48
|
+
min-width: 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.pds-card-title {
|
|
52
|
+
overflow: hidden;
|
|
53
|
+
text-overflow: ellipsis;
|
|
54
|
+
white-space: nowrap;
|
|
55
|
+
min-width: 0;
|
|
56
|
+
margin-left: 0.4rem;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.pds-dot {
|
|
60
|
+
width: 10px;
|
|
61
|
+
height: 10px;
|
|
62
|
+
border-radius: 50%;
|
|
63
|
+
flex-shrink: 0;
|
|
64
|
+
background-color: color-mix(in srgb, var(--pds-color-green) 25%, transparent);
|
|
65
|
+
border: 1px solid color-mix(in srgb, var(--pds-color-green) 40%, transparent);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.pds-card-body {
|
|
69
|
+
padding: 1.4rem 1.6rem;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@media (max-width: 440px) {
|
|
73
|
+
.pds-card-body {
|
|
74
|
+
padding: 1.1rem;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
</style>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Text shown in the titlebar after the traffic-light dots. */
|
|
4
|
+
title?: string;
|
|
5
|
+
class?: string;
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
}
|
|
8
|
+
declare const TerminalCard: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type TerminalCard = ReturnType<typeof TerminalCard>;
|
|
10
|
+
export default TerminalCard;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { fetchPDSStatus } from './utils/fetchPDSStatus.js';
|
|
2
|
+
export type { PDSHealth, PDSDescription, PDSStatusResult } from './utils/fetchPDSStatus.js';
|
|
3
|
+
export { default as TerminalCard } from './components/TerminalCard.svelte';
|
|
4
|
+
export { default as PromptLine } from './components/PromptLine.svelte';
|
|
5
|
+
export { default as Tagline } from './components/Tagline.svelte';
|
|
6
|
+
export { default as SectionLabel } from './components/SectionLabel.svelte';
|
|
7
|
+
export { default as Divider } from './components/Divider.svelte';
|
|
8
|
+
export { default as KVGrid } from './components/KVGrid.svelte';
|
|
9
|
+
export type { KVItem } from './components/KVGrid.svelte';
|
|
10
|
+
export { default as LinkList } from './components/LinkList.svelte';
|
|
11
|
+
export type { LinkItem } from './components/LinkList.svelte';
|
|
12
|
+
export { default as StatusGrid } from './components/StatusGrid.svelte';
|
|
13
|
+
export { default as ContactSection } from './components/ContactSection.svelte';
|
|
14
|
+
export { default as PDSFooter } from './components/PDSFooter.svelte';
|
|
15
|
+
export { default as PDSPage } from './components/PDSPage.svelte';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// ─── Utilities ────────────────────────────────────────────────────────────────
|
|
2
|
+
export { fetchPDSStatus } from './utils/fetchPDSStatus.js';
|
|
3
|
+
// ─── Primitive components ─────────────────────────────────────────────────────
|
|
4
|
+
export { default as TerminalCard } from './components/TerminalCard.svelte';
|
|
5
|
+
export { default as PromptLine } from './components/PromptLine.svelte';
|
|
6
|
+
export { default as Tagline } from './components/Tagline.svelte';
|
|
7
|
+
export { default as SectionLabel } from './components/SectionLabel.svelte';
|
|
8
|
+
export { default as Divider } from './components/Divider.svelte';
|
|
9
|
+
export { default as KVGrid } from './components/KVGrid.svelte';
|
|
10
|
+
export { default as LinkList } from './components/LinkList.svelte';
|
|
11
|
+
// ─── Smart / data-fetching components ────────────────────────────────────────
|
|
12
|
+
export { default as StatusGrid } from './components/StatusGrid.svelte';
|
|
13
|
+
// ─── Compound / section components ───────────────────────────────────────────
|
|
14
|
+
export { default as ContactSection } from './components/ContactSection.svelte';
|
|
15
|
+
export { default as PDSFooter } from './components/PDSFooter.svelte';
|
|
16
|
+
// ─── Full-page convenience component ─────────────────────────────────────────
|
|
17
|
+
export { default as PDSPage } from './components/PDSPage.svelte';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types and utilities for fetching live status from an ATProto PDS.
|
|
3
|
+
*/
|
|
4
|
+
export interface PDSHealth {
|
|
5
|
+
reachable: boolean;
|
|
6
|
+
/** Reported software version, or null if unreachable / not exposed. */
|
|
7
|
+
version: string | null;
|
|
8
|
+
}
|
|
9
|
+
export interface PDSDescription {
|
|
10
|
+
did: string | null;
|
|
11
|
+
inviteCodeRequired: boolean | null;
|
|
12
|
+
phoneVerificationRequired: boolean | null;
|
|
13
|
+
availableUserDomains: string[];
|
|
14
|
+
links: {
|
|
15
|
+
privacyPolicy?: string;
|
|
16
|
+
termsOfService?: string;
|
|
17
|
+
} | null;
|
|
18
|
+
contact: {
|
|
19
|
+
email?: string;
|
|
20
|
+
} | null;
|
|
21
|
+
}
|
|
22
|
+
export interface PDSStatusResult {
|
|
23
|
+
health: PDSHealth;
|
|
24
|
+
description: PDSDescription;
|
|
25
|
+
/** Total repo count, or -1 if the fetch failed. */
|
|
26
|
+
accountCount: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Fetch live status from a PDS.
|
|
30
|
+
*
|
|
31
|
+
* @param baseUrl - Origin to prepend to `/xrpc/…` paths.
|
|
32
|
+
* Defaults to `''` (same origin, works in-browser).
|
|
33
|
+
*/
|
|
34
|
+
export declare function fetchPDSStatus(baseUrl?: string): Promise<PDSStatusResult>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types and utilities for fetching live status from an ATProto PDS.
|
|
3
|
+
*/
|
|
4
|
+
async function fetchJSON(url) {
|
|
5
|
+
const r = await fetch(url);
|
|
6
|
+
if (!r.ok)
|
|
7
|
+
throw new Error(`HTTP ${r.status}`);
|
|
8
|
+
return r.json();
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Fetch live status from a PDS.
|
|
12
|
+
*
|
|
13
|
+
* @param baseUrl - Origin to prepend to `/xrpc/…` paths.
|
|
14
|
+
* Defaults to `''` (same origin, works in-browser).
|
|
15
|
+
*/
|
|
16
|
+
export async function fetchPDSStatus(baseUrl = '') {
|
|
17
|
+
// ── health ────────────────────────────────────────────────────────────────
|
|
18
|
+
let health = { reachable: false, version: null };
|
|
19
|
+
try {
|
|
20
|
+
const h = (await fetchJSON(`${baseUrl}/xrpc/_health`));
|
|
21
|
+
health = { reachable: true, version: h.version ?? null };
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// leave defaults
|
|
25
|
+
}
|
|
26
|
+
// ── description ───────────────────────────────────────────────────────────
|
|
27
|
+
let description = {
|
|
28
|
+
did: null,
|
|
29
|
+
inviteCodeRequired: null,
|
|
30
|
+
phoneVerificationRequired: null,
|
|
31
|
+
availableUserDomains: [],
|
|
32
|
+
links: null,
|
|
33
|
+
contact: null
|
|
34
|
+
};
|
|
35
|
+
try {
|
|
36
|
+
const d = (await fetchJSON(`${baseUrl}/xrpc/com.atproto.server.describeServer`));
|
|
37
|
+
description = {
|
|
38
|
+
did: d.did ?? null,
|
|
39
|
+
inviteCodeRequired: d.inviteCodeRequired ?? null,
|
|
40
|
+
phoneVerificationRequired: d.phoneVerificationRequired ?? null,
|
|
41
|
+
availableUserDomains: d.availableUserDomains ?? [],
|
|
42
|
+
links: d.links ?? null,
|
|
43
|
+
contact: d.contact ?? null
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// leave defaults
|
|
48
|
+
}
|
|
49
|
+
// ── account count (paginated) ─────────────────────────────────────────────
|
|
50
|
+
let accountCount = 0;
|
|
51
|
+
try {
|
|
52
|
+
let cursor;
|
|
53
|
+
do {
|
|
54
|
+
const url = `${baseUrl}/xrpc/com.atproto.sync.listRepos?limit=1000` +
|
|
55
|
+
(cursor ? `&cursor=${encodeURIComponent(cursor)}` : '');
|
|
56
|
+
const r = (await fetchJSON(url));
|
|
57
|
+
accountCount += (r.repos ?? []).length;
|
|
58
|
+
cursor = r.cursor;
|
|
59
|
+
} while (cursor);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
accountCount = -1;
|
|
63
|
+
}
|
|
64
|
+
return { health, description, accountCount };
|
|
65
|
+
}
|
package/package.json
CHANGED
|
@@ -1,32 +1,69 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
2
|
+
"name": "@ewanc26/pds-landing",
|
|
3
|
+
"version": "2.0.2",
|
|
4
|
+
"description": "Composable Svelte components for an ATProto PDS landing page — terminal-aesthetic UI with live status fetching.",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "vite dev",
|
|
7
|
+
"build": "vite build && npm run prepack",
|
|
8
|
+
"preview": "vite preview",
|
|
9
|
+
"prepare": "svelte-kit sync || echo ''",
|
|
10
|
+
"prepack": "svelte-kit sync && svelte-package && publint",
|
|
11
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
12
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
13
|
+
"lint": "prettier --check .",
|
|
14
|
+
"format": "prettier --write ."
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"!dist/**/*.test.*",
|
|
19
|
+
"!dist/**/*.spec.*"
|
|
20
|
+
],
|
|
21
|
+
"sideEffects": [
|
|
22
|
+
"**/*.css"
|
|
23
|
+
],
|
|
24
|
+
"svelte": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"svelte": "./dist/index.js",
|
|
31
|
+
"default": "./dist/index.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@sveltejs/kit": ">=2.0.0",
|
|
36
|
+
"svelte": "^5.0.0",
|
|
37
|
+
"tailwindcss": ">=4.0.0"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@ewanc26/ui": "workspace:*"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@sveltejs/adapter-static": "^3.0.10",
|
|
44
|
+
"@sveltejs/kit": "^2.50.2",
|
|
45
|
+
"@sveltejs/package": "^2.5.7",
|
|
46
|
+
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
47
|
+
"@tailwindcss/typography": "^0.5.19",
|
|
48
|
+
"@tailwindcss/vite": "^4.1.18",
|
|
49
|
+
"prettier": "^3.8.1",
|
|
50
|
+
"prettier-plugin-svelte": "^3.4.1",
|
|
51
|
+
"prettier-plugin-tailwindcss": "^0.7.2",
|
|
52
|
+
"publint": "^0.3.17",
|
|
53
|
+
"svelte": "^5.51.0",
|
|
54
|
+
"svelte-check": "^4.4.2",
|
|
55
|
+
"tailwindcss": "^4.1.18",
|
|
56
|
+
"typescript": "^5.9.3",
|
|
57
|
+
"vite": "^7.3.1"
|
|
58
|
+
},
|
|
59
|
+
"keywords": [
|
|
60
|
+
"svelte",
|
|
61
|
+
"atproto",
|
|
62
|
+
"bluesky",
|
|
63
|
+
"pds",
|
|
64
|
+
"landing-page"
|
|
65
|
+
],
|
|
66
|
+
"publishConfig": {
|
|
67
|
+
"access": "public"
|
|
68
|
+
}
|
|
69
|
+
}
|