@krak-stack/registry 0.1.39 → 0.1.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -98,6 +98,164 @@ if (!import.meta.env.SSR) {
98
98
  }
99
99
  ```
100
100
 
101
+ ### Named events and page views
102
+
103
+ For workers, workflows, or CLIs without an HTTP server, provide `ServerOtlp.layer`
104
+ from `@krak-stack/registry/opentelemetry/server`. `HttpApiOtlp.layer` already
105
+ includes this layer alongside the browser proxy routes.
106
+
107
+ ```ts
108
+ import { Effect } from "effect";
109
+ import { ServerOtlp } from "@krak-stack/registry/opentelemetry/server";
110
+
111
+ const program = ServerOtlp.track("app.order.completed", {
112
+ "order.id": "order-123",
113
+ }).pipe(Effect.provide(ServerOtlp.layer));
114
+ ```
115
+
116
+ Enable server events with `OTEL_LOGS_EXPORTER=otlp`, `OTEL_SERVICE_NAME`, and
117
+ `OTEL_EXPORTER_OTLP_ENDPOINT` (the exporter appends `/v1/logs`), or an explicit
118
+ `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT`. Signal-specific headers override
119
+ `OTEL_EXPORTER_OTLP_HEADERS`; `OTEL_SDK_DISABLED=true` disables export. Use a
120
+ long-lived application runtime to retain batching; dispose it on shutdown to
121
+ flush pending events.
122
+
123
+ Browser events are separately opt-in and do not enable application log export:
124
+
125
+ ```ts
126
+ BrowserOtlp.layer({ serviceName: "my-app-web", events: {} });
127
+
128
+ // Effects use the active BrowserOtlp.layer, even from a different calling runtime.
129
+ const telemetry = Effect.gen(function* () {
130
+ yield* BrowserOtlp.track("app.checkout.started", {
131
+ "checkout.source": "cart",
132
+ });
133
+ yield* BrowserOtlp.page({
134
+ name: "Documentation",
135
+ category: "Docs",
136
+ path: "/docs",
137
+ });
138
+ });
139
+ ```
140
+
141
+ With `events: {}`, page views are automatic: one when the layer starts, then one
142
+ for each pathname change through `pushState`, `replaceState`, or `popstate`
143
+ (back/forward). Repeated same-path changes and query/hash-only changes are ignored.
144
+ History hooks and listeners are scoped and restored on disposal. Tracking follows
145
+ URL changes, not framework loader completion. The first page may be anonymous if
146
+ identity has not been set yet; identify does not retroactively change it.
147
+
148
+ Set `events: { pageViews: false }` to disable automatic tracking while retaining custom events.
149
+ Then call `page` (`pageView` alias) manually for route templates or application-specific
150
+ navigation timing. Do not also manually emit the same automatic page view.
151
+ Without `path`, it reads `location.pathname` when executed. It emits the
152
+ application-defined event `browser.page_view` with required attribute `url.path`,
153
+ stripping query strings and fragments. Supply sanitized paths or route templates
154
+ when paths contain personal information. No cookies or visitor/session IDs are
155
+ created. Only the pathname is captured; paths may still contain personal IDs.
156
+
157
+ ### Browser identity
158
+
159
+ Call `BrowserOtlp.identify` when auth state becomes available or changes, and
160
+ `BrowserOtlp.identify(null)` on logout. These remain Effects, but the caller does not need
161
+ the browser exporter's services in its own runtime:
162
+
163
+ ```ts
164
+ const telemetry = Effect.gen(function* () {
165
+ yield* BrowserOtlp.identify({
166
+ userId: "user-123",
167
+ organizationId: "org-123",
168
+ });
169
+ yield* BrowserOtlp.track("app.checkout.started");
170
+ yield* BrowserOtlp.page({ path: "/checkout" });
171
+ yield* BrowserOtlp.identify(null);
172
+ });
173
+ ```
174
+
175
+ Identity is in memory per telemetry runtime, not cookies or local storage.
176
+ `identify` is best-effort and has no typed failure: invalid identity leaves the
177
+ previous identity unchanged and logs a warning without including supplied values.
178
+ `identify` replaces the complete identity; omitting an organization clears the
179
+ previous organization. It emits no event. Identity is copied into each future
180
+ event when recorded; changes and resets do not rewrite queued events. Identified
181
+ user/org attributes override per-call properties/options, while scoped log
182
+ annotations take precedence over identified identity. This applies only to event
183
+ telemetry, not automatic trace/log exporters, API request headers, or authorization.
184
+ Browser identity is client-supplied and untrusted; use trusted server events for
185
+ billing, authorization, audit trails, or tenant-isolated reporting. Re-identify
186
+ after a reload and update identity before recording events after an auth or
187
+ organization change. The OTLP proxy remains passthrough and never enriches identity.
188
+
189
+ Initialize the browser layer once on the application's long-lived runtime. After
190
+ it starts, `Effect.runPromise(BrowserOtlp.identify(...))` and
191
+ `Effect.runPromise(BrowserOtlp.track(...))` work from ordinary application code.
192
+ Event methods retain typed validation errors and the calling fiber's trace/log
193
+ context; `identify` handles validation failures internally.
194
+ Calls executed before startup or after disposal are no-ops; nothing is buffered.
195
+ If several browser layers are active, calls target the most recently started one;
196
+ use a single browser layer in normal applications. There is no analytics bridge,
197
+ React hook, or automatic identification.
198
+
199
+ ### Event options
200
+
201
+ Both environments accept
202
+ `track(name, properties?, options?)`. Common options are explicit and per-call,
203
+ so server requests never share mutable visitor state:
204
+
205
+ ```ts
206
+ const telemetry = Effect.gen(function* () {
207
+ yield* ServerOtlp.track(
208
+ "app.order.completed",
209
+ { "order.total": 49 },
210
+ {
211
+ sessionId: "session-123",
212
+ messageId: "order-123-completed",
213
+ context: { "organization.id": "org-123", "app.locale": "en" },
214
+ },
215
+ );
216
+ });
217
+ ```
218
+
219
+ `context` and event properties become flat OTLP attributes; properties override
220
+ context on collision. Options `userId`, `anonymousId`, `sessionId`, and `messageId`
221
+ map to `user.id`, `analytics.anonymous_id`, `session.id`, and `log.record.uid`,
222
+ overriding properties with the same keys. Scoped `Effect.annotateLogs` annotations
223
+ are inherited automatically and take precedence over all per-call attributes.
224
+ In authenticated server handlers, `AuthMiddleware` already supplies `user.id` and
225
+ `organization.id`, so do not repeat them in `.track()` calls. Background jobs and
226
+ browser code can provide identity explicitly when needed. Annotations remain
227
+ fiber-scoped; no identity state is persisted by the event service.
228
+ Identifiers are optional; callers own
229
+ consent and identity lifecycle. Message IDs aid correlation, not guaranteed
230
+ deduplication. Optional `timestamp` accepts an Effect `DateTime.Utc` for delayed
231
+ events; the SDK still sets observed time to the time of recording. There is no
232
+ clock-skew correction. `page` accepts `properties` and these same `options`, plus
233
+ optional `name` and `category` mapped to application-defined
234
+ `analytics.page.name` and `analytics.page.category` attributes. It always owns
235
+ the sanitized `url.path`. The `analytics.*` fields and `browser.page_view` name
236
+ are project conventions, not standardized OpenTelemetry analytics semantics.
237
+ Group, Alias, profile storage, destination routing, and automatic
238
+ collection of IPs, user agents, page titles, referrers, and full URLs are not
239
+ implemented.
240
+
241
+ Both `.track(name, attributes?)` methods follow the
242
+ [OpenTelemetry event conventions](https://opentelemetry.io/docs/specs/semconv/general/events/):
243
+ records have the native `eventName` field, an occurrence timestamp, INFO severity
244
+ number (9), flat structured attributes, and no body. The SDK sets observed time
245
+ when it records the occurrence. Events inherit active trace/span IDs without
246
+ creating spans. Use stable domain-qualified names with no dynamic identifiers;
247
+ put changing values in attributes. Document each custom event's trigger and
248
+ required/optional attributes; include `error.type` for failures. These conventions
249
+ are still in development. Attribute values support strings, finite numbers,
250
+ booleans, and homogeneous arrays of those values. Invalid inputs fail with an
251
+ Effect schema error.
252
+
253
+ The logs proxy must be enabled with `OTEL_LOGS_EXPORTER=otlp` for browser events.
254
+ OTLP transports events; configure a collector and a logs backend such as Loki for
255
+ storage and Grafana dashboards. Browser flushing on hidden/pagehide is best
256
+ effort. Apply payload limits and rate limits at your public ingestion boundary,
257
+ avoid sensitive attributes, and keep high-cardinality fields out of Loki labels.
258
+
101
259
  `@krak-stack/registry/service-notification` is the source-compatible direct
102
260
  dispatcher. Use the separate `@krak-stack/notifications` package for a durable
103
261
  inbox, package-owned migrations, persisted delivery jobs, retries, workers, and
@@ -32,10 +32,10 @@ export interface DataTableListItemContext<TData> {
32
32
  }
33
33
  export interface DataTableListOptions {
34
34
  emptyLabel: ReactNode;
35
- display?: "list" | "icon";
36
- locale?: string;
37
- messages?: DataTableMessageTranslations;
38
- visibleCount?: number;
35
+ display?: "list" | "icon" | undefined;
36
+ locale?: string | undefined;
37
+ messages?: DataTableMessageTranslations | undefined;
38
+ visibleCount?: number | undefined;
39
39
  }
40
40
  export type DataTableListColumn<TData> = DataTableListOptions & {
41
41
  /** Includes items not loaded in the preview; the dropdown only shows supplied items. */
@@ -76,8 +76,8 @@ interface EditableDataTableListCellProps extends DataTableListOptions {
76
76
  totalCount?: number | undefined;
77
77
  }
78
78
  interface DataTableListCellActionsProps {
79
- actionsLabel?: string;
80
- itemActions?: readonly DataTableItemAction<DataTableListItem>[];
79
+ actionsLabel?: string | undefined;
80
+ itemActions?: readonly DataTableItemAction<DataTableListItem>[] | undefined;
81
81
  }
82
82
  export type DataTableListCellProps = DataTableListCellActionsProps & (EditableDataTableListCellProps | (DataTableListSummaryProps & {
83
83
  options?: never;
@@ -206,6 +206,23 @@ export type DataTablePaginationFeature = false | {
206
206
  rowCount: number;
207
207
  pageSizes?: readonly number[];
208
208
  };
209
+ export interface DataTableExportAction {
210
+ id: string;
211
+ label: string;
212
+ icon?: ReactNode;
213
+ disabled?: boolean;
214
+ /** The app owns fetching, authorization, field selection, and downloading. */
215
+ onExport: () => void | Promise<void>;
216
+ }
217
+ export interface DataTableExportFeature {
218
+ baseName: string;
219
+ /** Both scopes operate only on rows already supplied to the table. */
220
+ scope?: "currentPage" | "filteredRows";
221
+ /** Set false to offer only app-provided actions. Defaults to true. */
222
+ builtIn?: boolean;
223
+ actions?: readonly DataTableExportAction[];
224
+ onError?: (error: unknown, action: DataTableExportAction) => void;
225
+ }
209
226
  export interface DataTableFeatures<TData> {
210
227
  toolbar?: false | {
211
228
  /** Additional `MenubarMenu` controls rendered at the start of the toolbar. */
@@ -228,10 +245,7 @@ export interface DataTableFeatures<TData> {
228
245
  label?: string;
229
246
  items: readonly DataTableRowAction<TData>[];
230
247
  };
231
- export?: false | {
232
- baseName: string;
233
- scope?: "currentPage" | "filteredRows";
234
- };
248
+ export?: false | DataTableExportFeature;
235
249
  gallery?: false | {
236
250
  name: string;
237
251
  description?: string;
@@ -255,6 +269,7 @@ export type DataTableMessages = PaginationMessages & {
255
269
  export: string;
256
270
  exportCsv: string;
257
271
  exportJson: string;
272
+ exportFailed?: string;
258
273
  refresh: string;
259
274
  view: string;
260
275
  tableView: string;
@@ -295,6 +310,7 @@ export declare const dataTableMessages: (locale?: string, translations?: DataTab
295
310
  export: string;
296
311
  exportCsv: string;
297
312
  exportJson: string;
313
+ exportFailed: string;
298
314
  refresh: string;
299
315
  view: string;
300
316
  tableView: string;
@@ -332,6 +348,7 @@ export declare const dataTableMessages: (locale?: string, translations?: DataTab
332
348
  export: string;
333
349
  exportCsv: string;
334
350
  exportJson: string;
351
+ exportFailed: string;
335
352
  refresh: string;
336
353
  view: string;
337
354
  tableView: string;
@@ -993,6 +993,7 @@ var messages2 = {
993
993
  export: "Export",
994
994
  exportCsv: "CSV",
995
995
  exportJson: "JSON",
996
+ exportFailed: "Export failed. Please try again.",
996
997
  refresh: "Refresh",
997
998
  view: "View",
998
999
  tableView: "Table",
@@ -1024,6 +1025,7 @@ var messages2 = {
1024
1025
  export: "Exporter",
1025
1026
  exportCsv: "CSV",
1026
1027
  exportJson: "JSON",
1028
+ exportFailed: "L’exportation a échoué. Veuillez réessayer.",
1027
1029
  refresh: "Rafraîchir",
1028
1030
  view: "Affichage",
1029
1031
  tableView: "Tableau",
@@ -1586,7 +1588,7 @@ var DataTableRowActions = ({
1586
1588
  event.stopPropagation();
1587
1589
  action.onClick(row);
1588
1590
  },
1589
- variant: action.variant,
1591
+ variant: action.variant ?? "default",
1590
1592
  children: [
1591
1593
  action.icon,
1592
1594
  action.name
@@ -1641,6 +1643,9 @@ var DataTableToolbar = ({
1641
1643
  tableAtom
1642
1644
  }) => {
1643
1645
  const [store, setStore] = useTableAtom(tableAtom);
1646
+ const exportPending = useRef(false);
1647
+ const [exporting, setExporting] = useState(false);
1648
+ const [exportFailed, setExportFailed] = useState(false);
1644
1649
  const { features, labels } = store.config;
1645
1650
  const model = deriveModel(store);
1646
1651
  const selectedRows = getSelectedRows(store, model);
@@ -1653,6 +1658,27 @@ var DataTableToolbar = ({
1653
1658
  const update = (next) => changeState(setStore, next);
1654
1659
  const visibleBulkActions = bulkActions ? bulkActions.items.filter((action) => !action.visible || action.visible(selectedRows)) : [];
1655
1660
  const exportRows = features.export && features.export.scope === "filteredRows" ? model.filteredRows : model.pageRows;
1661
+ const exportFeature = features.export;
1662
+ const exportActions = exportFeature ? exportFeature.actions ?? [] : [];
1663
+ const builtInExport = exportFeature && exportFeature.builtIn !== false;
1664
+ const canExport = builtInExport && exportRows.length > 0 || exportActions.some((action) => !action.disabled);
1665
+ const runExport = async (action) => {
1666
+ if (exportPending.current || action.disabled)
1667
+ return;
1668
+ exportPending.current = true;
1669
+ setExporting(true);
1670
+ setExportFailed(false);
1671
+ try {
1672
+ await action.onExport();
1673
+ } catch (error) {
1674
+ setExportFailed(true);
1675
+ if (exportFeature)
1676
+ exportFeature.onError?.(error, action);
1677
+ } finally {
1678
+ exportPending.current = false;
1679
+ setExporting(false);
1680
+ }
1681
+ };
1656
1682
  return /* @__PURE__ */ jsxs8("div", {
1657
1683
  className: "flex flex-col gap-2 pb-4",
1658
1684
  children: [
@@ -1716,7 +1742,7 @@ var DataTableToolbar = ({
1716
1742
  /* @__PURE__ */ jsx14(MenubarSeparator, {}),
1717
1743
  visibleBulkActions.map((action) => /* @__PURE__ */ jsxs8(MenubarItem, {
1718
1744
  onClick: () => action.onClick(selectedRows),
1719
- variant: action.variant,
1745
+ variant: action.variant ?? "default",
1720
1746
  children: [
1721
1747
  action.icon,
1722
1748
  action.name
@@ -1786,12 +1812,15 @@ var DataTableToolbar = ({
1786
1812
  children: [
1787
1813
  /* @__PURE__ */ jsxs8(MenubarTrigger, {
1788
1814
  "aria-label": labels.export,
1789
- disabled: !exportRows.length,
1815
+ "aria-busy": exporting,
1816
+ disabled: exporting || !canExport,
1790
1817
  children: [
1791
- /* @__PURE__ */ jsx14(Download, {}),
1818
+ exporting ? /* @__PURE__ */ jsx14(RefreshCw, {
1819
+ className: "animate-spin"
1820
+ }) : /* @__PURE__ */ jsx14(Download, {}),
1792
1821
  /* @__PURE__ */ jsx14("span", {
1793
1822
  className: "hidden sm:inline",
1794
- children: labels.export
1823
+ children: exporting ? labels.loading : labels.export
1795
1824
  })
1796
1825
  ]
1797
1826
  }),
@@ -1803,23 +1832,38 @@ var DataTableToolbar = ({
1803
1832
  children: labels.export
1804
1833
  }),
1805
1834
  /* @__PURE__ */ jsx14(MenubarSeparator, {}),
1806
- /* @__PURE__ */ jsxs8(MenubarItem, {
1807
- onClick: () => downloadCsv(model.visibleColumns.map(columnLabel), exportRows.map((row) => model.visibleColumns.map((column) => String(getDataTableExportValue(row, column, store.config.locale) ?? ""))), features.export ? features.export.baseName : "table"),
1835
+ builtInExport ? /* @__PURE__ */ jsxs8(Fragment, {
1808
1836
  children: [
1809
- /* @__PURE__ */ jsx14(FileText, {}),
1810
- labels.exportCsv
1837
+ /* @__PURE__ */ jsxs8(MenubarItem, {
1838
+ disabled: exporting || !exportRows.length,
1839
+ onClick: () => downloadCsv(model.visibleColumns.map(columnLabel), exportRows.map((row) => model.visibleColumns.map((column) => String(getDataTableExportValue(row, column, store.config.locale) ?? ""))), features.export ? features.export.baseName : "table"),
1840
+ children: [
1841
+ /* @__PURE__ */ jsx14(FileText, {}),
1842
+ labels.exportCsv
1843
+ ]
1844
+ }),
1845
+ /* @__PURE__ */ jsxs8(MenubarItem, {
1846
+ disabled: exporting || !exportRows.length,
1847
+ onClick: () => downloadJson(exportRows.map((row) => Object.fromEntries(model.visibleColumns.map((column) => [
1848
+ column.id,
1849
+ getDataTableExportValue(row, column, store.config.locale)
1850
+ ]))), features.export ? features.export.baseName : "table"),
1851
+ children: [
1852
+ /* @__PURE__ */ jsx14(FileJson, {}),
1853
+ labels.exportJson
1854
+ ]
1855
+ })
1811
1856
  ]
1812
- }),
1813
- /* @__PURE__ */ jsxs8(MenubarItem, {
1814
- onClick: () => downloadJson(exportRows.map((row) => Object.fromEntries(model.visibleColumns.map((column) => [
1815
- column.id,
1816
- getDataTableExportValue(row, column, store.config.locale)
1817
- ]))), features.export ? features.export.baseName : "table"),
1857
+ }) : null,
1858
+ builtInExport && exportActions.length ? /* @__PURE__ */ jsx14(MenubarSeparator, {}) : null,
1859
+ exportActions.map((action) => /* @__PURE__ */ jsxs8(MenubarItem, {
1860
+ disabled: exporting || action.disabled,
1861
+ onClick: () => void runExport(action),
1818
1862
  children: [
1819
- /* @__PURE__ */ jsx14(FileJson, {}),
1820
- labels.exportJson
1863
+ action.icon,
1864
+ action.label
1821
1865
  ]
1822
- })
1866
+ }, action.id))
1823
1867
  ]
1824
1868
  })
1825
1869
  })
@@ -1827,7 +1871,12 @@ var DataTableToolbar = ({
1827
1871
  }) : null
1828
1872
  ]
1829
1873
  })
1830
- })
1874
+ }),
1875
+ exportFeature && exportFailed ? /* @__PURE__ */ jsx14("p", {
1876
+ role: "alert",
1877
+ className: "text-destructive text-sm",
1878
+ children: labels.exportFailed ?? dataTableMessages(store.config.locale).exportFailed
1879
+ }) : null
1831
1880
  ]
1832
1881
  });
1833
1882
  };
@@ -2293,7 +2342,7 @@ var GroupActions = ({
2293
2342
  children: /* @__PURE__ */ jsx14(DropdownMenuGroup, {
2294
2343
  children: visible.map((action) => /* @__PURE__ */ jsxs8(DropdownMenuItem, {
2295
2344
  onClick: () => action.onClick(groupId),
2296
- variant: action.variant,
2345
+ variant: action.variant ?? "default",
2297
2346
  children: [
2298
2347
  action.icon,
2299
2348
  " ",
@@ -2796,23 +2845,21 @@ var HydratedDataTable = ({
2796
2845
  setPersistedUiState,
2797
2846
  ...props
2798
2847
  }) => {
2799
- const transientUiAtomRef = useRef(null);
2800
- if (!transientUiAtomRef.current) {
2848
+ const [transientUiAtom] = useState(() => {
2801
2849
  const grouping = props.features?.grouping;
2802
- transientUiAtomRef.current = Atom.make({
2850
+ return Atom.make({
2803
2851
  ...transientUiFromState(DEFAULT_UI_STATE),
2804
2852
  grouping: grouping && grouping.initial ? grouping.initial : []
2805
2853
  });
2806
- }
2807
- const [transientUiState, setTransientUiState] = useAtom(transientUiAtomRef.current);
2854
+ });
2855
+ const [transientUiState, setTransientUiState] = useAtom(transientUiAtom);
2808
2856
  const uiState = useMemo(() => ({ ...persistedUiState, ...transientUiState }), [persistedUiState, transientUiState]);
2809
2857
  const setUiState = useCallback((state) => {
2810
2858
  setPersistedUiState(persistedUiFromState(state));
2811
2859
  setTransientUiState(transientUiFromState(state));
2812
2860
  }, [setPersistedUiState, setTransientUiState]);
2813
2861
  const validatedConfig = resolveConfig(props, (state) => setUiState(state));
2814
- const atomRef = useRef(null);
2815
- if (!atomRef.current) {
2862
+ const [tableAtom] = useState(() => {
2816
2863
  const initial = {
2817
2864
  rowData: props.rowData,
2818
2865
  columnDefs: props.columnDefs,
@@ -2820,12 +2867,11 @@ var HydratedDataTable = ({
2820
2867
  state: initialPublicState(props, uiState),
2821
2868
  controlledState: props.state
2822
2869
  };
2823
- atomRef.current = Atom.make({
2870
+ return Atom.make({
2824
2871
  ...initial,
2825
2872
  model: buildDataTableModel(initial)
2826
2873
  });
2827
- }
2828
- const tableAtom = atomRef.current;
2874
+ });
2829
2875
  const setStore = useAtomSet(tableAtom);
2830
2876
  useLayoutEffect(() => {
2831
2877
  setStore((store) => {
@@ -2886,20 +2932,19 @@ var HydratedDataTable = ({
2886
2932
  };
2887
2933
  var DataTable = (props) => {
2888
2934
  const locale = useKrakstackLocale3(props.locale);
2889
- const persistedUiAtomRef = useRef(null);
2890
- if (!persistedUiAtomRef.current) {
2935
+ const [persistedUiAtom] = useState(() => {
2891
2936
  const pathname = globalThis.window?.location.pathname ?? "/";
2892
2937
  const columnIds = normalizeDataTableColumns(props.columnDefs).map(({ id }) => id).join(",");
2893
2938
  const viewCapability = props.features?.gallery ? "gallery" : "table";
2894
- persistedUiAtomRef.current = Atom.kvs({
2939
+ return Atom.kvs({
2895
2940
  mode: "async",
2896
2941
  runtime: dataTableStorageRuntime,
2897
2942
  key: `data-table:${pathname}:${columnIds}:${viewCapability}:ui`,
2898
2943
  schema: DataTablePersistedUiStateSchema,
2899
2944
  defaultValue: () => persistedUiFromState(DEFAULT_UI_STATE)
2900
2945
  });
2901
- }
2902
- const [persistedUiResult, setPersistedUiState] = useAtom(persistedUiAtomRef.current);
2946
+ });
2947
+ const [persistedUiResult, setPersistedUiState] = useAtom(persistedUiAtom);
2903
2948
  if (!AsyncResult.isSuccess(persistedUiResult) || AsyncResult.isWaiting(persistedUiResult)) {
2904
2949
  return /* @__PURE__ */ jsx14("div", {
2905
2950
  "aria-busy": "true",
@@ -3139,7 +3184,7 @@ var DataTableListMenu = ({
3139
3184
  event.stopPropagation();
3140
3185
  action.onClick(item);
3141
3186
  },
3142
- variant: action.variant,
3187
+ variant: action.variant ?? "default",
3143
3188
  children: [
3144
3189
  action.icon,
3145
3190
  action.name
@@ -22,6 +22,7 @@ export type GoogleMapMessages = {
22
22
  errorBody: string;
23
23
  zoomIn: string;
24
24
  zoomOut: string;
25
+ showArea: string;
25
26
  };
26
27
  export type GoogleMapMessageTranslations = Partial<Record<string, Partial<GoogleMapMessages>>>;
27
28
  export type GoogleMapProps = {
@@ -32,6 +33,8 @@ export type GoogleMapProps = {
32
33
  radius?: GoogleMapRadiusOptions | undefined;
33
34
  interactive?: boolean | undefined;
34
35
  zoomControls?: boolean | undefined;
36
+ fitRadius?: boolean | undefined;
37
+ fitPadding?: number | undefined;
35
38
  minZoom?: number | undefined;
36
39
  maxZoom?: number | undefined;
37
40
  className?: string | undefined;
@@ -55,11 +58,18 @@ type GoogleMapOptions = {
55
58
  zoomControl: boolean;
56
59
  };
57
60
  type GoogleMapInstance = {
61
+ fitBounds: (bounds: GoogleMapBounds, padding: number) => void;
58
62
  getZoom: () => number | undefined;
59
63
  setCenter: (center: GoogleMapPoint) => void;
60
64
  setOptions: (options: Partial<GoogleMapOptions>) => void;
61
65
  setZoom: (zoom: number) => void;
62
66
  };
67
+ type GoogleMapBounds = {
68
+ north: number;
69
+ south: number;
70
+ east: number;
71
+ west: number;
72
+ };
63
73
  type GoogleMarkerInstance = {
64
74
  setIcon: (icon?: string) => void;
65
75
  setMap: (map: GoogleMapInstance | null) => void;
@@ -67,6 +77,9 @@ type GoogleMarkerInstance = {
67
77
  };
68
78
  type GoogleCircleStyleOptions = Omit<GoogleMapRadiusOptions, "radiusMeters">;
69
79
  type GoogleCircleInstance = {
80
+ getBounds: () => {
81
+ toJSON: () => GoogleMapBounds;
82
+ } | undefined;
70
83
  setCenter: (center: GoogleMapPoint) => void;
71
84
  setMap: (map: GoogleMapInstance | null) => void;
72
85
  setOptions: (options: GoogleCircleStyleOptions) => void;
@@ -96,5 +109,5 @@ export type GoogleMapsApi = {
96
109
  };
97
110
  };
98
111
  };
99
- export declare function GoogleMap({ apiKey, center, zoom, marker, radius, interactive, zoomControls, minZoom, maxZoom, className, messages: messageOverrides, locale: localeOverride, }: GoogleMapProps): import("react").JSX.Element;
112
+ export declare function GoogleMap({ apiKey, center, zoom, marker, radius, interactive, zoomControls, fitRadius, fitPadding, minZoom, maxZoom, className, messages: messageOverrides, locale: localeOverride, }: GoogleMapProps): import("react").JSX.Element;
100
113
  export {};