Dorian 0d073fa89e Add comprehensive installation and setup documentation
- Add GETTING_STARTED.md with quick start guide and development modes
- Add INSTALL.sh automated installation script
- Add INSTALLATION_CHECKLIST.md, INSTALLATION_SUCCESS.md, and INSTALLATION_SUMMARY.md
- Add QUICK_REFERENCE.md for common commands
- Add SETUP_GUIDE.md with detailed setup instructions
- Update README.md with improved project overview
- Add did-wallet app dependencies and node_modules
2026-01-27 17:18:21 +00:00

139 lines
4.2 KiB
JavaScript

import { decodeErrPrefix } from './common.js';
import { Type } from './token.js';
import {
jump,
quick
} from './jump.js';
const defaultDecodeOptions = {
strict: false,
allowIndefinite: true,
allowUndefined: true,
allowBigInt: true
};
class Tokeniser {
constructor(data, options = {}) {
this.pos = 0;
this.data = data;
this.options = options;
}
done() {
return this.pos >= this.data.length;
}
next() {
const byt = this.data[this.pos];
let token = quick[byt];
if (token === undefined) {
const decoder = jump[byt];
if (!decoder) {
throw new Error(`${ decodeErrPrefix } no decoder for major type ${ byt >>> 5 } (byte 0x${ byt.toString(16).padStart(2, '0') })`);
}
const minor = byt & 31;
token = decoder(this.data, this.pos, minor, this.options);
}
this.pos += token.encodedLength;
return token;
}
}
const DONE = Symbol.for('DONE');
const BREAK = Symbol.for('BREAK');
function tokenToArray(token, tokeniser, options) {
const arr = [];
for (let i = 0; i < token.value; i++) {
const value = tokensToObject(tokeniser, options);
if (value === BREAK) {
if (token.value === Infinity) {
break;
}
throw new Error(`${ decodeErrPrefix } got unexpected break to lengthed array`);
}
if (value === DONE) {
throw new Error(`${ decodeErrPrefix } found array but not enough entries (got ${ i }, expected ${ token.value })`);
}
arr[i] = value;
}
return arr;
}
function tokenToMap(token, tokeniser, options) {
const useMaps = options.useMaps === true;
const obj = useMaps ? undefined : {};
const m = useMaps ? new Map() : undefined;
for (let i = 0; i < token.value; i++) {
const key = tokensToObject(tokeniser, options);
if (key === BREAK) {
if (token.value === Infinity) {
break;
}
throw new Error(`${ decodeErrPrefix } got unexpected break to lengthed map`);
}
if (key === DONE) {
throw new Error(`${ decodeErrPrefix } found map but not enough entries (got ${ i } [no key], expected ${ token.value })`);
}
if (useMaps !== true && typeof key !== 'string') {
throw new Error(`${ decodeErrPrefix } non-string keys not supported (got ${ typeof key })`);
}
if (options.rejectDuplicateMapKeys === true) {
if (useMaps && m.has(key) || !useMaps && key in obj) {
throw new Error(`${ decodeErrPrefix } found repeat map key "${ key }"`);
}
}
const value = tokensToObject(tokeniser, options);
if (value === DONE) {
throw new Error(`${ decodeErrPrefix } found map but not enough entries (got ${ i } [no value], expected ${ token.value })`);
}
if (useMaps) {
m.set(key, value);
} else {
obj[key] = value;
}
}
return useMaps ? m : obj;
}
function tokensToObject(tokeniser, options) {
if (tokeniser.done()) {
return DONE;
}
const token = tokeniser.next();
if (token.type === Type.break) {
return BREAK;
}
if (token.type.terminal) {
return token.value;
}
if (token.type === Type.array) {
return tokenToArray(token, tokeniser, options);
}
if (token.type === Type.map) {
return tokenToMap(token, tokeniser, options);
}
if (token.type === Type.tag) {
if (options.tags && typeof options.tags[token.value] === 'function') {
const tagged = tokensToObject(tokeniser, options);
return options.tags[token.value](tagged);
}
throw new Error(`${ decodeErrPrefix } tag not supported (${ token.value })`);
}
throw new Error('unsupported');
}
function decode(data, options) {
if (!(data instanceof Uint8Array)) {
throw new Error(`${ decodeErrPrefix } data to decode must be a Uint8Array`);
}
options = Object.assign({}, defaultDecodeOptions, options);
const tokeniser = options.tokenizer || new Tokeniser(data, options);
const decoded = tokensToObject(tokeniser, options);
if (decoded === DONE) {
throw new Error(`${ decodeErrPrefix } did not find any content to decode`);
}
if (decoded === BREAK) {
throw new Error(`${ decodeErrPrefix } got unexpected break`);
}
if (!tokeniser.done()) {
throw new Error(`${ decodeErrPrefix } too many terminals, data makes no sense`);
}
return decoded;
}
export {
Tokeniser,
tokensToObject,
decode
};