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.
- package/CHANGELOG.md +122 -0
- package/dist/cli/add/index.d.ts +2 -2
- package/dist/cli/add/index.js +4 -3
- package/dist/cli/add/npm-utils.d.ts +6 -6
- package/dist/cli/add/tryToInstallPlugins.d.ts +1 -1
- package/dist/cli/add/tryToInstallPlugins.js +6 -5
- package/dist/cli/add/updateStudioCMSConfig.d.ts +1 -1
- package/dist/cli/add/updateStudioCMSConfig.js +3 -4
- package/dist/cli/add/validatePlugins.d.ts +1 -2
- package/dist/cli/add/validatePlugins.js +15 -9
- package/dist/cli/crypto/genJWT/index.d.ts +1 -1
- package/dist/cli/crypto/genJWT/index.js +8 -9
- package/dist/cli/crypto/index.d.ts +1 -1
- package/dist/cli/init/steps/env.js +14 -4
- package/dist/cli/init/steps/next.d.ts +1 -1
- package/dist/cli/init/steps/next.js +6 -5
- package/dist/cli/migrator/index.d.ts +1 -1
- package/dist/cli/migrator/index.js +2 -2
- package/dist/cli/users/index.d.ts +1 -1
- package/dist/cli/users/shared.js +2 -2
- package/dist/cli/users/steps/createUsers.js +7 -7
- package/dist/cli/users/steps/modifyUsers.js +2 -2
- package/dist/cli/users/steps/next.d.ts +1 -1
- package/dist/cli/utils/checkRequiredEnvVars.js +2 -2
- package/dist/cli/utils/context.d.ts +2 -4
- package/dist/cli/utils/context.js +1 -3
- package/dist/cli/utils/getCliDbClient.d.ts +1 -1
- package/dist/cli/utils/intro.d.ts +1 -1
- package/dist/cli/utils/loadConfig.d.ts +54 -49
- package/dist/cli/utils/loadConfig.js +5 -8
- package/dist/cli/utils/logger.js +3 -3
- package/dist/cli/utils/user-utils.d.ts +1 -1
- package/dist/cli/utils/user-utils.js +4 -3
- package/dist/client/apiClient.d.ts +4923 -0
- package/dist/client/apiClient.js +72 -0
- package/dist/config.d.ts +1734 -1
- package/dist/consts.d.ts +5 -5
- package/dist/consts.js +3 -2
- package/dist/db/plugins.d.ts +1 -1
- package/dist/db/plugins.js +5 -8
- package/dist/handlers/frontend/routes.d.ts +4 -18
- package/dist/handlers/frontend/routes.js +13 -152
- package/dist/handlers/frontend/types.d.ts +1 -1
- package/dist/handlers/frontend/utils.js +0 -18
- package/dist/handlers/pluginHandler.d.ts +34 -257
- package/dist/handlers/pluginHandler.js +92 -46
- package/dist/handlers/routeHandler.js +32 -11
- package/dist/handlers/setupDbStudio.d.ts +3 -1
- package/dist/handlers/setupDbStudio.js +19 -10
- package/dist/handlers/storage-manager/core/effectify-astro-context.d.ts +25 -0
- package/dist/handlers/storage-manager/core/effectify-astro-context.js +78 -0
- package/dist/handlers/storage-manager/no-op.d.ts +2 -2
- package/dist/handlers/storage-manager/no-op.js +2 -3
- package/dist/index.d.ts +0 -1
- package/dist/index.js +10 -20
- package/dist/integrations/robots/index.d.ts +2 -2
- package/dist/integrations/robots/index.js +1 -3
- package/dist/integrations/robots/schema.d.ts +102 -273
- package/dist/integrations/robots/schema.js +220 -209
- package/dist/plugins/analytics/assets/schemas.d.ts +14 -9
- package/dist/plugins/analytics/assets/schemas.js +25 -17
- package/dist/plugins/analytics/db-client.d.ts +1 -1
- package/dist/plugins/analytics/index.d.ts +823 -3
- package/dist/plugins/analytics/index.js +4 -5
- package/dist/plugins/analytics/schemas.d.ts +54 -62
- package/dist/plugins/analytics/schemas.js +64 -13
- package/dist/plugins/analytics/table.d.ts +1 -1
- package/dist/plugins.d.ts +0 -1
- package/dist/schemas/config/api.d.ts +17 -0
- package/dist/schemas/config/api.js +14 -0
- package/dist/schemas/config/auth.d.ts +55 -59
- package/dist/schemas/config/auth.js +34 -11
- package/dist/schemas/config/dashboard.d.ts +43 -79
- package/dist/schemas/config/dashboard.js +43 -12
- package/dist/schemas/config/db.d.ts +15 -17
- package/dist/schemas/config/db.js +13 -5
- package/dist/schemas/config/developer.d.ts +33 -45
- package/dist/schemas/config/developer.js +22 -5
- package/dist/schemas/config/index.d.ts +398 -521
- package/dist/schemas/config/index.js +115 -57
- package/dist/schemas/config/sdk.d.ts +50 -196
- package/dist/schemas/config/sdk.js +61 -73
- package/dist/schemas/custom.d.ts +40 -0
- package/dist/schemas/custom.js +41 -0
- package/dist/schemas/external-schemas.d.ts +171 -0
- package/dist/schemas/external-schemas.js +179 -0
- package/dist/schemas/index.d.ts +2 -0
- package/dist/schemas/index.js +2 -0
- package/dist/schemas/plugins/i18n.d.ts +59 -39
- package/dist/schemas/plugins/i18n.js +42 -5
- package/dist/schemas/plugins/index.d.ts +7126 -10296
- package/dist/schemas/plugins/index.js +260 -276
- package/dist/schemas/plugins/shared.d.ts +1293 -3718
- package/dist/schemas/plugins/shared.js +320 -329
- package/dist/test-utils.d.ts +15 -4
- package/dist/test-utils.js +27 -11
- package/dist/toolbar/db-viewer/db-shared-types.d.ts +6 -6
- package/dist/toolbar/db-viewer/studio/connection.d.ts +8 -4
- package/dist/toolbar/db-viewer/studio/connection.js +2 -28
- package/dist/toolbar/db-viewer/studio/env/libsql.d.ts +7 -0
- package/dist/toolbar/db-viewer/studio/env/libsql.js +17 -0
- package/dist/toolbar/db-viewer/studio/env/mysql.d.ts +7 -0
- package/dist/toolbar/db-viewer/studio/env/mysql.js +23 -0
- package/dist/toolbar/db-viewer/studio/env/postgres.d.ts +7 -0
- package/dist/toolbar/db-viewer/studio/env/postgres.js +23 -0
- package/dist/toolbar/db-viewer/studio/index.js +20 -56
- package/dist/toolbar/db-viewer/studio/type.d.ts +1 -2
- package/dist/toolbar/db-viewer/studio/virtual-connection/libsql.d.ts +3 -0
- package/dist/toolbar/db-viewer/studio/virtual-connection/libsql.js +24 -0
- package/dist/toolbar/db-viewer/studio/virtual-connection/mysql.d.ts +3 -0
- package/dist/toolbar/db-viewer/studio/virtual-connection/mysql.js +9 -0
- package/dist/toolbar/db-viewer/studio/virtual-connection/postgres.d.ts +3 -0
- package/dist/toolbar/db-viewer/studio/virtual-connection/postgres.js +9 -0
- package/dist/toolbar/db-viewer/viewer.js +20 -21
- package/dist/types.d.ts +30 -0
- package/dist/utils/effects/smtp.d.ts +1 -1
- package/dist/utils/lang-helper.d.ts +10 -2
- package/dist/virtual.d.ts +35 -28
- package/dist/virtuals/auth/core.d.ts +5 -5
- package/dist/virtuals/auth/verify-email.d.ts +6 -6
- package/dist/virtuals/components/Generator.astro +2 -2
- package/dist/virtuals/components/Renderer.astro +9 -1
- package/dist/virtuals/components/renderFn.d.ts +3 -1
- package/dist/virtuals/components/renderFn.js +18 -0
- package/dist/virtuals/lib/headDefaults.d.ts +4 -2
- package/dist/virtuals/lib/headDefaults.js +0 -2
- package/dist/virtuals/lib/routeMap.d.ts +0 -12
- package/dist/virtuals/lib/routeMap.js +2 -14
- package/dist/virtuals/mailer/index.d.ts +3 -3
- package/dist/virtuals/notifier/index.d.ts +5 -5
- package/dist/virtuals/plugins/dashboard-pages.d.ts +2 -64
- package/dist/virtuals/scripts/StorageFileBrowser.d.ts +1 -172
- package/dist/virtuals/scripts/StorageFileBrowser.js +216 -119
- package/dist/virtuals/template-engine/index.d.ts +4 -4
- package/frontend/components/dashboard/configuration/ConfigForm.astro +218 -110
- package/frontend/components/dashboard/content-mgmt/ContentSearch.astro +21 -22
- package/frontend/components/dashboard/content-mgmt/CreateFolder.astro +66 -54
- package/frontend/components/dashboard/content-mgmt/CreatePage.astro +58 -104
- package/frontend/components/dashboard/content-mgmt/EditFolder.astro +65 -67
- package/frontend/components/dashboard/content-mgmt/EditPage.astro +86 -134
- package/frontend/components/dashboard/content-mgmt/InnerSidebarElement.astro +0 -1
- package/frontend/components/dashboard/content-mgmt/PageHeader.astro +33 -52
- package/frontend/components/dashboard/content-mgmt/PageTypeHandler.astro +2 -2
- package/frontend/components/dashboard/profile/APITokens.astro +219 -158
- package/frontend/components/dashboard/profile/BasicInfo.astro +165 -106
- package/frontend/components/dashboard/profile/Notifications.astro +27 -18
- package/frontend/components/dashboard/profile/UpdatePassword.astro +134 -94
- package/frontend/components/dashboard/sidebar/VersionCheck.astro +31 -16
- package/frontend/components/dashboard/sidebar/VersionCheckChangelog.astro +18 -11
- package/frontend/components/dashboard/sidebar-modals/VersionModal.astro +2 -2
- package/frontend/components/dashboard/smtp-config/TemplateEditor.astro +14 -14
- package/frontend/components/dashboard/taxonomy/InnerSidebarElement.astro +0 -1
- package/frontend/components/dashboard/taxonomy/MetaContainer.astro +0 -2
- package/frontend/components/dashboard/taxonomy/PageHeader.astro +16 -24
- package/frontend/components/dashboard/taxonomy/TaxonomySearch.astro +23 -27
- package/frontend/components/dashboard/user-mgmt/InnerSidebarElement.astro +111 -104
- package/frontend/components/dashboard/user-mgmt/UserListItem.astro +9 -22
- package/frontend/components/dashboard/user-mgmt/UserListItems.astro +18 -0
- package/frontend/components/first-time-setup/snippets/{opt2-studiocms.config.diff → studiocms.config.diff} +1 -0
- package/frontend/components/shared/Code.astro +1 -4
- package/frontend/components/shared/DynamicSettingsRenderer.astro +1 -1
- package/frontend/components/shared/SSRUser.astro +2 -4
- package/frontend/components/shared/foldertree/FolderTreeNode.astro +0 -6
- package/frontend/components/shared/storage-manager/StorageCopyOutput.astro +0 -1
- package/frontend/components/shared/taxonomy/TaxonomyTreeNode.astro +0 -6
- package/frontend/layouts/DashboardLayout.astro +1 -10
- package/frontend/layouts/TaxonomyLayout.astro +0 -1
- package/frontend/middleware/index.ts +102 -61
- package/frontend/pages/404.astro +5 -9
- package/frontend/pages/[dashboard]/[...pluginPage].astro +10 -1
- package/frontend/pages/[dashboard]/configuration.astro +10 -1
- package/frontend/pages/[dashboard]/content-management/createfolder.astro +10 -1
- package/frontend/pages/[dashboard]/content-management/createpage.astro +10 -1
- package/frontend/pages/[dashboard]/content-management/diff.astro +39 -14
- package/frontend/pages/[dashboard]/content-management/editfolder.astro +10 -1
- package/frontend/pages/[dashboard]/content-management/editpage.astro +10 -1
- package/frontend/pages/[dashboard]/content-management/index.astro +10 -1
- package/frontend/pages/[dashboard]/index.astro +10 -1
- package/frontend/pages/[dashboard]/login.astro +86 -25
- package/frontend/pages/[dashboard]/password-reset.astro +22 -16
- package/frontend/pages/[dashboard]/plugins/[plugin].astro +10 -1
- package/frontend/pages/[dashboard]/profile.astro +10 -1
- package/frontend/pages/[dashboard]/signup.astro +153 -52
- package/frontend/pages/[dashboard]/smtp-configuration.astro +77 -75
- package/frontend/pages/[dashboard]/system-management.astro +10 -1
- package/frontend/pages/[dashboard]/taxonomy/categories.astro +30 -41
- package/frontend/pages/[dashboard]/taxonomy/index.astro +10 -0
- package/frontend/pages/[dashboard]/taxonomy/tags.astro +33 -43
- package/frontend/pages/[dashboard]/unverified-email.astro +29 -21
- package/frontend/pages/[dashboard]/user-management/edit.astro +170 -90
- package/frontend/pages/[dashboard]/user-management/index.astro +10 -1
- package/frontend/pages/studiocms_api/[...all].ts +106 -0
- package/frontend/pages/studiocms_api/_handlers/_utils/auth.ts +26 -0
- package/frontend/pages/studiocms_api/_handlers/_utils/changelog.ts +147 -0
- package/frontend/pages/studiocms_api/_handlers/_utils/db-studio-driver.ts +46 -0
- package/frontend/pages/studiocms_api/_handlers/_utils/parseLogLevel.ts +27 -0
- package/frontend/pages/studiocms_api/_handlers/auth/auth.ts +459 -0
- package/frontend/pages/studiocms_api/_handlers/auth/index.ts +17 -0
- package/frontend/pages/studiocms_api/_handlers/auth/oauth.ts +91 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/_shared.ts +57 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/apiTokens.ts +134 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/config.ts +64 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/content.ts +741 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/create.ts +480 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/emailNotifications.ts +49 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/index.ts +45 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/mailer.ts +136 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/plugins.ts +80 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/profile.ts +275 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/resetPassword.ts +140 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/search.ts +63 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/taxonomy.ts +285 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/templates.ts +75 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/users.ts +312 -0
- package/frontend/pages/studiocms_api/_handlers/dashboard/verifyEndpoints.ts +307 -0
- package/frontend/pages/studiocms_api/_handlers/integration/dbStudio.ts +98 -0
- package/frontend/pages/studiocms_api/_handlers/integration/index.ts +17 -0
- package/frontend/pages/studiocms_api/_handlers/integration/storageManager.ts +107 -0
- package/frontend/pages/studiocms_api/_handlers/rest-api/index.ts +8 -0
- package/frontend/pages/studiocms_api/_handlers/rest-api/v1/_shared.ts +41 -0
- package/frontend/pages/studiocms_api/_handlers/rest-api/v1/index.ts +17 -0
- package/frontend/pages/studiocms_api/_handlers/rest-api/v1/public.ts +195 -0
- package/frontend/pages/studiocms_api/_handlers/rest-api/v1/secure.ts +1726 -0
- package/frontend/pages/studiocms_api/_handlers/sdk.ts +129 -0
- package/frontend/pages/studiocms_api/_middleware/astroLocals.ts +36 -0
- package/frontend/pages/studiocms_api/_middleware/restApi.ts +56 -0
- package/frontend/pages/studiocms_api/integrations/[...all].ts +8 -0
- package/frontend/scripts/formdata.ts +219 -0
- package/frontend/setup-pages/3-done.astro +4 -20
- package/frontend/setup-pages/studiocms_api/dashboard/step-2.ts +3 -6
- package/frontend/styles/dashboard-base.css +0 -1
- package/frontend/web-vitals/endpoint.ts +2 -1
- package/package.json +35 -31
- package/dist/global.d.ts +0 -9
- package/frontend/components/dashboard/LoginChecker.astro +0 -68
- package/frontend/components/dashboard/user-mgmt/RankCheck.astro +0 -57
- package/frontend/components/first-time-setup/snippets/opt1-astro.config.diff +0 -14
- package/frontend/components/first-time-setup/snippets/opt2-astro.config.diff +0 -9
- package/frontend/middleware/_authmap.ts +0 -63
- package/frontend/pages/studiocms_api/auth/[path].ts +0 -390
- package/frontend/pages/studiocms_api/auth/[provider]/[...id].ts +0 -64
- package/frontend/pages/studiocms_api/auth/[provider]/_effects/index.ts +0 -101
- package/frontend/pages/studiocms_api/auth/_shared.ts +0 -52
- package/frontend/pages/studiocms_api/dashboard/api-tokens.ts +0 -115
- package/frontend/pages/studiocms_api/dashboard/config.ts +0 -74
- package/frontend/pages/studiocms_api/dashboard/content/diff.ts +0 -73
- package/frontend/pages/studiocms_api/dashboard/content/folder.ts +0 -220
- package/frontend/pages/studiocms_api/dashboard/content/page.ts +0 -359
- package/frontend/pages/studiocms_api/dashboard/create-reset-link.ts +0 -77
- package/frontend/pages/studiocms_api/dashboard/create-user-invite.ts +0 -231
- package/frontend/pages/studiocms_api/dashboard/create-user.ts +0 -186
- package/frontend/pages/studiocms_api/dashboard/email-notification-settings-site.ts +0 -74
- package/frontend/pages/studiocms_api/dashboard/mailer/check-email.ts +0 -75
- package/frontend/pages/studiocms_api/dashboard/mailer/config.ts +0 -136
- package/frontend/pages/studiocms_api/dashboard/plugins/[plugin].ts +0 -80
- package/frontend/pages/studiocms_api/dashboard/profile.ts +0 -245
- package/frontend/pages/studiocms_api/dashboard/resend-verify-email.ts +0 -77
- package/frontend/pages/studiocms_api/dashboard/reset-password.ts +0 -124
- package/frontend/pages/studiocms_api/dashboard/search-list.ts +0 -59
- package/frontend/pages/studiocms_api/dashboard/taxonomy-search.ts +0 -47
- package/frontend/pages/studiocms_api/dashboard/taxonomy.ts +0 -230
- package/frontend/pages/studiocms_api/dashboard/templates.ts +0 -74
- package/frontend/pages/studiocms_api/dashboard/update-user-notifications.ts +0 -86
- package/frontend/pages/studiocms_api/dashboard/users.ts +0 -236
- package/frontend/pages/studiocms_api/dashboard/verify-email.ts +0 -83
- package/frontend/pages/studiocms_api/dashboard/verify-session.ts +0 -187
- package/frontend/pages/studiocms_api/integrations/[type]/[...id].ts +0 -15
- package/frontend/pages/studiocms_api/integrations/[type]/_routes/db-studio.ts +0 -173
- package/frontend/pages/studiocms_api/integrations/[type]/_routes/storage.ts +0 -88
- package/frontend/pages/studiocms_api/partials/editor.astro +0 -74
- package/frontend/pages/studiocms_api/partials/render.astro +0 -39
- package/frontend/pages/studiocms_api/partials/user-list-items.astro +0 -43
- package/frontend/pages/studiocms_api/rest/utils/auth-token.ts +0 -59
- package/frontend/pages/studiocms_api/rest/v1/[type]/[...id].ts +0 -23
- package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/categories.ts +0 -267
- package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/folders.ts +0 -283
- package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/pages.ts +0 -505
- package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/settings.ts +0 -100
- package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/tags.ts +0 -229
- package/frontend/pages/studiocms_api/rest/v1/[type]/_routes/users.ts +0 -553
- package/frontend/pages/studiocms_api/rest/v1/public/[type]/[...id].ts +0 -19
- package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/categories.ts +0 -74
- package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/folders.ts +0 -85
- package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/pages.ts +0 -103
- package/frontend/pages/studiocms_api/rest/v1/public/[type]/_routes/tags.ts +0 -67
- package/frontend/pages/studiocms_api/sdk/[...path].ts +0 -97
- package/frontend/pages/studiocms_api/sdk/utils/changelog.ts +0 -119
- package/frontend/scripts/auth/formListener.ts +0 -81
- package/frontend/scripts/formdata-utils.ts +0 -116
- package/frontend/utils/build-partial-schema.ts +0 -46
- package/frontend/utils/errors.ts +0 -6
- package/frontend/utils/param-extractor.ts +0 -83
- 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
|
-
};
|
package/frontend/utils/errors.ts
DELETED
|
@@ -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
|
-
}
|