@loomcli/validators 0.5.0 → 0.6.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.
@@ -0,0 +1,183 @@
1
+ import { isRuleIdentity } from '@loomcli/core';
2
+ import { isPlainObject } from './data.js';
3
+ import { fault, quote } from './faults.js';
4
+ import { issueCodeConfig, issueCodeName, issueCodeSchema, issueParameters } from './rules.js';
5
+ /** The Standard Schema version this package reads. */
6
+ const standardVersion = 1;
7
+ /** Whether a value is an object or a function, which alone can carry fields. */
8
+ function isObjectLike(value) {
9
+ return value !== null && (typeof value === 'object' || typeof value === 'function');
10
+ }
11
+ /**
12
+ * Whether a value answers the Standard Schema v1 contract, as core checks a validator. A value
13
+ * whose fields throw when read, through a getter or a proxy trap, is not a Standard Schema.
14
+ */
15
+ function isStandardSchema(value) {
16
+ try {
17
+ if (!isObjectLike(value)) {
18
+ return false;
19
+ }
20
+ const standard = '~standard' in value ? value['~standard'] : undefined;
21
+ return (standard !== null &&
22
+ typeof standard === 'object' &&
23
+ 'version' in standard &&
24
+ standard.version === standardVersion &&
25
+ 'vendor' in standard &&
26
+ typeof standard.vendor === 'string' &&
27
+ 'validate' in standard &&
28
+ typeof standard.validate === 'function');
29
+ }
30
+ catch {
31
+ return false;
32
+ }
33
+ }
34
+ /**
35
+ * The verdict a schema's result states, or `undefined` for a value that is not a Standard Schema
36
+ * result: a success must hold its own `value` and no issues, and a failure an issues array. Both
37
+ * `issue` and `read` read a result here, so neither keeps parameters the schema never output. A
38
+ * getter or a proxy trap that throws while the result is read propagates to the caller.
39
+ */
40
+ function verdictOf(result) {
41
+ if (!isObjectLike(result)) {
42
+ return undefined;
43
+ }
44
+ if (result.issues === undefined) {
45
+ return Object.hasOwn(result, 'value') ? { kind: 'accepted', params: result.value } : undefined;
46
+ }
47
+ const issues = result.issues;
48
+ return Array.isArray(issues) ? { kind: 'rejected' } : undefined;
49
+ }
50
+ /** The verdict a result states, where a result that throws while it is read states none. */
51
+ function verdictOrNothing(result) {
52
+ try {
53
+ return verdictOf(result);
54
+ }
55
+ catch {
56
+ return undefined;
57
+ }
58
+ }
59
+ /**
60
+ * Whether a schema's answer is a promise or another thenable, which a synchronous read cannot
61
+ * wait for. A value whose `then` cannot be read is not a thenable, so the test never throws.
62
+ */
63
+ function isThenable(value) {
64
+ try {
65
+ return isObjectLike(value) && 'then' in value && typeof value.then === 'function';
66
+ }
67
+ catch {
68
+ return false;
69
+ }
70
+ }
71
+ /** The `issueCode()` call a fault marks one part of: the code, the config, or one of its keys. */
72
+ function declarationAt(code, config, mark) {
73
+ return { arguments: [code, config], factory: 'issueCode', mark };
74
+ }
75
+ /** The mark for one key of a config object, or the config itself when the key is absent. */
76
+ function keyMark(config, key) {
77
+ return key in config ? `1.${key}` : '1';
78
+ }
79
+ /** Faults on a declaration that bypassed the types: a code outside the grammar, or a bad config. */
80
+ function checkDeclaration(code, config) {
81
+ if (!isRuleIdentity(code)) {
82
+ throw fault(issueCodeName, declarationAt(code, config, '0'), {
83
+ correction: 'Supply a code such as "@acme/validators/port-range", with each subpath segment and the rule of lowercase letters and digits in words joined by single hyphens.',
84
+ sentence: `issueCode() code ${quote(code)} is not a package name, any subpath segments, and a rule name joined by slashes.`,
85
+ });
86
+ }
87
+ if (!isPlainObject(config)) {
88
+ throw fault(issueCodeConfig, declarationAt(code, config, '1'), {
89
+ correction: 'Supply an object with a schema and a message function.',
90
+ sentence: 'issueCode() config is not a plain object.',
91
+ });
92
+ }
93
+ if (!isStandardSchema(config.schema)) {
94
+ throw fault(issueCodeConfig, declarationAt(code, config, keyMark(config, 'schema')), {
95
+ correction: 'Supply a Standard Schema value that validates the parameters.',
96
+ sentence: 'issueCode() schema is not a Standard Schema.',
97
+ });
98
+ }
99
+ if (typeof config.message !== 'function') {
100
+ throw fault(issueCodeConfig, declarationAt(code, config, keyMark(config, 'message')), {
101
+ correction: 'Supply a function that builds the sentence from the parameters.',
102
+ sentence: 'issueCode() message is not a function.',
103
+ });
104
+ }
105
+ }
106
+ /**
107
+ * Declares one issue code with its parameter schema and its one sentence, and returns a frozen
108
+ * descriptor. `issue` builds the rejection a `parse` function returns, and `read` recovers the
109
+ * typed parameters from an issue carrying the code.
110
+ */
111
+ function issueCode(code, config) {
112
+ checkDeclaration(code, config);
113
+ const { message, schema } = config;
114
+ // A schema fault is the declaration's, so it marks the schema the code was declared with.
115
+ const schemaAt = declarationAt(code, config, '1.schema');
116
+ /** The schema's synchronous result; a promise faults, since neither caller can wait for it. */
117
+ function settled(answer) {
118
+ if (isThenable(answer)) {
119
+ // The unawaited answer may still reject, and an unobserved rejection would end the process.
120
+ void Promise.resolve(answer).catch(() => undefined);
121
+ throw fault(issueCodeSchema, schemaAt, {
122
+ correction: 'Supply a schema that validates synchronously.',
123
+ sentence: `Issue code ${JSON.stringify(code)} has a schema that answers with a promise.`,
124
+ });
125
+ }
126
+ return answer;
127
+ }
128
+ /** The schema's answer to the parameters `issue` was given; a throw is the declaration's fault. */
129
+ function declaredAnswer(params) {
130
+ try {
131
+ return schema['~standard'].validate(params);
132
+ }
133
+ catch {
134
+ throw fault(issueCodeSchema, schemaAt, {
135
+ correction: 'Supply a schema that returns its issues instead of throwing.',
136
+ sentence: `Issue code ${JSON.stringify(code)} has a schema that throws in issue().`,
137
+ });
138
+ }
139
+ }
140
+ /**
141
+ * The verdict `issue` builds from. Anything but a well-formed synchronous result is the
142
+ * declaration's fault, so a result that throws while it is read is not a result either.
143
+ */
144
+ function declaredVerdict(params) {
145
+ const verdict = verdictOrNothing(settled(declaredAnswer(params)));
146
+ if (verdict === undefined) {
147
+ throw fault(issueCodeSchema, schemaAt, {
148
+ correction: 'Supply a schema that returns its value or its issues.',
149
+ sentence: `Issue code ${JSON.stringify(code)} has a schema that answers with a value that is not a Standard Schema result.`,
150
+ });
151
+ }
152
+ return verdict;
153
+ }
154
+ return Object.freeze({
155
+ code,
156
+ issue: (params) => {
157
+ const verdict = declaredVerdict(params);
158
+ if (verdict.kind === 'rejected') {
159
+ throw fault(issueParameters, { arguments: [params], factory: 'issue', mark: '0' }, {
160
+ correction: 'Supply parameters its schema accepts.',
161
+ sentence: `Issue code ${JSON.stringify(code)} rejects the parameters passed to issue().`,
162
+ });
163
+ }
164
+ return Object.freeze({ code, message: message(verdict.params), params: verdict.params });
165
+ },
166
+ // Core hands a failure view a plain-object copy of each issue a validator returned.
167
+ // Reading the copy's own fields runs no getter, and its parameters reach the schema as they are.
168
+ read: (issue) => {
169
+ const candidate = issue;
170
+ if (!isObjectLike(candidate) || !('code' in candidate) || candidate.code !== code) {
171
+ return undefined;
172
+ }
173
+ const params = 'params' in candidate ? candidate.params : undefined;
174
+ if (!isObjectLike(params)) {
175
+ return undefined;
176
+ }
177
+ const verdict = verdictOf(settled(schema['~standard'].validate(params)));
178
+ return verdict?.kind === 'accepted' ? verdict.params : undefined;
179
+ },
180
+ schema,
181
+ });
182
+ }
183
+ export { issueCode };
package/dist/issues.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { StandardSchemaV1 } from '@loomcli/core';
2
- /** A rejection: one issue with no path and no code, carrying the configuration's one sentence. */
3
- declare function reject(message: string): StandardSchemaV1.FailureResult;
2
+ /** A rejection: one issue with no path, carrying the configuration's code and one sentence. */
3
+ declare function reject(issue: StandardSchemaV1.Issue): StandardSchemaV1.FailureResult;
4
4
  /** Joins items as a sentence lists them: `a`, `a or b`, or `a, b, or c`. */
