studiocms 0.2.0 → 0.4.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 (293) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/dist/cli/add/index.d.ts +2 -2
  3. package/dist/cli/add/index.js +4 -3
  4. package/dist/cli/add/npm-utils.d.ts +6 -6
  5. package/dist/cli/add/tryToInstallPlugins.d.ts +1 -1
  6. package/dist/cli/add/tryToInstallPlugins.js +6 -5
  7. package/dist/cli/add/updateStudioCMSConfig.d.ts +1 -1
  8. package/dist/cli/add/updateStudioCMSConfig.js +3 -4
  9. package/dist/cli/add/validatePlugins.d.ts +1 -2
  10. package/dist/cli/add/validatePlugins.js +15 -9
  11. package/dist/cli/crypto/genJWT/index.d.ts +1 -1
  12. package/dist/cli/crypto/genJWT/index.js +8 -9
  13. package/dist/cli/crypto/index.d.ts +1 -1
  14. package/dist/cli/init/steps/env.js +14 -4
  15. package/dist/cli/init/steps/next.d.ts +1 -1
  16. package/dist/cli/init/steps/next.js +6 -5
  17. package/dist/cli/migrator/index.d.ts +1 -1
  18. package/dist/cli/migrator/index.js +2 -2
  19. package/dist/cli/users/index.d.ts +1 -1
  20. package/dist/cli/users/shared.js +2 -2
  21. package/dist/cli/users/steps/createUsers.js +7 -7
  22. package/dist/cli/users/steps/modifyUsers.js +2 -2
  23. package/dist/cli/users/steps/next.d.ts +1 -1
  24. package/dist/cli/utils/checkRequiredEnvVars.js +2 -2
  25. package/dist/cli/utils/context.d.ts +2 -4
  26. package/dist/cli/utils/context.js +1 -3
  27. package/dist/cli/utils/getCliDbClient.d.ts +1 -1
  28. package/dist/cli/utils/intro.d.ts +1 -1
  29. package/dist/cli/utils/loadConfig.d.ts +54 -49
  30. package/dist/cli/utils/loadConfig.js +5 -8
  31. package/dist/cli/utils/logger.js +3 -3
  32. package/dist/cli/utils/user-utils.d.ts +1 -1
  33. package/dist/cli/utils/user-utils.js +4 -3
  34. package/dist/client/apiClient.d.ts +4923 -0
  35. package/dist/client/apiClient.js +72 -0
  36. package/dist/config.d.ts +1734 -1
  37. package/dist/consts.d.ts +5 -5
  38. package/dist/consts.js +3 -2
  39. package/dist/db/plugins.d.ts +1 -1
  40. package/dist/db/plugins.js +5 -8
  41. package/dist/handlers/frontend/routes.d.ts +4 -18
  42. package/dist/handlers/frontend/routes.js +13 -152
  43. package/dist/handlers/frontend/types.d.ts +1 -1
  44. package/dist/handlers/frontend/utils.js +0 -18
  45. package/dist/handlers/pluginHandler.d.ts +34 -257
  46. package/dist/handlers/pluginHandler.js +92 -46
  47. package/dist/handlers/routeHandler.js +32 -11
  48. package/dist/handlers/setupDbStudio.d.ts +3 -1
  49. package/dist/handlers/setupDbStudio.js +19 -10
  50. package/dist/handlers/storage-manager/core/effectify-astro-context.d.ts +25 -0
  51. package/dist/handlers/storage-manager/core/effectify-astro-context.js +78 -0
  52. package/dist/handlers/storage-manager/no-op.d.ts +2 -2
  53. package/dist/handlers/storage-manager/no-op.js +2 -3
  54. package/dist/index.d.ts +0 -1
  55. package/dist/index.js +10 -20
  56. package/dist/integrations/robots/index.d.ts +2 -2
  57. package/dist/integrations/robots/index.js +1 -3
  58. package/dist/integrations/robots/schema.d.ts +102 -273
  59. package/dist/integrations/robots/schema.js +220 -209
  60. package/dist/plugins/analytics/assets/schemas.d.ts +14 -9
  61. package/dist/plugins/analytics/assets/schemas.js +25 -17
  62. package/dist/plugins/analytics/db-client.d.ts +1 -1
  63. package/dist/plugins/analytics/index.d.ts +823 -3
  64. package/dist/plugins/analytics/index.js +4 -5
  65. package/dist/plugins/analytics/schemas.d.ts +54 -62
  66. package/dist/plugins/analytics/schemas.js +64 -13
  67. package/dist/plugins/analytics/table.d.ts +1 -1
  68. package/dist/plugins.d.ts +0 -1
  69. package/dist/schemas/config/api.d.ts +17 -0
  70. package/dist/schemas/config/api.js +14 -0
  71. package/dist/schemas/config/auth.d.ts +55 -59
  72. package/dist/schemas/config/auth.js +34 -11
  73. package/dist/schemas/config/dashboard.d.ts +43 -79
  74. package/dist/schemas/config/dashboard.js +43 -12
  75. package/dist/schemas/config/db.d.ts +15 -17
  76. package/dist/schemas/config/db.js +13 -5
  77. package/dist/schemas/config/developer.d.ts +33 -45
  78. package/dist/schemas/config/developer.js +22 -5
  79. package/dist/schemas/config/index.d.ts +398 -521
  80. package/dist/schemas/config/index.js +115 -57
  81. package/dist/schemas/config/sdk.d.ts +50 -196
  82. package/dist/schemas/config/sdk.js +61 -73
  83. package/dist/schemas/custom.d.ts +40 -0
  84. package/dist/schemas/custom.js +41 -0
  85. package/dist/schemas/external-schemas.d.ts +171 -0
  86. package/dist/schemas/external-schemas.js +179 -0
  87. package/dist/schemas/index.d.ts +2 -0
  88. package/dist/schemas/index.js +2 -0
  89. package/dist/schemas/plugins/i18n.d.ts +59 -39
  90. package/dist/schemas/plugins/i18n.js +42 -5
  91. package/dist/schemas/plugins/index.d.ts +7126 -10296
  92. package/dist/schemas/plugins/index.js +260 -276
  93. package/dist/schemas/plugins/shared.d.ts +1293 -3718
  94. package/dist/schemas/plugins/shared.js +320 -329
  95. package/dist/test-utils.d.ts +15 -4
  96. package/dist/test-utils.js +27 -11
  97. package/dist/toolbar/db-viewer/db-shared-types.d.ts +6 -6
  98. package/dist/toolbar/db-viewer/studio/connection.d.ts +8 -4
  99. package/dist/toolbar/db-viewer/studio/connection.js +2 -28
  100. package/dist/toolbar/db-viewer/studio/env/libsql.d.ts +7 -0
  101. package/dist/toolbar/db-viewer/studio/env/libsql.js +17 -0
  102. package/dist/toolbar/db-viewer/studio/env/mysql.d.ts +7 -0
  103. package/dist/toolbar/db-viewer/studio/env/mysql.js +23 -0
  104. package/dist/toolbar/db-viewer/studio/env/postgres.d.ts +7 -0
  105. package/dist/toolbar/db-viewer/studio/env/postgres.js +23 -0
  106. package/dist/toolbar/db-viewer/studio/index.js +20 -56
  107. package/dist/toolbar/db-viewer/studio/type.d.ts +1 -2
  108. package/dist/toolbar/db-viewer/studio/virtual-connection/libsql.d.ts +3 -0
  109. package/dist/toolbar/db-viewer/studio/virtual-connection/libsql.js +24 -0
  110. package/dist/toolbar/db-viewer/studio/virtual-connection/mysql.d.ts +3 -0
  111. package/dist/toolbar/db-viewer/studio/virtual-connection/mysql.js +9 -0
  112. package/dist/toolbar/db-viewer/studio/virtual-connection/postgres.d.ts +3 -0
  113. package/dist/toolbar/db-viewer/studio/virtual-connection/postgres.js +9 -0
  114. package/dist/toolbar/db-viewer/viewer.js +20 -21
  115. package/dist/types.d.ts +30 -0
  116. package/dist/utils/effects/smtp.d.ts +1 -1
  117. package/dist/utils/lang-helper.d.ts +10 -2
  118. package/dist/virtual.d.ts +35 -28
  119. package/dist/virtuals/auth/core.d.ts +5 -5
  120. package/dist/virtuals/auth/verify-email.d.ts +6 -6
  121. package/dist/virtuals/components/Generator.astro +2 -2
  122. package/dist/virtuals/components/Renderer.astro +9 -1
  123. package/dist/virtuals/components/renderFn.d.ts +3 -1
  124. package/dist/virtuals/components/renderFn.js +18 -0
  125. package/dist/virtuals/lib/headDefaults.d.ts +4 -2
  126. package/dist/virtuals/lib/headDefaults.js +0 -2
  127. package/dist/virtuals/lib/routeMap.d.ts +0 -12
  128. package/dist/virtuals/lib/routeMap.js +2 -14
  129. package/dist/virtuals/mailer/index.d.ts +3 -3
  130. package/dist/virtuals/notifier/index.d.ts +5 -5
  131. package/dist/virtuals/plugins/dashboard-pages.d.ts +2 -64
  132. package/dist/virtuals/scripts/StorageFileBrowser.d.ts +1 -172
  133. package/dist/virtuals/scripts/StorageFileBrowser.js +216 -119
  134. package/dist/virtuals/template-engine/index.d.ts +4 -4
  135. package/frontend/components/dashboard/configuration/ConfigForm.astro +218 -110
  136. package/frontend/components/dashboard/content-mgmt/ContentSearch.astro +21 -22
  137. package/frontend/components/dashboard/content-mgmt/CreateFolder.astro +66 -54
  138. package/frontend/components/dashboard/content-mgmt/CreatePage.astro +58 -104
  139. package/frontend/components/dashboard/content-mgmt/EditFolder.astro +65 -67
  140. package/frontend/components/dashboard/content-mgmt/EditPage.astro +86 -134
  141. package/frontend/components/dashboard/content-mgmt/InnerSidebarElement.astro +0 -1
  142. package/frontend/components/dashboard/content-mgmt/PageHeader.astro +33 -52
  143. package/frontend/components/dashboard/content-mgmt/PageTypeHandler.astro +2 -2
  144. package/frontend/components/dashboard/profile/APITokens.astro +219 -158
  145. package/frontend/components/dashboard/profile/BasicInfo.astro +165 -106
  146. package/frontend/components/dashboard/profile/Notifications.astro +27 -18
  147. package/frontend/components/dashboard/profile/UpdatePassword.astro +134 -94
  148. package/frontend/components/dashboard/sidebar/VersionCheck.astro +31 -16
  149. package/frontend/components/dashboard/sidebar/VersionCheckChangelog.astro +18 -11
  150. package/frontend/components/dashboard/sidebar-modals/VersionModal.astro +2 -2
  151. package/frontend/components/dashboard/smtp-config/TemplateEditor.astro +14 -14
  152. package/frontend/components/dashboard/taxonomy/InnerSidebarElement.astro +0 -1
  153. package/frontend/components/dashboard/taxonomy/MetaContainer.astro +0 -2
  154. package/frontend/components/dashboard/taxonomy/PageHeader.astro +16 -24
  155. package/frontend/components/dashboard/taxonomy/TaxonomySearch.astro +23 -27
  156. package/frontend/components/dashboard/user-mgmt/InnerSidebarElement.astro +111 -104
  157. package/frontend/components/dashboard/user-mgmt/UserListItem.astro +9 -22
  158. package/frontend/components/dashboard/user-mgmt/UserListItems.astro +18 -0
  159. package/frontend/components/first-time-setup/snippets/{opt2-studiocms.config.diff → studiocms.config.diff} +1 -0
  160. package/frontend/components/shared/Code.astro +1 -4
  161. package/frontend/components/shared/DynamicSettingsRenderer.astro +1 -1
  162. package/frontend/components/shared/SSRUser.astro +2 -4
  163. package/frontend/components/shared/foldertree/FolderTreeNode.astro +0 -6
  164. package/frontend/components/shared/storage-manager/StorageCopyOutput.astro +0 -1
  165. package/frontend/components/shared/taxonomy/TaxonomyTreeNode.astro +0 -6
  166. package/frontend/layouts/DashboardLayout.astro +1 -10
  167. package/frontend/layouts/TaxonomyLayout.astro +0 -1
  168. package/frontend/middleware/index.ts +102 -61
  169. package/frontend/pages/404.astro +5 -9
  170. package/frontend/pages/[dashboard]/[...pluginPage].astro +10 -1
  171. package/frontend/pages/[dashboard]/configuration.astro +10 -1
  172. package/frontend/pages/[dashboard]/content-management/createfolder.astro +10 -1
  173. package/frontend/pages/[dashboard]/content-management/createpage.astro +10 -1
  174. package/frontend/pages/[dashboard]/content-management/diff.astro +39 -14
  175. package/frontend/pages/[dashboard]/content-management/editfolder.astro +10 -1
  176. package/frontend/pages/[dashboard]/content-management/editpage.astro +10 -1
  177. package/frontend/pages/[dashboard]/content-management/index.astro +10 -1
  178. package/frontend/pages/[dashboard]/index.astro +10 -1
  179. package/frontend/pages/[dashboard]/login.astro +86 -25
  180. package/frontend/pages/[dashboard]/password-reset.astro +22 -16
  181. package/frontend/pages/[dashboard]/plugins/[plugin].astro +10 -1
  182. package/frontend/pages/[dashboard]/profile.astro +10 -1
  183. package/frontend/pages/[dashboard]/signup.astro +153 -52
  184. package/frontend/pages/[dashboard]/smtp-configuration.astro +77 -75
  185. package/frontend/pages/[dashboard]/system-management.astro +10 -1
  186. package/frontend/pages/[dashboard]/taxonomy/categories.astro +30 -41
  187. package/frontend/pages/[dashboard]/taxonomy/index.astro +10 -0
  188. package/frontend/pages/[dashboard]/taxonomy/tags.astro +33 -43
  189. package/frontend/pages/[dashboard]/unverified-email.astro +29 -21
  190. package/frontend/pages/[dashboard]/user-management/edit.astro +170 -90
  191. package/frontend/pages/[dashboard]/user-management/index.astro +10 -1
  192. package/frontend/pages/studiocms_api/[...all].ts +106 -0
  193. package/frontend/pages/studiocms_api/_handlers/_utils/auth.ts +26 -0
  194. package/frontend/pages/studiocms_api/_handlers/_utils/changelog.ts +147 -0
  195. package/frontend/pages/studiocms_api/_handlers/_utils/db-studio-driver.ts +46 -0
  196. package/frontend/pages/studiocms_api/_handlers/_utils/parseLogLevel.ts +27 -0
  197. package/frontend/pages/studiocms_api/_handlers/auth/auth.ts +459 -0
  198. package/frontend/pages/studiocms_api/_handlers/auth/index.ts +17 -0
  199. package/frontend/pages/studiocms_api/_handlers/auth/oauth.ts +91 -0
  200. package/frontend/pages/studiocms_api/_handlers/dashboard/_shared.ts +57 -0
  201. package/frontend/pages/studiocms_api/_handlers/dashboard/apiTokens.ts +134 -0
  202. package/frontend/pages/studiocms_api/_handlers/dashboard/config.ts +64 -0
  203. package/frontend/pages/studiocms_api/_handlers/dashboard/content.ts +741 -0
  204. package/frontend/pages/studiocms_api/_handlers/dashboard/create.ts +480 -0
  205. package/frontend/pages/studiocms_api/_handlers/dashboard/emailNotifications.ts +49 -0
  206. package/frontend/pages/studiocms_api/_handlers/dashboard/index.ts +45 -0
  207. package/frontend/pages/studiocms_api/_handlers/dashboard/mailer.ts +136 -0
  208. package/frontend/pages/studiocms_api/_handlers/dashboard/plugins.ts +80 -0
  209. package/frontend/pages/studiocms_api/_handlers/dashboard/profile.ts +275 -0
  210. package/frontend/pages/studiocms_api/_handlers/dashboard/resetPassword.ts +140 -0
  211. package/frontend/pages/studiocms_api/_handlers/dashboard/search.ts +63 -0
  212. package/frontend/pages/studiocms_api/_handlers/dashboard/taxonomy.ts +285 -0
  213. package/frontend/pages/studiocms_api/_handlers/dashboard/templates.ts +75 -0
  214. package/frontend/pages/studiocms_api/_handlers/dashboard/users.ts +312 -0
  215. package/frontend/pages/studiocms_api/_handlers/dashboard/verifyEndpoints.ts +307 -0
  216. package/frontend/pages/studiocms_api/_handlers/integration/dbStudio.ts +98 -0
  217. package/frontend/pages/studiocms_api/_handlers/integration/index.ts +17 -0
  218. package/frontend/pages/studiocms_api/_handlers/integration/storageManager.ts +107 -0
  219. package/frontend/pages/studiocms_api/_handlers/rest-api/index.ts +8 -0
  220. package/frontend/pages/studiocms_api/_handlers/rest-api/v1/_shared.ts +41 -0
  221. package/frontend/pages/studiocms_api/_handlers/rest-api/v1/index.ts +17 -0
  222. package/frontend/pages/studiocms_api/_handlers/rest-api/v1/public.ts +195 -0
  223. package/frontend/pages/studiocms_api/_handlers/rest-api/v1/secure.ts +1726 -0
  224. package/frontend/pages/studiocms_api/_handlers/sdk.ts +129 -0
  225. package/frontend/pages/studiocms_api/_middleware/astroLocals.ts +36 -0
  226. package/frontend/pages/studiocms_api/_middleware/restApi.ts +56 -0
  227. package/frontend/pages/studiocms_api/integrations/[...all].ts +8 -0
  228. package/frontend/scripts/formdata.ts +219 -0
  229. package/frontend/setup-pages/3-done.astro +4 -20
  230. package/frontend/setup-pages/studiocms_api/dashboard/step-2.ts +3 -6
  231. package/frontend/styles/dashboard-base.css +0 -1
  232. package/frontend/web-vitals/endpoint.ts +2 -1
  233. package/package.json +35 -31
  234. package/dist/global.d.ts +0 -9
  235. package/frontend/components/dashboard/LoginChecker.astro +0 -68
  236. package/frontend/components/dashboard/user-mgmt/RankCheck.astro +0 -57
  237. package/frontend/components/first-time-setup/snippets/opt1-astro.config.diff +0 -14
  238. package/frontend/components/first-time-setup/snippets/opt2-astro.config.diff +0 -9
  239. package/frontend/middleware/_authmap.ts +0 -63
  240. package/frontend/pages/studiocms_api/auth/[path].ts +0 -390
  241. package/frontend/pages/studiocms_api/auth/[provider]/[...id].ts +0 -64
  242. package/frontend/pages/studiocms_api/auth/[provider]/_effects/index.ts +0 -101
  243. package/frontend/pages/studiocms_api/auth/_shared.ts +0 -52
  244. package/frontend/pages/studiocms_api/dashboard/api-tokens.ts +0 -115
  245. package/frontend/pages/studiocms_api/dashboard/config.ts +0 -74
  246. package/frontend/pages/studiocms_api/dashboard/content/diff.ts +0 -73
  247. package/frontend/pages/studiocms_api/dashboard/content/folder.ts +0 -220
  248. package/frontend/pages/studiocms_api/dashboard/content/page.ts +0 -359
  249. package/frontend/pages/studiocms_api/dashboard/create-reset-link.ts +0 -77
  250. package/frontend/pages/studiocms_api/dashboard/create-user-invite.ts +0 -231
  251. package/frontend/pages/studiocms_api/dashboard/create-user.ts +0 -186
  252. package/frontend/pages/studiocms_api/dashboard/email-notification-settings-site.ts +0 -74
  253. package/frontend/pages/studiocms_api/dashboard/mailer/check-email.ts +0 -75
  254. package/frontend/pages/studiocms_api/dashboard/mailer/config.ts +0 -136
  255. package/frontend/pages/studiocms_api/dashboard/plugins/[plugin].ts +0 -80
  256. package/frontend/pages/studiocms_api/dashboard/profile.ts +0 -245
  257. package/frontend/pages/studiocms_api/dashboard/resend-verify-email.ts +0 -77
  258. package/frontend/pages/studiocms_api/dashboard/reset-password.ts +0 -124
  259. package/frontend/pages/studiocms_api/dashboard/search-list.ts +0 -59
  260. package/frontend/pages/studiocms_api/dashboard/taxonomy-search.ts +0 -47
  261. package/frontend/pages/studiocms_api/dashboard/taxonomy.ts +0 -230
  262. package/frontend/pages/studiocms_api/dashboard/templates.ts +0 -74
  263. package/frontend/pages/studiocms_api/dashboard/update-user-notifications.ts +0 -86
  264. package/frontend/pages/studiocms_api/dashboard/users.ts +0 -236
  265. package/frontend/pages/studiocms_api/dashboard/verify-email.ts +0 -83
  266. package/frontend/pages/studiocms_api/dashboard/verify-session.ts +0 -187
  267. package/frontend/pages/studiocms_api/integrations/[type]/[...id].ts +0 -15
  268. package/frontend/pages/studiocms_api/integrations/[type]/_routes/db-studio.ts +0 -173
  269. package/frontend/pages/studiocms_api/integrations/[type]/_routes/storage.ts +0 -88
  270. package/frontend/pages/studiocms_api/partials/editor.astro +0 -74
  271. package/frontend/pages/studiocms_api/partials/render.astro +0 -39
  272. package/frontend/pages/studiocms_api/partials/user-list-items.astro +0 -43
  273. package/frontend/pages/studiocms_api/rest/utils/auth-token.ts +0 -59
  274. package/frontend/pages/studiocms_api/rest/v1/[type]/[...id].ts +0 -23
  275. package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/categories.ts +0 -267
  276. package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/folders.ts +0 -283
  277. package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/pages.ts +0 -505
  278. package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/settings.ts +0 -100
  279. package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/tags.ts +0 -229
  280. package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/users.ts +0 -553
  281. package/frontend/pages/studiocms_api/rest/v1/public/[type]/[...id].ts +0 -19
  282. package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/categories.ts +0 -74
  283. package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/folders.ts +0 -85
  284. package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/pages.ts +0 -103
  285. package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/tags.ts +0 -67
  286. package/frontend/pages/studiocms_api/sdk/[...path].ts +0 -97
  287. package/frontend/pages/studiocms_api/sdk/utils/changelog.ts +0 -119
  288. package/frontend/scripts/auth/formListener.ts +0 -81
  289. package/frontend/scripts/formdata-utils.ts +0 -116
  290. package/frontend/utils/build-partial-schema.ts +0 -46
  291. package/frontend/utils/errors.ts +0 -6
  292. package/frontend/utils/param-extractor.ts +0 -83
  293. package/frontend/utils/rest-router.ts +0 -444
@@ -1,46 +0,0 @@
1
- import { Schema } from 'effect';
2
-
3
- /**
4
- * Type guard to check if a schema is of type Schema.Struct with all fields of type Schema.All.
5
- *
6
- * @param schema - The schema to check
7
- * @returns True if the schema is of type Schema.All, false otherwise
8
- */
9
- export const isSchemaAll = <T extends Schema.Struct.Fields>(
10
- schema: unknown
11
- ): schema is Schema.Struct<T> => {
12
- return schema instanceof Schema.Struct;
13
- };
14
-
15
- /**
16
- * Type helper to create a partial schema type with all fields optional.
17
- */
18
- export type PartialSchema<T extends Schema.Struct.Fields> = {
19
- [K in keyof T]: T[K] extends Schema.Schema.All ? Schema.optional<T[K]> : never;
20
- };
21
-
22
- /**
23
- * Builds a partial schema from a given base schema, making all fields optional.
24
- *
25
- * @template Fields - The fields of the base schema
26
- * @param base - The base schema to build the partial schema from
27
- * @returns A new schema with all fields optional
28
- */
29
- export const buildPartialSchema = <const Fields extends Schema.Struct.Fields>(
30
- base: Schema.Struct<Fields>
31
- ) => {
32
- // Construct a new schema with all fields optional except 'id'
33
- const partialFields = Object.entries(base.fields).reduce(
34
- (acc, [key, entry]) => {
35
- if (isSchemaAll(entry)) {
36
- const safeKey = key as keyof Fields;
37
- // biome-ignore lint/suspicious/noExplicitAny: cursed magic
38
- (acc as any)[safeKey] = Schema.optional(entry);
39
- }
40
- return acc;
41
- },
42
- {} as PartialSchema<Fields>
43
- );
44
-
45
- return Schema.Struct(partialFields) as Schema.Struct<PartialSchema<Fields>>;
46
- };
@@ -1,6 +0,0 @@
1
- import { Data } from 'effect';
2
-
3
- export class StudioCMSAPIError extends Data.TaggedError('StudioCMSAPIError')<{
4
- message: string;
5
- cause?: unknown;
6
- }> {}
@@ -1,83 +0,0 @@
1
- import type { APIContext } from 'astro';
2
- import { Effect, Either, ParseResult, Schema } from 'effect';
3
- import type { ParseOptions } from 'effect/SchemaAST';
4
-
5
- /**
6
- * Type definition for the parameter extraction function.
7
- */
8
- export type ExtractEndpointParams = <T, E>(
9
- schema: Schema.Schema<T, E, never>,
10
- overrideOptions?: ParseOptions
11
- ) => <EE>(
12
- callback: (params: T, ctx: APIContext) => Effect.Effect<Response, EE, never>
13
- ) => (ctx: APIContext) => Effect.Effect<Response, EE, never>;
14
-
15
- /**
16
- * Creates a higher-order function that extracts and validates route parameters from an API context.
17
- *
18
- * This utility function takes a schema and optional parse options, returning a function that accepts
19
- * a callback. The returned function validates the API context parameters against the provided schema
20
- * and passes the decoded parameters to the callback.
21
- *
22
- * @template A - The type of the successfully decoded schema output
23
- * @template E - The type of the schema encoding/input
24
- *
25
- * @param schema - An Effect Schema used to validate and decode the context parameters
26
- * @param overrideOptions - Optional parse options to customize the schema decoding behavior
27
- *
28
- * @returns A function that takes a callback accepting validated parameters and returns another
29
- * function that processes an APIContext, returning an Effect with a Response
30
- *
31
- * @example
32
- * ```typescript
33
- * const paramsSchema = Schema.Struct({
34
- * id: Schema.String,
35
- * page: Schema.Number
36
- * });
37
- *
38
- * const handler = extractParams(paramsSchema)((params) =>
39
- * Effect.succeed(new Response(JSON.stringify(params)))
40
- * );
41
- * ```
42
- */
43
- export const extractParams: ExtractEndpointParams = <T, E>(
44
- schema: Schema.Schema<T, E, never>,
45
- overrideOptions?: ParseOptions
46
- ) => {
47
- // Return a function that takes a callback with the validated parameters
48
- return <EE>(callback: (params: T, ctx: APIContext) => Effect.Effect<Response, EE, never>) => {
49
- // Return a function that processes the APIContext
50
- return (ctx: APIContext): Effect.Effect<Response, EE, never> => {
51
- // get user override options or default to empty object
52
- const userSchemaOpts = overrideOptions ?? {};
53
-
54
- // merge user options with default error handling options
55
- const schemaOpts: ParseOptions = {
56
- errors: 'all',
57
- ...userSchemaOpts,
58
- };
59
-
60
- // decode and validate the context parameters using the provided schema and options
61
- const data = Schema.decodeUnknownEither(schema, schemaOpts)(ctx.params);
62
-
63
- // If decoding fails, format and return the errors in a Response
64
- if (Either.isLeft(data)) {
65
- const parsedErrors = ParseResult.ArrayFormatter.formatErrorSync(data.left);
66
-
67
- return Effect.succeed(
68
- new Response(
69
- JSON.stringify({ error: 'API Parameters Decoding Error', errors: parsedErrors }),
70
- {
71
- status: 400,
72
- statusText: 'Bad Request',
73
- headers: { 'Content-Type': 'application/json' },
74
- }
75
- )
76
- );
77
- }
78
-
79
- // If decoding is successful, invoke the callback with the validated parameters
80
- return callback(data.right, ctx);
81
- };
82
- };
83
- };
@@ -1,444 +0,0 @@
1
- import {
2
- AllResponse,
3
- createEffectAPIRoute,
4
- createJsonResponse,
5
- genLogger,
6
- type HTTPMethod,
7
- } from '@withstudiocms/effect';
8
- import type { APIContext, APIRoute } from 'astro';
9
- import { Effect, type ParseResult, Schema, type SchemaAST } from 'effect';
10
- import type { NonEmptyReadonlyArray } from 'effect/Array';
11
- import { StudioCMSAPIError } from './errors.js';
12
- import { extractParams } from './param-extractor.js';
13
-
14
- /**
15
- * An entry in the route registry defining handlers for a specific ID type.
16
- *
17
- * @template IdType - The type of ID used in the routes, either 'number' or 'string'
18
- *
19
- * @remarks
20
- * This type defines a structure for registering API route handlers based on
21
- * the type of ID used. It includes a mapping for general routes (`__index`)
22
- * and a function to retrieve handlers for specific IDs.
23
- *
24
- * @example
25
- * ```typescript
26
- * const userRouteEntry: RegistryEntry<'number'> = {
27
- * __idType: 'number',
28
- * __index: { GET: async (ctx) => { ... } },
29
- * id: (id: number) => ({ GET: async (ctx) => { ... } })
30
- * };
31
- * ```
32
- */
33
- export type RegistryEntry<IdType extends 'number' | 'string' = 'number'> = {
34
- __idType: IdType;
35
- __index: Partial<Record<HTTPMethod | 'ALL', APIRoute>>;
36
- id?: (
37
- id: IdType extends 'number' ? number : string
38
- ) => Partial<Record<HTTPMethod | 'ALL', APIRoute>>;
39
- };
40
-
41
- /**
42
- * A union type representing an endpoint route configuration.
43
- *
44
- * @remarks
45
- * This type can either be a `RegistryEntry` configured for numeric IDs
46
- * or for string IDs, allowing flexibility in defining API endpoint routes.
47
- *
48
- * @example
49
- * ```typescript
50
- * const numericRoute: EndpointRoute = {
51
- * __idType: 'number',
52
- * __index: { GET: async (ctx) => { ... } },
53
- * id: (id: number) => ({ GET: async (ctx) => { ... } })
54
- * };
55
- *
56
- * const stringRoute: EndpointRoute = {
57
- * __idType: 'string',
58
- * __index: { GET: async (ctx) => { ... } },
59
- * id: (id: string) => ({ GET: async (ctx) => { ... } })
60
- * };
61
- * ```
62
- */
63
- export type EndpointRoute = RegistryEntry<'number'> | RegistryEntry<'string'>;
64
-
65
- /**
66
- * A registry mapping route types to their corresponding endpoint routes.
67
- *
68
- * @remarks
69
- * This type defines a flexible registry structure where each key represents
70
- * a route type identifier (string) and maps to an `EndpointRoute` configuration.
71
- * It allows dynamic registration and lookup of API endpoint routes.
72
- *
73
- * @example
74
- * ```typescript
75
- * const routes: RouteRegistry = {
76
- * 'user': userEndpointRoute,
77
- * 'posts': postsEndpointRoute
78
- * };
79
- * ```
80
- */
81
- export type RouteRegistry = {
82
- [type: string]: EndpointRoute;
83
- };
84
-
85
- /**
86
- * A function type that defines a sub-page router for REST API endpoints.
87
- *
88
- * @param id - The unique identifier for the route or resource
89
- * @param params - Optional record of string key-value pairs representing route parameters
90
- * @returns A partial record mapping HTTP methods (or 'ALL' for all methods) to their corresponding API route handlers
91
- *
92
- * @example
93
- * ```typescript
94
- * const myRouter: SubPageRouter = (id, params) => ({
95
- * GET: async (context) => { ... },
96
- * POST: async (context) => { ... }
97
- * });
98
- * ```
99
- */
100
- export type SubPageRouter = (
101
- id: string,
102
- params?: Record<string, string>
103
- ) => Partial<Record<HTTPMethod | 'ALL', APIRoute>>;
104
-
105
- /**
106
- * A record type mapping sub-path strings to their corresponding sub-page router functions.
107
- *
108
- * @remarks
109
- * This type defines a structure for associating specific sub-paths with their
110
- * respective router functions, enabling modular handling of REST API endpoints.
111
- *
112
- * @example
113
- * ```typescript
114
- * const subRouters: SubPathRouter = {
115
- * 'history': (id) => ({ GET: async (ctx) => { ... } }),
116
- * 'comments': (id) => ({ POST: async (ctx) => { ... } })
117
- * };
118
- * ```
119
- */
120
- export type SubPathRouter = Record<string, SubPageRouter>;
121
-
122
- const firstLetterUppercase = (str: string) => {
123
- return str.charAt(0).toUpperCase() + str.slice(1);
124
- };
125
-
126
- function isNumber(value: unknown): value is number {
127
- return typeof value === 'number' && !Number.isNaN(value);
128
- }
129
-
130
- function isString(value: unknown): value is string {
131
- return typeof value === 'string';
132
- }
133
-
134
- /**
135
- * A router function that handles both root paths and sub-paths for REST API endpoints.
136
- *
137
- * @param id - The unique identifier for the route or resource, which may include sub-paths
138
- * @param rootRoute - A function that generates the root route handlers based on the provided ID
139
- * @param subPathRouter - A record mapping sub-path strings to their corresponding sub-page router functions
140
- * @returns A partial record mapping HTTP methods (or 'ALL' for all methods) to their corresponding API route handlers
141
- *
142
- * @example
143
- * ```typescript
144
- * const router = idOrPathRouter(
145
- * '123/history/456',
146
- * (id) => ({ GET: async (ctx) => { ... } }),
147
- * {
148
- * 'history': (id) => ({ GET: async (ctx) => { ... } }),
149
- * 'history/[diffid]': (id, params) => ({ GET: async (ctx) => { ... } })
150
- * }
151
- * );
152
- * ```
153
- */
154
- export function idOrPathRouter(
155
- id: string,
156
- rootRoute: (id: string) => Partial<Record<HTTPMethod | 'ALL', APIRoute>>,
157
- subPathRouter: SubPathRouter
158
- ): Partial<Record<HTTPMethod | 'ALL', APIRoute>> {
159
- // Handle root path (e.g., /[id])
160
- if (!id.includes('/')) {
161
- return rootRoute(id);
162
- }
163
-
164
- // Handle sub-paths
165
- const parts = id.split('/'); // /[id]/subpath/...
166
-
167
- // parse id and subpath
168
- const pageId = parts[0];
169
- const subPath = parts.slice(1).join('/');
170
-
171
- // Find sub-router (e.g., /[id]/history)
172
- const subRouter = subPathRouter[subPath];
173
- if (subRouter) {
174
- const router = subRouter(pageId);
175
- return router;
176
- }
177
-
178
- // if no sub-router found, look for sub-paths with parameters (e.g., /[id]/history/[diffid])
179
- for (const key in subPathRouter) {
180
- if (key.includes('[') && key.includes(']')) {
181
- // create a regex to match the pattern
182
- const pattern = key.replace(/\[([^\]]+)\]/g, '([^/]+)');
183
- const regex = new RegExp(`^${pattern}$`);
184
- const match = subPath.match(regex);
185
- if (match) {
186
- const params: Record<string, string> = {};
187
- const paramNames = Array.from(key.matchAll(/\[([^\]]+)\]/g)).map((m) => m[1]);
188
- paramNames.forEach((name, index) => {
189
- params[name] = match[index + 1];
190
- });
191
- const router = subPathRouter[key](pageId, params);
192
- return router;
193
- }
194
- }
195
- }
196
-
197
- // If no matching sub-router found, return an empty router
198
- return {};
199
- }
200
-
201
- /**
202
- * Processes an API route handler and returns the corresponding response.
203
- *
204
- * @param handler - The API route handler function to be executed
205
- * @param ctx - The API context containing request and other relevant information
206
- * @param path - The path of the API route being processed
207
- * @param method - The HTTP method of the request (e.g., GET, POST)
208
- * @returns An Effect that resolves to a Response object
209
- *
210
- * @remarks
211
- * This function executes the provided API route handler within an Effect context,
212
- * handling any errors that may occur during execution. If the handler is undefined,
213
- * it returns an AllResponse. In case of errors, it logs the error and returns a
214
- * standardized JSON response indicating an internal server error.
215
- *
216
- * @example
217
- * ```typescript
218
- * const responseEffect = processHandler(myHandler, apiContext, '/my-path', 'GET');
219
- * ```
220
- */
221
- const processHandler = Effect.fn('processHandler')(function* (
222
- handler: APIRoute | undefined,
223
- ctx: APIContext,
224
- path: string,
225
- method: string
226
- ): Effect.fn.Return<Response, never, never> {
227
- if (!handler) {
228
- return AllResponse();
229
- }
230
-
231
- const response = yield* Effect.tryPromise({
232
- try: async () => await handler(ctx),
233
- catch: (error) =>
234
- new StudioCMSAPIError({
235
- message: `Error in handler for path ${path} [${method}]: ${String(error)}`,
236
- cause: error,
237
- }),
238
- }).pipe(
239
- Effect.catchAll((error) =>
240
- Effect.logError(`API Route Error: ${String(error)}`).pipe(
241
- Effect.as(createJsonResponse({ error: 'Internal Server Error' }, { status: 500 }))
242
- )
243
- )
244
- );
245
-
246
- return response;
247
- });
248
-
249
- /**
250
- * Creates a REST router that handles HTTP requests based on route type and optional ID parameters.
251
- *
252
- * @template Literals - A non-empty readonly array of literal values representing valid route types
253
- *
254
- * @param prefix - A prefix string used for logging purposes to identify the route group
255
- * @param types - A Schema.Literal containing the valid type literals that can be used in the routes
256
- * @param registry - A RouteRegistry object mapping route types to their handler configurations
257
- *
258
- * @returns An Effect API route handler that:
259
- * - Extracts and validates `type` and `id` parameters from the request
260
- * - Determines the appropriate handler based on the route type and ID
261
- * - Supports both numeric and string ID types based on the route configuration
262
- * - Handles special `__index` routes when no ID is provided
263
- * - Dispatches requests to the appropriate HTTP method handler (GET, POST, etc.) or ALL handler
264
- * - Returns appropriate error responses for invalid IDs or missing handlers
265
- * - Logs errors and returns 500 status for handler execution failures
266
- *
267
- * @example
268
- * ```typescript
269
- * const router = createRestRouter(
270
- * 'api',
271
- * Schema.Literal('users', 'posts'),
272
- * routeRegistry
273
- * );
274
- * ```
275
- */
276
- export const createRestRouter = <
277
- const Literals extends NonEmptyReadonlyArray<SchemaAST.LiteralValue>,
278
- >(
279
- prefix: string,
280
- types: Schema.Literal<Literals>,
281
- registry: RouteRegistry
282
- ) => {
283
- const paramSchema = Schema.Struct({
284
- type: types,
285
- id: Schema.optional(Schema.String),
286
- }).annotations({
287
- identifier: 'TypeParams',
288
- parseIssueTitle: ({ actual }: ParseResult.ParseIssue) => {
289
- if (Schema.is(paramSchema)(actual)) {
290
- return `Type: ${
291
- // biome-ignore lint/style/noNonNullAssertion: we know it's defined here
292
- firstLetterUppercase(actual.type!.toString())
293
- }`;
294
- }
295
- },
296
- });
297
-
298
- return createEffectAPIRoute(
299
- extractParams(paramSchema)(({ type, id = '__index' }, ctx) =>
300
- genLogger(`${prefix}:${type}${id !== '__index' ? `:${id}` : ''}:${ctx.request.method}`)(
301
- function* () {
302
- const method = ctx.request.method.toUpperCase() as keyof Record<
303
- HTTPMethod | 'ALL',
304
- APIRoute
305
- >;
306
- const routeGroup = registry[type as string];
307
-
308
- let handlers: Partial<Record<HTTPMethod | 'ALL', APIRoute>> | undefined;
309
-
310
- switch (routeGroup.__idType) {
311
- case 'number': {
312
- if (id === '__index') {
313
- handlers = routeGroup.__index;
314
- } else {
315
- const numericId = Number(id);
316
- if (!isNumber(numericId)) {
317
- return createJsonResponse(
318
- { error: `Invalid ID for type ${type}: ${id}` },
319
- { status: 400 }
320
- );
321
- }
322
- handlers = routeGroup.id ? routeGroup.id(numericId) : undefined;
323
- }
324
- break;
325
- }
326
- case 'string': {
327
- if (id === '__index') {
328
- handlers = routeGroup.__index;
329
- } else {
330
- if (!isString(id)) {
331
- return createJsonResponse(
332
- { error: `Invalid ID for type ${type}: ${id}` },
333
- { status: 400 }
334
- );
335
- }
336
- handlers = routeGroup.id ? routeGroup.id(id) : undefined;
337
- }
338
- break;
339
- }
340
- }
341
-
342
- const handler = handlers ? handlers[method] || handlers.ALL : undefined;
343
-
344
- return yield* processHandler(handler, ctx, `${type}/${id}`, method);
345
- }
346
- )
347
- )
348
- );
349
- };
350
-
351
- /**
352
- * Creates a simple path-based router for REST API endpoints.
353
- *
354
- * @param prefix - A prefix string used for logging purposes to identify the route group
355
- * @param subPathRouter - A record mapping sub-path strings to their corresponding partial API route handlers
356
- *
357
- * @returns An Effect API route handler that:
358
- * - Extracts and validates the `path` parameter from the request
359
- * - Determines the appropriate handler based on the specified sub-path
360
- * - Dispatches requests to the appropriate HTTP method handler (GET, POST, etc.) or ALL handler
361
- * - Returns an AllResponse if no matching handler is found
362
- * - Logs errors and returns 500 status for handler execution failures
363
- *
364
- * @example
365
- * ```typescript
366
- * const router = createSimplePathRouter(
367
- * 'api',
368
- * {
369
- * 'status': { GET: async (ctx) => { ... } },
370
- * 'info': { ALL: async (ctx) => { ... } }
371
- * }
372
- * );
373
- * ```
374
- */
375
- export const createSimplePathRouter = (
376
- prefix: string,
377
- router: Record<string, Partial<Record<HTTPMethod | 'ALL', APIRoute>>>
378
- ) => {
379
- const pathSchema = Schema.Struct({
380
- path: Schema.transform(Schema.UndefinedOr(Schema.String), Schema.String, {
381
- strict: true,
382
- decode: (value) => (value === undefined || value === '' ? '__index' : value),
383
- encode: (value) => value,
384
- }),
385
- }).annotations({
386
- identifier: 'PathParams',
387
- parseIssueTitle: ({ actual }: ParseResult.ParseIssue) => {
388
- if (Schema.is(pathSchema)(actual)) {
389
- return `Path: ${firstLetterUppercase(actual.path?.toString() || 'index')}`;
390
- }
391
- },
392
- });
393
-
394
- return createEffectAPIRoute(
395
- extractParams(pathSchema)(({ path }, ctx) =>
396
- genLogger(`${prefix}:${path}:${ctx.request.method}`)(function* () {
397
- const method = ctx.request.method.toUpperCase() as keyof Record<
398
- HTTPMethod | 'ALL',
399
- APIRoute
400
- >;
401
-
402
- const handlers = router[path];
403
-
404
- if (!handlers) {
405
- return AllResponse();
406
- }
407
-
408
- const handler = handlers[method] || handlers.ALL;
409
-
410
- return yield* processHandler(handler, ctx, path, method);
411
- })
412
- )
413
- );
414
- };
415
-
416
- /**
417
- * Retrieves the router handlers for a specific ID from a sub-path router.
418
- *
419
- * @param id - The unique identifier for the route or resource
420
- * @param subPathRouter - A record mapping sub-path strings to their corresponding sub-page router functions
421
- * @returns A partial record mapping HTTP methods (or 'ALL' for all methods) to their corresponding API route handlers
422
- *
423
- * @example
424
- * ```typescript
425
-
426
- * const handlers = pathRouter(
427
- * '123',
428
- * {
429
- * 'history': (id) => ({ GET: async (ctx) => { ... } }),
430
- * 'comments': (id) => ({ POST: async (ctx) => { ... } })
431
- * }
432
- * );
433
- * ```
434
- */
435
- export function pathRouter(
436
- id: string,
437
- subPathRouter: SubPathRouter
438
- ): Partial<Record<HTTPMethod | 'ALL', APIRoute>> {
439
- // Look for sub-router (e.g., /history)
440
- if (!subPathRouter[id]) return {};
441
-
442
- // Return the router for the specified ID
443
- return subPathRouter[id](id);
444
- }