npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@alexisapp/leave-core

v0.1.3

Published

Platform-agnostic core package for the leave management feature. Contains the GraphQL client, error handling, query/mutation factories, form schemas, domain logic, i18n, and stores.

Readme

@leave/core

Platform-agnostic core package for the leave management feature. Contains the GraphQL client, error handling, query/mutation factories, form schemas, domain logic, i18n, and stores.

This is a leaf package — it has zero platform dependencies (react-native, react-dom are forbidden).

Setup

pnpm install
pnpm build       # tsup — produces dist/ with ESM (.mjs) + declarations (.d.ts)
pnpm clean       # rm -rf dist
pnpm typecheck   # tsc --noEmit
pnpm lint        # eslint src/

Build

pnpm build runs tsup which produces ESM output (.mjs) and TypeScript declarations (.d.ts) for all subpath exports into dist/.

Dual-resolution strategy:

  • Metro (React Native): resolves the react-native condition → raw .ts in src/ — Metro transpiles at build time
  • Vite (Web): resolves the import condition → pre-built .mjs in dist/

Public API

Client Lifecycle

import { initializeClient, resetClient, execute } from '@leave/core';
import type { ClientConfig, GraphQLEndpoints } from '@leave/core';

initializeClient(config: ClientConfig): void

Creates a module-scoped ky instance configured for GraphQL requests. Must be called before any execute call. Throws if called twice without resetClient() first (singleton guard — see ADR-011).

resetClient(): void

Resets the module-scoped singleton to null. Call in useEffect cleanup on unmount (prevents stale closure on MF remount) and in afterEach() in tests.

execute.gateway<TResult, TVariables>({ query, variables, signal? }): Promise<TResult>

Sends a GraphQL request to the gateway service (new leave operations). query is a TypedDocumentString from generated-gateway/TResult and TVariables are inferred from it. Classifies all errors at this boundary. Auth errors (401/403) trigger onAuthError() callback, then throw — zero retry.

// Types are inferred from the document — no manual generics needed
const data = await execute.gateway({
  query: ListLeaveSelfCertifiedDocument,
  variables: { filters: { ... } },
  signal,
});

execute.hrCore<TResult, TVariables>({ query, variables, signal? }): Promise<TResult>

Same as execute.gateway but targets HR Core service (legacy employee/policy lookups). query is a TypedDocumentString from generated-hr-core/.

Types

ClientConfig

| Property | Type | Required | Description | | ------------- | ------------------------------- | -------- | --------------------------------------------------------- | | endpoints | GraphQLEndpoints | Yes | Fully resolved GraphQL endpoint URLs | | getToken | () => Promise<string \| null> | No | Mobile only — returns Auth0 JWT | | onAuthError | () => void | No | Mobile only — called on 401/403 before throwing | | devToken | string | No | Hardcoded token for dev. Takes precedence over getToken |

GraphQLEndpoints

| Property | Type | Description | | --------- | -------- | ------------------------------------ | | gateway | string | Full URL for gateway GraphQL service | | hrCore | string | Full URL for HR Core GraphQL service |

Error Classes

| Class | Code | Description | | -------------- | --------------- | --------------------------------------------------- | | LeaveError | varies | Base class. All domain errors extend this. | | AuthError | AUTH_ERROR | 401/403. Never retried. Triggers onAuthError(). | | GraphQLError | GRAPHQL_ERROR | Server returned errors[] in the GraphQL response. | | NetworkError | NETWORK_ERROR | Fetch failed, timeout, no connectivity. | | DomainError | DOMAIN_ERROR | Business rule violation from server. |

All errors are classified via classifyError() at the execute boundary — consumers catch typed LeaveError subclasses. Classification is idempotent (passing a LeaveError through returns it unchanged). AbortError (host unmounts mid-request) is classified as ABORT_ERROR — a no-op in the UI (no toast, no error boundary).

Error Code Mapping

ErrorCode enum and getErrorCode(error) provide consumer-facing error classification for UI messages:

import { ErrorCode, getErrorCode } from '@leave/core';

const code = getErrorCode(error); // e.g. ErrorCode.CONNECTION_FAILURE

Platforms use getErrorCode() to map errors to i18n-keyed display messages. Core provides the enum and classification; platforms provide the translations.

Error Helpers

import { isRetryable, isHttpError, getErrorMessage } from '@leave/core';
  • isRetryable(error: unknown): booleantrue for NetworkError, GraphQLError, 5xx HTTP; false for AuthError, DomainError
  • isHttpError(error: unknown): error is HTTPError — type guard for ky's HTTPError
  • getErrorMessage(error: unknown): { title: string; description: string } — returns i18n-localized error title and description using ErrorCode mapping

AsyncBoundary

import { AsyncBoundary } from '@leave/core/components';

Composable boundary combining QueryErrorResetBoundary + ErrorBoundary (react-error-boundary) + Suspense. Platform packages provide their own fallbackRender.

<AsyncBoundary
  suspenseFallback={<LoadingSpinner />}
  errorBoundaryProps={{
    fallbackRender: ({ error, resetErrorBoundary }) => (
      <MyErrorFallback error={error} onRetry={resetErrorBoundary} />
    ),
  }}
>
  <MyDataComponent />
</AsyncBoundary>

The onReset handler automatically calls QueryErrorResetBoundary.reset() so TanStack Query retries failed queries on retry.

GraphQL Codegen

Two separate GraphQL services require two codegen configs:

| Service | Endpoint | Codegen Config | Generated Output | Entities | | ----------- | ------------- | -------------------- | -------------------- | ---------------------------------------------------- | | Gateway | /v2/graphql | codegen-gateway.ts | generated-gateway/ | LeaveSelfCertified, LeaveChange, SelfCertifiedPolicy | | HR Core | /graphql | codegen-hr-core.ts | generated-hr-core/ | Leave, Employee, Policy, LeaveBalance |

