@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.
Files changed (80) hide show
  1. package/dist/agent-helpers.d.mts +45 -0
  2. package/dist/agent-helpers.d.mts.map +1 -0
  3. package/dist/agent-helpers.mjs +36 -0
  4. package/dist/agent-helpers.mjs.map +1 -0
  5. package/dist/keystone/keystone-config-builder.respec.d.mts +2 -0
  6. package/dist/keystone/keystone-config-builder.respec.d.mts.map +1 -0
  7. package/dist/keystone/keystone-config-builder.respec.mjs +34 -0
  8. package/dist/keystone/keystone-config-builder.respec.mjs.map +1 -0
  9. package/dist/keystone/keystone-constants.d.mts +2 -0
  10. package/dist/keystone/keystone-constants.d.mts.map +1 -1
  11. package/dist/keystone/keystone-constants.mjs +2 -0
  12. package/dist/keystone/keystone-constants.mjs.map +1 -1
  13. package/dist/keystone/keystone-helpers.d.mts +54 -1
  14. package/dist/keystone/keystone-helpers.d.mts.map +1 -1
  15. package/dist/keystone/keystone-helpers.mjs +185 -1
  16. package/dist/keystone/keystone-helpers.mjs.map +1 -1
  17. package/dist/keystone/keystone-service-v1.d.mts +49 -16
  18. package/dist/keystone/keystone-service-v1.d.mts.map +1 -1
  19. package/dist/keystone/keystone-service-v1.mjs +151 -328
  20. package/dist/keystone/keystone-service-v1.mjs.map +1 -1
  21. package/dist/keystone/keystone-service-v1.respec.mjs +401 -20
  22. package/dist/keystone/keystone-service-v1.respec.mjs.map +1 -1
  23. package/dist/keystone/keystone-types.d.mts +22 -0
  24. package/dist/keystone/keystone-types.d.mts.map +1 -1
  25. package/dist/sync/sync-constants.d.mts +17 -0
  26. package/dist/sync/sync-constants.d.mts.map +1 -0
  27. package/dist/sync/sync-constants.mjs +16 -0
  28. package/dist/sync/sync-constants.mjs.map +1 -0
  29. package/dist/sync/sync-helpers.d.mts +15 -0
  30. package/dist/sync/sync-helpers.d.mts.map +1 -0
  31. package/dist/sync/sync-helpers.mjs +46 -0
  32. package/dist/sync/sync-helpers.mjs.map +1 -0
  33. package/dist/sync/sync-local-spaces.respec.d.mts +2 -0
  34. package/dist/sync/sync-local-spaces.respec.d.mts.map +1 -0
  35. package/dist/sync/sync-local-spaces.respec.mjs +159 -0
  36. package/dist/sync/sync-local-spaces.respec.mjs.map +1 -0
  37. package/dist/sync/sync-saga-coordinator.d.mts +118 -0
  38. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -0
  39. package/dist/sync/sync-saga-coordinator.mjs +399 -0
  40. package/dist/sync/sync-saga-coordinator.mjs.map +1 -0
  41. package/dist/sync/sync-saga-coordinator.respec.d.mts +2 -0
  42. package/dist/sync/sync-saga-coordinator.respec.d.mts.map +1 -0
  43. package/dist/sync/sync-saga-coordinator.respec.mjs +40 -0
  44. package/dist/sync/sync-saga-coordinator.respec.mjs.map +1 -0
  45. package/dist/sync/sync-types.d.mts +103 -0
  46. package/dist/sync/sync-types.d.mts.map +1 -0
  47. package/dist/sync/sync-types.mjs +2 -0
  48. package/dist/sync/sync-types.mjs.map +1 -0
  49. package/dist/test/mock-space.d.mts +39 -0
  50. package/dist/test/mock-space.d.mts.map +1 -0
  51. package/dist/test/mock-space.mjs +79 -0
  52. package/dist/test/mock-space.mjs.map +1 -0
  53. package/dist/witness/space/inner-space/inner-space-v1.respec.mjs +163 -201
  54. package/dist/witness/space/inner-space/inner-space-v1.respec.mjs.map +1 -1
  55. package/dist/witness/space/space-helper.d.mts.map +1 -1
  56. package/dist/witness/space/space-helper.mjs +43 -4
  57. package/dist/witness/space/space-helper.mjs.map +1 -1
  58. package/dist/witness/space/space-helper.respec.d.mts +2 -0
  59. package/dist/witness/space/space-helper.respec.d.mts.map +1 -0
  60. package/dist/witness/space/space-helper.respec.mjs +30 -0
  61. package/dist/witness/space/space-helper.respec.mjs.map +1 -0
  62. package/package.json +2 -2
  63. package/src/agent-helpers.mts +58 -0
  64. package/src/keystone/keystone-config-builder.respec.mts +49 -0
  65. package/src/keystone/keystone-constants.mts +2 -0
  66. package/src/keystone/keystone-helpers.mts +211 -2
  67. package/src/keystone/keystone-service-v1.mts +183 -367
  68. package/src/keystone/keystone-service-v1.respec.mts +484 -21
  69. package/src/keystone/keystone-types.mts +24 -0
  70. package/src/sync/sync-constants.mts +24 -0
  71. package/src/sync/sync-helpers.mts +59 -0
  72. package/src/sync/sync-local-spaces.respec.mts +200 -0
  73. package/src/sync/sync-saga-coordinator.mts +477 -0
  74. package/src/sync/sync-saga-coordinator.respec.mts +52 -0
  75. package/src/sync/sync-types.mts +120 -0
  76. package/src/test/mock-space.mts +85 -0
  77. package/src/witness/space/inner-space/inner-space-v1.respec.mts +181 -228
  78. package/src/witness/space/space-helper.mts +42 -4
  79. package/src/witness/space/space-helper.respec.mts +42 -0
  80. 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
