mailschema 0.2.2 → 0.3.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.
Files changed (52) hide show
  1. package/README.md +25 -119
  2. package/bin/mailschema.js +40 -53
  3. package/dist/context.jsonld +50 -0
  4. package/dist/contract.schema.json +206 -0
  5. package/dist/core/artifacts.d.ts +11 -36
  6. package/dist/core/artifacts.js +21 -69
  7. package/dist/core/bundled.d.ts +4 -4
  8. package/dist/core/bundled.js +7 -7
  9. package/dist/core/contract.d.ts +59 -56
  10. package/dist/core/contract.js +224 -298
  11. package/dist/core/description.d.ts +45 -0
  12. package/dist/core/description.js +47 -0
  13. package/dist/core/implementation.d.ts +32 -0
  14. package/dist/core/implementation.js +25 -0
  15. package/dist/core/index.d.ts +7 -6
  16. package/dist/core/index.js +9 -9
  17. package/dist/core/json.d.ts +23 -0
  18. package/dist/core/{document.js → json.js} +61 -51
  19. package/dist/core/patterns.d.ts +12 -0
  20. package/dist/core/patterns.js +165 -0
  21. package/dist/core/sha256.d.ts +2 -0
  22. package/dist/core/sha256.js +59 -0
  23. package/dist/core.schema.json +210 -0
  24. package/dist/implementation.schema.json +173 -0
  25. package/dist/index.d.ts +4 -9
  26. package/dist/index.js +2 -14
  27. package/dist/profile.json +27 -0
  28. package/package.json +9 -10
  29. package/dist/contribution.schema.json +0 -874
  30. package/dist/core/binding.d.ts +0 -24
  31. package/dist/core/binding.js +0 -50
  32. package/dist/core/document.d.ts +0 -24
  33. package/dist/core/documents.d.ts +0 -68
  34. package/dist/core/documents.js +0 -145
  35. package/dist/core/limits.d.ts +0 -19
  36. package/dist/core/limits.js +0 -55
  37. package/dist/core/pointer.d.ts +0 -12
  38. package/dist/core/pointer.js +0 -48
  39. package/dist/core/schemas.d.ts +0 -21
  40. package/dist/core/schemas.js +0 -125
  41. package/dist/core/types.d.ts +0 -127
  42. package/dist/core/types.js +0 -2
  43. package/dist/forms-0.1.schema.json +0 -329
  44. package/dist/map-0.2.jsonld +0 -91
  45. package/dist/map-0.2.schema.json +0 -1033
  46. package/dist/map.d.ts +0 -8
  47. package/dist/map.js +0 -18
  48. package/dist/model.d.ts +0 -95
  49. package/dist/model.js +0 -5
  50. package/dist/type-contract-0.2.schema.json +0 -179
  51. package/dist/validation.d.ts +0 -15
  52. package/dist/validation.js +0 -64
