@ibgib/core-gib 0.0.112 → 0.1.1

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 (67) hide show
  1. package/dist/common/import-export/import-export-types.d.mts +8 -0
  2. package/dist/common/import-export/import-export-types.d.mts.map +1 -1
  3. package/dist/timeline/timeline-api.d.mts +297 -0
  4. package/dist/timeline/timeline-api.d.mts.map +1 -0
  5. package/dist/timeline/timeline-api.mjs +725 -0
  6. package/dist/timeline/timeline-api.mjs.map +1 -0
  7. package/dist/timeline/timeline-types.d.mts +42 -0
  8. package/dist/timeline/timeline-types.d.mts.map +1 -0
  9. package/dist/timeline/timeline-types.mjs +2 -0
  10. package/dist/timeline/timeline-types.mjs.map +1 -0
  11. package/dist/witness/space/filesystem-space/filesystem-space-v1.d.mts +0 -1
  12. package/dist/witness/space/filesystem-space/filesystem-space-v1.d.mts.map +1 -1
  13. package/dist/witness/space/filesystem-space/filesystem-space-v1.mjs +0 -3
  14. package/dist/witness/space/filesystem-space/filesystem-space-v1.mjs.map +1 -1
  15. package/dist/witness/space/inner-space/inner-space-constants.d.mts +2 -0
  16. package/dist/witness/space/inner-space/inner-space-constants.d.mts.map +1 -0
  17. package/dist/witness/space/inner-space/inner-space-constants.mjs +2 -0
  18. package/dist/witness/space/inner-space/inner-space-constants.mjs.map +1 -0
  19. package/dist/witness/space/inner-space/inner-space-types.d.mts +23 -0
  20. package/dist/witness/space/inner-space/inner-space-types.d.mts.map +1 -0
  21. package/dist/witness/space/inner-space/inner-space-types.mjs +32 -0
  22. package/dist/witness/space/inner-space/inner-space-types.mjs.map +1 -0
  23. package/dist/witness/space/inner-space/inner-space-v1.d.mts +13 -7
  24. package/dist/witness/space/inner-space/inner-space-v1.d.mts.map +1 -1
  25. package/dist/witness/space/inner-space/inner-space-v1.mjs +159 -8
  26. package/dist/witness/space/inner-space/inner-space-v1.mjs.map +1 -1
  27. package/dist/witness/space/inner-space/inner-space-v1.respec.mjs +206 -52
  28. package/dist/witness/space/inner-space/inner-space-v1.respec.mjs.map +1 -1
  29. package/dist/witness/space/metaspace/metaspace-base.d.mts.map +1 -1
  30. package/dist/witness/space/metaspace/metaspace-base.mjs +4 -2
  31. package/dist/witness/space/metaspace/metaspace-base.mjs.map +1 -1
  32. package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace-helper.d.mts.map +1 -1
  33. package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace-helper.mjs +28 -9
  34. package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace-helper.mjs.map +1 -1
  35. package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.d.mts.map +1 -1
  36. package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mjs +27 -3
  37. package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mjs.map +1 -1
  38. package/dist/witness/space/reconciliation-space/reconciliation-space-base.d.mts +100 -0
  39. package/dist/witness/space/reconciliation-space/reconciliation-space-base.d.mts.map +1 -0
  40. package/dist/witness/space/reconciliation-space/reconciliation-space-base.mjs +758 -0
  41. package/dist/witness/space/reconciliation-space/reconciliation-space-base.mjs.map +1 -0
  42. package/dist/witness/space/reconciliation-space/reconciliation-space-helper.d.mts +40 -0
  43. package/dist/witness/space/reconciliation-space/reconciliation-space-helper.d.mts.map +1 -0
  44. package/dist/witness/space/reconciliation-space/reconciliation-space-helper.mjs +90 -0
  45. package/dist/witness/space/reconciliation-space/reconciliation-space-helper.mjs.map +1 -0
  46. package/dist/witness/space/space-helper.d.mts.map +1 -1
  47. package/dist/witness/space/space-helper.mjs +11 -2
  48. package/dist/witness/space/space-helper.mjs.map +1 -1
  49. package/dist/witness/space/space-respec-helper.d.mts.map +1 -1
  50. package/dist/witness/space/space-respec-helper.mjs +3 -0
  51. package/dist/witness/space/space-respec-helper.mjs.map +1 -1
  52. package/package.json +1 -1
  53. package/src/common/import-export/import-export-types.mts +8 -0
  54. package/src/timeline/timeline-api.mts +943 -0
  55. package/src/timeline/timeline-types.mts +38 -0
  56. package/src/witness/space/filesystem-space/filesystem-space-v1.mts +0 -3
  57. package/src/witness/space/inner-space/inner-space-constants.mts +1 -0
  58. package/src/witness/space/inner-space/inner-space-types.mts +44 -0
  59. package/src/witness/space/inner-space/inner-space-v1.mts +157 -28
  60. package/src/witness/space/inner-space/inner-space-v1.respec.mts +242 -58
  61. package/src/witness/space/metaspace/metaspace-base.mts +7 -1
  62. package/src/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace-helper.mts +32 -10
  63. package/src/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mts +76 -53
  64. package/src/witness/space/reconciliation-space/reconciliation-space-base.mts +884 -0
  65. package/src/witness/space/reconciliation-space/reconciliation-space-helper.mts +125 -0
  66. package/src/witness/space/space-helper.mts +6 -2
  67. package/src/witness/space/space-respec-helper.mts +4 -4
