@ibgib/core-gib 0.1.48 → 0.1.50

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 (94) hide show
  1. package/dist/common/comment/comment-constants.d.mts +5 -0
  2. package/dist/common/comment/comment-constants.d.mts.map +1 -1
  3. package/dist/common/comment/comment-constants.mjs +5 -0
  4. package/dist/common/comment/comment-constants.mjs.map +1 -1
  5. package/dist/common/comment/comment-helper.d.mts +9 -1
  6. package/dist/common/comment/comment-helper.d.mts.map +1 -1
  7. package/dist/common/comment/comment-helper.mjs +7 -5
  8. package/dist/common/comment/comment-helper.mjs.map +1 -1
  9. package/dist/sync/sync-conflict-adv-multitimelines.respec.mjs +1 -1
  10. package/dist/sync/sync-conflict-adv-multitimelines.respec.mjs.map +1 -1
  11. package/dist/sync/sync-conflict-basic-divergence.respec.mjs +1 -1
  12. package/dist/sync/sync-conflict-basic-divergence.respec.mjs.map +1 -1
  13. package/dist/sync/sync-conflict-basic-multitimelines.respec.mjs +1 -1
  14. package/dist/sync/sync-conflict-basic-multitimelines.respec.mjs.map +1 -1
  15. package/dist/sync/sync-conflict-text-merge.respec.mjs +1 -1
  16. package/dist/sync/sync-conflict-text-merge.respec.mjs.map +1 -1
  17. package/dist/sync/sync-innerspace-constants.respec.mjs +1 -1
  18. package/dist/sync/sync-innerspace-constants.respec.mjs.map +1 -1
  19. package/dist/sync/sync-innerspace-deep-updates.respec.mjs +1 -1
  20. package/dist/sync/sync-innerspace-deep-updates.respec.mjs.map +1 -1
  21. package/dist/sync/sync-innerspace-dest-ahead-withid.respec.mjs +13 -13
  22. package/dist/sync/sync-innerspace-dest-ahead-withid.respec.mjs.map +1 -1
  23. package/dist/sync/sync-innerspace-dest-ahead.respec.mjs +1 -1
  24. package/dist/sync/sync-innerspace-dest-ahead.respec.mjs.map +1 -1
  25. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs +1 -1
  26. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
  27. package/dist/sync/sync-innerspace-partial-update.respec.mjs +1 -1
  28. package/dist/sync/sync-innerspace-partial-update.respec.mjs.map +1 -1
  29. package/dist/sync/sync-innerspace.respec.mjs +1 -1
  30. package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
  31. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-http-node-adapter.d.mts +57 -0
  32. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-http-node-adapter.d.mts.map +1 -0
  33. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-http-node-adapter.mjs +310 -0
  34. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-http-node-adapter.mjs.map +1 -0
  35. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-types.d.mts +27 -0
  36. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-types.d.mts.map +1 -0
  37. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-types.mjs +2 -0
  38. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-types.mjs.map +1 -0
  39. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.d.mts +29 -0
  40. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.d.mts.map +1 -0
  41. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.mjs +122 -0
  42. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.mjs.map +1 -0
  43. package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-types.d.mts +27 -0
  44. package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-types.d.mts.map +1 -0
  45. package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-types.mjs +2 -0
  46. package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-types.mjs.map +1 -0
  47. package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.d.mts +24 -0
  48. package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.d.mts.map +1 -0
  49. package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.mjs +111 -0
  50. package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.mjs.map +1 -0
  51. package/dist/sync/sync-peer/sync-peer-http.respec.d.mts +8 -0
  52. package/dist/sync/sync-peer/sync-peer-http.respec.d.mts.map +1 -0
  53. package/dist/sync/sync-peer/sync-peer-http.respec.mjs +333 -0
  54. package/dist/sync/sync-peer/sync-peer-http.respec.mjs.map +1 -0
  55. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts.map +1 -1
  56. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs +8 -7
  57. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
  58. package/dist/sync/sync-peer/sync-peer-types.d.mts +5 -5
  59. package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -1
  60. package/dist/sync/sync-peer/sync-peer-v1.d.mts +0 -2
  61. package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
  62. package/dist/sync/sync-peer/sync-peer-v1.mjs +21 -5
  63. package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
  64. package/dist/sync/sync-saga-coordinator.mjs +1 -1
  65. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  66. package/ibgib-foundations.md +109 -89
  67. package/package.json +1 -1
  68. package/src/common/comment/comment-constants.mts +6 -0
  69. package/src/common/comment/comment-helper.mts +13 -4
  70. package/src/sync/sync-conflict-adv-multitimelines.respec.mts +1 -1
  71. package/src/sync/sync-conflict-basic-divergence.respec.mts +1 -1
  72. package/src/sync/sync-conflict-basic-multitimelines.respec.mts +1 -1
  73. package/src/sync/sync-conflict-text-merge.respec.mts +1 -1
  74. package/src/sync/sync-innerspace-constants.respec.mts +1 -1
  75. package/src/sync/sync-innerspace-deep-updates.respec.mts +1 -1
  76. package/src/sync/sync-innerspace-dest-ahead-withid.respec.mts +12 -12
  77. package/src/sync/sync-innerspace-dest-ahead.respec.mts +1 -1
  78. package/src/sync/sync-innerspace-multiple-timelines.respec.mts +1 -1
  79. package/src/sync/sync-innerspace-partial-update.respec.mts +1 -1
  80. package/src/sync/sync-innerspace.respec.mts +1 -1
  81. package/src/sync/sync-peer/sync-peer-http-receiver/sync-http-node-adapter.mts +319 -0
  82. package/src/sync/sync-peer/sync-peer-http-receiver/sync-http-node-adapter.mts.metadata.md +72 -0
  83. package/src/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-types.mts +30 -0
  84. package/src/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.mts +146 -0
  85. package/src/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.mts.metadata.md +71 -0
  86. package/src/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-types.mts +32 -0
  87. package/src/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.mts +129 -0
  88. package/src/sync/sync-peer/sync-peer-http.respec.mts +364 -0
  89. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.mts +8 -8
  90. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +10 -7
  91. package/src/sync/sync-peer/sync-peer-types.mts +11 -11
  92. package/src/sync/sync-peer/sync-peer-v1.mts +5 -7
  93. package/src/sync/sync-saga-coordinator.mts +1 -1
  94. package/tmp-certs/server.pfx +0 -0
