@webamoki/web-svelte 1.2.2 → 2.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 (117) hide show
  1. package/README.md +3 -1
  2. package/dist/components/showcase/CodeBlock.svelte +1 -1
  3. package/dist/{components → shared/components}/form/Button.svelte +2 -2
  4. package/dist/{components → shared/components}/form/Button.svelte.d.ts +2 -2
  5. package/dist/{components → shared/components}/form/Errors.svelte +1 -1
  6. package/dist/{components → shared/components}/form/FieldWrapper.svelte +1 -1
  7. package/dist/{components → shared/components}/form/fields/ChoiceMultiField.svelte +3 -1
  8. package/dist/{components → shared/components}/form/fields/DateField.svelte +2 -2
  9. package/dist/{components → shared/components}/form/fields/MessageField.svelte +2 -2
  10. package/dist/{components → shared/components}/form/fields/NumberField.svelte +2 -2
  11. package/dist/{components → shared/components}/form/fields/PasswordField.svelte +2 -2
  12. package/dist/{components → shared/components}/form/fields/SelectField.svelte +3 -3
  13. package/dist/{components → shared/components}/form/fields/SelectMultiField.svelte +3 -3
  14. package/dist/{components → shared/components}/form/fields/TextField.svelte +2 -2
  15. package/dist/{components → shared/components}/form/fields/TextFieldNullable.svelte +2 -2
  16. package/dist/{components → shared/components}/form/fields/TimeField.svelte +2 -2
  17. package/dist/{components → shared/components}/ui/choice/ChoiceInternal.svelte +1 -1
  18. package/dist/{components → shared/components}/ui/choice/WeekdayChoice.svelte +6 -3
  19. package/dist/{components → shared/components}/ui/choice/WeekdayChoice.svelte.d.ts +1 -1
  20. package/dist/{components → shared/components}/ui/choice/WeekdayChoiceMulti.svelte +6 -3
  21. package/dist/{components → shared/components}/ui/choice/WeekdayChoiceMulti.svelte.d.ts +1 -1
  22. package/dist/{components → shared/components}/ui/context-menu/ContextMenuContent.svelte +1 -1
  23. package/dist/{components → shared/components}/ui/context-menu/ContextMenuItem.svelte +1 -1
  24. package/dist/{components → shared/components}/ui/search/SearchBar.svelte +2 -2
  25. package/dist/{utils/types → shared/utils}/arktype.d.ts +4 -12
  26. package/dist/shared/utils/arktype.js +40 -0
  27. package/dist/shared/utils/datetime/datetime.spec.d.ts +1 -0
  28. package/dist/shared/utils/datetime/datetime.spec.js +768 -0
  29. package/dist/{utils → shared/utils}/datetime/index.d.ts +22 -14
  30. package/dist/{utils → shared/utils}/datetime/index.js +44 -32
  31. package/dist/{utils → shared/utils}/email/README.md +5 -5
  32. package/dist/{utils → shared/utils}/email/ses.js +17 -9
  33. package/dist/shared/utils/email/ses.test.d.ts +1 -0
  34. package/dist/shared/utils/email/ses.test.js +335 -0
  35. package/dist/shared/utils/functional/index.d.ts +2 -0
  36. package/dist/shared/utils/functional/index.js +2 -0
  37. package/dist/shared/utils/functional/result.d.ts +72 -0
  38. package/dist/shared/utils/functional/result.js +86 -0
  39. package/dist/shared/utils/functional/result.spec.d.ts +1 -0
  40. package/dist/shared/utils/functional/result.spec.js +96 -0
  41. package/dist/shared/utils/remote.d.ts +28 -0
  42. package/dist/shared/utils/remote.js +74 -0
  43. package/dist/{utils/search.d.ts → shared/utils/string.d.ts} +1 -0
  44. package/dist/{utils/search.js → shared/utils/string.js} +13 -4
  45. package/package.json +28 -33
  46. package/dist/utils/email/aws-signer.d.ts +0 -17
  47. package/dist/utils/email/aws-signer.js +0 -83
  48. package/dist/utils/string.d.ts +0 -1
  49. package/dist/utils/string.js +0 -4
  50. package/dist/utils/types/arktype.js +0 -92
  51. package/dist/utils/types/consts.d.ts +0 -5
  52. package/dist/utils/types/consts.js +0 -5
  53. package/dist/utils/types/db.d.ts +0 -57
  54. package/dist/utils/types/db.js +0 -34
  55. /package/dist/{components → shared/components}/form/Errors.svelte.d.ts +0 -0
  56. /package/dist/{components → shared/components}/form/FieldWrapper.svelte.d.ts +0 -0
  57. /package/dist/{components → shared/components}/form/Form.svelte +0 -0
  58. /package/dist/{components → shared/components}/form/Form.svelte.d.ts +0 -0
  59. /package/dist/{components → shared/components}/form/IconInputWrapper.svelte +0 -0
  60. /package/dist/{components → shared/components}/form/IconInputWrapper.svelte.d.ts +0 -0
  61. /package/dist/{components → shared/components}/form/fields/ChoiceField.svelte +0 -0
  62. /package/dist/{components → shared/components}/form/fields/ChoiceField.svelte.d.ts +0 -0
  63. /package/dist/{components → shared/components}/form/fields/ChoiceMultiField.svelte.d.ts +0 -0
  64. /package/dist/{components → shared/components}/form/fields/DateField.svelte.d.ts +0 -0
  65. /package/dist/{components → shared/components}/form/fields/HexColorField.svelte +0 -0
  66. /package/dist/{components → shared/components}/form/fields/HexColorField.svelte.d.ts +0 -0
  67. /package/dist/{components → shared/components}/form/fields/MessageField.svelte.d.ts +0 -0
  68. /package/dist/{components → shared/components}/form/fields/NumberField.svelte.d.ts +0 -0
  69. /package/dist/{components → shared/components}/form/fields/PasswordField.svelte.d.ts +0 -0
  70. /package/dist/{components → shared/components}/form/fields/SelectField.svelte.d.ts +0 -0
  71. /package/dist/{components → shared/components}/form/fields/SelectMultiField.svelte.d.ts +0 -0
  72. /package/dist/{components → shared/components}/form/fields/TextField.svelte.d.ts +0 -0
  73. /package/dist/{components → shared/components}/form/fields/TextFieldNullable.svelte.d.ts +0 -0
  74. /package/dist/{components → shared/components}/form/fields/TimeField.svelte.d.ts +0 -0
  75. /package/dist/{components → shared/components}/form/fields/WeekdayChoiceField.svelte +0 -0
  76. /package/dist/{components → shared/components}/form/fields/WeekdayChoiceField.svelte.d.ts +0 -0
  77. /package/dist/{components → shared/components}/form/fields/WeekdayChoiceMultiField.svelte +0 -0
  78. /package/dist/{components → shared/components}/form/fields/WeekdayChoiceMultiField.svelte.d.ts +0 -0
  79. /package/dist/{components → shared/components}/form/index.d.ts +0 -0
  80. /package/dist/{components → shared/components}/form/index.js +0 -0
  81. /package/dist/{components → shared/components}/ui/choice/Choice.svelte +0 -0
  82. /package/dist/{components → shared/components}/ui/choice/Choice.svelte.d.ts +0 -0
  83. /package/dist/{components → shared/components}/ui/choice/ChoiceInternal.svelte.d.ts +0 -0
  84. /package/dist/{components → shared/components}/ui/choice/ChoiceMulti.svelte +0 -0
  85. /package/dist/{components → shared/components}/ui/choice/ChoiceMulti.svelte.d.ts +0 -0
  86. /package/dist/{components → shared/components}/ui/context-menu/ContextMenu.svelte +0 -0
  87. /package/dist/{components → shared/components}/ui/context-menu/ContextMenu.svelte.d.ts +0 -0
  88. /package/dist/{components → shared/components}/ui/context-menu/ContextMenuContent.svelte.d.ts +0 -0
  89. /package/dist/{components → shared/components}/ui/context-menu/ContextMenuItem.svelte.d.ts +0 -0
  90. /package/dist/{components → shared/components}/ui/context-menu/ContextMenuSeparator.svelte +0 -0
  91. /package/dist/{components → shared/components}/ui/context-menu/ContextMenuSeparator.svelte.d.ts +0 -0
  92. /package/dist/{components → shared/components}/ui/context-menu/ContextMenuTrigger.svelte +0 -0
  93. /package/dist/{components → shared/components}/ui/context-menu/ContextMenuTrigger.svelte.d.ts +0 -0
  94. /package/dist/{components → shared/components}/ui/context-menu/context-menu-state.svelte.d.ts +0 -0
  95. /package/dist/{components → shared/components}/ui/context-menu/context-menu-state.svelte.js +0 -0
  96. /package/dist/{components → shared/components}/ui/drag-drop/Draggable.svelte +0 -0
  97. /package/dist/{components → shared/components}/ui/drag-drop/Draggable.svelte.d.ts +0 -0
  98. /package/dist/{components → shared/components}/ui/drag-drop/Dropzone.svelte +0 -0
  99. /package/dist/{components → shared/components}/ui/drag-drop/Dropzone.svelte.d.ts +0 -0
  100. /package/dist/{components → shared/components}/ui/drag-drop/drag-manager.d.ts +0 -0
  101. /package/dist/{components → shared/components}/ui/drag-drop/drag-manager.js +0 -0
  102. /package/dist/{components → shared/components}/ui/index.d.ts +0 -0
  103. /package/dist/{components → shared/components}/ui/index.js +0 -0
  104. /package/dist/{components → shared/components}/ui/search/SearchBar.svelte.d.ts +0 -0
  105. /package/dist/{server → shared/server}/form-handler.d.ts +0 -0
  106. /package/dist/{server → shared/server}/form-handler.js +0 -0
  107. /package/dist/{server → shared/server}/form-processor.d.ts +0 -0
  108. /package/dist/{server → shared/server}/form-processor.js +0 -0
  109. /package/dist/{utils → shared/utils}/email/index.d.ts +0 -0
  110. /package/dist/{utils → shared/utils}/email/index.js +0 -0
  111. /package/dist/{utils → shared/utils}/email/ses.d.ts +0 -0
  112. /package/dist/{utils → shared/utils}/form/index.d.ts +0 -0
  113. /package/dist/{utils → shared/utils}/form/index.js +0 -0
  114. /package/dist/{utils → shared/utils}/form/virtual-form.d.ts +0 -0
  115. /package/dist/{utils → shared/utils}/form/virtual-form.js +0 -0
  116. /package/dist/{highlight.d.ts → utils/highlight.d.ts} +0 -0
  117. /package/dist/{highlight.js → utils/highlight.js} +0 -0
