@cosmoops/connect-sdk 0.1.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.
Files changed (126) hide show
  1. package/LICENSE +34 -0
  2. package/README.md +446 -0
  3. package/dist/cjs/cli/bin.js +16 -0
  4. package/dist/cjs/cli/init.js +179 -0
  5. package/dist/cjs/client/components/account-sessions.js +112 -0
  6. package/dist/cjs/client/components/apps-grid.js +46 -0
  7. package/dist/cjs/client/components/auth-buttons.js +42 -0
  8. package/dist/cjs/client/components/feature-gate.js +61 -0
  9. package/dist/cjs/client/components/link-button.js +24 -0
  10. package/dist/cjs/client/components/logo.js +27 -0
  11. package/dist/cjs/client/components/plan-pricing.js +166 -0
  12. package/dist/cjs/client/components/plans-page.js +39 -0
  13. package/dist/cjs/client/components/plans.js +212 -0
  14. package/dist/cjs/client/components/readiness.js +117 -0
  15. package/dist/cjs/client/components/subscribe-dialog.js +60 -0
  16. package/dist/cjs/client/components/subscription.js +189 -0
  17. package/dist/cjs/client/components/usage-meter.js +68 -0
  18. package/dist/cjs/client/components/user-menu.js +172 -0
  19. package/dist/cjs/client/index.js +99 -0
  20. package/dist/cjs/client/mark.js +16 -0
  21. package/dist/cjs/client/provider.js +259 -0
  22. package/dist/cjs/client/use-checkout.js +173 -0
  23. package/dist/cjs/errors.js +74 -0
  24. package/dist/cjs/format-relative-time.js +18 -0
  25. package/dist/cjs/helpers.js +30 -0
  26. package/dist/cjs/index.js +22 -0
  27. package/dist/cjs/next/index.js +311 -0
  28. package/dist/cjs/package.json +3 -0
  29. package/dist/cjs/server/client.js +114 -0
  30. package/dist/cjs/server/handler.js +333 -0
  31. package/dist/cjs/server/http.js +160 -0
  32. package/dist/cjs/server/index.js +18 -0
  33. package/dist/cjs/server/resources/access.js +28 -0
  34. package/dist/cjs/server/resources/ai.js +36 -0
  35. package/dist/cjs/server/resources/apps.js +30 -0
  36. package/dist/cjs/server/resources/billing.js +71 -0
  37. package/dist/cjs/server/resources/drive.js +39 -0
  38. package/dist/cjs/server/resources/entitlements.js +49 -0
  39. package/dist/cjs/server/resources/identity.js +194 -0
  40. package/dist/cjs/server/resources/logs.js +41 -0
  41. package/dist/cjs/server/resources/otp.js +29 -0
  42. package/dist/cjs/server/resources/sessions.js +82 -0
  43. package/dist/cjs/types.js +15 -0
  44. package/dist/esm/cli/bin.js +13 -0
  45. package/dist/esm/cli/init.js +174 -0
  46. package/dist/esm/client/components/account-sessions.js +109 -0
  47. package/dist/esm/client/components/apps-grid.js +43 -0
  48. package/dist/esm/client/components/auth-buttons.js +38 -0
  49. package/dist/esm/client/components/feature-gate.js +57 -0
  50. package/dist/esm/client/components/link-button.js +21 -0
  51. package/dist/esm/client/components/logo.js +24 -0
  52. package/dist/esm/client/components/plan-pricing.js +156 -0
  53. package/dist/esm/client/components/plans-page.js +36 -0
  54. package/dist/esm/client/components/plans.js +209 -0
  55. package/dist/esm/client/components/readiness.js +113 -0
  56. package/dist/esm/client/components/subscribe-dialog.js +57 -0
  57. package/dist/esm/client/components/subscription.js +181 -0
  58. package/dist/esm/client/components/usage-meter.js +65 -0
  59. package/dist/esm/client/components/user-menu.js +164 -0
  60. package/dist/esm/client/index.js +47 -0
  61. package/dist/esm/client/mark.js +13 -0
  62. package/dist/esm/client/provider.js +247 -0
  63. package/dist/esm/client/use-checkout.js +168 -0
  64. package/dist/esm/errors.js +69 -0
  65. package/dist/esm/format-relative-time.js +15 -0
  66. package/dist/esm/helpers.js +26 -0
  67. package/dist/esm/index.js +15 -0
  68. package/dist/esm/next/index.js +279 -0
  69. package/dist/esm/package.json +3 -0
  70. package/dist/esm/server/client.js +110 -0
  71. package/dist/esm/server/handler.js +330 -0
  72. package/dist/esm/server/http.js +156 -0
  73. package/dist/esm/server/index.js +10 -0
  74. package/dist/esm/server/resources/access.js +24 -0
  75. package/dist/esm/server/resources/ai.js +32 -0
  76. package/dist/esm/server/resources/apps.js +26 -0
  77. package/dist/esm/server/resources/billing.js +67 -0
  78. package/dist/esm/server/resources/drive.js +35 -0
  79. package/dist/esm/server/resources/entitlements.js +45 -0
  80. package/dist/esm/server/resources/identity.js +189 -0
  81. package/dist/esm/server/resources/logs.js +37 -0
  82. package/dist/esm/server/resources/otp.js +25 -0
  83. package/dist/esm/server/resources/sessions.js +78 -0
  84. package/dist/esm/types.js +14 -0
  85. package/dist/theme.css +84 -0
  86. package/dist/types/cli/bin.d.ts +1 -0
  87. package/dist/types/cli/init.d.ts +34 -0
  88. package/dist/types/client/components/account-sessions.d.ts +25 -0
  89. package/dist/types/client/components/apps-grid.d.ts +20 -0
  90. package/dist/types/client/components/auth-buttons.d.ts +31 -0
  91. package/dist/types/client/components/feature-gate.d.ts +57 -0
  92. package/dist/types/client/components/link-button.d.ts +20 -0
  93. package/dist/types/client/components/logo.d.ts +35 -0
  94. package/dist/types/client/components/plan-pricing.d.ts +96 -0
  95. package/dist/types/client/components/plans-page.d.ts +36 -0
  96. package/dist/types/client/components/plans.d.ts +34 -0
  97. package/dist/types/client/components/readiness.d.ts +52 -0
  98. package/dist/types/client/components/subscribe-dialog.d.ts +34 -0
  99. package/dist/types/client/components/subscription.d.ts +123 -0
  100. package/dist/types/client/components/usage-meter.d.ts +24 -0
  101. package/dist/types/client/components/user-menu.d.ts +86 -0
  102. package/dist/types/client/index.d.ts +47 -0
  103. package/dist/types/client/mark.d.ts +11 -0
  104. package/dist/types/client/provider.d.ts +209 -0
  105. package/dist/types/client/use-checkout.d.ts +93 -0
  106. package/dist/types/errors.d.ts +62 -0
  107. package/dist/types/format-relative-time.d.ts +2 -0
  108. package/dist/types/helpers.d.ts +33 -0
  109. package/dist/types/index.d.ts +17 -0
  110. package/dist/types/next/index.d.ts +136 -0
  111. package/dist/types/server/client.d.ts +83 -0
  112. package/dist/types/server/handler.d.ts +67 -0
  113. package/dist/types/server/http.d.ts +52 -0
  114. package/dist/types/server/index.d.ts +15 -0
  115. package/dist/types/server/resources/access.d.ts +17 -0
  116. package/dist/types/server/resources/ai.d.ts +24 -0
  117. package/dist/types/server/resources/apps.d.ts +12 -0
  118. package/dist/types/server/resources/billing.d.ts +41 -0
  119. package/dist/types/server/resources/drive.d.ts +26 -0
  120. package/dist/types/server/resources/entitlements.d.ts +31 -0
  121. package/dist/types/server/resources/identity.d.ts +76 -0
  122. package/dist/types/server/resources/logs.d.ts +20 -0
  123. package/dist/types/server/resources/otp.d.ts +17 -0
  124. package/dist/types/server/resources/sessions.d.ts +37 -0
  125. package/dist/types/types.d.ts +434 -0
  126. package/package.json +102 -0
