@effected/schemastore 0.11.0 → 0.13.0

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.
@@ -194,23 +194,27 @@ var SchemaVersioning = class SchemaVersioning {
194
194
  * Derives the schema file name for a catalog name: `name.json`
195
195
  * unversioned, `name-<version>.json` versioned under the `"flat"`
196
196
  * layout (the default and the only shape SchemaStore serves), or
197
- * `<version>/name-<version>.json` under `"versioned"`.
197
+ * `<version>/name-<version>.json` under `"versioned"` — `<version>/name.json`
198
+ * when `appendVersion` is `false`, which only the versioned layout can
199
+ * carry (under `"flat"` two versions would share one file name, so that
200
+ * combination throws).
198
201
  *
199
202
  * The name must be a simple file base name (no separators, no
200
203
  * whitespace); anything else is a wiring mistake and throws.
201
204
  */
202
- static fileName(name, version, layout = "flat") {
205
+ static fileName(name, version, layout = "flat", appendVersion = true) {
203
206
  assertSimpleName(name);
207
+ if (!appendVersion && layout !== "versioned") throw new Error(`appendVersion: false requires the "versioned" layout; under "${layout}" every version of "${name}" would share one file name`);
204
208
  if (version === void 0) return `${name}.json`;
205
- const file = `${name}-${version}.json`;
209
+ const file = appendVersion ? `${name}-${version}.json` : `${name}.json`;
206
210
  return layout === "versioned" ? `${version}/${file}` : file;
207
211
  }
208
212
  /**
209
213
  * The canonical URL a schema file is hosted at: `baseUrl` joined with
210
214
  * {@link SchemaVersioning.fileName}.
211
215
  */
212
- static schemaUrl(baseUrl, name, version, layout = "flat") {
213
- return joinUrl(baseUrl, SchemaVersioning.fileName(name, version, layout));
216
+ static schemaUrl(baseUrl, name, version, layout = "flat", appendVersion = true) {
217
+ return joinUrl(baseUrl, SchemaVersioning.fileName(name, version, layout, appendVersion));
214
218
  }
215
219
  /**
216
220
  * Assembles the `url`/`versions` half of a catalog entry.
@@ -228,9 +232,10 @@ var SchemaVersioning = class SchemaVersioning {
228
232
  * verbatim — so a differently-spelled equivalent (`"1.2"` matching a
229
233
  * `"1.2.0"` member) still points `url` at the same file the map does.
230
234
  *
231
- * `layout` (default `"flat"`) is forwarded to every URL derivation, so
232
- * `"versioned"` nests every map value and `url` under its own version
233
- * directory.
235
+ * `layout` (default `"flat"`) and `appendVersion` (default `true`) are
236
+ * forwarded to every URL derivation, so `"versioned"` nests every map
237
+ * value and `url` under its own version directory, with or without the
238
+ * `-<version>` file suffix.
234
239
  *
235
240
  * Labels are inserted in ascending {@link SchemaVersioning.Order}; see
236
241
  * {@link CatalogUrls.versions} for why a bare-major key's serialized
@@ -239,17 +244,18 @@ var SchemaVersioning = class SchemaVersioning {
239
244
  static catalogUrls(options) {
240
245
  const { baseUrl, name, versions } = options;
241
246
  const layout = options.layout ?? "flat";
242
- if (versions === void 0) return { url: SchemaVersioning.schemaUrl(baseUrl, name, void 0, layout) };
247
+ const appendVersion = options.appendVersion ?? true;
248
+ if (versions === void 0) return { url: SchemaVersioning.schemaUrl(baseUrl, name, void 0, layout, appendVersion) };
243
249
  if (versions.length === 0) throw new Error(`catalogUrls received an empty versions array for "${name}": pass undefined for the unversioned mode`);
244
250
  const ascending = [...versions].sort(SchemaVersioning.Order);
245
251
  const map = {};
246
- for (const version of ascending) map[version] = SchemaVersioning.schemaUrl(baseUrl, name, version, layout);
252
+ for (const version of ascending) map[version] = SchemaVersioning.schemaUrl(baseUrl, name, version, layout, appendVersion);
247
253
  const newest = ascending[ascending.length - 1];
248
254
  const requested = options.current ?? newest;
249
255
  const current = ascending.find((v) => SchemaVersioning.Order(v, requested) === 0);
250
256
  if (current === void 0) throw new Error(`catalogUrls: current "${requested}" is not one of the versions of "${name}"`);
251
257
  return {
252
- url: SchemaVersioning.schemaUrl(baseUrl, name, current, layout),
258
+ url: SchemaVersioning.schemaUrl(baseUrl, name, current, layout, appendVersion),
253
259
  versions: map
254
260
  };
255
261
  }
@@ -1,22 +1,12 @@
1
1
  import { SchemaVersioning } from "./SchemaVersioning.js";
2
2
  import { CatalogEntry } from "./CatalogEntry.js";
3
3
  import { DriftPolicy } from "./DriftPolicy.js";
4
+ import { HostedSchema } from "./HostedSchema.js";
4
5
  import { SchemaTarget } from "./SchemaTarget.js";
5
- import { Option, Predicate, Result, Schema } from "effect";
6
+ import { Predicate, Result, Schema } from "effect";
6
7
 
7
8
  //#region src/SchemastoreConfig.ts
8
9
  const ConfigBrand = Symbol.for("@effected/schemastore/SchemastoreConfig");
9
- /** The host SchemaStore-hosted documents declare in `$id`. @public */
10
- const SCHEMASTORE_ID_BASE = "https://json.schemastore.org";
11
- /** The host SchemaStore's `catalog.json` points `url` at. @public */
12
- const SCHEMASTORE_CATALOG_BASE = "https://www.schemastore.org";
13
- const DRIFT_TOLERANCES = [
14
- "strict",
15
- "semantic",
16
- "allow"
17
- ];
18
- const ON_DRIFT = ["error", "warn"];
19
- const LAYOUTS = ["flat", "versioned"];
20
10
  const fail = (message) => {
21
11
  throw new Error(`defineConfig: ${message}`);
22
12
  };
@@ -25,121 +15,130 @@ const trimSlashes = (dir) => {
25
15
  while (end > 1 && dir.charCodeAt(end - 1) === 47) end -= 1;
26
16
  return dir.slice(0, end);
27
17
  };
28
- const resolveHosting = (name, baseUrl, layout) => {
29
- if (baseUrl === void 0) return fail(`schema "${name}" has no baseUrl and the config declares no default`);
30
- if (typeof baseUrl !== "string") return fail(`schema "${name}" has a baseUrl that is not a string`);
31
- if (baseUrl.length === 0) return fail(`schema "${name}" has no baseUrl and the config declares no default`);
32
- if (layout !== void 0 && !LAYOUTS.includes(layout)) return fail(`schema "${name}" has an invalid layout "${String(layout)}"; expected "flat" or "versioned"`);
33
- if (baseUrl === "schemastore") {
34
- if (layout !== void 0) return fail(`schema "${name}" declares layout "${layout}" under baseUrl "schemastore", which serves only the flat layout`);
35
- return {
36
- idBase: SCHEMASTORE_ID_BASE,
37
- catalogBase: SCHEMASTORE_CATALOG_BASE,
38
- layout: "flat"
39
- };
40
- }
41
- if (!baseUrl.startsWith("https://") || baseUrl.length === 8) return fail(`schema "${name}" has baseUrl "${baseUrl}"; expected "schemastore" or an https:// URL`);
42
- return {
43
- idBase: baseUrl,
44
- catalogBase: baseUrl,
45
- layout: layout ?? "versioned"
46
- };
47
- };
48
- const parseLabel = (name, label) => {
49
- if (typeof label !== "string") return fail(`schema "${name}" has a version label that is not a string: ${String(label)}`);
50
- return Result.getOrThrowWith(SchemaVersioning.parseResult(label), (error) => /* @__PURE__ */ new Error(`defineConfig: schema "${name}" has an invalid version label "${label}": ${error.message}`));
18
+ const members = (record) => Object.keys(record);
19
+ const DriftToleranceInput = Schema.Literals(members({
20
+ strict: null,
21
+ semantic: null,
22
+ allow: null
23
+ }));
24
+ const OnDriftInput = Schema.Literals(members({
25
+ error: null,
26
+ warn: null
27
+ }));
28
+ const LayoutInput = Schema.Literals(members({
29
+ flat: null,
30
+ versioned: null
31
+ }));
32
+ const EffectSchemaInput = Schema.declare((u) => Schema.isSchema(u), { expected: "an Effect Schema" });
33
+ const OptionsInput = Schema.declare((u) => Predicate.isObject(u), { expected: "an object" });
34
+ const CatalogBlockInput = Schema.Struct({
35
+ description: Schema.String,
36
+ fileMatch: Schema.NonEmptyArray(Schema.String)
37
+ });
38
+ const EntryInput = Schema.Struct({
39
+ schema: EffectSchemaInput,
40
+ hosted: Schema.optionalKey(Schema.instanceOf(HostedSchema, { expected: "a HostedSchema" })),
41
+ versions: Schema.optionalKey(Schema.Array(Schema.String)),
42
+ current: Schema.optionalKey(Schema.String),
43
+ published: Schema.optionalKey(Schema.Boolean),
44
+ baseUrl: Schema.optionalKey(Schema.String),
45
+ layout: Schema.optionalKey(LayoutInput),
46
+ appendVersion: Schema.optionalKey(Schema.Boolean),
47
+ drift: Schema.optionalKey(DriftToleranceInput),
48
+ catalog: Schema.optionalKey(CatalogBlockInput),
49
+ jsonSchema: Schema.optionalKey(OptionsInput),
50
+ rootAnnotations: Schema.optionalKey(OptionsInput)
51
+ });
52
+ const ConfigInput = Schema.Struct({
53
+ outputDir: Schema.NonEmptyString,
54
+ baseUrl: Schema.optionalKey(Schema.String),
55
+ drift: Schema.optionalKey(DriftToleranceInput),
56
+ onDrift: Schema.optionalKey(OnDriftInput),
57
+ catalogPath: Schema.optionalKey(Schema.NonEmptyString),
58
+ schemas: Schema.Record(Schema.String, Schema.Unknown)
59
+ });
60
+ const DECODE_OPTIONS = {
61
+ onExcessProperty: "error",
62
+ errors: "all"
51
63
  };
52
- const resolveVersions = (name, entry) => {
53
- if (entry.versions === void 0) {
54
- if (entry.current !== void 0) return fail(`schema "${name}" declares current "${entry.current}" without versions`);
55
- return;
56
- }
57
- if (!Array.isArray(entry.versions)) return fail(`schema "${name}" declares versions that is not an array`);
58
- if (entry.versions.length === 0) return fail(`schema "${name}" declares versions as an empty array; omit versions for an unversioned schema`);
59
- const versions = [];
60
- for (const label of entry.versions) {
61
- const version = parseLabel(name, label);
62
- const duplicate = versions.find((v) => SchemaVersioning.Order(v, version) === 0);
63
- if (duplicate !== void 0) return fail(`schema "${name}" declares the same version twice, as "${duplicate}" and "${version}"`);
64
- versions.push(version);
64
+ const withoutUndefined = (input) => Predicate.isObject(input) && !Array.isArray(input) ? Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0)) : input;
65
+ const decodeOrThrow = (schema, input, prefix) => Result.getOrThrowWith(Schema.decodeUnknownResult(schema)(input, DECODE_OPTIONS), (error) => /* @__PURE__ */ new Error(`defineConfig: ${prefix}${error.message.replace(/\n\s*/g, " ")}`));
66
+ const resolveIdentity = (name, entry, defaultBaseUrl) => {
67
+ if (entry.hosted !== void 0) {
68
+ if (entry.hosted.name !== name) return fail(`schema "${name}" is keyed differently from its hosted identity "${entry.hosted.name}"`);
69
+ const spelled = [
70
+ "baseUrl",
71
+ "versions",
72
+ "current",
73
+ "layout",
74
+ "appendVersion"
75
+ ].filter((key) => entry[key] !== void 0);
76
+ if (spelled.length > 0) return fail(`schema "${name}" declares ${spelled.map((key) => `"${key}"`).join(", ")} beside hosted; the hosted identity owns them`);
77
+ return entry.hosted;
65
78
  }
66
- const newest = Option.getOrThrow(SchemaVersioning.latest(versions));
67
- if (entry.current === void 0) return {
68
- versions,
69
- current: newest
70
- };
71
- const current = parseLabel(name, entry.current);
72
- const match = versions.find((v) => SchemaVersioning.Order(v, current) === 0);
73
- if (match === void 0) return fail(`schema "${name}" declares current "${entry.current}" which is not one of its versions`);
74
- return {
75
- versions,
76
- current: match
77
- };
78
- };
79
- const isDriftTolerance = (value) => DRIFT_TOLERANCES.includes(value);
80
- const resolveConfigDrift = (value) => {
81
- if (value === void 0) return DriftPolicy.defaults.policy;
82
- return isDriftTolerance(value) ? value : fail(`config has an invalid drift tolerance "${String(value)}"`);
83
- };
84
- const resolveSchemaDrift = (name, value, fallback) => {
85
- if (value === void 0) return fallback;
86
- return isDriftTolerance(value) ? value : fail(`schema "${name}" has an invalid drift tolerance "${String(value)}"`);
79
+ const baseUrl = entry.baseUrl ?? defaultBaseUrl;
80
+ if (baseUrl === void 0) return fail(`schema "${name}" has no baseUrl and the config declares no default`);
81
+ const decoded = Schema.decodeUnknownResult(HostedSchema)(withoutUndefined({
82
+ name,
83
+ baseUrl,
84
+ versions: entry.versions,
85
+ current: entry.current,
86
+ layout: entry.layout,
87
+ appendVersion: entry.appendVersion
88
+ }));
89
+ return Result.getOrThrowWith(decoded, (error) => /* @__PURE__ */ new Error(`defineConfig: ${error.message.replace(/\n\s*/g, " ")}`));
87
90
  };
88
- const resolveEntry = (name, entry, defaults, outputDir) => {
91
+ const resolveEntry = (name, input, defaults, outputDir) => {
89
92
  if (!SchemaVersioning.isSimpleName(name)) return fail(`schema "${name}" must be keyed by a simple file base name (no separators, no whitespace)`);
90
- if (!Predicate.isObject(entry)) return fail(`schema "${name}" must be an object`);
91
- if (!Schema.isSchema(entry.schema)) return fail(`schema "${name}" has a schema that is not an Effect Schema`);
92
- if (entry.published !== void 0 && typeof entry.published !== "boolean") return fail(`schema "${name}" has a published that is not a boolean`);
93
- const baseUrl = entry.baseUrl ?? defaults.baseUrl;
94
- const hosting = resolveHosting(name, baseUrl, entry.layout);
95
- const versioned = resolveVersions(name, entry);
96
- if (baseUrl === "schemastore" && entry.catalog === void 0) return fail(`schema "${name}" must declare a catalog block under baseUrl "schemastore"`);
97
- if (entry.catalog !== void 0 && (!Predicate.isObject(entry.catalog) || !Array.isArray(entry.catalog.fileMatch) || typeof entry.catalog.description !== "string")) return fail(`schema "${name}" declares an invalid catalog block (expected { description, fileMatch[] })`);
98
- if (entry.catalog !== void 0 && entry.catalog.fileMatch.length === 0) return fail(`schema "${name}" declares a catalog with an empty fileMatch`);
99
- const file = (version) => `${outputDir}/${SchemaVersioning.fileName(name, version, hosting.layout)}`;
100
- const urlOf = (base) => (version) => SchemaVersioning.schemaUrl(base, name, version, hosting.layout);
101
- const idOf = urlOf(hosting.idBase);
102
- const catalogUrlOf = urlOf(hosting.catalogBase);
103
- const current = versioned?.current;
93
+ const entry = decodeOrThrow(EntryInput, withoutUndefined(input), `schema "${name}" `);
94
+ const hosted = resolveIdentity(name, entry, defaults.baseUrl);
95
+ if (hosted.baseUrl === "schemastore" && entry.catalog === void 0) return fail(`schema "${name}" must declare a catalog block under baseUrl "schemastore"`);
96
+ const versions = hosted.resolvedVersions;
97
+ const current = hosted.resolvedCurrent;
98
+ const file = (version) => `${outputDir}/${hosted.fileNameFor(version)}`;
99
+ const generation = {
100
+ ...entry.jsonSchema !== void 0 ? { jsonSchema: entry.jsonSchema } : {},
101
+ ...entry.rootAnnotations !== void 0 ? { rootAnnotations: entry.rootAnnotations } : {}
102
+ };
104
103
  const target = current === void 0 ? SchemaTarget.make({
105
104
  schema: entry.schema,
106
- $id: idOf(current),
105
+ $id: hosted.idFor(current),
107
106
  name,
108
107
  path: file(current),
109
108
  published: entry.published ?? false,
110
- ...entry.jsonSchema !== void 0 ? { jsonSchema: entry.jsonSchema } : {},
111
- ...entry.rootAnnotations !== void 0 ? { rootAnnotations: entry.rootAnnotations } : {}
109
+ ...generation
112
110
  }) : SchemaTarget.make({
113
111
  schema: entry.schema,
114
- $id: idOf(current),
112
+ $id: hosted.idFor(current),
115
113
  name,
116
114
  path: file(current),
117
115
  version: current,
118
116
  published: entry.published ?? false,
119
- ...entry.jsonSchema !== void 0 ? { jsonSchema: entry.jsonSchema } : {},
120
- ...entry.rootAnnotations !== void 0 ? { rootAnnotations: entry.rootAnnotations } : {}
117
+ ...generation
121
118
  });
122
- const frozen = versioned === void 0 ? [] : versioned.versions.filter((v) => v !== versioned.current).map((version) => ({
119
+ const frozen = versions.filter((v) => v !== current).map((version) => ({
123
120
  version,
124
121
  path: file(version),
125
- url: catalogUrlOf(version)
122
+ $id: hosted.idFor(version),
123
+ url: hosted.urlFor(version)
126
124
  }));
127
125
  const catalog = entry.catalog === void 0 ? void 0 : CatalogEntry.assemble({
128
126
  name,
129
127
  description: entry.catalog.description,
130
128
  fileMatch: entry.catalog.fileMatch,
131
- baseUrl: hosting.catalogBase,
132
- layout: hosting.layout,
133
- ...versioned !== void 0 ? {
134
- versions: versioned.versions,
135
- current: versioned.current
129
+ baseUrl: hosted.catalogBase,
130
+ layout: hosted.resolvedLayout,
131
+ appendVersion: hosted.resolvedAppendVersion,
132
+ ...current !== void 0 ? {
133
+ versions,
134
+ current
136
135
  } : {}
137
136
  });
138
137
  return {
139
138
  name,
140
139
  target,
141
140
  frozen,
142
- drift: resolveSchemaDrift(name, entry.drift, defaults.drift),
141
+ drift: entry.drift ?? defaults.drift,
143
142
  ...catalog !== void 0 ? { catalog } : {}
144
143
  };
145
144
  };
@@ -180,44 +179,41 @@ const assertUniquePaths = (paths) => {
180
179
  * for the catalog URL, forcing the `"flat"` layout; any other `baseUrl` is
181
180
  * used as one base for both, defaulting to the `"versioned"` layout.
182
181
  *
183
- * Throws a plain `Error` (never a raw `TypeError`) naming the offending
184
- * schema on: a non-object `input`; an empty `schemas` record; a
185
- * missing/empty `outputDir`; a schema key that is not a simple file base
186
- * name; a schema whose `schema` is not an Effect Schema; a schema with no
187
- * `baseUrl` anywhere, or a `baseUrl` that is not a string; a `baseUrl` that
188
- * is neither `"schemastore"` nor an `https://` URL; a `versions` that is not
189
- * an array, or an empty `versions` array; a version label (or `current`)
190
- * that is not a string, or an otherwise invalid version label; two labels
191
- * spelling the same version; `current` given without `versions`, or naming
192
- * one not among them; a non-boolean `published`; `layout` declared under
193
- * `baseUrl: "schemastore"`; a missing `catalog` under
194
- * `baseUrl: "schemastore"`, or one with an empty `fileMatch`; an invalid
195
- * `drift` or top-level `onDrift`; and an output path (a target, a frozen
196
- * file, or the catalog path) declared twice, compared after a lexical
197
- * normalisation (`./`, `..`, trailing `/`) — the CLI's loader re-checks on
198
- * the resolved absolute paths. Branding the result lets a loader recognise a
199
- * config module's default export via {@link isSchemastoreConfig}.
182
+ * Throws a plain `Error` (never a raw `TypeError`) on every malformed
183
+ * input. The shape is decoded once per level with a `Schema.Struct`
184
+ * (`errors: "all"`, so every issue on an entry is reported at once, and
185
+ * `onExcessProperty: "error"`, so a typo'd key is named rather than
186
+ * dropped); the message is `defineConfig: schema "<name>" ` followed by the
187
+ * decode issues (`Expected string at ["baseUrl"]`). After a shape-clean
188
+ * decode the cross-field rules run: the entry's identity — its
189
+ * {@link HostedSchema}, or one built from `baseUrl`/`versions`/`current`/
190
+ * `layout` and the config default — is validated by `HostedSchema` itself
191
+ * (a `hosted` entry must be keyed by `hosted.name` and must not spell those
192
+ * four fields beside it); a schema key must be a simple file base name; a
193
+ * `catalog` is required under `baseUrl: "schemastore"`; an empty `schemas`
194
+ * record is rejected; and an output path (a target, a frozen file, or the
195
+ * catalog path) declared twice is rejected after a lexical normalisation
196
+ * (`./`, `..`, trailing `/`) — the CLI's loader re-checks on the resolved
197
+ * absolute paths. Branding the result lets a loader recognise a config
198
+ * module's default export via {@link isSchemastoreConfig}.
200
199
  *
201
200
  * @public
202
201
  */
203
202
  const defineConfig = (input) => {
204
- if (!Predicate.isObject(input)) return fail("expected a config object");
205
- if (typeof input.outputDir !== "string" || input.outputDir.length === 0) return fail("outputDir is required");
206
- const outputDir = trimSlashes(input.outputDir);
207
- if (!Predicate.isObject(input.schemas) || Object.keys(input.schemas).length === 0) return fail("at least one schema is required");
208
- if (input.onDrift !== void 0 && !ON_DRIFT.includes(input.onDrift)) return fail(`invalid onDrift "${String(input.onDrift)}"`);
209
- if (input.catalogPath !== void 0 && (typeof input.catalogPath !== "string" || input.catalogPath.length === 0)) return fail("catalogPath must be a non-empty string when given");
203
+ const config = decodeOrThrow(ConfigInput, withoutUndefined(input), "");
204
+ const outputDir = trimSlashes(config.outputDir);
205
+ if (Object.keys(config.schemas).length === 0) return fail("at least one schema is required");
210
206
  const defaults = {
211
- baseUrl: input.baseUrl,
212
- drift: resolveConfigDrift(input.drift)
207
+ baseUrl: config.baseUrl,
208
+ drift: config.drift ?? DriftPolicy.defaults.policy
213
209
  };
214
- const schemas = Object.entries(input.schemas).map(([name, entry]) => resolveEntry(name, entry, defaults, outputDir));
215
- const catalogPath = input.catalogPath ?? `${outputDir}/catalog.json`;
210
+ const schemas = Object.entries(config.schemas).map(([name, entry]) => resolveEntry(name, entry, defaults, outputDir));
211
+ const catalogPath = config.catalogPath ?? `${outputDir}/catalog.json`;
216
212
  assertUniquePaths([...schemas.flatMap((s) => [s.target.path, ...s.frozen.map((f) => f.path)]), catalogPath]);
217
213
  return {
218
214
  [ConfigBrand]: true,
219
215
  outputDir,
220
- onDrift: input.onDrift ?? DriftPolicy.defaults.onDrift,
216
+ onDrift: config.onDrift ?? DriftPolicy.defaults.onDrift,
221
217
  catalogPath,
222
218
  schemas
223
219
  };
@@ -231,4 +227,4 @@ const defineConfig = (input) => {
231
227
  const isSchemastoreConfig = (value) => typeof value === "object" && value !== null && value[ConfigBrand] === true;
232
228
 
233
229
  //#endregion
234
- export { SCHEMASTORE_CATALOG_BASE, SCHEMASTORE_ID_BASE, defineConfig, isSchemastoreConfig };
230
+ export { defineConfig, isSchemastoreConfig };
package/StoreDocument.js CHANGED
@@ -207,6 +207,7 @@ var StoreDocument = class StoreDocument extends Schema.Class("StoreDocument")({
207
207
  const userIncludes = options.jsonSchema?.includeAnnotationKey;
208
208
  const undeclared = /* @__PURE__ */ new Set();
209
209
  const document = Schema.toJsonSchemaDocument(source, {
210
+ onExcessProperty: "error",
210
211
  ...options.jsonSchema,
211
212
  includeAnnotationKey: (key) => {
212
213
  if (KeywordFamilies.isDeclared(key)) return true;