@webamoki/web-svelte 0.5.34 → 0.6.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.
@@ -13,7 +13,7 @@
13
13
  </script>
14
14
 
15
15
  <div class="relative w-full max-w-sm">
16
- <Search class="absolute top-2.5 left-2.5 h-4 w-4 text-muted-foreground" />
16
+ <Search class="absolute top-1/2 left-2.5 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
17
17
  <Input
18
18
  type="search"
19
19
  {placeholder}
@@ -1,29 +1,32 @@
1
1
  import { type SuperValidated } from 'sveltekit-superforms';
2
+ import { type VirtualFormValidated } from './form-processor.js';
2
3
  /**
3
4
  * automatically handle database errors from catch.
4
5
  * used in form/action handling in page.server.ts
5
6
  */
6
- export declare function handleDbErrorForm<T extends Record<string, unknown>>(form: SuperValidated<T>, message: string, err: unknown): import("@sveltejs/kit").ActionFailure<{
7
- form: SuperValidated<T>;
7
+ export declare function handleDbErrorForm<T extends Record<string, unknown>>(form: SuperValidated<T> | VirtualFormValidated<T>, message: string, err: unknown): import("@sveltejs/kit").ActionFailure<{
8
+ form: SuperValidated<T> | VirtualFormValidated<T>;
8
9
  }>;
9
10
  /**
10
11
  * check if an error returned by a try catch is a duplicate value error in postgre
11
12
  */
12
13
  export declare function isDuplicateDbError(err: unknown): boolean;
13
- export declare function successMessage<T extends Record<string, unknown>>(form: SuperValidated<T>): {
14
- form: SuperValidated<T, any, T>;
15
- } | import("@sveltejs/kit").ActionFailure<{
16
- form: SuperValidated<T, any, T>;
17
- }>;
18
- export declare function errorMessage<T extends Record<string, unknown>>(form: SuperValidated<T>, options?: {
14
+ export declare function successMessage<T extends Record<string, unknown>>(form: SuperValidated<T> | VirtualFormValidated<T>, options?: {
19
15
  showToast?: boolean;
20
16
  text?: string;
21
17
  data?: unknown;
22
- }): {
23
- form: SuperValidated<T, any, T>;
24
- } | import("@sveltejs/kit").ActionFailure<{
25
- form: SuperValidated<T, any, T>;
26
- }>;
27
- export declare function failFormValidation<T extends Record<string, unknown>>(form: SuperValidated<T>): import("@sveltejs/kit").ActionFailure<{
28
- form: SuperValidated<T>;
18
+ }): any;
19
+ export declare function errorMessage<T extends Record<string, unknown>>(form: SuperValidated<T> | VirtualFormValidated<T>, options?: {
20
+ showToast?: boolean;
21
+ text?: string;
22
+ data?: unknown;
23
+ }): any;
24
+ export declare function failFormValidation<T extends Record<string, unknown>>(form: SuperValidated<T> | {
25
+ valid: boolean;
26
+ }): import("@sveltejs/kit").ActionFailure<{
27
+ message: string;
28
+ }> | import("@sveltejs/kit").ActionFailure<{
29
+ form: SuperValidated<T> | {
30
+ valid: boolean;
31
+ };
29
32
  }>;
@@ -1,5 +1,7 @@
1
1
  import { DatabaseError } from 'pg';
2
2
  import { fail, message as superFormMessage } from 'sveltekit-superforms';
3
+ import { FormError } from './form-processor.js';
4
+ import { fail as failKit } from '@sveltejs/kit';
3
5
  /**
4
6
  * automatically handle database errors from catch.
5
7
  * used in form/action handling in page.server.ts
@@ -12,23 +14,44 @@ export function handleDbErrorForm(form, message, err) {
12
14
  console.error(`Unexpected Error ${message}:`, err);
13
15
  return fail(500, { form });
14
16
  }
17
+ function isVirtualFormValidated(form) {
18
+ return 'virtual' in form && form.virtual;
19
+ }
15
20
  /**
16
21
  * check if an error returned by a try catch is a duplicate value error in postgre
17
22
  */
18
23
  export function isDuplicateDbError(err) {
19
24
  return err instanceof DatabaseError && err.code === '23505';
20
25
  }
21
- export function successMessage(form) {
22
- return superFormMessage(form, { success: true, showToast: true, text: 'Success' });
26
+ export function successMessage(form, options) {
27
+ const message = {
28
+ success: true,
29
+ showToast: options?.showToast ?? true,
30
+ text: options?.text ?? 'Success',
31
+ data: options?.data
32
+ };
33
+ if ('virtual' in form && form.virtual) {
34
+ return message;
35
+ }
36
+ return superFormMessage(form, message);
23
37
  }
24
38
  export function errorMessage(form, options) {
25
- return superFormMessage(form, {
39
+ const message = {
26
40
  success: false,
27
41
  showToast: options?.showToast ?? false,
28
42
  text: options?.text,
29
43
  data: options?.data
30
- });
44
+ };
45
+ if (isVirtualFormValidated(form)) {
46
+ return message;
47
+ }
48
+ return superFormMessage(form, message);
31
49
  }
32
50
  export function failFormValidation(form) {
51
+ if (form.valid)
52
+ throw new Error('Invalid form passed');
53
+ if (form instanceof FormError) {
54
+ return failKit(400, { message: form.message });
55
+ }
33
56
  return fail(400, { form });
34
57
  }
@@ -0,0 +1,12 @@
1
+ import { type } from 'arktype';
2
+ export declare class FormError {
3
+ message: string;
4
+ valid: false;
5
+ constructor(message: string);
6
+ }
7
+ export type VirtualFormValidated<T extends Record<string, unknown>> = {
8
+ data: T;
9
+ virtual: boolean;
10
+ valid: boolean;
11
+ };
12
+ export declare function processForm<S extends type.Any<Record<string, unknown>>>(request: Request, schema: S): Promise<FormError | import("sveltekit-superforms").SuperValidated<S["infer"], any, S["inferIn"]> | VirtualFormValidated<S["infer"]>>;
@@ -0,0 +1,48 @@
1
+ import { dateTransport } from '../utils/datetime/index.js';
2
+ import { type } from 'arktype';
3
+ import { superValidate } from 'sveltekit-superforms';
4
+ import { arktype } from 'sveltekit-superforms/adapters';
5
+ export class FormError {
6
+ message;
7
+ valid;
8
+ constructor(message) {
9
+ this.message = message;
10
+ this.valid = false;
11
+ }
12
+ }
13
+ export async function processForm(request, schema) {
14
+ let formData;
15
+ try {
16
+ formData = await request.formData();
17
+ }
18
+ catch (error) {
19
+ return new FormError(error instanceof Error ? error.message : 'Invalid form data');
20
+ }
21
+ if ('__superform_id' in formData) {
22
+ return await superValidate(request, arktype(schema), { transport: dateTransport });
23
+ }
24
+ try {
25
+ // Parse form data and extract the JSON string
26
+ const dataString = formData.get('data');
27
+ if (typeof dataString !== 'string') {
28
+ return new FormError('Missing or invalid data field');
29
+ }
30
+ // Parse the JSON string
31
+ const body = JSON.parse(dataString);
32
+ // Validate against the schema
33
+ const validated = schema(body);
34
+ // Check if validation failed
35
+ if (validated instanceof type.errors) {
36
+ return new FormError(validated.summary);
37
+ }
38
+ // Return validated data
39
+ return {
40
+ data: validated,
41
+ virtual: true,
42
+ valid: true
43
+ };
44
+ }
45
+ catch (error) {
46
+ return new FormError(error instanceof Error ? error.message : 'Invalid form data');
47
+ }
48
+ }
@@ -20,4 +20,3 @@ export declare function prepareEmptyForm<S extends type.Any<Record<string, unkno
20
20
  delayed: import("svelte/store").Readable<boolean>;
21
21
  errors: import("sveltekit-superforms/client").SuperFormErrors<S["infer"]>;
22
22
  };
23
- export declare function getServerForm<S extends type.Any<Record<string, unknown>>>(request: Request, schema: S): Promise<SuperValidated<S["infer"], any, S["inferIn"]>>;
@@ -1,8 +1,8 @@
1
1
  import { type } from 'arktype';
2
2
  import { toast } from 'svelte-sonner';
3
- import { defaults, superForm, superValidate } from 'sveltekit-superforms';
3
+ import { defaults, superForm } from 'sveltekit-superforms';
4
4
  import { arktype, arktypeClient } from 'sveltekit-superforms/adapters';
5
- import { dateTransport } from './datetime/index.js';
5
+ import { dateTransport } from '../datetime/index.js';
6
6
  export function prepareForm(validated, schema, options) {
7
7
  const form = superForm(validated, {
8
8
  validators: arktypeClient(schema),
@@ -65,6 +65,3 @@ export function prepareEmptyForm(schema, options) {
65
65
  const errors = form.errors;
66
66
  return { form, data: form.form, delayed, errors };
67
67
  }
68
- export async function getServerForm(request, schema) {
69
- return await superValidate(request, arktype(schema), { transport: dateTransport });
70
- }
@@ -0,0 +1,11 @@
1
+ import { type } from 'arktype';
2
+ export declare class VirtualForm<S extends type.Any<Record<string, unknown>>> {
3
+ #private;
4
+ constructor(schema: S, action: string, options?: {
5
+ actionName?: string;
6
+ onSuccess?: (message: App.Superforms.Message) => void;
7
+ onError?: (message: App.Superforms.Message) => void;
8
+ });
9
+ submit(data: S['infer']): Promise<void>;
10
+ get isLoading(): boolean;
11
+ }
@@ -0,0 +1,96 @@
1
+ import { type } from 'arktype';
2
+ import { toast } from 'svelte-sonner';
3
+ import { createSubscriber } from 'svelte/reactivity';
4
+ function decodeMessage(json) {
5
+ const parsed = JSON.parse(json);
6
+ // Basic validation
7
+ if (!Array.isArray(parsed) || typeof parsed[0] !== 'object' || parsed[0] === null) {
8
+ throw new Error('Invalid encoded message format');
9
+ }
10
+ const schema = parsed[0];
11
+ // resolve an index into the parsed array
12
+ const resolve = (idx) => (idx >= 0 ? parsed[idx] : undefined);
13
+ const output = {};
14
+ // dynamically resolve each schema key
15
+ for (const key of Object.keys(schema)) {
16
+ const index = schema[key];
17
+ output[key] = resolve(index);
18
+ }
19
+ return output;
20
+ }
21
+ export class VirtualForm {
22
+ // state storage
23
+ #isLoading = false;
24
+ #url = '';
25
+ #schema;
26
+ #onSuccess;
27
+ #onError;
28
+ // svelte reactive tracking
29
+ #subscribe;
30
+ #update = () => { };
31
+ constructor(schema, action, options = {}) {
32
+ this.#url = `${action}${options.actionName ? '?/' + options.actionName : ''}`;
33
+ this.#schema = schema;
34
+ this.#onSuccess = options.onSuccess;
35
+ this.#onError = options.onError;
36
+ this.#subscribe = createSubscriber((update) => {
37
+ this.#update = update;
38
+ return () => { };
39
+ });
40
+ }
41
+ async submit(data) {
42
+ this.#isLoading = true;
43
+ this.#update();
44
+ // Validate data against schema
45
+ const validated = this.#schema(data);
46
+ if (validated instanceof type.errors) {
47
+ console.error('Validation failed:', validated.summary);
48
+ this.#onError?.({
49
+ text: 'Validation failed',
50
+ data: validated.summary,
51
+ success: false,
52
+ showToast: false
53
+ });
54
+ return;
55
+ }
56
+ try {
57
+ // Encode JSON as form data (like superforms does)
58
+ const formData = new FormData();
59
+ formData.append('data', JSON.stringify(validated));
60
+ const res = await fetch(this.#url, {
61
+ method: 'POST',
62
+ body: formData
63
+ });
64
+ const result = await res.json();
65
+ if (!res.ok || result.status === 400) {
66
+ console.error('Request failed:', result);
67
+ this.#onError?.(result);
68
+ this.#isLoading = false;
69
+ this.#update();
70
+ return;
71
+ }
72
+ const message = decodeMessage(result['data']);
73
+ const text = message?.text;
74
+ if (message.success) {
75
+ this.#onSuccess?.(message);
76
+ if (text && message?.showToast)
77
+ toast.success(text);
78
+ }
79
+ else {
80
+ this.#onError?.(message);
81
+ if (text && message?.showToast)
82
+ toast.error(text);
83
+ }
84
+ }
85
+ catch (err) {
86
+ console.error(err);
87
+ this.#onError?.({ text: 'Network error', data: err, success: false, showToast: false });
88
+ }
89
+ this.#isLoading = false;
90
+ this.#update();
91
+ }
92
+ get isLoading() {
93
+ this.#subscribe();
94
+ return this.#isLoading;
95
+ }
96
+ }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.5.34",
6
+ "version": "0.6.0",
7
7
  "license": "MIT",
