Skip to content

Conversation

leite08
Copy link
Member

@leite08 leite08 commented Aug 15, 2025

Dependencies

See below

Description

Merging these PRs into develop:

Testing

See each PR ^.

Release Plan

  • Merge this

@coderabbitai ignore

leite08 added 30 commits July 15, 2025 16:02
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref: #000

 - api@1.27.15-alpha.0
 - @metriport/api-sdk@17.2.0-alpha.0
 - @metriport/carequality-cert-runner@1.18.16-alpha.0
 - @metriport/commonwell-cert-runner@1.26.18-alpha.0
 - @metriport/commonwell-jwt-maker@1.25.0-alpha.0
 - @metriport/commonwell-sdk@6.0.0-alpha.0
 - @metriport/core@1.25.0-alpha.0
 - @metriport/ihe-gateway-sdk@0.19.16-alpha.0
 - @metriport/shared@0.25.0-alpha.0
 - utils@1.25.14-alpha.0

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref: #000

 - api@1.27.16-alpha.0
 - @metriport/api-sdk@17.2.1-alpha.0
 - @metriport/carequality-cert-runner@1.18.17-alpha.0
 - @metriport/carequality-sdk@1.7.1-alpha.0
 - @metriport/commonwell-cert-runner@2.0.0-alpha.0
 - @metriport/commonwell-jwt-maker@1.25.1-alpha.0
 - @metriport/commonwell-sdk@6.0.1-alpha.0
 - @metriport/core@1.25.1-alpha.0
 - @metriport/eslint-plugin-eslint-rules@1.0.7-alpha.0
 - @metriport/fhir-sdk@1.0.6-alpha.0
 - @metriport/ihe-gateway-sdk@0.19.17-alpha.0
 - infrastructure@1.22.15-alpha.0
 - mllp-server@0.3.15-alpha.0
 - @metriport/shared@0.25.1-alpha.0
 - utils@1.25.15-alpha.0

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref: #000

 - api@1.27.15-alpha.0
 - @metriport/api-sdk@17.2.0-alpha.0
 - @metriport/carequality-cert-runner@1.18.16-alpha.0
 - @metriport/commonwell-cert-runner@1.26.18-alpha.0
 - @metriport/commonwell-jwt-maker@1.25.0-alpha.0
 - @metriport/commonwell-sdk@6.0.0-alpha.0
 - @metriport/core@1.25.0-alpha.0
 - @metriport/ihe-gateway-sdk@0.19.16-alpha.0
 - @metriport/shared@0.25.0-alpha.0
 - utils@1.25.14-alpha.0

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
Copy link

linear bot commented Aug 15, 2025

Copy link

coderabbitai bot commented Aug 15, 2025

Walkthrough

Repository-wide migration of CommonWell imports to a versioned v1 namespace, introduction of a new CommonWell v2 adapter and mocks, substantial refactor and expansion of commonwell-cert-runner (new flows, CLI, server, payloads), major SDK API redesign (clients, interfaces, models), and supporting shared/zod utilities reorganization. Minor config/docs and dependency updates included.

Changes

