@canva/cli 1.19.0 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +1 -9
  3. package/cli.js +408 -409
  4. package/package.json +1 -2
  5. package/templates/base/backend/base_backend/create.ts +0 -114
  6. package/templates/base/backend/database/database.ts +0 -42
  7. package/templates/base/backend/routers/auth.ts +0 -288
  8. package/templates/base/declarations/declarations.d.ts +0 -29
  9. package/templates/base/eslint.config.mjs +0 -14
  10. package/templates/base/package.json +0 -91
  11. package/templates/base/scripts/copy_env.ts +0 -13
  12. package/templates/base/scripts/ssl/ssl.ts +0 -131
  13. package/templates/base/scripts/start/app_runner.ts +0 -223
  14. package/templates/base/scripts/start/context.ts +0 -171
  15. package/templates/base/scripts/start/start.ts +0 -46
  16. package/templates/base/scripts/start.tests.ts +0 -61
  17. package/templates/base/styles/components.css +0 -38
  18. package/templates/base/tsconfig.json +0 -56
  19. package/templates/base/webpack.config.ts +0 -247
  20. package/templates/common/.env.template +0 -6
  21. package/templates/common/.gitignore.template +0 -8
  22. package/templates/common/.nvmrc +0 -1
  23. package/templates/common/.prettierrc +0 -21
  24. package/templates/common/LICENSE.md +0 -48
  25. package/templates/common/README.md +0 -179
  26. package/templates/common/jest.config.mjs +0 -35
  27. package/templates/common/jest.setup.ts +0 -35
  28. package/templates/content_publisher/README.md +0 -58
  29. package/templates/content_publisher/canva-app.json +0 -17
  30. package/templates/content_publisher/declarations/declarations.d.ts +0 -29
  31. package/templates/content_publisher/eslint.config.mjs +0 -14
  32. package/templates/content_publisher/package.json +0 -90
  33. package/templates/content_publisher/scripts/copy_env.ts +0 -13
  34. package/templates/content_publisher/scripts/ssl/ssl.ts +0 -131
  35. package/templates/content_publisher/scripts/start/app_runner.ts +0 -223
  36. package/templates/content_publisher/scripts/start/context.ts +0 -171
  37. package/templates/content_publisher/scripts/start/start.ts +0 -46
  38. package/templates/content_publisher/src/index.tsx +0 -4
  39. package/templates/content_publisher/src/intents/content_publisher/index.tsx +0 -107
  40. package/templates/content_publisher/src/intents/content_publisher/post_preview.tsx +0 -240
  41. package/templates/content_publisher/src/intents/content_publisher/preview_ui.tsx +0 -62
  42. package/templates/content_publisher/src/intents/content_publisher/settings_ui.tsx +0 -81
  43. package/templates/content_publisher/src/intents/content_publisher/types.ts +0 -29
  44. package/templates/content_publisher/styles/components.css +0 -38
  45. package/templates/content_publisher/styles/preview_ui.css +0 -49
  46. package/templates/content_publisher/tsconfig.json +0 -56
  47. package/templates/content_publisher/webpack.config.ts +0 -247
  48. package/templates/dam/backend/routers/dam.ts +0 -108
  49. package/templates/dam/backend/server.ts +0 -65
  50. package/templates/dam/canva-app.json +0 -25
  51. package/templates/dam/declarations/declarations.d.ts +0 -29
  52. package/templates/dam/eslint.config.mjs +0 -14
  53. package/templates/dam/package.json +0 -97
  54. package/templates/dam/scripts/copy_env.ts +0 -13
  55. package/templates/dam/scripts/ssl/ssl.ts +0 -131
  56. package/templates/dam/scripts/start/app_runner.ts +0 -223
  57. package/templates/dam/scripts/start/context.ts +0 -171
  58. package/templates/dam/scripts/start/start.ts +0 -46
  59. package/templates/dam/src/index.tsx +0 -4
  60. package/templates/dam/src/intents/design_editor/adapter.ts +0 -44
  61. package/templates/dam/src/intents/design_editor/app.tsx +0 -35
  62. package/templates/dam/src/intents/design_editor/config.ts +0 -220
  63. package/templates/dam/src/intents/design_editor/index.css +0 -3
  64. package/templates/dam/src/intents/design_editor/index.tsx +0 -25
  65. package/templates/dam/tsconfig.json +0 -56
  66. package/templates/dam/utils/backend/base_backend/create.ts +0 -114
  67. package/templates/dam/webpack.config.ts +0 -247
  68. package/templates/data_connector/README.md +0 -84
  69. package/templates/data_connector/canva-app.json +0 -21
  70. package/templates/data_connector/declarations/declarations.d.ts +0 -29
  71. package/templates/data_connector/eslint.config.mjs +0 -14
  72. package/templates/data_connector/package.json +0 -92
  73. package/templates/data_connector/scripts/copy_env.ts +0 -13
  74. package/templates/data_connector/scripts/ssl/ssl.ts +0 -131
  75. package/templates/data_connector/scripts/start/app_runner.ts +0 -223
  76. package/templates/data_connector/scripts/start/context.ts +0 -171
  77. package/templates/data_connector/scripts/start/start.ts +0 -46
  78. package/templates/data_connector/src/api/connect_client.ts +0 -6
  79. package/templates/data_connector/src/api/data_source.ts +0 -97
  80. package/templates/data_connector/src/api/data_sources/designs.tsx +0 -296
  81. package/templates/data_connector/src/api/data_sources/index.ts +0 -4
  82. package/templates/data_connector/src/api/data_sources/templates.tsx +0 -328
  83. package/templates/data_connector/src/api/fetch_data_table.ts +0 -55
  84. package/templates/data_connector/src/api/index.ts +0 -4
  85. package/templates/data_connector/src/api/oauth.ts +0 -8
  86. package/templates/data_connector/src/api/tests/data_source.test.tsx +0 -99
  87. package/templates/data_connector/src/components/app_error.tsx +0 -15
  88. package/templates/data_connector/src/components/footer.tsx +0 -26
  89. package/templates/data_connector/src/components/header.tsx +0 -40
  90. package/templates/data_connector/src/components/index.ts +0 -3
  91. package/templates/data_connector/src/components/inputs/messages.tsx +0 -95
  92. package/templates/data_connector/src/components/inputs/search_filter.tsx +0 -109
  93. package/templates/data_connector/src/components/inputs/select_field.tsx +0 -26
  94. package/templates/data_connector/src/context/app_context.tsx +0 -125
  95. package/templates/data_connector/src/context/index.ts +0 -2
  96. package/templates/data_connector/src/context/use_app_context.ts +0 -17
  97. package/templates/data_connector/src/index.tsx +0 -4
  98. package/templates/data_connector/src/intents/data_connector/app.tsx +0 -20
  99. package/templates/data_connector/src/intents/data_connector/entrypoint.tsx +0 -70
  100. package/templates/data_connector/src/intents/data_connector/home.tsx +0 -21
  101. package/templates/data_connector/src/intents/data_connector/index.tsx +0 -56
  102. package/templates/data_connector/src/pages/data_source_config.tsx +0 -9
  103. package/templates/data_connector/src/pages/error.tsx +0 -37
  104. package/templates/data_connector/src/pages/index.ts +0 -4
  105. package/templates/data_connector/src/pages/login.tsx +0 -145
  106. package/templates/data_connector/src/pages/select_source.tsx +0 -24
  107. package/templates/data_connector/src/routes/index.ts +0 -2
  108. package/templates/data_connector/src/routes/paths.ts +0 -7
  109. package/templates/data_connector/src/routes/protected_route.tsx +0 -26
  110. package/templates/data_connector/src/routes/routes.tsx +0 -42
  111. package/templates/data_connector/src/utils/data_params.ts +0 -17
  112. package/templates/data_connector/src/utils/data_table.ts +0 -116
  113. package/templates/data_connector/src/utils/fetch_result.ts +0 -36
  114. package/templates/data_connector/src/utils/index.ts +0 -2
  115. package/templates/data_connector/src/utils/tests/data_table.test.ts +0 -133
  116. package/templates/data_connector/styles/components.css +0 -38
  117. package/templates/data_connector/tsconfig.json +0 -56
  118. package/templates/data_connector/webpack.config.ts +0 -247
  119. package/templates/gen_ai/README.md +0 -27
  120. package/templates/gen_ai/backend/routers/image.ts +0 -232
  121. package/templates/gen_ai/backend/server.ts +0 -65
  122. package/templates/gen_ai/canva-app.json +0 -25
  123. package/templates/gen_ai/declarations/declarations.d.ts +0 -29
  124. package/templates/gen_ai/eslint.config.mjs +0 -14
  125. package/templates/gen_ai/package.json +0 -101
  126. package/templates/gen_ai/scripts/copy_env.ts +0 -13
  127. package/templates/gen_ai/scripts/ssl/ssl.ts +0 -131
  128. package/templates/gen_ai/scripts/start/app_runner.ts +0 -223
  129. package/templates/gen_ai/scripts/start/context.ts +0 -171
  130. package/templates/gen_ai/scripts/start/start.ts +0 -46
  131. package/templates/gen_ai/src/api/api.ts +0 -194
  132. package/templates/gen_ai/src/api/index.ts +0 -1
  133. package/templates/gen_ai/src/components/app_error.tsx +0 -18
  134. package/templates/gen_ai/src/components/footer.messages.ts +0 -48
  135. package/templates/gen_ai/src/components/footer.tsx +0 -156
  136. package/templates/gen_ai/src/components/image_grid.tsx +0 -103
  137. package/templates/gen_ai/src/components/index.ts +0 -7
  138. package/templates/gen_ai/src/components/loading_results.tsx +0 -169
  139. package/templates/gen_ai/src/components/prompt_input.messages.ts +0 -14
  140. package/templates/gen_ai/src/components/prompt_input.tsx +0 -154
  141. package/templates/gen_ai/src/components/remaining_credits.tsx +0 -84
  142. package/templates/gen_ai/src/components/report_box.tsx +0 -54
  143. package/templates/gen_ai/src/components/tests/remaining_credit.tests.tsx +0 -47
  144. package/templates/gen_ai/src/config.ts +0 -21
  145. package/templates/gen_ai/src/context/app_context.tsx +0 -153
  146. package/templates/gen_ai/src/context/context.messages.ts +0 -30
  147. package/templates/gen_ai/src/context/index.ts +0 -2
  148. package/templates/gen_ai/src/context/use_app_context.ts +0 -17
  149. package/templates/gen_ai/src/index.tsx +0 -4
  150. package/templates/gen_ai/src/intents/design_editor/app.tsx +0 -19
  151. package/templates/gen_ai/src/intents/design_editor/home.tsx +0 -13
  152. package/templates/gen_ai/src/intents/design_editor/index.tsx +0 -17
  153. package/templates/gen_ai/src/pages/error.tsx +0 -41
  154. package/templates/gen_ai/src/pages/generate.tsx +0 -9
  155. package/templates/gen_ai/src/pages/index.ts +0 -3
  156. package/templates/gen_ai/src/pages/results.tsx +0 -31
  157. package/templates/gen_ai/src/routes/index.ts +0 -1
  158. package/templates/gen_ai/src/routes/paths.ts +0 -4
  159. package/templates/gen_ai/src/routes/routes.tsx +0 -24
  160. package/templates/gen_ai/src/utils/index.ts +0 -1
  161. package/templates/gen_ai/src/utils/obscenity_filter.ts +0 -33
  162. package/templates/gen_ai/styles/components.css +0 -38
  163. package/templates/gen_ai/styles/utils.css +0 -3
  164. package/templates/gen_ai/tsconfig.json +0 -56
  165. package/templates/gen_ai/utils/backend/base_backend/create.ts +0 -114
  166. package/templates/gen_ai/webpack.config.ts +0 -247
  167. package/templates/hello_world/canva-app.json +0 -21
  168. package/templates/hello_world/declarations/declarations.d.ts +0 -29
  169. package/templates/hello_world/eslint.config.mjs +0 -14
  170. package/templates/hello_world/package.json +0 -90
  171. package/templates/hello_world/scripts/copy_env.ts +0 -13
  172. package/templates/hello_world/scripts/ssl/ssl.ts +0 -131
  173. package/templates/hello_world/scripts/start/app_runner.ts +0 -223
  174. package/templates/hello_world/scripts/start/context.ts +0 -171
  175. package/templates/hello_world/scripts/start/start.ts +0 -46
  176. package/templates/hello_world/src/index.tsx +0 -4
  177. package/templates/hello_world/src/intents/design_editor/app.tsx +0 -86
  178. package/templates/hello_world/src/intents/design_editor/index.tsx +0 -25
  179. package/templates/hello_world/src/intents/design_editor/tests/__snapshots__/app.tests.tsx.snap +0 -45
  180. package/templates/hello_world/src/intents/design_editor/tests/app.tests.tsx +0 -92
  181. package/templates/hello_world/styles/components.css +0 -38
  182. package/templates/hello_world/tsconfig.json +0 -56
  183. package/templates/hello_world/webpack.config.ts +0 -247
  184. package/templates/mls/README.md +0 -81
  185. package/templates/mls/canva-app.json +0 -25
  186. package/templates/mls/declarations/declarations.d.ts +0 -29
  187. package/templates/mls/eslint.config.mjs +0 -14
  188. package/templates/mls/jest.config.mjs +0 -36
  189. package/templates/mls/jest.setup.ts +0 -39
  190. package/templates/mls/package.json +0 -117
  191. package/templates/mls/scripts/copy_env.ts +0 -13
  192. package/templates/mls/scripts/ssl/ssl.ts +0 -131
  193. package/templates/mls/scripts/start/app_runner.ts +0 -223
  194. package/templates/mls/scripts/start/context.ts +0 -171
  195. package/templates/mls/scripts/start/start.ts +0 -46
  196. package/templates/mls/src/__tests__/app.tests.tsx +0 -11
  197. package/templates/mls/src/__tests__/office_selection_page.tests.tsx +0 -72
  198. package/templates/mls/src/__tests__/utils.tsx +0 -19
  199. package/templates/mls/src/adapter.ts +0 -126
  200. package/templates/mls/src/components/agent/agent_card.tsx +0 -57
  201. package/templates/mls/src/components/agent/agent_grid.tsx +0 -37
  202. package/templates/mls/src/components/agent/agent_list.tsx +0 -17
  203. package/templates/mls/src/components/agent/agent_search_filters.tsx +0 -88
  204. package/templates/mls/src/components/breadcrumb/breadcrumb.tsx +0 -40
  205. package/templates/mls/src/components/listing/listing_card.tsx +0 -64
  206. package/templates/mls/src/components/listing/listing_grid.tsx +0 -37
  207. package/templates/mls/src/components/listing/listing_list.tsx +0 -21
  208. package/templates/mls/src/components/listing/listing_search_filters.tsx +0 -145
  209. package/templates/mls/src/components/placeholders/placeholders.tsx +0 -65
  210. package/templates/mls/src/data.ts +0 -359
  211. package/templates/mls/src/index.tsx +0 -4
  212. package/templates/mls/src/intents/design_editor/app.tsx +0 -44
  213. package/templates/mls/src/intents/design_editor/index.tsx +0 -25
  214. package/templates/mls/src/pages/agent_details_page/agent_details_page.tsx +0 -175
  215. package/templates/mls/src/pages/list_page/agent_tab_panel.tsx +0 -126
  216. package/templates/mls/src/pages/list_page/list_page.tsx +0 -67
  217. package/templates/mls/src/pages/list_page/listing_tab_panel.tsx +0 -135
  218. package/templates/mls/src/pages/listing_details_page/listing_details_page.tsx +0 -418
  219. package/templates/mls/src/pages/loading_page/loading_page.tsx +0 -152
  220. package/templates/mls/src/pages/office_selection_page/office_selection_page.tsx +0 -144
  221. package/templates/mls/src/real_estate.type.ts +0 -44
  222. package/templates/mls/src/util/use_add_element.tsx +0 -62
  223. package/templates/mls/src/util/use_drag_element.tsx +0 -68
  224. package/templates/mls/styles/components.css +0 -38
  225. package/templates/mls/tsconfig.json +0 -54
  226. package/templates/mls/webpack.config.ts +0 -248
  227. package/templates/optional/.cursor/mcp.json +0 -8
  228. package/templates/optional/.vscode/extensions.json +0 -6
  229. package/templates/optional/.vscode/mcp.json +0 -9
  230. package/templates/optional/AGENTS.md +0 -154
  231. package/templates/optional/CLAUDE.md +0 -154
