@nimbalyst/extension-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +151 -0
  3. package/dist/MaterialSymbol.d.ts +2 -0
  4. package/dist/MaterialSymbol.d.ts.map +1 -0
  5. package/dist/MaterialSymbol.js +1 -0
  6. package/dist/clipboard.d.ts +14 -0
  7. package/dist/clipboard.d.ts.map +1 -0
  8. package/dist/clipboard.js +27 -0
  9. package/dist/createReadOnlyHost.d.ts +45 -0
  10. package/dist/createReadOnlyHost.d.ts.map +1 -0
  11. package/dist/createReadOnlyHost.js +81 -0
  12. package/dist/documentPath.d.ts +6 -0
  13. package/dist/documentPath.d.ts.map +1 -0
  14. package/dist/documentPath.js +4 -0
  15. package/dist/externals.d.ts +23 -0
  16. package/dist/externals.d.ts.map +1 -0
  17. package/dist/externals.js +42 -0
  18. package/dist/externalsPlugin.d.ts +39 -0
  19. package/dist/externalsPlugin.d.ts.map +1 -0
  20. package/dist/externalsPlugin.js +251 -0
  21. package/dist/index.d.ts +33 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +39 -0
  24. package/dist/tailwind.d.ts +112 -0
  25. package/dist/tailwind.d.ts.map +1 -0
  26. package/dist/tailwind.js +129 -0
  27. package/dist/testing.d.ts +122 -0
  28. package/dist/testing.d.ts.map +1 -0
  29. package/dist/testing.js +199 -0
  30. package/dist/types/editor.d.ts +385 -0
  31. package/dist/types/editor.d.ts.map +1 -0
  32. package/dist/types/editor.js +15 -0
  33. package/dist/types/editors.d.ts +56 -0
  34. package/dist/types/editors.d.ts.map +1 -0
  35. package/dist/types/editors.js +19 -0
  36. package/dist/types/extension.d.ts +744 -0
  37. package/dist/types/extension.d.ts.map +1 -0
  38. package/dist/types/extension.js +4 -0
  39. package/dist/types/index.d.ts +9 -0
  40. package/dist/types/index.d.ts.map +1 -0
  41. package/dist/types/index.js +8 -0
  42. package/dist/types/panel.d.ts +575 -0
  43. package/dist/types/panel.d.ts.map +1 -0
  44. package/dist/types/panel.js +14 -0
  45. package/dist/types/theme.d.ts +148 -0
  46. package/dist/types/theme.d.ts.map +1 -0
  47. package/dist/types/theme.js +7 -0
  48. package/dist/useEditorLifecycle.d.ts +166 -0
  49. package/dist/useEditorLifecycle.d.ts.map +1 -0
  50. package/dist/useEditorLifecycle.js +327 -0
  51. package/dist/validate.d.ts +31 -0
  52. package/dist/validate.d.ts.map +1 -0
  53. package/dist/validate.js +128 -0
  54. package/dist/vite.d.ts +92 -0
  55. package/dist/vite.d.ts.map +1 -0
  56. package/dist/vite.js +179 -0
  57. package/package.json +91 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,39 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@nimbalyst/extension-sdk` are documented here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this package follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ The SDK is versioned independently of the Nimbalyst app. Each release declares its minimum compatible app version under the `nimbalyst.minAppVersion` field of `package.json`.
8
+
9
+ | SDK version | Minimum Nimbalyst app version |
10
+ | --- | --- |
11
+ | 0.1.0 | 0.58.5 |
12
+
13
+ ## [Unreleased]
14
+
15
+ ## [0.1.0] - Initial release
16
+
17
+ Initial public release of the Nimbalyst extension SDK.
18
+
19
+ ### Added
20
+
21
+ - `EditorHost` and `EditorHostProps` contract for custom editor extensions
22
+ - `useEditorLifecycle` hook for editor load/save/dirty/theme handling
23
+ - `ExtensionAITool`, `AIToolContext`, `ExtensionToolResult` types for AI tool extensions
24
+ - `ExtensionContext`, `ExtensionManifest`, `ExtensionContributions` for the extension manifest schema
25
+ - `PanelContribution`, `PanelExport`, `PanelHost` for non-file-based panels
26
+ - `SettingsPanelContribution`, `SettingsPanelProps` for extension settings panels
27
+ - `ThemeContribution` and `ThemeColors` for custom theme extensions
28
+ - `createExtensionConfig` Vite helper at `@nimbalyst/extension-sdk/vite`
29
+ - `createManifestValidationPlugin` Vite plugin to validate build output against `manifest.json`
30
+ - `mergeExtensionConfig` for extending the base Vite config
31
+ - Tailwind preset at `@nimbalyst/extension-sdk/tailwind`
32
+ - Testing utilities at `@nimbalyst/extension-sdk/testing`
33
+ - `ROLLUP_EXTERNALS`, `REQUIRED_EXTERNALS`, `EXTERNAL_PATTERNS` constants for the externals system
34
+ - `MaterialSymbol` re-export, `createReadOnlyHost`, `clipboard`, and document-path utilities
35
+
36
+ ### Notes
37
+
38
+ - Several legacy aliases (`CustomEditorProps`, `ToolContext`, `ToolResult`, `AIToolDefinition`, `FileIconContribution`, `LexicalNodeContribution`) are exported for migration but marked `@deprecated`. They will be removed in a future major version.
39
+ - Requires Vite 7+ and React 18 or 19 (declared as optional peer dependencies).
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # `@nimbalyst/extension-sdk`
2
+
3
+ Build Nimbalyst extensions with a stable TypeScript contract and Vite helpers.
4
+
5
+ This package provides:
6
+
7
+ - Shared extension types such as `ExtensionContext`, `EditorHostProps`, and `ExtensionAITool`
8
+ - `createExtensionConfig()` for the required extension build setup
9
+ - `validateExtensionBundle()` for bundle and manifest validation
10
+ - Tailwind helpers for extension styling
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ npm install --save-dev @nimbalyst/extension-sdk typescript vite
16
+ ```
17
+
18
+ If your extension renders React UI, also install:
19
+
20
+ ```bash
21
+ npm install react react-dom
22
+ npm install --save-dev @vitejs/plugin-react
23
+ ```
24
+
25
+ ## Vite Setup
26
+
27
+ ```ts
28
+ import react from '@vitejs/plugin-react';
29
+ import { createExtensionConfig } from '@nimbalyst/extension-sdk/vite';
30
+
31
+ export default createExtensionConfig({
32
+ entry: './src/index.tsx',
33
+ plugins: [react()],
34
+ });
35
+ ```
36
+
37
+ ## Custom Editor Example
38
+
39
+ Use the `useEditorLifecycle` hook to handle all editor lifecycle concerns (loading, saving, echo detection, file watching, diff mode, theme):
40
+
41
+ ```tsx
42
+ import { useRef } from 'react';
43
+ import { useEditorLifecycle, type EditorHostProps } from '@nimbalyst/extension-sdk';
44
+
45
+ export function ExampleEditor({ host }: EditorHostProps) {
46
+ const dataRef = useRef<MyData>(defaultData);
47
+
48
+ const { isLoading, markDirty, theme } = useEditorLifecycle(host, {
49
+ applyContent: (data: MyData) => { dataRef.current = data; },
50
+ getCurrentContent: () => dataRef.current,
51
+ parse: (raw) => JSON.parse(raw),
52
+ serialize: (data) => JSON.stringify(data),
53
+ });
54
+
55
+ if (isLoading) return <div>Loading...</div>;
56
+ return <MyEditorUI data={dataRef.current} onChange={markDirty} />;
57
+ }
58
+
59
+ export const components = {
60
+ ExampleEditor,
61
+ };
62
+ ```
63
+
64
+ See the [custom editors guide](packages/extension-sdk-docs/custom-editors.md) for architecture patterns and advanced options.
65
+
66
+ ## Shared Editor Components
67
+
68
+ Extensions can use the host's built-in `MonacoEditor` (code) and `MarkdownEditor` (rich text) instead of bundling their own. These are provided at runtime via the externals system with zero bundle size impact. The `MarkdownEditor` is pre-configured with toolbar, image handling, and other Nimbalyst platform features.
69
+
70
+ ```tsx
71
+ import { MonacoEditor } from '@nimbalyst/runtime';
72
+ import type { EditorHostProps } from '@nimbalyst/extension-sdk';
73
+
74
+ // Use as a full file editor
75
+ export const MyCodeEditor = ({ host }: EditorHostProps) => {
76
+ return <MonacoEditor host={host} fileName={host.fileName} />;
77
+ };
78
+ ```
79
+
80
+ For embedded read-only panels, use `createReadOnlyHost`:
81
+
82
+ ```tsx
83
+ import { MonacoEditor } from '@nimbalyst/runtime';
84
+ import { createReadOnlyHost } from '@nimbalyst/extension-sdk';
85
+
86
+ const previewHost = createReadOnlyHost(code, {
87
+ fileName: 'preview.tsx',
88
+ theme: host.theme,
89
+ });
90
+
91
+ <MonacoEditor host={previewHost} fileName="preview.tsx" />
92
+ ```
93
+
94
+ Type imports for props: `MonacoEditorProps`, `MonacoEditorConfig`, `MarkdownEditorProps`, `MarkdownEditorConfig` from `@nimbalyst/extension-sdk`.
95
+
96
+ ## AI Tool Example
97
+
98
+ ```ts
99
+ import type {
100
+ ExtensionAITool,
101
+ ExtensionToolResult,
102
+ } from '@nimbalyst/extension-sdk';
103
+
104
+ export const aiTools: ExtensionAITool[] = [
105
+ {
106
+ name: 'example.describe_file',
107
+ description: 'Describe the active file',
108
+ scope: 'editor',
109
+ inputSchema: {
110
+ type: 'object',
111
+ properties: {},
112
+ },
113
+ handler: async (_args, context): Promise<ExtensionToolResult> => {
114
+ const filePath = context.activeFilePath;
115
+ if (!filePath) {
116
+ return { success: false, error: 'No active file.' };
117
+ }
118
+
119
+ const content = await context.extensionContext.services.filesystem.readFile(filePath);
120
+ return {
121
+ success: true,
122
+ data: {
123
+ filePath,
124
+ length: content.length,
125
+ },
126
+ };
127
+ },
128
+ },
129
+ ];
130
+ ```
131
+
132
+ ## Manifest Notes
133
+
134
+ - `apiVersion` is currently optional but recommended.
135
+ - `contributions.aiTools` must be an array of tool-name strings, not full tool objects.
136
+ - `contributions.fileIcons` must be an object map such as `{ "*.csv": "table" }`.
137
+
138
+ ## Docs
139
+
140
+ - Getting started: `packages/extension-sdk-docs/getting-started.md`
141
+ - Manifest reference: `packages/extension-sdk-docs/manifest-reference.md`
142
+ - API reference: `packages/extension-sdk-docs/api-reference.md`
143
+ - Examples: `packages/extension-sdk-docs/examples/`
144
+
145
+ ## Release Checks
146
+
147
+ From the monorepo root:
148
+
149
+ ```bash
150
+ npm run extension-sdk:check-public
151
+ ```
@@ -0,0 +1,2 @@
1
+ export { MaterialSymbol } from '@nimbalyst/runtime';
2
+ //# sourceMappingURL=MaterialSymbol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MaterialSymbol.d.ts","sourceRoot":"","sources":["../src/MaterialSymbol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1 @@
1
+ export { MaterialSymbol } from '@nimbalyst/runtime';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Clipboard utilities for Nimbalyst extensions.
3
+ *
4
+ * Routes through Electron's native clipboard via IPC when available,
5
+ * falling back to the standard navigator.clipboard Web API for
6
+ * non-Electron contexts.
7
+ *
8
+ * navigator.clipboard.writeText() can silently fail in Electron -- the
9
+ * promise resolves but nothing is written to the system clipboard.
10
+ * These helpers avoid that issue by using the IPC bridge.
11
+ */
12
+ export declare function copyToClipboard(text: string): Promise<void>;
13
+ export declare function readClipboard(): Promise<string>;
14
+ //# sourceMappingURL=clipboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../src/clipboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOjE;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAOrD"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Clipboard utilities for Nimbalyst extensions.
3
+ *
4
+ * Routes through Electron's native clipboard via IPC when available,
5
+ * falling back to the standard navigator.clipboard Web API for
6
+ * non-Electron contexts.
7
+ *
8
+ * navigator.clipboard.writeText() can silently fail in Electron -- the
9
+ * promise resolves but nothing is written to the system clipboard.
10
+ * These helpers avoid that issue by using the IPC bridge.
11
+ */
12
+ export async function copyToClipboard(text) {
13
+ const electronAPI = window.electronAPI;
14
+ if (electronAPI?.copyToClipboard) {
15
+ await electronAPI.copyToClipboard(text);
16
+ return;
17
+ }
18
+ await navigator.clipboard.writeText(text);
19
+ }
20
+ export async function readClipboard() {
21
+ const electronAPI = window.electronAPI;
22
+ if (electronAPI?.readClipboard) {
23
+ const result = await electronAPI.readClipboard();
24
+ return result.text ?? '';
25
+ }
26
+ return navigator.clipboard.readText();
27
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * ReadOnlyEditorHost factory for web share viewers.
3
+ *
4
+ * Creates a minimal EditorHost implementation that serves pre-loaded content
5
+ * in read-only mode. Used by the Cloudflare share viewer to render extension
6
+ * editors on the web without any Electron/IPC dependencies.
7
+ *
8
+ * Also useful for testing extensions in isolation.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const host = createReadOnlyHost(decryptedContent, {
13
+ * theme: 'dark',
14
+ * fileName: 'diagram.mindmap',
15
+ * });
16
+ *
17
+ * // Mount extension component
18
+ * root.render(<MindmapEditor host={host} />);
19
+ *
20
+ * // Toggle theme from viewer chrome
21
+ * host.setTheme('light');
22
+ * ```
23
+ */
24
+ import type { EditorHost } from './types/editor.js';
25
+ export interface ReadOnlyHostOptions {
26
+ /** Current theme name (e.g., 'dark', 'light') */
27
+ theme: string;
28
+ /** File name for display */
29
+ fileName: string;
30
+ /** Optional synthetic file path (defaults to `/shared/{fileName}`) */
31
+ filePath?: string;
32
+ }
33
+ export interface ReadOnlyHost extends EditorHost {
34
+ /** Update the theme (call from viewer chrome's theme toggle) */
35
+ setTheme(theme: string): void;
36
+ }
37
+ /**
38
+ * Create a read-only EditorHost that serves pre-loaded content.
39
+ *
40
+ * All mutating methods (save, dirty, file change) are no-ops.
41
+ * The `readOnly` flag is set to `true` so extensions can check it
42
+ * to disable editing UI.
43
+ */
44
+ export declare function createReadOnlyHost(content: string, opts: ReadOnlyHostOptions): ReadOnlyHost;
45
+ //# sourceMappingURL=createReadOnlyHost.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createReadOnlyHost.d.ts","sourceRoot":"","sources":["../src/createReadOnlyHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAiC,MAAM,mBAAmB,CAAC;AAEnF,MAAM,WAAW,mBAAmB;IAClC,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IAEd,4BAA4B;IAC5B,QAAQ,EAAE,MAAM,CAAC;IAEjB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAa,SAAQ,UAAU;IAC9C,gEAAgE;IAChE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,mBAAmB,GACxB,YAAY,CAyDd"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * ReadOnlyEditorHost factory for web share viewers.
3
+ *
4
+ * Creates a minimal EditorHost implementation that serves pre-loaded content
5
+ * in read-only mode. Used by the Cloudflare share viewer to render extension
6
+ * editors on the web without any Electron/IPC dependencies.
7
+ *
8
+ * Also useful for testing extensions in isolation.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const host = createReadOnlyHost(decryptedContent, {
13
+ * theme: 'dark',
14
+ * fileName: 'diagram.mindmap',
15
+ * });
16
+ *
17
+ * // Mount extension component
18
+ * root.render(<MindmapEditor host={host} />);
19
+ *
20
+ * // Toggle theme from viewer chrome
21
+ * host.setTheme('light');
22
+ * ```
23
+ */
24
+ /**
25
+ * Create a read-only EditorHost that serves pre-loaded content.
26
+ *
27
+ * All mutating methods (save, dirty, file change) are no-ops.
28
+ * The `readOnly` flag is set to `true` so extensions can check it
29
+ * to disable editing UI.
30
+ */
31
+ export function createReadOnlyHost(content, opts) {
32
+ let themeCallbacks = [];
33
+ let currentTheme = opts.theme;
34
+ const host = {
35
+ // -- File info --
36
+ filePath: opts.filePath ?? `/shared/${opts.fileName}`,
37
+ fileName: opts.fileName,
38
+ readOnly: true,
39
+ theme: currentTheme,
40
+ isActive: true,
41
+ // -- Content loading --
42
+ loadContent: async () => content,
43
+ loadBinaryContent: async () => new TextEncoder().encode(content).buffer,
44
+ // -- Theme --
45
+ onThemeChanged: (cb) => {
46
+ themeCallbacks.push(cb);
47
+ return () => {
48
+ themeCallbacks = themeCallbacks.filter((c) => c !== cb);
49
+ };
50
+ },
51
+ // -- No-ops for read-only --
52
+ saveContent: async () => { },
53
+ setDirty: () => { },
54
+ onSaveRequested: () => () => { },
55
+ onFileChanged: () => () => { },
56
+ openHistory: () => { },
57
+ registerMenuItems: (_items) => { },
58
+ registerEditorAPI: () => { },
59
+ setEditorContext: (_context) => { },
60
+ // -- Storage (in-memory, non-persistent) --
61
+ storage: {
62
+ get: (_key) => undefined,
63
+ set: async () => { },
64
+ delete: async () => { },
65
+ getGlobal: (_key) => undefined,
66
+ setGlobal: async () => { },
67
+ deleteGlobal: async () => { },
68
+ getSecret: async () => undefined,
69
+ setSecret: async () => { },
70
+ deleteSecret: async () => { },
71
+ },
72
+ // -- Theme toggle for viewer chrome --
73
+ setTheme(theme) {
74
+ currentTheme = theme;
75
+ // Update the host's own theme property
76
+ host.theme = theme;
77
+ themeCallbacks.forEach((cb) => cb(theme));
78
+ },
79
+ };
80
+ return host;
81
+ }
@@ -0,0 +1,6 @@
1
+ export interface DocumentPathContextValue {
2
+ documentPath: string | null;
3
+ documentDir: string | null;
4
+ }
5
+ export declare function useDocumentPath(): DocumentPathContextValue;
6
+ //# sourceMappingURL=documentPath.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"documentPath.d.ts","sourceRoot":"","sources":["../src/documentPath.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,wBAAwB;IACvC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,wBAAgB,eAAe,IAAI,wBAAwB,CAE1D"}
@@ -0,0 +1,4 @@
1
+ import { useDocumentPath as useHostDocumentPath } from '@nimbalyst/runtime';
2
+ export function useDocumentPath() {
3
+ return useHostDocumentPath();
4
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Libraries that MUST be externalized by extensions.
3
+ *
4
+ * These libraries are provided by the Nimbalyst host and must not be bundled
5
+ * by extensions. Bundling them would cause runtime errors due to:
6
+ * - React: Multiple instances break hooks ("rendered more hooks than previous render")
7
+ * - Lexical: Extensions contribute nodes to the host's editor instance
8
+ *
9
+ * Extensions should bundle their own utility libraries (zustand, lodash, etc.)
10
+ * for version independence.
11
+ */
12
+ export declare const REQUIRED_EXTERNALS: readonly ["react", "react-dom", "react-dom/client", "react/jsx-runtime", "react/jsx-dev-runtime", "lexical", "@lexical/react/LexicalComposerContext", "@lexical/react/useLexicalEditable", "@lexical/react/useLexicalNodeSelection", "@lexical/utils", "@lexical/markdown"];
13
+ /**
14
+ * Regex patterns for externals that match multiple modules.
15
+ */
16
+ export declare const EXTERNAL_PATTERNS: readonly [RegExp, RegExp];
17
+ /**
18
+ * Combined externals for rollup configuration.
19
+ * Use this in rollupOptions.external
20
+ */
21
+ export declare const ROLLUP_EXTERNALS: readonly ["react", "react-dom", "react-dom/client", "react/jsx-runtime", "react/jsx-dev-runtime", "lexical", "@lexical/react/LexicalComposerContext", "@lexical/react/useLexicalEditable", "@lexical/react/useLexicalNodeSelection", "@lexical/utils", "@lexical/markdown", RegExp, RegExp, "@nimbalyst/editor-context"];
22
+ export type RequiredExternal = (typeof REQUIRED_EXTERNALS)[number];
23
+ //# sourceMappingURL=externals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"externals.d.ts","sourceRoot":"","sources":["../src/externals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,6QAerB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,iBAAiB,2BAGpB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,gBAAgB,0TAInB,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Libraries that MUST be externalized by extensions.
3
+ *
4
+ * These libraries are provided by the Nimbalyst host and must not be bundled
5
+ * by extensions. Bundling them would cause runtime errors due to:
6
+ * - React: Multiple instances break hooks ("rendered more hooks than previous render")
7
+ * - Lexical: Extensions contribute nodes to the host's editor instance
8
+ *
9
+ * Extensions should bundle their own utility libraries (zustand, lodash, etc.)
10
+ * for version independence.
11
+ */
12
+ export const REQUIRED_EXTERNALS = [
13
+ // React core - multiple instances break hooks
14
+ 'react',
15
+ 'react-dom',
16
+ 'react-dom/client',
17
+ 'react/jsx-runtime',
18
+ 'react/jsx-dev-runtime',
19
+ // Lexical - extensions contribute nodes to host's editor
20
+ 'lexical',
21
+ '@lexical/react/LexicalComposerContext',
22
+ '@lexical/react/useLexicalEditable',
23
+ '@lexical/react/useLexicalNodeSelection',
24
+ '@lexical/utils',
25
+ '@lexical/markdown',
26
+ ];
27
+ /**
28
+ * Regex patterns for externals that match multiple modules.
29
+ */
30
+ export const EXTERNAL_PATTERNS = [
31
+ /^@lexical\//,
32
+ /^@nimbalyst\/runtime/,
33
+ ];
34
+ /**
35
+ * Combined externals for rollup configuration.
36
+ * Use this in rollupOptions.external
37
+ */
38
+ export const ROLLUP_EXTERNALS = [
39
+ ...REQUIRED_EXTERNALS,
40
+ ...EXTERNAL_PATTERNS,
41
+ '@nimbalyst/editor-context',
42
+ ];
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Vite/Rollup plugin that transforms external imports to window.__nimbalyst_extensions references.
3
+ *
4
+ * This plugin runs at BUILD TIME, transforming imports BEFORE minification.
5
+ * This is more robust than runtime regex transformation because:
6
+ * 1. Source code has predictable import syntax
7
+ * 2. Minification happens AFTER transformation
8
+ * 3. Build errors are caught early
9
+ *
10
+ * HOW IT WORKS:
11
+ * Instead of marking modules as "external" (which keeps bare import statements that
12
+ * blob URLs can't resolve), we resolve imports to virtual modules that access
13
+ * window.__nimbalyst_extensions at runtime.
14
+ *
15
+ * Example:
16
+ * import React, { useState } from 'react'
17
+ * ->
18
+ * // Generated virtual module gets bundled inline:
19
+ * const __mod = window.__nimbalyst_extensions["react"];
20
+ * export default __mod;
21
+ * export const useState = __mod.useState;
22
+ * // etc.
23
+ *
24
+ * The extension's code then imports from this virtual module, which after bundling
25
+ * becomes direct property accesses on window.__nimbalyst_extensions.
26
+ */
27
+ import type { Plugin } from 'vite';
28
+ /**
29
+ * Creates a Vite plugin that transforms external imports at build time.
30
+ *
31
+ * This approach uses renderChunk to transform the final output after bundling
32
+ * but captures the original import information during the transform phase.
33
+ *
34
+ * The key insight: we DON'T mark these as external. Instead, we let Rollup
35
+ * try to resolve them, fail, and then our resolveId catches them and returns
36
+ * a virtual module. The virtual module's code accesses window.__nimbalyst_extensions.
37
+ */
38
+ export declare function nimbalystExternalsPlugin(): Plugin;
39
+ //# sourceMappingURL=externalsPlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"externalsPlugin.d.ts","sourceRoot":"","sources":["../src/externalsPlugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAqDnC;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAyDjD"}