@webamoki/web-svelte 0.4.2 → 0.5.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.
@@ -0,0 +1,62 @@
1
+ <script lang="ts" generics="S extends type.Any<Record<string, unknown>>">
2
+ import { toast } from 'svelte-sonner';
3
+ import { dateTransport } from '../../utils/index.js';
4
+ import { type } from 'arktype';
5
+ import type { Snippet } from 'svelte';
6
+ import { superForm, type SuperForm, type SuperValidated } from 'sveltekit-superforms';
7
+ import { arktypeClient } from 'sveltekit-superforms/adapters';
8
+ import type { SuperFormData } from 'sveltekit-superforms/client';
9
+
10
+ interface Props {
11
+ validated: SuperValidated<S['infer']> | S['infer'];
12
+ schema: S;
13
+ onSuccess?: (
14
+ form: Readonly<SuperValidated<S['infer'], App.Superforms.Message, S['infer']>>
15
+ ) => void;
16
+ invalidateAll?: boolean;
17
+ children: Snippet<
18
+ [{ form: SuperForm<S['infer'], App.Superforms.Message>; data: SuperFormData<S['infer']> }]
19
+ >;
20
+ // TODO: Enforce use of resolve
21
+ action: string;
22
+ actionName: string;
23
+ class: string;
24
+ }
25
+
26
+ let {
27
+ validated,
28
+ schema,
29
+ onSuccess,
30
+ invalidateAll = false,
31
+ children,
32
+ action: _action,
33
+ actionName,
34
+ class: className
35
+ }: Props = $props();
36
+
37
+ const form = superForm(validated, {
38
+ validators: arktypeClient(schema),
39
+ dataType: 'json',
40
+ invalidateAll,
41
+ transport: dateTransport,
42
+ onUpdated({ form }) {
43
+ const text = form.message?.text;
44
+ if (text === undefined) return;
45
+
46
+ if (form.message?.success) {
47
+ toast.success(text);
48
+ } else {
49
+ toast.error(text);
50
+ }
51
+
52
+ if (form.valid) {
53
+ onSuccess?.(form);
54
+ }
55
+ }
56
+ });
57
+ const data = form.form;
58
+ </script>
59
+
60
+ <form class={className} action="{_action}?/{actionName}" method="POST" use:form.enhance>
61
+ {@render children({ form, data })}
62
+ </form>
@@ -0,0 +1,40 @@
1
+ import { type } from 'arktype';
2
+ import type { Snippet } from 'svelte';
3
+ import { type SuperForm, type SuperValidated } from 'sveltekit-superforms';
4
+ import type { SuperFormData } from 'sveltekit-superforms/client';
5
+ declare function $$render<S extends type.Any<Record<string, unknown>>>(): {
6
+ props: {
7
+ validated: SuperValidated<S["infer"]> | S["infer"];
8
+ schema: S;
9
+ onSuccess?: (form: Readonly<SuperValidated<S["infer"], App.Superforms.Message, S["infer"]>>) => void;
10
+ invalidateAll?: boolean;
11
+ children: Snippet<[{
12
+ form: SuperForm<S["infer"], App.Superforms.Message>;
13
+ data: SuperFormData<S["infer"]>;
14
+ }]>;
15
+ action: string;
16
+ actionName: string;
17
+ class: string;
18
+ };
19
+ exports: {};
20
+ bindings: "";
21
+ slots: {};
22
+ events: {};
23
+ };
24
+ declare class __sveltets_Render<S extends type.Any<Record<string, unknown>>> {
25
+ props(): ReturnType<typeof $$render<S>>['props'];
26
+ events(): ReturnType<typeof $$render<S>>['events'];
27
+ slots(): ReturnType<typeof $$render<S>>['slots'];
28
+ bindings(): "";
29
+ exports(): {};
30
+ }
31
+ interface $$IsomorphicComponent {
32
+ new <S extends type.Any<Record<string, unknown>>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<S>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<S>['props']>, ReturnType<__sveltets_Render<S>['events']>, ReturnType<__sveltets_Render<S>['slots']>> & {
33
+ $$bindings?: ReturnType<__sveltets_Render<S>['bindings']>;
34
+ } & ReturnType<__sveltets_Render<S>['exports']>;
35
+ <S extends type.Any<Record<string, unknown>>>(internal: unknown, props: ReturnType<__sveltets_Render<S>['props']> & {}): ReturnType<__sveltets_Render<S>['exports']>;
36
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
37
+ }
38
+ declare const Form: $$IsomorphicComponent;
39
+ type Form<S extends type.Any<Record<string, unknown>>> = InstanceType<typeof Form<S>>;
40
+ export default Form;
@@ -6,10 +6,18 @@
6
6
  value?: string;
