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

@edsis/angular

v22.1.6

Published

Readme

@edsis/angular

Angular library package for Edsis shared application features.

Published entry points:

  • @edsis/angular
  • @edsis/angular/services/runtime
  • @edsis/angular/module/auth
  • @edsis/angular/platform/mfe

The generated API SDK is published separately as @edsis/sdk.

module/auth: persistent cookie session

Configure the module once at application bootstrap. provideEdsisAuth() runs AuthService.initialize() through provideAppInitializer, before initial navigation and route guards. It restores a session with the HttpOnly refresh cookie, while the access token is kept only in the running application's memory.

provideEdsisAuth({
  apiUrl: environment.apiUrl,
  appUrl: environment.appUrl,
});

Use AuthService for password login/logout and its user, accessToken, and isAuthenticated signals for UI state. The library calls the configured /v1/auth/* endpoints with credentials. The interceptor adds withCredentials and the in-memory bearer token to API requests, coalesces concurrent 401 refreshes, retries once, and redirects to the configured login route when refresh fails. It never writes authentication tokens to sessionStorage, localStorage, or document.cookie.

For optional native notification badges, call setAuthNotificationBadge(unreadCount); it is a no-op in a normal browser and uses the bundled Tauri API when a native host is present.

The persistent cookie belongs to the browser/WebView origin. Therefore a production, development, and local build must use distinct origins; keeping an origin and its WebView data directory stable preserves the session across a Tauri restart or update.

services/runtime: desktop, tablet, and browser context

Register provideEdsisRuntime() before rendering the structural shell. It validates a schema V2 window.EDSIS_RUNTIME, falls back to the runtime_context Tauri command and edsis:runtime-ready event, then safely resolves a responsive web context in an ordinary browser. Trusted schema V1 payloads are accepted and normalized to V2 during migration. V2 adds the canonical deviceType value: browser, windows, macos, android, or ipados.

RuntimeContextService exposes deviceType plus signals for desktop, iPadOS, Android tablet, and the shell capability list without using the User-Agent. It also writes the resolved host/platform/device/device-type/profile to data-edsis-* attributes on the document root. browser is reserved for a non-Tauri host; an invalid native response fails initialization instead of being silently mislabeled as a browser.

When the consumer also uses provideUiTheme(), register the opt-in bridge so UI components and CSS receive the same resolved device:

import { provideRuntimeThemeDevice } from '@edsis/angular/block/themes';
import { provideEdsisRuntime } from '@edsis/angular/services/runtime';
import { provideUiTheme } from '@edsis/theme/styles';

providers: [provideUiTheme(), provideEdsisRuntime(), provideRuntimeThemeDevice()];

The bridge waits for runtime initialization and then updates ThemeDeviceService once. This keeps the base runtime entry point independent from the optional theme package.

TabletViewportService reports landscape and the 600 CSS pixel minimum. The consumer decides where to render its blocking view and must gate it with RuntimeContextService.isTabletShell() so narrow browser windows are not blocked.

module/auth: Magic-link destination

When the login form is submitted, the auth module sends the origin derived from appUrl as frontend_url. This lets the backend keep only the route path in its environment configuration:

AUTH_SERVICE_MAGIC_LINK_VERIFY_URL=/verify

For example, an app configured with appUrl: 'https://edsis.dev' submits frontend_url: 'https://edsis.dev'; the backend creates the final destination https://edsis.dev/verify#token=.... An absolute backend verify URL remains a supported fallback for older deployments.

module/auth: Google Sign-In

@edsis/angular/module/auth ships two discriminated Google Sign-In modes. The existing direct authorization-code mode remains backward compatible and stays disabled until a client ID is configured:

provideEdsisAuth({
  apiUrl: environment.apiUrl,
  appUrl: environment.appUrl,
  oauth: {
    google: { clientId: environment.google.clientId },
    nativeGoogle: { apiUrl: '/api' },
  },
});

On desktop/browser the existing authorization-code redirect remains unchanged. A tablet runtime advertising google-native-sign-in instead asks the same-origin challenge endpoint, invokes the native Google adapter, and posts its short-lived ID token to /api/v1/auth/google/native. Refresh, user, and logout requests then stay on /api so the host-only HttpOnly cookie is first-party. A tablet never falls back to the browser OAuth redirect: when the native capability/plugin is unavailable, Google Sign-In is disabled with an explicit error. Access, refresh, and Google ID tokens are never persisted by JavaScript.

  • redirectUri is not configurable; the library derives it from appUrl as ${appUrl}/auth/google/callback and authRoutes serves that path with AuthGoogleCallbackPage. Register exactly this URI in the Google Cloud Console.
  • An empty clientId is treated as "not configured", so a build-time injected environment value can be passed through as-is.
  • Clicking the Google button stores a crypto-random state in sessionStorage (auth-google-state) and performs a full-page redirect to https://accounts.google.com/o/oauth2/v2/auth with client_id, redirect_uri, response_type=code, scope=openid email profile, and state.
  • Microsoft and Apple remain placeholders.

Central Auth Gateway mode for MFEs

An MFE does not need its own Google client configuration. Select gateway mode with the lower-kebab mfeId (maximum 64 characters) registered in the central Gateway allowlist:

provideEdsisAuth({
  apiUrl: environment.apiUrl,
  appUrl: environment.appUrl,
  oauth: {
    google: {
      mode: 'gateway',
      mfeId: 'developer-guide',
      // Optional. Omit `gateway` to use `apiUrl` and the default endpoints.
      gateway: {
        baseUrl: `${environment.apiUrl}/auth-gateway`,
        // Optional; these are the defaults:
        startEndpoint: '/v1/auth/google/start',
        handoffEndpoint: '/v1/auth/google/handoff',
      },
    },
  },
});

The library derives the exact MFE returnUrl from appUrl as ${appUrl}/auth/google/callback. Register that pair with mfeId on the Gateway. Only the Gateway callback is registered with Google. gateway and gateway.baseUrl are optional and default to apiUrl. An explicit baseUrl may use a different path, but its origin (scheme, host, and port) must exactly match apiUrl; configuration fails immediately otherwise. This keeps the handoff, refresh, current-user, and logout requests on one cookie origin. baseUrl must use HTTPS; plain HTTP is accepted only for exact loopback hosts (localhost, 127.0.0.1, and [::1]) during local development. The browser supplies the trusted Origin; the Gateway requires it to equal the origin of returnUrl.

The application origin must also be schemeful same-site with this cookie origin, or route auth through an application-owned same-origin BFF. CORS plus SameSite=None is not sufficient on browsers that block third-party cookies; cross-site deployments must not be promoted until login, reload/refresh, and logout pass with third-party cookies blocked.

The browser flow is:

  1. create a 256-bit PKCE verifier and S256 challenge;
  2. POST {gateway.baseUrl}{startEndpoint} with { mfeId, returnUrl, codeChallenge, codeChallengeMethod: 'S256' };
  3. accept only an authorization URL on the exact trusted Google https://accounts.google.com/o/oauth2/v2/auth endpoint, then redirect;
  4. receive only handoffCode at the MFE callback, scrub all callback query parameters, and consume the verifier once;
  5. POST {gateway.baseUrl}{handoffEndpoint} with credentials and { mfeId, returnUrl, handoffCode, codeVerifier };
  6. pass the session response to AuthService.acceptTokenResponse() and keep its access token in memory. Refresh, current-user, and logout then use the central Gateway's HttpOnly refresh cookie.

sessionStorage contains only auth-google-gateway-session: the PKCE verifier, mfeId, returnUrl, and expiry metadata. It never contains an access token, refresh token, Google token, authorization URL, or handoff code. The record is removed before every success/error handoff branch so reload and replay fail closed. A failed start or handoff returns the UI to a retryable error state.

Backend contract

The code exchange happens on the backend so the client secret never reaches the frontend. The callback page calls the generated SDK method AuthSessionService.exchangeGoogleCode(...) from @edsis/sdk/auth (no raw HttpClient, consistent with verifyMagicLink/refreshAuthToken), which maps to:

POST {apiUrl}/v1/auth/google
Body: { "code": string, "redirectUri": string }

Because the path lives under /v1/auth/, edsisAuthInterceptor sends it with cookie credentials automatically. The endpoint:

  1. exchanges code at Google's token endpoint using the server-side client secret and the received redirectUri;
  2. sets the refresh-token cookie, exactly like magic-link verify does;
  3. responds with a session payload identical to POST /v1/auth/verify ({ status, message, data: { access_token, user_id, role, name } }).

On success the callback page passes the response to AuthService, which keeps the access token only in memory, then redirects to authenticatedHomePath; on failure it shows the backend message when one is provided (with a retry link back to loginPath).

The endpoint definition comes from openapi.bundle.yaml; regenerate the SDK in library/sdk after spec changes with bun run gen:sdk (driven by config/sdk.config.json, generator: @ojiepermana/angular-sdk).

platform/mfe: standalone profile and hybrid contract

Every MFE registry entry declares standaloneProfile: 'mfe-standalone'; the remote contract does not duplicate that registry-owned value. The named profile centralizes the current Developer standalone defaults: desktop/compact theme, base/horizontal/full layout, and horizontal/flyout/top navigation. Hosted routes/components inherit the shell singletons and must not register standalone providers again.

import { defineMfe, provideStandaloneMfe, readMfeContract } from '@edsis/angular/platform/mfe';

export const mfe = defineMfe({
  contractVersion: 1,
  id: 'developer-guide',
  displayName: 'Developer Guide',
  featureRoutes,
  navigation,
  capabilities,
  exposures,
});

bootstrapApplication(App, {
  providers: [
    provideStandaloneMfe({
      standaloneProfile: registration.standaloneProfile,
      auth: environment.auth,
      runtime: environment.runtime,
    }),
  ],
});

provideStandaloneMfe() owns runtime, theme, profile preferences, navigation defaults, and auth bootstrap for standalone mode. Shared Edsis components own their accessibility behavior; no parallel per-MFE a11y singleton is required.

Federation hosts load the remote namespace through the public boundary helper:

const contract = readMfeContract(remoteModule, {
  id: registration.id,
  supportedContractVersions: registration.supportedContractVersions,
});

The helper requires the exact named mfe export, validates the complete V1 shape first, then checks registry ID and supported contract majors. Shape failures throw MfeContractValidationError with diagnostic issues; namespace and registry-expectation failures use the same error class with an empty issue array and a boundary-specific message.

Generate and build

Use the root workspace scripts so SDK generation and library packaging stay in sync:

bun run build:library

That command builds the UI package, then the SDK, and finally packages this library:

bun run build:ui
bun run gen:sdk
bunx ng build sdk
bunx ng build library

The packaged output is written to dist/library, including the reusable @edsis/angular/module/auth entry point.

Full release

Run the guarded release flow with:

bun run release:library

The script will:

  • reject the release if the git worktree is dirty
  • reject the release if the current branch still has commits that are not pushed, is behind, or is diverged from its upstream branch
  • ask for a version bump using X, Y, or Z
  • map X to major, Y to minor, and Z to patch, then increment the current version automatically
  • ask for a final confirmation before continuing
  • run bun run build:library
  • commit the version bump and generated SDK updates with update version ke {{version}}
  • push the commit
  • publish dist/library to the public npm registry

For a non-interactive release, provide the bump and confirmation flags explicitly:

bun run release:library --bump Z --yes

Preview the release plan without changing files:

bun run release:library:dry-run --bump Y --yes

Versioning

Update the publishable package version in library/angular/package.json with:

bun run version:library patch
bun run version:library minor
bun run version:library 1.2.3
bun run version:library prerelease --preid beta

The version script only updates the source package manifest. The built package in dist/library picks up the version on the next bun run build:library.

Pack and publish locally

Run the interactive local publish workflow with:

bun run publish

The script will:

  • verify the active npm login against the public npm registry
  • ask for a version bump using X, Y, or Z
  • update library/sdk/package.json, config/sdk.config.json, and library/angular/package.json
  • run bun run build:library, which builds the UI package, then the SDK, and finally the library
  • publish dist/sdk and then dist/library to npm public with --access public

For a non-interactive publish, provide the bump and confirmation flags explicitly:

bun run publish --bump Z --yes

Preview the publish plan without changing files:

bun run publish --dry-run --bump Y --yes

Publish only the SDK package:

bun run publish --sdk --bump Z --yes

Publish only the library package:

bun run publish --lib --bump Z --yes

Preview the npm tarball:

bun run pack:library

Publish to npm:

bun run publish
bun run publish --tag next
bun run publish:sdk
bun run publish:library

Run a local publish dry-run:

bun run publish:sdk:dry-run
bun run publish:library:dry-run

When updating @ojiepermana/angular, use the helper below instead of a plain bun update so the version pin, patch mapping, install step, and SDK build verification stay in sync:

bun run update:edsis

If a specific version needs to be tested:

bun run update:edsis 21.1.11

publish publishes @edsis/sdk before @edsis/angular. publish:sdk and publish:library use the same helper with --sdk or --lib and force --access public.

For local publishes, the script does not add --provenance by default because npm provenance generation is not supported outside compatible CI providers. If you need provenance in a supported CI environment, pass it explicitly:

bun run publish --provenance

GitHub publish workflow

The repository includes .github/workflows/publish-library.yml.

It supports:

  • pushing a tag named library-vX.Y.Z
  • manual workflow_dispatch runs with an npm tag and optional dry-run

The workflow uses npm trusted publishing via GitHub Actions OIDC, so it does not require a repository NPM_TOKEN secret. The npm package must be configured to trust this repository workflow as a publisher. On tag pushes, the tag version must match library/angular/package.json.

Recommended local release flow:

  1. Run bun run release:library.
  2. Select X, Y, or Z, then confirm the computed version.
  3. Let the script build, commit, push, and publish the package.