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

@ftisindia/sdk

v0.14.0

Published

Per-project client SDK generator for FTIS backends — emits self-contained Dart/Flutter and Angular/TypeScript SDKs from a backend's OpenAPI contract.

Readme

@ftisindia/sdk

ftis-sdk — per-project client SDK generator for FTIS backends. Reads a backend's OpenAPI contract (including custom modules and the x-ftis vendor extensions) and emits a self-contained client SDK for two targets:

  • Dart/Flutter (default): the ftis_core runtime (vendored) plus typed models, per-tag API classes, an org-scoped facade, permission-key constants, and — on by default — the full clean-architecture stack (domain entities, pure request inputs, repository contracts + implementations, one use case per operation, and a sealed Result/Failure core), so a BLoC app only hand-writes its presentation layer.
  • Angular/TypeScript (--target angular), no FTIS npm packages required at runtime:
    • runtime (vendored as source): auth with single-flight refresh, organisation context, RBAC, realtime, and the Angular provider, signal services, origin-scoped auth interceptor, session and permission guards, and *ftisHasPermission;
    • contract layer: typed models (interfaces over the wire JSON, optionality and nullability kept apart) and injectable API services returning cold Observables that compose with rxResource, plus typed permission keys and a static route → permission map;
    • forms: per request model, a validation spec, a typed Reactive FormGroup factory, and a Signal Forms factory (Angular 22);
    • state and routing: per-module facades (keyset-paged signal state, errors normalized to Failure) and permission-guarded route skeletons;
    • testing: deterministic model factories, MSW handlers, an in-memory testing core (provideFtisCoreTesting), and generated smoke specs.

