Skip to content
Merged
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
2 changes: 1 addition & 1 deletion examples/typescript-r4/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ const baseObservation: Observation = {

// Use profile class to add required slices
const profile = new bodyweightProfile(baseObservation)
.setVscat({ text: 'Vital Signs' });
.setVSCat({ text: 'Vital Signs' });

const observation = profile.toResource();
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export class observation_bodyweightProfile {
return this.resource as observation_bodyweight
}

public setVscat (input?: Observation_bodyweight_Category_VSCatSliceInput): this {
public setVSCat (input?: Observation_bodyweight_Category_VSCatSliceInput): this {
const match = {"coding":{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}} as Record<string, unknown>
const value = applySliceMatch((input ?? {}) as Record<string, unknown>, match) as unknown as CodeableConcept
const list = (this.resource.category ??= [])
Expand All @@ -140,7 +140,7 @@ export class observation_bodyweightProfile {
return this
}

public getVscat (): Observation_bodyweight_Category_VSCatSliceInput | undefined {
public getVSCat (): Observation_bodyweight_Category_VSCatSliceInput | undefined {
const match = {"coding":{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}} as Record<string, unknown>
const list = this.resource.category
if (!list) return undefined
Expand All @@ -149,7 +149,7 @@ export class observation_bodyweightProfile {
return extractSliceSimplified(item as unknown as Record<string, unknown>, ["coding"]) as Observation_bodyweight_Category_VSCatSliceInput
}

public getVscatRaw (): CodeableConcept | undefined {
public getVSCatRaw (): CodeableConcept | undefined {
const match = {"coding":{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}} as Record<string, unknown>
const list = this.resource.category
if (!list) return undefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export class observation_bpProfile {
return this.resource as observation_bp
}

public setVscat (input?: Observation_bp_Category_VSCatSliceInput): this {
public setVSCat (input?: Observation_bp_Category_VSCatSliceInput): this {
const match = {"coding":{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}} as Record<string, unknown>
const value = applySliceMatch((input ?? {}) as Record<string, unknown>, match) as unknown as CodeableConcept
const list = (this.resource.category ??= [])
Expand All @@ -153,7 +153,7 @@ export class observation_bpProfile {
return this
}

public setSystolicBp (input?: Observation_bp_Component_SystolicBPSliceInput): this {
public setSystolicBP (input?: Observation_bp_Component_SystolicBPSliceInput): this {
const match = {"code":{"coding":{"code":"8480-6","system":"http://loinc.org"}}} as Record<string, unknown>
const value = applySliceMatch((input ?? {}) as Record<string, unknown>, match) as unknown as ObservationComponent
const list = (this.resource.component ??= [])
Expand All @@ -166,7 +166,7 @@ export class observation_bpProfile {
return this
}

public setDiastolicBp (input?: Observation_bp_Component_DiastolicBPSliceInput): this {
public setDiastolicBP (input?: Observation_bp_Component_DiastolicBPSliceInput): this {
const match = {"code":{"coding":{"code":"8462-4","system":"http://loinc.org"}}} as Record<string, unknown>
const value = applySliceMatch((input ?? {}) as Record<string, unknown>, match) as unknown as ObservationComponent
const list = (this.resource.component ??= [])
Expand All @@ -179,7 +179,7 @@ export class observation_bpProfile {
return this
}

public getVscat (): Observation_bp_Category_VSCatSliceInput | undefined {
public getVSCat (): Observation_bp_Category_VSCatSliceInput | undefined {
const match = {"coding":{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}} as Record<string, unknown>
const list = this.resource.category
if (!list) return undefined
Expand All @@ -188,15 +188,15 @@ export class observation_bpProfile {
return extractSliceSimplified(item as unknown as Record<string, unknown>, ["coding"]) as Observation_bp_Category_VSCatSliceInput
}

public getVscatRaw (): CodeableConcept | undefined {
public getVSCatRaw (): CodeableConcept | undefined {
const match = {"coding":{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}} as Record<string, unknown>
const list = this.resource.category
if (!list) return undefined
const item = list.find((item) => matchesSlice(item, match))
return item
}

public getSystolicBp (): Observation_bp_Component_SystolicBPSliceInput | undefined {
public getSystolicBP (): Observation_bp_Component_SystolicBPSliceInput | undefined {
const match = {"code":{"coding":{"code":"8480-6","system":"http://loinc.org"}}} as Record<string, unknown>
const list = this.resource.component
if (!list) return undefined
Expand All @@ -205,15 +205,15 @@ export class observation_bpProfile {
return extractSliceSimplified(item as unknown as Record<string, unknown>, ["code"]) as Observation_bp_Component_SystolicBPSliceInput
}

public getSystolicBpRaw (): ObservationComponent | undefined {
public getSystolicBPRaw (): ObservationComponent | undefined {
const match = {"code":{"coding":{"code":"8480-6","system":"http://loinc.org"}}} as Record<string, unknown>
const list = this.resource.component
if (!list) return undefined
const item = list.find((item) => matchesSlice(item, match))
return item
}

public getDiastolicBp (): Observation_bp_Component_DiastolicBPSliceInput | undefined {
public getDiastolicBP (): Observation_bp_Component_DiastolicBPSliceInput | undefined {
const match = {"code":{"coding":{"code":"8462-4","system":"http://loinc.org"}}} as Record<string, unknown>
const list = this.resource.component
if (!list) return undefined
Expand All @@ -222,7 +222,7 @@ export class observation_bpProfile {
return extractSliceSimplified(item as unknown as Record<string, unknown>, ["code"]) as Observation_bp_Component_DiastolicBPSliceInput
}

public getDiastolicBpRaw (): ObservationComponent | undefined {
public getDiastolicBPRaw (): ObservationComponent | undefined {
const match = {"code":{"coding":{"code":"8462-4","system":"http://loinc.org"}}} as Record<string, unknown>
const list = this.resource.component
if (!list) return undefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export class observation_vitalsignsProfile {
return this.resource as observation_vitalsigns
}

public setVscat (input?: Observation_vitalsigns_Category_VSCatSliceInput): this {
public setVSCat (input?: Observation_vitalsigns_Category_VSCatSliceInput): this {
const match = {"coding":{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}} as Record<string, unknown>
const value = applySliceMatch((input ?? {}) as Record<string, unknown>, match) as unknown as CodeableConcept
const list = (this.resource.category ??= [])
Expand All @@ -131,7 +131,7 @@ export class observation_vitalsignsProfile {
return this
}

public getVscat (): Observation_vitalsigns_Category_VSCatSliceInput | undefined {
public getVSCat (): Observation_vitalsigns_Category_VSCatSliceInput | undefined {
const match = {"coding":{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}} as Record<string, unknown>
const list = this.resource.category
if (!list) return undefined
Expand All @@ -140,7 +140,7 @@ export class observation_vitalsignsProfile {
return extractSliceSimplified(item as unknown as Record<string, unknown>, ["coding"]) as Observation_vitalsigns_Category_VSCatSliceInput
}

public getVscatRaw (): CodeableConcept | undefined {
public getVSCatRaw (): CodeableConcept | undefined {
const match = {"coding":{"code":"vital-signs","system":"http://terminology.hl7.org/CodeSystem/observation-category"}} as Record<string, unknown>
const list = this.resource.category
if (!list) return undefined
Expand Down
30 changes: 15 additions & 15 deletions examples/typescript-r4/profile-bodyweight.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,45 +117,45 @@ describe("bodyweight profile slice accessors", () => {
subject: { reference: "Patient/pt-1" },
});

test("getVscat returns empty simplified view from auto-populated stub", () => {
test("getVSCat returns empty simplified view from auto-populated stub", () => {
// category is auto-populated with VSCat discriminator match
expect(profile.getVscatRaw()).toBeDefined();
const raw = profile.getVscatRaw()!;
expect(profile.getVSCatRaw()).toBeDefined();
const raw = profile.getVSCatRaw()!;
expect(raw.coding as unknown).toEqual({
code: "vital-signs",
system: "http://terminology.hl7.org/CodeSystem/observation-category",
});
// simplified view strips discriminator keys, leaving empty object
expect(profile.getVscat()).toEqual({});
expect(profile.getVSCat()).toEqual({});
});

test("setVscat adds category with discriminator values", () => {
profile.setVscat({ text: "Vital Signs" });
test("setVSCat adds category with discriminator values", () => {
profile.setVSCat({ text: "Vital Signs" });

const raw = profile.getVscatRaw()!;
const raw = profile.getVSCatRaw()!;
expect(raw.text).toBe("Vital Signs");
expect(raw.coding as unknown).toEqual({
code: "vital-signs",
system: "http://terminology.hl7.org/CodeSystem/observation-category",
});
});

test("getVscat returns simplified view without discriminator", () => {
const simplified = profile.getVscat();
test("getVSCat returns simplified view without discriminator", () => {
const simplified = profile.getVSCat();
expect(simplified).toEqual({ text: "Vital Signs" });
expect("coding" in simplified!).toBe(false);
});

test("getVscatRaw returns full element including discriminator", () => {
const raw = profile.getVscatRaw()!;
test("getVSCatRaw returns full element including discriminator", () => {
const raw = profile.getVSCatRaw()!;
expect(raw.text).toBe("Vital Signs");
expect(raw.coding).toBeDefined();
});

test("setVscat replaces existing slice element", () => {
profile.setVscat({ text: "Updated" });
test("setVSCat replaces existing slice element", () => {
profile.setVSCat({ text: "Updated" });

expect(profile.getVscat()!.text).toBe("Updated");
expect(profile.getVSCat()!.text).toBe("Updated");
expect(profile.toResource().category!.length).toBe(1);
});
});
Expand Down Expand Up @@ -228,7 +228,7 @@ describe("bodyweight profile mutability", () => {
profile.setStatus("amended");
expect(obs.status).toBe("amended");

profile.setVscat({ text: "Vital Signs" });
profile.setVSCat({ text: "Vital Signs" });
expect(obs.category!.length).toBe(1);
});
});
58 changes: 29 additions & 29 deletions examples/typescript-r4/profile-bp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,35 +39,35 @@ describe("blood pressure profile", () => {
const obs = fresh.toResource();
expect(obs.component).toHaveLength(2);
// stubs contain only discriminator match values
expect(fresh.getSystolicBpRaw()).toBeDefined();
expect(fresh.getDiastolicBpRaw()).toBeDefined();
expect(fresh.getSystolicBPRaw()).toBeDefined();
expect(fresh.getDiastolicBPRaw()).toBeDefined();
});

test("setSystolicBp / getSystolicBp / getSystolicBpRaw", () => {
profile.setSystolicBp({
test("setSystolicBP / getSystolicBP / getSystolicBPRaw", () => {
profile.setSystolicBP({
valueQuantity: { value: 120, unit: "mmHg", system: "http://unitsofmeasure.org", code: "mm[Hg]" },
});

expect(profile.getSystolicBp()).toEqual({
expect(profile.getSystolicBP()).toEqual({
valueQuantity: { value: 120, unit: "mmHg", system: "http://unitsofmeasure.org", code: "mm[Hg]" },
});

expect(profile.getSystolicBpRaw() as unknown).toEqual({
expect(profile.getSystolicBPRaw() as unknown).toEqual({
valueQuantity: { value: 120, unit: "mmHg", system: "http://unitsofmeasure.org", code: "mm[Hg]" },
code: { coding: { code: "8480-6", system: "http://loinc.org" } },
});
});

test("setDiastolicBp / getDiastolicBp / getDiastolicBpRaw", () => {
profile.setDiastolicBp({
test("setDiastolicBP / getDiastolicBP / getDiastolicBPRaw", () => {
profile.setDiastolicBP({
valueQuantity: { value: 80, unit: "mmHg", system: "http://unitsofmeasure.org", code: "mm[Hg]" },
});

expect(profile.getDiastolicBp()).toEqual({
expect(profile.getDiastolicBP()).toEqual({
valueQuantity: { value: 80, unit: "mmHg", system: "http://unitsofmeasure.org", code: "mm[Hg]" },
});

expect(profile.getDiastolicBpRaw() as unknown).toEqual({
expect(profile.getDiastolicBPRaw() as unknown).toEqual({
valueQuantity: { value: 80, unit: "mmHg", system: "http://unitsofmeasure.org", code: "mm[Hg]" },
code: { coding: { code: "8462-4", system: "http://loinc.org" } },
});
Expand All @@ -78,25 +78,25 @@ describe("blood pressure profile", () => {
// auto-populated stubs + set values = still 2 items (set replaces stubs)
expect(obs.component).toHaveLength(2);

const systolicCode = profile.getSystolicBpRaw()!.code as Record<string, unknown>;
const diastolicCode = profile.getDiastolicBpRaw()!.code as Record<string, unknown>;
const systolicCode = profile.getSystolicBPRaw()!.code as Record<string, unknown>;
const diastolicCode = profile.getDiastolicBPRaw()!.code as Record<string, unknown>;
expect(systolicCode.coding).toEqual({ code: "8480-6", system: "http://loinc.org" });
expect(diastolicCode.coding).toEqual({ code: "8462-4", system: "http://loinc.org" });
expect(profile.getSystolicBpRaw()!.valueQuantity!.value).toBe(120);
expect(profile.getDiastolicBpRaw()!.valueQuantity!.value).toBe(80);
expect(profile.getSystolicBPRaw()!.valueQuantity!.value).toBe(120);
expect(profile.getDiastolicBPRaw()!.valueQuantity!.value).toBe(80);
});

test("setSystolicBp replaces an existing systolic component", () => {
profile.setSystolicBp({ valueQuantity: { value: 130, unit: "mmHg" } });
test("setSystolicBP replaces an existing systolic component", () => {
profile.setSystolicBP({ valueQuantity: { value: 130, unit: "mmHg" } });

expect(profile.toResource().component).toHaveLength(2);
expect(profile.getSystolicBpRaw()!.valueQuantity!.value).toBe(130);
expect(profile.getSystolicBPRaw()!.valueQuantity!.value).toBe(130);
});

test("setVscat adds category with discriminator values", () => {
profile.setVscat({ text: "Vital Signs" });
test("setVSCat adds category with discriminator values", () => {
profile.setVSCat({ text: "Vital Signs" });

const raw = profile.getVscatRaw()!;
const raw = profile.getVSCatRaw()!;
expect(raw.text).toBe("Vital Signs");
expect(raw.coding as unknown).toEqual({
code: "vital-signs",
Expand All @@ -113,26 +113,26 @@ describe("blood pressure profile", () => {
test("fluent chaining across all accessor types", () => {
const result = profile
.setStatus("final")
.setVscat({ text: "Vital Signs" })
.setVSCat({ text: "Vital Signs" })
.setEffectiveDateTime("2024-06-15")
.setSubject({ reference: "Patient/pt-2" })
.setSystolicBp({ valueQuantity: { value: 120, unit: "mmHg" } })
.setDiastolicBp({ valueQuantity: { value: 80, unit: "mmHg" } });
.setSystolicBP({ valueQuantity: { value: 120, unit: "mmHg" } })
.setDiastolicBP({ valueQuantity: { value: 80, unit: "mmHg" } });

expect(result).toBe(profile);
expect(profile.getStatus()).toBe("final");
expect(profile.getVscat()!.text).toBe("Vital Signs");
expect(profile.getVSCat()!.text).toBe("Vital Signs");
expect(profile.getEffectiveDateTime()).toBe("2024-06-15");
expect(profile.getSubject()!.reference).toBe("Patient/pt-2");
expect(profile.getSystolicBpRaw()!.valueQuantity!.value).toBe(120);
expect(profile.getDiastolicBpRaw()!.valueQuantity!.value).toBe(80);
expect(profile.getSystolicBPRaw()!.valueQuantity!.value).toBe(120);
expect(profile.getDiastolicBPRaw()!.valueQuantity!.value).toBe(80);
});

test("setSystolicBp with no args inserts discriminator-only component", () => {
test("setSystolicBP with no args inserts discriminator-only component", () => {
const fresh = createBp();
fresh.setSystolicBp();
fresh.setSystolicBP();

const raw = fresh.getSystolicBpRaw()!;
const raw = fresh.getSystolicBPRaw()!;
const rawCode = raw.code as Record<string, unknown>;
expect(rawCode.coding).toEqual({ code: "8480-6", system: "http://loinc.org" });
expect(raw.valueQuantity).toBeUndefined();
Expand Down
7 changes: 3 additions & 4 deletions src/api/writer-generator/typescript/name.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import type { TypeSchemaIndex } from "@root/typeschema/utils";
// biome-ignore format: too long
const tsKeywords = new Set([ "class", "function", "return", "if", "for", "while", "const", "let", "var", "import", "export", "interface" ]);

const normalizeTsName = (n: string): string => {
export const normalizeTsName = (n: string): string => {
if (tsKeywords.has(n)) n = `${n}_`;
return n.replace(/\[x\]/g, "_x_").replace(/[- :.]/g, "_");
};
Expand Down Expand Up @@ -100,8 +100,7 @@ export const tsExtensionInputTypeName = (profileName: string, extensionName: str
};

export const tsSliceMethodName = (sliceName: string): string => {
const normalized = tsCamelCase(sliceName);
return `set${uppercaseFirstLetter(normalized || "Slice")}`;
return `set${uppercaseFirstLetter(normalizeTsName(sliceName) || "Slice")}`;
};

export const tsExtensionMethodName = (name: string): string => {
Expand All @@ -122,6 +121,6 @@ export const tsQualifiedExtensionMethodName = (name: string, path?: string): str

export const tsQualifiedSliceMethodName = (fieldName: string, sliceName: string): string => {
const fieldPart = uppercaseFirstLetter(tsCamelCase(fieldName) || "Field");
const slicePart = uppercaseFirstLetter(tsCamelCase(sliceName) || "Slice");
const slicePart = uppercaseFirstLetter(normalizeTsName(sliceName) || "Slice");
return `setSlice${fieldPart}${slicePart}`;
};
3 changes: 2 additions & 1 deletion src/api/writer-generator/typescript/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
} from "@root/typeschema/types";
import type { TypeSchemaIndex } from "@root/typeschema/utils";
import {
normalizeTsName,
tsCamelCase,
tsExtensionInputTypeName,
tsExtensionMethodName,
Expand Down Expand Up @@ -1079,7 +1080,7 @@ export const generateProfileClass = (
// 1. get{SliceName}() - returns simplified (without discriminator fields)
// 2. get{SliceName}Raw() - returns full FHIR type with all fields
for (const sliceDef of sliceDefs) {
const baseName = uppercaseFirstLetter(tsCamelCase(sliceDef.sliceName));
const baseName = uppercaseFirstLetter(normalizeTsName(sliceDef.sliceName));
const getMethodName = `get${baseName}`;
const getRawMethodName = `get${baseName}Raw`;
if (generatedGetMethods.has(getMethodName)) continue;
Expand Down
Loading