mailschema 0.1.3 → 0.1.5

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/README.md CHANGED
@@ -3,9 +3,9 @@
3
3
  [![npm](https://img.shields.io/npm/v/mailschema)](https://www.npmjs.com/package/mailschema)
4
4
  [![CI](https://github.com/mailschema/javascript/actions/workflows/test.yml/badge.svg)](https://github.com/mailschema/javascript/actions/workflows/test.yml)
5
5
 
6
- Validate Mail Action Protocol documents and MailSchema Registry contributions without making a network request.
6
+ The Mail Action Protocol 0.2 core artifacts, and local validation of MailSchema Registry contributions, with no network requests.
7
7
 
8
- [Specification](https://mailschema.org/specification/) · [Registry](https://mailschema.org/registry/) · [Tools](https://mailschema.org/tools/) · [Source](https://github.com/mailschema/javascript)
8
+ [Specification](https://mailschema.org/specification) · [Registry](https://mailschema.org/registry) · [Tools](https://mailschema.org/tools) · [Source](https://github.com/mailschema/javascript)
9
9
 
10
10
  ## Install
11
11
 
@@ -15,51 +15,40 @@ npm install mailschema
15
15
 
16
16
  Node.js 22 or newer is required. The package is ESM and includes TypeScript declarations.
17
17
 
18
- ## Validate MAP documents
18
+ ## MAP 0.2 core artifacts
19
19
 
20
20
  ```js
21
- import { assertMapDocument, assertContentReviewRequest, getMapSchema } from 'mailschema';
22
-
23
- assertMapDocument(description);
24
- assertContentReviewRequest(request);
25
- const mapSchema = getMapSchema();
21
+ import {
22
+ MAP_PROFILE,
23
+ getContractFormatSchema,
24
+ getFormsSchema,
25
+ getMapContext,
26
+ getMapSchema,
27
+ } from 'mailschema';
26
28
  ```
27
29
 
28
- The CLI performs the same checks against local JSON files:
29
-
30
- ```sh
31
- npx mailschema check description.json --map
32
- npx mailschema check request.json --content-review
33
- ```
30
+ Each is byte-identical to the file the [profile record](https://mailschema.org/profiles/map/0.2.json) binds by SHA-256, and is also exported raw as `mailschema/map-0.2.schema.json`, `mailschema/map-0.2.jsonld`, `mailschema/type-contract-0.2.schema.json` and `mailschema/forms-0.1.schema.json`. Type contracts are not bundled: a client obtains them from the [Registry catalogue](https://mailschema.org/registry/catalog.json) by digest. Processing MAP messages is outside this package; see the [profile](https://mailschema.org/specification/profile).
34
31
 
35
- ## Work with Registry data
32
+ ## Registry data
36
33
 
37
34
  ```js
38
- import {
39
- assertContribution,
40
- contributionErrors,
41
- getContributionSchema,
42
- referenceErrors,
43
- } from 'mailschema';
35
+ import { assertContribution, contributionErrors, referenceErrors } from 'mailschema';
44
36
 
45
37
  const errors = contributionErrors(candidate);
46
38
  if (errors.length) console.error(errors);
47
39
  else assertContribution(candidate);
48
40
 
49
- const schema = getContributionSchema();
50
41
  const referenceProblems = referenceErrors(candidate, catalog);
51
42
  ```
52
43
 
53
- `assertTypeRecord`, `getRecordSchema` and `mailschema check record.json --record` handle expanded Registry records. Reference checks use a catalogue supplied by the caller; the package never fetches one automatically.
54
-
55
- Raw Draft 2020-12 schemas are exported as:
44
+ `assertTypeRecord` and `getRecordSchema` handle expanded Registry records. Reference checks use a catalogue supplied by the caller; the package never fetches one.
56
45
 
57
- - `mailschema/map-0.1.schema.json`
58
- - `mailschema/content-review-0.1.schema.json`
59
- - `mailschema/contribution.schema.json`
46
+ ```sh
47
+ npx mailschema check contribution.json
48
+ npx mailschema check record.json --record
49
+ npx mailschema schema --map
50
+ ```
60
51
 
61
52
  ## Trust boundary
62
53
 
63
- A valid document is structured input. Validation does not authenticate a service, grant authority, approve an action or establish product conformance. Implementations must apply their own endpoint trust, credentials, permissions and policy before executing a request.
64
-
65
- The CLI reads one local file of at most 256 KiB and does not upload or modify it. Package versions and MAP profile versions advance independently. MIT licensed.
54
+ A valid document is structured input. Validation does not authenticate a service, grant authority or establish product conformance. The CLI reads one local file of at most 256 KiB and does not upload or modify it. Package versions and MAP profile versions advance independently. MIT licensed.
package/bin/mailschema.js CHANGED
@@ -3,59 +3,52 @@ import { open } from 'node:fs/promises';
3
3
  import { parseArgs } from 'node:util';
4
4
  import {
5
5
  assertContribution,
6
- assertContentReviewRequest,
7
- assertMapDocument,
8
6
  assertTypeRecord,
9
- getContentReviewSchema,
7
+ getContractFormatSchema,
10
8
  getContributionSchema,
9
+ getFormsSchema,
10
+ getMapContext,
11
11
  getMapSchema,
12
12
  getRecordSchema,
13
13
  } from '../dist/index.js';
14
14
 
15
- const help = `MailSchema contribution tools
15
+ const help = `MailSchema Registry tools
16
16
 
17
- mailschema check <file.json> [--record | --map | --content-review]
18
- mailschema schema [--record | --map | --content-review]
17
+ mailschema check <file.json> [--record]
18
+ mailschema schema [--record | --map | --context | --contract-format | --forms]
19
19
 
20
- Checks JSON structure and required fields locally. Registry references and
21
- editorial acceptance are separate checks. No files are uploaded or changed.`;
20
+ check validates a Registry contribution, or with --record an expanded record,
21
+ locally. schema prints a bundled schema: the contribution schema by default, or
22
+ a MAP 0.2 core artifact. No files are uploaded or changed.`;
23
+
24
+ const schemas = {
25
+ record: getRecordSchema,
26
+ map: getMapSchema,
27
+ context: getMapContext,
28
+ 'contract-format': getContractFormatSchema,
29
+ forms: getFormsSchema,
30
+ };
22
31
 
23
32
  try {
24
33
  const { values, positionals } = parseArgs({
25
34
  allowPositionals: true,
26
35
  options: {
27
- record: { type: 'boolean' },
28
- map: { type: 'boolean' },
29
- 'content-review': { type: 'boolean' },
36
+ ...Object.fromEntries(Object.keys(schemas).map((name) => [name, { type: 'boolean' }])),
30
37
  help: { type: 'boolean', short: 'h' },
31
38
  },
32
39
  });
33
40
  const [command, path, ...extra] = positionals;
34
- const formats = [values.record, values.map, values['content-review']].filter(Boolean);
35
- if (formats.length > 1) throw new Error('Choose only one document format.');
36
- const selected = values['content-review']
37
- ? 'Content Review request'
38
- : values.map
39
- ? 'MAP document'
40
- : values.record
41
- ? 'type record'
42
- : 'contribution';
41
+ const chosen = Object.keys(schemas).filter((name) => values[name]);
42
+ if (chosen.length > 1) throw new Error('Choose only one schema.');
43
43
  if (values.help || !command) console.log(help);
44
44
  else if (command === 'schema' && !path && !extra.length)
45
- console.log(
46
- JSON.stringify(
47
- values['content-review']
48
- ? getContentReviewSchema()
49
- : values.map
50
- ? getMapSchema()
51
- : values.record
52
- ? getRecordSchema()
53
- : getContributionSchema(),
54
- null,
55
- 2,
56
- ),
57
- );
58
- else if (command === 'check' && path && !extra.length) {
45
+ console.log(JSON.stringify((schemas[chosen[0]] ?? getContributionSchema)(), null, 2));
46
+ else if (
47
+ command === 'check' &&
48
+ path &&
49
+ !extra.length &&
50
+ chosen.every((name) => name === 'record')
51
+ ) {
59
52
  const file = await open(path, 'r');
60
53
  let value;
61
54
  try {
@@ -69,11 +62,9 @@ try {
69
62
  } finally {
70
63
  await file.close();
71
64
  }
72
- if (values['content-review']) assertContentReviewRequest(value);
73
- else if (values.map) assertMapDocument(value);
74
- else if (values.record) assertTypeRecord(value);
65
+ if (values.record) assertTypeRecord(value);
75
66
  else assertContribution(value);
76
- console.log(`Valid MailSchema ${selected}.`);
67
+ console.log(`Valid MailSchema ${values.record ? 'type record' : 'contribution'}.`);
77
68
  } else throw new Error(help);
78
69
  } catch (error) {
79
70
  console.error(error instanceof Error ? error.message : String(error));
@@ -0,0 +1,329 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://mailschema.org/schemas/forms-0.1.schema.json",
4
+ "title": "MailSchema form fields 0.1",
5
+ "description": "Shared, immutable blocks for types that collect values: a closed subset of JSON Schema based on Model Context Protocol elicitation, with HTML autofill field names.",
6
+ "$defs": {
7
+ "fieldName": {
8
+ "type": "string",
9
+ "pattern": "^[a-z][A-Za-z0-9]{0,63}$"
10
+ },
11
+ "text": {
12
+ "type": "string",
13
+ "minLength": 1,
14
+ "maxLength": 2000,
15
+ "pattern": "[^\\t\\n\\u000b\\f\\r \\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff]"
16
+ },
17
+ "autocomplete": {
18
+ "type": "string",
19
+ "maxLength": 200,
20
+ "pattern": "^(?:section-[a-z0-9-]+ )?(?:(?:shipping|billing) )?(?:(?:home|work|mobile|fax|pager) )?[a-z][a-z0-9-]*$",
21
+ "not": {
22
+ "pattern": "(?:^| )(?:current-password|new-password|one-time-code|cc-[a-z-]+)$"
23
+ }
24
+ },
25
+ "choice": {
26
+ "type": "object",
27
+ "additionalProperties": false,
28
+ "properties": {
29
+ "const": {
30
+ "type": "string",
31
+ "minLength": 1,
32
+ "maxLength": 200
33
+ },
34
+ "title": {
35
+ "type": "string",
36
+ "minLength": 1,
37
+ "maxLength": 240,
38
+ "pattern": "[^\\t\\n\\u000b\\f\\r \\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff]"
39
+ }
40
+ },
41
+ "required": ["const", "title"]
42
+ },
43
+ "choices": {
44
+ "type": "array",
45
+ "items": {
46
+ "$ref": "#/$defs/choice"
47
+ },
48
+ "minItems": 1,
49
+ "maxItems": 100
50
+ },
51
+ "textField": {
52
+ "type": "object",
53
+ "additionalProperties": false,
54
+ "properties": {
55
+ "type": {
56
+ "const": "string"
57
+ },
58
+ "title": {
59
+ "$ref": "#/$defs/text"
60
+ },
61
+ "description": {
62
+ "$ref": "#/$defs/text"
63
+ },
64
+ "minLength": {
65
+ "type": "integer",
66
+ "minimum": 0,
67
+ "maximum": 12000
68
+ },
69
+ "maxLength": {
70
+ "type": "integer",
71
+ "minimum": 1,
72
+ "maximum": 12000
73
+ },
74
+ "format": {
75
+ "enum": ["email", "uri", "date", "date-time"]
76
+ },
77
+ "default": {
78
+ "type": "string",
79
+ "maxLength": 12000
80
+ },
81
+ "autocomplete": {
82
+ "$ref": "#/$defs/autocomplete"
83
+ }
84
+ },
85
+ "required": ["type", "title"]
86
+ },
87
+ "numberField": {
88
+ "type": "object",
89
+ "additionalProperties": false,
90
+ "properties": {
91
+ "type": {
92
+ "enum": ["number", "integer"]
93
+ },
94
+ "title": {
95
+ "$ref": "#/$defs/text"
96
+ },
97
+ "description": {
98
+ "$ref": "#/$defs/text"
99
+ },
100
+ "minimum": {
101
+ "type": "integer",
102
+ "minimum": -9007199254740991,
103
+ "maximum": 9007199254740991
104
+ },
105
+ "maximum": {
106
+ "type": "integer",
107
+ "minimum": -9007199254740991,
108
+ "maximum": 9007199254740991
109
+ },
110
+ "default": {
111
+ "type": "integer",
112
+ "minimum": -9007199254740991,
113
+ "maximum": 9007199254740991
114
+ },
115
+ "autocomplete": {
116
+ "$ref": "#/$defs/autocomplete"
117
+ }
118
+ },
119
+ "required": ["type", "title"]
120
+ },
121
+ "booleanField": {
122
+ "type": "object",
123
+ "additionalProperties": false,
124
+ "properties": {
125
+ "type": {
126
+ "const": "boolean"
127
+ },
128
+ "title": {
129
+ "$ref": "#/$defs/text"
130
+ },
131
+ "description": {
132
+ "$ref": "#/$defs/text"
133
+ },
134
+ "default": {
135
+ "type": "boolean"
136
+ }
137
+ },
138
+ "required": ["type", "title"]
139
+ },
140
+ "choiceField": {
141
+ "type": "object",
142
+ "additionalProperties": false,
143
+ "properties": {
144
+ "type": {
145
+ "const": "string"
146
+ },
147
+ "title": {
148
+ "$ref": "#/$defs/text"
149
+ },
150
+ "description": {
151
+ "$ref": "#/$defs/text"
152
+ },
153
+ "oneOf": {
154
+ "$ref": "#/$defs/choices"
155
+ },
156
+ "default": {
157
+ "type": "string",
158
+ "maxLength": 200
159
+ },
160
+ "autocomplete": {
161
+ "$ref": "#/$defs/autocomplete"
162
+ }
163
+ },
164
+ "required": ["type", "title", "oneOf"]
165
+ },
166
+ "choicesField": {
167
+ "type": "object",
168
+ "additionalProperties": false,
169
+ "properties": {
170
+ "type": {
171
+ "const": "array"
172
+ },
173
+ "title": {
174
+ "$ref": "#/$defs/text"
175
+ },
176
+ "description": {
177
+ "$ref": "#/$defs/text"
178
+ },
179
+ "items": {
180
+ "type": "object",
181
+ "additionalProperties": false,
182
+ "properties": {
183
+ "anyOf": {
184
+ "$ref": "#/$defs/choices"
185
+ }
186
+ },
187
+ "required": ["anyOf"]
188
+ },
189
+ "minItems": {
190
+ "type": "integer",
191
+ "minimum": 0,
192
+ "maximum": 100
193
+ },
194
+ "maxItems": {
195
+ "type": "integer",
196
+ "minimum": 1,
197
+ "maximum": 100
198
+ },
199
+ "uniqueItems": {
200
+ "const": true
201
+ },
202
+ "default": {
203
+ "type": "array",
204
+ "items": {
205
+ "type": "string",
206
+ "maxLength": 200
207
+ },
208
+ "maxItems": 100,
209
+ "uniqueItems": true
210
+ }
211
+ },
212
+ "required": ["type", "title", "items", "uniqueItems"]
213
+ },
214
+ "fields": {
215
+ "type": "object",
216
+ "additionalProperties": false,
217
+ "properties": {
218
+ "type": {
219
+ "const": "object"
220
+ },
221
+ "properties": {
222
+ "type": "object",
223
+ "minProperties": 1,
224
+ "maxProperties": 50,
225
+ "propertyNames": {
226
+ "$ref": "#/$defs/fieldName"
227
+ },
228
+ "additionalProperties": {
229
+ "oneOf": [
230
+ {
231
+ "$ref": "#/$defs/textField"
232
+ },
233
+ {
234
+ "$ref": "#/$defs/numberField"
235
+ },
236
+ {
237
+ "$ref": "#/$defs/booleanField"
238
+ },
239
+ {
240
+ "$ref": "#/$defs/choiceField"
241
+ },
242
+ {
243
+ "$ref": "#/$defs/choicesField"
244
+ }
245
+ ]
246
+ }
247
+ },
248
+ "required": {
249
+ "type": "array",
250
+ "items": {
251
+ "$ref": "#/$defs/fieldName"
252
+ },
253
+ "uniqueItems": true,
254
+ "maxItems": 50
255
+ }
256
+ },
257
+ "required": ["type", "properties"]
258
+ },
259
+ "choiceFields": {
260
+ "type": "object",
261
+ "additionalProperties": false,
262
+ "properties": {
263
+ "type": {
264
+ "const": "object"
265
+ },
266
+ "properties": {
267
+ "type": "object",
268
+ "minProperties": 1,
269
+ "maxProperties": 50,
270
+ "propertyNames": {
271
+ "$ref": "#/$defs/fieldName"
272
+ },
273
+ "additionalProperties": {
274
+ "oneOf": [
275
+ {
276
+ "$ref": "#/$defs/booleanField"
277
+ },
278
+ {
279
+ "$ref": "#/$defs/choiceField"
280
+ },
281
+ {
282
+ "$ref": "#/$defs/choicesField"
283
+ }
284
+ ]
285
+ }
286
+ },
287
+ "required": {
288
+ "type": "array",
289
+ "items": {
290
+ "$ref": "#/$defs/fieldName"
291
+ },
292
+ "uniqueItems": true,
293
+ "maxItems": 50
294
+ }
295
+ },
296
+ "required": ["type", "properties"]
297
+ },
298
+ "fieldValues": {
299
+ "type": "object",
300
+ "maxProperties": 50,
301
+ "propertyNames": {
302
+ "$ref": "#/$defs/fieldName"
303
+ },
304
+ "additionalProperties": {
305
+ "anyOf": [
306
+ {
307
+ "type": "string",
308
+ "maxLength": 12000
309
+ },
310
+ {
311
+ "type": "number"
312
+ },
313
+ {
314
+ "type": "boolean"
315
+ },
316
+ {
317
+ "type": "array",
318
+ "items": {
319
+ "type": "string",
320
+ "maxLength": 200
321
+ },
322
+ "maxItems": 100,
323
+ "uniqueItems": true
324
+ }
325
+ ]
326
+ }
327
+ }
328
+ }
329
+ }
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export { contributionErrors, assertContribution, assertTypeRecord, referenceErro
2
2
  export type { CatalogView } from './validation.js';
3
3
  export { typeStages } from './model.js';
4
4
  export type { Party, TypeDefinition, TypeRecord, Contribution, Implementation } from './model.js';
5
- export { mapErrors, assertMapDocument, contentReviewRequestErrors, assertContentReviewRequest, getMapSchema, getContentReviewSchema, } from './map.js';
5
+ export { MAP_PROFILE, getMapSchema, getMapContext, getContractFormatSchema, getFormsSchema, } from './map.js';
6
6
  /** Return an independent copy of the contribution JSON Schema (Draft 2020-12). */
7
7
  export declare function getContributionSchema(): Record<string, unknown>;
8
8
  /** Return the schema for an expanded Registry record, including attribution and history. */
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- /** Schemas and validation tools for MAP documents and MailSchema Registry contributions. */
1
+ /** The MAP 0.2 core artifacts and validation tools for MailSchema Registry contributions. */
2
2
  import schema from './contribution.schema.json' with { type: 'json' };
3
3
  export { contributionErrors, assertContribution, assertTypeRecord, referenceErrors, } from './validation.js';
4
4
  export { typeStages } from './model.js';
5
- export { mapErrors, assertMapDocument, contentReviewRequestErrors, assertContentReviewRequest, getMapSchema, getContentReviewSchema, } from './map.js';
5
+ export { MAP_PROFILE, getMapSchema, getMapContext, getContractFormatSchema, getFormsSchema, } from './map.js';
6
6
  /** Return an independent copy of the contribution JSON Schema (Draft 2020-12). */
7
7
  export function getContributionSchema() {
8
8
  return structuredClone(schema);
@@ -0,0 +1,91 @@
1
+ {
2
+ "@context": {
3
+ "@version": 1.1,
4
+ "map": "https://mailschema.org/ns/map#",
5
+ "schema": "https://schema.org/",
6
+ "MailAction": "map:MailAction",
7
+ "profile": {
8
+ "@id": "map:profile",
9
+ "@type": "@id"
10
+ },
11
+ "type": {
12
+ "@id": "map:interactionType",
13
+ "@context": {
14
+ "id": {
15
+ "@id": "map:id",
16
+ "@type": "@id"
17
+ }
18
+ }
19
+ },
20
+ "version": "schema:version",
21
+ "contractDigest": "map:contractDigest",
22
+ "describedAt": {
23
+ "@id": "schema:dateCreated",
24
+ "@type": "http://www.w3.org/2001/XMLSchema#dateTime"
25
+ },
26
+ "expiresAt": {
27
+ "@id": "schema:expires",
28
+ "@type": "http://www.w3.org/2001/XMLSchema#dateTime"
29
+ },
30
+ "service": {
31
+ "@id": "schema:provider",
32
+ "@context": {
33
+ "id": {
34
+ "@id": "map:id",
35
+ "@type": "@id"
36
+ }
37
+ }
38
+ },
39
+ "name": "schema:name",
40
+ "authority": "map:authority",
41
+ "onBehalfOf": {
42
+ "@id": "map:onBehalfOf",
43
+ "@context": {
44
+ "id": {
45
+ "@id": "map:id",
46
+ "@type": "@id"
47
+ }
48
+ }
49
+ },
50
+ "resource": {
51
+ "@id": "map:resource",
52
+ "@type": "@id"
53
+ },
54
+ "execution": "map:execution",
55
+ "url": {
56
+ "@id": "schema:url",
57
+ "@type": "@id"
58
+ },
59
+ "resultUrlTemplate": "map:resultUrlTemplate",
60
+ "resultRetentionSeconds": "map:resultRetentionSeconds",
61
+ "humanUrl": {
62
+ "@id": "map:humanUrl",
63
+ "@type": "@id"
64
+ },
65
+ "target": {
66
+ "@id": "schema:object",
67
+ "@context": {
68
+ "id": {
69
+ "@id": "map:id",
70
+ "@type": "@id"
71
+ }
72
+ }
73
+ },
74
+ "recipient": "schema:recipient",
75
+ "details": {
76
+ "@id": "map:details",
77
+ "@type": "@json"
78
+ },
79
+ "revision": "schema:version",
80
+ "title": "schema:name",
81
+ "digest": "map:digest",
82
+ "operations": {
83
+ "@id": "schema:potentialAction",
84
+ "@container": "@set",
85
+ "@context": {
86
+ "id": "map:operationId"
87
+ }
88
+ },
89
+ "description": "schema:description"
90
+ }
91
+ }