@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.
- package/dist/common/import-export/import-export-types.d.mts +8 -0
- package/dist/common/import-export/import-export-types.d.mts.map +1 -1
- package/dist/timeline/timeline-api.d.mts +297 -0
- package/dist/timeline/timeline-api.d.mts.map +1 -0
- package/dist/timeline/timeline-api.mjs +725 -0
- package/dist/timeline/timeline-api.mjs.map +1 -0
- package/dist/timeline/timeline-types.d.mts +42 -0
- package/dist/timeline/timeline-types.d.mts.map +1 -0
- package/dist/timeline/timeline-types.mjs +2 -0
- package/dist/timeline/timeline-types.mjs.map +1 -0
- package/dist/witness/space/filesystem-space/filesystem-space-v1.d.mts +0 -1
- package/dist/witness/space/filesystem-space/filesystem-space-v1.d.mts.map +1 -1
- package/dist/witness/space/filesystem-space/filesystem-space-v1.mjs +0 -3
- package/dist/witness/space/filesystem-space/filesystem-space-v1.mjs.map +1 -1
- package/dist/witness/space/inner-space/inner-space-constants.d.mts +2 -0
- package/dist/witness/space/inner-space/inner-space-constants.d.mts.map +1 -0
- package/dist/witness/space/inner-space/inner-space-constants.mjs +2 -0
- package/dist/witness/space/inner-space/inner-space-constants.mjs.map +1 -0
- package/dist/witness/space/inner-space/inner-space-types.d.mts +23 -0
- package/dist/witness/space/inner-space/inner-space-types.d.mts.map +1 -0
- package/dist/witness/space/inner-space/inner-space-types.mjs +32 -0
- package/dist/witness/space/inner-space/inner-space-types.mjs.map +1 -0
- package/dist/witness/space/inner-space/inner-space-v1.d.mts +13 -7
- package/dist/witness/space/inner-space/inner-space-v1.d.mts.map +1 -1
- package/dist/witness/space/inner-space/inner-space-v1.mjs +159 -8
- package/dist/witness/space/inner-space/inner-space-v1.mjs.map +1 -1
- package/dist/witness/space/inner-space/inner-space-v1.respec.mjs +206 -52
- package/dist/witness/space/inner-space/inner-space-v1.respec.mjs.map +1 -1
- package/dist/witness/space/metaspace/metaspace-base.d.mts.map +1 -1
- package/dist/witness/space/metaspace/metaspace-base.mjs +4 -2
- package/dist/witness/space/metaspace/metaspace-base.mjs.map +1 -1
- package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace-helper.d.mts.map +1 -1
- package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace-helper.mjs +28 -9
- package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace-helper.mjs.map +1 -1
- package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.d.mts.map +1 -1
- package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mjs +27 -3
- package/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mjs.map +1 -1
- package/dist/witness/space/reconciliation-space/reconciliation-space-base.d.mts +100 -0
- package/dist/witness/space/reconciliation-space/reconciliation-space-base.d.mts.map +1 -0
- package/dist/witness/space/reconciliation-space/reconciliation-space-base.mjs +758 -0
- package/dist/witness/space/reconciliation-space/reconciliation-space-base.mjs.map +1 -0
- package/dist/witness/space/reconciliation-space/reconciliation-space-helper.d.mts +40 -0
- package/dist/witness/space/reconciliation-space/reconciliation-space-helper.d.mts.map +1 -0
- package/dist/witness/space/reconciliation-space/reconciliation-space-helper.mjs +90 -0
- package/dist/witness/space/reconciliation-space/reconciliation-space-helper.mjs.map +1 -0
- package/dist/witness/space/space-helper.d.mts.map +1 -1
- package/dist/witness/space/space-helper.mjs +11 -2
- package/dist/witness/space/space-helper.mjs.map +1 -1
- package/dist/witness/space/space-respec-helper.d.mts.map +1 -1
- package/dist/witness/space/space-respec-helper.mjs +3 -0
- package/dist/witness/space/space-respec-helper.mjs.map +1 -1
- package/package.json +1 -1
- package/src/common/import-export/import-export-types.mts +8 -0
- package/src/timeline/timeline-api.mts +943 -0
- package/src/timeline/timeline-types.mts +38 -0
- package/src/witness/space/filesystem-space/filesystem-space-v1.mts +0 -3
- package/src/witness/space/inner-space/inner-space-constants.mts +1 -0
- package/src/witness/space/inner-space/inner-space-types.mts +44 -0
- package/src/witness/space/inner-space/inner-space-v1.mts +157 -28
- package/src/witness/space/inner-space/inner-space-v1.respec.mts +242 -58
- package/src/witness/space/metaspace/metaspace-base.mts +7 -1
- package/src/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace-helper.mts +32 -10
- package/src/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mts +76 -53
- package/src/witness/space/reconciliation-space/reconciliation-space-base.mts +884 -0
- package/src/witness/space/reconciliation-space/reconciliation-space-helper.mts +125 -0
- package/src/witness/space/space-helper.mts +6 -2
- 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
|
+
}
|