@payloadcms/figma 0.0.1-alpha.26 → 0.0.1-alpha.28

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 (55) hide show
  1. package/dist/api/control-plane.d.ts +18 -0
  2. package/dist/api/control-plane.d.ts.map +1 -1
  3. package/dist/api/control-plane.js +39 -8
  4. package/dist/api/control-plane.js.map +1 -1
  5. package/dist/api/figma-api.d.ts.map +1 -1
  6. package/dist/api/figma-api.js +5 -2
  7. package/dist/api/figma-api.js.map +1 -1
  8. package/dist/commands/deploy.d.ts.map +1 -1
  9. package/dist/commands/deploy.js +3 -18
  10. package/dist/commands/deploy.js.map +1 -1
  11. package/dist/commands/init.d.ts.map +1 -1
  12. package/dist/commands/init.js +59 -2
  13. package/dist/commands/init.js.map +1 -1
  14. package/dist/config/oauth.js +2 -1
  15. package/dist/config/oauth.js.map +1 -1
  16. package/dist/db-content-api/generated/content-api-types.d.ts +3129 -0
  17. package/dist/db-content-api/generated/content-api-types.d.ts.map +1 -0
  18. package/dist/db-content-api/generated/content-api-types.js +7 -0
  19. package/dist/db-content-api/generated/content-api-types.js.map +1 -0
  20. package/dist/db-content-api/index.d.ts +31 -0
  21. package/dist/db-content-api/index.d.ts.map +1 -0
  22. package/dist/db-content-api/index.js +842 -0
  23. package/dist/db-content-api/index.js.map +1 -0
  24. package/dist/db-content-api/types.d.ts +31 -0
  25. package/dist/db-content-api/types.d.ts.map +1 -0
  26. package/dist/db-content-api/types.js +7 -0
  27. package/dist/db-content-api/types.js.map +1 -0
  28. package/dist/index.d.ts +1 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +2 -0
  31. package/dist/index.js.map +1 -1
  32. package/dist/oauth/endpoints/getLoginEndpoint.js +1 -1
  33. package/dist/oauth/endpoints/getLoginEndpoint.js.map +1 -1
  34. package/dist/oauth/strategy/index.d.ts.map +1 -1
  35. package/dist/oauth/strategy/index.js +4 -0
  36. package/dist/oauth/strategy/index.js.map +1 -1
  37. package/dist/plugin/build-config.d.ts.map +1 -1
  38. package/dist/plugin/build-config.js +35 -17
  39. package/dist/plugin/build-config.js.map +1 -1
  40. package/dist/utils/payload-config-ast.d.ts +5 -0
  41. package/dist/utils/payload-config-ast.d.ts.map +1 -1
  42. package/dist/utils/payload-config-ast.js +29 -2
  43. package/dist/utils/payload-config-ast.js.map +1 -1
  44. package/dist/utils/payload-config-modifier.d.ts.map +1 -1
  45. package/dist/utils/payload-config-modifier.js +4 -0
  46. package/dist/utils/payload-config-modifier.js.map +1 -1
  47. package/dist/utils/payload-package-check.d.ts +5 -0
  48. package/dist/utils/payload-package-check.d.ts.map +1 -1
  49. package/dist/utils/payload-package-check.js +41 -0
  50. package/dist/utils/payload-package-check.js.map +1 -1
  51. package/package.json +3 -1
  52. package/dist/db-adapter.d.ts +0 -17
  53. package/dist/db-adapter.d.ts.map +0 -1
  54. package/dist/db-adapter.js +0 -760
  55. package/dist/db-adapter.js.map +0 -1
@@ -75,6 +75,15 @@ export interface PerformDeploymentApiResponse {
75
75
  };
76
76
  status: number;
77
77
  }
78
+ /**
79
+ * Response from getDatasetInfo endpoint
80
+ */
81
+ export type DatasetInfo = {
82
+ cmsId: string;
83
+ oauthClientId: string;
84
+ oauthClientSecret: string;
85
+ tenantId: string;
86
+ };
78
87
  /**
79
88
  * List all tenant instances for the authenticated user
80
89
  * Maps to: GET /v1/tenant
@@ -121,4 +130,13 @@ export declare function createDeployment(accessToken: string, tenantId: string,
121
130
  * @returns Deployment status and message
122
131
  */
123
132
  export declare function performDeployment(accessToken: string, tenantId: string, options: PerformDeploymentOptions): Promise<PerformDeploymentResponse>;
133
+ /**
134
+ * Get dataset info including tenant ID and OAuth credentials
135
+ * Maps to: GET /v1/cms/dataset/:datasetId
136
+ *
137
+ * @param accessToken - OAuth access token
138
+ * @param datasetId - Content System ID (dataset ID)
139
+ * @returns Dataset info with tenant ID and OAuth credentials
140
+ */
141
+ export declare function getDatasetInfo(accessToken: string, datasetId: string): Promise<DatasetInfo>;
124
142
  //# sourceMappingURL=control-plane.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"control-plane.d.ts","sourceRoot":"","sources":["../../src/api/control-plane.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAoBhD;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAGjC,UAAU,CAAC,EAAE,MAAM;IACnB,KAAK,CAAC,EAAE,KAAK;gBAFpB,OAAO,EAAE,MAAM,EACR,UAAU,CAAC,EAAE,MAAM,YAAA,EACnB,KAAK,CAAC,EAAE,KAAK,YAAA;CAKvB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,yBAAyB;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,2BAA2B;IAC3B,UAAU,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,WAAW,CAAA;CAC1C;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,uDAAuD;IACvD,YAAY,EAAE,MAAM,EAAE,CAAA;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,kDAAkD;IAClD,aAAa,EAAE,MAAM,CAAA;IACrB,2BAA2B;IAC3B,YAAY,EAAE,MAAM,CAAA;IACpB,+DAA+D;IAC/D,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC9C;AAED,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,OAAO,CAAA;IACd,IAAI,EAAE;QACJ,aAAa,EAAE,MAAM,CAAA;QACrB,qBAAqB,EAAE,MAAM,CAAA;QAC7B,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KACjD,CAAA;IACD,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,0CAA0C;IAC1C,YAAY,EAAE,MAAM,CAAA;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,sCAAsC;IACtC,YAAY,EAAE,MAAM,CAAA;IACpB,qBAAqB;IACrB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,OAAO,CAAA;IACd,IAAI,EAAE;QACJ,aAAa,EAAE,MAAM,CAAA;QACrB,OAAO,EAAE,MAAM,CAAA;QACf,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;IACD,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAyCxE;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,MAAM,CAAC,CAuCjB;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgC7F;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,wBAAwB,CAAC,CA8DnC;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,yBAAyB,CAAC,CA2CpC"}
1
+ {"version":3,"file":"control-plane.d.ts","sourceRoot":"","sources":["../../src/api/control-plane.ts"],"names":[],"mappings":"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"}
@@ -32,9 +32,7 @@ import * as log from '../utils/log.js';
32
32
  * @param accessToken - OAuth access token
33
33
  * @returns Array of tenant instances
