@ibgib/core-gib 0.1.38 → 0.1.40

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 (37) hide show
  1. package/dist/common/other/graph-helper.d.mts.map +1 -1
  2. package/dist/common/other/graph-helper.mjs +18 -3
  3. package/dist/common/other/graph-helper.mjs.map +1 -1
  4. package/dist/sync/graft-info/graft-info-helpers.mjs +1 -1
  5. package/dist/sync/graft-info/graft-info-helpers.mjs.map +1 -1
  6. package/dist/sync/sync-conflict-adv-multitimelines.respec.mjs +321 -235
  7. package/dist/sync/sync-conflict-adv-multitimelines.respec.mjs.map +1 -1
  8. package/dist/sync/sync-conflict-basic-multitimelines.respec.mjs +6 -6
  9. package/dist/sync/sync-conflict-basic-multitimelines.respec.mjs.map +1 -1
  10. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs +1 -1
  11. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
  12. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  13. package/dist/sync/sync-saga-coordinator.mjs +49 -12
  14. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  15. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +4 -0
  16. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
  17. package/dist/test-helpers.d.mts +104 -47
  18. package/dist/test-helpers.d.mts.map +1 -1
  19. package/dist/test-helpers.mjs +335 -131
  20. package/dist/test-helpers.mjs.map +1 -1
  21. package/dist/test-types.d.mts +25 -7
  22. package/dist/test-types.d.mts.map +1 -1
  23. package/dist/witness/space/reconciliation-space/reconciliation-space-helper.mjs +2 -2
  24. package/dist/witness/space/reconciliation-space/reconciliation-space-helper.mjs.map +1 -1
  25. package/package.json +1 -1
  26. package/src/common/other/graph-helper.mts +14 -2
  27. package/src/sync/SYNC_TESTING.md +135 -70
  28. package/src/sync/graft-info/graft-info-helpers.mts +1 -1
  29. package/src/sync/sync-conflict-adv-multitimelines.respec.mts +332 -245
  30. package/src/sync/sync-conflict-basic-multitimelines.respec.mts +5 -5
  31. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +1 -1
  32. package/src/sync/sync-saga-coordinator.mts +58 -14
  33. package/src/sync/sync-saga-message/sync-saga-message-types.mts +4 -0
  34. package/src/test-helpers.mts +429 -152
  35. package/src/test-types.mts +30 -7
  36. package/src/witness/space/reconciliation-space/reconciliation-space-helper.mts +2 -2
  37. package/tmp.md +205 -1160
@@ -8,6 +8,7 @@
8
8
 
9
9
  import { extractErrorMsg, getUUID } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
