@ptkl/toolkit 0.5.0 → 0.6.1

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.
@@ -77,7 +77,34 @@ class AppsCommand {
77
77
  const { apptype, directory, build } = options;
78
78
  const profile = util.getCurrentProfile();
79
79
  const client = new Api({ token: profile.token, host: profile.host }).app(apptype);
80
- return await client.upload(directory, build);
80
+ try {
81
+ let buffer = Buffer.alloc(0);
82
+ const bufferStream = new Writable({
83
+ write(chunk, encoding, callback) {
84
+ buffer = Buffer.concat([buffer, chunk]);
85
+ callback();
86
+ },
87
+ });
88
+ // Create tar.gz in memory
89
+ await new Promise((resolve, reject) => {
90
+ c({ gzip: true, cwd: directory }, ['.'])
91
+ .pipe(bufferStream)
92
+ .on('finish', resolve)
93
+ .on('error', reject);
94
+ });
95
+ console.log('Archive created successfully');
96
+ // Create FormData with buffer
97
+ const formData = new FormData();
98
+ const blob = new Blob([buffer], { type: 'application/gzip' });
99
+ formData.append('app', blob, 'app.tar.gz');
100
+ if (build) {
101
+ formData.append('build', 'true');
102
+ }
103
+ return await client.upload(formData);
104
+ }
105
+ catch (error) {
106
+ throw error;
107
+ }
81
108
  }
