@burdenoff/microfe-vibecontrols 2026.522.2 → 2026.522.4

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.
Files changed (111) hide show
  1. package/dist/node_modules/@apollo/client/errors/ServerError.js +17 -0
  2. package/dist/node_modules/@apollo/client/errors/ServerError.js.map +1 -0
  3. package/dist/node_modules/@apollo/client/errors/ServerParseError.js +17 -0
  4. package/dist/node_modules/@apollo/client/errors/ServerParseError.js.map +1 -0
  5. package/dist/node_modules/@apollo/client/link/batch/batchLink.js +28 -0
  6. package/dist/node_modules/@apollo/client/link/batch/batchLink.js.map +1 -0
  7. package/dist/node_modules/@apollo/client/link/batch/batching.js +70 -0
  8. package/dist/node_modules/@apollo/client/link/batch/batching.js.map +1 -0
  9. package/dist/node_modules/@apollo/client/link/batch-http/BaseBatchHttpLink.js +74 -0
  10. package/dist/node_modules/@apollo/client/link/batch-http/BaseBatchHttpLink.js.map +1 -0
  11. package/dist/node_modules/@apollo/client/link/batch-http/batchHttpLink.js +17 -0
  12. package/dist/node_modules/@apollo/client/link/batch-http/batchHttpLink.js.map +1 -0
  13. package/dist/node_modules/@apollo/client/link/client-awareness/ClientAwarenessLink.js +32 -0
  14. package/dist/node_modules/@apollo/client/link/client-awareness/ClientAwarenessLink.js.map +1 -0
  15. package/dist/node_modules/@apollo/client/link/core/ApolloLink.js +52 -0
  16. package/dist/node_modules/@apollo/client/link/core/ApolloLink.js.map +1 -0
  17. package/dist/node_modules/@apollo/client/link/http/checkFetcher.js +9 -0
  18. package/dist/node_modules/@apollo/client/link/http/checkFetcher.js.map +1 -0
  19. package/dist/node_modules/@apollo/client/link/http/parseAndCheckHttpResponse.js +45 -0
  20. package/dist/node_modules/@apollo/client/link/http/parseAndCheckHttpResponse.js.map +1 -0
  21. package/dist/node_modules/@apollo/client/link/http/selectHttpOptionsAndBody.js +60 -0
  22. package/dist/node_modules/@apollo/client/link/http/selectHttpOptionsAndBody.js.map +1 -0
  23. package/dist/node_modules/@apollo/client/link/http/selectURI.js +6 -0
  24. package/dist/node_modules/@apollo/client/link/http/selectURI.js.map +1 -0
  25. package/dist/node_modules/@apollo/client/link/utils/createOperation.js +34 -0
  26. package/dist/node_modules/@apollo/client/link/utils/createOperation.js.map +1 -0
  27. package/dist/node_modules/@apollo/client/link/utils/filterOperationVariables.js +14 -0
  28. package/dist/node_modules/@apollo/client/link/utils/filterOperationVariables.js.map +1 -0
  29. package/dist/node_modules/@apollo/client/utilities/caching/sizes.js +6 -0
  30. package/dist/node_modules/@apollo/client/utilities/caching/sizes.js.map +1 -0
  31. package/dist/node_modules/@apollo/client/utilities/graphql/print.js +16 -0
  32. package/dist/node_modules/@apollo/client/utilities/graphql/print.js.map +1 -0
  33. package/dist/node_modules/@apollo/client/utilities/internal/caches.js +19 -0
  34. package/dist/node_modules/@apollo/client/utilities/internal/caches.js.map +1 -0
  35. package/dist/node_modules/@apollo/client/utilities/internal/checkDocument.js +21 -0
  36. package/dist/node_modules/@apollo/client/utilities/internal/checkDocument.js.map +1 -0
  37. package/dist/node_modules/@apollo/client/utilities/internal/compact.js +14 -0
  38. package/dist/node_modules/@apollo/client/utilities/internal/compact.js.map +1 -0
  39. package/dist/node_modules/@apollo/client/utilities/internal/getMemoryInternals.js +9 -0
  40. package/dist/node_modules/@apollo/client/utilities/internal/getMemoryInternals.js.map +1 -0
  41. package/dist/node_modules/@apollo/client/utilities/internal/getOperationDefinition.js +9 -0
  42. package/dist/node_modules/@apollo/client/utilities/internal/getOperationDefinition.js.map +1 -0
  43. package/dist/node_modules/@apollo/client/utilities/internal/getOperationName.js +8 -0
  44. package/dist/node_modules/@apollo/client/utilities/internal/getOperationName.js.map +1 -0
  45. package/dist/node_modules/@apollo/client/utilities/internal/globals/global.js +9 -0
  46. package/dist/node_modules/@apollo/client/utilities/internal/globals/global.js.map +1 -0
  47. package/dist/node_modules/@apollo/client/utilities/internal/globals/maybe.js +10 -0
  48. package/dist/node_modules/@apollo/client/utilities/internal/globals/maybe.js.map +1 -0
  49. package/dist/node_modules/@apollo/client/utilities/internal/makeUniqueId.js +10 -0
  50. package/dist/node_modules/@apollo/client/utilities/internal/makeUniqueId.js.map +1 -0
  51. package/dist/node_modules/@apollo/client/utilities/internal/memoize.js +23 -0
  52. package/dist/node_modules/@apollo/client/utilities/internal/memoize.js.map +1 -0
  53. package/dist/node_modules/@apollo/client/utilities/internal/stringifyForDisplay.js +10 -0
  54. package/dist/node_modules/@apollo/client/utilities/internal/stringifyForDisplay.js.map +1 -0
  55. package/dist/node_modules/@apollo/client/utilities/invariant/index.js +57 -0
  56. package/dist/node_modules/@apollo/client/utilities/invariant/index.js.map +1 -0
  57. package/dist/node_modules/@apollo/client/version.js +6 -0
  58. package/dist/node_modules/@apollo/client/version.js.map +1 -0
  59. package/dist/node_modules/@wry/caches/lib/weak.js +67 -0
  60. package/dist/node_modules/@wry/caches/lib/weak.js.map +1 -0
  61. package/dist/node_modules/@wry/trie/lib/index.js +53 -0
  62. package/dist/node_modules/@wry/trie/lib/index.js.map +1 -0
  63. package/dist/node_modules/rxjs/dist/esm5/internal/NotificationFactories.js +21 -0
  64. package/dist/node_modules/rxjs/dist/esm5/internal/NotificationFactories.js.map +1 -0
  65. package/dist/node_modules/rxjs/dist/esm5/internal/Observable.js +78 -0
  66. package/dist/node_modules/rxjs/dist/esm5/internal/Observable.js.map +1 -0
  67. package/dist/node_modules/rxjs/dist/esm5/internal/Subscriber.js +117 -0
  68. package/dist/node_modules/rxjs/dist/esm5/internal/Subscriber.js.map +1 -0
  69. package/dist/node_modules/rxjs/dist/esm5/internal/Subscription.js +93 -0
  70. package/dist/node_modules/rxjs/dist/esm5/internal/Subscription.js.map +1 -0
  71. package/dist/node_modules/rxjs/dist/esm5/internal/config.js +12 -0
  72. package/dist/node_modules/rxjs/dist/esm5/internal/config.js.map +1 -0
  73. package/dist/node_modules/rxjs/dist/esm5/internal/observable/empty.js +9 -0
  74. package/dist/node_modules/rxjs/dist/esm5/internal/observable/empty.js.map +1 -0
  75. package/dist/node_modules/rxjs/dist/esm5/internal/observable/throwError.js +17 -0
  76. package/dist/node_modules/rxjs/dist/esm5/internal/observable/throwError.js.map +1 -0
  77. package/dist/node_modules/rxjs/dist/esm5/internal/scheduler/timeoutProvider.js +16 -0
  78. package/dist/node_modules/rxjs/dist/esm5/internal/scheduler/timeoutProvider.js.map +1 -0
  79. package/dist/node_modules/rxjs/dist/esm5/internal/symbol/observable.js +8 -0
  80. package/dist/node_modules/rxjs/dist/esm5/internal/symbol/observable.js.map +1 -0
  81. package/dist/node_modules/rxjs/dist/esm5/internal/util/UnsubscriptionError.js +13 -0
  82. package/dist/node_modules/rxjs/dist/esm5/internal/util/UnsubscriptionError.js.map +1 -0
  83. package/dist/node_modules/rxjs/dist/esm5/internal/util/arrRemove.js +11 -0
  84. package/dist/node_modules/rxjs/dist/esm5/internal/util/arrRemove.js.map +1 -0
  85. package/dist/node_modules/rxjs/dist/esm5/internal/util/createErrorClass.js +11 -0
  86. package/dist/node_modules/rxjs/dist/esm5/internal/util/createErrorClass.js.map +1 -0
  87. package/dist/node_modules/rxjs/dist/esm5/internal/util/errorContext.js +22 -0
  88. package/dist/node_modules/rxjs/dist/esm5/internal/util/errorContext.js.map +1 -0
  89. package/dist/node_modules/rxjs/dist/esm5/internal/util/identity.js +8 -0
  90. package/dist/node_modules/rxjs/dist/esm5/internal/util/identity.js.map +1 -0
  91. package/dist/node_modules/rxjs/dist/esm5/internal/util/isFunction.js +8 -0
  92. package/dist/node_modules/rxjs/dist/esm5/internal/util/isFunction.js.map +1 -0
  93. package/dist/node_modules/rxjs/dist/esm5/internal/util/noop.js +6 -0
  94. package/dist/node_modules/rxjs/dist/esm5/internal/util/noop.js.map +1 -0
  95. package/dist/node_modules/rxjs/dist/esm5/internal/util/pipe.js +13 -0
  96. package/dist/node_modules/rxjs/dist/esm5/internal/util/pipe.js.map +1 -0
  97. package/dist/node_modules/rxjs/dist/esm5/internal/util/reportUnhandledError.js +14 -0
  98. package/dist/node_modules/rxjs/dist/esm5/internal/util/reportUnhandledError.js.map +1 -0
  99. package/dist/node_modules/rxjs/node_modules/tslib/tslib.es6.js +52 -0
  100. package/dist/node_modules/rxjs/node_modules/tslib/tslib.es6.js.map +1 -0
  101. package/dist/pages/AgentsPage.js +242 -239
  102. package/dist/pages/AgentsPage.js.map +1 -1
  103. package/dist/pages/CatalogPage.js +146 -98
  104. package/dist/pages/CatalogPage.js.map +1 -1
  105. package/dist/pages/ComponentsPage.js +163 -117
  106. package/dist/pages/ComponentsPage.js.map +1 -1
  107. package/dist/pages/TemplatesPage.js +74 -26
  108. package/dist/pages/TemplatesPage.js.map +1 -1
  109. package/dist/providers/VibeControlsProvider.js +35 -30
  110. package/dist/providers/VibeControlsProvider.js.map +1 -1
  111. package/package.json +1 -1
