@vormio/plugin-sdk 0.0.9 → 0.0.10-rc.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.
- package/README.md +54 -6
- package/dist/bridge.d.ts +11 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/manifest.d.ts +18 -2
- package/dist/manifest.js +0 -0
- package/dist/node/index.d.ts +2 -2
- package/dist/node/index.js +1 -1
- package/dist/node/package.d.ts +8 -0
- package/dist/node/package.js +22 -1
- package/dist/report.d.ts +2 -0
- package/dist/report.js +18 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The manifest schema, runtime helpers and packaging command for writing Vormio plugins, plus the commands that publish a signed plugin catalog. The package is versioned with Vormio-Core (currently 0.0.9) and published to npm under the MIT license: `npm install @vormio/plugin-sdk@0.0.9`.
|
|
4
4
|
|
|
5
|
-
Plugin contract version: `apiVersion:
|
|
5
|
+
Plugin contract version: `apiVersion: 5`. During development (0.0.x) there is no cross-version compatibility promise: the host loads only plugins with the same `apiVersion` as its own.
|
|
6
6
|
|
|
7
7
|
## What a plugin can do
|
|
8
8
|
|
|
@@ -28,7 +28,7 @@ A plugin is a folder: a `manifest.json` at the root plus the built static files.
|
|
|
28
28
|
|
|
29
29
|
```json
|
|
30
30
|
{
|
|
31
|
-
"apiVersion":
|
|
31
|
+
"apiVersion": 5,
|
|
32
32
|
"id": "acme.model-viewer",
|
|
33
33
|
"version": "0.1.0",
|
|
34
34
|
"name": "Model Viewer",
|
|
@@ -59,6 +59,7 @@ A plugin is a folder: a `manifest.json` at the root plus the built static files.
|
|
|
59
59
|
- `id` is `publisher.name`, using only lowercase letters, digits and hyphens. The `vormio` publisher is reserved for the host.
|
|
60
60
|
- `formats` and `companionFormats` are lowercase extensions without the dot. Wildcards are not allowed.
|
|
61
61
|
- `entry` is an HTML file in the package. Pages run under a strict Content Security Policy: scripts must be separate files, not inline.
|
|
62
|
+
- `agreement` is optional; see [Agreements users accept before enabling](#agreements-users-accept-before-enabling).
|
|
62
63
|
- Unknown fields are rejected.
|
|
63
64
|
|
|
64
65
|
**Allowed files**: `html js mjs css json wasm png jpg jpeg webp svg woff2 txt md`. Links, `..` paths, Windows reserved names and names that differ only by case are not allowed.
|
|
@@ -73,6 +74,24 @@ A plugin is a folder: a `manifest.json` at the root plus the built static files.
|
|
|
73
74
|
|
|
74
75
|
Reading more formats or companion formats, or adding an extension point, requires the user to enable the plugin again, and that approval is added to what was approved before.
|
|
75
76
|
|
|
77
|
+
### Agreements users accept before enabling
|
|
78
|
+
|
|
79
|
+
Some third-party terms require every user of the plugin to accept them, such as a runtime license that each user must hold. Name the terms in the manifest:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"agreement": {
|
|
84
|
+
"title": "Spine Runtimes License Agreement",
|
|
85
|
+
"file": "licenses/SPINE-RUNTIMES.txt"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
- `title` is 1–80 characters without control characters. `file` is a `.txt` or `.md` file in the package, at most 256 KiB of UTF-8; Markdown is shown as plain text. A package whose agreement file is missing, too large or not UTF-8 is invalid. One agreement per plugin.
|
|
91
|
+
- When the plugin is enabled in a library, the host's confirmation names the agreement and offers its full text; the plugin is enabled only when the user agrees. Declining leaves the plugin installed and not enabled. Development folders ask the same way.
|
|
92
|
+
- The library records the SHA-256 of the file's bytes with the grant. A version whose agreement file differs needs enabling again, like reading more formats; a version with a file the user already accepted, such as one rolled back to, does not.
|
|
93
|
+
- Use it only for terms that require acceptance by each user. The host shows and records the user's choice; neither the host nor your plugin can verify that the user meets the terms, so do not suggest otherwise.
|
|
94
|
+
|
|
76
95
|
## Runtime API
|
|
77
96
|
|
|
78
97
|
```ts
|
|
@@ -162,8 +181,9 @@ interface CompositionReport {
|
|
|
162
181
|
references: {
|
|
163
182
|
from?: string // Key of the referencing subresource; omitted means the file itself
|
|
164
183
|
uri?: string // Points to another file: the raw address written in the file
|
|
165
|
-
|
|
166
|
-
|
|
184
|
+
uris?: string[] // Several candidate files, the preferred first; only with uriKey
|
|
185
|
+
key?: string // Points to a subresource in this file; give exactly one of uri, uris and key
|
|
186
|
+
uriKey?: string // With uri or uris: a subresource of that file, by the key its report gives it
|
|
167
187
|
role: string // Display role, e.g. "Base color texture"
|
|
168
188
|
evidence?: string // Location in the file, e.g. "/images/2/uri"
|
|
169
189
|
inferred?: string // Not written in the file: how you inferred it, e.g. "Same name as the skeleton"
|
|
@@ -175,8 +195,8 @@ interface CompositionReport {
|
|
|
175
195
|
}
|
|
176
196
|
```
|
|
177
197
|
|
|
178
|
-
- The host validates reports with `compositionReportSchema` (`parseReport` also checks the size); plugins can check themselves against the same schema before returning. The report serialized as JSON is at most 4 MiB (counted in UTF-8 bytes). Names and keys are at most 200 characters, kinds and roles 80, bases of inference 120, evidence 300, diagnostics 500 and addresses 2,048. Names, kinds, roles, bases of inference and evidence must not contain control characters.
|
|
179
|
-
- The report is invalid if it has duplicate keys, a `parent`, `from` or `key` that points to a missing subresource, a cyclic hierarchy, a reference with
|
|
198
|
+
- The host validates reports with `compositionReportSchema` (`parseReport` also checks the size); plugins can check themselves against the same schema before returning. The report serialized as JSON is at most 4 MiB (counted in UTF-8 bytes). Names and keys are at most 200 characters, kinds and roles 80, bases of inference 120, evidence 300, diagnostics 500 and addresses 2,048; `uris` has 2–16 addresses. Names, kinds, roles, bases of inference and evidence must not contain control characters.
|
|
199
|
+
- The report is invalid if it has duplicate keys, a `parent`, `from` or `key` that points to a missing subresource, a cyclic hierarchy, a reference with other than exactly one of `uri`, `uris` and `key`, a `uriKey` with a `key`, `uris` without a `uriKey` or with a repeated address, or an `inferred` reference with a `key`. A `uriKey` is not checked against this report: it names a subresource of the other file.
|
|
180
200
|
- Write `uri` exactly as it appears in the file; do not resolve it yourself. The host decodes percent-encoding once, ignores everything after `?` and `#`, treats `\` as `/`, resolves it relative to the analyzed file and classifies it: a file in the linked folder, out of bounds (`..` leaving the linked folder), embedded (`data:`), external (absolute paths, drive letters, other schemes) or invalid (undecodable, containing control characters, or pointing to a folder). Whether the target exists is decided by the host at query time.
|
|
181
201
|
- For embedded data (`data:`), report only the header up to the comma, e.g. `data:image/png;base64`. The host classifies it as embedded by prefix and does not store the content; a full `data:` address often exceeds 2,048 characters and would invalidate the whole report.
|
|
182
202
|
- Reporting a reference does not require reading the target file, so you do not need `companionFormats` just to report references. Declare them only when the analysis actually reads companion files.
|
|
@@ -235,6 +255,34 @@ When a format looks an object up by name in another file, report the object itse
|
|
|
235
255
|
|
|
236
256
|
- Keys follow the limits of `key`: at most 200 characters. Do not put the key in the address: the host ignores everything after `?` and `#`.
|
|
237
257
|
|
|
258
|
+
### References that look in one of several files
|
|
259
|
+
|
|
260
|
+
Some formats look an object up by name in several files in turn. An OBJ file can name several material libraries, and `usemtl Body` uses the `Body` of the last library that defines it. Report one reference with every candidate file in `uris`, the one that wins first:
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
// mtllib base.mtl
|
|
264
|
+
// mtllib ../materials/paint.mtl
|
|
265
|
+
// usemtl Body
|
|
266
|
+
{
|
|
267
|
+
from: 'groups/0',
|
|
268
|
+
uris: ['../materials/paint.mtl', 'base.mtl'], // The last mtllib wins, so it comes first
|
|
269
|
+
uriKey: 'material:Body',
|
|
270
|
+
role: 'Material',
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
- Use `uris` only when there is more than one candidate; with one, give `uri` as before. The order is yours to get right: the host cannot tell which file the format would use.
|
|
275
|
+
- The host goes through the candidates in order, at query time, and resolves to the first whose report has the key. A candidate that does not exist is skipped. A candidate the host cannot settle stops it, and the reference is unknown with that candidate and the reason: never resolved to a later candidate while an earlier one might have the key.
|
|
276
|
+
|
|
277
|
+
| The candidates | Written reference | Inferred reference |
|
|
278
|
+
| -------------------------------------------------------------------------------------------------------- | ------------------------ | ------------------------ |
|
|
279
|
+
| One has the key, and every earlier one does not exist or has a complete report without it | Resolved to that one | Resolved to that one |
|
|
280
|
+
| None exists, or every existing one has a complete report without the key | Missing | Not found |
|
|
281
|
+
| An earlier one is ignored, offline, outside the linked folder, or has no complete report from your build | Unknown, with the reason | Unknown, with the reason |
|
|
282
|
+
|
|
283
|
+
- A missing reference counts once, however many candidates it has. The reference is listed as a referrer only of the file it resolves to; a candidate whose object is shadowed by an earlier one does not list it. Report the files themselves (each `mtllib`) as references of their own.
|
|
284
|
+
- As with `uri` and `uriKey`, every candidate must be analyzed by the same build of your plugin, and you do not read the candidates to check them.
|
|
285
|
+
|
|
238
286
|
## Focusing a subresource
|
|
239
287
|
|
|
240
288
|
When the user picks a subresource in the inspector, or jumps to a subresource of another file, the host asks the viewer to show it:
|
package/dist/bridge.d.ts
CHANGED
|
@@ -33,10 +33,18 @@ export interface ReportReference {
|
|
|
33
33
|
from?: string;
|
|
34
34
|
/** The address as written in the file, relative to the analyzed file. */
|
|
35
35
|
uri?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Files the target is looked up in, in priority order with the preferred first: 2–16 addresses as
|
|
38
|
+
* written in the file, relative to the analyzed file. Only with `uriKey`, instead of `uri`. The
|
|
39
|
+
* host resolves to the first candidate whose report has `uriKey`; while an earlier candidate is
|
|
40
|
+
* not settled, such as not yet analyzed, the reference is unknown.
|
|
41
|
+
*/
|
|
42
|
+
uris?: string[];
|
|
36
43
|
/** A subresource of the analyzed file. */
|
|
37
44
|
key?: string;
|
|
38
45
|
/**
|
|
39
|
-
* A subresource of the file at `uri`, by the key that file's
|
|
46
|
+
* A subresource of the file at `uri`, or of one of the files in `uris`, by the key that file's
|
|
47
|
+
* report gives it; required with `uris`.
|
|
40
48
|
* The host connects it when the same build of your plugin analyzed that file.
|
|
41
49
|
*/
|
|
42
50
|
uriKey?: string;
|
|
@@ -46,7 +54,8 @@ export interface ReportReference {
|
|
|
46
54
|
evidence?: string;
|
|
47
55
|
/**
|
|
48
56
|
* The reference is not written in the file: how the analyzer inferred it, shown to the user, such
|
|
49
|
-
* as "Same name as the skeleton". Only with `uri`. A missing target is "not found", not
|
|
57
|
+
* as "Same name as the skeleton". Only with `uri` or `uris`. A missing target is "not found", not
|
|
58
|
+
* missing.
|
|
50
59
|
*/
|
|
51
60
|
inferred?: string;
|
|
52
61
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { contributionsOf, manifestSchema, missingEntries, parseManifest, permissionSummary, pluginApiVersion, type Contribution, type ContributionKind, type ManifestResult, type PluginManifest, } from './manifest.ts';
|
|
1
|
+
export { agreementMaxBytes, contributionsOf, manifestSchema, missingEntries, parseManifest, permissionSummary, pluginApiVersion, type Contribution, type ContributionKind, type ManifestResult, type PluginAgreement, type PluginManifest, } from './manifest.ts';
|
|
2
2
|
export { checkPackageFiles, checkPackagePath, packageExtensions, packageLimits, type PackageFile, } from './package-rules.ts';
|
|
3
3
|
export { compositionReportSchema, parseReport, reportLimits, type ReportResult } from './report.ts';
|
|
4
4
|
export type { ReportDiagnostic, ReportReference, ReportSubresource } from './bridge.ts';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { contributionsOf, manifestSchema, missingEntries, parseManifest, permissionSummary, pluginApiVersion, } from "./manifest.js";
|
|
1
|
+
export { agreementMaxBytes, contributionsOf, manifestSchema, missingEntries, parseManifest, permissionSummary, pluginApiVersion, } from "./manifest.js";
|
|
2
2
|
export { checkPackageFiles, checkPackagePath, packageExtensions, packageLimits, } from "./package-rules.js";
|
|
3
3
|
export { compositionReportSchema, parseReport, reportLimits } from "./report.js";
|
|
4
4
|
export { analyzer, bridge, isNotApplicable, notApplicable, readCompanion, readInput, thumbnail, viewer, } from "./runtime.js";
|
package/dist/manifest.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
/** The plugin contract version this SDK describes. Hosts load only plugins with this version. */
|
|
3
|
-
export declare const pluginApiVersion =
|
|
3
|
+
export declare const pluginApiVersion = 5;
|
|
4
4
|
declare const contributionSchema: z.ZodObject<{
|
|
5
5
|
id: z.ZodString;
|
|
6
6
|
entry: z.ZodString;
|
|
@@ -8,16 +8,32 @@ declare const contributionSchema: z.ZodObject<{
|
|
|
8
8
|
companionFormats: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
9
9
|
}, z.core.$strict>;
|
|
10
10
|
export type Contribution = z.output<typeof contributionSchema>;
|
|
11
|
+
/** The largest agreement file, in bytes. */
|
|
12
|
+
export declare const agreementMaxBytes: number;
|
|
13
|
+
/**
|
|
14
|
+
* Terms a third party requires every user to accept before using the plugin, such as a runtime
|
|
15
|
+
* license. The host shows the file when the plugin is enabled and runs the plugin only once the user
|
|
16
|
+
* agrees, per library; a changed file asks again. Nothing is verified.
|
|
17
|
+
*/
|
|
18
|
+
declare const agreementSchema: z.ZodObject<{
|
|
19
|
+
title: z.ZodString;
|
|
20
|
+
file: z.ZodString;
|
|
21
|
+
}, z.core.$strict>;
|
|
22
|
+
export type PluginAgreement = z.output<typeof agreementSchema>;
|
|
11
23
|
/** `publisher.name`. The `vormio` publisher is reserved for the host. */
|
|
12
24
|
export declare const pluginIdSchema: z.ZodString;
|
|
13
25
|
/** A semantic version without build metadata. */
|
|
14
26
|
export declare const versionSchema: z.ZodString;
|
|
15
27
|
export declare const manifestSchema: z.ZodObject<{
|
|
16
|
-
apiVersion: z.ZodLiteral<
|
|
28
|
+
apiVersion: z.ZodLiteral<5>;
|
|
17
29
|
id: z.ZodString;
|
|
18
30
|
version: z.ZodString;
|
|
19
31
|
name: z.ZodString;
|
|
20
32
|
description: z.ZodDefault<z.ZodString>;
|
|
33
|
+
agreement: z.ZodOptional<z.ZodObject<{
|
|
34
|
+
title: z.ZodString;
|
|
35
|
+
file: z.ZodString;
|
|
36
|
+
}, z.core.$strict>>;
|
|
21
37
|
contributions: z.ZodObject<{
|
|
22
38
|
thumbnails: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
23
39
|
id: z.ZodString;
|
package/dist/manifest.js
CHANGED
|
Binary file
|
package/dist/node/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { checkPackage, PackageError, readPackageArchive, readPackageDirectory, type CheckedPackage, } from './package.ts';
|
|
1
|
+
export { checkPackage, PackageError, type CheckedAgreement, readPackageArchive, readPackageDirectory, type CheckedPackage, } from './package.ts';
|
|
2
2
|
export { readZip, writeZip, ZipError, type ZipEntry } from './zip.ts';
|
|
3
3
|
export { CatalogError, catalogKeyId, catalogLimits, catalogPublicKey, compareVersions, generateCatalogKeys, releaseManifest, signCatalog, verifyCatalog, type Catalog, type CatalogErrorCode, type CatalogKeys, type CatalogPayload, type CatalogPlugin, type CatalogRelease, } from './catalog.ts';
|
|
4
4
|
export { PublishError, writeCatalog, writeCatalogKeys, type WrittenCatalog, type WrittenKeys, } from './publish.ts';
|
|
5
|
-
export { contributionsOf, manifestSchema, missingEntries, parseManifest, permissionSummary, pluginApiVersion, type Contribution, type ContributionKind, type PluginManifest, } from '../manifest.ts';
|
|
5
|
+
export { agreementMaxBytes, contributionsOf, manifestSchema, missingEntries, parseManifest, permissionSummary, pluginApiVersion, type Contribution, type ContributionKind, type PluginAgreement, type PluginManifest, } from '../manifest.ts';
|
|
6
6
|
export { checkPackageFiles, checkPackagePath, packageLimits } from '../package-rules.ts';
|
|
7
7
|
export { compositionReportSchema, parseReport, reportLimits } from '../report.ts';
|
|
8
8
|
export type { CompositionReport } from '../bridge.ts';
|
package/dist/node/index.js
CHANGED
|
@@ -3,6 +3,6 @@ export { readZip, writeZip, ZipError } from "./zip.js";
|
|
|
3
3
|
export { CatalogError, catalogKeyId, catalogLimits, catalogPublicKey, compareVersions, generateCatalogKeys, releaseManifest, signCatalog, verifyCatalog, } from "./catalog.js";
|
|
4
4
|
export { PublishError, writeCatalog, writeCatalogKeys, } from "./publish.js";
|
|
5
5
|
// The manifest and package rules again, so Node code needs no DOM types from the runtime entry.
|
|
6
|
-
export { contributionsOf, manifestSchema, missingEntries, parseManifest, permissionSummary, pluginApiVersion, } from "../manifest.js";
|
|
6
|
+
export { agreementMaxBytes, contributionsOf, manifestSchema, missingEntries, parseManifest, permissionSummary, pluginApiVersion, } from "../manifest.js";
|
|
7
7
|
export { checkPackageFiles, checkPackagePath, packageLimits } from "../package-rules.js";
|
|
8
8
|
export { compositionReportSchema, parseReport, reportLimits } from "../report.js";
|
package/dist/node/package.d.ts
CHANGED
|
@@ -10,6 +10,14 @@ export interface CheckedPackage {
|
|
|
10
10
|
files: PackageFile[];
|
|
11
11
|
/** SHA-256 over every file's path and content, in path order. */
|
|
12
12
|
digest: string;
|
|
13
|
+
/** The agreement the manifest names, with its text and the SHA-256 of its bytes. */
|
|
14
|
+
agreement: CheckedAgreement | null;
|
|
15
|
+
}
|
|
16
|
+
export interface CheckedAgreement {
|
|
17
|
+
title: string;
|
|
18
|
+
file: string;
|
|
19
|
+
text: string;
|
|
20
|
+
sha256: string;
|
|
13
21
|
}
|
|
14
22
|
/** Validates file contents given as path → bytes (from a zip or a directory). */
|
|
15
23
|
export declare function checkPackage(contents: ReadonlyMap<string, Buffer>): CheckedPackage;
|
package/dist/node/package.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { lstat, readdir, readFile } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
import { missingEntries, parseManifest } from "../manifest.js";
|
|
4
|
+
import { agreementMaxBytes, missingEntries, parseManifest, } from "../manifest.js";
|
|
5
5
|
import { checkPackageFiles, packageLimits } from "../package-rules.js";
|
|
6
6
|
import { readZip } from "./zip.js";
|
|
7
7
|
export class PackageError extends Error {
|
|
@@ -12,6 +12,25 @@ export class PackageError extends Error {
|
|
|
12
12
|
this.name = 'PackageError';
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
+
/** Reads the agreement file the manifest names: present, at most 256 KiB, and valid UTF-8. */
|
|
16
|
+
function checkAgreement(manifest, contents) {
|
|
17
|
+
if (!manifest.agreement)
|
|
18
|
+
return null;
|
|
19
|
+
const { title, file } = manifest.agreement;
|
|
20
|
+
const data = contents.get(file);
|
|
21
|
+
if (!data)
|
|
22
|
+
throw new PackageError([`${file}: agreement not found`]);
|
|
23
|
+
if (data.length > agreementMaxBytes)
|
|
24
|
+
throw new PackageError([`${file}: agreement larger than 256 KiB`]);
|
|
25
|
+
let text;
|
|
26
|
+
try {
|
|
27
|
+
text = new TextDecoder('utf-8', { fatal: true }).decode(data);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
throw new PackageError([`${file}: agreement is not UTF-8`]);
|
|
31
|
+
}
|
|
32
|
+
return { title, file, text, sha256: createHash('sha256').update(data).digest('hex') };
|
|
33
|
+
}
|
|
15
34
|
/** Validates file contents given as path → bytes (from a zip or a directory). */
|
|
16
35
|
export function checkPackage(contents) {
|
|
17
36
|
const files = [...contents].map(([path, data]) => ({ path, size: data.length }));
|
|
@@ -32,6 +51,7 @@ export function checkPackage(contents) {
|
|
|
32
51
|
const missing = missingEntries(parsed.manifest, new Set(contents.keys()));
|
|
33
52
|
if (missing.length)
|
|
34
53
|
throw new PackageError(missing.map((entry) => `${entry}: entry not found`));
|
|
54
|
+
const agreement = checkAgreement(parsed.manifest, contents);
|
|
35
55
|
const digest = createHash('sha256');
|
|
36
56
|
for (const path of [...contents.keys()].sort()) {
|
|
37
57
|
digest.update(path).update('\0');
|
|
@@ -41,6 +61,7 @@ export function checkPackage(contents) {
|
|
|
41
61
|
manifest: parsed.manifest,
|
|
42
62
|
files: files.sort((a, b) => (a.path < b.path ? -1 : 1)),
|
|
43
63
|
digest: digest.digest('hex'),
|
|
64
|
+
agreement,
|
|
44
65
|
};
|
|
45
66
|
}
|
|
46
67
|
/**
|
package/dist/report.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ import type { CompositionReport } from './bridge.ts';
|
|
|
4
4
|
export declare const reportLimits: {
|
|
5
5
|
readonly subresources: 10000;
|
|
6
6
|
readonly references: 50000;
|
|
7
|
+
/** Candidate files of one reference (`uris`). */
|
|
8
|
+
readonly candidates: 16;
|
|
7
9
|
readonly diagnostics: 500;
|
|
8
10
|
/** Serialized size: UTF-8 bytes of the report as JSON. */
|
|
9
11
|
readonly bytes: number;
|
package/dist/report.js
CHANGED
|
@@ -3,6 +3,8 @@ import { z } from 'zod';
|
|
|
3
3
|
export const reportLimits = {
|
|
4
4
|
subresources: 10_000,
|
|
5
5
|
references: 50_000,
|
|
6
|
+
/** Candidate files of one reference (`uris`). */
|
|
7
|
+
candidates: 16,
|
|
6
8
|
diagnostics: 500,
|
|
7
9
|
/** Serialized size: UTF-8 bytes of the report as JSON. */
|
|
8
10
|
bytes: 4 * 1024 * 1024,
|
|
@@ -14,6 +16,7 @@ const plain = (max) => z
|
|
|
14
16
|
.max(max)
|
|
15
17
|
.refine((text) => !controlCharacters.test(text), 'Control characters are not allowed');
|
|
16
18
|
const key = z.string().min(1).max(200);
|
|
19
|
+
const uri = z.string().min(1).max(2048);
|
|
17
20
|
const subresourceSchema = z.strictObject({
|
|
18
21
|
key,
|
|
19
22
|
kind: plain(80).min(1),
|
|
@@ -23,21 +26,29 @@ const subresourceSchema = z.strictObject({
|
|
|
23
26
|
const referenceSchema = z
|
|
24
27
|
.strictObject({
|
|
25
28
|
from: key.optional(),
|
|
26
|
-
uri:
|
|
29
|
+
uri: uri.optional(),
|
|
30
|
+
uris: z
|
|
31
|
+
.array(uri)
|
|
32
|
+
.min(2)
|
|
33
|
+
.max(reportLimits.candidates)
|
|
34
|
+
.refine((uris) => new Set(uris).size === uris.length, 'Candidates must not repeat')
|
|
35
|
+
.optional(),
|
|
27
36
|
key: key.optional(),
|
|
28
37
|
uriKey: key.optional(),
|
|
29
38
|
role: plain(80).min(1),
|
|
30
39
|
evidence: plain(300).optional(),
|
|
31
40
|
inferred: plain(120).min(1).optional(),
|
|
32
41
|
})
|
|
33
|
-
.refine((reference) =>
|
|
34
|
-
message: 'Give exactly one of uri and key'
|
|
42
|
+
.refine((reference) => [reference.uri, reference.uris, reference.key].filter((each) => each !== undefined).length ===
|
|
43
|
+
1, { message: 'Give exactly one of uri, uris and key' })
|
|
44
|
+
.refine((reference) => reference.inferred === undefined || reference.key === undefined, {
|
|
45
|
+
message: 'Only a reference with a uri or uris can be inferred',
|
|
35
46
|
})
|
|
36
|
-
.refine((reference) => reference.
|
|
37
|
-
message: '
|
|
47
|
+
.refine((reference) => reference.uriKey === undefined || reference.key === undefined, {
|
|
48
|
+
message: 'Give uriKey only with uri or uris',
|
|
38
49
|
})
|
|
39
|
-
.refine((reference) => reference.
|
|
40
|
-
message: 'Give uriKey
|
|
50
|
+
.refine((reference) => reference.uris === undefined || reference.uriKey !== undefined, {
|
|
51
|
+
message: 'Give uriKey with uris',
|
|
41
52
|
});
|
|
42
53
|
const diagnosticSchema = z.strictObject({
|
|
43
54
|
severity: z.enum(['error', 'warning', 'info']),
|