@cosmoops/connect-sdk 0.7.0 → 0.15.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 +662 -0
- package/README.md +376 -124
- package/dist/cjs/cli/doctor.js +3 -3
- package/dist/cjs/cli/init.js +3 -3
- package/dist/cjs/client/components/access-gate.js +59 -5
- package/dist/cjs/client/components/ai-format.js +135 -0
- package/dist/cjs/client/components/ai-widgets.js +232 -0
- package/dist/cjs/client/components/app-shell.js +80 -45
- package/dist/cjs/client/components/apps-grid.js +3 -2
- package/dist/cjs/client/components/ask-ai.js +353 -0
- package/dist/cjs/client/components/confirm-dialog.js +40 -0
- package/dist/cjs/client/components/cursor-pagination.js +22 -0
- package/dist/cjs/client/components/filter-tabs.js +24 -0
- package/dist/cjs/client/components/grievance-notice.js +30 -0
- package/dist/cjs/client/components/link-button.js +17 -10
- package/dist/cjs/client/components/logo.js +4 -3
- package/dist/cjs/client/components/nav.js +42 -1
- package/dist/cjs/client/components/plan-pricing.js +27 -0
- package/dist/cjs/client/components/plans.js +1 -1
- package/dist/cjs/client/components/platform-gate.js +48 -0
- package/dist/cjs/client/components/shell-context.js +149 -0
- package/dist/cjs/client/components/side-nav.js +50 -24
- package/dist/cjs/client/components/sidebar.js +127 -0
- package/dist/cjs/client/components/subscribe-dialog.js +2 -2
- package/dist/cjs/client/components/subscription.js +1 -1
- package/dist/cjs/client/components/user-menu.js +122 -40
- package/dist/cjs/client/components/workspace-switcher.js +100 -0
- package/dist/cjs/client/components/workspace-type-gate.js +48 -0
- package/dist/cjs/client/index.js +98 -3
- package/dist/cjs/client/provider.js +100 -2
- package/dist/cjs/client/use-checkout.js +9 -5
- package/dist/cjs/errors.js +1 -1
- package/dist/cjs/index.js +11 -1
- package/dist/cjs/next/index.js +2 -2
- package/dist/cjs/server/client.js +25 -2
- package/dist/cjs/server/handler.js +237 -16
- package/dist/cjs/server/http.js +52 -26
- package/dist/cjs/server/resources/acting-as.js +16 -0
- package/dist/cjs/server/resources/ai.js +15 -2
- package/dist/cjs/server/resources/data.js +374 -0
- package/dist/cjs/server/resources/entitlements.js +41 -0
- package/dist/cjs/server/resources/identity.js +9 -3
- package/dist/cjs/server/resources/kv.js +84 -0
- package/dist/cjs/server/resources/platform.js +43 -0
- package/dist/cjs/server/resources/sessions.js +1 -1
- package/dist/cjs/server/resources/workspaces.js +74 -0
- package/dist/cjs/store.js +152 -0
- package/dist/cjs/ui/empty-state.js +28 -0
- package/dist/cjs/ui/index.js +44 -0
- package/dist/cjs/ui/page-header.js +27 -0
- package/dist/cjs/ui/page-shell.js +44 -0
- package/dist/cjs/ui/section.js +20 -0
- package/dist/cjs/ui/setting-row.js +14 -0
- package/dist/cjs/ui/status-chip.js +17 -0
- package/dist/esm/cli/doctor.js +3 -3
- package/dist/esm/cli/init.js +3 -3
- package/dist/esm/client/components/access-gate.js +60 -6
- package/dist/esm/client/components/ai-format.js +129 -0
- package/dist/esm/client/components/ai-widgets.js +223 -0
- package/dist/esm/client/components/app-shell.js +85 -50
- package/dist/esm/client/components/apps-grid.js +3 -2
- package/dist/esm/client/components/ask-ai.js +350 -0
- package/dist/esm/client/components/confirm-dialog.js +37 -0
- package/dist/esm/client/components/cursor-pagination.js +19 -0
- package/dist/esm/client/components/filter-tabs.js +21 -0
- package/dist/esm/client/components/grievance-notice.js +27 -0
- package/dist/esm/client/components/link-button.js +17 -10
- package/dist/esm/client/components/logo.js +4 -3
- package/dist/esm/client/components/nav.js +41 -1
- package/dist/esm/client/components/plan-pricing.js +25 -0
- package/dist/esm/client/components/plans.js +2 -2
- package/dist/esm/client/components/platform-gate.js +45 -0
- package/dist/esm/client/components/shell-context.js +141 -0
- package/dist/esm/client/components/side-nav.js +49 -22
- package/dist/esm/client/components/sidebar.js +120 -0
- package/dist/esm/client/components/subscribe-dialog.js +3 -3
- package/dist/esm/client/components/subscription.js +2 -2
- package/dist/esm/client/components/user-menu.js +121 -41
- package/dist/esm/client/components/workspace-switcher.js +97 -0
- package/dist/esm/client/components/workspace-type-gate.js +45 -0
- package/dist/esm/client/index.js +58 -5
- package/dist/esm/client/provider.js +94 -2
- package/dist/esm/client/use-checkout.js +9 -5
- package/dist/esm/errors.js +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/next/index.js +2 -2
- package/dist/esm/server/client.js +25 -2
- package/dist/esm/server/handler.js +237 -16
- package/dist/esm/server/http.js +52 -26
- package/dist/esm/server/resources/acting-as.js +13 -0
- package/dist/esm/server/resources/ai.js +15 -2
- package/dist/esm/server/resources/data.js +369 -0
- package/dist/esm/server/resources/entitlements.js +41 -0
- package/dist/esm/server/resources/identity.js +9 -3
- package/dist/esm/server/resources/kv.js +80 -0
- package/dist/esm/server/resources/platform.js +39 -0
- package/dist/esm/server/resources/sessions.js +1 -1
- package/dist/esm/server/resources/workspaces.js +70 -0
- package/dist/esm/store.js +141 -0
- package/dist/esm/ui/empty-state.js +25 -0
- package/dist/esm/ui/index.js +34 -0
- package/dist/esm/ui/page-header.js +24 -0
- package/dist/esm/ui/page-shell.js +41 -0
- package/dist/esm/ui/section.js +17 -0
- package/dist/esm/ui/setting-row.js +11 -0
- package/dist/esm/ui/status-chip.js +14 -0
- package/dist/theme.css +87 -0
- package/dist/types/client/components/access-gate.d.ts +17 -3
- package/dist/types/client/components/ai-format.d.ts +24 -0
- package/dist/types/client/components/ai-widgets.d.ts +42 -0
- package/dist/types/client/components/app-shell.d.ts +85 -31
- package/dist/types/client/components/apps-grid.d.ts +11 -2
- package/dist/types/client/components/ask-ai.d.ts +73 -0
- package/dist/types/client/components/confirm-dialog.d.ts +34 -0
- package/dist/types/client/components/cursor-pagination.d.ts +27 -0
- package/dist/types/client/components/filter-tabs.d.ts +26 -0
- package/dist/types/client/components/grievance-notice.d.ts +31 -0
- package/dist/types/client/components/link-button.d.ts +32 -9
- package/dist/types/client/components/logo.d.ts +18 -2
- package/dist/types/client/components/nav.d.ts +27 -3
- package/dist/types/client/components/plan-pricing.d.ts +10 -0
- package/dist/types/client/components/platform-gate.d.ts +53 -0
- package/dist/types/client/components/shell-context.d.ts +116 -0
- package/dist/types/client/components/side-nav.d.ts +28 -14
- package/dist/types/client/components/sidebar.d.ts +95 -0
- package/dist/types/client/components/user-menu.d.ts +70 -23
- package/dist/types/client/components/workspace-switcher.d.ts +49 -0
- package/dist/types/client/components/workspace-type-gate.d.ts +48 -0
- package/dist/types/client/index.d.ts +36 -7
- package/dist/types/client/provider.d.ts +149 -3
- package/dist/types/client/use-checkout.d.ts +8 -4
- package/dist/types/errors.d.ts +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/next/index.d.ts +1 -1
- package/dist/types/server/client.d.ts +25 -2
- package/dist/types/server/http.d.ts +14 -3
- package/dist/types/server/index.d.ts +4 -0
- package/dist/types/server/resources/acting-as.d.ts +11 -0
- package/dist/types/server/resources/ai.d.ts +9 -1
- package/dist/types/server/resources/data.d.ts +258 -0
- package/dist/types/server/resources/entitlements.d.ts +33 -1
- package/dist/types/server/resources/kv.d.ts +63 -0
- package/dist/types/server/resources/platform.d.ts +32 -0
- package/dist/types/server/resources/workspaces.d.ts +55 -0
- package/dist/types/store.d.ts +96 -0
- package/dist/types/types.d.ts +491 -13
- package/dist/types/ui/empty-state.d.ts +34 -0
- package/dist/types/ui/index.d.ts +40 -0
- package/dist/types/ui/page-header.d.ts +34 -0
- package/dist/types/ui/page-shell.d.ts +39 -0
- package/dist/types/ui/section.d.ts +26 -0
- package/dist/types/ui/setting-row.d.ts +18 -0
- package/dist/types/ui/status-chip.d.ts +36 -0
- package/package.json +13 -3
package/dist/types/types.d.ts
CHANGED
|
@@ -25,8 +25,71 @@ export interface ConnectUser {
|
|
|
25
25
|
* failing outright - so pair the two rather than branching on `null`.
|
|
26
26
|
*/
|
|
27
27
|
photoUrl: string | null;
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
/**
|
|
29
|
+
* The workspace this session is scoped to - the tenant to key your own data
|
|
30
|
+
* on. **`null` only for a Connect platform staff session**, where `staff`
|
|
31
|
+
* is `true`; see `SessionAccess`.
|
|
32
|
+
*
|
|
33
|
+
* Nullable since 0.11.0, and the breaking change in that release. If you do
|
|
34
|
+
* not turn staff access on for your application (it is off by default and
|
|
35
|
+
* per-application), this is never `null` and the only work is a type
|
|
36
|
+
* narrow. If you do, decide first what your tenanting does with a session
|
|
37
|
+
* that has no tenant - typically: serve your operator console off `staff`,
|
|
38
|
+
* and nothing else.
|
|
39
|
+
*/
|
|
40
|
+
workspaceId: string | null;
|
|
41
|
+
/** `null` on a staff session, for the same reason as `workspaceId`. */
|
|
42
|
+
workspaceName: string | null;
|
|
43
|
+
/**
|
|
44
|
+
* Whether this is a one-seat personal account or a team.
|
|
45
|
+
*
|
|
46
|
+
* The one fact about the signed-in workspace you cannot look up yourself -
|
|
47
|
+
* the application API has no workspace read - so branch on it rather than
|
|
48
|
+
* inferring an account's shape from whether it happens to have a second
|
|
49
|
+
* member. An `"individual"` workspace has exactly one seat and can never
|
|
50
|
+
* have another, so a members table, an invite button or a seat-count meter
|
|
51
|
+
* is dead UI for it.
|
|
52
|
+
*
|
|
53
|
+
* Re-read it from `identity.getUser()`; do not cache it past the sign-in.
|
|
54
|
+
* An `"individual"` workspace can convert to `"business"` under a live
|
|
55
|
+
* session (the reverse never happens).
|
|
56
|
+
*
|
|
57
|
+
* `null` on a staff session - there is no workspace to have a type.
|
|
58
|
+
*/
|
|
59
|
+
workspaceType: WorkspaceType | null;
|
|
60
|
+
}
|
|
61
|
+
export type WorkspaceType = "individual" | "business";
|
|
62
|
+
export type ApplicationAudience = "b2b" | "b2c" | "both";
|
|
63
|
+
/**
|
|
64
|
+
* Whether the signed-in workspace is inside your application's audience -
|
|
65
|
+
* returned alongside `user` by both `exchangeCode()` and `getUser()`.
|
|
66
|
+
*
|
|
67
|
+
* `user.workspaceType` answers what the account *is*; this answers what your
|
|
68
|
+
* application is *sold to*, and whether the two agree. Both ship together so
|
|
69
|
+
* the check stays `!audience.matches` rather than a hardcoded audience
|
|
70
|
+
* constant in your own code that goes stale the moment the catalogue entry
|
|
71
|
+
* changes.
|
|
72
|
+
*
|
|
73
|
+
* `matches: false` means readiness will report an `audience_mismatch` blocker
|
|
74
|
+
* and any attempt to subscribe will be refused, so treat it as a hard block:
|
|
75
|
+
* `<ConnectAccessGate>` already does. What it is NOT is a reason to sign the
|
|
76
|
+
* user out - they have a valid Connect account, just the wrong kind of one
|
|
77
|
+
* for this application.
|
|
78
|
+
*/
|
|
79
|
+
export interface SessionAudience {
|
|
80
|
+
/** Your catalogue entry's setting - `"both"` if you have never narrowed it. */
|
|
81
|
+
application: ApplicationAudience;
|
|
82
|
+
/** The single `workspaceType` `application` demands - `null` for `"both"`, which accepts either. */
|
|
83
|
+
requiredWorkspaceType: WorkspaceType | null;
|
|
84
|
+
/** `false` means this user is outside your audience. */
|
|
85
|
+
matches: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Whether Connect refuses the sign-in itself on a mismatch. When `true`,
|
|
88
|
+
* a `matches: false` can only reach you on a session minted before the
|
|
89
|
+
* setting was turned on. When `false`, gating is entirely yours to do -
|
|
90
|
+
* which is what `<ConnectAccessGate>` is for.
|
|
91
|
+
*/
|
|
92
|
+
enforcedAtSignIn: boolean;
|
|
30
93
|
}
|
|
31
94
|
export type SubscriptionStatus = "created" | "authenticated" | "active" | "paused" | "cancelled";
|
|
32
95
|
/** The compact subscription block returned alongside identity - `null` when the workspace has never subscribed to this application. */
|
|
@@ -38,15 +101,61 @@ export interface SessionSubscription {
|
|
|
38
101
|
isTrialing: boolean;
|
|
39
102
|
}
|
|
40
103
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
104
|
+
* Who the signed-in account is to you, in three flat fields.
|
|
105
|
+
*
|
|
106
|
+
* Replaces the `applicationRole` (`{role, granted}`) and `platformAccess`
|
|
107
|
+
* (`{roles}`) blocks, which were mutually exclusive by construction: a
|
|
108
|
+
* Connect platform account belongs to no workspace, so one of the two was
|
|
109
|
+
* always the empty answer and every integration wrote the same null-check
|
|
110
|
+
* twice.
|
|
111
|
+
*
|
|
112
|
+
* Read `staff` first - it decides what the other two mean.
|
|
113
|
+
*
|
|
114
|
+
* **`staff` is not a client-side security boundary.** `<ConnectPlatformGate>`
|
|
115
|
+
* and the hooks around it decide what to *render*; the browser can ignore all
|
|
116
|
+
* of it. Re-check it server-side with `client.identity.getUser(sessionToken)`
|
|
117
|
+
* before serving anything an operator console exposes.
|
|
44
118
|
*/
|
|
45
|
-
export interface
|
|
119
|
+
export interface SessionAccess {
|
|
120
|
+
/**
|
|
121
|
+
* The one role this session holds, or `null`.
|
|
122
|
+
*
|
|
123
|
+
* For a member (`staff: false`): the role matched on *your* application,
|
|
124
|
+
* resolved for your `client_id` rather than their workspace-wide roles -
|
|
125
|
+
* `null` when their permissions match no single role you defined, including
|
|
126
|
+
* when they have no access to your application at all.
|
|
127
|
+
*
|
|
128
|
+
* For staff (`staff: true`): the highest-authority Connect platform role
|
|
129
|
+
* **your application accepts** - never Connect's full role list for that
|
|
130
|
+
* person. An application accepting only `developer` is told `"developer"`
|
|
131
|
+
* about a `platform_admin` who also holds it.
|
|
132
|
+
*/
|
|
46
133
|
role: string | null;
|
|
47
|
-
|
|
134
|
+
/**
|
|
135
|
+
* What to gate on. Permission ids from *your* own `permissionCatalog` for a
|
|
136
|
+
* member; Connect `platform:*` ids for staff. One vocabulary at a time,
|
|
137
|
+
* decided by `staff` - a member session never carries a `platform:*` id and
|
|
138
|
+
* a staff session never carries one of yours.
|
|
139
|
+
*/
|
|
140
|
+
permissions: string[];
|
|
141
|
+
/**
|
|
142
|
+
* `true` when this is one of Connect's own operators rather than your
|
|
143
|
+
* customer.
|
|
144
|
+
*
|
|
145
|
+
* Only ever `true` when a Connect platform account signs in **and** your
|
|
146
|
+
* application has staff access turned on for that role. It is off by
|
|
147
|
+
* default; a platform admin enables it per application in the Connect
|
|
148
|
+
* Console, so until then this is always `false`.
|
|
149
|
+
*
|
|
150
|
+
* A staff session is shaped differently, and every difference is
|
|
151
|
+
* deliberate: `user.workspaceId`/`workspaceName`/`workspaceType` are `null`
|
|
152
|
+
* (these accounts belong to no workspace, by enforcement rather than by
|
|
153
|
+
* accident) and `subscription` is `null` (nobody is paying for staff to be
|
|
154
|
+
* here). Serve your operator console off this session and nothing else.
|
|
155
|
+
*/
|
|
156
|
+
staff: boolean;
|
|
48
157
|
}
|
|
49
|
-
export interface TokenResponse {
|
|
158
|
+
export interface TokenResponse extends SessionAccess {
|
|
50
159
|
idToken: string;
|
|
51
160
|
tokenType: "Bearer";
|
|
52
161
|
/** Seconds until `idToken` expires - about 5 minutes. */
|
|
@@ -57,12 +166,18 @@ export interface TokenResponse {
|
|
|
57
166
|
sessionTokenExpiresIn: number;
|
|
58
167
|
user: ConnectUser;
|
|
59
168
|
subscription: SessionSubscription | null;
|
|
60
|
-
|
|
169
|
+
/** Whether this account is the kind your application serves - see `SessionAudience`. */
|
|
170
|
+
audience: SessionAudience;
|
|
61
171
|
}
|
|
62
|
-
export interface UserResponse {
|
|
172
|
+
export interface UserResponse extends SessionAccess {
|
|
63
173
|
user: ConnectUser;
|
|
64
174
|
subscription: SessionSubscription | null;
|
|
65
|
-
|
|
175
|
+
/**
|
|
176
|
+
* Live, like `subscription` and `role`/`permissions`/`staff` - a workspace
|
|
177
|
+
* that converted to a business since sign-in matches here without another
|
|
178
|
+
* sign-in.
|
|
179
|
+
*/
|
|
180
|
+
audience: SessionAudience;
|
|
66
181
|
/** ISO 8601. Refresh proactively off this rather than waiting for a 401. */
|
|
67
182
|
sessionExpiresAt: string;
|
|
68
183
|
}
|
|
@@ -119,6 +234,8 @@ export interface ReadinessBlocker {
|
|
|
119
234
|
resolveUrl: string | null;
|
|
120
235
|
/** When this blocker resolves itself automatically - currently only set on `payment_retry_pending`, the deadline dunning suspends the subscription. `undefined`/`null` for every other code. */
|
|
121
236
|
graceEndsAt?: string | null;
|
|
237
|
+
/** The account type this application requires - set only on `audience_mismatch`, `undefined`/`null` otherwise. Branch on this rather than reading it out of `message`. */
|
|
238
|
+
requiredWorkspaceType?: "individual" | "business" | null;
|
|
122
239
|
}
|
|
123
240
|
export interface ReadinessResponse {
|
|
124
241
|
workspaceId: string;
|
|
@@ -194,7 +311,7 @@ export interface Plan {
|
|
|
194
311
|
/** GST-exclusive, in paise. */
|
|
195
312
|
price: number;
|
|
196
313
|
currency: "INR";
|
|
197
|
-
billingInterval:
|
|
314
|
+
billingInterval: BillingInterval;
|
|
198
315
|
gstRate: number;
|
|
199
316
|
addons: PlanAddon[];
|
|
200
317
|
seats: PlanSeats;
|
|
@@ -203,6 +320,16 @@ export interface Plan {
|
|
|
203
320
|
/** What this plan unlocks, labelled for display - see `PlanFeature`. */
|
|
204
321
|
features: PlanFeature[];
|
|
205
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* How often a plan bills.
|
|
325
|
+
*
|
|
326
|
+
* `"one-time"` is a plan that is paid for once and does not renew, so it has
|
|
327
|
+
* no cadence to pair with: `planGroup` still groups it, but there is no
|
|
328
|
+
* monthly/annual toggle to render for it. A pricing grid that assumes two
|
|
329
|
+
* cadences will show it under whichever tab happens to be selected - check the
|
|
330
|
+
* interval before offering the switch.
|
|
331
|
+
*/
|
|
332
|
+
export type BillingInterval = "monthly" | "annual" | "one-time";
|
|
206
333
|
export interface PlansResponse {
|
|
207
334
|
plans: Plan[];
|
|
208
335
|
}
|
|
@@ -210,7 +337,7 @@ export interface PlansResponse {
|
|
|
210
337
|
export interface SubscribedPlan {
|
|
211
338
|
id: string;
|
|
212
339
|
name: string;
|
|
213
|
-
billingInterval:
|
|
340
|
+
billingInterval: BillingInterval;
|
|
214
341
|
price: number;
|
|
215
342
|
currency: "INR";
|
|
216
343
|
gstRate: number;
|
|
@@ -261,6 +388,33 @@ export interface SubscriptionResponse {
|
|
|
261
388
|
/** `null` exactly when `subscription` is. */
|
|
262
389
|
plan: SubscribedPlan | null;
|
|
263
390
|
}
|
|
391
|
+
/** One row of `subscribers.list()` - a workspace subscribed to your application. */
|
|
392
|
+
export interface ApplicationSubscriber {
|
|
393
|
+
subscriptionId: string;
|
|
394
|
+
workspaceId: string;
|
|
395
|
+
/** `null` when the workspace has been purged and only its tombstone remains. */
|
|
396
|
+
workspaceName: string | null;
|
|
397
|
+
/** `"active"`, `"suspended"`, `"pending_deletion"`, `"deleted"` - or `null` alongside a null `workspaceName`. */
|
|
398
|
+
workspaceStatus: string | null;
|
|
399
|
+
planId: string;
|
|
400
|
+
/** `null` if the plan was hard-deleted rather than archived. */
|
|
401
|
+
planName: string | null;
|
|
402
|
+
status: "created" | "authenticated" | "active" | "paused" | "cancelled";
|
|
403
|
+
seatCount: number;
|
|
404
|
+
/** ISO 8601. */
|
|
405
|
+
subscribedAt: string;
|
|
406
|
+
/** ISO 8601, or `null` for a cancelled/paused subscription. */
|
|
407
|
+
nextBillingDate: string | null;
|
|
408
|
+
}
|
|
409
|
+
export interface SubscribersResponse {
|
|
410
|
+
items: ApplicationSubscriber[];
|
|
411
|
+
/**
|
|
412
|
+
* Pass back as `before` for the next page. `null` on the last page - which
|
|
413
|
+
* is the only reliable end-of-list signal, since a full page can still be
|
|
414
|
+
* the last one.
|
|
415
|
+
*/
|
|
416
|
+
nextBefore: string | null;
|
|
417
|
+
}
|
|
264
418
|
/** One consumable add-on's remaining balance on a workspace's subscription. */
|
|
265
419
|
export interface AddonUsage {
|
|
266
420
|
addonId: string;
|
|
@@ -402,6 +556,46 @@ export interface WorkspaceApp {
|
|
|
402
556
|
/** Launchable but not healthy (paused, or cancelling at period end) - badge it, do not hide it. */
|
|
403
557
|
needsAttention: boolean;
|
|
404
558
|
}
|
|
559
|
+
/**
|
|
560
|
+
* The platform's DPDP grievance channel - `companyProfile.grievance`.
|
|
561
|
+
*
|
|
562
|
+
* Flat and non-nullable: Connect resolves every field against its published
|
|
563
|
+
* defaults before answering, so there is no "not configured yet" state to
|
|
564
|
+
* branch on here. See `PlatformResource.getCompanyProfile`.
|
|
565
|
+
*/
|
|
566
|
+
export interface GrievanceChannel {
|
|
567
|
+
officerName: string;
|
|
568
|
+
officerEmail: string;
|
|
569
|
+
/** Free text, e.g. `"Monday - Friday, 10:00 AM - 6:00 PM IST"`. */
|
|
570
|
+
hours: string;
|
|
571
|
+
/** The redressal window the published notice commits to, in days. */
|
|
572
|
+
responseDays: number;
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Who operates the platform your application runs on, and how a user
|
|
576
|
+
* complains to them.
|
|
577
|
+
*
|
|
578
|
+
* You are a Data Processor on this notice, not a second Data Fiduciary - so
|
|
579
|
+
* this is the block your privacy page and your consent capture print, rather
|
|
580
|
+
* than one of your own kept in environment variables. That is the whole point
|
|
581
|
+
* of reading it: a statutory contact copied into `.env` goes stale silently,
|
|
582
|
+
* and every application holding a copy publishes a channel that reaches
|
|
583
|
+
* nobody.
|
|
584
|
+
*/
|
|
585
|
+
export interface CompanyProfile {
|
|
586
|
+
legalName: string;
|
|
587
|
+
/** Trading name, for prose the full registered name reads oddly in. */
|
|
588
|
+
brandName: string;
|
|
589
|
+
registeredAddress: string;
|
|
590
|
+
contactEmail: string;
|
|
591
|
+
website: string | null;
|
|
592
|
+
/** GSTIN, `null` if the operating entity is not GST-registered. */
|
|
593
|
+
gstNumber: string | null;
|
|
594
|
+
grievance: GrievanceChannel;
|
|
595
|
+
}
|
|
596
|
+
export interface CompanyProfileResponse {
|
|
597
|
+
companyProfile: CompanyProfile;
|
|
598
|
+
}
|
|
405
599
|
export type SessionDeviceType = "desktop" | "mobile" | "tablet" | "unknown";
|
|
406
600
|
export interface SessionDevice {
|
|
407
601
|
userAgent: string;
|
|
@@ -613,3 +807,287 @@ export interface ConnectWebhookEnvelope<T extends ConnectWebhookEventType = Conn
|
|
|
613
807
|
apiVersion: string;
|
|
614
808
|
data: ConnectWebhookEventMap[T];
|
|
615
809
|
}
|
|
810
|
+
/** How a scalar should be presented. See `AiValue` for what each one expects. */
|
|
811
|
+
export type AiValueFormat = "text" | "number"
|
|
812
|
+
/** A number in rupees - `12500`, not paise and not a formatted string. */
|
|
813
|
+
| "currency"
|
|
814
|
+
/** A fraction - `0.42` renders as 42%. */
|
|
815
|
+
| "percent"
|
|
816
|
+
/** An ISO 8601 date string. */
|
|
817
|
+
| "date"
|
|
818
|
+
/** An ISO 8601 datetime string. */
|
|
819
|
+
| "datetime"
|
|
820
|
+
/** A number of seconds. */
|
|
821
|
+
| "duration"
|
|
822
|
+
/** A number of bytes. */
|
|
823
|
+
| "bytes"
|
|
824
|
+
/** A short state word, drawn as a chip. */
|
|
825
|
+
| "status";
|
|
826
|
+
export type AiTone = "neutral" | "positive" | "warning" | "danger";
|
|
827
|
+
/** One cell. `null` is "no value" and renders as an em dash. */
|
|
828
|
+
export type AiValue = string | number | null;
|
|
829
|
+
export interface AiTableInput {
|
|
830
|
+
title?: string;
|
|
831
|
+
columns: {
|
|
832
|
+
label: string;
|
|
833
|
+
format: AiValueFormat;
|
|
834
|
+
}[];
|
|
835
|
+
/** Positional: one array per row, values in the same order as `columns`. */
|
|
836
|
+
rows: AiValue[][];
|
|
837
|
+
caption?: string;
|
|
838
|
+
}
|
|
839
|
+
export interface AiChartInput {
|
|
840
|
+
title?: string;
|
|
841
|
+
kind: "bar" | "line" | "area" | "pie";
|
|
842
|
+
xLabel?: string;
|
|
843
|
+
yLabel?: string;
|
|
844
|
+
valueFormat: AiValueFormat;
|
|
845
|
+
/** Every series shares the same x values, in the same order. */
|
|
846
|
+
series: {
|
|
847
|
+
name: string;
|
|
848
|
+
points: {
|
|
849
|
+
x: string;
|
|
850
|
+
y: number | null;
|
|
851
|
+
}[];
|
|
852
|
+
}[];
|
|
853
|
+
caption?: string;
|
|
854
|
+
}
|
|
855
|
+
export interface AiMetricsInput {
|
|
856
|
+
title?: string;
|
|
857
|
+
metrics: {
|
|
858
|
+
label: string;
|
|
859
|
+
value: AiValue;
|
|
860
|
+
format: AiValueFormat;
|
|
861
|
+
/** Signed change against the previous period, as a fraction. */
|
|
862
|
+
delta?: number | null;
|
|
863
|
+
/** Whether a rise is good - churn is `"down-is-good"`. Colouring a delta by sign alone gets this wrong half the time. */
|
|
864
|
+
deltaMeaning: "up-is-good" | "down-is-good" | "neutral";
|
|
865
|
+
hint?: string;
|
|
866
|
+
}[];
|
|
867
|
+
}
|
|
868
|
+
export interface AiTimelineInput {
|
|
869
|
+
title?: string;
|
|
870
|
+
/** Oldest first. */
|
|
871
|
+
events: {
|
|
872
|
+
at: string;
|
|
873
|
+
title: string;
|
|
874
|
+
detail?: string;
|
|
875
|
+
tone: AiTone;
|
|
876
|
+
}[];
|
|
877
|
+
}
|
|
878
|
+
export interface AiDetailsInput {
|
|
879
|
+
title?: string;
|
|
880
|
+
fields: {
|
|
881
|
+
label: string;
|
|
882
|
+
value: AiValue;
|
|
883
|
+
format: AiValueFormat;
|
|
884
|
+
tone: AiTone;
|
|
885
|
+
}[];
|
|
886
|
+
}
|
|
887
|
+
export interface AiStepsInput {
|
|
888
|
+
title?: string;
|
|
889
|
+
steps: {
|
|
890
|
+
title: string;
|
|
891
|
+
detail?: string;
|
|
892
|
+
}[];
|
|
893
|
+
}
|
|
894
|
+
export interface AiSourcesInput {
|
|
895
|
+
sources: {
|
|
896
|
+
title: string;
|
|
897
|
+
uri?: string;
|
|
898
|
+
snippet?: string;
|
|
899
|
+
}[];
|
|
900
|
+
}
|
|
901
|
+
/** The tools this client knows how to draw. Anything else in the stream is ignored. */
|
|
902
|
+
export type AiToolName = "showTable" | "showChart" | "showMetrics" | "showTimeline" | "showDetails" | "showSteps" | "showSources";
|
|
903
|
+
/**
|
|
904
|
+
* One document from the `data` store.
|
|
905
|
+
*
|
|
906
|
+
* Connect's own bookkeeping rides *inside* `data` under a `_` prefix -
|
|
907
|
+
* `_createdAt`, `_updatedAt`, `_createdBy`, `_updatedBy` - rather than as
|
|
908
|
+
* sibling fields, so a document round-trips through your code unchanged. You
|
|
909
|
+
* cannot write a `_`-prefixed field; that is what makes `_createdBy`
|
|
910
|
+
* trustworthy. It is `"application"` for a server-to-server write, or the
|
|
911
|
+
* Connect user id when a signed-in user drove it.
|
|
912
|
+
*/
|
|
913
|
+
export interface StoreDocument {
|
|
914
|
+
id: string;
|
|
915
|
+
/**
|
|
916
|
+
* The document's own fields, plus Connect's bookkeeping under `_`:
|
|
917
|
+
* `_createdAt`, `_updatedAt`, `_createdBy`, `_updatedBy`, and - on anything
|
|
918
|
+
* that came from a read - `_updateTime`.
|
|
919
|
+
*
|
|
920
|
+
* `_updateTime` is the value a `{ updateTime }` precondition on `commit` is
|
|
921
|
+
* compared against. It is Firestore's own write timestamp rather than the
|
|
922
|
+
* `_updatedAt` field beside it, because a transform in the same commit can
|
|
923
|
+
* move the field and cannot touch the write timestamp.
|
|
924
|
+
*
|
|
925
|
+
* None of them may be written back - strip every `_`-prefixed key before
|
|
926
|
+
* sending a document you read.
|
|
927
|
+
*/
|
|
928
|
+
data: Record<string, unknown>;
|
|
929
|
+
}
|
|
930
|
+
/** The comparison operators a store query may use - narrower than Firestore's own set. */
|
|
931
|
+
export type StoreQueryOperator = "==" | "!=" | "<" | "<=" | ">" | ">=" | "in" | "not-in" | "array-contains";
|
|
932
|
+
export interface StoreQueryFilter {
|
|
933
|
+
/** Must be one of the collection's declared `queryableFields`. */
|
|
934
|
+
field: string;
|
|
935
|
+
op: StoreQueryOperator;
|
|
936
|
+
value: unknown;
|
|
937
|
+
}
|
|
938
|
+
export interface StoreOrderBy {
|
|
939
|
+
field: string;
|
|
940
|
+
direction: "asc" | "desc";
|
|
941
|
+
}
|
|
942
|
+
export interface StoreQueryPage {
|
|
943
|
+
items: StoreDocument[];
|
|
944
|
+
/** Pass as the next `cursor`. `null` is the end of the collection. */
|
|
945
|
+
nextCursor: string | null;
|
|
946
|
+
}
|
|
947
|
+
export type StoreAggregationOperation = "count" | "sum" | "avg";
|
|
948
|
+
/**
|
|
949
|
+
* One aggregation over a filtered collection.
|
|
950
|
+
*
|
|
951
|
+
* `alias` is what the result is keyed by, so a single request can carry a
|
|
952
|
+
* count and a sum without the response shape depending on argument order.
|
|
953
|
+
*/
|
|
954
|
+
export interface StoreAggregation {
|
|
955
|
+
op: StoreAggregationOperation;
|
|
956
|
+
/** Required for `sum`/`avg`. `count` counts documents and takes none. */
|
|
957
|
+
field?: string | null;
|
|
958
|
+
alias: string;
|
|
959
|
+
}
|
|
960
|
+
export interface StoreAggregatePage {
|
|
961
|
+
/**
|
|
962
|
+
* One number per alias.
|
|
963
|
+
*
|
|
964
|
+
* A `sum` or `avg` over nothing is `null`, not `0` - "nothing matched" and
|
|
965
|
+
* "the total is zero" are different facts. A `count` is always a number.
|
|
966
|
+
*/
|
|
967
|
+
values: Record<string, number | null>;
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* A value Connect computes at write time instead of storing verbatim.
|
|
971
|
+
*
|
|
972
|
+
* Build them with the `increment`/`serverTimestamp`/`arrayUnion`/
|
|
973
|
+
* `arrayRemove`/`deleteField` helpers rather than by hand - the sentinel shape
|
|
974
|
+
* is an encoding, not an API.
|
|
975
|
+
*
|
|
976
|
+
* `increment` is the one that cannot be expressed any other way: it is
|
|
977
|
+
* commutative, so concurrent increments all land with no precondition and no
|
|
978
|
+
* retry, which read-modify-write from your own process can never guarantee.
|
|
979
|
+
*/
|
|
980
|
+
export type StoreFieldTransform = {
|
|
981
|
+
__transform: "increment";
|
|
982
|
+
by: number;
|
|
983
|
+
} | {
|
|
984
|
+
__transform: "serverTimestamp";
|
|
985
|
+
} | {
|
|
986
|
+
__transform: "arrayUnion";
|
|
987
|
+
values: unknown[];
|
|
988
|
+
} | {
|
|
989
|
+
__transform: "arrayRemove";
|
|
990
|
+
values: unknown[];
|
|
991
|
+
} | {
|
|
992
|
+
__transform: "delete";
|
|
993
|
+
};
|
|
994
|
+
/**
|
|
995
|
+
* What must still be true of a document for a commit to land.
|
|
996
|
+
*
|
|
997
|
+
* `{ updateTime }` is the `_updateTime` a read returned - the optimistic half
|
|
998
|
+
* of read, decide, commit. A precondition that no longer holds fails the whole
|
|
999
|
+
* commit with a 409 and writes nothing.
|
|
1000
|
+
*/
|
|
1001
|
+
export type StorePrecondition = {
|
|
1002
|
+
exists: boolean;
|
|
1003
|
+
} | {
|
|
1004
|
+
updateTime: string;
|
|
1005
|
+
};
|
|
1006
|
+
export type StoreCommitMode = "create" | "replace" | "merge" | "delete";
|
|
1007
|
+
/** One write inside a commit. Each names its own collection - a commit may span several. */
|
|
1008
|
+
export interface StoreCommitOperation {
|
|
1009
|
+
op: StoreCommitMode;
|
|
1010
|
+
collection: string;
|
|
1011
|
+
/** Omit only on `create`, to have an id allocated. */
|
|
1012
|
+
id?: string | null;
|
|
1013
|
+
/** Absent on `delete`. Top-level values may be `StoreFieldTransform`s. */
|
|
1014
|
+
data?: Record<string, unknown> | null;
|
|
1015
|
+
/** Not accepted on `create`, which already means "and it must not exist". */
|
|
1016
|
+
precondition?: StorePrecondition | null;
|
|
1017
|
+
}
|
|
1018
|
+
export interface StoreCommitResult {
|
|
1019
|
+
/** The document id each operation wrote, positionally aligned with the operations sent. */
|
|
1020
|
+
ids: string[];
|
|
1021
|
+
}
|
|
1022
|
+
/** One entry from the `kv` store. */
|
|
1023
|
+
export interface KvEntry {
|
|
1024
|
+
key: string;
|
|
1025
|
+
value: unknown;
|
|
1026
|
+
/** ISO-8601, or `null` when the entry never expires. */
|
|
1027
|
+
expiresAt: string | null;
|
|
1028
|
+
updatedAt: string;
|
|
1029
|
+
}
|
|
1030
|
+
export interface KvPage {
|
|
1031
|
+
items: KvEntry[];
|
|
1032
|
+
/**
|
|
1033
|
+
* Pass as the next `cursor`. `null` is the end of the namespace.
|
|
1034
|
+
*
|
|
1035
|
+
* A page can be shorter than `limit` and still have one - expired entries
|
|
1036
|
+
* are filtered after paging - so this, not an empty `items`, is the signal
|
|
1037
|
+
* to stop.
|
|
1038
|
+
*/
|
|
1039
|
+
nextCursor: string | null;
|
|
1040
|
+
}
|
|
1041
|
+
/** One row from a cross-workspace scan - carries the workspace it belongs to. */
|
|
1042
|
+
export interface ScannedStoreDocument extends StoreDocument {
|
|
1043
|
+
workspaceId: string;
|
|
1044
|
+
}
|
|
1045
|
+
export interface StoreScanPage {
|
|
1046
|
+
items: ScannedStoreDocument[];
|
|
1047
|
+
/** `{workspaceId}/{documentId}`. Pass as the next `cursor`; `null` is the end. */
|
|
1048
|
+
nextCursor: string | null;
|
|
1049
|
+
}
|
|
1050
|
+
/** One workspace the signed-in user belongs to - a row in a workspace switcher. */
|
|
1051
|
+
export interface UserWorkspace {
|
|
1052
|
+
workspaceId: string;
|
|
1053
|
+
name: string;
|
|
1054
|
+
type: WorkspaceType;
|
|
1055
|
+
/** The workspace this session is currently pinned to. */
|
|
1056
|
+
isActive: boolean;
|
|
1057
|
+
/**
|
|
1058
|
+
* Whether your application is sold to this kind of workspace. `false` rows
|
|
1059
|
+
* are worth showing disabled with the reason - a workspace that silently
|
|
1060
|
+
* disappears from a switcher reads as data loss to the person who owns it.
|
|
1061
|
+
*/
|
|
1062
|
+
inAudience: boolean;
|
|
1063
|
+
/** Whether it holds an entitling subscription to your application. */
|
|
1064
|
+
subscribed: boolean;
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* The result of a workspace switch.
|
|
1068
|
+
*
|
|
1069
|
+
* `sessionToken` is a **new** credential - the one you switched from is
|
|
1070
|
+
* revoked. Persist this before navigating, or the user is signed out.
|
|
1071
|
+
*/
|
|
1072
|
+
export interface WorkspaceSwitchResult {
|
|
1073
|
+
sessionToken: string;
|
|
1074
|
+
sessionTokenExpiresIn: number;
|
|
1075
|
+
workspaceId: string;
|
|
1076
|
+
workspaceName: string;
|
|
1077
|
+
workspaceType: WorkspaceType;
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* One member of the workspace a session is on.
|
|
1081
|
+
*
|
|
1082
|
+
* `granted` is resolved for *your* application - permission ids from your own
|
|
1083
|
+
* catalog, not the member's Connect roles.
|
|
1084
|
+
*/
|
|
1085
|
+
export interface ConnectWorkspaceMember {
|
|
1086
|
+
userId: string;
|
|
1087
|
+
email: string;
|
|
1088
|
+
displayName: string | null;
|
|
1089
|
+
photoUrl: string | null;
|
|
1090
|
+
status: string;
|
|
1091
|
+
granted: string[];
|
|
1092
|
+
role: string | null;
|
|
1093
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export interface ConnectEmptyStateProps {
|
|
3
|
+
/** Rendered at `size-8`. Any icon element - lucide, an SVG, a glyph. */
|
|
4
|
+
icon?: ReactNode;
|
|
5
|
+
/** What is absent, in the user's words. One line. */
|
|
6
|
+
title: ReactNode;
|
|
7
|
+
description?: ReactNode;
|
|
8
|
+
/**
|
|
9
|
+
* The one control that resolves the emptiness - "New contact" for a list
|
|
10
|
+
* that has never had anything in it, "Clear filter" for one whose contents
|
|
11
|
+
* are merely hidden.
|
|
12
|
+
*/
|
|
13
|
+
action?: ReactNode;
|
|
14
|
+
variant?: "card" | "dashed" | "plain";
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* What a list, grid or table shows when it has nothing to show.
|
|
19
|
+
*
|
|
20
|
+
* **The two empty states are different facts and the copy must say so.** "You
|
|
21
|
+
* have no contacts" and "no contacts match this filter" call for different
|
|
22
|
+
* words and a different action - create the thing, versus undo the filter
|
|
23
|
+
* hiding it. Both render through here; the caller decides which by what it
|
|
24
|
+
* passes as `action`. Getting this wrong costs a user real time: an
|
|
25
|
+
* unfiltered-looking empty state in front of a filtered list reads as data
|
|
26
|
+
* loss.
|
|
27
|
+
*
|
|
28
|
+
* `variant` chooses the container by what the state stands *in*: `card` (the
|
|
29
|
+
* default), `dashed` for a region that will be filled in place - a file grid,
|
|
30
|
+
* a board column - where the outline is what says "things appear here", and
|
|
31
|
+
* `plain` for a slot already inside a card that would otherwise nest two
|
|
32
|
+
* borders.
|
|
33
|
+
*/
|
|
34
|
+
export declare function ConnectEmptyState({ icon, title, description, action, variant, className, }: Readonly<ConnectEmptyStateProps>): import("react").JSX.Element;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page-composition primitives - the layer between HeroUI and your features.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately **not** `"use client"`. Everything here is presentational, with
|
|
5
|
+
* no hooks and no state, so it renders in a Server Component and stays out of
|
|
6
|
+
* the browser bundle. That is the whole reason this is a separate entry point
|
|
7
|
+
* from `@cosmoops/connect-sdk/client`: that module's barrel carries a
|
|
8
|
+
* `"use client"` directive, so re-exporting a `<Card>` wrapper through it
|
|
9
|
+
* would turn every page shell in your app into a client boundary for no gain.
|
|
10
|
+
*
|
|
11
|
+
* The interactive counterparts - a confirm dialog, cursor pagination, filter
|
|
12
|
+
* tabs - hold state and live in `/client` alongside the rest of the browser
|
|
13
|
+
* half.
|
|
14
|
+
*
|
|
15
|
+
* ```tsx
|
|
16
|
+
* import { ConnectPageShell, ConnectSection } from "@cosmoops/connect-sdk/ui";
|
|
17
|
+
*
|
|
18
|
+
* export default function ContactsPage() {
|
|
19
|
+
* return (
|
|
20
|
+
* <ConnectPageShell title="Contacts" description="Everyone you do business with.">
|
|
21
|
+
* <ConnectPageShell.Async fallback={<TableSkeleton />}>
|
|
22
|
+
* <ContactsTable />
|
|
23
|
+
* </ConnectPageShell.Async>
|
|
24
|
+
* </ConnectPageShell>
|
|
25
|
+
* );
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export { ConnectPageShell } from "./page-shell.js";
|
|
30
|
+
export type { ConnectPageShellProps } from "./page-shell.js";
|
|
31
|
+
export { ConnectPageHeader } from "./page-header.js";
|
|
32
|
+
export type { ConnectPageHeaderProps } from "./page-header.js";
|
|
33
|
+
export { ConnectSection } from "./section.js";
|
|
34
|
+
export type { ConnectSectionProps } from "./section.js";
|
|
35
|
+
export { ConnectEmptyState } from "./empty-state.js";
|
|
36
|
+
export type { ConnectEmptyStateProps } from "./empty-state.js";
|
|
37
|
+
export { ConnectSettingRow } from "./setting-row.js";
|
|
38
|
+
export type { ConnectSettingRowProps } from "./setting-row.js";
|
|
39
|
+
export { ConnectStatusChip } from "./status-chip.js";
|
|
40
|
+
export type { ConnectStatusTone, ConnectStatusPresentation } from "./status-chip.js";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export interface ConnectPageHeaderProps {
|
|
3
|
+
title: ReactNode;
|
|
4
|
+
description?: ReactNode;
|
|
5
|
+
/**
|
|
6
|
+
* Rendered opposite the title. Keep it to one control - a page has one
|
|
7
|
+
* primary action, and a second here has nothing to align against.
|
|
8
|
+
*/
|
|
9
|
+
action?: ReactNode;
|
|
10
|
+
/** Set `false` when the description carries a link that must stay readable on a phone. */
|
|
11
|
+
clamp?: boolean;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The title / description / action block a page opens with.
|
|
16
|
+
*
|
|
17
|
+
* The structure is the point, not the styling. The action is a peer of the
|
|
18
|
+
* **title**, not of the block, so nothing below the first line can move it -
|
|
19
|
+
* the common hand-rolled shape puts the action after the description, which
|
|
20
|
+
* makes the vertical position of a page's primary control a function of how
|
|
21
|
+
* long someone wrote the paragraph above it. On a phone that pushes it most of
|
|
22
|
+
* a thumb's reach down the screen.
|
|
23
|
+
*
|
|
24
|
+
* Mobile-first, and the two axes are separate decisions: the title row is a
|
|
25
|
+
* `justify-between` at every width (title left, action right, where a thumb
|
|
26
|
+
* expects it), while the description is clamped to two lines on a phone and
|
|
27
|
+
* released from `sm` up.
|
|
28
|
+
*
|
|
29
|
+
* An `h2`, because the document's `h1` is the application itself.
|
|
30
|
+
*
|
|
31
|
+
* No hooks and no state, so this renders from a Server Component - which is
|
|
32
|
+
* why it lives in `@cosmoops/connect-sdk/ui` rather than `/client`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function ConnectPageHeader({ title, description, action, clamp, className, }: Readonly<ConnectPageHeaderProps>): import("react").JSX.Element;
|