@datalayer/core 0.0.20 → 0.0.23

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 (44) hide show
  1. package/lib/api/iam/datasources.d.ts +86 -0
  2. package/lib/api/iam/datasources.js +185 -0
  3. package/lib/api/iam/index.d.ts +2 -0
  4. package/lib/api/iam/index.js +2 -0
  5. package/lib/api/iam/secrets.d.ts +85 -0
  6. package/lib/api/iam/secrets.js +196 -0
  7. package/lib/client/auth/storage.js +17 -62
  8. package/lib/client/base.d.ts +3 -0
  9. package/lib/client/base.js +2 -2
  10. package/lib/client/index.d.ts +82 -3
  11. package/lib/client/index.js +5 -1
  12. package/lib/client/mixins/IAMMixin.d.ts +62 -0
  13. package/lib/client/mixins/IAMMixin.js +116 -0
  14. package/lib/collaboration/DatalayerCollaboration.d.ts +1 -2
  15. package/lib/collaboration/DatalayerCollaborationProvider.d.ts +1 -1
  16. package/lib/collaboration/DatalayerCollaborationProvider.js +1 -1
  17. package/lib/hooks/useBackdrop.d.ts +2 -3
  18. package/lib/hooks/useBackdropJupyterLab.d.ts +2 -2
  19. package/lib/hooks/useCache.d.ts +3 -3
  20. package/lib/hooks/useScreenshot.d.ts +2 -3
  21. package/lib/hooks/useWindowSize.d.ts +1 -2
  22. package/lib/index.d.ts +1 -1
  23. package/lib/index.js +3 -1
  24. package/lib/models/Datasource.d.ts +170 -0
  25. package/lib/models/Datasource.js +140 -0
  26. package/lib/models/Runtime.d.ts +1 -1
  27. package/lib/models/RuntimeSnapshotDTO.d.ts +1 -1
  28. package/lib/models/RuntimeSnapshotDTO.js +1 -1
  29. package/lib/models/Secret.d.ts +159 -0
  30. package/lib/models/Secret.js +135 -0
  31. package/lib/models/SpaceDTO.d.ts +0 -11
  32. package/lib/state/substates/IAMState.d.ts +1 -1
  33. package/lib/state/substates/LayoutState.d.ts +2 -2
  34. package/lib/state/substates/NbformatState.d.ts +1 -1
  35. package/lib/utils/File.d.ts +1 -1
  36. package/lib/utils/File.js +1 -1
  37. package/lib/utils/Notebook.d.ts +5 -3
  38. package/lib/utils/Notebook.js +5 -3
  39. package/package.json +9 -8
  40. package/patches/.gitkeep +1 -0
  41. package/scripts/apply-patches.sh +44 -0
  42. package/scripts/create-patches.sh +40 -0
  43. package/scripts/fix-esm-imports.cjs +124 -0
  44. package/scripts/sync-jupyter.sh +121 -0
@@ -11,3 +11,138 @@ export const asSecret = (s) => {
11
11
  value: s.value_s,
12
12
  };
13
13
  };
