@atscript/vue-wf 0.1.58
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/LICENSE +21 -0
- package/README.md +23 -0
- package/dist/as-wf-form-CBBvLmgY.mjs +350 -0
- package/dist/as-wf-form.d.mts +2 -0
- package/dist/as-wf-form.mjs +2 -0
- package/dist/as-wf-form.vue-BP739hjJ.d.mts +190 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +2 -0
- package/package.json +75 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Artem Maltsev <artem@maltsev.nl>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# @atscript/vue-wf
|
|
2
|
+
|
|
3
|
+
Vue 3 client for the atscript-ui workflow form — HTTP round-trip loop driven by metadata exchanged with a [`@atscript/moost-wf`](../moost-wf) server.
|
|
4
|
+
|
|
5
|
+
Part of the [atscript-ui](https://github.com/moostjs/atscript-ui) monorepo.
|
|
6
|
+
|
|
7
|
+
## What it provides
|
|
8
|
+
|
|
9
|
+
- `<AsWfForm>` — single component that drives the full workflow: posts current state, renders the next step's form, validates with the schema returned by the server, and resumes after pauses
|
|
10
|
+
- Hooks for custom transport, error display, and per-step UI overrides
|
|
11
|
+
- Built on [`@atscript/vue-form`](../vue-form), so all form-rendering primitives (field types, default renderers, layout grid) carry over
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
pnpm add @atscript/vue-wf
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Peer requirements: `vue@^3`, `@atscript/vue-form`, `@atscript/typescript`.
|
|
20
|
+
|
|
21
|
+
## License
|
|
22
|
+
|
|
23
|
+
MIT © Artem Maltsev
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, defineComponent, guardReactiveProps, normalizeProps, onMounted, onUnmounted, openBlock, reactive, ref, renderSlot, shallowRef, toDisplayString, toRaw, unref, watch, withCtx } from "vue";
|
|
2
|
+
import { WF_ACTION_WITH_DATA, createFormData, createFormDef, getFieldMeta } from "@atscript/ui";
|
|
3
|
+
import { deserializeAnnotatedType } from "@atscript/typescript/utils";
|
|
4
|
+
import { AsForm } from "@atscript/vue-form";
|
|
5
|
+
//#region src/use-wf-form.ts
|
|
6
|
+
function useWfForm(options) {
|
|
7
|
+
const tokenName = options.tokenName ?? "wfs";
|
|
8
|
+
const wfidName = options.wfidName ?? "wfid";
|
|
9
|
+
const transport = options.tokenTransport ?? "body";
|
|
10
|
+
const formDef = shallowRef(null);
|
|
11
|
+
const formData = shallowRef(null);
|
|
12
|
+
const formContext = shallowRef({});
|
|
13
|
+
const errors = shallowRef({});
|
|
14
|
+
const response = shallowRef(null);
|
|
15
|
+
const error = shallowRef(null);
|
|
16
|
+
const loading = ref(false);
|
|
17
|
+
const finished = ref(false);
|
|
18
|
+
/** Increments each time the form schema changes — used as :key on AsForm to force remount. */
|
|
19
|
+
const formKey = ref(0);
|
|
20
|
+
let token;
|
|
21
|
+
let lastRequestBody;
|
|
22
|
+
let lastPayloadJson;
|
|
23
|
+
let abortController;
|
|
24
|
+
const { headers: extraHeaders, ...restFetchOpts } = options.fetchOptions ?? {};
|
|
25
|
+
const baseFetchOpts = {
|
|
26
|
+
method: "POST",
|
|
27
|
+
headers: {
|
|
28
|
+
"Content-Type": "application/json",
|
|
29
|
+
...extraHeaders instanceof Headers ? Object.fromEntries(extraHeaders) : Array.isArray(extraHeaders) ? Object.fromEntries(extraHeaders) : extraHeaders
|
|
30
|
+
},
|
|
31
|
+
...transport === "cookie" ? { credentials: "include" } : {},
|
|
32
|
+
...restFetchOpts
|
|
33
|
+
};
|
|
34
|
+
function readInitialToken() {
|
|
35
|
+
if (options.initialToken) return options.initialToken;
|
|
36
|
+
if (transport === "query") return new URLSearchParams(window.location.search).get(tokenName) ?? void 0;
|
|
37
|
+
}
|
|
38
|
+
function buildBody(payload) {
|
|
39
|
+
const body = { ...payload };
|
|
40
|
+
if (transport !== "cookie" && token) body[tokenName] = token;
|
|
41
|
+
return body;
|
|
42
|
+
}
|
|
43
|
+
function extractToken(data) {
|
|
44
|
+
if (transport !== "cookie" && tokenName in data) token = data[tokenName];
|
|
45
|
+
}
|
|
46
|
+
function processResponse(data) {
|
|
47
|
+
extractToken(data);
|
|
48
|
+
if (data.finished) {
|
|
49
|
+
finished.value = true;
|
|
50
|
+
response.value = data;
|
|
51
|
+
formDef.value = null;
|
|
52
|
+
formData.value = null;
|
|
53
|
+
lastPayloadJson = void 0;
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (data.sent === true || typeof data.outlet === "string") {
|
|
57
|
+
finished.value = true;
|
|
58
|
+
response.value = data;
|
|
59
|
+
formDef.value = null;
|
|
60
|
+
formData.value = null;
|
|
61
|
+
lastPayloadJson = void 0;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (data.error) {
|
|
65
|
+
error.value = data.error;
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const ir = data.inputRequired;
|
|
69
|
+
if (!ir) {
|
|
70
|
+
error.value = { message: "Unexpected response format" };
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const { errors: serverErrors, ...remainingCtx } = ir.context ?? {};
|
|
74
|
+
const payloadJson = JSON.stringify(ir.payload);
|
|
75
|
+
if (payloadJson !== lastPayloadJson) {
|
|
76
|
+
lastPayloadJson = payloadJson;
|
|
77
|
+
formKey.value++;
|
|
78
|
+
const type = deserializeAnnotatedType(ir.payload);
|
|
79
|
+
formDef.value = createFormDef(type);
|
|
80
|
+
formData.value = reactive(createFormData(type));
|
|
81
|
+
}
|
|
82
|
+
formContext.value = remainingCtx;
|
|
83
|
+
errors.value = serverErrors ?? {};
|
|
84
|
+
finished.value = false;
|
|
85
|
+
error.value = null;
|
|
86
|
+
}
|
|
87
|
+
async function post(body) {
|
|
88
|
+
abortController?.abort();
|
|
89
|
+
abortController = new AbortController();
|
|
90
|
+
const { signal } = abortController;
|
|
91
|
+
loading.value = true;
|
|
92
|
+
error.value = null;
|
|
93
|
+
lastRequestBody = body;
|
|
94
|
+
try {
|
|
95
|
+
const res = await (options.fetch ?? fetch)(options.path, {
|
|
96
|
+
...baseFetchOpts,
|
|
97
|
+
signal,
|
|
98
|
+
body: JSON.stringify(buildBody(body))
|
|
99
|
+
});
|
|
100
|
+
if (!res.ok) {
|
|
101
|
+
error.value = {
|
|
102
|
+
message: (await res.json().catch(() => ({ message: res.statusText }))).message ?? res.statusText,
|
|
103
|
+
status: res.status
|
|
104
|
+
};
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
processResponse(await res.json());
|
|
108
|
+
} catch (err) {
|
|
109
|
+
if (err instanceof DOMException && err.name === "AbortError") return;
|
|
110
|
+
error.value = { message: err instanceof Error ? err.message : "Network error" };
|
|
111
|
+
} finally {
|
|
112
|
+
loading.value = false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function start(input) {
|
|
116
|
+
const initialToken = readInitialToken();
|
|
117
|
+
if (initialToken) token = initialToken;
|
|
118
|
+
const body = { [wfidName]: options.name };
|
|
119
|
+
if (input) body.input = input;
|
|
120
|
+
if (initialToken) body[tokenName] = initialToken;
|
|
121
|
+
await post(body);
|
|
122
|
+
}
|
|
123
|
+
async function submit(data) {
|
|
124
|
+
await post({ input: data });
|
|
125
|
+
}
|
|
126
|
+
async function action(name) {
|
|
127
|
+
await post({ action: name });
|
|
128
|
+
}
|
|
129
|
+
async function actionWithData(name, data) {
|
|
130
|
+
await post({
|
|
131
|
+
input: data,
|
|
132
|
+
action: name
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
async function retry() {
|
|
136
|
+
if (lastRequestBody) await post(lastRequestBody);
|
|
137
|
+
}
|
|
138
|
+
if (options.autoStart !== false) onMounted(() => start(options.input));
|
|
139
|
+
onUnmounted(() => abortController?.abort());
|
|
140
|
+
return {
|
|
141
|
+
formDef,
|
|
142
|
+
formData,
|
|
143
|
+
formContext,
|
|
144
|
+
errors,
|
|
145
|
+
formKey,
|
|
146
|
+
loading,
|
|
147
|
+
finished,
|
|
148
|
+
response,
|
|
149
|
+
error,
|
|
150
|
+
start,
|
|
151
|
+
submit,
|
|
152
|
+
action,
|
|
153
|
+
actionWithData,
|
|
154
|
+
retry
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/components/as-wf-form.vue?vue&type=script&setup=true&lang.ts
|
|
159
|
+
const _hoisted_1 = { key: 0 };
|
|
160
|
+
const _hoisted_2 = { key: 1 };
|
|
161
|
+
const _hoisted_3 = { key: 2 };
|
|
162
|
+
const _hoisted_4 = { key: 3 };
|
|
163
|
+
const _hoisted_5 = {
|
|
164
|
+
role: "alert",
|
|
165
|
+
class: "as-wf-form-error"
|
|
166
|
+
};
|
|
167
|
+
const _hoisted_6 = ["disabled"];
|
|
168
|
+
//#endregion
|
|
169
|
+
//#region src/components/as-wf-form.vue
|
|
170
|
+
var as_wf_form_default = /* @__PURE__ */ defineComponent({
|
|
171
|
+
__name: "as-wf-form",
|
|
172
|
+
props: {
|
|
173
|
+
types: {
|
|
174
|
+
type: Object,
|
|
175
|
+
required: true
|
|
176
|
+
},
|
|
177
|
+
firstValidation: {
|
|
178
|
+
type: String,
|
|
179
|
+
required: false
|
|
180
|
+
},
|
|
181
|
+
components: {
|
|
182
|
+
type: Object,
|
|
183
|
+
required: false
|
|
184
|
+
},
|
|
185
|
+
clientFactory: {
|
|
186
|
+
type: Function,
|
|
187
|
+
required: false
|
|
188
|
+
},
|
|
189
|
+
path: {
|
|
190
|
+
type: String,
|
|
191
|
+
required: true
|
|
192
|
+
},
|
|
193
|
+
name: {
|
|
194
|
+
type: String,
|
|
195
|
+
required: true
|
|
196
|
+
},
|
|
197
|
+
input: {
|
|
198
|
+
type: Object,
|
|
199
|
+
required: false
|
|
200
|
+
},
|
|
201
|
+
tokenTransport: {
|
|
202
|
+
type: String,
|
|
203
|
+
required: false,
|
|
204
|
+
default: "body"
|
|
205
|
+
},
|
|
206
|
+
tokenName: {
|
|
207
|
+
type: String,
|
|
208
|
+
required: false,
|
|
209
|
+
default: "wfs"
|
|
210
|
+
},
|
|
211
|
+
wfidName: {
|
|
212
|
+
type: String,
|
|
213
|
+
required: false,
|
|
214
|
+
default: "wfid"
|
|
215
|
+
},
|
|
216
|
+
fetchOptions: {
|
|
217
|
+
type: null,
|
|
218
|
+
required: false
|
|
219
|
+
},
|
|
220
|
+
fetch: {
|
|
221
|
+
type: null,
|
|
222
|
+
required: false
|
|
223
|
+
},
|
|
224
|
+
autoStart: {
|
|
225
|
+
type: Boolean,
|
|
226
|
+
required: false,
|
|
227
|
+
default: true
|
|
228
|
+
},
|
|
229
|
+
initialToken: {
|
|
230
|
+
type: String,
|
|
231
|
+
required: false
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
emits: [
|
|
235
|
+
"finished",
|
|
236
|
+
"error",
|
|
237
|
+
"form",
|
|
238
|
+
"submit",
|
|
239
|
+
"loading"
|
|
240
|
+
],
|
|
241
|
+
setup(__props, { emit: __emit }) {
|
|
242
|
+
const props = __props;
|
|
243
|
+
const emit = __emit;
|
|
244
|
+
const wf = useWfForm(props);
|
|
245
|
+
watch(() => wf.loading.value, (v) => emit("loading", v));
|
|
246
|
+
watch(() => wf.finished.value, (v) => {
|
|
247
|
+
if (v) emit("finished", wf.response.value);
|
|
248
|
+
});
|
|
249
|
+
watch(() => wf.error.value, (v) => {
|
|
250
|
+
if (v) emit("error", v);
|
|
251
|
+
});
|
|
252
|
+
watch([() => wf.formDef.value, () => wf.formContext.value], ([def, ctx]) => {
|
|
253
|
+
if (def) emit("form", def, ctx);
|
|
254
|
+
});
|
|
255
|
+
const withDataActions = computed(() => {
|
|
256
|
+
const def = wf.formDef.value;
|
|
257
|
+
if (!def) return /* @__PURE__ */ new Set();
|
|
258
|
+
const set = /* @__PURE__ */ new Set();
|
|
259
|
+
for (const field of def.fields) {
|
|
260
|
+
const wfAction = getFieldMeta(field.prop, WF_ACTION_WITH_DATA);
|
|
261
|
+
if (wfAction) set.add(wfAction);
|
|
262
|
+
}
|
|
263
|
+
return set;
|
|
264
|
+
});
|
|
265
|
+
function onSubmit(data) {
|
|
266
|
+
const raw = toRaw(data);
|
|
267
|
+
emit("submit", raw);
|
|
268
|
+
wf.submit(raw);
|
|
269
|
+
}
|
|
270
|
+
function onAction(name, data) {
|
|
271
|
+
if (withDataActions.value.has(name)) wf.actionWithData(name, toRaw(data));
|
|
272
|
+
else wf.action(name);
|
|
273
|
+
}
|
|
274
|
+
return (_ctx, _cache) => {
|
|
275
|
+
return renderSlot(_ctx.$slots, "default", {
|
|
276
|
+
form: {
|
|
277
|
+
def: unref(wf).formDef.value,
|
|
278
|
+
formData: unref(wf).formData.value,
|
|
279
|
+
formContext: unref(wf).formContext.value
|
|
280
|
+
},
|
|
281
|
+
state: {
|
|
282
|
+
loading: unref(wf).loading.value,
|
|
283
|
+
error: unref(wf).error.value,
|
|
284
|
+
finished: unref(wf).finished.value,
|
|
285
|
+
response: unref(wf).response.value
|
|
286
|
+
},
|
|
287
|
+
actions: {
|
|
288
|
+
start: unref(wf).start,
|
|
289
|
+
submit: onSubmit,
|
|
290
|
+
retry: unref(wf).retry
|
|
291
|
+
}
|
|
292
|
+
}, () => [unref(wf).loading.value && !unref(wf).formDef.value ? (openBlock(), createElementBlock("div", _hoisted_1, [renderSlot(_ctx.$slots, "wf.loading")])) : unref(wf).error.value && !unref(wf).formDef.value ? (openBlock(), createElementBlock("div", _hoisted_2, [renderSlot(_ctx.$slots, "wf.error", {
|
|
293
|
+
error: unref(wf).error.value,
|
|
294
|
+
retry: unref(wf).retry
|
|
295
|
+
}, () => [createElementVNode("div", null, toDisplayString(unref(wf).error.value?.message ?? "Error"), 1)])])) : unref(wf).finished.value ? (openBlock(), createElementBlock("div", _hoisted_3, [renderSlot(_ctx.$slots, "wf.finished", { response: unref(wf).response.value })])) : unref(wf).formDef.value && unref(wf).formData.value ? (openBlock(), createElementBlock("div", _hoisted_4, [unref(wf).error.value ? renderSlot(_ctx.$slots, "form.error", {
|
|
296
|
+
key: 0,
|
|
297
|
+
error: unref(wf).error.value,
|
|
298
|
+
retry: unref(wf).retry
|
|
299
|
+
}, () => [createElementVNode("div", _hoisted_5, toDisplayString(unref(wf).error.value?.message ?? "Error"), 1)]) : createCommentVNode("v-if", true), (openBlock(), createBlock(unref(AsForm), {
|
|
300
|
+
key: unref(wf).formKey.value,
|
|
301
|
+
def: unref(wf).formDef.value,
|
|
302
|
+
"form-data": unref(wf).formData.value,
|
|
303
|
+
types: __props.types,
|
|
304
|
+
errors: unref(wf).errors.value,
|
|
305
|
+
"form-context": unref(wf).formContext.value,
|
|
306
|
+
"first-validation": __props.firstValidation,
|
|
307
|
+
components: __props.components,
|
|
308
|
+
"client-factory": __props.clientFactory,
|
|
309
|
+
loading: unref(wf).loading.value,
|
|
310
|
+
onSubmit,
|
|
311
|
+
onAction,
|
|
312
|
+
onUnsupportedAction: onAction
|
|
313
|
+
}, {
|
|
314
|
+
"form.header": withCtx((slotProps) => [renderSlot(_ctx.$slots, "form.header", normalizeProps(guardReactiveProps({
|
|
315
|
+
...slotProps,
|
|
316
|
+
loading: unref(wf).loading.value
|
|
317
|
+
})))]),
|
|
318
|
+
"form.before": withCtx((slotProps) => [renderSlot(_ctx.$slots, "form.before", normalizeProps(guardReactiveProps({
|
|
319
|
+
...slotProps,
|
|
320
|
+
loading: unref(wf).loading.value
|
|
321
|
+
})))]),
|
|
322
|
+
"form.after": withCtx((slotProps) => [renderSlot(_ctx.$slots, "form.after", normalizeProps(guardReactiveProps({
|
|
323
|
+
...slotProps,
|
|
324
|
+
loading: unref(wf).loading.value
|
|
325
|
+
})))]),
|
|
326
|
+
"form.submit": withCtx((slotProps) => [renderSlot(_ctx.$slots, "form.submit", normalizeProps(guardReactiveProps({
|
|
327
|
+
...slotProps,
|
|
328
|
+
loading: unref(wf).loading.value
|
|
329
|
+
})), () => [createElementVNode("button", { disabled: slotProps.disabled || unref(wf).loading.value }, toDisplayString(slotProps.text), 9, _hoisted_6)])]),
|
|
330
|
+
"form.footer": withCtx((slotProps) => [renderSlot(_ctx.$slots, "form.footer", normalizeProps(guardReactiveProps({
|
|
331
|
+
...slotProps,
|
|
332
|
+
loading: unref(wf).loading.value
|
|
333
|
+
})))]),
|
|
334
|
+
_: 3
|
|
335
|
+
}, 8, [
|
|
336
|
+
"def",
|
|
337
|
+
"form-data",
|
|
338
|
+
"types",
|
|
339
|
+
"errors",
|
|
340
|
+
"form-context",
|
|
341
|
+
"first-validation",
|
|
342
|
+
"components",
|
|
343
|
+
"client-factory",
|
|
344
|
+
"loading"
|
|
345
|
+
]))])) : createCommentVNode("v-if", true)]);
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
//#endregion
|
|
350
|
+
export { useWfForm as n, as_wf_form_default as t };
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import * as vue from "vue";
|
|
2
|
+
import { Component, Ref, ShallowRef } from "vue";
|
|
3
|
+
import { ClientFactory, FormDef } from "@atscript/ui";
|
|
4
|
+
import { TAsTypeComponents, TFormState } from "@atscript/vue-form";
|
|
5
|
+
|
|
6
|
+
//#region src/use-wf-form.d.ts
|
|
7
|
+
interface UseWfFormOptions {
|
|
8
|
+
/** HTTP endpoint for the workflow trigger (e.g., '/api/auth/flow') */
|
|
9
|
+
path: string;
|
|
10
|
+
/** Workflow ID to start (e.g., 'auth/login') */
|
|
11
|
+
name: string;
|
|
12
|
+
/** Initial input to send with the start request */
|
|
13
|
+
input?: Record<string, unknown>;
|
|
14
|
+
/**
|
|
15
|
+
* How to transport the workflow state token.
|
|
16
|
+
* - 'body' (default): token travels in request/response JSON body
|
|
17
|
+
* - 'cookie': token travels via Set-Cookie / Cookie headers
|
|
18
|
+
* - 'query': token is read from URL query params (for email magic links)
|
|
19
|
+
*/
|
|
20
|
+
tokenTransport?: "body" | "cookie" | "query";
|
|
21
|
+
/** Token parameter name (default: 'wfs') */
|
|
22
|
+
tokenName?: string;
|
|
23
|
+
/** Workflow ID parameter name (default: 'wfid') */
|
|
24
|
+
wfidName?: string;
|
|
25
|
+
/** Custom fetch options (headers, credentials, etc.) */
|
|
26
|
+
fetchOptions?: RequestInit;
|
|
27
|
+
/**
|
|
28
|
+
* Override the global `fetch` — lets the host wire its own status-code
|
|
29
|
+
* bus or interceptors (e.g. a wrapper that emits on `on410` so the app's
|
|
30
|
+
* expiry banner lights up). Defaults to `globalThis.fetch`.
|
|
31
|
+
*/
|
|
32
|
+
fetch?: typeof fetch;
|
|
33
|
+
/** Whether to auto-start the workflow on mount (default: true) */
|
|
34
|
+
autoStart?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Pre-existing workflow state token to resume from. Use when the token lives
|
|
37
|
+
* outside `window.location.search` — e.g. encoded in a Vue Router path param
|
|
38
|
+
* (`/invite/:token`) or pulled from app state. Included in the first request
|
|
39
|
+
* so the server resumes the paused workflow instead of starting a new one.
|
|
40
|
+
* Takes precedence over `tokenTransport: "query"` auto-detection.
|
|
41
|
+
*/
|
|
42
|
+
initialToken?: string;
|
|
43
|
+
}
|
|
44
|
+
interface UseWfFormReturn {
|
|
45
|
+
formDef: ShallowRef<FormDef | null>;
|
|
46
|
+
formData: ShallowRef<Record<string, unknown> | null>;
|
|
47
|
+
formContext: ShallowRef<Record<string, unknown>>;
|
|
48
|
+
errors: ShallowRef<Record<string, string>>;
|
|
49
|
+
/** Increments each time the form schema changes — use as :key on AsForm to force remount. */
|
|
50
|
+
formKey: Ref<number>;
|
|
51
|
+
loading: Ref<boolean>;
|
|
52
|
+
finished: Ref<boolean>;
|
|
53
|
+
response: ShallowRef<unknown>;
|
|
54
|
+
error: ShallowRef<unknown>;
|
|
55
|
+
start: (input?: Record<string, unknown>) => Promise<void>;
|
|
56
|
+
submit: (data: unknown) => Promise<void>;
|
|
57
|
+
action: (name: string) => Promise<void>;
|
|
58
|
+
actionWithData: (name: string, data: unknown) => Promise<void>;
|
|
59
|
+
retry: () => Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
declare function useWfForm(options: UseWfFormOptions): UseWfFormReturn;
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/components/as-wf-form.vue.d.ts
|
|
64
|
+
interface AsWfFormProps extends UseWfFormOptions {
|
|
65
|
+
/** Type-to-component map for AsForm rendering */
|
|
66
|
+
types: TAsTypeComponents;
|
|
67
|
+
/** First validation strategy passed to AsForm */
|
|
68
|
+
firstValidation?: TFormState["firstValidation"];
|
|
69
|
+
/** Custom components map passed to AsForm */
|
|
70
|
+
components?: Record<string, Component>;
|
|
71
|
+
/** Per-form client factory override (FK value-help). Forwarded to AsForm. */
|
|
72
|
+
clientFactory?: ClientFactory;
|
|
73
|
+
}
|
|
74
|
+
declare function onSubmit(data: unknown): void;
|
|
75
|
+
declare var __VLS_1: {
|
|
76
|
+
form: {
|
|
77
|
+
def: FormDef | null;
|
|
78
|
+
formData: Record<string, unknown> | null;
|
|
79
|
+
formContext: Record<string, unknown>;
|
|
80
|
+
};
|
|
81
|
+
state: {
|
|
82
|
+
loading: boolean;
|
|
83
|
+
error: unknown;
|
|
84
|
+
finished: boolean;
|
|
85
|
+
response: unknown;
|
|
86
|
+
};
|
|
87
|
+
actions: {
|
|
88
|
+
start: (input?: Record<string, unknown>) => Promise<void>;
|
|
89
|
+
submit: typeof onSubmit;
|
|
90
|
+
retry: () => Promise<void>;
|
|
91
|
+
};
|
|
92
|
+
}, __VLS_3: {}, __VLS_5: {
|
|
93
|
+
error: {};
|
|
94
|
+
retry: () => Promise<void>;
|
|
95
|
+
}, __VLS_7: {
|
|
96
|
+
response: unknown;
|
|
97
|
+
}, __VLS_9: {
|
|
98
|
+
error: {};
|
|
99
|
+
retry: () => Promise<void>;
|
|
100
|
+
}, __VLS_22: {
|
|
101
|
+
loading: boolean;
|
|
102
|
+
clearErrors: () => void;
|
|
103
|
+
reset: () => Promise<void>;
|
|
104
|
+
setErrors: (errors: Record<string, string>) => void;
|
|
105
|
+
formContext: Record<string, unknown> | undefined;
|
|
106
|
+
disabled: boolean;
|
|
107
|
+
}, __VLS_25: {
|
|
108
|
+
loading: boolean;
|
|
109
|
+
clearErrors: () => void;
|
|
110
|
+
reset: () => Promise<void>;
|
|
111
|
+
setErrors: (errors: Record<string, string>) => void;
|
|
112
|
+
formContext: Record<string, unknown> | undefined;
|
|
113
|
+
disabled: boolean;
|
|
114
|
+
}, __VLS_28: {
|
|
115
|
+
loading: boolean;
|
|
116
|
+
clearErrors: () => void;
|
|
117
|
+
reset: () => Promise<void>;
|
|
118
|
+
setErrors: (errors: Record<string, string>) => void;
|
|
119
|
+
disabled: boolean;
|
|
120
|
+
formContext: Record<string, unknown> | undefined;
|
|
121
|
+
}, __VLS_31: {
|
|
122
|
+
loading: boolean;
|
|
123
|
+
disabled: boolean;
|
|
124
|
+
text: string;
|
|
125
|
+
clearErrors: () => void;
|
|
126
|
+
reset: () => Promise<void>;
|
|
127
|
+
setErrors: (errors: Record<string, string>) => void;
|
|
128
|
+
formContext: Record<string, unknown> | undefined;
|
|
129
|
+
}, __VLS_34: {
|
|
130
|
+
loading: boolean;
|
|
131
|
+
disabled: boolean;
|
|
132
|
+
clearErrors: () => void;
|
|
133
|
+
reset: () => Promise<void>;
|
|
134
|
+
setErrors: (errors: Record<string, string>) => void;
|
|
135
|
+
formContext: Record<string, unknown> | undefined;
|
|
136
|
+
};
|
|
137
|
+
type __VLS_Slots = {} & {
|
|
138
|
+
default?: (props: typeof __VLS_1) => any;
|
|
139
|
+
} & {
|
|
140
|
+
'wf.loading'?: (props: typeof __VLS_3) => any;
|
|
141
|
+
} & {
|
|
142
|
+
'wf.error'?: (props: typeof __VLS_5) => any;
|
|
143
|
+
} & {
|
|
144
|
+
'wf.finished'?: (props: typeof __VLS_7) => any;
|
|
145
|
+
} & {
|
|
146
|
+
'form.error'?: (props: typeof __VLS_9) => any;
|
|
147
|
+
} & {
|
|
148
|
+
'form.header'?: (props: typeof __VLS_22) => any;
|
|
149
|
+
} & {
|
|
150
|
+
'form.before'?: (props: typeof __VLS_25) => any;
|
|
151
|
+
} & {
|
|
152
|
+
'form.after'?: (props: typeof __VLS_28) => any;
|
|
153
|
+
} & {
|
|
154
|
+
'form.submit'?: (props: typeof __VLS_31) => any;
|
|
155
|
+
} & {
|
|
156
|
+
'form.footer'?: (props: typeof __VLS_34) => any;
|
|
157
|
+
};
|
|
158
|
+
declare const __VLS_base: vue.DefineComponent<AsWfFormProps, {}, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {} & {
|
|
159
|
+
finished: (response: unknown) => any;
|
|
160
|
+
error: (error: {
|
|
161
|
+
message: string;
|
|
162
|
+
status?: number;
|
|
163
|
+
}) => any;
|
|
164
|
+
submit: (data: unknown) => any;
|
|
165
|
+
loading: (isLoading: boolean) => any;
|
|
166
|
+
form: (def: FormDef, context?: Record<string, unknown> | undefined) => any;
|
|
167
|
+
}, string, vue.PublicProps, Readonly<AsWfFormProps> & Readonly<{
|
|
168
|
+
onFinished?: ((response: unknown) => any) | undefined;
|
|
169
|
+
onError?: ((error: {
|
|
170
|
+
message: string;
|
|
171
|
+
status?: number;
|
|
172
|
+
}) => any) | undefined;
|
|
173
|
+
onSubmit?: ((data: unknown) => any) | undefined;
|
|
174
|
+
onLoading?: ((isLoading: boolean) => any) | undefined;
|
|
175
|
+
onForm?: ((def: FormDef, context?: Record<string, unknown> | undefined) => any) | undefined;
|
|
176
|
+
}>, {
|
|
177
|
+
tokenTransport: "body" | "cookie" | "query";
|
|
178
|
+
tokenName: string;
|
|
179
|
+
wfidName: string;
|
|
180
|
+
autoStart: boolean;
|
|
181
|
+
}, {}, {}, {}, string, vue.ComponentProvideOptions, false, {}, any>;
|
|
182
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
183
|
+
declare const _default: typeof __VLS_export;
|
|
184
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
185
|
+
new (): {
|
|
186
|
+
$slots: S;
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
//#endregion
|
|
190
|
+
export { useWfForm as i, UseWfFormOptions as n, UseWfFormReturn as r, _default as t };
|
package/dist/index.d.mts
ADDED
package/dist/index.mjs
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atscript/vue-wf",
|
|
3
|
+
"version": "0.1.58",
|
|
4
|
+
"description": "Workflow form integration for Vue 3 — HTTP round-trip loop driven by atscript type metadata",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"atscript",
|
|
7
|
+
"form",
|
|
8
|
+
"metadata",
|
|
9
|
+
"moost",
|
|
10
|
+
"type-driven",
|
|
11
|
+
"typescript",
|
|
12
|
+
"vue",
|
|
13
|
+
"vue3",
|
|
14
|
+
"wf",
|
|
15
|
+
"workflow"
|
|
16
|
+
],
|
|
17
|
+
"homepage": "https://github.com/moostjs/atscript-ui/tree/main/packages/vue-wf#readme",
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/moostjs/atscript-ui/issues"
|
|
20
|
+
},
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"author": "Artem Maltsev <artem@maltsev.nl>",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/moostjs/atscript-ui.git",
|
|
26
|
+
"directory": "packages/vue-wf"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist"
|
|
30
|
+
],
|
|
31
|
+
"type": "module",
|
|
32
|
+
"main": "dist/index.mjs",
|
|
33
|
+
"types": "dist/index.d.mts",
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"types": "./dist/index.d.mts",
|
|
37
|
+
"import": "./dist/index.mjs"
|
|
38
|
+
},
|
|
39
|
+
"./as-wf-form": {
|
|
40
|
+
"types": "./dist/as-wf-form.d.mts",
|
|
41
|
+
"import": "./dist/as-wf-form.mjs"
|
|
42
|
+
},
|
|
43
|
+
"./package.json": "./package.json"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@atscript/ui": "^0.1.58"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@atscript/core": "^0.1.54",
|
|
53
|
+
"@atscript/typescript": "^0.1.54",
|
|
54
|
+
"@vitejs/plugin-vue": "^6",
|
|
55
|
+
"@vue/test-utils": "^2",
|
|
56
|
+
"happy-dom": "^18",
|
|
57
|
+
"unplugin-atscript": "^0.1.54",
|
|
58
|
+
"vitest": "npm:@voidzero-dev/vite-plus-test@latest",
|
|
59
|
+
"vue": "^3",
|
|
60
|
+
"vue-tsc": "^3.2.6",
|
|
61
|
+
"@atscript/moost-wf": "^0.1.58",
|
|
62
|
+
"@atscript/vue-form": "^0.1.58"
|
|
63
|
+
},
|
|
64
|
+
"peerDependencies": {
|
|
65
|
+
"@atscript/typescript": "^0.1.54",
|
|
66
|
+
"vue": "^3",
|
|
67
|
+
"@atscript/vue-form": "^0.1.58"
|
|
68
|
+
},
|
|
69
|
+
"scripts": {
|
|
70
|
+
"build": "node ../../scripts/gen-exports.mjs && vp pack",
|
|
71
|
+
"dev": "vp pack --watch",
|
|
72
|
+
"test": "vp test",
|
|
73
|
+
"check": "vp check"
|
|
74
|
+
}
|
|
75
|
+
}
|