@open-mercato/shared 0.4.7-develop-0a657b411f → 0.4.7-develop-11728c8558

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.
@@ -0,0 +1,13 @@
1
+ async function readJsonSafe(source, fallback = null) {
2
+ const raw = typeof source === "string" ? source : await source.text();
3
+ if (!raw) return fallback;
4
+ try {
5
+ return JSON.parse(raw);
6
+ } catch {
7
+ return fallback;
8
+ }
9
+ }
10
+ export {
11
+ readJsonSafe
12
+ };
13
+ //# sourceMappingURL=readJsonSafe.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/http/readJsonSafe.ts"],
4
+ "sourcesContent": ["export async function readJsonSafe<T>(\n source: Request | Response | string,\n fallback: T | null = null,\n): Promise<T | null> {\n const raw = typeof source === 'string' ? source : await source.text()\n if (!raw) return fallback\n\n try {\n return JSON.parse(raw) as T\n } catch {\n return fallback\n }\n}\n"],
5
+ "mappings": "AAAA,eAAsB,aACpB,QACA,WAAqB,MACF;AACnB,QAAM,MAAM,OAAO,WAAW,WAAW,SAAS,MAAM,OAAO,KAAK;AACpE,MAAI,CAAC,IAAK,QAAO;AAEjB,MAAI;AACF,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;",
6
+ "names": []
7
+ }
@@ -1,7 +1,15 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { createContext, useContext, useMemo } from "react";
4
- const I18nContext = createContext(null);
4
+ const I18N_CONTEXT_KEY = "__openMercatoI18nContext";
5
+ function getI18nContext() {
6
+ const store = globalThis;
7
+ if (!store[I18N_CONTEXT_KEY]) {
8
+ store[I18N_CONTEXT_KEY] = createContext(null);
9
+ }
10
+ return store[I18N_CONTEXT_KEY];
11
+ }
12
+ const I18nContext = getI18nContext();
5
13
  function format(template, params) {
6
14
  if (!params) return template;
7
15
  return template.replace(/\{\{(\w+)\}\}|\{(\w+)\}/g, (_, doubleKey, singleKey) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/i18n/context.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport { createContext, useContext, useMemo, type ReactNode } from 'react'\nimport type { Locale } from './config'\n\nexport type Dict = Record<string, string>\n\nexport type TranslateParams = Record<string, string | number>\n\nexport type TranslateFn = (\n key: string,\n fallbackOrParams?: string | TranslateParams,\n params?: TranslateParams\n) => string\n\nexport type I18nContextValue = {\n locale: Locale\n t: TranslateFn\n}\n\nconst I18nContext = createContext<I18nContextValue | null>(null)\n\nfunction format(template: string, params?: TranslateParams) {\n if (!params) return template\n return template.replace(/\\{\\{(\\w+)\\}\\}|\\{(\\w+)\\}/g, (_, doubleKey, singleKey) => {\n const key = doubleKey ?? singleKey\n if (!key) return _\n const value = params[key]\n if (value === undefined) {\n return doubleKey ? `{{${key}}}` : `{${key}}`\n }\n return String(value)\n })\n}\n\nexport function I18nProvider({ children, locale, dict }: { children: ReactNode; locale: Locale; dict: Dict }) {\n const value = useMemo<I18nContextValue>(() => ({\n locale,\n t: (key, fallbackOrParams, params) => {\n let fallback: string | undefined\n let resolvedParams: TranslateParams | undefined\n\n if (typeof fallbackOrParams === 'string') {\n fallback = fallbackOrParams\n resolvedParams = params\n } else {\n resolvedParams = fallbackOrParams ?? params\n }\n\n const template = dict[key] ?? fallback ?? key\n return format(template, resolvedParams)\n },\n }), [locale, dict])\n return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>\n}\n\nexport function useT() {\n const ctx = useContext(I18nContext)\n if (!ctx) throw new Error('useT must be used within I18nProvider')\n return ctx.t\n}\n\nexport function useLocale() {\n const ctx = useContext(I18nContext)\n if (!ctx) throw new Error('useLocale must be used within I18nProvider')\n return ctx.locale\n}\n"],
5
- "mappings": ";AAoDS;AAnDT,SAAS,eAAe,YAAY,eAA+B;AAkBnE,MAAM,cAAc,cAAuC,IAAI;AAE/D,SAAS,OAAO,UAAkB,QAA0B;AAC1D,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,SAAS,QAAQ,4BAA4B,CAAC,GAAG,WAAW,cAAc;AAC/E,UAAM,MAAM,aAAa;AACzB,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,QAAQ,OAAO,GAAG;AACxB,QAAI,UAAU,QAAW;AACvB,aAAO,YAAY,KAAK,GAAG,OAAO,IAAI,GAAG;AAAA,IAC3C;AACA,WAAO,OAAO,KAAK;AAAA,EACrB,CAAC;AACH;AAEO,SAAS,aAAa,EAAE,UAAU,QAAQ,KAAK,GAAwD;AAC5G,QAAM,QAAQ,QAA0B,OAAO;AAAA,IAC7C;AAAA,IACA,GAAG,CAAC,KAAK,kBAAkB,WAAW;AACpC,UAAI;AACJ,UAAI;AAEJ,UAAI,OAAO,qBAAqB,UAAU;AACxC,mBAAW;AACX,yBAAiB;AAAA,MACnB,OAAO;AACL,yBAAiB,oBAAoB;AAAA,MACvC;AAEA,YAAM,WAAW,KAAK,GAAG,KAAK,YAAY;AAC1C,aAAO,OAAO,UAAU,cAAc;AAAA,IACxC;AAAA,EACF,IAAI,CAAC,QAAQ,IAAI,CAAC;AAClB,SAAO,oBAAC,YAAY,UAAZ,EAAqB,OAAe,UAAS;AACvD;AAEO,SAAS,OAAO;AACrB,QAAM,MAAM,WAAW,WAAW;AAClC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,uCAAuC;AACjE,SAAO,IAAI;AACb;AAEO,SAAS,YAAY;AAC1B,QAAM,MAAM,WAAW,WAAW;AAClC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,4CAA4C;AACtE,SAAO,IAAI;AACb;",
4
+ "sourcesContent": ["\"use client\"\nimport { createContext, useContext, useMemo, type ReactNode } from 'react'\nimport type { Locale } from './config'\n\nexport type Dict = Record<string, string>\n\nexport type TranslateParams = Record<string, string | number>\n\nexport type TranslateFn = (\n key: string,\n fallbackOrParams?: string | TranslateParams,\n params?: TranslateParams\n) => string\n\nexport type I18nContextValue = {\n locale: Locale\n t: TranslateFn\n}\n\nconst I18N_CONTEXT_KEY = '__openMercatoI18nContext'\n\ntype GlobalI18nContextStore = typeof globalThis & {\n [I18N_CONTEXT_KEY]?: ReturnType<typeof createContext<I18nContextValue | null>>\n}\n\nfunction getI18nContext() {\n const store = globalThis as GlobalI18nContextStore\n if (!store[I18N_CONTEXT_KEY]) {\n store[I18N_CONTEXT_KEY] = createContext<I18nContextValue | null>(null)\n }\n return store[I18N_CONTEXT_KEY]\n}\n\nconst I18nContext = getI18nContext()\n\nfunction format(template: string, params?: TranslateParams) {\n if (!params) return template\n return template.replace(/\\{\\{(\\w+)\\}\\}|\\{(\\w+)\\}/g, (_, doubleKey, singleKey) => {\n const key = doubleKey ?? singleKey\n if (!key) return _\n const value = params[key]\n if (value === undefined) {\n return doubleKey ? `{{${key}}}` : `{${key}}`\n }\n return String(value)\n })\n}\n\nexport function I18nProvider({ children, locale, dict }: { children: ReactNode; locale: Locale; dict: Dict }) {\n const value = useMemo<I18nContextValue>(() => ({\n locale,\n t: (key, fallbackOrParams, params) => {\n let fallback: string | undefined\n let resolvedParams: TranslateParams | undefined\n\n if (typeof fallbackOrParams === 'string') {\n fallback = fallbackOrParams\n resolvedParams = params\n } else {\n resolvedParams = fallbackOrParams ?? params\n }\n\n const template = dict[key] ?? fallback ?? key\n return format(template, resolvedParams)\n },\n }), [locale, dict])\n return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>\n}\n\nexport function useT() {\n const ctx = useContext(I18nContext)\n if (!ctx) throw new Error('useT must be used within I18nProvider')\n return ctx.t\n}\n\nexport function useLocale() {\n const ctx = useContext(I18nContext)\n if (!ctx) throw new Error('useLocale must be used within I18nProvider')\n return ctx.locale\n}\n"],
5
+ "mappings": ";AAkES;AAjET,SAAS,eAAe,YAAY,eAA+B;AAkBnE,MAAM,mBAAmB;AAMzB,SAAS,iBAAiB;AACxB,QAAM,QAAQ;AACd,MAAI,CAAC,MAAM,gBAAgB,GAAG;AAC5B,UAAM,gBAAgB,IAAI,cAAuC,IAAI;AAAA,EACvE;AACA,SAAO,MAAM,gBAAgB;AAC/B;AAEA,MAAM,cAAc,eAAe;AAEnC,SAAS,OAAO,UAAkB,QAA0B;AAC1D,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,SAAS,QAAQ,4BAA4B,CAAC,GAAG,WAAW,cAAc;AAC/E,UAAM,MAAM,aAAa;AACzB,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,QAAQ,OAAO,GAAG;AACxB,QAAI,UAAU,QAAW;AACvB,aAAO,YAAY,KAAK,GAAG,OAAO,IAAI,GAAG;AAAA,IAC3C;AACA,WAAO,OAAO,KAAK;AAAA,EACrB,CAAC;AACH;AAEO,SAAS,aAAa,EAAE,UAAU,QAAQ,KAAK,GAAwD;AAC5G,QAAM,QAAQ,QAA0B,OAAO;AAAA,IAC7C;AAAA,IACA,GAAG,CAAC,KAAK,kBAAkB,WAAW;AACpC,UAAI;AACJ,UAAI;AAEJ,UAAI,OAAO,qBAAqB,UAAU;AACxC,mBAAW;AACX,yBAAiB;AAAA,MACnB,OAAO;AACL,yBAAiB,oBAAoB;AAAA,MACvC;AAEA,YAAM,WAAW,KAAK,GAAG,KAAK,YAAY;AAC1C,aAAO,OAAO,UAAU,cAAc;AAAA,IACxC;AAAA,EACF,IAAI,CAAC,QAAQ,IAAI,CAAC;AAClB,SAAO,oBAAC,YAAY,UAAZ,EAAqB,OAAe,UAAS;AACvD;AAEO,SAAS,OAAO;AACrB,QAAM,MAAM,WAAW,WAAW;AAClC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,uCAAuC;AACjE,SAAO,IAAI;AACb;AAEO,SAAS,YAAY;AAC1B,QAAM,MAAM,WAAW,WAAW;AAClC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,4CAA4C;AACtE,SAAO,IAAI;AACb;",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,4 @@
1
- const APP_VERSION = "0.4.7-develop-0a657b411f";
1
+ const APP_VERSION = "0.4.7-develop-11728c8558";
2
2
  const appVersion = APP_VERSION;
3
3
  export {
4
4
  APP_VERSION,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/version.ts"],
4
- "sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.4.7-develop-0a657b411f'\nexport const appVersion = APP_VERSION\n"],
4
+ "sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.4.7-develop-11728c8558'\nexport const appVersion = APP_VERSION\n"],
5
5
  "mappings": "AACO,MAAM,cAAc;AACpB,MAAM,aAAa;",
6
6
  "names": []
7
7
  }
