From 5cde858a544af07393a7c9c16039ca9bade77c26 Mon Sep 17 00:00:00 2001 From: Luke Melia Date: Wed, 21 Jan 2026 18:10:29 -0500 Subject: [PATCH] Simplify isFileDef and improve typing --- packages/base/card-api.gts | 50 ++++++------------- packages/base/links-to-editor.gts | 2 +- packages/base/links-to-many-component.gts | 8 +-- packages/host/app/lib/gc-card-store.ts | 7 +-- packages/host/app/resources/card-resource.ts | 10 +--- .../components/card-basics-test.gts | 2 +- packages/runtime-common/code-ref.ts | 11 ++++ 7 files changed, 33 insertions(+), 57 deletions(-) diff --git a/packages/base/card-api.gts b/packages/base/card-api.gts index f444844bb3..a562ef1086 100644 --- a/packages/base/card-api.gts +++ b/packages/base/card-api.gts @@ -16,7 +16,6 @@ import { getLinksToManyComponent } from './links-to-many-component'; import { assertIsSerializerName, baseRef, - baseRealm, CardContextName, CardError, CodeRef, @@ -32,12 +31,12 @@ import { getSerializer, humanReadable, identifyCard, - normalizeCodeRef, isBaseInstance, isCardError, isCardInstance as _isCardInstance, isCardResource, isFileMetaResource, + isFileDef, isField, isFieldInstance, isRelationship, @@ -167,6 +166,7 @@ export { getFields, isCard, isField, + isFileDef, localId, meta, primitive, @@ -333,28 +333,6 @@ export function instanceOf(instance: BaseDef, clazz: typeof BaseDef): boolean { return false; } -export function isFileDefConstructor(card: typeof BaseDef): boolean { - let baseFileDefRef = { - module: `${baseRealm.url}file-api`, - name: 'FileDef', - }; - let current: typeof BaseDef | undefined = card; - while (current) { - let ref = identifyCard(current); - if (ref) { - let normalized = normalizeCodeRef(ref); - if ( - normalized.module === baseFileDefRef.module && - normalized.name === baseFileDefRef.name - ) { - return true; - } - } - current = getAncestor(current) as typeof BaseDef | undefined; - } - return false; -} - class Logger { private promises: Promise[] = []; @@ -1087,7 +1065,7 @@ class LinksTo implements Field { visited: Set, opts?: SerializeOpts, ) { - let relationshipType = isFileDefConstructor(this.card as typeof BaseDef) + let relationshipType = isFileDef(this.card) ? FileMetaResourceType : CardResourceType; if (isNotLoadedValue(value)) { @@ -1110,7 +1088,7 @@ class LinksTo implements Field { }, }; } - if (isFileDefConstructor(this.card as typeof BaseDef) && !value.id) { + if (isFileDef(this.card) && !value.id) { throw new Error( `linksTo field '${this.name}' cannot serialize a FileDef without an id`, ); @@ -1266,7 +1244,7 @@ class LinksTo implements Field { if (isNotLoadedValue(value)) { return value; } - if (isFileDefConstructor(this.card as typeof BaseDef) && !value.id) { + if (isFileDef(this.card) && !value.id) { throw new Error( `field validation error: the linksTo field '${this.name}' cannot reference a FileDef without an id`, ); @@ -1310,7 +1288,7 @@ class LinksTo implements Field { let innerModel = model.field(fieldName); return innerModel as unknown as Box; }; - let isFileDef = isFileDefConstructor(linksToField.card as typeof BaseDef); + let isFileDefField = isFileDef(linksToField.card); function shouldRenderEditor( format: Format | undefined, defaultFormat: Format, @@ -1323,14 +1301,14 @@ class LinksTo implements Field { format: Format | undefined, defaultFormat: Format, model: Box, - isFileDef: boolean, + isFileDefField: boolean, ) { let effectiveFormat = format ?? defaultFormat; if ( effectiveFormat === 'edit' && (('isCardDef' in model.value.constructor && model.value.constructor.isCardDef) || - isFileDef) + isFileDefField) ) { return 'fitted'; } @@ -1352,7 +1330,7 @@ class LinksTo implements Field { {{#if (shouldRenderEditor - @format defaultFormats.cardDef isComputed isFileDef + @format defaultFormats.cardDef isComputed isFileDefField ) }} implements Field { @format defaultFormats.cardDef model - isFileDef + isFileDefField }} @displayContainer={{@displayContainer}} ...attributes @@ -1580,7 +1558,7 @@ class LinksToMany throw new Error(`Expected array for field value ${this.name}`); } - let relationshipType = isFileDefConstructor(this.card as typeof BaseDef) + let relationshipType = isFileDef(this.card) ? FileMetaResourceType : CardResourceType; let relationships: Record = {}; @@ -1603,7 +1581,7 @@ class LinksToMany }; return; } - if (isFileDefConstructor(this.card as typeof BaseDef) && !value.id) { + if (isFileDef(this.card) && !value.id) { throw new Error( `linksToMany field '${this.name}' cannot serialize a FileDef without an id`, ); @@ -1815,7 +1793,7 @@ class LinksToMany if ( !isNotLoadedValue(value) && value != null && - isFileDefConstructor(expectedCard as typeof BaseDef) && + isFileDef(expectedCard) && !value.id ) { throw new Error( @@ -2770,7 +2748,7 @@ function lazilyLoadLink( ), ); (async () => { - let isFileLink = isFileDefConstructor(field.card as typeof BaseDef); + let isFileLink = isFileDef(field.card); try { let fieldValue: CardDef | FileDef; if (isFileLink) { diff --git a/packages/base/links-to-editor.gts b/packages/base/links-to-editor.gts index d451f07b60..2a8bab9d6a 100644 --- a/packages/base/links-to-editor.gts +++ b/packages/base/links-to-editor.gts @@ -1,4 +1,5 @@ import GlimmerComponent from '@glimmer/component'; +import { hash } from '@ember/helper'; import { on } from '@ember/modifier'; import { restartableTask, @@ -32,7 +33,6 @@ import { import { Button, IconButton } from '@cardstack/boxel-ui/components'; import { IconMinusCircle } from '@cardstack/boxel-ui/icons'; import { consume } from 'ember-provide-consume-context'; -import { hash } from '@ember/helper'; interface Signature { Element: HTMLElement; diff --git a/packages/base/links-to-many-component.gts b/packages/base/links-to-many-component.gts index 2a6b173fd2..8b6ebda88b 100644 --- a/packages/base/links-to-many-component.gts +++ b/packages/base/links-to-many-component.gts @@ -13,7 +13,7 @@ import { type LinkableDefConstructor, CreateCardFn, CardCrudFunctions, - isFileDefConstructor, + isFileDef, } from './card-api'; import { BoxComponentSignature, @@ -515,13 +515,13 @@ export function getLinksToManyComponent({ getBoxComponent(cardTypeFor(field, child), child, field), ); // Wrap the the components in a function so that the template is reactive to changes in the model (this is essentially a helper) let isComputed = !!field.computeVia || !!field.queryDefinition; - let isFileDef = isFileDefConstructor(field.card as typeof BaseDef); + let isFileDefField = isFileDef(field.card); let linksToManyComponent = class LinksToManyComponent extends GlimmerComponent {