📄 src/models/pid.ts
D-OPEN SOVEREIGN
1/*
2 * Copyright (c) 2026 DataPond D-Library Pty Ltd. ("The Sanctuary")
3 * Non-Profit Public Library — The World Library
4 *
5 * D-SAFE Certification issued by POND Enterprise.
6 * Published under the D-Open Code Sovereign Licence v1.0 framework
7 * DataPond D-Library All exclusive Right reserved on modifiying the code - CC Attribution to data pond, Non modifiable, Non Commercial.
8 * https://registry.world.bibliotech.com/licence
9 * Source code donated to DataPond D-Library by it's author: data pond.
10 *
11 * Technical Guardian ("The Shield"): Pond Enterprise Pty Ltd. (ACN 694 747 987)
12 * All liability claims about D-SAFE certification issues coming from the published D-Safe direction must be addressed with Pond Enterprise Pty Ltd.
13 * Code Modification automatically voids the certified liability protection provided by Pond Enterprise.
14 */
15const BASE34_ALPHABET = '0123456789ABCDEFGHJKLMNPQRSTUVWXYZ';
16const ISO_ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*';
17
18function computeMod37_2(input: string): string {
19 let p = 0;
20 for (let i = 0; i < input.length; i++) {
21 const val = ISO_ALPHABET.indexOf(input[i]);
22 if (val === -1) {
23 throw new Error(`[pid] Invalid character for ISO 7064 MOD 37-2: ${input[i]}`);
24 }
25 let s = (p + val) % 37;
26 if (s === 0) s = 37;
27 p = (s * 2) % 37;
28 }
29 const checkVal = (38 - p) % 37;
30 let checkChar = ISO_ALPHABET[checkVal];
31
32 if (checkChar === '*') checkChar = 'X';
33 if (checkChar === 'I') checkChar = 'J';
34 if (checkChar === 'O') checkChar = 'P';
35
36 return checkChar;
37}
38
39export function base34(wireId: number): string {
40 if (!Number.isInteger(wireId) || wireId < 0 || wireId > 4294967295) {
41 throw new Error(`[pid] wireId ${wireId} must be an unsigned 32-bit integer`);
42 }
43 if (wireId === 0) return '0000000';
44
45 let str = '';
46 let val = wireId;
47 while (val > 0) {
48 str = BASE34_ALPHABET[val % 34] + str;
49 val = Math.floor(val / 34);
50 }
51 return str.padStart(7, '0');
52}
53
54export function unbase34(str: string): number {
55 if (str.length !== 7) {
56 throw new Error(`[pid] Base34 body must be exactly 7 characters, got ${str.length}`);
57 }
58 let val = 0;
59 for (let i = 0; i < str.length; i++) {
60 const idx = BASE34_ALPHABET.indexOf(str[i]);
61 if (idx === -1) {
62 throw new Error(`[pid] Invalid character in base-34 body: ${str[i]}`);
63 }
64 val = val * 34 + idx;
65 }
66 return val;
67}
68
69export function encodePID(typePrefix: string, wireId: number): string {
70 if (typePrefix.length !== 2) {
71 throw new Error(`[pid] typePrefix must be exactly 2 characters`);
72 }
73 const body = base34(wireId);
74 const check = computeMod37_2(typePrefix + body);
75 return typePrefix + body + check;
76}
77
78export function decodePID(typePrefix: string, pid: string): { wireId: number } {
79 if (!pid.startsWith(typePrefix)) {
80 throw new Error(`[pid] PID ${pid} does not start with expected prefix ${typePrefix}`);
81 }
82 if (pid.length !== 10) {
83 throw new Error(`[pid] PID must be exactly 10 characters long, got ${pid.length}`);
84 }
85
86 const body = pid.slice(2, 9);
87 const providedCheck = pid[9];
88
89 const expectedCheck = computeMod37_2(typePrefix + body);
90 if (providedCheck !== expectedCheck) {
91 throw new Error(`[pid] Invalid check character in PID ${pid}. Expected ${expectedCheck}`);
92 }
93
94 return { wireId: unbase34(body) };
95}
96