14
+ // ============================================================================
15
+ // New API Types and DTO
16
+ // ============================================================================
17
+ import { validateJSON } from '../api/utils/validation';
18
+ /**
19
+ * Secret domain model for the Datalayer SDK.
20
+ * Provides state management and operations for user secrets.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * const secret = await sdk.createSecret({
25
+ * variant: 'password',
26
+ * name: 'db_password',
27
+ * description: 'Production DB password',
28
+ * value: 'my-secure-password'
29
+ * });
30
+ *
31
+ * await secret.update({ description: 'Updated description' });
32
+ * await secret.delete();
33
+ * ```
34
+ * @public
35
+ */
36
+ export class SecretDTO {
37
+ /** @internal */
38
+ _data;
39
+ _sdk;
40
+ _deleted = false;
41
+ /**
42
+ * Create a Secret instance.
43
+ * @param data - Secret data from API
44
+ * @param sdk - SDK instance
45
+ */
46
+ constructor(data, sdk) {
47
+ this._data = data;
48
+ this._sdk = sdk;
49
+ }
50
+ // ========================================================================
51
+ // Helper Methods
52
+ // ========================================================================
53
+ _checkDeleted() {
54
+ if (this._deleted) {
55
+ throw new Error(`Secret ${this._data.name_s} has been deleted and no longer exists`);
56
+ }
57
+ }
58
+ _decodeValue(encodedValue) {
59
+ try {
60
+ if (typeof Buffer !== 'undefined') {
61
+ // Node.js environment
62
+ return Buffer.from(encodedValue, 'base64').toString();
63
+ }
64
+ else {
65
+ // Browser environment
66
+ return atob(encodedValue);
67
+ }
68
+ }
69
+ catch (error) {
70
+ console.error('Failed to decode secret value:', error);
71
+ return encodedValue; // Return as-is if decode fails
72
+ }
73
+ }
74
+ // ========================================================================
75
+ // Properties
76
+ // ========================================================================
77
+ get uid() {
78
+ this._checkDeleted();
79
+ return this._data.uid;
80
+ }
81
+ get variant() {
82
+ this._checkDeleted();
83
+ return this._data.variant_s;
84
+ }
85
+ get name() {
86
+ this._checkDeleted();
87
+ return this._data.name_s;
88
+ }
89
+ get description() {
90
+ this._checkDeleted();
91
+ return this._data.description_t;
92
+ }
93
+ /** Returns decoded (plain text) secret value */
94
+ get value() {
95
+ this._checkDeleted();
96
+ return this._decodeValue(this._data.value_s);
97
+ }
98
+ // ========================================================================
99
+ // Action Methods
100
+ // ========================================================================
101
+ /**
102
+ * Update this secret.
103
+ * @param updates - Fields to update
104
+ * @returns Updated Secret instance
105
+ */
106
+ async update(updates) {
107
+ this._checkDeleted();
108
+ const updated = await this._sdk.updateSecret(this.uid, updates);
109
+ return updated;
110
+ }
111
+ /**
112
+ * Delete this secret permanently.
113
+ */
114
+ async delete() {
115
+ this._checkDeleted();
116
+ await this._sdk.deleteSecret(this.uid);
117
+ this._deleted = true;
118
+ }
119
+ // ========================================================================
120
+ // Utility Methods
121
+ // ========================================================================
122
+ /**
123
+ * Get secret data in camelCase format.
124
+ */
125
+ toJSON() {
126
+ this._checkDeleted();
127
+ const obj = {
128
+ uid: this.uid,
129
+ variant: this.variant,
130
+ name: this.name,
131
+ description: this.description,
132
+ value: this.value, // Returns decoded value
133
+ };
134
+ validateJSON(obj, 'Secret');
135
+ return obj;
136
+ }
137
+ /**
138
+ * Get raw secret data exactly as received from API.
139
+ */
140
+ rawData() {
141
+ this._checkDeleted();
142
+ return this._data;
143
+ }
144
+ toString() {
145
+ this._checkDeleted();
146
+ return `Secret(${this.name}, ${this.variant})`;
147
+ }
148
+ }
@@ -202,17 +202,6 @@ export interface GetNotebookResponse {
202
202
  message: string;
203
203
  notebook?: NotebookData;
204
204
  }
205
- /**
206
- * Request payload for creating a notebook
207
- * @interface CreateNotebookRequest
208
- */
209
- export interface CreateNotebookRequest {
210
- spaceId: string;
211
- notebookType: string;
212
- name: string;
213
- description: string;
214
- file?: File | Blob;
215
- }
216
205
  /**
217
206
  * Request payload for updating a notebook
218
207
  * @interface UpdateNotebookRequest
@@ -4,7 +4,7 @@ import type { ICredits, ICreditsReservation } from '../../models';
4
4
  * Limit to warn about low credits in milliseconds.
5
5
  */