@@ -0,0 +1,32 @@
1
+ export interface ImplementationRecord {
2
+ service: string;
3
+ maintainer: {
4
+ name: string;
5
+ url: string;
6
+ };
7
+ type: {
8
+ id: string;
9
+ version: string;
10
+ contractDigest: string;
11
+ };
12
+ operations: string[];
13
+ binding: string;
14
+ artifact?: {
15
+ url: string;
16
+ digest: string;
17
+ digestMode: 'canonical-json' | 'bytes';
18
+ };
19
+ format?: string;
20
+ status: 'Draft' | 'Experimental' | 'Stable' | 'Deprecated' | 'Superseded';
21
+ documentation: string;
22
+ evidence: {
23
+ kind: 'declaration' | 'test-report';
24
+ url: string;
25
+ summary: string;
26
+ }[];
27
+ successor?: string;
28
+ }
29
+ /** Why the value is not an implementation record, or no reasons. */
30
+ export declare function implementationErrors(value: unknown): string[];
31
+ /** The record in the bytes or text, or InvalidDocument with every reason it is not one. */
32
+ export declare function parseImplementation(input: string | Uint8Array): ImplementationRecord;
@@ -0,0 +1,25 @@
1
+ // An implementation record: a Registry declaration or report of a service's support for exact
2
+ // contract versions.
3
+ import { CONTRACT_MAX_BYTES, createValidator, implementationSchema, schemaErrors, } from "./artifacts.js";
4
+ import { InvalidDocument, parse } from "./json.js";
5
+ const validate = createValidator().compile(implementationSchema);
6
+ /** Why the value is not an implementation record, or no reasons. */
7
+ export function implementationErrors(value) {
8
+ if (!validate(value))
9
+ return schemaErrors(validate.errors);
10
+ const errors = [];
11
+ const { origin } = new URL(value.service);
12
+ if (value.service !== origin && value.service !== `${origin}/`)
13
+ errors.push('/service: must be an HTTPS origin, not an operation path');
14
+ if (new Set(value.operations).size !== value.operations.length)
15
+ errors.push('/operations: identifiers must be unique');
16
+ return errors;
17
+ }
18
+ /** The record in the bytes or text, or InvalidDocument with every reason it is not one. */
19
+ export function parseImplementation(input) {
20
+ const value = parse(input, CONTRACT_MAX_BYTES);
21
+ const errors = implementationErrors(value);
22
+ if (errors.length)
23
+ throw new InvalidDocument('The value is not an implementation record.', errors);
24
+ return value;
25
+ }
@@ -1,6 +1,7 @@
1
- export { MAP_PROFILE, CORE_SCHEMA, FORMS_SCHEMA, CONTRACT_FORMAT, CONTEXT, mapErrors, descriptionErrors, requestErrors, resultErrors, problemErrors, isRequestId, reached, } from './artifacts.ts';
2
- export { InvalidDocument, parse, canonicalize, digest } from './document.ts';
3
- export { APPROVAL_REASONS, Contract, InvalidContract, type RequestProblem } from './contract.ts';
4
- export { PROBLEM_STATUS, type ProblemCode, result, transition, problem, resultStatus, retainUntil, settle, } from './documents.ts';
5
- export { DESCRIPTION_MEDIA_TYPE, isDescriptionPart, capability, writtenPath, isJsonRequest, resultUrl, } from './binding.ts';
6
- export type { Authority, Consequence, ContractOperation, InputError, JsonObject, MapDescription, MapProblem, MapRequest, MapResult, ResultState, SchemaReference, Target, TypeContract, TypeReference, } from './types.ts';
1
+ export { PROFILE, CONTEXT, DESCRIPTION_MEDIA_TYPE, DESCRIPTION_MAX_BYTES, CONTRACT_MAX_BYTES, EFFECTS, FORMATS, coreSchema, contractSchema, implementationSchema, } from './artifacts.ts';
2
+ export { InvalidDocument, MAX_DEPTH, decodeUtf8, parse, canonicalize, digest } from './json.ts';
3
+ export { sha256 } from './sha256.ts';
4
+ export { descriptionErrors, parseDescription, type Description } from './description.ts';
5
+ export { CAPABILITY_KINDS, Contract, contractErrors, type CapabilityKind, type ContractDocument, type Operation, } from './contract.ts';
6
+ export { implementationErrors, parseImplementation, type ImplementationRecord, } from './implementation.ts';
7
+ export { unportablePattern } from './patterns.ts';
@@ -1,9 +1,9 @@
1
- // Mail Action Protocol 0.2 processing. It parses, canonicalizes, digests and validates MAP
2
- // documents, checks the type contracts an implementation vendors, and builds results and
3
- // problems. It does not establish endpoint trust, verify email authentication, grant
4
- // authority or send email.
5
- export { MAP_PROFILE, CORE_SCHEMA, FORMS_SCHEMA, CONTRACT_FORMAT, CONTEXT, mapErrors, descriptionErrors, requestErrors, resultErrors, problemErrors, isRequestId, reached, } from "./artifacts.js";
6
- export { InvalidDocument, parse, canonicalize, digest } from "./document.js";
7
- export { APPROVAL_REASONS, Contract, InvalidContract } from "./contract.js";
8
- export { PROBLEM_STATUS, result, transition, problem, resultStatus, retainUntil, settle, } from "./documents.js";
9
- export { DESCRIPTION_MEDIA_TYPE, isDescriptionPart, capability, writtenPath, isJsonRequest, resultUrl, } from "./binding.js";
1
+ // Mail Action Protocol 0.3 processing: one implementation, run unchanged by the repository's
2
+ // site and checks and shipped as the npm package.
3
+ export { PROFILE, CONTEXT, DESCRIPTION_MEDIA_TYPE, DESCRIPTION_MAX_BYTES, CONTRACT_MAX_BYTES, EFFECTS, FORMATS, coreSchema, contractSchema, implementationSchema, } from "./artifacts.js";
4
+ export { InvalidDocument, MAX_DEPTH, decodeUtf8, parse, canonicalize, digest } from "./json.js";
5
+ export { sha256 } from "./sha256.js";
6
+ export { descriptionErrors, parseDescription } from "./description.js";
7
+ export { CAPABILITY_KINDS, Contract, contractErrors, } from "./contract.js";
8
+ export { implementationErrors, parseImplementation, } from "./implementation.js";
9
+ export { unportablePattern } from "./patterns.js";
@@ -0,0 +1,23 @@
1
+ /** A document that is not MAP JSON, or not the document it claims to be. */
2
+ export declare class InvalidDocument extends Error {
3
+ readonly errors: string[];
4
+ constructor(message: string, errors?: string[]);
5
+ }
6
+ export declare const MAX_DEPTH = 32;
7
+ /** The bytes as text, refusing anything but well-formed UTF-8. */
8
+ export declare function decodeUtf8(input: Uint8Array): string;
9
+ /**
10
+ * Parse MAP JSON from its original text: UTF-8 I-JSON (RFC 7493) of at most `maxBytes`, with
11
+ * no byte order mark, duplicate member names, lone surrogates, noncharacters or U+0000, no
12
+ * container deeper than 32 counting the root as 1, and every number a finite token of at most
13
+ * 64 characters within ±(2^53−1) that does not underflow to zero. These rules apply to the
14
+ * original tokens, before a general parser could erase them.
15
+ */
16
+ export declare function parse(input: string | Uint8Array, maxBytes: number): unknown;
17
+ /**
18
+ * The RFC 8785 canonical form of a JSON value: members sorted by their UTF-16 code units,
19
+ * ECMAScript's minimal string escapes and its number format.
20
+ */
21
+ export declare function canonicalize(value: unknown): string;
22
+ /** `sha-256:` and the SHA-256 of the value's RFC 8785 form, as MAP identifies contracts. */
23
+ export declare const digest: (value: unknown) => string;
@@ -1,38 +1,46 @@
1
- // Parsing MAP documents as I-JSON within the core limits.
2
- import { createHash } from 'node:crypto';
3
- /** A MAP document that is not I-JSON within the core limits. */
1
+ // MAP JSON: I-JSON parsed from its original text, and the RFC 8785 digests that identify
2
+ // contracts and descriptions.
3
+ import { sha256 } from "./sha256.js";
4
+ /** A document that is not MAP JSON, or not the document it claims to be. */
4
5
  export class InvalidDocument extends Error {
6
+ errors;
7
+ constructor(message, errors = [message]) {
8
+ super(message);
9
+ this.errors = errors;
10
+ }
5
11
  }