10
10
  import { IbGib_V1, IbGibRel8ns_V1, IbGibData_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
11
+ import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
11
12
 
12
13
  import { GLOBAL_LOG_A_LOT } from './core-constants.mjs';
13
14
  import { KeystoneService_V1 } from './keystone/keystone-service-v1.mjs';
@@ -16,7 +17,7 @@ import { Factory_V1 } from '@ibgib/ts-gib/dist/V1/factory.mjs';
16
17
  import { createTimeline, mut8Timeline, appendToTimeline } from './timeline/timeline-api.mjs';
17
18
  import { persistTransformResult, registerNewIbGib } from './witness/space/space-helper.mjs';
18
19
  import { MetaspaceService } from './witness/space/metaspace/metaspace-types.mjs';
19
- import { TransformResult } from '@ibgib/ts-gib/dist/types.mjs';
20
+ import { Ib, TransformResult } from '@ibgib/ts-gib/dist/types.mjs';
20
21
  import { TestMut8Info, TestRel8Info, TestStepInfo, TestRound } from './test-types.mjs';
21
22
 
22
23
  const logalot = GLOBAL_LOG_A_LOT;
@@ -86,16 +87,6 @@ export async function createTimelineRootTestHelper<TData extends IbGibData_V1 =
86
87
  try {
87
88
  if (logalot) { console.log(`${lc} starting... (I: 8f50a8cf0268742048f7c188feb5a825)`); }
88
89
 
89
- // const resTimeline = await createTimeline({
90
- // ib,
91
- // data,
92
- // rel8ns: undefined,
93
- // metaspace, space
94
- // });
95
- // const timeline = resTimeline.newIbGib;
96
-
97
- // return timeline;
98
-
99
90
  // Determine the parent to fork based on the provided options.
100
91
  const atom = ib.split(' ').at(0)!;
101
92
  const parentIbGib = Factory_V1.primitive({ ib: atom });
@@ -159,6 +150,140 @@ export async function getTestKeystoneServiceHelper(): Promise<KeystoneService_V1
159
150
  return new MockKeystoneService() as unknown as KeystoneService_V1;
160
151
  }
161
152
 
153
+ export interface TestIbGibIb {
154
+ /**
155
+ * @example alpha, beta, gamma, etc.
156
+ */
157
+ atom: string;
158
+ /**
159
+ * concatenated r0 mut8 history
160
+ * @example r0, r0r1, r0r1r2
161
+ */
162
+ rounds: string
163
+ /**
164
+ * @example 0 for r0, 1 for r0r1, 2 for r0r1r2, etc.
165
+ */
166
+ roundNumber: number
167
+ /**
168
+ * concatenated v0 mut8 history
169
+ * @example v0, v0v1, v0v1v2
170
+ */
171
+ versions: string
172
+ /**
173
+ * @example 0 for v0, 1 for v0v1, 2 for v0v1v2, etc.
174
+ */
175
+ versionNumber: number
176
+ /**
177
+ * optional comments sans spaces, e.g., "thisIsAComment_thisIsAnother_heckWhyNotAThird"
178
+ */
179
+ comments_snake_plus_CamelCase: string | undefined;
180
+ }
181
+
182
+ /**
183
+ * r0, r0r1, r0r1r2
184
+ */
185
+ const TEST_IB_ROUNDS_REGEXP = /^(r\d)+$/;
186
+ /**
187
+ * v0, v0v1, v0v1v2
188
+ */
189
+ const TEST_IB_VERSIONS_REGEXP = /^(v\d)+$/;
190
+
191
+ /**
192
+ * Parses ib to extract base, rounds, and version.
193
+ * Schema: [base] [r1r2...] [v0v1...] (space-delimited)
194
+ * Example: "alpha r0r1 v0v1v2" → { base: "alpha", rounds: "r1", version: 2 }
195
+ */
196
+ export function parseTestIb(ib: string): TestIbGibIb {
197
+ const lc = `[${parseTestIb.name}]`;
198
+ try {
199
+ if (logalot) { console.log(`${lc} starting... (I: 87b6d85567f2e2a4b84a4278fb1bc826)`); }
200
+
201
+ const parts = ib.split(' ');
202
+
203
+ if (parts.length === 1) {
204
+ // Initial ib, no rounds or versions yet
205
+ throw new Error(`(UNEXPECTED) does this ever hit? (E: 584978e0c644aec1e86f7e964a547526)`);
206
+ // return {
207
+ // atom: parts[0],
208
+ // rounds: '',
209
+ // roundNumber: -1,
210
+ // versions: '',
211
+ // versionNumber: -1,
212
+ // comments_snake_plus_CamelCase: undefined,
213
+ // };
214
+ }
215
+
216
+ const [
217
+ atom, // alpha, beta, etc.
218
+ rounds,
219
+ versions,
220
+ comments_snake_plus_CamelCase,
221
+ ] = parts;
222
+
223
+ // ignoring possibility of later erroneous parts for now
224
+
225
+ // Find round and version parts
226
+ if (!rounds) { throw new Error(`invalid ib. rounds falsy (E: b706c8005e7f619e0386b6981d835826)`); }
227
+ if (!TEST_IB_ROUNDS_REGEXP.test(rounds)) { throw new Error(`invalid ib. rounds (${rounds}) does not match TEST_IB_ROUNDS_REGEXP: ${TEST_IB_ROUNDS_REGEXP.source} (E: 5498b89c25321a97a8728be82b1f7b26)`); }
228
+ const roundNumber = Number.parseInt(rounds.split('r').at(-1)!);
229
+ if (!versions) { throw new Error(`invalid ib. versions falsy (E: 79aa180471b434c685ea27987b50e726)`); }
230
+ if (!TEST_IB_VERSIONS_REGEXP.test(versions)) { throw new Error(`invalid ib. versions (${versions}) does not match TEST_IB_VERSIONS_REGEXP: ${TEST_IB_VERSIONS_REGEXP.source} (E: 5498b89c25321a97a8728be82b1f7b26)`); }
231
+ const versionNumber = Number.parseInt(versions.split('v').at(-1)!);
232
+
233
+ return {
234
+ atom,
235
+ rounds,
236
+ roundNumber,
237
+ versions,
238
+ versionNumber,
239
+ comments_snake_plus_CamelCase,
240
+ }
241
+
242
+ } catch (error) {
243
+ console.error(`${lc} ${extractErrorMsg(error)}`);
244
+ throw error;
245
+ } finally {
246
+ if (logalot) { console.log(`${lc} complete.`); }
247
+ }
248
+ }
249
+
250
+ function getNewTestIb({
251
+ atom,
252
+ comments_snake_plus_CamelCase,
253
+ }: {
254
+ /**
255
+ * should be 'alpha', 'beta', 'gamma', etc.
256
+ */
257
+ atom: string,
258
+ /**
259
+ * @default undefined
260
+ */
261
+ comments_snake_plus_CamelCase?: string,
262
+ }): Ib {
263
+ const lc = `[${getNewTestIb.name}]`;
264
+ try {
265
+ if (logalot) { console.log(`${lc} starting... (I: 60aebdb828f72bbfbcbf401e7af09826)`); }
266
+ if (!atom) throw new Error(`atom required (E: dc041852cdd88d692a8c2168ae4c7626)`);
267
+
268
+ let ib = [
269
+ atom,
270
+ 'r1',
271
+ 'v0',
272
+ ].join(' ');
273
+
274
+ if (comments_snake_plus_CamelCase) {
275
+ ib += ' ' + comments_snake_plus_CamelCase;
276
+ }
277
+
278
+ return ib;
279
+ } catch (error) {
280
+ console.error(`${lc} ${extractErrorMsg(error)}`);
281
+ throw error;
282
+ } finally {
283
+ if (logalot) { console.log(`${lc} complete.`); }
284
+ }
285
+ }
286
+
162
287
  /**
163
288
  * Stateful transformer for test operations.
164
289
  * Manages counters, rounds, and execution history.
@@ -169,40 +294,45 @@ export async function getTestKeystoneServiceHelper(): Promise<KeystoneService_V1
169
294
  * const r1 = transformer.newRound({ name: 'r1_common_history' });
170
295
  *
171
296
  * const step = await transformer.mut8({
172
- * ibGib: alpha, space: sourceSpace, metaspace,
173
- * name: 'r1_alpha_v1_source'
174
- * });
297
+ * Test transformer class that provides fluent method chaining for timeline testing.
298
+ * Automatically tracks rounds, steps, provides test data snapshots, and auto-versions ibs.
175
299
  *
176
- * // Later verify
177
- * r1.steps.forEach(step => {
178
- * const mut8Info = step.infos[0] as TestMut8Info;
179
- * // verify step.ibGib.data[mut8Info.key] === mut8Info.value
180
- * });
181
- * ```
300
+ * Ib versioning schema: `[base] [r1r2...] [v0v1v2]` (space-delimited)
301
+ * Example: `alpha r1 v0` → `alpha r1 v1` → `alpha r1r2 v0`
182
302
  */
183
303
  export class TestTransformer {
304
+ private lc = `[${TestTransformer.name}]`;
305
+ private metaspace: MetaspaceService;
306
+ private sourceSpace: IbGibSpaceAny;
307
+ private destSpace: IbGibSpaceAny;
184
308
  private mut8Counter = 0;
185
309
  private rel8Counter = 0;
186
310
  private rounds: TestRound[] = [];
187
- private currentRound?: TestRound;
311
+ private get currentRound(): TestRound | undefined {
312
+ return this.rounds && this.rounds.length > 0 ?
313
+ this.rounds.at(-1)! :
314
+ undefined;
315
+ };
316
+
317
+ constructor(
318
+ metaspace: MetaspaceService,
319
+ sourceSpace: IbGibSpaceAny,
320
+ destSpace: IbGibSpaceAny
321
+ ) {
322
+ this.metaspace = metaspace;
323
+ this.sourceSpace = sourceSpace;
324
+ this.destSpace = destSpace;
325
+ }
188
326
 
189
327
  /**
190
- * Start a new round of test operations.
328
+ * Start a new round of testing operations.
191
329
  */
192
330
  newRound({ name, description }: { name: string, description?: string }): TestRound {
193
331
  const round: TestRound = { name, description, steps: [] };
194
332
  this.rounds.push(round);
195
- this.currentRound = round;
196
333
  return round;
197
334
  }
198
335
 
199
- /**
200
- * Get the current active round.
201
- */
202
- getCurrentRound(): TestRound | undefined {
203
- return this.currentRound;
204
- }
205
-
206
336
  /**
207
337
  * Get all steps across all rounds.
208
338
  */
@@ -224,172 +354,319 @@ export class TestTransformer {
224
354
  this.mut8Counter = 0;
225
355
  this.rel8Counter = 0;
226
356
  this.rounds = [];
227
- this.currentRound = undefined;
228
357
  }
229
358
 
230
- private getNextMut8Key(prefix?: string): string {
231
- this.mut8Counter++;
232
- const baseKey = prefix ?? 'testField';
233
- return `${baseKey}_${this.mut8Counter}`;
359
+ /**
360
+ * Get space by location.
361
+ */
362
+ private getSpace(location: 'source' | 'dest'): IbGibSpaceAny {
363
+ return location === 'source' ? this.sourceSpace : this.destSpace;
234
364
  }
235
365
 
236
- private getNextMut8Value(): string {
237
- return `value_${this.mut8Counter}`;
238
- }
239
366
 
240
- private getNextRel8nName(prefix?: string): string {
241
- this.rel8Counter++;
242
- const baseName = prefix ?? 'testrel8n';
243
- return `${baseName}_${this.rel8Counter}`;
367
+ /**
368
+ * Generates next ib based on current ib and round context.
369
+ * Increments version and appends round if needed.
370
+ */
371
+ private generateNextIb({
372
+ currentIb,
373
+ comments_snake_plus_CamelCase,
374
+ }: {
375
+ currentIb: Ib,
376
+ comments_snake_plus_CamelCase?: string,
377
+ }): string {
378
+ const lc = `${this.lc}[${this.generateNextIb.name}]`;
379
+ try {
380
+ if (logalot) { console.log(`${lc} starting... (I: 9291a8f3c6b168e53856c61ba9d5c826)`); }
381
+
382
+ const { atom, rounds, roundNumber, versions, versionNumber } = parseTestIb(currentIb);
383
+
384
+ if (!this.currentRound) { throw new Error(`(UNEXPECTED) this.currentRound falsy? (E: 4594188ef0a859759daaa4b8ea1a7926)`); }
385
+
386
+ const currentRoundNumber = this.rounds.length;
387
+ const needsNewRounds = roundNumber !== currentRoundNumber;
388
+
389
+ /**
390
+ * the next rounds is only different if we are in a different round.
391
+ */
392
+ const nextRounds = needsNewRounds ?
393
+ `${rounds}r${roundNumber + 1}` :
394
+ rounds;
395
+ /**
396
+ * always increment the version
397
+ */
398
+ const nextVersions = `${versions}v${versionNumber + 1}`;
399
+
400
+ if (comments_snake_plus_CamelCase && comments_snake_plus_CamelCase.includes(' ')) {
401
+ throw new Error(`comments_snake_plus_CamelCase contains spaces. must be underscore-delimited camelCase comments (E: de725d4090de648bf221cbaf4e041826)`);
402
+ }
403
+
404
+ let nextIb = `${atom} ${nextRounds} ${nextVersions}`;
405
+ if (comments_snake_plus_CamelCase) {
406
+ nextIb += ' ' + comments_snake_plus_CamelCase;
407
+ }
408
+
409
+ return nextIb;
410
+ } catch (error) {
411
+ console.error(`${lc} ${extractErrorMsg(error)}`);
412
+ throw error;
413
+ } finally {
414
+ if (logalot) { console.log(`${lc} complete.`); }
415
+ }
244
416
  }
245
417
 
246
418
  /**
247
- * Perform a mutation with auto-generated field/value.
248
- * Automatically adds step to current round if one exists.
419
+ * Create a timeline root with auto-tracking to current round.
249
420
  *
250
421
  * @example
251
- * // Start new step
252
- * let step = await transformer.mut8({ ibGib: alpha, space, metaspace });
422
+ * const transformer = new TestTransformer(metaspace, sourceSpace, destSpace);
423
+ * transformer.newRound({ name: 'r1_setup' });
424
+ * const step = await transformer.create({
425
+ * ib: 'alpha',
426
+ * data: { type: 'alpha' },
427
+ * in: 'source',
428
+ * });
429
+ */
430
+ async create<TData extends IbGibData_V1 = any>({
431
+ atom,
432
+ data,
433
+ in: location,
434
+ name,
435
+ comments_snake_plus_CamelCase,
436
+ }: {
437
+ atom: string;
438
+ data?: TData;
439
+ in: 'source' | 'dest';
440
+ name?: string;
441
+ comments_snake_plus_CamelCase?: string,
442
+ }): Promise<TestStepInfo> {
443
+ const lc = `${this.lc}[create]`;
444
+
445
+ if (!this.currentRound) {
446
+ throw new Error(`${lc} No active round. Call newRound() first.`);
447
+ }
448
+
449
+ const space = this.getSpace(location);
450
+
451
+ const timeline = await createTimelineRootTestHelper<TData>({
452
+ ib: getNewTestIb({ atom, comments_snake_plus_CamelCase }),
453
+ data,
454
+ space,
455
+ });
456
+
457
+ const result: TestStepInfo = {
458
+ ibGib: timeline,
459
+ addr: getIbGibAddr({ ibGib: timeline }),
460
+ space,
461
+ metaspace: this.metaspace,
462
+ infos: [],
463
+ name: name || `${this.currentRound.name}_${atom}_create`,
464
+ };
465
+
466
+ this.currentRound.steps.push(result);
467
+
468
+ return result;
469
+ }
470
+
471
+ /**
472
+ * Perform a mutation with optional field mutation and auto-versioning.
253
473
  *
254
- * // Continue chaining
255
- * step = await transformer.mut8({ stepInfo: step, fieldPrefix: 'count' });
474
+ * @example
475
+ * // With field and auto value
476
+ * const step = await transformer.mut8({
477
+ * stepInfo: prevStep,
478
+ * strField: 'commonField',
479
+ * });
480
+ * // Creates: alpha r1 v0, sets commonField: 'value_1'
256
481
  *
257
482
  * @example
258
- * // With naming convention
483
+ * // With field and custom value
259
484
  * const step = await transformer.mut8({
260
- * ibGib: alpha, space, metaspace,
261
- * name: 'r1_alpha_v2_source'
485
+ * stepInfo: prevStep,
486
+ * strField: { name: 'count', value: '42' },
262
487
  * });
488
+ *
489
+ * @example
490
+ * // Ib-only mutation (no field)
491
+ * const step = await transformer.mut8({ stepInfo: prevStep });
263
492
  */
264
- async mut8({
493
+ async mut8<TData extends IbGibData_V1 = any>({
265
494
  ibGib,
266
- space,
267
- metaspace,
268
- fieldPrefix,
269
495
  stepInfo,
496
+ in: location,
497
+ strField,
270
498
  name,
499
+ comments_snake_plus_CamelCase,
271
500
  }: {
272
- ibGib?: IbGib_V1;
273
- space?: IbGibSpaceAny;
274
- metaspace: MetaspaceService; // Required for mut8Timeline
275
- fieldPrefix?: string;
501
+ ibGib?: IbGib_V1<TData>;
276
502
  stepInfo?: TestStepInfo;
503
+ in?: 'source' | 'dest';
504
+ strField?: string | { name: string; value: string };
277
505
  name?: string;
506
+ comments_snake_plus_CamelCase?: string,
278
507
  }): Promise<TestStepInfo> {
279
- const lc = `[${TestTransformer.name}.${this.mut8.name}]`;
280
- try {
281
- const currentIbGib = stepInfo?.ibGib ?? ibGib;
282
- const currentSpace = stepInfo?.space ?? space;
283
- const currentMetaspace = stepInfo?.metaspace ?? metaspace;
284
- const currentName = name ?? stepInfo?.name;
285
-
286
- if (!currentIbGib) throw new Error(`ibGib required (E: 8c4a5e1f2b3d4e5f6a7b8c9d0e1f2a3b)`);
287
- if (!currentSpace) throw new Error(`space required (E: 9d5b6f2c3e4a5d6f7b8c9d0e1f2a3b4c)`);
288
-
289
- const key = this.getNextMut8Key(fieldPrefix);
290
- const value = this.getNextMut8Value();
291
- const mut8Ib = `${currentIbGib.ib} x_${value}`;
292
-
293
- const mutated = await mut8Timeline({
294
- timeline: currentIbGib,
295
- mut8Opts: {
296
- mut8Ib,
297
- dataToAddOrPatch: { [key]: value }
298
- },
299
- metaspace: currentMetaspace,
300
- space: currentSpace,
301
- });
508
+ const lc = `${this.lc}[mut8]`;
302
509
 
303
- const mut8Info: TestMut8Info = { key, value };
304
- const result: TestStepInfo = {
305
- ibGib: mutated,
306
- space: currentSpace,
307
- metaspace: currentMetaspace,
308
- infos: [...(stepInfo?.infos ?? []), mut8Info],
309
- name: currentName,
310
- };
510
+ if (!this.currentRound) {
511
+ throw new Error(`${lc} No active round. Call newRound() first.`);
512
+ }
311
513
 
312
- // Add to current round if exists
313
- this.currentRound?.steps.push(result);
514
+ // Extract context
515
+ let targetIbGib: IbGib_V1<TData>;
516
+ let space: IbGibSpaceAny;
517
+
518
+ if (stepInfo) {
519
+ targetIbGib = stepInfo.ibGib as IbGib_V1<TData>;
520
+ space = stepInfo.space;
521
+ } else if (ibGib && location) {
522
+ targetIbGib = ibGib;
523
+ space = this.getSpace(location);
524
+ } else {
525
+ throw new Error(`${lc} Must provide either stepInfo or (ibGib + in)`);
526
+ }
314
527
 
315
- return result;
316
- } catch (error) {
317
- console.error(`${lc} ${extractErrorMsg(error)}`);
318
- throw error;
528
+ // Generate next ib with auto-versioning
529
+ const nextIb = this.generateNextIb({
530
+ currentIb: targetIbGib.ib,
531
+ comments_snake_plus_CamelCase,
532
+ });
533
+
534
+ // Build data to add/patch
535
+ const dataToAddOrPatch: Partial<TData> = {};
536
+
537
+ if (strField) {
538
+ let fieldName: string;
539
+ let fieldValue: string;
540
+
541
+ if (typeof strField === 'string') {
542
+ // Field name only, generate random value
543
+ fieldName = strField;
544
+ fieldValue = `value_${++this.mut8Counter}`;
545
+ } else {
546
+ // Field name and value provided
547
+ fieldName = strField.name;
548
+ fieldValue = strField.value;
549
+ this.mut8Counter++; // Still increment counter
550
+ }
551
+
552
+ (dataToAddOrPatch as any)[fieldName] = fieldValue;
319
553
  }
554
+
555
+ // Perform mutation
556
+ const mutatedIbGib = await mut8Timeline({
557
+ timeline: targetIbGib,
558
+ mut8Opts: {
559
+ mut8Ib: nextIb,
560
+ dataToAddOrPatch,
561
+ },
562
+ metaspace: this.metaspace,
563
+ space,
564
+ });
565
+
566
+ const mut8Info: TestMut8Info = {
567
+ type: 'mut8',
568
+ ib: nextIb,
569
+ key: strField ? (typeof strField === 'string' ? strField : strField.name) : undefined,
570
+ value: strField ? (typeof strField === 'string' ? `value_${this.mut8Counter}` : strField.value) : undefined,
571
+ };
572
+
573
+ const result: TestStepInfo = {
574
+ ibGib: mutatedIbGib,
575
+ addr: getIbGibAddr({ ibGib: mutatedIbGib }),
576
+ space,
577
+ metaspace: this.metaspace,
578
+ infos: [mut8Info],
579
+ name: name || `${this.currentRound.name}_${nextIb}`,
580
+ };
581
+
582
+ this.currentRound.steps.push(result);
583
+
584
+ return result;
320
585
  }
321
586
 
322
587
  /**
323
- * Perform a relation with auto-generated rel8n name.
324
- * Automatically adds step to current round if one exists.
588
+ * Perform a relation with auto-generated rel8n name and auto-versioning.
325
589
  *
326
590
  * @example
327
- * // Start new step
328
- * let step = await transformer.rel8({
329
- * ibGib: alpha, targetIbGibs: [beta],
330
- * space, metaspace
591
+ * const step = await transformer.rel8({
592
+ * stepInfo: prevStep,
593
+ * targetIbGibs: [beta, gamma],
594
+ * rel8nName: 'depends_on',
331
595
  * });
332
- *
333
- * // Continue chaining
334
- * step = await transformer.rel8({ stepInfo: step, targetIbGibs: [gamma] });
335
596
  */
336
597
  async rel8({
598
+ stepInfo,
337
599
  ibGib,
600
+ in: location,
338
601
  targetIbGibs,
339
- space,
340
- metaspace,
341
- rel8nPrefix,
342
- stepInfo,
602
+ rel8nName,
343
603
  name,
604
+ // comments_snake_plus_CamelCase,
344
605
  }: {
345
- ibGib?: IbGib_V1;
346
- targetIbGibs: IbGib_V1 | IbGib_V1[];
347
- space?: IbGibSpaceAny;
348
- metaspace?: MetaspaceService;
349
- rel8nPrefix?: string;
350
606
  stepInfo?: TestStepInfo;
607
+ ibGib?: IbGib_V1;
608
+ in: 'source' | 'dest';
609
+ targetIbGibs: IbGib_V1[];
610
+ rel8nName: string;
351
611
  name?: string;
612
+ // comments_snake_plus_CamelCase?: string,
352
613
  }): Promise<TestStepInfo> {
353
- const lc = `[${TestTransformer.name}.${this.rel8.name}]`;
354
- try {
355
- const currentIbGib = stepInfo?.ibGib ?? ibGib;
356
- const currentSpace = stepInfo?.space ?? space;
357
- const currentMetaspace = stepInfo?.metaspace ?? metaspace;
358
- const currentName = name ?? stepInfo?.name;
359
-
360
- if (!currentIbGib) throw new Error(`ibGib required (E: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6)`);
361
- if (!currentSpace) throw new Error(`space required (E: b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7)`);
362
- if (!currentMetaspace) throw new Error(`metaspace required (E: c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8)`);
363
-
364
- const rel8nName = this.getNextRel8nName(rel8nPrefix);
365
- const targets = Array.isArray(targetIbGibs) ? targetIbGibs : [targetIbGibs];
366
-
367
- const related = await appendToTimeline({
368
- timeline: currentIbGib,
369
- metaspace: currentMetaspace,
370
- space: currentSpace,
371
- rel8nInfos: [{
372
- rel8nName,
373
- ibGibs: targets
374
- }],
375
- });
376
-
377
- const rel8Info: TestRel8Info = { rel8nName, targetIbGibs: targets };
378
- const result: TestStepInfo = {
379
- ibGib: related,
380
- space: currentSpace,
381
- metaspace: currentMetaspace,
382
- infos: [...(stepInfo?.infos ?? []), rel8Info],
383
- name: currentName,
384
- };
614
+ const lc = `${this.lc}[rel8]`;
385
615
 
386
- // Add to current round if exists
387
- this.currentRound?.steps.push(result);
616
+ if (!this.currentRound) {
617
+ throw new Error(`${lc} No active round. Call newRound() first.`);
618
+ }
388
619
 
389
- return result;
390
- } catch (error) {
391
- console.error(`${lc} ${extractErrorMsg(error)}`);
392
- throw error;
620
+ // Extract context
621
+ let sourceIbGib: IbGib_V1;
622
+ let space: IbGibSpaceAny;
623
+
624
+ if (stepInfo) {
625
+ sourceIbGib = stepInfo.ibGib;
626
+ space = stepInfo.space;
627
+ } else if (ibGib && location) {
628
+ sourceIbGib = ibGib;
629
+ space = this.getSpace(location);
630
+ } else {
631
+ throw new Error(`${lc} Must provide either stepInfo or (ibGib + in)`);
393
632
  }
633
+
634
+ // Generate next ib with auto-versioning
635
+ // we can't do this right now because the rel8 transform cannot itself
636
+ // change the ib. this only currently is available through the mut8
637
+ // call.
638
+ // const nextIb = this.generateNextIb({currentIb: sourceIbGib.ib, comments_snake_plus_CamelCase});
639
+
640
+ // Perform relation
641
+ const relatedIbGib = await appendToTimeline({
642
+ timeline: sourceIbGib,
643
+ rel8nInfos: [{
644
+ rel8nName,
645
+ ibGibs: targetIbGibs
646
+ }],
647
+ metaspace: this.metaspace,
648
+ space,
649
+ });
650
+
651
+ const rel8Info: TestRel8Info = {
652
+ type: 'rel8',
653
+ ib: sourceIbGib.ib,
654
+ rel8nName,
655
+ targetAddrs: targetIbGibs.map(x => getIbGibAddr({ ibGib: x })),
656
+ };
657
+
658
+ const result: TestStepInfo = {
659
+ ibGib: relatedIbGib,
660
+ addr: getIbGibAddr({ ibGib: relatedIbGib }),
661
+ space,
662
+ metaspace: this.metaspace,
663
+ infos: [rel8Info],
664
+ name: name || `${this.currentRound.name}_autoname_${sourceIbGib.ib}`,
665
+ };
666
+
667
+ this.currentRound.steps.push(result);
668
+
669
+ return result;
394
670
  }
395
671
  }
672
+