7
7
  }
8
8
  let { value = $bindable(), ...fieldProps }: Props = $props();
9
+
10
+ function get() {
11
+ return `#${value}`;
12
+ }
13
+ function set(raw: string | undefined) {
14
+ if (raw === undefined) return undefined;
15
+ return raw.slice(1); // remove #
16
+ }
9
17
  </script>
10
18
 
11
19
  <FieldWrapper {...fieldProps}>
12
20
  {#snippet formElem()}
13
- <ColorPicker bind:hex={value} label={value} isAlpha={false} position="responsive" />
21
+ <ColorPicker bind:hex={get, set} label={value} isAlpha={false} position="responsive" />
14
22
  {/snippet}
15
23
  </FieldWrapper>
@@ -1,7 +1,7 @@
1
1
  import { CalendarDate, Time, ZonedDateTime, type DateDuration } from '@internationalized/date';
2
+ import type { Transport } from '@sveltejs/kit';
3
+ import type { Day } from '../types/arktype.js';
2
4
  export declare const Days: readonly ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
3
- export type Day = (typeof Days)[number];
4
- export declare const Day: import("arktype/internal/methods/string.ts").StringType<"Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday" | "Sunday", {}>;
5
5
  /**
6
6
  * Gets the day of the week for a given date.
7
7
  * @param date - The date to get the day of the week for.
@@ -166,3 +166,4 @@ export declare function unfreezeDate(raw: $state.Snapshot<CalendarDate>): Calend
166
166
  * @returns The unfrozen Time object.
167
167
  */
168
168
  export declare function unfreezeTime(raw: $state.Snapshot<Time>): Time;
169
+ export declare const dateTransport: Transport;
@@ -1,5 +1,4 @@
1
- import { CalendarDate, DateFormatter, getDayOfWeek, getLocalTimeZone, startOfMonth, Time, toCalendarDate, today, toTime, ZonedDateTime } from '@internationalized/date';
2
- import { type } from 'arktype';
1
+ import { CalendarDate, DateFormatter, fromDate, getDayOfWeek, getLocalTimeZone, startOfMonth, Time, toCalendarDate, today, toTime, ZonedDateTime } from '@internationalized/date';
3
2
  import { map, range } from 'ramda';
4
3
  const DEFAULT_TIME_ZONE = 'Europe/London';
5
4
  const DEFAULT_LOCALE = 'en-GB';
@@ -13,7 +12,6 @@ export const Days = [
13
12
  'Saturday',
14
13
  'Sunday'
15
14
  ];
16
- export const Day = type.enumerated(...Days);
17
15
  const DayIndex = Object.fromEntries(Days.map((day, index) => [day, index]));
18
16
  /** Gets the day index of the date */
19
17
  function getDayIndex(date) {
@@ -320,3 +318,21 @@ export function unfreezeDate(raw) {
320
318
  export function unfreezeTime(raw) {
321
319
  return new Time(raw.hour, raw.minute, raw.second, raw.millisecond);
322
320
  }
321
+ // SerDe
322
+ export const dateTransport = {
323
+ Time: {
324
+ encode: (t) => t instanceof Time && [t.hour, t.minute, t.second, t.millisecond],
325
+ decode: ([hour, minute, second, millisecond]) => new Time(hour, minute, second, millisecond)
326
+ },
327
+ CalendarDate: {
328
+ encode: (d) => d instanceof CalendarDate && [d.year, d.month, d.day],
329
+ decode: ([year, month, day]) => new CalendarDate(year, month, day)
330
+ },
331
+ ZonedDateTime: {
332
+ encode: (value) => value instanceof ZonedDateTime && [value.toAbsoluteString(), value.timeZone],
333
+ decode: ([absoluteString, timezone]) => {
334
+ const nativeDate = new Date(absoluteString);
335
+ return fromDate(nativeDate, timezone);
336
+ }
337
+ }
338
+ };
@@ -1 +1,5 @@
1
1
  export * from './datetime/index.js';
2
+ export * from './types/arktype.js';
3
+ export * from './types/db.js';
4
+ export * from './types/consts.js';
5
+ export * from './string.js';
@@ -1 +1,5 @@
1
1
  export * from './datetime/index.js';
2
+ export * from './types/arktype.js';
3
+ export * from './types/db.js';
4
+ export * from './types/consts.js';
5
+ export * from './string.js';
@@ -0,0 +1 @@
1
+ export declare function toTitleCase(str: string): string;
@@ -0,0 +1,4 @@
1
+ /* --- String functions --- */
2
+ export function toTitleCase(str) {
3
+ return str.toLowerCase().replace(/\b\w/g, (char) => char.toUpperCase());
4
+ }
@@ -0,0 +1,16 @@
1
+ import { Type } from 'arktype';
2
+ import { Days } from '../datetime/index.js';
3
+ import { Time as timeImport, CalendarDate as calendarImport } from '@internationalized/date';
4
+ /** Type string which is trimmed before narrowing the type checking */
5
+ export declare function trimTo(typeTo: Type<string>): import("arktype/internal/methods/object.ts").ObjectType<(In: string) => import("arktype/internal/attributes.ts").To<string>, {}>;
6
+ export declare const Day: import("arktype/internal/methods/string.ts").StringType<"Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday" | "Sunday", {}>;
7
+ export type Day = (typeof Days)[number];
8
+ export declare const Time: import("arktype/internal/methods/object.ts").ObjectType<timeImport, {}>;
9
+ export declare const CalendarDate: import("arktype/internal/methods/object.ts").ObjectType<calendarImport, {}>;
10
+ export declare const HexColor: import("arktype/internal/methods/string.ts").StringType<string, {}>;
11
+ export declare const Email: import("arktype/internal/methods/object.ts").ObjectType<(In: string) => import("arktype").Out<string>, {}>;
12
+ export declare const FirstName: import("arktype/internal/methods/object.ts").ObjectType<(In: string) => import("arktype").Out<string>, {}>;
13
+ export declare const LastName: import("arktype/internal/methods/object.ts").ObjectType<(In: string) => import("arktype").Out<string>, {}>;
14
+ export declare const Id: import("arktype/internal/methods/number.ts").NumberType<number, {}>;
15
+ export declare const PasswordType: import("arktype/internal/methods/string.ts").StringType<string, {}>;
16
+ export declare const Duration: import("arktype/internal/methods/number.ts").NumberType<number, {}>;
@@ -0,0 +1,82 @@
1
+ import { Type, type } from 'arktype';
2
+ import { Days } from '../datetime/index.js';
3
+ import { Time as timeImport, CalendarDate as calendarImport } from '@internationalized/date';
4
+ import { EMAIL_MAX, FIRST_NAME_MAX, LAST_NAME_MAX } from './consts.js';
5
+ import { toTitleCase } from '../string.js';
6
+ /** Type string which is trimmed before narrowing the type checking */
7
+ export function trimTo(typeTo) {
8
+ return type('string.trim').to(typeTo);
9
+ }
10
+ export const Day = type.enumerated(...Days);
11
+ export const Time = type.instanceOf(timeImport).configure({
12
+ problem: () => 'invalid time'
13
+ });
14
+ export const CalendarDate = type.instanceOf(calendarImport).configure({
15
+ problem: () => 'invalid date'
16
+ });
17
+ export const HexColor = type('string').narrow((value, ctx) => {
18
+ // Regex explanation:
19
+ // ^ : start of string
20
+ // [0-9A-Fa-f]{3} : three hex digits (short form)
21
+ // ([0-9A-Fa-f]{3})? : optional three more digits (long form #RRGGBB)
22
+ // $ : end of string
23
+ if (!/^[0-9A-Fa-f]{3}([0-9A-Fa-f]{3})?$/.test(value)) {
24
+ return ctx.reject({
25
+ problem: 'invalid hex color value'
26
+ });
27
+ }
28
+ return true;
29
+ });
30
+ export const Email = trimTo(type('string.email')
31
+ .configure({
32
+ problem: () => 'invalid email address'
33
+ })
34
+ .atMostLength(EMAIL_MAX)).pipe((email) => email.toLowerCase());
35
+ export const FirstName = trimTo(type.string.moreThanLength(0).atMostLength(FIRST_NAME_MAX)).pipe(toTitleCase);
36
+ export const LastName = trimTo(type.string.moreThanLength(0).atMostLength(LAST_NAME_MAX)).pipe(toTitleCase);
37
+ export const Id = type('number >= 0');
38
+ const passwordMaxLength = 20;
39
+ const passwordMinLength = 8;
40
+ const allowedChars = /^[A-Za-z0-9!@$#()]+$/;
41
+ export const PasswordType = type('string')
42
+ .atLeastLength(passwordMinLength)
43
+ .atMostLength(passwordMaxLength)
44
+ .configure({
45
+ problem: (ctx) => `Must be ${ctx.expected}`
46
+ })
47
+ .narrow((data, ctx) => {
48
+ if (!allowedChars.test(data)) {
49
+ return ctx.reject({
50
+ problem: 'invalid characters. Only A–Z, a–z, 0–9, !@$#() are allowed.'
51
+ });
52
+ }
53
+ if (!/[A-Z]/.test(data)) {
54
+ return ctx.reject({
55
+ problem: 'must contain at least one uppercase letter.'
56
+ });
57
+ }
58
+ if (!/[a-z]/.test(data)) {
59
+ return ctx.reject({
60
+ problem: 'must contain at least one lowercase letter.'
61
+ });
62
+ }
63
+ if (!/[0-9]/.test(data)) {
64
+ return ctx.reject({
65
+ problem: 'must contain at least one number.'
66
+ });
67
+ }
68
+ if (!/[!@$#()]/.test(data)) {
69
+ return ctx.reject({
70
+ problem: 'must contain at least one special character (!@$#()).'
71
+ });
72
+ }
73
+ return true;
74
+ });
75
+ export const Duration = type('number').narrow((value, ctx) => {
76
+ if (!Number.isInteger(value) || value <= 0) {
77
+ return ctx.reject({
78
+ problem: 'invalid minutes'
79
+ });
80
+ }
81
+ return true;
82
+ });
@@ -0,0 +1,5 @@
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;
@@ -0,0 +1,5 @@
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;
@@ -0,0 +1,56 @@
1
+ import { CalendarDate, type Time } from '@internationalized/date';
2
+ export declare function stringEnum<T extends string>(): import("drizzle-orm/column-builder", { with: { "resolution-mode": "require" } }).$Type<import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgVarcharBuilderInitial<"", [string, ...string[]], 50>, T>;
3
+ export declare const name: () => import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgVarcharBuilderInitial<"", [string, ...string[]], 50>;
4
+ export declare const hexColor: () => import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgVarcharBuilderInitial<"", [string, ...string[]], 6>;
5
+ export declare const time: {
6
+ (): import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgCustomColumnBuilder<{
7
+ name: "";
8
+ dataType: "custom";
9
+ columnType: "PgCustomColumn";
10
+ data: Time;
11
+ driverParam: string;
12
+ enumValues: undefined;
13
+ }>;
14
+ <TConfig extends Record<string, any>>(fieldConfig?: TConfig | undefined): import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgCustomColumnBuilder<{
15
+ name: "";
16
+ dataType: "custom";
17
+ columnType: "PgCustomColumn";
18
+ data: Time;
19
+ driverParam: string;
20
+ enumValues: undefined;
21
+ }>;
22
+ <TName extends string>(dbName: TName, fieldConfig?: unknown): import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgCustomColumnBuilder<{
23
+ name: TName;
24
+ dataType: "custom";
25
+ columnType: "PgCustomColumn";
26
+ data: Time;
27
+ driverParam: string;
28
+ enumValues: undefined;
29
+ }>;
30
+ };
31
+ export declare const date: {
32
+ (): import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgCustomColumnBuilder<{
33
+ name: "";
34
+ dataType: "custom";
35
+ columnType: "PgCustomColumn";
36
+ data: CalendarDate;
37
+ driverParam: string;
38
+ enumValues: undefined;
39
+ }>;
40
+ <TConfig extends Record<string, any>>(fieldConfig?: TConfig | undefined): import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgCustomColumnBuilder<{
41
+ name: "";
42
+ dataType: "custom";
43
+ columnType: "PgCustomColumn";
44
+ data: CalendarDate;
45
+ driverParam: string;
46
+ enumValues: undefined;
47
+ }>;
48
+ <TName extends string>(dbName: TName, fieldConfig?: unknown): import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgCustomColumnBuilder<{
49
+ name: TName;
50
+ dataType: "custom";
51
+ columnType: "PgCustomColumn";
52
+ data: CalendarDate;
53
+ driverParam: string;
54
+ enumValues: undefined;
55
+ }>;
56
+ };
@@ -0,0 +1,34 @@
1
+ import { customType, varchar } from 'drizzle-orm/pg-core';
2
+ import { HEX_COLOR_MAX, NAME_MAX } from './consts.js';
3
+ import { CalendarDate, parseDate, parseTime } from '@internationalized/date';
4
+ // General
5
+ const STRING_ENUM_MAX = 50;
6
+ export function stringEnum() {
7
+ return varchar({ length: STRING_ENUM_MAX }).$type();
8
+ }
9
+ export const name = () => varchar({ length: NAME_MAX });
10
+ export const hexColor = () => varchar({ length: HEX_COLOR_MAX });
11
+ // Date Time
12
+ // TODO: Absolute date time
13
+ export const time = customType({
14
+ dataType() {
15
+ return 'time(0)';
16
+ },
17
+ toDriver(time) {
18
+ return time.toString();
19
+ },
20
+ fromDriver(value) {
21
+ return parseTime(value);
22
+ }
23
+ });
24
+ export const date = customType({
25
+ dataType() {
26
+ return 'date';
27
+ },
28
+ toDriver(date) {
29
+ return date.toString();
30
+ },
31
+ fromDriver(value) {
32
+ return parseDate(value);
33
+ }
34
+ });
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.4.2",
6
+ "version": "0.5.0",
7
7
  "license": "MIT",
8
8
  "files": [
9
9
  "dist",
@@ -78,10 +78,14 @@
78
78
  "svelte"
79
79
  ],
80
80
  "dependencies": {
81
+ "dotenv": "^17.2.3",
82
+ "drizzle-orm": "^0.44.5",
81
83
  "formsnap": "^2.0.1",
84
+ "pg": "^8.16.3",
82
85
  "ramda": "^0.31.3",
83
86
  "sorted-array-functions": "^1.3.0",
84
- "svelte-awesome-color-picker": "^4.0.2"
87
+ "svelte-awesome-color-picker": "^4.0.2",
88
+ "svelte-sonner": "^1.0.5"
85
89
  },
86
90
  "scripts": {
87
91
  "dev": "vite dev",