@@ -0,0 +1,20 @@
1
+ export interface ConnectAppsGridProps {
2
+ /** Extra classes on the trigger button. */
3
+ className?: string;
4
+ /** Trigger label for screen readers. */
5
+ label?: string;
6
+ }
7
+ /**
8
+ * The Google-style app launcher: a grid icon in your top bar that opens a
9
+ * modal of every Connect service this organization can actually open.
10
+ *
11
+ * Only launchable apps appear, so no tile is a dead end - a paused
12
+ * subscription still shows, badged, because a dunning cycle should not make a
13
+ * product look deleted. The current app is pinned first and marked, rather
14
+ * than filtered out, so the grid never has a hole where "you are here" should
15
+ * be.
16
+ *
17
+ * Renders nothing when signed out: a launcher with nothing to launch is just a
18
+ * button that opens an empty box.
19
+ */
20
+ export declare function ConnectAppsGrid({ className, label }: ConnectAppsGridProps): import("react").JSX.Element | null;
@@ -0,0 +1,31 @@
1
+ import { Button } from "@heroui/react";
2
+ import type { ComponentProps, ReactNode } from "react";
3
+ type ButtonProps = ComponentProps<typeof Button>;
4
+ export interface ConnectAuthButtonProps extends Omit<ButtonProps, "onPress" | "children"> {
5
+ /** Defaults to "Continue with Connect" / "Sign up with Connect". */
6
+ children?: ReactNode;
7
+ }
8
+ /**
9
+ * "Continue with Connect" - the federated sign-in button, in the shape people
10
+ * already know from Google and GitHub: neutral surface, brand mark on the
11
+ * left, one line of text.
12
+ *
13
+ * `variant="secondary"` by default rather than `primary`, on purpose. A
14
+ * federated button that outshouts the host app's own primary action reads as
15
+ * an ad; every mature identity button is quiet. Pass `variant` to override if
16
+ * Connect *is* the primary path in your flow.
17
+ *
18
+ * Hidden while a session is still loading, so a signed-in user never sees a
19
+ * Login button flash before their avatar resolves.
20
+ */
21
+ export declare function ConnectLoginButton({ children, ...props }: ConnectAuthButtonProps): import("react").JSX.Element | null;
22
+ /**
23
+ * The same button pointed at registration (`prompt=create`).
24
+ *
25
+ * Only the starting screen differs - each screen links to the other carrying
26
+ * the same in-flight request, and anyone who already has a Connect session
27
+ * skips straight to consent either way. So shipping both buttons costs nothing
28
+ * and matches what users expect from a "Sign up" / "Log in" pair.
29
+ */
30
+ export declare function ConnectRegisterButton({ children, ...props }: ConnectAuthButtonProps): import("react").JSX.Element | null;
31
+ export {};
@@ -0,0 +1,57 @@
1
+ import type { ReactNode } from "react";
2
+ /**
3
+ * Whether the live subscription unlocks a plan feature.
4
+ *
5
+ * The browser twin of `client.entitlements.hasFeature` on the server, and the
6
+ * same posture: `false` rather than a throw when there is no subscription at
7
+ * all, because "not entitled" is the same answer either way and a feature check
8
+ * should not need a try/catch around it.
9
+ *
10
+ * `isLoading` is returned separately and matters: `false` while the first read
11
+ * is in flight means "we don't know yet", not "no". Gate on `isEntitled` for
12
+ * what to render and on `isLoading` for whether to render anything at all -
13
+ * `ConnectFeatureGate` does exactly that.
14
+ */
15
+ export declare function useConnectFeature(feature: string): {
16
+ isEntitled: boolean;
17
+ isLoading: boolean;
18
+ /** Every feature key the current plan unlocks - for a settings page listing what's included. */
19
+ features: string[];
20
+ };
21
+ export interface ConnectFeatureGateProps {
22
+ /** The feature key, as it appears in `plan.features`. */
23
+ feature: string;
24
+ children: ReactNode;
25
+ /**
26
+ * Rendered when the plan does not include `feature`. Defaults to an upsell
27
+ * card pointing at your plans page - pass `null` to hide the module silently
28
+ * instead.
29
+ */
30
+ fallback?: ReactNode;
31
+ /** Shown while the first read is in flight. Defaults to nothing. */
32
+ loading?: ReactNode;
33
+ /** Where your plan catalogue is mounted, for the default upsell. */
34
+ plansHref?: string;
35
+ /** Headline for the default upsell - name the feature, not the plan. */
36
+ title?: string;
37
+ description?: string;
38
+ className?: string;
39
+ }
40
+ /**
41
+ * Renders `children` only when the workspace's plan includes `feature`.
42
+ *
43
+ * ```tsx
44
+ * <ConnectFeatureGate feature="advanced_reports" title="Reports are on Growth">
45
+ * <Reports />
46
+ * </ConnectFeatureGate>
47
+ * ```
48
+ *
49
+ * Like every gate in this package, it is a **UX gate, not a security
50
+ * boundary**: the entitlement is read through your own origin and the browser
51
+ * can ignore it. Check `client.entitlements.hasFeature(orgId, feature)` on the
52
+ * server before doing the thing the feature unlocks.
53
+ *
54
+ * Renders `loading` - not `fallback` - while the answer is unknown, so a paying
55
+ * customer never sees an upsell for something they already have.
56
+ */
57
+ export declare function ConnectFeatureGate({ feature, children, fallback, loading, plansHref, title, description, className, }: ConnectFeatureGateProps): import("react").JSX.Element;
@@ -0,0 +1,20 @@
1
+ import { type ButtonProps } from "@heroui/react";
2
+ export type ConnectLinkButtonProps = Omit<ButtonProps, "render"> & {
3
+ href: string;
4
+ };
5
+ /**
6
+ * A HeroUI `Button` that renders a real `<a>`.
7
+ *
8
+ * A plain anchor, deliberately not a router link: this package imports nothing
9
+ * from `next` (see the `./next` entry's doc comment for why that is a hard
10
+ * rule), so it cannot know which router - if any - the host app uses. An
11
+ * `<a href>` navigates under all of them, and a host that wants client-side
12
+ * routing can pass its own element via `render` on the underlying `Button`.
13
+ *
14
+ * The cast is the same one HeroUI's own "Render Function" example needs:
15
+ * `render` types its argument as the props of the element `Button` would have
16
+ * emitted, which is a `<button>` - including a `ref` typed to
17
+ * `HTMLButtonElement`. Spreading that onto an anchor is sound at runtime and
18
+ * unrepresentable in the type, which is exactly what a cast is for.
19
+ */
20
+ export declare function ConnectLinkButton({ href, ...props }: Readonly<ConnectLinkButtonProps>): import("react").JSX.Element;
@@ -0,0 +1,35 @@
1
+ import type { ReactNode } from "react";
2
+ export interface ConnectLogoProps {
3
+ /**
4
+ * This service's name - rendered as the sub-brand beneath the `cosmo·ops`
5
+ * wordmark, in the house `cosmo·ops VOICE` treatment.
6
+ */
7
+ name: string;
8
+ /**
9
+ * This service's own glyph, drawn inside the accent badge. Any node - a
10
+ * `lucide-react` icon, an `<img>`, an inline `<svg>`. Paint it with
11
+ * `currentColor` and it inherits `--accent-foreground` from the badge, so it
12
+ * stays legible when a host app changes the accent.
13
+ *
14
+ * Falls back to the Connect rings when omitted.
15
+ */
16
+ icon?: ReactNode;
17
+ /** Mark only, no wordmark - for a collapsed sidebar or a favicon-sized slot. */
18
+ collapsed?: boolean;
19
+ className?: string;
20
+ }
21
+ /**
22
+ * The shared platform lockup: a service's own glyph on the CosmoOps badge,
23
+ * `cosmo·ops` beside it, and the service name underneath.
24
+ *
25
+ * This is what makes a family of service platforms look like one product. Each
26
+ * platform passes only what differs - its `name` and its `icon` - and inherits
27
+ * the mark geometry, the wordmark treatment, the separator, and the accent from
28
+ * one place. Change the lockup here and every platform follows.
29
+ *
30
+ * Sizes are `em`-based throughout, so the whole lockup scales from the
31
+ * wrapper's font size: `className="text-base"` renders it half again as large
32
+ * as `text-sm`, with the badge, gap and tracking all in proportion. That is
33
+ * why there is no `size` prop.
34
+ */
35
+ export declare function ConnectLogo({ name, icon, collapsed, className }: ConnectLogoProps): import("react").JSX.Element;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The pricing arithmetic the plan grid and its confirmation dialog have to
3
+ * agree on. Extracted so they cannot drift: a dialog that called an upgrade
4
+ * an upgrade while the card behind it said downgrade would be worse than
5
+ * either label being wrong on its own.
6
+ *
7
+ * Mirrors `packages/data/src/services/catalog.ts` and
8
+ * `packages/data/src/payments/plan-change.ts`, which are the copies Connect's
9
+ * own UI uses and which decide which use-case actually runs. The duplication
10
+ * is deliberate and load-bearing: this package carries no dependency on
11
+ * `@connect/data` so a partner's bundle never pulls Connect's internals, and
12
+ * `conformance.test.ts` type-checks the wire shapes against the server DTOs so
13
+ * the two can't drift silently.
14
+ *
15
+ * No React here - it is imported by both a client component and its dialog,
16
+ * and nothing in it needs to be.
17
+ */
18
+ import type { AppliedDiscount, Plan, SubscriptionResponse } from "../../types.js";
19
+ /** Paise are the wire unit everywhere in Connect; rupees are the display unit. */
20
+ export declare function formatPrice(paise: number): string;
21
+ /**
22
+ * An annual plan's price is 12 cycles of the same product, so any comparison
23
+ * across billing intervals has to normalise first - otherwise every annual
24
+ * plan reads as an upgrade over every monthly one.
25
+ */
26
+ export declare function monthlyOf(price: number, billingInterval: Plan["billingInterval"]): number;
27
+ export declare function discountLabel(discount: AppliedDiscount): string;
28
+ /**
29
+ * The minimum a plan-like object has to expose to be grouped or compared -
30
+ * lets the dialog pass a `SubscribedPlan` (which carries no add-ons or
31
+ * features) wherever a comparison is wanted.
32
+ */
33
+ export interface ComparablePlan {
34
+ name: string;
35
+ price: number;
36
+ billingInterval: Plan["billingInterval"];
37
+ planGroup?: string | null;
38
+ }
39
+ /**
40
+ * Falls back to a slug of the name for a Connect deployment predating
41
+ * `planGroup` on the wire, mirroring `resolvePlanGroup` server-side. Kept so
42
+ * an SDK upgrade doesn't require the partner's Connect instance to be
43
+ * upgraded in lockstep - an older server simply omits the field and the grid
44
+ * groups the way it always did.
45
+ */
46
+ export declare function resolvePlanGroup(plan: ComparablePlan): string;
47
+ /** One tier of the catalog: the product, and whichever cadences it is sold at. */
48
+ export interface PlanTier {
49
+ planGroup: string;
50
+ name: string;
51
+ description: string;
52
+ monthly: Plan | null;
53
+ annual: Plan | null;
54
+ /** Whole-percent saving from paying annually, or `null` when this tier has no pair to compare. */
55
+ annualSavingPercent: number | null;
56
+ }
57
+ /**
58
+ * Collapses a flat plan list into one card per tier.
59
+ *
60
+ * This is the shape the grid wants and the old one didn't have. It used to
61
+ * filter the whole list down to the toggled interval, which meant a tier sold
62
+ * monthly-only simply vanished when a customer looked at annual pricing, and a
63
+ * catalog with no monthly plans at all rendered an empty grid on first paint.
64
+ * Grouping first means the toggle picks a *price inside a card*, and a card
65
+ * with only one cadence still shows.
66
+ *
67
+ * Ordered by the tier's cheapest monthly-equivalent price, so the grid reads
68
+ * left-to-right cheapest-first regardless of what order the API returned.
69
+ */
70
+ export declare function groupPlansByTier(plans: readonly Plan[]): PlanTier[];
71
+ /**
72
+ * Annual is quoted per year on the wire (`billingInterval: "annual"`), so a
73
+ * side-by-side grid has to normalise before it can compare. Showing the saving
74
+ * against 12x the monthly plan is the comparison a customer is actually making.
75
+ *
76
+ * **Call this with a matched pair from one tier.** It used to be handed the
77
+ * first monthly and the first annual plan in the whole response - two plans
78
+ * that were the same tier only by the accident of document order, and that
79
+ * produced one badge for a catalog where each tier discounts differently.
80
+ * `groupPlansByTier` now does the pairing.
81
+ */
82
+ export declare function annualSaving(monthly: Plan | null | undefined, annual: Plan | null | undefined): number | null;
83
+ /**
84
+ * What moving to `plan` would be, given what the org is on today.
85
+ *
86
+ * `switch-to-annual`/`switch-to-monthly` exist because a change of *cadence*
87
+ * is neither an upgrade nor a downgrade, and calling it one is not a wording
88
+ * problem. Connect picks the use-case that runs from the same classification
89
+ * (`classifyPlanChange` in `packages/data`), and a same-tier move to annual
90
+ * compares *cheaper* per month - so it used to be sent down the downgrade
91
+ * path, where the price delta across cadences is negative, floors to zero,
92
+ * and settles the whole switch for no money at all.
93
+ */
94
+ export type PlanAction = "current" | "upgrade" | "downgrade" | "switch-to-annual" | "switch-to-monthly" | "subscribe";
95
+ export declare function resolveAction(plan: Plan, current: SubscriptionResponse | null): PlanAction;
96
+ export declare const ACTION_LABEL: Record<PlanAction, string>;
@@ -0,0 +1,36 @@
1
+ import { type ConnectPlansProps } from "./plans.js";
2
+ export interface ConnectPlansPageProps extends ConnectPlansProps {
3
+ title?: string;
4
+ description?: string;
5
+ /**
6
+ * Show the current subscription above the grid. On by default - someone
7
+ * arriving at a pricing page while already subscribed is usually here to
8
+ * change something, and the first thing they need is what they are on now.
9
+ */
10
+ showCurrentSubscription?: boolean;
11
+ }
12
+ /**
13
+ * A complete `/plans` page, assembled from the pieces so the host app's route
14
+ * file is one line:
15
+ *
16
+ * ```tsx
17
+ * // app/plans/page.tsx
18
+ * "use client";
19
+ * export { ConnectPlansPage as default } from "@cosmoops/connect-sdk/client";
20
+ * ```
21
+ *
22
+ * The assembly is the value here, not the layout. Three things have to be on
23
+ * this page and are easy to leave off:
24
+ *
25
+ * 1. **The readiness banner, above the grid.** A workspace that still needs
26
+ * KYC can press Subscribe all day and be refused at the last step. Saying so
27
+ * before they pick is the difference between a page that works and one that
28
+ * ends in a rejected charge.
29
+ * 2. **What they're on now**, so "Upgrade" on a card has something to mean.
30
+ * 3. **A signed-out state that is a login button**, not an empty grid - the
31
+ * catalogue reads through the bridge, which needs a session.
32
+ *
33
+ * Every prop `ConnectPlans` takes passes straight through, and each piece is
34
+ * exported on its own for an app that wants a different arrangement.
35
+ */
36
+ export declare function ConnectPlansPage({ title, description, showCurrentSubscription, ...plansProps }: ConnectPlansPageProps): import("react").JSX.Element;
@@ -0,0 +1,34 @@
1
+ export interface ConnectPlansProps {
2
+ /** Restrict the grid to these plan ids, in this order. Defaults to every active plan. */
3
+ planIds?: string[];
4
+ /** Hide the coupon field. */
5
+ hideCoupon?: boolean;
6
+ className?: string;
7
+ }
8
+ /**
9
+ * The plan table: monthly/annual toggle, features, add-ons, the discount
10
+ * actually in effect, and the subscribe / upgrade / downgrade flows.
11
+ *
12
+ * ## Where the flow runs, and where it finishes
13
+ *
14
+ * The *flow* is here: picking a plan, seeing what it costs with GST, being
15
+ * told what happens to the cycle you are part-way through, and confirming.
16
+ * The *payment* is not, and that is deliberate. A plan change is not one
17
+ * write - it re-runs the readiness gates, prices proration against
18
+ * accumulated credit, re-checks the RBI mandate ceiling for the new amount,
19
+ * and issues a GST invoice. Connect owns that sequence end to end; a
20
+ * partner-side "change plan" call would be a second implementation of it
21
+ * that drifts. It would also mean a browser holding the partner's
22
+ * credentials could move money, which is the one thing the bridge exists to
23
+ * prevent.
24
+ *
25
+ * So confirming opens Connect - on the *exact* confirmation screen for the
26
+ * plan just picked, via an intercepted parallel route, not a catalogue the
27
+ * customer has to search again - and this component watches `/entitlements`
28
+ * until the change lands, then repaints itself. See `useConnectCheckout`.
29
+ *
30
+ * Coupon codes work the same way: the field carries the code into Connect's
31
+ * checkout, where it is validated and applied against the real cart. Anything
32
+ * this component claimed about a code would be a guess it cannot honour.
33
+ */
34
+ export declare function ConnectPlans({ planIds, hideCoupon, className }: ConnectPlansProps): import("react").JSX.Element;
@@ -0,0 +1,52 @@
1
+ export interface ConnectReadinessBannerProps {
2
+ /** Show advisory blockers too. Off by default - they are not a reason to interrupt anyone mid-task. */
3
+ includeAdvisory?: boolean;
4
+ /** Render nothing at all once the workspace is ready. On by default. */
5
+ hideWhenReady?: boolean;
6
+ className?: string;
7
+ }
8
+ /**
9
+ * Every gate between this workspace and your service, with the way out of each.
10
+ *
11
+ * This is the component that keeps a blocked customer from staring at a broken
12
+ * app. `getReadiness` resolves subscription state, KYC/KYB, a chargeable
13
+ * payment method and seat headroom in one call, and **each blocker carries a
14
+ * `resolveUrl` into the exact Connect screen that fixes it** - so being blocked
15
+ * comes with somewhere to go rather than a refusal.
16
+ *
17
+ * Render it high on any page your service gates. It draws nothing at all when
18
+ * the workspace is ready, so it is safe to leave mounted permanently.
19
+ *
20
+ * Two deliberate refusals:
21
+ *
22
+ * - **Unrecognised codes are still shown, and still treated as blocking.**
23
+ * Connect adds codes without a major version (see `ReadinessBlockerCode`), so
24
+ * a switch that only knew today's codes would silently drop tomorrow's - the
25
+ * one failure mode where a customer is locked out with no explanation.
26
+ * - **`message` is rendered, never parsed.** It is human-facing and subject to
27
+ * rewording; branching on its text would break on a copy edit.
28
+ */
29
+ export declare function ConnectReadinessBanner({ includeAdvisory, hideWhenReady, className, }: ConnectReadinessBannerProps): import("react").JSX.Element | null;
30
+ export interface ConnectReadinessGateProps {
31
+ children: React.ReactNode;
32
+ /** Rendered instead of `children` while blocked. Defaults to the banner in a card. */
33
+ fallback?: React.ReactNode;
34
+ /** Shown while the first readiness read is in flight. */
35
+ loading?: React.ReactNode;
36
+ className?: string;
37
+ }
38
+ /**
39
+ * Renders `children` only once the workspace clears every blocking gate.
40
+ *
41
+ * The hard version of `ConnectReadinessBanner`, for a route that genuinely
42
+ * cannot function while blocked. Prefer the banner where the page still does
43
+ * something useful - a gate that hides a whole screen behind a verification
44
+ * notice is a worse experience than one that lets people look around while
45
+ * they sort it out.
46
+ *
47
+ * **This is a UX gate, not a security boundary.** The readiness read reaches
48
+ * your own origin and a determined browser can ignore whatever it says. The
49
+ * authoritative check is `client.entitlements.getReadiness` on your server,
50
+ * before you do the thing.
51
+ */
52
+ export declare function ConnectReadinessGate({ children, fallback, loading, className, }: ConnectReadinessGateProps): import("react").JSX.Element;
@@ -0,0 +1,34 @@
1
+ import { type PlanAction } from "./plan-pricing.js";
2
+ import type { Plan, SubscriptionResponse } from "../../types.js";
3
+ export interface ConnectSubscribeDialogProps {
4
+ /** `null` closes it. The plan being subscribed to or moved to. */
5
+ plan: Plan | null;
6
+ /** Which of subscribe / upgrade / downgrade this is - decides the headline and what the notice says about money. */
7
+ action: PlanAction;
8
+ /** The org's live subscription, for the "from" side of a plan change. */
9
+ current: SubscriptionResponse | null;
10
+ /** Coupon typed into the plan grid, echoed here so it is visible before the hand-off carries it. */
11
+ coupon?: string;
12
+ /** Whether this org's `plan.trialDays` still applies is Connect's call, not this component's - see the notice below. */
13
+ onClose(): void;
14
+ onConfirm(): void;
15
+ }
16
+ /**
17
+ * The review step before a customer is sent to Connect.
18
+ *
19
+ * It exists because the hand-off is a context switch, and a context switch
20
+ * the customer did not expect reads as being dumped somewhere. So this says
21
+ * what they are about to buy, what it costs with GST, what happens to the
22
+ * cycle they are part-way through, and - plainly - that the payment itself
23
+ * happens on Connect. Then one button, which opens it.
24
+ *
25
+ * **Every number here is a preview, and deliberately never the last word.**
26
+ * The authoritative price is computed by Connect against the real cart:
27
+ * proration is priced against the actual cycle, a coupon is validated
28
+ * against the plan it is being applied to, trial eligibility depends on
29
+ * whether this organization has ever subscribed to this service before, and
30
+ * an accumulated credit is deducted. None of that is knowable from the
31
+ * partner's origin, so this component states the list price and says where
32
+ * the final figure comes from rather than quietly guessing at one.
33
+ */
34
+ export declare function ConnectSubscribeDialog({ plan, action, current, coupon, onClose, onConfirm, }: Readonly<ConnectSubscribeDialogProps>): import("react").JSX.Element;
@@ -0,0 +1,123 @@
1
+ import type { ReactNode } from "react";
2
+ import type { SubscribedPlan, SubscriptionDetail, SubscriptionResponse } from "../../types.js";
3
+ /**
4
+ * What a subscription is actually doing, collapsed to the five cases worth
5
+ * rendering differently.
6
+ *
7
+ * `status` alone cannot answer this. `"active"` covers both a paying customer
8
+ * and one still in a trial (`isTrialing` on the session block is what splits
9
+ * them), and it says nothing about a subscription winding down -
10
+ * `cancelAtPeriodEnd` is a separate field precisely so a cancelling
11
+ * subscription is not mistaken for a renewing one.
12
+ */
13
+ export type ConnectSubscriptionState = "none" | "trialing" | "active" | "cancelling"
14
+ /** `"created"`/`"authenticated"` - subscribed, but the first debit has not confirmed yet. */
15
+ | "pending"
16
+ /** `"paused"` (dunning exhausted) or `"cancelled"` - access is gone or going. */
17
+ | "problem";
18
+ export declare function resolveSubscriptionState(subscription: SubscriptionResponse | null, isTrialing: boolean): ConnectSubscriptionState;
19
+ export interface ConnectNoSubscriptionCardProps {
20
+ /** Where your plan catalogue is mounted. Defaults to `/plans`. */
21
+ plansHref?: string;
22
+ /** Override the headline - "Upgrade to unlock reports", say, rather than the generic one. */
23
+ title?: string;
24
+ description?: string;
25
+ className?: string;
26
+ }
27
+ /**
28
+ * The "you are not on a plan yet" card.
29
+ *
30
+ * Points at your **own** `/plans` page rather than opening Connect, and that is
31
+ * the deliberate part: choosing a plan is browsing, it belongs in your product
32
+ * where your pricing story is, and only the final confirmation needs to happen
33
+ * on Connect. Sending someone straight out of the app to pick is a context
34
+ * switch two steps too early.
35
+ *
36
+ * Rendered on its own it is just a card; `ConnectSubscriptionGate` is the usual
37
+ * way in, since it picks this only when there is genuinely nothing to show.
38
+ */
39
+ export declare function ConnectNoSubscriptionCard({ plansHref, title, description, className, }: ConnectNoSubscriptionCardProps): import("react").JSX.Element;
40
+ export interface ConnectSubscriptionCardProps {
41
+ plansHref?: string;
42
+ className?: string;
43
+ }
44
+ /**
45
+ * The current subscription at a glance: plan, what it costs, what happens next,
46
+ * and one way into Connect to change it.
47
+ *
48
+ * The counterpart to `ConnectNoSubscriptionCard` - the same slot in the
49
+ * signed-up state - so an account or settings page can render this and get the
50
+ * right thing either way.
51
+ *
52
+ * Dates are formatted, never computed. "Renews on 3 Mar" is a fact from the
53
+ * wire; "renews in 12 days" would be a clock read during render, which is
54
+ * impure and re-renders wrong.
55
+ */
56
+ export declare function ConnectSubscriptionCard({ plansHref, className }: ConnectSubscriptionCardProps): import("react").JSX.Element;
57
+ /**
58
+ * Seats used against what the plan includes.
59
+ *
60
+ * Only meaningful for `"fixed"` and `"per-seat"` - `"unlimited"` has nothing to
61
+ * count against, so it renders nothing rather than a meter that is always
62
+ * empty. Under `"per-seat"` going over is billable rather than blocked, so it
63
+ * says what the extra costs instead of warning.
64
+ */
65
+ export declare function ConnectSeatUsage({ plan, detail, className, }: Readonly<{
66
+ plan: SubscribedPlan;
67
+ detail: SubscriptionDetail;
68
+ className?: string;
69
+ }>): import("react").JSX.Element | null;
70
+ export interface ConnectTrialBannerProps {
71
+ plansHref?: string;
72
+ className?: string;
73
+ }
74
+ /**
75
+ * A trial that is running, and when it converts.
76
+ *
77
+ * Renders only while trialing, so it can live permanently in a layout. It says
78
+ * the charge date rather than a countdown for the same reason
79
+ * `ConnectSubscriptionCard` does - a countdown means reading the clock while
80
+ * rendering.
81
+ *
82
+ * The wire carries `isTrialing` but no trial *end* date; `nextBillingDate` on a
83
+ * trialing subscription is the first charge, which is the date a customer
84
+ * actually cares about anyway.
85
+ */
86
+ export declare function ConnectTrialBanner({ plansHref, className }: ConnectTrialBannerProps): import("react").JSX.Element | null;
87
+ export interface ConnectSubscriptionGateProps {
88
+ children: ReactNode;
89
+ /** Where your plan catalogue is mounted. Defaults to `/plans`. */
90
+ plansHref?: string;
91
+ /** Replaces the default "not on a plan yet" card. */
92
+ fallback?: ReactNode;
93
+ /** Shown while the first read is in flight. Defaults to a skeleton, never a fallback - see below. */
94
+ loading?: ReactNode;
95
+ /**
96
+ * Let a trialing subscription through. On by default: a trial is a
97
+ * subscription, and gating it out is how a customer gets sold a trial and
98
+ * then locked out of it.
99
+ */
100
+ allowTrialing?: boolean;
101
+ /**
102
+ * Let a subscription whose first debit hasn't confirmed through. On by
103
+ * default - the charge settles on a webhook and can take minutes, and the
104
+ * alternative is telling someone who just paid that they haven't.
105
+ */
106
+ allowPending?: boolean;
107
+ className?: string;
108
+ }
109
+ /**
110
+ * Renders `children` for a subscribed workspace, and the "pick a plan" card for
111
+ * everyone else.
112
+ *
113
+ * **Never renders the fallback while loading.** That flash - "you're not
114
+ * subscribed" for 200ms, then the real content - is the specific bug this
115
+ * component exists to make impossible, and it is what hand-rolled versions of
116
+ * it get wrong: the honest states are "still asking" and "asked, and the answer
117
+ * is no", and only the second one is a sales pitch.
118
+ *
119
+ * **This is a UX gate, not a security boundary** - same caveat as
120
+ * `ConnectReadinessGate`. Anything that must not happen without a subscription
121
+ * is checked on your server with `client.entitlements.hasFeature`.
122
+ */
123
+ export declare function ConnectSubscriptionGate({ children, plansHref, fallback, loading, allowTrialing, allowPending, className, }: ConnectSubscriptionGateProps): import("react").JSX.Element;
@@ -0,0 +1,24 @@
1
+ export interface ConnectUsageMeterProps {
2
+ /** Restrict to these add-on ids, in this order. Defaults to every metered add-on. */
3
+ addonIds?: string[];
4
+ /** Render nothing when there is nothing to meter, instead of an empty-state card. On by default. */
5
+ hideWhenEmpty?: boolean;
6
+ title?: string;
7
+ className?: string;
8
+ }
9
+ /**
10
+ * How much of each consumable add-on this workspace has left.
11
+ *
12
+ * Reads the balance Connect actually debits, so the number here and the number
13
+ * `consumeAddon` enforces cannot disagree. Before the `addons:read` endpoint
14
+ * existed this component was impossible to write honestly: a service could
15
+ * spend an allowance down on every request and the only thing the API exposed
16
+ * was the list of add-on *ids* on the subscription.
17
+ *
18
+ * **It shows `balance` against `includedPerCycle`, and never a "used" figure.**
19
+ * With `carryForward` an unspent balance survives the cycle boundary, so
20
+ * `included - balance` can go negative - a meter that runs backwards. Real
21
+ * consumption for a cycle is a ledger question, and computing it from a
22
+ * balance would be a guess rendered as a fact.
23
+ */
24
+ export declare function ConnectUsageMeter({ addonIds, hideWhenEmpty, title, className, }: ConnectUsageMeterProps): import("react").JSX.Element | null;
@@ -0,0 +1,86 @@
1
+ import type { ThemeMode } from "../../types.js";
2
+ export interface ConnectUserMenuProps {
3
+ /** Hide the name beside the avatar - for a dense top bar. */
4
+ compact?: boolean;
5
+ /** Called after sign-out completes, e.g. to route home. */
6
+ onSignOut?: () => void;
7
+ className?: string;
8
+ /**
9
+ * Current theme, and where to send a change - the SDK has no theme
10
+ * provider of its own (no `next-themes` dependency), so this is entirely
11
+ * host-controlled. The Appearance section only renders when both are
12
+ * supplied; omit either to leave theme control out of the modal.
13
+ */
14
+ theme?: ThemeMode;
15
+ onThemeChange?: (theme: ThemeMode) => void;
16
+ }
17
+ /**
18
+ * Avatar and name in the trigger; an identity card, the three Connect
19
+ * destinations (Profile, Billing, Connect), an optional appearance control,
20
+ * this account's active sessions, and sign-out in the modal it opens.
21
+ *
22
+ * Profile/Billing/Connect open in a **new tab**, each marked with an
23
+ * external-link icon. They are a different application at a different
24
+ * origin, and navigating the host app away to reach them would lose
25
+ * whatever the user was doing here. Sign out is the only action that acts
26
+ * in place, which is why it sits alone in the footer rather than as a fourth
27
+ * item in the same list as three navigations.
28
+ *
29
+ * The session list is account-wide, not scoped to this service - see
30
+ * `ConnectUserSession`'s doc comment in `types.ts`. Appearance only renders
31
+ * when both `theme` and `onThemeChange` are supplied.
32
+ *
33
+ * Renders nothing when signed out - pair it with `<ConnectLoginButton>`, which
34
+ * renders nothing when signed *in*, and the two swap cleanly with no branch in
35
+ * your own layout.
36
+ *
37
+ * On the HeroUI shape: the trigger is a plain `<Button>` as a **direct child**
38
+ * of `<Modal>`, not wrapped in `<Modal.Trigger>`. `Dropdown.Trigger` (and by
39
+ * the same construction, `Modal.Trigger`) is itself a React Aria `Button`, so
40
+ * wrapping one produces a button inside a button - invalid markup, and press
41
+ * handling stops working. Kept uncontrolled (no `isOpen`/`onOpenChange`),
42
+ * matching `<ConnectAppsGrid>`'s own `<Modal>` - nothing here needs to close
43
+ * the modal programmatically; sign-out uses `slot="close"` instead.
44
+ */
45
+ export declare function ConnectUserMenu({ compact, onSignOut, className, theme, onThemeChange, }: ConnectUserMenuProps): import("react").JSX.Element | null;
46
+ /**
47
+ * The "Manage subscription" action on its own, for layouts that want it
48
+ * visible rather than buried in the account menu - a billing banner, an empty
49
+ * state, a settings page.
50
+ */
51
+ export declare function ConnectManageSubscriptionButton({ className }: Readonly<{
52
+ className?: string;
53
+ }>): import("react").JSX.Element;
54
+ /**
55
+ * Invoices and payment history, on Connect.
56
+ *
57
+ * A deep link rather than a component that lists them, and that is not a
58
+ * placeholder: nothing in `/api/v1/service/*` exposes invoices, so an SDK-side
59
+ * list would have nothing to read. It is also the right answer regardless -
60
+ * a GST tax invoice is a legal document with a fixed format, downloadable and
61
+ * emailed from one place, and a partner-rendered copy of one would be a second
62
+ * version of a record that must have exactly one.
63
+ */
64
+ export declare function ConnectInvoicesButton({ className }: Readonly<{
65
+ className?: string;
66
+ }>): import("react").JSX.Element | null;
67
+ /**
68
+ * The "Profile" action on its own - the third of the three Connect
69
+ * destinations, alongside `ConnectManageSubscriptionButton` (Billing) and
70
+ * `ConnectHomeButton`.
71
+ *
72
+ * It was reachable only from inside `ConnectUserMenu`, which meant an app that
73
+ * builds its own account menu had to hardcode `${appUrl}/profile` to offer it.
74
+ */
75
+ export declare function ConnectProfileButton({ className }: Readonly<{
76
+ className?: string;
77
+ }>): import("react").JSX.Element | null;
78
+ /** A plain "Open Connect" button, for a footer or a help menu. */
79
+ export declare function ConnectHomeButton({ className }: Readonly<{
80
+ className?: string;
81
+ }>): import("react").JSX.Element;
82
+ /** Sign out on its own, for a settings page that wants it as a destructive row. */
83
+ export declare function ConnectSignOutButton({ onSignOut, className, }: Readonly<{
84
+ onSignOut?: () => void;
85
+ className?: string;
86
+ }>): import("react").JSX.Element | null;