# atom.io/foundations/canonical

Source: docs/source/pages/docs/foundations/canonical.mdx
URL: /docs/foundations/canonical

# <low-emphasis>atom.io</low-emphasis>/foundations/canonical

Byte-stable JSON-like keys and compact packing helpers for storing those keys as strings.

A `Canonical` value is a JSON value that only has one representation in bytes.

Plain objects are not canonical. Object property order is not a key contract: the same
logical object can be serialized in different property orders, which means different
bytes. TypeScript's duck typing is also a problem here because extra runtime properties
can exist even when the type you meant to use does not mention them.

The [JSON `primitive` type](/docs/foundations/json) is canonical. A readonly array is
canonical when every value inside it is canonical, so tuples are the usual shape for
compound keys.

### canonical values
Source: docs/source/exhibits/foundations/canonical/canonical-values.ts

```ts
import type { Canonical } from "atom.io/foundations/canonical"

const primitiveKey = `road-trip` satisfies Canonical
const tupleKey = [`playlist`, `road-trip`, 3] as const satisfies Canonical
const nestedKey = [
	`track`,
	[`playlist`, `road-trip`],
] as const satisfies Canonical

// @ts-expect-error Objects are not Canonical values.
const objectKey = { playlist: `road-trip`, index: 3 } satisfies Canonical
```

### compound key
Source: docs/source/exhibits/foundations/canonical/compound-key.ts

```ts
import {
	type Canonical,
	packCanonical,
	unpackCanonical,
} from "atom.io/foundations/canonical"

type TrackKey = readonly [playlistId: string, trackId: string]

const key = [`road-trip`, `dreams`] as const satisfies Canonical
const packed = packCanonical<TrackKey>(key)
const unpacked = unpackCanonical(packed)
```

## package contents

<table-wrapper>

| Export | Description |
| --- | --- |
| `Canonical` | A primitive or readonly array of canonical values. |
| `packed` | A string branded with the canonical value it represents. |
| `packCanonical` | Convert a canonical value into a compact string key. |
| `unpackCanonical` | Recover a canonical value from a packed key. |

</table-wrapper>
