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

@genexus/kasstor-webkit

v0.3.0

Published

Utilities for web applications: array helpers, internationalization (i18n), type-ahead search, frame synchronization, and shared local storage keys.

Downloads

3,843

Readme

@genexus/kasstor-webkit

Utilities for web applications: array helpers, internationalization (i18n), type-ahead search, frame synchronization, and shared local storage keys.

Table of Contents

Installation

npm i @genexus/kasstor-webkit

Array utilities

Helpers for in-place array insertion and removal. Import from @genexus/kasstor-webkit/array.js.

insertIntoIndex

Inserts a single element at a given index. Mutates the array.

  • Behavior: Uses splice; elements at and after the index shift right. Does not return a value.

Example

import { insertIntoIndex } from "@genexus/kasstor-webkit/array.js";

const items = ["apple", "cherry", "date"];
insertIntoIndex(items, "banana", 1);
console.log(items); // ['apple', 'banana', 'cherry', 'date']

removeIndex

Removes the element at a single index and returns it. Mutates the array.

  • Behavior: Uses splice; subsequent elements shift left. Returns the removed element (or undefined if index out of range).

Example

import { removeIndex } from "@genexus/kasstor-webkit/array.js";

const items = ["apple", "banana", "cherry", "date"];
const removed = removeIndex(items, 1);
console.log(removed); // 'banana'
console.log(items); // ['apple', 'cherry', 'date']

Internationalization (i18n)

Multi-language support with async translation loaders, language detection, URL/path handling, and subscription to language changes. Import from @genexus/kasstor-webkit/internationalization.js.

Feature ID: what is a “feature”?

A feature is the scope of a set of translations. The term is intentionally broad: a feature can be the whole application, a module, or a single component. You can register one feature for shared/app-wide strings, another per area (e.g. “trial”, “common”), or one per component. Each component (or base class) then uses the same featureId in getCurrentTranslations and subscribeToLanguageChanges. The same ID is used when calling registerTranslations for that feature.

Quick start

Minimal example

Suggested layout (paths relative to your app or feature):

src/
├── common/
│   └── feature-ids.ts
├── managers/
│   └── internationalization/
│       ├── locales/
│       │   ├── en.ts
│       │   └── es.ts
│       ├── translations-scheme.ts
│       └── register-translations.ts
└── main.ts          (or router / bootstrap entry)

common/feature-ids.ts — feature ID constant:

export const APP_MAIN_FEATURE_ID = "app-main";

managers/internationalization/translations-scheme.ts — translation schema (type the loader and locale files with this):

export type AppTranslationsSchema = {
  greeting: string;
  farewell: string;
};

managers/internationalization/register-translations.ts — loader and registration (typed so keys and return shape are checked):

import type { KasstorLanguage } from "@genexus/kasstor-webkit";
import { registerTranslations } from "@genexus/kasstor-webkit/internationalization.js";
import { APP_MAIN_FEATURE_ID } from "../../common/feature-ids";
import type { AppTranslationsSchema } from "./translations-scheme";

const loader: Record<KasstorLanguage, () => Promise<AppTranslationsSchema>> = {
  english: () => import("./locales/en").then(m => m.default),
  spanish: () => import("./locales/es").then(m => m.default)
};

export const registerAppTranslations = (): void =>
  registerTranslations(APP_MAIN_FEATURE_ID, loader);

Bootstrap (e.g. main.ts or router). Pass your app's or framework's navigate function so the URL updates when the language changes. Omit pathname when not using server-side rendering.

import { setInitialApplicationLanguage } from "@genexus/kasstor-webkit/internationalization.js";
import { registerAppTranslations } from "./managers/internationalization/register-translations";

registerAppTranslations();

setInitialApplicationLanguage({
  // Your framework or app's function to navigate to a path (e.g. React Router
  // navigate, Angular router, Vue Router). Called with the new pathname when
  // the language changes.
  locationChangeCallback: frameworkNavigateFunction
  // pathname: only pass when using server-side rendering; in the browser
  // without SSR, the current window location is used automatically.
});

