storybooker 0.19.4 → 0.22.0-canary.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 (215) hide show
  1. package/README.md +40 -18
  2. package/dist/adapters/_internal/queue.d.mts +127 -0
  3. package/dist/aws-dynamodb.d.mts +22 -0
  4. package/dist/aws-dynamodb.mjs +118 -0
  5. package/dist/aws-dynamodb.mjs.map +1 -0
  6. package/dist/aws-s3.d.mts +20 -0
  7. package/dist/aws-s3.mjs +96 -0
  8. package/dist/aws-s3.mjs.map +1 -0
  9. package/dist/azure-blob-storage.d.mts +20 -0
  10. package/dist/azure-blob-storage.mjs +126 -0
  11. package/dist/azure-blob-storage.mjs.map +1 -0
  12. package/dist/azure-cosmos-db.d.mts +23 -0
  13. package/dist/azure-cosmos-db.mjs +87 -0
  14. package/dist/azure-cosmos-db.mjs.map +1 -0
  15. package/dist/azure-data-tables.d.mts +23 -0
  16. package/dist/azure-data-tables.mjs +127 -0
  17. package/dist/azure-data-tables.mjs.map +1 -0
  18. package/dist/azure-easy-auth.d.mts +50 -0
  19. package/dist/azure-easy-auth.mjs +88 -0
  20. package/dist/azure-easy-auth.mjs.map +1 -0
  21. package/dist/azure-functions.d.mts +62 -0
  22. package/dist/azure-functions.mjs +147 -0
  23. package/dist/azure-functions.mjs.map +1 -0
  24. package/dist/fs.d.mts +37 -0
  25. package/dist/fs.mjs +240 -0
  26. package/dist/fs.mjs.map +1 -0
  27. package/dist/gcp-big-table.d.mts +23 -0
  28. package/dist/gcp-big-table.mjs +92 -0
  29. package/dist/gcp-big-table.mjs.map +1 -0
  30. package/dist/gcp-firestore.d.mts +22 -0
  31. package/dist/gcp-firestore.mjs +87 -0
  32. package/dist/gcp-firestore.mjs.map +1 -0
  33. package/dist/gcp-storage.d.mts +20 -0
  34. package/dist/gcp-storage.mjs +96 -0
  35. package/dist/gcp-storage.mjs.map +1 -0
  36. package/dist/handlers/handle-process-zip.mjs +90 -0
  37. package/dist/handlers/handle-process-zip.mjs.map +1 -0
  38. package/dist/handlers/handle-purge.d.mts +12 -0
  39. package/dist/handlers/handle-purge.mjs +36 -0
  40. package/dist/handlers/handle-purge.mjs.map +1 -0
  41. package/dist/handlers/handle-serve-storybook.mjs +94 -0
  42. package/dist/handlers/handle-serve-storybook.mjs.map +1 -0
  43. package/dist/index.d.mts +28 -0
  44. package/dist/index.mjs +62 -0
  45. package/dist/index.mjs.map +1 -0
  46. package/dist/models/builds-model.mjs +248 -0
  47. package/dist/models/builds-model.mjs.map +1 -0
  48. package/dist/models/builds-schema.d.mts +171 -0
  49. package/dist/models/builds-schema.mjs +67 -0
  50. package/dist/models/builds-schema.mjs.map +1 -0
  51. package/dist/models/projects-model.mjs +122 -0
  52. package/dist/models/projects-model.mjs.map +1 -0
  53. package/dist/models/projects-schema.d.mts +70 -0
  54. package/dist/models/projects-schema.mjs +37 -0
  55. package/dist/models/projects-schema.mjs.map +1 -0
  56. package/dist/models/tags-model.mjs +110 -0
  57. package/dist/models/tags-model.mjs.map +1 -0
  58. package/dist/models/tags-schema.d.mts +76 -0
  59. package/dist/models/tags-schema.mjs +34 -0
  60. package/dist/models/tags-schema.mjs.map +1 -0
  61. package/dist/models/~model.mjs +43 -0
  62. package/dist/models/~model.mjs.map +1 -0
  63. package/dist/models/~shared-schema.d.mts +1 -0
  64. package/dist/models/~shared-schema.mjs +20 -0
  65. package/dist/models/~shared-schema.mjs.map +1 -0
  66. package/dist/mysql.d.mts +39 -0
  67. package/dist/mysql.mjs +151 -0
  68. package/dist/mysql.mjs.map +1 -0
  69. package/dist/redis.d.mts +33 -0
  70. package/dist/redis.mjs +118 -0
  71. package/dist/redis.mjs.map +1 -0
  72. package/dist/routers/account-router.mjs +91 -0
  73. package/dist/routers/account-router.mjs.map +1 -0
  74. package/dist/routers/builds-router.mjs +347 -0
  75. package/dist/routers/builds-router.mjs.map +1 -0
  76. package/dist/routers/projects-router.mjs +236 -0
  77. package/dist/routers/projects-router.mjs.map +1 -0
  78. package/dist/routers/root-router.mjs +108 -0
  79. package/dist/routers/root-router.mjs.map +1 -0
  80. package/dist/routers/tags-router.mjs +269 -0
  81. package/dist/routers/tags-router.mjs.map +1 -0
  82. package/dist/routers/tasks-router.mjs +71 -0
  83. package/dist/routers/tasks-router.mjs.map +1 -0
  84. package/dist/urls.d.mts +47 -0
  85. package/dist/urls.mjs +208 -0
  86. package/dist/urls.mjs.map +1 -0
  87. package/dist/utils/adapter-utils.d.mts +14 -0
  88. package/dist/utils/adapter-utils.mjs +14 -0
  89. package/dist/utils/adapter-utils.mjs.map +1 -0
  90. package/dist/utils/auth.mjs +25 -0
  91. package/dist/utils/auth.mjs.map +1 -0
  92. package/dist/utils/error.d.mts +21 -0
  93. package/dist/utils/error.mjs +109 -0
  94. package/dist/utils/error.mjs.map +1 -0
  95. package/dist/utils/file-utils.mjs +16 -0
  96. package/dist/utils/file-utils.mjs.map +1 -0
  97. package/dist/utils/openapi-utils.mjs +45 -0
  98. package/dist/utils/openapi-utils.mjs.map +1 -0
  99. package/dist/utils/request.mjs +35 -0
  100. package/dist/utils/request.mjs.map +1 -0
  101. package/dist/utils/response.mjs +24 -0
  102. package/dist/utils/response.mjs.map +1 -0
  103. package/dist/utils/store.mjs +54 -0
  104. package/dist/utils/store.mjs.map +1 -0
  105. package/dist/utils/ui-utils.mjs +38 -0
  106. package/dist/utils/ui-utils.mjs.map +1 -0
  107. package/dist/utils/url-utils.d.mts +10 -0
  108. package/dist/utils/url-utils.mjs +54 -0
  109. package/dist/utils/url-utils.mjs.map +1 -0
  110. package/dist/~internal/adapter/auth.d.mts +123 -0
  111. package/dist/~internal/adapter/auth.mjs +20 -0
  112. package/dist/~internal/adapter/auth.mjs.map +1 -0
  113. package/dist/~internal/adapter/database.d.mts +240 -0
  114. package/dist/~internal/adapter/database.mjs +63 -0
  115. package/dist/~internal/adapter/database.mjs.map +1 -0
  116. package/dist/~internal/adapter/logger.d.mts +34 -0
  117. package/dist/~internal/adapter/logger.mjs +13 -0
  118. package/dist/~internal/adapter/logger.mjs.map +1 -0
  119. package/dist/~internal/adapter/storage.d.mts +208 -0
  120. package/dist/~internal/adapter/storage.mjs +63 -0
  121. package/dist/~internal/adapter/storage.mjs.map +1 -0
  122. package/dist/~internal/adapter/ui.d.mts +109 -0
  123. package/dist/~internal/adapter/ui.mjs +1 -0
  124. package/dist/~internal/adapter.d.mts +8 -0
  125. package/dist/~internal/adapter.mjs +6 -0
  126. package/dist/~internal/constants.d.mts +24 -0
  127. package/dist/~internal/constants.mjs +32 -0
  128. package/dist/~internal/constants.mjs.map +1 -0
  129. package/dist/~internal/mimes.d.mts +449 -0
  130. package/dist/~internal/mimes.mjs +454 -0
  131. package/dist/~internal/mimes.mjs.map +1 -0
  132. package/dist/~internal/router.d.mts +1651 -0
  133. package/dist/~internal/router.mjs +39 -0
  134. package/dist/~internal/router.mjs.map +1 -0
  135. package/dist/~internal/types.d.mts +77 -0
  136. package/dist/~internal/types.mjs +1 -0
  137. package/dist/~internal/utils.d.mts +4 -0
  138. package/dist/~internal/utils.mjs +5 -0
  139. package/openapi.json +3162 -0
  140. package/package.json +148 -27
  141. package/src/adapters/_internal/auth.ts +135 -0
  142. package/src/adapters/_internal/database.ts +241 -0
  143. package/src/adapters/_internal/index.ts +8 -0
  144. package/src/adapters/_internal/logger.ts +41 -0
  145. package/src/adapters/_internal/queue.ts +151 -0
  146. package/src/adapters/_internal/storage.ts +197 -0
  147. package/src/adapters/_internal/ui.ts +103 -0
  148. package/src/adapters/aws-dynamodb.ts +201 -0
  149. package/src/adapters/aws-s3.ts +160 -0
  150. package/src/adapters/azure-blob-storage.ts +223 -0
  151. package/src/adapters/azure-cosmos-db.ts +158 -0
  152. package/src/adapters/azure-data-tables.ts +223 -0
  153. package/src/adapters/azure-easy-auth.ts +174 -0
  154. package/src/adapters/azure-functions.ts +242 -0
  155. package/src/adapters/fs.ts +398 -0
  156. package/src/adapters/gcp-big-table.ts +157 -0
  157. package/src/adapters/gcp-firestore.ts +146 -0
  158. package/src/adapters/gcp-storage.ts +141 -0
  159. package/src/adapters/mysql.ts +296 -0
  160. package/src/adapters/redis.ts +242 -0
  161. package/src/handlers/handle-process-zip.ts +117 -0
  162. package/src/handlers/handle-purge.ts +65 -0
  163. package/src/handlers/handle-serve-storybook.ts +101 -0
  164. package/src/index.ts +81 -16
  165. package/src/mocks/mock-auth-service.ts +51 -0
  166. package/src/mocks/mock-store.ts +26 -0
  167. package/src/models/builds-model.ts +373 -0
  168. package/src/models/builds-schema.ts +84 -0
  169. package/src/models/projects-model.ts +177 -0
  170. package/src/models/projects-schema.ts +69 -0
  171. package/src/models/tags-model.ts +138 -0
  172. package/src/models/tags-schema.ts +45 -0
  173. package/src/models/~model.ts +79 -0
  174. package/src/models/~shared-schema.ts +14 -0
  175. package/src/routers/_app-router.ts +57 -0
  176. package/src/routers/account-router.ts +136 -0
  177. package/src/routers/builds-router.ts +464 -0
  178. package/src/routers/projects-router.ts +309 -0
  179. package/src/routers/root-router.ts +127 -0
  180. package/src/routers/tags-router.ts +339 -0
  181. package/src/routers/tasks-router.ts +75 -0
  182. package/src/types.ts +107 -0
  183. package/src/urls.ts +327 -0
  184. package/src/utils/adapter-utils.ts +26 -0
  185. package/src/utils/auth.test.ts +71 -0
  186. package/src/utils/auth.ts +39 -0
  187. package/src/utils/constants.ts +31 -0
  188. package/src/utils/date-utils.ts +10 -0
  189. package/src/utils/error.test.ts +86 -0
  190. package/src/utils/error.ts +140 -0
  191. package/src/utils/file-utils.test.ts +65 -0
  192. package/src/utils/file-utils.ts +43 -0
  193. package/src/utils/index.ts +3 -0
  194. package/src/utils/mime-utils.ts +457 -0
  195. package/src/utils/openapi-utils.ts +49 -0
  196. package/src/utils/request.ts +97 -0
  197. package/src/utils/response.ts +20 -0
  198. package/src/utils/store.ts +85 -0
  199. package/src/utils/story-utils.ts +42 -0
  200. package/src/utils/text-utils.ts +10 -0
  201. package/src/utils/ui-utils.ts +57 -0
  202. package/src/utils/url-utils.ts +113 -0
  203. package/dist/index.js +0 -554
  204. package/src/commands/create.ts +0 -263
  205. package/src/commands/purge.ts +0 -70
  206. package/src/commands/test.ts +0 -42
  207. package/src/service-schema.d.ts +0 -2023
  208. package/src/utils/auth-utils.ts +0 -31
  209. package/src/utils/pkg-utils.ts +0 -37
  210. package/src/utils/sb-build.ts +0 -55
  211. package/src/utils/sb-test.ts +0 -115
  212. package/src/utils/schema-utils.ts +0 -123
  213. package/src/utils/stream-utils.ts +0 -72
  214. package/src/utils/types.ts +0 -4
  215. package/src/utils/zip.ts +0 -77
