@chill-sharp/vue-client 1.1.12 → 1.1.15
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/README.md +448 -448
- package/package.json +48 -48
- package/src/composables.ts +547 -547
- package/src/index.ts +78 -78
- package/src/plugin.ts +58 -58
- package/src/version.ts +20 -20
package/src/composables.ts
CHANGED
|
@@ -1,547 +1,547 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* ChillSharp is a lightweight .NET library that sits on top of Entity Framework Core
|
|
3
|
-
* and turns an existing data model into a fully working REST API with almost no setup.
|
|
4
|
-
* Copyright (C) 2025 Andrea Piovesan
|
|
5
|
-
*
|
|
6
|
-
* This program is free software: you can redistribute it and/or modify
|
|
7
|
-
* it under the terms of the GNU Affero General Public License as published by
|
|
8
|
-
* the Free Software Foundation, either version 3 of the License, or
|
|
9
|
-
* (at your option) any later version.
|
|
10
|
-
*
|
|
11
|
-
* This program is distributed in the hope that it will be useful,
|
|
12
|
-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
-
* GNU Affero General Public License for more details.
|
|
15
|
-
*
|
|
16
|
-
* You should have received a copy of the GNU Affero General Public License
|
|
17
|
-
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { onScopeDispose, ref, shallowRef, watch, watchEffect, type Ref } from "vue";
|
|
21
|
-
import { CHILL_SHARP_VUE_CLIENT_VERSION } from "./version.js";
|
|
22
|
-
import { useChillSharpClient } from "./plugin.js";
|
|
23
|
-
import type {
|
|
24
|
-
ChillDtoSchemaListItem,
|
|
25
|
-
ChillUserPreferences,
|
|
26
|
-
ChillEntityChangeCallback,
|
|
27
|
-
ChillEntityChangeSubscription,
|
|
28
|
-
ChillValidationError,
|
|
29
|
-
GetTextRequest,
|
|
30
|
-
JsonObject
|
|
31
|
-
} from "@chill-sharp/ts-client";
|
|
32
|
-
|
|
33
|
-
export interface UseChillAsyncState<TData> {
|
|
34
|
-
data: ReadonlyRef<TData | null>;
|
|
35
|
-
error: ReadonlyRef<unknown>;
|
|
36
|
-
isLoading: ReadonlyRef<boolean>;
|
|
37
|
-
reload: () => Promise<TData | null>;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface UseChillMutationState<TData> {
|
|
41
|
-
data: ReadonlyRef<TData | null>;
|
|
42
|
-
error: ReadonlyRef<unknown>;
|
|
43
|
-
isLoading: ReadonlyRef<boolean>;
|
|
44
|
-
execute: (...args: unknown[]) => Promise<TData>;
|
|
45
|
-
reset: () => void;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface UseChillSubscriptionState {
|
|
49
|
-
error: ReadonlyRef<unknown>;
|
|
50
|
-
isSubscribed: ReadonlyRef<boolean>;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function useSchema(
|
|
54
|
-
chillType: Ref<string> | string,
|
|
55
|
-
chillViewCode: Ref<string> | string = "default",
|
|
56
|
-
cultureName?: Ref<string | undefined> | string,
|
|
57
|
-
update: Ref<boolean | undefined> | boolean = false
|
|
58
|
-
): UseChillAsyncState<JsonObject> {
|
|
59
|
-
const client = useChillSharpClient();
|
|
60
|
-
const data = ref<JsonObject | null>(null);
|
|
61
|
-
const error = ref<unknown>(null);
|
|
62
|
-
const isLoading = ref<boolean>(true);
|
|
63
|
-
|
|
64
|
-
const load = async () => {
|
|
65
|
-
isLoading.value = true;
|
|
66
|
-
error.value = null;
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
const response = await client.getSchema(
|
|
70
|
-
readRef(chillType),
|
|
71
|
-
readRef(chillViewCode),
|
|
72
|
-
cultureName === undefined ? undefined : readRef(cultureName),
|
|
73
|
-
readRef(update) ?? false
|
|
74
|
-
);
|
|
75
|
-
data.value = response;
|
|
76
|
-
return response;
|
|
77
|
-
} catch (err) {
|
|
78
|
-
error.value = err;
|
|
79
|
-
throw err;
|
|
80
|
-
} finally {
|
|
81
|
-
isLoading.value = false;
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
watchEffect(() => {
|
|
86
|
-
void load();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
data,
|
|
91
|
-
error,
|
|
92
|
-
isLoading,
|
|
93
|
-
reload: load
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function useSchemaList(
|
|
98
|
-
cultureName?: Ref<string | undefined> | string
|
|
99
|
-
): UseChillAsyncState<JsonObject[]> {
|
|
100
|
-
const client = useChillSharpClient();
|
|
101
|
-
const data = ref<JsonObject[] | null>(null);
|
|
102
|
-
const error = ref<unknown>(null);
|
|
103
|
-
const isLoading = ref<boolean>(true);
|
|
104
|
-
|
|
105
|
-
const load = async () => {
|
|
106
|
-
isLoading.value = true;
|
|
107
|
-
error.value = null;
|
|
108
|
-
|
|
109
|
-
try {
|
|
110
|
-
const response = await client.getSchemaList(cultureName === undefined ? undefined : readRef(cultureName));
|
|
111
|
-
data.value = response;
|
|
112
|
-
return response;
|
|
113
|
-
} catch (err) {
|
|
114
|
-
error.value = err;
|
|
115
|
-
throw err;
|
|
116
|
-
} finally {
|
|
117
|
-
isLoading.value = false;
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
watchEffect(() => {
|
|
122
|
-
void load();
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
return {
|
|
126
|
-
data,
|
|
127
|
-
error,
|
|
128
|
-
isLoading,
|
|
129
|
-
reload: load
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export function useText(request: Ref<JsonObject> | JsonObject): UseChillAsyncState<JsonObject> {
|
|
134
|
-
const client = useChillSharpClient();
|
|
135
|
-
const data = ref<JsonObject | null>(null);
|
|
136
|
-
const error = ref<unknown>(null);
|
|
137
|
-
const isLoading = ref<boolean>(true);
|
|
138
|
-
|
|
139
|
-
const load = async () => {
|
|
140
|
-
isLoading.value = true;
|
|
141
|
-
error.value = null;
|
|
142
|
-
|
|
143
|
-
try {
|
|
144
|
-
const response = await client.getText(readRef(request) as GetTextRequest);
|
|
145
|
-
data.value = response;
|
|
146
|
-
return response;
|
|
147
|
-
} catch (err) {
|
|
148
|
-
error.value = err;
|
|
149
|
-
throw err;
|
|
150
|
-
} finally {
|
|
151
|
-
isLoading.value = false;
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
watchEffect(() => {
|
|
156
|
-
void load();
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
return {
|
|
160
|
-
data,
|
|
161
|
-
error,
|
|
162
|
-
isLoading,
|
|
163
|
-
reload: load
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
export function useTexts(requests: Ref<JsonObject[]> | JsonObject[]): UseChillAsyncState<Array<JsonObject | null>> {
|
|
168
|
-
const client = useChillSharpClient();
|
|
169
|
-
const data = ref<Array<JsonObject | null> | null>(null);
|
|
170
|
-
const error = ref<unknown>(null);
|
|
171
|
-
const isLoading = ref<boolean>(true);
|
|
172
|
-
|
|
173
|
-
const load = async () => {
|
|
174
|
-
isLoading.value = true;
|
|
175
|
-
error.value = null;
|
|
176
|
-
|
|
177
|
-
try {
|
|
178
|
-
const response = await client.getTexts(readRef(requests) as GetTextRequest[]) as Array<JsonObject | null>;
|
|
179
|
-
data.value = response;
|
|
180
|
-
return response;
|
|
181
|
-
} catch (err) {
|
|
182
|
-
error.value = err;
|
|
183
|
-
throw err;
|
|
184
|
-
} finally {
|
|
185
|
-
isLoading.value = false;
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
watchEffect(() => {
|
|
190
|
-
void load();
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
data,
|
|
195
|
-
error,
|
|
196
|
-
isLoading,
|
|
197
|
-
reload: load
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export function useVersion(): string {
|
|
202
|
-
return CHILL_SHARP_VUE_CLIENT_VERSION;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
export function useTest(): UseChillAsyncState<string> {
|
|
206
|
-
const client = useChillSharpClient();
|
|
207
|
-
const data = ref<string | null>(null);
|
|
208
|
-
const error = ref<unknown>(null);
|
|
209
|
-
const isLoading = ref<boolean>(true);
|
|
210
|
-
|
|
211
|
-
const load = async () => {
|
|
212
|
-
isLoading.value = true;
|
|
213
|
-
error.value = null;
|
|
214
|
-
|
|
215
|
-
try {
|
|
216
|
-
const response = await client.test();
|
|
217
|
-
data.value = response;
|
|
218
|
-
return response;
|
|
219
|
-
} catch (err) {
|
|
220
|
-
error.value = err;
|
|
221
|
-
throw err;
|
|
222
|
-
} finally {
|
|
223
|
-
isLoading.value = false;
|
|
224
|
-
}
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
watchEffect(() => {
|
|
228
|
-
void load();
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
return {
|
|
232
|
-
data,
|
|
233
|
-
error,
|
|
234
|
-
isLoading,
|
|
235
|
-
reload: load
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
export function useCurrentUserPreferences(): UseChillAsyncState<ChillUserPreferences> {
|
|
240
|
-
const client = useChillSharpClient();
|
|
241
|
-
// ChillUserPreferences extends the recursive JsonObject type. Keep it shallow so Vue
|
|
242
|
-
// does not recursively unwrap that type while inferring the composable return value.
|
|
243
|
-
const data = shallowRef<ChillUserPreferences | null>(null);
|
|
244
|
-
const error = ref<unknown>(null);
|
|
245
|
-
const isLoading = ref<boolean>(true);
|
|
246
|
-
|
|
247
|
-
const load = async () => {
|
|
248
|
-
isLoading.value = true;
|
|
249
|
-
error.value = null;
|
|
250
|
-
try {
|
|
251
|
-
const response = await client.getCurrentUserPreferences();
|
|
252
|
-
data.value = response;
|
|
253
|
-
return response;
|
|
254
|
-
} catch (err) {
|
|
255
|
-
error.value = err;
|
|
256
|
-
throw err;
|
|
257
|
-
} finally {
|
|
258
|
-
isLoading.value = false;
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
watchEffect(() => { void load(); });
|
|
263
|
-
return { data, error, isLoading, reload: load };
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
export function useQueryMutation(): UseChillMutationState<JsonObject> {
|
|
267
|
-
const client = useChillSharpClient();
|
|
268
|
-
const data = ref<JsonObject | null>(null);
|
|
269
|
-
const error = ref<unknown>(null);
|
|
270
|
-
const isLoading = ref<boolean>(false);
|
|
271
|
-
|
|
272
|
-
const execute = async (...args: unknown[]) => {
|
|
273
|
-
const payload = args[0] as JsonObject;
|
|
274
|
-
isLoading.value = true;
|
|
275
|
-
error.value = null;
|
|
276
|
-
|
|
277
|
-
try {
|
|
278
|
-
const response = await client.query(payload);
|
|
279
|
-
data.value = response;
|
|
280
|
-
return response;
|
|
281
|
-
} catch (err) {
|
|
282
|
-
error.value = err;
|
|
283
|
-
throw err;
|
|
284
|
-
} finally {
|
|
285
|
-
isLoading.value = false;
|
|
286
|
-
}
|
|
287
|
-
};
|
|
288
|
-
|
|
289
|
-
return {
|
|
290
|
-
data,
|
|
291
|
-
error,
|
|
292
|
-
isLoading,
|
|
293
|
-
execute,
|
|
294
|
-
reset: () => {
|
|
295
|
-
data.value = null;
|
|
296
|
-
error.value = null;
|
|
297
|
-
isLoading.value = false;
|
|
298
|
-
}
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
export function useLookupMutation(): UseChillMutationState<JsonObject> {
|
|
303
|
-
const client = useChillSharpClient();
|
|
304
|
-
const data = ref<JsonObject | null>(null);
|
|
305
|
-
const error = ref<unknown>(null);
|
|
306
|
-
const isLoading = ref<boolean>(false);
|
|
307
|
-
|
|
308
|
-
const execute = async (...args: unknown[]) => {
|
|
309
|
-
const payload = args[0] as JsonObject;
|
|
310
|
-
isLoading.value = true;
|
|
311
|
-
error.value = null;
|
|
312
|
-
|
|
313
|
-
try {
|
|
314
|
-
const response = await client.lookup(payload);
|
|
315
|
-
data.value = response;
|
|
316
|
-
return response;
|
|
317
|
-
} catch (err) {
|
|
318
|
-
error.value = err;
|
|
319
|
-
throw err;
|
|
320
|
-
} finally {
|
|
321
|
-
isLoading.value = false;
|
|
322
|
-
}
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
return {
|
|
326
|
-
data,
|
|
327
|
-
error,
|
|
328
|
-
isLoading,
|
|
329
|
-
execute,
|
|
330
|
-
reset: () => {
|
|
331
|
-
data.value = null;
|
|
332
|
-
error.value = null;
|
|
333
|
-
isLoading.value = false;
|
|
334
|
-
}
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
export function useAutocompleteMutation(): UseChillMutationState<JsonObject> {
|
|
339
|
-
const client = useChillSharpClient();
|
|
340
|
-
const data = ref<JsonObject | null>(null);
|
|
341
|
-
const error = ref<unknown>(null);
|
|
342
|
-
const isLoading = ref<boolean>(false);
|
|
343
|
-
|
|
344
|
-
const execute = async (...args: unknown[]) => {
|
|
345
|
-
const payload = args[0] as JsonObject;
|
|
346
|
-
isLoading.value = true;
|
|
347
|
-
error.value = null;
|
|
348
|
-
|
|
349
|
-
try {
|
|
350
|
-
const response = await client.autocomplete(payload);
|
|
351
|
-
data.value = response;
|
|
352
|
-
return response;
|
|
353
|
-
} catch (err) {
|
|
354
|
-
error.value = err;
|
|
355
|
-
throw err;
|
|
356
|
-
} finally {
|
|
357
|
-
isLoading.value = false;
|
|
358
|
-
}
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
return {
|
|
362
|
-
data,
|
|
363
|
-
error,
|
|
364
|
-
isLoading,
|
|
365
|
-
execute,
|
|
366
|
-
reset: () => {
|
|
367
|
-
data.value = null;
|
|
368
|
-
error.value = null;
|
|
369
|
-
isLoading.value = false;
|
|
370
|
-
}
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
export function useValidateMutation(): UseChillMutationState<ChillValidationError[]> {
|
|
375
|
-
const client = useChillSharpClient();
|
|
376
|
-
const data = ref<ChillValidationError[] | null>(null);
|
|
377
|
-
const error = ref<unknown>(null);
|
|
378
|
-
const isLoading = ref<boolean>(false);
|
|
379
|
-
|
|
380
|
-
const execute = async (...args: unknown[]) => {
|
|
381
|
-
const payload = args[0] as JsonObject;
|
|
382
|
-
isLoading.value = true;
|
|
383
|
-
error.value = null;
|
|
384
|
-
|
|
385
|
-
try {
|
|
386
|
-
const response = await client.validate(payload);
|
|
387
|
-
data.value = response;
|
|
388
|
-
return response;
|
|
389
|
-
} catch (err) {
|
|
390
|
-
error.value = err;
|
|
391
|
-
throw err;
|
|
392
|
-
} finally {
|
|
393
|
-
isLoading.value = false;
|
|
394
|
-
}
|
|
395
|
-
};
|
|
396
|
-
|
|
397
|
-
return {
|
|
398
|
-
data,
|
|
399
|
-
error,
|
|
400
|
-
isLoading,
|
|
401
|
-
execute,
|
|
402
|
-
reset: () => {
|
|
403
|
-
data.value = null;
|
|
404
|
-
error.value = null;
|
|
405
|
-
isLoading.value = false;
|
|
406
|
-
}
|
|
407
|
-
} as UseChillMutationState<ChillValidationError[]>;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
export function useEntityMutation(action: "find" | "create" | "update" | "delete"): UseChillMutationState<JsonObject | null> {
|
|
411
|
-
const client = useChillSharpClient();
|
|
412
|
-
const data = ref<JsonObject | null>(null);
|
|
413
|
-
const error = ref<unknown>(null);
|
|
414
|
-
const isLoading = ref<boolean>(false);
|
|
415
|
-
|
|
416
|
-
const execute = async (...args: unknown[]) => {
|
|
417
|
-
const payload = args[0] as JsonObject;
|
|
418
|
-
isLoading.value = true;
|
|
419
|
-
error.value = null;
|
|
420
|
-
|
|
421
|
-
try {
|
|
422
|
-
let response: JsonObject | null;
|
|
423
|
-
switch (action) {
|
|
424
|
-
case "find":
|
|
425
|
-
response = await client.find(payload);
|
|
426
|
-
break;
|
|
427
|
-
case "create":
|
|
428
|
-
response = await client.create(payload);
|
|
429
|
-
break;
|
|
430
|
-
case "update":
|
|
431
|
-
response = await client.update(payload);
|
|
432
|
-
break;
|
|
433
|
-
default:
|
|
434
|
-
await client.delete(payload);
|
|
435
|
-
response = null;
|
|
436
|
-
break;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
data.value = response;
|
|
440
|
-
return response;
|
|
441
|
-
} catch (err) {
|
|
442
|
-
error.value = err;
|
|
443
|
-
throw err;
|
|
444
|
-
} finally {
|
|
445
|
-
isLoading.value = false;
|
|
446
|
-
}
|
|
447
|
-
};
|
|
448
|
-
|
|
449
|
-
return {
|
|
450
|
-
data,
|
|
451
|
-
error,
|
|
452
|
-
isLoading,
|
|
453
|
-
execute,
|
|
454
|
-
reset: () => {
|
|
455
|
-
data.value = null;
|
|
456
|
-
error.value = null;
|
|
457
|
-
isLoading.value = false;
|
|
458
|
-
}
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
export function useEntityChanges(
|
|
463
|
-
chillType: Ref<string> | string,
|
|
464
|
-
onChanges: Ref<ChillEntityChangeCallback> | ChillEntityChangeCallback,
|
|
465
|
-
guid?: Ref<string | null | undefined> | string | null
|
|
466
|
-
): UseChillSubscriptionState {
|
|
467
|
-
const client = useChillSharpClient();
|
|
468
|
-
const error = ref<unknown>(null);
|
|
469
|
-
const isSubscribed = ref<boolean>(false);
|
|
470
|
-
let subscription: ChillEntityChangeSubscription | null = null;
|
|
471
|
-
|
|
472
|
-
const release = async () => {
|
|
473
|
-
const currentSubscription = subscription;
|
|
474
|
-
subscription = null;
|
|
475
|
-
isSubscribed.value = false;
|
|
476
|
-
if (currentSubscription) {
|
|
477
|
-
await currentSubscription.unsubscribe();
|
|
478
|
-
}
|
|
479
|
-
};
|
|
480
|
-
|
|
481
|
-
watch(
|
|
482
|
-
[
|
|
483
|
-
() => readRef(chillType),
|
|
484
|
-
() => (guid === undefined ? null : (readRef(guid) ?? null)),
|
|
485
|
-
() => readRef(onChanges)
|
|
486
|
-
],
|
|
487
|
-
async ([nextChillType, nextGuid, nextOnChanges], _previousValue, onCleanup) => {
|
|
488
|
-
error.value = null;
|
|
489
|
-
await release();
|
|
490
|
-
|
|
491
|
-
let isActive = true;
|
|
492
|
-
onCleanup(() => {
|
|
493
|
-
isActive = false;
|
|
494
|
-
void release();
|
|
495
|
-
});
|
|
496
|
-
|
|
497
|
-
try {
|
|
498
|
-
const nextSubscription = await client.subscribeToEntityChanges(
|
|
499
|
-
nextChillType,
|
|
500
|
-
async (changes) => {
|
|
501
|
-
await nextOnChanges(changes);
|
|
502
|
-
},
|
|
503
|
-
nextGuid
|
|
504
|
-
);
|
|
505
|
-
|
|
506
|
-
if (!isActive) {
|
|
507
|
-
await nextSubscription.unsubscribe();
|
|
508
|
-
return;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
subscription = nextSubscription;
|
|
512
|
-
isSubscribed.value = true;
|
|
513
|
-
} catch (err) {
|
|
514
|
-
if (isActive) {
|
|
515
|
-
error.value = err;
|
|
516
|
-
isSubscribed.value = false;
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
},
|
|
520
|
-
{ immediate: true }
|
|
521
|
-
);
|
|
522
|
-
|
|
523
|
-
onScopeDispose(() => {
|
|
524
|
-
void release();
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
return {
|
|
528
|
-
error,
|
|
529
|
-
isSubscribed
|
|
530
|
-
};
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
type ReadonlyRef<T> = Ref<T>;
|
|
534
|
-
|
|
535
|
-
function asReadonlyRef<T>(value: Ref<T>): ReadonlyRef<T> {
|
|
536
|
-
return value;
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
function readRef<T>(value: Ref<T> | T): T {
|
|
540
|
-
if (typeof value === "object" && value !== null && "value" in value) {
|
|
541
|
-
return value.value;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
return value;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
|
|
1
|
+
/*
|
|
2
|
+
* ChillSharp is a lightweight .NET library that sits on top of Entity Framework Core
|
|
3
|
+
* and turns an existing data model into a fully working REST API with almost no setup.
|
|
4
|
+
* Copyright (C) 2025 Andrea Piovesan
|
|
5
|
+
*
|
|
6
|
+
* This program is free software: you can redistribute it and/or modify
|
|
7
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
8
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
* (at your option) any later version.
|
|
10
|
+
*
|
|
11
|
+
* This program is distributed in the hope that it will be useful,
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
* GNU Affero General Public License for more details.
|
|
15
|
+
*
|
|
16
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
17
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { onScopeDispose, ref, shallowRef, watch, watchEffect, type Ref } from "vue";
|
|
21
|
+
import { CHILL_SHARP_VUE_CLIENT_VERSION } from "./version.js";
|
|
22
|
+
import { useChillSharpClient } from "./plugin.js";
|
|
23
|
+
import type {
|
|
24
|
+
ChillDtoSchemaListItem,
|
|
25
|
+
ChillUserPreferences,
|
|
26
|
+
ChillEntityChangeCallback,
|
|
27
|
+
ChillEntityChangeSubscription,
|
|
28
|
+
ChillValidationError,
|
|
29
|
+
GetTextRequest,
|
|
30
|
+
JsonObject
|
|
31
|
+
} from "@chill-sharp/ts-client";
|
|
32
|
+
|
|
33
|
+
export interface UseChillAsyncState<TData> {
|
|
34
|
+
data: ReadonlyRef<TData | null>;
|
|
35
|
+
error: ReadonlyRef<unknown>;
|
|
36
|
+
isLoading: ReadonlyRef<boolean>;
|
|
37
|
+
reload: () => Promise<TData | null>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface UseChillMutationState<TData> {
|
|
41
|
+
data: ReadonlyRef<TData | null>;
|
|
42
|
+
error: ReadonlyRef<unknown>;
|
|
43
|
+
isLoading: ReadonlyRef<boolean>;
|
|
44
|
+
execute: (...args: unknown[]) => Promise<TData>;
|
|
45
|
+
reset: () => void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface UseChillSubscriptionState {
|
|
49
|
+
error: ReadonlyRef<unknown>;
|
|
50
|
+
isSubscribed: ReadonlyRef<boolean>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function useSchema(
|
|
54
|
+
chillType: Ref<string> | string,
|
|
55
|
+
chillViewCode: Ref<string> | string = "default",
|
|
56
|
+
cultureName?: Ref<string | undefined> | string,
|
|
57
|
+
update: Ref<boolean | undefined> | boolean = false
|
|
58
|
+
): UseChillAsyncState<JsonObject> {
|
|
59
|
+
const client = useChillSharpClient();
|
|
60
|
+
const data = ref<JsonObject | null>(null);
|
|
61
|
+
const error = ref<unknown>(null);
|
|
62
|
+
const isLoading = ref<boolean>(true);
|
|
63
|
+
|
|
64
|
+
const load = async () => {
|
|
65
|
+
isLoading.value = true;
|
|
66
|
+
error.value = null;
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const response = await client.getSchema(
|
|
70
|
+
readRef(chillType),
|
|
71
|
+
readRef(chillViewCode),
|
|
72
|
+
cultureName === undefined ? undefined : readRef(cultureName),
|
|
73
|
+
readRef(update) ?? false
|
|
74
|
+
);
|
|
75
|
+
data.value = response;
|
|
76
|
+
return response;
|
|
77
|
+
} catch (err) {
|
|
78
|
+
error.value = err;
|
|
79
|
+
throw err;
|
|
80
|
+
} finally {
|
|
81
|
+
isLoading.value = false;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
watchEffect(() => {
|
|
86
|
+
void load();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
data,
|
|
91
|
+
error,
|
|
92
|
+
isLoading,
|
|
93
|
+
reload: load
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function useSchemaList(
|
|
98
|
+
cultureName?: Ref<string | undefined> | string
|
|
99
|
+
): UseChillAsyncState<JsonObject[]> {
|
|
100
|
+
const client = useChillSharpClient();
|
|
101
|
+
const data = ref<JsonObject[] | null>(null);
|
|
102
|
+
const error = ref<unknown>(null);
|
|
103
|
+
const isLoading = ref<boolean>(true);
|
|
104
|
+
|
|
105
|
+
const load = async () => {
|
|
106
|
+
isLoading.value = true;
|
|
107
|
+
error.value = null;
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const response = await client.getSchemaList(cultureName === undefined ? undefined : readRef(cultureName));
|
|
111
|
+
data.value = response;
|
|
112
|
+
return response;
|
|
113
|
+
} catch (err) {
|
|
114
|
+
error.value = err;
|
|
115
|
+
throw err;
|
|
116
|
+
} finally {
|
|
117
|
+
isLoading.value = false;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
watchEffect(() => {
|
|
122
|
+
void load();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
data,
|
|
127
|
+
error,
|
|
128
|
+
isLoading,
|
|
129
|
+
reload: load
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function useText(request: Ref<JsonObject> | JsonObject): UseChillAsyncState<JsonObject> {
|
|
134
|
+
const client = useChillSharpClient();
|
|
135
|
+
const data = ref<JsonObject | null>(null);
|
|
136
|
+
const error = ref<unknown>(null);
|
|
137
|
+
const isLoading = ref<boolean>(true);
|
|
138
|
+
|
|
139
|
+
const load = async () => {
|
|
140
|
+
isLoading.value = true;
|
|
141
|
+
error.value = null;
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const response = await client.getText(readRef(request) as GetTextRequest);
|
|
145
|
+
data.value = response;
|
|
146
|
+
return response;
|
|
147
|
+
} catch (err) {
|
|
148
|
+
error.value = err;
|
|
149
|
+
throw err;
|
|
150
|
+
} finally {
|
|
151
|
+
isLoading.value = false;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
watchEffect(() => {
|
|
156
|
+
void load();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
data,
|
|
161
|
+
error,
|
|
162
|
+
isLoading,
|
|
163
|
+
reload: load
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function useTexts(requests: Ref<JsonObject[]> | JsonObject[]): UseChillAsyncState<Array<JsonObject | null>> {
|
|
168
|
+
const client = useChillSharpClient();
|
|
169
|
+
const data = ref<Array<JsonObject | null> | null>(null);
|
|
170
|
+
const error = ref<unknown>(null);
|
|
171
|
+
const isLoading = ref<boolean>(true);
|
|
172
|
+
|
|
173
|
+
const load = async () => {
|
|
174
|
+
isLoading.value = true;
|
|
175
|
+
error.value = null;
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
const response = await client.getTexts(readRef(requests) as GetTextRequest[]) as Array<JsonObject | null>;
|
|
179
|
+
data.value = response;
|
|
180
|
+
return response;
|
|
181
|
+
} catch (err) {
|
|
182
|
+
error.value = err;
|
|
183
|
+
throw err;
|
|
184
|
+
} finally {
|
|
185
|
+
isLoading.value = false;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
watchEffect(() => {
|
|
190
|
+
void load();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
data,
|
|
195
|
+
error,
|
|
196
|
+
isLoading,
|
|
197
|
+
reload: load
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function useVersion(): string {
|
|
202
|
+
return CHILL_SHARP_VUE_CLIENT_VERSION;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function useTest(): UseChillAsyncState<string> {
|
|
206
|
+
const client = useChillSharpClient();
|
|
207
|
+
const data = ref<string | null>(null);
|
|
208
|
+
const error = ref<unknown>(null);
|
|
209
|
+
const isLoading = ref<boolean>(true);
|
|
210
|
+
|
|
211
|
+
const load = async () => {
|
|
212
|
+
isLoading.value = true;
|
|
213
|
+
error.value = null;
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
const response = await client.test();
|
|
217
|
+
data.value = response;
|
|
218
|
+
return response;
|
|
219
|
+
} catch (err) {
|
|
220
|
+
error.value = err;
|
|
221
|
+
throw err;
|
|
222
|
+
} finally {
|
|
223
|
+
isLoading.value = false;
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
watchEffect(() => {
|
|
228
|
+
void load();
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
data,
|
|
233
|
+
error,
|
|
234
|
+
isLoading,
|
|
235
|
+
reload: load
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function useCurrentUserPreferences(): UseChillAsyncState<ChillUserPreferences> {
|
|
240
|
+
const client = useChillSharpClient();
|
|
241
|
+
// ChillUserPreferences extends the recursive JsonObject type. Keep it shallow so Vue
|
|
242
|
+
// does not recursively unwrap that type while inferring the composable return value.
|
|
243
|
+
const data = shallowRef<ChillUserPreferences | null>(null);
|
|
244
|
+
const error = ref<unknown>(null);
|
|
245
|
+
const isLoading = ref<boolean>(true);
|
|
246
|
+
|
|
247
|
+
const load = async () => {
|
|
248
|
+
isLoading.value = true;
|
|
249
|
+
error.value = null;
|
|
250
|
+
try {
|
|
251
|
+
const response = await client.getCurrentUserPreferences();
|
|
252
|
+
data.value = response;
|
|
253
|
+
return response;
|
|
254
|
+
} catch (err) {
|
|
255
|
+
error.value = err;
|
|
256
|
+
throw err;
|
|
257
|
+
} finally {
|
|
258
|
+
isLoading.value = false;
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
watchEffect(() => { void load(); });
|
|
263
|
+
return { data, error, isLoading, reload: load };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export function useQueryMutation(): UseChillMutationState<JsonObject> {
|
|
267
|
+
const client = useChillSharpClient();
|
|
268
|
+
const data = ref<JsonObject | null>(null);
|
|
269
|
+
const error = ref<unknown>(null);
|
|
270
|
+
const isLoading = ref<boolean>(false);
|
|
271
|
+
|
|
272
|
+
const execute = async (...args: unknown[]) => {
|
|
273
|
+
const payload = args[0] as JsonObject;
|
|
274
|
+
isLoading.value = true;
|
|
275
|
+
error.value = null;
|
|
276
|
+
|
|
277
|
+
try {
|
|
278
|
+
const response = await client.query(payload);
|
|
279
|
+
data.value = response;
|
|
280
|
+
return response;
|
|
281
|
+
} catch (err) {
|
|
282
|
+
error.value = err;
|
|
283
|
+
throw err;
|
|
284
|
+
} finally {
|
|
285
|
+
isLoading.value = false;
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
data,
|
|
291
|
+
error,
|
|
292
|
+
isLoading,
|
|
293
|
+
execute,
|
|
294
|
+
reset: () => {
|
|
295
|
+
data.value = null;
|
|
296
|
+
error.value = null;
|
|
297
|
+
isLoading.value = false;
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function useLookupMutation(): UseChillMutationState<JsonObject> {
|
|
303
|
+
const client = useChillSharpClient();
|
|
304
|
+
const data = ref<JsonObject | null>(null);
|
|
305
|
+
const error = ref<unknown>(null);
|
|
306
|
+
const isLoading = ref<boolean>(false);
|
|
307
|
+
|
|
308
|
+
const execute = async (...args: unknown[]) => {
|
|
309
|
+
const payload = args[0] as JsonObject;
|
|
310
|
+
isLoading.value = true;
|
|
311
|
+
error.value = null;
|
|
312
|
+
|
|
313
|
+
try {
|
|
314
|
+
const response = await client.lookup(payload);
|
|
315
|
+
data.value = response;
|
|
316
|
+
return response;
|
|
317
|
+
} catch (err) {
|
|
318
|
+
error.value = err;
|
|
319
|
+
throw err;
|
|
320
|
+
} finally {
|
|
321
|
+
isLoading.value = false;
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
data,
|
|
327
|
+
error,
|
|
328
|
+
isLoading,
|
|
329
|
+
execute,
|
|
330
|
+
reset: () => {
|
|
331
|
+
data.value = null;
|
|
332
|
+
error.value = null;
|
|
333
|
+
isLoading.value = false;
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export function useAutocompleteMutation(): UseChillMutationState<JsonObject> {
|
|
339
|
+
const client = useChillSharpClient();
|
|
340
|
+
const data = ref<JsonObject | null>(null);
|
|
341
|
+
const error = ref<unknown>(null);
|
|
342
|
+
const isLoading = ref<boolean>(false);
|
|
343
|
+
|
|
344
|
+
const execute = async (...args: unknown[]) => {
|
|
345
|
+
const payload = args[0] as JsonObject;
|
|
346
|
+
isLoading.value = true;
|
|
347
|
+
error.value = null;
|
|
348
|
+
|
|
349
|
+
try {
|
|
350
|
+
const response = await client.autocomplete(payload);
|
|
351
|
+
data.value = response;
|
|
352
|
+
return response;
|
|
353
|
+
} catch (err) {
|
|
354
|
+
error.value = err;
|
|
355
|
+
throw err;
|
|
356
|
+
} finally {
|
|
357
|
+
isLoading.value = false;
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
return {
|
|
362
|
+
data,
|
|
363
|
+
error,
|
|
364
|
+
isLoading,
|
|
365
|
+
execute,
|
|
366
|
+
reset: () => {
|
|
367
|
+
data.value = null;
|
|
368
|
+
error.value = null;
|
|
369
|
+
isLoading.value = false;
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export function useValidateMutation(): UseChillMutationState<ChillValidationError[]> {
|
|
375
|
+
const client = useChillSharpClient();
|
|
376
|
+
const data = ref<ChillValidationError[] | null>(null);
|
|
377
|
+
const error = ref<unknown>(null);
|
|
378
|
+
const isLoading = ref<boolean>(false);
|
|
379
|
+
|
|
380
|
+
const execute = async (...args: unknown[]) => {
|
|
381
|
+
const payload = args[0] as JsonObject;
|
|
382
|
+
isLoading.value = true;
|
|
383
|
+
error.value = null;
|
|
384
|
+
|
|
385
|
+
try {
|
|
386
|
+
const response = await client.validate(payload);
|
|
387
|
+
data.value = response;
|
|
388
|
+
return response;
|
|
389
|
+
} catch (err) {
|
|
390
|
+
error.value = err;
|
|
391
|
+
throw err;
|
|
392
|
+
} finally {
|
|
393
|
+
isLoading.value = false;
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
return {
|
|
398
|
+
data,
|
|
399
|
+
error,
|
|
400
|
+
isLoading,
|
|
401
|
+
execute,
|
|
402
|
+
reset: () => {
|
|
403
|
+
data.value = null;
|
|
404
|
+
error.value = null;
|
|
405
|
+
isLoading.value = false;
|
|
406
|
+
}
|
|
407
|
+
} as UseChillMutationState<ChillValidationError[]>;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export function useEntityMutation(action: "find" | "create" | "update" | "delete"): UseChillMutationState<JsonObject | null> {
|
|
411
|
+
const client = useChillSharpClient();
|
|
412
|
+
const data = ref<JsonObject | null>(null);
|
|
413
|
+
const error = ref<unknown>(null);
|
|
414
|
+
const isLoading = ref<boolean>(false);
|
|
415
|
+
|
|
416
|
+
const execute = async (...args: unknown[]) => {
|
|
417
|
+
const payload = args[0] as JsonObject;
|
|
418
|
+
isLoading.value = true;
|
|
419
|
+
error.value = null;
|
|
420
|
+
|
|
421
|
+
try {
|
|
422
|
+
let response: JsonObject | null;
|
|
423
|
+
switch (action) {
|
|
424
|
+
case "find":
|
|
425
|
+
response = await client.find(payload);
|
|
426
|
+
break;
|
|
427
|
+
case "create":
|
|
428
|
+
response = await client.create(payload);
|
|
429
|
+
break;
|
|
430
|
+
case "update":
|
|
431
|
+
response = await client.update(payload);
|
|
432
|
+
break;
|
|
433
|
+
default:
|
|
434
|
+
await client.delete(payload);
|
|
435
|
+
response = null;
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
data.value = response;
|
|
440
|
+
return response;
|
|
441
|
+
} catch (err) {
|
|
442
|
+
error.value = err;
|
|
443
|
+
throw err;
|
|
444
|
+
} finally {
|
|
445
|
+
isLoading.value = false;
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
return {
|
|
450
|
+
data,
|
|
451
|
+
error,
|
|
452
|
+
isLoading,
|
|
453
|
+
execute,
|
|
454
|
+
reset: () => {
|
|
455
|
+
data.value = null;
|
|
456
|
+
error.value = null;
|
|
457
|
+
isLoading.value = false;
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
export function useEntityChanges(
|
|
463
|
+
chillType: Ref<string> | string,
|
|
464
|
+
onChanges: Ref<ChillEntityChangeCallback> | ChillEntityChangeCallback,
|
|
465
|
+
guid?: Ref<string | null | undefined> | string | null
|
|
466
|
+
): UseChillSubscriptionState {
|
|
467
|
+
const client = useChillSharpClient();
|
|
468
|
+
const error = ref<unknown>(null);
|
|
469
|
+
const isSubscribed = ref<boolean>(false);
|
|
470
|
+
let subscription: ChillEntityChangeSubscription | null = null;
|
|
471
|
+
|
|
472
|
+
const release = async () => {
|
|
473
|
+
const currentSubscription = subscription;
|
|
474
|
+
subscription = null;
|
|
475
|
+
isSubscribed.value = false;
|
|
476
|
+
if (currentSubscription) {
|
|
477
|
+
await currentSubscription.unsubscribe();
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
watch(
|
|
482
|
+
[
|
|
483
|
+
() => readRef(chillType),
|
|
484
|
+
() => (guid === undefined ? null : (readRef(guid) ?? null)),
|
|
485
|
+
() => readRef(onChanges)
|
|
486
|
+
],
|
|
487
|
+
async ([nextChillType, nextGuid, nextOnChanges], _previousValue, onCleanup) => {
|
|
488
|
+
error.value = null;
|
|
489
|
+
await release();
|
|
490
|
+
|
|
491
|
+
let isActive = true;
|
|
492
|
+
onCleanup(() => {
|
|
493
|
+
isActive = false;
|
|
494
|
+
void release();
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
try {
|
|
498
|
+
const nextSubscription = await client.subscribeToEntityChanges(
|
|
499
|
+
nextChillType,
|
|
500
|
+
async (changes) => {
|
|
501
|
+
await nextOnChanges(changes);
|
|
502
|
+
},
|
|
503
|
+
nextGuid
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
if (!isActive) {
|
|
507
|
+
await nextSubscription.unsubscribe();
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
subscription = nextSubscription;
|
|
512
|
+
isSubscribed.value = true;
|
|
513
|
+
} catch (err) {
|
|
514
|
+
if (isActive) {
|
|
515
|
+
error.value = err;
|
|
516
|
+
isSubscribed.value = false;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
},
|
|
520
|
+
{ immediate: true }
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
onScopeDispose(() => {
|
|
524
|
+
void release();
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
return {
|
|
528
|
+
error,
|
|
529
|
+
isSubscribed
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
type ReadonlyRef<T> = Ref<T>;
|
|
534
|
+
|
|
535
|
+
function asReadonlyRef<T>(value: Ref<T>): ReadonlyRef<T> {
|
|
536
|
+
return value;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function readRef<T>(value: Ref<T> | T): T {
|
|
540
|
+
if (typeof value === "object" && value !== null && "value" in value) {
|
|
541
|
+
return value.value;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return value;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
|