Commands

pnpm codegen            # Run both configs sequentially
pnpm codegen:gateway    # Generate gateway types only
pnpm codegen:hr-core    # Generate HR Core types only

Schema Source

Schemas are introspected from remote API endpoints at codegen time — no local schema files are stored in the repo. This matches the salary-review-ui pattern.

  • Gateway: https://api-2.dev-alexishr.io/v2/graphql
  • HR Core: https://api-2.dev-alexishr.io/graphql

To force a fresh introspection (e.g. after API schema changes): turbo codegen --force

Operation Structure

src/graphql/operations/
├── gateway/                    # Operations for execute.gateway()
│   ├── leave-self-certified/   # queries.graphql, mutations.graphql
│   ├── leave-change/           # queries.graphql, mutations.graphql
│   └── self-certified-policy/  # queries.graphql
└── hr-core/                    # Operations for execute.hrCore()
    ├── leave/                  # queries.graphql, mutations.graphql
    ├── policy/                 # queries.graphql
    └── employee/               # queries.graphql

Generated Output

generated-gateway/ and generated-hr-core/never edit these files manually. They are overwritten by codegen.

Uses documentMode: 'string' because execute() takes document: string (not AST). All document constants are TypedDocumentString instances that serialize to string.

Endpoint-to-Domain Mapping

| Operation | Executor | Why | | ----------------------------------------------- | ----------------- | ------------------------------------- | | getLeave, listLeave, createLeave, updateLeave | execute.hrCore | Standard leave CRUD in HR Core | | getEmployeeVacationBalance, listVacationBalance | execute.hrCore | Balance data in HR Core | | getEmployee | execute.hrCore | Employee data in HR Core | | getLeavePolicy, listLeavePolicies | execute.hrCore | Leave policies in HR Core | | getLeaveSelfCertified, create/update | execute.gateway | New self-certified service on gateway | | getLeaveChange, leaveChangeCreate | execute.gateway | New change-request service on gateway | | getLeaveSelfCertifiedPolicy | execute.gateway | Self-certified policy on gateway |

Query Factories

Import from @leave/core/queries. Pattern matches salary-review-ui (officesQueries, employeesQueries).

Four factories with cascading keys and embedded queryOptions (ADR-014):

| Factory | Root key (all) | Domains covered | | ----------------- | ---------------- | --------------------------------------------------------- | | leaveQueries | ['leave'] | Leave CRUD, balance, approvals, self-certified, time bank | | policyQueries | ['policy'] | Leave policies, self-certified policies, leave types | | employeeQueries | ['employee'] | Employee, employment, work week, holidays | | settingsQueries | ['settings'] | Time-off settings |

Each factory uses a cascading key hierarchy — every level spreads the parent:

import { leaveQueries } from '@leave/core/queries';

// Key-only levels (for invalidation targeting)
leaveQueries.all              // ['leave']
leaveQueries.lists()          // [...all, 'list']
leaveQueries.details()        // [...all, 'detail']

// Leaf levels accept codegen-typed QueryVariables and pass them directly to execute
leaveQueries.list(variables?)       // queryOptions({ queryKey: [...lists(), variables], queryFn })
leaveQueries.detail(variables)      // queryOptions({ queryKey: [...details(), variables], queryFn })

// Usage with TanStack Query — consumers build the variables object at the call site
const { data } = useQuery(leaveQueries.list({ employeeIdList: [employeeId] }));
const { data } = useQuery(leaveQueries.detail({ id: leaveId }));
const { data } = useQuery(leaveQueries.balance({ id: employeeId }));

// Nuclear invalidation — wipes all leave queries
queryClient.invalidateQueries({ queryKey: leaveQueries.all });

All leaf methods accept codegen-typed QueryVariables and pass them through — never remap or rename fields inside the factory. All queryFn implementations pass { signal } to execute for request cancellation on component unmount.

Subpath Exports

| Subpath | Purpose | | -------------- | ------------------------------------------------------------------------------- | | @leave/core | Client lifecycle (initializeClient, resetClient, execute) | | ./queries | TanStack Query queryOptions factories | | ./mutations | Mutation wrappers (callback-based) | | ./forms | TanStack Form + Zod form schemas | | ./domain | Business rules, permissions, types | | ./i18n | i18next instance + locale resources | | ./components | AsyncBoundary (react-error-boundary + Suspense + QueryErrorResetBoundary) | | ./stores | Zustand stores | | ./hooks | Shared hooks (useCurrentEmployeeId, useLeaveList, useBalances, useBalanceTiles) | | ./utils | Shared utils (dateUtils, apiUtils, typeSafeUtils) | | ./constants | Const enums (LeaveDisplayStatus, LeaveChangeType, FILTER_KEYS) | | ./types | Domain types (ILeaveListItem, ILeaveDisplayStatus, IPagination) |

Metro (React Native) resolves raw .ts via the react-native condition. Bundler/Node resolves compiled dist/.

Peer Dependencies

| Package | Range | | ----------------------- | ---------------------- | | react | ^18.0.0 \|\| ^19.0.0 | | @tanstack/react-query | ^5.0.0 | | react-error-boundary | ^5.0.0 | | zod | ^4.0.0 |

Dependencies

| Package | Range | Purpose | | --------- | --------- | ---------------------------- | | ky | ^1.0.0 | HTTP client (timeout, retry) | | i18next | ^23.0.0 | Internationalization | | graphql | ^16.0.0 | GraphQL utilities |