@svadmin/directus 0.9.22 → 0.10.6

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": "@svadmin/directus",
3
- "version": "0.9.22",
3
+ "version": "0.10.6",
4
4
  "description": "Directus DataProvider for svadmin",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -20,12 +20,12 @@
20
20
  }
21
21
  },
22
22
  "peerDependencies": {
23
- "@svadmin/core": "^0.50.0"
23
+ "@svadmin/core": "^0.52.0"
24
24
  },
25
25
  "license": "MIT",
26
26
  "repository": {
27
27
  "type": "git",
28
- "url": "https://github.com/vibeunion/svadmin.git",
28
+ "url": "git+https://github.com/vibeunion/svadmin.git",
29
29
  "directory": "packages/directus"
30
30
  },
31
31
  "peerDependenciesMeta": {
@@ -1,24 +1,11 @@
1
+ import { definedOptions } from '@svadmin/core/options';
2
+ import { withValidatedResponses, type DataTransport } from '@svadmin/core/schema';
1
3
  import type {
2
- BaseRecord,
3
- CreateParams,
4
- CreateResult,
5
4
  CrudOperator,
6
- CustomParams,
7
- CustomResult,
8
5
  DataProvider,
9
- DeleteParams,
10
- DeleteResult,
11
6
  FieldFilter,
12
7
  Filter,
13
- GetListParams,
14
- GetListResult,
15
- GetManyParams,
16
- GetManyResult,
17
- GetOneParams,
18
- GetOneResult,
19
8
  Sort,
20
- UpdateParams,
21
- UpdateResult,
22
9
  } from '@svadmin/core';
23
10
 
24
11
  const DIRECTUS_OPERATOR_MAP: Record<CrudOperator, string> = {
@@ -57,15 +44,8 @@ type RequestOptions = Omit<RequestInit, 'headers'> & {
57
44
  headers?: Record<string, string>;
58
45
  };
59
46
 
60
- interface DirectusListResponse<TData> {
61
- data?: TData[];
62
- meta?: {
63
- total_count?: number;
64
- };
65
- }
66
-
67
- interface DirectusItemResponse<TData> {
68
- data?: TData;
47
+ function isObject(value: unknown): value is Record<string, unknown> {
48
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
69
49
  }
70
50
 
71
51
  function mergeHeaders(...sources: Array<Record<string, string> | undefined>): Record<string, string> {
@@ -112,7 +92,8 @@ function buildDirectusRule(filter: Filter): Record<string, unknown> {
112
92
  function buildDirectusFilter(filters?: Filter[]): Record<string, unknown> {
113
93
  if (!filters?.length) return {};
114
94
  const rules = filters.map(buildDirectusRule);
115
- return rules.length === 1 ? rules[0] : { _and: rules };
95
+ const firstRule = rules[0];
96
+ return rules.length === 1 && firstRule ? firstRule : { _and: rules };
116
97
  }
117
98
 
118
99
  function serializeQueryValue(value: unknown): string {
@@ -153,7 +134,7 @@ function appendFilters(params: URLSearchParams, filters?: Filter[]): void {
153
134
  }
154
135
 
155
136
  function fieldsFromMeta(meta?: Record<string, unknown>): string[] | undefined {
156
- const fields = meta?.fields;
137
+ const fields = meta?.['fields'];
157
138
  if (!Array.isArray(fields) || !fields.every(field => typeof field === 'string')) return undefined;
158
139
  return fields;
159
140
  }
@@ -168,45 +149,45 @@ function isSameOrigin(apiUrl: string, targetUrl: string): boolean {
168
149
  }
169
150
  }
170
151
 
171
- async function parseResponse<TData>(response: Response): Promise<TData> {
152
+ async function parseResponse(response: Response): Promise<unknown> {
172
153
  if (response.status === 204 || response.status === 205) {
173
- return undefined as unknown as TData;
154
+ return undefined;
174
155
  }
175
156
 
176
157
  const contentLength = response.headers?.get('content-length');
177
158
  if (contentLength?.trim() === '0') {
178
- return undefined as unknown as TData;
159
+ return undefined;
179
160
  }
180
161
 
181
162
  const body = await response.text();
182
163
  if (!body || body.trim() === '') {
183
- return undefined as unknown as TData;
164
+ return undefined;
184
165
  }
185
166
 
186
- return JSON.parse(body) as TData;
167
+ return JSON.parse(body);
187
168
  }
188
169
 
189
- async function fetchData<TData>(
170
+ async function fetchData(
190
171
  url: string,
191
172
  headers: Record<string, string>,
192
173
  init: RequestOptions | undefined,
193
174
  errorMessage: (status: number) => string,
194
- ): Promise<TData> {
175
+ ): Promise<unknown> {
195
176
  const response = await fetch(url, {
196
177
  ...init,
197
178
  headers: mergeHeaders(headers, init?.headers),
198
179
  });
199
180
  if (!response.ok) throw new Error(errorMessage(response.status));
200
- return parseResponse<TData>(response);
181
+ return parseResponse(response);
201
182
  }
202
183
 
203
184
  export function createDirectusDataProvider(apiUrl: string, token?: string): DataProvider {
204
185
  const providerHeaders: Record<string, string> = { 'Content-Type': 'application/json' };
205
- if (token) providerHeaders.Authorization = `Bearer ${token}`;
186
+ if (token) providerHeaders['Authorization'] = `Bearer ${token}`;
206
187
  const baseUrl = apiUrl.replace(/\/+$/, '');
207
188
 
208
- function request<TData>(path: string, init?: RequestOptions): Promise<TData> {
209
- return fetchData<TData>(
189
+ function request(path: string, init?: RequestOptions): Promise<unknown> {
190
+ return fetchData(
210
191
  `${baseUrl}${path}`,
211
192
  providerHeaders,
212
193
  init,
@@ -214,10 +195,10 @@ export function createDirectusDataProvider(apiUrl: string, token?: string): Data
214
195
  );
215
196
  }
216
197
 
217
- return {
198
+ const transport: DataTransport = {
218
199
  getApiUrl: () => apiUrl,
219
200
 
220
- async getList<TData extends BaseRecord = BaseRecord>({ resource, pagination, sorters, filters, meta }: GetListParams): Promise<GetListResult<TData>> {
201
+ async getList({ resource, pagination, sorters, filters, meta }) {
221
202
  const { current = 1, pageSize = 10 } = pagination ?? {};
222
203
  const params = new URLSearchParams();
223
204
  params.set('limit', String(pageSize));
@@ -228,55 +209,53 @@ export function createDirectusDataProvider(apiUrl: string, token?: string): Data
228
209
  appendSorters(params, sorters);
229
210
  appendFilters(params, filters);
230
211
 
231
- const response = await request<DirectusListResponse<TData>>(`/items/${resource}?${params}`);
212
+ const response = await request(`/items/${resource}?${params}`);
213
+ if (!isObject(response)) return response;
214
+ const metadata = response['meta'];
232
215
  return {
233
- data: response?.data ?? [],
234
- total: response?.meta?.total_count ?? 0,
216
+ data: response['data'],
217
+ total: isObject(metadata) ? metadata['total_count'] : undefined,
235
218
  };
236
219
  },
237
220
 
238
- async getOne<TData extends BaseRecord = BaseRecord>({ resource, id, meta }: GetOneParams): Promise<GetOneResult<TData>> {
221
+ async getOne({ resource, id, meta }) {
239
222
  const params = new URLSearchParams();
240
223
  const fields = fieldsFromMeta(meta);
241
224
  if (fields) params.set('fields', fields.join(','));
242
225
  const query = params.size ? `?${params}` : '';
243
- const response = await request<DirectusItemResponse<TData>>(
226
+ return request(
244
227
  `/items/${resource}/${encodeURIComponent(String(id))}${query}`,
245
228
  );
246
- return { data: response?.data as TData };
247
229
  },
248
230
 
249
- async create<TData extends BaseRecord = BaseRecord, TVariables = unknown>({ resource, variables }: CreateParams<TVariables>): Promise<CreateResult<TData>> {
250
- const response = await request<DirectusItemResponse<TData>>(`/items/${resource}`, {
231
+ async create({ resource, variables }) {
232
+ return request(`/items/${resource}`, {
251
233
  method: 'POST',
252
234
  body: JSON.stringify(variables),
253
235
  });
254
- return { data: response?.data as TData };
255
236
  },
256
237
 
257
- async update<TData extends BaseRecord = BaseRecord, TVariables = unknown>({ resource, id, variables }: UpdateParams<TVariables>): Promise<UpdateResult<TData>> {
258
- const response = await request<DirectusItemResponse<TData>>(
238
+ async update({ resource, id, variables }) {
239
+ return request(
259
240
  `/items/${resource}/${encodeURIComponent(String(id))}`,
260
241
  { method: 'PATCH', body: JSON.stringify(variables) },
261
242
  );
262
- return { data: response?.data as TData };
263
243
  },
264
244
 
265
- async deleteOne<TData extends BaseRecord = BaseRecord, TVariables = unknown>({ resource, id }: DeleteParams<TVariables>): Promise<DeleteResult<TData>> {
266
- await request(`/items/${resource}/${encodeURIComponent(String(id))}`, { method: 'DELETE' });
267
- return { data: { id } as unknown as TData };
245
+ async deleteOne({ resource, id }) {
246
+ const response = await request(`/items/${resource}/${encodeURIComponent(String(id))}`, { method: 'DELETE' });
247
+ return response === undefined ? { data: { id } } : response;
268
248
  },
269
249
 
270
- async getMany<TData extends BaseRecord = BaseRecord>({ resource, ids, meta }: GetManyParams): Promise<GetManyResult<TData>> {
250
+ async getMany({ resource, ids, meta }) {
271
251
  const params = new URLSearchParams();
272
252
  params.set('filter', JSON.stringify({ id: { _in: ids } }));
273
253
  const fields = fieldsFromMeta(meta);
274
254
  if (fields) params.set('fields', fields.join(','));
275
- const response = await request<DirectusListResponse<TData>>(`/items/${resource}?${params}`);
276
- return { data: response?.data ?? [] };
255
+ return request(`/items/${resource}?${params}`);
277
256
  },
278
257
 
279
- async custom<TData = unknown, TVariables = unknown>({
258
+ async custom({
280
259
  url,
281
260
  method,
282
261
  payload,
@@ -284,7 +263,7 @@ export function createDirectusDataProvider(apiUrl: string, token?: string): Data
284
263
  headers,
285
264
  sorters,
286
265
  filters,
287
- }: CustomParams<TVariables>): Promise<CustomResult<TData>> {
266
+ }) {
288
267
  const parsed = new URL(url, apiUrl.endsWith('/') ? apiUrl : `${apiUrl}/`);
289
268
  appendQuery(parsed.searchParams, query);
290
269
  appendSorters(parsed.searchParams, sorters);
@@ -295,16 +274,17 @@ export function createDirectusDataProvider(apiUrl: string, token?: string): Data
295
274
  sameOrigin ? providerHeaders : withoutSensitiveHeaders(providerHeaders),
296
275
  headers,
297
276
  );
298
- const data = await fetchData<TData>(
277
+ const data = await fetchData(
299
278
  requestUrl,
300
279
  requestHeaders,
301
- {
280
+ definedOptions({
302
281
  method: method.toUpperCase(),
303
282
  body: payload === undefined ? undefined : JSON.stringify(payload),
304
- },
283
+ }),
305
284
  status => `Custom request failed: ${status}`,
306
285
  );
307
286
  return { data };
308
287
  },
309
288
  };
289
+ return withValidatedResponses(transport);
310
290
  }