@superwall/core 0.2.0 → 0.2.1

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.
@@ -0,0 +1,49 @@
1
+ export type SuperwallErrorCode = "NETWORK" | "AUTH" | "NOT_FOUND" | "TIMEOUT" | "DECODING";
2
+ export declare class SuperwallError extends Error {
3
+ readonly name: string;
4
+ readonly code: SuperwallErrorCode;
5
+ constructor(message: string, code: SuperwallErrorCode);
6
+ }
7
+ export declare class SuperwallNetworkError extends SuperwallError {
8
+ readonly name = "SuperwallNetworkError";
9
+ readonly status?: number;
10
+ readonly url?: string;
11
+ readonly cause?: unknown;
12
+ constructor(message: string, opts?: {
13
+ status?: number;
14
+ url?: string;
15
+ cause?: unknown;
16
+ });
17
+ }
18
+ export declare class SuperwallAuthError extends SuperwallError {
19
+ readonly name = "SuperwallAuthError";
20
+ readonly url?: string;
21
+ constructor(message: string, opts?: {
22
+ url?: string;
23
+ });
24
+ }
25
+ export declare class SuperwallNotFoundError extends SuperwallError {
26
+ readonly name = "SuperwallNotFoundError";
27
+ readonly url?: string;
28
+ constructor(message: string, opts?: {
29
+ url?: string;
30
+ });
31
+ }
32
+ export declare class SuperwallTimeoutError extends SuperwallError {
33
+ readonly name = "SuperwallTimeoutError";
34
+ readonly url?: string;
35
+ readonly timeoutMs?: number;
36
+ constructor(message: string, opts?: {
37
+ url?: string;
38
+ timeoutMs?: number;
39
+ });
40
+ }
41
+ export declare class SuperwallDecodingError extends SuperwallError {
42
+ readonly name = "SuperwallDecodingError";
43
+ readonly url?: string;
44
+ readonly cause?: unknown;
45
+ constructor(message: string, opts?: {
46
+ url?: string;
47
+ cause?: unknown;
48
+ });
49
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,69 @@
1
+ // Plain error classes shared between browser and server SDKs. The browser
2
+ // SDK has additional Effect-flavored variants in its own `internal/errors.ts`
3
+ // — those translate to these classes at the runtime boundary. Server SDK
4
+ // uses these directly.
5
+ export class SuperwallError extends Error {
6
+ name = "SuperwallError";
7
+ code;
8
+ constructor(message, code) {
9
+ super(message);
10
+ this.code = code;
11
+ }
12
+ }
13
+ export class SuperwallNetworkError extends SuperwallError {
14
+ name = "SuperwallNetworkError";
15
+ status;
16
+ url;
17
+ cause;
18
+ constructor(message, opts = {}) {
19
+ super(message, "NETWORK");
20
+ if (opts.status !== undefined)
21
+ this.status = opts.status;
22
+ if (opts.url !== undefined)
23
+ this.url = opts.url;
24
+ if (opts.cause !== undefined)
25
+ this.cause = opts.cause;
26
+ }
27
+ }
28
+ export class SuperwallAuthError extends SuperwallError {
29
+ name = "SuperwallAuthError";
30
+ url;
31
+ constructor(message, opts = {}) {
32
+ super(message, "AUTH");
33
+ if (opts.url !== undefined)
34
+ this.url = opts.url;
35
+ }
36
+ }
37
+ export class SuperwallNotFoundError extends SuperwallError {
38
+ name = "SuperwallNotFoundError";
39
+ url;
40
+ constructor(message, opts = {}) {
41
+ super(message, "NOT_FOUND");
42
+ if (opts.url !== undefined)
43
+ this.url = opts.url;
44
+ }
45
+ }
46
+ export class SuperwallTimeoutError extends SuperwallError {
47
+ name = "SuperwallTimeoutError";
48
+ url;
49
+ timeoutMs;
50
+ constructor(message, opts = {}) {
51
+ super(message, "TIMEOUT");
52
+ if (opts.url !== undefined)
53
+ this.url = opts.url;
54
+ if (opts.timeoutMs !== undefined)
55
+ this.timeoutMs = opts.timeoutMs;
56
+ }
57
+ }
58
+ export class SuperwallDecodingError extends SuperwallError {
59
+ name = "SuperwallDecodingError";
60
+ url;
61
+ cause;
62
+ constructor(message, opts = {}) {
63
+ super(message, "DECODING");
64
+ if (opts.url !== undefined)
65
+ this.url = opts.url;
66
+ if (opts.cause !== undefined)
67
+ this.cause = opts.cause;
68
+ }
69
+ }
@@ -0,0 +1,3 @@
1
+ import type { EnvironmentHosts, NetworkEnvironment } from "./types.js";
2
+ export declare const resolveHosts: (env: NetworkEnvironment) => EnvironmentHosts;
3
+ export declare const isSandbox: (env: NetworkEnvironment) => boolean;
package/dist/hosts.js ADDED
@@ -0,0 +1,33 @@
1
+ const RELEASE_HOSTS = {
2
+ base: "api.superwall.me",
3
+ collector: "collector.superwall.com",
4
+ enrichment: "enrichment-api.superwall.com",
5
+ subscriptions: "subscriptions-api.superwall.com",
6
+ };
7
+ const RC_HOSTS = {
8
+ base: "api.superwallcanary.com",
9
+ collector: "collector.superwallcanary.com",
10
+ enrichment: "enrichment-api.superwall.dev",
11
+ subscriptions: "subscriptions-api.superwall.dev",
12
+ };
13
+ const DEV_HOSTS = {
14
+ base: "api.superwall.dev",
15
+ collector: "collector.superwall.com",
16
+ enrichment: "enrichment-api.superwall.dev",
17
+ subscriptions: "subscriptions-api.superwall.dev",
18
+ };
19
+ export const resolveHosts = (env) => {
20
+ if (typeof env === "string") {
21
+ switch (env) {
22
+ case "release":
23
+ return RELEASE_HOSTS;
24
+ case "releaseCandidate":
25
+ return RC_HOSTS;
26
+ case "developer":
27
+ return DEV_HOSTS;
28
+ }
29
+ }
30
+ return env.custom;
31
+ };
32
+ // Custom environments are typically internal proxies → assume production.
33
+ export const isSandbox = (env) => typeof env === "string" ? env !== "release" : false;
@@ -0,0 +1,4 @@
1
+ export * from "./types.js";
2
+ export * from "./hosts.js";
3
+ export * from "./wire.js";
4
+ export * from "./errors.js";
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./types.js";
2
+ export * from "./hosts.js";
3
+ export * from "./wire.js";
4
+ export * from "./errors.js";
@@ -0,0 +1,69 @@
1
+ export type JsonValue = string | number | boolean | null | JsonValue[] | {
2
+ [key: string]: JsonValue;
3
+ };
4
+ export type SubscriptionStatus = {
5
+ status: "UNKNOWN";
6
+ } | {
7
+ status: "INACTIVE";
8
+ } | {
9
+ status: "ACTIVE";
10
+ entitlements: Entitlement[];
11
+ };
12
+ export type ProductStore = "appStore" | "stripe" | "paddle" | "playStore" | "superwall" | "other";
13
+ export type LatestSubscriptionState = "inGracePeriod" | "subscribed" | "expired" | "inBillingRetryPeriod" | "revoked";
14
+ export type LatestSubscriptionOfferType = "trial" | "code" | "promotional" | "winback";
15
+ export interface Entitlement {
16
+ id: string;
17
+ type: "SERVICE_LEVEL";
18
+ isActive: boolean;
19
+ productIds: string[];
20
+ latestProductId?: string;
21
+ store?: ProductStore;
22
+ startsAt?: number;
23
+ renewedAt?: number;
24
+ expiresAt?: number;
25
+ isLifetime?: boolean;
26
+ willRenew?: boolean;
27
+ state?: LatestSubscriptionState;
28
+ offerType?: LatestSubscriptionOfferType;
29
+ }
30
+ export interface Entitlements {
31
+ active: Entitlement[];
32
+ inactive: Entitlement[];
33
+ all: Entitlement[];
34
+ }
35
+ export type PaywallPresentationStyle = {
36
+ type: "MODAL";
37
+ } | {
38
+ type: "FULLSCREEN";
39
+ } | {
40
+ type: "NO_ANIMATION";
41
+ } | {
42
+ type: "PUSH";
43
+ } | {
44
+ type: "DRAWER";
45
+ height: number;
46
+ cornerRadius: number;
47
+ } | {
48
+ type: "POPUP";
49
+ height: number;
50
+ width: number;
51
+ cornerRadius: number;
52
+ } | {
53
+ type: "NONE";
54
+ };
55
+ export interface CustomEnvironmentHosts {
56
+ base: string;
57
+ collector: string;
58
+ enrichment: string;
59
+ subscriptions: string;
60
+ }
61
+ export type NetworkEnvironment = "release" | "releaseCandidate" | "developer" | {
62
+ custom: CustomEnvironmentHosts;
63
+ };
64
+ export interface EnvironmentHosts {
65
+ readonly base: string;
66
+ readonly collector: string;
67
+ readonly enrichment: string;
68
+ readonly subscriptions: string;
69
+ }
package/dist/types.js ADDED
@@ -0,0 +1,4 @@
1
+ // Cross-SDK domain types. Anything shared between @superwall/paywalls-js
2
+ // (browser) and @superwall/server lives here. Browser-only and server-only
3
+ // types stay in their respective packages.
4
+ export {};
package/dist/wire.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ import type { Entitlements } from "./types.js";
2
+ export interface WebEntitlementsResponse {
3
+ readonly customerInfo?: {
4
+ entitlements?: ReadonlyArray<WireEntitlement>;
5
+ };
6
+ readonly entitlements?: ReadonlyArray<WireEntitlement>;
7
+ /** Short-lived (≈1h) Superwall-signed JWT asserting the active entitlements,
8
+ * for offline server-side verification via `@superwall/verify`. Best-effort:
9
+ * the backend omits it when signing is unavailable (e.g. no key configured
10
+ * in that environment), so treat its absence gracefully. */
11
+ readonly entitlementsToken?: string;
12
+ }
13
+ export interface WireEntitlement {
14
+ /** BE wire shape uses `identifier`; older/other shapes use `id`. */
15
+ identifier?: string;
16
+ id?: string;
17
+ isActive?: boolean;
18
+ productIds?: string[];
19
+ type?: string;
20
+ }
21
+ /**
22
+ * Normalize a `WebEntitlementsResponse` into a domain `Entitlements`
23
+ * bucket. Prefers the `entitlements` top-level array if present, falls
24
+ * back to `customerInfo.entitlements`.
25
+ */
26
+ export declare const parseEntitlements: (res: WebEntitlementsResponse) => Entitlements;
package/dist/wire.js ADDED
@@ -0,0 +1,28 @@
1
+ const toDomain = (e) => ({
2
+ id: e.identifier ?? e.id ?? "",
3
+ type: "SERVICE_LEVEL",
4
+ isActive: e.isActive ?? false,
5
+ productIds: e.productIds ?? [],
6
+ });
7
+ /**
8
+ * Normalize a `WebEntitlementsResponse` into a domain `Entitlements`
9
+ * bucket. Prefers the `entitlements` top-level array if present, falls
10
+ * back to `customerInfo.entitlements`.
11
+ */
12
+ export const parseEntitlements = (res) => {
13
+ // The BE sends `entitlements: []` (empty) at the top level and the real
14
+ // ones under `customerInfo.entitlements`. A plain `??` would pick the
15
+ // empty top-level array (it isn't nullish) and drop the real ones — so
16
+ // only fall back to the top level when customerInfo has nothing.
17
+ const wire = res.customerInfo?.entitlements && res.customerInfo.entitlements.length > 0
18
+ ? res.customerInfo.entitlements
19
+ : res.entitlements && res.entitlements.length > 0
20
+ ? res.entitlements
21
+ : (res.customerInfo?.entitlements ?? res.entitlements ?? []);
22
+ const all = wire.map(toDomain);
23
+ return {
24
+ active: all.filter((e) => e.isActive),
25
+ inactive: all.filter((e) => !e.isActive),
26
+ all,
27
+ };
28
+ };
package/package.json CHANGED
@@ -1,22 +1,31 @@
1
1
  {
2
2
  "name": "@superwall/core",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
9
+ "sideEffects": false,
10
+ "main": "./dist/index.js",
11
+ "module": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "files": [
14
+ "dist"
15
+ ],
9
16
  "exports": {
10
17
  ".": {
11
- "types": "./src/index.ts",
12
- "default": "./src/index.ts"
18
+ "@superwall/source": "./src/index.ts",
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js",
21
+ "default": "./dist/index.js"
13
22
  }
14
23
  },
15
24
  "scripts": {
16
25
  "test": "bun test",
17
26
  "typecheck": "tsc --noEmit",
18
- "build": "echo 'no-op for v0; consumers import .ts directly via Bun/Vite/Next ESM'",
19
- "clean": "rm -rf node_modules .turbo *.tsbuildinfo"
27
+ "build": "bun run ../../scripts/build-package.ts",
28
+ "clean": "rm -rf dist node_modules .turbo *.tsbuildinfo"
20
29
  },
21
30
  "devDependencies": {
22
31
  "@types/bun": "latest",
@@ -1,3 +0,0 @@
1
-
2
- $ echo 'no-op for v0; consumers import .ts directly via Bun/Vite/Next ESM'
3
- no-op for v0; consumers import .ts directly via Bun/Vite/Next ESM
@@ -1,12 +0,0 @@
1
- $ bun test
2
- bun test v1.3.11 (af24e281)
3
-
4
- src/wire.test.ts:
5
- (pass) parseEntitlements reads the BE `identifier` field (not `id`) [0.40ms]
6
- (pass) parseEntitlements: isActive:false → inactive bucket, id still resolved [0.03ms]
7
- (pass) parseEntitlements: legacy `id` field still works [0.02ms]
8
-
9
- 3 pass
10
- 0 fail
11
- 7 expect() calls
12
- Ran 3 tests across 1 file. [14.00ms]
@@ -1 +0,0 @@
1
- $ tsc --noEmit
package/src/errors.ts DELETED
@@ -1,88 +0,0 @@
1
- // Plain error classes shared between browser and server SDKs. The browser
2
- // SDK has additional Effect-flavored variants in its own `internal/errors.ts`
3
- // — those translate to these classes at the runtime boundary. Server SDK
4
- // uses these directly.
5
-
6
- export type SuperwallErrorCode =
7
- | "NETWORK"
8
- | "AUTH"
9
- | "NOT_FOUND"
10
- | "TIMEOUT"
11
- | "DECODING";
12
-
13
- export class SuperwallError extends Error {
14
- override readonly name: string = "SuperwallError";
15
- readonly code: SuperwallErrorCode;
16
-
17
- constructor(message: string, code: SuperwallErrorCode) {
18
- super(message);
19
- this.code = code;
20
- }
21
- }
22
-
23
- export class SuperwallNetworkError extends SuperwallError {
24
- override readonly name = "SuperwallNetworkError";
25
- readonly status?: number;
26
- readonly url?: string;
27
- override readonly cause?: unknown;
28
-
29
- constructor(
30
- message: string,
31
- opts: { status?: number; url?: string; cause?: unknown } = {},
32
- ) {
33
- super(message, "NETWORK");
34
- if (opts.status !== undefined) this.status = opts.status;
35
- if (opts.url !== undefined) this.url = opts.url;
36
- if (opts.cause !== undefined) this.cause = opts.cause;
37
- }
38
- }
39
-
40
- export class SuperwallAuthError extends SuperwallError {
41
- override readonly name = "SuperwallAuthError";
42
- readonly url?: string;
43
-
44
- constructor(message: string, opts: { url?: string } = {}) {
45
- super(message, "AUTH");
46
- if (opts.url !== undefined) this.url = opts.url;
47
- }
48
- }
49
-
50
- export class SuperwallNotFoundError extends SuperwallError {
51
- override readonly name = "SuperwallNotFoundError";
52
- readonly url?: string;
53
-
54
- constructor(message: string, opts: { url?: string } = {}) {
55
- super(message, "NOT_FOUND");
56
- if (opts.url !== undefined) this.url = opts.url;
57
- }
58
- }
59
-
60
- export class SuperwallTimeoutError extends SuperwallError {
61
- override readonly name = "SuperwallTimeoutError";
62
- readonly url?: string;
63
- readonly timeoutMs?: number;
64
-
65
- constructor(
66
- message: string,
67
- opts: { url?: string; timeoutMs?: number } = {},
68
- ) {
69
- super(message, "TIMEOUT");
70
- if (opts.url !== undefined) this.url = opts.url;
71
- if (opts.timeoutMs !== undefined) this.timeoutMs = opts.timeoutMs;
72
- }
73
- }
74
-
75
- export class SuperwallDecodingError extends SuperwallError {
76
- override readonly name = "SuperwallDecodingError";
77
- readonly url?: string;
78
- override readonly cause?: unknown;
79
-
80
- constructor(
81
- message: string,
82
- opts: { url?: string; cause?: unknown } = {},
83
- ) {
84
- super(message, "DECODING");
85
- if (opts.url !== undefined) this.url = opts.url;
86
- if (opts.cause !== undefined) this.cause = opts.cause;
87
- }
88
- }
package/src/hosts.ts DELETED
@@ -1,40 +0,0 @@
1
- import type { EnvironmentHosts, NetworkEnvironment } from "./types.ts";
2
-
3
- const RELEASE_HOSTS: EnvironmentHosts = {
4
- base: "api.superwall.me",
5
- collector: "collector.superwall.com",
6
- enrichment: "enrichment-api.superwall.com",
7
- subscriptions: "subscriptions-api.superwall.com",
8
- };
9
-
10
- const RC_HOSTS: EnvironmentHosts = {
11
- base: "api.superwallcanary.com",
12
- collector: "collector.superwallcanary.com",
13
- enrichment: "enrichment-api.superwall.dev",
14
- subscriptions: "subscriptions-api.superwall.dev",
15
- };
16
-
17
- const DEV_HOSTS: EnvironmentHosts = {
18
- base: "api.superwall.dev",
19
- collector: "collector.superwall.com",
20
- enrichment: "enrichment-api.superwall.dev",
21
- subscriptions: "subscriptions-api.superwall.dev",
22
- };
23
-
24
- export const resolveHosts = (env: NetworkEnvironment): EnvironmentHosts => {
25
- if (typeof env === "string") {
26
- switch (env) {
27
- case "release":
28
- return RELEASE_HOSTS;
29
- case "releaseCandidate":
30
- return RC_HOSTS;
31
- case "developer":
32
- return DEV_HOSTS;
33
- }
34
- }
35
- return env.custom;
36
- };
37
-
38
- // Custom environments are typically internal proxies → assume production.
39
- export const isSandbox = (env: NetworkEnvironment): boolean =>
40
- typeof env === "string" ? env !== "release" : false;
package/src/index.ts DELETED
@@ -1,4 +0,0 @@
1
- export * from "./types.ts";
2
- export * from "./hosts.ts";
3
- export * from "./wire.ts";
4
- export * from "./errors.ts";
package/src/types.ts DELETED
@@ -1,92 +0,0 @@
1
- // Cross-SDK domain types. Anything shared between @superwall/paywalls-js
2
- // (browser) and @superwall/server lives here. Browser-only and server-only
3
- // types stay in their respective packages.
4
-
5
- export type JsonValue =
6
- | string
7
- | number
8
- | boolean
9
- | null
10
- | JsonValue[]
11
- | { [key: string]: JsonValue };
12
-
13
- // Subscription & entitlements
14
-
15
- export type SubscriptionStatus =
16
- | { status: "UNKNOWN" }
17
- | { status: "INACTIVE" }
18
- | { status: "ACTIVE"; entitlements: Entitlement[] };
19
-
20
- export type ProductStore =
21
- | "appStore"
22
- | "stripe"
23
- | "paddle"
24
- | "playStore"
25
- | "superwall"
26
- | "other";
27
-
28
- export type LatestSubscriptionState =
29
- | "inGracePeriod"
30
- | "subscribed"
31
- | "expired"
32
- | "inBillingRetryPeriod"
33
- | "revoked";
34
-
35
- export type LatestSubscriptionOfferType =
36
- | "trial"
37
- | "code"
38
- | "promotional"
39
- | "winback";
40
-
41
- export interface Entitlement {
42
- id: string;
43
- type: "SERVICE_LEVEL";
44
- isActive: boolean;
45
- productIds: string[];
46
- latestProductId?: string;
47
- store?: ProductStore;
48
- startsAt?: number;
49
- renewedAt?: number;
50
- expiresAt?: number;
51
- isLifetime?: boolean;
52
- willRenew?: boolean;
53
- state?: LatestSubscriptionState;
54
- offerType?: LatestSubscriptionOfferType;
55
- }
56
-
57
- export interface Entitlements {
58
- active: Entitlement[];
59
- inactive: Entitlement[];
60
- all: Entitlement[];
61
- }
62
-
63
- export type PaywallPresentationStyle =
64
- | { type: "MODAL" }
65
- | { type: "FULLSCREEN" }
66
- | { type: "NO_ANIMATION" }
67
- | { type: "PUSH" }
68
- | { type: "DRAWER"; height: number; cornerRadius: number }
69
- | { type: "POPUP"; height: number; width: number; cornerRadius: number }
70
- | { type: "NONE" };
71
-
72
- // Network environment selector. Both SDKs accept this on construction.
73
-
74
- export interface CustomEnvironmentHosts {
75
- base: string;
76
- collector: string;
77
- enrichment: string;
78
- subscriptions: string;
79
- }
80
-
81
- export type NetworkEnvironment =
82
- | "release"
83
- | "releaseCandidate"
84
- | "developer"
85
- | { custom: CustomEnvironmentHosts };
86
-
87
- export interface EnvironmentHosts {
88
- readonly base: string;
89
- readonly collector: string;
90
- readonly enrichment: string;
91
- readonly subscriptions: string;
92
- }
package/src/wire.test.ts DELETED
@@ -1,45 +0,0 @@
1
- import { test, expect } from "bun:test";
2
- import { parseEntitlements, type WebEntitlementsResponse } from "./wire.ts";
3
-
4
- test("parseEntitlements reads the BE `identifier` field (not `id`)", () => {
5
- // Real BE shape: customerInfo.entitlements[].identifier + isActive.
6
- const res: WebEntitlementsResponse = {
7
- entitlements: [],
8
- customerInfo: {
9
- entitlements: [
10
- {
11
- identifier: "best",
12
- type: "SERVICE_LEVEL",
13
- isActive: true,
14
- productIds: ["test:price_a", "test:price_b"],
15
- },
16
- ],
17
- },
18
- };
19
- const out = parseEntitlements(res);
20
- expect(out.all).toHaveLength(1);
21
- expect(out.all[0]!.id).toBe("best");
22
- expect(out.active.map((e) => e.id)).toEqual(["best"]);
23
- expect(out.inactive).toHaveLength(0);
24
- });
25
-
26
- test("parseEntitlements: isActive:false → inactive bucket, id still resolved", () => {
27
- const res: WebEntitlementsResponse = {
28
- customerInfo: {
29
- entitlements: [
30
- { identifier: "best", isActive: false, productIds: ["p"] },
31
- ],
32
- },
33
- };
34
- const out = parseEntitlements(res);
35
- expect(out.active).toHaveLength(0);
36
- expect(out.inactive[0]!.id).toBe("best");
37
- });
38
-
39
- test("parseEntitlements: legacy `id` field still works", () => {
40
- const res: WebEntitlementsResponse = {
41
- entitlements: [{ id: "pro", isActive: true, productIds: [] }],
42
- };
43
- const out = parseEntitlements(res);
44
- expect(out.all[0]!.id).toBe("pro");
45
- });
package/src/wire.ts DELETED
@@ -1,58 +0,0 @@
1
- import type { Entitlement, Entitlements } from "./types.ts";
2
-
3
- // Wire shape returned by GET /subscriptions-api/public/v1/users/{id}/entitlements.
4
- // All fields optional — backend may return either a `customerInfo` envelope
5
- // or a flat `entitlements` array. Domain types tighten these up.
6
- export interface WebEntitlementsResponse {
7
- readonly customerInfo?: {
8
- entitlements?: ReadonlyArray<WireEntitlement>;
9
- };
10
- readonly entitlements?: ReadonlyArray<WireEntitlement>;
11
- /** Short-lived (≈1h) Superwall-signed JWT asserting the active entitlements,
12
- * for offline server-side verification via `@superwall/verify`. Best-effort:
13
- * the backend omits it when signing is unavailable (e.g. no key configured
14
- * in that environment), so treat its absence gracefully. */
15
- readonly entitlementsToken?: string;
16
- }
17
-
18
- export interface WireEntitlement {
19
- /** BE wire shape uses `identifier`; older/other shapes use `id`. */
20
- identifier?: string;
21
- id?: string;
22
- isActive?: boolean;
23
- productIds?: string[];
24
- type?: string;
25
- }
26
-
27
- const toDomain = (e: WireEntitlement): Entitlement => ({
28
- id: e.identifier ?? e.id ?? "",
29
- type: "SERVICE_LEVEL",
30
- isActive: e.isActive ?? false,
31
- productIds: e.productIds ?? [],
32
- });
33
-
34
- /**
35
- * Normalize a `WebEntitlementsResponse` into a domain `Entitlements`
36
- * bucket. Prefers the `entitlements` top-level array if present, falls
37
- * back to `customerInfo.entitlements`.
38
- */
39
- export const parseEntitlements = (
40
- res: WebEntitlementsResponse,
41
- ): Entitlements => {
42
- // The BE sends `entitlements: []` (empty) at the top level and the real
43
- // ones under `customerInfo.entitlements`. A plain `??` would pick the
44
- // empty top-level array (it isn't nullish) and drop the real ones — so
45
- // only fall back to the top level when customerInfo has nothing.
46
- const wire =
47
- res.customerInfo?.entitlements && res.customerInfo.entitlements.length > 0
48
- ? res.customerInfo.entitlements
49
- : res.entitlements && res.entitlements.length > 0
50
- ? res.entitlements
51
- : (res.customerInfo?.entitlements ?? res.entitlements ?? []);
52
- const all = wire.map(toDomain);
53
- return {
54
- active: all.filter((e) => e.isActive),
55
- inactive: all.filter((e) => !e.isActive),
56
- all,
57
- };
58
- };
package/tsconfig.json DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "rootDir": "src"
5
- },
6
- "include": ["src/**/*"]
7
- }