Cohort / File(s) Summary
CW v1 import migration
packages/api/src/... commonwell-v1*, packages/core/src/... commonwell-v1*, packages/lambdas/src/... commonwell-v1*, packages/utils/src/... commonwell-v1*
Updated imports from non-versioned CommonWell modules to commonwell-v1 equivalents across commands, routes, tests, proxies, and utilities. No runtime logic changes.
Add CommonWell v2 adapter + mocks
packages/api/src/external/commonwell-v2/api.ts, .../mock/api-mock.ts, .../mock/member-mock.ts, .../mock/payloads.ts
New v2 factories for member/org APIs and certificate accessor; mock implementations for API and member operations; helper payload builders.
Cert runner refactor (2.0 alpha)
packages/commonwell-cert-runner/* (README.md, package.json, src/flows/, src/single-commands/, src/env.ts, src/util.ts, src/payloads/, src/flows/contribution/), deletions of legacy src/*
Reworked into flow-based runner with new org/patient/link/document flows, contribution HTTP server, utilities, payloads, and CLI single-commands. Removed legacy person/link/doc modules and test/data artifacts.
SDK client/API overhaul
packages/commonwell-sdk/src/client/* (common.ts, commonwell.ts, commonwell-api.ts, commonwell-member*.ts), removed client/document.ts
New options-based clients, member API, richer patient/link/document methods, new metadata types, updated JWT maker, in-memory/stream download utilities. Removed legacy document client.
SDK models restructuring
packages/commonwell-sdk/src/models/* (address, contact, date, demographics, document, identifier, organization, patient, patient-organization, period, reference), removals (enrollment-summary.ts, person.ts, link.ts, iso-*.ts), tests updates
Standardized schemas (nullish/normalizers), new/changed enums, DocumentReference/Bundle shapes, identifier split (patient vs strong), org union types, patient collections/probable links, added reference schema; removed legacy Person/Link models.
Shared zod/date/string reorg
packages/shared/src/external/zod/*, packages/shared/src/index.ts, packages/shared/src/util/zod.ts, .../util/index.ts, .../common/date.ts, tests
Introduced external zod modules (date/string/zip), exposed via shared index; util/zod now proxies external exports; added DOB validator; added OID helper and tests.
Docs and configs
.gitleaksignore, packages/commonwell-sdk/README.md, packages/commonwell-cert-runner/README.md
Added gitleaks ignore entry; updated SDK README to new constructors; expanded cert-runner docs.
Package manifests
packages/api/package.json, packages/core/package.json, packages/commonwell-sdk/package.json, packages/commonwell-jwt-maker/package.json, packages/lambdas/package.json, packages/utils/package.json, packages/commonwell-cert-runner/package.json
Added alias @metriport/commonwell-sdk-v1, version bumps, dependency adjustments (mime-types, jsonwebtoken), local SDK reference for cert-runner.
Minor behavior tweaks
packages/lambdas/src/sqs-to-opensearch-xml.ts, packages/core/src/external/commonwell-v1/document/document-contribution.ts
Added duration log and refactor of timing; updated contribution URL path to commonwell-v1.

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant CLI as Cert Runner CLI
  participant OM as OrgManagement (Member API)
  participant CW as CommonWell (Org API)

  User->>CLI: Run runner
  CLI->>OM: Create or load Organization (certs, NPI)
  OM-->>CLI: OrgManagementResponse (CommonWell client)
  CLI->>CW: Execute flows (patient/link/doc)
  CW-->>CLI: Results (IDs, links, docs)
  CLI-->>User: Report success/fail per flow
Loading
sequenceDiagram
  participant Cons as Contributor (CommonWell)
  participant Srv as ContributionServer
  participant Cw as CommonWell Consumer
  participant FS as File System

  Cw->>Srv: POST /oauth/token (Basic)
  Srv-->>Cw: access_token (Bearer)
  Cw->>Srv: GET /oauth/fhir/DocumentReference?patient=...
  Srv-->>Cw: Bundle[DocumentReference(url=/Binary/:id)]
  Cw->>Srv: GET /oauth/fhir/Binary/:id (Bearer)
  Srv-->>Cw: Binary(data)
  Cw->>FS: Save contents
Loading
sequenceDiagram
  participant CW as CommonWell API
  participant Flow as DocumentConsumption Flow
  participant FS as File System

  Flow->>CW: createOrUpdatePatient
  CW-->>Flow: PatientCollection (id)
  Flow->>CW: queryDocuments(patientId)
  CW-->>Flow: DocumentReference[]
  loop docs
    Flow->>CW: retrieveDocument(url, stream)
    CW-->>Flow: {contentType, size}
    Flow->>FS: Write file(s)
  end
  Flow->>CW: deletePatient(ids)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120+ minutes

Possibly related PRs

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch eng-200-cw-v2_merge-branch

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Ref eng-200

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
…repo

ENG-513 Move CW folders to commonwell-v1 on api and core
@leite08 leite08 changed the title ENG-xxx ...WIP ENG-200 Ship new CW SDK v2 Aug 15, 2025
@leite08 leite08 marked this pull request as ready for review August 15, 2025 16:21
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 49

🔭 Outside diff range comments (17)
packages/commonwell-jwt-maker/package.json (1)

13-15: Add a tag to publishConfig for pre-releases

To prevent this alpha from being published under latest, update packages/commonwell-jwt-maker/package.json:

"publishConfig": {
  "access": "public",
+ "tag": "alpha"
},

No other packages currently define a publishConfig.tag, so this introduces the convention for alpha builds. If your release pipeline already applies a tag, you can ignore this.

packages/api/src/external/commonwell-v1/admin/recreate-patients-at-hies.ts (1)

150-153: Bug: Unconditionally deleting the “old” patient can delete the newly created patient

After creating the new CW patient, the code deletes originalCWPatientId unconditionally, even when originalCWPatientId === newCWPatientId. In that case, you’d delete the patient you just created/updated.

Guard the deletion:

-      // remove old patient
-      log(`Deleting old patient from CW...`);
-      await commonWell.deletePatient(queryMeta, originalCWPatientId);
+      // remove old patient only if IDs differ
+      if (originalCWPatientId !== newCWPatientId) {
+        log(`Deleting old patient from CW...`);
+        await commonWell.deletePatient(queryMeta, originalCWPatientId);
+      } else {
+        log(`Skipping deletion: original and new CW patient IDs match.`);
+      }
packages/api/src/external/commonwell-v1/mock.ts (1)

393-395: Method signature mismatch: resetPatientLink missing parameters

CommonWellAPI.resetPatientLink is invoked elsewhere as resetPatientLink(meta, personId, patientId), but the mock defines it with no parameters. This risks interface non-compliance and runtime confusion in tests.

Align the signature:

-  async resetPatientLink(): Promise<void> {
+  async resetPatientLink(
+    meta: sdk.RequestMetadata,
+    personId: string,
+    patientId: string
+  ): Promise<void> {
     return;
   }
packages/api/src/external/commonwell-v1/link/shared.ts (3)

100-100: Fix logical operator usage in filter condition.

The current expression isLOLA2 || isLOLA3 evaluates to true due to both functions being truthy, causing all network links to pass the filter instead of filtering for LOLA2 or LOLA3 links.

Apply this fix to use the logical OR correctly:

-    const validLola2or3Links = networkLinks.flatMap(filterTruthy).filter(isLOLA2 || isLOLA3);
+    const validLola2or3Links = networkLinks.flatMap(filterTruthy).filter(link => isLOLA2(link) || isLOLA3(link));

101-101: Fix logical operator usage in filter condition.

Same issue as Line 100 - the expression isLOLA2 || isLOLA3 will always evaluate to true, causing incorrect filtering behavior.

Apply this fix:

-    const invalidLola2or3Links = invalidLinks.flatMap(filterTruthy).filter(isLOLA2 || isLOLA3);
+    const invalidLola2or3Links = invalidLinks.flatMap(filterTruthy).filter(link => isLOLA2(link) || isLOLA3(link));

216-216: Improve type safety and handle undefined gender code.

The current code accesses patient?.details.gender.code without null-checking the gender property, which could cause runtime errors if gender is undefined.

Apply this fix to improve safety:

-  const genderCode = patient?.details.gender.code;
+  const genderCode = patient?.details.gender?.code;
packages/api/src/external/commonwell-v1/proxy/cw-process-request.ts (1)

42-42: Bug: _count filter is silently ignored

allowedQueryParams excludes "_count" and applyFilterParams reads "count" instead of "_count". As a result, the requested count limit is never applied.

Apply:

-const allowedQueryParams = ["status", "date", "_summary"];
+const allowedQueryParams = ["status", "date", "_summary", "_count"];
 export function applyFilterParams(
   docRefs: DocumentReference[],
   params: URLSearchParams
 ): DocumentReference[] {
   const date = params.get("date");
-  const countParam = params.get("count");
+  const countStr = params.get("_count");
   const status = params.get("status");
@@
-  if (countParam) {
-    const count = parseInt(countParam);
+  if (countStr) {
+    const count = parseInt(countStr);
     if (!isNaN(count)) {
       filteredDocRefs = filteredDocRefs.slice(0, count);
     }
   }

Also applies to: 203-249

packages/lambdas/src/cw-enhanced-coverage-link-patients.ts (1)

118-131: Bug: patientIds and cqOrgIds validations are inverted (should be arrays), breaking Input contract

The lambda expects lists of patient IDs and CQ org IDs (per the file’s description and typical Input shape for link flows). Current checks reject arrays and allow non-arrays — this will cause runtime failures and/or incorrect payloads passed downstream.

Fix the validations to accept non-empty arrays and ensure 'done' is boolean. Also prefer MetriportError with additional context, consistent with your error handling guidelines.

Apply this diff:

-  const patientIds = body.patientIds;
-  if (!patientIds) throw new Error(`Missing patientIds in body`);
-  if (Array.isArray(patientIds)) throw new Error(`Invalid patientIds in body`);
+  const patientIds = body.patientIds;
+  if (!Array.isArray(patientIds) || patientIds.length === 0) {
+    throw new MetriportError("Invalid patientIds in body", undefined, { body: stringify(body) });
+  }
 
-  const cqOrgIds = body.cqOrgIds;
-  if (!cqOrgIds) throw new Error(`Missing cqOrgIds in body`);
-  if (Array.isArray(cqOrgIds)) throw new Error(`Invalid cqOrgIds in body`);
+  const cqOrgIds = body.cqOrgIds;
+  if (!Array.isArray(cqOrgIds) || cqOrgIds.length === 0) {
+    throw new MetriportError("Invalid cqOrgIds in body", undefined, { body: stringify(body) });
+  }
 
-  const done = body.done;
-  if (done == undefined) throw new Error(`Missing 'done' in body`);
+  const done = body.done;
+  if (typeof done !== "boolean") {
+    throw new MetriportError("Missing or invalid 'done' in body", undefined, { body: stringify(body) });
+  }
 
   if (done) return { cxId, cxOrgOID, patientIds, done };
   return { cxId, cxOrgOID, patientIds, cqOrgIds, done };
packages/shared/src/common/date.ts (1)

42-53: validateDateOfBirth may throw instead of returning false (uses throwing validator by default)

The function claims to return true/false, but the default validateIsPastOrPresent throws on future dates. This will propagate an exception instead of returning false.

Use the safe validator by default and guard against thrown validators.

Apply this diff:

   const {
-    validateDateIsAfter1900: _validateDateIsAfter1900 = validateDateIsAfter1900,
-    validateIsPastOrPresent: _validateIsPastOrPresent = validateIsPastOrPresent,
+    validateDateIsAfter1900: _validateDateIsAfter1900 = validateDateIsAfter1900,
+    validateIsPastOrPresent: _validateIsPastOrPresent = validateIsPastOrPresentSafe,
   } = options || {};
-  return (
-    _validateDateIsAfter1900(parsedDate.format(ISO_DATE)) &&
-    _validateIsPastOrPresent(parsedDate.format(ISO_DATE))
-  );
+  const iso = parsedDate.format(ISO_DATE);
+  const after1900 = _validateDateIsAfter1900(iso);
+  let pastOrPresent = false;
+  try {
+    pastOrPresent = _validateIsPastOrPresent(iso);
+  } catch {
+    pastOrPresent = false;
+  }
+  return after1900 && pastOrPresent;

If you want, I can add unit tests covering valid DOB, future date, invalid strings, and override hooks.

packages/api/src/external/hie/set-doc-query-progress.ts (1)

215-224: Fix aggregation precedence: failed should override completed

Current order returns "completed" even if any source failed (as long as some completed). Prefer:

  • processing > failed > completed

Apply this diff:

 export function aggregateStatus(docQueryProgress: DocumentQueryStatus[]): DocumentQueryStatus {
   const hasProcessing = docQueryProgress.some(status => status === "processing");
   const hasFailed = docQueryProgress.some(status => status === "failed");
   const hasCompleted = docQueryProgress.some(status => status === "completed");

-  if (hasProcessing) return "processing";
-  if (hasCompleted) return "completed";
-  if (hasFailed) return "failed";
-  return "completed";
+  if (hasProcessing) return "processing";
+  if (hasFailed) return "failed";
+  if (hasCompleted) return "completed";
+  return "completed";
 }
packages/api/src/external/fhir-converter/converter.ts (1)

27-30: Use strict checks; avoid abstract inequality

Per guidelines, avoid !=. Also no need to union with undefined in the param type.

Apply this diff:

-export function isConvertible(mimeType?: string | undefined): boolean {
-  // TODO move to core's isMimeTypeXML()
-  return mimeType != null && ["text/xml", "application/xml"].includes(mimeType);
-}
+export function isConvertible(mimeType?: string): boolean {
+  // TODO move to core's isMimeTypeXML()
+  return typeof mimeType === "string" && ["text/xml", "application/xml"].includes(mimeType);
+}
packages/api/src/external/commonwell-v1/document/shared.ts (1)

26-35: Fix filename composition to avoid 'undefined' segment and extra underscore

If display is undefined, the template currently injects the string "undefined" into the filename and also always adds an underscore after the prefix. Make the display segment optional and only append the trailing underscore when present.

Apply this diff:

 export function getFileName(patient: Patient, doc: Document): string {
   const prefix = "document_" + patient.id;
-  const display = doc.content?.type?.coding?.length
-    ? doc.content?.type.coding[0]?.display
-    : undefined;
+  const display = doc.content?.type?.coding?.[0]?.display?.trim();
   const suffix = getSuffix(doc.id);
   const extension = getFileExtension(doc.content?.mimeType);
-  const fileName = `${prefix}_${display ? display + "_" : display}${suffix}${extension}`;
+  const displaySegment = display ? `${display}_` : "";
+  const fileName = `${prefix}_${displaySegment}${suffix}${extension}`;
   return fileName.replace(/\s/g, "-");
 }
packages/utils/src/commonwell/cq-org-builder.ts (1)

4-10: Update resultFilename to use v1 path in CQ Org Builder

The builder is currently writing to the non-versioned JSON, but the v1 get-orgs importer reads from the versioned folder. We need to align them:

• File: packages/utils/src/commonwell/cq-org-builder.ts
– Line 34: change the output path to point at commonwell-v1 instead of commonwell

Suggested diff:

--- a/packages/utils/src/commonwell/cq-org-builder.ts
+++ b/packages/utils/src/commonwell/cq-org-builder.ts
@@ 34c34
-const resultFilename = `../../packages/core/src/external/commonwell/cq-bridge/cq-org-list.json`;
+const resultFilename = `../../packages/core/src/external/commonwell-v1/cq-bridge/cq-org-list.json`;

After updating, rerun the builder and verify that packages/core/src/external/commonwell-v1/cq-bridge/cq-org-list.json is correctly overwritten and in sync with getOrgs.

packages/api/src/external/commonwell-v1/document/document-query.ts (1)

314-316: Use organization name with organizationQueryMeta in document-query.ts

The vast majority of v1 call sites pass the org name (e.g. initiator.name) into organizationQueryMeta, and core tests assume that signature. In document-query.ts you’re passing the raw OID instead, which is inconsistent and likely incorrect.

• File: packages/api/src/external/commonwell-v1/document/document-query.ts
Line 315

Suggested change:

- const queryMeta = organizationQueryMeta(initiator.oid, { npi: initiator.npi });
+ const queryMeta = organizationQueryMeta(initiator.name, { npi: initiator.npi });
packages/lambdas/src/cw-session-management.ts (1)

50-55: Avoid logging secrets and credentials (security/PII concern)

Logging secret ARNs and usernames can leak sensitive information into CloudWatch/Sentry logs. Remove or mask these values.

Apply this diff to redact/remove sensitive logs:

-  console.log(
-    `Running with cookieSecretArn: ${cookieSecretArn}, ` +
-      `codeChallengeSecretArn: ${codeChallengeSecretArn}, ` +
-      `baseUrl: ${baseUrl}`
-  );
+  console.log(`Starting CW session management`, { baseUrl });
-    console.log(`...username ${username}, password 🤫`);
+    console.log(`Credentials loaded`);

Also applies to: 68-70

packages/commonwell-sdk/src/models/certificates.ts (1)

28-33: Remove _links from CertificateResp mocks and adjust tests
Since certificateRespSchema now only includes the certificates array, any mock or test that returns or asserts _links on a CertificateResp will fail. Please update all call sites accordingly:

  • packages/api/src/external/commonwell-v1/mock.ts
    • In the addCertificateToOrg mock, remove the _links property and return an object shaped like { certificates: [{ purpose: … }] }.
  • packages/api/src/external/commonwell-v1/tests/cw.register-facility.test.ts
    • Change the .mockImplementation(() => Promise.resolve({} as CertificateResp)) to return a valid CertificateResp, e.g. Promise.resolve({ certificates: [] }).
  • Audit packages/api for any other mocks or consumers that return or reference _links on certificate responses and update them to match the new schema.
packages/commonwell-cert-runner/src/env.ts (1)

25-26: Harden CONTRIB_SERVER_PORT parsing (radix + NaN check)

Ensure radix is specified and fail fast on invalid ports.

Apply this diff:

-export const contribServerPort = parseInt(getEnvOrFail("CONTRIB_SERVER_PORT"));
+const contribServerPortRaw = getEnvOrFail("CONTRIB_SERVER_PORT");
+export const contribServerPort = Number.parseInt(contribServerPortRaw, 10);
+if (Number.isNaN(contribServerPort)) {
+  throw new Error(`Invalid CONTRIB_SERVER_PORT: ${contribServerPortRaw}`);
+}

@@ -32,6 +32,7 @@
"@metriport/api-sdk": "file:packages/api-sdk",
"@metriport/carequality-sdk": "file:packages/carequality-sdk",
"@metriport/commonwell-sdk": "file:packages/commonwell-sdk",
"@metriport/commonwell-sdk-v1": "npm:@metriport/commonwell-sdk@^5.9.11",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Alias introduction for v1 SDK — verify import usage

Adding "@metriport/commonwell-sdk-v1" via the npm alias is the right approach to decouple v1 from the local v2 workspace. Please verify that all v1-intended imports use this alias and that no v1 code still imports "@metriport/commonwell-sdk".


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Imports of v1 alias (expected to be present):"
rg -nP -C2 'from\s+[\"\']@metriport/commonwell-sdk-v1[\"\']' --type ts

echo
echo "Stray imports to the unversioned SDK within v1 code (should be zero):"
rg -nP -C3 'from\s+[\"\']@metriport/commonwell-sdk[\"\']' --type ts \
  | gawk 'BEGIN{bad=0} /external\/commonwell-v1/ {print; bad=1} END{exit bad}'

Length of output: 238


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "🔍 Checking for imports of the v1 alias (@metriport/commonwell-sdk-v1):"
rg -nP -C2 "from\s+['\"]@metriport/commonwell-sdk-v1['\"]" --type ts || true

echo
echo "🔍 Checking for stray imports of the unversioned SDK (@metriport/commonwell-sdk):"
rg -nP -C2 "from\s+['\"]@metriport/commonwell-sdk['\"]" --type ts || true

Length of output: 30452


Alias introduction for v1 SDK — stray unversioned imports detected

We still have numerous imports of the unversioned @metriport/commonwell-sdk in code that targets v1. All v1-intended imports must use the new @metriport/commonwell-sdk-v1 alias. Please update the following areas:

• packages/commonwell-cert-runner/src/…
• packages/utils/src/commonwell/…
• packages/lambdas/src/document-downloader.ts
• packages/commonwell-jwt-maker/src/index.ts

For example, in each affected file:

-import { CommonWellAPI } from "@metriport/commonwell-sdk";
+import { CommonWellAPI } from "@metriport/commonwell-sdk-v1";

Once updated, verify there are no remaining @metriport/commonwell-sdk imports in v1 code.

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +5 to +7
import { CommonWell } from "@metriport/commonwell-sdk-v1/client/commonwell";
import { Organization } from "@metriport/commonwell-sdk-v1/models/organization";
import { CertificateResp } from "@metriport/commonwell-sdk-v1/models/certificates";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

CW SDK import migration to v1 looks consistent; please verify no lingering v0 imports

The switch to v1 paths for the CommonWell client and models aligns with the PR-wide migration.

Run this script to ensure there are no remaining imports from the non-v1 SDK across the repo:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Checking for any lingering imports from @metriport/commonwell-sdk (non -v1)..."
rg -nP '@metriport/commonwell-sdk(?!-v1)' -g '!**/node_modules/**' -S || true

echo "Checking for any remaining internal imports pointing to external/commonwell (non -v1)..."
rg -nP 'external/commonwell(?!-v1)' -S || true

Length of output: 12497


🔍 Remaining v0 CommonWell SDK imports found
I ran the grep script and discovered multiple lingering @metriport/commonwell-sdk references across the repo. To complete the v0→v1 migration, please update these to @metriport/commonwell-sdk-v1 (or document why they remain on v0):

• packages/utils/src/commonwell/coverage-assessment.ts
• packages/utils/src/commonwell/migrate-patients.ts
• packages/utils/src/commonwell/delete-network-links.ts
• packages/utils/src/commonwell/cq-org-builder.ts
• packages/lambdas/src/document-downloader.ts
• packages/commonwell-cert-runner/src//*.ts
• packages/commonwell-jwt-maker/src/index.ts
• packages/api/src/external/commonwell-v2/
/*.ts
• packages/shared/src/net/tests/axios.ts (fixture paths in compiled code)

🤖 Prompt for AI Agents
In
packages/api/src/external/commonwell-v1/__tests__/cw.register-facility.test.ts
around lines 5 to 7, the test still imports from the old
@metriport/commonwell-sdk package; update these imports to
@metriport/commonwell-sdk-v1 (or add a brief comment explaining why v0 must
remain) and then search the repo for any remaining @metriport/commonwell-sdk
references (the review lists specific files) and change them to
@metriport/commonwell-sdk-v1 or document exceptions; after changes, update
package.json dependencies if needed, run the grep/test suite to ensure no
lingering references, and fix any compiled fixture paths that still point to v0.

Comment on lines +1 to +5
import {
CommonWellAPI,
organizationQueryMeta,
RequestMetadata,
} from "@metriport/commonwell-sdk-v1";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Prefer type-only imports for types from the v1 SDK

CommonWellAPI and RequestMetadata are types; importing them with import type avoids pulling them into the runtime bundle and aligns with TS best practices. Keep organizationQueryMeta as a value import.

Apply this diff:

-import {
-  CommonWellAPI,
-  organizationQueryMeta,
-  RequestMetadata,
-} from "@metriport/commonwell-sdk-v1";
+import type { CommonWellAPI, RequestMetadata } from "@metriport/commonwell-sdk-v1";
+import { organizationQueryMeta } from "@metriport/commonwell-sdk-v1";

Run this script to ensure there are no lingering non-v1 SDK imports and that the v1 alias is present:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Searching for non-v1 CommonWell SDK imports..."
rg -nP -C2 --type ts --type tsx --type js \
  $'from\\s+[\'"]@metriport/commonwell-sdk(?!-v1)[\'"]|require\\([\'"]@metriport/commonwell-sdk(?!-v1)[\'"]\\)'

echo
echo "Verifying v1 alias/dependency is declared in packages/api/package.json..."
rg -nC2 '"@metriport/commonwell-sdk-v1"' packages/api/package.json || {
  echo "Missing @metriport/commonwell-sdk-v1 alias/dependency in packages/api/package.json"; exit 1;
}

Length of output: 308


I’ve updated the import-search to use glob patterns instead of unrecognized --type=tsx. Let’s re-run verification:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Searching for non-v1 CommonWell SDK imports in .ts, .tsx, .js files..."
rg -nP -C2 -g '*.ts' -g '*.tsx' -g '*.js' \
  'from\s+["'\'']@metriport/commonwell-sdk(?!-v1)["'\'']|require\(["'\'']@metriport/commonwell-sdk(?!-v1)["'\'']\)'

echo
echo "Verifying @metriport/commonwell-sdk-v1 is declared in packages/api/package.json..."
rg -nC2 '"@metriport/commonwell-sdk-v1"' packages/api/package.json || {
  echo "Missing @metriport/commonwell-sdk-v1 alias/dependency in packages/api/package.json"
  exit 1
}

Length of output: 13523


Prefer type-only imports for v1 SDK types

We’ve confirmed that in packages/api/src/external/commonwell-v1/admin/shared.ts there are no stray non-v1 imports and that "@metriport/commonwell-sdk-v1" is declared in packages/api/package.json. Please update the imports as follows to keep types out of the runtime bundle:

-import {
-  CommonWellAPI,
-  organizationQueryMeta,
-  RequestMetadata,
-} from "@metriport/commonwell-sdk-v1";
+import type { CommonWellAPI, RequestMetadata } from "@metriport/commonwell-sdk-v1";
+import { organizationQueryMeta } from "@metriport/commonwell-sdk-v1";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import {
CommonWellAPI,
organizationQueryMeta,
RequestMetadata,
} from "@metriport/commonwell-sdk-v1";
import type { CommonWellAPI, RequestMetadata } from "@metriport/commonwell-sdk-v1";
import { organizationQueryMeta } from "@metriport/commonwell-sdk-v1";
🤖 Prompt for AI Agents
In packages/api/src/external/commonwell-v1/admin/shared.ts around lines 1-5, the
current import from "@metriport/commonwell-sdk-v1" pulls types into the runtime
bundle; change it to a type-only import (use "import type { CommonWellAPI,
organizationQueryMeta, RequestMetadata } from
\"@metriport/commonwell-sdk-v1\";") so these symbols are erased at compile time
and do not end up in the runtime bundle.

Comment on lines +15 to +17
import { getAndUpdateCWOrgAndMetriportOrg } from "../../../external/commonwell-v1/command/create-or-update-cw-organization";
import { getParsedOrgOrFail } from "../../../external/commonwell-v1/organization";
import { cwOrgActiveSchema } from "../../../external/commonwell-v1/shared";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

LGTM: imports migrated to commonwell-v1.

Import paths look correct and scoped to v1.

To ensure there are no lingering imports to the unversioned path elsewhere, run:

Also applies to: 22-22


🏁 Script executed:

#!/bin/bash
# Find any remaining imports from external/commonwell (non-v1/v2)
rg -nP -C2 'external/commonwell(?!-v1|-v2)'

Length of output: 3220


Update remaining unversioned external/commonwell reference

A hard-coded path in packages/utils/src/commonwell/cq-org-builder.ts still points at the unversioned directory:

  • File: packages/utils/src/commonwell/cq-org-builder.ts
    Line 34:
    const resultFilename = `../../packages/core/src/external/commonwell/cq-bridge/cq-org-list.json`;
    This should be updated to reference the v1 path, for example:
    const resultFilename = `../../packages/core/src/external/commonwell-v1/cq-bridge/cq-org-list.json`;

Please update this (and any similar literals) to use the versioned directory.

🤖 Prompt for AI Agents
In packages/utils/src/commonwell/cq-org-builder.ts around line 34, the code uses
an unversioned relative path ("../../packages/core/src/external/commonwell/...")
for cq-bridge output; change the literal to point to the versioned directory
(e.g.
"../../packages/core/src/external/commonwell-v1/cq-bridge/cq-org-list.json").
Also search the repository for any other hard-coded
"../../packages/core/src/external/commonwell" literals and update them to
"../../packages/core/src/external/commonwell-v1" (or the appropriate v1 path) so
all references use the versioned directory.

Comment on lines +102 to +108
CW_MEMBER_CERTIFICATE="-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----"
COMMONWELL_MEMBER_OID=1.3.6.1.4.1.18.12.29.2022.945
COMMONWELL_ORG_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
CW_MEMBER_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----"
COMMONWELL_ORG_CERTIFICATE="-----BEGIN CERTIFICATE-----
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Prevent secret scanners from flagging the README and avoid multiline .env pitfalls.

Inline PEM examples trigger gitleaks and many .env loaders don’t support multiline values. Recommend base64 or file-path variables instead.

-CW_MEMBER_CERTIFICATE="-----BEGIN CERTIFICATE-----
-...
------END CERTIFICATE-----"
-CW_MEMBER_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
-...
------END PRIVATE KEY-----"
+# Prefer base64 or file paths to avoid multiline .env issues and secret scanners
+# Option A (base64):
+CW_MEMBER_CERTIFICATE_BASE64=REPLACE_WITH_BASE64_CERT
+CW_MEMBER_PRIVATE_KEY_BASE64=REPLACE_WITH_BASE64_KEY
+# Option B (file paths):
+CW_MEMBER_CERTIFICATE_PATH=/path/to/member-cert.pem
+CW_MEMBER_PRIVATE_KEY_PATH=/path/to/member-key.pem
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
CW_MEMBER_CERTIFICATE="-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----"
COMMONWELL_MEMBER_OID=1.3.6.1.4.1.18.12.29.2022.945
COMMONWELL_ORG_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
CW_MEMBER_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----"
COMMONWELL_ORG_CERTIFICATE="-----BEGIN CERTIFICATE-----
```
# Prefer base64 or file paths to avoid multiline .env issues and secret scanners
# Option A (base64):
CW_MEMBER_CERTIFICATE_BASE64=REPLACE_WITH_BASE64_CERT
CW_MEMBER_PRIVATE_KEY_BASE64=REPLACE_WITH_BASE64_KEY
# Option B (file paths):
CW_MEMBER_CERTIFICATE_PATH=/path/to/member-cert.pem
CW_MEMBER_PRIVATE_KEY_PATH=/path/to/member-key.pem
🤖 Prompt for AI Agents
In packages/commonwell-cert-runner/README.md around lines 102 to 108, the README
currently embeds inline PEM blocks for CW_MEMBER_CERTIFICATE and
CW_MEMBER_PRIVATE_KEY which can trigger secret scanners and break .env parsers;
replace these inline multiline PEM examples with either base64-encoded values or
examples that point to file paths (e.g., CW_MEMBER_CERTIFICATE_BASE64 or
CW_MEMBER_CERT_PATH) and update the usage notes to show how to decode the base64
into files or how to reference the file path at runtime, plus add a short note
recommending storing real secrets in a secure vault or environment-specific
files excluded from repo.

Comment on lines +92 to +94
const duration = Date.now() - ingestionStart;
metrics.ingestion = { duration, timestamp: new Date() };
log(`Done in ${duration} ms`);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix shadowing/redeclaration: local duration collides with imported dayjs plugin

You import the dayjs duration plugin (Line 7) and then redeclare const duration locally (Line 92). This will fail to compile with an identifier collision. Rename the local variable to avoid the clash and clarify units.

Apply this diff:

-      const duration = Date.now() - ingestionStart;
-      metrics.ingestion = { duration, timestamp: new Date() };
-      log(`Done in ${duration} ms`);
+      const ingestionDurationMs = Date.now() - ingestionStart;
+      metrics.ingestion = { duration: ingestionDurationMs, timestamp: new Date() };
+      log(`Done in ${ingestionDurationMs} ms`);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const duration = Date.now() - ingestionStart;
metrics.ingestion = { duration, timestamp: new Date() };
log(`Done in ${duration} ms`);
const ingestionDurationMs = Date.now() - ingestionStart;
metrics.ingestion = { duration: ingestionDurationMs, timestamp: new Date() };
log(`Done in ${ingestionDurationMs} ms`);
🤖 Prompt for AI Agents
In packages/lambdas/src/sqs-to-opensearch-xml.ts around lines 92 to 94, the
local const named `duration` collides with the imported dayjs `duration` plugin;
rename the local variable (for example to `ingestionDurationMs` or `durationMs`)
and update its usages (metrics.ingestion = { duration, ... } → metrics.ingestion
= { duration: ingestionDurationMs, ... } and log(`Done in ${duration} ms`) →
log(`Done in ${ingestionDurationMs} ms`)) so the identifier no longer shadows
the imported plugin and the units remain clear.

Comment on lines +25 to +33
const existingLevels = startFrom.split(".").map(Number);

if (existingLevels.length > amountOfLevels) {
throw new Error(
`startFrom OID has ${existingLevels.length} levels, but amountOfLevels is ${amountOfLevels}`
);
}

const levelsNeeded = amountOfLevels - existingLevels.length;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Validate startFrom components are integers and within range

Protect against non-numeric parts producing NaN and ensure arcs fall within the configured range.

Apply this diff:

-  const existingLevels = startFrom.split(".").map(Number);
+  const existingLevels = startFrom.split(".").map(part => {
+    const n = Number(part);
+    if (!Number.isInteger(n)) {
+      throw new Error(`startFrom contains a non-integer component: "${part}"`);
+    }
+    if (n < range.min || n > range.max) {
+      throw new Error(
+        `startFrom component ${n} is out of range [${range.min}, ${range.max}]`
+      );
+    }
+    return n;
+  });
🤖 Prompt for AI Agents
In packages/shared/src/common/__tests__/oid.ts around lines 25 to 33, validate
that each component of startFrom.split('.') is a numeric integer and within the
allowed arc range before computing levelsNeeded: parse each part with Number(),
ensure Number.isInteger(value) and not NaN, and if any part fails throw a clear
Error listing the offending component and index; additionally check each integer
is >= 0 and < the configured max arcs per level (replace "configured max arcs"
with the actual config variable used in the module) and throw if out of range;
only after these checks compute existingLevels and levelsNeeded.

Comment on lines +23 to +50
describe("invalid ISO dates", () => {
const invalidDates = [
"0195-01-01",
"2023-13-01", // invalid month
"2023-00-01", // invalid month
"2023-01-32", // invalid day
"2023-01-00", // invalid day
"2023-02-30", // invalid day for February
"2023-04-31", // invalid day for April
"2023-1-01", // missing leading zero in month
"2023-01-1", // missing leading zero in day
"2023/01/01", // wrong separator
"01-01-2023", // wrong format
// "2023-01-01T00:00:00Z", // includes time
// "2023-01-01 00:00:00", // includes time
"2023-1-1", // missing leading zeros
"23-01-01", // short year
"2023-01", // missing day
"2023", // missing month and day
"", // empty string
"invalid-date",
];

test.each(invalidDates)("should not match invalid ISO date: %s", date => {
expect(ISO_DATE_REGEX.test(date)).toBe(false);
expect(() => isoDateSchema.parse(date)).toThrow();
});
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Regex anchoring gap allows false positives (time suffixes, prefixes) — expand tests and fix regex

Current regexes accept substrings due to missing anchors/grouping:

  • ISO_DATE_REGEX lacks ^ and $ entirely (date.ts:10-11).
  • US_DATE_REGEX places ^ and $ only on the first/last alternation, not the whole alternation (date.ts:16-17), effectively behaving like ^A|B|C|D$.

This means strings like "2023-01-01T00:00:00Z" or "foo01/01/2023bar" can pass. Your test file currently comments out time-suffixed invalid cases, which hides this bug.

Action:

  1. Enable invalid cases with time suffixes in this test.
  2. Add prefix/suffix contamination cases.
  3. Anchor and group the regexes in date.ts.

Enable the "includes time" cases and add prefix/suffix contamination checks:

       const invalidDates = [
         "0195-01-01",
         "2023-13-01", // invalid month
         "2023-00-01", // invalid month
         "2023-01-32", // invalid day
         "2023-01-00", // invalid day
         "2023-02-30", // invalid day for February
         "2023-04-31", // invalid day for April
         "2023-1-01", // missing leading zero in month
         "2023-01-1", // missing leading zero in day
         "2023/01/01", // wrong separator
         "01-01-2023", // wrong format
-        // "2023-01-01T00:00:00Z", // includes time
-        // "2023-01-01 00:00:00", // includes time
+        "2023-01-01T00:00:00Z", // includes time
+        "2023-01-01 00:00:00", // includes time
+        "foo2023-01-01", // prefix contamination
+        "2023-01-01bar", // suffix contamination
         "2023-1-1", // missing leading zeros
         "23-01-01", // short year
         "2023-01", // missing day
         "2023", // missing month and day
         "", // empty string
         "invalid-date",
       ];
       const invalidDates = [
         "13/01/2023", // invalid month
         "00/01/2023", // invalid month
         "01/32/2023", // invalid day
         "01/00/2023", // invalid day
         "02/30/2023", // invalid day for February
         "04/31/2023", // invalid day for April
         "1/01/2023", // missing leading zero in month
         "01/1/2023", // missing leading zero in day
         "2023-01-01", // wrong format
         "2023/01/01", // wrong separator
         "01-01-2023", // wrong separator
         "01/01/23", // short year
-        // "01/01/2023T00:00:00Z", // includes time
-        // "01/01/2023 00:00:00", // includes time
+        "01/01/2023T00:00:00Z", // includes time
+        "01/01/2023 00:00:00", // includes time
+        "foo01/01/2023", // prefix contamination
+        "01/01/2023bar", // suffix contamination
         "01/2023", // missing day
         "2023", // missing month and day
         "", // empty string
         "invalid-date",
       ];

Also update the production regexes in packages/shared/src/external/zod/date.ts:

Outside this file (for maintainers applying the fix), consider:

- export const ISO_DATE_REGEX =
-   /(?:[1-9]\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)/;
+ export const ISO_DATE_REGEX =
+   /^(?:[1-9]\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)$/;
- export const US_DATE_REGEX =
-   /^(?:0[1-9]|1[0-2])\/(?:0[1-9]|1\d|2[0-8])\/(?:[1-9]\d{3})|(?:0[13-9]|1[0-2])\/(?:29|30)\/(?:[1-9]\d{3})|(?:0[13578]|1[02])\/31\/(?:[1-9]\d{3})|02\/29\/(?:(?:[1-9]\d(?:0[48]|[2468][048]|[13579][26]))|(?:[2468][048]|[13579][26])00)$/;
+ export const US_DATE_REGEX =
+   /^(?:(?:0[1-9]|1[0-2])\/(?:0[1-9]|1\d|2[0-8])\/(?:[1-9]\d{3})|(?:0[13-9]|1[0-2])\/(?:29|30)\/(?:[1-9]\d{3})|(?:0[13578]|1[02])\/31\/(?:[1-9]\d{3})|02\/29\/(?:(?:[1-9]\d(?:0[48]|[2468][048]|[13579][26]))|(?:[2468][048]|[13579][26])00))$/;

Also applies to: 72-99


🏁 Script executed:

#!/bin/bash
set -e

echo "== ISO_DATE_REGEX definition =="
rg -n 'export const ISO_DATE_REGEX' -C3 packages/shared

echo
echo "== US_DATE_REGEX definition =="
rg -n 'export const US_DATE_REGEX' -C3 packages/shared

echo
echo "== invalidDates in iso-date.test.ts =="
rg -n 'const invalidDates' -C5 packages/shared/src/external/zod/__tests__/iso-date.test.ts

Length of output: 2815


Anchor regexes to match full strings and expand invalid-date tests

The current ISO_DATE_REGEX (date.ts:11–12) is unanchored, and US_DATE_REGEX (date.ts:17–18) only anchors the first and last alternation, allowing partial matches (e.g. "2023-01-01T00:00:00Z" or "foo01/01/2023bar"). The tests in iso-date.test.ts haven’t been updated to catch these gaps.

• In packages/shared/src/external/zod/date.ts:

  • Wrap the entire ISO_DATE_REGEX in ^…$.
  • Group the US_DATE_REGEX alternation in a non-capturing (?:… ) and place ^/$ outside.

• In packages/shared/src/external/zod/__tests__/iso-date.test.ts:

  • Under describe("invalid ISO dates") (≈ line 23), uncomment and include:
    “2023-01-01T00:00:00Z”
    “2023-01-01 00:00:00”
    Add contamination cases:
    “foo2023-01-01”
    “2023-01-01bar”

• In the same test file under describe("invalid US dates") (≈ line 72):

  • Include analogous time-suffix and contamination cases:
    “01/01/2023T00:00:00Z”, “01/01/2023 00:00:00”, “foo01/01/2023”, “01/01/2023bar”

Example diffs:

--- a/packages/shared/src/external/zod/date.ts
+++ b/packages/shared/src/external/zod/date.ts
@@ -11,7 +11,7 @@
-export const ISO_DATE_REGEX =
-  /(?:[1-9]\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\d|2[0-8])|…)-02-29)/;
+export const ISO_DATE_REGEX =
+  /^(?:[1-9]\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\d|2[0-8])|…)-02-29)$/;

@@ -17,7 +17,7 @@
-export const US_DATE_REGEX =
-  /^(?:0[1-9]|1[0-2])\/…|02\/29\/…$/;
+export const US_DATE_REGEX =
+  /^(?:(?:0[1-9]|1[0-2])\/…|02\/29\/…)$/;
--- a/packages/shared/src/external/zod/__tests__/iso-date.test.ts
+++ b/packages/shared/src/external/zod/__tests__/iso-date.test.ts
@@ describe("invalid ISO dates")
-        // "2023-01-01T00:00:00Z", // includes time
-        // "2023-01-01 00:00:00", // includes time
+        "2023-01-01T00:00:00Z", // includes time
+        "2023-01-01 00:00:00", // includes time
+        "foo2023-01-01",        // prefix contamination
+        "2023-01-01bar",        // suffix contamination
@@ describe("invalid US dates")
-        // "01/01/2023T00:00:00Z", // includes time
-        // "01/01/2023 00:00:00", // includes time
+        "01/01/2023T00:00:00Z", // includes time
+        "01/01/2023 00:00:00", // includes time
+        "foo01/01/2023",       // prefix contamination
+        "01/01/2023bar",       // suffix contamination

These changes will ensure your regexes cannot be bypassed by extra prefixes/suffixes or time components.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/shared/src/external/zod/__tests__/iso-date.test.ts around lines
23–50 and the related regex definitions in
packages/shared/src/external/zod/date.ts (ISO_DATE_REGEX at ~lines 11–12 and
US_DATE_REGEX at ~lines 17–18): the ISO regex must be anchored to the full
string and the US regex alternation must be grouped and anchored so partial
matches or trailing/leading contamination/time-suffixes are rejected; fix
date.ts by wrapping ISO_DATE_REGEX with ^...$ and change US_DATE_REGEX to use a
non-capturing group (?: ... ) with ^ and $ outside, then update this test file
by uncommenting and including "2023-01-01T00:00:00Z" and "2023-01-01 00:00:00"
in the invalid ISO dates and adding contamination cases "foo2023-01-01" and
"2023-01-01bar"; also update the invalid US dates block (~line 72) to include
"01/01/2023T00:00:00Z", "01/01/2023 00:00:00", "foo01/01/2023", and
"01/01/2023bar" so the tests catch prefix/suffix and time-component bypasses.

@@ -8,6 +8,7 @@ export { emptyFunction } from "./common/general";
export { metriportOrganization } from "./common/metriport-organization";
export { isValidUrl } from "./common/net";
export { normalizeOid } from "./common/normalize-oid";
export { makeOid } from "./common/__tests__/oid";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Do not export from a test-only path (tests) — move makeOid into production code

Exporting from ./common/tests/oid risks publish/build failures (test dirs are commonly excluded) and leaks test helpers into the public API. Move makeOid into a non-test module (e.g., ./common/oid.ts) and export from there.

Apply this change to the export:

-export { makeOid } from "./common/__tests__/oid";
+export { makeOid } from "./common/oid";

Follow-up:

  • Create packages/shared/src/common/oid.ts and move the implementation from tests/oid.ts into it.
  • Keep tests in tests/oid.test.ts targeting the new production path.

Verification script to ensure tests is excluded from builds (and would currently break this export):


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Inspecting shared package tsconfig for __tests__ exclusions..."
rg -n '__tests__' packages/shared/tsconfig*.json || true

echo "Inspecting shared package.json for published files (looking for dist-only publishing)..."
rg -n '"files"\s*:' packages/shared/package.json -n || true
rg -n '"exports"\s*:' packages/shared/package.json -n || true

echo "Listing any runtime imports referencing __tests__ from shared..."
rg -n '__tests__' packages/shared/src/index.ts || true

echo "Done."

Length of output: 832


Do not export from a test-only path — move makeOid into production code

Exporting makeOid from ./common/__tests__/oid risks build or publish failures (test directories are often excluded) and unintentionally exposes test helpers in your public API. Please:

  • Create a new production module at
    packages/shared/src/common/oid.ts
    and move the implementation from
    packages/shared/src/common/__tests__/oid.ts into it.
  • Update the test file to import from the new path:
    packages/shared/src/common/__tests__/oid.test.tspackages/shared/src/common/oid.ts
  • Change the public export in
    packages/shared/src/index.ts
-export { makeOid } from "./common/__tests__/oid";
+export { makeOid } from "./common/oid";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export { makeOid } from "./common/__tests__/oid";
export { makeOid } from "./common/oid";
🤖 Prompt for AI Agents
In packages/shared/src/index.ts around line 11 you're exporting makeOid from a
test-only path which can break builds; create a new production module at
packages/shared/src/common/oid.ts and move the implementation from
packages/shared/src/common/__tests__/oid.ts into that file, update the test file
to import from the new path (packages/shared/src/common/__tests__/oid.test.ts
should import from ../oid or ../../common/oid as appropriate), and change the
public export in packages/shared/src/index.ts to export { makeOid } from
"./common/oid" (remove the export from the __tests__ path).

@@ -55,6 +55,7 @@
"@langchain/community": "^0.3.16",
"@langchain/core": "^0.3.19",
"@metriport/api-sdk": "file:packages/api-sdk",
"@metriport/commonwell-sdk": "5.9.11",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Confirm which CW surface utils consumes; add v1 alias here if utils imports it

If any code in utils imports "@metriport/commonwell-sdk-v1", this package should also declare the alias dependency; otherwise isolated installs/builds of utils will fail to resolve it (don’t rely on cross-package alias leakage). Also consider using a caret range to allow compatible patch updates.

  • If utils needs v1 imports, add:
    "@metriport/commonwell-sdk-v1": "npm:@metriport/commonwell-sdk@^5.9.11"

  • If utils directly targets the non-versioned SDK (intentionally), keeping "@metriport/commonwell-sdk": "5.9.11" is fine, though you may prefer "^5.9.11" for patch upgrades.

Run this to verify import usage within utils:

If results include @metriport/commonwell-sdk-v1, add the alias to dependencies here. If results include only @metriport/commonwell-sdk, no change needed beyond optionally switching to ^5.9.11.


🏁 Script executed:

#!/bin/bash
# Find CW SDK imports within utils and show context
rg -nP --type=ts -C3 $'from\\s+[\'"]@metriport/commonwell-sdk(-v1)?[\'"]|import\\([\'"]@metriport/commonwell-sdk(-v1)?[\'"]\\)' packages/utils

Length of output: 2234


No alias needed for @metriport/commonwell-sdk-v1; just bump to a caret range

After searching packages/utils, all imports reference "@metriport/commonwell-sdk" (no -v1), so you don’t need to add the v1 alias. To allow patch upgrades, update the dependency spec:

--- packages/utils/package.json
@@ dependencies
-   "@metriport/commonwell-sdk": "5.9.11",
+   "@metriport/commonwell-sdk": "^5.9.11",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"@metriport/commonwell-sdk": "5.9.11",
"@metriport/commonwell-sdk": "^5.9.11",
🤖 Prompt for AI Agents
In packages/utils/package.json around line 58, the dependency currently pins
"@metriport/commonwell-sdk" to "5.9.11" and the PR added an unnecessary alias
for "-v1"; remove any alias and update the dependency spec to use a caret range
so patch upgrades are allowed: change the version string to "^5.9.11" for
"@metriport/commonwell-sdk" and do not add a separate
"@metriport/commonwell-sdk-v1" entry.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review continued from previous batch...

Comment on lines +60 to +62
const isMemberAPI = orgOID === Config.getCWMemberOID();
if (isMemberAPI) throw new Error("Cannot use the member OID as an organization OID");

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use consistent error type and include context

Elsewhere you throw MetriportError with additionalInfo. Do the same here for consistency and better diagnostics.

Apply this diff:

-  const isMemberAPI = orgOID === Config.getCWMemberOID();
-  if (isMemberAPI) throw new Error("Cannot use the member OID as an organization OID");
+  const isMemberAPI = orgOID === Config.getCWMemberOID();
+  if (isMemberAPI)
+    throw new MetriportError("Cannot use the member OID as an organization OID", undefined, {
+      orgOID,
+    });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const isMemberAPI = orgOID === Config.getCWMemberOID();
if (isMemberAPI) throw new Error("Cannot use the member OID as an organization OID");
const isMemberAPI = orgOID === Config.getCWMemberOID();
if (isMemberAPI)
throw new MetriportError(
"Cannot use the member OID as an organization OID",
undefined,
{ orgOID },
);
🤖 Prompt for AI Agents
In packages/api/src/external/commonwell-v2/api.ts around lines 60 to 62, replace
the plain Error throw with a MetriportError that uses the same descriptive
message but also sets a suitable error code and includes additionalInfo (e.g., {
orgOID, memberOID: Config.getCWMemberOID() }) so callers and logs get diagnostic
context; ensure MetriportError is imported if not already and preserve existing
stack behavior by constructing the MetriportError with the message and
additionalInfo.

Comment on lines +33 to +35
given: demographics?.name?.[0]?.given ?? ["John"],
family: demographics?.name?.[0]?.family ?? ["Doe"],
use: NameUseCodes.usual,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix inconsistent array wrapping for name fields

The given and family fields should be strings, not arrays, to match the type definitions and other parts of the codebase.

Apply this diff to fix the inconsistency:

-        given: demographics?.name?.[0]?.given ?? ["John"],
-        family: demographics?.name?.[0]?.family ?? ["Doe"],
+        given: demographics?.name?.[0]?.given ?? ["John"],
+        family: demographics?.name?.[0]?.family ?? "Doe",

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/api/src/external/commonwell-v2/mock/payloads.ts around lines 33 to
35, the given and family name fields are currently set as arrays but should be
strings to match the type definitions; change them to produce strings (e.g., use
demographics?.name?.[0]?.given?.join(' ') ?? "John" for given and
demographics?.name?.[0]?.family?.join(' ') ?? "Doe" for family) so the fields
are plain strings rather than arrays.

Comment on lines +101 to +103
given: ["John"],
family: ["Doe"],
use: "usual",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix inconsistent array wrapping for name fields in createPatientLink

Same issue as above - the given and family fields should match the expected types.

Apply this diff:

-              given: ["John"],
-              family: ["Doe"],
+              given: ["John"],
+              family: "Doe",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
given: ["John"],
family: ["Doe"],
use: "usual",
given: ["John"],
family: "Doe",
use: "usual",
🤖 Prompt for AI Agents
In packages/api/src/external/commonwell-v2/mock/payloads.ts around lines 101 to
103, the name fields in createPatientLink are using inconsistent types; change
the given and family fields to be arrays of strings (e.g., ["John"] and ["Doe"])
so they match the expected types and the rest of the payload structure.

Comment on lines +166 to +168
family: ["Doe"],
given: ["John"],
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix inconsistent array wrapping for name fields in createDocument

The name fields in the Patient resource should be consistent with the expected types.

Apply this diff:

-            family: ["Doe"],
-            given: ["John"],
+            family: "Doe",
+            given: ["John"],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
family: ["Doe"],
given: ["John"],
},
family: "Doe",
given: ["John"],
},
🤖 Prompt for AI Agents
In packages/api/src/external/commonwell-v2/mock/payloads.ts around lines 166 to
168, the Patient.name fields are not consistently wrapped as an array of
HumanName objects; update the payload so Patient.name is an array (i.e., wrap
the existing name object in an array) and ensure its family and given properties
remain arrays (family: ["Doe"], given: ["John"]) so the resource matches the
expected FHIR types.

Comment on lines +141 to +144
if (contentsFileName.endsWith(".bin")) {
const newFileExtension = getFileExtension(contentType);
fs.renameSync(contentsFileName, `${contentsFileName.replace(".bin", "")}${newFileExtension}`);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Potential file handling issue with rename operation

The file rename operation after download might fail if the file is still being written to or if there are permission issues. Consider adding error handling.

     if (contentsFileName.endsWith(".bin")) {
       const newFileExtension = getFileExtension(contentType);
-      fs.renameSync(contentsFileName, `${contentsFileName.replace(".bin", "")}${newFileExtension}`);
+      const newFileName = `${contentsFileName.replace(".bin", "")}${newFileExtension}`;
+      try {
+        fs.renameSync(contentsFileName, newFileName);
+        console.log(`>>> Renamed file to ${newFileName}`);
+      } catch (renameError) {
+        console.log(`>>> Warning: Could not rename file: ${errorToString(renameError)}`);
+        // File is still saved with .bin extension, which is acceptable
+      }
     }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/commonwell-cert-runner/src/flows/document-consumption.ts around
lines 141 to 144, the synchronous fs.rename call can fail if the file is still
being written or on permission errors; wrap the rename in robust error handling
by ensuring any download/write stream is closed/flushed before renaming, replace
the plain renameSync with a try/catch around an atomic async rename (use
fs.promises.rename) or a small retry/backoff loop, and on failure log the
filename, target name and the caught error then either retry a few times or
surface the error to the caller so callers can handle the failure.

Comment on lines +58 to +89
export function makeDemographics(): Omit<Demographics, "identifier"> {
const shouldAddDriversLicense = Math.random() < 0.5;
const driversLicense: PatientIdentifier | undefined = shouldAddDriversLicense
? {
type: "DL",
value: makeId(),
system: caDriversLicenseUri,
use: "secondary",
}
: undefined;
return {
address: [
{
use: NameUseCodes.usual,
zip,
country: "USA",
use: AddressUseCodes.home,
postalCode: faker.location.zipCode(),
state: faker.location.state(),
line: [faker.location.streetAddress()],
city: faker.location.city(),
},
],
name: [
{
use: NameUseCodes.usual,
family: [lastName],
given: [firstName],
given: [faker.person.firstName()],
family: [faker.person.lastName()],
},
],
gender: {
code: gender,
},
birthDate: dob,
},
});
export const makeDocPerson = (init?: PersonDataOnOrg) => {
const docPatient = makeDocPatient(init);
gender: faker.helpers.arrayElement([GenderCodes.M, GenderCodes.F]),
birthDate: faker.date.birthdate().toISOString().split("T")[0],
...(driversLicense ? { identifier: [driversLicense] } : {}),
};
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Fix return type to match actual shape (identifier may be present).

makeDemographics sometimes includes identifier (driver’s license), but the return type excludes it. Expose the optional identifier in the type to avoid losing type information and suppressing legit access in callers.

Apply this diff:

-export function makeDemographics(): Omit<Demographics, "identifier"> {
+export function makeDemographics(): Omit<Demographics, "identifier"> &
+  Partial<Pick<Demographics, "identifier">> {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function makeDemographics(): Omit<Demographics, "identifier"> {
const shouldAddDriversLicense = Math.random() < 0.5;
const driversLicense: PatientIdentifier | undefined = shouldAddDriversLicense
? {
type: "DL",
value: makeId(),
system: caDriversLicenseUri,
use: "secondary",
}
: undefined;
return {
address: [
{
use: NameUseCodes.usual,
zip,
country: "USA",
use: AddressUseCodes.home,
postalCode: faker.location.zipCode(),
state: faker.location.state(),
line: [faker.location.streetAddress()],
city: faker.location.city(),
},
],
name: [
{
use: NameUseCodes.usual,
family: [lastName],
given: [firstName],
given: [faker.person.firstName()],
family: [faker.person.lastName()],
},
],
gender: {
code: gender,
},
birthDate: dob,
},
});
export const makeDocPerson = (init?: PersonDataOnOrg) => {
const docPatient = makeDocPatient(init);
gender: faker.helpers.arrayElement([GenderCodes.M, GenderCodes.F]),
birthDate: faker.date.birthdate().toISOString().split("T")[0],
...(driversLicense ? { identifier: [driversLicense] } : {}),
};
}
export function makeDemographics(): Omit<Demographics, "identifier"> &
Partial<Pick<Demographics, "identifier">> {
const shouldAddDriversLicense = Math.random() < 0.5;
const driversLicense: PatientIdentifier | undefined = shouldAddDriversLicense
? {
type: "DL",
value: makeId(),
system: caDriversLicenseUri,
use: "secondary",
}
: undefined;
return {
address: [
{
use: AddressUseCodes.home,
postalCode: faker.location.zipCode(),
state: faker.location.state(),
line: [faker.location.streetAddress()],
city: faker.location.city(),
},
],
name: [
{
use: NameUseCodes.usual,
given: [faker.person.firstName()],
family: [faker.person.lastName()],
},
],
gender: faker.helpers.arrayElement([GenderCodes.M, GenderCodes.F]),
birthDate: faker.date.birthdate().toISOString().split("T")[0],
...(driversLicense ? { identifier: [driversLicense] } : {}),
};
}
🤖 Prompt for AI Agents
In packages/commonwell-cert-runner/src/payloads.ts around lines 58 to 89, the
function return type Omit<Demographics, "identifier"> excludes the optional
identifier that the function can add; change the return type to include an
optional identifier (for example: Omit<Demographics, "identifier"> & {
identifier?: Demographics["identifier"] }) so callers retain correct typing when
identifier is present.

Comment on lines +101 to +117
...(demographics.identifier ?? []),
{
use: "official",
system: `${facilityId}`,
value: makePatientId({ facilityId }),
assigner: memberName,
},
],
managingOrganization: {
identifier: [
{
system: facilityId,
},
],
},
};
};
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Check PatientIdentifier.assigner type (string vs Reference).

You set assigner: memberName (string). patientIdentifierSchema.assigner commonly is a Reference shape; if it doesn’t accept string, this will fail zod validation.

Run this script to inspect the schema:


🏁 Script executed:

#!/bin/bash
# Show patientIdentifierSchema and referenceInIdentifierSchema to verify assigner type
rg -n -C3 -g '!**/dist/**' -g '!**/build/**' -g '!**/node_modules/**' \
  -P 'export\s+const\s+patientIdentifierSchema\s*=\s*z\.object|referenceInIdentifierSchema' \
  packages/commonwell-sdk/src/models/identifier.ts

Length of output: 959


Ensure assigner is a Reference, not a raw string

The patientIdentifierSchema defines assigner as referenceInIdentifierSchema.nullish(), i.e. a Reference‐shaped object, not a bare string. As written,

assigner: memberName,

will fail zod validation.

Please update the payload to wrap memberName in a Reference object. For example:

--- a/packages/commonwell-cert-runner/src/payloads.ts
+++ b/packages/commonwell-cert-runner/src/payloads.ts
@@ lines 101-117
-      assigner: memberName,
+      assigner: {
+        display: memberName,
+      },
  • File: packages/commonwell-cert-runner/src/payloads.ts
  • Lines: ~101–117
🤖 Prompt for AI Agents
packages/commonwell-cert-runner/src/payloads.ts around lines 101 to 117: the
assigner field is currently set to a raw string (memberName) but
patientIdentifierSchema expects a Reference-shaped object; replace the raw
string with a Reference object that wraps memberName (e.g. an object with a
reference property containing the memberName or the appropriate resource type +
id), so assigner becomes a Reference-shaped object matching
referenceInIdentifierSchema.nullish().

Comment on lines +26 to 34
line: z.array(z.string()).nullish(),
city: z.string().nullish(),
state: z.string().nullish(),
country: emptyStringToUndefinedSchema,
postalCode: z.string(),
use: emptyStringToUndefinedSchema.pipe(addressUseCodesSchema.nullish()),
type: emptyStringToUndefinedSchema.pipe(addressTypeCodesSchema.nullish()),
period: periodSchema.nullish(),
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Normalization and required fields: strengthen address schema

  • city/state currently accept empty strings; align with your emptyStringToUndefined normalization for consistency.
  • postalCode is required but z.string() permits empty strings; enforce non-empty.

Apply this diff:

 export const addressSchema = z.object({
-  line: z.array(z.string()).nullish(),
-  city: z.string().nullish(),
-  state: z.string().nullish(),
+  line: z.array(z.string()).nullish(),
+  city: emptyStringToUndefinedSchema,
+  state: emptyStringToUndefinedSchema,
   country: emptyStringToUndefinedSchema,
-  postalCode: z.string(),
+  postalCode: z.string().min(1),
   use: emptyStringToUndefinedSchema.pipe(addressUseCodesSchema.nullish()),
   type: emptyStringToUndefinedSchema.pipe(addressTypeCodesSchema.nullish()),
   period: periodSchema.nullish(),
 });
🤖 Prompt for AI Agents
In packages/commonwell-sdk/src/models/address.ts around lines 26 to 34, update
the schema so city and state use the same emptyStringToUndefinedSchema
normalization as country, and make postalCode a non-empty string; specifically
replace city: z.string().nullish() and state: z.string().nullish() with city:
emptyStringToUndefinedSchema and state: emptyStringToUndefinedSchema, and change
postalCode: z.string() to postalCode: z.string().min(1) (or
emptyStringToUndefinedSchema.pipe(z.string().min(1)) if you want to normalize
then enforce non-empty).

Comment on lines +26 to 35
export const genderCodesSchema = z.preprocess(
zodToLowerCase,
z
.nativeEnum(GenderCodes)
.or(z.nativeEnum(GenderCodesBackwardsCompatible))
.or(z.enum(Object.keys(GenderCodes).map(k => k.toLowerCase()) as [string, ...string[]]))
.transform(coerceGender)
);
export type Gender = z.infer<typeof genderCodesSchema>;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Bug: ‘UN’/‘UNK’ codes rejected due to lowercasing before enum check

zodToLowerCase lowercases inputs, but z.nativeEnum(GenderCodesBackwardsCompatible) expects "UN" and not "un". Likewise, "UNK" becomes "unk" and is not allowed by the union. Because validation fails before .transform(coerceGender), these inputs never reach your coercion logic.

Simplify acceptance to “any string -> coerce” to cover all intended inputs:

-export const genderCodesSchema = z.preprocess(
-  zodToLowerCase,
-  z
-    .nativeEnum(GenderCodes)
-    .or(z.nativeEnum(GenderCodesBackwardsCompatible))
-    .or(z.enum(Object.keys(GenderCodes).map(k => k.toLowerCase()) as [string, ...string[]]))
-    .transform(coerceGender)
-);
+export const genderCodesSchema = z.preprocess(
+  zodToLowerCase,
+  z.string().transform(coerceGender)
+);

This relies solely on coerceGender for validation/normalization, which already supports “f/m/o/u”, “female/male/other/unknown”, “un/undifferentiated”, and “unk/unknown”.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const genderCodesSchema = z.preprocess(
zodToLowerCase,
z
.nativeEnum(GenderCodes)
.or(z.nativeEnum(GenderCodesBackwardsCompatible))
.or(z.enum(Object.keys(GenderCodes).map(k => k.toLowerCase()) as [string, ...string[]]))
.transform(coerceGender)
);
export type Gender = z.infer<typeof genderCodesSchema>;
export const genderCodesSchema = z.preprocess(
zodToLowerCase,
z.string().transform(coerceGender)
);
export type Gender = z.infer<typeof genderCodesSchema>;
🤖 Prompt for AI Agents
In packages/commonwell-sdk/src/models/demographics.ts around lines 26 to 35, the
existing z.preprocess lowercases input before checking native enums which causes
valid codes like "UN"/"UNK" to be rejected; instead accept any string and let
coerceGender handle normalization/validation. Replace the current chained
enum/preprocess schema with a simple z.string().transform(coerceGender) (keep
the exported Gender type as z.infer<typeof genderCodesSchema>), removing
zodToLowerCase and the enum unions so all inputs reach coerceGender for
normalization.

@leite08 leite08 added this pull request to the merge queue Aug 15, 2025
Merged via the queue into develop with commit d0085f2 Aug 15, 2025
31 checks passed
@leite08 leite08 deleted the eng-200-cw-v2_merge-branch branch August 15, 2025 18:32
leite08 added a commit that referenced this pull request Aug 17, 2025
…ranch"

This reverts commit d0085f2, reversing
changes made to 7180d7e.

Signed-off-by: Rafael Leite <2132564+leite08@users.noreply.github.com>
This was referenced Aug 17, 2025
@lucasdellabella lucasdellabella mentioned this pull request Aug 17, 2025
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants