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 +5 -1
- package/dist/cli.js +0 -0
- package/dist/index.js +4 -1
- package/package.json +1 -1
- package/src/app/components/util/index.ts +17 -9
- package/src/index.ts +3 -0
- package/dist/commands/platform/init.d.ts +0 -6
- package/dist/commands/platform/init.js +0 -231
- package/dist/commands/platform/login.d.ts +0 -6
- package/dist/commands/platform/login.js +0 -108
- package/dist/commands/platform/logout.d.ts +0 -6
- package/dist/commands/platform/logout.js +0 -88
- package/dist/commands/platform/pull.d.ts +0 -8
- package/dist/commands/platform/pull.js +0 -112
- package/dist/commands/platform/push.d.ts +0 -8
- package/dist/commands/platform/push.js +0 -428
- package/dist/platform/client.d.ts +0 -122
- package/dist/platform/client.js +0 -228
- package/dist/platform/credentials.d.ts +0 -40
- package/dist/platform/credentials.js +0 -91
- package/dist/platform/sync.d.ts +0 -68
- package/dist/platform/sync.js +0 -349
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.
|
|
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
|
@@ -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:
|
|
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:
|
|
329
|
+
path: `${basePath}/system/tokens/foundations`,
|
|
324
330
|
menu: [
|
|
325
331
|
{
|
|
326
332
|
title: `Colors`,
|
|
327
|
-
path:
|
|
333
|
+
path: `${basePath}/system/tokens/foundations/colors`,
|
|
328
334
|
},
|
|
329
335
|
{
|
|
330
336
|
title: `Effects`,
|
|
331
|
-
path:
|
|
337
|
+
path: `${basePath}/system/tokens/foundations/effects`,
|
|
332
338
|
},
|
|
333
339
|
{
|
|
334
340
|
title: `Typography`,
|
|
335
|
-
path:
|
|
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:
|
|
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:
|
|
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:
|
|
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,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,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,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;
|