@datalayer/core 0.0.4 → 0.0.6
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.
- package/lib/hooks/useParams.js +1 -41
- package/lib/services/DatalayerServiceManager.d.ts +1 -1
- package/lib/services/DatalayerServiceManager.js +1 -11
- package/lib/state/substates/CoreState.js +4 -3
- package/lib/state/substates/IAMState.js +43 -46
- package/lib/state/substates/RuntimesState.js +5 -1
- package/lib/theme/DatalayerThemeProvider.js +8 -13
- package/lib/utils/logger.d.ts +16 -0
- package/lib/utils/logger.js +52 -0
- package/package.json +16 -8
package/lib/hooks/useParams.js
CHANGED
|
@@ -79,40 +79,6 @@ export const useParams = () => {
|
|
|
79
79
|
window.removeEventListener('replacestate', updateSearchParams);
|
|
80
80
|
};
|
|
81
81
|
}, [isClient, paramsSource]);
|
|
82
|
-
// 4. For fallback, also try to parse route params from URL path
|
|
83
|
-
const fallbackRouteParams = useMemo(() => {
|
|
84
|
-
if (!isClient || paramsSource !== 'fallback')
|
|
85
|
-
return {};
|
|
86
|
-
const pathname = window.location.pathname;
|
|
87
|
-
const params = {};
|
|
88
|
-
// Common patterns to match
|
|
89
|
-
// /products/123 -> { id: '123' }
|
|
90
|
-
// /users/john-doe -> { username: 'john-doe' }
|
|
91
|
-
// /blog/2024/01/my-post -> { year: '2024', month: '01', slug: 'my-post' }
|
|
92
|
-
const patterns = [
|
|
93
|
-
{ regex: /^\/products\/([^/]+)$/, params: ['id'] },
|
|
94
|
-
{ regex: /^\/products\/([^/]+)\/reviews$/, params: ['id'] },
|
|
95
|
-
{ regex: /^\/users\/([^/]+)$/, params: ['username'] },
|
|
96
|
-
{
|
|
97
|
-
regex: /^\/blog\/(\d{4})\/(\d{2})\/([^/]+)$/,
|
|
98
|
-
params: ['year', 'month', 'slug'],
|
|
99
|
-
},
|
|
100
|
-
{ regex: /^\/posts\/([^/]+)$/, params: ['slug'] },
|
|
101
|
-
{ regex: /^\/([^/]+)\/([^/]+)$/, params: ['category', 'id'] },
|
|
102
|
-
];
|
|
103
|
-
for (const pattern of patterns) {
|
|
104
|
-
const match = pathname.match(pattern.regex);
|
|
105
|
-
if (match) {
|
|
106
|
-
pattern.params.forEach((paramName, index) => {
|
|
107
|
-
if (match[index + 1]) {
|
|
108
|
-
params[paramName] = match[index + 1];
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
break;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
return params;
|
|
115
|
-
}, [isClient, paramsSource, isClient ? window.location.pathname : '']);
|
|
116
82
|
// Combine all params with proper memoization to prevent re-renders
|
|
117
83
|
const combinedParams = useMemo(() => {
|
|
118
84
|
const result = {};
|
|
@@ -133,19 +99,13 @@ export const useParams = () => {
|
|
|
133
99
|
result[key] = value;
|
|
134
100
|
}
|
|
135
101
|
}
|
|
136
|
-
//
|
|
137
|
-
if (paramsSource === 'fallback') {
|
|
138
|
-
for (const [key, value] of Object.entries(fallbackRouteParams)) {
|
|
139
|
-
result[key] = value;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
102
|
+
// Fallback route params are not supported without router configuration
|
|
142
103
|
return result;
|
|
143
104
|
}, [
|
|
144
105
|
// Use JSON.stringify for deep comparison
|
|
145
106
|
JSON.stringify(routeParams),
|
|
146
107
|
JSON.stringify(queryParams),
|
|
147
108
|
JSON.stringify(searchParams),
|
|
148
|
-
JSON.stringify(fallbackRouteParams),
|
|
149
109
|
paramsSource,
|
|
150
110
|
]);
|
|
151
111
|
return combinedParams;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ServiceManager } from '@jupyterlab/services';
|
|
2
2
|
/**
|
|
3
|
-
* Creates a ServiceManager configured for Datalayer
|
|
3
|
+
* Creates a ServiceManager configured for Datalayer.
|
|
4
4
|
*
|
|
5
5
|
* This function requests a new kernel from Datalayer's platform and
|
|
6
6
|
* returns a configured ServiceManager that connects to the allocated
|
|
@@ -7,7 +7,7 @@ import { coreStore } from '../state/substates/CoreState';
|
|
|
7
7
|
import { DEFAULT_DATALAYER_CONFIG } from '../config/Configuration';
|
|
8
8
|
import { createRuntime } from '../api/runtimes/actions';
|
|
9
9
|
/**
|
|
10
|
-
* Creates a ServiceManager configured for Datalayer
|
|
10
|
+
* Creates a ServiceManager configured for Datalayer.
|
|
11
11
|
*
|
|
12
12
|
* This function requests a new kernel from Datalayer's platform and
|
|
13
13
|
* returns a configured ServiceManager that connects to the allocated
|
|
@@ -52,16 +52,6 @@ export const createDatalayerServiceManager = async (environmentName, credits) =>
|
|
|
52
52
|
appendToken: true,
|
|
53
53
|
});
|
|
54
54
|
const serviceManager = new ServiceManager({ serverSettings });
|
|
55
|
-
// Store runtime info on the serviceManager instance for access
|
|
56
|
-
serviceManager.__datalayerRuntime = {
|
|
57
|
-
environmentName: actualEnvironmentName,
|
|
58
|
-
credits: actualCredits,
|
|
59
|
-
reservationId: runtime.reservation_id,
|
|
60
|
-
podName: runtime.pod_name,
|
|
61
|
-
ingress: runtime.ingress,
|
|
62
|
-
token: runtime.token,
|
|
63
|
-
createdAt: new Date().toISOString(),
|
|
64
|
-
};
|
|
65
55
|
console.log('Created Datalayer service manager:', {
|
|
66
56
|
environmentName: actualEnvironmentName,
|
|
67
57
|
credits: actualCredits,
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { createStore } from 'zustand/vanilla';
|
|
6
6
|
import { useStore } from 'zustand';
|
|
7
|
+
import { configLogger } from '../../utils/logger';
|
|
7
8
|
let loadConfigurationFromServer = true;
|
|
8
9
|
let initialConfiguration = {
|
|
9
10
|
runUrl: 'https://prod1.datalayer.run',
|
|
@@ -57,13 +58,13 @@ try {
|
|
|
57
58
|
...initialConfiguration,
|
|
58
59
|
...htmlOverridingConfiguration,
|
|
59
60
|
};
|
|
60
|
-
|
|
61
|
+
configLogger.info('Datalayer configuration loaded from HTML page', initialConfiguration);
|
|
61
62
|
window.document.title = `${initialConfiguration.brand.name} Ξ ${initialConfiguration.brand.about}`;
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
catch (error) {
|
|
66
|
-
|
|
67
|
+
configLogger.debug('No valid configuration found in the webpage.', error);
|
|
67
68
|
}
|
|
68
69
|
export const coreStore = createStore((set, get) => ({
|
|
69
70
|
tab: 0.0,
|
|
@@ -71,7 +72,7 @@ export const coreStore = createStore((set, get) => ({
|
|
|
71
72
|
setTab: (tab) => set((state) => ({ tab })),
|
|
72
73
|
configuration: initialConfiguration,
|
|
73
74
|
setConfiguration: (configuration) => {
|
|
74
|
-
|
|
75
|
+
configLogger.debug('Setting Datalayer configuration', configuration);
|
|
75
76
|
set(state => ({
|
|
76
77
|
configuration: {
|
|
77
78
|
...state.configuration,
|
|
@@ -235,52 +235,49 @@ const creditsPoll = new Poll({
|
|
|
235
235
|
standby: () => (iamStore.getState().user?.id ? 'when-hidden' : true),
|
|
236
236
|
});
|
|
237
237
|
// Initialize the IAM store with the stored token if it is valid.
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
})
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
}, 100); // 100ms delay to allow app initialization
|
|
238
|
+
iamStore
|
|
239
|
+
.getState()
|
|
240
|
+
.refreshUserByTokenStored()
|
|
241
|
+
.catch(reason => {
|
|
242
|
+
console.error('Failed to refresh to validate the stored token.', reason);
|
|
243
|
+
})
|
|
244
|
+
.finally(() => {
|
|
245
|
+
const { externalToken, iamRunUrl, checkIAMToken, token } = iamStore.getState();
|
|
246
|
+
// If the stored token is invalid and an external token exists, try authenticating with it.
|
|
247
|
+
if (!token && externalToken) {
|
|
248
|
+
console.debug('Can not login with token - Trying with the external token.');
|
|
249
|
+
requestDatalayerAPI({
|
|
250
|
+
url: `${iamRunUrl}/api/iam/v1/login`,
|
|
251
|
+
method: 'POST',
|
|
252
|
+
body: { token: externalToken },
|
|
253
|
+
})
|
|
254
|
+
.then(response => {
|
|
255
|
+
if (response.token) {
|
|
256
|
+
checkIAMToken(response.token);
|
|
257
|
+
}
|
|
258
|
+
})
|
|
259
|
+
.catch(reason => {
|
|
260
|
+
console.debug('Can not login with token.', token, reason);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
if (token) {
|
|
264
|
+
console.log('Logged in with token and external token.');
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
console.debug('Failed to login with token and no external token available.');
|
|
268
|
+
}
|
|
269
|
+
// Start the credits poll in any case after trying to validate the user token.
|
|
270
|
+
creditsPoll.start();
|
|
271
|
+
// Force a refresh when the user comeback to the application tab
|
|
272
|
+
// Useful for checkout platform redirecting to another tab to add credits.
|
|
273
|
+
if (typeof document !== 'undefined') {
|
|
274
|
+
document.addEventListener('visibilitychange', () => {
|
|
275
|
+
if (!document.hidden && iamStore.getState().user?.id) {
|
|
276
|
+
creditsPoll.refresh();
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
});
|
|
284
281
|
// Connect the core store with the iam store.
|
|
285
282
|
coreStore.subscribe((state, prevState) => {
|
|
286
283
|
if (state.configuration?.iamRunUrl &&
|
|
@@ -53,7 +53,11 @@ export const runtimesStore = createStore((set, get) => {
|
|
|
53
53
|
*/
|
|
54
54
|
addRuntimeModel: (model) => {
|
|
55
55
|
const kernels = get().runtimeModels;
|
|
56
|
-
|
|
56
|
+
// TODO
|
|
57
|
+
// We need to review the IRuntimeModel/IRuntimePod/Kernel.IModel and their id/uid handling.
|
|
58
|
+
// The id is the kernel id, which is no more always present for some reasons.
|
|
59
|
+
// So we need to also check the uid of the model.
|
|
60
|
+
const index = kernels.findIndex(m => (model.id === m.id || model.uid === m.uid) ?? -1);
|
|
57
61
|
if (index < 0) {
|
|
58
62
|
set({ runtimeModels: [...kernels, model] });
|
|
59
63
|
}
|
|
@@ -33,19 +33,14 @@ export function DatalayerThemeProvider(props) {
|
|
|
33
33
|
: 'light');
|
|
34
34
|
}
|
|
35
35
|
if (inJupyterLab) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
catch (e) {
|
|
48
|
-
console.error('Error while setting the theme', e);
|
|
36
|
+
const themeManager = jupyterLabAdapter?.service('@jupyterlab/apputils-extension:themes');
|
|
37
|
+
console.log('---------DLA', inJupyterLab, jupyterLabAdapter, themeManager);
|
|
38
|
+
if (themeManager) {
|
|
39
|
+
updateColorMode(themeManager);
|
|
40
|
+
themeManager.themeChanged.connect(updateColorMode);
|
|
41
|
+
return () => {
|
|
42
|
+
themeManager.themeChanged.disconnect(updateColorMode);
|
|
43
|
+
};
|
|
49
44
|
}
|
|
50
45
|
}
|
|
51
46
|
else {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
declare class Logger {
|
|
2
|
+
private scope;
|
|
3
|
+
private isDevelopment;
|
|
4
|
+
constructor(scope: string);
|
|
5
|
+
private log;
|
|
6
|
+
debug(message: string, ...args: any[]): void;
|
|
7
|
+
info(message: string, ...args: any[]): void;
|
|
8
|
+
warn(message: string, ...args: any[]): void;
|
|
9
|
+
error(message: string, ...args: any[]): void;
|
|
10
|
+
}
|
|
11
|
+
export declare function createLogger(scope: string): Logger;
|
|
12
|
+
export declare const coreLogger: Logger;
|
|
13
|
+
export declare const apiLogger: Logger;
|
|
14
|
+
export declare const stateLogger: Logger;
|
|
15
|
+
export declare const configLogger: Logger;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
class Logger {
|
|
6
|
+
scope;
|
|
7
|
+
isDevelopment;
|
|
8
|
+
constructor(scope) {
|
|
9
|
+
this.scope = scope;
|
|
10
|
+
this.isDevelopment = process.env.NODE_ENV === 'development';
|
|
11
|
+
}
|
|
12
|
+
log(level, message, ...args) {
|
|
13
|
+
if (!this.isDevelopment && level === 'debug') {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const timestamp = new Date().toISOString();
|
|
17
|
+
const prefix = `[${timestamp}] [${this.scope}] [${level.toUpperCase()}]`;
|
|
18
|
+
switch (level) {
|
|
19
|
+
case 'debug':
|
|
20
|
+
console.debug(prefix, message, ...args);
|
|
21
|
+
break;
|
|
22
|
+
case 'info':
|
|
23
|
+
console.info(prefix, message, ...args);
|
|
24
|
+
break;
|
|
25
|
+
case 'warn':
|
|
26
|
+
console.warn(prefix, message, ...args);
|
|
27
|
+
break;
|
|
28
|
+
case 'error':
|
|
29
|
+
console.error(prefix, message, ...args);
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
debug(message, ...args) {
|
|
34
|
+
this.log('debug', message, ...args);
|
|
35
|
+
}
|
|
36
|
+
info(message, ...args) {
|
|
37
|
+
this.log('info', message, ...args);
|
|
38
|
+
}
|
|
39
|
+
warn(message, ...args) {
|
|
40
|
+
this.log('warn', message, ...args);
|
|
41
|
+
}
|
|
42
|
+
error(message, ...args) {
|
|
43
|
+
this.log('error', message, ...args);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export function createLogger(scope) {
|
|
47
|
+
return new Logger(scope);
|
|
48
|
+
}
|
|
49
|
+
export const coreLogger = createLogger('core');
|
|
50
|
+
export const apiLogger = createLogger('api');
|
|
51
|
+
export const stateLogger = createLogger('state');
|
|
52
|
+
export const configLogger = createLogger('config');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datalayer/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"workspaces": [
|
|
6
6
|
".",
|
|
@@ -47,13 +47,20 @@
|
|
|
47
47
|
"dev": "vite",
|
|
48
48
|
"build": "gulp resources-to-lib && tsc -b && vite build",
|
|
49
49
|
"build:lib": "gulp resources-to-lib && tsc -b",
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
50
|
+
"build:nextjs": "npm run build --workspace=nextjs-notebook-example",
|
|
51
|
+
"build:electron": "npm run build --workspace=@datalayer/electron-example",
|
|
52
|
+
"build:examples": "npm run build:nextjs && npm run build:electron",
|
|
53
|
+
"build:all": "npm run build && npm run build:examples",
|
|
54
|
+
"lint": "eslint . --quiet",
|
|
55
|
+
"lint:fix": "eslint . --fix",
|
|
56
|
+
"lint:all": "npm run lint && npm run lint --workspaces --if-present",
|
|
57
|
+
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,css,json,md}\" \"examples/**/*.{js,jsx,ts,tsx,css,json,md,mjs}\"",
|
|
58
|
+
"format:check": "prettier --check \"src/**/*.{js,jsx,ts,tsx,css,json,md}\" \"examples/**/*.{js,jsx,ts,tsx,css,json,md,mjs}\"",
|
|
59
|
+
"format:all": "npm run format && npm run format --workspaces --if-present",
|
|
60
|
+
"type-check": "tsc --noEmit",
|
|
61
|
+
"type-check:all": "npm run type-check && npm run type-check --workspaces --if-present",
|
|
62
|
+
"check": "npm run format:check && npm run lint:all && npm run type-check:all",
|
|
63
|
+
"check:fix": "npm run format:all && npm run lint:fix && npm run type-check:all",
|
|
57
64
|
"preview": "vite preview",
|
|
58
65
|
"start": "vite",
|
|
59
66
|
"clean": "rimraf lib tsconfig.tsbuildinfo",
|
|
@@ -70,6 +77,7 @@
|
|
|
70
77
|
"watch:lib:src": "tsc -b -w",
|
|
71
78
|
"example": "vite --config vite.examples.config.ts",
|
|
72
79
|
"example:nextjs": "npm run dev --workspace=nextjs-notebook-example",
|
|
80
|
+
"example:electron": "cd examples/electron && npm run dev",
|
|
73
81
|
"prepare": "husky"
|
|
74
82
|
},
|
|
75
83
|
"dependencies": {
|