- // import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
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 { createTimeline, getHistory, mut8Timeline } from '../../../timeline/timeline-api.mjs';
21
- import { MetaspaceService } from '../metaspace/metaspace-types.mjs';
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
- * not sure where to put this, but we probably will want to reuse this in the
28
- * future (assuming it works)
29
- * @returns metaspace service reference
30
- */
31
- async function getNewInitializedInMemoryMetaspaceForTesting({
32
- defaultSpaceName,
33
- }: {
34
- defaultSpaceName: string,
35
- }): Promise<MetaspaceService> {
36
- const lc = `[${getNewInitializedInMemoryMetaspaceForTesting.name}]`;
37
- try {
38
- if (logalot) { console.log(`${lc} starting... (I: 766d7596addcb73f4820586469233b25)`); }
39
-
40
- let metaspace = new Metaspace_Innerspace(/*cacheSvc*/undefined);
41
- if (logalot) { console.log(`${lc} creating metaspace complete. initializing... (I: 61b74d62e8832c9fa853e4b8c4c2d825)`); }
42
-
43
- await metaspace.initialize({
44
- spaceName: defaultSpaceName,
45
- metaspaceFactory: undefined,
46
- getFnAlert: () => { return async ({ title, msg }) => console.log(title, msg) },
47
- getFnPrompt: () => {
48
- return async ({ title, msg }) => {
49
- // if this is needed, we might set up some way for testing
50
- // to prepare either a queue of prompts or some kind of map
51
- // and put it on the metaspace itself
52
- throw new Error(`not implemented (E: c7ef688a02f8cb74487260f9274ac825)`);
53
- // promptForText({ title, msg, confirm: false });
54
- }
55
- },
56
- getFnPromptPassword: () => {
57
- return async () => {
58
- return 'password';
59
- // promptForSecret({ confirm: true })
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
- return metaspace;
64
- } catch (error) {
65
- console.error(`${lc} ${extractErrorMsg(error)}`);
66
- throw error;
67
- } finally {
68
- if (logalot) { console.log(`${lc} complete.`); }
69
- }
70
- }
71
-
72
- await respecfully(sir, 'metaspace setup itself', async () => {
73
- await ifWe(sir, 'should init a metaspace', async () => {
74
- const testSpaceName = 'TestSpaceName';
75
-
76
- const metaspace = await getNewInitializedInMemoryMetaspaceForTesting({
77
- defaultSpaceName: testSpaceName
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
- // await respecfully(sir, `[${InnerSpace_V1.name} specs]`, async () => {
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
- const resPut = await putInSpace({ ibGib: resLockIbGib, force: true, space });
2697
- if (resPut.success) {
2698
- resLockIbGib.data!.success = true;
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.errorMsg}`;
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
+