@opum-ai/lore 0.1.0 → 0.2.0

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 (90) hide show
  1. package/README.md +33 -22
  2. package/bin/lore.cjs +35 -7
  3. package/package.json +17 -17
  4. package/src/adapters/backlog.ts +0 -1084
  5. package/src/adapters/git.ts +0 -221
  6. package/src/cli.ts +0 -667
  7. package/src/commands/agent.ts +0 -301
  8. package/src/commands/agents.ts +0 -302
  9. package/src/commands/args.ts +0 -209
  10. package/src/commands/changed.ts +0 -70
  11. package/src/commands/check.ts +0 -1031
  12. package/src/commands/codex-bridge.ts +0 -49
  13. package/src/commands/concurrency.ts +0 -48
  14. package/src/commands/context.ts +0 -292
  15. package/src/commands/discover.ts +0 -89
  16. package/src/commands/explorer.ts +0 -253
  17. package/src/commands/export.ts +0 -93
  18. package/src/commands/fswrite.ts +0 -928
  19. package/src/commands/graph.ts +0 -291
  20. package/src/commands/help.ts +0 -151
  21. package/src/commands/impact.ts +0 -59
  22. package/src/commands/init.ts +0 -583
  23. package/src/commands/instructions.ts +0 -91
  24. package/src/commands/link.ts +0 -929
  25. package/src/commands/new.ts +0 -476
  26. package/src/commands/orphans.ts +0 -457
  27. package/src/commands/path.ts +0 -67
  28. package/src/commands/provenance.ts +0 -68
  29. package/src/commands/query.ts +0 -312
  30. package/src/commands/reconcile-shared.ts +0 -280
  31. package/src/commands/rename.ts +0 -585
  32. package/src/commands/replace.ts +0 -320
  33. package/src/commands/scaffold.ts +0 -346
  34. package/src/commands/schema.ts +0 -293
  35. package/src/commands/snapshot.ts +0 -130
  36. package/src/commands/supersede.ts +0 -400
  37. package/src/commands/sync.ts +0 -371
  38. package/src/commands/tasks.ts +0 -271
  39. package/src/commands/traversal.ts +0 -151
  40. package/src/commands/validate.ts +0 -226
  41. package/src/config.ts +0 -598
  42. package/src/core/agent-bridge.ts +0 -287
  43. package/src/core/agent-context.ts +0 -498
  44. package/src/core/agent-profile.ts +0 -447
  45. package/src/core/bundle.ts +0 -893
  46. package/src/core/check.ts +0 -853
  47. package/src/core/codex-bridge.ts +0 -100
  48. package/src/core/concept.ts +0 -597
  49. package/src/core/consumer-scaffold.ts +0 -433
  50. package/src/core/context.ts +0 -271
  51. package/src/core/explorer-contract.ts +0 -441
  52. package/src/core/explorer-qualification.ts +0 -58
  53. package/src/core/explorer.ts +0 -518
  54. package/src/core/finding.ts +0 -31
  55. package/src/core/graph.ts +0 -201
  56. package/src/core/indexes.ts +0 -436
  57. package/src/core/instructions.ts +0 -209
  58. package/src/core/ladybug-driver.ts +0 -1795
  59. package/src/core/ladybug-lifecycle.ts +0 -1178
  60. package/src/core/ladybug-native.ts +0 -95
  61. package/src/core/ladybug-source.ts +0 -667
  62. package/src/core/links.ts +0 -681
  63. package/src/core/log.ts +0 -253
  64. package/src/core/managed-block.ts +0 -540
  65. package/src/core/manifest.ts +0 -718
  66. package/src/core/order.ts +0 -13
  67. package/src/core/profile.ts +0 -1007
  68. package/src/core/projection.ts +0 -195
  69. package/src/core/query.ts +0 -542
  70. package/src/core/reconcile.ts +0 -236
  71. package/src/core/replace.ts +0 -419
  72. package/src/core/retrieval.ts +0 -213
  73. package/src/core/rewrite.ts +0 -940
  74. package/src/core/scaffold.ts +0 -255
  75. package/src/core/schema.ts +0 -366
  76. package/src/core/snapshot-runtime.ts +0 -52
  77. package/src/core/snapshot-store.ts +0 -287
  78. package/src/core/snapshot.ts +0 -711
  79. package/src/core/template.ts +0 -429
  80. package/src/core/traversal.ts +0 -487
  81. package/src/core/validate.ts +0 -517
  82. package/src/core/workspace-contract.ts +0 -473
  83. package/src/core/workspace-projection.ts +0 -365
  84. package/src/core/workspace-retrieval.ts +0 -196
  85. package/src/core/workspace-source.ts +0 -174
  86. package/src/errors.ts +0 -697
  87. package/src/meta.ts +0 -7
  88. package/src/output.ts +0 -589
  89. package/src/scripts/upstream-backlog-watch.ts +0 -288
  90. package/src/state.ts +0 -390