What each part does

  1. Locale files — One file per language (e.g. managers/internationalization/locales/en.ts, locales/es.ts), each exporting the translation object (default export or named language). Use a shared type in translations-scheme.ts so all locales satisfy the same schema.

  2. Feature ID constant — Define a constant per feature and use it everywhere (registration, components, base class).

  3. Loader with dynamic imports — Build an object that maps each KasstorLanguage to a function that returns import("./locales/xx").then(m => m.default) (or m.language). Use dynamic import() only; see Best practices > i18n.

  4. Register once — Call registerTranslations(featureId, loader) once per feature: at app bootstrap or when a module loads. Optionally guard so registration runs only once (e.g. for libraries or HMR).

  5. Bootstrap — Call setInitialApplicationLanguage({ locationChangeCallback, pathname? }) once at app startup. locationChangeCallback should call your app's or framework's navigation (e.g. React Router's navigate, Angular router, or history.replaceState) so the URL reflects the new language. Pass pathname only when using server-side rendering; in the browser without SSR, the current window location is used and pathname can be omitted.

  6. In components — Use getCurrentTranslations(featureId) for the initial value. In connectedCallback, call subscribeToLanguageChanges(featureId, newTranslations => { this.translations = newTranslations }) and store the returned ID; in disconnectedCallback, call unsubscribeToLanguageChanges(id).

Locale files and loader shape

  • One file per language — e.g. managers/internationalization/locales/en.ts, locales/es.ts, locales/ar.ts. Each file exports a single object (default export or named like language) that matches your translation schema.

  • Typed schema — Define a TypeScript type (or interface) for that object and use satisfies MySchema in each locale file so keys stay consistent. Use the same type in getCurrentTranslations<MySchema>(featureId) and in the subscribe callback.

  • Loader — An object with a key for every supported language (KasstorLanguage). Each value is a function that returns a Promise of the translation object. Use dynamic import() so the bundler code-splits; each locale is loaded only when that language is selected.

Multiple features and register-once

  • Multiple features — You can split translations into several features (e.g. app-wide strings, a "trial" flow, shared UI). Each feature has its own ID and loader.

  • Register once — Call registerTranslations(featureId, loader) once per feature at app startup. Do not call it inside render or on every request.

  • Guard — If the same module can run more than once (e.g. HMR or a library used by a host app), use a simple guard so registration runs only once.

Example: two features (app-main and trial)

Define the feature IDs:

// common/feature-ids.ts
export const APP_MAIN_FEATURE_ID = "app-main";
export const TRIAL_FEATURE_ID = "trial";

Schema and locales for app-main (welcome, footer):

// managers/internationalization/schemas/app-main-schema.ts
export type AppMainTranslationsSchema = {
  welcome: string;
  footer: string;
};
// managers/internationalization/locales/app-main/en.ts
import type { AppMainTranslationsSchema } from "../schemas/app-main-schema";

export default {
  welcome: "Welcome",
  footer: "© My App"
} satisfies AppMainTranslationsSchema;
// managers/internationalization/locales/app-main/es.ts
import type { AppMainTranslationsSchema } from "../schemas/app-main-schema";

export default {
  welcome: "Bienvenido",
  footer: "© Mi App"
} satisfies AppMainTranslationsSchema;

Schema and locales for trial (pricing, limits):

// managers/internationalization/schemas/trial-schema.ts
export type TrialTranslationsSchema = {
  pricing: string;
  limits: string;
};
// managers/internationalization/locales/trial/en.ts
import type { TrialTranslationsSchema } from "../schemas/trial-schema";

export default {
  pricing: "Pricing",
  limits: "Limits"
} satisfies TrialTranslationsSchema;
// managers/internationalization/locales/trial/es.ts
import type { TrialTranslationsSchema } from "../schemas/trial-schema";

export default {
  pricing: "Precios",
  limits: "Límites"
} satisfies TrialTranslationsSchema;

Loaders and registration:

// managers/internationalization/register-translations.ts
import type { KasstorLanguage } from "@genexus/kasstor-webkit";
import { registerTranslations } from "@genexus/kasstor-webkit/internationalization.js";
import { APP_MAIN_FEATURE_ID, TRIAL_FEATURE_ID } from "../../common/feature-ids";
import type { AppMainTranslationsSchema } from "./schemas/app-main-schema";
import type { TrialTranslationsSchema } from "./schemas/trial-schema";

const appMainLoader: Record<KasstorLanguage, () => Promise<AppMainTranslationsSchema>> = {
  english: () => import("./locales/app-main/en").then(m => m.default),
  spanish: () => import("./locales/app-main/es").then(m => m.default)
};

const trialLoader: Record<KasstorLanguage, () => Promise<TrialTranslationsSchema>> = {
  english: () => import("./locales/trial/en").then(m => m.default),
  spanish: () => import("./locales/trial/es").then(m => m.default)
};

export function registerAllTranslations(): void {
  registerTranslations(APP_MAIN_FEATURE_ID, appMainLoader);
  registerTranslations(TRIAL_FEATURE_ID, trialLoader);
}

Bootstrap: call once in main.ts (or your app entry):

import { registerAllTranslations } from "./managers/internationalization/register-translations";

registerAllTranslations();

In components, use the same feature ID: getCurrentTranslations(APP_MAIN_FEATURE_ID) or getCurrentTranslations(TRIAL_FEATURE_ID).

Getting current language and translations

  • getCurrentLanguage() — Returns { fullLanguageName, subtag } or undefined if no language is set.

  • getCurrentTranslations(featureId) — Returns the translation object for the current language and feature, or undefined if not loaded. Use the same feature ID constant as in registration and subscription.

Example

import {
  getCurrentLanguage,
  getCurrentTranslations
} from "@genexus/kasstor-webkit/internationalization.js";
import { APP_MAIN_FEATURE_ID } from "./common/feature-ids";

const currentLanguage = getCurrentLanguage();
console.log(currentLanguage?.subtag); // e.g. 'en'

const currentTranslations = getCurrentTranslations(APP_MAIN_FEATURE_ID);
console.log(currentTranslations?.greeting); // e.g. 'Hello'

Changing language and subscribing

  • setLanguage(language, executeLocationChange?) — Sets the active language, loads translations, updates document and (optionally) URL, then notifies subscribers. Returns the new pathname if the URL was updated.

  • subscribeToLanguageChanges(featureId, callback) — Subscribes to language changes. The callback receives the new translation object for that feature; set this.translations = newTranslations there. Returns a subscriber ID.

  • unsubscribeToLanguageChanges(subscriberId) — Removes the subscription. Must be called in disconnectedCallback to avoid leaks.

For Lit components, see Using i18n with Lit below.

Using i18n with Lit

The following examples use @genexus/kasstor-core and Lit decorators. Prefer a base component with automatic translations: it simplifies every concrete component (subscribe/unsubscribe and translations updates are handled once in the base), and translations stay in sync when the language changes.

Suggested layout (paths relative to your app; registration and locales as in Quick start / Multiple features):

src/
├── typings/
│   ├── app-metadata.ts
│   └── translation-schemas.ts
├── app-component.ts
├── header.lit.ts
├── footer.lit.ts
├── managers/
│   └── internationalization/
│       ├── locales/
│       │   ├── header/
│       │   │   ├── en.ts
│       │   │   └── es.ts
│       │   └── footer/
│       │       ├── en.ts
│       │       └── es.ts
│       └── register-translations.ts
└── main.ts

Recommended: base component with automatic translations

Use a base class that reads the feature ID from component decorator metadata and manages translations and subscribe/unsubscribe. Concrete components only pass metadata: { featureId: "..." } in the decorator and implement render(). No per-component helper or getTranslationsFeatureId() — the feature is declared once in metadata. Register each feature's translations once at app or base-module load.

1. Metadata and translation schemas

Define a metadata type whose featureId is the same string you use in registerTranslations. Define one translation schema type per feature and a conditional type that maps featureId to the right schema (so each component gets typed translations).

// typings/app-metadata.ts
export type AppMetadata = {
  featureId: "header" | "footer";
};
// typings/translation-schemas.ts
import type { AppMetadata } from "./app-metadata";

export type HeaderTranslationsSchema = { title: string };
export type FooterTranslationsSchema = { copyright: string };

export type AppTranslationsSchema<T extends AppMetadata["featureId"]> =
  T extends "header" ? HeaderTranslationsSchema : FooterTranslationsSchema;

2. Base class and optional custom decorator

The base class uses this.kstMetadata.featureId for getCurrentTranslations and subscribeToLanguageChanges. Optionally, a custom decorator (e.g. AppComponent) wraps Component and requires metadata, so every component is forced to declare its feature.

// app-component.ts
import {
  Component,
  KasstorElement,
  type ComponentOptions
} from "@genexus/kasstor-core/decorators/component.js";
import {
  getCurrentTranslations,
  subscribeToLanguageChanges,
  unsubscribeToLanguageChanges
} from "@genexus/kasstor-webkit/internationalization.js";
import { state } from "lit/decorators";
import type { AppMetadata } from "./typings/app-metadata";
import type { AppTranslationsSchema } from "./typings/translation-schemas";

export const AppComponent = <
  Metadata extends AppMetadata,
  T extends typeof AppElement<Metadata>
>(
  options: ComponentOptions<"app-", Metadata> & { metadata: Metadata }
) => Component<"app-", Metadata, T>(options);

export abstract class AppElement<Metadata extends AppMetadata> extends KasstorElement<Metadata> {
  @state() protected translations: AppTranslationsSchema<Metadata["featureId"]> | undefined =
    getCurrentTranslations(this.kstMetadata!.featureId);

  #subscriberId!: string;

  override connectedCallback(): void {
    super.connectedCallback();
    this.#subscriberId = subscribeToLanguageChanges(
      this.kstMetadata!.featureId,
      newTranslations => {
        this.translations = newTranslations as AppTranslationsSchema<Metadata["featureId"]>;
      }
    );
  }

  override disconnectedCallback(): void {
    super.disconnectedCallback();
    unsubscribeToLanguageChanges(this.#subscriberId);
  }
}

3. Concrete components

Each component defines a metadata constant (with featureId) and passes it to the decorator. It extends the base with that metadata type so this.translations is typed correctly. Only render() is implemented.

// header.lit.ts
import { html } from "lit";
import { AppComponent, AppElement } from "./app-component";
import type { AppMetadata } from "./typings/app-metadata";

const headerMetadata = { featureId: "header" } as const satisfies {
  featureId: AppMetadata["featureId"];
};

@AppComponent({ tag: "app-header", metadata: headerMetadata })
export class AppHeaderElement extends AppElement<typeof headerMetadata> {
  override render() {
    return html`<h1>${this.translations?.title ?? ""}</h1>`;
  }
}
// footer.lit.ts
import { html } from "lit";
import { AppComponent, AppElement } from "./app-component";
import type { AppMetadata } from "./typings/app-metadata";

const footerMetadata = { featureId: "footer" } as const satisfies {
  featureId: AppMetadata["featureId"];
};

@AppComponent({ tag: "app-footer", metadata: footerMetadata })
export class AppFooterElement extends AppElement<typeof footerMetadata> {
  override render() {
    return html`<footer>${this.translations?.copyright ?? ""}</footer>`;
  }
}

If you don't use a custom decorator, use @Component from @genexus/kasstor-core/decorators/component.js and pass metadata: { featureId: "header" } (or "footer") in the options; the base class and concrete components stay the same.

Alternative: single component (no base class)

If you prefer not to use a base class, subscribe in connectedCallback and unsubscribe in disconnectedCallback; in the callback, set this.translations = newTranslations.

import { Component, KasstorElement } from "@genexus/kasstor-core/decorators/component.js";
import {
  getCurrentTranslations,
  subscribeToLanguageChanges,
  unsubscribeToLanguageChanges
} from "@genexus/kasstor-webkit/internationalization.js";
import { html } from "lit";
import { state } from "lit/decorators";
import { GREETING_FEATURE_ID } from "../common/feature-ids";

/**
 * Greeting that subscribes to i18n changes for a feature; no base class.
 * @access public
 */
@Component({ tag: "app-greeting" })
export class AppGreeting extends KasstorElement {
  @state() private translations = getCurrentTranslations(GREETING_FEATURE_ID);
  #subscriberId: string | null = null;

  override connectedCallback(): void {
    super.connectedCallback();
    this.#subscriberId = subscribeToLanguageChanges(GREETING_FEATURE_ID, newTranslations => {
      this.translations = newTranslations;
    });
  }

  override disconnectedCallback(): void {
    if (this.#subscriberId !== null) {
      unsubscribeToLanguageChanges(this.#subscriberId);
      this.#subscriberId = null;
    }
    super.disconnectedCallback();
  }

  override render() {
    return html`<h1>${this.translations?.greeting ?? ""}</h1>`;
  }
}