6
- export const MAX_BYTES = 64 * 1024;
7
12
  export const MAX_DEPTH = 32;
13
+ const MAX_NUMBER_TOKEN = 64;
8
14
  const LONE_SURROGATE = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/;
9
- // Unicode's noncharacters: U+FDD0 to U+FDEF, and the last two code points of every plane.
10
- const NONCHARACTER = new RegExp(`[\\u{FDD0}-\\u{FDEF}${Array.from({ length: 17 }, (_, plane) => `\\u{${(plane * 0x10000 + 0xfffe).toString(16)}}\\u{${(plane * 0x10000 + 0xffff).toString(16)}}`).join('')}]`, 'u');
15
+ const NUMBER = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/;
16
+ const ZERO = /^-?0(?:\.0+)?(?:[eE][+-]?[0-9]+)?$/;
17
+ const forbidden = (point) => point === 0 || (point >= 0xfdd0 && point <= 0xfdef) || (point & 0xfffe) === 0xfffe;
18
+ /** The bytes as text, refusing anything but well-formed UTF-8. */
19
+ export function decodeUtf8(input) {
20
+ try {
21
+ // The byte order mark is kept, so parsing refuses it.
22
+ return new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(input);
23
+ }
24
+ catch {
25
+ throw new InvalidDocument('The JSON is not valid UTF-8.');
26
+ }
27
+ }
11
28
  /**
12
- * Parse a MAP document as I-JSON (RFC 7493) within the core limits: valid UTF-8 with no
13
- * byte order mark, at most 64 KiB, no duplicate member names, no lone surrogates,
14
- * noncharacters or U+0000 in any string, every number within ±(2^53−1), and arrays and
15
- * objects nested at most 32 deep, the outermost counting as one. Text is measured in its
16
- * UTF-8 bytes; bytes must be strict UTF-8.
29
+ * Parse MAP JSON from its original text: UTF-8 I-JSON (RFC 7493) of at most `maxBytes`, with
30
+ * no byte order mark, duplicate member names, lone surrogates, noncharacters or U+0000, no
31
+ * container deeper than 32 counting the root as 1, and every number a finite token of at most
32
+ * 64 characters within ±(2^53−1) that does not underflow to zero. These rules apply to the
33
+ * original tokens, before a general parser could erase them.
17
34
  */