@@ -1,487 +0,0 @@
1
- /** Storage-neutral bounded path and impact traversal over authored projection facts. */
2
-
3
- import { LoreError } from "../errors";
4
- import type {
5
- LadybugProjectionSource,
6
- ProjectionConceptRecord,
7
- ProjectionEdgeRecord,
8
- ProjectionTaskRecord,
9
- } from "./ladybug-source";
10
- import { compareCodeUnits } from "./order";
11
- import type { WorkspaceRecordProvenance, WorkspaceResultScope } from "./workspace-contract";
12
-
13
- export type TraversalEndpointKind = "concept" | "task";
14
- export type TraversalDirection = "outbound" | "inbound" | "either";
15
-
16
- export const DEFAULT_TRAVERSAL_MAX_DEPTH = 4;
17
- export const MAX_TRAVERSAL_DEPTH = 16;
18
- export const DEFAULT_TRAVERSAL_LIMIT = 20;
19
- export const MAX_TRAVERSAL_LIMIT = 100;
20
- export const MAX_TRAVERSAL_EDGE_VISITS = 10_000;
21
-
22
- export interface RepositoryRecordProvenance {
23
- readonly repositoryScopeKey: string;
24
- readonly bundleId: string;
25
- readonly gitCommit: string | null;
26
- readonly exportDigest: string;
27
- readonly recordKind: TraversalEndpointKind;
28
- readonly recordKey: string;
29
- readonly sourceRecordKey: string;
30
- readonly sourcePath: string | null;
31
- readonly sourceId: string;
32
- }
33
-
34
- export type TraversalRecordProvenance = RepositoryRecordProvenance | WorkspaceRecordProvenance;
35
-
36
- export interface TraversalEndpointRef {
37
- readonly kind: TraversalEndpointKind;
38
- readonly id: string;
39
- }
40
-
41
- export interface TraversalEndpoint extends TraversalEndpointRef {
42
- readonly recordKey: string;
43
- readonly provenance: TraversalRecordProvenance;
44
- }
45
-
46
- export interface TraversalEdge {
47
- readonly recordKey: string;
48
- readonly from: TraversalEndpointRef;
49
- readonly to: TraversalEndpointRef | null;
50
- /** Public authored kind. Workspace manifest kinds replace the storage-level link/task kind. */
51
- readonly kind: string;
52
- readonly sourceKind: string;
53
- readonly target: string;
54
- readonly ordinal: number;
55
- readonly dangling: boolean;
56
- readonly provenance: TraversalEdgeProvenance;
57
- }
58
-
59
- export interface TraversalEdgeProvenance {
60
- readonly recordKind: "authored-edge";
61
- readonly recordKey: string;
62
- readonly sourceRecordKey: string;
63
- readonly from: TraversalRecordProvenance;
64
- readonly to: TraversalRecordProvenance | null;
65
- }
66
-
67
- export interface TraversalStep {
68
- readonly from: TraversalEndpoint;
69
- readonly to: TraversalEndpoint;
70
- readonly edge: TraversalEdge;
71
- /** How this step traversed the authored edge, whose stored orientation remains in edge.from/edge.to. */
72
- readonly direction: "outbound" | "inbound";
73
- }
74
-
75
- export interface TraversalSnapshot {
76
- readonly endpoints: ReadonlyMap<string, TraversalEndpoint>;
77
- readonly edges: readonly TraversalEdge[];
78
- readonly workspace?: WorkspaceResultScope;
79
- }
80
-
81
- export interface TraversalSourceRecords {
82
- readonly concepts: readonly ProjectionConceptRecord[];
83
- readonly tasks: readonly ProjectionTaskRecord[];
84
- readonly authoredEdges: readonly ProjectionEdgeRecord[];
85
- }
86
-
87
- export interface BuildTraversalSnapshotOptions {
88
- readonly workspace?: WorkspaceResultScope;
89
- readonly conceptProvenanceById?: ReadonlyMap<string, WorkspaceRecordProvenance>;
90
- readonly taskProvenanceById?: ReadonlyMap<string, WorkspaceRecordProvenance>;
91
- /** Restrict a full workspace source to the already validated selected repository subset. */
92
- readonly allowedIds?: ReadonlySet<string>;
93
- }
94
-
95
- export interface TraversalLimits {
96
- readonly maxDepth: number;
97
- readonly limit: number;
98
- readonly maxEdgeVisits: number;
99
- }
100
-
101
- export interface TraversalAccounting {
102
- readonly edgeVisits: number;
103
- readonly depthBoundReached: boolean;
104
- readonly truncated: boolean;
105
- readonly complete: boolean;
106
- }
107
-
108
- export interface PathOptions {
109
- readonly from: TraversalEndpointRef;
110
- readonly to: TraversalEndpointRef;
111
- readonly direction: TraversalDirection;
112
- readonly edgeKinds?: readonly string[];
113
- readonly maxDepth?: number;
114
- readonly limit?: number;
115
- }
116
-
117
- export interface TraversalPath {
118
- readonly depth: number;
119
- readonly endpoints: readonly TraversalEndpoint[];
120
- readonly edges: readonly TraversalStep[];
121
- }
122
-
123
- export interface PathResult extends TraversalAccounting {
124
- readonly schemaVersion: "lore-path-result/1";
125
- readonly from: TraversalEndpoint;
126
- readonly to: TraversalEndpoint;
127
- readonly direction: TraversalDirection;
128
- readonly edgeKinds: readonly string[];
129
- readonly limits: TraversalLimits;
130
- readonly paths: readonly TraversalPath[];
131
- readonly shown: number;
132
- readonly workspace?: WorkspaceResultScope;
133
- }
134
-
135
- export interface ImpactOptions {
136
- readonly root: TraversalEndpointRef;
137
- readonly direction: TraversalDirection;
138
- readonly edgeKinds?: readonly string[];
139
- readonly maxDepth?: number;
140
- readonly limit?: number;
141
- }
142
-
143
- export interface ImpactEntry {
144
- readonly endpoint: TraversalEndpoint;
145
- readonly depth: number;
146
- readonly relationship: "direct" | "transitive";
147
- readonly evidence: readonly TraversalStep[];
148
- }
149
-
150
- export interface ImpactResult extends TraversalAccounting {
151
- readonly schemaVersion: "lore-impact-result/1";
152
- readonly root: TraversalEndpoint;
153
- readonly direction: TraversalDirection;
154
- readonly edgeKinds: readonly string[];
155
- readonly limits: TraversalLimits;
156
- readonly impacts: readonly ImpactEntry[];
157
- readonly shown: number;
158
- readonly workspace?: WorkspaceResultScope;
159
- }
160
-
161
- interface WalkState {
162
- readonly endpointKey: string;
163
- readonly endpoints: readonly TraversalEndpoint[];
164
- readonly steps: readonly TraversalStep[];
165
- readonly visited: ReadonlySet<string>;
166
- }
167
-
168
- interface AdjacentStep {
169
- readonly endpointKey: string;
170
- readonly step: TraversalStep;
171
- }
172
-
173
- /** Build exact typed traversal facts from the already validated projection source. */
174
- export function buildTraversalSnapshot(
175
- source: LadybugProjectionSource,
176
- records: TraversalSourceRecords = source,
177
- options: BuildTraversalSnapshotOptions = {},
178
- ): TraversalSnapshot {
179
- const endpoints = new Map<string, TraversalEndpoint>();
180
- const byRecordKey = new Map<string, TraversalEndpoint>();
181
- for (const record of records.concepts) addEndpoint(record, "concept", source, options, endpoints, byRecordKey);
182
- for (const record of records.tasks) addEndpoint(record, "task", source, options, endpoints, byRecordKey);
183
-
184
- const edges: TraversalEdge[] = [];
185
- for (const record of records.authoredEdges) {
186
- const from = byRecordKey.get(record.from);
187
- const to = record.to === null ? null : byRecordKey.get(record.to);
188
- if (from === undefined || (record.to !== null && to === undefined)) {
189
- // A selected workspace subset deliberately excludes cross-boundary facts.
190
- if (options.allowedIds !== undefined) continue;
191
- throw invalidTraversal(`edge ${record.key} names an unknown endpoint`);
192
- }
193
- edges.push({
194
- recordKey: record.key,
195
- from: endpointRef(from),
196
- to: to === null || to === undefined ? null : endpointRef(to),
197
- kind: record.workspaceLinkKind ?? record.kind,
198
- sourceKind: record.kind,
199
- target: record.target,
200
- ordinal: record.ordinal,
201
- dangling: record.dangling || to === null,
202
- provenance: {
203
- recordKind: "authored-edge",
204
- recordKey: record.key,
205
- sourceRecordKey: record.workspaceSourceRecordKey ?? record.key,
206
- from: from.provenance,
207
- to: to?.provenance ?? null,
208
- },
209
- });
210
- }
211
- edges.sort(compareEdges);
212
- return {
213
- endpoints,
214
- edges,
215
- ...(options.workspace !== undefined ? { workspace: options.workspace } : {}),
216
- };
217
- }
218
-
219
- /** Return up to the requested number of shortest deterministic simple paths. */
220
- export function findPaths(snapshot: TraversalSnapshot, options: PathOptions): PathResult {
221
- const limits = normalizedLimits(options.maxDepth, options.limit);
222
- const from = requireEndpoint(snapshot, options.from);
223
- const to = requireEndpoint(snapshot, options.to);
224
- const allowedKinds = normalizedKinds(options.edgeKinds, snapshot.edges);
225
- const adjacency = buildDirectedAdjacency(snapshot, options.direction, new Set(allowedKinds));
226
- const queue: WalkState[] = [stateFor(from)];
227
- const matches: TraversalPath[] = [];
228
- let cursor = 0;
229
- let edgeVisits = 0;
230
- let visitTruncated = false;
231
- let depthBoundReached = false;
232
-
233
- while (cursor < queue.length && matches.length <= limits.limit && !visitTruncated) {
234
- const state = queue[cursor++] as WalkState;
235
- if (state.endpointKey === endpointKey(to)) {
236
- matches.push({
237
- depth: state.steps.length,
238
- endpoints: state.endpoints,
239
- edges: state.steps,
240
- });
241
- continue;
242
- }
243
- if (state.steps.length >= limits.maxDepth) {
244
- if ((adjacency.get(state.endpointKey) ?? []).some((adjacent) => !state.visited.has(adjacent.endpointKey))) {
245
- depthBoundReached = true;
246
- }
247
- continue;
248
- }
249
- for (const adjacent of adjacency.get(state.endpointKey) ?? []) {
250
- edgeVisits += 1;
251
- if (edgeVisits > limits.maxEdgeVisits) {
252
- visitTruncated = true;
253
- break;
254
- }
255
- if (state.visited.has(adjacent.endpointKey)) continue;
256
- const endpoint = snapshot.endpoints.get(adjacent.endpointKey);
257
- if (endpoint === undefined) throw invalidTraversal("adjacency names an unknown endpoint");
258
- queue.push({
259
- endpointKey: adjacent.endpointKey,
260
- endpoints: [...state.endpoints, endpoint],
261
- steps: [...state.steps, adjacent.step],
262
- visited: new Set([...state.visited, adjacent.endpointKey]),
263
- });
264
- }
265
- }
266
-
267
- const resultTruncated = matches.length > limits.limit;
268
- const paths = matches.slice(0, limits.limit);
269
- const truncated = resultTruncated || visitTruncated;
270
- return {
271
- schemaVersion: "lore-path-result/1",
272
- from,
273
- to,
274
- direction: options.direction,
275
- edgeKinds: allowedKinds,
276
- limits,
277
- paths,
278
- shown: paths.length,
279
- edgeVisits: Math.min(edgeVisits, limits.maxEdgeVisits),
280
- depthBoundReached,
281
- truncated,
282
- complete: !truncated,
283
- ...(snapshot.workspace !== undefined ? { workspace: snapshot.workspace } : {}),
284
- };
285
- }
286
-
287
- /** Expand a bounded impact frontier and retain one canonical shortest evidence chain per endpoint. */
288
- export function findImpact(snapshot: TraversalSnapshot, options: ImpactOptions): ImpactResult {
289
- const limits = normalizedLimits(options.maxDepth, options.limit);
290
- const root = requireEndpoint(snapshot, options.root);
291
- const allowedKinds = normalizedKinds(options.edgeKinds, snapshot.edges);
292
- const adjacency = buildDirectedAdjacency(snapshot, options.direction, new Set(allowedKinds));
293
- const queue: WalkState[] = [stateFor(root)];
294
- const visited = new Set<string>([endpointKey(root)]);
295
- const impacts: ImpactEntry[] = [];
296
- let cursor = 0;
297
- let edgeVisits = 0;
298
- let visitTruncated = false;
299
- let depthBoundReached = false;
300
-
301
- while (cursor < queue.length && impacts.length <= limits.limit && !visitTruncated) {
302
- const state = queue[cursor++] as WalkState;
303
- if (state.steps.length >= limits.maxDepth) {
304
- if ((adjacency.get(state.endpointKey) ?? []).some((adjacent) => !visited.has(adjacent.endpointKey))) {
305
- depthBoundReached = true;
306
- }
307
- continue;
308
- }
309
- for (const adjacent of adjacency.get(state.endpointKey) ?? []) {
310
- edgeVisits += 1;
311
- if (edgeVisits > limits.maxEdgeVisits) {
312
- visitTruncated = true;
313
- break;
314
- }
315
- if (visited.has(adjacent.endpointKey)) continue;
316
- const endpoint = snapshot.endpoints.get(adjacent.endpointKey);
317
- if (endpoint === undefined) throw invalidTraversal("adjacency names an unknown endpoint");
318
- visited.add(adjacent.endpointKey);
319
- const steps = [...state.steps, adjacent.step];
320
- impacts.push({
321
- endpoint,
322
- depth: steps.length,
323
- relationship: steps.length === 1 ? "direct" : "transitive",
324
- evidence: steps,
325
- });
326
- queue.push({
327
- endpointKey: adjacent.endpointKey,
328
- endpoints: [...state.endpoints, endpoint],
329
- steps,
330
- visited,
331
- });
332
- if (impacts.length > limits.limit) break;
333
- }
334
- }
335
-
336
- const resultTruncated = impacts.length > limits.limit;
337
- const shown = impacts.slice(0, limits.limit);
338
- const truncated = resultTruncated || visitTruncated;
339
- return {
340
- schemaVersion: "lore-impact-result/1",
341
- root,
342
- direction: options.direction,
343
- edgeKinds: allowedKinds,
344
- limits,
345
- impacts: shown,
346
- shown: shown.length,
347
- edgeVisits: Math.min(edgeVisits, limits.maxEdgeVisits),
348
- depthBoundReached,
349
- truncated,
350
- complete: !truncated,
351
- ...(snapshot.workspace !== undefined ? { workspace: snapshot.workspace } : {}),
352
- };
353
- }
354
-
355
- function addEndpoint(
356
- record: ProjectionConceptRecord | ProjectionTaskRecord,
357
- kind: TraversalEndpointKind,
358
- source: LadybugProjectionSource,
359
- options: BuildTraversalSnapshotOptions,
360
- endpoints: Map<string, TraversalEndpoint>,
361
- byRecordKey: Map<string, TraversalEndpoint>,
362
- ): void {
363
- if (options.allowedIds !== undefined && !options.allowedIds.has(record.id)) return;
364
- const workspaceProvenance =
365
- kind === "concept" ? options.conceptProvenanceById?.get(record.id) : options.taskProvenanceById?.get(record.id);
366
- const provenance: TraversalRecordProvenance = workspaceProvenance ?? repositoryProvenance(record, kind, source);
367
- const endpoint: TraversalEndpoint = {
368
- kind,
369
- id: record.id,
370
- recordKey: record.key,
371
- provenance,
372
- };
373
- const key = endpointKey(endpoint);
374
- if (endpoints.has(key) || byRecordKey.has(record.key))
375
- throw invalidTraversal(`duplicate ${kind} endpoint ${record.id}`);
376
- endpoints.set(key, endpoint);
377
- byRecordKey.set(record.key, endpoint);
378
- }
379
-
380
- function repositoryProvenance(
381
- record: ProjectionConceptRecord | ProjectionTaskRecord,
382
- kind: TraversalEndpointKind,
383
- source: LadybugProjectionSource,
384
- ): RepositoryRecordProvenance {
385
- return {
386
- repositoryScopeKey: source.repositoryScopeKey,
387
- bundleId: source.manifest.bundle.id,
388
- gitCommit: source.manifest.bundle.gitCommit,
389
- exportDigest: source.exportDigest,
390
- recordKind: kind,
391
- recordKey: record.key,
392
- sourceRecordKey: record.key,
393
- sourcePath: kind === "concept" ? (record as ProjectionConceptRecord).path : null,
394
- sourceId: record.id,
395
- };
396
- }
397
-
398
- function normalizedLimits(maxDepth = DEFAULT_TRAVERSAL_MAX_DEPTH, limit = DEFAULT_TRAVERSAL_LIMIT): TraversalLimits {
399
- if (!Number.isSafeInteger(maxDepth) || maxDepth < 0 || maxDepth > MAX_TRAVERSAL_DEPTH) {
400
- throw invalidTraversal(`maximum depth must be between 0 and ${MAX_TRAVERSAL_DEPTH}`);
401
- }
402
- if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_TRAVERSAL_LIMIT) {
403
- throw invalidTraversal(`result limit must be between 1 and ${MAX_TRAVERSAL_LIMIT}`);
404
- }
405
- return { maxDepth, limit, maxEdgeVisits: MAX_TRAVERSAL_EDGE_VISITS };
406
- }
407
-
408
- function normalizedKinds(requested: readonly string[] | undefined, edges: readonly TraversalEdge[]): string[] {
409
- const values = requested === undefined || requested.length === 0 ? edges.map((edge) => edge.kind) : requested;
410
- return [...new Set(values)].sort(compareCodeUnits);
411
- }
412
-
413
- function requireEndpoint(snapshot: TraversalSnapshot, ref: TraversalEndpointRef): TraversalEndpoint {
414
- const endpoint = snapshot.endpoints.get(endpointKey(ref));
415
- if (endpoint !== undefined) return endpoint;
416
- throw new LoreError(
417
- "not_found",
418
- `${ref.kind} "${ref.id}" is not in the selected traversal scope`,
419
- "check the typed endpoint and explicit repository selection",
420
- );
421
- }
422
-
423
- function stateFor(endpoint: TraversalEndpoint): WalkState {
424
- const key = endpointKey(endpoint);
425
- return {
426
- endpointKey: key,
427
- endpoints: [endpoint],
428
- steps: [],
429
- visited: new Set([key]),
430
- };
431
- }
432
-
433
- function buildDirectedAdjacency(
434
- snapshot: TraversalSnapshot,
435
- direction: TraversalDirection,
436
- allowedKinds: ReadonlySet<string>,
437
- ): Map<string, AdjacentStep[]> {
438
- const adjacency = new Map<string, AdjacentStep[]>();
439
- const add = (
440
- from: TraversalEndpoint,
441
- to: TraversalEndpoint,
442
- edge: TraversalEdge,
443
- stepDirection: "outbound" | "inbound",
444
- ) => {
445
- const key = endpointKey(from);
446
- const values = adjacency.get(key) ?? [];
447
- values.push({
448
- endpointKey: endpointKey(to),
449
- step: { from, to, edge, direction: stepDirection },
450
- });
451
- adjacency.set(key, values);
452
- };
453
- for (const edge of snapshot.edges) {
454
- if (edge.to === null || !allowedKinds.has(edge.kind)) continue;
455
- const from = snapshot.endpoints.get(endpointKey(edge.from));
456
- const to = snapshot.endpoints.get(endpointKey(edge.to));
457
- if (from === undefined || to === undefined)
458
- throw invalidTraversal("edge endpoint is missing from traversal snapshot");
459
- if (direction === "outbound" || direction === "either") add(from, to, edge, "outbound");
460
- if (direction === "inbound" || direction === "either") add(to, from, edge, "inbound");
461
- }
462
- return adjacency;
463
- }
464
-
465
- function endpointRef(endpoint: TraversalEndpoint): TraversalEndpointRef {
466
- return { kind: endpoint.kind, id: endpoint.id };
467
- }
468
-
469
- function endpointKey(endpoint: TraversalEndpointRef): string {
470
- return `${endpoint.kind}\0${endpoint.id}`;
471
- }
472
-
473
- function compareEdges(a: TraversalEdge, b: TraversalEdge): number {
474
- return (
475
- compareCodeUnits(a.from.kind, b.from.kind) ||
476
- compareCodeUnits(a.from.id, b.from.id) ||
477
- compareCodeUnits(a.to?.kind ?? "", b.to?.kind ?? "") ||
478
- compareCodeUnits(a.to?.id ?? "", b.to?.id ?? "") ||
479
- compareCodeUnits(a.kind, b.kind) ||
480
- a.ordinal - b.ordinal ||
481
- compareCodeUnits(a.recordKey, b.recordKey)
482
- );
483
- }
484
-
485
- function invalidTraversal(message: string): LoreError {
486
- return new LoreError("validation", `traversal projection is invalid: ${message}`);
487
- }