82
109
  async deploy(options) {
83
110
  const { apptype, dev_version, public_version, ref } = options;
@@ -30,83 +30,99 @@ class ForgeCommand {
30
30
  }
31
31
  async bundle(options) {
32
32
  const { path, upload } = options;
33
- const module = await import(`${path}/ptkl.config.js`);
34
- const { views, name, version, distPath, icon, type, label, permissions, } = module.default ?? {};
35
- // build manifest file
36
- const manifest = {
37
- name,
38
- version,
39
- views: {},
40
- label,
41
- icon,
42
- permissions,
43
- type: type || 'platform', // default to 'platform' if not specified
44
- };
45
- const client = Util.getClientForProfile();
46
- // get base url of the platform client
47
- const baseUrl = client.getPlatformBaseURL();
48
- const base = `${baseUrl}/luma/appservice/v1/forge/static/bundle/${name}/${version}/`;
49
- manifest.icon = `${base}/${icon}`;
50
- const buildViews = Object.keys(views).map((view) => {
51
- manifest.views[view] = `${view}.bundle.js`;
52
- return build({
53
- root: path,
54
- base,
55
- build: {
56
- rollupOptions: {
57
- input: views[view],
58
- output: {
59
- format: 'esm',
60
- entryFileNames: `[name].bundle.js`,
61
- assetFileNames: (assetInfo) => {
62
- return '[name].[ext]'; // Example: Customize the output file name format
63
- },
64
- }
65
- },
66
- },
67
- plugins: [
68
- {
69
- name: 'transform-dynamic-imports',
70
- transform(code, id) {
71
- if (code.includes('import(')) {
72
- // Transform relative dynamic imports to use full base URL
73
- const transformedCode = code.replace(/import\(['"`]\.\/locales\/([^'"`]+)['"`]\)/g, `import('https://lemon.stage.protokol.io/luma/appservice/v1/forge/static/bundle/operator/0.3.0/$1')`);
74
- return transformedCode;
33
+ // Store original working directory
34
+ const originalCwd = process.cwd();
35
+ try {
36
+ // Change to the app directory
37
+ process.chdir(path);
38
+ const module = await import(`${path}/ptkl.config.js`);
39
+ const { views, name, version, distPath, icon, type, label, permissions, } = module.default ?? {};
40
+ // build manifest file
41
+ const manifest = {
42
+ name,
43
+ version,
44
+ views: {},
45
+ label,
46
+ icon,
47
+ permissions,
48
+ type: type || 'platform', // default to 'platform' if not specified
49
+ };
50
+ const client = Util.getClientForProfile();
51
+ // get base url of the platform client
52
+ const baseUrl = client.getPlatformBaseURL();
53
+ const base = `${baseUrl}/luma/appservice/v1/forge/static/bundle/${name}/${version}/`;
54
+ manifest.icon = `${base}/${icon}`;
55
+ const buildViews = Object.keys(views).map((view) => {
56
+ manifest.views[view] = `${view}.bundle.js`;
57
+ return build({
58
+ root: path,
59
+ base,
60
+ build: {
61
+ rollupOptions: {
62
+ input: views[view],
63
+ output: {
64
+ format: 'esm',
65
+ entryFileNames: `[name].bundle.js`,
66
+ assetFileNames: (assetInfo) => {
67
+ return '[name].[ext]'; // Example: Customize the output file name format
68
+ },
69
+ manualChunks: undefined,
70
+ inlineDynamicImports: true,
75
71
  }
76
- return code;
77
- }
72
+ },
78
73
  },
79
- ]
80
- });
81
- });
82
- await Promise.allSettled(buildViews);
83
- console.log("Packaging app...");
84
- // // write manifest file
85
- const manifestPath = `${distPath}/manifest.json`;
86
- writeFileSync(manifestPath, JSON.stringify(manifest, null, 4));
87
- if (upload) {
88
- let buffer = Buffer.alloc(0);
89
- const bufferStream = new Writable({
90
- write(chunk, encoding, callback) {
91
- buffer = Buffer.concat([buffer, chunk]);
92
- callback();
93
- },
94
- });
95
- c({ gzip: true, cwd: distPath }, ['.']).pipe(bufferStream).on('finish', () => {
96
- client.forge().bundleUpload(buffer).then(() => {
97
- console.log('Bundle uploaded successfully');
74
+ plugins: [
75
+ {
76
+ name: 'transform-dynamic-imports',
77
+ generateBundle(options, bundle) {
78
+ // Transform after bundling is complete
79
+ for (const fileName in bundle) {
80
+ const chunk = bundle[fileName];
81
+ if (chunk.type === 'chunk' && chunk.code) {
82
+ // Transform dynamic imports in the final bundled code
83
+ chunk.code = chunk.code.replace(/import\(['"`]\.\/([^'"`]+)['"`]\)/g, `dynamicImport('${base}$1')`);
84
+ // Also handle relative paths without ./
85
+ chunk.code = chunk.code.replace(/import\(['"`]([^'"`\/]+\.js)['"`]\)/g, `dynamicImport('${base}$1')`);
86
+ }
87
+ }
88
+ }
89
+ },
90
+ ]
98
91
  });
99
92
  });
100
- }
101
- else {
102
- try {
103
- await c({ gzip: true, cwd: distPath, file: join(distPath, `/../bundle-${manifest.version}.tar.gz`) }, ['.']);
104
- console.log('Bundle created successfully');
93
+ await Promise.allSettled(buildViews);
94
+ console.log("Packaging app...");
95
+ // // write manifest file
96
+ const manifestPath = `${distPath}/manifest.json`;
97
+ writeFileSync(manifestPath, JSON.stringify(manifest, null, 4));
98
+ if (upload) {
99
+ let buffer = Buffer.alloc(0);
100
+ const bufferStream = new Writable({
101
+ write(chunk, encoding, callback) {
102
+ buffer = Buffer.concat([buffer, chunk]);
103
+ callback();
104
+ },
105
+ });
106
+ c({ gzip: true, cwd: distPath }, ['.']).pipe(bufferStream).on('finish', () => {
107
+ client.forge().bundleUpload(buffer).then(() => {
108
+ console.log('Bundle uploaded successfully');
109
+ });
110
+ });
105
111
  }
106
- catch (error) {
107
- throw error;
112
+ else {
113
+ try {
114
+ await c({ gzip: true, cwd: distPath, file: join(distPath, `/../bundle-${manifest.version}.tar.gz`) }, ['.']);
115
+ console.log('Bundle created successfully');
116
+ }
117
+ catch (error) {
118
+ throw error;
119
+ }
108
120
  }
109
121
  }
122
+ finally {
123
+ // Always restore the original working directory
124
+ process.chdir(originalCwd);
125
+ }
110
126
  }
111
127
  async runDev(options) {
112
128
  const { path } = options;
@@ -23,7 +23,14 @@ export default class Playground {
23
23
  console.log(`Starting playground at ${port}`);
24
24
  console.log(`SDK Version: ${this.sdkVersion}`);
25
25
  console.log(`Engine: ${this.engine}`);
26
- this.watcher = chokidar.watch([], { persistent: true, ignoreInitial: true });
26
+ // Initialize watcher with better options
27
+ this.watcher = chokidar.watch([], {
28
+ persistent: true,
29
+ ignoreInitial: true,
30
+ usePolling: false, // Set to true if still having issues on some systems
31
+ interval: 100,
32
+ binaryInterval: 300
33
+ });
27
34
  this.srcPath = "";
28
35
  }
29
36
  async build(files) {
@@ -119,53 +126,72 @@ export default class Playground {
119
126
  }
120
127
  // Start watching and listen for events
121
128
  start() {
129
+ console.log(`Adding watch path: ${this.srcPath}`);
122
130
  this.watcher.add(this.srcPath);
123
- this.wss.on("connection", async (ws) => {
124
- this.connectedClient = ws;
125
- if (this.apptype == 'public-app') {
126
- await this.auth(this.connectedClient);
127
- }
128
- this.initalWsSend(this.connectedClient);
129
- console.log("Playground connected");
130
- });
131
- const handleUpdate = async (filePath) => {
131
+ // Log watched paths for debugging
132
+ console.log(`Watched paths:`, this.watcher.getWatched());
133
+ // Define handlers outside and register them once
134
+ const handleUpdate = this.debounce(async (filePath) => {
135
+ console.log("handling update for:", filePath);
132
136
  try {
133
137
  const fileData = readFileSync(filePath, "utf-8");
134
138
  const relativeFilePath = relative(this.srcPath, filePath);
135
139
  let dist = await this.build({ [relativeFilePath]: fileData });
136
140
  if (dist) {
137
- this.connectedClient.send(JSON.stringify({ action: "update", dist }));
141
+ if (this.connectedClient) {
142
+ this.connectedClient.send(JSON.stringify({ action: "update", dist }));
143
+ }
144
+ else {
145
+ console.log("No connected client to send update.");
146
+ }
138
147
  console.log(`Updating ${relativeFilePath}`);
139
148
  }
140
- this.watcher.on("add", this.debounce(handleUpdate, 300, false));
141
- this.watcher.on("change", this.debounce(handleUpdate, 300, false));
142
- this.watcher.on("unlink", this.debounce((filePath) => {
143
- const relativeFilePath = relative(this.srcPath, filePath);
144
- this.connectedClient.send(JSON.stringify({ action: "delete", files: [relativeFilePath] }));
145
- console.log(`Deleting ${relativeFilePath}`);
146
- }, 300, false));
147
149
  }
148
150
  catch (err) {
149
151
  console.log(err.message);
150
152
  }
151
- };
153
+ }, 300, false);
154
+ const handleDelete = this.debounce((filePath) => {
155
+ const relativeFilePath = relative(this.srcPath, filePath);
156
+ this.connectedClient?.send(JSON.stringify({ action: "delete", files: [relativeFilePath] }));
157
+ console.log(`Deleting ${relativeFilePath}`);
158
+ }, 300, false);
159
+ // Register event listeners once
160
+ this.watcher.on("add", handleUpdate);
161
+ this.watcher.on("change", handleUpdate);
162
+ this.watcher.on("unlink", handleDelete);
163
+ // Add error handler for watcher
164
+ this.watcher.on("error", (error) => {
165
+ console.error("Watcher error:", error);
166
+ });
167
+ this.wss.on("connection", async (ws) => {
168
+ this.connectedClient = ws;
169
+ if (this.apptype == 'public-app') {
170
+ await this.auth(this.connectedClient);
171
+ }
172
+ this.initalWsSend(this.connectedClient);
173
+ console.log("Playground connected");
174
+ });
152
175
  }
153
176
  close() {
154
177
  this.watcher.close();
155
- console.log("Watcher closed.");
178
+ this.wss.close();
179
+ console.log("Watcher and WebSocket server closed.");
156
180
  }
157
181
  debounce(func, wait, immediate) {
158
- let timeout;
182
+ let timeout = null;
159
183
  return function (...args) {
160
184
  const callNow = immediate && !timeout;
161
- clearTimeout(timeout);
185
+ if (timeout) {
186
+ clearTimeout(timeout);
187
+ }
162
188
  timeout = setTimeout(() => {
163
189
  timeout = null;
164
190
  if (!immediate)
165
- func.apply(null, args);
191
+ func.apply(this, args);
166
192
  }, wait);
167
193
  if (callNow)
168
- func.apply(null, args);
194
+ func.apply(this, args);
169
195
  };
170
196
  }
171
197
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ptkl/toolkit",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "scripts": {
5
5
  "test": "echo \"Error: no test specified\" && exit 1",
6
6
  "build": "npx tsc",
@@ -13,7 +13,7 @@
13
13
  "type": "module",
14
14
  "dependencies": {
15
15
  "@babel/standalone": "^7.26.10",
16
- "@ptkl/sdk": "^0.9.0",
16
+ "@ptkl/sdk": "file:../protokol-sdk",
17
17
  "@types/axios": "^0.14.0",
18
18
  "@types/commander": "^2.12.2",
19
19
  "@types/js-yaml": "^4.0.9",