@open-mercato/core 0.6.6-develop.6255.1.a6ee4a57c1 → 0.6.6-develop.6256.1.9fc16aedc4
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/.turbo/turbo-build.log +1 -1
- package/dist/modules/perspectives/api/[tableId]/[perspectiveId]/route.js +20 -9
- package/dist/modules/perspectives/api/[tableId]/[perspectiveId]/route.js.map +2 -2
- package/dist/modules/perspectives/api/[tableId]/roles/[roleId]/route.js +43 -6
- package/dist/modules/perspectives/api/[tableId]/roles/[roleId]/route.js.map +2 -2
- package/dist/modules/perspectives/api/[tableId]/route.js +21 -4
- package/dist/modules/perspectives/api/[tableId]/route.js.map +2 -2
- package/dist/modules/perspectives/api/openapi.js +1 -0
- package/dist/modules/perspectives/api/openapi.js.map +2 -2
- package/dist/modules/perspectives/data/validators.js +2 -0
- package/dist/modules/perspectives/data/validators.js.map +2 -2
- package/dist/modules/perspectives/services/perspectiveService.js +135 -6
- package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/perspectives/api/[tableId]/[perspectiveId]/route.ts +20 -9
- package/src/modules/perspectives/api/[tableId]/roles/[roleId]/route.ts +45 -6
- package/src/modules/perspectives/api/[tableId]/route.ts +23 -1
- package/src/modules/perspectives/api/openapi.ts +1 -1
- package/src/modules/perspectives/data/validators.ts +2 -0
- package/src/modules/perspectives/services/perspectiveService.ts +171 -7
package/.turbo/turbo-build.log
CHANGED
|
@@ -2,6 +2,7 @@ import { NextResponse } from "next/server";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
4
4
|
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
5
|
+
import { isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
5
6
|
import {
|
|
6
7
|
runCrudMutationGuardAfterSuccess,
|
|
7
8
|
validateCrudMutationGuard
|
|
@@ -51,15 +52,24 @@ async function DELETE(req, ctx) {
|
|
|
51
52
|
if (guardResult && !guardResult.ok) {
|
|
52
53
|
return NextResponse.json(guardResult.body, { status: guardResult.status });
|
|
53
54
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
let deleted = false;
|
|
56
|
+
try {
|
|
57
|
+
deleted = await deleteUserPerspective(em, cache, {
|
|
58
|
+
scope: {
|
|
59
|
+
userId: auth.sub,
|
|
60
|
+
tenantId: auth.tenantId ?? null,
|
|
61
|
+
organizationId: auth.orgId ?? null
|
|
62
|
+
},
|
|
63
|
+
tableId,
|
|
64
|
+
perspectiveId,
|
|
65
|
+
request: req
|
|
66
|
+
});
|
|
67
|
+
} catch (err) {
|
|
68
|
+
if (isCrudHttpError(err)) {
|
|
69
|
+
return NextResponse.json(err.body, { status: err.status });
|
|
70
|
+
}
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
63
73
|
if (deleted && guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
64
74
|
await runCrudMutationGuardAfterSuccess(container, {
|
|
65
75
|
tenantId: auth.tenantId ?? "",
|
|
@@ -89,6 +99,7 @@ const perspectiveDeleteDoc = {
|
|
|
89
99
|
errors: [
|
|
90
100
|
{ status: 400, description: "Invalid identifiers supplied", schema: perspectivesErrorSchema },
|
|
91
101
|
{ status: 401, description: "Authentication required", schema: perspectivesErrorSchema },
|
|
102
|
+
{ status: 409, description: "Optimistic lock conflict", schema: perspectivesErrorSchema },
|
|
92
103
|
{ status: 404, description: "Perspective not found", schema: perspectivesErrorSchema }
|
|
93
104
|
]
|
|
94
105
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/perspectives/api/%5BtableId%5D/%5BperspectiveId%5D/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport { deleteUserPerspective } from '@open-mercato/core/modules/perspectives/services/perspectiveService'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { perspectivesTag, perspectivesErrorSchema, perspectivesSuccessSchema } from '../../openapi'\n\nexport const metadata = {\n DELETE: { requireAuth: true, requireFeatures: ['perspectives.use'] },\n}\n\nconst decodeParam = (value: string | string[] | undefined): string => {\n if (!value) return ''\n const raw = Array.isArray(value) ? value[0] : value\n try {\n return decodeURIComponent(raw)\n } catch {\n return raw\n }\n}\n\nexport async function DELETE(req: Request, ctx: { params: { tableId: string; perspectiveId: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const tableId = decodeParam(ctx.params?.tableId).trim()\n const perspectiveId = decodeParam(ctx.params?.perspectiveId).trim()\n if (!tableId || !perspectiveId) {\n return NextResponse.json({ error: 'Invalid parameters' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as import('@mikro-orm/postgresql').EntityManager\n const cache = ((): import('@open-mercato/cache').CacheStrategy | null => {\n try {\n return container.resolve('cache') as import('@open-mercato/cache').CacheStrategy\n } catch {\n return null\n }\n })()\n\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId ?? '',\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n resourceKind: 'perspectives.perspective',\n resourceId: perspectiveId,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: { tableId, perspectiveId },\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,6BAA6B;AAEtC,SAAS,iBAAiB,yBAAyB,iCAAiC;AAE7E,MAAM,WAAW;AAAA,EACtB,QAAQ,EAAE,aAAa,MAAM,iBAAiB,CAAC,kBAAkB,EAAE;AACrE;AAEA,MAAM,cAAc,CAAC,UAAiD;AACpE,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,MAAM,MAAM,QAAQ,KAAK,IAAI,MAAM,CAAC,IAAI;AAC9C,MAAI;AACF,WAAO,mBAAmB,GAAG;AAAA,EAC/B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,OAAO,KAAc,KAA6D;AACtG,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAE9E,QAAM,UAAU,YAAY,IAAI,QAAQ,OAAO,EAAE,KAAK;AACtD,QAAM,gBAAgB,YAAY,IAAI,QAAQ,aAAa,EAAE,KAAK;AAClE,MAAI,CAAC,WAAW,CAAC,eAAe;AAC9B,WAAO,aAAa,KAAK,EAAE,OAAO,qBAAqB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC3E;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,KAAK,UAAU,QAAQ,IAAI;AACjC,QAAM,SAAS,MAA0D;AACvE,QAAI;AACF,aAAO,UAAU,QAAQ,OAAO;AAAA,IAClC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF,GAAG;AAEH,QAAM,cAAc,MAAM,0BAA0B,WAAW;AAAA,IAC7D,UAAU,KAAK,YAAY;AAAA,IAC3B,gBAAgB,KAAK,SAAS;AAAA,IAC9B,QAAQ,KAAK;AAAA,IACb,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,eAAe,IAAI;AAAA,IACnB,gBAAgB,IAAI;AAAA,IACpB,iBAAiB,EAAE,SAAS,cAAc;AAAA,EAC5C,CAAC;AACD,MAAI,eAAe,CAAC,YAAY,IAAI;AAClC,WAAO,aAAa,KAAK,YAAY,MAAM,EAAE,QAAQ,YAAY,OAAO,CAAC;AAAA,EAC3E;AAEA,
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport { deleteUserPerspective } from '@open-mercato/core/modules/perspectives/services/perspectiveService'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { perspectivesTag, perspectivesErrorSchema, perspectivesSuccessSchema } from '../../openapi'\n\nexport const metadata = {\n DELETE: { requireAuth: true, requireFeatures: ['perspectives.use'] },\n}\n\nconst decodeParam = (value: string | string[] | undefined): string => {\n if (!value) return ''\n const raw = Array.isArray(value) ? value[0] : value\n try {\n return decodeURIComponent(raw)\n } catch {\n return raw\n }\n}\n\nexport async function DELETE(req: Request, ctx: { params: { tableId: string; perspectiveId: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const tableId = decodeParam(ctx.params?.tableId).trim()\n const perspectiveId = decodeParam(ctx.params?.perspectiveId).trim()\n if (!tableId || !perspectiveId) {\n return NextResponse.json({ error: 'Invalid parameters' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as import('@mikro-orm/postgresql').EntityManager\n const cache = ((): import('@open-mercato/cache').CacheStrategy | null => {\n try {\n return container.resolve('cache') as import('@open-mercato/cache').CacheStrategy\n } catch {\n return null\n }\n })()\n\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId ?? '',\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n resourceKind: 'perspectives.perspective',\n resourceId: perspectiveId,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: { tableId, perspectiveId },\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n let deleted = false\n try {\n deleted = await deleteUserPerspective(em, cache, {\n scope: {\n userId: auth.sub,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n },\n tableId,\n perspectiveId,\n request: req,\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n throw err\n }\n\n if (deleted && guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId ?? '',\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n resourceKind: 'perspectives.perspective',\n resourceId: perspectiveId,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return NextResponse.json({ success: true })\n}\n\nconst perspectiveDeletePathParamsSchema = z.object({\n tableId: z.string().min(1),\n perspectiveId: z.string().uuid(),\n})\n\nconst perspectiveDeleteDoc: OpenApiMethodDoc = {\n summary: 'Delete a personal perspective',\n description: 'Removes a perspective owned by the current user for the given table.',\n tags: [perspectivesTag],\n responses: [\n { status: 200, description: 'Perspective removed.', schema: perspectivesSuccessSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid identifiers supplied', schema: perspectivesErrorSchema },\n { status: 401, description: 'Authentication required', schema: perspectivesErrorSchema },\n { status: 409, description: 'Optimistic lock conflict', schema: perspectivesErrorSchema },\n { status: 404, description: 'Perspective not found', schema: perspectivesErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: perspectivesTag,\n summary: 'Delete personal perspective',\n pathParams: perspectiveDeletePathParamsSchema,\n methods: {\n DELETE: perspectiveDeleteDoc,\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -2,6 +2,7 @@ import { NextResponse } from "next/server";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
4
4
|
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
5
|
+
import { isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
5
6
|
import {
|
|
6
7
|
runCrudMutationGuardAfterSuccess,
|
|
7
8
|
validateCrudMutationGuard
|
|
@@ -21,6 +22,10 @@ const decodeParam = (value) => {
|
|
|
21
22
|
return raw;
|
|
22
23
|
}
|
|
23
24
|
};
|
|
25
|
+
const rolePerspectiveDeleteBodySchema = z.object({
|
|
26
|
+
roleExpectedUpdatedAtByRoleId: z.record(z.string().uuid(), z.string().min(1).nullable()).optional(),
|
|
27
|
+
roleExpectedUpdatedAtByPerspectiveId: z.record(z.string().uuid(), z.string().min(1).nullable()).optional()
|
|
28
|
+
}).optional();
|
|
24
29
|
async function DELETE(req, ctx) {
|
|
25
30
|
const auth = await getAuthFromRequest(req);
|
|
26
31
|
if (!auth) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
@@ -29,6 +34,21 @@ async function DELETE(req, ctx) {
|
|
|
29
34
|
if (!tableId || !roleId) {
|
|
30
35
|
return NextResponse.json({ error: "Invalid parameters" }, { status: 400 });
|
|
31
36
|
}
|
|
37
|
+
let parsedBody = void 0;
|
|
38
|
+
if (req.body && (req.headers.get("content-type") ?? "").includes("application/json")) {
|
|
39
|
+
try {
|
|
40
|
+
const rawBody = await req.text();
|
|
41
|
+
if (rawBody.trim().length > 0) {
|
|
42
|
+
const parsed = rolePerspectiveDeleteBodySchema.safeParse(JSON.parse(rawBody));
|
|
43
|
+
if (!parsed.success) {
|
|
44
|
+
return NextResponse.json({ error: "Invalid payload", details: parsed.error.flatten() }, { status: 400 });
|
|
45
|
+
}
|
|
46
|
+
parsedBody = parsed.data;
|
|
47
|
+
}
|
|
48
|
+
} catch {
|
|
49
|
+
return NextResponse.json({ error: "Invalid JSON" }, { status: 400 });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
32
52
|
const container = await createRequestContainer();
|
|
33
53
|
const em = container.resolve("em");
|
|
34
54
|
const cache = (() => {
|
|
@@ -55,12 +75,23 @@ async function DELETE(req, ctx) {
|
|
|
55
75
|
if (guardResult && !guardResult.ok) {
|
|
56
76
|
return NextResponse.json(guardResult.body, { status: guardResult.status });
|
|
57
77
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
78
|
+
let clearedCount = 0;
|
|
79
|
+
try {
|
|
80
|
+
clearedCount = await clearRolePerspectives(em, cache, {
|
|
81
|
+
tableId,
|
|
82
|
+
tenantId: auth.tenantId ?? null,
|
|
83
|
+
organizationId: auth.orgId ?? null,
|
|
84
|
+
roleIds: [roleId],
|
|
85
|
+
expectedUpdatedAtByRoleId: parsedBody?.roleExpectedUpdatedAtByRoleId,
|
|
86
|
+
expectedUpdatedAtByPerspectiveId: parsedBody?.roleExpectedUpdatedAtByPerspectiveId,
|
|
87
|
+
request: req
|
|
88
|
+
});
|
|
89
|
+
} catch (err) {
|
|
90
|
+
if (isCrudHttpError(err)) {
|
|
91
|
+
return NextResponse.json(err.body, { status: err.status });
|
|
92
|
+
}
|
|
93
|
+
throw err;
|
|
94
|
+
}
|
|
64
95
|
if (clearedCount > 0 && guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
65
96
|
await runCrudMutationGuardAfterSuccess(container, {
|
|
66
97
|
tenantId: auth.tenantId ?? "",
|
|
@@ -84,6 +115,11 @@ const rolePerspectiveDeleteDoc = {
|
|
|
84
115
|
summary: "Clear role perspectives for a table",
|
|
85
116
|
description: "Removes all role-level perspectives associated with the provided role identifier for the table.",
|
|
86
117
|
tags: [perspectivesTag],
|
|
118
|
+
requestBody: {
|
|
119
|
+
contentType: "application/json",
|
|
120
|
+
schema: rolePerspectiveDeleteBodySchema,
|
|
121
|
+
description: "Optional per-role or per-perspective optimistic-lock tokens."
|
|
122
|
+
},
|
|
87
123
|
responses: [
|
|
88
124
|
{ status: 200, description: "Role perspectives cleared.", schema: perspectivesSuccessSchema }
|
|
89
125
|
],
|
|
@@ -91,6 +127,7 @@ const rolePerspectiveDeleteDoc = {
|
|
|
91
127
|
{ status: 400, description: "Invalid identifiers supplied", schema: perspectivesErrorSchema },
|
|
92
128
|
{ status: 401, description: "Authentication required", schema: perspectivesErrorSchema },
|
|
93
129
|
{ status: 403, description: "Missing perspectives.role_defaults feature", schema: perspectivesErrorSchema },
|
|
130
|
+
{ status: 409, description: "Optimistic lock conflict", schema: perspectivesErrorSchema },
|
|
94
131
|
{ status: 404, description: "Role not found in scope", schema: perspectivesErrorSchema }
|
|
95
132
|
]
|
|
96
133
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/perspectives/api/%5BtableId%5D/roles/%5BroleId%5D/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport { clearRolePerspectives } from '@open-mercato/core/modules/perspectives/services/perspectiveService'\nimport { Role } from '@open-mercato/core/modules/auth/data/entities'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { perspectivesTag, perspectivesErrorSchema, perspectivesSuccessSchema } from '../../../openapi'\n\nexport const metadata = {\n DELETE: { requireAuth: true, requireFeatures: ['perspectives.role_defaults'] },\n}\n\nconst decodeParam = (value: string | string[] | undefined): string => {\n if (!value) return ''\n const raw = Array.isArray(value) ? value[0] : value\n try {\n return decodeURIComponent(raw)\n } catch {\n return raw\n }\n}\n\nexport async function DELETE(req: Request, ctx: { params: { tableId: string; roleId: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const tableId = decodeParam(ctx.params?.tableId).trim()\n const roleId = decodeParam(ctx.params?.roleId).trim()\n if (!tableId || !roleId) {\n return NextResponse.json({ error: 'Invalid parameters' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as import('@mikro-orm/postgresql').EntityManager\n const cache = ((): import('@open-mercato/cache').CacheStrategy | null => {\n try {\n return container.resolve('cache') as import('@open-mercato/cache').CacheStrategy\n } catch {\n return null\n }\n })()\n\n const scope = auth.tenantId\n ? { $or: [{ tenantId: auth.tenantId }, { tenantId: null }] }\n : { tenantId: null }\n\n const role = await em.findOne(Role, { id: roleId, deletedAt: null, ...(scope as any) } as any)\n if (!role) return NextResponse.json({ error: 'Role not found' }, { status: 404 })\n\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId ?? '',\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n resourceKind: 'perspectives.role_perspective',\n resourceId: roleId,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: { tableId, roleId },\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,6BAA6B;AACtC,SAAS,YAAY;AAErB,SAAS,iBAAiB,yBAAyB,iCAAiC;AAE7E,MAAM,WAAW;AAAA,EACtB,QAAQ,EAAE,aAAa,MAAM,iBAAiB,CAAC,4BAA4B,EAAE;AAC/E;AAEA,MAAM,cAAc,CAAC,UAAiD;AACpE,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,MAAM,MAAM,QAAQ,KAAK,IAAI,MAAM,CAAC,IAAI;AAC9C,MAAI;AACF,WAAO,mBAAmB,GAAG;AAAA,EAC/B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,OAAO,KAAc,KAAsD;AAC/F,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAE9E,QAAM,UAAU,YAAY,IAAI,QAAQ,OAAO,EAAE,KAAK;AACtD,QAAM,SAAS,YAAY,IAAI,QAAQ,MAAM,EAAE,KAAK;AACpD,MAAI,CAAC,WAAW,CAAC,QAAQ;AACvB,WAAO,aAAa,KAAK,EAAE,OAAO,qBAAqB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC3E;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,KAAK,UAAU,QAAQ,IAAI;AACjC,QAAM,SAAS,MAA0D;AACvE,QAAI;AACF,aAAO,UAAU,QAAQ,OAAO;AAAA,IAClC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF,GAAG;AAEH,QAAM,QAAQ,KAAK,WACf,EAAE,KAAK,CAAC,EAAE,UAAU,KAAK,SAAS,GAAG,EAAE,UAAU,KAAK,CAAC,EAAE,IACzD,EAAE,UAAU,KAAK;AAErB,QAAM,OAAO,MAAM,GAAG,QAAQ,MAAM,EAAE,IAAI,QAAQ,WAAW,MAAM,GAAI,MAAc,CAAQ;AAC7F,MAAI,CAAC,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAEhF,QAAM,cAAc,MAAM,0BAA0B,WAAW;AAAA,IAC7D,UAAU,KAAK,YAAY;AAAA,IAC3B,gBAAgB,KAAK,SAAS;AAAA,IAC9B,QAAQ,KAAK;AAAA,IACb,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,eAAe,IAAI;AAAA,IACnB,gBAAgB,IAAI;AAAA,IACpB,iBAAiB,EAAE,SAAS,OAAO;AAAA,EACrC,CAAC;AACD,MAAI,eAAe,CAAC,YAAY,IAAI;AAClC,WAAO,aAAa,KAAK,YAAY,MAAM,EAAE,QAAQ,YAAY,OAAO,CAAC;AAAA,EAC3E;AAEA,
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport { clearRolePerspectives } from '@open-mercato/core/modules/perspectives/services/perspectiveService'\nimport { Role } from '@open-mercato/core/modules/auth/data/entities'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { perspectivesTag, perspectivesErrorSchema, perspectivesSuccessSchema } from '../../../openapi'\n\nexport const metadata = {\n DELETE: { requireAuth: true, requireFeatures: ['perspectives.role_defaults'] },\n}\n\nconst decodeParam = (value: string | string[] | undefined): string => {\n if (!value) return ''\n const raw = Array.isArray(value) ? value[0] : value\n try {\n return decodeURIComponent(raw)\n } catch {\n return raw\n }\n}\n\nconst rolePerspectiveDeleteBodySchema = z.object({\n roleExpectedUpdatedAtByRoleId: z.record(z.string().uuid(), z.string().min(1).nullable()).optional(),\n roleExpectedUpdatedAtByPerspectiveId: z.record(z.string().uuid(), z.string().min(1).nullable()).optional(),\n}).optional()\n\nexport async function DELETE(req: Request, ctx: { params: { tableId: string; roleId: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const tableId = decodeParam(ctx.params?.tableId).trim()\n const roleId = decodeParam(ctx.params?.roleId).trim()\n if (!tableId || !roleId) {\n return NextResponse.json({ error: 'Invalid parameters' }, { status: 400 })\n }\n\n let parsedBody: z.infer<typeof rolePerspectiveDeleteBodySchema> = undefined\n if (req.body && (req.headers.get('content-type') ?? '').includes('application/json')) {\n try {\n const rawBody = await req.text()\n if (rawBody.trim().length > 0) {\n const parsed = rolePerspectiveDeleteBodySchema.safeParse(JSON.parse(rawBody))\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 400 })\n }\n parsedBody = parsed.data\n }\n } catch {\n return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 })\n }\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as import('@mikro-orm/postgresql').EntityManager\n const cache = ((): import('@open-mercato/cache').CacheStrategy | null => {\n try {\n return container.resolve('cache') as import('@open-mercato/cache').CacheStrategy\n } catch {\n return null\n }\n })()\n\n const scope = auth.tenantId\n ? { $or: [{ tenantId: auth.tenantId }, { tenantId: null }] }\n : { tenantId: null }\n\n const role = await em.findOne(Role, { id: roleId, deletedAt: null, ...(scope as any) } as any)\n if (!role) return NextResponse.json({ error: 'Role not found' }, { status: 404 })\n\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId ?? '',\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n resourceKind: 'perspectives.role_perspective',\n resourceId: roleId,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: { tableId, roleId },\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n let clearedCount = 0\n try {\n clearedCount = await clearRolePerspectives(em, cache, {\n tableId,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n roleIds: [roleId],\n expectedUpdatedAtByRoleId: parsedBody?.roleExpectedUpdatedAtByRoleId,\n expectedUpdatedAtByPerspectiveId: parsedBody?.roleExpectedUpdatedAtByPerspectiveId,\n request: req,\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n throw err\n }\n\n if (clearedCount > 0 && guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId ?? '',\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n resourceKind: 'perspectives.role_perspective',\n resourceId: roleId,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return NextResponse.json({ success: true })\n}\n\nconst rolePerspectiveDeleteParamsSchema = z.object({\n tableId: z.string().min(1),\n roleId: z.string().uuid(),\n})\n\nconst rolePerspectiveDeleteDoc: OpenApiMethodDoc = {\n summary: 'Clear role perspectives for a table',\n description: 'Removes all role-level perspectives associated with the provided role identifier for the table.',\n tags: [perspectivesTag],\n requestBody: {\n contentType: 'application/json',\n schema: rolePerspectiveDeleteBodySchema,\n description: 'Optional per-role or per-perspective optimistic-lock tokens.',\n },\n responses: [\n { status: 200, description: 'Role perspectives cleared.', schema: perspectivesSuccessSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid identifiers supplied', schema: perspectivesErrorSchema },\n { status: 401, description: 'Authentication required', schema: perspectivesErrorSchema },\n { status: 403, description: 'Missing perspectives.role_defaults feature', schema: perspectivesErrorSchema },\n { status: 409, description: 'Optimistic lock conflict', schema: perspectivesErrorSchema },\n { status: 404, description: 'Role not found in scope', schema: perspectivesErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: perspectivesTag,\n summary: 'Delete role-level perspectives',\n pathParams: rolePerspectiveDeleteParamsSchema,\n methods: {\n DELETE: rolePerspectiveDeleteDoc,\n },\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,uBAAuB;AAChC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,6BAA6B;AACtC,SAAS,YAAY;AAErB,SAAS,iBAAiB,yBAAyB,iCAAiC;AAE7E,MAAM,WAAW;AAAA,EACtB,QAAQ,EAAE,aAAa,MAAM,iBAAiB,CAAC,4BAA4B,EAAE;AAC/E;AAEA,MAAM,cAAc,CAAC,UAAiD;AACpE,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,MAAM,MAAM,QAAQ,KAAK,IAAI,MAAM,CAAC,IAAI;AAC9C,MAAI;AACF,WAAO,mBAAmB,GAAG;AAAA,EAC/B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,MAAM,kCAAkC,EAAE,OAAO;AAAA,EAC/C,+BAA+B,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,CAAC,EAAE,SAAS;AAAA,EAClG,sCAAsC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,CAAC,EAAE,SAAS;AAC3G,CAAC,EAAE,SAAS;AAEZ,eAAsB,OAAO,KAAc,KAAsD;AAC/F,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAE9E,QAAM,UAAU,YAAY,IAAI,QAAQ,OAAO,EAAE,KAAK;AACtD,QAAM,SAAS,YAAY,IAAI,QAAQ,MAAM,EAAE,KAAK;AACpD,MAAI,CAAC,WAAW,CAAC,QAAQ;AACvB,WAAO,aAAa,KAAK,EAAE,OAAO,qBAAqB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC3E;AAEA,MAAI,aAA8D;AAClE,MAAI,IAAI,SAAS,IAAI,QAAQ,IAAI,cAAc,KAAK,IAAI,SAAS,kBAAkB,GAAG;AACpF,QAAI;AACF,YAAM,UAAU,MAAM,IAAI,KAAK;AAC/B,UAAI,QAAQ,KAAK,EAAE,SAAS,GAAG;AAC7B,cAAM,SAAS,gCAAgC,UAAU,KAAK,MAAM,OAAO,CAAC;AAC5E,YAAI,CAAC,OAAO,SAAS;AACnB,iBAAO,aAAa,KAAK,EAAE,OAAO,mBAAmB,SAAS,OAAO,MAAM,QAAQ,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,QACzG;AACA,qBAAa,OAAO;AAAA,MACtB;AAAA,IACF,QAAQ;AACN,aAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACrE;AAAA,EACF;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,KAAK,UAAU,QAAQ,IAAI;AACjC,QAAM,SAAS,MAA0D;AACvE,QAAI;AACF,aAAO,UAAU,QAAQ,OAAO;AAAA,IAClC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF,GAAG;AAEH,QAAM,QAAQ,KAAK,WACf,EAAE,KAAK,CAAC,EAAE,UAAU,KAAK,SAAS,GAAG,EAAE,UAAU,KAAK,CAAC,EAAE,IACzD,EAAE,UAAU,KAAK;AAErB,QAAM,OAAO,MAAM,GAAG,QAAQ,MAAM,EAAE,IAAI,QAAQ,WAAW,MAAM,GAAI,MAAc,CAAQ;AAC7F,MAAI,CAAC,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAEhF,QAAM,cAAc,MAAM,0BAA0B,WAAW;AAAA,IAC7D,UAAU,KAAK,YAAY;AAAA,IAC3B,gBAAgB,KAAK,SAAS;AAAA,IAC9B,QAAQ,KAAK;AAAA,IACb,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,eAAe,IAAI;AAAA,IACnB,gBAAgB,IAAI;AAAA,IACpB,iBAAiB,EAAE,SAAS,OAAO;AAAA,EACrC,CAAC;AACD,MAAI,eAAe,CAAC,YAAY,IAAI;AAClC,WAAO,aAAa,KAAK,YAAY,MAAM,EAAE,QAAQ,YAAY,OAAO,CAAC;AAAA,EAC3E;AAEA,MAAI,eAAe;AACnB,MAAI;AACF,mBAAe,MAAM,sBAAsB,IAAI,OAAO;AAAA,MACpD;AAAA,MACA,UAAU,KAAK,YAAY;AAAA,MAC3B,gBAAgB,KAAK,SAAS;AAAA,MAC9B,SAAS,CAAC,MAAM;AAAA,MAChB,2BAA2B,YAAY;AAAA,MACvC,kCAAkC,YAAY;AAAA,MAC9C,SAAS;AAAA,IACX,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,QAAI,gBAAgB,GAAG,GAAG;AACxB,aAAO,aAAa,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IAC3D;AACA,UAAM;AAAA,EACR;AAEA,MAAI,eAAe,KAAK,aAAa,MAAM,YAAY,uBAAuB;AAC5E,UAAM,iCAAiC,WAAW;AAAA,MAChD,UAAU,KAAK,YAAY;AAAA,MAC3B,gBAAgB,KAAK,SAAS;AAAA,MAC9B,QAAQ,KAAK;AAAA,MACb,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,eAAe,IAAI;AAAA,MACnB,gBAAgB,IAAI;AAAA,MACpB,UAAU,YAAY,YAAY;AAAA,IACpC,CAAC;AAAA,EACH;AAEA,SAAO,aAAa,KAAK,EAAE,SAAS,KAAK,CAAC;AAC5C;AAEA,MAAM,oCAAoC,EAAE,OAAO;AAAA,EACjD,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACzB,QAAQ,EAAE,OAAO,EAAE,KAAK;AAC1B,CAAC;AAED,MAAM,2BAA6C;AAAA,EACjD,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM,CAAC,eAAe;AAAA,EACtB,aAAa;AAAA,IACX,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,aAAa;AAAA,EACf;AAAA,EACA,WAAW;AAAA,IACT,EAAE,QAAQ,KAAK,aAAa,8BAA8B,QAAQ,0BAA0B;AAAA,EAC9F;AAAA,EACA,QAAQ;AAAA,IACN,EAAE,QAAQ,KAAK,aAAa,gCAAgC,QAAQ,wBAAwB;AAAA,IAC5F,EAAE,QAAQ,KAAK,aAAa,2BAA2B,QAAQ,wBAAwB;AAAA,IACvF,EAAE,QAAQ,KAAK,aAAa,8CAA8C,QAAQ,wBAAwB;AAAA,IAC1G,EAAE,QAAQ,KAAK,aAAa,4BAA4B,QAAQ,wBAAwB;AAAA,IACxF,EAAE,QAAQ,KAAK,aAAa,2BAA2B,QAAQ,wBAAwB;AAAA,EACzF;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,SAAS;AAAA,IACP,QAAQ;AAAA,EACV;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -71,13 +71,19 @@ async function GET(_req, ctx) {
|
|
|
71
71
|
) ?? false;
|
|
72
72
|
const roleScope = auth.tenantId ? { $or: [{ tenantId: auth.tenantId }, { tenantId: null }] } : { tenantId: null };
|
|
73
73
|
const availableRoles = canApplyToRoles ? await em.find(Role, { ...roleScope, deletedAt: null }, { orderBy: { name: "asc" } }) : assignedRoles;
|
|
74
|
+
const manageableRoleIds = canApplyToRoles ? availableRoles.map((role) => role.id) : assignedRoleIds;
|
|
74
75
|
const state = await loadPerspectivesState(em, cache, {
|
|
75
76
|
scope: buildScope(auth),
|
|
76
77
|
tableId,
|
|
77
78
|
roleIds: assignedRoleIds
|
|
78
79
|
});
|
|
80
|
+
const manageableState = manageableRoleIds.length === assignedRoleIds.length && manageableRoleIds.every((roleId) => assignedRoleIds.includes(roleId)) ? state : await loadPerspectivesState(em, cache, {
|
|
81
|
+
scope: buildScope(auth),
|
|
82
|
+
tableId,
|
|
83
|
+
roleIds: manageableRoleIds
|
|
84
|
+
});
|
|
79
85
|
const rolePerspectiveByRole = /* @__PURE__ */ new Map();
|
|
80
|
-
for (const item of
|
|
86
|
+
for (const item of manageableState.rolePerspectives) {
|
|
81
87
|
const entry = rolePerspectiveByRole.get(item.roleId) ?? { hasDefault: false, count: 0 };
|
|
82
88
|
entry.count += 1;
|
|
83
89
|
entry.hasDefault = entry.hasDefault || item.isDefault;
|
|
@@ -91,6 +97,10 @@ async function GET(_req, ctx) {
|
|
|
91
97
|
...rp,
|
|
92
98
|
roleName: availableRoles.find((role) => role.id === rp.roleId)?.name ?? assignedRoles.find((role) => role.id === rp.roleId)?.name ?? null
|
|
93
99
|
})),
|
|
100
|
+
manageableRolePerspectives: manageableState.rolePerspectives.map((rp) => ({
|
|
101
|
+
...rp,
|
|
102
|
+
roleName: availableRoles.find((role) => role.id === rp.roleId)?.name ?? assignedRoles.find((role) => role.id === rp.roleId)?.name ?? null
|
|
103
|
+
})),
|
|
94
104
|
roles: availableRoles.map((role) => {
|
|
95
105
|
const stats = rolePerspectiveByRole.get(role.id);
|
|
96
106
|
return {
|
|
@@ -217,7 +227,10 @@ async function POST(req, ctx) {
|
|
|
217
227
|
name: parsed.data.name,
|
|
218
228
|
settings: parsed.data.settings,
|
|
219
229
|
setDefault: parsed.data.setRoleDefault ?? false
|
|
220
|
-
}
|
|
230
|
+
},
|
|
231
|
+
expectedUpdatedAtByRoleId: parsed.data.roleExpectedUpdatedAtByRoleId,
|
|
232
|
+
expectedUpdatedAtByPerspectiveId: parsed.data.roleExpectedUpdatedAtByPerspectiveId,
|
|
233
|
+
request: req
|
|
221
234
|
});
|
|
222
235
|
}
|
|
223
236
|
},
|
|
@@ -227,7 +240,10 @@ async function POST(req, ctx) {
|
|
|
227
240
|
tableId,
|
|
228
241
|
tenantId: auth.tenantId ?? null,
|
|
229
242
|
organizationId: auth.orgId ?? null,
|
|
230
|
-
roleIds: clearRoleIds
|
|
243
|
+
roleIds: clearRoleIds,
|
|
244
|
+
expectedUpdatedAtByRoleId: parsed.data.roleExpectedUpdatedAtByRoleId,
|
|
245
|
+
expectedUpdatedAtByPerspectiveId: parsed.data.roleExpectedUpdatedAtByPerspectiveId,
|
|
246
|
+
request: req
|
|
231
247
|
});
|
|
232
248
|
}
|
|
233
249
|
}
|
|
@@ -301,7 +317,8 @@ const perspectivesPostDoc = {
|
|
|
301
317
|
errors: [
|
|
302
318
|
{ status: 400, description: "Validation failed or invalid roles provided", schema: perspectivesErrorSchema },
|
|
303
319
|
{ status: 401, description: "Authentication required", schema: perspectivesErrorSchema },
|
|
304
|
-
{ status: 403, description: "Missing perspectives.role_defaults feature for role updates", schema: perspectivesErrorSchema }
|
|
320
|
+
{ status: 403, description: "Missing perspectives.role_defaults feature for role updates", schema: perspectivesErrorSchema },
|
|
321
|
+
{ status: 409, description: "Optimistic lock conflict", schema: perspectivesErrorSchema }
|
|
305
322
|
]
|
|
306
323
|
};
|
|
307
324
|
const openApi = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/perspectives/api/%5BtableId%5D/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport { perspectiveSaveSchema } from '@open-mercato/core/modules/perspectives/data/validators'\nimport {\n loadPerspectivesState,\n saveUserPerspective,\n saveRolePerspectives,\n clearRolePerspectives,\n type PerspectiveScope,\n} from '@open-mercato/core/modules/perspectives/services/perspectiveService'\nimport { Role } from '@open-mercato/core/modules/auth/data/entities'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport {\n perspectivesTag,\n perspectivesErrorSchema,\n perspectivesIndexResponseSchema,\n perspectiveSaveResponseSchema,\n} from '../openapi'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['perspectives.use'] },\n POST: { requireAuth: true, requireFeatures: ['perspectives.use'] },\n}\n\nconst decodeParam = (value: string | string[] | undefined): string => {\n if (!value) return ''\n const raw = Array.isArray(value) ? value[0] : value\n try {\n return decodeURIComponent(raw)\n } catch {\n return raw\n }\n}\n\nfunction buildScope(auth: NonNullable<Awaited<ReturnType<typeof getAuthFromRequest>>>): PerspectiveScope {\n return {\n userId: auth.sub,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n }\n}\n\nexport async function GET(_req: Request, ctx: { params: { tableId: string } }) {\n const auth = await getAuthFromRequest(_req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const tableId = decodeParam(ctx.params?.tableId).trim()\n if (!tableId) return NextResponse.json({ error: 'Invalid table id' }, { status: 400 })\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as import('@mikro-orm/postgresql').EntityManager\n const cache = ((): import('@open-mercato/cache').CacheStrategy | null => {\n try {\n return container.resolve('cache') as import('@open-mercato/cache').CacheStrategy\n } catch {\n return null\n }\n })()\n const rbac = container.resolve('rbacService') as {\n userHasAllFeatures?: (\n userId: string,\n features: string[],\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<boolean>\n }\n\n const assignedRoleNames = Array.isArray(auth.roles)\n ? Array.from(new Set(auth.roles.filter((role): role is string => typeof role === 'string' && role.trim().length > 0)))\n : []\n const assignedRoles = assignedRoleNames.length && auth.tenantId\n ? await em.find(Role, {\n name: { $in: assignedRoleNames as any },\n tenantId: auth.tenantId,\n deletedAt: null,\n } as any, { orderBy: { name: 'asc' } })\n : []\n const assignedRoleIds = assignedRoles.map((role) => role.id)\n\n const canApplyToRoles = await rbac.userHasAllFeatures?.(\n auth.sub,\n ['perspectives.role_defaults'],\n { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },\n ) ?? false\n\n const roleScope = auth.tenantId\n ? { $or: [{ tenantId: auth.tenantId }, { tenantId: null }] }\n : { tenantId: null }\n const availableRoles = canApplyToRoles\n ? await em.find(Role, { ...roleScope as any, deletedAt: null } as any, { orderBy: { name: 'asc' } })\n : assignedRoles\n\n const state = await loadPerspectivesState(em, cache, {\n scope: buildScope(auth),\n tableId,\n roleIds: assignedRoleIds,\n })\n\n const rolePerspectiveByRole = new Map<string, { hasDefault: boolean; count: number }>()\n for (const item of state.rolePerspectives) {\n const entry = rolePerspectiveByRole.get(item.roleId) ?? { hasDefault: false, count: 0 }\n entry.count += 1\n entry.hasDefault = entry.hasDefault || item.isDefault\n rolePerspectiveByRole.set(item.roleId, entry)\n }\n\n return NextResponse.json({\n tableId,\n perspectives: state.personal,\n defaultPerspectiveId: state.personalDefaultId,\n rolePerspectives: state.rolePerspectives.map((rp) => ({\n ...rp,\n roleName: availableRoles.find((role) => role.id === rp.roleId)?.name ?? assignedRoles.find((role) => role.id === rp.roleId)?.name ?? null,\n })),\n roles: availableRoles.map((role) => {\n const stats = rolePerspectiveByRole.get(role.id)\n return {\n id: role.id,\n name: role.name,\n hasPerspective: Boolean(stats?.count),\n hasDefault: Boolean(stats?.hasDefault),\n }\n }),\n canApplyToRoles,\n })\n}\n\nexport async function POST(req: Request, ctx: { params: { tableId: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const tableId = decodeParam(ctx.params?.tableId).trim()\n if (!tableId) return NextResponse.json({ error: 'Invalid table id' }, { status: 400 })\n\n let parsedBody: unknown\n try {\n parsedBody = await req.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 })\n }\n\n const parsed = perspectiveSaveSchema.safeParse(parsedBody)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as import('@mikro-orm/postgresql').EntityManager\n const cache = ((): import('@open-mercato/cache').CacheStrategy | null => {\n try {\n return container.resolve('cache') as import('@open-mercato/cache').CacheStrategy\n } catch {\n return null\n }\n })()\n const rbac = container.resolve('rbacService') as {\n userHasAllFeatures?: (\n userId: string,\n features: string[],\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<boolean>\n }\n\n const scope = buildScope(auth)\n\n const applyToRoles = Array.from(new Set(parsed.data.applyToRoles ?? [])).filter((id) => id.trim().length > 0)\n const clearRoleIds = Array.from(new Set(parsed.data.clearRoleIds ?? [])).filter((id) => id.trim().length > 0)\n const hasRoleOps = applyToRoles.length > 0 || clearRoleIds.length > 0\n const targetRoleIds = Array.from(new Set([...applyToRoles, ...clearRoleIds]))\n\n if (hasRoleOps) {\n const canApplyToRoles = await rbac.userHasAllFeatures?.(\n auth.sub,\n ['perspectives.role_defaults'],\n { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },\n ) ?? false\n\n if (!canApplyToRoles) {\n return NextResponse.json({ error: 'Forbidden', requiredFeatures: ['perspectives.role_defaults'] }, { status: 403 })\n }\n\n const roleScope = auth.tenantId\n ? { $or: [{ tenantId: auth.tenantId }, { tenantId: null }] }\n : { tenantId: null }\n const roles = await em.find(Role, {\n id: { $in: targetRoleIds as any },\n ...(roleScope as any),\n deletedAt: null,\n } as any)\n const validRoleIds = new Set(roles.map((role) => role.id))\n\n const missing = targetRoleIds.filter((id) => !validRoleIds.has(id))\n if (missing.length) {\n return NextResponse.json({ error: 'Invalid roles', missing }, { status: 400 })\n }\n }\n\n const guardResourceId = parsed.data.perspectiveId ?? tableId\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId ?? '',\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n resourceKind: 'perspectives.perspective',\n resourceId: guardResourceId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: { ...parsed.data, tableId },\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n let roleGuardResult: Awaited<ReturnType<typeof validateCrudMutationGuard>> | null = null\n if (hasRoleOps) {\n roleGuardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId ?? '',\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n resourceKind: 'perspectives.role_perspective',\n resourceId: targetRoleIds.join(','),\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: {\n tableId,\n applyToRoles,\n clearRoleIds,\n name: parsed.data.name,\n settings: parsed.data.settings,\n setRoleDefault: parsed.data.setRoleDefault ?? false,\n },\n })\n }\n if (roleGuardResult && !roleGuardResult.ok) {\n return NextResponse.json(roleGuardResult.body, { status: roleGuardResult.status })\n }\n\n let saved: Awaited<ReturnType<typeof saveUserPerspective>> | null = null\n let updatedRolePerspectives: Awaited<ReturnType<typeof saveRolePerspectives>> | null = null\n let clearedRolePerspectiveCount = 0\n\n try {\n await withAtomicFlush(em, [\n async () => {\n saved = await saveUserPerspective(em, cache, {\n scope,\n tableId,\n input: parsed.data,\n request: req,\n })\n },\n async () => {\n if (applyToRoles.length) {\n updatedRolePerspectives = await saveRolePerspectives(em, cache, {\n tableId,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n input: {\n roleIds: applyToRoles,\n name: parsed.data.name,\n settings: parsed.data.settings,\n setDefault: parsed.data.setRoleDefault ?? false,\n },\n })\n }\n },\n async () => {\n if (clearRoleIds.length) {\n clearedRolePerspectiveCount = await clearRolePerspectives(em, cache, {\n tableId,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n roleIds: clearRoleIds,\n })\n }\n },\n ], { transaction: true })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n throw err\n }\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId ?? '',\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n resourceKind: 'perspectives.perspective',\n resourceId: guardResourceId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n const didWriteRolePerspectives = applyToRoles.length > 0 || clearedRolePerspectiveCount > 0\n if (didWriteRolePerspectives && roleGuardResult?.ok && roleGuardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId ?? '',\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n resourceKind: 'perspectives.role_perspective',\n resourceId: targetRoleIds.join(','),\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: roleGuardResult.metadata ?? null,\n })\n }\n\n return NextResponse.json({\n perspective: saved,\n rolePerspectives: updatedRolePerspectives ?? [],\n clearedRoleIds: clearRoleIds ?? [],\n })\n}\n\nconst perspectivePathParamsSchema = z.object({\n tableId: z.string().min(1),\n})\n\nconst perspectivesGetDoc: OpenApiMethodDoc = {\n summary: 'Load perspectives for a table',\n description: 'Returns personal perspectives and available role defaults for the requested table identifier.',\n tags: [perspectivesTag],\n responses: [\n { status: 200, description: 'Current perspectives and defaults.', schema: perspectivesIndexResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid table identifier', schema: perspectivesErrorSchema },\n { status: 401, description: 'Authentication required', schema: perspectivesErrorSchema },\n ],\n}\n\nconst perspectivesPostDoc: OpenApiMethodDoc = {\n summary: 'Create or update a perspective',\n description: 'Saves a personal perspective and optionally applies the same configuration to selected roles.',\n tags: [perspectivesTag],\n requestBody: {\n contentType: 'application/json',\n schema: perspectiveSaveSchema,\n description: 'Perspective payload including optional role defaults.',\n },\n responses: [\n { status: 200, description: 'Perspective saved successfully.', schema: perspectiveSaveResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed or invalid roles provided', schema: perspectivesErrorSchema },\n { status: 401, description: 'Authentication required', schema: perspectivesErrorSchema },\n { status: 403, description: 'Missing perspectives.role_defaults feature for role updates', schema: perspectivesErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: perspectivesTag,\n summary: 'Manage table perspectives',\n pathParams: perspectivePathParamsSchema,\n methods: {\n GET: perspectivesGetDoc,\n POST: perspectivesPostDoc,\n },\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport { perspectiveSaveSchema } from '@open-mercato/core/modules/perspectives/data/validators'\nimport {\n loadPerspectivesState,\n saveUserPerspective,\n saveRolePerspectives,\n clearRolePerspectives,\n type PerspectiveScope,\n} from '@open-mercato/core/modules/perspectives/services/perspectiveService'\nimport { Role } from '@open-mercato/core/modules/auth/data/entities'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport {\n perspectivesTag,\n perspectivesErrorSchema,\n perspectivesIndexResponseSchema,\n perspectiveSaveResponseSchema,\n} from '../openapi'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['perspectives.use'] },\n POST: { requireAuth: true, requireFeatures: ['perspectives.use'] },\n}\n\nconst decodeParam = (value: string | string[] | undefined): string => {\n if (!value) return ''\n const raw = Array.isArray(value) ? value[0] : value\n try {\n return decodeURIComponent(raw)\n } catch {\n return raw\n }\n}\n\nfunction buildScope(auth: NonNullable<Awaited<ReturnType<typeof getAuthFromRequest>>>): PerspectiveScope {\n return {\n userId: auth.sub,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n }\n}\n\nexport async function GET(_req: Request, ctx: { params: { tableId: string } }) {\n const auth = await getAuthFromRequest(_req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const tableId = decodeParam(ctx.params?.tableId).trim()\n if (!tableId) return NextResponse.json({ error: 'Invalid table id' }, { status: 400 })\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as import('@mikro-orm/postgresql').EntityManager\n const cache = ((): import('@open-mercato/cache').CacheStrategy | null => {\n try {\n return container.resolve('cache') as import('@open-mercato/cache').CacheStrategy\n } catch {\n return null\n }\n })()\n const rbac = container.resolve('rbacService') as {\n userHasAllFeatures?: (\n userId: string,\n features: string[],\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<boolean>\n }\n\n const assignedRoleNames = Array.isArray(auth.roles)\n ? Array.from(new Set(auth.roles.filter((role): role is string => typeof role === 'string' && role.trim().length > 0)))\n : []\n const assignedRoles = assignedRoleNames.length && auth.tenantId\n ? await em.find(Role, {\n name: { $in: assignedRoleNames as any },\n tenantId: auth.tenantId,\n deletedAt: null,\n } as any, { orderBy: { name: 'asc' } })\n : []\n const assignedRoleIds = assignedRoles.map((role) => role.id)\n\n const canApplyToRoles = await rbac.userHasAllFeatures?.(\n auth.sub,\n ['perspectives.role_defaults'],\n { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },\n ) ?? false\n\n const roleScope = auth.tenantId\n ? { $or: [{ tenantId: auth.tenantId }, { tenantId: null }] }\n : { tenantId: null }\n const availableRoles = canApplyToRoles\n ? await em.find(Role, { ...roleScope as any, deletedAt: null } as any, { orderBy: { name: 'asc' } })\n : assignedRoles\n const manageableRoleIds = canApplyToRoles\n ? availableRoles.map((role) => role.id)\n : assignedRoleIds\n\n const state = await loadPerspectivesState(em, cache, {\n scope: buildScope(auth),\n tableId,\n roleIds: assignedRoleIds,\n })\n const manageableState = manageableRoleIds.length === assignedRoleIds.length\n && manageableRoleIds.every((roleId) => assignedRoleIds.includes(roleId))\n ? state\n : await loadPerspectivesState(em, cache, {\n scope: buildScope(auth),\n tableId,\n roleIds: manageableRoleIds,\n })\n\n const rolePerspectiveByRole = new Map<string, { hasDefault: boolean; count: number }>()\n for (const item of manageableState.rolePerspectives) {\n const entry = rolePerspectiveByRole.get(item.roleId) ?? { hasDefault: false, count: 0 }\n entry.count += 1\n entry.hasDefault = entry.hasDefault || item.isDefault\n rolePerspectiveByRole.set(item.roleId, entry)\n }\n\n return NextResponse.json({\n tableId,\n perspectives: state.personal,\n defaultPerspectiveId: state.personalDefaultId,\n rolePerspectives: state.rolePerspectives.map((rp) => ({\n ...rp,\n roleName: availableRoles.find((role) => role.id === rp.roleId)?.name ?? assignedRoles.find((role) => role.id === rp.roleId)?.name ?? null,\n })),\n manageableRolePerspectives: manageableState.rolePerspectives.map((rp) => ({\n ...rp,\n roleName: availableRoles.find((role) => role.id === rp.roleId)?.name ?? assignedRoles.find((role) => role.id === rp.roleId)?.name ?? null,\n })),\n roles: availableRoles.map((role) => {\n const stats = rolePerspectiveByRole.get(role.id)\n return {\n id: role.id,\n name: role.name,\n hasPerspective: Boolean(stats?.count),\n hasDefault: Boolean(stats?.hasDefault),\n }\n }),\n canApplyToRoles,\n })\n}\n\nexport async function POST(req: Request, ctx: { params: { tableId: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const tableId = decodeParam(ctx.params?.tableId).trim()\n if (!tableId) return NextResponse.json({ error: 'Invalid table id' }, { status: 400 })\n\n let parsedBody: unknown\n try {\n parsedBody = await req.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 })\n }\n\n const parsed = perspectiveSaveSchema.safeParse(parsedBody)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as import('@mikro-orm/postgresql').EntityManager\n const cache = ((): import('@open-mercato/cache').CacheStrategy | null => {\n try {\n return container.resolve('cache') as import('@open-mercato/cache').CacheStrategy\n } catch {\n return null\n }\n })()\n const rbac = container.resolve('rbacService') as {\n userHasAllFeatures?: (\n userId: string,\n features: string[],\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<boolean>\n }\n\n const scope = buildScope(auth)\n\n const applyToRoles = Array.from(new Set(parsed.data.applyToRoles ?? [])).filter((id) => id.trim().length > 0)\n const clearRoleIds = Array.from(new Set(parsed.data.clearRoleIds ?? [])).filter((id) => id.trim().length > 0)\n const hasRoleOps = applyToRoles.length > 0 || clearRoleIds.length > 0\n const targetRoleIds = Array.from(new Set([...applyToRoles, ...clearRoleIds]))\n\n if (hasRoleOps) {\n const canApplyToRoles = await rbac.userHasAllFeatures?.(\n auth.sub,\n ['perspectives.role_defaults'],\n { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },\n ) ?? false\n\n if (!canApplyToRoles) {\n return NextResponse.json({ error: 'Forbidden', requiredFeatures: ['perspectives.role_defaults'] }, { status: 403 })\n }\n\n const roleScope = auth.tenantId\n ? { $or: [{ tenantId: auth.tenantId }, { tenantId: null }] }\n : { tenantId: null }\n const roles = await em.find(Role, {\n id: { $in: targetRoleIds as any },\n ...(roleScope as any),\n deletedAt: null,\n } as any)\n const validRoleIds = new Set(roles.map((role) => role.id))\n\n const missing = targetRoleIds.filter((id) => !validRoleIds.has(id))\n if (missing.length) {\n return NextResponse.json({ error: 'Invalid roles', missing }, { status: 400 })\n }\n }\n\n const guardResourceId = parsed.data.perspectiveId ?? tableId\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId ?? '',\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n resourceKind: 'perspectives.perspective',\n resourceId: guardResourceId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: { ...parsed.data, tableId },\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n let roleGuardResult: Awaited<ReturnType<typeof validateCrudMutationGuard>> | null = null\n if (hasRoleOps) {\n roleGuardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId ?? '',\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n resourceKind: 'perspectives.role_perspective',\n resourceId: targetRoleIds.join(','),\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: {\n tableId,\n applyToRoles,\n clearRoleIds,\n name: parsed.data.name,\n settings: parsed.data.settings,\n setRoleDefault: parsed.data.setRoleDefault ?? false,\n },\n })\n }\n if (roleGuardResult && !roleGuardResult.ok) {\n return NextResponse.json(roleGuardResult.body, { status: roleGuardResult.status })\n }\n\n let saved: Awaited<ReturnType<typeof saveUserPerspective>> | null = null\n let updatedRolePerspectives: Awaited<ReturnType<typeof saveRolePerspectives>> | null = null\n let clearedRolePerspectiveCount = 0\n\n try {\n await withAtomicFlush(em, [\n async () => {\n saved = await saveUserPerspective(em, cache, {\n scope,\n tableId,\n input: parsed.data,\n request: req,\n })\n },\n async () => {\n if (applyToRoles.length) {\n updatedRolePerspectives = await saveRolePerspectives(em, cache, {\n tableId,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n input: {\n roleIds: applyToRoles,\n name: parsed.data.name,\n settings: parsed.data.settings,\n setDefault: parsed.data.setRoleDefault ?? false,\n },\n expectedUpdatedAtByRoleId: parsed.data.roleExpectedUpdatedAtByRoleId,\n expectedUpdatedAtByPerspectiveId: parsed.data.roleExpectedUpdatedAtByPerspectiveId,\n request: req,\n })\n }\n },\n async () => {\n if (clearRoleIds.length) {\n clearedRolePerspectiveCount = await clearRolePerspectives(em, cache, {\n tableId,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n roleIds: clearRoleIds,\n expectedUpdatedAtByRoleId: parsed.data.roleExpectedUpdatedAtByRoleId,\n expectedUpdatedAtByPerspectiveId: parsed.data.roleExpectedUpdatedAtByPerspectiveId,\n request: req,\n })\n }\n },\n ], { transaction: true })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n throw err\n }\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId ?? '',\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n resourceKind: 'perspectives.perspective',\n resourceId: guardResourceId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n const didWriteRolePerspectives = applyToRoles.length > 0 || clearedRolePerspectiveCount > 0\n if (didWriteRolePerspectives && roleGuardResult?.ok && roleGuardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId ?? '',\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n resourceKind: 'perspectives.role_perspective',\n resourceId: targetRoleIds.join(','),\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: roleGuardResult.metadata ?? null,\n })\n }\n\n return NextResponse.json({\n perspective: saved,\n rolePerspectives: updatedRolePerspectives ?? [],\n clearedRoleIds: clearRoleIds ?? [],\n })\n}\n\nconst perspectivePathParamsSchema = z.object({\n tableId: z.string().min(1),\n})\n\nconst perspectivesGetDoc: OpenApiMethodDoc = {\n summary: 'Load perspectives for a table',\n description: 'Returns personal perspectives and available role defaults for the requested table identifier.',\n tags: [perspectivesTag],\n responses: [\n { status: 200, description: 'Current perspectives and defaults.', schema: perspectivesIndexResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid table identifier', schema: perspectivesErrorSchema },\n { status: 401, description: 'Authentication required', schema: perspectivesErrorSchema },\n ],\n}\n\nconst perspectivesPostDoc: OpenApiMethodDoc = {\n summary: 'Create or update a perspective',\n description: 'Saves a personal perspective and optionally applies the same configuration to selected roles.',\n tags: [perspectivesTag],\n requestBody: {\n contentType: 'application/json',\n schema: perspectiveSaveSchema,\n description: 'Perspective payload including optional role defaults.',\n },\n responses: [\n { status: 200, description: 'Perspective saved successfully.', schema: perspectiveSaveResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Validation failed or invalid roles provided', schema: perspectivesErrorSchema },\n { status: 401, description: 'Authentication required', schema: perspectivesErrorSchema },\n { status: 403, description: 'Missing perspectives.role_defaults feature for role updates', schema: perspectivesErrorSchema },\n { status: 409, description: 'Optimistic lock conflict', schema: perspectivesErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: perspectivesTag,\n summary: 'Manage table perspectives',\n pathParams: perspectivePathParamsSchema,\n methods: {\n GET: perspectivesGetDoc,\n POST: perspectivesPostDoc,\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -24,6 +24,7 @@ const perspectivesIndexResponseSchema = z.object({
|
|
|
24
24
|
perspectives: z.array(perspectiveDtoSchema),
|
|
25
25
|
defaultPerspectiveId: z.string().uuid().nullable(),
|
|
26
26
|
rolePerspectives: z.array(rolePerspectiveDtoSchema),
|
|
27
|
+
manageableRolePerspectives: z.array(rolePerspectiveDtoSchema),
|
|
27
28
|
roles: z.array(
|
|
28
29
|
z.object({
|
|
29
30
|
id: z.string().uuid(),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/perspectives/api/openapi.ts"],
|
|
4
|
-
"sourcesContent": ["import { z } from 'zod'\nimport { perspectiveSettingsSchema } from '../data/validators'\n\nexport const perspectivesTag = 'Perspectives'\n\nexport const perspectivesErrorSchema = z.object({\n error: z.string(),\n}).passthrough()\n\nexport const perspectiveDtoSchema = z.object({\n id: z.string().uuid(),\n name: z.string(),\n tableId: z.string(),\n settings: perspectiveSettingsSchema,\n isDefault: z.boolean(),\n createdAt: z.string(),\n updatedAt: z.string().nullable(),\n})\n\nexport const rolePerspectiveDtoSchema = perspectiveDtoSchema.extend({\n roleId: z.string().uuid(),\n tenantId: z.string().uuid().nullable(),\n organizationId: z.string().uuid().nullable(),\n roleName: z.string().nullable(),\n})\n\nexport const perspectivesIndexResponseSchema = z.object({\n tableId: z.string(),\n perspectives: z.array(perspectiveDtoSchema),\n defaultPerspectiveId: z.string().uuid().nullable(),\n rolePerspectives: z.array(rolePerspectiveDtoSchema),\n roles: z.array(\n z.object({\n id: z.string().uuid(),\n name: z.string(),\n hasPerspective: z.boolean(),\n hasDefault: z.boolean(),\n }),\n ),\n canApplyToRoles: z.boolean(),\n})\n\nexport const perspectiveSaveResponseSchema = z.object({\n perspective: perspectiveDtoSchema,\n rolePerspectives: z.array(rolePerspectiveDtoSchema),\n clearedRoleIds: z.array(z.string().uuid()),\n})\n\nexport const perspectivesSuccessSchema = z.object({\n success: z.literal(true),\n})\n
|
|
5
|
-
"mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,iCAAiC;AAEnC,MAAM,kBAAkB;AAExB,MAAM,0BAA0B,EAAE,OAAO;AAAA,EAC9C,OAAO,EAAE,OAAO;AAClB,CAAC,EAAE,YAAY;AAER,MAAM,uBAAuB,EAAE,OAAO;AAAA,EAC3C,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,MAAM,EAAE,OAAO;AAAA,EACf,SAAS,EAAE,OAAO;AAAA,EAClB,UAAU;AAAA,EACV,WAAW,EAAE,QAAQ;AAAA,EACrB,WAAW,EAAE,OAAO;AAAA,EACpB,WAAW,EAAE,OAAO,EAAE,SAAS;AACjC,CAAC;AAEM,MAAM,2BAA2B,qBAAqB,OAAO;AAAA,EAClE,QAAQ,EAAE,OAAO,EAAE,KAAK;AAAA,EACxB,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACrC,gBAAgB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC3C,UAAU,EAAE,OAAO,EAAE,SAAS;AAChC,CAAC;AAEM,MAAM,kCAAkC,EAAE,OAAO;AAAA,EACtD,SAAS,EAAE,OAAO;AAAA,EAClB,cAAc,EAAE,MAAM,oBAAoB;AAAA,EAC1C,sBAAsB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACjD,kBAAkB,EAAE,MAAM,wBAAwB;AAAA,EAClD,OAAO,EAAE;AAAA,IACP,EAAE,OAAO;AAAA,MACP,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,MACpB,MAAM,EAAE,OAAO;AAAA,MACf,gBAAgB,EAAE,QAAQ;AAAA,MAC1B,YAAY,EAAE,QAAQ;AAAA,IACxB,CAAC;AAAA,EACH;AAAA,EACA,iBAAiB,EAAE,QAAQ;AAC7B,CAAC;AAEM,MAAM,gCAAgC,EAAE,OAAO;AAAA,EACpD,aAAa;AAAA,EACb,kBAAkB,EAAE,MAAM,wBAAwB;AAAA,EAClD,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;AAC3C,CAAC;AAEM,MAAM,4BAA4B,EAAE,OAAO;AAAA,EAChD,SAAS,EAAE,QAAQ,IAAI;AACzB,CAAC;",
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\nimport { perspectiveSettingsSchema } from '../data/validators'\n\nexport const perspectivesTag = 'Perspectives'\n\nexport const perspectivesErrorSchema = z.object({\n error: z.string(),\n}).passthrough()\n\nexport const perspectiveDtoSchema = z.object({\n id: z.string().uuid(),\n name: z.string(),\n tableId: z.string(),\n settings: perspectiveSettingsSchema,\n isDefault: z.boolean(),\n createdAt: z.string(),\n updatedAt: z.string().nullable(),\n})\n\nexport const rolePerspectiveDtoSchema = perspectiveDtoSchema.extend({\n roleId: z.string().uuid(),\n tenantId: z.string().uuid().nullable(),\n organizationId: z.string().uuid().nullable(),\n roleName: z.string().nullable(),\n})\n\nexport const perspectivesIndexResponseSchema = z.object({\n tableId: z.string(),\n perspectives: z.array(perspectiveDtoSchema),\n defaultPerspectiveId: z.string().uuid().nullable(),\n rolePerspectives: z.array(rolePerspectiveDtoSchema),\n manageableRolePerspectives: z.array(rolePerspectiveDtoSchema),\n roles: z.array(\n z.object({\n id: z.string().uuid(),\n name: z.string(),\n hasPerspective: z.boolean(),\n hasDefault: z.boolean(),\n }),\n ),\n canApplyToRoles: z.boolean(),\n})\n\nexport const perspectiveSaveResponseSchema = z.object({\n perspective: perspectiveDtoSchema,\n rolePerspectives: z.array(rolePerspectiveDtoSchema),\n clearedRoleIds: z.array(z.string().uuid()),\n})\n\nexport const perspectivesSuccessSchema = z.object({\n success: z.literal(true),\n})\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,iCAAiC;AAEnC,MAAM,kBAAkB;AAExB,MAAM,0BAA0B,EAAE,OAAO;AAAA,EAC9C,OAAO,EAAE,OAAO;AAClB,CAAC,EAAE,YAAY;AAER,MAAM,uBAAuB,EAAE,OAAO;AAAA,EAC3C,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,MAAM,EAAE,OAAO;AAAA,EACf,SAAS,EAAE,OAAO;AAAA,EAClB,UAAU;AAAA,EACV,WAAW,EAAE,QAAQ;AAAA,EACrB,WAAW,EAAE,OAAO;AAAA,EACpB,WAAW,EAAE,OAAO,EAAE,SAAS;AACjC,CAAC;AAEM,MAAM,2BAA2B,qBAAqB,OAAO;AAAA,EAClE,QAAQ,EAAE,OAAO,EAAE,KAAK;AAAA,EACxB,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACrC,gBAAgB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC3C,UAAU,EAAE,OAAO,EAAE,SAAS;AAChC,CAAC;AAEM,MAAM,kCAAkC,EAAE,OAAO;AAAA,EACtD,SAAS,EAAE,OAAO;AAAA,EAClB,cAAc,EAAE,MAAM,oBAAoB;AAAA,EAC1C,sBAAsB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACjD,kBAAkB,EAAE,MAAM,wBAAwB;AAAA,EAClD,4BAA4B,EAAE,MAAM,wBAAwB;AAAA,EAC5D,OAAO,EAAE;AAAA,IACP,EAAE,OAAO;AAAA,MACP,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,MACpB,MAAM,EAAE,OAAO;AAAA,MACf,gBAAgB,EAAE,QAAQ;AAAA,MAC1B,YAAY,EAAE,QAAQ;AAAA,IACxB,CAAC;AAAA,EACH;AAAA,EACA,iBAAiB,EAAE,QAAQ;AAC7B,CAAC;AAEM,MAAM,gCAAgC,EAAE,OAAO;AAAA,EACpD,aAAa;AAAA,EACb,kBAAkB,EAAE,MAAM,wBAAwB;AAAA,EAClD,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;AAC3C,CAAC;AAEM,MAAM,4BAA4B,EAAE,OAAO;AAAA,EAChD,SAAS,EAAE,QAAQ,IAAI;AACzB,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -14,6 +14,8 @@ const perspectiveSaveSchema = z.object({
|
|
|
14
14
|
isDefault: z.boolean().optional(),
|
|
15
15
|
applyToRoles: z.array(z.string().uuid()).optional(),
|
|
16
16
|
clearRoleIds: z.array(z.string().uuid()).optional(),
|
|
17
|
+
roleExpectedUpdatedAtByRoleId: z.record(z.string().uuid(), z.string().min(1).nullable()).optional(),
|
|
18
|
+
roleExpectedUpdatedAtByPerspectiveId: z.record(z.string().uuid(), z.string().min(1).nullable()).optional(),
|
|
17
19
|
setRoleDefault: z.boolean().optional()
|
|
18
20
|
});
|
|
19
21
|
const perspectiveDeleteSchema = z.object({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/perspectives/data/validators.ts"],
|
|
4
|
-
"sourcesContent": ["import { z } from 'zod'\nimport type { PerspectiveSettings } from '@open-mercato/shared/modules/perspectives/types'\n\nexport const perspectiveSettingsSchema: z.ZodType<PerspectiveSettings> = z.object({\n columnOrder: z.array(z.string().min(1)).max(120).optional(),\n columnVisibility: z.record(z.string(), z.boolean()).optional(),\n filters: z.record(z.string(), z.unknown()).optional(),\n sorting: z\n .array(z.object({ id: z.string().min(1), desc: z.boolean().optional() }))\n .max(20)\n .optional(),\n pageSize: z.number().int().positive().max(500).optional(),\n searchValue: z.string().max(200).optional(),\n})\n\nexport type { PerspectiveSettings }\n\nexport const perspectiveSaveSchema = z.object({\n perspectiveId: z.string().uuid().optional(),\n name: z.string().min(1).max(120),\n settings: perspectiveSettingsSchema,\n isDefault: z.boolean().optional(),\n applyToRoles: z.array(z.string().uuid()).optional(),\n clearRoleIds: z.array(z.string().uuid()).optional(),\n setRoleDefault: z.boolean().optional(),\n})\n\nexport const perspectiveDeleteSchema = z.object({\n force: z.boolean().optional(),\n})\n\nexport const rolePerspectiveSaveSchema = z.object({\n roleIds: z.array(z.string().uuid()).min(1),\n name: z.string().min(1).max(120),\n settings: perspectiveSettingsSchema,\n setDefault: z.boolean().optional(),\n})\n\nexport type PerspectiveSaveInput = z.infer<typeof perspectiveSaveSchema>\nexport type RolePerspectiveSaveInput = z.infer<typeof rolePerspectiveSaveSchema>\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,SAAS;AAGX,MAAM,4BAA4D,EAAE,OAAO;AAAA,EAChF,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EAC1D,kBAAkB,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS;AAAA,EAC7D,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS;AAAA,EACpD,SAAS,EACN,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC,EACvE,IAAI,EAAE,EACN,SAAS;AAAA,EACZ,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACxD,aAAa,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AAC5C,CAAC;AAIM,MAAM,wBAAwB,EAAE,OAAO;AAAA,EAC5C,eAAe,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC1C,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EAC/B,UAAU;AAAA,EACV,WAAW,EAAE,QAAQ,EAAE,SAAS;AAAA,EAChC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,SAAS;AAAA,EAClD,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,SAAS;AAAA,EAClD,gBAAgB,EAAE,QAAQ,EAAE,SAAS;AACvC,CAAC;AAEM,MAAM,0BAA0B,EAAE,OAAO;AAAA,EAC9C,OAAO,EAAE,QAAQ,EAAE,SAAS;AAC9B,CAAC;AAEM,MAAM,4BAA4B,EAAE,OAAO;AAAA,EAChD,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC;AAAA,EACzC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EAC/B,UAAU;AAAA,EACV,YAAY,EAAE,QAAQ,EAAE,SAAS;AACnC,CAAC;",
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\nimport type { PerspectiveSettings } from '@open-mercato/shared/modules/perspectives/types'\n\nexport const perspectiveSettingsSchema: z.ZodType<PerspectiveSettings> = z.object({\n columnOrder: z.array(z.string().min(1)).max(120).optional(),\n columnVisibility: z.record(z.string(), z.boolean()).optional(),\n filters: z.record(z.string(), z.unknown()).optional(),\n sorting: z\n .array(z.object({ id: z.string().min(1), desc: z.boolean().optional() }))\n .max(20)\n .optional(),\n pageSize: z.number().int().positive().max(500).optional(),\n searchValue: z.string().max(200).optional(),\n})\n\nexport type { PerspectiveSettings }\n\nexport const perspectiveSaveSchema = z.object({\n perspectiveId: z.string().uuid().optional(),\n name: z.string().min(1).max(120),\n settings: perspectiveSettingsSchema,\n isDefault: z.boolean().optional(),\n applyToRoles: z.array(z.string().uuid()).optional(),\n clearRoleIds: z.array(z.string().uuid()).optional(),\n roleExpectedUpdatedAtByRoleId: z.record(z.string().uuid(), z.string().min(1).nullable()).optional(),\n roleExpectedUpdatedAtByPerspectiveId: z.record(z.string().uuid(), z.string().min(1).nullable()).optional(),\n setRoleDefault: z.boolean().optional(),\n})\n\nexport const perspectiveDeleteSchema = z.object({\n force: z.boolean().optional(),\n})\n\nexport const rolePerspectiveSaveSchema = z.object({\n roleIds: z.array(z.string().uuid()).min(1),\n name: z.string().min(1).max(120),\n settings: perspectiveSettingsSchema,\n setDefault: z.boolean().optional(),\n})\n\nexport type PerspectiveSaveInput = z.infer<typeof perspectiveSaveSchema>\nexport type RolePerspectiveSaveInput = z.infer<typeof rolePerspectiveSaveSchema>\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,SAAS;AAGX,MAAM,4BAA4D,EAAE,OAAO;AAAA,EAChF,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EAC1D,kBAAkB,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS;AAAA,EAC7D,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS;AAAA,EACpD,SAAS,EACN,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC,EACvE,IAAI,EAAE,EACN,SAAS;AAAA,EACZ,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACxD,aAAa,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AAC5C,CAAC;AAIM,MAAM,wBAAwB,EAAE,OAAO;AAAA,EAC5C,eAAe,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC1C,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EAC/B,UAAU;AAAA,EACV,WAAW,EAAE,QAAQ,EAAE,SAAS;AAAA,EAChC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,SAAS;AAAA,EAClD,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,SAAS;AAAA,EAClD,+BAA+B,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,CAAC,EAAE,SAAS;AAAA,EAClG,sCAAsC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,CAAC,EAAE,SAAS;AAAA,EACzG,gBAAgB,EAAE,QAAQ,EAAE,SAAS;AACvC,CAAC;AAEM,MAAM,0BAA0B,EAAE,OAAO;AAAA,EAC9C,OAAO,EAAE,QAAQ,EAAE,SAAS;AAC9B,CAAC;AAEM,MAAM,4BAA4B,EAAE,OAAO;AAAA,EAChD,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC;AAAA,EACzC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EAC/B,UAAU;AAAA,EACV,YAAY,EAAE,QAAQ,EAAE,SAAS;AACnC,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|