Skip to content

Commit bbbf2c4

Browse files
committedFeb 27, 2025
Before trying to load command code refs, we need to ensure they are using absolute URLs
1 parent b9bcbec commit bbbf2c4

File tree

6 files changed

+53
-24
lines changed

6 files changed

+53
-24
lines changed
 

‎packages/base/code-ref.gts

+3-10
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,13 @@ import { restartableTask } from 'ember-concurrency';
1919
import { consume } from 'ember-provide-consume-context';
2020
import {
2121
type ResolvedCodeRef,
22+
isUrlLike,
2223
CardURLContextName,
2324
} from '@cardstack/runtime-common';
2425
import { not } from '@cardstack/boxel-ui/helpers';
2526
import { BoxelInput } from '@cardstack/boxel-ui/components';
2627
import CodeIcon from '@cardstack/boxel-icons/code';
2728

28-
function moduleIsUrlLike(module: string) {
29-
return (
30-
module.startsWith('http') ||
31-
module.startsWith('.') ||
32-
module.startsWith('/')
33-
);
34-
}
35-
3629
class BaseView extends Component<typeof CodeRefField> {
3730
<template>
3831
<div data-test-ref>
@@ -122,7 +115,7 @@ export default class CodeRefField extends FieldDef {
122115
...codeRef,
123116
...(opts?.maybeRelativeURL &&
124117
!opts?.useAbsoluteURL &&
125-
moduleIsUrlLike(codeRef.module)
118+
isUrlLike(codeRef.module)
126119
? { module: opts.maybeRelativeURL(codeRef.module) }
127120
: {}),
128121
};
@@ -153,7 +146,7 @@ function maybeSerializeCodeRef(
153146
stack: CardDef[] = [],
154147
) {
155148
if (codeRef) {
156-
if (moduleIsUrlLike(codeRef.module)) {
149+
if (isUrlLike(codeRef.module)) {
157150
// if a stack is passed in, use the containing card to resolve relative references
158151
let moduleHref =
159152
stack.length > 0

‎packages/host/app/services/matrix-service.ts

+17-7
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,16 @@ import { TrackedMap } from 'tracked-built-ins';
2020
import { v4 as uuidv4 } from 'uuid';
2121

2222
import {
23-
type LooseSingleCardDocument,
24-
markdownToHtml,
25-
splitStringIntoChunks,
23+
aiBotUsername,
2624
baseRealm,
25+
codeRefWithAbsoluteURL,
26+
getClass,
2727
LooseCardResource,
28+
markdownToHtml,
2829
ResolvedCodeRef,
29-
aiBotUsername,
30-
getClass,
30+
splitStringIntoChunks,
31+
type LooseSingleCardDocument,
32+
isUrlLike,
3133
} from '@cardstack/runtime-common';
3234

3335
import {
@@ -65,7 +67,11 @@ import { getRandomBackgroundURL, iconURLFor } from '@cardstack/host/lib/utils';
6567
import { getMatrixProfile } from '@cardstack/host/resources/matrix-profile';
6668

6769
import type { Base64ImageField as Base64ImageFieldType } from 'https://cardstack.com/base/base64-image';
68-
import { BaseDef, type CardDef } from 'https://cardstack.com/base/card-api';
70+
import type {
71+
relativeTo,
72+
BaseDef,
73+
CardDef,
74+
} from 'https://cardstack.com/base/card-api';
6975
import type * as CardAPI from 'https://cardstack.com/base/card-api';
7076
import type * as FileAPI from 'https://cardstack.com/base/file-api';
7177
import { type FileDef } from 'https://cardstack.com/base/file-api';
@@ -599,8 +605,12 @@ export default class MatrixService extends Service {
599605
}[] = [];
600606
const mappings = await basicMappings(this.loaderService.loader);
601607
for (let commandDef of commandDefinitions) {
602-
const Command = await getClass(
608+
let absoluteCodeRef = codeRefWithAbsoluteURL(
603609
commandDef.codeRef,
610+
commandDef[Symbol.for('cardstack-relative-to') as typeof relativeTo],
611+
) as ResolvedCodeRef;
612+
const Command = await getClass(
613+
absoluteCodeRef,
604614
this.loaderService.loader,
605615
);
606616
const command = new Command(this.commandService.commandContext);

‎packages/host/tests/acceptance/commands-test.gts

+18
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,16 @@ module('Acceptance | Commands tests', function (hooks) {
191191
}
192192
}
193193

194+
class BoomCommand extends Command<undefined, undefined> {
195+
static displayName = 'BoomCommand';
196+
async getInputType() {
197+
return undefined;
198+
}
199+
protected async run(): Promise<undefined> {
200+
throw new Error('Boom!');
201+
}
202+
}
203+
194204
class Person extends CardDef {
195205
static displayName = 'Person';
196206
@field firstName = contains(StringField);
@@ -385,6 +395,7 @@ module('Acceptance | Commands tests', function (hooks) {
385395
pet: mangoPet,
386396
friends: [mangoPet],
387397
}),
398+
'boom-command.ts': { default: BoomCommand },
388399
'Skill/useful-commands.json': {
389400
data: {
390401
type: 'card',
@@ -413,6 +424,13 @@ module('Acceptance | Commands tests', function (hooks) {
413424
},
414425
executors: [],
415426
},
427+
{
428+
codeRef: {
429+
name: 'default',
430+
module: `/test/boom-command`,
431+
},
432+
executors: [],
433+
},
416434
],
417435
title: 'Useful Commands',
418436
description: null,

‎packages/runtime-common/code-ref.ts

+9-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
import { Loader } from './loader';
99
import { isField } from './constants';
1010
import { CardError } from './error';
11-
import { trimExecutableExtension } from './index';
11+
import { isUrlLike, trimExecutableExtension } from './index';
1212

1313
export type ResolvedCodeRef = {
1414
module: string;
@@ -105,11 +105,15 @@ export function codeRefWithAbsoluteURL(
105105
opts?: { trimExecutableExtension?: true },
106106
): CodeRef {
107107
if (!('type' in ref)) {
108-
let moduleURL = new URL(ref.module, relativeTo);
109-
if (opts?.trimExecutableExtension) {
110-
moduleURL = trimExecutableExtension(moduleURL);
108+
if (isUrlLike(ref.module)) {
109+
let moduleURL = new URL(ref.module, relativeTo);
110+
if (opts?.trimExecutableExtension) {
111+
moduleURL = trimExecutableExtension(moduleURL);
112+
}
113+
return { ...ref, module: moduleURL.href };
114+
} else {
115+
return { ...ref };
111116
}
112-
return { ...ref, module: moduleURL.href };
113117
}
114118
return { ...ref, card: codeRefWithAbsoluteURL(ref.card, relativeTo) };
115119
}

‎packages/runtime-common/index.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,11 @@ export const isNode =
9999
'[object process]';
100100

101101
export { SupportedMimeType } from './router';
102-
export { VirtualNetwork, type ResponseWithNodeStream } from './virtual-network';
102+
export {
103+
isUrlLike,
104+
VirtualNetwork,
105+
type ResponseWithNodeStream,
106+
} from './virtual-network';
103107
export { RealmAuthDataSource } from './realm-auth-data-source';
104108

105109
export type {

‎packages/runtime-common/virtual-network.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ export class VirtualNetwork {
208208
}
209209
}
210210

211-
function isUrlLike(moduleIdentifier: string): boolean {
211+
export function isUrlLike(moduleIdentifier: string): boolean {
212212
return (
213213
moduleIdentifier.startsWith('.') ||
214214
moduleIdentifier.startsWith('/') ||

0 commit comments

Comments
 (0)
Failed to load comments.