@@ -9,8 +9,13 @@ import {
99import type { IKernel } from '@jupyterlite/services' ;
1010
1111import { IKernelSpecs } from '@jupyterlite/services' ;
12+ import { DisposableDelegate } from '@lumino/disposable' ;
13+ import type { IDisposable } from '@lumino/disposable' ;
1214
13- import { JavaScriptKernel } from '@jupyterlite/javascript-kernel' ;
15+ import {
16+ IJavaScriptKernelStartup ,
17+ JavaScriptKernel
18+ } from '@jupyterlite/javascript-kernel' ;
1419import type { RuntimeMode } from '@jupyterlite/javascript-kernel' ;
1520
1621import jsLogo32 from '../style/icons/logo-32x32.png' ;
@@ -24,13 +29,14 @@ interface IRegisterKernelOptions {
2429 name : string ;
2530 displayName : string ;
2631 runtime : RuntimeMode ;
32+ startup : IJavaScriptKernelStartup ;
2733}
2834
2935const registerKernel = (
3036 kernelspecs : IKernelSpecs ,
3137 options : IRegisterKernelOptions
3238) => {
33- const { name, displayName, runtime } = options ;
39+ const { name, displayName, runtime, startup } = options ;
3440
3541 kernelspecs . register ( {
3642 spec : {
@@ -54,26 +60,111 @@ const registerKernel = (
5460 }
5561 } ,
5662 create : async ( options : IKernel . IOptions ) : Promise < IKernel > => {
57- return new JavaScriptKernel ( {
63+ const kernel = new JavaScriptKernel ( {
5864 ...options ,
59- runtime
65+ runtime,
66+ startupExtensions : startup . startupExtensions
6067 } as JavaScriptKernel . IOptions ) ;
68+ if ( startup instanceof JavaScriptKernelStartup ) {
69+ startup . trackKernel ( kernel ) ;
70+ }
71+ return kernel ;
6172 }
6273 } ) ;
6374} ;
6475
76+ /**
77+ * In-memory registry for JavaScript kernel startup extensions.
78+ */
79+ class JavaScriptKernelStartup implements IJavaScriptKernelStartup {
80+ get startupExtensions ( ) : readonly JavaScriptKernel . IStartupExtension [ ] {
81+ return [ ...this . _startupExtensions ] ;
82+ }
83+
84+ registerStartupExtension (
85+ extension : JavaScriptKernel . IStartupExtension
86+ ) : IDisposable {
87+ const existing = this . _startupExtensions . findIndex (
88+ item => item . id === extension . id
89+ ) ;
90+
91+ if ( existing !== - 1 ) {
92+ throw new Error (
93+ `JavaScript kernel startup extension "${ extension . id } " is already registered`
94+ ) ;
95+ }
96+
97+ this . _startupExtensions . push ( extension ) ;
98+ void Promise . all (
99+ [ ...this . _kernels ] . map ( kernel => kernel . applyStartupExtension ( extension ) )
100+ ) . catch ( error => {
101+ console . error (
102+ `[javascript-kernel] Failed to apply startup extension "${ extension . id } ".` ,
103+ error
104+ ) ;
105+ } ) ;
106+
107+ return new DisposableDelegate ( ( ) => {
108+ const index = this . _startupExtensions . indexOf ( extension ) ;
109+ if ( index !== - 1 ) {
110+ this . _startupExtensions . splice ( index , 1 ) ;
111+ void Promise . all (
112+ [ ...this . _kernels ] . map ( kernel =>
113+ kernel . removeStartupExtension ( extension )
114+ )
115+ ) . catch ( error => {
116+ console . error (
117+ `[javascript-kernel] Failed to remove startup extension "${ extension . id } ".` ,
118+ error
119+ ) ;
120+ } ) ;
121+ }
122+ } ) ;
123+ }
124+
125+ /**
126+ * Track an active JavaScript kernel for late startup registrations.
127+ */
128+ trackKernel ( kernel : JavaScriptKernel ) : void {
129+ this . _kernels . add ( kernel ) ;
130+ const untrackKernel = ( sender : JavaScriptKernel ) : void => {
131+ this . _kernels . delete ( sender ) ;
132+ sender . disposed . disconnect ( untrackKernel ) ;
133+ } ;
134+ kernel . disposed . connect ( untrackKernel ) ;
135+ }
136+
137+ private _startupExtensions : JavaScriptKernel . IStartupExtension [ ] = [ ] ;
138+ private _kernels = new Set < JavaScriptKernel > ( ) ;
139+ }
140+
141+ /**
142+ * Plugin providing the JavaScript kernel startup extension registry.
143+ */
144+ const startupExtensions : JupyterFrontEndPlugin < IJavaScriptKernelStartup > = {
145+ id : '@jupyterlite/javascript-kernel-extension:startup-extensions' ,
146+ autoStart : true ,
147+ provides : IJavaScriptKernelStartup ,
148+ activate : ( ) => new JavaScriptKernelStartup ( )
149+ } ;
150+
65151/**
66152 * Plugin registering the iframe JavaScript kernel.
67153 */
68154const kernelIFrame : JupyterFrontEndPlugin < void > = {
69155 id : '@jupyterlite/javascript-kernel-extension:kernel-iframe' ,
70156 autoStart : true ,
71- requires : [ IKernelSpecs ] ,
72- activate : ( app : JupyterFrontEnd , kernelspecs : IKernelSpecs ) => {
157+ requires : [ IKernelSpecs , IJavaScriptKernelStartup ] ,
158+ activate : (
159+ app : JupyterFrontEnd ,
160+ kernelspecs : IKernelSpecs ,
161+ startup : IJavaScriptKernelStartup
162+ ) => {
73163 registerKernel ( kernelspecs , {
74164 name : 'javascript' ,
75165 displayName : 'JavaScript (IFrame)' ,
76- runtime : 'iframe'
166+ runtime : 'iframe' ,
167+ startup
77168 } ) ;
78169 }
79170} ;
@@ -84,16 +175,23 @@ const kernelIFrame: JupyterFrontEndPlugin<void> = {
84175const kernelWorker : JupyterFrontEndPlugin < void > = {
85176 id : '@jupyterlite/javascript-kernel-extension:kernel-worker' ,
86177 autoStart : true ,
87- requires : [ IKernelSpecs ] ,
88- activate : ( app : JupyterFrontEnd , kernelspecs : IKernelSpecs ) => {
178+ requires : [ IKernelSpecs , IJavaScriptKernelStartup ] ,
179+ activate : (
180+ app : JupyterFrontEnd ,
181+ kernelspecs : IKernelSpecs ,
182+ startup : IJavaScriptKernelStartup
183+ ) => {
89184 registerKernel ( kernelspecs , {
90185 name : 'javascript-worker' ,
91186 displayName : 'JavaScript (Web Worker)' ,
92- runtime : 'worker'
187+ runtime : 'worker' ,
188+ startup
93189 } ) ;
94190 }
95191} ;
96192
97- const plugins : JupyterFrontEndPlugin < void > [ ] = [ kernelIFrame , kernelWorker ] ;
193+ const plugins : Array <
194+ JupyterFrontEndPlugin < IJavaScriptKernelStartup > | JupyterFrontEndPlugin < void >
195+ > = [ startupExtensions , kernelIFrame , kernelWorker ] ;
98196
99197export default plugins ;
0 commit comments