5
5
  declare function listing(items: readonly string[]): string;
6
6
  export { listing, reject };
package/dist/issues.js CHANGED
@@ -3,9 +3,9 @@ const pair = 2;
3
3
  const first = 0;
4
4
  /** The index of the last item, counted from the end. */
5
5
  const last = -1;
6
- /** A rejection: one issue with no path and no code, carrying the configuration's one sentence. */
7
- function reject(message) {
8
- return { issues: [{ message }] };
6
+ /** A rejection: one issue with no path, carrying the configuration's code and one sentence. */
7
+ function reject(issue) {
8
+ return { issues: [issue] };
9
9
  }
10
10
  /** Joins items as a sentence lists them: `a`, `a or b`, or `a, b, or c`. */
11
11
  function listing(items) {
package/dist/number.js CHANGED
@@ -1,4 +1,5 @@
1
- import { boundsSchema, boundsSentence, readBounds, withinBounds, withoutNegativeZero, } from './bounds.js';
1
+ import { boundsIssue, boundsSchema, readBounds, withinBounds, withoutNegativeZero, } from './bounds.js';
2
+ import { numberCodes } from './codes.js';
2
3
  import { createValidator } from './create.js';
3
4
  import { reject } from './issues.js';
4
5
  const decimal = /^-?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$/u;
@@ -10,15 +11,15 @@ function number(options) {
10
11
  factory: 'number',
11
12
  requirement: 'a finite number',
12
13
  });
13
- const sentence = boundsSentence('a number', bounds);
14
+ const issue = boundsIssue(numberCodes, bounds);
14
15
  return createValidator({
15
16
  inputSchema: boundsSchema('number', bounds),
16
17
  parse: (raw) => {
17
18
  if (!decimal.test(raw)) {
18
- return reject(sentence);
19
+ return reject(issue);
19
20
  }
20
21
  const value = withoutNegativeZero(Number(raw));
21
- return Number.isFinite(value) && withinBounds(value, bounds) ? { value } : reject(sentence);
22
+ return Number.isFinite(value) && withinBounds(value, bounds) ? { value } : reject(issue);
22
23
  },
23
24
  });
24
25
  }
