Skip to content

feat(lib): add createEnclosedClient factory for one-time SDK configuration #471

Description

@Educg550

What type of request is this?

New feature idea

Clear and concise description of the feature you are proposing

Problem

The @enclosed/lib SDK currently exposes standalone utility functions (createNote, fetchNote, storeNote, etc.) that each accept an optional apiBaseUrl parameter to override the default https://enclosed.cc instance. This forces consumers to pass configuration values on every single call, which is verbose:

// Today: apiBaseUrl must be repeated everywhere
const note = await createNote({
  content: 'Hello',
  apiBaseUrl: 'https://my-enclosed-instance.com', // again...
  clientBaseUrl: 'https://my-enclosed-instance.com',
});

const fetched = await fetchNote({
  noteId,
  apiBaseUrl: 'https://my-enclosed-instance.com', // and again
});

The CLI partially solves this by storing instance-url in a local config file (enclosed config set instance-url …) and reading it before each command, but this is not something in the current lib.

Well-designed SDKs (Anthropic, OpenAI, AWS SDK v3, FastAPI's FastAPI() app instance, Axios's axios.create(...)) follow a configure-once pattern: you initialize a client with your settings and all subsequent calls inherit them.

Proposed Solution

Export a createEnclosedClient factory from @enclosed/lib that returns a pre-configured set of the existing utility functions:

import { createEnclosedClient } from '@enclosed/lib';

const enclosed = createEnclosedClient({
  apiBaseUrl: 'https://my-enclosed-instance.com',
  clientBaseUrl: 'https://my-enclosed-instance.com',
  // other params/defaults: encryptionAlgorithm, serializationFormat, etc.
});

// All methods inherit the configured baseUrls
const { noteUrl } = await enclosed.createNote({ content: 'Hello' });
const { payload } = await enclosed.fetchNote({ noteId });

The factory would bind the provided config to each underlying function, returning an object that mirrors the current free-function API surface:

type EnclosedClient = {
  createNote: (params: Omit<CreateNoteParams, 'apiBaseUrl' | 'clientBaseUrl'>) => Promise<CreateNoteResult>;
  fetchNote: (params: Omit<FetchNoteParams, 'apiBaseUrl'>) => Promise<FetchNoteResult>;
  storeNote: (params: Omit<StoreNoteParams, 'apiBaseUrl'>) => Promise<StoreNoteResult>;
  // … rest of the API surface
};

function createEnclosedClient(config: {
  apiBaseUrl?: string;       // default: 'https://enclosed.cc'
  clientBaseUrl?: string;    // default: apiBaseUrl
  encryptionAlgorithm?: EncryptionAlgorithm;
  serializationFormat?: SerializationFormat;
}): EnclosedClient;

The standalone free functions remain unchanged for backward compatibility. createEnclosedClient is purely additive.

Additional context

  • The internal apiClient factory in packages/lib/src/api/api.client.ts already accepts a baseUrl parameter but is not exported. createEnclosedClient would expose this capability at the public API level.
  • The CLI's config.usecases.ts already demonstrates the need: it calls getInstanceUrl() before every command to retrieve the stored URL. An SDK-level client object would eliminate this boilerplate for programmatic consumers.
  • This pattern is idiomatic for TypeScript SDKs and would improve the developer experience for self-hosted Enclosed deployments, where every call currently requires specifying a custom apiBaseUrl.

Validations

  • Check the feature is not already implemented in the project.
  • Check that there isn't already an issue that request the same feature to avoid creating a duplicate.
  • Check that the feature is technically feasible and aligns with the project's goals.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions