@open-mercato/checkout 0.7.1-develop.7130.1.fef2396fd8 → 0.7.1-develop.7131.1.20abf9e885
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.
|
@@ -4,6 +4,7 @@ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
|
4
4
|
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
5
5
|
import { serializeOperationMetadata } from "@open-mercato/shared/lib/commands/operationMetadata";
|
|
6
6
|
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
7
|
+
import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
|
|
7
8
|
import { CHECKOUT_PASSWORD_COOKIE } from "../lib/constants.js";
|
|
8
9
|
import { verifyCheckoutAccessToken } from "../lib/utils.js";
|
|
9
10
|
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
@@ -70,6 +71,10 @@ function handleCheckoutRouteError(error) {
|
|
|
70
71
|
if (error instanceof CrudHttpError) {
|
|
71
72
|
return NextResponse.json(error.body ?? { error: error.message }, { status: error.status });
|
|
72
73
|
}
|
|
74
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(error);
|
|
75
|
+
if (interceptorRejection) {
|
|
76
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
|
|
77
|
+
}
|
|
73
78
|
if (error instanceof z.ZodError) {
|
|
74
79
|
const fieldErrors = error.issues.reduce((result, issue) => {
|
|
75
80
|
const path = issue.path.map((part) => String(part)).join(".");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/checkout/api/helpers.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { getAuthFromRequest, type AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands/command-bus'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { CHECKOUT_PASSWORD_COOKIE } from '../lib/constants'\nimport { verifyCheckoutAccessToken } from '../lib/utils'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('checkout')\n\nexport async function requireAdminContext(req: Request): Promise<{\n auth: Exclude<AuthContext, null>\n container: Awaited<ReturnType<typeof createRequestContainer>>\n em: EntityManager\n commandBus: CommandBus\n}> {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n throw new CrudHttpError(401, { error: 'Unauthorized' })\n }\n const container = await createRequestContainer()\n return {\n auth,\n container,\n em: container.resolve('em') as EntityManager,\n commandBus: container.resolve('commandBus') as CommandBus,\n }\n}\n\nexport async function requirePreviewContext(\n req: Request,\n feature = 'checkout.view',\n): Promise<{\n auth: Exclude<AuthContext, null>\n container: Awaited<ReturnType<typeof createRequestContainer>>\n em: EntityManager\n}> {\n const context = await requireAdminContext(req)\n const allowed = await userHasCheckoutFeature(context.container, context.auth, feature)\n if (!allowed) {\n throw new CrudHttpError(403, { error: 'Forbidden' })\n }\n return {\n auth: context.auth,\n container: context.container,\n em: context.em,\n }\n}\n\nexport function buildCommandRuntimeContext(\n req: Request,\n container: Awaited<ReturnType<typeof createRequestContainer>>,\n auth: AuthContext,\n): CommandRuntimeContext {\n return {\n container,\n auth,\n organizationScope: null,\n selectedOrganizationId: auth?.orgId ?? null,\n organizationIds: auth?.orgId ? [auth.orgId] : null,\n request: req,\n }\n}\n\nexport async function userHasCheckoutFeature(\n container: Awaited<ReturnType<typeof createRequestContainer>>,\n auth: Exclude<AuthContext, null>,\n feature: string,\n): Promise<boolean> {\n const rbac = container.resolve('rbacService') as RbacService\n return rbac.userHasAllFeatures(auth.sub, [feature], {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n })\n}\n\nexport function readCheckoutAccessCookie(req: Request): string | null {\n const cookieHeader = req.headers.get('cookie') ?? ''\n const match = cookieHeader.match(new RegExp(`(?:^|;\\\\s*)${CHECKOUT_PASSWORD_COOKIE}=([^;]+)`))\n if (!match?.[1]) return null\n try {\n return decodeURIComponent(match[1])\n } catch {\n return match[1]\n }\n}\n\nexport function requireCheckoutPasswordSession(\n req: Request,\n slug: string,\n options?: { linkId?: string | null; sessionVersion?: Date | string | null },\n): void {\n const token = readCheckoutAccessCookie(req)\n if (!verifyCheckoutAccessToken(token, slug, options)) {\n throw new CrudHttpError(401, { error: 'Password verification is required' })\n }\n}\n\nexport function handleCheckoutRouteError(error: unknown) {\n if (error instanceof CrudHttpError) {\n return NextResponse.json(error.body ?? { error: error.message }, { status: error.status })\n }\n if (error instanceof z.ZodError) {\n const fieldErrors = error.issues.reduce<Record<string, string>>((result, issue) => {\n const path = issue.path.map((part) => String(part)).join('.')\n if (!path || result[path]) return result\n result[path] = issue.message\n return result\n }, {})\n return NextResponse.json(\n {\n error: 'Validation failed',\n fieldErrors,\n details: error.issues.map(({ path, code, message }) => ({ path, code, message })),\n },\n { status: 400 },\n )\n }\n if (error instanceof Error) {\n logger.error('Unhandled route error', { err: error })\n }\n return NextResponse.json({ error: 'Internal server error' }, { status: 500 })\n}\n\ntype CheckoutOperationLogLike = {\n id?: string | null\n undoToken?: string | null\n commandId?: string | null\n actionLabel?: string | null\n resourceKind?: string | null\n resourceId?: string | null\n createdAt?: Date | string | null\n}\n\nexport function attachOperationMetadataHeader(\n response: NextResponse,\n logEntry: CheckoutOperationLogLike | null | undefined,\n defaults: {\n resourceKind: string\n resourceId?: string | null\n },\n): NextResponse {\n if (!logEntry?.id || !logEntry.undoToken || !logEntry.commandId) return response\n const executedAt = logEntry.createdAt instanceof Date\n ? logEntry.createdAt.toISOString()\n : typeof logEntry.createdAt === 'string' && logEntry.createdAt\n ? logEntry.createdAt\n : new Date().toISOString()\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? defaults.resourceKind,\n resourceId: logEntry.resourceId ?? defaults.resourceId ?? null,\n executedAt,\n }),\n )\n return response\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { getAuthFromRequest, type AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands/command-bus'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { CHECKOUT_PASSWORD_COOKIE } from '../lib/constants'\nimport { verifyCheckoutAccessToken } from '../lib/utils'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('checkout')\n\nexport async function requireAdminContext(req: Request): Promise<{\n auth: Exclude<AuthContext, null>\n container: Awaited<ReturnType<typeof createRequestContainer>>\n em: EntityManager\n commandBus: CommandBus\n}> {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n throw new CrudHttpError(401, { error: 'Unauthorized' })\n }\n const container = await createRequestContainer()\n return {\n auth,\n container,\n em: container.resolve('em') as EntityManager,\n commandBus: container.resolve('commandBus') as CommandBus,\n }\n}\n\nexport async function requirePreviewContext(\n req: Request,\n feature = 'checkout.view',\n): Promise<{\n auth: Exclude<AuthContext, null>\n container: Awaited<ReturnType<typeof createRequestContainer>>\n em: EntityManager\n}> {\n const context = await requireAdminContext(req)\n const allowed = await userHasCheckoutFeature(context.container, context.auth, feature)\n if (!allowed) {\n throw new CrudHttpError(403, { error: 'Forbidden' })\n }\n return {\n auth: context.auth,\n container: context.container,\n em: context.em,\n }\n}\n\nexport function buildCommandRuntimeContext(\n req: Request,\n container: Awaited<ReturnType<typeof createRequestContainer>>,\n auth: AuthContext,\n): CommandRuntimeContext {\n return {\n container,\n auth,\n organizationScope: null,\n selectedOrganizationId: auth?.orgId ?? null,\n organizationIds: auth?.orgId ? [auth.orgId] : null,\n request: req,\n }\n}\n\nexport async function userHasCheckoutFeature(\n container: Awaited<ReturnType<typeof createRequestContainer>>,\n auth: Exclude<AuthContext, null>,\n feature: string,\n): Promise<boolean> {\n const rbac = container.resolve('rbacService') as RbacService\n return rbac.userHasAllFeatures(auth.sub, [feature], {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n })\n}\n\nexport function readCheckoutAccessCookie(req: Request): string | null {\n const cookieHeader = req.headers.get('cookie') ?? ''\n const match = cookieHeader.match(new RegExp(`(?:^|;\\\\s*)${CHECKOUT_PASSWORD_COOKIE}=([^;]+)`))\n if (!match?.[1]) return null\n try {\n return decodeURIComponent(match[1])\n } catch {\n return match[1]\n }\n}\n\nexport function requireCheckoutPasswordSession(\n req: Request,\n slug: string,\n options?: { linkId?: string | null; sessionVersion?: Date | string | null },\n): void {\n const token = readCheckoutAccessCookie(req)\n if (!verifyCheckoutAccessToken(token, slug, options)) {\n throw new CrudHttpError(401, { error: 'Password verification is required' })\n }\n}\n\nexport function handleCheckoutRouteError(error: unknown) {\n if (error instanceof CrudHttpError) {\n return NextResponse.json(error.body ?? { error: error.message }, { status: error.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(error)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n if (error instanceof z.ZodError) {\n const fieldErrors = error.issues.reduce<Record<string, string>>((result, issue) => {\n const path = issue.path.map((part) => String(part)).join('.')\n if (!path || result[path]) return result\n result[path] = issue.message\n return result\n }, {})\n return NextResponse.json(\n {\n error: 'Validation failed',\n fieldErrors,\n details: error.issues.map(({ path, code, message }) => ({ path, code, message })),\n },\n { status: 400 },\n )\n }\n if (error instanceof Error) {\n logger.error('Unhandled route error', { err: error })\n }\n return NextResponse.json({ error: 'Internal server error' }, { status: 500 })\n}\n\ntype CheckoutOperationLogLike = {\n id?: string | null\n undoToken?: string | null\n commandId?: string | null\n actionLabel?: string | null\n resourceKind?: string | null\n resourceId?: string | null\n createdAt?: Date | string | null\n}\n\nexport function attachOperationMetadataHeader(\n response: NextResponse,\n logEntry: CheckoutOperationLogLike | null | undefined,\n defaults: {\n resourceKind: string\n resourceId?: string | null\n },\n): NextResponse {\n if (!logEntry?.id || !logEntry.undoToken || !logEntry.commandId) return response\n const executedAt = logEntry.createdAt instanceof Date\n ? logEntry.createdAt.toISOString()\n : typeof logEntry.createdAt === 'string' && logEntry.createdAt\n ? logEntry.createdAt\n : new Date().toISOString()\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? defaults.resourceKind,\n resourceId: logEntry.resourceId ?? defaults.resourceId ?? null,\n executedAt,\n }),\n )\n return response\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/checkout",
|
|
3
|
-
"version": "0.7.1-develop.
|
|
3
|
+
"version": "0.7.1-develop.7131.1.20abf9e885",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -62,18 +62,18 @@
|
|
|
62
62
|
}
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@open-mercato/core": "0.7.1-develop.
|
|
66
|
-
"@open-mercato/ui": "0.7.1-develop.
|
|
65
|
+
"@open-mercato/core": "0.7.1-develop.7131.1.20abf9e885",
|
|
66
|
+
"@open-mercato/ui": "0.7.1-develop.7131.1.20abf9e885",
|
|
67
67
|
"bcryptjs": "^3.0.3"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
70
|
"@mikro-orm/postgresql": "^7.0.14",
|
|
71
|
-
"@open-mercato/shared": "0.7.1-develop.
|
|
71
|
+
"@open-mercato/shared": "0.7.1-develop.7131.1.20abf9e885",
|
|
72
72
|
"react": "^19.0.0",
|
|
73
73
|
"react-dom": "^19.0.0"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
|
-
"@open-mercato/shared": "0.7.1-develop.
|
|
76
|
+
"@open-mercato/shared": "0.7.1-develop.7131.1.20abf9e885",
|
|
77
77
|
"@types/jest": "^30.0.0",
|
|
78
78
|
"@types/react": "^19.2.17",
|
|
79
79
|
"@types/react-dom": "^19.2.3",
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** @jest-environment node */
|
|
2
|
+
import { CommandInterceptorError } from '@open-mercato/shared/lib/commands/errors'
|
|
3
|
+
import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
4
|
+
import { handleCheckoutRouteError } from '../helpers'
|
|
5
|
+
|
|
6
|
+
describe('handleCheckoutRouteError command interceptor rejections', () => {
|
|
7
|
+
it('surfaces the status and body of a rejection that carries one', async () => {
|
|
8
|
+
const response = handleCheckoutRouteError(
|
|
9
|
+
new CommandInterceptorError('Missing required fields', {
|
|
10
|
+
status: 422,
|
|
11
|
+
body: { error: 'Missing required fields', missingFields: ['email'] },
|
|
12
|
+
}),
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
expect(response.status).toBe(422)
|
|
16
|
+
await expect(response.json()).resolves.toEqual({
|
|
17
|
+
error: 'Missing required fields',
|
|
18
|
+
missingFields: ['email'],
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('derives the body from the message when the rejection supplies none', async () => {
|
|
23
|
+
const response = handleCheckoutRouteError(new CommandInterceptorError('Blocked by policy', { status: 409 }))
|
|
24
|
+
|
|
25
|
+
expect(response.status).toBe(409)
|
|
26
|
+
await expect(response.json()).resolves.toEqual({ error: 'Blocked by policy' })
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('keeps the generic 500 when the rejection carries no status', async () => {
|
|
30
|
+
const response = handleCheckoutRouteError(new CommandInterceptorError('Blocked without a status'))
|
|
31
|
+
|
|
32
|
+
expect(response.status).toBe(500)
|
|
33
|
+
await expect(response.json()).resolves.toEqual({ error: 'Internal server error' })
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('keeps mapping CrudHttpError ahead of the interceptor branch', async () => {
|
|
37
|
+
const response = handleCheckoutRouteError(new CrudHttpError(404, { error: 'Link not found' }))
|
|
38
|
+
|
|
39
|
+
expect(response.status).toBe(404)
|
|
40
|
+
await expect(response.json()).resolves.toEqual({ error: 'Link not found' })
|
|
41
|
+
})
|
|
42
|
+
})
|
|
@@ -8,6 +8,7 @@ import type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'
|
|
|
8
8
|
import { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'
|
|
9
9
|
import type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
|
|
10
10
|
import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
11
|
+
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
11
12
|
import { CHECKOUT_PASSWORD_COOKIE } from '../lib/constants'
|
|
12
13
|
import { verifyCheckoutAccessToken } from '../lib/utils'
|
|
13
14
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
@@ -106,6 +107,10 @@ export function handleCheckoutRouteError(error: unknown) {
|
|
|
106
107
|
if (error instanceof CrudHttpError) {
|
|
107
108
|
return NextResponse.json(error.body ?? { error: error.message }, { status: error.status })
|
|
108
109
|
}
|
|
110
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(error)
|
|
111
|
+
if (interceptorRejection) {
|
|
112
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
|
|
113
|
+
}
|
|
109
114
|
if (error instanceof z.ZodError) {
|
|
110
115
|
const fieldErrors = error.issues.reduce<Record<string, string>>((result, issue) => {
|
|
111
116
|
const path = issue.path.map((part) => String(part)).join('.')
|