pa-ruler-factory 0.0.14

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 puijs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,297 @@
1
+ # ruler-factory
2
+
3
+ [English](./README.md) · [简体中文](./README.zh-CN.md)
4
+
5
+ A flexible, chainable validation rule factory for TypeScript/JavaScript.
6
+
7
+ ## Features
8
+
9
+ - Chainable API for building complex validation rules
10
+ - Supports string, number, array, boolean, object, symbol, bigint, null, undefined
11
+ - Customizable error messages
12
+ - Easy to extend and integrate
13
+ - TypeScript support
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pnpm add ruler-factory
19
+ # or
20
+ npm install ruler-factory
21
+ # or
22
+ yarn add ruler-factory
23
+ ```
24
+
25
+ ## Integration with UI frameworks
26
+
27
+ ### Pui UI
28
+
29
+ ```vue
30
+ <script setup lang="ts">
31
+ import { ref } from 'vue'
32
+ import { rulerFactory } from 'ruler-factory'
33
+
34
+ const r = rulerFactory((validator) => {
35
+ return (value) => {
36
+ const e = validator(value)
37
+ return e ? e.message : true
38
+ }
39
+ })
40
+
41
+ const model = ref({
42
+ name: '',
43
+ email: '',
44
+ })
45
+ </script>
46
+
47
+ <template>
48
+ <var-form>
49
+ <var-input
50
+ v-model="model.name"
51
+ placeholder="Name"
52
+ :rules="r().required('Required').min(2, 'Wrong length').done()"
53
+ />
54
+ <var-input v-model="model.age" placeholder="Email" :rules="r().email('Must be email format').done()" />
55
+ </var-form>
56
+ </template>
57
+ ```
58
+
59
+ ### Vant
60
+
61
+ ```vue
62
+ <script setup lang="ts">
63
+ import { ref } from 'vue'
64
+ import { rulerFactory } from 'ruler-factory'
65
+ import type { FieldRule } from 'vant'
66
+
67
+ const r = rulerFactory<FieldRule>((validator, params) => ({
68
+ validator(value) {
69
+ const e = validator(value)
70
+
71
+ return e ? e.message : true
72
+ },
73
+ trigger: ['onChange', 'onBlur', 'onSubmit'],
74
+ ...params,
75
+ }))
76
+
77
+ const model = ref({
78
+ name: '',
79
+ email: '',
80
+ })
81
+ </script>
82
+
83
+ <template>
84
+ <van-form>
85
+ <van-cell-group inset>
86
+ <van-field v-model="model.name" label="Name" placeholder="Name" :rules="r().required('Required').done()" />
87
+ <van-field
88
+ v-model="model.email"
89
+ label="Email"
90
+ placeholder="Email"
91
+ :rules="r().email('Must be email format').done()"
92
+ />
93
+ </van-cell-group>
94
+ </van-form>
95
+ </template>
96
+ ```
97
+
98
+ ### Naive UI
99
+
100
+ ```vue
101
+ <script setup lang="ts">
102
+ import { ref } from 'vue'
103
+ import type { FormItemRule } from 'naive-ui'
104
+ import { rulerFactory } from 'ruler-factory'
105
+
106
+ const r = rulerFactory<FormItemRule>((validator, params = {}) => ({
107
+ trigger: ['blur', 'change', 'input'],
108
+ validator: (_, value) => validator(value),
109
+ ...params,
110
+ }))
111
+
112
+ const model = ref({
113
+ name: '',
114
+ age: 20,
115
+ })
116
+ </script>
117
+
118
+ <template>
119
+ <n-form :model>
120
+ <n-form-item path="name" label="Name" :rule="r().required('Required').min(2, 'Wrong length').done()">
121
+ <n-input v-model:value="model.name" />
122
+ </n-form-item>
123
+ <n-form-item path="age" label="Age" :rule="r().number().required('Required').negative('Must be negative').done()">
124
+ <n-input-number v-model:value="model.age" />
125
+ </n-form-item>
126
+ </n-form>
127
+ </template>
128
+ ```
129
+
130
+ ### Element Plus
131
+
132
+ ```vue
133
+ <script setup lang="ts">
134
+ import { ref } from 'vue'
135
+ import type { FormItemRule } from 'element-plus'
136
+ import { rulerFactory } from 'ruler-factory'
137
+
138
+ const r = rulerFactory<FormItemRule>((validator, params) => ({
139
+ validator(_, value, callback) {
140
+ const e = validator(value)
141
+
142
+ e ? callback(e) : callback()
143
+ },
144
+ trigger: ['blur', 'change', 'input'],
145
+ ...params,
146
+ }))
147
+
148
+ const model = ref({
149
+ name: '',
150
+ email: '',
151
+ })
152
+ </script>
153
+
154
+ <template>
155
+ <el-form :model>
156
+ <el-form-item prop="name" label="Name" :rules="r().required('Required').done()">
157
+ <el-input v-model="model.name" />
158
+ </el-form-item>
159
+ <el-form-item prop="email" label="Email" :rules="r().email('Must be email format').done()">
160
+ <el-input v-model="model.email" />
161
+ </el-form-item>
162
+ </el-form>
163
+ </template>
164
+ ```
165
+
166
+ ### Extend API
167
+
168
+ Take `Naive UI` as an example
169
+
170
+ ```ts
171
+ import { FormItemRule } from 'naive-ui'
172
+ import { RulerContext, rulerFactory, RulerFactoryMessage } from 'ruler-factory'
173
+
174
+ interface RulerExtendedContext {
175
+ ip(message: RulerFactoryMessage, params?: FormItemRule): RulerContext<FormItemRule, FormItemRule, this>
176
+ }
177
+
178
+ const r = rulerFactory<FormItemRule, FormItemRule, RulerExtendedContext>(
179
+ (validator, params = {}) => ({
180
+ trigger: ['blur', 'change', 'input'],
181
+ validator: (_, value) => validator(value),
182
+ ...params,
183
+ }),
184
+ (ctx) => {
185
+ function ip(message: RulerFactoryMessage, params?: FormItemRule) {
186
+ ctx.addRule((value) => {
187
+ // Implement isString and isIP by yourself
188
+ if (!isString(value) || !isIP(value)) {
189
+ return new Error(ctx.getMessage(message))
190
+ }
191
+ }, params)
192
+
193
+ return ctx
194
+ }
195
+
196
+ return { ip }
197
+ },
198
+ )
199
+
200
+ r().ip('ip format error').done()
201
+ ```
202
+
203
+ ## API
204
+
205
+ ### Types Validation
206
+
207
+ - `.string(message?, params?)`
208
+ - `.number(message?, params?)`
209
+ - `.array(message?, params?)`
210
+ - `.boolean(message?, params?)`
211
+ - `.object(message?, params?)`
212
+ - `.symbol(message?, params?)`
213
+ - `.bigint(message?, params?)`
214
+ - `.null(message?, params?)`
215
+ - `.undefined(message?, params?)`
216
+ - `.true(message?, params?)`
217
+ - `.false(message?, params?)`
218
+
219
+ ### Non Empty Validation
220
+
221
+ - `.required(message)`
222
+
223
+ ### String Validation
224
+
225
+ - `.min(value, message, params?)`
226
+ - `.max(value, message, params?)`
227
+ - `.length(value, message, params?)`
228
+ - `.regex(regexp, message, params?)`
229
+ - `.startsWith(value, message, params?)`
230
+ - `.endsWith(value, message, params?)`
231
+ - `.includes(value, message, params?)`
232
+ - `.uppercase(message, params?)`
233
+ - `.lowercase(message, params?)`
234
+ - `.email(message, params?)`
235
+
236
+ ### Number Validation
237
+
238
+ - `.number().min(value, message, params?)`
239
+ - `.number().max(value, message, params?)`
240
+ - `.number().gt(value, message, params?)`
241
+ - `.number().gte(value, message, params?)` alias .min
242
+ - `.number().lt(value, message, params?)`
243
+ - `.number().lte(value, message, params?)` alias .max
244
+ - `.number().positive(value, message, params?)`
245
+ - `.number().negative(value, message, params?)`
246
+
247
+ ### Bigint Validation
248
+
249
+ - `.bigint().min(value, message, params?)`
250
+ - `.bigint().max(value, message, params?)`
251
+ - `.bigint().gt(value, message, params?)`
252
+ - `.bigint().gte(value, message, params?)` alias .min
253
+ - `.bigint().lt(value, message, params?)`
254
+ - `.bigint().lte(value, message, params?)` alias .max
255
+ - `.bigint().positive(value, message, params?)`
256
+ - `.bigint().negative(value, message, params?)`
257
+
258
+ ### Array Validation
259
+
260
+ - `.array().min(value, message, params?)`
261
+ - `.array().max(value, message, params?)`
262
+ - `.array().length(value, message, params?)`
263
+ - `.array().includes(value, message, params?)`
264
+
265
+ ### Predicate Validation
266
+
267
+ - `.is(fn, message, params?)`
268
+ - `.not(fn, message, params?)`
269
+
270
+ ### Complete rule building
271
+
272
+ - `.done()`
273
+
274
+ ### Custom Rule
275
+
276
+ - `.addRule(validator)`
277
+
278
+ ### Value Transformer
279
+
280
+ - `.trim()`
281
+ - `.toLowerCase()`
282
+ - `.toUpperCase()`
283
+ - `.transform(fn)`
284
+
285
+ ## License
286
+
287
+ MIT
288
+
289
+ ## Links
290
+
291
+ - [GitHub Repository](https://github.com/puijs/ruler-factory)
292
+ - [Issues](https://github.com/puijs/ruler-factory/issues)
293
+
294
+ ## Inspired By
295
+
296
+ [`zod`](https://zod.dev/)
297
+ [`yup`](https://github.com/jquense/yup)
@@ -0,0 +1,288 @@
1
+ # ruler-factory
2
+
3
+ [English](./README.md) · [简体中文](./README.zh-CN.md)
4
+
5
+ 一个灵活、可链式调用的 TypeScript/JavaScript 校验规则工厂。
6
+
7
+ ## 特性
8
+
9
+ - 链式 API,可构建复杂校验规则
10
+ - 支持 string、number、array、boolean、object、symbol、bigint、null、undefined
11
+ - 可自定义错误信息
12
+ - 易于扩展与集成
13
+ - 支持 TypeScript
14
+
15
+ ## 安装
16
+
17
+ ```bash
18
+ pnpm add ruler-factory
19
+ # 或
20
+ npm install ruler-factory
21
+ # 或
22
+ yarn add ruler-factory
23
+ ```
24
+
25
+ ## 与 UI 框架集成
26
+
27
+ ### Pui UI
28
+
29
+ ```vue
30
+ <script setup lang="ts">
31
+ import { ref } from 'vue'
32
+ import { rulerFactory } from 'ruler-factory'
33
+
34
+ const r = rulerFactory((validator) => {
35
+ return (value) => {
36
+ const e = validator(value)
37
+ return e ? e.message : true
38
+ }
39
+ })
40
+
41
+ const model = ref({
42
+ name: '',
43
+ email: '',
44
+ })
45
+ </script>
46
+
47
+ <template>
48
+ <var-form>
49
+ <var-input v-model="model.name" placeholder="姓名" :rules="r().required('必填').min(2, '长度不正确').done()" />
50
+ <var-input v-model="model.age" placeholder="邮箱" :rules="r().email('必须是邮箱格式').done()" />
51
+ </var-form>
52
+ </template>
53
+ ```
54
+
55
+ ### Vant
56
+
57
+ ```vue
58
+ <script setup lang="ts">
59
+ import { ref } from 'vue'
60
+ import { rulerFactory } from 'ruler-factory'
61
+ import type { FieldRule } from 'vant'
62
+
63
+ const r = rulerFactory<FieldRule>((validator, params) => ({
64
+ validator(value) {
65
+ const e = validator(value)
66
+
67
+ return e ? e.message : true
68
+ },
69
+ trigger: ['onChange', 'onBlur', 'onSubmit'],
70
+ ...params,
71
+ }))
72
+
73
+ const model = ref({
74
+ name: '',
75
+ email: '',
76
+ })
77
+ </script>
78
+
79
+ <template>
80
+ <van-form>
81
+ <van-cell-group inset>
82
+ <van-field v-model="model.name" label="姓名" placeholder="姓名" :rules="r().required('必填').done()" />
83
+ <van-field v-model="model.email" label="邮箱" placeholder="邮箱" :rules="r().email('必须是邮箱格式').done()" />
84
+ </van-cell-group>
85
+ </van-form>
86
+ </template>
87
+ ```
88
+
89
+ ### Naive UI
90
+
91
+ ```vue
92
+ <script setup lang="ts">
93
+ import { ref } from 'vue'
94
+ import type { FormItemRule } from 'naive-ui'
95
+ import { rulerFactory } from 'ruler-factory'
96
+
97
+ const r = rulerFactory<FormItemRule>((validator, params = {}) => ({
98
+ trigger: ['blur', 'change', 'input'],
99
+ validator: (_, value) => validator(value),
100
+ ...params,
101
+ }))
102
+
103
+ const model = ref({
104
+ name: '',
105
+ age: 20,
106
+ })
107
+ </script>
108
+
109
+ <template>
110
+ <n-form :model>
111
+ <n-form-item path="name" label="姓名" :rule="r().required('必填').min(2, '长度不正确').done()">
112
+ <n-input v-model:value="model.name" />
113
+ </n-form-item>
114
+ <n-form-item path="age" label="年龄" :rule="r().number().required('必填').negative('必须为负数').done()">
115
+ <n-input-number v-model:value="model.age" />
116
+ </n-form-item>
117
+ </n-form>
118
+ </template>
119
+ ```
120
+
121
+ ### Element Plus
122
+
123
+ ```vue
124
+ <script setup lang="ts">
125
+ import { ref } from 'vue'
126
+ import type { FormItemRule } from 'element-plus'
127
+ import { rulerFactory } from 'ruler-factory'
128
+
129
+ const r = rulerFactory<FormItemRule>((validator, params) => ({
130
+ validator(_, value, callback) {
131
+ const e = validator(value)
132
+
133
+ e ? callback(e) : callback()
134
+ },
135
+ trigger: ['blur', 'change', 'input'],
136
+ ...params,
137
+ }))
138
+
139
+ const model = ref({
140
+ name: '',
141
+ email: '',
142
+ })
143
+ </script>
144
+
145
+ <template>
146
+ <el-form :model>
147
+ <el-form-item prop="name" label="姓名" :rules="r().required('必填').done()">
148
+ <el-input v-model="model.name" />
149
+ </el-form-item>
150
+ <el-form-item prop="email" label="邮箱" :rules="r().email('必须是邮箱格式').done()">
151
+ <el-input v-model="model.email" />
152
+ </el-form-item>
153
+ </el-form>
154
+ </template>
155
+ ```
156
+
157
+ ### 扩展 API
158
+
159
+ 以 Naive UI 为例
160
+
161
+ ```ts
162
+ import { FormItemRule } from 'naive-ui'
163
+ import { RulerContext, rulerFactory, RulerFactoryMessage } from 'ruler-factory'
164
+
165
+ interface RulerExtendedContext {
166
+ ip(message: RulerFactoryMessage, params?: FormItemRule): RulerContext<FormItemRule, FormItemRule, this>
167
+ }
168
+
169
+ const r = rulerFactory<FormItemRule, FormItemRule, RulerExtendedContext>(
170
+ (validator, params = {}) => ({
171
+ trigger: ['blur', 'change', 'input'],
172
+ validator: (_, value) => validator(value),
173
+ ...params,
174
+ }),
175
+ (ctx) => {
176
+ function ip(message: RulerFactoryMessage, params?: FormItemRule) {
177
+ ctx.addRule((value) => {
178
+ // 自行实现 isString 和 isIP
179
+ if (!isString(value) || !isIP(value)) {
180
+ return new Error(ctx.getMessage(message))
181
+ }
182
+ }, params)
183
+
184
+ return ctx
185
+ }
186
+
187
+ return { ip }
188
+ },
189
+ )
190
+
191
+ r().ip('IP 格式错误').done()
192
+ ```
193
+
194
+ ## API
195
+
196
+ ### 类型校验
197
+
198
+ - `.string(message?, params?)`
199
+ - `.number(message?, params?)`
200
+ - `.array(message?, params?)`
201
+ - `.boolean(message?, params?)`
202
+ - `.object(message?, params?)`
203
+ - `.symbol(message?, params?)`
204
+ - `.bigint(message?, params?)`
205
+ - `.null(message?, params?)`
206
+ - `.undefined(message?, params?)`
207
+ - `.true(message?, params?)`
208
+ - `.false(message?, params?)`
209
+
210
+ ### 非空校验
211
+
212
+ - `.required(message)`
213
+
214
+ ### 字符串校验
215
+
216
+ - `.min(value, message, params?)`
217
+ - `.max(value, message, params?)`
218
+ - `.length(value, message, params?)`
219
+ - `.regex(regexp, message, params?)`
220
+ - `.startsWith(value, message, params?)`
221
+ - `.endsWith(value, message, params?)`
222
+ - `.includes(value, message, params?)`
223
+ - `.uppercase(message, params?)`
224
+ - `.lowercase(message, params?)`
225
+ - `.email(message, params?)`
226
+
227
+ ### 数字校验
228
+
229
+ - `.number().min(value, message, params?)`
230
+ - `.number().max(value, message, params?)`
231
+ - `.number().gt(value, message, params?)`
232
+ - `.number().gte(value, message, params?)` 别名 .min
233
+ - `.number().lt(value, message, params?)`
234
+ - `.number().lte(value, message, params?)` 别名 .max
235
+ - `.number().positive(value, message, params?)`
236
+ - `.number().negative(value, message, params?)`
237
+
238
+ ### Bigint 校验
239
+
240
+ - `.bigint().min(value, message, params?)`
241
+ - `.bigint().max(value, message, params?)`
242
+ - `.bigint().gt(value, message, params?)`
243
+ - `.bigint().gte(value, message, params?)` 别名 .min
244
+ - `.bigint().lt(value, message, params?)`
245
+ - `.bigint().lte(value, message, params?)` 别名 .max
246
+ - `.bigint().positive(value, message, params?)`
247
+ - `.bigint().negative(value, message, params?)`
248
+
249
+ ### 数组校验
250
+
251
+ - `.array().min(value, message, params?)`
252
+ - `.array().max(value, message, params?)`
253
+ - `.array().length(value, message, params?)`
254
+ - `.array().includes(value, message, params?)`
255
+
256
+ ### 谓词校验
257
+
258
+ - `.is(fn, message, params?)`
259
+ - `.not(fn, message, params?)`
260
+
261
+ ### 完成规则构建
262
+
263
+ - `.done()`
264
+
265
+ ### 自定义规则
266
+
267
+ - `.addRule(validator)`
268
+
269
+ ### 值转换
270
+
271
+ - `.trim()`
272
+ - `.toLowerCase()`
273
+ - `.toUpperCase()`
274
+ - `.transform(fn)`
275
+
276
+ ## 许可证
277
+
278
+ MIT
279
+
280
+ ## 链接
281
+
282
+ - [GitHub 仓库](https://github.com/puijs/ruler-factory)
283
+ - [Issues](https://github.com/puijs/ruler-factory/issues)
284
+
285
+ ## 灵感来源
286
+
287
+ [`zod`](https://zod.dev/)
288
+ [`yup`](https://github.com/jquense/yup)
@@ -0,0 +1,55 @@
1
+ //#region src/index.d.ts
2
+ type RulerFactoryMessage = string | (() => string);
3
+ type RulerFactoryValidator = (value: any) => undefined | Error;
4
+ type RulerFactoryGenerator<R, P> = (validator: RulerFactoryValidator, params?: P) => R;
5
+ type RulerContext<R, P, E> = Omit<{
6
+ rules: R[];
7
+ addRule: (validator: RulerFactoryValidator, params?: P) => RulerContext<R, P, E>;
8
+ generator: RulerFactoryGenerator<R, P>;
9
+ getMessage: (message: RulerFactoryMessage) => string;
10
+ type: string;
11
+ string: (message?: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
12
+ number: (message?: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
13
+ array: (message?: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
14
+ boolean: (message?: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
15
+ object: (message?: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
16
+ symbol: (message?: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
17
+ bigint: (message?: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
18
+ null: (message?: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
19
+ undefined: (message?: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
20
+ true: (message?: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
21
+ false: (message?: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
22
+ length: (v: number, message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
23
+ min: (v: number | bigint, message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
24
+ max: (v: number | bigint, message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
25
+ regex: (v: RegExp, message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
26
+ required: (message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
27
+ startsWith: (v: string, message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
28
+ endsWith: (v: string, message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
29
+ includes: (v: string, message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
30
+ uppercase: (message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
31
+ lowercase: (message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
32
+ email: (message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
33
+ gt: (v: number | bigint, message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
34
+ gte: (v: number | bigint, message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
35
+ lt: (v: number | bigint, message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
36
+ lte: (v: number | bigint, message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
37
+ positive: (message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
38
+ negative: (message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
39
+ uniq: (message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
40
+ uniqBy: (v: (a: any, b: any) => any, message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
41
+ is: (v: (value: any) => boolean, message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
42
+ not: (v: (value: any) => boolean, message: RulerFactoryMessage, params?: P) => RulerContext<R, P, E>;
43
+ done: () => R[];
44
+ transformer: (value: any) => any;
45
+ transform: (v: (value: any) => any) => void;
46
+ trim: () => RulerContext<R, P, E>;
47
+ shouldTrim: boolean;
48
+ toLowerCase: () => RulerContext<R, P, E>;
49
+ shouldToLowerCase: boolean;
50
+ toUpperCase: () => RulerContext<R, P, E>;
51
+ shouldToUpperCase: boolean;
52
+ }, keyof E> & E;
53
+ declare function rulerFactory<R, P = R, E extends Record<string, any> = {}>(generator: RulerFactoryGenerator<R, P>, extend?: (ctx: RulerContext<R, P, E>) => E): () => RulerContext<R, P, E>;
54
+ //#endregion
55
+ export { RulerContext, RulerFactoryGenerator, RulerFactoryMessage, RulerFactoryValidator, rulerFactory };
package/dist/index.mjs ADDED
@@ -0,0 +1,340 @@
1
+ import { hasDuplicates, hasDuplicatesBy, isArray, isBoolean, isEmpty, isFunction, isNumber, isPlainObject, isString, isSymbol } from "rattail";
2
+ //#region src/index.ts
3
+ const EMAIL_REGEX = /^(?!\.)(?!.*\.\.)([a-z0-9_'+\-\\.]*)[a-z0-9_+-]@([a-z0-9][a-z0-9\\-]*\.)+[a-z]{2,}$/i;
4
+ function rulerFactory(generator, extend) {
5
+ return function ruler() {
6
+ const rules = [];
7
+ const ctx = {
8
+ rules,
9
+ addRule,
10
+ generator,
11
+ getMessage,
12
+ type: "string",
13
+ string,
14
+ number,
15
+ array,
16
+ boolean,
17
+ object,
18
+ symbol,
19
+ bigint,
20
+ null: _null,
21
+ undefined: _undefined,
22
+ true: _true,
23
+ false: _false,
24
+ length,
25
+ min,
26
+ max,
27
+ regex,
28
+ required,
29
+ startsWith,
30
+ endsWith,
31
+ includes,
32
+ uppercase,
33
+ lowercase,
34
+ email,
35
+ gt,
36
+ gte,
37
+ lt,
38
+ lte,
39
+ positive,
40
+ negative,
41
+ uniq,
42
+ uniqBy,
43
+ is,
44
+ not,
45
+ done,
46
+ transformer,
47
+ transform,
48
+ trim,
49
+ shouldTrim: false,
50
+ toLowerCase,
51
+ shouldToLowerCase: false,
52
+ toUpperCase,
53
+ shouldToUpperCase: false
54
+ };
55
+ Object.assign(ctx, extend?.(ctx) ?? {});
56
+ function string(message, params) {
57
+ ctx.type = "string";
58
+ addRule((value) => {
59
+ if (message == null) return;
60
+ if (!isString(value)) return new Error(getMessage(message));
61
+ }, params);
62
+ return ctx;
63
+ }
64
+ function number(message, params) {
65
+ ctx.type = "number";
66
+ addRule((value) => {
67
+ if (message == null) return;
68
+ if (!isNumber(value)) return new Error(getMessage(message));
69
+ }, params);
70
+ return ctx;
71
+ }
72
+ function object(message, params) {
73
+ ctx.type = "object";
74
+ addRule((value) => {
75
+ if (message == null) return;
76
+ if (!isPlainObject(value)) return new Error(getMessage(message));
77
+ }, params);
78
+ return ctx;
79
+ }
80
+ function array(message, params) {
81
+ ctx.type = "array";
82
+ addRule((value) => {
83
+ if (message == null) return;
84
+ if (!isArray(value)) return new Error(getMessage(message));
85
+ }, params);
86
+ return ctx;
87
+ }
88
+ function boolean(message, params) {
89
+ ctx.type = "boolean";
90
+ addRule((value) => {
91
+ if (message == null) return;
92
+ if (!isBoolean(value)) return new Error(getMessage(message));
93
+ }, params);
94
+ return ctx;
95
+ }
96
+ function _true(message, params) {
97
+ ctx.type = "boolean";
98
+ addRule((value) => {
99
+ if (message == null) return;
100
+ if (value !== true) return new Error(getMessage(message));
101
+ }, params);
102
+ return ctx;
103
+ }
104
+ function _false(message, params) {
105
+ ctx.type = "boolean";
106
+ addRule((value) => {
107
+ if (message == null) return;
108
+ if (value !== false) return new Error(getMessage(message));
109
+ }, params);
110
+ return ctx;
111
+ }
112
+ function symbol(message, params) {
113
+ ctx.type = "symbol";
114
+ addRule((value) => {
115
+ if (message == null) return;
116
+ if (!isSymbol(value)) return new Error(getMessage(message));
117
+ }, params);
118
+ return ctx;
119
+ }
120
+ function _null(message, params) {
121
+ ctx.type = "null";
122
+ addRule((value) => {
123
+ if (message == null) return;
124
+ if (value !== null) return new Error(getMessage(message));
125
+ }, params);
126
+ return ctx;
127
+ }
128
+ function _undefined(message, params) {
129
+ ctx.type = "undefined";
130
+ addRule((value) => {
131
+ if (message == null) return;
132
+ if (value !== void 0) return new Error(getMessage(message));
133
+ }, params);
134
+ return ctx;
135
+ }
136
+ function bigint(message, params) {
137
+ ctx.type = "bigint";
138
+ addRule((value) => {
139
+ if (message == null) return;
140
+ if (typeof value !== "bigint") return new Error(getMessage(message));
141
+ }, params);
142
+ return ctx;
143
+ }
144
+ function required(message, params) {
145
+ addRule((value) => {
146
+ if (isEmpty(value)) return new Error(getMessage(message));
147
+ }, params);
148
+ return ctx;
149
+ }
150
+ function email(message, params) {
151
+ addRule((value) => {
152
+ if (ctx.type === "string" && (!isString(value) || !EMAIL_REGEX.test(value))) return new Error(getMessage(message));
153
+ }, params);
154
+ return ctx;
155
+ }
156
+ function min(v, message, params) {
157
+ addRule((value) => {
158
+ if (ctx.type === "string" && (!isString(value) || value.length < v)) return new Error(getMessage(message));
159
+ if (ctx.type === "number" || ctx.type === "bigint") {
160
+ if (!isNumber(value) && typeof value !== "bigint") return new Error(getMessage(message));
161
+ if (value < v) return new Error(getMessage(message));
162
+ }
163
+ if (ctx.type === "array" && (!isArray(value) || value.length < v)) return new Error(getMessage(message));
164
+ }, params);
165
+ return ctx;
166
+ }
167
+ function max(v, message, params) {
168
+ addRule((value) => {
169
+ if (ctx.type === "string" && (!isString(value) || value.length > v)) return new Error(getMessage(message));
170
+ if (ctx.type === "number" || ctx.type === "bigint") {
171
+ if (!isNumber(value) && typeof value !== "bigint") return new Error(getMessage(message));
172
+ if (value > v) return new Error(getMessage(message));
173
+ }
174
+ if (ctx.type === "array" && (!isArray(value) || value.length > v)) return new Error(getMessage(message));
175
+ }, params);
176
+ return ctx;
177
+ }
178
+ function length(v, message, params) {
179
+ addRule((value) => {
180
+ if (ctx.type === "string" && (!isString(value) || value.length !== v)) return new Error(getMessage(message));
181
+ if (ctx.type === "array" && (!isArray(value) || value.length !== v)) return new Error(getMessage(message));
182
+ }, params);
183
+ return ctx;
184
+ }
185
+ function regex(v, message, params) {
186
+ addRule((value) => {
187
+ if (ctx.type === "string" && (!isString(value) || !v.test(value))) return new Error(getMessage(message));
188
+ }, params);
189
+ return ctx;
190
+ }
191
+ function startsWith(v, message, params) {
192
+ addRule((value) => {
193
+ if (ctx.type === "string" && (!isString(value) || !value.startsWith(v))) return new Error(getMessage(message));
194
+ }, params);
195
+ return ctx;
196
+ }
197
+ function endsWith(v, message, params) {
198
+ addRule((value) => {
199
+ if (ctx.type === "string" && (!isString(value) || !value.endsWith(v))) return new Error(getMessage(message));
200
+ }, params);
201
+ return ctx;
202
+ }
203
+ function includes(v, message, params) {
204
+ addRule((value) => {
205
+ if (ctx.type === "string" && (!isString(value) || !value.includes(v))) return new Error(getMessage(message));
206
+ if (ctx.type === "array" && (!isArray(value) || !value.includes(v))) return new Error(getMessage(message));
207
+ }, params);
208
+ return ctx;
209
+ }
210
+ function uppercase(message, params) {
211
+ addRule((value) => {
212
+ if (ctx.type === "string" && (!isString(value) || value !== value.toUpperCase())) return new Error(getMessage(message));
213
+ }, params);
214
+ return ctx;
215
+ }
216
+ function lowercase(message, params) {
217
+ addRule((value) => {
218
+ if (ctx.type === "string" && (!isString(value) || value !== value.toLowerCase())) return new Error(getMessage(message));
219
+ }, params);
220
+ return ctx;
221
+ }
222
+ function gt(v, message, params) {
223
+ addRule((value) => {
224
+ if (ctx.type === "number" || ctx.type === "bigint") {
225
+ if (!isNumber(value) && typeof value !== "bigint") return new Error(getMessage(message));
226
+ if (value <= v) return new Error(getMessage(message));
227
+ }
228
+ }, params);
229
+ return ctx;
230
+ }
231
+ function gte(v, message, params) {
232
+ addRule((value) => {
233
+ if (ctx.type === "number" || ctx.type === "bigint") {
234
+ if (!isNumber(value) && typeof value !== "bigint") return new Error(getMessage(message));
235
+ if (value < v) return new Error(getMessage(message));
236
+ }
237
+ }, params);
238
+ return ctx;
239
+ }
240
+ function lt(v, message, params) {
241
+ addRule((value) => {
242
+ if (ctx.type === "number" || ctx.type === "bigint") {
243
+ if (!isNumber(value) && typeof value !== "bigint") return new Error(getMessage(message));
244
+ if (value >= v) return new Error(getMessage(message));
245
+ }
246
+ }, params);
247
+ return ctx;
248
+ }
249
+ function lte(v, message, params) {
250
+ addRule((value) => {
251
+ if (ctx.type === "number" || ctx.type === "bigint") {
252
+ if (!isNumber(value) && typeof value !== "bigint") return new Error(getMessage(message));
253
+ if (value > v) return new Error(getMessage(message));
254
+ }
255
+ }, params);
256
+ return ctx;
257
+ }
258
+ function positive(message, params) {
259
+ addRule((value) => {
260
+ if (ctx.type === "number" || ctx.type === "bigint") {
261
+ if (!isNumber(value) && typeof value !== "bigint") return new Error(getMessage(message));
262
+ if (value <= 0) return new Error(getMessage(message));
263
+ }
264
+ }, params);
265
+ return ctx;
266
+ }
267
+ function negative(message, params) {
268
+ addRule((value) => {
269
+ if (ctx.type === "number" || ctx.type === "bigint") {
270
+ if (!isNumber(value) && typeof value !== "bigint") return new Error(getMessage(message));
271
+ if (value >= 0) return new Error(getMessage(message));
272
+ }
273
+ }, params);
274
+ return ctx;
275
+ }
276
+ function uniq(message, params) {
277
+ addRule((value) => {
278
+ if (ctx.type === "array" && hasDuplicates(value)) return new Error(getMessage(message));
279
+ }, params);
280
+ return ctx;
281
+ }
282
+ function uniqBy(v, message, params) {
283
+ addRule((value) => {
284
+ if (ctx.type === "array" && hasDuplicatesBy(value, v)) return new Error(getMessage(message));
285
+ }, params);
286
+ return ctx;
287
+ }
288
+ function is(v, message, params) {
289
+ addRule((value) => {
290
+ if (!v(value)) return new Error(getMessage(message));
291
+ }, params);
292
+ return ctx;
293
+ }
294
+ function not(v, message, params) {
295
+ addRule((value) => {
296
+ if (v(value)) return new Error(getMessage(message));
297
+ }, params);
298
+ return ctx;
299
+ }
300
+ function trim() {
301
+ ctx.shouldTrim = true;
302
+ return ctx;
303
+ }
304
+ function toLowerCase() {
305
+ ctx.shouldToLowerCase = true;
306
+ return ctx;
307
+ }
308
+ function toUpperCase() {
309
+ ctx.shouldToUpperCase = true;
310
+ return ctx;
311
+ }
312
+ function transformer(value) {
313
+ if (ctx.type === "string" && isString(value)) {
314
+ ctx.shouldTrim && (value = value.trim());
315
+ ctx.shouldToLowerCase && (value = value.toLowerCase());
316
+ ctx.shouldToUpperCase && (value = value.toUpperCase());
317
+ }
318
+ return value;
319
+ }
320
+ function transform(v) {
321
+ ctx.transformer = v;
322
+ }
323
+ function addRule(validator, params) {
324
+ rules.push(generator((value) => {
325
+ value = ctx.transformer(value);
326
+ return validator(value);
327
+ }, params));
328
+ return ctx;
329
+ }
330
+ function done() {
331
+ return rules;
332
+ }
333
+ function getMessage(message) {
334
+ return isFunction(message) ? message() : message;
335
+ }
336
+ return ctx;
337
+ };
338
+ }
339
+ //#endregion
340
+ export { rulerFactory };
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "pa-ruler-factory",
3
+ "version": "0.0.14",
4
+ "description": "A flexible, chainable validation rule factory for typeScript/javaScript.",
5
+ "keywords": [
6
+ "chainable",
7
+ "data",
8
+ "factory",
9
+ "form",
10
+ "javascript",
11
+ "rules",
12
+ "validation",
13
+ "validator"
14
+ ],
15
+ "bugs": {
16
+ "url": "https://github.com/puijs/ruler-factory/issues"
17
+ },
18
+ "license": "MIT",
19
+ "author": "haoziqaq <357229046@qq.com>",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/puijs/ruler-factory.git"
23
+ },
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "type": "module",
28
+ "sideEffects": false,
29
+ "main": "dist/index.mjs",
30
+ "module": "dist/index.mjs",
31
+ "types": "dist/index.d.mts",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/index.d.mts",
35
+ "import": "./dist/index.mjs"
36
+ }
37
+ },
38
+ "scripts": {
39
+ "build": "vp pack",
40
+ "clean": "rimraf node_modules dist",
41
+ "dev": "vp test watch",
42
+ "format": "vp fmt",
43
+ "lint": "vp lint --fix",
44
+ "prepare": "vp config --hooks-dir .vite-hooks",
45
+ "release": "pnpm build && vr release",
46
+ "test": "vp test run --coverage"
47
+ },
48
+ "dependencies": {
49
+ "rattail": "^1.8.3"
50
+ },
51
+ "devDependencies": {
52
+ "@types/node": "^22.8.1",
53
+ "@nanawan/release": "^2.2.8",
54
+ "@vitest/coverage-istanbul": "^4.1.0",
55
+ "jsdom": "^25.0.1",
56
+ "rimraf": "^6.0.1",
57
+ "typescript": "5.3.3",
58
+ "vite-plus": "0.1.16",
59
+ "vitest": "$vitest"
60
+ },
61
+ "engines": {
62
+ "pnpm": ">=10.0.0"
63
+ },
64
+ "packageManager": "pnpm@10.0.0",
65
+ "pnpm": {
66
+ "overrides": {
67
+ "vite": "npm:@voidzero-dev/vite-plus-core@0.1.16",
68
+ "vitest": "npm:@voidzero-dev/vite-plus-test@0.1.16"
69
+ }
70
+ }
71
+ }