@burdenoff/microfe-channel 2026.526.1 → 2026.530.1
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/channel/ChannelRoutes.js +5 -6
- package/dist/channel/ChannelRoutes.js.map +1 -1
- package/dist/channel/components/AdapterCredentialsForm.js +14 -2
- package/dist/channel/components/AdapterCredentialsForm.js.map +1 -1
- package/dist/channel/components/FeatureNotEnabled.js +1 -1
- package/dist/channel/components/FeatureNotEnabled.js.map +1 -1
- package/dist/channel/components/PermissionGuard.js +1 -1
- package/dist/channel/components/PermissionGuard.js.map +1 -1
- package/dist/channel/config/adapterCredentials.js +19 -11
- package/dist/channel/config/adapterCredentials.js.map +1 -1
- package/dist/channel/constants/permissions.js +3 -0
- package/dist/channel/constants/permissions.js.map +1 -1
- package/dist/channel/hooks/useChannelSubscription.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelDashboardPage.js +244 -220
- package/dist/channel/modules/channels/pages/ChannelDashboardPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelDetailsPage.js +416 -390
- package/dist/channel/modules/channels/pages/ChannelDetailsPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelSettingsPage.js +30 -15
- package/dist/channel/modules/channels/pages/ChannelSettingsPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelUsersPage.js +8 -2
- package/dist/channel/modules/channels/pages/ChannelUsersPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelsListPage.js +241 -213
- package/dist/channel/modules/channels/pages/ChannelsListPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js +107 -98
- package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ConnectionsListPage.js +160 -140
- package/dist/channel/modules/channels/pages/ConnectionsListPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/CreateChannelPage.js +47 -50
- package/dist/channel/modules/channels/pages/CreateChannelPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/CreateConnectionPage.js +16 -5
- package/dist/channel/modules/channels/pages/CreateConnectionPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js +11 -4
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/EditChannelPage.js +17 -8
- package/dist/channel/modules/channels/pages/EditChannelPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/EditConnectionPage.js +11 -6
- package/dist/channel/modules/channels/pages/EditConnectionPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/SendMessagePage.js +21 -9
- package/dist/channel/modules/channels/pages/SendMessagePage.js.map +1 -1
- package/dist/channel/modules/channels/pages/WebhookEventsPage.js +8 -1
- package/dist/channel/modules/channels/pages/WebhookEventsPage.js.map +1 -1
- package/dist/channel/pages/ChannelLandingPage.js +50 -35
- package/dist/channel/pages/ChannelLandingPage.js.map +1 -1
- package/dist/channel/providers/ChannelProvider.js +1 -1
- package/dist/channel/providers/ChannelProvider.js.map +1 -1
- package/package.json +2 -2
- package/dist/channel/modules/channels/pages/index.js +0 -14
|
@@ -12,9 +12,8 @@ import { ChannelSettingsPage as u } from "./modules/channels/pages/ChannelSettin
|
|
|
12
12
|
import { DeliveryHistoryPage as d } from "./modules/channels/pages/DeliveryHistoryPage.js";
|
|
13
13
|
import { WebhookEventsPage as f } from "./modules/channels/pages/WebhookEventsPage.js";
|
|
14
14
|
import { ChannelUsersPage as p } from "./modules/channels/pages/ChannelUsersPage.js";
|
|
15
|
-
import "./
|
|
16
|
-
import { ChannelLandingPage as
|
|
17
|
-
import { FeatureNotEnabled as h } from "./components/FeatureNotEnabled.js";
|
|
15
|
+
import { FeatureNotEnabled as m } from "./components/FeatureNotEnabled.js";
|
|
16
|
+
import { ChannelLandingPage as h } from "./pages/ChannelLandingPage.js";
|
|
18
17
|
import { Navigate as g, Route as _, Routes as v } from "react-router-dom";
|
|
19
18
|
import { jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
20
19
|
import { FeatureGate as x } from "@burdenoff/fe-libs/shared/feature-flags";
|
|
@@ -22,8 +21,8 @@ import { FeatureGate as x } from "@burdenoff/fe-libs/shared/feature-flags";
|
|
|
22
21
|
function S(e, t) {
|
|
23
22
|
return /* @__PURE__ */ y(x, {
|
|
24
23
|
flag: e,
|
|
25
|
-
defaultValue: !
|
|
26
|
-
fallback: /* @__PURE__ */ y(
|
|
24
|
+
defaultValue: !0,
|
|
25
|
+
fallback: /* @__PURE__ */ y(m, { flag: e }),
|
|
27
26
|
children: t
|
|
28
27
|
});
|
|
29
28
|
}
|
|
@@ -31,7 +30,7 @@ function C() {
|
|
|
31
30
|
return /* @__PURE__ */ b(v, { children: [
|
|
32
31
|
/* @__PURE__ */ y(_, {
|
|
33
32
|
index: !0,
|
|
34
|
-
element: S("channels.dashboard", /* @__PURE__ */ y(
|
|
33
|
+
element: S("channels.dashboard", /* @__PURE__ */ y(h, {}))
|
|
35
34
|
}),
|
|
36
35
|
/* @__PURE__ */ y(_, {
|
|
37
36
|
path: "dashboard",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelRoutes.js","names":[],"sources":["../../src/channel/ChannelRoutes.tsx"],"sourcesContent":["import { Navigate, Route, Routes } from 'react-router-dom';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport {
|
|
1
|
+
{"version":3,"file":"ChannelRoutes.js","names":[],"sources":["../../src/channel/ChannelRoutes.tsx"],"sourcesContent":["import { Navigate, Route, Routes } from 'react-router-dom';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { ChannelDashboardPage } from './modules/channels/pages/ChannelDashboardPage';\nimport { ChannelsListPage } from './modules/channels/pages/ChannelsListPage';\nimport { ChannelDetailsPage } from './modules/channels/pages/ChannelDetailsPage';\nimport { CreateChannelPage } from './modules/channels/pages/CreateChannelPage';\nimport { EditChannelPage } from './modules/channels/pages/EditChannelPage';\nimport { SendMessagePage } from './modules/channels/pages/SendMessagePage';\nimport { ConnectionsListPage } from './modules/channels/pages/ConnectionsListPage';\nimport { ConnectionDetailsPage } from './modules/channels/pages/ConnectionDetailsPage';\nimport { CreateConnectionPage } from './modules/channels/pages/CreateConnectionPage';\nimport { EditConnectionPage } from './modules/channels/pages/EditConnectionPage';\nimport { ChannelSettingsPage } from './modules/channels/pages/ChannelSettingsPage';\nimport { DeliveryHistoryPage } from './modules/channels/pages/DeliveryHistoryPage';\nimport { WebhookEventsPage } from './modules/channels/pages/WebhookEventsPage';\nimport { ChannelUsersPage } from './modules/channels/pages/ChannelUsersPage';\nimport { ChannelLandingPage } from './pages/ChannelLandingPage';\nimport { FeatureNotEnabled } from './components/FeatureNotEnabled';\nimport type { ReactNode } from 'react';\n\n/**\n * Wraps a route element in a FeatureGate. The channel module is shipped & stable\n * and now lives in the admin shell, and every `channels.*` flag is seeded ON in\n * global-tenant-svc — so absent flags default to ENABLED (defaultValue=true) to\n * match the backend and avoid a hard \"Feature not enabled\" lockout when the\n * evaluations map hasn't loaded (no-workspace / pre-bootstrap / fetch error).\n * An explicit `channels.<x> = false` still acts as a kill-switch (flag value wins).\n */\nfunction gated(flag: string, element: ReactNode): ReactNode {\n return (\n <FeatureGate flag={flag} defaultValue={true} fallback={<FeatureNotEnabled flag={flag} />}>\n {element}\n </FeatureGate>\n );\n}\n\n/**\n * Internal router for the Channel microfrontend.\n *\n * The shell mounts this at /channels/* so all paths here are relative to /channels/.\n * Every concrete route is wrapped with a FeatureGate so individual surfaces can\n * be rolled out or kill-switched independently. Flag names follow\n * `channels.<route-name>` (see vibecontrols-channel-stability tracker).\n */\nexport function ChannelRoutes() {\n return (\n <Routes>\n {/* Tabbed landing — gated under the dashboard flag */}\n <Route index element={gated('channels.dashboard', <ChannelLandingPage />)} />\n\n {/* Dashboard */}\n <Route path=\"dashboard\" element={gated('channels.dashboard', <ChannelDashboardPage />)} />\n\n {/* Channel CRUD — flat under /channels/ */}\n <Route path=\"list\" element={gated('channels.list', <ChannelsListPage />)} />\n <Route path=\"create\" element={gated('channels.create', <CreateChannelPage />)} />\n <Route path=\":channelId\" element={gated('channels.detail', <ChannelDetailsPage />)} />\n <Route path=\":channelId/edit\" element={gated('channels.edit', <EditChannelPage />)} />\n <Route\n path=\":channelId/deliveries\"\n element={gated('channels.deliveries', <DeliveryHistoryPage />)}\n />\n <Route\n path=\":channelId/webhooks\"\n element={gated('channels.webhooks', <WebhookEventsPage />)}\n />\n <Route path=\":channelId/users\" element={gated('channels.users', <ChannelUsersPage />)} />\n\n {/* Connections */}\n <Route\n path=\"connections\"\n element={gated('channels.connections.list', <ConnectionsListPage />)}\n />\n <Route\n path=\"connections/create\"\n element={gated('channels.connections.create', <CreateConnectionPage />)}\n />\n <Route\n path=\"connections/:connectionId\"\n element={gated('channels.connections.detail', <ConnectionDetailsPage />)}\n />\n <Route\n path=\"connections/:connectionId/edit\"\n element={gated('channels.connections.edit', <EditConnectionPage />)}\n />\n\n {/* Send Message */}\n <Route path=\"send\" element={gated('channels.send', <SendMessagePage />)} />\n\n {/* Settings */}\n <Route path=\"settings\" element={gated('channels.settings', <ChannelSettingsPage />)} />\n\n {/* Catch-all → dashboard */}\n <Route path=\"*\" element={<Navigate to=\"dashboard\" replace />} />\n </Routes>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA4BA,SAAS,EAAM,GAAc,GAA+B;AAC1D,QACE,kBAAC,GAAD;EAAmB;EAAM,cAAc;EAAM,UAAU,kBAAC,GAAD,EAAyB,SAAQ,CAAA;YACrF;EACW,CAAA;;AAYlB,SAAgB,IAAgB;AAC9B,QACE,kBAAC,GAAD,EAAA,UAAA;EAEE,kBAAC,GAAD;GAAO,OAAA;GAAM,SAAS,EAAM,sBAAsB,kBAAC,GAAD,EAAsB,CAAA,CAAC;GAAI,CAAA;EAG7E,kBAAC,GAAD;GAAO,MAAK;GAAY,SAAS,EAAM,sBAAsB,kBAAC,GAAD,EAAwB,CAAA,CAAC;GAAI,CAAA;EAG1F,kBAAC,GAAD;GAAO,MAAK;GAAO,SAAS,EAAM,iBAAiB,kBAAC,GAAD,EAAoB,CAAA,CAAC;GAAI,CAAA;EAC5E,kBAAC,GAAD;GAAO,MAAK;GAAS,SAAS,EAAM,mBAAmB,kBAAC,GAAD,EAAqB,CAAA,CAAC;GAAI,CAAA;EACjF,kBAAC,GAAD;GAAO,MAAK;GAAa,SAAS,EAAM,mBAAmB,kBAAC,GAAD,EAAsB,CAAA,CAAC;GAAI,CAAA;EACtF,kBAAC,GAAD;GAAO,MAAK;GAAkB,SAAS,EAAM,iBAAiB,kBAAC,GAAD,EAAmB,CAAA,CAAC;GAAI,CAAA;EACtF,kBAAC,GAAD;GACE,MAAK;GACL,SAAS,EAAM,uBAAuB,kBAAC,GAAD,EAAuB,CAAA,CAAC;GAC9D,CAAA;EACF,kBAAC,GAAD;GACE,MAAK;GACL,SAAS,EAAM,qBAAqB,kBAAC,GAAD,EAAqB,CAAA,CAAC;GAC1D,CAAA;EACF,kBAAC,GAAD;GAAO,MAAK;GAAmB,SAAS,EAAM,kBAAkB,kBAAC,GAAD,EAAoB,CAAA,CAAC;GAAI,CAAA;EAGzF,kBAAC,GAAD;GACE,MAAK;GACL,SAAS,EAAM,6BAA6B,kBAAC,GAAD,EAAuB,CAAA,CAAC;GACpE,CAAA;EACF,kBAAC,GAAD;GACE,MAAK;GACL,SAAS,EAAM,+BAA+B,kBAAC,GAAD,EAAwB,CAAA,CAAC;GACvE,CAAA;EACF,kBAAC,GAAD;GACE,MAAK;GACL,SAAS,EAAM,+BAA+B,kBAAC,GAAD,EAAyB,CAAA,CAAC;GACxE,CAAA;EACF,kBAAC,GAAD;GACE,MAAK;GACL,SAAS,EAAM,6BAA6B,kBAAC,GAAD,EAAsB,CAAA,CAAC;GACnE,CAAA;EAGF,kBAAC,GAAD;GAAO,MAAK;GAAO,SAAS,EAAM,iBAAiB,kBAAC,GAAD,EAAmB,CAAA,CAAC;GAAI,CAAA;EAG3E,kBAAC,GAAD;GAAO,MAAK;GAAW,SAAS,EAAM,qBAAqB,kBAAC,GAAD,EAAuB,CAAA,CAAC;GAAI,CAAA;EAGvF,kBAAC,GAAD;GAAO,MAAK;GAAI,SAAS,kBAAC,GAAD;IAAU,IAAG;IAAY,SAAA;IAAU,CAAA;GAAI,CAAA;EACzD,EAAA,CAAA"}
|
|
@@ -69,9 +69,10 @@ function m({ adapterType: s, credentials: m, onChange: h, onValidationChange: g,
|
|
|
69
69
|
[e]: !t[e]
|
|
70
70
|
}));
|
|
71
71
|
}, k = (e) => {
|
|
72
|
-
let t = m[e.key], n = C[e.key] && e.required && !t, r = `w-full px-3 py-2 text-sm bg-surface border rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-primary disabled:opacity-50 disabled:cursor-not-allowed ${n ? "border-status-error-border" : "border-border"}`;
|
|
72
|
+
let t = m[e.key], n = C[e.key] && e.required && !t, r = `w-full px-3 py-2 text-sm bg-surface border rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-primary disabled:opacity-50 disabled:cursor-not-allowed ${n ? "border-status-error-border" : "border-border"}`, i = `adapter-cred-${e.key}`;
|
|
73
73
|
return /* @__PURE__ */ p("div", { children: [
|
|
74
74
|
/* @__PURE__ */ p("label", {
|
|
75
|
+
htmlFor: i,
|
|
75
76
|
className: "block text-sm font-medium text-foreground mb-1.5",
|
|
76
77
|
children: [e.displayName, e.required && /* @__PURE__ */ f("span", {
|
|
77
78
|
className: "text-status-error-text ml-1",
|
|
@@ -79,6 +80,8 @@ function m({ adapterType: s, credentials: m, onChange: h, onValidationChange: g,
|
|
|
79
80
|
})]
|
|
80
81
|
}),
|
|
81
82
|
e.type === "text" && /* @__PURE__ */ f("input", {
|
|
83
|
+
id: i,
|
|
84
|
+
"aria-label": e.displayName,
|
|
82
85
|
type: "text",
|
|
83
86
|
value: t || "",
|
|
84
87
|
onChange: (t) => E(e.key, t.target.value),
|
|
@@ -90,6 +93,8 @@ function m({ adapterType: s, credentials: m, onChange: h, onValidationChange: g,
|
|
|
90
93
|
e.type === "password" && /* @__PURE__ */ p("div", {
|
|
91
94
|
className: "relative",
|
|
92
95
|
children: [/* @__PURE__ */ f("input", {
|
|
96
|
+
id: i,
|
|
97
|
+
"aria-label": e.displayName,
|
|
93
98
|
type: x[e.key] ? "text" : "password",
|
|
94
99
|
value: t || "",
|
|
95
100
|
onChange: (t) => E(e.key, t.target.value),
|
|
@@ -100,12 +105,15 @@ function m({ adapterType: s, credentials: m, onChange: h, onValidationChange: g,
|
|
|
100
105
|
}), /* @__PURE__ */ f("button", {
|
|
101
106
|
type: "button",
|
|
102
107
|
onClick: () => O(e.key),
|
|
108
|
+
"aria-label": x[e.key] ? `Hide ${e.displayName}` : `Show ${e.displayName}`,
|
|
103
109
|
className: "absolute right-2 top-1/2 -translate-y-1/2 p-1 text-muted-foreground hover:text-foreground",
|
|
104
110
|
tabIndex: -1,
|
|
105
111
|
children: x[e.key] ? /* @__PURE__ */ f(u, { size: 16 }) : /* @__PURE__ */ f(l, { size: 16 })
|
|
106
112
|
})]
|
|
107
113
|
}),
|
|
108
114
|
e.type === "textarea" && /* @__PURE__ */ f("textarea", {
|
|
115
|
+
id: i,
|
|
116
|
+
"aria-label": e.displayName,
|
|
109
117
|
value: t || "",
|
|
110
118
|
onChange: (t) => E(e.key, t.target.value),
|
|
111
119
|
onBlur: () => D(e.key),
|
|
@@ -115,6 +123,8 @@ function m({ adapterType: s, credentials: m, onChange: h, onValidationChange: g,
|
|
|
115
123
|
className: `${r} resize-none font-mono text-xs`
|
|
116
124
|
}),
|
|
117
125
|
e.type === "number" && /* @__PURE__ */ f("input", {
|
|
126
|
+
id: i,
|
|
127
|
+
"aria-label": e.displayName,
|
|
118
128
|
type: "number",
|
|
119
129
|
value: t ?? e.defaultValue ?? "",
|
|
120
130
|
onChange: (t) => E(e.key, t.target.value ? Number(t.target.value) : void 0),
|
|
@@ -130,13 +140,15 @@ function m({ adapterType: s, credentials: m, onChange: h, onValidationChange: g,
|
|
|
130
140
|
checked: t ?? e.defaultValue ?? !1,
|
|
131
141
|
onChange: (t) => E(e.key, t.target.checked),
|
|
132
142
|
disabled: _,
|
|
133
|
-
className: "
|
|
143
|
+
className: "size-4 rounded border-border text-accent-primary focus:ring-accent-primary"
|
|
134
144
|
}), /* @__PURE__ */ f("span", {
|
|
135
145
|
className: "text-sm text-muted-foreground",
|
|
136
146
|
children: e.description
|
|
137
147
|
})]
|
|
138
148
|
}),
|
|
139
149
|
e.type === "select" && e.options && /* @__PURE__ */ p("select", {
|
|
150
|
+
id: i,
|
|
151
|
+
"aria-label": e.displayName,
|
|
140
152
|
value: t ?? e.defaultValue ?? "",
|
|
141
153
|
onChange: (t) => E(e.key, t.target.value),
|
|
142
154
|
onBlur: () => D(e.key),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdapterCredentialsForm.js","names":[],"sources":["../../../src/channel/components/AdapterCredentialsForm.tsx"],"sourcesContent":["/**\n * Adapter Credentials Form\n *\n * Dynamic form that renders credential fields based on the selected adapter type.\n * Field names match exactly what the backend adapters expect.\n */\n\nimport { useState, useEffect } from 'react';\nimport { ExternalLink, Eye, EyeOff, AlertCircle, CheckCircle2 } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport type { AdapterType } from '../hooks/useChannels';\nimport {\n getAdapterCredentialSchema,\n getDefaultCredentials,\n validateCredentials,\n type CredentialField,\n} from '../config/adapterCredentials';\n\ninterface AdapterCredentialsFormProps {\n adapterType: AdapterType | undefined;\n credentials: Record<string, unknown>;\n onChange: (credentials: Record<string, unknown>) => void;\n onValidationChange?: (isValid: boolean, errors: string[]) => void;\n disabled?: boolean;\n showPlatformMetadata?: boolean;\n}\n\nexport function AdapterCredentialsForm({\n adapterType,\n credentials,\n onChange,\n onValidationChange,\n disabled = false,\n showPlatformMetadata = true,\n}: AdapterCredentialsFormProps) {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const [showPasswords, setShowPasswords] = useState<Record<string, boolean>>({});\n const [touched, setTouched] = useState<Record<string, boolean>>({});\n\n const schema = getAdapterCredentialSchema(adapterType);\n\n // Initialize with default values when adapter type changes\n useEffect(() => {\n if (adapterType) {\n const defaults = getDefaultCredentials(adapterType);\n const merged = { ...defaults, ...credentials };\n if (JSON.stringify(merged) !== JSON.stringify(credentials)) {\n onChange(merged);\n }\n }\n }, [adapterType]);\n\n // Validate on change\n useEffect(() => {\n if (onValidationChange && adapterType) {\n const { valid, errors } = validateCredentials(adapterType, credentials);\n onValidationChange(valid, errors);\n }\n }, [credentials, adapterType, onValidationChange]);\n\n if (!adapterType) {\n return (\n <div className=\"text-center py-6 text-muted-foreground\">\n <p className=\"text-sm\">\n {tr('channel.selectAdapterType', 'Select an adapter type to configure credentials')}\n </p>\n </div>\n );\n }\n\n if (!schema) {\n return (\n <div className=\"p-4 bg-status-warning-bg-subtle border border-warning-300 rounded-lg\">\n <div className=\"flex items-start gap-2\">\n <AlertCircle size={16} className=\"text-status-warning-text mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-warning-700\">\n {tr('channel.noCredentialSchema', 'No credential schema defined for')} {adapterType}\n </p>\n <p className=\"text-xs text-warning-600 mt-1\">\n {tr(\n 'channel.enterCredentialsAsJson',\n 'You can still enter credentials as JSON in the raw format below.'\n )}\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n const handleFieldChange = (key: string, value: unknown) => {\n onChange({ ...credentials, [key]: value });\n };\n\n const handleFieldBlur = (key: string) => {\n setTouched((prev) => ({ ...prev, [key]: true }));\n };\n\n const togglePasswordVisibility = (key: string) => {\n setShowPasswords((prev) => ({ ...prev, [key]: !prev[key] }));\n };\n\n const renderField = (field: CredentialField) => {\n const value = credentials[field.key];\n const fieldError = touched[field.key] && field.required && !value;\n\n const commonInputClasses = `w-full px-3 py-2 text-sm bg-surface border rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-primary disabled:opacity-50 disabled:cursor-not-allowed ${\n fieldError ? 'border-status-error-border' : 'border-border'\n }`;\n\n return (\n <div key={field.key}>\n <label className=\"block text-sm font-medium text-foreground mb-1.5\">\n {field.displayName}\n {field.required && <span className=\"text-status-error-text ml-1\">*</span>}\n </label>\n\n {field.type === 'text' && (\n <input\n type=\"text\"\n value={(value as string) || ''}\n onChange={(e) => handleFieldChange(field.key, e.target.value)}\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n placeholder={field.placeholder}\n className={commonInputClasses}\n />\n )}\n\n {field.type === 'password' && (\n <div className=\"relative\">\n <input\n type={showPasswords[field.key] ? 'text' : 'password'}\n value={(value as string) || ''}\n onChange={(e) => handleFieldChange(field.key, e.target.value)}\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n placeholder={field.placeholder}\n className={`${commonInputClasses} pr-10`}\n />\n <button\n type=\"button\"\n onClick={() => togglePasswordVisibility(field.key)}\n className=\"absolute right-2 top-1/2 -translate-y-1/2 p-1 text-muted-foreground hover:text-foreground\"\n tabIndex={-1}\n >\n {showPasswords[field.key] ? <EyeOff size={16} /> : <Eye size={16} />}\n </button>\n </div>\n )}\n\n {field.type === 'textarea' && (\n <textarea\n value={(value as string) || ''}\n onChange={(e) => handleFieldChange(field.key, e.target.value)}\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n placeholder={field.placeholder}\n rows={4}\n className={`${commonInputClasses} resize-none font-mono text-xs`}\n />\n )}\n\n {field.type === 'number' && (\n <input\n type=\"number\"\n value={(value as number) ?? field.defaultValue ?? ''}\n onChange={(e) =>\n handleFieldChange(field.key, e.target.value ? Number(e.target.value) : undefined)\n }\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n placeholder={field.placeholder}\n className={commonInputClasses}\n />\n )}\n\n {field.type === 'boolean' && (\n <label className=\"flex items-center gap-2 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={(value as boolean) ?? field.defaultValue ?? false}\n onChange={(e) => handleFieldChange(field.key, e.target.checked)}\n disabled={disabled}\n className=\"w-4 h-4 rounded border-border text-accent-primary focus:ring-accent-primary\"\n />\n <span className=\"text-sm text-muted-foreground\">{field.description}</span>\n </label>\n )}\n\n {field.type === 'select' && field.options && (\n <select\n value={(value as string) ?? field.defaultValue ?? ''}\n onChange={(e) => handleFieldChange(field.key, e.target.value)}\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n className={`${commonInputClasses} bg-bg-surface`}\n >\n <option value=\"\">\n {tr('channel.select', 'Select')} {field.displayName.toLowerCase()}\n </option>\n {field.options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n )}\n\n {field.type !== 'boolean' && (\n <p className=\"mt-1 text-xs text-muted-foreground\">{field.description}</p>\n )}\n\n {fieldError && (\n <p className=\"mt-1 text-xs text-status-error-text\">\n {tr('channel.fieldRequired', 'This field is required')}\n </p>\n )}\n </div>\n );\n };\n\n return (\n <div className=\"space-y-5\">\n {/* Header with adapter info */}\n <div className=\"flex items-start justify-between\">\n <div>\n <h3 className=\"text-sm font-semibold text-foreground\">{schema.displayName}</h3>\n <p className=\"text-xs text-muted-foreground mt-0.5\">{schema.description}</p>\n </div>\n {schema.docUrl && (\n <a\n href={schema.docUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"flex items-center gap-1 text-xs text-accent-primary hover:text-accent-primary-hover\"\n >\n <span>{tr('channel.docs', 'Docs')}</span>\n <ExternalLink size={12} />\n </a>\n )}\n </div>\n\n {/* Credential fields */}\n <div className=\"space-y-4\">\n {schema.credentials.length > 0 && (\n <div className=\"space-y-4\">{schema.credentials.map(renderField)}</div>\n )}\n\n {/* Platform metadata fields (optional settings) */}\n {showPlatformMetadata && schema.platformMetadata && schema.platformMetadata.length > 0 && (\n <>\n <div className=\"border-t border-border pt-4 mt-4\">\n <p className=\"text-xs font-medium text-muted-foreground mb-3\">\n {tr('channel.optionalSettings', 'Optional Settings')}\n </p>\n <div className=\"space-y-4\">{schema.platformMetadata.map(renderField)}</div>\n </div>\n </>\n )}\n </div>\n </div>\n );\n}\n\n/**\n * Compact validation status indicator\n */\nexport function CredentialValidationStatus({\n adapterType,\n credentials,\n}: {\n adapterType: AdapterType | undefined;\n credentials: Record<string, unknown>;\n}) {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n if (!adapterType) return null;\n\n const { valid, errors } = validateCredentials(adapterType, credentials);\n\n if (valid) {\n return (\n <div className=\"flex items-center gap-1.5 text-status-success-text\">\n <CheckCircle2 size={14} />\n <span className=\"text-xs\">\n {tr('channel.allRequiredFieldsFilled', 'All required fields filled')}\n </span>\n </div>\n );\n }\n\n return (\n <div className=\"flex items-center gap-1.5 text-status-warning-text\">\n <AlertCircle size={14} />\n <span className=\"text-xs\">\n {errors.length} {tr('channel.fieldsNeedAttention', 'field(s) need attention')}\n </span>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AdapterCredentialsForm.js","names":[],"sources":["../../../src/channel/components/AdapterCredentialsForm.tsx"],"sourcesContent":["/**\n * Adapter Credentials Form\n *\n * Dynamic form that renders credential fields based on the selected adapter type.\n * Field names match exactly what the backend adapters expect.\n */\n\nimport { useState, useEffect } from 'react';\nimport { ExternalLink, Eye, EyeOff, AlertCircle, CheckCircle2 } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport type { AdapterType } from '../hooks/useChannels';\nimport {\n getAdapterCredentialSchema,\n getDefaultCredentials,\n validateCredentials,\n type CredentialField,\n} from '../config/adapterCredentials';\n\ninterface AdapterCredentialsFormProps {\n adapterType: AdapterType | undefined;\n credentials: Record<string, unknown>;\n onChange: (credentials: Record<string, unknown>) => void;\n onValidationChange?: (isValid: boolean, errors: string[]) => void;\n disabled?: boolean;\n showPlatformMetadata?: boolean;\n}\n\nexport function AdapterCredentialsForm({\n adapterType,\n credentials,\n onChange,\n onValidationChange,\n disabled = false,\n showPlatformMetadata = true,\n}: AdapterCredentialsFormProps) {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const [showPasswords, setShowPasswords] = useState<Record<string, boolean>>({});\n const [touched, setTouched] = useState<Record<string, boolean>>({});\n\n const schema = getAdapterCredentialSchema(adapterType);\n\n // Initialize with default values when adapter type changes\n useEffect(() => {\n if (adapterType) {\n const defaults = getDefaultCredentials(adapterType);\n const merged = { ...defaults, ...credentials };\n if (JSON.stringify(merged) !== JSON.stringify(credentials)) {\n onChange(merged);\n }\n }\n }, [adapterType]);\n\n // Validate on change\n useEffect(() => {\n if (onValidationChange && adapterType) {\n const { valid, errors } = validateCredentials(adapterType, credentials);\n onValidationChange(valid, errors);\n }\n }, [credentials, adapterType, onValidationChange]);\n\n if (!adapterType) {\n return (\n <div className=\"text-center py-6 text-muted-foreground\">\n <p className=\"text-sm\">\n {tr('channel.selectAdapterType', 'Select an adapter type to configure credentials')}\n </p>\n </div>\n );\n }\n\n if (!schema) {\n return (\n <div className=\"p-4 bg-status-warning-bg-subtle border border-warning-300 rounded-lg\">\n <div className=\"flex items-start gap-2\">\n <AlertCircle size={16} className=\"text-status-warning-text mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-warning-700\">\n {tr('channel.noCredentialSchema', 'No credential schema defined for')} {adapterType}\n </p>\n <p className=\"text-xs text-warning-600 mt-1\">\n {tr(\n 'channel.enterCredentialsAsJson',\n 'You can still enter credentials as JSON in the raw format below.'\n )}\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n const handleFieldChange = (key: string, value: unknown) => {\n onChange({ ...credentials, [key]: value });\n };\n\n const handleFieldBlur = (key: string) => {\n setTouched((prev) => ({ ...prev, [key]: true }));\n };\n\n const togglePasswordVisibility = (key: string) => {\n setShowPasswords((prev) => ({ ...prev, [key]: !prev[key] }));\n };\n\n const renderField = (field: CredentialField) => {\n const value = credentials[field.key];\n const fieldError = touched[field.key] && field.required && !value;\n\n const commonInputClasses = `w-full px-3 py-2 text-sm bg-surface border rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-primary disabled:opacity-50 disabled:cursor-not-allowed ${\n fieldError ? 'border-status-error-border' : 'border-border'\n }`;\n\n const fieldId = `adapter-cred-${field.key}`;\n return (\n <div key={field.key}>\n <label htmlFor={fieldId} className=\"block text-sm font-medium text-foreground mb-1.5\">\n {field.displayName}\n {field.required && <span className=\"text-status-error-text ml-1\">*</span>}\n </label>\n\n {field.type === 'text' && (\n <input\n id={fieldId}\n aria-label={field.displayName}\n type=\"text\"\n value={(value as string) || ''}\n onChange={(e) => handleFieldChange(field.key, e.target.value)}\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n placeholder={field.placeholder}\n className={commonInputClasses}\n />\n )}\n\n {field.type === 'password' && (\n <div className=\"relative\">\n <input\n id={fieldId}\n aria-label={field.displayName}\n type={showPasswords[field.key] ? 'text' : 'password'}\n value={(value as string) || ''}\n onChange={(e) => handleFieldChange(field.key, e.target.value)}\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n placeholder={field.placeholder}\n className={`${commonInputClasses} pr-10`}\n />\n <button\n type=\"button\"\n onClick={() => togglePasswordVisibility(field.key)}\n aria-label={\n showPasswords[field.key] ? `Hide ${field.displayName}` : `Show ${field.displayName}`\n }\n className=\"absolute right-2 top-1/2 -translate-y-1/2 p-1 text-muted-foreground hover:text-foreground\"\n tabIndex={-1}\n >\n {showPasswords[field.key] ? <EyeOff size={16} /> : <Eye size={16} />}\n </button>\n </div>\n )}\n\n {field.type === 'textarea' && (\n <textarea\n id={fieldId}\n aria-label={field.displayName}\n value={(value as string) || ''}\n onChange={(e) => handleFieldChange(field.key, e.target.value)}\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n placeholder={field.placeholder}\n rows={4}\n className={`${commonInputClasses} resize-none font-mono text-xs`}\n />\n )}\n\n {field.type === 'number' && (\n <input\n id={fieldId}\n aria-label={field.displayName}\n type=\"number\"\n value={(value as number) ?? field.defaultValue ?? ''}\n onChange={(e) =>\n handleFieldChange(field.key, e.target.value ? Number(e.target.value) : undefined)\n }\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n placeholder={field.placeholder}\n className={commonInputClasses}\n />\n )}\n\n {field.type === 'boolean' && (\n <label className=\"flex items-center gap-2 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={(value as boolean) ?? field.defaultValue ?? false}\n onChange={(e) => handleFieldChange(field.key, e.target.checked)}\n disabled={disabled}\n className=\"size-4 rounded border-border text-accent-primary focus:ring-accent-primary\"\n />\n <span className=\"text-sm text-muted-foreground\">{field.description}</span>\n </label>\n )}\n\n {field.type === 'select' && field.options && (\n <select\n id={fieldId}\n aria-label={field.displayName}\n value={(value as string) ?? field.defaultValue ?? ''}\n onChange={(e) => handleFieldChange(field.key, e.target.value)}\n onBlur={() => handleFieldBlur(field.key)}\n disabled={disabled}\n className={`${commonInputClasses} bg-bg-surface`}\n >\n <option value=\"\">\n {tr('channel.select', 'Select')} {field.displayName.toLowerCase()}\n </option>\n {field.options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n )}\n\n {field.type !== 'boolean' && (\n <p className=\"mt-1 text-xs text-muted-foreground\">{field.description}</p>\n )}\n\n {fieldError && (\n <p className=\"mt-1 text-xs text-status-error-text\">\n {tr('channel.fieldRequired', 'This field is required')}\n </p>\n )}\n </div>\n );\n };\n\n return (\n <div className=\"space-y-5\">\n {/* Header with adapter info */}\n <div className=\"flex items-start justify-between\">\n <div>\n <h3 className=\"text-sm font-semibold text-foreground\">{schema.displayName}</h3>\n <p className=\"text-xs text-muted-foreground mt-0.5\">{schema.description}</p>\n </div>\n {schema.docUrl && (\n <a\n href={schema.docUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"flex items-center gap-1 text-xs text-accent-primary hover:text-accent-primary-hover\"\n >\n <span>{tr('channel.docs', 'Docs')}</span>\n <ExternalLink size={12} />\n </a>\n )}\n </div>\n\n {/* Credential fields */}\n <div className=\"space-y-4\">\n {schema.credentials.length > 0 && (\n <div className=\"space-y-4\">{schema.credentials.map(renderField)}</div>\n )}\n\n {/* Platform metadata fields (optional settings) */}\n {showPlatformMetadata && schema.platformMetadata && schema.platformMetadata.length > 0 && (\n <>\n <div className=\"border-t border-border pt-4 mt-4\">\n <p className=\"text-xs font-medium text-muted-foreground mb-3\">\n {tr('channel.optionalSettings', 'Optional Settings')}\n </p>\n <div className=\"space-y-4\">{schema.platformMetadata.map(renderField)}</div>\n </div>\n </>\n )}\n </div>\n </div>\n );\n}\n\n/**\n * Compact validation status indicator\n */\nexport function CredentialValidationStatus({\n adapterType,\n credentials,\n}: {\n adapterType: AdapterType | undefined;\n credentials: Record<string, unknown>;\n}) {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n if (!adapterType) return null;\n\n const { valid, errors } = validateCredentials(adapterType, credentials);\n\n if (valid) {\n return (\n <div className=\"flex items-center gap-1.5 text-status-success-text\">\n <CheckCircle2 size={14} />\n <span className=\"text-xs\">\n {tr('channel.allRequiredFieldsFilled', 'All required fields filled')}\n </span>\n </div>\n );\n }\n\n return (\n <div className=\"flex items-center gap-1.5 text-status-warning-text\">\n <AlertCircle size={14} />\n <span className=\"text-xs\">\n {errors.length} {tr('channel.fieldsNeedAttention', 'field(s) need attention')}\n </span>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -8,7 +8,7 @@ function i({ flag: i }) {
|
|
|
8
8
|
className: "flex flex-col items-center justify-center min-h-[400px] gap-4 text-center p-6",
|
|
9
9
|
children: [/* @__PURE__ */ n("div", {
|
|
10
10
|
className: "p-3 rounded-full bg-bg-elevated",
|
|
11
|
-
children: /* @__PURE__ */ n(e, { className: "
|
|
11
|
+
children: /* @__PURE__ */ n(e, { className: "size-8 text-fg-muted" })
|
|
12
12
|
}), /* @__PURE__ */ r("div", {
|
|
13
13
|
className: "space-y-2 max-w-md",
|
|
14
14
|
children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FeatureNotEnabled.js","names":[],"sources":["../../../src/channel/components/FeatureNotEnabled.tsx"],"sourcesContent":["import { Lock } from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\n\ninterface FeatureNotEnabledProps {\n flag: string;\n}\n\n/**\n * Default fallback rendered when a feature flag is disabled. Behaves as a\n * \"404-style\" panel — never throws, never crashes the route.\n *\n * - The flag key is only shown to super admins (developer-facing detail).\n * - The CTA links to Feature Previews so admins can enable the flag directly.\n */\nexport function FeatureNotEnabled({ flag }: FeatureNotEnabledProps) {\n const { hasRole } = usePermissions();\n const isSuperAdmin = hasRole('SUPER_ADMIN');\n\n return (\n <div className=\"flex flex-col items-center justify-center min-h-[400px] gap-4 text-center p-6\">\n <div className=\"p-3 rounded-full bg-bg-elevated\">\n <Lock className=\"
|
|
1
|
+
{"version":3,"file":"FeatureNotEnabled.js","names":[],"sources":["../../../src/channel/components/FeatureNotEnabled.tsx"],"sourcesContent":["import { Lock } from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\n\ninterface FeatureNotEnabledProps {\n flag: string;\n}\n\n/**\n * Default fallback rendered when a feature flag is disabled. Behaves as a\n * \"404-style\" panel — never throws, never crashes the route.\n *\n * - The flag key is only shown to super admins (developer-facing detail).\n * - The CTA links to Feature Previews so admins can enable the flag directly.\n */\nexport function FeatureNotEnabled({ flag }: FeatureNotEnabledProps) {\n const { hasRole } = usePermissions();\n const isSuperAdmin = hasRole('SUPER_ADMIN');\n\n return (\n <div className=\"flex flex-col items-center justify-center min-h-[400px] gap-4 text-center p-6\">\n <div className=\"p-3 rounded-full bg-bg-elevated\">\n <Lock className=\"size-8 text-fg-muted\" />\n </div>\n <div className=\"space-y-2 max-w-md\">\n <h3 className=\"text-lg font-semibold text-fg\">Feature not enabled</h3>\n <p className=\"text-sm text-fg-muted\">\n This feature is currently disabled for your workspace.\n </p>\n {isSuperAdmin ? (\n <p className=\"text-sm text-fg-muted\">\n You can enable it in{' '}\n <a\n href=\"/feature-previews\"\n className=\"text-action-primary-text underline underline-offset-2 hover:text-action-primary-text-hover\"\n >\n Feature Previews\n </a>\n .\n </p>\n ) : (\n <p className=\"text-sm text-fg-muted\">\n Contact your administrator if you believe this is in error.\n </p>\n )}\n {isSuperAdmin && <p className=\"text-xs text-fg-subtle font-mono mt-1\">flag: {flag}</p>}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;AAcA,SAAgB,EAAkB,EAAE,WAAgC;CAClE,IAAM,EAAE,eAAY,GAAgB,EAC9B,IAAe,EAAQ,cAAc;AAE3C,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,GAAD,EAAM,WAAU,wBAAyB,CAAA;GACrC,CAAA,EACN,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,MAAD;KAAI,WAAU;eAAgC;KAAwB,CAAA;IACtE,kBAAC,KAAD;KAAG,WAAU;eAAwB;KAEjC,CAAA;IACH,IACC,kBAAC,KAAD;KAAG,WAAU;eAAb;MAAqC;MACd;MACrB,kBAAC,KAAD;OACE,MAAK;OACL,WAAU;iBACX;OAEG,CAAA;;MAEF;SAEJ,kBAAC,KAAD;KAAG,WAAU;eAAwB;KAEjC,CAAA;IAEL,KAAgB,kBAAC,KAAD;KAAG,WAAU;eAAb,CAAqD,UAAO,EAAS;;IAClF;KACF"}
|
|
@@ -11,7 +11,7 @@ var o = ({ permission: o, children: s }) => {
|
|
|
11
11
|
};
|
|
12
12
|
return l ? /* @__PURE__ */ a("div", {
|
|
13
13
|
className: "flex min-h-screen items-center justify-center bg-bg-canvas",
|
|
14
|
-
children: /* @__PURE__ */ a(n, { className: "
|
|
14
|
+
children: /* @__PURE__ */ a(n, { className: "size-8 animate-spin text-primary" })
|
|
15
15
|
}) : c(o) ? /* @__PURE__ */ a(i, { children: s }) : /* @__PURE__ */ a(e, {
|
|
16
16
|
title: d("channel.accessDenied", "Access Denied"),
|
|
17
17
|
description: d("channel.noPermissionView", "You don't have permission to view this page.")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PermissionGuard.js","names":[],"sources":["../../../src/channel/components/PermissionGuard.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { Loader2 } from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport type { ChannelPermission } from '../constants/permissions';\n\ninterface PermissionGuardProps {\n permission: ChannelPermission;\n children: ReactNode;\n}\n\n/**\n * Wraps page content with a permission check.\n * Shows a loading spinner while permissions resolve, then either\n * renders children (if permitted) or an AccessDenied screen.\n */\nexport const PermissionGuard: FC<PermissionGuardProps> = ({ permission, children }) => {\n const { hasPermission, isLoading } = usePermissions();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n if (isLoading) {\n return (\n <div className=\"flex min-h-screen items-center justify-center bg-bg-canvas\">\n <Loader2 className=\"
|
|
1
|
+
{"version":3,"file":"PermissionGuard.js","names":[],"sources":["../../../src/channel/components/PermissionGuard.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { Loader2 } from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport type { ChannelPermission } from '../constants/permissions';\n\ninterface PermissionGuardProps {\n permission: ChannelPermission;\n children: ReactNode;\n}\n\n/**\n * Wraps page content with a permission check.\n * Shows a loading spinner while permissions resolve, then either\n * renders children (if permitted) or an AccessDenied screen.\n */\nexport const PermissionGuard: FC<PermissionGuardProps> = ({ permission, children }) => {\n const { hasPermission, isLoading } = usePermissions();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n if (isLoading) {\n return (\n <div className=\"flex min-h-screen items-center justify-center bg-bg-canvas\">\n <Loader2 className=\"size-8 animate-spin text-primary\" />\n </div>\n );\n }\n\n if (!hasPermission(permission)) {\n return (\n <AccessDenied\n title={tr('channel.accessDenied', 'Access Denied')}\n description={tr('channel.noPermissionView', \"You don't have permission to view this page.\")}\n />\n );\n }\n\n return <>{children}</>;\n};\n"],"mappings":";;;;;;AAiBA,IAAa,KAA6C,EAAE,eAAY,kBAAe;CACrF,IAAM,EAAE,kBAAe,iBAAc,GAAgB,EAC/C,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAQ,EAAE,EAAI;AACpB,SAAO,MAAU,IAAM,IAAW;;AAoBpC,QAjBI,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAS,WAAU,oCAAqC,CAAA;EACpD,CAAA,GAIL,EAAc,EAAW,GASvB,kBAAA,GAAA,EAAG,aAAY,CAAA,GAPlB,kBAAC,GAAD;EACE,OAAO,EAAG,wBAAwB,gBAAgB;EAClD,aAAa,EAAG,4BAA4B,+CAA+C;EAC3F,CAAA"}
|
|
@@ -913,27 +913,35 @@ var e = {
|
|
|
913
913
|
function t(t) {
|
|
914
914
|
return t && e[t] || null;
|
|
915
915
|
}
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
916
|
+
var n = /* @__PURE__ */ new Map();
|
|
917
|
+
function r(e) {
|
|
918
|
+
let t = n.get(e);
|
|
919
|
+
return t || (t = new RegExp(e), n.set(e, t)), t;
|
|
920
|
+
}
|
|
921
|
+
function i(e, n) {
|
|
922
|
+
let i = t(e);
|
|
923
|
+
if (!i) return {
|
|
919
924
|
valid: !1,
|
|
920
925
|
errors: ["Unknown adapter type"]
|
|
921
926
|
};
|
|
922
|
-
let
|
|
923
|
-
for (let e of
|
|
927
|
+
let a = [];
|
|
928
|
+
for (let e of i.credentials) {
|
|
924
929
|
let t = n[e.key];
|
|
925
930
|
if (e.required && (t == null || t === "")) {
|
|
926
|
-
|
|
931
|
+
a.push(`${e.displayName} is required`);
|
|
927
932
|
continue;
|
|
928
933
|
}
|
|
929
|
-
!e.required && (t == null || t === "")
|
|
934
|
+
if (!(!e.required && (t == null || t === "")) && (e.validation?.pattern && typeof t == "string" && (r(e.validation.pattern).test(t) || a.push(`${e.displayName} has an invalid format`)), typeof t == "string")) {
|
|
935
|
+
let n = e.validation?.minLength, r = e.validation?.maxLength;
|
|
936
|
+
n && t.length < n && a.push(`${e.displayName} must be at least ${n} characters`), r && t.length > r && a.push(`${e.displayName} must be at most ${r} characters`);
|
|
937
|
+
}
|
|
930
938
|
}
|
|
931
939
|
return {
|
|
932
|
-
valid:
|
|
933
|
-
errors:
|
|
940
|
+
valid: a.length === 0,
|
|
941
|
+
errors: a
|
|
934
942
|
};
|
|
935
943
|
}
|
|
936
|
-
function
|
|
944
|
+
function a(e) {
|
|
937
945
|
let n = t(e);
|
|
938
946
|
if (!n) return {};
|
|
939
947
|
let r = {};
|
|
@@ -941,6 +949,6 @@ function r(e) {
|
|
|
941
949
|
return r;
|
|
942
950
|
}
|
|
943
951
|
//#endregion
|
|
944
|
-
export { t as getAdapterCredentialSchema,
|
|
952
|
+
export { t as getAdapterCredentialSchema, a as getDefaultCredentials, i as validateCredentials };
|
|
945
953
|
|
|
946
954
|
//# sourceMappingURL=adapterCredentials.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapterCredentials.js","names":[],"sources":["../../../src/channel/config/adapterCredentials.ts"],"sourcesContent":["/**\n * Adapter Credential Configuration\n *\n * Defines the credential fields required by each adapter type.\n * Field names MUST match exactly what the backend adapters expect.\n *\n * Based on Novu's pattern: https://docs.novu.co/community/add-a-new-provider\n */\n\nimport type { AdapterType } from '../hooks/useChannels';\n\nexport interface CredentialField {\n key: string;\n displayName: string;\n description: string;\n type: 'text' | 'password' | 'textarea' | 'number' | 'boolean' | 'select';\n required: boolean;\n placeholder?: string;\n defaultValue?: string | number | boolean;\n options?: { value: string; label: string }[];\n validation?: {\n pattern?: string;\n minLength?: number;\n maxLength?: number;\n };\n}\n\nexport interface AdapterCredentialSchema {\n displayName: string;\n description: string;\n docUrl?: string;\n logoUrl?: string;\n credentials: CredentialField[];\n platformMetadata?: CredentialField[];\n}\n\n/**\n * Credential schemas for each adapter type.\n * Keys match the AdapterType enum values.\n */\nconst ADAPTER_CREDENTIALS: Partial<Record<AdapterType, AdapterCredentialSchema>> = {\n // ===================\n // SMS ADAPTERS\n // ===================\n TWILIO_SMS: {\n displayName: 'Twilio SMS',\n description: 'Send SMS via Twilio',\n docUrl: 'https://www.twilio.com/docs/sms',\n credentials: [\n {\n key: 'accountSid',\n displayName: 'Account SID',\n description: 'Your Twilio Account SID (starts with AC)',\n type: 'text',\n required: true,\n placeholder: 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',\n validation: { pattern: '^AC[a-zA-Z0-9]{32}$' },\n },\n {\n key: 'authToken',\n displayName: 'Auth Token',\n description: 'Your Twilio Auth Token',\n type: 'password',\n required: true,\n placeholder: 'Your auth token',\n },\n {\n key: 'fromNumber',\n displayName: 'From Phone Number',\n description: 'Sender phone number in E.164 format',\n type: 'text',\n required: true,\n placeholder: '+1234567890',\n validation: { pattern: '^\\\\+[1-9]\\\\d{1,14}$' },\n },\n ],\n platformMetadata: [\n {\n key: 'statusCallbackUrl',\n displayName: 'Status Callback URL',\n description: 'Webhook URL for delivery status updates (optional)',\n type: 'text',\n required: false,\n placeholder: 'https://your-domain.com/webhooks/twilio',\n },\n ],\n },\n\n AWS_SNS: {\n displayName: 'AWS SNS',\n description: 'Send SMS via Amazon Simple Notification Service',\n docUrl: 'https://docs.aws.amazon.com/sns/',\n credentials: [\n {\n key: 'accessKeyId',\n displayName: 'AWS Access Key ID',\n description: 'Your AWS access key ID',\n type: 'text',\n required: true,\n placeholder: 'AKIAIOSFODNN7EXAMPLE',\n },\n {\n key: 'secretAccessKey',\n displayName: 'AWS Secret Access Key',\n description: 'Your AWS secret access key',\n type: 'password',\n required: true,\n placeholder: 'Your secret access key',\n },\n {\n key: 'region',\n displayName: 'AWS Region',\n description: 'AWS region for SNS service',\n type: 'select',\n required: true,\n defaultValue: 'us-east-1',\n options: [\n { value: 'us-east-1', label: 'US East (N. Virginia)' },\n { value: 'us-east-2', label: 'US East (Ohio)' },\n { value: 'us-west-1', label: 'US West (N. California)' },\n { value: 'us-west-2', label: 'US West (Oregon)' },\n { value: 'ca-central-1', label: 'Canada (Central)' },\n { value: 'ca-west-1', label: 'Canada West (Calgary)' },\n { value: 'eu-west-1', label: 'Europe (Ireland)' },\n { value: 'eu-west-2', label: 'Europe (London)' },\n { value: 'eu-west-3', label: 'Europe (Paris)' },\n { value: 'eu-central-1', label: 'Europe (Frankfurt)' },\n { value: 'eu-central-2', label: 'Europe (Zurich)' },\n { value: 'eu-north-1', label: 'Europe (Stockholm)' },\n { value: 'eu-south-1', label: 'Europe (Milan)' },\n { value: 'eu-south-2', label: 'Europe (Spain)' },\n { value: 'ap-south-1', label: 'Asia Pacific (Mumbai)' },\n { value: 'ap-south-2', label: 'Asia Pacific (Hyderabad)' },\n { value: 'ap-southeast-1', label: 'Asia Pacific (Singapore)' },\n { value: 'ap-southeast-2', label: 'Asia Pacific (Sydney)' },\n { value: 'ap-southeast-3', label: 'Asia Pacific (Jakarta)' },\n { value: 'ap-southeast-4', label: 'Asia Pacific (Melbourne)' },\n { value: 'ap-northeast-1', label: 'Asia Pacific (Tokyo)' },\n { value: 'ap-northeast-2', label: 'Asia Pacific (Seoul)' },\n { value: 'ap-northeast-3', label: 'Asia Pacific (Osaka)' },\n { value: 'ap-east-1', label: 'Asia Pacific (Hong Kong)' },\n { value: 'sa-east-1', label: 'South America (São Paulo)' },\n { value: 'me-south-1', label: 'Middle East (Bahrain)' },\n { value: 'me-central-1', label: 'Middle East (UAE)' },\n { value: 'af-south-1', label: 'Africa (Cape Town)' },\n { value: 'il-central-1', label: 'Israel (Tel Aviv)' },\n ],\n },\n ],\n platformMetadata: [\n {\n key: 'senderId',\n displayName: 'Sender ID',\n description: 'Sender ID for supported regions (optional)',\n type: 'text',\n required: false,\n placeholder: 'YourBrand',\n },\n {\n key: 'messageType',\n displayName: 'Message Type',\n description: 'SMS message type',\n type: 'select',\n required: false,\n defaultValue: 'Transactional',\n options: [\n { value: 'Transactional', label: 'Transactional' },\n { value: 'Promotional', label: 'Promotional' },\n ],\n },\n ],\n },\n\n // ===================\n // EMAIL ADAPTERS\n // ===================\n SENDGRID: {\n displayName: 'SendGrid',\n description: 'Send emails via SendGrid',\n docUrl: 'https://docs.sendgrid.com/',\n credentials: [\n {\n key: 'apiKey',\n displayName: 'API Key',\n description: 'Your SendGrid API Key',\n type: 'password',\n required: true,\n placeholder: 'SG.xxxx...',\n },\n {\n key: 'fromEmail',\n displayName: 'From Email',\n description: 'Verified sender email address',\n type: 'text',\n required: true,\n placeholder: 'noreply@yourdomain.com',\n },\n {\n key: 'fromName',\n displayName: 'From Name',\n description: 'Sender display name',\n type: 'text',\n required: false,\n placeholder: 'Your Company',\n defaultValue: 'Notification',\n },\n ],\n },\n\n AWS_SES: {\n displayName: 'AWS SES',\n description: 'Send emails via Amazon Simple Email Service',\n docUrl: 'https://docs.aws.amazon.com/ses/',\n credentials: [\n {\n key: 'accessKeyId',\n displayName: 'AWS Access Key ID',\n description: 'Your AWS access key ID',\n type: 'text',\n required: true,\n placeholder: 'AKIAIOSFODNN7EXAMPLE',\n },\n {\n key: 'secretAccessKey',\n displayName: 'AWS Secret Access Key',\n description: 'Your AWS secret access key',\n type: 'password',\n required: true,\n placeholder: 'Your secret access key',\n },\n {\n key: 'region',\n displayName: 'AWS Region',\n description: 'AWS region for SES service',\n type: 'select',\n required: true,\n defaultValue: 'us-east-1',\n options: [\n { value: 'us-east-1', label: 'US East (N. Virginia)' },\n { value: 'us-east-2', label: 'US East (Ohio)' },\n { value: 'us-west-1', label: 'US West (N. California)' },\n { value: 'us-west-2', label: 'US West (Oregon)' },\n { value: 'ca-central-1', label: 'Canada (Central)' },\n { value: 'ca-west-1', label: 'Canada West (Calgary)' },\n { value: 'eu-west-1', label: 'Europe (Ireland)' },\n { value: 'eu-west-2', label: 'Europe (London)' },\n { value: 'eu-west-3', label: 'Europe (Paris)' },\n { value: 'eu-central-1', label: 'Europe (Frankfurt)' },\n { value: 'eu-central-2', label: 'Europe (Zurich)' },\n { value: 'eu-north-1', label: 'Europe (Stockholm)' },\n { value: 'eu-south-1', label: 'Europe (Milan)' },\n { value: 'eu-south-2', label: 'Europe (Spain)' },\n { value: 'ap-south-1', label: 'Asia Pacific (Mumbai)' },\n { value: 'ap-south-2', label: 'Asia Pacific (Hyderabad)' },\n { value: 'ap-southeast-1', label: 'Asia Pacific (Singapore)' },\n { value: 'ap-southeast-2', label: 'Asia Pacific (Sydney)' },\n { value: 'ap-southeast-3', label: 'Asia Pacific (Jakarta)' },\n { value: 'ap-southeast-4', label: 'Asia Pacific (Melbourne)' },\n { value: 'ap-northeast-1', label: 'Asia Pacific (Tokyo)' },\n { value: 'ap-northeast-2', label: 'Asia Pacific (Seoul)' },\n { value: 'ap-northeast-3', label: 'Asia Pacific (Osaka)' },\n { value: 'ap-east-1', label: 'Asia Pacific (Hong Kong)' },\n { value: 'sa-east-1', label: 'South America (São Paulo)' },\n { value: 'me-south-1', label: 'Middle East (Bahrain)' },\n { value: 'me-central-1', label: 'Middle East (UAE)' },\n { value: 'af-south-1', label: 'Africa (Cape Town)' },\n { value: 'il-central-1', label: 'Israel (Tel Aviv)' },\n ],\n },\n {\n key: 'fromEmail',\n displayName: 'From Email',\n description: 'Verified sender email address',\n type: 'text',\n required: false,\n placeholder: 'noreply@yourdomain.com',\n },\n ],\n platformMetadata: [\n {\n key: 'configurationSetName',\n displayName: 'Configuration Set',\n description: 'SES Configuration Set for tracking (optional)',\n type: 'text',\n required: false,\n placeholder: 'my-configuration-set',\n },\n ],\n },\n\n AZURE_ACS: {\n displayName: 'Azure Communication Services',\n description: 'Send emails via Azure Communication Services',\n docUrl: 'https://docs.microsoft.com/azure/communication-services/',\n credentials: [\n {\n key: 'connectionString',\n displayName: 'Connection String',\n description: 'Azure ACS connection string (endpoint=...;accesskey=...)',\n type: 'password',\n required: true,\n placeholder: 'endpoint=https://your-resource.communication.azure.com/;accesskey=...',\n },\n {\n key: 'senderAddress',\n displayName: 'Sender Email',\n description: 'Azure verified sender email address',\n type: 'text',\n required: true,\n placeholder: 'DoNotReply@your-domain.azurecomm.net',\n },\n ],\n },\n\n MAILGUN: {\n displayName: 'Mailgun',\n description: 'Send emails via Mailgun',\n docUrl: 'https://documentation.mailgun.com/',\n credentials: [\n {\n key: 'apiKey',\n displayName: 'API Key',\n description: 'Your Mailgun API Key',\n type: 'password',\n required: true,\n placeholder: 'key-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',\n },\n {\n key: 'domain',\n displayName: 'Domain',\n description: 'Your Mailgun sending domain',\n type: 'text',\n required: true,\n placeholder: 'mg.yourdomain.com',\n },\n {\n key: 'fromEmail',\n displayName: 'From Email',\n description: 'Sender email address',\n type: 'text',\n required: true,\n placeholder: 'noreply@mg.yourdomain.com',\n },\n {\n key: 'fromName',\n displayName: 'From Name',\n description: 'Sender display name',\n type: 'text',\n required: false,\n placeholder: 'Your Company',\n },\n ],\n platformMetadata: [\n {\n key: 'region',\n displayName: 'Region',\n description: 'Mailgun region',\n type: 'select',\n required: false,\n defaultValue: 'us',\n options: [\n { value: 'us', label: 'US' },\n { value: 'eu', label: 'EU' },\n ],\n },\n ],\n },\n\n POSTMARK: {\n displayName: 'Postmark',\n description: 'Send emails via Postmark',\n docUrl: 'https://postmarkapp.com/developer',\n credentials: [\n {\n key: 'serverToken',\n displayName: 'Server API Token',\n description: 'Your Postmark Server API Token',\n type: 'password',\n required: true,\n placeholder: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',\n },\n {\n key: 'fromEmail',\n displayName: 'From Email',\n description: 'Verified sender email address',\n type: 'text',\n required: true,\n placeholder: 'noreply@yourdomain.com',\n },\n {\n key: 'fromName',\n displayName: 'From Name',\n description: 'Sender display name',\n type: 'text',\n required: false,\n placeholder: 'Your Company',\n },\n ],\n },\n\n RESEND: {\n displayName: 'Resend',\n description: 'Send emails via Resend',\n docUrl: 'https://resend.com/docs',\n credentials: [\n {\n key: 'apiKey',\n displayName: 'API Key',\n description: 'Your Resend API Key',\n type: 'password',\n required: true,\n placeholder: 're_xxxxxxxxxxxxxxxxxxxxxxxxxxxx',\n },\n {\n key: 'fromEmail',\n displayName: 'From Email',\n description: 'Verified sender email address',\n type: 'text',\n required: true,\n placeholder: 'noreply@yourdomain.com',\n },\n {\n key: 'fromName',\n displayName: 'From Name',\n description: 'Sender display name',\n type: 'text',\n required: false,\n placeholder: 'Your Company',\n },\n ],\n },\n\n SMTP: {\n displayName: 'Custom SMTP',\n description: 'Send emails via custom SMTP server',\n credentials: [\n {\n key: 'host',\n displayName: 'SMTP Host',\n description: 'SMTP server hostname',\n type: 'text',\n required: true,\n placeholder: 'smtp.yourdomain.com',\n },\n {\n key: 'port',\n displayName: 'SMTP Port',\n description: 'SMTP server port (usually 587 for TLS, 465 for SSL)',\n type: 'number',\n required: true,\n defaultValue: 587,\n placeholder: '587',\n },\n {\n key: 'username',\n displayName: 'Username',\n description: 'SMTP authentication username',\n type: 'text',\n required: true,\n placeholder: 'your-username',\n },\n {\n key: 'password',\n displayName: 'Password',\n description: 'SMTP authentication password',\n type: 'password',\n required: true,\n placeholder: 'Your password',\n },\n {\n key: 'fromEmail',\n displayName: 'From Email',\n description: 'Sender email address',\n type: 'text',\n required: true,\n placeholder: 'noreply@yourdomain.com',\n },\n {\n key: 'fromName',\n displayName: 'From Name',\n description: 'Sender display name',\n type: 'text',\n required: false,\n placeholder: 'Your Company',\n },\n ],\n platformMetadata: [\n {\n key: 'secure',\n displayName: 'Use SSL/TLS',\n description: 'Use SSL/TLS encryption',\n type: 'boolean',\n required: false,\n defaultValue: true,\n },\n ],\n },\n\n // ===================\n // PUSH NOTIFICATION ADAPTERS\n // ===================\n FCM: {\n displayName: 'Firebase Cloud Messaging',\n description: 'Send push notifications via FCM',\n docUrl: 'https://firebase.google.com/docs/cloud-messaging',\n credentials: [\n {\n key: 'projectId',\n displayName: 'Firebase Project ID',\n description: 'Your Firebase project ID',\n type: 'text',\n required: true,\n placeholder: 'your-project-id',\n },\n {\n key: 'clientEmail',\n displayName: 'Service Account Email',\n description: 'Service account email from Firebase credentials JSON',\n type: 'text',\n required: true,\n placeholder: 'firebase-adminsdk-xxxxx@your-project.iam.gserviceaccount.com',\n },\n {\n key: 'privateKey',\n displayName: 'Private Key',\n description:\n 'Service account private key (PEM format, paste the entire key including BEGIN/END lines)',\n type: 'textarea',\n required: true,\n placeholder: 'Paste the full PEM private key from your service account JSON',\n },\n ],\n },\n\n APNS: {\n displayName: 'Apple Push Notification Service',\n description: 'Send push notifications to iOS devices via APNs',\n docUrl: 'https://developer.apple.com/documentation/usernotifications',\n credentials: [\n {\n key: 'teamId',\n displayName: 'Team ID',\n description: 'Your 10-character Apple Developer Team ID',\n type: 'text',\n required: true,\n placeholder: 'ABCDE12345',\n validation: { pattern: '^[A-Z0-9]{10}$' },\n },\n {\n key: 'keyId',\n displayName: 'Key ID',\n description: 'Your 10-character Key ID from Apple Developer Account',\n type: 'text',\n required: true,\n placeholder: 'ABC123DEFG',\n validation: { pattern: '^[A-Z0-9]{10}$' },\n },\n {\n key: 'privateKey',\n displayName: 'Private Key (.p8)',\n description: 'Content of your .p8 private key file',\n type: 'textarea',\n required: true,\n placeholder: '-----BEGIN PRIVATE KEY-----\\nYour .p8 key content\\n-----END PRIVATE KEY-----',\n },\n {\n key: 'bundleId',\n displayName: 'Bundle ID',\n description: \"Your app's Bundle Identifier\",\n type: 'text',\n required: true,\n placeholder: 'com.yourcompany.yourapp',\n },\n ],\n platformMetadata: [\n {\n key: 'production',\n displayName: 'Production Environment',\n description: 'Use production APNs server (uncheck for sandbox/development)',\n type: 'boolean',\n required: false,\n defaultValue: false,\n },\n ],\n },\n\n ONESIGNAL: {\n displayName: 'OneSignal',\n description: 'Send push notifications via OneSignal',\n docUrl: 'https://documentation.onesignal.com/',\n credentials: [\n {\n key: 'appId',\n displayName: 'App ID',\n description: 'Your OneSignal App ID',\n type: 'text',\n required: true,\n placeholder: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',\n },\n {\n key: 'apiKey',\n displayName: 'REST API Key',\n description: 'Your OneSignal REST API Key',\n type: 'password',\n required: true,\n placeholder: 'Your API key',\n },\n ],\n },\n\n EXPO: {\n displayName: 'Expo Push',\n description: 'Send push notifications via Expo',\n docUrl: 'https://docs.expo.dev/push-notifications/',\n credentials: [\n {\n key: 'accessToken',\n displayName: 'Access Token',\n description: 'Your Expo access token (optional for increased rate limits)',\n type: 'password',\n required: false,\n placeholder: 'Your Expo access token',\n },\n ],\n },\n\n // ===================\n // MESSAGING PLATFORM ADAPTERS\n // ===================\n SLACK_BOT: {\n displayName: 'Slack Bot',\n description: 'Send messages via Slack Bot',\n docUrl: 'https://api.slack.com/bot-users',\n credentials: [\n {\n key: 'botToken',\n displayName: 'Bot Token',\n description: 'Your Slack Bot User OAuth Token (xoxb-...)',\n type: 'password',\n required: true,\n placeholder: 'xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx',\n },\n ],\n platformMetadata: [\n {\n key: 'defaultChannel',\n displayName: 'Default Channel',\n description: 'Default channel ID for messages (optional)',\n type: 'text',\n required: false,\n placeholder: 'C0123456789',\n },\n ],\n },\n\n SLACK_WEBHOOK: {\n displayName: 'Slack Webhook',\n description: 'Send messages via Slack Incoming Webhook',\n docUrl: 'https://api.slack.com/messaging/webhooks',\n credentials: [\n {\n key: 'webhookUrl',\n displayName: 'Webhook URL',\n description: 'Your Slack Incoming Webhook URL',\n type: 'password',\n required: true,\n placeholder: 'Paste your Slack Incoming Webhook URL',\n },\n ],\n },\n\n TEAMS_WEBHOOK: {\n displayName: 'Microsoft Teams Webhook',\n description: 'Send messages via Teams Incoming Webhook',\n docUrl: 'https://docs.microsoft.com/microsoftteams/platform/webhooks-and-connectors/',\n credentials: [\n {\n key: 'webhookUrl',\n displayName: 'Webhook URL',\n description: 'Your Microsoft Teams Incoming Webhook URL',\n type: 'password',\n required: true,\n placeholder: 'https://outlook.office.com/webhook/...',\n },\n ],\n },\n\n DISCORD_BOT: {\n displayName: 'Discord Bot',\n description: 'Send messages via Discord Bot',\n docUrl: 'https://discord.com/developers/docs',\n credentials: [\n {\n key: 'botToken',\n displayName: 'Bot Token',\n description: 'Your Discord Bot Token',\n type: 'password',\n required: true,\n placeholder: 'Your Discord bot token',\n },\n ],\n platformMetadata: [\n {\n key: 'defaultChannelId',\n displayName: 'Default Channel ID',\n description: 'Default channel ID for messages (optional)',\n type: 'text',\n required: false,\n placeholder: '123456789012345678',\n },\n ],\n },\n\n DISCORD_WEBHOOK: {\n displayName: 'Discord Webhook',\n description: 'Send messages via Discord Webhook',\n docUrl: 'https://discord.com/developers/docs/resources/webhook',\n credentials: [\n {\n key: 'webhookUrl',\n displayName: 'Webhook URL',\n description: 'Your Discord Webhook URL',\n type: 'password',\n required: true,\n placeholder: 'https://discord.com/api/webhooks/...',\n },\n ],\n },\n\n WHATSAPP_CLOUD: {\n displayName: 'WhatsApp Cloud API',\n description: 'Send messages via WhatsApp Business Cloud API',\n docUrl: 'https://developers.facebook.com/docs/whatsapp/cloud-api',\n credentials: [\n {\n key: 'accessToken',\n displayName: 'Access Token',\n description: 'Your WhatsApp Business API Access Token',\n type: 'password',\n required: true,\n placeholder: 'Your access token',\n },\n {\n key: 'phoneNumberId',\n displayName: 'Phone Number ID',\n description: 'Your WhatsApp Business Phone Number ID',\n type: 'text',\n required: true,\n placeholder: '123456789012345',\n },\n {\n key: 'businessAccountId',\n displayName: 'Business Account ID',\n description: 'Your WhatsApp Business Account ID',\n type: 'text',\n required: true,\n placeholder: '123456789012345',\n },\n ],\n },\n\n WHATSAPP_TWILIO: {\n displayName: 'WhatsApp via Twilio',\n description: 'Send WhatsApp messages via Twilio',\n docUrl: 'https://www.twilio.com/docs/whatsapp',\n credentials: [\n {\n key: 'accountSid',\n displayName: 'Account SID',\n description: 'Your Twilio Account SID (starts with AC)',\n type: 'text',\n required: true,\n placeholder: 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',\n },\n {\n key: 'authToken',\n displayName: 'Auth Token',\n description: 'Your Twilio Auth Token',\n type: 'password',\n required: true,\n placeholder: 'Your auth token',\n },\n {\n key: 'fromNumber',\n displayName: 'WhatsApp Number',\n description: 'Twilio WhatsApp number (whatsapp:+1234567890)',\n type: 'text',\n required: true,\n placeholder: 'whatsapp:+1234567890',\n },\n ],\n },\n\n // ===================\n // WEBHOOK ADAPTERS\n // ===================\n WEBHOOK_HTTP: {\n displayName: 'HTTP Webhook',\n description: 'Send notifications via HTTP webhook',\n credentials: [\n {\n key: 'webhookUrl',\n displayName: 'Webhook URL',\n description: 'HTTP endpoint to receive notifications',\n type: 'text',\n required: true,\n placeholder: 'https://your-api.com/webhooks/notifications',\n },\n ],\n platformMetadata: [\n {\n key: 'method',\n displayName: 'HTTP Method',\n description: 'HTTP method for the request',\n type: 'select',\n required: false,\n defaultValue: 'POST',\n options: [\n { value: 'POST', label: 'POST' },\n { value: 'PUT', label: 'PUT' },\n ],\n },\n {\n key: 'headers',\n displayName: 'Custom Headers (JSON)',\n description: 'Additional HTTP headers as JSON object',\n type: 'textarea',\n required: false,\n placeholder: '{\"Authorization\": \"Bearer token\", \"X-Custom-Header\": \"value\"}',\n },\n {\n key: 'secret',\n displayName: 'Signing Secret',\n description: 'Secret for HMAC signature verification (optional)',\n type: 'password',\n required: false,\n placeholder: 'Your webhook signing secret',\n },\n ],\n },\n};\n\n/**\n * Get credential schema for an adapter type\n */\nexport function getAdapterCredentialSchema(\n adapterType: AdapterType | undefined\n): AdapterCredentialSchema | null {\n if (!adapterType) return null;\n return ADAPTER_CREDENTIALS[adapterType] || null;\n}\n\n/**\n * Validate credentials against adapter schema\n */\nexport function validateCredentials(\n adapterType: AdapterType | undefined,\n credentials: Record<string, unknown>\n): { valid: boolean; errors: string[] } {\n const schema = getAdapterCredentialSchema(adapterType);\n if (!schema) {\n return { valid: false, errors: ['Unknown adapter type'] };\n }\n\n const errors: string[] = [];\n\n for (const field of schema.credentials) {\n const value = credentials[field.key];\n\n // Check required fields\n if (field.required && (value === undefined || value === null || value === '')) {\n errors.push(`${field.displayName} is required`);\n continue;\n }\n\n // Skip validation for empty optional fields\n if (!field.required && (value === undefined || value === null || value === '')) {\n continue;\n }\n\n // Validate pattern if specified\n if (field.validation?.pattern && typeof value === 'string') {\n const regex = new RegExp(field.validation.pattern);\n if (!regex.test(value)) {\n errors.push(`${field.displayName} has an invalid format`);\n }\n }\n\n // Validate min/max length\n if (typeof value === 'string') {\n if (field.validation?.minLength && value.length < field.validation.minLength) {\n errors.push(\n `${field.displayName} must be at least ${field.validation.minLength} characters`\n );\n }\n if (field.validation?.maxLength && value.length > field.validation.maxLength) {\n errors.push(\n `${field.displayName} must be at most ${field.validation.maxLength} characters`\n );\n }\n }\n }\n\n return { valid: errors.length === 0, errors };\n}\n\n/**\n * Get default values for an adapter's credentials\n */\nexport function getDefaultCredentials(\n adapterType: AdapterType | undefined\n): Record<string, unknown> {\n const schema = getAdapterCredentialSchema(adapterType);\n if (!schema) return {};\n\n const defaults: Record<string, unknown> = {};\n for (const field of [...schema.credentials, ...(schema.platformMetadata || [])]) {\n if (field.defaultValue !== undefined) {\n defaults[field.key] = field.defaultValue;\n }\n }\n return defaults;\n}\n"],"mappings":"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CR,aAAa,CACX;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,EACD;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACF;CAED,SAAS;EACP,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa;GACX;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACF;EACD,kBAAkB,CAChB;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,cAAc;GACd,SAAS,CACP;IAAE,OAAO;IAAM,OAAO;IAAM,EAC5B;IAAE,OAAO;IAAM,OAAO;IAAM,CAC7B;GACF,CACF;EACF;CAED,UAAU;EACR,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa;GACX;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACF;EACF;CAED,QAAQ;EACN,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa;GACX;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACF;EACF;CAED,MAAM;EACJ,aAAa;EACb,aAAa;EACb,aAAa;GACX;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,cAAc;IACd,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACF;EACD,kBAAkB,CAChB;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,cAAc;GACf,CACF;EACF;CAKD,KAAK;EACH,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa;GACX;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aACE;IACF,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACF;EACF;CAED,MAAM;EACJ,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa;GACX;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACb,YAAY,EAAE,SAAS,kBAAkB;IAC1C;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACb,YAAY,EAAE,SAAS,kBAAkB;IAC1C;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACF;EACD,kBAAkB,CAChB;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,cAAc;GACf,CACF;EACF;CAED,WAAW;EACT,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa,CACX;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,EACD;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACF;CAED,MAAM;EACJ,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa,CACX;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACF;CAKD,WAAW;EACT,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa,CACX;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACD,kBAAkB,CAChB;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACF;CAED,eAAe;EACb,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa,CACX;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACF;CAED,eAAe;EACb,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa,CACX;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACF;CAED,aAAa;EACX,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa,CACX;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACD,kBAAkB,CAChB;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACF;CAED,iBAAiB;EACf,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa,CACX;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACF;CAED,gBAAgB;EACd,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa;GACX;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACF;EACF;CAED,iBAAiB;EACf,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa;GACX;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACF;EACF;CAKD,cAAc;EACZ,aAAa;EACb,aAAa;EACb,aAAa,CACX;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACD,kBAAkB;GAChB;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,cAAc;IACd,SAAS,CACP;KAAE,OAAO;KAAQ,OAAO;KAAQ,EAChC;KAAE,OAAO;KAAO,OAAO;KAAO,CAC/B;IACF;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACF;EACF;CACF;AAKD,SAAgB,EACd,GACgC;AAEhC,QADK,KACE,EAAoB,MADF;;AAO3B,SAAgB,EACd,GACA,GACsC;CACtC,IAAM,IAAS,EAA2B,EAAY;AACtD,KAAI,CAAC,EACH,QAAO;EAAE,OAAO;EAAO,QAAQ,CAAC,uBAAuB;EAAE;CAG3D,IAAM,IAAmB,EAAE;AAE3B,MAAK,IAAM,KAAS,EAAO,aAAa;EACtC,IAAM,IAAQ,EAAY,EAAM;AAGhC,MAAI,EAAM,aAAa,KAAiC,QAAQ,MAAU,KAAK;AAC7E,KAAO,KAAK,GAAG,EAAM,YAAY,cAAc;AAC/C;;AAIE,GAAC,EAAM,aAAa,KAAiC,QAAQ,MAAU,QAKvE,EAAM,YAAY,WAAW,OAAO,KAAU,aAClC,IAAI,OAAO,EAAM,WAAW,QAAQ,CACvC,KAAK,EAAM,IACpB,EAAO,KAAK,GAAG,EAAM,YAAY,wBAAwB,GAKzD,OAAO,KAAU,aACf,EAAM,YAAY,aAAa,EAAM,SAAS,EAAM,WAAW,aACjE,EAAO,KACL,GAAG,EAAM,YAAY,oBAAoB,EAAM,WAAW,UAAU,aACrE,EAEC,EAAM,YAAY,aAAa,EAAM,SAAS,EAAM,WAAW,aACjE,EAAO,KACL,GAAG,EAAM,YAAY,mBAAmB,EAAM,WAAW,UAAU,aACpE;;AAKP,QAAO;EAAE,OAAO,EAAO,WAAW;EAAG;EAAQ;;AAM/C,SAAgB,EACd,GACyB;CACzB,IAAM,IAAS,EAA2B,EAAY;AACtD,KAAI,CAAC,EAAQ,QAAO,EAAE;CAEtB,IAAM,IAAoC,EAAE;AAC5C,MAAK,IAAM,KAAS,CAAC,GAAG,EAAO,aAAa,GAAI,EAAO,oBAAoB,EAAE,CAAE,CAC7E,CAAI,EAAM,iBAAiB,KAAA,MACzB,EAAS,EAAM,OAAO,EAAM;AAGhC,QAAO"}
|
|
1
|
+
{"version":3,"file":"adapterCredentials.js","names":[],"sources":["../../../src/channel/config/adapterCredentials.ts"],"sourcesContent":["/**\n * Adapter Credential Configuration\n *\n * Defines the credential fields required by each adapter type.\n * Field names MUST match exactly what the backend adapters expect.\n *\n * Based on Novu's pattern: https://docs.novu.co/community/add-a-new-provider\n */\n\nimport type { AdapterType } from '../hooks/useChannels';\n\nexport interface CredentialField {\n key: string;\n displayName: string;\n description: string;\n type: 'text' | 'password' | 'textarea' | 'number' | 'boolean' | 'select';\n required: boolean;\n placeholder?: string;\n defaultValue?: string | number | boolean;\n options?: { value: string; label: string }[];\n validation?: {\n pattern?: string;\n minLength?: number;\n maxLength?: number;\n };\n}\n\nexport interface AdapterCredentialSchema {\n displayName: string;\n description: string;\n docUrl?: string;\n logoUrl?: string;\n credentials: CredentialField[];\n platformMetadata?: CredentialField[];\n}\n\n/**\n * Credential schemas for each adapter type.\n * Keys match the AdapterType enum values.\n */\nconst ADAPTER_CREDENTIALS: Partial<Record<AdapterType, AdapterCredentialSchema>> = {\n // ===================\n // SMS ADAPTERS\n // ===================\n TWILIO_SMS: {\n displayName: 'Twilio SMS',\n description: 'Send SMS via Twilio',\n docUrl: 'https://www.twilio.com/docs/sms',\n credentials: [\n {\n key: 'accountSid',\n displayName: 'Account SID',\n description: 'Your Twilio Account SID (starts with AC)',\n type: 'text',\n required: true,\n placeholder: 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',\n validation: { pattern: '^AC[a-zA-Z0-9]{32}$' },\n },\n {\n key: 'authToken',\n displayName: 'Auth Token',\n description: 'Your Twilio Auth Token',\n type: 'password',\n required: true,\n placeholder: 'Your auth token',\n },\n {\n key: 'fromNumber',\n displayName: 'From Phone Number',\n description: 'Sender phone number in E.164 format',\n type: 'text',\n required: true,\n placeholder: '+1234567890',\n validation: { pattern: '^\\\\+[1-9]\\\\d{1,14}$' },\n },\n ],\n platformMetadata: [\n {\n key: 'statusCallbackUrl',\n displayName: 'Status Callback URL',\n description: 'Webhook URL for delivery status updates (optional)',\n type: 'text',\n required: false,\n placeholder: 'https://your-domain.com/webhooks/twilio',\n },\n ],\n },\n\n AWS_SNS: {\n displayName: 'AWS SNS',\n description: 'Send SMS via Amazon Simple Notification Service',\n docUrl: 'https://docs.aws.amazon.com/sns/',\n credentials: [\n {\n key: 'accessKeyId',\n displayName: 'AWS Access Key ID',\n description: 'Your AWS access key ID',\n type: 'text',\n required: true,\n placeholder: 'AKIAIOSFODNN7EXAMPLE',\n },\n {\n key: 'secretAccessKey',\n displayName: 'AWS Secret Access Key',\n description: 'Your AWS secret access key',\n type: 'password',\n required: true,\n placeholder: 'Your secret access key',\n },\n {\n key: 'region',\n displayName: 'AWS Region',\n description: 'AWS region for SNS service',\n type: 'select',\n required: true,\n defaultValue: 'us-east-1',\n options: [\n { value: 'us-east-1', label: 'US East (N. Virginia)' },\n { value: 'us-east-2', label: 'US East (Ohio)' },\n { value: 'us-west-1', label: 'US West (N. California)' },\n { value: 'us-west-2', label: 'US West (Oregon)' },\n { value: 'ca-central-1', label: 'Canada (Central)' },\n { value: 'ca-west-1', label: 'Canada West (Calgary)' },\n { value: 'eu-west-1', label: 'Europe (Ireland)' },\n { value: 'eu-west-2', label: 'Europe (London)' },\n { value: 'eu-west-3', label: 'Europe (Paris)' },\n { value: 'eu-central-1', label: 'Europe (Frankfurt)' },\n { value: 'eu-central-2', label: 'Europe (Zurich)' },\n { value: 'eu-north-1', label: 'Europe (Stockholm)' },\n { value: 'eu-south-1', label: 'Europe (Milan)' },\n { value: 'eu-south-2', label: 'Europe (Spain)' },\n { value: 'ap-south-1', label: 'Asia Pacific (Mumbai)' },\n { value: 'ap-south-2', label: 'Asia Pacific (Hyderabad)' },\n { value: 'ap-southeast-1', label: 'Asia Pacific (Singapore)' },\n { value: 'ap-southeast-2', label: 'Asia Pacific (Sydney)' },\n { value: 'ap-southeast-3', label: 'Asia Pacific (Jakarta)' },\n { value: 'ap-southeast-4', label: 'Asia Pacific (Melbourne)' },\n { value: 'ap-northeast-1', label: 'Asia Pacific (Tokyo)' },\n { value: 'ap-northeast-2', label: 'Asia Pacific (Seoul)' },\n { value: 'ap-northeast-3', label: 'Asia Pacific (Osaka)' },\n { value: 'ap-east-1', label: 'Asia Pacific (Hong Kong)' },\n { value: 'sa-east-1', label: 'South America (São Paulo)' },\n { value: 'me-south-1', label: 'Middle East (Bahrain)' },\n { value: 'me-central-1', label: 'Middle East (UAE)' },\n { value: 'af-south-1', label: 'Africa (Cape Town)' },\n { value: 'il-central-1', label: 'Israel (Tel Aviv)' },\n ],\n },\n ],\n platformMetadata: [\n {\n key: 'senderId',\n displayName: 'Sender ID',\n description: 'Sender ID for supported regions (optional)',\n type: 'text',\n required: false,\n placeholder: 'YourBrand',\n },\n {\n key: 'messageType',\n displayName: 'Message Type',\n description: 'SMS message type',\n type: 'select',\n required: false,\n defaultValue: 'Transactional',\n options: [\n { value: 'Transactional', label: 'Transactional' },\n { value: 'Promotional', label: 'Promotional' },\n ],\n },\n ],\n },\n\n // ===================\n // EMAIL ADAPTERS\n // ===================\n SENDGRID: {\n displayName: 'SendGrid',\n description: 'Send emails via SendGrid',\n docUrl: 'https://docs.sendgrid.com/',\n credentials: [\n {\n key: 'apiKey',\n displayName: 'API Key',\n description: 'Your SendGrid API Key',\n type: 'password',\n required: true,\n placeholder: 'SG.xxxx...',\n },\n {\n key: 'fromEmail',\n displayName: 'From Email',\n description: 'Verified sender email address',\n type: 'text',\n required: true,\n placeholder: 'noreply@yourdomain.com',\n },\n {\n key: 'fromName',\n displayName: 'From Name',\n description: 'Sender display name',\n type: 'text',\n required: false,\n placeholder: 'Your Company',\n defaultValue: 'Notification',\n },\n ],\n },\n\n AWS_SES: {\n displayName: 'AWS SES',\n description: 'Send emails via Amazon Simple Email Service',\n docUrl: 'https://docs.aws.amazon.com/ses/',\n credentials: [\n {\n key: 'accessKeyId',\n displayName: 'AWS Access Key ID',\n description: 'Your AWS access key ID',\n type: 'text',\n required: true,\n placeholder: 'AKIAIOSFODNN7EXAMPLE',\n },\n {\n key: 'secretAccessKey',\n displayName: 'AWS Secret Access Key',\n description: 'Your AWS secret access key',\n type: 'password',\n required: true,\n placeholder: 'Your secret access key',\n },\n {\n key: 'region',\n displayName: 'AWS Region',\n description: 'AWS region for SES service',\n type: 'select',\n required: true,\n defaultValue: 'us-east-1',\n options: [\n { value: 'us-east-1', label: 'US East (N. Virginia)' },\n { value: 'us-east-2', label: 'US East (Ohio)' },\n { value: 'us-west-1', label: 'US West (N. California)' },\n { value: 'us-west-2', label: 'US West (Oregon)' },\n { value: 'ca-central-1', label: 'Canada (Central)' },\n { value: 'ca-west-1', label: 'Canada West (Calgary)' },\n { value: 'eu-west-1', label: 'Europe (Ireland)' },\n { value: 'eu-west-2', label: 'Europe (London)' },\n { value: 'eu-west-3', label: 'Europe (Paris)' },\n { value: 'eu-central-1', label: 'Europe (Frankfurt)' },\n { value: 'eu-central-2', label: 'Europe (Zurich)' },\n { value: 'eu-north-1', label: 'Europe (Stockholm)' },\n { value: 'eu-south-1', label: 'Europe (Milan)' },\n { value: 'eu-south-2', label: 'Europe (Spain)' },\n { value: 'ap-south-1', label: 'Asia Pacific (Mumbai)' },\n { value: 'ap-south-2', label: 'Asia Pacific (Hyderabad)' },\n { value: 'ap-southeast-1', label: 'Asia Pacific (Singapore)' },\n { value: 'ap-southeast-2', label: 'Asia Pacific (Sydney)' },\n { value: 'ap-southeast-3', label: 'Asia Pacific (Jakarta)' },\n { value: 'ap-southeast-4', label: 'Asia Pacific (Melbourne)' },\n { value: 'ap-northeast-1', label: 'Asia Pacific (Tokyo)' },\n { value: 'ap-northeast-2', label: 'Asia Pacific (Seoul)' },\n { value: 'ap-northeast-3', label: 'Asia Pacific (Osaka)' },\n { value: 'ap-east-1', label: 'Asia Pacific (Hong Kong)' },\n { value: 'sa-east-1', label: 'South America (São Paulo)' },\n { value: 'me-south-1', label: 'Middle East (Bahrain)' },\n { value: 'me-central-1', label: 'Middle East (UAE)' },\n { value: 'af-south-1', label: 'Africa (Cape Town)' },\n { value: 'il-central-1', label: 'Israel (Tel Aviv)' },\n ],\n },\n {\n key: 'fromEmail',\n displayName: 'From Email',\n description: 'Verified sender email address',\n type: 'text',\n required: false,\n placeholder: 'noreply@yourdomain.com',\n },\n ],\n platformMetadata: [\n {\n key: 'configurationSetName',\n displayName: 'Configuration Set',\n description: 'SES Configuration Set for tracking (optional)',\n type: 'text',\n required: false,\n placeholder: 'my-configuration-set',\n },\n ],\n },\n\n AZURE_ACS: {\n displayName: 'Azure Communication Services',\n description: 'Send emails via Azure Communication Services',\n docUrl: 'https://docs.microsoft.com/azure/communication-services/',\n credentials: [\n {\n key: 'connectionString',\n displayName: 'Connection String',\n description: 'Azure ACS connection string (endpoint=...;accesskey=...)',\n type: 'password',\n required: true,\n placeholder: 'endpoint=https://your-resource.communication.azure.com/;accesskey=...',\n },\n {\n key: 'senderAddress',\n displayName: 'Sender Email',\n description: 'Azure verified sender email address',\n type: 'text',\n required: true,\n placeholder: 'DoNotReply@your-domain.azurecomm.net',\n },\n ],\n },\n\n MAILGUN: {\n displayName: 'Mailgun',\n description: 'Send emails via Mailgun',\n docUrl: 'https://documentation.mailgun.com/',\n credentials: [\n {\n key: 'apiKey',\n displayName: 'API Key',\n description: 'Your Mailgun API Key',\n type: 'password',\n required: true,\n placeholder: 'key-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',\n },\n {\n key: 'domain',\n displayName: 'Domain',\n description: 'Your Mailgun sending domain',\n type: 'text',\n required: true,\n placeholder: 'mg.yourdomain.com',\n },\n {\n key: 'fromEmail',\n displayName: 'From Email',\n description: 'Sender email address',\n type: 'text',\n required: true,\n placeholder: 'noreply@mg.yourdomain.com',\n },\n {\n key: 'fromName',\n displayName: 'From Name',\n description: 'Sender display name',\n type: 'text',\n required: false,\n placeholder: 'Your Company',\n },\n ],\n platformMetadata: [\n {\n key: 'region',\n displayName: 'Region',\n description: 'Mailgun region',\n type: 'select',\n required: false,\n defaultValue: 'us',\n options: [\n { value: 'us', label: 'US' },\n { value: 'eu', label: 'EU' },\n ],\n },\n ],\n },\n\n POSTMARK: {\n displayName: 'Postmark',\n description: 'Send emails via Postmark',\n docUrl: 'https://postmarkapp.com/developer',\n credentials: [\n {\n key: 'serverToken',\n displayName: 'Server API Token',\n description: 'Your Postmark Server API Token',\n type: 'password',\n required: true,\n placeholder: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',\n },\n {\n key: 'fromEmail',\n displayName: 'From Email',\n description: 'Verified sender email address',\n type: 'text',\n required: true,\n placeholder: 'noreply@yourdomain.com',\n },\n {\n key: 'fromName',\n displayName: 'From Name',\n description: 'Sender display name',\n type: 'text',\n required: false,\n placeholder: 'Your Company',\n },\n ],\n },\n\n RESEND: {\n displayName: 'Resend',\n description: 'Send emails via Resend',\n docUrl: 'https://resend.com/docs',\n credentials: [\n {\n key: 'apiKey',\n displayName: 'API Key',\n description: 'Your Resend API Key',\n type: 'password',\n required: true,\n placeholder: 're_xxxxxxxxxxxxxxxxxxxxxxxxxxxx',\n },\n {\n key: 'fromEmail',\n displayName: 'From Email',\n description: 'Verified sender email address',\n type: 'text',\n required: true,\n placeholder: 'noreply@yourdomain.com',\n },\n {\n key: 'fromName',\n displayName: 'From Name',\n description: 'Sender display name',\n type: 'text',\n required: false,\n placeholder: 'Your Company',\n },\n ],\n },\n\n SMTP: {\n displayName: 'Custom SMTP',\n description: 'Send emails via custom SMTP server',\n credentials: [\n {\n key: 'host',\n displayName: 'SMTP Host',\n description: 'SMTP server hostname',\n type: 'text',\n required: true,\n placeholder: 'smtp.yourdomain.com',\n },\n {\n key: 'port',\n displayName: 'SMTP Port',\n description: 'SMTP server port (usually 587 for TLS, 465 for SSL)',\n type: 'number',\n required: true,\n defaultValue: 587,\n placeholder: '587',\n },\n {\n key: 'username',\n displayName: 'Username',\n description: 'SMTP authentication username',\n type: 'text',\n required: true,\n placeholder: 'your-username',\n },\n {\n key: 'password',\n displayName: 'Password',\n description: 'SMTP authentication password',\n type: 'password',\n required: true,\n placeholder: 'Your password',\n },\n {\n key: 'fromEmail',\n displayName: 'From Email',\n description: 'Sender email address',\n type: 'text',\n required: true,\n placeholder: 'noreply@yourdomain.com',\n },\n {\n key: 'fromName',\n displayName: 'From Name',\n description: 'Sender display name',\n type: 'text',\n required: false,\n placeholder: 'Your Company',\n },\n ],\n platformMetadata: [\n {\n key: 'secure',\n displayName: 'Use SSL/TLS',\n description: 'Use SSL/TLS encryption',\n type: 'boolean',\n required: false,\n defaultValue: true,\n },\n ],\n },\n\n // ===================\n // PUSH NOTIFICATION ADAPTERS\n // ===================\n FCM: {\n displayName: 'Firebase Cloud Messaging',\n description: 'Send push notifications via FCM',\n docUrl: 'https://firebase.google.com/docs/cloud-messaging',\n credentials: [\n {\n key: 'projectId',\n displayName: 'Firebase Project ID',\n description: 'Your Firebase project ID',\n type: 'text',\n required: true,\n placeholder: 'your-project-id',\n },\n {\n key: 'clientEmail',\n displayName: 'Service Account Email',\n description: 'Service account email from Firebase credentials JSON',\n type: 'text',\n required: true,\n placeholder: 'firebase-adminsdk-xxxxx@your-project.iam.gserviceaccount.com',\n },\n {\n key: 'privateKey',\n displayName: 'Private Key',\n description:\n 'Service account private key (PEM format, paste the entire key including BEGIN/END lines)',\n type: 'textarea',\n required: true,\n placeholder: 'Paste the full PEM private key from your service account JSON',\n },\n ],\n },\n\n APNS: {\n displayName: 'Apple Push Notification Service',\n description: 'Send push notifications to iOS devices via APNs',\n docUrl: 'https://developer.apple.com/documentation/usernotifications',\n credentials: [\n {\n key: 'teamId',\n displayName: 'Team ID',\n description: 'Your 10-character Apple Developer Team ID',\n type: 'text',\n required: true,\n placeholder: 'ABCDE12345',\n validation: { pattern: '^[A-Z0-9]{10}$' },\n },\n {\n key: 'keyId',\n displayName: 'Key ID',\n description: 'Your 10-character Key ID from Apple Developer Account',\n type: 'text',\n required: true,\n placeholder: 'ABC123DEFG',\n validation: { pattern: '^[A-Z0-9]{10}$' },\n },\n {\n key: 'privateKey',\n displayName: 'Private Key (.p8)',\n description: 'Content of your .p8 private key file',\n type: 'textarea',\n required: true,\n placeholder: '-----BEGIN PRIVATE KEY-----\\nYour .p8 key content\\n-----END PRIVATE KEY-----',\n },\n {\n key: 'bundleId',\n displayName: 'Bundle ID',\n description: \"Your app's Bundle Identifier\",\n type: 'text',\n required: true,\n placeholder: 'com.yourcompany.yourapp',\n },\n ],\n platformMetadata: [\n {\n key: 'production',\n displayName: 'Production Environment',\n description: 'Use production APNs server (uncheck for sandbox/development)',\n type: 'boolean',\n required: false,\n defaultValue: false,\n },\n ],\n },\n\n ONESIGNAL: {\n displayName: 'OneSignal',\n description: 'Send push notifications via OneSignal',\n docUrl: 'https://documentation.onesignal.com/',\n credentials: [\n {\n key: 'appId',\n displayName: 'App ID',\n description: 'Your OneSignal App ID',\n type: 'text',\n required: true,\n placeholder: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',\n },\n {\n key: 'apiKey',\n displayName: 'REST API Key',\n description: 'Your OneSignal REST API Key',\n type: 'password',\n required: true,\n placeholder: 'Your API key',\n },\n ],\n },\n\n EXPO: {\n displayName: 'Expo Push',\n description: 'Send push notifications via Expo',\n docUrl: 'https://docs.expo.dev/push-notifications/',\n credentials: [\n {\n key: 'accessToken',\n displayName: 'Access Token',\n description: 'Your Expo access token (optional for increased rate limits)',\n type: 'password',\n required: false,\n placeholder: 'Your Expo access token',\n },\n ],\n },\n\n // ===================\n // MESSAGING PLATFORM ADAPTERS\n // ===================\n SLACK_BOT: {\n displayName: 'Slack Bot',\n description: 'Send messages via Slack Bot',\n docUrl: 'https://api.slack.com/bot-users',\n credentials: [\n {\n key: 'botToken',\n displayName: 'Bot Token',\n description: 'Your Slack Bot User OAuth Token (xoxb-...)',\n type: 'password',\n required: true,\n placeholder: 'xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx',\n },\n ],\n platformMetadata: [\n {\n key: 'defaultChannel',\n displayName: 'Default Channel',\n description: 'Default channel ID for messages (optional)',\n type: 'text',\n required: false,\n placeholder: 'C0123456789',\n },\n ],\n },\n\n SLACK_WEBHOOK: {\n displayName: 'Slack Webhook',\n description: 'Send messages via Slack Incoming Webhook',\n docUrl: 'https://api.slack.com/messaging/webhooks',\n credentials: [\n {\n key: 'webhookUrl',\n displayName: 'Webhook URL',\n description: 'Your Slack Incoming Webhook URL',\n type: 'password',\n required: true,\n placeholder: 'Paste your Slack Incoming Webhook URL',\n },\n ],\n },\n\n TEAMS_WEBHOOK: {\n displayName: 'Microsoft Teams Webhook',\n description: 'Send messages via Teams Incoming Webhook',\n docUrl: 'https://docs.microsoft.com/microsoftteams/platform/webhooks-and-connectors/',\n credentials: [\n {\n key: 'webhookUrl',\n displayName: 'Webhook URL',\n description: 'Your Microsoft Teams Incoming Webhook URL',\n type: 'password',\n required: true,\n placeholder: 'https://outlook.office.com/webhook/...',\n },\n ],\n },\n\n DISCORD_BOT: {\n displayName: 'Discord Bot',\n description: 'Send messages via Discord Bot',\n docUrl: 'https://discord.com/developers/docs',\n credentials: [\n {\n key: 'botToken',\n displayName: 'Bot Token',\n description: 'Your Discord Bot Token',\n type: 'password',\n required: true,\n placeholder: 'Your Discord bot token',\n },\n ],\n platformMetadata: [\n {\n key: 'defaultChannelId',\n displayName: 'Default Channel ID',\n description: 'Default channel ID for messages (optional)',\n type: 'text',\n required: false,\n placeholder: '123456789012345678',\n },\n ],\n },\n\n DISCORD_WEBHOOK: {\n displayName: 'Discord Webhook',\n description: 'Send messages via Discord Webhook',\n docUrl: 'https://discord.com/developers/docs/resources/webhook',\n credentials: [\n {\n key: 'webhookUrl',\n displayName: 'Webhook URL',\n description: 'Your Discord Webhook URL',\n type: 'password',\n required: true,\n placeholder: 'https://discord.com/api/webhooks/...',\n },\n ],\n },\n\n WHATSAPP_CLOUD: {\n displayName: 'WhatsApp Cloud API',\n description: 'Send messages via WhatsApp Business Cloud API',\n docUrl: 'https://developers.facebook.com/docs/whatsapp/cloud-api',\n credentials: [\n {\n key: 'accessToken',\n displayName: 'Access Token',\n description: 'Your WhatsApp Business API Access Token',\n type: 'password',\n required: true,\n placeholder: 'Your access token',\n },\n {\n key: 'phoneNumberId',\n displayName: 'Phone Number ID',\n description: 'Your WhatsApp Business Phone Number ID',\n type: 'text',\n required: true,\n placeholder: '123456789012345',\n },\n {\n key: 'businessAccountId',\n displayName: 'Business Account ID',\n description: 'Your WhatsApp Business Account ID',\n type: 'text',\n required: true,\n placeholder: '123456789012345',\n },\n ],\n },\n\n WHATSAPP_TWILIO: {\n displayName: 'WhatsApp via Twilio',\n description: 'Send WhatsApp messages via Twilio',\n docUrl: 'https://www.twilio.com/docs/whatsapp',\n credentials: [\n {\n key: 'accountSid',\n displayName: 'Account SID',\n description: 'Your Twilio Account SID (starts with AC)',\n type: 'text',\n required: true,\n placeholder: 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',\n },\n {\n key: 'authToken',\n displayName: 'Auth Token',\n description: 'Your Twilio Auth Token',\n type: 'password',\n required: true,\n placeholder: 'Your auth token',\n },\n {\n key: 'fromNumber',\n displayName: 'WhatsApp Number',\n description: 'Twilio WhatsApp number (whatsapp:+1234567890)',\n type: 'text',\n required: true,\n placeholder: 'whatsapp:+1234567890',\n },\n ],\n },\n\n // ===================\n // WEBHOOK ADAPTERS\n // ===================\n WEBHOOK_HTTP: {\n displayName: 'HTTP Webhook',\n description: 'Send notifications via HTTP webhook',\n credentials: [\n {\n key: 'webhookUrl',\n displayName: 'Webhook URL',\n description: 'HTTP endpoint to receive notifications',\n type: 'text',\n required: true,\n placeholder: 'https://your-api.com/webhooks/notifications',\n },\n ],\n platformMetadata: [\n {\n key: 'method',\n displayName: 'HTTP Method',\n description: 'HTTP method for the request',\n type: 'select',\n required: false,\n defaultValue: 'POST',\n options: [\n { value: 'POST', label: 'POST' },\n { value: 'PUT', label: 'PUT' },\n ],\n },\n {\n key: 'headers',\n displayName: 'Custom Headers (JSON)',\n description: 'Additional HTTP headers as JSON object',\n type: 'textarea',\n required: false,\n placeholder: '{\"Authorization\": \"Bearer token\", \"X-Custom-Header\": \"value\"}',\n },\n {\n key: 'secret',\n displayName: 'Signing Secret',\n description: 'Secret for HMAC signature verification (optional)',\n type: 'password',\n required: false,\n placeholder: 'Your webhook signing secret',\n },\n ],\n },\n};\n\n/**\n * Get credential schema for an adapter type\n */\nexport function getAdapterCredentialSchema(\n adapterType: AdapterType | undefined\n): AdapterCredentialSchema | null {\n if (!adapterType) return null;\n return ADAPTER_CREDENTIALS[adapterType] || null;\n}\n\n/**\n * Module-level RegExp cache so validateCredentials() doesn't allocate inside its loop.\n */\nconst REGEX_CACHE = new Map<string, RegExp>();\nfunction getCachedRegex(pattern: string): RegExp {\n let r = REGEX_CACHE.get(pattern);\n if (!r) {\n r = new RegExp(pattern);\n REGEX_CACHE.set(pattern, r);\n }\n return r;\n}\n\n/**\n * Validate credentials against adapter schema\n */\nexport function validateCredentials(\n adapterType: AdapterType | undefined,\n credentials: Record<string, unknown>\n): { valid: boolean; errors: string[] } {\n const schema = getAdapterCredentialSchema(adapterType);\n if (!schema) {\n return { valid: false, errors: ['Unknown adapter type'] };\n }\n\n const errors: string[] = [];\n\n for (const field of schema.credentials) {\n const value = credentials[field.key];\n\n // Check required fields\n if (field.required && (value === undefined || value === null || value === '')) {\n errors.push(`${field.displayName} is required`);\n continue;\n }\n\n // Skip validation for empty optional fields\n if (!field.required && (value === undefined || value === null || value === '')) {\n continue;\n }\n\n // Validate pattern if specified\n if (field.validation?.pattern && typeof value === 'string') {\n const regex = getCachedRegex(field.validation.pattern);\n if (!regex.test(value)) {\n errors.push(`${field.displayName} has an invalid format`);\n }\n }\n\n // Validate min/max length\n if (typeof value === 'string') {\n const minLength = field.validation?.minLength;\n const maxLength = field.validation?.maxLength;\n if (minLength && value.length < minLength) {\n errors.push(`${field.displayName} must be at least ${minLength} characters`);\n }\n if (maxLength && value.length > maxLength) {\n errors.push(`${field.displayName} must be at most ${maxLength} characters`);\n }\n }\n }\n\n return { valid: errors.length === 0, errors };\n}\n\n/**\n * Get default values for an adapter's credentials\n */\nexport function getDefaultCredentials(\n adapterType: AdapterType | undefined\n): Record<string, unknown> {\n const schema = getAdapterCredentialSchema(adapterType);\n if (!schema) return {};\n\n const defaults: Record<string, unknown> = {};\n for (const field of [...schema.credentials, ...(schema.platformMetadata || [])]) {\n if (field.defaultValue !== undefined) {\n defaults[field.key] = field.defaultValue;\n }\n }\n return defaults;\n}\n"],"mappings":"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CR,aAAa,CACX;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,EACD;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACF;CAED,SAAS;EACP,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa;GACX;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACF;EACD,kBAAkB,CAChB;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,cAAc;GACd,SAAS,CACP;IAAE,OAAO;IAAM,OAAO;IAAM,EAC5B;IAAE,OAAO;IAAM,OAAO;IAAM,CAC7B;GACF,CACF;EACF;CAED,UAAU;EACR,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa;GACX;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACF;EACF;CAED,QAAQ;EACN,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa;GACX;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACF;EACF;CAED,MAAM;EACJ,aAAa;EACb,aAAa;EACb,aAAa;GACX;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,cAAc;IACd,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACF;EACD,kBAAkB,CAChB;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,cAAc;GACf,CACF;EACF;CAKD,KAAK;EACH,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa;GACX;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aACE;IACF,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACF;EACF;CAED,MAAM;EACJ,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa;GACX;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACb,YAAY,EAAE,SAAS,kBAAkB;IAC1C;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACb,YAAY,EAAE,SAAS,kBAAkB;IAC1C;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACF;EACD,kBAAkB,CAChB;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,cAAc;GACf,CACF;EACF;CAED,WAAW;EACT,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa,CACX;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,EACD;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACF;CAED,MAAM;EACJ,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa,CACX;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACF;CAKD,WAAW;EACT,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa,CACX;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACD,kBAAkB,CAChB;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACF;CAED,eAAe;EACb,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa,CACX;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACF;CAED,eAAe;EACb,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa,CACX;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACF;CAED,aAAa;EACX,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa,CACX;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACD,kBAAkB,CAChB;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACF;CAED,iBAAiB;EACf,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa,CACX;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACF;CAED,gBAAgB;EACd,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa;GACX;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACF;EACF;CAED,iBAAiB;EACf,aAAa;EACb,aAAa;EACb,QAAQ;EACR,aAAa;GACX;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACF;EACF;CAKD,cAAc;EACZ,aAAa;EACb,aAAa;EACb,aAAa,CACX;GACE,KAAK;GACL,aAAa;GACb,aAAa;GACb,MAAM;GACN,UAAU;GACV,aAAa;GACd,CACF;EACD,kBAAkB;GAChB;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,cAAc;IACd,SAAS,CACP;KAAE,OAAO;KAAQ,OAAO;KAAQ,EAChC;KAAE,OAAO;KAAO,OAAO;KAAO,CAC/B;IACF;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACD;IACE,KAAK;IACL,aAAa;IACb,aAAa;IACb,MAAM;IACN,UAAU;IACV,aAAa;IACd;GACF;EACF;CACF;AAKD,SAAgB,EACd,GACgC;AAEhC,QADK,KACE,EAAoB,MADF;;AAO3B,IAAM,oBAAc,IAAI,KAAqB;AAC7C,SAAS,EAAe,GAAyB;CAC/C,IAAI,IAAI,EAAY,IAAI,EAAQ;AAKhC,QAJK,MACH,IAAI,IAAI,OAAO,EAAQ,EACvB,EAAY,IAAI,GAAS,EAAE,GAEtB;;AAMT,SAAgB,EACd,GACA,GACsC;CACtC,IAAM,IAAS,EAA2B,EAAY;AACtD,KAAI,CAAC,EACH,QAAO;EAAE,OAAO;EAAO,QAAQ,CAAC,uBAAuB;EAAE;CAG3D,IAAM,IAAmB,EAAE;AAE3B,MAAK,IAAM,KAAS,EAAO,aAAa;EACtC,IAAM,IAAQ,EAAY,EAAM;AAGhC,MAAI,EAAM,aAAa,KAAiC,QAAQ,MAAU,KAAK;AAC7E,KAAO,KAAK,GAAG,EAAM,YAAY,cAAc;AAC/C;;AAIE,SAAC,EAAM,aAAa,KAAiC,QAAQ,MAAU,SAKvE,EAAM,YAAY,WAAW,OAAO,KAAU,aAClC,EAAe,EAAM,WAAW,QAAQ,CAC3C,KAAK,EAAM,IACpB,EAAO,KAAK,GAAG,EAAM,YAAY,wBAAwB,GAKzD,OAAO,KAAU,WAAU;GAC7B,IAAM,IAAY,EAAM,YAAY,WAC9B,IAAY,EAAM,YAAY;AAIpC,GAHI,KAAa,EAAM,SAAS,KAC9B,EAAO,KAAK,GAAG,EAAM,YAAY,oBAAoB,EAAU,aAAa,EAE1E,KAAa,EAAM,SAAS,KAC9B,EAAO,KAAK,GAAG,EAAM,YAAY,mBAAmB,EAAU,aAAa;;;AAKjF,QAAO;EAAE,OAAO,EAAO,WAAW;EAAG;EAAQ;;AAM/C,SAAgB,EACd,GACyB;CACzB,IAAM,IAAS,EAA2B,EAAY;AACtD,KAAI,CAAC,EAAQ,QAAO,EAAE;CAEtB,IAAM,IAAoC,EAAE;AAC5C,MAAK,IAAM,KAAS,CAAC,GAAG,EAAO,aAAa,GAAI,EAAO,oBAAoB,EAAE,CAAE,CAC7E,CAAI,EAAM,iBAAiB,KAAA,MACzB,EAAS,EAAM,OAAO,EAAM;AAGhC,QAAO"}
|
|
@@ -7,6 +7,9 @@ var e = {
|
|
|
7
7
|
CHANNEL_ARCHIVE: "channel:archive",
|
|
8
8
|
CHANNEL_EXECUTE: "channel:execute",
|
|
9
9
|
CHANNEL_EXPORT: "channel:export",
|
|
10
|
+
CHANNEL_DELIVERY_EXPORT: "channel-delivery:export",
|
|
11
|
+
CHANNEL_WEBHOOK_EXPORT: "channel-webhook:export",
|
|
12
|
+
CHANNEL_USER_EXPORT: "channel-user:export",
|
|
10
13
|
CHANNEL_ADMIN: "channel:update",
|
|
11
14
|
CHANNEL_CONNECTION_READ: "channel-connection:read",
|
|
12
15
|
CHANNEL_CONNECTION_CREATE: "channel-connection:create",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.js","names":[],"sources":["../../../src/channel/constants/permissions.ts"],"sourcesContent":["/**\n * RBAC permission constants for the Channel microfrontend.\n * Mirrors the @rbac directives in global-channel-svc.\n */\n\nexport const CHANNEL_PERMISSIONS = {\n // channel resource\n CHANNEL_READ: 'channel:read',\n CHANNEL_CREATE: 'channel:create',\n CHANNEL_UPDATE: 'channel:update',\n CHANNEL_DELETE: 'channel:delete',\n CHANNEL_ARCHIVE: 'channel:archive',\n CHANNEL_EXECUTE: 'channel:execute',\n CHANNEL_EXPORT: 'channel:export',\n
|
|
1
|
+
{"version":3,"file":"permissions.js","names":[],"sources":["../../../src/channel/constants/permissions.ts"],"sourcesContent":["/**\n * RBAC permission constants for the Channel microfrontend.\n * Mirrors the @rbac directives in global-channel-svc.\n */\n\nexport const CHANNEL_PERMISSIONS = {\n // channel resource\n CHANNEL_READ: 'channel:read',\n CHANNEL_CREATE: 'channel:create',\n CHANNEL_UPDATE: 'channel:update',\n CHANNEL_DELETE: 'channel:delete',\n CHANNEL_ARCHIVE: 'channel:archive',\n CHANNEL_EXECUTE: 'channel:execute',\n /** Per-resource CSV export permissions — match the backend @rbac(action:\"export\", resource:...). */\n CHANNEL_EXPORT: 'channel:export',\n CHANNEL_DELIVERY_EXPORT: 'channel-delivery:export',\n CHANNEL_WEBHOOK_EXPORT: 'channel-webhook:export',\n CHANNEL_USER_EXPORT: 'channel-user:export',\n /** @deprecated alias of channel:update — do NOT use for export gating (use the *_EXPORT perms). */\n CHANNEL_ADMIN: 'channel:update',\n\n // channel-connection resource\n CHANNEL_CONNECTION_READ: 'channel-connection:read',\n CHANNEL_CONNECTION_CREATE: 'channel-connection:create',\n CHANNEL_CONNECTION_UPDATE: 'channel-connection:update',\n CHANNEL_CONNECTION_DELETE: 'channel-connection:delete',\n\n // channel-delivery resource\n CHANNEL_DELIVERY_READ: 'channel-delivery:read',\n CHANNEL_DELIVERY_SEND: 'channel-delivery:send',\n CHANNEL_DELIVERY_EXECUTE: 'channel-delivery:execute',\n CHANNEL_DELIVERY_DELETE: 'channel-delivery:delete',\n\n // channel-webhook resource\n CHANNEL_WEBHOOK_READ: 'channel-webhook:read',\n CHANNEL_WEBHOOK_EXECUTE: 'channel-webhook:execute',\n CHANNEL_WEBHOOK_UPDATE: 'channel-webhook:update',\n\n // user-platform-connection resource\n USER_PLATFORM_CONNECTION_READ: 'user-platform-connection:read',\n USER_PLATFORM_CONNECTION_CREATE: 'user-platform-connection:create',\n USER_PLATFORM_CONNECTION_UPDATE: 'user-platform-connection:update',\n USER_PLATFORM_CONNECTION_DELETE: 'user-platform-connection:delete',\n} as const;\n\nexport type ChannelPermission = (typeof CHANNEL_PERMISSIONS)[keyof typeof CHANNEL_PERMISSIONS];\n"],"mappings":";AAKA,IAAa,IAAsB;CAEjC,cAAc;CACd,gBAAgB;CAChB,gBAAgB;CAChB,gBAAgB;CAChB,iBAAiB;CACjB,iBAAiB;CAEjB,gBAAgB;CAChB,yBAAyB;CACzB,wBAAwB;CACxB,qBAAqB;CAErB,eAAe;CAGf,yBAAyB;CACzB,2BAA2B;CAC3B,2BAA2B;CAC3B,2BAA2B;CAG3B,uBAAuB;CACvB,uBAAuB;CACvB,0BAA0B;CAC1B,yBAAyB;CAGzB,sBAAsB;CACtB,yBAAyB;CACzB,wBAAwB;CAGxB,+BAA+B;CAC/B,iCAAiC;CACjC,iCAAiC;CACjC,iCAAiC;CAClC"}
|