@@ -0,0 +1,943 @@
1
+ /**
2
+ * @module timeline-api.mts This facade provides a simplified, timeline-oriented
3
+ * API for interacting with ibGibs, abstracting away low-level transform details
4
+ * (like nCounter, dna, dna, tjp rel8ns) and handling concurrency control (via
5
+ * locking) for maintaining timeline integrity.
6
+ */
7
+
8
+ import { extractErrorMsg, getTimestampInTicks } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
9
+ import {
10
+ IbGib_V1, IbGibData_V1, IbGibRel8ns_V1, Rel8n
11
+ } from "@ibgib/ts-gib/dist/V1/types.mjs";
12
+ import {
13
+ IbGibAddr, TransformOpts_Mut8, TransformResult, Ib, IbGibRel8ns,
14
+ } from "@ibgib/ts-gib/dist/types.mjs";
15
+ import { getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
16
+ import { mut8 } from "@ibgib/ts-gib/dist/V1/transforms/mut8.mjs";
17
+ import { rel8 } from "@ibgib/ts-gib/dist/V1/transforms/rel8.mjs";
18
+ import { getGibInfo, isPrimitive } from "@ibgib/ts-gib/dist/V1/transforms/transform-helper.mjs";
19
+ import { Factory_V1 } from "@ibgib/ts-gib/dist/V1/factory.mjs";
20
+ import { ROOT } from '@ibgib/ts-gib/dist/V1/constants.mjs';
21
+
22
+ import { GLOBAL_LOG_A_LOT } from "../core-constants.mjs";
23
+ import { MetaspaceService } from "../witness/space/metaspace/metaspace-types.mjs";
24
+ import { IbGibSpaceAny } from "../witness/space/space-base-v1.mjs";
25
+ import { SpecialIbGibType } from "../common/other/other-types.mjs";
26
+ import { getSpecialConfigKey, toDto } from "../common/other/ibgib-helper.mjs";
27
+ import { execInSpaceWithLocking, getFromSpace, persistTransformResult } from "../witness/space/space-helper.mjs";
28
+ import { createCommentIbGib } from "../common/comment/comment-helper.mjs";
29
+ import { TimelineHistoryInfo } from "./timeline-types.mjs";
30
+
31
+ const logalot = GLOBAL_LOG_A_LOT;
32
+
33
+ export interface Rel8nInfo {
34
+ rel8nName: string;
35
+ ibGibs: IbGib_V1[];
36
+ }
37
+
38
+ export interface CommonTimelineOptsSansSkipLock {
39
+ /**
40
+ * space of spaces. Should be a singleton. @see {@link MetaspaceService}
41
+ */
42
+ metaspace: MetaspaceService,
43
+ /**
44
+ * the space that contains both the {@link timeline} and ibGibs contained in
45
+ * {@link rel8nInfos}.
46
+ *
47
+ * if falsy, then the default local user space will be gotten from the
48
+ * {@link metaspace}.
49
+ */
50
+ space?: IbGibSpaceAny,
51
+ }
52
+ export interface CommonTimelineOpts extends CommonTimelineOptsSansSkipLock {
53
+ /**
54
+ * if true, will not lock the timeline. ONLY use this if you know what you
55
+ * are doing, otherwise divergent timelines can occur.
56
+ *
57
+ * Really at that point, you may consider using the raw transforms
58
+ * themselves.
59
+ */
60
+ skipLock?: boolean,
61
+ }
62
+
63
+ /**
64
+ * Deterministically generates a lock scope for a given timeline.
65
+ *
66
+ * ## internal details, may change
67
+ *
68
+ * ATOW (05/2025), this will return the tjpGib of the timeline. So if the addr
69
+ * is `comment Foo^ABC123.DEF456`, where ABC123 and DEF456 are hashes, then this
70
+ * will return `DEF456`. For an addr that is just `comment Bar^GHI789`, then
71
+ * this *is* the TJP and thus its gib is `GHI789` and that is what is returned.
72
+ */
73
+ export async function getLockScope({ timeline }: { timeline: IbGib_V1 }): Promise<string> {
74
+ const lc = `[${getLockScope.name}]`;
75
+ try {
76
+ if (logalot) { console.log(`${lc} starting...`); }
77
+
78
+ if (isPrimitive({ ibGib: timeline })) {
79
+ // this is an abnormal/unusual path
80
+ console.warn(`${lc} getting lock scope on a primitive ibgib can grossly impact performance. (W: 55203e415ae2ac7ddb19c21772b28925)`)
81
+ return getIbGibAddr({ ibGib: timeline });
82
+ } else {
83
+ // happy path
84
+ const gibInfo = getGibInfo({ gib: timeline.gib });
85
+ const tjpGib = gibInfo.tjpGib ?? gibInfo.punctiliarHash;
86
+ if (!tjpGib) {
87
+ // This should not happen for a valid ibGib, but as a safeguard
88
+ throw new Error(`${lc} (UNEXPECTED) Could not determine tjpGib or punctiliarHash from ibGib. (E: eb3b6472e9b335991a1b0869f772f7d3)`);
89
+ }
90
+ return tjpGib;
91
+ }
92
+
93
+ } catch (error) {
94
+ console.error(`${lc} ${extractErrorMsg(error)}`);
95
+ throw error;
96
+ } finally {
97
+ if (logalot) { console.log(`${lc} complete.`); }
98
+ }
99
+ }
100
+
101
+ export async function isSameTimeline({
102
+ a,
103
+ b,
104
+ }: {
105
+ a: IbGib_V1,
106
+ b: IbGib_V1,
107
+ }): Promise<boolean> {
108
+ const lc = `[${isSameTimeline.name}]`;
109
+ try {
110
+ if (logalot) { console.log(`${lc} starting... (I: 0f8b68106f48a5c5c8d67f58b4b4d825)`); }
111
+ // DRY kluge b/c I'm soooooooo tired ...need to gtfo here
112
+ const aLockScope = await getLockScope({ timeline: a });
113
+ const bLockScope = await getLockScope({ timeline: b });
114
+ return aLockScope === bLockScope;
115
+ } catch (error) {
116
+ console.error(`${lc} ${extractErrorMsg(error)}`);
117
+ throw error;
118
+ } finally {
119
+ if (logalot) { console.log(`${lc} complete.`); }
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Appends a list of ibGibs to a timeline in the order given using the
125
+ * associated rel8nName (named edge).
126
+ *
127
+ * ## regarding persistence and registerNewIbGib pubsub
128
+ *
129
+ * The caller is responsible for persisting any ibGibs that are contained in
130
+ * {@link rel8nInfos}. However, all newly created ibgibs resulting from extending
131
+ * {@link timeline} *WILL* be saved in the {@link space}.
132
+ *
133
+ * This also will call `metaspace.registerNewIbGib` for the new timeline ibgib
134
+ * created ONLY, and will only do this AFTER both...
135
+ * * the timeline's lock is released AND AFTER
136
+ * * any indexes are updated if given via {@link timelineIndexInfo}.
137
+ * * atow (05/2025) this does not await the registerNewIbGib call, but this may
138
+ * change at any time.
139
+ */
140
+ export async function appendToTimeline({
141
+ timeline,
142
+ rel8nInfos,
143
+ timelineIndexInfo,
144
+ metaspace,
145
+ space,
146
+ skipLock,
147
+ }: {
148
+ timeline: IbGib_V1,
149
+ /**
150
+ * rel8nName/ibGibs pairings to append to the timeline.
151
+ */
152
+ rel8nInfos: Rel8nInfo[],
153
+ /**
154
+ * If provided, will automatically update the index associated with the
155
+ * given {@link type} AFTER the timeline is extended but BEFORE the
156
+ * timeline's new ibgib is published via the {@link registerNewIbGib}
157
+ * function on {@link metaspace}.
158
+ *
159
+ * NOTE that this WILL LOCK the special ibgib index unless {@link skipLock}
160
+ * is true.
161
+ *
162
+ * @see {@link updateSpecialIndex}
163
+ */
164
+ timelineIndexInfo?: {
165
+ type: SpecialIbGibType,
166
+ rel8nName: string,
167
+ },
168
+ } & CommonTimelineOpts): Promise<IbGib_V1> {
169
+ const lc = `[${appendToTimeline.name}]`;
170
+ try {
171
+ if (logalot) { console.log(`${lc} starting... (I: fcdff10832215068019355265f4cbc25)`); }
172
+ space ??= await metaspace.getLocalUserSpace({ lock: false });
173
+ if (!space) { throw new Error(`(UNEXPECTED) space falsy? couldn't even get default local user space? (E: 2051b44a052d6a2984b9a22f680e4625)`); }
174
+
175
+ const lockScope = await getLockScope({ timeline });
176
+
177
+ /**
178
+ * This fn actually does the append and will be executed in the locked
179
+ * setting if skipLock is falsy.
180
+ */
181
+ const fn: () => Promise<IbGib_V1> = async () => {
182
+ if (!space) { throw new Error(`(UNEXPECTED) space falsy? couldn't even get default local user space? (E: 845c4181c8c15d7219166ceb3c4dca25)`); }
183
+ // Get the latest version of the provided timeline ibgib.
184
+ const timelineAddr = getIbGibAddr({ ibGib: timeline });
185
+ const latestTimelineIbGibAddr = await metaspace.getLatestAddr({
186
+ addr: timelineAddr,
187
+ space,
188
+ });
189
+ if (!latestTimelineIbGibAddr) {
190
+ throw new Error(`${lc} (UNEXPECTED) Could not get latest address for timeline ${getIbGibAddr({ ibGib: timeline })}. (E: a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9)`);
191
+ }
192
+ let latestTimelineIbGibDto: IbGib_V1 = toDto({ ibGib: timeline });
193
+ if (timelineAddr !== latestTimelineIbGibAddr) {
194
+ const resGetLatest = await metaspace.get({
195
+ addrs: [latestTimelineIbGibAddr],
196
+ space,
197
+ });
198
+ if (resGetLatest.errorMsg || (resGetLatest.ibGibs ?? []).length !== 1) {
199
+ throw new Error(`${lc} (UNEXPECTED) couldn't get latest timeline ibgib (${latestTimelineIbGibAddr}) from space (${space.ib}). errorMsg: ${resGetLatest.errorMsg ?? '[unknown error]'} (E: b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0)`);
200
+ }
201
+ latestTimelineIbGibDto = resGetLatest.ibGibs!.at(0)!;
202
+ }
203
+
204
+ // Prepare rel8nsToAddByAddr
205
+ const rel8nsToAddByAddr: { [name: string]: IbGibAddr[] } = {};
206
+ // Add the 'past' rel8n pointing to the previous timeline frame.
207
+ // rel8nsToAddByAddr.past = [getIbGibAddr({ ibGib: latestTimelineIbGib })]; // this happens automatically
208
+
209
+ // Add rel8ns from the provided rel8nInfos.
210
+ for (const rel8nInfo of rel8nInfos) {
211
+ const { rel8nName, ibGibs } = rel8nInfo;
212
+ if (ibGibs.length === 0) {
213
+ console.warn(`${lc} tried to add 0 ibGibs to rel8nName ${rel8nName}? skipping... (W: 708252dfdeb60757fee4554e769aaa25)`);
214
+ continue;
215
+ }
216
+ if (!rel8nsToAddByAddr[rel8nName]) { rel8nsToAddByAddr[rel8nName] = []; }
217
+ rel8nsToAddByAddr[rel8nName].push(...ibGibs.map(ibGib => getIbGibAddr({ ibGib })));
218
+ }
219
+
220
+ // Perform a rel8 transform on the latest timeline ibgib.
221
+ const resRel8 = await rel8({
222
+ type: 'rel8', // assuming rel8 is the transform type
223
+ src: latestTimelineIbGibDto, // Use the latest as the source
224
+ rel8nsToAddByAddr,
225
+ dna: true,
226
+ nCounter: true,
227
+ });
228
+ const newTimelineIbGib = resRel8.newIbGib;
229
+
230
+ // Persist the transform result.
231
+ await metaspace.persistTransformResult({ resTransform: resRel8, space });
232
+
233
+ // If timelineIndexInfo is provided, update the special index.
234
+ if (timelineIndexInfo) {
235
+ await updateSpecialIndex({
236
+ type: timelineIndexInfo.type,
237
+ rel8nInfos: [{ rel8nName: timelineIndexInfo.rel8nName, ibGibs: [newTimelineIbGib] }],
238
+ metaspace,
239
+ space,
240
+ skipLock,
241
+ });
242
+ }
243
+
244
+ // should we publish within the lock or not within the lock?
245
+ // should we await the publish or spin it off
246
+ // right now I have this spin off, inside the lock
247
+ // await metaspace.registerNewIbGib({ ibGib: newTimelineIbGib, space });
248
+ metaspace.registerNewIbGib({ ibGib: newTimelineIbGib, space }); // spin off
249
+
250
+ return newTimelineIbGib;
251
+ };
252
+
253
+ const newTimelineIbGib = skipLock ?
254
+ await fn() :
255
+ await execInSpaceWithLocking({
256
+ scope: lockScope,
257
+ secondsValid: 180, // Example timeout
258
+ maxDelayMs: 100, // Example delay
259
+ maxLockAttempts: 1800, // Example attempts
260
+ space,
261
+ callerInstanceId: getTimestampInTicks(),
262
+ fn,
263
+ });
264
+
265
+ // should we publish within the lock or not within the lock?
266
+ // should we await the publish or spin it off
267
+ // right now I have this spin off, inside the lock
268
+ // await metaspace.registerNewIbGib({ ibGib: newTimelineIbGib, space });
269
+ // metaspace.registerNewIbGib({ ibGib: newTimelineIbGib, space }); // spin off
270
+
271
+ return newTimelineIbGib;
272
+ } catch (error) {
273
+ console.error(`${lc} ${extractErrorMsg(error)}`);
274
+ throw error;
275
+ } finally {
276
+ if (logalot) { console.log(`${lc} complete.`); }
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Updates a special ibgib in the given {@link space} with new and/or updated
282
+ * {@link rel8nInfos}.
283
+ *
284
+ * ## on "special" ibgibs
285
+ *
286
+ * "Special" ibgibs are mostly used as indexes, but not necessarily. These are
287
+ * slightly different than most normal ibgibs in that they are not in some way
288
+ * or another attached to a root in the space, rather, they are rel8d directly
289
+ * to the local user space itself. So when a special ibgib is changed, i.e. its
290
+ * own timeline is extended, its new address is always proactively updated on
291
+ * the space directly. so `space.rel8ns[key]` (where key is derived from the
292
+ * given {@link type}) is updated with newSpecialIbGib's addr.
293
+ *
294
+ * @see {@link setConfigAddr}
295
+ * @see {@link rel8ToSpecialIbGib}
296
+ *
297
+ * ## upsert
298
+ *
299
+ * This is analogous to an upsert operation *into* the special index. So we are
300
+ * updating the index's rel8ns either way whether or not that rel8n already
301
+ * exists.
302
+ */
303
+ export async function updateSpecialIndex({
304
+ type,
305
+ rel8nInfos,
306
+ dataToAddOrPatch,
307
+ metaspace,
308
+ space,
309
+ skipLock,
310
+ }: {
311
+ /**
312
+ * identifying type of the special index. For example, spaces usually get
313
+ * initialized with a "tags" index. Individual tag ibgibs will be rel8d
314
+ * to this index using the `SpecialIbGibType.tags`, but really any string
315
+ * can be used as this type.
316
+ *
317
+ * For example, agents (atow 05/2025) have a soft type in their data.
318
+ * These agents are then indexed via this agent.data.type so that each type of
319
+ * agent has its own index, e.g., "primaryagent" or "renderable".
320
+ */
321
+ type: SpecialIbGibType,
322
+ rel8nInfos?: Rel8nInfo[],
323
+ dataToAddOrPatch?: any,
324
+ } & CommonTimelineOpts): Promise<void> {
325
+ const lc = `[${updateSpecialIndex.name}]`;
326
+ try {
327
+ if (logalot) { console.log(`${lc} starting... (I: 6e7d05fe916d813b71766e2eaa747c25)`); }
328
+
329
+ if (type === "latest") { throw new Error(`cannot update the "latest" special index by this function. The registerNewIbGib will do this automatically. (E: 231eb78a221ccdbcf56cd1899260a425)`); }
330
+
331
+ space ??= await metaspace.getLocalUserSpace({ lock: false });
332
+ if (!space) { throw new Error(`(UNEXPECTED) space falsy? couldn't even get default local user space? (E: 43067821a4749a7648fe2612d25d7f25)`); }
333
+
334
+ /**
335
+ * This fn actually does the update and will be executed in the locked
336
+ * setting.
337
+ */
338
+ const fn: () => Promise<void> = async () => {
339
+ const fnGetSpecialIndex = async () => {
340
+ // always get the latest version of the special index ibgib
341
+ // fresh. The initialize: true option will create it if it
342
+ // doesn't exist.
343
+ const specialIndex = await metaspace.getSpecialIbGib({
344
+ type,
345
+ space,
346
+ initialize: true,
347
+ });
348
+ if (!specialIndex) {
349
+ throw new Error(`(UNEXPECTED) couldn't initialize/get special ibgib index of type ${type}. (E: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6)`);
350
+ }
351
+ if (!specialIndex.rel8ns) {
352
+ console.warn(`${lc} not sure if this ever hits, but is there ever a scenario where specialIndex.rel8ns is falsy? (W: 06161c366bc7ec858b9b5aa7ee9b6d25)`)
353
+ }
354
+ return specialIndex;
355
+ }
356
+
357
+ for (const rel8nInfo of (rel8nInfos ?? [])) {
358
+ // parse the rel8nInfo for this iteration
359
+ const { rel8nName, ibGibs } = rel8nInfo;
360
+ if (ibGibs.length === 0) {
361
+ console.error(`${lc} ibGibs empty. skipping rel8nName (${rel8nName}). (E: 150d74f73f97aea1b97d173d92c77125)`);
362
+ }
363
+
364
+ const specialIndex = await fnGetSpecialIndex();
365
+
366
+ // // always get the latest version of the special index ibgib
367
+ // // fresh. The initialize: true option will create it if it
368
+ // // doesn't exist.
369
+ // const specialIndex = await metaspace.getSpecialIbGib({
370
+ // type,
371
+ // space,
372
+ // initialize: true,
373
+ // });
374
+ // if (!specialIndex) {
375
+ // throw new Error(`(UNEXPECTED) couldn't initialize/get special ibgib index of type ${type}. (E: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6)`);
376
+ // }
377
+ // if (!specialIndex.rel8ns) {
378
+ // console.warn(`${lc} not sure if this ever hits, but is there ever a scenario where specialIndex.rel8ns is falsy? (W: 06161c366bc7ec858b9b5aa7ee9b6d25)`)
379
+ // }
380
+
381
+ const existingSpecialIndexRel8ns = specialIndex.rel8ns ?? {};
382
+
383
+ // before we just add all the ibgibs, we need to gather info on
384
+ // what previous ibgibs we need to unrel8
385
+ let addrsToUnRel8: undefined | IbGibAddr[] = undefined;
386
+
387
+ // check if we even need to even compare existing rel8ns
388
+ const existingAddrsThisRel8nName = existingSpecialIndexRel8ns[rel8nName] ?? [];
389
+ if (existingAddrsThisRel8nName.length > 0) {
390
+
391
+ // we have existing rel8ns for this rel8nName, so remove any
392
+ // that share the tjpGib of addrs we're adding or use the
393
+ // past/current full addrs. check actual code here for impl
394
+ const toUnrel8ThisRel8nName_Set: Set<IbGibAddr> = new Set();
395
+ ibGibs.forEach(ibGib => {
396
+ const addr = getIbGibAddr({ ibGib });
397
+
398
+ // by tjpGib impl
399
+ // const gibInfo = getGibInfo({ gib: ibGib.gib });
400
+ // const tjpGib = gibInfo.tjpGib ?? gibInfo.punctiliarHash ?? ibGib.gib ?? GIB;
401
+ // existingAddrsThisRel8nName
402
+ // .filter(x => x.includes(tjpGib))
403
+ // .forEach(x => toUnrel8ThisRel8nName_Set.add(x));
404
+
405
+ // by full addrs impl
406
+ const pastAddrs = ibGib.rel8ns?.past ?? [];
407
+ existingAddrsThisRel8nName
408
+ .filter(x => [...pastAddrs, addr].includes(x))
409
+ .forEach(x => toUnrel8ThisRel8nName_Set.add(x));
410
+ });
411
+
412
+ if (toUnrel8ThisRel8nName_Set.size > 0) {
413
+ addrsToUnRel8 = Array.from(toUnrel8ThisRel8nName_Set);
414
+ }
415
+ }
416
+
417
+ // we now have enough to execute the rel8
418
+ await metaspace.rel8ToSpecialIbGib({
419
+ type,
420
+ rel8nName,
421
+ ibGibsToRel8: ibGibs,
422
+ addrsToUnRel8,
423
+ space,
424
+ linked: false,
425
+ severPast: false,
426
+ deletePreviousSpecialIbGib: false, // only used in latest special ibgib
427
+ })
428
+ }
429
+
430
+ if (Object.keys(dataToAddOrPatch ?? {}).length > 0) {
431
+ const specialIndex = await fnGetSpecialIndex();
432
+ const resNewSpecial = await mut8({
433
+ src: specialIndex,
434
+ dataToAddOrPatch,
435
+ dna: false,
436
+ linkedRel8ns: [Rel8n.past],
437
+ nCounter: true,
438
+ });
439
+
440
+ const newSpecialIbGib = resNewSpecial.newIbGib;
441
+
442
+ // persist
443
+ await persistTransformResult({ resTransform: resNewSpecial, space: space! });
444
+
445
+ // update the space ibgib which contains the special/config information
446
+ const configKey = getSpecialConfigKey({ type });
447
+ await metaspace.setConfigAddr({
448
+ key: configKey,
449
+ addr: getIbGibAddr({ ibGib: newSpecialIbGib }),
450
+ space,
451
+ });
452
+
453
+ await metaspace.registerNewIbGib({ ibGib: newSpecialIbGib, space, });
454
+ }
455
+ }
456
+
457
+ if (skipLock) {
458
+ await fn();
459
+ } else {
460
+ // Use execInSpaceWithLocking with the special index type as the scope.
461
+ // We are currently choosing to always lock for special indexes for
462
+ // safety. The skipLock parameter from CommonTimelineOpts is included
463
+ // in the signature for consistency but not used in the locking logic
464
+ // here based on our decision to always lock special indexes.
465
+ await execInSpaceWithLocking({
466
+ scope: type, // Use the special index type as the lock scope.
467
+ secondsValid: 60, // Example timeout
468
+ maxDelayMs: 100, // Example delay
469
+ maxLockAttempts: 600, // Example attempts
470
+ space,
471
+ // callerInstanceId: // You might need a way to generate a unique ID for the caller
472
+ fn,
473
+ });
474
+ }
475
+
476
+ } catch (error) {
477
+ console.error(`${lc} ${extractErrorMsg(error)}`);
478
+ throw error;
479
+ } finally {
480
+ if (logalot) { console.log(`${lc} complete.`); }
481
+ }
482
+ }
483
+
484
+ export async function getLatestTimelineIbGibDto_nonLocking<TIbGib extends IbGib_V1>({
485
+ timeline,
486
+ timelineAddr,
487
+ metaspace,
488
+ space,
489
+ }: {
490
+ timeline?: TIbGib,
491
+ timelineAddr?: IbGibAddr,
492
+ metaspace: MetaspaceService,
493
+ space: IbGibSpaceAny,
494
+ }): Promise<TIbGib> {
495
+ const lc = `[${getLatestTimelineIbGibDto_nonLocking.name}]`;
496
+ try {
497
+ if (logalot) { console.log(`${lc} starting... (I: f2b605361368e1956f6db878dbfeb825)`); }
498
+
499
+ if (!timeline && !timelineAddr) { throw new Error(`either timeline or timelineAddr required. (E: 97551b2b18ecd22a18185a783f9a0825)`); }
500
+ timelineAddr ??= getIbGibAddr({ ibGib: timeline });
501
+
502
+ // const timelineAddr = getIbGibAddr({ ibGib: timeline });
503
+ const latestTimelineIbGibAddr = await metaspace.getLatestAddr({
504
+ addr: timelineAddr,
505
+ space,
506
+ }) ?? timelineAddr;
507
+ if (!latestTimelineIbGibAddr) {
508
+ throw new Error(`${lc} (UNEXPECTED) Could not get latest address for timeline ${getIbGibAddr({ ibGib: timeline })}. (E: a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9)`);
509
+ }
510
+ let latestTimelineIbGibDto: TIbGib;// = toDto({ ibGib: timeline }) as TIbGib;
511
+ if (timelineAddr === latestTimelineIbGibAddr) {
512
+ // latest addr is the same
513
+ if (!timeline) {
514
+ const resGetLatest = await metaspace.get({
515
+ addrs: [latestTimelineIbGibAddr],
516
+ space,
517
+ });
518
+ if (resGetLatest.errorMsg || (resGetLatest.ibGibs ?? []).length !== 1) {
519
+ throw new Error(`${lc} (UNEXPECTED) couldn't get latest timeline ibgib (${latestTimelineIbGibAddr}) from space (${space.ib}). errorMsg: ${resGetLatest.errorMsg ?? '[unknown error]'} (E: 3fc767a594788a43a4ba18c87491a825)`);
520
+ }
521
+ timeline = resGetLatest.ibGibs!.at(0)! as TIbGib;
522
+ }
523
+ latestTimelineIbGibDto = toDto({ ibGib: timeline }) as TIbGib;
524
+ } else {
525
+ // latest addr is different
526
+ const resGetLatest = await metaspace.get({
527
+ addrs: [latestTimelineIbGibAddr],
528
+ space,
529
+ });
530
+ if (resGetLatest.errorMsg || (resGetLatest.ibGibs ?? []).length !== 1) {
531
+ throw new Error(`${lc} (UNEXPECTED) couldn't get latest timeline ibgib (${latestTimelineIbGibAddr}) from space (${space.ib}). errorMsg: ${resGetLatest.errorMsg ?? '[unknown error]'} (E: b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0)`);
532
+ }
533
+ latestTimelineIbGibDto = resGetLatest.ibGibs!.at(0)! as TIbGib;
534
+ }
535
+
536
+ return latestTimelineIbGibDto;
537
+ } catch (error) {
538
+ console.error(`${lc} ${extractErrorMsg(error)}`);
539
+ throw error;
540
+ } finally {
541
+ if (logalot) { console.log(`${lc} complete.`); }
542
+ }
543
+ }
544
+
545
+ export async function mut8Timeline<TData extends IbGibData_V1 = any>({
546
+ timeline,
547
+ timelineAddr,
548
+ mut8Opts,
549
+ metaspace,
550
+ space,
551
+ skipLock,
552
+ timelineIndexInfo,
553
+ }: {
554
+ /**
555
+ * timeline ibgib whose `data` we're mutating.
556
+ */
557
+ timeline?: IbGib_V1<TData, any>,
558
+ timelineAddr?: IbGibAddr,
559
+ /**
560
+ * 'dataToRename' | 'dataToRemove' | 'dataToAddOrPatch' | 'mut8Ib'
561
+ *
562
+ * The rest of the mut8 options are automatic in this simplified timeline
563
+ * api.
564
+ */
565
+ mut8Opts: Pick<TransformOpts_Mut8<IbGib_V1, Partial<TData>>, 'dataToRename' | 'dataToRemove' | 'dataToAddOrPatch' | 'mut8Ib'>,
566
+ /**
567
+ * If provided, will automatically update the index associated with the
568
+ * given {@link type} AFTER the timeline is extended but BEFORE the
569
+ * timeline's new ibgib is published via the {@link registerNewIbGib}
570
+ * function on {@link metaspace}.
571
+ *
572
+ * NOTE that this WILL LOCK the special ibgib index unless {@link skipLock}
573
+ * is true.
574
+ *
575
+ * @see {@link updateSpecialIndex}
576
+ */
577
+ timelineIndexInfo?: {
578
+ type: SpecialIbGibType,
579
+ rel8nName: string,
580
+ },
581
+ } & CommonTimelineOpts): Promise<IbGib_V1<TData, any>> {
582
+ const lc = `[${mut8Timeline.name}]`;
583
+ try {
584
+ if (logalot) { console.log(`${lc} starting... (I: genuuid)`); }
585
+
586
+ if (!timeline && !timelineAddr) { throw new Error(`(UNEXPECTED) both timeline and timelineAddr falsy? either one is required. (E: a19d98c6acb87af3b876c1484f6ac825)`); }
587
+
588
+ space ??= await metaspace.getLocalUserSpace({ lock: false });
589
+ if (!space) { throw new Error(`(UNEXPECTED) space falsy? couldn't even get default local user space? (E: genuuid)`); }
590
+
591
+ if (!timeline) {
592
+ const resGet = await metaspace.get({
593
+ addrs: [timelineAddr!],
594
+ space,
595
+ });
596
+ if (!resGet.success || resGet.ibGibs?.length !== 1) {
597
+ throw new Error(`couldn't get timeline ibGib from timelineAddr (${timelineAddr!}) in space (${space?.ib ?? 'undefined'}) (E: genuuid)`);
598
+ }
599
+ timeline = resGet.ibGibs.at(0)! as IbGib_V1<TData, any>;
600
+ }
601
+
602
+ const lockScope = await getLockScope({ timeline });
603
+
604
+ /**
605
+ * This fn actually does the update and will be executed in the locked
606
+ * setting.
607
+ */
608
+ const fn: () => Promise<IbGib_V1<TData, any>> = async () => {
609
+ if (!space) { throw new Error(`(UNEXPECTED) space falsy? couldn't even get default local user space? (E: 845c4181c8c15d7219166ceb3c4dca25)`); }
610
+ // Get the latest version of the provided timeline ibgib.
611
+
612
+ if (!timeline) { throw new Error(`(UNEXPECTED) timeline falsy? we should be ensured that it's truthy at this point. (E: 2e1758b2cc3820531f7ac7f5f6b3a825)`); }
613
+
614
+ const latestTimelineIbGibDto = await getLatestTimelineIbGibDto_nonLocking({
615
+ timeline, metaspace, space,
616
+ });
617
+
618
+ // adjust mut8Opts depending
619
+ const resMut8 = await mut8({
620
+ ...mut8Opts,
621
+ src: latestTimelineIbGibDto,
622
+ dna: true,
623
+ nCounter: true,
624
+ }) as TransformResult<IbGib_V1<TData, any>>;
625
+ const newTimelineIbGib = resMut8.newIbGib;
626
+
627
+ // Persist the transform result.
628
+ await metaspace.persistTransformResult({ resTransform: resMut8, space });
629
+
630
+ // If timelineIndexInfo is provided, update the special index.
631
+ if (timelineIndexInfo) {
632
+ await updateSpecialIndex({
633
+ type: timelineIndexInfo.type,
634
+ rel8nInfos: [{ rel8nName: timelineIndexInfo.rel8nName, ibGibs: [newTimelineIbGib] }],
635
+ metaspace,
636
+ space,
637
+ skipLock,
638
+ });
639
+ }
640
+
641
+ // should we publish within the lock or not within the lock?
642
+ // should we await the publish or spin it off
643
+ // right now I have this spin off, inside the lock
644
+ // await metaspace.registerNewIbGib({ ibGib: newTimelineIbGib, space });
645
+ // we await the register call because this spins off the publish events (we are assuming implementation details here)
646
+ await metaspace.registerNewIbGib({ ibGib: newTimelineIbGib, space }); // already spins off
647
+
648
+ return newTimelineIbGib;
649
+ }
650
+
651
+ const newTimelineIbGib = skipLock ?
652
+ await fn() :
653
+ await execInSpaceWithLocking({
654
+ scope: lockScope,
655
+ secondsValid: 180, // Example timeout
656
+ maxDelayMs: 100, // Example delay
657
+ maxLockAttempts: 1800, // Example attempts
658
+ space,
659
+ callerInstanceId: getTimestampInTicks(),
660
+ fn,
661
+ });
662
+
663
+ return newTimelineIbGib;
664
+ } catch (error) {
665
+ console.error(`${lc} ${extractErrorMsg(error)}`);
666
+ throw error;
667
+ } finally {
668
+ if (logalot) { console.log(`${lc} complete.`); }
669
+ }
670
+ }
671
+
672
+ // todo: implement simplified locking wrappers to raw `rel8`, and `fork`
673
+ // transforms functions. These should be `rel8Timeline`, and `forkTimeline`. But
674
+ // these should not have skipLock available, since at that point you're really
675
+ // basically doing the raw transforms and can use those.
676
+
677
+ /**
678
+ * Creates a new ibGib timeline and persists it to the given space.
679
+ *
680
+ * This is the primary entry point for creating any new, trackable piece of data
681
+ * in the ibGib system.
682
+ *
683
+ * ## On usage of this factory function
684
+ *
685
+ * This is often used for one-offs or testing, but usually you will want to
686
+ * create your own factory function and use this internally.
687
+ *
688
+ * ## On "New" vs. "Fork"
689
+ *
690
+ * Every ibGib is a transformation (a "fork") of an existing ibgib, with the
691
+ * most primitive ibGib being the {@link ROOT} primitive. This is like the ibgib
692
+ * analog to a zero/infinity value (the same asymptote from two sides), or in CS
693
+ * OOP, this is like the root/base of the class hierarchy. But it's important to
694
+ * keep in mind that time is a first-class citizen and that we are always
695
+ * working with that instead of some detached "new" siloed thing.
696
+ *
697
+ * ## On Parenting
698
+ *
699
+ * The identity of an ibGib is tied to its ancestry via the `ancestor` rel8n
700
+ * (`someIbGib.rel8ns.ancestor`). This function gives you two ways to specify
701
+ * the parent to fork:
702
+ *
703
+ * 1. {@link parentIb}: If you provide a simple string like `'comment'` or `'picture'`,
704
+ * this function will handle creating the "primitive" parent ibGib
705
+ * (`comment^gib` or `picture^gib`) for you. This is the easiest way to
706
+ * create "typed" data, so that if you examine the `ancestor` rel8n, and see
707
+ * this, you know would know that this ibgib is a "comment" or "picture". Use
708
+ * this if you are not forking a particular instance of another ibGib.
709
+ *
710
+ * 2. {@link parentIbGib}: Provide a full, existing `IbGib` reference to be the
711
+ * parent. This can be used when using a previous ibGib as a template for
712
+ * just one use case. Note that this parent ibgib (and all of its
713
+ * dependency graph) must exist in the same given {@link space} (if
714
+ * provided), or in the default local user space via the {@link metaspace}.
715
+ * Almost always (always?), an existing parent ibgib will itself ultimately
716
+ * descend from a primitive ibgib.
717
+ *
718
+ * If neither `parentIb` nor `parentIbGib` is provided, it defaults to forking
719
+ * the absolute {@link ROOT} ibGib, resulting in a generic, untyped timeline (or
720
+ * relegates the typing to some other mechanism, like the `ibGib.ib` or other).
721
+ *
722
+ * @returns The entire transform result object, which includes `newIbGib` (the
723
+ * latest/head of the newly created timeline) and `intermediateIbGibs` (the
724
+ * primitive ibGibs that form its DNA and any interstitial ibGibs that were
725
+ * created).
726
+ */
727
+ export async function createTimeline<TData extends IbGibData_V1 = IbGibData_V1>({
728
+ ib,
729
+ data,
730
+ rel8ns,
731
+ parentIb,
732
+ parentIbGib,
733
+ metaspace,
734
+ space,
735
+ }: {
736
+ /**
737
+ * The "ib" content of the new ibGib. Often a short descriptor.
738
+ * e.g., "some_atom SomeMetadata".
739
+ *
740
+ * ## notes on `ib` schemas
741
+ *
742
+ * Note that the `ib` is usually a space-delimited string, with the first
743
+ * piece being an "atom" that is a naive "type" of the ibgib. Any
744
+ * constituent pieces of the `ib`, including that atom`, are usually
745
+ * underscore-delimited, but this is not a definite rule. The `ib` cannot
746
+ * contain the ib^gib delimiter (the caret ^).
747
+ *
748
+ * ## factory functions for common ibgibs
749
+ *
750
+ * There are also existing factory functions for comments (text ibgibs),
751
+ * pics, links, and some others. These create additional special schemas for
752
+ * those, if you wish to use the existing constructs, so those factory
753
+ * functions should be used instead of this one.
754
+ */
755
+ ib: string,
756
+ /**
757
+ * Optional data payload for the new ibGib.
758
+ */
759
+ data?: TData,
760
+ /**
761
+ * Optional relationships to other ibGibs.
762
+ */
763
+ rel8ns?: IbGibRel8ns,
764
+ /**
765
+ * The `ib` of the primitive parent to fork, e.g., `'comment'`.
766
+ * (See "On Parenting" in function docs).
767
+ */
768
+ parentIb?: Ib,
769
+ /**
770
+ * The actual `IbGib` object to fork. Overrides `parentIb` if provided.
771
+ * (See "On Parenting" in function docs).
772
+ */
773
+ parentIbGib?: IbGib_V1,
774
+ } & CommonTimelineOptsSansSkipLock): Promise<TransformResult<IbGib_V1<TData, IbGibRel8ns_V1>>> {
775
+ const lc = `[${createTimeline.name}]`;
776
+ try {
777
+ if (logalot) { console.log(`${lc} starting...`); }
778
+ space ??= await metaspace.getLocalUserSpace({ lock: false });
779
+ if (!space) { throw new Error(`(UNEXPECTED) space is required, but is falsy. (E: 252994a554224b17aa9816c7a765c925)`); }
780
+
781
+ // Determine the parent to fork based on the provided options.
782
+ parentIbGib ??= parentIb ?
783
+ // Easiest consumer-friendly path: create a primitive parent from a string.
784
+ parentIbGib = Factory_V1.primitive({ ib: parentIb }) :
785
+ // Default to the absolute ROOT if no parent info is given.
786
+ parentIbGib = ROOT;
787
+
788
+ const resNew = await Factory_V1.firstGen<TData>({
789
+ parentIbGib,
790
+ ib,
791
+ data,
792
+ rel8ns,
793
+ // these options are what make this a "timeline"
794
+ dna: true,
795
+ nCounter: true,
796
+ tjp: { timestamp: true, uuid: true },
797
+ });
798
+
799
+ // Persist the new timeline and its DNA to the designated space.
800
+ await metaspace.persistTransformResult({ resTransform: resNew, space });
801
+ await metaspace.registerNewIbGib({ ibGib: resNew.newIbGib, space });
802
+
803
+ // We don't need to lock here because it's a new, independent timeline,
804
+ // so there are no concurrency concerns yet.
805
+
806
+ return resNew as TransformResult<IbGib_V1<TData, IbGibRel8ns_V1>>;
807
+ } catch (error) {
808
+ console.error(`${lc} ${extractErrorMsg(error)}`);
809
+ throw error;
810
+ }
811
+ }
812
+
813
+ /**
814
+ * Retrieves the DNA ibGibs for a given ibGib.
815
+ *
816
+ * In V1, DNA is considered atomic and is not nested. This function performs a
817
+ * direct lookup of the ibGibs pointed to by the `dna` rel8n.
818
+ *
819
+ * @returns An array of the primitive DNA ibGibs.
820
+ */
821
+ export async function getDna({
822
+ ibGib,
823
+ metaspace,
824
+ space,
825
+ }: {
826
+ ibGib: IbGib_V1,
827
+ } & CommonTimelineOptsSansSkipLock): Promise<IbGib_V1[]> {
828
+ const lc = `[${getDna.name}]`;
829
+ try {
830
+ if (logalot) { console.log(`${lc} starting...`); }
831
+ space ??= await metaspace.getLocalUserSpace({ lock: false });
832
+ if (!space) { throw new Error(`(UNEXPECTED) space is required. (E: c1b9a9b8c7d6e5f4a3b2a1a0b9c8d7e6)`); }
833
+
834
+ const dnaAddrs = ibGib.rel8ns?.dna ?? [];
835
+ if (dnaAddrs.length === 0) { return []; }
836
+
837
+ const resGet = await metaspace.get({ addrs: dnaAddrs, space });
838
+ if (!resGet.success || (resGet.ibGibs ?? []).length !== dnaAddrs.length) {
839
+ const notFound = dnaAddrs.filter(addr => !resGet.ibGibs?.some(x => getIbGibAddr({ ibGib: x }) === addr));
840
+ throw new Error(`Could not get all dna ibGibs for ${getIbGibAddr({ ibGib })}. Addrs not found: ${notFound.join(', ')}. Errors: ${resGet.errorMsg ?? '[unknown error (E: 92cf95774828d8a978331da859098625)]'} (E: d2c0b1a9a8b7c6d5e4f3a2b1a0b9c8d7)`);
841
+ }
842
+
843
+ return resGet.ibGibs!;
844
+ } catch (error) {
845
+ console.error(`${lc} ${extractErrorMsg(error)}`);
846
+ throw error;
847
+ }
848
+ }
849
+
850
+ /**
851
+ * Constructs a {@link TimelineHistoryInfo} record about the given timeline
852
+ * (using the {@link timeline} `ibGib.rel8ns.past`), with loaded past ibgibs,
853
+ * and if {@link includeDna}, a reference to their dna ibgibs as well.
854
+ *
855
+ * @returns A complete, ordered array of ibGibs constituting the timeline,
856
+ * from the earliest ancestor to the provided `timeline` ibGib.
857
+ */
858
+ export async function getHistory({
859
+ timeline,
860
+ getLatest,
861
+ includeDna,
862
+ metaspace,
863
+ space,
864
+ }: {
865
+ /**
866
+ * The ibGib at the head of the timeline whose history you want to retrieve.
867
+ */
868
+ timeline: IbGib_V1,
869
+ /**
870
+ * if true, will first retrieve the latest ibgib in the {@link timeline} in
871
+ * the given {@link space} if provided, else in the default local user space
872
+ * according to the {@link metaspace}.
873
+ */
874
+ getLatest?: boolean,
875
+ /**
876
+ * If true, includes all DNA ibGibs for the entire timeline at the
877
+ * beginning of the returned array.
878
+ * @default true
879
+ */
880
+ includeDna?: boolean,
881
+ } & CommonTimelineOptsSansSkipLock): Promise<TimelineHistoryInfo> {
882
+ const lc = `[${getHistory.name}]`;
883
+ try {
884
+ if (logalot) { console.log(`${lc} starting... (I: 7fbc442a27e8f9fda8ac4228ded84825)`); }
885
+
886
+ space ??= await metaspace.getLocalUserSpace({ lock: false });
887
+ if (!space) { throw new Error(`(UNEXPECTED) space is required. (E: e3d1c0b1a9a8b7c6d5e4f3a2b1a0b9c8)`); }
888
+
889
+ const ibGibsToMap = (ibgibs: IbGib_V1[]) => {
890
+ const map: { [addr: IbGibAddr]: IbGib_V1 } = {};
891
+ ibgibs.forEach(x => { map[getIbGibAddr({ ibGib: x })] = x; });
892
+ return map;
893
+ }
894
+
895
+ const headIbGib = getLatest ?
896
+ await getLatestTimelineIbGibDto_nonLocking({ timeline, metaspace, space }) :
897
+ toDto({ ibGib: timeline }) as IbGib_V1;
898
+ const headAddr = getIbGibAddr({ ibGib: headIbGib });
899
+
900
+ let pastIbGibMap: { [addr: IbGibAddr]: IbGib_V1 } = {};
901
+
902
+ const dnaMap = includeDna ?
903
+ ibGibsToMap(await getDna({ ibGib: headIbGib, metaspace, space })) :
904
+ undefined;
905
+
906
+ const pastAddrs = timeline.rel8ns?.past ?? [];
907
+ if (pastAddrs.length === 0) {
908
+ return {
909
+ headIbGib,
910
+ orderedPastIbGibs: [],
911
+ pastIbGibMap,
912
+ dnaMap,
913
+ } /* <<<< returns early */
914
+ }
915
+
916
+ const resGet = await metaspace.get({ addrs: pastAddrs, space });
917
+ if (!resGet.success || (resGet.ibGibs ?? []).length !== pastAddrs.length) {
918
+ const notFound = pastAddrs.filter(addr => !resGet.ibGibs?.some(x => getIbGibAddr({ ibGib: x }) === addr));
919
+ throw new Error(`Could not get all dna ibGibs for ${headAddr}. Addrs not found: ${notFound.join(', ')}. Errors: ${resGet.errorMsg ?? '[unknown error (E: 887339d8b5580ed1ac9892a554255525)]'} (E: 4ab7a8a58adff4e238171f7c110c6825)`);
920
+ }
921
+
922
+ pastIbGibMap = ibGibsToMap(resGet.ibGibs!);
923
+
924
+ const orderedPastIbGibs: IbGib_V1[] = [];
925
+ for (const pastAddr of pastAddrs) {
926
+ const pastIbGib = pastIbGibMap[pastAddr];
927
+ if (!pastIbGib) { throw new Error(`(UNEXPECTED) pastIbGib falsy? due to our logic, the pastIbGibMap should be complete. (E: 6010d8c14184124008631928e1265525)`); }
928
+ orderedPastIbGibs.push(pastIbGib);
929
+ }
930
+
931
+ return {
932
+ headIbGib,
933
+ orderedPastIbGibs,
934
+ pastIbGibMap,
935
+ dnaMap,
936
+ }
937
+ } catch (error) {
938
+ console.error(`${lc} ${extractErrorMsg(error)}`);
939
+ throw error;
940
+ } finally {
941
+ if (logalot) { console.log(`${lc} complete.`); }
942
+ }
943
+ }