@amalgm/shell 0.1.135 → 0.1.137

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.
@@ -0,0 +1,3 @@
1
+ /** Run `perform` over every item with at most `concurrency` in flight. The
2
+ * first failure stops admission and rejects the whole loop. */
3
+ export declare function boundedForEach<T>(items: readonly T[], concurrency: number, perform: (item: T) => Promise<void>): Promise<void>;
@@ -0,0 +1,26 @@
1
+ /** Run `perform` over every item with at most `concurrency` in flight. The
2
+ * first failure stops admission and rejects the whole loop. */
3
+ export async function boundedForEach(items, concurrency, perform) {
4
+ if (!Number.isSafeInteger(concurrency) || concurrency < 1) {
5
+ throw new Error("bounded loop concurrency must be a positive integer");
6
+ }
7
+ let next = 0;
8
+ let stopped = false;
9
+ const worker = async () => {
10
+ while (!stopped) {
11
+ const index = next;
12
+ next += 1;
13
+ if (index >= items.length)
14
+ return;
15
+ try {
16
+ await perform(items[index]);
17
+ }
18
+ catch (error) {
19
+ stopped = true;
20
+ throw error;
21
+ }
22
+ }
23
+ };
24
+ await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => worker()));
25
+ }
26
+ //# sourceMappingURL=bounded-for-each.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounded-for-each.js","sourceRoot":"","sources":["../src/bounded-for-each.ts"],"names":[],"mappings":"AAAA;+DAC+D;AAC/D,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAmB,EACnB,WAAmB,EACnB,OAAmC;IAEnC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,WAAW,CAAC,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;IACD,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,MAAM,MAAM,GAAG,KAAK,IAAI,EAAE;QACxB,OAAO,CAAC,OAAO,EAAE,CAAC;YAChB,MAAM,KAAK,GAAG,IAAI,CAAC;YACnB,IAAI,IAAI,CAAC,CAAC;YACV,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM;gBAAE,OAAO;YAClC,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,KAAK,CAAC,KAAK,CAAM,CAAC,CAAC;YACnC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,GAAG,IAAI,CAAC;gBACf,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAC1B,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,EAC/C,GAAG,EAAE,CAAC,MAAM,EAAE,CACf,CAAC,CAAC;AACL,CAAC"}
@@ -3,6 +3,9 @@ export declare function ensurePrivateDir(directory: string): void;
3
3
  * gets 0755 — the same default a fresh mkdir gives the user — and an
4
4
  * existing directory keeps whatever mode the user chose. */
5
5
  export declare function ensureUserDir(directory: string): void;
6
+ /** The batch form of ensureUserDir: the same mode law for a whole directory
7
+ * skeleton, created ahead of the files it will hold and off the event loop. */
8
+ export declare function ensureUserDirs(directories: readonly string[], concurrency: number): Promise<void>;
6
9
  export declare function atomicWrite(file: string, bytes: string | Uint8Array, mode?: number): void;
7
10
  export declare function atomicCopy(source: string, destination: string, mode?: number): void;
8
11
  /** Assemble verified chunk files without holding the complete artifact in RAM. */
