@warlock.js/seal 5.9.0 → 5.11.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.
- package/CHANGELOG.md +14 -1
- package/cjs/index.cjs +330 -232
- package/cjs/index.cjs.map +1 -1
- package/esm/index.d.mts +2 -1
- package/esm/index.mjs +2 -1
- package/esm/mutators/boolean-mutators.d.mts +30 -0
- package/esm/mutators/boolean-mutators.d.mts.map +1 -0
- package/esm/mutators/boolean-mutators.mjs +40 -0
- package/esm/mutators/boolean-mutators.mjs.map +1 -0
- package/esm/mutators/index.mjs +1 -0
- package/esm/rules/scalar/accepted-rule.mjs +20 -13
- package/esm/rules/scalar/accepted-rule.mjs.map +1 -1
- package/esm/rules/scalar/declined-rule.mjs +20 -13
- package/esm/rules/scalar/declined-rule.mjs.map +1 -1
- package/esm/rules/scalar/index.mjs +2 -2
- package/esm/validators/boolean-validator.d.mts +44 -2
- package/esm/validators/boolean-validator.d.mts.map +1 -1
- package/esm/validators/boolean-validator.mjs +64 -16
- package/esm/validators/boolean-validator.mjs.map +1 -1
- package/esm/validators/scalar-validator.mjs +2 -2
- package/llms-full.txt +57 -24
- package/package.json +1 -1
- package/skills/pick-seal-primitive/boolean-methods.md +57 -24
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to `@warlock.js/seal` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
|
|
6
6
|
|
|
7
|
+
## 5.11.0 - 2026-09-14
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- `v.boolean().accepted()` / `.declined()` (and their conditional variants) rejected every string form they advertised — `"yes"`, `"on"`, `"1"` — because the boolean type rule ran first. They now parse those form values into a real boolean.
|
|
12
|
+
|
|
13
|
+
## 5.10.0 - 2026-09-14
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- `.coerce()` on `v.boolean()` — opt-in query-string coercion (`"true"`/`"1"` → `true`, `"false"`/`"0"` → `false`).
|
|
18
|
+
|
|
7
19
|
## 5.9.0 - 2026-09-13
|
|
8
20
|
|
|
9
21
|
### Added
|
|
@@ -17,6 +29,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
17
29
|
- Internal type-safety hardening; no behaviour change.
|
|
18
30
|
|
|
19
31
|
## 5.6.0 - 2026-09-08
|
|
32
|
+
|
|
20
33
|
### Fixed
|
|
21
34
|
|
|
22
35
|
- **`warlock build` and `warlock start` died inside Seal before any application code ran**, with a `ReferenceError` from `array-validator.ts` extending `BaseValidator` before `base-validator.ts` had finished evaluating. The cause was one inline `type` import: under `verbatimModuleSyntax`, `import { type ValidateOptions } from "./validators"` is emitted as a real side-effect import, which closed an import cycle that a type-only import would never have created. Written as `import type { ... }`, the module is not pulled into evaluation at all. Nothing about the validators changed — only which modules load, and when.
|
|
@@ -92,7 +105,7 @@ changelogs.
|
|
|
92
105
|
- `v.literal("")` could never pass. Every validator is required by default and `required` rejects anything the empty-value check calls empty — which includes `""` — so a schema demanding an exact empty string reported "is required" for a field that was present. A literal set containing an empty value now uses `present` (the key must exist) instead of `required`, leaving the literal set to judge the value. Only the empty string was affected; `v.literal(0)` and `v.literal(false)` always worked
|
|
93
106
|
- `v.literal("").optional()` silently disabled the literal check rather than fixing it, accepting `""`, `null` **and** a missing key alike. The literal rule now runs on empty values (`requiresValue: false`) while treating absence as the required/present rule's question, so `.optional()` means optional again and a present value must still match
|
|
94
107
|
- a **failed** validation no longer returns the input it rejected. `object` returned the raw input — including the unknown keys it had just complained about — while `discriminatedUnion` returned `undefined`; the same call shape had two contracts. Validating an outbound DTO to keep internal fields out of a response, then reading `data` without branching on `isValid`, shipped every field the schema existed to exclude. `data` is now `undefined` whenever `isValid` is `false`
|
|
95
|
-
- `v.number().toFixed(n)` could never produce a valid result — the mutator returned `Number(value).toFixed(n)`, a
|
|
108
|
+
- `v.number().toFixed(n)` could never produce a valid result — the mutator returned `Number(value).toFixed(n)`, a _string_, which the validator's own `number` type rule then rejected. It now yields a number (`3.14159` → `3.14`), so the method works where it lives. No working code can have depended on the old output, since every such validation failed; for a fixed-point _string_, format at the presentation edge rather than asking a number schema to emit one
|
|
96
109
|
|
|
97
110
|
## 4.2.11
|
|
98
111
|
|
package/cjs/index.cjs
CHANGED
|
@@ -1127,6 +1127,273 @@ const removeEmptyArrayElementsMutator = async (value) => {
|
|
|
1127
1127
|
return value.filter((item) => !isEmptyValue(item));
|
|
1128
1128
|
};
|
|
1129
1129
|
|
|
1130
|
+
//#endregion
|
|
1131
|
+
//#region ../seal/src/rules/scalar/accepted-rule.ts
|
|
1132
|
+
/**
|
|
1133
|
+
* The full set of values treated as "accepted" by `acceptedRule` and its
|
|
1134
|
+
* conditional variants. Shared with `formBooleanMutator` (see
|
|
1135
|
+
* `../../mutators/boolean-mutators`) so `v.boolean().accepted()` recognizes
|
|
1136
|
+
* exactly this list — single source of truth, no duplicated literals.
|
|
1137
|
+
*/
|
|
1138
|
+
const ACCEPTED_VALUES = [
|
|
1139
|
+
"1",
|
|
1140
|
+
"true",
|
|
1141
|
+
"yes",
|
|
1142
|
+
"y",
|
|
1143
|
+
"on",
|
|
1144
|
+
1,
|
|
1145
|
+
true,
|
|
1146
|
+
"Yes",
|
|
1147
|
+
"Y",
|
|
1148
|
+
"On"
|
|
1149
|
+
];
|
|
1150
|
+
const isAcceptedValue = (value) => {
|
|
1151
|
+
return ACCEPTED_VALUES.includes(value);
|
|
1152
|
+
};
|
|
1153
|
+
/**
|
|
1154
|
+
* Validate value as accepted if it equals:
|
|
1155
|
+
* 1 | "1" | true | "true" | "yes" | "y" | "on"
|
|
1156
|
+
*/
|
|
1157
|
+
const acceptedRule = {
|
|
1158
|
+
name: "accepted",
|
|
1159
|
+
defaultErrorMessage: "The :input must be accepted",
|
|
1160
|
+
description: "The value must be accepted if it equals: 1 | '1' | true | 'true' | 'yes' | 'y' | 'on' | 1 | true | 'Yes' | 'Y' | 'On'",
|
|
1161
|
+
async validate(value, context) {
|
|
1162
|
+
if (isAcceptedValue(value)) return VALID_RULE;
|
|
1163
|
+
return invalidRule(this, context);
|
|
1164
|
+
}
|
|
1165
|
+
};
|
|
1166
|
+
/**
|
|
1167
|
+
* Accepted value if another field's value equals to a specific value
|
|
1168
|
+
*/
|
|
1169
|
+
const acceptedIfRule = {
|
|
1170
|
+
name: "acceptedIf",
|
|
1171
|
+
description: "The field must be accepted if :field's value equals to :value",
|
|
1172
|
+
defaultErrorMessage: "The :input must be accepted",
|
|
1173
|
+
async validate(value, context) {
|
|
1174
|
+
const fieldValue = getFieldValue(this, context);
|
|
1175
|
+
const { value: expectedValue } = this.context.options;
|
|
1176
|
+
this.context.translatableParams.field = this.context.options.field;
|
|
1177
|
+
this.context.translatableParams.value = this.context.options.value;
|
|
1178
|
+
if (fieldValue !== expectedValue) return invalidRule(this, context);
|
|
1179
|
+
if (!isAcceptedValue(value)) return invalidRule(this, context);
|
|
1180
|
+
return VALID_RULE;
|
|
1181
|
+
}
|
|
1182
|
+
};
|
|
1183
|
+
/**
|
|
1184
|
+
* Accepted if another field is not equal to the given value
|
|
1185
|
+
*/
|
|
1186
|
+
const acceptedUnlessRule = {
|
|
1187
|
+
name: "acceptedUnless",
|
|
1188
|
+
description: "The field must be accepted if :field's value is not equal to :value",
|
|
1189
|
+
defaultErrorMessage: "The :input must be accepted",
|
|
1190
|
+
async validate(value, context) {
|
|
1191
|
+
const fieldValue = getFieldValue(this, context);
|
|
1192
|
+
const { value: expectedValue } = this.context.options;
|
|
1193
|
+
this.context.translatableParams.field = this.context.options.field;
|
|
1194
|
+
this.context.translatableParams.value = this.context.options.value;
|
|
1195
|
+
if (fieldValue === expectedValue) return invalidRule(this, context);
|
|
1196
|
+
if (!isAcceptedValue(value)) return invalidRule(this, context);
|
|
1197
|
+
return VALID_RULE;
|
|
1198
|
+
}
|
|
1199
|
+
};
|
|
1200
|
+
/**
|
|
1201
|
+
* Accepted if another field is required
|
|
1202
|
+
*/
|
|
1203
|
+
const acceptedIfRequiredRule = {
|
|
1204
|
+
name: "acceptedIfRequired",
|
|
1205
|
+
description: "The field must be accepted if :field is required",
|
|
1206
|
+
defaultErrorMessage: "The :input must be accepted",
|
|
1207
|
+
async validate(value, context) {
|
|
1208
|
+
const fieldValue = getFieldValue(this, context);
|
|
1209
|
+
this.context.translatableParams.field = this.context.options.field;
|
|
1210
|
+
if (isEmptyValue(fieldValue)) return invalidRule(this, context);
|
|
1211
|
+
if (!isAcceptedValue(value)) return invalidRule(this, context);
|
|
1212
|
+
return VALID_RULE;
|
|
1213
|
+
}
|
|
1214
|
+
};
|
|
1215
|
+
/**
|
|
1216
|
+
* Accepted if another field is present
|
|
1217
|
+
*/
|
|
1218
|
+
const acceptedIfPresentRule = {
|
|
1219
|
+
name: "acceptedIfPresent",
|
|
1220
|
+
description: "The field must be accepted if :field is present",
|
|
1221
|
+
defaultErrorMessage: "The :input must be accepted",
|
|
1222
|
+
async validate(value, context) {
|
|
1223
|
+
const fieldValue = getFieldValue(this, context);
|
|
1224
|
+
this.context.translatableParams.field = this.context.options.field;
|
|
1225
|
+
if (fieldValue === void 0) return invalidRule(this, context);
|
|
1226
|
+
if (!isAcceptedValue(value)) return invalidRule(this, context);
|
|
1227
|
+
return VALID_RULE;
|
|
1228
|
+
}
|
|
1229
|
+
};
|
|
1230
|
+
/**
|
|
1231
|
+
* Accepted if another field is missing
|
|
1232
|
+
*/
|
|
1233
|
+
const acceptedWithoutRule = {
|
|
1234
|
+
name: "acceptedWithout",
|
|
1235
|
+
description: "The field must be accepted if :field is missing",
|
|
1236
|
+
defaultErrorMessage: "The :input must be accepted",
|
|
1237
|
+
async validate(value, context) {
|
|
1238
|
+
const fieldValue = getFieldValue(this, context);
|
|
1239
|
+
this.context.translatableParams.field = this.context.options.field;
|
|
1240
|
+
if (fieldValue !== void 0) return invalidRule(this, context);
|
|
1241
|
+
if (!isAcceptedValue(value)) return invalidRule(this, context);
|
|
1242
|
+
return VALID_RULE;
|
|
1243
|
+
}
|
|
1244
|
+
};
|
|
1245
|
+
|
|
1246
|
+
//#endregion
|
|
1247
|
+
//#region ../seal/src/rules/scalar/declined-rule.ts
|
|
1248
|
+
/**
|
|
1249
|
+
* The full set of values treated as "declined" by `declinedRule` and its
|
|
1250
|
+
* conditional variants. Shared with `formBooleanMutator` (see
|
|
1251
|
+
* `../../mutators/boolean-mutators`) so `v.boolean().declined()` recognizes
|
|
1252
|
+
* exactly this list — single source of truth, no duplicated literals.
|
|
1253
|
+
*/
|
|
1254
|
+
const DECLINED_VALUES = [
|
|
1255
|
+
"0",
|
|
1256
|
+
"false",
|
|
1257
|
+
"no",
|
|
1258
|
+
"n",
|
|
1259
|
+
"off",
|
|
1260
|
+
0,
|
|
1261
|
+
false,
|
|
1262
|
+
"No",
|
|
1263
|
+
"N",
|
|
1264
|
+
"Off"
|
|
1265
|
+
];
|
|
1266
|
+
const isDeclinedValue = (value) => {
|
|
1267
|
+
return DECLINED_VALUES.includes(value);
|
|
1268
|
+
};
|
|
1269
|
+
/**
|
|
1270
|
+
* Validate value as declined if it equals:
|
|
1271
|
+
* 0 | "0" | false | "false" | "no" | "n" | "off" | 0 | false | "No" | "N" | "Off"
|
|
1272
|
+
*/
|
|
1273
|
+
const declinedRule = {
|
|
1274
|
+
name: "declined",
|
|
1275
|
+
defaultErrorMessage: "The :input must be declined",
|
|
1276
|
+
description: "The value must be declined if it equals: 0 | '0' | false | 'false' | 'no' | 'n' | 'off' | 0 | false | 'No' | 'N' | 'Off'",
|
|
1277
|
+
async validate(value, context) {
|
|
1278
|
+
if (isDeclinedValue(value)) return VALID_RULE;
|
|
1279
|
+
return invalidRule(this, context);
|
|
1280
|
+
}
|
|
1281
|
+
};
|
|
1282
|
+
/**
|
|
1283
|
+
* Declined value if another field's value equals to a specific value
|
|
1284
|
+
*/
|
|
1285
|
+
const declinedIfRule = {
|
|
1286
|
+
name: "declinedIf",
|
|
1287
|
+
description: "The field must be declined if :field's value equals to :value",
|
|
1288
|
+
defaultErrorMessage: "The :input must be declined",
|
|
1289
|
+
async validate(value, context) {
|
|
1290
|
+
const fieldValue = getFieldValue(this, context);
|
|
1291
|
+
const { value: expectedValue } = this.context.options;
|
|
1292
|
+
this.context.translatableParams.field = this.context.options.field;
|
|
1293
|
+
this.context.translatableParams.value = this.context.options.value;
|
|
1294
|
+
if (fieldValue !== expectedValue) return invalidRule(this, context);
|
|
1295
|
+
if (!isDeclinedValue(value)) return invalidRule(this, context);
|
|
1296
|
+
return VALID_RULE;
|
|
1297
|
+
}
|
|
1298
|
+
};
|
|
1299
|
+
/**
|
|
1300
|
+
* Declined if another field is not equal to the given value
|
|
1301
|
+
*/
|
|
1302
|
+
const declinedUnlessRule = {
|
|
1303
|
+
name: "declinedUnless",
|
|
1304
|
+
description: "The field must be declined if :field's value is not equal to :value",
|
|
1305
|
+
defaultErrorMessage: "The :input must be declined",
|
|
1306
|
+
async validate(value, context) {
|
|
1307
|
+
const fieldValue = getFieldValue(this, context);
|
|
1308
|
+
const { value: expectedValue } = this.context.options;
|
|
1309
|
+
this.context.translatableParams.field = this.context.options.field;
|
|
1310
|
+
this.context.translatableParams.value = this.context.options.value;
|
|
1311
|
+
if (fieldValue === expectedValue) return invalidRule(this, context);
|
|
1312
|
+
if (!isDeclinedValue(value)) return invalidRule(this, context);
|
|
1313
|
+
return VALID_RULE;
|
|
1314
|
+
}
|
|
1315
|
+
};
|
|
1316
|
+
/**
|
|
1317
|
+
* Declined if another field is required
|
|
1318
|
+
*/
|
|
1319
|
+
const declinedIfRequiredRule = {
|
|
1320
|
+
name: "declinedIfRequired",
|
|
1321
|
+
description: "The field must be declined if :field is required",
|
|
1322
|
+
defaultErrorMessage: "The :input must be declined",
|
|
1323
|
+
async validate(value, context) {
|
|
1324
|
+
const fieldValue = getFieldValue(this, context);
|
|
1325
|
+
this.context.translatableParams.field = this.context.options.field;
|
|
1326
|
+
if (isEmptyValue(fieldValue)) return invalidRule(this, context);
|
|
1327
|
+
if (!isDeclinedValue(value)) return invalidRule(this, context);
|
|
1328
|
+
return VALID_RULE;
|
|
1329
|
+
}
|
|
1330
|
+
};
|
|
1331
|
+
/**
|
|
1332
|
+
* Declined if another field is present
|
|
1333
|
+
*/
|
|
1334
|
+
const declinedIfPresentRule = {
|
|
1335
|
+
name: "declinedIfPresent",
|
|
1336
|
+
description: "The field must be declined if :field is present",
|
|
1337
|
+
defaultErrorMessage: "The :input must be declined",
|
|
1338
|
+
async validate(value, context) {
|
|
1339
|
+
const fieldValue = getFieldValue(this, context);
|
|
1340
|
+
this.context.translatableParams.field = this.context.options.field;
|
|
1341
|
+
if (fieldValue === void 0) return invalidRule(this, context);
|
|
1342
|
+
if (!isDeclinedValue(value)) return invalidRule(this, context);
|
|
1343
|
+
return VALID_RULE;
|
|
1344
|
+
}
|
|
1345
|
+
};
|
|
1346
|
+
/**
|
|
1347
|
+
* Declined if another field is missing
|
|
1348
|
+
*/
|
|
1349
|
+
const declinedWithoutRule = {
|
|
1350
|
+
name: "declinedWithout",
|
|
1351
|
+
description: "The field must be declined if :field is missing",
|
|
1352
|
+
defaultErrorMessage: "The :input must be declined",
|
|
1353
|
+
async validate(value, context) {
|
|
1354
|
+
const fieldValue = getFieldValue(this, context);
|
|
1355
|
+
this.context.translatableParams.field = this.context.options.field;
|
|
1356
|
+
if (fieldValue !== void 0) return invalidRule(this, context);
|
|
1357
|
+
if (!isDeclinedValue(value)) return invalidRule(this, context);
|
|
1358
|
+
return VALID_RULE;
|
|
1359
|
+
}
|
|
1360
|
+
};
|
|
1361
|
+
|
|
1362
|
+
//#endregion
|
|
1363
|
+
//#region ../seal/src/mutators/boolean-mutators.ts
|
|
1364
|
+
/**
|
|
1365
|
+
* Coerce a query-string-shaped boolean into a real boolean.
|
|
1366
|
+
*
|
|
1367
|
+
* Converts exactly: `"true"` / `"1"` / `1` → `true`, `"false"` / `"0"` / `0` → `false`.
|
|
1368
|
+
* Case-sensitive, no trimming. Any other value (`"yes"`, `"on"`, `"TRUE"`, `""`,
|
|
1369
|
+
* `2`, `null`, etc.) passes through unchanged so the boolean type rule rejects it.
|
|
1370
|
+
*/
|
|
1371
|
+
const booleanCoerceMutator = async (value) => {
|
|
1372
|
+
if (value === "true" || value === "1" || value === 1) return true;
|
|
1373
|
+
if (value === "false" || value === "0" || value === 0) return false;
|
|
1374
|
+
return value;
|
|
1375
|
+
};
|
|
1376
|
+
/**
|
|
1377
|
+
* Parse form-shaped boolean values into a real boolean, ahead of the type
|
|
1378
|
+
* rule.
|
|
1379
|
+
*
|
|
1380
|
+
* Opted into by calling any `accepted*`/`declined*` method on
|
|
1381
|
+
* `v.boolean()` (see `BooleanValidator`'s `withFormBooleanMutator`). Converts
|
|
1382
|
+
* every value in `ACCEPTED_VALUES` (`1`, `"1"`, `true`, `"true"`, `"yes"`,
|
|
1383
|
+
* `"y"`, `"on"`, `"Yes"`, `"Y"`, `"On"`) to `true`, and every value in
|
|
1384
|
+
* `DECLINED_VALUES` (`0`, `"0"`, `false`, `"false"`, `"no"`, `"n"`, `"off"`,
|
|
1385
|
+
* `"No"`, `"N"`, `"Off"`) to `false`. Any other value passes through
|
|
1386
|
+
* unchanged so the boolean type rule still rejects it.
|
|
1387
|
+
*
|
|
1388
|
+
* Named (not anonymous) so callers can detect whether it has already been
|
|
1389
|
+
* added — see the idempotence check in `BooleanValidator`.
|
|
1390
|
+
*/
|
|
1391
|
+
const formBooleanMutator = async (value) => {
|
|
1392
|
+
if (ACCEPTED_VALUES.includes(value)) return true;
|
|
1393
|
+
if (DECLINED_VALUES.includes(value)) return false;
|
|
1394
|
+
return value;
|
|
1395
|
+
};
|
|
1396
|
+
|
|
1130
1397
|
//#endregion
|
|
1131
1398
|
//#region ../seal/src/mutators/date-mutators.ts
|
|
1132
1399
|
/**
|
|
@@ -5484,224 +5751,6 @@ var PrimitiveValidator = class extends BaseValidator {
|
|
|
5484
5751
|
}
|
|
5485
5752
|
};
|
|
5486
5753
|
|
|
5487
|
-
//#endregion
|
|
5488
|
-
//#region ../seal/src/rules/scalar/accepted-rule.ts
|
|
5489
|
-
const isAcceptedValue = (value) => {
|
|
5490
|
-
return [
|
|
5491
|
-
"1",
|
|
5492
|
-
"true",
|
|
5493
|
-
"yes",
|
|
5494
|
-
"y",
|
|
5495
|
-
"on",
|
|
5496
|
-
1,
|
|
5497
|
-
true,
|
|
5498
|
-
"Yes",
|
|
5499
|
-
"Y",
|
|
5500
|
-
"On"
|
|
5501
|
-
].includes(value);
|
|
5502
|
-
};
|
|
5503
|
-
/**
|
|
5504
|
-
* Validate value as accepted if it equals:
|
|
5505
|
-
* 1 | "1" | true | "true" | "yes" | "y" | "on"
|
|
5506
|
-
*/
|
|
5507
|
-
const acceptedRule = {
|
|
5508
|
-
name: "accepted",
|
|
5509
|
-
defaultErrorMessage: "The :input must be accepted",
|
|
5510
|
-
description: "The value must be accepted if it equals: 1 | '1' | true | 'true' | 'yes' | 'y' | 'on' | 1 | true | 'Yes' | 'Y' | 'On'",
|
|
5511
|
-
async validate(value, context) {
|
|
5512
|
-
if (isAcceptedValue(value)) return VALID_RULE;
|
|
5513
|
-
return invalidRule(this, context);
|
|
5514
|
-
}
|
|
5515
|
-
};
|
|
5516
|
-
/**
|
|
5517
|
-
* Accepted value if another field's value equals to a specific value
|
|
5518
|
-
*/
|
|
5519
|
-
const acceptedIfRule = {
|
|
5520
|
-
name: "acceptedIf",
|
|
5521
|
-
description: "The field must be accepted if :field's value equals to :value",
|
|
5522
|
-
defaultErrorMessage: "The :input must be accepted",
|
|
5523
|
-
async validate(value, context) {
|
|
5524
|
-
const fieldValue = getFieldValue(this, context);
|
|
5525
|
-
const { value: expectedValue } = this.context.options;
|
|
5526
|
-
this.context.translatableParams.field = this.context.options.field;
|
|
5527
|
-
this.context.translatableParams.value = this.context.options.value;
|
|
5528
|
-
if (fieldValue !== expectedValue) return invalidRule(this, context);
|
|
5529
|
-
if (!isAcceptedValue(value)) return invalidRule(this, context);
|
|
5530
|
-
return VALID_RULE;
|
|
5531
|
-
}
|
|
5532
|
-
};
|
|
5533
|
-
/**
|
|
5534
|
-
* Accepted if another field is not equal to the given value
|
|
5535
|
-
*/
|
|
5536
|
-
const acceptedUnlessRule = {
|
|
5537
|
-
name: "acceptedUnless",
|
|
5538
|
-
description: "The field must be accepted if :field's value is not equal to :value",
|
|
5539
|
-
defaultErrorMessage: "The :input must be accepted",
|
|
5540
|
-
async validate(value, context) {
|
|
5541
|
-
const fieldValue = getFieldValue(this, context);
|
|
5542
|
-
const { value: expectedValue } = this.context.options;
|
|
5543
|
-
this.context.translatableParams.field = this.context.options.field;
|
|
5544
|
-
this.context.translatableParams.value = this.context.options.value;
|
|
5545
|
-
if (fieldValue === expectedValue) return invalidRule(this, context);
|
|
5546
|
-
if (!isAcceptedValue(value)) return invalidRule(this, context);
|
|
5547
|
-
return VALID_RULE;
|
|
5548
|
-
}
|
|
5549
|
-
};
|
|
5550
|
-
/**
|
|
5551
|
-
* Accepted if another field is required
|
|
5552
|
-
*/
|
|
5553
|
-
const acceptedIfRequiredRule = {
|
|
5554
|
-
name: "acceptedIfRequired",
|
|
5555
|
-
description: "The field must be accepted if :field is required",
|
|
5556
|
-
defaultErrorMessage: "The :input must be accepted",
|
|
5557
|
-
async validate(value, context) {
|
|
5558
|
-
const fieldValue = getFieldValue(this, context);
|
|
5559
|
-
this.context.translatableParams.field = this.context.options.field;
|
|
5560
|
-
if (isEmptyValue(fieldValue)) return invalidRule(this, context);
|
|
5561
|
-
if (!isAcceptedValue(value)) return invalidRule(this, context);
|
|
5562
|
-
return VALID_RULE;
|
|
5563
|
-
}
|
|
5564
|
-
};
|
|
5565
|
-
/**
|
|
5566
|
-
* Accepted if another field is present
|
|
5567
|
-
*/
|
|
5568
|
-
const acceptedIfPresentRule = {
|
|
5569
|
-
name: "acceptedIfPresent",
|
|
5570
|
-
description: "The field must be accepted if :field is present",
|
|
5571
|
-
defaultErrorMessage: "The :input must be accepted",
|
|
5572
|
-
async validate(value, context) {
|
|
5573
|
-
const fieldValue = getFieldValue(this, context);
|
|
5574
|
-
this.context.translatableParams.field = this.context.options.field;
|
|
5575
|
-
if (fieldValue === void 0) return invalidRule(this, context);
|
|
5576
|
-
if (!isAcceptedValue(value)) return invalidRule(this, context);
|
|
5577
|
-
return VALID_RULE;
|
|
5578
|
-
}
|
|
5579
|
-
};
|
|
5580
|
-
/**
|
|
5581
|
-
* Accepted if another field is missing
|
|
5582
|
-
*/
|
|
5583
|
-
const acceptedWithoutRule = {
|
|
5584
|
-
name: "acceptedWithout",
|
|
5585
|
-
description: "The field must be accepted if :field is missing",
|
|
5586
|
-
defaultErrorMessage: "The :input must be accepted",
|
|
5587
|
-
async validate(value, context) {
|
|
5588
|
-
const fieldValue = getFieldValue(this, context);
|
|
5589
|
-
this.context.translatableParams.field = this.context.options.field;
|
|
5590
|
-
if (fieldValue !== void 0) return invalidRule(this, context);
|
|
5591
|
-
if (!isAcceptedValue(value)) return invalidRule(this, context);
|
|
5592
|
-
return VALID_RULE;
|
|
5593
|
-
}
|
|
5594
|
-
};
|
|
5595
|
-
|
|
5596
|
-
//#endregion
|
|
5597
|
-
//#region ../seal/src/rules/scalar/declined-rule.ts
|
|
5598
|
-
const isDeclinedValue = (value) => {
|
|
5599
|
-
return [
|
|
5600
|
-
"0",
|
|
5601
|
-
"false",
|
|
5602
|
-
"no",
|
|
5603
|
-
"n",
|
|
5604
|
-
"off",
|
|
5605
|
-
0,
|
|
5606
|
-
false,
|
|
5607
|
-
"No",
|
|
5608
|
-
"N",
|
|
5609
|
-
"Off"
|
|
5610
|
-
].includes(value);
|
|
5611
|
-
};
|
|
5612
|
-
/**
|
|
5613
|
-
* Validate value as declined if it equals:
|
|
5614
|
-
* 0 | "0" | false | "false" | "no" | "n" | "off" | 0 | false | "No" | "N" | "Off"
|
|
5615
|
-
*/
|
|
5616
|
-
const declinedRule = {
|
|
5617
|
-
name: "declined",
|
|
5618
|
-
defaultErrorMessage: "The :input must be declined",
|
|
5619
|
-
description: "The value must be declined if it equals: 0 | '0' | false | 'false' | 'no' | 'n' | 'off' | 0 | false | 'No' | 'N' | 'Off'",
|
|
5620
|
-
async validate(value, context) {
|
|
5621
|
-
if (isDeclinedValue(value)) return VALID_RULE;
|
|
5622
|
-
return invalidRule(this, context);
|
|
5623
|
-
}
|
|
5624
|
-
};
|
|
5625
|
-
/**
|
|
5626
|
-
* Declined value if another field's value equals to a specific value
|
|
5627
|
-
*/
|
|
5628
|
-
const declinedIfRule = {
|
|
5629
|
-
name: "declinedIf",
|
|
5630
|
-
description: "The field must be declined if :field's value equals to :value",
|
|
5631
|
-
defaultErrorMessage: "The :input must be declined",
|
|
5632
|
-
async validate(value, context) {
|
|
5633
|
-
const fieldValue = getFieldValue(this, context);
|
|
5634
|
-
const { value: expectedValue } = this.context.options;
|
|
5635
|
-
this.context.translatableParams.field = this.context.options.field;
|
|
5636
|
-
this.context.translatableParams.value = this.context.options.value;
|
|
5637
|
-
if (fieldValue !== expectedValue) return invalidRule(this, context);
|
|
5638
|
-
if (!isDeclinedValue(value)) return invalidRule(this, context);
|
|
5639
|
-
return VALID_RULE;
|
|
5640
|
-
}
|
|
5641
|
-
};
|
|
5642
|
-
/**
|
|
5643
|
-
* Declined if another field is not equal to the given value
|
|
5644
|
-
*/
|
|
5645
|
-
const declinedUnlessRule = {
|
|
5646
|
-
name: "declinedUnless",
|
|
5647
|
-
description: "The field must be declined if :field's value is not equal to :value",
|
|
5648
|
-
defaultErrorMessage: "The :input must be declined",
|
|
5649
|
-
async validate(value, context) {
|
|
5650
|
-
const fieldValue = getFieldValue(this, context);
|
|
5651
|
-
const { value: expectedValue } = this.context.options;
|
|
5652
|
-
this.context.translatableParams.field = this.context.options.field;
|
|
5653
|
-
this.context.translatableParams.value = this.context.options.value;
|
|
5654
|
-
if (fieldValue === expectedValue) return invalidRule(this, context);
|
|
5655
|
-
if (!isDeclinedValue(value)) return invalidRule(this, context);
|
|
5656
|
-
return VALID_RULE;
|
|
5657
|
-
}
|
|
5658
|
-
};
|
|
5659
|
-
/**
|
|
5660
|
-
* Declined if another field is required
|
|
5661
|
-
*/
|
|
5662
|
-
const declinedIfRequiredRule = {
|
|
5663
|
-
name: "declinedIfRequired",
|
|
5664
|
-
description: "The field must be declined if :field is required",
|
|
5665
|
-
defaultErrorMessage: "The :input must be declined",
|
|
5666
|
-
async validate(value, context) {
|
|
5667
|
-
const fieldValue = getFieldValue(this, context);
|
|
5668
|
-
this.context.translatableParams.field = this.context.options.field;
|
|
5669
|
-
if (isEmptyValue(fieldValue)) return invalidRule(this, context);
|
|
5670
|
-
if (!isDeclinedValue(value)) return invalidRule(this, context);
|
|
5671
|
-
return VALID_RULE;
|
|
5672
|
-
}
|
|
5673
|
-
};
|
|
5674
|
-
/**
|
|
5675
|
-
* Declined if another field is present
|
|
5676
|
-
*/
|
|
5677
|
-
const declinedIfPresentRule = {
|
|
5678
|
-
name: "declinedIfPresent",
|
|
5679
|
-
description: "The field must be declined if :field is present",
|
|
5680
|
-
defaultErrorMessage: "The :input must be declined",
|
|
5681
|
-
async validate(value, context) {
|
|
5682
|
-
const fieldValue = getFieldValue(this, context);
|
|
5683
|
-
this.context.translatableParams.field = this.context.options.field;
|
|
5684
|
-
if (fieldValue === void 0) return invalidRule(this, context);
|
|
5685
|
-
if (!isDeclinedValue(value)) return invalidRule(this, context);
|
|
5686
|
-
return VALID_RULE;
|
|
5687
|
-
}
|
|
5688
|
-
};
|
|
5689
|
-
/**
|
|
5690
|
-
* Declined if another field is missing
|
|
5691
|
-
*/
|
|
5692
|
-
const declinedWithoutRule = {
|
|
5693
|
-
name: "declinedWithout",
|
|
5694
|
-
description: "The field must be declined if :field is missing",
|
|
5695
|
-
defaultErrorMessage: "The :input must be declined",
|
|
5696
|
-
async validate(value, context) {
|
|
5697
|
-
const fieldValue = getFieldValue(this, context);
|
|
5698
|
-
this.context.translatableParams.field = this.context.options.field;
|
|
5699
|
-
if (fieldValue !== void 0) return invalidRule(this, context);
|
|
5700
|
-
if (!isDeclinedValue(value)) return invalidRule(this, context);
|
|
5701
|
-
return VALID_RULE;
|
|
5702
|
-
}
|
|
5703
|
-
};
|
|
5704
|
-
|
|
5705
5754
|
//#endregion
|
|
5706
5755
|
//#region ../seal/src/validators/boolean-validator.ts
|
|
5707
5756
|
/**
|
|
@@ -5722,65 +5771,112 @@ var BooleanValidator = class extends PrimitiveValidator {
|
|
|
5722
5771
|
matchesType(value) {
|
|
5723
5772
|
return typeof value === "boolean";
|
|
5724
5773
|
}
|
|
5725
|
-
/**
|
|
5774
|
+
/**
|
|
5775
|
+
* Add a rule from the accepted/declined family, opting the validator into
|
|
5776
|
+
* form-boolean parsing.
|
|
5777
|
+
*
|
|
5778
|
+
* Calling ANY `accepted*`/`declined*` method on `v.boolean()` adds (once,
|
|
5779
|
+
* idempotently — checked by mutator identity so chaining several of these
|
|
5780
|
+
* methods never stacks duplicates) `formBooleanMutator`, which runs before
|
|
5781
|
+
* the type rule and converts the accepted set (`1`, `"1"`, `true`,
|
|
5782
|
+
* `"true"`, `"yes"`, `"y"`, `"on"`, `"Yes"`, `"Y"`, `"On"`) to `true` and
|
|
5783
|
+
* the declined set (`0`, `"0"`, `false`, `"false"`, `"no"`, `"n"`, `"off"`,
|
|
5784
|
+
* `"No"`, `"N"`, `"Off"`) to `false`; any other value passes through
|
|
5785
|
+
* unchanged and is rejected by the strict type rule as before.
|
|
5786
|
+
*
|
|
5787
|
+
* The mutator and the rule are added to the SAME resolved instance in one
|
|
5788
|
+
* pass — resolving `this.instance` twice (once per `addRule`/`addMutator`
|
|
5789
|
+
* call) would clone twice on an immutable validator and silently drop one
|
|
5790
|
+
* of the two additions.
|
|
5791
|
+
*/
|
|
5792
|
+
addFormBooleanRule(rule, errorMessage, options = {}) {
|
|
5793
|
+
const instance = this.instance;
|
|
5794
|
+
if (!instance.mutators.some((mutator) => mutator.mutate === formBooleanMutator)) instance.addMutableMutator(formBooleanMutator);
|
|
5795
|
+
instance.addMutableRule(rule, errorMessage, options);
|
|
5796
|
+
return instance;
|
|
5797
|
+
}
|
|
5798
|
+
/** Value must be accepted (true, "yes", 1, "on", etc. — form strings are parsed into a real boolean first) */
|
|
5726
5799
|
accepted(errorMessage) {
|
|
5727
|
-
return this.
|
|
5800
|
+
return this.addFormBooleanRule(acceptedRule, errorMessage);
|
|
5728
5801
|
}
|
|
5729
5802
|
/** Value must be accepted if another field equals a value */
|
|
5730
5803
|
acceptedIf(field, value, errorMessage) {
|
|
5731
|
-
return this.
|
|
5804
|
+
return this.addFormBooleanRule(acceptedIfRule, errorMessage, {
|
|
5732
5805
|
field,
|
|
5733
5806
|
value
|
|
5734
5807
|
});
|
|
5735
5808
|
}
|
|
5736
5809
|
/** Value must be accepted unless another field equals a value */
|
|
5737
5810
|
acceptedUnless(field, value, errorMessage) {
|
|
5738
|
-
return this.
|
|
5811
|
+
return this.addFormBooleanRule(acceptedUnlessRule, errorMessage, {
|
|
5739
5812
|
field,
|
|
5740
5813
|
value
|
|
5741
5814
|
});
|
|
5742
5815
|
}
|
|
5743
5816
|
/** Value must be accepted if another field is required */
|
|
5744
5817
|
acceptedIfRequired(field, errorMessage) {
|
|
5745
|
-
return this.
|
|
5818
|
+
return this.addFormBooleanRule(acceptedIfRequiredRule, errorMessage, { field });
|
|
5746
5819
|
}
|
|
5747
5820
|
/** Value must be accepted if another field is present */
|
|
5748
5821
|
acceptedIfPresent(field, errorMessage) {
|
|
5749
|
-
return this.
|
|
5822
|
+
return this.addFormBooleanRule(acceptedIfPresentRule, errorMessage, { field });
|
|
5750
5823
|
}
|
|
5751
5824
|
/** Value must be accepted if another field is missing */
|
|
5752
5825
|
acceptedWithout(field, errorMessage) {
|
|
5753
|
-
return this.
|
|
5826
|
+
return this.addFormBooleanRule(acceptedWithoutRule, errorMessage, { field });
|
|
5754
5827
|
}
|
|
5755
|
-
/** Value must be declined (false, "no", 0, "off", etc.) */
|
|
5828
|
+
/** Value must be declined (false, "no", 0, "off", etc. — form strings are parsed into a real boolean first) */
|
|
5756
5829
|
declined(errorMessage) {
|
|
5757
|
-
return this.
|
|
5830
|
+
return this.addFormBooleanRule(declinedRule, errorMessage);
|
|
5758
5831
|
}
|
|
5759
5832
|
/** Value must be declined if another field equals a value */
|
|
5760
5833
|
declinedIf(field, value, errorMessage) {
|
|
5761
|
-
return this.
|
|
5834
|
+
return this.addFormBooleanRule(declinedIfRule, errorMessage, {
|
|
5762
5835
|
field,
|
|
5763
5836
|
value
|
|
5764
5837
|
});
|
|
5765
5838
|
}
|
|
5766
5839
|
/** Value must be declined unless another field equals a value */
|
|
5767
5840
|
declinedUnless(field, value, errorMessage) {
|
|
5768
|
-
return this.
|
|
5841
|
+
return this.addFormBooleanRule(declinedUnlessRule, errorMessage, {
|
|
5769
5842
|
field,
|
|
5770
5843
|
value
|
|
5771
5844
|
});
|
|
5772
5845
|
}
|
|
5773
5846
|
/** Value must be declined if another field is required */
|
|
5774
5847
|
declinedIfRequired(field, errorMessage) {
|
|
5775
|
-
return this.
|
|
5848
|
+
return this.addFormBooleanRule(declinedIfRequiredRule, errorMessage, { field });
|
|
5776
5849
|
}
|
|
5777
5850
|
/** Value must be declined if another field is present */
|
|
5778
5851
|
declinedIfPresent(field, errorMessage) {
|
|
5779
|
-
return this.
|
|
5852
|
+
return this.addFormBooleanRule(declinedIfPresentRule, errorMessage, { field });
|
|
5780
5853
|
}
|
|
5781
5854
|
/** Value must be declined if another field is missing */
|
|
5782
5855
|
declinedWithout(field, errorMessage) {
|
|
5783
|
-
return this.
|
|
5856
|
+
return this.addFormBooleanRule(declinedWithoutRule, errorMessage, { field });
|
|
5857
|
+
}
|
|
5858
|
+
/**
|
|
5859
|
+
* Opt in to coercion: an exact query-string-shaped boolean is converted to a
|
|
5860
|
+
* real boolean **before** the type rules run; any other value passes through
|
|
5861
|
+
* unchanged so bad input still fails the type rule.
|
|
5862
|
+
*
|
|
5863
|
+
* Converts exactly `"true"` / `"1"` / `1` → `true` and `"false"` / `"0"` / `0`
|
|
5864
|
+
* → `false`. Case-sensitive, no trimming — `"TRUE"`, `"yes"`, `"on"`, `""`, and
|
|
5865
|
+
* `2` all pass through unchanged and fail the type rule. `v.boolean()` does
|
|
5866
|
+
* **not** coerce by default; this only takes effect when explicitly chained.
|
|
5867
|
+
* For form-style truthy strings like `"yes"` / `"on"`, use `.accepted()` /
|
|
5868
|
+
* `.declined()` instead — those methods opt into their own form-boolean
|
|
5869
|
+
* parsing (a wider value set, case-insensitive on a few forms) and then
|
|
5870
|
+
* apply the accepted/declined pass-fail rule on top of the parsed boolean.
|
|
5871
|
+
*
|
|
5872
|
+
* The output type is unchanged (`Infer<>` keys off the validator class), so
|
|
5873
|
+
* `v.boolean().coerce()` still infers `boolean`.
|
|
5874
|
+
*
|
|
5875
|
+
* @example
|
|
5876
|
+
* v.boolean().coerce() // "true" → true; "1" → true; "0" → false; "yes" → invalid
|
|
5877
|
+
*/
|
|
5878
|
+
coerce() {
|
|
5879
|
+
return this.addMutator(booleanCoerceMutator);
|
|
5784
5880
|
}
|
|
5785
5881
|
/**
|
|
5786
5882
|
* Value must be strictly true (not "yes", "on", 1, etc.)
|
|
@@ -8808,6 +8904,7 @@ exports.betweenNumbersRule = betweenNumbersRule;
|
|
|
8808
8904
|
exports.betweenTimesRule = betweenTimesRule;
|
|
8809
8905
|
exports.betweenYearsRule = betweenYearsRule;
|
|
8810
8906
|
exports.birthdayRule = birthdayRule;
|
|
8907
|
+
exports.booleanCoerceMutator = booleanCoerceMutator;
|
|
8811
8908
|
exports.booleanMutator = booleanMutator;
|
|
8812
8909
|
exports.booleanRule = booleanRule;
|
|
8813
8910
|
exports.businessDayRule = businessDayRule;
|
|
@@ -8839,6 +8936,7 @@ exports.forbiddenIfNotRule = forbiddenIfNotRule;
|
|
|
8839
8936
|
exports.forbiddenIfRule = forbiddenIfRule;
|
|
8840
8937
|
exports.forbiddenMethodsApplied = forbiddenMethodsApplied;
|
|
8841
8938
|
exports.forbiddenRule = forbiddenRule;
|
|
8939
|
+
exports.formBooleanMutator = formBooleanMutator;
|
|
8842
8940
|
exports.fromHourRule = fromHourRule;
|
|
8843
8941
|
exports.fromMinuteRule = fromMinuteRule;
|
|
8844
8942
|
exports.fromTodayRule = fromTodayRule;
|