nuxt-openapi-hyperfetch 0.3.81-beta → 1.0.0

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.
Files changed (64) hide show
  1. package/README.md +218 -212
  2. package/dist/generators/components/connector-generator/templates.js +67 -17
  3. package/dist/generators/components/schema-analyzer/intent-detector.js +1 -12
  4. package/dist/generators/components/schema-analyzer/openapi-reader.js +10 -1
  5. package/dist/generators/components/schema-analyzer/resource-grouper.js +7 -0
  6. package/dist/generators/components/schema-analyzer/schema-field-mapper.js +1 -22
  7. package/dist/generators/components/schema-analyzer/types.d.ts +10 -0
  8. package/dist/generators/connectors/generator.d.ts +12 -0
  9. package/dist/generators/connectors/generator.js +115 -0
  10. package/dist/generators/connectors/runtime/connector-types.d.ts +147 -0
  11. package/dist/generators/connectors/runtime/connector-types.js +10 -0
  12. package/dist/generators/connectors/runtime/useCreateConnector.d.ts +26 -0
  13. package/dist/generators/connectors/runtime/useCreateConnector.js +156 -0
  14. package/dist/generators/connectors/runtime/useDeleteConnector.d.ts +30 -0
  15. package/dist/generators/connectors/runtime/useDeleteConnector.js +143 -0
  16. package/dist/generators/connectors/runtime/useGetAllConnector.d.ts +25 -0
  17. package/dist/generators/connectors/runtime/useGetAllConnector.js +127 -0
  18. package/dist/generators/connectors/runtime/useGetConnector.d.ts +15 -0
  19. package/dist/generators/connectors/runtime/useGetConnector.js +99 -0
  20. package/dist/generators/connectors/runtime/useUpdateConnector.d.ts +34 -0
  21. package/dist/generators/connectors/runtime/useUpdateConnector.js +211 -0
  22. package/dist/generators/connectors/runtime/zod-error-merger.d.ts +23 -0
  23. package/dist/generators/connectors/runtime/zod-error-merger.js +106 -0
  24. package/dist/generators/connectors/templates.d.ts +4 -0
  25. package/dist/generators/connectors/templates.js +376 -0
  26. package/dist/generators/connectors/types.d.ts +37 -0
  27. package/dist/generators/connectors/types.js +7 -0
  28. package/dist/generators/shared/runtime/useDeleteConnector.js +4 -2
  29. package/dist/generators/shared/runtime/useDetailConnector.d.ts +0 -1
  30. package/dist/generators/shared/runtime/useDetailConnector.js +9 -20
  31. package/dist/generators/shared/runtime/useFormConnector.js +4 -3
  32. package/dist/generators/use-async-data/runtime/useApiAsyncData.js +14 -5
  33. package/dist/generators/use-async-data/templates.js +20 -16
  34. package/dist/generators/use-fetch/templates.js +1 -1
  35. package/dist/index.js +1 -16
  36. package/dist/module/index.js +2 -3
  37. package/package.json +4 -3
  38. package/src/cli/prompts.ts +1 -7
  39. package/src/generators/components/connector-generator/templates.ts +97 -22
  40. package/src/generators/components/schema-analyzer/intent-detector.ts +1 -16
  41. package/src/generators/components/schema-analyzer/openapi-reader.ts +14 -1
  42. package/src/generators/components/schema-analyzer/resource-grouper.ts +9 -0
  43. package/src/generators/components/schema-analyzer/schema-field-mapper.ts +1 -26
  44. package/src/generators/components/schema-analyzer/types.ts +11 -0
  45. package/src/generators/connectors/generator.ts +137 -0
  46. package/src/generators/connectors/runtime/connector-types.ts +207 -0
  47. package/src/generators/connectors/runtime/useCreateConnector.ts +199 -0
  48. package/src/generators/connectors/runtime/useDeleteConnector.ts +179 -0
  49. package/src/generators/connectors/runtime/useGetAllConnector.ts +151 -0
  50. package/src/generators/connectors/runtime/useGetConnector.ts +120 -0
  51. package/src/generators/connectors/runtime/useUpdateConnector.ts +257 -0
  52. package/src/generators/connectors/runtime/zod-error-merger.ts +119 -0
  53. package/src/generators/connectors/templates.ts +481 -0
  54. package/src/generators/connectors/types.ts +39 -0
  55. package/src/generators/shared/runtime/useDeleteConnector.ts +4 -2
  56. package/src/generators/shared/runtime/useDetailConnector.ts +8 -19
  57. package/src/generators/shared/runtime/useFormConnector.ts +4 -3
  58. package/src/generators/use-async-data/runtime/useApiAsyncData.ts +16 -5
  59. package/src/generators/use-async-data/templates.ts +24 -16
  60. package/src/generators/use-fetch/templates.ts +1 -1
  61. package/src/index.ts +2 -19
  62. package/src/module/index.ts +2 -5
  63. package/docs/generated-components.md +0 -615
  64. package/docs/headless-composables-ui.md +0 -569
