@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.
Files changed (48) hide show
  1. package/dist/auth/callback-server.d.ts +19 -7
  2. package/dist/auth/callback-server.js +72 -31
  3. package/dist/auth/crypto-utils.d.ts +11 -0
  4. package/dist/auth/crypto-utils.js +22 -1
  5. package/dist/auth/oauth-flow.d.ts +3 -1
  6. package/dist/auth/oauth-flow.js +14 -6
  7. package/dist/auth/project-token.d.ts +15 -10
  8. package/dist/auth/project-token.js +147 -64
  9. package/dist/auth/token-store-migration.js +2 -2
  10. package/dist/auth/token-store.d.ts +2 -0
  11. package/dist/auth/token-store.js +43 -3
  12. package/dist/auth/types.d.ts +11 -0
  13. package/dist/cli.js +15 -1
  14. package/dist/commands/bootstrap.d.ts +18 -0
  15. package/dist/commands/bootstrap.js +90 -0
  16. package/dist/commands/init.d.ts +4 -0
  17. package/dist/commands/init.js +76 -4
  18. package/dist/config/oauth.d.ts +2 -1
  19. package/dist/config/oauth.js +7 -1
  20. package/dist/db-content-api/generated/content-api-types.d.ts +6 -0
  21. package/dist/db-content-api/index.d.ts +2 -0
  22. package/dist/db-content-api/index.js +36 -74
  23. package/dist/lib/download-skill.d.ts +13 -0
  24. package/dist/lib/download-skill.js +79 -0
  25. package/dist/oauth/endpoints/getLoginEndpoint.js +26 -107
  26. package/dist/oauth/endpoints/getTokenLoginEndpoint.d.ts +17 -0
  27. package/dist/oauth/endpoints/getTokenLoginEndpoint.js +105 -0
  28. package/dist/oauth/index.js +8 -0
  29. package/dist/oauth/utilities/establishSession.d.ts +23 -0
  30. package/dist/oauth/utilities/establishSession.js +82 -0
  31. package/dist/oauth/utilities/exchangeCodeForAccessToken.d.ts +24 -0
  32. package/dist/oauth/utilities/exchangeCodeForAccessToken.js +28 -0
  33. package/dist/oauth/utilities/isAbsoluteURL.d.ts +2 -0
  34. package/dist/oauth/utilities/isAbsoluteURL.js +3 -0
  35. package/dist/plugin/build-config.js +65 -46
  36. package/dist/types.d.ts +2 -0
  37. package/dist/utils/download-template.d.ts +9 -1
  38. package/dist/utils/download-template.js +24 -19
  39. package/dist/utils/messages.js +9 -0
  40. package/dist/utils/parse-template-spec.d.ts +12 -0
  41. package/dist/utils/parse-template-spec.js +62 -0
  42. package/dist/utils/payload-config-modifier.js +96 -107
  43. package/dist/utils/payload-package-check.d.ts +21 -1
  44. package/dist/utils/payload-package-check.js +66 -26
  45. package/dist/utils/project.d.ts +2 -1
  46. package/dist/utils/project.js +2 -2
  47. package/package.json +9 -1
  48. package/dist/db-content-api/README.md +0 -98
@@ -5,7 +5,7 @@ import * as log from './log.js';
5
5
  import { getAddCommand, getRunCommand } from './package-manager.js';
6
6
  import { addFigmaProperty, applyModifications, detectRequiredChanges } from './payload-config-ast.js';
7
7
  import { findPayloadConfig } from './payload-config-finder.js';
8
- import { checkPackageInstalled, getPayloadVersion, installPackage, uninstallPackage } from './payload-package-check.js';
8
+ import { checkPackageInstalled, getPayloadVersion, installPackages, uninstallPackages } from './payload-package-check.js';
9
9
  import { runTypeScriptCheck } from './typescript-validator.js';
10
10
  import { getOwnVersion } from './version-check.js';
