@skalfa/skalfa-app-core 1.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 (41) hide show
  1. package/dist/api.util.d.ts +49 -0
  2. package/dist/api.util.js +125 -0
  3. package/dist/auth.util.d.ts +10 -0
  4. package/dist/auth.util.js +37 -0
  5. package/dist/cavity.util.d.ts +20 -0
  6. package/dist/cavity.util.js +124 -0
  7. package/dist/cn.util.d.ts +3 -0
  8. package/dist/cn.util.js +45 -0
  9. package/dist/commands/barrels.d.ts +1 -0
  10. package/dist/commands/barrels.js +22 -0
  11. package/dist/commands/blueprint.d.ts +3 -0
  12. package/dist/commands/blueprint.js +306 -0
  13. package/dist/commands/light.d.ts +1 -0
  14. package/dist/commands/light.js +16 -0
  15. package/dist/commands/logger.d.ts +10 -0
  16. package/dist/commands/logger.js +36 -0
  17. package/dist/commands/use-pdf.d.ts +1 -0
  18. package/dist/commands/use-pdf.js +17 -0
  19. package/dist/conversion.util.d.ts +10 -0
  20. package/dist/conversion.util.js +53 -0
  21. package/dist/encryption.util.d.ts +6 -0
  22. package/dist/encryption.util.js +56 -0
  23. package/dist/form.util.d.ts +87 -0
  24. package/dist/form.util.js +294 -0
  25. package/dist/index.d.ts +27 -0
  26. package/dist/index.js +79 -0
  27. package/dist/langs/index.d.ts +1 -0
  28. package/dist/langs/index.js +1 -0
  29. package/dist/langs/validation.langs.d.ts +17 -0
  30. package/dist/langs/validation.langs.js +17 -0
  31. package/dist/registry/index.d.ts +19 -0
  32. package/dist/registry/index.js +10 -0
  33. package/dist/resource.util.d.ts +36 -0
  34. package/dist/resource.util.js +81 -0
  35. package/dist/shortcut.util.d.ts +12 -0
  36. package/dist/shortcut.util.js +22 -0
  37. package/dist/table.util.d.ts +51 -0
  38. package/dist/table.util.js +140 -0
  39. package/dist/validation.util.d.ts +18 -0
  40. package/dist/validation.util.js +150 -0
  41. package/package.json +47 -0
