@ibgib/ts-gib 0.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/.vscode/launch.json +24 -0
  2. package/.vscode/settings.json +34 -0
  3. package/.vscode/tasks.json +37 -0
  4. package/CHANGELOG.md +159 -0
  5. package/README.md +502 -0
  6. package/dist/V1/constants.d.mts +22 -0
  7. package/dist/V1/constants.d.mts.map +1 -0
  8. package/dist/V1/constants.mjs +21 -0
  9. package/dist/V1/constants.mjs.map +1 -0
  10. package/dist/V1/factory.d.mts +23 -0
  11. package/dist/V1/factory.d.mts.map +1 -0
  12. package/dist/V1/factory.mjs +78 -0
  13. package/dist/V1/factory.mjs.map +1 -0
  14. package/dist/V1/index.d.mts +6 -0
  15. package/dist/V1/index.d.mts.map +1 -0
  16. package/dist/V1/index.mjs +6 -0
  17. package/dist/V1/index.mjs.map +1 -0
  18. package/dist/V1/sha256v1.d.mts +19 -0
  19. package/dist/V1/sha256v1.d.mts.map +1 -0
  20. package/dist/V1/sha256v1.mjs +86 -0
  21. package/dist/V1/sha256v1.mjs.map +1 -0
  22. package/dist/V1/transforms/fork.d.mts +16 -0
  23. package/dist/V1/transforms/fork.d.mts.map +1 -0
  24. package/dist/V1/transforms/fork.mjs +111 -0
  25. package/dist/V1/transforms/fork.mjs.map +1 -0
  26. package/dist/V1/transforms/index.d.mts +5 -0
  27. package/dist/V1/transforms/index.d.mts.map +1 -0
  28. package/dist/V1/transforms/index.mjs +5 -0
  29. package/dist/V1/transforms/index.mjs.map +1 -0
  30. package/dist/V1/transforms/mut8.d.mts +50 -0
  31. package/dist/V1/transforms/mut8.d.mts.map +1 -0
  32. package/dist/V1/transforms/mut8.mjs +246 -0
  33. package/dist/V1/transforms/mut8.mjs.map +1 -0
  34. package/dist/V1/transforms/rel8.d.mts +14 -0
  35. package/dist/V1/transforms/rel8.d.mts.map +1 -0
  36. package/dist/V1/transforms/rel8.mjs +176 -0
  37. package/dist/V1/transforms/rel8.mjs.map +1 -0
  38. package/dist/V1/transforms/transform-helper.d.mts +92 -0
  39. package/dist/V1/transforms/transform-helper.d.mts.map +1 -0
  40. package/dist/V1/transforms/transform-helper.mjs +189 -0
  41. package/dist/V1/transforms/transform-helper.mjs.map +1 -0
  42. package/dist/V1/types.d.mts +79 -0
  43. package/dist/V1/types.d.mts.map +1 -0
  44. package/dist/V1/types.mjs +12 -0
  45. package/dist/V1/types.mjs.map +1 -0
  46. package/dist/helper.d.mts +77 -0
  47. package/dist/helper.d.mts.map +1 -0
  48. package/dist/helper.mjs +179 -0
  49. package/dist/helper.mjs.map +1 -0
  50. package/dist/index.cjs +4 -0
  51. package/dist/index.cjs.map +1 -0
  52. package/dist/index.d.cts +4 -0
  53. package/dist/index.d.cts.map +1 -0
  54. package/dist/index.d.mts +4 -0
  55. package/dist/index.d.mts.map +1 -0
  56. package/dist/index.mjs +4 -0
  57. package/dist/index.mjs.map +1 -0
  58. package/dist/types.d.mts +242 -0
  59. package/dist/types.d.mts.map +1 -0
  60. package/dist/types.mjs +2 -0
  61. package/dist/types.mjs.map +1 -0
  62. package/jasmine-browser.json +18 -0
  63. package/jasmine.json +6 -0
  64. package/package.json +61 -0
  65. package/src/V1/constants.mts +23 -0
  66. package/src/V1/factory.mts +110 -0
  67. package/src/V1/factory.spec.mts +162 -0
  68. package/src/V1/index.mts +5 -0
  69. package/src/V1/sha256v1.mts +85 -0
  70. package/src/V1/sha256v1.spec.mts +221 -0
  71. package/src/V1/transforms/fork.mts +100 -0
  72. package/src/V1/transforms/fork.spec.mts +410 -0
  73. package/src/V1/transforms/index.mts +4 -0
  74. package/src/V1/transforms/mut8.mts +239 -0
  75. package/src/V1/transforms/mut8.spec.mts +656 -0
  76. package/src/V1/transforms/rel8.mts +173 -0
  77. package/src/V1/transforms/rel8.spec.mts +556 -0
  78. package/src/V1/transforms/transform-helper.mts +263 -0
  79. package/src/V1/transforms/transform-helper.spec.mts +45 -0
  80. package/src/V1/types.mts +84 -0
  81. package/src/helper.mts +192 -0
  82. package/src/helper.spec.mts +127 -0
  83. package/src/index.cts +3 -0
  84. package/src/index.mts +3 -0
  85. package/src/types.mts +242 -0
  86. package/tsconfig.json +15 -0
  87. package/tsconfig.test.json +10 -0
