@owsp/js
v0.13.13
Published
Runtime utilities for OpenWinSpec schemas: projection via `x-partial`, canonical stringification (4-decimal numbers), hashing, and validation helpers.
Readme
@owsp/js
Runtime utilities for OpenWinSpec schemas: projection via x-partial, canonical stringification (4-decimal numbers), hashing, and validation helpers.
Quick usage
import {
listSerializeFields,
extractBySerializeGroup,
hashBySerializeGroup,
printHashedFieldsBySerializeGroup,
} from "@owsp/js";
const alias = "Material Profile System"; // schema title
// Inspect which fields participate in a group
const fields = listSerializeFields(alias, "summary");
// Build a projection and hash it deterministically
const projection = extractBySerializeGroup(alias, data, "summary");
const hash = hashBySerializeGroup(alias, data, "summary");
// Print hash + full list of normalized hashed paths
printHashedFieldsBySerializeGroup(alias, data, "summary");See docs: ../../docs/guide-x-partial.md.
Validation: x-uniqueBy
AjvValidator supports custom schema keyword x-uniqueBy for arrays of objects.
- Use it when uniqueness must be enforced by selected fields, for example
shortName. - It supports single-field and composite uniqueness.
Schema example:
properties:
colorways:
type: array
x-uniqueBy: [shortName]
items:
$ref: "./profile-system-colorway.json"Notes:
- This is a runtime custom keyword in
AjvValidator. - It complements
uniqueItems(which checks full item equality, not selected fields).
Details: ../../docs/guide-x-unique-by.md.
Schema property ordering (x-order)
The package exposes helpers to order object properties according to x-order annotations in schemas. This is useful for stable serialization/UI forms.
APIs:
- Auto-detect schema and order:
import { orderObject } from "@owsp/js";
const ordered = orderObject(data);- Order by bundle type (when known):
import { orderObjectByType, BundleType } from "@owsp/js";
const ordered = orderObjectByType(BundleType.MaterialSystem, data);- Order by schema $id (internal tools):
import { orderObjectBySchemaId } from "@owsp/js";
const ordered = orderObjectBySchemaId(
"https://openwinspec.org/schemas/v1/material/profile/profile-system.json",
data
);Details: ../../docs/schema-ordering.md.
Behavior notes:
- If some known properties have
x-order, they come first byx-ordervalue, then known properties withoutx-order(sorted by key), then unknown properties (sorted by key). - If at a given object level none of the known properties have
x-order, the original key order is preserved at that level; nested values are still processed recursively and may be ordered if their schemas specifyx-order.
Build & test
nx build js
nx test jsProjection & Sorting Details
- Deep projection:
extractBySerializeGroup()walks nested objects/arrays, resolving$ref,oneOf, andallOfto collect fields annotated withx-partial. - Array sorting: If a schema property has
x-partial.sortArrayBy: [key1, ...], arrays are sorted deterministically by these keys. Numeric keys sort ascending numerically; otherwise comparison falls back to canonical JSON strings. - Post-projection sort: Arrays are re-sorted after projecting inner items to ensure final ordering matches sorting rules.
- Targeted fallback for glazing: For
glazingassemblies, inner arraysglazings[*].sealsandglazings[*].glazing_beadsproject as{vendor, id}when nox-partialfields are present, withthicknessincluded when available. This avoids changing schema structure. - Scope: The glazing fallback is deliberate and not applied to other assemblies (e.g.,
sash_seal). Behavior for other assemblies depends on theirx-partialannotations.
Hashed Fields Command
Use printHashedFieldsBySerializeGroup() when you want to inspect exactly which projection paths participate in hash generation for concrete input data.
import {
inspectHashedFieldsBySerializeGroup,
listHashedPathsBySerializeGroup,
printHashedFieldsBySerializeGroup,
} from "@owsp/js";
const alias = "Material Profile System";
const group = "default";
const paths = listHashedPathsBySerializeGroup(alias, data, group);
const inspected = inspectHashedFieldsBySerializeGroup(alias, data, group);
printHashedFieldsBySerializeGroup(alias, data, group);
console.log(inspected.hash);
console.log(paths);Notes:
- Paths are normalized with
[]for arrays (for example:/colorways[]/details/frames[]/type). - The list is data-dependent: only paths present in the current projection are included.
- You can disable normalization or container paths via options:
listHashedPathsBySerializeGroup(alias, data, group, { normalizeArrays: false, includeContainers: false }).
CLI Script (Saved Projection + Canonical)
For quick local checks, use the repository script:
node scripts/inspect-hashed-projection.js --input packages/schemas/src/v1/examples/material/system/delight.rhs.profile.material-system.json --alias "Material Profile System" --group default --outDir tmp --outPrefix projection-delightScript outputs:
tmp/projection-delight.json- pretty JSON projection used for hash.tmp/projection-delight-canonical.json- canonical JSON string (sorted keys, fixed number format).tmp/projection-delight-meta.json- hash, paths count, and full hashed paths list.
Arguments:
--inputpath to input JSON file.--aliasschema title (default:Material Profile System).--groupserialize group (default:default).--outDiroutput directory (default:tmp).--outPrefixoutput files prefix (default:projection-delight).
