open-index/open-npm huggingface dataset:
npm package: @lopatnov/namespace-component
Description: Reusable components for @lopatnov/namespace. Template + scoped CSS + reactive state + lifecycle hooks — no Shadow DOM, jQuery-compatible.
Author: lopatnov
Latest version: 3.0.1

# @lopatnov/namespace-component

[![npm](https://img.shields.io/npm/v/@lopatnov/namespace-component)](https://www.npmjs.com/package/@lopatnov/namespace-component)
[![license](https://img.shields.io/npm/l/@lopatnov/namespace-component)](LICENSE)

Reusable components for `@lopatnov/namespace`. Template + scoped CSS + reactive state + lifecycle hooks — no Shadow DOM, jQuery-compatible.

## Install

```sh
npm install @lopatnov/namespace-mvvm @lopatnov/namespace-component
```

## Quick start

```ts
import { defineComponent, mount } from '@lopatnov/namespace-component';

const Counter = defineComponent({
  name: 'Counter',
  template: `
    <div class="counter">
      <span data-bind="text: count"></span>
      <button data-bind="click: increment">+1</button>
    </div>
  `,
  styles: `.counter { display: flex; gap: 8px; align-items: center; }`,
  state: (props: { initial?: number }) => ({
    count: props.initial ?? 0,
  }),
  methods: {
    increment() { this.count++; },
  },
  mounted(el) { console.log('Counter mounted', el); },
  unmounted() { console.log('Counter destroyed'); },
});

// Mount into a container
const instance = mount(Counter, document.getElementById('app'), { initial: 5 });

// Later: clean up
instance.destroy();
```

## API

### `defineComponent(options)`

Type-safe identity helper — ensures correct inference of `Props` and `State`. Returns the same options object.

### `mount(def, container, props?)`

Renders the component into `container`, injects scoped styles (once per component name), creates reactive state, wires bindings, and calls `mounted`. Returns a `ComponentInstance`.

```ts
interface ComponentInstance {
  el: HTMLElement;      // root DOM element
  destroy(): void;      // unmount and clean up
}
```

### ComponentOptions

```ts
interface ComponentOptions<Props, State> {
  name: string;
  template: string;
  styles?: string;                               // injected once as <style data-ns-component="name">
  state?: (props: Props) => State;
  methods?: Record<string, (this: State & Props, ...args: unknown[]) => unknown>;
  mounted?: (el: HTMLElement) => void;
  unmounted?: () => void;
}
```

### Reactivity

Component state is wrapped in `reactive()` from `@lopatnov/namespace-mvvm`. All `data-bind` attributes in the template are resolved automatically.

## License

Apache-2.0 © [lopatnov](https://github.com/lopatnov)
npm package: @lopatnov/namespace-guards
Description: Route and service guards for @lopatnov/namespace. Intercept navigation and protect namespace keys with access checks.
Author: lopatnov
Latest version: 3.0.1

# @lopatnov/namespace-guards

[![npm](https://img.shields.io/npm/v/@lopatnov/namespace-guards)](https://www.npmjs.com/package/@lopatnov/namespace-guards)
[![license](https://img.shields.io/npm/l/@lopatnov/namespace-guards)](LICENSE)

Route guards and namespace access checks for `@lopatnov/namespace`. Intercept navigation with predicates, redirect unauthenticated users, and protect namespace keys from unauthorized reads.

## Install

```sh
npm install @lopatnov/namespace @lopatnov/namespace-router @lopatnov/namespace-guards
```

## Quick start

### Route guard

```ts
import { createNamespace, get } from '@lopatnov/namespace';
import { createRouter, start } from '@lopatnov/namespace-router';
import { guard } from '@lopatnov/namespace-guards';

const ns = createNamespace();
const router = createRouter(ns, { mode: 'hash', root: '#app' });

// Redirect unauthenticated users
const unguard = guard(router, '/dashboard/*', ({ path }) => {
  const user = get(ns, 'user');
  return user ? true : '/login';
});

start(router);

// Remove the guard when no longer needed
unguard();
```

### Namespace protection

```ts
import { protect, allowed } from '@lopatnov/namespace-guards';

protect(ns, 'admin.config', () => get<boolean>(ns, 'user.isAdmin') === true);

allowed(ns, 'admin.config'); // false if user is not admin
```

## API

| Function | Description |
|----------|-------------|
| `guard(router, pattern, fn)` | Register a route guard. Returns unsubscribe function |
| `protect(ns, key, fn)` | Protect a namespace key with an access predicate. Returns unsubscribe function |
| `allowed(ns, key)` | Returns `true` if all predicates for the key pass |

### Guard function

```ts
type GuardFn = (ctx: GuardContext) => boolean | string;

interface GuardContext {
  path: string;
  query: URLSearchParams;
}
```

Return values:
- `true` — allow navigation
- `false` — cancel (stay on current page)
- `string` — redirect to this path

### Patterns

Route patterns support `:param` named segments and `*` wildcard, identical to `route()` in `@lopatnov/namespace-router`.

## License

Apache-2.0 © [lopatnov](https://github.com/lopatnov)
npm package: @lopatnov/namespace-microfrontends
Description: Cross-app/cross-tab communication bus and leader election for @lopatnov/namespace. BroadcastChannel event bus and localStorage-based leader election.
Author: lopatnov
Latest version: 3.0.1

# @lopatnov/namespace-microfrontends

[![npm](https://img.shields.io/npm/v/@lopatnov/namespace-microfrontends)](https://www.npmjs.com/package/@lopatnov/namespace-microfrontends)
[![license](https://img.shields.io/npm/l/@lopatnov/namespace-microfrontends)](LICENSE)

Cross-tab communication bus and leader election for `@lopatnov/namespace`. `BroadcastChannel`-based event bus with a `localStorage` heartbeat leader election — zero dependencies.

## Install

```sh
npm install @lopatnov/namespace-microfrontends
```

## Quick start

### Event bus

```ts
import { createBus } from '@lopatnov/namespace-microfrontends';

const bus = createBus('my-app');

// Subscribe in any tab
const unsubscribe = bus.on<{ user: string }>('login', ({ user }) => {
  console.log('User logged in:', user);
});

// Emit from any tab — all tabs receive it
bus.emit('login', { user: 'Alice' });

// Clean up
unsubscribe();
bus.destroy();
```

### Leader election

```ts
import { leaderElection } from '@lopatnov/namespace-microfrontends';

const election = leaderElection('my-app');

if (election.isLeader()) {
  // Only this tab runs background tasks
  startPolling();
}

election.onLeaderChange((isLeader) => {
  if (isLeader) startPolling();
  else stopPolling();
});

// Clean up when navigating away
election.destroy();
```

## API

### `createBus(channelName): Bus`

Creates a `BroadcastChannel`-based event bus. Messages are delivered to **all** tabs and windows sharing the same channel name, including the sender's own local subscribers.

Falls back to a local-only bus if `BroadcastChannel` is not available.

```ts
interface Bus {
  on<T>(event: string, cb: (data: T) => void): () => void;
  emit<T>(event: string, data?: T): void;
  destroy(): void;
}
```

### `leaderElection(name): LeaderElection`

Elect one leader among all tabs using a `localStorage` heartbeat. Leadership transfers automatically when the current leader closes.

```ts
interface LeaderElection {
  isLeader(): boolean;
  onLeaderChange(cb: (isLeader: boolean) => void): () => void;
  destroy(): void;
}
```

### Notes

- `bus.emit()` delivers messages to local subscribers **and** other tabs. There is no separate "local" emit — use a `sender` field in your payload to filter out echoes if needed.
- `leaderElection` stores its state in `localStorage` under `ns-leader:<name>`. The heartbeat interval is 1 second; a tab is considered dead after 2 missed heartbeats.

## License

Apache-2.0 © [lopatnov](https://github.com/lopatnov)
npm package: @lopatnov/namespace-mvvm
Description: Reactive DOM bindings for @lopatnov/namespace. Knockout.js-style data-bind with ES2024 Proxy — no virtual DOM, no JSX, works with jQuery.
Author: lopatnov
Latest version: 3.0.1

# @lopatnov/namespace-mvvm

[![npm](https://img.shields.io/npm/v/@lopatnov/namespace-mvvm)](https://www.npmjs.com/package/@lopatnov/namespace-mvvm)
[![license](https://img.shields.io/npm/l/@lopatnov/namespace-mvvm)](LICENSE)

Reactive DOM bindings for `@lopatnov/namespace`. Knockout.js-style `data-bind` attributes powered by ES2024 `Proxy` — no virtual DOM, no JSX, works alongside jQuery.

## Install

```sh
npm install @lopatnov/namespace-mvvm
```

## Quick start

```ts
import { reactive, bind } from '@lopatnov/namespace-mvvm';

const state = reactive({ name: 'Alice', count: 0 });

// Bind to a DOM container
bind(state, '#app');

// Any assignment triggers DOM update automatically
state.name = 'Bob';
state.count++;
```

```html
<div id="app">
  <p data-bind="text: name"></p>
  <p data-bind="text: count"></p>
  <input data-bind="value: name" />
  <button data-bind="click: increment">+1</button>
</div>
```

## Bindings

| Binding | Description |
|---------|-------------|
| `text: expr` | Sets `element.textContent` |
| `html: expr` | Sets `element.innerHTML` |
| `value: expr` | Two-way binding for `<input>`, `<select>`, `<textarea>` |
| `checked: expr` | Two-way binding for checkboxes |
| `visible: expr` | Toggles `display: none` |
| `css: { class: expr }` | Conditionally add/remove CSS classes |
| `attr: { name: expr }` | Set arbitrary attributes |
| `click: method` | Attach a click handler (method name on state) |
| `foreach: array` | Repeat child template for each item |
| `if: expr` | Conditionally render element |

## API

### `reactive<T>(obj: T): T`

Wraps an object in an ES2024 `Proxy`. Property reads inside active effects are tracked; assignments automatically notify dependents.

> Arrays: use immutable updates to trigger reactivity:
> `state.items = [...state.items, newItem]`
> In-place mutations (`push`, `pop`, etc.) do not auto-trigger.

### `bind(state, root): () => void`

Walk the DOM under `root` (CSS selector or `Element`), wire up all `data-bind` attributes, and subscribe to `state` changes. Returns a cleanup function that detaches all listeners.

## License

Apache-2.0 © [lopatnov](https://github.com/lopatnov)
npm package: @lopatnov/namespace-plugin
Description: Plugin system for @lopatnov/namespace — definePlugin, usePlugin, installed, uninstallPlugin. Tree-shakeable, ES2024.
Author: lopatnov
Latest version: 3.0.1

# @lopatnov/namespace-plugin

[![npm](https://img.shields.io/npm/v/@lopatnov/namespace-plugin)](https://www.npmjs.com/package/@lopatnov/namespace-plugin)
[![license](https://img.shields.io/npm/l/@lopatnov/namespace-plugin)](LICENSE)

Plugin system for `@lopatnov/namespace` pure-function users. Defines, installs, queries, and uninstalls plugins on a namespace — without `createApp`.

> If you use `createApp()`, the plugin system is built-in via `app.use(plugin)`. This package is for developers using the tree-shakeable functions directly.

## Install

```sh
npm install @lopatnov/namespace @lopatnov/namespace-plugin
```

## Quick start

```ts
import { createNamespace } from '@lopatnov/namespace';
import { definePlugin, usePlugin, installed, uninstallPlugin } from '@lopatnov/namespace-plugin';

// Define a plugin
const LogPlugin = definePlugin({
  id: 'log',
  install(ns) {
    set(ns, 'logger', console);
  },
  uninstall(ns) {
    remove(ns, 'logger');
  },
});

const ns = createNamespace();
usePlugin(ns, LogPlugin);          // install (idempotent)
installed(ns, LogPlugin);          // true
installed(ns, 'log');             // true — check by id string
uninstallPlugin(ns, LogPlugin);    // uninstall (calls plugin.uninstall if defined)
```

## API

| Function | Description |
|----------|-------------|
| `definePlugin(options)` | Identity helper for TypeScript inference; zero runtime cost |
| `usePlugin(ns, plugin, options?)` | Install a plugin. Idempotent — safe to call multiple times |
| `installed(ns, plugin \| id)` | Check if a plugin is installed |
| `uninstallPlugin(ns, plugin \| id)` | Uninstall and call `plugin.uninstall(ns)` if defined |

### Plugin contract

```ts
interface NamespacePlugin<TOptions = void> {
  readonly id: string;
  install(ns: Namespace, options: TOptions): void;
  uninstall?(ns: Namespace): void;
}
```

### Events emitted on install / uninstall

- `plugin:installed` — `{ id, plugin }`
- `plugin:uninstalled` — `{ id, plugin }`

## License

Apache-2.0 © [lopatnov](https://github.com/lopatnov)
npm package: @lopatnov/namespace-pwa
Description: PWA utilities for @lopatnov/namespace. Service Worker registration, install prompt, network status, and update management.
Author: lopatnov
Latest version: 3.0.1

# @lopatnov/namespace-pwa

[![npm](https://img.shields.io/npm/v/@lopatnov/namespace-pwa)](https://www.npmjs.com/package/@lopatnov/namespace-pwa)
[![license](https://img.shields.io/npm/l/@lopatnov/namespace-pwa)](LICENSE)

PWA utilities for `@lopatnov/namespace`. Service Worker registration, install prompt, network status detection, and SW update management.

## Install

```sh
npm install @lopatnov/namespace-pwa
```

## Quick start

```ts
import {
  registerSW,
  onInstallPrompt,
  promptInstall,
  isOnline,
  onOffline,
  onOnline,
  onUpdateAvailable,
  activateUpdate,
} from '@lopatnov/namespace-pwa';

// Register Service Worker
registerSW('./sw.js');

// Detect network state
onOffline(() => document.getElementById('banner').hidden = false);
onOnline(() => document.getElementById('banner').hidden = true);

// Install prompt
onInstallPrompt(() => {
  document.getElementById('install-btn').hidden = false;
});
document.getElementById('install-btn').onclick = () => promptInstall();

// SW updates
onUpdateAvailable((reg) => {
  if (confirm('New version available. Reload?')) activateUpdate(reg);
});
```

## API

### Service Worker

| Function | Description |
|----------|-------------|
| `registerSW(url, options?)` | Register a Service Worker. Returns `Promise<ServiceWorkerRegistration \| undefined>` |

`RegisterSWOptions`: `onSuccess(reg)`, `onError(err)`.

### Install prompt

| Function | Description |
|----------|-------------|
| `onInstallPrompt(cb)` | Subscribe to `beforeinstallprompt`. Returns unsubscribe function |
| `promptInstall()` | Show the native install prompt. Returns `Promise<InstallOutcome>` |

`InstallOutcome`: `"accepted"` \| `"dismissed"` \| `"unavailable"`.

### Network status

| Function | Description |
|----------|-------------|
| `isOnline()` | Returns current `navigator.onLine` |
| `onOffline(cb)` | Subscribe to going offline. Returns unsubscribe function |
| `onOnline(cb)` | Subscribe to coming back online. Returns unsubscribe function |

### SW updates

| Function | Description |
|----------|-------------|
| `onUpdateAvailable(cb)` | Called when a new SW is waiting to activate. Returns unsubscribe function |
| `activateUpdate(reg)` | Send `SKIP_WAITING` message to the waiting SW |

## License

Apache-2.0 © [lopatnov](https://github.com/lopatnov)
npm package: @lopatnov/namespace-router
Description: SPA router with lazy loading for @lopatnov/namespace. History & hash modes, dynamic imports, route params.
Author: lopatnov
Latest version: 3.0.1

# @lopatnov/namespace-router

[![npm](https://img.shields.io/npm/v/@lopatnov/namespace-router)](https://www.npmjs.com/package/@lopatnov/namespace-router)
[![license](https://img.shields.io/npm/l/@lopatnov/namespace-router)](LICENSE)

SPA router for `@lopatnov/namespace`. Hash and history modes, route parameters, lazy loading, and programmatic navigation.

## Install

```sh
npm install @lopatnov/namespace @lopatnov/namespace-router
```

## Quick start

```ts
import { createNamespace } from '@lopatnov/namespace';
import { createRouter, route, lazyRoute, navigate, start } from '@lopatnov/namespace-router';

const ns = createNamespace();
const router = createRouter(ns, { mode: 'hash', root: '#app' });

route(router, '/', (container) => { container.innerHTML = '<h1>Home</h1>'; });
route(router, '/users/:id', (container, params) => {
  container.innerHTML = `<p>User: ${params.id}</p>`;
});

// Lazy-loaded page
route(router, '/settings', lazyRoute(() => import('./pages/settings')));

start(router);
```

## API

| Function | Description |
|----------|-------------|
| `createRouter(ns, options?)` | Create a router; registers it in the namespace |
| `start(router)` | Begin listening to navigation events |
| `stop(router)` | Stop listening |
| `route(router, pattern, handler)` | Register a route. Supports `:param` and `*` wildcard |
| `lazyRoute(loader)` | Wrap a dynamic `import()` as a route handler |
| `navigate(router, path)` | Navigate programmatically |
| `back()` | `history.back()` |
| `forward()` | `history.forward()` |
| `getCurrentPath(router)` | Current path string |

### Route handler signature

```ts
type RouteHandler = (
  container: Element,
  params: Record<string, string>,
  query: URLSearchParams
) => void | (() => void) | Promise<undefined | (() => void)>;
```

Return a cleanup function to run before the next navigation.

### Lazy routes

A lazy route module must export a `default` route handler:

```ts
// pages/settings.ts
export default function settings(container: Element) {
  container.innerHTML = '<h1>Settings</h1>';
}
```

### Router options

```ts
interface RouterOptions {
  mode?: 'history' | 'hash'; // default: 'hash'
  root?: string;             // CSS selector for the render container; default: '#app'
}
```

### Events (on namespace)

- `router:before` — fired before navigation
- `router:after` — fired after the new page renders

## License

Apache-2.0 © [lopatnov](https://github.com/lopatnov)
npm package: @lopatnov/namespace-storage
Description: Persistent storage for @lopatnov/namespace. Auto-sync namespace keys to localStorage, sessionStorage, or IndexedDB.
Author: lopatnov
Latest version: 3.0.1

# @lopatnov/namespace-storage

[![npm](https://img.shields.io/npm/v/@lopatnov/namespace-storage)](https://www.npmjs.com/package/@lopatnov/namespace-storage)
[![license](https://img.shields.io/npm/l/@lopatnov/namespace-storage)](LICENSE)

Persistent storage for `@lopatnov/namespace`. Auto-sync namespace keys to `localStorage`, `sessionStorage`, or `IndexedDB` with optional debounce.

## Install

```sh
npm install @lopatnov/namespace @lopatnov/namespace-storage
```

## Quick start

```ts
import { createNamespace, set } from '@lopatnov/namespace';
import { persist, restore } from '@lopatnov/namespace-storage';

const ns = createNamespace();

// Restore saved state on startup
await restore(ns, { keys: ['user', 'settings'], storage: localStorage });

// Auto-save on every change (debounced 300 ms)
const stop = persist(ns, {
  keys: ['user', 'settings'],
  storage: localStorage,
  debounce: 300,
});

set(ns, 'user', { name: 'Alice' }); // written to localStorage automatically

// Stop persisting when no longer needed
stop();
```

## IndexedDB

```ts
import { createIndexedDB, persist, restore } from '@lopatnov/namespace-storage';

const idb = createIndexedDB('myApp', 'ns-store');
await restore(ns, { keys: ['cache'], storage: idb });
persist(ns, { keys: ['cache'], storage: idb });
```

## API

| Function | Description |
|----------|-------------|
| `persist(ns, options)` | Subscribe to namespace changes and write keys to storage. Returns `stop()` |
| `restore(ns, options)` | Read keys from storage and load them into the namespace |
| `createIndexedDB(dbName, storeName?)` | Create an IndexedDB-backed `StorageAdapter` |

### PersistOptions

```ts
interface PersistOptions {
  keys: string[];           // dot-path keys to watch
  storage: StorageAdapter;  // localStorage / sessionStorage / custom
  debounce?: number;        // ms before writing (default: 0)
  prefix?: string;          // storage key prefix (default: 'ns:')
}
```

### StorageAdapter interface

Any object that satisfies:

```ts
interface StorageAdapter {
  getItem(key: string): string | null | Promise<string | null>;
  setItem(key: string, value: string): void | Promise<void>;
  removeItem(key: string): void | Promise<void>;
}
```

`localStorage` and `sessionStorage` satisfy this interface natively.

## License

Apache-2.0 © [lopatnov](https://github.com/lopatnov)