@burdenoff/microfe-assethandler 2026.820.4 → 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.
@@ -3,6 +3,7 @@ import { AssetHandlerRootProps } from '../types';
3
3
  import { ValueFormatter } from '../components/charts/chartTheme';
4
4
 
5
5
  export { formatAssetCategory } from '../utils/categoryPresentation';
6
+ export declare function AssetHandlerBootstrapHydrator(): null;
6
7
  export declare const useAssetHandler: () => AssetHandlerRootProps;
7
8
  export declare const DEFAULT_TERMINOLOGY: {
8
9
  readonly asset: "Asset";
@@ -1 +1 @@
1
- {"version":3,"file":"AssetHandlerProvider.d.ts","sourceRoot":"","sources":["../../../src/assethandler/providers/AssetHandlerProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAKnD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAItD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AA8UpE,eAAO,MAAM,eAAe,6BAI3B,CAAC;AAEF,eAAO,MAAM,mBAAmB;;;CAAgD,CAAC;AAEjF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,QAAO;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAMrE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,QAAO;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,KAAK,MAAM,CAAC;IACrD,kEAAkE;IAClE,OAAO,EAAE,cAAc,CAAC;CAazB,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,oBAAoB,QAAO,GAAG,CAAC,MAAM,CAGjD,CAAC;AAEF,eAAO,MAAM,4BAA4B,QAAO;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAC;CAIpD,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,EAAE,CAAC,iBAAiB,CAAC,qBAAqB,CAAC,CAwC7E,CAAC"}
1
+ {"version":3,"file":"AssetHandlerProvider.d.ts","sourceRoot":"","sources":["../../../src/assethandler/providers/AssetHandlerProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAKnD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAItD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAiRpE,wBAAgB,6BAA6B,SA+D5C;AAED,eAAO,MAAM,eAAe,6BAI3B,CAAC;AAEF,eAAO,MAAM,mBAAmB;;;CAAgD,CAAC;AAEjF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,QAAO;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAMrE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,QAAO;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,KAAK,MAAM,CAAC;IACrD,kEAAkE;IAClE,OAAO,EAAE,cAAc,CAAC;CAazB,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,oBAAoB,QAAO,GAAG,CAAC,MAAM,CAGjD,CAAC;AAEF,eAAO,MAAM,4BAA4B,QAAO;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAC;CAIpD,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,EAAE,CAAC,iBAAiB,CAAC,qBAAqB,CAAC,CAwC7E,CAAC"}
@@ -4,11 +4,11 @@ import { formatCurrencyCompact as r } from "../components/charts/chartTheme.js";
4
4
  import "../utils/categoryPresentation.js";
5
5
  import { createContext as i, useContext as a, useEffect as o, useMemo as s } from "react";
6
6
  import { MemoryRouter as c } from "react-router";
7
- import { ApolloClient as l, HttpLink as u, InMemoryCache as d, gql as f } from "@apollo/client";
8
- import { ApolloProvider as p, useApolloClient as m, useQuery as h } from "@apollo/client/react";
9
- import { jsx as g, jsxs as _ } from "react/jsx-runtime";
7
+ import { ApolloClient as l, HttpLink as u, InMemoryCache as d, NetworkStatus as f, gql as p } from "@apollo/client";
8
+ import { ApolloProvider as m, useApolloClient as h, useQuery as g } from "@apollo/client/react";
9
+ import { jsx as _, jsxs as v } from "react/jsx-runtime";
10
10
  //#region src/assethandler/providers/AssetHandlerProvider.tsx
11
- var v = i(null), y = f`
11
+ var y = i(null), b = p`
12
12
  query AssetHandlerBootstrap {
13
13
  assets {
14
14
  id
@@ -54,7 +54,7 @@ var v = i(null), y = f`
54
54
  # PortfolioPortfolioOverviewPage, which now fetches it itself.
55
55
  }
56
56
  }
57
- `, b = f`
57
+ `, x = p`
58
58
  query AssetHandlerBootstrapOverview {
59
59
  dashboardOverview {
60
60
  totalAssets
@@ -63,7 +63,7 @@ var v = i(null), y = f`
63
63
  totalTransactions
64
64
  }
65
65
  }
66
- `, x = f`
66
+ `, S = p`
67
67
  query AssetHandlerBootstrapOps {
68
68
  incidents {
69
69
  id
@@ -93,7 +93,7 @@ var v = i(null), y = f`
93
93
  completedAt
94
94
  }
95
95
  }
96
- `, S = f`
96
+ `, C = p`
97
97
  query AssetHandlerBootstrapMaintenance {
98
98
  maintenanceRecords {
99
99
  id
@@ -124,7 +124,7 @@ var v = i(null), y = f`
124
124
  occurredAt
125
125
  }
126
126
  }
127
- `, C = f`
127
+ `, w = p`
128
128
  query AssetHandlerBootstrapItil {
129
129
  changeRequests {
130
130
  id
@@ -158,7 +158,7 @@ var v = i(null), y = f`
158
158
  updatedAt
159
159
  }
160
160
  }
161
- `, w = f`
161
+ `, T = p`
162
162
  query AssetHandlerBootstrapFinance {
163
163
  budgets {
164
164
  id
@@ -185,7 +185,7 @@ var v = i(null), y = f`
185
185
  paidAt
186
186
  }
187
187
  }
188
- `, T = f`
188
+ `, E = p`
189
189
  query AssetHandlerBootstrapSecurity {
190
190
  securityEvents: getVaultSecurityEvents {
191
191
  items {
@@ -232,24 +232,25 @@ var v = i(null), y = f`
232
232
  }
233
233
  }
234
234
  `;
235
- function E() {
236
- let t = e((e) => e.hydrateFromBootstrap), n = e((e) => e.setBootstrapRefetch), r = m(), i = h(y, {
235
+ function D() {
236
+ let t = e((e) => e.hydrateFromBootstrap), n = e((e) => e.setBootstrapRefetch), r = h(), i = g(b, {
237
237
  fetchPolicy: "cache-and-network",
238
- errorPolicy: "all"
238
+ errorPolicy: "all",
239
+ notifyOnNetworkStatusChange: !0
239
240
  });
240
241
  o(() => {
241
242
  i.data && t(i.data);
242
243
  }, [i.data, t]), o(() => {
243
- if (!i.data) return;
244
+ if (!i.data || i.networkStatus !== f.ready) return;
244
245
  let e = !1;
245
246
  return (async () => {
246
247
  for (let n of [
247
- b,
248
248
  x,
249
249
  S,
250
250
  C,
251
251
  w,
252
- T
252
+ T,
253
+ E
253
254
  ]) {
254
255
  if (e) return;
255
256
  try {
@@ -266,6 +267,7 @@ function E() {
266
267
  };
267
268
  }, [
268
269
  i.data,
270
+ i.networkStatus,
269
271
  r,
270
272
  t
271
273
  ]);
@@ -274,37 +276,37 @@ function E() {
274
276
  a();
275
277
  }), () => n(null)), [a, n]), null;
276
278
  }
277
- var D = () => {
278
- let e = a(v);
279
+ var O = () => {
280
+ let e = a(y);
279
281
  if (!e) throw Error("useAssetHandler must be used within AssetHandlerProvider");
280
282
  return e;
281
- }, O = {
283
+ }, k = {
282
284
  asset: "Asset",
283
285
  assets: "Assets"
284
- }, k = () => {
285
- let { terminology: e } = D();
286
+ }, A = () => {
287
+ let { terminology: e } = O();
286
288
  return {
287
- asset: e?.asset || O.asset,
288
- assets: e?.assets || O.assets
289
+ asset: e?.asset || k.asset,
290
+ assets: e?.assets || k.assets
289
291
  };
290
- }, A = () => {
291
- let { currency: e } = D(), i = e || "USD";
292
+ }, j = () => {
293
+ let { currency: e } = O(), i = e || "USD";
292
294
  return s(() => ({
293
295
  currency: i,
294
296
  symbol: t(i),
295
297
  format: (e) => n(e, i),
296
298
  compact: r(i)
297
299
  }), [i]);
298
- }, j = () => {
299
- let { hiddenCategoryIds: e } = D();
300
- return s(() => new Set(e ?? []), [e]);
301
300
  }, M = () => {
302
- let { categoryLabels: e, categoryOptions: t } = D();
301
+ let { hiddenCategoryIds: e } = O();
302
+ return s(() => new Set(e ?? []), [e]);
303
+ }, N = () => {
304
+ let { categoryLabels: e, categoryOptions: t } = O();
303
305
  return {
304
306
  labels: e,
305
307
  options: t
306
308
  };
307
- }, N = ({ children: e, ...t }) => {
309
+ }, P = ({ children: e, ...t }) => {
308
310
  let n = s(() => new l({
309
311
  link: new u({
310
312
  uri: t.apiGatewayUrl ? `${t.apiGatewayUrl}/workspaces/graphql` : "/workspaces/graphql",
@@ -318,16 +320,16 @@ var D = () => {
318
320
  t.apiGatewayUrl,
319
321
  t.authToken,
320
322
  t.workspaceToken
321
- ]), r = !!t.basePath, i = /* @__PURE__ */ _(v.Provider, {
323
+ ]), r = !!t.basePath, i = /* @__PURE__ */ v(y.Provider, {
322
324
  value: t,
323
- children: [/* @__PURE__ */ g(E, {}), e]
325
+ children: [/* @__PURE__ */ _(D, {}), e]
324
326
  });
325
- return r ? i : /* @__PURE__ */ g(p, {
327
+ return r ? i : /* @__PURE__ */ _(m, {
326
328
  client: n,
327
- children: /* @__PURE__ */ g(c, { children: i })
329
+ children: /* @__PURE__ */ _(c, { children: i })
328
330
  });
329
331
  };
330
332
  //#endregion
331
- export { N as AssetHandlerProvider, M as useAssetCategoryPresentation, D as useAssetHandler, A as useAssetMoney, k as useAssetTerminology, j as useHiddenCategoryIds };
333
+ export { P as AssetHandlerProvider, N as useAssetCategoryPresentation, O as useAssetHandler, j as useAssetMoney, A as useAssetTerminology, M as useHiddenCategoryIds };
332
334
 
333
335
  //# sourceMappingURL=AssetHandlerProvider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AssetHandlerProvider.js","names":[],"sources":["../../../src/assethandler/providers/AssetHandlerProvider.tsx"],"sourcesContent":["import type { FC, PropsWithChildren } from 'react';\nimport { createContext, useContext, useMemo, useEffect } from 'react';\nimport { MemoryRouter } from 'react-router';\nimport { ApolloClient, HttpLink, InMemoryCache, gql } from '@apollo/client';\nimport { ApolloProvider, useApolloClient, useQuery } from '@apollo/client/react';\nimport type { AssetHandlerRootProps } from '../types';\nimport type { AssetHandlerBootstrapPayload } from '../store/assethandlerStore';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { DEFAULT_CURRENCY, currencySymbol, formatMoney } from '../utils/money';\nimport type { ValueFormatter } from '../components/charts/chartTheme';\nimport { formatCurrencyCompact } from '../components/charts/chartTheme';\nexport { formatAssetCategory } from '../utils/categoryPresentation';\n\ntype AssetHandlerContextValue = AssetHandlerRootProps;\n\nconst AssetHandlerContext = createContext<AssetHandlerContextValue | null>(null);\n\n/**\n * The module's data is fetched as several small queries rather than one query\n * with all thirteen root fields.\n *\n * BOFF-6381: `wspace-assethandler-svc` sets `enableRlsGucTagging: true`, so\n * be-sdk routes every Prisma call through `runOperationWithRlsGuc`, which wraps\n * it in its own interactive transaction and pins a pooled connection for the\n * duration. GraphQL resolves root fields in parallel, so the old single query\n * asked for thirteen simultaneously-pinned transactions from a service that\n * measured a hard ceiling of about three. It failed 100% of the time in\n * production — `data: null`, so `hydrateFromBootstrap` never ran and every\n * AssetHandler page rendered empty, including the machinery register.\n *\n * Measured against prod (success rate by concurrent operations):\n * 1 -> 100% 2 -> 100% 3 -> 100% 4 -> 90% 6 -> 57% 8 -> 45%\n * A sibling service without the extension was 100% at all of those, so the\n * ceiling is this service's, not the database's.\n *\n * The batches below are therefore sized to stay at or under that ceiling, and\n * the hydrator awaits them one at a time — issuing them in parallel would\n * recreate exactly the concurrency the split exists to avoid.\n * `dashboardOverview` is alone because it internally fans out to four parallel\n * counts in `AssetHandlerService.getDashboardOverview`.\n *\n * The first batch keeps the name `AssetHandlerBootstrap` so the ~36 existing\n * `refetchQueries: ['AssetHandlerBootstrap']` call sites keep working. When it\n * refetches, its new `data` identity re-runs the sequential loop, so those\n * sites still refresh the whole module, as before.\n */\n\nconst Q_ASSETS = gql`\n query AssetHandlerBootstrap {\n assets {\n id\n assetNumber\n tagId\n name\n description\n category\n status\n condition\n serialNumber\n locationName\n assignedTo\n tags\n createdAt\n updatedAt\n purchaseValue\n purchaseDate\n usefulLifeMonths\n salvageValue\n depreciationMethod\n meterUnit\n department\n warrantyExpiry\n supplier\n disposalDate\n disposalMethod\n disposalNotes\n currentValue\n accumulatedDepreciation\n # totalCostOfOwnership is deliberately NOT selected here. Unlike\n # currentValue and accumulatedDepreciation, which are computed in-process\n # from fields already on the row, Asset.totalCostOfOwnership calls\n # AssetHandlerService.computeAssetTco, which issues TWO aggregate queries\n # per asset (maintenanceRecord + workOrder) under Promise.all. Selecting\n # it turns this one-root query into 1 + 2N database operations — 21 for\n # ten assets — against a service that caps out around three (BOFF-6395),\n # so it errored the batch and left the whole module empty again even\n # after the bootstrap was split. It scales with the number of assets, so\n # it can never be made to fit here.\n #\n # The only consumer that read it from the store is\n # PortfolioPortfolioOverviewPage, which now fetches it itself.\n }\n }\n`;\n\nconst Q_OVERVIEW = gql`\n query AssetHandlerBootstrapOverview {\n dashboardOverview {\n totalAssets\n openIncidents\n openWorkOrders\n totalTransactions\n }\n }\n`;\n\nconst Q_OPS = gql`\n query AssetHandlerBootstrapOps {\n incidents {\n id\n title\n description\n category\n priority\n status\n assignedTo\n reportedBy\n reportedAt\n resolvedAt\n resolution\n slaBreached\n }\n workOrders {\n id\n title\n description\n type\n status\n priority\n assetId\n assignedTo\n createdAt\n updatedAt\n completedAt\n }\n }\n`;\n\nconst Q_MAINTENANCE = gql`\n query AssetHandlerBootstrapMaintenance {\n maintenanceRecords {\n id\n assetId\n title\n description\n type\n status\n priority\n scheduledAt\n completedAt\n estimatedCost\n actualCost\n checklist\n createdAt\n updatedAt\n }\n transactions {\n id\n type\n status\n amount\n currency\n category\n description\n vendor\n department\n occurredAt\n }\n }\n`;\n\nconst Q_ITIL = gql`\n query AssetHandlerBootstrapItil {\n changeRequests {\n id\n title\n description\n type\n status\n risk\n requestedBy\n assignedTo\n affectedAssets\n scheduledStart\n scheduledEnd\n approvals\n createdAt\n updatedAt\n }\n problems {\n id\n title\n description\n status\n priority\n category\n assignedTo\n relatedIncidents\n rootCause\n workaround\n resolution\n createdAt\n updatedAt\n }\n }\n`;\n\nconst Q_FINANCE = gql`\n query AssetHandlerBootstrapFinance {\n budgets {\n id\n name\n category\n allocated\n spent\n remaining\n period\n department\n status\n }\n invoices {\n id\n number\n vendor\n amount\n currency\n issueDate\n dueDate\n status\n items\n relatedPO\n paidAt\n }\n }\n`;\n\nconst Q_SECURITY = gql`\n query AssetHandlerBootstrapSecurity {\n securityEvents: getVaultSecurityEvents {\n items {\n id\n eventType\n severity\n title: message\n description\n source: resourceType\n userId\n ipAddress\n occurredAt: timestamp\n resolved: reviewed\n }\n }\n complianceFrameworks: securityComplianceFrameworks(filter: { sourceProduct: \"assethandler\" }) {\n items {\n id\n name\n version\n status\n score\n totalControls\n passedControls\n failedControls\n lastAssessment\n nextAssessment\n lastAssessedAt\n }\n }\n securityPolicies: securityGovernancePolicies(filter: { sourceProduct: \"assethandler\" }) {\n items {\n id\n name\n description\n category\n status\n version\n effectiveDate\n reviewDate\n owner\n applicableTo\n }\n }\n }\n`;\n\nfunction AssetHandlerBootstrapHydrator() {\n const hydrateFromBootstrap = useAssetHandlerStore((s) => s.hydrateFromBootstrap);\n const setBootstrapRefetch = useAssetHandlerStore((s) => s.setBootstrapRefetch);\n\n const client = useApolloClient();\n\n // Batch 1 stays a `useQuery` named `AssetHandlerBootstrap` so Apollo keeps it\n // as an active, named, cached query — that is what makes the existing\n // `refetchQueries: ['AssetHandlerBootstrap']` call sites work.\n const assets = useQuery<AssetHandlerBootstrapPayload>(Q_ASSETS, {\n fetchPolicy: 'cache-and-network',\n errorPolicy: 'all',\n });\n\n useEffect(() => {\n if (assets.data) hydrateFromBootstrap(assets.data);\n }, [assets.data, hydrateFromBootstrap]);\n\n // The remaining batches run strictly one after another, awaited in a loop\n // rather than as parallel hooks — running them concurrently would recreate\n // exactly the load the split exists to avoid. Hydration is additive, so a\n // batch that fails leaves its slice at the previous value instead of\n // blanking the whole module the way the single combined query did.\n useEffect(() => {\n if (!assets.data) return;\n let cancelled = false;\n void (async () => {\n for (const query of [Q_OVERVIEW, Q_OPS, Q_MAINTENANCE, Q_ITIL, Q_FINANCE, Q_SECURITY]) {\n if (cancelled) return;\n try {\n const result = await client.query<AssetHandlerBootstrapPayload>({\n query,\n fetchPolicy: 'network-only',\n errorPolicy: 'all',\n });\n if (!cancelled && result.data) hydrateFromBootstrap(result.data);\n } catch {\n // One failed section must not stop the others, and must not clear\n // what is already on screen.\n }\n }\n })();\n return () => {\n cancelled = true;\n };\n }, [assets.data, client, hydrateFromBootstrap]);\n\n // Refetching batch 1 is enough to refresh the module: a new `assets.data`\n // identity re-runs the sequential loop above, so the existing\n // `refetchQueries: ['AssetHandlerBootstrap']` sites still refresh everything.\n const refetchAssets = assets.refetch;\n useEffect(() => {\n setBootstrapRefetch(() => {\n void refetchAssets();\n });\n return () => setBootstrapRefetch(null);\n }, [refetchAssets, setBootstrapRefetch]);\n\n return null;\n}\n\nexport const useAssetHandler = () => {\n const context = useContext(AssetHandlerContext);\n if (!context) throw new Error('useAssetHandler must be used within AssetHandlerProvider');\n return context;\n};\n\nexport const DEFAULT_TERMINOLOGY = { asset: 'Asset', assets: 'Assets' } as const;\n\n/**\n * Resolved singular/plural noun for the module, applying consumer overrides on\n * top of the module defaults (\"Asset\"/\"Assets\").\n */\nexport const useAssetTerminology = (): { asset: string; assets: string } => {\n const { terminology } = useAssetHandler();\n return {\n asset: terminology?.asset || DEFAULT_TERMINOLOGY.asset,\n assets: terminology?.assets || DEFAULT_TERMINOLOGY.assets,\n };\n};\n\n/**\n * Resolved money presentation for the module: the consumer's `currency` prop\n * with the module default (\"USD\") applied, plus the three formatters every\n * surface needs. Amounts are never converted — see {@link formatMoney}.\n */\nexport const useAssetMoney = (): {\n currency: string;\n symbol: string;\n /** Full amount; renders an em dash for null/undefined/non-finite. */\n format: (value: number | null | undefined) => string;\n /** Compact amount for axis ticks and dense KPIs, e.g. `₹8.5L`. */\n compact: ValueFormatter;\n} => {\n const { currency } = useAssetHandler();\n const resolved = currency || DEFAULT_CURRENCY;\n return useMemo(\n () => ({\n currency: resolved,\n symbol: currencySymbol(resolved),\n format: (value: number | null | undefined) => formatMoney(value, resolved),\n compact: formatCurrencyCompact(resolved),\n }),\n [resolved]\n );\n};\n\n/** Set of category ids the consumer asked to hide (empty by default). */\nexport const useHiddenCategoryIds = (): Set<string> => {\n const { hiddenCategoryIds } = useAssetHandler();\n return useMemo(() => new Set(hiddenCategoryIds ?? []), [hiddenCategoryIds]);\n};\n\nexport const useAssetCategoryPresentation = (): {\n labels?: Record<string, string>;\n options?: AssetHandlerRootProps['categoryOptions'];\n} => {\n const { categoryLabels, categoryOptions } = useAssetHandler();\n return { labels: categoryLabels, options: categoryOptions };\n};\n\nexport const AssetHandlerProvider: FC<PropsWithChildren<AssetHandlerRootProps>> = ({\n children,\n ...props\n}) => {\n const apolloClient = useMemo(\n () =>\n new ApolloClient({\n link: new HttpLink({\n uri: props.apiGatewayUrl\n ? `${props.apiGatewayUrl}/workspaces/graphql`\n : '/workspaces/graphql',\n headers: props.authToken\n ? {\n Authorization: `Bearer ${props.authToken}`,\n ...(props.workspaceToken\n ? { 'X-Workspace-Authorization': `Bearer ${props.workspaceToken}` }\n : {}),\n }\n : undefined,\n }),\n cache: new InMemoryCache(),\n }),\n [props.apiGatewayUrl, props.authToken, props.workspaceToken]\n );\n\n const isIntegrated = !!props.basePath;\n const content = (\n <AssetHandlerContext.Provider value={props}>\n <AssetHandlerBootstrapHydrator />\n {children}\n </AssetHandlerContext.Provider>\n );\n\n return isIntegrated ? (\n content\n ) : (\n <ApolloProvider client={apolloClient}>\n <MemoryRouter>{content}</MemoryRouter>\n </ApolloProvider>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AssetHandlerProvider.js","names":[],"sources":["../../../src/assethandler/providers/AssetHandlerProvider.tsx"],"sourcesContent":["import type { FC, PropsWithChildren } from 'react';\nimport { createContext, useContext, useMemo, useEffect } from 'react';\nimport { MemoryRouter } from 'react-router';\nimport { ApolloClient, HttpLink, InMemoryCache, NetworkStatus, gql } from '@apollo/client';\nimport { ApolloProvider, useApolloClient, useQuery } from '@apollo/client/react';\nimport type { AssetHandlerRootProps } from '../types';\nimport type { AssetHandlerBootstrapPayload } from '../store/assethandlerStore';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { DEFAULT_CURRENCY, currencySymbol, formatMoney } from '../utils/money';\nimport type { ValueFormatter } from '../components/charts/chartTheme';\nimport { formatCurrencyCompact } from '../components/charts/chartTheme';\nexport { formatAssetCategory } from '../utils/categoryPresentation';\n\ntype AssetHandlerContextValue = AssetHandlerRootProps;\n\nconst AssetHandlerContext = createContext<AssetHandlerContextValue | null>(null);\n\n/**\n * The module's data is fetched as several small queries rather than one query\n * with all thirteen root fields.\n *\n * BOFF-6381: `wspace-assethandler-svc` sets `enableRlsGucTagging: true`, so\n * be-sdk routes every Prisma call through `runOperationWithRlsGuc`, which wraps\n * it in its own interactive transaction and pins a pooled connection for the\n * duration. GraphQL resolves root fields in parallel, so the old single query\n * asked for thirteen simultaneously-pinned transactions from a service that\n * measured a hard ceiling of about three. It failed 100% of the time in\n * production — `data: null`, so `hydrateFromBootstrap` never ran and every\n * AssetHandler page rendered empty, including the machinery register.\n *\n * Measured against prod (success rate by concurrent operations):\n * 1 -> 100% 2 -> 100% 3 -> 100% 4 -> 90% 6 -> 57% 8 -> 45%\n * A sibling service without the extension was 100% at all of those, so the\n * ceiling is this service's, not the database's.\n *\n * The batches below are therefore sized to stay at or under that ceiling, and\n * the hydrator awaits them one at a time — issuing them in parallel would\n * recreate exactly the concurrency the split exists to avoid.\n * `dashboardOverview` is alone because it internally fans out to four parallel\n * counts in `AssetHandlerService.getDashboardOverview`.\n *\n * The first batch keeps the name `AssetHandlerBootstrap` so the ~36 existing\n * `refetchQueries: ['AssetHandlerBootstrap']` call sites keep working. When it\n * refetches, its new `data` identity re-runs the sequential loop, so those\n * sites still refresh the whole module, as before.\n */\n\nconst Q_ASSETS = gql`\n query AssetHandlerBootstrap {\n assets {\n id\n assetNumber\n tagId\n name\n description\n category\n status\n condition\n serialNumber\n locationName\n assignedTo\n tags\n createdAt\n updatedAt\n purchaseValue\n purchaseDate\n usefulLifeMonths\n salvageValue\n depreciationMethod\n meterUnit\n department\n warrantyExpiry\n supplier\n disposalDate\n disposalMethod\n disposalNotes\n currentValue\n accumulatedDepreciation\n # totalCostOfOwnership is deliberately NOT selected here. Unlike\n # currentValue and accumulatedDepreciation, which are computed in-process\n # from fields already on the row, Asset.totalCostOfOwnership calls\n # AssetHandlerService.computeAssetTco, which issues TWO aggregate queries\n # per asset (maintenanceRecord + workOrder) under Promise.all. Selecting\n # it turns this one-root query into 1 + 2N database operations — 21 for\n # ten assets — against a service that caps out around three (BOFF-6395),\n # so it errored the batch and left the whole module empty again even\n # after the bootstrap was split. It scales with the number of assets, so\n # it can never be made to fit here.\n #\n # The only consumer that read it from the store is\n # PortfolioPortfolioOverviewPage, which now fetches it itself.\n }\n }\n`;\n\nconst Q_OVERVIEW = gql`\n query AssetHandlerBootstrapOverview {\n dashboardOverview {\n totalAssets\n openIncidents\n openWorkOrders\n totalTransactions\n }\n }\n`;\n\nconst Q_OPS = gql`\n query AssetHandlerBootstrapOps {\n incidents {\n id\n title\n description\n category\n priority\n status\n assignedTo\n reportedBy\n reportedAt\n resolvedAt\n resolution\n slaBreached\n }\n workOrders {\n id\n title\n description\n type\n status\n priority\n assetId\n assignedTo\n createdAt\n updatedAt\n completedAt\n }\n }\n`;\n\nconst Q_MAINTENANCE = gql`\n query AssetHandlerBootstrapMaintenance {\n maintenanceRecords {\n id\n assetId\n title\n description\n type\n status\n priority\n scheduledAt\n completedAt\n estimatedCost\n actualCost\n checklist\n createdAt\n updatedAt\n }\n transactions {\n id\n type\n status\n amount\n currency\n category\n description\n vendor\n department\n occurredAt\n }\n }\n`;\n\nconst Q_ITIL = gql`\n query AssetHandlerBootstrapItil {\n changeRequests {\n id\n title\n description\n type\n status\n risk\n requestedBy\n assignedTo\n affectedAssets\n scheduledStart\n scheduledEnd\n approvals\n createdAt\n updatedAt\n }\n problems {\n id\n title\n description\n status\n priority\n category\n assignedTo\n relatedIncidents\n rootCause\n workaround\n resolution\n createdAt\n updatedAt\n }\n }\n`;\n\nconst Q_FINANCE = gql`\n query AssetHandlerBootstrapFinance {\n budgets {\n id\n name\n category\n allocated\n spent\n remaining\n period\n department\n status\n }\n invoices {\n id\n number\n vendor\n amount\n currency\n issueDate\n dueDate\n status\n items\n relatedPO\n paidAt\n }\n }\n`;\n\nconst Q_SECURITY = gql`\n query AssetHandlerBootstrapSecurity {\n securityEvents: getVaultSecurityEvents {\n items {\n id\n eventType\n severity\n title: message\n description\n source: resourceType\n userId\n ipAddress\n occurredAt: timestamp\n resolved: reviewed\n }\n }\n complianceFrameworks: securityComplianceFrameworks(filter: { sourceProduct: \"assethandler\" }) {\n items {\n id\n name\n version\n status\n score\n totalControls\n passedControls\n failedControls\n lastAssessment\n nextAssessment\n lastAssessedAt\n }\n }\n securityPolicies: securityGovernancePolicies(filter: { sourceProduct: \"assethandler\" }) {\n items {\n id\n name\n description\n category\n status\n version\n effectiveDate\n reviewDate\n owner\n applicableTo\n }\n }\n }\n`;\n\nexport function AssetHandlerBootstrapHydrator() {\n const hydrateFromBootstrap = useAssetHandlerStore((s) => s.hydrateFromBootstrap);\n const setBootstrapRefetch = useAssetHandlerStore((s) => s.setBootstrapRefetch);\n\n const client = useApolloClient();\n\n // Batch 1 stays a `useQuery` named `AssetHandlerBootstrap` so Apollo keeps it\n // as an active, named, cached query — that is what makes the existing\n // `refetchQueries: ['AssetHandlerBootstrap']` call sites work.\n const assets = useQuery<AssetHandlerBootstrapPayload>(Q_ASSETS, {\n fetchPolicy: 'cache-and-network',\n errorPolicy: 'all',\n // A mutation can change only a supplemental slice (maintenance, ITIL,\n // finance, security) while returning an identical assets payload. The\n // network-status transition is therefore the reliable refetch signal.\n notifyOnNetworkStatusChange: true,\n });\n\n useEffect(() => {\n if (assets.data) hydrateFromBootstrap(assets.data);\n }, [assets.data, hydrateFromBootstrap]);\n\n // The remaining batches run strictly one after another, awaited in a loop\n // rather than as parallel hooks — running them concurrently would recreate\n // exactly the load the split exists to avoid. Hydration is additive, so a\n // batch that fails leaves its slice at the previous value instead of\n // blanking the whole module the way the single combined query did.\n useEffect(() => {\n if (!assets.data || assets.networkStatus !== NetworkStatus.ready) return;\n let cancelled = false;\n void (async () => {\n for (const query of [Q_OVERVIEW, Q_OPS, Q_MAINTENANCE, Q_ITIL, Q_FINANCE, Q_SECURITY]) {\n if (cancelled) return;\n try {\n const result = await client.query<AssetHandlerBootstrapPayload>({\n query,\n fetchPolicy: 'network-only',\n errorPolicy: 'all',\n });\n if (!cancelled && result.data) hydrateFromBootstrap(result.data);\n } catch {\n // One failed section must not stop the others, and must not clear\n // what is already on screen.\n }\n }\n })();\n return () => {\n cancelled = true;\n };\n }, [assets.data, assets.networkStatus, client, hydrateFromBootstrap]);\n\n // Refetching batch 1 is enough to refresh the module: a new `assets.data`\n // identity re-runs the sequential loop above, so the existing\n // `refetchQueries: ['AssetHandlerBootstrap']` sites still refresh everything.\n const refetchAssets = assets.refetch;\n useEffect(() => {\n setBootstrapRefetch(() => {\n void refetchAssets();\n });\n return () => setBootstrapRefetch(null);\n }, [refetchAssets, setBootstrapRefetch]);\n\n return null;\n}\n\nexport const useAssetHandler = () => {\n const context = useContext(AssetHandlerContext);\n if (!context) throw new Error('useAssetHandler must be used within AssetHandlerProvider');\n return context;\n};\n\nexport const DEFAULT_TERMINOLOGY = { asset: 'Asset', assets: 'Assets' } as const;\n\n/**\n * Resolved singular/plural noun for the module, applying consumer overrides on\n * top of the module defaults (\"Asset\"/\"Assets\").\n */\nexport const useAssetTerminology = (): { asset: string; assets: string } => {\n const { terminology } = useAssetHandler();\n return {\n asset: terminology?.asset || DEFAULT_TERMINOLOGY.asset,\n assets: terminology?.assets || DEFAULT_TERMINOLOGY.assets,\n };\n};\n\n/**\n * Resolved money presentation for the module: the consumer's `currency` prop\n * with the module default (\"USD\") applied, plus the three formatters every\n * surface needs. Amounts are never converted — see {@link formatMoney}.\n */\nexport const useAssetMoney = (): {\n currency: string;\n symbol: string;\n /** Full amount; renders an em dash for null/undefined/non-finite. */\n format: (value: number | null | undefined) => string;\n /** Compact amount for axis ticks and dense KPIs, e.g. `₹8.5L`. */\n compact: ValueFormatter;\n} => {\n const { currency } = useAssetHandler();\n const resolved = currency || DEFAULT_CURRENCY;\n return useMemo(\n () => ({\n currency: resolved,\n symbol: currencySymbol(resolved),\n format: (value: number | null | undefined) => formatMoney(value, resolved),\n compact: formatCurrencyCompact(resolved),\n }),\n [resolved]\n );\n};\n\n/** Set of category ids the consumer asked to hide (empty by default). */\nexport const useHiddenCategoryIds = (): Set<string> => {\n const { hiddenCategoryIds } = useAssetHandler();\n return useMemo(() => new Set(hiddenCategoryIds ?? []), [hiddenCategoryIds]);\n};\n\nexport const useAssetCategoryPresentation = (): {\n labels?: Record<string, string>;\n options?: AssetHandlerRootProps['categoryOptions'];\n} => {\n const { categoryLabels, categoryOptions } = useAssetHandler();\n return { labels: categoryLabels, options: categoryOptions };\n};\n\nexport const AssetHandlerProvider: FC<PropsWithChildren<AssetHandlerRootProps>> = ({\n children,\n ...props\n}) => {\n const apolloClient = useMemo(\n () =>\n new ApolloClient({\n link: new HttpLink({\n uri: props.apiGatewayUrl\n ? `${props.apiGatewayUrl}/workspaces/graphql`\n : '/workspaces/graphql',\n headers: props.authToken\n ? {\n Authorization: `Bearer ${props.authToken}`,\n ...(props.workspaceToken\n ? { 'X-Workspace-Authorization': `Bearer ${props.workspaceToken}` }\n : {}),\n }\n : undefined,\n }),\n cache: new InMemoryCache(),\n }),\n [props.apiGatewayUrl, props.authToken, props.workspaceToken]\n );\n\n const isIntegrated = !!props.basePath;\n const content = (\n <AssetHandlerContext.Provider value={props}>\n <AssetHandlerBootstrapHydrator />\n {children}\n </AssetHandlerContext.Provider>\n );\n\n return isIntegrated ? (\n content\n ) : (\n <ApolloProvider client={apolloClient}>\n <MemoryRouter>{content}</MemoryRouter>\n </ApolloProvider>\n );\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-assethandler",
3
- "version": "2026.820.4",
3
+ "version": "2026.825.1",
4
4
  "description": "Burdenoff AssetHandler Microfrontend",
5
5
  "type": "module",
6
6
  "files": [