@@ -0,0 +1,129 @@
1
+ /**
2
+ * @module sync-peer-http-sender-v1
3
+ */
4
+
5
+ import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
6
+ import { SyncPeer_V1 } from '../sync-peer-v1.mjs';
7
+ import { SyncSagaContextIbGib_V1 } from '../../sync-saga-context/sync-saga-context-types.mjs';
8
+ import { IbGibSpaceAny } from '../../../witness/space/space-base-v1.mjs';
9
+ import { GLOBAL_LOG_A_LOT } from '../../../core-constants.mjs';
10
+ import {
11
+ InitializeSyncPeerHttpSenderOpts,
12
+ SyncPeerHttpSenderData_V1,
13
+ SyncPeerHttpSenderRel8ns_V1,
14
+ SyncPeerHttpSenderIbGib_V1
15
+ } from './sync-peer-http-sender-types.mjs';
16
+
17
+ const logalot = GLOBAL_LOG_A_LOT;
18
+
19
+ /**
20
+ * Http Sender Peer implementation.
21
+ * Sends Context to a remote server utilizing POST for uplink and SSE for downlink.
22
+ */
23
+ export class SyncPeerHttpSender_V1
24
+ extends SyncPeer_V1<InitializeSyncPeerHttpSenderOpts>
25
+ implements SyncPeerHttpSenderIbGib_V1 {
26
+
27
+ protected override lc: string = `[${SyncPeerHttpSender_V1.name}]`;
28
+
29
+ override get classname(): string {
30
+ return SyncPeerHttpSender_V1.name;
31
+ }
32
+
33
+ declare data: SyncPeerHttpSenderData_V1 | undefined;
34
+ declare rel8ns: SyncPeerHttpSenderRel8ns_V1 | undefined;
35
+
36
+ /**
37
+ * Stored reference to the SSE EventSource connection.
38
+ * Ensure you polyfill EventSource in Node environments if needed.
39
+ */
40
+ protected eventSource?: EventSource;
41
+
42
+ constructor(
43
+ initialData: SyncPeerHttpSenderData_V1,
44
+ initialRel8ns?: SyncPeerHttpSenderRel8ns_V1,
45
+ ) {
46
+ super(initialData, initialRel8ns);
47
+ }
48
+
49
+
50
+
51
+ protected override async sendContextRequest(context: SyncSagaContextIbGib_V1): Promise<SyncSagaContextIbGib_V1 | undefined> {
52
+ const lc = `${this.lc}[${this.sendContextRequest.name}]`;
53
+ try {
54
+ if (logalot) { console.log(`${lc} starting...`); }
55
+
56
+ if (!this.data?.syncEventsRoute) { throw new Error(`data.syncEventsRoute required.`); }
57
+ if (!this.data?.syncRoute) { throw new Error(`data.syncRoute required.`); }
58
+
59
+ // 1. Establish SSE connection to `this.data.syncEventsRoute` if not already open
60
+ // (In a real implementation, you might initialize this elsewhere or ensure it stays reconnecting)
61
+ if (!this.eventSource) {
62
+ if (logalot) { console.log(`${lc} initializing EventSource to ${this.data.syncEventsRoute}`); }
63
+ this.eventSource = new EventSource(this.data.syncEventsRoute);
64
+ this.eventSource.onerror = (err) => console.error(`${lc} EventSource error:`, err);
65
+ }
66
+
67
+ return new Promise<SyncSagaContextIbGib_V1 | undefined>(async (resolve, reject) => {
68
+ // 2. Setup listeners for SSE incoming context and ibgib payloads
69
+ const onMessage = async (event: MessageEvent) => {
70
+ try {
71
+ const parsedData = JSON.parse(event.data);
72
+
73
+ // TODO [USER]: Determine if `parsedData` is a domain payload ibGib OR the final response context.
74
+ // e.g.:
75
+ // if (parsedData.isDomainIbGib) {
76
+ // // Deserialize to IbGib_V1
77
+ // const ibGib = parsedData;
78
+ // await this.payloadIbGibsDomainReceived$.next(ibGib);
79
+ // } else if (parsedData.isResponseContext) {
80
+ // // Deserialize to SyncSagaContextIbGib_V1
81
+ // const responseContext = parsedData;
82
+ // this.eventSource?.removeEventListener('message', onMessage);
83
+ // resolve(responseContext);
84
+ // }
85
+ } catch (e) {
86
+ console.error(`${lc} failed to parse SSE message`, e);
87
+ }
88
+ };
89
+
90
+ this.eventSource!.addEventListener('message', onMessage);
91
+
92
+ try {
93
+ // 3. Serialize Context and Control IbGibs
94
+ // TODO [USER]: Serialize the `context` and `controlIbGibs` into your wire format
95
+ const payloadBody = JSON.stringify({
96
+ context: context,
97
+ // controlIbGibs: ...
98
+ });
99
+
100
+ // 4. POST the context to `this.data.syncRoute`
101
+ if (!this.data) { throw new Error(`(UNEXPECTED) this.data falsy? (E: 8c6aa9e1aa7869465a26ee3a7d136826)`); }
102
+ const response = await fetch(this.data.syncRoute, {
103
+ method: 'POST',
104
+ headers: {
105
+ 'Content-Type': 'application/json',
106
+ // Add Authorization headers here if needed
107
+ },
108
+ body: payloadBody,
109
+ });
110
+
111
+ if (!response.ok) {
112
+ throw new Error(`HTTP Sync Error: ${response.status} ${await response.text()}`);
113
+ }
114
+
115
+ // 5. Wait for the SSE stream response to resolve with the responseContext
116
+ // The Promise will remain pending until `onMessage` identifies the final response context and calls resolve().
117
+ } catch (e) {
118
+ this.eventSource?.removeEventListener('message', onMessage);
119
+ reject(e);
120
+ }
121
+ });
122
+ } catch (error) {
123
+ console.error(`${lc} ${extractErrorMsg(error)}`);
124
+ throw error;
125
+ } finally {
126
+ if (logalot) { console.log(`${lc} complete.`); }
127
+ }
128
+ }
129
+ }
@@ -0,0 +1,364 @@
1
+ /**
2
+ * @module sync-peer-http.respec
3
+ *
4
+ * Verifies SyncSagaCoordinator using a mock HTTP/SSE "thin layer".
5
+ * This utilizes SyncPeerHttpSender_V1 and SyncPeerHttpReceiver_V1 for network synchronization simulations.
6
+ */
7
+
8
+ import * as cp from 'child_process';
9
+ import * as fs from 'fs';
10
+ import * as path from 'path';
11
+
12
+ import {
13
+ respecfully, lastOfAll, iReckon, ifWe, ifWeMight
14
+ } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
15
+ const maam = `[${import.meta.url}]`, sir = maam;
16
+ import { clone, delay, extractErrorMsg, pretty } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
17
+ import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
18
+
19
+ import { GLOBAL_LOG_A_LOT } from '../../core-constants.mjs';
20
+ import { SyncSagaCoordinator } from '../sync-saga-coordinator.mjs';
21
+ import { getFromSpace, putInSpace, registerNewIbGib } from '../../witness/space/space-helper.mjs';
22
+ import { Metaspace_Innerspace } from '../../witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mjs';
23
+ import { InnerSpace_V1 } from '../../witness/space/inner-space/inner-space-v1.mjs';
24
+ import { createTimelineRootTestHelper, getTestKeystoneServiceHelper } from '../../test-helpers.mjs';
25
+ import { mut8Timeline, } from '../../timeline/timeline-api.mjs';
26
+ import { DEFAULT_INNER_SPACE_DATA_V1 } from '../../witness/space/inner-space/inner-space-types.mjs';
27
+ import { toDto } from '../../common/other/ibgib-helper.mjs';
28
+ import { getSyncSagaFrameDependencyGraph } from '../sync-helpers.mjs';
29
+ import { SyncIbGib_V1 } from '../sync-types.mjs';
30
+ import { SyncPeerHttpSender_V1 } from './sync-peer-http-sender/sync-peer-http-sender-v1.mjs';
31
+ import { SyncPeerHttpReceiver_V1 } from './sync-peer-http-receiver/sync-peer-http-receiver-v1.mjs';
32
+ import { SyncPeerHttpSenderData_V1 } from './sync-peer-http-sender/sync-peer-http-sender-types.mjs';
33
+ import { SyncPeerHttpReceiverData_V1 } from './sync-peer-http-receiver/sync-peer-http-receiver-types.mjs';
34
+ import { SyncSagaContextIbGib_V1 } from '../sync-saga-context/sync-saga-context-types.mjs';
35
+ import { SyncHttpNodeAdapter_V1 } from './sync-peer-http-receiver/sync-http-node-adapter.mjs';
36
+
37
+ const logalot = GLOBAL_LOG_A_LOT;
38
+ const lc = `[sync-peer-http.respec]`;
39
+
40
+ await respecfully(sir, `Sync HTTP Peers`, async () => {
41
+
42
+ let metaspace: Metaspace_Innerspace;
43
+ let sourceSpace: InnerSpace_V1;
44
+ let destSpace: InnerSpace_V1;
45
+
46
+ // We focus this test initially for development
47
+ await ifWeMight(sir, `Basic Push Sync over Mocked HTTP/SSE`, async () => {
48
+ // 1. Setup Spaces
49
+ metaspace = new Metaspace_Innerspace(undefined);
50
+ await metaspace.initialize({
51
+ getFnAlert: () => async ({ title, msg }) => { console.log(`[Alert] ${title}: ${msg}`); },
52
+ getFnPrompt: () => async ({ title, msg }) => { console.log(`[Prompt] ${title}: ${msg}`); return ''; },
53
+ getFnPromptPassword: () => async (title, msg) => { console.log(`[PromptPwd] ${title}: ${msg}`); return null; },
54
+ });
55
+ while (!metaspace.initialized) { await delay(10); }
56
+
57
+ const defaultLocalUserSpace = await metaspace.getLocalUserSpace({ lock: false });
58
+ if (!defaultLocalUserSpace) { throw new Error(`(UNEXPECTED) defaultLocalUserSpace falsy?`); }
59
+ await defaultLocalUserSpace.initialized;
60
+
61
+ sourceSpace = new InnerSpace_V1({
62
+ ...DEFAULT_INNER_SPACE_DATA_V1,
63
+ name: 'source',
64
+ uuid: 'source_uuid',
65
+ description: 'source test space',
66
+ });
67
+ await sourceSpace.initialized;
68
+
69
+ destSpace = new InnerSpace_V1({
70
+ ...DEFAULT_INNER_SPACE_DATA_V1,
71
+ name: 'dest',
72
+ uuid: 'dest_uuid',
73
+ description: 'dest test space',
74
+ });
75
+ await destSpace.initialized;
76
+
77
+ // 2. Seed Source Data
78
+ if (logalot) { console.log(`${lc} Creating data in Source...`); }
79
+ const root = await createTimelineRootTestHelper({
80
+ ib: 'timeline_root',
81
+ data: { type: 'root', },
82
+ space: sourceSpace,
83
+ });
84
+
85
+ const child = await mut8Timeline({
86
+ timeline: root,
87
+ mut8Opts: {
88
+ dataToAddOrPatch: { type: 'child', },
89
+ },
90
+ metaspace,
91
+ space: sourceSpace,
92
+ });
93
+ const childAddr = getIbGibAddr({ ibGib: child });
94
+
95
+ // 3. Setup Coordinators & Peer
96
+ if (logalot) { console.log(`${lc} Setting up Coordinators...`); }
97
+ const mockKeystone = await getTestKeystoneServiceHelper();
98
+
99
+ const senderCoordinator = new SyncSagaCoordinator(mockKeystone);
100
+ const receiverCoordinator = new SyncSagaCoordinator(mockKeystone);
101
+
102
+ // --- Mocking the HTTP "Network" ---
103
+ // Instead of real fetch and EventSource, we wire the Sender's outgoing HTTP
104
+ // calls directly to the Receiver's incoming endpoints.
105
+
106
+ const receiverPeerData: SyncPeerHttpReceiverData_V1 = { classname: SyncPeerHttpReceiver_V1.name };
107
+ const receiverPeer = new SyncPeerHttpReceiver_V1(receiverPeerData);
108
+ await receiverPeer.initialized;
109
+ await receiverPeer.initializeOpts({
110
+ localCoordinator: receiverCoordinator,
111
+ localMetaspace: metaspace,
112
+ localSpace: destSpace,
113
+ });
114
+
115
+ // We'll subclass/monkey-patch the Sender peer to use mock fetching
116
+ class MockSyncPeerHttpSender extends SyncPeerHttpSender_V1 {
117
+ override async sendContextRequest(context: SyncSagaContextIbGib_V1): Promise<SyncSagaContextIbGib_V1 | undefined> {
118
+ // Mock POST /sync/frame
119
+ // Real sender would serialize and POST. Here we just hand it to receiver:
120
+ const responsePromise = new Promise<SyncSagaContextIbGib_V1 | undefined>(async (resolve, reject) => {
121
+ try {
122
+ const rel8ns = context.rel8ns;
123
+ if (!rel8ns || (rel8ns.sagaFrame || []).length !== 1) { throw new Error(`(UNEXPECTED) context.rel8ns.sagaFrame missing?`); }
124
+ const sagaAddr = rel8ns.sagaFrame![0];
125
+ const getSagaRes = await getFromSpace({ addr: sagaAddr, space: sourceSpace });
126
+ const sagaFrame = getSagaRes.ibGibs![0] as SyncIbGib_V1;
127
+
128
+ const { msgStones, identities } = await getSyncSagaFrameDependencyGraph({
129
+ sagaIbGib: sagaFrame,
130
+ localSpace: sourceSpace,
131
+ });
132
+ const msg = msgStones[0];
133
+ const controlIbGibs = [...identities, msg, sagaFrame, context].filter(x => !!x).map(x => toDto({ ibGib: x }));
134
+
135
+ // The Server Adapter calls receiverPeer.handleIncomingSyncRequest
136
+ const responseContext = await receiverPeer.handleIncomingSyncRequest({
137
+ context,
138
+ controlIbGibs
139
+ });
140
+
141
+ if (!responseContext) {
142
+ resolve(undefined);
143
+ return;
144
+ }
145
+
146
+ // **Mock the Server Adapter returning data**
147
+ // Real server would stream the newly evolved Control Context ibgibs to SSE,
148
+ // followed by the response domain payload ibgibs.
149
+ // We must MANUALLY persist the control ibgibs to the sender space FIRST,
150
+ // to simulate the client downloading and validating them from the SSE connection.
151
+ await delay(100);
152
+ const responseSagaAddr = responseContext.rel8ns!.sagaFrame![0];
153
+ const getResponseSagaRes = await getFromSpace({ addr: responseSagaAddr, space: destSpace });
154
+ const responseSagaFrame = getResponseSagaRes.ibGibs![0] as SyncIbGib_V1;
155
+ const depGraphResponse = await getSyncSagaFrameDependencyGraph({
156
+ sagaIbGib: responseSagaFrame,
157
+ localSpace: destSpace,
158
+ });
159
+ const responseControlIbGibs = [
160
+ ...identities, // ORIGINAL identities, not the receiver's!
161
+ depGraphResponse.msgStones[0],
162
+ responseSagaFrame,
163
+ context
164
+ ].filter(x => !!x).map(x => toDto({ ibGib: x }));
165
+
166
+ for (const ibGib of responseControlIbGibs) {
167
+ await putInSpace({ space: sourceSpace, ibGibs: [ibGib] });
168
+ await registerNewIbGib({ ibGib, space: sourceSpace, fnBroadcast: undefined });
169
+ }
170
+
171
+ await delay(5); // Simulate end of SSE task event
172
+ // Resolve with the responseContext
173
+ resolve(responseContext);
174
+
175
+ // THEN simulate streaming the domain payload ibgibs to the local subject
176
+ if (responseContext.payloadIbGibsDomain) {
177
+ new Promise<void>(async (resDom) => {
178
+ for (const domainIbGib of responseContext.payloadIbGibsDomain!) {
179
+ await delay(1); // emulate network gap between SSE events
180
+ await this.payloadIbGibsDomainReceived$.next(domainIbGib);
181
+ }
182
+ resDom();
183
+ });
184
+ }
185
+
186
+ } catch (e) {
187
+ reject(e);
188
+ }
189
+ });
190
+ return responsePromise;
191
+ }
192
+ }
193
+
194
+ const senderPeerData: SyncPeerHttpSenderData_V1 = {
195
+ classname: SyncPeerHttpSender_V1.name,
196
+ syncRoute: 'http://mock/sync/frame',
197
+ syncEventsRoute: 'http://mock/sync/events'
198
+ };
199
+ const senderPeer = new MockSyncPeerHttpSender(senderPeerData);
200
+ await senderPeer.initialized;
201
+
202
+ // We configure localSpace and a senderTempSpace for the mock:
203
+ const senderTempSpace = new InnerSpace_V1({
204
+ ...DEFAULT_INNER_SPACE_DATA_V1, name: 'sender_temp', uuid: 'sender_temp_uuid'
205
+ });
206
+ await senderTempSpace.initialized;
207
+
208
+ await senderPeer.initializeOpts({
209
+ localSpace: sourceSpace,
210
+ localTempSpace: senderTempSpace
211
+ });
212
+
213
+ // 4. Run Sync
214
+ if (logalot) { console.log(`${lc} Running Sync...`); }
215
+ try {
216
+ const resSync = await senderCoordinator.sync({
217
+ peer: senderPeer,
218
+ localSpace: sourceSpace,
219
+ metaspace: metaspace,
220
+ domainIbGibs: [child], // Sync the child
221
+ useSessionIdentity: false, // Testing logic only
222
+ });
223
+
224
+ await resSync.done;
225
+ iReckon(sir, true).asTo('Mocked request ran').isGonnaBeTrue();
226
+
227
+ // We now expect the entire saga to complete because handleIncomingSyncRequest works
228
+ let childInDest = await getFromSpace({ addr: childAddr, space: destSpace });
229
+ iReckon(sir, childInDest.success).asTo('Child is correctly pushed to destination space').isGonnaBeTrue();
230
+
231
+ } catch (error: any) {
232
+ console.error(`${lc}[senderCoordinator.sync] FULL ERROR:`, error.stack || error.message || error);
233
+ // Will fail initially because handleIncomingSyncRequest is not implemented
234
+ iReckon(sir, false).asTo(`Unexpected error: ${extractErrorMsg(error)}`).isGonnaBeTrue();
235
+ }
236
+
237
+ });
238
+
239
+ await respecfully(sir, `Real Sync via Local HTTP/2 Node Adapter with Self-Signed Certs`, async () => {
240
+ // 1. Generate Self-Signed Certs via OpenSSL or PowerShell
241
+ const tmpDir = path.join(process.cwd(), 'tmp-certs');
242
+ const keyPath = path.join(tmpDir, 'server.key');
243
+ const certPath = path.join(tmpDir, 'server.cert');
244
+ const pfxPath = path.join(tmpDir, 'server.pfx');
245
+
246
+ let hasTlsGenerator = false;
247
+ let isWin = process.platform === 'win32';
248
+
249
+ if (isWin) {
250
+ hasTlsGenerator = true;
251
+ } else {
252
+ try {
253
+ cp.execSync('openssl version', { stdio: 'ignore' });
254
+ hasTlsGenerator = true;
255
+ } catch (e) {
256
+ console.warn(`${lc} OpenSSL not found in PATH. Skipping HTTP/2 Native test.`);
257
+ }
258
+ }
259
+
260
+ if (hasTlsGenerator) {
261
+ if (!fs.existsSync(tmpDir)) { fs.mkdirSync(tmpDir, { recursive: true }); }
262
+
263
+ let keyInfo: Buffer | undefined;
264
+ let certInfo: Buffer | undefined;
265
+ let pfxInfo: Buffer | undefined;
266
+
267
+ if (isWin) {
268
+ if (!fs.existsSync(pfxPath)) {
269
+ if (logalot) { console.log(`${lc} Generating self-signed cert via PowerShell...`); }
270
+ const psScript = `
271
+ $cert = New-SelfSignedCertificate -CertStoreLocation "Cert:\\CurrentUser\\My" -Subject "localhost" -KeyExportPolicy Exportable -NotAfter (Get-Date).AddDays(1)
272
+ $pwd = ConvertTo-SecureString -String "testpass" -Force -AsPlainText
273
+ Export-PfxCertificate -Cert $cert -FilePath "${pfxPath.replace(/\\/g, '\\\\')}" -Password $pwd
274
+ Remove-Item -Path $cert.PSPath
275
+ `;
276
+ cp.execSync(`powershell -Command "${psScript.replace(/\n/g, '; ')}"`, { stdio: 'ignore' });
277
+ }
278
+ pfxInfo = fs.readFileSync(pfxPath);
279
+ } else {
280
+ if (!fs.existsSync(keyPath) || !fs.existsSync(certPath)) {
281
+ if (logalot) { console.log(`${lc} Generating self-signed cert...`); }
282
+ cp.execSync(`openssl req -x509 -newkey rsa:2048 -nodes -keyout ${keyPath} -out ${certPath} -days 1 -subj "/CN=localhost"`, { stdio: 'ignore' });
283
+ }
284
+ keyInfo = fs.readFileSync(keyPath);
285
+ certInfo = fs.readFileSync(certPath);
286
+ }
287
+
288
+ // Bypass self-signed cert rejection in Node fetch
289
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
290
+
291
+ // 2. Setup Spaces
292
+ metaspace = new Metaspace_Innerspace(undefined);
293
+ await metaspace.initialize({
294
+ getFnAlert: () => async () => { },
295
+ getFnPrompt: () => async () => '',
296
+ getFnPromptPassword: () => async () => null,
297
+ });
298
+ while (!metaspace.initialized) { await delay(10); }
299
+
300
+ const defaultLocalUserSpace = await metaspace.getLocalUserSpace({ lock: false });
301
+ await defaultLocalUserSpace?.initialized;
302
+
303
+ sourceSpace = new InnerSpace_V1({ ...DEFAULT_INNER_SPACE_DATA_V1, name: 'source_h2', uuid: 'source_h2_uuid' });
304
+ await sourceSpace.initialized;
305
+ destSpace = new InnerSpace_V1({ ...DEFAULT_INNER_SPACE_DATA_V1, name: 'dest_h2', uuid: 'dest_h2_uuid' });
306
+ await destSpace.initialized;
307
+
308
+ // 3. Setup Node Adapter & Receiver
309
+ const mockKeystone = await getTestKeystoneServiceHelper();
310
+ const receiverCoordinator = new SyncSagaCoordinator(mockKeystone);
311
+
312
+ const receiverPeer = new SyncPeerHttpReceiver_V1({ classname: SyncPeerHttpReceiver_V1.name });
313
+ await receiverPeer.initialized;
314
+ await receiverPeer.initializeOpts({
315
+ localCoordinator: receiverCoordinator,
316
+ localMetaspace: metaspace,
317
+ localSpace: destSpace,
318
+ });
319
+
320
+ const port = 8443;
321
+ const adapter = new SyncHttpNodeAdapter_V1({
322
+ receiverPeer,
323
+ port,
324
+ key: keyInfo,
325
+ cert: certInfo,
326
+ pfx: pfxInfo,
327
+ passphrase: 'testpass',
328
+ basePath: '/sync'
329
+ });
330
+
331
+ await adapter.start();
332
+
333
+ // 4. Test the Adapter Connection
334
+ if (logalot) { console.log(`${lc} Pinging the HTTP/2 Adapter via POST...`); }
335
+
336
+ // Create a dummy context structure just to appease the parser and prove connection success
337
+ const testContext: any = { classname: 'SyncSagaContextIbGib_V1', rel8ns: {}, data: {} };
338
+ const payloadBody = JSON.stringify({ context: testContext, controlIbGibs: [] });
339
+
340
+ try {
341
+ const res = await fetch(`https://localhost:${port}/sync/frame`, {
342
+ method: 'POST',
343
+ headers: { 'Content-Type': 'application/json', 'x-client-id': 'test-123' },
344
+ body: payloadBody
345
+ });
346
+
347
+ await ifWeMight(sir, 'check http2 adapter', async () => {
348
+ iReckon(sir, res.ok).asTo('Successfully reached HTTP/2 Adapter via POST').isGonnaBeTrue();
349
+ });
350
+
351
+
352
+ // The receiver peer inside the adapter will throw a normal logged warning about invalid saga formats from our dummy context,
353
+ // but the HTTP transport should respond 200 OK, verifying the http2 server is cleanly bound!
354
+
355
+ } catch (err) {
356
+ console.error(`${lc} HTTP/2 test failed:`, err);
357
+ iReckon(sir, false).asTo('HTTP/2 Adapter POST should not throw').isGonnaBeTrue();
358
+ } finally {
359
+ await adapter.stop();
360
+ }
361
+ }
362
+ });
363
+
364
+ });
@@ -15,30 +15,30 @@ import { SyncPeerData_V1, SyncPeerRel8ns_V1 } from '../sync-peer-types.mjs';
15
15
  */
