@lessly/sdk-app 93.0.0 → 94.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.
package/CHANGELOG.md CHANGED
@@ -29,6 +29,16 @@ are not covered here — 84.0.0 is the first version published as
29
29
  `@lepsto/sdk-app`. `@lessly/sdk-app` 86.0.0 and 87.0.0 were never mirrored to
30
30
  `@lepsto/sdk-app`, so those two numbers are absent from this package.
31
31
 
32
+ ## Unreleased (runtime minor)
33
+
34
+ ### Added
35
+
36
+ - `isEntitlementError(err)` — true for billing's plan refusal: HTTP 402 with a
37
+ body `error` of `entitlement_exhausted` or `entitlement_unavailable`, the same rule the Workspace shell
38
+ uses to show its one refusal window. Structural, like `isAccessDenied`.
39
+ - `upgradeUrl(err)` — the refusal's `upgrade_url` (absolute `http(s)` only),
40
+ for Apps the shell cannot see. Recipe: `docs/recipes/entitlements.md`.
41
+
32
42
  ## 87.3.1 — 2026-09-17
33
43
 
34
44
  ### Fixed
@@ -115,15 +125,9 @@ before this point lives in that package.
115
125
  The publish pipeline appends its machine-rendered block for the release being
116
126
  built below this line. It is not committed back to the repo, so in a git
117
127
  checkout this section is empty — see the note at the top.
118
- ## 93.0.0 (major)
119
-
120
- ### platform
121
- - removed `organization_billing_caps_clear`
122
- - removed `organization_billing_caps_set`
123
- - added `organization_billing_budget_clear`
124
- - added `organization_billing_budget_resume`
125
- - added `organization_billing_budget_set`
126
- - `organization_billing_budgets_get`: description updated
127
- - `organization_member_list`: new optional field `members.email (output)`
128
- - `organization_member_list`: description updated
128
+ ## 94.1.0 (minor)
129
+
130
+ ### *
131
+ - runtime update
132
+ - documentation update
129
133
 
@@ -1,7 +1,7 @@
1
1
  export { createLesslyApp } from './runtime/createLesslyApp.js';
2
2
  export type { LesslyClientBase } from './runtime/createLesslyApp.js';
3
3
  export { connectStream } from './runtime/connectStream.js';
4
- export { LesslyApiError, isAccessDenied } from './runtime/errors.js';
4
+ export { LesslyApiError, isAccessDenied, isEntitlementError, upgradeUrl } from './runtime/errors.js';
5
5
  export { accessReason } from './runtime/access-reason.js';
6
6
  export { platformApiBaseUrl } from './runtime/platform-base-url.js';
7
7
  export { operations } from './runtime/operations.js';
@@ -16,3 +16,16 @@ export declare function parseErrorBody(body: unknown, status: number): LesslyApi
16
16
  * report false there and an App would render a red crash for an ordinary permission refusal.
17
17
  */
18
18
  export declare function isAccessDenied(err: unknown): boolean;
19
+ /**
20
+ * True iff `err` is billing refusing the call over the organization's plan (trial cap reached,
21
+ * a class closed on a trial, billing or budget paused). Inside the Workspace shell the shell
22
+ * shows the refusal itself: an App uses this to render nothing, not a second error.
23
+ */
24
+ export declare function isEntitlementError(err: unknown): boolean;
25
+ /**
26
+ * The refusal's `upgrade_url` — the Workspace billing page with the upgrade offer open for the
27
+ * refused organization — or null when `err` is not an entitlement refusal or carries no usable
28
+ * URL. For Apps the shell cannot see (standalone, or calling through their own `fetch`). Only an
29
+ * absolute http(s) URL is returned: the value is meant to be navigated to.
30
+ */
31
+ export declare function upgradeUrl(err: unknown): string | null;
package/dist/index.cjs CHANGED
@@ -33,6 +33,27 @@ function isAccessDenied(err) {
33
33
  const { status, code } = err;
34
34
  return status === 403 && typeof code === "string" && ACCESS_DENIED_CODES.has(code);
35
35
  }
36
+ var ENTITLEMENT_ERRORS = /* @__PURE__ */ new Set(["entitlement_exhausted", "entitlement_unavailable"]);
37
+ function entitlementRefusal(err) {
38
+ if (typeof err !== "object" || err === null) return null;
39
+ const { status, body } = err;
40
+ if (status !== 402 || typeof body !== "object" || body === null) return null;
41
+ const { error } = body;
42
+ return typeof error === "string" && ENTITLEMENT_ERRORS.has(error) ? body : null;
43
+ }
44
+ function isEntitlementError(err) {
45
+ return entitlementRefusal(err) !== null;
46
+ }
47
+ function upgradeUrl(err) {
48
+ const url = entitlementRefusal(err)?.upgrade_url;
49
+ if (typeof url !== "string") return null;
50
+ try {
51
+ const { protocol } = new URL(url);
52
+ return protocol === "https:" || protocol === "http:" ? url : null;
53
+ } catch {
54
+ return null;
55
+ }
56
+ }
36
57
 
37
58
  // src/runtime/csrf.ts
38
59
  var CSRF_COOKIES = ["lepsto_csrf", "lessly_csrf"];
@@ -12740,7 +12761,9 @@ exports.compositions = compositions2;
12740
12761
  exports.connectStream = connectStream;
12741
12762
  exports.createLesslyApp = createLesslyApp;
12742
12763
  exports.isAccessDenied = isAccessDenied;
12764
+ exports.isEntitlementError = isEntitlementError;
12743
12765
  exports.operations = operations2;
12744
12766
  exports.platformApiBaseUrl = platformApiBaseUrl;
12767
+ exports.upgradeUrl = upgradeUrl;
12745
12768
  //# sourceMappingURL=index.cjs.map
12746
12769
  //# sourceMappingURL=index.cjs.map