@delfinjs/core 1.0.0 → 1.0.1
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.
- package/dist/{appConfig-hHCWS_gL.js → appConfig-DxhVfL1J.js} +2 -2
- package/dist/auth/index.js +3 -3
- package/dist/{helpers-CbBec0n2.js → helpers-DMOWsSP3.js} +4 -4
- package/dist/index.js +2 -2
- package/dist/utils/index.js +2 -2
- package/dist/validators/index.js +1 -1
- package/package.json +7 -7
- package/src/api/index.js +99 -0
- package/src/auth/index.js +127 -0
- package/src/crypto/index.js +78 -0
- package/src/eventBus/index.js +119 -0
- package/src/index.js +65 -0
- package/src/types/index.js +72 -0
- package/src/utils/appConfig.js +35 -0
- package/src/utils/colorConverter.js +31 -0
- package/src/utils/formatters.js +42 -0
- package/src/utils/helpers.js +56 -0
- package/src/utils/index.js +5 -0
- package/src/utils/microuiConfigLoader.js +70 -0
- package/src/validators/index.js +68 -0
package/dist/auth/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { parse as s, serialize as u } from "cookie-es";
|
|
2
2
|
import { destr as c } from "destr";
|
|
3
|
-
import {
|
|
3
|
+
import { a as f } from "../appConfig-DxhVfL1J.js";
|
|
4
4
|
const r = {};
|
|
5
|
-
function
|
|
5
|
+
function g() {
|
|
6
6
|
if (typeof document > "u") return !1;
|
|
7
7
|
const e = s(document.cookie), n = e.accessToken, t = e.userData;
|
|
8
8
|
if (n)
|
|
@@ -76,7 +76,7 @@ function _() {
|
|
|
76
76
|
}
|
|
77
77
|
export {
|
|
78
78
|
i as getShellUrl,
|
|
79
|
-
|
|
79
|
+
g as isAuthenticated,
|
|
80
80
|
_ as receiveAuthFromUrl,
|
|
81
81
|
y as redirectToLogin
|
|
82
82
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { formatCurrency as a, formatDate as o, formatNumber as t, hexToRgb as i, loadAllMicroUIConfigs as l, loadMicroUIConfig as p, rgbToHex as d } from "./utils/index.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { d as n, i as f, a as m, b as g, c, e as x } from "./helpers-DMOWsSP3.js";
|
|
3
|
+
import { g as A, a as V } from "./appConfig-DxhVfL1J.js";
|
|
4
4
|
import { decrypt as C, encrypt as u, hasCredential as b, parseCredentials as T, parseJwt as U } from "./crypto/index.js";
|
|
5
5
|
import { MFEventTypes as N, eventBus as w } from "./eventBus/index.js";
|
|
6
6
|
import { createAppApi as F, getApiBaseUrl as M } from "./api/index.js";
|
package/dist/utils/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { d as y, i as _, a as C, b as O, c as U, e as h } from "../helpers-DMOWsSP3.js";
|
|
2
|
+
import { g as x, a as N } from "../appConfig-DxhVfL1J.js";
|
|
3
3
|
const l = (n) => {
|
|
4
4
|
const e = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
|
|
5
5
|
n = n.replace(e, (r, a, o, i) => a + a + o + o + i + i);
|
package/dist/validators/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i, b as n, a as s } from "../helpers-DMOWsSP3.js";
|
|
2
2
|
const d = (r) => n(r) || s(r) || r === !1 ? "Bu alan zorunludur" : !!String(r).trim().length || "Bu alan zorunludur", l = (r) => {
|
|
3
3
|
if (i(r)) return !0;
|
|
4
4
|
const t = /^(?:[^<>()[\]\\.,;:\s@"]+(?:\.[^<>()[\]\\.,;:\s@"]+)*|".+")@(?:\[\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\]|(?:[a-z\-\d]+\.)+[a-z]{2,})$/i;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@delfinjs/core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Framework-agnostic shared utilities, event bus, API client, auth helpers, validators, and types for Delfin micro-frontends",
|
|
@@ -17,13 +17,10 @@
|
|
|
17
17
|
"./types": "./src/types/index.js"
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
|
+
"src",
|
|
20
21
|
"dist",
|
|
21
22
|
"README.md"
|
|
22
23
|
],
|
|
23
|
-
"scripts": {
|
|
24
|
-
"build": "vite build",
|
|
25
|
-
"prepublishOnly": "npm run build"
|
|
26
|
-
},
|
|
27
24
|
"dependencies": {
|
|
28
25
|
"cookie-es": "^1.2.2",
|
|
29
26
|
"crypto-js": "^4.2.0",
|
|
@@ -41,5 +38,8 @@
|
|
|
41
38
|
"event-bus",
|
|
42
39
|
"api-client"
|
|
43
40
|
],
|
|
44
|
-
"license": "UNLICENSED"
|
|
45
|
-
|
|
41
|
+
"license": "UNLICENSED",
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "vite build"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/api/index.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { ofetch } from 'ofetch'
|
|
2
|
+
import { eventBus, MFEventTypes } from '../eventBus/index.js'
|
|
3
|
+
import { getAppConfig } from '../utils/appConfig.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the API base URL from runtime config or environment.
|
|
7
|
+
* @returns {string}
|
|
8
|
+
*/
|
|
9
|
+
export function getApiBaseUrl() {
|
|
10
|
+
if (typeof window !== 'undefined' && window.__APP_CONFIG__?.VITE_API_BASE_URL) {
|
|
11
|
+
return window.__APP_CONFIG__.VITE_API_BASE_URL
|
|
12
|
+
}
|
|
13
|
+
return (typeof import.meta !== 'undefined' && import.meta.env?.VITE_API_BASE_URL) || '/api'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Framework-agnostic API client factory.
|
|
18
|
+
*
|
|
19
|
+
* @param {Object} options
|
|
20
|
+
* @param {string} [options.baseURL] - API base URL (defaults to runtime config)
|
|
21
|
+
* @param {string} [options.appSlug] - Micro-UI slug for auto token refresh via event bus
|
|
22
|
+
* @param {Record<string,string>} [options.headers] - Extra default headers
|
|
23
|
+
* @param {() => string|null} [options.getToken] - Callback to retrieve the current auth token
|
|
24
|
+
* @returns {{ (url: string, opts?: any): Promise<any>, setToken: (t: string) => void }}
|
|
25
|
+
*/
|
|
26
|
+
export function createAppApi(options = {}) {
|
|
27
|
+
const { baseURL, appSlug, headers: customHeaders, getToken } = typeof options === 'string'
|
|
28
|
+
? { baseURL: options, appSlug: undefined, headers: undefined, getToken: undefined }
|
|
29
|
+
: options
|
|
30
|
+
|
|
31
|
+
let _token = null
|
|
32
|
+
let _refreshPromise = null
|
|
33
|
+
|
|
34
|
+
function requestTokenRefresh() {
|
|
35
|
+
if (_refreshPromise) return _refreshPromise
|
|
36
|
+
|
|
37
|
+
_refreshPromise = new Promise(resolve => {
|
|
38
|
+
const timeout = setTimeout(() => {
|
|
39
|
+
unsubscribe()
|
|
40
|
+
_refreshPromise = null
|
|
41
|
+
resolve(null)
|
|
42
|
+
}, 10_000)
|
|
43
|
+
|
|
44
|
+
const unsubscribe = eventBus.on(
|
|
45
|
+
MFEventTypes.AUTH_MICROUI_TOKEN_REFRESH_RESPONSE,
|
|
46
|
+
({ slug, token }) => {
|
|
47
|
+
if (slug === appSlug) {
|
|
48
|
+
clearTimeout(timeout)
|
|
49
|
+
unsubscribe()
|
|
50
|
+
_token = token
|
|
51
|
+
_refreshPromise = null
|
|
52
|
+
resolve(token)
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
eventBus.emit(MFEventTypes.AUTH_MICROUI_TOKEN_REFRESH_REQUEST, { slug: appSlug })
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
return _refreshPromise
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const baseApi = ofetch.create({
|
|
64
|
+
baseURL: baseURL || getApiBaseUrl(),
|
|
65
|
+
async onRequest({ options: reqOpts }) {
|
|
66
|
+
const bearerToken = _token || getToken?.() || null
|
|
67
|
+
reqOpts.headers = {
|
|
68
|
+
...(customHeaders || {}),
|
|
69
|
+
...(reqOpts.headers || {}),
|
|
70
|
+
...(bearerToken ? { Authorization: `Bearer ${bearerToken}` } : {}),
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
const api = async (url, opts) => {
|
|
76
|
+
try {
|
|
77
|
+
return await baseApi(url, opts)
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (error?.response?.status === 401 && appSlug) {
|
|
80
|
+
const newToken = await requestTokenRefresh()
|
|
81
|
+
if (newToken) {
|
|
82
|
+
return baseApi(url, {
|
|
83
|
+
...opts,
|
|
84
|
+
headers: {
|
|
85
|
+
...(opts?.headers || {}),
|
|
86
|
+
Authorization: `Bearer ${newToken}`,
|
|
87
|
+
},
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
throw error
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Manually set the bearer token (e.g. received from the host). */
|
|
96
|
+
api.setToken = token => { _token = token }
|
|
97
|
+
|
|
98
|
+
return api
|
|
99
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { parse, serialize } from 'cookie-es'
|
|
2
|
+
import { destr } from 'destr'
|
|
3
|
+
import { getAppConfig } from '../utils/appConfig.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Check if user is authenticated by reading cookies.
|
|
7
|
+
* @returns {boolean}
|
|
8
|
+
*/
|
|
9
|
+
export function isAuthenticated() {
|
|
10
|
+
if (typeof document === 'undefined') return false
|
|
11
|
+
|
|
12
|
+
const cookies = parse(document.cookie)
|
|
13
|
+
const accessToken = cookies.accessToken
|
|
14
|
+
const userData = cookies.userData
|
|
15
|
+
|
|
16
|
+
if (accessToken) {
|
|
17
|
+
try {
|
|
18
|
+
const token = destr(decodeURIComponent(accessToken))
|
|
19
|
+
return !!token && token !== 'null' && token !== 'undefined'
|
|
20
|
+
} catch {
|
|
21
|
+
return false
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (userData) {
|
|
26
|
+
try {
|
|
27
|
+
const user = destr(decodeURIComponent(userData))
|
|
28
|
+
return !!user && typeof user === 'object' && user.id
|
|
29
|
+
} catch {
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Determine the shell (host) application URL.
|
|
39
|
+
* @returns {string}
|
|
40
|
+
*/
|
|
41
|
+
export function getShellUrl() {
|
|
42
|
+
const runtimeHost = getAppConfig('VITE_DELFIN_UI_BASE_URL')
|
|
43
|
+
if (runtimeHost) return runtimeHost.replace(/\/+$/, '')
|
|
44
|
+
|
|
45
|
+
if (typeof window !== 'undefined' && window.location.hostname === 'localhost' && window.location.port !== '3000') {
|
|
46
|
+
return 'http://localhost:3000'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return typeof window !== 'undefined' ? window.location.origin : ''
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isCrossOrigin() {
|
|
53
|
+
const shellUrl = getShellUrl()
|
|
54
|
+
try {
|
|
55
|
+
return typeof window !== 'undefined' && window.location.origin !== new URL(shellUrl).origin
|
|
56
|
+
} catch {
|
|
57
|
+
return false
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function getAppSlug() {
|
|
62
|
+
if (typeof window !== 'undefined' && window.__APP_CONFIG__?.VITE_APP_SLUG)
|
|
63
|
+
return window.__APP_CONFIG__.VITE_APP_SLUG
|
|
64
|
+
return typeof import.meta !== 'undefined' ? import.meta.env?.VITE_APP_SLUG : undefined
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function cleanAuthParam(url) {
|
|
68
|
+
try {
|
|
69
|
+
const u = new URL(url)
|
|
70
|
+
u.searchParams.delete('_auth')
|
|
71
|
+
return u.toString()
|
|
72
|
+
} catch {
|
|
73
|
+
return url
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function redirectToAuthBridge() {
|
|
78
|
+
const shellUrl = getShellUrl()
|
|
79
|
+
const slug = getAppSlug()
|
|
80
|
+
const currentUrl = cleanAuthParam(window.location.href)
|
|
81
|
+
const slugParam = slug ? `&slug=${encodeURIComponent(slug)}` : ''
|
|
82
|
+
window.location.href = `${shellUrl}/auth-bridge?redirect=${encodeURIComponent(currentUrl)}${slugParam}`
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Redirect to the shell login page.
|
|
87
|
+
*/
|
|
88
|
+
export function redirectToLogin() {
|
|
89
|
+
if (typeof window === 'undefined') return
|
|
90
|
+
|
|
91
|
+
if (isCrossOrigin()) {
|
|
92
|
+
redirectToAuthBridge()
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const shellUrl = getShellUrl()
|
|
97
|
+
window.location.href = `${shellUrl}/login?redirect=${encodeURIComponent(window.location.href)}`
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Receive auth cookies from the `_auth` query parameter (set by auth-bridge redirect).
|
|
102
|
+
* Call once at app startup before any route guard runs.
|
|
103
|
+
* @returns {boolean} true if auth data was received and stored
|
|
104
|
+
*/
|
|
105
|
+
export function receiveAuthFromUrl() {
|
|
106
|
+
if (typeof window === 'undefined') return false
|
|
107
|
+
|
|
108
|
+
const params = new URLSearchParams(window.location.search)
|
|
109
|
+
const encoded = params.get('_auth')
|
|
110
|
+
if (!encoded) return false
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
const authCookies = JSON.parse(atob(decodeURIComponent(encoded)))
|
|
114
|
+
|
|
115
|
+
for (const [name, value] of Object.entries(authCookies)) {
|
|
116
|
+
document.cookie = serialize(name, value, {
|
|
117
|
+
path: '/',
|
|
118
|
+
maxAge: 60 * 60 * 24 * 30,
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
window.history.replaceState(null, '', cleanAuthParam(window.location.href))
|
|
123
|
+
return true
|
|
124
|
+
} catch {
|
|
125
|
+
return false
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import CryptoJS from 'crypto-js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AES-256-CBC encrypt (OpenSSL-compatible, matches .NET CryptoJsCompatDecryptorHelper).
|
|
5
|
+
* @param {string} plainText
|
|
6
|
+
* @param {string} passphrase
|
|
7
|
+
* @returns {string} base64 ciphertext
|
|
8
|
+
*/
|
|
9
|
+
export function encrypt(plainText, passphrase) {
|
|
10
|
+
return CryptoJS.AES.encrypt(plainText, passphrase).toString()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* AES-256-CBC decrypt (OpenSSL-compatible).
|
|
15
|
+
* @param {string} encryptedBase64
|
|
16
|
+
* @param {string} passphrase
|
|
17
|
+
* @returns {string} plaintext
|
|
18
|
+
*/
|
|
19
|
+
export function decrypt(encryptedBase64, passphrase) {
|
|
20
|
+
const bytes = CryptoJS.AES.decrypt(encryptedBase64, passphrase)
|
|
21
|
+
return bytes.toString(CryptoJS.enc.Utf8)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Parse a JWT token and return its payload.
|
|
26
|
+
* @param {string} token
|
|
27
|
+
* @returns {Record<string, any>}
|
|
28
|
+
*/
|
|
29
|
+
export function parseJwt(token) {
|
|
30
|
+
const base64Url = token.split('.')[1]
|
|
31
|
+
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')
|
|
32
|
+
const jsonPayload = decodeURIComponent(
|
|
33
|
+
atob(base64)
|
|
34
|
+
.split('')
|
|
35
|
+
.map(c => `%${(`00${c.charCodeAt(0).toString(16)}`).slice(-2)}`)
|
|
36
|
+
.join(''),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
return JSON.parse(jsonPayload)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Parse JWT credentials claim into a Set of active credential names.
|
|
44
|
+
* Format: "CredName###1" where 1 = active.
|
|
45
|
+
* @param {string} token
|
|
46
|
+
* @returns {Set<string>}
|
|
47
|
+
*/
|
|
48
|
+
export function parseCredentials(token) {
|
|
49
|
+
if (!token) return new Set()
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const payload = parseJwt(token)
|
|
53
|
+
const raw = payload.credentials
|
|
54
|
+
if (!Array.isArray(raw)) return new Set()
|
|
55
|
+
|
|
56
|
+
const active = new Set()
|
|
57
|
+
for (const entry of raw) {
|
|
58
|
+
const [name, value] = entry.split('###')
|
|
59
|
+
if (value === '1' && name) active.add(name)
|
|
60
|
+
}
|
|
61
|
+
return active
|
|
62
|
+
} catch (err) {
|
|
63
|
+
console.error('[credentials] Failed to parse JWT credentials:', err)
|
|
64
|
+
return new Set()
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Check whether a required credential is satisfied.
|
|
70
|
+
* Returns true if no credential is required.
|
|
71
|
+
* @param {Set<string>} credentials
|
|
72
|
+
* @param {string|undefined} required
|
|
73
|
+
* @returns {boolean}
|
|
74
|
+
*/
|
|
75
|
+
export function hasCredential(credentials, required) {
|
|
76
|
+
if (!required) return true
|
|
77
|
+
return credentials.has(required)
|
|
78
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Microfrontend Event Types
|
|
3
|
+
*/
|
|
4
|
+
export const MFEventTypes = {
|
|
5
|
+
AUTH_LOGIN: 'mf:auth:login',
|
|
6
|
+
AUTH_LOGOUT: 'mf:auth:logout',
|
|
7
|
+
AUTH_TOKEN_REFRESH: 'mf:auth:token-refresh',
|
|
8
|
+
AUTH_USER_UPDATE: 'mf:auth:user-update',
|
|
9
|
+
AUTH_MICROUI_TOKEN_REFRESH_REQUEST: 'mf:auth:microui-token-refresh-request',
|
|
10
|
+
AUTH_MICROUI_TOKEN_REFRESH_RESPONSE: 'mf:auth:microui-token-refresh-response',
|
|
11
|
+
|
|
12
|
+
NAV_ROUTE_CHANGE: 'mf:nav:route-change',
|
|
13
|
+
NAV_BREADCRUMB_UPDATE: 'mf:nav:breadcrumb-update',
|
|
14
|
+
|
|
15
|
+
THEME_CHANGE: 'mf:theme:change',
|
|
16
|
+
LOCALE_CHANGE: 'mf:locale:change',
|
|
17
|
+
|
|
18
|
+
LOADING_START: 'mf:loading:start',
|
|
19
|
+
LOADING_END: 'mf:loading:end',
|
|
20
|
+
|
|
21
|
+
ERROR_GLOBAL: 'mf:error:global',
|
|
22
|
+
ERROR_AUTH: 'mf:error:auth',
|
|
23
|
+
|
|
24
|
+
APP_MOUNTED: 'mf:app:mounted',
|
|
25
|
+
APP_UNMOUNTED: 'mf:app:unmounted',
|
|
26
|
+
APP_READY: 'mf:app:ready',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class EventBus {
|
|
30
|
+
constructor() {
|
|
31
|
+
this.events = new Map()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Subscribe to an event.
|
|
36
|
+
* @param {string} event
|
|
37
|
+
* @param {Function} callback
|
|
38
|
+
* @returns {Function} unsubscribe
|
|
39
|
+
*/
|
|
40
|
+
on(event, callback) {
|
|
41
|
+
if (!this.events.has(event)) {
|
|
42
|
+
this.events.set(event, new Set())
|
|
43
|
+
}
|
|
44
|
+
this.events.get(event).add(callback)
|
|
45
|
+
return () => this.off(event, callback)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Subscribe once.
|
|
50
|
+
* @param {string} event
|
|
51
|
+
* @param {Function} callback
|
|
52
|
+
*/
|
|
53
|
+
once(event, callback) {
|
|
54
|
+
const wrapper = (...args) => {
|
|
55
|
+
callback(...args)
|
|
56
|
+
this.off(event, wrapper)
|
|
57
|
+
}
|
|
58
|
+
this.on(event, wrapper)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Unsubscribe.
|
|
63
|
+
* @param {string} event
|
|
64
|
+
* @param {Function} callback
|
|
65
|
+
*/
|
|
66
|
+
off(event, callback) {
|
|
67
|
+
if (this.events.has(event)) {
|
|
68
|
+
this.events.get(event).delete(callback)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Emit an event.
|
|
74
|
+
* @param {string} event
|
|
75
|
+
* @param {*} data
|
|
76
|
+
*/
|
|
77
|
+
emit(event, data) {
|
|
78
|
+
if (this.events.has(event)) {
|
|
79
|
+
this.events.get(event).forEach(callback => {
|
|
80
|
+
try {
|
|
81
|
+
callback(data)
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error(`[EventBus] Error in handler for ${event}:`, error)
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (typeof window !== 'undefined') {
|
|
89
|
+
window.dispatchEvent(new CustomEvent(event, { detail: data }))
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Clear subscriptions.
|
|
95
|
+
* @param {string} [event] - If omitted, clears all.
|
|
96
|
+
*/
|
|
97
|
+
clear(event) {
|
|
98
|
+
if (event) {
|
|
99
|
+
this.events.delete(event)
|
|
100
|
+
} else {
|
|
101
|
+
this.events.clear()
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Singleton EventBus instance */
|
|
107
|
+
export const eventBus = new EventBus()
|
|
108
|
+
|
|
109
|
+
if (typeof window !== 'undefined') {
|
|
110
|
+
Object.values(MFEventTypes).forEach(eventType => {
|
|
111
|
+
window.addEventListener(eventType, event => {
|
|
112
|
+
if (!event._fromEventBus) {
|
|
113
|
+
eventBus.events.get(eventType)?.forEach(callback => {
|
|
114
|
+
callback(event.detail)
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Utils
|
|
2
|
+
export {
|
|
3
|
+
hexToRgb,
|
|
4
|
+
rgbToHex,
|
|
5
|
+
formatDate,
|
|
6
|
+
formatCurrency,
|
|
7
|
+
formatNumber,
|
|
8
|
+
isEmpty,
|
|
9
|
+
isNullOrUndefined,
|
|
10
|
+
isEmptyArray,
|
|
11
|
+
isObject,
|
|
12
|
+
isToday,
|
|
13
|
+
deepClone,
|
|
14
|
+
getAppConfig,
|
|
15
|
+
getAllAppConfig,
|
|
16
|
+
loadMicroUIConfig,
|
|
17
|
+
loadAllMicroUIConfigs,
|
|
18
|
+
} from './utils/index.js'
|
|
19
|
+
|
|
20
|
+
// Crypto & Credentials
|
|
21
|
+
export {
|
|
22
|
+
encrypt,
|
|
23
|
+
decrypt,
|
|
24
|
+
parseJwt,
|
|
25
|
+
parseCredentials,
|
|
26
|
+
hasCredential,
|
|
27
|
+
} from './crypto/index.js'
|
|
28
|
+
|
|
29
|
+
// Event Bus
|
|
30
|
+
export { eventBus, MFEventTypes } from './eventBus/index.js'
|
|
31
|
+
|
|
32
|
+
// API Client
|
|
33
|
+
export { createAppApi, getApiBaseUrl } from './api/index.js'
|
|
34
|
+
|
|
35
|
+
// Auth Helpers
|
|
36
|
+
export {
|
|
37
|
+
isAuthenticated,
|
|
38
|
+
getShellUrl,
|
|
39
|
+
redirectToLogin,
|
|
40
|
+
receiveAuthFromUrl,
|
|
41
|
+
} from './auth/index.js'
|
|
42
|
+
|
|
43
|
+
// Validators
|
|
44
|
+
export {
|
|
45
|
+
requiredValidator,
|
|
46
|
+
emailValidator,
|
|
47
|
+
passwordValidator,
|
|
48
|
+
confirmedValidator,
|
|
49
|
+
betweenValidator,
|
|
50
|
+
integerValidator,
|
|
51
|
+
regexValidator,
|
|
52
|
+
alphaValidator,
|
|
53
|
+
urlValidator,
|
|
54
|
+
lengthValidator,
|
|
55
|
+
alphaDashValidator,
|
|
56
|
+
} from './validators/index.js'
|
|
57
|
+
|
|
58
|
+
// Types & Enums
|
|
59
|
+
export {
|
|
60
|
+
AppContentLayoutNav,
|
|
61
|
+
ContentWidth,
|
|
62
|
+
NavbarType,
|
|
63
|
+
FooterType,
|
|
64
|
+
Skins,
|
|
65
|
+
} from './types/index.js'
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} ThemeConfig
|
|
3
|
+
* @property {string} theme - 'light' | 'dark' | 'system'
|
|
4
|
+
* @property {boolean} isVerticalNavCollapsed
|
|
5
|
+
* @property {string} contentWidth - 'boxed' | 'fluid'
|
|
6
|
+
* @property {string} contentLayoutNav - 'vertical' | 'horizontal'
|
|
7
|
+
* @property {boolean} isVerticalNavSemiDark
|
|
8
|
+
* @property {boolean} isAppRTL
|
|
9
|
+
* @property {string} navbarType - 'sticky' | 'static' | 'hidden'
|
|
10
|
+
* @property {string} footerType - 'sticky' | 'static' | 'hidden'
|
|
11
|
+
* @property {boolean} navbarBlur
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {Object} UserData
|
|
16
|
+
* @property {string|number} id
|
|
17
|
+
* @property {string} fullName
|
|
18
|
+
* @property {string} username
|
|
19
|
+
* @property {string} email
|
|
20
|
+
* @property {string} avatar
|
|
21
|
+
* @property {string} role
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {Object} AbilityRule
|
|
26
|
+
* @property {string} action
|
|
27
|
+
* @property {string} subject
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @typedef {Object} NavItem
|
|
32
|
+
* @property {string} title
|
|
33
|
+
* @property {string} [to]
|
|
34
|
+
* @property {Object} [icon]
|
|
35
|
+
* @property {NavItem[]} [children]
|
|
36
|
+
* @property {string} [heading]
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @typedef {Object} MFAppConfig
|
|
41
|
+
* @property {string} name
|
|
42
|
+
* @property {string} url
|
|
43
|
+
* @property {string} scope
|
|
44
|
+
* @property {string} module
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
export const AppContentLayoutNav = {
|
|
48
|
+
Vertical: 'vertical',
|
|
49
|
+
Horizontal: 'horizontal',
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const ContentWidth = {
|
|
53
|
+
Boxed: 'boxed',
|
|
54
|
+
Fluid: 'fluid',
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const NavbarType = {
|
|
58
|
+
Sticky: 'sticky',
|
|
59
|
+
Static: 'static',
|
|
60
|
+
Hidden: 'hidden',
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const FooterType = {
|
|
64
|
+
Sticky: 'sticky',
|
|
65
|
+
Static: 'static',
|
|
66
|
+
Hidden: 'hidden',
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const Skins = {
|
|
70
|
+
Default: 'default',
|
|
71
|
+
Bordered: 'bordered',
|
|
72
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime configuration loader.
|
|
3
|
+
*
|
|
4
|
+
* Host app (Docker/K8s): reads from window.__APP_CONFIG__
|
|
5
|
+
* Micro-UI (loaded in host): reads from window.__MICROUI_CONFIG__[slug]
|
|
6
|
+
* Development (local): falls back to import.meta.env
|
|
7
|
+
*
|
|
8
|
+
* @param {string} key - Config key (e.g., 'VITE_API_BASE_URL')
|
|
9
|
+
* @param {string} [appSlug] - Optional micro-UI slug for namespaced config
|
|
10
|
+
* @returns {string|undefined}
|
|
11
|
+
*/
|
|
12
|
+
export function getAppConfig(key, appSlug) {
|
|
13
|
+
if (typeof window !== 'undefined') {
|
|
14
|
+
if (appSlug && window.__MICROUI_CONFIG__?.[appSlug]?.[key] !== undefined)
|
|
15
|
+
return window.__MICROUI_CONFIG__[appSlug][key]
|
|
16
|
+
|
|
17
|
+
if (window.__APP_CONFIG__?.[key] !== undefined)
|
|
18
|
+
return window.__APP_CONFIG__[key]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return typeof import.meta !== 'undefined' ? import.meta.env?.[key] : undefined
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get all app configuration merged (build-time + host + micro-UI).
|
|
26
|
+
* @param {string} [appSlug]
|
|
27
|
+
* @returns {Record<string, string>}
|
|
28
|
+
*/
|
|
29
|
+
export function getAllAppConfig(appSlug) {
|
|
30
|
+
const buildConfig = (typeof import.meta !== 'undefined' && import.meta.env) || {}
|
|
31
|
+
const hostConfig = (typeof window !== 'undefined' && window.__APP_CONFIG__) || {}
|
|
32
|
+
const microConfig = (typeof window !== 'undefined' && appSlug && window.__MICROUI_CONFIG__?.[appSlug]) || {}
|
|
33
|
+
|
|
34
|
+
return { ...buildConfig, ...hostConfig, ...microConfig }
|
|
35
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert hex color to RGB string
|
|
3
|
+
* @param {string} hex - Hex color string (e.g., '#ff0000' or 'ff0000')
|
|
4
|
+
* @returns {string|null} RGB string (e.g., '255, 0, 0')
|
|
5
|
+
*/
|
|
6
|
+
export const hexToRgb = hex => {
|
|
7
|
+
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i
|
|
8
|
+
hex = hex.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b)
|
|
9
|
+
|
|
10
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
|
|
11
|
+
|
|
12
|
+
return result
|
|
13
|
+
? `${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}`
|
|
14
|
+
: null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Convert RGB values to hex color string
|
|
19
|
+
* @param {number} r - Red (0-255)
|
|
20
|
+
* @param {number} g - Green (0-255)
|
|
21
|
+
* @param {number} b - Blue (0-255)
|
|
22
|
+
* @returns {string} Hex color string (e.g., '#ff0000')
|
|
23
|
+
*/
|
|
24
|
+
export const rgbToHex = (r, g, b) => {
|
|
25
|
+
const toHex = c => {
|
|
26
|
+
const hex = c.toString(16)
|
|
27
|
+
return hex.length === 1 ? '0' + hex : hex
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`
|
|
31
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format date to locale string
|
|
3
|
+
* @param {Date|string|number} date
|
|
4
|
+
* @param {string} [locale='en-US']
|
|
5
|
+
* @param {Intl.DateTimeFormatOptions} [options={}]
|
|
6
|
+
* @returns {string}
|
|
7
|
+
*/
|
|
8
|
+
export const formatDate = (date, locale = 'en-US', options = {}) => {
|
|
9
|
+
const defaultOptions = {
|
|
10
|
+
year: 'numeric',
|
|
11
|
+
month: 'short',
|
|
12
|
+
day: 'numeric',
|
|
13
|
+
...options,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return new Intl.DateTimeFormat(locale, defaultOptions).format(new Date(date))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Format number as currency
|
|
21
|
+
* @param {number} value
|
|
22
|
+
* @param {string} [currency='USD']
|
|
23
|
+
* @param {string} [locale='en-US']
|
|
24
|
+
* @returns {string}
|
|
25
|
+
*/
|
|
26
|
+
export const formatCurrency = (value, currency = 'USD', locale = 'en-US') => {
|
|
27
|
+
return new Intl.NumberFormat(locale, {
|
|
28
|
+
style: 'currency',
|
|
29
|
+
currency,
|
|
30
|
+
}).format(value)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Format number with locale
|
|
35
|
+
* @param {number} value
|
|
36
|
+
* @param {string} [locale='en-US']
|
|
37
|
+
* @param {Intl.NumberFormatOptions} [options={}]
|
|
38
|
+
* @returns {string}
|
|
39
|
+
*/
|
|
40
|
+
export const formatNumber = (value, locale = 'en-US', options = {}) => {
|
|
41
|
+
return new Intl.NumberFormat(locale, options).format(value)
|
|
42
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if value is empty (null, undefined, empty string, empty array, empty object)
|
|
3
|
+
* @param {*} value
|
|
4
|
+
* @returns {boolean}
|
|
5
|
+
*/
|
|
6
|
+
export const isEmpty = value => {
|
|
7
|
+
if (value === null || value === undefined || value === '') return true
|
|
8
|
+
if (Array.isArray(value) && value.length === 0) return true
|
|
9
|
+
if (typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0) return true
|
|
10
|
+
return false
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** @param {*} value */
|
|
14
|
+
export const isNullOrUndefined = value => value === null || value === undefined
|
|
15
|
+
|
|
16
|
+
/** @param {*} arr */
|
|
17
|
+
export const isEmptyArray = arr => Array.isArray(arr) && arr.length === 0
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Check if value is a plain object (not array, not null)
|
|
21
|
+
* @param {*} value
|
|
22
|
+
* @returns {boolean}
|
|
23
|
+
*/
|
|
24
|
+
export const isObject = value => value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Check if a Date is today
|
|
28
|
+
* @param {Date} date
|
|
29
|
+
* @returns {boolean}
|
|
30
|
+
*/
|
|
31
|
+
export const isToday = date => {
|
|
32
|
+
const today = new Date()
|
|
33
|
+
return (
|
|
34
|
+
date.getDate() === today.getDate() &&
|
|
35
|
+
date.getMonth() === today.getMonth() &&
|
|
36
|
+
date.getFullYear() === today.getFullYear()
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Deep clone an object
|
|
42
|
+
* @param {*} obj
|
|
43
|
+
* @returns {*}
|
|
44
|
+
*/
|
|
45
|
+
export const deepClone = obj => {
|
|
46
|
+
if (obj === null || typeof obj !== 'object') return obj
|
|
47
|
+
if (Array.isArray(obj)) return obj.map(deepClone)
|
|
48
|
+
|
|
49
|
+
const cloned = {}
|
|
50
|
+
for (const key in obj) {
|
|
51
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
52
|
+
cloned[key] = deepClone(obj[key])
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return cloned
|
|
56
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { hexToRgb, rgbToHex } from './colorConverter.js'
|
|
2
|
+
export { formatDate, formatCurrency, formatNumber } from './formatters.js'
|
|
3
|
+
export { isEmpty, isNullOrUndefined, isEmptyArray, isObject, isToday, deepClone } from './helpers.js'
|
|
4
|
+
export { getAppConfig, getAllAppConfig } from './appConfig.js'
|
|
5
|
+
export { loadMicroUIConfig, loadAllMicroUIConfigs } from './microuiConfigLoader.js'
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetches each micro-UI's /config.js from its container origin,
|
|
3
|
+
* parses the JSON object from it, and stores it under
|
|
4
|
+
* window.__MICROUI_CONFIG__[slug].
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
if (typeof window !== 'undefined' && !window.__MICROUI_CONFIG__) {
|
|
8
|
+
window.__MICROUI_CONFIG__ = {}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function extractOrigin(remoteUrl) {
|
|
12
|
+
try {
|
|
13
|
+
return new URL(remoteUrl).origin
|
|
14
|
+
} catch {
|
|
15
|
+
return null
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function parseConfigJs(text) {
|
|
20
|
+
const start = text.indexOf('{')
|
|
21
|
+
const end = text.lastIndexOf('}')
|
|
22
|
+
if (start === -1 || end === -1) return null
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
return JSON.parse(text.slice(start, end + 1))
|
|
26
|
+
} catch {
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Load a single micro-UI's runtime config from its container.
|
|
33
|
+
* @param {string} slug
|
|
34
|
+
* @param {string} remoteUrl - The remoteEntry.js URL (used to derive origin)
|
|
35
|
+
* @returns {Promise<Record<string, string>|null>}
|
|
36
|
+
*/
|
|
37
|
+
export async function loadMicroUIConfig(slug, remoteUrl) {
|
|
38
|
+
const origin = extractOrigin(remoteUrl)
|
|
39
|
+
if (!origin) return null
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const response = await fetch(`${origin}/config.js`, { cache: 'no-cache' })
|
|
43
|
+
if (!response.ok) return null
|
|
44
|
+
|
|
45
|
+
const text = await response.text()
|
|
46
|
+
const config = parseConfigJs(text)
|
|
47
|
+
if (config && typeof window !== 'undefined') {
|
|
48
|
+
window.__MICROUI_CONFIG__[slug] = config
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return config
|
|
52
|
+
} catch {
|
|
53
|
+
console.warn(`[MicroUIConfig] Failed to load config for ${slug} from ${origin}/config.js`)
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Load runtime configs for all registered micro-UIs in parallel.
|
|
60
|
+
* @param {Record<string, { remoteUrl: string }>} microuiConfig
|
|
61
|
+
* @returns {Promise<void>}
|
|
62
|
+
*/
|
|
63
|
+
export async function loadAllMicroUIConfigs(microuiConfig) {
|
|
64
|
+
const entries = Object.entries(microuiConfig)
|
|
65
|
+
if (entries.length === 0) return
|
|
66
|
+
|
|
67
|
+
await Promise.allSettled(
|
|
68
|
+
entries.map(([slug, config]) => loadMicroUIConfig(slug, config.remoteUrl)),
|
|
69
|
+
)
|
|
70
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { isEmpty, isNullOrUndefined, isEmptyArray } from '../utils/helpers.js'
|
|
2
|
+
|
|
3
|
+
export const requiredValidator = value => {
|
|
4
|
+
if (isNullOrUndefined(value) || isEmptyArray(value) || value === false)
|
|
5
|
+
return 'Bu alan zorunludur'
|
|
6
|
+
|
|
7
|
+
return !!String(value).trim().length || 'Bu alan zorunludur'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const emailValidator = value => {
|
|
11
|
+
if (isEmpty(value)) return true
|
|
12
|
+
const re = /^(?:[^<>()[\]\\.,;:\s@"]+(?:\.[^<>()[\]\\.,;:\s@"]+)*|".+")@(?:\[\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\]|(?:[a-z\-\d]+\.)+[a-z]{2,})$/i
|
|
13
|
+
if (Array.isArray(value))
|
|
14
|
+
return value.every(val => re.test(String(val))) || 'Gecerli bir e-posta adresi girin'
|
|
15
|
+
|
|
16
|
+
return re.test(String(value)) || 'Gecerli bir e-posta adresi girin'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const passwordValidator = password => {
|
|
20
|
+
const regExp = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%&*()]).{8,}/
|
|
21
|
+
return regExp.test(password) || 'Alan en az bir buyuk harf, kucuk harf, ozel karakter ve rakam icermeli ve en az 8 karakter uzunlugunda olmalidir'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const confirmedValidator = (value, target) =>
|
|
25
|
+
value === target || 'Sifreler eslesmedi'
|
|
26
|
+
|
|
27
|
+
export const betweenValidator = (value, min, max) => {
|
|
28
|
+
const n = Number(value)
|
|
29
|
+
return (Number(min) <= n && Number(max) >= n) || `${min} ve ${max} arasi bir deger girin`
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const integerValidator = value => {
|
|
33
|
+
if (isEmpty(value)) return true
|
|
34
|
+
if (Array.isArray(value))
|
|
35
|
+
return value.every(val => /^-?\d+$/.test(String(val))) || 'Bu alan tam sayi olmalidir'
|
|
36
|
+
|
|
37
|
+
return /^-?\d+$/.test(String(value)) || 'Bu alan tam sayi olmalidir'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const regexValidator = (value, regex) => {
|
|
41
|
+
if (isEmpty(value)) return true
|
|
42
|
+
let re = regex
|
|
43
|
+
if (typeof re === 'string') re = new RegExp(re)
|
|
44
|
+
if (Array.isArray(value)) return value.every(val => regexValidator(val, re))
|
|
45
|
+
|
|
46
|
+
return re.test(String(value)) || 'Gecersiz format'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const alphaValidator = value => {
|
|
50
|
+
if (isEmpty(value)) return true
|
|
51
|
+
return /^[A-Z]*$/i.test(String(value)) || 'Sadece harf karakterleri kullanilaabilir'
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const urlValidator = value => {
|
|
55
|
+
if (isEmpty(value)) return true
|
|
56
|
+
const re = /^https?:\/\/[^\s$.?#].\S*$/
|
|
57
|
+
return re.test(String(value)) || 'Gecersiz URL'
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const lengthValidator = (value, length) => {
|
|
61
|
+
if (isEmpty(value)) return true
|
|
62
|
+
return String(value).length === length || `Karakter uzunlugu ${length} karakter olmalidir`
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const alphaDashValidator = value => {
|
|
66
|
+
if (isEmpty(value)) return true
|
|
67
|
+
return /^[\w-]*$/.test(String(value)) || 'Gecersiz karakter'
|
|
68
|
+
}
|