@@ -0,0 +1,263 @@
1
+ import { TransformOpts, IbGib, Gib, Ib, IbGibAddr, } from "../../types.mjs";
2
+ import { clone, getIbGibAddr, getIbAndGib, HashAlgorithm, extractErrorMsg, } from "../../helper.mjs";
3
+ import { sha256v1, } from "../sha256v1.mjs";
4
+ import { GibInfo, IbGib_V1, } from "../types.mjs";
5
+ import { IBGIB_DELIMITER, GIB, GIB_DELIMITER } from "../constants.mjs";
6
+
7
+ export async function buildDna<TSrc extends IbGib_V1, TOpts extends TransformOpts<TSrc>>(
8
+ opts: TOpts,
9
+ ): Promise<IbGib_V1> {
10
+ const transformData: TOpts = clone(opts);
11
+ let lc = `[${buildDna.name}]`;
12
+
13
+ // remove references to the srcAddr, as this will be captured
14
+ // by the ibGib's `past` rel8n. This way, we can reapply dna
15
+ // to different src's more easily, as well as have less unique dna
16
+ if (transformData.srcAddr) { delete transformData.srcAddr; }
17
+
18
+ // remove all references to actual objects in this, we just
19
+ // want the data. ATOW, src is a reference to the src object
20
+ // and we only want the srcAddr
21
+ delete transformData.src;
22
+
23
+ // dna is never timestamped or uniquely identified hmm...
24
+ // or rel8d to anything...hmmm
25
+ // so much dna, best to minimize though of course we dont
26
+ // want to prematurely optimize...but there's a looooot of dna.
27
+ // best to share/reuse as much as possible.
28
+ const result: IbGib_V1 = {
29
+ ib: transformData.type!,
30
+ data: transformData,
31
+ rel8ns: {
32
+ ancestor: [
33
+ `${transformData.type!.toString()}${IBGIB_DELIMITER}${GIB}` // e.g. fork^gib
34
+ ]
35
+ }
36
+ };
37
+
38
+ result.gib = await sha256v1(result);
39
+
40
+ return result;
41
+ }
42
+
43
+ export function isDna({ ibGib }: { ibGib: IbGib }): boolean {
44
+ const lc = `[${isDna.name}]`;
45
+ try {
46
+ if (!ibGib) { throw new Error(`ibGib required.`); }
47
+
48
+ // console.log(`${lc} ibGib: ${pretty(ibGib)}`);
49
+
50
+ // ancestor is known transform is the best way for v1 ATOW
51
+ const knownTransformPrimitiveAddrs =
52
+ ['fork', 'mut8', 'rel8', 'plan'].map(x => `${x}^${GIB}`); // plan from prev versions
53
+ const hasTransformAncestor =
54
+ ((ibGib as any).rel8ns) &&
55
+ ((ibGib as any).rel8ns.ancestor) &&
56
+ (Array.isArray((ibGib as any).rel8ns.ancestor)) &&
57
+ ((((ibGib as any).rel8ns.ancestor) as any) as any).
58
+ some((x: any) => knownTransformPrimitiveAddrs.includes(x));
59
+
60
+ return hasTransformAncestor || false;
61
+ } catch (error) {
62
+ console.error(`${lc} ${extractErrorMsg(error)}`);
63
+ throw error;
64
+ }
65
+ }
66
+
67
+ export function isPrimitive({ ibGib, gib }: { ibGib?: IbGib_V1, gib?: Gib }): boolean {
68
+ if (ibGib) {
69
+ return isPrimitive({ gib: ibGib.gib });
70
+ } else if (gib) {
71
+ return gib === GIB;
72
+ } else {
73
+ // falsy gib means it's primitive or a programming error...hmm
74
+ return true;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Generates the gib of a given `ibGib` depending on
80
+ * @returns gib - either bare hash for those with no tjp; or tjp-scoped hash; or 'gib' for primitives
81
+ */
82
+ export async function getGib({
83
+ ibGib,
84
+ hasTjp,
85
+ tjpAddr,
86
+ gibDelimiter,
87
+ isPrimitive,
88
+ hashAlgorithm,
89
+ }: {
90
+ /**
91
+ * We'll take the `ib`, `data`, and `rel8ns` from this
92
+ */
93
+ ibGib: IbGib_V1,
94
+ /**
95
+ * If true, then we'll treat this as having a tjp.
96
+ * If falsy, then we'll check the internals of the ibGib to see if it has a tjp.
97
+ */
98
+ hasTjp?: boolean;
99
+ /**
100
+ * @deprecated
101
+ *
102
+ * IGNORED
103
+ *
104
+ * This is now always searched for in the ibGib itself.
105
+ */
106
+ tjpAddr?: IbGibAddr,
107
+ /**
108
+ * What to use as the delimiter for the `gib`.
109
+ *
110
+ * @default GIB_DELIMITER
111
+ *
112
+ * ## notes
113
+ *
114
+ * This is not the same thing necessarily as the ib^gib delimiter.
115
+ * In fact the default gibDelimiter ATOW ('.') is different than the
116
+ * default ib^gib delimiter ('^').
117
+ */
118
+ gibDelimiter?: string,
119
+ /**
120
+ * If this is primitive, then the `gib` is always GIB ("gib" string literal).
121
+ */
122
+ isPrimitive?: boolean,
123
+ /**
124
+ * Hash algorithm to use when calculating the gib hash of the ibgib datum.
125
+ *
126
+ * ## notes
127
+ *
128
+ * * I'm just including this now to show where we will expand when working with
129
+ * multiple hash algorithms. For now though, in all of V1, we use 'SHA-256'
130
+ * when calculating the gib hashes. This is why I'm hard-coding the type
131
+ * here instead of using this lib's `HashAlgorithm` type.
132
+ * * This param is actually ignored ATOW.
133
+ * * In my current consuming use case (ionic-gib), though,
134
+ * I am indeed using SHA-512 when encrypting using encrypt-gib. I only note
135
+ * this in case someone sees "SHA-512" code somewhere and is confused.
136
+ */
137
+ hashAlgorithm?: 'SHA-256',
138
+ }): Promise<string> {
139
+ const lc = `[${getGib.name}]`;
140
+ try {
141
+ if (!ibGib) { throw new Error(`ibGib required. (E: 17d073226b9d42fd841e5a94b065ef21)`); }
142
+ if (isPrimitive) { return GIB; }
143
+ const ibGibHash = await sha256v1(ibGib, '');
144
+ const rel8ns = ibGib.rel8ns ?? {};
145
+ const data = ibGib.data ?? {};
146
+ gibDelimiter = gibDelimiter || GIB_DELIMITER;
147
+ if (!hasTjp) { hasTjp = (rel8ns.tjp?.length ?? []) > 0 || data.isTjp || false; }
148
+ if (hasTjp) {
149
+ let tjpAddrGib: string | undefined;
150
+ if (rel8ns.tjp) {
151
+ if (rel8ns.tjp.length === 1) {
152
+ if (rel8ns.tjp[0]) { // checking for empty string
153
+ tjpAddr = rel8ns.tjp[0];
154
+ } else {
155
+ throw new Error(`rel8ns.tjp[0] is falsy. (E: ed879d2b039543f8b1902e8b7b5a5a7b)`);
156
+ }
157
+ } else if (rel8ns.tjp.length > 1) {
158
+ if (rel8ns.tjp[rel8ns.tjp.length - 1]) {
159
+ console.warn(`${lc} found more than one tjp addr...only expecting 1 ATOW. (W: 10ed43f716e743e0afd1954f1ab46789)`);
160
+ tjpAddr = rel8ns.tjp[rel8ns.tjp.length - 1];
161
+ } else {
162
+ throw new Error(`multiple tjp addrs, and the last (most recent) one is falsy. (E: bc835dc89be24075bba8b2b6616ea069)`);
163
+ }
164
+ } else {
165
+ // empty rel8ns.tjp array?
166
+ throw new Error(`hasTjp is true but rel8ns.tjp is empty array. (E: d08b2f9e86494814b5e7d7b4602b2ab7)`);
167
+ }
168
+ } else if (data.isTjp) {
169
+ // the ibGib itself is the tjp
170
+ tjpAddr = getIbGibAddr({ ib: ibGib.ib, gib: ibGibHash });
171
+ } else {
172
+ throw new Error(`hasTjp is true, but both ibGib.rel8ns.tjp and ibGib.data.isTjp are falsy. (E: 4e246897e52044789594d853bb5b66ee)`)
173
+ }
174
+ tjpAddrGib = tjpAddr ? getIbAndGib({ ibGibAddr: tjpAddr }).gib : undefined;
175
+
176
+ if (tjpAddrGib) {
177
+ // if the ibGib IS the tjp, then the gib is only the hash
178
+
179
+ // if the ibGib is NOT the tjp, then the gib is the hash plus
180
+ // tjpGib. note in the future, if multiple tjps are going, then
181
+ // tjpAddrGib itself may have another delim inside it, so we
182
+ // will end up with a gib with multiple delimiters.
183
+ return data.isTjp ? ibGibHash : `${ibGibHash}${GIB_DELIMITER}${tjpAddrGib}`;
184
+ } else {
185
+ throw new Error(`hasTjp is true but could not find tjpAddrGib. (E: 1863df626b754744a1d431a683cb0ba0)`);
186
+ }
187
+ } else {
188
+ // no tjp, so gib is just the hash
189
+ return ibGibHash;
190
+ }
191
+ } catch (error) {
192
+ console.error(`${lc} ${extractErrorMsg(error)}`);
193
+ throw error;
194
+ }
195
+
196
+ }
197
+
198
+ /**
199
+ * Parses gib (either via `gib` param or `ibGibAddr.gib`) and returns information
200
+ * about it.
201
+ *
202
+ * @returns Information about the given `gib` (or `gib` extracted from `ibGibAddr`)
203
+ */
204
+ export function getGibInfo({
205
+ ibGibAddr,
206
+ gib,
207
+ gibDelimiter,
208
+ }: {
209
+ /**
210
+ * If given, will extract `gib` from this.
211
+ */
212
+ ibGibAddr?: IbGibAddr,
213
+ /**
214
+ * `gib` to analyze.
215
+ */
216
+ gib?: Gib,
217
+ /**
218
+ * Delimiter among pieces of gib, if applicable.
219
+ *
220
+ * @default GIB_DELIMITER (ATOW '.')
221
+ *
222
+ * ## notes
223
+ *
224
+ * Some `gib` values will include tjp information, while others
225
+ * are just the hash.
226
+ */
227
+ gibDelimiter?: string,
228
+ }): GibInfo {
229
+ const lc = `[${getGibInfo.name}]`;
230
+ try {
231
+ if (!ibGibAddr && !gib) { throw new Error(`Either ibGibAddr or gib required. (E: 25e3dcbe63cd44909032df12af9df75e)`); }
232
+ gib = gib || getIbAndGib({ ibGibAddr }).gib;
233
+
234
+ if (gib === GIB) { return { isPrimitive: true } }
235
+
236
+ gibDelimiter = gibDelimiter ?? GIB_DELIMITER;
237
+
238
+ if (gib.includes(gibDelimiter)) {
239
+ const pieces = gib.split(gibDelimiter);
240
+ if (pieces.some(p => p === '')) { throw new Error(`unexpected gib that contains gibDelimiter (${gibDelimiter}) but has at least one piece with empty string. (E: 75a94280045541009ee68182d12d3449)`); }
241
+
242
+ const piecesCount = pieces.length;
243
+ if (piecesCount > 2) { console.warn(`${lc} gib only expected to have two pieces ATOW. re-examine please. (W: aa4283ac5a5747a386a69966ecdad39d)`); }
244
+
245
+ const punctiliarHashPiece = pieces.splice(0, 1);
246
+ return {
247
+ punctiliarHash: punctiliarHashPiece[0],
248
+ tjpGib: pieces.join(gibDelimiter), // after splice, piece/s is/are tjp
249
+ piecesCount,
250
+ delimiter: gibDelimiter,
251
+ }
252
+ } else {
253
+ return {
254
+ punctiliarHash: gib,
255
+ piecesCount: 1,
256
+ delimiter: gibDelimiter,
257
+ }
258
+ }
259
+ } catch (error) {
260
+ console.error(`${lc} ${extractErrorMsg(error)}`);
261
+ throw error;
262
+ }
263
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Test transform-helper.
3
+ */
4
+
5
+
6
+ import { IbGib_V1, IbGibRel8ns_V1, Rel8n } from '../types.mjs';
7
+ import { TransformOpts_Rel8, IbGibAddr, IbGibRel8ns } from '../../types.mjs';
8
+ import { pretty, clone, delay, getIbGibAddr } from '../../helper.mjs';
9
+ import { ROOT, ROOT_ADDR } from '../constants.mjs';
10
+ import { fork } from './fork.mjs';
11
+ // import { mut8 } from './mut8.mjs';
12
+ import { rel8 } from './rel8.mjs';
13
+ import { Factory_V1 as factory } from '../factory.mjs';
14
+ import { isDna } from './transform-helper.mjs';
15
+
16
+ const PRIMITIVE_IBGIBS = [
17
+ factory.root(),
18
+ ...factory.primitives({
19
+ ibs: [
20
+ 'a', '7', 'tag',
21
+ 'any string/value that isnt hashed with a gib is a primitive',
22
+ // e.g. 6 -> 6^ -> 6^gib are all equivalent ib^gib addresses,
23
+ ]
24
+ }),
25
+ ];
26
+
27
+ describe(`isDna`, () => {
28
+ for (const src of PRIMITIVE_IBGIBS) {
29
+
30
+ it(`should return true for dna ibgibs`, async () => {
31
+ const resFork = await fork({ src, dna: true });
32
+ resFork.dnas?.every(x => {
33
+ const resIsDna = isDna({ ibGib: x });
34
+ expect(resIsDna).toBeTrue()
35
+ });
36
+ });
37
+
38
+ it(`should return false for non-dna ibgibs`, async () => {
39
+ const resFork = await fork({ src, dna: true });
40
+ const resIsDna = isDna({ ibGib: resFork.newIbGib });
41
+ expect(resIsDna).toBeFalse();
42
+ });
43
+
44
+ }
45
+ });
@@ -0,0 +1,84 @@
1
+ /**
2
+ * The core of the simplicity of the ibGib protocol is that you are
3
+ * taking two (or more) ibGibs and producing other ibGibs.
4
+ *
5
+ * There are three primary functions: mut8, rel8, fork
6
+ *
7
+ * These are actually different aspects of the single function of
8
+ * relationship and time, either:
9
+ * 1) Creating a 'new' timeline, or...
10
+ * 2) Extending an existing one.
11
+ *
12
+ * Mut8 is intrinsic, rel8 is extrinsic, fork is a new timeline.
13
+ * Mut8 changes a timeline, rel8 changes a timeline's link(s),
14
+ * fork creates a new timeline.
15
+ */
16
+ import {
17
+ IbGibRel8ns, IbGibAddr, IbGibWithDataAndRel8ns, Gib,
18
+ } from '../types.mjs';
19
+
20
+ /**
21
+ * Need to see if I can remove this...
22
+ */
23
+ export declare type IbGibData_V1 = {
24
+ [key: string]: any;
25
+ isTjp?: boolean;
26
+ n?: number;
27
+ timestamp?: string;
28
+ uuid?: string;
29
+ };
30
+
31
+ /**
32
+ * Convenience enum to avoid spelling mistakes. (optional)
33
+ */
34
+ export enum Rel8n {
35
+ past = 'past',
36
+ ancestor = 'ancestor',
37
+ dna = 'dna',
38
+ identity = 'identity',
39
+ tjp = 'tjp',
40
+ }
41
+ export interface IbGibRel8ns_V1 extends IbGibRel8ns {
42
+ [Rel8n.past]?: IbGibAddr[];
43
+ [Rel8n.identity]?: IbGibAddr[];
44
+ [Rel8n.ancestor]?: IbGibAddr[];
45
+ [Rel8n.dna]?: IbGibAddr[];
46
+ [Rel8n.tjp]?: IbGibAddr[];
47
+ }
48
+ export interface IbGib_V1<TData = IbGibData_V1, TRel8ns extends IbGibRel8ns_V1 = IbGibRel8ns_V1>
49
+ extends IbGibWithDataAndRel8ns<TData, TRel8ns> {
50
+ }
51
+
52
+ export interface GibInfo {
53
+ /**
54
+ * Hash for this ibgib frame in time.
55
+ */
56
+ punctiliarHash?: string;
57
+ /**
58
+ * The gib for this ibgib's most recent tjp.
59
+ *
60
+ * ## notes
61
+ *
62
+ * ATOW, only one tjp expected really, though I've been coding
63
+ * with the possibility of having multiple tjp's similar to
64
+ * checkpoints.
65
+ */
66
+ tjpGib?: Gib;
67
+ /**
68
+ * If
69
+ */
70
+ piecesCount?: number;
71
+ /**
72
+ * If a delimiter is used in this gib, this is the delimiter.
73
+ *
74
+ * ## notes
75
+ *
76
+ * ATOW, the caller already knows the delimiter. But I'm thinking that
77
+ * I may be persisting this at some point and it would be good to include.
78
+ */
79
+ delimiter?: string;
80
+ /**
81
+ * True the gib is just 'gib' (GIB constant), else falsy.
82
+ */
83
+ isPrimitive?: boolean;
84
+ }
package/src/helper.mts ADDED
@@ -0,0 +1,192 @@
1
+ import { Ib, Gib, IbGib, IbGibAddr, IbAndGib } from './types.mjs';
2
+ let crypto: any = globalThis.crypto;
3
+ let { subtle } = crypto;
4
+
5
+ export type HashAlgorithm = 'SHA-256' | 'SHA-512';
6
+ export const HashAlgorithm: { [key: string]: HashAlgorithm } = {
7
+ 'sha_256': 'SHA-256' as HashAlgorithm,
8
+ 'sha_512': 'SHA-512' as HashAlgorithm,
9
+ }
10
+
11
+ export function clone(obj: any) {
12
+ return JSON.parse(JSON.stringify(obj));
13
+ }
14
+ export function getTimestamp() {
15
+ return (new Date()).toUTCString();
16
+ }
17
+ /**
18
+ * Gets the ib^gib address from the given ib and gib or
19
+ * from the ibGib object.
20
+ *
21
+ * Need to refactor to getIbGibAddr
22
+ */
23
+ export function getIbGibAddr({
24
+ ib, gib, ibGib, delimiter = '^'
25
+ }: {
26
+ ib?: Ib,
27
+ gib?: Gib,
28
+ ibGib?: IbGib,
29
+ delimiter?: string
30
+ }) {
31
+ ib = ib || ibGib?.ib || '';
32
+ gib = gib || ibGib?.gib || '';
33
+ return ib + delimiter + gib;
34
+ }
35
+
36
+ /**
37
+ * Get the ib and gib fields from an ibGib object or ibGibAddr
38
+ * with the given `delimiter`.
39
+ */
40
+ export function getIbAndGib({
41
+ ibGib,
42
+ ibGibAddr,
43
+ delimiter = '^'
44
+ }: {
45
+ ibGibAddr?: IbGibAddr,
46
+ ibGib?: IbGib,
47
+ delimiter?: string
48
+ }): IbAndGib {
49
+ const lc = '[getIbAndGib]';
50
+ if (!ibGibAddr) {
51
+ if (ibGib) {
52
+ ibGibAddr = getIbGibAddr({ ibGib });
53
+ } else {
54
+ throw new Error(`${lc} We need either an address or an ibGib object`);
55
+ }
56
+ }
57
+ if (!ibGibAddr) { throw new Error(`${lc} Couldn't get ibGibAddr. ibGib invalid?`); }
58
+
59
+ if (!delimiter) { delimiter = '^'; }
60
+
61
+ const pieces = ibGibAddr.split(delimiter);
62
+ if (pieces.length === 2) {
63
+ // normal v1 case, e.g. 'ib^gib' or 'tag home^ABC123'
64
+ return { ib: pieces[0], gib: pieces[1] };
65
+ } else if (pieces.length === 1 && ibGibAddr.endsWith(delimiter)) {
66
+ // normal v1 primitive, e.g. '7^' or 'name^'
67
+ return { ib: pieces[0], gib: '' };
68
+ } else if (pieces.length === 1 && ibGibAddr.startsWith(delimiter)) {
69
+ // only gib/hash is provided like maybe a binary file
70
+ // e.g. ^ABC123 or ^XYZ456 or ^some_gib_that_isnt_a_hash
71
+ return { ib: '', gib: pieces[0] };
72
+ } else if (pieces.length === 2 && pieces[0] === '' && pieces[1] === '') {
73
+ // edge case of address is only the delimiter.
74
+ // So it's the primitive for that delimiter
75
+ return { ib: delimiter, gib: '' };
76
+ // } else if (pieces.length === 0 ) {
77
+ // ibGibAddr is falsy, so would have thrown earlier in this function
78
+ // I'm just noting this case for intent ATOW
79
+ } else {
80
+ console.warn(`${lc} multiple delimiters found in ibGibAddr. Considering last delimiter as the demarcation of gib hash`);
81
+ // e.g. 'ib^ABC123^gib'
82
+ // ib: 'ib^ABC123'
83
+ // gib: 'gib'
84
+ return {
85
+ ib: pieces.slice(0, pieces.length - 1).join(delimiter),
86
+ gib: pieces.slice(pieces.length - 1)[0],
87
+ }
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Simple hash function.
93
+ *
94
+ * NOTE:
95
+ * This is not used for ibGib.gib values (ATOW)
96
+ * but rather as a helper function for generating random UUIDs.
97
+ *
98
+ * @param s string to hash
99
+ * @param algorithm to use, currently only 'SHA-256'
100
+ */
101
+ export async function hash({
102
+ s,
103
+ algorithm = 'SHA-256',
104
+ }: {
105
+ s: string,
106
+ algorithm?: HashAlgorithm,
107
+ }): Promise<string> {
108
+ if (!s) { return ''; }
109
+
110
+ const validAlgorithms = Object.values(HashAlgorithm);
111
+ if (!validAlgorithms.includes(algorithm)) {
112
+ console.error(`Only ${validAlgorithms} implemented`); return '';
113
+ }
114
+ try {
115
+ const msgUint8 = new TextEncoder().encode(s);
116
+ const buffer = await subtle.digest(algorithm, msgUint8);
117
+ const asArray = Array.from(new Uint8Array(buffer));
118
+ return asArray.map(b => b.toString(16).padStart(2, '0')).join('');
119
+ } catch (e) {
120
+ console.error(extractErrorMsg(e.message));
121
+ return '';
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Simple func to generate UUID (sha-256 hash basically).
127
+ *
128
+ * @param seedSize size of seed for UUID generation
129
+ */
130
+ export async function getUUID(seedSize = 64): Promise<string> {
131
+ let uuid: string = '';
132
+ if (seedSize < 32) { throw new Error(`Seed size must be at least 32`); }
133
+ if (!globalThis.crypto) { throw new Error(`Cannot create UUID, as unknown crypto library version. If using node.js, v19+ is required. (E: c02cee3fd8a94f678d3f4ebe9dc49797)`); }
134
+
135
+ const values = crypto.getRandomValues(new Uint8Array(16));
136
+ uuid = await hash({ s: values.join('') });
137
+
138
+ if (!uuid) { throw new Error(`Did not create UUID...hmm...`); }
139
+
140
+ return uuid;
141
+ }
142
+
143
+ /**
144
+ * Syntactic sugar for JSON.stringify(obj, null, 2);
145
+ *
146
+ * @param obj to pretty stringify
147
+ */
148
+ export function pretty(obj: any): string {
149
+ return JSON.stringify(obj, null, 2);
150
+ }
151
+
152
+ /**
153
+ * Just delays given number of ms.
154
+ *
155
+ * @param ms milliseconds to delay
156
+ */
157
+ export async function delay(ms: number): Promise<void> {
158
+ return new Promise<void>(resolve => {
159
+ setTimeout(() => {
160
+ resolve();
161
+ }, ms);
162
+ });
163
+ }
164
+
165
+ /**
166
+ * extracts the error message from an error object/string/falsy arg.
167
+ *
168
+ * ## notes
169
+ *
170
+ * * some libs throw errors, some throw just strings.
171
+ * * who knows what else it could be.
172
+ *
173
+ * ## todo
174
+ *
175
+ * * extract inner errors/causes if we ever use this function extensively.
176
+ *
177
+ * @param error the error object in the catch area of the try..catch block.
178
+ * @returns error.message if it's a string, error itself if it's a string, or canned error messages if it's falsy or none of the above.
179
+ */
180
+ export function extractErrorMsg(error: any): string {
181
+ if (!error && error !== 0) {
182
+ return '[error is falsy]';
183
+ } else if (typeof error === 'string') {
184
+ return error;
185
+ } else if (typeof error.message === 'string') {
186
+ return error.message;
187
+ } else if (typeof error === 'number') {
188
+ return JSON.stringify(error);
189
+ } else {
190
+ return `[error is not a string and error.message is not a string. typeof error: ${typeof error}]`;
191
+ }
192
+ }