@@ -11,7 +11,7 @@ import { VIBE_PERMISSIONS as c } from "../constants/permissions.js";
11
11
  import { PageSkeleton as l } from "../components/shared/PageSkeleton.js";
12
12
  import { TemplateCard as u } from "../components/templates/TemplateCard.js";
13
13
  import "../components/templates/index.js";
14
- import { useCallback as d, useMemo as f, useState as p } from "react";
14
+ import { useCallback as d, useEffect as f, useState as p } from "react";
15
15
  import { useI18n as m } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
16
16
  import { FileCode as h, RefreshCw as g } from "lucide-react";
17
17
  import { usePermissions as _ } from "@burdenoff/fe-libs/shared/providers/shell";
@@ -60,13 +60,20 @@ function w() {
60
60
  value: "CUSTOM",
61
61
  label: O("template.categoryCustom", "Custom")
62
62
  }
63
- ], [A, j] = p(""), [M, N] = p("all"), { data: P, loading: F, error: I, refetch: L } = e({
63
+ ], [A, j] = p(""), [M, N] = p("all"), [P, F] = p([]), [I, L] = p(!1), { data: R, loading: z, error: B, refetch: V, fetchMore: H } = e({
64
64
  variables: {
65
- filter: { category: M === "all" ? void 0 : M },
66
- ...A ? { pagination: { limit: 1e3 } } : {}
65
+ filter: {
66
+ category: M === "all" ? void 0 : M,
67
+ search: A || void 0
68
+ },
69
+ pagination: {
70
+ limit: 50,
71
+ offset: 0
72
+ }
67
73
  },
68
- fetchPolicy: "cache-and-network"
69
- }), R = f(() => (P?.vibecontrolsTemplates?.items ?? []).map((e) => ({
74
+ fetchPolicy: "cache-and-network",
75
+ notifyOnNetworkStatusChange: !0
76
+ }), U = d((e) => e.map((e) => ({
70
77
  id: e.id,
71
78
  name: e.name,
72
79
  description: e.description || null,
@@ -75,11 +82,43 @@ function w() {
75
82
  tags: e.tags || [],
76
83
  createdAt: e.createdAt,
77
84
  updatedAt: e.updatedAt
78
- })), [P?.vibecontrolsTemplates?.items]), z = f(() => {
79
- if (!A) return R;
80
- let e = A.toLowerCase();
81
- return R.filter((t) => t.name.toLowerCase().includes(e) || t.description?.toLowerCase().includes(e) || t.tags?.some((t) => t.toLowerCase().includes(e)));
82
- }, [R, A]), B = (e) => {
85
+ })), []);
86
+ f(() => {
87
+ F(U(R?.vibecontrolsTemplates?.items ?? []));
88
+ }, [R?.vibecontrolsTemplates?.items, U]);
89
+ let W = R?.vibecontrolsTemplates?.total ?? 0, G = P.length < W, K = P, q = P, J = d(async () => {
90
+ if (!(I || !G)) {
91
+ L(!0);
92
+ try {
93
+ await H({
94
+ variables: { pagination: {
95
+ limit: 50,
96
+ offset: P.length
97
+ } },
98
+ updateQuery: (e, { fetchMoreResult: t }) => {
99
+ if (!t?.vibecontrolsTemplates) return e;
100
+ let n = e.vibecontrolsTemplates, r = t.vibecontrolsTemplates;
101
+ return {
102
+ ...e,
103
+ vibecontrolsTemplates: {
104
+ ...r,
105
+ items: [...n?.items ?? [], ...r.items],
106
+ total: r.total,
107
+ hasMore: r.hasMore
108
+ }
109
+ };
110
+ }
111
+ });
112
+ } finally {
113
+ L(!1);
114
+ }
115
+ }
116
+ }, [
117
+ H,
118
+ P.length,
119
+ I,
120
+ G
121
+ ]), Y = (e) => {
83
122
  w(`/templates/${e.id}`);
84
123
  };
85
124
  return D ? /* @__PURE__ */ y("div", {
@@ -88,25 +127,25 @@ function w() {
88
127
  className: "text-text-secondary",
89
128
  children: "Loading permissions..."
90
129
  })
91
- }) : E(c.VIBE_LIST) ? I && R.length === 0 ? /* @__PURE__ */ y(C, {
130
+ }) : E(c.VIBE_LIST) ? B && K.length === 0 ? /* @__PURE__ */ y(C, {
92
131
  className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
93
132
  children: /* @__PURE__ */ y(r, {
94
133
  title: O("template.failedLoad", "Failed to load templates"),
95
- message: I.message,
96
- onRetry: () => L()
134
+ message: B.message,
135
+ onRetry: () => V()
97
136
  })
98
137
  }) : /* @__PURE__ */ b(C, {
99
138
  className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
100
139
  children: [
101
140
  /* @__PURE__ */ y(n, {
102
141
  title: O("template.title", "Templates"),
103
- description: O("template.description", "{{total}} templates available", { total: R.length }),
142
+ description: O("template.description", "{{total}} templates available", { total: K.length }),
104
143
  actions: /* @__PURE__ */ y("button", {
105
- onClick: () => L(),
106
- disabled: F,
144
+ onClick: () => V(),
145
+ disabled: z,
107
146
  className: "flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50",
108
147
  title: O("common.refresh", "Refresh"),
109
- children: /* @__PURE__ */ y(g, { className: `w-4 h-4 ${F ? "animate-spin" : ""}` })
148
+ children: /* @__PURE__ */ y(g, { className: `w-4 h-4 ${z ? "animate-spin" : ""}` })
110
149
  })
111
150
  }),
112
151
  /* @__PURE__ */ b("div", {
@@ -123,27 +162,27 @@ function w() {
123
162
  onChange: N
124
163
  })]
125
164
  }),
126
- (A || M !== "all") && /* @__PURE__ */ b("p", {
165
+ /* @__PURE__ */ b("p", {
127
166
  className: "text-sm text-text-secondary",
128
167
  children: [O("template.showingResults", "Showing {{filtered}} of {{total}} templates", {
129
- filtered: z.length,
130
- total: R.length
131
- }), F && /* @__PURE__ */ y("span", {
168
+ filtered: q.length,
169
+ total: W
170
+ }), z && /* @__PURE__ */ y("span", {
132
171
  className: "ml-2 text-text-muted",
133
172
  children: O("common.refreshing", "(refreshing...)")
134
173
  })]
135
174
  }),
136
- F && R.length === 0 ? /* @__PURE__ */ y(l, {
175
+ z && K.length === 0 ? /* @__PURE__ */ y(l, {
137
176
  variant: "cards",
138
177
  count: 6
139
- }) : z.length > 0 ? /* @__PURE__ */ y(v, {
178
+ }) : q.length > 0 ? /* @__PURE__ */ y(v, {
140
179
  mode: "wait",
141
180
  children: /* @__PURE__ */ y(S, {
142
181
  stagger: .04,
143
182
  className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4",
144
- children: z.map((e) => /* @__PURE__ */ y(x, { children: /* @__PURE__ */ y(u, {
183
+ children: q.map((e) => /* @__PURE__ */ y(x, { children: /* @__PURE__ */ y(u, {
145
184
  template: e,
146
- onOpen: B
185
+ onOpen: Y
147
186
  }) }, e.id))
148
187
  }, "template-grid")
149
188
  }) : /* @__PURE__ */ y(i, {
@@ -151,6 +190,15 @@ function w() {
151
190
  title: A || M !== "all" ? O("template.emptyNoResults", "No templates found") : O("template.emptyNoTemplates", "No templates yet"),
152
191
  description: A || M !== "all" ? O("template.emptyAdjustSearch", "Try adjusting your search or filters") : O("template.emptyCreate", "Templates will appear here when components are published as templates"),
153
192
  action: !A && M === "all" ? /* @__PURE__ */ y(s, { prompt: "What is a Template in VibeControls and how do I create one?" }) : void 0
193
+ }),
194
+ G && q.length > 0 && /* @__PURE__ */ y("div", {
195
+ className: "flex justify-center pt-2",
196
+ children: /* @__PURE__ */ y("button", {
197
+ onClick: J,
198
+ disabled: I,
199
+ className: "flex items-center gap-2 px-4 py-2 bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken-hover transition-colors disabled:opacity-50",
200
+ children: I ? O("common.loading", "Loading...") : O("common.loadMore", "Load more")
201
+ })
154
202
  })
155
203
  ]
156
204
  }) : /* @__PURE__ */ y("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"TemplatesPage.js","names":[],"sources":["../../src/pages/TemplatesPage.tsx"],"sourcesContent":["import { useState, useMemo, useCallback } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { FileCode, RefreshCw } from 'lucide-react';\nimport { TemplateCard } from '@/components/templates';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport type { TemplateCardData } from '@/components/templates';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n ErrorState,\n GetHelpButton,\n} from '@/components/shared';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { AnimatePresence } from 'framer-motion';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useListTemplatesQuery } from '@/generated/wspace-operations';\n\nexport function TemplatesPage() {\n const navigate = useVibeNavigate();\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const tr = useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n if (!params) return template;\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n },\n [t]\n );\n\n const categoryOptions = [\n { value: 'all', label: tr('template.allCategories', 'All Categories') },\n { value: 'STARTER', label: tr('template.categoryStarter', 'Starter') },\n { value: 'MICROSERVICE', label: tr('template.categoryMicroservice', 'Microservice') },\n { value: 'FRONTEND', label: tr('template.categoryFrontend', 'Frontend') },\n { value: 'BACKEND', label: tr('template.categoryBackend', 'Backend') },\n { value: 'FULLSTACK', label: tr('template.categoryFullstack', 'Fullstack') },\n { value: 'INFRASTRUCTURE', label: tr('template.categoryInfrastructure', 'Infrastructure') },\n { value: 'LIBRARY', label: tr('template.categoryLibrary', 'Library') },\n { value: 'CUSTOM', label: tr('template.categoryCustom', 'Custom') },\n ];\n\n const [searchQuery, setSearchQuery] = useState('');\n const [categoryFilter, setCategoryFilter] = useState('all');\n\n const { data, loading, error, refetch } = useListTemplatesQuery({\n variables: {\n filter: {\n category: categoryFilter !== 'all' ? categoryFilter : undefined,\n },\n ...(searchQuery ? { pagination: { limit: 1000 } } : {}),\n },\n fetchPolicy: 'cache-and-network',\n });\n\n const templates: TemplateCardData[] = useMemo(() => {\n const items = data?.vibecontrolsTemplates?.items ?? [];\n return items.map((item: Record<string, unknown>) => ({\n id: item.id as string,\n name: item.name as string,\n description: (item.description as string) || null,\n category: (item.category as string) || null,\n cloneCount: (item.cloneCount as number) || 0,\n tags: (item.tags as string[]) || [],\n createdAt: item.createdAt as string,\n updatedAt: item.updatedAt as string,\n }));\n }, [data?.vibecontrolsTemplates?.items]);\n\n const filteredTemplates = useMemo(() => {\n if (!searchQuery) return templates;\n const query = searchQuery.toLowerCase();\n return templates.filter(\n (tmpl) =>\n tmpl.name.toLowerCase().includes(query) ||\n tmpl.description?.toLowerCase().includes(query) ||\n tmpl.tags?.some((tag) => tag.toLowerCase().includes(query))\n );\n }, [templates, searchQuery]);\n\n const handleTemplateSelect = (tmpl: TemplateCardData) => {\n navigate(`/templates/${tmpl.id}`);\n };\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions...</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.VIBE_LIST))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n if (error && templates.length === 0) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <ErrorState\n title={tr('template.failedLoad', 'Failed to load templates')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <PageHeader\n title={tr('template.title', 'Templates')}\n description={tr('template.description', '{{total}} templates available', {\n total: templates.length,\n })}\n actions={\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n }\n />\n\n {/* Filters */}\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('template.searchPlaceholder', 'Search templates...')}\n className=\"flex-1 sm:w-64 sm:flex-none\"\n />\n <FilterDropdown\n label={tr('template.filterCategory', 'Category')}\n value={categoryFilter}\n options={categoryOptions}\n onChange={setCategoryFilter}\n />\n </div>\n\n {/* Results count */}\n {(searchQuery || categoryFilter !== 'all') && (\n <p className=\"text-sm text-text-secondary\">\n {tr('template.showingResults', 'Showing {{filtered}} of {{total}} templates', {\n filtered: filteredTemplates.length,\n total: templates.length,\n })}\n {loading && (\n <span className=\"ml-2 text-text-muted\">\n {tr('common.refreshing', '(refreshing...)')}\n </span>\n )}\n </p>\n )}\n\n {/* Grid */}\n {loading && templates.length === 0 ? (\n <PageSkeleton variant=\"cards\" count={6} />\n ) : filteredTemplates.length > 0 ? (\n <AnimatePresence mode=\"wait\">\n <MotionList\n key=\"template-grid\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {filteredTemplates.map((tmpl) => (\n <MotionItem key={tmpl.id}>\n <TemplateCard template={tmpl} onOpen={handleTemplateSelect} />\n </MotionItem>\n ))}\n </MotionList>\n </AnimatePresence>\n ) : (\n <EmptyState\n icon={<FileCode className=\"w-8 h-8 text-text-secondary\" />}\n title={\n searchQuery || categoryFilter !== 'all'\n ? tr('template.emptyNoResults', 'No templates found')\n : tr('template.emptyNoTemplates', 'No templates yet')\n }\n description={\n searchQuery || categoryFilter !== 'all'\n ? tr('template.emptyAdjustSearch', 'Try adjusting your search or filters')\n : tr(\n 'template.emptyCreate',\n 'Templates will appear here when components are published as templates'\n )\n }\n action={\n !searchQuery && categoryFilter === 'all' ? (\n <GetHelpButton prompt=\"What is a Template in VibeControls and how do I create one?\" />\n ) : undefined\n }\n />\n )}\n </MotionPage>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TemplatesPage.js","names":[],"sources":["../../src/pages/TemplatesPage.tsx"],"sourcesContent":["import { useState, useCallback, useEffect } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { FileCode, RefreshCw } from 'lucide-react';\nimport { TemplateCard } from '@/components/templates';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport type { TemplateCardData } from '@/components/templates';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n ErrorState,\n GetHelpButton,\n} from '@/components/shared';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { AnimatePresence } from 'framer-motion';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useListTemplatesQuery } from '@/generated/wspace-operations';\n\nexport function TemplatesPage() {\n const navigate = useVibeNavigate();\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const tr = useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n if (!params) return template;\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n },\n [t]\n );\n\n const categoryOptions = [\n { value: 'all', label: tr('template.allCategories', 'All Categories') },\n { value: 'STARTER', label: tr('template.categoryStarter', 'Starter') },\n { value: 'MICROSERVICE', label: tr('template.categoryMicroservice', 'Microservice') },\n { value: 'FRONTEND', label: tr('template.categoryFrontend', 'Frontend') },\n { value: 'BACKEND', label: tr('template.categoryBackend', 'Backend') },\n { value: 'FULLSTACK', label: tr('template.categoryFullstack', 'Fullstack') },\n { value: 'INFRASTRUCTURE', label: tr('template.categoryInfrastructure', 'Infrastructure') },\n { value: 'LIBRARY', label: tr('template.categoryLibrary', 'Library') },\n { value: 'CUSTOM', label: tr('template.categoryCustom', 'Custom') },\n ];\n\n const PAGE_SIZE = 50;\n const [searchQuery, setSearchQuery] = useState('');\n const [categoryFilter, setCategoryFilter] = useState('all');\n const [accumulated, setAccumulated] = useState<TemplateCardData[]>([]);\n const [loadingMore, setLoadingMore] = useState(false);\n\n const { data, loading, error, refetch, fetchMore } = useListTemplatesQuery({\n variables: {\n filter: {\n category: categoryFilter !== 'all' ? categoryFilter : undefined,\n search: searchQuery || undefined,\n },\n pagination: { limit: PAGE_SIZE, offset: 0 },\n },\n fetchPolicy: 'cache-and-network',\n notifyOnNetworkStatusChange: true,\n });\n\n const mapItems = useCallback(\n (items: ReadonlyArray<Record<string, unknown>>): TemplateCardData[] =>\n items.map((item) => ({\n id: item.id as string,\n name: item.name as string,\n description: (item.description as string) || null,\n category: (item.category as string) || null,\n cloneCount: (item.cloneCount as number) || 0,\n tags: (item.tags as string[]) || [],\n createdAt: item.createdAt as string,\n updatedAt: item.updatedAt as string,\n })),\n []\n );\n\n useEffect(() => {\n const items = data?.vibecontrolsTemplates?.items ?? [];\n setAccumulated(mapItems(items as ReadonlyArray<Record<string, unknown>>));\n }, [data?.vibecontrolsTemplates?.items, mapItems]);\n\n const total = data?.vibecontrolsTemplates?.total ?? 0;\n const hasMore = accumulated.length < total;\n const templates = accumulated;\n const filteredTemplates = accumulated;\n\n const handleLoadMore = useCallback(async () => {\n if (loadingMore || !hasMore) return;\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { pagination: { limit: PAGE_SIZE, offset: accumulated.length } },\n updateQuery: (prev, { fetchMoreResult }) => {\n if (!fetchMoreResult?.vibecontrolsTemplates) return prev;\n const prevList = prev.vibecontrolsTemplates;\n const nextList = fetchMoreResult.vibecontrolsTemplates;\n return {\n ...prev,\n vibecontrolsTemplates: {\n ...nextList,\n items: [...(prevList?.items ?? []), ...nextList.items],\n total: nextList.total,\n hasMore: nextList.hasMore,\n },\n };\n },\n });\n } finally {\n setLoadingMore(false);\n }\n }, [fetchMore, accumulated.length, loadingMore, hasMore]);\n\n const handleTemplateSelect = (tmpl: TemplateCardData) => {\n navigate(`/templates/${tmpl.id}`);\n };\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions...</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.VIBE_LIST))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n if (error && templates.length === 0) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <ErrorState\n title={tr('template.failedLoad', 'Failed to load templates')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <PageHeader\n title={tr('template.title', 'Templates')}\n description={tr('template.description', '{{total}} templates available', {\n total: templates.length,\n })}\n actions={\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n }\n />\n\n {/* Filters */}\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('template.searchPlaceholder', 'Search templates...')}\n className=\"flex-1 sm:w-64 sm:flex-none\"\n />\n <FilterDropdown\n label={tr('template.filterCategory', 'Category')}\n value={categoryFilter}\n options={categoryOptions}\n onChange={setCategoryFilter}\n />\n </div>\n\n {/* Results count */}\n <p className=\"text-sm text-text-secondary\">\n {tr('template.showingResults', 'Showing {{filtered}} of {{total}} templates', {\n filtered: filteredTemplates.length,\n total,\n })}\n {loading && (\n <span className=\"ml-2 text-text-muted\">{tr('common.refreshing', '(refreshing...)')}</span>\n )}\n </p>\n\n {/* Grid */}\n {loading && templates.length === 0 ? (\n <PageSkeleton variant=\"cards\" count={6} />\n ) : filteredTemplates.length > 0 ? (\n <AnimatePresence mode=\"wait\">\n <MotionList\n key=\"template-grid\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {filteredTemplates.map((tmpl) => (\n <MotionItem key={tmpl.id}>\n <TemplateCard template={tmpl} onOpen={handleTemplateSelect} />\n </MotionItem>\n ))}\n </MotionList>\n </AnimatePresence>\n ) : (\n <EmptyState\n icon={<FileCode className=\"w-8 h-8 text-text-secondary\" />}\n title={\n searchQuery || categoryFilter !== 'all'\n ? tr('template.emptyNoResults', 'No templates found')\n : tr('template.emptyNoTemplates', 'No templates yet')\n }\n description={\n searchQuery || categoryFilter !== 'all'\n ? tr('template.emptyAdjustSearch', 'Try adjusting your search or filters')\n : tr(\n 'template.emptyCreate',\n 'Templates will appear here when components are published as templates'\n )\n }\n action={\n !searchQuery && categoryFilter === 'all' ? (\n <GetHelpButton prompt=\"What is a Template in VibeControls and how do I create one?\" />\n ) : undefined\n }\n />\n )}\n\n {/* Load more */}\n {hasMore && filteredTemplates.length > 0 && (\n <div className=\"flex justify-center pt-2\">\n <button\n onClick={handleLoadMore}\n disabled={loadingMore}\n className=\"flex items-center gap-2 px-4 py-2 bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken-hover transition-colors disabled:opacity-50\"\n >\n {loadingMore\n ? tr('common.loading', 'Loading...')\n : tr('common.loadMore', 'Load more')}\n </button>\n </div>\n )}\n </MotionPage>\n );\n}\n"],"mappings":"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"}
@@ -1,9 +1,10 @@
1
1
  import { CombinedGraphQLErrors as e } from "../node_modules/@apollo/client/errors/CombinedGraphQLErrors.js";
2
- import t from "./AgentEventsProvider.js";
3
- import n from "../hooks/useInitialDataLoader.js";
4
- import { createContext as r, useCallback as i, useContext as a, useEffect as o, useMemo as s, useRef as c, useState as l } from "react";
5
- import { MemoryRouter as u, useNavigate as d } from "react-router-dom";
6
- import { ApolloClient as f, HttpLink as p, InMemoryCache as m, from as h } from "@apollo/client";
2
+ import { BatchHttpLink as t } from "../node_modules/@apollo/client/link/batch-http/batchHttpLink.js";
3
+ import n from "./AgentEventsProvider.js";
4
+ import r from "../hooks/useInitialDataLoader.js";
5
+ import { createContext as i, useCallback as a, useContext as o, useEffect as s, useMemo as c, useRef as l, useState as u } from "react";
6
+ import { MemoryRouter as d, useNavigate as f } from "react-router-dom";
7
+ import { ApolloClient as p, InMemoryCache as m, from as h } from "@apollo/client";
7
8
  import { onError as g } from "@apollo/client/link/error";
8
9
  import { setContext as _ } from "@apollo/client/link/context";
9
10
  import { ApolloProvider as v } from "@apollo/client/react";
@@ -20,13 +21,13 @@ function O(e = []) {
20
21
  return t === "UNAUTHENTICATED" || n === "TOKEN_EXPIRED" || r.includes("token_expired") || r.includes("token expired") || r.includes("session expired") || r.includes("unauthorized") || r.includes("unauthenticated") || r.includes("authentication required") || r.includes("workspace context missing");
21
22
  });
22
23
  }
23
- var k = ({ workspaceId: e, children: t }) => (n({ workspaceId: e }), /* @__PURE__ */ D(E, { children: t })), A = r(null), j = () => {
24
- let e = a(A);
24
+ var k = ({ workspaceId: e, children: t }) => (r({ workspaceId: e }), /* @__PURE__ */ D(E, { children: t })), A = i(null), j = () => {
25
+ let e = o(A);
25
26
  if (!e) throw Error("useVibeControls must be used within VibeControlsProvider");
26
27
  return e;
27
28
  }, M = () => {
28
- let { navigate: e, basePath: t } = j(), n = d();
29
- return i((r) => {
29
+ let { navigate: e, basePath: t } = j(), n = f();
30
+ return a((r) => {
30
31
  let i = t && t !== "/" ? `${t}${r}` : r;
31
32
  e ? e(i) : n(i);
32
33
  }, [
@@ -34,21 +35,21 @@ var k = ({ workspaceId: e, children: t }) => (n({ workspaceId: e }), /* @__PURE_
34
35
  t,
35
36
  n
36
37
  ]);
37
- }, N = ({ children: n, basePath: r, navigate: i, currentUser: a, workspaceId: d, tenantId: E, apiGatewayUrl: j, authToken: M, workspaceToken: N, onAgentSelect: P, onSessionStart: F, onVibeActivate: I, defaultView: L, registerNavItems: R, registerFooterItems: z, onAuthError: B }) => {
38
- let [V] = l(() => new y({ defaultOptions: { queries: {
38
+ }, N = ({ children: r, basePath: i, navigate: a, currentUser: o, workspaceId: f, tenantId: E, apiGatewayUrl: j, authToken: M, workspaceToken: N, onAgentSelect: P, onSessionStart: F, onVibeActivate: I, defaultView: L, registerNavItems: R, registerFooterItems: z, onAuthError: B }) => {
39
+ let [V] = u(() => new y({ defaultOptions: { queries: {
39
40
  staleTime: 300 * 1e3,
40
41
  retry: 2
41
- } } })), H = c(M), U = c(N), W = c(d), G = c(B);
42
- o(() => {
42
+ } } })), H = l(M), U = l(N), W = l(f), G = l(B);
43
+ s(() => {
43
44
  H.current = M;
44
- }, [M]), o(() => {
45
+ }, [M]), s(() => {
45
46
  U.current = N;
46
- }, [N]), o(() => {
47
- W.current = d;
48
- }, [d]), o(() => {
47
+ }, [N]), s(() => {
48
+ W.current = f;
49
+ }, [f]), s(() => {
49
50
  G.current = B;
50
51
  }, [B]);
51
- let K = s(() => new f({
52
+ let K = c(() => new p({
52
53
  link: h([
53
54
  g(({ error: t }) => {
54
55
  if (e.is(t) && O(t.errors)) {
@@ -66,7 +67,11 @@ var k = ({ workspaceId: e, children: t }) => (n({ workspaceId: e }), /* @__PURE_
66
67
  ...i ? { "x-workspace-id": i } : {}
67
68
  } };
68
69
  }),
69
- new p({ uri: j ? `${j}/workspaces/graphql` : "/workspaces/graphql" })
70
+ new t({
71
+ uri: j ? `${j}/workspaces/graphql` : "/workspaces/graphql",
72
+ batchMax: 10,
73
+ batchInterval: 10
74
+ })
70
75
  ]),
71
76
  cache: new m(),
72
77
  defaultOptions: {
@@ -78,10 +83,10 @@ var k = ({ workspaceId: e, children: t }) => (n({ workspaceId: e }), /* @__PURE_
78
83
  mutate: { errorPolicy: "all" }
79
84
  }
80
85
  }), [j]), q = {
81
- basePath: r,
82
- navigate: i,
83
- currentUser: a,
84
- workspaceId: d,
86
+ basePath: i,
87
+ navigate: a,
88
+ currentUser: o,
89
+ workspaceId: f,
85
90
  tenantId: E,
86
91
  apiGatewayUrl: j,
87
92
  authToken: M,
@@ -93,21 +98,21 @@ var k = ({ workspaceId: e, children: t }) => (n({ workspaceId: e }), /* @__PURE_
93
98
  registerNavItems: R,
94
99
  registerFooterItems: z,
95
100
  onAuthError: B
96
- }, J = typeof window < "u" && /^\/vibecontrols\/(share|deck)\//.test(window.location.pathname) ? n : /* @__PURE__ */ D(k, {
97
- workspaceId: d,
101
+ }, J = typeof window < "u" && /^\/vibecontrols\/(share|deck)\//.test(window.location.pathname) ? r : /* @__PURE__ */ D(k, {
102
+ workspaceId: f,
98
103
  children: /* @__PURE__ */ D(w, {
99
- scopeId: d ?? null,
104
+ scopeId: f ?? null,
100
105
  gateway: "workspace",
101
- children: /* @__PURE__ */ D(t, {
106
+ children: /* @__PURE__ */ D(n, {
102
107
  toastPosition: "bottom-right",
103
108
  autoDismissDelay: 5e3,
104
109
  maxVisibleToasts: 3,
105
- children: n
110
+ children: r
106
111
  })
107
112
  })
108
113
  }), Y = /* @__PURE__ */ D(b, {
109
114
  client: V,
110
- children: r ? /* @__PURE__ */ D(A.Provider, {
115
+ children: i ? /* @__PURE__ */ D(A.Provider, {
111
116
  value: q,
112
117
  children: J
113
118
  }) : /* @__PURE__ */ D(v, {
@@ -118,7 +123,7 @@ var k = ({ workspaceId: e, children: t }) => (n({ workspaceId: e }), /* @__PURE_
118
123
  })
119
124
  })
120
125
  });
121
- return r ? Y : /* @__PURE__ */ D(u, { children: Y });
126
+ return i ? Y : /* @__PURE__ */ D(d, { children: Y });
122
127
  };
123
128
  //#endregion
124
129
  export { N as VibeControlsProvider, j as useVibeControls, M as useVibeNavigate };
@@ -1 +1 @@
1
- {"version":3,"file":"VibeControlsProvider.js","names":[],"sources":["../../src/providers/VibeControlsProvider.tsx"],"sourcesContent":["import type { FC, PropsWithChildren } from 'react';\nimport {\n createContext,\n useContext,\n useMemo,\n useCallback,\n useRef,\n useEffect,\n useState,\n} from 'react';\nimport { MemoryRouter, useNavigate as useRouterNavigate } from 'react-router-dom';\nimport { ApolloClient, InMemoryCache, HttpLink, from } from '@apollo/client';\nimport { onError } from '@apollo/client/link/error';\nimport { CombinedGraphQLErrors } from '@apollo/client/errors';\nimport { setContext } from '@apollo/client/link/context';\nimport { ApolloProvider } from '@apollo/client/react';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport { getStoredAuthToken, getStoredWorkspaceToken } from '@burdenoff/fe-libs/shared/graphql';\nimport { getStoredWorkspaceId } from '@burdenoff/fe-libs/shared/graphql/fetch';\nimport { PermissionProvider } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { isAuthNetworkError } from '@burdenoff/fe-libs/shared/utils';\nimport type { VibeControlsRootProps } from '../types';\nimport { AgentEventsProvider } from './AgentEventsProvider';\nimport { useInitialDataLoader } from '../hooks/useInitialDataLoader';\n\nfunction hasSessionAuthErrors(\n errors: ReadonlyArray<{ message?: string; extensions?: Record<string, unknown> }> = []\n): boolean {\n return errors.some((error) => {\n const code = typeof error.extensions?.code === 'string' ? error.extensions.code : '';\n const errorCode =\n typeof error.extensions?.errorCode === 'string' ? error.extensions.errorCode : '';\n const message = (error.message ?? '').toLowerCase();\n\n return (\n code === 'UNAUTHENTICATED' ||\n errorCode === 'TOKEN_EXPIRED' ||\n message.includes('token_expired') ||\n message.includes('token expired') ||\n message.includes('session expired') ||\n message.includes('unauthorized') ||\n message.includes('unauthenticated') ||\n message.includes('authentication required') ||\n message.includes('workspace context missing')\n );\n });\n}\n\n/**\n * Runs the initial data query inside the ApolloProvider tree so the result\n * lands in the MFE's own Apollo cache (not the shell's).\n */\nconst InitialDataPrefetcher: FC<PropsWithChildren<{ workspaceId?: string | null }>> = ({\n workspaceId,\n children,\n}) => {\n useInitialDataLoader({ workspaceId });\n return <>{children}</>;\n};\n\n/**\n * Context value for VibeControls microfrontend\n */\ntype VibeControlsContextValue = VibeControlsRootProps;\n\nconst VibeControlsContext = createContext<VibeControlsContextValue | null>(null);\n\n/**\n * Hook to access VibeControls context\n */\nexport const useVibeControls = () => {\n const context = useContext(VibeControlsContext);\n if (!context) {\n throw new Error('useVibeControls must be used within VibeControlsProvider');\n }\n return context;\n};\n\n/**\n * Hook for navigation within the VibeControls microfrontend.\n * Uses the shell's navigate function if available, falling back to\n * React Router's useNavigate. Automatically prepends basePath.\n *\n * Usage:\n * const navigate = useVibeNavigate();\n * navigate('/agents/123'); // navigates to /vibecontrols/agents/123\n */\nexport const useVibeNavigate = () => {\n const { navigate: shellNavigate, basePath } = useVibeControls();\n const routerNavigate = useRouterNavigate();\n\n return useCallback(\n (path: string) => {\n const fullPath = basePath && basePath !== '/' ? `${basePath}${path}` : path;\n if (shellNavigate) {\n shellNavigate(fullPath);\n } else {\n routerNavigate(fullPath);\n }\n },\n [shellNavigate, basePath, routerNavigate]\n );\n};\n\n/**\n * Provider component for VibeControls microfrontend\n * Sets up routing, Apollo Client with auth error detection, and context.\n *\n * The Apollo Client is configured with:\n * - An onError link that detects UNAUTHENTICATED / FORBIDDEN responses and\n * invokes the `onAuthError` prop so the host shell can trigger logout.\n * - A dynamic auth link that reads the token per-request (not baked in at\n * client creation time), so token refreshes take effect immediately.\n */\nexport const VibeControlsProvider: FC<PropsWithChildren<VibeControlsRootProps>> = ({\n children,\n basePath,\n navigate,\n currentUser,\n workspaceId,\n tenantId,\n apiGatewayUrl,\n authToken,\n workspaceToken,\n onAgentSelect,\n onSessionStart,\n onVibeActivate,\n defaultView,\n registerNavItems,\n registerFooterItems,\n onAuthError,\n}) => {\n // Per-instance QueryClient so multiple MFE mounts don't share cache\n const [queryClient] = useState(\n () =>\n new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 5 * 60 * 1000, // 5 minutes\n retry: 2,\n },\n },\n })\n );\n\n // Store tokens and callbacks in refs so the Apollo Client (and its cache)\n // survives token refreshes. The setContext auth link reads from refs\n // per-request, so refreshed tokens take effect immediately without\n // destroying the InMemoryCache.\n const authTokenRef = useRef(authToken);\n const workspaceTokenRef = useRef(workspaceToken);\n const workspaceIdRef = useRef(workspaceId);\n const onAuthErrorRef = useRef(onAuthError);\n useEffect(() => {\n authTokenRef.current = authToken;\n }, [authToken]);\n useEffect(() => {\n workspaceTokenRef.current = workspaceToken;\n }, [workspaceToken]);\n useEffect(() => {\n workspaceIdRef.current = workspaceId;\n }, [workspaceId]);\n useEffect(() => {\n onAuthErrorRef.current = onAuthError;\n }, [onAuthError]);\n\n // Build Apollo Client with error link + dynamic auth link.\n // Only recreated when apiGatewayUrl changes (different endpoint = different client).\n const apolloClient = useMemo(() => {\n // Error link (Apollo v4 pattern): detect authentication failures\n const errorLink = onError(({ error }) => {\n if (CombinedGraphQLErrors.is(error) && hasSessionAuthErrors(error.errors)) {\n console.warn('[VibeControls] Auth error detected — notifying host shell');\n onAuthErrorRef.current?.();\n return;\n }\n\n if (error instanceof Error && isAuthNetworkError(error)) {\n console.warn('[VibeControls] Auth network error — notifying host shell');\n onAuthErrorRef.current?.();\n }\n });\n\n // Auth link: dynamically attach dual-token auth and workspace context per-request.\n // Reads from refs so token refreshes don't recreate the client.\n const authLink = setContext((_, { headers }) => {\n const resolvedAuthToken = authTokenRef.current ?? getStoredAuthToken();\n const resolvedWorkspaceToken = workspaceTokenRef.current ?? getStoredWorkspaceToken();\n // Fall back to storage when workspaceId prop is empty (e.g. during initial render\n // before ActiveContextProvider has propagated the value through workspaceBaseProps)\n const resolvedWorkspaceId = workspaceIdRef.current || getStoredWorkspaceId();\n\n return {\n headers: {\n ...headers,\n ...(resolvedAuthToken ? { Authorization: `Bearer ${resolvedAuthToken}` } : {}),\n ...(resolvedWorkspaceToken\n ? { 'X-Workspace-Authorization': `Bearer ${resolvedWorkspaceToken}` }\n : {}),\n ...(resolvedWorkspaceId ? { 'x-workspace-id': resolvedWorkspaceId } : {}),\n },\n };\n });\n\n const httpLink = new HttpLink({\n uri: apiGatewayUrl ? `${apiGatewayUrl}/workspaces/graphql` : '/workspaces/graphql',\n });\n\n return new ApolloClient({\n link: from([errorLink, authLink, httpLink]),\n cache: new InMemoryCache(),\n defaultOptions: {\n watchQuery: { fetchPolicy: 'cache-first', errorPolicy: 'all' },\n query: { errorPolicy: 'all' },\n mutate: { errorPolicy: 'all' },\n },\n });\n }, [apiGatewayUrl]);\n\n const contextValue: VibeControlsContextValue = {\n basePath,\n navigate,\n currentUser,\n workspaceId,\n tenantId,\n apiGatewayUrl,\n authToken,\n workspaceToken,\n onAgentSelect,\n onSessionStart,\n onVibeActivate,\n defaultView,\n registerNavItems,\n registerFooterItems,\n onAuthError,\n };\n\n const isPublicLinkRoute =\n typeof window !== 'undefined' &&\n /^\\/vibecontrols\\/(share|deck)\\//.test(window.location.pathname);\n\n // Core providers that wrap the children\n // PermissionProvider requires AuthProvider which is provided by the shell.\n // It fetches permissions from wspace-rbac-svc via the workspace gateway.\n // AgentEventsProvider manages WebSocket connections to agents for real-time notifications.\n const routedChildren = isPublicLinkRoute ? (\n children\n ) : (\n <InitialDataPrefetcher workspaceId={workspaceId}>\n <PermissionProvider scopeId={workspaceId ?? null} gateway=\"workspace\">\n <AgentEventsProvider\n toastPosition=\"bottom-right\"\n autoDismissDelay={5000}\n maxVisibleToasts={3}\n >\n {children}\n </AgentEventsProvider>\n </PermissionProvider>\n </InitialDataPrefetcher>\n );\n\n const coreProviders = (\n <QueryClientProvider client={queryClient}>\n {basePath ? (\n <VibeControlsContext.Provider value={contextValue}>\n {routedChildren}\n </VibeControlsContext.Provider>\n ) : (\n <ApolloProvider client={apolloClient}>\n <VibeControlsContext.Provider value={contextValue}>\n {routedChildren}\n </VibeControlsContext.Provider>\n </ApolloProvider>\n )}\n </QueryClientProvider>\n );\n\n // When integrated into a shell (basePath provided), the shell already has a router\n // so we don't create another one. When standalone (no basePath), use MemoryRouter.\n if (basePath) {\n // Integrated mode: shell already has BrowserRouter, don't create nested router\n return coreProviders;\n }\n\n // Standalone mode: create MemoryRouter for independent operation\n return <MemoryRouter>{coreProviders}</MemoryRouter>;\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAyBA,SAAS,EACP,IAAoF,EAAE,EAC7E;AACT,QAAO,EAAO,MAAM,MAAU;EAC5B,IAAM,IAAO,OAAO,EAAM,YAAY,QAAS,WAAW,EAAM,WAAW,OAAO,IAC5E,IACJ,OAAO,EAAM,YAAY,aAAc,WAAW,EAAM,WAAW,YAAY,IAC3E,KAAW,EAAM,WAAW,IAAI,aAAa;AAEnD,SACE,MAAS,qBACT,MAAc,mBACd,EAAQ,SAAS,gBAAgB,IACjC,EAAQ,SAAS,gBAAgB,IACjC,EAAQ,SAAS,kBAAkB,IACnC,EAAQ,SAAS,eAAe,IAChC,EAAQ,SAAS,kBAAkB,IACnC,EAAQ,SAAS,0BAA0B,IAC3C,EAAQ,SAAS,4BAA4B;GAE/C;;AAOJ,IAAM,KAAiF,EACrF,gBACA,mBAEA,EAAqB,EAAE,gBAAa,CAAC,EAC9B,kBAAA,GAAA,EAAG,aAAY,CAAA,GAQlB,IAAsB,EAA+C,KAAK,EAKnE,UAAwB;CACnC,IAAM,IAAU,EAAW,EAAoB;AAC/C,KAAI,CAAC,EACH,OAAU,MAAM,2DAA2D;AAE7E,QAAO;GAYI,UAAwB;CACnC,IAAM,EAAE,UAAU,GAAe,gBAAa,GAAiB,EACzD,IAAiB,GAAmB;AAE1C,QAAO,GACJ,MAAiB;EAChB,IAAM,IAAW,KAAY,MAAa,MAAM,GAAG,IAAW,MAAS;AACvE,EAAI,IACF,EAAc,EAAS,GAEvB,EAAe,EAAS;IAG5B;EAAC;EAAe;EAAU;EAAe,CAC1C;GAaU,KAAsE,EACjF,aACA,aACA,aACA,gBACA,gBACA,aACA,kBACA,cACA,mBACA,kBACA,mBACA,mBACA,gBACA,qBACA,wBACA,qBACI;CAEJ,IAAM,CAAC,KAAe,QAElB,IAAI,EAAY,EACd,gBAAgB,EACd,SAAS;EACP,WAAW,MAAS;EACpB,OAAO;EACR,EACF,EACF,CAAC,CACL,EAMK,IAAe,EAAO,EAAU,EAChC,IAAoB,EAAO,EAAe,EAC1C,IAAiB,EAAO,EAAY,EACpC,IAAiB,EAAO,EAAY;AAU1C,CATA,QAAgB;AACd,IAAa,UAAU;IACtB,CAAC,EAAU,CAAC,EACf,QAAgB;AACd,IAAkB,UAAU;IAC3B,CAAC,EAAe,CAAC,EACpB,QAAgB;AACd,IAAe,UAAU;IACxB,CAAC,EAAY,CAAC,EACjB,QAAgB;AACd,IAAe,UAAU;IACxB,CAAC,EAAY,CAAC;CAIjB,IAAM,IAAe,QAwCZ,IAAI,EAAa;EACtB,MAAM,EAAK;GAvCK,GAAS,EAAE,eAAY;AACvC,QAAI,EAAsB,GAAG,EAAM,IAAI,EAAqB,EAAM,OAAO,EAAE;AAEzE,KADA,QAAQ,KAAK,4DAA4D,EACzE,EAAe,WAAW;AAC1B;;AAGF,IAAI,aAAiB,SAAS,EAAmB,EAAM,KACrD,QAAQ,KAAK,2DAA2D,EACxE,EAAe,WAAW;KAE5B;GAIe,GAAY,GAAG,EAAE,iBAAc;IAC9C,IAAM,IAAoB,EAAa,WAAW,GAAoB,EAChE,IAAyB,EAAkB,WAAW,GAAyB,EAG/E,IAAsB,EAAe,WAAW,GAAsB;AAE5E,WAAO,EACL,SAAS;KACP,GAAG;KACH,GAAI,IAAoB,EAAE,eAAe,UAAU,KAAqB,GAAG,EAAE;KAC7E,GAAI,IACA,EAAE,6BAA6B,UAAU,KAA0B,GACnE,EAAE;KACN,GAAI,IAAsB,EAAE,kBAAkB,GAAqB,GAAG,EAAE;KACzE,EACF;KACD;GAEe,IAAI,EAAS,EAC5B,KAAK,IAAgB,GAAG,EAAc,uBAAuB,uBAC9D,CAAC;GAG0C,CAAC;EAC3C,OAAO,IAAI,GAAe;EAC1B,gBAAgB;GACd,YAAY;IAAE,aAAa;IAAe,aAAa;IAAO;GAC9D,OAAO,EAAE,aAAa,OAAO;GAC7B,QAAQ,EAAE,aAAa,OAAO;GAC/B;EACF,CAAC,EACD,CAAC,EAAc,CAAC,EAEb,IAAyC;EAC7C;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,EAUK,IAPJ,OAAO,SAAW,OAClB,kCAAkC,KAAK,OAAO,SAAS,SAAS,GAOhE,IAEA,kBAAC,GAAD;EAAoC;YAClC,kBAAC,GAAD;GAAoB,SAAS,KAAe;GAAM,SAAQ;aACxD,kBAAC,GAAD;IACE,eAAc;IACd,kBAAkB;IAClB,kBAAkB;IAEjB;IACmB,CAAA;GACH,CAAA;EACC,CAAA,EAGpB,IACJ,kBAAC,GAAD;EAAqB,QAAQ;YAC1B,IACC,kBAAC,EAAoB,UAArB;GAA8B,OAAO;aAClC;GAC4B,CAAA,GAE/B,kBAAC,GAAD;GAAgB,QAAQ;aACtB,kBAAC,EAAoB,UAArB;IAA8B,OAAO;cAClC;IAC4B,CAAA;GAChB,CAAA;EAEC,CAAA;AAWxB,QANI,IAEK,IAIF,kBAAC,GAAD,EAAA,UAAe,GAA6B,CAAA"}
1
+ {"version":3,"file":"VibeControlsProvider.js","names":[],"sources":["../../src/providers/VibeControlsProvider.tsx"],"sourcesContent":["import type { FC, PropsWithChildren } from 'react';\nimport {\n createContext,\n useContext,\n useMemo,\n useCallback,\n useRef,\n useEffect,\n useState,\n} from 'react';\nimport { MemoryRouter, useNavigate as useRouterNavigate } from 'react-router-dom';\nimport { ApolloClient, InMemoryCache, from } from '@apollo/client';\nimport { BatchHttpLink } from '@apollo/client/link/batch-http';\nimport { onError } from '@apollo/client/link/error';\nimport { CombinedGraphQLErrors } from '@apollo/client/errors';\nimport { setContext } from '@apollo/client/link/context';\nimport { ApolloProvider } from '@apollo/client/react';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport { getStoredAuthToken, getStoredWorkspaceToken } from '@burdenoff/fe-libs/shared/graphql';\nimport { getStoredWorkspaceId } from '@burdenoff/fe-libs/shared/graphql/fetch';\nimport { PermissionProvider } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { isAuthNetworkError } from '@burdenoff/fe-libs/shared/utils';\nimport type { VibeControlsRootProps } from '../types';\nimport { AgentEventsProvider } from './AgentEventsProvider';\nimport { useInitialDataLoader } from '../hooks/useInitialDataLoader';\n\nfunction hasSessionAuthErrors(\n errors: ReadonlyArray<{ message?: string; extensions?: Record<string, unknown> }> = []\n): boolean {\n return errors.some((error) => {\n const code = typeof error.extensions?.code === 'string' ? error.extensions.code : '';\n const errorCode =\n typeof error.extensions?.errorCode === 'string' ? error.extensions.errorCode : '';\n const message = (error.message ?? '').toLowerCase();\n\n return (\n code === 'UNAUTHENTICATED' ||\n errorCode === 'TOKEN_EXPIRED' ||\n message.includes('token_expired') ||\n message.includes('token expired') ||\n message.includes('session expired') ||\n message.includes('unauthorized') ||\n message.includes('unauthenticated') ||\n message.includes('authentication required') ||\n message.includes('workspace context missing')\n );\n });\n}\n\n/**\n * Runs the initial data query inside the ApolloProvider tree so the result\n * lands in the MFE's own Apollo cache (not the shell's).\n */\nconst InitialDataPrefetcher: FC<PropsWithChildren<{ workspaceId?: string | null }>> = ({\n workspaceId,\n children,\n}) => {\n useInitialDataLoader({ workspaceId });\n return <>{children}</>;\n};\n\n/**\n * Context value for VibeControls microfrontend\n */\ntype VibeControlsContextValue = VibeControlsRootProps;\n\nconst VibeControlsContext = createContext<VibeControlsContextValue | null>(null);\n\n/**\n * Hook to access VibeControls context\n */\nexport const useVibeControls = () => {\n const context = useContext(VibeControlsContext);\n if (!context) {\n throw new Error('useVibeControls must be used within VibeControlsProvider');\n }\n return context;\n};\n\n/**\n * Hook for navigation within the VibeControls microfrontend.\n * Uses the shell's navigate function if available, falling back to\n * React Router's useNavigate. Automatically prepends basePath.\n *\n * Usage:\n * const navigate = useVibeNavigate();\n * navigate('/agents/123'); // navigates to /vibecontrols/agents/123\n */\nexport const useVibeNavigate = () => {\n const { navigate: shellNavigate, basePath } = useVibeControls();\n const routerNavigate = useRouterNavigate();\n\n return useCallback(\n (path: string) => {\n const fullPath = basePath && basePath !== '/' ? `${basePath}${path}` : path;\n if (shellNavigate) {\n shellNavigate(fullPath);\n } else {\n routerNavigate(fullPath);\n }\n },\n [shellNavigate, basePath, routerNavigate]\n );\n};\n\n/**\n * Provider component for VibeControls microfrontend\n * Sets up routing, Apollo Client with auth error detection, and context.\n *\n * The Apollo Client is configured with:\n * - An onError link that detects UNAUTHENTICATED / FORBIDDEN responses and\n * invokes the `onAuthError` prop so the host shell can trigger logout.\n * - A dynamic auth link that reads the token per-request (not baked in at\n * client creation time), so token refreshes take effect immediately.\n */\nexport const VibeControlsProvider: FC<PropsWithChildren<VibeControlsRootProps>> = ({\n children,\n basePath,\n navigate,\n currentUser,\n workspaceId,\n tenantId,\n apiGatewayUrl,\n authToken,\n workspaceToken,\n onAgentSelect,\n onSessionStart,\n onVibeActivate,\n defaultView,\n registerNavItems,\n registerFooterItems,\n onAuthError,\n}) => {\n // Per-instance QueryClient so multiple MFE mounts don't share cache\n const [queryClient] = useState(\n () =>\n new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 5 * 60 * 1000, // 5 minutes\n retry: 2,\n },\n },\n })\n );\n\n // Store tokens and callbacks in refs so the Apollo Client (and its cache)\n // survives token refreshes. The setContext auth link reads from refs\n // per-request, so refreshed tokens take effect immediately without\n // destroying the InMemoryCache.\n const authTokenRef = useRef(authToken);\n const workspaceTokenRef = useRef(workspaceToken);\n const workspaceIdRef = useRef(workspaceId);\n const onAuthErrorRef = useRef(onAuthError);\n useEffect(() => {\n authTokenRef.current = authToken;\n }, [authToken]);\n useEffect(() => {\n workspaceTokenRef.current = workspaceToken;\n }, [workspaceToken]);\n useEffect(() => {\n workspaceIdRef.current = workspaceId;\n }, [workspaceId]);\n useEffect(() => {\n onAuthErrorRef.current = onAuthError;\n }, [onAuthError]);\n\n // Build Apollo Client with error link + dynamic auth link.\n // Only recreated when apiGatewayUrl changes (different endpoint = different client).\n const apolloClient = useMemo(() => {\n // Error link (Apollo v4 pattern): detect authentication failures\n const errorLink = onError(({ error }) => {\n if (CombinedGraphQLErrors.is(error) && hasSessionAuthErrors(error.errors)) {\n console.warn('[VibeControls] Auth error detected — notifying host shell');\n onAuthErrorRef.current?.();\n return;\n }\n\n if (error instanceof Error && isAuthNetworkError(error)) {\n console.warn('[VibeControls] Auth network error — notifying host shell');\n onAuthErrorRef.current?.();\n }\n });\n\n // Auth link: dynamically attach dual-token auth and workspace context per-request.\n // Reads from refs so token refreshes don't recreate the client.\n const authLink = setContext((_, { headers }) => {\n const resolvedAuthToken = authTokenRef.current ?? getStoredAuthToken();\n const resolvedWorkspaceToken = workspaceTokenRef.current ?? getStoredWorkspaceToken();\n // Fall back to storage when workspaceId prop is empty (e.g. during initial render\n // before ActiveContextProvider has propagated the value through workspaceBaseProps)\n const resolvedWorkspaceId = workspaceIdRef.current || getStoredWorkspaceId();\n\n return {\n headers: {\n ...headers,\n ...(resolvedAuthToken ? { Authorization: `Bearer ${resolvedAuthToken}` } : {}),\n ...(resolvedWorkspaceToken\n ? { 'X-Workspace-Authorization': `Bearer ${resolvedWorkspaceToken}` }\n : {}),\n ...(resolvedWorkspaceId ? { 'x-workspace-id': resolvedWorkspaceId } : {}),\n },\n };\n });\n\n const httpLink = new BatchHttpLink({\n uri: apiGatewayUrl ? `${apiGatewayUrl}/workspaces/graphql` : '/workspaces/graphql',\n batchMax: 10,\n batchInterval: 10, // ms — tight window so latency isn't hurt\n });\n\n return new ApolloClient({\n link: from([errorLink, authLink, httpLink]),\n cache: new InMemoryCache(),\n defaultOptions: {\n watchQuery: { fetchPolicy: 'cache-first', errorPolicy: 'all' },\n query: { errorPolicy: 'all' },\n mutate: { errorPolicy: 'all' },\n },\n });\n }, [apiGatewayUrl]);\n\n const contextValue: VibeControlsContextValue = {\n basePath,\n navigate,\n currentUser,\n workspaceId,\n tenantId,\n apiGatewayUrl,\n authToken,\n workspaceToken,\n onAgentSelect,\n onSessionStart,\n onVibeActivate,\n defaultView,\n registerNavItems,\n registerFooterItems,\n onAuthError,\n };\n\n const isPublicLinkRoute =\n typeof window !== 'undefined' &&\n /^\\/vibecontrols\\/(share|deck)\\//.test(window.location.pathname);\n\n // Core providers that wrap the children\n // PermissionProvider requires AuthProvider which is provided by the shell.\n // It fetches permissions from wspace-rbac-svc via the workspace gateway.\n // AgentEventsProvider manages WebSocket connections to agents for real-time notifications.\n const routedChildren = isPublicLinkRoute ? (\n children\n ) : (\n <InitialDataPrefetcher workspaceId={workspaceId}>\n <PermissionProvider scopeId={workspaceId ?? null} gateway=\"workspace\">\n <AgentEventsProvider\n toastPosition=\"bottom-right\"\n autoDismissDelay={5000}\n maxVisibleToasts={3}\n >\n {children}\n </AgentEventsProvider>\n </PermissionProvider>\n </InitialDataPrefetcher>\n );\n\n const coreProviders = (\n <QueryClientProvider client={queryClient}>\n {basePath ? (\n <VibeControlsContext.Provider value={contextValue}>\n {routedChildren}\n </VibeControlsContext.Provider>\n ) : (\n <ApolloProvider client={apolloClient}>\n <VibeControlsContext.Provider value={contextValue}>\n {routedChildren}\n </VibeControlsContext.Provider>\n </ApolloProvider>\n )}\n </QueryClientProvider>\n );\n\n // When integrated into a shell (basePath provided), the shell already has a router\n // so we don't create another one. When standalone (no basePath), use MemoryRouter.\n if (basePath) {\n // Integrated mode: shell already has BrowserRouter, don't create nested router\n return coreProviders;\n }\n\n // Standalone mode: create MemoryRouter for independent operation\n return <MemoryRouter>{coreProviders}</MemoryRouter>;\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-vibecontrols",
3
- "version": "2026.522.2",
3
+ "version": "2026.522.4",
4
4
  "description": "VibeControls microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [