1- import { Client } from "@synonymdev/pubky" ;
1+ import { Pubky } from "@synonymdev/pubky" ;
22import { createStore } from "solid-js/store" ;
33
4- export const client =
5- import . meta. env . VITE_TESTNET == "true" ? Client . testnet ( ) : new Client ( ) ;
4+ export const pubky =
5+ import . meta. env . VITE_TESTNET === "true" ? Pubky . testnet ( ) : new Pubky ( ) ;
6+ const publicStorage = pubky . publicStorage ;
67
78export type ListItem = { link : string ; name : string ; isDirectory : boolean } ;
89
10+ const PUBKEY_RE = / ^ [ a - z 0 - 9 ] { 52 } $ / i;
11+
12+ export function isPubkeySegment ( value : string ) : boolean {
13+ return PUBKEY_RE . test ( value ) ;
14+ }
15+
16+ export function formatDisplayPath ( path : string ) : string {
17+ return path . replace ( / ^ ( [ a - z 0 - 9 ] { 52 } ) (? = \/ | $ ) / i, "pubky$1" ) ;
18+ }
19+
20+ export function stripInputPrefixes ( raw : string ) : string {
21+ let path = ( raw || "" ) . trim ( ) ;
22+ path = path . replace ( / ^ p u b k y : \/ \/ ? / i, "" ) . replace ( / ^ p k : / i, "" ) ;
23+ if ( / ^ p u b k y [ a - z 0 - 9 ] { 52 } (?: \/ | $ ) / i. test ( path ) ) {
24+ path = path . replace ( / ^ p u b k y / i, "" ) ;
25+ }
26+ return path ;
27+ }
28+
929type PreviewState = {
1030 open : boolean ;
1131 link : string ;
@@ -114,15 +134,10 @@ export function loadMore() {
114134 const reqId = ++ currentRequestId ;
115135 isFetching = true ;
116136
117- client
118- . list ( `pubky://${ path } ` , cursor || "" , false , limit , store . shallow )
137+ listDirectory ( path , cursor || null , limit )
119138 . then ( ( l : Array < string > ) => {
120139 if ( reqId !== currentRequestId ) return ; // stale; ignore
121- const list = l . map ( ( link ) => {
122- let name = link . replace ( "pubky://" , "" ) . replace ( store . dir , "" ) ;
123- let isDirectory = name . endsWith ( "/" ) ;
124- return { link, isDirectory, name } ;
125- } ) ;
140+ const list = listToItems ( l , store . dir ) ;
126141
127142 let map = new Map < string , ListItem > ( ) ;
128143 for ( let item of store . list ) map . set ( item . name , item ) ;
@@ -204,7 +219,7 @@ export function updateDir(
204219export async function downloadFile ( link : string ) {
205220 setStore ( "loading" , true ) ;
206221 try {
207- const response : Response = await client . fetch ( link ) ;
222+ const response : Response = await publicStorage . get ( link ) ;
208223 if ( ! response . ok ) {
209224 throw new Error (
210225 `Failed to fetch file: ${ response . status } ${ response . statusText } ` ,
@@ -266,7 +281,7 @@ export async function openPreview(
266281 } ) ;
267282
268283 try {
269- const res = await client . fetch ( link ) ;
284+ const res = await publicStorage . get ( link ) ;
270285 if ( ! res . ok ) throw new Error ( `Failed to fetch file: ${ res . status } ` ) ;
271286 const mime = ( res . headers . get ( "content-type" ) || "" ) . toLowerCase ( ) ;
272287
@@ -369,14 +384,9 @@ export function prefetchDir(dirPath: string) {
369384 prefetchInFlight . add ( key ) ;
370385
371386 const limit = Math . ceil ( window . innerHeight / 40 ) ;
372- client
373- . list ( `pubky://${ dir } ` , "" , false , limit , store . shallow )
387+ listDirectory ( dir , null , limit )
374388 . then ( ( l : Array < string > ) => {
375- const list = l . map ( ( link ) => {
376- const name = link . replace ( "pubky://" , "" ) . replace ( dir , "" ) ;
377- const isDirectory = name . endsWith ( "/" ) ;
378- return { link, isDirectory, name } ;
379- } ) ;
389+ const list = listToItems ( l , dir ) ;
380390 cachePut ( dir , { list, scroll : 0 } ) ;
381391 } )
382392 . finally ( ( ) => prefetchInFlight . delete ( key ) ) ;
@@ -388,23 +398,38 @@ export function cacheSaveScroll(scroll: number = window.scrollY) {
388398 cacheSaveScrollFor ( store . dir , scroll ) ;
389399}
390400
391- // --- helpers: cache, sort, normalize, revalidate ---
401+ // --- helpers: listing, cache, sort, normalize, revalidate ---
402+
403+ function listDirectory (
404+ path : string ,
405+ cursor : string | null ,
406+ limit : number ,
407+ ) : Promise < string [ ] > {
408+ const address = `pubky://${ path } ` ;
409+ return publicStorage . list ( address , cursor , false , limit , store . shallow ) ;
410+ }
411+
412+ function listToItems ( links : string [ ] , dir : string ) : ListItem [ ] {
413+ return links . map ( ( link ) => {
414+ let name = link
415+ . replace ( / ^ p u b k y : \/ \/ / i, "" )
416+ . replace ( / ^ p u b k y (? = [ a - z 0 - 9 ] { 52 } (?: \/ | $ ) ) / i, "" ) ;
417+ if ( name . startsWith ( dir ) ) name = name . slice ( dir . length ) ;
418+ const isDirectory = name . endsWith ( "/" ) ;
419+ return { link, isDirectory, name } ;
420+ } ) ;
421+ }
392422
393423function backgroundRevalidate ( path : string ) {
394424 const limit = Math . ceil ( window . innerHeight / 40 ) ;
395425 const reqId = ++ currentRequestId ;
396426 isFetching = true ;
397427 setStore ( "loading" , true ) ;
398428
399- client
400- . list ( `pubky://${ path } ` , "" , false , limit , store . shallow )
429+ listDirectory ( path , null , limit )
401430 . then ( ( l : Array < string > ) => {
402431 if ( reqId !== currentRequestId ) return ;
403- const head = l . map ( ( link ) => {
404- let name = link . replace ( "pubky://" , "" ) . replace ( path , "" ) ;
405- let isDirectory = name . endsWith ( "/" ) ;
406- return { link, isDirectory, name } ;
407- } ) ;
432+ const head = listToItems ( l , path ) ;
408433
409434 // merge head with existing list
410435 const map = new Map < string , ListItem > ( ) ;
@@ -437,15 +462,14 @@ function sortItems(items: ListItem[]): ListItem[] {
437462/** Normalize a directory path (always ends with '/'). */
438463function normalizeDir ( raw : string ) : string {
439464 let path = stripPrefixesAndResolve ( raw ) ;
440- if ( path . length === 52 ) path = path + "/pub/" ;
465+ if ( isPubkeySegment ( path ) ) path = path + "/pub/" ;
441466 if ( ! path . endsWith ( "/" ) ) path = path + "/" ;
442467 return path ;
443468}
444469
445470/** Strip pubky:// or pk: and resolve ., .. and any accidental protocol/host. */
446471function stripPrefixesAndResolve ( raw : string ) : string {
447- let path = ( raw || "" ) . trim ( ) ;
448- path = path . replace ( / ^ p u b k y : \/ \/ ? / i, "" ) . replace ( / ^ p k : / i, "" ) ;
472+ let path = stripInputPrefixes ( raw ) ;
449473 try {
450474 if ( / ^ [ a - z ] + : \/ \/ / i. test ( path ) ) {
451475 const u = new URL ( path ) ;
@@ -472,6 +496,17 @@ function normalizeError(e: any): string {
472496 return "Network error or PK not found" ;
473497 return e ;
474498 }
499+ const name = e ?. name ;
500+ const statusCode =
501+ e ?. data && typeof e . data === "object" && "statusCode" in e . data
502+ ? ( e . data as { statusCode ?: number } ) . statusCode
503+ : undefined ;
504+ if ( name === "RequestError" && typeof statusCode === "number" ) {
505+ if ( statusCode === 404 ) return "Not found" ;
506+ if ( statusCode === 403 ) return "Forbidden" ;
507+ return `Request failed (${ statusCode } )` ;
508+ }
509+ if ( name === "InvalidInput" ) return "Invalid input" ;
475510 const msg = e . message || "Unknown error" ;
476511 if ( / a b o r t / i. test ( msg ) ) return "Request canceled" ;
477512 if ( / 4 0 4 / . test ( msg ) ) return "Not found" ;
0 commit comments