handoff-app 1.0.3-canary.9 → 1.0.4

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/Changelog.md CHANGED
@@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to
7
7
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8
8
 
9
- ## \[1.0.3] - 2026-02-17
9
+ ## \[1.0.4] - 2026-02-19
10
10
 
11
11
  Bugfixes, UX improvements to the component page, improvements to react docgen,
12
12
  a new quickstart command, and security fixes.
@@ -62,6 +62,10 @@ a new quickstart command, and security fixes.
62
62
  * Fixes a bug in webpack that made the include path lookups fail. @handoff and @/util now
63
63
  works properly.
64
64
 
65
+ ## \[1.0.3]
66
+
67
+ This this version was skipped.
68
+
65
69
  ## \[1.0.2] - 2026-01-12
66
70
 
67
71
  Bugfixes focused on developer quality of life.
package/dist/cli.js CHANGED
File without changes
package/dist/index.js CHANGED
@@ -71,12 +71,15 @@ class Handoff {
71
71
  global.handoff = this;
72
72
  }
73
73
  init(configOverride) {
74
- var _a, _b;
74
+ var _a, _b, _c;
75
75
  const config = initConfig(configOverride !== null && configOverride !== void 0 ? configOverride : {});
76
76
  this.config = config;
77
77
  this.exportsDirectory = (_a = config.exportsOutputDirectory) !== null && _a !== void 0 ? _a : this.exportsDirectory;
78
78
  this.sitesDirectory = (_b = config.sitesOutputDirectory) !== null && _b !== void 0 ? _b : this.exportsDirectory;
79
79
  [this.runtimeConfig, this._configFilePaths] = (0, exports.initRuntimeConfig)(this);
80
+ if (this.config.app.base_path && !process.env.HANDOFF_APP_BASE_PATH) {
81
+ process.env.HANDOFF_APP_BASE_PATH = (_c = this.config.app.base_path) !== null && _c !== void 0 ? _c : '';
82
+ }
80
83
  return this;
81
84
  }
82
85
  reload() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "handoff-app",
3
- "version": "1.0.3-canary.9",
3
+ "version": "1.0.4",
4
4
  "description": "Automated documentation toolchain for building client side documentation from figma",
