@open-mercato/core 0.6.7-develop.6631.1.9d64b49ddc → 0.6.7-develop.6645.1.52f0b37813

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/dist/modules/attachments/components/AttachmentLibrary.js +13 -2
  2. package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
  3. package/dist/modules/catalog/di.js +1 -1
  4. package/dist/modules/catalog/di.js.map +2 -2
  5. package/dist/modules/dashboards/lib/widgets.js +5 -3
  6. package/dist/modules/dashboards/lib/widgets.js.map +2 -2
  7. package/dist/modules/directory/api/get/organizations/lookup.js +51 -3
  8. package/dist/modules/directory/api/get/organizations/lookup.js.map +2 -2
  9. package/dist/modules/directory/api/get/tenants/lookup.js +31 -1
  10. package/dist/modules/directory/api/get/tenants/lookup.js.map +2 -2
  11. package/dist/modules/notifications/di.js +1 -1
  12. package/dist/modules/notifications/di.js.map +2 -2
  13. package/dist/modules/planner/commands/availability-rule-sets.js +37 -15
  14. package/dist/modules/planner/commands/availability-rule-sets.js.map +2 -2
  15. package/dist/modules/planner/commands/availability.js +37 -11
  16. package/dist/modules/planner/commands/availability.js.map +2 -2
  17. package/dist/modules/planner/commands/shared.js +34 -1
  18. package/dist/modules/planner/commands/shared.js.map +2 -2
  19. package/package.json +7 -7
  20. package/src/modules/attachments/components/AttachmentLibrary.tsx +9 -3
  21. package/src/modules/catalog/di.ts +3 -1
  22. package/src/modules/dashboards/lib/widgets.ts +5 -3
  23. package/src/modules/directory/api/get/organizations/lookup.ts +87 -2
  24. package/src/modules/directory/api/get/tenants/lookup.ts +46 -0
  25. package/src/modules/notifications/di.ts +1 -1
  26. package/src/modules/planner/commands/availability-rule-sets.ts +38 -14
  27. package/src/modules/planner/commands/availability.ts +44 -11
  28. package/src/modules/planner/commands/shared.ts +51 -0
@@ -2,6 +2,10 @@ import { NextResponse } from "next/server";
2
2
  import { z } from "zod";
3
3
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
4
  import { Tenant } from "@open-mercato/core/modules/directory/data/entities";
