handoff-app 2.0.0-1 → 2.0.0-3
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 +170 -91
- package/config/docs/foundations/colors/guidelines.md +9 -0
- package/config/docs/foundations/effects/guidelines.md +9 -0
- package/config/docs/foundations/effects/resources.md +9 -0
- package/config/docs/foundations/grid.md +10 -0
- package/config/docs/foundations/icons/guidelines.md +9 -0
- package/config/docs/foundations/images/guidelines.md +9 -0
- package/config/docs/foundations/images/resources.md +9 -0
- package/config/docs/foundations/images.md +10 -0
- package/config/docs/foundations/logo/resources.md +9 -0
- package/config/docs/foundations/typography/guidelines.md +9 -0
- package/config/docs/foundations/typography/resources.md +9 -0
- package/config/docs/foundations.md +5 -3
- package/dist/app-builder/build.js +109 -49
- package/dist/app-builder/client-config.js +13 -12
- package/dist/app-builder/paths.js +7 -9
- package/dist/app-builder/static-export.js +6 -6
- package/dist/app-builder/vercel-output.js +2 -2
- package/dist/artifacts/url.d.ts +7 -7
- package/dist/artifacts/url.js +7 -7
- package/dist/cli/auth/browser.d.ts +2 -0
- package/dist/cli/auth/browser.js +33 -0
- package/dist/cli/auth/device.d.ts +11 -0
- package/dist/cli/auth/device.js +155 -0
- package/dist/cli/auth/store.d.ts +29 -0
- package/dist/cli/auth/store.js +146 -0
- package/dist/cli/create.js +48 -3
- package/dist/cli/scaffold.js +20 -154
- package/dist/commands/checkout/index.d.ts +6 -6
- package/dist/commands/checkout/index.js +8 -12
- package/dist/commands/index.js +4 -0
- package/dist/commands/login/index.d.ts +8 -0
- package/dist/commands/login/index.js +53 -0
- package/dist/commands/logout/index.d.ts +7 -0
- package/dist/commands/logout/index.js +54 -0
- package/dist/commands/publish/index.d.ts +6 -5
- package/dist/commands/publish/index.js +8 -11
- package/dist/commands/utils.d.ts +6 -0
- package/dist/commands/utils.js +9 -1
- package/dist/config/entries.d.ts +47 -0
- package/dist/config/entries.js +162 -0
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.js +5 -1
- package/dist/config/runtime.d.ts +9 -9
- package/dist/config/runtime.js +15 -15
- package/dist/index.d.ts +12 -8
- package/dist/index.js +26 -10
- package/dist/registry/asset-storage/resolve.d.ts +1 -1
- package/dist/registry/asset-storage/resolve.js +1 -1
- package/dist/registry/auth/access-tokens.d.ts +46 -0
- package/dist/registry/auth/access-tokens.js +165 -0
- package/dist/registry/auth/action-tokens.d.ts +66 -0
- package/dist/registry/auth/action-tokens.js +178 -0
- package/dist/registry/auth/crypto.d.ts +4 -0
- package/dist/registry/auth/crypto.js +16 -0
- package/dist/registry/auth/database.d.ts +7 -0
- package/dist/registry/auth/database.js +22 -0
- package/dist/registry/auth/device-authorizations.d.ts +51 -0
- package/dist/registry/auth/device-authorizations.js +170 -0
- package/dist/registry/auth/gravatar.d.ts +1 -0
- package/dist/registry/auth/gravatar.js +8 -0
- package/dist/registry/auth/index.d.ts +11 -0
- package/dist/registry/auth/index.js +72 -0
- package/dist/registry/auth/installation.d.ts +42 -0
- package/dist/registry/auth/installation.js +105 -0
- package/dist/registry/auth/password.d.ts +12 -0
- package/dist/registry/auth/password.js +58 -0
- package/dist/registry/auth/rate-limits.d.ts +22 -0
- package/dist/registry/auth/rate-limits.js +75 -0
- package/dist/registry/auth/types.d.ts +26 -0
- package/dist/registry/auth/types.js +9 -0
- package/dist/registry/auth/user-record.d.ts +13 -0
- package/dist/registry/auth/user-record.js +16 -0
- package/dist/registry/auth/users.d.ts +31 -0
- package/dist/registry/auth/users.js +135 -0
- package/dist/registry/auth/validation.d.ts +3 -0
- package/dist/registry/auth/validation.js +28 -0
- package/dist/registry/checkout/assets.js +4 -2
- package/dist/registry/checkout/index.d.ts +11 -6
- package/dist/registry/checkout/index.js +159 -30
- package/dist/registry/checkout/tokens.js +4 -2
- package/dist/registry/client.d.ts +2 -1
- package/dist/registry/client.js +8 -0
- package/dist/registry/connection.d.ts +9 -0
- package/dist/registry/connection.js +29 -1
- package/dist/registry/db/client.d.ts +9 -9
- package/dist/registry/db/client.js +7 -7
- package/dist/registry/db/driver.d.ts +46 -0
- package/dist/registry/db/{adapter.js → driver.js} +22 -19
- package/dist/registry/db/index.d.ts +2 -2
- package/dist/registry/db/index.js +15 -9
- package/dist/registry/db/migrate.d.ts +1 -1
- package/dist/registry/db/migrate.js +5 -5
- package/dist/registry/db/schema.d.ts +2521 -750
- package/dist/registry/db/schema.js +126 -1
- package/dist/registry/publish/assets.js +1 -1
- package/dist/registry/publish/errors.js +3 -3
- package/dist/registry/publish/index.d.ts +9 -2
- package/dist/registry/publish/index.js +96 -10
- package/dist/registry/publish/tokens.js +1 -1
- package/dist/registry/transfer.d.ts +13 -0
- package/dist/types/config.d.ts +11 -4
- package/docs/cli.md +4 -0
- package/drizzle/0005_add_registry_auth.sql +95 -0
- package/drizzle/meta/0005_snapshot.json +1926 -0
- package/drizzle/meta/_journal.json +8 -1
- package/drizzle.config.ts +1 -1
- package/package.json +5 -4
- package/src/app/components/Auth/AccountLayout.tsx +87 -0
- package/src/app/components/Auth/AuthControls.tsx +99 -0
- package/src/app/components/Auth/AuthShell.tsx +73 -0
- package/src/app/components/Auth/RegistryInstallGate.tsx +23 -0
- package/src/app/components/Auth/RegistrySessionProvider.tsx +15 -0
- package/src/app/components/Auth/api.ts +20 -0
- package/src/app/components/Foundations/DisplayLogo.tsx +3 -3
- package/src/app/components/Layout/Header.tsx +2 -0
- package/src/app/components/Navigation/MobileNav.tsx +2 -0
- package/src/app/lib/auth/api.ts +113 -0
- package/src/app/lib/auth/config.ts +175 -0
- package/src/app/lib/auth/email.ts +34 -0
- package/src/app/lib/docs-api/runtime-config.ts +10 -24
- package/src/app/lib/registry-api/auth.ts +29 -46
- package/src/app/lib/registry-api/errors.ts +2 -2
- package/src/app/lib/registry-api/handler.ts +19 -14
- package/src/app/lib/registry-api/token-transfer.ts +1 -1
- package/src/app/lib/registry-api/transfer.ts +31 -2
- package/src/app/lib/registry-connection.ts +2 -2
- package/src/app/next.config.mjs +13 -4
- package/src/app/pages/_app.tsx +10 -4
- package/src/app/pages/account/index.tsx +153 -0
- package/src/app/pages/account/tokens.tsx +230 -0
- package/src/app/pages/account/users.tsx +310 -0
- package/src/app/pages/api/account/index.ts +26 -0
- package/src/app/pages/api/account/tokens/[id].ts +17 -0
- package/src/app/pages/api/account/tokens/index.ts +44 -0
- package/src/app/pages/api/admin/users/[id]/[action].ts +89 -0
- package/src/app/pages/api/admin/users/index.ts +53 -0
- package/src/app/pages/api/auth/[...nextauth].ts +4 -0
- package/src/app/pages/api/auth/request-reset.ts +53 -0
- package/src/app/pages/api/auth/reset-password.ts +29 -0
- package/src/app/pages/api/install.ts +102 -0
- package/src/app/pages/api/oauth/device/approve.ts +23 -0
- package/src/app/pages/api/oauth/device/index.ts +41 -0
- package/src/app/pages/api/oauth/revoke.ts +24 -0
- package/src/app/pages/api/oauth/token.ts +31 -0
- package/src/app/pages/api/registry/health.ts +4 -3
- package/src/app/pages/api/registry/transfer/[kind]/[id].ts +3 -3
- package/src/app/pages/api/registry/transfer/[kind]/index.ts +21 -0
- package/src/app/pages/api/registry/transfer/assets/[collection].ts +2 -1
- package/src/app/pages/api/registry/transfer/assets/blobs/[hash].ts +2 -1
- package/src/app/pages/api/registry/transfer/tokens/[...setId].ts +2 -2
- package/src/app/pages/cli/device.tsx +143 -0
- package/src/app/pages/foundations/icons/index.tsx +2 -2
- package/src/app/pages/foundations/icons/{[name] → library/[name]}/index.tsx +6 -6
- package/src/app/pages/install.tsx +336 -0
- package/src/app/pages/login.tsx +117 -0
- package/src/app/pages/reset-password.tsx +159 -0
- package/src/app/types/next-auth.d.ts +27 -0
- package/src/app-builder/build.ts +96 -59
- package/src/app-builder/client-config.ts +12 -11
- package/src/app-builder/paths.ts +7 -9
- package/src/app-builder/static-export.ts +6 -6
- package/src/app-builder/vercel-output.ts +2 -2
- package/src/artifacts/url.ts +7 -7
- package/src/cli/auth/browser.ts +29 -0
- package/src/cli/auth/device.ts +181 -0
- package/src/cli/auth/store.ts +137 -0
- package/src/cli/create.ts +58 -3
- package/src/cli/scaffold.ts +20 -197
- package/src/commands/checkout/index.ts +10 -15
- package/src/commands/index.ts +4 -0
- package/src/commands/login/index.ts +48 -0
- package/src/commands/logout/index.ts +51 -0
- package/src/commands/publish/index.ts +10 -14
- package/src/commands/utils.ts +12 -0
- package/src/config/entries.ts +175 -0
- package/src/config/index.ts +4 -0
- package/src/config/runtime.ts +15 -14
- package/src/index.ts +26 -12
- package/src/registry/asset-storage/resolve.ts +1 -1
- package/src/registry/auth/access-tokens.ts +198 -0
- package/src/registry/auth/action-tokens.ts +225 -0
- package/src/registry/auth/crypto.ts +13 -0
- package/src/registry/auth/database.ts +15 -0
- package/src/registry/auth/device-authorizations.ts +223 -0
- package/src/registry/auth/gravatar.ts +6 -0
- package/src/registry/auth/index.ts +89 -0
- package/src/registry/auth/installation.ts +117 -0
- package/src/registry/auth/password.ts +46 -0
- package/src/registry/auth/rate-limits.ts +84 -0
- package/src/registry/auth/types.ts +34 -0
- package/src/registry/auth/user-record.ts +25 -0
- package/src/registry/auth/users.ts +145 -0
- package/src/registry/auth/validation.ts +24 -0
- package/src/registry/checkout/assets.ts +4 -2
- package/src/registry/checkout/index.ts +182 -30
- package/src/registry/checkout/tokens.ts +4 -2
- package/src/registry/client.ts +7 -1
- package/src/registry/connection.ts +23 -0
- package/src/registry/db/client.ts +12 -12
- package/src/registry/db/{adapter.ts → driver.ts} +24 -21
- package/src/registry/db/index.ts +16 -4
- package/src/registry/db/migrate.ts +5 -5
- package/src/registry/db/schema.ts +174 -1
- package/src/registry/publish/assets.ts +1 -1
- package/src/registry/publish/errors.ts +3 -3
- package/src/registry/publish/index.ts +108 -10
- package/src/registry/publish/tokens.ts +1 -1
- package/src/registry/transfer.ts +14 -0
- package/src/types/config.ts +11 -4
- package/templates/default/README.md.tpl +58 -1
- package/templates/default/gitignore.tpl +19 -3
- package/templates/default/handoff.config.js.tpl +13 -0
- package/templates/default/handoff.config.ts.tpl +13 -0
- package/templates/default/handoff.config.with-examples.js.tpl +13 -0
- package/templates/default/handoff.config.with-examples.ts.tpl +13 -0
- package/templates/default/package.json.tpl +2 -1
- package/templates/default/vercel.json.tpl +4 -0
- package/dist/registry/db/adapter.d.ts +0 -43
|
@@ -1,4 +1,27 @@
|
|
|
1
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
|
+
};
|
|
2
25
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
26
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
27
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -13,6 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
13
36
|
};
|
|
14
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
38
|
exports.devApp = exports.watchApp = exports.getRegistryBuildOutputPath = exports.HandoffBuildError = exports.DEFAULT_BUILD_TARGET = void 0;
|
|
39
|
+
const p = __importStar(require("@clack/prompts"));
|
|
16
40
|
const cross_spawn_1 = __importDefault(require("cross-spawn"));
|
|
17
41
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
18
42
|
const path_1 = __importDefault(require("path"));
|
|
@@ -21,7 +45,7 @@ const patterns_1 = require("../pipeline/patterns");
|
|
|
21
45
|
const builder_1 = __importDefault(require("../transformers/preview/component/builder"));
|
|
22
46
|
const css_1 = require("../transformers/preview/component/css");
|
|
23
47
|
const javascript_1 = require("../transformers/preview/component/javascript");
|
|
24
|
-
const
|
|
48
|
+
const driver_1 = require("../registry/db/driver");
|
|
25
49
|
const resolve_1 = require("../registry/asset-storage/resolve");
|
|
26
50
|
const logger_1 = require("../utils/logger");
|
|
27
51
|
const client_config_1 = require("./client-config");
|
|
@@ -78,6 +102,51 @@ const assertStaticBuildAllowed = (handoff) => {
|
|
|
78
102
|
'or package the registry app with `handoff-app build --target registry`.');
|
|
79
103
|
}
|
|
80
104
|
};
|
|
105
|
+
/**
|
|
106
|
+
* Run the Next.js production build while keeping its delayed output out of the successful CLI flow.
|
|
107
|
+
* Captured output is replayed only when the child fails so compiler diagnostics remain available.
|
|
108
|
+
*/
|
|
109
|
+
const runNextBuild = (appPath, target, errorLabel) => __awaiter(void 0, void 0, void 0, function* () {
|
|
110
|
+
const spinner = p.spinner();
|
|
111
|
+
spinner.start('Building Next.js app...');
|
|
112
|
+
const stdout = [];
|
|
113
|
+
const stderr = [];
|
|
114
|
+
yield new Promise((resolve, reject) => {
|
|
115
|
+
var _a, _b;
|
|
116
|
+
const buildProcess = (0, cross_spawn_1.default)('npx', ['next', 'build'], {
|
|
117
|
+
cwd: appPath,
|
|
118
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
119
|
+
env: Object.assign(Object.assign({}, process.env), { NODE_ENV: 'production', HANDOFF_BUILD_TARGET: target }),
|
|
120
|
+
});
|
|
121
|
+
(_a = buildProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (chunk) => stdout.push(Buffer.from(chunk)));
|
|
122
|
+
(_b = buildProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (chunk) => stderr.push(Buffer.from(chunk)));
|
|
123
|
+
let settled = false;
|
|
124
|
+
const fail = (message) => {
|
|
125
|
+
if (settled)
|
|
126
|
+
return;
|
|
127
|
+
settled = true;
|
|
128
|
+
spinner.stop('Next.js app build failed');
|
|
129
|
+
logger_1.Logger.childProcessBuffer(Buffer.concat(stdout));
|
|
130
|
+
logger_1.Logger.childProcessBuffer(Buffer.concat(stderr));
|
|
131
|
+
reject(new Error(message));
|
|
132
|
+
};
|
|
133
|
+
buildProcess.once('error', (error) => {
|
|
134
|
+
fail(`${errorLabel} failed to start\nSpawn error: ${error.message}`);
|
|
135
|
+
});
|
|
136
|
+
buildProcess.once('close', (code, signal) => {
|
|
137
|
+
if (settled)
|
|
138
|
+
return;
|
|
139
|
+
if (code === 0) {
|
|
140
|
+
settled = true;
|
|
141
|
+
spinner.stop('Next.js app built successfully');
|
|
142
|
+
resolve();
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const reason = signal ? ` because it was terminated by signal ${signal}` : ` with exit code ${code}`;
|
|
146
|
+
fail(`${errorLabel} failed${reason}`);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
81
150
|
/**
|
|
82
151
|
* Output directory for the packaged registry app. Lives under the configurable sites output
|
|
83
152
|
* directory (`sitesOutputDirectory`, default `out`) alongside the static target's `out/<projectId>`
|
|
@@ -147,9 +216,8 @@ const initializeProjectApp = (handoff_1, ...args_1) => __awaiter(void 0, [handof
|
|
|
147
216
|
// Resolved runtime mode + registry connection inputs (names only) baked into the Next bundle so the
|
|
148
217
|
// deployed registry app resolves its mode/DB env-var name without any build-machine filesystem.
|
|
149
218
|
const escapedRuntimeMode = escapeForSingleQuotedJsString(runtimeMode);
|
|
150
|
-
const
|
|
151
|
-
const escapedDatabaseUrlEnv = escapeForSingleQuotedJsString((0,
|
|
152
|
-
const escapedApiTokenEnv = escapeForSingleQuotedJsString((0, adapter_1.resolveApiTokenEnv)(handoff.config));
|
|
219
|
+
const escapedRegistryDriver = escapeForSingleQuotedJsString((0, driver_1.resolveRegistryDriver)(handoff.config));
|
|
220
|
+
const escapedDatabaseUrlEnv = escapeForSingleQuotedJsString((0, driver_1.resolveDatabaseUrlEnv)(handoff.config));
|
|
153
221
|
// Asset storage selection baked (provider + module + env-var names + non-secret options JSON).
|
|
154
222
|
const assetStorage = (0, resolve_1.resolveAssetStorageFromConfig)(handoff.config);
|
|
155
223
|
const escapedAssetStorageAdapter = escapeForSingleQuotedJsString(assetStorage.adapterKind);
|
|
@@ -165,9 +233,8 @@ const initializeProjectApp = (handoff_1, ...args_1) => __awaiter(void 0, [handof
|
|
|
165
233
|
'%HANDOFF_EXPORT_PATH%': escapedExportPath,
|
|
166
234
|
'%HANDOFF_WEBSOCKET_PORT%': escapedWebsocketPort,
|
|
167
235
|
'%HANDOFF_RUNTIME_MODE%': escapedRuntimeMode,
|
|
168
|
-
'%
|
|
236
|
+
'%HANDOFF_REGISTRY_DRIVER%': escapedRegistryDriver,
|
|
169
237
|
'%HANDOFF_REGISTRY_DATABASE_URL_ENV%': escapedDatabaseUrlEnv,
|
|
170
|
-
'%HANDOFF_REGISTRY_API_TOKEN_ENV%': escapedApiTokenEnv,
|
|
171
238
|
'%HANDOFF_ASSET_STORAGE_ADAPTER%': escapedAssetStorageAdapter,
|
|
172
239
|
'%HANDOFF_ASSET_STORAGE_MODULE%': escapedAssetStorageModule,
|
|
173
240
|
'%HANDOFF_ASSET_STORAGE_TOKEN_ENV%': escapedAssetStorageTokenEnv,
|
|
@@ -216,20 +283,7 @@ const buildApp = (handoff_1, ...args_1) => __awaiter(void 0, [handoff_1, ...args
|
|
|
216
283
|
(0, static_export_1.validateReferencedArtifacts)(handoff);
|
|
217
284
|
// Build app. The static target drives Next's `output: 'export'` via HANDOFF_BUILD_TARGET so the
|
|
218
285
|
// export gate is tied to the resolved target, not to NODE_ENV.
|
|
219
|
-
|
|
220
|
-
cwd: appPath,
|
|
221
|
-
stdio: ['inherit', 'pipe', 'pipe'],
|
|
222
|
-
env: Object.assign(Object.assign({}, process.env), { NODE_ENV: 'production', HANDOFF_BUILD_TARGET: target }),
|
|
223
|
-
});
|
|
224
|
-
logger_1.Logger.childProcessBuffer(buildResult.stdout);
|
|
225
|
-
logger_1.Logger.childProcessBuffer(buildResult.stderr);
|
|
226
|
-
if (buildResult.status !== 0) {
|
|
227
|
-
let errorMsg = `Next.js build failed with exit code ${buildResult.status}`;
|
|
228
|
-
if (buildResult.error) {
|
|
229
|
-
errorMsg += `\nSpawn error: ${buildResult.error.message}`;
|
|
230
|
-
}
|
|
231
|
-
throw new Error(errorMsg);
|
|
232
|
-
}
|
|
286
|
+
yield runNextBuild(appPath, target, 'Next.js build');
|
|
233
287
|
// Reproduce the docs read API (`/api/docs/*`) as route-shaped static files in the export output,
|
|
234
288
|
// since `output: 'export'` disables the live API routes.
|
|
235
289
|
const exportDir = path_1.default.resolve(appPath, 'out');
|
|
@@ -295,7 +349,11 @@ and \`.next/static/\` already copied alongside so the server serves them.
|
|
|
295
349
|
## Required environment variables (supplied at deploy time, never baked in)
|
|
296
350
|
|
|
297
351
|
- \`${databaseUrlEnv}\` — PostgreSQL/Neon connection string, read at request time.
|
|
298
|
-
- \`
|
|
352
|
+
- \`AUTH_SECRET\` — a long, random secret used to sign browser sessions.
|
|
353
|
+
- \`AUTH_URL\` — the canonical public registry URL, including the configured base path.
|
|
354
|
+
|
|
355
|
+
Optional email delivery uses \`RESEND_API_KEY\` and \`AUTH_FROM_EMAIL\`. Without them, invitation
|
|
356
|
+
links are shown once to an administrator for manual delivery.
|
|
299
357
|
|
|
300
358
|
## Database migrations
|
|
301
359
|
|
|
@@ -306,10 +364,25 @@ pipeline) before deploying new code:
|
|
|
306
364
|
${databaseUrlEnv}="postgres://…" handoff-app db:migrate
|
|
307
365
|
\`\`\`
|
|
308
366
|
|
|
309
|
-
\`db:migrate\` reads your project config + DB env vars, resolves the same database
|
|
367
|
+
\`db:migrate\` reads your project config + DB env vars, resolves the same database driver the app was
|
|
310
368
|
built with, and applies the package-owned migration set. It runs independently of \`build\` and of
|
|
311
369
|
starting the server, so run it as a release/one-shot job.
|
|
312
370
|
|
|
371
|
+
## Complete the one-time installation
|
|
372
|
+
|
|
373
|
+
After migrations and deployment, open \`/install\` immediately and create the initial administrator.
|
|
374
|
+
The installer verifies the deployment but never changes the schema. An exposed, uninstalled registry
|
|
375
|
+
can be claimed by its first visitor, so do not leave this step unattended.
|
|
376
|
+
|
|
377
|
+
Once installation completes, sign in and authorize a workspace:
|
|
378
|
+
|
|
379
|
+
\`\`\`bash
|
|
380
|
+
handoff-app login --url https://registry.example.com
|
|
381
|
+
\`\`\`
|
|
382
|
+
|
|
383
|
+
The browser approval issues a revocable, user-owned credential. The former
|
|
384
|
+
\`HANDOFF_REGISTRY_API_TOKEN\` fixed server secret is no longer accepted.
|
|
385
|
+
|
|
313
386
|
## Run it (self-hosting)
|
|
314
387
|
|
|
315
388
|
Standalone is the documented artifact for self-hosting on a Node.js server, VPS, Docker, or
|
|
@@ -352,15 +425,15 @@ containers, custom Node servers, and other non-Vercel hosts.
|
|
|
352
425
|
});
|
|
353
426
|
/**
|
|
354
427
|
* Resolve the set of npm packages the packaged registry app must be able to `require`/`import` at
|
|
355
|
-
* runtime, given the configured database
|
|
428
|
+
* runtime, given the configured database driver. `next`/`react`/`react-dom` run the server and React
|
|
356
429
|
* runtime; `drizzle-orm` backs both the request-time DB client and the migration runner; the driver
|
|
357
|
-
* is
|
|
430
|
+
* package is driver-specific (the Neon serverless driver also needs `ws` for its Node WebSocket transport).
|
|
358
431
|
*/
|
|
359
432
|
const getRequiredRegistryRuntimeModules = (handoff) => {
|
|
360
433
|
var _a;
|
|
361
|
-
const base = ['next', 'react', 'react-dom', 'drizzle-orm'];
|
|
362
|
-
const
|
|
363
|
-
const
|
|
434
|
+
const base = ['next', 'next-auth', 'react', 'react-dom', 'drizzle-orm'];
|
|
435
|
+
const driver = (0, driver_1.resolveRegistryDriver)(handoff.config);
|
|
436
|
+
const driverModules = driver === 'neon' ? ['@neondatabase/serverless', 'ws'] : ['pg'];
|
|
364
437
|
// Asset-storage SDKs the deployed registry must be able to load at request time. The pre-packaged
|
|
365
438
|
// Vercel Blob adapter needs `@vercel/blob`; a custom adapter may declare its installed SDK
|
|
366
439
|
// package(s) via `assetStorage.options.sdkModules` so the trace/assertion covers them.
|
|
@@ -373,7 +446,7 @@ const getRequiredRegistryRuntimeModules = (handoff) => {
|
|
|
373
446
|
if (Array.isArray(sdkModules)) {
|
|
374
447
|
storage.push(...sdkModules.filter((mod) => typeof mod === 'string'));
|
|
375
448
|
}
|
|
376
|
-
return [...base, ...
|
|
449
|
+
return [...base, ...driverModules, ...storage];
|
|
377
450
|
};
|
|
378
451
|
/**
|
|
379
452
|
* Build-time guard against the empty-`node_modules` regression (the artifact would otherwise boot-fail
|
|
@@ -482,7 +555,7 @@ const assembleRegistryStandalone = (handoff, appPath, standaloneRoot, entryDir)
|
|
|
482
555
|
* - `vercel` → the Vercel Build Output API directory (`.vercel/output`) at the repo root: the
|
|
483
556
|
* traced bundle wrapped as a Node function plus CDN static assets and a route table.
|
|
484
557
|
*
|
|
485
|
-
* Mode is forced to `registry`; the database
|
|
558
|
+
* Mode is forced to `registry`; the database driver is honored from config (single-sourced so build
|
|
486
559
|
* and `db:migrate` never diverge). No connection-string value is ever baked — only env-var *names*.
|
|
487
560
|
*/
|
|
488
561
|
const buildRegistryApp = (handoff_1, ...args_1) => __awaiter(void 0, [handoff_1, ...args_1], void 0, function* (handoff, buildPackage = 'standalone') {
|
|
@@ -495,15 +568,15 @@ const buildRegistryApp = (handoff_1, ...args_1) => __awaiter(void 0, [handoff_1,
|
|
|
495
568
|
if (sourceMode !== 'registry') {
|
|
496
569
|
logger_1.Logger.info(`Source runtime.mode is "${sourceMode}"; forcing "registry" mode in the packaged registry app.`);
|
|
497
570
|
}
|
|
498
|
-
// The
|
|
571
|
+
// The driver stays single-sourced from config (never flipped by the package flag), so build and
|
|
499
572
|
// `db:migrate` always resolve the same one. `pg` uses long-lived TCP connections that pool poorly on
|
|
500
573
|
// serverless; warn (non-fatally) so the operator points DATABASE_URL at a pooled endpoint or selects
|
|
501
|
-
// the Neon
|
|
574
|
+
// the Neon driver. The connection-string value is not available at build time, so this cannot be
|
|
502
575
|
// validated — it is guidance only and never fails the build.
|
|
503
|
-
if (buildPackage === 'vercel' && (0,
|
|
504
|
-
logger_1.Logger.warn('Registry Vercel build uses the "pg"
|
|
576
|
+
if (buildPackage === 'vercel' && (0, driver_1.resolveRegistryDriver)(handoff.config) === 'pg') {
|
|
577
|
+
logger_1.Logger.warn('Registry Vercel build uses the "pg" driver (standard TCP connections), which pools poorly on ' +
|
|
505
578
|
'serverless and can exhaust the database connection limit. Point DATABASE_URL at a pooled endpoint ' +
|
|
506
|
-
'(PgBouncer / transaction pooling), or set runtime.registry.database.
|
|
579
|
+
'(PgBouncer / transaction pooling), or set runtime.registry.database.driver: "neon" for a ' +
|
|
507
580
|
'serverless-native HTTP/WebSocket driver. The build will continue.');
|
|
508
581
|
}
|
|
509
582
|
// Stage the app without any workspace-derived artifacts — the registry serves from the database.
|
|
@@ -524,20 +597,7 @@ const buildRegistryApp = (handoff_1, ...args_1) => __awaiter(void 0, [handoff_1,
|
|
|
524
597
|
yield fs_extra_1.default.remove((0, vercel_output_1.getVercelOutputPath)(handoff));
|
|
525
598
|
// Build the dynamic app. `output: 'standalone'` (driven by HANDOFF_BUILD_TARGET) traces the runtime
|
|
526
599
|
// and the selected Postgres/Neon driver into a self-contained bundle — never a static export.
|
|
527
|
-
|
|
528
|
-
cwd: appPath,
|
|
529
|
-
stdio: ['inherit', 'pipe', 'pipe'],
|
|
530
|
-
env: Object.assign(Object.assign({}, process.env), { NODE_ENV: 'production', HANDOFF_BUILD_TARGET: 'registry' }),
|
|
531
|
-
});
|
|
532
|
-
logger_1.Logger.childProcessBuffer(buildResult.stdout);
|
|
533
|
-
logger_1.Logger.childProcessBuffer(buildResult.stderr);
|
|
534
|
-
if (buildResult.status !== 0) {
|
|
535
|
-
let errorMsg = `Registry app build failed with exit code ${buildResult.status}`;
|
|
536
|
-
if (buildResult.error) {
|
|
537
|
-
errorMsg += `\nSpawn error: ${buildResult.error.message}`;
|
|
538
|
-
}
|
|
539
|
-
throw new Error(errorMsg);
|
|
540
|
-
}
|
|
600
|
+
yield runNextBuild(appPath, 'registry', 'Registry app build');
|
|
541
601
|
// Assemble the deployable package. Standalone tracing is rooted at the package (modulePath), so the
|
|
542
602
|
// app's traced files land under `<relAppDir>` inside the standalone tree; static assets and public
|
|
543
603
|
// files are not traced and must be copied alongside the server entry.
|
|
@@ -561,7 +621,7 @@ const buildRegistryApp = (handoff_1, ...args_1) => __awaiter(void 0, [handoff_1,
|
|
|
561
621
|
// `handoff-app` source/CLI (`handoff-app db:migrate`) as a controlled release step (e.g. CI/CD),
|
|
562
622
|
// never from the deployed bundle. See the generated README for the migrate-then-deploy flow.
|
|
563
623
|
const entryRelativePath = path_1.default.relative(output, serverEntry).split(path_1.default.sep).join('/');
|
|
564
|
-
yield writeRegistryDeploymentReadme(output, entryRelativePath, (0,
|
|
624
|
+
yield writeRegistryDeploymentReadme(output, entryRelativePath, (0, driver_1.resolveDatabaseUrlEnv)(handoff.config));
|
|
565
625
|
logger_1.Logger.success(`Packaged registry app at ${output} (start: \`node ${entryRelativePath}\`, migrate: \`handoff-app db:migrate\`).`);
|
|
566
626
|
});
|
|
567
627
|
/**
|
|
@@ -17,7 +17,8 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
|
17
17
|
const path_1 = __importDefault(require("path"));
|
|
18
18
|
const config_1 = require("../config");
|
|
19
19
|
const resolve_1 = require("../registry/asset-storage/resolve");
|
|
20
|
-
const
|
|
20
|
+
const connection_1 = require("../registry/connection");
|
|
21
|
+
const driver_1 = require("../registry/db/driver");
|
|
21
22
|
const paths_1 = require("./paths");
|
|
22
23
|
/**
|
|
23
24
|
* Publishes the tokens API files to the public directory.
|
|
@@ -55,7 +56,7 @@ exports.generateTokensApi = generateTokensApi;
|
|
|
55
56
|
* Build the server-only runtime config consumed by the docs read API.
|
|
56
57
|
*
|
|
57
58
|
* The browser-facing `client.config.json` carries only the resolved mode; the server additionally
|
|
58
|
-
* needs the registry connection *inputs* (selected
|
|
59
|
+
* needs the registry connection *inputs* (selected driver + the *name* of the database-URL env
|
|
59
60
|
* var) to back the registry-mode docs read API. These are non-secret — the connection-string value
|
|
60
61
|
* itself is never persisted, only resolved from the env var at request time.
|
|
61
62
|
*/
|
|
@@ -65,9 +66,8 @@ const buildServerRuntimeConfig = (config, modeOverride) => {
|
|
|
65
66
|
return {
|
|
66
67
|
mode: (_b = modeOverride !== null && modeOverride !== void 0 ? modeOverride : (_a = config === null || config === void 0 ? void 0 : config.runtime) === null || _a === void 0 ? void 0 : _a.mode) !== null && _b !== void 0 ? _b : 'workspace',
|
|
67
68
|
registry: {
|
|
68
|
-
|
|
69
|
-
databaseUrlEnv: (0,
|
|
70
|
-
apiTokenEnv: (0, adapter_1.resolveApiTokenEnv)(config),
|
|
69
|
+
driver: (0, driver_1.resolveRegistryDriver)(config),
|
|
70
|
+
databaseUrlEnv: (0, driver_1.resolveDatabaseUrlEnv)(config),
|
|
71
71
|
},
|
|
72
72
|
assetStorage: {
|
|
73
73
|
adapter: assetStorage.adapterKind,
|
|
@@ -84,17 +84,18 @@ const buildServerRuntimeConfig = (config, modeOverride) => {
|
|
|
84
84
|
* connection inputs from. Both are always written together so they never drift.
|
|
85
85
|
*/
|
|
86
86
|
const persistClientConfig = (handoff_1, ...args_1) => __awaiter(void 0, [handoff_1, ...args_1], void 0, function* (handoff, options = {}) {
|
|
87
|
+
var _a;
|
|
87
88
|
const appPath = (0, paths_1.getAppPath)(handoff);
|
|
88
89
|
// Ensure directory exists
|
|
89
90
|
yield fs_extra_1.default.ensureDir(appPath);
|
|
90
91
|
const clientConfig = (0, config_1.getClientConfig)(handoff.config);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
92
|
+
const mode = (_a = options.runtimeModeOverride) !== null && _a !== void 0 ? _a : clientConfig.runtime.mode;
|
|
93
|
+
// A registry build target is never a connected workspace, so it must bake `connected: false`
|
|
94
|
+
// even when packaged from a connected project. Otherwise a workspace is connected whenever a
|
|
95
|
+
// registry URL resolves — from config/env or a saved device login — so `login` alone surfaces
|
|
96
|
+
// the publish/checkout UI without also requiring HANDOFF_REGISTRY_URL.
|
|
97
|
+
const connected = mode === 'registry' ? false : Boolean((yield (0, connection_1.resolveAuthenticatedRegistryConnection)(handoff.config, handoff.workingPath)).url);
|
|
98
|
+
clientConfig.runtime = Object.assign(Object.assign({}, clientConfig.runtime), { mode, connected });
|
|
98
99
|
yield fs_extra_1.default.writeJson(path_1.default.resolve(appPath, 'client.config.json'), { config: clientConfig }, { spaces: 2 });
|
|
99
100
|
yield fs_extra_1.default.writeJson(path_1.default.resolve(appPath, 'runtime.server.json'), buildServerRuntimeConfig(handoff.config, options.runtimeModeOverride), { spaces: 2 });
|
|
100
101
|
});
|
|
@@ -51,13 +51,11 @@ const mirrorDirectory = (sourcePath, destinationPath) => __awaiter(void 0, void
|
|
|
51
51
|
yield fs_extra_1.default.copy(sourcePath, destinationPath, { overwrite: true });
|
|
52
52
|
});
|
|
53
53
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* makes the route resolve deterministically in workspace dev and static builds. Absent bundles (a
|
|
60
|
-
* project with no logos/icons) are skipped.
|
|
54
|
+
* Copy the fetch-produced asset bundles (`exported/<id>/{collection}.zip`) into the app's public
|
|
55
|
+
* root, where the docs read API serves them at `/api/docs/assets/{collection}/{collection}.zip`.
|
|
56
|
+
* `fetch` writes these bundles, but `build`/`start` cleans and re-stages `.handoff` without them, so
|
|
57
|
+
* the download links 404 until the next fetch. Copying them here keeps the route working in workspace
|
|
58
|
+
* dev and static builds. Projects with no logos/icons have no bundles, so those are skipped.
|
|
61
59
|
*/
|
|
62
60
|
const stageAssetDownloadBundles = (handoff, destinationPublicPath) => __awaiter(void 0, void 0, void 0, function* () {
|
|
63
61
|
const bundles = [
|
|
@@ -90,8 +88,8 @@ const syncPublicFiles = (handoff) => __awaiter(void 0, void 0, void 0, function*
|
|
|
90
88
|
});
|
|
91
89
|
yield mirrorDirectory(sourceApiPath, destinationApiPath);
|
|
92
90
|
}
|
|
93
|
-
// Stage the asset
|
|
94
|
-
//
|
|
91
|
+
// Stage the asset bundles even when the workspace has no `public/` dir; they come from the fetch
|
|
92
|
+
// output, not the workspace public tree.
|
|
95
93
|
yield stageAssetDownloadBundles(handoff, destinationPublicPath);
|
|
96
94
|
});
|
|
97
95
|
exports.syncPublicFiles = syncPublicFiles;
|
|
@@ -181,12 +181,12 @@ const materializeDocsReadModel = (handoff, outDir) => __awaiter(void 0, void 0,
|
|
|
181
181
|
});
|
|
182
182
|
exports.materializeDocsReadModel = materializeDocsReadModel;
|
|
183
183
|
/**
|
|
184
|
-
*
|
|
185
|
-
* (`api/docs/assets/{collection}/{collection}.zip`)
|
|
186
|
-
* registry mode, so the "Download" links
|
|
187
|
-
*
|
|
188
|
-
* fetch-time copy into
|
|
189
|
-
*
|
|
184
|
+
* Write the whole-collection asset bundles to the asset route
|
|
185
|
+
* (`api/docs/assets/{collection}/{collection}.zip`), the same URL the docs read API serves in
|
|
186
|
+
* registry mode, so the "Download" links work identically on a static host. The bundles come from the
|
|
187
|
+
* fetch output (`exported/<id>/{collection}.zip`); `next build` never re-creates them and the
|
|
188
|
+
* fetch-time copy into `public/` is wiped by the build's clean+restage, so we copy them here again.
|
|
189
|
+
* Projects with no logos/icons have no bundles, so those are skipped rather than treated as an error.
|
|
190
190
|
*/
|
|
191
191
|
const materializeAssetDownloads = (handoff, docsRoot) => __awaiter(void 0, void 0, void 0, function* () {
|
|
192
192
|
const bundles = [
|
|
@@ -74,7 +74,7 @@ const writeStaticVercelOutput = (handoff, exportDir) => __awaiter(void 0, void 0
|
|
|
74
74
|
});
|
|
75
75
|
exports.writeStaticVercelOutput = writeStaticVercelOutput;
|
|
76
76
|
/**
|
|
77
|
-
* Node.js runtime identifier baked into the registry function's `.vc-config.json`. The `pg`
|
|
77
|
+
* Node.js runtime identifier baked into the registry function's `.vc-config.json`. The `pg` driver
|
|
78
78
|
* (and the Neon serverless driver in its Node fallback) need the Node.js runtime, not the edge
|
|
79
79
|
* runtime — this is the required baseline. Vercel resolves the patch
|
|
80
80
|
* version within this major; pinning the major keeps the artifact stable.
|
|
@@ -102,7 +102,7 @@ const MUTABLE_REGISTRY_PAGE_SRC_ROUTE = '/[...slug]';
|
|
|
102
102
|
* data routes resolve the same way they do under the standalone server. `minimalMode:false` keeps
|
|
103
103
|
* the function doing full in-process routing (the catch-all route forwards everything here).
|
|
104
104
|
*
|
|
105
|
-
* Runtime mode,
|
|
105
|
+
* Runtime mode, driver, and the DB env-var *name* are already baked into the resolved config the
|
|
106
106
|
* standalone build wrote to `.next/required-server-files.json`; the connection-string value is still
|
|
107
107
|
* read from the named env var at request time, never baked.
|
|
108
108
|
*/
|
package/dist/artifacts/url.d.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
export declare const ARTIFACTS_ROUTE_SEGMENT = "api/docs/artifacts";
|
|
18
18
|
/** Route prefix for docs read API metadata (`.json`) reads. */
|
|
19
19
|
export declare const DOCS_ROUTE_SEGMENT = "api/docs";
|
|
20
|
-
/**
|
|
20
|
+
/** Route prefix all asset content URLs are served under, regardless of runtime mode. */
|
|
21
21
|
export declare const ASSETS_ROUTE_SEGMENT = "api/docs/assets";
|
|
22
22
|
/**
|
|
23
23
|
* Normalize a configured basePath to either an empty string or a single-leading-slash,
|
|
@@ -50,18 +50,18 @@ export declare const buildComponentDetailUrl: (id: string, basePath?: string | n
|
|
|
50
50
|
*/
|
|
51
51
|
export declare const buildPatternDetailUrl: (id: string, basePath?: string | null) => string;
|
|
52
52
|
/**
|
|
53
|
-
* Build the
|
|
53
|
+
* Build the basePath-aware content URL for a single asset in a collection
|
|
54
54
|
* (`{basePath}/api/docs/assets/{collection}/{logicalPath}`). This is the same route the docs read API
|
|
55
55
|
* serves in every runtime mode: from the database in registry mode, and from statically materialized
|
|
56
|
-
* files in a static export. Each segment is
|
|
57
|
-
*
|
|
56
|
+
* files in a static export. Each segment is encoded on its own and traversal segments are rejected,
|
|
57
|
+
* matching {@link buildArtifactUrl}.
|
|
58
58
|
*
|
|
59
59
|
* @throws If the collection or logical path has no usable segments, or contains a `.`/`..` segment.
|
|
60
60
|
*/
|
|
61
61
|
export declare const buildAssetUrl: (collection: string, logicalPath: string, basePath?: string | null) => string;
|
|
62
62
|
/**
|
|
63
|
-
* Build the
|
|
64
|
-
* (`{basePath}/api/docs/assets/{collection}/{collection}.zip`)
|
|
65
|
-
* "Download" links
|
|
63
|
+
* Build the download URL for a collection's zip bundle
|
|
64
|
+
* (`{basePath}/api/docs/assets/{collection}/{collection}.zip`). This is the default target for the
|
|
65
|
+
* docs "Download" links and can be overridden via `assets_zip_links`.
|
|
66
66
|
*/
|
|
67
67
|
export declare const buildAssetDownloadUrl: (collection: string, basePath?: string | null) => string;
|
package/dist/artifacts/url.js
CHANGED
|
@@ -20,7 +20,7 @@ exports.buildAssetDownloadUrl = exports.buildAssetUrl = exports.buildPatternDeta
|
|
|
20
20
|
exports.ARTIFACTS_ROUTE_SEGMENT = 'api/docs/artifacts';
|
|
21
21
|
/** Route prefix for docs read API metadata (`.json`) reads. */
|
|
22
22
|
exports.DOCS_ROUTE_SEGMENT = 'api/docs';
|
|
23
|
-
/**
|
|
23
|
+
/** Route prefix all asset content URLs are served under, regardless of runtime mode. */
|
|
24
24
|
exports.ASSETS_ROUTE_SEGMENT = 'api/docs/assets';
|
|
25
25
|
/**
|
|
26
26
|
* Normalize a configured basePath to either an empty string or a single-leading-slash,
|
|
@@ -87,11 +87,11 @@ exports.buildComponentDetailUrl = buildComponentDetailUrl;
|
|
|
87
87
|
const buildPatternDetailUrl = (id, basePath) => `${(0, exports.normalizeBasePath)(basePath)}/${exports.DOCS_ROUTE_SEGMENT}/patterns/${encodeURIComponent(id)}.json`;
|
|
88
88
|
exports.buildPatternDetailUrl = buildPatternDetailUrl;
|
|
89
89
|
/**
|
|
90
|
-
* Build the
|
|
90
|
+
* Build the basePath-aware content URL for a single asset in a collection
|
|
91
91
|
* (`{basePath}/api/docs/assets/{collection}/{logicalPath}`). This is the same route the docs read API
|
|
92
92
|
* serves in every runtime mode: from the database in registry mode, and from statically materialized
|
|
93
|
-
* files in a static export. Each segment is
|
|
94
|
-
*
|
|
93
|
+
* files in a static export. Each segment is encoded on its own and traversal segments are rejected,
|
|
94
|
+
* matching {@link buildArtifactUrl}.
|
|
95
95
|
*
|
|
96
96
|
* @throws If the collection or logical path has no usable segments, or contains a `.`/`..` segment.
|
|
97
97
|
*/
|
|
@@ -108,9 +108,9 @@ const buildAssetUrl = (collection, logicalPath, basePath) => {
|
|
|
108
108
|
};
|
|
109
109
|
exports.buildAssetUrl = buildAssetUrl;
|
|
110
110
|
/**
|
|
111
|
-
* Build the
|
|
112
|
-
* (`{basePath}/api/docs/assets/{collection}/{collection}.zip`)
|
|
113
|
-
* "Download" links
|
|
111
|
+
* Build the download URL for a collection's zip bundle
|
|
112
|
+
* (`{basePath}/api/docs/assets/{collection}/{collection}.zip`). This is the default target for the
|
|
113
|
+
* docs "Download" links and can be overridden via `assets_zip_links`.
|
|
114
114
|
*/
|
|
115
115
|
const buildAssetDownloadUrl = (collection, basePath) => (0, exports.buildAssetUrl)(collection, `${collection}.zip`, basePath);
|
|
116
116
|
exports.buildAssetDownloadUrl = buildAssetDownloadUrl;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.tryOpenBrowser = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
/** Fire-and-forget opening of a URL in the system browser. The printed URL remains the fallback. */
|
|
6
|
+
const tryOpenBrowser = (url) => {
|
|
7
|
+
try {
|
|
8
|
+
const { protocol } = new URL(url);
|
|
9
|
+
if (protocol !== 'http:' && protocol !== 'https:')
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
catch (_a) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const platform = process.platform;
|
|
16
|
+
// rundll32 is a plain executable rather than a shell, so spawn hands it the URL as one
|
|
17
|
+
// untouched argument. `cmd /c start` would re-parse shell metacharacters in it.
|
|
18
|
+
const command = platform === 'darwin' ? 'open' : platform === 'win32' ? 'rundll32.exe' : 'xdg-open';
|
|
19
|
+
const args = platform === 'win32' ? ['url.dll,FileProtocolHandler', url] : [url];
|
|
20
|
+
try {
|
|
21
|
+
const child = (0, child_process_1.spawn)(command, args, {
|
|
22
|
+
detached: true,
|
|
23
|
+
stdio: 'ignore',
|
|
24
|
+
windowsHide: platform === 'win32',
|
|
25
|
+
});
|
|
26
|
+
child.on('error', () => undefined);
|
|
27
|
+
child.unref();
|
|
28
|
+
}
|
|
29
|
+
catch (_b) {
|
|
30
|
+
// Headless and SSH environments can use the URL printed by the login flow.
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
exports.tryOpenBrowser = tryOpenBrowser;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type CliAuth } from './store';
|
|
2
|
+
export interface LoginOptions {
|
|
3
|
+
openBrowser?: boolean;
|
|
4
|
+
}
|
|
5
|
+
/** Complete the device grant and persist the one-time plaintext access token for this workspace. */
|
|
6
|
+
export declare const loginWithDevice: (workingPath: string, remoteUrl: string, options?: LoginOptions) => Promise<CliAuth>;
|
|
7
|
+
/**
|
|
8
|
+
* Best-effort remote revocation. The caller must clear the local credential regardless of failure.
|
|
9
|
+
* The bearer header identifies the current token while the body supports RFC 7009-style handlers.
|
|
10
|
+
*/
|
|
11
|
+
export declare const revokeAccessToken: (auth: CliAuth) => Promise<void>;
|