@omnistreamai/data-adapter-webdav 0.4.1 → 0.4.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnistreamai/data-adapter-webdav",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",
@@ -12,8 +12,14 @@
12
12
  }
13
13
  },
14
14
  "dependencies": {
15
- "@omnistreamai/data-core": "0.4.1"
15
+ "@omnistreamai/data-core": "0.4.2"
16
16
  },
17
+ "keywords": [
18
+ "tanstack-intent"
19
+ ],
20
+ "files": [
21
+ "skills"
22
+ ],
17
23
  "scripts": {
18
24
  "build": "tsdown",
19
25
  "test": "vitest run",
@@ -0,0 +1,186 @@
1
+ ---
2
+ name: configure-webdav-adapter
3
+ description: >
4
+ Configure WebDAVAdapter for remote synchronization. Use setService to configure
5
+ endpoint and authentication. Support Token, Basic, and Bearer auth types.
6
+ Set basePath for nested WebDAV paths. Call setService before any operations.
7
+ Use testConnection to verify setup.
8
+ type: core
9
+ library: '@omnistreamai/data-sync'
10
+ library_version: '0.4.1'
11
+ sources:
12
+ - 'omni-stream-ai/data-sync:packages/adapter-webdav/src/webdav-adapter.ts'
13
+ - 'omni-stream-ai/data-sync:packages/core/src/types.ts'
14
+ ---
15
+
16
+ # Configure WebDAV Adapter
17
+
18
+ Set up WebDAV for remote data synchronization with authentication.
19
+
20
+ ## Setup
21
+
22
+ ```typescript
23
+ import { WebDAVAdapter } from '@omnistreamai/data-adapter-webdav';
24
+ import { AuthType } from '@omnistreamai/data-core';
25
+
26
+ const adapter = new WebDAVAdapter();
27
+ adapter.setService({
28
+ endpoint: 'https://webdav.example.com/dav',
29
+ basePath: 'my-app/data', // Optional nested path
30
+ authentication: {
31
+ authType: AuthType.Bearer,
32
+ bearer: 'your-token-here',
33
+ },
34
+ });
35
+
36
+ // Verify connection before use
37
+ const connected = await adapter.testConnection();
38
+ console.log('WebDAV connection:', connected);
39
+ ```
40
+
41
+ ## Core Patterns
42
+
43
+ ### Bearer token authentication
44
+
45
+ ```typescript
46
+ adapter.setService({
47
+ endpoint: 'https://webdav.example.com/dav',
48
+ authentication: {
49
+ authType: AuthType.Bearer,
50
+ bearer: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
51
+ },
52
+ });
53
+ ```
54
+
55
+ ### Basic authentication
56
+
57
+ ```typescript
58
+ adapter.setService({
59
+ endpoint: 'https://webdav.example.com/dav',
60
+ authentication: {
61
+ authType: AuthType.Basic,
62
+ username: 'user',
63
+ password: 'password',
64
+ },
65
+ });
66
+ ```
67
+
68
+ ### Token authentication (OAuth-style)
69
+
70
+ ```typescript
71
+ adapter.setService({
72
+ endpoint: 'https://webdav.example.com/dav',
73
+ authentication: {
74
+ authType: AuthType.Token,
75
+ token: {
76
+ access_token: 'ya29.a0AfH6...',
77
+ token_type: 'Bearer',
78
+ expires_in: 3600,
79
+ refresh_token: '1//0g...',
80
+ },
81
+ },
82
+ });
83
+ ```
84
+
85
+ ### Use basePath for nested WebDAV directories
86
+
87
+ ```typescript
88
+ adapter.setService({
89
+ endpoint: 'https://webdav.example.com/dav',
90
+ basePath: 'users/123/data', // Creates/uses path: /dav/users/123/data/{store}.json
91
+ });
92
+ ```
93
+
94
+ ### List directory contents
95
+
96
+ ```typescript
97
+ const files = await adapter.listDirectory('');
98
+ console.log(files); // ['todos.json', 'users.json', 'settings.json']
99
+
100
+ const nested = await adapter.listDirectory('users/123');
101
+ ```
102
+
103
+ ## Common Mistakes
104
+
105
+ ### HIGH Call methods before setService
106
+
107
+ Wrong:
108
+
109
+ ```typescript
110
+ const adapter = new WebDAVAdapter();
111
+ await adapter.add('todos', { id: '1', title: 'Test' }); // ❌ Throws
112
+ // Error: "Service not configured. Call setService() first."
113
+ ```
114
+
115
+ Correct:
116
+
117
+ ```typescript
118
+ const adapter = new WebDAVAdapter();
119
+ adapter.setService({ endpoint: 'https://webdav.example.com/dav' });
120
+ await adapter.add('todos', { id: '1', title: 'Test' }); // ✅
121
+ ```
122
+
123
+ All adapter methods (`add`, `update`, `delete`, `getData`, `getList`) call `buildUrl()` which requires `service` to be configured. Calling any method before `setService()` throws.
124
+
125
+ Source: packages/adapter-webdav/src/webdav-adapter.ts:68-76
126
+
127
+ ---
128
+
129
+ ### HIGH Use wrong basePath format
130
+
131
+ Wrong:
132
+
133
+ ```typescript
134
+ // Double slashes or leading/trailing slashes cause issues
135
+ adapter.setService({
136
+ endpoint: 'https://webdav.example.com/dav/',
137
+ basePath: '/my-app/data/', // ❌ Leading slash and trailing slash
138
+ });
139
+ // Result: https://webdav.example.com/dav//my-app/data//todos.json
140
+ ```
141
+
142
+ Correct:
143
+
144
+ ```typescript
145
+ // basePath is normalized — leading/trailing slashes are stripped
146
+ adapter.setService({
147
+ endpoint: 'https://webdav.example.com/dav',
148
+ basePath: 'my-app/data', // ✅ Clean path
149
+ });
150
+ // Result: https://webdav.example.com/dav/my-app/data/todos.json
151
+ ```
152
+
153
+ Source: packages/adapter-webdav/src/webdav-adapter.ts:73-74
154
+
155
+ ---
156
+
157
+ ### MEDIUM Assume WebDAV server supports all HTTP methods
158
+
159
+ Wrong:
160
+
161
+ ```typescript
162
+ // Assuming delete works the same as on local storage
163
+ await adapter.delete('todos', '1');
164
+ // May throw if WebDAV server doesn't support DELETE or has restrictions
165
+ ```
166
+
167
+ Correct:
168
+
169
+ ```typescript
170
+ // 405 Method Not Allowed is NOT retried — it's a permanent failure
171
+ // Only retryable: 429, 500, 502, 503, 504
172
+ // Always implement error handling:
173
+ try {
174
+ await adapter.delete('todos', '1');
175
+ } catch (error) {
176
+ if (error.message.includes('405')) {
177
+ console.error('DELETE not supported by this WebDAV server');
178
+ }
179
+ }
180
+ ```
181
+
182
+ WebDAV servers vary in what methods they support. 405 responses are permanent failures (not queued for retry).
183
+
184
+ Source: packages/adapter-webdav/src/webdav-adapter.ts:110-113
185
+
186
+ See also: data-sync/offline-sync
package/CHANGELOG.md DELETED
@@ -1,59 +0,0 @@
1
- # @omnistreamai/data-adapter-webdav
2
-
3
- ## 0.4.1
4
-
5
- ### Patch Changes
6
-
7
- - Updated dependencies
8
- - @omnistreamai/data-core@0.4.1
9
-
10
- ## 0.3.1
11
-
12
- ### Patch Changes
13
-
14
- - Updated dependencies
15
- - @omnistreamai/data-core@0.3.1
16
-
17
- ## 0.3.0
18
-
19
- ### Minor Changes
20
-
21
- - 4825f5d: Support nested paths (e.g., /test1/test2) when initializing WebDAV stores. Store's adapter setters now return a Promise and automatically reinitialize when changing adapters.
22
-
23
- ### Patch Changes
24
-
25
- - Updated dependencies [4825f5d]
26
- - @omnistreamai/data-core@0.3.0
27
-
28
- ## 0.2.1
29
-
30
- ### Patch Changes
31
-
32
- - 修复 WebDAV 适配器在初始化存储时先检查路径是否存在,避免重复创建的问题。
33
-
34
- ## 0.2.0
35
-
36
- ### Minor Changes
37
-
38
- - Add sorting support to getList method with sortBy and sortOrder options.
39
-
40
- ### Patch Changes
41
-
42
- - Updated dependencies
43
- - @omnistreamai/data-core@0.2.0
44
-
45
- ## 0.1.2
46
-
47
- ### Patch Changes
48
-
49
- - Initial release of data-sync packages
50
- - Updated dependencies
51
- - @omnistreamai/data-core@0.1.2
52
-
53
- ## 0.1.1
54
-
55
- ### Patch Changes
56
-
57
- - Publish beta version with ESM and CJS support
58
- - Updated dependencies
59
- - @omnistreamai/data-core@0.1.1
package/dist/index.d.mts DELETED
@@ -1,79 +0,0 @@
1
- import { AdapterType, PaginatedResult, QueryOptions, RemoteAdapter, ServiceConfig } from "@omnistreamai/data-core";
2
-
3
- //#region src/webdav-adapter.d.ts
4
-
5
- /**
6
- * WebDAV adapter implementation
7
- */
8
- declare class WebDAVAdapter implements RemoteAdapter {
9
- readonly type = AdapterType.Remote;
10
- readonly name = "WebDAVAdapter";
11
- private service;
12
- /**
13
- * Set service configuration
14
- */
15
- setService(service: ServiceConfig): void;
16
- /**
17
- * Get request headers
18
- */
19
- private getHeaders;
20
- /**
21
- * Build URL
22
- */
23
- private buildUrl;
24
- /**
25
- * Send request
26
- */
27
- private request;
28
- /**
29
- * Create path if not exists
30
- */
31
- private createPathIfNotExists;
32
- /**
33
- * Initialize store
34
- */
35
- initStore(_storeName: string, _indexes?: string[], _idKey?: string): Promise<void>;
36
- /**
37
- * Add data
38
- */
39
- add<T extends Record<string, unknown>>(storeName: string, data: T, _idKey?: string): Promise<T>;
40
- /**
41
- * Update data
42
- */
43
- update<T extends Record<string, unknown>>(storeName: string, id: string, data: Partial<T>, idKey?: string): Promise<T>;
44
- /**
45
- * Delete data
46
- */
47
- delete(storeName: string, id: string, idKey?: string): Promise<void>;
48
- /**
49
- * Get data by ID
50
- */
51
- getData<T extends Record<string, unknown>>(storeName: string, id: string, idKey?: string): Promise<T | null>;
52
- /**
53
- * Get list data (with pagination support)
54
- */
55
- getList<T extends Record<string, unknown>>(storeName: string, options?: QueryOptions<T>, _idKey?: string): Promise<PaginatedResult<T>>;
56
- /**
57
- * Sort entries by specified field
58
- * @param entries Array of entries to sort
59
- * @param key Sorting field name
60
- * @param sortOrder Sort order: 'asc' or 'desc' (default: 'asc')
61
- * @returns Sorted array of entries
62
- */
63
- private sortEntries;
64
- /**
65
- * Clear store
66
- */
67
- clear(storeName: string): Promise<void>;
68
- /**
69
- * List directory contents
70
- */
71
- listDirectory(path?: string): Promise<string[]>;
72
- /**
73
- * Test connection
74
- */
75
- testConnection(): Promise<boolean>;
76
- }
77
- //#endregion
78
- export { WebDAVAdapter };
79
- //# sourceMappingURL=index.d.mts.map
@@ -1 +0,0 @@
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;EAiJV,SAAA,IAAA,GAAA,eAAA;EAMiB,QAAA,OAAA;EAEZ;;;EAyBe,UAAA,CAAA,OAAA,EA1KH,aA0KG,CAAA,EAAA,IAAA;EAGP;;;EAEb,QAAA,UAAA;EAiCA;;;EA+BA,QAAA,QAAA;EAoBqB;;;EAIG,QAAA,OAAA;EAAhB;;;EAoH6B,QAAA,qBAAA;EAiDhB;;;uEAnSrB;;;;gBAMiB,kDAEZ,qBAEL,QAAQ;;;;mBAuBY,8DAGf,QAAQ,qBAEb,QAAQ;;;;yDAiCR;;;;oBA2BqB,yEAIrB,QAAQ;;;;oBAoBa,sDAEb,aAAa,sBAErB,QAAQ,gBAAgB;;;;;;;;;;;;4BAwGK;;;;gCAYQ;;;;oBAiDhB"}
package/dist/index.d.ts DELETED
@@ -1,79 +0,0 @@
1
- import { AdapterType, PaginatedResult, QueryOptions, RemoteAdapter, ServiceConfig } from "@omnistreamai/data-core";
2
-
3
- //#region src/webdav-adapter.d.ts
4
-
5
- /**
6
- * WebDAV adapter implementation
7
- */
8
- declare class WebDAVAdapter implements RemoteAdapter {
9
- readonly type = AdapterType.Remote;
10
- readonly name = "WebDAVAdapter";
11
- private service;
12
- /**
13
- * Set service configuration
14
- */
15
- setService(service: ServiceConfig): void;
16
- /**
17
- * Get request headers
18
- */
19
- private getHeaders;
20
- /**
21
- * Build URL
22
- */
23
- private buildUrl;
24
- /**
25
- * Send request
26
- */
27
- private request;
28
- /**
29
- * Create path if not exists
30
- */
31
- private createPathIfNotExists;
32
- /**
33
- * Initialize store
34
- */
35
- initStore(_storeName: string, _indexes?: string[], _idKey?: string): Promise<void>;
36
- /**
37
- * Add data
38
- */
39
- add<T extends Record<string, unknown>>(storeName: string, data: T, _idKey?: string): Promise<T>;
40
- /**
41
- * Update data
42
- */
43
- update<T extends Record<string, unknown>>(storeName: string, id: string, data: Partial<T>, idKey?: string): Promise<T>;
44
- /**
45
- * Delete data
46
- */
47
- delete(storeName: string, id: string, idKey?: string): Promise<void>;
48
- /**
49
- * Get data by ID
50
- */
51
- getData<T extends Record<string, unknown>>(storeName: string, id: string, idKey?: string): Promise<T | null>;
52
- /**
53
- * Get list data (with pagination support)
54
- */
55
- getList<T extends Record<string, unknown>>(storeName: string, options?: QueryOptions<T>, _idKey?: string): Promise<PaginatedResult<T>>;
56
- /**
57
- * Sort entries by specified field
58
- * @param entries Array of entries to sort
59
- * @param key Sorting field name
60
- * @param sortOrder Sort order: 'asc' or 'desc' (default: 'asc')
61
- * @returns Sorted array of entries
62
- */
63
- private sortEntries;
64
- /**
65
- * Clear store
66
- */
67
- clear(storeName: string): Promise<void>;
68
- /**
69
- * List directory contents
70
- */
71
- listDirectory(path?: string): Promise<string[]>;
72
- /**
73
- * Test connection
74
- */
75
- testConnection(): Promise<boolean>;
76
- }
77
- //#endregion
78
- export { WebDAVAdapter };
79
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
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;EAiJV,SAAA,IAAA,GAAA,eAAA;EAMiB,QAAA,OAAA;EAEZ;;;EAyBe,UAAA,CAAA,OAAA,EA1KH,aA0KG,CAAA,EAAA,IAAA;EAGP;;;EAEb,QAAA,UAAA;EAiCA;;;EA+BA,QAAA,QAAA;EAoBqB;;;EAIG,QAAA,OAAA;EAAhB;;;EAoH6B,QAAA,qBAAA;EAiDhB;;;uEAnSrB;;;;gBAMiB,kDAEZ,qBAEL,QAAQ;;;;mBAuBY,8DAGf,QAAQ,qBAEb,QAAQ;;;;yDAiCR;;;;oBA2BqB,yEAIrB,QAAQ;;;;oBAoBa,sDAEb,aAAa,sBAErB,QAAQ,gBAAgB;;;;;;;;;;;;4BAwGK;;;;gCAYQ;;;;oBAiDhB"}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","names":["AdapterType","headers: Record<string, string>","AuthType","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): 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 basePath = this.service.basePath ? `/${this.service.basePath.replace(/^\\/+|\\/+$/g, \"\")}` : \"\";\n return `${baseUrl}${basePath}/${storeName}.json`;\n }\n\n /**\n * Send request\n */\n private async request<T>(\n url: string,\n options: RequestInit = {},\n {\n allowNotFound,\n createIfNotFound,\n }: {\n allowNotFound?: boolean;\n createIfNotFound?: 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 (response.status === 404) {\n if (allowNotFound) {\n return null;\n }\n if (createIfNotFound && options.method === \"GET\") {\n await this.createPathIfNotExists(url);\n return [] as T;\n }\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 * Create path if not exists\n */\n private async createPathIfNotExists(url: string): Promise<void> {\n const baseUrl = this.service!.endpoint.replace(/\\/$/, \"\");\n const relativePath = url.replace(baseUrl, \"\").replace(/^\\//, \"\");\n const pathParts = relativePath.split(\"/\");\n\n let fullPath = \"\";\n for (const part of pathParts) {\n fullPath = fullPath ? `${fullPath}/${part}` : part;\n const createUrl = `${baseUrl}/${fullPath}`;\n\n const response = await fetch(createUrl, {\n method: part.endsWith('.json') ? \"PUT\" : \"MKCOL\",\n headers: this.getHeaders(),\n body: part.endsWith('.json') ? JSON.stringify([]) : undefined,\n });\n\n if (!response.ok && response.status !== 405) {\n throw new Error(`Failed to create path: ${response.statusText}`);\n }\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 }\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 url = this.buildUrl(storeName);\n\n const existing = await this.request<T[]>(url, {\n method: \"GET\",\n }, {\n createIfNotFound: true,\n }) || [];\n\n const items = existing || [];\n items.push(data);\n\n await this.request(url, {\n method: \"PUT\",\n body: JSON.stringify(items),\n });\n\n return 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 url = this.buildUrl(storeName);\n\n const existing = await this.request<T[]>(url, {\n method: \"GET\",\n });\n\n if (!existing) {\n throw new Error(\"Not found id:\" + id);\n }\n\n const index = existing.findIndex(item => String(item[idKey]) === id);\n if (index === -1) {\n throw new Error(\"Not found id:\" + id);\n }\n\n existing[index] = { ...existing[index], ...data } as T;\n\n await this.request(url, {\n method: \"PUT\",\n body: JSON.stringify(existing),\n });\n\n return existing[index];\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);\n\n const existing = await this.request<Record<string, unknown>[]>(url, {\n method: \"GET\",\n });\n\n if (!existing) {\n throw new Error(\"Not found id:\" + id);\n }\n\n const index = existing.findIndex(item => String(item[idKey]) === id);\n if (index === -1) {\n throw new Error(\"Not found id:\" + id);\n }\n\n existing.splice(index, 1);\n\n await this.request(url, {\n method: \"PUT\",\n body: JSON.stringify(existing),\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);\n\n const existing = await this.request<T[]>(url, {\n method: \"GET\",\n }, {\n allowNotFound: true,\n });\n\n if (!existing) {\n return null;\n }\n\n const item = existing.find(item => String(item[idKey]) === id);\n return item ?? null;\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 url = this.buildUrl(storeName);\n\n const allData = await this.request<T[]>(url, {\n method: \"GET\",\n }, {\n createIfNotFound: true,\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 } catch {}\n const pathRegex = new RegExp(`^/*${path}`)\n relativePath = fullPath.replace(basePath, \"\").replace(pathRegex, '').replace(/^\\//, \"\");\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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"}