npm package: @opengeoweb/plugin-interface
Description: A TypeScript library for plugin management, built with React, TypeScript, and Vite.
Latest version: 1.6.2

# Plugin Interface

A TypeScript library for plugin management, built with React, TypeScript, and Vite.

## Automatic Releases

Releases are automatically created when code is merged to `main` using [semantic-release](https://semantic-release.gitbook.io/). Use conventional commit messages to trigger version bumps.

### Commit Types

| Commit Type | Version Bump | Example                       |
| ----------- | ------------ | ----------------------------- |
| `fix:`      | Patch        | `fix: resolve memory leak`    |
| `feat:`     | Minor        | `feat: add plugin validation` |
| `feat!:`    | Major        | `feat!: refactor plugin API`  |

**Other types** (`docs:`, `test:`, `chore:`, etc.) do not trigger releases.

### Version Examples

| Current | Commit Type | New Version |
| ------- | ----------- | ----------- |
| `0.0.1` | `fix:`      | `0.0.2`     |
| `0.0.1` | `feat:`     | `0.1.0`     |
| `0.1.0` | `feat!:`    | `1.0.0`     |

### Process

1. Commit with conventional message (`fix:`, `feat:`, etc.)
2. Merge to `main` branch
3. CI/CD runs tests → builds → publishes (if tests pass)

**Note:** Only commits on `main` trigger releases. Multiple commits use the highest version bump type.

## Plugin Generator

Generate a new plugin project from a template using an interactive prompt system or command-line arguments.

### Quick Start

**Using npx (recommended - no installation needed):**

```bash
npx @opengeoweb/plugin-interface
```

**Or install globally:**

```bash
npm install -g @opengeoweb/plugin-interface
create-plugin
```

### Interactive Mode (Recommended)

When you run `create-plugin` without arguments, you'll be prompted for all options:

```bash
npx @opengeoweb/plugin-interface
```

The interactive prompts will ask for:

- **Plugin name**: Name of your plugin (e.g., `MyAwesomePlugin`)
- **Scopes**: Multi-select interface to choose from available scopes:
  - `time` - Time state management (setTime events)
  - `animation` - Animation time span management (start/end times)
- **Initial version**: Defaults to `1.0.0`
- **Output directory**: Defaults to current directory
- **Package name**: Optional, defaults to `@your-org/<kebab-name>`

### Non-Interactive Mode

You can also provide all options via command-line arguments:

```bash
npx @opengeoweb/plugin-interface MyPlugin \
  --scopes time,animation \
  --version 1.0.0 \
  --output-dir ./ \
  --package-name @myorg/my-plugin
```

### Command-Line Options

- `<plugin-name>` (required in non-interactive mode): Name of your plugin
- `--scopes <scope1,scope2>`: Comma-separated list of scopes (default: `time`)
- `--version <version>`: Initial version (default: `1.0.0`)
- `--output-dir <dir>`: Output directory (default: current directory)
- `--package-name <name>`: Package name (default: `@your-org/<kebab-name>`)

### What Gets Generated

The generator creates a complete plugin project with:

- ✅ React component structure with scope integration
- ✅ TypeScript configuration
- ✅ Vite build configs
- ✅ Storybook setup for development and testing
- ✅ Default scope functionality (state subscriptions, event handlers, controls)
- ✅ Interactive demo story with scope controls
- ✅ Proper plugin interface integration
- ✅ Example event handlers and initialization code

### Example Usage

**Interactive mode:**

```bash
npx @opengeoweb/plugin-interface
# Follow the prompts
```

**Non-interactive mode:**

```bash
npx @opengeoweb/plugin-interface WeatherWidget \
  --scopes time,animation \
  --version 0.1.0
```

**After generation:**

```bash
cd weather-widget
npm install
npm run storybook  # Start development server
npm run build:vite # Build for production
```

### Available Scopes

- **`time`**: Manages time state with `setTime` events
- **`animation`**: Manages animation time spans with `setAnimationStartTime`, `setAnimationEndTime`, and `setAnimationTimeSpan` events

The generated plugin includes example code for all selected scopes, including state subscriptions, event handlers, and UI controls for testing.

## Developing the Generator

If you're making changes to the generator templates or code, you need to rebuild the generator before testing:

### Workflow

1. **Make changes** to templates in `templates/plugin-template/` or generator code in `src/generator/`

2. **Build the generator** to compile your changes:

   ```bash
   npm run build
   ```

3. **Run the generator** locally (relative path works):

   ```bash
   node bin/create-plugin.js
   ```

4. **Test the generated plugin**:
   ```bash
   cd [new-plugin-folder]
   npm install
   npm run storybook  # Start Storybook to test your plugin
   ```

### Quick Reference

```bash
# After making template changes
npm run build

# Generate a new plugin
node bin/create-plugin.js

# Test the generated plugin
cd [new-plugin-folder]
npm install
npm run storybook
```

## Development

```bash
npm install          # Install dependencies
npm run dev          # Development server
npm test             # Run tests
npm run build    # Build library
npm run lint         # Lint code
```

## Local Development with OpenGeoWeb

To test changes to the `plugin-interface` library directly inside OpenGeoWeb, you can use `npm link` for local development.

### Why use `npm link`?

When developing plugins or working on the `plugin-interface`, you often need to:

- Test changes in a real OpenGeoWeb environment
- Iterate quickly on plugin behavior and APIs
- Avoid publishing a new package version for every small change

`npm link` allows you to use your local version of `plugin-interface` inside OpenGeoWeb, so you can immediately see the effect of your changes while developing plugins.

This makes it much faster to develop, debug, and validate plugin integrations.

### Steps

1. Make changes in the `plugin-interface` repository

2. Build the library:

   ```bash
   npm run build
   ```

3. Link the package globally:

   ```bash
   npm link
   ```

4. Open the OpenGeoWeb project and link the package:

   ```bash
   npm link @opengeoweb/plugin-interface
   ```

5. Start OpenGeoWeb:

   ```bash
   nx serve
   ```

6. You should now see your local changes reflected in OpenGeoWeb.

### Updating Changes

7. After making new changes in `plugin-interface`, rebuild:
   ```bash
   npm run build
   ```

No need to relink, OpenGeoWeb will pick up the updated build automatically.

## Storybook

Storybook provides an interactive development environment for exploring and testing plugin integrations in isolation.

### Running Storybook

```bash
npm run dev          # Start Storybook dev server (usually http://localhost:6006)
```

### Available Stories

Located in the `./dev` folder:

- **PluginIntegration**: Shows real-time interaction with plugin state (time, animation, lifecycle controls)

### Benefits

- **Isolated Development**: Test components without running the full application
- **Live Documentation**: Interactive examples with editable props via Controls panel
- **Visual Testing**: See component states and variations side-by-side
- **Accessibility**: Built-in a11y addon helps catch accessibility issues early
- **Rapid Prototyping**: Quickly iterate on plugin behavior and UI integration

Stories include comprehensive documentation explaining the plugin architecture, custom hooks, and usage patterns.

## Time Scope API

```ts
import {
  PluginManager,
  setTime,
  toIso8601Utc,
  assertIso8601Utc,
  type Iso8601Utc,
} from 'plugin-interface';

const manager = new PluginManager();
const instance =
  /* previously registered plugin instance with 'time' scope */ null as any;

// Basic: pass a valid UTC timestamp literal (compile-time shape checked)
setTime(instance, '2024-10-10T10:10:10Z');

// From Date: convert explicitly
const isoFromDate = toIso8601Utc(new Date());
setTime(instance, isoFromDate);

// Dynamic strings: setTime performs runtime validation and throws on invalid input
const raw = await fetchSomeTimestamp();
setTime(instance, raw);

// Optional pre-validation for clearer local error handling
try {
  const safe: Iso8601Utc = assertIso8601Utc(raw);
  setTime(instance, safe);
} catch (err) {
  console.error('Invalid time format', err);
}

// Explicit conversion example
const iso: Iso8601Utc = toIso8601Utc(new Date('2025-01-01T00:00:00.999Z'));
setTime(instance, iso);
```

### Time Format & Validation

- Format: UTC ISO 8601 at seconds precision (`YYYY-MM-DDTHH:MM:SSZ`).
- Compile-time: template literal type `Iso8601Utc` validates only literal shapes.
- Runtime: `setTime` and helpers validate strings; errors include actionable hints (e.g., milliseconds present).

Helpers

- `setTime(instance, iso)`: Dispatches the `time` scope `setTime` event if `iso` is valid.
- `toIso8601Utc(date)`: Returns a compliant UTC string; removes milliseconds.
- `assertIso8601Utc(str)`: Throws if `str` is not compliant; narrows type to `Iso8601Utc` when valid.

If you need offsets or milliseconds support in the future, extend the validation and helpers while preserving the current API surface.