18
- export function parse(input) {
19
- let text;
20
- if (typeof input === 'string')
21
- text = input;
22
- else {
23
- try {
24
- // The byte order mark is kept, so the parser refuses it.
25
- text = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(input);
26
- }
27
- catch {
28
- throw new InvalidDocument('The MAP document is not valid UTF-8.');
29
- }
30
- }
31
- if (new TextEncoder().encode(text).length > MAX_BYTES)
32
- throw new InvalidDocument(`The MAP document exceeds ${MAX_BYTES} bytes.`);
35
+ export function parse(input, maxBytes) {
36
+ const text = typeof input === 'string' ? input : decodeUtf8(input);
37
+ if (text.charCodeAt(0) === 0xfeff)
38
+ throw new InvalidDocument('A byte order mark is not allowed.');
39
+ if (new TextEncoder().encode(text).length > maxBytes)
40
+ throw new InvalidDocument(`The JSON exceeds ${maxBytes} bytes.`);
33
41
  let index = 0;
34
- const fail = (message) => {
35
- throw new InvalidDocument(`Invalid MAP JSON at offset ${index}: ${message}`);
42
+ const fail = (reason) => {
43
+ throw new InvalidDocument(`Invalid MAP JSON at offset ${index}: ${reason}.`);
36
44
  };
37
45
  const space = () => {
38
46
  while (' \t\n\r'.includes(text[index] ?? '-'))
@@ -55,13 +63,12 @@ export function parse(input) {
55
63
  }
56
64
  if (LONE_SURROGATE.test(value))
57
65
  fail('lone surrogate');
58
- if (value.includes('\u0000'))
59
- fail('U+0000 in a string');
60
- if (NONCHARACTER.test(value))
61
- fail('a noncharacter in a string');
66
+ for (const character of value)
67
+ if (forbidden(character.codePointAt(0)))
68
+ fail('U+0000 or a noncharacter');
62
69
  return value;
63
70
  };
64
- // `depth` counts the arrays and objects enclosing the value.
71
+ // `depth` counts the containers enclosing the value.
65
72
  const value = (depth) => {
66
73
  space();
67
74
  const next = text[index];
@@ -131,15 +138,23 @@ export function parse(input) {
131
138
  }
132
139
  if (next === '"')
133
140
  return string();
134
- const literal = /^(?:true|false|null|-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)/.exec(text.slice(index));
135
- if (!literal)
136
- fail('unexpected token');
137
- index += literal[0].length;
138
- const parsed = JSON.parse(literal[0]);
139
- if (typeof parsed === 'number' &&
140
- (!Number.isFinite(parsed) || Math.abs(parsed) > Number.MAX_SAFE_INTEGER))
141
- fail('number outside the I-JSON range');
142
- return parsed;
141
+ for (const literal of ['true', 'false', 'null'])
142
+ if (text.startsWith(literal, index)) {
143
+ index += literal.length;
144
+ return JSON.parse(literal);
145
+ }
146
+ const token = NUMBER.exec(text.slice(index))?.[0];
147
+ if (token === undefined)
148
+ return fail('unexpected token');
149
+ index += token.length;
150
+ if (token.length > MAX_NUMBER_TOKEN)
151
+ fail(`a number token longer than ${MAX_NUMBER_TOKEN} characters`);
152
+ const number = Number(token);
153
+ if (!Number.isFinite(number) || Math.abs(number) > Number.MAX_SAFE_INTEGER)
154
+ fail('a number outside the I-JSON range');
155
+ if (number === 0 && !ZERO.test(token))
156
+ fail('a number that underflows to zero');
157
+ return number;
143
158
  };
144
159
  const result = value(0);
145
160
  space();
@@ -158,8 +173,7 @@ const ESCAPES = {
158
173
  };
159
174
  /**
160
175
  * The RFC 8785 canonical form of a JSON value: members sorted by their UTF-16 code units,
161
- * ECMAScript's minimal string escapes and its number format. An undefined member is
162
- * left out and an undefined array item is null, as JSON.stringify writes them.
176
+ * ECMAScript's minimal string escapes and its number format.
163
177
  */
164
178
  export function canonicalize(value) {
165
179
  if (value === null || typeof value === 'boolean')
@@ -172,19 +186,15 @@ export function canonicalize(value) {
172
186
  if (typeof value === 'string')
173
187
  return `"${value.replace(/["\\\u0000-\u001f]/g, (char) => ESCAPES[char] ?? `\\u${char.charCodeAt(0).toString(16).padStart(4, '0')}`)}"`;
174
188
  if (Array.isArray(value))
175
- return `[${value.map((item) => (item === undefined ? 'null' : canonicalize(item))).join(',')}]`;
189
+ return `[${value.map(canonicalize).join(',')}]`;
176
190
  if (typeof value === 'object') {
177
191
  const members = value;
178
192
  return `{${Object.keys(members)
179
- .filter((name) => members[name] !== undefined)
180
193
  .sort()
181
194
  .map((name) => `${canonicalize(name)}:${canonicalize(members[name])}`)
182
195
  .join(',')}}`;
183
196
  }
184
197
  throw new TypeError(`${typeof value} is not a JSON value.`);
185
198
  }
186
- /**
187
- * `sha-256:` and the SHA-256 of the RFC 8785 canonical form: the digest MAP uses for
188
- * descriptions, contracts and pinned schemas.
189
- */
190
- export const digest = (value) => `sha-256:${createHash('sha256').update(canonicalize(value)).digest('hex')}`;
199
+ /** `sha-256:` and the SHA-256 of the value's RFC 8785 form, as MAP identifies contracts. */
200
+ export const digest = (value) => `sha-256:${sha256(canonicalize(value))}`;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Why a pattern is outside the portable subset, or undefined. The subset is printable ASCII
3
+ * text, with any other code point written as \uXXXX. It has literal characters and escaped
4
+ * syntax characters; \t, \n, \f, \r and \d, the ASCII digits; non-empty classes of literal or
5
+ * escaped members and ranges, with a literal hyphen only first or last; (?: groups;
6
+ * alternation; ^ and $; and one *, +, ?, {n}, {n,} or {n,m} after an atom. Other class
7
+ * escapes and the dot match different characters in different engines, and brackets, && or a
8
+ * stray brace inside a class, or a quantifier on a quantifier, are read differently or refused
9
+ * by one of them. ^ and $ anchor the whole value, as in ECMA-262; an engine whose anchors also
10
+ * match at line breaks must read them so.
11
+ */
12
+ export declare function unportablePattern(pattern: string): string | undefined;
@@ -0,0 +1,165 @@
1
+ // The regular expressions a contract's schemas may use: the subset that ECMA-262 (with the
2
+ // u flag) and other engines read alike, so every implementation enforces the same contract.
3
+ const SYNTAX_CHARACTERS = '^$\\.*+?()[]{}|/';
4
+ const CONTROL_ESCAPES = { t: 0x09, n: 0x0a, f: 0x0c, r: 0x0d };
5
+ const QUANTIFIERS = '*+?{';
6
+ /**
7
+ * Why a pattern is outside the portable subset, or undefined. The subset is printable ASCII
8
+ * text, with any other code point written as \uXXXX. It has literal characters and escaped
9
+ * syntax characters; \t, \n, \f, \r and \d, the ASCII digits; non-empty classes of literal or
10
+ * escaped members and ranges, with a literal hyphen only first or last; (?: groups;
11
+ * alternation; ^ and $; and one *, +, ?, {n}, {n,} or {n,m} after an atom. Other class
12
+ * escapes and the dot match different characters in different engines, and brackets, && or a
13
+ * stray brace inside a class, or a quantifier on a quantifier, are read differently or refused
14
+ * by one of them. ^ and $ anchor the whole value, as in ECMA-262; an engine whose anchors also
15
+ * match at line breaks must read them so.
16
+ */
17
+ export function unportablePattern(pattern) {
18
+ if (!/^[\x20-\x7e]*$/.test(pattern))
19
+ return 'a character outside printable ASCII';
20
+ let at = 0;
21
+ // One escaped code point, the digit class outside a class, or a problem.
22
+ const escape = (inClass) => {
23
+ const next = pattern[at + 1];
24
+ if (next === undefined)
25
+ return 'a trailing backslash';
26
+ if (next === 'u') {
27
+ const digits = pattern.slice(at + 2, at + 6);
28
+ if (!/^[0-9a-fA-F]{4}$/.test(digits))
29
+ return 'a malformed \\u escape';
30
+ const code = parseInt(digits, 16);
31
+ if (code >= 0xd800 && code <= 0xdfff)
32
+ return 'a surrogate \\u escape';
33
+ at += 6;
34
+ return code;
35
+ }
36
+ at += 2;
37
+ if (next === 'd' && !inClass)
38
+ return -1;
39
+ if (Object.hasOwn(CONTROL_ESCAPES, next))
40
+ return CONTROL_ESCAPES[next];
41
+ if (SYNTAX_CHARACTERS.includes(next) || (inClass && next === '-'))
42
+ return next.charCodeAt(0);
43
+ return `the escape \\${next}`;
44
+ };
45
+ const characterClass = () => {
46
+ at += pattern[at + 1] === '^' ? 2 : 1;
47
+ // Each member's code point, or null for an unescaped hyphen.
48
+ const members = [];
49
+ while (pattern[at] !== ']') {
50
+ const char = pattern[at];
51
+ if (char === undefined)
52
+ return 'an unclosed character class';
53
+ if (char === '[')
54
+ return 'a bracket inside a class';
55
+ if (char === '&' && pattern[at + 1] === '&')
56
+ return '&& inside a class';
57
+ if (char === '\\') {
58
+ const code = escape(true);
59
+ if (typeof code === 'string')
60
+ return code;
61
+ members.push(code);
62
+ }
63
+ else {
64
+ members.push(char === '-' ? null : char.charCodeAt(0));
65
+ at += 1;
66
+ }
67
+ }
68
+ at += 1;
69
+ if (!members.length)
70
+ return 'an empty character class';
71
+ for (let index = 0; index < members.length; index += 1) {
72
+ const low = members[index];
73
+ if (low === null) {
74
+ if (index !== 0 && index !== members.length - 1)
75
+ return 'an ambiguous hyphen in a class';
76
+ continue;
77
+ }
78
+ if (members[index + 1] !== null || index + 2 >= members.length)
79
+ continue;
80
+ const high = members[index + 2];
81
+ if (high === null)
82
+ return 'an ambiguous hyphen in a class';
83
+ if (high < low)
84
+ return 'a class range out of order';
85
+ index += 2;
86
+ if (members[index + 1] === null && index + 1 !== members.length - 1)
87
+ return 'an ambiguous hyphen in a class';
88
+ }
89
+ };
90
+ const quantifier = () => {
91
+ if (pattern[at] === '{') {
92
+ const bounds = /^\{([0-9]{1,4})(?:(,)([0-9]{1,4})?)?\}/.exec(pattern.slice(at));
93
+ if (!bounds)
94
+ return 'a malformed {n,m} quantifier';
95
+ if (bounds[3] !== undefined && Number(bounds[3]) < Number(bounds[1]))
96
+ return 'a {n,m} quantifier with m below n';
97
+ at += bounds[0].length;
98
+ }
99
+ else
100
+ at += 1;
101
+ const next = pattern[at];
102
+ if (next !== undefined && QUANTIFIERS.includes(next))
103
+ return 'a quantifier on a quantifier';
104
+ };
105
+ const disjunction = (depth) => {
106
+ let quantifiable = false;
107
+ while (at < pattern.length) {
108
+ const char = pattern[at];
109
+ if (char === '|') {
110
+ at += 1;
111
+ quantifiable = false;
112
+ }
113
+ else if (char === ')') {
114
+ if (depth === 0)
115
+ return 'an unmatched )';
116
+ return;
117
+ }
118
+ else if (char === '^' || char === '$') {
119
+ at += 1;
120
+ quantifiable = false;
121
+ }
122
+ else if (QUANTIFIERS.includes(char)) {
123
+ if (!quantifiable)
124
+ return char === '{' ? 'an unescaped {' : 'a quantifier with nothing to repeat';
125
+ const problem = quantifier();
126
+ if (problem)
127
+ return problem;
128
+ quantifiable = false;
129
+ }
130
+ else if (char === '(') {
131
+ if (!pattern.startsWith('(?:', at))
132
+ return 'a group other than (?:';
133
+ at += 3;
134
+ const problem = disjunction(depth + 1);
135
+ if (problem)
136
+ return problem;
137
+ if (pattern[at] !== ')')
138
+ return 'an unclosed group';
139
+ at += 1;
140
+ quantifiable = true;
141
+ }
142
+ else if (char === '[') {
143
+ const problem = characterClass();
144
+ if (problem)
145
+ return problem;
146
+ quantifiable = true;
147
+ }
148
+ else if (char === '\\') {
149
+ const code = escape(false);
150
+ if (typeof code === 'string')
151
+ return code;
152
+ quantifiable = true;
153
+ }
154
+ else if (char === '.')
155
+ return 'an unescaped dot';
156
+ else if (char === ']' || char === '}')
157
+ return `an unescaped ${char}`;
158
+ else {
159
+ at += 1;
160
+ quantifiable = true;
161
+ }
162
+ }
163
+ };
164
+ return disjunction(0);
165
+ }
@@ -0,0 +1,2 @@
1
+ /** The lowercase hexadecimal SHA-256 of the bytes, or of the text's UTF-8 encoding. */
2
+ export declare function sha256(input: string | Uint8Array): string;
@@ -0,0 +1,59 @@
1
+ // SHA-256 (FIPS 180-4) over bytes, so the core needs no platform crypto and runs the same
2
+ // in Node, browsers and workers.
3
+ const K = Uint32Array.from([
4
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
5
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
6
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
7
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
8
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
9
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
10
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
11
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
12
+ ]);
13
+ const rotate = (value, bits) => (value >>> bits) | (value << (32 - bits));
14
+ /** The lowercase hexadecimal SHA-256 of the bytes, or of the text's UTF-8 encoding. */
15
+ export function sha256(input) {
16
+ const bytes = typeof input === 'string' ? new TextEncoder().encode(input) : input;
17
+ // The message, a 1 bit, zeros, and its length in bits as a 64-bit big-endian integer.
18
+ const padded = new Uint8Array(Math.ceil((bytes.length + 9) / 64) * 64);
19
+ padded.set(bytes);
20
+ padded[bytes.length] = 0x80;
21
+ const view = new DataView(padded.buffer);
22
+ view.setUint32(padded.length - 8, Math.floor(bytes.length / 0x20000000));
23
+ view.setUint32(padded.length - 4, (bytes.length * 8) >>> 0);
24
+ const hash = Uint32Array.from([
25
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
26
+ ]);
27
+ const w = new Uint32Array(64);
28
+ for (let block = 0; block < padded.length; block += 64) {
29
+ for (let t = 0; t < 16; t += 1)
30
+ w[t] = view.getUint32(block + t * 4);
31
+ for (let t = 16; t < 64; t += 1) {
32
+ const s0 = rotate(w[t - 15], 7) ^ rotate(w[t - 15], 18) ^ (w[t - 15] >>> 3);
33
+ const s1 = rotate(w[t - 2], 17) ^ rotate(w[t - 2], 19) ^ (w[t - 2] >>> 10);
34
+ w[t] = w[t - 16] + s0 + w[t - 7] + s1;
35
+ }
36
+ let [a, b, c, d, e, f, g, h] = hash;
37
+ for (let t = 0; t < 64; t += 1) {
38
+ const t1 = h + (rotate(e, 6) ^ rotate(e, 11) ^ rotate(e, 25)) + ((e & f) ^ (~e & g)) + K[t] + w[t];
39
+ const t2 = (rotate(a, 2) ^ rotate(a, 13) ^ rotate(a, 22)) + ((a & b) ^ (a & c) ^ (b & c));
40
+ h = g;
41
+ g = f;
42
+ f = e;
43
+ e = (d + t1) >>> 0;
44
+ d = c;
45
+ c = b;
46
+ b = a;
47
+ a = (t1 + t2) >>> 0;
48
+ }
49
+ hash[0] += a;
50
+ hash[1] += b;
51
+ hash[2] += c;
52
+ hash[3] += d;
53
+ hash[4] += e;
54
+ hash[5] += f;
55
+ hash[6] += g;
56
+ hash[7] += h;
57
+ }
58
+ return Array.from(hash, (word) => word.toString(16).padStart(8, '0')).join('');
59
+ }