@@ -0,0 +1,207 @@
1
+ /**
2
+ * connector-types.ts — Structural return type interfaces for the new connector system.
3
+ *
4
+ * Uses locally-defined minimal type aliases for Ref/ComputedRef so this file compiles
5
+ * in the CLI context (where Vue is not installed) and remains structurally compatible
6
+ * with Vue's actual types in the user's Nuxt project.
7
+ *
8
+ * Copied to the user's project alongside the generated connectors and runtime helpers.
9
+ */
10
+
11
+ // Minimal structural aliases — compatible with Vue's Ref<T> and ComputedRef<T>.
12
+ type Ref<T> = { value: T };
13
+ type ComputedRef<T> = { readonly value: T };
14
+
15
+ /** Operation name passed as context to connector-level callbacks. */
16
+ export type ConnectorOperation = 'create' | 'update' | 'delete' | 'get' | 'getAll';
17
+
18
+ export interface ConnectorCallbackContext {
19
+ operation: ConnectorOperation;
20
+ }
21
+
22
+ // ─── Column / field defs (mirrors schema-analyzer output) ────────────────────
23
+
24
+ export interface ColumnDef {
25
+ key: string;
26
+ label: string;
27
+ type: string;
28
+ }
29
+
30
+ export interface FormFieldDef {
31
+ key: string;
32
+ label: string;
33
+ type: string;
34
+ required: boolean;
35
+ options?: { label: string; value: string }[];
36
+ placeholder?: string;
37
+ hidden?: boolean;
38
+ }
39
+
40
+ // ─── Pagination ───────────────────────────────────────────────────────────────
41
+
42
+ export interface PaginationState {
43
+ currentPage: number;
44
+ perPage: number;
45
+ total: number;
46
+ totalPages: number;
47
+ hasNextPage: boolean;
48
+ hasPrevPage: boolean;
49
+ goToPage: (page: number) => void;
50
+ nextPage: () => void;
51
+ prevPage: () => void;
52
+ setPerPage: (n: number) => void;
53
+ }
54
+
55
+ // ─── UI state (shared sub-object in create / update / del) ───────────────────
56
+
57
+ export interface ConnectorUi {
58
+ isOpen: Ref<boolean>;
59
+ open: (...args: any[]) => void;
60
+ close: () => void;
61
+ }
62
+
63
+ // ─── GetAllConnectorReturn ────────────────────────────────────────────────────
64
+
65
+ export interface GetAllConnectorReturn<TRow = unknown> {
66
+ // State
67
+ items: ComputedRef<TRow[]>;
68
+ columns: ComputedRef<ColumnDef[]>;
69
+ loading: ComputedRef<boolean>;
70
+ error: ComputedRef<unknown>;
71
+
72
+ // Pagination
73
+ pagination: ComputedRef<PaginationState | null>;
74
+ goToPage: (page: number) => void;
75
+ nextPage: () => void;
76
+ prevPage: () => void;
77
+ setPerPage: (n: number) => void;
78
+
79
+ // Selection
80
+ selected: Ref<TRow[]>;
81
+ select: (item: TRow) => void;
82
+ deselect: (item: TRow) => void;
83
+ toggleSelect: (item: TRow) => void;
84
+ clearSelection: () => void;
85
+
86
+ // Actions
87
+ load: (params?: unknown) => Promise<void>;
88
+
89
+ // Callbacks (developer-assignable)
90
+ onSuccess: Ref<((items: TRow[]) => void) | null>;
91
+ onError: Ref<((err: unknown) => void) | null>;
92
+ }
93
+
94
+ // ─── GetConnectorReturn ───────────────────────────────────────────────────────
95
+
96
+ export interface GetConnectorReturn<TItem = unknown> {
97
+ // State
98
+ data: Ref<TItem | null>;
99
+ loading: Ref<boolean>;
100
+ error: Ref<unknown>;
101
+ fields: ComputedRef<FormFieldDef[]>;
102
+
103
+ // Actions
104
+ load: (id: string | number) => Promise<TItem>;
105
+ clear: () => void;
106
+
107
+ // Callbacks (developer-assignable via get.onSuccess(fn))
108
+ // The per-operation callback complements the connector-level onSuccess option.
109
+ onSuccess: (fn: (item: TItem) => void) => void;
110
+ onError: (fn: (err: unknown) => void) => void;
111
+ }
112
+
113
+ // ─── CreateConnectorReturn ────────────────────────────────────────────────────
114
+
115
+ export interface CreateConnectorReturn<TInput = Record<string, unknown>, TOutput = TInput> {
116
+ // Form state
117
+ model: Ref<Partial<TInput>>;
118
+ errors: Ref<Record<string, string[]>>;
119
+ loading: Ref<boolean>;
120
+ error: Ref<unknown>;
121
+ submitted: Ref<boolean>;
122
+ isValid: ComputedRef<boolean>;
123
+ hasErrors: ComputedRef<boolean>;
124
+ fields: ComputedRef<FormFieldDef[]>;
125
+
126
+ // Actions
127
+ execute: (data?: Partial<TInput>) => Promise<TOutput | undefined>;
128
+ refresh: (data?: Partial<TInput>) => Promise<TOutput | undefined>;
129
+ reset: () => void;
130
+ setValues: (data: Partial<TInput>) => void;
131
+ setField: (key: keyof TInput, value: unknown) => void;
132
+
133
+ // Callbacks (developer-assignable via create.onSuccess(fn))
134
+ // The per-operation callback complements the connector-level onSuccess option.
135
+ onSuccess: (fn: (data: TOutput) => void) => void;
136
+ onError: (fn: (err: unknown) => void) => void;
137
+
138
+ // UI coordination
139
+ ui: {
140
+ isOpen: Ref<boolean>;
141
+ open: () => void;
142
+ close: () => void;
143
+ };
144
+ }
145
+
146
+ // ─── UpdateConnectorReturn ────────────────────────────────────────────────────
147
+
148
+ export interface UpdateConnectorReturn<TInput = Record<string, unknown>, TOutput = TInput> {
149
+ // Form state
150
+ model: Ref<Partial<TInput>>;
151
+ errors: Ref<Record<string, string[]>>;
152
+ loading: Ref<boolean>;
153
+ error: Ref<unknown>;
154
+ submitted: Ref<boolean>;
155
+ isValid: ComputedRef<boolean>;
156
+ hasErrors: ComputedRef<boolean>;
157
+ fields: ComputedRef<FormFieldDef[]>;
158
+ targetId: Ref<string | number | null>;
159
+
160
+ // Actions
161
+ load: (id: string | number) => Promise<void>;
162
+ execute: (id: string | number, data?: Partial<TInput>) => Promise<TOutput | undefined>;
163
+ refresh: (id: string | number, data?: Partial<TInput>) => Promise<TOutput | undefined>;
164
+ reset: () => void;
165
+ setValues: (data: Partial<TInput>) => void;
166
+ setField: (key: keyof TInput, value: unknown) => void;
167
+
168
+ // Callbacks (developer-assignable via update.onSuccess(fn))
169
+ // The per-operation callback complements the connector-level onSuccess option.
170
+ onSuccess: (fn: (data: TOutput) => void) => void;
171
+ onError: (fn: (err: unknown) => void) => void;
172
+
173
+ // UI coordination
174
+ ui: {
175
+ isOpen: Ref<boolean>;
176
+ open: (item?: Partial<TInput> & Record<string, unknown>) => void;
177
+ close: () => void;
178
+ };
179
+ }
180
+
181
+ // ─── DeleteConnectorReturn ────────────────────────────────────────────────────
182
+
183
+ export interface DeleteConnectorReturn<TItem = unknown> {
184
+ // State
185
+ staged: Ref<TItem | null>;
186
+ hasStaged: ComputedRef<boolean>;
187
+ loading: Ref<boolean>;
188
+ error: Ref<unknown>;
189
+
190
+ // Actions
191
+ stage: (item: TItem) => void;
192
+ cancel: () => void;
193
+ execute: (item?: TItem) => Promise<void>;
194
+ refresh: (item?: TItem) => Promise<void>;
195
+
196
+ // Callbacks (developer-assignable via remove.onSuccess(fn))
197
+ // The per-operation callback complements the connector-level onSuccess option.
198
+ onSuccess: (fn: (item: TItem) => void) => void;
199
+ onError: (fn: (err: unknown) => void) => void;
200
+
201
+ // UI coordination
202
+ ui: {
203
+ isOpen: Ref<boolean>;
204
+ open: (item: TItem) => void;
205
+ close: () => void;
206
+ };
207
+ }
@@ -0,0 +1,199 @@
1
+ // @ts-nocheck - This file runs in user's Nuxt project with different TypeScript config
2
+ /**
3
+ * useCreateConnector — Runtime connector for POST endpoints.
4
+ *
5
+ * Uses $fetch directly (no useAsyncData) so:
6
+ * - execute() always fires a real network request, no SSR cache interference
7
+ * - Can be called multiple times (create multiple items)
8
+ * - Validates with Zod before sending
9
+ *
10
+ * Copied to the user's project alongside the generated connectors.
11
+ */
12
+ import { ref, computed } from 'vue';
13
+ import { mergeZodErrors } from './zod-error-merger.js';
14
+ import { getGlobalBaseUrl, mergeCallbacks } from '../composables/shared/runtime/apiHelpers.js';
15
+
16
+ /**
17
+ * @param url The endpoint URL string. e.g. '/pet'
18
+ * @param options Configuration: schema, fields, method, baseURL, callbacks, etc.
19
+ */
20
+ export function useCreateConnector(url, options = {}) {
21
+ const {
22
+ schema: baseSchema,
23
+ schemaOverride,
24
+ fields = [],
25
+ method = 'POST',
26
+ baseURL: baseURLOpt,
27
+ errorConfig = {},
28
+ onRequest: onRequestOpt,
29
+ onSuccess: onSuccessOpt,
30
+ onError: onErrorOpt,
31
+ onFinish: onFinishOpt,
32
+ autoClose = true,
33
+ autoReset = false,
34
+ skipGlobalCallbacks,
35
+ } = options;
36
+
37
+ const baseURL = baseURLOpt || getGlobalBaseUrl();
38
+ if (!baseURL) {
39
+ console.warn('[useCreateConnector] No baseURL configured. Set runtimeConfig.public.apiBaseUrl in nuxt.config.ts or pass baseURL in options.');
40
+ }
41
+
42
+ // Resolve active schema: schemaOverride(base) / schemaOverride / base / none
43
+ const schema = schemaOverride
44
+ ? typeof schemaOverride === 'function'
45
+ ? schemaOverride(baseSchema)
46
+ : schemaOverride
47
+ : baseSchema;
48
+
49
+ if (schemaOverride && !schema) {
50
+ console.warn('[useCreateConnector] schemaOverride resolved to undefined — validation will be skipped. Check your schemaOverride function returns a valid Zod schema.');
51
+ }
52
+
53
+ // ── Form state ─────────────────────────────────────────────────────────────
54
+
55
+ const model = ref({});
56
+ const errors = ref({});
57
+ const loading = ref(false);
58
+ const error = ref(null);
59
+ const submitted = ref(false);
60
+
61
+ // Callbacks — developer-assignable (can also be passed as options)
62
+ // Both the connector-level option and the per-operation registration are called.
63
+ let _localOnSuccess = null;
64
+ let _localOnError = null;
65
+
66
+ // ── UI state ───────────────────────────────────────────────────────────────
67
+
68
+ const isOpen = ref(false);
69
+
70
+ const ui = {
71
+ isOpen,
72
+ open() {
73
+ isOpen.value = true;
74
+ },
75
+ close() {
76
+ isOpen.value = false;
77
+ },
78
+ };
79
+
80
+ // ── Derived ────────────────────────────────────────────────────────────────
81
+
82
+ const isValid = computed(() => {
83
+ if (!schema) return true;
84
+ return schema.safeParse(model.value).success;
85
+ });
86
+
87
+ const hasErrors = computed(() => Object.keys(errors.value).length > 0);
88
+
89
+ // ── Actions ────────────────────────────────────────────────────────────────
90
+
91
+ function setValues(data) {
92
+ model.value = { ...model.value, ...data };
93
+ }
94
+
95
+ function setField(key, value) {
96
+ model.value = { ...model.value, [key]: value };
97
+ }
98
+
99
+ function reset() {
100
+ model.value = {};
101
+ errors.value = {};
102
+ error.value = null;
103
+ submitted.value = false;
104
+ }
105
+
106
+ /**
107
+ * Validate with Zod (if schema provided) then POST via $fetch.
108
+ * @param data Optional payload override. Falls back to model.value.
109
+ * @returns The response data, or undefined if validation failed.
110
+ */
111
+ async function execute(data) {
112
+ submitted.value = true;
113
+ const payload = data ?? model.value;
114
+
115
+ // 1. Zod validation
116
+ if (schema) {
117
+ const result = schema.safeParse(payload);
118
+ if (!result.success) {
119
+ errors.value = mergeZodErrors(result.error.flatten().fieldErrors, errorConfig);
120
+ console.error('[useCreateConnector] Validation failed — request was not sent.', errors.value);
121
+ return undefined;
122
+ }
123
+ errors.value = {};
124
+ }
125
+
126
+ // 2. $fetch POST
127
+ loading.value = true;
128
+ error.value = null;
129
+
130
+ // Merge global + local callbacks (onRequest modifications, rule-based suppression)
131
+ const merged = mergeCallbacks(url, method, {
132
+ onRequest: onRequestOpt,
133
+ onSuccess: onSuccessOpt,
134
+ onError: onErrorOpt,
135
+ onFinish: onFinishOpt,
136
+ }, skipGlobalCallbacks);
137
+
138
+ // onRequest hook — collects header/body/query modifications from global rules and local option
139
+ const requestMods = await merged.onRequest({ url, method, body: payload });
140
+
141
+ try {
142
+ const result = await $fetch(url, {
143
+ method,
144
+ body: requestMods?.body ?? payload,
145
+ ...(requestMods?.headers ? { headers: requestMods.headers } : {}),
146
+ ...(requestMods?.query ? { query: requestMods.query } : {}),
147
+ ...(baseURL ? { baseURL } : {}),
148
+ });
149
+
150
+ await merged.onSuccess(result, { operation: 'create' });
151
+ _localOnSuccess?.(result);
152
+
153
+ if (autoClose) ui.close();
154
+ if (autoReset) reset();
155
+
156
+ await merged.onFinish({ url, method, data: result, success: true });
157
+
158
+ return result;
159
+ } catch (err) {
160
+ error.value = err;
161
+ await merged.onError(err, { operation: 'create' });
162
+ _localOnError?.(err);
163
+
164
+ await merged.onFinish({ url, method, error: err, success: false });
165
+
166
+ throw err;
167
+ } finally {
168
+ loading.value = false;
169
+ }
170
+ }
171
+
172
+ // ── Return ─────────────────────────────────────────────────────────────────
173
+
174
+ return {
175
+ // Form state
176
+ model,
177
+ errors,
178
+ loading,
179
+ error,
180
+ submitted,
181
+ isValid,
182
+ hasErrors,
183
+ fields: computed(() => fields),
184
+
185
+ // Actions
186
+ execute,
187
+ refresh: execute,
188
+ reset,
189
+ setValues,
190
+ setField,
191
+
192
+ // Callbacks
193
+ onSuccess: (fn) => { _localOnSuccess = fn; },
194
+ onError: (fn) => { _localOnError = fn; },
195
+
196
+ // UI
197
+ ui,
198
+ };
199
+ }
@@ -0,0 +1,179 @@
1
+ // @ts-nocheck - This file runs in user's Nuxt project with different TypeScript config
2
+ /**
3
+ * useDeleteConnector — Runtime connector for DELETE endpoints.
4
+ *
5
+ * Uses $fetch directly (no useAsyncData) so:
6
+ * - execute() always fires a real network request, no SSR cache interference
7
+ * - Supports staging pattern (stage → confirm via ui) or direct execution
8
+ *
9
+ * Copied to the user's project alongside the generated connectors.
10
+ */
11
+ import { ref, computed } from 'vue';
12
+ import { getGlobalBaseUrl, mergeCallbacks } from '../composables/shared/runtime/apiHelpers.js';
13
+
14
+ /**
15
+ * @param idFn Function that extracts the resource ID from a staged item.
16
+ * e.g. (item) => item.petId ?? item.id
17
+ * @param urlFn URL string or function that receives an id and returns the URL string.
18
+ * e.g. '/pet' (when ID is sent via body) or (id) => `/pet/${id}`
19
+ * @param options Optional configuration
20
+ */
21
+ export function useDeleteConnector(idFn, urlFn, options = {}) {
22
+ const resolveUrl = (id) => (typeof urlFn === 'function' ? urlFn(id) : urlFn);
23
+ const {
24
+ baseURL: baseURLOpt,
25
+ onRequest: onRequestOpt,
26
+ onSuccess: onSuccessOpt,
27
+ onError: onErrorOpt,
28
+ onFinish: onFinishOpt,
29
+ autoClose = true,
30
+ skipGlobalCallbacks,
31
+ } = options;
32
+
33
+ const baseURL = baseURLOpt || getGlobalBaseUrl();
34
+ if (!baseURL) {
35
+ console.warn('[useDeleteConnector] No baseURL configured. Set runtimeConfig.public.apiBaseUrl in nuxt.config.ts or pass baseURL in options.');
36
+ }
37
+
38
+ // ── State ──────────────────────────────────────────────────────────────────
39
+
40
+ const staged = ref(null);
41
+ const loading = ref(false);
42
+ const error = ref(null);
43
+
44
+ // Callbacks — developer-assignable (can also be passed as options)
45
+ // Both the connector-level option and the per-operation registration are called.
46
+ let _localOnSuccess = null;
47
+ let _localOnError = null;
48
+
49
+ // ── UI state ───────────────────────────────────────────────────────────────
50
+
51
+ const isOpen = ref(false);
52
+
53
+ const ui = {
54
+ isOpen,
55
+ /**
56
+ * Stage the item and open the confirmation UI (modal/drawer/etc).
57
+ */
58
+ open(item) {
59
+ stage(item);
60
+ isOpen.value = true;
61
+ },
62
+ /**
63
+ * Cancel and close the confirmation UI.
64
+ */
65
+ close() {
66
+ cancel();
67
+ isOpen.value = false;
68
+ },
69
+ };
70
+
71
+ // ── Derived ────────────────────────────────────────────────────────────────
72
+
73
+ const hasStaged = computed(() => staged.value !== null);
74
+
75
+ // ── Actions ────────────────────────────────────────────────────────────────
76
+
77
+ /**
78
+ * Stage an item for deletion without opening any UI.
79
+ * Useful when you want to control the UI yourself.
80
+ */
81
+ function stage(item) {
82
+ staged.value = item;
83
+ }
84
+
85
+ /**
86
+ * Clear the staged item and reset error state.
87
+ */
88
+ function cancel() {
89
+ staged.value = null;
90
+ error.value = null;
91
+ }
92
+
93
+ /**
94
+ * Execute the DELETE request.
95
+ * @param item Optional — if provided, uses this item instead of staged.
96
+ * Allows direct deletion without staging: await del.execute(row)
97
+ */
98
+ async function execute(item) {
99
+ const target = item ?? staged.value;
100
+
101
+ if (!target) {
102
+ console.warn('[useDeleteConnector] execute() called with no item and nothing staged — request was not sent. Call stage(item) or pass the item directly to execute(item).');
103
+ return;
104
+ }
105
+
106
+ const id = idFn(target);
107
+ if (id === undefined || id === null) {
108
+ console.warn('[useDeleteConnector] idFn returned undefined/null — could not resolve resource ID. Request was not sent.', { target });
109
+ return;
110
+ }
111
+ const url = resolveUrl(id);
112
+
113
+ loading.value = true;
114
+ error.value = null;
115
+
116
+ // Merge global + local callbacks (onRequest modifications, rule-based suppression)
117
+ const merged = mergeCallbacks(url, 'DELETE', {
118
+ onRequest: onRequestOpt,
119
+ onSuccess: onSuccessOpt,
120
+ onError: onErrorOpt,
121
+ onFinish: onFinishOpt,
122
+ }, skipGlobalCallbacks);
123
+
124
+ // onRequest hook — collects header/body/query modifications
125
+ const requestMods = await merged.onRequest({ url, method: 'DELETE' });
126
+
127
+ try {
128
+ await $fetch(url, {
129
+ method: 'DELETE',
130
+ ...(requestMods?.headers ? { headers: requestMods.headers } : {}),
131
+ ...(requestMods?.query ? { query: requestMods.query } : {}),
132
+ ...(baseURL ? { baseURL } : {}),
133
+ });
134
+
135
+ const deletedItem = target;
136
+
137
+ await merged.onSuccess(deletedItem, { operation: 'delete' });
138
+ _localOnSuccess?.(deletedItem);
139
+ cancel();
140
+
141
+ if (autoClose) ui.close();
142
+
143
+ await merged.onFinish({ url, method: 'DELETE', success: true });
144
+ } catch (err) {
145
+ error.value = err;
146
+ await merged.onError(err, { operation: 'delete' });
147
+ _localOnError?.(err);
148
+
149
+ await merged.onFinish({ url, method: 'DELETE', error: err, success: false });
150
+
151
+ throw err;
152
+ } finally {
153
+ loading.value = false;
154
+ }
155
+ }
156
+
157
+ // ── Return ─────────────────────────────────────────────────────────────────
158
+
159
+ return {
160
+ // State
161
+ staged,
162
+ hasStaged,
163
+ loading,
164
+ error,
165
+
166
+ // Actions
167
+ stage,
168
+ cancel,
169
+ execute,
170
+ refresh: execute,
171
+
172
+ // Callbacks
173
+ onSuccess: (fn) => { _localOnSuccess = fn; },
174
+ onError: (fn) => { _localOnError = fn; },
175
+
176
+ // UI
177
+ ui,
178
+ };
179
+ }