From e0b3d1a392083e9287db0db33f6ee29c3acb31fd Mon Sep 17 00:00:00 2001
From: Stefan Zerkalica <zerkalica@gmail.com>
Date: Thu, 14 Nov 2024 02:10:11 +0300
Subject: [PATCH] $mol_file webdav 2

---
 fetch/fetch.ts        |  2 +-
 file/base/base.ts     | 21 ++++++++-----------
 file/file.node.ts     |  7 +------
 file/file.ts          |  2 --
 file/file.web.ts      | 38 ++++++++++++++++++++++++++++++++++
 file/webdav/webdav.ts | 47 +++++++++++++++----------------------------
 6 files changed, 64 insertions(+), 53 deletions(-)

diff --git a/fetch/fetch.ts b/fetch/fetch.ts
index 76befad510d..3a194ad940b 100644
--- a/fetch/fetch.ts
+++ b/fetch/fetch.ts
@@ -109,7 +109,7 @@ namespace $ {
 			const response = this.response( input , init )
 			if( response.status() === 'success' ) return response
 			
-			throw new Error( response.message() )
+			throw new Error( response.message(), { cause: response } )
 		}
 
 		@ $mol_action
diff --git a/file/base/base.ts b/file/base/base.ts
index f6ab55cc2f0..a4b269d48d5 100644
--- a/file/base/base.ts
+++ b/file/base/base.ts
@@ -178,7 +178,7 @@ namespace $ {
 
 		@ $mol_mem
 		version() {
-			return this.modified()?.getTime().toString( 36 ).toUpperCase() ?? ''
+			return this.stat()?.mtime.getTime().toString( 36 ).toUpperCase() ?? ''
 		}
 
 		protected info( path: string ) { return null as null | $mol_file_stat }
@@ -190,8 +190,6 @@ namespace $ {
 		protected kids() {
 			return [] as readonly this[]
 		}
-		static headers() { return {} as Record<string, string> }
-		headers() { return (this.constructor as typeof $mol_file_base).headers() }
 
 		@ $mol_mem_key
 		readable(opts: { start?: number, end?: number }) {
@@ -214,14 +212,6 @@ namespace $ {
 				if (! this.version() ) return new Uint8Array
 
 				next = this.read()
-				// try {
-				// } catch (error) {
-				// 	// Файл может удалиться между изменением version и read, обрабатываем эту ситуацию
-				// 	if (error instanceof $mol_file_error && error.cause.code === 'not found') {
-				// 		return new Uint8Array()
-				// 	}
-				// 	$mol_fail_hidden(error)
-				// }
 
 				const prev = $mol_mem_cached( ()=> this.buffer() )
 
@@ -281,8 +271,13 @@ namespace $ {
 
 		static watch_root = ''
 
+		// static watcher_warned = false
 		watcher() {
-			console.warn('$mol_file_web.watcher() not implemented')
+			// const constructor = this.constructor as typeof $mol_file_base
+			// if (! constructor.watcher_warned) {
+			// 	console.warn(`${constructor}.watcher() not implemented`)
+			// 	constructor.watcher_warned = true
+			// }
 
 			return {
 				destructor() {}
@@ -353,7 +348,7 @@ namespace $ {
 			if (! this.exists() ) return []
 			if ( this.type() !== 'dir') return []
 
-			this.stat()
+			this.version()
 
 			// Если дочерний file удалился, список надо обновить
 			return this.kids().filter(file => file.exists())
diff --git a/file/file.node.ts b/file/file.node.ts
index 7988a71c46d..d2a22d13723 100644
--- a/file/file.node.ts
+++ b/file/file.node.ts
@@ -125,13 +125,8 @@ namespace $ {
 			try {
 				return buffer_normalize($node.fs.readFileSync( path ))
 			} catch( error: any ) {
-
 				if (! $mol_promise_like(error)) {
-					error = new $mol_file_error(
-						error.message + '\n' + path,
-						{ code: error.code === 'ENOENT' ? 'not found' : null },
-						error
-					)
+					error.message += '\n' + path
 				}
 
 				$mol_fail_hidden( error )
diff --git a/file/file.ts b/file/file.ts
index 0c2e6bb896f..353a87982bc 100644
--- a/file/file.ts
+++ b/file/file.ts
@@ -10,8 +10,6 @@ namespace $ {
 		ctime: Date
 	}
 
-	export class $mol_file_error extends $mol_error_mix<{ code: 'not found' | null }> {}
-
 	export class $mol_file extends $mol_file_base {}
 
 }
diff --git a/file/file.web.ts b/file/file.web.ts
index ebf26286f4d..722343cbdc3 100644
--- a/file/file.web.ts
+++ b/file/file.web.ts
@@ -6,6 +6,44 @@ namespace $ {
 			? new URL( '.' , ($mol_dom_context.document.currentScript as any)['src'] ).toString()
 			: ''
 
+		// Вотчер выключен, версия всегда будет одна
+		override version() { return '' }
+		// Ворнинги подавляем, иначе в каждом приложении, загружающим локали, будет ворнинг
+		// override watcher() { return { destructor() {} }}
+
+		protected override info() {
+			// Директории не поддерживаются
+			try {
+				const response = this.fetch({ method: 'HEAD' })
+				const headers = response.headers()
+
+				let size = Number(headers.get('Content-Length'))
+				if (Number.isNaN(size)) size = 0
+	
+				const last = headers.get('Last-Modified')
+	
+				const mtime = last ? new Date(last) : new Date()
+	
+				return {
+					type: 'file',
+					size,
+					mtime,
+					atime: mtime,
+					ctime: mtime,
+				} as $mol_file_stat
+
+			} catch (error) {
+				if (
+					error instanceof Error
+					&& error.cause instanceof $mol_fetch_response
+					&& error.cause.native.status === 404
+				) return null
+
+				$mol_fail_hidden(error)
+			}
+
+		}
+	
 	}
 
 	$.$mol_file = $mol_file_web
diff --git a/file/webdav/webdav.ts b/file/webdav/webdav.ts
index 35a29d02fd9..5403bc019d5 100644
--- a/file/webdav/webdav.ts
+++ b/file/webdav/webdav.ts
@@ -4,14 +4,6 @@ namespace $ {
 			return this.absolute<This>( new URL( path , this.base ).toString() )
 		}
 
-		override watcher() {
-			console.warn('$mol_file_web.watcher() not implemented')
-
-			return {
-				destructor() {}
-			}
-		}
-
 		override resolve( path : string ) {
 			let res = this.path() + '/' + path
 
@@ -30,7 +22,8 @@ namespace $ {
 			return ( this.constructor as typeof $mol_file_base ).absolute( res ) as this
 		}
 
-		headers() { return {} as Record<string, string> }
+		static headers() { return {} as Record<string, string> }
+		headers() { return (this.constructor as typeof $mol_file_webdav).headers() }
 
 		protected fetch(init: RequestInit) {
 			return this.$.$mol_fetch.success(this.path(), {
@@ -43,12 +36,18 @@ namespace $ {
 		}
 
 		protected override read() {
-			const response = this.$.$mol_fetch.response(this.path(), {
-				headers: this.headers()
-			})
-			if (response.native.status === 404) return new Uint8Array
-
-			return new Uint8Array(response.buffer())
+			try {
+				const response = this.fetch({})
+				return new Uint8Array(response.buffer())
+			} catch (error) {
+				if (
+					error instanceof Error
+					&& error.cause instanceof $mol_fetch_response
+					&& error.cause.native.status === 404
+				)  return new Uint8Array
+
+				$mol_fail_hidden(error)
+			}
 		}
 
 		protected override write( body : Uint8Array ) { this.fetch({ method: 'PUT', body }) }
@@ -98,22 +97,8 @@ namespace $ {
 			}).stream() || $mol_fail(new Error('Not found'))
 		}
 
-		protected override info(): $mol_file_stat {
-			const response = this.fetch({ method: 'HEAD' })
-			const headers = response.headers()
-			let size = Number(headers.get('Content-Length'))
-			if (Number.isNaN(size)) size = 0
-			const last = headers.get('Last-Modified')
-
-			const mtime = last ? new Date(last) : new Date()
-
-			return {
-				type: 'file',
-				size,
-				mtime,
-				atime: mtime,
-				ctime: mtime,
-			}
+		protected override info() {
+			return this.kids().at(0)?.stat() ?? null
 		}
 	}