yarramate 1.15.1 → 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.
@@ -1,5 +1,6 @@
1
1
  import Ajv2020Module from 'ajv/dist/2020.js';
2
2
  import { diagnosticOrder, loadSourceDocument, locateSourcePath, } from './source-document.js';
3
+ import { compileValidator } from './schema-validation.js';
3
4
  import adapterMappingSchema from '../schema/yarramate-adapter-mapping.schema.json' with { type: 'json'
4
5
  };
5
6
  // `.default ?? module`, not a bare `.default`: NodeNext sees the raw CJS
@@ -8,7 +9,7 @@ import adapterMappingSchema from '../schema/yarramate-adapter-mapping.schema.jso
8
9
  // to keep track of (#252).
9
10
  const ajv2020Module = Ajv2020Module;
10
11
  const Ajv2020 = ajv2020Module.default ?? ajv2020Module;
11
- const validateSchema = new Ajv2020({ allErrors: true }).compile(adapterMappingSchema);
12
+ const validateSchema = compileValidator(adapterMappingSchema);
12
13
  const mappingLocations = new WeakMap();
13
14
  export function loadAdapterMapping(source) {
14
15
  const loaded = loadSourceDocument(source, validateSchema, 'Adapter mapping');
@@ -22,6 +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 { compileValidatorWith } from './schema-validation.js';
25
26
  // `.default ?? module`, not a bare `.default`: NodeNext sees the raw CJS
26
27
  // `module.exports` and a bundler sees the unwrapped class, and this file is
27
28
  // reachable from a browser through `./apply-operations.js` (#252).
@@ -29,10 +30,9 @@ const ajv2020Module = Ajv2020Module;
29
30
  const Ajv2020 = ajv2020Module.default ?? ajv2020Module;
30
31
  // `discriminator` routes a batch entry to the single branch its `op` names, so
31
32
  // one malformed operation reports one fault instead of ten near-misses.
32
- const validateOperations = new Ajv2020({
33
- allErrors: true,
34
- discriminator: true,
35
- }).compile(operationsSchema);
33
+ // Keeps its own Ajv instance: `discriminator` changes how a schema compiles,
34
+ // so it cannot share one with the nine that do not set it.
35
+ const validateOperations = compileValidatorWith({ allErrors: true, discriminator: true }, operationsSchema);
36
36
  // Scalar fields replace; list fields append; `remove` retracts (ADR 0062).
37
37
  // An answer enriches what is there and may explicitly take back what it
38
38
  // asserted — it never silently shrinks anything.
package/dist/compiler.js CHANGED
@@ -11,12 +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 { compileValidator } from './schema-validation.js';
14
15
  const coreProfile = 'yarramate/core@0.1';
15
16
  const ajv2020Module = Ajv2020Import;
16
17
  const Ajv2020 = ajv2020Module.default ?? ajv2020Module;
17
- const validateDocument = new Ajv2020({ allErrors: true }).compile(documentSchema);
18
- const validateProfile = new Ajv2020({ allErrors: true }).compile(profileSchema);
19
- const validatePattern = new Ajv2020({ allErrors: true }).compile(patternSchema);
18
+ const validateDocument = compileValidator(documentSchema);
19
+ const validateProfile = compileValidator(profileSchema);
20
+ const validatePattern = compileValidator(patternSchema);
20
21
  const immutableMap = (entries) => {
21
22
  const backing = new Map(entries);
22
23
  const facade = {
@@ -1,6 +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 { compileValidator } from './schema-validation.js';
4
5
  import coreContractSchema from '../schema/yarramate-core-contract.schema.json' with { type: 'json'
5
6
  };
6
7
  // `.default ?? module`, not a bare `.default`: NodeNext sees the raw CJS
@@ -9,7 +10,7 @@ import coreContractSchema from '../schema/yarramate-core-contract.schema.json' w
9
10
  // to keep track of (#252).
10
11
  const ajv2020Module = Ajv2020Module;
11
12
  const Ajv2020 = ajv2020Module.default ?? ajv2020Module;
12
- const validateCoreContract = new Ajv2020({ allErrors: true }).compile(coreContractSchema);
13
+ const validateCoreContract = compileValidator(coreContractSchema);
13
14
  export function loadCoreContract(source) {
14
15
  const loaded = loadSourceDocument(source, validateCoreContract, 'Core contract');
15
16
  if (!loaded.ok)
package/dist/evidence.js CHANGED
@@ -8,7 +8,8 @@ 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
- const validateEvidenceSchema = new Ajv2020({ allErrors: true }).compile(evidenceSchema);
11
+ import { compileValidator } from './schema-validation.js';
12
+ const validateEvidenceSchema = compileValidator(evidenceSchema);
12
13
  const evidenceLocations = new WeakMap();
13
14
  const observationTarget = (observation) => 'subject' in observation ? observation.subject : observation.claim;
14
15
  export function loadEvidence(source) {
@@ -10,7 +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
- const validateCatalogue = new Ajv2020({ allErrors: true }).compile(catalogueSchema);
13
+ import { compileValidator } from './schema-validation.js';
14
+ const validateCatalogue = compileValidator(catalogueSchema);
14
15
  /**
15
16
  * The version of condition evaluation itself, not of the package.
16
17
  *
@@ -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 = new Ajv2020({ allErrors: true }).compile(projectionSchema);
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,6 +20,7 @@ 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 { compileValidator } from './schema-validation.js';
23
24
  export function loadProjection(source) {
24
25
  const loaded = loadSourceDocument(source, validateProjection, 'Projection');
25
26
  return loaded.ok
@@ -0,0 +1,12 @@
1
+ import Ajv2020Module from 'ajv/dist/2020.js';
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>;
6
+ /**
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.
10
+ */
11
+ export declare const compileValidatorWith: <T = unknown>(options: ConstructorParameters<typeof Ajv2020>[0], schema: object) => ValidateFunction<T>;
12
+ export {};
@@ -0,0 +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;
6
+ /**
7
+ * Compiles a JSON Schema AT MODULE SCOPE, on an instance shared with every
8
+ * other schema that wants the same options.
9
+ *
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
+ *
15
+ * ```
16
+ * {"atStartup":"ALLOWED",
17
+ * "atRequest":"EvalError: Code generation from strings disallowed for this context"}
18
+ * ```
19
+ *
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.
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.
45
+ */
46
+ export const compileValidatorWith = (options, schema) => new Ajv2020(options).compile(schema);