@cosmoops/connect-sdk 0.33.0 → 0.34.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 +111 -0
- package/README.md +17 -5
- package/dist/cjs/client/components/access-gate.js +98 -2
- package/dist/cjs/client/provider.js +5 -2
- package/dist/cjs/client/use-checkout.js +3 -1
- package/dist/cjs/helpers.js +46 -0
- package/dist/cjs/index.js +5 -1
- package/dist/cjs/next/index.js +10 -3
- package/dist/cjs/server/client.js +1 -1
- package/dist/cjs/server/resources/billing.js +5 -0
- package/dist/cjs/server/resources/entitlements.js +47 -0
- package/dist/esm/client/components/access-gate.js +99 -3
- package/dist/esm/client/provider.js +5 -2
- package/dist/esm/client/use-checkout.js +3 -1
- package/dist/esm/helpers.js +43 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/next/index.js +10 -3
- package/dist/esm/server/client.js +1 -1
- package/dist/esm/server/resources/billing.js +5 -0
- package/dist/esm/server/resources/entitlements.js +47 -0
- package/dist/types/client/components/access-gate.d.ts +9 -0
- package/dist/types/client/provider.d.ts +155 -51
- package/dist/types/client/use-checkout.d.ts +18 -1
- package/dist/types/helpers.d.ts +36 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/server/client.d.ts +1 -1
- package/dist/types/server/resources/billing.d.ts +4 -0
- package/dist/types/server/resources/entitlements.d.ts +40 -1
- package/dist/types/types.d.ts +149 -0
- package/package.json +1 -1
|
@@ -77,6 +77,23 @@ export interface CheckoutUrlOptions {
|
|
|
77
77
|
subscriptionId?: string | null;
|
|
78
78
|
/** Carried into Connect's checkout, where it is validated against the real cart. */
|
|
79
79
|
coupon?: string | null;
|
|
80
|
+
/**
|
|
81
|
+
* Where Connect sends the customer once the subscription is live - an
|
|
82
|
+
* absolute URL on your own origin.
|
|
83
|
+
*
|
|
84
|
+
* **Without it they do not come back.** Connect's default destination is
|
|
85
|
+
* the new subscription's own billing page, which is the right answer for
|
|
86
|
+
* someone who started in Connect and the wrong one for someone who started
|
|
87
|
+
* on your pricing page: they pay, land on a billing screen for a product
|
|
88
|
+
* they were in the middle of adopting, and nothing on it points home.
|
|
89
|
+
*
|
|
90
|
+
* Connect validates the origin against the redirect URIs registered for
|
|
91
|
+
* your Connect client and silently ignores anything else, so a value it
|
|
92
|
+
* does not recognise costs the return trip and never redirects your
|
|
93
|
+
* customer somewhere you did not register. Register the origin you intend
|
|
94
|
+
* to use; the path and query are yours.
|
|
95
|
+
*/
|
|
96
|
+
next?: string | null;
|
|
80
97
|
}
|
|
81
98
|
/**
|
|
82
99
|
* The Connect URL that cancels a subscription.
|
|
@@ -103,4 +120,4 @@ export declare function buildCancelUrl({ appUrl, subscriptionId }: {
|
|
|
103
120
|
* prefix that has never served this page. Building the other one is what
|
|
104
121
|
* sent every first-time subscriber from a partner's pricing page to a 404.
|
|
105
122
|
*/
|
|
106
|
-
export declare function buildCheckoutUrl({ appUrl, applicationId, planId, subscriptionId, coupon, }: CheckoutUrlOptions): string;
|
|
123
|
+
export declare function buildCheckoutUrl({ appUrl, applicationId, planId, subscriptionId, coupon, next, }: CheckoutUrlOptions): string;
|
package/dist/types/helpers.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PlanFeature, ReadinessBlocker, ReadinessResponse } from "./types.js";
|
|
1
|
+
import type { PlanFeature, PlanLimit, ReadinessBlocker, ReadinessResponse } from "./types.js";
|
|
2
2
|
/**
|
|
3
3
|
* The blockers that actually deny access, in the order to show them.
|
|
4
4
|
*
|
|
@@ -31,3 +31,38 @@ export type WirePlanFeature = string | PlanFeature;
|
|
|
31
31
|
* hand-rolled a bridge instead of using `createConnectHandler`.
|
|
32
32
|
*/
|
|
33
33
|
export declare function normalizePlanFeatures(features: readonly WirePlanFeature[] | undefined): PlanFeature[];
|
|
34
|
+
/**
|
|
35
|
+
* What a key the plan carries no answer for reads as - see
|
|
36
|
+
* `SubscribedPlan.limits`, which is sparse.
|
|
37
|
+
*
|
|
38
|
+
* `"none"` rather than `"unlimited"`: a plan that was never asked about a
|
|
39
|
+
* limit has not sold it, and defaulting the other way would hand an unbounded
|
|
40
|
+
* allowance to every tier that predates the question.
|
|
41
|
+
*/
|
|
42
|
+
export declare const NO_PLAN_LIMIT: PlanLimit;
|
|
43
|
+
/**
|
|
44
|
+
* One plan's answer for one limit key, with the sparse-map default applied.
|
|
45
|
+
*
|
|
46
|
+
* Indexing `plan.limits` directly is what makes a missing key `undefined`,
|
|
47
|
+
* which then reads as unlimited through the first `?.` somebody writes. This
|
|
48
|
+
* returns a `PlanLimit` either way.
|
|
49
|
+
*/
|
|
50
|
+
export declare function planLimitFor(limits: Readonly<Record<string, PlanLimit>> | undefined, key: string): PlanLimit;
|
|
51
|
+
/**
|
|
52
|
+
* Whether `additional` more of the resource fit under this limit, given how
|
|
53
|
+
* many the subscriber already has. The comparison, written once.
|
|
54
|
+
*
|
|
55
|
+
* Every way of writing it at a call site is wrong in a different direction:
|
|
56
|
+
* `count < limit.value` is off by one as soon as you add more than one row,
|
|
57
|
+
* `if (!limit.value)` refuses the unlimited tier, and
|
|
58
|
+
* `limit.value ?? Infinity` grants unlimited to a plan that allows none. This
|
|
59
|
+
* is the same implementation Connect's own server uses to validate a plan.
|
|
60
|
+
*
|
|
61
|
+
* ```ts
|
|
62
|
+
* const limit = await connect.entitlements.getLimit(workspaceId, "sites");
|
|
63
|
+
* if (!planLimitAllows(limit, await countSites(workspaceId))) {
|
|
64
|
+
* throw new Error("Your plan's site limit is reached.");
|
|
65
|
+
* }
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare function planLimitAllows(limit: PlanLimit, currentCount: number, additional?: number): boolean;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
export { ConnectError, isConnectError } from "./errors.js";
|
|
17
17
|
export { blockingBlockers, normalizePlanFeatures } from "./helpers.js";
|
|
18
|
+
export { NO_PLAN_LIMIT, planLimitAllows, planLimitFor } from "./helpers.js";
|
|
18
19
|
export { arrayRemove, arrayUnion, deleteField, documentExists, increment, serverTimestamp, storeIndexFor, storeIndexSignature, unchangedSince, } from "./store.js";
|
|
19
20
|
export type { StoreIndexDirection, StoreIndexField } from "./store.js";
|
|
20
21
|
export { describeQueryRejection, storeIndexCovers } from "./store-schema.js";
|
|
@@ -12,6 +12,10 @@ export declare class BillingResource {
|
|
|
12
12
|
* independently of the instance it talks to. Without the coercion a partner
|
|
13
13
|
* pointed at a Connect that has not upgraded yet renders a feature list of
|
|
14
14
|
* blanks - which is exactly what happened. See `normalizePlanFeatures`.
|
|
15
|
+
*
|
|
16
|
+
* `limits` is defaulted for the same reason, one version later: an instance
|
|
17
|
+
* that predates limits sends no such field, and a pricing grid mapping over
|
|
18
|
+
* `plan.limits` would throw rather than simply show no limit rows.
|
|
15
19
|
*/
|
|
16
20
|
listPlans(): Promise<PlansResponse>;
|
|
17
21
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type TransportOptions } from "../http.js";
|
|
2
|
-
import type { ApplicationSubscriber, ReadinessResponse, SubscribersResponse, SubscriptionResponse } from "../../types.js";
|
|
2
|
+
import type { ApplicationSubscriber, PlanLimit, ReadinessResponse, SubscribersResponse, SubscriptionResponse } from "../../types.js";
|
|
3
3
|
/**
|
|
4
4
|
* The two checks worth making on every request: may this workspace be here at
|
|
5
5
|
* all, and what is it paying for.
|
|
@@ -28,6 +28,45 @@ export declare class EntitlementsResource {
|
|
|
28
28
|
* should not need a try/catch around it.
|
|
29
29
|
*/
|
|
30
30
|
hasFeature(workspaceId: string, feature: string): Promise<boolean>;
|
|
31
|
+
/**
|
|
32
|
+
* How much of a metered resource this workspace may have - the numeric
|
|
33
|
+
* counterpart of `hasFeature`, for the resources your application counts
|
|
34
|
+
* itself (sites, projects, pipelines).
|
|
35
|
+
*
|
|
36
|
+
* **This is the effective ceiling, not the plan's own number.** Connect has
|
|
37
|
+
* already folded in whatever the add-ons on the subscription raise it by, so
|
|
38
|
+
* "Starter includes 1 site, they bought two 5-site packs" arrives here as
|
|
39
|
+
* `{ model: "limited", value: 11 }`. You never join the plan against the
|
|
40
|
+
* add-on list yourself, and a customer who buys another pack needs no change
|
|
41
|
+
* on your side.
|
|
42
|
+
*
|
|
43
|
+
* Returns `{ model: "none" }` rather than throwing when there is no active
|
|
44
|
+
* subscription, when the plan carries no answer for this key, and when the
|
|
45
|
+
* Connect instance predates limits entirely - the same reasoning
|
|
46
|
+
* `hasFeature` gives for answering `false`: "not entitled" is the same answer
|
|
47
|
+
* either way, and a limit check should not need a try/catch around it.
|
|
48
|
+
*
|
|
49
|
+
* Connect does not enforce this. It cannot see what you are counting, so it
|
|
50
|
+
* hands you the number and your code refuses the next row - `canAdd` is that
|
|
51
|
+
* comparison if you already have the count.
|
|
52
|
+
*/
|
|
53
|
+
getLimit(workspaceId: string, key: string): Promise<PlanLimit>;
|
|
54
|
+
/**
|
|
55
|
+
* Whether this workspace may add `additional` more of a limited resource,
|
|
56
|
+
* given how many it already has.
|
|
57
|
+
*
|
|
58
|
+
* You supply the count, because only your application can produce it. This
|
|
59
|
+
* is `getLimit` plus `planLimitAllows`, which exists so the comparison is
|
|
60
|
+
* not rewritten per call site - see that helper for the three ways of
|
|
61
|
+
* getting it wrong.
|
|
62
|
+
*
|
|
63
|
+
* ```ts
|
|
64
|
+
* if (!(await connect.entitlements.canAdd(workspaceId, "sites", sites.length))) {
|
|
65
|
+
* return refuse("Your plan's site limit is reached.");
|
|
66
|
+
* }
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
canAdd(workspaceId: string, key: string, currentCount: number, additional?: number): Promise<boolean>;
|
|
31
70
|
/**
|
|
32
71
|
* One page of the workspaces subscribed to your application, newest first.
|
|
33
72
|
*
|
package/dist/types/types.d.ts
CHANGED
|
@@ -292,6 +292,57 @@ export interface PlanFeature {
|
|
|
292
292
|
/** Empty string when the catalog carries no description. */
|
|
293
293
|
description: string;
|
|
294
294
|
}
|
|
295
|
+
/**
|
|
296
|
+
* How much of one metered resource a plan allows, as the **catalogue** read
|
|
297
|
+
* reports it: the application's own declaration (`key`, `name`, `unit`) joined
|
|
298
|
+
* to this plan's answer.
|
|
299
|
+
*
|
|
300
|
+
* The limits counterpart of `PlanFeature`, and it carries the same asymmetry
|
|
301
|
+
* against `SubscribedPlan.limits` for the same reason - that one is a contract
|
|
302
|
+
* keyed for lookup, this one is a row to render.
|
|
303
|
+
*
|
|
304
|
+
* There is one entry per limit the application declares, including the ones a
|
|
305
|
+
* tier allows none of (`model: "none"`). "Starter: 1 site" only means anything
|
|
306
|
+
* beside "Growth: 5 sites", so a comparison table needs the row even where the
|
|
307
|
+
* answer is nothing; a card showing only what a tier includes filters on
|
|
308
|
+
* `model !== "none"`.
|
|
309
|
+
*/
|
|
310
|
+
export interface ResolvedPlanLimit {
|
|
311
|
+
/** What `getLimit` and `SubscribedPlan.limits` are keyed by. */
|
|
312
|
+
key: string;
|
|
313
|
+
/** Human label, e.g. "Sites". */
|
|
314
|
+
name: string;
|
|
315
|
+
/** The noun the number needs, e.g. "sites" - render as `${value} ${unit}`. */
|
|
316
|
+
unit: string;
|
|
317
|
+
/** Empty string when the application's catalog carries no description. */
|
|
318
|
+
description: string;
|
|
319
|
+
model: PlanLimit["model"];
|
|
320
|
+
/** Non-null iff `model` is `"limited"`. */
|
|
321
|
+
value: number | null;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* What a plan allows of one metered resource - the **gating** shape, which is
|
|
325
|
+
* what `getLimit` returns and `planLimitAllows` takes.
|
|
326
|
+
*
|
|
327
|
+
* **Three states rather than a nullable number, deliberately.** The obvious
|
|
328
|
+
* encoding - a `number | null` where `null` means unlimited - makes
|
|
329
|
+
* `if (!limit) refuse()` read as correct while refusing every subscriber on
|
|
330
|
+
* the most generous tier, which is the one case nobody tests. Use
|
|
331
|
+
* `planLimitAllows` rather than comparing by hand: `count < value` is off by
|
|
332
|
+
* one when adding more than one row, and `value ?? Infinity` grants unlimited
|
|
333
|
+
* to a plan that allows none.
|
|
334
|
+
*/
|
|
335
|
+
export interface PlanLimit {
|
|
336
|
+
/**
|
|
337
|
+
* - `"none"`: this plan does not allow the resource at all. Also what a key
|
|
338
|
+
* the plan carries no answer for reads as - see `SubscribedPlan.limits`.
|
|
339
|
+
* - `"limited"`: a ceiling of `value`.
|
|
340
|
+
* - `"unlimited"`: no ceiling.
|
|
341
|
+
*/
|
|
342
|
+
model: "none" | "limited" | "unlimited";
|
|
343
|
+
/** The ceiling. Non-null iff `model` is `"limited"`, and always >= 1 there - zero is spelled `"none"`. */
|
|
344
|
+
value: number | null;
|
|
345
|
+
}
|
|
295
346
|
export interface Plan {
|
|
296
347
|
id: string;
|
|
297
348
|
name: string;
|
|
@@ -319,6 +370,8 @@ export interface Plan {
|
|
|
319
370
|
trialDays: number | null;
|
|
320
371
|
/** What this plan unlocks, labelled for display - see `PlanFeature`. */
|
|
321
372
|
features: PlanFeature[];
|
|
373
|
+
/** How much of each metered resource this plan allows, labelled for display - see `ResolvedPlanLimit`. */
|
|
374
|
+
limits: ResolvedPlanLimit[];
|
|
322
375
|
}
|
|
323
376
|
/**
|
|
324
377
|
* How often a plan bills.
|
|
@@ -351,6 +404,27 @@ export interface SubscribedPlan {
|
|
|
351
404
|
* comparable, a display name is neither.
|
|
352
405
|
*/
|
|
353
406
|
features: string[];
|
|
407
|
+
/**
|
|
408
|
+
* How much of each metered resource this workspace may have, keyed by the
|
|
409
|
+
* limit key your application declared - see `PlanLimit`.
|
|
410
|
+
*
|
|
411
|
+
* **Effective, not the tier's raw number**: whatever the add-ons on this
|
|
412
|
+
* subscription raise a limit by is already folded in, so this is the figure
|
|
413
|
+
* to compare your own count against. The tier's base number and the add-ons
|
|
414
|
+
* that raised it are deliberately not broken out here - that is the join
|
|
415
|
+
* Connect does so nobody has to redo it per resource, per partner.
|
|
416
|
+
*
|
|
417
|
+
* **Sparse, and that is the thing to get right**: a key absent here is not
|
|
418
|
+
* "unlimited" and not an error, it is `{ model: "none" }`. Read it through
|
|
419
|
+
* `getLimit`/`planLimitFor` rather than indexing the map, which is what
|
|
420
|
+
* turns a missing key into `undefined` and then, one `?.` later, into an
|
|
421
|
+
* unbounded allowance.
|
|
422
|
+
*
|
|
423
|
+
* Same posture as `features`: Connect cannot intercept calls to your
|
|
424
|
+
* backend, so your backend compares its own count against this number.
|
|
425
|
+
* Connect stores the answer and hands it over.
|
|
426
|
+
*/
|
|
427
|
+
limits: Record<string, PlanLimit>;
|
|
354
428
|
}
|
|
355
429
|
export interface AppliedDiscount {
|
|
356
430
|
source: "coupon" | "admin";
|
|
@@ -1026,6 +1100,67 @@ export interface EmitEventsResponse {
|
|
|
1026
1100
|
* registered a handler for.
|
|
1027
1101
|
*/
|
|
1028
1102
|
export type ConnectWebhookEventType = "workspace.deletion_scheduled" | "workspace.deletion_cancelled" | "workspace.deleted" | "subscription.cancelled" | "call.completed" | "call.missed" | "booking.confirmed" | "booking.cancelled" | "booking.rescheduled" | "contact.created" | "contact.updated";
|
|
1103
|
+
/** What becomes of one class of data when the workspace is purged. */
|
|
1104
|
+
export type ConnectDataDisposition = "erase" | "anonymize" | "retain";
|
|
1105
|
+
/** Who answers for your copy of the customer's data. See the deletion-policy guide. */
|
|
1106
|
+
export type ConnectFiduciaryRole = "connect_owned" | "processor" | "independent";
|
|
1107
|
+
/** One kind of data your application holds, and what your policy said happens to it. */
|
|
1108
|
+
export interface ConnectDataClassNotice {
|
|
1109
|
+
/** Stable key, as declared in Console. Safe to switch on. */
|
|
1110
|
+
key: string;
|
|
1111
|
+
/** What the customer was shown - e.g. "Call records". */
|
|
1112
|
+
label: string;
|
|
1113
|
+
disposition: ConnectDataDisposition;
|
|
1114
|
+
/**
|
|
1115
|
+
* How many days this class outlives the workspace **in your own archive**,
|
|
1116
|
+
* or `null` when it goes with the purge.
|
|
1117
|
+
*
|
|
1118
|
+
* The one number in either deletion event you have to act on, and the
|
|
1119
|
+
* reason `policy` is on the wire at all: `purgeAfter` is Connect's clock,
|
|
1120
|
+
* this is yours. Read it rather than hardcoding the figure you typed into
|
|
1121
|
+
* Console - that copy drifts the first time somebody edits the policy, and
|
|
1122
|
+
* this one is the version the customer was actually promised.
|
|
1123
|
+
*
|
|
1124
|
+
* It is Connect's clamped value, so it is never longer than the platform
|
|
1125
|
+
* ceiling even if your stored policy says otherwise.
|
|
1126
|
+
*/
|
|
1127
|
+
retainDays: number | null;
|
|
1128
|
+
/** The clause or rule you named as requiring it, shown to the customer verbatim. `null` for a class that is erased. */
|
|
1129
|
+
basis: string | null;
|
|
1130
|
+
}
|
|
1131
|
+
/** An identifier allocated to the workspace that must be held before reissue - a third clock, neither the grace window nor a retention floor. */
|
|
1132
|
+
export interface ConnectResourceReleaseNotice {
|
|
1133
|
+
key: string;
|
|
1134
|
+
label: string;
|
|
1135
|
+
/** Days to hold it before handing it to somebody else. */
|
|
1136
|
+
quarantineDays: number;
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Your application's own data policy, as it stood when this deletion was
|
|
1140
|
+
* confirmed - the declaration the customer read before pressing the button.
|
|
1141
|
+
*
|
|
1142
|
+
* Carried on both deletion events so your handler can be driven by the
|
|
1143
|
+
* declaration rather than by a second copy of it in your code. Two things
|
|
1144
|
+
* about it are worth relying on:
|
|
1145
|
+
*
|
|
1146
|
+
* **It is frozen.** An operator editing the policy while a workspace is
|
|
1147
|
+
* already pending deletion does not change what that deletion runs under;
|
|
1148
|
+
* `version` is which one it is, so a deletion certificate can name it.
|
|
1149
|
+
*
|
|
1150
|
+
* **It is yours alone.** A deletion fans out to every application the
|
|
1151
|
+
* workspace subscribed to, and each one receives only its own policy.
|
|
1152
|
+
*/
|
|
1153
|
+
export interface ConnectDeletionPolicyNotice {
|
|
1154
|
+
/** The policy version in force when the deletion was confirmed. */
|
|
1155
|
+
version: number;
|
|
1156
|
+
fiduciaryRole: ConnectFiduciaryRole;
|
|
1157
|
+
/** Whether your policy promised the customer an export before the window closed. */
|
|
1158
|
+
exportBeforePurge: boolean;
|
|
1159
|
+
policyUrl: string | null;
|
|
1160
|
+
/** Empty when your application has declared no policy - which the customer was shown as "this application hasn't told us", not as "nothing is kept". */
|
|
1161
|
+
dataClasses: ConnectDataClassNotice[];
|
|
1162
|
+
resourceReleases: ConnectResourceReleaseNotice[];
|
|
1163
|
+
}
|
|
1029
1164
|
/**
|
|
1030
1165
|
* A workspace's deletion has been confirmed and the grace window is running.
|
|
1031
1166
|
*
|
|
@@ -1041,6 +1176,13 @@ export interface WorkspaceDeletionScheduledEvent {
|
|
|
1041
1176
|
purgeAfter: string;
|
|
1042
1177
|
requestedBy: "owner" | "platform_admin";
|
|
1043
1178
|
graceDays: number;
|
|
1179
|
+
/**
|
|
1180
|
+
* Your declared policy for this deletion - including how long each data
|
|
1181
|
+
* class may outlive the purge. `null` for a deletion confirmed before
|
|
1182
|
+
* policies existed, which is the only case where you fall back to your own
|
|
1183
|
+
* records.
|
|
1184
|
+
*/
|
|
1185
|
+
policy: ConnectDeletionPolicyNotice | null;
|
|
1044
1186
|
}
|
|
1045
1187
|
/**
|
|
1046
1188
|
* The deletion was withdrawn and the workspace is live again.
|
|
@@ -1058,6 +1200,13 @@ export interface WorkspaceDeletedEvent {
|
|
|
1058
1200
|
workspaceId: string;
|
|
1059
1201
|
/** ISO-8601. */
|
|
1060
1202
|
purgedAt: string;
|
|
1203
|
+
/**
|
|
1204
|
+
* The same frozen policy `workspace.deletion_scheduled` carried, repeated
|
|
1205
|
+
* here because this is the event you act on: whether a class is destroyed
|
|
1206
|
+
* now or moved to a retained store for another `retainDays` is this
|
|
1207
|
+
* object's answer, not your code's.
|
|
1208
|
+
*/
|
|
1209
|
+
policy: ConnectDeletionPolicyNotice | null;
|
|
1061
1210
|
}
|
|
1062
1211
|
/** A subscription ended. Fires on its own, not only as part of a deletion. */
|
|
1063
1212
|
export interface SubscriptionCancelledEvent {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmoops/connect-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.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",
|