@payloadcms/figma 0.0.1-alpha.63 → 0.0.1-alpha.65
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/auth/callback-server.d.ts +19 -7
- package/dist/auth/callback-server.js +72 -31
- package/dist/auth/crypto-utils.d.ts +11 -0
- package/dist/auth/crypto-utils.js +22 -1
- package/dist/auth/oauth-flow.d.ts +3 -1
- package/dist/auth/oauth-flow.js +14 -6
- package/dist/auth/project-token.d.ts +15 -10
- package/dist/auth/project-token.js +147 -64
- package/dist/auth/token-store-migration.js +2 -2
- package/dist/auth/token-store.d.ts +2 -0
- package/dist/auth/token-store.js +43 -3
- package/dist/auth/types.d.ts +11 -0
- package/dist/cli.js +15 -1
- package/dist/commands/bootstrap.d.ts +18 -0
- package/dist/commands/bootstrap.js +90 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.js +76 -4
- package/dist/config/oauth.d.ts +2 -1
- package/dist/config/oauth.js +7 -1
- package/dist/db-content-api/generated/content-api-types.d.ts +6 -0
- package/dist/db-content-api/index.d.ts +2 -0
- package/dist/db-content-api/index.js +36 -74
- package/dist/lib/download-skill.d.ts +13 -0
- package/dist/lib/download-skill.js +79 -0
- package/dist/oauth/endpoints/getLoginEndpoint.js +26 -107
- package/dist/oauth/endpoints/getTokenLoginEndpoint.d.ts +17 -0
- package/dist/oauth/endpoints/getTokenLoginEndpoint.js +105 -0
- package/dist/oauth/index.js +8 -0
- package/dist/oauth/utilities/establishSession.d.ts +23 -0
- package/dist/oauth/utilities/establishSession.js +82 -0
- package/dist/oauth/utilities/exchangeCodeForAccessToken.d.ts +24 -0
- package/dist/oauth/utilities/exchangeCodeForAccessToken.js +28 -0
- package/dist/oauth/utilities/isAbsoluteURL.d.ts +2 -0
- package/dist/oauth/utilities/isAbsoluteURL.js +3 -0
- package/dist/plugin/build-config.js +65 -46
- package/dist/types.d.ts +2 -0
- package/dist/utils/download-template.d.ts +9 -1
- package/dist/utils/download-template.js +24 -19
- package/dist/utils/messages.js +9 -0
- package/dist/utils/parse-template-spec.d.ts +12 -0
- package/dist/utils/parse-template-spec.js +62 -0
- package/dist/utils/payload-config-modifier.js +96 -107
- package/dist/utils/payload-package-check.d.ts +21 -1
- package/dist/utils/payload-package-check.js +66 -26
- package/dist/utils/project.d.ts +2 -1
- package/dist/utils/project.js +2 -2
- package/package.json +9 -1
- package/dist/db-content-api/README.md +0 -98
|
@@ -131,7 +131,7 @@ function safeGet(config, key) {
|
|
|
131
131
|
try {
|
|
132
132
|
return config.get(key);
|
|
133
133
|
} catch (error) {
|
|
134
|
-
log.
|
|
134
|
+
log.debug(`Migration read failed (key: ${String(key)}). ` + `File: ${config.path}. ` + `Error: ${error instanceof Error ? error.message : 'Unknown'}`);
|
|
135
135
|
return undefined;
|
|
136
136
|
}
|
|
137
137
|
}
|
|
@@ -148,7 +148,7 @@ function safeGet(config, key) {
|
|
|
148
148
|
projectName: params.projectName
|
|
149
149
|
});
|
|
150
150
|
} catch (error) {
|
|
151
|
-
log.
|
|
151
|
+
log.debug(`Legacy token store could not be opened; skipping migration and leaving file intact. ` + `Error: ${error instanceof Error ? error.message : 'Unknown'}`);
|
|
152
152
|
return null;
|
|
153
153
|
}
|
|
154
154
|
}
|
|
@@ -12,6 +12,7 @@ export declare function getTokenStore(environment?: Environment): TokenStore;
|
|
|
12
12
|
*/
|
|
13
13
|
export declare class TokenStore {
|
|
14
14
|
private baseConfigName;
|
|
15
|
+
private deriveLegacyKey;
|
|
15
16
|
private encryptionKey;
|
|
16
17
|
private environment;
|
|
17
18
|
private legacyRootDir;
|
|
@@ -69,6 +70,7 @@ export declare class TokenStore {
|
|
|
69
70
|
private getLegacyRootDir;
|
|
70
71
|
private getProjectStore;
|
|
71
72
|
private createConfig;
|
|
73
|
+
private tryMigrateLegacyKey;
|
|
72
74
|
private safeGet;
|
|
73
75
|
}
|
|
74
76
|
//# sourceMappingURL=token-store.d.ts.map
|
package/dist/auth/token-store.js
CHANGED
|
@@ -6,7 +6,7 @@ import { TOKEN_EXPIRY_BUFFER_SECONDS } from '../config/oauth.js';
|
|
|
6
6
|
import { getInfraEnvironment } from '../constants.js';
|
|
7
7
|
import { getEnvVarSync } from '../utils/env-management.js';
|
|
8
8
|
import * as log from '../utils/log.js';
|
|
9
|
-
import { deriveEncryptionKey } from './crypto-utils.js';
|
|
9
|
+
import { deriveEncryptionKey, deriveLegacyEncryptionKey } from './crypto-utils.js';
|
|
10
10
|
import { migrateOAuth, migrateProjectData, needsMigration } from './token-store-migration.js';
|
|
11
11
|
/**
|
|
12
12
|
* Environment-keyed instances for singleton pattern.
|
|
@@ -30,6 +30,7 @@ const PROJECT_STORE_SCHEMA_VERSION = 1;
|
|
|
30
30
|
* The constructor is still exported for test isolation.
|
|
31
31
|
*/ export class TokenStore {
|
|
32
32
|
baseConfigName;
|
|
33
|
+
deriveLegacyKey;
|
|
33
34
|
encryptionKey;
|
|
34
35
|
environment;
|
|
35
36
|
legacyRootDir;
|
|
@@ -45,13 +46,14 @@ const PROJECT_STORE_SCHEMA_VERSION = 1;
|
|
|
45
46
|
const encryptionKey = options?.encryptionKey ?? deriveEncryptionKey();
|
|
46
47
|
const baseConfigName = options?.configName || projectName;
|
|
47
48
|
this.baseConfigName = baseConfigName;
|
|
49
|
+
this.deriveLegacyKey = options?.deriveLegacyKey ?? deriveLegacyEncryptionKey;
|
|
48
50
|
this.encryptionKey = encryptionKey;
|
|
49
51
|
this.environment = environment;
|
|
50
52
|
this.projectName = projectName;
|
|
51
53
|
// Resolve the legacy root dir (matching Conf's env-paths convention)
|
|
52
54
|
// without instantiating Conf, which would read/parse the encrypted file
|
|
53
55
|
// and could wipe it under clearInvalidConfig.
|
|
54
|
-
this.legacyRootDir = envPaths(projectName).config;
|
|
56
|
+
this.legacyRootDir = options?.rootDir ?? envPaths(projectName).config;
|
|
55
57
|
const legacyFilePath = path.join(this.legacyRootDir, `${baseConfigName}.json`);
|
|
56
58
|
const newOAuthCwd = this.buildOAuthStoreCwd();
|
|
57
59
|
const newOAuthFilePath = path.join(newOAuthCwd, `${this.buildOAuthConfigName()}.json`);
|
|
@@ -84,7 +86,7 @@ const PROJECT_STORE_SCHEMA_VERSION = 1;
|
|
|
84
86
|
if (environment !== 'production') {
|
|
85
87
|
const oldEnvProjectName = `payloadcms-figma-${environment}`;
|
|
86
88
|
const oldEnvConfigName = options?.configName || oldEnvProjectName;
|
|
87
|
-
const oldEnvLegacyDir = envPaths(oldEnvProjectName).config;
|
|
89
|
+
const oldEnvLegacyDir = options?.oldEnvLegacyDir ?? envPaths(oldEnvProjectName).config;
|
|
88
90
|
const oldEnvLegacyFilePath = path.join(oldEnvLegacyDir, `${oldEnvConfigName}.json`);
|
|
89
91
|
if (needsMigration({
|
|
90
92
|
legacyFilePath: oldEnvLegacyFilePath,
|
|
@@ -396,6 +398,13 @@ const PROJECT_STORE_SCHEMA_VERSION = 1;
|
|
|
396
398
|
try {
|
|
397
399
|
return new Conf(confOptions);
|
|
398
400
|
} catch (error) {
|
|
401
|
+
const migrated = this.tryMigrateLegacyKey(confOptions);
|
|
402
|
+
if (migrated) {
|
|
403
|
+
if (this.environment === 'staging') {
|
|
404
|
+
log.info('Successfully migrated local token cache.');
|
|
405
|
+
}
|
|
406
|
+
return migrated;
|
|
407
|
+
}
|
|
399
408
|
// Deserialization failed (e.g. encryption key changed).
|
|
400
409
|
// Log diagnostics, then clear and replace with a fresh store.
|
|
401
410
|
if (this.environment === 'staging') {
|
|
@@ -412,6 +421,37 @@ const PROJECT_STORE_SCHEMA_VERSION = 1;
|
|
|
412
421
|
return config;
|
|
413
422
|
}
|
|
414
423
|
}
|
|
424
|
+
tryMigrateLegacyKey(confOptions) {
|
|
425
|
+
if (!confOptions.encryptionKey) {
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
let snapshot;
|
|
429
|
+
try {
|
|
430
|
+
const legacyConf = new Conf({
|
|
431
|
+
...confOptions,
|
|
432
|
+
clearInvalidConfig: false,
|
|
433
|
+
encryptionKey: this.deriveLegacyKey()
|
|
434
|
+
});
|
|
435
|
+
snapshot = {
|
|
436
|
+
...legacyConf.store
|
|
437
|
+
};
|
|
438
|
+
} catch {
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
try {
|
|
442
|
+
const rewritten = new Conf({
|
|
443
|
+
...confOptions,
|
|
444
|
+
clearInvalidConfig: true
|
|
445
|
+
});
|
|
446
|
+
rewritten.clear();
|
|
447
|
+
for (const [key, value] of Object.entries(snapshot)){
|
|
448
|
+
rewritten.set(key, value);
|
|
449
|
+
}
|
|
450
|
+
return rewritten;
|
|
451
|
+
} catch {
|
|
452
|
+
return null;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
415
455
|
safeGet(params) {
|
|
416
456
|
const { config, key } = params;
|
|
417
457
|
try {
|
package/dist/auth/types.d.ts
CHANGED
|
@@ -109,10 +109,21 @@ export interface OAuthTokenRefreshParams {
|
|
|
109
109
|
export type TokenStoreConfig = {
|
|
110
110
|
/** Config file name (for testing) */
|
|
111
111
|
configName?: string;
|
|
112
|
+
/**
|
|
113
|
+
* Override for the legacy key derivation used by transparent migration.
|
|
114
|
+
* Tests pass an explicit function so they don't depend on the host machine's
|
|
115
|
+
* actual entropy. Production code leaves this undefined and the constructor
|
|
116
|
+
* falls back to `deriveLegacyEncryptionKey`.
|
|
117
|
+
*/
|
|
118
|
+
deriveLegacyKey?: () => string;
|
|
112
119
|
/** Optional override for storage encryption key (primarily for tests). */
|
|
113
120
|
encryptionKey?: string;
|
|
114
121
|
/** Environment for config file naming */
|
|
115
122
|
environment?: Environment;
|
|
123
|
+
/** Override for the old per-environment legacy directory (primarily for tests). */
|
|
124
|
+
oldEnvLegacyDir?: string;
|
|
125
|
+
/** Override for the storage root directory (primarily for tests). */
|
|
126
|
+
rootDir?: string;
|
|
116
127
|
};
|
|
117
128
|
/**
|
|
118
129
|
* PKCE (Proof Key for Code Exchange) pair for OAuth2 public clients
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as p from '@clack/prompts';
|
|
2
2
|
import arg from 'arg';
|
|
3
3
|
import pc from 'picocolors';
|
|
4
|
+
import { bootstrapCommand } from './commands/bootstrap.js';
|
|
4
5
|
import { buildLambdaZipCommand } from './commands/build-lambda-zip.js';
|
|
5
6
|
import { debugCommand } from './commands/debug.js';
|
|
6
7
|
import { deployCommand } from './commands/deploy.js';
|
|
@@ -42,9 +43,12 @@ class Main {
|
|
|
42
43
|
'--help': Boolean,
|
|
43
44
|
'--id': String,
|
|
44
45
|
'--infra-env': String,
|
|
46
|
+
'--json': Boolean,
|
|
45
47
|
'--name': String,
|
|
48
|
+
'--no-skill': Boolean,
|
|
46
49
|
'--skip-auth': Boolean,
|
|
47
50
|
'--skip-build': Boolean,
|
|
51
|
+
'--template': String,
|
|
48
52
|
'--version': Boolean,
|
|
49
53
|
'--yes': Boolean,
|
|
50
54
|
// Aliases
|
|
@@ -53,6 +57,7 @@ class Main {
|
|
|
53
57
|
'-f': '--force',
|
|
54
58
|
'-h': '--help',
|
|
55
59
|
'-n': '--name',
|
|
60
|
+
'-t': '--template',
|
|
56
61
|
'-v': '--version',
|
|
57
62
|
'-y': '--yes'
|
|
58
63
|
}, {
|
|
@@ -112,6 +117,13 @@ class Main {
|
|
|
112
117
|
p.intro(pc.bgCyan(pc.black(' @payloadcms/figma ')));
|
|
113
118
|
// Route to appropriate command handler
|
|
114
119
|
switch(subcommand){
|
|
120
|
+
case 'bootstrap':
|
|
121
|
+
await bootstrapCommand({
|
|
122
|
+
id: this.args['--id'],
|
|
123
|
+
env: this.args['--env'],
|
|
124
|
+
json: this.args['--json']
|
|
125
|
+
});
|
|
126
|
+
break;
|
|
115
127
|
case 'build-lambda-zip':
|
|
116
128
|
await buildLambdaZipCommand();
|
|
117
129
|
break;
|
|
@@ -137,7 +149,9 @@ class Main {
|
|
|
137
149
|
debug: this.args['--debug'],
|
|
138
150
|
env: this.args['--env'],
|
|
139
151
|
force: this.args['--force'],
|
|
140
|
-
|
|
152
|
+
noSkill: this.args['--no-skill'],
|
|
153
|
+
skipAuth: this.args['--skip-auth'],
|
|
154
|
+
template: this.args['--template']
|
|
141
155
|
});
|
|
142
156
|
break;
|
|
143
157
|
case 'list-tokens':
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type BootstrapCommandOptions = {
|
|
2
|
+
/** Filter to a single environment (e.g. "production", "staging"). */
|
|
3
|
+
env?: string;
|
|
4
|
+
/** CMS Resource ID (FIGMA_PROJECT_ID, e.g. cms_…). */
|
|
5
|
+
id?: string;
|
|
6
|
+
/** Emit plain JSON instead of a styled note (handy for piping into jq). */
|
|
7
|
+
json?: boolean;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Fetch and display bootstrap info for a CMS resource.
|
|
11
|
+
*
|
|
12
|
+
* Calls the Control Plane bootstrap endpoint with the current OAuth credential
|
|
13
|
+
* and prints the resulting environments (`contentSystemId`, `tenantInstanceId`)
|
|
14
|
+
* plus OAuth client credentials. Useful for grabbing a tenant ID without having
|
|
15
|
+
* to `init` a project.
|
|
16
|
+
*/
|
|
17
|
+
export declare function bootstrapCommand(options?: BootstrapCommandOptions): Promise<void>;
|
|
18
|
+
//# sourceMappingURL=bootstrap.d.ts.map
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import pc from 'picocolors';
|
|
3
|
+
import { ControlPlaneError, getBootstrapInfo } from '../api/control-plane.js';
|
|
4
|
+
import { getValidCredential } from '../auth/oauth-flow.js';
|
|
5
|
+
import { getTokenStore } from '../auth/token-store.js';
|
|
6
|
+
import { getInfraEnvironment } from '../constants.js';
|
|
7
|
+
import { isDebug } from '../utils/is-debug.js';
|
|
8
|
+
import { maskToken } from '../utils/token-display.js';
|
|
9
|
+
/**
|
|
10
|
+
* Fetch and display bootstrap info for a CMS resource.
|
|
11
|
+
*
|
|
12
|
+
* Calls the Control Plane bootstrap endpoint with the current OAuth credential
|
|
13
|
+
* and prints the resulting environments (`contentSystemId`, `tenantInstanceId`)
|
|
14
|
+
* plus OAuth client credentials. Useful for grabbing a tenant ID without having
|
|
15
|
+
* to `init` a project.
|
|
16
|
+
*/ export async function bootstrapCommand(options = {}) {
|
|
17
|
+
if (!options.id) {
|
|
18
|
+
p.log.error(pc.red('--id <project-id> is required'));
|
|
19
|
+
p.note('The CMS Resource ID (FIGMA_PROJECT_ID), e.g. cms_abc123', 'Tip');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
const tokenStore = getTokenStore(getInfraEnvironment());
|
|
23
|
+
let credential;
|
|
24
|
+
try {
|
|
25
|
+
credential = await getValidCredential(tokenStore);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
p.log.error(pc.red(`Failed to read stored credentials: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
28
|
+
p.note(`Run ${pc.cyan(`@payloadcms/figma logout --infra-env ${getInfraEnvironment()} -y`)} ` + `then ${pc.cyan(`@payloadcms/figma login --infra-env ${getInfraEnvironment()}`)} and retry.`, 'Re-authenticate');
|
|
29
|
+
if (isDebug() && error instanceof Error) {
|
|
30
|
+
// eslint-disable-next-line no-console
|
|
31
|
+
console.error(error);
|
|
32
|
+
}
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
if (!credential) {
|
|
36
|
+
p.log.error(pc.red('Not authenticated.'));
|
|
37
|
+
p.note(`Run ${pc.cyan('@payloadcms/figma login')} first`, 'Tip');
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
let info;
|
|
41
|
+
try {
|
|
42
|
+
info = await getBootstrapInfo(credential, options.id, options.env);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (error instanceof ControlPlaneError) {
|
|
45
|
+
p.log.error(pc.red(`Bootstrap fetch failed: ${error.statusCode} ${error.message}`));
|
|
46
|
+
if (error.statusCode === 401) {
|
|
47
|
+
p.note(`Stored credentials for infra env ${pc.bold(getInfraEnvironment())} were rejected.\n` + `Run ${pc.cyan(`@payloadcms/figma logout --infra-env ${getInfraEnvironment()} -y`)} ` + `then ${pc.cyan(`@payloadcms/figma login --infra-env ${getInfraEnvironment()}`)} and retry.`, 'Re-authenticate');
|
|
48
|
+
} else if (error.statusCode === 404) {
|
|
49
|
+
p.note(`Resource ${pc.bold(options.id)} was not found in infra env ${pc.bold(getInfraEnvironment())}.\n` + `Try ${pc.cyan('--infra-env staging')} (or production) to target a different infra.`, 'Wrong infra environment?');
|
|
50
|
+
}
|
|
51
|
+
} else if (error instanceof Error) {
|
|
52
|
+
const causeMessage = error.cause instanceof Error ? `: ${error.cause.message}` : '';
|
|
53
|
+
p.log.error(pc.red(`${error.name}: ${error.message}${causeMessage}`));
|
|
54
|
+
if (isDebug()) {
|
|
55
|
+
// eslint-disable-next-line no-console
|
|
56
|
+
console.error(error);
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
p.log.error(pc.red('Unknown error'));
|
|
60
|
+
}
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
if (options.json) {
|
|
64
|
+
const showSecretsInJson = isDebug();
|
|
65
|
+
const payload = showSecretsInJson ? info : {
|
|
66
|
+
...info,
|
|
67
|
+
oauthCredentials: {
|
|
68
|
+
...info.oauthCredentials,
|
|
69
|
+
oauthClientSecret: maskToken(info.oauthCredentials.oauthClientSecret)
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
// eslint-disable-next-line no-console
|
|
73
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const showSecret = isDebug();
|
|
77
|
+
const lines = [
|
|
78
|
+
`Project ID: ${options.id}`,
|
|
79
|
+
`OAuth Client ID: ${info.oauthCredentials.oauthClientId}`,
|
|
80
|
+
`OAuth Client Secret: ${showSecret ? info.oauthCredentials.oauthClientSecret : maskToken(info.oauthCredentials.oauthClientSecret)}`,
|
|
81
|
+
'',
|
|
82
|
+
'Environments:'
|
|
83
|
+
];
|
|
84
|
+
for (const env of info.environments){
|
|
85
|
+
lines.push(` ${pc.bold(env.name)}`, ` Tenant ID (contentSystemId): ${env.contentSystemId}`, ` Tenant Instance ID: ${env.tenantInstanceId}`);
|
|
86
|
+
}
|
|
87
|
+
p.note(lines.join('\n'), 'Bootstrap Info');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
//# sourceMappingURL=bootstrap.js.map
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -12,8 +12,12 @@ export interface InitCommandOptions {
|
|
|
12
12
|
id?: string;
|
|
13
13
|
/** Project name (for scaffolding) */
|
|
14
14
|
name?: string;
|
|
15
|
+
/** Skip Payload skill installation */
|
|
16
|
+
noSkill?: boolean;
|
|
15
17
|
/** Skip authentication check (for testing/development) */
|
|
16
18
|
skipAuth?: boolean;
|
|
19
|
+
/** GitHub template override, e.g. "v3.80.0:website" or "owner/repo#ref:template-path" */
|
|
20
|
+
template?: string;
|
|
17
21
|
/** Skip prompts and use defaults where possible */
|
|
18
22
|
yes?: boolean;
|
|
19
23
|
}
|
package/dist/commands/init.js
CHANGED
|
@@ -8,6 +8,7 @@ import { tryGetCredential } from '../auth/oauth-flow.js';
|
|
|
8
8
|
import { getValidProjectToken, ProjectTokenError } from '../auth/project-token.js';
|
|
9
9
|
import { getTokenStore } from '../auth/token-store.js';
|
|
10
10
|
import { getInfraEnvironment, getProjectNotFoundMessage } from '../constants.js';
|
|
11
|
+
import { downloadSkill } from '../lib/download-skill.js';
|
|
11
12
|
import { ensureGitignore } from '../utils/config.js';
|
|
12
13
|
import { addOrUpdateEnvVar } from '../utils/env-management.js';
|
|
13
14
|
import { isDebug } from '../utils/is-debug.js';
|
|
@@ -21,6 +22,23 @@ import { resolveEnvironment } from '../utils/resolve-environment.js';
|
|
|
21
22
|
import { checkForUpdates, getOwnVersion } from '../utils/version-check.js';
|
|
22
23
|
import { loginCommand } from './login.js';
|
|
23
24
|
import { cacheAllEnvironments } from '../utils/cache-bootstrap.js';
|
|
25
|
+
import { DEFAULT_TEMPLATE_SOURCE } from '../utils/download-template.js';
|
|
26
|
+
import { parseTemplateSpec, TemplateSpecParseError } from '../utils/parse-template-spec.js';
|
|
27
|
+
async function maybeInstallPayloadSkill(args) {
|
|
28
|
+
if (args.noSkill) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const result = await downloadSkill({
|
|
32
|
+
projectDir: args.projectDir
|
|
33
|
+
});
|
|
34
|
+
if (result.ok) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (result.reason === 'already-installed') {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
p.log.warn(pc.yellow(`⚠ Could not install Payload skill (${result.reason}${result.detail ? `: ${result.detail}` : ''}). Continuing.`));
|
|
41
|
+
}
|
|
24
42
|
/**
|
|
25
43
|
* Generate and store project token for a content system
|
|
26
44
|
* Non-blocking - will show warning but not exit on failure
|
|
@@ -111,6 +129,18 @@ import { cacheAllEnvironments } from '../utils/cache-bootstrap.js';
|
|
|
111
129
|
process.exit(1);
|
|
112
130
|
}
|
|
113
131
|
const cmsResourceId = options.id;
|
|
132
|
+
// Parse --template spec early so malformed values abort before any I/O
|
|
133
|
+
let templateSource;
|
|
134
|
+
if (options.template) {
|
|
135
|
+
try {
|
|
136
|
+
templateSource = parseTemplateSpec(options.template);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
const message = error instanceof TemplateSpecParseError ? error.message : 'Invalid --template value';
|
|
139
|
+
p.log.error(pc.red(`✗ ${message}`));
|
|
140
|
+
p.note('Format: [<owner>/<repo>#]<ref>:<template-path>\n' + 'Examples:\n' + ' blank\n' + ' v3.80.0:website\n' + ' myorg/payload-fork#feat-x:templates/custom', 'Template spec');
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
114
144
|
// Fetch bootstrap info (oauth creds + environments)
|
|
115
145
|
let bootstrapInfo = null;
|
|
116
146
|
let resolvedEnv = null;
|
|
@@ -225,6 +255,18 @@ import { cacheAllEnvironments } from '../utils/cache-bootstrap.js';
|
|
|
225
255
|
p.note('Run this command in a directory without an existing project or in an existing Payload project.', 'Action Required');
|
|
226
256
|
process.exit(1);
|
|
227
257
|
}
|
|
258
|
+
// npm leaves node_modules in a transient state after a sequence of
|
|
259
|
+
// add/remove calls; without a final `install` the subprocess that runs
|
|
260
|
+
// generate:importmap silently misses @payloadcms/figma subpath components.
|
|
261
|
+
s.start('Reconciling dependencies...');
|
|
262
|
+
try {
|
|
263
|
+
await installDependencies(process.cwd(), packageManager);
|
|
264
|
+
s.stop(pc.green('✓ Dependencies reconciled'));
|
|
265
|
+
} catch (error) {
|
|
266
|
+
s.stop(pc.red('✗ Failed to reconcile dependencies before generate:importmap'));
|
|
267
|
+
log.error(error instanceof Error ? error.message : 'Unknown error');
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
228
270
|
// Generate import map to prevent errors on first dev run
|
|
229
271
|
const importMapResult = await runScript(process.cwd(), 'generate:importmap', packageManager);
|
|
230
272
|
if (!importMapResult) {
|
|
@@ -238,9 +280,16 @@ import { cacheAllEnvironments } from '../utils/cache-bootstrap.js';
|
|
|
238
280
|
'.env.local',
|
|
239
281
|
'lambda.zip'
|
|
240
282
|
]);
|
|
283
|
+
await maybeInstallPayloadSkill({
|
|
284
|
+
noSkill: options.noSkill,
|
|
285
|
+
projectDir: process.cwd()
|
|
286
|
+
});
|
|
241
287
|
// Generate project token (unless skipping auth)
|
|
242
288
|
if (!options.skipAuth && resolvedEnv) {
|
|
243
|
-
await generateProjectTokenWithFeedback(tokenStore, resolvedEnv.contentSystemId, s
|
|
289
|
+
await generateProjectTokenWithFeedback(tokenStore, resolvedEnv.contentSystemId, s, {
|
|
290
|
+
environmentName: resolvedEnv.name,
|
|
291
|
+
projectId: cmsResourceId
|
|
292
|
+
});
|
|
244
293
|
}
|
|
245
294
|
// Success message for existing project
|
|
246
295
|
p.outro(pc.green('✓ Project initialized successfully!'));
|
|
@@ -286,9 +335,13 @@ import { cacheAllEnvironments } from '../utils/cache-bootstrap.js';
|
|
|
286
335
|
process.exit(1);
|
|
287
336
|
}
|
|
288
337
|
// Scaffold project
|
|
289
|
-
|
|
338
|
+
const resolvedSource = {
|
|
339
|
+
...DEFAULT_TEMPLATE_SOURCE,
|
|
340
|
+
...templateSource
|
|
341
|
+
};
|
|
342
|
+
s.start(`Downloading template "${resolvedSource.templatePath}" from ` + `${resolvedSource.owner}/${resolvedSource.repo}@${resolvedSource.ref}...`);
|
|
290
343
|
try {
|
|
291
|
-
await scaffoldProject(fullPath, projectName, packageManager);
|
|
344
|
+
await scaffoldProject(fullPath, projectName, packageManager, templateSource);
|
|
292
345
|
s.stop(pc.green('✓ Template downloaded'));
|
|
293
346
|
s.start('Installing dependencies...');
|
|
294
347
|
await installDependencies(fullPath, packageManager);
|
|
@@ -339,14 +392,33 @@ import { cacheAllEnvironments } from '../utils/cache-bootstrap.js';
|
|
|
339
392
|
} catch (error) {
|
|
340
393
|
p.log.warn(pc.yellow(`⚠ Failed to update .env file: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
341
394
|
}
|
|
395
|
+
// npm leaves node_modules in a transient state after a sequence of
|
|
396
|
+
// add/remove calls; without a final `install` the subprocess that runs
|
|
397
|
+
// generate:importmap silently misses @payloadcms/figma subpath components.
|
|
398
|
+
s.start('Reconciling dependencies...');
|
|
399
|
+
try {
|
|
400
|
+
await installDependencies(fullPath, packageManager);
|
|
401
|
+
s.stop(pc.green('✓ Dependencies reconciled'));
|
|
402
|
+
} catch (error) {
|
|
403
|
+
s.stop(pc.red('✗ Failed to reconcile dependencies before generate:importmap'));
|
|
404
|
+
log.error(error instanceof Error ? error.message : 'Unknown error');
|
|
405
|
+
process.exit(1);
|
|
406
|
+
}
|
|
342
407
|
// Generate import map to prevent errors on first dev run
|
|
343
408
|
const importMapResult = await runScript(fullPath, 'generate:importmap', packageManager);
|
|
344
409
|
if (!importMapResult) {
|
|
345
410
|
p.log.warn(pc.yellow('⚠ Import map generation failed — it will be generated on first dev run'));
|
|
346
411
|
}
|
|
412
|
+
await maybeInstallPayloadSkill({
|
|
413
|
+
noSkill: options.noSkill,
|
|
414
|
+
projectDir: fullPath
|
|
415
|
+
});
|
|
347
416
|
// Generate project token (unless skipping auth)
|
|
348
417
|
if (!options.skipAuth && resolvedEnv) {
|
|
349
|
-
await generateProjectTokenWithFeedback(tokenStore, resolvedEnv.contentSystemId, s
|
|
418
|
+
await generateProjectTokenWithFeedback(tokenStore, resolvedEnv.contentSystemId, s, {
|
|
419
|
+
environmentName: resolvedEnv.name,
|
|
420
|
+
projectId: cmsResourceId
|
|
421
|
+
});
|
|
350
422
|
}
|
|
351
423
|
// Initialize git repository (after all files including lock file are ready)
|
|
352
424
|
initializeGitRepo(fullPath);
|
package/dist/config/oauth.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { OAuthConfig } from '../auth/types.js';
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const DEFAULT_CALLBACK_PORTS: number[];
|
|
3
|
+
export declare const DEFAULT_CALLBACK_PORT: number;
|
|
3
4
|
/**
|
|
4
5
|
* Get OAuth configuration for current environment.
|
|
5
6
|
*
|
package/dist/config/oauth.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { getEnvConfig, getInfraEnvironment } from '../constants.js';
|
|
2
|
-
export const
|
|
2
|
+
export const DEFAULT_CALLBACK_PORTS = [
|
|
3
|
+
34462,
|
|
4
|
+
34463,
|
|
5
|
+
34464,
|
|
6
|
+
34465
|
|
7
|
+
];
|
|
8
|
+
export const DEFAULT_CALLBACK_PORT = DEFAULT_CALLBACK_PORTS[0];
|
|
3
9
|
/**
|
|
4
10
|
* Get OAuth configuration for current environment.
|
|
5
11
|
*
|
|
@@ -3483,6 +3483,8 @@ export type components = {
|
|
|
3483
3483
|
CreateDocumentRequest: {
|
|
3484
3484
|
/** @example cms-xxxxx-xxxxx */
|
|
3485
3485
|
contentSystemId: string;
|
|
3486
|
+
/** User-defined Payload environment name (e.g. "production"). */
|
|
3487
|
+
environmentName?: string;
|
|
3486
3488
|
/** @example posts */
|
|
3487
3489
|
collection: string;
|
|
3488
3490
|
doc: components['schemas']['DocumentData'];
|
|
@@ -3594,6 +3596,8 @@ export type components = {
|
|
|
3594
3596
|
UpdateDocumentRequest: {
|
|
3595
3597
|
/** @example cms-xxxxx-xxxxx */
|
|
3596
3598
|
contentSystemId: string;
|
|
3599
|
+
/** User-defined Payload environment name (e.g. "production"). */
|
|
3600
|
+
environmentName?: string;
|
|
3597
3601
|
/** @example posts */
|
|
3598
3602
|
collection: string;
|
|
3599
3603
|
/** @example false */
|
|
@@ -3627,6 +3631,8 @@ export type components = {
|
|
|
3627
3631
|
DeleteDocumentRequest: {
|
|
3628
3632
|
/** @example cms-xxxxx-xxxxx */
|
|
3629
3633
|
contentSystemId: string;
|
|
3634
|
+
/** User-defined Payload environment name (e.g. "production"). */
|
|
3635
|
+
environmentName?: string;
|
|
3630
3636
|
/** @example posts */
|
|
3631
3637
|
collection: string;
|
|
3632
3638
|
locale?: components['schemas']['LocaleClause'];
|
|
@@ -6,6 +6,7 @@ type ContentAPIOptions = {
|
|
|
6
6
|
allowIDOnCreate?: boolean;
|
|
7
7
|
auth: AuthMode;
|
|
8
8
|
contentSystemId: string;
|
|
9
|
+
environmentName?: string;
|
|
9
10
|
url: string;
|
|
10
11
|
};
|
|
11
12
|
export type ContentAPIAdapter = {
|
|
@@ -13,6 +14,7 @@ export type ContentAPIAdapter = {
|
|
|
13
14
|
clearDatabase: () => Promise<void>;
|
|
14
15
|
client: ReturnType<typeof createClient<paths>>;
|
|
15
16
|
contentSystemId: string;
|
|
17
|
+
environmentName?: string;
|
|
16
18
|
idType: 'uuid';
|
|
17
19
|
url: string;
|
|
18
20
|
} & BaseDatabaseAdapter;
|