34
34
  */ export async function listTenants(accessToken) {
35
- // Check if control plane API should be mocked
36
- // Default to true until real API is available
37
- const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE !== 'false';
35
+ const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE === 'true';
38
36
  if (shouldMock) {
39
37
  // MOCK IMPLEMENTATION: Return sample tenants for testing
40
38
  log.debug('Using mock Control Plane API for listTenants (FIGMA_MOCK_CONTROL_PLANE != "false")');
@@ -77,7 +75,7 @@ import * as log from '../utils/log.js';
77
75
  * @returns Newly created tenant
78
76
  */ export async function createTenant(accessToken, options) {
79
77
  // Check if control plane API should be mocked
80
- const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE !== 'false';
78
+ const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE === 'true';
81
79
  if (shouldMock) {
82
80
  // MOCK IMPLEMENTATION: Generate mock tenant for testing
83
81
  log.debug('Using mock Control Plane API for createTenant (FIGMA_MOCK_CONTROL_PLANE != "false")');
@@ -118,7 +116,7 @@ import * as log from '../utils/log.js';
118
116
  * @returns Tenant details
119
117
  */ export async function getTenantDetails(accessToken, tenantId) {
120
118
  // Check if control plane API should be mocked
121
- const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE !== 'false';
119
+ const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE === 'true';
122
120
  if (shouldMock) {
123
121
  // MOCK IMPLEMENTATION: Return mock tenant details for testing
124
122
  log.debug('Using mock Control Plane API for getTenantDetails (FIGMA_MOCK_CONTROL_PLANE != "false")');
@@ -152,7 +150,7 @@ import * as log from '../utils/log.js';
152
150
  * @returns Deployment ID and upload URLs
153
151
  */ export async function createDeployment(accessToken, tenantId, options) {
154
152
  // Check if control plane API should be mocked
155
- const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE !== 'false';
153
+ const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE === 'true';
156
154
  if (shouldMock) {
157
155
  // MOCK IMPLEMENTATION: Generate mock deployment and signed URLs
158
156
  log.debug('Using mock Control Plane API for createDeployment (FIGMA_MOCK_CONTROL_PLANE === "true")');
@@ -207,8 +205,7 @@ import * as log from '../utils/log.js';
207
205
  * @param options - Deployment perform options
208
206
  * @returns Deployment status and message
209
207
  */ export async function performDeployment(accessToken, tenantId, options) {
210
- // Check if control plane API should be mocked
211
- const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE !== 'false';
208
+ const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE === 'true';
212
209
  if (shouldMock) {
213
210
  // MOCK IMPLEMENTATION: Return mock deployment status
214
211
  log.debug('Using mock Control Plane API for performDeployment (FIGMA_MOCK_CONTROL_PLANE != "false")');
@@ -240,5 +237,39 @@ import * as log from '../utils/log.js';
240
237
  message: data.meta.message || 'Deployment initiated'
241
238
  };
242
239
  }
240
+ /**
241
+ * Get dataset info including tenant ID and OAuth credentials
242
+ * Maps to: GET /v1/cms/dataset/:datasetId
243
+ *
244
+ * @param accessToken - OAuth access token
245
+ * @param datasetId - Content System ID (dataset ID)
246
+ * @returns Dataset info with tenant ID and OAuth credentials
247
+ */ export async function getDatasetInfo(accessToken, datasetId) {
248
+ const url = `${getControlPlaneBaseUrl()}/v1/cms/dataset/${datasetId}`;
249
+ log.debug(`Calling getDatasetInfo API at ${url}`);
250
+ const response = await fetch(url, {
251
+ headers: {
252
+ Authorization: `Bearer ${accessToken}`
253
+ }
254
+ });
255
+ if (!response.ok) {
256
+ throw new ControlPlaneError(`Failed to get dataset info: ${response.status} ${response.statusText}`, response.status);
257
+ }
258
+ const data = await response.json();
259
+ const sanitizedData = {
260
+ ...data,
261
+ meta: {
262
+ ...data.meta,
263
+ oauth_client_secret: '[REDACTED]'
264
+ }
265
+ };
266
+ log.debug(`Dataset API response: ${JSON.stringify(sanitizedData, null, 2)}`);
267
+ return {
268
+ cmsId: data.meta.cms_resource_id,
269
+ oauthClientId: data.meta.oauth_client_id,
270
+ oauthClientSecret: data.meta.oauth_client_secret,
271
+ tenantId: data.meta.tenant_instance_id
272
+ };
273
+ }
243
274
 
244
275
  //# sourceMappingURL=control-plane.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/api/control-plane.ts"],"sourcesContent":["/**\n * Control Plane API Client\n *\n * TODO: Replace with real API calls when Control Plane is available\n */\n\nimport type { Tenant } from '../types/config.js'\n\nimport { getEnvironment } from '../constants.js'\nimport * as log from '../utils/log.js'\n\n/**\n * Control Plane API base URLs\n */\nconst CONTROL_PLANE_API_URLS = {\n production: 'https://api.figma.com',\n staging: process.env.FIGMA_API_URL || 'https://api.staging.figma.com',\n}\n\n/**\n * Get the Control Plane API base URL based on environment\n */\nfunction getControlPlaneBaseUrl(): string {\n return process.env.FIGMA_API_BASE_URL || CONTROL_PLANE_API_URLS[getEnvironment()]\n}\n\n/**\n * Error thrown when Control Plane API calls fail\n */\nexport class ControlPlaneError extends Error {\n constructor(\n message: string,\n public statusCode?: number,\n public cause?: Error,\n ) {\n super(message)\n this.name = 'ControlPlaneError'\n }\n}\n\n/**\n * Options for creating a new tenant\n */\nexport interface CreateTenantOptions {\n /** Name for the new CMS instance */\n name: string\n /** Optional parent ID */\n parentId?: string\n /** Optional parent type */\n parentType?: 'org' | 'team' | 'workspace'\n}\n\n/**\n * Options for creating a new deployment\n */\nexport interface CreateDeploymentOptions {\n /** List of static asset paths that need upload URLs */\n staticAssets: string[]\n}\n\n/**\n * Response from creating a new deployment\n */\nexport interface CreateDeploymentResponse {\n /** Upload URL for the Lambda function code zip */\n codeUploadUrl: string\n /** Unique deployment ID */\n deploymentId: string\n /** Map of static asset paths to their signed S3 upload URLs */\n staticAssetUploadUrls: Record<string, string>\n}\n\nexport interface CreateDeploymentApiResponse {\n error: boolean\n meta: {\n deployment_id: string\n lambda_zip_upload_url: string\n static_asset_upload_urls: Record<string, string>\n }\n status: number\n}\n\n/**\n * Options for performing a deployment\n */\nexport interface PerformDeploymentOptions {\n /** Deployment ID from createDeployment */\n deploymentId: string\n}\n\n/**\n * Response from performing a deployment\n */\nexport interface PerformDeploymentResponse {\n /** Deployment ID that was deployed */\n deploymentId: string\n /** Status message */\n message: string\n}\n\nexport interface PerformDeploymentApiResponse {\n error: boolean\n meta: {\n deployment_id: string\n message: string\n status: string\n }\n status: number\n}\n\n/**\n * List all tenant instances for the authenticated user\n * Maps to: GET /v1/tenant\n *\n * @param accessToken - OAuth access token\n * @returns Array of tenant instances\n */\nexport async function listTenants(accessToken: string): Promise<Tenant[]> {\n // Check if control plane API should be mocked\n // Default to true until real API is available\n const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE !== 'false'\n\n if (shouldMock) {\n // MOCK IMPLEMENTATION: Return sample tenants for testing\n log.debug('Using mock Control Plane API for listTenants (FIGMA_MOCK_CONTROL_PLANE != \"false\")')\n return [\n {\n id: 'tenant_abc123',\n createdAt: '2025-01-15T10:00:00Z',\n domain: 'my-blog.figmacms.com',\n parentId: 'team_456',\n parentType: 'team',\n status: 'active',\n },\n {\n id: 'tenant_def456',\n createdAt: '2025-01-10T14:30:00Z',\n domain: 'my-store.figmacms.com',\n parentId: 'team_456',\n parentType: 'team',\n status: 'active',\n },\n ]\n }\n\n // REAL API IMPLEMENTATION\n const response = await fetch(`${getControlPlaneBaseUrl()}/v1/tenant`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n })\n\n if (!response.ok) {\n throw new ControlPlaneError(\n `Failed to list tenants: ${response.status} ${response.statusText}`,\n response.status,\n )\n }\n\n return response.json()\n}\n\n/**\n * Create a new tenant instance\n * Maps to: POST /v1/tenant\n *\n * @param accessToken - OAuth access token\n * @param options - Tenant creation options\n * @returns Newly created tenant\n */\nexport async function createTenant(\n accessToken: string,\n options: CreateTenantOptions,\n): Promise<Tenant> {\n // Check if control plane API should be mocked\n const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE !== 'false'\n\n if (shouldMock) {\n // MOCK IMPLEMENTATION: Generate mock tenant for testing\n log.debug('Using mock Control Plane API for createTenant (FIGMA_MOCK_CONTROL_PLANE != \"false\")')\n const tenantId = `tenant_${Math.random().toString(36).substring(2, 11)}`\n const slug = options.name.toLowerCase().replace(/[^a-z0-9]+/g, '-')\n const domain = `${slug}.figmacms.com`\n\n return {\n id: tenantId,\n createdAt: new Date().toISOString(),\n domain,\n parentId: options.parentId || 'team_456',\n parentType: options.parentType || 'team',\n status: 'provisioning',\n }\n }\n\n // REAL API IMPLEMENTATION\n const response = await fetch(`${getControlPlaneBaseUrl()}/v1/tenant`, {\n body: JSON.stringify({ name: options.name }),\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n\n if (!response.ok) {\n throw new ControlPlaneError(\n `Failed to create tenant: ${response.status} ${response.statusText}`,\n response.status,\n )\n }\n\n return response.json()\n}\n\n/**\n * Get details for a specific tenant\n * Maps to: GET /v1/tenant/{tenant_id}\n *\n * @param accessToken - OAuth access token\n * @param tenantId - Tenant ID\n * @returns Tenant details\n */\nexport async function getTenantDetails(accessToken: string, tenantId: string): Promise<Tenant> {\n // Check if control plane API should be mocked\n const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE !== 'false'\n\n if (shouldMock) {\n // MOCK IMPLEMENTATION: Return mock tenant details for testing\n log.debug(\n 'Using mock Control Plane API for getTenantDetails (FIGMA_MOCK_CONTROL_PLANE != \"false\")',\n )\n return {\n id: tenantId,\n createdAt: '2025-01-15T10:00:00Z',\n domain: 'example.figmacms.com',\n parentId: 'team_456',\n parentType: 'team',\n status: 'active',\n }\n }\n\n // REAL API IMPLEMENTATION\n const response = await fetch(`${getControlPlaneBaseUrl()}/v1/tenant/${tenantId}`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n })\n\n if (!response.ok) {\n throw new ControlPlaneError(\n `Failed to get tenant details: ${response.status} ${response.statusText}`,\n response.status,\n )\n }\n\n return response.json()\n}\n\n/**\n * Create a new deployment and get signed upload URLs\n * Maps to: POST /v1/tenant/{tenant_id}/deploy/create\n *\n * @param accessToken - OAuth access token\n * @param tenantId - Tenant ID\n * @param options - Deployment creation options\n * @returns Deployment ID and upload URLs\n */\nexport async function createDeployment(\n accessToken: string,\n tenantId: string,\n options: CreateDeploymentOptions,\n): Promise<CreateDeploymentResponse> {\n // Check if control plane API should be mocked\n const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE !== 'false'\n\n if (shouldMock) {\n // MOCK IMPLEMENTATION: Generate mock deployment and signed URLs\n log.debug(\n 'Using mock Control Plane API for createDeployment (FIGMA_MOCK_CONTROL_PLANE === \"true\")',\n )\n\n const deploymentId = `deploy_${Math.random().toString(36).substring(2, 11)}`\n const mockBucket = 'figma-cms-deployments-mock'\n const mockRegion = 'us-west-2'\n const mockBaseUrl = `https://${mockBucket}.s3.${mockRegion}.amazonaws.com`\n\n // Generate mock signed URLs for static assets\n const staticAssetUploadUrls: Record<string, string> = {}\n for (const assetPath of options.staticAssets) {\n const encodedPath = encodeURIComponent(assetPath)\n staticAssetUploadUrls[assetPath] =\n `${mockBaseUrl}/${tenantId}/${deploymentId}/static/${encodedPath}?X-Amz-Signature=mock`\n }\n\n // Generate mock signed URL for code zip\n const codeUploadUrl = `${mockBaseUrl}/${tenantId}/${deploymentId}/lambda.zip?X-Amz-Signature=mock`\n\n return {\n codeUploadUrl,\n deploymentId,\n staticAssetUploadUrls,\n }\n }\n\n const url = `${getControlPlaneBaseUrl()}/v1/cms/tenant/${tenantId}/deploy/create`\n\n log.debug(`Calling createDeployment API at ${url}`)\n\n // REAL API IMPLEMENTATION\n const response = await fetch(url, {\n body: JSON.stringify({ static_assets: options.staticAssets }),\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n\n if (!response.ok) {\n throw new ControlPlaneError(\n `Failed to create deployment: ${response.status} ${response.statusText}`,\n response.status,\n )\n }\n\n const data = (await response.json()) as CreateDeploymentApiResponse\n\n // Map API response format to our interface\n return {\n codeUploadUrl: data.meta.lambda_zip_upload_url,\n deploymentId: data.meta.deployment_id,\n staticAssetUploadUrls: data.meta.static_asset_upload_urls,\n }\n}\n\n/**\n * Perform deployment for a created deployment ID\n * Maps to: POST /v1/tenant/{tenant_id}/deploy/perform\n *\n * @param accessToken - OAuth access token\n * @param tenantId - Tenant ID\n * @param options - Deployment perform options\n * @returns Deployment status and message\n */\nexport async function performDeployment(\n accessToken: string,\n tenantId: string,\n options: PerformDeploymentOptions,\n): Promise<PerformDeploymentResponse> {\n // Check if control plane API should be mocked\n const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE !== 'false'\n\n if (shouldMock) {\n // MOCK IMPLEMENTATION: Return mock deployment status\n log.debug(\n 'Using mock Control Plane API for performDeployment (FIGMA_MOCK_CONTROL_PLANE != \"false\")',\n )\n\n return {\n deploymentId: options.deploymentId,\n message: 'Deployment initiated successfully',\n }\n }\n\n const url = `${getControlPlaneBaseUrl()}/v1/cms/tenant/${tenantId}/deploy/perform`\n log.debug(`Calling performDeployment API at ${url}`)\n\n // REAL API IMPLEMENTATION\n const response = await fetch(url, {\n body: JSON.stringify({ deployment_id: options.deploymentId }),\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n\n if (!response.ok) {\n throw new ControlPlaneError(\n `Failed to perform deployment: ${response.status} ${response.statusText}`,\n response.status,\n )\n }\n\n const data = (await response.json()) as PerformDeploymentApiResponse\n\n // Map API response to our interface\n return {\n deploymentId: data.meta.deployment_id,\n message: data.meta.message || 'Deployment initiated',\n }\n}\n"],"names":["getEnvironment","log","CONTROL_PLANE_API_URLS","production","staging","process","env","FIGMA_API_URL","getControlPlaneBaseUrl","FIGMA_API_BASE_URL","ControlPlaneError","Error","message","statusCode","cause","name","listTenants","accessToken","shouldMock","FIGMA_MOCK_CONTROL_PLANE","debug","id","createdAt","domain","parentId","parentType","status","response","fetch","headers","Authorization","ok","statusText","json","createTenant","options","tenantId","Math","random","toString","substring","slug","toLowerCase","replace","Date","toISOString","body","JSON","stringify","method","getTenantDetails","createDeployment","deploymentId","mockBucket","mockRegion","mockBaseUrl","staticAssetUploadUrls","assetPath","staticAssets","encodedPath","encodeURIComponent","codeUploadUrl","url","static_assets","data","meta","lambda_zip_upload_url","deployment_id","static_asset_upload_urls","performDeployment"],"mappings":"AAAA;;;;CAIC,GAID,SAASA,cAAc,QAAQ,kBAAiB;AAChD,YAAYC,SAAS,kBAAiB;AAEtC;;CAEC,GACD,MAAMC,yBAAyB;IAC7BC,YAAY;IACZC,SAASC,QAAQC,GAAG,CAACC,aAAa,IAAI;AACxC;AAEA;;CAEC,GACD,SAASC;IACP,OAAOH,QAAQC,GAAG,CAACG,kBAAkB,IAAIP,sBAAsB,CAACF,iBAAiB;AACnF;AAEA;;CAEC,GACD,OAAO,MAAMU,0BAA0BC;;;IACrC,YACEC,OAAe,EACf,AAAOC,UAAmB,EAC1B,AAAOC,KAAa,CACpB;QACA,KAAK,CAACF,eAHCC,aAAAA,iBACAC,QAAAA;QAGP,IAAI,CAACC,IAAI,GAAG;IACd;AACF;AAwEA;;;;;;CAMC,GACD,OAAO,eAAeC,YAAYC,WAAmB;IACnD,8CAA8C;IAC9C,8CAA8C;IAC9C,MAAMC,aAAab,QAAQC,GAAG,CAACa,wBAAwB,KAAK;IAE5D,IAAID,YAAY;QACd,yDAAyD;QACzDjB,IAAImB,KAAK,CAAC;QACV,OAAO;YACL;gBACEC,IAAI;gBACJC,WAAW;gBACXC,QAAQ;gBACRC,UAAU;gBACVC,YAAY;gBACZC,QAAQ;YACV;YACA;gBACEL,IAAI;gBACJC,WAAW;gBACXC,QAAQ;gBACRC,UAAU;gBACVC,YAAY;gBACZC,QAAQ;YACV;SACD;IACH;IAEA,0BAA0B;IAC1B,MAAMC,WAAW,MAAMC,MAAM,GAAGpB,yBAAyB,UAAU,CAAC,EAAE;QACpEqB,SAAS;YAAEC,eAAe,CAAC,OAAO,EAAEb,aAAa;QAAC;IACpD;IAEA,IAAI,CAACU,SAASI,EAAE,EAAE;QAChB,MAAM,IAAIrB,kBACR,CAAC,wBAAwB,EAAEiB,SAASD,MAAM,CAAC,CAAC,EAAEC,SAASK,UAAU,EAAE,EACnEL,SAASD,MAAM;IAEnB;IAEA,OAAOC,SAASM,IAAI;AACtB;AAEA;;;;;;;CAOC,GACD,OAAO,eAAeC,aACpBjB,WAAmB,EACnBkB,OAA4B;IAE5B,8CAA8C;IAC9C,MAAMjB,aAAab,QAAQC,GAAG,CAACa,wBAAwB,KAAK;IAE5D,IAAID,YAAY;QACd,wDAAwD;QACxDjB,IAAImB,KAAK,CAAC;QACV,MAAMgB,WAAW,CAAC,OAAO,EAAEC,KAAKC,MAAM,GAAGC,QAAQ,CAAC,IAAIC,SAAS,CAAC,GAAG,KAAK;QACxE,MAAMC,OAAON,QAAQpB,IAAI,CAAC2B,WAAW,GAAGC,OAAO,CAAC,eAAe;QAC/D,MAAMpB,SAAS,GAAGkB,KAAK,aAAa,CAAC;QAErC,OAAO;YACLpB,IAAIe;YACJd,WAAW,IAAIsB,OAAOC,WAAW;YACjCtB;YACAC,UAAUW,QAAQX,QAAQ,IAAI;YAC9BC,YAAYU,QAAQV,UAAU,IAAI;YAClCC,QAAQ;QACV;IACF;IAEA,0BAA0B;IAC1B,MAAMC,WAAW,MAAMC,MAAM,GAAGpB,yBAAyB,UAAU,CAAC,EAAE;QACpEsC,MAAMC,KAAKC,SAAS,CAAC;YAAEjC,MAAMoB,QAAQpB,IAAI;QAAC;QAC1Cc,SAAS;YACPC,eAAe,CAAC,OAAO,EAAEb,aAAa;YACtC,gBAAgB;QAClB;QACAgC,QAAQ;IACV;IAEA,IAAI,CAACtB,SAASI,EAAE,EAAE;QAChB,MAAM,IAAIrB,kBACR,CAAC,yBAAyB,EAAEiB,SAASD,MAAM,CAAC,CAAC,EAAEC,SAASK,UAAU,EAAE,EACpEL,SAASD,MAAM;IAEnB;IAEA,OAAOC,SAASM,IAAI;AACtB;AAEA;;;;;;;CAOC,GACD,OAAO,eAAeiB,iBAAiBjC,WAAmB,EAAEmB,QAAgB;IAC1E,8CAA8C;IAC9C,MAAMlB,aAAab,QAAQC,GAAG,CAACa,wBAAwB,KAAK;IAE5D,IAAID,YAAY;QACd,8DAA8D;QAC9DjB,IAAImB,KAAK,CACP;QAEF,OAAO;YACLC,IAAIe;YACJd,WAAW;YACXC,QAAQ;YACRC,UAAU;YACVC,YAAY;YACZC,QAAQ;QACV;IACF;IAEA,0BAA0B;IAC1B,MAAMC,WAAW,MAAMC,MAAM,GAAGpB,yBAAyB,WAAW,EAAE4B,UAAU,EAAE;QAChFP,SAAS;YAAEC,eAAe,CAAC,OAAO,EAAEb,aAAa;QAAC;IACpD;IAEA,IAAI,CAACU,SAASI,EAAE,EAAE;QAChB,MAAM,IAAIrB,kBACR,CAAC,8BAA8B,EAAEiB,SAASD,MAAM,CAAC,CAAC,EAAEC,SAASK,UAAU,EAAE,EACzEL,SAASD,MAAM;IAEnB;IAEA,OAAOC,SAASM,IAAI;AACtB;AAEA;;;;;;;;CAQC,GACD,OAAO,eAAekB,iBACpBlC,WAAmB,EACnBmB,QAAgB,EAChBD,OAAgC;IAEhC,8CAA8C;IAC9C,MAAMjB,aAAab,QAAQC,GAAG,CAACa,wBAAwB,KAAK;IAE5D,IAAID,YAAY;QACd,gEAAgE;QAChEjB,IAAImB,KAAK,CACP;QAGF,MAAMgC,eAAe,CAAC,OAAO,EAAEf,KAAKC,MAAM,GAAGC,QAAQ,CAAC,IAAIC,SAAS,CAAC,GAAG,KAAK;QAC5E,MAAMa,aAAa;QACnB,MAAMC,aAAa;QACnB,MAAMC,cAAc,CAAC,QAAQ,EAAEF,WAAW,IAAI,EAAEC,WAAW,cAAc,CAAC;QAE1E,8CAA8C;QAC9C,MAAME,wBAAgD,CAAC;QACvD,KAAK,MAAMC,aAAatB,QAAQuB,YAAY,CAAE;YAC5C,MAAMC,cAAcC,mBAAmBH;YACvCD,qBAAqB,CAACC,UAAU,GAC9B,GAAGF,YAAY,CAAC,EAAEnB,SAAS,CAAC,EAAEgB,aAAa,QAAQ,EAAEO,YAAY,qBAAqB,CAAC;QAC3F;QAEA,wCAAwC;QACxC,MAAME,gBAAgB,GAAGN,YAAY,CAAC,EAAEnB,SAAS,CAAC,EAAEgB,aAAa,gCAAgC,CAAC;QAElG,OAAO;YACLS;YACAT;YACAI;QACF;IACF;IAEA,MAAMM,MAAM,GAAGtD,yBAAyB,eAAe,EAAE4B,SAAS,cAAc,CAAC;IAEjFnC,IAAImB,KAAK,CAAC,CAAC,gCAAgC,EAAE0C,KAAK;IAElD,0BAA0B;IAC1B,MAAMnC,WAAW,MAAMC,MAAMkC,KAAK;QAChChB,MAAMC,KAAKC,SAAS,CAAC;YAAEe,eAAe5B,QAAQuB,YAAY;QAAC;QAC3D7B,SAAS;YACPC,eAAe,CAAC,OAAO,EAAEb,aAAa;YACtC,gBAAgB;QAClB;QACAgC,QAAQ;IACV;IAEA,IAAI,CAACtB,SAASI,EAAE,EAAE;QAChB,MAAM,IAAIrB,kBACR,CAAC,6BAA6B,EAAEiB,SAASD,MAAM,CAAC,CAAC,EAAEC,SAASK,UAAU,EAAE,EACxEL,SAASD,MAAM;IAEnB;IAEA,MAAMsC,OAAQ,MAAMrC,SAASM,IAAI;IAEjC,2CAA2C;IAC3C,OAAO;QACL4B,eAAeG,KAAKC,IAAI,CAACC,qBAAqB;QAC9Cd,cAAcY,KAAKC,IAAI,CAACE,aAAa;QACrCX,uBAAuBQ,KAAKC,IAAI,CAACG,wBAAwB;IAC3D;AACF;AAEA;;;;;;;;CAQC,GACD,OAAO,eAAeC,kBACpBpD,WAAmB,EACnBmB,QAAgB,EAChBD,OAAiC;IAEjC,8CAA8C;IAC9C,MAAMjB,aAAab,QAAQC,GAAG,CAACa,wBAAwB,KAAK;IAE5D,IAAID,YAAY;QACd,qDAAqD;QACrDjB,IAAImB,KAAK,CACP;QAGF,OAAO;YACLgC,cAAcjB,QAAQiB,YAAY;YAClCxC,SAAS;QACX;IACF;IAEA,MAAMkD,MAAM,GAAGtD,yBAAyB,eAAe,EAAE4B,SAAS,eAAe,CAAC;IAClFnC,IAAImB,KAAK,CAAC,CAAC,iCAAiC,EAAE0C,KAAK;IAEnD,0BAA0B;IAC1B,MAAMnC,WAAW,MAAMC,MAAMkC,KAAK;QAChChB,MAAMC,KAAKC,SAAS,CAAC;YAAEmB,eAAehC,QAAQiB,YAAY;QAAC;QAC3DvB,SAAS;YACPC,eAAe,CAAC,OAAO,EAAEb,aAAa;YACtC,gBAAgB;QAClB;QACAgC,QAAQ;IACV;IAEA,IAAI,CAACtB,SAASI,EAAE,EAAE;QAChB,MAAM,IAAIrB,kBACR,CAAC,8BAA8B,EAAEiB,SAASD,MAAM,CAAC,CAAC,EAAEC,SAASK,UAAU,EAAE,EACzEL,SAASD,MAAM;IAEnB;IAEA,MAAMsC,OAAQ,MAAMrC,SAASM,IAAI;IAEjC,oCAAoC;IACpC,OAAO;QACLmB,cAAcY,KAAKC,IAAI,CAACE,aAAa;QACrCvD,SAASoD,KAAKC,IAAI,CAACrD,OAAO,IAAI;IAChC;AACF"}
1
+ {"version":3,"sources":["../../src/api/control-plane.ts"],"sourcesContent":["/**\n * Control Plane API Client\n *\n * TODO: Replace with real API calls when Control Plane is available\n */\n\nimport type { Tenant } from '../types/config.js'\n\nimport { getEnvironment } from '../constants.js'\nimport * as log from '../utils/log.js'\n\n/**\n * Control Plane API base URLs\n */\nconst CONTROL_PLANE_API_URLS = {\n production: 'https://api.figma.com',\n staging: process.env.FIGMA_API_URL || 'https://api.staging.figma.com',\n}\n\n/**\n * Get the Control Plane API base URL based on environment\n */\nfunction getControlPlaneBaseUrl(): string {\n return process.env.FIGMA_API_BASE_URL || CONTROL_PLANE_API_URLS[getEnvironment()]\n}\n\n/**\n * Error thrown when Control Plane API calls fail\n */\nexport class ControlPlaneError extends Error {\n constructor(\n message: string,\n public statusCode?: number,\n public cause?: Error,\n ) {\n super(message)\n this.name = 'ControlPlaneError'\n }\n}\n\n/**\n * Options for creating a new tenant\n */\nexport interface CreateTenantOptions {\n /** Name for the new CMS instance */\n name: string\n /** Optional parent ID */\n parentId?: string\n /** Optional parent type */\n parentType?: 'org' | 'team' | 'workspace'\n}\n\n/**\n * Options for creating a new deployment\n */\nexport interface CreateDeploymentOptions {\n /** List of static asset paths that need upload URLs */\n staticAssets: string[]\n}\n\n/**\n * Response from creating a new deployment\n */\nexport interface CreateDeploymentResponse {\n /** Upload URL for the Lambda function code zip */\n codeUploadUrl: string\n /** Unique deployment ID */\n deploymentId: string\n /** Map of static asset paths to their signed S3 upload URLs */\n staticAssetUploadUrls: Record<string, string>\n}\n\nexport interface CreateDeploymentApiResponse {\n error: boolean\n meta: {\n deployment_id: string\n lambda_zip_upload_url: string\n static_asset_upload_urls: Record<string, string>\n }\n status: number\n}\n\n/**\n * Options for performing a deployment\n */\nexport interface PerformDeploymentOptions {\n /** Deployment ID from createDeployment */\n deploymentId: string\n}\n\n/**\n * Response from performing a deployment\n */\nexport interface PerformDeploymentResponse {\n /** Deployment ID that was deployed */\n deploymentId: string\n /** Status message */\n message: string\n}\n\nexport interface PerformDeploymentApiResponse {\n error: boolean\n meta: {\n deployment_id: string\n message: string\n status: string\n }\n status: number\n}\n\n/**\n * Response from getDatasetInfo endpoint\n */\nexport type DatasetInfo = {\n cmsId: string\n oauthClientId: string\n oauthClientSecret: string\n tenantId: string\n}\n\n/**\n * Raw API response from /v1/cms/dataset/:datasetId\n */\ntype DatasetInfoApiResponse = {\n error: boolean\n meta: {\n cms_resource_id: string\n oauth_client_id: string\n oauth_client_secret: string\n tenant_instance_id: string\n }\n status: number\n}\n\n/**\n * List all tenant instances for the authenticated user\n * Maps to: GET /v1/tenant\n *\n * @param accessToken - OAuth access token\n * @returns Array of tenant instances\n */\nexport async function listTenants(accessToken: string): Promise<Tenant[]> {\n const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE === 'true'\n\n if (shouldMock) {\n // MOCK IMPLEMENTATION: Return sample tenants for testing\n log.debug('Using mock Control Plane API for listTenants (FIGMA_MOCK_CONTROL_PLANE != \"false\")')\n return [\n {\n id: 'tenant_abc123',\n createdAt: '2025-01-15T10:00:00Z',\n domain: 'my-blog.figmacms.com',\n parentId: 'team_456',\n parentType: 'team',\n status: 'active',\n },\n {\n id: 'tenant_def456',\n createdAt: '2025-01-10T14:30:00Z',\n domain: 'my-store.figmacms.com',\n parentId: 'team_456',\n parentType: 'team',\n status: 'active',\n },\n ]\n }\n\n // REAL API IMPLEMENTATION\n const response = await fetch(`${getControlPlaneBaseUrl()}/v1/tenant`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n })\n\n if (!response.ok) {\n throw new ControlPlaneError(\n `Failed to list tenants: ${response.status} ${response.statusText}`,\n response.status,\n )\n }\n\n return response.json()\n}\n\n/**\n * Create a new tenant instance\n * Maps to: POST /v1/tenant\n *\n * @param accessToken - OAuth access token\n * @param options - Tenant creation options\n * @returns Newly created tenant\n */\nexport async function createTenant(\n accessToken: string,\n options: CreateTenantOptions,\n): Promise<Tenant> {\n // Check if control plane API should be mocked\n const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE === 'true'\n\n if (shouldMock) {\n // MOCK IMPLEMENTATION: Generate mock tenant for testing\n log.debug('Using mock Control Plane API for createTenant (FIGMA_MOCK_CONTROL_PLANE != \"false\")')\n const tenantId = `tenant_${Math.random().toString(36).substring(2, 11)}`\n const slug = options.name.toLowerCase().replace(/[^a-z0-9]+/g, '-')\n const domain = `${slug}.figmacms.com`\n\n return {\n id: tenantId,\n createdAt: new Date().toISOString(),\n domain,\n parentId: options.parentId || 'team_456',\n parentType: options.parentType || 'team',\n status: 'provisioning',\n }\n }\n\n // REAL API IMPLEMENTATION\n const response = await fetch(`${getControlPlaneBaseUrl()}/v1/tenant`, {\n body: JSON.stringify({ name: options.name }),\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n\n if (!response.ok) {\n throw new ControlPlaneError(\n `Failed to create tenant: ${response.status} ${response.statusText}`,\n response.status,\n )\n }\n\n return response.json()\n}\n\n/**\n * Get details for a specific tenant\n * Maps to: GET /v1/tenant/{tenant_id}\n *\n * @param accessToken - OAuth access token\n * @param tenantId - Tenant ID\n * @returns Tenant details\n */\nexport async function getTenantDetails(accessToken: string, tenantId: string): Promise<Tenant> {\n // Check if control plane API should be mocked\n const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE === 'true'\n\n if (shouldMock) {\n // MOCK IMPLEMENTATION: Return mock tenant details for testing\n log.debug(\n 'Using mock Control Plane API for getTenantDetails (FIGMA_MOCK_CONTROL_PLANE != \"false\")',\n )\n return {\n id: tenantId,\n createdAt: '2025-01-15T10:00:00Z',\n domain: 'example.figmacms.com',\n parentId: 'team_456',\n parentType: 'team',\n status: 'active',\n }\n }\n\n // REAL API IMPLEMENTATION\n const response = await fetch(`${getControlPlaneBaseUrl()}/v1/tenant/${tenantId}`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n })\n\n if (!response.ok) {\n throw new ControlPlaneError(\n `Failed to get tenant details: ${response.status} ${response.statusText}`,\n response.status,\n )\n }\n\n return response.json()\n}\n\n/**\n * Create a new deployment and get signed upload URLs\n * Maps to: POST /v1/tenant/{tenant_id}/deploy/create\n *\n * @param accessToken - OAuth access token\n * @param tenantId - Tenant ID\n * @param options - Deployment creation options\n * @returns Deployment ID and upload URLs\n */\nexport async function createDeployment(\n accessToken: string,\n tenantId: string,\n options: CreateDeploymentOptions,\n): Promise<CreateDeploymentResponse> {\n // Check if control plane API should be mocked\n const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE === 'true'\n\n if (shouldMock) {\n // MOCK IMPLEMENTATION: Generate mock deployment and signed URLs\n log.debug(\n 'Using mock Control Plane API for createDeployment (FIGMA_MOCK_CONTROL_PLANE === \"true\")',\n )\n\n const deploymentId = `deploy_${Math.random().toString(36).substring(2, 11)}`\n const mockBucket = 'figma-cms-deployments-mock'\n const mockRegion = 'us-west-2'\n const mockBaseUrl = `https://${mockBucket}.s3.${mockRegion}.amazonaws.com`\n\n // Generate mock signed URLs for static assets\n const staticAssetUploadUrls: Record<string, string> = {}\n for (const assetPath of options.staticAssets) {\n const encodedPath = encodeURIComponent(assetPath)\n staticAssetUploadUrls[assetPath] =\n `${mockBaseUrl}/${tenantId}/${deploymentId}/static/${encodedPath}?X-Amz-Signature=mock`\n }\n\n // Generate mock signed URL for code zip\n const codeUploadUrl = `${mockBaseUrl}/${tenantId}/${deploymentId}/lambda.zip?X-Amz-Signature=mock`\n\n return {\n codeUploadUrl,\n deploymentId,\n staticAssetUploadUrls,\n }\n }\n\n const url = `${getControlPlaneBaseUrl()}/v1/cms/tenant/${tenantId}/deploy/create`\n\n log.debug(`Calling createDeployment API at ${url}`)\n\n // REAL API IMPLEMENTATION\n const response = await fetch(url, {\n body: JSON.stringify({ static_assets: options.staticAssets }),\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n\n if (!response.ok) {\n throw new ControlPlaneError(\n `Failed to create deployment: ${response.status} ${response.statusText}`,\n response.status,\n )\n }\n\n const data = (await response.json()) as CreateDeploymentApiResponse\n\n // Map API response format to our interface\n return {\n codeUploadUrl: data.meta.lambda_zip_upload_url,\n deploymentId: data.meta.deployment_id,\n staticAssetUploadUrls: data.meta.static_asset_upload_urls,\n }\n}\n\n/**\n * Perform deployment for a created deployment ID\n * Maps to: POST /v1/tenant/{tenant_id}/deploy/perform\n *\n * @param accessToken - OAuth access token\n * @param tenantId - Tenant ID\n * @param options - Deployment perform options\n * @returns Deployment status and message\n */\nexport async function performDeployment(\n accessToken: string,\n tenantId: string,\n options: PerformDeploymentOptions,\n): Promise<PerformDeploymentResponse> {\n const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE === 'true'\n\n if (shouldMock) {\n // MOCK IMPLEMENTATION: Return mock deployment status\n log.debug(\n 'Using mock Control Plane API for performDeployment (FIGMA_MOCK_CONTROL_PLANE != \"false\")',\n )\n\n return {\n deploymentId: options.deploymentId,\n message: 'Deployment initiated successfully',\n }\n }\n\n const url = `${getControlPlaneBaseUrl()}/v1/cms/tenant/${tenantId}/deploy/perform`\n log.debug(`Calling performDeployment API at ${url}`)\n\n // REAL API IMPLEMENTATION\n const response = await fetch(url, {\n body: JSON.stringify({ deployment_id: options.deploymentId }),\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n\n if (!response.ok) {\n throw new ControlPlaneError(\n `Failed to perform deployment: ${response.status} ${response.statusText}`,\n response.status,\n )\n }\n\n const data = (await response.json()) as PerformDeploymentApiResponse\n\n // Map API response to our interface\n return {\n deploymentId: data.meta.deployment_id,\n message: data.meta.message || 'Deployment initiated',\n }\n}\n\n/**\n * Get dataset info including tenant ID and OAuth credentials\n * Maps to: GET /v1/cms/dataset/:datasetId\n *\n * @param accessToken - OAuth access token\n * @param datasetId - Content System ID (dataset ID)\n * @returns Dataset info with tenant ID and OAuth credentials\n */\nexport async function getDatasetInfo(accessToken: string, datasetId: string): Promise<DatasetInfo> {\n const url = `${getControlPlaneBaseUrl()}/v1/cms/dataset/${datasetId}`\n log.debug(`Calling getDatasetInfo API at ${url}`)\n\n const response = await fetch(url, {\n headers: { Authorization: `Bearer ${accessToken}` },\n })\n\n if (!response.ok) {\n throw new ControlPlaneError(\n `Failed to get dataset info: ${response.status} ${response.statusText}`,\n response.status,\n )\n }\n\n const data = (await response.json()) as DatasetInfoApiResponse\n const sanitizedData = {\n ...data,\n meta: { ...data.meta, oauth_client_secret: '[REDACTED]' },\n }\n log.debug(`Dataset API response: ${JSON.stringify(sanitizedData, null, 2)}`)\n\n return {\n cmsId: data.meta.cms_resource_id,\n oauthClientId: data.meta.oauth_client_id,\n oauthClientSecret: data.meta.oauth_client_secret,\n tenantId: data.meta.tenant_instance_id,\n }\n}\n"],"names":["getEnvironment","log","CONTROL_PLANE_API_URLS","production","staging","process","env","FIGMA_API_URL","getControlPlaneBaseUrl","FIGMA_API_BASE_URL","ControlPlaneError","Error","message","statusCode","cause","name","listTenants","accessToken","shouldMock","FIGMA_MOCK_CONTROL_PLANE","debug","id","createdAt","domain","parentId","parentType","status","response","fetch","headers","Authorization","ok","statusText","json","createTenant","options","tenantId","Math","random","toString","substring","slug","toLowerCase","replace","Date","toISOString","body","JSON","stringify","method","getTenantDetails","createDeployment","deploymentId","mockBucket","mockRegion","mockBaseUrl","staticAssetUploadUrls","assetPath","staticAssets","encodedPath","encodeURIComponent","codeUploadUrl","url","static_assets","data","meta","lambda_zip_upload_url","deployment_id","static_asset_upload_urls","performDeployment","getDatasetInfo","datasetId","sanitizedData","oauth_client_secret","cmsId","cms_resource_id","oauthClientId","oauth_client_id","oauthClientSecret","tenant_instance_id"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"figma-api.d.ts","sourceRoot":"","sources":["../../src/api/figma-api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAwB,MAAM,kBAAkB,CAAA;AAI1E;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;IAG7B,UAAU,CAAC,EAAE,MAAM;IACnB,KAAK,CAAC,EAAE,KAAK;gBAFpB,OAAO,EAAE,MAAM,EACR,UAAU,CAAC,EAAE,MAAM,YAAA,EACnB,KAAK,CAAC,EAAE,KAAK,YAAA;CAKvB;AAmBD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,YAAY,CAAC,CA+HvB"}
1
+ {"version":3,"file":"figma-api.d.ts","sourceRoot":"","sources":["../../src/api/figma-api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAwB,MAAM,kBAAkB,CAAA;AAI1E;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;IAG7B,UAAU,CAAC,EAAE,MAAM;IACnB,KAAK,CAAC,EAAE,KAAK;gBAFpB,OAAO,EAAE,MAAM,EACR,UAAU,CAAC,EAAE,MAAM,YAAA,EACnB,KAAK,CAAC,EAAE,KAAK,YAAA;CAKvB;AAmBD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,YAAY,CAAC,CAkIvB"}
@@ -109,8 +109,11 @@
109
109
  let errorMessage = `Failed to get project token: ${response.status} ${response.statusText}`;
110
110
  try {
111
111
  const errorBody = await response.json();
112
- if (errorBody.error || errorBody.message) {
113
- errorMessage = errorBody.error || errorBody.message;
112
+ // Only use string error messages (API may return { error: true })
113
+ if (typeof errorBody.message === 'string') {
114
+ errorMessage = errorBody.message;
115
+ } else if (typeof errorBody.error === 'string') {
116
+ errorMessage = errorBody.error;
114
117
  }
115
118
  } catch {
116
119
  // Ignore JSON parse errors for error body
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/api/figma-api.ts"],"sourcesContent":["/**\n * Figma API Client\n *\n * Client for Figma REST API (Pixie) endpoints\n * Handles project token generation and other CMS-related API calls\n */\n\nimport type { ProjectToken, ProjectTokenResponse } from '../auth/types.js'\n\nimport * as log from '../utils/log.js'\n\n/**\n * Error thrown when Figma API calls fail\n */\nexport class FigmaApiError extends Error {\n constructor(\n message: string,\n public statusCode?: number,\n public cause?: Error,\n ) {\n super(message)\n this.name = 'FigmaApiError'\n }\n}\n\n/**\n * Base URLs for Figma API\n */\nconst API_BASE_URLS = {\n prod: 'https://api.figma.com',\n staging: process.env.FIGMA_API_URL || 'https://api.staging.figma.com',\n}\n\n/**\n * Get the Figma API base URL based on environment\n * TODO: Use this once real API endpoints are available\n */\nfunction _getApiBaseUrl(): string {\n // For now, default to staging to match OAuth config\n return process.env.FIGMA_API_BASE_URL || API_BASE_URLS.staging\n}\n\n/**\n * Get a project token (JWT) for a specific tenant/CMS\n *\n * This token is used by local Payload instances to authenticate\n * requests to the Content API. The token contains the CMS ID and\n * user identifying information in its claims.\n *\n * Endpoint: GET https://api.figma.com/v1/cms/:id/token/\n *\n * @param accessToken - OAuth access token for authentication\n * @param tenantId - The tenant/CMS ID\n * @returns ProjectToken with JWT and expiry information\n * @throws {FigmaApiError} If the API call fails\n */\nexport async function getProjectToken(\n accessToken: string,\n tenantId: string,\n): Promise<ProjectToken> {\n // Priority 1: Check for direct token value from environment\n const envTokenValue = process.env.FIGMA_MOCK_PROJECT_TOKEN_VALUE\n if (envTokenValue) {\n log.debug('Using direct project token from FIGMA_MOCK_PROJECT_TOKEN_VALUE')\n try {\n // Parse JWT payload to extract claims\n const parts = envTokenValue.split('.')\n if (parts.length !== 3) {\n throw new Error('Invalid JWT format: must have 3 parts (header.payload.signature)')\n }\n\n const payload = parts[1]\n const base64 = payload.replace(/-/g, '+').replace(/_/g, '/')\n const paddedBase64 = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), '=')\n const decodedPayload = JSON.parse(Buffer.from(paddedBase64, 'base64').toString('utf-8')) as {\n aud?: string\n exp?: number\n }\n\n if (!decodedPayload.exp || typeof decodedPayload.exp !== 'number') {\n throw new Error('JWT payload missing exp claim')\n }\n\n // Extract tenant ID from aud claim (use parameter as fallback)\n const extractedTenantId = decodedPayload.aud || tenantId\n\n return {\n expiresAt: decodedPayload.exp * 1000, // Convert from seconds to milliseconds\n tenantId: extractedTenantId,\n token: envTokenValue,\n }\n } catch (error) {\n throw new FigmaApiError(\n `Invalid JWT token in FIGMA_MOCK_PROJECT_TOKEN_VALUE: ${error instanceof Error ? error.message : 'Unknown error'}`,\n undefined,\n error instanceof Error ? error : undefined,\n )\n }\n }\n\n // Priority 2: Check if project token API should be mocked\n // Default to false (use real API)\n const shouldMock = process.env.FIGMA_MOCK_PROJECT_TOKEN === 'true'\n\n try {\n if (shouldMock) {\n // MOCK IMPLEMENTATION: Generate a mock JWT token for testing\n log.debug(\n 'Using mock project token API for getProjectToken (FIGMA_MOCK_PROJECT_TOKEN = \"true\")',\n )\n const mockToken = createMockJWT(tenantId, 25) // 25 minutes expiry\n\n // Mock response data\n const data: ProjectTokenResponse = {\n meta: {\n token: mockToken,\n },\n }\n\n if (!data.meta?.token) {\n throw new FigmaApiError('Invalid response: missing token in meta')\n }\n\n // Parse the JWT to extract expiry (it's in the payload)\n const expiresAt = parseJwtExpiry(data.meta.token)\n\n return {\n expiresAt,\n tenantId,\n token: data.meta.token,\n }\n }\n\n // REAL API IMPLEMENTATION\n const baseUrl = _getApiBaseUrl()\n const url = `${baseUrl}/v1/cms/${tenantId}/token/`\n\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n method: 'POST',\n })\n\n if (!response.ok) {\n // Handle error responses\n let errorMessage = `Failed to get project token: ${response.status} ${response.statusText}`\n\n try {\n const errorBody = await response.json()\n if (errorBody.error || errorBody.message) {\n errorMessage = errorBody.error || errorBody.message\n }\n } catch {\n // Ignore JSON parse errors for error body\n }\n\n throw new FigmaApiError(errorMessage, response.status)\n }\n\n const data: ProjectTokenResponse = await response.json()\n\n if (!data.meta?.token) {\n throw new FigmaApiError('Invalid response: missing token in meta')\n }\n\n // Parse the JWT to extract expiry (it's in the payload)\n const expiresAt = parseJwtExpiry(data.meta.token)\n\n return {\n expiresAt,\n tenantId,\n token: data.meta.token,\n }\n } catch (error) {\n if (error instanceof FigmaApiError) {\n throw error\n }\n\n // Handle network errors and other unexpected errors\n throw new FigmaApiError(\n `Failed to get project token: ${error instanceof Error ? error.message : 'Unknown error'}`,\n undefined,\n error instanceof Error ? error : undefined,\n )\n }\n}\n\n/**\n * Create a mock JWT token for testing\n * TODO: Remove this when real API is available\n */\nfunction createMockJWT(tenantId: string, expiresInMinutes: number): string {\n const header = {\n alg: 'RS256',\n typ: 'JWT',\n }\n\n const now = Math.floor(Date.now() / 1000) // Current time in seconds\n const exp = now + expiresInMinutes * 60 // Expiry in seconds\n\n const payload = {\n aud: tenantId, // Audience claim contains the CMS ID\n email: 'developer@example.com',\n exp,\n iat: now,\n sub: 'user123',\n }\n\n // Base64url encode\n const encodeBase64Url = (obj: object): string => {\n return Buffer.from(JSON.stringify(obj))\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '')\n }\n\n const headerEncoded = encodeBase64Url(header)\n const payloadEncoded = encodeBase64Url(payload)\n const signature = 'mock_signature_' + Math.random().toString(36).substring(2, 15)\n\n return `${headerEncoded}.${payloadEncoded}.${signature}`\n}\n\n/**\n * Parse JWT expiry from token\n * JWTs contain an 'exp' claim in the payload (second part)\n *\n * @param token - JWT token string\n * @returns Unix timestamp in milliseconds when token expires\n * @throws {FigmaApiError} If JWT cannot be parsed\n */\nfunction parseJwtExpiry(token: string): number {\n try {\n // JWT format: header.payload.signature\n const parts = token.split('.')\n if (parts.length !== 3) {\n throw new Error('Invalid JWT format')\n }\n\n // Decode the payload (second part)\n const payload = parts[1]\n // Use atob for base64url decoding (handle padding and URL-safe characters)\n const base64 = payload.replace(/-/g, '+').replace(/_/g, '/')\n const paddedBase64 = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), '=')\n\n const decodedPayload = JSON.parse(Buffer.from(paddedBase64, 'base64').toString('utf-8')) as {\n exp?: number\n }\n\n if (!decodedPayload.exp || typeof decodedPayload.exp !== 'number') {\n throw new Error('JWT payload missing exp claim')\n }\n\n // JWT exp claim is in seconds, convert to milliseconds\n return decodedPayload.exp * 1000\n } catch (error) {\n throw new FigmaApiError(\n `Failed to parse JWT expiry: ${error instanceof Error ? error.message : 'Unknown error'}`,\n undefined,\n error instanceof Error ? error : undefined,\n )\n }\n}\n"],"names":["log","FigmaApiError","Error","message","statusCode","cause","name","API_BASE_URLS","prod","staging","process","env","FIGMA_API_URL","_getApiBaseUrl","FIGMA_API_BASE_URL","getProjectToken","accessToken","tenantId","envTokenValue","FIGMA_MOCK_PROJECT_TOKEN_VALUE","debug","parts","split","length","payload","base64","replace","paddedBase64","padEnd","decodedPayload","JSON","parse","Buffer","from","toString","exp","extractedTenantId","aud","expiresAt","token","error","undefined","shouldMock","FIGMA_MOCK_PROJECT_TOKEN","mockToken","createMockJWT","data","meta","parseJwtExpiry","baseUrl","url","response","fetch","headers","Authorization","method","ok","errorMessage","status","statusText","errorBody","json","expiresInMinutes","header","alg","typ","now","Math","floor","Date","email","iat","sub","encodeBase64Url","obj","stringify","headerEncoded","payloadEncoded","signature","random","substring"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/api/figma-api.ts"],"sourcesContent":["/**\n * Figma API Client\n *\n * Client for Figma REST API (Pixie) endpoints\n * Handles project token generation and other CMS-related API calls\n */\n\nimport type { ProjectToken, ProjectTokenResponse } from '../auth/types.js'\n\nimport * as log from '../utils/log.js'\n\n/**\n * Error thrown when Figma API calls fail\n */\nexport class FigmaApiError extends Error {\n constructor(\n message: string,\n public statusCode?: number,\n public cause?: Error,\n ) {\n super(message)\n this.name = 'FigmaApiError'\n }\n}\n\n/**\n * Base URLs for Figma API\n */\nconst API_BASE_URLS = {\n prod: 'https://api.figma.com',\n staging: process.env.FIGMA_API_URL || 'https://api.staging.figma.com',\n}\n\n/**\n * Get the Figma API base URL based on environment\n * TODO: Use this once real API endpoints are available\n */\nfunction _getApiBaseUrl(): string {\n // For now, default to staging to match OAuth config\n return process.env.FIGMA_API_BASE_URL || API_BASE_URLS.staging\n}\n\n/**\n * Get a project token (JWT) for a specific tenant/CMS\n *\n * This token is used by local Payload instances to authenticate\n * requests to the Content API. The token contains the CMS ID and\n * user identifying information in its claims.\n *\n * Endpoint: GET https://api.figma.com/v1/cms/:id/token/\n *\n * @param accessToken - OAuth access token for authentication\n * @param tenantId - The tenant/CMS ID\n * @returns ProjectToken with JWT and expiry information\n * @throws {FigmaApiError} If the API call fails\n */\nexport async function getProjectToken(\n accessToken: string,\n tenantId: string,\n): Promise<ProjectToken> {\n // Priority 1: Check for direct token value from environment\n const envTokenValue = process.env.FIGMA_MOCK_PROJECT_TOKEN_VALUE\n if (envTokenValue) {\n log.debug('Using direct project token from FIGMA_MOCK_PROJECT_TOKEN_VALUE')\n try {\n // Parse JWT payload to extract claims\n const parts = envTokenValue.split('.')\n if (parts.length !== 3) {\n throw new Error('Invalid JWT format: must have 3 parts (header.payload.signature)')\n }\n\n const payload = parts[1]\n const base64 = payload.replace(/-/g, '+').replace(/_/g, '/')\n const paddedBase64 = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), '=')\n const decodedPayload = JSON.parse(Buffer.from(paddedBase64, 'base64').toString('utf-8')) as {\n aud?: string\n exp?: number\n }\n\n if (!decodedPayload.exp || typeof decodedPayload.exp !== 'number') {\n throw new Error('JWT payload missing exp claim')\n }\n\n // Extract tenant ID from aud claim (use parameter as fallback)\n const extractedTenantId = decodedPayload.aud || tenantId\n\n return {\n expiresAt: decodedPayload.exp * 1000, // Convert from seconds to milliseconds\n tenantId: extractedTenantId,\n token: envTokenValue,\n }\n } catch (error) {\n throw new FigmaApiError(\n `Invalid JWT token in FIGMA_MOCK_PROJECT_TOKEN_VALUE: ${error instanceof Error ? error.message : 'Unknown error'}`,\n undefined,\n error instanceof Error ? error : undefined,\n )\n }\n }\n\n // Priority 2: Check if project token API should be mocked\n // Default to false (use real API)\n const shouldMock = process.env.FIGMA_MOCK_PROJECT_TOKEN === 'true'\n\n try {\n if (shouldMock) {\n // MOCK IMPLEMENTATION: Generate a mock JWT token for testing\n log.debug(\n 'Using mock project token API for getProjectToken (FIGMA_MOCK_PROJECT_TOKEN = \"true\")',\n )\n const mockToken = createMockJWT(tenantId, 25) // 25 minutes expiry\n\n // Mock response data\n const data: ProjectTokenResponse = {\n meta: {\n token: mockToken,\n },\n }\n\n if (!data.meta?.token) {\n throw new FigmaApiError('Invalid response: missing token in meta')\n }\n\n // Parse the JWT to extract expiry (it's in the payload)\n const expiresAt = parseJwtExpiry(data.meta.token)\n\n return {\n expiresAt,\n tenantId,\n token: data.meta.token,\n }\n }\n\n // REAL API IMPLEMENTATION\n const baseUrl = _getApiBaseUrl()\n const url = `${baseUrl}/v1/cms/${tenantId}/token/`\n\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n method: 'POST',\n })\n\n if (!response.ok) {\n // Handle error responses\n let errorMessage = `Failed to get project token: ${response.status} ${response.statusText}`\n\n try {\n const errorBody = await response.json()\n // Only use string error messages (API may return { error: true })\n if (typeof errorBody.message === 'string') {\n errorMessage = errorBody.message\n } else if (typeof errorBody.error === 'string') {\n errorMessage = errorBody.error\n }\n } catch {\n // Ignore JSON parse errors for error body\n }\n\n throw new FigmaApiError(errorMessage, response.status)\n }\n\n const data: ProjectTokenResponse = await response.json()\n\n if (!data.meta?.token) {\n throw new FigmaApiError('Invalid response: missing token in meta')\n }\n\n // Parse the JWT to extract expiry (it's in the payload)\n const expiresAt = parseJwtExpiry(data.meta.token)\n\n return {\n expiresAt,\n tenantId,\n token: data.meta.token,\n }\n } catch (error) {\n if (error instanceof FigmaApiError) {\n throw error\n }\n\n // Handle network errors and other unexpected errors\n throw new FigmaApiError(\n `Failed to get project token: ${error instanceof Error ? error.message : 'Unknown error'}`,\n undefined,\n error instanceof Error ? error : undefined,\n )\n }\n}\n\n/**\n * Create a mock JWT token for testing\n * TODO: Remove this when real API is available\n */\nfunction createMockJWT(tenantId: string, expiresInMinutes: number): string {\n const header = {\n alg: 'RS256',\n typ: 'JWT',\n }\n\n const now = Math.floor(Date.now() / 1000) // Current time in seconds\n const exp = now + expiresInMinutes * 60 // Expiry in seconds\n\n const payload = {\n aud: tenantId, // Audience claim contains the CMS ID\n email: 'developer@example.com',\n exp,\n iat: now,\n sub: 'user123',\n }\n\n // Base64url encode\n const encodeBase64Url = (obj: object): string => {\n return Buffer.from(JSON.stringify(obj))\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '')\n }\n\n const headerEncoded = encodeBase64Url(header)\n const payloadEncoded = encodeBase64Url(payload)\n const signature = 'mock_signature_' + Math.random().toString(36).substring(2, 15)\n\n return `${headerEncoded}.${payloadEncoded}.${signature}`\n}\n\n/**\n * Parse JWT expiry from token\n * JWTs contain an 'exp' claim in the payload (second part)\n *\n * @param token - JWT token string\n * @returns Unix timestamp in milliseconds when token expires\n * @throws {FigmaApiError} If JWT cannot be parsed\n */\nfunction parseJwtExpiry(token: string): number {\n try {\n // JWT format: header.payload.signature\n const parts = token.split('.')\n if (parts.length !== 3) {\n throw new Error('Invalid JWT format')\n }\n\n // Decode the payload (second part)\n const payload = parts[1]\n // Use atob for base64url decoding (handle padding and URL-safe characters)\n const base64 = payload.replace(/-/g, '+').replace(/_/g, '/')\n const paddedBase64 = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), '=')\n\n const decodedPayload = JSON.parse(Buffer.from(paddedBase64, 'base64').toString('utf-8')) as {\n exp?: number\n }\n\n if (!decodedPayload.exp || typeof decodedPayload.exp !== 'number') {\n throw new Error('JWT payload missing exp claim')\n }\n\n // JWT exp claim is in seconds, convert to milliseconds\n return decodedPayload.exp * 1000\n } catch (error) {\n throw new FigmaApiError(\n `Failed to parse JWT expiry: ${error instanceof Error ? error.message : 'Unknown error'}`,\n undefined,\n error instanceof Error ? error : undefined,\n )\n }\n}\n"],"names":["log","FigmaApiError","Error","message","statusCode","cause","name","API_BASE_URLS","prod","staging","process","env","FIGMA_API_URL","_getApiBaseUrl","FIGMA_API_BASE_URL","getProjectToken","accessToken","tenantId","envTokenValue","FIGMA_MOCK_PROJECT_TOKEN_VALUE","debug","parts","split","length","payload","base64","replace","paddedBase64","padEnd","decodedPayload","JSON","parse","Buffer","from","toString","exp","extractedTenantId","aud","expiresAt","token","error","undefined","shouldMock","FIGMA_MOCK_PROJECT_TOKEN","mockToken","createMockJWT","data","meta","parseJwtExpiry","baseUrl","url","response","fetch","headers","Authorization","method","ok","errorMessage","status","statusText","errorBody","json","expiresInMinutes","header","alg","typ","now","Math","floor","Date","email","iat","sub","encodeBase64Url","obj","stringify","headerEncoded","payloadEncoded","signature","random","substring"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAgBA;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,wBAAwB;IACxB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,gBAAgB;IAChB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,gCAAgC;IAChC,GAAG,CAAC,EAAE,OAAO,CAAA;CACd;AA6BD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4ShF"}
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAgBA;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,wBAAwB;IACxB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,gBAAgB;IAChB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,gCAAgC;IAChC,GAAG,CAAC,EAAE,OAAO,CAAA;CACd;AA6BD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwRhF"}
@@ -2,7 +2,7 @@ import * as p from '@clack/prompts';
2
2
  import { spawn } from 'child_process';
3
3
  import path from 'path';
4
4
  import pc from 'picocolors';
5
- import { createDeployment, getTenantDetails, performDeployment } from '../api/control-plane.js';
5
+ import { createDeployment, performDeployment } from '../api/control-plane.js';
6
6
  import { getValidAccessToken } from '../auth/oauth-flow.js';
7
7
  import { getTokenStore } from '../auth/token-store.js';
8
8
  import { collectStaticAssets, createLambdaZip, getFileSize } from '../utils/asset-collection.js';
@@ -60,13 +60,6 @@ import { loginCommand } from './login.js';
60
60
  p.note('Create a project or connect an existing one to Figma: npx @payloadcms/figma init', 'Tip');
61
61
  process.exit(1);
62
62
  }
63
- // Read content system ID from environment variable
64
- const contentSystemId = process.env.FIGMA_CONTENT_SYSTEM_ID;
65
- if (!contentSystemId) {
66
- p.log.error(pc.red('✗ FIGMA_CONTENT_SYSTEM_ID environment variable is not set'));
67
- p.note('Ensure your .env file contains FIGMA_CONTENT_SYSTEM_ID=your-content-system-id', 'Tip');
68
- process.exit(1);
69
- }
70
63
  // Check authentication
71
64
  const tokenStore = getTokenStore();
72
65
  let accessToken = await getValidAccessToken(tokenStore);
@@ -262,16 +255,8 @@ import { loginCommand } from './login.js';
262
255
  // API endpoint: GET /v1/tenant/{tenant_id}/deploy/{deployment_id}/status
263
256
  // Success output
264
257
  p.log.message('This usually takes 2-3 minutes to complete.');
265
- // Get tenant details for URL
266
- try {
267
- const tenant = await getTenantDetails(accessToken, tenantId);
268
- if (tenant.domain) {
269
- p.log.message(`View: ${pc.cyan(`https://${tenant.domain}/`)}`);
270
- }
271
- } catch {
272
- // Non-critical - just skip URL display
273
- }
274
- p.note(`Manage your deployment at:\n${pc.cyan(`https://cms.figma.com/manage/tenants/${tenantId}`)}`, 'Next Steps');
258
+ // TODO: Get tenant details for URL using getTenantDetails
259
+ // TODO: Display next steps with url to manage their CMS ie. https://staging.figma.com/files/1234/cms/manager/12345678
275
260
  } catch (error) {
276
261
  spinner.stop(pc.red('✗ Failed'));
277
262
  const message = error instanceof Error ? error.message : 'Unknown error';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/deploy.ts"],"sourcesContent":["import * as p from '@clack/prompts'\nimport { spawn } from 'child_process'\nimport path from 'path'\nimport pc from 'picocolors'\n\nimport { createDeployment, getTenantDetails, performDeployment } from '../api/control-plane.js'\nimport { getValidAccessToken } from '../auth/oauth-flow.js'\nimport { getTokenStore } from '../auth/token-store.js'\nimport { collectStaticAssets, createLambdaZip, getFileSize } from '../utils/asset-collection.js'\nimport { detectBuild, getBuildCommand } from '../utils/build-detection.js'\nimport * as log from '../utils/log.js'\nimport { getPackageManager } from '../utils/package-manager.js'\nimport { isInProjectDirectory } from '../utils/project.js'\nimport { uploadLambdaZip, uploadStaticAssets } from '../utils/s3-upload.js'\nimport { loginCommand } from './login.js'\n\n/**\n * Options for deploy command\n */\nexport interface DeployCommandOptions {\n /** Enable debug mode */\n debug?: boolean\n /** Tenant ID */\n id?: string\n /** Skip confirmation prompts */\n yes?: boolean\n}\n\n/**\n * Format bytes to human-readable string\n */\nfunction formatBytes(bytes: number): string {\n if (bytes === 0) {\n return '0 B'\n }\n const k = 1024\n const sizes = ['B', 'KB', 'MB', 'GB']\n const i = Math.floor(Math.log(bytes) / Math.log(k))\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`\n}\n\n/**\n * Format date to human-readable string in local time\n */\nfunction formatDate(date: Date): string {\n return date.toLocaleString('en-US', {\n day: 'numeric',\n hour: 'numeric',\n minute: 'numeric',\n month: 'long',\n timeZoneName: 'short',\n year: 'numeric',\n })\n}\n\n/**\n * Execute the deploy command\n *\n * @param options - Command options\n */\nexport async function deployCommand(options: DeployCommandOptions): Promise<void> {\n const projectPath = process.cwd()\n const spinner = p.spinner()\n\n if (!options.id) {\n p.log.error(pc.red('✗ Tenant ID is required. Use --id <tenant_id> to specify.'))\n process.exit(1)\n }\n\n const tenantId = options.id\n\n try {\n // ===== PRE-FLIGHT CHECKS =====\n\n // Check if in project directory\n if (!(await isInProjectDirectory(projectPath))) {\n p.log.error(pc.red(\"✗ 'deploy' command is not available outside of a Payload project.\"))\n p.note(\n 'Create a project or connect an existing one to Figma: npx @payloadcms/figma init',\n 'Tip',\n )\n process.exit(1)\n }\n\n // Read content system ID from environment variable\n const contentSystemId = process.env.FIGMA_CONTENT_SYSTEM_ID\n if (!contentSystemId) {\n p.log.error(pc.red('✗ FIGMA_CONTENT_SYSTEM_ID environment variable is not set'))\n p.note('Ensure your .env file contains FIGMA_CONTENT_SYSTEM_ID=your-content-system-id', 'Tip')\n process.exit(1)\n }\n\n // Check authentication\n const tokenStore = getTokenStore()\n\n let accessToken = await getValidAccessToken(tokenStore)\n if (!accessToken) {\n p.log.message('Please log in to continue')\n await loginCommand()\n accessToken = await getValidAccessToken(tokenStore)\n if (!accessToken) {\n p.log.error(pc.red('✗ Authentication failed'))\n process.exit(1)\n }\n }\n\n // ===== BUILD VALIDATION =====\n\n spinner.start('Checking for build...')\n let buildInfo = await detectBuild(projectPath)\n\n if (!buildInfo) {\n spinner.stop(pc.yellow('⚠ No build found for this project.'))\n\n // Try to find build command\n const buildCmd = await getBuildCommand(projectPath)\n\n if (!buildCmd) {\n p.log.error('No build script found in package.json')\n p.note('Add a build script to package.json: \"build\": \"next build\"', 'Tip')\n process.exit(1)\n }\n\n // Prompt to run build\n if (!options.yes) {\n const shouldBuild = await p.confirm({\n initialValue: true,\n message: `Build command found in package.json. Run it?`,\n })\n\n if (p.isCancel(shouldBuild) || !shouldBuild) {\n p.log.message('Cancelling deployment...')\n process.exit(0)\n }\n }\n\n // Run build\n const packageManager = await getPackageManager(projectPath)\n const buildCommand = `${packageManager} run build`\n spinner.stop(`Running ${buildCommand}...\\n`)\n\n try {\n // Stream build output to user with formatting\n const buildProcess = spawn(packageManager, ['run', 'build'], {\n cwd: projectPath,\n stdio: ['ignore', 'pipe', 'pipe'],\n })\n\n // Indent and dim output\n buildProcess.stdout?.on('data', (data) => {\n const lines = data.toString().split('\\n')\n lines.forEach((line: string) => {\n if (line.trim()) {\n // eslint-disable-next-line no-console\n console.log(pc.dim(` ${line}`)) // Standard output\n }\n })\n })\n\n buildProcess.stderr?.on('data', (data) => {\n const lines = data.toString().split('\\n')\n lines.forEach((line: string) => {\n if (line.trim()) {\n // eslint-disable-next-line no-console\n console.log(pc.dim(` ${line}`)) // Standard error\n }\n })\n })\n\n const exitCode = await new Promise<number>((resolve) => {\n buildProcess.on('close', resolve)\n })\n\n if (exitCode !== 0) {\n throw new Error(`Build process exited with code ${exitCode}`)\n }\n\n p.log.success(pc.green('✓ Build completed'))\n } catch (error) {\n p.log.error(pc.red('✗ Build failed'))\n if (error instanceof Error && options.debug) {\n log.error(`Build error: ${error.message}`)\n }\n process.exit(1)\n }\n\n // Re-check for build\n buildInfo = await detectBuild(projectPath)\n if (!buildInfo) {\n p.log.error(pc.red('✗ Build completed but standalone build not found'))\n p.note('Ensure next.config.js has output: \"standalone\"', 'Check Lambda Configuration')\n process.exit(1)\n }\n } else {\n spinner.stop(pc.green('✓ Build found'))\n p.note(\n [\n `${pc.cyan('Build ID:')} ${buildInfo.buildId}`,\n `${pc.cyan('Date:')} ${formatDate(buildInfo.timestamp)}`,\n ].join('\\n'),\n 'Build Details',\n )\n }\n\n // ===== DEPLOYMENT PREPARATION =====\n\n // Create Lambda zip\n spinner.start('Creating deployment package...')\n try {\n await createLambdaZip(projectPath)\n spinner.stop(pc.green('✓ Deployment package created'))\n } catch (error) {\n spinner.stop(pc.red('✗ Failed to create deployment package'))\n throw error\n }\n\n // Collect static assets\n spinner.start('Collecting static assets...')\n const staticAssets = await collectStaticAssets(projectPath)\n const zipPath = path.join(projectPath, 'lambda.zip')\n const zipSize = await getFileSize(zipPath)\n spinner.stop(pc.green(`✓ ${staticAssets.length} static assets detected`))\n\n log.debug(`Lambda zip size: ${formatBytes(zipSize)}`)\n log.debug(`Static assets: ${staticAssets.length} files`)\n\n // ===== DEPLOYMENT CONFIRMATION =====\n\n if (!options.yes) {\n p.note(\n [\n `${pc.cyan('Build ID:')} ${buildInfo.buildId}`,\n `${pc.cyan('Date:')} ${formatDate(buildInfo.timestamp)}`,\n `${pc.cyan('Code size:')} ${formatBytes(zipSize)}`,\n `${pc.cyan('Static assets:')} ${staticAssets.length} files`,\n ].join('\\n'),\n 'Details',\n )\n\n const shouldDeploy = await p.confirm({\n initialValue: true,\n message: 'Deploy this build to Figma?',\n })\n\n if (p.isCancel(shouldDeploy) || !shouldDeploy) {\n p.log.message('Cancelling deployment...')\n process.exit(0)\n }\n }\n\n // ===== DEPLOYMENT EXECUTION =====\n\n // Step 1: Create deployment\n spinner.start('Creating deployment...')\n let deploymentId: string\n let codeUploadUrl: string\n let staticAssetUploadUrls: Record<string, string>\n\n try {\n const createResponse = await createDeployment(accessToken, tenantId, {\n staticAssets,\n })\n\n deploymentId = createResponse.deploymentId\n codeUploadUrl = createResponse.codeUploadUrl\n staticAssetUploadUrls = createResponse.staticAssetUploadUrls\n\n log.debug(`Deployment ID: ${deploymentId}`)\n } catch (error) {\n spinner.stop(pc.red('✗ Failed to create deployment'))\n throw error\n }\n\n // Step 2: Upload code\n spinner.message(`Uploading code (${formatBytes(zipSize)})...`)\n try {\n await uploadLambdaZip(zipPath, codeUploadUrl)\n spinner.stop(pc.green(`✓ Code uploaded (${formatBytes(zipSize)})`))\n } catch (error) {\n spinner.stop(pc.red('✗ Failed to upload code'))\n throw error\n }\n\n // Step 3: Upload static assets\n if (staticAssets.length > 0) {\n spinner.start('Uploading static assets...')\n\n try {\n const uploadResults = await uploadStaticAssets(\n projectPath,\n staticAssetUploadUrls,\n (uploaded, total) => {\n spinner.message(`Uploading static assets (${uploaded}/${total})...`)\n },\n )\n\n if (uploadResults.assetsFailed > 0) {\n spinner.stop(\n pc.yellow(\n `⚠ ${uploadResults.assetsUploaded}/${staticAssets.length} assets uploaded (${uploadResults.assetsFailed} failed)`,\n ),\n )\n p.log.warning(\n `${uploadResults.assetsFailed} assets failed to upload. Deployment may be incomplete.`,\n )\n } else {\n spinner.stop(\n pc.green(\n `✓ Static assets uploaded (${uploadResults.assetsUploaded} files, ${formatBytes(uploadResults.totalBytesUploaded)})`,\n ),\n )\n }\n } catch (error) {\n spinner.stop(pc.red('✗ Failed to upload assets'))\n throw error\n }\n }\n\n // Step 4: Perform deployment\n spinner.start('Performing deployment...')\n try {\n await performDeployment(accessToken, tenantId, {\n deploymentId,\n })\n spinner.stop(pc.green('✓ Deployment initiated'))\n } catch (error) {\n spinner.stop(pc.red('✗ Failed to perform deployment'))\n throw error\n }\n\n // TODO: Add deployment progress tracking\n // Future: Poll deployment status and show progress\n // API endpoint: GET /v1/tenant/{tenant_id}/deploy/{deployment_id}/status\n\n // Success output\n p.log.message('This usually takes 2-3 minutes to complete.')\n\n // Get tenant details for URL\n try {\n const tenant = await getTenantDetails(accessToken, tenantId)\n if (tenant.domain) {\n p.log.message(`View: ${pc.cyan(`https://${tenant.domain}/`)}`)\n }\n } catch {\n // Non-critical - just skip URL display\n }\n\n p.note(\n `Manage your deployment at:\\n${pc.cyan(`https://cms.figma.com/manage/tenants/${tenantId}`)}`,\n 'Next Steps',\n )\n } catch (error) {\n spinner.stop(pc.red('✗ Failed'))\n const message = error instanceof Error ? error.message : 'Unknown error'\n p.log.error(pc.red(`✗ ${message}`))\n if (options.debug && error instanceof Error && error.stack) {\n log.error(`Stack trace: ${error.stack}`)\n }\n process.exit(1)\n }\n}\n"],"names":["p","spawn","path","pc","createDeployment","getTenantDetails","performDeployment","getValidAccessToken","getTokenStore","collectStaticAssets","createLambdaZip","getFileSize","detectBuild","getBuildCommand","log","getPackageManager","isInProjectDirectory","uploadLambdaZip","uploadStaticAssets","loginCommand","formatBytes","bytes","k","sizes","i","Math","floor","parseFloat","pow","toFixed","formatDate","date","toLocaleString","day","hour","minute","month","timeZoneName","year","deployCommand","options","projectPath","process","cwd","spinner","id","error","red","exit","tenantId","note","contentSystemId","env","FIGMA_CONTENT_SYSTEM_ID","tokenStore","accessToken","message","start","buildInfo","stop","yellow","buildCmd","yes","shouldBuild","confirm","initialValue","isCancel","packageManager","buildCommand","buildProcess","stdio","stdout","on","data","lines","toString","split","forEach","line","trim","console","dim","stderr","exitCode","Promise","resolve","Error","success","green","debug","cyan","buildId","timestamp","join","staticAssets","zipPath","zipSize","length","shouldDeploy","deploymentId","codeUploadUrl","staticAssetUploadUrls","createResponse","uploadResults","uploaded","total","assetsFailed","assetsUploaded","warning","totalBytesUploaded","tenant","domain","stack"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/commands/deploy.ts"],"sourcesContent":["import * as p from '@clack/prompts'\nimport { spawn } from 'child_process'\nimport path from 'path'\nimport pc from 'picocolors'\n\nimport { createDeployment, performDeployment } from '../api/control-plane.js'\nimport { getValidAccessToken } from '../auth/oauth-flow.js'\nimport { getTokenStore } from '../auth/token-store.js'\nimport { collectStaticAssets, createLambdaZip, getFileSize } from '../utils/asset-collection.js'\nimport { detectBuild, getBuildCommand } from '../utils/build-detection.js'\nimport * as log from '../utils/log.js'\nimport { getPackageManager } from '../utils/package-manager.js'\nimport { isInProjectDirectory } from '../utils/project.js'\nimport { uploadLambdaZip, uploadStaticAssets } from '../utils/s3-upload.js'\nimport { loginCommand } from './login.js'\n\n/**\n * Options for deploy command\n */\nexport interface DeployCommandOptions {\n /** Enable debug mode */\n debug?: boolean\n /** Tenant ID */\n id?: string\n /** Skip confirmation prompts */\n yes?: boolean\n}\n\n/**\n * Format bytes to human-readable string\n */\nfunction formatBytes(bytes: number): string {\n if (bytes === 0) {\n return '0 B'\n }\n const k = 1024\n const sizes = ['B', 'KB', 'MB', 'GB']\n const i = Math.floor(Math.log(bytes) / Math.log(k))\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`\n}\n\n/**\n * Format date to human-readable string in local time\n */\nfunction formatDate(date: Date): string {\n return date.toLocaleString('en-US', {\n day: 'numeric',\n hour: 'numeric',\n minute: 'numeric',\n month: 'long',\n timeZoneName: 'short',\n year: 'numeric',\n })\n}\n\n/**\n * Execute the deploy command\n *\n * @param options - Command options\n */\nexport async function deployCommand(options: DeployCommandOptions): Promise<void> {\n const projectPath = process.cwd()\n const spinner = p.spinner()\n\n if (!options.id) {\n p.log.error(pc.red('✗ Tenant ID is required. Use --id <tenant_id> to specify.'))\n process.exit(1)\n }\n\n const tenantId = options.id\n\n try {\n // ===== PRE-FLIGHT CHECKS =====\n\n // Check if in project directory\n if (!(await isInProjectDirectory(projectPath))) {\n p.log.error(pc.red(\"✗ 'deploy' command is not available outside of a Payload project.\"))\n p.note(\n 'Create a project or connect an existing one to Figma: npx @payloadcms/figma init',\n 'Tip',\n )\n process.exit(1)\n }\n\n // Check authentication\n const tokenStore = getTokenStore()\n\n let accessToken = await getValidAccessToken(tokenStore)\n if (!accessToken) {\n p.log.message('Please log in to continue')\n await loginCommand()\n accessToken = await getValidAccessToken(tokenStore)\n if (!accessToken) {\n p.log.error(pc.red('✗ Authentication failed'))\n process.exit(1)\n }\n }\n\n // ===== BUILD VALIDATION =====\n\n spinner.start('Checking for build...')\n let buildInfo = await detectBuild(projectPath)\n\n if (!buildInfo) {\n spinner.stop(pc.yellow('⚠ No build found for this project.'))\n\n // Try to find build command\n const buildCmd = await getBuildCommand(projectPath)\n\n if (!buildCmd) {\n p.log.error('No build script found in package.json')\n p.note('Add a build script to package.json: \"build\": \"next build\"', 'Tip')\n process.exit(1)\n }\n\n // Prompt to run build\n if (!options.yes) {\n const shouldBuild = await p.confirm({\n initialValue: true,\n message: `Build command found in package.json. Run it?`,\n })\n\n if (p.isCancel(shouldBuild) || !shouldBuild) {\n p.log.message('Cancelling deployment...')\n process.exit(0)\n }\n }\n\n // Run build\n const packageManager = await getPackageManager(projectPath)\n const buildCommand = `${packageManager} run build`\n spinner.stop(`Running ${buildCommand}...\\n`)\n\n try {\n // Stream build output to user with formatting\n const buildProcess = spawn(packageManager, ['run', 'build'], {\n cwd: projectPath,\n stdio: ['ignore', 'pipe', 'pipe'],\n })\n\n // Indent and dim output\n buildProcess.stdout?.on('data', (data) => {\n const lines = data.toString().split('\\n')\n lines.forEach((line: string) => {\n if (line.trim()) {\n // eslint-disable-next-line no-console\n console.log(pc.dim(` ${line}`)) // Standard output\n }\n })\n })\n\n buildProcess.stderr?.on('data', (data) => {\n const lines = data.toString().split('\\n')\n lines.forEach((line: string) => {\n if (line.trim()) {\n // eslint-disable-next-line no-console\n console.log(pc.dim(` ${line}`)) // Standard error\n }\n })\n })\n\n const exitCode = await new Promise<number>((resolve) => {\n buildProcess.on('close', resolve)\n })\n\n if (exitCode !== 0) {\n throw new Error(`Build process exited with code ${exitCode}`)\n }\n\n p.log.success(pc.green('✓ Build completed'))\n } catch (error) {\n p.log.error(pc.red('✗ Build failed'))\n if (error instanceof Error && options.debug) {\n log.error(`Build error: ${error.message}`)\n }\n process.exit(1)\n }\n\n // Re-check for build\n buildInfo = await detectBuild(projectPath)\n if (!buildInfo) {\n p.log.error(pc.red('✗ Build completed but standalone build not found'))\n p.note('Ensure next.config.js has output: \"standalone\"', 'Check Lambda Configuration')\n process.exit(1)\n }\n } else {\n spinner.stop(pc.green('✓ Build found'))\n p.note(\n [\n `${pc.cyan('Build ID:')} ${buildInfo.buildId}`,\n `${pc.cyan('Date:')} ${formatDate(buildInfo.timestamp)}`,\n ].join('\\n'),\n 'Build Details',\n )\n }\n\n // ===== DEPLOYMENT PREPARATION =====\n\n // Create Lambda zip\n spinner.start('Creating deployment package...')\n try {\n await createLambdaZip(projectPath)\n spinner.stop(pc.green('✓ Deployment package created'))\n } catch (error) {\n spinner.stop(pc.red('✗ Failed to create deployment package'))\n throw error\n }\n\n // Collect static assets\n spinner.start('Collecting static assets...')\n const staticAssets = await collectStaticAssets(projectPath)\n const zipPath = path.join(projectPath, 'lambda.zip')\n const zipSize = await getFileSize(zipPath)\n spinner.stop(pc.green(`✓ ${staticAssets.length} static assets detected`))\n\n log.debug(`Lambda zip size: ${formatBytes(zipSize)}`)\n log.debug(`Static assets: ${staticAssets.length} files`)\n\n // ===== DEPLOYMENT CONFIRMATION =====\n\n if (!options.yes) {\n p.note(\n [\n `${pc.cyan('Build ID:')} ${buildInfo.buildId}`,\n `${pc.cyan('Date:')} ${formatDate(buildInfo.timestamp)}`,\n `${pc.cyan('Code size:')} ${formatBytes(zipSize)}`,\n `${pc.cyan('Static assets:')} ${staticAssets.length} files`,\n ].join('\\n'),\n 'Details',\n )\n\n const shouldDeploy = await p.confirm({\n initialValue: true,\n message: 'Deploy this build to Figma?',\n })\n\n if (p.isCancel(shouldDeploy) || !shouldDeploy) {\n p.log.message('Cancelling deployment...')\n process.exit(0)\n }\n }\n\n // ===== DEPLOYMENT EXECUTION =====\n\n // Step 1: Create deployment\n spinner.start('Creating deployment...')\n let deploymentId: string\n let codeUploadUrl: string\n let staticAssetUploadUrls: Record<string, string>\n\n try {\n const createResponse = await createDeployment(accessToken, tenantId, {\n staticAssets,\n })\n\n deploymentId = createResponse.deploymentId\n codeUploadUrl = createResponse.codeUploadUrl\n staticAssetUploadUrls = createResponse.staticAssetUploadUrls\n\n log.debug(`Deployment ID: ${deploymentId}`)\n } catch (error) {\n spinner.stop(pc.red('✗ Failed to create deployment'))\n throw error\n }\n\n // Step 2: Upload code\n spinner.message(`Uploading code (${formatBytes(zipSize)})...`)\n try {\n await uploadLambdaZip(zipPath, codeUploadUrl)\n spinner.stop(pc.green(`✓ Code uploaded (${formatBytes(zipSize)})`))\n } catch (error) {\n spinner.stop(pc.red('✗ Failed to upload code'))\n throw error\n }\n\n // Step 3: Upload static assets\n if (staticAssets.length > 0) {\n spinner.start('Uploading static assets...')\n\n try {\n const uploadResults = await uploadStaticAssets(\n projectPath,\n staticAssetUploadUrls,\n (uploaded, total) => {\n spinner.message(`Uploading static assets (${uploaded}/${total})...`)\n },\n )\n\n if (uploadResults.assetsFailed > 0) {\n spinner.stop(\n pc.yellow(\n `⚠ ${uploadResults.assetsUploaded}/${staticAssets.length} assets uploaded (${uploadResults.assetsFailed} failed)`,\n ),\n )\n p.log.warning(\n `${uploadResults.assetsFailed} assets failed to upload. Deployment may be incomplete.`,\n )\n } else {\n spinner.stop(\n pc.green(\n `✓ Static assets uploaded (${uploadResults.assetsUploaded} files, ${formatBytes(uploadResults.totalBytesUploaded)})`,\n ),\n )\n }\n } catch (error) {\n spinner.stop(pc.red('✗ Failed to upload assets'))\n throw error\n }\n }\n\n // Step 4: Perform deployment\n spinner.start('Performing deployment...')\n try {\n await performDeployment(accessToken, tenantId, {\n deploymentId,\n })\n spinner.stop(pc.green('✓ Deployment initiated'))\n } catch (error) {\n spinner.stop(pc.red('✗ Failed to perform deployment'))\n throw error\n }\n\n // TODO: Add deployment progress tracking\n // Future: Poll deployment status and show progress\n // API endpoint: GET /v1/tenant/{tenant_id}/deploy/{deployment_id}/status\n\n // Success output\n p.log.message('This usually takes 2-3 minutes to complete.')\n\n // TODO: Get tenant details for URL using getTenantDetails\n // TODO: Display next steps with url to manage their CMS ie. https://staging.figma.com/files/1234/cms/manager/12345678\n } catch (error) {\n spinner.stop(pc.red('✗ Failed'))\n const message = error instanceof Error ? error.message : 'Unknown error'\n p.log.error(pc.red(`✗ ${message}`))\n if (options.debug && error instanceof Error && error.stack) {\n log.error(`Stack trace: ${error.stack}`)\n }\n process.exit(1)\n }\n}\n"],"names":["p","spawn","path","pc","createDeployment","performDeployment","getValidAccessToken","getTokenStore","collectStaticAssets","createLambdaZip","getFileSize","detectBuild","getBuildCommand","log","getPackageManager","isInProjectDirectory","uploadLambdaZip","uploadStaticAssets","loginCommand","formatBytes","bytes","k","sizes","i","Math","floor","parseFloat","pow","toFixed","formatDate","date","toLocaleString","day","hour","minute","month","timeZoneName","year","deployCommand","options","projectPath","process","cwd","spinner","id","error","red","exit","tenantId","note","tokenStore","accessToken","message","start","buildInfo","stop","yellow","buildCmd","yes","shouldBuild","confirm","initialValue","isCancel","packageManager","buildCommand","buildProcess","stdio","stdout","on","data","lines","toString","split","forEach","line","trim","console","dim","stderr","exitCode","Promise","resolve","Error","success","green","debug","cyan","buildId","timestamp","join","staticAssets","zipPath","zipSize","length","shouldDeploy","deploymentId","codeUploadUrl","staticAssetUploadUrls","createResponse","uploadResults","uploaded","total","assetsFailed","assetsUploaded","warning","totalBytesUploaded","stack"],"mappings":"AAAA,YAAYA,OAAO,iBAAgB;AACnC,SAASC,KAAK,QAAQ,gBAAe;AACrC,OAAOC,UAAU,OAAM;AACvB,OAAOC,QAAQ,aAAY;AAE3B,SAASC,gBAAgB,EAAEC,iBAAiB,QAAQ,0BAAyB;AAC7E,SAASC,mBAAmB,QAAQ,wBAAuB;AAC3D,SAASC,aAAa,QAAQ,yBAAwB;AACtD,SAASC,mBAAmB,EAAEC,eAAe,EAAEC,WAAW,QAAQ,+BAA8B;AAChG,SAASC,WAAW,EAAEC,eAAe,QAAQ,8BAA6B;AAC1E,YAAYC,SAAS,kBAAiB;AACtC,SAASC,iBAAiB,QAAQ,8BAA6B;AAC/D,SAASC,oBAAoB,QAAQ,sBAAqB;AAC1D,SAASC,eAAe,EAAEC,kBAAkB,QAAQ,wBAAuB;AAC3E,SAASC,YAAY,QAAQ,aAAY;AAczC;;CAEC,GACD,SAASC,YAAYC,KAAa;IAChC,IAAIA,UAAU,GAAG;QACf,OAAO;IACT;IACA,MAAMC,IAAI;IACV,MAAMC,QAAQ;QAAC;QAAK;QAAM;QAAM;KAAK;IACrC,MAAMC,IAAIC,KAAKC,KAAK,CAACD,KAAKX,GAAG,CAACO,SAASI,KAAKX,GAAG,CAACQ;IAChD,OAAO,GAAGK,WAAW,AAACN,CAAAA,QAAQI,KAAKG,GAAG,CAACN,GAAGE,EAAC,EAAGK,OAAO,CAAC,IAAI,CAAC,EAAEN,KAAK,CAACC,EAAE,EAAE;AACzE;AAEA;;CAEC,GACD,SAASM,WAAWC,IAAU;IAC5B,OAAOA,KAAKC,cAAc,CAAC,SAAS;QAClCC,KAAK;QACLC,MAAM;QACNC,QAAQ;QACRC,OAAO;QACPC,cAAc;QACdC,MAAM;IACR;AACF;AAEA;;;;CAIC,GACD,OAAO,eAAeC,cAAcC,OAA6B;IAC/D,MAAMC,cAAcC,QAAQC,GAAG;IAC/B,MAAMC,UAAU3C,EAAE2C,OAAO;IAEzB,IAAI,CAACJ,QAAQK,EAAE,EAAE;QACf5C,EAAEa,GAAG,CAACgC,KAAK,CAAC1C,GAAG2C,GAAG,CAAC;QACnBL,QAAQM,IAAI,CAAC;IACf;IAEA,MAAMC,WAAWT,QAAQK,EAAE;IAE3B,IAAI;QACF,gCAAgC;QAEhC,gCAAgC;QAChC,IAAI,CAAE,MAAM7B,qBAAqByB,cAAe;YAC9CxC,EAAEa,GAAG,CAACgC,KAAK,CAAC1C,GAAG2C,GAAG,CAAC;YACnB9C,EAAEiD,IAAI,CACJ,oFACA;YAEFR,QAAQM,IAAI,CAAC;QACf;QAEA,uBAAuB;QACvB,MAAMG,aAAa3C;QAEnB,IAAI4C,cAAc,MAAM7C,oBAAoB4C;QAC5C,IAAI,CAACC,aAAa;YAChBnD,EAAEa,GAAG,CAACuC,OAAO,CAAC;YACd,MAAMlC;YACNiC,cAAc,MAAM7C,oBAAoB4C;YACxC,IAAI,CAACC,aAAa;gBAChBnD,EAAEa,GAAG,CAACgC,KAAK,CAAC1C,GAAG2C,GAAG,CAAC;gBACnBL,QAAQM,IAAI,CAAC;YACf;QACF;QAEA,+BAA+B;QAE/BJ,QAAQU,KAAK,CAAC;QACd,IAAIC,YAAY,MAAM3C,YAAY6B;QAElC,IAAI,CAACc,WAAW;YACdX,QAAQY,IAAI,CAACpD,GAAGqD,MAAM,CAAC;YAEvB,4BAA4B;YAC5B,MAAMC,WAAW,MAAM7C,gBAAgB4B;YAEvC,IAAI,CAACiB,UAAU;gBACbzD,EAAEa,GAAG,CAACgC,KAAK,CAAC;gBACZ7C,EAAEiD,IAAI,CAAC,6DAA6D;gBACpER,QAAQM,IAAI,CAAC;YACf;YAEA,sBAAsB;YACtB,IAAI,CAACR,QAAQmB,GAAG,EAAE;gBAChB,MAAMC,cAAc,MAAM3D,EAAE4D,OAAO,CAAC;oBAClCC,cAAc;oBACdT,SAAS,CAAC,4CAA4C,CAAC;gBACzD;gBAEA,IAAIpD,EAAE8D,QAAQ,CAACH,gBAAgB,CAACA,aAAa;oBAC3C3D,EAAEa,GAAG,CAACuC,OAAO,CAAC;oBACdX,QAAQM,IAAI,CAAC;gBACf;YACF;YAEA,YAAY;YACZ,MAAMgB,iBAAiB,MAAMjD,kBAAkB0B;YAC/C,MAAMwB,eAAe,GAAGD,eAAe,UAAU,CAAC;YAClDpB,QAAQY,IAAI,CAAC,CAAC,QAAQ,EAAES,aAAa,KAAK,CAAC;YAE3C,IAAI;gBACF,8CAA8C;gBAC9C,MAAMC,eAAehE,MAAM8D,gBAAgB;oBAAC;oBAAO;iBAAQ,EAAE;oBAC3DrB,KAAKF;oBACL0B,OAAO;wBAAC;wBAAU;wBAAQ;qBAAO;gBACnC;gBAEA,wBAAwB;gBACxBD,aAAaE,MAAM,EAAEC,GAAG,QAAQ,CAACC;oBAC/B,MAAMC,QAAQD,KAAKE,QAAQ,GAAGC,KAAK,CAAC;oBACpCF,MAAMG,OAAO,CAAC,CAACC;wBACb,IAAIA,KAAKC,IAAI,IAAI;4BACf,sCAAsC;4BACtCC,QAAQ/D,GAAG,CAACV,GAAG0E,GAAG,CAAC,CAAC,MAAM,EAAEH,MAAM,IAAG,kBAAkB;wBACzD;oBACF;gBACF;gBAEAT,aAAaa,MAAM,EAAEV,GAAG,QAAQ,CAACC;oBAC/B,MAAMC,QAAQD,KAAKE,QAAQ,GAAGC,KAAK,CAAC;oBACpCF,MAAMG,OAAO,CAAC,CAACC;wBACb,IAAIA,KAAKC,IAAI,IAAI;4BACf,sCAAsC;4BACtCC,QAAQ/D,GAAG,CAACV,GAAG0E,GAAG,CAAC,CAAC,MAAM,EAAEH,MAAM,IAAG,iBAAiB;wBACxD;oBACF;gBACF;gBAEA,MAAMK,WAAW,MAAM,IAAIC,QAAgB,CAACC;oBAC1ChB,aAAaG,EAAE,CAAC,SAASa;gBAC3B;gBAEA,IAAIF,aAAa,GAAG;oBAClB,MAAM,IAAIG,MAAM,CAAC,+BAA+B,EAAEH,UAAU;gBAC9D;gBAEA/E,EAAEa,GAAG,CAACsE,OAAO,CAAChF,GAAGiF,KAAK,CAAC;YACzB,EAAE,OAAOvC,OAAO;gBACd7C,EAAEa,GAAG,CAACgC,KAAK,CAAC1C,GAAG2C,GAAG,CAAC;gBACnB,IAAID,iBAAiBqC,SAAS3C,QAAQ8C,KAAK,EAAE;oBAC3CxE,IAAIgC,KAAK,CAAC,CAAC,aAAa,EAAEA,MAAMO,OAAO,EAAE;gBAC3C;gBACAX,QAAQM,IAAI,CAAC;YACf;YAEA,qBAAqB;YACrBO,YAAY,MAAM3C,YAAY6B;YAC9B,IAAI,CAACc,WAAW;gBACdtD,EAAEa,GAAG,CAACgC,KAAK,CAAC1C,GAAG2C,GAAG,CAAC;gBACnB9C,EAAEiD,IAAI,CAAC,kDAAkD;gBACzDR,QAAQM,IAAI,CAAC;YACf;QACF,OAAO;YACLJ,QAAQY,IAAI,CAACpD,GAAGiF,KAAK,CAAC;YACtBpF,EAAEiD,IAAI,CACJ;gBACE,GAAG9C,GAAGmF,IAAI,CAAC,aAAa,CAAC,EAAEhC,UAAUiC,OAAO,EAAE;gBAC9C,GAAGpF,GAAGmF,IAAI,CAAC,SAAS,CAAC,EAAEzD,WAAWyB,UAAUkC,SAAS,GAAG;aACzD,CAACC,IAAI,CAAC,OACP;QAEJ;QAEA,qCAAqC;QAErC,oBAAoB;QACpB9C,QAAQU,KAAK,CAAC;QACd,IAAI;YACF,MAAM5C,gBAAgB+B;YACtBG,QAAQY,IAAI,CAACpD,GAAGiF,KAAK,CAAC;QACxB,EAAE,OAAOvC,OAAO;YACdF,QAAQY,IAAI,CAACpD,GAAG2C,GAAG,CAAC;YACpB,MAAMD;QACR;QAEA,wBAAwB;QACxBF,QAAQU,KAAK,CAAC;QACd,MAAMqC,eAAe,MAAMlF,oBAAoBgC;QAC/C,MAAMmD,UAAUzF,KAAKuF,IAAI,CAACjD,aAAa;QACvC,MAAMoD,UAAU,MAAMlF,YAAYiF;QAClChD,QAAQY,IAAI,CAACpD,GAAGiF,KAAK,CAAC,CAAC,EAAE,EAAEM,aAAaG,MAAM,CAAC,uBAAuB,CAAC;QAEvEhF,IAAIwE,KAAK,CAAC,CAAC,iBAAiB,EAAElE,YAAYyE,UAAU;QACpD/E,IAAIwE,KAAK,CAAC,CAAC,eAAe,EAAEK,aAAaG,MAAM,CAAC,MAAM,CAAC;QAEvD,sCAAsC;QAEtC,IAAI,CAACtD,QAAQmB,GAAG,EAAE;YAChB1D,EAAEiD,IAAI,CACJ;gBACE,GAAG9C,GAAGmF,IAAI,CAAC,aAAa,CAAC,EAAEhC,UAAUiC,OAAO,EAAE;gBAC9C,GAAGpF,GAAGmF,IAAI,CAAC,SAAS,CAAC,EAAEzD,WAAWyB,UAAUkC,SAAS,GAAG;gBACxD,GAAGrF,GAAGmF,IAAI,CAAC,cAAc,CAAC,EAAEnE,YAAYyE,UAAU;gBAClD,GAAGzF,GAAGmF,IAAI,CAAC,kBAAkB,CAAC,EAAEI,aAAaG,MAAM,CAAC,MAAM,CAAC;aAC5D,CAACJ,IAAI,CAAC,OACP;YAGF,MAAMK,eAAe,MAAM9F,EAAE4D,OAAO,CAAC;gBACnCC,cAAc;gBACdT,SAAS;YACX;YAEA,IAAIpD,EAAE8D,QAAQ,CAACgC,iBAAiB,CAACA,cAAc;gBAC7C9F,EAAEa,GAAG,CAACuC,OAAO,CAAC;gBACdX,QAAQM,IAAI,CAAC;YACf;QACF;QAEA,mCAAmC;QAEnC,4BAA4B;QAC5BJ,QAAQU,KAAK,CAAC;QACd,IAAI0C;QACJ,IAAIC;QACJ,IAAIC;QAEJ,IAAI;YACF,MAAMC,iBAAiB,MAAM9F,iBAAiB+C,aAAaH,UAAU;gBACnE0C;YACF;YAEAK,eAAeG,eAAeH,YAAY;YAC1CC,gBAAgBE,eAAeF,aAAa;YAC5CC,wBAAwBC,eAAeD,qBAAqB;YAE5DpF,IAAIwE,KAAK,CAAC,CAAC,eAAe,EAAEU,cAAc;QAC5C,EAAE,OAAOlD,OAAO;YACdF,QAAQY,IAAI,CAACpD,GAAG2C,GAAG,CAAC;YACpB,MAAMD;QACR;QAEA,sBAAsB;QACtBF,QAAQS,OAAO,CAAC,CAAC,gBAAgB,EAAEjC,YAAYyE,SAAS,IAAI,CAAC;QAC7D,IAAI;YACF,MAAM5E,gBAAgB2E,SAASK;YAC/BrD,QAAQY,IAAI,CAACpD,GAAGiF,KAAK,CAAC,CAAC,iBAAiB,EAAEjE,YAAYyE,SAAS,CAAC,CAAC;QACnE,EAAE,OAAO/C,OAAO;YACdF,QAAQY,IAAI,CAACpD,GAAG2C,GAAG,CAAC;YACpB,MAAMD;QACR;QAEA,+BAA+B;QAC/B,IAAI6C,aAAaG,MAAM,GAAG,GAAG;YAC3BlD,QAAQU,KAAK,CAAC;YAEd,IAAI;gBACF,MAAM8C,gBAAgB,MAAMlF,mBAC1BuB,aACAyD,uBACA,CAACG,UAAUC;oBACT1D,QAAQS,OAAO,CAAC,CAAC,yBAAyB,EAAEgD,SAAS,CAAC,EAAEC,MAAM,IAAI,CAAC;gBACrE;gBAGF,IAAIF,cAAcG,YAAY,GAAG,GAAG;oBAClC3D,QAAQY,IAAI,CACVpD,GAAGqD,MAAM,CACP,CAAC,EAAE,EAAE2C,cAAcI,cAAc,CAAC,CAAC,EAAEb,aAAaG,MAAM,CAAC,kBAAkB,EAAEM,cAAcG,YAAY,CAAC,QAAQ,CAAC;oBAGrHtG,EAAEa,GAAG,CAAC2F,OAAO,CACX,GAAGL,cAAcG,YAAY,CAAC,uDAAuD,CAAC;gBAE1F,OAAO;oBACL3D,QAAQY,IAAI,CACVpD,GAAGiF,KAAK,CACN,CAAC,0BAA0B,EAAEe,cAAcI,cAAc,CAAC,QAAQ,EAAEpF,YAAYgF,cAAcM,kBAAkB,EAAE,CAAC,CAAC;gBAG1H;YACF,EAAE,OAAO5D,OAAO;gBACdF,QAAQY,IAAI,CAACpD,GAAG2C,GAAG,CAAC;gBACpB,MAAMD;YACR;QACF;QAEA,6BAA6B;QAC7BF,QAAQU,KAAK,CAAC;QACd,IAAI;YACF,MAAMhD,kBAAkB8C,aAAaH,UAAU;gBAC7C+C;YACF;YACApD,QAAQY,IAAI,CAACpD,GAAGiF,KAAK,CAAC;QACxB,EAAE,OAAOvC,OAAO;YACdF,QAAQY,IAAI,CAACpD,GAAG2C,GAAG,CAAC;YACpB,MAAMD;QACR;QAEA,yCAAyC;QACzC,mDAAmD;QACnD,yEAAyE;QAEzE,iBAAiB;QACjB7C,EAAEa,GAAG,CAACuC,OAAO,CAAC;IAEd,0DAA0D;IAC1D,sHAAsH;IACxH,EAAE,OAAOP,OAAO;QACdF,QAAQY,IAAI,CAACpD,GAAG2C,GAAG,CAAC;QACpB,MAAMM,UAAUP,iBAAiBqC,QAAQrC,MAAMO,OAAO,GAAG;QACzDpD,EAAEa,GAAG,CAACgC,KAAK,CAAC1C,GAAG2C,GAAG,CAAC,CAAC,EAAE,EAAEM,SAAS;QACjC,IAAIb,QAAQ8C,KAAK,IAAIxC,iBAAiBqC,SAASrC,MAAM6D,KAAK,EAAE;YAC1D7F,IAAIgC,KAAK,CAAC,CAAC,aAAa,EAAEA,MAAM6D,KAAK,EAAE;QACzC;QACAjE,QAAQM,IAAI,CAAC;IACf;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AA6BA;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,wBAAwB;IACxB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,gDAAgD;IAChD,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,+BAA+B;IAC/B,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,mDAAmD;IACnD,GAAG,CAAC,EAAE,OAAO,CAAA;CACd;AAiDD;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyS5E"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAiCA;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,wBAAwB;IACxB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,gDAAgD;IAChD,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,+BAA+B;IAC/B,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,mDAAmD;IACnD,GAAG,CAAC,EAAE,OAAO,CAAA;CACd;AAoED;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuW5E"}
@@ -2,14 +2,18 @@ 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 { getDatasetInfo } from '../api/control-plane.js';
5
6
  import { FigmaApiError } from '../api/figma-api.js';
6
7
  import { getValidAccessToken } from '../auth/oauth-flow.js';
7
8
  import { getValidProjectToken, ProjectTokenError } from '../auth/project-token.js';
8
9
  import { getTokenStore } from '../auth/token-store.js';
10
+ import { ensureGitignore } from '../utils/config.js';
9
11
  import { addOrUpdateEnvVar } from '../utils/env-management.js';
10
12
  import { isDebug } from '../utils/is-debug.js';
13
+ import { applyLambdaModifications } from '../utils/lambda-config.js';
11
14
  import * as log from '../utils/log.js';
12
15
  import { displayManualInstructions, ensurePayloadFigmaConfig } from '../utils/payload-config-modifier.js';
16
+ import { runScript } from '../utils/payload-package-check.js';
13
17
  import { detectPayloadProject, hasRequiredDependencies, initializeGitRepo, installDependencies, isInProjectDirectory, scaffoldProject, validatePayloadVersion } from '../utils/project.js';
14
18
  import { loginCommand } from './login.js';
15
19
  /**
@@ -47,6 +51,19 @@ import { loginCommand } from './login.js';
47
51
  // Don't exit - token generation failure shouldn't block initialization
48
52
  }
49
53
  }
54
+ /**
55
+ * Fetch dataset info from the API
56
+ * Throws on error - caller must handle
57
+ */ async function fetchDatasetInfo(accessToken, datasetId) {
58
+ log.debug('Fetching dataset info...');
59
+ const datasetInfo = await getDatasetInfo(accessToken, datasetId);
60
+ log.debug('Dataset info retrieved successfully');
61
+ return {
62
+ oauthClientId: datasetInfo.oauthClientId,
63
+ oauthClientSecret: datasetInfo.oauthClientSecret,
64
+ tenantId: datasetInfo.tenantId
65
+ };
66
+ }
50
67
  /**
51
68
  * Handle the `init` command
52
69
  *
@@ -104,6 +121,22 @@ import { loginCommand } from './login.js';
104
121
  process.exit(1);
105
122
  }
106
123
  const contentSystemId = options.id;
124
+ // Fetch dataset info (required - will fail if feature not enabled)
125
+ let datasetInfo = null;
126
+ if (!options.skipAuth) {
127
+ const accessToken = await getValidAccessToken(tokenStore);
128
+ if (accessToken) {
129
+ try {
130
+ datasetInfo = await fetchDatasetInfo(accessToken, contentSystemId);
131
+ } catch (error) {
132
+ const message = error instanceof Error ? error.message : 'Unknown error';
133
+ p.log.error(pc.red(`✗ Unable to fetch dataset info for content system "${contentSystemId}"`));
134
+ p.log.error(pc.dim(`Details: ${message}`));
135
+ p.note('This may indicate the CMS feature is not enabled for your account.\n' + 'Contact support if you believe this is an error.', 'Error');
136
+ process.exit(1);
137
+ }
138
+ }
139
+ }
107
140
  // Step 4: Detect or scaffold Payload project
108
141
  const isInProject = await isInProjectDirectory();
109
142
  if (isInProject) {
@@ -157,10 +190,16 @@ import { loginCommand } from './login.js';
157
190
  p.log.warn(pc.yellow(`⚠ ${warning}`));
158
191
  });
159
192
  }
160
- // Write content system ID to .env file
193
+ // Write environment variables to .env file
161
194
  try {
162
195
  await addOrUpdateEnvVar(process.cwd(), 'FIGMA_CONTENT_SYSTEM_ID', contentSystemId);
163
196
  log.debug(`Added FIGMA_CONTENT_SYSTEM_ID to .env file`);
197
+ if (datasetInfo) {
198
+ await addOrUpdateEnvVar(process.cwd(), 'FIGMA_TENANT_ID', datasetInfo.tenantId);
199
+ await addOrUpdateEnvVar(process.cwd(), 'FIGMA_OAUTH_CLIENT_ID', datasetInfo.oauthClientId);
200
+ await addOrUpdateEnvVar(process.cwd(), 'FIGMA_OAUTH_CLIENT_SECRET', datasetInfo.oauthClientSecret);
201
+ log.debug(`Added FIGMA_TENANT_ID, FIGMA_OAUTH_CLIENT_ID, FIGMA_OAUTH_CLIENT_SECRET to .env file`);
202
+ }
164
203
  } catch (error) {
165
204
  p.log.warn(pc.yellow(`⚠ Failed to update .env file: ${error instanceof Error ? error.message : 'Unknown error'}`));
166
205
  }
@@ -184,6 +223,16 @@ import { loginCommand } from './login.js';
184
223
  p.note('Create a new project or manually add Payload dependencies', 'Action Required');
185
224
  process.exit(1);
186
225
  }
226
+ // Generate import map to prevent errors on first pnpm dev
227
+ await runScript(process.cwd(), 'generate:importmap', projectInfo.packageManager || 'pnpm');
228
+ // Apply Lambda modifications (run.sh, build_for_lambda.sh, standalone output, etc.)
229
+ await applyLambdaModifications(process.cwd());
230
+ // Ensure .gitignore has required entries
231
+ await ensureGitignore(process.cwd(), [
232
+ '.env',
233
+ '.env.local',
234
+ 'lambda.zip'
235
+ ]);
187
236
  // Generate project token (unless skipping auth)
188
237
  if (!options.skipAuth) {
189
238
  await generateProjectTokenWithFeedback(tokenStore, contentSystemId, s);
@@ -264,13 +313,21 @@ import { loginCommand } from './login.js';
264
313
  p.log.warn(pc.yellow(`⚠ ${warning}`));
265
314
  });
266
315
  }
267
- // Write content system ID to .env file
316
+ // Write environment variables to .env file
268
317
  try {
269
318
  await addOrUpdateEnvVar(fullPath, 'FIGMA_CONTENT_SYSTEM_ID', contentSystemId);
270
319
  log.debug(`Added FIGMA_CONTENT_SYSTEM_ID to .env file`);
320
+ if (datasetInfo) {
321
+ await addOrUpdateEnvVar(fullPath, 'FIGMA_TENANT_ID', datasetInfo.tenantId);
322
+ await addOrUpdateEnvVar(fullPath, 'FIGMA_OAUTH_CLIENT_ID', datasetInfo.oauthClientId);
323
+ await addOrUpdateEnvVar(fullPath, 'FIGMA_OAUTH_CLIENT_SECRET', datasetInfo.oauthClientSecret);
324
+ log.debug(`Added FIGMA_TENANT_ID, FIGMA_OAUTH_CLIENT_ID, FIGMA_OAUTH_CLIENT_SECRET to .env file`);
325
+ }
271
326
  } catch (error) {
272
327
  p.log.warn(pc.yellow(`⚠ Failed to update .env file: ${error instanceof Error ? error.message : 'Unknown error'}`));
273
328
  }
329
+ // Generate import map to prevent errors on first pnpm dev
330
+ await runScript(fullPath, 'generate:importmap', 'pnpm');
274
331
  // Generate project token (unless skipping auth)
275
332
  if (!options.skipAuth) {
276
333
  await generateProjectTokenWithFeedback(tokenStore, contentSystemId, s);