+47
-9
atproto-notifications/package-lock.json
+47
-9
atproto-notifications/package-lock.json
···
8
8
"name": "atproto-notifications",
9
9
"version": "0.0.0",
10
10
"dependencies": {
11
+
"@atcute/client": "^4.0.3",
11
12
"@atcute/identity-resolver": "^1.1.3",
12
13
"@uidotdev/usehooks": "^2.4.1",
13
14
"lexicons": "file:../lexicons",
14
15
"psl": "^1.15.0",
15
16
"react": "^19.1.0",
16
17
"react-dom": "^19.1.0",
18
+
"react-error-boundary": "^6.0.0",
17
19
"react-router": "^7.6.3",
18
20
"react-time-ago": "^7.3.3",
19
21
"reactjs-popup": "^2.0.6"
···
33
35
}
34
36
},
35
37
"../lexicons": {
36
-
"version": "0.0.1"
38
+
"version": "0.0.1",
39
+
"dependencies": {
40
+
"@atcute/client": "^4.0.3",
41
+
"@atcute/identity-resolver": "^1.1.3",
42
+
"jsonpath-plus": "^10.3.0",
43
+
"psl": "^1.15.0"
44
+
}
37
45
},
38
46
"node_modules/@ampproject/remapping": {
39
47
"version": "2.3.0",
···
49
57
"node": ">=6.0.0"
50
58
}
51
59
},
60
+
"node_modules/@atcute/client": {
61
+
"version": "4.0.3",
62
+
"resolved": "https://registry.npmjs.org/@atcute/client/-/client-4.0.3.tgz",
63
+
"integrity": "sha512-RIOZWFVLca/HiPAAUDqQPOdOreCxTbL5cb+WUf5yqQOKIu5yEAP3eksinmlLmgIrlr5qVOE7brazUUzaskFCfw==",
64
+
"license": "MIT",
65
+
"dependencies": {
66
+
"@atcute/identity": "^1.0.2",
67
+
"@atcute/lexicons": "^1.0.3"
68
+
}
69
+
},
52
70
"node_modules/@atcute/identity": {
53
71
"version": "1.0.3",
54
72
"resolved": "https://registry.npmjs.org/@atcute/identity/-/identity-1.0.3.tgz",
55
73
"integrity": "sha512-mNMxbKHFGys03A8JXKk0KfMBzdd0vrYMzZZWjpw1nYTs0+ea6bo5S1hwqVUZxHdo1gFHSe/t63jxQIF4yL9aKw==",
56
74
"license": "0BSD",
57
-
"peer": true,
58
75
"dependencies": {
59
76
"@atcute/lexicons": "^1.0.4",
60
77
"@badrap/valita": "^0.4.5"
···
314
331
},
315
332
"peerDependencies": {
316
333
"@babel/core": "^7.0.0-0"
334
+
}
335
+
},
336
+
"node_modules/@babel/runtime": {
337
+
"version": "7.27.6",
338
+
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz",
339
+
"integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==",
340
+
"license": "MIT",
341
+
"engines": {
342
+
"node": ">=6.9.0"
317
343
}
318
344
},
319
345
"node_modules/@babel/template": {
···
949
975
}
950
976
},
951
977
"node_modules/@eslint/plugin-kit": {
952
-
"version": "0.3.2",
953
-
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.2.tgz",
954
-
"integrity": "sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==",
978
+
"version": "0.3.4",
979
+
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz",
980
+
"integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==",
955
981
"dev": true,
956
982
"license": "Apache-2.0",
957
983
"dependencies": {
958
-
"@eslint/core": "^0.15.0",
984
+
"@eslint/core": "^0.15.1",
959
985
"levn": "^0.4.1"
960
986
},
961
987
"engines": {
···
963
989
}
964
990
},
965
991
"node_modules/@eslint/plugin-kit/node_modules/@eslint/core": {
966
-
"version": "0.15.0",
967
-
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.0.tgz",
968
-
"integrity": "sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==",
992
+
"version": "0.15.1",
993
+
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz",
994
+
"integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==",
969
995
"dev": true,
970
996
"license": "Apache-2.0",
971
997
"dependencies": {
···
3062
3088
},
3063
3089
"peerDependencies": {
3064
3090
"react": "^19.1.0"
3091
+
}
3092
+
},
3093
+
"node_modules/react-error-boundary": {
3094
+
"version": "6.0.0",
3095
+
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.0.0.tgz",
3096
+
"integrity": "sha512-gdlJjD7NWr0IfkPlaREN2d9uUZUlksrfOx7SX62VRerwXbMY6ftGCIZua1VG1aXFNOimhISsTq+Owp725b9SiA==",
3097
+
"license": "MIT",
3098
+
"dependencies": {
3099
+
"@babel/runtime": "^7.12.5"
3100
+
},
3101
+
"peerDependencies": {
3102
+
"react": ">=16.13.1"
3065
3103
}
3066
3104
},
3067
3105
"node_modules/react-is": {
+2
atproto-notifications/package.json
+2
atproto-notifications/package.json
···
11
11
"preview": "vite preview"
12
12
},
13
13
"dependencies": {
14
+
"@atcute/client": "^4.0.3",
14
15
"@atcute/identity-resolver": "^1.1.3",
15
16
"@uidotdev/usehooks": "^2.4.1",
16
17
"lexicons": "file:../lexicons",
17
18
"psl": "^1.15.0",
18
19
"react": "^19.1.0",
19
20
"react-dom": "^19.1.0",
21
+
"react-error-boundary": "^6.0.0",
20
22
"react-router": "^7.6.3",
21
23
"react-time-ago": "^7.3.3",
22
24
"reactjs-popup": "^2.0.6"
+21
atproto-notifications/src/components/Notification.css
+21
atproto-notifications/src/components/Notification.css
···
7
7
box-sizing: border-box;
8
8
display: flex;
9
9
justify-content: space-between;
10
+
gap: 0.5rem;
10
11
}
11
12
a.notification {
12
13
font: inherit;
···
22
23
border-bottom-color: hsla(0, 0%, 0%, 0.3);
23
24
}
24
25
26
+
.notification.error {
27
+
background: hsla(347, 72%, 20%, 0.333);
28
+
align-items: center;
29
+
}
30
+
.notification.error p {
31
+
margin: 0;
32
+
}
33
+
34
+
.notification-info {
35
+
display: flex;
36
+
align-items: baseline;
37
+
}
38
+
25
39
.handle {
26
40
color: skyblue;
41
+
}
42
+
43
+
.notification-context {
44
+
font-size: 0.8rem;
45
+
opacity: 0.667;
46
+
margin: 0.25rem 0 0;
47
+
max-width: 48em;
27
48
}
28
49
29
50
.notification-when {
+49
-15
atproto-notifications/src/components/Notification.tsx
+49
-15
atproto-notifications/src/components/Notification.tsx
···
1
+
import { useState, useEffect } from 'react';
1
2
import ReactTimeAgo from 'react-time-ago';
2
3
import psl from 'psl';
3
-
import lexicons from 'lexicons';
4
+
import { default as lexicons, getLink, getContext } from 'lexicons';
4
5
import { resolveDid } from '../atproto/resolve';
5
6
import { Fetch } from './Fetch';
6
7
7
8
import './Notification.css';
8
9
10
+
export function fallbackRender({ error, resetErrorBoundary }) {
11
+
console.error('rendering fallback for error', error);
12
+
return (
13
+
<div className="notification error">
14
+
<p>sorry, something went wrong trying to show this notification</p>
15
+
<p><button onClick={resetErrorBoundary}>retry</button></p>
16
+
</div>
17
+
);
18
+
}
19
+
9
20
export function Notification({ app, group, source, source_record, source_did, subject, timestamp }) {
21
+
const [resolvedLink, setResolvedLink] = useState(null);
22
+
const [resolvedContext, setResolvedContext] = useState([]);
23
+
24
+
useEffect(() => {
25
+
(async () => {
26
+
const link = await getLink(source, source_record, subject);
27
+
if (link) setResolvedLink(link);
28
+
})();
29
+
(async() => {
30
+
const context = await getContext(source, source_record, subject);
31
+
setResolvedContext(context);
32
+
})();
33
+
}, [source, source_record, subject]);
10
34
11
35
// TODO: clean up / move this to lexicons package?
12
36
let title = source;
···
23
47
let link = lex?.clients[0]?.notifications;
24
48
appName = lex?.name;
25
49
const sourceRemainder = source.slice(app.length + 1);
26
-
title = lex?.known_sources[sourceRemainder] ?? source;
50
+
title = lex?.known_sources[sourceRemainder]?.name ?? source;
27
51
28
52
let directLink;
29
53
if (subject.startsWith('did:')) {
···
48
72
49
73
directLink = lex
50
74
?.clients[0]
51
-
?.direct_links[`at_uri:${sourceRemainder}`]
75
+
?.direct_links?.[`at_uri:${sourceRemainder}`]
52
76
?.replace('{subject.did}', did)
53
77
?.replace('{subject.collection}', collection)
54
78
?.replace('{subject.rkey}', rest.join('/') || null)
···
56
80
?.replace('{source_record.collection}', sCollection)
57
81
?.replace('{source_record.rkey}', sRest.join('/') || null);
58
82
}
59
-
link = directLink ?? link;
83
+
link = resolvedLink ?? directLink ?? link;
84
+
85
+
let contextClipped = resolvedContext.join(' ');
86
+
if (contextClipped.length > 240) {
87
+
contextClipped = contextClipped.slice(0, 239) + 'โฆ';
88
+
}
60
89
61
90
const contents = (
62
91
<>
···
64
93
{icon && (
65
94
<img className="app-icon" src={icon} title={appName ?? app} alt="" />
66
95
)}
67
-
{title} from
68
-
{' '}
69
-
{source_did ? (
70
-
<Fetch
71
-
using={resolveDid}
72
-
args={[source_did]}
73
-
ok={handle => <span className="handle">@{handle}</span>}
74
-
/>
75
-
) : (
76
-
source_record
77
-
)}
96
+
<div>
97
+
{title} from
98
+
{' '}
99
+
{source_did ? (
100
+
<Fetch
101
+
using={resolveDid}
102
+
args={[source_did]}
103
+
ok={handle => <span className="handle">@{handle}</span>}
104
+
/>
105
+
) : (
106
+
source_record
107
+
)}
108
+
{contextClipped.length > 0 && (
109
+
<p className="notification-context">{contextClipped}</p>
110
+
)}
111
+
</div>
78
112
</div>
79
113
{timestamp && (
80
114
<div className="notification-when">
+35
-25
atproto-notifications/src/components/NotificationSettings.tsx
+35
-25
atproto-notifications/src/components/NotificationSettings.tsx
···
1
-
import { useState } from 'react';
2
-
1
+
import { useState, useCallback } from 'react';
2
+
import { Link } from 'react-router';
3
+
import { GetJson, postJson } from './Fetch';
4
+
import { ButtonGroup } from './Buttons';
3
5
4
6
export function NotificationSettings({ secondary, secondaryFilter }) {
5
-
6
-
7
-
// const [notifyToggleCounter, setNotifyToggleCounter] = useState(0);
8
-
9
-
// // TODO move up (to chrome?) so it syncs
10
-
// const setGlobalNotifications = useCallback(async enabled => {
11
-
// const host = import.meta.env.VITE_NOTIFICATIONS_HOST;
12
-
// const url = new URL('/global-notify', host);
13
-
// try {
14
-
// await postJson(url, JSON.stringify({ notify_enabled: enabled }), true)
15
-
// } catch (err) {
16
-
// console.error('failed to set self-notify setting', err);
17
-
// }
18
-
// setNotifyToggleCounter(n => n + 1);
19
-
// });
7
+
const [notifyToggleCounter, setNotifyToggleCounter] = useState(0);
20
8
21
-
9
+
// TODO move up (to chrome?) so it syncs
10
+
const setGlobalNotifications = useCallback(async enabled => {
11
+
const host = import.meta.env.VITE_NOTIFICATIONS_HOST;
12
+
const url = new URL('/global-notify', host);
13
+
try {
14
+
await postJson(url, JSON.stringify({ notify_enabled: enabled }), true)
15
+
} catch (err) {
16
+
console.error('failed to set self-notify setting', err);
17
+
}
18
+
setNotifyToggleCounter(n => n + 1);
19
+
});
22
20
21
+
if (secondary !== 'all') return;
23
22
24
-
if (secondary === 'all') {
25
-
return <p>Notifications default: [todo: toggle mute], unknown sources: [toggle mute]</p>;
26
-
}
27
-
if (secondaryFilter === null) {
28
-
return null;
29
-
}
30
23
return (
31
-
<p>{secondaryFilter} [todo: toggle mute]</p>
24
+
<div className="feed-filter-type">
25
+
<h4>All notifications:</h4>
26
+
<GetJson
27
+
key={notifyToggleCounter}
28
+
endpoint="/global-notify"
29
+
credentials
30
+
ok={({ notify_enabled }) => (
31
+
<ButtonGroup
32
+
options={[
33
+
{val: 'paused', label: <>โธ pause{!notify_enabled && 'd'}</>},
34
+
{val: 'active', label: <>โถ {notify_enabled ? 'notifications active' : 'enable notifications'}</>},
35
+
]}
36
+
current={notify_enabled ? 'active' : 'paused'}
37
+
onChange={val => setGlobalNotifications(val === 'active')}
38
+
/>
39
+
)}
40
+
/>
41
+
</div>
32
42
);
33
43
}
+4
-21
atproto-notifications/src/components/SecretPassword.jsx
+4
-21
atproto-notifications/src/components/SecretPassword.jsx
···
2
2
import { PostJson } from './Fetch';
3
3
4
4
export function SecretPassword({ did, role }) {
5
-
const [begun, setBegun] = useState(false);
6
-
const [pw, setPw] = useState('');
7
5
const [submission, setSubmission] = useState(0);
8
6
const [submitting, setSubmitting] = useState(false);
9
7
···
15
13
16
14
return (
17
15
<form method="post" onSubmit={handleSubmit}>
18
-
<h2>Secret password required</h2>
19
-
<p>This demo is not ready for public yet, but you can get early access as a <a href="https://github.com/sponsors/uniphil/" target="_blank">github sponsor</a> or <a href="https://ko-fi.com/bad_example" target="_blank">ko-fi supporter</a>.</p>
16
+
<h2>Secret early access</h2>
17
+
<p>This demo is still in development! Your support helps keep it going: <a href="https://github.com/sponsors/uniphil/" target="_blank">github sponsors</a>, <a href="https://ko-fi.com/bad_example" target="_blank">ko-fi</a>.</p>
20
18
21
19
{submitting ? (
22
20
<PostJson
23
21
key={submission}
24
22
endpoint="/super-top-secret-access"
25
-
data={{ secret_password: pw }}
23
+
data={{ secret_password: "letmein" }}
26
24
credentials
27
25
loading={() => (<>Checking…</>)}
28
26
error={e => {
···
50
48
/>
51
49
) : (
52
50
<p>
53
-
<label>
54
-
Password:
55
-
{' '}
56
-
<input
57
-
type="text"
58
-
value={pw}
59
-
onFocus={() => setBegun(true)}
60
-
onChange={e => setPw(e.target.value)}
61
-
/>
62
-
</label>
63
-
{' '}
64
-
{begun && (
65
-
<button type="submit" className="subtle">
66
-
unlock
67
-
</button>
68
-
)}
51
+
<button type="submit">Let me in</button>
69
52
</p>
70
53
)}
71
54
</form>
+1
-1
atproto-notifications/src/components/WhoAmI.tsx
+1
-1
atproto-notifications/src/components/WhoAmI.tsx
+2
-2
atproto-notifications/src/index.css
+2
-2
atproto-notifications/src/index.css
+4
atproto-notifications/src/pages/Feed.css
+4
atproto-notifications/src/pages/Feed.css
+63
-14
atproto-notifications/src/pages/Feed.tsx
+63
-14
atproto-notifications/src/pages/Feed.tsx
···
1
-
import { useEffect, useState } from 'react';
1
+
import { useCallback, useEffect, useState } from 'react';
2
+
import { ErrorBoundary } from 'react-error-boundary';
2
3
import Popup from 'reactjs-popup';
3
4
import { getNotifications, getSecondary } from '../db';
4
5
import { ButtonGroup } from '../components/Buttons';
5
6
import { NotificationSettings } from '../components/NotificationSettings';
6
-
import { Notification } from '../components/Notification';
7
-
import { GetJson } from '../components/Fetch';
7
+
import { Notification, fallbackRender } from '../components/Notification';
8
+
import { GetJson, PostJson } from '../components/Fetch';
8
9
import psl from 'psl';
9
10
import lexicons from 'lexicons';
10
11
11
12
import './feed.css';
12
13
13
14
function FilterPref({ secondary, value }) {
14
-
return (
15
+
const [wanted, setWanted] = useState(null);
16
+
const [updateCount, setUpdateCount] = useState(0);
17
+
const v = `${updateCount}:${wanted}`;
18
+
19
+
const setFilterBool = useCallback(val => {
20
+
setUpdateCount(n => n + 1);
21
+
setWanted(val === 'notify');
22
+
});
23
+
const resetFilter = useCallback(() => {
24
+
setUpdateCount(n => n + 1);
25
+
setWanted(null);
26
+
});
27
+
28
+
const trigger = useCallback(notify => {
29
+
let icon = 'โ', title = 'Default (inherit)';
30
+
if (notify === true) {
31
+
icon = '๐';
32
+
title = 'Always notify';
33
+
} else if (notify === false) {
34
+
icon = '๐ซ';
35
+
title = 'Notifications muted';
36
+
}
37
+
return (
38
+
<div className="filter-pref-trigger" title={title}>
39
+
{icon}
40
+
</div>
41
+
);
42
+
});
43
+
44
+
const renderFilter = useCallback(({ notify }) => (
15
45
<Popup
16
-
trigger={
17
-
<div className="filter-pref-trigger">
18
-
โ
19
-
</div>
20
-
}
46
+
key="x"
47
+
trigger={trigger(notify)}
21
48
position={['bottom center']}
22
49
closeOnDocumentClick
23
50
>
···
28
55
{ val: 'notify', label: 'notify' },
29
56
{ val: 'mute' },
30
57
]}
31
-
current={null}
58
+
current={notify === null ? null : notify ? 'notify' : 'mute'}
59
+
onChange={setFilterBool}
32
60
/>
33
-
{/*<button className="subtle">reset</button>*/}
61
+
{notify !== null && (
62
+
<button className="subtle" onClick={resetFilter}>reset</button>
63
+
)}
34
64
</div>
35
65
</Popup>
36
-
);
66
+
));
67
+
68
+
const common = {
69
+
endpoint: '/notification-filter',
70
+
credentials: true,
71
+
ok: renderFilter,
72
+
loading: () => <>…</>,
73
+
};
74
+
75
+
return updateCount === 0
76
+
? <GetJson key={v}
77
+
params={{ selector: secondary, selection: value }}
78
+
{...common}
79
+
/>
80
+
: <PostJson key={v}
81
+
data={{ selector: secondary, selection: value, notify: wanted }}
82
+
{...common}
83
+
/>;
37
84
}
38
85
39
86
function SecondaryFilter({ inc, secondary, current, onUpdate }) {
···
76
123
const lex = lexicons[appPrefix];
77
124
icon = lex?.clients[0]?.icon;
78
125
appName = lex?.name;
79
-
title = lex?.known_sources[k.slice(app.length + 1)] ?? k;
126
+
title = lex?.known_sources[k.slice(app.length + 1)]?.name ?? k;
80
127
81
128
} else if (secondary === 'group') {
82
129
···
186
233
187
234
<div className="feed-notifications">
188
235
{feed.map(([k, n]) => (
189
-
<Notification key={k} {...n} />
236
+
<ErrorBoundary key={k} fallbackRender={fallbackRender}>
237
+
<Notification {...n} />
238
+
</ErrorBoundary>
190
239
))}
191
240
</div>
192
241
</div>
+1
-1
atproto-notifications/src/service-worker.ts
+1
-1
atproto-notifications/src/service-worker.ts
···
37
37
// TODO: user pref for alt client -> prefer that client's icon
38
38
const lex = lexicons[appPrefix];
39
39
const icon = lex?.clients[0]?.icon;
40
-
const title = lex?.known_sources[source.slice(app.length + 1)] ?? source;
40
+
const title = lex?.known_sources[source.slice(app.length + 1)]?.name ?? source;
41
41
const body = `from @${handle} on ${lex?.name ?? app}`;
42
42
43
43
// const tag = 'simple-push-demo-notification-tag';
+67
lexicons/atproto.js
+67
lexicons/atproto.js
···
1
+
import { Client, CredentialManager, ok, simpleFetchHandler } from '@atcute/client';
2
+
import { CompositeDidDocumentResolver, PlcDidDocumentResolver, WebDidDocumentResolver } from '@atcute/identity-resolver';
3
+
4
+
// cleanup needed
5
+
6
+
const docResolver = new CompositeDidDocumentResolver({
7
+
methods: {
8
+
plc: new PlcDidDocumentResolver(),
9
+
web: new WebDidDocumentResolver(),
10
+
},
11
+
});
12
+
13
+
async function resolve_did(did) {
14
+
return await docResolver.resolve(did);
15
+
}
16
+
17
+
function pds({ service }) {
18
+
if (!service) {
19
+
throw new Error('missing service from identity doc');
20
+
}
21
+
const { serviceEndpoint } = service[0];
22
+
if (!serviceEndpoint) {
23
+
throw new Error('missing serviceEndpoint from identity service array');
24
+
}
25
+
return serviceEndpoint;
26
+
}
27
+
28
+
29
+
async function get_pds_record(endpoint, did, collection, rkey) {
30
+
const handler = simpleFetchHandler({ service: endpoint });
31
+
const rpc = new Client({ handler });
32
+
const { ok, data } = await rpc.get('com.atproto.repo.getRecord', {
33
+
params: { repo: did, collection, rkey },
34
+
});
35
+
if (!ok) throw new Error('fetching pds record failed');
36
+
return data;
37
+
}
38
+
39
+
function parse_at_uri(uri) {
40
+
let collection, rkey;
41
+
if (!uri.startsWith('at://')) {
42
+
throw new Error('invalid at-uri: did not start with "at://"');
43
+
}
44
+
let remaining = uri.slice('at://'.length); // remove the at:// prefix
45
+
remaining = remaining.split('#')[0]; // hash is valid in at-uri but we don't handle them
46
+
remaining = remaining.split('?')[0]; // query is valid in at-uri but we don't handle it
47
+
const segments = remaining.split('/');
48
+
if (segments.length === 0) {
49
+
throw new Error('invalid at-uri: could not find did after "at://"');
50
+
}
51
+
const did = segments[0];
52
+
if (segments.length > 1) {
53
+
collection = segments[1];
54
+
}
55
+
if (segments.length > 2) {
56
+
rkey = segments.slice(2).join('/'); // hmm are slashes actually valid in rkey?
57
+
}
58
+
return { did, collection, rkey };
59
+
}
60
+
61
+
export async function getAtUri(atUri) {
62
+
const { did, collection, rkey } = parse_at_uri(atUri);
63
+
const doc = await resolve_did(did);
64
+
const endpoint = pds(doc);
65
+
const { value } = await get_pds_record(endpoint, did, collection, rkey);
66
+
return value;
67
+
}
+106
lexicons/bits.js
+106
lexicons/bits.js
···
1
+
import psl from 'psl';
2
+
import { JSONPath } from 'jsonpath-plus';
3
+
import defs from './defs.js';
4
+
import { getAtUri } from './atproto.js';
5
+
6
+
export function getBits(source) {
7
+
const [nsid, ...rp] = source.split(':');
8
+
const segments = nsid.split('.');
9
+
const group = segments.slice(0, segments.length - 1).join('.') ?? null;
10
+
segments.reverse();
11
+
const app = psl.parse(segments.join('.'))?.domain ?? null;
12
+
return { app, group };
13
+
}
14
+
15
+
function getAppDefs(source) {
16
+
const { app } = getBits(source);
17
+
const appPrefix = source.slice(0, app.length);
18
+
const appSource = source.slice(app.length + 1);
19
+
return [appSource, defs[appPrefix]];
20
+
}
21
+
22
+
const uriBits = async uri => {
23
+
const bits = uri.slice('at://'.length).split('/');
24
+
// TODO: identifier might be a handle
25
+
// TODO: rest might contain stuff after the rkey
26
+
const [did, nsid, rkey] = [bits[0], bits[1], bits.slice(2)];
27
+
return [did, nsid, rkey.join('/') || null];
28
+
};
29
+
30
+
export async function getLink(source, source_record, subject) {
31
+
// TODO: pass in preferred client
32
+
const [appSource, appDefs] = getAppDefs(source);
33
+
const appLinks = appDefs?.clients?.[0]?.direct_links;
34
+
const linkType = subject.startsWith('did:') ? 'did' : 'at_uri';
35
+
const linkTemplate = appLinks?.[`${linkType}:${appSource}`];
36
+
if (!linkTemplate) return null;
37
+
38
+
let link = linkTemplate;
39
+
40
+
// 1. sync subs
41
+
const [sourceDid, sourceNsid, sourceRkey] = await uriBits(source_record);
42
+
link = link
43
+
.replaceAll('{source_record.did}', sourceDid)
44
+
.replaceAll('{source_record.collection}', sourceNsid)
45
+
.replaceAll('{source_record.rkey}', sourceRkey);
46
+
if (linkTemplate === 'did') {
47
+
link = link.replaceAll('{subject.did}', subject);
48
+
} else {
49
+
const [subjectDid, subjectNsid, subjectRkey] = await uriBits(subject);
50
+
link = link
51
+
.replaceAll('{subject.did}', subjectDid)
52
+
.replaceAll('{subject.collection}', subjectNsid)
53
+
.replaceAll('{subject.rkey}', subjectRkey);
54
+
}
55
+
56
+
// 2. async lookups
57
+
58
+
// do we need to fetch anything from the link subject record?
59
+
if (linkType === 'at_uri') {
60
+
const subjectMatches = [...link.matchAll(/(\{@subject:(?<path>[^\}]+)\})/g)];
61
+
if (subjectMatches.length > 0) {
62
+
const subjectRecord = await getAtUri(subject);
63
+
64
+
// do the actual replacements
65
+
for (const match of subjectMatches) {
66
+
// TODO: JSONPath won't actually cut it once we get $type in
67
+
const sub = JSONPath({
68
+
path: `$.${match.groups.path}`,
69
+
json: subjectRecord,
70
+
})[0]; // TODO: array result?
71
+
72
+
link = link.replaceAll(match[0], sub);
73
+
}
74
+
}
75
+
}
76
+
77
+
// 2.b TODO: source record lookups if needed
78
+
return link;
79
+
}
80
+
81
+
export async function getContext(source, source_record, subject) {
82
+
const [appSource, appDefs] = getAppDefs(source);
83
+
const contexts = appDefs?.known_sources?.[appSource]?.context ?? [];
84
+
const linkType = subject.startsWith('did:') ? 'did' : 'at_uri';
85
+
86
+
let loaded = [];
87
+
for (const ctx of contexts) {
88
+
const [o, ...pathstuff] = ctx.split(':');
89
+
if (o !== '@subject') {
90
+
throw new Error('only @subject is implemented for context loading so far');
91
+
}
92
+
if (linkType !== 'at_uri') {
93
+
throw new Error('only at_uris can be used for @subject loading so far');
94
+
}
95
+
const path = pathstuff.join(':');
96
+
const subjectRecord = await getAtUri(subject);
97
+
// using json path is temporary -- need recordpath convention defined
98
+
const found = JSONPath({
99
+
path,
100
+
json: subjectRecord,
101
+
});
102
+
loaded = loaded.concat(found); // TODO: think about array handling
103
+
}
104
+
105
+
return loaded;
106
+
}
+271
lexicons/defs.js
+271
lexicons/defs.js
···
1
+
export default {
2
+
'blue.microcosm': {
3
+
name: 'microcosm',
4
+
clients: [
5
+
{
6
+
app_name: 'Spacedust notifications demo',
7
+
canonical: true,
8
+
main: 'https://notifications.microcosm.blue',
9
+
icon: '/icons/microcosm.png',
10
+
},
11
+
],
12
+
known_sources: {
13
+
'test.notification:hello': 'Hello spacedust!',
14
+
},
15
+
},
16
+
'app.bsky': {
17
+
name: 'Bluesky',
18
+
profile: {
19
+
display_name: 'app.bsky.actor.profile:displayName',
20
+
avatar: 'app.bsky.actor.profile:avatar',
21
+
},
22
+
clients: [
23
+
{
24
+
app_name: 'Bluesky Social',
25
+
canonical: true,
26
+
main: 'https://bsky.app',
27
+
icon: '/icons/app.bsky.png',
28
+
notifications: 'https://bsky.app/notifications',
29
+
direct_links: {
30
+
'at_uri:feed.like:subject.uri': 'https://bsky.app/profile/{subject.did}/post/{subject.rkey}',
31
+
'at_uri:feed.post:reply.parent.uri': 'https://bsky.app/profile/{source_record.did}/post/{source_record.rkey}',
32
+
'at_uri:feed.post:reply.root.uri': 'https://bsky.app/profile/{source_record.did}/post/{source_record.rkey}',
33
+
'at_uri:feed.post:embed.record.uri': 'https://bsky.app/profile/{source_record.did}/post/{source_record.rkey}',
34
+
'at_uri:feed.post:embed.record.record.uri': 'https://bsky.app/profile/{source_record.did}/post/{source_record.rkey}',
35
+
'did:graph.follow:subject': 'https://bsky.app/profile/{source_record.did}',
36
+
'did:feed.post:facets[app.bsky.richtext.facet].features[app.bsky.richtext.facet#mention].did': 'https://bsky.app/profile/{source_record.did}/post/{source_record.rkey}',
37
+
},
38
+
},
39
+
{
40
+
app_name: 'Deer Social',
41
+
main: 'https://deer.social',
42
+
notifications: 'https://deer.social/notifications',
43
+
direct_links: {
44
+
'at_uri:feed.post': 'https://deer.social/profile/{did}/post/{rkey}',
45
+
'did': 'https://deer.social/profile/{did}',
46
+
},
47
+
},
48
+
],
49
+
known_sources: {
50
+
'graph.follow:subject': {
51
+
name: 'Follow',
52
+
},
53
+
'graph.verification:subject': {
54
+
name: 'Verification',
55
+
},
56
+
'feed.like:subject.uri': {
57
+
name: 'Like',
58
+
context: ['@subject:text'],
59
+
},
60
+
'feed.like:via.uri': {
61
+
name: 'Repost like',
62
+
},
63
+
'feed.post:reply.parent.uri': {
64
+
name: 'Reply',
65
+
},
66
+
'feed.post:reply.root.uri': {
67
+
name: 'Reply in thread',
68
+
},
69
+
'feed.post:embed.record.uri': {
70
+
name: 'Quote',
71
+
},
72
+
'feed.post:embed.record.record.uri': {
73
+
name: 'Quote', // with media
74
+
},
75
+
'feed.post:facets[app.bsky.richtext.facet].features[app.bsky.richtext.facet#mention].did': {
76
+
name: 'Mention',
77
+
},
78
+
'feed.repost:subject.uri': {
79
+
name: 'Repost',
80
+
},
81
+
'feed.repost:via.uri': {
82
+
name: 'Repost repost',
83
+
},
84
+
},
85
+
torment_sources: {
86
+
'graph.block:subject': null,
87
+
'graph.listitem:subject': null, // we are never ever building listifications
88
+
'graph.listblock:subject': null, // "subscribed to your blocklist?" idk
89
+
'feed.threadgate:hiddenReplies[]': null,
90
+
'feed.postgate:detachedEmbeddingUris[]': null,
91
+
},
92
+
},
93
+
'pub.leaflet': {
94
+
name: 'Leaflet',
95
+
clients: [
96
+
{
97
+
app_name: 'leaflet.pub',
98
+
canonical: true,
99
+
icon: '/icons/pub.leaflet.jpg',
100
+
main: 'https://leaflet.pub/home',
101
+
direct_links: {
102
+
'at_uri:graph.subscription:publication': 'https://leaflet.pub/lish/{did}/{rkey}/dashboard',
103
+
},
104
+
}
105
+
],
106
+
known_sources: {
107
+
'graph.subscription:publication': {
108
+
name: 'Subscription',
109
+
},
110
+
},
111
+
},
112
+
'sh.tangled': {
113
+
name: 'Tangled',
114
+
clients: [
115
+
{
116
+
app_name: 'Tangled',
117
+
canonical: true,
118
+
icon: '/icons/sh.tangled.jpg',
119
+
main: 'https://tangled.sh',
120
+
direct_links: {
121
+
'at_uri:feed.star:subject': 'https://tangled.sh/{subject.did}/{@subject:name}',
122
+
'did:graph.follow:subject': 'https://tangled.sh/{source_record.did}',
123
+
},
124
+
}
125
+
],
126
+
known_sources: {
127
+
'feed.star:subject': {
128
+
name: 'Star',
129
+
context: ['@subject:name'],
130
+
},
131
+
'feed.reaction:subject': {
132
+
name: 'Reaction',
133
+
},
134
+
'graph.follow:subject': {
135
+
name: 'Follow',
136
+
},
137
+
'actor.profile:pinnedRepositories[]': {
138
+
name: 'Pinned repo',
139
+
},
140
+
'repo.issue.comment:issue': {
141
+
name: 'Issue comment',
142
+
},
143
+
'repo.issue.comment:owner': {
144
+
name: 'Issue comment',
145
+
},
146
+
'repo.issue.comment:repo': {
147
+
name: 'Issue comment',
148
+
},
149
+
'repo.pull:targetRepo': {
150
+
name: 'Pull',
151
+
},
152
+
'repo.pull.comment:owner': {
153
+
name: 'Pull comment',
154
+
},
155
+
'repo.pull.comment:pull': {
156
+
name: 'Pull comment',
157
+
},
158
+
'repo.pull.comment:repo': {
159
+
name: 'Pull comment',
160
+
},
161
+
'knot.member:subject': {
162
+
name: 'Knot member',
163
+
},
164
+
'spindle.member:subject': {
165
+
name: 'Spindle member',
166
+
},
167
+
},
168
+
},
169
+
'com.shinolabs': { // TODO: this app isn't exactly tld+1
170
+
name: 'Pinksea',
171
+
clients: [
172
+
{
173
+
app_name: 'Pinksea',
174
+
canonical: true,
175
+
icon: '/icons/com.shinolabs.jpg',
176
+
main: 'https://pinksea.art',
177
+
},
178
+
],
179
+
known_sources: {
180
+
'pinksea.oekaki:inResponseTo.uri': {
181
+
name: 'Response',
182
+
},
183
+
},
184
+
},
185
+
'place.stream': {
186
+
name: 'Streamplace',
187
+
clients: [
188
+
{
189
+
app_name: 'Streamplace',
190
+
canonical: true,
191
+
icon: '/icons/place.stream.png',
192
+
main: 'https://stream.place',
193
+
},
194
+
],
195
+
known_sources: {
196
+
'chat.message:streamer': {
197
+
name: 'Message',
198
+
},
199
+
'key:signingKey': {
200
+
name: 'Signing key',
201
+
},
202
+
},
203
+
},
204
+
'so.sprk': {
205
+
name: 'Spark',
206
+
clients: [
207
+
{
208
+
app_name: 'Spark',
209
+
canonical: true,
210
+
icon: '/icons/so.sprk.png',
211
+
main: 'https://spark.so',
212
+
},
213
+
],
214
+
known_sources: {
215
+
'feed.like:subject.uri': {
216
+
name: 'Like',
217
+
},
218
+
// it's not actually clear to me if *all* the bsky sources were copied for sprk posts or not
219
+
'feed.post:reply.parent.uri': {
220
+
name: 'Reply',
221
+
},
222
+
'feed.post:reply.root.uri': {
223
+
name: 'Reply in thread',
224
+
},
225
+
'feed.post:embed.record.uri': {
226
+
name: 'Quote',
227
+
},
228
+
'feed.post:embed.record.record.uri': {
229
+
name: 'Quote', // with media
230
+
},
231
+
'feed.post:facets[app.bsky.richtext.facet].features[app.bsky.richtext.facet#mention].did': {
232
+
name: 'Mention',
233
+
},
234
+
},
235
+
},
236
+
'events.smokesignal': {
237
+
name: 'Smoke Signal',
238
+
clients: [
239
+
{
240
+
app_name: 'Smoke Signal',
241
+
canonical: true,
242
+
icon: '/icons/events.smokesignal.png',
243
+
main: 'https://smokesignal.events',
244
+
},
245
+
],
246
+
known_sources: {
247
+
'calendar.rsvp:subject.uri': {
248
+
name: 'RSVP',
249
+
},
250
+
},
251
+
},
252
+
'app.popsky': {
253
+
name: 'Popsky',
254
+
clients: [
255
+
{
256
+
app_name: 'Popsky',
257
+
canonical: true,
258
+
icon: '/icons/app.popsky.png',
259
+
main: 'https://popsky.social',
260
+
},
261
+
],
262
+
known_sources: {
263
+
'like:subjectUri': {
264
+
name: 'Like',
265
+
},
266
+
'comment:subjectUri': {
267
+
name: 'Comment',
268
+
},
269
+
},
270
+
},
271
+
};
+2
-191
lexicons/index.js
+2
-191
lexicons/index.js
···
1
-
export default {
2
-
'blue.microcosm': {
3
-
name: 'microcosm',
4
-
clients: [
5
-
{
6
-
app_name: 'Spacedust notifications demo',
7
-
canonical: true,
8
-
main: 'https://notifications.microcosm.blue',
9
-
icon: '/icons/microcosm.png',
10
-
},
11
-
],
12
-
known_sources: {
13
-
'test.notification:hello': 'Hello spacedust!',
14
-
},
15
-
},
16
-
'app.bsky': {
17
-
name: 'Bluesky',
18
-
profile: {
19
-
display_name: 'app.bsky.actor.profile:displayName',
20
-
avatar: 'app.bsky.actor.profile:avatar',
21
-
},
22
-
clients: [
23
-
{
24
-
app_name: 'Bluesky Social',
25
-
canonical: true,
26
-
main: 'https://bsky.app',
27
-
icon: '/icons/app.bsky.png',
28
-
notifications: 'https://bsky.app/notifications',
29
-
direct_links: {
30
-
'at_uri:feed.like:subject.uri': 'https://bsky.app/profile/{subject.did}/post/{subject.rkey}',
31
-
'at_uri:feed.post:reply.parent.uri': 'https://bsky.app/profile/{source_record.did}/post/{source_record.rkey}',
32
-
'at_uri:feed.post:reply.root.uri': 'https://bsky.app/profile/{source_record.did}/post/{source_record.rkey}',
33
-
'at_uri:feed.post:embed.record.uri': 'https://bsky.app/profile/{source_record.did}/post/{source_record.rkey}',
34
-
'at_uri:feed.post:embed.record.record.uri': 'https://bsky.app/profile/{source_record.did}/post/{source_record.rkey}',
35
-
'did:graph.follow:subject': 'https://bsky.app/profile/{source_record.did}',
36
-
'did:feed.post:facets[app.bsky.richtext.facet].features[app.bsky.richtext.facet#mention].did': 'https://bsky.app/profile/{source_record.did}/post/{source_record.rkey}',
37
-
},
38
-
},
39
-
{
40
-
app_name: 'Deer Social',
41
-
main: 'https://deer.social',
42
-
notifications: 'https://deer.social/notifications',
43
-
direct_links: {
44
-
'at_uri:feed.post': 'https://deer.social/profile/{did}/post/{rkey}',
45
-
'did': 'https://deer.social/profile/{did}',
46
-
},
47
-
},
48
-
],
49
-
known_sources: {
50
-
'graph.follow:subject': 'Follow',
51
-
'graph.verification:subject': 'Verification',
52
-
'feed.like:subject.uri': 'Like',
53
-
'feed.like:via.uri': 'Repost like',
54
-
'feed.post:reply.parent.uri': 'Reply',
55
-
'feed.post:reply.root.uri': 'Reply in thread',
56
-
'feed.post:embed.record.uri': 'Quote',
57
-
'feed.post:embed.record.record.uri': 'Quote', // with media
58
-
'feed.post:facets[app.bsky.richtext.facet].features[app.bsky.richtext.facet#mention].did': 'Mention',
59
-
'feed.repost:subject.uri': 'Repost',
60
-
'feed.repost:via.uri': 'Repost repost',
61
-
},
62
-
torment_sources: {
63
-
'graph.block:subject': null,
64
-
'graph.listitem:subject': null, // we are never ever building listifications
65
-
'graph.listblock:subject': null, // "subscribed to your blocklist?" idk
66
-
'feed.threadgate:hiddenReplies[]': null,
67
-
'feed.postgate:detachedEmbeddingUris[]': null,
68
-
},
69
-
},
70
-
'pub.leaflet': {
71
-
name: 'Leaflet',
72
-
clients: [
73
-
{
74
-
app_name: 'leaflet.pub',
75
-
canonical: true,
76
-
icon: '/icons/pub.leaflet.jpg',
77
-
main: 'https://leaflet.pub/home',
78
-
direct_links: {
79
-
'at_uri:graph.subscription:publication': 'https://leaflet.pub/lish/{did}/{rkey}/dashboard',
80
-
},
81
-
}
82
-
],
83
-
known_sources: {
84
-
'graph.subscription:publication': 'Subscription',
85
-
},
86
-
},
87
-
'sh.tangled': {
88
-
name: 'Tangled',
89
-
clients: [
90
-
{
91
-
app_name: 'Tangled',
92
-
canonical: true,
93
-
icon: '/icons/sh.tangled.jpg',
94
-
main: 'https://tangled.sh',
95
-
}
96
-
],
97
-
known_sources: {
98
-
'feed.star:subject': 'Star',
99
-
'feed.reaction:subject': 'Reaction',
100
-
'graph.follow:subject': 'Follow',
101
-
'actor.profile:pinnedRepositories[]': 'Pinned repo',
102
-
'repo.issue.comment:issue': 'Issue comment',
103
-
'repo.issue.comment:owner': 'Issue comment',
104
-
'repo.issue.comment:repo': 'Issue comment',
105
-
'repo.pull:targetRepo': 'Pull',
106
-
'repo.pull.comment:owner': 'Pull comment',
107
-
'repo.pull.comment:pull': 'Pull comment',
108
-
'repo.pull.comment:repo': 'Pull comment',
109
-
'knot.member:subject': 'Knot member',
110
-
'spindle.member:subject': 'Spindle member',
111
-
},
112
-
},
113
-
'com.shinolabs': { // TODO: this app isn't exactly tld+1
114
-
name: 'Pinksea',
115
-
clients: [
116
-
{
117
-
app_name: 'Pinksea',
118
-
canonical: true,
119
-
icon: '/icons/com.shinolabs.jpg',
120
-
main: 'https://pinksea.art',
121
-
},
122
-
],
123
-
known_sources: {
124
-
'pinksea.oekaki:inResponseTo.uri': 'Response',
125
-
},
126
-
},
127
-
'place.stream': {
128
-
name: 'Streamplace',
129
-
clients: [
130
-
{
131
-
app_name: 'Streamplace',
132
-
canonical: true,
133
-
icon: '/icons/place.stream.png',
134
-
main: 'https://stream.place',
135
-
},
136
-
],
137
-
known_sources: {
138
-
'chat.message:streamer': 'Message',
139
-
'key:signingKey': 'Signing key',
140
-
},
141
-
},
142
-
'so.sprk': {
143
-
name: 'Spark',
144
-
clients: [
145
-
{
146
-
app_name: 'Spark',
147
-
canonical: true,
148
-
icon: '/icons/so.sprk.png',
149
-
main: 'https://spark.so',
150
-
},
151
-
],
152
-
known_sources: {
153
-
'feed.like:subject.uri': 'Like',
154
-
// it's not actually clear to me if *all* the bsky sources were copied for sprk posts or not
155
-
'feed.post:reply.parent.uri': 'Reply',
156
-
'feed.post:reply.root.uri': 'Reply in thread',
157
-
'feed.post:embed.record.uri': 'Quote',
158
-
'feed.post:embed.record.record.uri': 'Quote', // with media
159
-
'feed.post:facets[app.bsky.richtext.facet].features[app.bsky.richtext.facet#mention].did': 'Mention',
160
-
},
161
-
},
162
-
'events.smokesignal': {
163
-
name: 'Smoke Signal',
164
-
clients: [
165
-
{
166
-
app_name: 'Smoke Signal',
167
-
canonical: true,
168
-
icon: '/icons/events.smokesignal.png',
169
-
main: 'https://smokesignal.events',
170
-
},
171
-
],
172
-
known_sources: {
173
-
'calendar.rsvp:subject.uri': 'RSVP',
174
-
},
175
-
},
176
-
'app.popsky': {
177
-
name: 'Popsky',
178
-
clients: [
179
-
{
180
-
app_name: 'Popsky',
181
-
canonical: true,
182
-
icon: '/icons/app.popsky.png',
183
-
main: 'https://popsky.social',
184
-
},
185
-
],
186
-
known_sources: {
187
-
'like:subjectUri': 'Like',
188
-
'comment:subjectUri': 'Comment',
189
-
},
190
-
},
191
-
};
1
+
export { getBits, getLink, getContext } from './bits.js';
2
+
export { default } from './defs.js';
+157
lexicons/package-lock.json
+157
lexicons/package-lock.json
···
1
+
{
2
+
"name": "lexicons",
3
+
"version": "0.0.1",
4
+
"lockfileVersion": 3,
5
+
"requires": true,
6
+
"packages": {
7
+
"": {
8
+
"name": "lexicons",
9
+
"version": "0.0.1",
10
+
"dependencies": {
11
+
"@atcute/client": "^4.0.3",
12
+
"@atcute/identity-resolver": "^1.1.3",
13
+
"jsonpath-plus": "^10.3.0",
14
+
"psl": "^1.15.0"
15
+
}
16
+
},
17
+
"node_modules/@atcute/client": {
18
+
"version": "4.0.3",
19
+
"resolved": "https://registry.npmjs.org/@atcute/client/-/client-4.0.3.tgz",
20
+
"integrity": "sha512-RIOZWFVLca/HiPAAUDqQPOdOreCxTbL5cb+WUf5yqQOKIu5yEAP3eksinmlLmgIrlr5qVOE7brazUUzaskFCfw==",
21
+
"license": "MIT",
22
+
"dependencies": {
23
+
"@atcute/identity": "^1.0.2",
24
+
"@atcute/lexicons": "^1.0.3"
25
+
}
26
+
},
27
+
"node_modules/@atcute/identity": {
28
+
"version": "1.0.3",
29
+
"resolved": "https://registry.npmjs.org/@atcute/identity/-/identity-1.0.3.tgz",
30
+
"integrity": "sha512-mNMxbKHFGys03A8JXKk0KfMBzdd0vrYMzZZWjpw1nYTs0+ea6bo5S1hwqVUZxHdo1gFHSe/t63jxQIF4yL9aKw==",
31
+
"license": "0BSD",
32
+
"dependencies": {
33
+
"@atcute/lexicons": "^1.0.4",
34
+
"@badrap/valita": "^0.4.5"
35
+
}
36
+
},
37
+
"node_modules/@atcute/identity-resolver": {
38
+
"version": "1.1.3",
39
+
"resolved": "https://registry.npmjs.org/@atcute/identity-resolver/-/identity-resolver-1.1.3.tgz",
40
+
"integrity": "sha512-KZgGgg99CWaV7Df3+h3X/WMrDzTPQVfsaoIVbTNLx2B56BvCL2EmaxPSVw/7BFUJMZHlVU4rtoEB4lyvNyMswA==",
41
+
"license": "MIT",
42
+
"dependencies": {
43
+
"@atcute/lexicons": "^1.0.4",
44
+
"@atcute/util-fetch": "^1.0.1",
45
+
"@badrap/valita": "^0.4.4"
46
+
},
47
+
"peerDependencies": {
48
+
"@atcute/identity": "^1.0.0"
49
+
}
50
+
},
51
+
"node_modules/@atcute/lexicons": {
52
+
"version": "1.1.0",
53
+
"resolved": "https://registry.npmjs.org/@atcute/lexicons/-/lexicons-1.1.0.tgz",
54
+
"integrity": "sha512-LFqwnria78xLYb62Ri/+WwQpUTgZp2DuyolNGIIOV1dpiKhFFFh//nscHMA6IExFLQRqWDs3tTjy7zv0h3sf1Q==",
55
+
"license": "0BSD",
56
+
"dependencies": {
57
+
"esm-env": "^1.2.2"
58
+
}
59
+
},
60
+
"node_modules/@atcute/util-fetch": {
61
+
"version": "1.0.1",
62
+
"resolved": "https://registry.npmjs.org/@atcute/util-fetch/-/util-fetch-1.0.1.tgz",
63
+
"integrity": "sha512-Clc0E/5ufyGBVfYBUwWNlHONlZCoblSr4Ho50l1LhmRPGB1Wu/AQ9Sz+rsBg7fdaW/auve8ulmwhRhnX2cGRow==",
64
+
"license": "MIT",
65
+
"dependencies": {
66
+
"@badrap/valita": "^0.4.2"
67
+
}
68
+
},
69
+
"node_modules/@badrap/valita": {
70
+
"version": "0.4.5",
71
+
"resolved": "https://registry.npmjs.org/@badrap/valita/-/valita-0.4.5.tgz",
72
+
"integrity": "sha512-4QwGbuhh/JesHRQj79mO/l37PvJj4l/tlAu7+S1n4h47qwaNpZ0WDvIwUGLYUsdi9uQ5UPpiG9wb1Wm3XUFBUQ==",
73
+
"license": "MIT",
74
+
"engines": {
75
+
"node": ">= 18"
76
+
}
77
+
},
78
+
"node_modules/@jsep-plugin/assignment": {
79
+
"version": "1.3.0",
80
+
"resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz",
81
+
"integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==",
82
+
"license": "MIT",
83
+
"engines": {
84
+
"node": ">= 10.16.0"
85
+
},
86
+
"peerDependencies": {
87
+
"jsep": "^0.4.0||^1.0.0"
88
+
}
89
+
},
90
+
"node_modules/@jsep-plugin/regex": {
91
+
"version": "1.0.4",
92
+
"resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz",
93
+
"integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==",
94
+
"license": "MIT",
95
+
"engines": {
96
+
"node": ">= 10.16.0"
97
+
},
98
+
"peerDependencies": {
99
+
"jsep": "^0.4.0||^1.0.0"
100
+
}
101
+
},
102
+
"node_modules/esm-env": {
103
+
"version": "1.2.2",
104
+
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
105
+
"integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
106
+
"license": "MIT"
107
+
},
108
+
"node_modules/jsep": {
109
+
"version": "1.4.0",
110
+
"resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz",
111
+
"integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==",
112
+
"license": "MIT",
113
+
"engines": {
114
+
"node": ">= 10.16.0"
115
+
}
116
+
},
117
+
"node_modules/jsonpath-plus": {
118
+
"version": "10.3.0",
119
+
"resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.3.0.tgz",
120
+
"integrity": "sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==",
121
+
"license": "MIT",
122
+
"dependencies": {
123
+
"@jsep-plugin/assignment": "^1.3.0",
124
+
"@jsep-plugin/regex": "^1.0.4",
125
+
"jsep": "^1.4.0"
126
+
},
127
+
"bin": {
128
+
"jsonpath": "bin/jsonpath-cli.js",
129
+
"jsonpath-plus": "bin/jsonpath-cli.js"
130
+
},
131
+
"engines": {
132
+
"node": ">=18.0.0"
133
+
}
134
+
},
135
+
"node_modules/psl": {
136
+
"version": "1.15.0",
137
+
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
138
+
"integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==",
139
+
"license": "MIT",
140
+
"dependencies": {
141
+
"punycode": "^2.3.1"
142
+
},
143
+
"funding": {
144
+
"url": "https://github.com/sponsors/lupomontero"
145
+
}
146
+
},
147
+
"node_modules/punycode": {
148
+
"version": "2.3.1",
149
+
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
150
+
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
151
+
"license": "MIT",
152
+
"engines": {
153
+
"node": ">=6"
154
+
}
155
+
}
156
+
}
157
+
}
+7
-1
lexicons/package.json
+7
-1
lexicons/package.json
+24
live-embed/.gitignore
+24
live-embed/.gitignore
···
1
+
# Logs
2
+
logs
3
+
*.log
4
+
npm-debug.log*
5
+
yarn-debug.log*
6
+
yarn-error.log*
7
+
pnpm-debug.log*
8
+
lerna-debug.log*
9
+
10
+
node_modules
11
+
dist
12
+
dist-ssr
13
+
*.local
14
+
15
+
# Editor directories and files
16
+
.vscode/*
17
+
!.vscode/extensions.json
18
+
.idea
19
+
.DS_Store
20
+
*.suo
21
+
*.ntvs*
22
+
*.njsproj
23
+
*.sln
24
+
*.sw?
+69
live-embed/README.md
+69
live-embed/README.md
···
1
+
# React + TypeScript + Vite
2
+
3
+
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4
+
5
+
Currently, two official plugins are available:
6
+
7
+
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
8
+
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9
+
10
+
## Expanding the ESLint configuration
11
+
12
+
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
13
+
14
+
```js
15
+
export default tseslint.config([
16
+
globalIgnores(['dist']),
17
+
{
18
+
files: ['**/*.{ts,tsx}'],
19
+
extends: [
20
+
// Other configs...
21
+
22
+
// Remove tseslint.configs.recommended and replace with this
23
+
...tseslint.configs.recommendedTypeChecked,
24
+
// Alternatively, use this for stricter rules
25
+
...tseslint.configs.strictTypeChecked,
26
+
// Optionally, add this for stylistic rules
27
+
...tseslint.configs.stylisticTypeChecked,
28
+
29
+
// Other configs...
30
+
],
31
+
languageOptions: {
32
+
parserOptions: {
33
+
project: ['./tsconfig.node.json', './tsconfig.app.json'],
34
+
tsconfigRootDir: import.meta.dirname,
35
+
},
36
+
// other options...
37
+
},
38
+
},
39
+
])
40
+
```
41
+
42
+
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
43
+
44
+
```js
45
+
// eslint.config.js
46
+
import reactX from 'eslint-plugin-react-x'
47
+
import reactDom from 'eslint-plugin-react-dom'
48
+
49
+
export default tseslint.config([
50
+
globalIgnores(['dist']),
51
+
{
52
+
files: ['**/*.{ts,tsx}'],
53
+
extends: [
54
+
// Other configs...
55
+
// Enable lint rules for React
56
+
reactX.configs['recommended-typescript'],
57
+
// Enable lint rules for React DOM
58
+
reactDom.configs.recommended,
59
+
],
60
+
languageOptions: {
61
+
parserOptions: {
62
+
project: ['./tsconfig.node.json', './tsconfig.app.json'],
63
+
tsconfigRootDir: import.meta.dirname,
64
+
},
65
+
// other options...
66
+
},
67
+
},
68
+
])
69
+
```
+23
live-embed/eslint.config.js
+23
live-embed/eslint.config.js
···
1
+
import js from '@eslint/js'
2
+
import globals from 'globals'
3
+
import reactHooks from 'eslint-plugin-react-hooks'
4
+
import reactRefresh from 'eslint-plugin-react-refresh'
5
+
import tseslint from 'typescript-eslint'
6
+
import { globalIgnores } from 'eslint/config'
7
+
8
+
export default tseslint.config([
9
+
globalIgnores(['dist']),
10
+
{
11
+
files: ['**/*.{ts,tsx}'],
12
+
extends: [
13
+
js.configs.recommended,
14
+
tseslint.configs.recommended,
15
+
reactHooks.configs['recommended-latest'],
16
+
reactRefresh.configs.vite,
17
+
],
18
+
languageOptions: {
19
+
ecmaVersion: 2020,
20
+
globals: globals.browser,
21
+
},
22
+
},
23
+
])
+12
live-embed/index.html
+12
live-embed/index.html
···
1
+
<!doctype html>
2
+
<html lang="en">
3
+
<head>
4
+
<meta charset="UTF-8" />
5
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+
<title>zero bluesky live-updating post rendering</title>
7
+
</head>
8
+
<body>
9
+
<div id="root"></div>
10
+
<script type="module" src="/src/main.tsx"></script>
11
+
</body>
12
+
</html>
+3444
live-embed/package-lock.json
+3444
live-embed/package-lock.json
···
1
+
{
2
+
"name": "live-embed",
3
+
"version": "0.0.0",
4
+
"lockfileVersion": 3,
5
+
"requires": true,
6
+
"packages": {
7
+
"": {
8
+
"name": "live-embed",
9
+
"version": "0.0.0",
10
+
"dependencies": {
11
+
"@atcute/client": "^4.0.3",
12
+
"@atcute/identity-resolver": "^1.1.3",
13
+
"react": "^19.1.0",
14
+
"react-dom": "^19.1.0"
15
+
},
16
+
"devDependencies": {
17
+
"@eslint/js": "^9.30.1",
18
+
"@types/react": "^19.1.8",
19
+
"@types/react-dom": "^19.1.6",
20
+
"@vitejs/plugin-react": "^4.6.0",
21
+
"eslint": "^9.30.1",
22
+
"eslint-plugin-react-hooks": "^5.2.0",
23
+
"eslint-plugin-react-refresh": "^0.4.20",
24
+
"globals": "^16.3.0",
25
+
"typescript": "~5.8.3",
26
+
"typescript-eslint": "^8.35.1",
27
+
"vite": "^7.0.4"
28
+
}
29
+
},
30
+
"node_modules/@ampproject/remapping": {
31
+
"version": "2.3.0",
32
+
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
33
+
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
34
+
"dev": true,
35
+
"license": "Apache-2.0",
36
+
"dependencies": {
37
+
"@jridgewell/gen-mapping": "^0.3.5",
38
+
"@jridgewell/trace-mapping": "^0.3.24"
39
+
},
40
+
"engines": {
41
+
"node": ">=6.0.0"
42
+
}
43
+
},
44
+
"node_modules/@atcute/client": {
45
+
"version": "4.0.3",
46
+
"resolved": "https://registry.npmjs.org/@atcute/client/-/client-4.0.3.tgz",
47
+
"integrity": "sha512-RIOZWFVLca/HiPAAUDqQPOdOreCxTbL5cb+WUf5yqQOKIu5yEAP3eksinmlLmgIrlr5qVOE7brazUUzaskFCfw==",
48
+
"license": "MIT",
49
+
"dependencies": {
50
+
"@atcute/identity": "^1.0.2",
51
+
"@atcute/lexicons": "^1.0.3"
52
+
}
53
+
},
54
+
"node_modules/@atcute/identity": {
55
+
"version": "1.0.3",
56
+
"resolved": "https://registry.npmjs.org/@atcute/identity/-/identity-1.0.3.tgz",
57
+
"integrity": "sha512-mNMxbKHFGys03A8JXKk0KfMBzdd0vrYMzZZWjpw1nYTs0+ea6bo5S1hwqVUZxHdo1gFHSe/t63jxQIF4yL9aKw==",
58
+
"license": "0BSD",
59
+
"dependencies": {
60
+
"@atcute/lexicons": "^1.0.4",
61
+
"@badrap/valita": "^0.4.5"
62
+
}
63
+
},
64
+
"node_modules/@atcute/identity-resolver": {
65
+
"version": "1.1.3",
66
+
"resolved": "https://registry.npmjs.org/@atcute/identity-resolver/-/identity-resolver-1.1.3.tgz",
67
+
"integrity": "sha512-KZgGgg99CWaV7Df3+h3X/WMrDzTPQVfsaoIVbTNLx2B56BvCL2EmaxPSVw/7BFUJMZHlVU4rtoEB4lyvNyMswA==",
68
+
"license": "MIT",
69
+
"dependencies": {
70
+
"@atcute/lexicons": "^1.0.4",
71
+
"@atcute/util-fetch": "^1.0.1",
72
+
"@badrap/valita": "^0.4.4"
73
+
},
74
+
"peerDependencies": {
75
+
"@atcute/identity": "^1.0.0"
76
+
}
77
+
},
78
+
"node_modules/@atcute/lexicons": {
79
+
"version": "1.1.0",
80
+
"resolved": "https://registry.npmjs.org/@atcute/lexicons/-/lexicons-1.1.0.tgz",
81
+
"integrity": "sha512-LFqwnria78xLYb62Ri/+WwQpUTgZp2DuyolNGIIOV1dpiKhFFFh//nscHMA6IExFLQRqWDs3tTjy7zv0h3sf1Q==",
82
+
"license": "0BSD",
83
+
"dependencies": {
84
+
"esm-env": "^1.2.2"
85
+
}
86
+
},
87
+
"node_modules/@atcute/util-fetch": {
88
+
"version": "1.0.1",
89
+
"resolved": "https://registry.npmjs.org/@atcute/util-fetch/-/util-fetch-1.0.1.tgz",
90
+
"integrity": "sha512-Clc0E/5ufyGBVfYBUwWNlHONlZCoblSr4Ho50l1LhmRPGB1Wu/AQ9Sz+rsBg7fdaW/auve8ulmwhRhnX2cGRow==",
91
+
"license": "MIT",
92
+
"dependencies": {
93
+
"@badrap/valita": "^0.4.2"
94
+
}
95
+
},
96
+
"node_modules/@babel/code-frame": {
97
+
"version": "7.27.1",
98
+
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
99
+
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
100
+
"dev": true,
101
+
"license": "MIT",
102
+
"dependencies": {
103
+
"@babel/helper-validator-identifier": "^7.27.1",
104
+
"js-tokens": "^4.0.0",
105
+
"picocolors": "^1.1.1"
106
+
},
107
+
"engines": {
108
+
"node": ">=6.9.0"
109
+
}
110
+
},
111
+
"node_modules/@babel/compat-data": {
112
+
"version": "7.28.0",
113
+
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz",
114
+
"integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==",
115
+
"dev": true,
116
+
"license": "MIT",
117
+
"engines": {
118
+
"node": ">=6.9.0"
119
+
}
120
+
},
121
+
"node_modules/@babel/core": {
122
+
"version": "7.28.0",
123
+
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz",
124
+
"integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==",
125
+
"dev": true,
126
+
"license": "MIT",
127
+
"dependencies": {
128
+
"@ampproject/remapping": "^2.2.0",
129
+
"@babel/code-frame": "^7.27.1",
130
+
"@babel/generator": "^7.28.0",
131
+
"@babel/helper-compilation-targets": "^7.27.2",
132
+
"@babel/helper-module-transforms": "^7.27.3",
133
+
"@babel/helpers": "^7.27.6",
134
+
"@babel/parser": "^7.28.0",
135
+
"@babel/template": "^7.27.2",
136
+
"@babel/traverse": "^7.28.0",
137
+
"@babel/types": "^7.28.0",
138
+
"convert-source-map": "^2.0.0",
139
+
"debug": "^4.1.0",
140
+
"gensync": "^1.0.0-beta.2",
141
+
"json5": "^2.2.3",
142
+
"semver": "^6.3.1"
143
+
},
144
+
"engines": {
145
+
"node": ">=6.9.0"
146
+
},
147
+
"funding": {
148
+
"type": "opencollective",
149
+
"url": "https://opencollective.com/babel"
150
+
}
151
+
},
152
+
"node_modules/@babel/generator": {
153
+
"version": "7.28.0",
154
+
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz",
155
+
"integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==",
156
+
"dev": true,
157
+
"license": "MIT",
158
+
"dependencies": {
159
+
"@babel/parser": "^7.28.0",
160
+
"@babel/types": "^7.28.0",
161
+
"@jridgewell/gen-mapping": "^0.3.12",
162
+
"@jridgewell/trace-mapping": "^0.3.28",
163
+
"jsesc": "^3.0.2"
164
+
},
165
+
"engines": {
166
+
"node": ">=6.9.0"
167
+
}
168
+
},
169
+
"node_modules/@babel/helper-compilation-targets": {
170
+
"version": "7.27.2",
171
+
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
172
+
"integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
173
+
"dev": true,
174
+
"license": "MIT",
175
+
"dependencies": {
176
+
"@babel/compat-data": "^7.27.2",
177
+
"@babel/helper-validator-option": "^7.27.1",
178
+
"browserslist": "^4.24.0",
179
+
"lru-cache": "^5.1.1",
180
+
"semver": "^6.3.1"
181
+
},
182
+
"engines": {
183
+
"node": ">=6.9.0"
184
+
}
185
+
},
186
+
"node_modules/@babel/helper-globals": {
187
+
"version": "7.28.0",
188
+
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
189
+
"integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
190
+
"dev": true,
191
+
"license": "MIT",
192
+
"engines": {
193
+
"node": ">=6.9.0"
194
+
}
195
+
},
196
+
"node_modules/@babel/helper-module-imports": {
197
+
"version": "7.27.1",
198
+
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
199
+
"integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
200
+
"dev": true,
201
+
"license": "MIT",
202
+
"dependencies": {
203
+
"@babel/traverse": "^7.27.1",
204
+
"@babel/types": "^7.27.1"
205
+
},
206
+
"engines": {
207
+
"node": ">=6.9.0"
208
+
}
209
+
},
210
+
"node_modules/@babel/helper-module-transforms": {
211
+
"version": "7.27.3",
212
+
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz",
213
+
"integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==",
214
+
"dev": true,
215
+
"license": "MIT",
216
+
"dependencies": {
217
+
"@babel/helper-module-imports": "^7.27.1",
218
+
"@babel/helper-validator-identifier": "^7.27.1",
219
+
"@babel/traverse": "^7.27.3"
220
+
},
221
+
"engines": {
222
+
"node": ">=6.9.0"
223
+
},
224
+
"peerDependencies": {
225
+
"@babel/core": "^7.0.0"
226
+
}
227
+
},
228
+
"node_modules/@babel/helper-plugin-utils": {
229
+
"version": "7.27.1",
230
+
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
231
+
"integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
232
+
"dev": true,
233
+
"license": "MIT",
234
+
"engines": {
235
+
"node": ">=6.9.0"
236
+
}
237
+
},
238
+
"node_modules/@babel/helper-string-parser": {
239
+
"version": "7.27.1",
240
+
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
241
+
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
242
+
"dev": true,
243
+
"license": "MIT",
244
+
"engines": {
245
+
"node": ">=6.9.0"
246
+
}
247
+
},
248
+
"node_modules/@babel/helper-validator-identifier": {
249
+
"version": "7.27.1",
250
+
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
251
+
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
252
+
"dev": true,
253
+
"license": "MIT",
254
+
"engines": {
255
+
"node": ">=6.9.0"
256
+
}
257
+
},
258
+
"node_modules/@babel/helper-validator-option": {
259
+
"version": "7.27.1",
260
+
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
261
+
"integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
262
+
"dev": true,
263
+
"license": "MIT",
264
+
"engines": {
265
+
"node": ">=6.9.0"
266
+
}
267
+
},
268
+
"node_modules/@babel/helpers": {
269
+
"version": "7.28.2",
270
+
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz",
271
+
"integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==",
272
+
"dev": true,
273
+
"license": "MIT",
274
+
"dependencies": {
275
+
"@babel/template": "^7.27.2",
276
+
"@babel/types": "^7.28.2"
277
+
},
278
+
"engines": {
279
+
"node": ">=6.9.0"
280
+
}
281
+
},
282
+
"node_modules/@babel/parser": {
283
+
"version": "7.28.0",
284
+
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
285
+
"integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
286
+
"dev": true,
287
+
"license": "MIT",
288
+
"dependencies": {
289
+
"@babel/types": "^7.28.0"
290
+
},
291
+
"bin": {
292
+
"parser": "bin/babel-parser.js"
293
+
},
294
+
"engines": {
295
+
"node": ">=6.0.0"
296
+
}
297
+
},
298
+
"node_modules/@babel/plugin-transform-react-jsx-self": {
299
+
"version": "7.27.1",
300
+
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
301
+
"integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
302
+
"dev": true,
303
+
"license": "MIT",
304
+
"dependencies": {
305
+
"@babel/helper-plugin-utils": "^7.27.1"
306
+
},
307
+
"engines": {
308
+
"node": ">=6.9.0"
309
+
},
310
+
"peerDependencies": {
311
+
"@babel/core": "^7.0.0-0"
312
+
}
313
+
},
314
+
"node_modules/@babel/plugin-transform-react-jsx-source": {
315
+
"version": "7.27.1",
316
+
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
317
+
"integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
318
+
"dev": true,
319
+
"license": "MIT",
320
+
"dependencies": {
321
+
"@babel/helper-plugin-utils": "^7.27.1"
322
+
},
323
+
"engines": {
324
+
"node": ">=6.9.0"
325
+
},
326
+
"peerDependencies": {
327
+
"@babel/core": "^7.0.0-0"
328
+
}
329
+
},
330
+
"node_modules/@babel/template": {
331
+
"version": "7.27.2",
332
+
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
333
+
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
334
+
"dev": true,
335
+
"license": "MIT",
336
+
"dependencies": {
337
+
"@babel/code-frame": "^7.27.1",
338
+
"@babel/parser": "^7.27.2",
339
+
"@babel/types": "^7.27.1"
340
+
},
341
+
"engines": {
342
+
"node": ">=6.9.0"
343
+
}
344
+
},
345
+
"node_modules/@babel/traverse": {
346
+
"version": "7.28.0",
347
+
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz",
348
+
"integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==",
349
+
"dev": true,
350
+
"license": "MIT",
351
+
"dependencies": {
352
+
"@babel/code-frame": "^7.27.1",
353
+
"@babel/generator": "^7.28.0",
354
+
"@babel/helper-globals": "^7.28.0",
355
+
"@babel/parser": "^7.28.0",
356
+
"@babel/template": "^7.27.2",
357
+
"@babel/types": "^7.28.0",
358
+
"debug": "^4.3.1"
359
+
},
360
+
"engines": {
361
+
"node": ">=6.9.0"
362
+
}
363
+
},
364
+
"node_modules/@babel/types": {
365
+
"version": "7.28.2",
366
+
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
367
+
"integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
368
+
"dev": true,
369
+
"license": "MIT",
370
+
"dependencies": {
371
+
"@babel/helper-string-parser": "^7.27.1",
372
+
"@babel/helper-validator-identifier": "^7.27.1"
373
+
},
374
+
"engines": {
375
+
"node": ">=6.9.0"
376
+
}
377
+
},
378
+
"node_modules/@badrap/valita": {
379
+
"version": "0.4.5",
380
+
"resolved": "https://registry.npmjs.org/@badrap/valita/-/valita-0.4.5.tgz",
381
+
"integrity": "sha512-4QwGbuhh/JesHRQj79mO/l37PvJj4l/tlAu7+S1n4h47qwaNpZ0WDvIwUGLYUsdi9uQ5UPpiG9wb1Wm3XUFBUQ==",
382
+
"license": "MIT",
383
+
"engines": {
384
+
"node": ">= 18"
385
+
}
386
+
},
387
+
"node_modules/@esbuild/aix-ppc64": {
388
+
"version": "0.25.8",
389
+
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz",
390
+
"integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==",
391
+
"cpu": [
392
+
"ppc64"
393
+
],
394
+
"dev": true,
395
+
"license": "MIT",
396
+
"optional": true,
397
+
"os": [
398
+
"aix"
399
+
],
400
+
"engines": {
401
+
"node": ">=18"
402
+
}
403
+
},
404
+
"node_modules/@esbuild/android-arm": {
405
+
"version": "0.25.8",
406
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz",
407
+
"integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==",
408
+
"cpu": [
409
+
"arm"
410
+
],
411
+
"dev": true,
412
+
"license": "MIT",
413
+
"optional": true,
414
+
"os": [
415
+
"android"
416
+
],
417
+
"engines": {
418
+
"node": ">=18"
419
+
}
420
+
},
421
+
"node_modules/@esbuild/android-arm64": {
422
+
"version": "0.25.8",
423
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz",
424
+
"integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==",
425
+
"cpu": [
426
+
"arm64"
427
+
],
428
+
"dev": true,
429
+
"license": "MIT",
430
+
"optional": true,
431
+
"os": [
432
+
"android"
433
+
],
434
+
"engines": {
435
+
"node": ">=18"
436
+
}
437
+
},
438
+
"node_modules/@esbuild/android-x64": {
439
+
"version": "0.25.8",
440
+
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz",
441
+
"integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==",
442
+
"cpu": [
443
+
"x64"
444
+
],
445
+
"dev": true,
446
+
"license": "MIT",
447
+
"optional": true,
448
+
"os": [
449
+
"android"
450
+
],
451
+
"engines": {
452
+
"node": ">=18"
453
+
}
454
+
},
455
+
"node_modules/@esbuild/darwin-arm64": {
456
+
"version": "0.25.8",
457
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz",
458
+
"integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==",
459
+
"cpu": [
460
+
"arm64"
461
+
],
462
+
"dev": true,
463
+
"license": "MIT",
464
+
"optional": true,
465
+
"os": [
466
+
"darwin"
467
+
],
468
+
"engines": {
469
+
"node": ">=18"
470
+
}
471
+
},
472
+
"node_modules/@esbuild/darwin-x64": {
473
+
"version": "0.25.8",
474
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz",
475
+
"integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==",
476
+
"cpu": [
477
+
"x64"
478
+
],
479
+
"dev": true,
480
+
"license": "MIT",
481
+
"optional": true,
482
+
"os": [
483
+
"darwin"
484
+
],
485
+
"engines": {
486
+
"node": ">=18"
487
+
}
488
+
},
489
+
"node_modules/@esbuild/freebsd-arm64": {
490
+
"version": "0.25.8",
491
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz",
492
+
"integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==",
493
+
"cpu": [
494
+
"arm64"
495
+
],
496
+
"dev": true,
497
+
"license": "MIT",
498
+
"optional": true,
499
+
"os": [
500
+
"freebsd"
501
+
],
502
+
"engines": {
503
+
"node": ">=18"
504
+
}
505
+
},
506
+
"node_modules/@esbuild/freebsd-x64": {
507
+
"version": "0.25.8",
508
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz",
509
+
"integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==",
510
+
"cpu": [
511
+
"x64"
512
+
],
513
+
"dev": true,
514
+
"license": "MIT",
515
+
"optional": true,
516
+
"os": [
517
+
"freebsd"
518
+
],
519
+
"engines": {
520
+
"node": ">=18"
521
+
}
522
+
},
523
+
"node_modules/@esbuild/linux-arm": {
524
+
"version": "0.25.8",
525
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz",
526
+
"integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==",
527
+
"cpu": [
528
+
"arm"
529
+
],
530
+
"dev": true,
531
+
"license": "MIT",
532
+
"optional": true,
533
+
"os": [
534
+
"linux"
535
+
],
536
+
"engines": {
537
+
"node": ">=18"
538
+
}
539
+
},
540
+
"node_modules/@esbuild/linux-arm64": {
541
+
"version": "0.25.8",
542
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz",
543
+
"integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==",
544
+
"cpu": [
545
+
"arm64"
546
+
],
547
+
"dev": true,
548
+
"license": "MIT",
549
+
"optional": true,
550
+
"os": [
551
+
"linux"
552
+
],
553
+
"engines": {
554
+
"node": ">=18"
555
+
}
556
+
},
557
+
"node_modules/@esbuild/linux-ia32": {
558
+
"version": "0.25.8",
559
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz",
560
+
"integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==",
561
+
"cpu": [
562
+
"ia32"
563
+
],
564
+
"dev": true,
565
+
"license": "MIT",
566
+
"optional": true,
567
+
"os": [
568
+
"linux"
569
+
],
570
+
"engines": {
571
+
"node": ">=18"
572
+
}
573
+
},
574
+
"node_modules/@esbuild/linux-loong64": {
575
+
"version": "0.25.8",
576
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz",
577
+
"integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==",
578
+
"cpu": [
579
+
"loong64"
580
+
],
581
+
"dev": true,
582
+
"license": "MIT",
583
+
"optional": true,
584
+
"os": [
585
+
"linux"
586
+
],
587
+
"engines": {
588
+
"node": ">=18"
589
+
}
590
+
},
591
+
"node_modules/@esbuild/linux-mips64el": {
592
+
"version": "0.25.8",
593
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz",
594
+
"integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==",
595
+
"cpu": [
596
+
"mips64el"
597
+
],
598
+
"dev": true,
599
+
"license": "MIT",
600
+
"optional": true,
601
+
"os": [
602
+
"linux"
603
+
],
604
+
"engines": {
605
+
"node": ">=18"
606
+
}
607
+
},
608
+
"node_modules/@esbuild/linux-ppc64": {
609
+
"version": "0.25.8",
610
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz",
611
+
"integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==",
612
+
"cpu": [
613
+
"ppc64"
614
+
],
615
+
"dev": true,
616
+
"license": "MIT",
617
+
"optional": true,
618
+
"os": [
619
+
"linux"
620
+
],
621
+
"engines": {
622
+
"node": ">=18"
623
+
}
624
+
},
625
+
"node_modules/@esbuild/linux-riscv64": {
626
+
"version": "0.25.8",
627
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz",
628
+
"integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==",
629
+
"cpu": [
630
+
"riscv64"
631
+
],
632
+
"dev": true,
633
+
"license": "MIT",
634
+
"optional": true,
635
+
"os": [
636
+
"linux"
637
+
],
638
+
"engines": {
639
+
"node": ">=18"
640
+
}
641
+
},
642
+
"node_modules/@esbuild/linux-s390x": {
643
+
"version": "0.25.8",
644
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz",
645
+
"integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==",
646
+
"cpu": [
647
+
"s390x"
648
+
],
649
+
"dev": true,
650
+
"license": "MIT",
651
+
"optional": true,
652
+
"os": [
653
+
"linux"
654
+
],
655
+
"engines": {
656
+
"node": ">=18"
657
+
}
658
+
},
659
+
"node_modules/@esbuild/linux-x64": {
660
+
"version": "0.25.8",
661
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz",
662
+
"integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==",
663
+
"cpu": [
664
+
"x64"
665
+
],
666
+
"dev": true,
667
+
"license": "MIT",
668
+
"optional": true,
669
+
"os": [
670
+
"linux"
671
+
],
672
+
"engines": {
673
+
"node": ">=18"
674
+
}
675
+
},
676
+
"node_modules/@esbuild/netbsd-arm64": {
677
+
"version": "0.25.8",
678
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz",
679
+
"integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==",
680
+
"cpu": [
681
+
"arm64"
682
+
],
683
+
"dev": true,
684
+
"license": "MIT",
685
+
"optional": true,
686
+
"os": [
687
+
"netbsd"
688
+
],
689
+
"engines": {
690
+
"node": ">=18"
691
+
}
692
+
},
693
+
"node_modules/@esbuild/netbsd-x64": {
694
+
"version": "0.25.8",
695
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz",
696
+
"integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==",
697
+
"cpu": [
698
+
"x64"
699
+
],
700
+
"dev": true,
701
+
"license": "MIT",
702
+
"optional": true,
703
+
"os": [
704
+
"netbsd"
705
+
],
706
+
"engines": {
707
+
"node": ">=18"
708
+
}
709
+
},
710
+
"node_modules/@esbuild/openbsd-arm64": {
711
+
"version": "0.25.8",
712
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz",
713
+
"integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==",
714
+
"cpu": [
715
+
"arm64"
716
+
],
717
+
"dev": true,
718
+
"license": "MIT",
719
+
"optional": true,
720
+
"os": [
721
+
"openbsd"
722
+
],
723
+
"engines": {
724
+
"node": ">=18"
725
+
}
726
+
},
727
+
"node_modules/@esbuild/openbsd-x64": {
728
+
"version": "0.25.8",
729
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz",
730
+
"integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==",
731
+
"cpu": [
732
+
"x64"
733
+
],
734
+
"dev": true,
735
+
"license": "MIT",
736
+
"optional": true,
737
+
"os": [
738
+
"openbsd"
739
+
],
740
+
"engines": {
741
+
"node": ">=18"
742
+
}
743
+
},
744
+
"node_modules/@esbuild/openharmony-arm64": {
745
+
"version": "0.25.8",
746
+
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz",
747
+
"integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==",
748
+
"cpu": [
749
+
"arm64"
750
+
],
751
+
"dev": true,
752
+
"license": "MIT",
753
+
"optional": true,
754
+
"os": [
755
+
"openharmony"
756
+
],
757
+
"engines": {
758
+
"node": ">=18"
759
+
}
760
+
},
761
+
"node_modules/@esbuild/sunos-x64": {
762
+
"version": "0.25.8",
763
+
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz",
764
+
"integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==",
765
+
"cpu": [
766
+
"x64"
767
+
],
768
+
"dev": true,
769
+
"license": "MIT",
770
+
"optional": true,
771
+
"os": [
772
+
"sunos"
773
+
],
774
+
"engines": {
775
+
"node": ">=18"
776
+
}
777
+
},
778
+
"node_modules/@esbuild/win32-arm64": {
779
+
"version": "0.25.8",
780
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz",
781
+
"integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==",
782
+
"cpu": [
783
+
"arm64"
784
+
],
785
+
"dev": true,
786
+
"license": "MIT",
787
+
"optional": true,
788
+
"os": [
789
+
"win32"
790
+
],
791
+
"engines": {
792
+
"node": ">=18"
793
+
}
794
+
},
795
+
"node_modules/@esbuild/win32-ia32": {
796
+
"version": "0.25.8",
797
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz",
798
+
"integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==",
799
+
"cpu": [
800
+
"ia32"
801
+
],
802
+
"dev": true,
803
+
"license": "MIT",
804
+
"optional": true,
805
+
"os": [
806
+
"win32"
807
+
],
808
+
"engines": {
809
+
"node": ">=18"
810
+
}
811
+
},
812
+
"node_modules/@esbuild/win32-x64": {
813
+
"version": "0.25.8",
814
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz",
815
+
"integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==",
816
+
"cpu": [
817
+
"x64"
818
+
],
819
+
"dev": true,
820
+
"license": "MIT",
821
+
"optional": true,
822
+
"os": [
823
+
"win32"
824
+
],
825
+
"engines": {
826
+
"node": ">=18"
827
+
}
828
+
},
829
+
"node_modules/@eslint-community/eslint-utils": {
830
+
"version": "4.7.0",
831
+
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
832
+
"integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
833
+
"dev": true,
834
+
"license": "MIT",
835
+
"dependencies": {
836
+
"eslint-visitor-keys": "^3.4.3"
837
+
},
838
+
"engines": {
839
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
840
+
},
841
+
"funding": {
842
+
"url": "https://opencollective.com/eslint"
843
+
},
844
+
"peerDependencies": {
845
+
"eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
846
+
}
847
+
},
848
+
"node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
849
+
"version": "3.4.3",
850
+
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
851
+
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
852
+
"dev": true,
853
+
"license": "Apache-2.0",
854
+
"engines": {
855
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
856
+
},
857
+
"funding": {
858
+
"url": "https://opencollective.com/eslint"
859
+
}
860
+
},
861
+
"node_modules/@eslint-community/regexpp": {
862
+
"version": "4.12.1",
863
+
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
864
+
"integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
865
+
"dev": true,
866
+
"license": "MIT",
867
+
"engines": {
868
+
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
869
+
}
870
+
},
871
+
"node_modules/@eslint/config-array": {
872
+
"version": "0.21.0",
873
+
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz",
874
+
"integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
875
+
"dev": true,
876
+
"license": "Apache-2.0",
877
+
"dependencies": {
878
+
"@eslint/object-schema": "^2.1.6",
879
+
"debug": "^4.3.1",
880
+
"minimatch": "^3.1.2"
881
+
},
882
+
"engines": {
883
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
884
+
}
885
+
},
886
+
"node_modules/@eslint/config-helpers": {
887
+
"version": "0.3.0",
888
+
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz",
889
+
"integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==",
890
+
"dev": true,
891
+
"license": "Apache-2.0",
892
+
"engines": {
893
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
894
+
}
895
+
},
896
+
"node_modules/@eslint/core": {
897
+
"version": "0.15.1",
898
+
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz",
899
+
"integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==",
900
+
"dev": true,
901
+
"license": "Apache-2.0",
902
+
"dependencies": {
903
+
"@types/json-schema": "^7.0.15"
904
+
},
905
+
"engines": {
906
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
907
+
}
908
+
},
909
+
"node_modules/@eslint/eslintrc": {
910
+
"version": "3.3.1",
911
+
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
912
+
"integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
913
+
"dev": true,
914
+
"license": "MIT",
915
+
"dependencies": {
916
+
"ajv": "^6.12.4",
917
+
"debug": "^4.3.2",
918
+
"espree": "^10.0.1",
919
+
"globals": "^14.0.0",
920
+
"ignore": "^5.2.0",
921
+
"import-fresh": "^3.2.1",
922
+
"js-yaml": "^4.1.0",
923
+
"minimatch": "^3.1.2",
924
+
"strip-json-comments": "^3.1.1"
925
+
},
926
+
"engines": {
927
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
928
+
},
929
+
"funding": {
930
+
"url": "https://opencollective.com/eslint"
931
+
}
932
+
},
933
+
"node_modules/@eslint/eslintrc/node_modules/globals": {
934
+
"version": "14.0.0",
935
+
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
936
+
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
937
+
"dev": true,
938
+
"license": "MIT",
939
+
"engines": {
940
+
"node": ">=18"
941
+
},
942
+
"funding": {
943
+
"url": "https://github.com/sponsors/sindresorhus"
944
+
}
945
+
},
946
+
"node_modules/@eslint/js": {
947
+
"version": "9.31.0",
948
+
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz",
949
+
"integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==",
950
+
"dev": true,
951
+
"license": "MIT",
952
+
"engines": {
953
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
954
+
},
955
+
"funding": {
956
+
"url": "https://eslint.org/donate"
957
+
}
958
+
},
959
+
"node_modules/@eslint/object-schema": {
960
+
"version": "2.1.6",
961
+
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
962
+
"integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
963
+
"dev": true,
964
+
"license": "Apache-2.0",
965
+
"engines": {
966
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
967
+
}
968
+
},
969
+
"node_modules/@eslint/plugin-kit": {
970
+
"version": "0.3.4",
971
+
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz",
972
+
"integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==",
973
+
"dev": true,
974
+
"license": "Apache-2.0",
975
+
"dependencies": {
976
+
"@eslint/core": "^0.15.1",
977
+
"levn": "^0.4.1"
978
+
},
979
+
"engines": {
980
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
981
+
}
982
+
},
983
+
"node_modules/@humanfs/core": {
984
+
"version": "0.19.1",
985
+
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
986
+
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
987
+
"dev": true,
988
+
"license": "Apache-2.0",
989
+
"engines": {
990
+
"node": ">=18.18.0"
991
+
}
992
+
},
993
+
"node_modules/@humanfs/node": {
994
+
"version": "0.16.6",
995
+
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
996
+
"integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
997
+
"dev": true,
998
+
"license": "Apache-2.0",
999
+
"dependencies": {
1000
+
"@humanfs/core": "^0.19.1",
1001
+
"@humanwhocodes/retry": "^0.3.0"
1002
+
},
1003
+
"engines": {
1004
+
"node": ">=18.18.0"
1005
+
}
1006
+
},
1007
+
"node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
1008
+
"version": "0.3.1",
1009
+
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
1010
+
"integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
1011
+
"dev": true,
1012
+
"license": "Apache-2.0",
1013
+
"engines": {
1014
+
"node": ">=18.18"
1015
+
},
1016
+
"funding": {
1017
+
"type": "github",
1018
+
"url": "https://github.com/sponsors/nzakas"
1019
+
}
1020
+
},
1021
+
"node_modules/@humanwhocodes/module-importer": {
1022
+
"version": "1.0.1",
1023
+
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
1024
+
"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
1025
+
"dev": true,
1026
+
"license": "Apache-2.0",
1027
+
"engines": {
1028
+
"node": ">=12.22"
1029
+
},
1030
+
"funding": {
1031
+
"type": "github",
1032
+
"url": "https://github.com/sponsors/nzakas"
1033
+
}
1034
+
},
1035
+
"node_modules/@humanwhocodes/retry": {
1036
+
"version": "0.4.3",
1037
+
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
1038
+
"integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
1039
+
"dev": true,
1040
+
"license": "Apache-2.0",
1041
+
"engines": {
1042
+
"node": ">=18.18"
1043
+
},
1044
+
"funding": {
1045
+
"type": "github",
1046
+
"url": "https://github.com/sponsors/nzakas"
1047
+
}
1048
+
},
1049
+
"node_modules/@jridgewell/gen-mapping": {
1050
+
"version": "0.3.12",
1051
+
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz",
1052
+
"integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==",
1053
+
"dev": true,
1054
+
"license": "MIT",
1055
+
"dependencies": {
1056
+
"@jridgewell/sourcemap-codec": "^1.5.0",
1057
+
"@jridgewell/trace-mapping": "^0.3.24"
1058
+
}
1059
+
},
1060
+
"node_modules/@jridgewell/resolve-uri": {
1061
+
"version": "3.1.2",
1062
+
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
1063
+
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
1064
+
"dev": true,
1065
+
"license": "MIT",
1066
+
"engines": {
1067
+
"node": ">=6.0.0"
1068
+
}
1069
+
},
1070
+
"node_modules/@jridgewell/sourcemap-codec": {
1071
+
"version": "1.5.4",
1072
+
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",
1073
+
"integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==",
1074
+
"dev": true,
1075
+
"license": "MIT"
1076
+
},
1077
+
"node_modules/@jridgewell/trace-mapping": {
1078
+
"version": "0.3.29",
1079
+
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz",
1080
+
"integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==",
1081
+
"dev": true,
1082
+
"license": "MIT",
1083
+
"dependencies": {
1084
+
"@jridgewell/resolve-uri": "^3.1.0",
1085
+
"@jridgewell/sourcemap-codec": "^1.4.14"
1086
+
}
1087
+
},
1088
+
"node_modules/@nodelib/fs.scandir": {
1089
+
"version": "2.1.5",
1090
+
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
1091
+
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
1092
+
"dev": true,
1093
+
"license": "MIT",
1094
+
"dependencies": {
1095
+
"@nodelib/fs.stat": "2.0.5",
1096
+
"run-parallel": "^1.1.9"
1097
+
},
1098
+
"engines": {
1099
+
"node": ">= 8"
1100
+
}
1101
+
},
1102
+
"node_modules/@nodelib/fs.stat": {
1103
+
"version": "2.0.5",
1104
+
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
1105
+
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
1106
+
"dev": true,
1107
+
"license": "MIT",
1108
+
"engines": {
1109
+
"node": ">= 8"
1110
+
}
1111
+
},
1112
+
"node_modules/@nodelib/fs.walk": {
1113
+
"version": "1.2.8",
1114
+
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
1115
+
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
1116
+
"dev": true,
1117
+
"license": "MIT",
1118
+
"dependencies": {
1119
+
"@nodelib/fs.scandir": "2.1.5",
1120
+
"fastq": "^1.6.0"
1121
+
},
1122
+
"engines": {
1123
+
"node": ">= 8"
1124
+
}
1125
+
},
1126
+
"node_modules/@rolldown/pluginutils": {
1127
+
"version": "1.0.0-beta.27",
1128
+
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
1129
+
"integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
1130
+
"dev": true,
1131
+
"license": "MIT"
1132
+
},
1133
+
"node_modules/@rollup/rollup-android-arm-eabi": {
1134
+
"version": "4.45.1",
1135
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz",
1136
+
"integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==",
1137
+
"cpu": [
1138
+
"arm"
1139
+
],
1140
+
"dev": true,
1141
+
"license": "MIT",
1142
+
"optional": true,
1143
+
"os": [
1144
+
"android"
1145
+
]
1146
+
},
1147
+
"node_modules/@rollup/rollup-android-arm64": {
1148
+
"version": "4.45.1",
1149
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz",
1150
+
"integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==",
1151
+
"cpu": [
1152
+
"arm64"
1153
+
],
1154
+
"dev": true,
1155
+
"license": "MIT",
1156
+
"optional": true,
1157
+
"os": [
1158
+
"android"
1159
+
]
1160
+
},
1161
+
"node_modules/@rollup/rollup-darwin-arm64": {
1162
+
"version": "4.45.1",
1163
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz",
1164
+
"integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==",
1165
+
"cpu": [
1166
+
"arm64"
1167
+
],
1168
+
"dev": true,
1169
+
"license": "MIT",
1170
+
"optional": true,
1171
+
"os": [
1172
+
"darwin"
1173
+
]
1174
+
},
1175
+
"node_modules/@rollup/rollup-darwin-x64": {
1176
+
"version": "4.45.1",
1177
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz",
1178
+
"integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==",
1179
+
"cpu": [
1180
+
"x64"
1181
+
],
1182
+
"dev": true,
1183
+
"license": "MIT",
1184
+
"optional": true,
1185
+
"os": [
1186
+
"darwin"
1187
+
]
1188
+
},
1189
+
"node_modules/@rollup/rollup-freebsd-arm64": {
1190
+
"version": "4.45.1",
1191
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz",
1192
+
"integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==",
1193
+
"cpu": [
1194
+
"arm64"
1195
+
],
1196
+
"dev": true,
1197
+
"license": "MIT",
1198
+
"optional": true,
1199
+
"os": [
1200
+
"freebsd"
1201
+
]
1202
+
},
1203
+
"node_modules/@rollup/rollup-freebsd-x64": {
1204
+
"version": "4.45.1",
1205
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz",
1206
+
"integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==",
1207
+
"cpu": [
1208
+
"x64"
1209
+
],
1210
+
"dev": true,
1211
+
"license": "MIT",
1212
+
"optional": true,
1213
+
"os": [
1214
+
"freebsd"
1215
+
]
1216
+
},
1217
+
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
1218
+
"version": "4.45.1",
1219
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz",
1220
+
"integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==",
1221
+
"cpu": [
1222
+
"arm"
1223
+
],
1224
+
"dev": true,
1225
+
"license": "MIT",
1226
+
"optional": true,
1227
+
"os": [
1228
+
"linux"
1229
+
]
1230
+
},
1231
+
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
1232
+
"version": "4.45.1",
1233
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz",
1234
+
"integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==",
1235
+
"cpu": [
1236
+
"arm"
1237
+
],
1238
+
"dev": true,
1239
+
"license": "MIT",
1240
+
"optional": true,
1241
+
"os": [
1242
+
"linux"
1243
+
]
1244
+
},
1245
+
"node_modules/@rollup/rollup-linux-arm64-gnu": {
1246
+
"version": "4.45.1",
1247
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz",
1248
+
"integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==",
1249
+
"cpu": [
1250
+
"arm64"
1251
+
],
1252
+
"dev": true,
1253
+
"license": "MIT",
1254
+
"optional": true,
1255
+
"os": [
1256
+
"linux"
1257
+
]
1258
+
},
1259
+
"node_modules/@rollup/rollup-linux-arm64-musl": {
1260
+
"version": "4.45.1",
1261
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz",
1262
+
"integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==",
1263
+
"cpu": [
1264
+
"arm64"
1265
+
],
1266
+
"dev": true,
1267
+
"license": "MIT",
1268
+
"optional": true,
1269
+
"os": [
1270
+
"linux"
1271
+
]
1272
+
},
1273
+
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
1274
+
"version": "4.45.1",
1275
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz",
1276
+
"integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==",
1277
+
"cpu": [
1278
+
"loong64"
1279
+
],
1280
+
"dev": true,
1281
+
"license": "MIT",
1282
+
"optional": true,
1283
+
"os": [
1284
+
"linux"
1285
+
]
1286
+
},
1287
+
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
1288
+
"version": "4.45.1",
1289
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz",
1290
+
"integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==",
1291
+
"cpu": [
1292
+
"ppc64"
1293
+
],
1294
+
"dev": true,
1295
+
"license": "MIT",
1296
+
"optional": true,
1297
+
"os": [
1298
+
"linux"
1299
+
]
1300
+
},
1301
+
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
1302
+
"version": "4.45.1",
1303
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz",
1304
+
"integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==",
1305
+
"cpu": [
1306
+
"riscv64"
1307
+
],
1308
+
"dev": true,
1309
+
"license": "MIT",
1310
+
"optional": true,
1311
+
"os": [
1312
+
"linux"
1313
+
]
1314
+
},
1315
+
"node_modules/@rollup/rollup-linux-riscv64-musl": {
1316
+
"version": "4.45.1",
1317
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz",
1318
+
"integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==",
1319
+
"cpu": [
1320
+
"riscv64"
1321
+
],
1322
+
"dev": true,
1323
+
"license": "MIT",
1324
+
"optional": true,
1325
+
"os": [
1326
+
"linux"
1327
+
]
1328
+
},
1329
+
"node_modules/@rollup/rollup-linux-s390x-gnu": {
1330
+
"version": "4.45.1",
1331
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz",
1332
+
"integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==",
1333
+
"cpu": [
1334
+
"s390x"
1335
+
],
1336
+
"dev": true,
1337
+
"license": "MIT",
1338
+
"optional": true,
1339
+
"os": [
1340
+
"linux"
1341
+
]
1342
+
},
1343
+
"node_modules/@rollup/rollup-linux-x64-gnu": {
1344
+
"version": "4.45.1",
1345
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz",
1346
+
"integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==",
1347
+
"cpu": [
1348
+
"x64"
1349
+
],
1350
+
"dev": true,
1351
+
"license": "MIT",
1352
+
"optional": true,
1353
+
"os": [
1354
+
"linux"
1355
+
]
1356
+
},
1357
+
"node_modules/@rollup/rollup-linux-x64-musl": {
1358
+
"version": "4.45.1",
1359
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz",
1360
+
"integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==",
1361
+
"cpu": [
1362
+
"x64"
1363
+
],
1364
+
"dev": true,
1365
+
"license": "MIT",
1366
+
"optional": true,
1367
+
"os": [
1368
+
"linux"
1369
+
]
1370
+
},
1371
+
"node_modules/@rollup/rollup-win32-arm64-msvc": {
1372
+
"version": "4.45.1",
1373
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz",
1374
+
"integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==",
1375
+
"cpu": [
1376
+
"arm64"
1377
+
],
1378
+
"dev": true,
1379
+
"license": "MIT",
1380
+
"optional": true,
1381
+
"os": [
1382
+
"win32"
1383
+
]
1384
+
},
1385
+
"node_modules/@rollup/rollup-win32-ia32-msvc": {
1386
+
"version": "4.45.1",
1387
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz",
1388
+
"integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==",
1389
+
"cpu": [
1390
+
"ia32"
1391
+
],
1392
+
"dev": true,
1393
+
"license": "MIT",
1394
+
"optional": true,
1395
+
"os": [
1396
+
"win32"
1397
+
]
1398
+
},
1399
+
"node_modules/@rollup/rollup-win32-x64-msvc": {
1400
+
"version": "4.45.1",
1401
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz",
1402
+
"integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==",
1403
+
"cpu": [
1404
+
"x64"
1405
+
],
1406
+
"dev": true,
1407
+
"license": "MIT",
1408
+
"optional": true,
1409
+
"os": [
1410
+
"win32"
1411
+
]
1412
+
},
1413
+
"node_modules/@types/babel__core": {
1414
+
"version": "7.20.5",
1415
+
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
1416
+
"integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
1417
+
"dev": true,
1418
+
"license": "MIT",
1419
+
"dependencies": {
1420
+
"@babel/parser": "^7.20.7",
1421
+
"@babel/types": "^7.20.7",
1422
+
"@types/babel__generator": "*",
1423
+
"@types/babel__template": "*",
1424
+
"@types/babel__traverse": "*"
1425
+
}
1426
+
},
1427
+
"node_modules/@types/babel__generator": {
1428
+
"version": "7.27.0",
1429
+
"resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
1430
+
"integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
1431
+
"dev": true,
1432
+
"license": "MIT",
1433
+
"dependencies": {
1434
+
"@babel/types": "^7.0.0"
1435
+
}
1436
+
},
1437
+
"node_modules/@types/babel__template": {
1438
+
"version": "7.4.4",
1439
+
"resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
1440
+
"integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
1441
+
"dev": true,
1442
+
"license": "MIT",
1443
+
"dependencies": {
1444
+
"@babel/parser": "^7.1.0",
1445
+
"@babel/types": "^7.0.0"
1446
+
}
1447
+
},
1448
+
"node_modules/@types/babel__traverse": {
1449
+
"version": "7.20.7",
1450
+
"resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz",
1451
+
"integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==",
1452
+
"dev": true,
1453
+
"license": "MIT",
1454
+
"dependencies": {
1455
+
"@babel/types": "^7.20.7"
1456
+
}
1457
+
},
1458
+
"node_modules/@types/estree": {
1459
+
"version": "1.0.8",
1460
+
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
1461
+
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
1462
+
"dev": true,
1463
+
"license": "MIT"
1464
+
},
1465
+
"node_modules/@types/json-schema": {
1466
+
"version": "7.0.15",
1467
+
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
1468
+
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
1469
+
"dev": true,
1470
+
"license": "MIT"
1471
+
},
1472
+
"node_modules/@types/react": {
1473
+
"version": "19.1.8",
1474
+
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz",
1475
+
"integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==",
1476
+
"dev": true,
1477
+
"license": "MIT",
1478
+
"dependencies": {
1479
+
"csstype": "^3.0.2"
1480
+
}
1481
+
},
1482
+
"node_modules/@types/react-dom": {
1483
+
"version": "19.1.6",
1484
+
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz",
1485
+
"integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==",
1486
+
"dev": true,
1487
+
"license": "MIT",
1488
+
"peerDependencies": {
1489
+
"@types/react": "^19.0.0"
1490
+
}
1491
+
},
1492
+
"node_modules/@typescript-eslint/eslint-plugin": {
1493
+
"version": "8.38.0",
1494
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz",
1495
+
"integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==",
1496
+
"dev": true,
1497
+
"license": "MIT",
1498
+
"dependencies": {
1499
+
"@eslint-community/regexpp": "^4.10.0",
1500
+
"@typescript-eslint/scope-manager": "8.38.0",
1501
+
"@typescript-eslint/type-utils": "8.38.0",
1502
+
"@typescript-eslint/utils": "8.38.0",
1503
+
"@typescript-eslint/visitor-keys": "8.38.0",
1504
+
"graphemer": "^1.4.0",
1505
+
"ignore": "^7.0.0",
1506
+
"natural-compare": "^1.4.0",
1507
+
"ts-api-utils": "^2.1.0"
1508
+
},
1509
+
"engines": {
1510
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1511
+
},
1512
+
"funding": {
1513
+
"type": "opencollective",
1514
+
"url": "https://opencollective.com/typescript-eslint"
1515
+
},
1516
+
"peerDependencies": {
1517
+
"@typescript-eslint/parser": "^8.38.0",
1518
+
"eslint": "^8.57.0 || ^9.0.0",
1519
+
"typescript": ">=4.8.4 <5.9.0"
1520
+
}
1521
+
},
1522
+
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
1523
+
"version": "7.0.5",
1524
+
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
1525
+
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
1526
+
"dev": true,
1527
+
"license": "MIT",
1528
+
"engines": {
1529
+
"node": ">= 4"
1530
+
}
1531
+
},
1532
+
"node_modules/@typescript-eslint/parser": {
1533
+
"version": "8.38.0",
1534
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz",
1535
+
"integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==",
1536
+
"dev": true,
1537
+
"license": "MIT",
1538
+
"dependencies": {
1539
+
"@typescript-eslint/scope-manager": "8.38.0",
1540
+
"@typescript-eslint/types": "8.38.0",
1541
+
"@typescript-eslint/typescript-estree": "8.38.0",
1542
+
"@typescript-eslint/visitor-keys": "8.38.0",
1543
+
"debug": "^4.3.4"
1544
+
},
1545
+
"engines": {
1546
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1547
+
},
1548
+
"funding": {
1549
+
"type": "opencollective",
1550
+
"url": "https://opencollective.com/typescript-eslint"
1551
+
},
1552
+
"peerDependencies": {
1553
+
"eslint": "^8.57.0 || ^9.0.0",
1554
+
"typescript": ">=4.8.4 <5.9.0"
1555
+
}
1556
+
},
1557
+
"node_modules/@typescript-eslint/project-service": {
1558
+
"version": "8.38.0",
1559
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz",
1560
+
"integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==",
1561
+
"dev": true,
1562
+
"license": "MIT",
1563
+
"dependencies": {
1564
+
"@typescript-eslint/tsconfig-utils": "^8.38.0",
1565
+
"@typescript-eslint/types": "^8.38.0",
1566
+
"debug": "^4.3.4"
1567
+
},
1568
+
"engines": {
1569
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1570
+
},
1571
+
"funding": {
1572
+
"type": "opencollective",
1573
+
"url": "https://opencollective.com/typescript-eslint"
1574
+
},
1575
+
"peerDependencies": {
1576
+
"typescript": ">=4.8.4 <5.9.0"
1577
+
}
1578
+
},
1579
+
"node_modules/@typescript-eslint/scope-manager": {
1580
+
"version": "8.38.0",
1581
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz",
1582
+
"integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==",
1583
+
"dev": true,
1584
+
"license": "MIT",
1585
+
"dependencies": {
1586
+
"@typescript-eslint/types": "8.38.0",
1587
+
"@typescript-eslint/visitor-keys": "8.38.0"
1588
+
},
1589
+
"engines": {
1590
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1591
+
},
1592
+
"funding": {
1593
+
"type": "opencollective",
1594
+
"url": "https://opencollective.com/typescript-eslint"
1595
+
}
1596
+
},
1597
+
"node_modules/@typescript-eslint/tsconfig-utils": {
1598
+
"version": "8.38.0",
1599
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz",
1600
+
"integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==",
1601
+
"dev": true,
1602
+
"license": "MIT",
1603
+
"engines": {
1604
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1605
+
},
1606
+
"funding": {
1607
+
"type": "opencollective",
1608
+
"url": "https://opencollective.com/typescript-eslint"
1609
+
},
1610
+
"peerDependencies": {
1611
+
"typescript": ">=4.8.4 <5.9.0"
1612
+
}
1613
+
},
1614
+
"node_modules/@typescript-eslint/type-utils": {
1615
+
"version": "8.38.0",
1616
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz",
1617
+
"integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==",
1618
+
"dev": true,
1619
+
"license": "MIT",
1620
+
"dependencies": {
1621
+
"@typescript-eslint/types": "8.38.0",
1622
+
"@typescript-eslint/typescript-estree": "8.38.0",
1623
+
"@typescript-eslint/utils": "8.38.0",
1624
+
"debug": "^4.3.4",
1625
+
"ts-api-utils": "^2.1.0"
1626
+
},
1627
+
"engines": {
1628
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1629
+
},
1630
+
"funding": {
1631
+
"type": "opencollective",
1632
+
"url": "https://opencollective.com/typescript-eslint"
1633
+
},
1634
+
"peerDependencies": {
1635
+
"eslint": "^8.57.0 || ^9.0.0",
1636
+
"typescript": ">=4.8.4 <5.9.0"
1637
+
}
1638
+
},
1639
+
"node_modules/@typescript-eslint/types": {
1640
+
"version": "8.38.0",
1641
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz",
1642
+
"integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==",
1643
+
"dev": true,
1644
+
"license": "MIT",
1645
+
"engines": {
1646
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1647
+
},
1648
+
"funding": {
1649
+
"type": "opencollective",
1650
+
"url": "https://opencollective.com/typescript-eslint"
1651
+
}
1652
+
},
1653
+
"node_modules/@typescript-eslint/typescript-estree": {
1654
+
"version": "8.38.0",
1655
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz",
1656
+
"integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==",
1657
+
"dev": true,
1658
+
"license": "MIT",
1659
+
"dependencies": {
1660
+
"@typescript-eslint/project-service": "8.38.0",
1661
+
"@typescript-eslint/tsconfig-utils": "8.38.0",
1662
+
"@typescript-eslint/types": "8.38.0",
1663
+
"@typescript-eslint/visitor-keys": "8.38.0",
1664
+
"debug": "^4.3.4",
1665
+
"fast-glob": "^3.3.2",
1666
+
"is-glob": "^4.0.3",
1667
+
"minimatch": "^9.0.4",
1668
+
"semver": "^7.6.0",
1669
+
"ts-api-utils": "^2.1.0"
1670
+
},
1671
+
"engines": {
1672
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1673
+
},
1674
+
"funding": {
1675
+
"type": "opencollective",
1676
+
"url": "https://opencollective.com/typescript-eslint"
1677
+
},
1678
+
"peerDependencies": {
1679
+
"typescript": ">=4.8.4 <5.9.0"
1680
+
}
1681
+
},
1682
+
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
1683
+
"version": "2.0.2",
1684
+
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
1685
+
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
1686
+
"dev": true,
1687
+
"license": "MIT",
1688
+
"dependencies": {
1689
+
"balanced-match": "^1.0.0"
1690
+
}
1691
+
},
1692
+
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
1693
+
"version": "9.0.5",
1694
+
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
1695
+
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
1696
+
"dev": true,
1697
+
"license": "ISC",
1698
+
"dependencies": {
1699
+
"brace-expansion": "^2.0.1"
1700
+
},
1701
+
"engines": {
1702
+
"node": ">=16 || 14 >=14.17"
1703
+
},
1704
+
"funding": {
1705
+
"url": "https://github.com/sponsors/isaacs"
1706
+
}
1707
+
},
1708
+
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
1709
+
"version": "7.7.2",
1710
+
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
1711
+
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
1712
+
"dev": true,
1713
+
"license": "ISC",
1714
+
"bin": {
1715
+
"semver": "bin/semver.js"
1716
+
},
1717
+
"engines": {
1718
+
"node": ">=10"
1719
+
}
1720
+
},
1721
+
"node_modules/@typescript-eslint/utils": {
1722
+
"version": "8.38.0",
1723
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz",
1724
+
"integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==",
1725
+
"dev": true,
1726
+
"license": "MIT",
1727
+
"dependencies": {
1728
+
"@eslint-community/eslint-utils": "^4.7.0",
1729
+
"@typescript-eslint/scope-manager": "8.38.0",
1730
+
"@typescript-eslint/types": "8.38.0",
1731
+
"@typescript-eslint/typescript-estree": "8.38.0"
1732
+
},
1733
+
"engines": {
1734
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1735
+
},
1736
+
"funding": {
1737
+
"type": "opencollective",
1738
+
"url": "https://opencollective.com/typescript-eslint"
1739
+
},
1740
+
"peerDependencies": {
1741
+
"eslint": "^8.57.0 || ^9.0.0",
1742
+
"typescript": ">=4.8.4 <5.9.0"
1743
+
}
1744
+
},
1745
+
"node_modules/@typescript-eslint/visitor-keys": {
1746
+
"version": "8.38.0",
1747
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz",
1748
+
"integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==",
1749
+
"dev": true,
1750
+
"license": "MIT",
1751
+
"dependencies": {
1752
+
"@typescript-eslint/types": "8.38.0",
1753
+
"eslint-visitor-keys": "^4.2.1"
1754
+
},
1755
+
"engines": {
1756
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1757
+
},
1758
+
"funding": {
1759
+
"type": "opencollective",
1760
+
"url": "https://opencollective.com/typescript-eslint"
1761
+
}
1762
+
},
1763
+
"node_modules/@vitejs/plugin-react": {
1764
+
"version": "4.7.0",
1765
+
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
1766
+
"integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
1767
+
"dev": true,
1768
+
"license": "MIT",
1769
+
"dependencies": {
1770
+
"@babel/core": "^7.28.0",
1771
+
"@babel/plugin-transform-react-jsx-self": "^7.27.1",
1772
+
"@babel/plugin-transform-react-jsx-source": "^7.27.1",
1773
+
"@rolldown/pluginutils": "1.0.0-beta.27",
1774
+
"@types/babel__core": "^7.20.5",
1775
+
"react-refresh": "^0.17.0"
1776
+
},
1777
+
"engines": {
1778
+
"node": "^14.18.0 || >=16.0.0"
1779
+
},
1780
+
"peerDependencies": {
1781
+
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
1782
+
}
1783
+
},
1784
+
"node_modules/acorn": {
1785
+
"version": "8.15.0",
1786
+
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
1787
+
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
1788
+
"dev": true,
1789
+
"license": "MIT",
1790
+
"bin": {
1791
+
"acorn": "bin/acorn"
1792
+
},
1793
+
"engines": {
1794
+
"node": ">=0.4.0"
1795
+
}
1796
+
},
1797
+
"node_modules/acorn-jsx": {
1798
+
"version": "5.3.2",
1799
+
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
1800
+
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
1801
+
"dev": true,
1802
+
"license": "MIT",
1803
+
"peerDependencies": {
1804
+
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
1805
+
}
1806
+
},
1807
+
"node_modules/ajv": {
1808
+
"version": "6.12.6",
1809
+
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
1810
+
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
1811
+
"dev": true,
1812
+
"license": "MIT",
1813
+
"dependencies": {
1814
+
"fast-deep-equal": "^3.1.1",
1815
+
"fast-json-stable-stringify": "^2.0.0",
1816
+
"json-schema-traverse": "^0.4.1",
1817
+
"uri-js": "^4.2.2"
1818
+
},
1819
+
"funding": {
1820
+
"type": "github",
1821
+
"url": "https://github.com/sponsors/epoberezkin"
1822
+
}
1823
+
},
1824
+
"node_modules/ansi-styles": {
1825
+
"version": "4.3.0",
1826
+
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
1827
+
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
1828
+
"dev": true,
1829
+
"license": "MIT",
1830
+
"dependencies": {
1831
+
"color-convert": "^2.0.1"
1832
+
},
1833
+
"engines": {
1834
+
"node": ">=8"
1835
+
},
1836
+
"funding": {
1837
+
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
1838
+
}
1839
+
},
1840
+
"node_modules/argparse": {
1841
+
"version": "2.0.1",
1842
+
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
1843
+
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
1844
+
"dev": true,
1845
+
"license": "Python-2.0"
1846
+
},
1847
+
"node_modules/balanced-match": {
1848
+
"version": "1.0.2",
1849
+
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
1850
+
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
1851
+
"dev": true,
1852
+
"license": "MIT"
1853
+
},
1854
+
"node_modules/brace-expansion": {
1855
+
"version": "1.1.12",
1856
+
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
1857
+
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
1858
+
"dev": true,
1859
+
"license": "MIT",
1860
+
"dependencies": {
1861
+
"balanced-match": "^1.0.0",
1862
+
"concat-map": "0.0.1"
1863
+
}
1864
+
},
1865
+
"node_modules/braces": {
1866
+
"version": "3.0.3",
1867
+
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
1868
+
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
1869
+
"dev": true,
1870
+
"license": "MIT",
1871
+
"dependencies": {
1872
+
"fill-range": "^7.1.1"
1873
+
},
1874
+
"engines": {
1875
+
"node": ">=8"
1876
+
}
1877
+
},
1878
+
"node_modules/browserslist": {
1879
+
"version": "4.25.1",
1880
+
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz",
1881
+
"integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==",
1882
+
"dev": true,
1883
+
"funding": [
1884
+
{
1885
+
"type": "opencollective",
1886
+
"url": "https://opencollective.com/browserslist"
1887
+
},
1888
+
{
1889
+
"type": "tidelift",
1890
+
"url": "https://tidelift.com/funding/github/npm/browserslist"
1891
+
},
1892
+
{
1893
+
"type": "github",
1894
+
"url": "https://github.com/sponsors/ai"
1895
+
}
1896
+
],
1897
+
"license": "MIT",
1898
+
"dependencies": {
1899
+
"caniuse-lite": "^1.0.30001726",
1900
+
"electron-to-chromium": "^1.5.173",
1901
+
"node-releases": "^2.0.19",
1902
+
"update-browserslist-db": "^1.1.3"
1903
+
},
1904
+
"bin": {
1905
+
"browserslist": "cli.js"
1906
+
},
1907
+
"engines": {
1908
+
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
1909
+
}
1910
+
},
1911
+
"node_modules/callsites": {
1912
+
"version": "3.1.0",
1913
+
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
1914
+
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
1915
+
"dev": true,
1916
+
"license": "MIT",
1917
+
"engines": {
1918
+
"node": ">=6"
1919
+
}
1920
+
},
1921
+
"node_modules/caniuse-lite": {
1922
+
"version": "1.0.30001727",
1923
+
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz",
1924
+
"integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==",
1925
+
"dev": true,
1926
+
"funding": [
1927
+
{
1928
+
"type": "opencollective",
1929
+
"url": "https://opencollective.com/browserslist"
1930
+
},
1931
+
{
1932
+
"type": "tidelift",
1933
+
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
1934
+
},
1935
+
{
1936
+
"type": "github",
1937
+
"url": "https://github.com/sponsors/ai"
1938
+
}
1939
+
],
1940
+
"license": "CC-BY-4.0"
1941
+
},
1942
+
"node_modules/chalk": {
1943
+
"version": "4.1.2",
1944
+
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
1945
+
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
1946
+
"dev": true,
1947
+
"license": "MIT",
1948
+
"dependencies": {
1949
+
"ansi-styles": "^4.1.0",
1950
+
"supports-color": "^7.1.0"
1951
+
},
1952
+
"engines": {
1953
+
"node": ">=10"
1954
+
},
1955
+
"funding": {
1956
+
"url": "https://github.com/chalk/chalk?sponsor=1"
1957
+
}
1958
+
},
1959
+
"node_modules/color-convert": {
1960
+
"version": "2.0.1",
1961
+
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
1962
+
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
1963
+
"dev": true,
1964
+
"license": "MIT",
1965
+
"dependencies": {
1966
+
"color-name": "~1.1.4"
1967
+
},
1968
+
"engines": {
1969
+
"node": ">=7.0.0"
1970
+
}
1971
+
},
1972
+
"node_modules/color-name": {
1973
+
"version": "1.1.4",
1974
+
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
1975
+
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
1976
+
"dev": true,
1977
+
"license": "MIT"
1978
+
},
1979
+
"node_modules/concat-map": {
1980
+
"version": "0.0.1",
1981
+
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
1982
+
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
1983
+
"dev": true,
1984
+
"license": "MIT"
1985
+
},
1986
+
"node_modules/convert-source-map": {
1987
+
"version": "2.0.0",
1988
+
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
1989
+
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
1990
+
"dev": true,
1991
+
"license": "MIT"
1992
+
},
1993
+
"node_modules/cross-spawn": {
1994
+
"version": "7.0.6",
1995
+
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
1996
+
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
1997
+
"dev": true,
1998
+
"license": "MIT",
1999
+
"dependencies": {
2000
+
"path-key": "^3.1.0",
2001
+
"shebang-command": "^2.0.0",
2002
+
"which": "^2.0.1"
2003
+
},
2004
+
"engines": {
2005
+
"node": ">= 8"
2006
+
}
2007
+
},
2008
+
"node_modules/csstype": {
2009
+
"version": "3.1.3",
2010
+
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
2011
+
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
2012
+
"dev": true,
2013
+
"license": "MIT"
2014
+
},
2015
+
"node_modules/debug": {
2016
+
"version": "4.4.1",
2017
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
2018
+
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
2019
+
"dev": true,
2020
+
"license": "MIT",
2021
+
"dependencies": {
2022
+
"ms": "^2.1.3"
2023
+
},
2024
+
"engines": {
2025
+
"node": ">=6.0"
2026
+
},
2027
+
"peerDependenciesMeta": {
2028
+
"supports-color": {
2029
+
"optional": true
2030
+
}
2031
+
}
2032
+
},
2033
+
"node_modules/deep-is": {
2034
+
"version": "0.1.4",
2035
+
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
2036
+
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
2037
+
"dev": true,
2038
+
"license": "MIT"
2039
+
},
2040
+
"node_modules/electron-to-chromium": {
2041
+
"version": "1.5.190",
2042
+
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.190.tgz",
2043
+
"integrity": "sha512-k4McmnB2091YIsdCgkS0fMVMPOJgxl93ltFzaryXqwip1AaxeDqKCGLxkXODDA5Ab/D+tV5EL5+aTx76RvLRxw==",
2044
+
"dev": true,
2045
+
"license": "ISC"
2046
+
},
2047
+
"node_modules/esbuild": {
2048
+
"version": "0.25.8",
2049
+
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz",
2050
+
"integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==",
2051
+
"dev": true,
2052
+
"hasInstallScript": true,
2053
+
"license": "MIT",
2054
+
"bin": {
2055
+
"esbuild": "bin/esbuild"
2056
+
},
2057
+
"engines": {
2058
+
"node": ">=18"
2059
+
},
2060
+
"optionalDependencies": {
2061
+
"@esbuild/aix-ppc64": "0.25.8",
2062
+
"@esbuild/android-arm": "0.25.8",
2063
+
"@esbuild/android-arm64": "0.25.8",
2064
+
"@esbuild/android-x64": "0.25.8",
2065
+
"@esbuild/darwin-arm64": "0.25.8",
2066
+
"@esbuild/darwin-x64": "0.25.8",
2067
+
"@esbuild/freebsd-arm64": "0.25.8",
2068
+
"@esbuild/freebsd-x64": "0.25.8",
2069
+
"@esbuild/linux-arm": "0.25.8",
2070
+
"@esbuild/linux-arm64": "0.25.8",
2071
+
"@esbuild/linux-ia32": "0.25.8",
2072
+
"@esbuild/linux-loong64": "0.25.8",
2073
+
"@esbuild/linux-mips64el": "0.25.8",
2074
+
"@esbuild/linux-ppc64": "0.25.8",
2075
+
"@esbuild/linux-riscv64": "0.25.8",
2076
+
"@esbuild/linux-s390x": "0.25.8",
2077
+
"@esbuild/linux-x64": "0.25.8",
2078
+
"@esbuild/netbsd-arm64": "0.25.8",
2079
+
"@esbuild/netbsd-x64": "0.25.8",
2080
+
"@esbuild/openbsd-arm64": "0.25.8",
2081
+
"@esbuild/openbsd-x64": "0.25.8",
2082
+
"@esbuild/openharmony-arm64": "0.25.8",
2083
+
"@esbuild/sunos-x64": "0.25.8",
2084
+
"@esbuild/win32-arm64": "0.25.8",
2085
+
"@esbuild/win32-ia32": "0.25.8",
2086
+
"@esbuild/win32-x64": "0.25.8"
2087
+
}
2088
+
},
2089
+
"node_modules/escalade": {
2090
+
"version": "3.2.0",
2091
+
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
2092
+
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
2093
+
"dev": true,
2094
+
"license": "MIT",
2095
+
"engines": {
2096
+
"node": ">=6"
2097
+
}
2098
+
},
2099
+
"node_modules/escape-string-regexp": {
2100
+
"version": "4.0.0",
2101
+
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
2102
+
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
2103
+
"dev": true,
2104
+
"license": "MIT",
2105
+
"engines": {
2106
+
"node": ">=10"
2107
+
},
2108
+
"funding": {
2109
+
"url": "https://github.com/sponsors/sindresorhus"
2110
+
}
2111
+
},
2112
+
"node_modules/eslint": {
2113
+
"version": "9.31.0",
2114
+
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz",
2115
+
"integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==",
2116
+
"dev": true,
2117
+
"license": "MIT",
2118
+
"dependencies": {
2119
+
"@eslint-community/eslint-utils": "^4.2.0",
2120
+
"@eslint-community/regexpp": "^4.12.1",
2121
+
"@eslint/config-array": "^0.21.0",
2122
+
"@eslint/config-helpers": "^0.3.0",
2123
+
"@eslint/core": "^0.15.0",
2124
+
"@eslint/eslintrc": "^3.3.1",
2125
+
"@eslint/js": "9.31.0",
2126
+
"@eslint/plugin-kit": "^0.3.1",
2127
+
"@humanfs/node": "^0.16.6",
2128
+
"@humanwhocodes/module-importer": "^1.0.1",
2129
+
"@humanwhocodes/retry": "^0.4.2",
2130
+
"@types/estree": "^1.0.6",
2131
+
"@types/json-schema": "^7.0.15",
2132
+
"ajv": "^6.12.4",
2133
+
"chalk": "^4.0.0",
2134
+
"cross-spawn": "^7.0.6",
2135
+
"debug": "^4.3.2",
2136
+
"escape-string-regexp": "^4.0.0",
2137
+
"eslint-scope": "^8.4.0",
2138
+
"eslint-visitor-keys": "^4.2.1",
2139
+
"espree": "^10.4.0",
2140
+
"esquery": "^1.5.0",
2141
+
"esutils": "^2.0.2",
2142
+
"fast-deep-equal": "^3.1.3",
2143
+
"file-entry-cache": "^8.0.0",
2144
+
"find-up": "^5.0.0",
2145
+
"glob-parent": "^6.0.2",
2146
+
"ignore": "^5.2.0",
2147
+
"imurmurhash": "^0.1.4",
2148
+
"is-glob": "^4.0.0",
2149
+
"json-stable-stringify-without-jsonify": "^1.0.1",
2150
+
"lodash.merge": "^4.6.2",
2151
+
"minimatch": "^3.1.2",
2152
+
"natural-compare": "^1.4.0",
2153
+
"optionator": "^0.9.3"
2154
+
},
2155
+
"bin": {
2156
+
"eslint": "bin/eslint.js"
2157
+
},
2158
+
"engines": {
2159
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
2160
+
},
2161
+
"funding": {
2162
+
"url": "https://eslint.org/donate"
2163
+
},
2164
+
"peerDependencies": {
2165
+
"jiti": "*"
2166
+
},
2167
+
"peerDependenciesMeta": {
2168
+
"jiti": {
2169
+
"optional": true
2170
+
}
2171
+
}
2172
+
},
2173
+
"node_modules/eslint-plugin-react-hooks": {
2174
+
"version": "5.2.0",
2175
+
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
2176
+
"integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
2177
+
"dev": true,
2178
+
"license": "MIT",
2179
+
"engines": {
2180
+
"node": ">=10"
2181
+
},
2182
+
"peerDependencies": {
2183
+
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
2184
+
}
2185
+
},
2186
+
"node_modules/eslint-plugin-react-refresh": {
2187
+
"version": "0.4.20",
2188
+
"resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz",
2189
+
"integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==",
2190
+
"dev": true,
2191
+
"license": "MIT",
2192
+
"peerDependencies": {
2193
+
"eslint": ">=8.40"
2194
+
}
2195
+
},
2196
+
"node_modules/eslint-scope": {
2197
+
"version": "8.4.0",
2198
+
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
2199
+
"integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
2200
+
"dev": true,
2201
+
"license": "BSD-2-Clause",
2202
+
"dependencies": {
2203
+
"esrecurse": "^4.3.0",
2204
+
"estraverse": "^5.2.0"
2205
+
},
2206
+
"engines": {
2207
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
2208
+
},
2209
+
"funding": {
2210
+
"url": "https://opencollective.com/eslint"
2211
+
}
2212
+
},
2213
+
"node_modules/eslint-visitor-keys": {
2214
+
"version": "4.2.1",
2215
+
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
2216
+
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
2217
+
"dev": true,
2218
+
"license": "Apache-2.0",
2219
+
"engines": {
2220
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
2221
+
},
2222
+
"funding": {
2223
+
"url": "https://opencollective.com/eslint"
2224
+
}
2225
+
},
2226
+
"node_modules/esm-env": {
2227
+
"version": "1.2.2",
2228
+
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
2229
+
"integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
2230
+
"license": "MIT"
2231
+
},
2232
+
"node_modules/espree": {
2233
+
"version": "10.4.0",
2234
+
"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
2235
+
"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
2236
+
"dev": true,
2237
+
"license": "BSD-2-Clause",
2238
+
"dependencies": {
2239
+
"acorn": "^8.15.0",
2240
+
"acorn-jsx": "^5.3.2",
2241
+
"eslint-visitor-keys": "^4.2.1"
2242
+
},
2243
+
"engines": {
2244
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
2245
+
},
2246
+
"funding": {
2247
+
"url": "https://opencollective.com/eslint"
2248
+
}
2249
+
},
2250
+
"node_modules/esquery": {
2251
+
"version": "1.6.0",
2252
+
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
2253
+
"integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
2254
+
"dev": true,
2255
+
"license": "BSD-3-Clause",
2256
+
"dependencies": {
2257
+
"estraverse": "^5.1.0"
2258
+
},
2259
+
"engines": {
2260
+
"node": ">=0.10"
2261
+
}
2262
+
},
2263
+
"node_modules/esrecurse": {
2264
+
"version": "4.3.0",
2265
+
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
2266
+
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
2267
+
"dev": true,
2268
+
"license": "BSD-2-Clause",
2269
+
"dependencies": {
2270
+
"estraverse": "^5.2.0"
2271
+
},
2272
+
"engines": {
2273
+
"node": ">=4.0"
2274
+
}
2275
+
},
2276
+
"node_modules/estraverse": {
2277
+
"version": "5.3.0",
2278
+
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
2279
+
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
2280
+
"dev": true,
2281
+
"license": "BSD-2-Clause",
2282
+
"engines": {
2283
+
"node": ">=4.0"
2284
+
}
2285
+
},
2286
+
"node_modules/esutils": {
2287
+
"version": "2.0.3",
2288
+
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
2289
+
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
2290
+
"dev": true,
2291
+
"license": "BSD-2-Clause",
2292
+
"engines": {
2293
+
"node": ">=0.10.0"
2294
+
}
2295
+
},
2296
+
"node_modules/fast-deep-equal": {
2297
+
"version": "3.1.3",
2298
+
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
2299
+
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
2300
+
"dev": true,
2301
+
"license": "MIT"
2302
+
},
2303
+
"node_modules/fast-glob": {
2304
+
"version": "3.3.3",
2305
+
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
2306
+
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
2307
+
"dev": true,
2308
+
"license": "MIT",
2309
+
"dependencies": {
2310
+
"@nodelib/fs.stat": "^2.0.2",
2311
+
"@nodelib/fs.walk": "^1.2.3",
2312
+
"glob-parent": "^5.1.2",
2313
+
"merge2": "^1.3.0",
2314
+
"micromatch": "^4.0.8"
2315
+
},
2316
+
"engines": {
2317
+
"node": ">=8.6.0"
2318
+
}
2319
+
},
2320
+
"node_modules/fast-glob/node_modules/glob-parent": {
2321
+
"version": "5.1.2",
2322
+
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
2323
+
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
2324
+
"dev": true,
2325
+
"license": "ISC",
2326
+
"dependencies": {
2327
+
"is-glob": "^4.0.1"
2328
+
},
2329
+
"engines": {
2330
+
"node": ">= 6"
2331
+
}
2332
+
},
2333
+
"node_modules/fast-json-stable-stringify": {
2334
+
"version": "2.1.0",
2335
+
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
2336
+
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
2337
+
"dev": true,
2338
+
"license": "MIT"
2339
+
},
2340
+
"node_modules/fast-levenshtein": {
2341
+
"version": "2.0.6",
2342
+
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
2343
+
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
2344
+
"dev": true,
2345
+
"license": "MIT"
2346
+
},
2347
+
"node_modules/fastq": {
2348
+
"version": "1.19.1",
2349
+
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
2350
+
"integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
2351
+
"dev": true,
2352
+
"license": "ISC",
2353
+
"dependencies": {
2354
+
"reusify": "^1.0.4"
2355
+
}
2356
+
},
2357
+
"node_modules/file-entry-cache": {
2358
+
"version": "8.0.0",
2359
+
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
2360
+
"integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
2361
+
"dev": true,
2362
+
"license": "MIT",
2363
+
"dependencies": {
2364
+
"flat-cache": "^4.0.0"
2365
+
},
2366
+
"engines": {
2367
+
"node": ">=16.0.0"
2368
+
}
2369
+
},
2370
+
"node_modules/fill-range": {
2371
+
"version": "7.1.1",
2372
+
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
2373
+
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
2374
+
"dev": true,
2375
+
"license": "MIT",
2376
+
"dependencies": {
2377
+
"to-regex-range": "^5.0.1"
2378
+
},
2379
+
"engines": {
2380
+
"node": ">=8"
2381
+
}
2382
+
},
2383
+
"node_modules/find-up": {
2384
+
"version": "5.0.0",
2385
+
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
2386
+
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
2387
+
"dev": true,
2388
+
"license": "MIT",
2389
+
"dependencies": {
2390
+
"locate-path": "^6.0.0",
2391
+
"path-exists": "^4.0.0"
2392
+
},
2393
+
"engines": {
2394
+
"node": ">=10"
2395
+
},
2396
+
"funding": {
2397
+
"url": "https://github.com/sponsors/sindresorhus"
2398
+
}
2399
+
},
2400
+
"node_modules/flat-cache": {
2401
+
"version": "4.0.1",
2402
+
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
2403
+
"integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
2404
+
"dev": true,
2405
+
"license": "MIT",
2406
+
"dependencies": {
2407
+
"flatted": "^3.2.9",
2408
+
"keyv": "^4.5.4"
2409
+
},
2410
+
"engines": {
2411
+
"node": ">=16"
2412
+
}
2413
+
},
2414
+
"node_modules/flatted": {
2415
+
"version": "3.3.3",
2416
+
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
2417
+
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
2418
+
"dev": true,
2419
+
"license": "ISC"
2420
+
},
2421
+
"node_modules/fsevents": {
2422
+
"version": "2.3.3",
2423
+
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
2424
+
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
2425
+
"dev": true,
2426
+
"hasInstallScript": true,
2427
+
"license": "MIT",
2428
+
"optional": true,
2429
+
"os": [
2430
+
"darwin"
2431
+
],
2432
+
"engines": {
2433
+
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
2434
+
}
2435
+
},
2436
+
"node_modules/gensync": {
2437
+
"version": "1.0.0-beta.2",
2438
+
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
2439
+
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
2440
+
"dev": true,
2441
+
"license": "MIT",
2442
+
"engines": {
2443
+
"node": ">=6.9.0"
2444
+
}
2445
+
},
2446
+
"node_modules/glob-parent": {
2447
+
"version": "6.0.2",
2448
+
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
2449
+
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
2450
+
"dev": true,
2451
+
"license": "ISC",
2452
+
"dependencies": {
2453
+
"is-glob": "^4.0.3"
2454
+
},
2455
+
"engines": {
2456
+
"node": ">=10.13.0"
2457
+
}
2458
+
},
2459
+
"node_modules/globals": {
2460
+
"version": "16.3.0",
2461
+
"resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz",
2462
+
"integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==",
2463
+
"dev": true,
2464
+
"license": "MIT",
2465
+
"engines": {
2466
+
"node": ">=18"
2467
+
},
2468
+
"funding": {
2469
+
"url": "https://github.com/sponsors/sindresorhus"
2470
+
}
2471
+
},
2472
+
"node_modules/graphemer": {
2473
+
"version": "1.4.0",
2474
+
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
2475
+
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
2476
+
"dev": true,
2477
+
"license": "MIT"
2478
+
},
2479
+
"node_modules/has-flag": {
2480
+
"version": "4.0.0",
2481
+
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
2482
+
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
2483
+
"dev": true,
2484
+
"license": "MIT",
2485
+
"engines": {
2486
+
"node": ">=8"
2487
+
}
2488
+
},
2489
+
"node_modules/ignore": {
2490
+
"version": "5.3.2",
2491
+
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
2492
+
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
2493
+
"dev": true,
2494
+
"license": "MIT",
2495
+
"engines": {
2496
+
"node": ">= 4"
2497
+
}
2498
+
},
2499
+
"node_modules/import-fresh": {
2500
+
"version": "3.3.1",
2501
+
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
2502
+
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
2503
+
"dev": true,
2504
+
"license": "MIT",
2505
+
"dependencies": {
2506
+
"parent-module": "^1.0.0",
2507
+
"resolve-from": "^4.0.0"
2508
+
},
2509
+
"engines": {
2510
+
"node": ">=6"
2511
+
},
2512
+
"funding": {
2513
+
"url": "https://github.com/sponsors/sindresorhus"
2514
+
}
2515
+
},
2516
+
"node_modules/imurmurhash": {
2517
+
"version": "0.1.4",
2518
+
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
2519
+
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
2520
+
"dev": true,
2521
+
"license": "MIT",
2522
+
"engines": {
2523
+
"node": ">=0.8.19"
2524
+
}
2525
+
},
2526
+
"node_modules/is-extglob": {
2527
+
"version": "2.1.1",
2528
+
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
2529
+
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
2530
+
"dev": true,
2531
+
"license": "MIT",
2532
+
"engines": {
2533
+
"node": ">=0.10.0"
2534
+
}
2535
+
},
2536
+
"node_modules/is-glob": {
2537
+
"version": "4.0.3",
2538
+
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
2539
+
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
2540
+
"dev": true,
2541
+
"license": "MIT",
2542
+
"dependencies": {
2543
+
"is-extglob": "^2.1.1"
2544
+
},
2545
+
"engines": {
2546
+
"node": ">=0.10.0"
2547
+
}
2548
+
},
2549
+
"node_modules/is-number": {
2550
+
"version": "7.0.0",
2551
+
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
2552
+
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
2553
+
"dev": true,
2554
+
"license": "MIT",
2555
+
"engines": {
2556
+
"node": ">=0.12.0"
2557
+
}
2558
+
},
2559
+
"node_modules/isexe": {
2560
+
"version": "2.0.0",
2561
+
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
2562
+
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
2563
+
"dev": true,
2564
+
"license": "ISC"
2565
+
},
2566
+
"node_modules/js-tokens": {
2567
+
"version": "4.0.0",
2568
+
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
2569
+
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
2570
+
"dev": true,
2571
+
"license": "MIT"
2572
+
},
2573
+
"node_modules/js-yaml": {
2574
+
"version": "4.1.0",
2575
+
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
2576
+
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
2577
+
"dev": true,
2578
+
"license": "MIT",
2579
+
"dependencies": {
2580
+
"argparse": "^2.0.1"
2581
+
},
2582
+
"bin": {
2583
+
"js-yaml": "bin/js-yaml.js"
2584
+
}
2585
+
},
2586
+
"node_modules/jsesc": {
2587
+
"version": "3.1.0",
2588
+
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
2589
+
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
2590
+
"dev": true,
2591
+
"license": "MIT",
2592
+
"bin": {
2593
+
"jsesc": "bin/jsesc"
2594
+
},
2595
+
"engines": {
2596
+
"node": ">=6"
2597
+
}
2598
+
},
2599
+
"node_modules/json-buffer": {
2600
+
"version": "3.0.1",
2601
+
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
2602
+
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
2603
+
"dev": true,
2604
+
"license": "MIT"
2605
+
},
2606
+
"node_modules/json-schema-traverse": {
2607
+
"version": "0.4.1",
2608
+
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
2609
+
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
2610
+
"dev": true,
2611
+
"license": "MIT"
2612
+
},
2613
+
"node_modules/json-stable-stringify-without-jsonify": {
2614
+
"version": "1.0.1",
2615
+
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
2616
+
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
2617
+
"dev": true,
2618
+
"license": "MIT"
2619
+
},
2620
+
"node_modules/json5": {
2621
+
"version": "2.2.3",
2622
+
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
2623
+
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
2624
+
"dev": true,
2625
+
"license": "MIT",
2626
+
"bin": {
2627
+
"json5": "lib/cli.js"
2628
+
},
2629
+
"engines": {
2630
+
"node": ">=6"
2631
+
}
2632
+
},
2633
+
"node_modules/keyv": {
2634
+
"version": "4.5.4",
2635
+
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
2636
+
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
2637
+
"dev": true,
2638
+
"license": "MIT",
2639
+
"dependencies": {
2640
+
"json-buffer": "3.0.1"
2641
+
}
2642
+
},
2643
+
"node_modules/levn": {
2644
+
"version": "0.4.1",
2645
+
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
2646
+
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
2647
+
"dev": true,
2648
+
"license": "MIT",
2649
+
"dependencies": {
2650
+
"prelude-ls": "^1.2.1",
2651
+
"type-check": "~0.4.0"
2652
+
},
2653
+
"engines": {
2654
+
"node": ">= 0.8.0"
2655
+
}
2656
+
},
2657
+
"node_modules/locate-path": {
2658
+
"version": "6.0.0",
2659
+
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
2660
+
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
2661
+
"dev": true,
2662
+
"license": "MIT",
2663
+
"dependencies": {
2664
+
"p-locate": "^5.0.0"
2665
+
},
2666
+
"engines": {
2667
+
"node": ">=10"
2668
+
},
2669
+
"funding": {
2670
+
"url": "https://github.com/sponsors/sindresorhus"
2671
+
}
2672
+
},
2673
+
"node_modules/lodash.merge": {
2674
+
"version": "4.6.2",
2675
+
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
2676
+
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
2677
+
"dev": true,
2678
+
"license": "MIT"
2679
+
},
2680
+
"node_modules/lru-cache": {
2681
+
"version": "5.1.1",
2682
+
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
2683
+
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
2684
+
"dev": true,
2685
+
"license": "ISC",
2686
+
"dependencies": {
2687
+
"yallist": "^3.0.2"
2688
+
}
2689
+
},
2690
+
"node_modules/merge2": {
2691
+
"version": "1.4.1",
2692
+
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
2693
+
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
2694
+
"dev": true,
2695
+
"license": "MIT",
2696
+
"engines": {
2697
+
"node": ">= 8"
2698
+
}
2699
+
},
2700
+
"node_modules/micromatch": {
2701
+
"version": "4.0.8",
2702
+
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
2703
+
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
2704
+
"dev": true,
2705
+
"license": "MIT",
2706
+
"dependencies": {
2707
+
"braces": "^3.0.3",
2708
+
"picomatch": "^2.3.1"
2709
+
},
2710
+
"engines": {
2711
+
"node": ">=8.6"
2712
+
}
2713
+
},
2714
+
"node_modules/minimatch": {
2715
+
"version": "3.1.2",
2716
+
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
2717
+
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
2718
+
"dev": true,
2719
+
"license": "ISC",
2720
+
"dependencies": {
2721
+
"brace-expansion": "^1.1.7"
2722
+
},
2723
+
"engines": {
2724
+
"node": "*"
2725
+
}
2726
+
},
2727
+
"node_modules/ms": {
2728
+
"version": "2.1.3",
2729
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
2730
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
2731
+
"dev": true,
2732
+
"license": "MIT"
2733
+
},
2734
+
"node_modules/nanoid": {
2735
+
"version": "3.3.11",
2736
+
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
2737
+
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
2738
+
"dev": true,
2739
+
"funding": [
2740
+
{
2741
+
"type": "github",
2742
+
"url": "https://github.com/sponsors/ai"
2743
+
}
2744
+
],
2745
+
"license": "MIT",
2746
+
"bin": {
2747
+
"nanoid": "bin/nanoid.cjs"
2748
+
},
2749
+
"engines": {
2750
+
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
2751
+
}
2752
+
},
2753
+
"node_modules/natural-compare": {
2754
+
"version": "1.4.0",
2755
+
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
2756
+
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
2757
+
"dev": true,
2758
+
"license": "MIT"
2759
+
},
2760
+
"node_modules/node-releases": {
2761
+
"version": "2.0.19",
2762
+
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
2763
+
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
2764
+
"dev": true,
2765
+
"license": "MIT"
2766
+
},
2767
+
"node_modules/optionator": {
2768
+
"version": "0.9.4",
2769
+
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
2770
+
"integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
2771
+
"dev": true,
2772
+
"license": "MIT",
2773
+
"dependencies": {
2774
+
"deep-is": "^0.1.3",
2775
+
"fast-levenshtein": "^2.0.6",
2776
+
"levn": "^0.4.1",
2777
+
"prelude-ls": "^1.2.1",
2778
+
"type-check": "^0.4.0",
2779
+
"word-wrap": "^1.2.5"
2780
+
},
2781
+
"engines": {
2782
+
"node": ">= 0.8.0"
2783
+
}
2784
+
},
2785
+
"node_modules/p-limit": {
2786
+
"version": "3.1.0",
2787
+
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
2788
+
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
2789
+
"dev": true,
2790
+
"license": "MIT",
2791
+
"dependencies": {
2792
+
"yocto-queue": "^0.1.0"
2793
+
},
2794
+
"engines": {
2795
+
"node": ">=10"
2796
+
},
2797
+
"funding": {
2798
+
"url": "https://github.com/sponsors/sindresorhus"
2799
+
}
2800
+
},
2801
+
"node_modules/p-locate": {
2802
+
"version": "5.0.0",
2803
+
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
2804
+
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
2805
+
"dev": true,
2806
+
"license": "MIT",
2807
+
"dependencies": {
2808
+
"p-limit": "^3.0.2"
2809
+
},
2810
+
"engines": {
2811
+
"node": ">=10"
2812
+
},
2813
+
"funding": {
2814
+
"url": "https://github.com/sponsors/sindresorhus"
2815
+
}
2816
+
},
2817
+
"node_modules/parent-module": {
2818
+
"version": "1.0.1",
2819
+
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
2820
+
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
2821
+
"dev": true,
2822
+
"license": "MIT",
2823
+
"dependencies": {
2824
+
"callsites": "^3.0.0"
2825
+
},
2826
+
"engines": {
2827
+
"node": ">=6"
2828
+
}
2829
+
},
2830
+
"node_modules/path-exists": {
2831
+
"version": "4.0.0",
2832
+
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
2833
+
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
2834
+
"dev": true,
2835
+
"license": "MIT",
2836
+
"engines": {
2837
+
"node": ">=8"
2838
+
}
2839
+
},
2840
+
"node_modules/path-key": {
2841
+
"version": "3.1.1",
2842
+
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
2843
+
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
2844
+
"dev": true,
2845
+
"license": "MIT",
2846
+
"engines": {
2847
+
"node": ">=8"
2848
+
}
2849
+
},
2850
+
"node_modules/picocolors": {
2851
+
"version": "1.1.1",
2852
+
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
2853
+
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
2854
+
"dev": true,
2855
+
"license": "ISC"
2856
+
},
2857
+
"node_modules/picomatch": {
2858
+
"version": "2.3.1",
2859
+
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
2860
+
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
2861
+
"dev": true,
2862
+
"license": "MIT",
2863
+
"engines": {
2864
+
"node": ">=8.6"
2865
+
},
2866
+
"funding": {
2867
+
"url": "https://github.com/sponsors/jonschlinkert"
2868
+
}
2869
+
},
2870
+
"node_modules/postcss": {
2871
+
"version": "8.5.6",
2872
+
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
2873
+
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
2874
+
"dev": true,
2875
+
"funding": [
2876
+
{
2877
+
"type": "opencollective",
2878
+
"url": "https://opencollective.com/postcss/"
2879
+
},
2880
+
{
2881
+
"type": "tidelift",
2882
+
"url": "https://tidelift.com/funding/github/npm/postcss"
2883
+
},
2884
+
{
2885
+
"type": "github",
2886
+
"url": "https://github.com/sponsors/ai"
2887
+
}
2888
+
],
2889
+
"license": "MIT",
2890
+
"dependencies": {
2891
+
"nanoid": "^3.3.11",
2892
+
"picocolors": "^1.1.1",
2893
+
"source-map-js": "^1.2.1"
2894
+
},
2895
+
"engines": {
2896
+
"node": "^10 || ^12 || >=14"
2897
+
}
2898
+
},
2899
+
"node_modules/prelude-ls": {
2900
+
"version": "1.2.1",
2901
+
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
2902
+
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
2903
+
"dev": true,
2904
+
"license": "MIT",
2905
+
"engines": {
2906
+
"node": ">= 0.8.0"
2907
+
}
2908
+
},
2909
+
"node_modules/punycode": {
2910
+
"version": "2.3.1",
2911
+
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
2912
+
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
2913
+
"dev": true,
2914
+
"license": "MIT",
2915
+
"engines": {
2916
+
"node": ">=6"
2917
+
}
2918
+
},
2919
+
"node_modules/queue-microtask": {
2920
+
"version": "1.2.3",
2921
+
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
2922
+
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
2923
+
"dev": true,
2924
+
"funding": [
2925
+
{
2926
+
"type": "github",
2927
+
"url": "https://github.com/sponsors/feross"
2928
+
},
2929
+
{
2930
+
"type": "patreon",
2931
+
"url": "https://www.patreon.com/feross"
2932
+
},
2933
+
{
2934
+
"type": "consulting",
2935
+
"url": "https://feross.org/support"
2936
+
}
2937
+
],
2938
+
"license": "MIT"
2939
+
},
2940
+
"node_modules/react": {
2941
+
"version": "19.1.0",
2942
+
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
2943
+
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
2944
+
"license": "MIT",
2945
+
"engines": {
2946
+
"node": ">=0.10.0"
2947
+
}
2948
+
},
2949
+
"node_modules/react-dom": {
2950
+
"version": "19.1.0",
2951
+
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
2952
+
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
2953
+
"license": "MIT",
2954
+
"dependencies": {
2955
+
"scheduler": "^0.26.0"
2956
+
},
2957
+
"peerDependencies": {
2958
+
"react": "^19.1.0"
2959
+
}
2960
+
},
2961
+
"node_modules/react-refresh": {
2962
+
"version": "0.17.0",
2963
+
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
2964
+
"integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
2965
+
"dev": true,
2966
+
"license": "MIT",
2967
+
"engines": {
2968
+
"node": ">=0.10.0"
2969
+
}
2970
+
},
2971
+
"node_modules/resolve-from": {
2972
+
"version": "4.0.0",
2973
+
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
2974
+
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
2975
+
"dev": true,
2976
+
"license": "MIT",
2977
+
"engines": {
2978
+
"node": ">=4"
2979
+
}
2980
+
},
2981
+
"node_modules/reusify": {
2982
+
"version": "1.1.0",
2983
+
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
2984
+
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
2985
+
"dev": true,
2986
+
"license": "MIT",
2987
+
"engines": {
2988
+
"iojs": ">=1.0.0",
2989
+
"node": ">=0.10.0"
2990
+
}
2991
+
},
2992
+
"node_modules/rollup": {
2993
+
"version": "4.45.1",
2994
+
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz",
2995
+
"integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==",
2996
+
"dev": true,
2997
+
"license": "MIT",
2998
+
"dependencies": {
2999
+
"@types/estree": "1.0.8"
3000
+
},
3001
+
"bin": {
3002
+
"rollup": "dist/bin/rollup"
3003
+
},
3004
+
"engines": {
3005
+
"node": ">=18.0.0",
3006
+
"npm": ">=8.0.0"
3007
+
},
3008
+
"optionalDependencies": {
3009
+
"@rollup/rollup-android-arm-eabi": "4.45.1",
3010
+
"@rollup/rollup-android-arm64": "4.45.1",
3011
+
"@rollup/rollup-darwin-arm64": "4.45.1",
3012
+
"@rollup/rollup-darwin-x64": "4.45.1",
3013
+
"@rollup/rollup-freebsd-arm64": "4.45.1",
3014
+
"@rollup/rollup-freebsd-x64": "4.45.1",
3015
+
"@rollup/rollup-linux-arm-gnueabihf": "4.45.1",
3016
+
"@rollup/rollup-linux-arm-musleabihf": "4.45.1",
3017
+
"@rollup/rollup-linux-arm64-gnu": "4.45.1",
3018
+
"@rollup/rollup-linux-arm64-musl": "4.45.1",
3019
+
"@rollup/rollup-linux-loongarch64-gnu": "4.45.1",
3020
+
"@rollup/rollup-linux-powerpc64le-gnu": "4.45.1",
3021
+
"@rollup/rollup-linux-riscv64-gnu": "4.45.1",
3022
+
"@rollup/rollup-linux-riscv64-musl": "4.45.1",
3023
+
"@rollup/rollup-linux-s390x-gnu": "4.45.1",
3024
+
"@rollup/rollup-linux-x64-gnu": "4.45.1",
3025
+
"@rollup/rollup-linux-x64-musl": "4.45.1",
3026
+
"@rollup/rollup-win32-arm64-msvc": "4.45.1",
3027
+
"@rollup/rollup-win32-ia32-msvc": "4.45.1",
3028
+
"@rollup/rollup-win32-x64-msvc": "4.45.1",
3029
+
"fsevents": "~2.3.2"
3030
+
}
3031
+
},
3032
+
"node_modules/run-parallel": {
3033
+
"version": "1.2.0",
3034
+
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
3035
+
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
3036
+
"dev": true,
3037
+
"funding": [
3038
+
{
3039
+
"type": "github",
3040
+
"url": "https://github.com/sponsors/feross"
3041
+
},
3042
+
{
3043
+
"type": "patreon",
3044
+
"url": "https://www.patreon.com/feross"
3045
+
},
3046
+
{
3047
+
"type": "consulting",
3048
+
"url": "https://feross.org/support"
3049
+
}
3050
+
],
3051
+
"license": "MIT",
3052
+
"dependencies": {
3053
+
"queue-microtask": "^1.2.2"
3054
+
}
3055
+
},
3056
+
"node_modules/scheduler": {
3057
+
"version": "0.26.0",
3058
+
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
3059
+
"integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
3060
+
"license": "MIT"
3061
+
},
3062
+
"node_modules/semver": {
3063
+
"version": "6.3.1",
3064
+
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
3065
+
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
3066
+
"dev": true,
3067
+
"license": "ISC",
3068
+
"bin": {
3069
+
"semver": "bin/semver.js"
3070
+
}
3071
+
},
3072
+
"node_modules/shebang-command": {
3073
+
"version": "2.0.0",
3074
+
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
3075
+
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
3076
+
"dev": true,
3077
+
"license": "MIT",
3078
+
"dependencies": {
3079
+
"shebang-regex": "^3.0.0"
3080
+
},
3081
+
"engines": {
3082
+
"node": ">=8"
3083
+
}
3084
+
},
3085
+
"node_modules/shebang-regex": {
3086
+
"version": "3.0.0",
3087
+
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
3088
+
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
3089
+
"dev": true,
3090
+
"license": "MIT",
3091
+
"engines": {
3092
+
"node": ">=8"
3093
+
}
3094
+
},
3095
+
"node_modules/source-map-js": {
3096
+
"version": "1.2.1",
3097
+
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
3098
+
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
3099
+
"dev": true,
3100
+
"license": "BSD-3-Clause",
3101
+
"engines": {
3102
+
"node": ">=0.10.0"
3103
+
}
3104
+
},
3105
+
"node_modules/strip-json-comments": {
3106
+
"version": "3.1.1",
3107
+
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
3108
+
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
3109
+
"dev": true,
3110
+
"license": "MIT",
3111
+
"engines": {
3112
+
"node": ">=8"
3113
+
},
3114
+
"funding": {
3115
+
"url": "https://github.com/sponsors/sindresorhus"
3116
+
}
3117
+
},
3118
+
"node_modules/supports-color": {
3119
+
"version": "7.2.0",
3120
+
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
3121
+
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
3122
+
"dev": true,
3123
+
"license": "MIT",
3124
+
"dependencies": {
3125
+
"has-flag": "^4.0.0"
3126
+
},
3127
+
"engines": {
3128
+
"node": ">=8"
3129
+
}
3130
+
},
3131
+
"node_modules/tinyglobby": {
3132
+
"version": "0.2.14",
3133
+
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
3134
+
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
3135
+
"dev": true,
3136
+
"license": "MIT",
3137
+
"dependencies": {
3138
+
"fdir": "^6.4.4",
3139
+
"picomatch": "^4.0.2"
3140
+
},
3141
+
"engines": {
3142
+
"node": ">=12.0.0"
3143
+
},
3144
+
"funding": {
3145
+
"url": "https://github.com/sponsors/SuperchupuDev"
3146
+
}
3147
+
},
3148
+
"node_modules/tinyglobby/node_modules/fdir": {
3149
+
"version": "6.4.6",
3150
+
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
3151
+
"integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
3152
+
"dev": true,
3153
+
"license": "MIT",
3154
+
"peerDependencies": {
3155
+
"picomatch": "^3 || ^4"
3156
+
},
3157
+
"peerDependenciesMeta": {
3158
+
"picomatch": {
3159
+
"optional": true
3160
+
}
3161
+
}
3162
+
},
3163
+
"node_modules/tinyglobby/node_modules/picomatch": {
3164
+
"version": "4.0.3",
3165
+
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
3166
+
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
3167
+
"dev": true,
3168
+
"license": "MIT",
3169
+
"engines": {
3170
+
"node": ">=12"
3171
+
},
3172
+
"funding": {
3173
+
"url": "https://github.com/sponsors/jonschlinkert"
3174
+
}
3175
+
},
3176
+
"node_modules/to-regex-range": {
3177
+
"version": "5.0.1",
3178
+
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
3179
+
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
3180
+
"dev": true,
3181
+
"license": "MIT",
3182
+
"dependencies": {
3183
+
"is-number": "^7.0.0"
3184
+
},
3185
+
"engines": {
3186
+
"node": ">=8.0"
3187
+
}
3188
+
},
3189
+
"node_modules/ts-api-utils": {
3190
+
"version": "2.1.0",
3191
+
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
3192
+
"integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
3193
+
"dev": true,
3194
+
"license": "MIT",
3195
+
"engines": {
3196
+
"node": ">=18.12"
3197
+
},
3198
+
"peerDependencies": {
3199
+
"typescript": ">=4.8.4"
3200
+
}
3201
+
},
3202
+
"node_modules/type-check": {
3203
+
"version": "0.4.0",
3204
+
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
3205
+
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
3206
+
"dev": true,
3207
+
"license": "MIT",
3208
+
"dependencies": {
3209
+
"prelude-ls": "^1.2.1"
3210
+
},
3211
+
"engines": {
3212
+
"node": ">= 0.8.0"
3213
+
}
3214
+
},
3215
+
"node_modules/typescript": {
3216
+
"version": "5.8.3",
3217
+
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
3218
+
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
3219
+
"dev": true,
3220
+
"license": "Apache-2.0",
3221
+
"bin": {
3222
+
"tsc": "bin/tsc",
3223
+
"tsserver": "bin/tsserver"
3224
+
},
3225
+
"engines": {
3226
+
"node": ">=14.17"
3227
+
}
3228
+
},
3229
+
"node_modules/typescript-eslint": {
3230
+
"version": "8.38.0",
3231
+
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz",
3232
+
"integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==",
3233
+
"dev": true,
3234
+
"license": "MIT",
3235
+
"dependencies": {
3236
+
"@typescript-eslint/eslint-plugin": "8.38.0",
3237
+
"@typescript-eslint/parser": "8.38.0",
3238
+
"@typescript-eslint/typescript-estree": "8.38.0",
3239
+
"@typescript-eslint/utils": "8.38.0"
3240
+
},
3241
+
"engines": {
3242
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
3243
+
},
3244
+
"funding": {
3245
+
"type": "opencollective",
3246
+
"url": "https://opencollective.com/typescript-eslint"
3247
+
},
3248
+
"peerDependencies": {
3249
+
"eslint": "^8.57.0 || ^9.0.0",
3250
+
"typescript": ">=4.8.4 <5.9.0"
3251
+
}
3252
+
},
3253
+
"node_modules/update-browserslist-db": {
3254
+
"version": "1.1.3",
3255
+
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
3256
+
"integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
3257
+
"dev": true,
3258
+
"funding": [
3259
+
{
3260
+
"type": "opencollective",
3261
+
"url": "https://opencollective.com/browserslist"
3262
+
},
3263
+
{
3264
+
"type": "tidelift",
3265
+
"url": "https://tidelift.com/funding/github/npm/browserslist"
3266
+
},
3267
+
{
3268
+
"type": "github",
3269
+
"url": "https://github.com/sponsors/ai"
3270
+
}
3271
+
],
3272
+
"license": "MIT",
3273
+
"dependencies": {
3274
+
"escalade": "^3.2.0",
3275
+
"picocolors": "^1.1.1"
3276
+
},
3277
+
"bin": {
3278
+
"update-browserslist-db": "cli.js"
3279
+
},
3280
+
"peerDependencies": {
3281
+
"browserslist": ">= 4.21.0"
3282
+
}
3283
+
},
3284
+
"node_modules/uri-js": {
3285
+
"version": "4.4.1",
3286
+
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
3287
+
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
3288
+
"dev": true,
3289
+
"license": "BSD-2-Clause",
3290
+
"dependencies": {
3291
+
"punycode": "^2.1.0"
3292
+
}
3293
+
},
3294
+
"node_modules/vite": {
3295
+
"version": "7.0.6",
3296
+
"resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz",
3297
+
"integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==",
3298
+
"dev": true,
3299
+
"license": "MIT",
3300
+
"dependencies": {
3301
+
"esbuild": "^0.25.0",
3302
+
"fdir": "^6.4.6",
3303
+
"picomatch": "^4.0.3",
3304
+
"postcss": "^8.5.6",
3305
+
"rollup": "^4.40.0",
3306
+
"tinyglobby": "^0.2.14"
3307
+
},
3308
+
"bin": {
3309
+
"vite": "bin/vite.js"
3310
+
},
3311
+
"engines": {
3312
+
"node": "^20.19.0 || >=22.12.0"
3313
+
},
3314
+
"funding": {
3315
+
"url": "https://github.com/vitejs/vite?sponsor=1"
3316
+
},
3317
+
"optionalDependencies": {
3318
+
"fsevents": "~2.3.3"
3319
+
},
3320
+
"peerDependencies": {
3321
+
"@types/node": "^20.19.0 || >=22.12.0",
3322
+
"jiti": ">=1.21.0",
3323
+
"less": "^4.0.0",
3324
+
"lightningcss": "^1.21.0",
3325
+
"sass": "^1.70.0",
3326
+
"sass-embedded": "^1.70.0",
3327
+
"stylus": ">=0.54.8",
3328
+
"sugarss": "^5.0.0",
3329
+
"terser": "^5.16.0",
3330
+
"tsx": "^4.8.1",
3331
+
"yaml": "^2.4.2"
3332
+
},
3333
+
"peerDependenciesMeta": {
3334
+
"@types/node": {
3335
+
"optional": true
3336
+
},
3337
+
"jiti": {
3338
+
"optional": true
3339
+
},
3340
+
"less": {
3341
+
"optional": true
3342
+
},
3343
+
"lightningcss": {
3344
+
"optional": true
3345
+
},
3346
+
"sass": {
3347
+
"optional": true
3348
+
},
3349
+
"sass-embedded": {
3350
+
"optional": true
3351
+
},
3352
+
"stylus": {
3353
+
"optional": true
3354
+
},
3355
+
"sugarss": {
3356
+
"optional": true
3357
+
},
3358
+
"terser": {
3359
+
"optional": true
3360
+
},
3361
+
"tsx": {
3362
+
"optional": true
3363
+
},
3364
+
"yaml": {
3365
+
"optional": true
3366
+
}
3367
+
}
3368
+
},
3369
+
"node_modules/vite/node_modules/fdir": {
3370
+
"version": "6.4.6",
3371
+
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
3372
+
"integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
3373
+
"dev": true,
3374
+
"license": "MIT",
3375
+
"peerDependencies": {
3376
+
"picomatch": "^3 || ^4"
3377
+
},
3378
+
"peerDependenciesMeta": {
3379
+
"picomatch": {
3380
+
"optional": true
3381
+
}
3382
+
}
3383
+
},
3384
+
"node_modules/vite/node_modules/picomatch": {
3385
+
"version": "4.0.3",
3386
+
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
3387
+
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
3388
+
"dev": true,
3389
+
"license": "MIT",
3390
+
"engines": {
3391
+
"node": ">=12"
3392
+
},
3393
+
"funding": {
3394
+
"url": "https://github.com/sponsors/jonschlinkert"
3395
+
}
3396
+
},
3397
+
"node_modules/which": {
3398
+
"version": "2.0.2",
3399
+
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
3400
+
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
3401
+
"dev": true,
3402
+
"license": "ISC",
3403
+
"dependencies": {
3404
+
"isexe": "^2.0.0"
3405
+
},
3406
+
"bin": {
3407
+
"node-which": "bin/node-which"
3408
+
},
3409
+
"engines": {
3410
+
"node": ">= 8"
3411
+
}
3412
+
},
3413
+
"node_modules/word-wrap": {
3414
+
"version": "1.2.5",
3415
+
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
3416
+
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
3417
+
"dev": true,
3418
+
"license": "MIT",
3419
+
"engines": {
3420
+
"node": ">=0.10.0"
3421
+
}
3422
+
},
3423
+
"node_modules/yallist": {
3424
+
"version": "3.1.1",
3425
+
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
3426
+
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
3427
+
"dev": true,
3428
+
"license": "ISC"
3429
+
},
3430
+
"node_modules/yocto-queue": {
3431
+
"version": "0.1.0",
3432
+
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
3433
+
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
3434
+
"dev": true,
3435
+
"license": "MIT",
3436
+
"engines": {
3437
+
"node": ">=10"
3438
+
},
3439
+
"funding": {
3440
+
"url": "https://github.com/sponsors/sindresorhus"
3441
+
}
3442
+
}
3443
+
}
3444
+
}
+32
live-embed/package.json
+32
live-embed/package.json
···
1
+
{
2
+
"name": "live-embed",
3
+
"private": true,
4
+
"version": "0.0.0",
5
+
"type": "module",
6
+
"scripts": {
7
+
"dev": "vite",
8
+
"build": "tsc -b && vite build",
9
+
"bsky": "vite build --base=/zero-bluesky-realtime-embed/",
10
+
"lint": "eslint .",
11
+
"preview": "vite preview"
12
+
},
13
+
"dependencies": {
14
+
"@atcute/client": "^4.0.3",
15
+
"@atcute/identity-resolver": "^1.1.3",
16
+
"react": "^19.1.0",
17
+
"react-dom": "^19.1.0"
18
+
},
19
+
"devDependencies": {
20
+
"@eslint/js": "^9.30.1",
21
+
"@types/react": "^19.1.8",
22
+
"@types/react-dom": "^19.1.6",
23
+
"@vitejs/plugin-react": "^4.6.0",
24
+
"eslint": "^9.30.1",
25
+
"eslint-plugin-react-hooks": "^5.2.0",
26
+
"eslint-plugin-react-refresh": "^0.4.20",
27
+
"globals": "^16.3.0",
28
+
"typescript": "~5.8.3",
29
+
"typescript-eslint": "^8.35.1",
30
+
"vite": "^7.0.4"
31
+
}
32
+
}
live-embed/public/.gitkeep
live-embed/public/.gitkeep
This is a binary file and will not be displayed.
+22
live-embed/src/App.css
+22
live-embed/src/App.css
···
1
+
#root {
2
+
max-width: 51em;
3
+
margin: 0 auto;
4
+
padding: 2rem;
5
+
text-align: center;
6
+
}
7
+
8
+
.with {
9
+
margin: 0.5em 0 1.5em;
10
+
font-size: 1.2em;
11
+
}
12
+
13
+
.posts {
14
+
display: flex;
15
+
flex-wrap: wrap;
16
+
gap: 1rem;
17
+
justify-content: center;
18
+
}
19
+
20
+
.explain {
21
+
text-align: left;
22
+
}
+76
live-embed/src/App.tsx
+76
live-embed/src/App.tsx
···
1
+
import { useEffect, useState } from 'react';
2
+
import { Post } from './Post';
3
+
import { rotatingPair } from './samplePosts';
4
+
import { Spacedust } from './spacedust';
5
+
6
+
import './App.css'
7
+
8
+
function App() {
9
+
const [currentPair, setCurrentPair] = useState([]);
10
+
const [updates, setUpdates] = useState({});
11
+
12
+
useEffect(() => {
13
+
let iMightBeAZombie = false;
14
+
15
+
const spacedust = new Spacedust(handleLink);
16
+
const cancelRotation = rotatingPair(updatedPair => {
17
+
if (iMightBeAZombie) return;
18
+
19
+
setCurrentPair(updatedPair);
20
+
spacedust.setSubjects(updatedPair);
21
+
setUpdates(current => { // cleanup, probably should combine with pair directly
22
+
const next = {};
23
+
updatedPair.forEach(uri => next[uri] = current[uri] ?? {});
24
+
return next;
25
+
});
26
+
});
27
+
28
+
function handleLink({ subject, source }) {
29
+
setUpdates(current => ({
30
+
...current,
31
+
[subject]: {
32
+
...current[subject],
33
+
[source]: (current[subject][source] ?? 0) + 1
34
+
},
35
+
}));
36
+
}
37
+
38
+
return () => {
39
+
cancelRotation();
40
+
spacedust.close();
41
+
iMightBeAZombie = true;
42
+
};
43
+
}, []);
44
+
45
+
return (
46
+
<>
47
+
<h1>zero bluesky infra post rendering (WIP)</h1>
48
+
<p className="with">with real-time interaction count updates</p>
49
+
<div className="posts">
50
+
{currentPair.map(p => (
51
+
<Post key={p} atUri={p} updatedLinks={updates[p]} />
52
+
))}
53
+
</div>
54
+
55
+
<div className="explain">
56
+
<h2>How does it work?</h2>
57
+
<ul>
58
+
<li><strong>Post content</strong>: fetches direct from PDS with <a href="https://github.com/mary-ext/atcute" target="_blank">atcute</a>.</li>
59
+
<li><strong>Interaction counts</strong>: queries <a href="https://constellation.microcosm.blue/" target="_blank">constellation</a>.</li>
60
+
<li><strong>Interaction updates</strong>: subscribes to <a href="https://spacedust.microcosm.blue/" target="_blank">spacedust</a>.</li>
61
+
<li>There is no backend.</li>
62
+
</ul>
63
+
<p>The post selection takes a couple top posts from the public bluesky Discover feed so I guess it's kind of cheating but hey. And media files load from Bluesky's CDN (or, will soon) so that's also cheating.</p>
64
+
65
+
<p>Source code is on <a href="https://tangled.sh/@bad-example.com/spacedust-utils/tree/main/live-embed" target="_blank">tangled</a>. Still a work in progress.</p>
66
+
67
+
<h2>PS</h2>
68
+
<p>If you actually want to embed a post on a web page, check out <a href="https://mary-ext.codeberg.page/bluesky-embed/" target="_blank"><code><bluesky-embed></code></a> from <a href="https://mary.my.id" target="_blank">mary</a>. It's a very solid post renderer, unlike this demo.</p>
69
+
70
+
</div>
71
+
72
+
</>
73
+
)
74
+
}
75
+
76
+
export default App
+67
live-embed/src/Fetch.tsx
+67
live-embed/src/Fetch.tsx
···
1
+
import { useContext, useEffect, useState } from 'react';
2
+
3
+
const loadingDefault = () => (
4
+
<em>Loading…</em>
5
+
);
6
+
7
+
const errorDefault = err => (
8
+
<span className="error">
9
+
<strong>Error</strong>:<br/>{`${err}`}
10
+
</span>
11
+
);
12
+
13
+
export function Fetch({ using, args, ok, loading, error }) {
14
+
const [asyncData, setAsyncData] = useState({ state: null });
15
+
16
+
useEffect(() => {
17
+
let ignore = false;
18
+
setAsyncData({ state: 'loading' });
19
+
(async () => {
20
+
try {
21
+
const data = await using(...args);
22
+
!ignore && setAsyncData({ state: 'done', data });
23
+
} catch (err) {
24
+
!ignore && setAsyncData({ state: 'error', err });
25
+
}
26
+
})();
27
+
return () => { ignore = true; }
28
+
}, args);
29
+
30
+
if (asyncData.state === 'loading') {
31
+
return (loading || loadingDefault)(...args);
32
+
} else if (asyncData.state === 'error') {
33
+
return (error || errorDefault)(asyncData.err);
34
+
} else if (asyncData.state === null) {
35
+
return <span>wat, request has not started (bug?)</span>;
36
+
} else {
37
+
if (asyncData.state !== 'done') { console.warn(`unexpected async data state: ${asyncData.state}`); }
38
+
return ok(asyncData.data);
39
+
}
40
+
}
41
+
42
+
/////
43
+
44
+
async function getJson(url, credentials) {
45
+
const opts = {};
46
+
if (credentials) opts.credentials = 'include';
47
+
const res = await fetch(url, opts);
48
+
if (!res.ok) {
49
+
const m = await res.text();
50
+
throw new Error(`Failed to fetch: ${m}`);
51
+
}
52
+
return await res.json();
53
+
}
54
+
55
+
export function GetJson({ url, params, credentials, ...forFetch }) {
56
+
const u = new URL(url);
57
+
for (let [key, val] of Object.entries(params ?? {})) {
58
+
u.searchParams.append(key, val);
59
+
}
60
+
return (
61
+
<Fetch
62
+
using={getJson}
63
+
args={[u.toString(), credentials]}
64
+
{...forFetch}
65
+
/>
66
+
);
67
+
}
+42
live-embed/src/Post.css
+42
live-embed/src/Post.css
···
1
+
.post {
2
+
background-color: rgb(32, 41, 53);
3
+
border: 1px solid rgb(46, 64, 82);
4
+
border-radius: 0.1em;
5
+
display: flex;
6
+
flex-basis: 20em;
7
+
flex-direction: column;
8
+
min-width: 12em;
9
+
max-width: 24em;
10
+
}
11
+
12
+
.record-contents {
13
+
flex-grow: 1;
14
+
padding: 1em;
15
+
text-align: left;
16
+
align-content: center;
17
+
}
18
+
19
+
.stats {
20
+
border-top: 0.5px solid rgb(46, 64, 82);
21
+
display: flex;
22
+
justify-content: space-around;
23
+
margin: 0;
24
+
gap: 1em;
25
+
padding: 0 1em;
26
+
opacity: 0.667;
27
+
}
28
+
.stat.reply:before {
29
+
content: "๐ซง ";
30
+
}
31
+
.stat.repost:before {
32
+
content: "โป ";
33
+
}
34
+
.stat.like:before {
35
+
content: "โก ";
36
+
}
37
+
38
+
@media (prefers-color-scheme: light) {
39
+
.post {
40
+
background-color: rgb(241, 243, 245);
41
+
}
42
+
}
+68
live-embed/src/Post.tsx
+68
live-embed/src/Post.tsx
···
1
+
import { useEffect, useState } from 'react';
2
+
import { getPostStats } from './constellation';
3
+
import { getAtUri } from './getPost';
4
+
import linkSources from './linkSources';
5
+
6
+
import './Post.css';
7
+
8
+
export function Post({ atUri, updatedLinks }) {
9
+
const [record, setRecord] = useState({ state: 'loading' });
10
+
const [baseStats, setBaseStats] = useState({});
11
+
const liveStats = { ...baseStats };
12
+
13
+
for (const [key, val] of Object.entries(updatedLinks)) {
14
+
const name = linkSources[key];
15
+
if (!liveStats[name]) liveStats[name] = 0;
16
+
liveStats[name] += val;
17
+
}
18
+
19
+
useEffect(() => {
20
+
let alive = true;
21
+
22
+
getAtUri(atUri).then(
23
+
record => alive && setRecord({ state: 'loaded', record }),
24
+
error => alive && setRecord({ state: 'failed', error }));
25
+
26
+
getPostStats(atUri).then(
27
+
stats => alive && setBaseStats(stats),
28
+
e => console.warn('fetching base stats failed', e));
29
+
30
+
return () => alive = false;
31
+
}, [atUri]);
32
+
33
+
return (
34
+
<div className="post">
35
+
{record.state === 'loading'
36
+
? <p className="loading">loading post…</p>
37
+
: record.state === 'failed'
38
+
? <p className="failed">failed to load post :/ {`${record.error}`}</p>
39
+
: <RecordContents data={record.record} />
40
+
}
41
+
<p className="stats">
42
+
{liveStats.reply && (
43
+
<span className="stat reply" title="total replies">
44
+
{liveStats.reply.toLocaleString()}
45
+
</span>
46
+
)}
47
+
{liveStats.repost && (
48
+
<span className="stat repost" title="total reposts">
49
+
{liveStats.repost.toLocaleString()}
50
+
</span>
51
+
)}
52
+
{liveStats.like && (
53
+
<span className="stat like" title="total likes">
54
+
{liveStats.like.toLocaleString()}
55
+
</span>
56
+
)}
57
+
</p>
58
+
</div>
59
+
);
60
+
}
61
+
62
+
function RecordContents({ data }) {
63
+
return (
64
+
<div className="record-contents">
65
+
{data.text}
66
+
</div>
67
+
);
68
+
}
live-embed/src/assets/.gitkeep
live-embed/src/assets/.gitkeep
This is a binary file and will not be displayed.
+39
live-embed/src/constellation.ts
+39
live-embed/src/constellation.ts
···
1
+
import linkSources from './linkSources';
2
+
3
+
/**
4
+
* get nice historical counts from constellation
5
+
*
6
+
* constellation's api still uses separated collection/path sources, and
7
+
* likes should be distinct where everything else is record counts.
8
+
*
9
+
* constellation still can only specify one link source per request or /all
10
+
*
11
+
* handles stuff like that
12
+
**/
13
+
export async function getPostStats(
14
+
atUri: string,
15
+
endpoint: string = 'https://constellation.microcosm.blue'
16
+
) {
17
+
const url = new URL('/links/all', endpoint);
18
+
url.searchParams.set('target', atUri);
19
+
const res = await fetch(url, { signal: AbortSignal.timeout(4000) });
20
+
if (!res.ok) throw new Error(res);
21
+
const { links } = await res.json();
22
+
23
+
const niceLinks = {};
24
+
25
+
for (const [collection, paths] of Object.entries(links)) {
26
+
for (const [oldStylePath, counts] of Object.entries(paths)) {
27
+
const newStylePath = `${collection}:${oldStylePath.slice(1)}`;
28
+
const name = linkSources[newStylePath];
29
+
if (!name) continue; // perils of constellation's (soon-deprecated) /all
30
+
if (name === 'like') {
31
+
niceLinks[name] = counts.distinct_dids;
32
+
} else {
33
+
niceLinks[name] = counts.records;
34
+
}
35
+
}
36
+
}
37
+
38
+
return niceLinks;
39
+
}
+73
live-embed/src/getPost.ts
+73
live-embed/src/getPost.ts
···
1
+
// heyyyyyy if you're reading this:
2
+
//
3
+
// it's all copy-pasta from the atproto notifications demo, pasted over from
4
+
// previous experimental stuff.
5
+
//
6
+
// all to say it probably needs some attention and cleanup, in particular with
7
+
// handling failures. i'll get back to it hopefully soon.
8
+
9
+
import { Client, CredentialManager, ok, simpleFetchHandler } from '@atcute/client';
10
+
import { CompositeDidDocumentResolver, PlcDidDocumentResolver, WebDidDocumentResolver } from '@atcute/identity-resolver';
11
+
12
+
const docResolver = new CompositeDidDocumentResolver({
13
+
methods: {
14
+
plc: new PlcDidDocumentResolver(),
15
+
web: new WebDidDocumentResolver(),
16
+
},
17
+
});
18
+
19
+
async function resolve_did(did) {
20
+
return await docResolver.resolve(did);
21
+
}
22
+
23
+
function pds({ service }) {
24
+
if (!service) {
25
+
throw new Error('missing service from identity doc');
26
+
}
27
+
const { serviceEndpoint } = service[0];
28
+
if (!serviceEndpoint) {
29
+
throw new Error('missing serviceEndpoint from identity service array');
30
+
}
31
+
return serviceEndpoint;
32
+
}
33
+
34
+
35
+
async function get_pds_record(endpoint, did, collection, rkey) {
36
+
const handler = simpleFetchHandler({ service: endpoint });
37
+
const rpc = new Client({ handler });
38
+
const { ok, data } = await rpc.get('com.atproto.repo.getRecord', {
39
+
params: { repo: did, collection, rkey },
40
+
});
41
+
if (!ok) throw new Error('fetching pds record failed');
42
+
return data;
43
+
}
44
+
45
+
function parse_at_uri(uri) {
46
+
let collection, rkey;
47
+
if (!uri.startsWith('at://')) {
48
+
throw new Error('invalid at-uri: did not start with "at://"');
49
+
}
50
+
let remaining = uri.slice('at://'.length); // remove the at:// prefix
51
+
remaining = remaining.split('#')[0]; // hash is valid in at-uri but we don't handle them
52
+
remaining = remaining.split('?')[0]; // query is valid in at-uri but we don't handle it
53
+
const segments = remaining.split('/');
54
+
if (segments.length === 0) {
55
+
throw new Error('invalid at-uri: could not find did after "at://"');
56
+
}
57
+
const did = segments[0];
58
+
if (segments.length > 1) {
59
+
collection = segments[1];
60
+
}
61
+
if (segments.length > 2) {
62
+
rkey = segments.slice(2).join('/'); // hmm are slashes actually valid in rkey?
63
+
}
64
+
return { did, collection, rkey };
65
+
}
66
+
67
+
export async function getAtUri(atUri) {
68
+
const { did, collection, rkey } = parse_at_uri(atUri);
69
+
const doc = await resolve_did(did);
70
+
const endpoint = pds(doc);
71
+
const { value } = await get_pds_record(endpoint, did, collection, rkey);
72
+
return value;
73
+
}
+73
live-embed/src/index.css
+73
live-embed/src/index.css
···
1
+
:root {
2
+
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
3
+
line-height: 1.5;
4
+
font-weight: 400;
5
+
6
+
color-scheme: light dark;
7
+
color: rgba(255, 255, 255, 0.87);
8
+
background-color: rgb(24, 30, 38);
9
+
10
+
font-synthesis: none;
11
+
text-rendering: optimizeLegibility;
12
+
-webkit-font-smoothing: antialiased;
13
+
-moz-osx-font-smoothing: grayscale;
14
+
}
15
+
16
+
a {
17
+
font-weight: 500;
18
+
color: #646cff;
19
+
text-decoration: inherit;
20
+
}
21
+
a:hover {
22
+
color: #535bf2;
23
+
}
24
+
25
+
body {
26
+
margin: 0;
27
+
display: flex;
28
+
place-items: center;
29
+
min-width: 320px;
30
+
min-height: 100vh;
31
+
}
32
+
33
+
h1 {
34
+
font-size: 2em;
35
+
line-height: 1.1;
36
+
margin: 0;
37
+
}
38
+
39
+
h2 {
40
+
margin-top: 2em;
41
+
}
42
+
43
+
button {
44
+
border-radius: 8px;
45
+
border: 1px solid transparent;
46
+
padding: 0.6em 1.2em;
47
+
font-size: 1em;
48
+
font-weight: 500;
49
+
font-family: inherit;
50
+
background-color: #1a1a1a;
51
+
cursor: pointer;
52
+
transition: border-color 0.25s;
53
+
}
54
+
button:hover {
55
+
border-color: #646cff;
56
+
}
57
+
button:focus,
58
+
button:focus-visible {
59
+
outline: 4px auto -webkit-focus-ring-color;
60
+
}
61
+
62
+
@media (prefers-color-scheme: light) {
63
+
:root {
64
+
color: #213547;
65
+
background-color: #ffffff;
66
+
}
67
+
a:hover {
68
+
color: #747bff;
69
+
}
70
+
button {
71
+
background-color: #f9f9f9;
72
+
}
73
+
}
+9
live-embed/src/linkSources.ts
+9
live-embed/src/linkSources.ts
···
1
+
const linkSources = {
2
+
'app.bsky.feed.like:subject.uri': 'like',
3
+
'app.bsky.feed.repost:subject.uri': 'repost', // actual repost
4
+
'app.bsky.feed.post:embed.record.uri': 'repost', // normal quote (grouped for count)
5
+
'app.bsky.feed.post:embed.record.record.uri': 'repost', // RecordWithMedia quote (grouped for count)
6
+
'app.bsky.feed.post:reply.root.uri': 'reply', // root: count all descendent replies
7
+
};
8
+
9
+
export { linkSources as default };
+10
live-embed/src/main.tsx
+10
live-embed/src/main.tsx
+85
live-embed/src/samplePosts.ts
+85
live-embed/src/samplePosts.ts
···
1
+
import { getPostStats } from './constellation';
2
+
3
+
const SEKELETON_API = 'https://discover.bsky.app/xrpc/app.bsky.feed.getFeedSkeleton';
4
+
const FEED = 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot';
5
+
const POLL_DELAY = 9000;
6
+
const POST_LIMIT = 5;
7
+
8
+
async function getFeed() {
9
+
const url = new URL(SEKELETON_API);
10
+
url.searchParams.append('feed', FEED);
11
+
url.searchParams.append('limit', POST_LIMIT.toString());
12
+
const res = await fetch(url);
13
+
const data = await res.json();
14
+
return data;
15
+
}
16
+
17
+
/**
18
+
* fetch a pair of posts from discover and alternately replace the first/second
19
+
*
20
+
* TODO: check w constellation to prioritize popular posts
21
+
**/
22
+
export function rotatingPair(onRotate: any) {
23
+
let timer: number;
24
+
let dying = false;
25
+
const seen = new Set(); // TODO: mem leak, slowly
26
+
let A: string | null = null;
27
+
let B: string | null = null;
28
+
let which: 'A' | 'B' = 'A';
29
+
30
+
async function next() {
31
+
console.info('[sample posts: next]');
32
+
try {
33
+
const { feed } = await getFeed();
34
+
if (dying) return;
35
+
36
+
const withStats = await Promise.all(feed.map(async ({ post }) => {
37
+
if (seen.has(post)) return { post, total: 0 };
38
+
let stats = {};
39
+
try {
40
+
stats = await getPostStats(post);
41
+
} catch (e) {
42
+
console.warn('failed to get stats from constellation', e);
43
+
}
44
+
const total = Array.from(Object.values(stats)).reduce((a, b) => a + b, 0);
45
+
return ({ post, total })
46
+
}))
47
+
if (dying) return;
48
+
49
+
// idk if sorting by most interactions yields more-interactive posts but eh
50
+
withStats.sort(({ total: a }, { total: b }) => b - a);
51
+
52
+
// special case: first load
53
+
if (A === null && B === null) {
54
+
if (withStats.length < 2) throw new Error('withStats returned fewer than two posts to start');
55
+
seen.add(A = withStats[0].post);
56
+
seen.add(B = withStats[1].post);
57
+
} else {
58
+
for (const { post } of withStats) {
59
+
if (seen.has(post)) {
60
+
continue;
61
+
}
62
+
if (which === 'A') {
63
+
seen.add(B = post);
64
+
which = 'B';
65
+
} else {
66
+
seen.add(A = post);
67
+
which = 'A';
68
+
}
69
+
break;
70
+
}
71
+
}
72
+
onRotate([A, B]);
73
+
} catch (e) {
74
+
console.error('hmm, failed to get withStats', e);
75
+
}
76
+
timer = setTimeout(next, POLL_DELAY);
77
+
}
78
+
setTimeout(next);
79
+
80
+
return () => {
81
+
console.log('clearing');
82
+
clearTimeout(timer);
83
+
dying = true;
84
+
}
85
+
}
+160
live-embed/src/spacedust.ts
+160
live-embed/src/spacedust.ts
···
1
+
import linkSources from './linkSources';
2
+
3
+
type SpacedustStatus = 'disconnected' | 'connecting' | 'connected';
4
+
5
+
type Linkrement = { // this name should send me to jail
6
+
subject: String // at-uri
7
+
source: String // link source
8
+
};
9
+
type LinkHandler = (l: Linkrement) => void;
10
+
11
+
/**
12
+
* simple spacedust demo client
13
+
*
14
+
* only purpose for now is to serve this demo so it might contain hacks
15
+
**/
16
+
export class Spacedust {
17
+
#callback: LinkHandler;
18
+
#endpoint: string;
19
+
20
+
// #socket must be null when #status is 'disconnected'
21
+
#socket: WebSocket | null = null;
22
+
#status: SpacedustStatus = 'disconnected';
23
+
24
+
#subjects: string[];
25
+
#subjectsDirty: boolean = false; // in case we try to update while disconnected
26
+
#sources: string[] = Object.keys(linkSources); // hard-coding for demo
27
+
#eol: boolean = false; // flag: we should shut down
28
+
29
+
constructor(
30
+
onLink: LinkHandler,
31
+
endpoint: string = 'https://spacedust.microcosm.blue',
32
+
subjects: string[] = [],
33
+
) {
34
+
this.#callback = onLink;
35
+
this.#endpoint = endpoint;
36
+
this.#subjects = subjects;
37
+
this.#connect();
38
+
}
39
+
40
+
async #connect(reconnecting: boolean = false) {
41
+
this.#status = 'connecting';
42
+
43
+
if (reconnecting) {
44
+
const wait = Math.round(1000 + (Math.random() * 1800));
45
+
console.info(`waiting ${(wait / 1000).toFixed(1)}s to reconnect...`);
46
+
await new Promise(r => setTimeout(r, wait));
47
+
}
48
+
if (this.#eol) return this.close();
49
+
50
+
// up to date as of this connection init
51
+
this.#subjectsDirty = false;
52
+
53
+
if (this.#subjects.length === 0) {
54
+
console.info('no subjects, not connecting spacedust to avoid getting firehosed');
55
+
this.#status = 'disconnected';
56
+
return;
57
+
}
58
+
59
+
const url = new URL('/subscribe', this.#endpoint);
60
+
url.searchParams.set('instant', 'true');
61
+
62
+
for (const source of this.#sources) {
63
+
url.searchParams.append('wantedSources', source);
64
+
}
65
+
66
+
// note: here we put all subjects in the url
67
+
// that's fine since we only have a few for this demo
68
+
// but spacedust accepts up to 50,000 subjects! more than fit in a url-- you
69
+
// have to send a subscriber sourced message in that case after reconnect.
70
+
for (const subject of this.#subjects) {
71
+
url.searchParams.append('wantedSubjects', subject);
72
+
}
73
+
74
+
this.#socket = new WebSocket(url);
75
+
76
+
this.#socket.onopen = () => {
77
+
console.info('spacedust connected.');
78
+
if (this.#eol) return this.close();
79
+
this.#status = 'connected';
80
+
// in case the subjects were changed while connecting
81
+
if (this.#subjectsDirty) {
82
+
this.setSubjects(this.#subjects);
83
+
}
84
+
};
85
+
86
+
this.#socket.onmessage = message => {
87
+
if (this.#eol) return this.close();
88
+
this.#handleMessage(message);
89
+
};
90
+
91
+
this.#socket.onerror = err => {
92
+
console.warn('spacedust socket errored. reconnecting...', err);
93
+
this.#status = 'disconnected';
94
+
this.#connect(true);
95
+
};
96
+
97
+
this.#socket.onclose = () => {
98
+
if (this.#eol) {
99
+
console.info('spacedust socket closed and we\'re EOL, not restarting');
100
+
return;
101
+
}
102
+
console.info('spacedust socket closed. restarting...');
103
+
this.#status = 'disconnected';
104
+
this.#connect(true);
105
+
};
106
+
}
107
+
108
+
#handleMessage(m: MessageEvent) {
109
+
if (this.#eol) return;
110
+
const data = JSON.parse(m.data);
111
+
if (data.kind !== "link" || data.link.operation !== "create") {
112
+
console.info('ignoring non-link-create event', data);
113
+
return;
114
+
}
115
+
const { link: { subject, source } } = data;
116
+
this.#callback({ subject, source });
117
+
}
118
+
119
+
setSubjects(newSubjects: string[]) {
120
+
this.#subjects = newSubjects;
121
+
122
+
if (this.#subjects.length === 0) {
123
+
// no subjects specified: just disconnect (would get firehose from spacedust)
124
+
this.#socket?.close();
125
+
// closing should trigger the .onclose handler to take it from here
126
+
return;
127
+
}
128
+
129
+
if (this.#status === 'disconnected') {
130
+
console.info('spacedust currently disconnected; connecting for updated subjects');
131
+
this.#subjectsDirty = true;
132
+
this.#connect();
133
+
return;
134
+
} else if (this.#status === 'connecting') {
135
+
console.info('spacedust currently connecting; just flagging subjects as dirty');
136
+
this.#subjectsDirty = true; // on connect it should automatically update
137
+
return;
138
+
}
139
+
140
+
if (!this.#socket) {
141
+
throw new Error(`spacedust status is "${this.#status}" but the socket is null -- a bug?`);
142
+
}
143
+
144
+
this.#socket.send(JSON.stringify({
145
+
type: 'options_update',
146
+
payload: {
147
+
wantedSources: this.#sources,
148
+
wantedSubjects: this.#subjects,
149
+
},
150
+
}));
151
+
152
+
this.#subjectsDirty = false;
153
+
}
154
+
155
+
close() {
156
+
this.#eol = true;
157
+
this.#socket?.close();
158
+
}
159
+
160
+
}
+1
live-embed/src/vite-env.d.ts
+1
live-embed/src/vite-env.d.ts
···
1
+
/// <reference types="vite/client" />
+27
live-embed/tsconfig.app.json
+27
live-embed/tsconfig.app.json
···
1
+
{
2
+
"compilerOptions": {
3
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4
+
"target": "ES2022",
5
+
"useDefineForClassFields": true,
6
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+
"module": "ESNext",
8
+
"skipLibCheck": true,
9
+
10
+
/* Bundler mode */
11
+
"moduleResolution": "bundler",
12
+
"allowImportingTsExtensions": true,
13
+
"verbatimModuleSyntax": true,
14
+
"moduleDetection": "force",
15
+
"noEmit": true,
16
+
"jsx": "react-jsx",
17
+
18
+
/* Linting */
19
+
"strict": true,
20
+
"noUnusedLocals": true,
21
+
"noUnusedParameters": true,
22
+
"erasableSyntaxOnly": true,
23
+
"noFallthroughCasesInSwitch": true,
24
+
"noUncheckedSideEffectImports": true
25
+
},
26
+
"include": ["src"]
27
+
}
+7
live-embed/tsconfig.json
+7
live-embed/tsconfig.json
+25
live-embed/tsconfig.node.json
+25
live-embed/tsconfig.node.json
···
1
+
{
2
+
"compilerOptions": {
3
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4
+
"target": "ES2023",
5
+
"lib": ["ES2023"],
6
+
"module": "ESNext",
7
+
"skipLibCheck": true,
8
+
9
+
/* Bundler mode */
10
+
"moduleResolution": "bundler",
11
+
"allowImportingTsExtensions": true,
12
+
"verbatimModuleSyntax": true,
13
+
"moduleDetection": "force",
14
+
"noEmit": true,
15
+
16
+
/* Linting */
17
+
"strict": true,
18
+
"noUnusedLocals": true,
19
+
"noUnusedParameters": true,
20
+
"erasableSyntaxOnly": true,
21
+
"noFallthroughCasesInSwitch": true,
22
+
"noUncheckedSideEffectImports": true
23
+
},
24
+
"include": ["vite.config.ts"]
25
+
}
+7
live-embed/vite.config.ts
+7
live-embed/vite.config.ts
+29
server/api.js
+29
server/api.js
···
183
183
return gotIt(res);
184
184
};
185
185
186
+
const handleGetNotificationFilter = async (db, user, searchParams, res) => {
187
+
const selector = searchParams.get('selector');
188
+
if (!selector) return badRequest(res, '"selector" required in search query');
189
+
190
+
const selection = searchParams.get('selection');
191
+
if (!selection) return badRequest(res, '"selection" required in search query');
192
+
193
+
const { did } = user;
194
+
195
+
const notify = db.getNotificationFilter(did, selector, selection) ?? null;
196
+
return ok(res, { notify });
197
+
};
198
+
199
+
const handleSetNotificationFilter = async (db, user, req, res) => {
200
+
const body = await getRequesBody(req);
201
+
const { selector, selection, notify } = JSON.parse(body);
202
+
const { did } = user;
203
+
db.setNotificationFilter(did, selector, selection, notify);
204
+
return ok(res, { notify });
205
+
};
206
+
186
207
/// admin stuff
187
208
188
209
const handleListSecrets = async (db, res) => {
···
286
307
if (method === 'POST' && pathname === '/global-notify') {
287
308
if (!user) return unauthorized(res);
288
309
return handleSetGlobalNotifySettings(db, user, req, res);
310
+
}
311
+
if (method === 'GET' && pathname === '/notification-filter') {
312
+
if (!user) return unauthorized(res);
313
+
return handleGetNotificationFilter(db, user, searchParams, res);
314
+
}
315
+
if (method === 'POST' && pathname === '/notification-filter') {
316
+
if (!user) return unauthorized(res);
317
+
return handleSetNotificationFilter(db, user, req, res);
289
318
}
290
319
291
320
// non-public access required
+55
server/db.js
+55
server/db.js
···
2
2
import Database from 'better-sqlite3';
3
3
4
4
const SUBS_PER_ACCOUNT_LIMIT = 5;
5
+
const SECONDARY_FILTERS_LIMIT = 100;
6
+
5
7
const SCHEMA_FNAME = './schema.sql';
6
8
7
9
export class DB {
···
18
20
#stmt_set_role;
19
21
#stmt_get_notify_account_globals;
20
22
#stmt_set_notify_account_globals;
23
+
#stmt_set_notification_filter;
24
+
#stmt_get_notification_filter;
25
+
#stmt_count_notification_filters;
26
+
#stmt_rm_notification_filter;
21
27
22
28
#stmt_admin_add_secret;
23
29
#stmt_admin_expire_secret;
···
127
133
notify_self = :notify_self
128
134
where did = :did`);
129
135
136
+
this.#stmt_set_notification_filter = db.prepare(
137
+
`insert into notification_filters (account_did, selector, selection, notify)
138
+
values (:did, :selector, :selection, :notify)
139
+
on conflict do update
140
+
set notify = excluded.notify`);
141
+
142
+
this.#stmt_get_notification_filter = db.prepare(
143
+
`select notify
144
+
from notification_filters
145
+
where account_did = :did
146
+
and selector = :selector
147
+
and selection = :selection`);
148
+
149
+
this.#stmt_count_notification_filters = db.prepare(
150
+
`select count(*) as n
151
+
from notification_filters
152
+
where account_did = :did`);
153
+
154
+
this.#stmt_rm_notification_filter = db.prepare(
155
+
`delete from notification_filters
156
+
where account_did = :did
157
+
and selector = :selector
158
+
and selection = :selection`);
159
+
130
160
131
161
this.#stmt_admin_add_secret = db.prepare(
132
162
`insert into top_secret_passwords (password)
···
233
263
update.did = did;
234
264
this.#stmt_set_notify_account_globals.run(update);
235
265
});
266
+
}
267
+
268
+
getNotificationFilter(did, selector, selection) {
269
+
const res = this.#stmt_get_notification_filter.get({ did, selector, selection });
270
+
const dbNotify = res?.notify;
271
+
if (dbNotify === 1) return true;
272
+
else if (dbNotify === 0) return false;
273
+
else return null;
274
+
}
275
+
276
+
setNotificationFilter(did, selector, selection, notify) {
277
+
if (notify === null) {
278
+
this.#stmt_rm_notification_filter.run({ did, selector, selection });
279
+
} else {
280
+
this.#transactionally(() => {
281
+
const { n } = this.#stmt_count_notification_filters.get({ did });
282
+
if (n >= SECONDARY_FILTERS_LIMIT) {
283
+
throw new Error('max filters set for account');
284
+
}
285
+
let dbNotify = null;
286
+
if (notify === true) dbNotify = 1;
287
+
else if (notify === false) dbNotify = 0;
288
+
this.#stmt_set_notification_filter.run({ did, selector, selection, notify: dbNotify });
289
+
});
290
+
}
236
291
}
237
292
238
293
+20
server/index.js
+20
server/index.js
···
3
3
import { createRemoteJWKSet } from 'jose';
4
4
import fs from 'node:fs';
5
5
import { randomBytes } from 'node:crypto';
6
+
import https from 'node:https';
6
7
import webpush from 'web-push';
7
8
import { DB } from './db.js';
8
9
import { connectSpacedust } from './notifications.js';
···
26
27
return secrets;
27
28
}
28
29
30
+
function startHealthcheckPing(endpoint) {
31
+
const next = () => setTimeout(() => startHealthcheckPing(endpoint), 90 * 1000);
32
+
33
+
https
34
+
.get(endpoint, res => {
35
+
if (res.statusCode !== 200) console.warn('non-200 health check response', res.statusCode);
36
+
res
37
+
.on('data', () => {})
38
+
.on('end', next);
39
+
})
40
+
.on('error', err => {
41
+
console.warn('healthcheck request errored', err);
42
+
next();
43
+
});
44
+
}
45
+
29
46
const main = env => {
30
47
if (!env.ADMIN_DID) throw new Error('ADMIN_DID is required to run');
31
48
const adminDid = env.ADMIN_DID;
···
53
70
const port = parseInt(env.PORT ?? 8000, 10);
54
71
55
72
const allowedOrigin = env.ALLOWED_ORIGIN ?? 'http://127.0.0.1:5173';
73
+
74
+
if (env.HEALTHCHECK) startHealthcheckPing(env.HEALTHCHECK);
75
+
else console.warn('no HEALTHCHECK in env, not sending healthcheck pings');
56
76
57
77
server(secrets, jwks, allowedOrigin, whoamiHost, db, updateSubs, push, adminDid).listen(
58
78
port,
+19
-1
server/notifications.js
+19
-1
server/notifications.js
···
1
-
import lexicons from 'lexicons';
1
+
import { default as lexicons, getBits } from 'lexicons';
2
2
import psl from 'psl';
3
3
import webpush from 'web-push';
4
4
import WebSocket from 'ws';
···
140
140
}
141
141
if (source_did === did) {
142
142
console.warn(`ignoring self-notification`);
143
+
return;
144
+
}
145
+
}
146
+
147
+
// like above, this over-assumes that did is the only recipient we could care about for now
148
+
const { app, group } = getBits(source);
149
+
for (const [selector, selection] of [
150
+
['source', source],
151
+
['group', group],
152
+
['app', app],
153
+
]) {
154
+
const notify = db.getNotificationFilter(did, selector, selection);
155
+
if (notify === true) {
156
+
console.info(`explicitly allowing notification by filter for ${selector}=${selection}`);
157
+
break;
158
+
};
159
+
if (notify === false) {
160
+
console.warn(`ignoring filtered notification for ${selector}=${selection}`);
143
161
return;
144
162
}
145
163
}
+2
-2
server/schema.sql
+2
-2
server/schema.sql
···
31
31
check(length(password) >= 3)
32
32
) strict;
33
33
34
-
create table if not exists mute_by_secondary (
34
+
create table if not exists notification_filters (
35
35
account_did text not null,
36
36
selector text not null,
37
37
selection text not null,
38
-
mute integer not null default true,
38
+
notify integer null,
39
39
40
40
primary key(account_did, selector, selection),
41
41
check(selector in ('all', 'app', 'group', 'source')),