@omnistreamai/data-adapter-webdav 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/webdav-adapter.ts"],"sourcesContent":[],"mappings":";;;;;;AAYA;AACe,cADF,aAAA,YAAyB,aACvB,CAAA;EAQO,SAAA,IAAA,GARP,WAAA,CAAA,MAAA;EAgHV,SAAA,IAAA,GAAA,eAAA;EA4BiB,QAAA,OAAA;EAEZ;;;EAiBe,UAAA,CAAA,OAAA,EAvJH,aAuJG,CAAA,EAAA,IAAA;EAGP;;;EAEb,QAAA,UAAA;EAoBiE;;;EAejE,QAAA,QAAA;EAeqB;;;EAIG,QAAA,OAAA;EAAhB;;;EAyH6B,SAAA,CAAA,SAAA,EAAA,MAAA,EAAA,QAAA,CAAA,EAAA,MAAA,EAAA,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EAnOrC,OAmOqC,CAAA,IAAA,CAAA;EAgDhB;;;gBAvPJ,kDAEZ,oBAEL,QAAQ;;;;mBAeY,8DAGf,QAAQ,qBAEb,QAAQ;;;;0DAoByD;;;;oBAW5C,0EAIrB,QAAQ;;;;oBAea,sDAEb,aAAa,sBAErB,QAAQ,gBAAgB;;;;;;;;;;;;4BA6GK;;;;gCAYQ;;;;oBAgDhB"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/webdav-adapter.ts"],"sourcesContent":[],"mappings":";;;;;;AAYA;AACe,cADF,aAAA,YAAyB,aACvB,CAAA;EAQO,SAAA,IAAA,GARP,WAAA,CAAA,MAAA;EAqHV,SAAA,IAAA,GAAA,eAAA;EA4BiB,QAAA,OAAA;EAEZ;;;EAiBe,UAAA,CAAA,OAAA,EA5JH,aA4JG,CAAA,EAAA,IAAA;EAGP;;;EAEb,QAAA,UAAA;EAwBA;;;EAeA,QAAA,QAAA;EAmBqB;;;EAIG,QAAA,OAAA;EAAhB;;;EA0H6B,SAAA,CAAA,SAAA,EAAA,MAAA,EAAA,QAAA,CAAA,EAAA,MAAA,EAAA,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EA5OrC,OA4OqC,CAAA,IAAA,CAAA;EAmDhB;;;gBAnQJ,kDAEZ,oBAEL,QAAQ;;;;mBAeY,8DAGf,QAAQ,qBAEb,QAAQ;;;;0DAwBR;;;;oBAWqB,0EAIrB,QAAQ;;;;oBAmBa,sDAEb,aAAa,sBAErB,QAAQ,gBAAgB;;;;;;;;;;;;4BA8GK;;;;gCAYQ;;;;oBAmDhB"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/webdav-adapter.ts"],"sourcesContent":[],"mappings":";;;;;;AAYA;AACe,cADF,aAAA,YAAyB,aACvB,CAAA;EAQO,SAAA,IAAA,GARP,WAAA,CAAA,MAAA;EAgHV,SAAA,IAAA,GAAA,eAAA;EA4BiB,QAAA,OAAA;EAEZ;;;EAiBe,UAAA,CAAA,OAAA,EAvJH,aAuJG,CAAA,EAAA,IAAA;EAGP;;;EAEb,QAAA,UAAA;EAoBiE;;;EAejE,QAAA,QAAA;EAeqB;;;EAIG,QAAA,OAAA;EAAhB;;;EAyH6B,SAAA,CAAA,SAAA,EAAA,MAAA,EAAA,QAAA,CAAA,EAAA,MAAA,EAAA,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EAnOrC,OAmOqC,CAAA,IAAA,CAAA;EAgDhB;;;gBAvPJ,kDAEZ,oBAEL,QAAQ;;;;mBAeY,8DAGf,QAAQ,qBAEb,QAAQ;;;;0DAoByD;;;;oBAW5C,0EAIrB,QAAQ;;;;oBAea,sDAEb,aAAa,sBAErB,QAAQ,gBAAgB;;;;;;;;;;;;4BA6GK;;;;gCAYQ;;;;oBAgDhB"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/webdav-adapter.ts"],"sourcesContent":[],"mappings":";;;;;;AAYA;AACe,cADF,aAAA,YAAyB,aACvB,CAAA;EAQO,SAAA,IAAA,GARP,WAAA,CAAA,MAAA;EAqHV,SAAA,IAAA,GAAA,eAAA;EA4BiB,QAAA,OAAA;EAEZ;;;EAiBe,UAAA,CAAA,OAAA,EA5JH,aA4JG,CAAA,EAAA,IAAA;EAGP;;;EAEb,QAAA,UAAA;EAwBA;;;EAeA,QAAA,QAAA;EAmBqB;;;EAIG,QAAA,OAAA;EAAhB;;;EA0H6B,SAAA,CAAA,SAAA,EAAA,MAAA,EAAA,QAAA,CAAA,EAAA,MAAA,EAAA,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EA5OrC,OA4OqC,CAAA,IAAA,CAAA;EAmDhB;;;gBAnQJ,kDAEZ,oBAEL,QAAQ;;;;mBAeY,8DAGf,QAAQ,qBAEb,QAAQ;;;;0DAwBR;;;;oBAWqB,0EAIrB,QAAQ;;;;oBAmBa,sDAEb,aAAa,sBAErB,QAAQ,gBAAgB;;;;;;;;;;;;4BA8GK;;;;gCAYQ;;;;oBAmDhB"}
package/dist/index.js CHANGED
@@ -214,10 +214,13 @@ var WebDAVAdapter = class {
214
214
  const basePath = new URL(baseUrl).pathname;
215
215
  while ((match = hrefRegex.exec(text)) !== null) {
216
216
  const fullPath = match[1] || "";
217
+ let relativePath = "";
217
218
  try {
218
- let relativePath = new URL(fullPath).pathname.replace(basePath, "").replace(/^\//, "");
219
- if (relativePath && relativePath !== path.replace(/^\//, "")) files.push(relativePath);
220
- } catch {}
219
+ relativePath = new URL(fullPath).pathname.replace(basePath, "").replace(/^\//, "");
220
+ } catch {
221
+ relativePath = fullPath.replace(basePath, "").replace(/^\//, "");
222
+ }
223
+ if (relativePath && relativePath !== path.replace(/^\//, "")) files.push(relativePath);
221
224
  }
222
225
  return files;
223
226
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["AdapterType","headers: Record<string, string>","AuthType","allData: T[]","files: string[]"],"sources":["../src/webdav-adapter.ts"],"sourcesContent":["import {\n AdapterType,\n type RemoteAdapter,\n type QueryOptions,\n type PaginatedResult,\n type ServiceConfig,\n AuthType,\n} from '@omnistreamai/data-core';\n\n/**\n * WebDAV adapter implementation\n */\nexport class WebDAVAdapter implements RemoteAdapter {\n readonly type = AdapterType.Remote;\n readonly name = 'WebDAVAdapter';\n\n private service: ServiceConfig | null = null;\n\n /**\n * Set service configuration\n */\n setService(service: ServiceConfig): void {\n this.service = service;\n }\n\n /**\n * Get request headers\n */\n private getHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (!this.service?.authentication) {\n return headers;\n }\n\n const { authentication } = this.service;\n\n switch (authentication.authType) {\n case AuthType.Token:\n if (authentication.token) {\n headers['Authorization'] = `${authentication.token.token_type} ${authentication.token.access_token}`;\n }\n break;\n case AuthType.Bearer:\n if (authentication.bearer) {\n headers['Authorization'] = `Bearer ${authentication.bearer}`;\n }\n break;\n case AuthType.Basic:\n if (authentication.username && authentication.password) {\n const credentials = btoa(`${authentication.username}:${authentication.password}`);\n headers['Authorization'] = `Basic ${credentials}`;\n }\n break;\n }\n\n return headers;\n }\n\n /**\n * Build URL\n */\n private buildUrl(storeName: string, id?: string): string {\n if (!this.service) {\n throw new Error('Service not configured. Call setService() first.');\n }\n\n const baseUrl = this.service.endpoint.replace(/\\/$/, '');\n const storePath = `/${storeName}`;\n \n if (id) {\n return `${baseUrl}${storePath}/${id}`;\n }\n \n return `${baseUrl}${storePath}`;\n }\n\n /**\n * Send request\n */\n private async request<T>(\n url: string,\n options: RequestInit = {},\n {\n allowNotFound,\n }: {\n allowNotFound?: boolean;\n } = {}\n ): Promise<T | null> {\n const response = await fetch(url, {\n ...options,\n headers: {\n ...this.getHeaders(),\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n if (allowNotFound && response.status == 404) {\n return null;\n }\n throw new Error(`HTTP error! status: ${response.status}, message: ${response.statusText}`);\n }\n\n const text = await response.text();\n if (!text) {\n return null;\n }\n\n try {\n return JSON.parse(text) as T;\n } catch {\n return null;\n }\n }\n\n /**\n * Initialize store\n */\n async initStore(\n storeName: string,\n _indexes: string[] = [],\n _idKey: string = 'id'\n ): Promise<void> {\n const path = storeName.replace(/^\\/+|\\/+$/g, '');\n const parts = path.split('/');\n\n let currentPath = '';\n for (const part of parts) {\n currentPath = currentPath ? `${currentPath}/${part}` : part;\n\n try {\n await this.listDirectory(currentPath);\n } catch {\n const url = this.buildUrl(currentPath);\n\n const response = await fetch(url, {\n method: 'MKCOL',\n headers: this.getHeaders(),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to create store: ${response.statusText}`);\n }\n }\n }\n }\n\n /**\n * Add data\n */\n async add<T extends Record<string, unknown>>(\n storeName: string,\n data: T,\n idKey: string = 'id'\n ): Promise<T> {\n const id = String(data[idKey]);\n const url = this.buildUrl(storeName, id);\n\n const result = await this.request<T>(url, {\n method: 'PUT',\n body: JSON.stringify(data),\n });\n\n return result ?? data;\n }\n\n /**\n * Update data\n */\n async update<T extends Record<string, unknown>>(\n storeName: string,\n id: string,\n data: Partial<T>,\n idKey: string = 'id'\n ): Promise<T> {\n const existing = await this.getData<T>(storeName, id, idKey);\n if (!existing) {\n throw new Error('Not found id:' + id);\n }\n\n const updated = { ...existing, ...data };\n const url = this.buildUrl(storeName, id);\n\n const result = await this.request<T>(url, {\n method: 'PUT',\n body: JSON.stringify(updated),\n });\n\n return result ?? updated;\n }\n\n /**\n * Delete data\n */\n async delete(storeName: string, id: string, _idKey: string = 'id'): Promise<void> {\n const url = this.buildUrl(storeName, id);\n\n await this.request(url, {\n method: 'DELETE',\n });\n }\n\n /**\n * Get data by ID\n */\n async getData<T extends Record<string, unknown>>(\n storeName: string,\n id: string,\n _idKey: string = 'id'\n ): Promise<T | null> {\n const url = this.buildUrl(storeName, id);\n\n const result = await this.request<T>(url, {\n method: 'GET',\n }, {\n allowNotFound: true\n });\n\n return result;\n }\n\n /**\n * Get list data (with pagination support)\n */\n async getList<T extends Record<string, unknown>>(\n storeName: string,\n options: QueryOptions<T> = {},\n _idKey: string = 'id'\n ): Promise<PaginatedResult<T>> {\n const files = await this.listDirectory(storeName);\n\n const allData: T[] = [];\n for (const file of files) {\n try {\n const data = await this.request<T>(this.buildUrl(storeName, file), {\n method: 'GET',\n });\n if (data) {\n allData.push(data);\n }\n } catch {\n }\n }\n\n let filteredData = allData;\n\n if (options.where) {\n filteredData = allData.filter(item => {\n for (const key in options.where) {\n const itemValue = item[key];\n const whereValue = options.where![key];\n if (itemValue !== whereValue) {\n return false;\n }\n }\n return true;\n });\n }\n\n // Sorting\n if (options.sortBy) {\n filteredData = this.sortEntries(filteredData, String(options.sortBy), options.sortOrder);\n }\n\n const totalCount = filteredData.length;\n const page = options.page ?? 1;\n const limit = options.limit ?? totalCount;\n\n const startIndex = (page - 1) * limit;\n const endIndex = startIndex + limit;\n const paginatedData = filteredData.slice(startIndex, endIndex);\n\n return {\n data: paginatedData,\n totalCount,\n page,\n limit,\n };\n }\n\n /**\n * Sort entries by specified field\n * @param entries Array of entries to sort\n * @param key Sorting field name\n * @param sortOrder Sort order: 'asc' or 'desc' (default: 'asc')\n * @returns Sorted array of entries\n */\n private sortEntries<T extends Record<string, unknown>>(\n entries: T[],\n key: string,\n sortOrder: 'asc' | 'desc' = 'asc'\n ): T[] {\n return [...entries].sort((a, b) => {\n const aValue = a[key];\n const bValue = b[key];\n\n // Handle undefined or null values\n if (aValue == null && bValue == null) return 0;\n if (aValue == null) return 1; // null/undefined at the end\n if (bValue == null) return -1; // null/undefined at the end\n\n let comparison = 0;\n\n // Number type sorting\n if (typeof aValue === \"number\" && typeof bValue === \"number\") {\n comparison = aValue - bValue;\n }\n // String type sorting\n else if (typeof aValue === \"string\" && typeof bValue === \"string\") {\n comparison = aValue.localeCompare(bValue);\n }\n // Date type sorting (if string format date)\n else if (typeof aValue === \"string\" && typeof bValue === \"string\") {\n const aDate = new Date(aValue);\n const bDate = new Date(bValue);\n if (!isNaN(aDate.getTime()) && !isNaN(bDate.getTime())) {\n comparison = aDate.getTime() - bDate.getTime();\n } else {\n // Convert other types to string for comparison\n comparison = String(aValue).localeCompare(String(bValue));\n }\n }\n // Convert other types to string for comparison\n else {\n comparison = String(aValue).localeCompare(String(bValue));\n }\n\n // Apply sort order\n return sortOrder === 'desc' ? -comparison : comparison;\n });\n }\n\n\n\n /**\n * Clear store\n */\n async clear(storeName: string): Promise<void> {\n const list = await this.getList(storeName);\n\n for (const item of list.data) {\n const id = String((item as Record<string, unknown>)['id']);\n await this.delete(storeName, id);\n }\n }\n\n /**\n * List directory contents\n */\n async listDirectory(path: string = ''): Promise<string[]> {\n if (!this.service) {\n throw new Error('Service not configured. Call setService() first.');\n }\n\n const baseUrl = this.service.endpoint.replace(/\\/$/, '');\n const url = path ? `${baseUrl}/${path.replace(/^\\//, '')}` : baseUrl;\n\n const response = await fetch(url, {\n method: 'PROPFIND',\n headers: {\n ...this.getHeaders(),\n Depth: '1',\n },\n });\n\n if (!response.ok) {\n throw new Error(`Failed to list directory: ${response.statusText}`);\n }\n\n const text = await response.text();\n\n const hrefRegex = /<d:href[^>]*>([^<]+)<\\/d:href>/gi;\n const files: string[] = [];\n let match;\n\n const baseUrlObj = new URL(baseUrl);\n const basePath = baseUrlObj.pathname;\n\n while ((match = hrefRegex.exec(text)) !== null) {\n const fullPath = match[1] || '';\n try {\n const hrefUrl = new URL(fullPath);\n let relativePath = hrefUrl.pathname.replace(basePath, '').replace(/^\\//, '');\n\n if (relativePath && relativePath !== path.replace(/^\\//, '')) {\n files.push(relativePath);\n }\n } catch {\n }\n }\n\n return files;\n }\n\n /**\n * Test connection\n */\n async testConnection(): Promise<boolean> {\n try {\n await this.listDirectory('');\n return true;\n } catch {\n return false;\n }\n }\n}\n\n\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["AdapterType","headers: Record<string, string>","AuthType","allData: T[]","files: string[]"],"sources":["../src/webdav-adapter.ts"],"sourcesContent":["import {\n AdapterType,\n type RemoteAdapter,\n type QueryOptions,\n type PaginatedResult,\n type ServiceConfig,\n AuthType,\n} from \"@omnistreamai/data-core\";\n\n/**\n * WebDAV adapter implementation\n */\nexport class WebDAVAdapter implements RemoteAdapter {\n readonly type = AdapterType.Remote;\n readonly name = \"WebDAVAdapter\";\n\n private service: ServiceConfig | null = null;\n\n /**\n * Set service configuration\n */\n setService(service: ServiceConfig): void {\n this.service = service;\n }\n\n /**\n * Get request headers\n */\n private getHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n if (!this.service?.authentication) {\n return headers;\n }\n\n const { authentication } = this.service;\n\n switch (authentication.authType) {\n case AuthType.Token:\n if (authentication.token) {\n headers[\"Authorization\"] =\n `${authentication.token.token_type} ${authentication.token.access_token}`;\n }\n break;\n case AuthType.Bearer:\n if (authentication.bearer) {\n headers[\"Authorization\"] = `Bearer ${authentication.bearer}`;\n }\n break;\n case AuthType.Basic:\n if (authentication.username && authentication.password) {\n const credentials = btoa(\n `${authentication.username}:${authentication.password}`,\n );\n headers[\"Authorization\"] = `Basic ${credentials}`;\n }\n break;\n }\n\n return headers;\n }\n\n /**\n * Build URL\n */\n private buildUrl(storeName: string, id?: string): string {\n if (!this.service) {\n throw new Error(\"Service not configured. Call setService() first.\");\n }\n\n const baseUrl = this.service.endpoint.replace(/\\/$/, \"\");\n const storePath = `/${storeName}`;\n\n if (id) {\n return `${baseUrl}${storePath}/${id}`;\n }\n\n return `${baseUrl}${storePath}`;\n }\n\n /**\n * Send request\n */\n private async request<T>(\n url: string,\n options: RequestInit = {},\n {\n allowNotFound,\n }: {\n allowNotFound?: boolean;\n } = {},\n ): Promise<T | null> {\n const response = await fetch(url, {\n ...options,\n headers: {\n ...this.getHeaders(),\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n if (allowNotFound && response.status == 404) {\n return null;\n }\n throw new Error(\n `HTTP error! status: ${response.status}, message: ${response.statusText}`,\n );\n }\n\n const text = await response.text();\n if (!text) {\n return null;\n }\n\n try {\n return JSON.parse(text) as T;\n } catch {\n return null;\n }\n }\n\n /**\n * Initialize store\n */\n async initStore(\n storeName: string,\n _indexes: string[] = [],\n _idKey: string = \"id\",\n ): Promise<void> {\n const path = storeName.replace(/^\\/+|\\/+$/g, \"\");\n const parts = path.split(\"/\");\n\n let currentPath = \"\";\n for (const part of parts) {\n currentPath = currentPath ? `${currentPath}/${part}` : part;\n\n try {\n await this.listDirectory(currentPath);\n } catch {\n const url = this.buildUrl(currentPath);\n\n const response = await fetch(url, {\n method: \"MKCOL\",\n headers: this.getHeaders(),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to create store: ${response.statusText}`);\n }\n }\n }\n }\n\n /**\n * Add data\n */\n async add<T extends Record<string, unknown>>(\n storeName: string,\n data: T,\n idKey: string = \"id\",\n ): Promise<T> {\n const id = String(data[idKey]);\n const url = this.buildUrl(storeName, id);\n\n const result = await this.request<T>(url, {\n method: \"PUT\",\n body: JSON.stringify(data),\n });\n\n return result ?? data;\n }\n\n /**\n * Update data\n */\n async update<T extends Record<string, unknown>>(\n storeName: string,\n id: string,\n data: Partial<T>,\n idKey: string = \"id\",\n ): Promise<T> {\n const existing = await this.getData<T>(storeName, id, idKey);\n if (!existing) {\n throw new Error(\"Not found id:\" + id);\n }\n\n const updated = { ...existing, ...data };\n const url = this.buildUrl(storeName, id);\n\n const result = await this.request<T>(url, {\n method: \"PUT\",\n body: JSON.stringify(updated),\n });\n\n return result ?? updated;\n }\n\n /**\n * Delete data\n */\n async delete(\n storeName: string,\n id: string,\n _idKey: string = \"id\",\n ): Promise<void> {\n const url = this.buildUrl(storeName, id);\n\n await this.request(url, {\n method: \"DELETE\",\n });\n }\n\n /**\n * Get data by ID\n */\n async getData<T extends Record<string, unknown>>(\n storeName: string,\n id: string,\n _idKey: string = \"id\",\n ): Promise<T | null> {\n const url = this.buildUrl(storeName, id);\n\n const result = await this.request<T>(\n url,\n {\n method: \"GET\",\n },\n {\n allowNotFound: true,\n },\n );\n\n return result;\n }\n\n /**\n * Get list data (with pagination support)\n */\n async getList<T extends Record<string, unknown>>(\n storeName: string,\n options: QueryOptions<T> = {},\n _idKey: string = \"id\",\n ): Promise<PaginatedResult<T>> {\n const files = await this.listDirectory(storeName);\n\n const allData: T[] = [];\n for (const file of files) {\n try {\n const data = await this.request<T>(this.buildUrl(storeName, file), {\n method: \"GET\",\n });\n if (data) {\n allData.push(data);\n }\n } catch {}\n }\n\n let filteredData = allData;\n\n if (options.where) {\n filteredData = allData.filter((item) => {\n for (const key in options.where) {\n const itemValue = item[key];\n const whereValue = options.where![key];\n if (itemValue !== whereValue) {\n return false;\n }\n }\n return true;\n });\n }\n\n // Sorting\n if (options.sortBy) {\n filteredData = this.sortEntries(\n filteredData,\n String(options.sortBy),\n options.sortOrder,\n );\n }\n\n const totalCount = filteredData.length;\n const page = options.page ?? 1;\n const limit = options.limit ?? totalCount;\n\n const startIndex = (page - 1) * limit;\n const endIndex = startIndex + limit;\n const paginatedData = filteredData.slice(startIndex, endIndex);\n\n return {\n data: paginatedData,\n totalCount,\n page,\n limit,\n };\n }\n\n /**\n * Sort entries by specified field\n * @param entries Array of entries to sort\n * @param key Sorting field name\n * @param sortOrder Sort order: 'asc' or 'desc' (default: 'asc')\n * @returns Sorted array of entries\n */\n private sortEntries<T extends Record<string, unknown>>(\n entries: T[],\n key: string,\n sortOrder: \"asc\" | \"desc\" = \"asc\",\n ): T[] {\n return [...entries].sort((a, b) => {\n const aValue = a[key];\n const bValue = b[key];\n\n // Handle undefined or null values\n if (aValue == null && bValue == null) return 0;\n if (aValue == null) return 1; // null/undefined at the end\n if (bValue == null) return -1; // null/undefined at the end\n\n let comparison = 0;\n\n // Number type sorting\n if (typeof aValue === \"number\" && typeof bValue === \"number\") {\n comparison = aValue - bValue;\n }\n // String type sorting\n else if (typeof aValue === \"string\" && typeof bValue === \"string\") {\n comparison = aValue.localeCompare(bValue);\n }\n // Date type sorting (if string format date)\n else if (typeof aValue === \"string\" && typeof bValue === \"string\") {\n const aDate = new Date(aValue);\n const bDate = new Date(bValue);\n if (!isNaN(aDate.getTime()) && !isNaN(bDate.getTime())) {\n comparison = aDate.getTime() - bDate.getTime();\n } else {\n // Convert other types to string for comparison\n comparison = String(aValue).localeCompare(String(bValue));\n }\n }\n // Convert other types to string for comparison\n else {\n comparison = String(aValue).localeCompare(String(bValue));\n }\n\n // Apply sort order\n return sortOrder === \"desc\" ? -comparison : comparison;\n });\n }\n\n /**\n * Clear store\n */\n async clear(storeName: string): Promise<void> {\n const list = await this.getList(storeName);\n\n for (const item of list.data) {\n const id = String((item as Record<string, unknown>)[\"id\"]);\n await this.delete(storeName, id);\n }\n }\n\n /**\n * List directory contents\n */\n async listDirectory(path: string = \"\"): Promise<string[]> {\n if (!this.service) {\n throw new Error(\"Service not configured. Call setService() first.\");\n }\n\n const baseUrl = this.service.endpoint.replace(/\\/$/, \"\");\n const url = path ? `${baseUrl}/${path.replace(/^\\//, \"\")}` : baseUrl;\n\n const response = await fetch(url, {\n method: \"PROPFIND\",\n headers: {\n ...this.getHeaders(),\n Depth: \"1\",\n },\n });\n\n if (!response.ok) {\n throw new Error(`Failed to list directory: ${response.statusText}`);\n }\n\n const text = await response.text();\n\n const hrefRegex = /<d:href[^>]*>([^<]+)<\\/d:href>/gi;\n const files: string[] = [];\n let match;\n\n const baseUrlObj = new URL(baseUrl);\n const basePath = baseUrlObj.pathname;\n\n while ((match = hrefRegex.exec(text)) !== null) {\n const fullPath = match[1] || \"\";\n let relativePath = \"\";\n try {\n const hrefUrl = new URL(fullPath);\n relativePath = hrefUrl.pathname\n .replace(basePath, \"\")\n .replace(/^\\//, \"\");\n } catch {\n relativePath = fullPath.replace(basePath, \"\").replace(/^\\//, \"\");\n }\n if (relativePath && relativePath !== path.replace(/^\\//, \"\")) {\n files.push(relativePath);\n }\n }\n\n return files;\n }\n\n /**\n * Test connection\n */\n async testConnection(): Promise<boolean> {\n try {\n await this.listDirectory(\"\");\n return true;\n } catch {\n return false;\n }\n }\n}\n"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -214,10 +214,13 @@ var WebDAVAdapter = class {
214
214
  const basePath = new URL(baseUrl).pathname;
215
215
  while ((match = hrefRegex.exec(text)) !== null) {
216
216
  const fullPath = match[1] || "";
217
+ let relativePath = "";
217
218
  try {
218
- let relativePath = new URL(fullPath).pathname.replace(basePath, "").replace(/^\//, "");
219
- if (relativePath && relativePath !== path.replace(/^\//, "")) files.push(relativePath);
220
- } catch {}
219
+ relativePath = new URL(fullPath).pathname.replace(basePath, "").replace(/^\//, "");
220
+ } catch {
221
+ relativePath = fullPath.replace(basePath, "").replace(/^\//, "");
222
+ }
223
+ if (relativePath && relativePath !== path.replace(/^\//, "")) files.push(relativePath);
221
224
  }
222
225
  return files;
223
226
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["headers: Record<string, string>","allData: T[]","files: string[]"],"sources":["../src/webdav-adapter.ts"],"sourcesContent":["import {\n AdapterType,\n type RemoteAdapter,\n type QueryOptions,\n type PaginatedResult,\n type ServiceConfig,\n AuthType,\n} from '@omnistreamai/data-core';\n\n/**\n * WebDAV adapter implementation\n */\nexport class WebDAVAdapter implements RemoteAdapter {\n readonly type = AdapterType.Remote;\n readonly name = 'WebDAVAdapter';\n\n private service: ServiceConfig | null = null;\n\n /**\n * Set service configuration\n */\n setService(service: ServiceConfig): void {\n this.service = service;\n }\n\n /**\n * Get request headers\n */\n private getHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (!this.service?.authentication) {\n return headers;\n }\n\n const { authentication } = this.service;\n\n switch (authentication.authType) {\n case AuthType.Token:\n if (authentication.token) {\n headers['Authorization'] = `${authentication.token.token_type} ${authentication.token.access_token}`;\n }\n break;\n case AuthType.Bearer:\n if (authentication.bearer) {\n headers['Authorization'] = `Bearer ${authentication.bearer}`;\n }\n break;\n case AuthType.Basic:\n if (authentication.username && authentication.password) {\n const credentials = btoa(`${authentication.username}:${authentication.password}`);\n headers['Authorization'] = `Basic ${credentials}`;\n }\n break;\n }\n\n return headers;\n }\n\n /**\n * Build URL\n */\n private buildUrl(storeName: string, id?: string): string {\n if (!this.service) {\n throw new Error('Service not configured. Call setService() first.');\n }\n\n const baseUrl = this.service.endpoint.replace(/\\/$/, '');\n const storePath = `/${storeName}`;\n \n if (id) {\n return `${baseUrl}${storePath}/${id}`;\n }\n \n return `${baseUrl}${storePath}`;\n }\n\n /**\n * Send request\n */\n private async request<T>(\n url: string,\n options: RequestInit = {},\n {\n allowNotFound,\n }: {\n allowNotFound?: boolean;\n } = {}\n ): Promise<T | null> {\n const response = await fetch(url, {\n ...options,\n headers: {\n ...this.getHeaders(),\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n if (allowNotFound && response.status == 404) {\n return null;\n }\n throw new Error(`HTTP error! status: ${response.status}, message: ${response.statusText}`);\n }\n\n const text = await response.text();\n if (!text) {\n return null;\n }\n\n try {\n return JSON.parse(text) as T;\n } catch {\n return null;\n }\n }\n\n /**\n * Initialize store\n */\n async initStore(\n storeName: string,\n _indexes: string[] = [],\n _idKey: string = 'id'\n ): Promise<void> {\n const path = storeName.replace(/^\\/+|\\/+$/g, '');\n const parts = path.split('/');\n\n let currentPath = '';\n for (const part of parts) {\n currentPath = currentPath ? `${currentPath}/${part}` : part;\n\n try {\n await this.listDirectory(currentPath);\n } catch {\n const url = this.buildUrl(currentPath);\n\n const response = await fetch(url, {\n method: 'MKCOL',\n headers: this.getHeaders(),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to create store: ${response.statusText}`);\n }\n }\n }\n }\n\n /**\n * Add data\n */\n async add<T extends Record<string, unknown>>(\n storeName: string,\n data: T,\n idKey: string = 'id'\n ): Promise<T> {\n const id = String(data[idKey]);\n const url = this.buildUrl(storeName, id);\n\n const result = await this.request<T>(url, {\n method: 'PUT',\n body: JSON.stringify(data),\n });\n\n return result ?? data;\n }\n\n /**\n * Update data\n */\n async update<T extends Record<string, unknown>>(\n storeName: string,\n id: string,\n data: Partial<T>,\n idKey: string = 'id'\n ): Promise<T> {\n const existing = await this.getData<T>(storeName, id, idKey);\n if (!existing) {\n throw new Error('Not found id:' + id);\n }\n\n const updated = { ...existing, ...data };\n const url = this.buildUrl(storeName, id);\n\n const result = await this.request<T>(url, {\n method: 'PUT',\n body: JSON.stringify(updated),\n });\n\n return result ?? updated;\n }\n\n /**\n * Delete data\n */\n async delete(storeName: string, id: string, _idKey: string = 'id'): Promise<void> {\n const url = this.buildUrl(storeName, id);\n\n await this.request(url, {\n method: 'DELETE',\n });\n }\n\n /**\n * Get data by ID\n */\n async getData<T extends Record<string, unknown>>(\n storeName: string,\n id: string,\n _idKey: string = 'id'\n ): Promise<T | null> {\n const url = this.buildUrl(storeName, id);\n\n const result = await this.request<T>(url, {\n method: 'GET',\n }, {\n allowNotFound: true\n });\n\n return result;\n }\n\n /**\n * Get list data (with pagination support)\n */\n async getList<T extends Record<string, unknown>>(\n storeName: string,\n options: QueryOptions<T> = {},\n _idKey: string = 'id'\n ): Promise<PaginatedResult<T>> {\n const files = await this.listDirectory(storeName);\n\n const allData: T[] = [];\n for (const file of files) {\n try {\n const data = await this.request<T>(this.buildUrl(storeName, file), {\n method: 'GET',\n });\n if (data) {\n allData.push(data);\n }\n } catch {\n }\n }\n\n let filteredData = allData;\n\n if (options.where) {\n filteredData = allData.filter(item => {\n for (const key in options.where) {\n const itemValue = item[key];\n const whereValue = options.where![key];\n if (itemValue !== whereValue) {\n return false;\n }\n }\n return true;\n });\n }\n\n // Sorting\n if (options.sortBy) {\n filteredData = this.sortEntries(filteredData, String(options.sortBy), options.sortOrder);\n }\n\n const totalCount = filteredData.length;\n const page = options.page ?? 1;\n const limit = options.limit ?? totalCount;\n\n const startIndex = (page - 1) * limit;\n const endIndex = startIndex + limit;\n const paginatedData = filteredData.slice(startIndex, endIndex);\n\n return {\n data: paginatedData,\n totalCount,\n page,\n limit,\n };\n }\n\n /**\n * Sort entries by specified field\n * @param entries Array of entries to sort\n * @param key Sorting field name\n * @param sortOrder Sort order: 'asc' or 'desc' (default: 'asc')\n * @returns Sorted array of entries\n */\n private sortEntries<T extends Record<string, unknown>>(\n entries: T[],\n key: string,\n sortOrder: 'asc' | 'desc' = 'asc'\n ): T[] {\n return [...entries].sort((a, b) => {\n const aValue = a[key];\n const bValue = b[key];\n\n // Handle undefined or null values\n if (aValue == null && bValue == null) return 0;\n if (aValue == null) return 1; // null/undefined at the end\n if (bValue == null) return -1; // null/undefined at the end\n\n let comparison = 0;\n\n // Number type sorting\n if (typeof aValue === \"number\" && typeof bValue === \"number\") {\n comparison = aValue - bValue;\n }\n // String type sorting\n else if (typeof aValue === \"string\" && typeof bValue === \"string\") {\n comparison = aValue.localeCompare(bValue);\n }\n // Date type sorting (if string format date)\n else if (typeof aValue === \"string\" && typeof bValue === \"string\") {\n const aDate = new Date(aValue);\n const bDate = new Date(bValue);\n if (!isNaN(aDate.getTime()) && !isNaN(bDate.getTime())) {\n comparison = aDate.getTime() - bDate.getTime();\n } else {\n // Convert other types to string for comparison\n comparison = String(aValue).localeCompare(String(bValue));\n }\n }\n // Convert other types to string for comparison\n else {\n comparison = String(aValue).localeCompare(String(bValue));\n }\n\n // Apply sort order\n return sortOrder === 'desc' ? -comparison : comparison;\n });\n }\n\n\n\n /**\n * Clear store\n */\n async clear(storeName: string): Promise<void> {\n const list = await this.getList(storeName);\n\n for (const item of list.data) {\n const id = String((item as Record<string, unknown>)['id']);\n await this.delete(storeName, id);\n }\n }\n\n /**\n * List directory contents\n */\n async listDirectory(path: string = ''): Promise<string[]> {\n if (!this.service) {\n throw new Error('Service not configured. Call setService() first.');\n }\n\n const baseUrl = this.service.endpoint.replace(/\\/$/, '');\n const url = path ? `${baseUrl}/${path.replace(/^\\//, '')}` : baseUrl;\n\n const response = await fetch(url, {\n method: 'PROPFIND',\n headers: {\n ...this.getHeaders(),\n Depth: '1',\n },\n });\n\n if (!response.ok) {\n throw new Error(`Failed to list directory: ${response.statusText}`);\n }\n\n const text = await response.text();\n\n const hrefRegex = /<d:href[^>]*>([^<]+)<\\/d:href>/gi;\n const files: string[] = [];\n let match;\n\n const baseUrlObj = new URL(baseUrl);\n const basePath = baseUrlObj.pathname;\n\n while ((match = hrefRegex.exec(text)) !== null) {\n const fullPath = match[1] || '';\n try {\n const hrefUrl = new URL(fullPath);\n let relativePath = hrefUrl.pathname.replace(basePath, '').replace(/^\\//, '');\n\n if (relativePath && relativePath !== path.replace(/^\\//, '')) {\n files.push(relativePath);\n }\n } catch {\n }\n }\n\n return files;\n }\n\n /**\n * Test connection\n */\n async testConnection(): Promise<boolean> {\n try {\n await this.listDirectory('');\n return true;\n } catch {\n return false;\n }\n }\n}\n\n\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["headers: Record<string, string>","allData: T[]","files: string[]"],"sources":["../src/webdav-adapter.ts"],"sourcesContent":["import {\n AdapterType,\n type RemoteAdapter,\n type QueryOptions,\n type PaginatedResult,\n type ServiceConfig,\n AuthType,\n} from \"@omnistreamai/data-core\";\n\n/**\n * WebDAV adapter implementation\n */\nexport class WebDAVAdapter implements RemoteAdapter {\n readonly type = AdapterType.Remote;\n readonly name = \"WebDAVAdapter\";\n\n private service: ServiceConfig | null = null;\n\n /**\n * Set service configuration\n */\n setService(service: ServiceConfig): void {\n this.service = service;\n }\n\n /**\n * Get request headers\n */\n private getHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n if (!this.service?.authentication) {\n return headers;\n }\n\n const { authentication } = this.service;\n\n switch (authentication.authType) {\n case AuthType.Token:\n if (authentication.token) {\n headers[\"Authorization\"] =\n `${authentication.token.token_type} ${authentication.token.access_token}`;\n }\n break;\n case AuthType.Bearer:\n if (authentication.bearer) {\n headers[\"Authorization\"] = `Bearer ${authentication.bearer}`;\n }\n break;\n case AuthType.Basic:\n if (authentication.username && authentication.password) {\n const credentials = btoa(\n `${authentication.username}:${authentication.password}`,\n );\n headers[\"Authorization\"] = `Basic ${credentials}`;\n }\n break;\n }\n\n return headers;\n }\n\n /**\n * Build URL\n */\n private buildUrl(storeName: string, id?: string): string {\n if (!this.service) {\n throw new Error(\"Service not configured. Call setService() first.\");\n }\n\n const baseUrl = this.service.endpoint.replace(/\\/$/, \"\");\n const storePath = `/${storeName}`;\n\n if (id) {\n return `${baseUrl}${storePath}/${id}`;\n }\n\n return `${baseUrl}${storePath}`;\n }\n\n /**\n * Send request\n */\n private async request<T>(\n url: string,\n options: RequestInit = {},\n {\n allowNotFound,\n }: {\n allowNotFound?: boolean;\n } = {},\n ): Promise<T | null> {\n const response = await fetch(url, {\n ...options,\n headers: {\n ...this.getHeaders(),\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n if (allowNotFound && response.status == 404) {\n return null;\n }\n throw new Error(\n `HTTP error! status: ${response.status}, message: ${response.statusText}`,\n );\n }\n\n const text = await response.text();\n if (!text) {\n return null;\n }\n\n try {\n return JSON.parse(text) as T;\n } catch {\n return null;\n }\n }\n\n /**\n * Initialize store\n */\n async initStore(\n storeName: string,\n _indexes: string[] = [],\n _idKey: string = \"id\",\n ): Promise<void> {\n const path = storeName.replace(/^\\/+|\\/+$/g, \"\");\n const parts = path.split(\"/\");\n\n let currentPath = \"\";\n for (const part of parts) {\n currentPath = currentPath ? `${currentPath}/${part}` : part;\n\n try {\n await this.listDirectory(currentPath);\n } catch {\n const url = this.buildUrl(currentPath);\n\n const response = await fetch(url, {\n method: \"MKCOL\",\n headers: this.getHeaders(),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to create store: ${response.statusText}`);\n }\n }\n }\n }\n\n /**\n * Add data\n */\n async add<T extends Record<string, unknown>>(\n storeName: string,\n data: T,\n idKey: string = \"id\",\n ): Promise<T> {\n const id = String(data[idKey]);\n const url = this.buildUrl(storeName, id);\n\n const result = await this.request<T>(url, {\n method: \"PUT\",\n body: JSON.stringify(data),\n });\n\n return result ?? data;\n }\n\n /**\n * Update data\n */\n async update<T extends Record<string, unknown>>(\n storeName: string,\n id: string,\n data: Partial<T>,\n idKey: string = \"id\",\n ): Promise<T> {\n const existing = await this.getData<T>(storeName, id, idKey);\n if (!existing) {\n throw new Error(\"Not found id:\" + id);\n }\n\n const updated = { ...existing, ...data };\n const url = this.buildUrl(storeName, id);\n\n const result = await this.request<T>(url, {\n method: \"PUT\",\n body: JSON.stringify(updated),\n });\n\n return result ?? updated;\n }\n\n /**\n * Delete data\n */\n async delete(\n storeName: string,\n id: string,\n _idKey: string = \"id\",\n ): Promise<void> {\n const url = this.buildUrl(storeName, id);\n\n await this.request(url, {\n method: \"DELETE\",\n });\n }\n\n /**\n * Get data by ID\n */\n async getData<T extends Record<string, unknown>>(\n storeName: string,\n id: string,\n _idKey: string = \"id\",\n ): Promise<T | null> {\n const url = this.buildUrl(storeName, id);\n\n const result = await this.request<T>(\n url,\n {\n method: \"GET\",\n },\n {\n allowNotFound: true,\n },\n );\n\n return result;\n }\n\n /**\n * Get list data (with pagination support)\n */\n async getList<T extends Record<string, unknown>>(\n storeName: string,\n options: QueryOptions<T> = {},\n _idKey: string = \"id\",\n ): Promise<PaginatedResult<T>> {\n const files = await this.listDirectory(storeName);\n\n const allData: T[] = [];\n for (const file of files) {\n try {\n const data = await this.request<T>(this.buildUrl(storeName, file), {\n method: \"GET\",\n });\n if (data) {\n allData.push(data);\n }\n } catch {}\n }\n\n let filteredData = allData;\n\n if (options.where) {\n filteredData = allData.filter((item) => {\n for (const key in options.where) {\n const itemValue = item[key];\n const whereValue = options.where![key];\n if (itemValue !== whereValue) {\n return false;\n }\n }\n return true;\n });\n }\n\n // Sorting\n if (options.sortBy) {\n filteredData = this.sortEntries(\n filteredData,\n String(options.sortBy),\n options.sortOrder,\n );\n }\n\n const totalCount = filteredData.length;\n const page = options.page ?? 1;\n const limit = options.limit ?? totalCount;\n\n const startIndex = (page - 1) * limit;\n const endIndex = startIndex + limit;\n const paginatedData = filteredData.slice(startIndex, endIndex);\n\n return {\n data: paginatedData,\n totalCount,\n page,\n limit,\n };\n }\n\n /**\n * Sort entries by specified field\n * @param entries Array of entries to sort\n * @param key Sorting field name\n * @param sortOrder Sort order: 'asc' or 'desc' (default: 'asc')\n * @returns Sorted array of entries\n */\n private sortEntries<T extends Record<string, unknown>>(\n entries: T[],\n key: string,\n sortOrder: \"asc\" | \"desc\" = \"asc\",\n ): T[] {\n return [...entries].sort((a, b) => {\n const aValue = a[key];\n const bValue = b[key];\n\n // Handle undefined or null values\n if (aValue == null && bValue == null) return 0;\n if (aValue == null) return 1; // null/undefined at the end\n if (bValue == null) return -1; // null/undefined at the end\n\n let comparison = 0;\n\n // Number type sorting\n if (typeof aValue === \"number\" && typeof bValue === \"number\") {\n comparison = aValue - bValue;\n }\n // String type sorting\n else if (typeof aValue === \"string\" && typeof bValue === \"string\") {\n comparison = aValue.localeCompare(bValue);\n }\n // Date type sorting (if string format date)\n else if (typeof aValue === \"string\" && typeof bValue === \"string\") {\n const aDate = new Date(aValue);\n const bDate = new Date(bValue);\n if (!isNaN(aDate.getTime()) && !isNaN(bDate.getTime())) {\n comparison = aDate.getTime() - bDate.getTime();\n } else {\n // Convert other types to string for comparison\n comparison = String(aValue).localeCompare(String(bValue));\n }\n }\n // Convert other types to string for comparison\n else {\n comparison = String(aValue).localeCompare(String(bValue));\n }\n\n // Apply sort order\n return sortOrder === \"desc\" ? -comparison : comparison;\n });\n }\n\n /**\n * Clear store\n */\n async clear(storeName: string): Promise<void> {\n const list = await this.getList(storeName);\n\n for (const item of list.data) {\n const id = String((item as Record<string, unknown>)[\"id\"]);\n await this.delete(storeName, id);\n }\n }\n\n /**\n * List directory contents\n */\n async listDirectory(path: string = \"\"): Promise<string[]> {\n if (!this.service) {\n throw new Error(\"Service not configured. Call setService() first.\");\n }\n\n const baseUrl = this.service.endpoint.replace(/\\/$/, \"\");\n const url = path ? `${baseUrl}/${path.replace(/^\\//, \"\")}` : baseUrl;\n\n const response = await fetch(url, {\n method: \"PROPFIND\",\n headers: {\n ...this.getHeaders(),\n Depth: \"1\",\n },\n });\n\n if (!response.ok) {\n throw new Error(`Failed to list directory: ${response.statusText}`);\n }\n\n const text = await response.text();\n\n const hrefRegex = /<d:href[^>]*>([^<]+)<\\/d:href>/gi;\n const files: string[] = [];\n let match;\n\n const baseUrlObj = new URL(baseUrl);\n const basePath = baseUrlObj.pathname;\n\n while ((match = hrefRegex.exec(text)) !== null) {\n const fullPath = match[1] || \"\";\n let relativePath = \"\";\n try {\n const hrefUrl = new URL(fullPath);\n relativePath = hrefUrl.pathname\n .replace(basePath, \"\")\n .replace(/^\\//, \"\");\n } catch {\n relativePath = fullPath.replace(basePath, \"\").replace(/^\\//, \"\");\n }\n if (relativePath && relativePath !== path.replace(/^\\//, \"\")) {\n files.push(relativePath);\n }\n }\n\n return files;\n }\n\n /**\n * Test connection\n */\n async testConnection(): Promise<boolean> {\n try {\n await this.listDirectory(\"\");\n return true;\n } catch {\n return false;\n }\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnistreamai/data-adapter-webdav",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",
@@ -5,14 +5,14 @@ import {
5
5
  type PaginatedResult,
6
6
  type ServiceConfig,
7
7
  AuthType,
8
- } from '@omnistreamai/data-core';
8
+ } from "@omnistreamai/data-core";
9
9
 
10
10
  /**
11
11
  * WebDAV adapter implementation
12
12
  */
13
13
  export class WebDAVAdapter implements RemoteAdapter {
14
14
  readonly type = AdapterType.Remote;
15
- readonly name = 'WebDAVAdapter';
15
+ readonly name = "WebDAVAdapter";
16
16
 
17
17
  private service: ServiceConfig | null = null;
18
18
 
@@ -28,7 +28,7 @@ export class WebDAVAdapter implements RemoteAdapter {
28
28
  */
29
29
  private getHeaders(): Record<string, string> {
30
30
  const headers: Record<string, string> = {
31
- 'Content-Type': 'application/json',
31
+ "Content-Type": "application/json",
32
32
  };
33
33
 
34
34
  if (!this.service?.authentication) {
@@ -40,18 +40,21 @@ export class WebDAVAdapter implements RemoteAdapter {
40
40
  switch (authentication.authType) {
41
41
  case AuthType.Token:
42
42
  if (authentication.token) {
43
- headers['Authorization'] = `${authentication.token.token_type} ${authentication.token.access_token}`;
43
+ headers["Authorization"] =
44
+ `${authentication.token.token_type} ${authentication.token.access_token}`;
44
45
  }
45
46
  break;
46
47
  case AuthType.Bearer:
47
48
  if (authentication.bearer) {
48
- headers['Authorization'] = `Bearer ${authentication.bearer}`;
49
+ headers["Authorization"] = `Bearer ${authentication.bearer}`;
49
50
  }
50
51
  break;
51
52
  case AuthType.Basic:
52
53
  if (authentication.username && authentication.password) {
53
- const credentials = btoa(`${authentication.username}:${authentication.password}`);
54
- headers['Authorization'] = `Basic ${credentials}`;
54
+ const credentials = btoa(
55
+ `${authentication.username}:${authentication.password}`,
56
+ );
57
+ headers["Authorization"] = `Basic ${credentials}`;
55
58
  }
56
59
  break;
57
60
  }
@@ -64,16 +67,16 @@ export class WebDAVAdapter implements RemoteAdapter {
64
67
  */
65
68
  private buildUrl(storeName: string, id?: string): string {
66
69
  if (!this.service) {
67
- throw new Error('Service not configured. Call setService() first.');
70
+ throw new Error("Service not configured. Call setService() first.");
68
71
  }
69
72
 
70
- const baseUrl = this.service.endpoint.replace(/\/$/, '');
73
+ const baseUrl = this.service.endpoint.replace(/\/$/, "");
71
74
  const storePath = `/${storeName}`;
72
-
75
+
73
76
  if (id) {
74
77
  return `${baseUrl}${storePath}/${id}`;
75
78
  }
76
-
79
+
77
80
  return `${baseUrl}${storePath}`;
78
81
  }
79
82
 
@@ -87,7 +90,7 @@ export class WebDAVAdapter implements RemoteAdapter {
87
90
  allowNotFound,
88
91
  }: {
89
92
  allowNotFound?: boolean;
90
- } = {}
93
+ } = {},
91
94
  ): Promise<T | null> {
92
95
  const response = await fetch(url, {
93
96
  ...options,
@@ -101,7 +104,9 @@ export class WebDAVAdapter implements RemoteAdapter {
101
104
  if (allowNotFound && response.status == 404) {
102
105
  return null;
103
106
  }
104
- throw new Error(`HTTP error! status: ${response.status}, message: ${response.statusText}`);
107
+ throw new Error(
108
+ `HTTP error! status: ${response.status}, message: ${response.statusText}`,
109
+ );
105
110
  }
106
111
 
107
112
  const text = await response.text();
@@ -122,12 +127,12 @@ export class WebDAVAdapter implements RemoteAdapter {
122
127
  async initStore(
123
128
  storeName: string,
124
129
  _indexes: string[] = [],
125
- _idKey: string = 'id'
130
+ _idKey: string = "id",
126
131
  ): Promise<void> {
127
- const path = storeName.replace(/^\/+|\/+$/g, '');
128
- const parts = path.split('/');
132
+ const path = storeName.replace(/^\/+|\/+$/g, "");
133
+ const parts = path.split("/");
129
134
 
130
- let currentPath = '';
135
+ let currentPath = "";
131
136
  for (const part of parts) {
132
137
  currentPath = currentPath ? `${currentPath}/${part}` : part;
133
138
 
@@ -137,7 +142,7 @@ export class WebDAVAdapter implements RemoteAdapter {
137
142
  const url = this.buildUrl(currentPath);
138
143
 
139
144
  const response = await fetch(url, {
140
- method: 'MKCOL',
145
+ method: "MKCOL",
141
146
  headers: this.getHeaders(),
142
147
  });
143
148
 
@@ -154,13 +159,13 @@ export class WebDAVAdapter implements RemoteAdapter {
154
159
  async add<T extends Record<string, unknown>>(
155
160
  storeName: string,
156
161
  data: T,
157
- idKey: string = 'id'
162
+ idKey: string = "id",
158
163
  ): Promise<T> {
159
164
  const id = String(data[idKey]);
160
165
  const url = this.buildUrl(storeName, id);
161
166
 
162
167
  const result = await this.request<T>(url, {
163
- method: 'PUT',
168
+ method: "PUT",
164
169
  body: JSON.stringify(data),
165
170
  });
166
171
 
@@ -174,18 +179,18 @@ export class WebDAVAdapter implements RemoteAdapter {
174
179
  storeName: string,
175
180
  id: string,
176
181
  data: Partial<T>,
177
- idKey: string = 'id'
182
+ idKey: string = "id",
178
183
  ): Promise<T> {
179
184
  const existing = await this.getData<T>(storeName, id, idKey);
180
185
  if (!existing) {
181
- throw new Error('Not found id:' + id);
186
+ throw new Error("Not found id:" + id);
182
187
  }
183
188
 
184
189
  const updated = { ...existing, ...data };
185
190
  const url = this.buildUrl(storeName, id);
186
191
 
187
192
  const result = await this.request<T>(url, {
188
- method: 'PUT',
193
+ method: "PUT",
189
194
  body: JSON.stringify(updated),
190
195
  });
191
196
 
@@ -195,11 +200,15 @@ export class WebDAVAdapter implements RemoteAdapter {
195
200
  /**
196
201
  * Delete data
197
202
  */
198
- async delete(storeName: string, id: string, _idKey: string = 'id'): Promise<void> {
203
+ async delete(
204
+ storeName: string,
205
+ id: string,
206
+ _idKey: string = "id",
207
+ ): Promise<void> {
199
208
  const url = this.buildUrl(storeName, id);
200
209
 
201
210
  await this.request(url, {
202
- method: 'DELETE',
211
+ method: "DELETE",
203
212
  });
204
213
  }
205
214
 
@@ -209,15 +218,19 @@ export class WebDAVAdapter implements RemoteAdapter {
209
218
  async getData<T extends Record<string, unknown>>(
210
219
  storeName: string,
211
220
  id: string,
212
- _idKey: string = 'id'
221
+ _idKey: string = "id",
213
222
  ): Promise<T | null> {
214
223
  const url = this.buildUrl(storeName, id);
215
224
 
216
- const result = await this.request<T>(url, {
217
- method: 'GET',
218
- }, {
219
- allowNotFound: true
220
- });
225
+ const result = await this.request<T>(
226
+ url,
227
+ {
228
+ method: "GET",
229
+ },
230
+ {
231
+ allowNotFound: true,
232
+ },
233
+ );
221
234
 
222
235
  return result;
223
236
  }
@@ -228,7 +241,7 @@ export class WebDAVAdapter implements RemoteAdapter {
228
241
  async getList<T extends Record<string, unknown>>(
229
242
  storeName: string,
230
243
  options: QueryOptions<T> = {},
231
- _idKey: string = 'id'
244
+ _idKey: string = "id",
232
245
  ): Promise<PaginatedResult<T>> {
233
246
  const files = await this.listDirectory(storeName);
234
247
 
@@ -236,19 +249,18 @@ export class WebDAVAdapter implements RemoteAdapter {
236
249
  for (const file of files) {
237
250
  try {
238
251
  const data = await this.request<T>(this.buildUrl(storeName, file), {
239
- method: 'GET',
252
+ method: "GET",
240
253
  });
241
254
  if (data) {
242
255
  allData.push(data);
243
256
  }
244
- } catch {
245
- }
257
+ } catch {}
246
258
  }
247
259
 
248
260
  let filteredData = allData;
249
261
 
250
262
  if (options.where) {
251
- filteredData = allData.filter(item => {
263
+ filteredData = allData.filter((item) => {
252
264
  for (const key in options.where) {
253
265
  const itemValue = item[key];
254
266
  const whereValue = options.where![key];
@@ -262,7 +274,11 @@ export class WebDAVAdapter implements RemoteAdapter {
262
274
 
263
275
  // Sorting
264
276
  if (options.sortBy) {
265
- filteredData = this.sortEntries(filteredData, String(options.sortBy), options.sortOrder);
277
+ filteredData = this.sortEntries(
278
+ filteredData,
279
+ String(options.sortBy),
280
+ options.sortOrder,
281
+ );
266
282
  }
267
283
 
268
284
  const totalCount = filteredData.length;
@@ -291,7 +307,7 @@ export class WebDAVAdapter implements RemoteAdapter {
291
307
  private sortEntries<T extends Record<string, unknown>>(
292
308
  entries: T[],
293
309
  key: string,
294
- sortOrder: 'asc' | 'desc' = 'asc'
310
+ sortOrder: "asc" | "desc" = "asc",
295
311
  ): T[] {
296
312
  return [...entries].sort((a, b) => {
297
313
  const aValue = a[key];
@@ -329,12 +345,10 @@ export class WebDAVAdapter implements RemoteAdapter {
329
345
  }
330
346
 
331
347
  // Apply sort order
332
- return sortOrder === 'desc' ? -comparison : comparison;
348
+ return sortOrder === "desc" ? -comparison : comparison;
333
349
  });
334
350
  }
335
351
 
336
-
337
-
338
352
  /**
339
353
  * Clear store
340
354
  */
@@ -342,7 +356,7 @@ export class WebDAVAdapter implements RemoteAdapter {
342
356
  const list = await this.getList(storeName);
343
357
 
344
358
  for (const item of list.data) {
345
- const id = String((item as Record<string, unknown>)['id']);
359
+ const id = String((item as Record<string, unknown>)["id"]);
346
360
  await this.delete(storeName, id);
347
361
  }
348
362
  }
@@ -350,19 +364,19 @@ export class WebDAVAdapter implements RemoteAdapter {
350
364
  /**
351
365
  * List directory contents
352
366
  */
353
- async listDirectory(path: string = ''): Promise<string[]> {
367
+ async listDirectory(path: string = ""): Promise<string[]> {
354
368
  if (!this.service) {
355
- throw new Error('Service not configured. Call setService() first.');
369
+ throw new Error("Service not configured. Call setService() first.");
356
370
  }
357
371
 
358
- const baseUrl = this.service.endpoint.replace(/\/$/, '');
359
- const url = path ? `${baseUrl}/${path.replace(/^\//, '')}` : baseUrl;
372
+ const baseUrl = this.service.endpoint.replace(/\/$/, "");
373
+ const url = path ? `${baseUrl}/${path.replace(/^\//, "")}` : baseUrl;
360
374
 
361
375
  const response = await fetch(url, {
362
- method: 'PROPFIND',
376
+ method: "PROPFIND",
363
377
  headers: {
364
378
  ...this.getHeaders(),
365
- Depth: '1',
379
+ Depth: "1",
366
380
  },
367
381
  });
368
382
 
@@ -380,15 +394,18 @@ export class WebDAVAdapter implements RemoteAdapter {
380
394
  const basePath = baseUrlObj.pathname;
381
395
 
382
396
  while ((match = hrefRegex.exec(text)) !== null) {
383
- const fullPath = match[1] || '';
397
+ const fullPath = match[1] || "";
398
+ let relativePath = "";
384
399
  try {
385
400
  const hrefUrl = new URL(fullPath);
386
- let relativePath = hrefUrl.pathname.replace(basePath, '').replace(/^\//, '');
387
-
388
- if (relativePath && relativePath !== path.replace(/^\//, '')) {
389
- files.push(relativePath);
390
- }
401
+ relativePath = hrefUrl.pathname
402
+ .replace(basePath, "")
403
+ .replace(/^\//, "");
391
404
  } catch {
405
+ relativePath = fullPath.replace(basePath, "").replace(/^\//, "");
406
+ }
407
+ if (relativePath && relativePath !== path.replace(/^\//, "")) {
408
+ files.push(relativePath);
392
409
  }
393
410
  }
394
411
 
@@ -400,12 +417,10 @@ export class WebDAVAdapter implements RemoteAdapter {
400
417
  */
401
418
  async testConnection(): Promise<boolean> {
402
419
  try {
403
- await this.listDirectory('');
420
+ await this.listDirectory("");
404
421
  return true;
405
422
  } catch {
406
423
  return false;
407
424
  }
408
425
  }
409
426
  }
410
-
411
-