@ibgib/core-gib 0.1.13 → 0.1.15
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/keystone/keystone-helpers.mjs +3 -3
- package/dist/keystone/keystone-helpers.mjs.map +1 -1
- package/dist/sync/sync-constants.d.mts +6 -1
- package/dist/sync/sync-constants.d.mts.map +1 -1
- package/dist/sync/sync-constants.mjs +5 -0
- package/dist/sync/sync-constants.mjs.map +1 -1
- package/dist/sync/sync-helpers.d.mts +18 -2
- package/dist/sync/sync-helpers.d.mts.map +1 -1
- package/dist/sync/sync-helpers.mjs +84 -3
- package/dist/sync/sync-helpers.mjs.map +1 -1
- package/dist/sync/sync-innerspace.respec.d.mts +1 -1
- package/dist/sync/sync-innerspace.respec.mjs +52 -370
- package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-innerspace-v1.d.mts +39 -0
- package/dist/sync/sync-peer/sync-peer-innerspace-v1.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-innerspace-v1.mjs +112 -0
- package/dist/sync/sync-peer/sync-peer-innerspace-v1.mjs.map +1 -0
- package/dist/sync/sync-peer/sync-peer-types.d.mts +30 -0
- package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-types.mjs +5 -0
- package/dist/sync/sync-peer/sync-peer-types.mjs.map +1 -0
- package/dist/sync/sync-peer/sync-peer-v1.d.mts +22 -0
- package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-v1.mjs +13 -0
- package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -0
- package/dist/sync/sync-saga-context/sync-saga-context-constants.d.mts +8 -0
- package/dist/sync/sync-saga-context/sync-saga-context-constants.d.mts.map +1 -0
- package/dist/sync/sync-saga-context/sync-saga-context-constants.mjs +8 -0
- package/dist/sync/sync-saga-context/sync-saga-context-constants.mjs.map +1 -0
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts +54 -0
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts.map +1 -0
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs +87 -0
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs.map +1 -0
- package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts +66 -0
- package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts.map +1 -0
- package/dist/sync/sync-saga-context/sync-saga-context-types.mjs +12 -0
- package/dist/sync/sync-saga-context/sync-saga-context-types.mjs.map +1 -0
- package/dist/sync/sync-saga-coordinator.d.mts +201 -121
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +710 -434
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
- package/dist/sync/sync-saga-coordinator.respec.mjs +7 -7
- package/dist/sync/sync-saga-coordinator.respec.mjs.map +1 -1
- package/dist/sync/sync-saga-message/sync-saga-message-constants.d.mts +2 -0
- package/dist/sync/sync-saga-message/sync-saga-message-constants.d.mts.map +1 -0
- package/dist/sync/sync-saga-message/sync-saga-message-constants.mjs +2 -0
- package/dist/sync/sync-saga-message/sync-saga-message-constants.mjs.map +1 -0
- package/dist/sync/sync-saga-message/sync-saga-message-helpers.d.mts +15 -0
- package/dist/sync/sync-saga-message/sync-saga-message-helpers.d.mts.map +1 -0
- package/dist/sync/sync-saga-message/sync-saga-message-helpers.mjs +43 -0
- package/dist/sync/sync-saga-message/sync-saga-message-helpers.mjs.map +1 -0
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +51 -0
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -0
- package/dist/sync/sync-saga-message/sync-saga-message-types.mjs +2 -0
- package/dist/sync/sync-saga-message/sync-saga-message-types.mjs.map +1 -0
- package/dist/sync/sync-types.d.mts +85 -4
- package/dist/sync/sync-types.d.mts.map +1 -1
- package/dist/sync/sync-types.mjs +27 -1
- package/dist/sync/sync-types.mjs.map +1 -1
- package/dist/timeline/timeline-api.d.mts +16 -3
- package/dist/timeline/timeline-api.d.mts.map +1 -1
- package/dist/timeline/timeline-api.mjs +7 -7
- package/dist/timeline/timeline-api.mjs.map +1 -1
- package/dist/witness/space/outer-space/outer-space-types.d.mts +2 -0
- package/dist/witness/space/outer-space/outer-space-types.d.mts.map +1 -1
- package/dist/witness/space/space-base-v1.d.mts +19 -1
- package/dist/witness/space/space-base-v1.d.mts.map +1 -1
- package/dist/witness/space/space-base-v1.mjs +66 -6
- package/dist/witness/space/space-base-v1.mjs.map +1 -1
- package/dist/witness/space/space-helper.d.mts +14 -0
- package/dist/witness/space/space-helper.d.mts.map +1 -1
- package/dist/witness/space/space-helper.mjs +44 -1
- 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 +1 -1
- package/dist/witness/space/space-respec-helper.mjs.map +1 -1
- package/dist/witness/space/space-types.d.mts +12 -1
- package/dist/witness/space/space-types.d.mts.map +1 -1
- package/dist/witness/space/space-types.mjs +4 -0
- package/dist/witness/space/space-types.mjs.map +1 -1
- package/package.json +2 -2
- package/src/keystone/keystone-helpers.mts +3 -3
- package/src/sync/README.md +275 -0
- package/src/sync/sync-constants.mts +9 -1
- package/src/sync/sync-helpers.mts +105 -6
- package/src/sync/sync-innerspace.respec.mts +55 -402
- package/src/sync/sync-peer/sync-peer-innerspace-v1.mts +150 -0
- package/src/sync/sync-peer/sync-peer-types.mts +43 -0
- package/src/sync/sync-peer/sync-peer-v1.mts +28 -0
- package/src/sync/sync-saga-context/sync-saga-context-constants.mts +8 -0
- package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +147 -0
- package/src/sync/sync-saga-context/sync-saga-context-types.mts +80 -0
- package/src/sync/sync-saga-coordinator.mts +913 -517
- package/src/sync/sync-saga-coordinator.respec.mts +7 -7
- package/src/sync/sync-saga-message/sync-saga-message-constants.mts +1 -0
- package/src/sync/sync-saga-message/sync-saga-message-helpers.mts +59 -0
- package/src/sync/sync-saga-message/sync-saga-message-types.mts +66 -0
- package/src/sync/sync-types.mts +107 -4
- package/src/timeline/timeline-api.mts +20 -4
- package/src/witness/space/space-base-v1.mts +62 -12
- package/src/witness/space/space-helper.mts +50 -1
- package/src/witness/space/space-respec-helper.mts +2 -1
- package/src/witness/space/space-types.mts +13 -1
- package/tmp.md +122 -4
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @module sync-innerspace.respec
|
|
3
3
|
*
|
|
4
4
|
* Verifies SyncSagaCoordinator using InnerSpace (in-memory/local) spaces.
|
|
5
|
-
* This
|
|
5
|
+
* This utilizes SyncPeerInnerspace_V1 for local-to-local synchronization simulations.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {
|
|
@@ -12,446 +12,99 @@ import {
|
|
|
12
12
|
const maam = `[${import.meta.url}]`, sir = maam;
|
|
13
13
|
import { pretty } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
14
14
|
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
15
|
-
import { IbGibData_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
|
|
16
15
|
|
|
17
16
|
import { SyncSagaCoordinator } from './sync-saga-coordinator.mjs';
|
|
18
17
|
import { putInSpace, getFromSpace } from '../witness/space/space-helper.mjs';
|
|
19
18
|
import { Metaspace_Innerspace } from '../witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mjs';
|
|
20
19
|
import { InnerSpace_V1 } from '../witness/space/inner-space/inner-space-v1.mjs';
|
|
21
20
|
import { createStoneHelper, createTimelineRootHelper, getTestKeystoneServiceHelper } from '../agent-helpers.mjs';
|
|
22
|
-
import { mut8Timeline, appendToTimeline
|
|
21
|
+
import { mut8Timeline, appendToTimeline } from '../timeline/timeline-api.mjs';
|
|
23
22
|
import { getDependencyGraph } from '../common/other/graph-helper.mjs';
|
|
24
|
-
import {
|
|
25
|
-
import { FlatIbGibGraph } from '../common/other/graph-types.mjs';
|
|
23
|
+
import { SyncPeerInnerspace_V1 } from './sync-peer/sync-peer-innerspace-v1.mjs';
|
|
26
24
|
|
|
27
25
|
const logalot = true;
|
|
28
26
|
const lc = `[sync-innerspace.respec]`;
|
|
29
27
|
|
|
30
|
-
/**
|
|
31
|
-
* naive helper function to determine if two dependency graphs are equal.
|
|
32
|
-
*
|
|
33
|
-
* naive: only checks length of keys and if keys all match.
|
|
34
|
-
*
|
|
35
|
-
* @param a dependency graph from one space
|
|
36
|
-
* @param b dep. graph from another space
|
|
37
|
-
* @returns true if graphs are equal in size and keys, else false.
|
|
38
|
-
*/
|
|
39
|
-
function naiveGraphsAreEqual(a: FlatIbGibGraph, b: FlatIbGibGraph): boolean {
|
|
40
|
-
const keysA = Object.keys(a);
|
|
41
|
-
const keysB = Object.keys(b);
|
|
42
|
-
if (keysA.length !== keysB.length) { false; }
|
|
43
|
-
return keysA.every(key => !!b[key]);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
28
|
await respecfully(sir, `Sync InnerSpaces`, async () => {
|
|
47
29
|
|
|
48
30
|
let metaspace: Metaspace_Innerspace;
|
|
49
31
|
let sourceSpace: InnerSpace_V1;
|
|
50
32
|
let destSpace: InnerSpace_V1;
|
|
51
33
|
|
|
52
|
-
|
|
53
|
-
// For now, let's just do it inside the test block to be safe/simple.
|
|
54
|
-
|
|
55
|
-
// await ifWeMight(sir, `Basic Push Sync (Source -> Dest)`, async () => {
|
|
56
|
-
// // 1. Setup Spaces
|
|
57
|
-
// metaspace = new Metaspace_Innerspace(undefined);
|
|
58
|
-
// await metaspace.initialize({
|
|
59
|
-
// getFnAlert: () => async ({ title, msg }) => { console.log(`[Alert] ${title}: ${msg}`); },
|
|
60
|
-
// getFnPrompt: () => async ({ title, msg }) => { console.log(`[Prompt] ${title}: ${msg}`); return ''; },
|
|
61
|
-
// getFnPromptPassword: () => async (title, msg) => { console.log(`[PromptPwd] ${title}: ${msg}`); return null; },
|
|
62
|
-
// });
|
|
63
|
-
|
|
64
|
-
// // Create two distinct inner spaces manually or via metaspace if supported
|
|
65
|
-
// // Metaspace_Innerspace typically manages one local user space, but we can instantiate InnerSpace_V1 directly.
|
|
66
|
-
// sourceSpace = new InnerSpace_V1({ name: 'source', uuid: 'source_uuid' } as any);
|
|
67
|
-
// await (sourceSpace as any).initialize();
|
|
68
|
-
|
|
69
|
-
// destSpace = new InnerSpace_V1({ name: 'dest', uuid: 'dest_uuid' } as any);
|
|
70
|
-
// await (destSpace as any).initialize();
|
|
71
|
-
|
|
72
|
-
// // 2. Seed Source Data
|
|
73
|
-
|
|
74
|
-
// // 2.1 Create a "Stone"
|
|
75
|
-
// console.log(`${lc} Creating Stone...`);
|
|
76
|
-
// const stone = await createStoneHelper({
|
|
77
|
-
// ib: 'stone_data',
|
|
78
|
-
// data: { some: 'data' },
|
|
79
|
-
// });
|
|
80
|
-
// const stoneAddr = getIbGibAddr({ ibGib: stone });
|
|
81
|
-
// await putInSpace({ space: sourceSpace, ibGibs: [stone] });
|
|
82
|
-
|
|
83
|
-
// // 2.2 Create a "Living" Timeline (Root)
|
|
84
|
-
// console.log(`${lc} Creating Timeline Root...`);
|
|
85
|
-
// const root = await createTimelineRootHelper({
|
|
86
|
-
// ib: 'timeline_root',
|
|
87
|
-
// data: { type: 'root', n: 0 },
|
|
88
|
-
// space: sourceSpace,
|
|
89
|
-
// });
|
|
90
|
-
// const rootAddr = getIbGibAddr({ ibGib: root });
|
|
91
|
-
|
|
92
|
-
// // 2.3 Evolve Timeline (Root -> Child)
|
|
93
|
-
// // using mut8Timeline to simulate evolution
|
|
94
|
-
// console.log(`${lc} Evolving Timeline...`);
|
|
95
|
-
// const child = await mut8Timeline({
|
|
96
|
-
// timeline: root,
|
|
97
|
-
// mut8Opts: {
|
|
98
|
-
// dataToAddOrPatch: { type: 'child', n: 1 }
|
|
99
|
-
// },
|
|
100
|
-
// metaspace,
|
|
101
|
-
// space: sourceSpace,
|
|
102
|
-
// // We use skipLock=true for simplicity in single-threaded test setup if locking is complex,
|
|
103
|
-
// // but timeline-api handles locking. Let's try default first.
|
|
104
|
-
// // Actually InnerSpace locking might be no-op or simple.
|
|
105
|
-
// });
|
|
106
|
-
// const childAddr = getIbGibAddr({ ibGib: child });
|
|
107
|
-
|
|
108
|
-
// // 2.4 Rel8 Child to Stone (Dependency)
|
|
109
|
-
// console.log(`${lc} Linking Child to Stone...`);
|
|
110
|
-
// const childWithRel = await appendToTimeline({
|
|
111
|
-
// timeline: child,
|
|
112
|
-
// rel8nInfos: [{
|
|
113
|
-
// rel8nName: 'linked_stone',
|
|
114
|
-
// ibGibs: [stone]
|
|
115
|
-
// }],
|
|
116
|
-
// metaspace,
|
|
117
|
-
// space: sourceSpace,
|
|
118
|
-
// });
|
|
119
|
-
// const childWithRelAddr = getIbGibAddr({ ibGib: childWithRel });
|
|
120
|
-
|
|
121
|
-
// // 3. Setup Sync Coordinator
|
|
122
|
-
// console.log(`${lc} Setting up Coordinator...`);
|
|
123
|
-
// const mockKeystone = await getTestKeystoneServiceHelper();
|
|
124
|
-
// const identity = await (mockKeystone as any).getIdentity();
|
|
125
|
-
// const coordinator = new SyncSagaCoordinator(mockKeystone);
|
|
126
|
-
|
|
127
|
-
// // 4. Run Sync
|
|
128
|
-
// console.log(`${lc} Running Sync...`);
|
|
129
|
-
// const syncRes = await coordinator.sync({
|
|
130
|
-
// source: sourceSpace,
|
|
131
|
-
// dest: destSpace,
|
|
132
|
-
// domainIbGibs: [childWithRel], // Sync starting from the tip
|
|
133
|
-
// identity
|
|
134
|
-
// });
|
|
135
|
-
|
|
136
|
-
// // 5. Verify Dest (Full Dependency Graph)
|
|
137
|
-
// console.log(`${lc} Verifying Destination (Full Graph)...`);
|
|
138
|
-
|
|
139
|
-
// // We expect the graph to contain:
|
|
140
|
-
// // 1. childWithRel
|
|
141
|
-
// // 2. child (past of childWithRel)
|
|
142
|
-
// // 3. root (ancestor of child, past of child)
|
|
143
|
-
// // 4. stone (linked to childWithRel)
|
|
144
|
-
// // 5. primitives (timeline_root, child, stone_data, identity, fork, mut8, rel8, root^gib, etc.)
|
|
145
|
-
|
|
146
|
-
// // Let's get the graph from the source first to know what to expect (ground truth)
|
|
147
|
-
// const sourceGraph = await getDependencyGraph({
|
|
148
|
-
// ibGibs: [childWithRel],
|
|
149
|
-
// space: sourceSpace,
|
|
150
|
-
// });
|
|
151
|
-
// const sourceAddrs = Object.keys(sourceGraph);
|
|
152
|
-
// console.log(`${lc} Source Graph Size: ${sourceAddrs.length}`);
|
|
153
|
-
|
|
154
|
-
// // Now get the graph from the destination
|
|
155
|
-
// const destGraph = await getDependencyGraph({
|
|
156
|
-
// ibGibs: [childWithRel],
|
|
157
|
-
// space: destSpace,
|
|
158
|
-
// });
|
|
159
|
-
// const destAddrs = Object.keys(destGraph);
|
|
160
|
-
// console.log(`${lc} Dest Graph Size: ${destAddrs.length}`);
|
|
161
|
-
|
|
162
|
-
// // Assert
|
|
163
|
-
// iReckon(sir, destAddrs.length).asTo('dest graph size').isGonnaBe(sourceAddrs.length);
|
|
164
|
-
|
|
165
|
-
// for (const addr of sourceAddrs) {
|
|
166
|
-
// const inDest = !!destGraph[addr];
|
|
167
|
-
// if (!inDest) {
|
|
168
|
-
// console.error(`${lc} Missing in dest: ${addr}`);
|
|
169
|
-
// }
|
|
170
|
-
// iReckon(sir, inDest).asTo(`addr present in dest: ${addr}`).isGonnaBeTrue();
|
|
171
|
-
// }
|
|
172
|
-
|
|
173
|
-
// console.log(`${lc} Verified Full Dependency Graph synced.`);
|
|
174
|
-
// });
|
|
175
|
-
|
|
176
|
-
// await ifWeMight(sir, `Idempotency (No-op if already synced)`, async () => {
|
|
177
|
-
// // 1. Setup Spaces
|
|
178
|
-
// const metaspace = new Metaspace_Innerspace(undefined);
|
|
179
|
-
// await metaspace.initialize({
|
|
180
|
-
// getFnAlert: () => async ({ title, msg }) => { console.log(`[Alert] ${title}: ${msg}`); },
|
|
181
|
-
// getFnPrompt: () => async ({ title, msg }) => { console.log(`[Prompt] ${title}: ${msg}`); return ''; },
|
|
182
|
-
// getFnPromptPassword: () => async (title, msg) => { console.log(`[PromptPwd] ${title}: ${msg}`); return null; },
|
|
183
|
-
// });
|
|
184
|
-
|
|
185
|
-
// const sourceSpace = new InnerSpace_V1({ name: 'source_idem', uuid: 'source_idem_uuid' } as any);
|
|
186
|
-
// await (sourceSpace as any).initialize();
|
|
187
|
-
|
|
188
|
-
// const destSpace = new InnerSpace_V1({ name: 'dest_idem', uuid: 'dest_idem_uuid' } as any);
|
|
189
|
-
// await (destSpace as any).initialize();
|
|
190
|
-
|
|
191
|
-
// // 2. Seed Source and Sync ONCE
|
|
192
|
-
// const root = await createTimelineRootHelper({
|
|
193
|
-
// ib: 'timeline_root',
|
|
194
|
-
// data: { type: 'root', n: 0 },
|
|
195
|
-
// space: sourceSpace,
|
|
196
|
-
// });
|
|
197
|
-
|
|
198
|
-
// const mockKeystone = await getTestKeystoneServiceHelper();
|
|
199
|
-
// const identity = await (mockKeystone as any).getIdentity();
|
|
200
|
-
// const coordinator = new SyncSagaCoordinator(mockKeystone);
|
|
201
|
-
|
|
202
|
-
// console.log(`${lc} Running First Sync...`);
|
|
203
|
-
// await coordinator.sync({
|
|
204
|
-
// source: sourceSpace,
|
|
205
|
-
// dest: destSpace,
|
|
206
|
-
// domainIbGibs: [root],
|
|
207
|
-
// identity
|
|
208
|
-
// });
|
|
209
|
-
|
|
210
|
-
// // Verify Initial State
|
|
211
|
-
// const rootAddr = getIbGibAddr({ ibGib: root });
|
|
212
|
-
// const getRoot1 = await getFromSpace({ space: destSpace, addr: rootAddr });
|
|
213
|
-
// iReckon(sir, getRoot1.success).asTo('First Sync success').isGonnaBeTrue();
|
|
214
|
-
|
|
215
|
-
// // 3. Run Sync AGAIN (Should be No-op)
|
|
216
|
-
// console.log(`${lc} Running Second Sync...`);
|
|
217
|
-
// await coordinator.sync({
|
|
218
|
-
// source: sourceSpace,
|
|
219
|
-
// dest: destSpace,
|
|
220
|
-
// domainIbGibs: [root],
|
|
221
|
-
// identity
|
|
222
|
-
// });
|
|
223
|
-
|
|
224
|
-
// // 4. Verify State Unchanged / Still Valid
|
|
225
|
-
// const getRoot2 = await getFromSpace({ space: destSpace, addr: rootAddr });
|
|
226
|
-
// iReckon(sir, getRoot2.success).asTo('Second Sync success').isGonnaBeTrue();
|
|
227
|
-
|
|
228
|
-
// // Ensure no duplication or errors in destination (checking count in dependency graph might be good proxy?)
|
|
229
|
-
// const destGraph = await getDependencyGraph({
|
|
230
|
-
// ibGibs: [root],
|
|
231
|
-
// space: destSpace,
|
|
232
|
-
// });
|
|
233
|
-
// iReckon(sir, Object.keys(destGraph).length).asTo('Dest Graph Size unchanged').isGonnaBe(Object.keys(destGraph).length); // Trivial assertion, mostly checking for no throw
|
|
234
|
-
|
|
235
|
-
// console.log(`${lc} Verified Idempotency.`);
|
|
236
|
-
// });
|
|
237
|
-
|
|
238
|
-
await ifWeMight(sir, `Dest Ahead (Pull/Fast-Backward)`, async () => {
|
|
34
|
+
await ifWeMight(sir, `Basic Push Sync (Source -> Dest)`, async () => {
|
|
239
35
|
// 1. Setup Spaces
|
|
240
|
-
|
|
241
|
-
// But here we are instantiating them directly.
|
|
242
|
-
// User pointed out initialize is called in constructor.
|
|
243
|
-
const metaspace = new Metaspace_Innerspace(undefined);
|
|
36
|
+
metaspace = new Metaspace_Innerspace(undefined);
|
|
244
37
|
await metaspace.initialize({
|
|
245
38
|
getFnAlert: () => async ({ title, msg }) => { console.log(`[Alert] ${title}: ${msg}`); },
|
|
246
39
|
getFnPrompt: () => async ({ title, msg }) => { console.log(`[Prompt] ${title}: ${msg}`); return ''; },
|
|
247
40
|
getFnPromptPassword: () => async (title, msg) => { console.log(`[PromptPwd] ${title}: ${msg}`); return null; },
|
|
248
41
|
});
|
|
249
42
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
// await (sourceSpace as any).initialize(); // Already initialized in ctor
|
|
43
|
+
sourceSpace = new InnerSpace_V1({ name: 'source', uuid: 'source_uuid' } as any);
|
|
44
|
+
await (sourceSpace as any).initialize();
|
|
253
45
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
// await (destSpace as any).initialize(); // Already initialized in ctor
|
|
46
|
+
destSpace = new InnerSpace_V1({ name: 'dest', uuid: 'dest_uuid' } as any);
|
|
47
|
+
await (destSpace as any).initialize();
|
|
257
48
|
|
|
258
|
-
// 2.
|
|
259
|
-
|
|
260
|
-
console.log(`${lc} Seeding initial shared state...`);
|
|
49
|
+
// 2. Seed Source Data
|
|
50
|
+
console.log(`${lc} Creating data in Source...`);
|
|
261
51
|
const root = await createTimelineRootHelper({
|
|
262
|
-
ib: '
|
|
263
|
-
data: { type: 'root',
|
|
52
|
+
ib: 'timeline_root',
|
|
53
|
+
data: { type: 'root', n: 0 },
|
|
264
54
|
space: sourceSpace,
|
|
265
55
|
});
|
|
266
|
-
const rootAddr = getIbGibAddr({ ibGib: root });
|
|
267
|
-
if (logalot) { console.log(`${lc} rootAddr: ${rootAddr} (I: 486bcba15f0a3307f8e6aa88089d1825)`); }
|
|
268
|
-
const tjpAddr = getTjpAddr({ ibGib: root, defaultIfNone: 'incomingAddr' }) || rootAddr; // Root is typically its own TJP if not explicit
|
|
269
|
-
if (logalot) { console.log(`${lc} tjpAddr: ${tjpAddr} (I: b9326dfb66b8efe8a829673335869c25)`); }
|
|
270
|
-
if (logalot) { console.log(`${lc} root:\n${pretty(root)} (I: 3051584310780a36c8435d7881d2f825)`); }
|
|
271
|
-
|
|
272
|
-
const mockKeystone = await getTestKeystoneServiceHelper();
|
|
273
|
-
const identity = await (mockKeystone as any).getIdentity();
|
|
274
|
-
const coordinator = new SyncSagaCoordinator(mockKeystone);
|
|
275
56
|
|
|
276
|
-
await
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
let rootGraph_source: FlatIbGibGraph = await getDependencyGraph({ ibGib: root, space: sourceSpace });
|
|
284
|
-
let rootGraph_dest: FlatIbGibGraph = await getDependencyGraph({ ibGib: root, space: destSpace });
|
|
285
|
-
let rootsSynced = naiveGraphsAreEqual(rootGraph_source, rootGraph_dest);
|
|
286
|
-
if (rootsSynced) {
|
|
287
|
-
if (logalot) { console.log(`${lc} rootsSynced (I: 493982dbc9f40170fd26a0d8e0b69825)`); }
|
|
288
|
-
} else {
|
|
289
|
-
throw new Error(`(UNEXPECTED) graphs are not equal after simple root push sync? (E: f10be8a3d285f260989419c8b99bc225)`);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// 3. Evolve DESTINATION (Simulate external update)
|
|
293
|
-
// We act "as if" we are on Dest side evolving it. This is like we are
|
|
294
|
-
// syncing with the remote, and there were changes created by someone
|
|
295
|
-
// else but we have no local changes.
|
|
296
|
-
console.log(`${lc} Evolving Destination (making Source behind)...`);
|
|
297
|
-
const childDest = await mut8Timeline({
|
|
298
|
-
timeline: root, // works because mut8Timeline just needs the ib/data/rel8ns/n, doesn't need to be "in" the space object-wise, but we persist to destSpace
|
|
299
|
-
mut8Opts: { dataToAddOrPatch: { type: 'modified in dest', state: 'ahead' } },
|
|
57
|
+
const child = await mut8Timeline({
|
|
58
|
+
timeline: root,
|
|
59
|
+
mut8Opts: {
|
|
60
|
+
dataToAddOrPatch: { type: 'child', n: 1 }
|
|
61
|
+
},
|
|
300
62
|
metaspace,
|
|
301
|
-
space:
|
|
63
|
+
space: sourceSpace,
|
|
302
64
|
});
|
|
303
|
-
const
|
|
304
|
-
if (logalot) { console.log(`${lc} childDest:\n${pretty(childDest)}(I: ff4868842d8757df98cfe59ec5bea825)`); }
|
|
305
|
-
|
|
306
|
-
// Log debugging info for TJP
|
|
307
|
-
const childDestTjpAddr = getTjpAddr({ ibGib: childDest });
|
|
308
|
-
console.log(`${lc} childDestAddr: ${childDestAddr}`);
|
|
309
|
-
console.log(`${lc} rootAddr (Expected TJP?): ${rootAddr}`);
|
|
310
|
-
console.log(`${lc} childDest TJP Addr: ${childDestTjpAddr}`);
|
|
65
|
+
const childAddr = getIbGibAddr({ ibGib: child });
|
|
311
66
|
|
|
312
|
-
//
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
// Hack fix for test verification if mut8Timeline is slightly broken on TJP propagation for raw tests
|
|
317
|
-
if (!childDest.rel8ns) { childDest.rel8ns = {}; }
|
|
318
|
-
childDest.rel8ns.tjp = [tjpAddr];
|
|
319
|
-
// Re-put to update index?? No, InnerSpace uses in-memory map references often if we modified object.
|
|
320
|
-
// Better to re-put explicitly if we modified it.
|
|
321
|
-
await putInSpace({ space: destSpace, ibGibs: [childDest] });
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
console.log(`${lc} sourceSpace UUID: ${(sourceSpace as any).data.uuid}`);
|
|
325
|
-
console.log(`${lc} destSpace UUID: ${(destSpace as any).data.uuid}`);
|
|
67
|
+
// 3. Setup Coordinators & Peer
|
|
68
|
+
console.log(`${lc} Setting up Coordinators...`);
|
|
69
|
+
const mockKeystone = await getTestKeystoneServiceHelper();
|
|
70
|
+
const identity = await (mockKeystone as any).getIdentity();
|
|
326
71
|
|
|
327
|
-
//
|
|
328
|
-
const
|
|
329
|
-
|
|
72
|
+
// Sender Coordinator
|
|
73
|
+
const senderCoordinator = new SyncSagaCoordinator(mockKeystone);
|
|
74
|
+
|
|
75
|
+
// Receiver Coordinator
|
|
76
|
+
// Note: Receiver acts "globally" usually, but here we treat it as bound to the dest operations?
|
|
77
|
+
// Actually SyncSagaCoordinator isn't bound to a space. It works ON a space.
|
|
78
|
+
// So we can reuse or create separate instances. Let's create separate to simulate remote.
|
|
79
|
+
const receiverCoordinator = new SyncSagaCoordinator(mockKeystone);
|
|
80
|
+
|
|
81
|
+
// Peer (The "Network")
|
|
82
|
+
const peer = new SyncPeerInnerspace_V1({
|
|
83
|
+
senderSpace: sourceSpace, // "Client"
|
|
84
|
+
receiverSpace: destSpace, // "Server"
|
|
85
|
+
receiverCoordinator: receiverCoordinator,
|
|
86
|
+
receiverMetaspace: metaspace,
|
|
87
|
+
});
|
|
330
88
|
|
|
331
|
-
// 4. Run Sync
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
dest: destSpace,
|
|
340
|
-
domainIbGibs: [root], // We start with what Source knows (root)
|
|
89
|
+
// 4. Run Sync
|
|
90
|
+
console.log(`${lc} Running Sync...`);
|
|
91
|
+
const syncRes = await senderCoordinator.sync({
|
|
92
|
+
peer: peer,
|
|
93
|
+
localSpace: sourceSpace,
|
|
94
|
+
targetSpace: undefined, // Not used when peer is provided
|
|
95
|
+
metaspace: metaspace,
|
|
96
|
+
domainIbGibs: [child], // Sync the child
|
|
341
97
|
identity
|
|
342
|
-
});
|
|
98
|
+
} as any); // Type cast if needed until we align optional params
|
|
343
99
|
|
|
344
|
-
// 5. Verify
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
if (getChildInSourcePost.ibGibs) {
|
|
348
|
-
console.log(`${lc} getChildInSourcePost found: ${getChildInSourcePost.ibGibs.length} ibGibs`);
|
|
349
|
-
} else {
|
|
350
|
-
console.log(`${lc} getChildInSourcePost found: 0 ibGibs (undefined)`);
|
|
351
|
-
}
|
|
100
|
+
// 5. Verify Dest
|
|
101
|
+
console.log(`${lc} Verifying Destination...`);
|
|
102
|
+
const getChildInDest = await getFromSpace({ space: destSpace, addr: childAddr });
|
|
352
103
|
|
|
353
|
-
iReckon(sir,
|
|
354
|
-
iReckon(sir,
|
|
104
|
+
iReckon(sir, getChildInDest.success).asTo('Child present in Dest').isGonnaBeTrue();
|
|
105
|
+
iReckon(sir, getChildInDest.ibGibs?.[0]?.data?.n).asTo('Child content matches').isGonnaBe(1);
|
|
355
106
|
|
|
356
|
-
console.log(`${lc} Verified
|
|
107
|
+
console.log(`${lc} Verified Basic Push.`);
|
|
357
108
|
});
|
|
358
109
|
|
|
359
|
-
// await ifWeMight(sir, `Divergent (Conflict Detection)`, async () => {
|
|
360
|
-
// interface TestData extends IbGibData_V1 {
|
|
361
|
-
// type: string;
|
|
362
|
-
// state?: string;
|
|
363
|
-
// branch?: string;
|
|
364
|
-
// }
|
|
365
|
-
|
|
366
|
-
// // 1. Setup
|
|
367
|
-
// const metaspace = new Metaspace_Innerspace(undefined);
|
|
368
|
-
// await metaspace.initialize({
|
|
369
|
-
// getFnAlert: () => async ({ title, msg }) => { console.log(`[Alert] ${title}: ${msg}`); },
|
|
370
|
-
// getFnPrompt: () => async ({ title, msg }) => { console.log(`[Prompt] ${title}: ${msg}`); return ''; },
|
|
371
|
-
// getFnPromptPassword: () => async (title, msg) => { console.log(`[PromptPwd] ${title}: ${msg}`); return null; },
|
|
372
|
-
// });
|
|
373
|
-
|
|
374
|
-
// const sourceSpace = new InnerSpace_V1({ name: 'source_div', uuid: 'source_div_uuid' } as any);
|
|
375
|
-
// // await (sourceSpace as any).initialize();
|
|
376
|
-
// const destSpace = new InnerSpace_V1({ name: 'dest_div', uuid: 'dest_div_uuid' } as any);
|
|
377
|
-
// // await (destSpace as any).initialize();
|
|
378
|
-
|
|
379
|
-
// // 2. Setup Shared Root
|
|
380
|
-
// const root = await createTimelineRootHelper<TestData>({
|
|
381
|
-
// ib: 'timeline_div',
|
|
382
|
-
// data: { type: 'root' },
|
|
383
|
-
// space: sourceSpace,
|
|
384
|
-
// });
|
|
385
|
-
// const rootAddr = getIbGibAddr({ ibGib: root });
|
|
386
|
-
// const tjpAddr = getTjpAddr({ ibGib: root }) || rootAddr;
|
|
387
|
-
|
|
388
|
-
// // Sync root to dest so they share base
|
|
389
|
-
// await putInSpace({ space: destSpace, ibGibs: [root] });
|
|
390
|
-
// // NOTE: Must manually index TJP in dest if putInSpace doesn't trigger full indexing (InnerSpace might need register)
|
|
391
|
-
// // Ideally we run a sync to init, but manual put + hack fix is faster for setup.
|
|
392
|
-
// // Actually, let's use coordinator to sync root first to ensure clean state.
|
|
393
|
-
// const mockKeystone = await getTestKeystoneServiceHelper();
|
|
394
|
-
// const identity = await (mockKeystone as any).getIdentity();
|
|
395
|
-
// const coordinator = new SyncSagaCoordinator(mockKeystone);
|
|
396
|
-
|
|
397
|
-
// await coordinator.sync({
|
|
398
|
-
// source: sourceSpace,
|
|
399
|
-
// dest: destSpace,
|
|
400
|
-
// domainIbGibs: [root],
|
|
401
|
-
// identity
|
|
402
|
-
// });
|
|
403
|
-
|
|
404
|
-
// // 3. Diverge!
|
|
405
|
-
// // Source creates Child A
|
|
406
|
-
// const childA = await mut8Timeline({
|
|
407
|
-
// timeline: root,
|
|
408
|
-
// mut8Opts: { dataToAddOrPatch: { branch: 'A' } },
|
|
409
|
-
// metaspace,
|
|
410
|
-
// space: sourceSpace,
|
|
411
|
-
// });
|
|
412
|
-
// const childAAddr = getIbGibAddr({ ibGib: childA });
|
|
413
|
-
|
|
414
|
-
// // Dest (simulated) creates Child B from Root (Forking history)
|
|
415
|
-
// const childB = await mut8Timeline({
|
|
416
|
-
// timeline: root,
|
|
417
|
-
// mut8Opts: { dataToAddOrPatch: { branch: 'B' } }, // Logic: same n=1, but different content/hash
|
|
418
|
-
// metaspace,
|
|
419
|
-
// space: destSpace,
|
|
420
|
-
// });
|
|
421
|
-
// const childBAddr = getIbGibAddr({ ibGib: childB });
|
|
422
|
-
|
|
423
|
-
// // Ensure TJP indexing on Dest (test hack)
|
|
424
|
-
// const childBTjp = getTjpAddr({ ibGib: childB });
|
|
425
|
-
// if (!childBTjp || childBTjp !== tjpAddr) {
|
|
426
|
-
// if (!childB.rel8ns) childB.rel8ns = {};
|
|
427
|
-
// childB.rel8ns.tjp = [tjpAddr];
|
|
428
|
-
// await putInSpace({ space: destSpace, ibGibs: [childB] });
|
|
429
|
-
// }
|
|
430
|
-
|
|
431
|
-
// console.log(`${lc} Divergence Setup:`);
|
|
432
|
-
// console.log(`${lc} Root: ${rootAddr}`);
|
|
433
|
-
// console.log(`${lc} Source Tip (A): ${childAAddr}`);
|
|
434
|
-
// console.log(`${lc} Dest Tip (B): ${childBAddr}`);
|
|
435
|
-
|
|
436
|
-
// // 4. Sync with Strategy: 'abort' (Expect Error)
|
|
437
|
-
// console.log(`${lc} Running Sync (Abort Strategy)...`);
|
|
438
|
-
// let caughtError;
|
|
439
|
-
// try {
|
|
440
|
-
// await coordinator.sync({
|
|
441
|
-
// source: sourceSpace,
|
|
442
|
-
// dest: destSpace,
|
|
443
|
-
// domainIbGibs: [childA],
|
|
444
|
-
// identity,
|
|
445
|
-
// conflictStrategy: 'abort', // Should throw
|
|
446
|
-
// });
|
|
447
|
-
// } catch (e) {
|
|
448
|
-
// caughtError = e;
|
|
449
|
-
// console.log(`${lc} Caught expected error: ${e.message}`);
|
|
450
|
-
// }
|
|
451
|
-
// iReckon(sir, caughtError).asTo('Should throw on conflict with abort strategy').isGonnaBeTruthy();
|
|
452
|
-
|
|
453
|
-
// // 5. Sync with Strategy: 'optimistic' (Expect Merge)
|
|
454
|
-
// // TODO: Implement optimistic merging
|
|
455
|
-
// });
|
|
456
|
-
|
|
457
110
|
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module SyncPeerInnerspace_V1
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
|
|
6
|
+
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
7
|
+
import { IbGibSpaceAny } from '../../witness/space/space-base-v1.mjs';
|
|
8
|
+
import { SyncSagaContextIbGib_V1 } from '../sync-saga-context/sync-saga-context-types.mjs';
|
|
9
|
+
import { SyncPeer_V1 } from './sync-peer-v1.mjs';
|
|
10
|
+
import { SyncSagaCoordinator } from '../sync-saga-coordinator.mjs';
|
|
11
|
+
import { createSyncSagaContext } from '../sync-saga-context/sync-saga-context-helpers.mjs';
|
|
12
|
+
import { getFromSpace, putInSpace } from '../../witness/space/space-helper.mjs';
|
|
13
|
+
import { getDependencyGraph } from '../../witness/space/space-helper.mjs';
|
|
14
|
+
import { MetaspaceService } from '../../witness/space/metaspace/metaspace-types.mjs';
|
|
15
|
+
|
|
16
|
+
export interface SyncPeerInnerspaceOptions {
|
|
17
|
+
senderSpace: IbGibSpaceAny;
|
|
18
|
+
receiverSpace: IbGibSpaceAny;
|
|
19
|
+
receiverCoordinator: SyncSagaCoordinator;
|
|
20
|
+
receiverMetaspace: MetaspaceService; // Need this for receiver execution
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Concrete implementation of SyncPeer for local in-memory simulation (Innerspace to Innerspace).
|
|
25
|
+
*
|
|
26
|
+
* Acts as the "Network" layer, transferring data between two local spaces and
|
|
27
|
+
* executing the receiver's coordinator logic.
|
|
28
|
+
*/
|
|
29
|
+
export class SyncPeerInnerspace_V1 extends SyncPeer_V1 {
|
|
30
|
+
|
|
31
|
+
protected lc: string = `[${SyncPeerInnerspace_V1.name}]`;
|
|
32
|
+
|
|
33
|
+
constructor(
|
|
34
|
+
public opts: SyncPeerInnerspaceOptions
|
|
35
|
+
) {
|
|
36
|
+
super(opts.receiverSpace.data!); // Use receiver space data as initial phantom data? Or empty.
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Simulates the network transmission and receiver execution.
|
|
41
|
+
*/
|
|
42
|
+
async witness(arg: SyncSagaContextIbGib_V1): Promise<SyncSagaContextIbGib_V1 | undefined> {
|
|
43
|
+
const lc = `${this.lc}[${this.witness.name}]`;
|
|
44
|
+
const { senderSpace, receiverSpace, receiverCoordinator, receiverMetaspace } = this.opts;
|
|
45
|
+
|
|
46
|
+
// 1. "Receive" Request (Transfer from Sender -> Receiver)
|
|
47
|
+
const sagaFrameAddr = arg.rel8ns?.sagaFrame?.[0];
|
|
48
|
+
if (!sagaFrameAddr) { throw new Error(`${lc} Missing sagaFrame in context (E: a1b2c3d4e5)`); }
|
|
49
|
+
|
|
50
|
+
// Get Saga Frame (Sender internal -> Receiver internal)
|
|
51
|
+
await this.transfer({ addr: sagaFrameAddr, from: senderSpace, to: receiverSpace });
|
|
52
|
+
const sagaFrame = (await getFromSpace({ addr: sagaFrameAddr, space: receiverSpace })).ibGibs?.[0] as any; // Cast for now
|
|
53
|
+
|
|
54
|
+
// Get Payload (Sender internal -> Receiver internal)
|
|
55
|
+
const payloadAddrs = arg.rel8ns?.payloadAddrs || [];
|
|
56
|
+
if (payloadAddrs.length > 0) {
|
|
57
|
+
await this.transfer({ addrs: payloadAddrs, from: senderSpace, to: receiverSpace });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 2. Prepare Receiver Execution
|
|
61
|
+
// We need a srcGraph for the Receiver if it needs to send data (e.g. Ack/Push).
|
|
62
|
+
// Since we are stateless here (simulating a request), we rebuild the graph context ???
|
|
63
|
+
// Or we pass a lazy getter? For now, let's load what we can or pass empty if not needed.
|
|
64
|
+
// Actually, for Init/delta handling, we might not need the FULL graph if we use space lookups.
|
|
65
|
+
// But let's look at `handleSagaFrame` requirements.
|
|
66
|
+
|
|
67
|
+
let srcGraph: { [addr: string]: IbGib_V1 } = {};
|
|
68
|
+
// Optimally we wouldn't scan the whole space every request. But for testing:
|
|
69
|
+
// const allGraph = await getDependencyGraph({ space: receiverSpace, ... });
|
|
70
|
+
// TOO SLOW.
|
|
71
|
+
// Let's pass an empty graph initially and assume `handleSagaFrame` relies on Space helpers where possible?
|
|
72
|
+
// `SyncSagaCoordinator.analyzeTimelines` generates the graph.
|
|
73
|
+
// `handleAckFrame` uses `srcGraph` to fulfill requests.
|
|
74
|
+
// If we are mostly testing Init -> Ack (Empty) -> Delta (Receive), we might survive without it.
|
|
75
|
+
// But if Receiver acts as Sender...
|
|
76
|
+
|
|
77
|
+
// 3. Execute Receiver Logic
|
|
78
|
+
const result = await receiverCoordinator.handleSagaFrame({
|
|
79
|
+
sagaIbGib: sagaFrame,
|
|
80
|
+
srcGraph: srcGraph, // Empty for now, may fail if push needed
|
|
81
|
+
space: receiverSpace,
|
|
82
|
+
metaspace: receiverMetaspace,
|
|
83
|
+
// Identity? Receiver needs its own identity?
|
|
84
|
+
// If receiverCoordinator is initialized with an identity, or we pass one here?
|
|
85
|
+
// `sync()` does `getSessionIdentity`. Here we might need to mock/provide one.
|
|
86
|
+
// For now, undefined (anonymous sync) or we add to opts.
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
if (!result) {
|
|
90
|
+
// Null result means saga complete on receiver side? Or no response?
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const responseFrame = result.frame;
|
|
95
|
+
const responsePayload = result.payloadIbGibs || [];
|
|
96
|
+
|
|
97
|
+
// 4. "Send" Response (Transfer from Receiver -> Sender)
|
|
98
|
+
const responseFrameAddr = getIbGibAddr({ ibGib: responseFrame });
|
|
99
|
+
await putInSpace({ space: receiverSpace, ibGib: responseFrame }); // Ensure it's saved in Receiver
|
|
100
|
+
await this.transfer({ addr: responseFrameAddr, from: receiverSpace, to: senderSpace });
|
|
101
|
+
|
|
102
|
+
const responsePayloadAddrs: string[] = [];
|
|
103
|
+
if (responsePayload.length > 0) {
|
|
104
|
+
await putInSpace({ space: receiverSpace, ibGibs: responsePayload });
|
|
105
|
+
for (const p of responsePayload) {
|
|
106
|
+
const addr = getIbGibAddr({ ibGib: p });
|
|
107
|
+
responsePayloadAddrs.push(addr);
|
|
108
|
+
await this.transfer({ addr, from: receiverSpace, to: senderSpace });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 5. Create Response Context
|
|
113
|
+
const responseCtx = await createSyncSagaContext({
|
|
114
|
+
sagaFrame: responseFrame,
|
|
115
|
+
payloadAddrs: responsePayloadAddrs.length > 0 ? responsePayloadAddrs : undefined,
|
|
116
|
+
// TODO: identity/claims
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Ensure Context is in Sender Space (Network delivery)
|
|
120
|
+
await putInSpace({ space: senderSpace, ibGib: responseCtx });
|
|
121
|
+
|
|
122
|
+
return responseCtx;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Helper to copy ibgibs from A to B.
|
|
127
|
+
*/
|
|
128
|
+
protected async transfer({
|
|
129
|
+
addr,
|
|
130
|
+
addrs,
|
|
131
|
+
from,
|
|
132
|
+
to
|
|
133
|
+
}: {
|
|
134
|
+
addr?: string,
|
|
135
|
+
addrs?: string[],
|
|
136
|
+
from: IbGibSpaceAny,
|
|
137
|
+
to: IbGibSpaceAny
|
|
138
|
+
}): Promise<void> {
|
|
139
|
+
const targets = addrs || (addr ? [addr] : []);
|
|
140
|
+
if (targets.length === 0) return;
|
|
141
|
+
|
|
142
|
+
// Naive infinite dependency transfer or just shallow?
|
|
143
|
+
// Sync usually implies sending dependencies.
|
|
144
|
+
// `getDependencyGraph` from `from` space using these roots.
|
|
145
|
+
const graph = await getDependencyGraph({ space: from, ibGibAddrs: targets });
|
|
146
|
+
if (graph) {
|
|
147
|
+
await putInSpace({ space: to, ibGibs: Object.values(graph) });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|