5
5
  "author ": {
6
6
  "name": "Convertiv",
@@ -206,6 +206,9 @@ export const staticBuildMenu = () => {
206
206
  // // Contents of docs
207
207
  const docRoot = path.join(process.env.HANDOFF_MODULE_PATH ?? '', 'config/docs');
208
208
  // Get the file list
209
+ if(!fs.existsSync(docRoot)) {
210
+ return [];
211
+ }
209
212
  const files = fs.readdirSync(docRoot);
210
213
  let list = files;
211
214
  const workingPages = path.resolve(process.env.HANDOFF_WORKING_PATH ?? '', 'pages');
@@ -277,7 +280,7 @@ export const staticBuildMenu = () => {
277
280
  title: metadata.menuTitle ?? metadata.title,
278
281
  external,
279
282
  weight: metadata.weight ?? 0,
280
- path: filepath,
283
+ path: `${filepath}`,
281
284
  subSections,
282
285
  };
283
286
  }
@@ -287,6 +290,7 @@ export const staticBuildMenu = () => {
287
290
  };
288
291
 
289
292
  const staticBuildComponentMenu = (type?: boolean | string) => {
293
+ const basePath = (process.env.HANDOFF_APP_BASE_PATH ?? '').replace(/^\//, '');
290
294
  let menu = [];
291
295
  let components = fetchComponents({ includeTokens: false });
292
296
  if (typeof type === 'string' && type !== '') {
@@ -305,7 +309,7 @@ const staticBuildComponentMenu = (type?: boolean | string) => {
305
309
  if (component.name) {
306
310
  title = component.name;
307
311
  }
308
- menuGroup.menu.push({ path: `system/component/${component.id}`, title });
312
+ menuGroup.menu.push({ path: `${basePath}/system/component/${component.id}`, title });
309
313
  });
310
314
  // sort the menu group by name alphabetical
311
315
  menuGroup.menu = menuGroup.menu.sort((a, b) => a.title.localeCompare(b.title));
@@ -317,22 +321,24 @@ const staticBuildComponentMenu = (type?: boolean | string) => {
317
321
  };
318
322
 
319
323
  const staticBuildTokensMenu = () => {
324
+ const basePath = (process.env.HANDOFF_APP_BASE_PATH ?? '').replace(/^\//, '');
325
+
320
326
  const menu = [
321
327
  {
322
328
  title: `Foundations`,
323
- path: `system/tokens/foundations`,
329
+ path: `${basePath}/system/tokens/foundations`,
324
330
  menu: [
325
331
  {
326
332
  title: `Colors`,
327
- path: `system/tokens/foundations/colors`,
333
+ path: `${basePath}/system/tokens/foundations/colors`,
328
334
  },
329
335
  {
330
336
  title: `Effects`,
331
- path: `system/tokens/foundations/effects`,
337
+ path: `${basePath}/system/tokens/foundations/effects`,
332
338
  },
333
339
  {
334
340
  title: `Typography`,
335
- path: `system/tokens/foundations/typography`,
341
+ path: `${basePath}/system/tokens/foundations/typography`,
336
342
  },
337
343
  ],
338
344
  },
@@ -352,14 +358,14 @@ const staticBuildTokensMenu = () => {
352
358
  if (component.name) {
353
359
  title = component.name;
354
360
  }
355
- componentMenuItems.push({ path: `system/tokens/components/${component.id}`, title });
361
+ componentMenuItems.push({ path: `${basePath}/system/tokens/components/${component.id}`, title });
356
362
  });
357
363
  });
358
364
 
359
365
  if (componentMenuItems.length > 0) {
360
366
  menu.push({
361
367
  title: `Components`,
362
- path: `system/tokens/components`,
368
+ path: `${basePath}/system/tokens/components`,
363
369
  menu: componentMenuItems,
364
370
  });
365
371
  }
@@ -368,9 +374,11 @@ const staticBuildTokensMenu = () => {
368
374
  };
369
375
 
370
376
  const staticBuildTokenMenu = () => {
377
+ const basePath = (process.env.HANDOFF_APP_BASE_PATH ?? '').replace(/^\//, '');
378
+
371
379
  let subSections = {
372
380
  title: 'Tokens',
373
- path: 'system/tokens',
381
+ path: `${basePath}/system/tokens`,
374
382
  menu: [],
375
383
  };
376
384
  const tokens = getTokens();
package/src/index.ts CHANGED
@@ -55,6 +55,9 @@ class Handoff {
55
55
  this.exportsDirectory = config.exportsOutputDirectory ?? this.exportsDirectory;
56
56
  this.sitesDirectory = config.sitesOutputDirectory ?? this.exportsDirectory;
57
57
  [this.runtimeConfig, this._configFilePaths] = initRuntimeConfig(this);
58
+ if(this.config.app.base_path && !process.env.HANDOFF_APP_BASE_PATH) {
59
+ process.env.HANDOFF_APP_BASE_PATH = this.config.app.base_path ?? '';
60
+ }
58
61
  return this;
59
62
  }
60
63
 
@@ -1,6 +0,0 @@
1
- import { CommandModule } from 'yargs';
2
- export interface PlatformInitArgs {
3
- url?: string;
4
- }
5
- declare const command: CommandModule<{}, PlatformInitArgs>;
6
- export default command;
@@ -1,231 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
- return new (P || (P = Promise))(function (resolve, reject) {
28
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
- step((generator = generator.apply(thisArg, _arguments || [])).next());
32
- });
33
- };
34
- var __importDefault = (this && this.__importDefault) || function (mod) {
35
- return (mod && mod.__esModule) ? mod : { "default": mod };
36
- };
37
- Object.defineProperty(exports, "__esModule", { value: true });
38
- const p = __importStar(require("@clack/prompts"));
39
- const chalk_1 = __importDefault(require("chalk"));
40
- const fs_extra_1 = __importDefault(require("fs-extra"));
41
- const path_1 = __importDefault(require("path"));
42
- const client_1 = require("../../platform/client");
43
- const credentials_1 = require("../../platform/credentials");
44
- const DEFAULT_PLATFORM_URL = 'http://localhost:3000';
45
- const CONFIG_FILES = ['handoff.config.json', 'handoff.config.js', 'handoff.config.cjs'];
46
- const command = {
47
- command: 'platform:init',
48
- describe: 'Link this directory to a Handoff platform project',
49
- builder: (yargs) => {
50
- return yargs.option('url', {
51
- type: 'string',
52
- description: 'Platform URL',
53
- default: process.env.HANDOFF_PLATFORM_URL || DEFAULT_PLATFORM_URL,
54
- });
55
- },
56
- handler: (args) => __awaiter(void 0, void 0, void 0, function* () {
57
- const baseUrl = (args.url || DEFAULT_PLATFORM_URL).replace(/\/+$/, '');
58
- const workingDir = process.cwd();
59
- p.intro(chalk_1.default.bold('Handoff Platform Init'));
60
- // 1. Check login
61
- const token = (0, credentials_1.getToken)(baseUrl);
62
- if (!token) {
63
- p.cancel(`Not logged in to ${baseUrl}. Run ${chalk_1.default.cyan('handoff-app platform:login')} first.`);
64
- process.exit(1);
65
- }
66
- const spinner = p.spinner();
67
- try {
68
- // 2. Fetch project list
69
- spinner.start('Fetching projects...');
70
- const projects = yield (0, client_1.listProjects)(baseUrl, token);
71
- spinner.stop('Projects loaded.');
72
- if (projects.length === 0) {
73
- p.cancel('No projects found. Create a project in the Handoff platform first.');
74
- process.exit(1);
75
- }
76
- // 3. Interactive project selection
77
- const projectId = yield p.select({
78
- message: 'Select a project to link:',
79
- options: projects.map((proj) => ({
80
- value: proj.id,
81
- label: `${proj.name} (${proj.orgName})`,
82
- hint: `${proj.slug} — ${proj.role}`,
83
- })),
84
- });
85
- if (p.isCancel(projectId)) {
86
- p.cancel('Init cancelled.');
87
- process.exit(0);
88
- }
89
- const selectedProject = projects.find((proj) => proj.id === projectId);
90
- // 4. Check if a local config exists
91
- const existingConfigFile = CONFIG_FILES.find((file) => fs_extra_1.default.existsSync(path_1.default.resolve(workingDir, file)));
92
- if (!existingConfigFile) {
93
- // No local config — pull it from the remote project
94
- spinner.start('No local config found. Fetching config from platform...');
95
- try {
96
- const manifest = yield (0, client_1.getManifest)(baseUrl, token, selectedProject.id);
97
- // Look for handoff.config.js in the manifest
98
- const remoteConfigKey = Object.keys(manifest.files).find((key) => key === 'handoff.config.js' || key === 'handoff.config.json' || key === 'handoff.config.cjs');
99
- if (remoteConfigKey) {
100
- const content = yield (0, client_1.downloadFile)(baseUrl, token, selectedProject.id, remoteConfigKey);
101
- const localConfigPath = path_1.default.resolve(workingDir, remoteConfigKey);
102
- fs_extra_1.default.writeFileSync(localConfigPath, content);
103
- spinner.stop(`Downloaded ${remoteConfigKey} from platform.`);
104
- // Now inject the platform fields into it
105
- yield injectPlatformFields(localConfigPath, selectedProject.id, baseUrl);
106
- }
107
- else {
108
- spinner.stop('No remote config found either. Creating a new config.');
109
- // Create a minimal config with just the platform fields
110
- const configPath = path_1.default.resolve(workingDir, 'handoff.config.js');
111
- const configContent = generateMinimalConfig(selectedProject.id, baseUrl);
112
- fs_extra_1.default.writeFileSync(configPath, configContent);
113
- }
114
- }
115
- catch (err) {
116
- spinner.stop('Could not fetch remote config.');
117
- // Fall back to creating a minimal config
118
- const configPath = path_1.default.resolve(workingDir, 'handoff.config.js');
119
- const configContent = generateMinimalConfig(selectedProject.id, baseUrl);
120
- fs_extra_1.default.writeFileSync(configPath, configContent);
121
- }
122
- }
123
- else {
124
- // Local config exists — add/update platform fields
125
- const configPath = path_1.default.resolve(workingDir, existingConfigFile);
126
- yield injectPlatformFields(configPath, selectedProject.id, baseUrl);
127
- }
128
- p.note([
129
- `Project: ${chalk_1.default.bold(selectedProject.name)}`,
130
- `Organization: ${chalk_1.default.dim(selectedProject.orgName)}`,
131
- `Project ID: ${chalk_1.default.dim(selectedProject.id)}`,
132
- `Platform: ${chalk_1.default.dim(baseUrl)}`,
133
- ].join('\n'), 'Linked successfully');
134
- p.outro(`Run ${chalk_1.default.cyan('handoff-app platform:pull')} to sync files from the platform.`);
135
- }
136
- catch (err) {
137
- spinner.stop('Init failed.');
138
- p.cancel(err.message || 'Init failed');
139
- process.exit(1);
140
- }
141
- }),
142
- };
143
- /**
144
- * Inject platformProjectId and platformUrl into an existing config file.
145
- *
146
- * For JSON files, parse and re-serialize.
147
- * For JS/CJS files, use a regex approach to add/update the fields.
148
- */
149
- function injectPlatformFields(configPath, projectId, platformUrl) {
150
- return __awaiter(this, void 0, void 0, function* () {
151
- const ext = path_1.default.extname(configPath);
152
- if (ext === '.json') {
153
- const config = fs_extra_1.default.readJsonSync(configPath);
154
- config.platformProjectId = projectId;
155
- config.platformUrl = platformUrl;
156
- fs_extra_1.default.writeJsonSync(configPath, config, { spaces: 2 });
157
- }
158
- else {
159
- // JS or CJS file — read as text, inject/replace fields
160
- let content = fs_extra_1.default.readFileSync(configPath, 'utf8');
161
- content = upsertConfigField(content, 'platformProjectId', JSON.stringify(projectId));
162
- content = upsertConfigField(content, 'platformUrl', JSON.stringify(platformUrl));
163
- fs_extra_1.default.writeFileSync(configPath, content, 'utf8');
164
- }
165
- });
166
- }
167
- /**
168
- * Insert or replace a top-level field in a JS config export.
169
- * Looks for `fieldName: ...` and replaces the value, or appends
170
- * the field before the closing `};` of module.exports.
171
- */
172
- function upsertConfigField(source, fieldName, value) {
173
- // Pattern to match an existing field assignment (with optional trailing comma)
174
- const existingPattern = new RegExp(`(${fieldName}\\s*:\\s*)([^,}\\n]+)(,?)`);
175
- if (existingPattern.test(source)) {
176
- return source.replace(existingPattern, `$1${value}$3`);
177
- }
178
- // Insert before the last `};` (end of module.exports)
179
- // Handle both `module.exports = {` and `export default {` styles
180
- // Also ensure the previous line ends with a comma so the JS stays valid
181
- const closingBrace = /(\n)(};?\s*$)/;
182
- if (closingBrace.test(source)) {
183
- // Ensure the last property line before the closing brace has a trailing comma
184
- source = ensureTrailingComma(source);
185
- return source.replace(closingBrace, `$1 ${fieldName}: ${value},\n$2`);
186
- }
187
- // Fallback: just append as a comment warning
188
- return source + `\n// Added by platform:init — could not auto-inject:\n// ${fieldName}: ${value}\n`;
189
- }
190
- /**
191
- * Ensure the last property line before the closing `};` has a trailing comma.
192
- * This prevents invalid JS when appending new properties to an object literal.
193
- */
194
- function ensureTrailingComma(source) {
195
- const lines = source.split('\n');
196
- // Find the closing `};` or `}` at the start of a line (module.exports / export default)
197
- let closingIdx = -1;
198
- for (let i = lines.length - 1; i >= 0; i--) {
199
- if (/^};?\s*$/.test(lines[i])) {
200
- closingIdx = i;
201
- break;
202
- }
203
- }
204
- if (closingIdx <= 0)
205
- return source;
206
- // Walk backwards past blank lines and comment-only lines to find the last property line
207
- let lastPropIdx = closingIdx - 1;
208
- while (lastPropIdx >= 0 && (/^\s*$/.test(lines[lastPropIdx]) || /^\s*\/\//.test(lines[lastPropIdx]))) {
209
- lastPropIdx--;
210
- }
211
- if (lastPropIdx < 0)
212
- return source;
213
- // Strip inline comments for the check, but preserve them in the output
214
- const stripped = lines[lastPropIdx].replace(/\s*\/\/.*$/, '').trimEnd();
215
- if (stripped.length > 0 && !stripped.endsWith(',') && !stripped.endsWith('{')) {
216
- // Insert a comma after the last significant character, preserving any inline comment
217
- lines[lastPropIdx] = lines[lastPropIdx].replace(/(\S)(\s*(?:\/\/.*)?)$/, '$1,$2');
218
- }
219
- return lines.join('\n');
220
- }
221
- /**
222
- * Generate a minimal handoff.config.js with platform fields.
223
- */
224
- function generateMinimalConfig(projectId, platformUrl) {
225
- return `module.exports = {
226
- platformProjectId: ${JSON.stringify(projectId)},
227
- platformUrl: ${JSON.stringify(platformUrl)},
228
- };
229
- `;
230
- }
231
- exports.default = command;
@@ -1,6 +0,0 @@
1
- import { CommandModule } from 'yargs';
2
- export interface PlatformLoginArgs {
3
- url?: string;
4
- }
5
- declare const command: CommandModule<{}, PlatformLoginArgs>;
6
- export default command;
@@ -1,108 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
- return new (P || (P = Promise))(function (resolve, reject) {
28
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
- step((generator = generator.apply(thisArg, _arguments || [])).next());
32
- });
33
- };
34
- var __importDefault = (this && this.__importDefault) || function (mod) {
35
- return (mod && mod.__esModule) ? mod : { "default": mod };
36
- };
37
- Object.defineProperty(exports, "__esModule", { value: true });
38
- const p = __importStar(require("@clack/prompts"));
39
- const chalk_1 = __importDefault(require("chalk"));
40
- const child_process_1 = require("child_process");
41
- const client_1 = require("../../platform/client");
42
- const credentials_1 = require("../../platform/credentials");
43
- const DEFAULT_PLATFORM_URL = 'http://localhost:3000';
44
- const command = {
45
- command: 'platform:login',
46
- describe: 'Log in to the Handoff platform',
47
- builder: (yargs) => {
48
- return yargs.option('url', {
49
- type: 'string',
50
- description: 'Platform URL',
51
- default: process.env.HANDOFF_PLATFORM_URL || DEFAULT_PLATFORM_URL,
52
- });
53
- },
54
- handler: (args) => __awaiter(void 0, void 0, void 0, function* () {
55
- const baseUrl = (args.url || DEFAULT_PLATFORM_URL).replace(/\/+$/, '');
56
- p.intro(chalk_1.default.bold('Handoff Platform Login'));
57
- const spinner = p.spinner();
58
- try {
59
- spinner.start('Initiating authentication...');
60
- const deviceCode = yield (0, client_1.createDeviceCode)(baseUrl);
61
- spinner.stop('Authentication initiated.');
62
- // Display the user code prominently
63
- p.note([
64
- `Your one-time code: ${chalk_1.default.bold.cyan(deviceCode.userCode)}`,
65
- '',
66
- `Open this URL to sign in:`,
67
- chalk_1.default.underline(`${deviceCode.verificationUrl}`),
68
- ].join('\n'), 'Authentication');
69
- // Try to open the browser automatically
70
- openBrowser(deviceCode.verificationUrl);
71
- spinner.start('Waiting for approval (this will open in your browser)...');
72
- const result = yield (0, client_1.pollForToken)(baseUrl, deviceCode.deviceCode, deviceCode.interval, deviceCode.expiresIn);
73
- spinner.stop('Authentication approved!');
74
- // Store the token
75
- (0, credentials_1.setToken)(baseUrl, result.token, {
76
- name: result.user.name,
77
- email: result.user.email,
78
- });
79
- p.note([
80
- `Logged in as ${chalk_1.default.bold(result.user.name || result.user.email)}`,
81
- `Platform: ${chalk_1.default.dim(baseUrl)}`,
82
- ].join('\n'), 'Success');
83
- p.outro('You can now use platform:init to link a project.');
84
- }
85
- catch (err) {
86
- spinner.stop('Authentication failed.');
87
- p.cancel(err.message || 'Login failed');
88
- process.exit(1);
89
- }
90
- }),
91
- };
92
- /**
93
- * Attempt to open a URL in the default browser.
94
- * Fails silently — the user can always copy/paste the URL.
95
- */
96
- function openBrowser(url) {
97
- const command = process.platform === 'darwin'
98
- ? `open "${url}"`
99
- : process.platform === 'win32'
100
- ? `start "" "${url}"`
101
- : `xdg-open "${url}"`;
102
- (0, child_process_1.exec)(command, (err) => {
103
- if (err) {
104
- // Silently ignore — user will use the printed URL
105
- }
106
- });
107
- }
108
- exports.default = command;
@@ -1,6 +0,0 @@
1
- import { CommandModule } from 'yargs';
2
- export interface PlatformLogoutArgs {
3
- url?: string;
4
- }
5
- declare const command: CommandModule<{}, PlatformLogoutArgs>;
6
- export default command;
@@ -1,88 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
- return new (P || (P = Promise))(function (resolve, reject) {
28
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
- step((generator = generator.apply(thisArg, _arguments || [])).next());
32
- });
33
- };
34
- var __importDefault = (this && this.__importDefault) || function (mod) {
35
- return (mod && mod.__esModule) ? mod : { "default": mod };
36
- };
37
- Object.defineProperty(exports, "__esModule", { value: true });
38
- const p = __importStar(require("@clack/prompts"));
39
- const chalk_1 = __importDefault(require("chalk"));
40
- const client_1 = require("../../platform/client");
41
- const credentials_1 = require("../../platform/credentials");
42
- const DEFAULT_PLATFORM_URL = 'http://localhost:3000';
43
- const command = {
44
- command: 'platform:logout',
45
- describe: 'Log out from the Handoff platform',
46
- builder: (yargs) => {
47
- return yargs.option('url', {
48
- type: 'string',
49
- description: 'Platform URL',
50
- default: process.env.HANDOFF_PLATFORM_URL || DEFAULT_PLATFORM_URL,
51
- });
52
- },
53
- handler: (args) => __awaiter(void 0, void 0, void 0, function* () {
54
- const baseUrl = (args.url || DEFAULT_PLATFORM_URL).replace(/\/+$/, '');
55
- p.intro(chalk_1.default.bold('Handoff Platform Logout'));
56
- const token = (0, credentials_1.getToken)(baseUrl);
57
- if (!token) {
58
- p.note(`Not currently logged in to ${chalk_1.default.dim(baseUrl)}.`, 'Info');
59
- p.outro('Nothing to do.');
60
- return;
61
- }
62
- const spinner = p.spinner();
63
- try {
64
- // Revoke the token on the server (best-effort)
65
- spinner.start('Revoking token...');
66
- try {
67
- yield (0, client_1.revokeToken)(baseUrl, token);
68
- }
69
- catch (_a) {
70
- // If the server is unreachable or the token is already invalid,
71
- // still proceed to remove the local credential.
72
- }
73
- spinner.stop('Token revoked.');
74
- // Remove from local credentials store
75
- (0, credentials_1.removeToken)(baseUrl);
76
- p.note(`Logged out from ${chalk_1.default.dim(baseUrl)}.`, 'Success');
77
- p.outro('Local credentials removed.');
78
- }
79
- catch (err) {
80
- spinner.stop('Logout encountered an issue.');
81
- // Still remove the local token even if revocation fails
82
- (0, credentials_1.removeToken)(baseUrl);
83
- p.note(`Local credentials removed, but server revocation may have failed.`, 'Warning');
84
- p.outro('You are logged out locally.');
85
- }
86
- }),
87
- };
88
- exports.default = command;
@@ -1,8 +0,0 @@
1
- import { CommandModule } from 'yargs';
2
- export interface PlatformPullArgs {
3
- url?: string;
4
- force?: boolean;
5
- debug?: boolean;
6
- }
7
- declare const command: CommandModule<{}, PlatformPullArgs>;
8
- export default command;