jupyterlab-kernel-terminal-workspace-culler-extension 1.0.21__py3-none-any.whl
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.
- jupyterlab_kernel_terminal_workspace_culler_extension/__init__.py +54 -0
- jupyterlab_kernel_terminal_workspace_culler_extension/_version.py +4 -0
- jupyterlab_kernel_terminal_workspace_culler_extension/cli.py +430 -0
- jupyterlab_kernel_terminal_workspace_culler_extension/culler.py +499 -0
- jupyterlab_kernel_terminal_workspace_culler_extension/routes.py +176 -0
- jupyterlab_kernel_terminal_workspace_culler_extension/tests/__init__.py +1 -0
- jupyterlab_kernel_terminal_workspace_culler_extension/tests/test_culler.py +269 -0
- jupyterlab_kernel_terminal_workspace_culler_extension/tests/test_routes.py +17 -0
- jupyterlab_kernel_terminal_workspace_culler_extension-1.0.21.data/data/etc/jupyter/jupyter_server_config.d/jupyterlab_kernel_terminal_workspace_culler_extension.json +7 -0
- jupyterlab_kernel_terminal_workspace_culler_extension-1.0.21.data/data/share/jupyter/labextensions/jupyterlab_kernel_terminal_workspace_culler_extension/install.json +5 -0
- jupyterlab_kernel_terminal_workspace_culler_extension-1.0.21.data/data/share/jupyter/labextensions/jupyterlab_kernel_terminal_workspace_culler_extension/package.json +219 -0
- jupyterlab_kernel_terminal_workspace_culler_extension-1.0.21.data/data/share/jupyter/labextensions/jupyterlab_kernel_terminal_workspace_culler_extension/schemas/jupyterlab_kernel_terminal_workspace_culler_extension/package.json.orig +214 -0
- jupyterlab_kernel_terminal_workspace_culler_extension-1.0.21.data/data/share/jupyter/labextensions/jupyterlab_kernel_terminal_workspace_culler_extension/schemas/jupyterlab_kernel_terminal_workspace_culler_extension/plugin.json +64 -0
- jupyterlab_kernel_terminal_workspace_culler_extension-1.0.21.data/data/share/jupyter/labextensions/jupyterlab_kernel_terminal_workspace_culler_extension/static/728.b056947597422f9e496c.js +1 -0
- jupyterlab_kernel_terminal_workspace_culler_extension-1.0.21.data/data/share/jupyter/labextensions/jupyterlab_kernel_terminal_workspace_culler_extension/static/750.b2aa372edac477cffcb9.js +1 -0
- jupyterlab_kernel_terminal_workspace_culler_extension-1.0.21.data/data/share/jupyter/labextensions/jupyterlab_kernel_terminal_workspace_culler_extension/static/remoteEntry.61977aac2cfde9b88947.js +1 -0
- jupyterlab_kernel_terminal_workspace_culler_extension-1.0.21.data/data/share/jupyter/labextensions/jupyterlab_kernel_terminal_workspace_culler_extension/static/style.js +4 -0
- jupyterlab_kernel_terminal_workspace_culler_extension-1.0.21.data/data/share/jupyter/labextensions/jupyterlab_kernel_terminal_workspace_culler_extension/static/third-party-licenses.json +16 -0
- jupyterlab_kernel_terminal_workspace_culler_extension-1.0.21.dist-info/METADATA +208 -0
- jupyterlab_kernel_terminal_workspace_culler_extension-1.0.21.dist-info/RECORD +23 -0
- jupyterlab_kernel_terminal_workspace_culler_extension-1.0.21.dist-info/WHEEL +4 -0
- jupyterlab_kernel_terminal_workspace_culler_extension-1.0.21.dist-info/entry_points.txt +2 -0
- jupyterlab_kernel_terminal_workspace_culler_extension-1.0.21.dist-info/licenses/LICENSE +29 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"""Unit tests for the resource culler."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timedelta, timezone
|
|
4
|
+
from unittest.mock import AsyncMock, MagicMock, patch
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from jupyterlab_kernel_terminal_workspace_culler_extension.culler import ResourceCuller
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.fixture
|
|
12
|
+
def mock_server_app():
|
|
13
|
+
"""Create a mock server app with kernel, terminal, and session managers."""
|
|
14
|
+
app = MagicMock()
|
|
15
|
+
|
|
16
|
+
# Mock kernel manager
|
|
17
|
+
app.kernel_manager = MagicMock()
|
|
18
|
+
app.kernel_manager.list_kernel_ids.return_value = []
|
|
19
|
+
app.kernel_manager.get_kernel.return_value = None
|
|
20
|
+
app.kernel_manager.shutdown_kernel = AsyncMock()
|
|
21
|
+
|
|
22
|
+
# Mock terminal manager
|
|
23
|
+
app.terminal_manager = MagicMock()
|
|
24
|
+
app.terminal_manager.list.return_value = []
|
|
25
|
+
app.terminal_manager.terminate = AsyncMock()
|
|
26
|
+
|
|
27
|
+
# Mock session manager
|
|
28
|
+
app.session_manager = MagicMock()
|
|
29
|
+
app.session_manager.list_sessions = AsyncMock(return_value=[])
|
|
30
|
+
app.session_manager.delete_session = AsyncMock()
|
|
31
|
+
|
|
32
|
+
return app
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pytest.fixture
|
|
36
|
+
def culler(mock_server_app):
|
|
37
|
+
"""Create a ResourceCuller instance with mocked server app."""
|
|
38
|
+
return ResourceCuller(mock_server_app)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class TestDefaultSettings:
|
|
42
|
+
"""Test default settings initialization."""
|
|
43
|
+
|
|
44
|
+
def test_default_kernel_settings(self, culler):
|
|
45
|
+
"""Verify default kernel culling settings."""
|
|
46
|
+
settings = culler.get_settings()
|
|
47
|
+
assert settings["kernelCullEnabled"] is True
|
|
48
|
+
assert settings["kernelCullIdleTimeout"] == 60 # 1 hour
|
|
49
|
+
|
|
50
|
+
def test_default_terminal_settings(self, culler):
|
|
51
|
+
"""Verify default terminal culling settings."""
|
|
52
|
+
settings = culler.get_settings()
|
|
53
|
+
assert settings["terminalCullEnabled"] is True
|
|
54
|
+
assert settings["terminalCullIdleTimeout"] == 60 # 1 hour
|
|
55
|
+
|
|
56
|
+
def test_default_session_settings(self, culler):
|
|
57
|
+
"""Verify default session culling settings."""
|
|
58
|
+
settings = culler.get_settings()
|
|
59
|
+
assert settings["sessionCullEnabled"] is False
|
|
60
|
+
assert settings["sessionCullIdleTimeout"] == 10080 # 7 days
|
|
61
|
+
|
|
62
|
+
def test_default_check_interval(self, culler):
|
|
63
|
+
"""Verify default check interval."""
|
|
64
|
+
settings = culler.get_settings()
|
|
65
|
+
assert settings["cullCheckInterval"] == 5
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class TestUpdateSettings:
|
|
69
|
+
"""Test settings update functionality."""
|
|
70
|
+
|
|
71
|
+
def test_update_kernel_settings(self, culler):
|
|
72
|
+
"""Test updating kernel culling settings."""
|
|
73
|
+
culler.update_settings({
|
|
74
|
+
"kernelCullEnabled": False,
|
|
75
|
+
"kernelCullIdleTimeout": 120
|
|
76
|
+
})
|
|
77
|
+
settings = culler.get_settings()
|
|
78
|
+
assert settings["kernelCullEnabled"] is False
|
|
79
|
+
assert settings["kernelCullIdleTimeout"] == 120
|
|
80
|
+
|
|
81
|
+
def test_update_terminal_settings(self, culler):
|
|
82
|
+
"""Test updating terminal culling settings."""
|
|
83
|
+
culler.update_settings({
|
|
84
|
+
"terminalCullEnabled": False,
|
|
85
|
+
"terminalCullIdleTimeout": 45
|
|
86
|
+
})
|
|
87
|
+
settings = culler.get_settings()
|
|
88
|
+
assert settings["terminalCullEnabled"] is False
|
|
89
|
+
assert settings["terminalCullIdleTimeout"] == 45
|
|
90
|
+
|
|
91
|
+
def test_update_session_settings(self, culler):
|
|
92
|
+
"""Test updating session culling settings."""
|
|
93
|
+
culler.update_settings({
|
|
94
|
+
"sessionCullEnabled": True,
|
|
95
|
+
"sessionCullIdleTimeout": 60
|
|
96
|
+
})
|
|
97
|
+
settings = culler.get_settings()
|
|
98
|
+
assert settings["sessionCullEnabled"] is True
|
|
99
|
+
assert settings["sessionCullIdleTimeout"] == 60
|
|
100
|
+
|
|
101
|
+
def test_partial_update(self, culler):
|
|
102
|
+
"""Test that partial updates don't affect other settings."""
|
|
103
|
+
culler.update_settings({"kernelCullEnabled": False})
|
|
104
|
+
settings = culler.get_settings()
|
|
105
|
+
assert settings["kernelCullEnabled"] is False
|
|
106
|
+
# Other settings should remain at defaults
|
|
107
|
+
assert settings["terminalCullEnabled"] is True
|
|
108
|
+
assert settings["sessionCullEnabled"] is False
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class TestCullIdleKernel:
|
|
112
|
+
"""Test kernel culling functionality."""
|
|
113
|
+
|
|
114
|
+
@pytest.mark.asyncio
|
|
115
|
+
async def test_cull_idle_kernel(self, culler, mock_server_app):
|
|
116
|
+
"""Test that idle kernel exceeding timeout gets culled."""
|
|
117
|
+
kernel_id = "test-kernel-123"
|
|
118
|
+
idle_time = datetime.now(timezone.utc) - timedelta(minutes=120)
|
|
119
|
+
|
|
120
|
+
mock_kernel = MagicMock()
|
|
121
|
+
mock_kernel.execution_state = "idle"
|
|
122
|
+
mock_kernel.last_activity = idle_time
|
|
123
|
+
|
|
124
|
+
mock_server_app.kernel_manager.list_kernel_ids.return_value = [kernel_id]
|
|
125
|
+
mock_server_app.kernel_manager.get_kernel.return_value = mock_kernel
|
|
126
|
+
|
|
127
|
+
culled = await culler._cull_kernels()
|
|
128
|
+
|
|
129
|
+
assert kernel_id in culled
|
|
130
|
+
mock_server_app.kernel_manager.shutdown_kernel.assert_called_once_with(kernel_id)
|
|
131
|
+
|
|
132
|
+
@pytest.mark.asyncio
|
|
133
|
+
async def test_skip_busy_kernel(self, culler, mock_server_app):
|
|
134
|
+
"""Test that busy kernels are not culled."""
|
|
135
|
+
kernel_id = "busy-kernel-123"
|
|
136
|
+
idle_time = datetime.now(timezone.utc) - timedelta(minutes=120)
|
|
137
|
+
|
|
138
|
+
mock_kernel = MagicMock()
|
|
139
|
+
mock_kernel.execution_state = "busy"
|
|
140
|
+
mock_kernel.last_activity = idle_time
|
|
141
|
+
|
|
142
|
+
mock_server_app.kernel_manager.list_kernel_ids.return_value = [kernel_id]
|
|
143
|
+
mock_server_app.kernel_manager.get_kernel.return_value = mock_kernel
|
|
144
|
+
|
|
145
|
+
culled = await culler._cull_kernels()
|
|
146
|
+
|
|
147
|
+
assert kernel_id not in culled
|
|
148
|
+
mock_server_app.kernel_manager.shutdown_kernel.assert_not_called()
|
|
149
|
+
|
|
150
|
+
@pytest.mark.asyncio
|
|
151
|
+
async def test_skip_active_kernel(self, culler, mock_server_app):
|
|
152
|
+
"""Test that recently active kernels are not culled."""
|
|
153
|
+
kernel_id = "active-kernel-123"
|
|
154
|
+
# Last activity 5 minutes ago - below 60 minute threshold
|
|
155
|
+
recent_time = datetime.now(timezone.utc) - timedelta(minutes=5)
|
|
156
|
+
|
|
157
|
+
mock_kernel = MagicMock()
|
|
158
|
+
mock_kernel.execution_state = "idle"
|
|
159
|
+
mock_kernel.last_activity = recent_time
|
|
160
|
+
|
|
161
|
+
mock_server_app.kernel_manager.list_kernel_ids.return_value = [kernel_id]
|
|
162
|
+
mock_server_app.kernel_manager.get_kernel.return_value = mock_kernel
|
|
163
|
+
|
|
164
|
+
culled = await culler._cull_kernels()
|
|
165
|
+
|
|
166
|
+
assert kernel_id not in culled
|
|
167
|
+
mock_server_app.kernel_manager.shutdown_kernel.assert_not_called()
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class TestCullIdleTerminal:
|
|
171
|
+
"""Test terminal culling functionality."""
|
|
172
|
+
|
|
173
|
+
@pytest.mark.asyncio
|
|
174
|
+
async def test_cull_idle_terminal(self, culler, mock_server_app):
|
|
175
|
+
"""Test that idle terminal exceeding timeout gets culled."""
|
|
176
|
+
terminal_name = "terminal-1"
|
|
177
|
+
idle_time = datetime.now(timezone.utc) - timedelta(minutes=60)
|
|
178
|
+
|
|
179
|
+
mock_server_app.terminal_manager.list.return_value = [
|
|
180
|
+
{"name": terminal_name, "last_activity": idle_time}
|
|
181
|
+
]
|
|
182
|
+
|
|
183
|
+
culled = await culler._cull_terminals()
|
|
184
|
+
|
|
185
|
+
assert terminal_name in culled
|
|
186
|
+
mock_server_app.terminal_manager.terminate.assert_called_once_with(terminal_name)
|
|
187
|
+
|
|
188
|
+
@pytest.mark.asyncio
|
|
189
|
+
async def test_skip_active_terminal(self, culler, mock_server_app):
|
|
190
|
+
"""Test that recently active terminals are not culled."""
|
|
191
|
+
terminal_name = "terminal-1"
|
|
192
|
+
# Last activity 5 minutes ago - below 30 minute threshold
|
|
193
|
+
recent_time = datetime.now(timezone.utc) - timedelta(minutes=5)
|
|
194
|
+
|
|
195
|
+
mock_server_app.terminal_manager.list.return_value = [
|
|
196
|
+
{"name": terminal_name, "last_activity": recent_time}
|
|
197
|
+
]
|
|
198
|
+
|
|
199
|
+
culled = await culler._cull_terminals()
|
|
200
|
+
|
|
201
|
+
assert terminal_name not in culled
|
|
202
|
+
mock_server_app.terminal_manager.terminate.assert_not_called()
|
|
203
|
+
|
|
204
|
+
@pytest.mark.asyncio
|
|
205
|
+
async def test_cull_terminal_with_iso_string(self, culler, mock_server_app):
|
|
206
|
+
"""Test culling terminal with ISO string timestamp."""
|
|
207
|
+
terminal_name = "terminal-1"
|
|
208
|
+
idle_time = (datetime.now(timezone.utc) - timedelta(minutes=60)).isoformat()
|
|
209
|
+
|
|
210
|
+
mock_server_app.terminal_manager.list.return_value = [
|
|
211
|
+
{"name": terminal_name, "last_activity": idle_time}
|
|
212
|
+
]
|
|
213
|
+
|
|
214
|
+
culled = await culler._cull_terminals()
|
|
215
|
+
|
|
216
|
+
assert terminal_name in culled
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class TestCullResult:
|
|
220
|
+
"""Test cull result retrieval."""
|
|
221
|
+
|
|
222
|
+
def test_get_last_cull_result_empty(self, culler):
|
|
223
|
+
"""Test that initial cull result is empty."""
|
|
224
|
+
result = culler.get_last_cull_result()
|
|
225
|
+
assert result["kernels_culled"] == []
|
|
226
|
+
assert result["terminals_culled"] == []
|
|
227
|
+
assert result["sessions_culled"] == []
|
|
228
|
+
|
|
229
|
+
def test_result_consumed_flag(self, culler):
|
|
230
|
+
"""Test that result is marked as consumed after retrieval."""
|
|
231
|
+
# First call returns empty (result_consumed is True initially)
|
|
232
|
+
result1 = culler.get_last_cull_result()
|
|
233
|
+
|
|
234
|
+
# Simulate a culling that produced results
|
|
235
|
+
culler._last_cull_result = {
|
|
236
|
+
"kernels_culled": ["kernel-1"],
|
|
237
|
+
"terminals_culled": [],
|
|
238
|
+
"sessions_culled": [],
|
|
239
|
+
}
|
|
240
|
+
culler._result_consumed = False
|
|
241
|
+
|
|
242
|
+
# First retrieval should return the result
|
|
243
|
+
result2 = culler.get_last_cull_result()
|
|
244
|
+
assert result2["kernels_culled"] == ["kernel-1"]
|
|
245
|
+
|
|
246
|
+
# Second retrieval should return empty (consumed)
|
|
247
|
+
result3 = culler.get_last_cull_result()
|
|
248
|
+
assert result3["kernels_culled"] == []
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class TestStatus:
|
|
252
|
+
"""Test status retrieval."""
|
|
253
|
+
|
|
254
|
+
def test_status_not_running(self, culler):
|
|
255
|
+
"""Test status when culler is not running."""
|
|
256
|
+
status = culler.get_status()
|
|
257
|
+
assert status["running"] is False
|
|
258
|
+
|
|
259
|
+
def test_status_running(self, culler):
|
|
260
|
+
"""Test status when culler is running."""
|
|
261
|
+
with patch.object(culler, "_periodic_callback", MagicMock()):
|
|
262
|
+
status = culler.get_status()
|
|
263
|
+
assert status["running"] is True
|
|
264
|
+
|
|
265
|
+
def test_status_includes_settings(self, culler):
|
|
266
|
+
"""Test that status includes current settings."""
|
|
267
|
+
status = culler.get_status()
|
|
268
|
+
assert "settings" in status
|
|
269
|
+
assert "kernelCullEnabled" in status["settings"]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
async def test_hello(jp_fetch):
|
|
5
|
+
# When
|
|
6
|
+
response = await jp_fetch("jupyterlab-kernel-terminal-workspace-culler-extension", "hello")
|
|
7
|
+
|
|
8
|
+
# Then
|
|
9
|
+
assert response.code == 200
|
|
10
|
+
payload = json.loads(response.body)
|
|
11
|
+
assert payload == {
|
|
12
|
+
"data": (
|
|
13
|
+
"Hello, world!"
|
|
14
|
+
" This is the '/jupyterlab-kernel-terminal-workspace-culler-extension/hello' endpoint."
|
|
15
|
+
" Try visiting me in your browser!"
|
|
16
|
+
),
|
|
17
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jupyterlab_kernel_terminal_workspace_culler_extension",
|
|
3
|
+
"version": "1.0.21",
|
|
4
|
+
"description": "Jupyterlab extension to kill unused kernels, terminals and workspaces. User can configure the idle time (minutes) after which the resource will be released automatically. This helps with the locked memory, insane number of terminals opened etc.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"jupyter",
|
|
7
|
+
"jupyterlab",
|
|
8
|
+
"jupyterlab-extension"
|
|
9
|
+
],
|
|
10
|
+
"homepage": "https://github.com/stellarshenson/jupyterlab_kernel_terminal_workspace_culler_extension",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/stellarshenson/jupyterlab_kernel_terminal_workspace_culler_extension/issues"
|
|
13
|
+
},
|
|
14
|
+
"license": "BSD-3-Clause",
|
|
15
|
+
"author": {
|
|
16
|
+
"name": "Stellars Henson",
|
|
17
|
+
"email": "konrad.jelen@gmail.com"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
|
|
21
|
+
"style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
|
|
22
|
+
"src/**/*.{ts,tsx}",
|
|
23
|
+
"schema/*.json"
|
|
24
|
+
],
|
|
25
|
+
"main": "lib/index.js",
|
|
26
|
+
"types": "lib/index.d.ts",
|
|
27
|
+
"style": "style/index.css",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/stellarshenson/jupyterlab_kernel_terminal_workspace_culler_extension.git"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "jlpm build:lib && jlpm build:labextension:dev",
|
|
34
|
+
"build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension",
|
|
35
|
+
"build:labextension": "jupyter labextension build .",
|
|
36
|
+
"build:labextension:dev": "jupyter labextension build --development True .",
|
|
37
|
+
"build:lib": "tsc --sourceMap",
|
|
38
|
+
"build:lib:prod": "tsc",
|
|
39
|
+
"clean": "jlpm clean:lib",
|
|
40
|
+
"clean:lib": "rimraf lib tsconfig.tsbuildinfo",
|
|
41
|
+
"clean:lintcache": "rimraf .eslintcache .stylelintcache",
|
|
42
|
+
"clean:labextension": "rimraf jupyterlab_kernel_terminal_workspace_culler_extension/labextension jupyterlab_kernel_terminal_workspace_culler_extension/_version.py",
|
|
43
|
+
"clean:all": "jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache",
|
|
44
|
+
"eslint": "jlpm eslint:check --fix",
|
|
45
|
+
"eslint:check": "eslint . --cache --ext .ts,.tsx",
|
|
46
|
+
"install:extension": "jlpm build",
|
|
47
|
+
"lint": "jlpm stylelint && jlpm prettier && jlpm eslint",
|
|
48
|
+
"lint:check": "jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check",
|
|
49
|
+
"prettier": "jlpm prettier:base --write --list-different",
|
|
50
|
+
"prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"",
|
|
51
|
+
"prettier:check": "jlpm prettier:base --check",
|
|
52
|
+
"stylelint": "jlpm stylelint:check --fix",
|
|
53
|
+
"stylelint:check": "stylelint --cache \"style/**/*.css\"",
|
|
54
|
+
"test": "jest --coverage",
|
|
55
|
+
"watch": "run-p watch:src watch:labextension",
|
|
56
|
+
"watch:src": "tsc -w --sourceMap",
|
|
57
|
+
"watch:labextension": "jupyter labextension watch ."
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"@jupyterlab/application": "^4.0.0",
|
|
61
|
+
"@jupyterlab/coreutils": "^6.0.0",
|
|
62
|
+
"@jupyterlab/services": "^7.0.0",
|
|
63
|
+
"@jupyterlab/settingregistry": "^4.0.0",
|
|
64
|
+
"@jupyterlab/terminal": "^4.0.0"
|
|
65
|
+
},
|
|
66
|
+
"devDependencies": {
|
|
67
|
+
"@jupyterlab/builder": "^4.0.0",
|
|
68
|
+
"@jupyterlab/testutils": "^4.0.0",
|
|
69
|
+
"@types/jest": "^29.2.0",
|
|
70
|
+
"@types/json-schema": "^7.0.11",
|
|
71
|
+
"@types/react": "^18.0.26",
|
|
72
|
+
"@types/react-addons-linked-state-mixin": "^0.14.22",
|
|
73
|
+
"@typescript-eslint/eslint-plugin": "^6.1.0",
|
|
74
|
+
"@typescript-eslint/parser": "^6.1.0",
|
|
75
|
+
"css-loader": "^6.7.1",
|
|
76
|
+
"eslint": "^8.36.0",
|
|
77
|
+
"eslint-config-prettier": "^8.8.0",
|
|
78
|
+
"eslint-plugin-prettier": "^5.0.0",
|
|
79
|
+
"jest": "^29.2.0",
|
|
80
|
+
"mkdirp": "^1.0.3",
|
|
81
|
+
"npm-run-all2": "^7.0.1",
|
|
82
|
+
"prettier": "^3.0.0",
|
|
83
|
+
"rimraf": "^5.0.10",
|
|
84
|
+
"source-map-loader": "^1.0.2",
|
|
85
|
+
"style-loader": "^3.3.1",
|
|
86
|
+
"stylelint": "^15.10.1",
|
|
87
|
+
"stylelint-config-recommended": "^13.0.0",
|
|
88
|
+
"stylelint-config-standard": "^34.0.0",
|
|
89
|
+
"stylelint-csstree-validator": "^3.0.0",
|
|
90
|
+
"stylelint-prettier": "^4.0.0",
|
|
91
|
+
"typescript": "~5.5.4",
|
|
92
|
+
"yjs": "^13.5.0"
|
|
93
|
+
},
|
|
94
|
+
"resolutions": {
|
|
95
|
+
"lib0": "0.2.111"
|
|
96
|
+
},
|
|
97
|
+
"sideEffects": [
|
|
98
|
+
"style/*.css",
|
|
99
|
+
"style/index.js"
|
|
100
|
+
],
|
|
101
|
+
"styleModule": "style/index.js",
|
|
102
|
+
"publishConfig": {
|
|
103
|
+
"access": "public"
|
|
104
|
+
},
|
|
105
|
+
"jupyterlab": {
|
|
106
|
+
"discovery": {
|
|
107
|
+
"server": {
|
|
108
|
+
"managers": [
|
|
109
|
+
"pip"
|
|
110
|
+
],
|
|
111
|
+
"base": {
|
|
112
|
+
"name": "jupyterlab_kernel_terminal_workspace_culler_extension"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"extension": true,
|
|
117
|
+
"outputDir": "jupyterlab_kernel_terminal_workspace_culler_extension/labextension",
|
|
118
|
+
"schemaDir": "schema",
|
|
119
|
+
"_build": {
|
|
120
|
+
"load": "static/remoteEntry.61977aac2cfde9b88947.js",
|
|
121
|
+
"extension": "./extension",
|
|
122
|
+
"style": "./style"
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"eslintIgnore": [
|
|
126
|
+
"node_modules",
|
|
127
|
+
"dist",
|
|
128
|
+
"coverage",
|
|
129
|
+
"**/*.d.ts",
|
|
130
|
+
"tests",
|
|
131
|
+
"**/__tests__",
|
|
132
|
+
"ui-tests"
|
|
133
|
+
],
|
|
134
|
+
"eslintConfig": {
|
|
135
|
+
"extends": [
|
|
136
|
+
"eslint:recommended",
|
|
137
|
+
"plugin:@typescript-eslint/eslint-recommended",
|
|
138
|
+
"plugin:@typescript-eslint/recommended",
|
|
139
|
+
"plugin:prettier/recommended"
|
|
140
|
+
],
|
|
141
|
+
"parser": "@typescript-eslint/parser",
|
|
142
|
+
"parserOptions": {
|
|
143
|
+
"project": "tsconfig.json",
|
|
144
|
+
"sourceType": "module"
|
|
145
|
+
},
|
|
146
|
+
"plugins": [
|
|
147
|
+
"@typescript-eslint"
|
|
148
|
+
],
|
|
149
|
+
"rules": {
|
|
150
|
+
"@typescript-eslint/naming-convention": [
|
|
151
|
+
"error",
|
|
152
|
+
{
|
|
153
|
+
"selector": "interface",
|
|
154
|
+
"format": [
|
|
155
|
+
"PascalCase"
|
|
156
|
+
],
|
|
157
|
+
"custom": {
|
|
158
|
+
"regex": "^I[A-Z]",
|
|
159
|
+
"match": true
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
],
|
|
163
|
+
"@typescript-eslint/no-unused-vars": [
|
|
164
|
+
"warn",
|
|
165
|
+
{
|
|
166
|
+
"args": "none"
|
|
167
|
+
}
|
|
168
|
+
],
|
|
169
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
170
|
+
"@typescript-eslint/no-namespace": "off",
|
|
171
|
+
"@typescript-eslint/no-use-before-define": "off",
|
|
172
|
+
"@typescript-eslint/quotes": [
|
|
173
|
+
"error",
|
|
174
|
+
"single",
|
|
175
|
+
{
|
|
176
|
+
"avoidEscape": true,
|
|
177
|
+
"allowTemplateLiterals": false
|
|
178
|
+
}
|
|
179
|
+
],
|
|
180
|
+
"curly": [
|
|
181
|
+
"error",
|
|
182
|
+
"all"
|
|
183
|
+
],
|
|
184
|
+
"eqeqeq": "error",
|
|
185
|
+
"prefer-arrow-callback": "error"
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
"prettier": {
|
|
189
|
+
"singleQuote": true,
|
|
190
|
+
"trailingComma": "none",
|
|
191
|
+
"arrowParens": "avoid",
|
|
192
|
+
"endOfLine": "auto",
|
|
193
|
+
"overrides": [
|
|
194
|
+
{
|
|
195
|
+
"files": "package.json",
|
|
196
|
+
"options": {
|
|
197
|
+
"tabWidth": 4
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
},
|
|
202
|
+
"stylelint": {
|
|
203
|
+
"extends": [
|
|
204
|
+
"stylelint-config-recommended",
|
|
205
|
+
"stylelint-config-standard",
|
|
206
|
+
"stylelint-prettier/recommended"
|
|
207
|
+
],
|
|
208
|
+
"plugins": [
|
|
209
|
+
"stylelint-csstree-validator"
|
|
210
|
+
],
|
|
211
|
+
"rules": {
|
|
212
|
+
"csstree/validator": true,
|
|
213
|
+
"property-no-vendor-prefix": null,
|
|
214
|
+
"selector-class-pattern": "^([a-z][A-z\\d]*)(-[A-z\\d]+)*$",
|
|
215
|
+
"selector-no-vendor-prefix": null,
|
|
216
|
+
"value-no-vendor-prefix": null
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|