@payloadcms/figma 0.0.1-alpha.42 → 0.0.1-alpha.6
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/control-plane.d.ts +1 -22
- package/dist/api/control-plane.d.ts.map +1 -1
- package/dist/api/control-plane.js +22 -55
- package/dist/api/control-plane.js.map +1 -1
- package/dist/api/figma-api.d.ts.map +1 -1
- package/dist/api/figma-api.js +21 -45
- package/dist/api/figma-api.js.map +1 -1
- package/dist/auth/callback-server.d.ts.map +1 -1
- package/dist/auth/callback-server.js +1 -2
- package/dist/auth/callback-server.js.map +1 -1
- package/dist/auth/crypto-utils.d.ts.map +1 -1
- package/dist/auth/crypto-utils.js +5 -5
- package/dist/auth/crypto-utils.js.map +1 -1
- package/dist/auth/jwt-validator.d.ts +1 -2
- package/dist/auth/jwt-validator.d.ts.map +1 -1
- package/dist/auth/jwt-validator.js +2 -2
- package/dist/auth/jwt-validator.js.map +1 -1
- package/dist/auth/oauth-flow.d.ts +1 -1
- package/dist/auth/oauth-flow.d.ts.map +1 -1
- package/dist/auth/oauth-flow.js +11 -15
- package/dist/auth/oauth-flow.js.map +1 -1
- package/dist/auth/project-token.d.ts.map +1 -1
- package/dist/auth/project-token.js +4 -9
- package/dist/auth/project-token.js.map +1 -1
- package/dist/auth/refresh.d.ts +1 -2
- package/dist/auth/refresh.d.ts.map +1 -1
- package/dist/auth/refresh.js +12 -25
- package/dist/auth/refresh.js.map +1 -1
- package/dist/auth/token-store.d.ts +0 -12
- package/dist/auth/token-store.d.ts.map +1 -1
- package/dist/auth/token-store.js +5 -28
- package/dist/auth/token-store.js.map +1 -1
- package/dist/auth/types.d.ts +4 -7
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/auth/types.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -39
- package/dist/cli.js.map +1 -1
- package/dist/commands/deploy.d.ts +0 -4
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +129 -72
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/init.d.ts +5 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +85 -105
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list-tokens.d.ts +3 -3
- package/dist/commands/list-tokens.d.ts.map +1 -1
- package/dist/commands/list-tokens.js +7 -22
- package/dist/commands/list-tokens.js.map +1 -1
- package/dist/commands/login.d.ts +1 -8
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +12 -30
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.d.ts +4 -5
- package/dist/commands/logout.d.ts.map +1 -1
- package/dist/commands/logout.js +13 -56
- package/dist/commands/logout.js.map +1 -1
- package/dist/config/oauth.d.ts +14 -4
- package/dist/config/oauth.d.ts.map +1 -1
- package/dist/config/oauth.js +40 -19
- package/dist/config/oauth.js.map +1 -1
- package/dist/constants.d.ts +18 -26
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +20 -34
- package/dist/constants.js.map +1 -1
- package/dist/exports/client.d.ts +0 -1
- package/dist/exports/client.d.ts.map +1 -1
- package/dist/exports/client.js +0 -1
- package/dist/exports/client.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/dist/oauth/components/LoginButton/index.d.ts.map +1 -1
- package/dist/oauth/components/LoginButton/index.js +6 -50
- package/dist/oauth/components/LoginButton/index.js.map +1 -1
- package/dist/oauth/components/LoginButton/index.scss +3 -50
- package/dist/oauth/components/LogoutButton/index.d.ts.map +1 -1
- package/dist/oauth/components/LogoutButton/index.js +4 -5
- package/dist/oauth/components/LogoutButton/index.js.map +1 -1
- package/dist/oauth/defaults.d.ts.map +1 -1
- package/dist/oauth/defaults.js +5 -29
- package/dist/oauth/defaults.js.map +1 -1
- package/dist/oauth/endpoints/getLoginEndpoint.d.ts.map +1 -1
- package/dist/oauth/endpoints/getLoginEndpoint.js +11 -21
- package/dist/oauth/endpoints/getLoginEndpoint.js.map +1 -1
- package/dist/oauth/endpoints/getLogoutEndpoint.d.ts.map +1 -1
- package/dist/oauth/endpoints/getLogoutEndpoint.js +4 -7
- package/dist/oauth/endpoints/getLogoutEndpoint.js.map +1 -1
- package/dist/oauth/index.d.ts.map +1 -1
- package/dist/oauth/index.js +13 -40
- package/dist/oauth/index.js.map +1 -1
- package/dist/oauth/strategy/index.d.ts +2 -0
- package/dist/oauth/strategy/index.d.ts.map +1 -1
- package/dist/oauth/strategy/index.js +15 -38
- package/dist/oauth/strategy/index.js.map +1 -1
- package/dist/oauth/types.d.ts +6 -6
- package/dist/oauth/types.d.ts.map +1 -1
- package/dist/oauth/types.js.map +1 -1
- package/dist/oauth/utilities/createCookieOptions.d.ts.map +1 -1
- package/dist/oauth/utilities/createCookieOptions.js.map +1 -1
- package/dist/oauth/utilities/getAdminPath.d.ts +8 -0
- package/dist/oauth/utilities/getAdminPath.d.ts.map +1 -0
- package/dist/oauth/utilities/getAdminPath.js +9 -0
- package/dist/oauth/utilities/getAdminPath.js.map +1 -0
- package/dist/oauth/utilities/refreshTokens.d.ts.map +1 -1
- package/dist/oauth/utilities/refreshTokens.js +1 -1
- package/dist/oauth/utilities/refreshTokens.js.map +1 -1
- package/dist/plugin/build-config.d.ts +4 -21
- package/dist/plugin/build-config.d.ts.map +1 -1
- package/dist/plugin/build-config.js +7 -113
- package/dist/plugin/build-config.js.map +1 -1
- package/dist/types.d.ts +0 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/asset-collection.d.ts +6 -3
- package/dist/utils/asset-collection.d.ts.map +1 -1
- package/dist/utils/asset-collection.js +34 -10
- package/dist/utils/asset-collection.js.map +1 -1
- package/dist/utils/config.js +1 -1
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/env-management.d.ts +8 -9
- package/dist/utils/env-management.d.ts.map +1 -1
- package/dist/utils/env-management.js +74 -73
- package/dist/utils/env-management.js.map +1 -1
- package/dist/utils/formatter.d.ts.map +1 -1
- package/dist/utils/formatter.js +5 -3
- package/dist/utils/formatter.js.map +1 -1
- package/dist/utils/lambda-config.d.ts +1 -3
- package/dist/utils/lambda-config.d.ts.map +1 -1
- package/dist/utils/lambda-config.js +32 -38
- package/dist/utils/lambda-config.js.map +1 -1
- package/dist/utils/messages.js +8 -8
- package/dist/utils/messages.js.map +1 -1
- package/dist/utils/payload-config-ast.d.ts +3 -22
- package/dist/utils/payload-config-ast.d.ts.map +1 -1
- package/dist/utils/payload-config-ast.js +31 -89
- package/dist/utils/payload-config-ast.js.map +1 -1
- package/dist/utils/payload-config-finder.d.ts +2 -2
- package/dist/utils/payload-config-finder.d.ts.map +1 -1
- package/dist/utils/payload-config-finder.js +9 -48
- package/dist/utils/payload-config-finder.js.map +1 -1
- package/dist/utils/payload-config-modifier.d.ts.map +1 -1
- package/dist/utils/payload-config-modifier.js +6 -54
- package/dist/utils/payload-config-modifier.js.map +1 -1
- package/dist/utils/payload-package-check.d.ts +1 -12
- package/dist/utils/payload-package-check.d.ts.map +1 -1
- package/dist/utils/payload-package-check.js +5 -63
- package/dist/utils/payload-package-check.js.map +1 -1
- package/dist/utils/project.d.ts.map +1 -1
- package/dist/utils/project.js +5 -8
- package/dist/utils/project.js.map +1 -1
- package/dist/utils/s3-upload.d.ts +1 -0
- package/dist/utils/s3-upload.d.ts.map +1 -1
- package/dist/utils/s3-upload.js +3 -10
- package/dist/utils/s3-upload.js.map +1 -1
- package/dist/utils/token-display.d.ts.map +1 -1
- package/dist/utils/token-display.js +5 -11
- package/dist/utils/token-display.js.map +1 -1
- package/dist/utils/typescript-validator.d.ts.map +1 -1
- package/dist/utils/typescript-validator.js +6 -4
- package/dist/utils/typescript-validator.js.map +1 -1
- package/package.json +9 -27
- package/dist/commands/build-lambda-zip.d.ts +0 -5
- package/dist/commands/build-lambda-zip.d.ts.map +0 -1
- package/dist/commands/build-lambda-zip.js +0 -19
- package/dist/commands/build-lambda-zip.js.map +0 -1
- package/dist/db-content-api/README.md +0 -98
- package/dist/db-content-api/generated/content-api-types.d.ts +0 -3914
- package/dist/db-content-api/generated/content-api-types.d.ts.map +0 -1
- package/dist/db-content-api/generated/content-api-types.js +0 -7
- package/dist/db-content-api/generated/content-api-types.js.map +0 -1
- package/dist/db-content-api/index.d.ts +0 -18
- package/dist/db-content-api/index.d.ts.map +0 -1
- package/dist/db-content-api/index.js +0 -586
- package/dist/db-content-api/index.js.map +0 -1
- package/dist/db-content-api/temp-utilities/slug.d.ts +0 -2
- package/dist/db-content-api/temp-utilities/slug.d.ts.map +0 -1
- package/dist/db-content-api/temp-utilities/slug.js +0 -3
- package/dist/db-content-api/temp-utilities/slug.js.map +0 -1
- package/dist/db-content-api/temp-utilities/sorting.d.ts +0 -3
- package/dist/db-content-api/temp-utilities/sorting.d.ts.map +0 -1
- package/dist/db-content-api/temp-utilities/sorting.js +0 -32
- package/dist/db-content-api/temp-utilities/sorting.js.map +0 -1
- package/dist/db-content-api/temp-utilities/unwrapDocument.d.ts +0 -34
- package/dist/db-content-api/temp-utilities/unwrapDocument.d.ts.map +0 -1
- package/dist/db-content-api/temp-utilities/unwrapDocument.js +0 -53
- package/dist/db-content-api/temp-utilities/unwrapDocument.js.map +0 -1
- package/dist/db-content-api/utilities/auth.d.ts +0 -30
- package/dist/db-content-api/utilities/auth.d.ts.map +0 -1
- package/dist/db-content-api/utilities/auth.js +0 -81
- package/dist/db-content-api/utilities/auth.js.map +0 -1
- package/dist/db-content-api/utilities/buildPathTypes.d.ts +0 -12
- package/dist/db-content-api/utilities/buildPathTypes.d.ts.map +0 -1
- package/dist/db-content-api/utilities/buildPathTypes.js +0 -157
- package/dist/db-content-api/utilities/buildPathTypes.js.map +0 -1
- package/dist/db-content-api/utilities/data/applyDefaults.d.ts +0 -7
- package/dist/db-content-api/utilities/data/applyDefaults.d.ts.map +0 -1
- package/dist/db-content-api/utilities/data/applyDefaults.js +0 -59
- package/dist/db-content-api/utilities/data/applyDefaults.js.map +0 -1
- package/dist/db-content-api/utilities/data/castFieldValue.d.ts +0 -11
- package/dist/db-content-api/utilities/data/castFieldValue.d.ts.map +0 -1
- package/dist/db-content-api/utilities/data/castFieldValue.js +0 -66
- package/dist/db-content-api/utilities/data/castFieldValue.js.map +0 -1
- package/dist/db-content-api/utilities/data/index.d.ts +0 -32
- package/dist/db-content-api/utilities/data/index.d.ts.map +0 -1
- package/dist/db-content-api/utilities/data/index.js +0 -190
- package/dist/db-content-api/utilities/data/index.js.map +0 -1
- package/dist/db-content-api/utilities/data/removeVirtualFields.d.ts +0 -12
- package/dist/db-content-api/utilities/data/removeVirtualFields.d.ts.map +0 -1
- package/dist/db-content-api/utilities/data/removeVirtualFields.js +0 -54
- package/dist/db-content-api/utilities/data/removeVirtualFields.js.map +0 -1
- package/dist/db-content-api/utilities/data/stripFields.d.ts +0 -48
- package/dist/db-content-api/utilities/data/stripFields.d.ts.map +0 -1
- package/dist/db-content-api/utilities/data/stripFields.js +0 -195
- package/dist/db-content-api/utilities/data/stripFields.js.map +0 -1
- package/dist/db-content-api/utilities/joins.d.ts +0 -55
- package/dist/db-content-api/utilities/joins.d.ts.map +0 -1
- package/dist/db-content-api/utilities/joins.js +0 -136
- package/dist/db-content-api/utilities/joins.js.map +0 -1
- package/dist/db-content-api/utilities/where.d.ts +0 -12
- package/dist/db-content-api/utilities/where.d.ts.map +0 -1
- package/dist/db-content-api/utilities/where.js +0 -121
- package/dist/db-content-api/utilities/where.js.map +0 -1
- package/dist/endpoints/health.d.ts +0 -3
- package/dist/endpoints/health.d.ts.map +0 -1
- package/dist/endpoints/health.js +0 -11
- package/dist/endpoints/health.js.map +0 -1
- package/dist/oauth/utilities/isDuplicateKeyError.d.ts +0 -7
- package/dist/oauth/utilities/isDuplicateKeyError.d.ts.map +0 -1
- package/dist/oauth/utilities/isDuplicateKeyError.js +0 -14
- package/dist/oauth/utilities/isDuplicateKeyError.js.map +0 -1
- package/dist/storage-content-api/api-types.d.ts +0 -168
- package/dist/storage-content-api/api-types.d.ts.map +0 -1
- package/dist/storage-content-api/api-types.js +0 -6
- package/dist/storage-content-api/api-types.js.map +0 -1
- package/dist/storage-content-api/client-uploads/ClientUploadHandler.d.ts +0 -9
- package/dist/storage-content-api/client-uploads/ClientUploadHandler.d.ts.map +0 -1
- package/dist/storage-content-api/client-uploads/ClientUploadHandler.js +0 -42
- package/dist/storage-content-api/client-uploads/ClientUploadHandler.js.map +0 -1
- package/dist/storage-content-api/client-uploads/generateSignedURL.d.ts +0 -13
- package/dist/storage-content-api/client-uploads/generateSignedURL.d.ts.map +0 -1
- package/dist/storage-content-api/client-uploads/generateSignedURL.js +0 -32
- package/dist/storage-content-api/client-uploads/generateSignedURL.js.map +0 -1
- package/dist/storage-content-api/client.d.ts +0 -6
- package/dist/storage-content-api/client.d.ts.map +0 -1
- package/dist/storage-content-api/client.js +0 -36
- package/dist/storage-content-api/client.js.map +0 -1
- package/dist/storage-content-api/generateURL.d.ts +0 -7
- package/dist/storage-content-api/generateURL.d.ts.map +0 -1
- package/dist/storage-content-api/generateURL.js +0 -8
- package/dist/storage-content-api/generateURL.js.map +0 -1
- package/dist/storage-content-api/handleDelete.d.ts +0 -8
- package/dist/storage-content-api/handleDelete.d.ts.map +0 -1
- package/dist/storage-content-api/handleDelete.js +0 -17
- package/dist/storage-content-api/handleDelete.js.map +0 -1
- package/dist/storage-content-api/handleUpload.d.ts +0 -11
- package/dist/storage-content-api/handleUpload.d.ts.map +0 -1
- package/dist/storage-content-api/handleUpload.js +0 -144
- package/dist/storage-content-api/handleUpload.js.map +0 -1
- package/dist/storage-content-api/index.d.ts +0 -5
- package/dist/storage-content-api/index.d.ts.map +0 -1
- package/dist/storage-content-api/index.js +0 -31
- package/dist/storage-content-api/index.js.map +0 -1
- package/dist/storage-content-api/staticHandler.d.ts +0 -10
- package/dist/storage-content-api/staticHandler.d.ts.map +0 -1
- package/dist/storage-content-api/staticHandler.js +0 -56
- package/dist/storage-content-api/staticHandler.js.map +0 -1
- package/dist/storage-content-api/types.d.ts +0 -10
- package/dist/storage-content-api/types.d.ts.map +0 -1
- package/dist/storage-content-api/types.js +0 -3
- package/dist/storage-content-api/types.js.map +0 -1
- package/dist/templates/README.md +0 -19
- package/dist/utils/build-lambda-zip.d.ts +0 -14
- package/dist/utils/build-lambda-zip.d.ts.map +0 -1
- package/dist/utils/build-lambda-zip.js +0 -81
- package/dist/utils/build-lambda-zip.js.map +0 -1
- package/dist/utils/format-env-suffix.d.ts +0 -12
- package/dist/utils/format-env-suffix.d.ts.map +0 -1
- package/dist/utils/format-env-suffix.js +0 -13
- package/dist/utils/format-env-suffix.js.map +0 -1
- package/dist/utils/version-check.d.ts +0 -3
- package/dist/utils/version-check.d.ts.map +0 -1
- package/dist/utils/version-check.js +0 -38
- package/dist/utils/version-check.js.map +0 -1
|
@@ -44,8 +44,8 @@ export interface CreateDeploymentResponse {
|
|
|
44
44
|
export interface CreateDeploymentApiResponse {
|
|
45
45
|
error: boolean;
|
|
46
46
|
meta: {
|
|
47
|
+
code_upload_url: string;
|
|
47
48
|
deployment_id: string;
|
|
48
|
-
lambda_zip_upload_url: string;
|
|
49
49
|
static_asset_upload_urls: Record<string, string>;
|
|
50
50
|
};
|
|
51
51
|
status: number;
|
|
@@ -65,8 +65,6 @@ export interface PerformDeploymentResponse {
|
|
|
65
65
|
deploymentId: string;
|
|
66
66
|
/** Status message */
|
|
67
67
|
message: string;
|
|
68
|
-
/** List of tenant domains */
|
|
69
|
-
tenantDomains: string[];
|
|
70
68
|
}
|
|
71
69
|
export interface PerformDeploymentApiResponse {
|
|
72
70
|
error: boolean;
|
|
@@ -74,19 +72,9 @@ export interface PerformDeploymentApiResponse {
|
|
|
74
72
|
deployment_id: string;
|
|
75
73
|
message: string;
|
|
76
74
|
status: string;
|
|
77
|
-
tenant_domains: string[];
|
|
78
75
|
};
|
|
79
76
|
status: number;
|
|
80
77
|
}
|
|
81
|
-
/**
|
|
82
|
-
* Response from getDatasetInfo endpoint
|
|
83
|
-
*/
|
|
84
|
-
export type DatasetInfo = {
|
|
85
|
-
cmsId: string;
|
|
86
|
-
oauthClientId: string;
|
|
87
|
-
oauthClientSecret: string;
|
|
88
|
-
tenantId: string;
|
|
89
|
-
};
|
|
90
78
|
/**
|
|
91
79
|
* List all tenant instances for the authenticated user
|
|
92
80
|
* Maps to: GET /v1/tenant
|
|
@@ -133,13 +121,4 @@ export declare function createDeployment(accessToken: string, tenantId: string,
|
|
|
133
121
|
* @returns Deployment status and message
|
|
134
122
|
*/
|
|
135
123
|
export declare function performDeployment(accessToken: string, tenantId: string, options: PerformDeploymentOptions): Promise<PerformDeploymentResponse>;
|
|
136
|
-
/**
|
|
137
|
-
* Get dataset info including tenant ID and OAuth credentials
|
|
138
|
-
* Maps to: GET /v1/cms/dataset/:datasetId
|
|
139
|
-
*
|
|
140
|
-
* @param accessToken - OAuth access token
|
|
141
|
-
* @param datasetId - Content System ID (dataset ID)
|
|
142
|
-
* @returns Dataset info with tenant ID and OAuth credentials
|
|
143
|
-
*/
|
|
144
|
-
export declare function getDatasetInfo(accessToken: string, datasetId: string): Promise<DatasetInfo>;
|
|
145
124
|
//# 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;
|
|
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,eAAe,EAAE,MAAM,CAAA;QACvB,aAAa,EAAE,MAAM,CAAA;QACrB,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,CA0DnC;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,yBAAyB,CAAC,CAwCpC"}
|
|
@@ -2,12 +2,18 @@
|
|
|
2
2
|
* Control Plane API Client
|
|
3
3
|
*
|
|
4
4
|
* TODO: Replace with real API calls when Control Plane is available
|
|
5
|
-
*/ import {
|
|
5
|
+
*/ import { getEnvironment } from '../constants.js';
|
|
6
6
|
import * as log from '../utils/log.js';
|
|
7
|
+
/**
|
|
8
|
+
* Control Plane API base URLs
|
|
9
|
+
*/ const CONTROL_PLANE_API_URLS = {
|
|
10
|
+
production: 'https://api.figma.com',
|
|
11
|
+
staging: 'https://api.staging.figma.com'
|
|
12
|
+
};
|
|
7
13
|
/**
|
|
8
14
|
* Get the Control Plane API base URL based on environment
|
|
9
15
|
*/ function getControlPlaneBaseUrl() {
|
|
10
|
-
return process.env.FIGMA_API_BASE_URL ||
|
|
16
|
+
return process.env.FIGMA_API_BASE_URL || CONTROL_PLANE_API_URLS[getEnvironment()];
|
|
11
17
|
}
|
|
12
18
|
/**
|
|
13
19
|
* Error thrown when Control Plane API calls fail
|
|
@@ -26,7 +32,9 @@ import * as log from '../utils/log.js';
|
|
|
26
32
|
* @param accessToken - OAuth access token
|
|
27
33
|
* @returns Array of tenant instances
|
|
28
34
|
*/ export async function listTenants(accessToken) {
|
|
29
|
-
|
|
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';
|
|
30
38
|
if (shouldMock) {
|
|
31
39
|
// MOCK IMPLEMENTATION: Return sample tenants for testing
|
|
32
40
|
log.debug('Using mock Control Plane API for listTenants (FIGMA_MOCK_CONTROL_PLANE != "false")');
|
|
@@ -69,7 +77,7 @@ import * as log from '../utils/log.js';
|
|
|
69
77
|
* @returns Newly created tenant
|
|
70
78
|
*/ export async function createTenant(accessToken, options) {
|
|
71
79
|
// Check if control plane API should be mocked
|
|
72
|
-
const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE
|
|
80
|
+
const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE !== 'false';
|
|
73
81
|
if (shouldMock) {
|
|
74
82
|
// MOCK IMPLEMENTATION: Generate mock tenant for testing
|
|
75
83
|
log.debug('Using mock Control Plane API for createTenant (FIGMA_MOCK_CONTROL_PLANE != "false")');
|
|
@@ -110,7 +118,7 @@ import * as log from '../utils/log.js';
|
|
|
110
118
|
* @returns Tenant details
|
|
111
119
|
*/ export async function getTenantDetails(accessToken, tenantId) {
|
|
112
120
|
// Check if control plane API should be mocked
|
|
113
|
-
const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE
|
|
121
|
+
const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE !== 'false';
|
|
114
122
|
if (shouldMock) {
|
|
115
123
|
// MOCK IMPLEMENTATION: Return mock tenant details for testing
|
|
116
124
|
log.debug('Using mock Control Plane API for getTenantDetails (FIGMA_MOCK_CONTROL_PLANE != "false")');
|
|
@@ -144,10 +152,10 @@ import * as log from '../utils/log.js';
|
|
|
144
152
|
* @returns Deployment ID and upload URLs
|
|
145
153
|
*/ export async function createDeployment(accessToken, tenantId, options) {
|
|
146
154
|
// Check if control plane API should be mocked
|
|
147
|
-
const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE
|
|
155
|
+
const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE !== 'false';
|
|
148
156
|
if (shouldMock) {
|
|
149
157
|
// MOCK IMPLEMENTATION: Generate mock deployment and signed URLs
|
|
150
|
-
log.debug('Using mock Control Plane API for createDeployment (FIGMA_MOCK_CONTROL_PLANE
|
|
158
|
+
log.debug('Using mock Control Plane API for createDeployment (FIGMA_MOCK_CONTROL_PLANE != "false")');
|
|
151
159
|
const deploymentId = `deploy_${Math.random().toString(36).substring(2, 11)}`;
|
|
152
160
|
const mockBucket = 'figma-cms-deployments-mock';
|
|
153
161
|
const mockRegion = 'us-west-2';
|
|
@@ -166,10 +174,8 @@ import * as log from '../utils/log.js';
|
|
|
166
174
|
staticAssetUploadUrls
|
|
167
175
|
};
|
|
168
176
|
}
|
|
169
|
-
const url = `${getControlPlaneBaseUrl()}/v1/cms/tenant/${tenantId}/deploy/create`;
|
|
170
|
-
log.debug(`Calling createDeployment API at ${url}`);
|
|
171
177
|
// REAL API IMPLEMENTATION
|
|
172
|
-
const response = await fetch(
|
|
178
|
+
const response = await fetch(`${getControlPlaneBaseUrl()}/v1/tenant/${tenantId}/deploy/create`, {
|
|
173
179
|
body: JSON.stringify({
|
|
174
180
|
static_assets: options.staticAssets
|
|
175
181
|
}),
|
|
@@ -185,7 +191,7 @@ import * as log from '../utils/log.js';
|
|
|
185
191
|
const data = await response.json();
|
|
186
192
|
// Map API response format to our interface
|
|
187
193
|
return {
|
|
188
|
-
codeUploadUrl: data.meta.
|
|
194
|
+
codeUploadUrl: data.meta.code_upload_url,
|
|
189
195
|
deploymentId: data.meta.deployment_id,
|
|
190
196
|
staticAssetUploadUrls: data.meta.static_asset_upload_urls
|
|
191
197
|
};
|
|
@@ -199,22 +205,18 @@ import * as log from '../utils/log.js';
|
|
|
199
205
|
* @param options - Deployment perform options
|
|
200
206
|
* @returns Deployment status and message
|
|
201
207
|
*/ export async function performDeployment(accessToken, tenantId, options) {
|
|
202
|
-
|
|
208
|
+
// Check if control plane API should be mocked
|
|
209
|
+
const shouldMock = process.env.FIGMA_MOCK_CONTROL_PLANE !== 'false';
|
|
203
210
|
if (shouldMock) {
|
|
204
211
|
// MOCK IMPLEMENTATION: Return mock deployment status
|
|
205
212
|
log.debug('Using mock Control Plane API for performDeployment (FIGMA_MOCK_CONTROL_PLANE != "false")');
|
|
206
213
|
return {
|
|
207
214
|
deploymentId: options.deploymentId,
|
|
208
|
-
message: 'Deployment initiated successfully'
|
|
209
|
-
tenantDomains: [
|
|
210
|
-
'example.figmasource.site'
|
|
211
|
-
]
|
|
215
|
+
message: 'Deployment initiated successfully'
|
|
212
216
|
};
|
|
213
217
|
}
|
|
214
|
-
const url = `${getControlPlaneBaseUrl()}/v1/cms/tenant/${tenantId}/deploy/perform`;
|
|
215
|
-
log.debug(`Calling performDeployment API at ${url}`);
|
|
216
218
|
// REAL API IMPLEMENTATION
|
|
217
|
-
const response = await fetch(
|
|
219
|
+
const response = await fetch(`${getControlPlaneBaseUrl()}/v1/tenant/${tenantId}/deploy/perform`, {
|
|
218
220
|
body: JSON.stringify({
|
|
219
221
|
deployment_id: options.deploymentId
|
|
220
222
|
}),
|
|
@@ -231,42 +233,7 @@ import * as log from '../utils/log.js';
|
|
|
231
233
|
// Map API response to our interface
|
|
232
234
|
return {
|
|
233
235
|
deploymentId: data.meta.deployment_id,
|
|
234
|
-
message: data.meta.message || 'Deployment initiated'
|
|
235
|
-
tenantDomains: data.meta.tenant_domains
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* Get dataset info including tenant ID and OAuth credentials
|
|
240
|
-
* Maps to: GET /v1/cms/dataset/:datasetId
|
|
241
|
-
*
|
|
242
|
-
* @param accessToken - OAuth access token
|
|
243
|
-
* @param datasetId - Content System ID (dataset ID)
|
|
244
|
-
* @returns Dataset info with tenant ID and OAuth credentials
|
|
245
|
-
*/ export async function getDatasetInfo(accessToken, datasetId) {
|
|
246
|
-
const url = `${getControlPlaneBaseUrl()}/v1/cms/dataset/${datasetId}`;
|
|
247
|
-
log.debug(`Calling getDatasetInfo API at ${url}`);
|
|
248
|
-
const response = await fetch(url, {
|
|
249
|
-
headers: {
|
|
250
|
-
Authorization: `Bearer ${accessToken}`
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
if (!response.ok) {
|
|
254
|
-
throw new ControlPlaneError(`Failed to get dataset info: ${response.status} ${response.statusText}`, response.status);
|
|
255
|
-
}
|
|
256
|
-
const data = await response.json();
|
|
257
|
-
const sanitizedData = {
|
|
258
|
-
...data,
|
|
259
|
-
meta: {
|
|
260
|
-
...data.meta,
|
|
261
|
-
oauth_client_secret: '[REDACTED]'
|
|
262
|
-
}
|
|
263
|
-
};
|
|
264
|
-
log.debug(`Dataset API response: ${JSON.stringify(sanitizedData, null, 2)}`);
|
|
265
|
-
return {
|
|
266
|
-
cmsId: data.meta.cms_resource_id,
|
|
267
|
-
oauthClientId: data.meta.oauth_client_id,
|
|
268
|
-
oauthClientSecret: data.meta.oauth_client_secret,
|
|
269
|
-
tenantId: data.meta.tenant_instance_id
|
|
236
|
+
message: data.meta.message || 'Deployment initiated'
|
|
270
237
|
};
|
|
271
238
|
}
|
|
272
239
|
|
|
@@ -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 { ENV_CONFIG, getEnvironment } from '../constants.js'\nimport * as log from '../utils/log.js'\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 || ENV_CONFIG[getEnvironment()].apiBaseUrl\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 /** List of tenant domains */\n tenantDomains: string[]\n}\n\nexport interface PerformDeploymentApiResponse {\n error: boolean\n meta: {\n deployment_id: string\n message: string\n status: string\n tenant_domains: 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 tenantDomains: ['example.figmasource.site'],\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 tenantDomains: data.meta.tenant_domains,\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":["ENV_CONFIG","getEnvironment","log","getControlPlaneBaseUrl","process","env","FIGMA_API_BASE_URL","apiBaseUrl","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","tenantDomains","tenant_domains","getDatasetInfo","datasetId","sanitizedData","oauth_client_secret","cmsId","cms_resource_id","oauthClientId","oauth_client_id","oauthClientSecret","tenant_instance_id"],"mappings":"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"}
|
|
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: '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 code_upload_url: string\n deployment_id: 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 != \"false\")',\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 // REAL API IMPLEMENTATION\n const response = await fetch(`${getControlPlaneBaseUrl()}/v1/tenant/${tenantId}/deploy/create`, {\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.code_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 // REAL API IMPLEMENTATION\n const response = await fetch(`${getControlPlaneBaseUrl()}/v1/tenant/${tenantId}/deploy/perform`, {\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","getControlPlaneBaseUrl","process","env","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","static_assets","data","meta","code_upload_url","deployment_id","static_asset_upload_urls","performDeployment"],"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;
|
|
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,CAuFvB"}
|
package/dist/api/figma-api.js
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Client for Figma REST API (Pixie) endpoints
|
|
5
5
|
* Handles project token generation and other CMS-related API calls
|
|
6
|
-
*/ import
|
|
7
|
-
import * as log from '../utils/log.js';
|
|
6
|
+
*/ import * as log from '../utils/log.js';
|
|
8
7
|
/**
|
|
9
8
|
* Error thrown when Figma API calls fail
|
|
10
9
|
*/ export class FigmaApiError extends Error {
|
|
@@ -15,6 +14,19 @@ import * as log from '../utils/log.js';
|
|
|
15
14
|
this.name = 'FigmaApiError';
|
|
16
15
|
}
|
|
17
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Base URLs for Figma API
|
|
19
|
+
*/ const API_BASE_URLS = {
|
|
20
|
+
prod: 'https://api.figma.com',
|
|
21
|
+
staging: 'https://api.staging.figma.com'
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Get the Figma API base URL based on environment
|
|
25
|
+
* TODO: Use this once real API endpoints are available
|
|
26
|
+
*/ function _getApiBaseUrl() {
|
|
27
|
+
// For now, default to staging to match OAuth config
|
|
28
|
+
return process.env.FIGMA_API_BASE_URL || API_BASE_URLS.staging;
|
|
29
|
+
}
|
|
18
30
|
/**
|
|
19
31
|
* Get a project token (JWT) for a specific tenant/CMS
|
|
20
32
|
*
|
|
@@ -29,41 +41,13 @@ import * as log from '../utils/log.js';
|
|
|
29
41
|
* @returns ProjectToken with JWT and expiry information
|
|
30
42
|
* @throws {FigmaApiError} If the API call fails
|
|
31
43
|
*/ export async function getProjectToken(accessToken, tenantId) {
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
log.debug('Using direct project token from FIGMA_MOCK_PROJECT_TOKEN_VALUE');
|
|
36
|
-
try {
|
|
37
|
-
// Parse JWT payload to extract claims
|
|
38
|
-
const parts = envTokenValue.split('.');
|
|
39
|
-
if (parts.length !== 3) {
|
|
40
|
-
throw new Error('Invalid JWT format: must have 3 parts (header.payload.signature)');
|
|
41
|
-
}
|
|
42
|
-
const payload = parts[1];
|
|
43
|
-
const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');
|
|
44
|
-
const paddedBase64 = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, '=');
|
|
45
|
-
const decodedPayload = JSON.parse(Buffer.from(paddedBase64, 'base64').toString('utf-8'));
|
|
46
|
-
if (!decodedPayload.exp || typeof decodedPayload.exp !== 'number') {
|
|
47
|
-
throw new Error('JWT payload missing exp claim');
|
|
48
|
-
}
|
|
49
|
-
// Extract tenant ID from aud claim (use parameter as fallback)
|
|
50
|
-
const extractedTenantId = decodedPayload.aud || tenantId;
|
|
51
|
-
return {
|
|
52
|
-
expiresAt: decodedPayload.exp * 1000,
|
|
53
|
-
tenantId: extractedTenantId,
|
|
54
|
-
token: envTokenValue
|
|
55
|
-
};
|
|
56
|
-
} catch (error) {
|
|
57
|
-
throw new FigmaApiError(`Invalid JWT token in FIGMA_MOCK_PROJECT_TOKEN_VALUE: ${error instanceof Error ? error.message : 'Unknown error'}`, undefined, error instanceof Error ? error : undefined);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
// Priority 2: Check if project token API should be mocked
|
|
61
|
-
// Default to false (use real API)
|
|
62
|
-
const shouldMock = process.env.FIGMA_MOCK_PROJECT_TOKEN === 'true';
|
|
44
|
+
// Check if project token API should be mocked
|
|
45
|
+
// Default to true until real API is available
|
|
46
|
+
const shouldMock = process.env.FIGMA_MOCK_PROJECT_TOKEN !== 'false';
|
|
63
47
|
try {
|
|
64
48
|
if (shouldMock) {
|
|
65
49
|
// MOCK IMPLEMENTATION: Generate a mock JWT token for testing
|
|
66
|
-
log.debug('Using mock project token API for getProjectToken (FIGMA_MOCK_PROJECT_TOKEN
|
|
50
|
+
log.debug('Using mock project token API for getProjectToken (FIGMA_MOCK_PROJECT_TOKEN != "false")');
|
|
67
51
|
const mockToken = createMockJWT(tenantId, 25) // 25 minutes expiry
|
|
68
52
|
;
|
|
69
53
|
// Mock response data
|
|
@@ -84,8 +68,7 @@ import * as log from '../utils/log.js';
|
|
|
84
68
|
};
|
|
85
69
|
}
|
|
86
70
|
// REAL API IMPLEMENTATION
|
|
87
|
-
const
|
|
88
|
-
const baseUrl = process.env.FIGMA_API_BASE_URL || ENV_CONFIG[env].apiBaseUrl;
|
|
71
|
+
const baseUrl = _getApiBaseUrl();
|
|
89
72
|
const url = `${baseUrl}/v1/cms/${tenantId}/token/`;
|
|
90
73
|
const response = await fetch(url, {
|
|
91
74
|
headers: {
|
|
@@ -98,19 +81,12 @@ import * as log from '../utils/log.js';
|
|
|
98
81
|
let errorMessage = `Failed to get project token: ${response.status} ${response.statusText}`;
|
|
99
82
|
try {
|
|
100
83
|
const errorBody = await response.json();
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
errorMessage = errorBody.message;
|
|
104
|
-
} else if (typeof errorBody.error === 'string') {
|
|
105
|
-
errorMessage = errorBody.error;
|
|
84
|
+
if (errorBody.error || errorBody.message) {
|
|
85
|
+
errorMessage = errorBody.error || errorBody.message;
|
|
106
86
|
}
|
|
107
87
|
} catch {
|
|
108
88
|
// Ignore JSON parse errors for error body
|
|
109
89
|
}
|
|
110
|
-
// Provide actionable error for common cases
|
|
111
|
-
if (response.status === 404) {
|
|
112
|
-
errorMessage = `Content System ID "${tenantId}" not found. Ensure FIGMA_CONTENT_SYSTEM_ID is set to a valid content system ID in your .env file.`;
|
|
113
|
-
}
|
|
114
90
|
throw new FigmaApiError(errorMessage, response.status);
|
|
115
91
|
}
|
|
116
92
|
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 { ENV_CONFIG, getEnvironment } from '../constants.js'\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 * 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 env = getEnvironment()\n const baseUrl = process.env.FIGMA_API_BASE_URL || ENV_CONFIG[env].apiBaseUrl\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":["ENV_CONFIG","getEnvironment","log","FigmaApiError","Error","message","statusCode","cause","name","getProjectToken","accessToken","tenantId","envTokenValue","process","env","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","FIGMA_API_BASE_URL","apiBaseUrl","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: '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 // Check if project token API should be mocked\n // Default to true until real API is available\n const shouldMock = process.env.FIGMA_MOCK_PROJECT_TOKEN !== 'false'\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 != \"false\")',\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","_getApiBaseUrl","process","env","FIGMA_API_BASE_URL","getProjectToken","accessToken","tenantId","shouldMock","FIGMA_MOCK_PROJECT_TOKEN","debug","mockToken","createMockJWT","data","meta","token","expiresAt","parseJwtExpiry","baseUrl","url","response","fetch","headers","Authorization","method","ok","errorMessage","status","statusText","errorBody","json","error","undefined","expiresInMinutes","header","alg","typ","now","Math","floor","Date","exp","payload","aud","email","iat","sub","encodeBase64Url","obj","Buffer","from","JSON","stringify","toString","replace","headerEncoded","payloadEncoded","signature","random","substring","parts","split","length","base64","paddedBase64","padEnd","decodedPayload","parse"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"callback-server.d.ts","sourceRoot":"","sources":["../../src/auth/callback-server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"callback-server.d.ts","sourceRoot":"","sources":["../../src/auth/callback-server.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAA;IACZ,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;IAG7B,IAAI,CAAC,EAAE,MAAM;gBADpB,OAAO,EAAE,MAAM,EACR,IAAI,CAAC,EAAE,MAAM,YAAA;CAKvB;AAED;;;;;;;;GAQG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,UAAU,CAAC,CAAQ;IAC3B,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,SAAS,CAAQ;gBAEb,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO;IAM/D;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAIzB;;;OAGG;IACH,aAAa,IAAI,MAAM;IAIvB;;OAEG;IACH,IAAI,IAAI,IAAI;IAOZ;;;;;;OAMG;IACG,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;CA8GtE"}
|
|
@@ -2,7 +2,6 @@ import { readFileSync } from 'fs';
|
|
|
2
2
|
import { createServer } from 'http';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
|
-
import { DEFAULT_CALLBACK_PORT } from '../config/oauth.js';
|
|
6
5
|
const filename = fileURLToPath(import.meta.url);
|
|
7
6
|
const dirname = path.dirname(filename);
|
|
8
7
|
/**
|
|
@@ -28,7 +27,7 @@ const dirname = path.dirname(filename);
|
|
|
28
27
|
server = null;
|
|
29
28
|
timeoutMs;
|
|
30
29
|
constructor(options = {}){
|
|
31
|
-
this.preferredPort = options.port ||
|
|
30
|
+
this.preferredPort = options.port || 3000;
|
|
32
31
|
this.timeoutMs = options.timeoutMs || 120000; // 2 minutes default
|
|
33
32
|
this.server = createServer();
|
|
34
33
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/auth/callback-server.ts"],"sourcesContent":["import { readFileSync } from 'fs'\nimport { createServer, type IncomingMessage, type Server, type ServerResponse } from 'http'\nimport path from 'path'\nimport { fileURLToPath } from 'url'\n\nimport { DEFAULT_CALLBACK_PORT } from '../config/oauth.js'\n\nconst filename = fileURLToPath(import.meta.url)\nconst dirname = path.dirname(filename)\n\n/**\n * Result from OAuth callback server\n */\nexport interface CallbackResult {\n /** Authorization code from OAuth provider */\n code: string\n /** State parameter for CSRF verification */\n state: string\n}\n\n/**\n * Error from OAuth callback\n */\nexport class CallbackError extends Error {\n constructor(\n message: string,\n public code?: string,\n ) {\n super(message)\n this.name = 'CallbackError'\n }\n}\n\n/**\n * Local HTTP server for handling OAuth callbacks\n *\n * Starts a temporary server on localhost to receive the OAuth authorization code.\n * The server automatically shuts down after receiving the callback or on timeout.\n *\n * Implements dynamic port allocation: tries preferred port first, falls back to\n * OS-assigned port if unavailable.\n */\nexport class CallbackServer {\n private actualPort?: number\n private preferredPort: number\n private server: null | Server = null\n private timeoutMs: number\n\n constructor(options: { port?: number; timeoutMs?: number } = {}) {\n this.preferredPort = options.port || DEFAULT_CALLBACK_PORT\n this.timeoutMs = options.timeoutMs || 120000 // 2 minutes default\n this.server = createServer()\n }\n\n /**\n * Render error page shown in browser when auth fails\n */\n private renderErrorPage(error: string, description?: string): string {\n const template = readFileSync(path.join(dirname, 'pages', 'error.html'), 'utf-8')\n return template\n .replace('{{description}}', description || 'An error occurred during authentication.')\n .replace('{{error}}', error ? `<p>Error: <span class=\"error-code\">${error}</span></p>` : '')\n }\n\n /**\n * Render success page shown in browser after successful auth\n */\n private renderSuccessPage(): string {\n return readFileSync(path.join(dirname, 'pages', 'success.html'), 'utf-8')\n }\n\n /**\n * Get the actual port the server is listening on\n * @returns Port number (may differ from preferred if that was in use)\n */\n getActualPort(): number {\n return this.actualPort || this.preferredPort\n }\n\n /**\n * Stop the server if running\n */\n stop(): void {\n if (this.server) {\n this.server.close()\n this.server = null\n }\n }\n\n /**\n * Start server and wait for OAuth callback\n *\n * @param expectedState - State parameter to verify CSRF protection\n * @returns Promise resolving to callback result with code and state\n * @throws CallbackError if callback fails or times out\n */\n async waitForCallback(expectedState: string): Promise<CallbackResult> {\n return new Promise((resolve, reject) => {\n const cleanup = (handle: NodeJS.Timeout) => {\n clearTimeout(handle)\n if (this.server) {\n this.server.close()\n this.server = null\n }\n }\n\n // Set timeout for waiting\n const timeoutHandle = setTimeout(() => {\n cleanup(timeoutHandle)\n reject(new CallbackError('OAuth authorization timed out. Please try again.', 'timeout'))\n }, this.timeoutMs)\n\n // Create HTTP server\n this.server = createServer((req: IncomingMessage, res: ServerResponse) => {\n const url = new URL(req.url || '', `http://localhost:${this.getActualPort()}`)\n\n // Only handle /callback path\n if (url.pathname !== '/callback') {\n res.writeHead(404, { 'Content-Type': 'text/html' })\n res.end('<html><body><h1>404 Not Found</h1></body></html>')\n return\n }\n\n // Extract code and state from query params\n const code = url.searchParams.get('code')\n const state = url.searchParams.get('state')\n const error = url.searchParams.get('error')\n const errorDescription = url.searchParams.get('error_description')\n\n // Handle OAuth errors\n if (error) {\n cleanup(timeoutHandle)\n res.writeHead(400, { 'Content-Type': 'text/html' })\n res.end(this.renderErrorPage(error, errorDescription || undefined))\n reject(\n new CallbackError(\n `OAuth error: ${error}${errorDescription ? ` - ${errorDescription}` : ''}`,\n error,\n ),\n )\n return\n }\n\n // Validate required parameters\n if (!code || !state) {\n cleanup(timeoutHandle)\n res.writeHead(400, { 'Content-Type': 'text/html' })\n res.end(this.renderErrorPage('invalid_request', 'Missing code or state parameter'))\n reject(new CallbackError('Invalid callback: missing code or state', 'invalid_request'))\n return\n }\n\n // Verify state matches (CSRF protection)\n if (state !== expectedState) {\n cleanup(timeoutHandle)\n res.writeHead(400, { 'Content-Type': 'text/html' })\n res.end(this.renderErrorPage('invalid_state', 'State parameter mismatch'))\n reject(new CallbackError('State mismatch: possible CSRF attack', 'invalid_state'))\n return\n }\n\n // Success! Send response to browser\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(this.renderSuccessPage())\n\n // Resolve with the callback result\n cleanup(timeoutHandle)\n resolve({ code, state })\n })\n\n // Try to start server with dynamic port allocation\n const tryListen = (port: number, isRetry: boolean = false) => {\n if (!this.server) {\n reject(new CallbackError('Server instance is not available', 'server_error'))\n return\n }\n\n this.server.listen(port, '127.0.0.1', () => {\n // Capture the actual port that was assigned\n const addr = this.server!.address()\n if (addr && typeof addr === 'object') {\n this.actualPort = addr.port\n }\n })\n\n // Handle server errors (port in use, etc)\n this.server.once('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE' && !isRetry) {\n // Port in use, try with OS-assigned port (0 = dynamic)\n this.server!.removeAllListeners()\n tryListen(0, true)\n } else {\n cleanup(timeoutHandle)\n reject(\n new CallbackError(\n `Failed to start callback server: ${err.message}${!isRetry ? ' (tried fallback port)' : ''}`,\n ),\n )\n }\n })\n }\n\n // Start with preferred port\n tryListen(this.preferredPort)\n })\n }\n}\n"],"names":["readFileSync","createServer","path","fileURLToPath","DEFAULT_CALLBACK_PORT","filename","url","dirname","CallbackError","Error","message","code","name","CallbackServer","actualPort","preferredPort","server","timeoutMs","options","port","renderErrorPage","error","description","template","join","replace","renderSuccessPage","getActualPort","stop","close","waitForCallback","expectedState","Promise","resolve","reject","cleanup","handle","clearTimeout","timeoutHandle","setTimeout","req","res","URL","pathname","writeHead","end","searchParams","get","state","errorDescription","undefined","tryListen","isRetry","listen","addr","address","once","err","removeAllListeners"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/auth/callback-server.ts"],"sourcesContent":["import { readFileSync } from 'fs'\nimport { createServer, type IncomingMessage, type Server, type ServerResponse } from 'http'\nimport path from 'path'\nimport { fileURLToPath } from 'url'\n\nconst filename = fileURLToPath(import.meta.url)\nconst dirname = path.dirname(filename)\n\n/**\n * Result from OAuth callback server\n */\nexport interface CallbackResult {\n /** Authorization code from OAuth provider */\n code: string\n /** State parameter for CSRF verification */\n state: string\n}\n\n/**\n * Error from OAuth callback\n */\nexport class CallbackError extends Error {\n constructor(\n message: string,\n public code?: string,\n ) {\n super(message)\n this.name = 'CallbackError'\n }\n}\n\n/**\n * Local HTTP server for handling OAuth callbacks\n *\n * Starts a temporary server on localhost to receive the OAuth authorization code.\n * The server automatically shuts down after receiving the callback or on timeout.\n *\n * Implements dynamic port allocation: tries preferred port first, falls back to\n * OS-assigned port if unavailable.\n */\nexport class CallbackServer {\n private actualPort?: number\n private preferredPort: number\n private server: null | Server = null\n private timeoutMs: number\n\n constructor(options: { port?: number; timeoutMs?: number } = {}) {\n this.preferredPort = options.port || 3000\n this.timeoutMs = options.timeoutMs || 120000 // 2 minutes default\n this.server = createServer()\n }\n\n /**\n * Render error page shown in browser when auth fails\n */\n private renderErrorPage(error: string, description?: string): string {\n const template = readFileSync(path.join(dirname, 'pages', 'error.html'), 'utf-8')\n return template\n .replace('{{description}}', description || 'An error occurred during authentication.')\n .replace('{{error}}', error ? `<p>Error: <span class=\"error-code\">${error}</span></p>` : '')\n }\n\n /**\n * Render success page shown in browser after successful auth\n */\n private renderSuccessPage(): string {\n return readFileSync(path.join(dirname, 'pages', 'success.html'), 'utf-8')\n }\n\n /**\n * Get the actual port the server is listening on\n * @returns Port number (may differ from preferred if that was in use)\n */\n getActualPort(): number {\n return this.actualPort || this.preferredPort\n }\n\n /**\n * Stop the server if running\n */\n stop(): void {\n if (this.server) {\n this.server.close()\n this.server = null\n }\n }\n\n /**\n * Start server and wait for OAuth callback\n *\n * @param expectedState - State parameter to verify CSRF protection\n * @returns Promise resolving to callback result with code and state\n * @throws CallbackError if callback fails or times out\n */\n async waitForCallback(expectedState: string): Promise<CallbackResult> {\n return new Promise((resolve, reject) => {\n const cleanup = (handle: NodeJS.Timeout) => {\n clearTimeout(handle)\n if (this.server) {\n this.server.close()\n this.server = null\n }\n }\n\n // Set timeout for waiting\n const timeoutHandle = setTimeout(() => {\n cleanup(timeoutHandle)\n reject(new CallbackError('OAuth authorization timed out. Please try again.', 'timeout'))\n }, this.timeoutMs)\n\n // Create HTTP server\n this.server = createServer((req: IncomingMessage, res: ServerResponse) => {\n const url = new URL(req.url || '', `http://localhost:${this.getActualPort()}`)\n\n // Only handle /callback path\n if (url.pathname !== '/callback') {\n res.writeHead(404, { 'Content-Type': 'text/html' })\n res.end('<html><body><h1>404 Not Found</h1></body></html>')\n return\n }\n\n // Extract code and state from query params\n const code = url.searchParams.get('code')\n const state = url.searchParams.get('state')\n const error = url.searchParams.get('error')\n const errorDescription = url.searchParams.get('error_description')\n\n // Handle OAuth errors\n if (error) {\n cleanup(timeoutHandle)\n res.writeHead(400, { 'Content-Type': 'text/html' })\n res.end(this.renderErrorPage(error, errorDescription || undefined))\n reject(\n new CallbackError(\n `OAuth error: ${error}${errorDescription ? ` - ${errorDescription}` : ''}`,\n error,\n ),\n )\n return\n }\n\n // Validate required parameters\n if (!code || !state) {\n cleanup(timeoutHandle)\n res.writeHead(400, { 'Content-Type': 'text/html' })\n res.end(this.renderErrorPage('invalid_request', 'Missing code or state parameter'))\n reject(new CallbackError('Invalid callback: missing code or state', 'invalid_request'))\n return\n }\n\n // Verify state matches (CSRF protection)\n if (state !== expectedState) {\n cleanup(timeoutHandle)\n res.writeHead(400, { 'Content-Type': 'text/html' })\n res.end(this.renderErrorPage('invalid_state', 'State parameter mismatch'))\n reject(new CallbackError('State mismatch: possible CSRF attack', 'invalid_state'))\n return\n }\n\n // Success! Send response to browser\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(this.renderSuccessPage())\n\n // Resolve with the callback result\n cleanup(timeoutHandle)\n resolve({ code, state })\n })\n\n // Try to start server with dynamic port allocation\n const tryListen = (port: number, isRetry: boolean = false) => {\n if (!this.server) {\n reject(new CallbackError('Server instance is not available', 'server_error'))\n return\n }\n\n this.server.listen(port, '127.0.0.1', () => {\n // Capture the actual port that was assigned\n const addr = this.server!.address()\n if (addr && typeof addr === 'object') {\n this.actualPort = addr.port\n }\n })\n\n // Handle server errors (port in use, etc)\n this.server.once('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE' && !isRetry) {\n // Port in use, try with OS-assigned port (0 = dynamic)\n this.server!.removeAllListeners()\n tryListen(0, true)\n } else {\n cleanup(timeoutHandle)\n reject(\n new CallbackError(\n `Failed to start callback server: ${err.message}${!isRetry ? ' (tried fallback port)' : ''}`,\n ),\n )\n }\n })\n }\n\n // Start with preferred port\n tryListen(this.preferredPort)\n })\n }\n}\n"],"names":["readFileSync","createServer","path","fileURLToPath","filename","url","dirname","CallbackError","Error","message","code","name","CallbackServer","actualPort","preferredPort","server","timeoutMs","options","port","renderErrorPage","error","description","template","join","replace","renderSuccessPage","getActualPort","stop","close","waitForCallback","expectedState","Promise","resolve","reject","cleanup","handle","clearTimeout","timeoutHandle","setTimeout","req","res","URL","pathname","writeHead","end","searchParams","get","state","errorDescription","undefined","tryListen","isRetry","listen","addr","address","once","err","removeAllListeners"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto-utils.d.ts","sourceRoot":"","sources":["../../src/auth/crypto-utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"crypto-utils.d.ts","sourceRoot":"","sources":["../../src/auth/crypto-utils.ts"],"names":[],"mappings":"AAqEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAe5C"}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import Conf from 'conf';
|
|
2
2
|
import crypto from 'crypto';
|
|
3
3
|
import os from 'os';
|
|
4
|
+
// Configuration store for cryptographic salt
|
|
5
|
+
const cryptoConf = new Conf({
|
|
6
|
+
configName: 'payloadcms-figma-crypto',
|
|
7
|
+
projectName: 'payloadcms-figma'
|
|
8
|
+
});
|
|
4
9
|
/**
|
|
5
10
|
* Get or generate a random salt for key derivation
|
|
6
11
|
*
|
|
@@ -10,11 +15,6 @@ import os from 'os';
|
|
|
10
15
|
*
|
|
11
16
|
* @returns 32-byte hex string (64 characters)
|
|
12
17
|
*/ function getOrCreateSalt() {
|
|
13
|
-
// Configuration store for cryptographic salt
|
|
14
|
-
const cryptoConf = new Conf({
|
|
15
|
-
configName: 'payloadcms-figma-crypto',
|
|
16
|
-
projectName: 'payloadcms-figma'
|
|
17
|
-
});
|
|
18
18
|
let salt = cryptoConf.get('salt');
|
|
19
19
|
if (!salt) {
|
|
20
20
|
// Generate cryptographically secure random salt (32 bytes = 256 bits)
|