Skip to content

Latest commit

 

History

History
189 lines (134 loc) · 11.2 KB

File metadata and controls

189 lines (134 loc) · 11.2 KB

Release Notes

Format: Keep a Changelog.


[2.0.0] — 2026-04-17

BREAKING CHANGES

instanceId is now REQUIRED everywhere (no auto-generation fallback).

  • initializeStructuraWebview({ instanceId: 'unique-id', ... })instanceId is mandatory
  • createSchemaBuilder({ instanceId: 'unique-id', ... })instanceId is mandatory (in props object, not optional)
  • createCanvasPage(instanceId, config?, mcpUrl?)instanceId is now a required first positional parameter
  • create_redux_store(config, instanceId)instanceId is required (throws if missing)
  • getInstanceReduxStore(instanceId, config) — Use this instead of deprecated getGlobalReduxStore (now throws)
  • initToolboxConfigManager(instanceId)instanceId is required (throws if missing)
  • initExportRegistry(instanceId)instanceId is required (throws if missing)
  • createPersistence(store, instanceId)instanceId is required (throws if missing)

Type system enforces instanceId: Attempting to omit instanceId now results in TypeScript compilation errors.

Rationale: The v1.2.x fallback to auto-generation created accidental safety issues where developers could deploy instances without proper isolation. Requiring explicit instanceId ensures all multi-instance scenarios are explicit and verifiable.

Fixed

  • [CRITICAL] LocalStorage State Collision in Shared Origins — When multiple Structura instances run in the same browser origin (e.g., multiple VS Code webview panels in a single extension), they were colliding in localStorage due to hardcoded keys. v2.0.0 enforces this fix as mandatory:
    • All storage keys are now strictly namespaced: ${instanceId}:vbe2:*, ${instanceId}:vbs-*, ${instanceId}:atomos_*
    • getGlobalReduxStore() removed entirely (deprecated function now throws)
    • All persistence functions throw if instanceId is missing or empty
    • No fallback behavior — every instance must have an explicit, non-empty instanceId
    • See Bug Report: LocalStorage State Collision below.

Added

  • schema-create-auto intent action — The schema tab bar now dispatches a lightweight schema-create-auto action instead of generating an ID and firing schema-created directly. In standalone mode the Redux reducer handles it. When an MCP server URL is configured the canvas page intercepts the action via a dispatch hook, forwards the request to the MCP server, and the SSE schema-created event closes the loop. This eliminates duplicate ID generation between the UI and the server. (create-schema-tabs.ts, create-redux-store.ts, create-canvas-page.ts)

  • addDispatchHook on ReduxStore — A lightweight middleware hook that lets consumers intercept Redux actions before they reach the reducer. Returning null from a hook swallows the action; returning the action (or a replacement) forwards it. Used internally to route schema-create-auto to MCP. (redux-state.types.ts, create-redux-store.ts)

  • Canvas adapter registrygetCanvasAdapterFor(schemaId) replaces the global singleton canvas adapter. Each schema ID maps to its own CanvasAdapter instance, preventing cross-schema state pollution when multiple panels are open simultaneously. The old getCanvasAdapter() is kept as deprecated. (canvas-adapter.ts)

  • Explicit schema_id in MCP entity/link toolscreate-entity, update-entity, delete-entity, create-link now require a schema_id parameter and return 400 when it is absent or 404 when the schema is not found. get-entity falls back to the active schema for backwards compatibility. The change SSE event payload now includes schema_id. (mcp-server.ts)

  • Template injection tokenswebview/template.html now ships with ${mcpUrl} and ${schemaId} tokens instead of a commented-out placeholder. Consuming extensions call .replaceAll('${mcpUrl}', url) and .replaceAll('${schemaId}', id) inside buildHtml. Empty strings are treated as undefined by the webview init, preserving standalone-mode behaviour. (template.html, webview/index.ts)

  • Single-file IIFE buildpnpm run build:webview-iife (env BUILD_TARGET=webview-iife) produces dist/webview/index.iife.js — a self-contained bundle with no dynamic imports and the global name StructuraWebview. Recommended for VSIX-packaged extensions where ES module dynamic imports are unreliable. (vite.config.ts, package.json)

  • __APP_VERSION__ build constantvite.config.ts injects __APP_VERSION__ from package.json at build time. Used by the new About modal. (vite.config.ts)

  • About modal — New createAboutModal() utility renders the package name, version (from __APP_VERSION__), MIT licence notice, and links to documentation and the repository. Accessible via the toolbar burger menu ("About" button with info icon). (create-about-modal.ts, create-canvas-toolbar.ts)

  • --atp-modal-padding CSS custom property — The atp-modal shadow DOM .dialog now accepts --atp-modal-padding (default 0 4px) so consuming applications can adjust internal padding without replacing the entire style. (atp-modal.style.ts)

