@open-mercato/shared 0.10.1-develop.7576.1.21d5b50435 → 0.10.1-develop.7577.1.477f2486e5

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 (125) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +4 -4
  3. package/dist/lib/auth/server.js +1 -1
  4. package/dist/lib/auth/server.js.map +2 -2
  5. package/dist/lib/bootstrap/dynamicLoader.js +46 -6
  6. package/dist/lib/bootstrap/dynamicLoader.js.map +2 -2
  7. package/dist/lib/bootstrap/factory.js +4 -3
  8. package/dist/lib/bootstrap/factory.js.map +2 -2
  9. package/dist/lib/bootstrap/next-subpath-resolve-hook-worker.js +6 -0
  10. package/dist/lib/bootstrap/next-subpath-resolve-hook-worker.js.map +7 -0
  11. package/dist/lib/bootstrap/next-subpath-resolve-hook.js +62 -0
  12. package/dist/lib/bootstrap/next-subpath-resolve-hook.js.map +7 -0
  13. package/dist/lib/catalog-visibility/canonicalizeEffectiveScope.js +33 -0
  14. package/dist/lib/catalog-visibility/canonicalizeEffectiveScope.js.map +7 -0
  15. package/dist/lib/catalog-visibility/hashEffectiveScope.js +9 -0
  16. package/dist/lib/catalog-visibility/hashEffectiveScope.js.map +7 -0
  17. package/dist/lib/catalog-visibility/index.js +4 -0
  18. package/dist/lib/catalog-visibility/index.js.map +2 -2
  19. package/dist/lib/commands/command-bus.js +45 -13
  20. package/dist/lib/commands/command-bus.js.map +2 -2
  21. package/dist/lib/commands/types.js.map +2 -2
  22. package/dist/lib/crud/exporters.js +1 -1
  23. package/dist/lib/crud/exporters.js.map +2 -2
  24. package/dist/lib/crud/ids.js +13 -1
  25. package/dist/lib/crud/ids.js.map +2 -2
  26. package/dist/lib/db/advisoryLock.js +172 -0
  27. package/dist/lib/db/advisoryLock.js.map +7 -0
  28. package/dist/lib/db/pg-errors.js +14 -0
  29. package/dist/lib/db/pg-errors.js.map +2 -2
  30. package/dist/lib/localization/index.js +9 -1
  31. package/dist/lib/localization/index.js.map +2 -2
  32. package/dist/lib/localization/translatable-fields.js +26 -0
  33. package/dist/lib/localization/translatable-fields.js.map +2 -2
  34. package/dist/lib/number.js +6 -0
  35. package/dist/lib/number.js.map +2 -2
  36. package/dist/lib/openapi/generator.js +84 -67
  37. package/dist/lib/openapi/generator.js.map +2 -2
  38. package/dist/lib/query/engine.js +45 -10
  39. package/dist/lib/query/engine.js.map +2 -2
  40. package/dist/lib/query/join-utils.js +1 -1
  41. package/dist/lib/query/join-utils.js.map +2 -2
  42. package/dist/lib/query/overlap.js +58 -0
  43. package/dist/lib/query/overlap.js.map +7 -0
  44. package/dist/lib/query/types.js.map +2 -2
  45. package/dist/lib/url.js +24 -0
  46. package/dist/lib/url.js.map +2 -2
  47. package/dist/lib/version.js +1 -1
  48. package/dist/lib/version.js.map +1 -1
  49. package/dist/lib/webhooks/body.js +7 -3
  50. package/dist/lib/webhooks/body.js.map +2 -2
  51. package/dist/lib/webhooks/index.js +2 -0
  52. package/dist/lib/webhooks/index.js.map +2 -2
  53. package/dist/modules/integrations/types.js.map +2 -2
  54. package/dist/modules/notifications/safeHref.js +20 -0
  55. package/dist/modules/notifications/safeHref.js.map +7 -0
  56. package/dist/modules/notifications/types.js +4 -0
  57. package/dist/modules/notifications/types.js.map +3 -3
  58. package/dist/modules/payment_gateways/types.js +14 -1
  59. package/dist/modules/payment_gateways/types.js.map +2 -2
  60. package/dist/modules/registry.js.map +2 -2
  61. package/dist/modules/search.js.map +2 -2
  62. package/dist/modules/widgets/injection-loader.js +14 -3
  63. package/dist/modules/widgets/injection-loader.js.map +2 -2
  64. package/package.json +9 -2
  65. package/src/lib/__tests__/number.test.ts +24 -1
  66. package/src/lib/__tests__/url.cross-site.test.ts +140 -0
  67. package/src/lib/auth/__tests__/server.test.ts +14 -0
  68. package/src/lib/auth/server.ts +1 -1
  69. package/src/lib/bootstrap/__tests__/dynamicLoader.appDi.test.ts +2 -2
  70. package/src/lib/bootstrap/__tests__/dynamicLoader.appModuleOverrides.test.ts +2 -2
  71. package/src/lib/bootstrap/__tests__/dynamicLoader.cacheRecovery.test.ts +2 -2
  72. package/src/lib/bootstrap/__tests__/dynamicLoader.commandInterceptors.test.ts +2 -2
  73. package/src/lib/bootstrap/__tests__/dynamicLoader.externalSubpath.test.ts +141 -0
  74. package/src/lib/bootstrap/__tests__/dynamicLoader.tsconfig.test.ts +2 -2
  75. package/src/lib/bootstrap/__tests__/factory.test.ts +18 -6
  76. package/src/lib/bootstrap/__tests__/next-subpath-resolve-hook.test.ts +174 -0
  77. package/src/lib/bootstrap/dynamicLoader.ts +63 -5
  78. package/src/lib/bootstrap/factory.ts +8 -3
  79. package/src/lib/bootstrap/next-subpath-resolve-hook-worker.ts +3 -0
  80. package/src/lib/bootstrap/next-subpath-resolve-hook.ts +130 -0
  81. package/src/lib/bootstrap/types.ts +5 -1
  82. package/src/lib/catalog-visibility/__tests__/hashEffectiveScope.test.ts +121 -0
  83. package/src/lib/catalog-visibility/canonicalizeEffectiveScope.ts +41 -0
  84. package/src/lib/catalog-visibility/hashEffectiveScope.ts +8 -0
  85. package/src/lib/catalog-visibility/index.ts +2 -0
  86. package/src/lib/commands/__tests__/command-bus.test.ts +100 -1
  87. package/src/lib/commands/command-bus.ts +42 -10
  88. package/src/lib/commands/command-interceptor.ts +4 -0
  89. package/src/lib/commands/types.ts +14 -0
  90. package/src/lib/crud/__tests__/exporters.test.ts +18 -0
  91. package/src/lib/crud/__tests__/ids.test.ts +34 -1
  92. package/src/lib/crud/exporters.ts +1 -1
  93. package/src/lib/crud/ids.ts +24 -0
  94. package/src/lib/db/__tests__/advisoryLock.integration.test.ts +343 -0
  95. package/src/lib/db/__tests__/advisoryLock.test.ts +564 -0
  96. package/src/lib/db/__tests__/pg-errors.test.ts +68 -1
  97. package/src/lib/db/advisoryLock.ts +271 -0
  98. package/src/lib/db/pg-errors.ts +35 -0
  99. package/src/lib/encryption/__tests__/tenantDataEncryptionService.test.ts +23 -0
  100. package/src/lib/localization/__tests__/translatable-fields.test.ts +17 -0
  101. package/src/lib/localization/index.ts +8 -1
  102. package/src/lib/localization/translatable-fields.ts +46 -0
  103. package/src/lib/number.ts +13 -0
  104. package/src/lib/openapi/__tests__/optional-path-params.test.ts +102 -0
  105. package/src/lib/openapi/generator.ts +106 -77
  106. package/src/lib/query/__tests__/empty-ids-filter.pg.test.ts +89 -0
  107. package/src/lib/query/__tests__/engine.empty-in-filter.test.ts +79 -0
  108. package/src/lib/query/__tests__/overlap.test.ts +219 -0
  109. package/src/lib/query/engine.ts +58 -8
  110. package/src/lib/query/join-utils.ts +1 -1
  111. package/src/lib/query/overlap.ts +106 -0
  112. package/src/lib/query/types.ts +13 -1
  113. package/src/lib/url.ts +30 -0
  114. package/src/lib/webhooks/__tests__/body.test.ts +48 -0
  115. package/src/lib/webhooks/body.ts +11 -4
  116. package/src/lib/webhooks/index.ts +1 -0
  117. package/src/modules/integrations/types.ts +6 -0
  118. package/src/modules/notifications/safeHref.ts +19 -0
  119. package/src/modules/notifications/types.ts +7 -0
  120. package/src/modules/payment_gateways/__tests__/types.test.ts +44 -0
  121. package/src/modules/payment_gateways/types.ts +52 -6
  122. package/src/modules/registry.ts +5 -0
  123. package/src/modules/search.ts +39 -0
  124. package/src/modules/widgets/__tests__/injection-loader.partitioned-registration.test.ts +78 -0
  125. package/src/modules/widgets/injection-loader.ts +33 -3