TypeAhead

Type-ahead search over a navigable list: users type characters sequentially; the query resets after a configurable delay. Suited for lists, dropdowns, or keyboard navigation. Import from @genexus/kasstor-webkit/type-ahead.js.

Usage

Create a TypeAhead<Index> with options that describe how to get captions and traverse indices. Call search(character, activeItemIndex) with the typed character and the current active index (or undefined/null if none). Returns the index of the first matching item, or null if no match.

  • Behavior: Search is case-insensitive. Characters are accumulated until the delay (default 512 ms) passes between calls. Search starts from the next item after the active one and wraps to the start if needed. Repeating the same letter cycles through matches starting with that letter.

Example: string array by index

import { TypeAhead } from "@genexus/kasstor-webkit/type-ahead.js";

const items = ["Apple", "Banana", "Blueberry", "Cherry"];
const typeAhead = new TypeAhead<number>({
  getCaptionFromIndex: i => items[i],
  getFirstIndex: () => 0,
  getNextIndex: i => (i + 1 < items.length ? i + 1 : null),
  isSameIndex: (a, b) => a === b,
  delay: 400
});

let active: number | null = null;
active = typeAhead.search("b", active); // 1 (Banana)
active = typeAhead.search("b", active); // 2 (Blueberry) — same letter cycles
active = typeAhead.search("c", active); // 3 (Cherry)