8
8
  "files": [
9
9
  "dist",
@@ -40,13 +40,17 @@
40
40
  "import": "./dist/utils/types/db.js"
41
41
  },
42
42
  "./utils/form": {
43
- "types": "./dist/utils/form.d.ts",
44
- "import": "./dist/utils/form.js"
43
+ "types": "./dist/utils/form/index.d.ts",
44
+ "import": "./dist/utils/form/index.js"
45
45
  },
46
46
  "./server/form-handler": {
47
47
  "types": "./dist/server/form-handler.d.ts",
48
48
  "import": "./dist/server/form-handler.js"
49
49
  },
50
+ "./server/form-processor": {
51
+ "types": "./dist/server/form-processor.d.ts",
52
+ "import": "./dist/server/form-processor.js"
53
+ },
50
54
  "./utils/search": {
51
55
  "types": "./dist/utils/search.d.ts",
52
56
  "import": "./dist/utils/search.js"
@@ -63,7 +67,6 @@
63
67
  "@changesets/cli": "^2.29.7",
64
68
  "@eslint/compat": "^1.4.1",
65
69
  "@eslint/js": "^9.39.1",
66
- "@sveltejs/adapter-static": "^3.0.10",
67
70
  "@sveltejs/kit": "^2.48.4",
68
71
  "@sveltejs/package": "^2.5.4",
69
72
  "@sveltejs/vite-plugin-svelte": "^6.2.1",
@@ -101,6 +104,7 @@
101
104
  "dependencies": {
102
105
  "@internationalized/date": "^3.10.0",
103
106
  "@lucide/svelte": "^0.553.0",
107
+ "@sveltejs/adapter-auto": "^7.0.0",
104
108
  "arktype": "^2.1.26",
105
109
  "bits-ui": "^2.14.3",
106
110
  "drizzle-orm": "^0.44.7",