@kdtlabs/utils 0.0.8 → 0.0.10
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/SKILL.md +39 -1
- package/dist/chunk-k250wjfj.js +5 -0
- package/dist/chunk-k250wjfj.js.map +28 -0
- package/dist/errors/guards.d.ts +2 -0
- package/dist/errors/guards.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +5 -22
- package/dist/system/fs.d.ts +10 -0
- package/dist/system/fs.d.ts.map +1 -0
- package/dist/system/hash.d.ts +6 -0
- package/dist/system/hash.d.ts.map +1 -0
- package/dist/system/index.d.ts +2 -0
- package/dist/system/index.d.ts.map +1 -1
- package/dist/system/path.d.ts +1 -0
- package/dist/system/path.d.ts.map +1 -1
- package/dist/times/operations.d.ts +2 -0
- package/dist/times/operations.d.ts.map +1 -1
- package/dist/zod/index.d.ts +2 -0
- package/dist/zod/index.d.ts.map +1 -0
- package/dist/zod/index.js +4 -0
- package/dist/zod/index.js.map +10 -0
- package/dist/zod/schemas.d.ts +22 -0
- package/dist/zod/schemas.d.ts.map +1 -0
- package/package.json +15 -2
- package/src/errors/guards.ts +6 -0
- package/src/system/fs.ts +65 -0
- package/src/system/hash.ts +22 -0
- package/src/system/index.ts +2 -0
- package/src/system/path.ts +12 -1
- package/src/times/operations.ts +24 -0
- package/src/zod/index.ts +1 -0
- package/src/zod/schemas.ts +77 -0
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{isDirectory,isFile,isHexString,isNumberString,isReadable,isStrictHexString,isTrueLike,isValidDate,isWritable,isWritableDirectory}from"../chunk-k250wjfj.js";import{z}from"zod";var PROTOCOL_PATTERNS={file:/^file$/u,ftp:/^ftps?$/u,http:/^https?$/u,ssh:/^ssh$/u,ws:/^wss?$/u};function url(...protocols){let patterns=protocols.flat().map((p)=>PROTOCOL_PATTERNS[p]),combined=new RegExp(`^(?:${patterns.map((r)=>r.source.slice(1,-1)).join("|")})$`);return z.url({protocol:combined})}var hexString=(length)=>z.string().refine((val)=>isHexString(val,length),{error:length?`Invalid hex string (expected ${length} bytes)`:"Invalid hex string"}),strictHexString=(length)=>z.string().refine((val)=>isStrictHexString(val,length),{error:length?`Invalid strict hex string (expected 0x prefix, ${length} bytes)`:"Invalid strict hex string (expected 0x prefix)"}),numberString=()=>z.string().refine((val)=>isNumberString(val),{error:"Invalid number string"}),validDate=()=>z.custom((val)=>isValidDate(val),{error:"Invalid date"}),trueLike=(options)=>z.unknown().transform((val)=>isTrueLike(val,options)),filePath=()=>z.string().refine((val)=>isFile(val),{error:"Path is not a file"}),directoryPath=()=>z.string().refine((val)=>isDirectory(val),{error:"Path is not a directory"}),readable=()=>z.string().refine((val)=>isReadable(val),{error:"Path is not readable"}),writable=()=>z.string().refine((val)=>isWritable(val),{error:"Path is not writable"}),writableDirectory=()=>z.string().refine((val)=>isWritableDirectory(val),{error:"Path is not a writable directory"});export{writableDirectory,writable,validDate,url,trueLike,strictHexString,readable,numberString,hexString,filePath,directoryPath,PROTOCOL_PATTERNS};
|
|
2
|
+
|
|
3
|
+
//# debugId=CA2A82F81B89B66B64756E2164756E21
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/zod/schemas.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { IsTrueLikeOptions } from '../core'\nimport { z } from 'zod'\nimport { isTrueLike } from '../core'\nimport { isNumberString } from '../numbers'\nimport { isHexString, isStrictHexString } from '../strings'\nimport { isDirectory, isFile, isReadable, isWritable, isWritableDirectory } from '../system'\nimport { isValidDate } from '../times'\n\nexport const PROTOCOL_PATTERNS = <const>{\n file: /^file$/u,\n ftp: /^ftps?$/u,\n http: /^https?$/u,\n ssh: /^ssh$/u,\n ws: /^wss?$/u,\n}\n\nexport type Protocol = keyof typeof PROTOCOL_PATTERNS\n\nexport function url(...protocols: Array<Protocol | Protocol[]>) {\n const flat = protocols.flat()\n const patterns = flat.map((p) => PROTOCOL_PATTERNS[p])\n const combined = new RegExp(`^(?:${patterns.map((r) => r.source.slice(1, -1)).join('|')})$`)\n\n return z.url({ protocol: combined })\n}\n\nexport const hexString = (length?: number) => (\n z.string().refine((val) => isHexString(val, length), {\n error: length ? `Invalid hex string (expected ${length} bytes)` : 'Invalid hex string',\n })\n)\n\nexport const strictHexString = (length?: number) => (\n z.string().refine((val) => isStrictHexString(val, length), {\n error: length ? `Invalid strict hex string (expected 0x prefix, ${length} bytes)` : 'Invalid strict hex string (expected 0x prefix)',\n })\n)\n\nexport const numberString = () => (\n z.string().refine((val) => isNumberString(val), {\n error: 'Invalid number string',\n })\n)\n\nexport const validDate = () => (\n z.custom<Date>((val) => isValidDate(val), { error: 'Invalid date' })\n)\n\nexport const trueLike = (options?: IsTrueLikeOptions) => z.unknown().transform((val) => isTrueLike(val, options))\n\nexport const filePath = () => z.string().refine((val) => isFile(val), {\n error: 'Path is not a file',\n})\n\nexport const directoryPath = () => (\n z.string().refine((val) => isDirectory(val), {\n error: 'Path is not a directory',\n })\n)\n\nexport const readable = () => (\n z.string().refine((val) => isReadable(val), {\n error: 'Path is not readable',\n })\n)\n\nexport const writable = () => (\n z.string().refine((val) => isWritable(val), {\n error: 'Path is not writable',\n })\n)\n\nexport const writableDirectory = () => (\n z.string().refine((val) => isWritableDirectory(val), {\n error: 'Path is not a writable directory',\n })\n)\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "mKACA,mBAOO,IAAM,kBAA2B,CACpC,KAAM,UACN,IAAK,WACL,KAAM,YACN,IAAK,SACL,GAAI,SACR,EAIO,SAAS,GAAG,IAAI,UAAyC,CAE5D,IAAM,SADO,UAAU,KAAK,EACN,IAAI,CAAC,IAAM,kBAAkB,EAAE,EAC/C,SAAW,IAAI,OAAO,OAAO,SAAS,IAAI,CAAC,IAAM,EAAE,OAAO,MAAM,EAAG,EAAE,CAAC,EAAE,KAAK,GAAG,KAAK,EAE3F,OAAO,EAAE,IAAI,CAAE,SAAU,QAAS,CAAC,EAGhC,IAAM,UAAY,CAAC,SACtB,EAAE,OAAO,EAAE,OAAO,CAAC,MAAQ,YAAY,IAAK,MAAM,EAAG,CACjD,MAAO,OAAS,gCAAgC,gBAAkB,oBACtE,CAAC,EAGQ,gBAAkB,CAAC,SAC5B,EAAE,OAAO,EAAE,OAAO,CAAC,MAAQ,kBAAkB,IAAK,MAAM,EAAG,CACvD,MAAO,OAAS,kDAAkD,gBAAkB,gDACxF,CAAC,EAGQ,aAAe,IACxB,EAAE,OAAO,EAAE,OAAO,CAAC,MAAQ,eAAe,GAAG,EAAG,CAC5C,MAAO,uBACX,CAAC,EAGQ,UAAY,IACrB,EAAE,OAAa,CAAC,MAAQ,YAAY,GAAG,EAAG,CAAE,MAAO,cAAe,CAAC,EAG1D,SAAW,CAAC,UAAgC,EAAE,QAAQ,EAAE,UAAU,CAAC,MAAQ,WAAW,IAAK,OAAO,CAAC,EAEnG,SAAW,IAAM,EAAE,OAAO,EAAE,OAAO,CAAC,MAAQ,OAAO,GAAG,EAAG,CAClE,MAAO,oBACX,CAAC,EAEY,cAAgB,IACzB,EAAE,OAAO,EAAE,OAAO,CAAC,MAAQ,YAAY,GAAG,EAAG,CACzC,MAAO,yBACX,CAAC,EAGQ,SAAW,IACpB,EAAE,OAAO,EAAE,OAAO,CAAC,MAAQ,WAAW,GAAG,EAAG,CACxC,MAAO,sBACX,CAAC,EAGQ,SAAW,IACpB,EAAE,OAAO,EAAE,OAAO,CAAC,MAAQ,WAAW,GAAG,EAAG,CACxC,MAAO,sBACX,CAAC,EAGQ,kBAAoB,IAC7B,EAAE,OAAO,EAAE,OAAO,CAAC,MAAQ,oBAAoB,GAAG,EAAG,CACjD,MAAO,kCACX,CAAC",
|
|
8
|
+
"debugId": "CA2A82F81B89B66B64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { IsTrueLikeOptions } from '../core';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export declare const PROTOCOL_PATTERNS: {
|
|
4
|
+
readonly file: RegExp;
|
|
5
|
+
readonly ftp: RegExp;
|
|
6
|
+
readonly http: RegExp;
|
|
7
|
+
readonly ssh: RegExp;
|
|
8
|
+
readonly ws: RegExp;
|
|
9
|
+
};
|
|
10
|
+
export type Protocol = keyof typeof PROTOCOL_PATTERNS;
|
|
11
|
+
export declare function url(...protocols: Array<Protocol | Protocol[]>): z.ZodURL;
|
|
12
|
+
export declare const hexString: (length?: number) => z.ZodString;
|
|
13
|
+
export declare const strictHexString: (length?: number) => z.ZodString & z.ZodType<`0x${string}`, string, z.core.$ZodTypeInternals<`0x${string}`, string>>;
|
|
14
|
+
export declare const numberString: () => z.ZodString & z.ZodType<import("..").NumberString<true>, string, z.core.$ZodTypeInternals<import("..").NumberString<true>, string>>;
|
|
15
|
+
export declare const validDate: () => z.ZodCustom<Date, Date>;
|
|
16
|
+
export declare const trueLike: (options?: IsTrueLikeOptions) => z.ZodPipe<z.ZodUnknown, z.ZodTransform<boolean, unknown>>;
|
|
17
|
+
export declare const filePath: () => z.ZodString;
|
|
18
|
+
export declare const directoryPath: () => z.ZodString;
|
|
19
|
+
export declare const readable: () => z.ZodString;
|
|
20
|
+
export declare const writable: () => z.ZodString;
|
|
21
|
+
export declare const writableDirectory: () => z.ZodString;
|
|
22
|
+
//# sourceMappingURL=schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/zod/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAChD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAOvB,eAAO,MAAM,iBAAiB;;;;;;CAM7B,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,iBAAiB,CAAA;AAErD,wBAAgB,GAAG,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,QAAQ,GAAG,QAAQ,EAAE,CAAC,YAM7D;AAED,eAAO,MAAM,SAAS,GAAI,SAAS,MAAM,gBAIxC,CAAA;AAED,eAAO,MAAM,eAAe,GAAI,SAAS,MAAM,oGAI9C,CAAA;AAED,eAAO,MAAM,YAAY,2IAIxB,CAAA;AAED,eAAO,MAAM,SAAS,+BAErB,CAAA;AAED,eAAO,MAAM,QAAQ,GAAI,UAAU,iBAAiB,8DAA6D,CAAA;AAEjH,eAAO,MAAM,QAAQ,mBAEnB,CAAA;AAEF,eAAO,MAAM,aAAa,mBAIzB,CAAA;AAED,eAAO,MAAM,QAAQ,mBAIpB,CAAA;AAED,eAAO,MAAM,QAAQ,mBAIpB,CAAA;AAED,eAAO,MAAM,iBAAiB,mBAI7B,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kdtlabs/utils",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.10",
|
|
5
5
|
"description": "A comprehensive TypeScript utility library",
|
|
6
6
|
"author": "Diep Dang <kdt310722@gmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
".": {
|
|
17
17
|
"types": "./dist/index.d.ts",
|
|
18
18
|
"default": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./zod": {
|
|
21
|
+
"types": "./dist/zod/index.d.ts",
|
|
22
|
+
"default": "./dist/zod/index.js"
|
|
19
23
|
}
|
|
20
24
|
},
|
|
21
25
|
"main": "dist/index.js",
|
|
@@ -38,6 +42,14 @@
|
|
|
38
42
|
"up": "taze -I --group --force --peer",
|
|
39
43
|
"preinstall": "only-allow bun && ([ -d .git ] && simple-git-hooks || true)"
|
|
40
44
|
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"zod": "^4.0.0"
|
|
47
|
+
},
|
|
48
|
+
"peerDependenciesMeta": {
|
|
49
|
+
"zod": {
|
|
50
|
+
"optional": true
|
|
51
|
+
}
|
|
52
|
+
},
|
|
41
53
|
"devDependencies": {
|
|
42
54
|
"@commitlint/cli": "^20.5.0",
|
|
43
55
|
"@commitlint/config-conventional": "^20.5.0",
|
|
@@ -52,7 +64,8 @@
|
|
|
52
64
|
"rimraf": "^6.1.3",
|
|
53
65
|
"simple-git-hooks": "^2.13.1",
|
|
54
66
|
"taze": "^19.10.0",
|
|
55
|
-
"typescript": "^6.0.2"
|
|
67
|
+
"typescript": "^6.0.2",
|
|
68
|
+
"zod": "^4.3.6"
|
|
56
69
|
},
|
|
57
70
|
"simple-git-hooks": {
|
|
58
71
|
"commit-msg": "bunx --no -- commitlint --edit ${1}",
|
package/src/errors/guards.ts
CHANGED
|
@@ -10,3 +10,9 @@ export const isBaseError = (value: unknown): value is BaseError => value instanc
|
|
|
10
10
|
export const isAbortError = (error: unknown): error is DOMException => error instanceof DOMException && error.name === 'AbortError'
|
|
11
11
|
|
|
12
12
|
export const isErrorLike = (value: unknown): value is ErrorLike => isObject(value) && isKeysOf(value, 'name') && isString(value.name)
|
|
13
|
+
|
|
14
|
+
export const isErrnoException = (value: unknown): value is NodeJS.ErrnoException => (
|
|
15
|
+
value instanceof Error && 'code' in value && 'errno' in value
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
export const isMissingDirectoryError = (error: NodeJS.ErrnoException) => error.code === 'ENOENT'
|
package/src/system/fs.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { PathLike } from './types'
|
|
2
|
+
import { accessSync, constants, existsSync, type MakeDirectoryOptions, mkdirSync, statSync } from 'node:fs'
|
|
3
|
+
import { dirname, resolve } from 'node:path'
|
|
4
|
+
import { isErrnoException, isMissingDirectoryError } from '../errors'
|
|
5
|
+
import { pathToString } from './path'
|
|
6
|
+
|
|
7
|
+
export function hasAccess(path: PathLike, mode?: number) {
|
|
8
|
+
try {
|
|
9
|
+
accessSync(pathToString(path), mode)
|
|
10
|
+
|
|
11
|
+
return true
|
|
12
|
+
} catch {
|
|
13
|
+
return false
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const isReadable = (path: PathLike) => hasAccess(path, constants.R_OK)
|
|
18
|
+
|
|
19
|
+
export const isWritable = (path: PathLike) => hasAccess(path, constants.W_OK)
|
|
20
|
+
|
|
21
|
+
export function isFile(path: PathLike) {
|
|
22
|
+
try {
|
|
23
|
+
return statSync(pathToString(path)).isFile()
|
|
24
|
+
} catch {
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function isDirectory(path: PathLike) {
|
|
30
|
+
try {
|
|
31
|
+
return statSync(pathToString(path)).isDirectory()
|
|
32
|
+
} catch {
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function isWritableDirectory(dirPath: PathLike): boolean {
|
|
38
|
+
const resolved = resolve(pathToString(dirPath))
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
accessSync(resolved, constants.W_OK)
|
|
42
|
+
|
|
43
|
+
return true
|
|
44
|
+
} catch (error) {
|
|
45
|
+
if (isErrnoException(error) && isMissingDirectoryError(error)) {
|
|
46
|
+
const parent = dirname(resolved)
|
|
47
|
+
|
|
48
|
+
if (parent === resolved) {
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return isWritableDirectory(parent)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return false
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function ensureDirectory(path: PathLike, options: MakeDirectoryOptions = {}) {
|
|
60
|
+
const resolved = pathToString(path)
|
|
61
|
+
|
|
62
|
+
if (!existsSync(resolved)) {
|
|
63
|
+
mkdirSync(resolved, { recursive: true, ...options })
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { BinaryLike, BinaryToTextEncoding, HashOptions } from 'node:crypto'
|
|
2
|
+
import type { PathLike } from './types'
|
|
3
|
+
import { createHash } from 'node:crypto'
|
|
4
|
+
import { createReadStream, readFileSync } from 'node:fs'
|
|
5
|
+
import { pathToString } from './path'
|
|
6
|
+
|
|
7
|
+
export const computeHash = (data: BinaryLike, algorithm: string, encoding: BinaryToTextEncoding = 'hex', options: HashOptions = {}) => (
|
|
8
|
+
createHash(algorithm, options).update(data).digest(encoding)
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
export const getFileHash = (path: PathLike, algorithm: string, options: HashOptions = {}) => (
|
|
12
|
+
computeHash(readFileSync(pathToString(path)), algorithm, 'hex', options)
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
export const getLargeFileHash = (path: PathLike, algorithm: string, options: HashOptions = {}) => new Promise<string>((resolve, reject) => {
|
|
16
|
+
const hash = createHash(algorithm, options)
|
|
17
|
+
const stream = createReadStream(pathToString(path))
|
|
18
|
+
|
|
19
|
+
stream.on('data', (chunk) => hash.update(chunk))
|
|
20
|
+
stream.on('end', () => resolve(hash.digest('hex')))
|
|
21
|
+
stream.on('error', reject)
|
|
22
|
+
})
|
package/src/system/index.ts
CHANGED
package/src/system/path.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { PathLike } from './types'
|
|
2
|
-
import {
|
|
2
|
+
import { homedir } from 'node:os'
|
|
3
|
+
import { dirname, join, resolve } from 'node:path'
|
|
3
4
|
import { fileURLToPath } from 'node:url'
|
|
4
5
|
import { bufferToString, isBufferLike } from '../buffers'
|
|
5
6
|
|
|
@@ -10,3 +11,13 @@ export const pathToString = (path: PathLike) => (
|
|
|
10
11
|
export const pwd = (importMeta: ImportMeta, ...path: PathLike[]) => (
|
|
11
12
|
join(dirname(fileURLToPath(importMeta.url)), ...path.map(pathToString))
|
|
12
13
|
)
|
|
14
|
+
|
|
15
|
+
export function resolvePath(path: PathLike) {
|
|
16
|
+
const str = pathToString(path)
|
|
17
|
+
|
|
18
|
+
if (str.startsWith('~/')) {
|
|
19
|
+
return resolve(homedir(), str.slice(2))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return resolve(str)
|
|
23
|
+
}
|
package/src/times/operations.ts
CHANGED
|
@@ -82,3 +82,27 @@ const END_RESOLVERS: Record<TimeInterval, (date: DateLike, options: WeekOptions)
|
|
|
82
82
|
export const resolveInterval = (interval: TimeInterval, type: 'end' | 'start' = 'start', { now = new Date(), ...weekOptions }: ResolveIntervalOptions = {}) => (
|
|
83
83
|
(type === 'start' ? START_RESOLVERS : END_RESOLVERS)[interval](now, weekOptions)
|
|
84
84
|
)
|
|
85
|
+
|
|
86
|
+
const INTERVAL_SHIFTERS: Record<TimeInterval, (date: Date, amount: number) => void> = {
|
|
87
|
+
daily: (d, n) => d.setDate(d.getDate() + n),
|
|
88
|
+
hourly: (d, n) => d.setHours(d.getHours() + n),
|
|
89
|
+
monthly: (d, n) => d.setMonth(d.getMonth() + n),
|
|
90
|
+
weekly: (d, n) => d.setDate(d.getDate() + n * 7),
|
|
91
|
+
yearly: (d, n) => d.setFullYear(d.getFullYear() + n),
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function shiftInterval(interval: TimeInterval, amount: number, type: 'end' | 'start' = 'start', { now = new Date(), ...weekOptions }: ResolveIntervalOptions = {}) {
|
|
95
|
+
const shifted = new Date(now)
|
|
96
|
+
|
|
97
|
+
INTERVAL_SHIFTERS[interval](shifted, amount)
|
|
98
|
+
|
|
99
|
+
return (type === 'start' ? START_RESOLVERS : END_RESOLVERS)[interval](shifted, weekOptions)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export const addInterval = (interval: TimeInterval, amount: number, type: 'end' | 'start' = 'start', options?: ResolveIntervalOptions) => (
|
|
103
|
+
shiftInterval(interval, amount, type, options)
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
export const subtractInterval = (interval: TimeInterval, amount: number, type: 'end' | 'start' = 'start', options?: ResolveIntervalOptions) => (
|
|
107
|
+
shiftInterval(interval, -amount, type, options)
|
|
108
|
+
)
|
package/src/zod/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './schemas'
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { IsTrueLikeOptions } from '../core'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import { isTrueLike } from '../core'
|
|
4
|
+
import { isNumberString } from '../numbers'
|
|
5
|
+
import { isHexString, isStrictHexString } from '../strings'
|
|
6
|
+
import { isDirectory, isFile, isReadable, isWritable, isWritableDirectory } from '../system'
|
|
7
|
+
import { isValidDate } from '../times'
|
|
8
|
+
|
|
9
|
+
export const PROTOCOL_PATTERNS = <const>{
|
|
10
|
+
file: /^file$/u,
|
|
11
|
+
ftp: /^ftps?$/u,
|
|
12
|
+
http: /^https?$/u,
|
|
13
|
+
ssh: /^ssh$/u,
|
|
14
|
+
ws: /^wss?$/u,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type Protocol = keyof typeof PROTOCOL_PATTERNS
|
|
18
|
+
|
|
19
|
+
export function url(...protocols: Array<Protocol | Protocol[]>) {
|
|
20
|
+
const flat = protocols.flat()
|
|
21
|
+
const patterns = flat.map((p) => PROTOCOL_PATTERNS[p])
|
|
22
|
+
const combined = new RegExp(`^(?:${patterns.map((r) => r.source.slice(1, -1)).join('|')})$`)
|
|
23
|
+
|
|
24
|
+
return z.url({ protocol: combined })
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const hexString = (length?: number) => (
|
|
28
|
+
z.string().refine((val) => isHexString(val, length), {
|
|
29
|
+
error: length ? `Invalid hex string (expected ${length} bytes)` : 'Invalid hex string',
|
|
30
|
+
})
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
export const strictHexString = (length?: number) => (
|
|
34
|
+
z.string().refine((val) => isStrictHexString(val, length), {
|
|
35
|
+
error: length ? `Invalid strict hex string (expected 0x prefix, ${length} bytes)` : 'Invalid strict hex string (expected 0x prefix)',
|
|
36
|
+
})
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
export const numberString = () => (
|
|
40
|
+
z.string().refine((val) => isNumberString(val), {
|
|
41
|
+
error: 'Invalid number string',
|
|
42
|
+
})
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
export const validDate = () => (
|
|
46
|
+
z.custom<Date>((val) => isValidDate(val), { error: 'Invalid date' })
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
export const trueLike = (options?: IsTrueLikeOptions) => z.unknown().transform((val) => isTrueLike(val, options))
|
|
50
|
+
|
|
51
|
+
export const filePath = () => z.string().refine((val) => isFile(val), {
|
|
52
|
+
error: 'Path is not a file',
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
export const directoryPath = () => (
|
|
56
|
+
z.string().refine((val) => isDirectory(val), {
|
|
57
|
+
error: 'Path is not a directory',
|
|
58
|
+
})
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
export const readable = () => (
|
|
62
|
+
z.string().refine((val) => isReadable(val), {
|
|
63
|
+
error: 'Path is not readable',
|
|
64
|
+
})
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
export const writable = () => (
|
|
68
|
+
z.string().refine((val) => isWritable(val), {
|
|
69
|
+
error: 'Path is not writable',
|
|
70
|
+
})
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
export const writableDirectory = () => (
|
|
74
|
+
z.string().refine((val) => isWritableDirectory(val), {
|
|
75
|
+
error: 'Path is not a writable directory',
|
|
76
|
+
})
|
|
77
|
+
)
|