@cosmoops/connect-sdk 0.24.1 → 0.26.0
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/CHANGELOG.md +133 -0
- package/dist/cjs/client/components/access-gate.js +31 -5
- package/dist/cjs/client/components/account-sessions.js +4 -3
- package/dist/cjs/client/components/app-shell.js +2 -1
- package/dist/cjs/client/components/booking-calendar.js +2 -1
- package/dist/cjs/client/components/cursor-pagination.js +3 -2
- package/dist/cjs/client/components/filter-tabs.js +23 -5
- package/dist/cjs/client/components/grievance-notice.js +2 -1
- package/dist/cjs/client/components/logo.js +3 -2
- package/dist/cjs/client/components/one-tap.js +2 -1
- package/dist/cjs/client/components/plans.js +3 -2
- package/dist/cjs/client/components/readiness.js +4 -3
- package/dist/cjs/client/components/shell-context.js +2 -1
- package/dist/cjs/client/components/side-nav.js +2 -1
- package/dist/cjs/client/components/sidebar.js +2 -1
- package/dist/cjs/client/components/subscription.js +5 -4
- package/dist/cjs/client/components/usage-meter.js +2 -1
- package/dist/cjs/client/components/user-menu.js +3 -2
- package/dist/cjs/client/provider.js +28 -7
- package/dist/cjs/client/use-active-path.js +12 -1
- package/dist/cjs/cn.js +53 -0
- package/dist/cjs/next/index.js +41 -3
- package/dist/cjs/server/client.js +1 -1
- package/dist/cjs/server/http.js +3 -1
- package/dist/cjs/server/resources/identity.js +12 -2
- package/dist/cjs/store.js +14 -1
- package/dist/cjs/ui/empty-state.js +2 -1
- package/dist/cjs/ui/index.js +8 -1
- package/dist/cjs/ui/page-header.js +2 -1
- package/dist/cjs/ui/page-shell.js +3 -2
- package/dist/cjs/ui/section.js +2 -1
- package/dist/cjs/ui/setting-row.js +2 -1
- package/dist/esm/client/components/access-gate.js +33 -7
- package/dist/esm/client/components/account-sessions.js +4 -3
- package/dist/esm/client/components/app-shell.js +2 -1
- package/dist/esm/client/components/booking-calendar.js +2 -1
- package/dist/esm/client/components/cursor-pagination.js +3 -2
- package/dist/esm/client/components/filter-tabs.js +23 -5
- package/dist/esm/client/components/grievance-notice.js +2 -1
- package/dist/esm/client/components/logo.js +3 -2
- package/dist/esm/client/components/one-tap.js +2 -1
- package/dist/esm/client/components/plans.js +3 -2
- package/dist/esm/client/components/readiness.js +4 -3
- package/dist/esm/client/components/shell-context.js +2 -1
- package/dist/esm/client/components/side-nav.js +2 -1
- package/dist/esm/client/components/sidebar.js +2 -1
- package/dist/esm/client/components/subscription.js +5 -4
- package/dist/esm/client/components/usage-meter.js +2 -1
- package/dist/esm/client/components/user-menu.js +3 -2
- package/dist/esm/client/provider.js +28 -7
- package/dist/esm/client/use-active-path.js +12 -1
- package/dist/esm/cn.js +48 -0
- package/dist/esm/next/index.js +41 -3
- package/dist/esm/server/client.js +1 -1
- package/dist/esm/server/http.js +3 -1
- package/dist/esm/server/resources/identity.js +12 -2
- package/dist/esm/store.js +14 -1
- package/dist/esm/ui/empty-state.js +2 -1
- package/dist/esm/ui/index.js +6 -0
- package/dist/esm/ui/page-header.js +2 -1
- package/dist/esm/ui/page-shell.js +3 -2
- package/dist/esm/ui/section.js +2 -1
- package/dist/esm/ui/setting-row.js +2 -1
- package/dist/theme.css +53 -0
- package/dist/types/client/components/access-gate.d.ts +35 -4
- package/dist/types/client/components/cursor-pagination.d.ts +6 -1
- package/dist/types/client/components/filter-tabs.d.ts +9 -3
- package/dist/types/cn.d.ts +48 -0
- package/dist/types/next/index.d.ts +12 -2
- package/dist/types/server/client.d.ts +1 -1
- package/dist/types/server/http.d.ts +22 -0
- package/dist/types/store.d.ts +6 -0
- package/dist/types/ui/index.d.ts +6 -0
- package/dist/types/ui/page-shell.d.ts +7 -1
- package/package.json +1 -1
package/dist/esm/store.js
CHANGED
|
@@ -97,6 +97,12 @@ const EQUALITY_OPERATORS = new Set(["==", "in"]);
|
|
|
97
97
|
*
|
|
98
98
|
* Throws for a query Firestore cannot serve at all - two `array-contains`
|
|
99
99
|
* filters, ranges over two fields, or a range ordered by a different field.
|
|
100
|
+
*
|
|
101
|
+
* A **range bounded on both sides** over one field needs no declaration: two
|
|
102
|
+
* filters, one field, and the automatic single-field index serves it. Ask this
|
|
103
|
+
* function with the filters you actually send rather than with one of the two
|
|
104
|
+
* bounds - the answer is the same, and modelling the range as a single `>=`
|
|
105
|
+
* is how a schema comes to declare a shape its own runtime does not make.
|
|
100
106
|
*/
|
|
101
107
|
export function storeIndexFor(query) {
|
|
102
108
|
const filters = query.filters ?? [];
|
|
@@ -118,7 +124,14 @@ export function storeIndexFor(query) {
|
|
|
118
124
|
}
|
|
119
125
|
const ordered = orderBy?.field ?? rangeField;
|
|
120
126
|
const distinct = new Set([...filters.map((filter) => filter.field), ...(ordered ? [ordered] : [])]);
|
|
121
|
-
|
|
127
|
+
// Distinct *fields*, not filter count. A two-sided range over one field -
|
|
128
|
+
// `day >= from AND day <= to ORDER BY day`, the shape of every date window -
|
|
129
|
+
// is exactly what an automatic single-field index is for. Counting filters
|
|
130
|
+
// instead answered `[day asc]`: an index Firestore will not build, since a
|
|
131
|
+
// composite needs two fields, and which is by definition the automatic one
|
|
132
|
+
// already serving the query. `array-contains` is the exception, because an
|
|
133
|
+
// array field's automatic index does not serve an inequality on that field.
|
|
134
|
+
if (distinct.size <= 1 && (arrayContains.length === 0 || filters.length <= 1))
|
|
122
135
|
return null;
|
|
123
136
|
const fields = [
|
|
124
137
|
...[...new Set(equalities.map((filter) => filter.field))]
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Card } from "@heroui/react";
|
|
3
|
+
import { cn } from "../cn.js";
|
|
3
4
|
/**
|
|
4
5
|
* What a list, grid or table shows when it has nothing to show.
|
|
5
6
|
*
|
|
@@ -21,5 +22,5 @@ export function ConnectEmptyState({ icon, title, description, action, variant =
|
|
|
21
22
|
const body = (_jsxs("div", { className: "flex flex-col items-center text-center", children: [icon && (_jsx("span", { "aria-hidden": true, className: "mb-3 text-muted [&>svg]:size-8", children: icon })), _jsx("p", { className: "text-sm font-medium text-foreground", children: title }), description && _jsx("p", { className: "mt-1 max-w-md text-sm text-muted", children: description }), action && _jsx("div", { className: "mt-3", children: action })] }));
|
|
22
23
|
if (variant === "plain")
|
|
23
24
|
return _jsx("div", { className: className, children: body });
|
|
24
|
-
return (_jsx(Card, { className:
|
|
25
|
+
return (_jsx(Card, { className: cn("p-5 sm:p-8", variant === "dashed" ? "border-dashed" : "", className), children: body }));
|
|
25
26
|
}
|
package/dist/esm/ui/index.js
CHANGED
|
@@ -32,3 +32,9 @@ export { ConnectSection } from "./section.js";
|
|
|
32
32
|
export { ConnectEmptyState } from "./empty-state.js";
|
|
33
33
|
export { ConnectSettingRow } from "./setting-row.js";
|
|
34
34
|
export { ConnectStatusChip } from "./status-chip.js";
|
|
35
|
+
/**
|
|
36
|
+
* Merge class names the way these components do internally, so a utility you
|
|
37
|
+
* pass wins over a conflicting default instead of racing it in the stylesheet.
|
|
38
|
+
* Use it when composing your own components on top of these.
|
|
39
|
+
*/
|
|
40
|
+
export { cn } from "../cn.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../cn.js";
|
|
2
3
|
/**
|
|
3
4
|
* The title / description / action block a page opens with.
|
|
4
5
|
*
|
|
@@ -20,5 +21,5 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
20
21
|
* why it lives in `@cosmoops/connect-sdk/ui` rather than `/client`.
|
|
21
22
|
*/
|
|
22
23
|
export function ConnectPageHeader({ title, description, action, clamp = true, className, }) {
|
|
23
|
-
return (_jsxs("header", { className:
|
|
24
|
+
return (_jsxs("header", { className: cn("flex items-start justify-between gap-3", className), children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("h2", { className: "flex items-center gap-2 text-lg font-semibold", children: title }), description && (_jsx("p", { className: `mt-2 text-sm text-muted ${clamp ? "line-clamp-2 sm:line-clamp-none" : ""}`, children: description }))] }), action && _jsx("div", { className: "shrink-0", children: action })] }));
|
|
24
25
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Suspense } from "react";
|
|
3
3
|
import { ConnectPageHeader } from "./page-header.js";
|
|
4
|
+
import { cn } from "../cn.js";
|
|
4
5
|
/**
|
|
5
6
|
* The frame a page opens with: its vertical rhythm, its header, and the
|
|
6
7
|
* suspended regions beneath.
|
|
@@ -20,8 +21,8 @@ import { ConnectPageHeader } from "./page-header.js";
|
|
|
20
21
|
*
|
|
21
22
|
* `space-y-5` is the page gap, owned here so it has exactly one definition.
|
|
22
23
|
*/
|
|
23
|
-
export function ConnectPageShell({ title, description, action, clamp, above, children, }) {
|
|
24
|
-
return (_jsxs("div", { className: "space-y-5", children: [above, title !== undefined && (_jsx(ConnectPageHeader, { title: title, description: description, action: action, clamp: clamp })), children] }));
|
|
24
|
+
export function ConnectPageShell({ title, description, action, clamp, above, children, className, }) {
|
|
25
|
+
return (_jsxs("div", { className: cn("space-y-5", className), children: [above, title !== undefined && (_jsx(ConnectPageHeader, { title: title, description: description, action: action, clamp: clamp })), children] }));
|
|
25
26
|
}
|
|
26
27
|
/**
|
|
27
28
|
* The page header, for a detail route that can only name itself once its data
|
package/dist/esm/ui/section.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Card } from "@heroui/react";
|
|
3
|
+
import { cn } from "../cn.js";
|
|
3
4
|
/**
|
|
4
5
|
* One titled block inside a page: a card, its heading, what it is for, and the
|
|
5
6
|
* one control that acts on it.
|
|
@@ -13,5 +14,5 @@ import { Card } from "@heroui/react";
|
|
|
13
14
|
* block, so its position does not move with the length of the description.
|
|
14
15
|
*/
|
|
15
16
|
export function ConnectSection({ title, description, action, titleId, className, bodyClassName, children, }) {
|
|
16
|
-
return (_jsxs(Card, { className:
|
|
17
|
+
return (_jsxs(Card, { className: cn("p-4 sm:p-5", className), children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("h3", { id: titleId, className: "text-base font-semibold", children: title }), description && _jsx("p", { className: "mt-1 text-sm text-muted", children: description })] }), action && _jsx("div", { className: "shrink-0", children: action })] }), children && _jsx("div", { className: bodyClassName ?? "mt-4 space-y-4", children: children })] }));
|
|
17
18
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../cn.js";
|
|
2
3
|
/**
|
|
3
4
|
* One labelled setting inside a `ConnectSection`.
|
|
4
5
|
*
|
|
@@ -7,5 +8,5 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
7
8
|
* intrinsic width so truncation lands on the description.
|
|
8
9
|
*/
|
|
9
10
|
export function ConnectSettingRow({ title, description, action, children, className, }) {
|
|
10
|
-
return (_jsxs("div", { className:
|
|
11
|
+
return (_jsxs("div", { className: cn("flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between", className), children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-sm font-medium text-foreground", children: title }), description && _jsx("p", { className: "mt-1 text-sm text-muted", children: description }), children && _jsx("div", { className: "mt-3", children: children })] }), action && _jsx("div", { className: "shrink-0 sm:pl-4", children: action })] }));
|
|
11
12
|
}
|
package/dist/theme.css
CHANGED
|
@@ -106,6 +106,34 @@
|
|
|
106
106
|
--overlay: oklch(21% 0.002 145.67);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
/* ---------------------------------------------------------------------------
|
|
110
|
+
* Radius, as one number.
|
|
111
|
+
*
|
|
112
|
+
* HeroUI derives its whole radius scale from `--radius`: `rounded-sm` is
|
|
113
|
+
* `--radius * 0.5`, `rounded-lg` is `--radius * 1`, and `rounded-3xl` - what
|
|
114
|
+
* `.button` resolves to - is `--radius * 3`. The scale is a set of *ratios*,
|
|
115
|
+
* so this token is the only magnitude in it and one value decides every
|
|
116
|
+
* corner in every component.
|
|
117
|
+
*
|
|
118
|
+
* The stock pair is `--radius: 0.5rem` with `--field-radius` derived at
|
|
119
|
+
* `calc(var(--radius) * 1.5)`. Connect's own app has always run at `0.25rem`,
|
|
120
|
+
* but the shared theme carried neither token - so every partner platform
|
|
121
|
+
* silently inherited HeroUI's stock values and drew its cards and inputs at
|
|
122
|
+
* twice Connect's radius. That was the one place platforms sharing this
|
|
123
|
+
* palette visibly disagreed. Stating it here is what makes "the same theme"
|
|
124
|
+
* mean the same shapes as well as the same colours.
|
|
125
|
+
*
|
|
126
|
+
* `--field-radius` is set rather than derived, deliberately: at 1.5x an input
|
|
127
|
+
* rounds harder than the card holding it, which inverts the nesting the
|
|
128
|
+
* radius scale exists to express.
|
|
129
|
+
*
|
|
130
|
+
* Radius does not vary by theme, so this sits outside the light/dark blocks.
|
|
131
|
+
* ------------------------------------------------------------------------ */
|
|
132
|
+
:root {
|
|
133
|
+
--radius: 0.25rem;
|
|
134
|
+
--field-radius: 0.25rem;
|
|
135
|
+
}
|
|
136
|
+
|
|
109
137
|
body {
|
|
110
138
|
background: var(--background);
|
|
111
139
|
color: var(--foreground);
|
|
@@ -197,3 +225,28 @@ body {
|
|
|
197
225
|
animation: none;
|
|
198
226
|
}
|
|
199
227
|
}
|
|
228
|
+
|
|
229
|
+
/* ---------------------------------------------------------------------------
|
|
230
|
+
* A hand-rolled button still looks pressable.
|
|
231
|
+
*
|
|
232
|
+
* Tailwind v4's Preflight sets `cursor: default` on `button`, reversing v3 -
|
|
233
|
+
* and HeroUI restores the pointer only on its *own* classes, through
|
|
234
|
+
* `--cursor-interactive` on `.button`, `.chip` triggers and friends. So a
|
|
235
|
+
* plain `<button>` - which every app writes: a menu row, a filter chip, a
|
|
236
|
+
* calendar day, a card that is really a press target - renders with an arrow
|
|
237
|
+
* and reads as text.
|
|
238
|
+
*
|
|
239
|
+
* That is not a per-button oversight to fix per button. It is the default
|
|
240
|
+
* being wrong for this codebase, so it is corrected once, here, where every
|
|
241
|
+
* application platform importing this theme picks it up.
|
|
242
|
+
*
|
|
243
|
+
* `:where()` keeps the specificity at zero, so any component rule - including
|
|
244
|
+
* HeroUI's own disabled handling, which swaps in `--cursor-disabled` - still
|
|
245
|
+
* wins outright. The `:not()`s are belt and braces for the un-classed case,
|
|
246
|
+
* where there is no component rule to defer to.
|
|
247
|
+
* ------------------------------------------------------------------------ */
|
|
248
|
+
@layer base {
|
|
249
|
+
:where(button, [role="button"]):not(:disabled):not([aria-disabled="true"]):not([data-disabled="true"]) {
|
|
250
|
+
cursor: pointer;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
@@ -56,7 +56,16 @@ export interface ConnectAccessGateProps {
|
|
|
56
56
|
children: ReactNode;
|
|
57
57
|
/** Replaces the default card for every hard-block state (`suspended`/`no_subscription`/`onboarding_pending`/`blocked`) uniformly. For a different card per state, call `useConnectAccessGate()` directly instead. */
|
|
58
58
|
fallback?: ReactNode;
|
|
59
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* Shown while the first read is in flight, **in place of** `children`.
|
|
61
|
+
*
|
|
62
|
+
* Opt-in, and rarely what you want. The default is to render `children`
|
|
63
|
+
* during that window - see the component's doc comment on why a blanket
|
|
64
|
+
* placeholder over a whole application is worse than the flash it avoids.
|
|
65
|
+
* Pass this only for a gate wrapped around one small region whose blocked
|
|
66
|
+
* and unblocked states differ enough that showing the unblocked one first
|
|
67
|
+
* would mislead.
|
|
68
|
+
*/
|
|
60
69
|
loading?: ReactNode;
|
|
61
70
|
className?: string;
|
|
62
71
|
}
|
|
@@ -85,9 +94,31 @@ export interface ConnectAccessGateProps {
|
|
|
85
94
|
* entire app must not hide a public landing page behind an empty gate. Your
|
|
86
95
|
* own sign-in flow decides what a signed-out visitor sees.
|
|
87
96
|
*
|
|
97
|
+
* **While the verdict is unknown, `children` render.** The two reads behind
|
|
98
|
+
* this gate are client-side round trips that happen on *every* page load, and
|
|
99
|
+
* this component is mounted around an entire application - so blocking on
|
|
100
|
+
* them replaced every partner's whole page with one placeholder until both
|
|
101
|
+
* came back. Anything the host had streamed underneath was discarded: per-
|
|
102
|
+
* section skeletons never painted, and a page built as a static shell with
|
|
103
|
+
* suspended regions arrived all at once anyway. That is the common path, paid
|
|
104
|
+
* by every workspace on every navigation.
|
|
105
|
+
*
|
|
106
|
+
* Rendering `children` instead makes the loading case agree with the two
|
|
107
|
+
* cases either side of it. A *failed* read already falls through to `clear`
|
|
108
|
+
* ("fail open rather than hard-block the whole app on a network hiccup", in
|
|
109
|
+
* `useConnectAccessGate`), and a signed-out visitor already renders
|
|
110
|
+
* `children` too - "we do not know yet" was the one unknown treated as a
|
|
111
|
+
* refusal.
|
|
112
|
+
*
|
|
113
|
+
* What it costs is honest and small: a genuinely blocked workspace sees its
|
|
114
|
+
* app for the length of one round trip before the block card replaces it.
|
|
115
|
+
* That is the rare path, it is self-correcting, and it is the same exposure
|
|
116
|
+
* the failed-read branch has always accepted. Pass `loading` to opt back into
|
|
117
|
+
* a placeholder for a gate around one small region.
|
|
118
|
+
*
|
|
88
119
|
* **This is a UX gate, not a security boundary** - same caveat as every
|
|
89
|
-
* other gate in this package
|
|
90
|
-
* `client.entitlements.getReadiness` on your
|
|
91
|
-
* thing.
|
|
120
|
+
* other gate in this package, and the reason the trade above is available at
|
|
121
|
+
* all. The authoritative check is `client.entitlements.getReadiness` on your
|
|
122
|
+
* server, before you do the thing.
|
|
92
123
|
*/
|
|
93
124
|
export declare function ConnectAccessGate({ children, fallback, loading, className }: ConnectAccessGateProps): import("react").JSX.Element;
|
|
@@ -8,6 +8,11 @@ export interface ConnectCursorPaginationProps {
|
|
|
8
8
|
onNext: (cursor: string) => void;
|
|
9
9
|
onPrevious: () => void;
|
|
10
10
|
isPending?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Merged onto the root, so a utility here replaces the conflicting default
|
|
13
|
+
* rather than sitting beside it. See `cn`.
|
|
14
|
+
*/
|
|
15
|
+
className?: string;
|
|
11
16
|
/** Singular/plural noun for the count, e.g. `["contact", "contacts"]`. */
|
|
12
17
|
noun?: readonly [string, string];
|
|
13
18
|
}
|
|
@@ -24,4 +29,4 @@ export interface ConnectCursorPaginationProps {
|
|
|
24
29
|
* filtering or paging changed the result set. Without it the table silently
|
|
25
30
|
* swaps its contents.
|
|
26
31
|
*/
|
|
27
|
-
export declare function ConnectCursorPagination({ shown, nextCursor, hasPrevious, onNext, onPrevious, isPending, noun, }: Readonly<ConnectCursorPaginationProps>): import("react").JSX.Element;
|
|
32
|
+
export declare function ConnectCursorPagination({ shown, nextCursor, hasPrevious, onNext, onPrevious, isPending, noun, className, }: Readonly<ConnectCursorPaginationProps>): import("react").JSX.Element;
|
|
@@ -8,6 +8,11 @@ export interface ConnectFilterTabsProps<TValue extends string> {
|
|
|
8
8
|
options: ReadonlyArray<ConnectFilterTabsOption<TValue>>;
|
|
9
9
|
value: TValue | null;
|
|
10
10
|
onChange: (value: TValue | null) => void;
|
|
11
|
+
/**
|
|
12
|
+
* Merged onto the root, so a utility here replaces the conflicting default
|
|
13
|
+
* rather than sitting beside it. See `cn`.
|
|
14
|
+
*/
|
|
15
|
+
className?: string;
|
|
11
16
|
/** Names the group for assistive technology - "Filter contacts by lifecycle". */
|
|
12
17
|
label: string;
|
|
13
18
|
}
|
|
@@ -20,7 +25,8 @@ export interface ConnectFilterTabsProps<TValue extends string> {
|
|
|
20
25
|
* selected one should be announced as selected. A group of `<button>`s gets
|
|
21
26
|
* neither, and that is the usual hand-rolled shape.
|
|
22
27
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
28
|
+
* Wraps rather than scrolling on a narrow viewport. See the note on the
|
|
29
|
+
* container below: a horizontal scroller nested in a vertically scrolling
|
|
30
|
+
* page hides its overflow rather than deferring it.
|
|
25
31
|
*/
|
|
26
|
-
export declare function ConnectFilterTabs<TValue extends string>({ options, value, onChange, label, }: Readonly<ConnectFilterTabsProps<TValue>>): import("react").JSX.Element;
|
|
32
|
+
export declare function ConnectFilterTabs<TValue extends string>({ options, value, onChange, label, className, }: Readonly<ConnectFilterTabsProps<TValue>>): import("react").JSX.Element;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge class names so a caller's Tailwind utility actually wins.
|
|
3
|
+
*
|
|
4
|
+
* Every component in this package carries default utilities, and a partner
|
|
5
|
+
* styling one reaches for `className`. Concatenating the two does not do what
|
|
6
|
+
* it looks like it does:
|
|
7
|
+
*
|
|
8
|
+
* ```tsx
|
|
9
|
+
* // ConnectBookingCalendar with className="gap-8"
|
|
10
|
+
* <div className={`flex flex-col gap-4 ${className}`} />
|
|
11
|
+
* // -> class="flex flex-col gap-4 gap-8"
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Both rules are now in the stylesheet and both match, so the winner is
|
|
15
|
+
* decided by **source order in the generated CSS**, not by the order they
|
|
16
|
+
* appear in the attribute. Tailwind sorts its output by its own internal
|
|
17
|
+
* ordering, so whether `gap-8` beats `gap-4` depends on which utility Tailwind
|
|
18
|
+
* happens to emit later - `p-8` over `px-3 py-2` lands one way, `gap-8` over
|
|
19
|
+
* `gap-4` the other. An override that works on one prop and silently does
|
|
20
|
+
* nothing on the next is worse than one that never works, because nobody
|
|
21
|
+
* suspects the mechanism.
|
|
22
|
+
*
|
|
23
|
+
* `cn` resolves it by *removing* the conflicting default rather than adding to
|
|
24
|
+
* it - `cn("flex flex-col gap-4", "gap-8")` is `"flex flex-col gap-8"`, and
|
|
25
|
+
* `cn("px-3 py-2", "p-8")` is `"p-8"`. That is a real understanding of
|
|
26
|
+
* Tailwind's shorthand groups, which is why it is `tailwind-merge` and not a
|
|
27
|
+
* `join(" ")`.
|
|
28
|
+
*
|
|
29
|
+
* Re-exported rather than reimplemented. The implementation is
|
|
30
|
+
* `tailwind-variants`' `cn`, which `@heroui/react` re-exports and which every
|
|
31
|
+
* HeroUI component uses internally through `tv` - so a partner's override is
|
|
32
|
+
* resolved by exactly the same rules whether it lands on one of these
|
|
33
|
+
* components or on a HeroUI one. It is reached through `@heroui/react` rather
|
|
34
|
+
* than `tailwind-variants` directly because HeroUI is already a declared peer
|
|
35
|
+
* of both UI entry points, and importing a package this manifest does not name
|
|
36
|
+
* would be relying on someone else's transitive tree.
|
|
37
|
+
*
|
|
38
|
+
* Safe in the server-rendered `./ui` entry: the `@heroui/react` barrel carries
|
|
39
|
+
* no top-level `"use client"` (only its individual components do), so `cn`
|
|
40
|
+
* arrives as a real function rather than a client-reference proxy. That
|
|
41
|
+
* distinction matters for a *function* in a way it does not for a component -
|
|
42
|
+
* `ui/section.tsx` has always imported `Card` from the same barrel.
|
|
43
|
+
*
|
|
44
|
+
* Not exported from the root entry, which is isomorphic and React-free. It is
|
|
45
|
+
* public from `./ui`, because a partner composing on top of these primitives
|
|
46
|
+
* needs the same merge the primitives use.
|
|
47
|
+
*/
|
|
48
|
+
export { cn } from "@heroui/react";
|
|
@@ -93,8 +93,18 @@ export { handle as GET, handle as POST };
|
|
|
93
93
|
* any other request-scoped read - a page that awaits it above every boundary
|
|
94
94
|
* gives up its static shell.
|
|
95
95
|
*
|
|
96
|
-
* Returns `null`
|
|
97
|
-
*
|
|
96
|
+
* Returns `null` when there is **no session** - no cookie, or one Connect
|
|
97
|
+
* reports as expired, revoked or issued to another application. A page guard
|
|
98
|
+
* needs no try/catch for those, which are the cases where sending someone
|
|
99
|
+
* back through sign-in is the right answer.
|
|
100
|
+
*
|
|
101
|
+
* **It throws on anything else**, and that is deliberate. It previously
|
|
102
|
+
* swallowed every failure into `null`, so a rate-limited or unreachable
|
|
103
|
+
* Connect read as "signed out" - and `requireConnectSession` then redirected a
|
|
104
|
+
* signed-in user to sign-in, whose OAuth round trip spent more of the same
|
|
105
|
+
* exhausted budget and 429'd again on return. A login loop that could only end
|
|
106
|
+
* when the rate-limit window rolled. An error boundary is the honest outcome
|
|
107
|
+
* for "could not ask", and unlike a spurious sign-out it is recoverable.
|
|
98
108
|
*
|
|
99
109
|
* **Memoized per request for the plain, zero-argument call** - the shape
|
|
100
110
|
* every page, layout and Server Action actually uses - via React's `cache()`.
|
|
@@ -25,6 +25,28 @@ export interface RequestSpec {
|
|
|
25
25
|
*/
|
|
26
26
|
raw?: boolean;
|
|
27
27
|
signal?: AbortSignal;
|
|
28
|
+
/**
|
|
29
|
+
* Force this request into the retry path even though it is a `POST`.
|
|
30
|
+
*
|
|
31
|
+
* The default rule below - retry anything that is not a `POST`, plus a
|
|
32
|
+
* `POST` carrying an `Idempotency-Key` - is right about writes and wrong
|
|
33
|
+
* about the handful of endpoints that are reads wearing a `POST` because
|
|
34
|
+
* their input is a credential that must not sit in a URL. `POST /sso/user`
|
|
35
|
+
* is the one that matters: it takes a session token and answers who that
|
|
36
|
+
* is, replaying it changes nothing, and it is the **most-called endpoint on
|
|
37
|
+
* the platform** because the SDK asks it on every request a partner serves.
|
|
38
|
+
*
|
|
39
|
+
* It was therefore also the only one that could never retry a 429 - the
|
|
40
|
+
* transport had a correct `Retry-After` backoff that this endpoint was
|
|
41
|
+
* excluded from by the shape of its verb. A throttled read went straight
|
|
42
|
+
* back to the caller as a failure, and both session helpers turned that
|
|
43
|
+
* into "signed out".
|
|
44
|
+
*
|
|
45
|
+
* Set it **only** where replaying genuinely cannot change server state.
|
|
46
|
+
* `POST /sso/token` must never set it: it consumes a one-time authorization
|
|
47
|
+
* code, and a replay is a second exchange of a code that no longer exists.
|
|
48
|
+
*/
|
|
49
|
+
idempotent?: boolean;
|
|
28
50
|
}
|
|
29
51
|
export interface TransportOptions {
|
|
30
52
|
baseUrl: string;
|
package/dist/types/store.d.ts
CHANGED
|
@@ -82,6 +82,12 @@ export interface StoreIndexField {
|
|
|
82
82
|
*
|
|
83
83
|
* Throws for a query Firestore cannot serve at all - two `array-contains`
|
|
84
84
|
* filters, ranges over two fields, or a range ordered by a different field.
|
|
85
|
+
*
|
|
86
|
+
* A **range bounded on both sides** over one field needs no declaration: two
|
|
87
|
+
* filters, one field, and the automatic single-field index serves it. Ask this
|
|
88
|
+
* function with the filters you actually send rather than with one of the two
|
|
89
|
+
* bounds - the answer is the same, and modelling the range as a single `>=`
|
|
90
|
+
* is how a schema comes to declare a shape its own runtime does not make.
|
|
85
91
|
*/
|
|
86
92
|
export declare function storeIndexFor(query: {
|
|
87
93
|
filters?: readonly StoreQueryFilter[];
|
package/dist/types/ui/index.d.ts
CHANGED
|
@@ -38,3 +38,9 @@ export { ConnectSettingRow } from "./setting-row.js";
|
|
|
38
38
|
export type { ConnectSettingRowProps } from "./setting-row.js";
|
|
39
39
|
export { ConnectStatusChip } from "./status-chip.js";
|
|
40
40
|
export type { ConnectStatusTone, ConnectStatusPresentation } from "./status-chip.js";
|
|
41
|
+
/**
|
|
42
|
+
* Merge class names the way these components do internally, so a utility you
|
|
43
|
+
* pass wins over a conflicting default instead of racing it in the stylesheet.
|
|
44
|
+
* Use it when composing your own components on top of these.
|
|
45
|
+
*/
|
|
46
|
+
export { cn } from "../cn.js";
|
|
@@ -7,6 +7,12 @@ export interface ConnectPageShellProps {
|
|
|
7
7
|
clamp?: boolean;
|
|
8
8
|
/** Rendered above the header - a breadcrumb trail, a back link, a banner. */
|
|
9
9
|
above?: ReactNode;
|
|
10
|
+
/**
|
|
11
|
+
* Merged onto the page root, so a utility here beats the default rather than
|
|
12
|
+
* sitting beside it - `className="space-y-8"` really does widen the page gap.
|
|
13
|
+
* See `cn`.
|
|
14
|
+
*/
|
|
15
|
+
className?: string;
|
|
10
16
|
children?: ReactNode;
|
|
11
17
|
}
|
|
12
18
|
/**
|
|
@@ -28,7 +34,7 @@ export interface ConnectPageShellProps {
|
|
|
28
34
|
*
|
|
29
35
|
* `space-y-5` is the page gap, owned here so it has exactly one definition.
|
|
30
36
|
*/
|
|
31
|
-
export declare function ConnectPageShell({ title, description, action, clamp, above, children, }: Readonly<ConnectPageShellProps>): import("react").JSX.Element;
|
|
37
|
+
export declare function ConnectPageShell({ title, description, action, clamp, above, children, className, }: Readonly<ConnectPageShellProps>): import("react").JSX.Element;
|
|
32
38
|
export declare namespace ConnectPageShell {
|
|
33
39
|
var Header: typeof ConnectPageHeader;
|
|
34
40
|
var Async: ({ fallback, children, }: Readonly<{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmoops/connect-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "Official server-side SDK for the CosmoOps Connect platform API - Login with Connect, entitlements, billing and metering.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "commonjs",
|