mailschema 0.2.2 → 0.3.1

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 +27 -119
  2. package/bin/mailschema.js +42 -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
package/dist/map.d.ts DELETED
@@ -1,8 +0,0 @@
1
- /** A fresh copy of the MAP 0.2 core schema, as the profile record binds it by SHA-256. */
2
- export declare function getMapSchema(): Record<string, unknown>;
3
- /** A fresh copy of the MAP 0.2 JSON-LD context. */
4
- export declare function getMapContext(): Record<string, unknown>;
5
- /** A fresh copy of the type contract format every MAP 0.2 contract follows. */
6
- export declare function getContractFormatSchema(): Record<string, unknown>;
7
- /** A fresh copy of the form fields block contracts pin. */
8
- export declare function getFormsSchema(): Record<string, unknown>;
package/dist/map.js DELETED
@@ -1,18 +0,0 @@
1
- import { readFileSync } from 'node:fs';
2
- const artifact = (name) => JSON.parse(readFileSync(new URL(`./${name}`, import.meta.url), 'utf8'));
3
- /** A fresh copy of the MAP 0.2 core schema, as the profile record binds it by SHA-256. */
4
- export function getMapSchema() {
5
- return artifact('map-0.2.schema.json');
6
- }
7
- /** A fresh copy of the MAP 0.2 JSON-LD context. */
8
- export function getMapContext() {
9
- return artifact('map-0.2.jsonld');
10
- }
11
- /** A fresh copy of the type contract format every MAP 0.2 contract follows. */
12
- export function getContractFormatSchema() {
13
- return artifact('type-contract-0.2.schema.json');
14
- }
15
- /** A fresh copy of the form fields block contracts pin. */
16
- export function getFormsSchema() {
17
- return artifact('forms-0.1.schema.json');
18
- }
package/dist/model.d.ts DELETED
@@ -1,95 +0,0 @@
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
- id: string;
30
- name: string;
31
- description: string;
32
- }[];
33
- inputs: string;
34
- results: string;
35
- permissions: string;
36
- humanRoute: string;
37
- example: {
38
- title: string;
39
- steps: string[];
40
- exception: string;
41
- };
42
- references: {
43
- title: string;
44
- href: string;
45
- description: string;
46
- }[];
47
- openQuestions: string[];
48
- maintainers: Party[];
49
- definition?: {
50
- label: string;
51
- href: string;
52
- };
53
- }
54
- export interface TypeRecord extends TypeDefinition {
55
- origin: string;
56
- contributors: Party[];
57
- history: {
58
- label: string;
59
- description: string;
60
- contributionId?: string;
61
- }[];
62
- }
63
- interface Submission {
64
- format: 'mailschema-contribution/1';
65
- id: string;
66
- contributor: Party;
67
- source?: string;
68
- summary: string;
69
- }
70
- export type Contribution = Submission & ({
71
- kind: 'new-type';
72
- record: TypeDefinition;
73
- } | {
74
- kind: 'amendment';
75
- baseDigest: string;
76
- record: TypeDefinition;
77
- } | {
78
- kind: 'implementation';
79
- type: string;
80
- typeVersion: string;
81
- typeDigest: string;
82
- profile: string;
83
- product: Party;
84
- operations: string[];
85
- evidence: {
86
- kind: 'declaration' | 'test-report';
87
- url?: string;
88
- reproduction?: string;
89
- results?: string;
90
- };
91
- });
92
- export type Implementation = Extract<Contribution, {
93
- kind: 'implementation';
94
- }>;
95
- export {};
package/dist/model.js DELETED
@@ -1,5 +0,0 @@
1
- export const typeStages = [
2
- { status: 'Draft', singular: 'draft', plural: 'drafts' },
3
- { status: 'Proposal', singular: 'proposal', plural: 'proposals' },
4
- { status: 'Reuse assessment', singular: 'reuse assessment', plural: 'reuse assessments' },
5
- ];
@@ -1,179 +0,0 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://mailschema.org/schemas/type-contract-0.2.schema.json",
4
- "title": "MailSchema type contract for MAP 0.2",
5
- "description": "A versioned interaction contract: the details, inputs, results, authority, repeatability and consequences of each operation, with every schema it depends on pinned by digest.",
6
- "type": "object",
7
- "additionalProperties": false,
8
- "properties": {
9
- "kind": {
10
- "const": "MapTypeContract"
11
- },
12
- "id": {
13
- "type": "string",
14
- "pattern": "^https://mailschema\\.org/types/[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
15
- "maxLength": 2048
16
- },
17
- "version": {
18
- "type": "string",
19
- "pattern": "^[0-9]+\\.[0-9]+(?:\\.[0-9]+)?(?:-[a-zA-Z0-9.-]+)?$",
20
- "maxLength": 80
21
- },
22
- "profile": {
23
- "const": "https://mailschema.org/profiles/map/0.2"
24
- },
25
- "target": {
26
- "$ref": "#/$defs/prose"
27
- },
28
- "dependencies": {
29
- "type": "array",
30
- "minItems": 1,
31
- "maxItems": 20,
32
- "items": {
33
- "$ref": "#/$defs/schemaReference"
34
- }
35
- },
36
- "detailsSchema": {
37
- "type": "object"
38
- },
39
- "requestSchema": {
40
- "$ref": "#/$defs/schemaReference"
41
- },
42
- "operations": {
43
- "type": "array",
44
- "minItems": 1,
45
- "maxItems": 100,
46
- "items": {
47
- "type": "object",
48
- "additionalProperties": false,
49
- "properties": {
50
- "id": {
51
- "$ref": "#/$defs/token"
52
- },
53
- "effect": {
54
- "$ref": "#/$defs/prose"
55
- },
56
- "authority": {
57
- "type": "array",
58
- "items": {
59
- "enum": ["credential", "possession"]
60
- },
61
- "minItems": 1,
62
- "maxItems": 2,
63
- "uniqueItems": true
64
- },
65
- "consequences": {
66
- "type": "array",
67
- "items": {
68
- "enum": [
69
- "refusal",
70
- "protection",
71
- "record",
72
- "disclosure",
73
- "commitment",
74
- "authorization",
75
- "assertion"
76
- ]
77
- },
78
- "minItems": 1,
79
- "maxItems": 7,
80
- "uniqueItems": true
81
- },
82
- "repeatable": {
83
- "type": "boolean"
84
- },
85
- "fieldBindings": {
86
- "type": "array",
87
- "minItems": 1,
88
- "maxItems": 10,
89
- "items": {
90
- "type": "object",
91
- "additionalProperties": false,
92
- "properties": {
93
- "input": {
94
- "$ref": "#/$defs/pointer"
95
- },
96
- "fields": {
97
- "$ref": "#/$defs/pointer"
98
- }
99
- },
100
- "required": ["input", "fields"]
101
- }
102
- },
103
- "results": {
104
- "type": "array",
105
- "minItems": 1,
106
- "maxItems": 5,
107
- "items": {
108
- "type": "object",
109
- "additionalProperties": false,
110
- "properties": {
111
- "state": {
112
- "enum": ["accepted", "completed", "failed", "pending", "approval-required"]
113
- },
114
- "reasons": {
115
- "type": "array",
116
- "items": {
117
- "$ref": "#/$defs/token"
118
- },
119
- "minItems": 1,
120
- "maxItems": 20,
121
- "uniqueItems": true
122
- },
123
- "outputSchema": {
124
- "type": "object"
125
- }
126
- },
127
- "required": ["state", "outputSchema"]
128
- }
129
- }
130
- },
131
- "required": ["id", "effect", "authority", "consequences", "results"]
132
- }
133
- }
134
- },
135
- "required": [
136
- "kind",
137
- "id",
138
- "version",
139
- "profile",
140
- "target",
141
- "dependencies",
142
- "requestSchema",
143
- "operations"
144
- ],
145
- "$defs": {
146
- "prose": {
147
- "type": "string",
148
- "minLength": 1,
149
- "maxLength": 12000,
150
- "pattern": "[^\\t\\n\\u000b\\f\\r \\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff]"
151
- },
152
- "token": {
153
- "type": "string",
154
- "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
155
- "maxLength": 100
156
- },
157
- "pointer": {
158
- "type": "string",
159
- "pattern": "^(?:/(?:[^~/]|~[01])*)+$",
160
- "maxLength": 1000
161
- },
162
- "schemaReference": {
163
- "type": "object",
164
- "additionalProperties": false,
165
- "properties": {
166
- "url": {
167
- "type": "string",
168
- "pattern": "^https://mailschema\\.org/schemas/[a-z0-9.-]+\\.json$",
169
- "maxLength": 2048
170
- },
171
- "canonicalDigest": {
172
- "type": "string",
173
- "pattern": "^sha-256:[a-f0-9]{64}$"
174
- }
175
- },
176
- "required": ["url", "canonicalDigest"]
177
- }
178
- }
179
- }
@@ -1,15 +0,0 @@
1
- import type { Contribution, TypeRecord } from './model.ts';
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[];
@@ -1,64 +0,0 @@
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.id));
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 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)
61
- errors.push('Operation names must be unique within a type.');
62
- }
63
- return errors;
64
- }