@powerlines/schema 0.11.75 → 0.11.77
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/index.cjs +105 -27
- package/dist/index.d.cts +11 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +11 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +106 -28
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { createUnpluginResolver } from "@powerlines/core/lib/unplugin";
|
|
|
2
2
|
import { resolveOptions } from "@powerlines/unplugin/esbuild";
|
|
3
3
|
import { omit } from "@stryke/helpers/omit";
|
|
4
4
|
import { findFileName } from "@stryke/path/file-path-fns";
|
|
5
|
-
import defu
|
|
5
|
+
import defu from "defu";
|
|
6
6
|
import { build } from "esbuild";
|
|
7
7
|
import { createEsbuildPlugin } from "unplugin";
|
|
8
8
|
import { toBool } from "@stryke/convert/to-bool";
|
|
@@ -173,10 +173,9 @@ function getPrimarySchemaType(schema) {
|
|
|
173
173
|
//#endregion
|
|
174
174
|
//#region src/type-checks.ts
|
|
175
175
|
const isSetNumber = (value) => typeof value === "number" && Number.isFinite(value);
|
|
176
|
-
const
|
|
177
|
-
const isSchemaLikeValue = (value) => isSetObject(value) || isSetBoolean(value);
|
|
176
|
+
const isSchemaLikeValue = (value) => isSetObject(value) || isBoolean(value);
|
|
178
177
|
const isRecordOfSchemaLike = (value) => isSetObject(value) && Object.values(value).every((item) => isSchemaLikeValue(item));
|
|
179
|
-
const isVocabularyMap = (value) => isSetObject(value) && Object.values(value).every((item) =>
|
|
178
|
+
const isVocabularyMap = (value) => isSetObject(value) && Object.values(value).every((item) => isBoolean(item));
|
|
180
179
|
const isStringArray = (value) => Array.isArray(value) && value.every((item) => isSetString(item));
|
|
181
180
|
const isRecordOfStringArrays = (value) => isSetObject(value) && Object.values(value).every((item) => isStringArray(item));
|
|
182
181
|
const JSON_SCHEMA_PRIMITIVE_TYPE_SET = new Set(JSON_SCHEMA_PRIMITIVE_TYPES);
|
|
@@ -238,8 +237,8 @@ const isUntypedFunctionArg = (value) => {
|
|
|
238
237
|
const isRecordOfUntypedSchema = (value) => isSetObject(value) && Object.values(value).every((item) => isUntypedSchema(item));
|
|
239
238
|
const isArrayOf = (value, predicate) => Array.isArray(value) && value.every((item) => predicate(item));
|
|
240
239
|
const isTupleOfTwo = (value, aPredicate, bPredicate) => Array.isArray(value) && value.length === 2 && aPredicate(value[0]) && bPredicate(value[1]);
|
|
241
|
-
const isOptionalString = (value) => value === void 0 ||
|
|
242
|
-
const isOptionalBoolean = (value) => value === void 0 ||
|
|
240
|
+
const isOptionalString = (value, allowEmpty = false) => value === void 0 || isString(value) && (allowEmpty || value.length > 0);
|
|
241
|
+
const isOptionalBoolean = (value) => value === void 0 || isBoolean(value);
|
|
243
242
|
const isOptionalNumber = (value) => value === void 0 || isSetNumber(value);
|
|
244
243
|
const isOptionalBigint = (value) => value === void 0 || isSetBigint(value);
|
|
245
244
|
const isOptionalJsonSchema = (value) => value === void 0 || isJsonSchema(value);
|
|
@@ -250,15 +249,15 @@ const hasValidJsonSchemaKeywords = (schema) => {
|
|
|
250
249
|
if (!isOptionalString(schema.$id)) return false;
|
|
251
250
|
if (!isOptionalString(schema.$schema)) return false;
|
|
252
251
|
if (schema.$vocabulary !== void 0 && !isVocabularyMap(schema.$vocabulary)) return false;
|
|
253
|
-
if (!isOptionalString(schema.$comment)) return false;
|
|
252
|
+
if (!isOptionalString(schema.$comment, true)) return false;
|
|
254
253
|
if (!isOptionalString(schema.$anchor)) return false;
|
|
255
254
|
if (schema.$defs !== void 0 && !isRecordOfSchemaLike(schema.$defs)) return false;
|
|
256
255
|
if (!isOptionalString(schema.$dynamicRef)) return false;
|
|
257
256
|
if (!isOptionalString(schema.$dynamicAnchor)) return false;
|
|
258
257
|
if (!isOptionalString(schema.name)) return false;
|
|
259
|
-
if (!isOptionalString(schema.title)) return false;
|
|
260
|
-
if (!isOptionalString(schema.description)) return false;
|
|
261
|
-
if (!isOptionalString(schema.docs)) return false;
|
|
258
|
+
if (!isOptionalString(schema.title, true)) return false;
|
|
259
|
+
if (!isOptionalString(schema.description, true)) return false;
|
|
260
|
+
if (!isOptionalString(schema.docs, true)) return false;
|
|
262
261
|
if (schema.examples !== void 0 && !Array.isArray(schema.examples)) return false;
|
|
263
262
|
if (schema.alias !== void 0 && !isStringArray(schema.alias)) return false;
|
|
264
263
|
if (schema.tags !== void 0 && !isStringArray(schema.tags)) return false;
|
|
@@ -309,7 +308,7 @@ function isJsonSchemaArray(input) {
|
|
|
309
308
|
if (!isSetObject(input)) return false;
|
|
310
309
|
const schema = input;
|
|
311
310
|
if (!hasValidJsonSchemaKeywords(schema) || schema.type !== "array") return false;
|
|
312
|
-
return isOptionalJsonSchemaArray(schema.prefixItems) && isOptionalJsonSchema(schema.items) && isOptionalJsonSchema(schema.contains) && isOptionalNumber(schema.minItems) && isOptionalNumber(schema.maxItems) && isOptionalBoolean(schema.uniqueItems) && isOptionalNumber(schema.minContains) && isOptionalNumber(schema.maxContains) && (schema.unevaluatedItems === void 0 ||
|
|
311
|
+
return isOptionalJsonSchemaArray(schema.prefixItems) && isOptionalJsonSchema(schema.items) && isOptionalJsonSchema(schema.contains) && isOptionalNumber(schema.minItems) && isOptionalNumber(schema.maxItems) && isOptionalBoolean(schema.uniqueItems) && isOptionalNumber(schema.minContains) && isOptionalNumber(schema.maxContains) && (schema.unevaluatedItems === void 0 || isBoolean(schema.unevaluatedItems) || isJsonSchema(schema.unevaluatedItems));
|
|
313
312
|
}
|
|
314
313
|
/**
|
|
315
314
|
* Type guard for bigint-backed integer schemas.
|
|
@@ -364,7 +363,7 @@ function isJsonSchemaEnum(input) {
|
|
|
364
363
|
const defaultValue = schema.default;
|
|
365
364
|
if (typeName === "string") return enumValues.every((value) => isSetString(value)) && (defaultValue === void 0 || isSetString(defaultValue));
|
|
366
365
|
if (typeName === "number" || typeName === "integer") return enumValues.every((value) => isSetNumber(value)) && (defaultValue === void 0 || isSetNumber(defaultValue));
|
|
367
|
-
if (typeName === "boolean") return enumValues.every((value) =>
|
|
366
|
+
if (typeName === "boolean") return enumValues.every((value) => isBoolean(value)) && (defaultValue === void 0 || isBoolean(defaultValue));
|
|
368
367
|
return typeName === "null" && enumValues.every((value) => value === null) && (defaultValue === void 0 || defaultValue === null);
|
|
369
368
|
}
|
|
370
369
|
/**
|
|
@@ -377,7 +376,7 @@ function isJsonSchemaAllOf(input) {
|
|
|
377
376
|
if (!isSetObject(input)) return false;
|
|
378
377
|
const schema = input;
|
|
379
378
|
if (!hasValidJsonSchemaKeywords(schema) || !isArrayOf(schema.allOf, isJsonSchema)) return false;
|
|
380
|
-
return schema.unevaluatedProperties === void 0 ||
|
|
379
|
+
return schema.unevaluatedProperties === void 0 || isBoolean(schema.unevaluatedProperties) || isJsonSchema(schema.unevaluatedProperties);
|
|
381
380
|
}
|
|
382
381
|
/**
|
|
383
382
|
* Type guard for literal-value schemas.
|
|
@@ -491,7 +490,7 @@ function isJsonSchemaDecimal(input) {
|
|
|
491
490
|
function isJsonSchemaObject(input) {
|
|
492
491
|
if (!isSetObject(input)) return false;
|
|
493
492
|
const schema = input;
|
|
494
|
-
return hasValidJsonSchemaKeywords(schema) && schema.type === "object" && (schema.properties === void 0 || isRecordOfSchemaLike(schema.properties)) && (schema.patternProperties === void 0 || isRecordOfSchemaLike(schema.patternProperties)) && (schema.additionalProperties === void 0 ||
|
|
493
|
+
return hasValidJsonSchemaKeywords(schema) && schema.type === "object" && (schema.properties === void 0 || isRecordOfSchemaLike(schema.properties)) && (schema.patternProperties === void 0 || isRecordOfSchemaLike(schema.patternProperties)) && (schema.additionalProperties === void 0 || isBoolean(schema.additionalProperties) || isJsonSchema(schema.additionalProperties)) && (schema.required === void 0 || isStringArray(schema.required)) && (schema.unevaluatedProperties === void 0 || isBoolean(schema.unevaluatedProperties) || isJsonSchema(schema.unevaluatedProperties)) && (schema.dependencies === void 0 || isSetObject(schema.dependencies) && Object.values(schema.dependencies).every((item) => isStringArray(item) || isJsonSchema(item))) && (schema.dependentRequired === void 0 || isRecordOfStringArrays(schema.dependentRequired)) && (schema.dependentSchemas === void 0 || isRecordOfSchemaLike(schema.dependentSchemas)) && isOptionalNumber(schema.minProperties) && isOptionalNumber(schema.maxProperties) && (schema.primaryKey === void 0 || isStringArray(schema.primaryKey)) && isOptionalString(schema.databaseSchema);
|
|
495
494
|
}
|
|
496
495
|
/**
|
|
497
496
|
* Type guard for string schemas.
|
|
@@ -513,7 +512,7 @@ function isJsonSchemaString(input) {
|
|
|
513
512
|
function isJsonSchemaSet(input) {
|
|
514
513
|
if (!isSetObject(input)) return false;
|
|
515
514
|
const schema = input;
|
|
516
|
-
return hasValidJsonSchemaKeywords(schema) && schema.type === "array" && schema.uniqueItems === true && isOptionalJsonSchemaArray(schema.prefixItems) && isOptionalJsonSchema(schema.items) && isOptionalJsonSchema(schema.contains) && isOptionalNumber(schema.minItems) && isOptionalNumber(schema.maxItems) && isOptionalNumber(schema.minContains) && isOptionalNumber(schema.maxContains) && (schema.unevaluatedItems === void 0 ||
|
|
515
|
+
return hasValidJsonSchemaKeywords(schema) && schema.type === "array" && schema.uniqueItems === true && isOptionalJsonSchemaArray(schema.prefixItems) && isOptionalJsonSchema(schema.items) && isOptionalJsonSchema(schema.contains) && isOptionalNumber(schema.minItems) && isOptionalNumber(schema.maxItems) && isOptionalNumber(schema.minContains) && isOptionalNumber(schema.maxContains) && (schema.unevaluatedItems === void 0 || isBoolean(schema.unevaluatedItems) || isJsonSchema(schema.unevaluatedItems));
|
|
517
516
|
}
|
|
518
517
|
/**
|
|
519
518
|
* Type guard for record schemas.
|
|
@@ -524,7 +523,7 @@ function isJsonSchemaSet(input) {
|
|
|
524
523
|
function isJsonSchemaRecord(input) {
|
|
525
524
|
if (!isSetObject(input)) return false;
|
|
526
525
|
const schema = input;
|
|
527
|
-
return hasValidJsonSchemaKeywords(schema) && schema.type === "object" && (schema.patternProperties === void 0 || isRecordOfSchemaLike(schema.patternProperties)) && (schema.additionalProperties === void 0 ||
|
|
526
|
+
return hasValidJsonSchemaKeywords(schema) && schema.type === "object" && (schema.patternProperties === void 0 || isRecordOfSchemaLike(schema.patternProperties)) && (schema.additionalProperties === void 0 || isBoolean(schema.additionalProperties) || isJsonSchema(schema.additionalProperties)) && isOptionalJsonSchema(schema.propertyNames);
|
|
528
527
|
}
|
|
529
528
|
/**
|
|
530
529
|
* Type guard for tuple schemas.
|
|
@@ -535,7 +534,7 @@ function isJsonSchemaRecord(input) {
|
|
|
535
534
|
function isJsonSchemaTuple(input) {
|
|
536
535
|
if (!isSetObject(input)) return false;
|
|
537
536
|
const schema = input;
|
|
538
|
-
return hasValidJsonSchemaKeywords(schema) && schema.type === "array" && isArrayOf(schema.prefixItems, isJsonSchema) && isOptionalNumber(schema.minItems) && isOptionalNumber(schema.maxItems) && isOptionalJsonSchema(schema.items) && isOptionalJsonSchema(schema.contains) && isOptionalBoolean(schema.uniqueItems) && isOptionalNumber(schema.minContains) && isOptionalNumber(schema.maxContains) && (schema.unevaluatedItems === void 0 ||
|
|
537
|
+
return hasValidJsonSchemaKeywords(schema) && schema.type === "array" && isArrayOf(schema.prefixItems, isJsonSchema) && isOptionalNumber(schema.minItems) && isOptionalNumber(schema.maxItems) && isOptionalJsonSchema(schema.items) && isOptionalJsonSchema(schema.contains) && isOptionalBoolean(schema.uniqueItems) && isOptionalNumber(schema.minContains) && isOptionalNumber(schema.maxContains) && (schema.unevaluatedItems === void 0 || isBoolean(schema.unevaluatedItems) || isJsonSchema(schema.unevaluatedItems));
|
|
539
538
|
}
|
|
540
539
|
/**
|
|
541
540
|
* Type guard for undefined-representing schemas.
|
|
@@ -563,7 +562,7 @@ function isJsonSchemaPrimitiveUnion(input) {
|
|
|
563
562
|
if (!Array.isArray(schema.enum)) return false;
|
|
564
563
|
if (schema.type === "string") return schema.enum.every((value) => isSetString(value)) && (schema.default === void 0 || isSetString(schema.default));
|
|
565
564
|
if (schema.type === "number") return schema.enum.every((value) => isSetNumber(value)) && (schema.default === void 0 || isSetNumber(schema.default));
|
|
566
|
-
if (schema.type === "boolean") return schema.enum.every((value) =>
|
|
565
|
+
if (schema.type === "boolean") return schema.enum.every((value) => isBoolean(value)) && (schema.default === void 0 || isBoolean(schema.default));
|
|
567
566
|
if (schema.type === "integer") {
|
|
568
567
|
if (schema.format !== "int64") return false;
|
|
569
568
|
return schema.enum.every((value) => isSetBigint(value)) && (schema.default === void 0 || isSetBigint(schema.default));
|
|
@@ -636,7 +635,7 @@ function isStandardSchema(input) {
|
|
|
636
635
|
function isValibotSchema(input) {
|
|
637
636
|
if (!isSetObject(input) || !isStandardSchema(input)) return false;
|
|
638
637
|
const schema = input;
|
|
639
|
-
return schema.kind === "schema" && isSetString(schema.type) &&
|
|
638
|
+
return schema.kind === "schema" && isSetString(schema.type) && isBoolean(schema.async) && isFunction(schema.reference) && isSetString(schema.expects) && isFunction(schema["~run"]);
|
|
640
639
|
}
|
|
641
640
|
/**
|
|
642
641
|
* Type guard for JSON Schema types.
|
|
@@ -774,7 +773,7 @@ function isSchemaObject(input) {
|
|
|
774
773
|
*/
|
|
775
774
|
function getJsonSchema(input) {
|
|
776
775
|
const schema = isSchema(input) ? input.schema : input;
|
|
777
|
-
if (!isJsonSchema(schema)) throw new TypeError(`The provided input is not a valid JSON Schema
|
|
776
|
+
if (!isJsonSchema(schema)) throw new TypeError(`The provided input is not a valid JSON Schema`);
|
|
778
777
|
return schema;
|
|
779
778
|
}
|
|
780
779
|
/**
|
|
@@ -789,7 +788,7 @@ function getJsonSchema(input) {
|
|
|
789
788
|
*/
|
|
790
789
|
function getJsonSchemaObject(input) {
|
|
791
790
|
const schema = getJsonSchema(input);
|
|
792
|
-
if (!isJsonSchemaObject(schema)) throw new TypeError(`The provided input is not a valid JSON Schema object
|
|
791
|
+
if (!isJsonSchemaObject(schema)) throw new TypeError(`The provided input is not a valid JSON Schema object`);
|
|
793
792
|
return schema;
|
|
794
793
|
}
|
|
795
794
|
/**
|
|
@@ -852,21 +851,100 @@ function addProperty(obj, name, property) {
|
|
|
852
851
|
if (schema.required.length === 0) delete schema.required;
|
|
853
852
|
}
|
|
854
853
|
/**
|
|
854
|
+
* Keywords whose values are a flat record of named JSON Schema fragments.
|
|
855
|
+
* Each child schema is merged recursively with its counterpart.
|
|
856
|
+
*/
|
|
857
|
+
const SCHEMA_RECORD_KEYWORDS = new Set([
|
|
858
|
+
"properties",
|
|
859
|
+
"patternProperties",
|
|
860
|
+
"$defs",
|
|
861
|
+
"definitions",
|
|
862
|
+
"dependentSchemas"
|
|
863
|
+
]);
|
|
864
|
+
/**
|
|
865
|
+
* Keywords whose value is a single JSON Schema fragment that should be
|
|
866
|
+
* recursively merged when both sides define it.
|
|
867
|
+
*/
|
|
868
|
+
const SCHEMA_SINGLE_KEYWORDS = new Set([
|
|
869
|
+
"if",
|
|
870
|
+
"then",
|
|
871
|
+
"else",
|
|
872
|
+
"not",
|
|
873
|
+
"contains",
|
|
874
|
+
"items",
|
|
875
|
+
"additionalProperties",
|
|
876
|
+
"unevaluatedProperties",
|
|
877
|
+
"propertyNames",
|
|
878
|
+
"unevaluatedItems"
|
|
879
|
+
]);
|
|
880
|
+
/**
|
|
881
|
+
* Keywords whose values are arrays of JSON Schema fragments that should be
|
|
882
|
+
* concatenated (rather than overridden) during a merge.
|
|
883
|
+
*/
|
|
884
|
+
const SCHEMA_ARRAY_CONCAT_KEYWORDS = new Set([
|
|
885
|
+
"allOf",
|
|
886
|
+
"anyOf",
|
|
887
|
+
"oneOf"
|
|
888
|
+
]);
|
|
889
|
+
/**
|
|
890
|
+
* Recursively merges two JSON Schema fragments. `override` wins for any scalar
|
|
891
|
+
* key that both schemas define, while structured keywords are handled
|
|
892
|
+
* specially:
|
|
893
|
+
*
|
|
894
|
+
* - `properties`, `patternProperties`, `$defs`, `definitions`,
|
|
895
|
+
* `dependentSchemas` — each matching child schema is merged recursively.
|
|
896
|
+
* - `allOf`, `anyOf`, `oneOf` — arrays are concatenated.
|
|
897
|
+
* - `if`, `then`, `else`, `not`, `contains`, `items`,
|
|
898
|
+
* `additionalProperties`, `unevaluatedProperties`, `propertyNames`,
|
|
899
|
+
* `unevaluatedItems` — merged recursively when both sides are schemas.
|
|
900
|
+
* - `required` — arrays are unioned and deduplicated.
|
|
901
|
+
*/
|
|
902
|
+
function mergeTwo(base, override) {
|
|
903
|
+
const result = { ...base };
|
|
904
|
+
for (const [key, overrideValue] of Object.entries(override)) {
|
|
905
|
+
const baseValue = result[key];
|
|
906
|
+
if (key === "required") result[key] = getUnique([...Array.isArray(baseValue) ? baseValue : [], ...Array.isArray(overrideValue) ? overrideValue : []]);
|
|
907
|
+
else if (SCHEMA_RECORD_KEYWORDS.has(key) && isSetObject(baseValue) && isSetObject(overrideValue)) {
|
|
908
|
+
const merged = { ...baseValue };
|
|
909
|
+
for (const [childKey, childOverride] of Object.entries(overrideValue)) {
|
|
910
|
+
const childBase = merged[childKey];
|
|
911
|
+
merged[childKey] = isJsonSchema(childBase) && isJsonSchema(childOverride) ? mergeTwo(childBase, childOverride) : childOverride;
|
|
912
|
+
}
|
|
913
|
+
result[key] = merged;
|
|
914
|
+
} else if (SCHEMA_ARRAY_CONCAT_KEYWORDS.has(key) && Array.isArray(baseValue) && Array.isArray(overrideValue)) result[key] = [...baseValue, ...overrideValue];
|
|
915
|
+
else if (SCHEMA_SINGLE_KEYWORDS.has(key) && isJsonSchema(baseValue) && isJsonSchema(overrideValue)) result[key] = mergeTwo(baseValue, overrideValue);
|
|
916
|
+
else result[key] = overrideValue;
|
|
917
|
+
}
|
|
918
|
+
return result;
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
855
921
|
* Merges multiple JSON Schemas into one.
|
|
856
922
|
*
|
|
857
923
|
* @remarks
|
|
858
|
-
* This function takes multiple JSON Schemas or Schema wrappers and merges them
|
|
924
|
+
* This function takes multiple JSON Schemas or Schema wrappers and merges them
|
|
925
|
+
* into a single JSON Schema object. Later schemas in the argument list take
|
|
926
|
+
* precedence over earlier ones for scalar conflicts.
|
|
927
|
+
*
|
|
928
|
+
* Structured keywords are merged recursively:
|
|
929
|
+
* - Named child schemas (`properties`, `$defs`, etc.) are merged
|
|
930
|
+
* per-property via recursive calls to `merge`.
|
|
931
|
+
* - Composition arrays (`allOf`, `anyOf`, `oneOf`) are concatenated.
|
|
932
|
+
* - Single-schema keywords (`if`, `then`, `else`, `not`, `items`, etc.)
|
|
933
|
+
* are merged recursively when both sides define them.
|
|
934
|
+
* - `required` arrays are unioned and deduplicated.
|
|
859
935
|
*
|
|
860
936
|
* @param schemas - An array of JSON Schemas or Schema wrappers to merge.
|
|
861
937
|
* @returns A new JSON Schema that is the result of merging all input schemas.
|
|
862
938
|
*/
|
|
863
939
|
function merge(...schemas) {
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
940
|
+
const jsonSchemas = schemas.map((s) => getJsonSchema(s));
|
|
941
|
+
if (jsonSchemas.length === 0) return {};
|
|
942
|
+
return jsonSchemas.reduce((acc, schema) => {
|
|
943
|
+
const accType = acc.type;
|
|
944
|
+
const schemaType = schema.type;
|
|
945
|
+
if (accType && schemaType && accType !== schemaType) return schema;
|
|
946
|
+
return mergeTwo(acc, schema);
|
|
947
|
+
});
|
|
870
948
|
}
|
|
871
949
|
/**
|
|
872
950
|
* Returns whether a JSON Schema fragment accepts `null`.
|