@@ -0,0 +1,347 @@
1
+ import { QUERY_PARAMS } from "../~internal/constants.mjs";
2
+ import { mimes } from "../~internal/mimes.mjs";
3
+ import { getStore } from "../utils/store.mjs";
4
+ import { urlBuilder } from "../urls.mjs";
5
+ import { authenticateOrThrow } from "../utils/auth.mjs";
6
+ import { BuildIdSchema, ProjectIdSchema } from "../models/~shared-schema.mjs";
7
+ import { BuildCreateSchema, BuildUpdateSchema, BuildUploadFormBodySchema, BuildUploadQueryParamsSchema, BuildsGetResultSchema, BuildsListResultSchema } from "../models/builds-schema.mjs";
8
+ import "../models/projects-schema.mjs";
9
+ import { checkIsHTMLRequest, validateBuildUploadZipBody } from "../utils/request.mjs";
10
+ import { createUIResultResponse } from "../utils/ui-utils.mjs";
11
+ import { ProjectsModel } from "../models/projects-model.mjs";
12
+ import { BuildsModel } from "../models/builds-model.mjs";
13
+ import { openapiCommonErrorResponses, openapiErrorResponseContent, openapiResponseRedirect, openapiResponsesHtml } from "../utils/openapi-utils.mjs";
14
+ import { SuperHeaders } from "@remix-run/headers";
15
+ import { HTTPException } from "hono/http-exception";
16
+ import { OpenAPIHono, createRoute } from "@hono/zod-openapi";
17
+ import { z as z$1 } from "zod";
18
+
19
+ //#region src/routers/builds-router.ts
20
+ const buildTag = "Builds";
21
+ const projectIdPathParams = z$1.object({ projectId: ProjectIdSchema });
22
+ const buildIdPathParams = z$1.object({
23
+ projectId: ProjectIdSchema,
24
+ buildId: BuildIdSchema
25
+ });
26
+ /**
27
+ * @private
28
+ */
29
+ const buildsRouter = new OpenAPIHono().openapi(createRoute({
30
+ summary: "List builds",
31
+ method: "get",
32
+ path: "/projects/{projectId}/builds",
33
+ tags: [buildTag],
34
+ request: { params: projectIdPathParams },
35
+ responses: {
36
+ 200: {
37
+ description: "A list of builds in the project.",
38
+ content: {
39
+ [mimes.json]: { schema: BuildsListResultSchema },
40
+ ...openapiResponsesHtml
41
+ }
42
+ },
43
+ ...openapiCommonErrorResponses
44
+ }
45
+ }), async (context) => {
46
+ const { projectId } = context.req.valid("param");
47
+ const { ui } = getStore();
48
+ authenticateOrThrow({
49
+ action: "read",
50
+ projectId,
51
+ resource: "build"
52
+ });
53
+ const builds = await new BuildsModel(projectId).list();
54
+ if (ui?.renderBuildsListPage && checkIsHTMLRequest()) {
55
+ const project = await new ProjectsModel().get(projectId);
56
+ return createUIResultResponse(context, ui.renderBuildsListPage, {
57
+ builds,
58
+ project
59
+ });
60
+ }
61
+ return context.json({ builds });
62
+ }).openapi(createRoute({
63
+ summary: "Create build - UI",
64
+ method: "get",
65
+ path: "/projects/{projectId}/builds/create",
66
+ tags: [buildTag],
67
+ request: {
68
+ params: projectIdPathParams,
69
+ query: z$1.object({ [QUERY_PARAMS.tagId]: z$1.string().optional() })
70
+ },
71
+ responses: {
72
+ 200: {
73
+ description: "UI to create build",
74
+ content: openapiResponsesHtml
75
+ },
76
+ ...openapiCommonErrorResponses
77
+ }
78
+ }), async (context) => {
79
+ const { ui } = getStore();
80
+ if (!ui?.renderBuildCreatePage) throw new HTTPException(405, { message: "UI not available for this route." });
81
+ const { projectId } = context.req.valid("param");
82
+ const { tagId } = context.req.valid("query");
83
+ authenticateOrThrow({
84
+ action: "create",
85
+ projectId,
86
+ resource: "build"
87
+ });
88
+ const project = await new ProjectsModel().get(projectId);
89
+ return createUIResultResponse(context, ui.renderBuildCreatePage, {
90
+ project,
91
+ tagId
92
+ });
93
+ }).openapi(createRoute({
94
+ summary: "Create build - action",
95
+ method: "post",
96
+ path: "/projects/{projectId}/builds/create",
97
+ tags: [buildTag],
98
+ request: {
99
+ params: projectIdPathParams,
100
+ body: {
101
+ content: { [mimes.formEncoded]: { schema: BuildCreateSchema } },
102
+ required: true
103
+ }
104
+ },
105
+ responses: {
106
+ 201: {
107
+ description: "Build created successfully",
108
+ content: { [mimes.json]: { schema: BuildsGetResultSchema } }
109
+ },
110
+ 303: openapiResponseRedirect("Redirect to build."),
111
+ 409: {
112
+ content: openapiErrorResponseContent,
113
+ description: "Build already exists."
114
+ },
115
+ 415: {
116
+ content: openapiErrorResponseContent,
117
+ description: "Unsupported Media Type"
118
+ },
119
+ ...openapiCommonErrorResponses
120
+ }
121
+ }), async (context) => {
122
+ const { projectId } = context.req.valid("param");
123
+ if (!await new ProjectsModel().id(projectId).has()) throw new HTTPException(404, { message: `The project '${projectId}' does not exist.` });
124
+ authenticateOrThrow({
125
+ action: "create",
126
+ projectId,
127
+ resource: "build"
128
+ });
129
+ const data = context.req.valid("form");
130
+ const build = await new BuildsModel(projectId).create(data);
131
+ const url = urlBuilder.buildDetails(projectId, build.id);
132
+ if (checkIsHTMLRequest(true)) return context.redirect(url, 303);
133
+ return context.json({
134
+ build,
135
+ url
136
+ }, 201);
137
+ }).openapi(createRoute({
138
+ summary: "Build details",
139
+ method: "get",
140
+ path: "/projects/{projectId}/builds/{buildId}",
141
+ tags: [buildTag],
142
+ request: { params: buildIdPathParams },
143
+ responses: {
144
+ 200: {
145
+ description: "Details of the build",
146
+ content: {
147
+ [mimes.json]: { schema: BuildsGetResultSchema },
148
+ ...openapiResponsesHtml
149
+ }
150
+ },
151
+ 404: {
152
+ description: "Matching build not found.",
153
+ content: openapiErrorResponseContent
154
+ },
155
+ ...openapiCommonErrorResponses
156
+ }
157
+ }), async (context) => {
158
+ const { projectId, buildId } = context.req.valid("param");
159
+ const { ui } = getStore();
160
+ authenticateOrThrow({
161
+ action: "read",
162
+ projectId,
163
+ resource: "build"
164
+ });
165
+ const model = new BuildsModel(projectId);
166
+ const build = await model.get(buildId);
167
+ if (ui?.renderBuildDetailsPage && checkIsHTMLRequest()) {
168
+ const project = await new ProjectsModel().get(projectId);
169
+ const stories = await model.getStories(build);
170
+ return createUIResultResponse(context, ui.renderBuildDetailsPage, {
171
+ build,
172
+ stories,
173
+ project
174
+ });
175
+ }
176
+ return context.json({
177
+ build,
178
+ url: context.req.url
179
+ });
180
+ }).openapi(createRoute({
181
+ summary: "Delete build - action",
182
+ method: "post",
183
+ path: "/projects/{projectId}/builds/{buildId}/delete",
184
+ tags: [buildTag],
185
+ request: { params: buildIdPathParams },
186
+ responses: {
187
+ 204: { description: "Build deleted successfully." },
188
+ 303: openapiResponseRedirect("Redirect to builds list."),
189
+ 404: {
190
+ description: "Matching build not found.",
191
+ content: openapiErrorResponseContent
192
+ },
193
+ ...openapiCommonErrorResponses
194
+ }
195
+ }), async (context) => {
196
+ const { projectId, buildId } = context.req.valid("param");
197
+ authenticateOrThrow({
198
+ action: "delete",
199
+ projectId,
200
+ resource: "build"
201
+ });
202
+ await new BuildsModel(projectId).delete(buildId, true);
203
+ if (checkIsHTMLRequest(true)) return context.redirect(urlBuilder.buildsList(projectId), 303);
204
+ return new Response(null, { status: 204 });
205
+ }).openapi(createRoute({
206
+ summary: "Update build - action",
207
+ method: "post",
208
+ path: "/projects/{projectId}/builds/{buildId}/update",
209
+ tags: [buildTag],
210
+ request: {
211
+ params: buildIdPathParams,
212
+ body: {
213
+ content: { [mimes.formEncoded]: { schema: BuildUpdateSchema } },
214
+ required: true
215
+ }
216
+ },
217
+ responses: {
218
+ 202: { description: "Build updated successfully" },
219
+ 303: openapiResponseRedirect("Redirect to build."),
220
+ 404: {
221
+ description: "Matching project or build not found.",
222
+ content: openapiErrorResponseContent
223
+ },
224
+ 415: {
225
+ content: openapiErrorResponseContent,
226
+ description: "Unsupported Media Type"
227
+ },
228
+ ...openapiCommonErrorResponses
229
+ }
230
+ }), async (context) => {
231
+ const { buildId, projectId } = context.req.valid("param");
232
+ const buildsModel = new BuildsModel(projectId);
233
+ if (!await buildsModel.has(buildId)) throw new HTTPException(404, { message: `The build '${buildId}' does not exist in project '${projectId}'.` });
234
+ authenticateOrThrow({
235
+ action: "update",
236
+ projectId,
237
+ resource: "build"
238
+ });
239
+ const data = context.req.valid("form");
240
+ await buildsModel.update(buildId, data);
241
+ if (checkIsHTMLRequest(true)) return context.redirect(urlBuilder.buildDetails(projectId, buildId), 303);
242
+ return new Response(null, { status: 202 });
243
+ }).openapi(createRoute({
244
+ summary: "Upload build - UI",
245
+ method: "get",
246
+ path: "/projects/{projectId}/builds/{buildId}/upload",
247
+ tags: [buildTag],
248
+ request: {
249
+ params: buildIdPathParams,
250
+ query: BuildUploadQueryParamsSchema
251
+ },
252
+ responses: {
253
+ 200: {
254
+ description: "UI to upload build",
255
+ content: openapiResponsesHtml
256
+ },
257
+ 404: {
258
+ description: "Matching build not found.",
259
+ content: openapiErrorResponseContent
260
+ },
261
+ ...openapiCommonErrorResponses
262
+ }
263
+ }), async (context) => {
264
+ const { ui } = getStore();
265
+ if (!ui?.renderBuildUploadPage) throw new HTTPException(405, { message: "UI not available for this route." });
266
+ const { buildId, projectId } = context.req.valid("param");
267
+ authenticateOrThrow({
268
+ action: "update",
269
+ projectId,
270
+ resource: "build"
271
+ });
272
+ const build = await new BuildsModel(projectId).get(buildId);
273
+ const project = await new ProjectsModel().get(projectId);
274
+ const { variant: uploadVariant } = context.req.valid("query");
275
+ return createUIResultResponse(context, ui.renderBuildUploadPage, {
276
+ build,
277
+ uploadVariant,
278
+ project
279
+ });
280
+ }).openapi(createRoute({
281
+ summary: "Upload build - action",
282
+ method: "post",
283
+ path: "/projects/{projectId}/builds/{buildId}/upload",
284
+ tags: [buildTag],
285
+ request: {
286
+ params: buildIdPathParams,
287
+ query: BuildUploadQueryParamsSchema,
288
+ body: {
289
+ description: "Compressed zip containing files.",
290
+ content: {
291
+ [mimes.formMultipart]: { schema: BuildUploadFormBodySchema },
292
+ [mimes.zip]: {
293
+ schema: {
294
+ format: "binary",
295
+ type: "string"
296
+ },
297
+ example: "storybook.zip"
298
+ }
299
+ },
300
+ required: true
301
+ }
302
+ },
303
+ responses: {
304
+ 204: { description: "File uploaded successfully" },
305
+ 303: openapiResponseRedirect("Redirect to project."),
306
+ 404: {
307
+ description: "Matching project not found.",
308
+ content: openapiErrorResponseContent
309
+ },
310
+ 415: {
311
+ content: openapiErrorResponseContent,
312
+ description: "Unsupported Media Type"
313
+ },
314
+ ...openapiCommonErrorResponses
315
+ }
316
+ }), async (context) => {
317
+ const { buildId, projectId } = context.req.valid("param");
318
+ const buildsModel = new BuildsModel(projectId);
319
+ if (!await buildsModel.has(buildId)) throw new HTTPException(404, { message: `The build '${buildId}' does not exist in project '${projectId}'.` });
320
+ authenticateOrThrow({
321
+ action: "update",
322
+ projectId,
323
+ resource: "build"
324
+ });
325
+ const { contentType } = new SuperHeaders(context.req.header());
326
+ if (!contentType.toString()) throw new HTTPException(400, { message: "Content-Type header is required" });
327
+ const redirectUrl = urlBuilder.buildDetails(projectId, buildId);
328
+ if (contentType.mediaType?.startsWith(mimes.formMultipart)) {
329
+ const { file, variant } = BuildUploadFormBodySchema.parse(await context.req.parseBody());
330
+ await buildsModel.upload(buildId, variant, file);
331
+ if (checkIsHTMLRequest(true)) return context.redirect(redirectUrl, 303);
332
+ return new Response(null, { status: 204 });
333
+ }
334
+ if (contentType.mediaType?.startsWith(mimes.zip)) {
335
+ const bodyError = validateBuildUploadZipBody(context.req.raw);
336
+ if (bodyError) throw new HTTPException(bodyError.status, { message: bodyError.message });
337
+ const { variant } = context.req.valid("query");
338
+ await buildsModel.upload(buildId, variant);
339
+ if (checkIsHTMLRequest(true)) return context.redirect(redirectUrl, 303);
340
+ return new Response(null, { status: 204 });
341
+ }
342
+ throw new HTTPException(415, { message: `Invalid content type, expected ${mimes.zip} or ${mimes.formMultipart}.` });
343
+ });
344
+
345
+ //#endregion
346
+ export { buildsRouter };
347
+ //# sourceMappingURL=builds-router.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builds-router.mjs","names":["z"],"sources":["../../src/routers/builds-router.ts"],"sourcesContent":["import { createRoute, OpenAPIHono } from \"@hono/zod-openapi\";\nimport { SuperHeaders } from \"@remix-run/headers\";\nimport { HTTPException } from \"hono/http-exception\";\nimport type { ContentfulStatusCode } from \"hono/utils/http-status\";\nimport { z } from \"zod\";\nimport { BuildsModel } from \"../models/builds-model.ts\";\nimport {\n BuildCreateSchema,\n BuildIdSchema,\n BuildsGetResultSchema,\n BuildsListResultSchema,\n BuildUpdateSchema,\n BuildUploadFormBodySchema,\n BuildUploadQueryParamsSchema,\n} from \"../models/builds-schema.ts\";\nimport { ProjectsModel } from \"../models/projects-model.ts\";\nimport { ProjectIdSchema } from \"../models/projects-schema.ts\";\nimport { urlBuilder } from \"../urls.ts\";\nimport { authenticateOrThrow } from \"../utils/auth.ts\";\nimport { QUERY_PARAMS } from \"../utils/constants.ts\";\nimport { mimes } from \"../utils/mime-utils.ts\";\nimport {\n openapiCommonErrorResponses,\n openapiErrorResponseContent,\n openapiResponseRedirect,\n openapiResponsesHtml,\n} from \"../utils/openapi-utils.ts\";\nimport { checkIsHTMLRequest, validateBuildUploadZipBody } from \"../utils/request.ts\";\nimport { getStore } from \"../utils/store.ts\";\nimport { createUIResultResponse } from \"../utils/ui-utils.ts\";\n\nconst buildTag = \"Builds\";\nconst projectIdPathParams = z.object({ projectId: ProjectIdSchema });\nconst buildIdPathParams = z.object({\n projectId: ProjectIdSchema,\n buildId: BuildIdSchema,\n});\n\n/**\n * @private\n */\nexport const buildsRouter = new OpenAPIHono()\n .openapi(\n createRoute({\n summary: \"List builds\",\n method: \"get\",\n path: \"/projects/{projectId}/builds\",\n tags: [buildTag],\n request: {\n params: projectIdPathParams,\n },\n responses: {\n 200: {\n description: \"A list of builds in the project.\",\n content: {\n [mimes.json]: { schema: BuildsListResultSchema },\n ...openapiResponsesHtml,\n },\n },\n ...openapiCommonErrorResponses,\n },\n }),\n async (context) => {\n const { projectId } = context.req.valid(\"param\");\n const { ui } = getStore();\n\n authenticateOrThrow({\n action: \"read\",\n projectId,\n resource: \"build\",\n });\n\n const builds = await new BuildsModel(projectId).list();\n\n if (ui?.renderBuildsListPage && checkIsHTMLRequest()) {\n const project = await new ProjectsModel().get(projectId);\n\n return createUIResultResponse(context, ui.renderBuildsListPage, { builds, project });\n }\n\n return context.json({ builds });\n },\n )\n .openapi(\n createRoute({\n summary: \"Create build - UI\",\n method: \"get\",\n path: \"/projects/{projectId}/builds/create\",\n tags: [buildTag],\n request: {\n params: projectIdPathParams,\n query: z.object({ [QUERY_PARAMS.tagId]: z.string().optional() }),\n },\n responses: {\n 200: {\n description: \"UI to create build\",\n content: openapiResponsesHtml,\n },\n ...openapiCommonErrorResponses,\n },\n }),\n async (context) => {\n const { ui } = getStore();\n if (!ui?.renderBuildCreatePage) {\n throw new HTTPException(405, { message: \"UI not available for this route.\" });\n }\n\n const { projectId } = context.req.valid(\"param\");\n const { tagId } = context.req.valid(\"query\");\n\n authenticateOrThrow({\n action: \"create\",\n projectId,\n resource: \"build\",\n });\n\n const project = await new ProjectsModel().get(projectId);\n\n return createUIResultResponse(context, ui.renderBuildCreatePage, { project, tagId });\n },\n )\n .openapi(\n createRoute({\n summary: \"Create build - action\",\n method: \"post\",\n path: \"/projects/{projectId}/builds/create\",\n tags: [buildTag],\n request: {\n params: projectIdPathParams,\n body: {\n content: { [mimes.formEncoded]: { schema: BuildCreateSchema } },\n required: true,\n },\n },\n responses: {\n 201: {\n description: \"Build created successfully\",\n content: { [mimes.json]: { schema: BuildsGetResultSchema } },\n },\n 303: openapiResponseRedirect(\"Redirect to build.\"),\n 409: {\n content: openapiErrorResponseContent,\n description: \"Build already exists.\",\n },\n 415: {\n content: openapiErrorResponseContent,\n description: \"Unsupported Media Type\",\n },\n ...openapiCommonErrorResponses,\n },\n }),\n async (context) => {\n const { projectId } = context.req.valid(\"param\");\n\n const projectModel = new ProjectsModel().id(projectId);\n if (!(await projectModel.has())) {\n throw new HTTPException(404, { message: `The project '${projectId}' does not exist.` });\n }\n\n authenticateOrThrow({\n action: \"create\",\n projectId,\n resource: \"build\",\n });\n\n const data = context.req.valid(\"form\");\n const build = await new BuildsModel(projectId).create(data);\n const url = urlBuilder.buildDetails(projectId, build.id);\n\n if (checkIsHTMLRequest(true)) {\n return context.redirect(url, 303);\n }\n\n return context.json({ build, url }, 201);\n },\n )\n .openapi(\n createRoute({\n summary: \"Build details\",\n method: \"get\",\n path: \"/projects/{projectId}/builds/{buildId}\",\n tags: [buildTag],\n request: { params: buildIdPathParams },\n responses: {\n 200: {\n description: \"Details of the build\",\n content: {\n [mimes.json]: { schema: BuildsGetResultSchema },\n ...openapiResponsesHtml,\n },\n },\n 404: {\n description: \"Matching build not found.\",\n content: openapiErrorResponseContent,\n },\n ...openapiCommonErrorResponses,\n },\n }),\n async (context) => {\n const { projectId, buildId } = context.req.valid(\"param\");\n const { ui } = getStore();\n\n authenticateOrThrow({\n action: \"read\",\n projectId,\n resource: \"build\",\n });\n\n const model = new BuildsModel(projectId);\n const build = await model.get(buildId);\n\n if (ui?.renderBuildDetailsPage && checkIsHTMLRequest()) {\n // const [hasDeletePermission, hasUpdatePermission] = await Promise.all([\n // model.checkAuth(\"delete\"),\n // model.checkAuth(\"update\"),\n // ]);\n // const canDeleteBuild =\n // hasDeletePermission && project.latestBuildId !== build.id;\n\n const project = await new ProjectsModel().get(projectId);\n const stories = await model.getStories(build);\n\n return createUIResultResponse(context, ui.renderBuildDetailsPage, {\n build,\n stories,\n project,\n });\n }\n\n return context.json({ build, url: context.req.url });\n },\n )\n .openapi(\n createRoute({\n summary: \"Delete build - action\",\n method: \"post\",\n path: \"/projects/{projectId}/builds/{buildId}/delete\",\n tags: [buildTag],\n request: { params: buildIdPathParams },\n responses: {\n 204: { description: \"Build deleted successfully.\" },\n 303: openapiResponseRedirect(\"Redirect to builds list.\"),\n 404: {\n description: \"Matching build not found.\",\n content: openapiErrorResponseContent,\n },\n ...openapiCommonErrorResponses,\n },\n }),\n async (context) => {\n const { projectId, buildId } = context.req.valid(\"param\");\n authenticateOrThrow({\n action: \"delete\",\n projectId,\n resource: \"build\",\n });\n\n await new BuildsModel(projectId).delete(buildId, true);\n\n if (checkIsHTMLRequest(true)) {\n return context.redirect(urlBuilder.buildsList(projectId), 303);\n }\n\n return new Response(null, { status: 204 });\n },\n )\n .openapi(\n createRoute({\n summary: \"Update build - action\",\n method: \"post\",\n path: \"/projects/{projectId}/builds/{buildId}/update\",\n tags: [buildTag],\n request: {\n params: buildIdPathParams,\n body: {\n content: { [mimes.formEncoded]: { schema: BuildUpdateSchema } },\n required: true,\n },\n },\n responses: {\n 202: { description: \"Build updated successfully\" },\n 303: openapiResponseRedirect(\"Redirect to build.\"),\n 404: {\n description: \"Matching project or build not found.\",\n content: openapiErrorResponseContent,\n },\n 415: {\n content: openapiErrorResponseContent,\n description: \"Unsupported Media Type\",\n },\n ...openapiCommonErrorResponses,\n },\n }),\n async (context) => {\n const { buildId, projectId } = context.req.valid(\"param\");\n\n const buildsModel = new BuildsModel(projectId);\n\n if (!(await buildsModel.has(buildId))) {\n throw new HTTPException(404, {\n message: `The build '${buildId}' does not exist in project '${projectId}'.`,\n });\n }\n\n authenticateOrThrow({\n action: \"update\",\n projectId,\n resource: \"build\",\n });\n\n const data = context.req.valid(\"form\");\n await buildsModel.update(buildId, data);\n\n if (checkIsHTMLRequest(true)) {\n return context.redirect(urlBuilder.buildDetails(projectId, buildId), 303);\n }\n\n return new Response(null, { status: 202 });\n },\n )\n .openapi(\n createRoute({\n summary: \"Upload build - UI\",\n method: \"get\",\n path: \"/projects/{projectId}/builds/{buildId}/upload\",\n tags: [buildTag],\n request: {\n params: buildIdPathParams,\n query: BuildUploadQueryParamsSchema,\n },\n responses: {\n 200: {\n description: \"UI to upload build\",\n content: openapiResponsesHtml,\n },\n 404: {\n description: \"Matching build not found.\",\n content: openapiErrorResponseContent,\n },\n ...openapiCommonErrorResponses,\n },\n }),\n async (context) => {\n const { ui } = getStore();\n if (!ui?.renderBuildUploadPage) {\n throw new HTTPException(405, { message: \"UI not available for this route.\" });\n }\n\n const { buildId, projectId } = context.req.valid(\"param\");\n\n authenticateOrThrow({\n action: \"update\",\n projectId,\n resource: \"build\",\n });\n\n const build = await new BuildsModel(projectId).get(buildId);\n const project = await new ProjectsModel().get(projectId);\n const { variant: uploadVariant } = context.req.valid(\"query\");\n\n return createUIResultResponse(context, ui.renderBuildUploadPage, {\n build,\n uploadVariant,\n project,\n });\n },\n )\n .openapi(\n createRoute({\n summary: \"Upload build - action\",\n method: \"post\",\n path: \"/projects/{projectId}/builds/{buildId}/upload\",\n tags: [buildTag],\n request: {\n params: buildIdPathParams,\n query: BuildUploadQueryParamsSchema,\n body: {\n description: \"Compressed zip containing files.\",\n content: {\n [mimes.formMultipart]: { schema: BuildUploadFormBodySchema },\n [mimes.zip]: {\n schema: { format: \"binary\", type: \"string\" },\n example: \"storybook.zip\",\n },\n },\n required: true,\n },\n },\n responses: {\n 204: { description: \"File uploaded successfully\" },\n 303: openapiResponseRedirect(\"Redirect to project.\"),\n 404: {\n description: \"Matching project not found.\",\n content: openapiErrorResponseContent,\n },\n 415: {\n content: openapiErrorResponseContent,\n description: \"Unsupported Media Type\",\n },\n ...openapiCommonErrorResponses,\n },\n }),\n async (context) => {\n const { buildId, projectId } = context.req.valid(\"param\");\n\n const buildsModel = new BuildsModel(projectId);\n\n if (!(await buildsModel.has(buildId))) {\n throw new HTTPException(404, {\n message: `The build '${buildId}' does not exist in project '${projectId}'.`,\n });\n }\n\n authenticateOrThrow({\n action: \"update\",\n projectId,\n resource: \"build\",\n });\n\n const { contentType } = new SuperHeaders(context.req.header());\n\n if (!contentType.toString()) {\n throw new HTTPException(400, { message: \"Content-Type header is required\" });\n }\n\n const redirectUrl = urlBuilder.buildDetails(projectId, buildId);\n\n // Form submission\n if (contentType.mediaType?.startsWith(mimes.formMultipart)) {\n const { file, variant } = BuildUploadFormBodySchema.parse(await context.req.parseBody());\n\n await buildsModel.upload(buildId, variant, file);\n\n if (checkIsHTMLRequest(true)) {\n return context.redirect(redirectUrl, 303);\n }\n\n return new Response(null, { status: 204 });\n }\n\n if (contentType.mediaType?.startsWith(mimes.zip)) {\n const bodyError = validateBuildUploadZipBody(context.req.raw);\n if (bodyError) {\n throw new HTTPException(bodyError.status as ContentfulStatusCode, {\n message: bodyError.message,\n });\n }\n\n const { variant } = context.req.valid(\"query\");\n\n await buildsModel.upload(buildId, variant);\n\n if (checkIsHTMLRequest(true)) {\n return context.redirect(redirectUrl, 303);\n }\n\n return new Response(null, { status: 204 });\n }\n\n throw new HTTPException(415, {\n message: `Invalid content type, expected ${mimes.zip} or ${mimes.formMultipart}.`,\n });\n },\n );\n"],"mappings":"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"}
@@ -0,0 +1,236 @@
1
+ import { mimes } from "../~internal/mimes.mjs";
2
+ import { getStore } from "../utils/store.mjs";
3
+ import { urlBuilder } from "../urls.mjs";
4
+ import { authenticateOrThrow } from "../utils/auth.mjs";
5
+ import { ProjectIdSchema } from "../models/~shared-schema.mjs";
6
+ import { ProjectCreateSchema, ProjectGetResultSchema, ProjectUpdateSchema, ProjectsListResultSchema } from "../models/projects-schema.mjs";
7
+ import { checkIsHTMLRequest } from "../utils/request.mjs";
8
+ import { createUIResultResponse } from "../utils/ui-utils.mjs";
9
+ import { TagsModel } from "../models/tags-model.mjs";
10
+ import { ProjectsModel } from "../models/projects-model.mjs";
11
+ import { BuildsModel } from "../models/builds-model.mjs";
12
+ import { openapiCommonErrorResponses, openapiErrorResponseContent, openapiResponseRedirect, openapiResponsesHtml } from "../utils/openapi-utils.mjs";
13
+ import { HTTPException } from "hono/http-exception";
14
+ import { OpenAPIHono, createRoute } from "@hono/zod-openapi";
15
+ import { z as z$1 } from "zod";
16
+
17
+ //#region src/routers/projects-router.ts
18
+ const projectTag = "Projects";
19
+ const projectIdPathParams = z$1.object({ projectId: ProjectIdSchema });
20
+ /**
21
+ * @private
22
+ */
23
+ const projectsRouter = new OpenAPIHono().openapi(createRoute({
24
+ summary: "List projects",
25
+ method: "get",
26
+ path: "/projects",
27
+ tags: [projectTag],
28
+ responses: {
29
+ 200: {
30
+ description: "A list of projects.",
31
+ content: {
32
+ [mimes.json]: { schema: ProjectsListResultSchema },
33
+ ...openapiResponsesHtml
34
+ }
35
+ },
36
+ ...openapiCommonErrorResponses
37
+ }
38
+ }), async (context) => {
39
+ const { ui } = getStore();
40
+ authenticateOrThrow({
41
+ action: "read",
42
+ projectId: void 0,
43
+ resource: "project"
44
+ });
45
+ const projects = await new ProjectsModel().list();
46
+ if (ui?.renderProjectsListPage && checkIsHTMLRequest()) return createUIResultResponse(context, ui.renderProjectsListPage, { projects });
47
+ return context.json({ projects });
48
+ }).openapi(createRoute({
49
+ summary: "Create project - UI",
50
+ method: "get",
51
+ path: "/projects/create",
52
+ tags: [projectTag],
53
+ responses: {
54
+ 200: {
55
+ description: "UI to create project",
56
+ content: openapiResponsesHtml
57
+ },
58
+ ...openapiCommonErrorResponses
59
+ }
60
+ }), (context) => {
61
+ const { ui } = getStore();
62
+ if (!ui?.renderProjectCreatePage) throw new HTTPException(405, { message: "UI not available for this route." });
63
+ authenticateOrThrow({
64
+ action: "create",
65
+ projectId: void 0,
66
+ resource: "project"
67
+ });
68
+ return createUIResultResponse(context, ui.renderProjectCreatePage, {});
69
+ }).openapi(createRoute({
70
+ summary: "Create project - action",
71
+ method: "post",
72
+ path: "/projects/create",
73
+ tags: [projectTag],
74
+ request: { body: {
75
+ content: { [mimes.formEncoded]: { schema: ProjectCreateSchema } },
76
+ required: true
77
+ } },
78
+ responses: {
79
+ 201: {
80
+ description: "Project created successfully",
81
+ content: { [mimes.json]: { schema: ProjectGetResultSchema } }
82
+ },
83
+ 303: openapiResponseRedirect("Redirect to project."),
84
+ 409: {
85
+ content: openapiErrorResponseContent,
86
+ description: "Project already exists."
87
+ },
88
+ 415: {
89
+ content: openapiErrorResponseContent,
90
+ description: "Unsupported Media Type"
91
+ },
92
+ ...openapiCommonErrorResponses
93
+ }
94
+ }), async (context) => {
95
+ authenticateOrThrow({
96
+ action: "create",
97
+ projectId: void 0,
98
+ resource: "project"
99
+ });
100
+ const data = context.req.valid("form");
101
+ const project = await new ProjectsModel().create(data);
102
+ if (checkIsHTMLRequest(true)) return context.redirect(urlBuilder.projectDetails(project.id), 303);
103
+ return context.json({ project });
104
+ }).openapi(createRoute({
105
+ summary: "Project details",
106
+ method: "get",
107
+ path: "/projects/{projectId}",
108
+ tags: [projectTag],
109
+ request: { params: projectIdPathParams },
110
+ responses: {
111
+ 200: {
112
+ description: "Details of the project",
113
+ content: {
114
+ [mimes.json]: { schema: ProjectGetResultSchema },
115
+ ...openapiResponsesHtml
116
+ }
117
+ },
118
+ 404: {
119
+ description: "Matching project not found.",
120
+ content: openapiErrorResponseContent
121
+ },
122
+ ...openapiCommonErrorResponses
123
+ }
124
+ }), async (context) => {
125
+ const { ui } = getStore();
126
+ const { projectId } = context.req.valid("param");
127
+ authenticateOrThrow({
128
+ action: "read",
129
+ projectId,
130
+ resource: "project"
131
+ });
132
+ const project = await new ProjectsModel().get(projectId);
133
+ if (ui?.renderProjectDetailsPage && checkIsHTMLRequest()) {
134
+ const recentTags = await new TagsModel(projectId).list({ limit: 10 });
135
+ const recentBuilds = await new BuildsModel(projectId).list({ limit: 10 });
136
+ return createUIResultResponse(context, ui.renderProjectDetailsPage, {
137
+ project,
138
+ recentBuilds,
139
+ recentTags
140
+ });
141
+ }
142
+ return context.json({ project });
143
+ }).openapi(createRoute({
144
+ summary: "Delete project - action",
145
+ method: "post",
146
+ path: "/projects/{projectId}/delete",
147
+ tags: [projectTag],
148
+ request: { params: projectIdPathParams },
149
+ responses: {
150
+ 204: { description: "Project deleted successfully." },
151
+ 303: openapiResponseRedirect("Redirect to projects list."),
152
+ 404: {
153
+ description: "Matching project not found.",
154
+ content: openapiErrorResponseContent
155
+ },
156
+ ...openapiCommonErrorResponses
157
+ }
158
+ }), async (context) => {
159
+ const { projectId } = context.req.valid("param");
160
+ authenticateOrThrow({
161
+ action: "delete",
162
+ projectId,
163
+ resource: "project"
164
+ });
165
+ await new ProjectsModel().delete(projectId);
166
+ if (checkIsHTMLRequest(true)) return context.redirect(urlBuilder.projectsList(), 303);
167
+ return new Response(null, { status: 204 });
168
+ }).openapi(createRoute({
169
+ summary: "Update project - UI",
170
+ method: "get",
171
+ path: "/projects/{projectId}/update",
172
+ tags: [projectTag],
173
+ request: { params: projectIdPathParams },
174
+ responses: {
175
+ 200: {
176
+ description: "UI to update project",
177
+ content: openapiResponsesHtml
178
+ },
179
+ 404: {
180
+ description: "Matching project not found.",
181
+ content: openapiErrorResponseContent
182
+ },
183
+ ...openapiCommonErrorResponses
184
+ }
185
+ }), async (context) => {
186
+ const { ui } = getStore();
187
+ if (!ui?.renderProjectUpdatePage) throw new HTTPException(405, { message: "UI not available for this route." });
188
+ const { projectId } = context.req.valid("param");
189
+ authenticateOrThrow({
190
+ action: "update",
191
+ projectId,
192
+ resource: "project"
193
+ });
194
+ const project = await new ProjectsModel().get(projectId);
195
+ return createUIResultResponse(context, ui.renderProjectUpdatePage, { project });
196
+ }).openapi(createRoute({
197
+ summary: "Update project - action",
198
+ method: "post",
199
+ path: "/projects/{projectId}/update",
200
+ tags: [projectTag],
201
+ request: {
202
+ params: projectIdPathParams,
203
+ body: {
204
+ content: { [mimes.formEncoded]: { schema: ProjectUpdateSchema } },
205
+ required: true
206
+ }
207
+ },
208
+ responses: {
209
+ 202: { description: "Project updated successfully" },
210
+ 303: openapiResponseRedirect("Redirect to project."),
211
+ 404: {
212
+ description: "Matching project not found.",
213
+ content: openapiErrorResponseContent
214
+ },
215
+ 415: {
216
+ content: openapiErrorResponseContent,
217
+ description: "Unsupported Media Type"
218
+ },
219
+ ...openapiCommonErrorResponses
220
+ }
221
+ }), async (context) => {
222
+ const { projectId } = context.req.valid("param");
223
+ authenticateOrThrow({
224
+ action: "update",
225
+ projectId: void 0,
226
+ resource: "project"
227
+ });
228
+ const data = context.req.valid("form");
229
+ await new ProjectsModel().update(projectId, data);
230
+ if (checkIsHTMLRequest(true)) return context.redirect(urlBuilder.projectDetails(projectId), 303);
231
+ return new Response(null, { status: 202 });
232
+ });
233
+
234
+ //#endregion
235
+ export { projectsRouter };
236
+ //# sourceMappingURL=projects-router.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects-router.mjs","names":["z"],"sources":["../../src/routers/projects-router.ts"],"sourcesContent":["import { createRoute, OpenAPIHono } from \"@hono/zod-openapi\";\nimport { HTTPException } from \"hono/http-exception\";\nimport { z } from \"zod\";\nimport { BuildsModel } from \"../models/builds-model.ts\";\nimport { ProjectsModel } from \"../models/projects-model.ts\";\nimport {\n ProjectCreateSchema,\n ProjectGetResultSchema,\n ProjectIdSchema,\n ProjectsListResultSchema,\n ProjectUpdateSchema,\n} from \"../models/projects-schema.ts\";\nimport { TagsModel } from \"../models/tags-model.ts\";\nimport { urlBuilder } from \"../urls.ts\";\nimport { authenticateOrThrow } from \"../utils/auth.ts\";\nimport { mimes } from \"../utils/mime-utils.ts\";\nimport {\n openapiCommonErrorResponses,\n openapiErrorResponseContent,\n openapiResponseRedirect,\n openapiResponsesHtml,\n} from \"../utils/openapi-utils.ts\";\nimport { checkIsHTMLRequest } from \"../utils/request.ts\";\nimport { getStore } from \"../utils/store.ts\";\nimport { createUIResultResponse } from \"../utils/ui-utils.ts\";\n\nconst projectTag = \"Projects\";\nconst projectIdPathParams = z.object({ projectId: ProjectIdSchema });\n\n/**\n * @private\n */\nexport const projectsRouter = new OpenAPIHono()\n .openapi(\n createRoute({\n summary: \"List projects\",\n method: \"get\",\n path: \"/projects\",\n tags: [projectTag],\n responses: {\n 200: {\n description: \"A list of projects.\",\n content: {\n [mimes.json]: { schema: ProjectsListResultSchema },\n ...openapiResponsesHtml,\n },\n },\n ...openapiCommonErrorResponses,\n },\n }),\n async (context) => {\n const { ui } = getStore();\n\n authenticateOrThrow({\n action: \"read\",\n projectId: undefined,\n resource: \"project\",\n });\n\n const projects = await new ProjectsModel().list();\n\n if (ui?.renderProjectsListPage && checkIsHTMLRequest()) {\n return createUIResultResponse(context, ui.renderProjectsListPage, { projects });\n }\n\n return context.json({ projects });\n },\n )\n .openapi(\n createRoute({\n summary: \"Create project - UI\",\n method: \"get\",\n path: \"/projects/create\",\n tags: [projectTag],\n responses: {\n 200: {\n description: \"UI to create project\",\n content: openapiResponsesHtml,\n },\n ...openapiCommonErrorResponses,\n },\n }),\n (context) => {\n const { ui } = getStore();\n if (!ui?.renderProjectCreatePage) {\n throw new HTTPException(405, { message: \"UI not available for this route.\" });\n }\n\n authenticateOrThrow({\n action: \"create\",\n projectId: undefined,\n resource: \"project\",\n });\n\n return createUIResultResponse(context, ui.renderProjectCreatePage, {});\n },\n )\n .openapi(\n createRoute({\n summary: \"Create project - action\",\n method: \"post\",\n path: \"/projects/create\",\n tags: [projectTag],\n request: {\n body: {\n content: { [mimes.formEncoded]: { schema: ProjectCreateSchema } },\n required: true,\n },\n },\n responses: {\n 201: {\n description: \"Project created successfully\",\n content: { [mimes.json]: { schema: ProjectGetResultSchema } },\n },\n 303: openapiResponseRedirect(\"Redirect to project.\"),\n 409: {\n content: openapiErrorResponseContent,\n description: \"Project already exists.\",\n },\n 415: {\n content: openapiErrorResponseContent,\n description: \"Unsupported Media Type\",\n },\n ...openapiCommonErrorResponses,\n },\n }),\n async (context) => {\n authenticateOrThrow({\n action: \"create\",\n projectId: undefined,\n resource: \"project\",\n });\n const data = context.req.valid(\"form\");\n const project = await new ProjectsModel().create(data);\n\n if (checkIsHTMLRequest(true)) {\n return context.redirect(urlBuilder.projectDetails(project.id), 303);\n }\n\n return context.json({ project });\n },\n )\n .openapi(\n createRoute({\n summary: \"Project details\",\n method: \"get\",\n path: \"/projects/{projectId}\",\n tags: [projectTag],\n request: { params: projectIdPathParams },\n responses: {\n 200: {\n description: \"Details of the project\",\n content: {\n [mimes.json]: { schema: ProjectGetResultSchema },\n ...openapiResponsesHtml,\n },\n },\n 404: {\n description: \"Matching project not found.\",\n content: openapiErrorResponseContent,\n },\n ...openapiCommonErrorResponses,\n },\n }),\n async (context) => {\n const { ui } = getStore();\n const { projectId } = context.req.valid(\"param\");\n\n authenticateOrThrow({\n action: \"read\",\n projectId,\n resource: \"project\",\n });\n\n const project = await new ProjectsModel().get(projectId);\n\n if (ui?.renderProjectDetailsPage && checkIsHTMLRequest()) {\n const recentTags = await new TagsModel(projectId).list({ limit: 10 });\n const recentBuilds = await new BuildsModel(projectId).list({\n limit: 10,\n });\n\n return createUIResultResponse(context, ui.renderProjectDetailsPage, {\n project,\n recentBuilds,\n recentTags,\n });\n }\n\n return context.json({ project });\n },\n )\n .openapi(\n createRoute({\n summary: \"Delete project - action\",\n method: \"post\",\n path: \"/projects/{projectId}/delete\",\n tags: [projectTag],\n request: { params: projectIdPathParams },\n responses: {\n 204: { description: \"Project deleted successfully.\" },\n 303: openapiResponseRedirect(\"Redirect to projects list.\"),\n 404: {\n description: \"Matching project not found.\",\n content: openapiErrorResponseContent,\n },\n ...openapiCommonErrorResponses,\n },\n }),\n async (context) => {\n const { projectId } = context.req.valid(\"param\");\n authenticateOrThrow({\n action: \"delete\",\n projectId,\n resource: \"project\",\n });\n\n await new ProjectsModel().delete(projectId);\n\n if (checkIsHTMLRequest(true)) {\n return context.redirect(urlBuilder.projectsList(), 303);\n }\n\n return new Response(null, { status: 204 });\n },\n )\n .openapi(\n createRoute({\n summary: \"Update project - UI\",\n method: \"get\",\n path: \"/projects/{projectId}/update\",\n tags: [projectTag],\n request: { params: projectIdPathParams },\n responses: {\n 200: {\n description: \"UI to update project\",\n content: openapiResponsesHtml,\n },\n 404: {\n description: \"Matching project not found.\",\n content: openapiErrorResponseContent,\n },\n ...openapiCommonErrorResponses,\n },\n }),\n async (context) => {\n const { ui } = getStore();\n if (!ui?.renderProjectUpdatePage) {\n throw new HTTPException(405, { message: \"UI not available for this route.\" });\n }\n\n const { projectId } = context.req.valid(\"param\");\n authenticateOrThrow({\n action: \"update\",\n projectId,\n resource: \"project\",\n });\n\n const project = await new ProjectsModel().get(projectId);\n\n return createUIResultResponse(context, ui.renderProjectUpdatePage, { project });\n },\n )\n .openapi(\n createRoute({\n summary: \"Update project - action\",\n method: \"post\",\n path: \"/projects/{projectId}/update\",\n tags: [projectTag],\n request: {\n params: projectIdPathParams,\n body: {\n content: { [mimes.formEncoded]: { schema: ProjectUpdateSchema } },\n required: true,\n },\n },\n responses: {\n 202: { description: \"Project updated successfully\" },\n 303: openapiResponseRedirect(\"Redirect to project.\"),\n 404: {\n description: \"Matching project not found.\",\n content: openapiErrorResponseContent,\n },\n 415: {\n content: openapiErrorResponseContent,\n description: \"Unsupported Media Type\",\n },\n ...openapiCommonErrorResponses,\n },\n }),\n async (context) => {\n const { projectId } = context.req.valid(\"param\");\n\n authenticateOrThrow({\n action: \"update\",\n projectId: undefined,\n resource: \"project\",\n });\n\n const data = context.req.valid(\"form\");\n await new ProjectsModel().update(projectId, data);\n\n if (checkIsHTMLRequest(true)) {\n return context.redirect(urlBuilder.projectDetails(projectId), 303);\n }\n\n return new Response(null, { status: 202 });\n },\n );\n"],"mappings":"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"}