@@ -1,7 +1,9 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import { constants, chmodSync, copyFileSync, createReadStream, createWriteStream, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
3
+ import { chmod, mkdir } from "node:fs/promises";
3
4
  import { dirname } from "node:path";
4
5
  import { once } from "node:events";
6
+ import { boundedForEach } from "./bounded-for-each.js";
5
7
  export function ensurePrivateDir(directory) {
6
8
  mkdirSync(directory, { recursive: true, mode: 0o700 });
7
9
  chmodSync(directory, 0o700);
@@ -14,6 +16,15 @@ export function ensureUserDir(directory) {
14
16
  chmodSync(directory, 0o755);
15
17
  }
16
18
  }
19
+ /** The batch form of ensureUserDir: the same mode law for a whole directory
20
+ * skeleton, created ahead of the files it will hold and off the event loop. */
21
+ export async function ensureUserDirs(directories, concurrency) {
22
+ await boundedForEach(directories, concurrency, async (directory) => {
23
+ if (await mkdir(directory, { recursive: true, mode: 0o755 }) !== undefined) {
24
+ await chmod(directory, 0o755);
25
+ }
26
+ });
27
+ }
17
28
  /** File helpers own the file, never the parent's mode: an absent parent is
18
29
  * created private, an existing one — private or user-visible — keeps its
19
30
  * mode. */
@@ -1 +1 @@
1
- {"version":3,"file":"filesystem.js","sourceRoot":"","sources":["../src/filesystem.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"filesystem.js","sourceRoot":"","sources":["../src/filesystem.ts"],"names":[],"mappings":"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"}
@@ -26,3 +26,21 @@ export declare class TransferLegs {
26
26
  private enter;
27
27
  private exit;
28
28
  }
29
+ /** Partition one concurrent phase by three named activities. Counts preserve
30
+ * a bit until the last operation of that kind settles; the eight reported
31
+ * states therefore sum to wall time instead of double-counting overlap. */
32
+ export declare class ConcurrentActivityClock<Activity extends string> {
33
+ private readonly activities;
34
+ private readonly counts;
35
+ private readonly totals;
36
+ private state;
37
+ private changedAt;
38
+ constructor(activities: readonly [Activity, Activity, Activity]);
39
+ time<T>(activity: Activity, work: () => Promise<T>): Promise<T>;
40
+ timeSync<T>(activity: Activity, work: () => T): T;
41
+ /** Prefix + active activity names form stable ledger keys. */
42
+ report(prefix: string): Record<string, number>;
43
+ private index;
44
+ private transition;
45
+ private record;
46
+ }
@@ -65,4 +65,74 @@ export class TransferLegs {
65
65
  this.quietSince = now;
66
66
  }
67
67
  }
68
+ /** Partition one concurrent phase by three named activities. Counts preserve
69
+ * a bit until the last operation of that kind settles; the eight reported
70
+ * states therefore sum to wall time instead of double-counting overlap. */
71
+ export class ConcurrentActivityClock {
72
+ activities;
73
+ counts = [0, 0, 0];
74
+ totals = Array.from({ length: 8 }, () => 0);
75
+ state = 0;
76
+ changedAt = performance.now();
77
+ constructor(activities) {
78
+ this.activities = activities;
79
+ if (activities.some((activity) => activity.length === 0)
80
+ || new Set(activities).size !== activities.length) {
81
+ throw new Error("concurrent activity names must be distinct");
82
+ }
83
+ }
84
+ async time(activity, work) {
85
+ const index = this.index(activity);
86
+ this.transition(index, 1);
87
+ try {
88
+ return await work();
89
+ }
90
+ finally {
91
+ this.transition(index, -1);
92
+ }
93
+ }
94
+ timeSync(activity, work) {
95
+ const index = this.index(activity);
96
+ this.transition(index, 1);
97
+ try {
98
+ return work();
99
+ }
100
+ finally {
101
+ this.transition(index, -1);
102
+ }
103
+ }
104
+ /** Prefix + active activity names form stable ledger keys. */
105
+ report(prefix) {
106
+ if (!prefix)
107
+ throw new Error("concurrent activity report needs a prefix");
108
+ this.record(performance.now());
109
+ const key = (state) => {
110
+ if (state === 0)
111
+ return `${prefix}NeitherMs`;
112
+ const names = this.activities.filter((_activity, index) => (state & (1 << index)) !== 0);
113
+ const suffix = names.length === 1 ? "Only" : "";
114
+ return `${prefix}${names.map((name) => name.charAt(0).toUpperCase() + name.slice(1)).join("")}${suffix}Ms`;
115
+ };
116
+ return Object.fromEntries(this.totals.map((duration, state) => [key(state), Math.round(duration)]));
117
+ }
118
+ index(activity) {
119
+ const index = this.activities.indexOf(activity);
120
+ if (index < 0)
121
+ throw new Error(`unknown concurrent activity ${activity}`);
122
+ return index;
123
+ }
124
+ transition(index, delta) {
125
+ const now = performance.now();
126
+ this.record(now);
127
+ const next = (this.counts[index] ?? 0) + delta;
128
+ if (next < 0)
129
+ throw new Error("concurrent activity clock lost an operation");
130
+ this.counts[index] = next;
131
+ this.state = this.counts.reduce((mask, count, bit) => count > 0 ? mask | (1 << bit) : mask, 0);
132
+ }
133
+ record(now) {
134
+ this.totals[this.state] = (this.totals[this.state] ?? 0) + (now - this.changedAt);
135
+ this.changedAt = now;
136
+ }
137
+ }
68
138
  //# sourceMappingURL=transfer-instrumentation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"transfer-instrumentation.js","sourceRoot":"","sources":["../src/transfer-instrumentation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,YAAY;IACN,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC5C,QAAQ,GAAG,CAAC,CAAC;IACb,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IAC/B,MAAM,GAAG,CAAC,CAAC;IAEnB;2EACuE;IACvE,GAAG,CAAC,GAAW,EAAE,EAAU;QACzB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;YAAE,OAAO;QAC5C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,IAAI,CAAI,GAAW,EAAE,IAAsB;QAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,EAAE,CAAC;QACtB,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,QAAQ,CAAI,GAAW,EAAE,IAAa;QACpC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED;yCACqC;IACrC,MAAM;QACJ,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAC9B,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QACrE,OAAO;YACL,GAAG,MAAM,CAAC,WAAW,CACnB,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAC3D;YACD,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,YAAY,CAAC;SAC/C,CAAC;IACJ,CAAC;IAEO,KAAK;QACX,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAC9B,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC;YAAE,IAAI,CAAC,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC;QAC9D,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;QACnB,OAAO,GAAG,CAAC;IACb,CAAC;IAEO,IAAI,CAAC,GAAW,EAAE,SAAiB;QACzC,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAC9B,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,SAAS,CAAC,CAAC;QAC/B,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;QACnB,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC;YAAE,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC;IACjD,CAAC;CACF"}
1
+ {"version":3,"file":"transfer-instrumentation.js","sourceRoot":"","sources":["../src/transfer-instrumentation.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,7 @@ import { indexRowsByOutermostRoot } from "./ground-root-index.js";
14
14
  import { ContentCacheDownloadSet, captureContentFile, contentBlockCollector, hasSealedContent, hashContentFile, pruneSettledContentCache, sealCapturedArtifact, } from "./content-cache-host.js";
15
15
  import { RegisterUploadFusion } from "./register-upload-fusion.js";
16
16
  import { StagedContentSet, addStagingEvidenceDir, clearAddStagingEvidence, } from "./workspace-add-staging.js";
17
+ import { boundedForEach } from "./bounded-for-each.js";
17
18
  import { DirectContentPlane } from "./content-direct.js";
18
19
  import { encodeContentWireBytes } from "./content-wire-codec.js";
19
20
  import { TransferLegs } from "./transfer-instrumentation.js";
@@ -43,6 +44,12 @@ const CARGO_SEGMENT_LOAD_CONCURRENCY = 8;
43
44
  * few broad ranged reads. Memory is bounded by the byte laws below, never by
44
45
  * this count. */
45
46
  const DIRECT_OBJECT_DOWNLOAD_CONCURRENCY = CONTENT_REFS_BATCH_OBJECTS;
47
+ /** Independent destination writers. Pack reads are intentionally few, but
48
+ * one pack may contain thousands of unrelated files; those files must not
49
+ * inherit the pack's request count as their filesystem concurrency. Sixteen
50
+ * handles are bounded on a 1 GiB runtime and enough to hide metadata latency
51
+ * behind the volume's byte throughput. */
52
+ const DIRECT_CONTENT_STORE_CONCURRENCY = 16;
46
53
  // Opening one protected object transiently retains the fetched envelope,
47
54
  // WebCrypto's ciphertext-with-tag input, and the plaintext. Bound plaintext
48
55
  // to two full blocks so those three representations stay below 96 MiB even
@@ -400,7 +407,7 @@ export class UserGroundHost {
400
407
  });
401
408
  }
402
409
  },
403
- onStage: async (stage) => {
410
+ onStage: async (stage, measurements) => {
404
411
  const now = performance.now();
405
412
  this.stage({
406
413
  primitive: stage === "watching" ? "watch" : "download",
@@ -408,6 +415,7 @@ export class UserGroundHost {
408
415
  status: "completed",
409
416
  workspaceId: input.workspace.uuid,
410
417
  durationMs: now - milestoneAt,
418
+ ...(measurements ? { measurements } : {}),
411
419
  });
412
420
  milestoneAt = now;
413
421
  await this.options.onWorkspaceAddStage?.({
@@ -1870,7 +1878,7 @@ export class UserGroundHost {
1870
1878
  },
1871
1879
  });
1872
1880
  const downloads = new ContentCacheDownloadSet(cacheDir);
1873
- const stagedSet = staged ? new StagedContentSet(staged.evidenceDir) : null;
1881
+ let stagedSet = null;
1874
1882
  const targets = new Map();
1875
1883
  const target = (artifact) => {
1876
1884
  const found = targets.get(artifact.contentHash);
@@ -1883,16 +1891,22 @@ export class UserGroundHost {
1883
1891
  // which stays on the cache rail so a warm re-Add remains a local
1884
1892
  // clone instead of a fresh download.
1885
1893
  const route = (artifact, manifest) => {
1886
- const stagedPaths = stagedSet && staged?.destinations.get(artifact.contentHash);
1887
- if (stagedPaths && !hasSealedContent(cacheDir, artifact.contentHash, manifest.bytes)) {
1894
+ const set = stagedSet;
1895
+ const stagedPaths = set && staged?.destinations.get(artifact.contentHash);
1896
+ if (set && stagedPaths && !hasSealedContent(cacheDir, artifact.contentHash, manifest.bytes)) {
1888
1897
  stagedArtifacts += 1;
1889
- targets.set(artifact.contentHash, stagedSet.target(artifact, manifest, stagedPaths));
1898
+ targets.set(artifact.contentHash, set.target(artifact, manifest, stagedPaths));
1890
1899
  }
1891
1900
  else {
1892
1901
  targets.set(artifact.contentHash, downloads.target(artifact, manifest));
1893
1902
  }
1894
1903
  };
1895
1904
  try {
1905
+ // The staged rail's directory skeleton is the set's first act, so no
1906
+ // chunk's store path includes a mkdir.
1907
+ stagedSet = staged
1908
+ ? await StagedContentSet.open(staged.evidenceDir, staged.destinations)
1909
+ : null;
1896
1910
  // Every storage object — manifests and blocks alike — is fetched
1897
1911
  // straight from content storage over a presigned URL and opened
1898
1912
  // locally; the engine plans whole waves against these primitives, so
@@ -1954,6 +1968,7 @@ export class UserGroundHost {
1954
1968
  },
1955
1969
  }, {
1956
1970
  concurrency: DIRECT_OBJECT_DOWNLOAD_CONCURRENCY,
1971
+ storeConcurrency: DIRECT_CONTENT_STORE_CONCURRENCY,
1957
1972
  maxInFlightBytes: DIRECT_OBJECT_DOWNLOAD_MAX_IN_FLIGHT_BYTES,
1958
1973
  });
1959
1974
  const content = new Map();
@@ -1983,9 +1998,9 @@ export class UserGroundHost {
1983
1998
  // plan (manifest cache + routing), fetch (storage waits, whose
1984
1999
  // decrypt share the plane delta names), hash (chunk verification),
1985
2000
  // reuse (retained-chunk re-proof), write (chunk placement), seal
1986
- // (whole-file proof + durability), and idle — wall time with
1987
- // nothing in flight. The staged rail decomposes its own write and
1988
- // seal internals below; `stagedArtifacts` counts its placements.
2001
+ // (whole-file proof assertion + duplicates), and idle — wall time
2002
+ // with nothing in flight. The staged rail decomposes its own
2003
+ // placement legs below; `stagedArtifacts` counts its placements.
1989
2004
  requests,
1990
2005
  // The engine's own geometry: how manifests were known (local vs
1991
2006
  // fetched, packed vs standalone) and how many planned pack spans
@@ -5726,9 +5741,11 @@ async function materializeRecords(input) {
5726
5741
  for (const list of stagedDestinations.values())
5727
5742
  list.sort();
5728
5743
  }
5744
+ const contentStarted = performance.now();
5729
5745
  const fileContent = await readContent([...wanted.values()], staged && stagedDestinations
5730
5746
  ? { evidenceDir: staged.evidenceDir, destinations: stagedDestinations }
5731
5747
  : undefined);
5748
+ const chainStarted = performance.now();
5732
5749
  const repositoryChains = new Map();
5733
5750
  const walking = repositoryHeads.map(({ record, artifact }) => {
5734
5751
  const local = fileContent.get(artifact.contentHash);
@@ -5773,11 +5790,15 @@ async function materializeRecords(input) {
5773
5790
  walk.parent = inspectRepositoryTransportFile(parent.file).parentTransportVersion;
5774
5791
  }
5775
5792
  }
5793
+ const walkStarted = performance.now();
5794
+ let repositoriesMs = 0;
5776
5795
  const materializeRepository = async (record, destination) => {
5777
5796
  const chain = repositoryChains.get(record.uuid);
5778
5797
  if (!chain)
5779
5798
  throw new Error(`cloud repository ${record.uuid} transport was not downloaded`);
5799
+ const started = performance.now();
5780
5800
  const applied = await applyRepositoryFiles(destination, chain);
5801
+ repositoriesMs += performance.now() - started;
5781
5802
  const repository = { record, state: applied };
5782
5803
  repositories.push(repository);
5783
5804
  repositoriesByUuid.set(record.uuid, repository);
@@ -5931,6 +5952,7 @@ async function materializeRecords(input) {
5931
5952
  throw new Error(`materialization for ${record.type} requires its dedicated adapter`);
5932
5953
  }
5933
5954
  }
5955
+ const rowsStarted = performance.now();
5934
5956
  const rows = records.map((record) => {
5935
5957
  const relativePath = paths.get(record.uuid);
5936
5958
  const absolutePath = relativePath ? join(rootPath, ...relativePath.split("/")) : rootPath;
@@ -5944,7 +5966,18 @@ async function materializeRecords(input) {
5944
5966
  ...statFingerprint(stats),
5945
5967
  };
5946
5968
  });
5947
- return { rows, repositories };
5969
+ const finished = performance.now();
5970
+ return {
5971
+ rows,
5972
+ repositories,
5973
+ timing: {
5974
+ contentMs: Math.round(chainStarted - contentStarted),
5975
+ transportChainMs: Math.round(walkStarted - chainStarted),
5976
+ repositoriesMs: Math.round(repositoriesMs),
5977
+ groundWalkMs: Math.round(rowsStarted - walkStarted - repositoriesMs),
5978
+ rowsMs: Math.round(finished - rowsStarted),
5979
+ },
5980
+ };
5948
5981
  }
5949
5982
  /** Refresh the always-materialized portable reference inventory selected by
5950
5983
  * Live. Paths and bindings are local; UUIDs, parents, names, and versions are
@@ -6193,6 +6226,7 @@ async function installCloudWorkspace(input) {
6193
6226
  staged: { evidenceDir: addStagingEvidenceDir(database, intent.workspaceId) },
6194
6227
  readContent,
6195
6228
  });
6229
+ const graphStarted = performance.now();
6196
6230
  const rows = projectMaterializedGraph(materialized.rows, materialized.repositories.map((repository) => ({
6197
6231
  record: repository.record,
6198
6232
  identity: repository.state.identity,
@@ -6202,7 +6236,13 @@ async function installCloudWorkspace(input) {
6202
6236
  ? join(intent.destinationPath, ...row.relativePath.split("/"))
6203
6237
  : intent.destinationPath,
6204
6238
  }));
6205
- await onStage?.("materialization-verified");
6239
+ // The milestone's duration spans the whole materialization; its legs say
6240
+ // where that wall time went after the content set itself.
6241
+ await onStage?.("materialization-verified", {
6242
+ ...materialized.timing,
6243
+ graphMs: Math.round(performance.now() - graphStarted),
6244
+ repositories: materialized.repositories.length,
6245
+ });
6206
6246
  persistRows(database, identity, intent.resourceId, intent.workspaceId, rows);
6207
6247
  input.adoptMaterialization(intent.workspaceId);
6208
6248
  await onStage?.("rows-committed");
@@ -6292,26 +6332,6 @@ async function retryContent(operation, eventualNotFound = false, attempts = 10)
6292
6332
  }
6293
6333
  throw last;
6294
6334
  }
6295
- async function boundedForEach(items, concurrency, perform) {
6296
- let next = 0;
6297
- let stopped = false;
6298
- const worker = async () => {
6299
- while (!stopped) {
6300
- const index = next;
6301
- next += 1;
6302
- if (index >= items.length)
6303
- return;
6304
- try {
6305
- await perform(items[index]);
6306
- }
6307
- catch (error) {
6308
- stopped = true;
6309
- throw error;
6310
- }
6311
- }
6312
- };
6313
- await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => worker()));
6314
- }
6315
6335
  /** Admit queued async tasks with at most `limit` in flight. Scheduling never
6316
6336
  * blocks the caller; each task's settlement is its returned promise. The
6317
6337
  * admitted task re-checks the bound because a released slot may be claimed