tangled
alpha
login
or
join now
vielle.dev
/
dnd-astral-powers
0
fork
atom
this repo has no description
0
fork
atom
overview
issues
pulls
pipelines
initial /astral scafold
vielle.dev
1 month ago
ccc39113
1567a67b
verified
This commit was signed with the committer's
known signature
.
vielle.dev
SSH Key Fingerprint:
SHA256:/4bvxqoEh9iMdjAPgcgAgXKZZQTROL3ULiPt6nH9RSs=
+470
4 changed files
expand all
collapse all
unified
split
package.json
pnpm-lock.yaml
src
pages
astral
[user].astro
index.astro
+3
package.json
···
10
"lexgen": "lex-cli generate"
11
},
12
"dependencies": {
0
13
"@atcute/client": "^4.2.1",
0
14
"@atcute/identity-resolver": "^1.2.2",
0
15
"@atcute/lexicons": "^1.2.6",
16
"@atcute/oauth-browser-client": "^2.0.3",
17
"actor-typeahead": "^0.1.2",
···
10
"lexgen": "lex-cli generate"
11
},
12
"dependencies": {
13
+
"@atcute/atproto": "^3.1.10",
14
"@atcute/client": "^4.2.1",
15
+
"@atcute/identity": "^1.1.3",
16
"@atcute/identity-resolver": "^1.2.2",
17
+
"@atcute/identity-resolver-node": "^1.0.3",
18
"@atcute/lexicons": "^1.2.6",
19
"@atcute/oauth-browser-client": "^2.0.3",
20
"actor-typeahead": "^0.1.2",
+28
pnpm-lock.yaml
···
8
9
.:
10
dependencies:
0
0
0
11
'@atcute/client':
12
specifier: ^4.2.1
13
version: 4.2.1
0
0
0
14
'@atcute/identity-resolver':
15
specifier: ^1.2.2
16
version: 1.2.2(@atcute/identity@1.1.3)
0
0
0
17
'@atcute/lexicons':
18
specifier: ^1.2.6
19
version: 1.2.6
···
50
resolution: {integrity: sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==}
51
engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0}
52
0
0
0
53
'@atcute/car@5.0.0':
54
resolution: {integrity: sha512-OIY2xTXv8lSpZsDSn/UYQtJSMvDw5Hi4Q+uyvmiqSM+fht08QRAEq/nxa5YFciPZ3nfDFnZ3//EgJw7QhkSXLQ==}
55
···
64
65
'@atcute/crypto@2.3.0':
66
resolution: {integrity: sha512-w5pkJKCjbNMQu+F4JRHbR3ROQyhi1wbn+GSC6WDQamcYHkZmEZk1/eoI354bIQOOfkEM6aFLv718iskrkon4GQ==}
0
0
0
0
0
0
67
68
'@atcute/identity-resolver@1.2.2':
69
resolution: {integrity: sha512-eUh/UH4bFvuXS0X7epYCeJC/kj4rbBXfSRumLEH4smMVwNOgTo7cL/0Srty+P/qVPoZEyXdfEbS0PHJyzoXmHw==}
···
1686
transitivePeerDependencies:
1687
- supports-color
1688
0
0
0
0
1689
'@atcute/car@5.0.0':
1690
dependencies:
1691
'@atcute/cbor': 2.2.8
···
1714
'@atcute/multibase': 1.1.6
1715
'@atcute/uint8array': 1.0.6
1716
'@noble/secp256k1': 3.0.0
0
0
0
0
0
0
1717
1718
'@atcute/identity-resolver@1.2.2(@atcute/identity@1.1.3)':
1719
dependencies:
···
8
9
.:
10
dependencies:
11
+
'@atcute/atproto':
12
+
specifier: ^3.1.10
13
+
version: 3.1.10
14
'@atcute/client':
15
specifier: ^4.2.1
16
version: 4.2.1
17
+
'@atcute/identity':
18
+
specifier: ^1.1.3
19
+
version: 1.1.3
20
'@atcute/identity-resolver':
21
specifier: ^1.2.2
22
version: 1.2.2(@atcute/identity@1.1.3)
23
+
'@atcute/identity-resolver-node':
24
+
specifier: ^1.0.3
25
+
version: 1.0.3(@atcute/identity-resolver@1.2.2(@atcute/identity@1.1.3))(@atcute/identity@1.1.3)
26
'@atcute/lexicons':
27
specifier: ^1.2.6
28
version: 1.2.6
···
59
resolution: {integrity: sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==}
60
engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0}
61
62
+
'@atcute/atproto@3.1.10':
63
+
resolution: {integrity: sha512-+GKZpOc0PJcdWMQEkTfg/rSNDAAHxmAUGBl60g2az15etqJn5WaUPNGFE2sB7hKpwi5Ue2h/L0OacINcE/JDDQ==}
64
+
65
'@atcute/car@5.0.0':
66
resolution: {integrity: sha512-OIY2xTXv8lSpZsDSn/UYQtJSMvDw5Hi4Q+uyvmiqSM+fht08QRAEq/nxa5YFciPZ3nfDFnZ3//EgJw7QhkSXLQ==}
67
···
76
77
'@atcute/crypto@2.3.0':
78
resolution: {integrity: sha512-w5pkJKCjbNMQu+F4JRHbR3ROQyhi1wbn+GSC6WDQamcYHkZmEZk1/eoI354bIQOOfkEM6aFLv718iskrkon4GQ==}
79
+
80
+
'@atcute/identity-resolver-node@1.0.3':
81
+
resolution: {integrity: sha512-RPH5M4ZRayKRcGnJWUOPVhN5WSYURXXZxKzgVT9lj/WZCH6ij2Vg3P3Eva7GGs0SG1ytnX1XVBTMoIk8nF/SLQ==}
82
+
peerDependencies:
83
+
'@atcute/identity': ^1.0.0
84
+
'@atcute/identity-resolver': ^1.0.0
85
86
'@atcute/identity-resolver@1.2.2':
87
resolution: {integrity: sha512-eUh/UH4bFvuXS0X7epYCeJC/kj4rbBXfSRumLEH4smMVwNOgTo7cL/0Srty+P/qVPoZEyXdfEbS0PHJyzoXmHw==}
···
1704
transitivePeerDependencies:
1705
- supports-color
1706
1707
+
'@atcute/atproto@3.1.10':
1708
+
dependencies:
1709
+
'@atcute/lexicons': 1.2.6
1710
+
1711
'@atcute/car@5.0.0':
1712
dependencies:
1713
'@atcute/cbor': 2.2.8
···
1736
'@atcute/multibase': 1.1.6
1737
'@atcute/uint8array': 1.0.6
1738
'@noble/secp256k1': 3.0.0
1739
+
1740
+
'@atcute/identity-resolver-node@1.0.3(@atcute/identity-resolver@1.2.2(@atcute/identity@1.1.3))(@atcute/identity@1.1.3)':
1741
+
dependencies:
1742
+
'@atcute/identity': 1.1.3
1743
+
'@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.3)
1744
+
'@atcute/lexicons': 1.2.6
1745
1746
'@atcute/identity-resolver@1.2.2(@atcute/identity@1.1.3)':
1747
dependencies:
+217
src/pages/astral/[user].astro
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
+
---
2
+
export const prerender = false;
3
+
import { getPdsEndpoint, isAtprotoDid } from "@atcute/identity";
4
+
import { isHandle } from "@atcute/lexicons/syntax";
5
+
import Base from "../../Base.astro";
6
+
7
+
// slug user to pds
8
+
const { user } = Astro.params;
9
+
if (!user)
10
+
return new Response("", {
11
+
status: 308,
12
+
headers: {
13
+
Location: "/astral",
14
+
},
15
+
});
16
+
const actor = (isAtprotoDid(user) || isHandle(user)) && user;
17
+
if (!actor)
18
+
return new Response(
19
+
`<h1><code>${user}</code> is not a valid identifier</h1><p>Head to <a href="/">home</a> or <a href="/astral">astral powers</a></p>`,
20
+
{
21
+
status: 400,
22
+
headers: {
23
+
"Content-Type": "text/html",
24
+
},
25
+
}
26
+
);
27
+
---
28
+
29
+
<Base title={actor}>
30
+
<script set:html={`window.actor = "${actor}";`} />
31
+
<script>
32
+
import { Client, simpleFetchHandler } from "@atcute/client";
33
+
import {
34
+
normalPowers,
35
+
parentPowers,
36
+
powerData,
37
+
powers,
38
+
type powerId,
39
+
} from "../../consts/astral";
40
+
import { DevVielleDndAstral } from "../../lexicons";
41
+
import {
42
+
CompositeDidDocumentResolver,
43
+
CompositeHandleResolver,
44
+
DohJsonHandleResolver,
45
+
LocalActorResolver,
46
+
PlcDidDocumentResolver,
47
+
WebDidDocumentResolver,
48
+
WellKnownHandleResolver,
49
+
} from "@atcute/identity-resolver";
50
+
51
+
const actor =
52
+
"actor" in window
53
+
? (window.actor as
54
+
| undefined
55
+
| `did:plc:${string}`
56
+
| `did:web:${string}`
57
+
| `${string}.${string}`)
58
+
: undefined;
59
+
if (!actor) throw "loaded without actor in window. missing `window.actor`";
60
+
61
+
const actorResolver = new LocalActorResolver({
62
+
handleResolver: new CompositeHandleResolver({
63
+
methods: {
64
+
dns: new DohJsonHandleResolver({
65
+
dohUrl: "https://mozilla.cloudflare-dns.com/dns-query",
66
+
}),
67
+
http: new WellKnownHandleResolver(),
68
+
},
69
+
}),
70
+
didDocumentResolver: new CompositeDidDocumentResolver({
71
+
methods: {
72
+
plc: new PlcDidDocumentResolver(),
73
+
web: new WebDidDocumentResolver(),
74
+
},
75
+
}),
76
+
});
77
+
78
+
const doc = await actorResolver
79
+
.resolve(actor)
80
+
.catch((err) => console.error(err));
81
+
if (!doc) {
82
+
document.body.innerText =
83
+
"<p>Failed to resolve user. Check browser console for more info</p>";
84
+
throw "";
85
+
}
86
+
87
+
const client = new Client({
88
+
handler: simpleFetchHandler({ service: doc.pds }),
89
+
});
90
+
91
+
const res = await client
92
+
.get("com.atproto.repo.getRecord", {
93
+
params: {
94
+
repo: doc.did,
95
+
collection: "dev.vielle.dnd.astral",
96
+
rkey: "self",
97
+
},
98
+
})
99
+
.then((res) =>
100
+
res.ok
101
+
? res.data.value
102
+
: (() => {
103
+
throw res.data;
104
+
})()
105
+
)
106
+
.then(
107
+
async (res) =>
108
+
await DevVielleDndAstral.mainSchema["~standard"].validate(res)
109
+
)
110
+
.then((res) =>
111
+
!res.issues
112
+
? res.value
113
+
: (() => {
114
+
throw res.issues;
115
+
})()
116
+
)
117
+
.catch((err) => {
118
+
console.error(err);
119
+
return null;
120
+
});
121
+
122
+
const isValidPowers =
123
+
(res?.powers.reduce(
124
+
(acc, curr) =>
125
+
acc &&
126
+
curr !== "dev.vielle.dnd.power#invalid" &&
127
+
curr !== "dev.vielle.dnd.power#locked",
128
+
true
129
+
) &&
130
+
res?.powers.reduce(
131
+
(acc, curr) =>
132
+
acc &&
133
+
parentPowers[curr].reduce(
134
+
(acc, curr) => acc && res.powers.includes(curr),
135
+
true
136
+
),
137
+
true
138
+
) &&
139
+
res?.points >= res?.powers.length) ??
140
+
false;
141
+
142
+
// custom render
143
+
console.log(res, isValidPowers);
144
+
if (res) {
145
+
const powerUl = document.createElement("dl");
146
+
powerUl.append(
147
+
...(res?.powers
148
+
.map((x) =>
149
+
normalPowers.includes(x as (typeof normalPowers)[number])
150
+
? (x as powerId)
151
+
: powers.INVALID
152
+
)
153
+
.map(
154
+
(x) => [x, powerData[x]] as [powerId, (typeof powerData)[powerId]]
155
+
)
156
+
.map(([id, data]) => {
157
+
const dt = document.createElement("dt");
158
+
const dd = document.createElement("dd");
159
+
const dl = document.createElement("dl");
160
+
161
+
console.log(id, data);
162
+
163
+
dt.innerText = data.name;
164
+
165
+
console.log(id, data);
166
+
167
+
const description = [
168
+
document.createElement("dt"),
169
+
document.createElement("dd"),
170
+
];
171
+
description[0].innerText = "description";
172
+
description[1].innerText = data.description;
173
+
174
+
const img: [HTMLElement, HTMLElement, HTMLImageElement] = [
175
+
document.createElement("dt"),
176
+
document.createElement("dd"),
177
+
document.createElement("img"),
178
+
];
179
+
img[0].innerText = "Image";
180
+
img[2].src = data.image.src;
181
+
img[2].alt = data.image.alt;
182
+
img[1].append(img.pop() ?? "");
183
+
184
+
const meta = [
185
+
document.createElement("dt"),
186
+
document.createElement("dd"),
187
+
document.createElement("dl"),
188
+
];
189
+
meta[0].innerText = "Meta";
190
+
meta[2].append(
191
+
...data.meta
192
+
.map((x) => {
193
+
const res = [
194
+
document.createElement("dt"),
195
+
document.createElement("dd"),
196
+
];
197
+
res[0].innerText = x.name;
198
+
res[1].innerText = x.value;
199
+
return res;
200
+
})
201
+
.flat()
202
+
);
203
+
meta[1].append(meta.pop() ?? "");
204
+
205
+
dl.append(...description, ...img, ...meta);
206
+
207
+
dd.append(dl);
208
+
return [dt, dd];
209
+
})
210
+
.flat() ?? [])
211
+
);
212
+
document.body.prepend(powerUl);
213
+
} else {
214
+
document.body.append("No data for user");
215
+
}
216
+
</script>
217
+
</Base>
+222
src/pages/astral/index.astro
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
+
---
2
+
import Base from "../../Base.astro";
3
+
---
4
+
5
+
<Base title="Astral Powers">
6
+
<style>
7
+
:global(dt) {
8
+
font-weight: bold;
9
+
}
10
+
11
+
:global(.power) {
12
+
border-left: 1px solid white;
13
+
padding-inline-start: 10px;
14
+
}
15
+
</style>
16
+
<script>
17
+
import "@atcute/atproto";
18
+
import { getAuth } from "../../lib/auth";
19
+
import {
20
+
parentPowers,
21
+
powerTree,
22
+
powerData,
23
+
powers,
24
+
type powerId,
25
+
normalPowers,
26
+
} from "../../consts/astral";
27
+
import { DevVielleDndAstral } from "../../lexicons";
28
+
const [client, session, did] = await getAuth(true);
29
+
30
+
const res = await client
31
+
.get("com.atproto.repo.getRecord", {
32
+
params: {
33
+
repo: did,
34
+
collection: "dev.vielle.dnd.astral",
35
+
rkey: "self",
36
+
},
37
+
})
38
+
.then((res) =>
39
+
res.ok
40
+
? res.data.value
41
+
: (() => {
42
+
throw res.data;
43
+
})()
44
+
)
45
+
.then(
46
+
async (res) =>
47
+
await DevVielleDndAstral.mainSchema["~standard"].validate(res)
48
+
)
49
+
.then((res) =>
50
+
!res.issues
51
+
? res.value
52
+
: (() => {
53
+
throw res.issues;
54
+
})()
55
+
)
56
+
.catch((err) => {
57
+
console.error(err);
58
+
return null;
59
+
});
60
+
61
+
const isValidPowers =
62
+
(res?.powers.reduce(
63
+
(acc, curr) =>
64
+
acc &&
65
+
curr !== "dev.vielle.dnd.power#invalid" &&
66
+
curr !== "dev.vielle.dnd.power#locked",
67
+
true
68
+
) &&
69
+
res?.powers.reduce(
70
+
(acc, curr) =>
71
+
acc &&
72
+
parentPowers[curr].reduce(
73
+
(acc, curr) => acc && res.powers.includes(curr),
74
+
true
75
+
),
76
+
true
77
+
) &&
78
+
res?.points >= res?.powers.length) ??
79
+
false;
80
+
81
+
// custom render
82
+
console.log(res, isValidPowers);
83
+
84
+
const renderPower = (power: typeof powerTree) => {
85
+
const li = document.createElement("li");
86
+
const dl = document.createElement("dl");
87
+
dl.classList.add("power");
88
+
li.append(dl);
89
+
90
+
if (power.id === powers.LOCKED) {
91
+
dl.innerHTML = `<dt>Status</dt><dd>Locked</dd>`;
92
+
return li;
93
+
}
94
+
95
+
const name = [document.createElement("dt"), document.createElement("dd")];
96
+
name[0].innerText = "name";
97
+
name[1].innerText = powerData[power.id].name;
98
+
99
+
const description = [
100
+
document.createElement("dt"),
101
+
document.createElement("dd"),
102
+
];
103
+
description[0].innerText = "description";
104
+
description[1].innerText = powerData[power.id].description;
105
+
106
+
const img: [HTMLElement, HTMLElement, HTMLImageElement] = [
107
+
document.createElement("dt"),
108
+
document.createElement("dd"),
109
+
document.createElement("img"),
110
+
];
111
+
img[0].innerText = "Image";
112
+
img[2].src = powerData[power.id].image.src;
113
+
img[2].alt = powerData[power.id].image.alt;
114
+
img[1].append(img.pop() ?? "");
115
+
116
+
const meta = [
117
+
document.createElement("dt"),
118
+
document.createElement("dd"),
119
+
document.createElement("dl"),
120
+
];
121
+
meta[0].innerText = "Meta";
122
+
meta[2].append(
123
+
...powerData[power.id].meta
124
+
.map((x) => {
125
+
const res = [
126
+
document.createElement("dt"),
127
+
document.createElement("dd"),
128
+
];
129
+
res[0].innerText = x.name;
130
+
res[1].innerText = x.value;
131
+
return res;
132
+
})
133
+
.flat()
134
+
);
135
+
meta[1].append(meta.pop() ?? "");
136
+
137
+
const children = [
138
+
document.createElement("dt"),
139
+
document.createElement("dd"),
140
+
document.createElement("ul"),
141
+
];
142
+
children[0].innerText = "children";
143
+
children[2].append(...power.children.map(renderPower));
144
+
children[1].append(children.pop() ?? "");
145
+
146
+
dl.append(...name, ...img, ...description, ...meta, ...children);
147
+
148
+
return li;
149
+
};
150
+
151
+
const ul = document.createElement("ul");
152
+
ul.append(renderPower(powerTree));
153
+
document.body.append(ul);
154
+
155
+
const powerUl = document.createElement("dl");
156
+
powerUl.append(
157
+
...(res?.powers
158
+
.map((x) =>
159
+
normalPowers.includes(x as (typeof normalPowers)[number])
160
+
? (x as powerId)
161
+
: powers.INVALID
162
+
)
163
+
.map((x) => [x, powerData[x]] as [powerId, (typeof powerData)[powerId]])
164
+
.map(([id, data]) => {
165
+
const dt = document.createElement("dt");
166
+
const dd = document.createElement("dd");
167
+
const dl = document.createElement("dl");
168
+
169
+
console.log(id, data);
170
+
171
+
dt.innerText = data.name;
172
+
173
+
console.log(id, data);
174
+
175
+
const description = [
176
+
document.createElement("dt"),
177
+
document.createElement("dd"),
178
+
];
179
+
description[0].innerText = "description";
180
+
description[1].innerText = data.description;
181
+
182
+
const img: [HTMLElement, HTMLElement, HTMLImageElement] = [
183
+
document.createElement("dt"),
184
+
document.createElement("dd"),
185
+
document.createElement("img"),
186
+
];
187
+
img[0].innerText = "Image";
188
+
img[2].src = data.image.src;
189
+
img[2].alt = data.image.alt;
190
+
img[1].append(img.pop() ?? "");
191
+
192
+
const meta = [
193
+
document.createElement("dt"),
194
+
document.createElement("dd"),
195
+
document.createElement("dl"),
196
+
];
197
+
meta[0].innerText = "Meta";
198
+
meta[2].append(
199
+
...data.meta
200
+
.map((x) => {
201
+
const res = [
202
+
document.createElement("dt"),
203
+
document.createElement("dd"),
204
+
];
205
+
res[0].innerText = x.name;
206
+
res[1].innerText = x.value;
207
+
return res;
208
+
})
209
+
.flat()
210
+
);
211
+
meta[1].append(meta.pop() ?? "");
212
+
213
+
dl.append(...description, ...img, ...meta);
214
+
215
+
dd.append(dl);
216
+
return [dt, dd];
217
+
})
218
+
.flat() ?? [])
219
+
);
220
+
document.body.prepend(powerUl);
221
+
</script>
222
+
</Base>