Skip to content

Commit de6eb58

Browse files
authored
Remove global prefix mappings (#5081)
1 parent 9967535 commit de6eb58

100 files changed

Lines changed: 1516 additions & 1340 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/base/card-api.gts

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ import {
9090
runtimeNonQueryDependencyContext,
9191
runtimeQueryDependencyContext,
9292
type RuntimeDependencyTrackingContext,
93-
resolveCardReference,
9493
rri,
9594
type RealmResourceIdentifier,
9695
type VirtualNetwork,
@@ -471,8 +470,8 @@ export type GetSearchResourceFunc<T extends CardDef | FileDef = CardDef> = (
471470
export interface CardStore {
472471
// The VirtualNetwork that owns this store's realm mappings, used for
473472
// prefix/RRI resolution during (de)serialization. Optional so test doubles
474-
// don't need to implement it; resolution sites fall back to the deprecated
475-
// module-level resolver when it's absent.
473+
// don't need to implement it; resolution sites degrade — URL-form refs
474+
// still URL-join, prefix-form refs pass through unchanged.
476475
virtualNetwork?: VirtualNetwork;
477476
getCard(url: string): CardDef | undefined;
478477
getFileMeta(url: string): FileDef | undefined;
@@ -4647,25 +4646,46 @@ function getStore(instance: BaseDef): CardStore {
46474646

46484647
// The VirtualNetwork associated with an instance's store, for prefix/RRI
46494648
// resolution outside this module. Returns undefined when the store can't
4650-
// supply one, so callers fall back to the deprecated module-level resolver.
4649+
// supply one callers handle that by degrading to URL math or throwing.
46514650
export function virtualNetworkFor(
46524651
instance: BaseDef,
46534652
): VirtualNetwork | undefined {
46544653
return getStore(instance).virtualNetwork;
46554654
}
46564655

46574656
// Resolve a (possibly prefix-form or relative) reference to an absolute URL
4658-
// string through the store's VirtualNetwork when available, falling back to
4659-
// the deprecated module-level resolver when it's absent.
4657+
// string through the store's VirtualNetwork. When the store doesn't carry
4658+
// a VN (test stubs, detached instances), fall back to plain URL math: it
4659+
// covers URL-form refs and relative refs against URL-form bases. Prefix-form
4660+
// refs and refs against prefix-form bases can't be resolved without a VN —
4661+
// `new URL()` throws on those, so we return the raw reference unchanged
4662+
// instead of bubbling the error to callers (e.g. relationship deserialize
4663+
// uses the returned string as a "did this resolve?" signal).
46604664
function resolveRef(
46614665
store: CardStore | undefined,
46624666
reference: string,
46634667
relativeTo: RealmResourceIdentifier | URL | undefined,
46644668
): string {
46654669
let vn = store?.virtualNetwork;
4666-
return vn
4667-
? vn.resolveURL(reference, relativeTo).href
4668-
: resolveCardReference(reference, relativeTo);
4670+
if (vn) {
4671+
return vn.resolveURL(reference, relativeTo).href;
4672+
}
4673+
let base: URL | string | undefined;
4674+
if (relativeTo instanceof URL) {
4675+
base = relativeTo;
4676+
} else if (typeof relativeTo === 'string') {
4677+
if (
4678+
relativeTo.startsWith('http://') ||
4679+
relativeTo.startsWith('https://')
4680+
) {
4681+
base = relativeTo;
4682+
}
4683+
}
4684+
try {
4685+
return new URL(reference, base).href;
4686+
} catch {
4687+
return reference;
4688+
}
46694689
}
46704690

46714691
function myLoader(): Loader {
@@ -4747,7 +4767,13 @@ class FallbackCardStore implements CardStore {
47474767
opts?: { dependencyTrackingContext?: RuntimeDependencyTrackingContext },
47484768
) {
47494769
trackRuntimeInstanceDependency(url, opts?.dependencyTrackingContext);
4750-
let promise = loadCardDocument(fetch, url, this.virtualNetwork);
4770+
let vn = this.virtualNetwork;
4771+
if (!vn) {
4772+
throw new Error(
4773+
`CardStore.loadCardDocument requires a Loader with a VirtualNetwork`,
4774+
);
4775+
}
4776+
let promise = loadCardDocument(fetch, url, vn);
47514777
this.trackLoad(promise);
47524778
return await promise;
47534779
}
@@ -4757,7 +4783,13 @@ class FallbackCardStore implements CardStore {
47574783
opts?: { dependencyTrackingContext?: RuntimeDependencyTrackingContext },
47584784
) {
47594785
trackRuntimeFileDependency(url, opts?.dependencyTrackingContext);
4760-
let promise = loadFileMetaDocument(fetch, url, this.virtualNetwork);
4786+
let vn = this.virtualNetwork;
4787+
if (!vn) {
4788+
throw new Error(
4789+
`CardStore.loadFileMetaDocument requires a Loader with a VirtualNetwork`,
4790+
);
4791+
}
4792+
let promise = loadFileMetaDocument(fetch, url, vn);
47614793
this.trackLoad(promise);
47624794
return await promise;
47634795
}

packages/base/card-serialization.ts

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ import {
3030
getSerializer,
3131
humanReadable,
3232
identifyCard,
33-
isRegisteredPrefix,
34-
cardIdToURL,
3533
isSingleCardDocument,
3634
isSingleFileMetaDocument,
3735
loadCardDef,
@@ -75,8 +73,9 @@ export interface SerializeOpts {
7573
maybeRelativeReference?: (possibleReference: string) => string;
7674
overrides?: Map<string, typeof BaseDef>;
7775
// The VirtualNetwork to consult for prefix/RRI resolution during
78-
// serialization. Optional; sites fall back to the deprecated module-level
79-
// resolver when absent.
76+
// serialization. Optional: when absent, the `maybeRelativeReference`
77+
// closures degrade — prefix-form refs pass through unchanged and only
78+
// URL-form bases support URL math.
8079
virtualNetwork?: VirtualNetwork;
8180
}
8281

@@ -227,23 +226,36 @@ export function serializeCard(
227226
...opts,
228227
...{
229228
maybeRelativeReference(possibleReference: string) {
229+
let vn = opts?.virtualNetwork;
230230
// Registered prefix refs (e.g. @cardstack/catalog/foo) are already
231-
// in their canonical portable form — return as-is
232-
if (
233-
opts?.virtualNetwork
234-
? opts.virtualNetwork.isRegisteredPrefix(possibleReference)
235-
: isRegisteredPrefix(possibleReference)
236-
) {
231+
// in their canonical portable form — return as-is. Without a VN
232+
// we can't know which prefixes are registered, so the most we can
233+
// do for prefix-form refs is pass them through unchanged.
234+
if (vn ? vn.isRegisteredPrefix(possibleReference) : false) {
237235
return possibleReference;
238236
}
239-
let modelRelativeToForURL =
240-
typeof modelRelativeTo === 'string'
241-
? opts?.virtualNetwork
242-
? opts.virtualNetwork.toURL(modelRelativeTo)
243-
: cardIdToURL(modelRelativeTo)
244-
: modelRelativeTo;
237+
let modelRelativeToForURL: URL | undefined;
238+
if (typeof modelRelativeTo === 'string') {
239+
if (vn) {
240+
modelRelativeToForURL = vn.toURL(modelRelativeTo);
241+
} else if (
242+
modelRelativeTo.startsWith('http://') ||
243+
modelRelativeTo.startsWith('https://')
244+
) {
245+
modelRelativeToForURL = new URL(modelRelativeTo);
246+
}
247+
} else {
248+
modelRelativeToForURL = modelRelativeTo;
249+
}
245250
let url = maybeURL(possibleReference, modelRelativeToForURL);
246251
if (!url) {
252+
if (!vn) {
253+
// Without a VN we can't resolve a prefix-form reference. Pass
254+
// through unchanged so callers that don't need relativization
255+
// (e.g. test adapters serializing same-realm cards by URL)
256+
// don't blow up on portable refs.
257+
return possibleReference;
258+
}
247259
throw new Error(
248260
`could not determine url from '${possibleReference}' relative to ${modelRelativeTo}`,
249261
);
@@ -335,23 +347,33 @@ export function serializeFileDef(
335347
...opts,
336348
...{
337349
maybeRelativeReference(possibleReference: string) {
338-
// Registered prefix refs (e.g. @cardstack/catalog/foo) are already
339-
// in their canonical portable form — return as-is
340-
if (
341-
opts?.virtualNetwork
342-
? opts.virtualNetwork.isRegisteredPrefix(possibleReference)
343-
: isRegisteredPrefix(possibleReference)
344-
) {
350+
let vn = opts?.virtualNetwork;
351+
// Registered prefix refs (e.g. @cardstack/catalog/foo) are
352+
// already in their canonical portable form — return as-is.
353+
// Without a VN we can't know which prefixes are registered,
354+
// so the most we can do for prefix-form refs is pass them
355+
// through unchanged.
356+
if (vn ? vn.isRegisteredPrefix(possibleReference) : false) {
345357
return possibleReference;
346358
}
347-
let modelRelativeToForURL =
348-
typeof modelRelativeTo === 'string'
349-
? opts?.virtualNetwork
350-
? opts.virtualNetwork.toURL(modelRelativeTo)
351-
: cardIdToURL(modelRelativeTo)
352-
: modelRelativeTo;
359+
let modelRelativeToForURL: URL | undefined;
360+
if (typeof modelRelativeTo === 'string') {
361+
if (vn) {
362+
modelRelativeToForURL = vn.toURL(modelRelativeTo);
363+
} else if (
364+
modelRelativeTo.startsWith('http://') ||
365+
modelRelativeTo.startsWith('https://')
366+
) {
367+
modelRelativeToForURL = new URL(modelRelativeTo);
368+
}
369+
} else {
370+
modelRelativeToForURL = modelRelativeTo;
371+
}
353372
let url = maybeURL(possibleReference, modelRelativeToForURL);
354373
if (!url) {
374+
if (!vn) {
375+
return possibleReference;
376+
}
355377
throw new Error(
356378
`could not determine url from '${possibleReference}' relative to ${modelRelativeTo}`,
357379
);

packages/base/cards-grid.gts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import AllFilesIcon from '@cardstack/boxel-icons/files';
1616
import FileIcon from '@cardstack/boxel-icons/file';
1717

1818
import {
19-
cardIdToURL,
2019
chooseCard,
2120
specRef,
2221
baseRealm,
@@ -308,13 +307,15 @@ class Isolated extends Component<typeof CardsGrid> {
308307
}
309308

310309
if (spec && isCardInstance<Spec>(spec)) {
311-
await this.args.createCard?.(
312-
spec.ref,
313-
virtualNetworkFor(spec)?.toURL(spec.id!) ?? cardIdToURL(spec.id!),
314-
{
315-
realmURL: this.args.model[realmURL],
316-
},
317-
);
310+
let vn = virtualNetworkFor(spec);
311+
if (!vn) {
312+
throw new Error(
313+
`cards-grid createCard requires a VirtualNetwork on the spec's store`,
314+
);
315+
}
316+
await this.args.createCard?.(spec.ref, vn.toURL(spec.id!), {
317+
realmURL: this.args.model[realmURL],
318+
});
318319
} else if (activeFilterRef) {
319320
// No spec exists for the active type filter — create an instance of
320321
// the type directly. `activeFilterRef` is an absolute CodeRef sourced

packages/base/codemirror-editor.gts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { scheduleOnce } from '@ember/runloop';
88
import { eq } from '@cardstack/boxel-ui/helpers';
99

1010
import {
11-
resolveCardReference,
1211
trimJsonExtension,
1312
maybeRelativeReference,
1413
type VirtualNetwork,
@@ -90,14 +89,21 @@ function isInline(kind: string): boolean {
9089
function resolveUrl(
9190
raw: string,
9291
baseUrl: string | null | undefined,
93-
virtualNetwork?: VirtualNetwork,
92+
virtualNetwork: VirtualNetwork | undefined,
9493
): string {
94+
// With a VN, resolve through it so prefix-form bases and registered
95+
// prefix-form refs round-trip correctly. Without a VN, plain
96+
// `new URL(raw, baseUrl)` still handles the common case — URL-form
97+
// refs (with or without a base) and relative refs against a URL-form
98+
// base. Prefix-form bases need a VN; `new URL()` throws on those and
99+
// we fall back to the raw ref.
95100
try {
96-
return trimJsonExtension(
97-
virtualNetwork
98-
? virtualNetwork.resolveURL(raw, baseUrl || undefined).href
99-
: resolveCardReference(raw, baseUrl || undefined),
100-
);
101+
if (virtualNetwork) {
102+
return trimJsonExtension(
103+
virtualNetwork.resolveURL(raw, baseUrl || undefined).href,
104+
);
105+
}
106+
return trimJsonExtension(new URL(raw, baseUrl || undefined).href);
101107
} catch {
102108
return trimJsonExtension(raw);
103109
}

packages/base/default-templates/markdown.gts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {
1515
extractMermaidBlocks,
1616
processKatexPlaceholders,
1717
replaceMermaidSvgs,
18-
resolveCardReference,
1918
trimJsonExtension,
2019
type VirtualNetwork,
2120
} from '@cardstack/runtime-common';
@@ -68,14 +67,21 @@ interface RenderSlot {
6867
function resolveUrl(
6968
raw: string,
7069
baseUrl: string | null | undefined,
71-
virtualNetwork?: VirtualNetwork,
70+
virtualNetwork: VirtualNetwork | undefined,
7271
): string {
72+
// With a VN, resolve through it so prefix-form bases and registered
73+
// prefix-form refs round-trip correctly. Without a VN, plain
74+
// `new URL(raw, baseUrl)` still handles the common case — URL-form
75+
// refs (with or without a base) and relative refs against a URL-form
76+
// base. Prefix-form bases need a VN; `new URL()` throws on those and
77+
// we fall back to the raw ref.
7378
try {
74-
return trimJsonExtension(
75-
virtualNetwork
76-
? virtualNetwork.resolveURL(raw, baseUrl || undefined).href
77-
: resolveCardReference(raw, baseUrl || undefined),
78-
);
79+
if (virtualNetwork) {
80+
return trimJsonExtension(
81+
virtualNetwork.resolveURL(raw, baseUrl || undefined).href,
82+
);
83+
}
84+
return trimJsonExtension(new URL(raw, baseUrl || undefined).href);
7985
} catch {
8086
return trimJsonExtension(raw);
8187
}

packages/base/markdown-file-def.gts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
byteStreamToUint8Array,
33
extractCardReferenceUrls,
4+
VirtualNetwork,
45
} from '@cardstack/runtime-common';
56
import MarkdownIcon from '@cardstack/boxel-icons/align-box-left-middle';
67
import {
@@ -454,7 +455,7 @@ export class MarkdownDef extends FileDef {
454455
return extractCardReferenceUrls(
455456
this.content,
456457
this.id ?? '',
457-
virtualNetworkFor(this),
458+
virtualNetworkFor(this) ?? new VirtualNetwork(),
458459
);
459460
},
460461
});
@@ -520,7 +521,11 @@ export class MarkdownDef extends FileDef {
520521
title: extractTitle(markdown, fallbackTitle),
521522
excerpt: extractExcerpt(markdown),
522523
content: markdown,
523-
cardReferenceUrls: extractCardReferenceUrls(markdown, url),
524+
cardReferenceUrls: extractCardReferenceUrls(
525+
markdown,
526+
url,
527+
new VirtualNetwork(),
528+
),
524529
};
525530
}
526531
}

packages/base/menu-items.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { BaseDef, CardCrudFunctions, CardDef } from './card-api';
2+
import { virtualNetworkFor } from './card-api';
23
import {
34
copyCardURLToClipboard,
45
type MenuItemOptions,
@@ -76,6 +77,7 @@ export function getDefaultCardMenuItems(
7677
): MenuItemOptions[] {
7778
let cardId = card.id as unknown as string;
7879
let menuItems: MenuItemOptions[] = [];
80+
let vnForCard = virtualNetworkFor(card);
7981
if (
8082
['interact', 'code-mode-preview', 'code-mode-playground'].includes(
8183
params.menuContext,
@@ -130,7 +132,8 @@ export function getDefaultCardMenuItems(
130132
);
131133
}
132134
if (
133-
!isRealmIndexCard(card) && // workspace index card cannot be deleted
135+
vnForCard &&
136+
!isRealmIndexCard(card, vnForCard) && // workspace index card cannot be deleted
134137
cardId &&
135138
params.canEdit
136139
) {
@@ -212,11 +215,15 @@ export function getDefaultCardMenuItems(
212215
disabled: !cardId,
213216
});
214217
menuItems = [...menuItems, ...getSampleDataMenuItems(card, params)];
215-
if (!isRealmIndexCard(card)) {
218+
if (vnForCard && !isRealmIndexCard(card, vnForCard)) {
216219
menuItems.push({
217220
label: `Create Listing`,
218221
action: async () => {
219-
const codeRef = resolveAdoptsFrom(card);
222+
let vn = vnForCard;
223+
if (!vn) {
224+
throw new Error('No VirtualNetwork available to resolve codeRef');
225+
}
226+
const codeRef = resolveAdoptsFrom(card, vn);
220227
if (!codeRef) {
221228
throw new Error('Unable to resolve codeRef from card');
222229
}

0 commit comments

Comments
 (0)