ushman-ledger 1.2.1 → 1.2.2

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.
package/dist/doctor.js CHANGED
@@ -5,71 +5,202 @@ import { sha256File } from "./json.js";
5
5
  import { getOrderedEntryLocations, readManifestEntryBatch } from "./list.js";
6
6
  import { isReadIndexCurrent, readReadIndex } from "./read-index.js";
7
7
  import { loadLedgerState } from "./recovery.js";
8
+ import { getLedgerRuntimeConfig } from "./runtime-config.js";
8
9
  import { readManifest } from "./storage/filesystem.js";
9
- const BLOB_HASH_CONCURRENCY = 16;
10
10
  const CHECKPOINT_MAX_AGE_MS = 24 * 60 * 60 * 1_000;
11
- const ENTRY_READ_BATCH_SIZE = 32;
12
11
  const OPEN_ISSUE_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1_000;
13
- const checkPrevChain = (entry, previousByPhase, issues) => {
12
+ export const DOCTOR_FINDING_CODES = [
13
+ 'blob-corrupt',
14
+ 'blob-missing',
15
+ 'blob-unreadable',
16
+ 'change-log-rollback-missing-target',
17
+ 'change-log-smoke-failure-missing-rollback-plan',
18
+ 'manifest-entry-count-mismatch',
19
+ 'manifest-entry-location-missing',
20
+ 'manifest-entry-missing-on-disk',
21
+ 'manifest-last-sequence-mismatch',
22
+ 'manifest-per-phase-latest-mismatch',
23
+ 'manifest-phase-mismatch',
24
+ 'manifest-sequence-mismatch',
25
+ 'open-issue-stale',
26
+ 'phase-prev-entry-mismatch',
27
+ 'pre-change-checkpoint-stale',
28
+ 'read-failure',
29
+ ];
30
+ const createFinding = ({ code, message, metadata, remediation, }) => ({
31
+ code,
32
+ message,
33
+ metadata,
34
+ remediation,
35
+ });
36
+ const buildDoctorReport = (findings) => ({
37
+ checkedAt: new Date().toISOString(),
38
+ findings: [...findings],
39
+ issueCount: findings.length,
40
+ issues: findings.map((finding) => finding.message),
41
+ ok: findings.length === 0,
42
+ });
43
+ const pushFinding = (findings, finding) => {
44
+ findings.push(finding);
45
+ };
46
+ const isMissingPathError = (error) => {
47
+ const code = error.code;
48
+ return code === 'ENOENT' || code === 'ENOTDIR';
49
+ };
50
+ const buildReadFailure = (error) => buildDoctorReport([
51
+ createFinding({
52
+ code: 'read-failure',
53
+ message: `Failed to read ledger state: ${error instanceof Error ? error.message ?? error.name : String(error)}.`,
54
+ remediation: 'Re-open the ledger or rerun the command first so recovery can reconcile pending state. If the error persists, repair or restore the invalid manifest/read-index JSON before archiving.',
55
+ }),
56
+ ]);
57
+ const isChangeLogEntry = (entry) => entry.kind === 'change-log';
58
+ const isOpenIssueNote = (entry) => entry.kind === 'note' && entry.subkind === 'open-issue';
59
+ const checkPrevChain = (entry, findings, previousByPhase) => {
14
60
  const expectedPrev = previousByPhase.get(entry.phase) ?? null;
15
61
  if (entry.prevEntryId !== expectedPrev) {
16
- issues.push(`Phase ${entry.phase} has broken prevEntryId chain at ${entry.id}: expected ${expectedPrev ?? 'null'}, found ${entry.prevEntryId ?? 'null'}.`);
62
+ pushFinding(findings, createFinding({
63
+ code: 'phase-prev-entry-mismatch',
64
+ message: `Phase ${entry.phase} has broken prevEntryId chain at ${entry.id}: expected ${expectedPrev ?? 'null'}, found ${entry.prevEntryId ?? 'null'}.`,
65
+ metadata: {
66
+ entryId: entry.id,
67
+ expectedPrevEntryId: expectedPrev,
68
+ foundPrevEntryId: entry.prevEntryId,
69
+ phase: entry.phase,
70
+ },
71
+ remediation: 'Restore the edited entry or repair the phase chain so prevEntryId matches append order. Use a correction entry for content fixes instead of hand-editing ledger history.',
72
+ }));
17
73
  }
18
74
  previousByPhase.set(entry.phase, entry.id);
19
75
  };
20
76
  const checkBlobPresence = async (workspaceRoot, blobChecks) => {
21
- const results = await mapWithConcurrencyLimit(blobChecks, BLOB_HASH_CONCURRENCY, async ({ blobHash, entryId }) => {
77
+ const { blobHashConcurrency } = getLedgerRuntimeConfig();
78
+ const groupedBlobChecks = new Map();
79
+ for (const { blobHash, entryId } of blobChecks) {
80
+ const entryIds = groupedBlobChecks.get(blobHash) ?? [];
81
+ entryIds.push(entryId);
82
+ groupedBlobChecks.set(blobHash, entryIds);
83
+ }
84
+ const results = await mapWithConcurrencyLimit([...groupedBlobChecks.entries()], blobHashConcurrency, async ([blobHash, entryIds]) => {
85
+ const blobPath = resolveBlobPath(workspaceRoot, blobHash);
22
86
  try {
23
- const blobPath = resolveBlobPath(workspaceRoot, blobHash);
24
87
  await stat(blobPath);
88
+ }
89
+ catch (error) {
90
+ if (!isMissingPathError(error)) {
91
+ throw error;
92
+ }
93
+ return entryIds.map((entryId) => createFinding({
94
+ code: 'blob-missing',
95
+ message: `Missing blob ${blobHash} for ${entryId}.`,
96
+ metadata: {
97
+ blobSha256: blobHash,
98
+ entryId,
99
+ },
100
+ remediation: 'Restore the missing blob file under .lab/ledger/blobs/ or recreate the patch entry from the original diff before archiving.',
101
+ }));
102
+ }
103
+ try {
25
104
  const actualHash = await sha256File(blobPath);
26
105
  if (actualHash !== blobHash) {
27
- return `Blob ${blobHash} for ${entryId} is corrupted (found ${actualHash}).`;
106
+ return entryIds.map((entryId) => createFinding({
107
+ code: 'blob-corrupt',
108
+ message: `Blob ${blobHash} for ${entryId} is corrupted (found ${actualHash}).`,
109
+ metadata: {
110
+ actualSha256: actualHash,
111
+ blobSha256: blobHash,
112
+ entryId,
113
+ },
114
+ remediation: 'Restore the original blob content under .lab/ledger/blobs/ or recreate the patch entry from the original diff before archiving.',
115
+ }));
28
116
  }
29
- return null;
117
+ return [];
30
118
  }
31
- catch {
32
- return `Missing blob ${blobHash} for ${entryId}.`;
119
+ catch (error) {
120
+ return entryIds.map((entryId) => createFinding({
121
+ code: 'blob-unreadable',
122
+ message: `Blob ${blobHash} for ${entryId} could not be read: ${error instanceof Error ? error.message : String(error)}.`,
123
+ metadata: {
124
+ blobSha256: blobHash,
125
+ entryId,
126
+ },
127
+ remediation: 'Fix the filesystem permission or read error first, then rerun doctor so blob integrity can be verified before archiving.',
128
+ }));
33
129
  }
34
130
  });
35
- return results.filter((issue) => issue !== null);
131
+ return results.flat();
36
132
  };
37
- const checkManifestCounts = (manifest, entryCount) => {
38
- const issues = [];
133
+ const checkManifestCounts = (findings, manifest, entryCount) => {
39
134
  if (manifest.entryCount !== entryCount) {
40
- issues.push(`Manifest entryCount ${manifest.entryCount} does not match disk entries ${entryCount}.`);
135
+ pushFinding(findings, createFinding({
136
+ code: 'manifest-entry-count-mismatch',
137
+ message: `Manifest entryCount ${manifest.entryCount} does not match disk entries ${entryCount}.`,
138
+ metadata: {
139
+ diskEntryCount: entryCount,
140
+ manifestEntryCount: manifest.entryCount,
141
+ },
142
+ remediation: 'Rerun the command or reopen the ledger so recovery can replay pending commits. If the mismatch persists, compare manifest.json with the on-disk phase entries and repair the missing location or file.',
143
+ }));
41
144
  }
42
145
  if (manifest.lastSequence !== entryCount) {
43
- issues.push(`Manifest lastSequence ${manifest.lastSequence} does not match disk entries ${entryCount}.`);
146
+ pushFinding(findings, createFinding({
147
+ code: 'manifest-last-sequence-mismatch',
148
+ message: `Manifest lastSequence ${manifest.lastSequence} does not match disk entries ${entryCount}.`,
149
+ metadata: {
150
+ diskEntryCount: entryCount,
151
+ manifestLastSequence: manifest.lastSequence,
152
+ },
153
+ remediation: 'Repair manifest.json so lastSequence matches the highest durable append sequence after recovery completes.',
154
+ }));
44
155
  }
45
- return issues;
46
156
  };
47
- const finalizeManifestChecks = ({ entryCount, issues, latestByPhase, manifest, unseenManifestEntryIds, }) => {
48
- issues.push(...checkManifestCounts(manifest, entryCount));
157
+ const finalizeManifestChecks = ({ entryCount, findings, latestByPhase, manifest, unseenManifestEntryIds, }) => {
158
+ checkManifestCounts(findings, manifest, entryCount);
49
159
  for (const entryId of unseenManifestEntryIds) {
50
- issues.push(`Manifest entry location points to missing disk entry ${entryId}.`);
160
+ pushFinding(findings, createFinding({
161
+ code: 'manifest-entry-missing-on-disk',
162
+ message: `Manifest entry location points to missing disk entry ${entryId}.`,
163
+ metadata: { entryId },
164
+ remediation: 'Restore the missing entry file or repair manifest.entryLocations so every referenced entry id exists on disk before archiving.',
165
+ }));
51
166
  }
52
- for (const [phase, latest] of latestByPhase.entries()) {
53
- if (manifest.perPhaseLatest[phase] !== latest.entryId) {
54
- issues.push(`Manifest perPhaseLatest mismatch for ${phase}: expected ${latest.entryId}, found ${manifest.perPhaseLatest[phase] ?? 'missing'}.`);
167
+ const phasesToCheck = new Set([...Object.keys(manifest.perPhaseLatest), ...latestByPhase.keys()]);
168
+ for (const phase of phasesToCheck) {
169
+ const latest = latestByPhase.get(phase);
170
+ const expectedEntryId = latest?.entryId ?? null;
171
+ const foundEntryId = manifest.perPhaseLatest[phase] ?? null;
172
+ if (foundEntryId !== expectedEntryId) {
173
+ pushFinding(findings, createFinding({
174
+ code: 'manifest-per-phase-latest-mismatch',
175
+ message: `Manifest perPhaseLatest mismatch for ${phase}: expected ${expectedEntryId ?? 'missing'}, found ${foundEntryId ?? 'missing'}.`,
176
+ metadata: {
177
+ expectedEntryId,
178
+ foundEntryId,
179
+ phase,
180
+ },
181
+ remediation: 'Repair perPhaseLatest so each phase points at the newest durable entry in that phase.',
182
+ }));
55
183
  }
56
184
  }
57
185
  };
58
- const checkManifestSequenceOrder = (entryLocations, issues) => {
186
+ const checkManifestSequenceOrder = (entryLocations, findings) => {
59
187
  for (let index = 0; index < entryLocations.length; index += 1) {
60
188
  const [entryId, location] = entryLocations[index];
61
189
  const expectedSequence = index + 1;
62
190
  if (location.sequence !== expectedSequence) {
63
- issues.push(`Manifest sequence mismatch for ${entryId}: expected ${expectedSequence}, found ${location.sequence}.`);
191
+ pushFinding(findings, createFinding({
192
+ code: 'manifest-sequence-mismatch',
193
+ message: `Manifest sequence mismatch for ${entryId}: expected ${expectedSequence}, found ${location.sequence}.`,
194
+ metadata: {
195
+ entryId,
196
+ expectedSequence,
197
+ foundSequence: location.sequence,
198
+ },
199
+ remediation: 'Repair manifest.entryLocations so sequences remain contiguous and match append order.',
200
+ }));
64
201
  }
65
202
  }
66
203
  };
67
- const buildReadFailure = (error) => ({
68
- issues: [`Failed to read ledger state: ${error instanceof Error ? (error.message ?? error.name) : String(error)}.`],
69
- ok: false,
70
- });
71
- const isChangeLogEntry = (entry) => entry.kind === 'change-log';
72
- const isOpenIssueNote = (entry) => entry.kind === 'note' && entry.subkind === 'open-issue';
73
204
  const trackResolutionLinks = (entry, resolvedLedgerIds) => {
74
205
  if (entry.links.correctsLedgerId) {
75
206
  resolvedLedgerIds.add(entry.links.correctsLedgerId);
@@ -87,7 +218,7 @@ const trackIdempotencyEntry = (entry, entriesByIdempotencyKey) => {
87
218
  existingEntries.push({ id: entry.id, ts: entry.ts });
88
219
  entriesByIdempotencyKey.set(idempotencyKey, existingEntries);
89
220
  };
90
- const checkChangeLogWarnings = ({ checkpointEntries, entriesByIdempotencyKey, issues, nowMs, openIssueEntries, resolvedLedgerIds, }) => {
221
+ const checkChangeLogWarnings = ({ checkpointEntries, entriesByIdempotencyKey, findings, nowMs, openIssueEntries, resolvedLedgerIds, }) => {
91
222
  for (const checkpointEntry of checkpointEntries) {
92
223
  const ageMs = nowMs - Date.parse(checkpointEntry.ts);
93
224
  if (ageMs <= CHECKPOINT_MAX_AGE_MS) {
@@ -97,7 +228,15 @@ const checkChangeLogWarnings = ({ checkpointEntries, entriesByIdempotencyKey, is
97
228
  const hasFollowUp = typeof idempotencyKey === 'string' &&
98
229
  (entriesByIdempotencyKey.get(idempotencyKey) ?? []).some((candidate) => candidate.id !== checkpointEntry.id && candidate.ts >= checkpointEntry.ts);
99
230
  if (!hasFollowUp) {
100
- issues.push(`Pre-change checkpoint ${checkpointEntry.id} is older than 24h and has no follow-up entry with matching idempotencyKey.`);
231
+ pushFinding(findings, createFinding({
232
+ code: 'pre-change-checkpoint-stale',
233
+ message: `Pre-change checkpoint ${checkpointEntry.id} is older than 24h and has no follow-up entry with matching idempotencyKey.`,
234
+ metadata: {
235
+ entryId: checkpointEntry.id,
236
+ idempotencyKey: idempotencyKey ?? null,
237
+ },
238
+ remediation: 'Append a follow-up change-log entry with the same idempotency key, or close the stale checkpoint with a correction entry explaining the abandoned work.',
239
+ }));
101
240
  }
102
241
  }
103
242
  for (const openIssueEntry of openIssueEntries) {
@@ -105,17 +244,36 @@ const checkChangeLogWarnings = ({ checkpointEntries, entriesByIdempotencyKey, is
105
244
  if (ageMs <= OPEN_ISSUE_MAX_AGE_MS || resolvedLedgerIds.has(openIssueEntry.id)) {
106
245
  continue;
107
246
  }
108
- issues.push(`Open issue note ${openIssueEntry.id} is older than 30 days and has no resolution link.`);
247
+ pushFinding(findings, createFinding({
248
+ code: 'open-issue-stale',
249
+ message: `Open issue note ${openIssueEntry.id} is older than 30 days and has no resolution link.`,
250
+ metadata: { entryId: openIssueEntry.id },
251
+ remediation: 'Append a correction or superseding note that links back to the open issue once the follow-up is complete.',
252
+ }));
109
253
  }
110
254
  };
111
- const inspectManifestLocation = ({ entry, issues, latestByPhase, manifest, }) => {
255
+ const inspectManifestLocation = ({ entry, findings, latestByPhase, manifest, }) => {
112
256
  const manifestLocation = manifest.entryLocations[entry.id];
113
257
  if (!manifestLocation) {
114
- issues.push(`Manifest is missing entry location for ${entry.id}.`);
258
+ pushFinding(findings, createFinding({
259
+ code: 'manifest-entry-location-missing',
260
+ message: `Manifest is missing entry location for ${entry.id}.`,
261
+ metadata: { entryId: entry.id },
262
+ remediation: 'Repair manifest.entryLocations so every durable entry has a phase/sequence location before archiving.',
263
+ }));
115
264
  return null;
116
265
  }
117
266
  if (manifestLocation.phase !== entry.phase) {
118
- issues.push(`Manifest phase mismatch for ${entry.id}: expected ${entry.phase}, found ${manifestLocation.phase}.`);
267
+ pushFinding(findings, createFinding({
268
+ code: 'manifest-phase-mismatch',
269
+ message: `Manifest phase mismatch for ${entry.id}: expected ${entry.phase}, found ${manifestLocation.phase}.`,
270
+ metadata: {
271
+ entryId: entry.id,
272
+ expectedPhase: entry.phase,
273
+ foundPhase: manifestLocation.phase,
274
+ },
275
+ remediation: 'Move the entry back to the correct phase directory or repair manifest.entryLocations so the recorded phase matches the stored entry.',
276
+ }));
119
277
  }
120
278
  const currentLatest = latestByPhase.get(entry.phase);
121
279
  if (!currentLatest || manifestLocation.sequence > currentLatest.sequence) {
@@ -123,16 +281,26 @@ const inspectManifestLocation = ({ entry, issues, latestByPhase, manifest, }) =>
123
281
  }
124
282
  return manifestLocation;
125
283
  };
126
- const inspectNarrativeEntry = ({ checkpointEntries, entry, issues, openIssueEntries, }) => {
284
+ const inspectNarrativeEntry = ({ checkpointEntries, entry, findings, openIssueEntries, }) => {
127
285
  if (isChangeLogEntry(entry)) {
128
286
  if (entry.subkind === 'pre-change-checkpoint') {
129
287
  checkpointEntries.push(entry);
130
288
  }
131
289
  if (entry.smokeResult === 'fail' && !entry.rollbackPlan) {
132
- issues.push(`Change-log entry ${entry.id} has smokeResult=fail but no rollbackPlan.`);
290
+ pushFinding(findings, createFinding({
291
+ code: 'change-log-smoke-failure-missing-rollback-plan',
292
+ message: `Change-log entry ${entry.id} has smokeResult=fail but no rollbackPlan.`,
293
+ metadata: { entryId: entry.id },
294
+ remediation: 'Append a correction or follow-up change-log entry documenting the rollback plan before treating the failure as closed.',
295
+ }));
133
296
  }
134
297
  if (entry.subkind === 'rollback' && !entry.rollsBack) {
135
- issues.push(`Change-log rollback entry ${entry.id} is missing rollsBack.`);
298
+ pushFinding(findings, createFinding({
299
+ code: 'change-log-rollback-missing-target',
300
+ message: `Change-log rollback entry ${entry.id} is missing rollsBack.`,
301
+ metadata: { entryId: entry.id },
302
+ remediation: 'Append a correction or replacement rollback entry with rollsBack pointing at the reverted ledger entry id.',
303
+ }));
136
304
  }
137
305
  }
138
306
  if (isOpenIssueNote(entry)) {
@@ -147,14 +315,14 @@ const inspectPatchEntry = ({ blobChecks, entry, }) => {
147
315
  blobChecks.push({ blobHash, entryId: entry.id });
148
316
  }
149
317
  };
150
- const inspectDoctorEntry = ({ blobChecks, checkpointEntries, entry, entriesByIdempotencyKey, issues, latestByPhase, manifest, openIssueEntries, previousByPhase, resolvedLedgerIds, unseenManifestEntryIds, }) => {
318
+ const inspectDoctorEntry = ({ blobChecks, checkpointEntries, entriesByIdempotencyKey, entry, findings, latestByPhase, manifest, openIssueEntries, previousByPhase, resolvedLedgerIds, unseenManifestEntryIds, }) => {
151
319
  unseenManifestEntryIds.delete(entry.id);
152
- checkPrevChain(entry, previousByPhase, issues);
320
+ checkPrevChain(entry, findings, previousByPhase);
153
321
  trackIdempotencyEntry(entry, entriesByIdempotencyKey);
154
322
  trackResolutionLinks(entry, resolvedLedgerIds);
155
323
  if (!inspectManifestLocation({
156
324
  entry,
157
- issues,
325
+ findings,
158
326
  latestByPhase,
159
327
  manifest,
160
328
  })) {
@@ -163,7 +331,7 @@ const inspectDoctorEntry = ({ blobChecks, checkpointEntries, entry, entriesByIde
163
331
  inspectNarrativeEntry({
164
332
  checkpointEntries,
165
333
  entry,
166
- issues,
334
+ findings,
167
335
  openIssueEntries,
168
336
  });
169
337
  inspectPatchEntry({
@@ -172,7 +340,7 @@ const inspectDoctorEntry = ({ blobChecks, checkpointEntries, entry, entriesByIde
172
340
  });
173
341
  };
174
342
  const collectDoctorState = async (workspaceRoot, manifest, readIndex) => {
175
- const issues = [];
343
+ const findings = [];
176
344
  const previousByPhase = new Map();
177
345
  const latestByPhase = new Map();
178
346
  const unseenManifestEntryIds = new Set(Object.keys(manifest.entryLocations));
@@ -183,13 +351,15 @@ const collectDoctorState = async (workspaceRoot, manifest, readIndex) => {
183
351
  const resolvedLedgerIds = new Set();
184
352
  let entryCount = 0;
185
353
  const nowMs = Date.now();
354
+ const { scanBatchSize, scanConcurrency } = getLedgerRuntimeConfig();
186
355
  const orderedEntries = getOrderedEntryLocations(manifest, readIndex, {});
187
- checkManifestSequenceOrder(orderedEntries, issues);
188
- for (let index = 0; index < orderedEntries.length; index += ENTRY_READ_BATCH_SIZE) {
189
- const batch = orderedEntries.slice(index, index + ENTRY_READ_BATCH_SIZE);
356
+ checkManifestSequenceOrder(orderedEntries, findings);
357
+ for (let index = 0; index < orderedEntries.length; index += scanBatchSize) {
358
+ const batch = orderedEntries.slice(index, index + scanBatchSize);
190
359
  const resolvedEntries = await readManifestEntryBatch({
191
360
  allowMissing: true,
192
361
  entryLocations: batch,
362
+ entryReadConcurrency: scanConcurrency,
193
363
  workspaceRoot,
194
364
  });
195
365
  for (const resolvedEntry of resolvedEntries) {
@@ -202,7 +372,7 @@ const collectDoctorState = async (workspaceRoot, manifest, readIndex) => {
202
372
  checkpointEntries,
203
373
  entriesByIdempotencyKey,
204
374
  entry: resolvedEntry.entry,
205
- issues,
375
+ findings,
206
376
  latestByPhase,
207
377
  manifest,
208
378
  openIssueEntries,
@@ -215,7 +385,7 @@ const collectDoctorState = async (workspaceRoot, manifest, readIndex) => {
215
385
  checkChangeLogWarnings({
216
386
  checkpointEntries,
217
387
  entriesByIdempotencyKey,
218
- issues,
388
+ findings,
219
389
  nowMs,
220
390
  openIssueEntries,
221
391
  resolvedLedgerIds,
@@ -223,7 +393,7 @@ const collectDoctorState = async (workspaceRoot, manifest, readIndex) => {
223
393
  return {
224
394
  blobChecks,
225
395
  entryCount,
226
- issues,
396
+ findings,
227
397
  latestByPhase,
228
398
  unseenManifestEntryIds,
229
399
  };
@@ -256,17 +426,14 @@ export const runLedgerDoctor = async (workspaceRoot, options = {}) => {
256
426
  catch (error) {
257
427
  return buildReadFailure(error);
258
428
  }
259
- const { blobChecks, entryCount, issues, latestByPhase, unseenManifestEntryIds } = doctorState;
260
- issues.push(...(await checkBlobPresence(workspaceRoot, blobChecks)));
429
+ const { blobChecks, entryCount, findings, latestByPhase, unseenManifestEntryIds } = doctorState;
430
+ findings.push(...(await checkBlobPresence(workspaceRoot, blobChecks)));
261
431
  finalizeManifestChecks({
262
432
  entryCount,
263
- issues,
433
+ findings,
264
434
  latestByPhase,
265
435
  manifest: preparedState.manifest,
266
436
  unseenManifestEntryIds,
267
437
  });
268
- return {
269
- issues,
270
- ok: issues.length === 0,
271
- };
438
+ return buildDoctorReport(findings);
272
439
  };
package/dist/helpers.d.ts CHANGED
@@ -4,4 +4,5 @@ export declare const createWorkspaceFixture: () => Promise<{
4
4
  }>;
5
5
  export declare const ageFile: (filePath: string, ageMs?: number) => Promise<void>;
6
6
  export declare const waitForFile: (filePath: string, timeoutMs?: number) => Promise<void>;
7
+ export declare const withEnvOverrides: <Value>(overrides: Record<string, string | undefined>, action: () => Promise<Value>) => Promise<Value>;
7
8
  //# sourceMappingURL=helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB;;;EAuBlC,CAAC;AAEF,eAAO,MAAM,OAAO,GAAU,UAAU,MAAM,EAAE,cAAc,kBAG7D,CAAC;AAEF,eAAO,MAAM,WAAW,GAAU,UAAU,MAAM,EAAE,kBAAiB,kBAcpE,CAAC"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB;;;EAuBlC,CAAC;AAEF,eAAO,MAAM,OAAO,GAAU,UAAU,MAAM,EAAE,cAAc,kBAG7D,CAAC;AAEF,eAAO,MAAM,WAAW,GAAU,UAAU,MAAM,EAAE,kBAAiB,kBAcpE,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAU,KAAK,EACxC,WAAW,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC7C,QAAQ,MAAM,OAAO,CAAC,KAAK,CAAC,mBAuB/B,CAAC"}
package/dist/helpers.js CHANGED
@@ -36,3 +36,26 @@ export const waitForFile = async (filePath, timeoutMs = 1_000) => {
36
36
  }
37
37
  throw new Error(`Timed out waiting for file: ${filePath}`);
38
38
  };
39
+ export const withEnvOverrides = async (overrides, action) => {
40
+ const previousValues = new Map();
41
+ for (const [key, value] of Object.entries(overrides)) {
42
+ previousValues.set(key, process.env[key]);
43
+ if (value === undefined) {
44
+ delete process.env[key];
45
+ continue;
46
+ }
47
+ process.env[key] = value;
48
+ }
49
+ try {
50
+ return await action();
51
+ }
52
+ finally {
53
+ for (const [key, value] of previousValues.entries()) {
54
+ if (value === undefined) {
55
+ delete process.env[key];
56
+ continue;
57
+ }
58
+ process.env[key] = value;
59
+ }
60
+ }
61
+ };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  export { type BuildRecordInput, buildChangeLogRecord, buildCorrectionRecord, buildOperatorDecisionRecord, buildStripDecisionRevertedRecord, buildValidatorResultRecord, } from './builders.ts';
2
2
  export { runLedgerCli } from './cli.ts';
3
3
  export { type CoverageReport, computeCoverage } from './coverage.ts';
4
+ export { DOCTOR_FINDING_CODES, type DoctorFinding, type DoctorFindingCode, type DoctorFindingMetadataValue, type DoctorReport, runLedgerDoctor, } from './doctor.ts';
4
5
  export { type LedgerHandle, type LedgerRenderOptions, type LedgerRenderToOptions, openLedger, type RenderTarget, type RenderWriter, } from './handle.ts';
5
6
  export type { LedgerFilter } from './list.ts';
7
+ export { type LedgerRuntimeConfig, getLedgerRuntimeConfig, validateLedgerRuntimeConfig } from './runtime-config.ts';
6
8
  export { appendCleanupWaveNote, appendDecompositionWaveNote, appendNote, appendOpenIssueNote, appendSemanticCleanupSummaryNote, appendVerifiedFlowNote, type NoteBody, } from './note.ts';
7
9
  export { deriveFilesChangedFromPatch } from './patch-resolver.ts';
8
10
  export { type AgentPatchDiff, AgentPatchDiffSchema, AgentPatchEntrySchema, AgentPatchRecordSchema, type ChangeLogEntry, ChangeLogEntrySchema, type ChangeLogFileChange, ChangeLogFileChangeSchema, type ChangeLogParityStatus, ChangeLogParityStatusSchema, type ChangeLogRecord, ChangeLogRecordSchema, type ChangeLogSmokeResult, ChangeLogSmokeResultSchema, type ChangeLogSubkind, ChangeLogSubkindSchema, CorrectionEntrySchema, CorrectionRecordSchema, EmitterSchema, type LedgerEntry, LedgerEntrySchema, type LedgerKind, type LedgerLinks, LedgerLinksSchema, type LedgerPhase, LedgerPhaseSchema, type LedgerRecord, LedgerRecordSchema, type NoteEntry, NoteEntrySchema, type OperatorDecisionAction, OperatorDecisionActionSchema, OperatorDecisionEntrySchema, type OperatorDecisionPayload, OperatorDecisionPayloadSchema, OperatorDecisionRecordSchema, OperatorPatchEntrySchema, OperatorPatchRecordSchema, parseLedgerEntry, parseLedgerRecord, RuntimeEventEntrySchema, StripDecisionRevertedEntrySchema, type StripDecisionRevertedPayload, StripDecisionRevertedPayloadSchema, StripDecisionRevertedRecordSchema, ToolInvocationEntrySchema, ToolInvocationRecordSchema, ValidatorResultEntrySchema, ValidatorResultRecordSchema, } from './schema/entry.ts';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,gBAAgB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,2BAA2B,EAC3B,gCAAgC,EAChC,0BAA0B,GAC7B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,KAAK,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EACH,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,UAAU,EACV,KAAK,YAAY,EACjB,KAAK,YAAY,GACpB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EACH,qBAAqB,EACrB,2BAA2B,EAC3B,UAAU,EACV,mBAAmB,EACnB,gCAAgC,EAChC,sBAAsB,EACtB,KAAK,QAAQ,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EACH,KAAK,cAAc,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACtB,KAAK,cAAc,EACnB,oBAAoB,EACpB,KAAK,mBAAmB,EACxB,yBAAyB,EACzB,KAAK,qBAAqB,EAC1B,2BAA2B,EAC3B,KAAK,eAAe,EACpB,qBAAqB,EACrB,KAAK,oBAAoB,EACzB,0BAA0B,EAC1B,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,aAAa,EACb,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,YAAY,EACjB,kBAAkB,EAClB,KAAK,SAAS,EACd,eAAe,EACf,KAAK,sBAAsB,EAC3B,4BAA4B,EAC5B,2BAA2B,EAC3B,KAAK,uBAAuB,EAC5B,6BAA6B,EAC7B,4BAA4B,EAC5B,wBAAwB,EACxB,yBAAyB,EACzB,gBAAgB,EAChB,iBAAiB,EACjB,uBAAuB,EACvB,gCAAgC,EAChC,KAAK,4BAA4B,EACjC,kCAAkC,EAClC,iCAAiC,EACjC,yBAAyB,EACzB,0BAA0B,EAC1B,0BAA0B,EAC1B,2BAA2B,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,KAAK,WAAW,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,gBAAgB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,2BAA2B,EAC3B,gCAAgC,EAChC,0BAA0B,GAC7B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,KAAK,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EACH,oBAAoB,EACpB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,0BAA0B,EAC/B,KAAK,YAAY,EACjB,eAAe,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EACH,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,UAAU,EACV,KAAK,YAAY,EACjB,KAAK,YAAY,GACpB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,KAAK,mBAAmB,EAAE,sBAAsB,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AACpH,OAAO,EACH,qBAAqB,EACrB,2BAA2B,EAC3B,UAAU,EACV,mBAAmB,EACnB,gCAAgC,EAChC,sBAAsB,EACtB,KAAK,QAAQ,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EACH,KAAK,cAAc,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACtB,KAAK,cAAc,EACnB,oBAAoB,EACpB,KAAK,mBAAmB,EACxB,yBAAyB,EACzB,KAAK,qBAAqB,EAC1B,2BAA2B,EAC3B,KAAK,eAAe,EACpB,qBAAqB,EACrB,KAAK,oBAAoB,EACzB,0BAA0B,EAC1B,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,aAAa,EACb,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,YAAY,EACjB,kBAAkB,EAClB,KAAK,SAAS,EACd,eAAe,EACf,KAAK,sBAAsB,EAC3B,4BAA4B,EAC5B,2BAA2B,EAC3B,KAAK,uBAAuB,EAC5B,6BAA6B,EAC7B,4BAA4B,EAC5B,wBAAwB,EACxB,yBAAyB,EACzB,gBAAgB,EAChB,iBAAiB,EACjB,uBAAuB,EACvB,gCAAgC,EAChC,KAAK,4BAA4B,EACjC,kCAAkC,EAClC,iCAAiC,EACjC,yBAAyB,EACzB,0BAA0B,EAC1B,0BAA0B,EAC1B,2BAA2B,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,KAAK,WAAW,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC"}
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  export { buildChangeLogRecord, buildCorrectionRecord, buildOperatorDecisionRecord, buildStripDecisionRevertedRecord, buildValidatorResultRecord, } from "./builders.js";
2
2
  export { runLedgerCli } from "./cli.js";
3
3
  export { computeCoverage } from "./coverage.js";
4
+ export { DOCTOR_FINDING_CODES, runLedgerDoctor, } from "./doctor.js";
4
5
  export { openLedger, } from "./handle.js";
6
+ export { getLedgerRuntimeConfig, validateLedgerRuntimeConfig } from "./runtime-config.js";
5
7
  export { appendCleanupWaveNote, appendDecompositionWaveNote, appendNote, appendOpenIssueNote, appendSemanticCleanupSummaryNote, appendVerifiedFlowNote, } from "./note.js";
6
8
  export { deriveFilesChangedFromPatch } from "./patch-resolver.js";
7
9
  export { AgentPatchDiffSchema, AgentPatchEntrySchema, AgentPatchRecordSchema, ChangeLogEntrySchema, ChangeLogFileChangeSchema, ChangeLogParityStatusSchema, ChangeLogRecordSchema, ChangeLogSmokeResultSchema, ChangeLogSubkindSchema, CorrectionEntrySchema, CorrectionRecordSchema, EmitterSchema, LedgerEntrySchema, LedgerLinksSchema, LedgerPhaseSchema, LedgerRecordSchema, NoteEntrySchema, OperatorDecisionActionSchema, OperatorDecisionEntrySchema, OperatorDecisionPayloadSchema, OperatorDecisionRecordSchema, OperatorPatchEntrySchema, OperatorPatchRecordSchema, parseLedgerEntry, parseLedgerRecord, RuntimeEventEntrySchema, StripDecisionRevertedEntrySchema, StripDecisionRevertedPayloadSchema, StripDecisionRevertedRecordSchema, ToolInvocationEntrySchema, ToolInvocationRecordSchema, ValidatorResultEntrySchema, ValidatorResultRecordSchema, } from "./schema/entry.js";
package/dist/list.d.ts CHANGED
@@ -8,9 +8,10 @@ export type LedgerFilter = {
8
8
  readonly since?: string;
9
9
  };
10
10
  export declare const getOrderedEntryLocations: (manifest: LedgerManifest, readIndex: LedgerReadIndex, filter: LedgerFilter, direction?: "asc" | "desc") => ManifestEntryLocation[];
11
- export declare const readManifestEntryBatch: ({ allowMissing, entryLocations, workspaceRoot, }: {
11
+ export declare const readManifestEntryBatch: ({ allowMissing, entryLocations, entryReadConcurrency, workspaceRoot, }: {
12
12
  readonly allowMissing?: boolean;
13
13
  readonly entryLocations: readonly ManifestEntryLocation[];
14
+ readonly entryReadConcurrency?: number;
14
15
  readonly workspaceRoot: string;
15
16
  }) => Promise<({
16
17
  entry: {
@@ -1 +1 @@
1
- {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../src/list.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAG7B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAoB,MAAM,mBAAmB,CAAC;AAC1G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D,MAAM,MAAM,YAAY,GAAG;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAmDF,eAAO,MAAM,wBAAwB,GACjC,UAAU,cAAc,EACxB,WAAW,eAAe,EAC1B,QAAQ,YAAY,EACpB,YAAW,KAAK,GAAG,MAAc,4BAgBpC,CAAC;AAgCF,eAAO,MAAM,sBAAsB,GAAU,kDAI1C;IACC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAC1D,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAQI,CAAC;AAiDN,eAAO,MAAM,0BAA0B,GACnC,eAAe,MAAM,EACrB,UAAU,cAAc,EACxB,WAAW,eAAe,EAC1B,SAAQ,YAAiB,EACzB,YAAW,KAAK,GAAG,MAAc,KAClC,aAAa,CAAC,WAAW,CAwB3B,CAAC;AAEF,eAAO,MAAM,WAAW,GACpB,eAAe,MAAM,EACrB,SAAQ,YAAiB,KAC1B,aAAa,CAAC,WAAW,CAG3B,CAAC;AAEF,eAAO,MAAM,cAAc,GAAU,eAAe,MAAM,EAAE,SAAQ,YAAiB,KAAG,OAAO,CAAC,WAAW,EAAE,CAM5G,CAAC"}
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../src/list.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAG7B,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAoB,MAAM,mBAAmB,CAAC;AAC1G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D,MAAM,MAAM,YAAY,GAAG;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAgDF,eAAO,MAAM,wBAAwB,GACjC,UAAU,cAAc,EACxB,WAAW,eAAe,EAC1B,QAAQ,YAAY,EACpB,YAAW,KAAK,GAAG,MAAc,4BAgBpC,CAAC;AAiCF,eAAO,MAAM,sBAAsB,GAAU,wEAK1C;IACC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAC1D,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAQI,CAAC;AAsDN,eAAO,MAAM,0BAA0B,GACnC,eAAe,MAAM,EACrB,UAAU,cAAc,EACxB,WAAW,eAAe,EAC1B,SAAQ,YAAiB,EACzB,YAAW,KAAK,GAAG,MAAc,KAClC,aAAa,CAAC,WAAW,CAoC3B,CAAC;AAEF,eAAO,MAAM,WAAW,GACpB,eAAe,MAAM,EACrB,SAAQ,YAAiB,KAC1B,aAAa,CAAC,WAAW,CAG3B,CAAC;AAEF,eAAO,MAAM,cAAc,GAAU,eAAe,MAAM,EAAE,SAAQ,YAAiB,KAAG,OAAO,CAAC,WAAW,EAAE,CAM5G,CAAC"}
package/dist/list.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { mapWithConcurrencyLimit } from "./async.js";
2
2
  import { matchesReadIndexFilter, } from "./read-index.js";
3
+ import { getLedgerRuntimeConfig } from "./runtime-config.js";
3
4
  import { loadLedgerState } from "./recovery.js";
4
5
  import { parseLedgerEntry } from "./schema/entry.js";
5
6
  import { readPhaseEntryText } from "./storage/filesystem.js";
@@ -15,8 +16,6 @@ const matchesFilter = (entry, filter) => {
15
16
  }
16
17
  return true;
17
18
  };
18
- const ENTRY_READ_BATCH_SIZE = 32;
19
- const ENTRY_READ_CONCURRENCY = 16;
20
19
  const getFilteredReadIndexEntries = ({ direction, filter, manifest, readIndex, }) => {
21
20
  const sourceEntries = direction === 'asc' ? readIndex.entries : [...readIndex.entries].reverse();
22
21
  const filteredEntries = [];
@@ -71,20 +70,21 @@ const readManifestEntry = async ({ allowMissing, entryId, location, workspaceRoo
71
70
  throw error;
72
71
  }
73
72
  };
74
- export const readManifestEntryBatch = async ({ allowMissing = false, entryLocations, workspaceRoot, }) => mapWithConcurrencyLimit(entryLocations, ENTRY_READ_CONCURRENCY, async ([entryId, location]) => readManifestEntry({
73
+ export const readManifestEntryBatch = async ({ allowMissing = false, entryLocations, entryReadConcurrency = getLedgerRuntimeConfig().scanConcurrency, workspaceRoot, }) => mapWithConcurrencyLimit(entryLocations, entryReadConcurrency, async ([entryId, location]) => readManifestEntry({
75
74
  allowMissing,
76
75
  entryId,
77
76
  location,
78
77
  workspaceRoot,
79
78
  }));
80
- const collectLimitedEntries = async ({ direction, filter, manifest, readIndex, workspaceRoot, }) => {
79
+ const collectLimitedEntries = async ({ batchSize, direction, entryReadConcurrency, filter, manifest, readIndex, workspaceRoot, }) => {
81
80
  const collectedEntries = [];
82
81
  const orderedEntries = getOrderedEntryLocations(manifest, readIndex, filter, 'desc');
83
82
  const limit = filter.limit ?? 0;
84
- for (let index = 0; index < orderedEntries.length && collectedEntries.length < limit; index += ENTRY_READ_BATCH_SIZE) {
85
- const batch = orderedEntries.slice(index, index + ENTRY_READ_BATCH_SIZE);
83
+ for (let index = 0; index < orderedEntries.length && collectedEntries.length < limit; index += batchSize) {
84
+ const batch = orderedEntries.slice(index, index + batchSize);
86
85
  const resolvedEntries = await readManifestEntryBatch({
87
86
  entryLocations: batch,
87
+ entryReadConcurrency,
88
88
  workspaceRoot,
89
89
  });
90
90
  for (const resolvedEntry of resolvedEntries) {
@@ -105,17 +105,27 @@ const collectLimitedEntries = async ({ direction, filter, manifest, readIndex, w
105
105
  return collectedEntries;
106
106
  };
107
107
  export const iterateEntriesFromManifest = async function* (workspaceRoot, manifest, readIndex, filter = {}, direction = 'asc') {
108
+ const { scanBatchSize, scanConcurrency } = getLedgerRuntimeConfig();
108
109
  if (filter.limit) {
109
- for (const entry of await collectLimitedEntries({ direction, filter, manifest, readIndex, workspaceRoot })) {
110
+ for (const entry of await collectLimitedEntries({
111
+ batchSize: scanBatchSize,
112
+ direction,
113
+ entryReadConcurrency: scanConcurrency,
114
+ filter,
115
+ manifest,
116
+ readIndex,
117
+ workspaceRoot,
118
+ })) {
110
119
  yield entry;
111
120
  }
112
121
  return;
113
122
  }
114
123
  const orderedEntries = getOrderedEntryLocations(manifest, readIndex, filter, direction);
115
- for (let index = 0; index < orderedEntries.length; index += ENTRY_READ_BATCH_SIZE) {
116
- const batch = orderedEntries.slice(index, index + ENTRY_READ_BATCH_SIZE);
124
+ for (let index = 0; index < orderedEntries.length; index += scanBatchSize) {
125
+ const batch = orderedEntries.slice(index, index + scanBatchSize);
117
126
  const resolvedEntries = await readManifestEntryBatch({
118
127
  entryLocations: batch,
128
+ entryReadConcurrency: scanConcurrency,
119
129
  workspaceRoot,
120
130
  });
121
131
  for (const resolvedEntry of resolvedEntries) {
@@ -1 +1 @@
1
- {"version":3,"file":"patch-resolver.d.ts","sourceRoot":"","sources":["../src/patch-resolver.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,YAAY,EAEpB,MAAM,mBAAmB,CAAC;AAI3B,KAAK,gBAAgB,GAAG,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC,CAAC;AACvE,KAAK,mBAAmB,GAAG,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,CAAC,CAAC;AAQ7E,KAAK,wBAAwB,GAAG,IAAI,CAAC,gBAAgB,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC,GAAG;IACxF,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;CAC/B,CAAC;AACF,KAAK,2BAA2B,GAAG,IAAI,CAAC,mBAAmB,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC,GAAG;IAC9F,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,wBAAwB,GAAG,2BAA2B,CAAC;AAqKzF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,2BAA2B,GAAI,WAAW,MAAM,KAAG,mBAAmB,EAuClF,CAAC;AAmBF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACrC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;AACtC,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACrC,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAAC"}
1
+ {"version":3,"file":"patch-resolver.d.ts","sourceRoot":"","sources":["../src/patch-resolver.ts"],"names":[],"mappings":"AASA,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,YAAY,EAEpB,MAAM,mBAAmB,CAAC;AAK3B,KAAK,gBAAgB,GAAG,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC,CAAC;AACvE,KAAK,mBAAmB,GAAG,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,CAAC,CAAC;AAQ7E,KAAK,wBAAwB,GAAG,IAAI,CAAC,gBAAgB,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC,GAAG;IACxF,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;CAC/B,CAAC;AACF,KAAK,2BAA2B,GAAG,IAAI,CAAC,mBAAmB,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC,GAAG;IAC9F,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,wBAAwB,GAAG,2BAA2B,CAAC;AAsTzF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,2BAA2B,GAAI,WAAW,MAAM,KAAG,mBAAmB,EAuClF,CAAC;AAmBF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACrC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;AACtC,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACrC,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAAC"}