5
+ import { getCachedRateLimiterService } from "@open-mercato/core/bootstrap";
6
+ import { readEndpointRateLimitConfig } from "@open-mercato/shared/lib/ratelimit/config";
7
+ import { checkRateLimit, getClientIp, rateLimitErrorSchema } from "@open-mercato/shared/lib/ratelimit/helpers";
8
+ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
5
9
  const metadata = {
6
10
  path: "/directory/tenants/lookup",
7
11
  GET: {
@@ -11,7 +15,32 @@ const metadata = {
11
15
  const tenantLookupQuerySchema = z.object({
12
16
  tenantId: z.string().uuid()
13
17
  });
18
+ const tenantLookupIpRateLimitConfig = readEndpointRateLimitConfig("DIRECTORY_TENANT_LOOKUP", {
19
+ points: 60,
20
+ duration: 60,
21
+ blockDuration: 60,
22
+ keyPrefix: "directory-tenant-lookup"
23
+ });
24
+ async function enforceTenantLookupRateLimit(req) {
25
+ try {
26
+ const rateLimiterService = getCachedRateLimiterService();
27
+ if (!rateLimiterService) return null;
28
+ const clientIp = getClientIp(req, rateLimiterService.trustProxyDepth);
29
+ if (!clientIp) return null;
30
+ const { translate } = await resolveTranslations();
31
+ return await checkRateLimit(
32
+ rateLimiterService,
33
+ tenantLookupIpRateLimitConfig,
34
+ clientIp,
35
+ translate("api.errors.rateLimit", "Too many requests. Please try again later.")
36
+ );
37
+ } catch {
38
+ return null;
39
+ }
40
+ }
14
41
  async function GET(req) {
42
+ const rateLimitResponse = await enforceTenantLookupRateLimit(req);
43
+ if (rateLimitResponse) return rateLimitResponse;
15
44
  const url = new URL(req.url);
16
45
  const tenantId = url.searchParams.get("tenantId") || url.searchParams.get("tenant") || "";
17
46
  const parsed = tenantLookupQuerySchema.safeParse({ tenantId });
@@ -51,7 +80,8 @@ const tenantLookupDoc = {
51
80
  ],
52
81
  errors: [
53
82
  { status: 400, description: "Invalid tenant id", schema: tenantLookupErrorSchema },
54
- { status: 404, description: "Tenant not found", schema: tenantLookupErrorSchema }
83
+ { status: 404, description: "Tenant not found", schema: tenantLookupErrorSchema },
84
+ { status: 429, description: "Too many tenant lookups", schema: rateLimitErrorSchema }
55
85
  ]
56
86
  };
57
87
  const openApi = {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/directory/api/get/tenants/lookup.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nexport const metadata = {\n path: '/directory/tenants/lookup',\n GET: {\n requireAuth: false,\n },\n}\n\nconst tenantLookupQuerySchema = z.object({\n tenantId: z.string().uuid(),\n})\n\nexport async function GET(req: Request) {\n const url = new URL(req.url)\n const tenantId = url.searchParams.get('tenantId') || url.searchParams.get('tenant') || ''\n const parsed = tenantLookupQuerySchema.safeParse({ tenantId })\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'Invalid tenant id.' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n const tenant = await em.findOne(Tenant, { id: parsed.data.tenantId, deletedAt: null })\n if (!tenant) {\n return NextResponse.json({ ok: false, error: 'Tenant not found.' }, { status: 404 })\n }\n return NextResponse.json({\n ok: true,\n tenant: { id: String(tenant.id), name: tenant.name },\n })\n}\n\nconst lookupTag = 'Directory'\n\nconst tenantLookupSuccessSchema = z.object({\n ok: z.literal(true),\n tenant: z.object({\n id: z.string().uuid(),\n name: z.string(),\n }),\n})\n\nconst tenantLookupErrorSchema = z.object({\n ok: z.literal(false),\n error: z.string(),\n})\n\nconst tenantLookupDoc: OpenApiMethodDoc = {\n summary: 'Public tenant lookup',\n description: 'Resolves tenant metadata for login/activation flows.',\n tags: [lookupTag],\n query: tenantLookupQuerySchema,\n responses: [\n { status: 200, description: 'Tenant resolved.', schema: tenantLookupSuccessSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid tenant id', schema: tenantLookupErrorSchema },\n { status: 404, description: 'Tenant not found', schema: tenantLookupErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: lookupTag,\n summary: 'Public tenant lookup',\n methods: {\n GET: tenantLookupDoc,\n },\n}\n\nexport default GET\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAElB,SAAS,8BAA8B;AACvC,SAAS,cAAc;AAGhB,MAAM,WAAW;AAAA,EACtB,MAAM;AAAA,EACN,KAAK;AAAA,IACH,aAAa;AAAA,EACf;AACF;AAEA,MAAM,0BAA0B,EAAE,OAAO;AAAA,EACvC,UAAU,EAAE,OAAO,EAAE,KAAK;AAC5B,CAAC;AAED,eAAsB,IAAI,KAAc;AACtC,QAAM,MAAM,IAAI,IAAI,IAAI,GAAG;AAC3B,QAAM,WAAW,IAAI,aAAa,IAAI,UAAU,KAAK,IAAI,aAAa,IAAI,QAAQ,KAAK;AACvF,QAAM,SAAS,wBAAwB,UAAU,EAAE,SAAS,CAAC;AAC7D,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,qBAAqB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACtF;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,KAAM,UAAU,QAAQ,IAAI;AAClC,QAAM,SAAS,MAAM,GAAG,QAAQ,QAAQ,EAAE,IAAI,OAAO,KAAK,UAAU,WAAW,KAAK,CAAC;AACrF,MAAI,CAAC,QAAQ;AACX,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,oBAAoB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrF;AACA,SAAO,aAAa,KAAK;AAAA,IACvB,IAAI;AAAA,IACJ,QAAQ,EAAE,IAAI,OAAO,OAAO,EAAE,GAAG,MAAM,OAAO,KAAK;AAAA,EACrD,CAAC;AACH;AAEA,MAAM,YAAY;AAElB,MAAM,4BAA4B,EAAE,OAAO;AAAA,EACzC,IAAI,EAAE,QAAQ,IAAI;AAAA,EAClB,QAAQ,EAAE,OAAO;AAAA,IACf,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,IACpB,MAAM,EAAE,OAAO;AAAA,EACjB,CAAC;AACH,CAAC;AAED,MAAM,0BAA0B,EAAE,OAAO;AAAA,EACvC,IAAI,EAAE,QAAQ,KAAK;AAAA,EACnB,OAAO,EAAE,OAAO;AAClB,CAAC;AAED,MAAM,kBAAoC;AAAA,EACxC,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM,CAAC,SAAS;AAAA,EAChB,OAAO;AAAA,EACP,WAAW;AAAA,IACT,EAAE,QAAQ,KAAK,aAAa,oBAAoB,QAAQ,0BAA0B;AAAA,EACpF;AAAA,EACA,QAAQ;AAAA,IACN,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,wBAAwB;AAAA,IACjF,EAAE,QAAQ,KAAK,aAAa,oBAAoB,QAAQ,wBAAwB;AAAA,EAClF;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,KAAK;AAAA,EACP;AACF;AAEA,IAAO,iBAAQ;",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getCachedRateLimiterService } from '@open-mercato/core/bootstrap'\nimport { readEndpointRateLimitConfig } from '@open-mercato/shared/lib/ratelimit/config'\nimport { checkRateLimit, getClientIp, rateLimitErrorSchema } from '@open-mercato/shared/lib/ratelimit/helpers'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\n\nexport const metadata = {\n path: '/directory/tenants/lookup',\n GET: {\n requireAuth: false,\n },\n}\n\nconst tenantLookupQuerySchema = z.object({\n tenantId: z.string().uuid(),\n})\n\n// Unauthenticated login/onboarding resolver: it returns the tenant name to anyone\n// holding the id, so cap probes per IP to keep name-harvesting from leaked ids \u2014\n// and the unauthenticated DB query behind it \u2014 bounded and noisy.\n//\n// The cap is deliberately generous: the login screen resolves the tenant on every\n// render and a workforce behind one NAT egress IP shares this budget, while a\n// tripped lookup renders as an invalid tenant. 60/min/IP stays clear of that.\n// Tune per deployment with RATE_LIMIT_DIRECTORY_TENANT_LOOKUP_POINTS / _DURATION /\n// _BLOCK_DURATION.\nconst tenantLookupIpRateLimitConfig = readEndpointRateLimitConfig('DIRECTORY_TENANT_LOOKUP', {\n points: 60,\n duration: 60,\n blockDuration: 60,\n keyPrefix: 'directory-tenant-lookup',\n})\n\n// Fail-open, like auth's checkAuthRateLimit: this resolver sits on the unauthenticated\n// login bootstrap path, so a limiter outage must degrade to unlimited lookups rather\n// than 500 the login screen.\nasync function enforceTenantLookupRateLimit(req: Request): Promise<NextResponse | null> {\n try {\n const rateLimiterService = getCachedRateLimiterService()\n if (!rateLimiterService) return null\n const clientIp = getClientIp(req, rateLimiterService.trustProxyDepth)\n if (!clientIp) return null\n const { translate } = await resolveTranslations()\n return await checkRateLimit(\n rateLimiterService,\n tenantLookupIpRateLimitConfig,\n clientIp,\n translate('api.errors.rateLimit', 'Too many requests. Please try again later.'),\n )\n } catch {\n return null\n }\n}\n\nexport async function GET(req: Request) {\n // Consume the limit before validation or DB work, so junk input cannot bypass it.\n const rateLimitResponse = await enforceTenantLookupRateLimit(req)\n if (rateLimitResponse) return rateLimitResponse\n\n const url = new URL(req.url)\n const tenantId = url.searchParams.get('tenantId') || url.searchParams.get('tenant') || ''\n const parsed = tenantLookupQuerySchema.safeParse({ tenantId })\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'Invalid tenant id.' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n const tenant = await em.findOne(Tenant, { id: parsed.data.tenantId, deletedAt: null })\n if (!tenant) {\n return NextResponse.json({ ok: false, error: 'Tenant not found.' }, { status: 404 })\n }\n return NextResponse.json({\n ok: true,\n tenant: { id: String(tenant.id), name: tenant.name },\n })\n}\n\nconst lookupTag = 'Directory'\n\nconst tenantLookupSuccessSchema = z.object({\n ok: z.literal(true),\n tenant: z.object({\n id: z.string().uuid(),\n name: z.string(),\n }),\n})\n\nconst tenantLookupErrorSchema = z.object({\n ok: z.literal(false),\n error: z.string(),\n})\n\nconst tenantLookupDoc: OpenApiMethodDoc = {\n summary: 'Public tenant lookup',\n description: 'Resolves tenant metadata for login/activation flows.',\n tags: [lookupTag],\n query: tenantLookupQuerySchema,\n responses: [\n { status: 200, description: 'Tenant resolved.', schema: tenantLookupSuccessSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid tenant id', schema: tenantLookupErrorSchema },\n { status: 404, description: 'Tenant not found', schema: tenantLookupErrorSchema },\n { status: 429, description: 'Too many tenant lookups', schema: rateLimitErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: lookupTag,\n summary: 'Public tenant lookup',\n methods: {\n GET: tenantLookupDoc,\n },\n}\n\nexport default GET\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAElB,SAAS,8BAA8B;AACvC,SAAS,cAAc;AAEvB,SAAS,mCAAmC;AAC5C,SAAS,mCAAmC;AAC5C,SAAS,gBAAgB,aAAa,4BAA4B;AAClE,SAAS,2BAA2B;AAE7B,MAAM,WAAW;AAAA,EACtB,MAAM;AAAA,EACN,KAAK;AAAA,IACH,aAAa;AAAA,EACf;AACF;AAEA,MAAM,0BAA0B,EAAE,OAAO;AAAA,EACvC,UAAU,EAAE,OAAO,EAAE,KAAK;AAC5B,CAAC;AAWD,MAAM,gCAAgC,4BAA4B,2BAA2B;AAAA,EAC3F,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,eAAe;AAAA,EACf,WAAW;AACb,CAAC;AAKD,eAAe,6BAA6B,KAA4C;AACtF,MAAI;AACF,UAAM,qBAAqB,4BAA4B;AACvD,QAAI,CAAC,mBAAoB,QAAO;AAChC,UAAM,WAAW,YAAY,KAAK,mBAAmB,eAAe;AACpE,QAAI,CAAC,SAAU,QAAO;AACtB,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO,MAAM;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU,wBAAwB,4CAA4C;AAAA,IAChF;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,IAAI,KAAc;AAEtC,QAAM,oBAAoB,MAAM,6BAA6B,GAAG;AAChE,MAAI,kBAAmB,QAAO;AAE9B,QAAM,MAAM,IAAI,IAAI,IAAI,GAAG;AAC3B,QAAM,WAAW,IAAI,aAAa,IAAI,UAAU,KAAK,IAAI,aAAa,IAAI,QAAQ,KAAK;AACvF,QAAM,SAAS,wBAAwB,UAAU,EAAE,SAAS,CAAC;AAC7D,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,qBAAqB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACtF;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,KAAM,UAAU,QAAQ,IAAI;AAClC,QAAM,SAAS,MAAM,GAAG,QAAQ,QAAQ,EAAE,IAAI,OAAO,KAAK,UAAU,WAAW,KAAK,CAAC;AACrF,MAAI,CAAC,QAAQ;AACX,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,oBAAoB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrF;AACA,SAAO,aAAa,KAAK;AAAA,IACvB,IAAI;AAAA,IACJ,QAAQ,EAAE,IAAI,OAAO,OAAO,EAAE,GAAG,MAAM,OAAO,KAAK;AAAA,EACrD,CAAC;AACH;AAEA,MAAM,YAAY;AAElB,MAAM,4BAA4B,EAAE,OAAO;AAAA,EACzC,IAAI,EAAE,QAAQ,IAAI;AAAA,EAClB,QAAQ,EAAE,OAAO;AAAA,IACf,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,IACpB,MAAM,EAAE,OAAO;AAAA,EACjB,CAAC;AACH,CAAC;AAED,MAAM,0BAA0B,EAAE,OAAO;AAAA,EACvC,IAAI,EAAE,QAAQ,KAAK;AAAA,EACnB,OAAO,EAAE,OAAO;AAClB,CAAC;AAED,MAAM,kBAAoC;AAAA,EACxC,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM,CAAC,SAAS;AAAA,EAChB,OAAO;AAAA,EACP,WAAW;AAAA,IACT,EAAE,QAAQ,KAAK,aAAa,oBAAoB,QAAQ,0BAA0B;AAAA,EACpF;AAAA,EACA,QAAQ;AAAA,IACN,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,wBAAwB;AAAA,IACjF,EAAE,QAAQ,KAAK,aAAa,oBAAoB,QAAQ,wBAAwB;AAAA,IAChF,EAAE,QAAQ,KAAK,aAAa,2BAA2B,QAAQ,qBAAqB;AAAA,EACtF;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,KAAK;AAAA,EACP;AACF;AAEA,IAAO,iBAAQ;",
6
6
  "names": []
7
7
  }
@@ -4,7 +4,7 @@ function register(container) {
4
4
  container.register({
5
5
  notificationService: asFunction(
6
6
  ({ em, eventBus, commandBus }) => createNotificationService({ em, eventBus, commandBus })
7
- ).scoped()
7
+ ).scoped().proxy()
8
8
  });
9
9
  }
10
10
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/notifications/di.ts"],
4
- "sourcesContent": ["import type { AppContainer } from '@open-mercato/shared/lib/di/container'\nimport { asFunction } from 'awilix'\nimport { createNotificationService } from './lib/notificationService'\n\nexport function register(container: AppContainer): void {\n container.register({\n notificationService: asFunction(({ em, eventBus, commandBus }) =>\n createNotificationService({ em, eventBus, commandBus })\n ).scoped(),\n })\n}\n"],
5
- "mappings": "AACA,SAAS,kBAAkB;AAC3B,SAAS,iCAAiC;AAEnC,SAAS,SAAS,WAA+B;AACtD,YAAU,SAAS;AAAA,IACjB,qBAAqB;AAAA,MAAW,CAAC,EAAE,IAAI,UAAU,WAAW,MAC1D,0BAA0B,EAAE,IAAI,UAAU,WAAW,CAAC;AAAA,IACxD,EAAE,OAAO;AAAA,EACX,CAAC;AACH;",
4
+ "sourcesContent": ["import type { AppContainer } from '@open-mercato/shared/lib/di/container'\nimport { asFunction } from 'awilix'\nimport { createNotificationService } from './lib/notificationService'\n\nexport function register(container: AppContainer): void {\n container.register({\n notificationService: asFunction(({ em, eventBus, commandBus }) =>\n createNotificationService({ em, eventBus, commandBus })\n ).scoped().proxy(),\n })\n}\n"],
5
+ "mappings": "AACA,SAAS,kBAAkB;AAC3B,SAAS,iCAAiC;AAEnC,SAAS,SAAS,WAA+B;AACtD,YAAU,SAAS;AAAA,IACjB,qBAAqB;AAAA,MAAW,CAAC,EAAE,IAAI,UAAU,WAAW,MAC1D,0BAA0B,EAAE,IAAI,UAAU,WAAW,CAAC;AAAA,IACxD,EAAE,OAAO,EAAE,MAAM;AAAA,EACnB,CAAC;AACH;",
6
6
  "names": []
7
7
  }
@@ -11,18 +11,26 @@ import {
11
11
  } from "../data/validators.js";
12
12
  import { plannerAvailabilityRuleSetCrudEvents } from "../lib/crud.js";
13
13
  import { makeCreateRedo } from "@open-mercato/shared/lib/commands/redo";
14
- import { ensureOrganizationScope, ensureTenantScope, extractUndoPayload } from "./shared.js";
14
+ import {
15
+ applyScopeToWhere,
16
+ commandActorScope,
17
+ ensureOrganizationScope,
18
+ ensureTenantScope,
19
+ explicitPlannerCommandScope,
20
+ extractUndoPayload,
21
+ scopeForDecryption
22
+ } from "./shared.js";
15
23
  import { E } from "../../../generated/entities.ids.generated.js";
16
24
  const availabilityRuleSetCrudIndexer = {
17
25
  entityType: E.planner.planner_availability_rule_set
18
26
  };
19
- async function loadAvailabilityRuleSetSnapshot(em, id) {
27
+ async function loadAvailabilityRuleSetSnapshot(em, id, scope) {
20
28
  const ruleSet = await findOneWithDecryption(
21
29
  em,
22
30
  PlannerAvailabilityRuleSet,
23
- { id },
31
+ applyScopeToWhere({ id }, scope),
24
32
  void 0,
25
- { tenantId: null, organizationId: null }
33
+ scopeForDecryption(scope)
26
34
  );
27
35
  if (!ruleSet) return null;
28
36
  const custom = await loadCustomFieldSnapshot(em, {
@@ -85,15 +93,23 @@ const createAvailabilityRuleSetCommand = {
85
93
  });
86
94
  return { ruleSetId: record.id };
87
95
  },
88
- captureAfter: async (_input, result, ctx) => {
96
+ captureAfter: async (input, result, ctx) => {
89
97
  const em = ctx.container.resolve("em").fork();
90
- const snapshot = await loadAvailabilityRuleSetSnapshot(em, result.ruleSetId);
98
+ const snapshot = await loadAvailabilityRuleSetSnapshot(
99
+ em,
100
+ result.ruleSetId,
101
+ explicitPlannerCommandScope(input.tenantId, input.organizationId)
102
+ );
91
103
  if (!snapshot) return null;
92
104
  return snapshot;
93
105
  },
94
- buildLog: async ({ result, ctx }) => {
106
+ buildLog: async ({ input, result, ctx }) => {
95
107
  const em = ctx.container.resolve("em").fork();
96
- const snapshot = await loadAvailabilityRuleSetSnapshot(em, result.ruleSetId);
108
+ const snapshot = await loadAvailabilityRuleSetSnapshot(
109
+ em,
110
+ result.ruleSetId,
111
+ explicitPlannerCommandScope(input.tenantId, input.organizationId)
112
+ );
97
113
  if (!snapshot) return null;
98
114
  const { translate } = await resolveTranslations();
99
115
  return {
@@ -159,19 +175,20 @@ const updateAvailabilityRuleSetCommand = {
159
175
  async prepare(input, ctx) {
160
176
  const { parsed } = parseWithCustomFields(plannerAvailabilityRuleSetUpdateSchema, input);
161
177
  const em = ctx.container.resolve("em");
162
- const snapshot = await loadAvailabilityRuleSetSnapshot(em, parsed.id);
178
+ const snapshot = await loadAvailabilityRuleSetSnapshot(em, parsed.id, commandActorScope(ctx));
163
179
  if (!snapshot) return {};
164
180
  return { before: snapshot };
165
181
  },
166
182
  async execute(input, ctx) {
167
183
  const { parsed, custom } = parseWithCustomFields(plannerAvailabilityRuleSetUpdateSchema, input);
168
184
  const em = ctx.container.resolve("em").fork();
185
+ const scope = commandActorScope(ctx);
169
186
  const record = await findOneWithDecryption(
170
187
  em,
171
188
  PlannerAvailabilityRuleSet,
172
- { id: parsed.id, deletedAt: null },
189
+ applyScopeToWhere({ id: parsed.id, deletedAt: null }, scope),
173
190
  void 0,
174
- { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null }
191
+ scopeForDecryption(scope)
175
192
  );
176
193
  if (!record) throw new CrudHttpError(404, { error: "Planner availability rule set not found." });
177
194
  ensureTenantScope(ctx, record.tenantId);
@@ -208,7 +225,11 @@ const updateAvailabilityRuleSetCommand = {
208
225
  const before = snapshots.before;
209
226
  if (!before) return null;
210
227
  const em = ctx.container.resolve("em").fork();
211
- const after = await loadAvailabilityRuleSetSnapshot(em, before.id);
228
+ const after = await loadAvailabilityRuleSetSnapshot(
229
+ em,
230
+ before.id,
231
+ explicitPlannerCommandScope(before.tenantId, before.organizationId)
232
+ );
212
233
  if (!after) return null;
213
234
  const changes = buildChanges(before, after, [
214
235
  "name",
@@ -284,7 +305,7 @@ const deleteAvailabilityRuleSetCommand = {
284
305
  const id = input?.id;
285
306
  if (!id) throw new CrudHttpError(400, { error: "Availability rule set id is required." });
286
307
  const em = ctx.container.resolve("em");
287
- const snapshot = await loadAvailabilityRuleSetSnapshot(em, id);
308
+ const snapshot = await loadAvailabilityRuleSetSnapshot(em, id, commandActorScope(ctx));
288
309
  if (!snapshot) return {};
289
310
  return { before: snapshot };
290
311
  },
@@ -292,12 +313,13 @@ const deleteAvailabilityRuleSetCommand = {
292
313
  const id = input?.id;
293
314
  if (!id) throw new CrudHttpError(400, { error: "Availability rule set id is required." });
294
315
  const em = ctx.container.resolve("em").fork();
316
+ const scope = commandActorScope(ctx);
295
317
  const record = await findOneWithDecryption(
296
318
  em,
297
319
  PlannerAvailabilityRuleSet,
298
- { id, deletedAt: null },
320
+ applyScopeToWhere({ id, deletedAt: null }, scope),
299
321
  void 0,
300
- { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null }
322
+ scopeForDecryption(scope)
301
323
  );
302
324
  if (!record) throw new CrudHttpError(404, { error: "Planner availability rule set not found." });
303
325
  ensureTenantScope(ctx, record.tenantId);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/planner/commands/availability-rule-sets.ts"],
4
- "sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects, parseWithCustomFields, setCustomFieldsIfAny } from '@open-mercato/shared/lib/commands/helpers'\nimport { buildCustomFieldResetMap, diffCustomFieldChanges, loadCustomFieldSnapshot, type CustomFieldSnapshot } from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport { PlannerAvailabilityRuleSet } from '../data/entities'\nimport {\n plannerAvailabilityRuleSetCreateSchema,\n plannerAvailabilityRuleSetUpdateSchema,\n type PlannerAvailabilityRuleSetCreateInput,\n type PlannerAvailabilityRuleSetUpdateInput,\n} from '../data/validators'\nimport { plannerAvailabilityRuleSetCrudEvents } from '../lib/crud'\nimport { makeCreateRedo } from '@open-mercato/shared/lib/commands/redo'\nimport { ensureOrganizationScope, ensureTenantScope, extractUndoPayload } from './shared'\nimport { E } from '#generated/entities.ids.generated'\n\nconst availabilityRuleSetCrudIndexer: CrudIndexerConfig<PlannerAvailabilityRuleSet> = {\n entityType: E.planner.planner_availability_rule_set,\n}\n\ntype AvailabilityRuleSetSnapshot = {\n id: string\n tenantId: string\n organizationId: string\n name: string\n description: string | null\n timezone: string\n deletedAt: string | null\n custom?: CustomFieldSnapshot\n}\n\ntype AvailabilityRuleSetUndoPayload = {\n before?: AvailabilityRuleSetSnapshot | null\n after?: AvailabilityRuleSetSnapshot | null\n}\n\nasync function loadAvailabilityRuleSetSnapshot(\n em: EntityManager,\n id: string,\n): Promise<AvailabilityRuleSetSnapshot | null> {\n const ruleSet = await findOneWithDecryption(\n em,\n PlannerAvailabilityRuleSet,\n { id },\n undefined,\n { tenantId: null, organizationId: null },\n )\n if (!ruleSet) return null\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.planner.planner_availability_rule_set,\n recordId: ruleSet.id,\n tenantId: ruleSet.tenantId,\n organizationId: ruleSet.organizationId,\n })\n return {\n id: ruleSet.id,\n tenantId: ruleSet.tenantId,\n organizationId: ruleSet.organizationId,\n name: ruleSet.name,\n description: ruleSet.description ?? null,\n timezone: ruleSet.timezone,\n deletedAt: ruleSet.deletedAt ? ruleSet.deletedAt.toISOString() : null,\n custom,\n }\n}\n\nconst createAvailabilityRuleSetCommand: CommandHandler<PlannerAvailabilityRuleSetCreateInput, { ruleSetId: string }> = {\n id: 'planner.availability-rule-sets.create',\n async execute(input, ctx) {\n const { parsed, custom } = parseWithCustomFields(plannerAvailabilityRuleSetCreateSchema, input)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const now = new Date()\n const record = em.create(PlannerAvailabilityRuleSet, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n name: parsed.name,\n description: parsed.description ?? null,\n timezone: parsed.timezone,\n createdAt: now,\n updatedAt: now,\n deletedAt: null,\n })\n em.persist(record)\n await em.flush()\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.planner.planner_availability_rule_set,\n recordId: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n values: custom,\n })\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: plannerAvailabilityRuleSetCrudEvents,\n indexer: availabilityRuleSetCrudIndexer,\n })\n return { ruleSetId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadAvailabilityRuleSetSnapshot(em, result.ruleSetId)\n if (!snapshot) return null\n return snapshot\n },\n buildLog: async ({ result, ctx }) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadAvailabilityRuleSetSnapshot(em, result.ruleSetId)\n if (!snapshot) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('planner.audit.availabilityRuleSets.create', 'Create availability schedule'),\n resourceKind: 'planner.availabilityRuleSet',\n resourceId: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n snapshotAfter: snapshot,\n payload: {\n undo: {\n after: snapshot,\n } satisfies AvailabilityRuleSetUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AvailabilityRuleSetUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const ruleSet = await em.findOne(PlannerAvailabilityRuleSet, { id: after.id })\n if (ruleSet) {\n ruleSet.deletedAt = new Date()\n ruleSet.updatedAt = new Date()\n await em.flush()\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'deleted',\n entity: ruleSet,\n identifiers: {\n id: ruleSet.id,\n organizationId: ruleSet.organizationId,\n tenantId: ruleSet.tenantId,\n },\n events: plannerAvailabilityRuleSetCrudEvents,\n indexer: availabilityRuleSetCrudIndexer,\n })\n }\n },\n redo: makeCreateRedo<PlannerAvailabilityRuleSet, AvailabilityRuleSetSnapshot, PlannerAvailabilityRuleSetCreateInput, { ruleSetId: string }>({\n entityClass: PlannerAvailabilityRuleSet,\n getSnapshotId: (snapshot) => snapshot.id,\n seedFromSnapshot: (snapshot) => ({\n id: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n name: snapshot.name,\n description: snapshot.description ?? null,\n timezone: snapshot.timezone,\n createdAt: new Date(),\n updatedAt: new Date(),\n deletedAt: null,\n }),\n buildResult: (entity) => ({ ruleSetId: entity.id }),\n events: plannerAvailabilityRuleSetCrudEvents,\n indexer: availabilityRuleSetCrudIndexer,\n }),\n}\n\nconst updateAvailabilityRuleSetCommand: CommandHandler<PlannerAvailabilityRuleSetUpdateInput, { ruleSetId: string }> = {\n id: 'planner.availability-rule-sets.update',\n async prepare(input, ctx) {\n const { parsed } = parseWithCustomFields(plannerAvailabilityRuleSetUpdateSchema, input)\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadAvailabilityRuleSetSnapshot(em, parsed.id)\n if (!snapshot) return {}\n return { before: snapshot }\n },\n async execute(input, ctx) {\n const { parsed, custom } = parseWithCustomFields(plannerAvailabilityRuleSetUpdateSchema, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await findOneWithDecryption(\n em,\n PlannerAvailabilityRuleSet,\n { id: parsed.id, deletedAt: null },\n undefined,\n { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null },\n )\n if (!record) throw new CrudHttpError(404, { error: 'Planner availability rule set not found.' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n\n if (parsed.name !== undefined) record.name = parsed.name\n if (parsed.description !== undefined) record.description = parsed.description ?? null\n if (parsed.timezone !== undefined) record.timezone = parsed.timezone\n record.updatedAt = new Date()\n await em.flush()\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.planner.planner_availability_rule_set,\n recordId: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n values: custom,\n })\n await emitCrudSideEffects({\n dataEngine,\n action: 'updated',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: plannerAvailabilityRuleSetCrudEvents,\n indexer: availabilityRuleSetCrudIndexer,\n })\n return { ruleSetId: record.id }\n },\n buildLog: async ({ snapshots, ctx }) => {\n const before = snapshots.before as AvailabilityRuleSetSnapshot | undefined\n if (!before) return null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const after = await loadAvailabilityRuleSetSnapshot(em, before.id)\n if (!after) return null\n const changes = buildChanges(before as unknown as Record<string, unknown>, after as unknown as Record<string, unknown>, [\n 'name',\n 'description',\n 'timezone',\n 'deletedAt',\n ])\n const customChanges = diffCustomFieldChanges(before.custom, after.custom)\n if (Object.keys(customChanges).length) {\n changes.customFields = { from: before.custom ?? null, to: after.custom ?? null }\n }\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('planner.audit.availabilityRuleSets.update', 'Update availability schedule'),\n resourceKind: 'planner.availabilityRuleSet',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n snapshotAfter: after,\n changes,\n payload: {\n undo: {\n before,\n after,\n } satisfies AvailabilityRuleSetUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AvailabilityRuleSetUndoPayload>(logEntry)\n const before = payload?.before ?? (logEntry?.snapshotBefore as AvailabilityRuleSetSnapshot | null | undefined)\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const ruleSet = await em.findOne(PlannerAvailabilityRuleSet, { id: before.id })\n if (!ruleSet) return\n ruleSet.name = before.name\n ruleSet.description = before.description ?? null\n ruleSet.timezone = before.timezone\n ruleSet.deletedAt = before.deletedAt ? new Date(before.deletedAt) : null\n ruleSet.updatedAt = new Date()\n await em.flush()\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n const afterSnapshot = payload?.after ?? (logEntry?.snapshotAfter as AvailabilityRuleSetSnapshot | null | undefined)\n if (before.custom || afterSnapshot?.custom) {\n const reset = buildCustomFieldResetMap(before.custom, afterSnapshot?.custom)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.planner.planner_availability_rule_set,\n recordId: ruleSet.id,\n tenantId: ruleSet.tenantId,\n organizationId: ruleSet.organizationId,\n values: reset,\n })\n }\n\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'updated',\n entity: ruleSet,\n identifiers: {\n id: ruleSet.id,\n organizationId: ruleSet.organizationId,\n tenantId: ruleSet.tenantId,\n },\n events: plannerAvailabilityRuleSetCrudEvents,\n indexer: availabilityRuleSetCrudIndexer,\n })\n },\n}\n\nconst deleteAvailabilityRuleSetCommand: CommandHandler<{ id?: string }, { ruleSetId: string }> = {\n id: 'planner.availability-rule-sets.delete',\n async prepare(input, ctx) {\n const id = input?.id\n if (!id) throw new CrudHttpError(400, { error: 'Availability rule set id is required.' })\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadAvailabilityRuleSetSnapshot(em, id)\n if (!snapshot) return {}\n return { before: snapshot }\n },\n async execute(input, ctx) {\n const id = input?.id\n if (!id) throw new CrudHttpError(400, { error: 'Availability rule set id is required.' })\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await findOneWithDecryption(\n em,\n PlannerAvailabilityRuleSet,\n { id, deletedAt: null },\n undefined,\n { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null },\n )\n if (!record) throw new CrudHttpError(404, { error: 'Planner availability rule set not found.' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n\n record.deletedAt = new Date()\n record.updatedAt = new Date()\n await em.flush()\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine,\n action: 'deleted',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: plannerAvailabilityRuleSetCrudEvents,\n indexer: availabilityRuleSetCrudIndexer,\n })\n return { ruleSetId: record.id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as AvailabilityRuleSetSnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('planner.audit.availabilityRuleSets.delete', 'Delete availability schedule'),\n resourceKind: 'planner.availabilityRuleSet',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: {\n undo: {\n before,\n } satisfies AvailabilityRuleSetUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AvailabilityRuleSetUndoPayload>(logEntry)\n const before = payload?.before ?? (logEntry?.snapshotBefore as AvailabilityRuleSetSnapshot | null | undefined)\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let ruleSet = await em.findOne(PlannerAvailabilityRuleSet, { id: before.id })\n if (!ruleSet) {\n ruleSet = em.create(PlannerAvailabilityRuleSet, {\n id: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n name: before.name,\n description: before.description ?? null,\n timezone: before.timezone,\n deletedAt: null,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(ruleSet)\n } else {\n ruleSet.name = before.name\n ruleSet.description = before.description ?? null\n ruleSet.timezone = before.timezone\n ruleSet.deletedAt = null\n ruleSet.updatedAt = new Date()\n }\n await em.flush()\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n if (before.custom) {\n const reset = buildCustomFieldResetMap(before.custom, undefined)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.planner.planner_availability_rule_set,\n recordId: ruleSet.id,\n tenantId: ruleSet.tenantId,\n organizationId: ruleSet.organizationId,\n values: reset,\n })\n }\n\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'created',\n entity: ruleSet,\n identifiers: {\n id: ruleSet.id,\n organizationId: ruleSet.organizationId,\n tenantId: ruleSet.tenantId,\n },\n events: plannerAvailabilityRuleSetCrudEvents,\n indexer: availabilityRuleSetCrudIndexer,\n })\n },\n}\n\nregisterCommand(createAvailabilityRuleSetCommand)\nregisterCommand(updateAvailabilityRuleSetCommand)\nregisterCommand(deleteAvailabilityRuleSetCommand)\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects, parseWithCustomFields, setCustomFieldsIfAny } from '@open-mercato/shared/lib/commands/helpers'\nimport { buildCustomFieldResetMap, diffCustomFieldChanges, loadCustomFieldSnapshot, type CustomFieldSnapshot } from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport { PlannerAvailabilityRuleSet } from '../data/entities'\nimport {\n plannerAvailabilityRuleSetCreateSchema,\n plannerAvailabilityRuleSetUpdateSchema,\n type PlannerAvailabilityRuleSetCreateInput,\n type PlannerAvailabilityRuleSetUpdateInput,\n} from '../data/validators'\nimport { plannerAvailabilityRuleSetCrudEvents } from '../lib/crud'\nimport { makeCreateRedo } from '@open-mercato/shared/lib/commands/redo'\nimport {\n applyScopeToWhere,\n commandActorScope,\n ensureOrganizationScope,\n ensureTenantScope,\n explicitPlannerCommandScope,\n extractUndoPayload,\n scopeForDecryption,\n type PlannerCommandScope,\n} from './shared'\nimport { E } from '#generated/entities.ids.generated'\n\nconst availabilityRuleSetCrudIndexer: CrudIndexerConfig<PlannerAvailabilityRuleSet> = {\n entityType: E.planner.planner_availability_rule_set,\n}\n\ntype AvailabilityRuleSetSnapshot = {\n id: string\n tenantId: string\n organizationId: string\n name: string\n description: string | null\n timezone: string\n deletedAt: string | null\n custom?: CustomFieldSnapshot\n}\n\ntype AvailabilityRuleSetUndoPayload = {\n before?: AvailabilityRuleSetSnapshot | null\n after?: AvailabilityRuleSetSnapshot | null\n}\n\nasync function loadAvailabilityRuleSetSnapshot(\n em: EntityManager,\n id: string,\n scope: PlannerCommandScope,\n): Promise<AvailabilityRuleSetSnapshot | null> {\n const ruleSet = await findOneWithDecryption(\n em,\n PlannerAvailabilityRuleSet,\n applyScopeToWhere<PlannerAvailabilityRuleSet>({ id }, scope),\n undefined,\n scopeForDecryption(scope),\n )\n if (!ruleSet) return null\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.planner.planner_availability_rule_set,\n recordId: ruleSet.id,\n tenantId: ruleSet.tenantId,\n organizationId: ruleSet.organizationId,\n })\n return {\n id: ruleSet.id,\n tenantId: ruleSet.tenantId,\n organizationId: ruleSet.organizationId,\n name: ruleSet.name,\n description: ruleSet.description ?? null,\n timezone: ruleSet.timezone,\n deletedAt: ruleSet.deletedAt ? ruleSet.deletedAt.toISOString() : null,\n custom,\n }\n}\n\nconst createAvailabilityRuleSetCommand: CommandHandler<PlannerAvailabilityRuleSetCreateInput, { ruleSetId: string }> = {\n id: 'planner.availability-rule-sets.create',\n async execute(input, ctx) {\n const { parsed, custom } = parseWithCustomFields(plannerAvailabilityRuleSetCreateSchema, input)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const now = new Date()\n const record = em.create(PlannerAvailabilityRuleSet, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n name: parsed.name,\n description: parsed.description ?? null,\n timezone: parsed.timezone,\n createdAt: now,\n updatedAt: now,\n deletedAt: null,\n })\n em.persist(record)\n await em.flush()\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.planner.planner_availability_rule_set,\n recordId: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n values: custom,\n })\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: plannerAvailabilityRuleSetCrudEvents,\n indexer: availabilityRuleSetCrudIndexer,\n })\n return { ruleSetId: record.id }\n },\n captureAfter: async (input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadAvailabilityRuleSetSnapshot(\n em,\n result.ruleSetId,\n explicitPlannerCommandScope(input.tenantId, input.organizationId),\n )\n if (!snapshot) return null\n return snapshot\n },\n buildLog: async ({ input, result, ctx }) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadAvailabilityRuleSetSnapshot(\n em,\n result.ruleSetId,\n explicitPlannerCommandScope(input.tenantId, input.organizationId),\n )\n if (!snapshot) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('planner.audit.availabilityRuleSets.create', 'Create availability schedule'),\n resourceKind: 'planner.availabilityRuleSet',\n resourceId: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n snapshotAfter: snapshot,\n payload: {\n undo: {\n after: snapshot,\n } satisfies AvailabilityRuleSetUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AvailabilityRuleSetUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const ruleSet = await em.findOne(PlannerAvailabilityRuleSet, { id: after.id })\n if (ruleSet) {\n ruleSet.deletedAt = new Date()\n ruleSet.updatedAt = new Date()\n await em.flush()\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'deleted',\n entity: ruleSet,\n identifiers: {\n id: ruleSet.id,\n organizationId: ruleSet.organizationId,\n tenantId: ruleSet.tenantId,\n },\n events: plannerAvailabilityRuleSetCrudEvents,\n indexer: availabilityRuleSetCrudIndexer,\n })\n }\n },\n redo: makeCreateRedo<PlannerAvailabilityRuleSet, AvailabilityRuleSetSnapshot, PlannerAvailabilityRuleSetCreateInput, { ruleSetId: string }>({\n entityClass: PlannerAvailabilityRuleSet,\n getSnapshotId: (snapshot) => snapshot.id,\n seedFromSnapshot: (snapshot) => ({\n id: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n name: snapshot.name,\n description: snapshot.description ?? null,\n timezone: snapshot.timezone,\n createdAt: new Date(),\n updatedAt: new Date(),\n deletedAt: null,\n }),\n buildResult: (entity) => ({ ruleSetId: entity.id }),\n events: plannerAvailabilityRuleSetCrudEvents,\n indexer: availabilityRuleSetCrudIndexer,\n }),\n}\n\nconst updateAvailabilityRuleSetCommand: CommandHandler<PlannerAvailabilityRuleSetUpdateInput, { ruleSetId: string }> = {\n id: 'planner.availability-rule-sets.update',\n async prepare(input, ctx) {\n const { parsed } = parseWithCustomFields(plannerAvailabilityRuleSetUpdateSchema, input)\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadAvailabilityRuleSetSnapshot(em, parsed.id, commandActorScope(ctx))\n if (!snapshot) return {}\n return { before: snapshot }\n },\n async execute(input, ctx) {\n const { parsed, custom } = parseWithCustomFields(plannerAvailabilityRuleSetUpdateSchema, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope = commandActorScope(ctx)\n const record = await findOneWithDecryption(\n em,\n PlannerAvailabilityRuleSet,\n applyScopeToWhere<PlannerAvailabilityRuleSet>({ id: parsed.id, deletedAt: null }, scope),\n undefined,\n scopeForDecryption(scope),\n )\n if (!record) throw new CrudHttpError(404, { error: 'Planner availability rule set not found.' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n\n if (parsed.name !== undefined) record.name = parsed.name\n if (parsed.description !== undefined) record.description = parsed.description ?? null\n if (parsed.timezone !== undefined) record.timezone = parsed.timezone\n record.updatedAt = new Date()\n await em.flush()\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.planner.planner_availability_rule_set,\n recordId: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n values: custom,\n })\n await emitCrudSideEffects({\n dataEngine,\n action: 'updated',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: plannerAvailabilityRuleSetCrudEvents,\n indexer: availabilityRuleSetCrudIndexer,\n })\n return { ruleSetId: record.id }\n },\n buildLog: async ({ snapshots, ctx }) => {\n const before = snapshots.before as AvailabilityRuleSetSnapshot | undefined\n if (!before) return null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const after = await loadAvailabilityRuleSetSnapshot(\n em,\n before.id,\n explicitPlannerCommandScope(before.tenantId, before.organizationId),\n )\n if (!after) return null\n const changes = buildChanges(before as unknown as Record<string, unknown>, after as unknown as Record<string, unknown>, [\n 'name',\n 'description',\n 'timezone',\n 'deletedAt',\n ])\n const customChanges = diffCustomFieldChanges(before.custom, after.custom)\n if (Object.keys(customChanges).length) {\n changes.customFields = { from: before.custom ?? null, to: after.custom ?? null }\n }\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('planner.audit.availabilityRuleSets.update', 'Update availability schedule'),\n resourceKind: 'planner.availabilityRuleSet',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n snapshotAfter: after,\n changes,\n payload: {\n undo: {\n before,\n after,\n } satisfies AvailabilityRuleSetUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AvailabilityRuleSetUndoPayload>(logEntry)\n const before = payload?.before ?? (logEntry?.snapshotBefore as AvailabilityRuleSetSnapshot | null | undefined)\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const ruleSet = await em.findOne(PlannerAvailabilityRuleSet, { id: before.id })\n if (!ruleSet) return\n ruleSet.name = before.name\n ruleSet.description = before.description ?? null\n ruleSet.timezone = before.timezone\n ruleSet.deletedAt = before.deletedAt ? new Date(before.deletedAt) : null\n ruleSet.updatedAt = new Date()\n await em.flush()\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n const afterSnapshot = payload?.after ?? (logEntry?.snapshotAfter as AvailabilityRuleSetSnapshot | null | undefined)\n if (before.custom || afterSnapshot?.custom) {\n const reset = buildCustomFieldResetMap(before.custom, afterSnapshot?.custom)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.planner.planner_availability_rule_set,\n recordId: ruleSet.id,\n tenantId: ruleSet.tenantId,\n organizationId: ruleSet.organizationId,\n values: reset,\n })\n }\n\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'updated',\n entity: ruleSet,\n identifiers: {\n id: ruleSet.id,\n organizationId: ruleSet.organizationId,\n tenantId: ruleSet.tenantId,\n },\n events: plannerAvailabilityRuleSetCrudEvents,\n indexer: availabilityRuleSetCrudIndexer,\n })\n },\n}\n\nconst deleteAvailabilityRuleSetCommand: CommandHandler<{ id?: string }, { ruleSetId: string }> = {\n id: 'planner.availability-rule-sets.delete',\n async prepare(input, ctx) {\n const id = input?.id\n if (!id) throw new CrudHttpError(400, { error: 'Availability rule set id is required.' })\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadAvailabilityRuleSetSnapshot(em, id, commandActorScope(ctx))\n if (!snapshot) return {}\n return { before: snapshot }\n },\n async execute(input, ctx) {\n const id = input?.id\n if (!id) throw new CrudHttpError(400, { error: 'Availability rule set id is required.' })\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope = commandActorScope(ctx)\n const record = await findOneWithDecryption(\n em,\n PlannerAvailabilityRuleSet,\n applyScopeToWhere<PlannerAvailabilityRuleSet>({ id, deletedAt: null }, scope),\n undefined,\n scopeForDecryption(scope),\n )\n if (!record) throw new CrudHttpError(404, { error: 'Planner availability rule set not found.' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n\n record.deletedAt = new Date()\n record.updatedAt = new Date()\n await em.flush()\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine,\n action: 'deleted',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: plannerAvailabilityRuleSetCrudEvents,\n indexer: availabilityRuleSetCrudIndexer,\n })\n return { ruleSetId: record.id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as AvailabilityRuleSetSnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('planner.audit.availabilityRuleSets.delete', 'Delete availability schedule'),\n resourceKind: 'planner.availabilityRuleSet',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: {\n undo: {\n before,\n } satisfies AvailabilityRuleSetUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AvailabilityRuleSetUndoPayload>(logEntry)\n const before = payload?.before ?? (logEntry?.snapshotBefore as AvailabilityRuleSetSnapshot | null | undefined)\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let ruleSet = await em.findOne(PlannerAvailabilityRuleSet, { id: before.id })\n if (!ruleSet) {\n ruleSet = em.create(PlannerAvailabilityRuleSet, {\n id: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n name: before.name,\n description: before.description ?? null,\n timezone: before.timezone,\n deletedAt: null,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(ruleSet)\n } else {\n ruleSet.name = before.name\n ruleSet.description = before.description ?? null\n ruleSet.timezone = before.timezone\n ruleSet.deletedAt = null\n ruleSet.updatedAt = new Date()\n }\n await em.flush()\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n if (before.custom) {\n const reset = buildCustomFieldResetMap(before.custom, undefined)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.planner.planner_availability_rule_set,\n recordId: ruleSet.id,\n tenantId: ruleSet.tenantId,\n organizationId: ruleSet.organizationId,\n values: reset,\n })\n }\n\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'created',\n entity: ruleSet,\n identifiers: {\n id: ruleSet.id,\n organizationId: ruleSet.organizationId,\n tenantId: ruleSet.tenantId,\n },\n events: plannerAvailabilityRuleSetCrudEvents,\n indexer: availabilityRuleSetCrudIndexer,\n })\n },\n}\n\nregisterCommand(createAvailabilityRuleSetCommand)\nregisterCommand(updateAvailabilityRuleSetCommand)\nregisterCommand(deleteAvailabilityRuleSetCommand)\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -6,12 +6,21 @@ import {
6
6
  plannerAvailabilityRuleCreateSchema,
7
7
  plannerAvailabilityRuleUpdateSchema
8
8
  } from "../data/validators.js";
9
- import { ensureOrganizationScope, ensureTenantScope } from "./shared.js";
10
- import { extractUndoPayload } from "./shared.js";
9
+ import {
10
+ applyScopeToWhere,
11
+ commandActorScope,
12
+ ensureOrganizationScope,
13
+ ensureTenantScope,
14
+ explicitPlannerCommandScope,
15
+ extractUndoPayload
16
+ } from "./shared.js";
11
17
  import { resolveRedoSnapshot } from "@open-mercato/shared/lib/commands/redo";
12
18
  const AVAILABILITY_RULE_RESOURCE_KIND = "planner.availability.rule";
13
- async function loadAvailabilityRuleSnapshot(em, id) {
14
- const record = await em.findOne(PlannerAvailabilityRule, { id });
19
+ async function loadAvailabilityRuleSnapshot(em, id, scope) {
20
+ const record = await em.findOne(
21
+ PlannerAvailabilityRule,
22
+ applyScopeToWhere({ id }, scope)
23
+ );
15
24
  if (!record) return null;
16
25
  return {
17
26
  id: record.id,
@@ -92,9 +101,13 @@ const createAvailabilityRuleCommand = {
92
101
  await em.flush();
93
102
  return { ruleId: record.id };
94
103
  },
95
- captureAfter: async (_input, result, ctx) => {
104
+ captureAfter: async (input, result, ctx) => {
96
105
  const em = ctx.container.resolve("em").fork();
97
- return loadAvailabilityRuleSnapshot(em, result.ruleId);
106
+ return loadAvailabilityRuleSnapshot(
107
+ em,
108
+ result.ruleId,
109
+ explicitPlannerCommandScope(input.tenantId, input.organizationId)
110
+ );
98
111
  },
99
112
  buildLog: async ({ input, result, ctx, snapshots }) => {
100
113
  const snapshot = snapshots.after;
@@ -137,13 +150,19 @@ const updateAvailabilityRuleCommand = {
137
150
  async prepare(input, ctx) {
138
151
  const parsed = plannerAvailabilityRuleUpdateSchema.parse(input);
139
152
  const em = ctx.container.resolve("em");
140
- const snapshot = await loadAvailabilityRuleSnapshot(em, parsed.id);
153
+ const snapshot = await loadAvailabilityRuleSnapshot(em, parsed.id, commandActorScope(ctx));
141
154
  return snapshot ? { before: snapshot } : {};
142
155
  },
143
156
  async execute(input, ctx) {
144
157
  const parsed = plannerAvailabilityRuleUpdateSchema.parse(input);
145
158
  const em = ctx.container.resolve("em").fork();
146
- const record = await em.findOne(PlannerAvailabilityRule, { id: parsed.id, deletedAt: null });
159
+ const record = await em.findOne(
160
+ PlannerAvailabilityRule,
161
+ applyScopeToWhere(
162
+ { id: parsed.id, deletedAt: null },
163
+ commandActorScope(ctx)
164
+ )
165
+ );
147
166
  if (!record) throw new CrudHttpError(404, { error: "Planner availability rule not found." });
148
167
  ensureTenantScope(ctx, record.tenantId);
149
168
  ensureOrganizationScope(ctx, record.organizationId);
@@ -172,7 +191,11 @@ const updateAvailabilityRuleCommand = {
172
191
  buildLog: async ({ snapshots, input, result, ctx }) => {
173
192
  const before = snapshots.before;
174
193
  const em = ctx.container.resolve("em").fork();
175
- const afterSnapshot = before ? await loadAvailabilityRuleSnapshot(em, before.id) : null;
194
+ const afterSnapshot = before ? await loadAvailabilityRuleSnapshot(
195
+ em,
196
+ before.id,
197
+ explicitPlannerCommandScope(before.tenantId, before.organizationId)
198
+ ) : null;
176
199
  const { translate } = await resolveTranslations();
177
200
  return {
178
201
  actionLabel: translate("planner.audit.availability.update", "Update availability rule"),
@@ -205,14 +228,17 @@ const deleteAvailabilityRuleCommand = {
205
228
  const id = input?.id;
206
229
  if (!id) return {};
207
230
  const em = ctx.container.resolve("em");
208
- const snapshot = await loadAvailabilityRuleSnapshot(em, id);
231
+ const snapshot = await loadAvailabilityRuleSnapshot(em, id, commandActorScope(ctx));
209
232
  return snapshot ? { before: snapshot } : {};
210
233
  },
211
234
  async execute(input, ctx) {
212
235
  const id = input?.id;
213
236
  if (!id) throw new CrudHttpError(400, { error: "Availability rule id is required." });
214
237
  const em = ctx.container.resolve("em").fork();
215
- const record = await em.findOne(PlannerAvailabilityRule, { id, deletedAt: null });
238
+ const record = await em.findOne(
239
+ PlannerAvailabilityRule,
240
+ applyScopeToWhere({ id, deletedAt: null }, commandActorScope(ctx))
241
+ );
216
242
  if (!record) return { ruleId: id };
217
243
  ensureTenantScope(ctx, record.tenantId);
218
244
  ensureOrganizationScope(ctx, record.organizationId);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/planner/commands/availability.ts"],
4
- "sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { PlannerAvailabilityRule } from '../data/entities'\nimport {\n plannerAvailabilityRuleCreateSchema,\n plannerAvailabilityRuleUpdateSchema,\n type PlannerAvailabilityRuleCreateInput,\n type PlannerAvailabilityRuleUpdateInput,\n} from '../data/validators'\nimport { ensureOrganizationScope, ensureTenantScope } from './shared'\nimport type { PlannerAvailabilityKind, PlannerAvailabilitySubjectType } from '../data/entities'\nimport { extractUndoPayload } from './shared'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\n\nconst AVAILABILITY_RULE_RESOURCE_KIND = 'planner.availability.rule'\n\ntype AvailabilityRuleSnapshot = {\n id: string\n tenantId: string\n organizationId: string\n subjectType: PlannerAvailabilitySubjectType\n subjectId: string\n timezone: string\n rrule: string\n exdates: string[]\n kind: PlannerAvailabilityKind\n note: string | null\n unavailabilityReasonEntryId: string | null\n unavailabilityReasonValue: string | null\n deletedAt: Date | null\n}\n\ntype AvailabilityRuleUndoPayload = {\n before?: AvailabilityRuleSnapshot | null\n after?: AvailabilityRuleSnapshot | null\n}\n\nasync function loadAvailabilityRuleSnapshot(em: EntityManager, id: string): Promise<AvailabilityRuleSnapshot | null> {\n const record = await em.findOne(PlannerAvailabilityRule, { id })\n if (!record) return null\n return {\n id: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n subjectType: record.subjectType,\n subjectId: record.subjectId,\n timezone: record.timezone,\n rrule: record.rrule,\n exdates: [...(record.exdates ?? [])],\n kind: record.kind,\n note: record.note ?? null,\n unavailabilityReasonEntryId: record.unavailabilityReasonEntryId ?? null,\n unavailabilityReasonValue: record.unavailabilityReasonValue ?? null,\n deletedAt: record.deletedAt ?? null,\n }\n}\n\nasync function restoreAvailabilityRuleFromSnapshot(em: EntityManager, snapshot: AvailabilityRuleSnapshot): Promise<void> {\n let record = await em.findOne(PlannerAvailabilityRule, { id: snapshot.id })\n if (!record) {\n record = em.create(PlannerAvailabilityRule, {\n id: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n subjectType: snapshot.subjectType,\n subjectId: snapshot.subjectId,\n timezone: snapshot.timezone,\n rrule: snapshot.rrule,\n exdates: snapshot.exdates ?? [],\n kind: snapshot.kind ?? 'availability',\n note: snapshot.note ?? null,\n unavailabilityReasonEntryId: snapshot.unavailabilityReasonEntryId ?? null,\n unavailabilityReasonValue: snapshot.unavailabilityReasonValue ?? null,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(record)\n } else {\n record.subjectType = snapshot.subjectType\n record.subjectId = snapshot.subjectId\n record.timezone = snapshot.timezone\n record.rrule = snapshot.rrule\n record.exdates = snapshot.exdates ?? []\n record.kind = snapshot.kind ?? 'availability'\n record.note = snapshot.note ?? null\n record.unavailabilityReasonEntryId = snapshot.unavailabilityReasonEntryId ?? null\n record.unavailabilityReasonValue = snapshot.unavailabilityReasonValue ?? null\n record.deletedAt = snapshot.deletedAt ?? null\n }\n}\n\nconst createAvailabilityRuleCommand: CommandHandler<PlannerAvailabilityRuleCreateInput, { ruleId: string }> = {\n id: 'planner.availability.create',\n async execute(input, ctx) {\n const parsed = plannerAvailabilityRuleCreateSchema.parse(input)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const now = new Date()\n const kind = parsed.kind ?? 'availability'\n const unavailabilityReasonEntryId = kind === 'unavailability' ? parsed.unavailabilityReasonEntryId ?? null : null\n const unavailabilityReasonValue = kind === 'unavailability'\n ? (parsed.unavailabilityReasonValue ?? null)\n : null\n const record = em.create(PlannerAvailabilityRule, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n subjectType: parsed.subjectType,\n subjectId: parsed.subjectId,\n timezone: parsed.timezone,\n rrule: parsed.rrule,\n exdates: parsed.exdates ?? [],\n kind,\n note: parsed.note ?? null,\n unavailabilityReasonEntryId,\n unavailabilityReasonValue,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(record)\n await em.flush()\n return { ruleId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadAvailabilityRuleSnapshot(em, result.ruleId)\n },\n buildLog: async ({ input, result, ctx, snapshots }) => {\n const snapshot = snapshots.after as AvailabilityRuleSnapshot | undefined\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('planner.audit.availability.create', 'Create availability rule'),\n resourceKind: AVAILABILITY_RULE_RESOURCE_KIND,\n resourceId: result?.ruleId ?? null,\n tenantId: input?.tenantId ?? ctx.auth?.tenantId ?? null,\n organizationId: input?.organizationId ?? ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n snapshotAfter: snapshot ?? null,\n payload: {\n undo: {\n after: snapshot,\n } satisfies AvailabilityRuleUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const ruleId = logEntry?.resourceId ?? null\n if (!ruleId) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(PlannerAvailabilityRule, { id: ruleId })\n if (record) {\n record.deletedAt = new Date()\n await em.flush()\n }\n },\n redo: async ({ logEntry, ctx }) => {\n const after = resolveRedoSnapshot<AvailabilityRuleSnapshot>(logEntry)\n if (!after) throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for availability rule create' })\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n await restoreAvailabilityRuleFromSnapshot(em, { ...after, deletedAt: null })\n await em.flush()\n return { ruleId: after.id }\n },\n}\n\nconst updateAvailabilityRuleCommand: CommandHandler<PlannerAvailabilityRuleUpdateInput, { ruleId: string }> = {\n id: 'planner.availability.update',\n async prepare(input, ctx) {\n const parsed = plannerAvailabilityRuleUpdateSchema.parse(input)\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadAvailabilityRuleSnapshot(em, parsed.id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(input, ctx) {\n const parsed = plannerAvailabilityRuleUpdateSchema.parse(input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(PlannerAvailabilityRule, { id: parsed.id, deletedAt: null })\n if (!record) throw new CrudHttpError(404, { error: 'Planner availability rule not found.' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n\n if (parsed.subjectType !== undefined) record.subjectType = parsed.subjectType\n if (parsed.subjectId !== undefined) record.subjectId = parsed.subjectId\n if (parsed.timezone !== undefined) record.timezone = parsed.timezone\n if (parsed.rrule !== undefined) record.rrule = parsed.rrule\n if (parsed.exdates !== undefined) record.exdates = parsed.exdates\n if (parsed.kind !== undefined) record.kind = parsed.kind\n if (parsed.note !== undefined) record.note = parsed.note ?? null\n const nextKind = parsed.kind ?? record.kind\n if (nextKind !== 'unavailability') {\n record.unavailabilityReasonEntryId = null\n record.unavailabilityReasonValue = null\n } else {\n if (parsed.unavailabilityReasonEntryId !== undefined) {\n record.unavailabilityReasonEntryId = parsed.unavailabilityReasonEntryId ?? null\n }\n if (parsed.unavailabilityReasonValue !== undefined) {\n record.unavailabilityReasonValue = parsed.unavailabilityReasonValue ?? null\n }\n }\n\n await em.flush()\n return { ruleId: record.id }\n },\n buildLog: async ({ snapshots, input, result, ctx }) => {\n const before = snapshots.before as AvailabilityRuleSnapshot | undefined\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const afterSnapshot = before ? await loadAvailabilityRuleSnapshot(em, before.id) : null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('planner.audit.availability.update', 'Update availability rule'),\n resourceKind: AVAILABILITY_RULE_RESOURCE_KIND,\n resourceId: result?.ruleId ?? input?.id ?? null,\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: afterSnapshot ?? null,\n payload: {\n undo: {\n before: before ?? null,\n after: afterSnapshot ?? null,\n } satisfies AvailabilityRuleUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AvailabilityRuleUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n await restoreAvailabilityRuleFromSnapshot(em, before)\n await em.flush()\n },\n}\n\nconst deleteAvailabilityRuleCommand: CommandHandler<{ id?: string }, { ruleId: string }> = {\n id: 'planner.availability.delete',\n async prepare(input, ctx) {\n const id = input?.id\n if (!id) return {}\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadAvailabilityRuleSnapshot(em, id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(input, ctx) {\n const id = input?.id\n if (!id) throw new CrudHttpError(400, { error: 'Availability rule id is required.' })\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(PlannerAvailabilityRule, { id, deletedAt: null })\n if (!record) return { ruleId: id }\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n record.deletedAt = new Date()\n await em.flush()\n return { ruleId: record.id }\n },\n buildLog: async ({ snapshots, input, result, ctx }) => {\n const before = snapshots.before as AvailabilityRuleSnapshot | undefined\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('planner.audit.availability.delete', 'Delete availability rule'),\n resourceKind: AVAILABILITY_RULE_RESOURCE_KIND,\n resourceId: result?.ruleId ?? input?.id ?? null,\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n snapshotBefore: before ?? null,\n payload: {\n undo: {\n before: before ?? null,\n } satisfies AvailabilityRuleUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AvailabilityRuleUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n await restoreAvailabilityRuleFromSnapshot(em, { ...before, deletedAt: null })\n await em.flush()\n },\n}\n\nregisterCommand(createAvailabilityRuleCommand)\nregisterCommand(updateAvailabilityRuleCommand)\nregisterCommand(deleteAvailabilityRuleCommand)\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { PlannerAvailabilityRule } from '../data/entities'\nimport {\n plannerAvailabilityRuleCreateSchema,\n plannerAvailabilityRuleUpdateSchema,\n type PlannerAvailabilityRuleCreateInput,\n type PlannerAvailabilityRuleUpdateInput,\n} from '../data/validators'\nimport {\n applyScopeToWhere,\n commandActorScope,\n ensureOrganizationScope,\n ensureTenantScope,\n explicitPlannerCommandScope,\n extractUndoPayload,\n type PlannerCommandScope,\n} from './shared'\nimport type { PlannerAvailabilityKind, PlannerAvailabilitySubjectType } from '../data/entities'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\n\nconst AVAILABILITY_RULE_RESOURCE_KIND = 'planner.availability.rule'\n\ntype AvailabilityRuleSnapshot = {\n id: string\n tenantId: string\n organizationId: string\n subjectType: PlannerAvailabilitySubjectType\n subjectId: string\n timezone: string\n rrule: string\n exdates: string[]\n kind: PlannerAvailabilityKind\n note: string | null\n unavailabilityReasonEntryId: string | null\n unavailabilityReasonValue: string | null\n deletedAt: Date | null\n}\n\ntype AvailabilityRuleUndoPayload = {\n before?: AvailabilityRuleSnapshot | null\n after?: AvailabilityRuleSnapshot | null\n}\n\nasync function loadAvailabilityRuleSnapshot(\n em: EntityManager,\n id: string,\n scope: PlannerCommandScope,\n): Promise<AvailabilityRuleSnapshot | null> {\n const record = await em.findOne(\n PlannerAvailabilityRule,\n applyScopeToWhere<PlannerAvailabilityRule>({ id }, scope),\n )\n if (!record) return null\n return {\n id: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n subjectType: record.subjectType,\n subjectId: record.subjectId,\n timezone: record.timezone,\n rrule: record.rrule,\n exdates: [...(record.exdates ?? [])],\n kind: record.kind,\n note: record.note ?? null,\n unavailabilityReasonEntryId: record.unavailabilityReasonEntryId ?? null,\n unavailabilityReasonValue: record.unavailabilityReasonValue ?? null,\n deletedAt: record.deletedAt ?? null,\n }\n}\n\nasync function restoreAvailabilityRuleFromSnapshot(em: EntityManager, snapshot: AvailabilityRuleSnapshot): Promise<void> {\n let record = await em.findOne(PlannerAvailabilityRule, { id: snapshot.id })\n if (!record) {\n record = em.create(PlannerAvailabilityRule, {\n id: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n subjectType: snapshot.subjectType,\n subjectId: snapshot.subjectId,\n timezone: snapshot.timezone,\n rrule: snapshot.rrule,\n exdates: snapshot.exdates ?? [],\n kind: snapshot.kind ?? 'availability',\n note: snapshot.note ?? null,\n unavailabilityReasonEntryId: snapshot.unavailabilityReasonEntryId ?? null,\n unavailabilityReasonValue: snapshot.unavailabilityReasonValue ?? null,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(record)\n } else {\n record.subjectType = snapshot.subjectType\n record.subjectId = snapshot.subjectId\n record.timezone = snapshot.timezone\n record.rrule = snapshot.rrule\n record.exdates = snapshot.exdates ?? []\n record.kind = snapshot.kind ?? 'availability'\n record.note = snapshot.note ?? null\n record.unavailabilityReasonEntryId = snapshot.unavailabilityReasonEntryId ?? null\n record.unavailabilityReasonValue = snapshot.unavailabilityReasonValue ?? null\n record.deletedAt = snapshot.deletedAt ?? null\n }\n}\n\nconst createAvailabilityRuleCommand: CommandHandler<PlannerAvailabilityRuleCreateInput, { ruleId: string }> = {\n id: 'planner.availability.create',\n async execute(input, ctx) {\n const parsed = plannerAvailabilityRuleCreateSchema.parse(input)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const now = new Date()\n const kind = parsed.kind ?? 'availability'\n const unavailabilityReasonEntryId = kind === 'unavailability' ? parsed.unavailabilityReasonEntryId ?? null : null\n const unavailabilityReasonValue = kind === 'unavailability'\n ? (parsed.unavailabilityReasonValue ?? null)\n : null\n const record = em.create(PlannerAvailabilityRule, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n subjectType: parsed.subjectType,\n subjectId: parsed.subjectId,\n timezone: parsed.timezone,\n rrule: parsed.rrule,\n exdates: parsed.exdates ?? [],\n kind,\n note: parsed.note ?? null,\n unavailabilityReasonEntryId,\n unavailabilityReasonValue,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(record)\n await em.flush()\n return { ruleId: record.id }\n },\n captureAfter: async (input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadAvailabilityRuleSnapshot(\n em,\n result.ruleId,\n explicitPlannerCommandScope(input.tenantId, input.organizationId),\n )\n },\n buildLog: async ({ input, result, ctx, snapshots }) => {\n const snapshot = snapshots.after as AvailabilityRuleSnapshot | undefined\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('planner.audit.availability.create', 'Create availability rule'),\n resourceKind: AVAILABILITY_RULE_RESOURCE_KIND,\n resourceId: result?.ruleId ?? null,\n tenantId: input?.tenantId ?? ctx.auth?.tenantId ?? null,\n organizationId: input?.organizationId ?? ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n snapshotAfter: snapshot ?? null,\n payload: {\n undo: {\n after: snapshot,\n } satisfies AvailabilityRuleUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const ruleId = logEntry?.resourceId ?? null\n if (!ruleId) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(PlannerAvailabilityRule, { id: ruleId })\n if (record) {\n record.deletedAt = new Date()\n await em.flush()\n }\n },\n redo: async ({ logEntry, ctx }) => {\n const after = resolveRedoSnapshot<AvailabilityRuleSnapshot>(logEntry)\n if (!after) throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for availability rule create' })\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n await restoreAvailabilityRuleFromSnapshot(em, { ...after, deletedAt: null })\n await em.flush()\n return { ruleId: after.id }\n },\n}\n\nconst updateAvailabilityRuleCommand: CommandHandler<PlannerAvailabilityRuleUpdateInput, { ruleId: string }> = {\n id: 'planner.availability.update',\n async prepare(input, ctx) {\n const parsed = plannerAvailabilityRuleUpdateSchema.parse(input)\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadAvailabilityRuleSnapshot(em, parsed.id, commandActorScope(ctx))\n return snapshot ? { before: snapshot } : {}\n },\n async execute(input, ctx) {\n const parsed = plannerAvailabilityRuleUpdateSchema.parse(input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(\n PlannerAvailabilityRule,\n applyScopeToWhere<PlannerAvailabilityRule>(\n { id: parsed.id, deletedAt: null },\n commandActorScope(ctx),\n ),\n )\n if (!record) throw new CrudHttpError(404, { error: 'Planner availability rule not found.' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n\n if (parsed.subjectType !== undefined) record.subjectType = parsed.subjectType\n if (parsed.subjectId !== undefined) record.subjectId = parsed.subjectId\n if (parsed.timezone !== undefined) record.timezone = parsed.timezone\n if (parsed.rrule !== undefined) record.rrule = parsed.rrule\n if (parsed.exdates !== undefined) record.exdates = parsed.exdates\n if (parsed.kind !== undefined) record.kind = parsed.kind\n if (parsed.note !== undefined) record.note = parsed.note ?? null\n const nextKind = parsed.kind ?? record.kind\n if (nextKind !== 'unavailability') {\n record.unavailabilityReasonEntryId = null\n record.unavailabilityReasonValue = null\n } else {\n if (parsed.unavailabilityReasonEntryId !== undefined) {\n record.unavailabilityReasonEntryId = parsed.unavailabilityReasonEntryId ?? null\n }\n if (parsed.unavailabilityReasonValue !== undefined) {\n record.unavailabilityReasonValue = parsed.unavailabilityReasonValue ?? null\n }\n }\n\n await em.flush()\n return { ruleId: record.id }\n },\n buildLog: async ({ snapshots, input, result, ctx }) => {\n const before = snapshots.before as AvailabilityRuleSnapshot | undefined\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const afterSnapshot = before\n ? await loadAvailabilityRuleSnapshot(\n em,\n before.id,\n explicitPlannerCommandScope(before.tenantId, before.organizationId),\n )\n : null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('planner.audit.availability.update', 'Update availability rule'),\n resourceKind: AVAILABILITY_RULE_RESOURCE_KIND,\n resourceId: result?.ruleId ?? input?.id ?? null,\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: afterSnapshot ?? null,\n payload: {\n undo: {\n before: before ?? null,\n after: afterSnapshot ?? null,\n } satisfies AvailabilityRuleUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AvailabilityRuleUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n await restoreAvailabilityRuleFromSnapshot(em, before)\n await em.flush()\n },\n}\n\nconst deleteAvailabilityRuleCommand: CommandHandler<{ id?: string }, { ruleId: string }> = {\n id: 'planner.availability.delete',\n async prepare(input, ctx) {\n const id = input?.id\n if (!id) return {}\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadAvailabilityRuleSnapshot(em, id, commandActorScope(ctx))\n return snapshot ? { before: snapshot } : {}\n },\n async execute(input, ctx) {\n const id = input?.id\n if (!id) throw new CrudHttpError(400, { error: 'Availability rule id is required.' })\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(\n PlannerAvailabilityRule,\n applyScopeToWhere<PlannerAvailabilityRule>({ id, deletedAt: null }, commandActorScope(ctx)),\n )\n if (!record) return { ruleId: id }\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n record.deletedAt = new Date()\n await em.flush()\n return { ruleId: record.id }\n },\n buildLog: async ({ snapshots, input, result, ctx }) => {\n const before = snapshots.before as AvailabilityRuleSnapshot | undefined\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('planner.audit.availability.delete', 'Delete availability rule'),\n resourceKind: AVAILABILITY_RULE_RESOURCE_KIND,\n resourceId: result?.ruleId ?? input?.id ?? null,\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n snapshotBefore: before ?? null,\n payload: {\n undo: {\n before: before ?? null,\n } satisfies AvailabilityRuleUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AvailabilityRuleUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n await restoreAvailabilityRuleFromSnapshot(em, { ...before, deletedAt: null })\n await em.flush()\n },\n}\n\nregisterCommand(createAvailabilityRuleCommand)\nregisterCommand(updateAvailabilityRuleCommand)\nregisterCommand(deleteAvailabilityRuleCommand)\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,8 +1,41 @@
1
1
  import { ensureOrganizationScope, ensureTenantScope } from "@open-mercato/shared/lib/commands/scope";
2
2
  import { extractUndoPayload } from "@open-mercato/shared/lib/commands/undo";
3
+ function commandActorScope(ctx) {
4
+ const isPrivilegedActor = ctx.auth?.isSuperAdmin === true || ctx.systemActor === true;
5
+ const tenantId = isPrivilegedActor ? null : ctx.auth?.tenantId ?? ctx.organizationScope?.tenantId ?? null;
6
+ const organizationId = isPrivilegedActor ? null : ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null;
7
+ const organizationUnrestricted = isPrivilegedActor || organizationId === null && tenantId !== null && ctx.organizationScope?.allowedIds === null;
8
+ return {
9
+ tenantId,
10
+ organizationId: organizationUnrestricted ? null : organizationId,
11
+ requireTenant: !isPrivilegedActor,
12
+ requireOrganization: !organizationUnrestricted
13
+ };
14
+ }
15
+ function explicitPlannerCommandScope(tenantId, organizationId) {
16
+ return {
17
+ tenantId,
18
+ organizationId,
19
+ requireTenant: true,
20
+ requireOrganization: true
21
+ };
22
+ }
23
+ function applyScopeToWhere(where, scope) {
24
+ const scoped = { ...where };
25
+ if (scope.requireTenant || scope.tenantId !== null) scoped.tenantId = scope.tenantId;
26
+ if (scope.requireOrganization || scope.organizationId !== null) scoped.organizationId = scope.organizationId;
27
+ return scoped;
28
+ }
29
+ function scopeForDecryption(scope) {
30
+ return { tenantId: scope.tenantId, organizationId: scope.organizationId };
31
+ }
3
32
  export {
33
+ applyScopeToWhere,
34
+ commandActorScope,
4
35
  ensureOrganizationScope,
5
36
  ensureTenantScope,
6
- extractUndoPayload
37
+ explicitPlannerCommandScope,
38
+ extractUndoPayload,
39
+ scopeForDecryption
7
40
  };
8
41
  //# sourceMappingURL=shared.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/planner/commands/shared.ts"],
4
- "sourcesContent": ["import { ensureOrganizationScope, ensureTenantScope } from '@open-mercato/shared/lib/commands/scope'\nimport { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'\n\nexport { ensureOrganizationScope, ensureTenantScope, extractUndoPayload }\n"],
5
- "mappings": "AAAA,SAAS,yBAAyB,yBAAyB;AAC3D,SAAS,0BAA0B;",
4
+ "sourcesContent": ["import type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { ensureOrganizationScope, ensureTenantScope } from '@open-mercato/shared/lib/commands/scope'\nimport { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport type { FilterQuery } from '@mikro-orm/postgresql'\n\nexport { ensureOrganizationScope, ensureTenantScope, extractUndoPayload }\n\nexport type PlannerCommandScope = {\n tenantId: string | null\n organizationId: string | null\n requireTenant: boolean\n requireOrganization: boolean\n}\n\nexport function commandActorScope(ctx: CommandRuntimeContext): PlannerCommandScope {\n const isPrivilegedActor = ctx.auth?.isSuperAdmin === true || ctx.systemActor === true\n const tenantId = isPrivilegedActor ? null : (ctx.auth?.tenantId ?? ctx.organizationScope?.tenantId ?? null)\n const organizationId = isPrivilegedActor ? null : (ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null)\n const organizationUnrestricted =\n isPrivilegedActor || (organizationId === null && tenantId !== null && ctx.organizationScope?.allowedIds === null)\n return {\n tenantId,\n organizationId: organizationUnrestricted ? null : organizationId,\n requireTenant: !isPrivilegedActor,\n requireOrganization: !organizationUnrestricted,\n }\n}\n\nexport function explicitPlannerCommandScope(\n tenantId: string | null,\n organizationId: string | null,\n): PlannerCommandScope {\n return {\n tenantId,\n organizationId,\n requireTenant: true,\n requireOrganization: true,\n }\n}\n\nexport function applyScopeToWhere<TEntity extends object>(\n where: FilterQuery<TEntity>,\n scope: PlannerCommandScope,\n): FilterQuery<TEntity> {\n const scoped = { ...(where as Record<string, unknown>) }\n if (scope.requireTenant || scope.tenantId !== null) scoped.tenantId = scope.tenantId\n if (scope.requireOrganization || scope.organizationId !== null) scoped.organizationId = scope.organizationId\n return scoped as FilterQuery<TEntity>\n}\n\nexport function scopeForDecryption(\n scope: PlannerCommandScope,\n): { tenantId: string | null; organizationId: string | null } {\n return { tenantId: scope.tenantId, organizationId: scope.organizationId }\n}\n"],
5
+ "mappings": "AACA,SAAS,yBAAyB,yBAAyB;AAC3D,SAAS,0BAA0B;AAY5B,SAAS,kBAAkB,KAAiD;AACjF,QAAM,oBAAoB,IAAI,MAAM,iBAAiB,QAAQ,IAAI,gBAAgB;AACjF,QAAM,WAAW,oBAAoB,OAAQ,IAAI,MAAM,YAAY,IAAI,mBAAmB,YAAY;AACtG,QAAM,iBAAiB,oBAAoB,OAAQ,IAAI,0BAA0B,IAAI,MAAM,SAAS;AACpG,QAAM,2BACJ,qBAAsB,mBAAmB,QAAQ,aAAa,QAAQ,IAAI,mBAAmB,eAAe;AAC9G,SAAO;AAAA,IACL;AAAA,IACA,gBAAgB,2BAA2B,OAAO;AAAA,IAClD,eAAe,CAAC;AAAA,IAChB,qBAAqB,CAAC;AAAA,EACxB;AACF;AAEO,SAAS,4BACd,UACA,gBACqB;AACrB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf,qBAAqB;AAAA,EACvB;AACF;AAEO,SAAS,kBACd,OACA,OACsB;AACtB,QAAM,SAAS,EAAE,GAAI,MAAkC;AACvD,MAAI,MAAM,iBAAiB,MAAM,aAAa,KAAM,QAAO,WAAW,MAAM;AAC5E,MAAI,MAAM,uBAAuB,MAAM,mBAAmB,KAAM,QAAO,iBAAiB,MAAM;AAC9F,SAAO;AACT;AAEO,SAAS,mBACd,OAC4D;AAC5D,SAAO,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAC1E;",
6
6
  "names": []
7
7
  }