Frame synchronization

SyncWithRAF batches work to run on the next animation frame. Use it for scroll or resize handlers to coalesce updates and avoid layout thrash. Import from @genexus/kasstor-webkit/sync-with-frames.js.

API

  • perform(computationInFrame, computationBeforeFrame?) — Schedules a callback to run on the next frame. If perform is called multiple times before the frame, only the first computationInFrame runs in the frame; the optional computationBeforeFrame runs synchronously on every call (e.g. to capture scroll position).

  • cancel() — Cancels the scheduled frame work.

Example

import { SyncWithRAF } from "@genexus/kasstor-webkit/sync-with-frames.js";

const sync = new SyncWithRAF();

element.addEventListener("scroll", () => {
  sync.perform(
    () => {
      updateVisibleRange();
      requestUpdate();
    },
    () => {
      scrollTop = element.scrollTop;
    }
  );
});

Local storage keys

Kasstor may store references in localStorage to improve the user experience. Currently, the only value stored is the user’s last selected language (so it can be restored in future sessions). The key is exposed as SHARED_LOCAL_STORAGE_KEYS.LANGUAGE. Import from @genexus/kasstor-webkit/shared-local-storage-keys.js.

The goal is to keep the last selected language across sessions. When you clear localStorage (e.g. on logout, reset, or “clear app data”), iterate over the keys and do not remove this one so the user's language preference is preserved.

