mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Extract reverse-lookup scan to core/relationships (one shared impl)
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { datasetUsageCounts, snippetsReferencingDataset } from './relationships';
|
||||
|
||||
/** A minimal snippet-like record — the helpers only read `datasetRefs`. */
|
||||
const snip = (id: string, datasetRefs: string[]) => ({ id, datasetRefs });
|
||||
|
||||
describe('snippetsReferencingDataset', () => {
|
||||
const snippets = [
|
||||
snip('a', ['Sales']),
|
||||
snip('b', ['SALES', 'Regions']),
|
||||
snip('c', ['Other']),
|
||||
snip('d', []),
|
||||
];
|
||||
|
||||
test('matches case-insensitively and preserves the input records', () => {
|
||||
expect(snippetsReferencingDataset(snippets, 'sales').map((s) => s.id)).toEqual(['a', 'b']);
|
||||
// The matched element is the same object that was passed in (no remapping).
|
||||
expect(snippetsReferencingDataset(snippets, 'sales')[0]).toBe(snippets[0]);
|
||||
});
|
||||
|
||||
test('returns an empty array when nothing references the name', () => {
|
||||
expect(snippetsReferencingDataset(snippets, 'Nonexistent')).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('datasetUsageCounts', () => {
|
||||
test('counts every reference, keyed by lower-cased name, in one pass', () => {
|
||||
const counts = datasetUsageCounts([
|
||||
snip('a', ['Sales']),
|
||||
snip('b', ['SALES', 'Regions']),
|
||||
snip('c', ['regions']),
|
||||
]);
|
||||
expect(counts.get('sales')).toBe(2);
|
||||
expect(counts.get('regions')).toBe(2);
|
||||
expect(counts.get('other')).toBeUndefined();
|
||||
});
|
||||
|
||||
test('is empty for a library with no references', () => {
|
||||
expect(datasetUsageCounts([snip('a', []), snip('b', [])]).size).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Snippet → dataset reverse lookup (docs/architecture/07 §4).
|
||||
*
|
||||
* The forward link is a snippet's `datasetRefs` (recomputed from its published
|
||||
* spec). The reverse — "which snippets use this dataset" — is **derived** by a
|
||||
* scan, never stored, so it can't drift. This module is that scan's single pure
|
||||
* implementation: it takes plain snippet records and a name and returns plain
|
||||
* data, so the same logic serves both the reactive UI (the Dataset Manager passes
|
||||
* its live `snippets` selection) and the non-reactive `RelationshipService` (which
|
||||
* passes a store snapshot). One definition, per arch/07 §6 ("'who references this'
|
||||
* has exactly one implementation").
|
||||
*
|
||||
* Matching is **case-insensitive**, mirroring the naming policy (`core/naming`):
|
||||
* a reference to `"sales"` and a dataset named `"Sales"` are the same link.
|
||||
*/
|
||||
|
||||
/** The minimal snippet shape the reverse lookup needs (its forward refs). */
|
||||
interface HasDatasetRefs {
|
||||
datasetRefs: readonly string[];
|
||||
}
|
||||
|
||||
/** Snippets whose `datasetRefs` include `name` (case-insensitive). */
|
||||
export function snippetsReferencingDataset<T extends HasDatasetRefs>(
|
||||
snippets: readonly T[],
|
||||
name: string,
|
||||
): T[] {
|
||||
const lower = name.toLowerCase();
|
||||
return snippets.filter((s) => s.datasetRefs.some((ref) => ref.toLowerCase() === lower));
|
||||
}
|
||||
|
||||
/**
|
||||
* Usage counts for every referenced dataset name, keyed by **lower-cased** name —
|
||||
* one pass over the library for the Dataset Manager's per-row badges (cheaper than
|
||||
* an O(snippets) `snippetsReferencingDataset` scan per dataset). Look up a dataset
|
||||
* with `counts.get(name.toLowerCase()) ?? 0`.
|
||||
*/
|
||||
export function datasetUsageCounts(snippets: readonly HasDatasetRefs[]): Map<string, number> {
|
||||
const counts = new Map<string, number>();
|
||||
for (const s of snippets) {
|
||||
for (const ref of s.datasetRefs) {
|
||||
const key = ref.toLowerCase();
|
||||
counts.set(key, (counts.get(key) ?? 0) + 1);
|
||||
}
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
Reference in New Issue
Block a user