mailschema 0.1.0 → 0.1.2

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/dist/index.d.ts CHANGED
@@ -2,6 +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
6
  /** Return an independent copy of the contribution JSON Schema (Draft 2020-12). */
6
7
  export declare function getContributionSchema(): Record<string, unknown>;
7
8
  /** Return the schema for an expanded Registry record, including attribution and history. */
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
- /** Tools for preparing MailSchema Registry contributions. */
1
+ /** Schemas and validation tools for MAP documents and 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
6
  /** Return an independent copy of the contribution JSON Schema (Draft 2020-12). */
6
7
  export function getContributionSchema() {
7
8
  return structuredClone(schema);
@@ -0,0 +1,275 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://mailschema.org/schemas/map-0.1.schema.json",
4
+ "title": "Mail Action Protocol 0.1",
5
+ "description": "Description, request, result and problem documents for the MAP 0.1 authenticated HTTPS profile.",
6
+ "oneOf": [
7
+ { "$ref": "#/$defs/description" },
8
+ { "$ref": "#/$defs/request" },
9
+ { "$ref": "#/$defs/result" },
10
+ { "$ref": "#/$defs/problem" }
11
+ ],
12
+ "$defs": {
13
+ "profile": {
14
+ "const": "https://mailschema.org/profiles/map/0.1"
15
+ },
16
+ "identifier": {
17
+ "type": "string",
18
+ "format": "uri",
19
+ "maxLength": 2048
20
+ },
21
+ "uuidUrn": {
22
+ "type": "string",
23
+ "pattern": "^urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
24
+ },
25
+ "digest": {
26
+ "type": "string",
27
+ "pattern": "^sha-256:[a-f0-9]{64}$"
28
+ },
29
+ "recordDigest": {
30
+ "type": "string",
31
+ "pattern": "^sha-256:[a-f0-9]{64}$"
32
+ },
33
+ "typeReference": {
34
+ "type": "object",
35
+ "additionalProperties": false,
36
+ "properties": {
37
+ "id": { "$ref": "#/$defs/identifier" },
38
+ "version": {
39
+ "type": "string",
40
+ "pattern": "^[0-9]+\\.[0-9]+(?:\\.[0-9]+)?(?:-[a-zA-Z0-9.-]+)?$",
41
+ "maxLength": 80
42
+ },
43
+ "recordDigest": { "$ref": "#/$defs/recordDigest" }
44
+ },
45
+ "required": ["id", "version", "recordDigest"]
46
+ },
47
+ "target": {
48
+ "type": "object",
49
+ "additionalProperties": false,
50
+ "properties": {
51
+ "id": { "$ref": "#/$defs/identifier" },
52
+ "revision": { "type": "string", "minLength": 1, "maxLength": 240 },
53
+ "title": { "type": "string", "minLength": 1, "maxLength": 500 },
54
+ "digest": { "$ref": "#/$defs/digest" }
55
+ },
56
+ "required": ["id", "revision", "digest"]
57
+ },
58
+ "operation": {
59
+ "type": "object",
60
+ "additionalProperties": false,
61
+ "properties": {
62
+ "id": {
63
+ "type": "string",
64
+ "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
65
+ "maxLength": 100
66
+ },
67
+ "name": { "type": "string", "minLength": 1, "maxLength": 240 },
68
+ "description": { "type": "string", "minLength": 1, "maxLength": 2000 },
69
+ "inputSchema": { "$ref": "#/$defs/identifier" }
70
+ },
71
+ "required": ["id", "name", "description", "inputSchema"]
72
+ },
73
+ "description": {
74
+ "type": "object",
75
+ "additionalProperties": false,
76
+ "properties": {
77
+ "@context": {
78
+ "const": "https://mailschema.org/contexts/map-0.1.jsonld"
79
+ },
80
+ "@type": { "const": "MailAction" },
81
+ "@id": { "$ref": "#/$defs/uuidUrn" },
82
+ "profile": { "$ref": "#/$defs/profile" },
83
+ "type": { "$ref": "#/$defs/typeReference" },
84
+ "describedAt": { "type": "string", "format": "date-time" },
85
+ "expiresAt": { "type": "string", "format": "date-time" },
86
+ "service": {
87
+ "type": "object",
88
+ "additionalProperties": false,
89
+ "properties": {
90
+ "id": { "$ref": "#/$defs/identifier" },
91
+ "name": { "type": "string", "minLength": 1, "maxLength": 240 },
92
+ "execution": {
93
+ "type": "object",
94
+ "additionalProperties": false,
95
+ "properties": {
96
+ "url": { "$ref": "#/$defs/identifier" },
97
+ "method": { "const": "POST" },
98
+ "requestMediaType": { "const": "application/json" },
99
+ "resultMediaType": { "const": "application/json" },
100
+ "resultUrlTemplate": {
101
+ "type": "string",
102
+ "format": "uri-template",
103
+ "pattern": "^https://.*\\{requestId\\}.*$",
104
+ "maxLength": 2048
105
+ },
106
+ "resultRetentionSeconds": {
107
+ "type": "integer",
108
+ "minimum": 300,
109
+ "maximum": 31536000
110
+ }
111
+ },
112
+ "required": [
113
+ "url",
114
+ "method",
115
+ "requestMediaType",
116
+ "resultMediaType",
117
+ "resultUrlTemplate",
118
+ "resultRetentionSeconds"
119
+ ]
120
+ },
121
+ "humanUrl": { "$ref": "#/$defs/identifier" },
122
+ "authorization": {
123
+ "type": "object",
124
+ "additionalProperties": false,
125
+ "properties": {
126
+ "kind": { "const": "service-configured" },
127
+ "schemes": {
128
+ "type": "array",
129
+ "items": { "enum": ["oauth2", "bearer", "session"] },
130
+ "minItems": 1,
131
+ "uniqueItems": true
132
+ },
133
+ "audience": { "type": "string", "minLength": 1, "maxLength": 500 }
134
+ },
135
+ "required": ["kind", "schemes"]
136
+ }
137
+ },
138
+ "required": ["id", "name", "execution", "humanUrl", "authorization"]
139
+ },
140
+ "target": { "$ref": "#/$defs/target" },
141
+ "operations": {
142
+ "type": "array",
143
+ "items": { "$ref": "#/$defs/operation" },
144
+ "minItems": 1,
145
+ "maxItems": 100
146
+ }
147
+ },
148
+ "required": [
149
+ "@context",
150
+ "@type",
151
+ "@id",
152
+ "profile",
153
+ "type",
154
+ "describedAt",
155
+ "expiresAt",
156
+ "service",
157
+ "target",
158
+ "operations"
159
+ ]
160
+ },
161
+ "request": {
162
+ "type": "object",
163
+ "additionalProperties": false,
164
+ "properties": {
165
+ "kind": { "const": "MapRequest" },
166
+ "profile": { "$ref": "#/$defs/profile" },
167
+ "requestId": { "$ref": "#/$defs/uuidUrn" },
168
+ "interactionId": { "$ref": "#/$defs/uuidUrn" },
169
+ "requestedAt": { "type": "string", "format": "date-time" },
170
+ "type": { "$ref": "#/$defs/typeReference" },
171
+ "operation": {
172
+ "type": "string",
173
+ "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
174
+ "maxLength": 100
175
+ },
176
+ "target": { "$ref": "#/$defs/target" },
177
+ "input": { "type": "object", "maxProperties": 100 }
178
+ },
179
+ "required": [
180
+ "kind",
181
+ "profile",
182
+ "requestId",
183
+ "interactionId",
184
+ "requestedAt",
185
+ "type",
186
+ "operation",
187
+ "target",
188
+ "input"
189
+ ]
190
+ },
191
+ "result": {
192
+ "type": "object",
193
+ "additionalProperties": false,
194
+ "properties": {
195
+ "kind": { "const": "MapResult" },
196
+ "profile": { "$ref": "#/$defs/profile" },
197
+ "requestId": { "$ref": "#/$defs/uuidUrn" },
198
+ "interactionId": { "$ref": "#/$defs/uuidUrn" },
199
+ "operation": {
200
+ "type": "string",
201
+ "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
202
+ "maxLength": 100
203
+ },
204
+ "state": { "enum": ["accepted", "completed", "pending", "approval-required"] },
205
+ "target": { "$ref": "#/$defs/target" },
206
+ "recordedAt": { "type": "string", "format": "date-time" },
207
+ "resultUrl": { "$ref": "#/$defs/identifier" },
208
+ "output": { "type": "object", "maxProperties": 100 }
209
+ },
210
+ "required": [
211
+ "kind",
212
+ "profile",
213
+ "requestId",
214
+ "interactionId",
215
+ "operation",
216
+ "state",
217
+ "target",
218
+ "recordedAt",
219
+ "resultUrl",
220
+ "output"
221
+ ]
222
+ },
223
+ "problem": {
224
+ "type": "object",
225
+ "additionalProperties": false,
226
+ "properties": {
227
+ "type": {
228
+ "enum": [
229
+ "https://mailschema.org/problems/refused",
230
+ "https://mailschema.org/problems/stale-target",
231
+ "https://mailschema.org/problems/expired-interaction",
232
+ "https://mailschema.org/problems/invalid-request",
233
+ "https://mailschema.org/problems/unsupported-profile",
234
+ "https://mailschema.org/problems/unsupported-type",
235
+ "https://mailschema.org/problems/unsupported-operation",
236
+ "https://mailschema.org/problems/idempotency-conflict",
237
+ "https://mailschema.org/problems/request-in-progress"
238
+ ]
239
+ },
240
+ "title": { "type": "string", "minLength": 1, "maxLength": 240 },
241
+ "status": { "type": "integer", "minimum": 400, "maximum": 599 },
242
+ "detail": { "type": "string", "minLength": 1, "maxLength": 4000 },
243
+ "instance": { "$ref": "#/$defs/identifier" },
244
+ "profile": { "$ref": "#/$defs/profile" },
245
+ "requestId": { "$ref": "#/$defs/uuidUrn" },
246
+ "interactionId": { "$ref": "#/$defs/uuidUrn" },
247
+ "code": {
248
+ "enum": [
249
+ "refused",
250
+ "stale-target",
251
+ "expired-interaction",
252
+ "invalid-request",
253
+ "unsupported-profile",
254
+ "unsupported-type",
255
+ "unsupported-operation",
256
+ "idempotency-conflict",
257
+ "request-in-progress"
258
+ ]
259
+ },
260
+ "target": { "$ref": "#/$defs/target" }
261
+ },
262
+ "required": [
263
+ "type",
264
+ "title",
265
+ "status",
266
+ "detail",
267
+ "instance",
268
+ "profile",
269
+ "requestId",
270
+ "interactionId",
271
+ "code"
272
+ ]
273
+ }
274
+ }
275
+ }
package/dist/map.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export declare function mapErrors(value: unknown): string[];
2
+ export declare function assertMapDocument(value: unknown): void;
3
+ export declare function contentReviewRequestErrors(value: unknown): string[];
4
+ export declare function assertContentReviewRequest(value: unknown): void;
5
+ export declare function getMapSchema(): Record<string, unknown>;
6
+ export declare function getContentReviewSchema(): Record<string, unknown>;
package/dist/map.js ADDED
@@ -0,0 +1,35 @@
1
+ import Ajv2020 from 'ajv/dist/2020.js';
2
+ import addFormats from 'ajv-formats';
3
+ import mapSchema from './map-0.1.schema.json' with { type: 'json' };
4
+ import contentReviewSchema from './content-review-0.1.schema.json' with { type: 'json' };
5
+ const ajv = new Ajv2020({ allErrors: true, strict: true, strictRequired: false });
6
+ addFormats(ajv);
7
+ const mapDocument = ajv.compile(mapSchema);
8
+ const contentReviewRequest = ajv.compile(contentReviewSchema);
9
+ function errors(validate, value) {
10
+ if (validate(value))
11
+ return [];
12
+ return (validate.errors || []).map((error) => `${error.instancePath || '/'} ${error.message}${error.keyword === 'required' ? `: ${error.params.missingProperty}` : ''}`);
13
+ }
14
+ export function mapErrors(value) {
15
+ return errors(mapDocument, value);
16
+ }
17
+ export function assertMapDocument(value) {
18
+ const found = mapErrors(value);
19
+ if (found.length)
20
+ throw new Error(`Invalid MAP 0.1 document:\n${found.join('\n')}`);
21
+ }
22
+ export function contentReviewRequestErrors(value) {
23
+ return errors(contentReviewRequest, value);
24
+ }
25
+ export function assertContentReviewRequest(value) {
26
+ const found = contentReviewRequestErrors(value);
27
+ if (found.length)
28
+ throw new Error(`Invalid Content Review 0.1 request:\n${found.join('\n')}`);
29
+ }
30
+ export function getMapSchema() {
31
+ return structuredClone(mapSchema);
32
+ }
33
+ export function getContentReviewSchema() {
34
+ return structuredClone(contentReviewSchema);
35
+ }
package/dist/model.d.ts CHANGED
@@ -26,6 +26,7 @@ export interface TypeDefinition {
26
26
  overview: string;
27
27
  target: string;
28
28
  operations: {
29
+ id: string;
29
30
  name: string;
30
31
  description: string;
31
32
  }[];
@@ -39,7 +39,7 @@ export function referenceErrors(input, catalog) {
39
39
  ];
40
40
  if (target.profile !== input.profile)
41
41
  errors.push('Execution profile does not match the targeted record.');
42
- const operations = new Set(target.operations.map((operation) => operation.name));
42
+ const operations = new Set(target.operations.map((operation) => operation.id));
43
43
  if (new Set(input.operations).size !== input.operations.length ||
44
44
  input.operations.some((operation) => !operations.has(operation)))
45
45
  errors.push('Unknown or duplicate operation in the implementation declaration.');
@@ -53,8 +53,11 @@ export function referenceErrors(input, catalog) {
53
53
  if (catalog.types.some((item) => item.record.slug !== input.record.slug &&
54
54
  item.record.name.toLocaleLowerCase() === input.record.name.toLocaleLowerCase()))
55
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)
56
+ const operationIds = input.record.operations.map((operation) => operation.id);
57
+ const operationNames = input.record.operations.map((operation) => operation.name.toLocaleLowerCase());
58
+ if (new Set(operationIds).size !== operationIds.length)
59
+ errors.push('Operation identifiers must be unique within a type.');
60
+ if (new Set(operationNames).size !== operationNames.length)
58
61
  errors.push('Operation names must be unique within a type.');
59
62
  }
