deno.land / x / replicache@v10.0.0-beta.0 / hash.ts

نووسراو ببینە
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import {assert} from './asserts';import {encode} from './base32-encode';import type {ReadonlyJSONValue} from './json';import * as utf8 from './utf8';
export const BYTE_LENGTH = 20;
export const STRING_LENGTH = 32;
// We use an opaque type so that we can make sure that a hash is always a hash.// TypeScript does not have direct support but we can use a trick described// here://// https://evertpot.com/opaque-ts-types///// The basic idea is to declare a type that cannot be created. We then use// functions that cast a string to this type.//
// By using declare we tell the type system that there is a unique symbol.// However, there is no such symbol but the type system does not care.declare const hashTag: unique symbol;
/** * Opaque type representing a hash. The only way to create one is using `parse` * or `hashOf` (except for static unsafe cast of course). */export type Hash = {[hashTag]: true};
const hashRe = /^[0-9a-v]{32}$/;const tempHashRe = /^t\/[0-9a-v]{30}$/;
/** * Computes a SHA512 hash of the given data. */export async function hashOf(value: ReadonlyJSONValue): Promise<Hash> { const typedArray = utf8.encode(JSON.stringify(value)); const buf = await crypto.subtle.digest('SHA-512', typedArray); const buf2 = new Uint8Array(buf, 0, BYTE_LENGTH); return encode(buf2) as unknown as Hash;}
export function parse(s: string): Hash { assertHash(s); return s;}
export const emptyHash = '00000000000000000000000000000000' as unknown as Hash;
// Temp hashes needs to have the same length as non temp hashes. This is// important because we split B+Tree nodes based on the size and we want the// size to be the same independent of whether the hash is temp or not.
export const newTempHash = makeNewTempHashFunction();
/** * Creates a new temp hash function. * @param prefix The prefix of the hash. If left out the prefix is 't/' which * signifies a temp hash. */export function makeNewTempHashFunction(): () => Hash { return makeNewFakeHashFunction('t/');}
/** * Creates a new fake hash function. * @param prefix The prefix of the hash. If the prefix starts with 't/' it is * considered a temp hash. */export function makeNewFakeHashFunction(hashPrefix: string): () => Hash { let tempHashCounter = 0; return () => { // Must not overlap with hashOf results return (hashPrefix + (tempHashCounter++) .toString() .padStart(STRING_LENGTH - hashPrefix.length, '0')) as unknown as Hash; };}
export function isHash(v: unknown): v is Hash { return typeof v === 'string' && (hashRe.test(v) || tempHashRe.test(v));}
export function isTempHash(v: unknown): v is Hash { return typeof v === 'string' && tempHashRe.test(v);}
export function assertNotTempHash(hash: Hash): void { if (tempHashRe.test(hash as unknown as string)) { throw new Error('Unexpected temp hash'); }}
export function assertHash(v: unknown): asserts v is Hash { if (!isHash(v)) { throw new Error(`Invalid hash: '${v}'`); }}
/** * Generates a fake hash useful for testing. */export function fakeHash(s: string): Hash { const fake = 'fake'; assert( /^[a-v0-9]*$/.test(s), `Fake hash must be a valid substring of a hash: ${s}`, ); assert(s.length <= STRING_LENGTH - fake.length, 'Fake hash is too long'); return (fake + s.padStart(STRING_LENGTH - fake.length, '0')) as unknown as Hash;}
replicache

Version Info

Tagged at
2 years ago