diff --git a/packages/base/markdown-file-def.gts b/packages/base/markdown-file-def.gts
index cd170a319e..574003808f 100644
--- a/packages/base/markdown-file-def.gts
+++ b/packages/base/markdown-file-def.gts
@@ -4,7 +4,6 @@ import {
extractFileReferenceUrls,
FRONTMATTER_PARSE_ERROR_SYMBOL,
identifyCard,
- VirtualNetwork,
type FrontmatterParseError,
} from '@cardstack/runtime-common';
import MarkdownIcon from '@cardstack/boxel-icons/align-box-left-middle';
@@ -17,7 +16,6 @@ import {
containsMany,
field,
linksToMany,
- virtualNetworkFor,
} from './card-api';
import MarkdownTemplate from './default-templates/markdown';
import {
@@ -497,11 +495,7 @@ export class MarkdownDef extends FileDef {
if (!this.content) {
return [];
}
- return extractCardReferenceUrls(
- this.content,
- this.id ?? '',
- virtualNetworkFor(this) ?? new VirtualNetwork(),
- );
+ return extractCardReferenceUrls(this.content, this.id ?? '');
},
});
@@ -518,11 +512,7 @@ export class MarkdownDef extends FileDef {
if (!this.content) {
return [];
}
- return extractFileReferenceUrls(
- this.content,
- this.id ?? '',
- virtualNetworkFor(this) ?? new VirtualNetwork(),
- );
+ return extractFileReferenceUrls(this.content, this.id ?? '');
},
});
@@ -629,16 +619,8 @@ export class MarkdownDef extends FileDef {
// `frontmatter.rawContent`, and the verbatim file is always served from
// the realm, so nothing is lost.
content: body,
- cardReferenceUrls: extractCardReferenceUrls(
- body,
- url,
- new VirtualNetwork(),
- ),
- fileReferenceUrls: extractFileReferenceUrls(
- body,
- url,
- new VirtualNetwork(),
- ),
+ cardReferenceUrls: extractCardReferenceUrls(body, url),
+ fileReferenceUrls: extractFileReferenceUrls(body, url),
};
// Boxel-specific frontmatter is namespaced under `boxel:`; generic
diff --git a/packages/base/rich-markdown.gts b/packages/base/rich-markdown.gts
index ec32ddc446..bfff78b96f 100644
--- a/packages/base/rich-markdown.gts
+++ b/packages/base/rich-markdown.gts
@@ -3,7 +3,6 @@ import {
extractFileReferenceUrls,
fieldSerializer,
relativeTo,
- VirtualNetwork,
} from '@cardstack/runtime-common';
import { TrackedObject } from 'tracked-built-ins';
import { eq } from '@cardstack/boxel-ui/helpers';
@@ -62,22 +61,18 @@ export class RichMarkdownField extends FieldDef {
if (!rel) {
return '';
}
- return typeof rel === 'string'
- ? (virtualNetworkFor(this)?.toURL(rel).href ?? rel)
- : rel.href;
+ // `relativeTo` is already a canonical RRI; references resolve against it in
+ // RRI space (no VirtualNetwork).
+ return typeof rel === 'string' ? rel : rel.href;
}
- /** Resolved absolute URLs of `:card[URL]` and `::card[URL]` references. */
+ /** Resolved canonical-RRI references of `:card[URL]` and `::card[URL]`. */
@field cardReferenceUrls = containsMany(StringField, {
computeVia: function (this: RichMarkdownField) {
if (!this.content) {
return [];
}
- return extractCardReferenceUrls(
- this.content,
- this.refBaseUrl,
- virtualNetworkFor(this) ?? new VirtualNetwork(),
- );
+ return extractCardReferenceUrls(this.content, this.refBaseUrl);
},
});
@@ -97,11 +92,7 @@ export class RichMarkdownField extends FieldDef {
if (!this.content) {
return [];
}
- return extractFileReferenceUrls(
- this.content,
- this.refBaseUrl,
- virtualNetworkFor(this) ?? new VirtualNetwork(),
- );
+ return extractFileReferenceUrls(this.content, this.refBaseUrl);
},
});
@@ -235,7 +226,10 @@ export class RichMarkdownField extends FieldDef {
{{! Preview has no CodeMirrorEditor, so the sticky mode selector
lives in its own docked bar above the rendered markdown. }}
-
+
<:leadingControls>
-
+
diff --git a/packages/host/app/components/operator-mode/preview-panel/rendered-markdown.gts b/packages/host/app/components/operator-mode/preview-panel/rendered-markdown.gts
index 37863c9d72..e6667a4653 100644
--- a/packages/host/app/components/operator-mode/preview-panel/rendered-markdown.gts
+++ b/packages/host/app/components/operator-mode/preview-panel/rendered-markdown.gts
@@ -29,15 +29,14 @@ import {
extractFileReferenceUrls,
fileNameFromUrl,
isCardErrorJSONAPI,
+ resolveRRIReference,
rri,
trimJsonExtension,
- type VirtualNetwork,
} from '@cardstack/runtime-common';
import { markdownToHtml } from '@cardstack/runtime-common/marked-sync';
import CardRenderer from '@cardstack/host/components/card-renderer';
-import type NetworkService from '@cardstack/host/services/network';
import type StoreService from '@cardstack/host/services/store';
import type {
@@ -70,14 +69,14 @@ interface RenderSlot {
typeName?: string; // present when state === 'unresolved'
}
-function resolveUrl(
- raw: string,
- baseUrl: string | undefined,
- virtualNetwork: VirtualNetwork,
-): string {
+function resolveUrl(raw: string, baseUrl: string | undefined): string {
try {
+ // Resolve in RRI space (no VirtualNetwork), the same way
+ // `extractCardReferenceUrls`/`extractFileReferenceUrls` resolve the refs
+ // that key `loadedCards`/`loadedFiles` — so a slot's resolved key matches
+ // the loaded instance's map key.
return trimJsonExtension(
- virtualNetwork.resolveRRI(raw, baseUrl ? rri(baseUrl) : undefined),
+ resolveRRIReference(raw, baseUrl ? rri(baseUrl) : undefined),
);
} catch {
return trimJsonExtension(raw);
@@ -116,7 +115,6 @@ const DEFAULT_CARD_CONTEXT: Partial = {
};
export default class RenderedMarkdown extends Component {
- @service declare private network: NetworkService;
@service declare private store: StoreService;
@consume(CardContextName) declare private dynamicCardContext: CardContext;
@@ -163,7 +161,6 @@ export default class RenderedMarkdown extends Component {
return extractCardReferenceUrls(
this.args.content,
this.args.cardReferenceBaseUrl ?? '',
- this.network.virtualNetwork,
);
}
@@ -173,7 +170,6 @@ export default class RenderedMarkdown extends Component {
return extractFileReferenceUrls(
this.args.content,
this.args.cardReferenceBaseUrl ?? '',
- this.network.virtualNetwork,
);
}
@@ -277,11 +273,7 @@ export default class RenderedMarkdown extends Component {
);
}
- let resolvedUrl = resolveUrl(
- rawUrl,
- baseUrl,
- this.network.virtualNetwork,
- );
+ let resolvedUrl = resolveUrl(rawUrl, baseUrl);
if (refType === 'file') {
let file = filesByUrl.get(resolvedUrl);
diff --git a/packages/host/tests/unit/bfm-card-references-test.ts b/packages/host/tests/unit/bfm-card-references-test.ts
index 8e16f658c4..a2ad01c2c7 100644
--- a/packages/host/tests/unit/bfm-card-references-test.ts
+++ b/packages/host/tests/unit/bfm-card-references-test.ts
@@ -15,29 +15,18 @@ import {
type BfmSizeSpec,
} from '@cardstack/runtime-common/bfm-card-references';
import { markdownToHtml } from '@cardstack/runtime-common/marked-sync';
-import { VirtualNetwork } from '@cardstack/runtime-common/virtual-network';
-
-const virtualNetwork = new VirtualNetwork();
module('Unit | bfm-card-references', function () {
module('extractCardReferenceUrls', function () {
test('extracts inline card references', function (assert) {
let markdown = 'See :card[https://example.com/cards/1] for details.';
- let urls = extractCardReferenceUrls(
- markdown,
- 'https://base.com/',
- virtualNetwork,
- );
+ let urls = extractCardReferenceUrls(markdown, 'https://base.com/');
assert.deepEqual(urls, ['https://example.com/cards/1']);
});
test('extracts block card references', function (assert) {
let markdown = '::card[https://example.com/cards/1]\n';
- let urls = extractCardReferenceUrls(
- markdown,
- 'https://base.com/',
- virtualNetwork,
- );
+ let urls = extractCardReferenceUrls(markdown, 'https://base.com/');
assert.deepEqual(urls, ['https://example.com/cards/1']);
});
@@ -47,11 +36,7 @@ module('Unit | bfm-card-references', function () {
'',
'Text with :card[https://example.com/cards/2] inline.',
].join('\n');
- let urls = extractCardReferenceUrls(
- markdown,
- 'https://base.com/',
- virtualNetwork,
- );
+ let urls = extractCardReferenceUrls(markdown, 'https://base.com/');
assert.deepEqual(urls, [
'https://example.com/cards/1',
'https://example.com/cards/2',
@@ -61,11 +46,7 @@ module('Unit | bfm-card-references', function () {
test('extracts the URL from an inline ref with a size spec', function (assert) {
let markdown =
'See :card[https://example.com/cards/1 | embedded] for details.';
- let urls = extractCardReferenceUrls(
- markdown,
- 'https://base.com/',
- virtualNetwork,
- );
+ let urls = extractCardReferenceUrls(markdown, 'https://base.com/');
assert.deepEqual(
urls,
['https://example.com/cards/1'],
@@ -78,7 +59,6 @@ module('Unit | bfm-card-references', function () {
let urls = extractCardReferenceUrls(
markdown,
'https://realm.example/docs/file.md',
- virtualNetwork,
);
assert.deepEqual(urls, ['https://realm.example/docs/my-card']);
});
@@ -89,11 +69,7 @@ module('Unit | bfm-card-references', function () {
'',
'::card[https://example.com/cards/2.json]',
].join('\n');
- let urls = extractCardReferenceUrls(
- markdown,
- 'https://base.com/',
- virtualNetwork,
- );
+ let urls = extractCardReferenceUrls(markdown, 'https://base.com/');
assert.deepEqual(urls, [
'https://example.com/cards/1',
'https://example.com/cards/2',
@@ -105,11 +81,7 @@ module('Unit | bfm-card-references', function () {
':card[https://example.com/cards/1]',
':card[https://example.com/cards/1.json]',
].join('\n');
- let urls = extractCardReferenceUrls(
- markdown,
- 'https://base.com/',
- virtualNetwork,
- );
+ let urls = extractCardReferenceUrls(markdown, 'https://base.com/');
assert.deepEqual(urls, ['https://example.com/cards/1']);
});
@@ -118,11 +90,7 @@ module('Unit | bfm-card-references', function () {
':card[https://example.com/cards/1]',
':card[https://example.com/cards/1]',
].join('\n');
- let urls = extractCardReferenceUrls(
- markdown,
- 'https://base.com/',
- virtualNetwork,
- );
+ let urls = extractCardReferenceUrls(markdown, 'https://base.com/');
assert.deepEqual(urls, ['https://example.com/cards/1']);
});
@@ -130,56 +98,36 @@ module('Unit | bfm-card-references', function () {
let markdown = ['```', ':card[https://example.com/cards/1]', '```'].join(
'\n',
);
- let urls = extractCardReferenceUrls(
- markdown,
- 'https://base.com/',
- virtualNetwork,
- );
+ let urls = extractCardReferenceUrls(markdown, 'https://base.com/');
assert.deepEqual(urls, []);
});
test('ignores references inside inline code', function (assert) {
let markdown = 'Use `:card[url]` syntax.';
- let urls = extractCardReferenceUrls(
- markdown,
- 'https://base.com/',
- virtualNetwork,
- );
+ let urls = extractCardReferenceUrls(markdown, 'https://base.com/');
assert.deepEqual(urls, []);
});
test('ignores references inside multi-backtick inline code', function (assert) {
let markdown = 'Use ``:card[https://example.com/cards/1]`` syntax.';
- let urls = extractCardReferenceUrls(
- markdown,
- 'https://base.com/',
- virtualNetwork,
- );
+ let urls = extractCardReferenceUrls(markdown, 'https://base.com/');
assert.deepEqual(urls, []);
});
test('skips malformed URLs with empty base', function (assert) {
let markdown = ':card[not a valid url at all]';
- let urls = extractCardReferenceUrls(markdown, '', virtualNetwork);
+ let urls = extractCardReferenceUrls(markdown, '');
assert.deepEqual(urls, []);
});
test('returns empty array for markdown without references', function (assert) {
let markdown = '# Hello World\n\nNo card references here.';
- let urls = extractCardReferenceUrls(
- markdown,
- 'https://base.com/',
- virtualNetwork,
- );
+ let urls = extractCardReferenceUrls(markdown, 'https://base.com/');
assert.deepEqual(urls, []);
});
test('returns empty array for empty markdown', function (assert) {
- let urls = extractCardReferenceUrls(
- '',
- 'https://base.com/',
- virtualNetwork,
- );
+ let urls = extractCardReferenceUrls('', 'https://base.com/');
assert.deepEqual(urls, []);
});
});
@@ -191,11 +139,7 @@ module('Unit | bfm-card-references', function () {
':file[https://example.com/files/1.pdf]',
'::file[https://example.com/files/2.pdf]',
].join('\n');
- let urls = extractFileReferenceUrls(
- markdown,
- 'https://base.com/',
- virtualNetwork,
- );
+ let urls = extractFileReferenceUrls(markdown, 'https://base.com/');
assert.deepEqual(urls, [
'https://example.com/files/1.pdf',
'https://example.com/files/2.pdf',
@@ -207,18 +151,13 @@ module('Unit | bfm-card-references', function () {
let urls = extractFileReferenceUrls(
markdown,
'https://realm.example/notes/file.md',
- virtualNetwork,
);
assert.deepEqual(urls, ['https://realm.example/notes/docs/report.pdf']);
});
test('returns empty array when there are no file references', function (assert) {
let markdown = ':card[https://example.com/cards/1]';
- let urls = extractFileReferenceUrls(
- markdown,
- 'https://base.com/',
- virtualNetwork,
- );
+ let urls = extractFileReferenceUrls(markdown, 'https://base.com/');
assert.deepEqual(urls, []);
});
});
@@ -229,12 +168,10 @@ module('Unit | bfm-card-references', function () {
':card[https://example.com/cards/1]',
':file[https://example.com/files/1]',
].join('\n');
- let refs = extractBfmReferences(
- markdown,
- 'https://base.com/',
- ['card', 'file'],
- virtualNetwork,
- );
+ let refs = extractBfmReferences(markdown, 'https://base.com/', [
+ 'card',
+ 'file',
+ ]);
assert.deepEqual(refs, [
{ url: 'https://example.com/cards/1', keyword: 'card' },
{ url: 'https://example.com/files/1', keyword: 'file' },
@@ -246,12 +183,10 @@ module('Unit | bfm-card-references', function () {
':card[https://example.com/thing]',
':file[https://example.com/thing]',
].join('\n');
- let refs = extractBfmReferences(
- markdown,
- 'https://base.com/',
- ['card', 'file'],
- virtualNetwork,
- );
+ let refs = extractBfmReferences(markdown, 'https://base.com/', [
+ 'card',
+ 'file',
+ ]);
assert.strictEqual(refs.length, 1);
assert.strictEqual(refs[0].url, 'https://example.com/thing');
});
@@ -984,31 +919,19 @@ module('Unit | bfm-card-references', function () {
module('extractCardReferenceUrls with pipe syntax', function () {
test('extracts URL from block ref with size specifier', function (assert) {
let markdown = '::card[https://example.com/cards/1 | strip]\n';
- let urls = extractCardReferenceUrls(
- markdown,
- 'https://base.com/',
- virtualNetwork,
- );
+ let urls = extractCardReferenceUrls(markdown, 'https://base.com/');
assert.deepEqual(urls, ['https://example.com/cards/1']);
});
test('extracts URL from block ref with custom dimensions', function (assert) {
let markdown = '::card[https://example.com/cards/1 | 400x200]\n';
- let urls = extractCardReferenceUrls(
- markdown,
- 'https://base.com/',
- virtualNetwork,
- );
+ let urls = extractCardReferenceUrls(markdown, 'https://base.com/');
assert.deepEqual(urls, ['https://example.com/cards/1']);
});
test('extracts URL from block ref with isolated', function (assert) {
let markdown = '::card[https://example.com/cards/1 | isolated]\n';
- let urls = extractCardReferenceUrls(
- markdown,
- 'https://base.com/',
- virtualNetwork,
- );
+ let urls = extractCardReferenceUrls(markdown, 'https://base.com/');
assert.deepEqual(urls, ['https://example.com/cards/1']);
});
@@ -1017,7 +940,6 @@ module('Unit | bfm-card-references', function () {
let urls = extractCardReferenceUrls(
markdown,
'https://realm.example/docs/file.md',
- virtualNetwork,
);
assert.deepEqual(urls, ['https://realm.example/docs/my-card']);
});
@@ -1027,11 +949,7 @@ module('Unit | bfm-card-references', function () {
'::card[https://example.com/cards/1 | strip]',
'::card[https://example.com/cards/1 | tile]',
].join('\n');
- let urls = extractCardReferenceUrls(
- markdown,
- 'https://base.com/',
- virtualNetwork,
- );
+ let urls = extractCardReferenceUrls(markdown, 'https://base.com/');
assert.deepEqual(urls, ['https://example.com/cards/1']);
});
});
diff --git a/packages/runtime-common/bfm-card-references.ts b/packages/runtime-common/bfm-card-references.ts
index f1b11d5474..5cae81d5a5 100644
--- a/packages/runtime-common/bfm-card-references.ts
+++ b/packages/runtime-common/bfm-card-references.ts
@@ -1,6 +1,6 @@
import { escapeHtml } from './helpers/html.ts';
-import type { VirtualNetwork } from './virtual-network.ts';
-import { trimJsonExtension } from './url.ts';
+import { resolveRRIReference, trimJsonExtension } from './url.ts';
+import type { RealmResourceIdentifier } from './realm-identifiers.ts';
import { FITTED_FORMATS } from './formats.ts';
import type { TokenizerAndRendererExtension } from './marked.mts';
@@ -11,16 +11,28 @@ const FENCED_CODE_RE = /```[\s\S]*?```/g;
// (e.g. `code`, ``code``, ```code```).
const INLINE_CODE_RE = new RegExp('(`+)([\\s\\S]*?)\\1', 'g');
-function resolveUrl(
- ref: string,
- baseUrl: string | undefined,
- virtualNetwork: VirtualNetwork,
-): string | null {
+function resolveUrl(ref: string, baseUrl: string | undefined): string | null {
+ let resolved: string;
try {
- return virtualNetwork.resolveURL(ref, baseUrl || undefined).href;
+ // Identifiers are canonical RRI; resolve the reference against the base in
+ // RRI space (no VirtualNetwork). The search index tolerates the resulting
+ // canonical-RRI value for the `in:{id}` / `in:{url}` reference queries.
+ resolved = resolveRRIReference(
+ ref,
+ baseUrl ? (baseUrl as RealmResourceIdentifier) : undefined,
+ );
} catch {
return null;
}
+ // Keep only references that resolved to an absolute identifier — a URL or a
+ // prefix-form RRI. A reference that couldn't be made absolute (e.g. a
+ // relative ref with no base) is dropped rather than emitted as a bare,
+ // unmatchable query value.
+ return resolved.startsWith('http://') ||
+ resolved.startsWith('https://') ||
+ resolved.startsWith('@')
+ ? resolved
+ : null;
}
// ── BFM size spec parsing ──
@@ -274,7 +286,6 @@ export function extractBfmReferences(
markdown: string,
baseUrl: string,
keywords: string[],
- virtualNetwork: VirtualNetwork,
): BfmReference[] {
// Strip code blocks so references inside them are not extracted
let stripped = markdown
@@ -291,7 +302,7 @@ export function extractBfmReferences(
for (let match of stripped.matchAll(blockRe)) {
let { url: rawUrl } = splitBfmContent(match[1]);
- let resolved = resolveUrl(rawUrl, baseUrl, virtualNetwork);
+ let resolved = resolveUrl(rawUrl, baseUrl);
if (resolved) {
matches.push({
index: match.index!,
@@ -302,7 +313,7 @@ export function extractBfmReferences(
}
for (let match of stripped.matchAll(inlineRe)) {
let { url: rawUrl } = splitBfmContent(match[1]);
- let resolved = resolveUrl(rawUrl, baseUrl, virtualNetwork);
+ let resolved = resolveUrl(rawUrl, baseUrl);
if (resolved) {
matches.push({
index: match.index!,
@@ -335,11 +346,8 @@ export function extractBfmReferences(
export function extractCardReferenceUrls(
markdown: string,
baseUrl: string,
- virtualNetwork: VirtualNetwork,
): string[] {
- return extractBfmReferences(markdown, baseUrl, ['card'], virtualNetwork).map(
- (r) => r.url,
- );
+ return extractBfmReferences(markdown, baseUrl, ['card']).map((r) => r.url);
}
/**
@@ -349,11 +357,8 @@ export function extractCardReferenceUrls(
export function extractFileReferenceUrls(
markdown: string,
baseUrl: string,
- virtualNetwork: VirtualNetwork,
): string[] {
- return extractBfmReferences(markdown, baseUrl, ['file'], virtualNetwork).map(
- (r) => r.url,
- );
+ return extractBfmReferences(markdown, baseUrl, ['file']).map((r) => r.url);
}
/**