@north-light/crouter 0.3.227 → 0.3.228
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/api/client.d.ts +5 -5
- package/dist/api/client.js +3 -3
- package/dist/api/dto/broker-ops.d.ts +8 -0
- package/dist/api/dto/memory.d.ts +8 -7
- package/dist/api/dto/memory.js +2 -2
- package/dist/builtin-memory/05-kinds/design/00-base.md +3 -4
- package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +4 -3
- package/dist/builtin-memory/05-kinds/design/design-contract.md +3 -6
- package/dist/builtin-memory/05-kinds/plan/00-base.md +3 -4
- package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +4 -5
- package/dist/builtin-memory/05-kinds/plan/plan-contract.md +5 -13
- package/dist/builtin-memory/design/guide.md +38 -16
- package/dist/builtin-memory/design/roadmap.md +4 -4
- package/dist/builtin-memory/insights/init.md +5 -5
- package/dist/builtin-memory/internal/INDEX.md +1 -1
- package/dist/builtin-memory/internal/examples/INDEX.md +1 -1
- package/dist/builtin-memory/internal/memory-loading.md +6 -6
- package/dist/builtin-memory/internal/plugins.md +1 -1
- package/dist/builtin-memory/plan/guide.md +53 -0
- package/dist/builtin-memory/plan/roadmap.md +10 -8
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +50 -37
- package/dist/clients/attach/__tests__/pane-tag-successor.test.js +56 -0
- package/dist/clients/attach/__tests__/profile-files.test.js +21 -0
- package/dist/clients/attach/chrome/inbox-strip.js +11 -7
- package/dist/clients/attach/session/pane-tag.d.ts +28 -2
- package/dist/clients/attach/session/pane-tag.js +48 -22
- package/dist/clients/attach/session/profile-files.d.ts +6 -0
- package/dist/clients/attach/session/profile-files.js +44 -11
- package/dist/clients/attach/slash/dispatch.js +4 -7
- package/dist/clients/attach/viewer.js +588 -596
- package/dist/commands/__tests__/surface-reload-target.test.js +21 -0
- package/dist/commands/api-client.d.ts +3 -7
- package/dist/commands/api-client.js +7 -12
- package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +292 -0
- package/dist/commands/memory/__tests__/repository-root-lint.test.js +146 -0
- package/dist/commands/memory/delete.js +51 -24
- package/dist/commands/memory/edit.js +50 -10
- package/dist/commands/memory/find.js +105 -56
- package/dist/commands/memory/history.js +51 -31
- package/dist/commands/memory/lint.d.ts +0 -9
- package/dist/commands/memory/lint.js +78 -322
- package/dist/commands/memory/list.d.ts +12 -4
- package/dist/commands/memory/list.js +64 -25
- package/dist/commands/memory/move.js +103 -73
- package/dist/commands/memory/origin.js +35 -5
- package/dist/commands/memory/read.d.ts +4 -0
- package/dist/commands/memory/read.js +132 -80
- package/dist/commands/memory/shared.d.ts +115 -25
- package/dist/commands/memory/shared.js +331 -74
- package/dist/commands/memory/write.js +72 -21
- package/dist/commands/memory.js +2 -2
- package/dist/commands/node/lifecycle.js +2 -2
- package/dist/commands/pkg/market-manage.js +25 -24
- package/dist/commands/pkg/plugin-manage.d.ts +22 -6
- package/dist/commands/pkg/plugin-manage.js +118 -25
- package/dist/commands/pkg/shared.d.ts +8 -0
- package/dist/commands/pkg/shared.js +23 -20
- package/dist/commands/surface-reload.d.ts +5 -0
- package/dist/commands/surface-reload.js +9 -1
- package/dist/commands/sys/__tests__/migrate.test.js +1140 -22
- package/dist/commands/sys/__tests__/sync-project-guidance.test.js +218 -0
- package/dist/commands/sys/migrate.js +126 -140
- package/dist/commands/sys/panels/profiles-panel.d.ts +66 -0
- package/dist/commands/sys/panels/profiles-panel.js +599 -0
- package/dist/commands/sys/settings-shell.d.ts +4 -2
- package/dist/commands/sys/settings-shell.js +35 -4
- package/dist/commands/sys/settings.js +21 -5
- package/dist/commands/sys/sync-deps.d.ts +2 -0
- package/dist/commands/sys/sync-deps.js +26 -15
- package/dist/commands/sys/sync-project-guidance.js +228 -145
- package/dist/commands/sys/sync-skills.js +28 -17
- package/dist/commands/sys/update.js +11 -3
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +15 -2
- package/dist/core/__tests__/config-change-delta.test.js +106 -0
- package/dist/core/__tests__/context-intro.test.js +13 -9
- package/dist/core/__tests__/daemon-boot.test.js +22 -16
- package/dist/core/__tests__/fixtures/memory-slash-live-probe.js +5 -1
- package/dist/core/__tests__/human-deliver.test.js +19 -0
- package/dist/core/__tests__/inline-memory-refs.test.js +50 -46
- package/dist/core/__tests__/{serial → integration}/broker-fork-seam.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/broker-sdk-wiring.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/broker-snapshot-history.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/command-plugins.test.js +94 -3
- package/dist/core/__tests__/{serial → integration}/deferred-no-wake.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/flagship-lifecycle.test.js +13 -27
- package/dist/core/__tests__/{serial → integration}/host-teardown-process-group.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/human-deliver-e2e.test.js +1 -1
- package/dist/core/__tests__/{live-mutation-verbs.test.js → integration/live-mutation-verbs.test.js} +22 -33
- package/dist/core/__tests__/{serial → integration}/live-mutation.test.js +21 -41
- package/dist/core/__tests__/integration/refresh-stall-recycle.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/refresh-stall-recycle.test.js +1 -1
- package/dist/core/__tests__/integration/revive.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/revive.test.js +38 -19
- package/dist/core/__tests__/integration/spawn-root.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/spawn-root.test.js +104 -1
- package/dist/core/__tests__/integration/subscription-delivery.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/subscription-delivery.test.js +1 -1
- package/dist/core/__tests__/integration/tmux-surface.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/tmux-surface.test.js +1 -1
- package/dist/core/__tests__/integration/worktree-land.test.d.ts +1 -0
- package/dist/core/__tests__/integration/worktree-land.test.js +400 -0
- package/dist/core/__tests__/integration/worktree-reap.test.d.ts +1 -0
- package/dist/core/__tests__/{serial/worktree.test.js → integration/worktree-reap.test.js} +6 -338
- package/dist/core/__tests__/kickoff.test.js +16 -5
- package/dist/core/__tests__/memory-resolver-precedence.test.js +122 -91
- package/dist/core/__tests__/nested-store-discovery.test.js +5 -3
- package/dist/core/__tests__/on-read-crouter-home-fence.test.js +2 -1
- package/dist/core/__tests__/on-read-dedup-resume.test.js +39 -27
- package/dist/core/__tests__/on-read-nested-store.test.js +19 -13
- package/dist/core/__tests__/profile-project-memory-delivery.test.js +138 -44
- package/dist/core/__tests__/repository-association.test.d.ts +1 -0
- package/dist/core/__tests__/repository-association.test.js +153 -0
- package/dist/core/__tests__/repository-root-identity.test.d.ts +1 -0
- package/dist/core/__tests__/repository-root-identity.test.js +219 -0
- package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.js +27 -11
- package/dist/core/__tests__/warm-claim-preference-snapshot.test.d.ts +1 -0
- package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +40 -0
- package/dist/core/canvas/canvas.d.ts +3 -0
- package/dist/core/canvas/canvas.js +30 -10
- package/dist/core/canvas/db.js +28 -1
- package/dist/core/canvas/paths.d.ts +4 -6
- package/dist/core/canvas/paths.js +8 -6
- package/dist/core/canvas/render-source.js +2 -2
- package/dist/core/canvas/types.d.ts +9 -13
- package/dist/core/exclusive-lock.d.ts +2 -0
- package/dist/core/exclusive-lock.js +21 -0
- package/dist/core/git.d.ts +0 -1
- package/dist/core/git.js +0 -3
- package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +1 -0
- package/dist/core/human/feedback-companion.js +3 -0
- package/dist/core/human/scan.js +8 -1
- package/dist/core/keybindings/catalog.d.ts +2 -2
- package/dist/core/keybindings/catalog.js +2 -1
- package/dist/core/memory/doc-link-grammar.js +2 -2
- package/dist/core/memory/history.d.ts +24 -0
- package/dist/core/memory/history.js +66 -1
- package/dist/core/memory/identity.d.ts +65 -0
- package/dist/core/memory/identity.js +185 -0
- package/dist/core/memory/inline-ref-guidance.d.ts +1 -1
- package/dist/core/memory/inline-ref-guidance.js +1 -1
- package/dist/core/memory/inline-ref-inventory.d.ts +3 -10
- package/dist/core/memory/inline-ref-inventory.js +30 -62
- package/dist/core/memory/lint.d.ts +129 -0
- package/dist/core/memory/lint.js +517 -0
- package/dist/core/memory/project-namespace.d.ts +31 -0
- package/dist/core/memory/project-namespace.js +85 -0
- package/dist/core/memory/repository-association.d.ts +31 -0
- package/dist/core/memory/repository-association.js +129 -0
- package/dist/core/memory/tree.d.ts +39 -0
- package/dist/core/memory/tree.js +93 -0
- package/dist/core/memory-resolver.d.ts +193 -90
- package/dist/core/memory-resolver.js +461 -375
- package/dist/core/nested-stores.js +6 -13
- package/dist/core/profiles/select.d.ts +4 -1
- package/dist/core/profiles/select.js +108 -48
- package/dist/core/review/__tests__/capture-origin.test.js +2 -2
- package/dist/core/review/__tests__/stage-identity.test.js +2 -2
- package/dist/core/review/companion.js +11 -2
- package/dist/core/runtime/bearings.d.ts +2 -2
- package/dist/core/runtime/bearings.js +3 -3
- package/dist/core/runtime/broker/daemon-ops.d.ts +2 -2
- package/dist/core/runtime/broker/rebind.js +5 -0
- package/dist/core/runtime/broker-extension-render.d.ts +7 -3
- package/dist/core/runtime/broker-extension-render.js +10 -5
- package/dist/core/runtime/broker-persona-guidance.d.ts +19 -5
- package/dist/core/runtime/broker-persona-guidance.js +122 -27
- package/dist/core/runtime/deliver-live.d.ts +16 -4
- package/dist/core/runtime/deliver-live.js +29 -15
- package/dist/core/runtime/kickoff.d.ts +3 -3
- package/dist/core/runtime/kickoff.js +8 -16
- package/dist/core/runtime/lifecycle.js +2 -3
- package/dist/core/runtime/nodes.d.ts +3 -4
- package/dist/core/runtime/nodes.js +3 -4
- package/dist/core/runtime/persona.d.ts +8 -12
- package/dist/core/runtime/persona.js +24 -96
- package/dist/core/runtime/promote.d.ts +2 -2
- package/dist/core/runtime/promote.js +8 -21
- package/dist/core/runtime/revive.js +20 -17
- package/dist/core/runtime/spawn.js +18 -7
- package/dist/core/runtime/tmux-bindings.js +2 -3
- package/dist/core/runtime/warm-pool.js +3 -4
- package/dist/core/scope.js +2 -0
- package/dist/core/self-update.d.ts +0 -2
- package/dist/core/self-update.js +2 -35
- package/dist/core/substrate/__tests__/surface-match-memory-read.test.d.ts +1 -0
- package/dist/core/substrate/__tests__/surface-match-memory-read.test.js +28 -0
- package/dist/core/substrate/index.d.ts +2 -2
- package/dist/core/substrate/index.js +1 -1
- package/dist/core/substrate/injected-store.d.ts +43 -27
- package/dist/core/substrate/injected-store.js +208 -104
- package/dist/core/substrate/listings.d.ts +19 -12
- package/dist/core/substrate/listings.js +75 -52
- package/dist/core/substrate/on-read-node.d.ts +4 -7
- package/dist/core/substrate/on-read-node.js +6 -8
- package/dist/core/substrate/on-read.d.ts +22 -25
- package/dist/core/substrate/on-read.js +103 -147
- package/dist/core/substrate/render-node.d.ts +4 -7
- package/dist/core/substrate/render-node.js +5 -7
- package/dist/core/substrate/render.d.ts +21 -3
- package/dist/core/substrate/render.js +291 -223
- package/dist/core/substrate/schema.d.ts +1 -13
- package/dist/core/substrate/schema.js +5 -40
- package/dist/core/substrate/session-cache.d.ts +14 -4
- package/dist/core/substrate/session-cache.js +40 -22
- package/dist/core/substrate/surface-match.d.ts +9 -7
- package/dist/core/substrate/surface-match.js +26 -25
- package/dist/daemon/__tests__/helpers/source-daemon.d.ts +30 -0
- package/dist/daemon/__tests__/helpers/source-daemon.js +174 -0
- package/dist/daemon/__tests__/integration/migration-startup.test.d.ts +1 -0
- package/dist/daemon/__tests__/integration/migration-startup.test.js +97 -0
- package/dist/daemon/api/__tests__/bridge-heartbeat.test.js +21 -5
- package/dist/daemon/api/handlers/broker-ops.js +21 -16
- package/dist/daemon/api/handlers/memory.js +2 -0
- package/dist/daemon/crtrd.js +2 -0
- package/dist/daemon/human/finish.js +8 -1
- package/dist/daemon/manage.d.ts +0 -1
- package/dist/daemon/manage.js +7 -16
- package/dist/daemon/startup-policy.d.ts +1 -0
- package/dist/daemon/startup-policy.js +1 -0
- package/dist/migrations/001-surfaces-frontmatter.js +21 -109
- package/dist/migrations/002-profile-project-memory.js +1 -0
- package/dist/migrations/003-repository-root-memory-identity/front-door.d.ts +26 -0
- package/dist/migrations/003-repository-root-memory-identity/front-door.js +231 -0
- package/dist/migrations/003-repository-root-memory-identity/index.d.ts +2 -0
- package/dist/migrations/003-repository-root-memory-identity/index.js +513 -0
- package/dist/migrations/003-repository-root-memory-identity/references.d.ts +95 -0
- package/dist/migrations/003-repository-root-memory-identity/references.js +469 -0
- package/dist/migrations/003-repository-root-memory-identity/repository-facts.d.ts +39 -0
- package/dist/migrations/003-repository-root-memory-identity/repository-facts.js +349 -0
- package/dist/migrations/__tests__/activation-concurrency.test.d.ts +1 -0
- package/dist/migrations/__tests__/activation-concurrency.test.js +145 -0
- package/dist/migrations/__tests__/activation.test.d.ts +1 -0
- package/dist/migrations/__tests__/activation.test.js +149 -0
- package/dist/migrations/__tests__/deletion-and-root-declaration.test.d.ts +1 -0
- package/dist/migrations/__tests__/deletion-and-root-declaration.test.js +149 -0
- package/dist/migrations/activation.d.ts +16 -0
- package/dist/migrations/activation.js +78 -0
- package/dist/migrations/convergent.d.ts +14 -3
- package/dist/migrations/convergent.js +21 -10
- package/dist/migrations/corpus.d.ts +78 -0
- package/dist/migrations/corpus.js +497 -0
- package/dist/migrations/frontmatter-splice.d.ts +15 -0
- package/dist/migrations/frontmatter-splice.js +176 -0
- package/dist/migrations/registry.d.ts +6 -1
- package/dist/migrations/registry.js +7 -2
- package/dist/migrations/runner.d.ts +41 -0
- package/dist/migrations/runner.js +81 -0
- package/dist/migrations/types.d.ts +148 -9
- package/dist/migrations/types.js +9 -2
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +225 -17
- package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +11 -3
- package/dist/pi-extensions/canvas-context-intro.d.ts +3 -5
- package/dist/pi-extensions/canvas-context-intro.js +50 -46
- package/dist/pi-extensions/canvas-doc-substrate.d.ts +1 -8
- package/dist/pi-extensions/canvas-doc-substrate.js +55 -122
- package/dist/pi-extensions/canvas-stophook.js +6 -13
- package/dist/shared/generated-context.d.ts +0 -3
- package/dist/shared/generated-context.js +0 -57
- package/dist/shared/tool-groups.js +2 -3
- package/package.json +5 -4
- package/runtime.lock.json +2 -2
- /package/dist/api/__tests__/{serial → integration}/client.test.d.ts +0 -0
- /package/dist/api/__tests__/{serial → integration}/client.test.js +0 -0
- /package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/{serial → integration}/provider-rotation.test.ts +0 -0
- /package/dist/clients/{inbox/__tests__/serial/inbox-controller.test.d.ts → attach/__tests__/pane-tag-successor.test.d.ts} +0 -0
- /package/dist/clients/{inbox/__tests__/serial/mount-panel.test.d.ts → attach/__tests__/profile-files.test.d.ts} +0 -0
- /package/dist/{core/__tests__/live-mutation-verbs.test.d.ts → clients/inbox/__tests__/integration/inbox-controller.test.d.ts} +0 -0
- /package/dist/clients/inbox/__tests__/{serial → integration}/inbox-controller.test.js +0 -0
- /package/dist/{core/__tests__/serial/broker-fork-seam.test.d.ts → clients/inbox/__tests__/integration/mount-panel.test.d.ts} +0 -0
- /package/dist/clients/inbox/__tests__/{serial → integration}/mount-panel.test.js +0 -0
- /package/dist/{core/__tests__/serial/broker-sdk-wiring.test.d.ts → commands/__tests__/surface-reload-target.test.d.ts} +0 -0
- /package/dist/{core/__tests__/serial/broker-snapshot-history.test.d.ts → commands/memory/__tests__/command-selector-and-mutation-guards.test.d.ts} +0 -0
- /package/dist/{core/__tests__/serial/command-plugins.test.d.ts → commands/memory/__tests__/repository-root-lint.test.d.ts} +0 -0
- /package/dist/{core/__tests__/serial/deferred-no-wake.test.d.ts → commands/sys/__tests__/sync-project-guidance.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/flagship-lifecycle.test.d.ts → config-change-delta.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/host-teardown-process-group.test.d.ts → integration/broker-fork-seam.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/human-deliver-e2e.test.d.ts → integration/broker-sdk-wiring.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/live-mutation.test.d.ts → integration/broker-snapshot-history.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/refresh-stall-recycle.test.d.ts → integration/command-plugins.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/revive.test.d.ts → integration/deferred-no-wake.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/spawn-root.test.d.ts → integration/flagship-lifecycle.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/subscription-delivery.test.d.ts → integration/host-teardown-process-group.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/tmux-surface.test.d.ts → integration/human-deliver-e2e.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/worktree.test.d.ts → integration/live-mutation-verbs.test.d.ts} +0 -0
- /package/dist/core/{human/__tests__/serial/inbox-core.test.d.ts → __tests__/integration/live-mutation.test.d.ts} +0 -0
- /package/dist/core/human/__tests__/{serial → integration}/inbox-core.test.js +0 -0
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
import { matchesGlob } from 'node:path';
|
|
2
|
+
import { isMap, isSeq, parseDocument } from 'yaml';
|
|
3
|
+
import { parseFrontmatterGeneric } from '../../core/frontmatter.js';
|
|
4
|
+
import { findDocLinks } from '../../core/memory/doc-link-grammar.js';
|
|
5
|
+
import { canonicalParent, composeCanonicalName, INDEX_SEGMENT, representationForPath, routingAnchorFor, stripNamespace, } from '../../core/memory/identity.js';
|
|
6
|
+
/** Right-to-left, widest-first at a shared start. A whole-key removal beginning
|
|
7
|
+
* at the YAML block base must run before a zero-width insertion there. */
|
|
8
|
+
export function applySplices(source, splices) {
|
|
9
|
+
let out = source;
|
|
10
|
+
for (const splice of [...splices].sort((a, b) => b.start - a.start || b.end - a.end)) {
|
|
11
|
+
out = out.slice(0, splice.start) + splice.text + out.slice(splice.end);
|
|
12
|
+
}
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
/** Locate the raw YAML within a complete document source. */
|
|
16
|
+
export function yamlBlock(source) {
|
|
17
|
+
const parsed = parseFrontmatterGeneric(source);
|
|
18
|
+
if (parsed.raw === '')
|
|
19
|
+
return null;
|
|
20
|
+
const bodyStart = source.length - parsed.body.length;
|
|
21
|
+
const base = source.slice(0, bodyStart).indexOf(parsed.raw);
|
|
22
|
+
return base < 0 ? null : { base, raw: parsed.raw };
|
|
23
|
+
}
|
|
24
|
+
export function yamlMap(raw) {
|
|
25
|
+
const parsed = parseDocument(raw);
|
|
26
|
+
return parsed.errors.length === 0 && isMap(parsed.contents) ? parsed.contents : null;
|
|
27
|
+
}
|
|
28
|
+
export function itemFor(map, key) {
|
|
29
|
+
return map.items.find((item) => String(item.key) === key) ?? null;
|
|
30
|
+
}
|
|
31
|
+
/** Whole-line span of a mapping key, including its multiline value. */
|
|
32
|
+
export function keyLineSpan(raw, item) {
|
|
33
|
+
const keyRange = item.key?.range;
|
|
34
|
+
if (keyRange === undefined)
|
|
35
|
+
return null;
|
|
36
|
+
const valueRange = item.value?.range;
|
|
37
|
+
let start = raw.lastIndexOf('\n', keyRange[0] - 1) + 1;
|
|
38
|
+
let end = valueRange?.[2] ?? keyRange[2];
|
|
39
|
+
if (!(end > 0 && raw[end - 1] === '\n')) {
|
|
40
|
+
while (end < raw.length && (raw[end] === ' ' || raw[end] === '\t' || raw[end] === '\r'))
|
|
41
|
+
end++;
|
|
42
|
+
if (raw[end] === '\n')
|
|
43
|
+
end++;
|
|
44
|
+
}
|
|
45
|
+
if (end >= raw.length && start > 0 && raw[start - 1] === '\n')
|
|
46
|
+
start -= 1;
|
|
47
|
+
return [start, end];
|
|
48
|
+
}
|
|
49
|
+
/** Span of a mapping value with trailing whitespace excluded. */
|
|
50
|
+
export function valueSpan(raw, item) {
|
|
51
|
+
const range = item.value?.range;
|
|
52
|
+
if (range === undefined)
|
|
53
|
+
return null;
|
|
54
|
+
let end = range[1];
|
|
55
|
+
while (end > range[0] && /\s/.test(raw[end - 1]))
|
|
56
|
+
end--;
|
|
57
|
+
return [range[0], end];
|
|
58
|
+
}
|
|
59
|
+
const PLAIN_SCALAR_RE = /^[A-Za-z][A-Za-z0-9._\-/]*$/;
|
|
60
|
+
export function renderScalar(value) {
|
|
61
|
+
return PLAIN_SCALAR_RE.test(value) ? value : JSON.stringify(value);
|
|
62
|
+
}
|
|
63
|
+
/** Render one or several route globs without expanding a one-line value into a
|
|
64
|
+
* block sequence. */
|
|
65
|
+
export function renderGlobList(globs) {
|
|
66
|
+
return globs.length === 1 ? JSON.stringify(globs[0]) : `[${globs.map((glob) => JSON.stringify(glob)).join(', ')}]`;
|
|
67
|
+
}
|
|
68
|
+
export function viewsForStore(corpus, store) {
|
|
69
|
+
// A store no target mounts still holds references and resolves them against
|
|
70
|
+
// itself alone.
|
|
71
|
+
if (store.targets.length === 0) {
|
|
72
|
+
return [{ signature: store.key, storeKeys: [store.key], targetIds: [] }];
|
|
73
|
+
}
|
|
74
|
+
const views = new Map();
|
|
75
|
+
for (const targetId of store.targets) {
|
|
76
|
+
const storeKeys = corpus.storesForTarget(targetId).map((candidate) => candidate.key);
|
|
77
|
+
const signature = storeKeys.join('\u0000');
|
|
78
|
+
const existing = views.get(signature);
|
|
79
|
+
if (existing === undefined)
|
|
80
|
+
views.set(signature, { signature, storeKeys, targetIds: [targetId] });
|
|
81
|
+
else
|
|
82
|
+
existing.targetIds.push(targetId);
|
|
83
|
+
}
|
|
84
|
+
return [...views.values()];
|
|
85
|
+
}
|
|
86
|
+
function safeGlob(target, glob) {
|
|
87
|
+
try {
|
|
88
|
+
return matchesGlob(target, glob);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** `foo/INDEX.md` deterministically fronts `foo.md`, then lexical path order
|
|
95
|
+
* breaks every remaining old-name tie. */
|
|
96
|
+
function oldNameWinner(a, b) {
|
|
97
|
+
const aIndex = a.doc.relPath.endsWith(`${INDEX_SEGMENT}.md`);
|
|
98
|
+
const bIndex = b.doc.relPath.endsWith(`${INDEX_SEGMENT}.md`);
|
|
99
|
+
if (aIndex !== bIndex)
|
|
100
|
+
return aIndex ? a : b;
|
|
101
|
+
return a.doc.relPath.localeCompare(b.doc.relPath) <= 0 ? a : b;
|
|
102
|
+
}
|
|
103
|
+
/** Recover the effective prefix pinned into a document's final identity. */
|
|
104
|
+
function effectivePrefixOf(fact) {
|
|
105
|
+
if (fact.finalLocal === '')
|
|
106
|
+
return fact.finalName;
|
|
107
|
+
if (fact.finalName === fact.finalLocal)
|
|
108
|
+
return '';
|
|
109
|
+
const suffix = `/${fact.finalLocal}`;
|
|
110
|
+
if (!fact.finalName.endsWith(suffix)) {
|
|
111
|
+
throw new Error(`migration 003 reference facts for ${fact.doc.path} disagree: final name \`${fact.finalName}\` does not compose from local name \`${fact.finalLocal}\``);
|
|
112
|
+
}
|
|
113
|
+
return fact.finalName.slice(0, -suffix.length);
|
|
114
|
+
}
|
|
115
|
+
/** Reference resolution under the frozen old corpus view. */
|
|
116
|
+
export class OldCorpus {
|
|
117
|
+
facts = new Map();
|
|
118
|
+
matchCache = new Map();
|
|
119
|
+
constructor(docsByStoreKey) {
|
|
120
|
+
for (const [storeKey, docs] of docsByStoreKey) {
|
|
121
|
+
const byOldName = new Map();
|
|
122
|
+
let oldPrefix = null;
|
|
123
|
+
let effectivePrefix = null;
|
|
124
|
+
for (const fact of docs) {
|
|
125
|
+
const existing = byOldName.get(fact.oldName);
|
|
126
|
+
byOldName.set(fact.oldName, existing === undefined ? fact : oldNameWinner(existing, fact));
|
|
127
|
+
if (oldPrefix === null)
|
|
128
|
+
oldPrefix = fact.oldPrefix;
|
|
129
|
+
else if (oldPrefix !== fact.oldPrefix) {
|
|
130
|
+
throw new Error(`migration 003 reference facts for store ${storeKey} disagree on old prefix (\`${oldPrefix}\` vs \`${fact.oldPrefix}\`)`);
|
|
131
|
+
}
|
|
132
|
+
const candidate = effectivePrefixOf(fact);
|
|
133
|
+
if (effectivePrefix === null)
|
|
134
|
+
effectivePrefix = candidate;
|
|
135
|
+
else if (effectivePrefix !== candidate) {
|
|
136
|
+
throw new Error(`migration 003 reference facts for store ${storeKey} disagree on effective prefix (\`${effectivePrefix}\` vs \`${candidate}\`)`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
this.facts.set(storeKey, {
|
|
140
|
+
docs,
|
|
141
|
+
byOldName,
|
|
142
|
+
oldPrefix: oldPrefix ?? '',
|
|
143
|
+
effectivePrefix: effectivePrefix ?? '',
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** The physical old-name winner in one view, including a synthetic document
|
|
148
|
+
* phase 2 will delete. The retired directory-index fallback is part of this
|
|
149
|
+
* frozen lookup. A virtual bare-directory prefix has no physical fact. */
|
|
150
|
+
resolveOldFact(view, written) {
|
|
151
|
+
for (const key of view.storeKeys) {
|
|
152
|
+
const hit = this.facts.get(key)?.byOldName.get(written);
|
|
153
|
+
if (hit !== undefined)
|
|
154
|
+
return hit;
|
|
155
|
+
}
|
|
156
|
+
for (const key of view.storeKeys) {
|
|
157
|
+
const hit = this.facts.get(key)?.byOldName.get(`${written}/${INDEX_SEGMENT}`);
|
|
158
|
+
if (hit !== undefined)
|
|
159
|
+
return hit;
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
/** The final canonical name a written old name denoted in one view. Deleted
|
|
164
|
+
* physical winners have no final projection; dangling references and
|
|
165
|
+
* unprovable bare-directory mappings likewise return null. */
|
|
166
|
+
resolveOld(view, written) {
|
|
167
|
+
const winner = this.resolveOldFact(view, written);
|
|
168
|
+
if (winner !== null)
|
|
169
|
+
return winner.deleted ? null : winner.finalName;
|
|
170
|
+
const prefix = `${written}/`;
|
|
171
|
+
let derived = null;
|
|
172
|
+
let matched = false;
|
|
173
|
+
for (const key of view.storeKeys) {
|
|
174
|
+
for (const fact of this.facts.get(key)?.docs ?? []) {
|
|
175
|
+
if (!fact.oldName.startsWith(prefix))
|
|
176
|
+
continue;
|
|
177
|
+
matched = true;
|
|
178
|
+
const rest = fact.oldName.slice(prefix.length);
|
|
179
|
+
if (!fact.finalName.endsWith(`/${rest}`))
|
|
180
|
+
return null;
|
|
181
|
+
const candidate = fact.finalName.slice(0, fact.finalName.length - rest.length - 1);
|
|
182
|
+
if (derived === null)
|
|
183
|
+
derived = candidate;
|
|
184
|
+
else if (derived !== candidate)
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return matched ? derived : null;
|
|
189
|
+
}
|
|
190
|
+
/** Realpaths a glob names under old or final identity, plus the effective
|
|
191
|
+
* prefixes of stores contributing a match. */
|
|
192
|
+
matchSet(view, glob, phase) {
|
|
193
|
+
const cacheKey = `${view.signature}\u0000${phase}\u0000${glob}`;
|
|
194
|
+
const cached = this.matchCache.get(cacheKey);
|
|
195
|
+
if (cached !== undefined)
|
|
196
|
+
return cached;
|
|
197
|
+
const paths = new Set();
|
|
198
|
+
const namespaces = new Set();
|
|
199
|
+
for (const key of view.storeKeys) {
|
|
200
|
+
const store = this.facts.get(key);
|
|
201
|
+
if (store === undefined)
|
|
202
|
+
continue;
|
|
203
|
+
for (const fact of store.docs) {
|
|
204
|
+
if (phase === 'final' && fact.deleted)
|
|
205
|
+
continue;
|
|
206
|
+
const name = phase === 'old' ? fact.oldName : fact.finalName;
|
|
207
|
+
if (name === '' || !safeGlob(name, glob))
|
|
208
|
+
continue;
|
|
209
|
+
paths.add(fact.realPath);
|
|
210
|
+
if (store.effectivePrefix !== '')
|
|
211
|
+
namespaces.add(store.effectivePrefix);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const result = { paths, namespaces };
|
|
215
|
+
this.matchCache.set(cacheKey, result);
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
/** Candidate qualification for both accepted old models. A pre-003 store
|
|
219
|
+
* gains its final prefix; a superseded per-store declaration has its old
|
|
220
|
+
* prefix replaced by the repository-derived one. */
|
|
221
|
+
finalGlobSpellings(views, glob) {
|
|
222
|
+
const candidates = new Set();
|
|
223
|
+
for (const view of views) {
|
|
224
|
+
for (const key of view.storeKeys) {
|
|
225
|
+
const store = this.facts.get(key);
|
|
226
|
+
if (store === undefined || !store.docs.some((fact) => fact.oldName !== '' && safeGlob(fact.oldName, glob))) {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
candidates.add(composeCanonicalName(store.effectivePrefix, glob));
|
|
230
|
+
if (store.oldPrefix === '')
|
|
231
|
+
continue;
|
|
232
|
+
if (glob === store.oldPrefix)
|
|
233
|
+
candidates.add(store.effectivePrefix);
|
|
234
|
+
else if (glob.startsWith(`${store.oldPrefix}/`)) {
|
|
235
|
+
candidates.add(composeCanonicalName(store.effectivePrefix, glob.slice(store.oldPrefix.length + 1)));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
candidates.delete('');
|
|
240
|
+
return [...candidates].sort();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function isSubset(a, b) {
|
|
244
|
+
for (const value of a)
|
|
245
|
+
if (!b.has(value))
|
|
246
|
+
return false;
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
function sameSet(a, b) {
|
|
250
|
+
return a.size === b.size && isSubset(a, b);
|
|
251
|
+
}
|
|
252
|
+
/** Candidate final spellings for one authored glob, in preference order. */
|
|
253
|
+
function candidateSpellings(base) {
|
|
254
|
+
const out = [];
|
|
255
|
+
const push = (candidate) => {
|
|
256
|
+
if (candidate !== '' && !out.includes(candidate))
|
|
257
|
+
out.push(candidate);
|
|
258
|
+
};
|
|
259
|
+
for (const candidate of base) {
|
|
260
|
+
push(candidate);
|
|
261
|
+
const collapsed = candidate.endsWith(`/${INDEX_SEGMENT}`)
|
|
262
|
+
? candidate.slice(0, -(INDEX_SEGMENT.length + 1))
|
|
263
|
+
: candidate.endsWith('/**')
|
|
264
|
+
? candidate.slice(0, -3)
|
|
265
|
+
: candidate.endsWith('/*')
|
|
266
|
+
? candidate.slice(0, -2)
|
|
267
|
+
: null;
|
|
268
|
+
if (collapsed !== null)
|
|
269
|
+
push(collapsed);
|
|
270
|
+
}
|
|
271
|
+
return out;
|
|
272
|
+
}
|
|
273
|
+
/** The minimal glob list naming exactly the old match set under final identity,
|
|
274
|
+
* or null when no exact rewrite can be proved. */
|
|
275
|
+
export function provenGlobs(corpus, views, glob) {
|
|
276
|
+
const perView = views.map((view) => ({ view, expected: corpus.matchSet(view, glob, 'old').paths }));
|
|
277
|
+
if (perView.every(({ expected }) => expected.size === 0))
|
|
278
|
+
return [glob];
|
|
279
|
+
const candidates = candidateSpellings([glob, ...corpus.finalGlobSpellings(views, glob)]);
|
|
280
|
+
let kept = candidates.filter((candidate) => perView.every(({ view, expected }) => isSubset(corpus.matchSet(view, candidate, 'final').paths, expected)));
|
|
281
|
+
const covers = (list) => perView.every(({ view, expected }) => {
|
|
282
|
+
const union = new Set();
|
|
283
|
+
for (const candidate of list) {
|
|
284
|
+
for (const path of corpus.matchSet(view, candidate, 'final').paths)
|
|
285
|
+
union.add(path);
|
|
286
|
+
}
|
|
287
|
+
return sameSet(union, expected);
|
|
288
|
+
});
|
|
289
|
+
if (!covers(kept))
|
|
290
|
+
return null;
|
|
291
|
+
for (let index = kept.length - 1; index >= 0; index--) {
|
|
292
|
+
const without = kept.filter((_, candidateIndex) => candidateIndex !== index);
|
|
293
|
+
if (without.length > 0 && covers(without))
|
|
294
|
+
kept = without;
|
|
295
|
+
}
|
|
296
|
+
return kept;
|
|
297
|
+
}
|
|
298
|
+
export function linkRewrites(corpus, views, store, fact, result, proofs) {
|
|
299
|
+
const links = findDocLinks(fact.doc.body);
|
|
300
|
+
if (links.length === 0)
|
|
301
|
+
return [];
|
|
302
|
+
const bodyBase = fact.doc.source.length - fact.doc.body.length;
|
|
303
|
+
const resolvedByName = new Map();
|
|
304
|
+
const splices = [];
|
|
305
|
+
for (const link of links) {
|
|
306
|
+
let target = resolvedByName.get(link.name);
|
|
307
|
+
if (target === undefined) {
|
|
308
|
+
const resolvedFacts = views.map((view) => ({ view, fact: corpus.resolveOldFact(view, link.name) }));
|
|
309
|
+
const deleted = resolvedFacts.filter((row) => row.fact?.deleted === true);
|
|
310
|
+
if (deleted.length > 0) {
|
|
311
|
+
const paths = [...new Set(deleted.map((row) => row.fact.doc.path))];
|
|
312
|
+
result.blockers.push({
|
|
313
|
+
kind: 'divergent-reference',
|
|
314
|
+
message: `[[${link.name}]] resolves to the synthetic document${paths.length === 1 ? '' : 's'} ${paths.map((path) => `\`${path}\``).join(', ')}, but this migration deletes ${paths.length === 1 ? 'that exact document' : 'those exact documents'} and no surviving physical document has the same identity — rewrite the link to the intended surviving document and re-run`,
|
|
315
|
+
storeRoot: store.key,
|
|
316
|
+
path: fact.doc.path,
|
|
317
|
+
name: link.name,
|
|
318
|
+
paths,
|
|
319
|
+
...(deleted[0]?.view.targetIds[0] === undefined ? {} : { targetId: deleted[0].view.targetIds[0] }),
|
|
320
|
+
});
|
|
321
|
+
target = null;
|
|
322
|
+
resolvedByName.set(link.name, target);
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const resolutions = views
|
|
326
|
+
.map((view) => ({ view, name: corpus.resolveOld(view, link.name) }))
|
|
327
|
+
.filter((row) => row.name !== null);
|
|
328
|
+
const distinct = [...new Set(resolutions.map((row) => row.name))];
|
|
329
|
+
if (distinct.length > 1) {
|
|
330
|
+
result.blockers.push({
|
|
331
|
+
kind: 'divergent-reference',
|
|
332
|
+
message: `[[${link.name}]] resolves to different documents depending on the target view (${distinct.map((name) => `\`${name}\``).join(' vs ')}) — this migration never guesses which one the author meant; rewrite the link by hand and re-run`,
|
|
333
|
+
storeRoot: store.key,
|
|
334
|
+
path: fact.doc.path,
|
|
335
|
+
name: link.name,
|
|
336
|
+
...(resolutions[0] === undefined || resolutions[0].view.targetIds[0] === undefined
|
|
337
|
+
? {}
|
|
338
|
+
: { targetId: resolutions[0].view.targetIds[0] }),
|
|
339
|
+
});
|
|
340
|
+
target = null;
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
target = distinct[0] ?? null;
|
|
344
|
+
if (target !== null && target !== link.name) {
|
|
345
|
+
for (const row of resolutions) {
|
|
346
|
+
const targetId = row.view.targetIds[0];
|
|
347
|
+
if (targetId !== undefined)
|
|
348
|
+
proofs.push({ targetId, carrier: fact.doc.path, name: target });
|
|
349
|
+
}
|
|
350
|
+
result.references.push({
|
|
351
|
+
storeRoot: store.key,
|
|
352
|
+
path: fact.doc.path,
|
|
353
|
+
kind: 'link',
|
|
354
|
+
from: link.name,
|
|
355
|
+
to: target,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
resolvedByName.set(link.name, target);
|
|
360
|
+
}
|
|
361
|
+
if (target === null || target === link.name)
|
|
362
|
+
continue;
|
|
363
|
+
splices.push({ start: bodyBase + link.start, end: bodyBase + link.end, text: `[[${target}]]` });
|
|
364
|
+
}
|
|
365
|
+
return splices;
|
|
366
|
+
}
|
|
367
|
+
export function routeRewrites(corpus, views, store, fact, prefix, result, proofs) {
|
|
368
|
+
const surfaces = fact.doc.frontmatter?.['surfaces'];
|
|
369
|
+
if (!Array.isArray(surfaces))
|
|
370
|
+
return [];
|
|
371
|
+
const block = yamlBlock(fact.doc.source);
|
|
372
|
+
if (block === null)
|
|
373
|
+
return [];
|
|
374
|
+
const map = yamlMap(block.raw);
|
|
375
|
+
const seq = map === null ? null : itemFor(map, 'surfaces')?.value;
|
|
376
|
+
// A relative route remains safe when its carrier keeps the same local anchor
|
|
377
|
+
// while the entire store moves from oldPrefix to effectivePrefix.
|
|
378
|
+
const oldAnchor = canonicalParent(fact.oldName);
|
|
379
|
+
const oldLocalAnchor = stripNamespace(oldAnchor, fact.oldPrefix) ?? oldAnchor;
|
|
380
|
+
const expectedAnchor = composeCanonicalName(prefix.effectivePrefix, oldLocalAnchor);
|
|
381
|
+
const newAnchor = routingAnchorFor(fact.finalName, representationForPath(fact.doc.relPath));
|
|
382
|
+
const anchorMoved = newAnchor !== expectedAnchor;
|
|
383
|
+
const splices = [];
|
|
384
|
+
for (let index = 0; index < surfaces.length; index++) {
|
|
385
|
+
const entry = surfaces[index];
|
|
386
|
+
if (entry === null || typeof entry !== 'object' || entry['on'] !== 'memory-read')
|
|
387
|
+
continue;
|
|
388
|
+
const raw = entry['match'];
|
|
389
|
+
const globs = (typeof raw === 'string' ? [raw] : Array.isArray(raw) ? raw : []).filter((glob) => typeof glob === 'string');
|
|
390
|
+
if (globs.length === 0)
|
|
391
|
+
continue;
|
|
392
|
+
const rewritten = [];
|
|
393
|
+
let changed = false;
|
|
394
|
+
for (const glob of globs) {
|
|
395
|
+
if (glob.startsWith('./')) {
|
|
396
|
+
if (anchorMoved) {
|
|
397
|
+
result.blockers.push({
|
|
398
|
+
kind: 'unsafe-route',
|
|
399
|
+
message: `memory-read glob \`${glob}\` is anchored at \`${oldAnchor}\`, but this document's explicit \`name:\` re-anchors it at \`${newAnchor}\` after the identity cut — a different subtree, which no rewrite of the glob restores; spell the route out absolutely (or give the document the \`${expectedAnchor}/${INDEX_SEGMENT}.md\` path its name implies) and re-run`,
|
|
400
|
+
storeRoot: store.key,
|
|
401
|
+
path: fact.doc.path,
|
|
402
|
+
name: glob,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
rewritten.push(glob);
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
const proven = provenGlobs(corpus, views, glob);
|
|
409
|
+
if (proven === null) {
|
|
410
|
+
result.blockers.push({
|
|
411
|
+
kind: 'unsafe-route',
|
|
412
|
+
message: `memory-read glob \`${glob}\` cannot be proved to name the same documents under repository-root identity — rewrite the entry by hand (or anchor it with \`./\`) and re-run`,
|
|
413
|
+
storeRoot: store.key,
|
|
414
|
+
path: fact.doc.path,
|
|
415
|
+
name: glob,
|
|
416
|
+
});
|
|
417
|
+
rewritten.push(glob);
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
if (proven.length !== 1 || proven[0] !== glob) {
|
|
421
|
+
changed = true;
|
|
422
|
+
for (const view of views) {
|
|
423
|
+
const targetId = view.targetIds[0];
|
|
424
|
+
const expected = corpus.matchSet(view, glob, 'old').paths;
|
|
425
|
+
if (targetId === undefined || expected.size === 0)
|
|
426
|
+
continue;
|
|
427
|
+
proofs.push({ targetId, carrier: fact.doc.path, globs: proven, expected: [...expected] });
|
|
428
|
+
}
|
|
429
|
+
result.references.push({
|
|
430
|
+
storeRoot: store.key,
|
|
431
|
+
path: fact.doc.path,
|
|
432
|
+
kind: 'memory-read',
|
|
433
|
+
from: glob,
|
|
434
|
+
to: proven.join(', '),
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
rewritten.push(...proven);
|
|
438
|
+
}
|
|
439
|
+
if (!changed)
|
|
440
|
+
continue;
|
|
441
|
+
if (!isSeq(seq)) {
|
|
442
|
+
result.blockers.push({
|
|
443
|
+
kind: 'unsafe-route',
|
|
444
|
+
message: `memory-read routes in ${fact.doc.relPath} need rewriting but its \`surfaces:\` block is not a YAML sequence — fix the file and re-run`,
|
|
445
|
+
storeRoot: store.key,
|
|
446
|
+
path: fact.doc.path,
|
|
447
|
+
});
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
const entryNode = seq.items[index];
|
|
451
|
+
const item = isMap(entryNode) ? itemFor(entryNode, 'match') : null;
|
|
452
|
+
const span = item === null ? null : valueSpan(block.raw, item);
|
|
453
|
+
if (span === null) {
|
|
454
|
+
result.blockers.push({
|
|
455
|
+
kind: 'unsafe-route',
|
|
456
|
+
message: `memory-read route in ${fact.doc.relPath} needs rewriting but its \`match\` value cannot be located in the frontmatter text — fix the file and re-run`,
|
|
457
|
+
storeRoot: store.key,
|
|
458
|
+
path: fact.doc.path,
|
|
459
|
+
});
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
splices.push({
|
|
463
|
+
start: block.base + span[0],
|
|
464
|
+
end: block.base + span[1],
|
|
465
|
+
text: renderGlobList([...new Set(rewritten)]),
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
return splices;
|
|
469
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type ProjectNamespaceSource } from '../../core/memory/project-namespace.js';
|
|
2
|
+
import type { CorpusBlocker, CorpusSnapshot, CorpusStore } from '../types.js';
|
|
3
|
+
export interface RepositoryDecision {
|
|
4
|
+
repositoryKey: string;
|
|
5
|
+
repositoryRoot: string;
|
|
6
|
+
declarationPath: string;
|
|
7
|
+
namespace: string;
|
|
8
|
+
source: 'declared' | ProjectNamespaceSource;
|
|
9
|
+
action: 'kept' | 'created' | 'filled-linked-root';
|
|
10
|
+
/** Store keys grouped under this repository, in corpus order. */
|
|
11
|
+
storeKeys: readonly string[];
|
|
12
|
+
}
|
|
13
|
+
export type NestedDeclarationAction = 'none' | 'removed' | 'synthetic-index-removed';
|
|
14
|
+
export interface StorePrefixFacts {
|
|
15
|
+
store: CorpusStore;
|
|
16
|
+
storeKey: string;
|
|
17
|
+
storeRoot: string;
|
|
18
|
+
ownerDir: string;
|
|
19
|
+
repositoryKey: string;
|
|
20
|
+
repositoryRoot: string;
|
|
21
|
+
ownerRelativePath: string;
|
|
22
|
+
/** The prefix this store's documents answered under BEFORE this run. */
|
|
23
|
+
oldPrefix: string;
|
|
24
|
+
/** How that prefix was proven: `'per-store-declaration'` when a declaration on
|
|
25
|
+
* disk — this store's own legacy one, or the repository-root one a nested
|
|
26
|
+
* store already answers under — names it, `'pre-003'` when nothing declares
|
|
27
|
+
* it and the documents answered at bare local names. */
|
|
28
|
+
oldRule: 'pre-003' | 'per-store-declaration';
|
|
29
|
+
/** repositoryNamespace ⊕ ownerRelativePath. */
|
|
30
|
+
effectivePrefix: string;
|
|
31
|
+
nestedDeclarationAction: NestedDeclarationAction;
|
|
32
|
+
/** The legacy nested `namespace:` value, when one was present. */
|
|
33
|
+
legacyDeclaration: string | null;
|
|
34
|
+
}
|
|
35
|
+
export declare function planRepositories(corpus: CorpusSnapshot): {
|
|
36
|
+
repositories: RepositoryDecision[];
|
|
37
|
+
prefixes: Map<string, StorePrefixFacts>;
|
|
38
|
+
blockers: CorpusBlocker[];
|
|
39
|
+
};
|