npm package: @pryv/stable-object-representation
Description: Stable JSON Object representation
Author: Pryv S.A.
Latest version: 1.1.4

# Pryv Javascript tools to create checksums on Pryv.io data item 

It provides utility to 
1. Create a stable representation of a JSON object (all keys are ordered)
2. Makes a Hash string of this stable representation (default SHA256)
3. Creates a unique key to finds back the data item

## Usage & Format

### stringify: `stableRepresentation.event.stringify(event)` 

Returns a "stable" JSON reprersentation of an event as described bellow.

### hash: `stableRepresentation.event.hash(event, [algorithm])` 

Returns a **hash** of "stable" JSON reprersentation of an event, prefixed with 'EVENT:O' and encoded in base64 following **subresource integrity (SRI)** specifcation [See W3.org/TR/SRI](https://www.w3.org/TR/SRI/). By default SHA256 is used.

Example: `EVENT:0:sha256-X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=` 

### key: `stableRepresentation.event.key(event)` 

Returns a **unique key** for this event version.

### compute: `stableRepresentation.event.compute(event, [algorithm])` 

Returns an object equivalent of `{ key: key(event), hash: hash(event, algorithm) }`

```
{
  key: 'EVENT:0:ckoxy0giw000hrs9yvi3rnlfm:1621578570.151',
  integrity: 'EVENT:0:sha256-X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE='
}
```

### General rules to create stable representation of Pryv data

In this example we will use javascript as pseudo code examples. 

 

- Representation is a valid JSON object. `JSON.parse(stableRep)` must be valid. (`stableRep;` is a stable representation of a JSON object as a string.)
- JSON representation should not have any 'spaces' between seprators. 
`{"keyA":"valueA","keyB":["item0B","item1B"]}` is valid,   
while `{"keyA": "valueA", "keyB": ["item0B", "item1B"]}` is not. 
- Numbers fields are represented without `"`.  
`{"keyForNumericalValue":12.23}` is valid,  
while `{"keyForNumericalValue":"12.23"}` is not.
- Boolean fields are represented without `"`.  
`{"keyForBooleanValue":false}` is valid,  
while `{"keyForBooleanValue":"false"}` is not.
- Array are kept is their original order. If order is modified stable representation differs. 
- Object fields are ordered by their UTF16 characters codes value.
The folowing code exposes the logic implemented in javascript to be consumed by a `sort()` function. 

```javascript
/**
 * Compare two strings based on the UTF16 code value of their characters.
 * Characters are consumed one by one on each string. 
 * if the shortest string equals the first characters of the longest one, the shortest is before.
 * @param a
 * @param b
 * @returns {number} -1 if a is before b, 1 if b is before a, 0 if they are equals.
 */
function utf16StrCompare(a, b) {
  var lA = a.length;
  var lB = b.length;
  // default A is shorter and equals firstsB chars: A first
  var l = lA;
  var dres = -1;
  // B is shorter and equals firstsA chars: B first
  if (lA > lB) { l = lB; dres = -1; }
  // A and B are the same size
  if (lA === lB) {dres = 0; }

  var res;
  for (var i = 0; i < l; i++) {
    res = a.charCodeAt(i) - b.charCodeAt(i);
    if (res !== 0) {
      return res;
    }
  }
  return dres;
}
```


## Event scheme hash: v0

. `key`
  . structure: `{item code}:{item code version}:{item id}:{item version}` usually the item version is the `modified` date property
  . They key is unique it is used to find a corresponding checksum.
. `integrity`
  . structure: `{item code}:{item code version}:{algorithm}-{checksum base64 encode}`

### Item code versionning (key)

. `EVENT:0:` correspond to `item id = event.id` and `item version = event.modified`
  . If `event.modified` is not present, then `event.deleted` is used.
. `ACCESS:0:` correspond to `item id =access.id` and `item version = access.modified`
  . If `access.modified` is not present, then `access.deleted` is used.

### Ckecksum versionning (hash)

. `EVENT:0:` correspond to encoding of current implementation of `stableRepresentation.event.stringify0(event)`
. `ACCESS:0:` correspond to encoding of current implementation of `stableRepresentation.access.stringify0(access)`

### Properties expected in an access object

Properties with a null values, will be ignored from the representation. 

Special attention is required for 

- **integrity**: Obviously integrity is not taken for computation... 
- **apiEndpoint**: is ignored from computation (full domain name is part of settings not data)
- **lastUsed**: is ignored as too dynamic...
- **calls**: is ignored as too dynamic...

### Properties expected in an event object

Properties with a null values, will be ignored from the representation. 

Special attention is required for 

- **integrity**: Obviously integrity is not taken for computation... 
- **tags**: !! Are always ignored as deprecated and now included in streamIds
- **trashed**: if `false` is ignored from the representation.
- **deleted**: if `null` is ignored from the representation.
- **duration**: if `0` is ignored from the representation <=> to `undefined`.
- **streamId**: if exists is replaced by `streamIds` whith the value `[streamId]`
- **attachments.readToken**: is ignored in the representation.

**Example**

```javascript
{
  "id": "ciusga35r000sgwg4o1sr1j5q",
  "time": 1477575221.247,
  "streamId": "diary",
  "duration": 0,
  "type": "picture/attached",
  "tags": [],
  "description":  "test\"te\"st",
  "attachments": [
    {
      "id": "ciusga35r000tgwg4hcz2i22u",
      "fileName": "photo.jpg",
      "type": "image/jpeg",
      "size": 2561,
      "readToken": "cjasdashdhgad-asdjhasdhsdh"
    },
    {
      "id": "ciusga35r000tgwg4hcz2i32u",
      "fileName": "photo.jpg",
      "type": "image/jpeg",
      "size": 2561,
      "readToken": "cjasdashdhgad-asdjhasdhsdh"
    }
  ],
  "created": 1477575221.247,
  "createdBy": "ciusga33w0004gwg436uhtqs2",
  "modified": 1477575221.247,
  "modifiedBy": "ciusga33w0004gwg436uhtqs2",
  "trashed": false,
  "clientData": {
    "key2": "value2",
    "key1": "value1"
  }
}
```

Stable representation:

```javascript
{"attachments":[{"fileName":"photo.jpg","id":"ciusga35r000tgwg4hcz2i22u","size":2561,"type":"image/jpeg"},{"fileName":"photo.jpg","id":"ciusga35r000tgwg4hcz2i32u","size":2561,"type":"image/jpeg"}],"clientData":{"key1":"value1","key2":"value2"},"created":1477575221.247,"createdBy":"ciusga33w0004gwg436uhtqs2","description":"test\"te\"st","id":"ciusga35r000sgwg4o1sr1j5q","modified":1477575221.247,"modifiedBy":"ciusga33w0004gwg436uhtqs2","streamId":"diary","time":1477575221.247,"type":"picture/attached"}
```

##


# License
Copyright (C) 2020-2021 Pryv S.A. https://pryv.com 
This file is part of Open-Pryv.io and released under BSD-Clause-3 License
SPDX-License-Identifier: BSD-3-Clause