@@ -47,7 +47,13 @@ function resolveIntegrationCredentialsSchema(integrationId) {
47
47
  function getIntegrationTitle(integrationId) {
48
48
  return integrationRegistry.get(integrationId)?.title ?? integrationId;
49
49
  }
50
+ const LEGACY_INTEGRATION_DETAIL_TABS_SPOT_ID = "integrations.detail:tabs";
51
+ function buildIntegrationDetailWidgetSpotId(integrationId) {
52
+ return `integrations.detail:${integrationId}`;
53
+ }
50
54
  export {
55
+ LEGACY_INTEGRATION_DETAIL_TABS_SPOT_ID,
56
+ buildIntegrationDetailWidgetSpotId,
51
57
  clearRegisteredIntegrations,
52
58
  getAllBundles,
53
59
  getAllIntegrations,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/integrations/types.ts"],
4
- "sourcesContent": ["export type IntegrationScope = {\n organizationId: string\n tenantId: string\n}\n\nexport type IntegrationCategory =\n | 'payment'\n | 'shipping'\n | 'data_sync'\n | 'communication'\n | 'webhook'\n | 'storage'\n | 'other'\n\nexport type IntegrationHubId =\n | 'payment_gateways'\n | 'shipping_carriers'\n | 'data_sync'\n | 'communication_channels'\n | 'webhook_endpoints'\n | 'storage_hubs'\n | string\n\nexport type CredentialFieldType =\n | 'text'\n | 'secret'\n | 'select'\n | 'boolean'\n | 'url'\n | 'oauth'\n | 'ssh_keypair'\n\nexport interface CredentialFieldOption {\n value: string\n label: string\n}\n\nexport interface CredentialFieldVisibleWhen {\n field: string\n equals: string | number | boolean\n}\n\nexport interface IntegrationCredentialFieldBase {\n key: string\n label: string\n required?: boolean\n placeholder?: string\n helpText?: string\n visibleWhen?: CredentialFieldVisibleWhen\n}\n\nexport interface IntegrationCredentialFieldText extends IntegrationCredentialFieldBase {\n type: Extract<CredentialFieldType, 'text' | 'secret' | 'url'>\n}\n\nexport interface IntegrationCredentialFieldBoolean extends IntegrationCredentialFieldBase {\n type: Extract<CredentialFieldType, 'boolean'>\n}\n\nexport interface IntegrationCredentialFieldSelect extends IntegrationCredentialFieldBase {\n type: Extract<CredentialFieldType, 'select'>\n options: CredentialFieldOption[]\n}\n\nexport interface IntegrationCredentialFieldOauth extends IntegrationCredentialFieldBase {\n type: Extract<CredentialFieldType, 'oauth'>\n authUrl?: string\n tokenUrl?: string\n scopes?: string[]\n clientIdField?: string\n clientSecretField?: string\n}\n\nexport interface IntegrationCredentialFieldSshKeypair extends IntegrationCredentialFieldBase {\n type: Extract<CredentialFieldType, 'ssh_keypair'>\n algorithm?: 'ed25519' | 'rsa'\n rsaBits?: 2048 | 3072 | 4096\n}\n\nexport type IntegrationCredentialField =\n | IntegrationCredentialFieldText\n | IntegrationCredentialFieldBoolean\n | IntegrationCredentialFieldSelect\n | IntegrationCredentialFieldOauth\n | IntegrationCredentialFieldSshKeypair\n\nexport interface IntegrationCredentialsSchema {\n fields: IntegrationCredentialField[]\n}\n\nexport interface IntegrationHealthCheckConfig {\n service: string\n}\n\nexport interface ApiVersionDefinition {\n id: string\n label: string\n status: 'stable' | 'deprecated' | 'experimental'\n default?: boolean\n changelog?: string\n deprecatedAt?: string\n sunsetAt?: string\n migrationGuide?: string\n}\n\nexport interface IntegrationBundle {\n id: string\n title: string\n description: string\n icon?: string\n package?: string\n version?: string\n author?: string\n credentials: IntegrationCredentialsSchema\n healthCheck?: IntegrationHealthCheckConfig\n}\n\nexport interface IntegrationDefinition {\n id: string\n title: string\n icon?: string\n buildExternalUrl?: (externalId: string) => string\n bundleId?: string\n apiVersions?: ApiVersionDefinition[]\n description?: string\n category?: IntegrationCategory | string\n hub?: IntegrationHubId\n providerKey?: string\n docsUrl?: string\n package?: string\n version?: string\n author?: string\n license?: string\n tags?: string[]\n credentials?: IntegrationCredentialsSchema\n healthCheck?: IntegrationHealthCheckConfig\n}\n\nexport interface ExternalIdEnrichment {\n _integrations: Record<string, ExternalIdMapping>\n}\n\nexport interface ExternalIdMapping {\n externalId: string\n externalUrl?: string\n lastSyncedAt?: string\n syncStatus: 'synced' | 'pending' | 'error' | 'not_synced'\n}\n\nconst integrationRegistry = new Map<string, IntegrationDefinition>()\nconst integrationBundleRegistry = new Map<string, IntegrationBundle>()\n\nexport function registerIntegration(definition: IntegrationDefinition): void {\n integrationRegistry.set(definition.id, definition)\n}\n\nexport function registerIntegrations(definitions: IntegrationDefinition[]): void {\n for (const definition of definitions) {\n integrationRegistry.set(definition.id, definition)\n }\n}\n\nexport function registerBundle(bundle: IntegrationBundle): void {\n integrationBundleRegistry.set(bundle.id, bundle)\n}\n\nexport function registerBundles(bundles: IntegrationBundle[]): void {\n for (const bundle of bundles) {\n integrationBundleRegistry.set(bundle.id, bundle)\n }\n}\n\nexport function clearRegisteredIntegrations(): void {\n integrationRegistry.clear()\n integrationBundleRegistry.clear()\n}\n\nexport function getIntegration(integrationId: string): IntegrationDefinition | undefined {\n return integrationRegistry.get(integrationId)\n}\n\nexport function getAllIntegrations(): IntegrationDefinition[] {\n return Array.from(integrationRegistry.values())\n}\n\nexport function getBundle(bundleId: string): IntegrationBundle | undefined {\n return integrationBundleRegistry.get(bundleId)\n}\n\nexport function getAllBundles(): IntegrationBundle[] {\n return Array.from(integrationBundleRegistry.values())\n}\n\nexport function getBundleIntegrations(bundleId: string): IntegrationDefinition[] {\n return Array.from(integrationRegistry.values()).filter((integration) => integration.bundleId === bundleId)\n}\n\nexport function resolveIntegrationCredentialsSchema(integrationId: string): IntegrationCredentialsSchema | undefined {\n const definition = integrationRegistry.get(integrationId)\n if (!definition) return undefined\n\n if (definition.credentials && definition.credentials.fields.length > 0) {\n return definition.credentials\n }\n\n if (!definition.bundleId) return definition.credentials\n return integrationBundleRegistry.get(definition.bundleId)?.credentials\n}\n\nexport function getIntegrationTitle(integrationId: string): string {\n return integrationRegistry.get(integrationId)?.title ?? integrationId\n}\n"],
5
- "mappings": "AAqJA,MAAM,sBAAsB,oBAAI,IAAmC;AACnE,MAAM,4BAA4B,oBAAI,IAA+B;AAE9D,SAAS,oBAAoB,YAAyC;AAC3E,sBAAoB,IAAI,WAAW,IAAI,UAAU;AACnD;AAEO,SAAS,qBAAqB,aAA4C;AAC/E,aAAW,cAAc,aAAa;AACpC,wBAAoB,IAAI,WAAW,IAAI,UAAU;AAAA,EACnD;AACF;AAEO,SAAS,eAAe,QAAiC;AAC9D,4BAA0B,IAAI,OAAO,IAAI,MAAM;AACjD;AAEO,SAAS,gBAAgB,SAAoC;AAClE,aAAW,UAAU,SAAS;AAC5B,8BAA0B,IAAI,OAAO,IAAI,MAAM;AAAA,EACjD;AACF;AAEO,SAAS,8BAAoC;AAClD,sBAAoB,MAAM;AAC1B,4BAA0B,MAAM;AAClC;AAEO,SAAS,eAAe,eAA0D;AACvF,SAAO,oBAAoB,IAAI,aAAa;AAC9C;AAEO,SAAS,qBAA8C;AAC5D,SAAO,MAAM,KAAK,oBAAoB,OAAO,CAAC;AAChD;AAEO,SAAS,UAAU,UAAiD;AACzE,SAAO,0BAA0B,IAAI,QAAQ;AAC/C;AAEO,SAAS,gBAAqC;AACnD,SAAO,MAAM,KAAK,0BAA0B,OAAO,CAAC;AACtD;AAEO,SAAS,sBAAsB,UAA2C;AAC/E,SAAO,MAAM,KAAK,oBAAoB,OAAO,CAAC,EAAE,OAAO,CAAC,gBAAgB,YAAY,aAAa,QAAQ;AAC3G;AAEO,SAAS,oCAAoC,eAAiE;AACnH,QAAM,aAAa,oBAAoB,IAAI,aAAa;AACxD,MAAI,CAAC,WAAY,QAAO;AAExB,MAAI,WAAW,eAAe,WAAW,YAAY,OAAO,SAAS,GAAG;AACtE,WAAO,WAAW;AAAA,EACpB;AAEA,MAAI,CAAC,WAAW,SAAU,QAAO,WAAW;AAC5C,SAAO,0BAA0B,IAAI,WAAW,QAAQ,GAAG;AAC7D;AAEO,SAAS,oBAAoB,eAA+B;AACjE,SAAO,oBAAoB,IAAI,aAAa,GAAG,SAAS;AAC1D;",
4
+ "sourcesContent": ["export type IntegrationScope = {\n organizationId: string\n tenantId: string\n}\n\nexport type IntegrationCategory =\n | 'payment'\n | 'shipping'\n | 'data_sync'\n | 'communication'\n | 'webhook'\n | 'storage'\n | 'other'\n\nexport type IntegrationHubId =\n | 'payment_gateways'\n | 'shipping_carriers'\n | 'data_sync'\n | 'communication_channels'\n | 'webhook_endpoints'\n | 'storage_hubs'\n | string\n\nexport type CredentialFieldType =\n | 'text'\n | 'secret'\n | 'select'\n | 'boolean'\n | 'url'\n | 'oauth'\n | 'ssh_keypair'\n\nexport interface CredentialFieldOption {\n value: string\n label: string\n}\n\nexport interface CredentialFieldVisibleWhen {\n field: string\n equals: string | number | boolean\n}\n\nexport interface IntegrationCredentialWebhookHelp {\n kind: 'webhook_setup'\n title: string\n summary: string\n endpointPath: string\n dashboardPathLabel: string\n steps: string[]\n events?: string[]\n localDevelopment?: {\n tunnelCommand: string\n publicUrlExample: string\n note?: string\n }\n}\n\nexport interface IntegrationCredentialFieldBase {\n key: string\n label: string\n required?: boolean\n placeholder?: string\n helpText?: string\n helpDetails?: IntegrationCredentialWebhookHelp\n visibleWhen?: CredentialFieldVisibleWhen\n}\n\nexport interface IntegrationCredentialFieldText extends IntegrationCredentialFieldBase {\n type: Extract<CredentialFieldType, 'text' | 'secret' | 'url'>\n}\n\nexport interface IntegrationCredentialFieldBoolean extends IntegrationCredentialFieldBase {\n type: Extract<CredentialFieldType, 'boolean'>\n}\n\nexport interface IntegrationCredentialFieldSelect extends IntegrationCredentialFieldBase {\n type: Extract<CredentialFieldType, 'select'>\n options: CredentialFieldOption[]\n}\n\nexport interface IntegrationCredentialFieldOauth extends IntegrationCredentialFieldBase {\n type: Extract<CredentialFieldType, 'oauth'>\n authUrl?: string\n tokenUrl?: string\n scopes?: string[]\n clientIdField?: string\n clientSecretField?: string\n}\n\nexport interface IntegrationCredentialFieldSshKeypair extends IntegrationCredentialFieldBase {\n type: Extract<CredentialFieldType, 'ssh_keypair'>\n algorithm?: 'ed25519' | 'rsa'\n rsaBits?: 2048 | 3072 | 4096\n}\n\nexport type IntegrationCredentialField =\n | IntegrationCredentialFieldText\n | IntegrationCredentialFieldBoolean\n | IntegrationCredentialFieldSelect\n | IntegrationCredentialFieldOauth\n | IntegrationCredentialFieldSshKeypair\n\nexport interface IntegrationCredentialsSchema {\n fields: IntegrationCredentialField[]\n}\n\nexport interface IntegrationHealthCheckConfig {\n service: string\n}\n\nexport interface ApiVersionDefinition {\n id: string\n label: string\n status: 'stable' | 'deprecated' | 'experimental'\n default?: boolean\n changelog?: string\n deprecatedAt?: string\n sunsetAt?: string\n migrationGuide?: string\n}\n\nexport interface IntegrationBundle {\n id: string\n title: string\n description: string\n icon?: string\n package?: string\n version?: string\n author?: string\n credentials: IntegrationCredentialsSchema\n healthCheck?: IntegrationHealthCheckConfig\n}\n\nexport interface IntegrationDetailPageConfig {\n /**\n * UMES widget spot rendered on the integration detail page.\n * Widgets registered here can render inline blocks, grouped panels,\n * or additional tabs via `placement.kind`.\n */\n widgetSpotId?: string\n}\n\nexport interface IntegrationDefinition {\n id: string\n title: string\n icon?: string\n buildExternalUrl?: (externalId: string) => string\n bundleId?: string\n apiVersions?: ApiVersionDefinition[]\n description?: string\n category?: IntegrationCategory | string\n hub?: IntegrationHubId\n providerKey?: string\n docsUrl?: string\n package?: string\n version?: string\n author?: string\n company?: string\n license?: string\n tags?: string[]\n detailPage?: IntegrationDetailPageConfig\n credentials?: IntegrationCredentialsSchema\n healthCheck?: IntegrationHealthCheckConfig\n}\n\nexport interface ExternalIdEnrichment {\n _integrations: Record<string, ExternalIdMapping>\n}\n\nexport interface ExternalIdMapping {\n externalId: string\n externalUrl?: string\n lastSyncedAt?: string\n syncStatus: 'synced' | 'pending' | 'error' | 'not_synced'\n}\n\nconst integrationRegistry = new Map<string, IntegrationDefinition>()\nconst integrationBundleRegistry = new Map<string, IntegrationBundle>()\n\nexport function registerIntegration(definition: IntegrationDefinition): void {\n integrationRegistry.set(definition.id, definition)\n}\n\nexport function registerIntegrations(definitions: IntegrationDefinition[]): void {\n for (const definition of definitions) {\n integrationRegistry.set(definition.id, definition)\n }\n}\n\nexport function registerBundle(bundle: IntegrationBundle): void {\n integrationBundleRegistry.set(bundle.id, bundle)\n}\n\nexport function registerBundles(bundles: IntegrationBundle[]): void {\n for (const bundle of bundles) {\n integrationBundleRegistry.set(bundle.id, bundle)\n }\n}\n\nexport function clearRegisteredIntegrations(): void {\n integrationRegistry.clear()\n integrationBundleRegistry.clear()\n}\n\nexport function getIntegration(integrationId: string): IntegrationDefinition | undefined {\n return integrationRegistry.get(integrationId)\n}\n\nexport function getAllIntegrations(): IntegrationDefinition[] {\n return Array.from(integrationRegistry.values())\n}\n\nexport function getBundle(bundleId: string): IntegrationBundle | undefined {\n return integrationBundleRegistry.get(bundleId)\n}\n\nexport function getAllBundles(): IntegrationBundle[] {\n return Array.from(integrationBundleRegistry.values())\n}\n\nexport function getBundleIntegrations(bundleId: string): IntegrationDefinition[] {\n return Array.from(integrationRegistry.values()).filter((integration) => integration.bundleId === bundleId)\n}\n\nexport function resolveIntegrationCredentialsSchema(integrationId: string): IntegrationCredentialsSchema | undefined {\n const definition = integrationRegistry.get(integrationId)\n if (!definition) return undefined\n\n if (definition.credentials && definition.credentials.fields.length > 0) {\n return definition.credentials\n }\n\n if (!definition.bundleId) return definition.credentials\n return integrationBundleRegistry.get(definition.bundleId)?.credentials\n}\n\nexport function getIntegrationTitle(integrationId: string): string {\n return integrationRegistry.get(integrationId)?.title ?? integrationId\n}\n\nexport const LEGACY_INTEGRATION_DETAIL_TABS_SPOT_ID = 'integrations.detail:tabs'\n\nexport function buildIntegrationDetailWidgetSpotId(integrationId: string): string {\n return `integrations.detail:${integrationId}`\n}\n"],
5
+ "mappings": "AAgLA,MAAM,sBAAsB,oBAAI,IAAmC;AACnE,MAAM,4BAA4B,oBAAI,IAA+B;AAE9D,SAAS,oBAAoB,YAAyC;AAC3E,sBAAoB,IAAI,WAAW,IAAI,UAAU;AACnD;AAEO,SAAS,qBAAqB,aAA4C;AAC/E,aAAW,cAAc,aAAa;AACpC,wBAAoB,IAAI,WAAW,IAAI,UAAU;AAAA,EACnD;AACF;AAEO,SAAS,eAAe,QAAiC;AAC9D,4BAA0B,IAAI,OAAO,IAAI,MAAM;AACjD;AAEO,SAAS,gBAAgB,SAAoC;AAClE,aAAW,UAAU,SAAS;AAC5B,8BAA0B,IAAI,OAAO,IAAI,MAAM;AAAA,EACjD;AACF;AAEO,SAAS,8BAAoC;AAClD,sBAAoB,MAAM;AAC1B,4BAA0B,MAAM;AAClC;AAEO,SAAS,eAAe,eAA0D;AACvF,SAAO,oBAAoB,IAAI,aAAa;AAC9C;AAEO,SAAS,qBAA8C;AAC5D,SAAO,MAAM,KAAK,oBAAoB,OAAO,CAAC;AAChD;AAEO,SAAS,UAAU,UAAiD;AACzE,SAAO,0BAA0B,IAAI,QAAQ;AAC/C;AAEO,SAAS,gBAAqC;AACnD,SAAO,MAAM,KAAK,0BAA0B,OAAO,CAAC;AACtD;AAEO,SAAS,sBAAsB,UAA2C;AAC/E,SAAO,MAAM,KAAK,oBAAoB,OAAO,CAAC,EAAE,OAAO,CAAC,gBAAgB,YAAY,aAAa,QAAQ;AAC3G;AAEO,SAAS,oCAAoC,eAAiE;AACnH,QAAM,aAAa,oBAAoB,IAAI,aAAa;AACxD,MAAI,CAAC,WAAY,QAAO;AAExB,MAAI,WAAW,eAAe,WAAW,YAAY,OAAO,SAAS,GAAG;AACtE,WAAO,WAAW;AAAA,EACpB;AAEA,MAAI,CAAC,WAAW,SAAU,QAAO,WAAW;AAC5C,SAAO,0BAA0B,IAAI,WAAW,QAAQ,GAAG;AAC7D;AAEO,SAAS,oBAAoB,eAA+B;AACjE,SAAO,oBAAoB,IAAI,aAAa,GAAG,SAAS;AAC1D;AAEO,MAAM,yCAAyC;AAE/C,SAAS,mCAAmC,eAA+B;AAChF,SAAO,uBAAuB,aAAa;AAC7C;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,2 @@
1
+ export * from "./types.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/payment_gateways/index.ts"],
4
+ "sourcesContent": ["export * from './types'\n"],
5
+ "mappings": "AAAA,cAAc;",
6
+ "names": []
7
+ }
@@ -0,0 +1,81 @@
1
+ const ADAPTER_REGISTRY_KEY = "__openMercatoPaymentGatewayAdapters__";
2
+ const WEBHOOK_REGISTRY_KEY = "__openMercatoPaymentGatewayWebhookHandlers__";
3
+ function getAdapterRegistry() {
4
+ const globalState = globalThis;
5
+ if (!globalState[ADAPTER_REGISTRY_KEY]) {
6
+ globalState[ADAPTER_REGISTRY_KEY] = /* @__PURE__ */ new Map();
7
+ }
8
+ return globalState[ADAPTER_REGISTRY_KEY];
9
+ }
10
+ function getWebhookHandlerRegistry() {
11
+ const globalState = globalThis;
12
+ if (!globalState[WEBHOOK_REGISTRY_KEY]) {
13
+ globalState[WEBHOOK_REGISTRY_KEY] = /* @__PURE__ */ new Map();
14
+ }
15
+ return globalState[WEBHOOK_REGISTRY_KEY];
16
+ }
17
+ function adapterKey(providerKey, version) {
18
+ return version ? `${providerKey}:${version}` : providerKey;
19
+ }
20
+ function registerGatewayAdapter(adapter, options) {
21
+ const adapterRegistry = getAdapterRegistry();
22
+ const key = adapterKey(adapter.providerKey, options?.version);
23
+ adapterRegistry.set(key, adapter);
24
+ if (options?.version) {
25
+ if (!adapterRegistry.has(adapter.providerKey)) {
26
+ adapterRegistry.set(adapter.providerKey, adapter);
27
+ }
28
+ }
29
+ return () => {
30
+ adapterRegistry.delete(key);
31
+ };
32
+ }
33
+ function getGatewayAdapter(providerKey, version) {
34
+ const adapterRegistry = getAdapterRegistry();
35
+ if (version) {
36
+ return adapterRegistry.get(adapterKey(providerKey, version)) ?? adapterRegistry.get(providerKey);
37
+ }
38
+ return adapterRegistry.get(providerKey);
39
+ }
40
+ function listGatewayAdapters() {
41
+ const adapterRegistry = getAdapterRegistry();
42
+ const seen = /* @__PURE__ */ new Set();
43
+ const result = [];
44
+ for (const [key, adapter] of adapterRegistry) {
45
+ if (!key.includes(":") && !seen.has(adapter.providerKey)) {
46
+ seen.add(adapter.providerKey);
47
+ result.push(adapter);
48
+ }
49
+ }
50
+ return result;
51
+ }
52
+ function clearGatewayAdapters() {
53
+ getAdapterRegistry().clear();
54
+ }
55
+ function registerWebhookHandler(providerKey, handler, options) {
56
+ const webhookHandlerRegistry = getWebhookHandlerRegistry();
57
+ webhookHandlerRegistry.set(providerKey, {
58
+ handler,
59
+ queue: options?.queue,
60
+ readSessionIdHint: options?.readSessionIdHint
61
+ });
62
+ return () => {
63
+ webhookHandlerRegistry.delete(providerKey);
64
+ };
65
+ }
66
+ function getWebhookHandler(providerKey) {
67
+ return getWebhookHandlerRegistry().get(providerKey);
68
+ }
69
+ function clearWebhookHandlers() {
70
+ getWebhookHandlerRegistry().clear();
71
+ }
72
+ export {
73
+ clearGatewayAdapters,
74
+ clearWebhookHandlers,
75
+ getGatewayAdapter,
76
+ getWebhookHandler,
77
+ listGatewayAdapters,
78
+ registerGatewayAdapter,
79
+ registerWebhookHandler
80
+ };
81
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/payment_gateways/types.ts"],
4
+ "sourcesContent": ["// \u2500\u2500 Unified Payment Status \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type UnifiedPaymentStatus =\n | 'pending'\n | 'authorized'\n | 'captured'\n | 'partially_captured'\n | 'refunded'\n | 'partially_refunded'\n | 'cancelled'\n | 'failed'\n | 'expired'\n | 'unknown'\n\n// \u2500\u2500 GatewayAdapter Interface \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface GatewayAdapter {\n readonly providerKey: string\n\n /** Create a payment session / payment intent */\n createSession(input: CreateSessionInput): Promise<CreateSessionResult>\n\n /** Capture an authorized payment */\n capture(input: CaptureInput): Promise<CaptureResult>\n\n /** Refund a captured payment (full or partial) */\n refund(input: RefundInput): Promise<RefundResult>\n\n /** Cancel / void an authorized payment before capture */\n cancel(input: CancelInput): Promise<CancelResult>\n\n /** Get current payment status from provider */\n getStatus(input: GetStatusInput): Promise<GatewayPaymentStatus>\n\n /** Verify and parse an inbound webhook event */\n verifyWebhook(input: VerifyWebhookInput): Promise<WebhookEvent>\n\n /** Map provider status to unified status */\n mapStatus(providerStatus: string, eventType?: string): UnifiedPaymentStatus\n}\n\n// \u2500\u2500 Input / Output Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface CreateSessionInput {\n orderId?: string\n paymentId: string\n tenantId: string\n organizationId: string\n amount: number\n currencyCode: string\n captureMethod?: 'automatic' | 'manual'\n paymentTypes?: string[]\n description?: string\n successUrl?: string\n cancelUrl?: string\n metadata?: Record<string, unknown>\n credentials: Record<string, unknown>\n lineItems?: SessionLineItem[]\n}\n\nexport interface SessionLineItem {\n name: string\n quantity: number\n unitAmount: number\n currencyCode: string\n}\n\nexport interface CreateSessionResult {\n sessionId: string\n clientSecret?: string\n redirectUrl?: string\n status: UnifiedPaymentStatus\n providerData?: Record<string, unknown>\n}\n\nexport interface CaptureInput {\n sessionId: string\n amount?: number\n credentials: Record<string, unknown>\n metadata?: Record<string, unknown>\n}\n\nexport interface CaptureResult {\n status: UnifiedPaymentStatus\n capturedAmount: number\n providerData?: Record<string, unknown>\n}\n\nexport interface RefundInput {\n sessionId: string\n amount?: number\n reason?: string\n credentials: Record<string, unknown>\n metadata?: Record<string, unknown>\n}\n\nexport interface RefundResult {\n refundId: string\n status: UnifiedPaymentStatus\n refundedAmount: number\n providerData?: Record<string, unknown>\n}\n\nexport interface CancelInput {\n sessionId: string\n reason?: string\n credentials: Record<string, unknown>\n}\n\nexport interface CancelResult {\n status: UnifiedPaymentStatus\n providerData?: Record<string, unknown>\n}\n\nexport interface GetStatusInput {\n sessionId: string\n credentials: Record<string, unknown>\n}\n\nexport interface GatewayPaymentStatus {\n status: UnifiedPaymentStatus\n amount: number\n amountReceived: number\n currencyCode: string\n providerData?: Record<string, unknown>\n}\n\nexport interface VerifyWebhookInput {\n rawBody: string | Buffer\n headers: Record<string, string | string[] | undefined>\n credentials: Record<string, unknown>\n}\n\nexport interface WebhookEvent {\n eventType: string\n eventId: string\n data: Record<string, unknown>\n idempotencyKey: string\n timestamp: Date\n}\n\n// \u2500\u2500 Webhook Handler \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface WebhookHandlerRegistration {\n handler: (input: VerifyWebhookInput) => Promise<WebhookEvent>\n queue?: string\n readSessionIdHint?: (payload: Record<string, unknown> | null) => string | null\n}\n\n// \u2500\u2500 Adapter Registry Options \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface RegisterAdapterOptions {\n version?: string\n}\n\n// \u2500\u2500 Registries \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst ADAPTER_REGISTRY_KEY = '__openMercatoPaymentGatewayAdapters__'\nconst WEBHOOK_REGISTRY_KEY = '__openMercatoPaymentGatewayWebhookHandlers__'\n\nfunction getAdapterRegistry(): Map<string, GatewayAdapter> {\n const globalState = globalThis as typeof globalThis & {\n [ADAPTER_REGISTRY_KEY]?: Map<string, GatewayAdapter>\n }\n if (!globalState[ADAPTER_REGISTRY_KEY]) {\n globalState[ADAPTER_REGISTRY_KEY] = new Map<string, GatewayAdapter>()\n }\n return globalState[ADAPTER_REGISTRY_KEY]\n}\n\nfunction getWebhookHandlerRegistry(): Map<string, WebhookHandlerRegistration> {\n const globalState = globalThis as typeof globalThis & {\n [WEBHOOK_REGISTRY_KEY]?: Map<string, WebhookHandlerRegistration>\n }\n if (!globalState[WEBHOOK_REGISTRY_KEY]) {\n globalState[WEBHOOK_REGISTRY_KEY] = new Map<string, WebhookHandlerRegistration>()\n }\n return globalState[WEBHOOK_REGISTRY_KEY]\n}\n\nfunction adapterKey(providerKey: string, version?: string): string {\n return version ? `${providerKey}:${version}` : providerKey\n}\n\nexport function registerGatewayAdapter(adapter: GatewayAdapter, options?: RegisterAdapterOptions): () => void {\n const adapterRegistry = getAdapterRegistry()\n const key = adapterKey(adapter.providerKey, options?.version)\n adapterRegistry.set(key, adapter)\n if (options?.version) {\n // Also register as default if no default exists\n if (!adapterRegistry.has(adapter.providerKey)) {\n adapterRegistry.set(adapter.providerKey, adapter)\n }\n }\n return () => {\n adapterRegistry.delete(key)\n }\n}\n\nexport function getGatewayAdapter(providerKey: string, version?: string): GatewayAdapter | undefined {\n const adapterRegistry = getAdapterRegistry()\n if (version) {\n return adapterRegistry.get(adapterKey(providerKey, version)) ?? adapterRegistry.get(providerKey)\n }\n return adapterRegistry.get(providerKey)\n}\n\nexport function listGatewayAdapters(): GatewayAdapter[] {\n const adapterRegistry = getAdapterRegistry()\n const seen = new Set<string>()\n const result: GatewayAdapter[] = []\n for (const [key, adapter] of adapterRegistry) {\n if (!key.includes(':') && !seen.has(adapter.providerKey)) {\n seen.add(adapter.providerKey)\n result.push(adapter)\n }\n }\n return result\n}\n\nexport function clearGatewayAdapters(): void {\n getAdapterRegistry().clear()\n}\n\nexport function registerWebhookHandler(\n providerKey: string,\n handler: (input: VerifyWebhookInput) => Promise<WebhookEvent>,\n options?: {\n queue?: string\n readSessionIdHint?: (payload: Record<string, unknown> | null) => string | null\n },\n): () => void {\n const webhookHandlerRegistry = getWebhookHandlerRegistry()\n webhookHandlerRegistry.set(providerKey, {\n handler,\n queue: options?.queue,\n readSessionIdHint: options?.readSessionIdHint,\n })\n return () => {\n webhookHandlerRegistry.delete(providerKey)\n }\n}\n\nexport function getWebhookHandler(providerKey: string): WebhookHandlerRegistration | undefined {\n return getWebhookHandlerRegistry().get(providerKey)\n}\n\nexport function clearWebhookHandlers(): void {\n getWebhookHandlerRegistry().clear()\n}\n"],
5
+ "mappings": "AA6JA,MAAM,uBAAuB;AAC7B,MAAM,uBAAuB;AAE7B,SAAS,qBAAkD;AACzD,QAAM,cAAc;AAGpB,MAAI,CAAC,YAAY,oBAAoB,GAAG;AACtC,gBAAY,oBAAoB,IAAI,oBAAI,IAA4B;AAAA,EACtE;AACA,SAAO,YAAY,oBAAoB;AACzC;AAEA,SAAS,4BAAqE;AAC5E,QAAM,cAAc;AAGpB,MAAI,CAAC,YAAY,oBAAoB,GAAG;AACtC,gBAAY,oBAAoB,IAAI,oBAAI,IAAwC;AAAA,EAClF;AACA,SAAO,YAAY,oBAAoB;AACzC;AAEA,SAAS,WAAW,aAAqB,SAA0B;AACjE,SAAO,UAAU,GAAG,WAAW,IAAI,OAAO,KAAK;AACjD;AAEO,SAAS,uBAAuB,SAAyB,SAA8C;AAC5G,QAAM,kBAAkB,mBAAmB;AAC3C,QAAM,MAAM,WAAW,QAAQ,aAAa,SAAS,OAAO;AAC5D,kBAAgB,IAAI,KAAK,OAAO;AAChC,MAAI,SAAS,SAAS;AAEpB,QAAI,CAAC,gBAAgB,IAAI,QAAQ,WAAW,GAAG;AAC7C,sBAAgB,IAAI,QAAQ,aAAa,OAAO;AAAA,IAClD;AAAA,EACF;AACA,SAAO,MAAM;AACX,oBAAgB,OAAO,GAAG;AAAA,EAC5B;AACF;AAEO,SAAS,kBAAkB,aAAqB,SAA8C;AACnG,QAAM,kBAAkB,mBAAmB;AAC3C,MAAI,SAAS;AACX,WAAO,gBAAgB,IAAI,WAAW,aAAa,OAAO,CAAC,KAAK,gBAAgB,IAAI,WAAW;AAAA,EACjG;AACA,SAAO,gBAAgB,IAAI,WAAW;AACxC;AAEO,SAAS,sBAAwC;AACtD,QAAM,kBAAkB,mBAAmB;AAC3C,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,SAA2B,CAAC;AAClC,aAAW,CAAC,KAAK,OAAO,KAAK,iBAAiB;AAC5C,QAAI,CAAC,IAAI,SAAS,GAAG,KAAK,CAAC,KAAK,IAAI,QAAQ,WAAW,GAAG;AACxD,WAAK,IAAI,QAAQ,WAAW;AAC5B,aAAO,KAAK,OAAO;AAAA,IACrB;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,uBAA6B;AAC3C,qBAAmB,EAAE,MAAM;AAC7B;AAEO,SAAS,uBACd,aACA,SACA,SAIY;AACZ,QAAM,yBAAyB,0BAA0B;AACzD,yBAAuB,IAAI,aAAa;AAAA,IACtC;AAAA,IACA,OAAO,SAAS;AAAA,IAChB,mBAAmB,SAAS;AAAA,EAC9B,CAAC;AACD,SAAO,MAAM;AACX,2BAAuB,OAAO,WAAW;AAAA,EAC3C;AACF;AAEO,SAAS,kBAAkB,aAA6D;AAC7F,SAAO,0BAA0B,EAAE,IAAI,WAAW;AACpD;AAEO,SAAS,uBAA6B;AAC3C,4BAA0B,EAAE,MAAM;AACpC;",
6
+ "names": []
7
+ }
@@ -65,8 +65,10 @@ function findApi(modules, method, pathname) {
65
65
  if (params && handler) return { handler, params, requireAuth: a.requireAuth, requireRoles: a.requireRoles, metadata: a.metadata };
66
66
  } else {
67
67
  const al = a;
68
- if (al.method === method && al.path === pathname) {
69
- return { handler: al.handler, params: {}, metadata: al.metadata };
68
+ if (al.method !== method) continue;
69
+ const params = matchPattern(al.path, pathname);
70
+ if (params) {
71
+ return { handler: al.handler, params, metadata: al.metadata };
70
72
  }
71
73
  }
72
74
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/modules/registry.ts"],
4
- "sourcesContent": ["import type { ReactNode } from 'react'\nimport type { OpenApiRouteDoc, OpenApiMethodDoc } from '@open-mercato/shared/lib/openapi/types'\nimport type { SyncCrudEventResult } from '../lib/crud/sync-event-types'\nimport type { DashboardWidgetModule } from './dashboard/widgets'\nimport type { InjectionAnyWidgetModule, ModuleInjectionTable } from './widgets/injection'\nimport type { IntegrationBundle, IntegrationDefinition } from './integrations/types'\n\n// Context passed to dynamic metadata guards\nexport type RouteVisibilityContext = { path?: string; auth?: any }\n\n// Metadata you can export from page.meta.ts or directly from a server page\nexport type PageMetadata = {\n requireAuth?: boolean\n requireRoles?: readonly string[]\n // Optional fine-grained feature requirements\n requireFeatures?: readonly string[]\n // Titles and grouping (aliases supported)\n title?: string\n titleKey?: string\n pageTitle?: string\n pageTitleKey?: string\n group?: string\n groupKey?: string\n pageGroup?: string\n pageGroupKey?: string\n // Ordering and visuals\n order?: number\n pageOrder?: number\n icon?: ReactNode\n navHidden?: boolean\n // Dynamic flags\n visible?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n enabled?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n // Optional static breadcrumb trail for header\n breadcrumb?: Array<{ label: string; labelKey?: string; href?: string }>\n // Navigation context for tiered navigation:\n // - 'main' (default): Main sidebar business operations\n // - 'admin': Collapsible \"Settings & Admin\" section at bottom of sidebar\n // - 'settings': Hidden from sidebar, only accessible via Settings hub page\n // - 'profile': Profile dropdown items\n pageContext?: 'main' | 'admin' | 'settings' | 'profile'\n placement?: {\n section: string\n sectionLabel?: string\n sectionLabelKey?: string\n order?: number\n }\n}\n\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n\nexport type ApiHandler = (req: Request, ctx?: any) => Promise<Response> | Response\n\nexport type ModuleRoute = {\n pattern?: string\n path?: string\n requireAuth?: boolean\n requireRoles?: string[]\n // Optional fine-grained feature requirements\n requireFeatures?: string[]\n title?: string\n titleKey?: string\n group?: string\n groupKey?: string\n icon?: ReactNode\n order?: number\n priority?: number\n navHidden?: boolean\n visible?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n enabled?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n breadcrumb?: Array<{ label: string; labelKey?: string; href?: string }>\n pageContext?: 'main' | 'admin' | 'settings' | 'profile'\n placement?: {\n section: string\n sectionLabel?: string\n sectionLabelKey?: string\n order?: number\n }\n Component: (props: any) => ReactNode | Promise<ReactNode>\n}\n\nexport type ModuleApiLegacy = {\n method: HttpMethod\n path: string\n handler: ApiHandler\n metadata?: Record<string, unknown>\n docs?: OpenApiMethodDoc\n}\n\nexport type ModuleApiRouteFile = {\n path: string\n handlers: Partial<Record<HttpMethod, ApiHandler>>\n requireAuth?: boolean\n requireRoles?: string[]\n // Optional fine-grained feature requirements for the entire route file\n // Note: per-method feature requirements should be expressed inside metadata\n requireFeatures?: string[]\n docs?: OpenApiRouteDoc\n metadata?: Partial<Record<HttpMethod, unknown>>\n}\n\nexport type ModuleApi = ModuleApiLegacy | ModuleApiRouteFile\n\nexport type ModuleCli = {\n command: string\n run: (argv: string[]) => Promise<void> | void\n}\n\nexport type ModuleInfo = {\n name?: string\n title?: string\n version?: string\n description?: string\n author?: string\n license?: string\n homepage?: string\n copyright?: string\n // Optional hard dependencies: module ids that must be enabled\n requires?: string[]\n // Whether this module can be ejected into the app's src/modules/ for customization\n ejectable?: boolean\n}\n\nexport type ModuleDashboardWidgetEntry = {\n moduleId: string\n key: string\n source: 'app' | 'package'\n loader: () => Promise<DashboardWidgetModule<any>>\n}\n\nexport type ModuleInjectionWidgetEntry = {\n moduleId: string\n key: string\n source: 'app' | 'package'\n loader: () => Promise<InjectionAnyWidgetModule<any, any>>\n}\n\nexport type Module = {\n id: string\n info?: ModuleInfo\n backendRoutes?: ModuleRoute[]\n frontendRoutes?: ModuleRoute[]\n apis?: ModuleApi[]\n cli?: ModuleCli[]\n translations?: Record<string, Record<string, string>>\n // Optional: per-module feature declarations discovered from acl.ts (module root)\n features?: Array<{ id: string; title: string; module: string }>\n // Auto-discovered event subscribers\n subscribers?: Array<{\n id: string\n event: string\n persistent?: boolean\n /** When true, subscriber runs synchronously inside the mutation pipeline */\n sync?: boolean\n /** Execution priority for sync subscribers (lower = earlier). Default: 50 */\n priority?: number\n // Imported function reference; will be registered into event bus\n handler: (payload: any, ctx: any) => Promise<void | SyncCrudEventResult> | void | SyncCrudEventResult\n }>\n // Auto-discovered queue workers\n workers?: Array<{\n id: string\n queue: string\n concurrency: number\n // Imported function reference; will be called by the queue worker\n handler: (job: unknown, ctx: unknown) => Promise<void> | void\n }>\n // Optional: per-module declared entity extensions and custom fields (static)\n // Extensions discovered from data/extensions.ts; Custom fields discovered from ce.ts (entities[].fields)\n entityExtensions?: import('./entities').EntityExtension[]\n customFieldSets?: import('./entities').CustomFieldSet[]\n // Optional: per-module declared custom entities (virtual/logical entities)\n // Discovered from ce.ts (module root). Each entry represents an entityId with optional label/description.\n customEntities?: Array<{ id: string; label?: string; description?: string }>\n dashboardWidgets?: ModuleDashboardWidgetEntry[]\n injectionWidgets?: ModuleInjectionWidgetEntry[]\n injectionTable?: ModuleInjectionTable\n // Optional: per-module vector search configuration (discovered from vector.ts)\n vector?: import('./vector').VectorModuleConfig\n // Optional: module-specific tenant setup configuration (from setup.ts)\n setup?: import('./setup').ModuleSetupConfig\n // Optional: integration marketplace declarations discovered from integration.ts\n integrations?: IntegrationDefinition[]\n bundles?: IntegrationBundle[]\n}\n\nfunction normPath(s: string) {\n return (s.startsWith('/') ? s : '/' + s).replace(/\\/+$/, '') || '/'\n}\n\nfunction matchPattern(pattern: string, pathname: string): Record<string, string | string[]> | undefined {\n const p = normPath(pattern)\n const u = normPath(pathname)\n const pSegs = p.split('/').slice(1)\n const uSegs = u.split('/').slice(1)\n const params: Record<string, string | string[]> = {}\n let i = 0\n for (let j = 0; j < pSegs.length; j++, i++) {\n const seg = pSegs[j]\n const mCatchAll = seg.match(/^\\[\\.\\.\\.(.+)\\]$/)\n const mOptCatch = seg.match(/^\\[\\[\\.\\.\\.(.+)\\]\\]$/)\n const mDyn = seg.match(/^\\[(.+)\\]$/)\n if (mCatchAll) {\n const key = mCatchAll[1]\n if (i >= uSegs.length) return undefined\n params[key] = uSegs.slice(i)\n i = uSegs.length\n return i === uSegs.length ? params : undefined\n } else if (mOptCatch) {\n const key = mOptCatch[1]\n params[key] = i < uSegs.length ? uSegs.slice(i) : []\n i = uSegs.length\n return params\n } else if (mDyn) {\n if (i >= uSegs.length) return undefined\n params[mDyn[1]] = uSegs[i]\n } else {\n if (i >= uSegs.length || uSegs[i] !== seg) return undefined\n }\n }\n if (i !== uSegs.length) return undefined\n return params\n}\n\nfunction getPattern(r: ModuleRoute) {\n return r.pattern ?? r.path ?? '/'\n}\n\nexport function findFrontendMatch(modules: Module[], pathname: string): { route: ModuleRoute; params: Record<string, string | string[]> } | undefined {\n for (const m of modules) {\n const routes = m.frontendRoutes ?? []\n for (const r of routes) {\n const params = matchPattern(getPattern(r), pathname)\n if (params) return { route: r, params }\n }\n }\n}\n\nexport function findBackendMatch(modules: Module[], pathname: string): { route: ModuleRoute; params: Record<string, string | string[]> } | undefined {\n for (const m of modules) {\n const routes = m.backendRoutes ?? []\n for (const r of routes) {\n const params = matchPattern(getPattern(r), pathname)\n if (params) return { route: r, params }\n }\n }\n}\n\nexport function findApi(modules: Module[], method: HttpMethod, pathname: string): { handler: ApiHandler; params: Record<string, string | string[]>; requireAuth?: boolean; requireRoles?: string[]; metadata?: any } | undefined {\n for (const m of modules) {\n const apis = m.apis ?? []\n for (const a of apis) {\n if ('handlers' in a) {\n const params = matchPattern(a.path, pathname)\n const handler = (a.handlers as any)[method]\n if (params && handler) return { handler, params, requireAuth: a.requireAuth, requireRoles: (a as any).requireRoles, metadata: (a as any).metadata }\n } else {\n const al = a as ModuleApiLegacy\n if (al.method === method && al.path === pathname) {\n return { handler: al.handler, params: {}, metadata: al.metadata }\n }\n }\n }\n }\n}\n\n// CLI modules registry - shared between CLI and module workers\nlet _cliModules: Module[] | null = null\n\nexport function registerCliModules(modules: Module[]) {\n if (_cliModules !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] CLI modules re-registered (this may occur during HMR)')\n }\n _cliModules = modules\n}\n\nexport function getCliModules(): Module[] {\n // Return empty array if not registered - allows generate command to work without bootstrap\n return _cliModules ?? []\n}\n\nexport function hasCliModules(): boolean {\n return _cliModules !== null && _cliModules.length > 0\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { ReactNode } from 'react'\nimport type { OpenApiRouteDoc, OpenApiMethodDoc } from '@open-mercato/shared/lib/openapi/types'\nimport type { SyncCrudEventResult } from '../lib/crud/sync-event-types'\nimport type { DashboardWidgetModule } from './dashboard/widgets'\nimport type { InjectionAnyWidgetModule, ModuleInjectionTable } from './widgets/injection'\nimport type { IntegrationBundle, IntegrationDefinition } from './integrations/types'\n\n// Context passed to dynamic metadata guards\nexport type RouteVisibilityContext = { path?: string; auth?: any }\n\n// Metadata you can export from page.meta.ts or directly from a server page\nexport type PageMetadata = {\n requireAuth?: boolean\n requireRoles?: readonly string[]\n // Optional fine-grained feature requirements\n requireFeatures?: readonly string[]\n // Titles and grouping (aliases supported)\n title?: string\n titleKey?: string\n pageTitle?: string\n pageTitleKey?: string\n group?: string\n groupKey?: string\n pageGroup?: string\n pageGroupKey?: string\n // Ordering and visuals\n order?: number\n pageOrder?: number\n icon?: ReactNode\n navHidden?: boolean\n // Dynamic flags\n visible?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n enabled?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n // Optional static breadcrumb trail for header\n breadcrumb?: Array<{ label: string; labelKey?: string; href?: string }>\n // Navigation context for tiered navigation:\n // - 'main' (default): Main sidebar business operations\n // - 'admin': Collapsible \"Settings & Admin\" section at bottom of sidebar\n // - 'settings': Hidden from sidebar, only accessible via Settings hub page\n // - 'profile': Profile dropdown items\n pageContext?: 'main' | 'admin' | 'settings' | 'profile'\n placement?: {\n section: string\n sectionLabel?: string\n sectionLabelKey?: string\n order?: number\n }\n}\n\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n\nexport type ApiHandler = (req: Request, ctx?: any) => Promise<Response> | Response\n\nexport type ModuleRoute = {\n pattern?: string\n path?: string\n requireAuth?: boolean\n requireRoles?: string[]\n // Optional fine-grained feature requirements\n requireFeatures?: string[]\n title?: string\n titleKey?: string\n group?: string\n groupKey?: string\n icon?: ReactNode\n order?: number\n priority?: number\n navHidden?: boolean\n visible?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n enabled?: (ctx: RouteVisibilityContext) => boolean | Promise<boolean>\n breadcrumb?: Array<{ label: string; labelKey?: string; href?: string }>\n pageContext?: 'main' | 'admin' | 'settings' | 'profile'\n placement?: {\n section: string\n sectionLabel?: string\n sectionLabelKey?: string\n order?: number\n }\n Component: (props: any) => ReactNode | Promise<ReactNode>\n}\n\nexport type ModuleApiLegacy = {\n method: HttpMethod\n path: string\n handler: ApiHandler\n metadata?: Record<string, unknown>\n docs?: OpenApiMethodDoc\n}\n\nexport type ModuleApiRouteFile = {\n path: string\n handlers: Partial<Record<HttpMethod, ApiHandler>>\n requireAuth?: boolean\n requireRoles?: string[]\n // Optional fine-grained feature requirements for the entire route file\n // Note: per-method feature requirements should be expressed inside metadata\n requireFeatures?: string[]\n docs?: OpenApiRouteDoc\n metadata?: Partial<Record<HttpMethod, unknown>>\n}\n\nexport type ModuleApi = ModuleApiLegacy | ModuleApiRouteFile\n\nexport type ModuleCli = {\n command: string\n run: (argv: string[]) => Promise<void> | void\n}\n\nexport type ModuleInfo = {\n name?: string\n title?: string\n version?: string\n description?: string\n author?: string\n license?: string\n homepage?: string\n copyright?: string\n // Optional hard dependencies: module ids that must be enabled\n requires?: string[]\n // Whether this module can be ejected into the app's src/modules/ for customization\n ejectable?: boolean\n}\n\nexport type ModuleDashboardWidgetEntry = {\n moduleId: string\n key: string\n source: 'app' | 'package'\n loader: () => Promise<DashboardWidgetModule<any>>\n}\n\nexport type ModuleInjectionWidgetEntry = {\n moduleId: string\n key: string\n source: 'app' | 'package'\n loader: () => Promise<InjectionAnyWidgetModule<any, any>>\n}\n\nexport type Module = {\n id: string\n info?: ModuleInfo\n backendRoutes?: ModuleRoute[]\n frontendRoutes?: ModuleRoute[]\n apis?: ModuleApi[]\n cli?: ModuleCli[]\n translations?: Record<string, Record<string, string>>\n // Optional: per-module feature declarations discovered from acl.ts (module root)\n features?: Array<{ id: string; title: string; module: string }>\n // Auto-discovered event subscribers\n subscribers?: Array<{\n id: string\n event: string\n persistent?: boolean\n /** When true, subscriber runs synchronously inside the mutation pipeline */\n sync?: boolean\n /** Execution priority for sync subscribers (lower = earlier). Default: 50 */\n priority?: number\n // Imported function reference; will be registered into event bus\n handler: (payload: any, ctx: any) => Promise<void | SyncCrudEventResult> | void | SyncCrudEventResult\n }>\n // Auto-discovered queue workers\n workers?: Array<{\n id: string\n queue: string\n concurrency: number\n // Imported function reference; will be called by the queue worker\n handler: (job: unknown, ctx: unknown) => Promise<void> | void\n }>\n // Optional: per-module declared entity extensions and custom fields (static)\n // Extensions discovered from data/extensions.ts; Custom fields discovered from ce.ts (entities[].fields)\n entityExtensions?: import('./entities').EntityExtension[]\n customFieldSets?: import('./entities').CustomFieldSet[]\n // Optional: per-module declared custom entities (virtual/logical entities)\n // Discovered from ce.ts (module root). Each entry represents an entityId with optional label/description.\n customEntities?: Array<{ id: string; label?: string; description?: string }>\n dashboardWidgets?: ModuleDashboardWidgetEntry[]\n injectionWidgets?: ModuleInjectionWidgetEntry[]\n injectionTable?: ModuleInjectionTable\n // Optional: per-module vector search configuration (discovered from vector.ts)\n vector?: import('./vector').VectorModuleConfig\n // Optional: module-specific tenant setup configuration (from setup.ts)\n setup?: import('./setup').ModuleSetupConfig\n // Optional: integration marketplace declarations discovered from integration.ts\n integrations?: IntegrationDefinition[]\n bundles?: IntegrationBundle[]\n}\n\nfunction normPath(s: string) {\n return (s.startsWith('/') ? s : '/' + s).replace(/\\/+$/, '') || '/'\n}\n\nfunction matchPattern(pattern: string, pathname: string): Record<string, string | string[]> | undefined {\n const p = normPath(pattern)\n const u = normPath(pathname)\n const pSegs = p.split('/').slice(1)\n const uSegs = u.split('/').slice(1)\n const params: Record<string, string | string[]> = {}\n let i = 0\n for (let j = 0; j < pSegs.length; j++, i++) {\n const seg = pSegs[j]\n const mCatchAll = seg.match(/^\\[\\.\\.\\.(.+)\\]$/)\n const mOptCatch = seg.match(/^\\[\\[\\.\\.\\.(.+)\\]\\]$/)\n const mDyn = seg.match(/^\\[(.+)\\]$/)\n if (mCatchAll) {\n const key = mCatchAll[1]\n if (i >= uSegs.length) return undefined\n params[key] = uSegs.slice(i)\n i = uSegs.length\n return i === uSegs.length ? params : undefined\n } else if (mOptCatch) {\n const key = mOptCatch[1]\n params[key] = i < uSegs.length ? uSegs.slice(i) : []\n i = uSegs.length\n return params\n } else if (mDyn) {\n if (i >= uSegs.length) return undefined\n params[mDyn[1]] = uSegs[i]\n } else {\n if (i >= uSegs.length || uSegs[i] !== seg) return undefined\n }\n }\n if (i !== uSegs.length) return undefined\n return params\n}\n\nfunction getPattern(r: ModuleRoute) {\n return r.pattern ?? r.path ?? '/'\n}\n\nexport function findFrontendMatch(modules: Module[], pathname: string): { route: ModuleRoute; params: Record<string, string | string[]> } | undefined {\n for (const m of modules) {\n const routes = m.frontendRoutes ?? []\n for (const r of routes) {\n const params = matchPattern(getPattern(r), pathname)\n if (params) return { route: r, params }\n }\n }\n}\n\nexport function findBackendMatch(modules: Module[], pathname: string): { route: ModuleRoute; params: Record<string, string | string[]> } | undefined {\n for (const m of modules) {\n const routes = m.backendRoutes ?? []\n for (const r of routes) {\n const params = matchPattern(getPattern(r), pathname)\n if (params) return { route: r, params }\n }\n }\n}\n\nexport function findApi(modules: Module[], method: HttpMethod, pathname: string): { handler: ApiHandler; params: Record<string, string | string[]>; requireAuth?: boolean; requireRoles?: string[]; metadata?: any } | undefined {\n for (const m of modules) {\n const apis = m.apis ?? []\n for (const a of apis) {\n if ('handlers' in a) {\n const params = matchPattern(a.path, pathname)\n const handler = (a.handlers as any)[method]\n if (params && handler) return { handler, params, requireAuth: a.requireAuth, requireRoles: (a as any).requireRoles, metadata: (a as any).metadata }\n } else {\n const al = a as ModuleApiLegacy\n if (al.method !== method) continue\n const params = matchPattern(al.path, pathname)\n if (params) {\n return { handler: al.handler, params, metadata: al.metadata }\n }\n }\n }\n }\n}\n\n// CLI modules registry - shared between CLI and module workers\nlet _cliModules: Module[] | null = null\n\nexport function registerCliModules(modules: Module[]) {\n if (_cliModules !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] CLI modules re-registered (this may occur during HMR)')\n }\n _cliModules = modules\n}\n\nexport function getCliModules(): Module[] {\n // Return empty array if not registered - allows generate command to work without bootstrap\n return _cliModules ?? []\n}\n\nexport function hasCliModules(): boolean {\n return _cliModules !== null && _cliModules.length > 0\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/shared",
3
- "version": "0.4.7-develop-0a657b411f",
3
+ "version": "0.4.7-develop-11728c8558",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -0,0 +1,13 @@
1
+ export async function readJsonSafe<T>(
2
+ source: Request | Response | string,
3
+ fallback: T | null = null,
4
+ ): Promise<T | null> {
5
+ const raw = typeof source === 'string' ? source : await source.text()
6
+ if (!raw) return fallback
7
+
8
+ try {
9
+ return JSON.parse(raw) as T
10
+ } catch {
11
+ return fallback
12
+ }
13
+ }
@@ -17,7 +17,21 @@ export type I18nContextValue = {
17
17
  t: TranslateFn
18
18
  }
19
19
 
20
- const I18nContext = createContext<I18nContextValue | null>(null)
20
+ const I18N_CONTEXT_KEY = '__openMercatoI18nContext'
21
+
22
+ type GlobalI18nContextStore = typeof globalThis & {
23
+ [I18N_CONTEXT_KEY]?: ReturnType<typeof createContext<I18nContextValue | null>>
24
+ }
25
+
26
+ function getI18nContext() {
27
+ const store = globalThis as GlobalI18nContextStore
28
+ if (!store[I18N_CONTEXT_KEY]) {
29
+ store[I18N_CONTEXT_KEY] = createContext<I18nContextValue | null>(null)
30
+ }
31
+ return store[I18N_CONTEXT_KEY]
32
+ }
33
+
34
+ const I18nContext = getI18nContext()
21
35
 
22
36
  function format(template: string, params?: TranslateParams) {
23
37
  if (!params) return template
@@ -40,12 +40,28 @@ export interface CredentialFieldVisibleWhen {
40
40
  equals: string | number | boolean
41
41
  }
42
42
 
43
+ export interface IntegrationCredentialWebhookHelp {
44
+ kind: 'webhook_setup'
45
+ title: string
46
+ summary: string
47
+ endpointPath: string
48
+ dashboardPathLabel: string
49
+ steps: string[]
50
+ events?: string[]
51
+ localDevelopment?: {
52
+ tunnelCommand: string
53
+ publicUrlExample: string
54
+ note?: string
55
+ }
56
+ }
57
+
43
58
  export interface IntegrationCredentialFieldBase {
44
59
  key: string
45
60
  label: string
46
61
  required?: boolean
47
62
  placeholder?: string
48
63
  helpText?: string
64
+ helpDetails?: IntegrationCredentialWebhookHelp
49
65
  visibleWhen?: CredentialFieldVisibleWhen
50
66
  }
51
67
 
@@ -115,6 +131,15 @@ export interface IntegrationBundle {
115
131
  healthCheck?: IntegrationHealthCheckConfig
116
132
  }
117
133
 
134
+ export interface IntegrationDetailPageConfig {
135
+ /**
136
+ * UMES widget spot rendered on the integration detail page.
137
+ * Widgets registered here can render inline blocks, grouped panels,
138
+ * or additional tabs via `placement.kind`.
139
+ */
140
+ widgetSpotId?: string
141
+ }
142
+
118
143
  export interface IntegrationDefinition {
119
144
  id: string
120
145
  title: string
@@ -130,8 +155,10 @@ export interface IntegrationDefinition {
130
155
  package?: string
131
156
  version?: string
132
157
  author?: string
158
+ company?: string
133
159
  license?: string
134
160
  tags?: string[]
161
+ detailPage?: IntegrationDetailPageConfig
135
162
  credentials?: IntegrationCredentialsSchema
136
163
  healthCheck?: IntegrationHealthCheckConfig
137
164
  }
@@ -210,3 +237,9 @@ export function resolveIntegrationCredentialsSchema(integrationId: string): Inte
210
237
  export function getIntegrationTitle(integrationId: string): string {
211
238
  return integrationRegistry.get(integrationId)?.title ?? integrationId
212
239
  }
240
+
241
+ export const LEGACY_INTEGRATION_DETAIL_TABS_SPOT_ID = 'integrations.detail:tabs'
242
+
243
+ export function buildIntegrationDetailWidgetSpotId(integrationId: string): string {
244
+ return `integrations.detail:${integrationId}`
245
+ }
@@ -0,0 +1 @@
1
+ export * from './types'
@@ -0,0 +1,250 @@
1
+ // ── Unified Payment Status ──────────────────────────────────────────────────
2
+
3
+ export type UnifiedPaymentStatus =
4
+ | 'pending'
5
+ | 'authorized'
6
+ | 'captured'
7
+ | 'partially_captured'
8
+ | 'refunded'
9
+ | 'partially_refunded'
10
+ | 'cancelled'
11
+ | 'failed'
12
+ | 'expired'
13
+ | 'unknown'
14
+
15
+ // ── GatewayAdapter Interface ────────────────────────────────────────────────
16
+
17
+ export interface GatewayAdapter {
18
+ readonly providerKey: string
19
+
20
+ /** Create a payment session / payment intent */
21
+ createSession(input: CreateSessionInput): Promise<CreateSessionResult>
22
+
23
+ /** Capture an authorized payment */
24
+ capture(input: CaptureInput): Promise<CaptureResult>
25
+
26
+ /** Refund a captured payment (full or partial) */
27
+ refund(input: RefundInput): Promise<RefundResult>
28
+
29
+ /** Cancel / void an authorized payment before capture */
30
+ cancel(input: CancelInput): Promise<CancelResult>
31
+
32
+ /** Get current payment status from provider */
33
+ getStatus(input: GetStatusInput): Promise<GatewayPaymentStatus>
34
+
35
+ /** Verify and parse an inbound webhook event */
36
+ verifyWebhook(input: VerifyWebhookInput): Promise<WebhookEvent>
37
+
38
+ /** Map provider status to unified status */
39
+ mapStatus(providerStatus: string, eventType?: string): UnifiedPaymentStatus
40
+ }
41
+
42
+ // ── Input / Output Types ────────────────────────────────────────────────────
43
+
44
+ export interface CreateSessionInput {
45
+ orderId?: string
46
+ paymentId: string
47
+ tenantId: string
48
+ organizationId: string
49
+ amount: number
50
+ currencyCode: string
51
+ captureMethod?: 'automatic' | 'manual'
52
+ paymentTypes?: string[]
53
+ description?: string
54
+ successUrl?: string
55
+ cancelUrl?: string
56
+ metadata?: Record<string, unknown>
57
+ credentials: Record<string, unknown>
58
+ lineItems?: SessionLineItem[]
59
+ }
60
+
61
+ export interface SessionLineItem {
62
+ name: string
63
+ quantity: number
64
+ unitAmount: number
65
+ currencyCode: string
66
+ }
67
+
68
+ export interface CreateSessionResult {
69
+ sessionId: string
70
+ clientSecret?: string
71
+ redirectUrl?: string
72
+ status: UnifiedPaymentStatus
73
+ providerData?: Record<string, unknown>
74
+ }
75
+
76
+ export interface CaptureInput {
77
+ sessionId: string
78
+ amount?: number
79
+ credentials: Record<string, unknown>
80
+ metadata?: Record<string, unknown>
81
+ }
82
+
83
+ export interface CaptureResult {
84
+ status: UnifiedPaymentStatus
85
+ capturedAmount: number
86
+ providerData?: Record<string, unknown>
87
+ }
88
+
89
+ export interface RefundInput {
90
+ sessionId: string
91
+ amount?: number
92
+ reason?: string
93
+ credentials: Record<string, unknown>
94
+ metadata?: Record<string, unknown>
95
+ }
96
+
97
+ export interface RefundResult {
98
+ refundId: string
99
+ status: UnifiedPaymentStatus
100
+ refundedAmount: number
101
+ providerData?: Record<string, unknown>
102
+ }
103
+
104
+ export interface CancelInput {
105
+ sessionId: string
106
+ reason?: string
107
+ credentials: Record<string, unknown>
108
+ }
109
+
110
+ export interface CancelResult {
111
+ status: UnifiedPaymentStatus
112
+ providerData?: Record<string, unknown>
113
+ }
114
+
115
+ export interface GetStatusInput {
116
+ sessionId: string
117
+ credentials: Record<string, unknown>
118
+ }
119
+
120
+ export interface GatewayPaymentStatus {
121
+ status: UnifiedPaymentStatus
122
+ amount: number
123
+ amountReceived: number
124
+ currencyCode: string
125
+ providerData?: Record<string, unknown>
126
+ }
127
+
128
+ export interface VerifyWebhookInput {
129
+ rawBody: string | Buffer
130
+ headers: Record<string, string | string[] | undefined>
131
+ credentials: Record<string, unknown>
132
+ }
133
+
134
+ export interface WebhookEvent {
135
+ eventType: string
136
+ eventId: string
137
+ data: Record<string, unknown>
138
+ idempotencyKey: string
139
+ timestamp: Date
140
+ }
141
+
142
+ // ── Webhook Handler ─────────────────────────────────────────────────────────
143
+
144
+ export interface WebhookHandlerRegistration {
145
+ handler: (input: VerifyWebhookInput) => Promise<WebhookEvent>
146
+ queue?: string
147
+ readSessionIdHint?: (payload: Record<string, unknown> | null) => string | null
148
+ }
149
+
150
+ // ── Adapter Registry Options ────────────────────────────────────────────────
151
+
152
+ export interface RegisterAdapterOptions {
153
+ version?: string
154
+ }
155
+
156
+ // ── Registries ──────────────────────────────────────────────────────────────
157
+
158
+ const ADAPTER_REGISTRY_KEY = '__openMercatoPaymentGatewayAdapters__'
159
+ const WEBHOOK_REGISTRY_KEY = '__openMercatoPaymentGatewayWebhookHandlers__'
160
+
161
+ function getAdapterRegistry(): Map<string, GatewayAdapter> {
162
+ const globalState = globalThis as typeof globalThis & {
163
+ [ADAPTER_REGISTRY_KEY]?: Map<string, GatewayAdapter>
164
+ }
165
+ if (!globalState[ADAPTER_REGISTRY_KEY]) {
166
+ globalState[ADAPTER_REGISTRY_KEY] = new Map<string, GatewayAdapter>()
167
+ }
168
+ return globalState[ADAPTER_REGISTRY_KEY]
169
+ }
170
+
171
+ function getWebhookHandlerRegistry(): Map<string, WebhookHandlerRegistration> {
172
+ const globalState = globalThis as typeof globalThis & {
173
+ [WEBHOOK_REGISTRY_KEY]?: Map<string, WebhookHandlerRegistration>
174
+ }
175
+ if (!globalState[WEBHOOK_REGISTRY_KEY]) {
176
+ globalState[WEBHOOK_REGISTRY_KEY] = new Map<string, WebhookHandlerRegistration>()
177
+ }
178
+ return globalState[WEBHOOK_REGISTRY_KEY]
179
+ }
180
+
181
+ function adapterKey(providerKey: string, version?: string): string {
182
+ return version ? `${providerKey}:${version}` : providerKey
183
+ }
184
+
185
+ export function registerGatewayAdapter(adapter: GatewayAdapter, options?: RegisterAdapterOptions): () => void {
186
+ const adapterRegistry = getAdapterRegistry()
187
+ const key = adapterKey(adapter.providerKey, options?.version)
188
+ adapterRegistry.set(key, adapter)
189
+ if (options?.version) {
190
+ // Also register as default if no default exists
191
+ if (!adapterRegistry.has(adapter.providerKey)) {
192
+ adapterRegistry.set(adapter.providerKey, adapter)
193
+ }
194
+ }
195
+ return () => {
196
+ adapterRegistry.delete(key)
197
+ }
198
+ }
199
+
200
+ export function getGatewayAdapter(providerKey: string, version?: string): GatewayAdapter | undefined {
201
+ const adapterRegistry = getAdapterRegistry()
202
+ if (version) {
203
+ return adapterRegistry.get(adapterKey(providerKey, version)) ?? adapterRegistry.get(providerKey)
204
+ }
205
+ return adapterRegistry.get(providerKey)
206
+ }
207
+
208
+ export function listGatewayAdapters(): GatewayAdapter[] {
209
+ const adapterRegistry = getAdapterRegistry()
210
+ const seen = new Set<string>()
211
+ const result: GatewayAdapter[] = []
212
+ for (const [key, adapter] of adapterRegistry) {
213
+ if (!key.includes(':') && !seen.has(adapter.providerKey)) {
214
+ seen.add(adapter.providerKey)
215
+ result.push(adapter)
216
+ }
217
+ }
218
+ return result
219
+ }
220
+
221
+ export function clearGatewayAdapters(): void {
222
+ getAdapterRegistry().clear()
223
+ }
224
+
225
+ export function registerWebhookHandler(
226
+ providerKey: string,
227
+ handler: (input: VerifyWebhookInput) => Promise<WebhookEvent>,
228
+ options?: {
229
+ queue?: string
230
+ readSessionIdHint?: (payload: Record<string, unknown> | null) => string | null
231
+ },
232
+ ): () => void {
233
+ const webhookHandlerRegistry = getWebhookHandlerRegistry()
234
+ webhookHandlerRegistry.set(providerKey, {
235
+ handler,
236
+ queue: options?.queue,
237
+ readSessionIdHint: options?.readSessionIdHint,
238
+ })
239
+ return () => {
240
+ webhookHandlerRegistry.delete(providerKey)
241
+ }
242
+ }
243
+
244
+ export function getWebhookHandler(providerKey: string): WebhookHandlerRegistration | undefined {
245
+ return getWebhookHandlerRegistry().get(providerKey)
246
+ }
247
+
248
+ export function clearWebhookHandlers(): void {
249
+ getWebhookHandlerRegistry().clear()
250
+ }
@@ -256,8 +256,10 @@ export function findApi(modules: Module[], method: HttpMethod, pathname: string)
256
256
  if (params && handler) return { handler, params, requireAuth: a.requireAuth, requireRoles: (a as any).requireRoles, metadata: (a as any).metadata }
257
257
  } else {
258
258
  const al = a as ModuleApiLegacy
259
- if (al.method === method && al.path === pathname) {
260
- return { handler: al.handler, params: {}, metadata: al.metadata }
259
+ if (al.method !== method) continue
260
+ const params = matchPattern(al.path, pathname)
261
+ if (params) {
262
+ return { handler: al.handler, params, metadata: al.metadata }
261
263
  }
262
264
  }
263
265
  }