16
16
  export interface SyncPeerInnerspaceOptions {
17
17
  /**
18
- * The "receiver" durable space (not temp).
19
- *
18
+ * The "receiver" durable space (not temp).
19
+ *
20
20
  * ## notes
21
- *
21
+ *
22
22
  * when syncing/merging locally, we are working with two spaces and this
23
23
  * acts as the "remote" space even though it's technically still local.
24
24
  */
25
25
  receiverSpace: IbGibSpaceAny;
26
26
  /**
27
- * Coordinator for the "receiver".
28
- *
27
+ * Coordinator for the "receiver".
28
+ *
29
29
  * @see {@link receiverSpace} notes.
30
30
  */
31
31
  receiverCoordinator: SyncSagaCoordinator;
32
32
  /**
33
- * Metaspace for the "receiver".
34
- *
33
+ * Metaspace for the "receiver".
34
+ *
35
35
  * @see {@link receiverSpace} notes.
36
36
  */
37
37
  receiverMetaspace: MetaspaceService;
38
38
  /**
39
39
  * Temporary sync transaction space for the "receiver", for storage of
40
40
  * domain ibgibs created/received throughout the sync saga until commit.
41
- *
41
+ *
42
42
  * @see {@link receiverSpace} notes.
43
43
  */
44
44
  receiverTempSpace: IbGibSpaceAny;
@@ -67,7 +67,7 @@ export class SyncPeerInnerspace_V1 extends SyncPeer_V1<InitializeSyncPeerInnersp
67
67
  super(initialData, initialRel8ns);
68
68
  }
