Kill the Clipboard - SMART Health Cards Library - v1.0.0
    Preparing search index...

    Class SHLManifestBuilder

    Class that builds the manifest and files for a SMART Health Link.

    This class handles file encryption, upload, and manifest building for SHL content. It encrypts files using JWE with A256GCM, manages file storage through provided upload/retrieval functions, and generates manifests with embedded or location-based file descriptors based on size thresholds.

    Per the SHL specification, servers SHALL generate manifests with short-lived URLs on each request. This ensures URLs remain secure and can be rotated frequently. For this to work, the SHLManifestBuilder must be reconstructed on each request. Use the toDBAttrs to persist the builder state after the SHL is created, and the fromDBAttrs to reconstruct the builder when handling each manifest request.

    The builder supports:

    • FHIR JSON resources
    • SMART Health Card files (JWS format)
    • Optional compression with raw DEFLATE
    • Embedded vs location-based file serving
    • toDBAttrs/fromDBAttrs for persistence
    // Create SHL and builder
    const shl = SHL.generate({
    baseManifestURL: 'https://shl.example.org/manifests/',
    manifestPath: '/manifest.json',
    flag: 'P'
    });
    const builder = new SHLManifestBuilder({
    shl,
    uploadFile: async (content) => {
    // Upload encrypted content to storage, return path
    return await myStorage.upload(content);
    },
    getFileURL: (path) => {
    // Generate short-lived signed URL for path
    return myStorage.getSignedURL(path);
    },
    maxParallelism: 3 // Process up to 3 files concurrently
    });

    // Add files
    await builder.addHealthCard({ shc: mySHC });
    await builder.addFHIRResource({ content: myFhirBundle });

    // Generate manifest
    const manifest = await builder.buildManifest({ embeddedLengthMax: 16384 });
    Index

    Accessors

    • get files(): SHLManifestFileDBAttrs[]

      Get the current list of files in the manifest.

      Returns metadata about all files that have been added to the builder. Does not include the actual file content, only the storage paths, content types, and ciphertext lengths.

      Returns SHLManifestFileDBAttrs[]

      Array of file metadata objects (copies, safe to modify)

    • get manifestId(): string

      Get the manifest ID for the SHL instance used by this builder.

      Useful for storing the SHL and its manifest in a database with a unique identifier.

      The manifest ID is the 43-character base64url-encoded entropy segment that uniquely identifies this SHL's manifest URL. This ID is extracted from the manifest URL path and can be used as a database key or identifier.

      For example, if the manifest URL is: 'https://shl.example.org/manifests/abc123def456.../manifest.json' The manifest ID would be: 'abc123def456...'

      Returns string

      The 43-character manifest ID string

      If the manifest URL cannot be parsed to extract the ID

      const manifestId = builder.manifestId;
      console.log('Database key:', manifestId); // 'abc123def456...'
    • get shl(): SHL

      Get the SHL instance used by this builder.

      Returns the immutable SHL that contains the encryption key and manifest URL for this builder's files.

      Returns SHL

      The SHL instance provided in the constructor

    Constructors

    • Create a manifest builder for the given SHL.

      The builder requires functions for file storage operations. These functions abstract the storage backend (S3, filesystem, database, etc.) and allow the builder to work with any storage system.

      Parameters

      • params: {
            fetch?: (url: string, options?: RequestInit) => Promise<Response>;
            getFileURL: (path: string) => Promise<string>;
            loadFile?: (path: string) => Promise<string>;
            maxParallelism?: number;
            removeFile?: (path: string) => Promise<void>;
            shl: SHL;
            updateFile?: (
                path: string,
                content: string,
                contentType?: SHLFileContentType,
            ) => Promise<void>;
            uploadFile: (
                content: string,
                contentType?: SHLFileContentType,
            ) => Promise<string>;
        }
        • Optionalfetch?: (url: string, options?: RequestInit) => Promise<Response>

          Optional fetch implementation for the default loadFile. Only used if loadFile is not provided. Defaults to global fetch.

        • getFileURL: (path: string) => Promise<string>

          Function to generate URLs for stored files. Receives a storage path from uploadFile and returns an HTTPS URL. Per SHL spec, URLs SHALL be short-lived and intended for single use. Called each time a manifest is built for location-based files.

        • OptionalloadFile?: (path: string) => Promise<string>

          Optional function to load file content from storage. Receives a storage path and returns the JWE content as a string. If not provided, defaults to fetching via getFileURL() with the provided fetch implementation. Called when building manifests for embedded files.

        • OptionalmaxParallelism?: number

          Optional maximum number of concurrent file operations. Defaults to 5. Used for parallelizing file loading and URL generation in buildManifest.

        • OptionalremoveFile?: (path: string) => Promise<void>

          Optional function to remove files from storage. Receives a storage path and removes the file. Required for file removal operations.

        • shl: SHL

          The immutable SHL instance this builder manages. Contains the encryption key and manifest URL for this SHL.

        • OptionalupdateFile?: (
              path: string,
              content: string,
              contentType?: SHLFileContentType,
          ) => Promise<void>

          Optional function to update files in storage. Receives a storage path, new content, and optional content type. Required for file update operations.

        • uploadFile: (content: string, contentType?: SHLFileContentType) => Promise<string>

          Function to upload encrypted files to storage. Receives the JWE content as a string and optional content type. Must return a storage path/key that can be used with getFileURL and loadFile. Called once per file when added to the builder.

      Returns SHLManifestBuilder

      // With S3-like storage
      const builder = new SHLManifestBuilder({
      shl: myShl,
      uploadFile: async (content, contentType) => {
      const key = `shl-files/${crypto.randomUUID()}.jwe`;
      await s3.putObject({ Key: key, Body: content, ContentType: 'application/jose' });
      return key;
      },
      getFileURL: async (path) => {
      return s3.getSignedUrl('getObject', { Key: path, Expires: 3600 });
      },
      loadFile: async (path) => {
      const result = await s3.getObject({ Key: path });
      return result.Body.toString();
      },
      removeFile: async (path) => {
      await s3.deleteObject({ Key: path });
      },
      updateFile: async (path, content, contentType) => {
      await s3.putObject({ Key: path, Body: content, ContentType: 'application/jose' });
      },
      maxParallelism: 10
      });

    Methods

    • Add a FHIR JSON resource file to the manifest.

      Encrypts the FHIR resource as a JWE file and uploads it to storage. The resource is serialized as JSON and can be any valid FHIR R4 resource.

      Parameters

      • params: { content: Resource; enableCompression?: boolean }

        Configuration for adding the FHIR resource. The object should contain:

        • content: FHIR R4 resource object to add (must have valid resourceType field)
        • enableCompression: Optional. Whether to compress the file content before encryption (defaults to true)

      Returns Promise<
          {
              ciphertextLength: number;
              encryptedFile: SHLFileJWE;
              storagePath: string;
          },
      >

      Promise resolving to object with encrypted file metadata and storage path

      SHLEncryptionError When encryption fails due to invalid key, content, or crypto operations

      // Add a FHIR Bundle
      const result = await builder.addFHIRResource({
      content: {
      resourceType: 'Bundle',
      type: 'collection',
      entry: [
      { resource: { resourceType: 'Patient', id: '123', ... } },
      // ... more resources
      ]
      }
      });
      console.log('Added FHIR resource:', result.storagePath, 'Size:', result.ciphertextLength);
    • Add a SMART Health Card file to the manifest.

      Encrypts the SMART Health Card as a JWE file and uploads it to storage. The SHC is wrapped in a verifiableCredential array as per SMART Health Cards specification.

      Parameters

      • params: { enableCompression?: boolean; shc: string | SHC }

        Configuration for adding the health card. The object should contain:

        • shc: SMART Health Card to add (JWS string or SHC object)
        • enableCompression: Optional. Whether to compress the file content before encryption (defaults to false, as SHCs are typically already compressed)

      Returns Promise<
          {
              ciphertextLength: number;
              encryptedFile: SHLFileJWE;
              storagePath: string;
          },
      >

      Promise resolving to object with encrypted file metadata and storage path

      SHLEncryptionError When encryption fails due to invalid key, content, or crypto operations

      // Add from SHC object
      const result = await builder.addHealthCard({ shc: mySHC });
      console.log('Added file:', result.storagePath, 'Size:', result.ciphertextLength);

      // Add from JWS string
      await builder.addHealthCard({ shc: 'eyJhbGciOiJFUzI1NiIsImtpZCI6IjNLZmRnLVh3UC03Z...' });

      // Add with compression (not recommended for SHCs)
      await builder.addHealthCard({
      shc: mySHC,
      enableCompression: true
      });
    • Build the manifest as JSON.

      Generates a fresh manifest response with up-to-date file descriptors. Files are either embedded directly in the manifest or referenced by location URLs based on the size threshold. Location URLs are generated fresh each time to ensure security and proper access control. embeddedLengthMax may vary per request.

      Parameters

      • params: {
            embeddedLengthMax?: number;
            list?: List;
            status?: "finalized" | "can-change" | "no-longer-valid";
        } = {}
        • OptionalembeddedLengthMax?: number

          Maximum size in bytes for embedded files. Files with ciphertext length ≤ this value will be embedded directly in the manifest. Files larger than this will be referenced by location URLs. Defaults to 16384 (16 KiB). Typical range: 4096-32768.

        • Optionallist?: List

          Optional FHIR List resource to include in the manifest root. Provides metadata about the collection of files.

        • Optionalstatus?: "finalized" | "can-change" | "no-longer-valid"

          Optional value indicating whether files may change in the future. When provided, will be included in the manifest root.

      Returns Promise<SHLManifestV1>

      Promise resolving to SHL manifest object conforming to v1 specification (SHLManifestV1)

      SHLExpiredError When the SHL has expired (exp field < current time)

      SHLManifestError When a stored file cannot be loaded or content is missing

      SHLNetworkError When storage network requests fail

      // Use default settings
      const manifest = await builder.buildManifest();

      // Custom settings with can-change status and FHIR List
      const manifest = await builder.buildManifest({
      embeddedLengthMax: 4096,
      status: "can-change",
      list: {
      resourceType: 'List',
      status: 'current',
      mode: 'working',
      title: 'Patient Summary'
      }
      });
    • Find a file in the manifest by storage path.

      Returns the file metadata for the specified storage path, or null if not found. Useful for checking if a file exists before attempting updates or for getting file information.

      Parameters

      • storagePath: string

        Storage path to search for

      Returns null | SHLManifestFileDBAttrs

      File metadata object or null if not found

      const fileInfo = builder.findFile('shl-files/bundle123.jwe');
      if (fileInfo) {
      console.log('File type:', fileInfo.type);
      console.log('File size:', fileInfo.ciphertextLength);
      } else {
      console.log('File not found');
      }
    • Reconstruct a builder from database attributes returned by toDBAttrs method.

      Creates a new SHLManifestBuilder instance from previously stored database attributes. The provided functions are used for subsequent file operations.

      Parameters

      • params: {
            attrs: SHLManifestBuilderDBAttrs;
            fetch?: (url: string, options?: RequestInit) => Promise<Response>;
            getFileURL: (path: string) => Promise<string>;
            loadFile?: (path: string) => Promise<string>;
            maxParallelism?: number;
            removeFile?: (path: string) => Promise<void>;
            shl: SHLPayloadV1;
            updateFile?: (
                path: string,
                content: string,
                contentType?: SHLFileContentType,
            ) => Promise<void>;
            uploadFile: (
                content: string,
                contentType?: SHLFileContentType,
            ) => Promise<string>;
        }
        • attrs: SHLManifestBuilderDBAttrs

          Database attributes from a previous toDBAttrs() call

        • Optionalfetch?: (url: string, options?: RequestInit) => Promise<Response>

          Optional fetch implementation for default loadFile (same signature as constructor)

        • getFileURL: (path: string) => Promise<string>

          Function to generate file URLs (same signature as constructor)

        • OptionalloadFile?: (path: string) => Promise<string>

          Optional function to load file content (same signature as constructor)

        • OptionalmaxParallelism?: number

          Optional maximum number of concurrent file operations (same signature as constructor)

        • OptionalremoveFile?: (path: string) => Promise<void>

          Optional function to remove files (same signature as constructor)

        • shl: SHLPayloadV1

          SHL payload stored separately in database

        • OptionalupdateFile?: (
              path: string,
              content: string,
              contentType?: SHLFileContentType,
          ) => Promise<void>

          Optional function to update files (same signature as constructor)

        • uploadFile: (content: string, contentType?: SHLFileContentType) => Promise<string>

          Function to upload encrypted files (same signature as constructor)

      Returns SHLManifestBuilder

      New SHLManifestBuilder instance with restored state

      // Load from database (when serving the manifest)
      const savedAttrs = await database.getManifestBuilder(shlId);
      const shlPayload = await database.getSHL(shlId);

      // Reconstruct builder (pass the same functions passed to the constructor)
      const builder = SHLManifestBuilder.fromDBAttrs({
      shl: shlPayload,
      attrs: savedAttrs,
      uploadFile: async (content) => await storage.upload(content),
      getFileURL: async (path) => await storage.getSignedURL(path),
      loadFile: async (path) => await storage.download(path),
      removeFile: async (path) => await storage.delete(path),
      updateFile: async (path, content) => await storage.update(path, content)
      });

      // Builder is ready to serve the manifest
      const manifest = await builder.buildManifest();
    • Remove a file from the manifest and storage.

      Removes the file metadata from the builder's internal state and calls the removeFile function to delete the actual file from storage. The file is identified by its storage path.

      Parameters

      • storagePath: string

        Storage path of the file to remove (as returned by uploadFile)

      Returns Promise<void>

      Promise that resolves when the file is removed from both manifest and storage

      SHLManifestError When removeFile function is not provided or file not found

      SHLNetworkError When storage removal fails

      // Remove a specific file
      // Note: This requires providing removeFile function in constructor
      await builder.removeFile('shl-files/abc123.jwe');
    • Return database attributes for DB persistence. Use the fromDBAttrs method to reconstruct the builder later.

      Returns an object containing only the files metadata. This is NOT the same as an SHLManifestV1 - it's the builder's internal state that can be stored in a database and used to reconstruct the builder later. The SHL payload should be stored separately in the database.

      The database attributes include:

      • File metadata (content types, storage paths, ciphertext lengths)

      Does NOT include:

      • SHL payload (stored separately in database)
      • Actual file content (stored separately via uploadFile)
      • Short-lived URLs (generated fresh via getFileURL)

      Returns SHLManifestBuilderDBAttrs

      Database attributes suitable for storage (SHLManifestBuilderDBAttrs)

      // Store for persistence (when creating the SHL)
      await database.storeSHL(shlId, shl.payload);
      const builderAttrs = builder.toDBAttrs();
      await database.storeManifestBuilder(shlId, builderAttrs);

      // Later, reconstruct the builder (when serving the manifest)
      const shlPayload = await database.getSHL(shlId);
      const savedAttrs = await database.getManifestBuilder(shlId);
      const reconstructedBuilder = SHLManifestBuilder.fromDBAttrs({
      attrs: savedAttrs,
      shl: shlPayload,
      uploadFile: myUploadFunction,
      getFileURL: myGetURLFunction
      });
    • Update a FHIR resource file in the manifest and storage.

      Replaces an existing FHIR resource file with new content. The file is identified by its storage path. The updated content is encrypted and stored using the same storage path, and the manifest metadata is updated accordingly.

      Parameters

      • storagePath: string

        Storage path of the file to update (as returned by uploadFile)

      • content: Resource

        New FHIR resource content to store

      • OptionalenableCompression: boolean

        Whether to compress the file content before encryption (defaults to true)

      • OptionallastUpdated: Date

        Optional custom timestamp for the update. If not provided, the current time will be used.

      Returns Promise<void>

      Promise that resolves when the file is updated in both manifest and storage

      SHLManifestError When updateFile function is not provided, file not found, or file is not a FHIR resource

      SHLNetworkError When storage update fails

      SHLEncryptionError When encryption fails due to invalid key, content, or crypto operations

      // Update a FHIR Bundle
      // Note: This requires providing updateFile function in constructor
      await builder.updateFHIRResource('shl-files/bundle123.jwe', {
      resourceType: 'Bundle',
      type: 'collection',
      entry: [
      { resource: { resourceType: 'Patient', id: '456', ... } }
      ]
      });
    • Update a SMART Health Card file in the manifest and storage.

      Replaces an existing SMART Health Card file with new content. The file is identified by its storage path. The updated content is encrypted and stored using the same storage path, and the manifest metadata is updated accordingly.

      Parameters

      • storagePath: string

        Storage path of the file to update (as returned by uploadFile)

      • shc: string | SHC

        New SMART Health Card to store (JWS string or SHC object)

      • OptionalenableCompression: boolean

        Whether to compress the file content before encryption (defaults to false for SHCs)

      • OptionallastUpdated: Date

        Optional custom timestamp for the update. If not provided, the current time will be used.

      Returns Promise<void>

      Promise that resolves when the file is updated in both manifest and storage

      SHLManifestError When updateFile function is not provided, file not found, or file is not a SMART Health Card

      SHLNetworkError When storage update fails

      SHLEncryptionError When encryption fails due to invalid key, content, or crypto operations

      // Update with SHC object
      // Note: This requires providing updateFile function in constructor
      await builder.updateHealthCard('shl-files/card123.jwe', myUpdatedHealthCard);

      // Update with JWS string
      await builder.updateHealthCard('shl-files/card123.jwe', 'eyJhbGciOiJFUzI1NiIsImtpZCI6...');