@@ -59,6 +59,19 @@ function normalizePath(path) {
59
59
  }).join("/");
60
60
  return { path: "/" + normalized, params };
61
61
  }
62
+ function expandOptionalPathParams(normalized) {
63
+ const required = normalized.params.filter((param) => !param.optional);
64
+ const optionalParam = normalized.params.find((param) => param.optional);
65
+ const fullVariant = {
66
+ path: normalized.path,
67
+ params: normalized.params.map((param) => ({ ...param, optional: false }))
68
+ };
69
+ if (!optionalParam) return [fullVariant];
70
+ const placeholder = `/{${optionalParam.name}}`;
71
+ if (!normalized.path.endsWith(placeholder)) return [fullVariant];
72
+ const basePath = normalized.path.slice(0, -placeholder.length) || "/";
73
+ return [fullVariant, { path: basePath, params: required, omittedParam: optionalParam.name }];
74
+ }
62
75
  function unwrap(schema) {
63
76
  if (!schema) {
64
77
  return { schema: void 0, optional: true, nullable: false };
@@ -564,14 +577,14 @@ function buildParameters(schema, location, pathParamNames) {
564
577
  const params = [];
565
578
  if (location === "path" && pathParamNames && pathParamNames.length) {
566
579
  const merged = mergePathParamSchemas(schema, pathParamNames);
567
- for (const { name, schema: paramSchema, optional } of merged) {
580
+ for (const { name, schema: paramSchema } of merged) {
568
581
  const jsonSchema = zodToJsonSchema(paramSchema);
569
582
  const example = generateExample(paramSchema);
570
583
  const description = extractZodDescription(paramSchema);
571
584
  params.push({
572
585
  name,
573
586
  in: "path",
574
- required: !optional,
587
+ required: true,
575
588
  schema: jsonSchema ?? { type: "string" },
576
589
  example,
577
590
  ...description ? { description } : {}
@@ -631,8 +644,7 @@ function mergePathParamSchemas(schema, params) {
631
644
  for (const param of params) {
632
645
  merged.push({
633
646
  name: param.name,
634
- schema: map[param.name],
635
- optional: !!param.optional
647
+ schema: map[param.name]
636
648
  });
637
649
  }
638
650
  return merged;
@@ -883,6 +895,7 @@ function buildOpenApiDocument(modules, options = {}) {
883
895
  ensureSecurityComponents(doc);
884
896
  const includeAccessControlMetadata = options.includeAccessControlMetadata !== false;
885
897
  const tags = /* @__PURE__ */ new Map();
898
+ const derivedOperations = /* @__PURE__ */ new Set();
886
899
  for (const moduleEntry of modules) {
887
900
  const defaultTag = moduleEntry.info?.title ?? toTitle(moduleEntry.id);
888
901
  if (defaultTag) tags.set(defaultTag, moduleEntry.info?.description);
@@ -890,71 +903,75 @@ function buildOpenApiDocument(modules, options = {}) {
890
903
  for (const api of apis) {
891
904
  const routeDoc = collectRouteDoc(api, moduleEntry.id);
892
905
  const moduleTag = routeDoc?.tag ?? defaultTag;
893
- const normalized = normalizePath(api.path ?? api.path ?? "");
894
- const pathKey = normalized.path;
895
- if (!doc.paths[pathKey]) doc.paths[pathKey] = {};
896
906
  const availableMethods = "handlers" in api ? HTTP_METHODS.filter((method) => typeof api.handlers?.[method] === "function") : [api.method];
897
- for (const method of availableMethods) {
898
- const methodLower = method.toLowerCase();
899
- const existing = doc.paths[pathKey][methodLower];
900
- if (existing) continue;
901
- const metadata = "handlers" in api ? api.metadata?.[method] : void 0;
902
- const methodDoc = routeDoc?.methods?.[method];
903
- const summary = methodDoc?.summary ?? routeDoc?.summary ?? `${method} ${pathKey}`;
904
- const baseDescription = methodDoc?.description ?? routeDoc?.description;
905
- const meta = metadata && typeof metadata === "object" ? metadata : void 0;
906
- const requireFeatures = Array.isArray(meta?.["requireFeatures"]) ? meta["requireFeatures"] : void 0;
907
- const requireRoles = Array.isArray(meta?.["requireRoles"]) ? meta["requireRoles"] : void 0;
908
- const requireAuth = meta?.["requireAuth"] === true;
909
- const descriptionParts = [];
910
- if (baseDescription) descriptionParts.push(baseDescription);
911
- if (includeAccessControlMetadata && Array.isArray(requireFeatures) && requireFeatures.length) {
912
- descriptionParts.push(`Requires features: ${requireFeatures.join(", ")}`);
913
- }
914
- if (includeAccessControlMetadata && Array.isArray(requireRoles) && requireRoles.length) {
915
- descriptionParts.push(`Requires roles: ${requireRoles.join(", ")}`);
907
+ for (const normalized of expandOptionalPathParams(normalizePath(api.path ?? ""))) {
908
+ const pathKey = normalized.path;
909
+ if (!doc.paths[pathKey]) doc.paths[pathKey] = {};
910
+ for (const method of availableMethods) {
911
+ const methodLower = method.toLowerCase();
912
+ const operationKey = `${methodLower} ${pathKey}`;
913
+ const existing = doc.paths[pathKey][methodLower];
914
+ if (existing && (normalized.omittedParam || !derivedOperations.has(operationKey))) continue;
915
+ if (normalized.omittedParam) derivedOperations.add(operationKey);
916
+ else derivedOperations.delete(operationKey);
917
+ const metadata = "handlers" in api ? api.metadata?.[method] : void 0;
918
+ const methodDoc = routeDoc?.methods?.[method];
919
+ const summary = methodDoc?.summary ?? routeDoc?.summary ?? `${method} ${pathKey}`;
920
+ const baseDescription = methodDoc?.description ?? routeDoc?.description;
921
+ const meta = metadata && typeof metadata === "object" ? metadata : void 0;
922
+ const requireFeatures = Array.isArray(meta?.["requireFeatures"]) ? meta["requireFeatures"] : void 0;
923
+ const requireRoles = Array.isArray(meta?.["requireRoles"]) ? meta["requireRoles"] : void 0;
924
+ const requireAuth = meta?.["requireAuth"] === true;
925
+ const descriptionParts = [];
926
+ if (baseDescription) descriptionParts.push(baseDescription);
927
+ if (includeAccessControlMetadata && Array.isArray(requireFeatures) && requireFeatures.length) {
928
+ descriptionParts.push(`Requires features: ${requireFeatures.join(", ")}`);
929
+ }
930
+ if (includeAccessControlMetadata && Array.isArray(requireRoles) && requireRoles.length) {
931
+ descriptionParts.push(`Requires roles: ${requireRoles.join(", ")}`);
932
+ }
933
+ const querySchema = methodDoc?.query;
934
+ const pathSchema = methodDoc?.pathParams ?? routeDoc?.pathParams;
935
+ const headerSchema = methodDoc?.headers;
936
+ const requestBody = methodDoc?.requestBody;
937
+ const examples = collectExamples(querySchema, requestBody?.schema, pathSchema, headerSchema, metadata);
938
+ const curlSample = buildCurlSample(
939
+ method,
940
+ pathKey,
941
+ normalized.params,
942
+ examples,
943
+ options.baseUrlForExamples ?? "https://api.open-mercato.local",
944
+ metadata,
945
+ requestBody
946
+ );
947
+ doc.paths[pathKey][methodLower] = {
948
+ operationId: methodDoc?.operationId ? normalized.omittedParam ? `${methodDoc.operationId}_without_${normalizeOperationIdSegment(normalized.omittedParam)}` : methodDoc.operationId : resolveOperationId(moduleEntry.id, pathKey, method),
949
+ summary,
950
+ description: descriptionParts.length ? descriptionParts.join("\n\n") : void 0,
951
+ tags: methodDoc?.tags ?? (moduleTag ? [moduleTag] : void 0),
952
+ deprecated: methodDoc?.deprecated,
953
+ externalDocs: methodDoc?.externalDocs,
954
+ parameters: [
955
+ ...buildParameters(pathSchema, "path", normalized.params),
956
+ ...buildParameters(querySchema, "query"),
957
+ ...buildParameters(headerSchema, "header")
958
+ ].filter(Boolean),
959
+ requestBody: buildRequestBody(requestBody),
960
+ responses: buildResponses(method, methodDoc?.responses, methodDoc?.errors, metadata),
961
+ security: buildSecurity(metadata, methodDoc, options.defaultSecurity),
962
+ "x-codeSamples": methodDoc?.codeSamples ?? [
963
+ {
964
+ lang: "curl",
965
+ label: "cURL",
966
+ source: curlSample
967
+ }
968
+ ],
969
+ ...includeAccessControlMetadata && Array.isArray(requireFeatures) && requireFeatures.length ? { "x-require-features": requireFeatures } : {},
970
+ ...includeAccessControlMetadata && Array.isArray(requireRoles) && requireRoles.length ? { "x-require-roles": requireRoles } : {},
971
+ ...requireAuth ? { "x-require-auth": true } : {},
972
+ ...methodDoc?.extensions ?? {}
973
+ };
916
974
  }
917
- const querySchema = methodDoc?.query;
918
- const pathSchema = methodDoc?.pathParams ?? routeDoc?.pathParams;
919
- const headerSchema = methodDoc?.headers;
920
- const requestBody = methodDoc?.requestBody;
921
- const examples = collectExamples(querySchema, requestBody?.schema, pathSchema, headerSchema, metadata);
922
- const curlSample = buildCurlSample(
923
- method,
924
- pathKey,
925
- normalized.params,
926
- examples,
927
- options.baseUrlForExamples ?? "https://api.open-mercato.local",
928
- metadata,
929
- requestBody
930
- );
931
- doc.paths[pathKey][methodLower] = {
932
- operationId: methodDoc?.operationId ?? resolveOperationId(moduleEntry.id, pathKey, method),
933
- summary,
934
- description: descriptionParts.length ? descriptionParts.join("\n\n") : void 0,
935
- tags: methodDoc?.tags ?? (moduleTag ? [moduleTag] : void 0),
936
- deprecated: methodDoc?.deprecated,
937
- externalDocs: methodDoc?.externalDocs,
938
- parameters: [
939
- ...buildParameters(pathSchema, "path", normalized.params),
940
- ...buildParameters(querySchema, "query"),
941
- ...buildParameters(headerSchema, "header")
942
- ].filter(Boolean),
943
- requestBody: buildRequestBody(requestBody),
944
- responses: buildResponses(method, methodDoc?.responses, methodDoc?.errors, metadata),
945
- security: buildSecurity(metadata, methodDoc, options.defaultSecurity),
946
- "x-codeSamples": methodDoc?.codeSamples ?? [
947
- {
948
- lang: "curl",
949
- label: "cURL",
950
- source: curlSample
951
- }
952
- ],
953
- ...includeAccessControlMetadata && Array.isArray(requireFeatures) && requireFeatures.length ? { "x-require-features": requireFeatures } : {},
954
- ...includeAccessControlMetadata && Array.isArray(requireRoles) && requireRoles.length ? { "x-require-roles": requireRoles } : {},
955
- ...requireAuth ? { "x-require-auth": true } : {},
956
- ...methodDoc?.extensions ?? {}
957
- };
958
975
  }
959
976
  }
960
977
  }