Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 17 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
[![npm](https://img.shields.io/npm/v/mailschema)](https://www.npmjs.com/package/mailschema)
[![CI](https://github.com/mailschema/javascript/actions/workflows/test.yml/badge.svg)](https://github.com/mailschema/javascript/actions/workflows/test.yml)

Validate Mail Action Protocol documents and MailSchema Registry contributions without making a network request.
The Mail Action Protocol 0.2 core artifacts, and local validation of MailSchema Registry contributions, with no network requests.

[Specification](https://mailschema.org/specification/) · [Registry](https://mailschema.org/registry/) · [Tools](https://mailschema.org/tools/) · [Source](https://github.com/mailschema/javascript)
[Specification](https://mailschema.org/specification) · [Registry](https://mailschema.org/registry) · [Tools](https://mailschema.org/tools) · [Source](https://github.com/mailschema/javascript)

## Install

Expand All @@ -15,62 +15,40 @@ npm install mailschema

Node.js 22 or newer is required. The package is ESM and includes TypeScript declarations.

## Validate MAP documents
## MAP 0.2 core artifacts

```js
import {
assertMapDocument,
assertContentReviewRequest,
getContentReviewContract,
MAP_PROFILE,
getContractFormatSchema,
getFormsSchema,
getMapContext,
getMapSchema,
} from 'mailschema';

assertMapDocument(description);
assertContentReviewRequest(request);
const mapSchema = getMapSchema();
const contentReviewContract = getContentReviewContract();
```

The CLI performs the same checks against local JSON files:

```sh
npx mailschema check description.json --map
npx mailschema check request.json --content-review
```
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).

## Work with Registry data
## Registry data

```js
import {
assertContribution,
contributionErrors,
getContributionSchema,
referenceErrors,
} from 'mailschema';
import { assertContribution, contributionErrors, referenceErrors } from 'mailschema';

const errors = contributionErrors(candidate);
if (errors.length) console.error(errors);
else assertContribution(candidate);

const schema = getContributionSchema();
const referenceProblems = referenceErrors(candidate, catalog);
```

`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.
`assertTypeRecord` and `getRecordSchema` handle expanded Registry records. Reference checks use a catalogue supplied by the caller; the package never fetches one.

Raw Draft 2020-12 schemas are exported as:

- `mailschema/map-0.1.schema.json`
- `mailschema/content-review-0.1.schema.json`
- `mailschema/content-review-0.1.contract.json`
- `mailschema/content-review-0.2.schema.json`
- `mailschema/content-review-0.2.contract.json`
- `mailschema/contribution.schema.json`

The unversioned Content Review helpers use 0.2. The `*ContentReview01*` helpers and 0.1 raw exports remain available for implementations pinned to the earlier draft.
```sh
npx mailschema check contribution.json
npx mailschema check record.json --record
npx mailschema schema --map
```

## Trust boundary

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.

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.
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.
13 changes: 6 additions & 7 deletions UPSTREAM.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
# Release provenance

Version `0.1.4` derives from [`mailschema/mailschema@f630ec5`](https://github.com/mailschema/mailschema/commit/f630ec5569e7167114fb2a39a10548febfa6b979).
Version `0.1.5` derives from [`mailschema/mailschema@61a12c9`](https://github.com/mailschema/mailschema/commit/61a12c97663061b46707f8d996bd1f59cae9a544), in [pull request #22](https://github.com/mailschema/mailschema/pull/22).

The bundled files are exact projections of that source commit and do not independently define MAP:

- MAP 0.1 schema: `c39befeecf8643e8beda1f53a8d1411482b68ddf14974f3806cfa95776103f85`
- Content Review 0.1 schema: `10e2263c820ba3a6ef0381c377078e2c1f324c3b142a6f8caf92b298cc66c692`
- Content Review 0.1 contract: `754ab7245ddb09ff4da000fccc60d746724379d4146ea227973e5d87f105f800`
- Content Review 0.2 schema: `d2286ffe410cceae56f9caa145634fd9613f5ce51e11ba089cad67b9f62259b7`
- Content Review 0.2 contract: `3bd1cd4a86e00d00590eeb150b7b29cebece589058d6094a40aa9d497dc8efe0`
- MAP 0.2 core schema: `732d60fcb758d945dc15c57d885c9d1c0c76423a7439096d9755e8ec9830836a`
- MAP 0.2 JSON-LD context: `dfed0189524d7d16d31ed3d8af97e9a0c458200c4c034b413f9d14fa35b60257`
- Type contract format: `e030007cc034662c9d9d448a8a035aff3106cfb96fb60e6aa9f92b99b25df0b9`
- Form fields block: `3a976b93a057380e3630ca05e77e221a43a98559fb7fda8066299ec4bf2a8f9e`
- Contribution schema: `a8a241b4837971a6251bcc135c704283536f4fb825fbbaedaf1598c6e688d564`

The canonical contracts live in the main MailSchema repository. This repository owns the JavaScript and TypeScript package surface and its release history.
The canonical artifacts live in the main MailSchema repository. This repository owns the JavaScript and TypeScript package surface and its release history.
7 changes: 7 additions & 0 deletions artifacts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
"contribution.schema.json",
"map-0.2.schema.json",
"map-0.2.jsonld",
"type-contract-0.2.schema.json",
"forms-0.1.schema.json"
]
67 changes: 29 additions & 38 deletions bin/mailschema.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,59 +3,52 @@ import { open } from 'node:fs/promises';
import { parseArgs } from 'node:util';
import {
assertContribution,
assertContentReviewRequest,
assertMapDocument,
assertTypeRecord,
getContentReviewSchema,
getContractFormatSchema,
getContributionSchema,
getFormsSchema,
getMapContext,
getMapSchema,
getRecordSchema,
} from '../dist/index.js';

const help = `MailSchema contribution tools
const help = `MailSchema Registry tools

mailschema check <file.json> [--record | --map | --content-review]
mailschema schema [--record | --map | --content-review]
mailschema check <file.json> [--record]
mailschema schema [--record | --map | --context | --contract-format | --forms]

Checks JSON structure and required fields locally. Registry references and
editorial acceptance are separate checks. No files are uploaded or changed.`;
check validates a Registry contribution, or with --record an expanded record,
locally. schema prints a bundled schema: the contribution schema by default, or
a MAP 0.2 core artifact. No files are uploaded or changed.`;

const schemas = {
record: getRecordSchema,
map: getMapSchema,
context: getMapContext,
'contract-format': getContractFormatSchema,
forms: getFormsSchema,
};

try {
const { values, positionals } = parseArgs({
allowPositionals: true,
options: {
record: { type: 'boolean' },
map: { type: 'boolean' },
'content-review': { type: 'boolean' },
...Object.fromEntries(Object.keys(schemas).map((name) => [name, { type: 'boolean' }])),
help: { type: 'boolean', short: 'h' },
},
});
const [command, path, ...extra] = positionals;
const formats = [values.record, values.map, values['content-review']].filter(Boolean);
if (formats.length > 1) throw new Error('Choose only one document format.');
const selected = values['content-review']
? 'Content Review request'
: values.map
? 'MAP document'
: values.record
? 'type record'
: 'contribution';
const chosen = Object.keys(schemas).filter((name) => values[name]);
if (chosen.length > 1) throw new Error('Choose only one schema.');
if (values.help || !command) console.log(help);
else if (command === 'schema' && !path && !extra.length)
console.log(
JSON.stringify(
values['content-review']
? getContentReviewSchema()
: values.map
? getMapSchema()
: values.record
? getRecordSchema()
: getContributionSchema(),
null,
2,
),
);
else if (command === 'check' && path && !extra.length) {
console.log(JSON.stringify((schemas[chosen[0]] ?? getContributionSchema)(), null, 2));
else if (
command === 'check' &&
path &&
!extra.length &&
chosen.every((name) => name === 'record')
) {
const file = await open(path, 'r');
let value;
try {
Expand All @@ -69,11 +62,9 @@ try {
} finally {
await file.close();
}
if (values['content-review']) assertContentReviewRequest(value);
else if (values.map) assertMapDocument(value);
else if (values.record) assertTypeRecord(value);
if (values.record) assertTypeRecord(value);
else assertContribution(value);
console.log(`Valid MailSchema ${selected}.`);
console.log(`Valid MailSchema ${values.record ? 'type record' : 'contribution'}.`);
} else throw new Error(help);
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
Expand Down
14 changes: 5 additions & 9 deletions build.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { execFileSync } from 'node:child_process';
import { copyFile } from 'node:fs/promises';
import { copyFile, readFile } from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

Expand All @@ -10,13 +10,9 @@ execFileSync(process.execPath, [resolve(root, 'node_modules/typescript/bin/tsc')
stdio: 'inherit',
});

for (const name of [
'contribution.schema.json',
'map-0.1.schema.json',
'content-review-0.1.schema.json',
'content-review-0.1.contract.json',
'content-review-0.2.schema.json',
'content-review-0.2.contract.json',
]) {
const artifacts = JSON.parse(await readFile(resolve(root, 'artifacts.json'), 'utf8'));
if (!Array.isArray(artifacts) || artifacts.some((name) => typeof name !== 'string'))
throw new Error('Invalid generated package artifact list.');
for (const name of artifacts) {
await copyFile(resolve(root, 'src', name), resolve(root, 'dist', name));
}
58 changes: 0 additions & 58 deletions dist/content-review-0.1.contract.json

This file was deleted.

54 changes: 0 additions & 54 deletions dist/content-review-0.1.schema.json

This file was deleted.

Loading
Loading