@upstart.gg/sdk 0.0.104 → 0.0.105

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 (225) hide show
  1. package/dist/shared/ajv.js +1 -1
  2. package/dist/shared/attributes.js +1 -1
  3. package/dist/shared/bricks/manifests/accordion.manifest.js +1 -1
  4. package/dist/shared/bricks/manifests/all-manifests.js +1 -1
  5. package/dist/shared/bricks/manifests/button.manifest.js +1 -1
  6. package/dist/shared/bricks/manifests/card.manifest.js +1 -1
  7. package/dist/shared/bricks/manifests/carousel.manifest.js +1 -1
  8. package/dist/shared/bricks/manifests/container.manifest.js +1 -1
  9. package/dist/shared/bricks/manifests/divider.manifest.js +1 -1
  10. package/dist/shared/bricks/manifests/footer.manifest.js +1 -1
  11. package/dist/shared/bricks/manifests/form.manifest.js +1 -1
  12. package/dist/shared/bricks/manifests/hero.manifest.js +1 -1
  13. package/dist/shared/bricks/manifests/icon.manifest.js +1 -1
  14. package/dist/shared/bricks/manifests/image.manifest.js +1 -1
  15. package/dist/shared/bricks/manifests/images-gallery.manifest.js +1 -1
  16. package/dist/shared/bricks/manifests/map.manifest.js +1 -1
  17. package/dist/shared/bricks/manifests/navbar.manifest.js +1 -1
  18. package/dist/shared/bricks/manifests/sidebar.manifest.js +1 -1
  19. package/dist/shared/bricks/manifests/social-links.manifest.js +1 -1
  20. package/dist/shared/bricks/manifests/testimonials.manifest.js +1 -1
  21. package/dist/shared/bricks/manifests/text.manifest.js +1 -1
  22. package/dist/shared/bricks/manifests/timeline.manifest.js +1 -1
  23. package/dist/shared/bricks/manifests/video.manifest.js +1 -1
  24. package/dist/shared/bricks.js +1 -1
  25. package/dist/shared/chunk-245ZLDVB.js +3 -0
  26. package/dist/shared/{chunk-QALNFBY7.js → chunk-2D6ZEIDX.js} +1 -1
  27. package/dist/shared/{chunk-CW5FWW5W.js → chunk-4BLCQRF5.js} +1 -1
  28. package/dist/shared/{chunk-TEPFFY32.js → chunk-5MGYT6FI.js} +1 -1
  29. package/dist/shared/{chunk-HQQYIKTE.js → chunk-CS5N4UQU.js} +1 -1
  30. package/dist/shared/{chunk-5IEGQ7YT.js → chunk-FRWB3BF3.js} +1 -1
  31. package/dist/shared/{chunk-I5S2MPPR.js → chunk-HYXCHFS5.js} +1 -1
  32. package/dist/shared/{chunk-TCZBR3ZU.js → chunk-IKSRCRZZ.js} +1 -1
  33. package/dist/shared/{chunk-LJQXN32B.js → chunk-ISF6EBPX.js} +1 -1
  34. package/dist/shared/{chunk-UK6H3FDX.js → chunk-JDBVMPSI.js} +1 -1
  35. package/dist/shared/{chunk-UISES3HA.js → chunk-M3Z3XDUC.js} +1 -1
  36. package/dist/shared/{chunk-22KRCQK5.js → chunk-MWP5IU33.js} +1 -1
  37. package/dist/shared/{chunk-7L3UOEMR.js → chunk-NCSLTHKN.js} +1 -1
  38. package/dist/shared/{chunk-O22VV7YR.js → chunk-OWON727Q.js} +1 -1
  39. package/dist/shared/{chunk-CEXHD4BO.js → chunk-P5RC56CL.js} +2 -2
  40. package/dist/shared/{chunk-ZFTWLZ3G.js → chunk-QI3BZDXL.js} +1 -1
  41. package/dist/shared/{chunk-F6MLW6DO.js → chunk-RDSK2G6V.js} +1 -1
  42. package/dist/shared/{chunk-I43NIQ2K.js → chunk-RXRPFXDF.js} +1 -1
  43. package/dist/shared/{chunk-POYVTV5F.js → chunk-VFGMOA4M.js} +1 -1
  44. package/dist/shared/{chunk-VDHLON5R.js → chunk-VPD626ML.js} +1 -1
  45. package/dist/shared/{chunk-WCNVFVDK.js → chunk-VROOAFFW.js} +1 -1
  46. package/dist/shared/{chunk-E2EBTWJI.js → chunk-WB4PGCZQ.js} +1 -1
  47. package/dist/shared/chunk-X2RJL33B.js +3 -0
  48. package/dist/shared/{chunk-OQIFFJ7I.js → chunk-X4ZBUWUL.js} +1 -1
  49. package/dist/shared/datasources/external/rss/fetcher.d.ts +1 -1
  50. package/dist/shared/datasources/external/rss/fetcher.d.ts.map +1 -1
  51. package/dist/shared/page.js +1 -1
  52. package/dist/shared/site.js +1 -1
  53. package/package.json +5 -9
  54. package/dist/shared/chunk-GPKRRX3D.js +0 -3
  55. package/dist/shared/chunk-JFDOR3UH.js +0 -3
  56. package/src/node/cli/api.ts +0 -101
  57. package/src/node/cli/commands/cmd-build.ts +0 -64
  58. package/src/node/cli/commands/login/cmd-login.ts +0 -111
  59. package/src/node/cli/commands/logout/cmd-logout.ts +0 -11
  60. package/src/node/cli/commands/publish/cmd-publish.ts +0 -135
  61. package/src/node/cli/commands/publish/parse-gitignore.ts +0 -278
  62. package/src/node/cli/commands/publish/uploader.ts +0 -333
  63. package/src/node/cli/constants.ts +0 -14
  64. package/src/node/cli/is-logged-in.ts +0 -28
  65. package/src/node/cli/program.ts +0 -77
  66. package/src/node/cli/store.ts +0 -64
  67. package/src/node/cli/tests/api.test.ts +0 -161
  68. package/src/node/cli/types.ts +0 -34
  69. package/src/node/cli/utils.ts +0 -20
  70. package/src/node/shared/config.ts +0 -69
  71. package/src/node/shared/logger.ts +0 -44
  72. package/src/shared/ajv.ts +0 -103
  73. package/src/shared/analytics/init.ts +0 -14
  74. package/src/shared/analytics/track.ts +0 -21
  75. package/src/shared/analytics/types.ts +0 -13
  76. package/src/shared/attributes.ts +0 -211
  77. package/src/shared/brick-manifest.ts +0 -110
  78. package/src/shared/bricks/manifests/accordion.manifest.ts +0 -179
  79. package/src/shared/bricks/manifests/all-manifests.ts +0 -92
  80. package/src/shared/bricks/manifests/button.manifest.ts +0 -145
  81. package/src/shared/bricks/manifests/card.manifest.ts +0 -269
  82. package/src/shared/bricks/manifests/carousel.manifest.ts +0 -106
  83. package/src/shared/bricks/manifests/container.manifest.ts +0 -357
  84. package/src/shared/bricks/manifests/divider.manifest.ts +0 -121
  85. package/src/shared/bricks/manifests/footer.manifest.ts +0 -487
  86. package/src/shared/bricks/manifests/form.manifest.ts +0 -112
  87. package/src/shared/bricks/manifests/hero.manifest.ts +0 -132
  88. package/src/shared/bricks/manifests/icon.manifest.ts +0 -130
  89. package/src/shared/bricks/manifests/image.manifest.ts +0 -203
  90. package/src/shared/bricks/manifests/images-gallery.manifest.ts +0 -227
  91. package/src/shared/bricks/manifests/map.manifest.ts +0 -75
  92. package/src/shared/bricks/manifests/navbar.manifest.ts +0 -344
  93. package/src/shared/bricks/manifests/sidebar.manifest.ts +0 -90
  94. package/src/shared/bricks/manifests/social-links.manifest.ts +0 -370
  95. package/src/shared/bricks/manifests/testimonials.manifest.ts +0 -397
  96. package/src/shared/bricks/manifests/tests/header.manifest.test.ts +0 -10
  97. package/src/shared/bricks/manifests/text.manifest.ts +0 -164
  98. package/src/shared/bricks/manifests/timeline.manifest.ts +0 -456
  99. package/src/shared/bricks/manifests/video.manifest.ts +0 -59
  100. package/src/shared/bricks/props/_style-presets.ts +0 -352
  101. package/src/shared/bricks/props/align.ts +0 -59
  102. package/src/shared/bricks/props/background.ts +0 -118
  103. package/src/shared/bricks/props/boolean.ts +0 -11
  104. package/src/shared/bricks/props/border.ts +0 -84
  105. package/src/shared/bricks/props/color.ts +0 -24
  106. package/src/shared/bricks/props/common.ts +0 -37
  107. package/src/shared/bricks/props/container.ts +0 -356
  108. package/src/shared/bricks/props/css-length.ts +0 -25
  109. package/src/shared/bricks/props/datasource.ts +0 -60
  110. package/src/shared/bricks/props/date.ts +0 -24
  111. package/src/shared/bricks/props/effects.ts +0 -123
  112. package/src/shared/bricks/props/enum.ts +0 -42
  113. package/src/shared/bricks/props/file.ts +0 -12
  114. package/src/shared/bricks/props/geolocation.ts +0 -30
  115. package/src/shared/bricks/props/helpers.ts +0 -101
  116. package/src/shared/bricks/props/image.ts +0 -90
  117. package/src/shared/bricks/props/number.ts +0 -16
  118. package/src/shared/bricks/props/padding.ts +0 -21
  119. package/src/shared/bricks/props/position.ts +0 -27
  120. package/src/shared/bricks/props/preset.ts +0 -136
  121. package/src/shared/bricks/props/string.ts +0 -56
  122. package/src/shared/bricks/props/tests/align.test.ts +0 -37
  123. package/src/shared/bricks/props/tests/background.test.ts +0 -102
  124. package/src/shared/bricks/props/tests/border.test.ts +0 -38
  125. package/src/shared/bricks/props/tests/effects.test.ts +0 -37
  126. package/src/shared/bricks/props/tests/helpers.test.ts +0 -133
  127. package/src/shared/bricks/props/tests/image.test.ts +0 -71
  128. package/src/shared/bricks/props/tests/padding.ts +0 -12
  129. package/src/shared/bricks/props/tests/string.test.ts +0 -79
  130. package/src/shared/bricks/props/text.ts +0 -66
  131. package/src/shared/bricks/props/types.ts +0 -57
  132. package/src/shared/bricks.ts +0 -232
  133. package/src/shared/context.ts +0 -39
  134. package/src/shared/datarecords/external/airtable/handler.ts +0 -21
  135. package/src/shared/datarecords/external/airtable/options.ts +0 -22
  136. package/src/shared/datarecords/external/generic-webhook/handler.ts +0 -10
  137. package/src/shared/datarecords/external/generic-webhook/options.ts +0 -13
  138. package/src/shared/datarecords/external/google/oauth/config.ts +0 -30
  139. package/src/shared/datarecords/external/google/sheets/handler.ts +0 -26
  140. package/src/shared/datarecords/external/google/sheets/options.ts +0 -9
  141. package/src/shared/datarecords/types.ts +0 -120
  142. package/src/shared/datarecords.ts +0 -5
  143. package/src/shared/datasources/README.md +0 -3
  144. package/src/shared/datasources/external/facebook/posts/fetcher.ts +0 -52
  145. package/src/shared/datasources/external/facebook/posts/sample.ts +0 -35
  146. package/src/shared/datasources/external/facebook/posts/schema.ts +0 -33
  147. package/src/shared/datasources/external/facebook/posts/tests/fetcher.test.ts +0 -73
  148. package/src/shared/datasources/external/http-json/fetcher.ts +0 -28
  149. package/src/shared/datasources/external/http-json/options.ts +0 -12
  150. package/src/shared/datasources/external/http-json/schema.ts +0 -6
  151. package/src/shared/datasources/external/http-json/tests/fetcher.test.ts +0 -70
  152. package/src/shared/datasources/external/instagram/feed/fetcher.ts +0 -33
  153. package/src/shared/datasources/external/instagram/feed/sample.ts +0 -22
  154. package/src/shared/datasources/external/instagram/feed/schema.ts +0 -23
  155. package/src/shared/datasources/external/instagram/feed/tests/fetcher.test.ts +0 -64
  156. package/src/shared/datasources/external/mastodon/account/fetcher.ts +0 -24
  157. package/src/shared/datasources/external/mastodon/account/sample.ts +0 -33
  158. package/src/shared/datasources/external/mastodon/account/schema.ts +0 -45
  159. package/src/shared/datasources/external/mastodon/account/tests/fetcher.test.ts +0 -47
  160. package/src/shared/datasources/external/mastodon/options.ts +0 -11
  161. package/src/shared/datasources/external/mastodon/status/fetcher.ts +0 -35
  162. package/src/shared/datasources/external/mastodon/status/sample.array.ts +0 -59
  163. package/src/shared/datasources/external/mastodon/status/sample.single.ts +0 -55
  164. package/src/shared/datasources/external/mastodon/status/schema.ts +0 -130
  165. package/src/shared/datasources/external/mastodon/status/tests/fetcher.test.ts +0 -74
  166. package/src/shared/datasources/external/meta/oauth/config.ts +0 -16
  167. package/src/shared/datasources/external/meta/options.ts +0 -11
  168. package/src/shared/datasources/external/rss/fetcher.ts +0 -29
  169. package/src/shared/datasources/external/rss/options.ts +0 -11
  170. package/src/shared/datasources/external/rss/sample.ts +0 -22
  171. package/src/shared/datasources/external/rss/schema.ts +0 -42
  172. package/src/shared/datasources/external/threads/media/fetcher.ts +0 -53
  173. package/src/shared/datasources/external/threads/media/sample.ts +0 -44
  174. package/src/shared/datasources/external/threads/media/schema.ts +0 -37
  175. package/src/shared/datasources/external/tiktok/oauth/config.ts +0 -17
  176. package/src/shared/datasources/external/tiktok/video/fetcher.ts +0 -39
  177. package/src/shared/datasources/external/tiktok/video/options.ts +0 -12
  178. package/src/shared/datasources/external/tiktok/video/sample.ts +0 -26
  179. package/src/shared/datasources/external/tiktok/video/schema.ts +0 -27
  180. package/src/shared/datasources/external/youtube/list/fetcher.ts +0 -37
  181. package/src/shared/datasources/external/youtube/list/options.ts +0 -15
  182. package/src/shared/datasources/external/youtube/list/sample.ts +0 -33
  183. package/src/shared/datasources/external/youtube/list/schema.ts +0 -38
  184. package/src/shared/datasources/external/youtube/oauth/config.ts +0 -15
  185. package/src/shared/datasources/fetcher.ts +0 -17
  186. package/src/shared/datasources/internal/blog/schema.ts +0 -69
  187. package/src/shared/datasources/internal/changelog/schema.ts +0 -48
  188. package/src/shared/datasources/internal/contact-info/schema.ts +0 -20
  189. package/src/shared/datasources/internal/cv/schema.ts +0 -217
  190. package/src/shared/datasources/internal/faq/schema.ts +0 -27
  191. package/src/shared/datasources/internal/job-board/schema.ts +0 -228
  192. package/src/shared/datasources/internal/links/schema.ts +0 -15
  193. package/src/shared/datasources/internal/recipes/schema.ts +0 -42
  194. package/src/shared/datasources/internal/restaurant/schema.ts +0 -225
  195. package/src/shared/datasources/provider-options.ts +0 -7
  196. package/src/shared/datasources/samples.ts +0 -26
  197. package/src/shared/datasources/schemas.ts +0 -45
  198. package/src/shared/datasources/types.ts +0 -276
  199. package/src/shared/datasources/utils.ts +0 -16
  200. package/src/shared/datasources.ts +0 -42
  201. package/src/shared/env.ts +0 -23
  202. package/src/shared/errors.ts +0 -1
  203. package/src/shared/images.ts +0 -44
  204. package/src/shared/index.ts +0 -3
  205. package/src/shared/layout-constants.ts +0 -25
  206. package/src/shared/manifest.ts +0 -50
  207. package/src/shared/oauth.ts +0 -16
  208. package/src/shared/page.ts +0 -61
  209. package/src/shared/prompt.ts +0 -9
  210. package/src/shared/responsive.ts +0 -5
  211. package/src/shared/site.ts +0 -97
  212. package/src/shared/sitemap.ts +0 -66
  213. package/src/shared/social-icons.ts +0 -307
  214. package/src/shared/tests/attributes.test.ts +0 -37
  215. package/src/shared/theme.ts +0 -245
  216. package/src/shared/themes/README.md +0 -34
  217. package/src/shared/themes/color-system.ts +0 -127
  218. package/src/shared/utils/canvas-data-uri.ts +0 -2
  219. package/src/shared/utils/invariant.ts +0 -25
  220. package/src/shared/utils/json-date.ts +0 -8
  221. package/src/shared/utils/merge.ts +0 -12
  222. package/src/shared/utils/object-hash.ts +0 -7
  223. package/src/shared/utils/schema.ts +0 -35
  224. package/src/shared/utils/try-catch.ts +0 -12
  225. package/src/shared/utils/typed-ref.ts +0 -41