69
69
 
70
- protected override async ensureReceiverTempSpace(): Promise<IbGibSpaceAny> {
70
+ protected async ensureReceiverTempSpace(): Promise<IbGibSpaceAny> {
71
71
  const lc = `${this.lc}[${this.ensureReceiverTempSpace.name}]`;
72
72
  try {
73
73
  if (logalot) { console.log(`${lc} starting... (I: bab87f15dea77cd4892bd8a8d2e65826)`); }
@@ -108,14 +108,17 @@ export class SyncPeerInnerspace_V1 extends SyncPeer_V1<InitializeSyncPeerInnersp
108
108
 
109
109
  if (!this.opts) { throw new Error(`(UNEXPECTED) this.opts falsy? (E: d8b1348e3233810128999596b1fa5826)`); }
110
110
 
111
+ await this.ensureReceiverTempSpace();
112
+
111
113
  const {
112
- senderSpace, senderTempSpace,
114
+ localSpace, localTempSpace,
113
115
  receiverSpace, receiverCoordinator, receiverMetaspace,
114
116
  receiverTempSpace
115
117
  } = this.opts;
116
118
 
117
119
  if (logalot) { console.log(`${lc} starting...Context: ${getIbGibAddr({ ibGib: context })}`); }
118
120
 
121
+
119
122
  const { sagaFrame } = context;
120
123
 
121
124
  // The context has already been validated, authenticated and authorized at this point.
@@ -128,7 +131,7 @@ export class SyncPeerInnerspace_V1 extends SyncPeer_V1<InitializeSyncPeerInnersp
128
131
  if (!sagaFrame.data) { throw new Error(`(UNEXPECTED) sagaFrame.data falsy? (E: f30e290a8b770e1b387377420ea73a26)`); }
129
132
  if (!sagaFrame.rel8ns) { throw new Error(`(UNEXPECTED) sagaFrame.rel8ns falsy? (E: f888caa698b8cf6b4893b7fd6df09726)`); }
130
133
  if (!receiverTempSpace) { throw new Error(`(UNEXPECTED) receiverTempSpace falsy? (E: 3b46d838f6fc645c58b29215bfddb826)`); }
131
- if (!senderTempSpace) { throw new Error(`(UNEXPECTED) senderTempSpace falsy? (E: b187a8f913c2295ae90d7ae2b47b6a26)`); }
134
+ if (!localTempSpace) { throw new Error(`(UNEXPECTED) localTempSpace falsy? (E: b187a8f913c2295ae90d7ae2b47b6a26)`); }
132
135
  // #endregion sanity validation assertions
133
136
 
134
137
  // let localSpace: IbGibSpaceAny;
@@ -149,7 +152,7 @@ export class SyncPeerInnerspace_V1 extends SyncPeer_V1<InitializeSyncPeerInnersp
149
152
 
150
153
  const { sagaIbGib: _alreadyHave, msgStones, identities } = await getSyncSagaFrameDependencyGraph({
151
154
  sagaIbGib: sagaFrame,
152
- localSpace: senderSpace,
155
+ localSpace: localSpace,
153
156
  });
154
157
  if (msgStones.length !== 1) { throw new Error(`(UNEXPECTED) msgStones.length !== 1? we're only geared for a single msg stone that is always present (not 0, not greater than 1) (E: 2d3138ed130f1aca116551889483e826)`); }
155
158
  const msg = msgStones[0];
@@ -238,13 +241,13 @@ export class SyncPeerInnerspace_V1 extends SyncPeer_V1<InitializeSyncPeerInnersp
238
241
  // ...put into sender's durable space
239
242
  await putInSpace({
240
243
  ibGibs: responsePayloadIbGibsControl,
241
- space: senderSpace,
244
+ space: localSpace,
242
245
  });
243
246
  // register yet??
244
247
  for (const control of responsePayloadIbGibsControl) {
245
248
  await registerNewIbGib({
246
249
  ibGib: control,
247
- space: senderSpace,
250
+ space: localSpace,
248
251
  fnBroadcast: undefined
249
252
  });
250
253
  }
@@ -266,7 +269,7 @@ export class SyncPeerInnerspace_V1 extends SyncPeer_V1<InitializeSyncPeerInnersp
266
269
 
267
270
  await putInSpace_dnasThenNonDnas({
268
271
  ibGibs: payloadIbGibsDomain_response,
269
- space: senderTempSpace,
272
+ space: localTempSpace,
270
273
  });
271
274
 
272
275
  for (const payloadDomain of payloadIbGibsDomain_response) {
@@ -35,20 +35,20 @@ export interface SyncPeerIbGib_V1 extends IbGib_V1<SyncPeerData_V1, SyncPeerRel8
35
35
  */
36
36
  export interface InitializeSyncPeerOpts {
37
37
  /**
38
- * sender's durable space.
39
- *
38
+ * local durable space.
39
+ *
40
40
  * will store control ibgibs (context, sync saga ibgib, control msg stones)
41
- * here (and in {@link senderTempSpace}) throughout the process for audit trail.
41
+ * here (and in {@link localTempSpace}) throughout the process for audit trail.
42
42
  */
43
- senderSpace: IbGibSpaceAny;
43
+ localSpace: IbGibSpaceAny;
44
44
  /**
45
- * sender's temporary space for the entire sync transaction.
46
- *
45
+ * local temporary space for the entire sync transaction.
46
+ *
47
47
  * In addition to control ibgibs which are stored in both temp and durable
48
48
  * spaces, this temp space will be where domain ibgibs are stored that are
49
49
  * received/created throughout the transaction until commit.
50
50
  */
51
- senderTempSpace?: IbGibSpaceAny;
51
+ localTempSpace?: IbGibSpaceAny;
52
52
  }
53
53
 
54
54
  /**
@@ -69,9 +69,9 @@ export interface SyncPeerWitness<TInitializeOpts extends InitializeSyncPeerOpts
69
69
 
70
70
  /**
71
71
  * sender-specific opts/config
72
- *
72
+ *
73
73
  * ## notes
74
- *
74
+ *
75
75
  * I'm doing this hack because I have to be able to create a peer without
76
76
  * being able to fully configure it at construction time. So I can't place
77
77
  * this init code in the {@link initialize} method.
@@ -81,14 +81,14 @@ export interface SyncPeerWitness<TInitializeOpts extends InitializeSyncPeerOpts
81
81
  /**
82
82
  * use this to set the senderTempSpace and any other optional opts that need
83
83
  * to be configured just before sending any contexts across the wire.
84
- *
84
+ *
85
85
  * @see {@link opts}
86
86
  */
87
87
  setOptionalOpts(arg: Partial<TInitializeOpts>): void;
88
88
 
89
89
  /**
90
90
  * Observable for streaming large domain payloads asynchronously.
91
- *
91
+ *
92
92
  * Implementations should publish domain ibgibs to this observable as they are received.
93
93
  * The handling code (e.g. SyncSagaCoordinator) will subscribe to this.
94
94
  */