npm package: @owsp/js
Description: Runtime utilities for OpenWinSpec schemas: projection via `x-partial`, canonical stringification (4-decimal numbers), hashing, and validation helpers.
Latest version: 0.14.5

# @owsp/js

Runtime utilities for OpenWinSpec schemas: projection via `x-partial`, canonical stringification (4-decimal numbers), hashing, and validation helpers.

## Quick usage

```ts
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:

```yaml
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:

```ts
import { orderObject } from "@owsp/js";

const ordered = orderObject(data);
```

- Order by bundle type (when known):

```ts
import { orderObjectByType, BundleType } from "@owsp/js";

const ordered = orderObjectByType(BundleType.MaterialSystem, data);
```

- Order by schema $id (internal tools):

```ts
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 by `x-order` value, then known properties without `x-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 specify `x-order`.

## Unit conversions

The package provides utilities to convert values between different units of measurement.

APIs:

- Generic conversion with explicit category:

```ts
import { convert } from "@owsp/js";

convert(1500, "mm", "m", "length");    // 1.5
convert(500, "g", "kg", "mass");       // 0.5
convert(5000, "cm2", "m2", "area");    // 0.5
```

- Type-safe category-specific functions:

```ts
import { convertLength, convertMass, convertArea, convertVolume } from "@owsp/js";

convertLength(1500, "mm", "m");        // 1.5
convertMass(500, "g", "kg");           // 0.5
convertArea(5000, "cm2", "m2");        // 0.5
convertVolume(250, "l", "m3");         // 0.25
```

Supported categories:
- **Length**: mm, cm, m, in, ft
- **Area**: mm², cm², m²
- **Mass**: g, kg
- **Volume**: l, m³
- **Countable**: pcs, pkg, pallet, roll, sheet, set

Details: `../../docs/unit-conversions.md`.

## Build & test

```pwsh
nx build js
nx test js
```

## Projection & Sorting Details

- Deep projection: `extractBySerializeGroup()` walks nested objects/arrays, resolving `$ref`, `oneOf`, and `allOf` to collect fields annotated with `x-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 `glazing` assemblies, inner arrays `glazings[*].seals` and `glazings[*].glazing_beads` project as `{vendor, id}` when no `x-partial` fields are present, with `thickness` included 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 their `x-partial` annotations.

## Hashed Fields Command

Use `printHashedFieldsBySerializeGroup()` when you want to inspect exactly which projection paths participate in hash generation for concrete input data.

```ts
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:

```pwsh
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-delight
```

Script 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:

- `--input` path to input JSON file.
- `--alias` schema title (default: `Material Profile System`).
- `--group` serialize group (default: `default`).
- `--outDir` output directory (default: `tmp`).
- `--outPrefix` output files prefix (default: `projection-delight`).