@ramathibodi/nuxt-commons 4.0.4 → 4.0.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/dist/module.json +1 -1
- package/dist/runtime/components/form/DateTime.vue +2 -6
- package/dist/runtime/components/model/Table.d.vue.ts +50 -50
- package/dist/runtime/components/model/Table.vue.d.ts +50 -50
- package/dist/runtime/components/model/iterator.d.vue.ts +62 -62
- package/dist/runtime/components/model/iterator.vue.d.ts +62 -62
- package/dist/runtime/composables/alert.d.ts +50 -2
- package/dist/runtime/composables/api.d.ts +60 -4
- package/dist/runtime/composables/api.js +59 -9
- package/dist/runtime/composables/apiLookupList.d.ts +3 -2
- package/dist/runtime/composables/apiLookupList.js +5 -16
- package/dist/runtime/composables/apiModel.d.ts +1 -1
- package/dist/runtime/composables/apiModel.js +13 -22
- package/dist/runtime/composables/apiModelItem.js +6 -23
- package/dist/runtime/composables/apiModelOperation.d.ts +19 -0
- package/dist/runtime/composables/apiModelOperation.js +56 -0
- package/dist/runtime/composables/graphql.d.ts +2 -1
- package/dist/runtime/composables/graphqlOperation.js +1 -0
- package/package.json +1 -1
|
@@ -37,12 +37,12 @@ declare function openDialog(item?: object): void;
|
|
|
37
37
|
declare var __VLS_15: {
|
|
38
38
|
operation: {
|
|
39
39
|
openDialog: typeof openDialog;
|
|
40
|
-
createItem: (
|
|
41
|
-
importItems: (
|
|
42
|
-
updateItem: (
|
|
43
|
-
deleteItem: (
|
|
44
|
-
reload: (
|
|
45
|
-
setSearch: (
|
|
40
|
+
createItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback, importing?: boolean) => Promise<any>;
|
|
41
|
+
importItems: (importItemsList: Record<string, any>[], callback?: import("../../types/formDialog.js").FormDialogCallback) => void;
|
|
42
|
+
updateItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
43
|
+
deleteItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
44
|
+
reload: () => void;
|
|
45
|
+
setSearch: (keyword: string) => void;
|
|
46
46
|
canServerPageable: boolean;
|
|
47
47
|
canServerSearch: boolean;
|
|
48
48
|
canCreate: boolean;
|
|
@@ -73,12 +73,12 @@ declare var __VLS_15: {
|
|
|
73
73
|
item: import("vuetify/lib/components/VDataIterator/composables/items.mjs").DataIteratorItem<Record<string, any>>;
|
|
74
74
|
operation: {
|
|
75
75
|
openDialog: typeof openDialog;
|
|
76
|
-
createItem: (
|
|
77
|
-
importItems: (
|
|
78
|
-
updateItem: (
|
|
79
|
-
deleteItem: (
|
|
80
|
-
reload: (
|
|
81
|
-
setSearch: (
|
|
76
|
+
createItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback, importing?: boolean) => Promise<any>;
|
|
77
|
+
importItems: (importItemsList: Record<string, any>[], callback?: import("../../types/formDialog.js").FormDialogCallback) => void;
|
|
78
|
+
updateItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
79
|
+
deleteItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
80
|
+
reload: () => void;
|
|
81
|
+
setSearch: (keyword: string) => void;
|
|
82
82
|
canServerPageable: boolean;
|
|
83
83
|
canServerSearch: boolean;
|
|
84
84
|
canCreate: boolean;
|
|
@@ -92,12 +92,12 @@ declare var __VLS_15: {
|
|
|
92
92
|
items: Record<string, any>[];
|
|
93
93
|
operation: {
|
|
94
94
|
openDialog: typeof openDialog;
|
|
95
|
-
createItem: (
|
|
96
|
-
importItems: (
|
|
97
|
-
updateItem: (
|
|
98
|
-
deleteItem: (
|
|
99
|
-
reload: (
|
|
100
|
-
setSearch: (
|
|
95
|
+
createItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback, importing?: boolean) => Promise<any>;
|
|
96
|
+
importItems: (importItemsList: Record<string, any>[], callback?: import("../../types/formDialog.js").FormDialogCallback) => void;
|
|
97
|
+
updateItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
98
|
+
deleteItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
99
|
+
reload: () => void;
|
|
100
|
+
setSearch: (keyword: string) => void;
|
|
101
101
|
canServerPageable: boolean;
|
|
102
102
|
canServerSearch: boolean;
|
|
103
103
|
canCreate: boolean;
|
|
@@ -124,17 +124,17 @@ declare var __VLS_15: {
|
|
|
124
124
|
itemsCount: number;
|
|
125
125
|
groupedItems: readonly (import("vuetify/lib/components/VDataIterator/composables/items.mjs").DataIteratorItem<Record<string, any>> | import("vuetify/lib/components/VDataTable/composables/group.mjs").Group<import("vuetify/lib/components/VDataIterator/composables/items.mjs").DataIteratorItem<Record<string, any>>> | import("vuetify/lib/components/VDataTable/composables/group.mjs").GroupSummary<import("vuetify/lib/components/VDataIterator/composables/items.mjs").DataIteratorItem<Record<string, any>>>)[];
|
|
126
126
|
}, __VLS_92: {
|
|
127
|
-
reload: (
|
|
127
|
+
reload: () => void;
|
|
128
128
|
}, __VLS_108: {
|
|
129
129
|
items: Record<string, any>[];
|
|
130
130
|
operation: {
|
|
131
131
|
openDialog: typeof openDialog;
|
|
132
|
-
createItem: (
|
|
133
|
-
importItems: (
|
|
134
|
-
updateItem: (
|
|
135
|
-
deleteItem: (
|
|
136
|
-
reload: (
|
|
137
|
-
setSearch: (
|
|
132
|
+
createItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback, importing?: boolean) => Promise<any>;
|
|
133
|
+
importItems: (importItemsList: Record<string, any>[], callback?: import("../../types/formDialog.js").FormDialogCallback) => void;
|
|
134
|
+
updateItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
135
|
+
deleteItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
136
|
+
reload: () => void;
|
|
137
|
+
setSearch: (keyword: string) => void;
|
|
138
138
|
canServerPageable: boolean;
|
|
139
139
|
canServerSearch: boolean;
|
|
140
140
|
canCreate: boolean;
|
|
@@ -145,12 +145,12 @@ declare var __VLS_15: {
|
|
|
145
145
|
items: Record<string, any>[];
|
|
146
146
|
operation: {
|
|
147
147
|
openDialog: typeof openDialog;
|
|
148
|
-
createItem: (
|
|
149
|
-
importItems: (
|
|
150
|
-
updateItem: (
|
|
151
|
-
deleteItem: (
|
|
152
|
-
reload: (
|
|
153
|
-
setSearch: (
|
|
148
|
+
createItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback, importing?: boolean) => Promise<any>;
|
|
149
|
+
importItems: (importItemsList: Record<string, any>[], callback?: import("../../types/formDialog.js").FormDialogCallback) => void;
|
|
150
|
+
updateItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
151
|
+
deleteItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
152
|
+
reload: () => void;
|
|
153
|
+
setSearch: (keyword: string) => void;
|
|
154
154
|
canServerPageable: boolean;
|
|
155
155
|
canServerSearch: boolean;
|
|
156
156
|
canCreate: boolean;
|
|
@@ -160,12 +160,12 @@ declare var __VLS_15: {
|
|
|
160
160
|
}, __VLS_175: never, __VLS_176: {
|
|
161
161
|
operation: {
|
|
162
162
|
openDialog: typeof openDialog;
|
|
163
|
-
createItem: (
|
|
164
|
-
importItems: (
|
|
165
|
-
updateItem: (
|
|
166
|
-
deleteItem: (
|
|
167
|
-
reload: (
|
|
168
|
-
setSearch: (
|
|
163
|
+
createItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback, importing?: boolean) => Promise<any>;
|
|
164
|
+
importItems: (importItemsList: Record<string, any>[], callback?: import("../../types/formDialog.js").FormDialogCallback) => void;
|
|
165
|
+
updateItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
166
|
+
deleteItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
167
|
+
reload: () => void;
|
|
168
|
+
setSearch: (keyword: string) => void;
|
|
169
169
|
canServerPageable: boolean;
|
|
170
170
|
canServerSearch: boolean;
|
|
171
171
|
canCreate: boolean;
|
|
@@ -175,12 +175,12 @@ declare var __VLS_15: {
|
|
|
175
175
|
}, __VLS_201: never, __VLS_202: {
|
|
176
176
|
operation: {
|
|
177
177
|
openDialog: typeof openDialog;
|
|
178
|
-
createItem: (
|
|
179
|
-
importItems: (
|
|
180
|
-
updateItem: (
|
|
181
|
-
deleteItem: (
|
|
182
|
-
reload: (
|
|
183
|
-
setSearch: (
|
|
178
|
+
createItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback, importing?: boolean) => Promise<any>;
|
|
179
|
+
importItems: (importItemsList: Record<string, any>[], callback?: import("../../types/formDialog.js").FormDialogCallback) => void;
|
|
180
|
+
updateItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
181
|
+
deleteItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
182
|
+
reload: () => void;
|
|
183
|
+
setSearch: (keyword: string) => void;
|
|
184
184
|
canServerPageable: boolean;
|
|
185
185
|
canServerSearch: boolean;
|
|
186
186
|
canCreate: boolean;
|
|
@@ -233,15 +233,15 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
|
|
|
233
233
|
itemsPerPage: number;
|
|
234
234
|
api: boolean;
|
|
235
235
|
}>>, {
|
|
236
|
-
reload: (
|
|
236
|
+
reload: () => void;
|
|
237
237
|
operation: import("vue").Ref<{
|
|
238
238
|
openDialog: typeof openDialog;
|
|
239
|
-
createItem: (
|
|
240
|
-
importItems: (
|
|
241
|
-
updateItem: (
|
|
242
|
-
deleteItem: (
|
|
243
|
-
reload: (
|
|
244
|
-
setSearch: (
|
|
239
|
+
createItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback, importing?: boolean) => Promise<any>;
|
|
240
|
+
importItems: (importItemsList: Record<string, any>[], callback?: import("../../types/formDialog.js").FormDialogCallback) => void;
|
|
241
|
+
updateItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
242
|
+
deleteItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
243
|
+
reload: () => void;
|
|
244
|
+
setSearch: (keyword: string) => void;
|
|
245
245
|
canServerPageable: boolean;
|
|
246
246
|
canServerSearch: boolean;
|
|
247
247
|
canCreate: boolean;
|
|
@@ -249,12 +249,12 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
|
|
|
249
249
|
canDelete: boolean;
|
|
250
250
|
}, {
|
|
251
251
|
openDialog: typeof openDialog;
|
|
252
|
-
createItem: (
|
|
253
|
-
importItems: (
|
|
254
|
-
updateItem: (
|
|
255
|
-
deleteItem: (
|
|
256
|
-
reload: (
|
|
257
|
-
setSearch: (
|
|
252
|
+
createItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback, importing?: boolean) => Promise<any>;
|
|
253
|
+
importItems: (importItemsList: Record<string, any>[], callback?: import("../../types/formDialog.js").FormDialogCallback) => void;
|
|
254
|
+
updateItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
255
|
+
deleteItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
256
|
+
reload: () => void;
|
|
257
|
+
setSearch: (keyword: string) => void;
|
|
258
258
|
canServerPageable: import("vue").ComputedRef<boolean>;
|
|
259
259
|
canServerSearch: import("vue").ComputedRef<boolean>;
|
|
260
260
|
canCreate: import("vue").ComputedRef<boolean>;
|
|
@@ -262,12 +262,12 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
|
|
|
262
262
|
canDelete: import("vue").ComputedRef<boolean>;
|
|
263
263
|
} | {
|
|
264
264
|
openDialog: typeof openDialog;
|
|
265
|
-
createItem: (
|
|
266
|
-
importItems: (
|
|
267
|
-
updateItem: (
|
|
268
|
-
deleteItem: (
|
|
269
|
-
reload: (
|
|
270
|
-
setSearch: (
|
|
265
|
+
createItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback, importing?: boolean) => Promise<any>;
|
|
266
|
+
importItems: (importItemsList: Record<string, any>[], callback?: import("../../types/formDialog.js").FormDialogCallback) => void;
|
|
267
|
+
updateItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
268
|
+
deleteItem: (item: Record<string, any>, callback?: import("../../types/formDialog.js").FormDialogCallback) => Promise<any>;
|
|
269
|
+
reload: () => void;
|
|
270
|
+
setSearch: (keyword: string) => void;
|
|
271
271
|
canServerPageable: boolean;
|
|
272
272
|
canServerSearch: boolean;
|
|
273
273
|
canCreate: boolean;
|
|
@@ -1,6 +1,54 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Reactive alert queue shared between `createAlert()` (provider) and `useAlert()` (consumer).
|
|
3
|
+
*
|
|
4
|
+
* ## Setup
|
|
5
|
+
* `createAlert()` must be called **once** in an ancestor component — typically the root
|
|
6
|
+
* `app.vue` in a Nuxt app — to register the provider via Vue `provide()`. The `<Alert />`
|
|
7
|
+
* component only *consumes* the queue; it does not set it up. If no ancestor calls
|
|
8
|
+
* `createAlert()`, every `useAlert()` call returns `null` and toast pushes will throw.
|
|
9
|
+
*
|
|
10
|
+
* ```vue
|
|
11
|
+
* <!-- app.vue -->
|
|
12
|
+
* <script lang="ts" setup>
|
|
13
|
+
* createAlert()
|
|
14
|
+
* </script>
|
|
15
|
+
*
|
|
16
|
+
* <template>
|
|
17
|
+
* <VApp>
|
|
18
|
+
* <NuxtLayout><NuxtPage /></NuxtLayout>
|
|
19
|
+
* <DialogHost />
|
|
20
|
+
* <Alert />
|
|
21
|
+
* </VApp>
|
|
22
|
+
* </template>
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* ## Usage
|
|
26
|
+
* `useAlert()` returns an `AlertProvide | null`. There are **no** convenience methods
|
|
27
|
+
* like `.success()` / `.error()` / `.warning()` / `.info()` — push items via `addAlert`:
|
|
28
|
+
*
|
|
29
|
+
* ```ts
|
|
30
|
+
* const alert = useAlert()
|
|
31
|
+
* alert?.addAlert({ alertType: 'success', message: 'Saved' })
|
|
32
|
+
* alert?.addAlert({ alertType: 'error', message: 'Failed: ' + err.message })
|
|
33
|
+
* alert?.addAlert({ alertType: 'warning', message: 'Check your input' })
|
|
34
|
+
* alert?.addAlert({ alertType: 'info', message: 'Processing...' })
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* `alertType` must be one of `'success' | 'error' | 'warning' | 'info'`. `message` is
|
|
38
|
+
* required — items without a `message` are silently dropped. Optional fields:
|
|
39
|
+
* `alertLocation` (default `'default'`, lets multiple `<Alert />` hosts filter),
|
|
40
|
+
* `alertExpireIn` (ms, default 60000), `alertIcon`, `data`.
|
|
41
|
+
*
|
|
42
|
+
* Always null-check the handle: `alert?.addAlert(...)`.
|
|
43
|
+
*
|
|
44
|
+
* ## Returned API (`AlertProvide`)
|
|
45
|
+
* - `addAlert(item)` — push a new alert onto the queue.
|
|
46
|
+
* - `takeAlert(location?)` — pop the next alert for the given location (default `'default'`).
|
|
47
|
+
* - `hasAlert(location?)` — whether anything is queued for the given location.
|
|
48
|
+
* - `clear()` — drop all queued alerts.
|
|
49
|
+
* - `items` — the underlying reactive ref (for custom consumers).
|
|
50
|
+
*
|
|
51
|
+
* Expired items (past `alertDateTime + alertExpireIn`) are auto-pruned once per second.
|
|
4
52
|
*/
|
|
5
53
|
import { type InjectionKey, type Ref } from 'vue';
|
|
6
54
|
import type { AlertItem } from '../types/alert.js';
|
|
@@ -1,14 +1,70 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useApi wraps authenticated HTTP calls, URL resolution, and optional response caching for runtime services.
|
|
3
|
+
*
|
|
4
|
+
* Caching contract for the `cache` parameter on get / getPromise / post / postPromise:
|
|
5
|
+
*
|
|
6
|
+
* - `false` (default) — no caching, every call hits the network.
|
|
7
|
+
* - `true` — cache the response for 5 minutes (legacy default; preserved
|
|
8
|
+
* for back-compat). Prefer `{ ttlSeconds }` for new code.
|
|
9
|
+
* - `{ ttlSeconds: 5 }` — cache for exactly 5 seconds. PREFERRED form.
|
|
10
|
+
* - `{ ttlMs: 5000 }` — same, expressed in milliseconds.
|
|
11
|
+
* - `number` (legacy) — DEPRECATED. Treated by an undocumented size-based
|
|
12
|
+
* heuristic where values <= 60 are read as MINUTES and
|
|
13
|
+
* values > 60 as SECONDS. Emits a console warning the
|
|
14
|
+
* first time a session sees this form. Migrate to
|
|
15
|
+
* `{ ttlSeconds }` / `{ ttlMs }` to remove the warning
|
|
16
|
+
* and get unambiguous behaviour.
|
|
17
|
+
*
|
|
18
|
+
* Mutations that change resources read by a cached query should call
|
|
19
|
+
* `useApi().invalidate(prefix)` so the next read fetches fresh data. Without that,
|
|
20
|
+
* cached entries stay valid until their TTL expires — `localStorage` survives reloads,
|
|
21
|
+
* so users can see stale empty / outdated lists for the full TTL window even after
|
|
22
|
+
* creating data themselves.
|
|
23
|
+
*
|
|
3
24
|
* This doc block is consumed by vue-docgen for generated API documentation.
|
|
4
25
|
*/
|
|
5
26
|
import type { UseFetchOptions } from 'nuxt/app';
|
|
6
27
|
import type { SearchParameters } from 'ofetch';
|
|
28
|
+
/**
|
|
29
|
+
* Explicit cache options. Use this in new code instead of passing a bare number.
|
|
30
|
+
* - `ttlSeconds`: cache lifetime in seconds. Mutually exclusive with `ttlMs`.
|
|
31
|
+
* - `ttlMs` : cache lifetime in milliseconds. Mutually exclusive with `ttlSeconds`.
|
|
32
|
+
*/
|
|
33
|
+
export type CacheOption = boolean | number | {
|
|
34
|
+
ttlSeconds: number;
|
|
35
|
+
} | {
|
|
36
|
+
ttlMs: number;
|
|
37
|
+
};
|
|
38
|
+
export declare function _resetLegacyHeuristicWarning(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Resolve a cache option to seconds. Pure function, exported for tests.
|
|
41
|
+
*
|
|
42
|
+
* - false / 0 / null / undefined → 0 (no cache)
|
|
43
|
+
* - true → DEFAULT_TTL_SECONDS_FOR_TRUE
|
|
44
|
+
* - { ttlSeconds: n } → n (clamped to >= 0, floored)
|
|
45
|
+
* - { ttlMs: n } → ceil(n / 1000)
|
|
46
|
+
* - number (legacy) → heuristic + warn
|
|
47
|
+
*/
|
|
48
|
+
export declare function resolveCacheTtlSeconds(cache: CacheOption | undefined | null): number;
|
|
49
|
+
/**
|
|
50
|
+
* Drop every `api-cache-*` localStorage entry whose stored URL contains `prefix`.
|
|
51
|
+
* Pass `undefined` (or no arg) to drop ALL api-cache entries.
|
|
52
|
+
*
|
|
53
|
+
* Recommended invalidation path after a mutation:
|
|
54
|
+
*
|
|
55
|
+
* await useApi().postPromise('/model/resource/create', input)
|
|
56
|
+
* useApi().invalidate('/model/resource') // drops list + lookup caches
|
|
57
|
+
*
|
|
58
|
+
* Returns the number of entries removed. Safe to call when the cache is empty.
|
|
59
|
+
*/
|
|
60
|
+
declare function invalidateCache(prefix?: string): number;
|
|
7
61
|
export declare function useApi(): {
|
|
8
62
|
urlBuilder: (url: string | string[]) => string;
|
|
9
|
-
get: <T>(url: string | string[], body?: Record<string, any> | [], params?: SearchParameters, options?: UseFetchOptions<unknown>, cache?:
|
|
10
|
-
getPromise: <T>(url: string | string[], body?: Record<string, any> | [], params?: SearchParameters, options?: UseFetchOptions<unknown>, cache?:
|
|
11
|
-
post: <T>(url: string | string[], body?: Record<string, any> | [], params?: SearchParameters, options?: UseFetchOptions<unknown>, cache?:
|
|
12
|
-
postPromise: <T>(url: string | string[], body?: Record<string, any> | [], params?: SearchParameters, options?: UseFetchOptions<unknown>, cache?:
|
|
63
|
+
get: <T>(url: string | string[], body?: Record<string, any> | [], params?: SearchParameters, options?: UseFetchOptions<unknown>, cache?: CacheOption) => Promise<T>;
|
|
64
|
+
getPromise: <T>(url: string | string[], body?: Record<string, any> | [], params?: SearchParameters, options?: UseFetchOptions<unknown>, cache?: CacheOption) => Promise<T>;
|
|
65
|
+
post: <T>(url: string | string[], body?: Record<string, any> | [], params?: SearchParameters, options?: UseFetchOptions<unknown>, cache?: CacheOption) => Promise<T>;
|
|
66
|
+
postPromise: <T>(url: string | string[], body?: Record<string, any> | [], params?: SearchParameters, options?: UseFetchOptions<unknown>, cache?: CacheOption) => Promise<T>;
|
|
13
67
|
hashKey: (data: any) => Promise<string>;
|
|
68
|
+
invalidate: typeof invalidateCache;
|
|
14
69
|
};
|
|
70
|
+
export {};
|
|
@@ -5,9 +5,65 @@ import { stableStringify } from "../utils/object.js";
|
|
|
5
5
|
import { sha256 } from "../utils/hash.js";
|
|
6
6
|
import { useRuntimeConfig } from "#imports";
|
|
7
7
|
import { useAuthentication } from "../bridges/authentication.js";
|
|
8
|
+
const DEFAULT_TTL_SECONDS_FOR_TRUE = 5 * 60;
|
|
9
|
+
const CACHE_PREFIX = "api-cache-";
|
|
10
|
+
let _legacyHeuristicWarned = false;
|
|
11
|
+
function warnLegacyHeuristic(value, resolvedSeconds) {
|
|
12
|
+
if (_legacyHeuristicWarned) return;
|
|
13
|
+
_legacyHeuristicWarned = true;
|
|
14
|
+
console.warn(
|
|
15
|
+
`[useApi] cache=${value} resolved to ${resolvedSeconds}s via the legacy heuristic (values <=60 are minutes, >60 are seconds). This is ambiguous \u2014 pass { ttlSeconds: N } or { ttlMs: N } instead. See useApi() docblock for details.`
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
export function _resetLegacyHeuristicWarning() {
|
|
19
|
+
_legacyHeuristicWarned = false;
|
|
20
|
+
}
|
|
21
|
+
export function resolveCacheTtlSeconds(cache) {
|
|
22
|
+
if (!cache) return 0;
|
|
23
|
+
if (typeof cache === "boolean") return DEFAULT_TTL_SECONDS_FOR_TRUE;
|
|
24
|
+
if (typeof cache === "number") {
|
|
25
|
+
const n = Math.max(0, Number(cache));
|
|
26
|
+
if (!Number.isFinite(n) || n === 0) return 0;
|
|
27
|
+
const resolved = n > 60 ? n : n * 60;
|
|
28
|
+
warnLegacyHeuristic(n, resolved);
|
|
29
|
+
return resolved;
|
|
30
|
+
}
|
|
31
|
+
if (typeof cache === "object") {
|
|
32
|
+
if ("ttlSeconds" in cache) return Math.max(0, Math.floor(Number(cache.ttlSeconds) || 0));
|
|
33
|
+
if ("ttlMs" in cache) return Math.max(0, Math.ceil((Number(cache.ttlMs) || 0) / 1e3));
|
|
34
|
+
}
|
|
35
|
+
return 0;
|
|
36
|
+
}
|
|
37
|
+
function invalidateCache(prefix) {
|
|
38
|
+
if (typeof window === "undefined" || !window.localStorage) return 0;
|
|
39
|
+
const keys = [];
|
|
40
|
+
for (let i = 0; i < window.localStorage.length; i++) {
|
|
41
|
+
const k = window.localStorage.key(i);
|
|
42
|
+
if (k && k.startsWith(CACHE_PREFIX)) keys.push(k);
|
|
43
|
+
}
|
|
44
|
+
let removed = 0;
|
|
45
|
+
for (const k of keys) {
|
|
46
|
+
if (!prefix) {
|
|
47
|
+
try {
|
|
48
|
+
ls.remove(k);
|
|
49
|
+
removed++;
|
|
50
|
+
} catch {
|
|
51
|
+
}
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const raw = window.localStorage.getItem(k);
|
|
56
|
+
if (raw && raw.includes(prefix)) {
|
|
57
|
+
ls.remove(k);
|
|
58
|
+
removed++;
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return removed;
|
|
64
|
+
}
|
|
8
65
|
export function useApi() {
|
|
9
66
|
const config = useRuntimeConfig();
|
|
10
|
-
const CACHE_PREFIX = "api-cache-";
|
|
11
67
|
function urlBuilder(url) {
|
|
12
68
|
let returnUrl;
|
|
13
69
|
if (Array.isArray(url)) {
|
|
@@ -64,12 +120,6 @@ export function useApi() {
|
|
|
64
120
|
const jsonString = stableStringify(data);
|
|
65
121
|
return sha256(jsonString);
|
|
66
122
|
}
|
|
67
|
-
function computeTtlSecond(cache) {
|
|
68
|
-
if (!cache) return 0;
|
|
69
|
-
if (typeof cache === "boolean") return 5 * 60;
|
|
70
|
-
let ttlNum = Math.max(0, Number(cache));
|
|
71
|
-
return ttlNum > 60 ? ttlNum : ttlNum * 60;
|
|
72
|
-
}
|
|
73
123
|
function safeGetCache(key) {
|
|
74
124
|
try {
|
|
75
125
|
const v = ls.get(key);
|
|
@@ -80,7 +130,7 @@ export function useApi() {
|
|
|
80
130
|
}
|
|
81
131
|
async function ofetchWithCache(url, method, body, params, options, cache) {
|
|
82
132
|
const builtUrl = urlBuilder(url);
|
|
83
|
-
const ttl =
|
|
133
|
+
const ttl = resolveCacheTtlSeconds(cache);
|
|
84
134
|
if (ttl === 0) {
|
|
85
135
|
return ofetch(builtUrl, optionBuilder(method, body, params, options));
|
|
86
136
|
}
|
|
@@ -94,5 +144,5 @@ export function useApi() {
|
|
|
94
144
|
ls.set(key, result, { ttl });
|
|
95
145
|
return result;
|
|
96
146
|
}
|
|
97
|
-
return { urlBuilder, get, getPromise, post, postPromise, hashKey };
|
|
147
|
+
return { urlBuilder, get, getPromise, post, postPromise, hashKey, invalidate: invalidateCache };
|
|
98
148
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* useApiLookupList loads and normalizes lookup options for fields that depend on remote or cached reference lists
|
|
3
|
-
*
|
|
2
|
+
* useApiLookupList loads and normalizes lookup options for fields that depend on remote or cached reference lists.
|
|
3
|
+
* Routes both the lookup-load Read (props.modelName) and the selection-resolution Read (props.modelSelectedItem)
|
|
4
|
+
* through useApiModelOperation, which owns the API-mode URL/body rules.
|
|
4
5
|
* This doc block is consumed by vue-docgen for generated API documentation.
|
|
5
6
|
*/
|
|
6
7
|
import { type Ref } from 'vue';
|
|
@@ -2,8 +2,7 @@ import { computed, ref, shallowRef, watch } from "vue";
|
|
|
2
2
|
import { watchDebounced } from "@vueuse/core";
|
|
3
3
|
import { union, isEmpty, isArray, sortBy, castArray } from "lodash-es";
|
|
4
4
|
import { useFuzzy } from "./utils/fuzzy.js";
|
|
5
|
-
import {
|
|
6
|
-
import { buildApiEndpoint } from "./apiModel.js";
|
|
5
|
+
import { useApiModelOperation } from "./apiModelOperation.js";
|
|
7
6
|
export function useApiLookupList(props, emit, selectedItems) {
|
|
8
7
|
const modelItems = shallowRef([]);
|
|
9
8
|
const items = shallowRef([]);
|
|
@@ -50,13 +49,8 @@ export function useApiLookupList(props, emit, selectedItems) {
|
|
|
50
49
|
variables[props.serverSearchKey] = searchData.value;
|
|
51
50
|
}
|
|
52
51
|
try {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
{ fields: queryFields.value, variables },
|
|
56
|
-
void 0,
|
|
57
|
-
void 0,
|
|
58
|
-
props.cache
|
|
59
|
-
);
|
|
52
|
+
const ops = useApiModelOperation(props.modelName);
|
|
53
|
+
const result = await ops.read(queryFields.value, variables, props.cache);
|
|
60
54
|
if (id !== requestId) return;
|
|
61
55
|
if (isArray(result)) {
|
|
62
56
|
modelItems.value = result;
|
|
@@ -127,13 +121,8 @@ export function useApiLookupList(props, emit, selectedItems) {
|
|
|
127
121
|
const key = props.modelSelectedItemKey || "id";
|
|
128
122
|
const variables = { ...props.modelSelectedItemBy || {} };
|
|
129
123
|
variables[key] = stillMissing;
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
{ fields: queryFields.value, variables },
|
|
133
|
-
void 0,
|
|
134
|
-
void 0,
|
|
135
|
-
props.cache
|
|
136
|
-
);
|
|
124
|
+
const ops = useApiModelOperation(props.modelSelectedItem);
|
|
125
|
+
const result = await ops.read(queryFields.value, variables, props.cache);
|
|
137
126
|
const resolved = castArray(result);
|
|
138
127
|
for (const obj of resolved) {
|
|
139
128
|
const v = obj?.[props.itemValue];
|
|
@@ -33,7 +33,7 @@ export declare function useApiModel<T extends ApiModelComposableProps>(props: T)
|
|
|
33
33
|
createItem: (item: Record<string, any>, callback?: FormDialogCallback, importing?: boolean) => Promise<any>;
|
|
34
34
|
importItems: (importItemsList: Record<string, any>[], callback?: FormDialogCallback) => void;
|
|
35
35
|
updateItem: (item: Record<string, any>, callback?: FormDialogCallback) => Promise<any>;
|
|
36
|
-
deleteItem: (item: Record<string, any>, callback?: FormDialogCallback) => Promise<
|
|
36
|
+
deleteItem: (item: Record<string, any>, callback?: FormDialogCallback) => Promise<any>;
|
|
37
37
|
loadItems: (options: any) => void;
|
|
38
38
|
reload: () => void;
|
|
39
39
|
isLoading: import("vue").Ref<boolean, boolean>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { computed, onMounted, ref } from "vue";
|
|
2
2
|
import { watchDebounced } from "@vueuse/core";
|
|
3
3
|
import { useAlert } from "./alert.js";
|
|
4
|
-
import {
|
|
4
|
+
import { useApiModelOperation } from "./apiModelOperation.js";
|
|
5
5
|
import { arrayWrap } from "../utils/array.js";
|
|
6
6
|
import pLimit from "p-limit";
|
|
7
7
|
export function stripModelByQualifier(modelName) {
|
|
@@ -14,7 +14,7 @@ export function buildApiEndpoint(modelName, action) {
|
|
|
14
14
|
}
|
|
15
15
|
export function useApiModel(props) {
|
|
16
16
|
const alert = useAlert();
|
|
17
|
-
const
|
|
17
|
+
const ops = computed(() => useApiModelOperation(props.modelName));
|
|
18
18
|
const items = ref([]);
|
|
19
19
|
const itemsLength = ref(0);
|
|
20
20
|
const search = ref();
|
|
@@ -49,13 +49,9 @@ export function useApiModel(props) {
|
|
|
49
49
|
}
|
|
50
50
|
return [.../* @__PURE__ */ new Set([...fieldsProps, ...tmpFields])];
|
|
51
51
|
});
|
|
52
|
-
function mutationModelName() {
|
|
53
|
-
return stripModelByQualifier(props.modelName);
|
|
54
|
-
}
|
|
55
52
|
function createItem(item, callback, importing = false) {
|
|
56
53
|
isLoading.value = true;
|
|
57
|
-
|
|
58
|
-
return api.postPromise(endpoint, { input: item, fields: fields.value }).then((result) => {
|
|
54
|
+
return ops.value.create(item, fields.value).then((result) => {
|
|
59
55
|
if (canServerPageable.value) {
|
|
60
56
|
if (!importing) loadItems(currentOptions.value);
|
|
61
57
|
} else {
|
|
@@ -92,8 +88,7 @@ export function useApiModel(props) {
|
|
|
92
88
|
}
|
|
93
89
|
function updateItem(item, callback) {
|
|
94
90
|
isLoading.value = true;
|
|
95
|
-
|
|
96
|
-
return api.postPromise(endpoint, { input: item, fields: fields.value }).then((result) => {
|
|
91
|
+
return ops.value.update(item, fields.value).then((result) => {
|
|
97
92
|
if (canServerPageable.value) {
|
|
98
93
|
loadItems(currentOptions.value);
|
|
99
94
|
} else {
|
|
@@ -113,8 +108,7 @@ export function useApiModel(props) {
|
|
|
113
108
|
}
|
|
114
109
|
function deleteItem(item, callback) {
|
|
115
110
|
isLoading.value = true;
|
|
116
|
-
|
|
117
|
-
return api.postPromise(endpoint, { input: item, fields: fields.value }).catch((error) => {
|
|
111
|
+
return ops.value.delete(item, fields.value).catch((error) => {
|
|
118
112
|
alert?.addAlert({ alertType: "error", message: error?.message || String(error) });
|
|
119
113
|
}).finally(() => {
|
|
120
114
|
isLoading.value = false;
|
|
@@ -124,17 +118,15 @@ export function useApiModel(props) {
|
|
|
124
118
|
}
|
|
125
119
|
function loadItems(options) {
|
|
126
120
|
currentOptions.value = options;
|
|
127
|
-
const endpoint = buildApiEndpoint(props.modelName, "pageable");
|
|
128
121
|
isLoading.value = true;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
fields: fields.value,
|
|
122
|
+
ops.value.readPageable(
|
|
123
|
+
fields.value,
|
|
124
|
+
props.modelBy,
|
|
125
|
+
{
|
|
134
126
|
page: options?.page,
|
|
135
127
|
perPage: options?.itemsPerPage,
|
|
136
128
|
sortBy: options?.sortBy
|
|
137
|
-
}
|
|
129
|
+
}
|
|
138
130
|
).then((result) => {
|
|
139
131
|
items.value = result.data;
|
|
140
132
|
itemsLength.value = result.meta.totalItems;
|
|
@@ -151,10 +143,9 @@ export function useApiModel(props) {
|
|
|
151
143
|
loadItems(currentOptions.value);
|
|
152
144
|
} else {
|
|
153
145
|
isLoading.value = true;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
{ variables: props.modelBy, fields: fields.value }
|
|
146
|
+
ops.value.read(
|
|
147
|
+
fields.value,
|
|
148
|
+
props.modelBy
|
|
158
149
|
).then((result) => {
|
|
159
150
|
items.value = arrayWrap(result);
|
|
160
151
|
}).catch((error) => {
|