60
63
  return errors;
package/package.json CHANGED
@@ -1,10 +1,18 @@
1
1
  {
2
2
  "name": "mailschema",
3
- "version": "0.1.0",
4
- "description": "JSON Schemas, TypeScript definitions and validation tools for MailSchema Registry contributions",
3
+ "version": "0.1.2",
4
+ "description": "Schemas and validation tools for Mail Action Protocol and the MailSchema Registry",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "MailSchema contributors",
8
+ "homepage": "https://mailschema.org/tools/",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/mailschema/javascript.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/mailschema/javascript/issues"
15
+ },
8
16
  "engines": {
9
17
  "node": ">=22"
10
18
  },
@@ -14,22 +22,31 @@
14
22
  "import": "./dist/index.js"
15
23
  },
16
24
  "./contribution.schema.json": "./dist/contribution.schema.json",
25
+ "./map-0.1.schema.json": "./dist/map-0.1.schema.json",
26
+ "./content-review-0.1.schema.json": "./dist/content-review-0.1.schema.json",
17
27
  "./package.json": "./package.json"
18
28
  },
19
29
  "types": "./dist/index.d.ts",
20
30
  "bin": {
21
- "mailschema": "./cli.mjs"
31
+ "mailschema": "./bin/mailschema.js"
22
32
  },
23
33
  "files": [
24
34
  "dist",
25
- "cli.mjs",
35
+ "bin",
26
36
  "README.md",
27
37
  "LICENSE"
28
38
  ],
39
+ "scripts": {
40
+ "build": "tsc -p tsconfig.json",
41
+ "test": "npm run build && node --test test/*.test.mjs"
42
+ },
29
43
  "dependencies": {
30
44
  "ajv": "8.20.0",
31
45
  "ajv-formats": "3.0.1"
32
46
  },
47
+ "devDependencies": {
48
+ "typescript": "5.9.3"
49
+ },
33
50
  "keywords": [
34
51
  "email",
35
52
  "schema",