package/dist/one-of.js CHANGED
@@ -1,46 +1,78 @@
1
+ import { DeclarationError } from '@loomcli/core';
2
+ import { oneOfIssue } from './codes.js';
1
3
  import { createValidator } from './create.js';
2
4
  import { fault } from './faults.js';
3
5
  import { reject } from './issues.js';
6
+ import { oneOfValues } from './rules.js';
4
7
  const empty = 0;
8
+ /** A fault of the list `oneOf()` received, marking the list or one of its values. */
9
+ function listFault(values, mark, parts) {
10
+ return fault(oneOfValues, { arguments: [values], factory: 'oneOf', mark }, parts);
11
+ }
5
12
  /** The declared list as unknown items, after faulting on one that is not an array or is empty. */
6
13
  function checkList(values) {
7
14
  if (!Array.isArray(values)) {
8
- throw fault('oneOf() values is not an array. Supply an array of strings.');
15
+ throw listFault(values, '0', {
16
+ correction: 'Supply an array of strings.',
17
+ sentence: 'oneOf() values is not an array.',
18
+ });
9
19
  }
10
20
  const items = values;
11
21
  if (items.length === empty) {
12
- throw fault('oneOf() values is empty. List at least one value.');
22
+ throw listFault(values, '0', {
23
+ correction: 'List at least one value.',
24
+ sentence: 'oneOf() values is empty.',
25
+ });
13
26
  }
14
27
  return items;
15
28
  }
