amalgm 0.1.208 → 0.1.209

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.
@@ -17,7 +17,7 @@ const CLAIM_LIMIT = 16;
17
17
  const CLAIM_LEASE_MS = 30_000;
18
18
  const RETRY_MS = 1_000;
19
19
  const REAUTHORIZE_MS = 30_000;
20
- const REPO_OBJECT_REQUEST_TIMEOUT_MS = 2_000;
20
+ const REPO_OBJECT_REQUEST_TIMEOUT_MS = 15_000;
21
21
 
22
22
  function createSharedRealtimeTunnel({
23
23
  send,
@@ -475,35 +475,45 @@ function createSharedRealtimeTunnel({
475
475
  }
476
476
 
477
477
  function handleRepoObjectStored(frame) {
478
- const object = repoUploadInflight.get(frame.request_id);
478
+ const object = repoUploadInflight.get(frame.request_id)
479
+ || [...repoUploadInflight.values()].find((entry) => entry.sha256 === frame.sha256);
479
480
  if (!object || object.sha256 !== frame.sha256) return;
480
481
  void localStateJson('POST', '/state/repo-objects/uploads/ack', {
481
482
  resourceId: object.resourceId,
482
483
  sha256: object.sha256,
483
484
  })
484
485
  .then(() => {
485
- repoUploadInflight.delete(frame.request_id);
486
+ for (const [requestId, entry] of repoUploadInflight) {
487
+ if (entry.sha256 === object.sha256) repoUploadInflight.delete(requestId);
488
+ }
486
489
  scheduleRepoObjects();
487
490
  })
488
491
  .catch(() => {
489
- repoUploadInflight.delete(frame.request_id);
492
+ for (const [requestId, entry] of repoUploadInflight) {
493
+ if (entry.sha256 === object.sha256) repoUploadInflight.delete(requestId);
494
+ }
490
495
  scheduleRepoObjects(RETRY_MS);
491
496
  });
492
497
  }
493
498
 
494
499
  function handleRepoObjectData(frame) {
495
- const object = repoDownloadInflight.get(frame.request_id);
500
+ const object = repoDownloadInflight.get(frame.request_id)
501
+ || [...repoDownloadInflight.values()].find((entry) => entry.sha256 === frame.sha256);
496
502
  if (!object || object.sha256 !== frame.sha256) return;
497
503
  void localStateJson('POST', '/state/repo-objects/downloads/receive', {
498
504
  resourceId: object.resourceId,
499
505
  sha256: object.sha256,
500
506
  dataBase64: frame.data_b64,
501
507
  }).then(() => {
502
- repoDownloadInflight.delete(frame.request_id);
508
+ for (const [requestId, entry] of repoDownloadInflight) {
509
+ if (entry.sha256 === object.sha256) repoDownloadInflight.delete(requestId);
510
+ }
503
511
  scheduleRepoObjects();
504
512
  }).catch((error) => {
505
513
  warn(`repo object ${object.sha256} was not stored locally: ${error?.message || error}`);
506
- repoDownloadInflight.delete(frame.request_id);
514
+ for (const [requestId, entry] of repoDownloadInflight) {
515
+ if (entry.sha256 === object.sha256) repoDownloadInflight.delete(requestId);
516
+ }
507
517
  scheduleRepoObjects(RETRY_MS);
508
518
  });
509
519
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amalgm",
3
- "version": "0.1.208",
3
+ "version": "0.1.209",
4
4
  "description": "Amalgm local computer runtime: login, MCP, chat, events, previews, and tunnels.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
@@ -127,6 +127,7 @@ test('repo object chunks retry when an acknowledgement is lost', async () => {
127
127
  const uploadBytes = Buffer.from('retry this content-addressed chunk');
128
128
  const uploadSha = crypto.createHash('sha256').update(uploadBytes).digest('hex');
129
129
  const resourceId = 'shr_cp1abcdefghijklmnopqrstuvwx';
130
+ let acknowledgements = 0;
130
131
  const server = http.createServer((req, res) => {
131
132
  req.resume();
132
133
  req.on('end', () => {
@@ -147,6 +148,9 @@ test('repo object chunks retry when an acknowledgement is lost', async () => {
147
148
  }));
148
149
  } else if (req.url === '/state/repo-objects/downloads/claim') {
149
150
  res.end('{"download":null}');
151
+ } else if (req.url === '/state/repo-objects/uploads/ack') {
152
+ acknowledgements += 1;
153
+ res.end('{"changed":true,"completed":true}');
150
154
  } else {
151
155
  res.end('{}');
152
156
  }
@@ -173,6 +177,13 @@ test('repo object chunks retry when an acknowledgement is lost', async () => {
173
177
  const puts = frames.filter((frame) => frame.type === 'repo.object.put');
174
178
  assert.notEqual(puts[0].request_id, puts[1].request_id);
175
179
  assert.equal(puts[0].sha256, puts[1].sha256);
180
+ tunnel.handleFrame({
181
+ type: 'repo.object.stored',
182
+ request_id: puts[0].request_id,
183
+ sha256: uploadSha,
184
+ bytes: uploadBytes.length,
185
+ });
186
+ await until(() => acknowledgements === 1, 'the late upload acknowledgement');
176
187
  } finally {
177
188
  tunnel.stop();
178
189
  server.close();
@@ -137,6 +137,19 @@ test('only a value different from both the materialized baseline and the rail is
137
137
  branches: [{ name: 'main', oid: oidA }],
138
138
  },
139
139
  ), false);
140
+ assert.equal(hasLocalContribution(
141
+ {
142
+ head: { branch: 'main', oid: oidB, detached: false },
143
+ refs: [{ name: 'main', oid: oidB }],
144
+ },
145
+ remoteAdvance,
146
+ baseline,
147
+ null,
148
+ {
149
+ head: { branch: 'main', oid: oidB, detached: false },
150
+ branches: [{ name: 'main', oid: oidB }],
151
+ },
152
+ ), false);
140
153
  assert.equal(hasLocalContribution(
141
154
  local,
142
155
  plan,
@@ -231,7 +231,11 @@ function hasLocalContribution(card, plan, baseline, checkpoint = null, previousC
231
231
  const atBaseline = baseline
232
232
  && card.head?.branch === baseline.branch
233
233
  && card.head?.oid === baseline.baseOid;
234
- if (!onRail && !atBaseline) return true;
234
+ const alreadyPublished = previousCard && samePosition(card, previousCard);
235
+ const reversesPublishedSharedPosition = previousCard
236
+ && samePosition(previousCard, plan.card)
237
+ && !alreadyPublished;
238
+ if ((!onRail && !atBaseline && !alreadyPublished) || reversesPublishedSharedPosition) return true;
235
239
 
236
240
  const currentRefs = oidMap(card.refs);
237
241
  const railRefs = oidMap(plan.branches);
@@ -246,12 +250,14 @@ function hasLocalContribution(card, plan, baseline, checkpoint = null, previousC
246
250
  const shared = railRefs.get(name) ?? null;
247
251
  const previous = baselineRefs.get(name) ?? null;
248
252
  const published = previousRefs.get(name) ?? null;
253
+ const matchesBaseline = baselineRefs.has(name) && current === previous;
254
+ const matchesPublished = previousRefs.has(name) && current === published;
249
255
  const reversesPublishedSharedValue = previousRefs.has(name)
250
256
  && published === shared
251
257
  && current !== published;
252
258
  if (
253
259
  current !== shared
254
- && (!baselineRefs.has(name) || current !== previous || reversesPublishedSharedValue)
260
+ && ((!matchesBaseline && !matchesPublished) || reversesPublishedSharedValue)
255
261
  ) return true;
256
262
  }
257
263