Both targets carry the client of the backend realtime plug-and-play module (ftis.realtime.v1 over one WebSocket): sdk.realtime on Dart and FtisRealtimeService on Angular — lazy, authenticated in-band from the session, token rotation re-sent, backoff reconnect with re-subscription (see the generated README's "Realtime" section). The vendored Dart runtime therefore depends on package:http and package:web_socket_channel; the web runtime has no dependencies.

# inside a scaffolded FTIS backend
npm run sdk:generate                       # → sdk/dart/<name>_sdk/

# against any backend
ftis-sdk generate --backend ./my-backend                             # Dart (exports the contract fresh)
ftis-sdk generate --backend ./my-backend --target angular            # Angular → sdk/angular/<name>-sdk/
ftis-sdk generate --backend https://api.example.com --out ./my_sdk   # fetches /docs-json (URL mode requires --out)

Flutter apps consume the Dart output via a path or git dependency — no pub.dev needed. Angular apps copy (or path-alias) the generated folder into the app and register provideFrontendCore — see the generated README. See the backend template's docs/SDK.md for usage; run ftis-sdk --help for all flags (--target, --use-existing, --strict, --force, --name, --out, --no-domain).

Clean-architecture layers

The generated tree mirrors the standard Flutter clean-architecture layout:

lib/src/generated/
├─ core/            Result, Failure, PagedResult, guardApiCall
├─ data/
│  ├─ models/       wire models (JSON) — *_model.dart
│  ├─ datasources/  typed API classes (remote data sources)
│  ├─ mappers/      model ↔ domain conversions
│  └─ repositories/ repository implementations
└─ domain/          entities/ inputs/ repositories/ usecases/<feature>/

Backend schema names ending in Dto are emitted as *Model Dart classes (SettingResponseDto → SettingResponseModel in setting_response_model.dart); a schema whose *Model form is itself a schema is a hard error (rename one DTO, e.g. @ApiSchema({ name })).

Names are append-stable. Every generated public name (API method, repository, entity/input, use case) depends only on its own operation, tag, or DTO — never on which siblings exist — so adding an endpoint, DTO, or scope can never rename something a consumer already uses. Where two symbols would derive the same name, generation fails with a message naming the backend declaration to add: @FtisSdkName('listArchived') for an operation (x-ftis-sdk-name) or @FtisSdkFeature('SettingsPublic') for the repository of a tag+scope (x-ftis-sdk-feature, needed when one tag has both root and org-scoped routes). The error also prints the names 0.12 promoted the colliding members to, so existing consumers can be kept unchanged by declaring exactly those. The one additive change no stateless rule can absorb: a request-only DTO that a new endpoint starts returning moves from *Input to *Entity.

Everything is derived deterministically from the contract: response-reachable models become pure *Entity classes (plus toEntity() mappers), request-only models become pure *Input classes (plus toModel() mappers), each API class becomes a repository contract + implementation, and every operation becomes a <Feature><Action>UseCase returning Future<Result<T>> (ApiException and transport errors normalize into a Failure value in one generated guard). Domain files import nothing outside the domain layer and the pure core trio (result/failure/paged_result) — enforced by tests. Session-managed auth operations (login, signup, oauth/exchange, refresh, logout) delegate to the vendored AuthEngine instead of the raw API so token storage and silent refresh keep working; the engine surface the templates rely on is pinned by test/engine-surface.test.mjs. Skipped operations get no domain surface. Pass --no-domain to emit only data/models/ + data/datasources/ (same nested layout, no core/domain/ mapper/repository layers).

How generation is driven

The backend declares SDK-relevant facts in its OpenAPI document (x-ftis block, x-ftis-public, x-ftis-skip, x-ftis-sdk-name, x-ftis-sdk-feature — see the template's src/common/swagger/ftis-sdk.decorators.ts). The generator consumes those first; documents without any x-ftis metadata fall back to heuristics (path allowlists, a hard-coded skip ledger, and the legacy long-form promotion of colliding method names) with a warning. --strict turns warnings into failures.

Simple multipart uploads (binary file parts + primitive fields, JSON response) generate typed upload methods, and single-media binary downloads generate typed methods returning FtisBinaryResponse. Operations a typed client still cannot represent are listed in the generated README and stay reachable through the escape hatches: request (JSON), upload (multipart), and requestBytes (binary responses). The skip reasons are stream-or-202-union, browser-redirect, browser-link (a clickable emailed-link target whose POST twin is the typed surface), dynamic-format, non-json-response, and multipart.

Three behaviours worth knowing:

  • Generated names may not collide with the vendored runtime. Every generated model — and, on the Dart target, every hoisted enum — is checked against the runtime exports the barrel re-exports beside it (ApiClient, AuthUser, Page, …). A collision warns, and warnings are fatal under --strict, because the Dart barrel would become ambiguous and TypeScript's export * would silently drop both conflicting names. Rename the backend DTO. This runs for both targets and regardless of --no-domain.

  • The heuristic fallback can rot. The ledger in src/ir.mjs (FALLBACK_SKIP_LEDGER, FALLBACK_PUBLIC_PATHS) hard-codes template routes as they existed before the x-ftis decorators; it is only consulted for contracts with no x-ftis block (typically URL mode against an old deployment). If those routes have since changed, fallback output can be wrong — prefer regenerating the backend so the contract carries metadata, and treat the fallback warning (fatal under --strict) as a prompt to do so.

  • Pagination detection is shape-exact. A response schema maps to the runtime's Page<T> only when it has exactly two properties, items (array) + nextCursor. Adding any field to a page envelope demotes it to a plain generated class — intentional, so enriched envelopes keep their fields — and the generator warns on that near-miss shape (fatal under --strict): move the extra fields elsewhere, or keep the plain class deliberately.

Development (monorepo)

The Dart runtime lives at mobile-starter/packages/ftis_core and is baked into assets/ by scripts/sync-sdk-runtime.mjs at prepack/pretest (the same lifecycle packages/cli uses for its template). Tests: npm test (goldens under test/golden/; refresh deliberately with node test/update-golden.mjs after contract or emitter changes).