16
- /** Faults on a value that is not a string, is empty, or was listed earlier. */
17
- function checkValue(item, seen) {
29
+ /**
30
+ * Faults on a value that is not a string, is empty, or was listed earlier. `seen` holds the
31
+ * position of each value listed so far, so a repeat marks both.
32
+ */
33
+ function checkValue(listed, index, seen) {
34
+ const item = listed[index];
18
35
  if (typeof item !== 'string') {
19
- throw fault('oneOf() lists a value that is not a string. List strings only.');
36
+ throw listFault(listed, `0.${String(index)}`, {
37
+ correction: 'List strings only.',
38
+ sentence: 'oneOf() lists a value that is not a string.',
39
+ });
20
40
  }
21
41
  if (item === '') {
22
- throw fault('oneOf() lists an empty string. List nonempty values only.');
42
+ throw listFault(listed, `0.${String(index)}`, {
43
+ correction: 'List nonempty values only.',
44
+ sentence: 'oneOf() lists an empty string.',
45
+ });
23
46
  }
24
- if (seen.has(item)) {
25
- throw fault(`oneOf() lists ${JSON.stringify(item)} twice. List each value once.`);
47
+ const first = seen.get(item);
48
+ if (first !== undefined) {
49
+ const call = { arguments: [listed], call: 'oneOf' };
50
+ throw new DeclarationError(oneOfValues, {
51
+ correction: 'List each value once.',
52
+ findings: [
53
+ { ...call, mark: `0.${String(first)}`, note: 'the first' },
54
+ { ...call, mark: `0.${String(index)}`, note: 'the second' },
55
+ ],
56
+ sentence: `oneOf() lists ${JSON.stringify(item)} twice.`,
57
+ });
26
58
  }
27
- seen.add(item);
59
+ seen.set(item, index);
28
60
  }
29
61
  /** A token exactly equal to one of the declared values, typed as their union. */
30
62
  function oneOf(values) {
31
63
  checkList(values);
32
64
  // The copy is what the validator keeps, so the checks read the copy and not the caller's list.
33
65
  const listed = [...values];
34
- const seen = new Set();
35
- for (const item of listed) {
36
- checkValue(item, seen);
66
+ const seen = new Map();
67
+ for (const index of listed.keys()) {
68
+ checkValue(listed, index, seen);
37
69
  }
38
- const sentence = `Expected one of: ${listed.join(', ')}.`;
70
+ const issue = oneOfIssue.issue({ values: listed });
39
71
  return createValidator({
40
72
  inputSchema: { type: 'string', enum: [...listed] },
41
73
  parse: (raw) => {
42
74
  const match = listed.find((value) => value === raw);
43
- return match === undefined ? reject(sentence) : { value: match };
75
+ return match === undefined ? reject(issue) : { value: match };
44
76
  },
45
77
  });
46
78
  }
@@ -0,0 +1,54 @@
1
+ import type { StandardSchemaV1 } from '@loomcli/core';
2
+ import type { PathKind } from './probe.js';
3
+ /** A check on one parameter that also tells the compiler its type. */
4
+ type Guard<Value> = (value: unknown) => value is Value;
5
+ /** The parameters of a code whose sentence has no blank. */
6
+ type NoParams = Readonly<Record<string, never>>;
7
+ declare const isCount: Guard<number>;
8
+ declare const isSafeInteger: Guard<number>;
9
+ declare const isFiniteNumber: Guard<number>;
10
+ declare const noParams: StandardSchemaV1<Readonly<Record<string, never>>, Readonly<Record<string, never>>>;
11
+ declare function minParams(accepts: Guard<number>): StandardSchemaV1<{
12
+ min: number;
13
+ }, {
14
+ min: number;
15
+ }>;
16
+ declare function maxParams(accepts: Guard<number>): StandardSchemaV1<{
17
+ max: number;
18
+ }, {
19
+ max: number;
20
+ }>;
21
+ declare function rangeParams(accepts: Guard<number>): StandardSchemaV1<{
22
+ max: number;
23
+ min: number;
24
+ }, {
25
+ max: number;
26
+ min: number;
27
+ }>;
28
+ declare const lengthParams: StandardSchemaV1<{
29
+ length: number;
30
+ }, {
31
+ length: number;
32
+ }>;
33
+ declare const messageParams: StandardSchemaV1<{
34
+ message: string;
35
+ }, {
36
+ message: string;
37
+ }>;
38
+ declare const valuesParams: StandardSchemaV1<{
39
+ values: readonly string[];
40
+ }, {
41
+ values: readonly string[];
42
+ }>;
43
+ declare const protocolsParams: StandardSchemaV1<{
44
+ protocols: readonly string[];
45
+ }, {
46
+ protocols: readonly string[];
47
+ }>;
48
+ declare const kindParams: StandardSchemaV1<{
49
+ kind: PathKind;
50
+ }, {
51
+ kind: PathKind;
52
+ }>;
53
+ export { isCount, isFiniteNumber, isSafeInteger, kindParams, lengthParams, maxParams, messageParams, minParams, noParams, protocolsParams, rangeParams, valuesParams, };
54
+ export type { NoParams };
package/dist/params.js ADDED
@@ -0,0 +1,68 @@
1
+ import { vendor } from './create.js';
2
+ import { isPlainObject } from './data.js';
3
+ /**
4
+ * A Standard Schema for one fixed parameter shape, built without a schema library. It accepts a
5
+ * plain object with exactly `keys`, which `read` turns into the typed parameters, and outputs a
6
+ * frozen copy, so an issue's parameters cannot be changed by a reader.
7
+ */
8
+ function paramsSchema(shape, keys, read) {
9
+ const refusal = () => ({
10
+ issues: [{ message: `Expected the parameters ${shape}.` }],
11
+ });
12
+ return Object.freeze({
13
+ '~standard': Object.freeze({
14
+ validate: (value) => {
15
+ if (!isPlainObject(value)) {
16
+ return refusal();
17
+ }
18
+ const exact = Object.keys(value).length === keys.length &&
19
+ keys.every((key) => Object.hasOwn(value, key));
20
+ const params = exact ? read(value) : undefined;
21
+ if (params === undefined) {
22
+ return refusal();
23
+ }
24
+ Object.freeze(params);
25
+ return { value: params };
26
+ },
27
+ vendor,
28
+ version: 1,
29
+ }),
30
+ });
31
+ }
32
+ const none = 0;
33
+ const isCount = (value) => typeof value === 'number' && Number.isSafeInteger(value) && value >= none;
34
+ const isSafeInteger = (value) => typeof value === 'number' && Number.isSafeInteger(value);
35
+ const isFiniteNumber = (value) => typeof value === 'number' && Number.isFinite(value);
36
+ const isKind = (value) => value === 'file' || value === 'directory' || value === 'any';
37
+ /** A nonempty list of strings, copied and frozen, or undefined for anything else. */
38
+ function stringList(value) {
39
+ if (!Array.isArray(value)) {
40
+ return undefined;
41
+ }
42
+ const items = value;
43
+ return items.length > none && items.every((item) => typeof item === 'string')
44
+ ? Object.freeze([...items])
45
+ : undefined;
46
+ }
47
+ const noParams = paramsSchema('{}', [], () => ({}));
48
+ function minParams(accepts) {
49
+ return paramsSchema('{ min: number }', ['min'], ({ min }) => accepts(min) ? { min } : undefined);
50
+ }
51
+ function maxParams(accepts) {
52
+ return paramsSchema('{ max: number }', ['max'], ({ max }) => accepts(max) ? { max } : undefined);
53
+ }
54
+ function rangeParams(accepts) {
55
+ return paramsSchema('{ min: number; max: number }', ['max', 'min'], ({ max, min }) => accepts(max) && accepts(min) ? { max, min } : undefined);
56
+ }
57
+ const lengthParams = paramsSchema('{ length: number }', ['length'], ({ length }) => isCount(length) ? { length } : undefined);
58
+ const messageParams = paramsSchema('{ message: string }', ['message'], ({ message }) => typeof message === 'string' && message !== '' ? { message } : undefined);
59
+ const valuesParams = paramsSchema('{ values: readonly string[] }', ['values'], (record) => {
60
+ const values = stringList(record.values);
61
+ return values === undefined ? undefined : { values };
62
+ });
63
+ const protocolsParams = paramsSchema('{ protocols: readonly string[] }', ['protocols'], (record) => {
64
+ const protocols = stringList(record.protocols);
65
+ return protocols === undefined ? undefined : { protocols };
66
+ });
67
+ const kindParams = paramsSchema("{ kind: 'file' | 'directory' | 'any' }", ['kind'], ({ kind }) => isKind(kind) ? { kind } : undefined);
68
+ export { isCount, isFiniteNumber, isSafeInteger, kindParams, lengthParams, maxParams, messageParams, minParams, noParams, protocolsParams, rangeParams, valuesParams, };
package/dist/path.js CHANGED
@@ -1,34 +1,34 @@
1
1
  import { posix, win32 } from 'node:path';
2
+ import { pathIssue, pathReadableIssue, pathWritableIssue } from './codes.js';
2
3
  import { createValidator } from './create.js';
3
4
  import { fault, readOptions } from './faults.js';
4
5
  import { reject } from './issues.js';
5
6
  import { readable, writable } from './probe.js';
6
- const sentences = {
7
- read: {
8
- any: 'Expected a readable file or directory that exists.',
9
- directory: 'Expected a readable directory that exists.',
10
- file: 'Expected a readable file that exists.',
11
- },
12
- write: {
13
- any: 'Expected a writable path, or a new path in a writable directory.',
14
- directory: 'Expected a writable directory, or a new directory in a writable directory.',
15
- file: 'Expected a writable file, or a new file in a writable directory.',
16
- },
17
- };
18
- function accessOf(value) {
7
+ import { pathCheck } from './rules.js';
8
+ function accessOf(call, value) {
19
9
  if (value === undefined || value === 'read' || value === 'write') {
20
10
  return value;
21
11
  }
22
- throw fault('path() access is not "read" or "write". Supply "read" or "write".');
12
+ throw fault(pathCheck, { ...call, mark: '0.access' }, {
13
+ correction: 'Supply "read" or "write".',
14
+ sentence: 'path() access is not "read" or "write".',
15
+ });
23
16
  }
24
- function kindOf(value, access) {
17
+ function kindOf(call, value, access) {
18
+ const at = { ...call, mark: '0.kind' };
25
19
  if (value !== undefined && access === undefined) {
26
- throw fault('path() kind has no access to check. Supply an access or leave out kind.');
20
+ throw fault(pathCheck, at, {
21
+ correction: 'Supply an access or leave out kind.',
22
+ sentence: 'path() kind has no access to check.',
23
+ });
27
24
  }
28
25
  if (value === undefined || value === 'file' || value === 'directory' || value === 'any') {
29
26
  return value ?? 'file';
30
27
  }
31
- throw fault('path() kind is not "file", "directory", or "any". Supply one of them.');
28
+ throw fault(pathCheck, at, {
29
+ correction: 'Supply one of them.',
30
+ sentence: 'path() kind is not "file", "directory", or "any".',
31
+ });
32
32
  }
33
33
  /**
34
34
  * A path resolved against the host's cwd under the host platform's rules, and normalized.
@@ -36,14 +36,17 @@ function kindOf(value, access) {
36
36
  */
37
37
  function path(options) {
38
38
  const declared = readOptions('path', options);
39
- const access = accessOf(declared.access);
40
- const kind = kindOf(declared.kind, access);
41
- const sentence = access === undefined ? 'Expected a path.' : sentences[access][kind];
39
+ const call = { arguments: [options], factory: 'path' };
40
+ const access = accessOf(call, declared.access);
41
+ const kind = kindOf(call, declared.kind, access);
42
+ const issue = access === undefined
43
+ ? pathIssue.issue({})
44
+ : (access === 'read' ? pathReadableIssue : pathWritableIssue).issue({ kind });
42
45
  return createValidator({
43
46
  inputSchema: { type: 'string', minLength: 1 },
44
47
  parse: (raw, context) => {
45
48
  if (raw === '' || raw.includes('\0')) {
46
- return reject(sentence);
49
+ return reject(issue);
47
50
  }
48
51
  const { cwd, platform } = context.host;
49
52
  const rules = platform === 'win32' ? win32 : posix;
@@ -54,7 +57,7 @@ function path(options) {
54
57
  const probe = access === 'read'
55
58
  ? readable(resolved, kind)
56
59
  : writable(resolved, kind, rules.dirname(resolved));
57
- return probe.then((passes) => (passes ? { value: resolved } : reject(sentence)));
60
+ return probe.then((passes) => (passes ? { value: resolved } : reject(issue)));
58
61
  },
59
62
  });
60
63
  }
package/dist/port.js CHANGED
@@ -1,17 +1,17 @@
1
1
  import { boundsSchema, withinBounds } from './bounds.js';
2
+ import { portIssue } from './codes.js';
2
3
  import { createValidator } from './create.js';
3
4
  import { readInteger } from './integer.js';
4
5
  import { reject } from './issues.js';
5
6
  const range = { max: 65_535, min: 1 };
6
7
  /** A TCP or UDP port the operator names, 1 through 65535; port 0 is left to `integer()`. */
7
8
  function port() {
9
+ const issue = portIssue.issue({});
8
10
  return createValidator({
9
11
  inputSchema: boundsSchema('integer', range),
10
12
  parse: (raw) => {
11
13
  const value = readInteger(raw);
12
- return value !== undefined && withinBounds(value, range)
13
- ? { value }
14
- : reject('Expected a port number from 1 through 65535.');
14
+ return value !== undefined && withinBounds(value, range) ? { value } : reject(issue);
15
15
  },
16
16
  });
17
17
  }
@@ -0,0 +1,32 @@
1
+ import type { DiagnosticRule } from '@loomcli/core';
2
+ /** A factory's options that are not a plain object. */
3
+ declare const factoryOptions: DiagnosticRule;
4
+ /** A `oneOf()` list that is not a list of distinct nonempty strings. */
5
+ declare const oneOfValues: DiagnosticRule;
6
+ /** A bound that is not a number of the kind its validator reads. */
7
+ declare const boundValue: DiagnosticRule;
8
+ /** A lower bound above its upper bound. */
9
+ declare const boundsOrder: DiagnosticRule;
10
+ /** A `url()` protocols list that is not a list of scheme names. */
11
+ declare const urlProtocols: DiagnosticRule;
12
+ /** A `path()` access or kind outside its set, or a kind with no access to narrow. */
13
+ declare const pathCheck: DiagnosticRule;
14
+ /** A `text()` pattern that JSON Schema cannot read as it is. */
15
+ declare const textPattern: DiagnosticRule;
16
+ /** A `text()` pattern without its sentence, or a sentence without a pattern. */
17
+ declare const patternMessage: DiagnosticRule;
18
+ /** An issue code outside the `<package>[/<subpath>...]/<kebab-case-rule>` grammar. */
19
+ declare const issueCodeName: DiagnosticRule;
20
+ /** An `issueCode()` config without a Standard Schema and a message function. */
21
+ declare const issueCodeConfig: DiagnosticRule;
22
+ /** An issue code schema that answers with a promise, throws, or answers with no result. */
23
+ declare const issueCodeSchema: DiagnosticRule;
24
+ /** Parameters an issue code's schema rejects. */
25
+ declare const issueParameters: DiagnosticRule;
26
+ /** A `createValidator()` definition without a parse function. */
27
+ declare const validatorDefinition: DiagnosticRule;
28
+ /** A `createValidator()` input schema that is not a plain object, or declares its own dialect. */
29
+ declare const inputSchema: DiagnosticRule;
30
+ /** A validator that read the validation context outside a Loom run. */
31
+ declare const contextOutsideRun: DiagnosticRule;
32
+ export { boundsOrder, boundValue, contextOutsideRun, factoryOptions, inputSchema, issueCodeConfig, issueCodeName, issueCodeSchema, issueParameters, oneOfValues, pathCheck, patternMessage, textPattern, urlProtocols, validatorDefinition, };