@ibgib/core-gib 0.1.8 → 0.1.10
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/agent-helpers.d.mts +45 -0
- package/dist/agent-helpers.d.mts.map +1 -0
- package/dist/agent-helpers.mjs +36 -0
- package/dist/agent-helpers.mjs.map +1 -0
- package/dist/keystone/keystone-config-builder.respec.d.mts +2 -0
- package/dist/keystone/keystone-config-builder.respec.d.mts.map +1 -0
- package/dist/keystone/keystone-config-builder.respec.mjs +34 -0
- package/dist/keystone/keystone-config-builder.respec.mjs.map +1 -0
- package/dist/keystone/keystone-constants.d.mts +2 -0
- package/dist/keystone/keystone-constants.d.mts.map +1 -1
- package/dist/keystone/keystone-constants.mjs +2 -0
- package/dist/keystone/keystone-constants.mjs.map +1 -1
- package/dist/keystone/keystone-helpers.d.mts +54 -1
- package/dist/keystone/keystone-helpers.d.mts.map +1 -1
- package/dist/keystone/keystone-helpers.mjs +185 -1
- package/dist/keystone/keystone-helpers.mjs.map +1 -1
- package/dist/keystone/keystone-service-v1.d.mts +49 -16
- package/dist/keystone/keystone-service-v1.d.mts.map +1 -1
- package/dist/keystone/keystone-service-v1.mjs +151 -328
- package/dist/keystone/keystone-service-v1.mjs.map +1 -1
- package/dist/keystone/keystone-service-v1.respec.mjs +401 -20
- package/dist/keystone/keystone-service-v1.respec.mjs.map +1 -1
- package/dist/keystone/keystone-types.d.mts +22 -0
- package/dist/keystone/keystone-types.d.mts.map +1 -1
- package/dist/sync/sync-constants.d.mts +17 -0
- package/dist/sync/sync-constants.d.mts.map +1 -0
- package/dist/sync/sync-constants.mjs +16 -0
- package/dist/sync/sync-constants.mjs.map +1 -0
- package/dist/sync/sync-helpers.d.mts +15 -0
- package/dist/sync/sync-helpers.d.mts.map +1 -0
- package/dist/sync/sync-helpers.mjs +46 -0
- package/dist/sync/sync-helpers.mjs.map +1 -0
- package/dist/sync/sync-local-spaces.respec.d.mts +2 -0
- package/dist/sync/sync-local-spaces.respec.d.mts.map +1 -0
- package/dist/sync/sync-local-spaces.respec.mjs +159 -0
- package/dist/sync/sync-local-spaces.respec.mjs.map +1 -0
- package/dist/sync/sync-saga-coordinator.d.mts +118 -0
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -0
- package/dist/sync/sync-saga-coordinator.mjs +399 -0
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -0
- package/dist/sync/sync-saga-coordinator.respec.d.mts +2 -0
- package/dist/sync/sync-saga-coordinator.respec.d.mts.map +1 -0
- package/dist/sync/sync-saga-coordinator.respec.mjs +40 -0
- package/dist/sync/sync-saga-coordinator.respec.mjs.map +1 -0
- package/dist/sync/sync-types.d.mts +103 -0
- package/dist/sync/sync-types.d.mts.map +1 -0
- package/dist/sync/sync-types.mjs +2 -0
- package/dist/sync/sync-types.mjs.map +1 -0
- package/dist/test/mock-space.d.mts +39 -0
- package/dist/test/mock-space.d.mts.map +1 -0
- package/dist/test/mock-space.mjs +79 -0
- package/dist/test/mock-space.mjs.map +1 -0
- package/dist/witness/space/inner-space/inner-space-v1.respec.mjs +163 -201
- package/dist/witness/space/inner-space/inner-space-v1.respec.mjs.map +1 -1
- package/dist/witness/space/space-helper.d.mts.map +1 -1
- package/dist/witness/space/space-helper.mjs +43 -4
- package/dist/witness/space/space-helper.mjs.map +1 -1
- package/dist/witness/space/space-helper.respec.d.mts +2 -0
- package/dist/witness/space/space-helper.respec.d.mts.map +1 -0
- package/dist/witness/space/space-helper.respec.mjs +30 -0
- package/dist/witness/space/space-helper.respec.mjs.map +1 -0
- package/package.json +2 -2
- package/src/agent-helpers.mts +58 -0
- package/src/keystone/keystone-config-builder.respec.mts +49 -0
- package/src/keystone/keystone-constants.mts +2 -0
- package/src/keystone/keystone-helpers.mts +211 -2
- package/src/keystone/keystone-service-v1.mts +183 -367
- package/src/keystone/keystone-service-v1.respec.mts +484 -21
- package/src/keystone/keystone-types.mts +24 -0
- package/src/sync/sync-constants.mts +24 -0
- package/src/sync/sync-helpers.mts +59 -0
- package/src/sync/sync-local-spaces.respec.mts +200 -0
- package/src/sync/sync-saga-coordinator.mts +477 -0
- package/src/sync/sync-saga-coordinator.respec.mts +52 -0
- package/src/sync/sync-types.mts +120 -0
- package/src/test/mock-space.mts +85 -0
- package/src/witness/space/inner-space/inner-space-v1.respec.mts +181 -228
- package/src/witness/space/space-helper.mts +42 -4
- package/src/witness/space/space-helper.respec.mts +42 -0
- package/tmp.md +11 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
|
|
2
|
+
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A simple in-memory map acting as a Space.
|
|
6
|
+
* Pure Storage. No Indexing logic.
|
|
7
|
+
*
|
|
8
|
+
* Copied/Adapted from Keystone Respecs.
|
|
9
|
+
*/
|
|
10
|
+
export class MockIbGibSpace {
|
|
11
|
+
store = new Map<string, IbGib_V1>();
|
|
12
|
+
|
|
13
|
+
constructor(public name: string = "mock_space") { }
|
|
14
|
+
|
|
15
|
+
async put({ ibGib }: { ibGib: IbGib_V1 }): Promise<void> {
|
|
16
|
+
const addr = getIbGibAddr({ ibGib });
|
|
17
|
+
this.store.set(addr, JSON.parse(JSON.stringify(ibGib))); // Deep copy
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async get({ addr }: { addr: string }): Promise<IbGib_V1 | null> {
|
|
21
|
+
const data = this.store.get(addr);
|
|
22
|
+
return data ? JSON.parse(JSON.stringify(data)) : null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async witness(arg: any): Promise<any> {
|
|
26
|
+
const cmd = arg.data?.cmd;
|
|
27
|
+
if (cmd === 'get') {
|
|
28
|
+
const addrs = arg.data.ibGibAddrs || [];
|
|
29
|
+
const ibGibs: IbGib_V1[] = [];
|
|
30
|
+
for (const addr of addrs) {
|
|
31
|
+
const ig = await this.get({ addr });
|
|
32
|
+
if (ig) ibGibs.push(ig);
|
|
33
|
+
}
|
|
34
|
+
return { ibGibs };
|
|
35
|
+
}
|
|
36
|
+
if (cmd === 'put') {
|
|
37
|
+
const ibGibs = arg.ibGibs || [];
|
|
38
|
+
for (const ibGib of ibGibs) {
|
|
39
|
+
await this.put({ ibGib });
|
|
40
|
+
}
|
|
41
|
+
return { ibGibs: [] }; // Return empty result or whatever witness expects
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* A partial mock of Metaspace.
|
|
49
|
+
*/
|
|
50
|
+
export class MockMetaspaceService {
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Map of TJP Gib (Timeline ID) -> Latest IbGib Addr (Head)
|
|
54
|
+
*/
|
|
55
|
+
timelineHeads = new Map<string, string>();
|
|
56
|
+
|
|
57
|
+
constructor(public space: MockIbGibSpace) { }
|
|
58
|
+
|
|
59
|
+
async getLocalUserSpace({ lock }: { lock: boolean }): Promise<MockIbGibSpace> {
|
|
60
|
+
return this.space;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async put(args: any): Promise<void> {
|
|
64
|
+
const target = args.space || this.space;
|
|
65
|
+
return target.put(args);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async registerNewIbGib(args: { ibGib: IbGib_V1, space?: any }): Promise<void> {
|
|
69
|
+
const { ibGib } = args;
|
|
70
|
+
const targetSpace = args.space || this.space;
|
|
71
|
+
|
|
72
|
+
// 1. Ensure it is stored
|
|
73
|
+
await targetSpace.put({ ibGib });
|
|
74
|
+
|
|
75
|
+
// 2. Extract TJP
|
|
76
|
+
const gib = ibGib.gib || '';
|
|
77
|
+
let tjpGib = gib;
|
|
78
|
+
if (gib.includes('.')) {
|
|
79
|
+
const parts = gib.split('.');
|
|
80
|
+
tjpGib = parts.slice(1).join('.');
|
|
81
|
+
}
|
|
82
|
+
const addr = getIbGibAddr({ ibGib });
|
|
83
|
+
this.timelineHeads.set(tjpGib, addr);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -5,10 +5,11 @@ import {
|
|
|
5
5
|
} from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
|
|
6
6
|
const maam = `[${import.meta.url}]`, sir = maam;
|
|
7
7
|
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
import { Factory_V1 } from '@ibgib/ts-gib/dist/V1/factory.mjs';
|
|
9
|
+
import { mut8 } from '@ibgib/ts-gib/dist/V1/transforms/mut8.mjs';
|
|
10
|
+
import { rel8 } from '@ibgib/ts-gib/dist/V1/transforms/rel8.mjs';
|
|
11
|
+
import { fork } from '@ibgib/ts-gib/dist/V1/transforms/fork.mjs';
|
|
10
12
|
import { getTjpAddr } from '../../../common/other/ibgib-helper.mjs';
|
|
11
|
-
// import { SyncSpaceOptionsIbGib } from '../outer-space/outer-space-types.mjs';
|
|
12
13
|
|
|
13
14
|
import {
|
|
14
15
|
testSpace_createAndInit,
|
|
@@ -17,235 +18,187 @@ import {
|
|
|
17
18
|
testSpace_registerNewIbGib_GetLatest,
|
|
18
19
|
} from '../space-respec-helper.mjs';
|
|
19
20
|
import { InnerSpace_V1, } from './inner-space-v1.mjs';
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import { Metaspace_Innerspace } from '../metaspace/metaspace-innerspace/metaspace-innerspace.mjs';
|
|
23
|
-
// import { getLatestAddrs, putInSpace } from '../space-helper.mjs';
|
|
21
|
+
import { DEFAULT_INNER_SPACE_DATA_V1 } from './inner-space-types.mjs';
|
|
22
|
+
import { putInSpace, getFromSpace } from '../space-helper.mjs';
|
|
24
23
|
|
|
25
24
|
const logalot = true;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
25
|
+
|
|
26
|
+
await respecfully(sir, `[${InnerSpace_V1.name} specs]`, async () => {
|
|
27
|
+
|
|
28
|
+
await testSpace_createAndInit({
|
|
29
|
+
respecfulTitle: sir,
|
|
30
|
+
// shortcircuitRespec: true,
|
|
31
|
+
fnGetInitialSpaceData: async () => {
|
|
32
|
+
return {
|
|
33
|
+
...DEFAULT_INNER_SPACE_DATA_V1,
|
|
34
|
+
name: (await getUUID()).substring(0, 8),
|
|
35
|
+
uuid: await getUUID(),
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
fnGetInitialSpaceRel8ns: () => {
|
|
39
|
+
return Promise.resolve(undefined);
|
|
40
|
+
},
|
|
41
|
+
fnGetSpace: (initialData: any, initialRel8ns) => {
|
|
42
|
+
return Promise.resolve(new InnerSpace_V1(initialData, initialRel8ns));
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
await testSpace_putGetDelete({
|
|
47
|
+
respecfulTitle: sir,
|
|
48
|
+
// shortcircuitRespec: true,
|
|
49
|
+
fnGetInitialSpaceData: async () => {
|
|
50
|
+
return {
|
|
51
|
+
...DEFAULT_INNER_SPACE_DATA_V1,
|
|
52
|
+
name: (await getUUID()).substring(0, 8),
|
|
53
|
+
uuid: await getUUID(),
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
fnGetInitialSpaceRel8ns: () => {
|
|
57
|
+
return Promise.resolve(undefined);
|
|
58
|
+
},
|
|
59
|
+
fnGetSpace: (initialData: any, initialRel8ns) => {
|
|
60
|
+
return Promise.resolve(new InnerSpace_V1(initialData, initialRel8ns));
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
await testSpace_persistTransformResult({
|
|
65
|
+
respecfulTitle: sir,
|
|
66
|
+
// shortcircuitRespec: true,
|
|
67
|
+
fnGetInitialSpaceData: async () => {
|
|
68
|
+
return {
|
|
69
|
+
...DEFAULT_INNER_SPACE_DATA_V1,
|
|
70
|
+
name: (await getUUID()).substring(0, 8),
|
|
71
|
+
uuid: await getUUID(),
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
fnGetInitialSpaceRel8ns: () => {
|
|
75
|
+
return Promise.resolve(undefined);
|
|
76
|
+
},
|
|
77
|
+
fnGetSpace: (initialData: any, initialRel8ns) => {
|
|
78
|
+
return Promise.resolve(new InnerSpace_V1(initialData, initialRel8ns));
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await testSpace_registerNewIbGib_GetLatest({
|
|
83
|
+
respecfulTitle: sir,
|
|
84
|
+
// shortcircuitRespec: true,
|
|
85
|
+
fnGetInitialSpaceData: async () => {
|
|
86
|
+
return {
|
|
87
|
+
...DEFAULT_INNER_SPACE_DATA_V1,
|
|
88
|
+
name: (await getUUID()).substring(0, 8),
|
|
89
|
+
uuid: await getUUID(),
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
fnGetInitialSpaceRel8ns: () => {
|
|
93
|
+
return Promise.resolve(undefined);
|
|
94
|
+
},
|
|
95
|
+
fnGetSpace: (initialData: any, initialRel8ns) => {
|
|
96
|
+
return Promise.resolve(new InnerSpace_V1(initialData, initialRel8ns));
|
|
97
|
+
},
|
|
98
|
+
testLatestIterations: 5,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
await respecfully(sir, 'Advanced Operations (mut8, rel8, fork)', async () => {
|
|
102
|
+
|
|
103
|
+
await ifWe(sir, 'should handle mut8 transform and persistence', async () => {
|
|
104
|
+
const space = new InnerSpace_V1(DEFAULT_INNER_SPACE_DATA_V1, undefined);
|
|
105
|
+
const gib = Factory_V1.primitive({ ib: 'gib' });
|
|
106
|
+
|
|
107
|
+
// 1. Create Origin
|
|
108
|
+
const resOrigin = await Factory_V1.firstGen({
|
|
109
|
+
ib: 'origin',
|
|
110
|
+
data: { initial: 'data' },
|
|
111
|
+
dna: true,
|
|
112
|
+
parentIbGib: gib,
|
|
113
|
+
});
|
|
114
|
+
const origin = resOrigin.newIbGib;
|
|
115
|
+
|
|
116
|
+
// 2. Mut8 (Mutate)
|
|
117
|
+
const resMut8 = await mut8({
|
|
118
|
+
src: origin,
|
|
119
|
+
dataToAddOrPatch: { mutated: 'data' },
|
|
120
|
+
dna: true,
|
|
121
|
+
});
|
|
122
|
+
const mutated = resMut8.newIbGib;
|
|
123
|
+
|
|
124
|
+
// 3. Put in space (should handle both if persisted correctly)
|
|
125
|
+
const resPut = await putInSpace({
|
|
126
|
+
space,
|
|
127
|
+
ibGibs: [origin, mutated],
|
|
128
|
+
});
|
|
129
|
+
iReckon(sir, resPut.success).isGonnaBeTrue();
|
|
130
|
+
|
|
131
|
+
// 4. Verify get
|
|
132
|
+
const resGet = await getFromSpace({
|
|
133
|
+
space,
|
|
134
|
+
addrs: [getIbGibAddr({ ibGib: mutated })],
|
|
135
|
+
});
|
|
136
|
+
iReckon(sir, resGet.success).isGonnaBeTrue();
|
|
137
|
+
// Using non-null assertion as we verified success
|
|
138
|
+
iReckon(sir, resGet.ibGibs![0].data!.mutated).isGonnaBe('data');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
await ifWe(sir, 'should handle rel8 transform and persistence', async () => {
|
|
142
|
+
const space = new InnerSpace_V1(DEFAULT_INNER_SPACE_DATA_V1, undefined);
|
|
143
|
+
const gib = Factory_V1.primitive({ ib: 'gib' });
|
|
144
|
+
|
|
145
|
+
// 1. Create A and B
|
|
146
|
+
const resA = await Factory_V1.firstGen({ ib: 'A', dna: true, parentIbGib: gib });
|
|
147
|
+
const A = resA.newIbGib;
|
|
148
|
+
const resB = await Factory_V1.firstGen({ ib: 'B', dna: true, parentIbGib: gib });
|
|
149
|
+
const B = resB.newIbGib;
|
|
150
|
+
const bAddr = getIbGibAddr({ ibGib: B });
|
|
151
|
+
|
|
152
|
+
// 2. Rel8 A -> B (A links to B)
|
|
153
|
+
const resRel8 = await rel8({
|
|
154
|
+
src: A,
|
|
155
|
+
rel8nsToAddByAddr: { 'linked_to': [bAddr] },
|
|
156
|
+
dna: true,
|
|
157
|
+
});
|
|
158
|
+
const A_linked = resRel8.newIbGib;
|
|
159
|
+
|
|
160
|
+
// 3. Put in space
|
|
161
|
+
await putInSpace({
|
|
162
|
+
space,
|
|
163
|
+
ibGibs: [A, B, A_linked],
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// 4. Verify relation
|
|
167
|
+
const resGet = await getFromSpace({
|
|
168
|
+
space,
|
|
169
|
+
addr: getIbGibAddr({ ibGib: A_linked }),
|
|
170
|
+
});
|
|
171
|
+
const retrieved = resGet.ibGibs![0];
|
|
172
|
+
iReckon(sir, retrieved.rel8ns!.linked_to).includes(bAddr);
|
|
62
173
|
});
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
174
|
+
|
|
175
|
+
await ifWe(sir, 'should handle fork persistence', async () => {
|
|
176
|
+
const space = new InnerSpace_V1(DEFAULT_INNER_SPACE_DATA_V1, undefined);
|
|
177
|
+
const gib = Factory_V1.primitive({ ib: 'gib' });
|
|
178
|
+
|
|
179
|
+
// 1. Create Root
|
|
180
|
+
const resRoot = await Factory_V1.firstGen({ ib: 'root', dna: true, parentIbGib: gib });
|
|
181
|
+
const root = resRoot.newIbGib;
|
|
182
|
+
|
|
183
|
+
// 2. Fork
|
|
184
|
+
const resFork = await fork({
|
|
185
|
+
src: root,
|
|
186
|
+
dna: true,
|
|
187
|
+
tjp: { timestamp: true },
|
|
188
|
+
});
|
|
189
|
+
const forkIbGib = resFork.newIbGib;
|
|
190
|
+
|
|
191
|
+
// 3. Put
|
|
192
|
+
await putInSpace({ space, ibGibs: [root, forkIbGib] });
|
|
193
|
+
|
|
194
|
+
// 4. Verify
|
|
195
|
+
const resGet = await getFromSpace({
|
|
196
|
+
space,
|
|
197
|
+
addr: getIbGibAddr({ ibGib: forkIbGib })
|
|
198
|
+
});
|
|
199
|
+
iReckon(sir, resGet.success).isGonnaBeTrue();
|
|
78
200
|
});
|
|
79
201
|
|
|
80
|
-
iReckon(sir, metaspace.zeroSpace).asTo('metaspace should have a zero space').isGonnaBeTruthy();
|
|
81
202
|
});
|
|
82
|
-
});
|
|
83
203
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
// await testSpace_createAndInit({
|
|
87
|
-
// respecfulTitle: sir,
|
|
88
|
-
// // shortcircuitRespec: true,
|
|
89
|
-
// fnGetInitialSpaceData: async () => {
|
|
90
|
-
// return {
|
|
91
|
-
// name: (await getUUID()).substring(0, 8),
|
|
92
|
-
// uuid: await getUUID(),
|
|
93
|
-
// };
|
|
94
|
-
// },
|
|
95
|
-
// fnGetInitialSpaceRel8ns: () => {
|
|
96
|
-
// return Promise.resolve(undefined);
|
|
97
|
-
// },
|
|
98
|
-
// fnGetSpace: (initialData: any, initialRel8ns) => {
|
|
99
|
-
// return Promise.resolve(new InnerSpace_V1(initialData, initialRel8ns));
|
|
100
|
-
// },
|
|
101
|
-
// })
|
|
102
|
-
|
|
103
|
-
// await testSpace_putGetDelete({
|
|
104
|
-
// respecfulTitle: sir,
|
|
105
|
-
// // shortcircuitRespec: true,
|
|
106
|
-
// fnGetInitialSpaceData: async () => {
|
|
107
|
-
// return {
|
|
108
|
-
// name: (await getUUID()).substring(0, 8),
|
|
109
|
-
// uuid: await getUUID(),
|
|
110
|
-
// };
|
|
111
|
-
// },
|
|
112
|
-
// fnGetInitialSpaceRel8ns: () => {
|
|
113
|
-
// return Promise.resolve(undefined);
|
|
114
|
-
// },
|
|
115
|
-
// fnGetSpace: (initialData: any, initialRel8ns) => {
|
|
116
|
-
// return Promise.resolve(new InnerSpace_V1(initialData, initialRel8ns));
|
|
117
|
-
// },
|
|
118
|
-
// });
|
|
119
|
-
|
|
120
|
-
// await testSpace_persistTransformResult({
|
|
121
|
-
// respecfulTitle: sir,
|
|
122
|
-
// // shortcircuitRespec: true,
|
|
123
|
-
// fnGetInitialSpaceData: async () => {
|
|
124
|
-
// return {
|
|
125
|
-
// name: (await getUUID()).substring(0, 8),
|
|
126
|
-
// uuid: await getUUID(),
|
|
127
|
-
// };
|
|
128
|
-
// },
|
|
129
|
-
// fnGetInitialSpaceRel8ns: () => {
|
|
130
|
-
// return Promise.resolve(undefined);
|
|
131
|
-
// },
|
|
132
|
-
// fnGetSpace: (initialData: any, initialRel8ns) => {
|
|
133
|
-
// return Promise.resolve(new InnerSpace_V1(initialData, initialRel8ns));
|
|
134
|
-
// },
|
|
135
|
-
// });
|
|
136
|
-
|
|
137
|
-
// await testSpace_registerNewIbGib_GetLatest({
|
|
138
|
-
// respecfulTitle: sir,
|
|
139
|
-
// // shortcircuitRespec: true,
|
|
140
|
-
// fnGetInitialSpaceData: async () => {
|
|
141
|
-
// return {
|
|
142
|
-
// name: (await getUUID()).substring(0, 8),
|
|
143
|
-
// uuid: await getUUID(),
|
|
144
|
-
// };
|
|
145
|
-
// },
|
|
146
|
-
// fnGetInitialSpaceRel8ns: () => {
|
|
147
|
-
// return Promise.resolve(undefined);
|
|
148
|
-
// },
|
|
149
|
-
// fnGetSpace: (initialData: any, initialRel8ns) => {
|
|
150
|
-
// return Promise.resolve(new InnerSpace_V1(initialData, initialRel8ns));
|
|
151
|
-
// },
|
|
152
|
-
// testLatestIterations: 5,
|
|
153
|
-
// });
|
|
154
|
-
|
|
155
|
-
// });
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
// await respecfully(sir, 'reconciliation', async () => {
|
|
160
|
-
// const createSpace = async (name: string): Promise<InnerSpace_V1> => {
|
|
161
|
-
// const spaceData: InnerSpaceData_V1 = {
|
|
162
|
-
// name,
|
|
163
|
-
// classname: InnerSpace_V1.name,
|
|
164
|
-
// uuid: await getUUID(),
|
|
165
|
-
// };
|
|
166
|
-
// const space = new InnerSpace_V1(spaceData);
|
|
167
|
-
// await space.initialized;
|
|
168
|
-
// return space;
|
|
169
|
-
// };
|
|
170
|
-
|
|
171
|
-
// const testSpaceName = 'TestSpaceName';
|
|
172
|
-
|
|
173
|
-
// // common setup for reconciliation tests
|
|
174
|
-
// const metaspace: MetaspaceService = await getNewInitializedInMemoryMetaspaceForTesting({
|
|
175
|
-
// defaultSpaceName: testSpaceName
|
|
176
|
-
// });
|
|
177
|
-
// // const metaspace = new Metaspace_V1();
|
|
178
|
-
// // await metaspace.initialize();
|
|
179
|
-
|
|
180
|
-
// await ifWe(sir, 'should sync a new timeline to an empty destination space', async () => {
|
|
181
|
-
// // ARRANGE
|
|
182
|
-
// const local_space = await createSpace('local');
|
|
183
|
-
// const outer_space = await createSpace('outer');
|
|
184
|
-
|
|
185
|
-
// // Create a timeline in local_space using the new timeline-api
|
|
186
|
-
// const { newIbGib: timelineV1 } = await createTimeline({
|
|
187
|
-
// parentIb: 'test',
|
|
188
|
-
// ib: 'my test timeline',
|
|
189
|
-
// data: { foo: 'bar' },
|
|
190
|
-
// metaspace,
|
|
191
|
-
// space: local_space,
|
|
192
|
-
// });
|
|
193
|
-
|
|
194
|
-
// const timelineV2 = await mut8Timeline({
|
|
195
|
-
// timeline: timelineV1,
|
|
196
|
-
// mut8Opts: { dataToAddOrPatch: { foo: 'rab' } },
|
|
197
|
-
// metaspace,
|
|
198
|
-
// space: local_space,
|
|
199
|
-
// });
|
|
200
|
-
|
|
201
|
-
// // Get history for sync
|
|
202
|
-
// const historyInfo = await getHistory({
|
|
203
|
-
// timeline: timelineV2,
|
|
204
|
-
// includeDna: true,
|
|
205
|
-
// metaspace,
|
|
206
|
-
// space: local_space,
|
|
207
|
-
// });
|
|
208
|
-
|
|
209
|
-
// const timelineIbGibs = [
|
|
210
|
-
// ...(Object.values(historyInfo.dnaMap ?? {})),
|
|
211
|
-
// ...historyInfo.orderedPastIbGibs,
|
|
212
|
-
// historyInfo.headIbGib,
|
|
213
|
-
// ];
|
|
214
|
-
// const timelineAddrs = timelineIbGibs.map(x => getIbGibAddr({ ibGib: x }));
|
|
215
|
-
// const tjpAddr = getTjpAddr({ ibGib: timelineV1 });
|
|
216
|
-
|
|
217
|
-
// // ensure outer_space is empty // there is no such functionality
|
|
218
|
-
// // const resGetAddrsOuterBefore = await outer_space.getAddrs({});
|
|
219
|
-
// // iReckon(sir, 'outer space should be empty initially', resGetAddrsOuterBefore.data.addrs.length, 0);
|
|
220
|
-
|
|
221
|
-
// // ACT
|
|
222
|
-
// // const syncOpts: SyncSpaceOptionsIbGib = {
|
|
223
|
-
// // ib: 'sync opts',
|
|
224
|
-
// // gib: 'sync_opts_gib',
|
|
225
|
-
// // data: {
|
|
226
|
-
// // cmdModifiers: ['sync'],
|
|
227
|
-
// // ibGibAddrs: timelineAddrs,
|
|
228
|
-
// // },
|
|
229
|
-
// // ibGibs: timelineIbGibs,
|
|
230
|
-
// // } as any;
|
|
231
|
-
|
|
232
|
-
// // // metaspace.syncIbGibs
|
|
233
|
-
// // putInSpace({})
|
|
234
|
-
// // const resSync = await outer_space.put(syncOpts);
|
|
235
|
-
|
|
236
|
-
// // ASSERT
|
|
237
|
-
// // iReckon(sir, `sync result success should be true. res: ${pretty(resSync)}`, resSync.data.success, true);
|
|
238
|
-
// // iReckon(sir, resSync?.data?.success).asTo(`sync result success`).isGonnaBeTrue();
|
|
239
|
-
|
|
240
|
-
// // const resGetAddrsOuterAfter = await outer_space.getAddrs({});
|
|
241
|
-
// // iReckon(sir, 'outer space should have all timeline ibgibs after sync', resGetAddrsOuterAfter.data.addrs.length, timelineAddrs.length);
|
|
242
|
-
|
|
243
|
-
// // const resGetLatest = await outer_space.getLatestAddrs({ data: { ibGibAddrs: [tjpAddr] } } as any);
|
|
244
|
-
// // const resGetLatest = getLatestAddrs({
|
|
245
|
-
// // tjpAddrs: tjpAddrs,
|
|
246
|
-
// // space: outer_space,
|
|
247
|
-
// // });
|
|
248
|
-
// // iReckon(sir, 'latest addr in outer space should match latest from local', resGetLatest.data.latestAddrsMap[tjpAddr], getIbGibAddr({ ibGib: timelineV2 }));
|
|
249
|
-
// });
|
|
250
|
-
|
|
251
|
-
// });
|
|
204
|
+
});
|
|
@@ -2693,11 +2693,49 @@ export async function lockSpace({
|
|
|
2693
2693
|
expirationUTC: getExpirationUTCString({ seconds: secondsValid }),
|
|
2694
2694
|
}
|
|
2695
2695
|
} as IbGibSpaceLockIbGib;
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2696
|
+
|
|
2697
|
+
// We must use space.witness (via argy) directly here instead of
|
|
2698
|
+
// `putInSpace` helper, because we need to check
|
|
2699
|
+
// `addrsAlreadyHave`. If the space already has the addr, then
|
|
2700
|
+
// we have lost a race condition and the space is ALREADY locked.
|
|
2701
|
+
const argPut = await space.argy({
|
|
2702
|
+
ibMetadata: getSpaceArgMetadata({ space }),
|
|
2703
|
+
argData: {
|
|
2704
|
+
cmd: 'put',
|
|
2705
|
+
force: true,
|
|
2706
|
+
ibGibAddrs: [spaceLockAddr],
|
|
2707
|
+
},
|
|
2708
|
+
ibGibs: [resLockIbGib],
|
|
2709
|
+
});
|
|
2710
|
+
const resPut = await space.witness(argPut);
|
|
2711
|
+
|
|
2712
|
+
if (resPut.data?.success) {
|
|
2713
|
+
if ((resPut.data.addrsAlreadyHave ?? []).includes(spaceLockAddr)) {
|
|
2714
|
+
// Race condition lost! The lock was put by someone else in the interim.
|
|
2715
|
+
if (logalot) { console.log(`${lc} racelost! addrsAlreadyHave includes spaceLockAddr (${spaceLockAddr}) (I: 2e8a1562947c40698110b64251141753)`); }
|
|
2716
|
+
|
|
2717
|
+
// get the existing lock that beat us
|
|
2718
|
+
const getLockRace = await getFromSpace({
|
|
2719
|
+
addr: spaceLockAddr, space, force: true
|
|
2720
|
+
});
|
|
2721
|
+
if (getLockRace.success && getLockRace.ibGibs?.length === 1) {
|
|
2722
|
+
const existingLockRace = getLockRace.ibGibs[0] as IbGibSpaceLockIbGib;
|
|
2723
|
+
resLockIbGib = clone(existingLockRace) as IbGibSpaceLockIbGib;
|
|
2724
|
+
resLockIbGib.data!.alreadyLocked = true;
|
|
2725
|
+
resLockIbGib.data!.success = false;
|
|
2726
|
+
} else {
|
|
2727
|
+
// Should be unreachable if addrsAlreadyHave is true, but handle gracefully
|
|
2728
|
+
const emsg = `${lc} (UNEXPECTED) addrsAlreadyHave true, but couldn't get lock? (E: 8374a2b6628b49369363bc5843105763)`;
|
|
2729
|
+
console.error(emsg);
|
|
2730
|
+
resLockIbGib.data!.success = false;
|
|
2731
|
+
resLockIbGib.data!.errorMsg = emsg;
|
|
2732
|
+
}
|
|
2733
|
+
} else {
|
|
2734
|
+
// Success! We put the lock and it was new.
|
|
2735
|
+
resLockIbGib.data!.success = true;
|
|
2736
|
+
}
|
|
2699
2737
|
} else {
|
|
2700
|
-
const emsg = `${lc} there was an error putting the lock in the space: ${resPut.
|
|
2738
|
+
const emsg = `${lc} there was an error putting the lock in the space: ${resPut.data?.errors?.join('|')}`;
|
|
2701
2739
|
resLockIbGib.data!.success = false;
|
|
2702
2740
|
resLockIbGib.data!.errorMsg = emsg;
|
|
2703
2741
|
console.error(emsg);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { respecfully, ifWe, iReckon } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
|
|
2
|
+
import { InnerSpace_V1 } from './inner-space/inner-space-v1.mjs';
|
|
3
|
+
import { DEFAULT_INNER_SPACE_DATA_V1 } from './inner-space/inner-space-types.mjs';
|
|
4
|
+
import { lockSpace, unlockSpace } from './space-helper.mjs';
|
|
5
|
+
|
|
6
|
+
const maam = `[${import.meta.url}]`, sir = maam;
|
|
7
|
+
|
|
8
|
+
await respecfully(sir, 'space-helper', async () => {
|
|
9
|
+
|
|
10
|
+
await respecfully(sir, 'lockSpace', async () => {
|
|
11
|
+
|
|
12
|
+
await ifWe(sir, 'should fail if two locks are acquired simultaneously on the same scope (race condition check)', async () => {
|
|
13
|
+
const space = new InnerSpace_V1(DEFAULT_INNER_SPACE_DATA_V1, undefined);
|
|
14
|
+
|
|
15
|
+
// initialize the space first (often required for internal structures)
|
|
16
|
+
await space.initialized;
|
|
17
|
+
|
|
18
|
+
const scope = 'test-lock-scope';
|
|
19
|
+
const secondsValid = 5;
|
|
20
|
+
|
|
21
|
+
// Try to acquire lock simultaneously from two "instances"
|
|
22
|
+
const p1 = lockSpace({ space, scope, secondsValid, instanceId: 'A' });
|
|
23
|
+
const p2 = lockSpace({ space, scope, secondsValid, instanceId: 'B' });
|
|
24
|
+
|
|
25
|
+
const [res1, res2] = await Promise.all([p1, p2]);
|
|
26
|
+
|
|
27
|
+
// Expectation: Only one should succeed
|
|
28
|
+
const success1 = res1.data?.success;
|
|
29
|
+
const success2 = res2.data?.success;
|
|
30
|
+
|
|
31
|
+
// If both verify, we have a problem (race condition)
|
|
32
|
+
// We reckon that NOT both are true.
|
|
33
|
+
iReckon(sir, success1 && success2).asTo('both locks acquired (mutual exclusion failed)').isGonnaBeFalse();
|
|
34
|
+
|
|
35
|
+
// Clean up
|
|
36
|
+
await unlockSpace({ space, scope, instanceId: 'A' });
|
|
37
|
+
await unlockSpace({ space, scope, instanceId: 'B' });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
});
|
package/tmp.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
OK, ty. The space-wide sync approach is not tenable. That is like trying to sync Alice's entire logical drive with Bob's entire logical drive instead of a particular folder on the drive. Your thinking that a single Domain IbGib does not contain many timelines is faulty. A dependency graph of a "single" domain may contain multiple, or even many/a great many, timelines. Think of this like in set theory and we are creating an isomorphic projection of nodes. I say isomorphic and not homomorphic, because we may have to do merges for timelines, so it really is a "one-way" projection. Though obviously, with "sync" being bi-directional, we are ultimately converging on both endpoints having "equivalent" timelines (there may be orphans on one and/or the other after merges, with either endpoint effectively doing a "rebase"). So our sync process has to start with getting a "live" dependency graph of a single domain ibgib. We can actually do multiple domain ibgibs, and then unify the entire dependency graph in the sync transmission ibgib, but I think it's best to just build for the single ibgib scenario. You can look at@beautifulMention, but I would really recommend looking at the entire @beautifulMentionfile. This is a non-trivial recursive function, but you should be able to fully grok it by looking at both the jsdocs/comments in that file and the code itself. Always look at the code itself of course!
|
|
2
|
+
|
|
3
|
+
That dependency graph comes in the form a @beautifulMention as defined by this:
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
/**
|
|
7
|
+
* Map of addr -> ibGib
|
|
8
|
+
*/
|
|
9
|
+
export type FlatIbGibGraph = { [addr: string]: IbGib_V1 };
|
|
10
|
+
```
|
|
11
|
+
|