@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.
- package/dist/lib/http/readJsonSafe.js +13 -0
- package/dist/lib/http/readJsonSafe.js.map +7 -0
- package/dist/lib/i18n/context.js +9 -1
- package/dist/lib/i18n/context.js.map +2 -2
- package/dist/lib/version.js +1 -1
- package/dist/lib/version.js.map +1 -1
- package/dist/modules/integrations/types.js +6 -0
- package/dist/modules/integrations/types.js.map +2 -2
- package/dist/modules/payment_gateways/index.js +2 -0
- package/dist/modules/payment_gateways/index.js.map +7 -0
- package/dist/modules/payment_gateways/types.js +81 -0
- package/dist/modules/payment_gateways/types.js.map +7 -0
- package/dist/modules/registry.js +4 -2
- package/dist/modules/registry.js.map +2 -2
- package/package.json +1 -1
- package/src/lib/http/readJsonSafe.ts +13 -0
- package/src/lib/i18n/context.tsx +15 -1
- package/src/modules/integrations/types.ts +33 -0
- package/src/modules/payment_gateways/index.ts +1 -0
- package/src/modules/payment_gateways/types.ts +250 -0
- package/src/modules/registry.ts +4 -2
|
@@ -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
|
+
}
|
package/dist/lib/i18n/context.js
CHANGED
|
@@ -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
|
|
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
|
|
5
|
-
"mappings": ";
|
|
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
|
}
|
package/dist/lib/version.js
CHANGED
package/dist/lib/version.js.map
CHANGED
|
@@ -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-
|
|
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": "
|
|
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,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
|
+
}
|
package/dist/modules/registry.js
CHANGED
|
@@ -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
|
|
69
|
-
|
|
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": "AA0LA,SAAS,SAAS,GAAW;AAC3B,UAAQ,EAAE,WAAW,GAAG,IAAI,IAAI,MAAM,GAAG,QAAQ,QAAQ,EAAE,KAAK;AAClE;AAEA,SAAS,aAAa,SAAiB,UAAiE;AACtG,QAAM,IAAI,SAAS,OAAO;AAC1B,QAAM,IAAI,SAAS,QAAQ;AAC3B,QAAM,QAAQ,EAAE,MAAM,GAAG,EAAE,MAAM,CAAC;AAClC,QAAM,QAAQ,EAAE,MAAM,GAAG,EAAE,MAAM,CAAC;AAClC,QAAM,SAA4C,CAAC;AACnD,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,KAAK;AAC1C,UAAM,MAAM,MAAM,CAAC;AACnB,UAAM,YAAY,IAAI,MAAM,kBAAkB;AAC9C,UAAM,YAAY,IAAI,MAAM,sBAAsB;AAClD,UAAM,OAAO,IAAI,MAAM,YAAY;AACnC,QAAI,WAAW;AACb,YAAM,MAAM,UAAU,CAAC;AACvB,UAAI,KAAK,MAAM,OAAQ,QAAO;AAC9B,aAAO,GAAG,IAAI,MAAM,MAAM,CAAC;AAC3B,UAAI,MAAM;AACV,aAAO,MAAM,MAAM,SAAS,SAAS;AAAA,IACvC,WAAW,WAAW;AACpB,YAAM,MAAM,UAAU,CAAC;AACvB,aAAO,GAAG,IAAI,IAAI,MAAM,SAAS,MAAM,MAAM,CAAC,IAAI,CAAC;AACnD,UAAI,MAAM;AACV,aAAO;AAAA,IACT,WAAW,MAAM;AACf,UAAI,KAAK,MAAM,OAAQ,QAAO;AAC9B,aAAO,KAAK,CAAC,CAAC,IAAI,MAAM,CAAC;AAAA,IAC3B,OAAO;AACL,UAAI,KAAK,MAAM,UAAU,MAAM,CAAC,MAAM,IAAK,QAAO;AAAA,IACpD;AAAA,EACF;AACA,MAAI,MAAM,MAAM,OAAQ,QAAO;AAC/B,SAAO;AACT;AAEA,SAAS,WAAW,GAAgB;AAClC,SAAO,EAAE,WAAW,EAAE,QAAQ;AAChC;AAEO,SAAS,kBAAkB,SAAmB,UAAiG;AACpJ,aAAW,KAAK,SAAS;AACvB,UAAM,SAAS,EAAE,kBAAkB,CAAC;AACpC,eAAW,KAAK,QAAQ;AACtB,YAAM,SAAS,aAAa,WAAW,CAAC,GAAG,QAAQ;AACnD,UAAI,OAAQ,QAAO,EAAE,OAAO,GAAG,OAAO;AAAA,IACxC;AAAA,EACF;AACF;AAEO,SAAS,iBAAiB,SAAmB,UAAiG;AACnJ,aAAW,KAAK,SAAS;AACvB,UAAM,SAAS,EAAE,iBAAiB,CAAC;AACnC,eAAW,KAAK,QAAQ;AACtB,YAAM,SAAS,aAAa,WAAW,CAAC,GAAG,QAAQ;AACnD,UAAI,OAAQ,QAAO,EAAE,OAAO,GAAG,OAAO;AAAA,IACxC;AAAA,EACF;AACF;AAEO,SAAS,QAAQ,SAAmB,QAAoB,UAAkK;AAC/N,aAAW,KAAK,SAAS;AACvB,UAAM,OAAO,EAAE,QAAQ,CAAC;AACxB,eAAW,KAAK,MAAM;AACpB,UAAI,cAAc,GAAG;AACnB,cAAM,SAAS,aAAa,EAAE,MAAM,QAAQ;AAC5C,cAAM,UAAW,EAAE,SAAiB,MAAM;AAC1C,YAAI,UAAU,QAAS,QAAO,EAAE,SAAS,QAAQ,aAAa,EAAE,aAAa,cAAe,EAAU,cAAc,UAAW,EAAU,SAAS;AAAA,MACpJ,OAAO;AACL,cAAM,KAAK;AACX,YAAI,GAAG,WAAW,
|
|
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": "AA0LA,SAAS,SAAS,GAAW;AAC3B,UAAQ,EAAE,WAAW,GAAG,IAAI,IAAI,MAAM,GAAG,QAAQ,QAAQ,EAAE,KAAK;AAClE;AAEA,SAAS,aAAa,SAAiB,UAAiE;AACtG,QAAM,IAAI,SAAS,OAAO;AAC1B,QAAM,IAAI,SAAS,QAAQ;AAC3B,QAAM,QAAQ,EAAE,MAAM,GAAG,EAAE,MAAM,CAAC;AAClC,QAAM,QAAQ,EAAE,MAAM,GAAG,EAAE,MAAM,CAAC;AAClC,QAAM,SAA4C,CAAC;AACnD,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,KAAK;AAC1C,UAAM,MAAM,MAAM,CAAC;AACnB,UAAM,YAAY,IAAI,MAAM,kBAAkB;AAC9C,UAAM,YAAY,IAAI,MAAM,sBAAsB;AAClD,UAAM,OAAO,IAAI,MAAM,YAAY;AACnC,QAAI,WAAW;AACb,YAAM,MAAM,UAAU,CAAC;AACvB,UAAI,KAAK,MAAM,OAAQ,QAAO;AAC9B,aAAO,GAAG,IAAI,MAAM,MAAM,CAAC;AAC3B,UAAI,MAAM;AACV,aAAO,MAAM,MAAM,SAAS,SAAS;AAAA,IACvC,WAAW,WAAW;AACpB,YAAM,MAAM,UAAU,CAAC;AACvB,aAAO,GAAG,IAAI,IAAI,MAAM,SAAS,MAAM,MAAM,CAAC,IAAI,CAAC;AACnD,UAAI,MAAM;AACV,aAAO;AAAA,IACT,WAAW,MAAM;AACf,UAAI,KAAK,MAAM,OAAQ,QAAO;AAC9B,aAAO,KAAK,CAAC,CAAC,IAAI,MAAM,CAAC;AAAA,IAC3B,OAAO;AACL,UAAI,KAAK,MAAM,UAAU,MAAM,CAAC,MAAM,IAAK,QAAO;AAAA,IACpD;AAAA,EACF;AACA,MAAI,MAAM,MAAM,OAAQ,QAAO;AAC/B,SAAO;AACT;AAEA,SAAS,WAAW,GAAgB;AAClC,SAAO,EAAE,WAAW,EAAE,QAAQ;AAChC;AAEO,SAAS,kBAAkB,SAAmB,UAAiG;AACpJ,aAAW,KAAK,SAAS;AACvB,UAAM,SAAS,EAAE,kBAAkB,CAAC;AACpC,eAAW,KAAK,QAAQ;AACtB,YAAM,SAAS,aAAa,WAAW,CAAC,GAAG,QAAQ;AACnD,UAAI,OAAQ,QAAO,EAAE,OAAO,GAAG,OAAO;AAAA,IACxC;AAAA,EACF;AACF;AAEO,SAAS,iBAAiB,SAAmB,UAAiG;AACnJ,aAAW,KAAK,SAAS;AACvB,UAAM,SAAS,EAAE,iBAAiB,CAAC;AACnC,eAAW,KAAK,QAAQ;AACtB,YAAM,SAAS,aAAa,WAAW,CAAC,GAAG,QAAQ;AACnD,UAAI,OAAQ,QAAO,EAAE,OAAO,GAAG,OAAO;AAAA,IACxC;AAAA,EACF;AACF;AAEO,SAAS,QAAQ,SAAmB,QAAoB,UAAkK;AAC/N,aAAW,KAAK,SAAS;AACvB,UAAM,OAAO,EAAE,QAAQ,CAAC;AACxB,eAAW,KAAK,MAAM;AACpB,UAAI,cAAc,GAAG;AACnB,cAAM,SAAS,aAAa,EAAE,MAAM,QAAQ;AAC5C,cAAM,UAAW,EAAE,SAAiB,MAAM;AAC1C,YAAI,UAAU,QAAS,QAAO,EAAE,SAAS,QAAQ,aAAa,EAAE,aAAa,cAAe,EAAU,cAAc,UAAW,EAAU,SAAS;AAAA,MACpJ,OAAO;AACL,cAAM,KAAK;AACX,YAAI,GAAG,WAAW,OAAQ;AAC1B,cAAM,SAAS,aAAa,GAAG,MAAM,QAAQ;AAC7C,YAAI,QAAQ;AACV,iBAAO,EAAE,SAAS,GAAG,SAAS,QAAQ,UAAU,GAAG,SAAS;AAAA,QAC9D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAGA,IAAI,cAA+B;AAE5B,SAAS,mBAAmB,SAAmB;AACpD,MAAI,gBAAgB,QAAQ,QAAQ,IAAI,aAAa,eAAe;AAClE,YAAQ,MAAM,mEAAmE;AAAA,EACnF;AACA,gBAAc;AAChB;AAEO,SAAS,gBAA0B;AAExC,SAAO,eAAe,CAAC;AACzB;AAEO,SAAS,gBAAyB;AACvC,SAAO,gBAAgB,QAAQ,YAAY,SAAS;AACtD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -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
|
+
}
|
package/src/lib/i18n/context.tsx
CHANGED
|
@@ -17,7 +17,21 @@ export type I18nContextValue = {
|
|
|
17
17
|
t: TranslateFn
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
const
|
|
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
|
+
}
|
package/src/modules/registry.ts
CHANGED
|
@@ -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
|
|
260
|
-
|
|
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
|
}
|