@burdenoff/microfe-bigconsole 2026.812.1 → 2026.825.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/bigconsole/components/widgets/PropertiesPanel.js +480 -473
- package/dist/bigconsole/components/widgets/PropertiesPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetRegistry.js +4 -4
- package/dist/bigconsole/components/widgets/WidgetRegistry.js.map +1 -1
- package/dist/bigconsole/components/widgets/gauge/CircularGauge.js +36 -32
- package/dist/bigconsole/components/widgets/gauge/CircularGauge.js.map +1 -1
- package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js +60 -50
- package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/gauge/GaugeWidget.js +63 -34
- package/dist/bigconsole/components/widgets/gauge/GaugeWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/gauge/LinearGauge.js +46 -45
- package/dist/bigconsole/components/widgets/gauge/LinearGauge.js.map +1 -1
- package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js +152 -89
- package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/MetricCardConfig.js +5 -1
- package/dist/bigconsole/components/widgets/metric-card/MetricCardConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js +112 -89
- package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js +21 -13
- package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/index.js +4 -4
- package/dist/bigconsole/components/widgets/progress/ProgressWidget.js +129 -114
- package/dist/bigconsole/components/widgets/progress/ProgressWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/utils/targetProgress.js +13 -0
- package/dist/bigconsole/components/widgets/utils/targetProgress.js.map +1 -0
- package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js +44 -47
- package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js.map +1 -1
- package/dist/bigconsole/customWidget/proposalSource.js +31 -24
- package/dist/bigconsole/customWidget/proposalSource.js.map +1 -1
- package/dist/bigconsole/hooks/useDataSinkOperations.js +2 -2
- package/dist/bigconsole/hooks/useDataSinkOperations.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
//#region src/bigconsole/customWidget/proposalSource.ts
|
|
2
|
-
|
|
2
|
+
function e(e) {
|
|
3
|
+
return e.filter((e) => !e.deletedAt).map(({ id: e, key: t, name: n }) => ({
|
|
4
|
+
id: e,
|
|
5
|
+
key: t,
|
|
6
|
+
name: n
|
|
7
|
+
}));
|
|
8
|
+
}
|
|
9
|
+
var t = [
|
|
3
10
|
{
|
|
4
11
|
type: "funnel_chart",
|
|
5
12
|
words: [
|
|
@@ -58,7 +65,7 @@ var e = [
|
|
|
58
65
|
"graph"
|
|
59
66
|
]
|
|
60
67
|
}
|
|
61
|
-
],
|
|
68
|
+
], n = [
|
|
62
69
|
{
|
|
63
70
|
chartType: "bar",
|
|
64
71
|
words: ["bar"]
|
|
@@ -85,17 +92,17 @@ var e = [
|
|
|
85
92
|
]
|
|
86
93
|
}
|
|
87
94
|
];
|
|
88
|
-
function n(t) {
|
|
89
|
-
let n = t.toLowerCase();
|
|
90
|
-
for (let { type: t, words: r } of e) if (r.some((e) => n.includes(e))) return t;
|
|
91
|
-
return "chart";
|
|
92
|
-
}
|
|
93
95
|
function r(e) {
|
|
94
96
|
let n = e.toLowerCase();
|
|
95
|
-
for (let {
|
|
97
|
+
for (let { type: e, words: r } of t) if (r.some((e) => n.includes(e))) return e;
|
|
98
|
+
return "chart";
|
|
99
|
+
}
|
|
100
|
+
function i(e) {
|
|
101
|
+
let t = e.toLowerCase();
|
|
102
|
+
for (let { chartType: e, words: r } of n) if (r.some((e) => t.includes(e))) return e;
|
|
96
103
|
return "line";
|
|
97
104
|
}
|
|
98
|
-
function
|
|
105
|
+
function a(e, t) {
|
|
99
106
|
if (t.length === 0) return;
|
|
100
107
|
let n = e.toLowerCase().split(/[^a-z0-9]+/).filter((e) => e.length > 2), r;
|
|
101
108
|
for (let e of t) {
|
|
@@ -107,7 +114,7 @@ function i(e, t) {
|
|
|
107
114
|
}
|
|
108
115
|
return r?.sink ?? t[0];
|
|
109
116
|
}
|
|
110
|
-
function
|
|
117
|
+
function o(e) {
|
|
111
118
|
switch (e) {
|
|
112
119
|
case "heatmap": return {
|
|
113
120
|
xAxisField: "column",
|
|
@@ -131,7 +138,7 @@ function a(e) {
|
|
|
131
138
|
};
|
|
132
139
|
}
|
|
133
140
|
}
|
|
134
|
-
function
|
|
141
|
+
function s(e, t) {
|
|
135
142
|
let n = {
|
|
136
143
|
showLegend: !0,
|
|
137
144
|
showTooltip: !0
|
|
@@ -139,7 +146,7 @@ function o(e, t) {
|
|
|
139
146
|
switch (e) {
|
|
140
147
|
case "chart": return {
|
|
141
148
|
...n,
|
|
142
|
-
chartType:
|
|
149
|
+
chartType: i(t),
|
|
143
150
|
showGrid: !0
|
|
144
151
|
};
|
|
145
152
|
case "funnel_chart": return {
|
|
@@ -166,39 +173,39 @@ function o(e, t) {
|
|
|
166
173
|
default: return n;
|
|
167
174
|
}
|
|
168
175
|
}
|
|
169
|
-
function
|
|
176
|
+
function c(e) {
|
|
170
177
|
let t = e.trim().replace(/^(please\s+)?(create|build|make|add|show me|show|give me)\s+(an?\s+|the\s+)?/i, "").split(/[.;\n]/)[0].trim();
|
|
171
178
|
if (!t) return "Custom widget";
|
|
172
179
|
let n = t.charAt(0).toUpperCase() + t.slice(1);
|
|
173
180
|
return n.length > 60 ? `${n.slice(0, 57)}…` : n;
|
|
174
181
|
}
|
|
175
|
-
var
|
|
182
|
+
var l = 0, u = {
|
|
176
183
|
id: "local-heuristic-v1",
|
|
177
|
-
async propose({ prompt: e, sinks: t, parsers:
|
|
178
|
-
let
|
|
179
|
-
return
|
|
184
|
+
async propose({ prompt: e, sinks: t, parsers: n }) {
|
|
185
|
+
let i = r(e), d = a(e, t), f = d ? n.find((e) => e.inputSinkKey === d.key) : void 0;
|
|
186
|
+
return l += 1, {
|
|
180
187
|
version: "1.0",
|
|
181
|
-
id: `proposal-${
|
|
188
|
+
id: `proposal-${l}`,
|
|
182
189
|
sourcePrompt: e,
|
|
183
|
-
generatedBy:
|
|
184
|
-
widgetType:
|
|
190
|
+
generatedBy: u.id,
|
|
191
|
+
widgetType: i,
|
|
185
192
|
binding: {
|
|
186
193
|
dataSinkId: d?.id ?? "",
|
|
187
194
|
dataSinkKey: d?.key,
|
|
188
195
|
parserId: f?.id,
|
|
189
196
|
parserName: f?.name
|
|
190
197
|
},
|
|
191
|
-
fieldMapping:
|
|
198
|
+
fieldMapping: o(i),
|
|
192
199
|
presentation: {
|
|
193
|
-
title:
|
|
200
|
+
title: c(e),
|
|
194
201
|
description: `Generated from: "${e}"`,
|
|
195
|
-
config:
|
|
202
|
+
config: s(i, e)
|
|
196
203
|
},
|
|
197
204
|
rationale: d ? `Matched DataSink "${d.key}"${f ? ` and parser "${f.name}"` : " (no parser found for this sink)"}.` : "No DataSink is available in this workspace — create one before saving this widget."
|
|
198
205
|
};
|
|
199
206
|
}
|
|
200
207
|
};
|
|
201
208
|
//#endregion
|
|
202
|
-
export {
|
|
209
|
+
export { u as localProposalSource, e as selectActiveProposalSinks };
|
|
203
210
|
|
|
204
211
|
//# sourceMappingURL=proposalSource.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proposalSource.js","names":[],"sources":["../../../src/bigconsole/customWidget/proposalSource.ts"],"sourcesContent":["/**\n * Proposal source — the seam between the review UI and whatever produces\n * proposals (BOFF-5532 / BOFF-3021).\n *\n * The UI only ever talks to `CustomWidgetProposalSource`. Today the default\n * implementation derives a proposal locally from the prompt and the workspace's\n * real sinks and parsers, because the backend operation that will emit typed\n * proposals is owned by BOFF-3021's backend half and does not exist yet.\n *\n * When it lands, add a `graphqlProposalSource` here that calls it and swap the\n * value passed to the panel. Nothing in the review, edit, approve or persist\n * path changes: those already run against real sinks, real parsers and the real\n * `createBigConsoleWidget` mutation.\n */\n\nimport {\n CUSTOM_WIDGET_PROPOSAL_VERSION,\n type CustomWidgetProposal,\n type ProposableWidgetType,\n type ProposalFieldMapping,\n} from './proposalContract';\n\n/** A sink the current user may bind to, as the panel knows it. */\nexport interface ProposalCandidateSink {\n id: string;\n key: string;\n name?: string | null;\n}\n\n/** A parser the current user may bind to. */\nexport interface ProposalCandidateParser {\n id: string;\n name: string;\n inputSinkKey: string;\n}\n\nexport interface ProposalRequest {\n prompt: string;\n sinks: readonly ProposalCandidateSink[];\n parsers: readonly ProposalCandidateParser[];\n}\n\nexport interface CustomWidgetProposalSource {\n /** Stable id, surfaced in the UI so reviewers know what produced a proposal. */\n readonly id: string;\n propose(request: ProposalRequest): Promise<CustomWidgetProposal>;\n}\n\n// ============================================================================\n// Local heuristic source\n// ============================================================================\n\n/**\n * Keyword → widget type. Ordered most-specific first so \"conversion funnel\"\n * matches funnel before a generic \"chart\" would win.\n */\nconst TYPE_KEYWORDS: ReadonlyArray<{ type: ProposableWidgetType; words: readonly string[] }> = [\n { type: 'funnel_chart', words: ['funnel', 'conversion', 'drop-off', 'dropoff', 'stage'] },\n { type: 'heatmap', words: ['heatmap', 'heat map', 'matrix', 'intensity', 'by hour', 'density'] },\n { type: 'retention', words: ['retention', 'cohort'] },\n { type: 'table', words: ['table', 'list of', 'rows', 'breakdown table', 'grid'] },\n { type: 'metric_card', words: ['kpi', 'single number', 'metric card', 'scorecard', 'total'] },\n { type: 'chart', words: ['chart', 'trend', 'over time', 'line', 'bar', 'area', 'pie', 'graph'] },\n];\n\nconst CHART_SUBTYPE_KEYWORDS: ReadonlyArray<{ chartType: string; words: readonly string[] }> = [\n { chartType: 'bar', words: ['bar'] },\n { chartType: 'area', words: ['area', 'cumulative'] },\n { chartType: 'pie', words: ['pie', 'composition', 'share', 'mix'] },\n { chartType: 'line', words: ['line', 'trend', 'over time'] },\n];\n\nfunction detectWidgetType(prompt: string): ProposableWidgetType {\n const lower = prompt.toLowerCase();\n for (const { type, words } of TYPE_KEYWORDS) {\n if (words.some((word) => lower.includes(word))) return type;\n }\n return 'chart';\n}\n\nfunction detectChartSubtype(prompt: string): string {\n const lower = prompt.toLowerCase();\n for (const { chartType, words } of CHART_SUBTYPE_KEYWORDS) {\n if (words.some((word) => lower.includes(word))) return chartType;\n }\n return 'line';\n}\n\n/**\n * Picks the sink whose key/name shares the most word-stems with the prompt.\n * Falls back to the first permitted sink so a proposal is always reviewable\n * rather than failing outright.\n */\nfunction pickSink(prompt: string, sinks: readonly ProposalCandidateSink[]): ProposalCandidateSink | undefined {\n if (sinks.length === 0) return undefined;\n const tokens = prompt\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter((token) => token.length > 2);\n\n let best: { sink: ProposalCandidateSink; score: number } | undefined;\n for (const sink of sinks) {\n const haystack = `${sink.key} ${sink.name ?? ''}`.toLowerCase();\n const score = tokens.reduce((acc, token) => (haystack.includes(token) ? acc + 1 : acc), 0);\n if (!best || score > best.score) best = { sink, score };\n }\n return best?.sink ?? sinks[0];\n}\n\n/** Default field mapping per widget type, using the renderers' conventions. */\nfunction defaultFieldMapping(type: ProposableWidgetType): ProposalFieldMapping {\n switch (type) {\n case 'heatmap':\n return { xAxisField: 'column', yAxisField: 'row', valueField: 'value' };\n case 'funnel_chart':\n return { labelField: 'name', valueField: 'value' };\n case 'metric_card':\n return { valueField: 'value' };\n case 'table':\n return {};\n case 'retention':\n return { cohortField: 'cohort', valueField: 'value' };\n case 'chart':\n default:\n return { xAxisField: 'name', labelField: 'name', valueField: 'value' };\n }\n}\n\nfunction defaultConfig(type: ProposableWidgetType, prompt: string): Record<string, unknown> {\n const base: Record<string, unknown> = { showLegend: true, showTooltip: true };\n switch (type) {\n case 'chart':\n return { ...base, chartType: detectChartSubtype(prompt), showGrid: true };\n case 'funnel_chart':\n return { ...base, chartType: 'funnel', showLabels: true, showPercentages: true, orientation: 'vertical' };\n case 'heatmap':\n return { ...base, colorScheme: 'blue', showValues: true };\n case 'table':\n return { ...base, pageSize: 10, sortable: true };\n case 'metric_card':\n return { ...base, showTrend: true };\n default:\n return base;\n }\n}\n\n/** Turns the prompt's first clause into a widget title. */\nfunction deriveTitle(prompt: string): string {\n const cleaned = prompt\n .trim()\n .replace(/^(please\\s+)?(create|build|make|add|show me|show|give me)\\s+(an?\\s+|the\\s+)?/i, '')\n .split(/[.;\\n]/)[0]\n .trim();\n if (!cleaned) return 'Custom widget';\n const titled = cleaned.charAt(0).toUpperCase() + cleaned.slice(1);\n return titled.length > 60 ? `${titled.slice(0, 57)}…` : titled;\n}\n\nlet proposalCounter = 0;\n\n/**\n * Derives a proposal from the prompt and the workspace's real, permitted sinks\n * and parsers. Deterministic, so the review UI and its tests behave predictably.\n */\nexport const localProposalSource: CustomWidgetProposalSource = {\n id: 'local-heuristic-v1',\n\n async propose({ prompt, sinks, parsers }: ProposalRequest): Promise<CustomWidgetProposal> {\n const widgetType = detectWidgetType(prompt);\n const sink = pickSink(prompt, sinks);\n const parser = sink ? parsers.find((candidate) => candidate.inputSinkKey === sink.key) : undefined;\n\n proposalCounter += 1;\n\n return {\n version: CUSTOM_WIDGET_PROPOSAL_VERSION,\n id: `proposal-${proposalCounter}`,\n sourcePrompt: prompt,\n generatedBy: localProposalSource.id,\n widgetType,\n binding: {\n dataSinkId: sink?.id ?? '',\n dataSinkKey: sink?.key,\n parserId: parser?.id,\n parserName: parser?.name,\n },\n fieldMapping: defaultFieldMapping(widgetType),\n presentation: {\n title: deriveTitle(prompt),\n description: `Generated from: \"${prompt}\"`,\n config: defaultConfig(widgetType, prompt),\n },\n rationale: sink\n ? `Matched DataSink \"${sink.key}\"${parser ? ` and parser \"${parser.name}\"` : ' (no parser found for this sink)'}.`\n : 'No DataSink is available in this workspace — create one before saving this widget.',\n };\n },\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"proposalSource.js","names":[],"sources":["../../../src/bigconsole/customWidget/proposalSource.ts"],"sourcesContent":["/**\n * Proposal source — the seam between the review UI and whatever produces\n * proposals (BOFF-5532 / BOFF-3021).\n *\n * The UI only ever talks to `CustomWidgetProposalSource`. Today the default\n * implementation derives a proposal locally from the prompt and the workspace's\n * real sinks and parsers, because the backend operation that will emit typed\n * proposals is owned by BOFF-3021's backend half and does not exist yet.\n *\n * When it lands, add a `graphqlProposalSource` here that calls it and swap the\n * value passed to the panel. Nothing in the review, edit, approve or persist\n * path changes: those already run against real sinks, real parsers and the real\n * `createBigConsoleWidget` mutation.\n */\n\nimport {\n CUSTOM_WIDGET_PROPOSAL_VERSION,\n type CustomWidgetProposal,\n type ProposableWidgetType,\n type ProposalFieldMapping,\n} from './proposalContract';\n\n/** A sink the current user may bind to, as the panel knows it. */\nexport interface ProposalCandidateSink {\n id: string;\n key: string;\n name?: string | null;\n}\n\n/**\n * Converts the workspace store into a proposal-safe candidate set. The API\n * excludes soft-deleted sinks, but this second boundary is intentional:\n * persisted client state can outlive an out-of-band deletion.\n */\nexport function selectActiveProposalSinks(\n sinks: ReadonlyArray<ProposalCandidateSink & { deletedAt?: string | null }>\n): ProposalCandidateSink[] {\n return sinks.filter((sink) => !sink.deletedAt).map(({ id, key, name }) => ({ id, key, name }));\n}\n\n/** A parser the current user may bind to. */\nexport interface ProposalCandidateParser {\n id: string;\n name: string;\n inputSinkKey: string;\n}\n\nexport interface ProposalRequest {\n prompt: string;\n sinks: readonly ProposalCandidateSink[];\n parsers: readonly ProposalCandidateParser[];\n}\n\nexport interface CustomWidgetProposalSource {\n /** Stable id, surfaced in the UI so reviewers know what produced a proposal. */\n readonly id: string;\n propose(request: ProposalRequest): Promise<CustomWidgetProposal>;\n}\n\n// ============================================================================\n// Local heuristic source\n// ============================================================================\n\n/**\n * Keyword → widget type. Ordered most-specific first so \"conversion funnel\"\n * matches funnel before a generic \"chart\" would win.\n */\nconst TYPE_KEYWORDS: ReadonlyArray<{ type: ProposableWidgetType; words: readonly string[] }> = [\n { type: 'funnel_chart', words: ['funnel', 'conversion', 'drop-off', 'dropoff', 'stage'] },\n { type: 'heatmap', words: ['heatmap', 'heat map', 'matrix', 'intensity', 'by hour', 'density'] },\n { type: 'retention', words: ['retention', 'cohort'] },\n { type: 'table', words: ['table', 'list of', 'rows', 'breakdown table', 'grid'] },\n { type: 'metric_card', words: ['kpi', 'single number', 'metric card', 'scorecard', 'total'] },\n { type: 'chart', words: ['chart', 'trend', 'over time', 'line', 'bar', 'area', 'pie', 'graph'] },\n];\n\nconst CHART_SUBTYPE_KEYWORDS: ReadonlyArray<{ chartType: string; words: readonly string[] }> = [\n { chartType: 'bar', words: ['bar'] },\n { chartType: 'area', words: ['area', 'cumulative'] },\n { chartType: 'pie', words: ['pie', 'composition', 'share', 'mix'] },\n { chartType: 'line', words: ['line', 'trend', 'over time'] },\n];\n\nfunction detectWidgetType(prompt: string): ProposableWidgetType {\n const lower = prompt.toLowerCase();\n for (const { type, words } of TYPE_KEYWORDS) {\n if (words.some((word) => lower.includes(word))) return type;\n }\n return 'chart';\n}\n\nfunction detectChartSubtype(prompt: string): string {\n const lower = prompt.toLowerCase();\n for (const { chartType, words } of CHART_SUBTYPE_KEYWORDS) {\n if (words.some((word) => lower.includes(word))) return chartType;\n }\n return 'line';\n}\n\n/**\n * Picks the sink whose key/name shares the most word-stems with the prompt.\n * Falls back to the first permitted sink so a proposal is always reviewable\n * rather than failing outright.\n */\nfunction pickSink(prompt: string, sinks: readonly ProposalCandidateSink[]): ProposalCandidateSink | undefined {\n if (sinks.length === 0) return undefined;\n const tokens = prompt\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter((token) => token.length > 2);\n\n let best: { sink: ProposalCandidateSink; score: number } | undefined;\n for (const sink of sinks) {\n const haystack = `${sink.key} ${sink.name ?? ''}`.toLowerCase();\n const score = tokens.reduce((acc, token) => (haystack.includes(token) ? acc + 1 : acc), 0);\n if (!best || score > best.score) best = { sink, score };\n }\n return best?.sink ?? sinks[0];\n}\n\n/** Default field mapping per widget type, using the renderers' conventions. */\nfunction defaultFieldMapping(type: ProposableWidgetType): ProposalFieldMapping {\n switch (type) {\n case 'heatmap':\n return { xAxisField: 'column', yAxisField: 'row', valueField: 'value' };\n case 'funnel_chart':\n return { labelField: 'name', valueField: 'value' };\n case 'metric_card':\n return { valueField: 'value' };\n case 'table':\n return {};\n case 'retention':\n return { cohortField: 'cohort', valueField: 'value' };\n case 'chart':\n default:\n return { xAxisField: 'name', labelField: 'name', valueField: 'value' };\n }\n}\n\nfunction defaultConfig(type: ProposableWidgetType, prompt: string): Record<string, unknown> {\n const base: Record<string, unknown> = { showLegend: true, showTooltip: true };\n switch (type) {\n case 'chart':\n return { ...base, chartType: detectChartSubtype(prompt), showGrid: true };\n case 'funnel_chart':\n return { ...base, chartType: 'funnel', showLabels: true, showPercentages: true, orientation: 'vertical' };\n case 'heatmap':\n return { ...base, colorScheme: 'blue', showValues: true };\n case 'table':\n return { ...base, pageSize: 10, sortable: true };\n case 'metric_card':\n return { ...base, showTrend: true };\n default:\n return base;\n }\n}\n\n/** Turns the prompt's first clause into a widget title. */\nfunction deriveTitle(prompt: string): string {\n const cleaned = prompt\n .trim()\n .replace(/^(please\\s+)?(create|build|make|add|show me|show|give me)\\s+(an?\\s+|the\\s+)?/i, '')\n .split(/[.;\\n]/)[0]\n .trim();\n if (!cleaned) return 'Custom widget';\n const titled = cleaned.charAt(0).toUpperCase() + cleaned.slice(1);\n return titled.length > 60 ? `${titled.slice(0, 57)}…` : titled;\n}\n\nlet proposalCounter = 0;\n\n/**\n * Derives a proposal from the prompt and the workspace's real, permitted sinks\n * and parsers. Deterministic, so the review UI and its tests behave predictably.\n */\nexport const localProposalSource: CustomWidgetProposalSource = {\n id: 'local-heuristic-v1',\n\n async propose({ prompt, sinks, parsers }: ProposalRequest): Promise<CustomWidgetProposal> {\n const widgetType = detectWidgetType(prompt);\n const sink = pickSink(prompt, sinks);\n const parser = sink ? parsers.find((candidate) => candidate.inputSinkKey === sink.key) : undefined;\n\n proposalCounter += 1;\n\n return {\n version: CUSTOM_WIDGET_PROPOSAL_VERSION,\n id: `proposal-${proposalCounter}`,\n sourcePrompt: prompt,\n generatedBy: localProposalSource.id,\n widgetType,\n binding: {\n dataSinkId: sink?.id ?? '',\n dataSinkKey: sink?.key,\n parserId: parser?.id,\n parserName: parser?.name,\n },\n fieldMapping: defaultFieldMapping(widgetType),\n presentation: {\n title: deriveTitle(prompt),\n description: `Generated from: \"${prompt}\"`,\n config: defaultConfig(widgetType, prompt),\n },\n rationale: sink\n ? `Matched DataSink \"${sink.key}\"${parser ? ` and parser \"${parser.name}\"` : ' (no parser found for this sink)'}.`\n : 'No DataSink is available in this workspace — create one before saving this widget.',\n };\n },\n};\n"],"mappings":"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"}
|
|
@@ -49,7 +49,7 @@ function g(e) {
|
|
|
49
49
|
function _(_ = {}) {
|
|
50
50
|
let { workspaceId: v, skipFetch: y = !1 } = _, [b, x] = m(!1), [S, C] = m(null), w = h(), T = e((e) => e.dataSinks), E = e((e) => e.setDataSinks), D = e((e) => e.addDataSink), O = e((e) => e.updateDataSink), k = e((e) => e.removeDataSink), A = e((e) => e.setDataSinkRefreshing), j = p(() => Array.from(T.values()), [T]), M = d(async (e) => {
|
|
51
51
|
try {
|
|
52
|
-
x(!0), C(null);
|
|
52
|
+
x(!0), C(null), E([]);
|
|
53
53
|
let t = ((await w.query({
|
|
54
54
|
query: c,
|
|
55
55
|
variables: {
|
|
@@ -61,7 +61,7 @@ function _(_ = {}) {
|
|
|
61
61
|
})).data?.listDataSinks?.edges || []).map((e) => g(e.node));
|
|
62
62
|
return E(t), t;
|
|
63
63
|
} catch (e) {
|
|
64
|
-
return C(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to fetch DataSinks")), [];
|
|
64
|
+
return C(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to fetch DataSinks")), E([]), [];
|
|
65
65
|
} finally {
|
|
66
66
|
x(!1);
|
|
67
67
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDataSinkOperations.js","names":[],"sources":["../../../src/bigconsole/hooks/useDataSinkOperations.ts"],"sourcesContent":["/**\n * DataSink Operations Hook\n *\n * Custom hook for DataSink CRUD operations with Apollo Client.\n * Integrates with the DataSink Zustand store for state management.\n */\n\nimport { useState, useMemo, useCallback, useEffect } from 'react';\nimport { useApolloClient } from '@apollo/client/react';\nimport { useDataSinkStore } from '../store/dataSinkStore';\nimport {\n ListDataSinksDocument,\n GetDataSinkDocument,\n GetDataSinkDataDocument,\n CreateDataSinkDocument,\n UpdateDataSinkDocument,\n DeleteDataSinkDocument,\n RefreshDataSinkDocument,\n InvalidateDataSinkCacheDocument,\n BatchCreateDataSinksDocument,\n BatchDeleteDataSinksDocument,\n} from '../../generated/wspace-operations';\nimport type { DataSink, DataSinkStatus, CreateDataSinkInput, UpdateDataSinkInput } from '../types';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface UseDataSinkOperationsOptions {\n workspaceId?: string;\n skipFetch?: boolean;\n}\n\ninterface DataSinkFilters {\n status?: DataSinkStatus;\n isActive?: boolean;\n search?: string;\n}\n\ninterface RefreshDataSinkInput {\n id: string;\n forceRefresh?: boolean;\n}\n\ninterface InvalidateCacheResult {\n count: number;\n invalidatedIds: string[];\n success: boolean;\n message?: string;\n}\n\ninterface DataSinkDataResult {\n raw: unknown;\n extracted: unknown;\n cached: boolean;\n version: number;\n fetchedAt: string;\n // v3.0: pagination metadata (non-null totalCount only for array payloads).\n totalCount?: number | null;\n limit?: number | null;\n offset?: number | null;\n hasMore?: boolean | null;\n}\n\n// GraphQL Query/Mutation Result Types\ninterface ListDataSinksResult {\n listDataSinks?: {\n edges: Array<{ node: unknown }>;\n pageInfo?: { hasNextPage: boolean; endCursor?: string };\n };\n}\n\ninterface GetDataSinkResult {\n getDataSink?: unknown;\n}\n\ninterface GetDataSinkDataResult {\n getDataSinkData?: DataSinkDataResult;\n}\n\ninterface CreateDataSinkResult {\n createDataSink?: unknown;\n}\n\ninterface UpdateDataSinkResult {\n updateDataSink?: unknown;\n}\n\ninterface DeleteDataSinkResult {\n deleteDataSink?: boolean;\n}\n\ninterface RefreshDataSinkResult {\n refreshDataSink?: unknown;\n}\n\ninterface InvalidateDataSinkCacheResult {\n invalidateDataSinkCache?: InvalidateCacheResult;\n}\n\ninterface BatchCreateDataSinksResult {\n batchCreateDataSinks?: unknown[];\n}\n\ninterface BatchDeleteDataSinksResult {\n batchDeleteDataSinks?: Array<{ id: string; deletedAt: string }>;\n}\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Normalize GraphQL response to DataSink type\n */\nfunction normalizeDataSink(data: unknown): DataSink {\n const raw = data as Record<string, unknown>;\n return {\n id: raw.id as string,\n workspaceId: raw.workspaceId as string,\n tenantId: raw.tenantId as string,\n name: (raw.name as string) || null,\n description: (raw.description as string) || null,\n key: raw.key as string,\n fluidGridsWorkflowId: (raw.fluidGridsWorkflowId as string) || null,\n fluidGridsWorkflowExecutionId: (raw.fluidGridsWorkflowExecutionId as string) || null,\n config: (raw.config as Record<string, unknown>) || null,\n data: (raw.data as Record<string, unknown>) || {},\n dataStats: (raw.dataStats as DataSink['dataStats']) || null,\n lastRefreshedAt: (raw.lastRefreshedAt as string) || new Date().toISOString(),\n lastRefreshedBy: (raw.lastRefreshedBy as string) || null,\n expiresAt: (raw.expiresAt as string) || null,\n nextRefreshAt: (raw.nextRefreshAt as string) || null,\n ttl: (raw.ttl as number) ?? 0,\n version: (raw.version as number) ?? 1,\n sizeBytes: (raw.sizeBytes as number) ?? 0,\n isActive: (raw.isActive as boolean) ?? true,\n fieldMaskingEnabled: (raw.fieldMaskingEnabled as boolean) ?? false,\n fieldMaskingRules: (raw.fieldMaskingRules as Record<string, unknown>) || null,\n status: (raw.status as DataSink['status']) || 'ready',\n statusChangedAt: (raw.statusChangedAt as string) || null,\n statusChangedBy: (raw.statusChangedBy as string) || null,\n setupState: (raw.setupState as DataSink['setupState']) || 'not_started',\n refresh: (raw.refresh as DataSink['refresh']) || null,\n exports: (raw.exports as DataSink['exports']) || null,\n alerts: (raw.alerts as DataSink['alerts']) || null,\n quota: (raw.quota as DataSink['quota']) || null,\n workflowLinks: raw.workflowLinks as DataSink['workflowLinks'],\n linkedWorkflowCount: raw.linkedWorkflowCount as number | undefined,\n metadata: (raw.metadata as Record<string, unknown>) || null,\n createdAt: raw.createdAt as string,\n createdBy: (raw.createdBy as string) || '',\n updatedAt: raw.updatedAt as string,\n updatedBy: (raw.updatedBy as string) || '',\n deletedAt: (raw.deletedAt as string) || null,\n deletedBy: (raw.deletedBy as string) || null,\n };\n}\n\n// ============================================================================\n// HOOK\n// ============================================================================\n\nexport function useDataSinkOperations(options: UseDataSinkOperationsOptions = {}) {\n const { workspaceId, skipFetch = false } = options;\n\n // Local state\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Apollo client\n const client = useApolloClient();\n\n // Store selectors and actions\n const dataSinksMap = useDataSinkStore((state) => state.dataSinks);\n const setDataSinks = useDataSinkStore((state) => state.setDataSinks);\n const addDataSink = useDataSinkStore((state) => state.addDataSink);\n const updateDataSinkInStore = useDataSinkStore((state) => state.updateDataSink);\n const removeDataSink = useDataSinkStore((state) => state.removeDataSink);\n const setDataSinkRefreshing = useDataSinkStore((state) => state.setDataSinkRefreshing);\n\n // Memoized array of datasinks\n const dataSinks = useMemo(() => Array.from(dataSinksMap.values()), [dataSinksMap]);\n\n // ============================================================================\n // OPERATIONS\n // ============================================================================\n\n /**\n * Fetch all datasinks with optional filters\n */\n const fetchDataSinks = useCallback(\n async (filters?: DataSinkFilters): Promise<DataSink[]> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.query<ListDataSinksResult>({\n query: ListDataSinksDocument,\n variables: {\n status: filters?.status,\n isActive: filters?.isActive,\n search: filters?.search,\n },\n fetchPolicy: 'network-only',\n });\n\n const edges = result.data?.listDataSinks?.edges || [];\n const normalized = edges.map((edge: { node: unknown }) => normalizeDataSink(edge.node));\n\n setDataSinks(normalized);\n return normalized;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to fetch DataSinks');\n setError(error);\n return [];\n } finally {\n setLoading(false);\n }\n },\n [client, setDataSinks]\n );\n\n /**\n * Fetch a single datasink by ID\n */\n const fetchDataSink = useCallback(\n async (id: string): Promise<DataSink | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.query<GetDataSinkResult>({\n query: GetDataSinkDocument,\n variables: { id },\n fetchPolicy: 'network-only',\n });\n\n if (result.data?.getDataSink) {\n const normalized = normalizeDataSink(result.data.getDataSink);\n addDataSink(normalized);\n return normalized;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to fetch DataSink');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client, addDataSink]\n );\n\n /**\n * Get datasink data for preview\n */\n const getDataSinkData = useCallback(\n async (\n idOrKey: { id?: string; key?: string },\n options?: {\n dataPath?: string;\n forceRefresh?: boolean;\n // v3.0: server-side pagination + filter for array payloads.\n limit?: number;\n offset?: number;\n filter?: Array<{ field: string; op: string; value: unknown }>;\n }\n ): Promise<DataSinkDataResult | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.query<GetDataSinkDataResult>({\n query: GetDataSinkDataDocument,\n variables: {\n id: idOrKey.id,\n key: idOrKey.key,\n dataPath: options?.dataPath,\n forceRefresh: options?.forceRefresh,\n limit: options?.limit,\n offset: options?.offset,\n filter: options?.filter,\n },\n fetchPolicy: 'network-only',\n });\n\n return result.data?.getDataSinkData || null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to get DataSink data');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client]\n );\n\n /**\n * Create a new datasink\n */\n const createDataSink = useCallback(\n async (input: CreateDataSinkInput): Promise<DataSink | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<CreateDataSinkResult>({\n mutation: CreateDataSinkDocument,\n variables: { input },\n });\n\n if (result.data?.createDataSink) {\n const normalized = normalizeDataSink(result.data.createDataSink);\n addDataSink(normalized);\n return normalized;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to create DataSink');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client, addDataSink]\n );\n\n /**\n * Update an existing datasink\n */\n const updateDataSink = useCallback(\n async (input: UpdateDataSinkInput): Promise<DataSink | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<UpdateDataSinkResult>({\n mutation: UpdateDataSinkDocument,\n variables: { input },\n });\n\n if (result.data?.updateDataSink) {\n const normalized = normalizeDataSink(result.data.updateDataSink);\n updateDataSinkInStore(normalized.id, normalized);\n return normalized;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to update DataSink');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client, updateDataSinkInStore]\n );\n\n /**\n * Delete a datasink\n */\n const deleteDataSink = useCallback(\n async (id: string): Promise<boolean> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<DeleteDataSinkResult>({\n mutation: DeleteDataSinkDocument,\n variables: { id },\n });\n\n if (result.data?.deleteDataSink) {\n removeDataSink(id);\n return true;\n }\n\n return false;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to delete DataSink');\n setError(error);\n return false;\n } finally {\n setLoading(false);\n }\n },\n [client, removeDataSink]\n );\n\n /**\n * Refresh a datasink's data\n */\n const refreshDataSink = useCallback(\n async (input: RefreshDataSinkInput): Promise<DataSink | null> => {\n try {\n setLoading(true);\n setError(null);\n\n if (input.id) {\n setDataSinkRefreshing(input.id, true);\n }\n\n const result = await client.mutate<RefreshDataSinkResult>({\n mutation: RefreshDataSinkDocument,\n variables: { input },\n });\n\n if (result.data?.refreshDataSink) {\n const normalized = normalizeDataSink(result.data.refreshDataSink);\n updateDataSinkInStore(normalized.id, normalized);\n setDataSinkRefreshing(normalized.id, false);\n return normalized;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to refresh DataSink');\n setError(error);\n if (input.id) {\n setDataSinkRefreshing(input.id, false);\n }\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client, updateDataSinkInStore, setDataSinkRefreshing]\n );\n\n /**\n * Invalidate datasink cache\n */\n const invalidateCache = useCallback(\n async (id?: string): Promise<InvalidateCacheResult> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<InvalidateDataSinkCacheResult>({\n mutation: InvalidateDataSinkCacheDocument,\n variables: {\n id,\n },\n });\n\n return (\n result.data?.invalidateDataSinkCache || {\n count: 0,\n invalidatedIds: [],\n success: false,\n }\n );\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to invalidate cache');\n setError(error);\n return {\n count: 0,\n invalidatedIds: [],\n success: false,\n message: error.message,\n };\n } finally {\n setLoading(false);\n }\n },\n [client, workspaceId]\n );\n\n /**\n * Batch create datasinks\n */\n const batchCreateDataSinks = useCallback(\n async (inputs: CreateDataSinkInput[]): Promise<DataSink[]> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<BatchCreateDataSinksResult>({\n mutation: BatchCreateDataSinksDocument,\n variables: { inputs },\n });\n\n if (result.data?.batchCreateDataSinks) {\n const normalized = result.data.batchCreateDataSinks.map(normalizeDataSink);\n normalized.forEach(addDataSink);\n return normalized;\n }\n\n return [];\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to batch create DataSinks');\n setError(error);\n return [];\n } finally {\n setLoading(false);\n }\n },\n [client, addDataSink]\n );\n\n /**\n * Batch delete datasinks\n */\n const batchDeleteDataSinks = useCallback(\n async (ids: string[]): Promise<{ id: string; deletedAt: string }[]> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<BatchDeleteDataSinksResult>({\n mutation: BatchDeleteDataSinksDocument,\n variables: { ids },\n });\n\n if (result.data?.batchDeleteDataSinks) {\n ids.forEach(removeDataSink);\n return result.data.batchDeleteDataSinks;\n }\n\n return [];\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to batch delete DataSinks');\n setError(error);\n return [];\n } finally {\n setLoading(false);\n }\n },\n [client, removeDataSink]\n );\n\n /**\n * Refetch all datasinks\n */\n const refetch = useCallback(async () => {\n await fetchDataSinks();\n }, [fetchDataSinks]);\n\n // Auto-fetch on mount if not skipped\n useEffect(() => {\n if (!skipFetch) {\n fetchDataSinks();\n }\n }, [skipFetch, fetchDataSinks]);\n\n return {\n loading,\n error,\n dataSinks,\n fetchDataSinks,\n fetchDataSink,\n getDataSinkData,\n createDataSink,\n updateDataSink,\n deleteDataSink,\n refreshDataSink,\n invalidateCache,\n batchCreateDataSinks,\n batchDeleteDataSinks,\n refetch,\n };\n}\n\nexport default useDataSinkOperations;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useDataSinkOperations.js","names":[],"sources":["../../../src/bigconsole/hooks/useDataSinkOperations.ts"],"sourcesContent":["/**\n * DataSink Operations Hook\n *\n * Custom hook for DataSink CRUD operations with Apollo Client.\n * Integrates with the DataSink Zustand store for state management.\n */\n\nimport { useState, useMemo, useCallback, useEffect } from 'react';\nimport { useApolloClient } from '@apollo/client/react';\nimport { useDataSinkStore } from '../store/dataSinkStore';\nimport {\n ListDataSinksDocument,\n GetDataSinkDocument,\n GetDataSinkDataDocument,\n CreateDataSinkDocument,\n UpdateDataSinkDocument,\n DeleteDataSinkDocument,\n RefreshDataSinkDocument,\n InvalidateDataSinkCacheDocument,\n BatchCreateDataSinksDocument,\n BatchDeleteDataSinksDocument,\n} from '../../generated/wspace-operations';\nimport type { DataSink, DataSinkStatus, CreateDataSinkInput, UpdateDataSinkInput } from '../types';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface UseDataSinkOperationsOptions {\n workspaceId?: string;\n skipFetch?: boolean;\n}\n\ninterface DataSinkFilters {\n status?: DataSinkStatus;\n isActive?: boolean;\n search?: string;\n}\n\ninterface RefreshDataSinkInput {\n id: string;\n forceRefresh?: boolean;\n}\n\ninterface InvalidateCacheResult {\n count: number;\n invalidatedIds: string[];\n success: boolean;\n message?: string;\n}\n\ninterface DataSinkDataResult {\n raw: unknown;\n extracted: unknown;\n cached: boolean;\n version: number;\n fetchedAt: string;\n // v3.0: pagination metadata (non-null totalCount only for array payloads).\n totalCount?: number | null;\n limit?: number | null;\n offset?: number | null;\n hasMore?: boolean | null;\n}\n\n// GraphQL Query/Mutation Result Types\ninterface ListDataSinksResult {\n listDataSinks?: {\n edges: Array<{ node: unknown }>;\n pageInfo?: { hasNextPage: boolean; endCursor?: string };\n };\n}\n\ninterface GetDataSinkResult {\n getDataSink?: unknown;\n}\n\ninterface GetDataSinkDataResult {\n getDataSinkData?: DataSinkDataResult;\n}\n\ninterface CreateDataSinkResult {\n createDataSink?: unknown;\n}\n\ninterface UpdateDataSinkResult {\n updateDataSink?: unknown;\n}\n\ninterface DeleteDataSinkResult {\n deleteDataSink?: boolean;\n}\n\ninterface RefreshDataSinkResult {\n refreshDataSink?: unknown;\n}\n\ninterface InvalidateDataSinkCacheResult {\n invalidateDataSinkCache?: InvalidateCacheResult;\n}\n\ninterface BatchCreateDataSinksResult {\n batchCreateDataSinks?: unknown[];\n}\n\ninterface BatchDeleteDataSinksResult {\n batchDeleteDataSinks?: Array<{ id: string; deletedAt: string }>;\n}\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Normalize GraphQL response to DataSink type\n */\nfunction normalizeDataSink(data: unknown): DataSink {\n const raw = data as Record<string, unknown>;\n return {\n id: raw.id as string,\n workspaceId: raw.workspaceId as string,\n tenantId: raw.tenantId as string,\n name: (raw.name as string) || null,\n description: (raw.description as string) || null,\n key: raw.key as string,\n fluidGridsWorkflowId: (raw.fluidGridsWorkflowId as string) || null,\n fluidGridsWorkflowExecutionId: (raw.fluidGridsWorkflowExecutionId as string) || null,\n config: (raw.config as Record<string, unknown>) || null,\n data: (raw.data as Record<string, unknown>) || {},\n dataStats: (raw.dataStats as DataSink['dataStats']) || null,\n lastRefreshedAt: (raw.lastRefreshedAt as string) || new Date().toISOString(),\n lastRefreshedBy: (raw.lastRefreshedBy as string) || null,\n expiresAt: (raw.expiresAt as string) || null,\n nextRefreshAt: (raw.nextRefreshAt as string) || null,\n ttl: (raw.ttl as number) ?? 0,\n version: (raw.version as number) ?? 1,\n sizeBytes: (raw.sizeBytes as number) ?? 0,\n isActive: (raw.isActive as boolean) ?? true,\n fieldMaskingEnabled: (raw.fieldMaskingEnabled as boolean) ?? false,\n fieldMaskingRules: (raw.fieldMaskingRules as Record<string, unknown>) || null,\n status: (raw.status as DataSink['status']) || 'ready',\n statusChangedAt: (raw.statusChangedAt as string) || null,\n statusChangedBy: (raw.statusChangedBy as string) || null,\n setupState: (raw.setupState as DataSink['setupState']) || 'not_started',\n refresh: (raw.refresh as DataSink['refresh']) || null,\n exports: (raw.exports as DataSink['exports']) || null,\n alerts: (raw.alerts as DataSink['alerts']) || null,\n quota: (raw.quota as DataSink['quota']) || null,\n workflowLinks: raw.workflowLinks as DataSink['workflowLinks'],\n linkedWorkflowCount: raw.linkedWorkflowCount as number | undefined,\n metadata: (raw.metadata as Record<string, unknown>) || null,\n createdAt: raw.createdAt as string,\n createdBy: (raw.createdBy as string) || '',\n updatedAt: raw.updatedAt as string,\n updatedBy: (raw.updatedBy as string) || '',\n deletedAt: (raw.deletedAt as string) || null,\n deletedBy: (raw.deletedBy as string) || null,\n };\n}\n\n// ============================================================================\n// HOOK\n// ============================================================================\n\nexport function useDataSinkOperations(options: UseDataSinkOperationsOptions = {}) {\n const { workspaceId, skipFetch = false } = options;\n\n // Local state\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Apollo client\n const client = useApolloClient();\n\n // Store selectors and actions\n const dataSinksMap = useDataSinkStore((state) => state.dataSinks);\n const setDataSinks = useDataSinkStore((state) => state.setDataSinks);\n const addDataSink = useDataSinkStore((state) => state.addDataSink);\n const updateDataSinkInStore = useDataSinkStore((state) => state.updateDataSink);\n const removeDataSink = useDataSinkStore((state) => state.removeDataSink);\n const setDataSinkRefreshing = useDataSinkStore((state) => state.setDataSinkRefreshing);\n\n // Memoized array of datasinks\n const dataSinks = useMemo(() => Array.from(dataSinksMap.values()), [dataSinksMap]);\n\n // ============================================================================\n // OPERATIONS\n // ============================================================================\n\n /**\n * Fetch all datasinks with optional filters\n */\n const fetchDataSinks = useCallback(\n async (filters?: DataSinkFilters): Promise<DataSink[]> => {\n try {\n setLoading(true);\n setError(null);\n // Fail closed while the authoritative workspace list is refreshed.\n // A persisted Zustand snapshot may contain sinks deleted by another\n // tab, an API client, or an E2E harness; proposal pickers must never\n // bind to those stale records while this network request is pending.\n setDataSinks([]);\n\n const result = await client.query<ListDataSinksResult>({\n query: ListDataSinksDocument,\n variables: {\n status: filters?.status,\n isActive: filters?.isActive,\n search: filters?.search,\n },\n fetchPolicy: 'network-only',\n });\n\n const edges = result.data?.listDataSinks?.edges || [];\n const normalized = edges.map((edge: { node: unknown }) => normalizeDataSink(edge.node));\n\n setDataSinks(normalized);\n return normalized;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to fetch DataSinks');\n setError(error);\n // Do not retain a previously permitted resource set after an auth,\n // workspace-context, or network failure.\n setDataSinks([]);\n return [];\n } finally {\n setLoading(false);\n }\n },\n [client, setDataSinks]\n );\n\n /**\n * Fetch a single datasink by ID\n */\n const fetchDataSink = useCallback(\n async (id: string): Promise<DataSink | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.query<GetDataSinkResult>({\n query: GetDataSinkDocument,\n variables: { id },\n fetchPolicy: 'network-only',\n });\n\n if (result.data?.getDataSink) {\n const normalized = normalizeDataSink(result.data.getDataSink);\n addDataSink(normalized);\n return normalized;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to fetch DataSink');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client, addDataSink]\n );\n\n /**\n * Get datasink data for preview\n */\n const getDataSinkData = useCallback(\n async (\n idOrKey: { id?: string; key?: string },\n options?: {\n dataPath?: string;\n forceRefresh?: boolean;\n // v3.0: server-side pagination + filter for array payloads.\n limit?: number;\n offset?: number;\n filter?: Array<{ field: string; op: string; value: unknown }>;\n }\n ): Promise<DataSinkDataResult | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.query<GetDataSinkDataResult>({\n query: GetDataSinkDataDocument,\n variables: {\n id: idOrKey.id,\n key: idOrKey.key,\n dataPath: options?.dataPath,\n forceRefresh: options?.forceRefresh,\n limit: options?.limit,\n offset: options?.offset,\n filter: options?.filter,\n },\n fetchPolicy: 'network-only',\n });\n\n return result.data?.getDataSinkData || null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to get DataSink data');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client]\n );\n\n /**\n * Create a new datasink\n */\n const createDataSink = useCallback(\n async (input: CreateDataSinkInput): Promise<DataSink | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<CreateDataSinkResult>({\n mutation: CreateDataSinkDocument,\n variables: { input },\n });\n\n if (result.data?.createDataSink) {\n const normalized = normalizeDataSink(result.data.createDataSink);\n addDataSink(normalized);\n return normalized;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to create DataSink');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client, addDataSink]\n );\n\n /**\n * Update an existing datasink\n */\n const updateDataSink = useCallback(\n async (input: UpdateDataSinkInput): Promise<DataSink | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<UpdateDataSinkResult>({\n mutation: UpdateDataSinkDocument,\n variables: { input },\n });\n\n if (result.data?.updateDataSink) {\n const normalized = normalizeDataSink(result.data.updateDataSink);\n updateDataSinkInStore(normalized.id, normalized);\n return normalized;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to update DataSink');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client, updateDataSinkInStore]\n );\n\n /**\n * Delete a datasink\n */\n const deleteDataSink = useCallback(\n async (id: string): Promise<boolean> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<DeleteDataSinkResult>({\n mutation: DeleteDataSinkDocument,\n variables: { id },\n });\n\n if (result.data?.deleteDataSink) {\n removeDataSink(id);\n return true;\n }\n\n return false;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to delete DataSink');\n setError(error);\n return false;\n } finally {\n setLoading(false);\n }\n },\n [client, removeDataSink]\n );\n\n /**\n * Refresh a datasink's data\n */\n const refreshDataSink = useCallback(\n async (input: RefreshDataSinkInput): Promise<DataSink | null> => {\n try {\n setLoading(true);\n setError(null);\n\n if (input.id) {\n setDataSinkRefreshing(input.id, true);\n }\n\n const result = await client.mutate<RefreshDataSinkResult>({\n mutation: RefreshDataSinkDocument,\n variables: { input },\n });\n\n if (result.data?.refreshDataSink) {\n const normalized = normalizeDataSink(result.data.refreshDataSink);\n updateDataSinkInStore(normalized.id, normalized);\n setDataSinkRefreshing(normalized.id, false);\n return normalized;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to refresh DataSink');\n setError(error);\n if (input.id) {\n setDataSinkRefreshing(input.id, false);\n }\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client, updateDataSinkInStore, setDataSinkRefreshing]\n );\n\n /**\n * Invalidate datasink cache\n */\n const invalidateCache = useCallback(\n async (id?: string): Promise<InvalidateCacheResult> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<InvalidateDataSinkCacheResult>({\n mutation: InvalidateDataSinkCacheDocument,\n variables: {\n id,\n },\n });\n\n return (\n result.data?.invalidateDataSinkCache || {\n count: 0,\n invalidatedIds: [],\n success: false,\n }\n );\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to invalidate cache');\n setError(error);\n return {\n count: 0,\n invalidatedIds: [],\n success: false,\n message: error.message,\n };\n } finally {\n setLoading(false);\n }\n },\n [client, workspaceId]\n );\n\n /**\n * Batch create datasinks\n */\n const batchCreateDataSinks = useCallback(\n async (inputs: CreateDataSinkInput[]): Promise<DataSink[]> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<BatchCreateDataSinksResult>({\n mutation: BatchCreateDataSinksDocument,\n variables: { inputs },\n });\n\n if (result.data?.batchCreateDataSinks) {\n const normalized = result.data.batchCreateDataSinks.map(normalizeDataSink);\n normalized.forEach(addDataSink);\n return normalized;\n }\n\n return [];\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to batch create DataSinks');\n setError(error);\n return [];\n } finally {\n setLoading(false);\n }\n },\n [client, addDataSink]\n );\n\n /**\n * Batch delete datasinks\n */\n const batchDeleteDataSinks = useCallback(\n async (ids: string[]): Promise<{ id: string; deletedAt: string }[]> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<BatchDeleteDataSinksResult>({\n mutation: BatchDeleteDataSinksDocument,\n variables: { ids },\n });\n\n if (result.data?.batchDeleteDataSinks) {\n ids.forEach(removeDataSink);\n return result.data.batchDeleteDataSinks;\n }\n\n return [];\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to batch delete DataSinks');\n setError(error);\n return [];\n } finally {\n setLoading(false);\n }\n },\n [client, removeDataSink]\n );\n\n /**\n * Refetch all datasinks\n */\n const refetch = useCallback(async () => {\n await fetchDataSinks();\n }, [fetchDataSinks]);\n\n // Auto-fetch on mount if not skipped\n useEffect(() => {\n if (!skipFetch) {\n fetchDataSinks();\n }\n }, [skipFetch, fetchDataSinks]);\n\n return {\n loading,\n error,\n dataSinks,\n fetchDataSinks,\n fetchDataSink,\n getDataSinkData,\n createDataSink,\n updateDataSink,\n deleteDataSink,\n refreshDataSink,\n invalidateCache,\n batchCreateDataSinks,\n batchDeleteDataSinks,\n refetch,\n };\n}\n\nexport default useDataSinkOperations;\n"],"mappings":"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"}
|