From f815fbab756537e86e916845f0e320de5e279f9a Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 12 Nov 2026 23:09:02 +0000 Subject: [PATCH 1/3] feat(node): find definitions and plugins in the config root's .sctx/ folder Uses structured-context's resourceDir, so map-views/ and plugins/ beside a config keep working with its deprecation warning, and the global config's layout is unchanged. The config dir defaults to resolveConfigPath().root, which is right for .sctx/config.json too. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019XphpeYqF5Au42ALp2Cw5m --- AGENTS.md | 2 +- README.md | 2 +- docs/definitions.md | 4 +- packages/map-views/src/node/cli.ts | 3 +- packages/map-views/src/node/index.ts | 21 +++++----- .../map-views/tests/load-definitions.test.ts | 40 +++++++++++++------ 6 files changed, 45 insertions(+), 27 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 938e128..32a4382 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,7 +9,7 @@ Declarative map views of structured-context spaces: the definition format, the r - `packages/map-views/src/layouts`: React components and the SVG edge overlay. Don't import host code from here. - `packages/map-views/src/node`: Node-only definition discovery (`loadMapViewDefinitions`), shipped as its own entry point (`structured-context-map-views/node`). Core and layouts must not import it. - `apps/navigator`: the standalone host (private package: a Vite app plus dev-server middleware calling the structured-context API). It consumes the library like any other host. -- `packages/map-views/map-views/*.json5`: definitions bundled with the library, only for structured-context's bundled schemas (`$id` `sctx-map-view://core/`). `bun run generate:definitions` embeds them as `src/node/bundled-definitions.ts`; a test fails if that is stale. Plugins ship their own; personal ones go in a `map-views/` folder next to the structured-context config. +- `packages/map-views/map-views/*.json5`: definitions bundled with the library, only for structured-context's bundled schemas (`$id` `sctx-map-view://core/`). `bun run generate:definitions` embeds them as `src/node/bundled-definitions.ts`; a test fails if that is stale. Plugins ship their own; personal ones go in the config root's `.sctx/map-views/` (structured-context's `resourceDir`). - `packages/map-views/src/core/format/*.ts`: the definition format's JSON Schemas (`as const`), the source of truth. Types derive from them (`FromSchema`); `bun run generate:schemas` writes `schemas/generated/latest/` at the repo root (so the published URLs stay stable), and a test fails if those are stale; `--release` (run when cutting a release) also freezes `schemas/generated//` if the format changed. `$id`s are the published https URLs (`schema-url.ts`). Use absolute `${SCHEMA_URL}/…` refs: `FromSchema` doesn't resolve relative refs inside referenced schemas. - `samples/launch-playbook/`: a public sample space. `packages/map-views/tests/fixtures/launch-playbook.json` is captured from it with `bun run capture-fixtures launch-playbook=../../samples/launch-playbook/.sctx.json` in `packages/map-views`. diff --git a/README.md b/README.md index f13aff5..46f3393 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Declarative visualisations of [structured-context](https://github.com/mindsocket/structured-context) spaces. -A **map view** is a short JSON5 definition that projects every space of one schema into a visual form: a tree, nested containers, or an XY plot. The same space can be shown several ways, and readers can switch settings such as orientation or grouping through parameters the definition declares. Definitions are found the way structured-context finds plugin schemas: bundled here, in plugins, or in a `map-views/` folder next to your config. +A **map view** is a short JSON5 definition that projects every space of one schema into a visual form: a tree, nested containers, or an XY plot. The same space can be shown several ways, and readers can switch settings such as orientation or grouping through parameters the definition declares. Definitions are found the way structured-context finds plugin schemas: bundled here, in plugins, or in the `.sctx/map-views/` folder beside your content. ```json5 { diff --git a/docs/definitions.md b/docs/definitions.md index 69ee622..24aae2e 100644 --- a/docs/definitions.md +++ b/docs/definitions.md @@ -124,8 +124,8 @@ Definitions are found the way structured-context finds plugin schemas, and each |---|---| | Bundled with this library (`map-views/`), for structured-context's bundled schemas | `sctx-map-view://core/` | | A plugin: the folder named by `"sctx": { "mapViews": "…" }` in its `package.json`, else its `map-views/` folder | `sctx-map-view:///` (the plugin's name without `sctx-`) | -| A `map-views/` folder next to a structured-context config file (including files pulled in by `includeSpacesFrom`) | `sctx-map-view://` | +| The `.sctx/map-views/` folder of a structured-context config root, or `map-views/` beside the global config ([resources folder](https://github.com/mindsocket/structured-context/blob/main/docs/config.md#resources-folder)), including configs pulled in by `includeSpacesFrom` | `sctx-map-view://` | -Plugins are those in a config folder's `plugins/`, plus those a space or the config names (in `plugins`, or as the prefix of `schema`). Two definitions with the same `$id` are an error: to change a definition, write another with its own `$id`. +Plugins are those in a config root's `.sctx/plugins/`, plus those a space or the config names (in `plugins`, or as the prefix of `schema`). Two definitions with the same `$id` are an error: to change a definition, write another with its own `$id`. A host lists the definitions whose `schema` matches the space's schema `$id` (`mapViewsForSchema`). diff --git a/packages/map-views/src/node/cli.ts b/packages/map-views/src/node/cli.ts index 279f58c..5b01b20 100644 --- a/packages/map-views/src/node/cli.ts +++ b/packages/map-views/src/node/cli.ts @@ -1,5 +1,4 @@ #!/usr/bin/env bun -import { dirname, resolve } from 'node:path'; import { Command } from 'commander'; import { loadConfig, resolveConfigPath, SctxError, setConfigPath } from 'structured-context/api'; import packageJson from '../../package.json' with { type: 'json' }; @@ -29,7 +28,7 @@ program setConfigPath(program.opts().config); try { const config = loadConfig(); - const configDir = dirname(resolve(resolveConfigPath().path)); + const configDir = resolveConfigPath().root; const result = await exportSnapshot(spaceName, config, configDir, options); process.stdout.write(`${JSON.stringify(result)}\n`); } catch (err) { diff --git a/packages/map-views/src/node/index.ts b/packages/map-views/src/node/index.ts index 7763a5a..41f4715 100644 --- a/packages/map-views/src/node/index.ts +++ b/packages/map-views/src/node/index.ts @@ -1,7 +1,7 @@ /// import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'; -import { dirname, join, resolve } from 'node:path'; -import { type Config, PLUGIN_PREFIX, resolveConfigPath, resolvePluginDir } from 'structured-context/api'; +import { join, resolve } from 'node:path'; +import { type Config, PLUGIN_PREFIX, resolveConfigPath, resolvePluginDir, resourceDir } from 'structured-context/api'; import { type MapViewDefinition, parseMapViewDefinition } from '../core/definition'; import { BUNDLED_DEFINITIONS } from './bundled-definitions'; @@ -18,7 +18,7 @@ const PACKAGE_NAME = 'structured-context-map-views'; type Source = { kind: 'core' } | { kind: 'plugin'; plugin: string } | { kind: 'config' }; export type LoadMapViewDefinitionsOptions = { - /** Directory of the main config file. Defaults to where structured-context finds it. */ + /** Root of the main config file (`resolveConfigPath().root`). Defaults to where structured-context finds it. */ configDir?: string; }; @@ -27,7 +27,7 @@ export type LoadMapViewDefinitionsOptions = { * - this package's embedded definitions (`sctx-map-view://core/`), for structured-context's bundled schemas; * - each plugin found from a config folder or named by a space (`sctx-map-view:///`): the * folder named by `"sctx": { "mapViews": "…" }` in its package.json, else its `map-views/` folder; - * - a `map-views/` folder next to each config file (`sctx-map-view://`). + * - the `map-views` resources folder of each config root (`sctx-map-view://`). * * Each definition's `$id` must be in the namespace of where it was found, and ids must be unique. * Throws on the first invalid definition. @@ -36,7 +36,7 @@ export function loadMapViewDefinitions( config: Config, options: LoadMapViewDefinitionsOptions = {}, ): MapViewDefinition[] { - const configDir = options.configDir ?? dirname(resolve(resolveConfigPath().path)); + const configDir = options.configDir ?? resolveConfigPath().root; const configDirs = [...new Set([configDir, ...config.spaces.flatMap((s) => (s.sourceDir ? [s.sourceDir] : []))])]; const folders: { dir: string; source: Source }[] = [ @@ -44,7 +44,10 @@ export function loadMapViewDefinitions( const definitions = pluginDefinitionsDir(dir); return definitions ? [{ dir: definitions, source: { kind: 'plugin', plugin } as const }] : []; }), - ...configDirs.map((dir) => ({ dir: join(dir, DEFINITIONS_DIR), source: { kind: 'config' } as const })), + ...configDirs.flatMap((dir) => { + const definitions = resourceDir(dir, DEFINITIONS_DIR); + return definitions ? [{ dir: definitions, source: { kind: 'config' } as const }] : []; + }), ]; const foundAt = new Map(); @@ -72,7 +75,7 @@ export function loadMapViewDefinitions( return definitions; } -/** Plugin short name → root directory, for plugins in a config folder's `plugins/` or named by the config. */ +/** Plugin short name → root directory, for plugins in a config root's plugins folder or named by the config. */ function pluginDirs(config: Config, configDir: string, configDirs: string[]): [string, string][] { const found = new Map(); const add = (name: string, fromDir: string) => { @@ -83,8 +86,8 @@ function pluginDirs(config: Config, configDir: string, configDirs: string[]): [s }; for (const dir of configDirs) { - const plugins = join(dir, CONFIG_PLUGINS_DIR); - if (!isDirectory(plugins)) continue; + const plugins = resourceDir(dir, CONFIG_PLUGINS_DIR); + if (!plugins) continue; for (const entry of readdirSync(plugins) .filter((e) => e.startsWith(PLUGIN_PREFIX)) .sort()) diff --git a/packages/map-views/tests/load-definitions.test.ts b/packages/map-views/tests/load-definitions.test.ts index ae5a5e2..56f6297 100644 --- a/packages/map-views/tests/load-definitions.test.ts +++ b/packages/map-views/tests/load-definitions.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, test } from 'bun:test'; +import { afterEach, beforeEach, describe, expect, spyOn, test } from 'bun:test'; import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { dirname, join } from 'node:path'; @@ -23,10 +23,10 @@ const ids = (config: Config, configDir = join(root, 'config')) => describe('loadMapViewDefinitions', () => { test('finds bundled, plugin and config folder definitions', () => { - write('config/plugins/sctx-conventional/map-views/a.json5', definition('sctx-map-view://conventional/a')); - write('config/plugins/sctx-declared/package.json', JSON.stringify({ sctx: { mapViews: 'views' } })); - write('config/plugins/sctx-declared/views/b.json5', definition('sctx-map-view://declared/b')); - write('config/map-views/c.json5', definition('sctx-map-view://c')); + write('config/.sctx/plugins/sctx-conventional/map-views/a.json5', definition('sctx-map-view://conventional/a')); + write('config/.sctx/plugins/sctx-declared/package.json', JSON.stringify({ sctx: { mapViews: 'views' } })); + write('config/.sctx/plugins/sctx-declared/views/b.json5', definition('sctx-map-view://declared/b')); + write('config/.sctx/map-views/c.json5', definition('sctx-map-view://c')); expect(ids(noSpaces)).toEqual([ 'sctx-map-view://core/strategy-tree', 'sctx-map-view://conventional/a', @@ -37,7 +37,7 @@ describe('loadMapViewDefinitions', () => { test('includes plugins named by a space and the folders of included configs', () => { write('included/node_modules/sctx-named/map-views/a.json5', definition('sctx-map-view://named/a')); - write('included/map-views/b.json5', definition('sctx-map-view://b')); + write('included/.sctx/map-views/b.json5', definition('sctx-map-view://b')); const config: Config = { spaces: [ { @@ -52,18 +52,34 @@ describe('loadMapViewDefinitions', () => { }); test.each([ - ['config/map-views/a.json5', 'sctx-map-view://core/a', /Use the form "sctx-map-view:\/\/"/], - ['config/plugins/sctx-p/map-views/a.json5', 'sctx-map-view://a', /Use the form "sctx-map-view:\/\/p\/"/], - ['config/plugins/sctx-p/map-views/a.json5', 'sctx-map-view://q/a', /Use the form "sctx-map-view:\/\/p\/"/], - ['config/plugins/sctx-core/map-views/a.json5', 'sctx-map-view://core/a', /namespace violation/], + ['config/.sctx/map-views/a.json5', 'sctx-map-view://core/a', /Use the form "sctx-map-view:\/\/"/], + [ + 'config/.sctx/plugins/sctx-p/map-views/a.json5', + 'sctx-map-view://a', + /Use the form "sctx-map-view:\/\/p\/"/, + ], + [ + 'config/.sctx/plugins/sctx-p/map-views/a.json5', + 'sctx-map-view://q/a', + /Use the form "sctx-map-view:\/\/p\/"/, + ], + ['config/.sctx/plugins/sctx-core/map-views/a.json5', 'sctx-map-view://core/a', /namespace violation/], ])('rejects %s with $id %s', (path, id, message) => { write(path, definition(id)); expect(() => ids(noSpaces)).toThrow(message); }); + test('still finds plugins/ and map-views/ beside a config, the old layout', () => { + write('config/plugins/sctx-old/map-views/a.json5', definition('sctx-map-view://old/a')); + write('config/map-views/b.json5', definition('sctx-map-view://b')); + const warn = spyOn(console, 'warn').mockImplementation(() => {}); + expect(ids(noSpaces)).toEqual(['sctx-map-view://core/strategy-tree', 'sctx-map-view://old/a', 'sctx-map-view://b']); + warn.mockRestore(); + }); + test('rejects two definitions with the same id', () => { - write('config/map-views/a.json5', definition('sctx-map-view://a')); - write('included/map-views/a.json5', definition('sctx-map-view://a')); + write('config/.sctx/map-views/a.json5', definition('sctx-map-view://a')); + write('included/.sctx/map-views/a.json5', definition('sctx-map-view://a')); const config: Config = { spaces: [{ name: 's', path: '.', sourceDir: join(root, 'included') }] }; expect(() => ids(config)).toThrow(/"sctx-map-view:\/\/a" is defined by both/); }); From 54d266bbf2e75584ba2f9fd6840bf42f9d139ac8 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 13 Nov 2026 03:05:10 +0000 Subject: [PATCH 2/3] test: drop the old-layout case; core no longer supports it Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019XphpeYqF5Au42ALp2Cw5m --- packages/map-views/tests/load-definitions.test.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/map-views/tests/load-definitions.test.ts b/packages/map-views/tests/load-definitions.test.ts index 56f6297..3e6a279 100644 --- a/packages/map-views/tests/load-definitions.test.ts +++ b/packages/map-views/tests/load-definitions.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, spyOn, test } from 'bun:test'; +import { afterEach, beforeEach, describe, expect, test } from 'bun:test'; import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { dirname, join } from 'node:path'; @@ -69,14 +69,6 @@ describe('loadMapViewDefinitions', () => { expect(() => ids(noSpaces)).toThrow(message); }); - test('still finds plugins/ and map-views/ beside a config, the old layout', () => { - write('config/plugins/sctx-old/map-views/a.json5', definition('sctx-map-view://old/a')); - write('config/map-views/b.json5', definition('sctx-map-view://b')); - const warn = spyOn(console, 'warn').mockImplementation(() => {}); - expect(ids(noSpaces)).toEqual(['sctx-map-view://core/strategy-tree', 'sctx-map-view://old/a', 'sctx-map-view://b']); - warn.mockRestore(); - }); - test('rejects two definitions with the same id', () => { write('config/.sctx/map-views/a.json5', definition('sctx-map-view://a')); write('included/.sctx/map-views/a.json5', definition('sctx-map-view://a')); From d6b4c15dc4f1de023d8687f2c3005ca055c82757 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 14 Nov 2026 05:47:31 +0000 Subject: [PATCH 3/3] chore: depend on structured-context ^0.24.0; local test builders Core no longer ships structured-context/testing. Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_019XphpeYqF5Au42ALp2Cw5m --- apps/navigator/package.json | 2 +- bun.lock | 10 ++-- packages/map-views/package.json | 4 +- packages/map-views/tests/annotations.test.ts | 2 +- packages/map-views/tests/builders.ts | 58 ++++++++++++++++++++ packages/map-views/tests/tree.test.ts | 2 +- 6 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 packages/map-views/tests/builders.ts diff --git a/apps/navigator/package.json b/apps/navigator/package.json index 0d3d6e6..a6a82ee 100644 --- a/apps/navigator/package.json +++ b/apps/navigator/package.json @@ -12,7 +12,7 @@ "dependencies": { "react": "^19.3.0", "react-dom": "^19.3.0", - "structured-context": "^0.23.0", + "structured-context": "^0.24.0", "structured-context-map-views": "workspace:*" }, "devDependencies": { diff --git a/bun.lock b/bun.lock index bdf9881..4a1bae7 100644 --- a/bun.lock +++ b/bun.lock @@ -17,7 +17,7 @@ "dependencies": { "react": "^19.3.0", "react-dom": "^19.3.0", - "structured-context": "^0.23.0", + "structured-context": "^0.24.0", "structured-context-map-views": "workspace:*", }, "devDependencies": { @@ -32,7 +32,7 @@ }, "packages/map-views": { "name": "structured-context-map-views", - "version": "0.5.0", + "version": "0.6.0", "bin": { "structured-context-map-views": "dist/cli.js", }, @@ -50,14 +50,14 @@ "@vitejs/plugin-react": "^6.1.1", "react": "^19.3.0", "react-dom": "^19.3.0", - "structured-context": "^0.23.0", + "structured-context": "^0.24.0", "tailwindcss": "^4.3.3", "vite": "^8.3.2", }, "peerDependencies": { "react": "^19.3.0", "react-dom": "^19.3.0", - "structured-context": "^0.23.0", + "structured-context": "^0.24.0", }, }, }, @@ -518,7 +518,7 @@ "strip-bom-string": ["strip-bom-string@1.0.0", "", {}, "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g=="], - "structured-context": ["structured-context@0.23.0", "", { "dependencies": { "ajv": "^8.20.0", "chokidar": "^5.0.0", "commander": "^15.0.0", "fast-glob": "^3.3.3", "gray-matter": "^4.0.3", "js-yaml": "^5.4.3", "json-schema-to-ts": "^3.1.1", "json5": "^2.2.3", "jsonata": "^2.2.1", "mdast-util-to-string": "^4.0.0", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "unified": "^11.0.5", "unist-util-visit": "^5.1.0" }, "peerDependencies": { "typescript": "^5" }, "bin": { "sctx": "dist/index.js" } }, "sha512-KWriaOPuyYW2kDoYEjDYvyHm7s1bUQqQ6QT8c2fRUDRbKzS65mFJhc3o9e7TpdFLwXi6VM3NFZEKszww9ZoYPQ=="], + "structured-context": ["structured-context@0.24.0", "", { "dependencies": { "ajv": "^8.20.0", "chokidar": "^5.0.0", "commander": "^15.0.0", "fast-glob": "^3.3.3", "gray-matter": "^4.0.3", "js-yaml": "^5.4.3", "json-schema-to-ts": "^3.1.1", "json5": "^2.2.3", "jsonata": "^2.2.1", "mdast-util-to-string": "^4.0.0", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "unified": "^11.0.5", "unist-util-visit": "^5.1.0" }, "peerDependencies": { "typescript": "^5" }, "bin": { "sctx": "dist/index.js" } }, "sha512-Bo+XOIwIgkaQyOeWugRKNvdoe/O6LH/hartIHU7Ifq0BaUNIi4Tuq1pnYnaZELtFjcAnQ9pAh/wBOzfIw5OCnw=="], "structured-context-map-views": ["structured-context-map-views@workspace:packages/map-views"], diff --git a/packages/map-views/package.json b/packages/map-views/package.json index 66054ad..d16e087 100644 --- a/packages/map-views/package.json +++ b/packages/map-views/package.json @@ -52,7 +52,7 @@ "peerDependencies": { "react": "^19.3.0", "react-dom": "^19.3.0", - "structured-context": "^0.23.0" + "structured-context": "^0.24.0" }, "dependencies": { "ajv": "^8.20.0", @@ -68,7 +68,7 @@ "@vitejs/plugin-react": "^6.1.1", "react": "^19.3.0", "react-dom": "^19.3.0", - "structured-context": "^0.23.0", + "structured-context": "^0.24.0", "tailwindcss": "^4.3.3", "vite": "^8.3.2" } diff --git a/packages/map-views/tests/annotations.test.ts b/packages/map-views/tests/annotations.test.ts index 35b61e4..20f1e9e 100644 --- a/packages/map-views/tests/annotations.test.ts +++ b/packages/map-views/tests/annotations.test.ts @@ -1,8 +1,8 @@ import { describe, expect, test } from 'bun:test'; import { buildSpaceGraph } from 'structured-context/api'; -import { makeSpaceNode } from 'structured-context/testing'; import { VALIDATION_ISSUE_KINDS } from 'structured-context/types'; import { annotationsFrom, filterAnnotations, type NodeAnnotation, worstSeverity } from '../src/core/annotations'; +import { makeSpaceNode } from './builders'; const graph = buildSpaceGraph([makeSpaceNode('A', 'Thing'), makeSpaceNode('B', 'Thing')], undefined); const idOf = (title: string) => [...graph.nodes.values()].find((n) => n.title === title)!.id; diff --git a/packages/map-views/tests/builders.ts b/packages/map-views/tests/builders.ts new file mode 100644 index 0000000..1cafa1c --- /dev/null +++ b/packages/map-views/tests/builders.ts @@ -0,0 +1,58 @@ +import type { + BaseNode, + Hierarchy, + HierarchyLevel, + NodeId, + ResolvedParentRef, + SpaceNode, +} from 'structured-context/types'; + +export const makeParentRef = (id: string, overrides: Partial = {}): ResolvedParentRef => { + const kind = overrides.kind ?? 'hierarchy'; + return { + id: id as NodeId, + field: 'parent', + kind, + ...(kind === 'hierarchy' ? { hierarchy: 'main' } : {}), + selfRef: false, + fieldOn: 'child', + ...overrides, + }; +}; + +export const makeLevel = (type: string, overrides: Partial = {}): HierarchyLevel => ({ + type, + field: 'parent', + fieldOn: 'child', + multiple: false, + selfRef: false, + ...overrides, +}); + +export const makeHierarchy = (levels: HierarchyLevel[], overrides: Partial = {}): Hierarchy => ({ + name: 'main', + levels, + ...overrides, +}); + +const makeNode = (title: string, type: string, extra: Record, linkTargets?: string[]): BaseNode => ({ + id: title as NodeId, + title, + schemaData: { title, type, ...extra }, + linkTargets: linkTargets ?? [title], + type, +}); + +export const makeSpaceNode = ( + title: string, + type: string, + extra: Record = {}, + linkTargets?: string[], + resolvedParents: ResolvedParentRef[] = [], + resolvedLinks: SpaceNode['resolvedLinks'] = [], +): SpaceNode => ({ + ...makeNode(title, type, extra, linkTargets), + resolvedType: type, + resolvedParents, + resolvedLinks, +}); diff --git a/packages/map-views/tests/tree.test.ts b/packages/map-views/tests/tree.test.ts index 6453c0c..4371f32 100644 --- a/packages/map-views/tests/tree.test.ts +++ b/packages/map-views/tests/tree.test.ts @@ -1,7 +1,7 @@ import { describe, expect, test } from 'bun:test'; import { buildSpaceGraph } from 'structured-context/api'; -import { makeHierarchy, makeLevel, makeParentRef, makeSpaceNode } from 'structured-context/testing'; import { buildTree, type TreeOccurrence, treeLinks } from '../src/core/tree'; +import { makeHierarchy, makeLevel, makeParentRef, makeSpaceNode } from './builders'; import { fixtureGraph } from './fixture-graph'; import sample from './fixtures/launch-playbook.json';