@open-mercato/shared 0.6.5-develop.4384.1.ce2ec6eaaa → 0.6.5-develop.4393.1.de282b5dfd

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.
@@ -45,6 +45,10 @@ function useT() {
45
45
  if (!ctx) throw new Error("useT must be used within I18nProvider");
46
46
  return ctx.t;
47
47
  }
48
+ function useOptionalT() {
49
+ const ctx = useContext(I18nContext);
50
+ return ctx?.t;
51
+ }
48
52
  function useLocale() {
49
53
  const ctx = useContext(I18nContext);
50
54
  if (!ctx) throw new Error("useLocale must be used within I18nProvider");
@@ -53,6 +57,7 @@ function useLocale() {
53
57
  export {
54
58
  I18nProvider,
55
59
  useLocale,
60
+ useOptionalT,
56
61
  useT
57
62
  };
58
63
  //# sourceMappingURL=context.js.map
@@ -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 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;",
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\n/**\n * Like `useT`, but returns `undefined` instead of throwing when no\n * `I18nProvider` is in scope. Use where a translator is desirable but not\n * guaranteed (e.g. plumbing `t` into side-effect handlers that may run before\n * the provider mounts) \u2014 callers MUST provide a fallback.\n */\nexport function useOptionalT(): TranslateFn | undefined {\n const ctx = useContext(I18nContext)\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;AAQO,SAAS,eAAwC;AACtD,QAAM,MAAM,WAAW,WAAW;AAClC,SAAO,KAAK;AACd;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.6.5-develop.4384.1.ce2ec6eaaa";
1
+ const APP_VERSION = "0.6.5-develop.4393.1.de282b5dfd";
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.6.5-develop.4384.1.ce2ec6eaaa'\nexport const appVersion = APP_VERSION\n"],
4
+ "sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.6.5-develop.4393.1.de282b5dfd'\nexport const appVersion = APP_VERSION\n"],
5
5
  "mappings": "AACO,MAAM,cAAc;AACpB,MAAM,aAAa;",
6
6
  "names": []
7
7
  }
@@ -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 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 * Built-in tabs to hide for this integration detail page.\n * Provider modules can replace these with injected tabs using the same spot.\n */\n hiddenTabs?: IntegrationDetailBuiltInTab[]\n}\n\nexport type IntegrationDetailBuiltInTab =\n | 'credentials'\n | 'version'\n | 'health'\n | 'logs'\n | 'data-sync-schedule'\n\nexport interface IntegrationDefaultStateConfig {\n isEnabled?: boolean\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 defaultState?: IntegrationDefaultStateConfig\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\ntype IntegrationRegistryState = {\n integrations: Map<string, IntegrationDefinition>\n bundles: Map<string, IntegrationBundle>\n}\n\nconst GLOBAL_INTEGRATION_REGISTRY_KEY = '__openMercatoIntegrationRegistry__' as const\n\ntype GlobalIntegrationRegistry = typeof globalThis & {\n [GLOBAL_INTEGRATION_REGISTRY_KEY]?: IntegrationRegistryState\n}\n\nfunction getIntegrationRegistryState(): IntegrationRegistryState {\n const globalRegistry = globalThis as GlobalIntegrationRegistry\n if (!globalRegistry[GLOBAL_INTEGRATION_REGISTRY_KEY]) {\n globalRegistry[GLOBAL_INTEGRATION_REGISTRY_KEY] = {\n integrations: new Map<string, IntegrationDefinition>(),\n bundles: new Map<string, IntegrationBundle>(),\n }\n }\n return globalRegistry[GLOBAL_INTEGRATION_REGISTRY_KEY]\n}\n\nexport function registerIntegration(definition: IntegrationDefinition): void {\n getIntegrationRegistryState().integrations.set(definition.id, definition)\n}\n\nexport function registerIntegrations(definitions: IntegrationDefinition[]): void {\n const registry = getIntegrationRegistryState().integrations\n for (const definition of definitions) {\n registry.set(definition.id, definition)\n }\n}\n\nexport function registerBundle(bundle: IntegrationBundle): void {\n getIntegrationRegistryState().bundles.set(bundle.id, bundle)\n}\n\nexport function registerBundles(bundles: IntegrationBundle[]): void {\n const registry = getIntegrationRegistryState().bundles\n for (const bundle of bundles) {\n registry.set(bundle.id, bundle)\n }\n}\n\nexport function clearRegisteredIntegrations(): void {\n const registry = getIntegrationRegistryState()\n registry.integrations.clear()\n registry.bundles.clear()\n}\n\nexport function getIntegration(integrationId: string): IntegrationDefinition | undefined {\n return getIntegrationRegistryState().integrations.get(integrationId)\n}\n\nexport function getAllIntegrations(): IntegrationDefinition[] {\n return Array.from(getIntegrationRegistryState().integrations.values())\n}\n\nexport function getBundle(bundleId: string): IntegrationBundle | undefined {\n return getIntegrationRegistryState().bundles.get(bundleId)\n}\n\nexport function getAllBundles(): IntegrationBundle[] {\n return Array.from(getIntegrationRegistryState().bundles.values())\n}\n\nexport function getBundleIntegrations(bundleId: string): IntegrationDefinition[] {\n return Array.from(getIntegrationRegistryState().integrations.values()).filter((integration) => integration.bundleId === bundleId)\n}\n\nexport function resolveIntegrationCredentialsSchema(integrationId: string): IntegrationCredentialsSchema | undefined {\n const registry = getIntegrationRegistryState()\n const definition = registry.integrations.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 registry.bundles.get(definition.bundleId)?.credentials\n}\n\nexport function getIntegrationTitle(integrationId: string): string {\n return getIntegrationRegistryState().integrations.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": "AAsMA,MAAM,kCAAkC;AAMxC,SAAS,8BAAwD;AAC/D,QAAM,iBAAiB;AACvB,MAAI,CAAC,eAAe,+BAA+B,GAAG;AACpD,mBAAe,+BAA+B,IAAI;AAAA,MAChD,cAAc,oBAAI,IAAmC;AAAA,MACrD,SAAS,oBAAI,IAA+B;AAAA,IAC9C;AAAA,EACF;AACA,SAAO,eAAe,+BAA+B;AACvD;AAEO,SAAS,oBAAoB,YAAyC;AAC3E,8BAA4B,EAAE,aAAa,IAAI,WAAW,IAAI,UAAU;AAC1E;AAEO,SAAS,qBAAqB,aAA4C;AAC/E,QAAM,WAAW,4BAA4B,EAAE;AAC/C,aAAW,cAAc,aAAa;AACpC,aAAS,IAAI,WAAW,IAAI,UAAU;AAAA,EACxC;AACF;AAEO,SAAS,eAAe,QAAiC;AAC9D,8BAA4B,EAAE,QAAQ,IAAI,OAAO,IAAI,MAAM;AAC7D;AAEO,SAAS,gBAAgB,SAAoC;AAClE,QAAM,WAAW,4BAA4B,EAAE;AAC/C,aAAW,UAAU,SAAS;AAC5B,aAAS,IAAI,OAAO,IAAI,MAAM;AAAA,EAChC;AACF;AAEO,SAAS,8BAAoC;AAClD,QAAM,WAAW,4BAA4B;AAC7C,WAAS,aAAa,MAAM;AAC5B,WAAS,QAAQ,MAAM;AACzB;AAEO,SAAS,eAAe,eAA0D;AACvF,SAAO,4BAA4B,EAAE,aAAa,IAAI,aAAa;AACrE;AAEO,SAAS,qBAA8C;AAC5D,SAAO,MAAM,KAAK,4BAA4B,EAAE,aAAa,OAAO,CAAC;AACvE;AAEO,SAAS,UAAU,UAAiD;AACzE,SAAO,4BAA4B,EAAE,QAAQ,IAAI,QAAQ;AAC3D;AAEO,SAAS,gBAAqC;AACnD,SAAO,MAAM,KAAK,4BAA4B,EAAE,QAAQ,OAAO,CAAC;AAClE;AAEO,SAAS,sBAAsB,UAA2C;AAC/E,SAAO,MAAM,KAAK,4BAA4B,EAAE,aAAa,OAAO,CAAC,EAAE,OAAO,CAAC,gBAAgB,YAAY,aAAa,QAAQ;AAClI;AAEO,SAAS,oCAAoC,eAAiE;AACnH,QAAM,WAAW,4BAA4B;AAC7C,QAAM,aAAa,SAAS,aAAa,IAAI,aAAa;AAC1D,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,SAAS,QAAQ,IAAI,WAAW,QAAQ,GAAG;AACpD;AAEO,SAAS,oBAAoB,eAA+B;AACjE,SAAO,4BAA4B,EAAE,aAAa,IAAI,aAAa,GAAG,SAAS;AACjF;AAEO,MAAM,yCAAyC;AAE/C,SAAS,mCAAmC,eAA+B;AAChF,SAAO,uBAAuB,aAAa;AAC7C;",
4
+ "sourcesContent": ["export type IntegrationScope = {\n organizationId: string\n tenantId: string\n /**\n * Optional per-user secret scoping. When set, credential lookups and writes\n * are scoped to a single user \u2014 used by per-user channels (Gmail, IMAP)\n * so two users on the same tenant don't share one row.\n *\n * When `undefined`/`null`, behaviour is unchanged \u2014 tenant-wide credentials\n * (e.g. shared Stripe/Akeneo API keys) keep working exactly as before.\n *\n * Stored on `IntegrationCredentials.user_id` (added 2026-05-26 by the email\n * integration spec). Backed by the partial unique index\n * `integration_credentials_user_lookup_idx` across integration, organization,\n * tenant, and user where `user_id IS NOT NULL AND deleted_at IS NULL`.\n */\n userId?: string | null\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 * Built-in tabs to hide for this integration detail page.\n * Provider modules can replace these with injected tabs using the same spot.\n */\n hiddenTabs?: IntegrationDetailBuiltInTab[]\n}\n\nexport type IntegrationDetailBuiltInTab =\n | 'credentials'\n | 'version'\n | 'health'\n | 'logs'\n | 'data-sync-schedule'\n\nexport interface IntegrationDefaultStateConfig {\n isEnabled?: boolean\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 defaultState?: IntegrationDefaultStateConfig\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\ntype IntegrationRegistryState = {\n integrations: Map<string, IntegrationDefinition>\n bundles: Map<string, IntegrationBundle>\n}\n\nconst GLOBAL_INTEGRATION_REGISTRY_KEY = '__openMercatoIntegrationRegistry__' as const\n\ntype GlobalIntegrationRegistry = typeof globalThis & {\n [GLOBAL_INTEGRATION_REGISTRY_KEY]?: IntegrationRegistryState\n}\n\nfunction getIntegrationRegistryState(): IntegrationRegistryState {\n const globalRegistry = globalThis as GlobalIntegrationRegistry\n if (!globalRegistry[GLOBAL_INTEGRATION_REGISTRY_KEY]) {\n globalRegistry[GLOBAL_INTEGRATION_REGISTRY_KEY] = {\n integrations: new Map<string, IntegrationDefinition>(),\n bundles: new Map<string, IntegrationBundle>(),\n }\n }\n return globalRegistry[GLOBAL_INTEGRATION_REGISTRY_KEY]\n}\n\nexport function registerIntegration(definition: IntegrationDefinition): void {\n getIntegrationRegistryState().integrations.set(definition.id, definition)\n}\n\nexport function registerIntegrations(definitions: IntegrationDefinition[]): void {\n const registry = getIntegrationRegistryState().integrations\n for (const definition of definitions) {\n registry.set(definition.id, definition)\n }\n}\n\nexport function registerBundle(bundle: IntegrationBundle): void {\n getIntegrationRegistryState().bundles.set(bundle.id, bundle)\n}\n\nexport function registerBundles(bundles: IntegrationBundle[]): void {\n const registry = getIntegrationRegistryState().bundles\n for (const bundle of bundles) {\n registry.set(bundle.id, bundle)\n }\n}\n\nexport function clearRegisteredIntegrations(): void {\n const registry = getIntegrationRegistryState()\n registry.integrations.clear()\n registry.bundles.clear()\n}\n\nexport function getIntegration(integrationId: string): IntegrationDefinition | undefined {\n return getIntegrationRegistryState().integrations.get(integrationId)\n}\n\nexport function getAllIntegrations(): IntegrationDefinition[] {\n return Array.from(getIntegrationRegistryState().integrations.values())\n}\n\nexport function getBundle(bundleId: string): IntegrationBundle | undefined {\n return getIntegrationRegistryState().bundles.get(bundleId)\n}\n\nexport function getAllBundles(): IntegrationBundle[] {\n return Array.from(getIntegrationRegistryState().bundles.values())\n}\n\nexport function getBundleIntegrations(bundleId: string): IntegrationDefinition[] {\n return Array.from(getIntegrationRegistryState().integrations.values()).filter((integration) => integration.bundleId === bundleId)\n}\n\nexport function resolveIntegrationCredentialsSchema(integrationId: string): IntegrationCredentialsSchema | undefined {\n const registry = getIntegrationRegistryState()\n const definition = registry.integrations.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 registry.bundles.get(definition.bundleId)?.credentials\n}\n\nexport function getIntegrationTitle(integrationId: string): string {\n return getIntegrationRegistryState().integrations.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": "AAoNA,MAAM,kCAAkC;AAMxC,SAAS,8BAAwD;AAC/D,QAAM,iBAAiB;AACvB,MAAI,CAAC,eAAe,+BAA+B,GAAG;AACpD,mBAAe,+BAA+B,IAAI;AAAA,MAChD,cAAc,oBAAI,IAAmC;AAAA,MACrD,SAAS,oBAAI,IAA+B;AAAA,IAC9C;AAAA,EACF;AACA,SAAO,eAAe,+BAA+B;AACvD;AAEO,SAAS,oBAAoB,YAAyC;AAC3E,8BAA4B,EAAE,aAAa,IAAI,WAAW,IAAI,UAAU;AAC1E;AAEO,SAAS,qBAAqB,aAA4C;AAC/E,QAAM,WAAW,4BAA4B,EAAE;AAC/C,aAAW,cAAc,aAAa;AACpC,aAAS,IAAI,WAAW,IAAI,UAAU;AAAA,EACxC;AACF;AAEO,SAAS,eAAe,QAAiC;AAC9D,8BAA4B,EAAE,QAAQ,IAAI,OAAO,IAAI,MAAM;AAC7D;AAEO,SAAS,gBAAgB,SAAoC;AAClE,QAAM,WAAW,4BAA4B,EAAE;AAC/C,aAAW,UAAU,SAAS;AAC5B,aAAS,IAAI,OAAO,IAAI,MAAM;AAAA,EAChC;AACF;AAEO,SAAS,8BAAoC;AAClD,QAAM,WAAW,4BAA4B;AAC7C,WAAS,aAAa,MAAM;AAC5B,WAAS,QAAQ,MAAM;AACzB;AAEO,SAAS,eAAe,eAA0D;AACvF,SAAO,4BAA4B,EAAE,aAAa,IAAI,aAAa;AACrE;AAEO,SAAS,qBAA8C;AAC5D,SAAO,MAAM,KAAK,4BAA4B,EAAE,aAAa,OAAO,CAAC;AACvE;AAEO,SAAS,UAAU,UAAiD;AACzE,SAAO,4BAA4B,EAAE,QAAQ,IAAI,QAAQ;AAC3D;AAEO,SAAS,gBAAqC;AACnD,SAAO,MAAM,KAAK,4BAA4B,EAAE,QAAQ,OAAO,CAAC;AAClE;AAEO,SAAS,sBAAsB,UAA2C;AAC/E,SAAO,MAAM,KAAK,4BAA4B,EAAE,aAAa,OAAO,CAAC,EAAE,OAAO,CAAC,gBAAgB,YAAY,aAAa,QAAQ;AAClI;AAEO,SAAS,oCAAoC,eAAiE;AACnH,QAAM,WAAW,4BAA4B;AAC7C,QAAM,aAAa,SAAS,aAAa,IAAI,aAAa;AAC1D,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,SAAS,QAAQ,IAAI,WAAW,QAAQ,GAAG;AACpD;AAEO,SAAS,oBAAoB,eAA+B;AACjE,SAAO,4BAA4B,EAAE,aAAa,IAAI,aAAa,GAAG,SAAS;AACjF;AAEO,MAAM,yCAAyC;AAE/C,SAAS,mCAAmC,eAA+B;AAChF,SAAO,uBAAuB,aAAa;AAC7C;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/shared",
3
- "version": "0.6.5-develop.4384.1.ce2ec6eaaa",
3
+ "version": "0.6.5-develop.4393.1.de282b5dfd",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -92,7 +92,7 @@
92
92
  "@mikro-orm/core": "^7.1.3",
93
93
  "@mikro-orm/decorators": "^7.1.3",
94
94
  "@mikro-orm/postgresql": "^7.1.3",
95
- "@open-mercato/cache": "0.6.5-develop.4384.1.ce2ec6eaaa",
95
+ "@open-mercato/cache": "0.6.5-develop.4393.1.de282b5dfd",
96
96
  "dotenv": "^17.4.2",
97
97
  "rate-limiter-flexible": "^11.1.0",
98
98
  "re2js": "2.8.3",
@@ -73,6 +73,17 @@ export function useT() {
73
73
  return ctx.t
74
74
  }
75
75
 
76
+ /**
77
+ * Like `useT`, but returns `undefined` instead of throwing when no
78
+ * `I18nProvider` is in scope. Use where a translator is desirable but not
79
+ * guaranteed (e.g. plumbing `t` into side-effect handlers that may run before
80
+ * the provider mounts) — callers MUST provide a fallback.
81
+ */
82
+ export function useOptionalT(): TranslateFn | undefined {
83
+ const ctx = useContext(I18nContext)
84
+ return ctx?.t
85
+ }
86
+
76
87
  export function useLocale() {
77
88
  const ctx = useContext(I18nContext)
78
89
  if (!ctx) throw new Error('useLocale must be used within I18nProvider')
@@ -1,6 +1,20 @@
1
1
  export type IntegrationScope = {
2
2
  organizationId: string
3
3
  tenantId: string
4
+ /**
5
+ * Optional per-user secret scoping. When set, credential lookups and writes
6
+ * are scoped to a single user — used by per-user channels (Gmail, IMAP)
7
+ * so two users on the same tenant don't share one row.
8
+ *
9
+ * When `undefined`/`null`, behaviour is unchanged — tenant-wide credentials
10
+ * (e.g. shared Stripe/Akeneo API keys) keep working exactly as before.
11
+ *
12
+ * Stored on `IntegrationCredentials.user_id` (added 2026-05-26 by the email
13
+ * integration spec). Backed by the partial unique index
14
+ * `integration_credentials_user_lookup_idx` across integration, organization,
15
+ * tenant, and user where `user_id IS NOT NULL AND deleted_at IS NULL`.
16
+ */
17
+ userId?: string | null
4
18
  }
5
19
 
6
20
  export type IntegrationCategory =
@@ -30,6 +30,13 @@ export type NotificationHandlerContext = {
30
30
  userId?: string
31
31
  features: string[]
32
32
  currentPath: string
33
+ /**
34
+ * Optional translator, present when the dispatch happens inside a React tree
35
+ * with an active `I18nProvider` (the poll/SSE notification hooks). Handlers
36
+ * that emit user-facing copy (e.g. a toast action label) MUST localize via
37
+ * this when available and fall back to an English literal only when absent.
38
+ */
39
+ t?: (key: string, fallback?: string) => string
33
40
  toast: (options: NotificationHandlerToastOptions) => void
34
41
  popup: (options: NotificationHandlerPopupOptions) => void
35
42
  emitEvent: (eventName: string, detail?: unknown) => void