@amalgm/shell 0.1.55 → 0.1.57

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.
@@ -3,11 +3,12 @@ import { existsSync, lstatSync, realpathSync, readFileSync, readdirSync, readlin
3
3
  import { open } from "node:fs/promises";
4
4
  import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
5
5
  import { buildUserHomeManifest, buildUserManifest, liveMachineStateDir, scopedAmalgmDir, shippedUserHomeDeclaration, } from "@amalgm/core/identity";
6
- import { CHUNK_BYTES, CONTENT_CONTRACT, ENTITY_CLOUD_CONTRACT, ENTITY_CLOUD_SCHEMA_VERSION, artifactForRecord, canonicalVersion, checkContentManifest, classifyDirectory, classifyFile, classifyRegisteredRoot, createLocalEntityRecord, createEntityRecordAuthorityPort, convergeUserGround, createUserGroundEnrollmentPolicy, isRepositoryMetadataEntry, download as downloadArtifact, encodeEntityRecord, EntityApplyRail, EntityRecordRail, membershipHash, parseSnapshot, pathIsSuspect, privateEntityResourceId, sameRecords, snapshotFromRecords, stableJson, travelingRecords, upload as uploadArtifact, userGroundRecords, } from "@amalgm/live";
6
+ import { CHUNK_BYTES, CONTENT_CONTRACT, ENTITY_CLOUD_CONTRACT, ENTITY_CLOUD_SCHEMA_VERSION, artifactForRecord, canonicalVersion, checkContentManifest, classifyDirectory, classifyFile, classifyRegisteredRoot, createLocalEntityRecord, createEntityRecordAuthorityPort, convergeUserGround, createUserGroundEnrollmentPolicy, isRepositoryMetadataEntry, indexRepositoryTerritory, download as downloadArtifact, encodeEntityRecord, EntityApplyRail, EntityRecordRail, membershipHash, parseSnapshot, pathIsSuspect, privateEntityResourceId, sameRecords, snapshotFromRecords, stableJson, travelingRecords, upload as uploadArtifact, userGroundRecords, } from "@amalgm/live";
7
7
  import Database from "better-sqlite3";
8
8
  import { atomicCopy, atomicWrite, ensurePrivateDir } from "./filesystem.js";
9
9
  import { EntityApplyHost } from "./entity-apply-host.js";
10
10
  import { EntityRecordSqliteStore } from "./entity-record-store.js";
11
+ import { indexRowsByOutermostRoot } from "./ground-root-index.js";
11
12
  import { ContentCacheDownload, captureContentFile, hashContentFile, sealCapturedArtifact, } from "./content-cache-host.js";
12
13
  import { decodeContentWireBytes, encodeContentWireBytes } from "./content-wire-codec.js";
13
14
  import { exactTextReplay, planGroundDetection, reconcileGroundUUIDs, } from "./detection/portable.js";
@@ -1983,6 +1984,14 @@ async function reconcileGround(input) {
1983
1984
  ? notebook.filter((row) => materializedUUIDs.has(row.uuid))
1984
1985
  : materialized;
1985
1986
  const materializedByUuid = new Map(materializedBaseline.map((row) => [row.uuid, row]));
1987
+ const materializedByRoot = new Map();
1988
+ for (const row of materializedBaseline) {
1989
+ if (row.resourceId !== resourceId)
1990
+ continue;
1991
+ const rows = materializedByRoot.get(row.rootUUID) || [];
1992
+ rows.push(row);
1993
+ materializedByRoot.set(row.rootUUID, rows);
1994
+ }
1986
1995
  const ignoreFile = join(userRoot, ".amalgmignore");
1987
1996
  const policy = createUserGroundEnrollmentPolicy(existsSync(ignoreFile) ? readFileSync(ignoreFile, "utf8") : "");
1988
1997
  const bindingDir = workspaceBindingDir(userRoot, identity.deviceId);
@@ -1991,6 +2000,8 @@ async function reconcileGround(input) {
1991
2000
  .map(({ workspaceId, directory }) => [workspaceId, directory]));
1992
2001
  const outerBoundRoots = boundRootEntries.filter((candidate) => !boundRootEntries.some((other) => other.workspaceId !== candidate.workspaceId
1993
2002
  && pathWithin(other.directory, candidate.directory)));
2003
+ const existingByUuid = new Map(existing.map((row) => [row.uuid, row]));
2004
+ const existingByOutermostRoot = indexRowsByOutermostRoot(outerBoundRoots.map(({ workspaceId, directory }) => ({ rootId: workspaceId, directory })), existing.filter((row) => row.resourceId === resourceId));
1994
2005
  const registeredBoundaries = new Map(boundRootEntries
1995
2006
  .map(({ workspaceId, directory }) => [resolve(directory), workspaceId]));
1996
2007
  const suspicionFor = (root) => {
@@ -2029,7 +2040,7 @@ async function reconcileGround(input) {
2029
2040
  notebookByUuid.delete(uuid);
2030
2041
  }
2031
2042
  const currentIds = new Set(result.rows.map((row) => row.record.uuid));
2032
- for (const prior of materializedBaseline.filter((row) => row.resourceId === parameters.resourceId && row.rootUUID === parameters.rootUUID)) {
2043
+ for (const prior of materializedByRoot.get(parameters.rootUUID) || []) {
2033
2044
  if (!currentIds.has(prior.uuid))
2034
2045
  materializedRemovals.add(prior.uuid);
2035
2046
  }
@@ -2081,8 +2092,8 @@ async function reconcileGround(input) {
2081
2092
  suspicion: observationFor(userRoot),
2082
2093
  });
2083
2094
  for (const { workspaceId, directory: rootPath } of outerBoundRoots) {
2084
- const existingRoot = existing.find((row) => row.uuid === workspaceId);
2085
- const existingWorkspaceRows = existing.filter((row) => row.resourceId === resourceId && pathWithin(rootPath, row.absolutePath));
2095
+ const existingRoot = existingByUuid.get(workspaceId);
2096
+ const existingWorkspaceRows = existingByOutermostRoot.get(workspaceId) || [];
2086
2097
  await scan({
2087
2098
  identity,
2088
2099
  resourceId,
@@ -2446,20 +2457,20 @@ async function reconcileRoot(input) {
2446
2457
  // children first so one capture pass is already the fixed point.
2447
2458
  const repositories = entries.filter((entry) => entry.type === "repo.git")
2448
2459
  .sort((left, right) => right.relativePath.length - left.relativePath.length);
2460
+ const territory = indexRepositoryTerritory(entries);
2461
+ const previousEntries = [...existingByPath.values()];
2462
+ const previousTerritory = indexRepositoryTerritory(previousEntries);
2449
2463
  const replayByUuid = new Map();
2450
- const properDescendantOf = (candidate, ancestor) => ancestor === "" ? candidate !== "" : candidate.startsWith(`${ancestor}/`);
2451
- const repositoryOwner = (entry) => repositories
2452
- .filter((repository) => repository.uuid !== entry.uuid
2453
- && properDescendantOf(entry.relativePath, repository.relativePath))
2454
- .sort((left, right) => right.relativePath.length - left.relativePath.length)[0] ?? null;
2455
2464
  try {
2456
2465
  for (const repository of repositories) {
2457
- let repoIdentity = entries
2458
- .filter((entry) => repositoryOwner(entry)?.uuid === repository.uuid)
2466
+ const ownedEntries = territory.ownedBy(repository.uuid);
2467
+ const repositoryPath = (entry) => repository.relativePath
2468
+ ? entry.relativePath.slice(repository.relativePath.length + 1)
2469
+ : entry.relativePath;
2470
+ const ownedByPath = new Map(ownedEntries.map((entry) => [repositoryPath(entry), entry]));
2471
+ let repoIdentity = ownedEntries
2459
2472
  .map((entry) => ({
2460
- path: repository.relativePath
2461
- ? entry.relativePath.slice(repository.relativePath.length + 1)
2462
- : entry.relativePath,
2473
+ path: repositoryPath(entry),
2463
2474
  uuid: entry.uuid,
2464
2475
  type: entry.type,
2465
2476
  payloadVersion: entry.payloadVersion,
@@ -2497,15 +2508,8 @@ async function reconcileRoot(input) {
2497
2508
  }
2498
2509
  }
2499
2510
  }
2500
- const previousRepositories = [...existingByPath.values()]
2501
- .filter((row) => row.type === "repo.git");
2502
- const previousOwner = (row) => previousRepositories
2503
- .filter((candidate) => candidate.uuid !== row.uuid
2504
- && properDescendantOf(row.relativePath, candidate.relativePath))
2505
- .sort((left, right) => right.relativePath.length - left.relativePath.length)[0] ?? null;
2506
2511
  const previousIdentity = prior && priorRow
2507
- ? [...existingByPath.values()]
2508
- .filter((row) => previousOwner(row)?.uuid === priorRow.uuid)
2512
+ ? previousTerritory.ownedBy(priorRow.uuid)
2509
2513
  .map((row) => ({
2510
2514
  path: priorRow.relativePath
2511
2515
  ? row.relativePath.slice(priorRow.relativePath.length + 1)
@@ -2532,10 +2536,7 @@ async function reconcileRoot(input) {
2532
2536
  throw new Error(`repository did not settle during capture: ${repository.absolutePath}`);
2533
2537
  }
2534
2538
  for (const identityEntry of repoIdentity) {
2535
- const entry = entries.find((candidate) => repositoryOwner(candidate)?.uuid === repository.uuid
2536
- && (repository.relativePath
2537
- ? candidate.relativePath.slice(repository.relativePath.length + 1)
2538
- : candidate.relativePath) === identityEntry.path);
2539
+ const entry = ownedByPath.get(identityEntry.path);
2539
2540
  if (!entry)
2540
2541
  continue;
2541
2542
  entry.type = identityEntry.type;
@@ -2625,7 +2626,7 @@ async function reconcileRoot(input) {
2625
2626
  const detected = rows.map((row) => {
2626
2627
  const base = beforeByUuid.get(row.record.uuid) ?? null;
2627
2628
  const entry = entryByUuid.get(row.record.uuid);
2628
- if (row.record.type !== "repo.git" && entry && repositoryOwner(entry)) {
2629
+ if (row.record.type !== "repo.git" && entry && territory.ownerOf(entry)) {
2629
2630
  return {
2630
2631
  record: row.record,
2631
2632
  relativePath: row.relativePath,