@payloadcms/figma 0.0.1-alpha.29 → 0.0.1-alpha.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/figma-api.d.ts.map +1 -1
- package/dist/api/figma-api.js +4 -0
- package/dist/api/figma-api.js.map +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +32 -50
- package/dist/commands/deploy.js.map +1 -1
- package/dist/db-content-api/README.md +108 -0
- package/dist/db-content-api/generated/content-api-types.d.ts +763 -190
- package/dist/db-content-api/generated/content-api-types.d.ts.map +1 -1
- package/dist/db-content-api/generated/content-api-types.js.map +1 -1
- package/dist/db-content-api/index.d.ts +6 -19
- package/dist/db-content-api/index.d.ts.map +1 -1
- package/dist/db-content-api/index.js +396 -687
- package/dist/db-content-api/index.js.map +1 -1
- package/dist/db-content-api/temp-utilities/pagination.d.ts +13 -0
- package/dist/db-content-api/temp-utilities/pagination.d.ts.map +1 -0
- package/dist/db-content-api/temp-utilities/pagination.js +45 -0
- package/dist/db-content-api/temp-utilities/pagination.js.map +1 -0
- package/dist/db-content-api/temp-utilities/slug.d.ts +2 -0
- package/dist/db-content-api/temp-utilities/slug.d.ts.map +1 -0
- package/dist/db-content-api/temp-utilities/slug.js +3 -0
- package/dist/db-content-api/temp-utilities/slug.js.map +1 -0
- package/dist/db-content-api/temp-utilities/sorting.d.ts +7 -0
- package/dist/db-content-api/temp-utilities/sorting.d.ts.map +1 -0
- package/dist/db-content-api/temp-utilities/sorting.js +71 -0
- package/dist/db-content-api/temp-utilities/sorting.js.map +1 -0
- package/dist/db-content-api/temp-utilities/unwrapDocument.d.ts +4 -0
- package/dist/db-content-api/temp-utilities/unwrapDocument.d.ts.map +1 -0
- package/dist/db-content-api/temp-utilities/unwrapDocument.js +45 -0
- package/dist/db-content-api/temp-utilities/unwrapDocument.js.map +1 -0
- package/dist/db-content-api/utilities/auth.d.ts +30 -0
- package/dist/db-content-api/utilities/auth.d.ts.map +1 -0
- package/dist/db-content-api/utilities/auth.js +81 -0
- package/dist/db-content-api/utilities/auth.js.map +1 -0
- package/dist/db-content-api/utilities/data.d.ts +24 -0
- package/dist/db-content-api/utilities/data.d.ts.map +1 -0
- package/dist/db-content-api/utilities/data.js +118 -0
- package/dist/db-content-api/utilities/data.js.map +1 -0
- package/dist/db-content-api/utilities/joins.d.ts +55 -0
- package/dist/db-content-api/utilities/joins.d.ts.map +1 -0
- package/dist/db-content-api/utilities/joins.js +130 -0
- package/dist/db-content-api/utilities/joins.js.map +1 -0
- package/dist/db-content-api/utilities/where.d.ts +12 -0
- package/dist/db-content-api/utilities/where.d.ts.map +1 -0
- package/dist/db-content-api/utilities/where.js +63 -0
- package/dist/db-content-api/utilities/where.js.map +1 -0
- package/dist/plugin/build-config.d.ts +1 -0
- package/dist/plugin/build-config.d.ts.map +1 -1
- package/dist/plugin/build-config.js +38 -0
- package/dist/plugin/build-config.js.map +1 -1
- package/dist/storage-content-api/api-types.d.ts +161 -0
- package/dist/storage-content-api/api-types.d.ts.map +1 -0
- package/dist/storage-content-api/api-types.js +6 -0
- package/dist/storage-content-api/api-types.js.map +1 -0
- package/dist/storage-content-api/client.d.ts +6 -0
- package/dist/storage-content-api/client.d.ts.map +1 -0
- package/dist/storage-content-api/client.js +36 -0
- package/dist/storage-content-api/client.js.map +1 -0
- package/dist/storage-content-api/generateURL.d.ts +7 -0
- package/dist/storage-content-api/generateURL.d.ts.map +1 -0
- package/dist/storage-content-api/generateURL.js +7 -0
- package/dist/storage-content-api/generateURL.js.map +1 -0
- package/dist/storage-content-api/handleDelete.d.ts +8 -0
- package/dist/storage-content-api/handleDelete.d.ts.map +1 -0
- package/dist/storage-content-api/handleDelete.js +15 -0
- package/dist/storage-content-api/handleDelete.js.map +1 -0
- package/dist/storage-content-api/handleUpload.d.ts +11 -0
- package/dist/storage-content-api/handleUpload.d.ts.map +1 -0
- package/dist/storage-content-api/handleUpload.js +126 -0
- package/dist/storage-content-api/handleUpload.js.map +1 -0
- package/dist/storage-content-api/index.d.ts +5 -0
- package/dist/storage-content-api/index.d.ts.map +1 -0
- package/dist/storage-content-api/index.js +31 -0
- package/dist/storage-content-api/index.js.map +1 -0
- package/dist/storage-content-api/staticHandler.d.ts +10 -0
- package/dist/storage-content-api/staticHandler.d.ts.map +1 -0
- package/dist/storage-content-api/staticHandler.js +35 -0
- package/dist/storage-content-api/staticHandler.js.map +1 -0
- package/dist/storage-content-api/types.d.ts +10 -0
- package/dist/storage-content-api/types.d.ts.map +1 -0
- package/dist/storage-content-api/types.js +3 -0
- package/dist/storage-content-api/types.js.map +1 -0
- package/dist/utils/lambda-config.d.ts.map +1 -1
- package/dist/utils/lambda-config.js +1 -0
- package/dist/utils/lambda-config.js.map +1 -1
- package/package.json +5 -2
- package/dist/db-content-api/types.d.ts +0 -31
- package/dist/db-content-api/types.d.ts.map +0 -1
- package/dist/db-content-api/types.js +0 -7
- package/dist/db-content-api/types.js.map +0 -1
|
@@ -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,
|
|
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,CAuIvB"}
|
package/dist/api/figma-api.js
CHANGED
|
@@ -118,6 +118,10 @@
|
|
|
118
118
|
} catch {
|
|
119
119
|
// Ignore JSON parse errors for error body
|
|
120
120
|
}
|
|
121
|
+
// Provide actionable error for common cases
|
|
122
|
+
if (response.status === 404) {
|
|
123
|
+
errorMessage = `Content System ID "${tenantId}" not found. Ensure FIGMA_CONTENT_SYSTEM_ID is set to a valid content system ID in your .env file.`;
|
|
124
|
+
}
|
|
121
125
|
throw new FigmaApiError(errorMessage, response.status);
|
|
122
126
|
}
|
|
123
127
|
const data = await response.json();
|
|
@@ -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 // 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
|
+
{"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 // Provide actionable error for common cases\n if (response.status === 404) {\n errorMessage = `Content System ID \"${tenantId}\" not found. Ensure FIGMA_CONTENT_SYSTEM_ID is set to a valid content system ID in your .env file.`\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":"AAiBA;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,wBAAwB;IACxB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,gBAAgB;IAChB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,+CAA+C;IAC/C,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,gCAAgC;IAChC,GAAG,CAAC,EAAE,OAAO,CAAA;CACd;AA6BD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAiBA;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,wBAAwB;IACxB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,gBAAgB;IAChB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,+CAA+C;IAC/C,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,gCAAgC;IAChC,GAAG,CAAC,EAAE,OAAO,CAAA;CACd;AA6BD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyPhF"}
|
package/dist/commands/deploy.js
CHANGED
|
@@ -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, performDeployment } from '../api/control-plane.js';
|
|
5
|
+
import { ControlPlaneError, 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';
|
|
@@ -187,61 +187,37 @@ import { loginCommand } from './login.js';
|
|
|
187
187
|
// ===== DEPLOYMENT EXECUTION =====
|
|
188
188
|
// Step 1: Create deployment
|
|
189
189
|
spinner.start('Creating deployment...');
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
deploymentId = createResponse.deploymentId;
|
|
198
|
-
codeUploadUrl = createResponse.codeUploadUrl;
|
|
199
|
-
staticAssetUploadUrls = createResponse.staticAssetUploadUrls;
|
|
200
|
-
log.debug(`Deployment ID: ${deploymentId}`);
|
|
201
|
-
} catch (error) {
|
|
202
|
-
spinner.stop(pc.red('✗ Failed to create deployment'));
|
|
203
|
-
throw error;
|
|
204
|
-
}
|
|
190
|
+
const createResponse = await createDeployment(accessToken, tenantId, {
|
|
191
|
+
staticAssets
|
|
192
|
+
});
|
|
193
|
+
const deploymentId = createResponse.deploymentId;
|
|
194
|
+
const codeUploadUrl = createResponse.codeUploadUrl;
|
|
195
|
+
const staticAssetUploadUrls = createResponse.staticAssetUploadUrls;
|
|
196
|
+
log.debug(`Deployment ID: ${deploymentId}`);
|
|
205
197
|
// Step 2: Upload code
|
|
206
198
|
spinner.message(`Uploading code (${formatBytes(zipSize)})...`);
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
spinner.stop(pc.green(`✓ Code uploaded (${formatBytes(zipSize)})`));
|
|
210
|
-
} catch (error) {
|
|
211
|
-
spinner.stop(pc.red('✗ Failed to upload code'));
|
|
212
|
-
throw error;
|
|
213
|
-
}
|
|
199
|
+
await uploadLambdaZip(zipPath, codeUploadUrl);
|
|
200
|
+
spinner.stop(pc.green(`✓ Code uploaded (${formatBytes(zipSize)})`));
|
|
214
201
|
// Step 3: Upload static assets
|
|
215
202
|
if (staticAssets.length > 0) {
|
|
216
203
|
spinner.start('Uploading static assets...');
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
spinner.stop(pc.green(`✓ Static assets uploaded (${uploadResults.assetsUploaded} files, ${formatBytes(uploadResults.totalBytesUploaded)})`));
|
|
226
|
-
}
|
|
227
|
-
} catch (error) {
|
|
228
|
-
spinner.stop(pc.red('✗ Failed to upload assets'));
|
|
229
|
-
throw error;
|
|
204
|
+
const uploadResults = await uploadStaticAssets(projectPath, staticAssetUploadUrls, (uploaded, total)=>{
|
|
205
|
+
spinner.message(`Uploading static assets (${uploaded}/${total})...`);
|
|
206
|
+
});
|
|
207
|
+
if (uploadResults.assetsFailed > 0) {
|
|
208
|
+
spinner.stop(pc.yellow(`⚠ ${uploadResults.assetsUploaded}/${staticAssets.length} assets uploaded (${uploadResults.assetsFailed} failed)`));
|
|
209
|
+
p.log.warning(`${uploadResults.assetsFailed} assets failed to upload. Deployment may be incomplete.`);
|
|
210
|
+
} else {
|
|
211
|
+
spinner.stop(pc.green(`✓ Static assets uploaded (${uploadResults.assetsUploaded} files, ${formatBytes(uploadResults.totalBytesUploaded)})`));
|
|
230
212
|
}
|
|
231
213
|
}
|
|
232
214
|
// Step 4: Perform deployment
|
|
233
215
|
spinner.start('Performing deployment...');
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
tenantDomains = result.tenantDomains ?? [];
|
|
240
|
-
spinner.stop(pc.green('✓ Deployment initiated'));
|
|
241
|
-
} catch (error) {
|
|
242
|
-
spinner.stop(pc.red('✗ Failed to perform deployment'));
|
|
243
|
-
throw error;
|
|
244
|
-
}
|
|
216
|
+
const result = await performDeployment(accessToken, tenantId, {
|
|
217
|
+
deploymentId
|
|
218
|
+
});
|
|
219
|
+
const tenantDomains = result.tenantDomains ?? [];
|
|
220
|
+
spinner.stop(pc.green('✓ Deployment initiated'));
|
|
245
221
|
// TODO: Add deployment progress tracking
|
|
246
222
|
// Future: Poll deployment status and show progress
|
|
247
223
|
// API endpoint: GET /v1/tenant/{tenant_id}/deploy/{deployment_id}/status
|
|
@@ -251,9 +227,15 @@ import { loginCommand } from './login.js';
|
|
|
251
227
|
}
|
|
252
228
|
p.log.step('This usually takes 2-3 minutes to complete.');
|
|
253
229
|
} catch (error) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
230
|
+
if (error instanceof ControlPlaneError) {
|
|
231
|
+
spinner.stop(pc.red(`✗ ${error.message}`));
|
|
232
|
+
if (error.statusCode) {
|
|
233
|
+
p.log.message(pc.dim(` HTTP ${error.statusCode}`));
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
237
|
+
spinner.stop(pc.red(`✗ ${message}`));
|
|
238
|
+
}
|
|
257
239
|
if (options.debug && error instanceof Error && error.stack) {
|
|
258
240
|
log.error(`Stack trace: ${error.stack}`);
|
|
259
241
|
}
|
|
@@ -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, 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 { getEnvVar } from '../utils/env-management.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 the build step (use existing build) */\n skipBuild?: boolean\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 let tenantId = options.id\n if (!tenantId) {\n tenantId = (await getEnvVar(projectPath, 'FIGMA_TENANT_ID')) ?? undefined\n }\n if (!tenantId) {\n p.log.error(pc.red('✗ Tenant ID not found. Run `@payloadcms/figma init` first.'))\n process.exit(1)\n }\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 =====\n\n let buildInfo: Awaited<ReturnType<typeof detectBuild>>\n\n if (options.skipBuild) {\n // User asserts build exists - verify it\n spinner.start('Checking for build...')\n buildInfo = await detectBuild(projectPath)\n\n if (!buildInfo) {\n spinner.stop(pc.red('✗ No build found'))\n p.log.error('Use deploy without --skip-build to build first.')\n process.exit(1)\n }\n\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 } else {\n // Always build (no prompt)\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 const packageManager = await getPackageManager(projectPath)\n const buildCommand = `${packageManager} run build`\n p.log.step(`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}`))\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}`))\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 // Verify build output\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 }\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\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 let tenantDomains: string[] = []\n try {\n const result = await performDeployment(accessToken, tenantId, {\n deploymentId,\n })\n tenantDomains = result.tenantDomains ?? []\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 if (tenantDomains.length > 0) {\n p.log.step(`URL: ${pc.cyan(`https://${tenantDomains[0]}`)}`)\n }\n p.log.step('This usually takes 2-3 minutes to complete.')\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","getEnvVar","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","tenantId","id","undefined","error","red","exit","note","tokenStore","accessToken","message","buildInfo","skipBuild","start","stop","green","cyan","buildId","timestamp","join","buildCmd","packageManager","buildCommand","step","buildProcess","stdio","stdout","on","data","lines","toString","split","forEach","line","trim","console","dim","stderr","exitCode","Promise","resolve","Error","success","debug","staticAssets","zipPath","zipSize","length","yes","deploymentId","codeUploadUrl","staticAssetUploadUrls","createResponse","uploadResults","uploaded","total","assetsFailed","yellow","assetsUploaded","warning","totalBytesUploaded","tenantDomains","result","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 { ControlPlaneError, 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 { getEnvVar } from '../utils/env-management.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 the build step (use existing build) */\n skipBuild?: boolean\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 let tenantId = options.id\n if (!tenantId) {\n tenantId = (await getEnvVar(projectPath, 'FIGMA_TENANT_ID')) ?? undefined\n }\n if (!tenantId) {\n p.log.error(pc.red('✗ Tenant ID not found. Run `@payloadcms/figma init` first.'))\n process.exit(1)\n }\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 =====\n\n let buildInfo: Awaited<ReturnType<typeof detectBuild>>\n\n if (options.skipBuild) {\n // User asserts build exists - verify it\n spinner.start('Checking for build...')\n buildInfo = await detectBuild(projectPath)\n\n if (!buildInfo) {\n spinner.stop(pc.red('✗ No build found'))\n p.log.error('Use deploy without --skip-build to build first.')\n process.exit(1)\n }\n\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 } else {\n // Always build (no prompt)\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 const packageManager = await getPackageManager(projectPath)\n const buildCommand = `${packageManager} run build`\n p.log.step(`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}`))\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}`))\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 // Verify build output\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 }\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\n // ===== DEPLOYMENT EXECUTION =====\n\n // Step 1: Create deployment\n spinner.start('Creating deployment...')\n const createResponse = await createDeployment(accessToken, tenantId, {\n staticAssets,\n })\n\n const deploymentId = createResponse.deploymentId\n const codeUploadUrl = createResponse.codeUploadUrl\n const staticAssetUploadUrls = createResponse.staticAssetUploadUrls\n\n log.debug(`Deployment ID: ${deploymentId}`)\n\n // Step 2: Upload code\n spinner.message(`Uploading code (${formatBytes(zipSize)})...`)\n await uploadLambdaZip(zipPath, codeUploadUrl)\n spinner.stop(pc.green(`✓ Code uploaded (${formatBytes(zipSize)})`))\n\n // Step 3: Upload static assets\n if (staticAssets.length > 0) {\n spinner.start('Uploading static assets...')\n\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 }\n\n // Step 4: Perform deployment\n spinner.start('Performing deployment...')\n const result = await performDeployment(accessToken, tenantId, {\n deploymentId,\n })\n const tenantDomains = result.tenantDomains ?? []\n spinner.stop(pc.green('✓ Deployment initiated'))\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 if (tenantDomains.length > 0) {\n p.log.step(`URL: ${pc.cyan(`https://${tenantDomains[0]}`)}`)\n }\n p.log.step('This usually takes 2-3 minutes to complete.')\n } catch (error) {\n if (error instanceof ControlPlaneError) {\n spinner.stop(pc.red(`✗ ${error.message}`))\n if (error.statusCode) {\n p.log.message(pc.dim(` HTTP ${error.statusCode}`))\n }\n } else {\n const message = error instanceof Error ? error.message : 'Unknown error'\n spinner.stop(pc.red(`✗ ${message}`))\n }\n\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","ControlPlaneError","createDeployment","performDeployment","getValidAccessToken","getTokenStore","collectStaticAssets","createLambdaZip","getFileSize","detectBuild","getBuildCommand","getEnvVar","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","tenantId","id","undefined","error","red","exit","note","tokenStore","accessToken","message","buildInfo","skipBuild","start","stop","green","cyan","buildId","timestamp","join","buildCmd","packageManager","buildCommand","step","buildProcess","stdio","stdout","on","data","lines","toString","split","forEach","line","trim","console","dim","stderr","exitCode","Promise","resolve","Error","success","debug","staticAssets","zipPath","zipSize","length","yes","createResponse","deploymentId","codeUploadUrl","staticAssetUploadUrls","uploadResults","uploaded","total","assetsFailed","yellow","assetsUploaded","warning","totalBytesUploaded","result","tenantDomains","statusCode","stack"],"mappings":"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"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Content API Database Adapter Parity
|
|
2
|
+
|
|
3
|
+
This document explains how the Content API database adapter differs from Payload today and how it will behave once alignment is complete.
|
|
4
|
+
|
|
5
|
+
The Content API is an internal service built exclusively for Payload. Because of that, the adapter should match Payload by default and only diverge when there is a strong technical reason.
|
|
6
|
+
|
|
7
|
+
## Where
|
|
8
|
+
|
|
9
|
+
Now
|
|
10
|
+
`{ path: "slug", operator: "equals", value: "hello", type: "text" }`
|
|
11
|
+
|
|
12
|
+
Future
|
|
13
|
+
`{ path: "slug", operator: "equals", value: "hello", type: "text" }`
|
|
14
|
+
|
|
15
|
+
The format stays different. This allows the Content API to evolve its filter schema, for example to support composite types like Point, which are not lexicographically sortable. Semantics must remain identical to Payload.
|
|
16
|
+
|
|
17
|
+
## Sorting
|
|
18
|
+
|
|
19
|
+
Now
|
|
20
|
+
`[{ path: "createdAt", direction: "desc" }]`
|
|
21
|
+
|
|
22
|
+
Future
|
|
23
|
+
`"-createdAt"` or `"createdAt"` or `["-createdAt", "title"]`
|
|
24
|
+
|
|
25
|
+
Sorting will match Payload. It can be a single string or an array of strings using Payload path syntax.
|
|
26
|
+
|
|
27
|
+
## Pagination
|
|
28
|
+
|
|
29
|
+
~~Now~~
|
|
30
|
+
~~`{ limit: 10, offset: 20 }`~~
|
|
31
|
+
|
|
32
|
+
✅ Partially Implemented
|
|
33
|
+
`{ limit: 10, page: 3 }`
|
|
34
|
+
|
|
35
|
+
**Status:**
|
|
36
|
+
|
|
37
|
+
- ✅ Content API main endpoints use `page` (matches Payload)
|
|
38
|
+
|
|
39
|
+
- ⚠️ **Issue #1 (Joins):** `JoinClause` still uses `offset` instead of `page`
|
|
40
|
+
- Payload `JoinQuery`: `{ page?: number, limit?: number }`
|
|
41
|
+
- Content API `JoinClause`: `{ offset?: number, limit?: number }`
|
|
42
|
+
- **Inconsistent** with main endpoints which use `page`
|
|
43
|
+
|
|
44
|
+
**Current workaround for joins:** The adapter converts `page` to `offset`:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// Payload join query
|
|
48
|
+
{ page: 2, limit: 5 }
|
|
49
|
+
// Adapter converts to offset for Content API
|
|
50
|
+
{ offset: 5, limit: 5 }
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Future:**
|
|
54
|
+
|
|
55
|
+
1. Content API should support `skip` parameter for main endpoints
|
|
56
|
+
2. Content API `JoinClause` should use `page` instead of `offset` (consistent with main endpoints)
|
|
57
|
+
3. Content API pagination response should match Payload's flat structure
|
|
58
|
+
|
|
59
|
+
### Pagination Response Structure
|
|
60
|
+
|
|
61
|
+
**Payload format (flat):**
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
{
|
|
65
|
+
docs: [...],
|
|
66
|
+
hasNextPage: boolean,
|
|
67
|
+
hasPrevPage: boolean,
|
|
68
|
+
limit: number,
|
|
69
|
+
nextPage: number | null,
|
|
70
|
+
page: number,
|
|
71
|
+
pagingCounter: number,
|
|
72
|
+
prevPage: number | null,
|
|
73
|
+
totalDocs: number,
|
|
74
|
+
totalPages: number
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Content API format (nested):**
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
{
|
|
82
|
+
data: [...],
|
|
83
|
+
pagination: {
|
|
84
|
+
total: number,
|
|
85
|
+
current: { limit: number, page: number },
|
|
86
|
+
next?: { limit: number, page: number },
|
|
87
|
+
prev?: { limit: number, page: number }
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Current workaround:** The adapter transforms Content API's nested structure to Payload's flat structure using `getPaginationData()` utility.
|
|
93
|
+
|
|
94
|
+
## Slugs
|
|
95
|
+
|
|
96
|
+
Now
|
|
97
|
+
`{ collection: { key: "posts" } }`
|
|
98
|
+
|
|
99
|
+
Future
|
|
100
|
+
`{ collection: { slug: "posts" } }`
|
|
101
|
+
|
|
102
|
+
Collection identifiers will match Payload. Consumers should not be aware of internal collection IDs.
|
|
103
|
+
|
|
104
|
+
## Temporary Conversions
|
|
105
|
+
|
|
106
|
+
Helpers that translate Payload queries into the current Content API format are temporary and placed in the `temp-utilities` folder.
|
|
107
|
+
|
|
108
|
+
They live in the `temp-utilities` folder until the Content API is fully aligned with Payload.
|