yarramate 1.15.2 → 1.15.3
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/adapter-mapping.js +3 -3
- package/dist/apply-command.js +3 -6
- package/dist/compiler.js +12 -12
- package/dist/core-contract.js +3 -3
- package/dist/evidence.js +3 -3
- package/dist/interrogate-command.js +3 -3
- package/dist/projection.js +3 -3
- package/dist/schema-validation.d.ts +9 -27
- package/dist/schema-validation.js +40 -26
- package/dist/visual-app-lib/editor.js +27347 -27330
- package/dist/visual-app-lib/types/schema-validation.d.ts +9 -27
- package/dist/workspace.js +3 -3
- package/package.json +1 -1
package/dist/adapter-mapping.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Ajv2020Module from 'ajv/dist/2020.js';
|
|
2
2
|
import { diagnosticOrder, loadSourceDocument, locateSourcePath, } from './source-document.js';
|
|
3
|
-
import {
|
|
3
|
+
import { compileValidator } from './schema-validation.js';
|
|
4
4
|
import adapterMappingSchema from '../schema/yarramate-adapter-mapping.schema.json' with { type: 'json'
|
|
5
5
|
};
|
|
6
6
|
// `.default ?? module`, not a bare `.default`: NodeNext sees the raw CJS
|
|
@@ -9,10 +9,10 @@ import adapterMappingSchema from '../schema/yarramate-adapter-mapping.schema.jso
|
|
|
9
9
|
// to keep track of (#252).
|
|
10
10
|
const ajv2020Module = Ajv2020Module;
|
|
11
11
|
const Ajv2020 = ajv2020Module.default ?? ajv2020Module;
|
|
12
|
-
const validateSchema =
|
|
12
|
+
const validateSchema = compileValidator(adapterMappingSchema);
|
|
13
13
|
const mappingLocations = new WeakMap();
|
|
14
14
|
export function loadAdapterMapping(source) {
|
|
15
|
-
const loaded = loadSourceDocument(source, validateSchema
|
|
15
|
+
const loaded = loadSourceDocument(source, validateSchema, 'Adapter mapping');
|
|
16
16
|
if (!loaded.ok)
|
|
17
17
|
return loaded;
|
|
18
18
|
const { value, yaml, lineCounter } = loaded.document;
|
package/dist/apply-command.js
CHANGED
|
@@ -22,7 +22,7 @@ export const posixDirectoryOf = (path) => {
|
|
|
22
22
|
};
|
|
23
23
|
import operationsSchema from '../schema/yarramate-operations.schema.json' with { type: 'json'
|
|
24
24
|
};
|
|
25
|
-
import {
|
|
25
|
+
import { compileValidatorWith } from './schema-validation.js';
|
|
26
26
|
// `.default ?? module`, not a bare `.default`: NodeNext sees the raw CJS
|
|
27
27
|
// `module.exports` and a bundler sees the unwrapped class, and this file is
|
|
28
28
|
// reachable from a browser through `./apply-operations.js` (#252).
|
|
@@ -32,10 +32,7 @@ const Ajv2020 = ajv2020Module.default ?? ajv2020Module;
|
|
|
32
32
|
// one malformed operation reports one fault instead of ten near-misses.
|
|
33
33
|
// Keeps its own Ajv instance: `discriminator` changes how a schema compiles,
|
|
34
34
|
// so it cannot share one with the nine that do not set it.
|
|
35
|
-
const validateOperations =
|
|
36
|
-
allErrors: true,
|
|
37
|
-
discriminator: true,
|
|
38
|
-
}).compile(operationsSchema));
|
|
35
|
+
const validateOperations = compileValidatorWith({ allErrors: true, discriminator: true }, operationsSchema);
|
|
39
36
|
// Scalar fields replace; list fields append; `remove` retracts (ADR 0062).
|
|
40
37
|
// An answer enriches what is there and may explicitly take back what it
|
|
41
38
|
// asserted — it never silently shrinks anything.
|
|
@@ -313,7 +310,7 @@ export const applyOperations = (input) => {
|
|
|
313
310
|
ok: false,
|
|
314
311
|
diagnostics,
|
|
315
312
|
});
|
|
316
|
-
const loadedOperations = loadSourceDocument(operations, validateOperations
|
|
313
|
+
const loadedOperations = loadSourceDocument(operations, validateOperations, 'Operations');
|
|
317
314
|
if (!loadedOperations.ok)
|
|
318
315
|
return failed(loadedOperations.diagnostics);
|
|
319
316
|
const operationList = loadedOperations.document.value.operations;
|
package/dist/compiler.js
CHANGED
|
@@ -11,13 +11,13 @@ import patternSchema from '../schema/yarramate-pattern.schema.json' with { type:
|
|
|
11
11
|
};
|
|
12
12
|
import { ATTESTATION_PREDICATE_PREFIX, attestationClaimValue } from './graph-claims.js';
|
|
13
13
|
import { shippedPolicyIdentity, shippedPolicySource, } from './shipped-profile.js';
|
|
14
|
-
import {
|
|
14
|
+
import { compileValidator } from './schema-validation.js';
|
|
15
15
|
const coreProfile = 'yarramate/core@0.1';
|
|
16
16
|
const ajv2020Module = Ajv2020Import;
|
|
17
17
|
const Ajv2020 = ajv2020Module.default ?? ajv2020Module;
|
|
18
|
-
const validateDocument =
|
|
19
|
-
const validateProfile =
|
|
20
|
-
const validatePattern =
|
|
18
|
+
const validateDocument = compileValidator(documentSchema);
|
|
19
|
+
const validateProfile = compileValidator(profileSchema);
|
|
20
|
+
const validatePattern = compileValidator(patternSchema);
|
|
21
21
|
const immutableMap = (entries) => {
|
|
22
22
|
const backing = new Map(entries);
|
|
23
23
|
const facade = {
|
|
@@ -161,12 +161,12 @@ const parseWorkspaceSource = (input) => {
|
|
|
161
161
|
fresh,
|
|
162
162
|
};
|
|
163
163
|
}
|
|
164
|
-
const valid = parseDiagnostics.length === 0 && validateDocument(
|
|
164
|
+
const valid = parseDiagnostics.length === 0 && validateDocument(value);
|
|
165
165
|
const schemaDiagnostics = parseDiagnostics.length > 0
|
|
166
166
|
? parseDiagnostics
|
|
167
167
|
: valid
|
|
168
168
|
? []
|
|
169
|
-
: (validateDocument
|
|
169
|
+
: (validateDocument.errors ?? []).map((error) => {
|
|
170
170
|
const property = error.keyword === 'additionalProperties'
|
|
171
171
|
? String(error.params.additionalProperty)
|
|
172
172
|
: undefined;
|
|
@@ -331,8 +331,8 @@ function compileWorkspaceResolved(parsed) {
|
|
|
331
331
|
profileDiagnostics.push(...entry.schemaDiagnostics);
|
|
332
332
|
continue;
|
|
333
333
|
}
|
|
334
|
-
if (!validateProfile(
|
|
335
|
-
for (const error of validateProfile
|
|
334
|
+
if (!validateProfile(value)) {
|
|
335
|
+
for (const error of validateProfile.errors ?? []) {
|
|
336
336
|
const property = error.keyword === 'additionalProperties'
|
|
337
337
|
? String(error.params.additionalProperty)
|
|
338
338
|
: undefined;
|
|
@@ -391,8 +391,8 @@ function compileWorkspaceResolved(parsed) {
|
|
|
391
391
|
if (entry.schemaDiagnostics.length > 0) {
|
|
392
392
|
profileDiagnostics.push(...entry.schemaDiagnostics);
|
|
393
393
|
}
|
|
394
|
-
else if (!validateProfile(
|
|
395
|
-
for (const error of validateProfile
|
|
394
|
+
else if (!validateProfile(value)) {
|
|
395
|
+
for (const error of validateProfile.errors ?? []) {
|
|
396
396
|
profileDiagnostics.push({
|
|
397
397
|
severity: 'error',
|
|
398
398
|
code: 'YM201',
|
|
@@ -641,8 +641,8 @@ function compileWorkspaceResolved(parsed) {
|
|
|
641
641
|
patternDiagnostics.push(...entry.schemaDiagnostics);
|
|
642
642
|
continue;
|
|
643
643
|
}
|
|
644
|
-
if (!validatePattern(
|
|
645
|
-
for (const error of validatePattern
|
|
644
|
+
if (!validatePattern(value)) {
|
|
645
|
+
for (const error of validatePattern.errors ?? []) {
|
|
646
646
|
const property = error.keyword === 'additionalProperties'
|
|
647
647
|
? String(error.params.additionalProperty)
|
|
648
648
|
: undefined;
|
package/dist/core-contract.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Ajv2020Module from 'ajv/dist/2020.js';
|
|
2
2
|
import { LineCounter, parseDocument } from 'yaml';
|
|
3
3
|
import { diagnosticOrder, loadSourceDocument, locateSourcePath, } from './source-document.js';
|
|
4
|
-
import {
|
|
4
|
+
import { compileValidator } from './schema-validation.js';
|
|
5
5
|
import coreContractSchema from '../schema/yarramate-core-contract.schema.json' with { type: 'json'
|
|
6
6
|
};
|
|
7
7
|
// `.default ?? module`, not a bare `.default`: NodeNext sees the raw CJS
|
|
@@ -10,9 +10,9 @@ import coreContractSchema from '../schema/yarramate-core-contract.schema.json' w
|
|
|
10
10
|
// to keep track of (#252).
|
|
11
11
|
const ajv2020Module = Ajv2020Module;
|
|
12
12
|
const Ajv2020 = ajv2020Module.default ?? ajv2020Module;
|
|
13
|
-
const validateCoreContract =
|
|
13
|
+
const validateCoreContract = compileValidator(coreContractSchema);
|
|
14
14
|
export function loadCoreContract(source) {
|
|
15
|
-
const loaded = loadSourceDocument(source, validateCoreContract
|
|
15
|
+
const loaded = loadSourceDocument(source, validateCoreContract, 'Core contract');
|
|
16
16
|
if (!loaded.ok)
|
|
17
17
|
return loaded;
|
|
18
18
|
const { value, yaml, lineCounter } = loaded.document;
|
package/dist/evidence.js
CHANGED
|
@@ -8,12 +8,12 @@ import evidenceSchema from '../schema/yarramate-evidence.schema.json' with { typ
|
|
|
8
8
|
// to keep track of (#252).
|
|
9
9
|
const ajv2020Module = Ajv2020Module;
|
|
10
10
|
const Ajv2020 = ajv2020Module.default ?? ajv2020Module;
|
|
11
|
-
import {
|
|
12
|
-
const validateEvidenceSchema =
|
|
11
|
+
import { compileValidator } from './schema-validation.js';
|
|
12
|
+
const validateEvidenceSchema = compileValidator(evidenceSchema);
|
|
13
13
|
const evidenceLocations = new WeakMap();
|
|
14
14
|
const observationTarget = (observation) => 'subject' in observation ? observation.subject : observation.claim;
|
|
15
15
|
export function loadEvidence(source) {
|
|
16
|
-
const loaded = loadSourceDocument(source, validateEvidenceSchema
|
|
16
|
+
const loaded = loadSourceDocument(source, validateEvidenceSchema, 'Evidence');
|
|
17
17
|
if (!loaded.ok)
|
|
18
18
|
return loaded;
|
|
19
19
|
const { value, yaml, lineCounter } = loaded.document;
|
|
@@ -10,8 +10,8 @@ import catalogueSchema from '../schema/yarramate-question-catalogue.schema.json'
|
|
|
10
10
|
// to keep track of (#252).
|
|
11
11
|
const ajv2020Module = Ajv2020Module;
|
|
12
12
|
const Ajv2020 = ajv2020Module.default ?? ajv2020Module;
|
|
13
|
-
import {
|
|
14
|
-
const validateCatalogue =
|
|
13
|
+
import { compileValidator } from './schema-validation.js';
|
|
14
|
+
const validateCatalogue = compileValidator(catalogueSchema);
|
|
15
15
|
/**
|
|
16
16
|
* The version of condition evaluation itself, not of the package.
|
|
17
17
|
*
|
|
@@ -917,7 +917,7 @@ const unauthorableOffers = (catalogue, profileContext) => {
|
|
|
917
917
|
return found;
|
|
918
918
|
};
|
|
919
919
|
const loadCatalogueDocument = (catalogueSource) => {
|
|
920
|
-
const loaded = loadSourceDocument(catalogueSource, validateCatalogue
|
|
920
|
+
const loaded = loadSourceDocument(catalogueSource, validateCatalogue, 'Question catalogue');
|
|
921
921
|
if (!loaded.ok)
|
|
922
922
|
return { ok: false, diagnostics: loaded.diagnostics };
|
|
923
923
|
return {
|
package/dist/projection.js
CHANGED
|
@@ -6,7 +6,7 @@ import projectionSchema from '../schema/yarramate-projection.schema.json' with {
|
|
|
6
6
|
};
|
|
7
7
|
const ajv2020Module = Ajv2020Import;
|
|
8
8
|
const Ajv2020 = ajv2020Module.default ?? ajv2020Module;
|
|
9
|
-
const validateProjection =
|
|
9
|
+
const validateProjection = compileValidator(projectionSchema);
|
|
10
10
|
/**
|
|
11
11
|
* A relationship kind a view may draw as nesting, and the default. Defined in
|
|
12
12
|
* `./nesting.js`, which imports nothing, and re-exported here so a consumer
|
|
@@ -20,9 +20,9 @@ export { DEFAULT_NESTING } from './nesting.js';
|
|
|
20
20
|
* nesting vocabulary above, and re-exported here on the same terms (ADR 0121).
|
|
21
21
|
*/
|
|
22
22
|
export { DEFAULT_DIRECTION } from './layout-direction.js';
|
|
23
|
-
import {
|
|
23
|
+
import { compileValidator } from './schema-validation.js';
|
|
24
24
|
export function loadProjection(source) {
|
|
25
|
-
const loaded = loadSourceDocument(source, validateProjection
|
|
25
|
+
const loaded = loadSourceDocument(source, validateProjection, 'Projection');
|
|
26
26
|
return loaded.ok
|
|
27
27
|
? { ok: true, projection: loaded.document.value }
|
|
28
28
|
: loaded;
|
|
@@ -1,30 +1,12 @@
|
|
|
1
|
+
import Ajv2020Module from 'ajv/dist/2020.js';
|
|
1
2
|
import type { ValidateFunction } from 'ajv';
|
|
3
|
+
declare const Ajv2020: typeof Ajv2020Module & typeof Ajv2020Module.Ajv2020;
|
|
4
|
+
/** Compiles on the shared `allErrors` instance. */
|
|
5
|
+
export declare const compileValidator: <T = unknown>(schema: object) => ValidateFunction<T>;
|
|
2
6
|
/**
|
|
3
|
-
*
|
|
4
|
-
* it
|
|
5
|
-
*
|
|
6
|
-
* Ten validators used to be constructed and compiled at MODULE SCOPE, so
|
|
7
|
-
* importing the package paid for every one of them whether or not a caller
|
|
8
|
-
* ever validated anything. Measured on the published 1.15.1 dist: ten
|
|
9
|
-
* `Ajv.compile` calls costing **123.7ms, 80% of the barrel's entire import
|
|
10
|
-
* time**, and one more on the `yarramate/interrogation` subpath, which is the
|
|
11
|
-
* entry we tell Workers consumers to prefer. That is not an abstract cost:
|
|
12
|
-
* Cloudflare Workers budget STARTUP CPU separately from request CPU and refuse
|
|
13
|
-
* a Worker that exceeds it, so an adopter's deploy was rejected outright
|
|
14
|
-
* (error 10021) by work no request had asked for.
|
|
15
|
-
*
|
|
16
|
-
* Deferring moves that cost to first use, where the budget is seconds rather
|
|
17
|
-
* than milliseconds, and a caller pays only for the schemas it actually
|
|
18
|
-
* touches - a consumer that compiles a workspace no longer pays for the
|
|
19
|
-
* evidence, adapter-mapping and core-contract validators it never calls.
|
|
20
|
-
*
|
|
21
|
-
* The accessor is a function rather than a getter so the deferral is visible
|
|
22
|
-
* at every call site: `validateDocument()(value)` reads as "get the validator,
|
|
23
|
-
* then use it", and `validateDocument().errors` cannot accidentally be read
|
|
24
|
-
* off a validator that was never run. Ajv attaches `errors` to the validator
|
|
25
|
-
* itself, so the two must come from the same object.
|
|
26
|
-
*
|
|
27
|
-
* `test/schema-validation-laziness.test.ts` asserts that importing the package
|
|
28
|
-
* compiles NOTHING, and fails on the next module-scope validator anyone adds.
|
|
7
|
+
* For a schema needing options the shared instance does not carry. Only
|
|
8
|
+
* `apply-command` uses it: `discriminator` changes how a schema compiles, so
|
|
9
|
+
* it cannot ride on an instance that does not set it.
|
|
29
10
|
*/
|
|
30
|
-
export declare const
|
|
11
|
+
export declare const compileValidatorWith: <T = unknown>(options: ConstructorParameters<typeof Ajv2020>[0], schema: object) => ValidateFunction<T>;
|
|
12
|
+
export {};
|
|
@@ -1,32 +1,46 @@
|
|
|
1
|
+
import Ajv2020Module from 'ajv/dist/2020.js';
|
|
2
|
+
// The published dual-export dance every other module here does; see the
|
|
3
|
+
// comment in `compiler.ts` for why (#252).
|
|
4
|
+
const ajv2020Module = Ajv2020Module;
|
|
5
|
+
const Ajv2020 = ajv2020Module.default ?? ajv2020Module;
|
|
1
6
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
7
|
+
* Compiles a JSON Schema AT MODULE SCOPE, on an instance shared with every
|
|
8
|
+
* other schema that wants the same options.
|
|
4
9
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* time**, and one more on the `yarramate/interrogation` subpath, which is the
|
|
10
|
-
* entry we tell Workers consumers to prefer. That is not an abstract cost:
|
|
11
|
-
* Cloudflare Workers budget STARTUP CPU separately from request CPU and refuse
|
|
12
|
-
* a Worker that exceeds it, so an adopter's deploy was rejected outright
|
|
13
|
-
* (error 10021) by work no request had asked for.
|
|
10
|
+
* **Compilation must happen at import, and this is not a preference.** Ajv
|
|
11
|
+
* compiles a schema by generating source and calling `new Function`.
|
|
12
|
+
* Cloudflare's `workerd` permits code generation during module evaluation and
|
|
13
|
+
* FORBIDS it at request time - measured on the real runtime binary:
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* ```
|
|
16
|
+
* {"atStartup":"ALLOWED",
|
|
17
|
+
* "atRequest":"EvalError: Code generation from strings disallowed for this context"}
|
|
18
|
+
* ```
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
20
|
+
* 1.15.2 deferred these compiles to first use to keep startup CPU down, which
|
|
21
|
+
* moved them into the one context that cannot run them: every request that
|
|
22
|
+
* validated anything threw. The startup budget was real, but a package that
|
|
23
|
+
* cannot serve a request is not a fix for it. Deferral is therefore not an
|
|
24
|
+
* option here, and `test/schema-validation-eagerness.test.ts` asserts the
|
|
25
|
+
* opposite of what its predecessor did - that importing the package compiles
|
|
26
|
+
* every validator, so no request has to.
|
|
25
27
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
+
* The cost is paid down by SHARING one Ajv instance rather than building ten.
|
|
29
|
+
* Each instance compiles the 2020-12 meta-schema again, and that dominates:
|
|
30
|
+
* ten instances cost 81.6ms against 35.0ms for one, a 57% reduction with no
|
|
31
|
+
* change to when anything runs. Sharing is safe here because no schema
|
|
32
|
+
* registers formats and all 39 ship distinct `$id`s, so nothing collides.
|
|
33
|
+
*
|
|
34
|
+
* The real ceiling-lifter is precompiled standalone validators (Ajv's
|
|
35
|
+
* `code: { source: true }`), which remove Ajv from the runtime altogether and
|
|
36
|
+
* satisfy both constraints at once. This is the shape that is correct today.
|
|
37
|
+
*/
|
|
38
|
+
const sharedAjv = new Ajv2020({ allErrors: true });
|
|
39
|
+
/** Compiles on the shared `allErrors` instance. */
|
|
40
|
+
export const compileValidator = (schema) => sharedAjv.compile(schema);
|
|
41
|
+
/**
|
|
42
|
+
* For a schema needing options the shared instance does not carry. Only
|
|
43
|
+
* `apply-command` uses it: `discriminator` changes how a schema compiles, so
|
|
44
|
+
* it cannot ride on an instance that does not set it.
|
|
28
45
|
*/
|
|
29
|
-
export const
|
|
30
|
-
let compiled;
|
|
31
|
-
return () => (compiled ??= compile());
|
|
32
|
-
};
|
|
46
|
+
export const compileValidatorWith = (options, schema) => new Ajv2020(options).compile(schema);
|