6
6
  export declare const RESERVATION_WARNING_TIME_MS: number;
7
- type IAMProviderAuthorizationURL = string;
7
+ export type IAMProviderAuthorizationURL = string;
8
8
  export type IIAMState = {
9
9
  /**
10
10
  * User credits
@@ -6,12 +6,12 @@ export type BackdropDisplay = {
6
6
  open: boolean;
7
7
  message?: string | void;
8
8
  };
9
- type BannerDisplay = {
9
+ export type BannerDisplay = {
10
10
  message: string;
11
11
  variant: BannerDisplayVariant;
12
12
  timestamp?: Date;
13
13
  };
14
- type PortalDisplay = {
14
+ export type PortalDisplay = {
15
15
  portal: ReactPortal;
16
16
  pinned: boolean;
17
17
  };
@@ -1,4 +1,4 @@
1
- type SaveRequest = {
1
+ export type SaveRequest = {
2
2
  counter: number;
3
3
  };
4
4
  export type INbformatState = {
@@ -2,6 +2,6 @@
2
2
  * creates name of file.
3
3
  *
4
4
  * @param {string} extension
5
- * @param {string[]} parts of file name
5
+ * @param {string[]} names parts of file name
6
6
  */
7
7
  export declare const createFileName: (extension?: string, ...names: any[]) => string;
package/lib/utils/File.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * creates name of file.
7
7
  *
8
8
  * @param {string} extension
9
- * @param {string[]} parts of file name
9
+ * @param {string[]} names parts of file name
10
10
  */
11
11
  export const createFileName = (extension = '', ...names) => {
12
12
  if (!extension) {
@@ -2,9 +2,11 @@ import { JupyterLab } from '@jupyterlab/application';
2
2
  /**
3
3
  * Create a notebook
4
4
  *
5
- * @param app JupyterLab application
6
- * @param name Notebook name
7
- * @param url Notebook content URL
5
+ * @param params Configuration object
6
+ * @param params.app JupyterLab application
7
+ * @param params.name Notebook name
8
+ * @param params.url Notebook content URL
9
+ * @param params.options Additional options for notebook creation
8
10
  */
9
11
  export declare const createNotebook: ({ app, name, url, options, }: {
10
12
  app: JupyterLab;
@@ -5,9 +5,11 @@
5
5
  /**
6
6
  * Create a notebook
7
7
  *
8
- * @param app JupyterLab application
9
- * @param name Notebook name
10
- * @param url Notebook content URL
8
+ * @param params Configuration object
9
+ * @param params.app JupyterLab application
10
+ * @param params.name Notebook name
11
+ * @param params.url Notebook content URL
12
+ * @param params.options Additional options for notebook creation
11
13
  */
12
14
  export const createNotebook = async ({ app, name, url, options, }) => {
13
15
  const notebook = await app.commands.execute('notebook:create-new', options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalayer/core",
3
- "version": "0.0.20",
3
+ "version": "0.0.23",
4
4
  "type": "module",
5
5
  "workspaces": [
6
6
  ".",
@@ -22,7 +22,9 @@
22
22
  "files": [
23
23
  "lib/**/*.{css,d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
24
24
  "style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
25
- "schema/*.json"
25
+ "schema/*.json",
26
+ "patches/",
27
+ "scripts/"
26
28
  ],
27
29
  "main": "lib/index.js",
28
30
  "types": "lib/index.d.ts",
@@ -50,7 +52,7 @@
50
52
  "clean:dist": "rimraf dist",
51
53
  "clean:cache": "rimraf node_modules/.vite",
52
54
  "build": "npm run clean && gulp resources-to-lib && tsc -b && vite build",
53
- "build:lib": "npm run clean:lib && gulp resources-to-lib && tsc -b",
55
+ "build:lib": "npm run clean:lib && gulp resources-to-lib && tsc -b && node scripts/fix-esm-imports.cjs",
54
56
  "build:types": "npm run clean:lib && tsc -b",
55
57
  "build:nextjs": "npm run build --workspace=nextjs-notebook-example",
56
58
  "build:examples": "npm run build:nextjs",
@@ -81,6 +83,7 @@
81
83
  "storybook": "storybook dev -p 6006",
82
84
  "build-storybook": "storybook build",
83
85
  "typedoc": "typedoc --options typedoc.json --out docs/docs/typescript_api",
86
+ "docs": "make typedoc && make pydoc && cd docs && npm install && npm run build",
84
87
  "watch:lib": "run-p 'watch:lib:*'",
85
88
  "watch:lib:res": "gulp resources-to-lib-watch",
86
89
  "watch:lib:src": "tsc -b -w",
@@ -89,8 +92,7 @@
89
92
  "examples:vite": "VITE_DATALAYER_RUN_URL=http://localhost:8888 vite --config vite.examples.config.ts",
90
93
  "examples:nextjs": "npm run dev --workspace=nextjs-notebook-example",
91
94
  "jupyter:start": "./dev/sh/start-jupyter-server.sh",
92
- "prepare": "husky",
93
- "postinstall": "bash scripts/apply-patches.sh",
95
+ "prepare": "husky || true",
94
96
  "kill": "./dev/sh/kill.sh || true",
95
97
  "sync:jupyter": "bash scripts/sync-jupyter.sh",
96
98
  "sync:jupyter:watch": "bash scripts/sync-jupyter.sh --watch",
@@ -101,8 +103,8 @@
101
103
  },
102
104
  "dependencies": {
103
105
  "@datalayer/icons-react": "^1.0.6",
104
- "@datalayer/jupyter-lexical": "^1.0.7",
105
- "@datalayer/jupyter-react": "^2.0.0",
106
+ "@datalayer/jupyter-lexical": "^1.0.8",
107
+ "@datalayer/jupyter-react": "^2.0.2",
106
108
  "@datalayer/primer-addons": "^1.0.4",
107
109
  "@datalayer/primer-rjsf": "^1.0.1",
108
110
  "@fluentui/react": "^8.125.3",
@@ -137,7 +139,6 @@
137
139
  "@tailwindcss/vite": "^4.1.13",
138
140
  "@tanstack/react-query": "^5.90.6",
139
141
  "@toon-format/toon": "^1.3.0",
140
- "ai": "^5.0.78",
141
142
  "ansi-to-html": "^0.7.2",
142
143
  "axios": "^1.7.7",
143
144
  "boring-avatars": "^2.0.1",
@@ -0,0 +1 @@
1
+ # Patches directory for patch-package
@@ -0,0 +1,44 @@
1
+ #!/bin/bash
2
+ # Copyright (c) 2023-2025 Datalayer, Inc.
3
+ # Distributed under the terms of the Modified BSD License.
4
+
5
+ # Apply patch-package patches for jupyter packages
6
+ # This is normally run automatically via npm's postinstall hook,
7
+ # but can be run manually if needed.
8
+
9
+ set -e
10
+
11
+ # Colors for output
12
+ GREEN='\033[0;32m'
13
+ BLUE='\033[0;34m'
14
+ YELLOW='\033[1;33m'
15
+ NC='\033[0m' # No Color
16
+
17
+ # Get the script directory and project root
18
+ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
19
+ CORE_ROOT="$( cd "$SCRIPT_DIR/.." && pwd )"
20
+
21
+ cd "$CORE_ROOT"
22
+
23
+ # On Netlify or other CI with caching issues, force reinstall packages that need patches
24
+ # This ensures we have clean npm versions before applying patches
25
+ if [ "$NETLIFY" = "true" ] || [ "$CI" = "true" ]; then
26
+ echo -e "${YELLOW}šŸ”„ CI detected - force reinstalling packages that need patches...${NC}"
27
+
28
+ # Remove the specific packages that need patches
29
+ rm -rf node_modules/@datalayer/jupyter-lexical
30
+ rm -rf node_modules/@datalayer/jupyter-react
31
+
32
+ # Reinstall them fresh from npm (with --ignore-scripts to prevent infinite loop)
33
+ npm install @datalayer/jupyter-lexical @datalayer/jupyter-react --no-save --ignore-scripts
34
+ fi
35
+
36
+ echo -e "${BLUE}šŸ“ Applying patches...${NC}"
37
+
38
+ # Check if patches directory exists and has patch files
39
+ if [ -d "patches" ] && [ -n "$(ls -A patches/*.patch 2>/dev/null)" ]; then
40
+ npx patch-package
41
+ echo -e "${GREEN}āœ… Patches applied successfully${NC}"
42
+ else
43
+ echo -e "${YELLOW}ā­ļø No patches found - skipping patch application${NC}"
44
+ fi
@@ -0,0 +1,40 @@
1
+ #!/bin/bash
2
+ # Copyright (c) 2023-2025 Datalayer, Inc.
3
+ # Distributed under the terms of the Modified BSD License.
4
+
5
+ # Create patch-package patches for locally modified jupyter packages
6
+ # This script generates patches that can be committed to the repo and
7
+ # applied automatically during npm install via the postinstall hook.
8
+
9
+ set -e
10
+
11
+ # Colors for output
12
+ GREEN='\033[0;32m'
13
+ BLUE='\033[0;34m'
14
+ YELLOW='\033[1;33m'
15
+ NC='\033[0m' # No Color
16
+
17
+ echo -e "${BLUE}šŸ”§ Creating patches for jupyter packages...${NC}"
18
+
19
+ # Get the script directory and project root
20
+ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
21
+ CORE_ROOT="$( cd "$SCRIPT_DIR/.." && pwd )"
22
+
23
+ cd "$CORE_ROOT"
24
+
25
+ # First, sync the latest changes from jupyter-ui to ensure patches include all modifications
26
+ echo -e "${BLUE}šŸ”„ Syncing latest changes from jupyter-ui...${NC}"
27
+ bash "$SCRIPT_DIR/sync-jupyter.sh"
28
+
29
+ # Ensure package-lock.json exists (required by patch-package)
30
+ if [ ! -f "package-lock.json" ]; then
31
+ echo -e "${YELLOW}āš ļø No package-lock.json found. Creating one...${NC}"
32
+ npm i --package-lock-only
33
+ fi
34
+
35
+ # Create patches
36
+ echo -e "${BLUE}šŸ“ Generating patches with patch-package...${NC}"
37
+ npx patch-package @datalayer/jupyter-lexical @datalayer/jupyter-react
38
+
39
+ echo -e "${GREEN}āœ… Patches created successfully in patches/ directory${NC}"
40
+ echo -e "${BLUE}ā„¹ļø Patches will be applied automatically on 'npm install' via postinstall hook${NC}"
@@ -0,0 +1,124 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+
6
+ /**
7
+ * Post-compilation script to add .js extensions to relative imports/exports
8
+ * This makes the compiled output compatible with Node.js 22 ES modules
9
+ * while keeping the TypeScript source clean.
10
+ */
11
+
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+
15
+ const LIB_DIR = path.join(__dirname, '..', 'lib');
16
+
17
+ /**
18
+ * Recursively get all .js files in a directory
19
+ */
20
+ function getAllJsFiles(dir, fileList = []) {
21
+ const files = fs.readdirSync(dir);
22
+
23
+ files.forEach(file => {
24
+ const filePath = path.join(dir, file);
25
+ const stat = fs.statSync(filePath);
26
+
27
+ if (stat.isDirectory()) {
28
+ getAllJsFiles(filePath, fileList);
29
+ } else if (file.endsWith('.js')) {
30
+ fileList.push(filePath);
31
+ }
32
+ });
33
+
34
+ return fileList;
35
+ }
36
+
37
+ /**
38
+ * Fix imports/exports in a JavaScript file
39
+ */
40
+ function fixImportsInFile(filePath) {
41
+ let content = fs.readFileSync(filePath, 'utf8');
42
+ let modified = false;
43
+
44
+ // Get the directory containing this file for path resolution
45
+ const fileDir = path.dirname(filePath);
46
+
47
+ // Match import/export statements with relative paths that don't have extensions
48
+ // Patterns to match:
49
+ // - export * from './something'
50
+ // - export { foo } from './something'
51
+ // - import { foo } from './something'
52
+ // - import './something'
53
+ // - import type { foo } from './something'
54
+
55
+ const patterns = [
56
+ // export * as foo from './path' or '.' -> export * as foo from './path/index.js' or './index.js'
57
+ /(\bexport\s+\*\s+as\s+\w+\s+from\s+['"])(\..*?)(['"])/g,
58
+ // export * from './path' or '.' -> export * from './path.js' or './index.js'
59
+ /(\bexport\s+\*\s+from\s+['"])(\..*?)(['"])/g,
60
+ // export { ... } from './path' or '.' -> export { ... } from './path.js' or './index.js'
61
+ /(\bexport\s+\{[^}]+\}\s+from\s+['"])(\..*?)(['"])/g,
62
+ // import { ... } from './path' or '.' -> import { ... } from './path.js' or './index.js'
63
+ /(\bimport\s+\{[^}]+\}\s+from\s+['"])(\..*?)(['"])/g,
64
+ // import * as foo from './path' or '.' -> import * as foo from './path.js' or './index.js'
65
+ /(\bimport\s+\*\s+as\s+\w+\s+from\s+['"])(\..*?)(['"])/g,
66
+ // import foo from './path' or '.' -> import foo from './path.js' or './index.js'
67
+ /(\bimport\s+\w+\s+from\s+['"])(\..*?)(['"])/g,
68
+ // import './path' or '.' -> import './path.js' or './index.js'
69
+ /(\bimport\s+['"])(\..*?)(['"])/g,
70
+ // import type { ... } from './path' or '.' -> import type { ... } from './path.js' or './index.js'
71
+ /(\bimport\s+type\s+\{[^}]+\}\s+from\s+['"])(\..*?)(['"])/g,
72
+ ];
73
+
74
+ patterns.forEach(pattern => {
75
+ content = content.replace(pattern, (match, prefix, importPath, suffix) => {
76
+ // Skip if already has extension
77
+ if (importPath.match(/\.(js|ts|jsx|tsx|json|css)$/)) {
78
+ return match;
79
+ }
80
+
81
+ // Skip if it's an asset import (images, svg, fonts, etc.)
82
+ if (importPath.match(/\.(jpg|jpeg|png|gif|svg|woff|woff2|ttf|eot|ico|webp|avif|mp4|webm|ogg|mp3|wav|flac|aac|pdf|zip|tar|gz)$/)) {
83
+ return match;
84
+ }
85
+
86
+ // Resolve the import path relative to this file
87
+ const resolvedPath = path.join(fileDir, importPath);
88
+
89
+ // Check if it's a directory (needs /index.js instead of .js)
90
+ let extension = '.js';
91
+ if (fs.existsSync(resolvedPath) && fs.statSync(resolvedPath).isDirectory()) {
92
+ extension = '/index.js';
93
+ }
94
+
95
+ // Add appropriate extension
96
+ modified = true;
97
+ return `${prefix}${importPath}${extension}${suffix}`;
98
+ });
99
+ });
100
+
101
+ if (modified) {
102
+ fs.writeFileSync(filePath, content, 'utf8');
103
+ return true;
104
+ }
105
+
106
+ return false;
107
+ }
108
+
109
+ // Main execution
110
+ console.log('šŸ”§ Fixing ESM imports in compiled output...\n');
111
+
112
+ const jsFiles = getAllJsFiles(LIB_DIR);
113
+ let fixedCount = 0;
114
+
115
+ jsFiles.forEach(filePath => {
116
+ const relativePath = path.relative(LIB_DIR, filePath);
117
+ if (fixImportsInFile(filePath)) {
118
+ console.log(` āœ… Fixed: ${relativePath}`);
119
+ fixedCount++;
120
+ }
121
+ });
122
+
123
+ console.log(`\nāœ… Fixed ${fixedCount} file(s) with missing .js extensions`);
124
+ console.log(' Output is now Node.js 22 ES modules compatible\n');
@@ -0,0 +1,121 @@
1
+ #!/bin/bash
2
+ # Copyright (c) 2023-2025 Datalayer, Inc.
3
+ # Distributed under the terms of the Modified BSD License.
4
+
5
+ # Sync local jupyter-ui packages to @datalayer/core node_modules
6
+ # This script builds the local jupyter packages and copies their lib/ outputs
7
+ # into the core package's node_modules for quick testing during development.
8
+ #
9
+ # Usage:
10
+ # ./sync-jupyter.sh # Run once and exit
11
+ # ./sync-jupyter.sh --watch # Watch for changes and auto-sync
12
+
13
+ set -e
14
+
15
+ # Colors for output
16
+ GREEN='\033[0;32m'
17
+ BLUE='\033[0;34m'
18
+ YELLOW='\033[1;33m'
19
+ NC='\033[0m' # No Color
20
+
21
+ # Get the script directory and project root
22
+ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
23
+ CORE_ROOT="$( cd "$SCRIPT_DIR/.." && pwd )"
24
+ JUPYTER_UI_ROOT="$( cd "$CORE_ROOT/../jupyter-ui" && pwd )"
25
+
26
+ # Function to perform the sync
27
+ sync_packages() {
28
+ echo -e "${BLUE}šŸ”„ Syncing jupyter-ui packages to @datalayer/core...${NC}"
29
+
30
+ # Build jupyter-react FIRST (lexical depends on it)
31
+ echo -e "${BLUE}šŸ“¦ Building @datalayer/jupyter-react...${NC}"
32
+ cd "$JUPYTER_UI_ROOT/packages/react"
33
+ echo -e "${YELLOW}[DEBUG] Current directory: $(pwd)${NC}"
34
+ rm -f tsconfig.tsbuildinfo
35
+ rm -rf lib
36
+ echo -e "${YELLOW}[DEBUG] Running gulp resources-to-lib...${NC}"
37
+ npx gulp resources-to-lib
38
+ echo -e "${YELLOW}[DEBUG] Running TypeScript...${NC}"
39
+ npx tsc --noEmitOnError false
40
+ TSC_EXIT=$?
41
+ echo -e "${YELLOW}[DEBUG] TypeScript exit code: $TSC_EXIT${NC}"
42
+ echo -e "${YELLOW}[DEBUG] Checking if lib exists...${NC}"
43
+ ls -la lib 2>&1 | head -5
44
+
45
+ # Verify lib was created
46
+ if [ ! -d "lib" ]; then
47
+ echo -e "${YELLOW}āš ļø lib directory was not created by TypeScript!${NC}"
48
+ exit 1
49
+ fi
50
+ echo -e "${YELLOW}[DEBUG] lib directory verified!${NC}"
51
+
52
+ # Copy react to core's node_modules for patch-package
53
+ echo -e "${BLUE}šŸ“‹ Copying react to core/node_modules...${NC}"
54
+ cd "$CORE_ROOT"
55
+ # Only replace lib/ directory, preserving package.json, LICENSE, README.md, etc from npm
56
+ rm -rf node_modules/@datalayer/jupyter-react/lib
57
+ mkdir -p node_modules/@datalayer/jupyter-react/lib
58
+ cp -r "$JUPYTER_UI_ROOT/packages/react/lib/." node_modules/@datalayer/jupyter-react/lib/
59
+
60
+ # Now build jupyter-lexical (finds react via workspace hoisting)
61
+ echo -e "${BLUE}šŸ“¦ Building @datalayer/jupyter-lexical...${NC}"
62
+ cd "$JUPYTER_UI_ROOT/packages/lexical"
63
+ rm -f tsconfig.tsbuildinfo
64
+ rm -rf lib
65
+ echo -e "${YELLOW}[DEBUG] Running gulp resources-to-lib...${NC}"
66
+ npx gulp resources-to-lib
67
+ echo -e "${YELLOW}[DEBUG] Running TypeScript...${NC}"
68
+ npx tsc --noEmitOnError false
69
+
70
+ # Copy lexical to node_modules
71
+ echo -e "${BLUE}šŸ“‹ Copying lexical to node_modules...${NC}"
72
+ cd "$CORE_ROOT"
73
+ # Only replace lib/ directory, preserving package.json, LICENSE, README.md, etc from npm
74
+ rm -rf node_modules/@datalayer/jupyter-lexical/lib
75
+ mkdir -p node_modules/@datalayer/jupyter-lexical/lib
76
+ cp -r "$JUPYTER_UI_ROOT/packages/lexical/lib/." node_modules/@datalayer/jupyter-lexical/lib/
77
+
78
+ echo -e "${GREEN}āœ… Successfully synced at $(date +"%H:%M:%S")${NC}"
79
+ }
80
+
81
+ # Check if watch mode is requested
82
+ if [[ "$1" == "--watch" ]]; then
83
+ # Check if fswatch is installed
84
+ if ! command -v fswatch &> /dev/null; then
85
+ echo -e "${YELLOW}āš ļø fswatch not found. Installing via Homebrew...${NC}"
86
+ if command -v brew &> /dev/null; then
87
+ brew install fswatch
88
+ else
89
+ echo -e "${YELLOW}āš ļø Homebrew not found. Please install fswatch manually:${NC}"
90
+ echo -e "${YELLOW} brew install fswatch${NC}"
91
+ echo -e "${YELLOW} or visit: https://github.com/emcrisostomo/fswatch${NC}"
92
+ exit 1
93
+ fi
94
+ fi
95
+
96
+ echo -e "${BLUE}šŸ‘ļø Watch mode enabled. Monitoring jupyter-ui packages for changes...${NC}"
97
+ echo -e "${YELLOW}šŸ“ Watching:${NC}"
98
+ echo -e "${YELLOW} - $JUPYTER_UI_ROOT/packages/lexical/src${NC}"
99
+ echo -e "${YELLOW} - $JUPYTER_UI_ROOT/packages/react/src${NC}"
100
+ echo -e "${YELLOW}Press Ctrl+C to stop${NC}"
101
+ echo ""
102
+
103
+ # Initial sync
104
+ sync_packages
105
+
106
+ # Watch for changes in src directories and trigger sync
107
+ # Using fswatch with:
108
+ # -r: recursive
109
+ # -e: exclude patterns (node_modules, lib, etc.)
110
+ # -l 1: latency 1 second (debounce rapid changes)
111
+ fswatch -r -l 1 \
112
+ -e ".*" -i "\\.tsx?$" -i "\\.jsx?$" -i "\\.css$" \
113
+ "$JUPYTER_UI_ROOT/packages/lexical/src" \
114
+ "$JUPYTER_UI_ROOT/packages/react/src" | while read -r file; do
115
+ echo -e "\n${YELLOW}šŸ“ Change detected in: $(basename "$file")${NC}"
116
+ sync_packages
117
+ done
118
+ else
119
+ # Single run mode
120
+ sync_packages
121
+ fi