11
11
  /**
@@ -14,10 +14,9 @@ import { getOwnVersion } from './version-check.js';
14
14
  */ export async function ensurePayloadFigmaConfig(projectPath, packageManager, figmaConfig) {
15
15
  const changes = [];
16
16
  const warnings = [];
17
- let modified = false;
17
+ let astModified = false;
18
18
  try {
19
19
  log.debug(`Starting payload config modification for: ${projectPath}`);
20
- // 1. Find config file
21
20
  const configPath = await findPayloadConfig(projectPath);
22
21
  if (!configPath) {
23
22
  log.debug(`Config file not found in: ${projectPath}`);
@@ -29,32 +28,10 @@ import { getOwnVersion } from './version-check.js';
29
28
  };
30
29
  }
31
30
  log.debug(`Found config file: ${configPath}`);
32
- // Get payload version to pin @payloadcms/* dependencies
31
+ // Pin @payloadcms/* packages to the project's payload version when possible.
33
32
  const payloadVersion = await getPayloadVersion(projectPath);
34
33
  log.debug(`Detected payload version: ${payloadVersion || 'unknown'}`);
35
- // 2. Check/install @payloadcms/figma package
36
- const isInstalled = await checkPackageInstalled(projectPath, '@payloadcms/figma');
37
- if (!isInstalled) {
38
- log.debug('@payloadcms/figma not installed, attempting installation...');
39
- try {
40
- const figmaVersion = await getOwnVersion();
41
- await installPackage(projectPath, '@payloadcms/figma', packageManager, figmaVersion);
42
- changes.push('Installed @payloadcms/figma');
43
- modified = true;
44
- log.debug('@payloadcms/figma installed successfully');
45
- } catch (error) {
46
- log.debug(`Failed to install @payloadcms/figma: ${error instanceof Error ? error.message : 'Unknown error'}`);
47
- return {
48
- changes: [],
49
- error: 'Failed to install @payloadcms/figma',
50
- modified: false,
51
- success: false
52
- };
53
- }
54
- } else {
55
- log.debug('@payloadcms/figma already installed');
56
- }
57
- // 3. Parse config with ts-morph
34
+ // Parse first so a malformed config fails fast before we touch node_modules.
58
35
  log.debug('Parsing config file with ts-morph...');
59
36
  const project = new Project({
60
37
  manipulationSettings: {
@@ -64,17 +41,15 @@ import { getOwnVersion } from './version-check.js';
64
41
  skipAddingFilesFromTsConfig: true
65
42
  });
66
43
  const sourceFile = project.addSourceFileAtPath(configPath);
67
- // 4. Detect required changes
68
44
  log.debug('Detecting required changes...');
69
45
  const detection = detectRequiredChanges(sourceFile);
70
46
  log.debug(`Detection results: needsImportChange=${detection.needsImportChange}, hasAlias=${detection.hasAlias}, hasBuildConfig=${detection.hasBuildConfig}`);
71
- // Handle failure modes
72
47
  if (detection.hasAlias) {
73
48
  log.debug('Config uses import alias (e.g., buildConfig as X)');
74
49
  return {
75
50
  changes,
76
51
  error: 'Config uses import alias - manual modification required',
77
- modified,
52
+ modified: false,
78
53
  success: false
79
54
  };
80
55
  }
@@ -83,25 +58,95 @@ import { getOwnVersion } from './version-check.js';
83
58
  return {
84
59
  changes,
85
60
  error: 'Config structure not recognized (no buildConfig call found)',
86
- modified,
61
+ modified: false,
87
62
  success: false
88
63
  };
89
64
  }
90
- // 5. Apply modifications
65
+ // Compute the full install/uninstall diff up front so each can run as a
66
+ // single package-manager call. npm leaves node_modules transient between
67
+ // back-to-back calls, which causes generate:importmap to silently miss
68
+ // subpath components — batching avoids that intermediate state.
69
+ const packagesToInstall = [];
70
+ const packagesToUninstall = [];
71
+ const figmaInstalled = await checkPackageInstalled(projectPath, '@payloadcms/figma');
72
+ if (!figmaInstalled) {
73
+ const figmaVersion = await getOwnVersion();
74
+ packagesToInstall.push({
75
+ name: '@payloadcms/figma',
76
+ changeMessage: 'Installed @payloadcms/figma',
77
+ version: figmaVersion
78
+ });
79
+ }
80
+ const editorIsDefault = !detection.editorProperty || detection.editorProperty.isDefault;
81
+ if (editorIsDefault) {
82
+ const hasLexical = await checkPackageInstalled(projectPath, '@payloadcms/richtext-lexical');
83
+ if (!hasLexical) {
84
+ packagesToInstall.push({
85
+ name: '@payloadcms/richtext-lexical',
86
+ changeMessage: 'Installed @payloadcms/richtext-lexical (default editor)',
87
+ version: payloadVersion
88
+ });
89
+ }
90
+ } else {
91
+ log.debug(`Custom editor detected (${detection.editorProperty?.importSource}) - skipping lexical installation`);
92
+ }
93
+ const hasCloudStorage = await checkPackageInstalled(projectPath, '@payloadcms/plugin-cloud-storage');
94
+ if (!hasCloudStorage) {
95
+ packagesToInstall.push({
96
+ name: '@payloadcms/plugin-cloud-storage',
97
+ changeMessage: 'Installed @payloadcms/plugin-cloud-storage',
98
+ version: payloadVersion
99
+ });
100
+ }
101
+ if (detection.dbProperty) {
102
+ packagesToUninstall.push(detection.dbProperty.importSource);
103
+ }
104
+ if (detection.sharpProperty) {
105
+ packagesToUninstall.push('sharp');
106
+ }
107
+ if (packagesToInstall.length > 0) {
108
+ log.debug(`Installing ${packagesToInstall.length} packages in a single operation: ${packagesToInstall.map((p)=>p.name).join(', ')}`);
109
+ try {
110
+ await installPackages(projectPath, packagesToInstall.map(({ name, version })=>({
111
+ name,
112
+ version
113
+ })), packageManager);
114
+ for (const { changeMessage } of packagesToInstall){
115
+ changes.push(changeMessage);
116
+ }
117
+ } catch (error) {
118
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
119
+ log.debug(`Batched install failed: ${errorMessage}`);
120
+ const figmaInBatch = packagesToInstall.some((p)=>p.name === '@payloadcms/figma');
121
+ if (figmaInBatch) {
122
+ // Without @payloadcms/figma the rewritten config would import a
123
+ // missing package, so abort before touching the file.
124
+ return {
125
+ changes: [],
126
+ error: 'Failed to install @payloadcms/figma',
127
+ modified: false,
128
+ success: false
129
+ };
130
+ }
131
+ // Lexical and cloud-storage are non-fatal — payload still loads without
132
+ // them, so warn (with package names) and continue.
133
+ const failedNames = packagesToInstall.map((p)=>p.name).join(', ');
134
+ warnings.push(`Could not install dependencies (${failedNames}) — install them manually. Details: ${errorMessage}`);
135
+ }
136
+ }
91
137
  log.debug('Applying modifications...');
92
138
  const modResult = applyModifications(sourceFile, detection);
93
139
  if (modResult.modified) {
94
140
  changes.push(...modResult.changes);
95
- modified = true;
141
+ astModified = true;
96
142
  log.debug(`Applied ${modResult.changes.length} modifications`);
97
143
  }
98
- // 6. Add figma property if config provided and doesn't exist
99
144
  if (figmaConfig && !detection.figmaObjectExists) {
100
145
  log.debug('Adding figma property to buildConfig...');
101
146
  const figmaResult = addFigmaProperty(sourceFile, figmaConfig);
102
147
  if (figmaResult.modified) {
103
148
  changes.push(...figmaResult.changes);
104
- modified = true;
149
+ astModified = true;
105
150
  log.debug(`Added figma property`);
106
151
  } else {
107
152
  log.debug('Figma property not added (may already exist)');
@@ -109,95 +154,39 @@ import { getOwnVersion } from './version-check.js';
109
154
  } else if (detection.figmaObjectExists) {
110
155
  log.debug('Figma property already exists, skipping');
111
156
  }
112
- // Save file if any modifications were made
113
- if (modified) {
157
+ if (astModified) {
114
158
  sourceFile.formatText({
115
159
  indentSize: 2,
116
160
  tabSize: 2
117
161
  });
118
162
  await sourceFile.save();
119
163
  log.debug('Config file saved');
120
- // 7. Handle package management
121
- const packagesToUninstall = [];
122
- // Uninstall db adapter (buildFigmaConfig provides its own)
123
- if (detection.dbProperty) {
124
- packagesToUninstall.push(detection.dbProperty.importSource);
125
- }
126
- // Uninstall sharp (Figma infrastructure handles image processing)
127
- if (detection.sharpProperty) {
128
- packagesToUninstall.push('sharp');
129
- }
130
- // Ensure lexical is installed if user doesn't have a custom editor
131
- // (buildFigmaConfig uses lexicalEditor() as default)
132
- if (!detection.editorProperty) {
133
- log.debug('No editor property detected - will ensure lexical is installed for default editor');
134
- } else if (detection.editorProperty.isDefault) {
135
- log.debug('Default lexicalEditor() detected - will ensure lexical is installed');
136
- } else {
137
- log.debug(`Custom editor detected (${detection.editorProperty.importSource}) - skipping lexical installation`);
138
- }
139
- if (!detection.editorProperty || detection.editorProperty.isDefault) {
140
- log.debug('Checking if @payloadcms/richtext-lexical is installed...');
141
- const hasLexical = await checkPackageInstalled(projectPath, '@payloadcms/richtext-lexical');
142
- if (!hasLexical) {
143
- log.debug('@payloadcms/richtext-lexical not found, installing...');
144
- try {
145
- await installPackage(projectPath, '@payloadcms/richtext-lexical', packageManager, payloadVersion);
146
- changes.push('Installed @payloadcms/richtext-lexical (default editor)');
147
- log.debug('Successfully installed @payloadcms/richtext-lexical');
148
- } catch (error) {
149
- log.debug(`Failed to install @payloadcms/richtext-lexical: ${error instanceof Error ? error.message : 'Unknown error'}`);
150
- // Don't fail on install error - user can install manually if needed
151
- warnings.push('Could not install @payloadcms/richtext-lexical - you may need to install it manually');
152
- }
153
- } else {
154
- log.debug('@payloadcms/richtext-lexical already installed, skipping');
155
- }
156
- }
157
- // Ensure cloud-storage is installed (required peer dependency for buildFigmaConfig)
158
- log.debug('Checking if @payloadcms/plugin-cloud-storage is installed...');
159
- const hasCloudStorage = await checkPackageInstalled(projectPath, '@payloadcms/plugin-cloud-storage');
160
- if (!hasCloudStorage) {
161
- log.debug('@payloadcms/plugin-cloud-storage not found, installing...');
162
- try {
163
- await installPackage(projectPath, '@payloadcms/plugin-cloud-storage', packageManager, payloadVersion);
164
- changes.push('Installed @payloadcms/plugin-cloud-storage');
165
- log.debug('Successfully installed @payloadcms/plugin-cloud-storage');
166
- } catch (error) {
167
- log.debug(`Failed to install @payloadcms/plugin-cloud-storage: ${error instanceof Error ? error.message : 'Unknown error'}`);
168
- warnings.push('Could not install @payloadcms/plugin-cloud-storage - you may need to install it manually');
169
- }
170
- } else {
171
- log.debug('@payloadcms/plugin-cloud-storage already installed, skipping');
172
- }
173
- if (packagesToUninstall.length > 0) {
174
- log.debug(`Uninstalling ${packagesToUninstall.length} orphaned packages...`);
175
- }
176
- for (const pkg of packagesToUninstall){
177
- try {
178
- await uninstallPackage(projectPath, pkg, packageManager);
179
- changes.push(`Uninstalled ${pkg}`);
180
- log.debug(`Uninstalled ${pkg}`);
181
- } catch (error) {
182
- log.debug(`Failed to uninstall ${pkg}: ${error instanceof Error ? error.message : 'Unknown error'}`);
183
- // Don't fail on uninstall errors - orphaned package is better than broken config
184
- }
185
- }
186
- // 8. Format file
187
164
  log.debug('Formatting config file...');
188
165
  const formatResult = await formatFile(configPath, packageManager);
189
166
  if (!formatResult.success && formatResult.warning) {
190
167
  warnings.push(formatResult.warning);
191
168
  log.debug(`Formatting warning: ${formatResult.warning}`);
192
169
  }
193
- // 9. Run TypeScript validation
194
170
  log.debug('Running TypeScript validation...');
195
171
  const tsResult = await runTypeScriptCheck(projectPath, packageManager);
196
172
  if (!tsResult.success && tsResult.errors.length > 0) {
197
173
  warnings.push(`TypeScript validation found issues - run '${getRunCommand(packageManager)} build' for details`);
198
174
  log.debug(`TypeScript validation found ${tsResult.errors.length} issues`);
199
175
  }
200
- } else {
176
+ }
177
+ if (packagesToUninstall.length > 0) {
178
+ log.debug(`Uninstalling ${packagesToUninstall.length} orphaned packages: ${packagesToUninstall.join(', ')}`);
179
+ const uninstallResult = await uninstallPackages(projectPath, packagesToUninstall, packageManager);
180
+ if (uninstallResult.success) {
181
+ for (const pkg of packagesToUninstall){
182
+ changes.push(`Uninstalled ${pkg}`);
183
+ }
184
+ } else {
185
+ warnings.push(`Could not uninstall ${uninstallResult.failedPackages.join(', ')} — they may remain as orphaned dependencies in package.json`);
186
+ }
187
+ }
188
+ const modified = astModified || changes.length > 0;
189
+ if (!modified) {
201
190
  log.debug('No modifications needed - config already correct');
202
191
  }
203
192
  log.debug('Payload config modification completed successfully');
@@ -215,7 +204,7 @@ import { getOwnVersion } from './version-check.js';
215
204
  return {
216
205
  changes,
217
206
  error: error instanceof Error ? error.message : 'Unknown error occurred',
218
- modified,
207
+ modified: astModified,
219
208
  success: false
220
209
  };
221
210
  }
@@ -1,4 +1,12 @@
1
1
  import type { PackageManager } from './package-manager.js';
2
+ export type PackageSpec = {
3
+ name: string;
4
+ version?: string;
5
+ };
6
+ export type UninstallResult = {
7
+ failedPackages: string[];
8
+ success: boolean;
9
+ };
2
10
  /**
3
11
  * Check if a package is installed in dependencies (not devDependencies)
4
12
  */
@@ -13,11 +21,23 @@ export declare function getPayloadVersion(projectPath: string): Promise<string |
13
21
  * @param version - Version to install (without caret). Defaults to 'latest'.
14
22
  */
15
23
  export declare function installPackage(projectPath: string, packageName: string, packageManager: PackageManager, version?: string): Promise<void>;
24
+ /**
25
+ * Install multiple packages as production dependencies in a single package-manager call.
26
+ * No-op when packages is empty. Throws on failure with exit code and truncated stderr.
27
+ */
28
+ export declare function installPackages(projectPath: string, packages: PackageSpec[], packageManager: PackageManager): Promise<void>;
16
29
  /**
17
30
  * Uninstall a package
18
31
  * Does not throw on failure - orphaned package is better than broken config
19
32
  */
20
- export declare function uninstallPackage(projectPath: string, packageName: string, packageManager: PackageManager): Promise<void>;
33
+ export declare function uninstallPackage(projectPath: string, packageName: string, packageManager: PackageManager): Promise<UninstallResult>;
34
+ /**
35
+ * Uninstall multiple packages in a single package-manager call.
36
+ * No-op when packageNames is empty. Does not throw — orphaned packages are
37
+ * preferable to a crash mid-config-rewrite. Returns a success flag so callers
38
+ * can avoid claiming "Uninstalled X" when the package manager actually failed.
39
+ */
40
+ export declare function uninstallPackages(projectPath: string, packageNames: string[], packageManager: PackageManager): Promise<UninstallResult>;
21
41
  /**
22
42
  * Run an npm script from package.json
23
43
  * Does not throw on failure - logs warning and continues
@@ -40,24 +40,33 @@ import * as log from './log.js';
40
40
  * Install a package as a production dependency
41
41
  * @param version - Version to install (without caret). Defaults to 'latest'.
42
42
  */ export async function installPackage(projectPath, packageName, packageManager, version = 'latest') {
43
+ return installPackages(projectPath, [
44
+ {
45
+ name: packageName,
46
+ version
47
+ }
48
+ ], packageManager);
49
+ }
50
+ /**
51
+ * Install multiple packages as production dependencies in a single package-manager call.
52
+ * No-op when packages is empty. Throws on failure with exit code and truncated stderr.
53
+ */ export async function installPackages(projectPath, packages, packageManager) {
54
+ if (packages.length === 0) {
55
+ return;
56
+ }
43
57
  return new Promise((resolve, reject)=>{
44
58
  const command = packageManager;
45
- const packageSpec = packageName.endsWith('.tgz') ? packageName : `${packageName}@${version}`;
59
+ const packageSpecs = packages.map(({ name, version })=>name.endsWith('.tgz') ? name : `${name}@${version ?? 'latest'}`);
46
60
  const args = [
47
- 'add',
48
- packageSpec
61
+ packageManager === 'npm' ? 'install' : 'add',
62
+ ...packageSpecs
49
63
  ];
50
- // npm uses 'install' instead of 'add'
51
- if (packageManager === 'npm') {
52
- args[0] = 'install';
53
- }
54
64
  log.debug(`Running: ${command} ${args.join(' ')} in ${projectPath}`);
55
65
  let stderr = '';
56
66
  const child = spawn(command, args, {
57
67
  cwd: projectPath,
58
68
  stdio: 'pipe'
59
69
  });
60
- // Capture stderr for debug logging
61
70
  if (child.stderr) {
62
71
  child.stderr.on('data', (data)=>{
63
72
  stderr += data.toString();
@@ -69,15 +78,17 @@ import * as log from './log.js';
69
78
  });
70
79
  child.on('close', (code)=>{
71
80
  if (code === 0) {
72
- log.debug(`Successfully installed ${packageName}`);
81
+ log.debug(`Successfully installed ${packageSpecs.join(', ')}`);
73
82
  resolve();
74
- } else {
75
- log.debug(`Failed to install ${packageName} (exit code: ${code})`);
76
- if (stderr) {
77
- log.debug(`stderr: ${stderr}`);
78
- }
79
- reject(new Error(`Failed to install ${packageName}`));
83
+ return;
84
+ }
85
+ log.debug(`Batched install failed (exit code: ${code})`);
86
+ if (stderr) {
87
+ log.debug(`stderr: ${stderr}`);
80
88
  }
89
+ const truncatedStderr = stderr.trim().slice(-500);
90
+ const detail = truncatedStderr ? `\n${truncatedStderr}` : '';
91
+ reject(new Error(`Failed to install: ${packageSpecs.join(', ')} (exit code ${code})${detail}`));
81
92
  });
82
93
  });
83
94
  }
@@ -85,29 +96,58 @@ import * as log from './log.js';
85
96
  * Uninstall a package
86
97
  * Does not throw on failure - orphaned package is better than broken config
87
98
  */ export async function uninstallPackage(projectPath, packageName, packageManager) {
99
+ return uninstallPackages(projectPath, [
100
+ packageName
101
+ ], packageManager);
102
+ }
103
+ /**
104
+ * Uninstall multiple packages in a single package-manager call.
105
+ * No-op when packageNames is empty. Does not throw — orphaned packages are
106
+ * preferable to a crash mid-config-rewrite. Returns a success flag so callers
107
+ * can avoid claiming "Uninstalled X" when the package manager actually failed.
108
+ */ export async function uninstallPackages(projectPath, packageNames, packageManager) {
109
+ if (packageNames.length === 0) {
110
+ return {
111
+ failedPackages: [],
112
+ success: true
113
+ };
114
+ }
88
115
  return new Promise((resolve)=>{
89
116
  const command = packageManager;
90
117
  const args = [
91
- 'remove',
92
- packageName
118
+ packageManager === 'npm' ? 'uninstall' : 'remove',
119
+ ...packageNames
93
120
  ];
94
- // npm uses 'uninstall' instead of 'remove'
95
- if (packageManager === 'npm') {
96
- args[0] = 'uninstall';
97
- }
98
121
  log.debug(`Running: ${command} ${args.join(' ')} in ${projectPath}`);
99
122
  const child = spawn(command, args, {
100
123
  cwd: projectPath,
101
124
  stdio: 'pipe'
102
125
  });
126
+ child.on('error', (error)=>{
127
+ log.debug(`Spawn error for ${command}: ${error.message}`);
128
+ resolve({
129
+ failedPackages: [
130
+ ...packageNames
131
+ ],
132
+ success: false
133
+ });
134
+ });
103
135
  child.on('close', (code)=>{
104
136
  if (code === 0) {
105
- log.debug(`Successfully uninstalled ${packageName}`);
106
- } else {
107
- log.debug(`Failed to uninstall ${packageName} (exit code: ${code}), continuing anyway`);
137
+ log.debug(`Successfully uninstalled ${packageNames.join(', ')}`);
138
+ resolve({
139
+ failedPackages: [],
140
+ success: true
141
+ });
142
+ return;
108
143
  }
109
- // Always resolve, even on error
110
- resolve();
144
+ log.debug(`Batched uninstall failed (exit code: ${code}), continuing anyway`);
145
+ resolve({
146
+ failedPackages: [
147
+ ...packageNames
148
+ ],
149
+ success: false
150
+ });
111
151
  });
112
152
  });
113
153
  }
@@ -2,6 +2,7 @@
2
2
  * Project detection and scaffolding utilities
3
3
  */
4
4
  import type { ProjectInfo } from '../types/config.js';
5
+ import type { TemplateSource } from './download-template.js';
5
6
  import type { PackageManager } from './package-manager.js';
6
7
  /**
7
8
  * Detect if current directory has a Payload project
@@ -23,7 +24,7 @@ export declare function validatePayloadVersion(version: string): boolean;
23
24
  * @param projectPath - Path where to create the project
24
25
  * @param projectName - Name for the project
25
26
  */
26
- export declare function scaffoldProject(projectPath: string, projectName: string, packageManager?: PackageManager): Promise<void>;
27
+ export declare function scaffoldProject(projectPath: string, projectName: string, packageManager?: PackageManager, templateSource?: Partial<TemplateSource>): Promise<void>;
27
28
  /**
28
29
  * Initialize git repository after all setup is complete
29
30
  *
@@ -120,13 +120,13 @@ import { getOwnVersion } from './version-check.js';
120
120
  *
121
121
  * @param projectPath - Path where to create the project
122
122
  * @param projectName - Name for the project
123
- */ export async function scaffoldProject(projectPath, projectName, packageManager = 'npm') {
123
+ */ export async function scaffoldProject(projectPath, projectName, packageManager = 'npm', templateSource) {
124
124
  // Create project directory if it doesn't exist
125
125
  await fs.mkdir(projectPath, {
126
126
  recursive: true
127
127
  });
128
128
  // Download template from GitHub
129
- await downloadTemplateFromGitHub(projectPath);
129
+ await downloadTemplateFromGitHub(projectPath, templateSource);
130
130
  // Apply Lambda modifications BEFORE updating package.json
131
131
  // This ensures we don't overwrite version replacements
132
132
  await applyLambdaModifications(projectPath, packageManager);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payloadcms/figma",
3
- "version": "0.0.1-alpha.63",
3
+ "version": "0.0.1-alpha.65",
4
4
  "license": "SEE LICENSE IN LICENSE.md",
5
5
  "type": "module",
6
6
  "exports": {
@@ -47,10 +47,18 @@
47
47
  "uuid": "^10.0.0"
48
48
  },
49
49
  "devDependencies": {
50
+ "@payloadcms/eslint-config": "3.28.0",
51
+ "@payloadcms/eslint-plugin": "3.28.0",
52
+ "@swc/cli": "0.7.7",
50
53
  "@types/archiver": "7.0.0",
51
54
  "@types/cross-spawn": "6.0.6",
55
+ "@types/jsonwebtoken": "^9.0.10",
52
56
  "@types/node": "22.12.0",
57
+ "@types/uuid": "^8.3.4",
58
+ "copyfiles": "^2.4.1",
59
+ "eslint": "9.22.0",
53
60
  "openapi-typescript": "^7.13.0",
61
+ "rimraf": "^6.1.3",
54
62
  "tsx": "4.20.6",
55
63
  "typescript": "5.7.3",
56
64
  "vitest": "4.0.15"
@@ -1,98 +0,0 @@
1
- # Content API Database Adapter Parity
2
-
3
- This document explains how the Content API database adapter differs from Payload today and how it will behave once alignment is complete.
4
-
5
- The Content API is an internal service built exclusively for Payload. Because of that, the adapter should match Payload by default and only diverge when there is a strong technical reason.
6
-
7
- ## Where
8
-
9
- Now
10
- `{ path: "slug", operator: "equals", value: "hello", type: "text" }`
11
-
12
- Future
13
- `{ path: "slug", operator: "equals", value: "hello", type: "text" }`
14
-
15
- The format stays different. This allows the Content API to evolve its filter schema, for example to support composite types like Point, which are not lexicographically sortable. Semantics must remain identical to Payload.
16
-
17
- ## Pagination
18
-
19
- ~~Now~~
20
- ~~`{ limit: 10, offset: 20 }`~~
21
-
22
- ✅ Partially Implemented
23
- `{ limit: 10, page: 3 }`
24
-
25
- **Status:**
26
-
27
- - ✅ Content API main endpoints use `page` (matches Payload)
28
-
29
- - ⚠️ **Issue #1 (Joins):** `JoinClause` still uses `offset` instead of `page`
30
- - Payload `JoinQuery`: `{ page?: number, limit?: number }`
31
- - Content API `JoinClause`: `{ offset?: number, limit?: number }`
32
- - **Inconsistent** with main endpoints which use `page`
33
-
34
- **Current workaround for joins:** The adapter converts `page` to `offset`:
35
-
36
- ```typescript
37
- // Payload join query
38
- { page: 2, limit: 5 }
39
- // Adapter converts to offset for Content API
40
- { offset: 5, limit: 5 }
41
- ```
42
-
43
- **Future:**
44
-
45
- 1. Content API should support `skip` parameter for main endpoints
46
- 2. Content API `JoinClause` should use `page` instead of `offset` (consistent with main endpoints)
47
- 3. Content API pagination response should match Payload's flat structure
48
-
49
- ### Pagination Response Structure
50
-
51
- **Payload format (flat):**
52
-
53
- ```typescript
54
- {
55
- docs: [...],
56
- hasNextPage: boolean,
57
- hasPrevPage: boolean,
58
- limit: number,
59
- nextPage: number | null,
60
- page: number,
61
- pagingCounter: number,
62
- prevPage: number | null,
63
- totalDocs: number,
64
- totalPages: number
65
- }
66
- ```
67
-
68
- **Content API format (nested):**
69
-
70
- ```typescript
71
- {
72
- data: [...],
73
- pagination: {
74
- total: number,
75
- current: { limit: number, page: number },
76
- next?: { limit: number, page: number },
77
- prev?: { limit: number, page: number }
78
- }
79
- }
80
- ```
81
-
82
- **Current workaround:** The adapter transforms Content API's nested structure to Payload's flat structure using `getPaginationData()` utility.
83
-
84
- ## Slugs
85
-
86
- Now
87
- `{ collection: { key: "posts" } }`
88
-
89
- Future
90
- `{ collection: { slug: "posts" } }`
91
-
92
- Collection identifiers will match Payload. Consumers should not be aware of internal collection IDs.
93
-
94
- ## Temporary Conversions
95
-
96
- Helpers that translate Payload queries into the current Content API format are temporary and placed in the `temp-utilities` folder.
97
-
98
- They live in the `temp-utilities` folder until the Content API is fully aligned with Payload.