@effect-app/vue-components 1.0.0 → 1.1.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.
@@ -1,5 +1,5 @@
1
- import { type DeepKeys, DeepValue } from "@tanstack/vue-form";
2
- import { Order, S } from "effect-app";
1
+ import { type DeepKeys, DeepValue, StandardSchemaV1Issue, ValidationError, ValidationErrorMap } from "@tanstack/vue-form";
2
+ import { Effect, Order, S } from "effect-app";
3
3
  import { type InjectionKey } from "vue";
4
4
  import { type InputProps } from "./InputProps";
5
5
  import { DefaultInputProps, type FilterItems, type FormProps, type MetaRecord, type NestedKeyOf, OmegaAutoGenMeta, OmegaError, type OmegaFormApi, OmegaFormState, OmegaInputProps, ShowErrorsOn } from "./OmegaFormStuff";
@@ -10,6 +10,20 @@ type keysRule<T> = {
10
10
  keys?: "You should only use one of banKeys or keys, not both, moron";
11
11
  banKeys?: NestedKeyOf<T>[];
12
12
  };
13
+ declare const FormErrors_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
14
+ readonly _tag: "FormErrors";
15
+ } & Readonly<A>;
16
+ export declare class FormErrors<From> extends FormErrors_base<{
17
+ form: {
18
+ errors: (Record<string, StandardSchemaV1Issue[]> | undefined)[];
19
+ errorMap: ValidationErrorMap<undefined, undefined, Record<string, StandardSchemaV1Issue[]>, undefined, undefined, undefined, undefined, undefined, undefined, undefined>;
20
+ };
21
+ fields: Record<DeepKeys<From>, {
22
+ errors: ValidationError[];
23
+ errorMap: ValidationErrorMap;
24
+ }>;
25
+ }> {
26
+ }
13
27
  export type OmegaConfig<T> = {
14
28
  i18nNamespace?: string;
15
29
  showErrorsOn?: ShowErrorsOn;
@@ -29,6 +43,20 @@ export interface OF<From, To> extends OmegaFormApi<From, To> {
29
43
  filterItems?: FilterItems;
30
44
  clear: () => void;
31
45
  i18nNamespace?: string;
46
+ /** @experimental */
47
+ handleSubmitEffect: {
48
+ /**
49
+ * when `checkErrors` is true, the Effect will fail with `FormErrors<From>` when there are validation errors
50
+ * @experimental */
51
+ (options: {
52
+ checkErrors: true;
53
+ meta?: Record<string, any>;
54
+ }): Effect.Effect<void, FormErrors<From>>;
55
+ /** @experimental */
56
+ (options?: {
57
+ meta?: Record<string, any>;
58
+ }): Effect.Effect<void>;
59
+ };
32
60
  }
33
61
  export declare const OmegaFormKey: InjectionKey<OF<any, any>>;
34
62
  type __VLS_PrettifyLocal<T> = {
@@ -1,63 +1,65 @@
1
- import { useForm as D, useStore as H } from "@tanstack/vue-form";
2
- import { S as O, Effect as d, Fiber as y } from "effect-app";
3
- import { runtimeFiberAsPromise as J } from "./vue-components.es10.js";
4
- import { isObject as q } from "./vue-components.es11.js";
5
- import { computed as I, onUnmounted as K, onMounted as B, onBeforeUnmount as G, watch as W, h as $ } from "vue";
6
- import k from "./vue-components.es12.js";
7
- import z from "./vue-components.es13.js";
8
- import { buildOmegaErrors as Q } from "./vue-components.es6.js";
9
- import T from "./vue-components.es14.js";
10
- import { generateMetaFromSchema as X } from "./vue-components.es8.js";
11
- import Y from "./vue-components.es5.js";
12
- import Z from "./vue-components.es15.js";
13
- import { trace as P } from "./vue-components.es16.js";
14
- import { context as g } from "./vue-components.es17.js";
15
- const p = (c) => function(o) {
1
+ import { useForm as J, useStore as q } from "@tanstack/vue-form";
2
+ import { Data as K, S as A, Effect as c, Fiber as y, Option as B } from "effect-app";
3
+ import { runtimeFiberAsPromise as k } from "./vue-components.es10.js";
4
+ import { isObject as G } from "./vue-components.es11.js";
5
+ import { computed as I, onUnmounted as T, onMounted as W, onBeforeUnmount as z, watch as Q, h as P } from "vue";
6
+ import X from "./vue-components.es12.js";
7
+ import Y from "./vue-components.es13.js";
8
+ import { buildOmegaErrors as Z } from "./vue-components.es6.js";
9
+ import C from "./vue-components.es14.js";
10
+ import { generateMetaFromSchema as ee } from "./vue-components.es8.js";
11
+ import re from "./vue-components.es5.js";
12
+ import te from "./vue-components.es15.js";
13
+ import { trace as M } from "./vue-components.es16.js";
14
+ import { context as E } from "./vue-components.es17.js";
15
+ class se extends K.TaggedError("FormErrors") {
16
+ }
17
+ const p = (l) => function(o) {
16
18
  return {
17
19
  render() {
18
- return $(o, {
19
- form: c,
20
+ return P(o, {
21
+ form: l,
20
22
  on: this.$listeners,
21
23
  attrs: this.$attrs
22
24
  }, this.$slots);
23
25
  }
24
26
  };
25
- }, C = (c) => function(o) {
27
+ }, ne = (l) => function(o) {
26
28
  return {
27
29
  setup() {
28
30
  return {
29
- ...c
31
+ ...l
30
32
  };
31
33
  },
32
- render({ errors: h, generalErrors: w, showErrors: S }) {
33
- return $(o, {
34
+ render({ errors: h, generalErrors: S, showErrors: w }) {
35
+ return P(o, {
34
36
  errors: h,
35
- generalErrors: w,
36
- showErrors: S,
37
+ generalErrors: S,
38
+ showErrors: w,
37
39
  on: this.$listeners,
38
40
  attrs: this.$attrs
39
41
  }, this.$slots);
40
42
  }
41
43
  };
42
- }, pe = (c, a, o) => {
43
- if (!c) throw new Error("Schema is required");
44
- const h = O.standardSchemaV1(c), w = O.decode(c), { filterItems: S, meta: f } = X(c), l = I(() => {
44
+ }, ve = (l, a, o) => {
45
+ if (!l) throw new Error("Schema is required");
46
+ const h = A.standardSchemaV1(l), S = A.decode(l), { filterItems: w, meta: d } = ee(l), m = I(() => {
45
47
  if (o?.persistency?.id)
46
48
  return o.persistency.id;
47
- const e = window.location.pathname, r = Object.keys(f);
49
+ const e = window.location.pathname, r = Object.keys(d);
48
50
  return `${e}-${r.join("-")}`;
49
- }), A = () => {
51
+ }), U = () => {
50
52
  const e = new URLSearchParams(window.location.search);
51
- e.delete(l.value);
53
+ e.delete(m.value);
52
54
  const r = new URL(window.location.href);
53
55
  r.search = e.toString(), window.history.replaceState({}, "", r.toString());
54
56
  };
55
- function E(e, r) {
57
+ function g(e, r) {
56
58
  for (const t in r)
57
- r[t] && q(r[t]) ? (e[t] || (e[t] = {}), E(e[t], r[t])) : e[t] = r[t];
59
+ r[t] && G(r[t]) ? (e[t] || (e[t] = {}), g(e[t], r[t])) : e[t] = r[t];
58
60
  return e;
59
61
  }
60
- const U = I(() => {
62
+ const $ = I(() => {
61
63
  if (a?.defaultValues && !o?.persistency?.overrideDefaultValues)
62
64
  return a?.defaultValues;
63
65
  let e;
@@ -65,8 +67,8 @@ const p = (c) => function(o) {
65
67
  if (!r?.policies || r.policies.length === 0) return {};
66
68
  if (r.policies.includes("querystring"))
67
69
  try {
68
- const s = new URLSearchParams(window.location.search).get(l.value);
69
- A(), s && (e = JSON.parse(s));
70
+ const s = new URLSearchParams(window.location.search).get(m.value);
71
+ U(), s && (e = JSON.parse(s));
70
72
  } catch (t) {
71
73
  console.error(t);
72
74
  }
@@ -78,9 +80,9 @@ const p = (c) => function(o) {
78
80
  if (t)
79
81
  try {
80
82
  const s = JSON.parse(
81
- t.getItem(l.value) || "{}"
83
+ t.getItem(m.value) || "{}"
82
84
  );
83
- t.removeItem(l.value), e = s;
85
+ t.removeItem(m.value), e = s;
84
86
  } catch (s) {
85
87
  console.error(s);
86
88
  }
@@ -89,95 +91,97 @@ const p = (c) => function(o) {
89
91
  return e;
90
92
  {
91
93
  const t = a?.defaultValues;
92
- return E(t, e);
94
+ return g(t, e);
93
95
  }
94
- }), M = (e, r) => e ? g.with(P.setSpan(g.active(), e), r) : r(), u = D({
96
+ }), N = (e, r) => e ? E.with(M.setSpan(E.active(), e), r) : r(), u = J({
95
97
  ...a,
96
98
  validators: {
97
99
  onSubmit: h,
98
100
  ...a?.validators || {}
99
101
  },
100
- onSubmit: a?.onSubmit ? ({ formApi: e, meta: r, value: t }) => M(r?.currentSpan, async () => {
101
- const s = await d.runPromise(w(t)), n = a.onSubmit({
102
+ onSubmit: a?.onSubmit ? ({ formApi: e, meta: r, value: t }) => N(r?.currentSpan, async () => {
103
+ const s = await c.runPromise(S(t)), n = a.onSubmit({
102
104
  formApi: e,
103
105
  meta: r,
104
106
  value: s
105
107
  });
106
- return y.isFiber(n) && y.isRuntimeFiber(n) ? await J(n) : d.isEffect(n) ? await d.runPromise(
108
+ return y.isFiber(n) && y.isRuntimeFiber(n) ? await k(n) : c.isEffect(n) ? await c.runPromise(
107
109
  n.pipe(
108
110
  // meta?.currentSpan
109
111
  // ? Effect.withParentSpan(meta.currentSpan)
110
112
  // : (_) => _,
111
- d.flatMap((m) => y.join(m))
113
+ c.flatMap((f) => y.join(f))
112
114
  )
113
115
  ) : n;
114
116
  }) : void 0,
115
- defaultValues: U.value
116
- }), N = () => {
117
- Object.keys(f).forEach((e) => {
117
+ defaultValues: $.value
118
+ }), _ = () => {
119
+ Object.keys(d).forEach((e) => {
118
120
  u.setFieldValue(e, void 0);
119
121
  });
120
- }, V = (e) => e.reduce((r, t) => {
122
+ }, F = (e) => e.reduce((r, t) => {
121
123
  const s = t.split(".");
122
- return s.reduce((n, m, x) => (x === s.length - 1 ? n[m] = u.getFieldValue(t) : n[m] = n[m] ?? {}, n[m]), r), r;
123
- }, {}), F = (e) => {
124
+ return s.reduce((n, f, H) => (H === s.length - 1 ? n[f] = u.getFieldValue(t) : n[f] = n[f] ?? {}, n[f]), r), r;
125
+ }, {}), V = (e) => {
124
126
  if (e) {
125
127
  if (Array.isArray(e.keys))
126
- return V(e.keys);
128
+ return F(e.keys);
127
129
  if (Array.isArray(e.banKeys)) {
128
- const r = Object.keys(f).filter((t) => e.banKeys?.includes(t));
129
- return V(r);
130
+ const r = Object.keys(d).filter((t) => e.banKeys?.includes(t));
131
+ return F(r);
130
132
  }
131
133
  return u.store.state.values;
132
134
  }
133
- }, v = () => {
135
+ }, b = () => {
134
136
  const e = o?.persistency;
135
137
  if (!(!e?.policies || e.policies.length === 0) && (e.policies.includes("local") || e.policies.includes("session"))) {
136
138
  const r = e.policies.includes("local") ? localStorage : sessionStorage;
137
139
  if (!r) return;
138
- const t = F(e);
139
- return r.setItem(l.value, JSON.stringify(t));
140
+ const t = V(e);
141
+ return r.setItem(m.value, JSON.stringify(t));
140
142
  }
141
- }, j = () => {
143
+ }, O = () => {
142
144
  const e = o?.persistency;
143
145
  if (!(!e?.policies || e.policies.length === 0) && e.policies.includes("querystring")) {
144
- const r = F(e), t = new URLSearchParams(window.location.search);
145
- t.set(l.value, JSON.stringify(r));
146
+ const r = V(e), t = new URLSearchParams(window.location.search);
147
+ t.set(m.value, JSON.stringify(r));
146
148
  const s = new URL(window.location.href);
147
149
  s.search = t.toString(), window.history.replaceState({}, "", s.toString());
148
150
  }
149
151
  };
150
- K(v), B(() => {
151
- window.addEventListener("beforeunload", v), window.addEventListener("blur", j);
152
- }), G(() => {
153
- window.removeEventListener("beforeunload", v), window.removeEventListener("blur", j);
152
+ T(b), W(() => {
153
+ window.addEventListener("beforeunload", b), window.addEventListener("blur", O);
154
+ }), z(() => {
155
+ window.removeEventListener("beforeunload", b), window.removeEventListener("blur", O);
154
156
  });
155
- const R = u.handleSubmit, i = Object.assign(u, {
157
+ const j = (e) => c.currentSpan.pipe(
158
+ c.option,
159
+ c.flatMap(
160
+ (r) => c.promise(() => u.handleSubmit(B.isSome(r) ? { currentSpan: r.value, ...e } : e))
161
+ )
162
+ ), R = (e) => e?.checkErrors ? j(e?.meta).pipe(c.flatMap(c.fnUntraced(function* () {
163
+ const r = u.getAllErrors();
164
+ if (r.form.errors.length)
165
+ return yield* new se({ form: r.form, fields: r.fields });
166
+ }))) : j(e?.meta), x = u.handleSubmit, i = Object.assign(u, {
156
167
  i18nNamespace: o?.i18nNamespace,
157
- meta: f,
158
- filterItems: S,
159
- clear: N,
168
+ meta: d,
169
+ filterItems: w,
170
+ clear: _,
160
171
  handleSubmit: (e) => {
161
- const r = P.getSpan(g.active());
162
- return R({ currentSpan: r, ...e });
163
- }
172
+ const r = M.getSpan(E.active());
173
+ return x({ currentSpan: r, ...e });
174
+ },
164
175
  // /** @experimental */
165
- // handleSubmitEffect: (meta?: Record<string, any>) =>
166
- // Effect.currentSpan.pipe(
167
- // Effect.option,
168
- // Effect
169
- // .flatMap((span) =>
170
- // Effect.promise(() => form.handleSubmit(Option.isSome(span) ? { currentSpan: span.value, ...meta } : meta))
171
- // )
172
- // )
173
- }), _ = H(
176
+ handleSubmitEffect: R
177
+ }), D = q(
174
178
  i.store,
175
179
  (e) => e.submissionAttempts
176
- ), b = i.useStore((e) => e.errors);
177
- W(
178
- () => [i.filterItems, b.value],
180
+ ), v = i.useStore((e) => e.errors);
181
+ Q(
182
+ () => [i.filterItems, v.value],
179
183
  () => {
180
- const e = i.filterItems, r = b.value;
184
+ const e = i.filterItems, r = v.value;
181
185
  return e ? r ? (Object.values(r).filter(
182
186
  (s) => !!s
183
187
  ).flatMap(
@@ -195,17 +199,18 @@ const p = (c) => function(o) {
195
199
  }), {}) : {} : {};
196
200
  }
197
201
  );
198
- const L = Q(_, b, o?.showErrorsOn);
202
+ const L = Z(D, v, o?.showErrorsOn);
199
203
  return Object.assign(i, {
200
204
  errorContext: L,
201
- Form: p(i)(Z),
202
- Input: p(i)(o?.input ?? Y),
205
+ Form: p(i)(te),
206
+ Input: p(i)(o?.input ?? re),
203
207
  Field: u.Field,
204
- Errors: C(L)(T),
205
- Array: p(i)(k),
206
- AutoGen: p(i)(z)
208
+ Errors: ne(L)(C),
209
+ Array: p(i)(X),
210
+ AutoGen: p(i)(Y)
207
211
  });
208
212
  };
209
213
  export {
210
- pe as useOmegaForm
214
+ se as FormErrors,
215
+ ve as useOmegaForm
211
216
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect-app/vue-components",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "peerDependencies": {
5
5
  "@mdi/js": "^7.4.47",
6
6
  "effect": "^3.17.13",
@@ -46,11 +46,11 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "@opentelemetry/api": "^1.9.0",
49
- "@tanstack/vue-form": "^1.23.0",
49
+ "@tanstack/vue-form": "^1.23.4",
50
50
  "highlight.js": "^11.11.1",
51
51
  "vue3-highlightjs": "^1.0.5",
52
- "@effect-app/vue": "2.80.3",
53
- "effect-app": "3.8.1"
52
+ "effect-app": "3.8.1",
53
+ "@effect-app/vue": "2.81.0"
54
54
  },
55
55
  "scripts": {
56
56
  "build": "pnpm build:run",
@@ -2,8 +2,8 @@
2
2
  /* eslint-disable @typescript-eslint/no-explicit-any */
3
3
 
4
4
  import * as api from "@opentelemetry/api"
5
- import { type DeepKeys, DeepValue, type FormAsyncValidateOrFn, type FormValidateOrFn, type StandardSchemaV1, StandardSchemaV1Issue, useForm, useStore } from "@tanstack/vue-form"
6
- import { Effect, Fiber, Order, S } from "effect-app"
5
+ import { type DeepKeys, DeepValue, type FormAsyncValidateOrFn, type FormValidateOrFn, type StandardSchemaV1, StandardSchemaV1Issue, useForm, useStore, ValidationError, ValidationErrorMap } from "@tanstack/vue-form"
6
+ import { Data, Effect, Fiber, Option, Order, S } from "effect-app"
7
7
  import { runtimeFiberAsPromise } from "effect-app/utils"
8
8
  import { isObject } from "effect/Predicate"
9
9
  import { Component, computed, ConcreteComponent, h, type InjectionKey, onBeforeUnmount, onMounted, onUnmounted, watch } from "vue"
@@ -26,6 +26,29 @@ type keysRule<T> =
26
26
  banKeys?: NestedKeyOf<T>[]
27
27
  }
28
28
 
29
+ export class FormErrors<From> extends Data.TaggedError("FormErrors")<{
30
+ form: {
31
+ // TODO: error shapes seem off, with `undefined` etc..
32
+ errors: (Record<string, StandardSchemaV1Issue[]> | undefined)[]
33
+ errorMap: ValidationErrorMap<
34
+ undefined,
35
+ undefined,
36
+ Record<string, StandardSchemaV1Issue[]>,
37
+ undefined,
38
+ undefined,
39
+ undefined,
40
+ undefined,
41
+ undefined,
42
+ undefined,
43
+ undefined
44
+ >
45
+ }
46
+ fields: Record<DeepKeys<From>, {
47
+ errors: ValidationError[]
48
+ errorMap: ValidationErrorMap
49
+ }>
50
+ }> {}
51
+
29
52
  const fHoc = (form: OF<any, any>) => {
30
53
  return function FormHoc<P>(
31
54
  WrappedComponent: Component<P>
@@ -87,8 +110,15 @@ export interface OF<From, To> extends OmegaFormApi<From, To> {
87
110
  filterItems?: FilterItems
88
111
  clear: () => void
89
112
  i18nNamespace?: string
90
- // /** @experimental */
91
- // handleSubmitEffect: (meta?: Record<string, any>) => Effect.Effect<void, never, never>
113
+ /** @experimental */
114
+ handleSubmitEffect: {
115
+ /**
116
+ * when `checkErrors` is true, the Effect will fail with `FormErrors<From>` when there are validation errors
117
+ * @experimental */
118
+ (options: { checkErrors: true; meta?: Record<string, any> }): Effect.Effect<void, FormErrors<From>>
119
+ /** @experimental */
120
+ (options?: { meta?: Record<string, any> }): Effect.Effect<void>
121
+ }
92
122
  }
93
123
 
94
124
  export const OmegaFormKey = Symbol("OmegaForm") as InjectionKey<OF<any, any>>
@@ -794,6 +824,30 @@ export const useOmegaForm = <
794
824
  window.removeEventListener("blur", saveDataInUrl)
795
825
  })
796
826
 
827
+ const handleSubmitEffect_ = (meta?: Record<string, any>) =>
828
+ Effect.currentSpan.pipe(
829
+ Effect.option,
830
+ Effect
831
+ .flatMap((span) =>
832
+ Effect.promise(() => form.handleSubmit(Option.isSome(span) ? { currentSpan: span.value, ...meta } : meta))
833
+ )
834
+ )
835
+
836
+ const handleSubmitEffect: {
837
+ (options: { checkErrors: true; meta?: Record<string, any> }): Effect.Effect<void, FormErrors<From>>
838
+ (options?: { meta?: Record<string, any> }): Effect.Effect<void>
839
+ } = (
840
+ options?: { meta?: Record<string, any>; checkErrors?: true }
841
+ ): any =>
842
+ options?.checkErrors
843
+ ? handleSubmitEffect_(options?.meta).pipe(Effect.flatMap(Effect.fnUntraced(function*() {
844
+ const errors = form.getAllErrors()
845
+ if (errors.form.errors.length) {
846
+ return yield* new FormErrors({ form: errors.form, fields: errors.fields })
847
+ }
848
+ })))
849
+ : handleSubmitEffect_(options?.meta)
850
+
797
851
  const handleSubmit = form.handleSubmit
798
852
  const formWithExtras: OF<From, To> = Object.assign(form, {
799
853
  i18nNamespace: omegaConfig?.i18nNamespace,
@@ -803,16 +857,9 @@ export const useOmegaForm = <
803
857
  handleSubmit: (meta?: Record<string, any>) => {
804
858
  const span = api.trace.getSpan(api.context.active())
805
859
  return handleSubmit({ currentSpan: span, ...meta })
806
- }
860
+ },
807
861
  // /** @experimental */
808
- // handleSubmitEffect: (meta?: Record<string, any>) =>
809
- // Effect.currentSpan.pipe(
810
- // Effect.option,
811
- // Effect
812
- // .flatMap((span) =>
813
- // Effect.promise(() => form.handleSubmit(Option.isSome(span) ? { currentSpan: span.value, ...meta } : meta))
814
- // )
815
- // )
862
+ handleSubmitEffect
816
863
  })
817
864
 
818
865
  const formSubmissionAttempts = useStore(