@payloadcms/figma 0.1.0-alpha.3 → 0.1.0-alpha.5
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/dist/api/check-project-subdomain-availability.d.ts +12 -0
- package/dist/api/check-project-subdomain-availability.js +32 -0
- package/dist/api/control-plane-error.d.ts +6 -0
- package/dist/api/control-plane-error.js +8 -0
- package/dist/api/control-plane-fetch.d.ts +7 -0
- package/dist/api/control-plane-fetch.js +37 -0
- package/dist/api/control-plane.d.ts +0 -8
- package/dist/api/control-plane.js +1 -47
- package/dist/api/create-project.d.ts +18 -0
- package/dist/api/create-project.js +37 -0
- package/dist/api/list-project-workspaces.d.ts +8 -0
- package/dist/api/list-project-workspaces.js +28 -0
- package/dist/api/project-resource-type.d.ts +1 -0
- package/dist/api/project-resource-type.js +1 -0
- package/dist/auth/headless-oauth-flow.d.ts +23 -0
- package/dist/auth/headless-oauth-flow.js +69 -0
- package/dist/auth/oauth-flow.d.ts +2 -0
- package/dist/auth/oauth-flow.js +13 -8
- package/dist/auth/token-store.d.ts +4 -1
- package/dist/auth/token-store.js +14 -0
- package/dist/auth/types.d.ts +12 -0
- package/dist/cli.d.ts +10 -1
- package/dist/cli.js +39 -4
- package/dist/commands/bootstrap.js +2 -1
- package/dist/commands/deploy.d.ts +10 -0
- package/dist/commands/deploy.js +39 -11
- package/dist/commands/detect-project-resource-type.d.ts +3 -0
- package/dist/commands/detect-project-resource-type.js +28 -0
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.js +163 -78
- package/dist/commands/login.d.ts +11 -1
- package/dist/commands/login.js +66 -4
- package/dist/commands/prompt-to-create-project/format-project-subdomain.d.ts +1 -0
- package/dist/commands/prompt-to-create-project/format-project-subdomain.js +3 -0
- package/dist/commands/prompt-to-create-project/format-unavailable-subdomain-reason.d.ts +1 -0
- package/dist/commands/prompt-to-create-project/format-unavailable-subdomain-reason.js +7 -0
- package/dist/commands/prompt-to-create-project/get-default-project-name.d.ts +1 -0
- package/dist/commands/prompt-to-create-project/get-default-project-name.js +13 -0
- package/dist/commands/prompt-to-create-project/prompt-for-available-subdomain.d.ts +7 -0
- package/dist/commands/prompt-to-create-project/prompt-for-available-subdomain.js +32 -0
- package/dist/commands/prompt-to-create-project/resolve-project-region.d.ts +8 -0
- package/dist/commands/prompt-to-create-project/resolve-project-region.js +27 -0
- package/dist/commands/prompt-to-create-project/resolve-project-subdomain.d.ts +9 -0
- package/dist/commands/prompt-to-create-project/resolve-project-subdomain.js +54 -0
- package/dist/commands/prompt-to-create-project/resolve-text-input.d.ts +9 -0
- package/dist/commands/prompt-to-create-project/resolve-text-input.js +23 -0
- package/dist/commands/prompt-to-create-project/resolve-workspace-plan-key.d.ts +12 -0
- package/dist/commands/prompt-to-create-project/resolve-workspace-plan-key.js +36 -0
- package/dist/commands/prompt-to-create-project/validate-subdomain.d.ts +1 -0
- package/dist/commands/prompt-to-create-project/validate-subdomain.js +9 -0
- package/dist/commands/prompt-to-create-project.d.ts +24 -0
- package/dist/commands/prompt-to-create-project.js +69 -0
- package/dist/commands/setup-agent.d.ts +12 -0
- package/dist/commands/setup-agent.js +123 -0
- package/dist/commands/write-project-configuration.d.ts +12 -0
- package/dist/commands/write-project-configuration.js +16 -0
- package/dist/db-content-api/index.js +52 -33
- package/dist/db-content-api/utilities/data/injectGlobalType.d.ts +6 -0
- package/dist/db-content-api/utilities/data/injectGlobalType.js +11 -0
- package/dist/db-content-api/utilities/isRecord.d.ts +9 -0
- package/dist/db-content-api/utilities/isRecord.js +19 -0
- package/dist/db-content-api/utilities/joins.js +5 -1
- package/dist/db-content-api/utilities/meta/resolvesToRelationshipValue.d.ts +10 -0
- package/dist/db-content-api/utilities/meta/resolvesToRelationshipValue.js +95 -0
- package/dist/db-content-api/utilities/meta/stringifyIdsInWhere.d.ts +9 -0
- package/dist/db-content-api/utilities/meta/stringifyIdsInWhere.js +112 -0
- package/dist/db-content-api/utilities/where.d.ts +2 -6
- package/dist/db-content-api/utilities/where.js +20 -9
- package/dist/exports/views.d.ts +1 -0
- package/dist/exports/views.js +1 -0
- package/dist/imageSizeOptions.d.ts +23 -0
- package/dist/imageSizeOptions.js +26 -0
- package/dist/index.d.ts +1 -0
- package/dist/lib/write-agent-config.d.ts +12 -0
- package/dist/lib/write-agent-config.js +52 -0
- package/dist/oauth/defaults.d.ts +1 -0
- package/dist/oauth/defaults.js +1 -0
- package/dist/oauth/endpoints/getLoginEndpoint.js +7 -2
- package/dist/oauth/index.js +24 -9
- package/dist/oauth/utilities/getAdminCollectionSlug.d.ts +1 -1
- package/dist/oauth/utilities/getAuthorizeURL.d.ts +3 -0
- package/dist/oauth/utilities/getAuthorizeURL.js +8 -2
- package/dist/plugin/build-config.js +22 -9
- package/dist/plugin/cloud-limits/CloudLimitModal/index.d.ts +2 -3
- package/dist/plugin/cloud-limits/CloudLimitModal/index.js +4 -16
- package/dist/plugin/cloud-limits/CloudLimitModal/index.scss +5 -0
- package/dist/plugin/cloud-limits/CloudLimitProvider/index.d.ts +0 -3
- package/dist/plugin/cloud-limits/CloudLimitProvider/index.js +65 -124
- package/dist/plugin/cloud-limits/LimitReachedView/index.css +14 -0
- package/dist/plugin/cloud-limits/LimitReachedView/index.d.ts +4 -0
- package/dist/plugin/cloud-limits/LimitReachedView/index.js +52 -0
- package/dist/plugin/cloud-limits/SlugRegistryContext/index.d.ts +7 -2
- package/dist/plugin/cloud-limits/SlugTracker/index.d.ts +0 -8
- package/dist/plugin/cloud-limits/SlugTracker/index.js +14 -10
- package/dist/plugin/cloud-limits/routes.d.ts +2 -0
- package/dist/plugin/cloud-limits/routes.js +4 -0
- package/dist/plugin/cloud-limits/server/redirectInitialAutosaveCreate.d.ts +9 -0
- package/dist/plugin/cloud-limits/server/redirectInitialAutosaveCreate.js +28 -0
- package/dist/plugin/cloud-limits/shared.d.ts +1 -1
- package/dist/plugin/cloud-limits/shared.js +25 -0
- package/dist/plugin/cloud-limits/utilities/cloudLimitInterceptor.d.ts +2 -0
- package/dist/plugin/cloud-limits/utilities/cloudLimitInterceptor.js +134 -0
- package/dist/plugin/cloud-limits/utilities/toasts.d.ts +4 -0
- package/dist/plugin/cloud-limits/utilities/toasts.js +19 -0
- package/dist/skills/deploy-to-figma/SKILL.md +234 -0
- package/dist/skills/deploy-to-figma/references/cli-reference.md +146 -0
- package/dist/types.d.ts +9 -0
- package/dist/utils/lambda-config.js +4 -14
- package/dist/utils/messages.js +22 -3
- package/dist/utils/payload-generate.d.ts +10 -0
- package/dist/utils/payload-generate.js +59 -0
- package/dist/utils/pnpm-builds.d.ts +14 -0
- package/dist/utils/pnpm-builds.js +125 -0
- package/package.json +9 -1
- package/dist/lib/download-skill.d.ts +0 -12
- package/dist/lib/download-skill.js +0 -77
|
@@ -7,16 +7,26 @@ export interface DeployCommandOptions {
|
|
|
7
7
|
adapter?: AdapterName;
|
|
8
8
|
/** Enable debug mode */
|
|
9
9
|
debug?: boolean;
|
|
10
|
+
/** Description used when creating an uninitialized project */
|
|
11
|
+
description?: string;
|
|
10
12
|
/** Figma environment name (e.g. 'production', 'staging') */
|
|
11
13
|
env?: string;
|
|
12
14
|
/** CMS Resource ID (overrides FIGMA_PROJECT_ID) */
|
|
13
15
|
id?: string;
|
|
16
|
+
/** Name used when creating an uninitialized project */
|
|
17
|
+
name?: string;
|
|
14
18
|
/** Exact framework build output root */
|
|
15
19
|
output?: string;
|
|
20
|
+
/** Region used when creating an uninitialized project */
|
|
21
|
+
region?: string;
|
|
16
22
|
/** Skip the build step (use existing build) */
|
|
17
23
|
skipBuild?: boolean;
|
|
18
24
|
/** Skip the client-side unzipped bundle size pre-flight check */
|
|
19
25
|
skipSizeCheck?: boolean;
|
|
26
|
+
/** Subdomain used when creating an uninitialized project */
|
|
27
|
+
subdomain?: string;
|
|
28
|
+
/** Workspace plan key used when creating an uninitialized project */
|
|
29
|
+
workspace?: string;
|
|
20
30
|
/** Skip confirmation prompts */
|
|
21
31
|
yes?: boolean;
|
|
22
32
|
}
|
package/dist/commands/deploy.js
CHANGED
|
@@ -2,7 +2,8 @@ import * as p from '@clack/prompts';
|
|
|
2
2
|
import spawn from 'cross-spawn';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import pc from 'picocolors';
|
|
5
|
-
import {
|
|
5
|
+
import { createDeployment, getBootstrapInfo, performDeployment } from '../api/control-plane.js';
|
|
6
|
+
import { ControlPlaneError } from '../api/control-plane-error.js';
|
|
6
7
|
import { tryGetCredential } from '../auth/oauth-flow.js';
|
|
7
8
|
import { getTokenStore } from '../auth/token-store.js';
|
|
8
9
|
import { getInfraEnvironment, getProjectNotFoundMessage } from '../constants.js';
|
|
@@ -16,7 +17,10 @@ import { getPackageManager } from '../utils/package-manager.js';
|
|
|
16
17
|
import { isInProjectDirectory } from '../utils/project.js';
|
|
17
18
|
import { resolveEnvironment } from '../utils/resolve-environment.js';
|
|
18
19
|
import { uploadLambdaZip, uploadStaticAssets } from '../utils/s3-upload.js';
|
|
20
|
+
import { detectProjectResourceType } from './detect-project-resource-type.js';
|
|
19
21
|
import { loginCommand } from './login.js';
|
|
22
|
+
import { promptToCreateProject } from './prompt-to-create-project.js';
|
|
23
|
+
import { writeProjectConfiguration } from './write-project-configuration.js';
|
|
20
24
|
/**
|
|
21
25
|
* Format bytes to human-readable string
|
|
22
26
|
*/ function formatBytes(bytes) {
|
|
@@ -52,15 +56,6 @@ import { loginCommand } from './login.js';
|
|
|
52
56
|
*/ export async function deployCommand(options) {
|
|
53
57
|
const projectPath = process.cwd();
|
|
54
58
|
const spinner = p.spinner();
|
|
55
|
-
// Resolve project ID
|
|
56
|
-
let projectId = options.id;
|
|
57
|
-
if (!projectId) {
|
|
58
|
-
projectId = await getEnvVar(projectPath, 'FIGMA_PROJECT_ID') ?? undefined;
|
|
59
|
-
}
|
|
60
|
-
if (!projectId) {
|
|
61
|
-
p.log.error(pc.red('✗ Project ID not found. Run `@payloadcms/figma init` first, or use --id.'));
|
|
62
|
-
process.exit(1);
|
|
63
|
-
}
|
|
64
59
|
// Resolve environment name
|
|
65
60
|
const environmentName = options.env ?? await getEnvVar(projectPath, 'FIGMA_ENVIRONMENT_NAME') ?? undefined;
|
|
66
61
|
try {
|
|
@@ -76,13 +71,38 @@ import { loginCommand } from './login.js';
|
|
|
76
71
|
let credential = await tryGetCredential(tokenStore);
|
|
77
72
|
if (!credential) {
|
|
78
73
|
p.log.message('Please log in to continue');
|
|
79
|
-
await loginCommand(
|
|
74
|
+
await loginCommand({
|
|
75
|
+
openBrowserWithoutPrompt: options.yes
|
|
76
|
+
});
|
|
80
77
|
credential = await tryGetCredential(tokenStore);
|
|
81
78
|
if (!credential) {
|
|
82
79
|
p.log.error(pc.red('✗ Authentication failed'));
|
|
83
80
|
process.exit(1);
|
|
84
81
|
}
|
|
85
82
|
}
|
|
83
|
+
// Resolve or create the CMS project.
|
|
84
|
+
let projectId = options.id;
|
|
85
|
+
if (!projectId) {
|
|
86
|
+
projectId = await getEnvVar(projectPath, 'FIGMA_PROJECT_ID') ?? undefined;
|
|
87
|
+
}
|
|
88
|
+
const isProjectCreated = !projectId;
|
|
89
|
+
if (!projectId) {
|
|
90
|
+
const resourceType = options.adapter === 'vite' ? 'vite' : options.adapter ? 'payload' : await detectProjectResourceType(projectPath);
|
|
91
|
+
const project = await promptToCreateProject({
|
|
92
|
+
credential,
|
|
93
|
+
inputs: {
|
|
94
|
+
name: options.name,
|
|
95
|
+
description: options.description,
|
|
96
|
+
region: options.region,
|
|
97
|
+
subdomain: options.subdomain,
|
|
98
|
+
workspacePlanKey: options.workspace
|
|
99
|
+
},
|
|
100
|
+
nonInteractive: options.yes,
|
|
101
|
+
projectPath,
|
|
102
|
+
resourceType
|
|
103
|
+
});
|
|
104
|
+
projectId = project.id;
|
|
105
|
+
}
|
|
86
106
|
// Resolve tenant instance ID via bootstrap
|
|
87
107
|
spinner.start('Resolving deployment target...');
|
|
88
108
|
let tenantInstanceId;
|
|
@@ -93,6 +113,14 @@ import { loginCommand } from './login.js';
|
|
|
93
113
|
environments: bootstrapInfo.environments
|
|
94
114
|
});
|
|
95
115
|
tenantInstanceId = resolvedEnv.tenantInstanceId;
|
|
116
|
+
await writeProjectConfiguration({
|
|
117
|
+
bootstrapInfo,
|
|
118
|
+
environmentName: resolvedEnv.name,
|
|
119
|
+
projectDir: projectPath,
|
|
120
|
+
projectId,
|
|
121
|
+
shouldWriteEnvironmentVariables: isProjectCreated,
|
|
122
|
+
tokenStore
|
|
123
|
+
});
|
|
96
124
|
spinner.stop(pc.green(`✓ Target: ${resolvedEnv.name}`));
|
|
97
125
|
} catch (error) {
|
|
98
126
|
if (error instanceof ControlPlaneError && (error.statusCode === 404 || error.statusCode === 410)) {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
/** Detect whether project provisioning should use static Vite or Payload infrastructure. */ export async function detectProjectResourceType(projectPath = process.cwd()) {
|
|
4
|
+
try {
|
|
5
|
+
const content = await fs.readFile(path.join(projectPath, 'package.json'), 'utf-8');
|
|
6
|
+
const packageJson = JSON.parse(content);
|
|
7
|
+
const dependencies = {
|
|
8
|
+
...packageJson.dependencies,
|
|
9
|
+
...packageJson.devDependencies
|
|
10
|
+
};
|
|
11
|
+
if (dependencies.payload) {
|
|
12
|
+
return 'payload';
|
|
13
|
+
}
|
|
14
|
+
if (dependencies.vite || Object.values(packageJson.scripts ?? {}).some((script)=>/\bvite\b/.test(script))) {
|
|
15
|
+
return 'vite';
|
|
16
|
+
}
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if (error.code !== 'ENOENT') {
|
|
19
|
+
return 'payload';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
await fs.access(path.join(projectPath, 'index.html'));
|
|
24
|
+
return 'vite';
|
|
25
|
+
} catch {
|
|
26
|
+
return 'payload';
|
|
27
|
+
}
|
|
28
|
+
}
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -12,8 +12,8 @@ export interface InitCommandOptions {
|
|
|
12
12
|
id?: string;
|
|
13
13
|
/** Project name (for scaffolding) */
|
|
14
14
|
name?: string;
|
|
15
|
-
/** Skip
|
|
16
|
-
|
|
15
|
+
/** Skip writing AI agent config files (AGENTS.md, CLAUDE.md) */
|
|
16
|
+
noAgent?: boolean;
|
|
17
17
|
/** npm dist-tag or explicit version for core payload packages (e.g. 'canary', 'latest', '4.0.0-canary.16'); defaults to 'canary' */
|
|
18
18
|
payloadVersion?: string;
|
|
19
19
|
/** Skip authentication check (for testing/development) */
|
package/dist/commands/init.js
CHANGED
|
@@ -2,13 +2,14 @@ import * as p from '@clack/prompts';
|
|
|
2
2
|
import fs from 'fs/promises';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import pc from 'picocolors';
|
|
5
|
-
import {
|
|
5
|
+
import { getBootstrapInfo } from '../api/control-plane.js';
|
|
6
|
+
import { ControlPlaneError } from '../api/control-plane-error.js';
|
|
6
7
|
import { FigmaApiError } from '../api/figma-api.js';
|
|
7
8
|
import { tryGetCredential } from '../auth/oauth-flow.js';
|
|
8
9
|
import { getValidProjectToken, ProjectTokenError } from '../auth/project-token.js';
|
|
9
10
|
import { getTokenStore } from '../auth/token-store.js';
|
|
10
11
|
import { getInfraEnvironment, getProjectNotFoundMessage } from '../constants.js';
|
|
11
|
-
import {
|
|
12
|
+
import { writeAgentConfigFiles } from '../lib/write-agent-config.js';
|
|
12
13
|
import { ensureGitignore } from '../utils/config.js';
|
|
13
14
|
import { addOrUpdateEnvVar } from '../utils/env-management.js';
|
|
14
15
|
import { isDebug } from '../utils/is-debug.js';
|
|
@@ -16,27 +17,54 @@ import { applyLambdaModifications } from '../utils/lambda-config.js';
|
|
|
16
17
|
import * as log from '../utils/log.js';
|
|
17
18
|
import { detectPackageManagerFromEnvironment, getRunCommand, resolvePackageManager } from '../utils/package-manager.js';
|
|
18
19
|
import { displayManualInstructions, ensurePayloadFigmaConfig } from '../utils/payload-config-modifier.js';
|
|
20
|
+
import { generateImportMap, generateTypes } from '../utils/payload-generate.js';
|
|
21
|
+
import { ensurePnpmWorkspace } from '../utils/pnpm-builds.js';
|
|
19
22
|
import { detectPayloadProject, hasRequiredDependencies, initializeGitRepo, installDependencies, isInProjectDirectory, scaffoldProject, validatePayloadVersion } from '../utils/project.js';
|
|
20
23
|
import { resolveEnvironment } from '../utils/resolve-environment.js';
|
|
21
24
|
import { checkForUpdates, getOwnVersion } from '../utils/version-check.js';
|
|
25
|
+
import { detectProjectResourceType } from './detect-project-resource-type.js';
|
|
22
26
|
import { loginCommand } from './login.js';
|
|
27
|
+
import { promptToCreateProject } from './prompt-to-create-project.js';
|
|
28
|
+
import { writeProjectConfiguration } from './write-project-configuration.js';
|
|
23
29
|
import { cacheAllEnvironments } from '../utils/cache-bootstrap.js';
|
|
24
30
|
import { DEFAULT_TEMPLATE_SOURCE } from '../utils/download-template.js';
|
|
25
31
|
import { parseTemplateSpec, TemplateSpecParseError } from '../utils/parse-template-spec.js';
|
|
26
|
-
async function
|
|
27
|
-
if (args.
|
|
32
|
+
async function maybeWriteAgentConfigFiles(args) {
|
|
33
|
+
if (args.noAgent) {
|
|
28
34
|
return;
|
|
29
35
|
}
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
try {
|
|
37
|
+
await writeAgentConfigFiles({
|
|
38
|
+
projectDir: args.projectDir
|
|
39
|
+
});
|
|
40
|
+
} catch (err) {
|
|
41
|
+
p.log.warn(pc.yellow(`⚠ Could not write AI agent config (${err instanceof Error ? err.message : String(err)}). Continuing.`));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Generate the admin import map and Payload types so the scaffold builds clean. */ async function runProjectCodegen(args) {
|
|
45
|
+
await runPayloadGenerate({
|
|
46
|
+
...args,
|
|
47
|
+
artifact: 'import map',
|
|
48
|
+
command: 'generate:importmap',
|
|
49
|
+
generate: generateImportMap
|
|
32
50
|
});
|
|
51
|
+
await runPayloadGenerate({
|
|
52
|
+
...args,
|
|
53
|
+
artifact: 'types',
|
|
54
|
+
command: 'generate:types',
|
|
55
|
+
generate: generateTypes
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/** Non-fatal: on failure, warn with the command to run later. */ async function runPayloadGenerate(args) {
|
|
59
|
+
args.spinner.start(`Generating ${args.artifact}...`);
|
|
60
|
+
const result = await args.generate(args.projectDir);
|
|
61
|
+
const capitalized = args.artifact.charAt(0).toUpperCase() + args.artifact.slice(1);
|
|
33
62
|
if (result.ok) {
|
|
63
|
+
args.spinner.stop(pc.green(`✓ ${capitalized} generated`));
|
|
34
64
|
return;
|
|
35
65
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
p.log.warn(pc.yellow(`⚠ Could not install Payload skill (${result.reason}${result.detail ? `: ${result.detail}` : ''}). Continuing.`));
|
|
66
|
+
args.spinner.stop(pc.yellow(`⚠ Could not generate ${args.artifact}`));
|
|
67
|
+
p.log.warn(pc.yellow(`Run '${getRunCommand(args.packageManager)} ${args.command}' later. ${result.error}`));
|
|
40
68
|
}
|
|
41
69
|
/**
|
|
42
70
|
* Generate and store project token for a content system
|
|
@@ -94,6 +122,7 @@ async function maybeInstallPayloadSkill(args) {
|
|
|
94
122
|
// Check authentication
|
|
95
123
|
const tokenStore = getTokenStore();
|
|
96
124
|
const s = p.spinner();
|
|
125
|
+
let credential = null;
|
|
97
126
|
if (options.skipAuth) {
|
|
98
127
|
// Skip authentication for testing/development
|
|
99
128
|
p.log.warn(pc.yellow('Skipping authentication (--skip-auth mode)'));
|
|
@@ -102,14 +131,14 @@ async function maybeInstallPayloadSkill(args) {
|
|
|
102
131
|
if (!tokenStore.hasValidOauthToken()) {
|
|
103
132
|
// Need to refresh or authenticate - show spinner for API call
|
|
104
133
|
s.start('Checking authentication...');
|
|
105
|
-
|
|
134
|
+
credential = await tryGetCredential(tokenStore);
|
|
106
135
|
if (!credential) {
|
|
107
136
|
s.stop('Authentication required to continue');
|
|
108
137
|
await loginCommand({
|
|
109
138
|
showNextSteps: false
|
|
110
139
|
});
|
|
111
|
-
|
|
112
|
-
if (!
|
|
140
|
+
credential = await tryGetCredential(tokenStore);
|
|
141
|
+
if (!credential) {
|
|
113
142
|
p.log.error('Authentication failed');
|
|
114
143
|
process.exit(1);
|
|
115
144
|
}
|
|
@@ -118,16 +147,12 @@ async function maybeInstallPayloadSkill(args) {
|
|
|
118
147
|
}
|
|
119
148
|
}
|
|
120
149
|
}
|
|
150
|
+
if (!options.skipAuth && !credential) {
|
|
151
|
+
credential = await tryGetCredential(tokenStore);
|
|
152
|
+
}
|
|
121
153
|
// Note: We no longer check for figma.config.json here
|
|
122
154
|
// Instead, the AST detection will check if figma object already exists in payload.config.ts
|
|
123
155
|
// and skip modification if it does (unless --force is used)
|
|
124
|
-
// Step 3: Get CMS Resource ID (required argument)
|
|
125
|
-
log.debug('Configuring Project...');
|
|
126
|
-
if (!options.id) {
|
|
127
|
-
p.log.error('CMS Resource ID is required. Use --id <cms-resource-id>');
|
|
128
|
-
process.exit(1);
|
|
129
|
-
}
|
|
130
|
-
const cmsResourceId = options.id;
|
|
131
156
|
// Parse --template spec early so malformed values abort before any I/O
|
|
132
157
|
let templateSource;
|
|
133
158
|
if (options.template) {
|
|
@@ -140,13 +165,95 @@ async function maybeInstallPayloadSkill(args) {
|
|
|
140
165
|
process.exit(1);
|
|
141
166
|
}
|
|
142
167
|
}
|
|
168
|
+
// Step 3: Resolve or create the CMS project
|
|
169
|
+
log.debug('Configuring Project...');
|
|
170
|
+
const hasPackageJson = await isInProjectDirectory();
|
|
171
|
+
const resourceType = await detectProjectResourceType();
|
|
172
|
+
const isInProject = hasPackageJson || resourceType === 'vite';
|
|
173
|
+
let projectDirectory = process.cwd();
|
|
174
|
+
if (isInProject && resourceType !== 'vite' && !options.id) {
|
|
175
|
+
const projectInfo = await detectPayloadProject();
|
|
176
|
+
if (!projectInfo.hasPayload) {
|
|
177
|
+
p.log.warn(pc.yellow('⚠ Payload was not detected in this project'));
|
|
178
|
+
p.note('Run this command in a directory without an existing project or in an existing Payload project.', 'Action Required');
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
if (projectInfo.payloadVersion && !validatePayloadVersion(projectInfo.payloadVersion)) {
|
|
182
|
+
p.log.warn(pc.yellow(`Payload version ${projectInfo.payloadVersion} detected. Version 4.x or newer is required.`));
|
|
183
|
+
p.note('Upgrade to Payload 4.x or newer before continuing', 'Action Required');
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (!isInProject) {
|
|
188
|
+
let projectPath;
|
|
189
|
+
if (options.name) {
|
|
190
|
+
projectPath = options.name;
|
|
191
|
+
} else {
|
|
192
|
+
const projectPathInput = await p.text({
|
|
193
|
+
initialValue: './',
|
|
194
|
+
message: 'Enter path to create project:',
|
|
195
|
+
placeholder: './my-cms-project',
|
|
196
|
+
validate: (value)=>{
|
|
197
|
+
if (!value) {
|
|
198
|
+
return 'Path is required';
|
|
199
|
+
}
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
if (p.isCancel(projectPathInput)) {
|
|
204
|
+
p.cancel('Operation cancelled');
|
|
205
|
+
process.exit(0);
|
|
206
|
+
}
|
|
207
|
+
projectPath = projectPathInput;
|
|
208
|
+
}
|
|
209
|
+
projectDirectory = path.resolve(process.cwd(), projectPath);
|
|
210
|
+
const projectName = path.basename(projectDirectory);
|
|
211
|
+
const packageManager = detectPackageManagerFromEnvironment();
|
|
212
|
+
try {
|
|
213
|
+
await fs.mkdir(projectDirectory, {
|
|
214
|
+
recursive: true
|
|
215
|
+
});
|
|
216
|
+
} catch (error) {
|
|
217
|
+
p.log.error(`Failed to create directory: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
220
|
+
const resolvedSource = {
|
|
221
|
+
...DEFAULT_TEMPLATE_SOURCE,
|
|
222
|
+
...templateSource
|
|
223
|
+
};
|
|
224
|
+
s.start(`Downloading template "${resolvedSource.templatePath}" from ` + `${resolvedSource.owner}/${resolvedSource.repo}@${resolvedSource.ref}...`);
|
|
225
|
+
try {
|
|
226
|
+
await scaffoldProject(projectDirectory, projectName, packageManager, templateSource, options.payloadVersion);
|
|
227
|
+
s.stop(pc.green('✓ Template downloaded'));
|
|
228
|
+
s.start('Installing dependencies...');
|
|
229
|
+
await installDependencies(projectDirectory, packageManager);
|
|
230
|
+
s.stop(pc.green('✓ Dependencies installed'));
|
|
231
|
+
} catch (error) {
|
|
232
|
+
s.stop(pc.red('✗ Failed'));
|
|
233
|
+
log.error(error instanceof Error ? error.message : 'Unknown error');
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
let cmsResourceId = options.id;
|
|
238
|
+
if (!cmsResourceId) {
|
|
239
|
+
if (!credential) {
|
|
240
|
+
p.log.error('Authentication is required to create a project');
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
const createdProject = await promptToCreateProject({
|
|
244
|
+
credential,
|
|
245
|
+
projectPath: projectDirectory,
|
|
246
|
+
resourceType
|
|
247
|
+
});
|
|
248
|
+
cmsResourceId = createdProject.id;
|
|
249
|
+
await addOrUpdateEnvVar(projectDirectory, 'FIGMA_PROJECT_ID', cmsResourceId);
|
|
250
|
+
}
|
|
143
251
|
// Fetch bootstrap info (oauth creds + environments)
|
|
144
252
|
let bootstrapInfo = null;
|
|
145
253
|
let resolvedEnv = null;
|
|
146
254
|
let oauthClientId = null;
|
|
147
255
|
let oauthClientSecret = null;
|
|
148
256
|
if (!options.skipAuth) {
|
|
149
|
-
const credential = await tryGetCredential(tokenStore);
|
|
150
257
|
if (credential) {
|
|
151
258
|
try {
|
|
152
259
|
bootstrapInfo = await getBootstrapInfo(credential, cmsResourceId);
|
|
@@ -178,8 +285,23 @@ async function maybeInstallPayloadSkill(args) {
|
|
|
178
285
|
}
|
|
179
286
|
}
|
|
180
287
|
}
|
|
181
|
-
// Step 4: Detect or scaffold
|
|
182
|
-
|
|
288
|
+
// Step 4: Detect or scaffold project
|
|
289
|
+
if (isInProject && resourceType === 'vite') {
|
|
290
|
+
try {
|
|
291
|
+
await writeProjectConfiguration({
|
|
292
|
+
bootstrapInfo,
|
|
293
|
+
environmentName: resolvedEnv?.name ?? 'production',
|
|
294
|
+
projectDir: process.cwd(),
|
|
295
|
+
projectId: cmsResourceId,
|
|
296
|
+
tokenStore
|
|
297
|
+
});
|
|
298
|
+
} catch (error) {
|
|
299
|
+
p.log.error(error instanceof Error ? error.message : 'Failed to configure project');
|
|
300
|
+
process.exit(1);
|
|
301
|
+
}
|
|
302
|
+
p.outro(pc.green('✓ Vite project initialized successfully!'));
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
183
305
|
if (isInProject) {
|
|
184
306
|
// ===== EXISTING PROJECT FLOW =====
|
|
185
307
|
log.debug('Project directory detected');
|
|
@@ -194,6 +316,9 @@ async function maybeInstallPayloadSkill(args) {
|
|
|
194
316
|
process.exit(1);
|
|
195
317
|
}
|
|
196
318
|
p.log.success(pc.green(`✓ Payload ${projectInfo.payloadVersion || 'detected'}`));
|
|
319
|
+
// Approvals must exist before any install: pnpm v11 aborts on unapproved
|
|
320
|
+
// build scripts, and re-init runs against an existing install.
|
|
321
|
+
await ensurePnpmWorkspace(process.cwd());
|
|
197
322
|
// Check dependencies
|
|
198
323
|
s.start('Checking dependencies...');
|
|
199
324
|
const hasDeps = await hasRequiredDependencies(process.cwd());
|
|
@@ -274,8 +399,13 @@ async function maybeInstallPayloadSkill(args) {
|
|
|
274
399
|
'.env.local',
|
|
275
400
|
'lambda.zip'
|
|
276
401
|
]);
|
|
277
|
-
await
|
|
278
|
-
|
|
402
|
+
await runProjectCodegen({
|
|
403
|
+
packageManager,
|
|
404
|
+
projectDir: process.cwd(),
|
|
405
|
+
spinner: s
|
|
406
|
+
});
|
|
407
|
+
await maybeWriteAgentConfigFiles({
|
|
408
|
+
noAgent: options.noAgent,
|
|
279
409
|
projectDir: process.cwd()
|
|
280
410
|
});
|
|
281
411
|
// Generate project token (unless skipping auth)
|
|
@@ -293,58 +423,8 @@ async function maybeInstallPayloadSkill(args) {
|
|
|
293
423
|
p.note(nextSteps.join('\n'), 'Next Steps');
|
|
294
424
|
} else {
|
|
295
425
|
// ===== NEW PROJECT FLOW =====
|
|
296
|
-
|
|
297
|
-
let projectPath;
|
|
298
|
-
if (options.name) {
|
|
299
|
-
projectPath = options.name;
|
|
300
|
-
} else {
|
|
301
|
-
const projectPathInput = await p.text({
|
|
302
|
-
initialValue: './',
|
|
303
|
-
message: 'Enter path to create project:',
|
|
304
|
-
placeholder: './my-cms-project',
|
|
305
|
-
validate: (value)=>{
|
|
306
|
-
if (!value) {
|
|
307
|
-
return 'Path is required';
|
|
308
|
-
}
|
|
309
|
-
// Allow relative or absolute paths
|
|
310
|
-
return undefined;
|
|
311
|
-
}
|
|
312
|
-
});
|
|
313
|
-
if (p.isCancel(projectPathInput)) {
|
|
314
|
-
p.cancel('Operation cancelled');
|
|
315
|
-
process.exit(0);
|
|
316
|
-
}
|
|
317
|
-
projectPath = projectPathInput;
|
|
318
|
-
}
|
|
319
|
-
const fullPath = path.resolve(process.cwd(), projectPath);
|
|
320
|
-
const projectName = path.basename(fullPath);
|
|
426
|
+
const fullPath = projectDirectory;
|
|
321
427
|
const packageManager = detectPackageManagerFromEnvironment();
|
|
322
|
-
// Create directory
|
|
323
|
-
try {
|
|
324
|
-
await fs.mkdir(fullPath, {
|
|
325
|
-
recursive: true
|
|
326
|
-
});
|
|
327
|
-
} catch (error) {
|
|
328
|
-
p.log.error(`Failed to create directory: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
329
|
-
process.exit(1);
|
|
330
|
-
}
|
|
331
|
-
// Scaffold project
|
|
332
|
-
const resolvedSource = {
|
|
333
|
-
...DEFAULT_TEMPLATE_SOURCE,
|
|
334
|
-
...templateSource
|
|
335
|
-
};
|
|
336
|
-
s.start(`Downloading template "${resolvedSource.templatePath}" from ` + `${resolvedSource.owner}/${resolvedSource.repo}@${resolvedSource.ref}...`);
|
|
337
|
-
try {
|
|
338
|
-
await scaffoldProject(fullPath, projectName, packageManager, templateSource, options.payloadVersion);
|
|
339
|
-
s.stop(pc.green('✓ Template downloaded'));
|
|
340
|
-
s.start('Installing dependencies...');
|
|
341
|
-
await installDependencies(fullPath, packageManager);
|
|
342
|
-
s.stop(pc.green('✓ Dependencies installed'));
|
|
343
|
-
} catch (error) {
|
|
344
|
-
s.stop(pc.red('✗ Failed'));
|
|
345
|
-
log.error(error instanceof Error ? error.message : 'Unknown error');
|
|
346
|
-
process.exit(1);
|
|
347
|
-
}
|
|
348
428
|
// Modify Payload configuration
|
|
349
429
|
s.start('Checking Payload configuration...');
|
|
350
430
|
const modResult = await ensurePayloadFigmaConfig(fullPath, packageManager, {
|
|
@@ -398,8 +478,13 @@ async function maybeInstallPayloadSkill(args) {
|
|
|
398
478
|
log.error(error instanceof Error ? error.message : 'Unknown error');
|
|
399
479
|
process.exit(1);
|
|
400
480
|
}
|
|
401
|
-
await
|
|
402
|
-
|
|
481
|
+
await runProjectCodegen({
|
|
482
|
+
packageManager,
|
|
483
|
+
projectDir: fullPath,
|
|
484
|
+
spinner: s
|
|
485
|
+
});
|
|
486
|
+
await maybeWriteAgentConfigFiles({
|
|
487
|
+
noAgent: options.noAgent,
|
|
403
488
|
projectDir: fullPath
|
|
404
489
|
});
|
|
405
490
|
// Generate project token (unless skipping auth)
|
package/dist/commands/login.d.ts
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
type LoginArgs = {
|
|
2
|
+
/** Redirect URL returned by the browser for resumable login completion. */
|
|
3
|
+
callbackUrl?: string;
|
|
4
|
+
/** Pending resumable login session to complete. */
|
|
5
|
+
completeSessionId?: string;
|
|
6
|
+
/** Start a resumable login without waiting on stdin or a callback server. */
|
|
7
|
+
headless?: boolean;
|
|
8
|
+
/** Emit machine-readable output for resumable login. */
|
|
9
|
+
json?: boolean;
|
|
10
|
+
/** Open the browser without asking for confirmation. */
|
|
11
|
+
openBrowserWithoutPrompt?: boolean;
|
|
2
12
|
/** Set this to show next steps after login
|
|
3
13
|
* @default true
|
|
4
14
|
*/
|
|
5
|
-
showNextSteps
|
|
15
|
+
showNextSteps?: boolean;
|
|
6
16
|
};
|
|
7
17
|
/**
|
|
8
18
|
* Handle the `@payloadcms/figma login` command
|
package/dist/commands/login.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as p from '@clack/prompts';
|
|
2
2
|
import pc from 'picocolors';
|
|
3
3
|
import { CallbackError } from '../auth/callback-server.js';
|
|
4
|
+
import { completeHeadlessOAuthFlow, startHeadlessOAuthFlow } from '../auth/headless-oauth-flow.js';
|
|
4
5
|
import { executeOAuthFlow, getValidCredential, OAuthFlowError } from '../auth/oauth-flow.js';
|
|
5
6
|
import { getTokenStore } from '../auth/token-store.js';
|
|
6
7
|
import { getOAuthConfig } from '../config/oauth.js';
|
|
@@ -13,11 +14,47 @@ import * as log from '../utils/log.js';
|
|
|
13
14
|
* Authenticates the user with Figma OAuth2 and stores tokens locally.
|
|
14
15
|
* If valid tokens already exist, shows an error message.
|
|
15
16
|
*/ export async function loginCommand(args) {
|
|
16
|
-
const { showNextSteps } = args
|
|
17
|
-
showNextSteps: false
|
|
18
|
-
};
|
|
17
|
+
const { callbackUrl, completeSessionId, headless = false, json = false, openBrowserWithoutPrompt = false, showNextSteps = false } = args ?? {};
|
|
19
18
|
const env = getInfraEnvironment();
|
|
20
19
|
const tokenStore = getTokenStore(env);
|
|
20
|
+
if (headless && completeSessionId) {
|
|
21
|
+
throw new Error('Use either --headless or --complete, not both.');
|
|
22
|
+
}
|
|
23
|
+
if (completeSessionId) {
|
|
24
|
+
if (!callbackUrl) {
|
|
25
|
+
throw new Error('--callback-url is required with --complete.');
|
|
26
|
+
}
|
|
27
|
+
const result = await completeHeadlessOAuthFlow({
|
|
28
|
+
callbackUrl,
|
|
29
|
+
sessionId: completeSessionId,
|
|
30
|
+
tokenStore
|
|
31
|
+
});
|
|
32
|
+
reportHeadlessLoginCompleted({
|
|
33
|
+
json,
|
|
34
|
+
userId: result.userId
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (headless) {
|
|
39
|
+
try {
|
|
40
|
+
const credential = await getValidCredential(tokenStore);
|
|
41
|
+
if (credential) {
|
|
42
|
+
reportHeadlessLoginCompleted({
|
|
43
|
+
json,
|
|
44
|
+
userId: tokenStore.getOauthInfo()?.userId
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
} catch {
|
|
49
|
+
// Invalid cached credentials should start a new login session.
|
|
50
|
+
}
|
|
51
|
+
const result = startHeadlessOAuthFlow(tokenStore);
|
|
52
|
+
reportHeadlessLoginStarted({
|
|
53
|
+
json,
|
|
54
|
+
...result
|
|
55
|
+
});
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
21
58
|
// Check for existing valid tokens
|
|
22
59
|
try {
|
|
23
60
|
const existingCredential = await getValidCredential(tokenStore);
|
|
@@ -37,18 +74,43 @@ import * as log from '../utils/log.js';
|
|
|
37
74
|
}
|
|
38
75
|
// Start OAuth flow
|
|
39
76
|
await handleAuthentication({
|
|
77
|
+
openBrowserWithoutPrompt,
|
|
40
78
|
showNextSteps,
|
|
41
79
|
tokenStore
|
|
42
80
|
});
|
|
43
81
|
}
|
|
82
|
+
function reportHeadlessLoginStarted({ authorizationUrl, expiresAt, json, sessionId }) {
|
|
83
|
+
if (json) {
|
|
84
|
+
process.stdout.write(`${JSON.stringify({
|
|
85
|
+
authorizationUrl,
|
|
86
|
+
expiresAt,
|
|
87
|
+
sessionId,
|
|
88
|
+
status: 'authorization_required'
|
|
89
|
+
})}\n`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
p.log.step(`Open this URL to authenticate:\n\n ${authorizationUrl}`);
|
|
93
|
+
p.note(`After approving, copy the full redirected URL and run:\n` + `@payloadcms/figma login --complete ${sessionId} --callback-url '<redirect-url>'`, 'Complete login');
|
|
94
|
+
}
|
|
95
|
+
function reportHeadlessLoginCompleted({ json, userId }) {
|
|
96
|
+
if (json) {
|
|
97
|
+
process.stdout.write(`${JSON.stringify({
|
|
98
|
+
status: 'authenticated',
|
|
99
|
+
userId
|
|
100
|
+
})}\n`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
p.log.success(pc.green('✓ Logged in'));
|
|
104
|
+
}
|
|
44
105
|
/**
|
|
45
106
|
* Handle authentication flow
|
|
46
|
-
*/ async function handleAuthentication({ showNextSteps, tokenStore }) {
|
|
107
|
+
*/ async function handleAuthentication({ openBrowserWithoutPrompt, showNextSteps, tokenStore }) {
|
|
47
108
|
try {
|
|
48
109
|
// Execute OAuth flow
|
|
49
110
|
const oauthConfig = getOAuthConfig();
|
|
50
111
|
const result = await executeOAuthFlow(tokenStore, {
|
|
51
112
|
clientId: oauthConfig.clientId,
|
|
113
|
+
openBrowserWithoutPrompt,
|
|
52
114
|
redirectUri: oauthConfig.redirectUri,
|
|
53
115
|
scopes: oauthConfig.scopes
|
|
54
116
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function formatProjectSubdomain(value: string): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function formatUnavailableSubdomainReason(reason: string): string;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function formatUnavailableSubdomainReason(reason) {
|
|
2
|
+
if (reason === 'already_taken') {
|
|
3
|
+
return 'Subdomain is already taken. Choose another.';
|
|
4
|
+
}
|
|
5
|
+
const reasonDetail = reason ? `: ${reason.replaceAll('_', ' ')}` : '';
|
|
6
|
+
return `Subdomain is unavailable${reasonDetail}. Choose another.`;
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getDefaultProjectName(projectPath: string): Promise<string>;
|