Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/base/card-api.gts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ import {
runtimeQueryDependencyContext,
type RuntimeDependencyTrackingContext,
resolveCardReference,
cardIdToURL,
} from '@cardstack/runtime-common';
import {
captureQueryFieldSeedData,
Expand Down Expand Up @@ -3366,7 +3367,7 @@ async function _updateFromSerialized<T extends BaseDefConstructor>({
relativeTo:
instanceRelativeTo ??
(resource.id && typeof resource.id === 'string'
? new URL(resource.id)
? cardIdToURL(resource.id)
: undefined),
dependencyTrackingContext: opts?.dependencyTrackingContext,
});
Expand Down
18 changes: 15 additions & 3 deletions packages/base/card-serialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import {
getSerializer,
humanReadable,
identifyCard,
isRegisteredPrefix,
cardIdToURL,
isSingleCardDocument,
isSingleFileMetaDocument,
loadCardDef,
Expand Down Expand Up @@ -111,7 +113,7 @@ export async function cardClassFromResource<CardT extends BaseDefConstructor>(
{
loader: myLoader(),
relativeTo:
relativeTo ?? (resource.id ? new URL(resource.id) : undefined),
relativeTo ?? (resource.id ? cardIdToURL(resource.id) : undefined),
},
);
if (!card) {
Expand Down Expand Up @@ -215,12 +217,17 @@ export function serializeCard(
};
let modelRelativeTo =
model.id != null
? new URL(model.id)
? cardIdToURL(model.id)
: (model[relativeTo] as URL | undefined);
let data = serializeCardResource(model, doc, {
...opts,
...{
maybeRelativeURL(possibleURL: string) {
// Registered prefix refs (e.g. @cardstack/catalog/foo) are already
// in their canonical portable form — return as-is
if (isRegisteredPrefix(possibleURL)) {
return possibleURL;
}
let url = maybeURL(possibleURL, modelRelativeTo);
if (!url) {
throw new Error(
Expand Down Expand Up @@ -307,7 +314,7 @@ export function serializeFileDef(
};
let modelRelativeTo =
model.id != null
? new URL(model.id)
? cardIdToURL(model.id)
: (model[relativeTo] as URL | undefined);
let data = serializeCardResource(
model,
Expand All @@ -316,6 +323,11 @@ export function serializeFileDef(
...opts,
...{
maybeRelativeURL(possibleURL: string) {
// Registered prefix refs (e.g. @cardstack/catalog/foo) are already
// in their canonical portable form — return as-is
if (isRegisteredPrefix(possibleURL)) {
return possibleURL;
}
let url = maybeURL(possibleURL, modelRelativeTo);
if (!url) {
throw new Error(
Expand Down
15 changes: 8 additions & 7 deletions packages/base/spec.gts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
} from '@cardstack/boxel-ui/components';
import {
getMenuItems,
cardIdToURL,
codeRefWithAbsoluteURL,
ensureExtension,
isPrimitive,
Expand Down Expand Up @@ -87,7 +88,7 @@ class PopulateFieldSpecExampleCommand extends PopulateWithSampleDataCommand {
}
codeRef = codeRefWithAbsoluteURL(
codeRef,
new URL(card.id!),
cardIdToURL(card.id!),
)! as ResolvedCodeRef;
let cardOrFieldModuleURL = codeRef.module
? ensureExtension(codeRef.module, { default: '.gts' })
Expand Down Expand Up @@ -148,7 +149,7 @@ export class SpecHeader extends GlimmerComponent<SpecHeaderSignature> {
if (this.args.model.ref && this.args.model.id) {
let cardDef = await loadCardDef(this.args.model.ref, {
loader: myLoader(),
relativeTo: new URL(this.args.model.id),
relativeTo: cardIdToURL(this.args.model.id),
});
cardDefObj.value = cardDef;
}
Expand Down Expand Up @@ -380,7 +381,7 @@ export class SpecExamplesSection extends GlimmerComponent<SpecExamplesSectionSig
if (this.args.model.ref && this.args.model.id) {
let cardDef = await loadCardDef(this.args.model.ref, {
loader: myLoader(),
relativeTo: new URL(this.args.model.id),
relativeTo: cardIdToURL(this.args.model.id),
});
cardDefObj.value = cardDef;
}
Expand Down Expand Up @@ -657,7 +658,7 @@ class Isolated extends Component<typeof Spec> {
if (!this.args.model.ref || !this.args.model.id) {
return undefined;
}
let url = new URL(this.args.model.id);
let url = cardIdToURL(this.args.model.id);
let ref = codeRefWithAbsoluteURL(this.args.model.ref, url);
if (!isResolvedCodeRef(ref)) {
throw new Error('ref is not a resolved code ref');
Expand Down Expand Up @@ -723,7 +724,7 @@ class Fitted extends Component<typeof Spec> {
if (this.args.model.ref && this.args.model.id) {
let card = await loadCardDef(this.args.model.ref, {
loader: myLoader(),
relativeTo: new URL(this.args.model.id),
relativeTo: cardIdToURL(this.args.model.id),
});
icon.value = card.icon;
}
Expand Down Expand Up @@ -768,7 +769,7 @@ class Edit extends Component<typeof Spec> {
if (!this.args.model.ref || !this.args.model.id) {
return undefined;
}
let url = new URL(this.args.model.id);
let url = cardIdToURL(this.args.model.id);
let ref = codeRefWithAbsoluteURL(this.args.model.ref, url);
if (!isResolvedCodeRef(ref)) {
throw new Error('ref is not a resolved code ref');
Expand Down Expand Up @@ -967,7 +968,7 @@ export class Spec extends CardDef {
count: GENERATED_EXAMPLE_COUNT,
codeRef: codeRefWithAbsoluteURL(
this.ref,
new URL(this.id),
cardIdToURL(this.id),
) as ResolvedCodeRef,
realm: this[realmURL]?.href,
exampleCard: this,
Expand Down
3 changes: 2 additions & 1 deletion packages/host/app/commands/listing-use.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { service } from '@ember/service';

import {
cardIdToURL,
codeRefWithAbsoluteURL,
isResolvedCodeRef,
loadCardDef,
Expand Down Expand Up @@ -62,7 +63,7 @@ export default class ListingUseCommand extends HostBaseCommand<
if (spec.isComponent) {
return;
}
let url = new URL(spec.id);
let url = cardIdToURL(spec.id);
let ref = codeRefWithAbsoluteURL(spec.ref, url);
if (!isResolvedCodeRef(ref)) {
throw new Error('ref is not a resolved code ref');
Expand Down
3 changes: 2 additions & 1 deletion packages/host/app/routes/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
SupportedMimeType,
isCardError,
isBaseDefInstance,
cardIdToURL,
type CardErrorsJSONAPI,
type LooseSingleCardDocument,
type RenderError,
Expand Down Expand Up @@ -417,7 +418,7 @@ export default class RenderRoute extends Route<Model> {
};

let hydratedInstance = await this.store.add(enhancedDoc, {
relativeTo: new URL(id),
relativeTo: cardIdToURL(id),
realm: realmURL,
doNotPersist: true,
});
Expand Down
5 changes: 3 additions & 2 deletions packages/host/app/routes/render/meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { isEqual } from 'lodash';
import type { CodeRef } from '@cardstack/runtime-common';
import {
baseRef,
cardIdToURL,
identifyCard,
internalKeyFor,
maybeRelativeURL,
Expand Down Expand Up @@ -57,8 +58,8 @@ export default class RenderMetaRoute extends Route<Model> {
includeComputeds: true,
maybeRelativeURL: (url: string) =>
maybeRelativeURL(
new URL(url),
new URL(instance.id),
cardIdToURL(url),
cardIdToURL(instance.id),
instance[realmURL],
),
}) as SingleCardDocument;
Expand Down
3 changes: 2 additions & 1 deletion packages/host/app/services/operator-mode-state-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type { CodeRef } from '@cardstack/runtime-common';
import {
RealmPaths,
type LocalPath,
cardIdToURL,
isResolvedCodeRef,
isCardInstance,
isLocalId,
Expand Down Expand Up @@ -493,7 +494,7 @@ export default class OperatorModeStateService extends Service {

private getRealmURLFromItemId(itemId: string): string {
try {
const url = new URL(itemId);
const url = cardIdToURL(itemId);
return this.realm.realmOfURL(url)?.href ?? this.realmURL;
} catch (error) {
return this.realmURL;
Expand Down
19 changes: 11 additions & 8 deletions packages/host/app/services/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { TrackedObject, TrackedMap } from 'tracked-built-ins';
import {
baseFileRef,
CardError,
cardIdToURL,
hasExecutableExtension,
isCardError,
isCardInstance,
Expand Down Expand Up @@ -274,7 +275,7 @@ export default class StoreService extends Service implements StoreInterface {
}
}
} else {
this.subscribeToRealm(new URL(id));
this.subscribeToRealm(cardIdToURL(id));
// intentionally not awaiting this. we keep track of the promise in
// this.newReferencePromises
this.wireUpNewReference(id, readType);
Expand Down Expand Up @@ -593,7 +594,7 @@ export default class StoreService extends Service implements StoreInterface {
}
let linkedCards = await this.loadPatchedInstances(
patch,
instance.id ? new URL(instance.id) : undefined,
instance.id ? cardIdToURL(instance.id) : undefined,
);
for (let [field, value] of Object.entries(linkedCards)) {
if (field.includes('.')) {
Expand Down Expand Up @@ -1326,7 +1327,7 @@ export default class StoreService extends Service implements StoreInterface {
(resource as any)[queryFieldSeedFromSearchSymbol] = true;
return this.add({ data: resource } as SingleCardDocument, {
doNotPersist: true,
relativeTo: new URL(resource.id),
relativeTo: cardIdToURL(resource.id),
dependencyTrackingContext,
}) as Promise<T>;
}
Expand Down Expand Up @@ -1432,7 +1433,9 @@ export default class StoreService extends Service implements StoreInterface {
if (!doc) {
let json: CardDocument | undefined;
if (this.isRenderStore && (globalThis as any).__boxelRenderContext) {
let result = await this.cardService.getSource(new URL(`${url}.json`));
let result = await this.cardService.getSource(
cardIdToURL(`${url}.json`),
);
if (result.status === 200) {
json = JSON.parse(result.content);
} else {
Expand All @@ -1457,7 +1460,7 @@ export default class StoreService extends Service implements StoreInterface {
// Source-mode loads in render context don't include realm metadata.
// Query-backed relationship fields require realmURL to build their
// fallback search query.
let realmURL = this.realm.realmOfURL(new URL(url))?.href;
let realmURL = this.realm.realmOfURL(cardIdToURL(url))?.href;
if (realmURL) {
json.data.meta = {
...(json.data.meta ?? {}),
Expand All @@ -1470,7 +1473,7 @@ export default class StoreService extends Service implements StoreInterface {
let instance = await this.createFromSerialized(
doc.data,
doc,
new URL(doc.data.id!), // instances from the server will have id's
cardIdToURL(doc.data.id!), // instances from the server will have id's
opts?.dependencyTrackingContext,
);
// in case the url is an alias for the id (like index card without the
Expand Down Expand Up @@ -1865,7 +1868,7 @@ export default class StoreService extends Service implements StoreInterface {
}
if (isNew) {
api.setId(instance, json.data.id!);
this.subscribeToRealm(new URL(instance.id));
this.subscribeToRealm(cardIdToURL(instance.id));
this.operatorModeStateService.handleCardIdAssignment(
instance[localIdSymbol],
);
Expand All @@ -1874,7 +1877,7 @@ export default class StoreService extends Service implements StoreInterface {
await this.startAutoSaving(instance);
}
if (this.onSaveSubscriber) {
this.onSaveSubscriber(new URL(json.data.id!), json);
this.onSaveSubscriber(cardIdToURL(json.data.id!), json);
}
return instance;
} catch (err) {
Expand Down
28 changes: 28 additions & 0 deletions packages/runtime-common/card-reference-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ export function registerCardReferencePrefix(
prefixMappings.set(prefix, targetURL);
}

export function isRegisteredPrefix(reference: string): boolean {
for (let [prefix] of prefixMappings) {
if (reference.startsWith(prefix)) {
return true;
}
}
return false;
}

function isUrlLikeReference(ref: string): boolean {
return (
ref.startsWith('.') ||
Expand All @@ -32,3 +41,22 @@ export function resolveCardReference(
}
return new URL(reference, relativeTo).href;
}

// Reverse of resolveCardReference: converts a resolved URL back to
// its registered prefix form if one matches.
// e.g. "http://localhost:4201/catalog/foo" → "@cardstack/catalog/foo"
export function unresolveCardReference(resolvedURL: string): string {
for (let [prefix, target] of prefixMappings) {
if (resolvedURL.startsWith(target)) {
return prefix + resolvedURL.slice(target.length);
}
}
return resolvedURL;
}

// Converts a card instance ID (which may be a registered prefix like
// @cardstack/catalog/foo or a regular URL) to a URL object by resolving
// the prefix to a real URL when needed.
export function cardIdToURL(id: string): URL {
return new URL(resolveCardReference(id, undefined));
}
13 changes: 7 additions & 6 deletions packages/runtime-common/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { resolveAdoptedCodeRef } from './code-ref';
import { realmURL } from './constants';
import { logger } from './log';
import type { LocalPath } from './paths';
import { cardIdToURL } from './card-reference-resolver';

// @ts-ignore TODO: fix catalog types in runtime-common
import type { Listing } from '@cardstack/catalog/listing/listing';
Expand Down Expand Up @@ -169,10 +170,10 @@ function resolveTargetCodeRef(
codeRef: ResolvedCodeRef,
resolver: ListingPathResolver,
): ResolvedCodeRef {
if (baseRealmPath.inRealm(new URL(codeRef.module))) {
if (baseRealmPath.inRealm(cardIdToURL(codeRef.module))) {
return codeRef;
} else {
let targetModule = resolver.target(codeRef.module);
let targetModule = resolver.target(cardIdToURL(codeRef.module).href);
return {
name: codeRef.name,
module: targetModule,
Expand All @@ -191,7 +192,7 @@ export function planModuleInstall(
return { module: s.moduleHref, name: s.ref.name };
});
let modulesCopy = codeRefs.flatMap((sourceCodeRef: ResolvedCodeRef) => {
if (baseRealmPath.inRealm(new URL(sourceCodeRef.module))) {
if (baseRealmPath.inRealm(cardIdToURL(sourceCodeRef.module))) {
return [];
}
let targetCodeRef = resolveTargetCodeRef(sourceCodeRef, resolver);
Expand All @@ -212,11 +213,11 @@ export function planInstanceInstall(
let modulesCopy: CopyMeta[] = [];
for (let instance of instances) {
let sourceCodeRef = resolveAdoptedCodeRef(instance);
let lid = resolver.local(instance.id);
if (baseRealmPath.inRealm(new URL(instance.id))) {
let lid = resolver.local(cardIdToURL(instance.id).href);
if (baseRealmPath.inRealm(cardIdToURL(instance.id))) {
throw new Error('Cannot install instance from base realm');
}
if (!baseRealmPath.inRealm(new URL(sourceCodeRef.module))) {
if (!baseRealmPath.inRealm(cardIdToURL(sourceCodeRef.module))) {
let targetCodeRef = resolveTargetCodeRef(sourceCodeRef, resolver);
modulesCopy.push({
sourceCodeRef,
Expand Down
Loading
Loading