Example: clearing app data while keeping the language preference

import { SHARED_LOCAL_STORAGE_KEYS } from "@genexus/kasstor-webkit/shared-local-storage-keys.js";

function clearAppLocalStorage(): void {
  const keys = Object.keys(localStorage);

  for (let i = 0; i < keys.length; i++) {
    const key = keys[i];

    if (key !== SHARED_LOCAL_STORAGE_KEYS.LANGUAGE) {
      localStorage.removeItem(key);
    }
  }
}

Best practices

i18n

Do: use dynamic imports and type the loader

Loader values must use dynamic import(). The bundler then code-splits: each locale is a separate chunk and is loaded only when that language is selected, improving initial load by not including unused translations in the main bundle. Type the loader as Record<KasstorLanguage, () => Promise<YourSchema>> so language keys and return types are checked and typos are caught.

import type { KasstorLanguage } from "@genexus/kasstor-webkit";
import type { AppTranslationsSchema } from "./translations-scheme";

const loader: Record<KasstorLanguage, () => Promise<AppTranslationsSchema>> = {
  english: () => import("./locales/en").then(m => m.default),
  spanish: () => import("./locales/es").then(m => m.default)
};

Don't: static import + Promise.resolve

Statically importing locale files and wrapping them in a Promise pulls every language into the main bundle, so users download all locales on first load.

