@canva/cli 0.0.1-beta.30 → 0.0.1-beta.32
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/cli.js +313 -276
- package/package.json +1 -1
- package/templates/base/package.json +4 -4
- package/templates/base/webpack.config.ts +5 -3
- package/templates/dam/package.json +4 -4
- package/templates/dam/webpack.config.ts +5 -3
- package/templates/data_connector/package.json +5 -5
- package/templates/data_connector/src/api/data_sources/designs.tsx +60 -17
- package/templates/data_connector/src/api/data_sources/templates.tsx +66 -24
- package/templates/data_connector/src/api/fetch_data_table.ts +12 -8
- package/templates/data_connector/src/app.tsx +3 -7
- package/templates/data_connector/src/components/inputs/messages.tsx +19 -0
- package/templates/data_connector/src/components/inputs/search_filter.tsx +108 -0
- package/templates/data_connector/src/context/app_context.tsx +10 -10
- package/templates/data_connector/src/entrypoint.tsx +5 -8
- package/templates/data_connector/src/index.tsx +10 -10
- package/templates/data_connector/src/utils/data_params.ts +9 -9
- package/templates/data_connector/src/utils/data_table.ts +17 -2
- package/templates/data_connector/src/utils/fetch_result.ts +6 -6
- package/templates/data_connector/src/utils/tests/data_table.test.ts +6 -6
- package/templates/data_connector/webpack.config.ts +5 -3
- package/templates/gen_ai/package.json +4 -4
- package/templates/gen_ai/webpack.config.ts +5 -3
- package/templates/hello_world/package.json +4 -4
- package/templates/hello_world/webpack.config.ts +5 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createContext, useState, useCallback } from "react";
|
|
2
|
-
import type {
|
|
2
|
+
import type { RenderSelectionUiRequest } from "@canva/intents/data";
|
|
3
3
|
import type { AccessTokenResponse } from "@canva/user";
|
|
4
4
|
import { auth } from "@canva/user";
|
|
5
5
|
import type { APIResponseItem, DataSourceHandler } from "src/api/data_source";
|
|
@@ -8,7 +8,7 @@ import { type DataSourceConfig } from "src/api/data_source";
|
|
|
8
8
|
export interface AppContextType {
|
|
9
9
|
appError: string;
|
|
10
10
|
setAppError: (value: string) => void;
|
|
11
|
-
|
|
11
|
+
request: RenderSelectionUiRequest;
|
|
12
12
|
isAuthenticated: boolean;
|
|
13
13
|
accessToken: AccessTokenResponse | undefined;
|
|
14
14
|
setAccessToken: (token: AccessTokenResponse | undefined) => void;
|
|
@@ -27,7 +27,7 @@ export interface AppContextType {
|
|
|
27
27
|
export const AppContext = createContext<AppContextType>({
|
|
28
28
|
appError: "",
|
|
29
29
|
setAppError: () => {},
|
|
30
|
-
|
|
30
|
+
request: {} as RenderSelectionUiRequest,
|
|
31
31
|
isAuthenticated: false,
|
|
32
32
|
accessToken: undefined,
|
|
33
33
|
setAccessToken: () => {},
|
|
@@ -51,15 +51,15 @@ export const AppContext = createContext<AppContextType>({
|
|
|
51
51
|
* For more information on React Context, refer to the official React documentation: {@link https://react.dev/learn/passing-data-deeply-with-context}.
|
|
52
52
|
*/
|
|
53
53
|
export const ContextProvider = ({
|
|
54
|
-
|
|
54
|
+
renderSelectionUiRequest,
|
|
55
55
|
children,
|
|
56
56
|
}: {
|
|
57
|
-
|
|
57
|
+
renderSelectionUiRequest: RenderSelectionUiRequest;
|
|
58
58
|
children: React.ReactNode;
|
|
59
59
|
}): JSX.Element => {
|
|
60
60
|
const [appError, setAppError] = useState<string>("");
|
|
61
|
-
const [
|
|
62
|
-
|
|
61
|
+
const [request] = useState<RenderSelectionUiRequest>(
|
|
62
|
+
renderSelectionUiRequest,
|
|
63
63
|
);
|
|
64
64
|
|
|
65
65
|
// authentication
|
|
@@ -77,7 +77,7 @@ export const ContextProvider = ({
|
|
|
77
77
|
// data connection
|
|
78
78
|
const loadDataSource = useCallback(
|
|
79
79
|
async (title: string, source: DataSourceConfig) => {
|
|
80
|
-
const result = await
|
|
80
|
+
const result = await request.updateDataRef({
|
|
81
81
|
title,
|
|
82
82
|
source: JSON.stringify(source),
|
|
83
83
|
});
|
|
@@ -91,7 +91,7 @@ export const ContextProvider = ({
|
|
|
91
91
|
setAppError("");
|
|
92
92
|
}
|
|
93
93
|
},
|
|
94
|
-
[
|
|
94
|
+
[request],
|
|
95
95
|
);
|
|
96
96
|
|
|
97
97
|
const logout = useCallback(async () => {
|
|
@@ -107,7 +107,7 @@ export const ContextProvider = ({
|
|
|
107
107
|
const value: AppContextType = {
|
|
108
108
|
appError,
|
|
109
109
|
setAppError,
|
|
110
|
-
|
|
110
|
+
request,
|
|
111
111
|
isAuthenticated,
|
|
112
112
|
accessToken,
|
|
113
113
|
setAccessToken,
|
|
@@ -26,27 +26,24 @@ const parseDataSource = (source: string) => {
|
|
|
26
26
|
export const Entrypoint = () => {
|
|
27
27
|
const navigate = useNavigate();
|
|
28
28
|
const context = useAppContext();
|
|
29
|
-
const {
|
|
29
|
+
const { request, setAppError, setDataSourceHandler } = context;
|
|
30
30
|
|
|
31
31
|
useEffect(() => {
|
|
32
32
|
// if the app was loaded with a populated data ref, we should reload the previous state.
|
|
33
33
|
// otherwise, if this is a first launch or there is an error, we should navigate to the first screen for a user - selecting a data source
|
|
34
34
|
let navigateTo: Paths | undefined;
|
|
35
35
|
|
|
36
|
-
if (isDataRefEmpty(
|
|
36
|
+
if (isDataRefEmpty(request)) {
|
|
37
37
|
// probably a first time launch - the user will need to select a data source
|
|
38
38
|
navigateTo = Paths.DATA_SOURCE_SELECTION;
|
|
39
|
-
} else if (
|
|
40
|
-
isOutdatedSource(dataParams) ||
|
|
41
|
-
isLaunchedWithError(dataParams)
|
|
42
|
-
) {
|
|
39
|
+
} else if (isOutdatedSource(request) || isLaunchedWithError(request)) {
|
|
43
40
|
// the configured source does not match the expected data source types
|
|
44
41
|
// so prompt the user to reconfigure the data source
|
|
45
42
|
setAppError("The data source configuration needs to be updated.");
|
|
46
43
|
navigateTo = Paths.DATA_SOURCE_SELECTION;
|
|
47
44
|
} else {
|
|
48
45
|
// there is a data ref, so we should parse it and navigate to the appropriate page
|
|
49
|
-
const dataRef =
|
|
46
|
+
const dataRef = request.invocationContext.dataSourceRef;
|
|
50
47
|
const parsedSource = parseDataSource(dataRef?.source ?? "");
|
|
51
48
|
|
|
52
49
|
if (parsedSource) {
|
|
@@ -67,7 +64,7 @@ export const Entrypoint = () => {
|
|
|
67
64
|
}
|
|
68
65
|
|
|
69
66
|
navigate(navigateTo || Paths.DATA_SOURCE_SELECTION);
|
|
70
|
-
}, [
|
|
67
|
+
}, [request]);
|
|
71
68
|
|
|
72
69
|
return <LoadingIndicator />;
|
|
73
70
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
RenderSelectionUiRequest,
|
|
3
|
+
GetDataTableRequest,
|
|
4
|
+
GetDataTableResponse,
|
|
5
5
|
} from "@canva/intents/data";
|
|
6
6
|
import { prepareDataConnector } from "@canva/intents/data";
|
|
7
7
|
import { createRoot } from "react-dom/client";
|
|
@@ -19,15 +19,15 @@ prepareDataConnector({
|
|
|
19
19
|
*
|
|
20
20
|
* This action is called in two scenarios:
|
|
21
21
|
*
|
|
22
|
-
* - During data selection to preview data before import (when {@link
|
|
22
|
+
* - During data selection to preview data before import (when {@link RenderSelectionUiRequest.updateDataRef} is called).
|
|
23
23
|
* - When refreshing previously imported data (when the user requests an update).
|
|
24
24
|
*
|
|
25
25
|
* @param params - Parameters for the data fetching operation.
|
|
26
26
|
* @returns A promise resolving to either a successful result with data or an error.
|
|
27
27
|
*/
|
|
28
|
-
|
|
29
|
-
params:
|
|
30
|
-
): Promise<
|
|
28
|
+
getDataTable: async (
|
|
29
|
+
params: GetDataTableRequest,
|
|
30
|
+
): Promise<GetDataTableResponse> => {
|
|
31
31
|
const oauth = auth.initOauth();
|
|
32
32
|
const token = await oauth.getAccessToken({ scope });
|
|
33
33
|
return buildDataTableResult(params, token?.token);
|
|
@@ -39,12 +39,12 @@ prepareDataConnector({
|
|
|
39
39
|
* When selection is complete, the implementation must call the `updateDataRef`
|
|
40
40
|
* callback provided in the params to preview and confirm the data selection.
|
|
41
41
|
*
|
|
42
|
-
* @param
|
|
42
|
+
* @param request - parameters that provide context and configuration for the data selection UI.
|
|
43
43
|
* Contains invocation context, size limits, and the updateDataRef callback
|
|
44
44
|
*/
|
|
45
|
-
renderSelectionUi: async (
|
|
45
|
+
renderSelectionUi: async (request: RenderSelectionUiRequest) => {
|
|
46
46
|
function render() {
|
|
47
|
-
root.render(<App
|
|
47
|
+
root.render(<App request={request} />);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
render();
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RenderSelectionUiRequest } from "@canva/intents/data";
|
|
2
2
|
|
|
3
|
-
export const isLaunchedWithError = (
|
|
4
|
-
return
|
|
3
|
+
export const isLaunchedWithError = (request: RenderSelectionUiRequest) => {
|
|
4
|
+
return request.invocationContext.reason === "app_error";
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
-
export const isOutdatedSource = (
|
|
8
|
-
return
|
|
7
|
+
export const isOutdatedSource = (request: RenderSelectionUiRequest) => {
|
|
8
|
+
return request.invocationContext.reason === "outdated_source_ref";
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
export const isDataRefEmpty = (
|
|
11
|
+
export const isDataRefEmpty = (request: RenderSelectionUiRequest) => {
|
|
12
12
|
return (
|
|
13
|
-
!
|
|
14
|
-
(!isLaunchedWithError(
|
|
15
|
-
!
|
|
13
|
+
!request?.invocationContext ||
|
|
14
|
+
(!isLaunchedWithError(request) &&
|
|
15
|
+
!request.invocationContext.dataSourceRef?.source)
|
|
16
16
|
);
|
|
17
17
|
};
|
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
DateDataTableCell,
|
|
6
6
|
NumberDataTableCell,
|
|
7
7
|
StringDataTableCell,
|
|
8
|
+
ColumnConfig,
|
|
8
9
|
} from "@canva/intents/data";
|
|
9
10
|
import type { APIResponseItem } from "src/api";
|
|
10
11
|
|
|
@@ -20,9 +21,9 @@ export function toDataTable<T extends APIResponseItem>(
|
|
|
20
21
|
rowLimit: number,
|
|
21
22
|
): DataTable {
|
|
22
23
|
const items = apiData.slice(0, rowLimit);
|
|
23
|
-
const headerRow = columns.map((column) => stringCell(column.label));
|
|
24
24
|
const dataTable: DataTable = {
|
|
25
|
-
|
|
25
|
+
columnConfigs: columnConfig(columns),
|
|
26
|
+
rows: [],
|
|
26
27
|
};
|
|
27
28
|
items.forEach((item) => {
|
|
28
29
|
const cells = columns.map((column) => {
|
|
@@ -37,6 +38,20 @@ export function toDataTable<T extends APIResponseItem>(
|
|
|
37
38
|
return dataTable;
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Converts an array of DataTableColumn to ColumnConfig.
|
|
43
|
+
* @param columns Array of DataTableColumn
|
|
44
|
+
* @returns Array of ColumnConfig
|
|
45
|
+
*/
|
|
46
|
+
function columnConfig<T extends APIResponseItem>(
|
|
47
|
+
columns: DataTableColumn<T>[],
|
|
48
|
+
): ColumnConfig[] {
|
|
49
|
+
return columns.map((column) => ({
|
|
50
|
+
name: column.label,
|
|
51
|
+
type: column.toCell({} as unknown).type, // Use an empty object to infer the type
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
|
|
40
55
|
/**
|
|
41
56
|
* Creates a string cell for the data table.
|
|
42
57
|
* @param value String containing up to 10,000 characters
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
DataTable,
|
|
3
3
|
DataTableMetadata,
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
GetDataTableError,
|
|
5
|
+
GetDataTableCompleted,
|
|
6
6
|
} from "@canva/intents/data";
|
|
7
7
|
|
|
8
8
|
export const completeDataTable = (
|
|
9
9
|
dataTable: DataTable,
|
|
10
10
|
metadata?: DataTableMetadata,
|
|
11
|
-
):
|
|
11
|
+
): GetDataTableCompleted => {
|
|
12
12
|
return {
|
|
13
13
|
status: "completed",
|
|
14
14
|
dataTable,
|
|
@@ -16,20 +16,20 @@ export const completeDataTable = (
|
|
|
16
16
|
};
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
export const appError = (message?: string):
|
|
19
|
+
export const appError = (message?: string): GetDataTableError => {
|
|
20
20
|
return {
|
|
21
21
|
status: "app_error",
|
|
22
22
|
message,
|
|
23
23
|
};
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
export const outdatedSourceRef = ():
|
|
26
|
+
export const outdatedSourceRef = (): GetDataTableError => {
|
|
27
27
|
return {
|
|
28
28
|
status: "outdated_source_ref",
|
|
29
29
|
};
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
export const remoteRequestFailed = ():
|
|
32
|
+
export const remoteRequestFailed = (): GetDataTableError => {
|
|
33
33
|
return {
|
|
34
34
|
status: "remote_request_failed",
|
|
35
35
|
};
|
|
@@ -68,11 +68,11 @@ describe("data table utils", () => {
|
|
|
68
68
|
},
|
|
69
69
|
];
|
|
70
70
|
|
|
71
|
-
test("toDataTable creates correct structure
|
|
71
|
+
test("toDataTable creates correct structure", () => {
|
|
72
72
|
const result = toDataTable(testItems, columns, 10);
|
|
73
73
|
|
|
74
|
-
expect(result.rows.length).toBe(
|
|
75
|
-
expect(result.
|
|
74
|
+
expect(result.rows.length).toBe(testItems.length);
|
|
75
|
+
expect(result.columnConfigs?.map((cell) => cell.name)).toEqual([
|
|
76
76
|
"ID",
|
|
77
77
|
"Name",
|
|
78
78
|
"Count",
|
|
@@ -85,15 +85,15 @@ describe("data table utils", () => {
|
|
|
85
85
|
test("toDataTable respects row limit", () => {
|
|
86
86
|
const result = toDataTable(testItems, columns, 1);
|
|
87
87
|
|
|
88
|
-
expect(result.rows.length).toBe(
|
|
88
|
+
expect(result.rows.length).toBe(1);
|
|
89
89
|
});
|
|
90
90
|
|
|
91
91
|
test("toDataTable handles function-based getValue", () => {
|
|
92
92
|
const result = toDataTable(testItems, columns, 10);
|
|
93
93
|
|
|
94
94
|
// Check the custom column values
|
|
95
|
-
expect(result.rows[
|
|
96
|
-
expect(result.rows[
|
|
95
|
+
expect(result.rows[0].cells[5].value).toBe("Test Item 1 (42)");
|
|
96
|
+
expect(result.rows[1].cells[5].value).toBe("Test Item 2 (0)");
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
test("cell formatter functions create correct cell structures", () => {
|
|
@@ -199,6 +199,7 @@ function buildDevConfig(options?: DevConfig): {
|
|
|
199
199
|
|
|
200
200
|
const { port, enableHmr, appOrigin, appId, enableHttps, certFile, keyFile } =
|
|
201
201
|
options;
|
|
202
|
+
const host = "localhost";
|
|
202
203
|
|
|
203
204
|
let devServer: DevServerConfiguration = {
|
|
204
205
|
server: enableHttps
|
|
@@ -210,7 +211,8 @@ function buildDevConfig(options?: DevConfig): {
|
|
|
210
211
|
},
|
|
211
212
|
}
|
|
212
213
|
: "http",
|
|
213
|
-
host
|
|
214
|
+
host,
|
|
215
|
+
allowedHosts: [host],
|
|
214
216
|
historyApiFallback: {
|
|
215
217
|
rewrites: [{ from: /^\/$/, to: "/app.js" }],
|
|
216
218
|
},
|
|
@@ -227,7 +229,7 @@ function buildDevConfig(options?: DevConfig): {
|
|
|
227
229
|
if (enableHmr && appOrigin) {
|
|
228
230
|
devServer = {
|
|
229
231
|
...devServer,
|
|
230
|
-
allowedHosts: new URL(appOrigin).hostname,
|
|
232
|
+
allowedHosts: [host, new URL(appOrigin).hostname],
|
|
231
233
|
headers: {
|
|
232
234
|
"Access-Control-Allow-Origin": appOrigin,
|
|
233
235
|
"Access-Control-Allow-Credentials": "true",
|
|
@@ -245,7 +247,7 @@ function buildDevConfig(options?: DevConfig): {
|
|
|
245
247
|
const appDomain = `app-${appId.toLowerCase().trim()}.canva-apps.com`;
|
|
246
248
|
devServer = {
|
|
247
249
|
...devServer,
|
|
248
|
-
allowedHosts: appDomain,
|
|
250
|
+
allowedHosts: [host, appDomain],
|
|
249
251
|
headers: {
|
|
250
252
|
"Access-Control-Allow-Origin": `https://${appDomain}`,
|
|
251
253
|
"Access-Control-Allow-Credentials": "true",
|
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@canva/app-i18n-kit": "^1.0.2",
|
|
22
|
-
"@canva/app-ui-kit": "^4.
|
|
22
|
+
"@canva/app-ui-kit": "^4.10.0",
|
|
23
23
|
"@canva/asset": "^2.2.0",
|
|
24
|
-
"@canva/design": "^2.
|
|
24
|
+
"@canva/design": "^2.6.0",
|
|
25
25
|
"@canva/error": "^2.1.0",
|
|
26
|
-
"@canva/platform": "^2.
|
|
26
|
+
"@canva/platform": "^2.2.0",
|
|
27
27
|
"@canva/user": "^2.1.0",
|
|
28
28
|
"cookie-parser": "1.4.7",
|
|
29
29
|
"cors": "2.8.5",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"url-loader": "4.1.1",
|
|
94
94
|
"webpack": "5.97.1",
|
|
95
95
|
"webpack-cli": "5.1.4",
|
|
96
|
-
"webpack-dev-server": "5.2.
|
|
96
|
+
"webpack-dev-server": "5.2.2",
|
|
97
97
|
"yargs": "17.7.2"
|
|
98
98
|
}
|
|
99
99
|
}
|
|
@@ -199,6 +199,7 @@ function buildDevConfig(options?: DevConfig): {
|
|
|
199
199
|
|
|
200
200
|
const { port, enableHmr, appOrigin, appId, enableHttps, certFile, keyFile } =
|
|
201
201
|
options;
|
|
202
|
+
const host = "localhost";
|
|
202
203
|
|
|
203
204
|
let devServer: DevServerConfiguration = {
|
|
204
205
|
server: enableHttps
|
|
@@ -210,7 +211,8 @@ function buildDevConfig(options?: DevConfig): {
|
|
|
210
211
|
},
|
|
211
212
|
}
|
|
212
213
|
: "http",
|
|
213
|
-
host
|
|
214
|
+
host,
|
|
215
|
+
allowedHosts: [host],
|
|
214
216
|
historyApiFallback: {
|
|
215
217
|
rewrites: [{ from: /^\/$/, to: "/app.js" }],
|
|
216
218
|
},
|
|
@@ -227,7 +229,7 @@ function buildDevConfig(options?: DevConfig): {
|
|
|
227
229
|
if (enableHmr && appOrigin) {
|
|
228
230
|
devServer = {
|
|
229
231
|
...devServer,
|
|
230
|
-
allowedHosts: new URL(appOrigin).hostname,
|
|
232
|
+
allowedHosts: [host, new URL(appOrigin).hostname],
|
|
231
233
|
headers: {
|
|
232
234
|
"Access-Control-Allow-Origin": appOrigin,
|
|
233
235
|
"Access-Control-Allow-Credentials": "true",
|
|
@@ -245,7 +247,7 @@ function buildDevConfig(options?: DevConfig): {
|
|
|
245
247
|
const appDomain = `app-${appId.toLowerCase().trim()}.canva-apps.com`;
|
|
246
248
|
devServer = {
|
|
247
249
|
...devServer,
|
|
248
|
-
allowedHosts: appDomain,
|
|
250
|
+
allowedHosts: [host, appDomain],
|
|
249
251
|
headers: {
|
|
250
252
|
"Access-Control-Allow-Origin": `https://${appDomain}`,
|
|
251
253
|
"Access-Control-Allow-Credentials": "true",
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@canva/app-i18n-kit": "^1.0.2",
|
|
23
|
-
"@canva/app-ui-kit": "^4.
|
|
23
|
+
"@canva/app-ui-kit": "^4.10.0",
|
|
24
24
|
"@canva/asset": "^2.2.0",
|
|
25
|
-
"@canva/design": "^2.
|
|
25
|
+
"@canva/design": "^2.6.0",
|
|
26
26
|
"@canva/error": "^2.1.0",
|
|
27
|
-
"@canva/platform": "^2.
|
|
27
|
+
"@canva/platform": "^2.2.0",
|
|
28
28
|
"@canva/user": "^2.1.0",
|
|
29
29
|
"react": "18.3.1",
|
|
30
30
|
"react-dom": "18.3.1",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"url-loader": "4.1.1",
|
|
83
83
|
"webpack": "5.97.1",
|
|
84
84
|
"webpack-cli": "5.1.4",
|
|
85
|
-
"webpack-dev-server": "5.2.
|
|
85
|
+
"webpack-dev-server": "5.2.2",
|
|
86
86
|
"yargs": "17.7.2"
|
|
87
87
|
}
|
|
88
88
|
}
|
|
@@ -199,6 +199,7 @@ function buildDevConfig(options?: DevConfig): {
|
|
|
199
199
|
|
|
200
200
|
const { port, enableHmr, appOrigin, appId, enableHttps, certFile, keyFile } =
|
|
201
201
|
options;
|
|
202
|
+
const host = "localhost";
|
|
202
203
|
|
|
203
204
|
let devServer: DevServerConfiguration = {
|
|
204
205
|
server: enableHttps
|
|
@@ -210,7 +211,8 @@ function buildDevConfig(options?: DevConfig): {
|
|
|
210
211
|
},
|
|
211
212
|
}
|
|
212
213
|
: "http",
|
|
213
|
-
host
|
|
214
|
+
host,
|
|
215
|
+
allowedHosts: [host],
|
|
214
216
|
historyApiFallback: {
|
|
215
217
|
rewrites: [{ from: /^\/$/, to: "/app.js" }],
|
|
216
218
|
},
|
|
@@ -227,7 +229,7 @@ function buildDevConfig(options?: DevConfig): {
|
|
|
227
229
|
if (enableHmr && appOrigin) {
|
|
228
230
|
devServer = {
|
|
229
231
|
...devServer,
|
|
230
|
-
allowedHosts: new URL(appOrigin).hostname,
|
|
232
|
+
allowedHosts: [host, new URL(appOrigin).hostname],
|
|
231
233
|
headers: {
|
|
232
234
|
"Access-Control-Allow-Origin": appOrigin,
|
|
233
235
|
"Access-Control-Allow-Credentials": "true",
|
|
@@ -245,7 +247,7 @@ function buildDevConfig(options?: DevConfig): {
|
|
|
245
247
|
const appDomain = `app-${appId.toLowerCase().trim()}.canva-apps.com`;
|
|
246
248
|
devServer = {
|
|
247
249
|
...devServer,
|
|
248
|
-
allowedHosts: appDomain,
|
|
250
|
+
allowedHosts: [host, appDomain],
|
|
249
251
|
headers: {
|
|
250
252
|
"Access-Control-Allow-Origin": `https://${appDomain}`,
|
|
251
253
|
"Access-Control-Allow-Credentials": "true",
|