Skip to content

Package Format (.bpx) ​

BluePLM extensions are distributed as .bpx files — ZIP archives containing the extension manifest, code, and assets. This document covers the package structure and creation process.

Package Structure ​

my-extension-1.0.0.bpx  (ZIP archive)
├── extension.json          # Manifest (required)
├── README.md               # Documentation (recommended)
├── CHANGELOG.md            # Version history (optional)
├── LICENSE                 # Open source license (required)
├── icon.png                # Extension icon (recommended)
├── SIGNATURE               # Ed25519 signature (verified only)
├── client/                 # Client-side code
│   ├── index.js            # Main entry (required if main specified)
│   └── components/         # React components
│       ├── Panel.js
│       └── Settings.js
└── server/                 # Server-side code
    ├── index.js            # Server entry (optional)
    ├── sync.js             # Route handlers
    └── webhook.js

Required Files ​

extension.json ​

The manifest file. See Manifest Reference.

json
{
  "$schema": "https://blueplm.io/schemas/extension-v1.schema.json",
  "id": "mycompany.my-extension",
  "name": "My Extension",
  "version": "1.0.0",
  "publisher": "mycompany",
  "license": "MIT",
  ...
}

LICENSE ​

An open source license file. All BluePLM extensions must be open source.

Common licenses:

  • MIT — Permissive, widely used
  • Apache-2.0 — Permissive with patent grant
  • GPL-3.0 — Copyleft

README.md ​

Documentation for users. Displayed in the Extension Store.

markdown
# My Extension

Brief description of what this extension does.

## Features

- Feature 1
- Feature 2

## Setup

1. Install from Extension Store
2. Configure in Settings → Extensions → My Extension
3. ...

## Usage

How to use the extension.

## Requirements

- BluePLM 1.0.0 or later
- ...

icon.png ​

Extension icon displayed in the store and settings.

Requirements:

  • Format: PNG
  • Size: 128×128 pixels (recommended)
  • Aspect ratio: Square
  • Background: Transparent supported

CHANGELOG.md ​

Version history for users.

markdown
# Changelog

## [1.1.0] - 2024-03-15
### Added
- Batch sync feature
- Progress indicator

### Fixed
- Memory leak in file watcher

## [1.0.0] - 2024-03-01
### Added
- Initial release

Code Organization ​

Client Code ​

Client code runs in the Extension Host (isolated renderer process).

client/
├── index.ts              # Entry point (activate/deactivate)
├── components/           # React components
│   ├── Panel.tsx
│   └── Settings.tsx
├── views/                # View components
│   └── Sidebar.tsx
└── utils/                # Helper functions
    └── helpers.ts

Entry Point (index.ts):

typescript
import type { ExtensionContext, ExtensionClientAPI } from '@blueplm/extension-api'

export async function activate(
  context: ExtensionContext,
  api: ExtensionClientAPI
): Promise<void> {
  // Initialization
}

export function deactivate(): void {
  // Cleanup
}

Server Code ​

Server code runs in V8 isolates on the organization's API server.

server/
├── index.ts              # Server entry (optional)
├── sync.ts               # Sync handler
├── status.ts             # Status handler
├── webhook.ts            # Webhook handler
└── utils/                # Shared utilities
    └── helpers.ts

Handler Structure:

typescript
import type { ExtensionServerAPI } from '@blueplm/extension-api'

export default async function handler(
  api: ExtensionServerAPI
): Promise<ExtensionResponse> {
  // Handler logic
  return api.response.json({ success: true })
}

Building ​

Prerequisites ​

  • Node.js 18+
  • TypeScript
  • Bundler (esbuild, rollup, or webpack)

Package.json ​

json
{
  "name": "my-extension",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "build": "npm run build:client && npm run build:server",
    "build:client": "esbuild client/index.ts --bundle --outfile=dist/client/index.js --format=esm --platform=browser --external:react --external:react-dom",
    "build:server": "esbuild server/*.ts --bundle --outdir=dist/server --format=esm --platform=node",
    "package": "npm run build && node scripts/package.js",
    "clean": "rimraf dist *.bpx"
  },
  "devDependencies": {
    "esbuild": "^0.19.0",
    "typescript": "^5.0.0",
    "rimraf": "^5.0.0"
  }
}

Build Script (esbuild) ​

scripts/package.js:

javascript
const fs = require('fs')
const path = require('path')
const archiver = require('archiver')

const manifest = JSON.parse(fs.readFileSync('extension.json', 'utf8'))
const filename = `${manifest.id.split('.')[1]}-${manifest.version}.bpx`

const output = fs.createWriteStream(filename)
const archive = archiver('zip', { zlib: { level: 9 } })

archive.pipe(output)

// Add manifest
archive.file('extension.json', { name: 'extension.json' })

// Add documentation
if (fs.existsSync('README.md')) {
  archive.file('README.md', { name: 'README.md' })
}
if (fs.existsSync('CHANGELOG.md')) {
  archive.file('CHANGELOG.md', { name: 'CHANGELOG.md' })
}
archive.file('LICENSE', { name: 'LICENSE' })

