Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion fync/examples/unified-usage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@

import { GitHub, GitLab, NPM, Spotify, Vercel } from "src";

/**
* Runs a set of GitLab usage examples against the GitLab API.
*
* Demonstrates common read, search and mutation operations using the unified GitLab client:
* - instantiates the client from `process.env.GITLAB_TOKEN`
* - retrieves users, projects (by slug and URL), commits (including timeframe variants)
* - queries project stars and aggregated user statistics
* - performs project searches with sorting/pagination
* - accesses advanced resources (issues, merge requests, pipelines, jobs, groups)
* - creates a project issue and logs the authenticated user
*
* Side effects: performs network requests and creates a new issue via `projects.createProjectIssue`.
* Requires a valid `GITLAB_TOKEN` in the environment; without it the client will fail to authenticate.
*/
async function gitlabExamples() {
const gitlab = GitLab({ token: process.env.GITLAB_TOKEN! });

Expand Down Expand Up @@ -75,6 +89,14 @@ async function gitlabExamples() {
console.log(`Authenticated as: ${currentUser.username}`);
}

/**
* Runs a set of example GitHub API calls using the unified GitHub client.
*
* Demonstrates instantiation of the client (requires GITHUB_TOKEN in the environment) and common operations:
* fetching a user, querying repositories (by owner/name and by URL), retrieving commits (all, latest, and within time frames),
* fetching repository stars and user star counts, collecting aggregated user statistics, performing repository searches,
* accessing advanced repo resources (issues) and creating a new issue. Results are logged to the console.
*/
async function githubExamples() {
const github = GitHub({ token: process.env.GITHUB_TOKEN! });

Expand Down Expand Up @@ -320,7 +342,15 @@ async function addingNewApiExample() {
// That's it! New API added in ~30 lines of code
}

// Run examples
/**
* Orchestrates and runs the example suites for the supported APIs based on environment tokens.
*
* For each supported service, prints a header and runs its example function only if the corresponding
* environment token is present. NPM examples are always executed. The function resolves when all
* invoked example tasks complete.
*
* @returns A promise that resolves when all selected examples have finished running.
*/
async function main() {
console.log("🚀 Fync Unified API Examples\n");

Expand Down
17 changes: 17 additions & 0 deletions fync/src/core/api-factory-enhanced.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,23 @@ async function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}

/**
* Creates an enhanced HTTP API client with built-in authentication headers, caching, rate limiting, and retry logic.
*
* The returned client supports typed request methods (request/get/post/put/delete/patch), cache management
* (clearCache, invalidateCache), and rate-limit introspection (getRateLimitInfo). Behavior is governed by
* the provided config: baseUrl and headers are used to build requests; auth selects authentication headers;
* cache controls an internal cache instance; rateLimit accepts either a preset name or a custom rate config;
* retryConfig controls automatic retries.
*
* Defaults applied when not provided:
* - retryConfig.maxRetries: 3
* - retryConfig.retryDelay: 1000 ms
* - retryConfig.retryOn: [429, 503, 504]
*
* @param config - API client configuration (baseUrl, optional headers, auth, cache, rateLimit, retryConfig).
* @returns A TEnhancedApiClient instance exposing request helpers, cache controls, and rate-limit information.
*/
export function createEnhancedApi(config: TApiConfig): TEnhancedApiClient {
const authHeaders = buildAuthHeaders(config.auth);
const defaultHeaders = {
Expand Down
11 changes: 11 additions & 0 deletions fync/src/core/api-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,17 @@ function buildUrl(
return url.toString();
}

/**
* Creates an API client configured from the provided config.
*
* The client merges config.headers and authentication headers into default headers,
* constructs URLs from config.baseUrl and per-call paths/params, and performs fetch requests.
* Request bodies for non-GET methods are JSON-stringified; responses are parsed as JSON and returned.
* Non-OK HTTP responses cause an Error that includes the status and response body text.
*
* @param config - API configuration (must include `baseUrl`; optional `headers` and `auth` are applied)
* @returns A TApiClient with `request`, `get`, `post`, `put`, `delete`, and `patch` methods
*/
export function createFyncApi(config: TApiConfig): TApiClient {
const authHeaders = buildAuthHeaders(config.auth);
const defaultHeaders = {
Expand Down
10 changes: 10 additions & 0 deletions fync/src/core/module-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ type TModule<TResources extends TModuleResources> = {
api: TApiClient;
};

/**
* Build a typed module that bundles an API client with resource handlers and optional helpers.
*
* Constructs and returns a TModule whose `api` property is an API client created from `config.apiConfig`,
* whose resource keys are handlers created from `config.resources`, and which includes any `config.helpers`
* attached at the top level.
*
* @param config - Module configuration containing `name`, `apiConfig`, `resources`, and optional `helpers`.
* @returns A fully assembled module typed as `TModule<TResources>`.
*/
export function createFyncModule<TResources extends TModuleResources>(
config: TModuleConfig<TResources>,
): TModule<TResources> {
Expand Down
22 changes: 21 additions & 1 deletion fync/src/core/resource-factory.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import type { TApiClient } from "./api-factory";

type TMethodDefinition = {
Expand All @@ -22,6 +21,15 @@ type TResource<TMethods extends TResourceMethods> = {
};


/**
* Replace `{key}` placeholders in a path template with URL-encoded values and collect remaining params as query parameters.
*
* The function returns an object with `path` — the template with any `{key}` placeholders replaced by `encodeURIComponent(String(value))` for matching keys — and `queryParams` — a map of all input entries whose keys did not correspond to a placeholder in the template.
*
* @param template - Path template containing zero or more placeholders in the form `{key}`.
* @param params - Key/value map used to interpolate placeholders and produce query parameters.
* @returns An object with the interpolated `path` and a `queryParams` record of remaining params.
*/
function interpolatePath(
template: string,
params: Record<string, any>,
Expand All @@ -41,6 +49,18 @@ function interpolatePath(
return { path, queryParams };
}

/**
* Builds a typed resource object from a resource configuration and an API client.
*
* Creates one function per entry in `config.methods`. Each generated method:
* - Interpolates `config.basePath + method.path` with provided options to produce the final path and query params.
* - For methods with HTTP verb POST/PUT/PATCH: produced function has signature `(data?: any, options?: any) => Promise<any>` and calls the corresponding `post|put|patch` client method with `(path, data, { params })`.
* - For other verbs (GET/DELETE or undefined): produced function has signature `(options?: any) => Promise<any>` and calls the corresponding `get|delete` client method with `(path, { params })`.
* - If a method `transform` function is provided in the method definition, its return value is used; otherwise the raw API response is returned.
*
* @param config - Resource configuration that defines the basePath and per-method definitions used to build the resource.
* @returns A typed resource object whose methods match `TMethods` with the runtime implementations described above.
*/
export function createFyncResource<TMethods extends TResourceMethods>(
config: TResourceConfig<TMethods>,
apiClient: TApiClient,
Expand Down
13 changes: 13 additions & 0 deletions fync/src/github/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,19 @@ type TGitHubModule = TModule<typeof resources> & {
getUserActivity: (username: string, options?: any) => Promise<any>;
};

/**
* Create a configured GitHub API client module.
*
* Returns a GitHub API wrapper built from the shared resource definitions and the provided personal access token. The returned module exposes the underlying resource methods plus convenience helpers such as:
* - getUser, getRepository, getRepositoryFromUrl
* - getUserCommits, getUserLatestCommit, getUserCommitsInTimeframe
* - getRepositoryStars, getUserStarredCount, getUserStats
* - searchRepositories, getUserActivity
*
* @param config - Configuration object containing authentication credentials.
* - token: A GitHub personal access token used for bearer authentication.
* @returns A fully configured TGitHubModule instance with the standard resource methods and additional convenience helpers.
*/
export function GitHub(config: { token: string }): TGitHubModule {
const base = buildGitHub(config, resources);

Expand Down
13 changes: 13 additions & 0 deletions fync/src/gitlab/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,19 @@ type TGitLabModule = TModule<typeof resources> & {
getCurrentUser: () => Promise<any>;
};

/**
* Create a configured GitLab API module exposing resource endpoints and higher-level helpers.
*
* Returns a module built against the GitLab API (https://gitlab.com/api/v4) that includes:
* - typed resource namespaces (users, projects, groups, search, snippets, issues, merge_requests, pipelines, activity, me)
* - convenience helpers for common workflows (e.g., getUser, getProject, getProjectFromUrl, getUserCommits, getUserStats, searchProjects, getCurrentUser)
*
* The returned module delegates most calls to the underlying resource clients and adds small utility functions
* for parsing GitLab URLs and computing timeframe dates used by commit-related helpers.
*
* @param config - Configuration object containing a GitLab personal access token: `{ token: string }`.
* @returns A configured TGitLabModule instance with resource methods and helper utilities.
*/
export function GitLab(config: { token: string }): TGitLabModule {
const base = buildGitLab(config, resources);

Expand Down
14 changes: 14 additions & 0 deletions fync/src/google-calendar/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,20 @@ type TGoogleCalendarModule = TModule<typeof resources> & {
quickAddEvent: (calendarId: string, text: string) => Promise<any>;
};

/**
* Constructs a Google Calendar API module bound to the provided OAuth bearer token.
*
* Returns a high-level calendar client with convenience methods for listing calendars and events,
* querying free/busy, creating/updating/deleting events, and other common operations. The returned
* object implements TGoogleCalendarModule and delegates to the underlying Google Calendar REST
* resources using the supplied token.
*
* Note: getAllCalendarEvents will ignore errors when fetching events for individual calendars
* (errors for a specific calendar are swallowed and processing continues).
*
* @param config - Configuration object containing the OAuth bearer token to use for requests.
* @returns A TGoogleCalendarModule instance with bound convenience methods.
*/
export function GoogleCalendar(config: {
token: string;
}): TGoogleCalendarModule {
Expand Down
12 changes: 12 additions & 0 deletions fync/src/google-drive/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,18 @@ type TGoogleDriveModule = TModule<typeof resources> & {
permanentlyDeleteFile: (fileId: string) => Promise<any>;
};

/**
* Creates a Google Drive client with low-level resource endpoints and high-level convenience methods.
*
* Returns an API module bound to the provided bearer token that exposes the underlying resource methods
* (files, permissions, comments, replies, revisions, drives, changes, channels, about) and a set of
* higher-level helper functions for common operations (listing, creating, updating, moving, downloading,
* exporting files; folder management; searching; sharing; quota retrieval; etc.).
*
* @param config - Configuration object
* @param config.token - OAuth2 bearer token used for authenticating requests
* @returns A TGoogleDriveModule instance providing both resource-level API methods and convenience helpers
*/
export function GoogleDrive(config: { token: string }): TGoogleDriveModule {
const base = buildGoogleDrive(config, resources);
const drive = base as unknown as TGoogleDriveModule;
Expand Down
13 changes: 13 additions & 0 deletions fync/src/npm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ type TNpmModule = TModule<typeof resources> & {
getPackageReadme: (packageName: string) => Promise<string>;
};

/**
* Create a configured NPM registry client module with convenience methods for querying package metadata, downloads, search, and stats.
*
* The returned module exposes resource-based API methods plus helpers such as:
* - getPackage, getPackageVersion, getLatestVersion
* - getPackageDownloads, getPackageSize, getPackageStats
* - searchPackages
* - getPackageDependencies, getPackageMaintainers, getPackageKeywords, getPackageReadme
* - isPackageDeprecated
*
* @param config - Optional configuration. If provided, `config.registry` overrides the default registry base URL.
* @returns A TNpmModule instance bound to the configured registry.
*/
export function NPM(config?: { registry?: string }): TNpmModule {
const base = buildNpm(
{
Expand Down
11 changes: 11 additions & 0 deletions fync/src/spotify/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,17 @@ type TSpotifyModule = TModule<typeof resources> & {
searchPlaylists: (query: string, options?: any) => Promise<any>;
};

/**
* Create a configured Spotify client module with convenience wrappers for common endpoints.
*
* The function builds a resource-based Spotify API client using the provided bearer token
* and augments it with higher-level helper methods (e.g., getTrack, searchTracks,
* createPlaylist, playTrack, pausePlayback, getMyTopTracks) that return Promises for
* corresponding Spotify API calls.
*
* @param config - Configuration object containing a Spotify OAuth bearer token (`token`) used for requests.
* @returns A configured TSpotifyModule instance exposing both the underlying resources and the convenience methods.
*/
export function Spotify(config: { token: string }): TSpotifyModule {
const base = buildSpotify(config, resources);
const spotify = base as unknown as TSpotifyModule;
Expand Down
12 changes: 12 additions & 0 deletions fync/src/vercel/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,18 @@ type TVercelModule = TModule<typeof resources> & {
getTeamUsage: (teamId: string) => Promise<any>;
};

/**
* Creates and returns a typed Vercel API module with convenience helpers.
*
* Builds a client configured with the provided token (and optional team ID)
* and exposes resource methods (projects, deployments, domains, teams, user)
* plus higher-level helpers such as getProject, listProjects, getLatestDeployment,
* getDeploymentStatus, redeployProject, getProjectAnalytics, getDomainStatus, and getTeamUsage.
*
* @param config.token - Vercel personal or team API token used for Authorization.
* @param config.teamId - Optional Vercel team ID; when provided it is sent as `x-vercel-team-id` on requests.
* @returns A TVercelModule instance: the generated API client augmented with higher-level helper methods.
*/
export function Vercel(config: {
token: string;
teamId?: string;
Expand Down