mailschema 0.1.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/LICENSE +21 -0
- package/README.md +31 -0
- package/cli.mjs +53 -0
- package/dist/contribution.schema.json +860 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +12 -0
- package/dist/model.d.ts +94 -0
- package/dist/model.js +5 -0
- package/dist/validation.d.ts +15 -0
- package/dist/validation.js +61 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MailSchema contributors
|
|
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,31 @@
|
|
|
1
|
+
# MailSchema
|
|
2
|
+
|
|
3
|
+
JSON Schemas, TypeScript definitions and validation tools for the MailSchema Registry.
|
|
4
|
+
|
|
5
|
+
Prepare a new interaction type, amend an existing definition, or declare an implementation's supported operations. This package checks the Registry contribution format. It does not implement email delivery, authorization or the draft Mail Action Protocol wire format.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install mailschema
|
|
9
|
+
npx mailschema check contribution.json
|
|
10
|
+
npx mailschema schema > contribution.schema.json
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Node.js 22 or newer. The JavaScript API is ESM; the CLI reads a local JSON file of at most 256 KiB and does not upload or modify it.
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import { assertContribution, contributionErrors, getContributionSchema } from 'mailschema';
|
|
17
|
+
|
|
18
|
+
const errors = contributionErrors(candidate);
|
|
19
|
+
if (errors.length) console.error(errors);
|
|
20
|
+
else assertContribution(candidate);
|
|
21
|
+
|
|
22
|
+
const schema = getContributionSchema(); // Independent copy, JSON Schema Draft 2020-12.
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Use `assertTypeRecord(value)` or `mailschema check record.json --record` for expanded Registry records. `getRecordSchema()` returns the equivalent schema. The raw contribution schema is also exported as `mailschema/contribution.schema.json`.
|
|
26
|
+
|
|
27
|
+
For a structurally valid contribution, `referenceErrors(contribution, catalog)` checks references against a supplied catalogue: current amendment bases, existing type names, exact implementation records, versions, profiles and operations. The catalogue contains `types` and `snapshots` arrays of `{ record, digest }` entries. It is never fetched automatically.
|
|
28
|
+
|
|
29
|
+
`Contribution`, `TypeRecord`, `TypeDefinition`, `Implementation`, `Party` and `CatalogView` are exported TypeScript types. Successful validation does not verify a contributor's identity, establish product compatibility or accept a submission. Full repository review also checks competing amendments and contribution identifiers.
|
|
30
|
+
|
|
31
|
+
Package version `0.1.0` is independent of any specification version. MIT licensed.
|
package/cli.mjs
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { open } from 'node:fs/promises';
|
|
3
|
+
import { parseArgs } from 'node:util';
|
|
4
|
+
import {
|
|
5
|
+
assertContribution,
|
|
6
|
+
assertTypeRecord,
|
|
7
|
+
getContributionSchema,
|
|
8
|
+
getRecordSchema,
|
|
9
|
+
} from './dist/index.js';
|
|
10
|
+
|
|
11
|
+
const help = `MailSchema contribution tools
|
|
12
|
+
|
|
13
|
+
mailschema check <file.json> [--record]
|
|
14
|
+
mailschema schema [--record]
|
|
15
|
+
|
|
16
|
+
Checks JSON structure and required fields locally. Registry references and
|
|
17
|
+
editorial acceptance are separate checks. No files are uploaded or changed.`;
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const { values, positionals } = parseArgs({
|
|
21
|
+
allowPositionals: true,
|
|
22
|
+
options: { record: { type: 'boolean' }, help: { type: 'boolean', short: 'h' } },
|
|
23
|
+
});
|
|
24
|
+
const [command, path, ...extra] = positionals;
|
|
25
|
+
if (values.help || !command) console.log(help);
|
|
26
|
+
else if (command === 'schema' && !path && !extra.length)
|
|
27
|
+
console.log(
|
|
28
|
+
JSON.stringify(values.record ? getRecordSchema() : getContributionSchema(), null, 2),
|
|
29
|
+
);
|
|
30
|
+
else if (command === 'check' && path && !extra.length) {
|
|
31
|
+
const file = await open(path, 'r');
|
|
32
|
+
let value;
|
|
33
|
+
try {
|
|
34
|
+
const info = await file.stat();
|
|
35
|
+
if (!info.isFile() || info.size > 256 * 1024)
|
|
36
|
+
throw new Error('Expected a JSON file no larger than 256 KiB.');
|
|
37
|
+
const bytes = Buffer.alloc(256 * 1024 + 1);
|
|
38
|
+
const { bytesRead } = await file.read(bytes, 0, bytes.length, 0);
|
|
39
|
+
if (bytesRead > 256 * 1024) throw new Error('JSON file exceeds 256 KiB.');
|
|
40
|
+
value = JSON.parse(bytes.subarray(0, bytesRead).toString('utf8'));
|
|
41
|
+
} finally {
|
|
42
|
+
await file.close();
|
|
43
|
+
}
|
|
44
|
+
if (values.record) assertTypeRecord(value);
|
|
45
|
+
else assertContribution(value);
|
|
46
|
+
console.log(
|
|
47
|
+
'Valid MailSchema ' + (values.record ? 'type record' : 'contribution') + ' structure.',
|
|
48
|
+
);
|
|
49
|
+
} else throw new Error(help);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
}
|
|
@@ -0,0 +1,860 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "MailSchema contribution format",
|
|
4
|
+
"description": "Repository contribution data. This is not a MAP message or wire representation.",
|
|
5
|
+
"$defs": {
|
|
6
|
+
"definition": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"slug": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
|
|
13
|
+
"maxLength": 100
|
|
14
|
+
},
|
|
15
|
+
"name": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"minLength": 1,
|
|
18
|
+
"maxLength": 240,
|
|
19
|
+
"pattern": "\\S"
|
|
20
|
+
},
|
|
21
|
+
"summary": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"minLength": 1,
|
|
24
|
+
"maxLength": 500,
|
|
25
|
+
"pattern": "\\S"
|
|
26
|
+
},
|
|
27
|
+
"category": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"minLength": 1,
|
|
30
|
+
"maxLength": 240,
|
|
31
|
+
"pattern": "\\S"
|
|
32
|
+
},
|
|
33
|
+
"status": {
|
|
34
|
+
"enum": ["Draft", "Proposal", "Reuse assessment"]
|
|
35
|
+
},
|
|
36
|
+
"version": {
|
|
37
|
+
"anyOf": [
|
|
38
|
+
{
|
|
39
|
+
"type": "string",
|
|
40
|
+
"pattern": "^[0-9]+\\.[0-9]+(?:\\.[0-9]+)?(?:-[a-zA-Z0-9.-]+)?$",
|
|
41
|
+
"maxLength": 80
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"type": "null"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"profile": {
|
|
49
|
+
"type": "string",
|
|
50
|
+
"minLength": 1,
|
|
51
|
+
"maxLength": 240,
|
|
52
|
+
"pattern": "\\S"
|
|
53
|
+
},
|
|
54
|
+
"overview": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"minLength": 1,
|
|
57
|
+
"maxLength": 12000,
|
|
58
|
+
"pattern": "\\S"
|
|
59
|
+
},
|
|
60
|
+
"target": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"minLength": 1,
|
|
63
|
+
"maxLength": 12000,
|
|
64
|
+
"pattern": "\\S"
|
|
65
|
+
},
|
|
66
|
+
"operations": {
|
|
67
|
+
"type": "array",
|
|
68
|
+
"items": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"additionalProperties": false,
|
|
71
|
+
"properties": {
|
|
72
|
+
"name": {
|
|
73
|
+
"type": "string",
|
|
74
|
+
"minLength": 1,
|
|
75
|
+
"maxLength": 240,
|
|
76
|
+
"pattern": "\\S"
|
|
77
|
+
},
|
|
78
|
+
"description": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"minLength": 1,
|
|
81
|
+
"maxLength": 12000,
|
|
82
|
+
"pattern": "\\S"
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"required": ["name", "description"]
|
|
86
|
+
},
|
|
87
|
+
"minItems": 1,
|
|
88
|
+
"maxItems": 100
|
|
89
|
+
},
|
|
90
|
+
"inputs": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"minLength": 1,
|
|
93
|
+
"maxLength": 12000,
|
|
94
|
+
"pattern": "\\S"
|
|
95
|
+
},
|
|
96
|
+
"results": {
|
|
97
|
+
"type": "string",
|
|
98
|
+
"minLength": 1,
|
|
99
|
+
"maxLength": 12000,
|
|
100
|
+
"pattern": "\\S"
|
|
101
|
+
},
|
|
102
|
+
"permissions": {
|
|
103
|
+
"type": "string",
|
|
104
|
+
"minLength": 1,
|
|
105
|
+
"maxLength": 12000,
|
|
106
|
+
"pattern": "\\S"
|
|
107
|
+
},
|
|
108
|
+
"humanRoute": {
|
|
109
|
+
"type": "string",
|
|
110
|
+
"minLength": 1,
|
|
111
|
+
"maxLength": 12000,
|
|
112
|
+
"pattern": "\\S"
|
|
113
|
+
},
|
|
114
|
+
"example": {
|
|
115
|
+
"type": "object",
|
|
116
|
+
"additionalProperties": false,
|
|
117
|
+
"properties": {
|
|
118
|
+
"title": {
|
|
119
|
+
"type": "string",
|
|
120
|
+
"minLength": 1,
|
|
121
|
+
"maxLength": 240,
|
|
122
|
+
"pattern": "\\S"
|
|
123
|
+
},
|
|
124
|
+
"steps": {
|
|
125
|
+
"type": "array",
|
|
126
|
+
"items": {
|
|
127
|
+
"type": "string",
|
|
128
|
+
"minLength": 1,
|
|
129
|
+
"maxLength": 12000,
|
|
130
|
+
"pattern": "\\S"
|
|
131
|
+
},
|
|
132
|
+
"minItems": 1,
|
|
133
|
+
"maxItems": 100
|
|
134
|
+
},
|
|
135
|
+
"exception": {
|
|
136
|
+
"type": "string",
|
|
137
|
+
"minLength": 1,
|
|
138
|
+
"maxLength": 12000,
|
|
139
|
+
"pattern": "\\S"
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
"required": ["title", "steps", "exception"]
|
|
143
|
+
},
|
|
144
|
+
"references": {
|
|
145
|
+
"type": "array",
|
|
146
|
+
"items": {
|
|
147
|
+
"type": "object",
|
|
148
|
+
"additionalProperties": false,
|
|
149
|
+
"properties": {
|
|
150
|
+
"title": {
|
|
151
|
+
"type": "string",
|
|
152
|
+
"minLength": 1,
|
|
153
|
+
"maxLength": 240,
|
|
154
|
+
"pattern": "\\S"
|
|
155
|
+
},
|
|
156
|
+
"href": {
|
|
157
|
+
"type": "string",
|
|
158
|
+
"format": "uri",
|
|
159
|
+
"pattern": "^https://",
|
|
160
|
+
"maxLength": 2048
|
|
161
|
+
},
|
|
162
|
+
"description": {
|
|
163
|
+
"type": "string",
|
|
164
|
+
"minLength": 1,
|
|
165
|
+
"maxLength": 12000,
|
|
166
|
+
"pattern": "\\S"
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
"required": ["title", "href", "description"]
|
|
170
|
+
},
|
|
171
|
+
"minItems": 1,
|
|
172
|
+
"maxItems": 100
|
|
173
|
+
},
|
|
174
|
+
"openQuestions": {
|
|
175
|
+
"type": "array",
|
|
176
|
+
"items": {
|
|
177
|
+
"type": "string",
|
|
178
|
+
"minLength": 1,
|
|
179
|
+
"maxLength": 12000,
|
|
180
|
+
"pattern": "\\S"
|
|
181
|
+
},
|
|
182
|
+
"minItems": 0,
|
|
183
|
+
"maxItems": 100
|
|
184
|
+
},
|
|
185
|
+
"maintainers": {
|
|
186
|
+
"type": "array",
|
|
187
|
+
"items": {
|
|
188
|
+
"type": "object",
|
|
189
|
+
"additionalProperties": false,
|
|
190
|
+
"properties": {
|
|
191
|
+
"name": {
|
|
192
|
+
"type": "string",
|
|
193
|
+
"minLength": 1,
|
|
194
|
+
"maxLength": 240,
|
|
195
|
+
"pattern": "\\S"
|
|
196
|
+
},
|
|
197
|
+
"url": {
|
|
198
|
+
"type": "string",
|
|
199
|
+
"format": "uri",
|
|
200
|
+
"pattern": "^https://",
|
|
201
|
+
"maxLength": 2048
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
"required": ["name"]
|
|
205
|
+
},
|
|
206
|
+
"minItems": 1,
|
|
207
|
+
"maxItems": 100
|
|
208
|
+
},
|
|
209
|
+
"definition": {
|
|
210
|
+
"type": "object",
|
|
211
|
+
"additionalProperties": false,
|
|
212
|
+
"properties": {
|
|
213
|
+
"label": {
|
|
214
|
+
"type": "string",
|
|
215
|
+
"minLength": 1,
|
|
216
|
+
"maxLength": 240,
|
|
217
|
+
"pattern": "\\S"
|
|
218
|
+
},
|
|
219
|
+
"href": {
|
|
220
|
+
"type": "string",
|
|
221
|
+
"pattern": "^/specification/(?:[a-z0-9-]+/)*$",
|
|
222
|
+
"maxLength": 300
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
"required": ["label", "href"]
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
"required": [
|
|
229
|
+
"slug",
|
|
230
|
+
"name",
|
|
231
|
+
"summary",
|
|
232
|
+
"category",
|
|
233
|
+
"status",
|
|
234
|
+
"version",
|
|
235
|
+
"profile",
|
|
236
|
+
"overview",
|
|
237
|
+
"target",
|
|
238
|
+
"operations",
|
|
239
|
+
"inputs",
|
|
240
|
+
"results",
|
|
241
|
+
"permissions",
|
|
242
|
+
"humanRoute",
|
|
243
|
+
"example",
|
|
244
|
+
"references",
|
|
245
|
+
"openQuestions",
|
|
246
|
+
"maintainers"
|
|
247
|
+
],
|
|
248
|
+
"allOf": [
|
|
249
|
+
{
|
|
250
|
+
"if": {
|
|
251
|
+
"properties": {
|
|
252
|
+
"status": {
|
|
253
|
+
"const": "Draft"
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
"required": ["status"]
|
|
257
|
+
},
|
|
258
|
+
"then": {
|
|
259
|
+
"required": ["definition"],
|
|
260
|
+
"properties": {
|
|
261
|
+
"version": {
|
|
262
|
+
"type": "string"
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
]
|
|
268
|
+
},
|
|
269
|
+
"record": {
|
|
270
|
+
"type": "object",
|
|
271
|
+
"additionalProperties": false,
|
|
272
|
+
"properties": {
|
|
273
|
+
"slug": {
|
|
274
|
+
"type": "string",
|
|
275
|
+
"pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
|
|
276
|
+
"maxLength": 100
|
|
277
|
+
},
|
|
278
|
+
"name": {
|
|
279
|
+
"type": "string",
|
|
280
|
+
"minLength": 1,
|
|
281
|
+
"maxLength": 240,
|
|
282
|
+
"pattern": "\\S"
|
|
283
|
+
},
|
|
284
|
+
"summary": {
|
|
285
|
+
"type": "string",
|
|
286
|
+
"minLength": 1,
|
|
287
|
+
"maxLength": 500,
|
|
288
|
+
"pattern": "\\S"
|
|
289
|
+
},
|
|
290
|
+
"category": {
|
|
291
|
+
"type": "string",
|
|
292
|
+
"minLength": 1,
|
|
293
|
+
"maxLength": 240,
|
|
294
|
+
"pattern": "\\S"
|
|
295
|
+
},
|
|
296
|
+
"status": {
|
|
297
|
+
"enum": ["Draft", "Proposal", "Reuse assessment"]
|
|
298
|
+
},
|
|
299
|
+
"version": {
|
|
300
|
+
"anyOf": [
|
|
301
|
+
{
|
|
302
|
+
"type": "string",
|
|
303
|
+
"pattern": "^[0-9]+\\.[0-9]+(?:\\.[0-9]+)?(?:-[a-zA-Z0-9.-]+)?$",
|
|
304
|
+
"maxLength": 80
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
"type": "null"
|
|
308
|
+
}
|
|
309
|
+
]
|
|
310
|
+
},
|
|
311
|
+
"profile": {
|
|
312
|
+
"type": "string",
|
|
313
|
+
"minLength": 1,
|
|
314
|
+
"maxLength": 240,
|
|
315
|
+
"pattern": "\\S"
|
|
316
|
+
},
|
|
317
|
+
"overview": {
|
|
318
|
+
"type": "string",
|
|
319
|
+
"minLength": 1,
|
|
320
|
+
"maxLength": 12000,
|
|
321
|
+
"pattern": "\\S"
|
|
322
|
+
},
|
|
323
|
+
"target": {
|
|
324
|
+
"type": "string",
|
|
325
|
+
"minLength": 1,
|
|
326
|
+
"maxLength": 12000,
|
|
327
|
+
"pattern": "\\S"
|
|
328
|
+
},
|
|
329
|
+
"operations": {
|
|
330
|
+
"type": "array",
|
|
331
|
+
"items": {
|
|
332
|
+
"type": "object",
|
|
333
|
+
"additionalProperties": false,
|
|
334
|
+
"properties": {
|
|
335
|
+
"name": {
|
|
336
|
+
"type": "string",
|
|
337
|
+
"minLength": 1,
|
|
338
|
+
"maxLength": 240,
|
|
339
|
+
"pattern": "\\S"
|
|
340
|
+
},
|
|
341
|
+
"description": {
|
|
342
|
+
"type": "string",
|
|
343
|
+
"minLength": 1,
|
|
344
|
+
"maxLength": 12000,
|
|
345
|
+
"pattern": "\\S"
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
"required": ["name", "description"]
|
|
349
|
+
},
|
|
350
|
+
"minItems": 1,
|
|
351
|
+
"maxItems": 100
|
|
352
|
+
},
|
|
353
|
+
"inputs": {
|
|
354
|
+
"type": "string",
|
|
355
|
+
"minLength": 1,
|
|
356
|
+
"maxLength": 12000,
|
|
357
|
+
"pattern": "\\S"
|
|
358
|
+
},
|
|
359
|
+
"results": {
|
|
360
|
+
"type": "string",
|
|
361
|
+
"minLength": 1,
|
|
362
|
+
"maxLength": 12000,
|
|
363
|
+
"pattern": "\\S"
|
|
364
|
+
},
|
|
365
|
+
"permissions": {
|
|
366
|
+
"type": "string",
|
|
367
|
+
"minLength": 1,
|
|
368
|
+
"maxLength": 12000,
|
|
369
|
+
"pattern": "\\S"
|
|
370
|
+
},
|
|
371
|
+
"humanRoute": {
|
|
372
|
+
"type": "string",
|
|
373
|
+
"minLength": 1,
|
|
374
|
+
"maxLength": 12000,
|
|
375
|
+
"pattern": "\\S"
|
|
376
|
+
},
|
|
377
|
+
"example": {
|
|
378
|
+
"type": "object",
|
|
379
|
+
"additionalProperties": false,
|
|
380
|
+
"properties": {
|
|
381
|
+
"title": {
|
|
382
|
+
"type": "string",
|
|
383
|
+
"minLength": 1,
|
|
384
|
+
"maxLength": 240,
|
|
385
|
+
"pattern": "\\S"
|
|
386
|
+
},
|
|
387
|
+
"steps": {
|
|
388
|
+
"type": "array",
|
|
389
|
+
"items": {
|
|
390
|
+
"type": "string",
|
|
391
|
+
"minLength": 1,
|
|
392
|
+
"maxLength": 12000,
|
|
393
|
+
"pattern": "\\S"
|
|
394
|
+
},
|
|
395
|
+
"minItems": 1,
|
|
396
|
+
"maxItems": 100
|
|
397
|
+
},
|
|
398
|
+
"exception": {
|
|
399
|
+
"type": "string",
|
|
400
|
+
"minLength": 1,
|
|
401
|
+
"maxLength": 12000,
|
|
402
|
+
"pattern": "\\S"
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
"required": ["title", "steps", "exception"]
|
|
406
|
+
},
|
|
407
|
+
"references": {
|
|
408
|
+
"type": "array",
|
|
409
|
+
"items": {
|
|
410
|
+
"type": "object",
|
|
411
|
+
"additionalProperties": false,
|
|
412
|
+
"properties": {
|
|
413
|
+
"title": {
|
|
414
|
+
"type": "string",
|
|
415
|
+
"minLength": 1,
|
|
416
|
+
"maxLength": 240,
|
|
417
|
+
"pattern": "\\S"
|
|
418
|
+
},
|
|
419
|
+
"href": {
|
|
420
|
+
"type": "string",
|
|
421
|
+
"format": "uri",
|
|
422
|
+
"pattern": "^https://",
|
|
423
|
+
"maxLength": 2048
|
|
424
|
+
},
|
|
425
|
+
"description": {
|
|
426
|
+
"type": "string",
|
|
427
|
+
"minLength": 1,
|
|
428
|
+
"maxLength": 12000,
|
|
429
|
+
"pattern": "\\S"
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
"required": ["title", "href", "description"]
|
|
433
|
+
},
|
|
434
|
+
"minItems": 1,
|
|
435
|
+
"maxItems": 100
|
|
436
|
+
},
|
|
437
|
+
"openQuestions": {
|
|
438
|
+
"type": "array",
|
|
439
|
+
"items": {
|
|
440
|
+
"type": "string",
|
|
441
|
+
"minLength": 1,
|
|
442
|
+
"maxLength": 12000,
|
|
443
|
+
"pattern": "\\S"
|
|
444
|
+
},
|
|
445
|
+
"minItems": 0,
|
|
446
|
+
"maxItems": 100
|
|
447
|
+
},
|
|
448
|
+
"maintainers": {
|
|
449
|
+
"type": "array",
|
|
450
|
+
"items": {
|
|
451
|
+
"type": "object",
|
|
452
|
+
"additionalProperties": false,
|
|
453
|
+
"properties": {
|
|
454
|
+
"name": {
|
|
455
|
+
"type": "string",
|
|
456
|
+
"minLength": 1,
|
|
457
|
+
"maxLength": 240,
|
|
458
|
+
"pattern": "\\S"
|
|
459
|
+
},
|
|
460
|
+
"url": {
|
|
461
|
+
"type": "string",
|
|
462
|
+
"format": "uri",
|
|
463
|
+
"pattern": "^https://",
|
|
464
|
+
"maxLength": 2048
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
"required": ["name"]
|
|
468
|
+
},
|
|
469
|
+
"minItems": 1,
|
|
470
|
+
"maxItems": 100
|
|
471
|
+
},
|
|
472
|
+
"definition": {
|
|
473
|
+
"type": "object",
|
|
474
|
+
"additionalProperties": false,
|
|
475
|
+
"properties": {
|
|
476
|
+
"label": {
|
|
477
|
+
"type": "string",
|
|
478
|
+
"minLength": 1,
|
|
479
|
+
"maxLength": 240,
|
|
480
|
+
"pattern": "\\S"
|
|
481
|
+
},
|
|
482
|
+
"href": {
|
|
483
|
+
"type": "string",
|
|
484
|
+
"pattern": "^/specification/(?:[a-z0-9-]+/)*$",
|
|
485
|
+
"maxLength": 300
|
|
486
|
+
}
|
|
487
|
+
},
|
|
488
|
+
"required": ["label", "href"]
|
|
489
|
+
},
|
|
490
|
+
"origin": {
|
|
491
|
+
"type": "string",
|
|
492
|
+
"minLength": 1,
|
|
493
|
+
"maxLength": 240,
|
|
494
|
+
"pattern": "\\S"
|
|
495
|
+
},
|
|
496
|
+
"contributors": {
|
|
497
|
+
"type": "array",
|
|
498
|
+
"items": {
|
|
499
|
+
"type": "object",
|
|
500
|
+
"additionalProperties": false,
|
|
501
|
+
"properties": {
|
|
502
|
+
"name": {
|
|
503
|
+
"type": "string",
|
|
504
|
+
"minLength": 1,
|
|
505
|
+
"maxLength": 240,
|
|
506
|
+
"pattern": "\\S"
|
|
507
|
+
},
|
|
508
|
+
"url": {
|
|
509
|
+
"type": "string",
|
|
510
|
+
"format": "uri",
|
|
511
|
+
"pattern": "^https://",
|
|
512
|
+
"maxLength": 2048
|
|
513
|
+
}
|
|
514
|
+
},
|
|
515
|
+
"required": ["name"]
|
|
516
|
+
},
|
|
517
|
+
"minItems": 1,
|
|
518
|
+
"maxItems": 100
|
|
519
|
+
},
|
|
520
|
+
"history": {
|
|
521
|
+
"type": "array",
|
|
522
|
+
"items": {
|
|
523
|
+
"type": "object",
|
|
524
|
+
"additionalProperties": false,
|
|
525
|
+
"properties": {
|
|
526
|
+
"label": {
|
|
527
|
+
"type": "string",
|
|
528
|
+
"minLength": 1,
|
|
529
|
+
"maxLength": 240,
|
|
530
|
+
"pattern": "\\S"
|
|
531
|
+
},
|
|
532
|
+
"description": {
|
|
533
|
+
"type": "string",
|
|
534
|
+
"minLength": 1,
|
|
535
|
+
"maxLength": 12000,
|
|
536
|
+
"pattern": "\\S"
|
|
537
|
+
},
|
|
538
|
+
"contributionId": {
|
|
539
|
+
"type": "string",
|
|
540
|
+
"pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
|
|
541
|
+
"maxLength": 100
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
"required": ["label", "description"]
|
|
545
|
+
},
|
|
546
|
+
"minItems": 1,
|
|
547
|
+
"maxItems": 100
|
|
548
|
+
}
|
|
549
|
+
},
|
|
550
|
+
"required": [
|
|
551
|
+
"slug",
|
|
552
|
+
"name",
|
|
553
|
+
"summary",
|
|
554
|
+
"category",
|
|
555
|
+
"status",
|
|
556
|
+
"version",
|
|
557
|
+
"profile",
|
|
558
|
+
"overview",
|
|
559
|
+
"target",
|
|
560
|
+
"operations",
|
|
561
|
+
"inputs",
|
|
562
|
+
"results",
|
|
563
|
+
"permissions",
|
|
564
|
+
"humanRoute",
|
|
565
|
+
"example",
|
|
566
|
+
"references",
|
|
567
|
+
"openQuestions",
|
|
568
|
+
"maintainers",
|
|
569
|
+
"origin",
|
|
570
|
+
"contributors",
|
|
571
|
+
"history"
|
|
572
|
+
],
|
|
573
|
+
"allOf": [
|
|
574
|
+
{
|
|
575
|
+
"if": {
|
|
576
|
+
"properties": {
|
|
577
|
+
"status": {
|
|
578
|
+
"const": "Draft"
|
|
579
|
+
}
|
|
580
|
+
},
|
|
581
|
+
"required": ["status"]
|
|
582
|
+
},
|
|
583
|
+
"then": {
|
|
584
|
+
"required": ["definition"],
|
|
585
|
+
"properties": {
|
|
586
|
+
"version": {
|
|
587
|
+
"type": "string"
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
]
|
|
593
|
+
}
|
|
594
|
+
},
|
|
595
|
+
"oneOf": [
|
|
596
|
+
{
|
|
597
|
+
"type": "object",
|
|
598
|
+
"additionalProperties": false,
|
|
599
|
+
"properties": {
|
|
600
|
+
"format": {
|
|
601
|
+
"const": "mailschema-contribution/1"
|
|
602
|
+
},
|
|
603
|
+
"id": {
|
|
604
|
+
"type": "string",
|
|
605
|
+
"pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
|
|
606
|
+
"maxLength": 100
|
|
607
|
+
},
|
|
608
|
+
"contributor": {
|
|
609
|
+
"type": "object",
|
|
610
|
+
"additionalProperties": false,
|
|
611
|
+
"properties": {
|
|
612
|
+
"name": {
|
|
613
|
+
"type": "string",
|
|
614
|
+
"minLength": 1,
|
|
615
|
+
"maxLength": 240,
|
|
616
|
+
"pattern": "\\S"
|
|
617
|
+
},
|
|
618
|
+
"url": {
|
|
619
|
+
"type": "string",
|
|
620
|
+
"format": "uri",
|
|
621
|
+
"pattern": "^https://",
|
|
622
|
+
"maxLength": 2048
|
|
623
|
+
}
|
|
624
|
+
},
|
|
625
|
+
"required": ["name"]
|
|
626
|
+
},
|
|
627
|
+
"source": {
|
|
628
|
+
"type": "string",
|
|
629
|
+
"format": "uri",
|
|
630
|
+
"pattern": "^https://",
|
|
631
|
+
"maxLength": 2048
|
|
632
|
+
},
|
|
633
|
+
"summary": {
|
|
634
|
+
"type": "string",
|
|
635
|
+
"minLength": 1,
|
|
636
|
+
"maxLength": 12000,
|
|
637
|
+
"pattern": "\\S"
|
|
638
|
+
},
|
|
639
|
+
"kind": {
|
|
640
|
+
"const": "new-type"
|
|
641
|
+
},
|
|
642
|
+
"record": {
|
|
643
|
+
"$ref": "#/$defs/definition"
|
|
644
|
+
}
|
|
645
|
+
},
|
|
646
|
+
"required": ["format", "id", "contributor", "summary", "kind", "record"]
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
"type": "object",
|
|
650
|
+
"additionalProperties": false,
|
|
651
|
+
"properties": {
|
|
652
|
+
"format": {
|
|
653
|
+
"const": "mailschema-contribution/1"
|
|
654
|
+
},
|
|
655
|
+
"id": {
|
|
656
|
+
"type": "string",
|
|
657
|
+
"pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
|
|
658
|
+
"maxLength": 100
|
|
659
|
+
},
|
|
660
|
+
"contributor": {
|
|
661
|
+
"type": "object",
|
|
662
|
+
"additionalProperties": false,
|
|
663
|
+
"properties": {
|
|
664
|
+
"name": {
|
|
665
|
+
"type": "string",
|
|
666
|
+
"minLength": 1,
|
|
667
|
+
"maxLength": 240,
|
|
668
|
+
"pattern": "\\S"
|
|
669
|
+
},
|
|
670
|
+
"url": {
|
|
671
|
+
"type": "string",
|
|
672
|
+
"format": "uri",
|
|
673
|
+
"pattern": "^https://",
|
|
674
|
+
"maxLength": 2048
|
|
675
|
+
}
|
|
676
|
+
},
|
|
677
|
+
"required": ["name"]
|
|
678
|
+
},
|
|
679
|
+
"source": {
|
|
680
|
+
"type": "string",
|
|
681
|
+
"format": "uri",
|
|
682
|
+
"pattern": "^https://",
|
|
683
|
+
"maxLength": 2048
|
|
684
|
+
},
|
|
685
|
+
"summary": {
|
|
686
|
+
"type": "string",
|
|
687
|
+
"minLength": 1,
|
|
688
|
+
"maxLength": 12000,
|
|
689
|
+
"pattern": "\\S"
|
|
690
|
+
},
|
|
691
|
+
"kind": {
|
|
692
|
+
"const": "amendment"
|
|
693
|
+
},
|
|
694
|
+
"baseDigest": {
|
|
695
|
+
"type": "string",
|
|
696
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
697
|
+
},
|
|
698
|
+
"record": {
|
|
699
|
+
"$ref": "#/$defs/definition"
|
|
700
|
+
}
|
|
701
|
+
},
|
|
702
|
+
"required": ["format", "id", "contributor", "summary", "kind", "baseDigest", "record"]
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
"type": "object",
|
|
706
|
+
"additionalProperties": false,
|
|
707
|
+
"properties": {
|
|
708
|
+
"format": {
|
|
709
|
+
"const": "mailschema-contribution/1"
|
|
710
|
+
},
|
|
711
|
+
"id": {
|
|
712
|
+
"type": "string",
|
|
713
|
+
"pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
|
|
714
|
+
"maxLength": 100
|
|
715
|
+
},
|
|
716
|
+
"contributor": {
|
|
717
|
+
"type": "object",
|
|
718
|
+
"additionalProperties": false,
|
|
719
|
+
"properties": {
|
|
720
|
+
"name": {
|
|
721
|
+
"type": "string",
|
|
722
|
+
"minLength": 1,
|
|
723
|
+
"maxLength": 240,
|
|
724
|
+
"pattern": "\\S"
|
|
725
|
+
},
|
|
726
|
+
"url": {
|
|
727
|
+
"type": "string",
|
|
728
|
+
"format": "uri",
|
|
729
|
+
"pattern": "^https://",
|
|
730
|
+
"maxLength": 2048
|
|
731
|
+
}
|
|
732
|
+
},
|
|
733
|
+
"required": ["name"]
|
|
734
|
+
},
|
|
735
|
+
"source": {
|
|
736
|
+
"type": "string",
|
|
737
|
+
"format": "uri",
|
|
738
|
+
"pattern": "^https://",
|
|
739
|
+
"maxLength": 2048
|
|
740
|
+
},
|
|
741
|
+
"summary": {
|
|
742
|
+
"type": "string",
|
|
743
|
+
"minLength": 1,
|
|
744
|
+
"maxLength": 12000,
|
|
745
|
+
"pattern": "\\S"
|
|
746
|
+
},
|
|
747
|
+
"kind": {
|
|
748
|
+
"const": "implementation"
|
|
749
|
+
},
|
|
750
|
+
"type": {
|
|
751
|
+
"type": "string",
|
|
752
|
+
"pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
|
|
753
|
+
"maxLength": 100
|
|
754
|
+
},
|
|
755
|
+
"typeVersion": {
|
|
756
|
+
"type": "string",
|
|
757
|
+
"pattern": "^[0-9]+\\.[0-9]+(?:\\.[0-9]+)?(?:-[a-zA-Z0-9.-]+)?$",
|
|
758
|
+
"maxLength": 80
|
|
759
|
+
},
|
|
760
|
+
"typeDigest": {
|
|
761
|
+
"type": "string",
|
|
762
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
763
|
+
},
|
|
764
|
+
"profile": {
|
|
765
|
+
"type": "string",
|
|
766
|
+
"minLength": 1,
|
|
767
|
+
"maxLength": 240,
|
|
768
|
+
"pattern": "\\S"
|
|
769
|
+
},
|
|
770
|
+
"product": {
|
|
771
|
+
"type": "object",
|
|
772
|
+
"additionalProperties": false,
|
|
773
|
+
"properties": {
|
|
774
|
+
"name": {
|
|
775
|
+
"type": "string",
|
|
776
|
+
"minLength": 1,
|
|
777
|
+
"maxLength": 240,
|
|
778
|
+
"pattern": "\\S"
|
|
779
|
+
},
|
|
780
|
+
"url": {
|
|
781
|
+
"type": "string",
|
|
782
|
+
"format": "uri",
|
|
783
|
+
"pattern": "^https://",
|
|
784
|
+
"maxLength": 2048
|
|
785
|
+
}
|
|
786
|
+
},
|
|
787
|
+
"required": ["name"]
|
|
788
|
+
},
|
|
789
|
+
"operations": {
|
|
790
|
+
"type": "array",
|
|
791
|
+
"items": {
|
|
792
|
+
"type": "string",
|
|
793
|
+
"minLength": 1,
|
|
794
|
+
"maxLength": 240,
|
|
795
|
+
"pattern": "\\S"
|
|
796
|
+
},
|
|
797
|
+
"minItems": 1,
|
|
798
|
+
"maxItems": 100
|
|
799
|
+
},
|
|
800
|
+
"evidence": {
|
|
801
|
+
"type": "object",
|
|
802
|
+
"additionalProperties": false,
|
|
803
|
+
"properties": {
|
|
804
|
+
"kind": {
|
|
805
|
+
"enum": ["declaration", "test-report"]
|
|
806
|
+
},
|
|
807
|
+
"url": {
|
|
808
|
+
"type": "string",
|
|
809
|
+
"format": "uri",
|
|
810
|
+
"pattern": "^https://",
|
|
811
|
+
"maxLength": 2048
|
|
812
|
+
},
|
|
813
|
+
"reproduction": {
|
|
814
|
+
"type": "string",
|
|
815
|
+
"minLength": 1,
|
|
816
|
+
"maxLength": 12000,
|
|
817
|
+
"pattern": "\\S"
|
|
818
|
+
},
|
|
819
|
+
"results": {
|
|
820
|
+
"type": "string",
|
|
821
|
+
"minLength": 1,
|
|
822
|
+
"maxLength": 12000,
|
|
823
|
+
"pattern": "\\S"
|
|
824
|
+
}
|
|
825
|
+
},
|
|
826
|
+
"required": ["kind"],
|
|
827
|
+
"allOf": [
|
|
828
|
+
{
|
|
829
|
+
"if": {
|
|
830
|
+
"properties": {
|
|
831
|
+
"kind": {
|
|
832
|
+
"const": "test-report"
|
|
833
|
+
}
|
|
834
|
+
},
|
|
835
|
+
"required": ["kind"]
|
|
836
|
+
},
|
|
837
|
+
"then": {
|
|
838
|
+
"required": ["url", "reproduction", "results"]
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
]
|
|
842
|
+
}
|
|
843
|
+
},
|
|
844
|
+
"required": [
|
|
845
|
+
"format",
|
|
846
|
+
"id",
|
|
847
|
+
"contributor",
|
|
848
|
+
"summary",
|
|
849
|
+
"kind",
|
|
850
|
+
"type",
|
|
851
|
+
"typeVersion",
|
|
852
|
+
"typeDigest",
|
|
853
|
+
"profile",
|
|
854
|
+
"product",
|
|
855
|
+
"operations",
|
|
856
|
+
"evidence"
|
|
857
|
+
]
|
|
858
|
+
}
|
|
859
|
+
]
|
|
860
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { contributionErrors, assertContribution, assertTypeRecord, referenceErrors, } from './validation.js';
|
|
2
|
+
export type { CatalogView } from './validation.js';
|
|
3
|
+
export { typeStages } from './model.js';
|
|
4
|
+
export type { Party, TypeDefinition, TypeRecord, Contribution, Implementation } from './model.js';
|
|
5
|
+
/** Return an independent copy of the contribution JSON Schema (Draft 2020-12). */
|
|
6
|
+
export declare function getContributionSchema(): Record<string, unknown>;
|
|
7
|
+
/** Return the schema for an expanded Registry record, including attribution and history. */
|
|
8
|
+
export declare function getRecordSchema(): Record<string, unknown>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Tools for preparing MailSchema Registry contributions. */
|
|
2
|
+
import schema from './contribution.schema.json' with { type: 'json' };
|
|
3
|
+
export { contributionErrors, assertContribution, assertTypeRecord, referenceErrors, } from './validation.js';
|
|
4
|
+
export { typeStages } from './model.js';
|
|
5
|
+
/** Return an independent copy of the contribution JSON Schema (Draft 2020-12). */
|
|
6
|
+
export function getContributionSchema() {
|
|
7
|
+
return structuredClone(schema);
|
|
8
|
+
}
|
|
9
|
+
/** Return the schema for an expanded Registry record, including attribution and history. */
|
|
10
|
+
export function getRecordSchema() {
|
|
11
|
+
return { $schema: schema.$schema, $defs: structuredClone(schema.$defs), $ref: '#/$defs/record' };
|
|
12
|
+
}
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
export declare const typeStages: readonly [{
|
|
2
|
+
readonly status: "Draft";
|
|
3
|
+
readonly singular: "draft";
|
|
4
|
+
readonly plural: "drafts";
|
|
5
|
+
}, {
|
|
6
|
+
readonly status: "Proposal";
|
|
7
|
+
readonly singular: "proposal";
|
|
8
|
+
readonly plural: "proposals";
|
|
9
|
+
}, {
|
|
10
|
+
readonly status: "Reuse assessment";
|
|
11
|
+
readonly singular: "reuse assessment";
|
|
12
|
+
readonly plural: "reuse assessments";
|
|
13
|
+
}];
|
|
14
|
+
export interface Party {
|
|
15
|
+
name: string;
|
|
16
|
+
url?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface TypeDefinition {
|
|
19
|
+
slug: string;
|
|
20
|
+
name: string;
|
|
21
|
+
summary: string;
|
|
22
|
+
category: string;
|
|
23
|
+
status: (typeof typeStages)[number]['status'];
|
|
24
|
+
version: string | null;
|
|
25
|
+
profile: string;
|
|
26
|
+
overview: string;
|
|
27
|
+
target: string;
|
|
28
|
+
operations: {
|
|
29
|
+
name: string;
|
|
30
|
+
description: string;
|
|
31
|
+
}[];
|
|
32
|
+
inputs: string;
|
|
33
|
+
results: string;
|
|
34
|
+
permissions: string;
|
|
35
|
+
humanRoute: string;
|
|
36
|
+
example: {
|
|
37
|
+
title: string;
|
|
38
|
+
steps: string[];
|
|
39
|
+
exception: string;
|
|
40
|
+
};
|
|
41
|
+
references: {
|
|
42
|
+
title: string;
|
|
43
|
+
href: string;
|
|
44
|
+
description: string;
|
|
45
|
+
}[];
|
|
46
|
+
openQuestions: string[];
|
|
47
|
+
maintainers: Party[];
|
|
48
|
+
definition?: {
|
|
49
|
+
label: string;
|
|
50
|
+
href: string;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export interface TypeRecord extends TypeDefinition {
|
|
54
|
+
origin: string;
|
|
55
|
+
contributors: Party[];
|
|
56
|
+
history: {
|
|
57
|
+
label: string;
|
|
58
|
+
description: string;
|
|
59
|
+
contributionId?: string;
|
|
60
|
+
}[];
|
|
61
|
+
}
|
|
62
|
+
interface Submission {
|
|
63
|
+
format: 'mailschema-contribution/1';
|
|
64
|
+
id: string;
|
|
65
|
+
contributor: Party;
|
|
66
|
+
source?: string;
|
|
67
|
+
summary: string;
|
|
68
|
+
}
|
|
69
|
+
export type Contribution = Submission & ({
|
|
70
|
+
kind: 'new-type';
|
|
71
|
+
record: TypeDefinition;
|
|
72
|
+
} | {
|
|
73
|
+
kind: 'amendment';
|
|
74
|
+
baseDigest: string;
|
|
75
|
+
record: TypeDefinition;
|
|
76
|
+
} | {
|
|
77
|
+
kind: 'implementation';
|
|
78
|
+
type: string;
|
|
79
|
+
typeVersion: string;
|
|
80
|
+
typeDigest: string;
|
|
81
|
+
profile: string;
|
|
82
|
+
product: Party;
|
|
83
|
+
operations: string[];
|
|
84
|
+
evidence: {
|
|
85
|
+
kind: 'declaration' | 'test-report';
|
|
86
|
+
url?: string;
|
|
87
|
+
reproduction?: string;
|
|
88
|
+
results?: string;
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
export type Implementation = Extract<Contribution, {
|
|
92
|
+
kind: 'implementation';
|
|
93
|
+
}>;
|
|
94
|
+
export {};
|
package/dist/model.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Contribution, TypeRecord } from './model.js';
|
|
2
|
+
export declare function contributionErrors(value: unknown): string[];
|
|
3
|
+
export declare function assertContribution(value: unknown): asserts value is Contribution;
|
|
4
|
+
export declare function assertTypeRecord(value: unknown): asserts value is TypeRecord;
|
|
5
|
+
export interface CatalogView {
|
|
6
|
+
types: {
|
|
7
|
+
record: TypeRecord;
|
|
8
|
+
digest: string;
|
|
9
|
+
}[];
|
|
10
|
+
snapshots: {
|
|
11
|
+
record: TypeRecord;
|
|
12
|
+
digest: string;
|
|
13
|
+
}[];
|
|
14
|
+
}
|
|
15
|
+
export declare function referenceErrors(input: Contribution, catalog: CatalogView): string[];
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import Ajv2020 from 'ajv/dist/2020.js';
|
|
2
|
+
import addFormats from 'ajv-formats';
|
|
3
|
+
import schema from './contribution.schema.json' with { type: 'json' };
|
|
4
|
+
const ajv = new Ajv2020({ allErrors: true, strict: true, strictRequired: false });
|
|
5
|
+
addFormats(ajv);
|
|
6
|
+
const contribution = ajv.compile(schema);
|
|
7
|
+
const variants = new Map(schema.oneOf.map((variant) => [
|
|
8
|
+
variant.properties.kind.const,
|
|
9
|
+
ajv.compile({ $schema: schema.$schema, $defs: schema.$defs, ...variant }),
|
|
10
|
+
]));
|
|
11
|
+
const record = ajv.compile({
|
|
12
|
+
$schema: schema.$schema,
|
|
13
|
+
$defs: schema.$defs,
|
|
14
|
+
$ref: '#/$defs/record',
|
|
15
|
+
});
|
|
16
|
+
export function contributionErrors(value) {
|
|
17
|
+
const kind = value && typeof value === 'object' && 'kind' in value ? value.kind : undefined;
|
|
18
|
+
const validate = typeof kind === 'string' ? variants.get(kind) || contribution : contribution;
|
|
19
|
+
if (validate(value))
|
|
20
|
+
return [];
|
|
21
|
+
return (validate.errors || []).map((error) => `${error.instancePath || '/'} ${error.message}${error.keyword === 'required' ? `: ${error.params.missingProperty}` : ''}`);
|
|
22
|
+
}
|
|
23
|
+
export function assertContribution(value) {
|
|
24
|
+
const errors = contributionErrors(value);
|
|
25
|
+
if (errors.length)
|
|
26
|
+
throw new Error(`Invalid contribution:\n${errors.join('\n')}`);
|
|
27
|
+
}
|
|
28
|
+
export function assertTypeRecord(value) {
|
|
29
|
+
if (!record(value))
|
|
30
|
+
throw new Error(`Invalid type record: ${ajv.errorsText(record.errors, { separator: '\n' })}`);
|
|
31
|
+
}
|
|
32
|
+
export function referenceErrors(input, catalog) {
|
|
33
|
+
const errors = [];
|
|
34
|
+
if (input.kind === 'implementation') {
|
|
35
|
+
const target = catalog.snapshots.find((item) => item.digest === input.typeDigest)?.record;
|
|
36
|
+
if (!target || target.slug !== input.type || target.version !== input.typeVersion)
|
|
37
|
+
return [
|
|
38
|
+
'Unknown type version or record digest. Download the current record from the Registry.',
|
|
39
|
+
];
|
|
40
|
+
if (target.profile !== input.profile)
|
|
41
|
+
errors.push('Execution profile does not match the targeted record.');
|
|
42
|
+
const operations = new Set(target.operations.map((operation) => operation.name));
|
|
43
|
+
if (new Set(input.operations).size !== input.operations.length ||
|
|
44
|
+
input.operations.some((operation) => !operations.has(operation)))
|
|
45
|
+
errors.push('Unknown or duplicate operation in the implementation declaration.');
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const existing = catalog.types.find((item) => item.record.slug === input.record.slug);
|
|
49
|
+
if (input.kind === 'new-type' && existing)
|
|
50
|
+
errors.push('This type already exists. Submit an amendment instead.');
|
|
51
|
+
if (input.kind === 'amendment' && (!existing || existing.digest !== input.baseDigest))
|
|
52
|
+
errors.push('Stale or unknown amendment base. Download the current record and reconcile your changes.');
|
|
53
|
+
if (catalog.types.some((item) => item.record.slug !== input.record.slug &&
|
|
54
|
+
item.record.name.toLocaleLowerCase() === input.record.name.toLocaleLowerCase()))
|
|
55
|
+
errors.push('Another type already uses this name.');
|
|
56
|
+
const operations = input.record.operations.map((operation) => operation.name.toLocaleLowerCase());
|
|
57
|
+
if (new Set(operations).size !== operations.length)
|
|
58
|
+
errors.push('Operation names must be unique within a type.');
|
|
59
|
+
}
|
|
60
|
+
return errors;
|
|
61
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mailschema",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "JSON Schemas, TypeScript definitions and validation tools for MailSchema Registry contributions",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "MailSchema contributors",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./contribution.schema.json": "./dist/contribution.schema.json",
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"bin": {
|
|
21
|
+
"mailschema": "./cli.mjs"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"cli.mjs",
|
|
26
|
+
"README.md",
|
|
27
|
+
"LICENSE"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"ajv": "8.20.0",
|
|
31
|
+
"ajv-formats": "3.0.1"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"email",
|
|
35
|
+
"schema",
|
|
36
|
+
"json-schema",
|
|
37
|
+
"agents",
|
|
38
|
+
"registry"
|
|
39
|
+
],
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public",
|
|
42
|
+
"registry": "https://registry.npmjs.org/"
|
|
43
|
+
}
|
|
44
|
+
}
|