import en from "./locales/en";
import es from "./locales/es";
const loader = {
  english: () => Promise.resolve(en),
  spanish: () => Promise.resolve(es)
};

Do

  • Use a feature ID constant everywhere (registration, getCurrentTranslations, subscribeToLanguageChanges).

  • In the subscribe callback, assign this.translations = newTranslations; the callback receives the new object for your feature.

  • Call unsubscribeToLanguageChanges(subscriberId) in disconnectedCallback to avoid leaks.

  • Call setInitialApplicationLanguage once at app startup.

  • Register each feature once (e.g. in a dedicated module or at bootstrap); use a guard if the module can run multiple times (e.g. HMR or library in host).

  • Await languageHasBeenInitialized() before reading language/translations when you need the first load to be complete.

Don't

  • Register the same feature more than once per feature.

  • Use static imports for locale modules in the loader.

TypeAhead

  • Create one TypeAhead instance per list and reuse it (e.g. in a class field). Do not create a new instance on every keystroke.

SyncWithRAF

  • Use computationBeforeFrame to capture values that change on every event (e.g. scroll position); use computationInFrame for DOM updates or component updates so they run once per frame.

API Reference

Array (@genexus/kasstor-webkit/array.js)

  • insertIntoIndex<T>(array: T[], element: T, index: number): T[] — Inserts one element at index; mutates the array.

  • removeIndex<T>(array: T[], index: number): T — Removes the element at index and returns it; mutates the array. May return undefined if index is out of range.

Internationalization (@genexus/kasstor-webkit/internationalization.js)

  • registerTranslations(featureId, loader) — Registers async loaders per language for a feature. Replaces existing loader for the same ID (e.g. HMR).

  • setInitialApplicationLanguage(options) — Sets initial language from URL or client; requires locationChangeCallback, optional pathname (required on server), optional languageChangeCallback. Returns { initialLanguage, locationToReplace }. Throws on server without pathname.

  • setLanguage(language, executeLocationChange?) — Sets active language, loads translations, updates document/URL, notifies subscribers. Returns new pathname or undefined.

  • getCurrentLanguage() — Returns { fullLanguageName, subtag } or undefined.

  • getCurrentTranslations(featureId) — Returns translations for current language and feature, or undefined.

  • getClientLanguage() — Returns preferred subtag (local storage or navigator); never null.

  • getLanguageFromUrl(pathname?) — Returns two-letter language from path, or null.

  • languageHasBeenInitialized() — Returns a Promise that resolves when language is initialized.

  • subscribeToLanguageChanges(featureId, callback) — Subscribes to language changes; returns subscriber ID.

  • unsubscribeToLanguageChanges(subscriberId) — Removes subscription; returns true if removed.

  • fromLanguageFullnameToSubtag(fullname) — Returns subtag for a full language name.

  • fromLanguageToFullnameAndSubtag(language) — Returns { fullLanguageName, subtag }.

  • ALL_SUPPORTED_LANGUAGE_SUBTAGSSet of supported subtags.

TypeAhead (@genexus/kasstor-webkit/type-ahead.js)

  • class TypeAhead<Index> — Type-ahead search over a generic index structure.
    • Constructor: { getCaptionFromIndex, getFirstIndex, getNextIndex, isSameIndex, delay? }.
    • search(character: string, activeItemIndex: Index | null | undefined): Index | null — Returns first match for the accumulated query; resets query after delay ms of inactivity.

Frame synchronization (@genexus/kasstor-webkit/sync-with-frames.js)

  • class SyncWithRAF — Batches work to the next animation frame.
    • perform(computationInFrame, computationBeforeFrame?) — Schedules one callback for the next frame; optional immediate callback runs on every call.
    • cancel() — Cancels the scheduled frame work.

Local storage keys (@genexus/kasstor-webkit/shared-local-storage-keys.js)

  • SHARED_LOCAL_STORAGE_KEYS — Keys used by webkit in localStorage (e.g. LANGUAGE for the last selected language). Use when clearing localStorage so those keys are not left behind.

Contributing

Kasstor is open source and we appreciate issue reports and pull requests. See CONTRIBUTING.md for more information.