@ojiepermana/angular-sdk
v22.0.59
Published
OpenAPI 3.x → Angular SDK generator (schematics + config surface) for @ojiepermana/angular.
Maintainers
Readme
@ojiepermana/angular-sdk
OpenAPI 3.x → Angular SDK generator for the @ojiepermana/angular design system.
Source lives in library/sdk; the package publishes as @ojiepermana/angular-sdk
and is also re-exported as the umbrella secondary entry point @ojiepermana/angular/sdk.
It generates a lightweight Angular SDK from any OpenAPI 3.x spec (including 3.2.0):
typed HttpClient services, tree-shakeable fn modules, optional metadata
(permissions / validators), and a navigation tree.
What the published package exposes
The npm artifact currently ships the typed config surface — the contract you author
your sdk.config.json against — from public-api.ts:
import {
resolveConfig,
resolveTarget,
type SdkConfig,
type SdkTargetConfig,
type SdkOutputMode,
type SdkFeatureFlags,
} from '@ojiepermana/angular-sdk';The generator implementation itself lives in the source tree:
| Path | Role |
| -------------------- | ------------------------------------------------------------------------------------------------ |
| src/config/ | Config schema + loader (JSONC, .js/.cjs) — resolveConfig / resolveTarget |
| src/parser/ | OpenAPI → intermediate representation (bundle, ir, types) |
| src/emit/ | One emitter per concern (models, operations, services, client, metadata, navigation, public-api) |
| src/layout/ | Post-emit transforms (e.g. per-domain split-by-domain reorganisation) |
| src/render/ | Template rendering |
| src/writer/ | Output-mode wrappers (standalone / library / secondary-entrypoint) |
| src/engine.ts | generate(target, workspaceRoot) — runs the full pipeline for one target |
| schematics/init/ | Scaffolds config/sdk.config.json from sdk.config.example.json |
| schematics/sdk/ | Runs the generator and writes files into the Angular CLI Tree |
| schematics/ng-add/ | ng add support — installs peer deps |
| bin/ | Compiled JS output of src/ + schematics/ (via tsconfig.schematics.json) |
Config shape
A single config file drives one or more generation targets:
{
"targets": [
{
"input": "./openapi.yaml",
"output": "./sdk",
"mode": "library", // "standalone" | "library" | "secondary-entrypoint"
"clientName": "Api",
"packageName": "@my-scope/sdk", // used in "library" mode
"packageVersion": "0.0.1", // used in "library" mode
"rootUrl": "", // optional; empty string means same-origin requests
"splitByDomain": true, // optional; defaults true for "library", false otherwise
"splitDepth": "service", // "service" (default) | "tag"
"features": {
"models": true,
"operations": true,
"services": true,
"client": true,
"metadata": true,
"navigation": true,
},
},
],
}Multiple targets are supported — one config run can emit several SDKs. See
sdk.config.example.json for the canonical template the
init schematic scaffolds.
If rootUrl is omitted or left empty, the generated SDK uses same-origin requests by
default. Consumer apps can override it at runtime with provideApiConfiguration(...).
Runtime base URL
The generated SDK does not read sdk.config.json at runtime. The value of
targets[].rootUrl is only used during code generation to seed the default
ApiConfiguration.rootUrl value.
rootUrl: ""or omitted: requests use the current origin, for example/api/userson the same host as the Angular app.rootUrl: "https://api.example.com": the generated SDK defaults to that absolute backend URL.- Runtime override: consumer apps can replace the default by providing a new value during bootstrap.
import { bootstrapApplication } from '@angular/platform-browser';
import { provideHttpClient } from '@angular/common/http';
import { AppComponent } from './app/app.component';
import { provideApiConfiguration } from '@my-scope/sdk';
bootstrapApplication(AppComponent, {
providers: [provideHttpClient(), provideApiConfiguration('https://api.example.com')],
});For standalone generated output inside the same workspace, import
provideApiConfiguration from the generated SDK barrel instead of an npm package path.
Output modes
| Mode | What it emits | Use when… |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- |
| standalone | A plain folder (no ng-package.json). | You consume the SDK via path alias / tsconfig.paths inside the same app. |
| library | Split-by-domain output plus package metadata and nested ng-package.json manifests for secondary entrypoints. | You want a buildable/publishable Angular package with efficient deep imports. |
| secondary-entrypoint | Standalone output plus a minimal ng-package.json pointing at public-api.ts, plus nested ng-package.json files for split-by-domain secondary entrypoints. | You drop the folder inside an existing library so ng-packagr picks it up as a subpath. |
In library mode, output also includes tsconfig.lib.json and tsconfig.lib.prod.json.
ng-package.dest defaults to dist/<output-folder-name>.
Per-domain layout
By default, standalone and secondary-entrypoint targets emit a flat layout
(models/, fn/, services/, … all at the output root). mode: "library"
defaults to splitByDomain: true so the generated package is split into
secondary entrypoints immediately. You can still set splitByDomain: false if
you need the older flat library shape. Cross-domain models and client primitives
land in shared/.
splitDepth controls granularity. It is only read when splitByDomain is true.
| splitDepth | Folder strategy | Example (spec with tags Access/Role, Access/Permission, Storage/GCS, Storage/S3, Auth) |
| ------------ | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| service | One folder per root tag (each tag's parent chain collapses to its root). One folder per backend service. | access/ (holds Role + Permission + Role-Permission + …), storage/ (holds GCS + S3), auth/, shared/, root public-api.ts |
| tag | One folder per leaf tag, nested under the parent chain. Keeps fine-grained separation while staying grouped. | access/role/, access/permission/, access/role-permission/, storage/gcs/, storage/s3/, auth/, shared/, root public-api.ts |
Every domain folder contains services/, fn/, models/, permissions/,
and its own public-api.ts. The root still owns the aggregate metadata barrel:
metadata.ts, openapi-helpers.ts, and permissions/index.ts stay at the SDK
root so shared/ never depends on sibling domains. In standalone and
secondary-entrypoint modes, the root public-api.ts aggregates shared, root
metadata helpers, and every domain for convenience. In library mode, domain
exports stay in their secondary entrypoints so consumers deep import the domain
they need.
Model ownership rule (per-domain mode):
- A model used by exactly one domain → emitted inside that domain's
models/. - A model shared across two or more domains → emitted inside
shared/models/. - Client primitives (
ApiConfiguration,BaseService,RequestBuilder,StrictHttpResponse,Api), shared metadata types, validators, and navigation always live undershared/. - Aggregate metadata helpers (
metadata.ts,openapi-helpers.ts) and the top-levelpermissions/index.tsstay at the SDK root.
Example consumption when using mode: 'library':
import { RoleService } from '@my-scope/sdk/access';
import { ApprovalInstanceService, SubmitRequest } from '@my-scope/sdk/approval';
import { GCSService } from '@my-scope/sdk/storage'; // splitDepth: 'service'
import { GCSService } from '@my-scope/sdk/storage/gcs'; // splitDepth: 'tag'Feature flags
All default to true. Turn off anything you don't need to shrink the output.
| Flag | Emits |
| ------------ | ------------------------------------------------------------------------------------------ |
| models | models/*.ts — flat interfaces, enum aliases, array aliases. |
| operations | fn/<tag>/<operation-id>.ts — tree-shakeable request functions with .PATH. |
| services | services/<tag>.service.ts — @Injectable({providedIn:'root'}) wrappers. |
| client | api-configuration.ts, base-service.ts, request-builder.ts, api.ts. |
| metadata | permissions/*, validators/*, metadata.ts, openapi-helpers.ts. |
| navigation | api.navigation.ts — NavigationItem[] ready for NavigationService.registerItems(...). |
Pipeline
sdk.config.json → loader → spec (YAML/JSON) → IR → emitters → layout → writer → Angular CLI TreeOutput ownership and safe cleanup
Each generated output directory contains .ojiepermana-sdk-manifest.json with
the relative files owned by the generator. A later run deletes stale files only
when they were recorded in that manifest; unrelated source and documentation in
the same directory are preserved.
The schematic rejects output at the workspace root, outside the workspace, or
inside .git/node_modules, and rejects absolute, traversing, duplicate, and
reserved virtual paths. It also refuses to overwrite an unmanaged file whose
content differs. On the first managed run, identical existing files are adopted;
move conflicting manual files (or regenerate into a clean directory) before
running again. Angular CLI --dry-run previews the complete Tree transaction.
Generated runtime conventions
- Tree-shakeable:
import { listUsers } from './sdk/fn/user/list-users'pulls only one HTTP call. - Services: every operation gets
op()(bodyObservable<T>) andop$Response()(fullStrictHttpResponse<T>). RequestBuilderis intentionally minimal — nostyle/explodelogic — to keep the output lightweight.- All files carry a
DO NOT EDITbanner and/* eslint-disable */.