@@ -0,0 +1,49 @@
1
+ import { AxiosResponse } from "axios";
2
+ export declare const authBearer: (bearer?: string) => string | null;
3
+ export type ApiFilterType = {
4
+ /** Use filter logic with: "and" / "or". */
5
+ logic?: "and" | "or";
6
+ /** Use filter type with: "eq" = Equal, "ne" = Not Equal, "in" = In, "ni" = Not In, "bw" = Between. */
7
+ type?: "eq" | "ne" | "in" | "ni" | "bw" | "";
8
+ column?: string;
9
+ value?: string | number | number[] | string[] | null;
10
+ };
11
+ export type ApiParamsType = {
12
+ page?: number;
13
+ paginate?: number;
14
+ sort?: string[];
15
+ search?: string;
16
+ searchable?: string[];
17
+ selectable?: string[];
18
+ expand?: string[];
19
+ selectableOption?: string[];
20
+ filter?: ApiFilterType[];
21
+ };
22
+ export type ApiType = {
23
+ path?: string;
24
+ url?: string;
25
+ method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
26
+ params?: ApiParamsType;
27
+ payload?: any;
28
+ includeParams?: Record<string, any>;
29
+ headers?: Record<string, any>;
30
+ bearer?: string;
31
+ };
32
+ export declare const ApiFilterValue: {
33
+ eq: string;
34
+ ne: string;
35
+ in: string;
36
+ ni: string;
37
+ bw: string;
38
+ };
39
+ export declare const api: ({ path, url, method, params, payload, includeParams, headers, bearer, }: ApiType) => Promise<AxiosResponse<any, any, {}>>;
40
+ export declare const useGetApi: (props: ApiType & {
41
+ method?: "GET";
42
+ cacheName?: string;
43
+ expired?: number;
44
+ }, sleep?: boolean) => {
45
+ loading: boolean;
46
+ code: number | null;
47
+ data: any;
48
+ reset: () => Promise<"" | undefined>;
49
+ };
@@ -0,0 +1,125 @@
1
+ "use client";
2
+ import { useEffect, useState } from "react";
3
+ import axios from "axios";
4
+ import { redirect } from "next/navigation";
5
+ import { auth } from "./auth.util";
6
+ import { cavity } from "./cavity.util";
7
+ // =========================>
8
+ // ## Build auth bearer
9
+ // =========================>
10
+ export const authBearer = (bearer) => {
11
+ const token = bearer || auth.getAccessToken() || null;
12
+ return token ? `Bearer ${token}` : null;
13
+ };
14
+ // =========================>
15
+ // ## Api error handler
16
+ // =========================>
17
+ const handleErrors = (fetch) => {
18
+ if (fetch?.status === 401)
19
+ redirect(auth.PATH_LOGIN);
20
+ if (fetch?.status === 403)
21
+ redirect(auth.PATH_BASE);
22
+ return fetch;
23
+ };
24
+ // =========================>
25
+ // ## Api filter value
26
+ // =========================>
27
+ export const ApiFilterValue = {
28
+ eq: "eq",
29
+ ne: "ne",
30
+ in: "in",
31
+ ni: "ni",
32
+ bw: "bw",
33
+ };
34
+ // =========================>
35
+ // ## Api fetching handler
36
+ // =========================>
37
+ export const api = async ({ path, url, method, params, payload, includeParams, headers, bearer, }) => {
38
+ const fetchUrl = url || `${process.env.NEXT_PUBLIC_API_HOST}/${path || ""}`;
39
+ const buildHeaders = { Authorization: authBearer(bearer) || "", ...headers };
40
+ buildHeaders["Content-Type"] = buildHeaders["Content-Type"] || "multipart/form-data";
41
+ const filter = {};
42
+ const jsonParams = {};
43
+ if (params?.filter) {
44
+ params?.filter?.map((val) => {
45
+ filter[val.column] = `${ApiFilterValue[val.type]}:${Array.isArray(val.value) ? val.value.join(",") : val.value}`;
46
+ });
47
+ }
48
+ if (params) {
49
+ const normalizeToJson = ["sort", "searchable", "selectable", "selectableOption", "expand"];
50
+ normalizeToJson.forEach((key) => {
51
+ const k = key;
52
+ if (Array.isArray(params[k])) {
53
+ jsonParams[k] = JSON.stringify(params[k]);
54
+ }
55
+ });
56
+ }
57
+ return await axios(fetchUrl, {
58
+ method: method || "GET",
59
+ headers: buildHeaders,
60
+ data: payload,
61
+ params: {
62
+ ...params,
63
+ ...jsonParams,
64
+ ...(params?.filter ? { filter: JSON.stringify(filter) } : {}),
65
+ ...includeParams,
66
+ },
67
+ })
68
+ .then((res) => res)
69
+ .catch((err) => handleErrors(err.response));
70
+ };
71
+ // =========================>
72
+ // ## Hook of get api
73
+ // =========================>
74
+ export const useGetApi = (props, sleep) => {
75
+ const [loading, setLoading] = useState(true);
76
+ const [code, setCode] = useState(null);
77
+ const [data, setData] = useState(null);
78
+ const fetch = async (revalidation = false) => {
79
+ setLoading(true);
80
+ // =========================>
81
+ // ## When cache ready
82
+ // =========================>
83
+ const cacheData = props.expired && !revalidation ? await cavity.get(props.cacheName || `fetch_${props?.path}`) : null;
84
+ if (cacheData) {
85
+ setLoading(false);
86
+ setCode(200);
87
+ setData(cacheData);
88
+ return "";
89
+ }
90
+ // =========================>
91
+ // ## Fetch from api
92
+ // =========================>
93
+ const response = await api(props);
94
+ if (response?.status) {
95
+ setLoading(false);
96
+ setCode(response?.status);
97
+ setData(response?.data);
98
+ // =========================>
99
+ // ## Save to cache
100
+ // =========================>
101
+ if (props.expired)
102
+ cavity.set({ key: props?.cacheName || `fetch_${props?.path}`, data: response?.data, expired: props.expired });
103
+ }
104
+ };
105
+ useEffect(() => {
106
+ if (!sleep && (props.path || props.url))
107
+ fetch();
108
+ }, [
109
+ props.path,
110
+ props.url,
111
+ props.params?.paginate,
112
+ props.params?.page,
113
+ props.params?.search,
114
+ props.params?.sort,
115
+ props.params?.filter,
116
+ props.params?.selectable,
117
+ props.params?.selectableOption,
118
+ props.includeParams,
119
+ props.headers,
120
+ props.bearer,
121
+ sleep
122
+ ]);
123
+ const reset = () => fetch(true);
124
+ return { loading, code, data, reset };
125
+ };
@@ -0,0 +1,10 @@
1
+ export declare const auth: {
2
+ PATH_LOGIN: string;
3
+ PATH_BASE: string;
4
+ ACCESS_TOKEN_EXPIRED: number;
5
+ ACCESS_TOKEN_NAME: string;
6
+ setAccessToken: (token: string | null, expired?: number) => string | undefined;
7
+ getAccessToken: () => any;
8
+ deleteAccessToken: () => void;
9
+ check: () => false;
10
+ };
@@ -0,0 +1,37 @@
1
+ import { redirect } from 'next/navigation';
2
+ import Cookies from 'js-cookie';
3
+ import { encryption } from './encryption.util';
4
+ export const auth = {
5
+ // ==============================>
6
+ // ## Path of login page
7
+ // ==============================>
8
+ PATH_LOGIN: '/auth/login',
9
+ // ==============================>
10
+ // ## Path of home page
11
+ // ==============================>
12
+ PATH_BASE: '/',
13
+ // ==============================>
14
+ // ## Access token expired (days)
15
+ // ==============================>
16
+ ACCESS_TOKEN_EXPIRED: 7,
17
+ // ==============================>
18
+ // ## Name of cookie access token
19
+ // ==============================>
20
+ ACCESS_TOKEN_NAME: String(process.env.NEXT_PUBLIC_APP_NAME || '').toLowerCase().trim().replace(/[^\w\s-]/g, '').replace(/[\s_-]+/g, '-').replace(/^-+|-+$/g, '') + '.user.token',
21
+ // ==============================>
22
+ // ## set access token to cookie
23
+ // ==============================>
24
+ setAccessToken: (token, expired) => Cookies.set(auth.ACCESS_TOKEN_NAME, token ? encryption.set(token) : "", { expires: expired || auth.ACCESS_TOKEN_EXPIRED, secure: true }),
25
+ // ==============================>
26
+ // ## get access token from cookie
27
+ // ==============================>
28
+ getAccessToken: () => encryption.get(Cookies.get(auth.ACCESS_TOKEN_NAME) || ""),
29
+ // ==============================>
30
+ // ## delete access token from cookie
31
+ // ==============================>
32
+ deleteAccessToken: () => Cookies.remove(auth.ACCESS_TOKEN_NAME),
33
+ // ==============================>
34
+ // ## Check auth
35
+ // ==============================>
36
+ check: () => (!Cookies.get(auth.ACCESS_TOKEN_NAME)) && redirect(auth.PATH_LOGIN),
37
+ };
@@ -0,0 +1,20 @@
1
+ type CavityType = {
2
+ key: string;
3
+ data: any;
4
+ expired: number;
5
+ };
6
+ export declare const cavity: {
7
+ set: ({ key, data, expired }: CavityType) => Promise<() => void>;
8
+ get: (key: string) => Promise<{
9
+ message: string;
10
+ data: Record<string, any>;
11
+ }>;
12
+ delete: (key: string) => Promise<() => void>;
13
+ socket: {
14
+ register: () => void;
15
+ subscribe(key: string): void;
16
+ unsubscribe(key: string): void;
17
+ invalidate(key: string): void;
18
+ };
19
+ };
20
+ export {};
@@ -0,0 +1,124 @@
1
+ import { registry } from "./registry";
2
+ import { logger } from "./commands/logger";
3
+ const name = String(process.env.NEXT_PUBLIC_APP_NAME || "").toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "") + ".cavity";
4
+ const storeName = "cache";
5
+ const version = 1;
6
+ const subscriptions = new Set();
7
+ let registered = false;
8
+ function getSocket() {
9
+ const s = registry.get("socket");
10
+ if (s && process.env.NEXT_PUBLIC_SOCKET_URL) {
11
+ return s.connect();
12
+ }
13
+ return null;
14
+ }
15
+ // ==============================>
16
+ // ## Init indexDb
17
+ // ==============================>
18
+ async function idb() {
19
+ return new Promise((resolve, reject) => {
20
+ const request = indexedDB.open(name, version);
21
+ request.onupgradeneeded = () => {
22
+ const db = request.result;
23
+ if (!db.objectStoreNames.contains(storeName)) {
24
+ db.createObjectStore(storeName, { keyPath: "key" });
25
+ }
26
+ };
27
+ request.onsuccess = () => resolve(request.result);
28
+ request.onerror = () => reject(request.error);
29
+ });
30
+ }
31
+ export const cavity = {
32
+ // ==============================>
33
+ // ## Set cache to indexDb
34
+ // ==============================>
35
+ set: async ({ key, data, expired }) => {
36
+ const db = await idb();
37
+ const tx = db.transaction(storeName, "readwrite");
38
+ const store = tx.objectStore(storeName);
39
+ const item = {
40
+ key,
41
+ expired: new Date().getTime() + expired * 60 * 1000,
42
+ data,
43
+ };
44
+ store.put(item);
45
+ return tx.commit;
46
+ },
47
+ // ==============================>
48
+ // ## Get cache from indexDb
49
+ // ==============================>
50
+ get: async (key) => {
51
+ const db = await idb();
52
+ const tx = db.transaction(storeName, "readonly");
53
+ const store = tx.objectStore(storeName);
54
+ return new Promise((resolve) => {
55
+ const request = store.get(key);
56
+ request.onsuccess = () => {
57
+ const item = request.result;
58
+ if (!item)
59
+ return resolve({ message: "Record not found!", data: [] });
60
+ if (item.expired > Date.now()) {
61
+ resolve(item.data);
62
+ }
63
+ else {
64
+ const deleteTx = db.transaction(storeName, "readwrite");
65
+ deleteTx.objectStore(storeName).delete(key);
66
+ resolve({ message: "Record not found!", data: [] });
67
+ }
68
+ };
69
+ request.onerror = () => resolve({ message: "Error!", data: [] });
70
+ });
71
+ },
72
+ // ==============================>
73
+ // ## Remove cache from indexDb
74
+ // ==============================>
75
+ delete: async (key) => {
76
+ const db = await idb();
77
+ const tx = db.transaction(storeName, "readwrite");
78
+ const store = tx.objectStore(storeName);
79
+ store.delete(key);
80
+ return tx.commit;
81
+ },
82
+ socket: {
83
+ register: () => {
84
+ const socket = getSocket();
85
+ if (registered || !socket)
86
+ return;
87
+ registered = true;
88
+ socket.on("cache:invalidate", async ({ key }) => {
89
+ await cavity.delete(key);
90
+ });
91
+ socket.on("connect", () => {
92
+ logger.cavity("WS connected:", socket.id);
93
+ subscriptions.forEach((key) => cavity.socket.subscribe(key));
94
+ });
95
+ socket.on("disconnect", (reason) => {
96
+ logger.cavityError("WS disconnected:", reason);
97
+ });
98
+ },
99
+ subscribe(key) {
100
+ const socket = getSocket();
101
+ if (!socket?.connected)
102
+ return;
103
+ if (subscriptions.has(key))
104
+ return;
105
+ subscriptions.add(key);
106
+ socket.emit("cache:subscribe", { key });
107
+ },
108
+ unsubscribe(key) {
109
+ const socket = getSocket();
110
+ if (!socket?.connected)
111
+ return;
112
+ if (!subscriptions.has(key))
113
+ return;
114
+ subscriptions.delete(key);
115
+ socket.emit("cache:unsubscribe", { key });
116
+ },
117
+ invalidate(key) {
118
+ const socket = getSocket();
119
+ if (!socket?.connected)
120
+ return;
121
+ socket.emit("cache:invalidate", { key });
122
+ },
123
+ }
124
+ };
@@ -0,0 +1,3 @@
1
+ import { ClassValue } from "clsx";
2
+ export declare const cn: (...classes: ClassValue[]) => string;
3
+ export declare const pcn: <prefixType>(className: string, prefix: prefixType, pseudoClass?: string) => string;
@@ -0,0 +1,45 @@
1
+ import { clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+ // ==============================>
4
+ // ## Merge class name
5
+ // ==============================>
6
+ export const cn = (...classes) => twMerge(clsx(classes));
7
+ // ==============================>
8
+ // ## Parse class name with custom prefix
9
+ // ==============================>
10
+ export const pcn = (className, prefix, pseudoClass) => {
11
+ const classes = className.split(" ");
12
+ const matchedClasses = classes.filter((cls) => {
13
+ const [clsPrefix, ...rest] = cls.split("::");
14
+ if (rest.length === 0 && (prefix === "input" || prefix === "base"))
15
+ return true;
16
+ if (rest.length > 0 && clsPrefix === prefix) {
17
+ if (pseudoClass) {
18
+ const [pseudo] = rest.join("::").split(":");
19
+ return pseudo === pseudoClass;
20
+ }
21
+ return true;
22
+ }
23
+ return false;
24
+ }).map((cls) => {
25
+ const [clsPrefix, ...rest] = cls.split("::");
26
+ if (rest.length > 0 && clsPrefix === prefix) {
27
+ const classNameWithoutPrefix = rest.join("::");
28
+ if (pseudoClass) {
29
+ return classNameWithoutPrefix.split(":").slice(1).join(":");
30
+ }
31
+ else {
32
+ if (/^(?!.*\b\w+:).*$/.test(classNameWithoutPrefix)) {
33
+ return classNameWithoutPrefix;
34
+ }
35
+ else {
36
+ return "";
37
+ }
38
+ }
39
+ }
40
+ if (rest.length === 0 && (prefix === "input" || prefix === "base"))
41
+ return cls;
42
+ return "";
43
+ }).filter(Boolean);
44
+ return matchedClasses.join(" ");
45
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,22 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import { exec } from "child_process";
4
+ import { logger } from "./logger";
5
+ const rootDir = path.resolve();
6
+ const configText = fs.readFileSync("barrels.json", "utf8");
7
+ const config = JSON.parse(configText);
8
+ const directories = Array.isArray(config.directory) ? config.directory : [config.directory];
9
+ directories.forEach((dir) => {
10
+ const absoluteDir = path.join(rootDir, dir);
11
+ if (!fs.existsSync(absoluteDir)) {
12
+ logger.error(`Barrels error: ${absoluteDir} directory not found`);
13
+ return;
14
+ }
15
+ fs.watch(absoluteDir, { recursive: true }, (_, filename) => {
16
+ if (filename && (filename.endsWith(".ts") || filename.endsWith(".tsx")) && filename !== "index.ts") {
17
+ exec("npx barrelsby -c barrels.json", { cwd: rootDir });
18
+ logger.info("Barrels updated " + absoluteDir + "/index.ts");
19
+ }
20
+ });
21
+ });
22
+ logger.start("Barrels watched " + directories.join(", "));
@@ -0,0 +1,3 @@
1
+ export declare function blueprint(options?: {
2
+ only?: string[];
3
+ }): Promise<void>;