Changed

  • create-mcp-sync.tsapplyChange now reads schema_id from the SSE payload and applies the update to that specific schema rather than always targeting the active schema. (create-mcp-sync.ts)

  • Entity settings modal button style — Removed hardcoded height: 24px; min-height: 24px override on addPropBtn; size is now determined by padding alone, matching other toolbar buttons. (create-entity-settings-modal.ts)

Documentation

  • EXTENSION_GUIDE.md — Updated buildHtml sample to use token replacements; added "Instance Isolation" and "Single-bundle IIFE" sections. (EXTENSION_GUIDE.md)

  • structura-mcp/README.md — Added "Targeting a specific schema" section with schema_id usage examples and SSE routing pattern. (README.md)

  • docs/IMPLEMENTATION_STATUS.md — Added architectural change note block documenting the v1.2.0 isolation improvements. (IMPLEMENTATION_STATUS.md)


[1.1.5] — 2026-04-14

  • Initial production release of @atomos-web/structura, @atomos-web/structura-core, @atomos-web/structura-mcp.
  • 20+ MCP tools for AI-driven schema creation.
  • Viewport control, session lifecycle, availability guards, menu configuration.
  • Export/import (JSON workspace, SVG, PNG).
  • Redux undo/redo with history skip for volatile actions.
  • @atomos-web/prime web component library: atp-modal, atp-dropdown, design system tokens.

Multi-Instance Isolation Bug Details

Root Cause Analysis

Structura relied on hardcoded localStorage keys for persistence. When multiple instances run in the same browser origin, all instances share the exact same localStorage environment, causing a singleton-like bug where canvases overwrite each other's state.

Affected Storage Keys:

File Keys Status
create-redux-store.ts vbe2:redux-state Fixed v1.2.2
create-persistence.ts vbs-canvas-state Fixed v1.2.2
toolbox-config-manager.ts atomos_toolbox_config, atomos_custom_shapes, atomos_general_settings, atomos_appearance_settings Fixed v1.2.2
create-export-registry.ts vbe2:custom-export-plugins Fixed v1.2.2
schema-builder.ts close() method clears all vbe2:* keys Fixed v1.2.2

Scenario (Before Fix)

// Webview Panel 1 opens "Schema A"
const app1 = await initializeStructuraWebview({
  containerId: 'app1',
  instanceId: 'panel-1'  // REQUIRED in v1.2.2+
});

// Webview Panel 2 opens "Schema B" — same origin
const app2 = await initializeStructuraWebview({
  containerId: 'app2',
  instanceId: 'panel-2'  // REQUIRED in v1.2.2+
});

// BEFORE FIX: Both stored state in same keys
// localStorage['vbe2:redux-state'] → continuously overwritten
// localStorage['vbs-canvas-state'] → continuously overwritten
// localStorage['atomos_toolbox_config'] → shared settings

// AFTER FIX: Fully namespaced
// localStorage['panel-1:vbe2:redux-state']
// localStorage['panel-2:vbe2:redux-state']
// localStorage['panel-1:vbs-canvas-state']
// localStorage['panel-2:vbs-canvas-state']
// localStorage['panel-1:atomos_toolbox_config']
// localStorage['panel-2:atomos_toolbox_config']

Implementation Pattern

All persistence layers now follow this pattern:

// Before
const STORAGE_KEY = 'some-key';
localStorage.setItem(STORAGE_KEY, value);

// After
export const someFunction = function(config: SomeConfig, instanceId?: string) {
  const STORAGE_KEY = `${instanceId}:some-key`;
  localStorage.setItem(STORAGE_KEY, value);
}

Migration Guide for v2.0.0

If you're using the headless API:

// v1.2.x (optional instanceId)
const builder = createSchemaBuilder();

// v2.0.0+ (REQUIRED instanceId)
const builder = createSchemaBuilder({
  instanceId: 'my-app-instance-1'  // MANDATORY — no auto-generation
});

If you're embedding in VS Code extensions:

// v1.2.x (optional instanceId, auto-generates if missing)
const app = await initializeStructuraWebview({
  containerId: 'app',
  // instanceId omitted — falls back to UUID
  mcpServerUrl: '...'
});

// v2.0.0+ (REQUIRED instanceId)
const app = await initializeStructuraWebview({
  containerId: 'app',
  instanceId: 'schema-editor-1',  // MANDATORY — must be unique per panel
  mcpServerUrl: '...'
});

Throwing if instanceId is missing:

// v2.0.0 throws for all of these:
initializeStructuraWebview({});  // throws: "instanceId is REQUIRED"
createSchemaBuilder({});  // throws: "instanceId is required"
create_redux_store(config, undefined);  // throws: "instanceId must be non-empty"
getInstanceReduxStore('');  // throws: "requires a non-empty instanceId"

Key Points:

  • Type system now enforces instanceId: string (non-optional) at compile time
  • Runtime validation throws with clear error messages if instanceId is empty or missing
  • No fallback behavior — every instance is explicit
  • Deterministic and testable: same instanceId always produces same localStorage namespace