// Add icon
if (fs.existsSync('icon.png')) {
  archive.file('icon.png', { name: 'icon.png' })
}

// Add built code
archive.directory('dist/client/', 'client/')
archive.directory('dist/server/', 'server/')

archive.finalize()

output.on('close', () => {
  console.log(`Created ${filename} (${archive.pointer()} bytes)`)
})

TypeScript Configuration ​

tsconfig.json:

json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "strict": true,
    "skipLibCheck": true,
    "declaration": true,
    "outDir": "dist",
    "rootDir": ".",
    "jsx": "react-jsx"
  },
  "include": ["client/**/*", "server/**/*"],
  "exclude": ["node_modules", "dist"]
}

Packaging Steps ​

1. Validate Manifest ​

bash
# Using CLI (if available)
blueplm-ext validate extension.json

# Or use JSON Schema validation
npx ajv validate -s schemas/extension-v1.schema.json -d extension.json

2. Build Code ​

bash
npm run build

3. Test Locally ​

Sideload the extension before packaging:

  1. Build the code
  2. Open BluePLM → Settings → Extensions
  3. Click "Sideload Extension"
  4. Select your extension.json file (from source directory)
  5. Test all functionality

4. Create Package ​

bash
npm run package

This creates my-extension-1.0.0.bpx.

5. Verify Package ​

bash
# List contents
unzip -l my-extension-1.0.0.bpx

# Expected output:
#   extension.json
#   README.md
#   LICENSE
#   icon.png
#   client/index.js
#   client/components/Panel.js
#   client/components/Settings.js
#   server/sync.js
#   server/status.js

Package Signing (Verified Extensions) ​

Verified extensions are signed with Ed25519 signatures by Blue Robotics.

Signature File ​

SIGNATURE

Contains:

-----BEGIN BLUEPLM SIGNATURE-----
KeyId: bluerobotics-2024
Hash: SHA256:a1b2c3d4e5f6...
Signature: base64-encoded-ed25519-signature
-----END BLUEPLM SIGNATURE-----

Verification Process ​

  1. Calculate SHA-256 hash of package contents (excluding SIGNATURE)
  2. Verify hash matches expected
  3. Verify Ed25519 signature with Blue Robotics public key
  4. Check key ID against revocation list

Getting Verified ​

To get your extension verified:

  1. Submit to Extension Store
  2. Request verification review
  3. Blue Robotics reviews code
  4. If approved, extension is signed and marked verified

Size Limits ​

LimitValueDescription
Package size10 MBMaximum .bpx file size
Client bundle2 MBMaximum client/index.js
Server handlers1 MB eachPer handler file
Total handlers5 MBCombined server code
Assets5 MBIcons, images, etc.

Reducing Size ​

  1. Tree shaking — Only import what you use
  2. Minification — Enable in bundler
  3. External deps — Don't bundle React (provided by host)
  4. Lazy loading — Split large components

esbuild with minification:

bash
esbuild client/index.ts \
  --bundle \
  --minify \
  --external:react \
  --external:react-dom \
  --outfile=dist/client/index.js

Best Practices ​

1. Keep Package Small ​

json
// .npmignore or add to .gitignore
node_modules/
src/
tests/
*.ts  // Only ship compiled .js

2. Include Source Maps for Debugging ​

bash
esbuild ... --sourcemap=external

3. Version Your Package ​

Use semantic versioning:

  • 1.0.0 → 1.0.1 (patch: bug fix)
  • 1.0.0 → 1.1.0 (minor: new feature)
  • 1.0.0 → 2.0.0 (major: breaking change)

4. Test Before Publishing ​

  1. Sideload and test all features
  2. Test on different platforms (if cross-platform)
  3. Test with different BluePLM versions
  4. Check performance (startup time < 200ms)

5. Document Everything ​

  • README with setup instructions
  • CHANGELOG with version history
  • Inline code comments for complex logic

Example Build Setup ​

Complete example with esbuild and React:

Directory Structure:

my-extension/
├── client/
│   ├── index.tsx
│   └── components/
│       └── Panel.tsx
├── server/
│   └── sync.ts
├── extension.json
├── README.md
├── LICENSE
├── icon.png
├── package.json
├── tsconfig.json
└── scripts/
    └── package.js

package.json:

json
{
  "name": "my-extension",
  "version": "1.0.0",
  "scripts": {
    "build": "npm run build:client && npm run build:server",
    "build:client": "esbuild client/index.tsx --bundle --outfile=dist/client/index.js --format=esm --platform=browser --external:react --external:react-dom --minify",
    "build:server": "esbuild server/*.ts --bundle --outdir=dist/server --format=esm --minify",
    "package": "npm run build && node scripts/package.js",
    "dev": "npm run build -- --watch",
    "lint": "eslint client server",
    "typecheck": "tsc --noEmit"
  },
  "devDependencies": {
    "@types/react": "^18.0.0",
    "archiver": "^6.0.0",
    "esbuild": "^0.19.0",
    "eslint": "^8.0.0",
    "typescript": "^5.0.0"
  }
}

Build and package:

bash
npm install
npm run package
# Output: my-extension-1.0.0.bpx

← Permissions Reference | Publishing Guide →