@@ -0,0 +1,86 @@
1
+ export const Result = {
2
+ /**
3
+ * Creates a failed result with the given error.
4
+ */
5
+ err(error) {
6
+ return { error, ok: false };
7
+ },
8
+ /**
9
+ * Creates a successful result with the given value.
10
+ */
11
+ ok(value) {
12
+ return { ok: true, value };
13
+ },
14
+ /**
15
+ * Returns true if the result is Ok.
16
+ */
17
+ isOk(result) {
18
+ return result.ok;
19
+ },
20
+ /**
21
+ * Returns true if the result is Err.
22
+ */
23
+ isErr(result) {
24
+ return !result.ok;
25
+ },
26
+ /**
27
+ * Returns the contained Ok value or a provided default.
28
+ */
29
+ unwrapOr(result, defaultValue) {
30
+ if (result.ok)
31
+ return result.value;
32
+ return defaultValue;
33
+ },
34
+ /**
35
+ * Returns the contained Ok value or computes it from the provided op.
36
+ * @param op - Computes value in error case or throws an error.
37
+ */
38
+ unwrapOrElse(result, op) {
39
+ if (result.ok)
40
+ return result.value;
41
+ return op(result.error);
42
+ },
43
+ /**
44
+ * Maps a Result<T, E> to Result<U, E> by applying a function to a contained Ok value,
45
+ * leaving an Err value untouched.
46
+ */
47
+ map(result, fn) {
48
+ if (result.ok)
49
+ return Result.ok(fn(result.value));
50
+ return result;
51
+ },
52
+ /**
53
+ * Maps a Result<T, E> to Result<T, F> by applying a function to a contained Err value,
54
+ * leaving an Ok value untouched.
55
+ */
56
+ mapErr(result, fn) {
57
+ if (!result.ok)
58
+ return Result.err(fn(result.error));
59
+ return result;
60
+ },
61
+ /**
62
+ * Calls op if the result is Ok, otherwise returns the Err value of self.
63
+ * This function can be used for control flow based on Result values.
64
+ */
65
+ andThen(result, op) {
66
+ if (result.ok)
67
+ return op(result.value);
68
+ return result;
69
+ },
70
+ /**
71
+ * Calls op if the result is Err, otherwise returns the Ok value of self.
72
+ */
73
+ orElse(result, op) {
74
+ if (!result.ok)
75
+ return op(result.error);
76
+ return result;
77
+ },
78
+ /**
79
+ * Pattern matches the Result and returns the result of the corresponding arm.
80
+ */
81
+ match(result, arms) {
82
+ if (result.ok)
83
+ return arms.ok(result.value);
84
+ return arms.err(result.error);
85
+ }
86
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,96 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { Result } from './result.js';
3
+ describe('Result', () => {
4
+ it('ok should create a successful result', () => {
5
+ const res = Result.ok(42);
6
+ expect(res).toEqual({ ok: true, value: 42 });
7
+ expect(Result.isOk(res)).toBe(true);
8
+ expect(Result.isErr(res)).toBe(false);
9
+ });
10
+ it('err should create a failed result', () => {
11
+ const res = Result.err('error');
12
+ expect(res).toEqual({ error: 'error', ok: false });
13
+ expect(Result.isOk(res)).toBe(false);
14
+ expect(Result.isErr(res)).toBe(true);
15
+ });
16
+ describe('unwrapOr', () => {
17
+ it('should return value for ok', () => {
18
+ expect(Result.unwrapOr(Result.ok(42), 0)).toBe(42);
19
+ });
20
+ it('should return default value for err', () => {
21
+ expect(Result.unwrapOr(Result.err('error'), 0)).toBe(0);
22
+ });
23
+ });
24
+ describe('unwrapOrElse', () => {
25
+ it('should return value for ok', () => {
26
+ expect(Result.unwrapOrElse(Result.ok(42), () => 0)).toBe(42);
27
+ });
28
+ it('should return computed value for err', () => {
29
+ expect(Result.unwrapOrElse(Result.err('error'), (e) => e.length)).toBe(5);
30
+ });
31
+ });
32
+ describe('map', () => {
33
+ it('should transform ok value', () => {
34
+ const res = Result.map(Result.ok(42), (v) => v * 2);
35
+ expect(res).toEqual(Result.ok(84));
36
+ });
37
+ it('should not transform err value', () => {
38
+ const res = Result.map(Result.err('error'), (v) => v * 2);
39
+ expect(res).toEqual(Result.err('error'));
40
+ });
41
+ });
42
+ describe('mapErr', () => {
43
+ it('should transform err value', () => {
44
+ const res = Result.mapErr(Result.err('error'), (e) => e.toUpperCase());
45
+ expect(res).toEqual(Result.err('ERROR'));
46
+ });
47
+ it('should not transform ok value', () => {
48
+ const res = Result.mapErr(Result.ok(42), (e) => e.toUpperCase());
49
+ expect(res).toEqual(Result.ok(42));
50
+ });
51
+ });
52
+ describe('andThen', () => {
53
+ it('should chain ok values', () => {
54
+ const res = Result.andThen(Result.ok(42), (v) => Result.ok(v * 2));
55
+ expect(res).toEqual(Result.ok(84));
56
+ });
57
+ it('should return first err', () => {
58
+ const res = Result.andThen(Result.err('error'), (v) => Result.ok(v * 2));
59
+ expect(res).toEqual(Result.err('error'));
60
+ });
61
+ it('should return nested err', () => {
62
+ const res = Result.andThen(Result.ok(42), () => Result.err('nested error'));
63
+ expect(res).toEqual(Result.err('nested error'));
64
+ });
65
+ });
66
+ describe('orElse', () => {
67
+ it('should chain err values', () => {
68
+ const res = Result.orElse(Result.err('error'), (e) => Result.err(e.toUpperCase()));
69
+ expect(res).toEqual(Result.err('ERROR'));
70
+ });
71
+ it('should return first ok', () => {
72
+ const res = Result.orElse(Result.ok(42), (e) => Result.err(e.toUpperCase()));
73
+ expect(res).toEqual(Result.ok(42));
74
+ });
75
+ it('should return nested ok', () => {
76
+ const res = Result.orElse(Result.err('error'), () => Result.ok(42));
77
+ expect(res).toEqual(Result.ok(42));
78
+ });
79
+ });
80
+ describe('match', () => {
81
+ it('should call ok branch', () => {
82
+ const res = Result.match(Result.ok(42), {
83
+ err: () => 0,
84
+ ok: (v) => v * 2
85
+ });
86
+ expect(res).toBe(84);
87
+ });
88
+ it('should call err branch', () => {
89
+ const res = Result.match(Result.err('error'), {
90
+ err: (e) => e.length,
91
+ ok: (v) => v * 2
92
+ });
93
+ expect(res).toBe(5);
94
+ });
95
+ });
96
+ });
@@ -0,0 +1,28 @@
1
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
2
+ import { type InvalidField, type RemoteCommand, type RemoteForm, type RemoteFormInput, type RemoteQueryFunction } from '@sveltejs/kit';
3
+ import { Result } from './functional/index.js';
4
+ export type CheckFunction = () => Promise<CheckResult>;
5
+ export type CheckResult = Result<void, ResponseError>;
6
+ export declare const CheckResult: {
7
+ ok(): {
8
+ ok: true;
9
+ value: void;
10
+ };
11
+ };
12
+ export type ResponseError = {
13
+ code: number;
14
+ message: string;
15
+ };
16
+ export type ResponseResult<T> = Result<T, ResponseError>;
17
+ export declare const ResponseResult: {
18
+ /**
19
+ * Unwraps a ResponseResult, throwing a sveltekit error if it is an error result.
20
+ */
21
+ unwrap<T>(result: ResponseResult<T>): T;
22
+ };
23
+ export declare function guardedCommand<Schema extends StandardSchemaV1, Output>(check: CheckFunction, schema: Schema, fn: (output: StandardSchemaV1.InferOutput<Schema>) => Promise<ResponseResult<Output>>): RemoteCommand<StandardSchemaV1.InferInput<Schema>, Promise<ResponseResult<Output>>>;
24
+ export declare function guardedCommandVoid<Output>(check: CheckFunction, fn: () => Promise<ResponseResult<Output>>): RemoteCommand<void, Promise<ResponseResult<Output>>>;
25
+ export declare function guardedForm<Schema extends StandardSchemaV1<RemoteFormInput, Record<string, unknown>>, Output>(schema: Schema, check: CheckFunction, fn: (output: StandardSchemaV1.InferOutput<Schema>, issue: InvalidField<StandardSchemaV1.InferInput<Schema>>) => Promise<Output>): RemoteForm<StandardSchemaV1.InferInput<Schema>, Output>;
26
+ export declare function guardedFormVoid<Output>(check: CheckFunction, fn: () => Promise<Output>): RemoteForm<void, Output>;
27
+ export declare function guardedQuery<Schema extends StandardSchemaV1, Output>(check: CheckFunction, schema: Schema, fn: (output: StandardSchemaV1.InferOutput<Schema>) => Promise<ResponseResult<Output>>): RemoteQueryFunction<StandardSchemaV1.InferInput<Schema>, ResponseResult<Output>>;
28
+ export declare function guardedQueryVoid<Output>(check: CheckFunction, fn: () => Promise<ResponseResult<Output>>): RemoteQueryFunction<void, ResponseResult<Output>>;
@@ -0,0 +1,74 @@
1
+ import { command, form, query } from '$app/server';
2
+ import { error } from '@sveltejs/kit';
3
+ import { Result } from './functional/index.js';
4
+ export const CheckResult = {
5
+ /* Helper function for ok check result. */
6
+ ok() {
7
+ return Result.ok(undefined);
8
+ }
9
+ };
10
+ export const ResponseResult = {
11
+ /**
12
+ * Unwraps a ResponseResult, throwing a sveltekit error if it is an error result.
13
+ */
14
+ unwrap(result) {
15
+ return Result.unwrapOrElse(result, (err) => error(err.code, err.message));
16
+ }
17
+ };
18
+ /* Guards command remote function with a check function. */
19
+ export function guardedCommand(check, schema, fn) {
20
+ return command(schema, async (output) => {
21
+ const outcome = await check();
22
+ // Command remote functions cannot redirect for error,
23
+ // so return result object.
24
+ if (!outcome.ok)
25
+ return Result.err(outcome.error);
26
+ return await fn(output);
27
+ });
28
+ }
29
+ /* Guards input-less command remote function with a check function. */
30
+ export function guardedCommandVoid(check, fn) {
31
+ return command(async () => {
32
+ const outcome = await check();
33
+ if (!outcome.ok)
34
+ return Result.err(outcome.error);
35
+ return await fn();
36
+ });
37
+ }
38
+ /* Guards form remote function with a check function. */
39
+ export function guardedForm(schema, check, fn) {
40
+ return form(schema, async (output, issue) => {
41
+ const outcome = await check();
42
+ // Use sveltekit error to play nicer form errors
43
+ if (!outcome.ok)
44
+ error(outcome.error.code, outcome.error.message);
45
+ return await fn(output, issue);
46
+ });
47
+ }
48
+ /* Guards input-less form remote function with a check function. */
49
+ export function guardedFormVoid(check, fn) {
50
+ return form(async () => {
51
+ const outcome = await check();
52
+ if (!outcome.ok)
53
+ error(outcome.error.code, outcome.error.message);
54
+ return await fn();
55
+ });
56
+ }
57
+ /* Guards query remote function with a check function. */
58
+ export function guardedQuery(check, schema, fn) {
59
+ return query(schema, async (output) => {
60
+ const outcome = await check();
61
+ if (!outcome.ok)
62
+ return Result.err(outcome.error);
63
+ return await fn(output);
64
+ });
65
+ }
66
+ /* Guards input-less query remote function with a check function. */
67
+ export function guardedQueryVoid(check, fn) {
68
+ return query(async () => {
69
+ const outcome = await check();
70
+ if (!outcome.ok)
71
+ return Result.err(outcome.error);
72
+ return await fn();
73
+ });
74
+ }
@@ -1,2 +1,3 @@
1
1
  export declare function fuzzySearch(needle: string, haystack: string): boolean;
2
2
  export declare function fuzzySearchHighlight(needle: string, haystack: string): null | string;
3
+ export declare function toTitleCase(str: string): string;
@@ -23,8 +23,14 @@ export function fuzzySearch(needle, haystack) {
23
23
  export function fuzzySearchHighlight(needle, haystack) {
24
24
  const hlen = haystack.length;
25
25
  const nlen = needle.length;
26
+ const escape = (text) => text
27
+ .replace(/&/g, '&amp;')
28
+ .replace(/</g, '&lt;')
29
+ .replace(/>/g, '&gt;')
30
+ .replace(/"/g, '&quot;')
31
+ .replace(/'/g, '&#39;');
26
32
  if (nlen === 0)
27
- return `<span>${haystack}</span>`;
33
+ return `<span>${escape(haystack)}</span>`;
28
34
  if (nlen > hlen)
29
35
  return null;
30
36
  let result = '';
@@ -37,7 +43,7 @@ export function fuzzySearchHighlight(needle, haystack) {
37
43
  const hch = haystack[j];
38
44
  if (hch.toLowerCase() === nch) {
39
45
  // match found → wrap in <b>
40
- result += `<b>${hch}</b>`;
46
+ result += `<b>${escape(hch)}</b>`;
41
47
  j++;
42
48
  found = true;
43
49
  matchedCount++;
@@ -45,7 +51,7 @@ export function fuzzySearchHighlight(needle, haystack) {
45
51
  }
46
52
  else {
47
53
  // non-match → normal text
48
- result += hch;
54
+ result += escape(hch);
49
55
  j++;
50
56
  }
51
57
  }
@@ -55,9 +61,12 @@ export function fuzzySearchHighlight(needle, haystack) {
55
61
  }
56
62
  }
57
63
  // add remaining unmatched characters
58
- result += haystack.slice(j);
64
+ result += escape(haystack.slice(j));
59
65
  // sanity check — must have matched all needle chars
60
66
  if (matchedCount < nlen)
61
67
  return null;
62
68
  return `<span>${result}</span>`;
63
69
  }
70
+ export function toTitleCase(str) {
71
+ return str.toLowerCase().replace(/\b\w/g, (char) => char.toUpperCase());
72
+ }
package/package.json CHANGED
@@ -4,16 +4,14 @@
4
4
  "access": "public",
5
5
  "provenance": true
6
6
  },
7
- "version": "1.2.2",
7
+ "version": "2.0.0",
8
8
  "license": "MIT",
9
9
  "repository": {
10
10
  "type": "git",
11
11
  "url": "git+https://github.com/Webamoki/Web-svelte.git"
12
12
  },
13
13
  "files": [
14
- "dist",
15
- "!dist/**/*.test.*",
16
- "!dist/**/*.spec.*"
14
+ "dist"
17
15
  ],
18
16
  "sideEffects": [
19
17
  "**/*.css"
@@ -21,48 +19,44 @@
21
19
  "type": "module",
22
20
  "exports": {
23
21
  "./components/form": {
24
- "types": "./dist/components/form/index.d.ts",
25
- "svelte": "./dist/components/form/index.js"
22
+ "types": "./dist/shared/components/form/index.d.ts",
23
+ "svelte": "./dist/shared/components/form/index.js"
26
24
  },
27
25
  "./components/ui": {
28
- "types": "./dist/components/ui/index.d.ts",
29
- "svelte": "./dist/components/ui/index.js"
26
+ "types": "./dist/shared/components/ui/index.d.ts",
27
+ "svelte": "./dist/shared/components/ui/index.js"
30
28
  },
31
29
  "./utils/datetime": {
32
- "types": "./dist/utils/datetime/index.d.ts",
33
- "import": "./dist/utils/datetime/index.js"
30
+ "types": "./dist/shared/utils/datetime/index.d.ts",
31
+ "import": "./dist/shared/utils/datetime/index.js"
34
32
  },
35
- "./utils/types/arktype": {
36
- "types": "./dist/utils/types/arktype.d.ts",
37
- "import": "./dist/utils/types/arktype.js"
38
- },
39
- "./utils/types/consts": {
40
- "types": "./dist/utils/types/consts.d.ts",
41
- "import": "./dist/utils/types/consts.js"
42
- },
43
- "./utils/types/db": {
44
- "types": "./dist/utils/types/db.d.ts",
45
- "import": "./dist/utils/types/db.js"
33
+ "./utils/arktype": {
34
+ "types": "./dist/shared/utils/arktype.d.ts",
35
+ "import": "./dist/shared/utils/arktype.js"
46
36
  },
47
37
  "./utils/form": {
48
- "types": "./dist/utils/form/index.d.ts",
49
- "import": "./dist/utils/form/index.js"
38
+ "types": "./dist/shared/utils/form/index.d.ts",
39
+ "import": "./dist/shared/utils/form/index.js"
50
40
  },
51
41
  "./utils/email": {
52
- "types": "./dist/utils/email/index.d.ts",
53
- "import": "./dist/utils/email/index.js"
42
+ "types": "./dist/shared/utils/email/index.d.ts",
43
+ "import": "./dist/shared/utils/email/index.js"
44
+ },
45
+ "./utils/functional": {
46
+ "types": "./dist/shared/utils/functional/index.d.ts",
47
+ "import": "./dist/shared/utils/functional/index.js"
54
48
  },
55
49
  "./server/form-handler": {
56
- "types": "./dist/server/form-handler.d.ts",
57
- "import": "./dist/server/form-handler.js"
50
+ "types": "./dist/shared/server/form-handler.d.ts",
51
+ "import": "./dist/shared/server/form-handler.js"
58
52
  },
59
53
  "./server/form-processor": {
60
- "types": "./dist/server/form-processor.d.ts",
61
- "import": "./dist/server/form-processor.js"
54
+ "types": "./dist/shared/server/form-processor.d.ts",
55
+ "import": "./dist/shared/server/form-processor.js"
62
56
  },
63
- "./utils/search": {
64
- "types": "./dist/utils/search.d.ts",
65
- "import": "./dist/utils/search.js"
57
+ "./utils/remote": {
58
+ "types": "./dist/shared/utils/remote.d.ts",
59
+ "import": "./dist/shared/utils/remote.js"
66
60
  }
67
61
  },
68
62
  "peerDependencies": {
@@ -80,6 +74,7 @@
80
74
  "@eslint/compat": "^2.0.3",
81
75
  "@eslint/js": "^9.39.4",
82
76
  "@internationalized/date": "^3.12.0",
77
+ "@standard-schema/spec": "^1.1.0",
83
78
  "@sveltejs/adapter-static": "^3.0.10",
84
79
  "@sveltejs/kit": "^2.55.0",
85
80
  "@sveltejs/package": "^2.5.7",
@@ -122,9 +117,9 @@
122
117
  ],
123
118
  "dependencies": {
124
119
  "@lucide/svelte": "^1.6.0",
120
+ "aws4fetch": "^1.0.20",
125
121
  "bits-ui": "^2.16.3",
126
122
  "devalue": "^5.6.4",
127
- "drizzle-orm": "^0.45.1",
128
123
  "formsnap": "^2.0.1",
129
124
  "ramda": "^0.32.0",
130
125
  "sorted-array-functions": "^1.3.0",
@@ -1,17 +0,0 @@
1
- /**
2
- * AWS Signature Version 4 signing utilities for SES API
3
- * Compatible with Cloudflare Workers and other edge runtimes
4
- */
5
- interface AwsCredentials {
6
- accessKeyId: string;
7
- region: string;
8
- secretAccessKey: string;
9
- }
10
- /**
11
- * Create AWS Signature V4 authorization header
12
- */
13
- export declare function signRequest(method: string, host: string, path: string, body: string, credentials: AwsCredentials, service?: string): Promise<{
14
- headers: Record<string, string>;
15
- signedHeaders: string;
16
- }>;
17
- export {};
@@ -1,83 +0,0 @@
1
- /**
2
- * AWS Signature Version 4 signing utilities for SES API
3
- * Compatible with Cloudflare Workers and other edge runtimes
4
- */
5
- /**
6
- * Create AWS Signature V4 authorization header
7
- */
8
- export async function signRequest(method, host, path, body, credentials, service = 'ses') {
9
- const { accessKeyId, region, secretAccessKey } = credentials;
10
- // Create timestamp
11
- const now = new Date();
12
- const amzDate = now.toISOString().replace(/[:-]|\.\d{3}/g, ''); // Format: YYYYMMDDTHHMMSSZ
13
- const dateStamp = amzDate.slice(0, 8); // Format: YYYYMMDD
14
- // Hash the request body
15
- const payloadHash = bufferToHex(await sha256(body));
16
- // Create canonical headers
17
- const canonicalHeaders = `host:${host}\n` + `x-amz-date:${amzDate}\n`;
18
- const signedHeaders = 'host;x-amz-date';
19
- // Create canonical request
20
- const canonicalRequest = `${method}\n` +
21
- `${path}\n` +
22
- `\n` + // Empty query string
23
- `${canonicalHeaders}\n` +
24
- `${signedHeaders}\n` +
25
- `${payloadHash}`;
26
- // Create string to sign
27
- const algorithm = 'AWS4-HMAC-SHA256';
28
- const credentialScope = `${dateStamp}/${region}/${service}/aws4_request`;
29
- const canonicalRequestHash = bufferToHex(await sha256(canonicalRequest));
30
- const stringToSign = `${algorithm}\n` + `${amzDate}\n` + `${credentialScope}\n` + `${canonicalRequestHash}`;
31
- // Calculate signature
32
- const signingKey = await getSignatureKey(secretAccessKey, dateStamp, region, service);
33
- const signature = bufferToHex(await hmacSha256(signingKey, stringToSign));
34
- // Create authorization header
35
- const authorizationHeader = `${algorithm} ` +
36
- `Credential=${accessKeyId}/${credentialScope}, ` +
37
- `SignedHeaders=${signedHeaders}, ` +
38
- `Signature=${signature}`;
39
- return {
40
- headers: {
41
- Authorization: authorizationHeader,
42
- 'Content-Type': 'application/x-www-form-urlencoded',
43
- 'X-Amz-Date': amzDate
44
- },
45
- signedHeaders
46
- };
47
- }
48
- /**
49
- * Convert ArrayBuffer to hex string
50
- */
51
- function bufferToHex(buffer) {
52
- return Array.from(new Uint8Array(buffer))
53
- .map((b) => b.toString(16).padStart(2, '0'))
54
- .join('');
55
- }
56
- /**
57
- * Get signing key for AWS Signature V4
58
- */
59
- async function getSignatureKey(key, dateStamp, regionName, serviceName) {
60
- const encoder = new TextEncoder();
61
- const kDate = await hmacSha256(encoder.encode('AWS4' + key), dateStamp);
62
- const kRegion = await hmacSha256(kDate, regionName);
63
- const kService = await hmacSha256(kRegion, serviceName);
64
- const kSigning = await hmacSha256(kService, 'aws4_request');
65
- return kSigning;
66
- }
67
- /**
68
- * Create a SHA-256 HMAC
69
- */
70
- async function hmacSha256(key, message) {
71
- const encoder = new TextEncoder();
72
- const keyData = key instanceof ArrayBuffer ? new Uint8Array(key) : key;
73
- const cryptoKey = await crypto.subtle.importKey('raw', keyData, { hash: 'SHA-256', name: 'HMAC' }, false, ['sign']);
74
- return await crypto.subtle.sign('HMAC', cryptoKey, encoder.encode(message));
75
- }
76
- /**
77
- * Create a SHA-256 hash
78
- */
79
- async function sha256(message) {
80
- const encoder = new TextEncoder();
81
- const data = encoder.encode(message);
82
- return await crypto.subtle.digest('SHA-256', data);
83
- }
@@ -1 +0,0 @@
1
- export declare function toTitleCase(str: string): string;
@@ -1,4 +0,0 @@
1
- /* --- String functions --- */
2
- export function toTitleCase(str) {
3
- return str.toLowerCase().replace(/\b\w/g, (char) => char.toUpperCase());
4
- }
@@ -1,92 +0,0 @@
1
- import { CalendarDate as calendarImport, Time as timeImport } from '@internationalized/date';
2
- import { type } from 'arktype';
3
- import { Days } from '../datetime/index.js';
4
- import { toTitleCase } from '../string.js';
5
- import { EMAIL_MAX, FIRST_NAME_MAX, LAST_NAME_MAX, NAME_MAX } from './consts.js';
6
- // Phone arktype: allows "+", digits, and spaces only
7
- export const Phone = type('string').narrow((value, ctx) => {
8
- if (!/^[+0-9 ]+$/.test(value)) {
9
- return ctx.reject({
10
- problem: 'invalid phone number. Only +, digits, and spaces are allowed.'
11
- });
12
- }
13
- return true;
14
- });
15
- /** Type string which is trimmed before narrowing the type checking */
16
- export function trimTo(typeTo) {
17
- return type('string.trim').to(typeTo);
18
- }
19
- export const Day = type.enumerated(...Days);
20
- export const Time = type.instanceOf(timeImport).configure({
21
- problem: () => 'invalid time'
22
- });
23
- export const CalendarDate = type.instanceOf(calendarImport).configure({
24
- problem: () => 'invalid date'
25
- });
26
- export const HexColor = type('string').narrow((value, ctx) => {
27
- // Regex explanation:
28
- // ^ : start of string
29
- // [0-9A-Fa-f]{3} : three hex digits (short form)
30
- // ([0-9A-Fa-f]{3})? : optional three more digits (long form #RRGGBB)
31
- // $ : end of string
32
- if (!/^[0-9A-Fa-f]{3}([0-9A-Fa-f]{3})?$/.test(value)) {
33
- return ctx.reject({
34
- problem: 'invalid hex color value'
35
- });
36
- }
37
- return true;
38
- });
39
- export const Email = trimTo(type('string.email')
40
- .configure({
41
- problem: () => 'invalid email address'
42
- })
43
- .atMostLength(EMAIL_MAX)).pipe((email) => email.toLowerCase());
44
- export const Name = trimTo(type.string.moreThanLength(0).atMostLength(NAME_MAX)).pipe(toTitleCase);
45
- export const FirstName = trimTo(type.string.moreThanLength(0).atMostLength(FIRST_NAME_MAX)).pipe(toTitleCase);
46
- export const LastName = trimTo(type.string.moreThanLength(0).atMostLength(LAST_NAME_MAX)).pipe(toTitleCase);
47
- export const Id = type('number >= 0');
48
- const passwordMaxLength = 20;
49
- const passwordMinLength = 8;
50
- const allowedChars = /^[A-Za-z0-9!@$#()]+$/;
51
- export const Password = type('string')
52
- .atLeastLength(passwordMinLength)
53
- .atMostLength(passwordMaxLength)
54
- .configure({
55
- problem: (ctx) => `Must be ${ctx.expected}`
56
- })
57
- .narrow((data, ctx) => {
58
- if (!allowedChars.test(data)) {
59
- return ctx.reject({
60
- problem: 'invalid characters. Only A–Z, a–z, 0–9, !@$#() are allowed.'
61
- });
62
- }
63
- if (!/[A-Z]/.test(data)) {
64
- return ctx.reject({
65
- problem: 'must contain at least one uppercase letter.'
66
- });
67
- }
68
- if (!/[a-z]/.test(data)) {
69
- return ctx.reject({
70
- problem: 'must contain at least one lowercase letter.'
71
- });
72
- }
73
- if (!/[0-9]/.test(data)) {
74
- return ctx.reject({
75
- problem: 'must contain at least one number.'
76
- });
77
- }
78
- if (!/[!@$#()]/.test(data)) {
79
- return ctx.reject({
80
- problem: 'must contain at least one special character (!@$#()).'
81
- });
82
- }
83
- return true;
84
- });
85
- export const Duration = type('number').narrow((value, ctx) => {
86
- if (!Number.isInteger(value) || value <= 0) {
87
- return ctx.reject({
88
- problem: 'invalid minutes'
89
- });
90
- }
91
- return true;
92
- });
@@ -1,5 +0,0 @@
1
- export declare const FIRST_NAME_MAX = 25;
2
- export declare const LAST_NAME_MAX = 25;
3
- export declare const NAME_MAX = 50;
4
- export declare const EMAIL_MAX = 100;
5
- export declare const HEX_COLOR_MAX = 6;
@@ -1,5 +0,0 @@
1
- export const FIRST_NAME_MAX = 25;
2
- export const LAST_NAME_MAX = 25;
3
- export const NAME_MAX = 50;
4
- export const EMAIL_MAX = 100;
5
- export const HEX_COLOR_MAX = 6;