@open-mercato/core 0.8.1-develop.7266.1.8e520bbe03 → 0.8.1-develop.7268.1.033c0546d5
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 +2 -2
- package/dist/generated/entities.ids.generated.js +0 -4
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +0 -21
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/create/page.js +5 -0
- package/dist/modules/customers/backend/customers/companies/create/page.js.map +2 -2
- package/dist/modules/wms/di.js +0 -3
- package/dist/modules/wms/di.js.map +2 -2
- package/generated/entities.ids.generated.ts +0 -4
- package/generated/entity-fields-registry.ts +0 -21
- package/package.json +7 -7
- package/src/modules/auth/i18n/de.json +0 -3
- package/src/modules/auth/i18n/en.json +0 -3
- package/src/modules/auth/i18n/es.json +0 -3
- package/src/modules/auth/i18n/ko.json +0 -3
- package/src/modules/auth/i18n/pl.json +0 -3
- package/src/modules/customer_accounts/AGENTS.md +1 -1
- package/src/modules/customers/backend/customers/companies/create/page.tsx +6 -0
- package/src/modules/wms/di.ts +0 -6
- package/dist/generated/entities/availability_policy/index.js +0 -41
- package/dist/generated/entities/availability_policy/index.js.map +0 -7
- package/dist/modules/availability/acl.js +0 -16
- package/dist/modules/availability/acl.js.map +0 -7
- package/dist/modules/availability/api/check/route.js +0 -95
- package/dist/modules/availability/api/check/route.js.map +0 -7
- package/dist/modules/availability/api/openapi.js +0 -27
- package/dist/modules/availability/api/openapi.js.map +0 -7
- package/dist/modules/availability/api/policies/resolve-preview/route.js +0 -58
- package/dist/modules/availability/api/policies/resolve-preview/route.js.map +0 -7
- package/dist/modules/availability/api/policies/route.js +0 -188
- package/dist/modules/availability/api/policies/route.js.map +0 -7
- package/dist/modules/availability/backend/availability/check/page.js +0 -185
- package/dist/modules/availability/backend/availability/check/page.js.map +0 -7
- package/dist/modules/availability/backend/availability/check/page.meta.js +0 -16
- package/dist/modules/availability/backend/availability/check/page.meta.js.map +0 -7
- package/dist/modules/availability/backend/availability/policies/ResolutionPreviewPanel.js +0 -79
- package/dist/modules/availability/backend/availability/policies/ResolutionPreviewPanel.js.map +0 -7
- package/dist/modules/availability/backend/availability/policies/ScopeFields.js +0 -53
- package/dist/modules/availability/backend/availability/policies/ScopeFields.js.map +0 -7
- package/dist/modules/availability/backend/availability/policies/[id]/page.js +0 -152
- package/dist/modules/availability/backend/availability/policies/[id]/page.js.map +0 -7
- package/dist/modules/availability/backend/availability/policies/[id]/page.meta.js +0 -12
- package/dist/modules/availability/backend/availability/policies/[id]/page.meta.js.map +0 -7
- package/dist/modules/availability/backend/availability/policies/create/page.js +0 -94
- package/dist/modules/availability/backend/availability/policies/create/page.js.map +0 -7
- package/dist/modules/availability/backend/availability/policies/create/page.meta.js +0 -12
- package/dist/modules/availability/backend/availability/policies/create/page.meta.js.map +0 -7
- package/dist/modules/availability/backend/availability/policies/formGroups.js +0 -75
- package/dist/modules/availability/backend/availability/policies/formGroups.js.map +0 -7
- package/dist/modules/availability/backend/availability/policies/page.js +0 -176
- package/dist/modules/availability/backend/availability/policies/page.js.map +0 -7
- package/dist/modules/availability/backend/availability/policies/page.meta.js +0 -16
- package/dist/modules/availability/backend/availability/policies/page.meta.js.map +0 -7
- package/dist/modules/availability/commands/policies.js +0 -312
- package/dist/modules/availability/commands/policies.js.map +0 -7
- package/dist/modules/availability/commands/scope.js +0 -19
- package/dist/modules/availability/commands/scope.js.map +0 -7
- package/dist/modules/availability/data/entities.js +0 -95
- package/dist/modules/availability/data/entities.js.map +0 -7
- package/dist/modules/availability/data/validators.js +0 -53
- package/dist/modules/availability/data/validators.js.map +0 -7
- package/dist/modules/availability/di.js +0 -41
- package/dist/modules/availability/di.js.map +0 -7
- package/dist/modules/availability/events.js +0 -18
- package/dist/modules/availability/events.js.map +0 -7
- package/dist/modules/availability/index.js +0 -15
- package/dist/modules/availability/index.js.map +0 -7
- package/dist/modules/availability/lib/policyResolution.js +0 -155
- package/dist/modules/availability/lib/policyResolution.js.map +0 -7
- package/dist/modules/availability/lib/tryResolve.js +0 -11
- package/dist/modules/availability/lib/tryResolve.js.map +0 -7
- package/dist/modules/availability/migrations/Migration20260922095844_availability.js +0 -16
- package/dist/modules/availability/migrations/Migration20260922095844_availability.js.map +0 -7
- package/dist/modules/availability/setup.js +0 -12
- package/dist/modules/availability/setup.js.map +0 -7
- package/dist/modules/wms/lib/availabilityCache.js +0 -85
- package/dist/modules/wms/lib/availabilityCache.js.map +0 -7
- package/dist/modules/wms/lib/availabilityCalculation.js +0 -178
- package/dist/modules/wms/lib/availabilityCalculation.js.map +0 -7
- package/dist/modules/wms/lib/availabilityProvider.js +0 -14
- package/dist/modules/wms/lib/availabilityProvider.js.map +0 -7
- package/dist/modules/wms/lib/sqlInClause.js +0 -9
- package/dist/modules/wms/lib/sqlInClause.js.map +0 -7
- package/dist/modules/wms/lib/tryResolve.js +0 -11
- package/dist/modules/wms/lib/tryResolve.js.map +0 -7
- package/dist/modules/wms/subscribers/invalidate-availability-cache.js +0 -16
- package/dist/modules/wms/subscribers/invalidate-availability-cache.js.map +0 -7
- package/generated/entities/availability_policy/index.ts +0 -19
- package/src/modules/availability/acl.ts +0 -12
- package/src/modules/availability/api/check/route.ts +0 -114
- package/src/modules/availability/api/openapi.ts +0 -28
- package/src/modules/availability/api/policies/resolve-preview/route.ts +0 -67
- package/src/modules/availability/api/policies/route.ts +0 -224
- package/src/modules/availability/backend/availability/check/page.meta.ts +0 -12
- package/src/modules/availability/backend/availability/check/page.tsx +0 -223
- package/src/modules/availability/backend/availability/policies/ResolutionPreviewPanel.tsx +0 -106
- package/src/modules/availability/backend/availability/policies/ScopeFields.tsx +0 -55
- package/src/modules/availability/backend/availability/policies/[id]/page.meta.ts +0 -8
- package/src/modules/availability/backend/availability/policies/[id]/page.tsx +0 -182
- package/src/modules/availability/backend/availability/policies/create/page.meta.ts +0 -8
- package/src/modules/availability/backend/availability/policies/create/page.tsx +0 -105
- package/src/modules/availability/backend/availability/policies/formGroups.ts +0 -77
- package/src/modules/availability/backend/availability/policies/page.meta.ts +0 -12
- package/src/modules/availability/backend/availability/policies/page.tsx +0 -196
- package/src/modules/availability/commands/policies.ts +0 -358
- package/src/modules/availability/commands/scope.ts +0 -26
- package/src/modules/availability/data/entities.ts +0 -92
- package/src/modules/availability/data/validators.ts +0 -70
- package/src/modules/availability/di.ts +0 -54
- package/src/modules/availability/events.ts +0 -17
- package/src/modules/availability/i18n/de.json +0 -81
- package/src/modules/availability/i18n/en.json +0 -81
- package/src/modules/availability/i18n/es.json +0 -81
- package/src/modules/availability/i18n/ko.json +0 -81
- package/src/modules/availability/i18n/pl.json +0 -81
- package/src/modules/availability/index.ts +0 -13
- package/src/modules/availability/lib/policyResolution.ts +0 -264
- package/src/modules/availability/lib/tryResolve.ts +0 -8
- package/src/modules/availability/migrations/.snapshot-open-mercato.json +0 -375
- package/src/modules/availability/migrations/Migration20260922095844_availability.ts +0 -13
- package/src/modules/availability/setup.ts +0 -10
- package/src/modules/wms/lib/availabilityCache.ts +0 -136
- package/src/modules/wms/lib/availabilityCalculation.ts +0 -289
- package/src/modules/wms/lib/availabilityProvider.ts +0 -29
- package/src/modules/wms/lib/sqlInClause.ts +0 -21
- package/src/modules/wms/lib/tryResolve.ts +0 -8
- package/src/modules/wms/subscribers/invalidate-availability-cache.ts +0 -30
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../../src/modules/availability/data/validators.ts"],
|
|
4
|
-
"sourcesContent": ["import { z } from 'zod'\n\nconst uuid = () => z.string().uuid()\n\nconst scopedSchema = z.object({\n organizationId: uuid(),\n tenantId: uuid(),\n})\n\nconst nonNegativeInt = () => z.number().int().min(0)\n\nconst policyFieldsSchema = z.object({\n storeId: uuid().nullable().optional(),\n productId: uuid().nullable().optional(),\n variantId: uuid().nullable().optional(),\n isStockManaged: z.boolean().optional(),\n allowBackorder: z.boolean().optional(),\n backorderLeadTimeDays: nonNegativeInt().nullable().optional(),\n preorderReleaseAt: z.coerce.date().nullable().optional(),\n lowStockThreshold: nonNegativeInt().nullable().optional(),\n minOrderQuantity: nonNegativeInt().nullable().optional(),\n maxOrderQuantity: nonNegativeInt().nullable().optional(),\n quantityIncrement: nonNegativeInt().nullable().optional(),\n hideWhenOutOfStock: z.boolean().optional(),\n isActive: z.boolean().optional(),\n})\n\nexport const AVAILABILITY_POLICY_VARIANT_REQUIRES_PRODUCT_MESSAGE_KEY =\n 'availability.policies.errors.variantRequiresProduct'\nexport const AVAILABILITY_POLICY_BACKORDER_REQUIRES_LEAD_TIME_MESSAGE_KEY =\n 'availability.policies.errors.backorderRequiresLeadTime'\nexport const AVAILABILITY_POLICY_MAX_BELOW_MIN_MESSAGE_KEY = 'availability.policies.errors.maxBelowMin'\n\nfunction refinePolicyConstraints<T extends z.ZodTypeAny>(schema: T) {\n return schema\n .refine((val: any) => !(val.variantId && !val.productId), {\n message: AVAILABILITY_POLICY_VARIANT_REQUIRES_PRODUCT_MESSAGE_KEY,\n path: ['variantId'],\n })\n .refine((val: any) => !(val.allowBackorder && val.backorderLeadTimeDays == null), {\n message: AVAILABILITY_POLICY_BACKORDER_REQUIRES_LEAD_TIME_MESSAGE_KEY,\n path: ['backorderLeadTimeDays'],\n })\n .refine(\n (val: any) =>\n !(val.minOrderQuantity != null && val.maxOrderQuantity != null && val.maxOrderQuantity < val.minOrderQuantity),\n { message: AVAILABILITY_POLICY_MAX_BELOW_MIN_MESSAGE_KEY, path: ['maxOrderQuantity'] },\n )\n}\n\nexport const availabilityPolicyCreateSchema = refinePolicyConstraints(\n scopedSchema.extend(policyFieldsSchema.shape),\n)\n\n// Cross-field constraints (variant requires product, backorder requires lead time, max >= min)\n// are NOT re-checked here at the zod layer: a partial update payload may touch only one side of\n// a constraint while the other side's value already satisfies it in the stored row. The update\n// command (commands/policies.ts) re-validates the same constraints against the merged\n// (existing-row + patch) record, where the full picture is available.\nexport const availabilityPolicyUpdateSchema = z\n .object({ id: uuid() })\n .merge(scopedSchema.extend(policyFieldsSchema.shape).partial())\n\nexport const availabilityPolicyDeleteSchema = scopedSchema.extend({ id: uuid() })\n\n/** Re-applies the create-time cross-field constraints to a merged (existing + patch) record. Used by the update command. */\nexport const availabilityPolicyMergedConstraintsSchema = refinePolicyConstraints(policyFieldsSchema)\n\nexport type AvailabilityPolicyCreateInput = z.infer<typeof availabilityPolicyCreateSchema>\nexport type AvailabilityPolicyUpdateInput = z.infer<typeof availabilityPolicyUpdateSchema>\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,SAAS;AAElB,MAAM,OAAO,MAAM,EAAE,OAAO,EAAE,KAAK;AAEnC,MAAM,eAAe,EAAE,OAAO;AAAA,EAC5B,gBAAgB,KAAK;AAAA,EACrB,UAAU,KAAK;AACjB,CAAC;AAED,MAAM,iBAAiB,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC;AAEnD,MAAM,qBAAqB,EAAE,OAAO;AAAA,EAClC,SAAS,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACpC,WAAW,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACtC,WAAW,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACtC,gBAAgB,EAAE,QAAQ,EAAE,SAAS;AAAA,EACrC,gBAAgB,EAAE,QAAQ,EAAE,SAAS;AAAA,EACrC,uBAAuB,eAAe,EAAE,SAAS,EAAE,SAAS;AAAA,EAC5D,mBAAmB,EAAE,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACvD,mBAAmB,eAAe,EAAE,SAAS,EAAE,SAAS;AAAA,EACxD,kBAAkB,eAAe,EAAE,SAAS,EAAE,SAAS;AAAA,EACvD,kBAAkB,eAAe,EAAE,SAAS,EAAE,SAAS;AAAA,EACvD,mBAAmB,eAAe,EAAE,SAAS,EAAE,SAAS;AAAA,EACxD,oBAAoB,EAAE,QAAQ,EAAE,SAAS;AAAA,EACzC,UAAU,EAAE,QAAQ,EAAE,SAAS;AACjC,CAAC;AAEM,MAAM,2DACX;AACK,MAAM,+DACX;AACK,MAAM,gDAAgD;AAE7D,SAAS,wBAAgD,QAAW;AAClE,SAAO,OACJ,OAAO,CAAC,QAAa,EAAE,IAAI,aAAa,CAAC,IAAI,YAAY;AAAA,IACxD,SAAS;AAAA,IACT,MAAM,CAAC,WAAW;AAAA,EACpB,CAAC,EACA,OAAO,CAAC,QAAa,EAAE,IAAI,kBAAkB,IAAI,yBAAyB,OAAO;AAAA,IAChF,SAAS;AAAA,IACT,MAAM,CAAC,uBAAuB;AAAA,EAChC,CAAC,EACA;AAAA,IACC,CAAC,QACC,EAAE,IAAI,oBAAoB,QAAQ,IAAI,oBAAoB,QAAQ,IAAI,mBAAmB,IAAI;AAAA,IAC/F,EAAE,SAAS,+CAA+C,MAAM,CAAC,kBAAkB,EAAE;AAAA,EACvF;AACJ;AAEO,MAAM,iCAAiC;AAAA,EAC5C,aAAa,OAAO,mBAAmB,KAAK;AAC9C;AAOO,MAAM,iCAAiC,EAC3C,OAAO,EAAE,IAAI,KAAK,EAAE,CAAC,EACrB,MAAM,aAAa,OAAO,mBAAmB,KAAK,EAAE,QAAQ,CAAC;AAEzD,MAAM,iCAAiC,aAAa,OAAO,EAAE,IAAI,KAAK,EAAE,CAAC;AAGzE,MAAM,4CAA4C,wBAAwB,kBAAkB;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { asValue } from "awilix";
|
|
2
|
-
import { setCatalogOnlyPolicyLookup, availabilityItemKey } from "@open-mercato/shared/lib/availability";
|
|
3
|
-
import { AvailabilityPolicy } from "./data/entities.js";
|
|
4
|
-
import { createPolicyResolutionService } from "./lib/policyResolution.js";
|
|
5
|
-
function register(container) {
|
|
6
|
-
container.register({
|
|
7
|
-
AvailabilityPolicy: asValue(AvailabilityPolicy),
|
|
8
|
-
policyResolutionService: {
|
|
9
|
-
resolve: (c) => createPolicyResolutionService(c)
|
|
10
|
-
}
|
|
11
|
-
});
|
|
12
|
-
setCatalogOnlyPolicyLookup(async (query) => {
|
|
13
|
-
const em = container.resolve("em").fork();
|
|
14
|
-
const service = createPolicyResolutionService(container);
|
|
15
|
-
const overrides = {};
|
|
16
|
-
const resolved = await service.resolveMany(
|
|
17
|
-
em,
|
|
18
|
-
query.items.map((item) => ({
|
|
19
|
-
tenantId: query.tenantId,
|
|
20
|
-
organizationId: query.organizationId,
|
|
21
|
-
storeId: query.storeId ?? null,
|
|
22
|
-
productId: item.catalogProductId,
|
|
23
|
-
variantId: item.catalogVariantId ?? null
|
|
24
|
-
}))
|
|
25
|
-
);
|
|
26
|
-
query.items.forEach((item, index) => {
|
|
27
|
-
const policy = resolved[index];
|
|
28
|
-
overrides[availabilityItemKey(item)] = {
|
|
29
|
-
isStockManaged: policy.isStockManaged.value,
|
|
30
|
-
isActive: policy.isActive.value,
|
|
31
|
-
preorderReleaseAt: policy.preorderReleaseAt.value ? policy.preorderReleaseAt.value.toISOString() : null,
|
|
32
|
-
policySourceId: policy.preorderReleaseAt.policySourceId ?? policy.isActive.policySourceId ?? policy.isStockManaged.policySourceId ?? null
|
|
33
|
-
};
|
|
34
|
-
});
|
|
35
|
-
return overrides;
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
export {
|
|
39
|
-
register
|
|
40
|
-
};
|
|
41
|
-
//# sourceMappingURL=di.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/modules/availability/di.ts"],
|
|
4
|
-
"sourcesContent": ["import { asValue } from 'awilix'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AppContainer } from '@open-mercato/shared/lib/di/container'\nimport { setCatalogOnlyPolicyLookup, availabilityItemKey } from '@open-mercato/shared/lib/availability'\nimport type { CatalogOnlyPolicyOverride } from '@open-mercato/shared/lib/availability'\nimport { AvailabilityPolicy } from './data/entities'\nimport { createPolicyResolutionService } from './lib/policyResolution'\n\nexport function register(container: AppContainer) {\n container.register({\n AvailabilityPolicy: asValue(AvailabilityPolicy),\n policyResolutionService: {\n resolve: (c) => createPolicyResolutionService(c),\n },\n })\n\n // Wires the shared catalog-only fallback's optional policy hook (decision 2,\n // PLAN.md \u00A7 Key design decisions) \u2014 closure captures the boot container, the\n // same technique `wms/di.ts` uses for its own provider registration.\n setCatalogOnlyPolicyLookup(async (query) => {\n const em = (container.resolve('em') as EntityManager).fork()\n const service = createPolicyResolutionService(container)\n const overrides: Record<string, CatalogOnlyPolicyOverride | null> = {}\n\n // Batched (R4): one resolveMany() call for the whole item set, not one\n // resolve() per item \u2014 the same discipline wms's provider follows.\n const resolved = await service.resolveMany(\n em,\n query.items.map((item) => ({\n tenantId: query.tenantId,\n organizationId: query.organizationId,\n storeId: query.storeId ?? null,\n productId: item.catalogProductId,\n variantId: item.catalogVariantId ?? null,\n })),\n )\n\n query.items.forEach((item, index) => {\n const policy = resolved[index]\n overrides[availabilityItemKey(item)] = {\n isStockManaged: policy.isStockManaged.value,\n isActive: policy.isActive.value,\n preorderReleaseAt: policy.preorderReleaseAt.value ? policy.preorderReleaseAt.value.toISOString() : null,\n policySourceId:\n policy.preorderReleaseAt.policySourceId\n ?? policy.isActive.policySourceId\n ?? policy.isStockManaged.policySourceId\n ?? null,\n }\n })\n\n return overrides\n })\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,eAAe;AAGxB,SAAS,4BAA4B,2BAA2B;AAEhE,SAAS,0BAA0B;AACnC,SAAS,qCAAqC;AAEvC,SAAS,SAAS,WAAyB;AAChD,YAAU,SAAS;AAAA,IACjB,oBAAoB,QAAQ,kBAAkB;AAAA,IAC9C,yBAAyB;AAAA,MACvB,SAAS,CAAC,MAAM,8BAA8B,CAAC;AAAA,IACjD;AAAA,EACF,CAAC;AAKD,6BAA2B,OAAO,UAAU;AAC1C,UAAM,KAAM,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC3D,UAAM,UAAU,8BAA8B,SAAS;AACvD,UAAM,YAA8D,CAAC;AAIrE,UAAM,WAAW,MAAM,QAAQ;AAAA,MAC7B;AAAA,MACA,MAAM,MAAM,IAAI,CAAC,UAAU;AAAA,QACzB,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM;AAAA,QACtB,SAAS,MAAM,WAAW;AAAA,QAC1B,WAAW,KAAK;AAAA,QAChB,WAAW,KAAK,oBAAoB;AAAA,MACtC,EAAE;AAAA,IACJ;AAEA,UAAM,MAAM,QAAQ,CAAC,MAAM,UAAU;AACnC,YAAM,SAAS,SAAS,KAAK;AAC7B,gBAAU,oBAAoB,IAAI,CAAC,IAAI;AAAA,QACrC,gBAAgB,OAAO,eAAe;AAAA,QACtC,UAAU,OAAO,SAAS;AAAA,QAC1B,mBAAmB,OAAO,kBAAkB,QAAQ,OAAO,kBAAkB,MAAM,YAAY,IAAI;AAAA,QACnG,gBACE,OAAO,kBAAkB,kBACtB,OAAO,SAAS,kBAChB,OAAO,eAAe,kBACtB;AAAA,MACP;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT,CAAC;AACH;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { createModuleEvents } from "@open-mercato/shared/modules/events";
|
|
2
|
-
const events = [
|
|
3
|
-
{ id: "availability.policy.created", label: "Availability Policy Created", entity: "policy", category: "crud" },
|
|
4
|
-
{ id: "availability.policy.updated", label: "Availability Policy Updated", entity: "policy", category: "crud" },
|
|
5
|
-
{ id: "availability.policy.deleted", label: "Availability Policy Deleted", entity: "policy", category: "crud" }
|
|
6
|
-
];
|
|
7
|
-
const eventsConfig = createModuleEvents({
|
|
8
|
-
moduleId: "availability",
|
|
9
|
-
events
|
|
10
|
-
});
|
|
11
|
-
const emitAvailabilityEvent = eventsConfig.emit;
|
|
12
|
-
var events_default = eventsConfig;
|
|
13
|
-
export {
|
|
14
|
-
events_default as default,
|
|
15
|
-
emitAvailabilityEvent,
|
|
16
|
-
eventsConfig
|
|
17
|
-
};
|
|
18
|
-
//# sourceMappingURL=events.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/modules/availability/events.ts"],
|
|
4
|
-
"sourcesContent": ["import { createModuleEvents } from '@open-mercato/shared/modules/events'\n\nconst events = [\n { id: 'availability.policy.created', label: 'Availability Policy Created', entity: 'policy', category: 'crud' },\n { id: 'availability.policy.updated', label: 'Availability Policy Updated', entity: 'policy', category: 'crud' },\n { id: 'availability.policy.deleted', label: 'Availability Policy Deleted', entity: 'policy', category: 'crud' },\n] as const\n\nexport const eventsConfig = createModuleEvents({\n moduleId: 'availability',\n events,\n})\n\nexport const emitAvailabilityEvent = eventsConfig.emit\nexport type AvailabilityEventId = typeof events[number]['id']\n\nexport default eventsConfig\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,0BAA0B;AAEnC,MAAM,SAAS;AAAA,EACb,EAAE,IAAI,+BAA+B,OAAO,+BAA+B,QAAQ,UAAU,UAAU,OAAO;AAAA,EAC9G,EAAE,IAAI,+BAA+B,OAAO,+BAA+B,QAAQ,UAAU,UAAU,OAAO;AAAA,EAC9G,EAAE,IAAI,+BAA+B,OAAO,+BAA+B,QAAQ,UAAU,UAAU,OAAO;AAChH;AAEO,MAAM,eAAe,mBAAmB;AAAA,EAC7C,UAAU;AAAA,EACV;AACF,CAAC;AAEM,MAAM,wBAAwB,aAAa;AAGlD,IAAO,iBAAQ;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
const metadata = {
|
|
2
|
-
name: "availability",
|
|
3
|
-
title: "Availability",
|
|
4
|
-
version: "0.1.0",
|
|
5
|
-
description: "Sell-policy layer (stock-managed, backorder, preorder, thresholds) on top of the shared availability contract.",
|
|
6
|
-
author: "Open Mercato Team",
|
|
7
|
-
license: "MIT",
|
|
8
|
-
ejectable: true
|
|
9
|
-
};
|
|
10
|
-
import { features } from "./acl.js";
|
|
11
|
-
export {
|
|
12
|
-
features,
|
|
13
|
-
metadata
|
|
14
|
-
};
|
|
15
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/modules/availability/index.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ModuleInfo } from '@open-mercato/shared/modules/registry'\n\nexport const metadata: ModuleInfo = {\n name: 'availability',\n title: 'Availability',\n version: '0.1.0',\n description: 'Sell-policy layer (stock-managed, backorder, preorder, thresholds) on top of the shared availability contract.',\n author: 'Open Mercato Team',\n license: 'MIT',\n ejectable: true,\n}\n\nexport { features } from './acl'\n"],
|
|
5
|
-
"mappings": "AAEO,MAAM,WAAuB;AAAA,EAClC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,SAAS;AAAA,EACT,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,WAAW;AACb;AAEA,SAAS,gBAAgB;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import { AvailabilityPolicy } from "../data/entities.js";
|
|
2
|
-
import { tryResolve } from "./tryResolve.js";
|
|
3
|
-
const POLICY_CHAIN_LEVELS = [
|
|
4
|
-
"variant_store",
|
|
5
|
-
"variant",
|
|
6
|
-
"product_store",
|
|
7
|
-
"product",
|
|
8
|
-
"store_default",
|
|
9
|
-
"module_default"
|
|
10
|
-
];
|
|
11
|
-
const NULLABLE_FIELDS = [
|
|
12
|
-
"backorderLeadTimeDays",
|
|
13
|
-
"preorderReleaseAt",
|
|
14
|
-
"lowStockThreshold",
|
|
15
|
-
"minOrderQuantity",
|
|
16
|
-
"maxOrderQuantity",
|
|
17
|
-
"quantityIncrement"
|
|
18
|
-
];
|
|
19
|
-
const BOOLEAN_FIELDS = ["isStockManaged", "allowBackorder", "hideWhenOutOfStock", "isActive"];
|
|
20
|
-
function candidateFilters(scope) {
|
|
21
|
-
const storeId = scope.storeId ?? null;
|
|
22
|
-
const variantId = scope.variantId ?? null;
|
|
23
|
-
const filters = [];
|
|
24
|
-
if (variantId) {
|
|
25
|
-
filters.push({ variantId, storeId });
|
|
26
|
-
filters.push({ variantId, storeId: null });
|
|
27
|
-
}
|
|
28
|
-
filters.push({ productId: scope.productId, variantId: null, storeId });
|
|
29
|
-
filters.push({ productId: scope.productId, variantId: null, storeId: null });
|
|
30
|
-
filters.push({ productId: null, variantId: null, storeId });
|
|
31
|
-
if (storeId) filters.push({ productId: null, variantId: null, storeId: null });
|
|
32
|
-
return filters;
|
|
33
|
-
}
|
|
34
|
-
function matchChainRows(scope, pool) {
|
|
35
|
-
const storeId = scope.storeId ?? null;
|
|
36
|
-
const variantId = scope.variantId ?? null;
|
|
37
|
-
const findMatch = (predicate) => pool.find(predicate) ?? null;
|
|
38
|
-
const rows = [];
|
|
39
|
-
if (variantId) {
|
|
40
|
-
rows.push(findMatch((r) => r.variantId === variantId && (r.storeId ?? null) === storeId));
|
|
41
|
-
rows.push(findMatch((r) => r.variantId === variantId && (r.storeId ?? null) === null));
|
|
42
|
-
}
|
|
43
|
-
rows.push(findMatch((r) => r.productId === scope.productId && !r.variantId && (r.storeId ?? null) === storeId));
|
|
44
|
-
rows.push(findMatch((r) => r.productId === scope.productId && !r.variantId && (r.storeId ?? null) === null));
|
|
45
|
-
rows.push(findMatch((r) => !r.productId && !r.variantId && (r.storeId ?? null) === storeId));
|
|
46
|
-
if (storeId) rows.push(findMatch((r) => !r.productId && !r.variantId && (r.storeId ?? null) === null));
|
|
47
|
-
return rows;
|
|
48
|
-
}
|
|
49
|
-
function resolveFromRows(rows, isStockManagedModuleDefault) {
|
|
50
|
-
const firstRow = rows.find((row) => row != null) ?? null;
|
|
51
|
-
const result = {};
|
|
52
|
-
for (const field of BOOLEAN_FIELDS) {
|
|
53
|
-
if (firstRow) {
|
|
54
|
-
;
|
|
55
|
-
result[field] = { value: firstRow[field], policySourceId: firstRow.id };
|
|
56
|
-
} else {
|
|
57
|
-
;
|
|
58
|
-
result[field] = { value: false, policySourceId: null };
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (!firstRow) result.isStockManaged = { value: isStockManagedModuleDefault, policySourceId: null };
|
|
62
|
-
if (!firstRow) result.isActive = { value: true, policySourceId: null };
|
|
63
|
-
for (const field of NULLABLE_FIELDS) {
|
|
64
|
-
let resolved = { value: null, policySourceId: null };
|
|
65
|
-
for (const row of rows) {
|
|
66
|
-
if (row && row[field] != null) {
|
|
67
|
-
resolved = { value: row[field], policySourceId: row.id };
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
;
|
|
72
|
-
result[field] = resolved;
|
|
73
|
-
}
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
76
|
-
async function resolveIsStockManagedModuleDefault(em, container, scope) {
|
|
77
|
-
const ProductInventoryProfile = tryResolve(container, "ProductInventoryProfile");
|
|
78
|
-
if (!ProductInventoryProfile) return false;
|
|
79
|
-
const baseFilter = {
|
|
80
|
-
tenantId: scope.tenantId,
|
|
81
|
-
organizationId: scope.organizationId,
|
|
82
|
-
catalogProductId: scope.productId,
|
|
83
|
-
deletedAt: null
|
|
84
|
-
};
|
|
85
|
-
if (scope.variantId) {
|
|
86
|
-
const variantRow = await em.findOne(ProductInventoryProfile, { ...baseFilter, catalogVariantId: scope.variantId });
|
|
87
|
-
if (variantRow) return true;
|
|
88
|
-
}
|
|
89
|
-
const productRow = await em.findOne(ProductInventoryProfile, { ...baseFilter, catalogVariantId: null });
|
|
90
|
-
return !!productRow;
|
|
91
|
-
}
|
|
92
|
-
async function resolveIsStockManagedModuleDefaultMany(em, container, scopes) {
|
|
93
|
-
const ProductInventoryProfile = tryResolve(container, "ProductInventoryProfile");
|
|
94
|
-
const results = /* @__PURE__ */ new Map();
|
|
95
|
-
if (!ProductInventoryProfile || scopes.length === 0) return results;
|
|
96
|
-
const first = scopes[0];
|
|
97
|
-
const productIds = Array.from(new Set(scopes.map((s) => s.productId)));
|
|
98
|
-
const rows = await em.find(ProductInventoryProfile, {
|
|
99
|
-
tenantId: first.tenantId,
|
|
100
|
-
organizationId: first.organizationId,
|
|
101
|
-
deletedAt: null,
|
|
102
|
-
catalogProductId: { $in: productIds }
|
|
103
|
-
});
|
|
104
|
-
const exists = new Set(rows.map((row) => `${row.catalogProductId}:${row.catalogVariantId ?? ""}`));
|
|
105
|
-
for (const scope of scopes) {
|
|
106
|
-
const variantHit = scope.variantId ? exists.has(`${scope.productId}:${scope.variantId}`) : false;
|
|
107
|
-
const productHit = exists.has(`${scope.productId}:`);
|
|
108
|
-
results.set(scopeKey(scope), variantHit || productHit);
|
|
109
|
-
}
|
|
110
|
-
return results;
|
|
111
|
-
}
|
|
112
|
-
function scopeKey(scope) {
|
|
113
|
-
return `${scope.productId}:${scope.variantId ?? ""}:${scope.storeId ?? ""}`;
|
|
114
|
-
}
|
|
115
|
-
function createPolicyResolutionService(container) {
|
|
116
|
-
async function resolveMany(em, scopes) {
|
|
117
|
-
if (scopes.length === 0) return [];
|
|
118
|
-
const first = scopes[0];
|
|
119
|
-
const seen = /* @__PURE__ */ new Set();
|
|
120
|
-
const orFilters = [];
|
|
121
|
-
for (const scope of scopes) {
|
|
122
|
-
for (const filter of candidateFilters(scope)) {
|
|
123
|
-
const key = JSON.stringify(filter);
|
|
124
|
-
if (seen.has(key)) continue;
|
|
125
|
-
seen.add(key);
|
|
126
|
-
orFilters.push(filter);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
const pool = orFilters.length ? await em.find(AvailabilityPolicy, {
|
|
130
|
-
tenantId: first.tenantId,
|
|
131
|
-
organizationId: first.organizationId,
|
|
132
|
-
deletedAt: null,
|
|
133
|
-
$or: orFilters
|
|
134
|
-
}) : [];
|
|
135
|
-
const rowsByScope = scopes.map((scope) => matchChainRows(scope, pool));
|
|
136
|
-
const needsModuleDefault = scopes.filter((scope, index) => !rowsByScope[index].some((row) => row != null));
|
|
137
|
-
const moduleDefaults = await resolveIsStockManagedModuleDefaultMany(em, container, needsModuleDefault);
|
|
138
|
-
return scopes.map(
|
|
139
|
-
(scope, index) => resolveFromRows(rowsByScope[index], moduleDefaults.get(scopeKey(scope)) ?? false)
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
return {
|
|
143
|
-
async resolve(em, scope) {
|
|
144
|
-
const [result] = await resolveMany(em, [scope]);
|
|
145
|
-
return result;
|
|
146
|
-
},
|
|
147
|
-
resolveMany
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
export {
|
|
151
|
-
POLICY_CHAIN_LEVELS,
|
|
152
|
-
createPolicyResolutionService,
|
|
153
|
-
resolveIsStockManagedModuleDefault
|
|
154
|
-
};
|
|
155
|
-
//# sourceMappingURL=policyResolution.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../../src/modules/availability/lib/policyResolution.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * `AvailabilityPolicy` resolution chain \u2014 six levels, most specific wins.\n *\n * @see .ai/specs/2026-08-14-availability-contract.md \u00A75.2\n *\n * Resolution design (documented here because \u00A75.2 does not fully spell it\n * out \u2014 see PLAN.md \u00A7 Key design decisions):\n *\n * - Boolean columns (`isStockManaged`, `allowBackorder`, `hideWhenOutOfStock`,\n * `isActive`) are NOT NULL, so a matched row's own value is always\n * concrete \u2014 there is no \"unset\" to fall through. The single most specific\n * EXISTING row decides every boolean field.\n * - Nullable columns (`backorderLeadTimeDays`, `preorderReleaseAt`,\n * `lowStockThreshold`, `minOrderQuantity`, `maxOrderQuantity`,\n * `quantityIncrement`) genuinely cascade: a matched row that leaves the\n * field `null` defers to the next-less-specific row, per US-A2's \"which\n * level currently decides each field\" requirement.\n *\n * `resolveMany()` exists so `wms`'s batched `check()` (\u00A74.2/R4 \u2014 one policy\n * lookup regardless of item count) can soft-resolve this service and still\n * issue exactly one query for the whole batch, rather than one per item.\n * `resolve()` is `resolveMany([scope])[0]`, so single-item callers (the admin\n * check tool, the resolve-preview endpoint) are unaffected.\n */\n\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { AvailabilityPolicy } from '../data/entities'\nimport { tryResolve } from './tryResolve'\n\nexport type PolicyResolutionScope = {\n tenantId: string\n organizationId: string\n storeId?: string | null\n productId: string\n variantId?: string | null\n}\n\nexport type ResolvedField<T> = {\n value: T\n /** The `AvailabilityPolicy` row id that decided, or `null` for the module default. */\n policySourceId: string | null\n}\n\nexport type ResolvedAvailabilityPolicy = {\n isStockManaged: ResolvedField<boolean>\n allowBackorder: ResolvedField<boolean>\n backorderLeadTimeDays: ResolvedField<number | null>\n preorderReleaseAt: ResolvedField<Date | null>\n lowStockThreshold: ResolvedField<number | null>\n minOrderQuantity: ResolvedField<number | null>\n maxOrderQuantity: ResolvedField<number | null>\n quantityIncrement: ResolvedField<number | null>\n hideWhenOutOfStock: ResolvedField<boolean>\n isActive: ResolvedField<boolean>\n}\n\n/** The six chain levels, most specific first. `null` = \"no row matched this level\". */\nexport type PolicyChainLevel =\n | 'variant_store'\n | 'variant'\n | 'product_store'\n | 'product'\n | 'store_default'\n | 'module_default'\n\nexport const POLICY_CHAIN_LEVELS: PolicyChainLevel[] = [\n 'variant_store',\n 'variant',\n 'product_store',\n 'product',\n 'store_default',\n 'module_default',\n]\n\nconst NULLABLE_FIELDS = [\n 'backorderLeadTimeDays',\n 'preorderReleaseAt',\n 'lowStockThreshold',\n 'minOrderQuantity',\n 'maxOrderQuantity',\n 'quantityIncrement',\n] as const\n\nconst BOOLEAN_FIELDS = ['isStockManaged', 'allowBackorder', 'hideWhenOutOfStock', 'isActive'] as const\n\nfunction candidateFilters(scope: PolicyResolutionScope): Array<Record<string, unknown>> {\n const storeId = scope.storeId ?? null\n const variantId = scope.variantId ?? null\n const filters: Array<Record<string, unknown>> = []\n if (variantId) {\n filters.push({ variantId, storeId })\n filters.push({ variantId, storeId: null })\n }\n filters.push({ productId: scope.productId, variantId: null, storeId })\n filters.push({ productId: scope.productId, variantId: null, storeId: null })\n filters.push({ productId: null, variantId: null, storeId })\n if (storeId) filters.push({ productId: null, variantId: null, storeId: null })\n return filters\n}\n\n/** Picks the (up to) five candidate rows for one scope out of a shared, already-loaded candidate pool. */\nfunction matchChainRows(scope: PolicyResolutionScope, pool: AvailabilityPolicy[]): Array<AvailabilityPolicy | null> {\n const storeId = scope.storeId ?? null\n const variantId = scope.variantId ?? null\n const findMatch = (predicate: (row: AvailabilityPolicy) => boolean) => pool.find(predicate) ?? null\n\n const rows: Array<AvailabilityPolicy | null> = []\n if (variantId) {\n rows.push(findMatch((r) => r.variantId === variantId && (r.storeId ?? null) === storeId))\n rows.push(findMatch((r) => r.variantId === variantId && (r.storeId ?? null) === null))\n }\n rows.push(findMatch((r) => r.productId === scope.productId && !r.variantId && (r.storeId ?? null) === storeId))\n rows.push(findMatch((r) => r.productId === scope.productId && !r.variantId && (r.storeId ?? null) === null))\n rows.push(findMatch((r) => !r.productId && !r.variantId && (r.storeId ?? null) === storeId))\n // The store-default level cascades store-specific \u2192 all-stores, same as\n // the variant/product levels above it (\"store_id null = applies to all\n // stores in the organization\", \u00A75.1) \u2014 otherwise an org-wide default row\n // is invisible to any query that names a concrete store.\n if (storeId) rows.push(findMatch((r) => !r.productId && !r.variantId && (r.storeId ?? null) === null))\n return rows\n}\n\nfunction resolveFromRows(\n rows: Array<AvailabilityPolicy | null>,\n isStockManagedModuleDefault: boolean,\n): ResolvedAvailabilityPolicy {\n const firstRow = rows.find((row): row is AvailabilityPolicy => row != null) ?? null\n const result = {} as ResolvedAvailabilityPolicy\n\n for (const field of BOOLEAN_FIELDS) {\n if (firstRow) {\n ;(result as any)[field] = { value: (firstRow as any)[field], policySourceId: firstRow.id }\n } else {\n ;(result as any)[field] = { value: false, policySourceId: null }\n }\n }\n\n // is_stock_managed's module default is dynamic (wms + profile existence), not a flat `false`.\n if (!firstRow) result.isStockManaged = { value: isStockManagedModuleDefault, policySourceId: null }\n // is_active's module default is \"active\" (nothing to deactivate).\n if (!firstRow) result.isActive = { value: true, policySourceId: null }\n\n for (const field of NULLABLE_FIELDS) {\n let resolved: ResolvedField<unknown> = { value: null, policySourceId: null }\n for (const row of rows) {\n if (row && (row as any)[field] != null) {\n resolved = { value: (row as any)[field], policySourceId: row.id }\n break\n }\n }\n ;(result as any)[field] = resolved\n }\n\n return result\n}\n\n/** `is_stock_managed`'s module default: true when `wms` is enabled and a `ProductInventoryProfile` exists for the item. */\nexport async function resolveIsStockManagedModuleDefault(\n em: EntityManager,\n container: { resolve: <T = unknown>(name: string) => T },\n scope: { tenantId: string; organizationId: string; productId: string; variantId?: string | null },\n): Promise<boolean> {\n const ProductInventoryProfile = tryResolve<new () => unknown>(container, 'ProductInventoryProfile')\n if (!ProductInventoryProfile) return false\n\n const baseFilter = {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n catalogProductId: scope.productId,\n deletedAt: null,\n }\n\n if (scope.variantId) {\n const variantRow = await em.findOne(ProductInventoryProfile as any, { ...baseFilter, catalogVariantId: scope.variantId })\n if (variantRow) return true\n }\n\n const productRow = await em.findOne(ProductInventoryProfile as any, { ...baseFilter, catalogVariantId: null })\n return !!productRow\n}\n\n/** Batched module-default check for `resolveMany` \u2014 one query for every scope that might need it. */\nasync function resolveIsStockManagedModuleDefaultMany(\n em: EntityManager,\n container: { resolve: <T = unknown>(name: string) => T },\n scopes: PolicyResolutionScope[],\n): Promise<Map<string, boolean>> {\n const ProductInventoryProfile = tryResolve<new () => unknown>(container, 'ProductInventoryProfile')\n const results = new Map<string, boolean>()\n if (!ProductInventoryProfile || scopes.length === 0) return results\n\n const first = scopes[0]\n const productIds = Array.from(new Set(scopes.map((s) => s.productId)))\n const rows = (await em.find(ProductInventoryProfile as any, {\n tenantId: first.tenantId,\n organizationId: first.organizationId,\n deletedAt: null,\n catalogProductId: { $in: productIds },\n } as any)) as Array<{ catalogProductId: string; catalogVariantId: string | null }>\n\n const exists = new Set(rows.map((row) => `${row.catalogProductId}:${row.catalogVariantId ?? ''}`))\n for (const scope of scopes) {\n const variantHit = scope.variantId ? exists.has(`${scope.productId}:${scope.variantId}`) : false\n const productHit = exists.has(`${scope.productId}:`)\n results.set(scopeKey(scope), variantHit || productHit)\n }\n return results\n}\n\nfunction scopeKey(scope: PolicyResolutionScope): string {\n return `${scope.productId}:${scope.variantId ?? ''}:${scope.storeId ?? ''}`\n}\n\nexport interface PolicyResolutionService {\n resolve(em: EntityManager, scope: PolicyResolutionScope): Promise<ResolvedAvailabilityPolicy>\n resolveMany(em: EntityManager, scopes: PolicyResolutionScope[]): Promise<ResolvedAvailabilityPolicy[]>\n}\n\nexport function createPolicyResolutionService(container: {\n resolve: <T = unknown>(name: string) => T\n}): PolicyResolutionService {\n async function resolveMany(em: EntityManager, scopes: PolicyResolutionScope[]): Promise<ResolvedAvailabilityPolicy[]> {\n if (scopes.length === 0) return []\n\n const first = scopes[0]\n const seen = new Set<string>()\n const orFilters: Array<Record<string, unknown>> = []\n for (const scope of scopes) {\n for (const filter of candidateFilters(scope)) {\n const key = JSON.stringify(filter)\n if (seen.has(key)) continue\n seen.add(key)\n orFilters.push(filter)\n }\n }\n\n const pool = orFilters.length\n ? await em.find(AvailabilityPolicy, {\n tenantId: first.tenantId,\n organizationId: first.organizationId,\n deletedAt: null,\n $or: orFilters as any,\n })\n : []\n\n // Only scopes that resolve to no row at all need the dynamic is_stock_managed\n // module default \u2014 batch just those.\n const rowsByScope = scopes.map((scope) => matchChainRows(scope, pool))\n const needsModuleDefault = scopes.filter((scope, index) => !rowsByScope[index].some((row) => row != null))\n const moduleDefaults = await resolveIsStockManagedModuleDefaultMany(em, container, needsModuleDefault)\n\n return scopes.map((scope, index) =>\n resolveFromRows(rowsByScope[index], moduleDefaults.get(scopeKey(scope)) ?? false),\n )\n }\n\n return {\n async resolve(em, scope) {\n const [result] = await resolveMany(em, [scope])\n return result\n },\n resolveMany,\n }\n}\n"],
|
|
5
|
-
"mappings": "AA0BA,SAAS,0BAA0B;AACnC,SAAS,kBAAkB;AAsCpB,MAAM,sBAA0C;AAAA,EACrD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,kBAAkB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,iBAAiB,CAAC,kBAAkB,kBAAkB,sBAAsB,UAAU;AAE5F,SAAS,iBAAiB,OAA8D;AACtF,QAAM,UAAU,MAAM,WAAW;AACjC,QAAM,YAAY,MAAM,aAAa;AACrC,QAAM,UAA0C,CAAC;AACjD,MAAI,WAAW;AACb,YAAQ,KAAK,EAAE,WAAW,QAAQ,CAAC;AACnC,YAAQ,KAAK,EAAE,WAAW,SAAS,KAAK,CAAC;AAAA,EAC3C;AACA,UAAQ,KAAK,EAAE,WAAW,MAAM,WAAW,WAAW,MAAM,QAAQ,CAAC;AACrE,UAAQ,KAAK,EAAE,WAAW,MAAM,WAAW,WAAW,MAAM,SAAS,KAAK,CAAC;AAC3E,UAAQ,KAAK,EAAE,WAAW,MAAM,WAAW,MAAM,QAAQ,CAAC;AAC1D,MAAI,QAAS,SAAQ,KAAK,EAAE,WAAW,MAAM,WAAW,MAAM,SAAS,KAAK,CAAC;AAC7E,SAAO;AACT;AAGA,SAAS,eAAe,OAA8B,MAA8D;AAClH,QAAM,UAAU,MAAM,WAAW;AACjC,QAAM,YAAY,MAAM,aAAa;AACrC,QAAM,YAAY,CAAC,cAAoD,KAAK,KAAK,SAAS,KAAK;AAE/F,QAAM,OAAyC,CAAC;AAChD,MAAI,WAAW;AACb,SAAK,KAAK,UAAU,CAAC,MAAM,EAAE,cAAc,cAAc,EAAE,WAAW,UAAU,OAAO,CAAC;AACxF,SAAK,KAAK,UAAU,CAAC,MAAM,EAAE,cAAc,cAAc,EAAE,WAAW,UAAU,IAAI,CAAC;AAAA,EACvF;AACA,OAAK,KAAK,UAAU,CAAC,MAAM,EAAE,cAAc,MAAM,aAAa,CAAC,EAAE,cAAc,EAAE,WAAW,UAAU,OAAO,CAAC;AAC9G,OAAK,KAAK,UAAU,CAAC,MAAM,EAAE,cAAc,MAAM,aAAa,CAAC,EAAE,cAAc,EAAE,WAAW,UAAU,IAAI,CAAC;AAC3G,OAAK,KAAK,UAAU,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,EAAE,cAAc,EAAE,WAAW,UAAU,OAAO,CAAC;AAK3F,MAAI,QAAS,MAAK,KAAK,UAAU,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,EAAE,cAAc,EAAE,WAAW,UAAU,IAAI,CAAC;AACrG,SAAO;AACT;AAEA,SAAS,gBACP,MACA,6BAC4B;AAC5B,QAAM,WAAW,KAAK,KAAK,CAAC,QAAmC,OAAO,IAAI,KAAK;AAC/E,QAAM,SAAS,CAAC;AAEhB,aAAW,SAAS,gBAAgB;AAClC,QAAI,UAAU;AACZ;AAAC,MAAC,OAAe,KAAK,IAAI,EAAE,OAAQ,SAAiB,KAAK,GAAG,gBAAgB,SAAS,GAAG;AAAA,IAC3F,OAAO;AACL;AAAC,MAAC,OAAe,KAAK,IAAI,EAAE,OAAO,OAAO,gBAAgB,KAAK;AAAA,IACjE;AAAA,EACF;AAGA,MAAI,CAAC,SAAU,QAAO,iBAAiB,EAAE,OAAO,6BAA6B,gBAAgB,KAAK;AAElG,MAAI,CAAC,SAAU,QAAO,WAAW,EAAE,OAAO,MAAM,gBAAgB,KAAK;AAErE,aAAW,SAAS,iBAAiB;AACnC,QAAI,WAAmC,EAAE,OAAO,MAAM,gBAAgB,KAAK;AAC3E,eAAW,OAAO,MAAM;AACtB,UAAI,OAAQ,IAAY,KAAK,KAAK,MAAM;AACtC,mBAAW,EAAE,OAAQ,IAAY,KAAK,GAAG,gBAAgB,IAAI,GAAG;AAChE;AAAA,MACF;AAAA,IACF;AACA;AAAC,IAAC,OAAe,KAAK,IAAI;AAAA,EAC5B;AAEA,SAAO;AACT;AAGA,eAAsB,mCACpB,IACA,WACA,OACkB;AAClB,QAAM,0BAA0B,WAA8B,WAAW,yBAAyB;AAClG,MAAI,CAAC,wBAAyB,QAAO;AAErC,QAAM,aAAa;AAAA,IACjB,UAAU,MAAM;AAAA,IAChB,gBAAgB,MAAM;AAAA,IACtB,kBAAkB,MAAM;AAAA,IACxB,WAAW;AAAA,EACb;AAEA,MAAI,MAAM,WAAW;AACnB,UAAM,aAAa,MAAM,GAAG,QAAQ,yBAAgC,EAAE,GAAG,YAAY,kBAAkB,MAAM,UAAU,CAAC;AACxH,QAAI,WAAY,QAAO;AAAA,EACzB;AAEA,QAAM,aAAa,MAAM,GAAG,QAAQ,yBAAgC,EAAE,GAAG,YAAY,kBAAkB,KAAK,CAAC;AAC7G,SAAO,CAAC,CAAC;AACX;AAGA,eAAe,uCACb,IACA,WACA,QAC+B;AAC/B,QAAM,0BAA0B,WAA8B,WAAW,yBAAyB;AAClG,QAAM,UAAU,oBAAI,IAAqB;AACzC,MAAI,CAAC,2BAA2B,OAAO,WAAW,EAAG,QAAO;AAE5D,QAAM,QAAQ,OAAO,CAAC;AACtB,QAAM,aAAa,MAAM,KAAK,IAAI,IAAI,OAAO,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AACrE,QAAM,OAAQ,MAAM,GAAG,KAAK,yBAAgC;AAAA,IAC1D,UAAU,MAAM;AAAA,IAChB,gBAAgB,MAAM;AAAA,IACtB,WAAW;AAAA,IACX,kBAAkB,EAAE,KAAK,WAAW;AAAA,EACtC,CAAQ;AAER,QAAM,SAAS,IAAI,IAAI,KAAK,IAAI,CAAC,QAAQ,GAAG,IAAI,gBAAgB,IAAI,IAAI,oBAAoB,EAAE,EAAE,CAAC;AACjG,aAAW,SAAS,QAAQ;AAC1B,UAAM,aAAa,MAAM,YAAY,OAAO,IAAI,GAAG,MAAM,SAAS,IAAI,MAAM,SAAS,EAAE,IAAI;AAC3F,UAAM,aAAa,OAAO,IAAI,GAAG,MAAM,SAAS,GAAG;AACnD,YAAQ,IAAI,SAAS,KAAK,GAAG,cAAc,UAAU;AAAA,EACvD;AACA,SAAO;AACT;AAEA,SAAS,SAAS,OAAsC;AACtD,SAAO,GAAG,MAAM,SAAS,IAAI,MAAM,aAAa,EAAE,IAAI,MAAM,WAAW,EAAE;AAC3E;AAOO,SAAS,8BAA8B,WAElB;AAC1B,iBAAe,YAAY,IAAmB,QAAwE;AACpH,QAAI,OAAO,WAAW,EAAG,QAAO,CAAC;AAEjC,UAAM,QAAQ,OAAO,CAAC;AACtB,UAAM,OAAO,oBAAI,IAAY;AAC7B,UAAM,YAA4C,CAAC;AACnD,eAAW,SAAS,QAAQ;AAC1B,iBAAW,UAAU,iBAAiB,KAAK,GAAG;AAC5C,cAAM,MAAM,KAAK,UAAU,MAAM;AACjC,YAAI,KAAK,IAAI,GAAG,EAAG;AACnB,aAAK,IAAI,GAAG;AACZ,kBAAU,KAAK,MAAM;AAAA,MACvB;AAAA,IACF;AAEA,UAAM,OAAO,UAAU,SACnB,MAAM,GAAG,KAAK,oBAAoB;AAAA,MAChC,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,WAAW;AAAA,MACX,KAAK;AAAA,IACP,CAAC,IACD,CAAC;AAIL,UAAM,cAAc,OAAO,IAAI,CAAC,UAAU,eAAe,OAAO,IAAI,CAAC;AACrE,UAAM,qBAAqB,OAAO,OAAO,CAAC,OAAO,UAAU,CAAC,YAAY,KAAK,EAAE,KAAK,CAAC,QAAQ,OAAO,IAAI,CAAC;AACzG,UAAM,iBAAiB,MAAM,uCAAuC,IAAI,WAAW,kBAAkB;AAErG,WAAO,OAAO;AAAA,MAAI,CAAC,OAAO,UACxB,gBAAgB,YAAY,KAAK,GAAG,eAAe,IAAI,SAAS,KAAK,CAAC,KAAK,KAAK;AAAA,IAClF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,QAAQ,IAAI,OAAO;AACvB,YAAM,CAAC,MAAM,IAAI,MAAM,YAAY,IAAI,CAAC,KAAK,CAAC;AAC9C,aAAO;AAAA,IACT;AAAA,IACA;AAAA,EACF;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../../src/modules/availability/lib/tryResolve.ts"],
|
|
4
|
-
"sourcesContent": ["/** Per-module local soft-resolve helper \u2014 see `packages/core/AGENTS.md` \u00A7 Cross-Module Coupling. */\nexport function tryResolve<T>(resolver: { resolve: <R = unknown>(name: string) => R }, name: string): T | undefined {\n try {\n return resolver.resolve<T>(name)\n } catch {\n return undefined\n }\n}\n"],
|
|
5
|
-
"mappings": "AACO,SAAS,WAAc,UAAyD,MAA6B;AAClH,MAAI;AACF,WAAO,SAAS,QAAW,IAAI;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { Migration } from "@mikro-orm/migrations";
|
|
2
|
-
class Migration20260922095844_availability extends Migration {
|
|
3
|
-
constructor() {
|
|
4
|
-
super(...arguments);
|
|
5
|
-
this.name = "Migration20260922095844";
|
|
6
|
-
}
|
|
7
|
-
up() {
|
|
8
|
-
this.addSql(`create table "availability_policies" ("id" uuid not null default gen_random_uuid(), "organization_id" uuid not null, "tenant_id" uuid not null, "store_id" uuid null, "product_id" uuid null, "variant_id" uuid null, "is_stock_managed" boolean not null default false, "allow_backorder" boolean not null default false, "backorder_lead_time_days" int null, "preorder_release_at" timestamptz null, "low_stock_threshold" int null, "min_order_quantity" int null, "max_order_quantity" int null, "quantity_increment" int null, "hide_when_out_of_stock" boolean not null default false, "is_active" boolean not null default true, "created_at" timestamptz not null, "updated_at" timestamptz not null, "deleted_at" timestamptz null, primary key ("id"));`);
|
|
9
|
-
this.addSql(`create unique index "availability_policies_scope_target_unique" on "availability_policies" ("tenant_id", "organization_id", "store_id", "product_id", "variant_id") nulls not distinct where deleted_at is null;`);
|
|
10
|
-
this.addSql(`create index "availability_policies_scope_idx" on "availability_policies" ("tenant_id", "organization_id");`);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
export {
|
|
14
|
-
Migration20260922095844_availability
|
|
15
|
-
};
|
|
16
|
-
//# sourceMappingURL=Migration20260922095844_availability.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../../src/modules/availability/migrations/Migration20260922095844_availability.ts"],
|
|
4
|
-
"sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260922095844_availability extends Migration {\n\n override name = 'Migration20260922095844';\n\n override up(): void | Promise<void> {\n this.addSql(`create table \"availability_policies\" (\"id\" uuid not null default gen_random_uuid(), \"organization_id\" uuid not null, \"tenant_id\" uuid not null, \"store_id\" uuid null, \"product_id\" uuid null, \"variant_id\" uuid null, \"is_stock_managed\" boolean not null default false, \"allow_backorder\" boolean not null default false, \"backorder_lead_time_days\" int null, \"preorder_release_at\" timestamptz null, \"low_stock_threshold\" int null, \"min_order_quantity\" int null, \"max_order_quantity\" int null, \"quantity_increment\" int null, \"hide_when_out_of_stock\" boolean not null default false, \"is_active\" boolean not null default true, \"created_at\" timestamptz not null, \"updated_at\" timestamptz not null, \"deleted_at\" timestamptz null, primary key (\"id\"));`);\n this.addSql(`create unique index \"availability_policies_scope_target_unique\" on \"availability_policies\" (\"tenant_id\", \"organization_id\", \"store_id\", \"product_id\", \"variant_id\") nulls not distinct where deleted_at is null;`);\n this.addSql(`create index \"availability_policies_scope_idx\" on \"availability_policies\" (\"tenant_id\", \"organization_id\");`);\n }\n\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,6CAA6C,UAAU;AAAA,EAA7D;AAAA;AAEL,SAAS,OAAO;AAAA;AAAA,EAEP,KAA2B;AAClC,SAAK,OAAO,ouBAAouB;AAChvB,SAAK,OAAO,kNAAkN;AAC9N,SAAK,OAAO,6GAA6G;AAAA,EAC3H;AAEF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/modules/availability/setup.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n admin: ['availability.*'],\n employee: ['availability.policies.view'],\n },\n}\n\nexport default setup\n"],
|
|
5
|
-
"mappings": "AAEO,MAAM,QAA2B;AAAA,EACtC,qBAAqB;AAAA,IACnB,OAAO,CAAC,gBAAgB;AAAA,IACxB,UAAU,CAAC,4BAA4B;AAAA,EACzC;AACF;AAEA,IAAO,gBAAQ;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { runWithCacheTenant } from "@open-mercato/cache";
|
|
2
|
-
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
3
|
-
import { availabilityItemKey } from "@open-mercato/shared/lib/availability";
|
|
4
|
-
import { computeAvailability } from "./availabilityCalculation.js";
|
|
5
|
-
const WMS_AVAILABILITY_CACHE_TAG = "wms:availability";
|
|
6
|
-
const WMS_AVAILABILITY_CACHE_TTL_MS = 6e4;
|
|
7
|
-
const logger = createLogger("wms").child({ component: "availability-cache" });
|
|
8
|
-
function resolveCache(container) {
|
|
9
|
-
if (!container?.resolve) return null;
|
|
10
|
-
for (const name of ["cache", "cacheService"]) {
|
|
11
|
-
try {
|
|
12
|
-
const candidate = container.resolve(name);
|
|
13
|
-
if (candidate && typeof candidate.deleteByTags === "function") return candidate;
|
|
14
|
-
} catch {
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
function cacheKeyForItem(query, item) {
|
|
20
|
-
return [
|
|
21
|
-
"wms:availability",
|
|
22
|
-
query.organizationId,
|
|
23
|
-
query.storeId ?? "",
|
|
24
|
-
item.catalogProductId,
|
|
25
|
-
item.catalogVariantId ?? "",
|
|
26
|
-
item.quantity
|
|
27
|
-
].join(":");
|
|
28
|
-
}
|
|
29
|
-
async function computeAvailabilityCached(em, container, query) {
|
|
30
|
-
const cache = resolveCache(container);
|
|
31
|
-
if (!cache || query.bypassCache) {
|
|
32
|
-
return computeAvailability(em, container, query);
|
|
33
|
-
}
|
|
34
|
-
return runWithCacheTenant(query.tenantId, async () => {
|
|
35
|
-
const byItem = {};
|
|
36
|
-
const missed = [];
|
|
37
|
-
for (const item of query.items) {
|
|
38
|
-
const key = availabilityItemKey(item);
|
|
39
|
-
try {
|
|
40
|
-
const cached = await cache.get(cacheKeyForItem(query, item));
|
|
41
|
-
if (cached) {
|
|
42
|
-
byItem[key] = { ...cached, isAuthoritative: false };
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
} catch (err) {
|
|
46
|
-
logger.warn("Availability cache read failed; computing live", { key, err });
|
|
47
|
-
}
|
|
48
|
-
missed.push(item);
|
|
49
|
-
}
|
|
50
|
-
if (missed.length > 0) {
|
|
51
|
-
const live = await computeAvailability(em, container, { ...query, items: missed });
|
|
52
|
-
for (const item of missed) {
|
|
53
|
-
const key = availabilityItemKey(item);
|
|
54
|
-
const result = live.byItem[key];
|
|
55
|
-
byItem[key] = result;
|
|
56
|
-
try {
|
|
57
|
-
await cache.set(cacheKeyForItem(query, item), result, {
|
|
58
|
-
ttl: WMS_AVAILABILITY_CACHE_TTL_MS,
|
|
59
|
-
tags: [WMS_AVAILABILITY_CACHE_TAG]
|
|
60
|
-
});
|
|
61
|
-
} catch (err) {
|
|
62
|
-
logger.warn("Availability cache write failed", { key, err });
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return { byItem };
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
async function invalidateWmsAvailabilityCache(container, tenantId) {
|
|
70
|
-
if (!tenantId) return;
|
|
71
|
-
const cache = resolveCache(container);
|
|
72
|
-
if (!cache) return;
|
|
73
|
-
try {
|
|
74
|
-
await runWithCacheTenant(tenantId, () => cache.deleteByTags([WMS_AVAILABILITY_CACHE_TAG]));
|
|
75
|
-
} catch (err) {
|
|
76
|
-
logger.warn("Failed to invalidate wms availability cache", { tenantId, err });
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
export {
|
|
80
|
-
WMS_AVAILABILITY_CACHE_TAG,
|
|
81
|
-
WMS_AVAILABILITY_CACHE_TTL_MS,
|
|
82
|
-
computeAvailabilityCached,
|
|
83
|
-
invalidateWmsAvailabilityCache
|
|
84
|
-
};
|
|
85
|
-
//# sourceMappingURL=availabilityCache.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../../src/modules/wms/lib/availabilityCache.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Read-through cache for `wms`'s `AvailabilityProvider` \u2014 \u00A76 staleness\n * budget (60s TTL for a browse-time `check`, never cached for\n * `query.bypassCache`), tag-based invalidation on `wms` balance changes.\n *\n * Tag granularity is a deliberate deviation from \u00A76's literal\n * `availability:{tenantId}:{variantId}` naming: this module already has a\n * coarse, tenant-scoped tag for the same purpose\n * (`enricherCacheTags.ts`'s `WMS_INVENTORY_CACHE_TAG`), justified there as\n * \"collection tags over-invalidate slightly; a per-warehouse scheme the\n * write side could miss would under-invalidate, which is the failure that\n * actually shows wrong stock to a user.\" The same reasoning applies here, so\n * `WMS_AVAILABILITY_CACHE_TAG` follows that established precedent rather\n * than a literal per-variant tag. `runWithCacheTenant` already scopes both\n * keys and tags to the current tenant (`packages/cache/src/service.ts`), so\n * neither needs `tenantId` embedded manually.\n */\n\nimport { runWithCacheTenant } from '@open-mercato/cache'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { availabilityItemKey } from '@open-mercato/shared/lib/availability'\nimport type { AvailabilityItemResult, AvailabilityQuery, AvailabilityResult } from '@open-mercato/shared/lib/availability'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { computeAvailability } from './availabilityCalculation'\n\nexport const WMS_AVAILABILITY_CACHE_TAG = 'wms:availability'\n/** \u00A76 staleness budget: a browse-time `check` may be up to 60s stale. */\nexport const WMS_AVAILABILITY_CACHE_TTL_MS = 60_000\n\nconst logger = createLogger('wms').child({ component: 'availability-cache' })\n\ntype CacheService = {\n get(key: string, options?: { returnExpired?: boolean }): Promise<unknown>\n set(key: string, value: unknown, options?: { ttl?: number; tags?: string[] }): Promise<void>\n deleteByTags(tags: string[]): Promise<number>\n}\n\ntype Resolver = { resolve: <T = unknown>(name: string) => T }\n\nfunction resolveCache(container: Resolver | null | undefined): CacheService | null {\n if (!container?.resolve) return null\n for (const name of ['cache', 'cacheService']) {\n try {\n const candidate = container.resolve<CacheService>(name)\n if (candidate && typeof candidate.deleteByTags === 'function') return candidate\n } catch {\n // try the next registration name\n }\n }\n return null\n}\n\nfunction cacheKeyForItem(query: AvailabilityQuery, item: AvailabilityQuery['items'][number]): string {\n return [\n 'wms:availability',\n query.organizationId,\n query.storeId ?? '',\n item.catalogProductId,\n item.catalogVariantId ?? '',\n item.quantity,\n ].join(':')\n}\n\n/**\n * Wraps `computeAvailability` with the read-through cache. Falls back to a\n * live computation when no cache service is registered or `bypassCache` is\n * set \u2014 the same code path either way, just without the read/write.\n */\nexport async function computeAvailabilityCached(\n em: EntityManager,\n container: Resolver,\n query: AvailabilityQuery,\n): Promise<AvailabilityResult> {\n const cache = resolveCache(container)\n if (!cache || query.bypassCache) {\n return computeAvailability(em, container, query)\n }\n\n return runWithCacheTenant(query.tenantId, async () => {\n const byItem: AvailabilityResult['byItem'] = {}\n const missed: AvailabilityQuery['items'] = []\n\n for (const item of query.items) {\n const key = availabilityItemKey(item)\n try {\n const cached = await cache.get(cacheKeyForItem(query, item))\n if (cached) {\n byItem[key] = { ...(cached as AvailabilityItemResult), isAuthoritative: false }\n continue\n }\n } catch (err) {\n logger.warn('Availability cache read failed; computing live', { key, err })\n }\n missed.push(item)\n }\n\n if (missed.length > 0) {\n const live = await computeAvailability(em, container, { ...query, items: missed })\n for (const item of missed) {\n const key = availabilityItemKey(item)\n const result = live.byItem[key]\n byItem[key] = result\n try {\n await cache.set(cacheKeyForItem(query, item), result, {\n ttl: WMS_AVAILABILITY_CACHE_TTL_MS,\n tags: [WMS_AVAILABILITY_CACHE_TAG],\n })\n } catch (err) {\n logger.warn('Availability cache write failed', { key, err })\n }\n }\n }\n\n return { byItem }\n })\n}\n\n/**\n * Best-effort tag invalidation \u2014 mirrors\n * `invalidateInventoryEnricherCache.ts`. A failure is logged and swallowed:\n * invalidation must never fail the write that triggered it, and the TTL\n * bounds how long a missed drop can serve stale data.\n */\nexport async function invalidateWmsAvailabilityCache(\n container: Resolver | null | undefined,\n tenantId: string | null | undefined,\n): Promise<void> {\n if (!tenantId) return\n const cache = resolveCache(container)\n if (!cache) return\n try {\n await runWithCacheTenant(tenantId, () => cache.deleteByTags([WMS_AVAILABILITY_CACHE_TAG]))\n } catch (err) {\n logger.warn('Failed to invalidate wms availability cache', { tenantId, err })\n }\n}\n"],
|
|
5
|
-
"mappings": "AAkBA,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;AAC7B,SAAS,2BAA2B;AAGpC,SAAS,2BAA2B;AAE7B,MAAM,6BAA6B;AAEnC,MAAM,gCAAgC;AAE7C,MAAM,SAAS,aAAa,KAAK,EAAE,MAAM,EAAE,WAAW,qBAAqB,CAAC;AAU5E,SAAS,aAAa,WAA6D;AACjF,MAAI,CAAC,WAAW,QAAS,QAAO;AAChC,aAAW,QAAQ,CAAC,SAAS,cAAc,GAAG;AAC5C,QAAI;AACF,YAAM,YAAY,UAAU,QAAsB,IAAI;AACtD,UAAI,aAAa,OAAO,UAAU,iBAAiB,WAAY,QAAO;AAAA,IACxE,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,OAA0B,MAAkD;AACnG,SAAO;AAAA,IACL;AAAA,IACA,MAAM;AAAA,IACN,MAAM,WAAW;AAAA,IACjB,KAAK;AAAA,IACL,KAAK,oBAAoB;AAAA,IACzB,KAAK;AAAA,EACP,EAAE,KAAK,GAAG;AACZ;AAOA,eAAsB,0BACpB,IACA,WACA,OAC6B;AAC7B,QAAM,QAAQ,aAAa,SAAS;AACpC,MAAI,CAAC,SAAS,MAAM,aAAa;AAC/B,WAAO,oBAAoB,IAAI,WAAW,KAAK;AAAA,EACjD;AAEA,SAAO,mBAAmB,MAAM,UAAU,YAAY;AACpD,UAAM,SAAuC,CAAC;AAC9C,UAAM,SAAqC,CAAC;AAE5C,eAAW,QAAQ,MAAM,OAAO;AAC9B,YAAM,MAAM,oBAAoB,IAAI;AACpC,UAAI;AACF,cAAM,SAAS,MAAM,MAAM,IAAI,gBAAgB,OAAO,IAAI,CAAC;AAC3D,YAAI,QAAQ;AACV,iBAAO,GAAG,IAAI,EAAE,GAAI,QAAmC,iBAAiB,MAAM;AAC9E;AAAA,QACF;AAAA,MACF,SAAS,KAAK;AACZ,eAAO,KAAK,kDAAkD,EAAE,KAAK,IAAI,CAAC;AAAA,MAC5E;AACA,aAAO,KAAK,IAAI;AAAA,IAClB;AAEA,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,OAAO,MAAM,oBAAoB,IAAI,WAAW,EAAE,GAAG,OAAO,OAAO,OAAO,CAAC;AACjF,iBAAW,QAAQ,QAAQ;AACzB,cAAM,MAAM,oBAAoB,IAAI;AACpC,cAAM,SAAS,KAAK,OAAO,GAAG;AAC9B,eAAO,GAAG,IAAI;AACd,YAAI;AACF,gBAAM,MAAM,IAAI,gBAAgB,OAAO,IAAI,GAAG,QAAQ;AAAA,YACpD,KAAK;AAAA,YACL,MAAM,CAAC,0BAA0B;AAAA,UACnC,CAAC;AAAA,QACH,SAAS,KAAK;AACZ,iBAAO,KAAK,mCAAmC,EAAE,KAAK,IAAI,CAAC;AAAA,QAC7D;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,OAAO;AAAA,EAClB,CAAC;AACH;AAQA,eAAsB,+BACpB,WACA,UACe;AACf,MAAI,CAAC,SAAU;AACf,QAAM,QAAQ,aAAa,SAAS;AACpC,MAAI,CAAC,MAAO;AACZ,MAAI;AACF,UAAM,mBAAmB,UAAU,MAAM,MAAM,aAAa,CAAC,0BAA0B,CAAC,CAAC;AAAA,EAC3F,SAAS,KAAK;AACZ,WAAO,KAAK,+CAA+C,EAAE,UAAU,IAAI,CAAC;AAAA,EAC9E;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|