@@ -1,38 +0,0 @@
1
- /* Scroll container */
2
- .scrollContainer {
3
- box-sizing: border-box;
4
- overflow-y: scroll;
5
- height: 100%;
6
- padding-top: var(--ui-kit-space-200);
7
- padding-right: var(--ui-kit-space-200);
8
- padding-bottom: var(--ui-kit-space-200);
9
-
10
- /* for firefox */
11
- scrollbar-width: thin;
12
- scrollbar-color: var(--ui-kit-color-content-placeholder-fg) transparent;
13
- }
14
-
15
- .scrollContainer::-webkit-scrollbar {
16
- position: absolute;
17
- width: var(--ui-kit-base-unit);
18
- height: 0;
19
- }
20
-
21
- .scrollContainer::-webkit-scrollbar-track {
22
- background: transparent;
23
- width: var(--ui-kit-base-unit);
24
- margin-top: var(--ui-kit-space-100);
25
- margin-bottom: var(--ui-kit-space-100);
26
- }
27
-
28
- .scrollContainer::-webkit-scrollbar-thumb {
29
- border-radius: var(--ui-kit-radius-element-standard);
30
- background: var(--ui-kit-color-content-placeholder-fg);
31
- visibility: hidden;
32
- }
33
-
34
- .scrollContainer:hover::-webkit-scrollbar-thumb,
35
- .scrollContainer:focus::-webkit-scrollbar-thumb,
36
- .scrollContainer:focus-within::-webkit-scrollbar-thumb {
37
- visibility: visible;
38
- }
@@ -1,56 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "jsx": "react-jsx",
4
- "lib": [
5
- "dom",
6
- "dom.iterable",
7
- "es2018",
8
- "es2019.array",
9
- "es2019.object",
10
- "es2019.string",
11
- "es2020.promise",
12
- "es2020.string"
13
- ],
14
- "types": ["node", "webpack-env", "jest"],
15
- "composite": false,
16
- "declaration": false,
17
- "declarationMap": false,
18
- "experimentalDecorators": true,
19
- "importHelpers": true,
20
- "noImplicitOverride": true,
21
- "moduleResolution": "bundler",
22
- "esModuleInterop": true,
23
- "rootDir": ".",
24
- "outDir": "dist",
25
- "strict": true,
26
- "skipLibCheck": true,
27
- "target": "ES2019",
28
- "sourceMap": true,
29
- "inlineSources": true,
30
- "module": "ESNext",
31
- "noImplicitAny": true,
32
- "noImplicitReturns": true,
33
- "noFallthroughCasesInSwitch": true,
34
- "noUncheckedIndexedAccess": true,
35
- "removeComments": true,
36
- "preserveConstEnums": true,
37
- "allowSyntheticDefaultImports": true,
38
- "baseUrl": "./",
39
- "paths": {
40
- "styles": ["./styles"]
41
- }
42
- },
43
- "include": [
44
- "./src/**/*",
45
- "./backend/**/*",
46
- "./utils/**/*",
47
- "./scripts/**/*",
48
- "./declarations/declarations.d.ts",
49
- "./styles/**/*"
50
- ],
51
- "ts-node": {
52
- "compilerOptions": {
53
- "module": "commonjs"
54
- }
55
- }
56
- }
@@ -1,247 +0,0 @@
1
- import type { Configuration } from "webpack";
2
- import { DefinePlugin, optimize } from "webpack";
3
- import path from "path";
4
- import TerserPlugin from "terser-webpack-plugin";
5
- import { transform } from "@formatjs/ts-transformer";
6
- import chalk from "chalk";
7
- import { config } from "dotenv";
8
- import { Configuration as DevServerConfiguration } from "webpack-dev-server";
9
-
10
- config();
11
-
12
- type DevConfig = {
13
- port: number;
14
- enableHmr: boolean;
15
- enableHttps: boolean;
16
- appOrigin?: string;
17
- appId?: string; // Deprecated in favour of appOrigin
18
- certFile?: string;
19
- keyFile?: string;
20
- };
21
-
22
- export function buildConfig({
23
- devConfig,
24
- appEntry = path.join(process.cwd(), "src", "index.tsx"),
25
- backendHost = process.env.CANVA_BACKEND_HOST,
26
- // For IN_HARNESS, refer to the following docs for more information: https://www.canva.dev/docs/apps/test-harness/
27
- inHarness = process.env.IN_HARNESS?.toLowerCase() === "true",
28
- }: {
29
- devConfig?: DevConfig;
30
- appEntry?: string;
31
- backendHost?: string;
32
- inHarness?: boolean;
33
- } = {}): Configuration & DevServerConfiguration {
34
- const mode = devConfig ? "development" : "production";
35
-
36
- if (!backendHost) {
37
- console.warn(
38
- chalk.yellow.bold("BACKEND_HOST is undefined."),
39
- `If your app requires a backend, refer to "Customizing the backend host" in the README.md for more information.`,
40
- );
41
- } else if (backendHost.includes("localhost") && mode === "production") {
42
- console.error(
43
- chalk.redBright.bold(
44
- "BACKEND_HOST should not be set to localhost for production builds!",
45
- ),
46
- `Refer to "Customizing the backend host" in the README.md for more information.`,
47
- );
48
- }
49
-
50
- return {
51
- mode,
52
- context: path.resolve(process.cwd(), "./"),
53
- entry: inHarness
54
- ? {
55
- harness: path.join(process.cwd(), "harness", "harness.tsx"),
56
- init: path.join(process.cwd(), "harness", "init.ts"),
57
- }
58
- : {
59
- app: appEntry,
60
- },
61
- target: "web",
62
- resolve: {
63
- alias: {
64
- styles: path.resolve(process.cwd(), "styles"),
65
- src: path.resolve(process.cwd(), "src"),
66
- },
67
- extensions: [".ts", ".tsx", ".js", ".css", ".svg", ".woff", ".woff2"],
68
- },
69
- infrastructureLogging: {
70
- level: inHarness ? "info" : "none",
71
- },
72
- module: {
73
- rules: [
74
- {
75
- test: /\.tsx?$/,
76
- exclude: /node_modules/,
77
- use: [
78
- {
79
- loader: "ts-loader",
80
- options: {
81
- transpileOnly: true,
82
- getCustomTransformers() {
83
- return {
84
- before: [
85
- transform({
86
- overrideIdFn: "[sha512:contenthash:base64:6]",
87
- }),
88
- ],
89
- };
90
- },
91
- },
92
- },
93
- ],
94
- },
95
- {
96
- test: /\.css$/,
97
- exclude: /node_modules/,
98
- use: [
99
- "style-loader",
100
- {
101
- loader: "css-loader",
102
- options: {
103
- modules: true,
104
- },
105
- },
106
- {
107
- loader: "postcss-loader",
108
- options: {
109
- postcssOptions: {
110
- plugins: [require("cssnano")({ preset: "default" })],
111
- },
112
- },
113
- },
114
- ],
115
- },
116
- {
117
- test: /\.(png|jpg|jpeg)$/i,
118
- type: "asset/inline",
119
- },
120
- {
121
- test: /\.(woff|woff2)$/,
122
- type: "asset/inline",
123
- },
124
- {
125
- test: /\.svg$/,
126
- oneOf: [
127
- {
128
- issuer: /\.[jt]sx?$/,
129
- resourceQuery: /react/, // *.svg?react
130
- use: ["@svgr/webpack", "url-loader"],
131
- },
132
- {
133
- type: "asset/resource",
134
- parser: {
135
- dataUrlCondition: {
136
- maxSize: 200,
137
- },
138
- },
139
- },
140
- ],
141
- },
142
- {
143
- test: /\.css$/,
144
- include: /node_modules/,
145
- use: [
146
- "style-loader",
147
- "css-loader",
148
- {
149
- loader: "postcss-loader",
150
- options: {
151
- postcssOptions: {
152
- plugins: [require("cssnano")({ preset: "default" })],
153
- },
154
- },
155
- },
156
- ],
157
- },
158
- ],
159
- },
160
- optimization: {
161
- minimizer: [
162
- new TerserPlugin({
163
- terserOptions: {
164
- format: {
165
- // Turned on because emoji and regex is not minified properly using default
166
- // https://github.com/facebook/create-react-app/issues/2488
167
- ascii_only: true,
168
- },
169
- },
170
- }),
171
- ],
172
- },
173
- output: {
174
- filename: `[name].js`,
175
- path: path.resolve(process.cwd(), "dist"),
176
- clean: true,
177
- },
178
- plugins: [
179
- new DefinePlugin({
180
- BACKEND_HOST: JSON.stringify(backendHost),
181
- }),
182
- // Apps can only submit a single JS file via the Developer Portal
183
- new optimize.LimitChunkCountPlugin({ maxChunks: 1 }),
184
- ].filter(Boolean),
185
- ...buildDevConfig(devConfig),
186
- };
187
- }
188
-
189
- function buildDevConfig(options?: DevConfig): {
190
- devtool?: string;
191
- devServer?: DevServerConfiguration;
192
- } {
193
- if (!options) {
194
- return {};
195
- }
196
-
197
- const { port, enableHmr, appOrigin, enableHttps, certFile, keyFile } =
198
- options;
199
- const host = "localhost";
200
-
201
- let devServer: DevServerConfiguration = {
202
- server: enableHttps
203
- ? {
204
- type: "https",
205
- options: {
206
- cert: certFile,
207
- key: keyFile,
208
- },
209
- }
210
- : "http",
211
- host,
212
- allowedHosts: [host],
213
- historyApiFallback: {
214
- rewrites: [{ from: /^\/$/, to: "/app.js" }],
215
- },
216
- port,
217
- client: {
218
- logging: "verbose",
219
- },
220
- };
221
-
222
- if (enableHmr && appOrigin) {
223
- devServer = {
224
- ...devServer,
225
- allowedHosts: [host, new URL(appOrigin).hostname],
226
- headers: {
227
- "Access-Control-Allow-Origin": appOrigin,
228
- "Access-Control-Allow-Credentials": "true",
229
- "Access-Control-Allow-Private-Network": "true",
230
- },
231
- };
232
- } else {
233
- if (enableHmr && !appOrigin) {
234
- console.warn(
235
- "Attempted to enable Hot Module Replacement (HMR) without configuring App Origin... Disabling HMR.",
236
- );
237
- }
238
- devServer.webSocketServer = false;
239
- }
240
-
241
- return {
242
- devtool: "source-map",
243
- devServer,
244
- };
245
- }
246
-
247
- export default buildConfig;
@@ -1,27 +0,0 @@
1
- ## Generative AI Template
2
-
3
- This template captures best practices for improving user experience in your application.
4
-
5
- ### State Management
6
-
7
- In this template, we've set up state management using `React Context`. It's just one way to do it, not a strict rule. If your app gets more complicated, you might want to check out other options like `Redux` or `MobX`.
8
-
9
- ### Routing
10
-
11
- As your application evolves, you may find the need for routing to manage multiple views or pages. In this template, we've integrated React Router to illustrate how routing can facilitate seamless navigation between various components.
12
-
13
- ### Loading state
14
-
15
- Creating AI assets can be time-consuming, often resulting in users facing extended waiting periods. Incorporating placeholders, a loading bar, and a message indicating the expected wait time can help alleviate the perceived wait time. We highly encourage adopting this approach and customizing it to suit your specific use case.
16
-
17
- ### Obscenity filter
18
-
19
- In this template, we've included a basic obscenity filter to stop users from creating offensive or harmful content. However, you might need additional filters or checks after content generation to ensure it meets your standards.
20
-
21
- ### Backend
22
-
23
- This template includes a simple Express server as a sample backend. Please note that this server is not production-ready, and we advise using it solely for instructional purposes to demonstrate API calls. If you require authentication for your app, we recommend looking at the authentication example provided in the [starter kit](https://github.com/canva-sdks/canva-apps-sdk-starter-kit).
24
-
25
- ### Thumbnails
26
-
27
- This template illustrates how your API could return thumbnails and demonstrates their usage within the code. Thumbnails play a crucial role in optimizing image uploads and previews by providing quick visual feedback and reducing load times.
@@ -1,232 +0,0 @@
1
- /**
2
- * DISCLAIMER:
3
- * This file contains a demonstration of how to implement a simple API with Express.js for educational purposes only.
4
- * It is NOT SUITABLE for use in a production environment. It lacks many essential features such as error handling,
5
- * input validation, authentication, and security measures. Additionally, the in-memory job queue is for illustrative
6
- * purposes only and is not efficient or scalable. For production use, consider using robust libraries and frameworks,
7
- * implementing proper error handling, security measures, and using appropriate database and job queue solutions.
8
- * Use this code as a learning resource, but do not deploy it in a real backend without significant modifications
9
- * to ensure reliability, security, and scalability.
10
- */
11
- import express from "express";
12
-
13
- interface ImageResponse {
14
- fullsize: { width: number; height: number; url: string };
15
- thumbnail: { width: number; height: number; url: string };
16
- }
17
-
18
- // Array of placeholder image URLs.
19
- // In a real-world scenario, these URLs would point to dynamically generated images.
20
- const imageUrls: ImageResponse[] = [
21
- {
22
- fullsize: {
23
- width: 1280,
24
- height: 853,
25
- url: "https://images.pexels.com/photos/1145720/pexels-photo-1145720.jpeg?auto=compress&cs=tinysrgb&w=1280&h=853&dpr=2",
26
- },
27
- thumbnail: {
28
- width: 640,
29
- height: 427,
30
- url: "https://images.pexels.com/photos/1145720/pexels-photo-1145720.jpeg?auto=compress&cs=tinysrgb&w=640&h=427&dpr=2",
31
- },
32
- },
33
- {
34
- fullsize: {
35
- width: 1280,
36
- height: 853,
37
- url: "https://images.pexels.com/photos/4010108/pexels-photo-4010108.jpeg?auto=compress&cs=tinysrgb&w=1280&h=863&dpr=2",
38
- },
39
- thumbnail: {
40
- width: 640,
41
- height: 427,
42
- url: "https://images.pexels.com/photos/4010108/pexels-photo-4010108.jpeg?auto=compress&cs=tinysrgb&w=640&h=427&dpr=2",
43
- },
44
- },
45
- {
46
- fullsize: {
47
- width: 1280,
48
- height: 853,
49
- url: "https://images.pexels.com/photos/1327496/pexels-photo-1327496.jpeg?auto=compress&cs=tinysrgb&w=1280&h=853&dpr=2",
50
- },
51
- thumbnail: {
52
- width: 640,
53
- height: 427,
54
- url: "https://images.pexels.com/photos/1327496/pexels-photo-1327496.jpeg?auto=compress&cs=tinysrgb&w=640&h=427&dpr=2",
55
- },
56
- },
57
- {
58
- fullsize: {
59
- width: 1280,
60
- height: 853,
61
- url: "https://images.pexels.com/photos/4693135/pexels-photo-4693135.jpeg?auto=compress&cs=tinysrgb&w=1280&h=853&dpr=2",
62
- },
63
- thumbnail: {
64
- width: 640,
65
- height: 427,
66
- url: "https://images.pexels.com/photos/4693135/pexels-photo-4693135.jpeg?auto=compress&cs=tinysrgb&w=640&h=427&dpr=2",
67
- },
68
- },
69
- ];
70
-
71
- export const createImageRouter = () => {
72
- const enum Routes {
73
- CREDITS = "/api/credits",
74
- PURCHASE_CREDITS = "/api/purchase-credits",
75
- QUEUE_IMAGE_GENERATION = "/api/queue-image-generation",
76
- JOB_STATUS = "/api/job-status",
77
- CANCEL_JOB = "/api/job-status/cancel",
78
- }
79
-
80
- const router = express.Router();
81
- const jobQueue: {
82
- jobId: string;
83
- prompt: string;
84
- timeoutId: NodeJS.Timeout;
85
- }[] = [];
86
- const completedJobs: Record<string, ImageResponse[]> = {};
87
- const cancelledJobs: { jobId: string }[] = [];
88
-
89
- // Initial credit allocation for users, which decreases with each use.
90
- // Users receive 10 credits initially and can purchase additional credits in bundles.
91
- let credits = 10;
92
- const CREDITS_IN_BUNDLE = 10;
93
-
94
- /**
95
- * GET endpoint to retrieve user credits.
96
- * Returns the current number of credits available to the user.
97
- */
98
- router.get(Routes.CREDITS, async (req, res) => {
99
- res.status(200).send({
100
- credits,
101
- });
102
- });
103
-
104
- /**
105
- * POST endpoint to purchase credits.
106
- * Increments the user's credits by the number of credits in a bundle.
107
- * This endpoint should be backed by proper input validation to prevent misuse.
108
- */
109
- router.post(Routes.PURCHASE_CREDITS, async (req, res) => {
110
- credits += CREDITS_IN_BUNDLE;
111
- res.status(200).send({
112
- credits,
113
- });
114
- });
115
-
116
- /**
117
- * GET endpoint to generate images based on a prompt.
118
- * Generates images based on the provided prompt and adds a job to the processing queue.
119
- * If there are not enough credits, it returns a 403 error.
120
- * If the prompt parameter is missing, it returns a 400 error.
121
- * Once the job is added to the queue, it returns a jobId that can be used to check the job status.
122
- * Note: The job processing time is simulated to be 5 seconds.
123
- */
124
- router.get(Routes.QUEUE_IMAGE_GENERATION, async (req, res) => {
125
- if (credits <= 0) {
126
- return res
127
- .status(403)
128
- .send("Not enough credits required to generate images.");
129
- }
130
-
131
- const prompt = req.query.prompt as string;
132
- if (!prompt) {
133
- return res.status(400).send("Missing prompt parameter.");
134
- }
135
-
136
- const jobId = generateJobId();
137
-
138
- const timeoutId = setTimeout(() => {
139
- const index = jobQueue.findIndex((job) => job.jobId === jobId);
140
- if (index !== -1) {
141
- jobQueue.splice(index, 1);
142
- completedJobs[jobId] = imageUrls.map((image) => {
143
- return { ...image, label: prompt };
144
- });
145
-
146
- // Reduce credits by 1 when images are successfully generated
147
- credits -= 1;
148
- }
149
- }, 5000); // Simulating 5 seconds processing time
150
-
151
- // Add the job to the jobQueue along with the timeoutId
152
- jobQueue.push({ jobId, prompt, timeoutId });
153
-
154
- return res.status(200).send({
155
- jobId,
156
- });
157
- });
158
-
159
- /**
160
- * GET endpoint to check the status of a job.
161
- * Retrieves the status of a job identified by its jobId parameter.
162
- * If the job is completed, it returns the images generated by the job.
163
- * If the job is still in the processing queue, it returns "processing".
164
- * If the job has been cancelled, it returns "cancelled".
165
- * If the job is not found, it returns a 404 error.
166
- */
167
- router.get(Routes.JOB_STATUS, async (req, res) => {
168
- const jobId = req.query.jobId as string;
169
-
170
- if (!jobId) {
171
- return res.status(400).send("Missing jobId parameter.");
172
- }
173
-
174
- if (completedJobs[jobId]) {
175
- return res.status(200).send({
176
- status: "completed",
177
- images: completedJobs[jobId],
178
- credits,
179
- });
180
- }
181
-
182
- if (jobQueue.some((job) => job.jobId === jobId)) {
183
- return res.status(200).send({
184
- status: "processing",
185
- });
186
- }
187
-
188
- if (cancelledJobs.some((job) => job.jobId === jobId)) {
189
- return res.status(200).send({
190
- status: "cancelled",
191
- });
192
- }
193
-
194
- return res.status(404).send("Job not found.");
195
- });
196
-
197
- /**
198
- * POST endpoint to cancel a job.
199
- * Cancels a job identified by its jobId parameter.
200
- * If the job is found and successfully cancelled, it removes the job from the processing queue and adds it to the cancelled jobs array.
201
- * If the job is not found, it returns a 404 error.
202
- */
203
- router.post(Routes.CANCEL_JOB, async (req, res) => {
204
- const jobId = req.query.jobId as string;
205
-
206
- if (!jobId) {
207
- return res.status(400).send("Missing jobId parameter.");
208
- }
209
-
210
- const index = jobQueue.findIndex((job) => job.jobId === jobId);
211
- const job = jobQueue[index];
212
- if (index !== -1 && job) {
213
- cancelledJobs.push({ jobId });
214
- // If the job is found, remove it from the jobQueue
215
- jobQueue.splice(index, 1);
216
- // Also clear the timeout associated with this job if it exists
217
- clearTimeout(job.timeoutId);
218
- return res.status(200).send("Job successfully cancelled.");
219
- }
220
-
221
- return res.status(404).send("Job not found.");
222
- });
223
-
224
- /**
225
- * Generates a unique job ID.
226
- */
227
- function generateJobId(): string {
228
- return Math.random().toString(36).substring(2, 15);
229
- }
230
-
231
- return router;
232
- };
@@ -1,65 +0,0 @@
1
- import { user } from "@canva/app-middleware/express";
2
- import cors from "cors";
3
- import express from "express";
4
- import { createBaseServer } from "../utils/backend/base_backend/create";
5
- import { createImageRouter } from "./routers/image";
6
-
7
- async function main() {
8
- // TODO: Set the CANVA_APP_ID environment variable in the project's .env file
9
- const APP_ID = process.env.CANVA_APP_ID;
10
-
11
- if (!APP_ID) {
12
- throw new Error(
13
- `The CANVA_APP_ID environment variable is undefined. Set the variable in the project's .env file.`,
14
- );
15
- }
16
-
17
- const router = express.Router();
18
-
19
- /**
20
- * TODO: Configure your CORS Policy
21
- *
22
- * Cross-Origin Resource Sharing
23
- * ([CORS](https://developer.mozilla.org/en-US/docs/Glossary/CORS)) is an
24
- * [HTTP](https://developer.mozilla.org/en-US/docs/Glossary/HTTP)-header based
25
- * mechanism that allows a server to indicate any
26
- * [origins](https://developer.mozilla.org/en-US/docs/Glossary/Origin)
27
- * (domain, scheme, or port) other than its own from which a browser should
28
- * permit loading resources.
29
- *
30
- * A basic CORS configuration would include the origin of your app in the
31
- * following example:
32
- * const corsOptions = {
33
- * origin: 'https://app-abcdefg.canva-apps.com',
34
- * optionsSuccessStatus: 200
35
- * }
36
- *
37
- * The origin of your app is https://app-${APP_ID}.canva-apps.com, and note
38
- * that the APP_ID should to be converted to lowercase.
39
- *
40
- * https://www.npmjs.com/package/cors#configuring-cors
41
- *
42
- * You may need to include multiple permissible origins, or dynamic origins
43
- * based on the environment in which the server is running. Further
44
- * information can be found
45
- * [here](https://www.npmjs.com/package/cors#configuring-cors-w-dynamic-origin).
46
- */
47
- router.use(cors());
48
-
49
- /**
50
- * Initialize JWT middleware to verify Canva user tokens
51
- * This middleware validates tokens sent from the frontend and extracts user information
52
- */
53
- router.use(user.verifyToken({ appId: APP_ID }));
54
-
55
- /**
56
- * Add routes for image generation.
57
- */
58
- const imageRouter = createImageRouter();
59
- router.use(imageRouter);
60
-
61
- const server = createBaseServer(router);
62
- server.start(process.env.CANVA_BACKEND_PORT);
63
- }
64
-
65
- main();
@@ -1,25 +0,0 @@
1
- {
2
- "$schema": "https://www.canva.dev/schemas/app/v1/manifest-schema.json",
3
- "manifest_schema_version": 1,
4
- "runtime": {
5
- "permissions": [
6
- {
7
- "name": "canva:asset:private:write",
8
- "type": "mandatory"
9
- },
10
- {
11
- "name": "canva:design:content:read",
12
- "type": "mandatory"
13
- },
14
- {
15
- "name": "canva:design:content:write",
16
- "type": "mandatory"
17
- }
18
- ]
19
- },
20
- "intent": {
21
- "design_editor": {
22
- "enrolled": true
23
- }
24
- }
25
- }