@@ -1,333 +0,0 @@
1
- import path from "node:path";
2
- import fs from "node:fs";
3
- import crypto from "node:crypto";
4
- import fg from "fast-glob";
5
- import { API_BASE_URL, DEFAULT_UPLOAD_MAX_CONCURRENCY } from "../../constants";
6
- import http from "node:http";
7
- import https from "node:https";
8
- import { pipeline } from "node:stream";
9
- import type { GenericApiError } from "../../types";
10
- import { logger } from "~/node/shared/logger";
11
- import chalk from "chalk";
12
- import FormData from "form-data";
13
- import parseGitIgnore from "./parse-gitignore";
14
- import PQueue from "p-queue";
15
- import ora from "ora";
16
- import { customAlphabet } from "nanoid";
17
- import { post } from "../../api";
18
-
19
- interface UploadStats {
20
- fileName: string;
21
- fileSize: number;
22
- uploadDuration: number;
23
- statusCode: number;
24
- serverResponse: string;
25
- }
26
-
27
- class UploadError extends Error implements GenericApiError {
28
- constructor(
29
- public error: string,
30
- public error_description: string,
31
- public filename: string,
32
- ) {
33
- super(`${error}: ${error_description}`);
34
- this.name = "UploadError";
35
- }
36
- }
37
-
38
- interface UploadConfig {
39
- maxDataSize: number;
40
- retryAttempts: number;
41
- retryDelay: number;
42
- }
43
-
44
- const defaultConfig: UploadConfig = {
45
- maxDataSize: 12 * 1024 * 1024, // 12MB
46
- retryAttempts: 2,
47
- retryDelay: 1000,
48
- };
49
-
50
- const agent = new http.Agent({
51
- keepAlive: true,
52
- maxSockets: 10,
53
- keepAliveMsecs: 3000, // Keep connections open for 3 seconds
54
- });
55
-
56
- async function discoverFiles(templateDir: string): Promise<string[]> {
57
- const gitignore = path.join(templateDir, ".gitignore");
58
- const gitignoreExists = fs.existsSync(gitignore);
59
- const gitignored = gitignoreExists ? parseGitIgnore(fs.readFileSync(gitignore, "utf-8")).patterns : [];
60
-
61
- return fg("**/*", {
62
- cwd: templateDir,
63
- onlyFiles: true,
64
- dot: true,
65
- absolute: true,
66
- ignore: [
67
- "node_modules/**",
68
- ".cache/**",
69
- "**/.DS_Store",
70
- ".gitignore",
71
- ".env",
72
- ".env.*",
73
- ".git/**",
74
- "dist/**",
75
- ...gitignored,
76
- ],
77
- });
78
- }
79
-
80
- async function uploadFile(
81
- filePath: string,
82
- relativePath: string,
83
- uploadUrl: string,
84
- templateId: string,
85
- spinner: ReturnType<typeof ora>,
86
- token: string,
87
- config: UploadConfig,
88
- ): Promise<UploadStats> {
89
- const uploadWithRetry = async (attempt: number): Promise<UploadStats> => {
90
- try {
91
- return await performUpload(filePath, relativePath, uploadUrl, templateId, spinner, token, config);
92
- } catch (error) {
93
- if (attempt < config.retryAttempts) {
94
- logger.warn(`Retrying upload for ${relativePath} (attempt ${attempt + 1})`);
95
- await new Promise((resolve) => setTimeout(resolve, config.retryDelay));
96
- return uploadWithRetry(attempt + 1);
97
- }
98
- throw error;
99
- }
100
- };
101
-
102
- return uploadWithRetry(0);
103
- }
104
-
105
- async function performUpload(
106
- filePath: string,
107
- relativePath: string,
108
- uploadUrl: string,
109
- templateId: string,
110
- spinner: ReturnType<typeof ora>,
111
- token: string,
112
- config: UploadConfig,
113
- ): Promise<UploadStats> {
114
- return new Promise<UploadStats>((resolve, reject) => {
115
- const stats = fs.statSync(filePath);
116
- const fileName = path.basename(filePath);
117
- const startTime = Date.now();
118
-
119
- const form = new FormData({
120
- maxDataSize: config.maxDataSize,
121
- });
122
- const fileStream = fs.createReadStream(filePath);
123
- let totalUploaded = 0;
124
-
125
- const updateProgress = (chunk: Buffer | string) => {
126
- totalUploaded += chunk.length;
127
- };
128
-
129
- fileStream.on("data", updateProgress);
130
- form.append("file", fileStream, {
131
- filename: fileName,
132
- knownLength: stats.size,
133
- });
134
-
135
- const options: https.RequestOptions = {
136
- agent,
137
- method: "POST",
138
- headers: {
139
- authorization: `Bearer ${token}`,
140
- "x-upstart-template-file-path": relativePath,
141
- "x-upstart-template-id": templateId,
142
- ...form.getHeaders(),
143
- },
144
- };
145
-
146
- const protocol = uploadUrl.startsWith("https") ? https : http;
147
- const req = protocol.request(uploadUrl, options, (res) => {
148
- let responseBody = "";
149
- res.on("data", (chunk) => {
150
- responseBody += chunk;
151
- });
152
-
153
- res.on("end", () => {
154
- const endTime = Date.now();
155
- const uploadStats: UploadStats = {
156
- fileName,
157
- fileSize: stats.size,
158
- uploadDuration: endTime - startTime,
159
- statusCode: res.statusCode ?? 0,
160
- serverResponse: responseBody,
161
- };
162
-
163
- if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
164
- spinner.text = `Uploaded ${relativePath}`;
165
- resolve(uploadStats);
166
- } else {
167
- let errorObject: UploadError;
168
- if (res.headers["content-type"]?.startsWith("application/json")) {
169
- const parsedError = JSON.parse(responseBody);
170
- errorObject = new UploadError(
171
- parsedError.error || "unknown_error",
172
- parsedError.error_description || "Unknown error occurred",
173
- relativePath,
174
- );
175
- } else {
176
- errorObject = new UploadError(
177
- "unknown_error",
178
- responseBody || "Unknown error occurred",
179
- relativePath,
180
- );
181
- }
182
-
183
- spinner.fail(
184
- `Failed to upload ${relativePath} [${errorObject.error}] ${errorObject.error_description}`,
185
- );
186
-
187
- reject(errorObject);
188
- }
189
- });
190
- });
191
-
192
- req.on("error", (error) => {
193
- reject(new UploadError("request_error", error.message, relativePath));
194
- });
195
-
196
- pipeline(form, req, (err) => {
197
- if (err) {
198
- reject(
199
- new UploadError("pipeline_error", `Pipeline failed for ${fileName}: ${err.message}`, relativePath),
200
- );
201
- }
202
- });
203
- });
204
- }
205
-
206
- function reportUploadStatistics(
207
- templateId: string,
208
- uploadedFiles: UploadStats[],
209
- failedFiles: PromiseRejectedResult[],
210
- ) {
211
- if (failedFiles.length > 0) {
212
- logger.error(chalk.red(`Failed to upload ${failedFiles.length} files:`));
213
- failedFiles.forEach((failure) => {
214
- const error = failure.reason as UploadError;
215
- logger.error(`- ${error.filename}: ${error.error_description} (${error.error})`);
216
- });
217
- } else {
218
- logger.info(
219
- chalk.green(`Uploaded ${uploadedFiles.length} files successfully for template ${templateId}.\n`),
220
- );
221
- }
222
- }
223
-
224
- export async function uploadTemplate(
225
- templateId: string,
226
- templateDir: string,
227
- token: string,
228
- dryRun = false,
229
- config: Partial<UploadConfig> = {},
230
- ) {
231
- const fullConfig = { ...defaultConfig, ...config };
232
- const queue = new PQueue({ concurrency: DEFAULT_UPLOAD_MAX_CONCURRENCY });
233
- const files = await discoverFiles(templateDir);
234
- const filesCount = files.length;
235
- const signatures: Record<string, string> = {};
236
- // generate a upload id
237
- const uploadId = generateUploadId();
238
- let completedUploads = 0;
239
-
240
- // compute signatures
241
- for (const file of files) {
242
- const relativePath = path.relative(templateDir, file);
243
- const md5 = await sha1sum(file);
244
- signatures[relativePath] = md5;
245
- }
246
-
247
- if (dryRun) {
248
- logger.info("Dry run mode enabled. Skipping upload.\n");
249
- logger.info("The following files would have been uploaded:");
250
- files.forEach((file) => logger.info(`- ${file}`));
251
- process.exit(0);
252
- }
253
-
254
- const spinner = ora(`Uploading ${filesCount} files...`).start();
255
-
256
- // upload files
257
- const uploadPromises = files.map((file) => {
258
- const fullPath = path.resolve(templateDir, file);
259
- const relativePath = path.relative(templateDir, file);
260
- const url = `${API_BASE_URL}/v1/templates/${templateId}/upload/${uploadId}`;
261
- return queue.add(async () => {
262
- const result = await uploadFile(fullPath, relativePath, url, templateId, spinner, token, fullConfig);
263
- completedUploads++;
264
- return result;
265
- });
266
- });
267
-
268
- const results = await Promise.allSettled(uploadPromises);
269
- spinner.stop();
270
-
271
- const uploadedFiles = results.filter(
272
- (result): result is PromiseFulfilledResult<UploadStats> => result.status === "fulfilled",
273
- );
274
-
275
- const failedFiles = results.filter(
276
- (result): result is PromiseRejectedResult => result.status === "rejected",
277
- );
278
-
279
- reportUploadStatistics(
280
- templateId,
281
- uploadedFiles.map((r) => r.value),
282
- failedFiles,
283
- );
284
-
285
- // finalize upload
286
- if (!failedFiles.length && uploadedFiles.length) {
287
- const finalizeUrl = `${API_BASE_URL}/v1/templates/${templateId}/upload/${uploadId}/finalize`;
288
- const finalizePayload = {
289
- signatures,
290
- };
291
-
292
- const finalizeResponse = await post(finalizeUrl, finalizePayload);
293
-
294
- if (finalizeResponse.isError) {
295
- logger.error(
296
- `Failed to finalize upload: ${finalizeResponse.data.error_description} (${finalizeResponse.data.error})`,
297
- );
298
- return {
299
- filesCount,
300
- uploadedFiles: uploadedFiles.map((r) => r.value),
301
- failedFiles: [],
302
- success: false,
303
- };
304
- }
305
- }
306
-
307
- return {
308
- filesCount,
309
- uploadedFiles: uploadedFiles.map((r) => r.value),
310
- failedFiles,
311
- success: failedFiles.length === 0,
312
- };
313
- }
314
-
315
- async function sha1sum(filePath: string): Promise<string> {
316
- const hash = await crypto.subtle.digest("SHA-1", fs.readFileSync(filePath));
317
- // convert arrayBuffer to hex
318
- return Array.from(new Uint8Array(hash))
319
- .map((b) => b.toString(16).padStart(2, "0"))
320
- .join("");
321
- }
322
-
323
- function generateUploadId(): string {
324
- const randomId = customAlphabet("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 5);
325
- const now = new Date();
326
- const year = now.getUTCFullYear();
327
- const month = (now.getUTCMonth() + 1).toString().padStart(2, "0");
328
- const day = now.getUTCDate().toString().padStart(2, "0");
329
- const hours = now.getUTCHours().toString().padStart(2, "0");
330
- const minutes = now.getUTCMinutes().toString().padStart(2, "0");
331
- const seconds = now.getUTCSeconds().toString().padStart(2, "0");
332
- return `${year}${month}${day}${hours}${minutes}${seconds}_${randomId()}`;
333
- }
@@ -1,14 +0,0 @@
1
- export const CLI_PROJECT_NAME = "upstart-cli";
2
- export const CLI_LOGIN_POLL_INTERVAL = 5000; // seconds
3
- export const CLI_LOGIN_CLIENT_ID =
4
- process.env.PUBLIC_UPSTART_OAUTH_CLIENT_ID ?? "50000000-0000-0000-0000-000000000001";
5
-
6
- export const API_BASE_URL = process.env.PUBLIC_UPSTART_API_BASE_URL ?? "https://api.upstart.gg";
7
- export const EDITOR_BASE_URL = process.env.PUBLIC_UPSTART_EDITOR_BASE_URL ?? "https://upstart.gg";
8
- export const DEFAULT_UPLOAD_MAX_CONCURRENCY = 10;
9
-
10
- export const OAUTH_ENDPOINT_DEVICE_CODE = "oauth/devicecode";
11
- export const OAUTH_ENDPOINT_TOKEN = "oauth/token";
12
- export const OAUTH_ENDPOINT_USER_INFO = "oauth/userinfo";
13
-
14
- export const API_ENDPOINT_REGISTER_TEMPLATE = "v1/templates";
@@ -1,28 +0,0 @@
1
- import { OAUTH_ENDPOINT_USER_INFO } from "./constants";
2
- import { accessStore } from "./store";
3
-
4
- export async function isLoggedIn(checkRemote = false): Promise<boolean> {
5
- const token = accessStore.get("access_token");
6
- const expiration = accessStore.get("expires_at");
7
-
8
- if (!token) {
9
- return false;
10
- }
11
-
12
- if (expiration && expiration < Date.now()) {
13
- console.log("Seems like your token expired...");
14
- return false;
15
- }
16
-
17
- if (!checkRemote) {
18
- return true;
19
- }
20
-
21
- // Check if token is valid
22
-
23
- // import dynamically "./api" to avoid circular dependency
24
- const { get } = await import("./api");
25
- const { isSuccess } = await get(OAUTH_ENDPOINT_USER_INFO);
26
-
27
- return isSuccess;
28
- }
@@ -1,77 +0,0 @@
1
- #!/usr/bin/env node
2
- import { program, type Command, type OptionValues } from "commander";
3
- import type { BuildOptions, CommonOptions } from "./types";
4
- import { publish } from "./commands/publish/cmd-publish";
5
- import { login } from "./commands/login/cmd-login";
6
- import { buildTemplate } from "./commands/cmd-build";
7
- import { createLogger, type Logger } from "../shared/logger";
8
- import { logout } from "./commands/logout/cmd-logout";
9
-
10
- let logger: Logger;
11
-
12
- program
13
- .name("enpage")
14
- .option("-l, --logLevel <level>", `[string] info | warn | error | silent | debug`)
15
- .option("--clearScreen", `[boolean] allow/disable clear screen when logging`)
16
- .option("--dry-run", `[boolean] run command without making changes`)
17
- .hook("preAction", (thisCommand) => {
18
- logger = createLogger(
19
- thisCommand.optsWithGlobals().logLevel,
20
- thisCommand.optsWithGlobals().clearScreen,
21
- true,
22
- );
23
- // for now, disable the form-data warning until they fix it
24
- process.removeAllListeners("warning");
25
- process.on("warning", (warning) => {
26
- if (warning.name === "DeprecationWarning" && warning.message.includes("util.isArray")) {
27
- return;
28
- }
29
- logger.warnOnce(`Warning: ${warning.name} - ${warning.message}`);
30
- });
31
- });
32
-
33
- program
34
- .command("build")
35
- .description("Build template")
36
- .option(
37
- "--ssr [type]",
38
- `Enable server side rendering.
39
- Pass --ssr to generate a SSR-enabled build.
40
- Pass --ssr=local to generate a SSR-enabled build that can be tested locally.`,
41
- Boolean,
42
- false,
43
- )
44
- .option("--no-clean", `Don't clean directory before buidling`)
45
- .action(function (this: Command) {
46
- buildTemplate(getArgsOptionsObject<BuildOptions>(this));
47
- });
48
-
49
- program
50
- .command("publish")
51
- .description("Publish a template to Enpage")
52
- .argument("[directory]", "Directory to publish")
53
- .option("--no-check", `Don't check for required files`)
54
-
55
- .action(function (this: Command) {
56
- publish(getArgsOptionsObject(this));
57
- });
58
-
59
- program
60
- .command("login")
61
- .description("Login to Enpage")
62
- .action(function (this: Command) {
63
- login(getArgsOptionsObject(this));
64
- });
65
-
66
- program
67
- .command("logout")
68
- .description("Logout from Enpage")
69
- .action(function (this: Command) {
70
- logout(getArgsOptionsObject(this));
71
- });
72
-
73
- program.parse();
74
-
75
- function getArgsOptionsObject<O extends OptionValues>(cmd: Command) {
76
- return { options: cmd.optsWithGlobals<CommonOptions & O>(), args: cmd.args, logger };
77
- }
@@ -1,64 +0,0 @@
1
- import type { CredentialsStore } from "./types";
2
- import { CLI_PROJECT_NAME, OAUTH_ENDPOINT_USER_INFO } from "./constants";
3
- import Conf from "conf";
4
- import path from "node:path";
5
- import crypto from "node:crypto";
6
- import fs from "node:fs";
7
- import { fileURLToPath } from "node:url";
8
- import { getPackageManager } from "./utils";
9
- import chalk from "chalk";
10
-
11
- const __dirname = fileURLToPath(new URL(".", import.meta.url));
12
- const key = getKey();
13
- const accessStore = new Conf<CredentialsStore>({
14
- projectName: CLI_PROJECT_NAME,
15
- encryptionKey: key,
16
- clearInvalidConfig: true,
17
- });
18
-
19
- /**
20
- * Get access token or throw error if not found
21
- */
22
- export function getTokenOrThrow() {
23
- const token = accessStore.get("access_token");
24
- if (!token) {
25
- const pkgCmd = getPackageManager();
26
- throw new Error(
27
- `Access token not found. Please run ${chalk.cyan(`${pkgCmd} run enpage:login`)} to authenticate.`,
28
- );
29
- }
30
- return token;
31
- }
32
-
33
- function findNearestNodeModules(): string | null {
34
- let currentDir = __dirname;
35
- while (currentDir !== path.parse(currentDir).root) {
36
- const nodeModulesPath = path.join(currentDir, "node_modules");
37
- if (fs.existsSync(nodeModulesPath)) {
38
- return nodeModulesPath;
39
- }
40
- currentDir = path.dirname(currentDir);
41
- }
42
- return null;
43
- }
44
-
45
- function getKey() {
46
- const nodeModulesPath = findNearestNodeModules();
47
- if (!nodeModulesPath) {
48
- throw new Error("Could not find nearest node_modules directory.");
49
- }
50
- const tmpSecureStoreDir = path.join(nodeModulesPath, ".enpage-tmp");
51
- if (!fs.existsSync(tmpSecureStoreDir)) {
52
- fs.mkdirSync(tmpSecureStoreDir, { recursive: true, mode: 0o700 });
53
- }
54
- const keyPath = path.join(tmpSecureStoreDir, ".enpage-key");
55
- if (!fs.existsSync(keyPath)) {
56
- const key = crypto.randomBytes(32).toString("hex");
57
- fs.writeFileSync(keyPath, key, { mode: 0o600 });
58
- return key;
59
- }
60
- const key = fs.readFileSync(keyPath, "utf8");
61
- return key;
62
- }
63
-
64
- export { accessStore };
@@ -1,161 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { post, get } from "../api";
3
- import { accessStore } from "../store";
4
-
5
- vi.mock("../store", () => ({
6
- accessStore: {
7
- get: vi.fn(),
8
- },
9
- getToken: vi.fn(),
10
- }));
11
-
12
- vi.mock("../shared/logger", () => ({
13
- logger: {
14
- error: vi.fn(),
15
- },
16
- }));
17
-
18
- global.fetch = vi.fn();
19
-
20
- describe("API functions", () => {
21
- beforeEach(() => {
22
- vi.resetAllMocks();
23
- });
24
-
25
- afterEach(() => {
26
- vi.restoreAllMocks();
27
- });
28
-
29
- describe("post", () => {
30
- it("should make a POST request with correct headers and body", async () => {
31
- const mockResponse = { data: "test" };
32
- (global.fetch as any).mockResolvedValueOnce({
33
- ok: true,
34
- status: 200,
35
- statusText: "OK",
36
- headers: new Headers({ "content-type": "application/json" }),
37
- json: () => Promise.resolve(mockResponse),
38
- });
39
-
40
- const result = await post("/test", { key: "value" });
41
-
42
- expect(global.fetch).toHaveBeenCalledWith(
43
- expect.any(URL),
44
- expect.objectContaining({
45
- method: "POST",
46
- headers: expect.objectContaining({
47
- "Content-Type": "application/json",
48
- }),
49
- body: JSON.stringify({ key: "value" }),
50
- }),
51
- );
52
- expect(result).toEqual({
53
- isSuccess: true,
54
- isError: false,
55
- status: 200,
56
- statusText: "OK",
57
- data: mockResponse,
58
- });
59
- });
60
-
61
- it("should handle URLSearchParams correctly", async () => {
62
- const mockResponse = { data: "test" };
63
- (global.fetch as any).mockResolvedValueOnce({
64
- ok: true,
65
- status: 200,
66
- statusText: "OK",
67
- headers: new Headers({ "content-type": "application/json" }),
68
- json: () => Promise.resolve(mockResponse),
69
- });
70
-
71
- const params = new URLSearchParams({ key: "value" });
72
- await post("/test", params);
73
-
74
- expect(global.fetch).toHaveBeenCalledWith(
75
- expect.any(URL),
76
- expect.objectContaining({
77
- headers: expect.objectContaining({
78
- "Content-Type": "application/x-www-form-urlencoded",
79
- }),
80
- body: params,
81
- }),
82
- );
83
- });
84
-
85
- it("should handle error responses", async () => {
86
- const errorResponse = { error: "Test error" };
87
- (global.fetch as any).mockResolvedValueOnce({
88
- ok: false,
89
- status: 400,
90
- statusText: "Bad Request",
91
- headers: new Headers({ "content-type": "application/json" }),
92
- json: () => Promise.resolve(errorResponse),
93
- });
94
-
95
- const result = await post("/test", { key: "value" });
96
-
97
- expect(result).toEqual({
98
- isSuccess: false,
99
- isError: true,
100
- status: 400,
101
- statusText: "Bad Request",
102
- data: errorResponse,
103
- });
104
- });
105
- });
106
-
107
- describe("get", () => {
108
- it("should make a GET request with correct headers", async () => {
109
- const mockResponse = { data: "test" };
110
- (global.fetch as any).mockResolvedValueOnce({
111
- ok: true,
112
- status: 200,
113
- statusText: "OK",
114
- headers: new Headers({ "content-type": "application/json" }),
115
- json: () => Promise.resolve(mockResponse),
116
- });
117
-
118
- vi.mocked(accessStore.get).mockReturnValue("test-token");
119
-
120
- const result = await get("/test");
121
-
122
- expect(global.fetch).toHaveBeenCalledWith(
123
- expect.any(URL),
124
- expect.objectContaining({
125
- method: "GET",
126
- headers: expect.objectContaining({
127
- Authorization: "Bearer test-token",
128
- }),
129
- }),
130
- );
131
- expect(result).toEqual({
132
- isSuccess: true,
133
- isError: false,
134
- status: 200,
135
- statusText: "OK",
136
- data: mockResponse,
137
- });
138
- });
139
-
140
- it("should handle error responses", async () => {
141
- const errorResponse = { error: "Test error" };
142
- (global.fetch as any).mockResolvedValueOnce({
143
- ok: false,
144
- status: 404,
145
- statusText: "Not Found",
146
- headers: new Headers({ "content-type": "application/json" }),
147
- json: () => Promise.resolve(errorResponse),
148
- });
149
-
150
- const result = await get("/test");
151
-
152
- expect(result).toEqual({
153
- isSuccess: false,
154
- isError: true,
155
- status: 404,
156
- statusText: "Not Found",
157
- data: errorResponse,
158
- });
159
- });
160
- });
161
- });