@happ-cli/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,51 @@
1
+ /**
2
+ * @happ/sdk — framework-agnostic core client for the Happ feature entitlement platform.
3
+ *
4
+ * Works in any Node.js ≥18 environment: Express, Fastify, Remix, plain scripts, etc.
5
+ * Uses native fetch — no extra dependencies.
6
+ *
7
+ * Usage:
8
+ * import { createHappClient } from "@happ/sdk"
9
+ *
10
+ * const happ = createHappClient() // reads HAPP_API_URL + HAPP_API_KEY from env
11
+ *
12
+ * const result = await happ.eval("invoices", userId)
13
+ * if (!result.allowed) return res.status(403).json({ error: "Access denied" })
14
+ *
15
+ * await createInvoice(...)
16
+ * await happ.increment("invoices", userId)
17
+ */
18
+ export interface HappClientOptions {
19
+ /** Happ API base URL. Defaults to HAPP_API_URL env var or the production URL. */
20
+ apiUrl?: string;
21
+ /** Happ project API key. Defaults to HAPP_API_KEY env var. */
22
+ apiKey?: string;
23
+ }
24
+ export interface EvalResult {
25
+ allowed: boolean;
26
+ reason?: string;
27
+ remaining?: number;
28
+ effectiveSubscriberId?: string;
29
+ }
30
+ /** Human-readable denial messages keyed by reason code from the Happ API. */
31
+ export declare const DENIAL_MESSAGES: Record<string, string>;
32
+ export declare class HappClient {
33
+ protected readonly apiUrl: string;
34
+ protected readonly apiKey: string;
35
+ constructor(options?: HappClientOptions);
36
+ /** Internal fetch with auth headers pre-applied. */
37
+ protected request(path: string, init?: RequestInit): Promise<Response>;
38
+ /**
39
+ * Evaluate whether a subscriber is allowed to use a feature.
40
+ * Fails open — returns `{ allowed: true }` if the API is unreachable.
41
+ */
42
+ eval(featureKey: string, subscriberId: string): Promise<EvalResult>;
43
+ /**
44
+ * Increment usage for a metered feature after a successful action.
45
+ * Fire-and-forget — never throws or blocks the response.
46
+ */
47
+ increment(featureKey: string, subscriberId: string): Promise<void>;
48
+ }
49
+ /** Create a Happ client. Options default to HAPP_API_URL and HAPP_API_KEY env vars. */
50
+ export declare function createHappClient(options?: HappClientOptions): HappClient;
51
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,MAAM,WAAW,iBAAiB;IAChC,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,qBAAqB,CAAC,EAAE,MAAM,CAAA;CAC/B;AAED,6EAA6E;AAC7E,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAMlD,CAAA;AAID,qBAAa,UAAU;IACrB,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACjC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;gBAErB,OAAO,GAAE,iBAAsB;IAK3C,oDAAoD;IACpD,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAW1E;;;OAGG;IACG,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAYzE;;;OAGG;IACG,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAWzE;AAED,uFAAuF;AACvF,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,UAAU,CAExE"}
package/dist/index.js ADDED
@@ -0,0 +1,80 @@
1
+ /**
2
+ * @happ/sdk — framework-agnostic core client for the Happ feature entitlement platform.
3
+ *
4
+ * Works in any Node.js ≥18 environment: Express, Fastify, Remix, plain scripts, etc.
5
+ * Uses native fetch — no extra dependencies.
6
+ *
7
+ * Usage:
8
+ * import { createHappClient } from "@happ/sdk"
9
+ *
10
+ * const happ = createHappClient() // reads HAPP_API_URL + HAPP_API_KEY from env
11
+ *
12
+ * const result = await happ.eval("invoices", userId)
13
+ * if (!result.allowed) return res.status(403).json({ error: "Access denied" })
14
+ *
15
+ * await createInvoice(...)
16
+ * await happ.increment("invoices", userId)
17
+ */
18
+ const DEFAULT_API_URL = "https://happ-api-prod.up.railway.app";
19
+ /** Human-readable denial messages keyed by reason code from the Happ API. */
20
+ export const DENIAL_MESSAGES = {
21
+ limit_exceeded: "You've reached the limit for your plan. Upgrade to continue.",
22
+ no_subscription: "No active subscription found.",
23
+ not_in_plan: "This feature is not included in your current plan.",
24
+ feature_not_live: "This feature is not yet available.",
25
+ subscription_expired: "Your subscription has expired.",
26
+ };
27
+ // ─── Client ───────────────────────────────────────────────────────────────────
28
+ export class HappClient {
29
+ constructor(options = {}) {
30
+ this.apiUrl = options.apiUrl ?? process.env["HAPP_API_URL"] ?? DEFAULT_API_URL;
31
+ this.apiKey = options.apiKey ?? process.env["HAPP_API_KEY"] ?? "";
32
+ }
33
+ /** Internal fetch with auth headers pre-applied. */
34
+ request(path, init = {}) {
35
+ return fetch(`${this.apiUrl}${path}`, {
36
+ ...init,
37
+ headers: {
38
+ Authorization: `Bearer ${this.apiKey}`,
39
+ ...init.headers,
40
+ },
41
+ cache: "no-store",
42
+ });
43
+ }
44
+ /**
45
+ * Evaluate whether a subscriber is allowed to use a feature.
46
+ * Fails open — returns `{ allowed: true }` if the API is unreachable.
47
+ */
48
+ async eval(featureKey, subscriberId) {
49
+ try {
50
+ const res = await this.request(`/api/eval/${featureKey}?subscriberId=${encodeURIComponent(subscriberId)}`);
51
+ if (!res.ok)
52
+ return { allowed: true }; // fail open on API error
53
+ return res.json();
54
+ }
55
+ catch {
56
+ return { allowed: true }; // fail open on network error
57
+ }
58
+ }
59
+ /**
60
+ * Increment usage for a metered feature after a successful action.
61
+ * Fire-and-forget — never throws or blocks the response.
62
+ */
63
+ async increment(featureKey, subscriberId) {
64
+ try {
65
+ await this.request(`/api/eval/${featureKey}/increment`, {
66
+ method: "POST",
67
+ headers: { "Content-Type": "application/json" },
68
+ body: JSON.stringify({ subscriberId }),
69
+ });
70
+ }
71
+ catch {
72
+ // intentionally swallowed — usage tracking must never block the user
73
+ }
74
+ }
75
+ }
76
+ /** Create a Happ client. Options default to HAPP_API_URL and HAPP_API_KEY env vars. */
77
+ export function createHappClient(options) {
78
+ return new HappClient(options);
79
+ }
80
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,eAAe,GAAG,sCAAsC,CAAA;AAkB9D,6EAA6E;AAC7E,MAAM,CAAC,MAAM,eAAe,GAA2B;IACrD,cAAc,EAAQ,8DAA8D;IACpF,eAAe,EAAO,+BAA+B;IACrD,WAAW,EAAW,oDAAoD;IAC1E,gBAAgB,EAAM,oCAAoC;IAC1D,oBAAoB,EAAE,gCAAgC;CACvD,CAAA;AAED,iFAAiF;AAEjF,MAAM,OAAO,UAAU;IAIrB,YAAY,UAA6B,EAAE;QACzC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,eAAe,CAAA;QAC9E,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAA;IACnE,CAAC;IAED,oDAAoD;IAC1C,OAAO,CAAC,IAAY,EAAE,OAAoB,EAAE;QACpD,OAAO,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,EAAE,EAAE;YACpC,GAAG,IAAI;YACP,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE;gBACtC,GAAG,IAAI,CAAC,OAAO;aAChB;YACD,KAAK,EAAE,UAA0B;SAClC,CAAC,CAAA;IACJ,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,IAAI,CAAC,UAAkB,EAAE,YAAoB;QACjD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAC5B,aAAa,UAAU,iBAAiB,kBAAkB,CAAC,YAAY,CAAC,EAAE,CAC3E,CAAA;YACD,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA,CAAC,yBAAyB;YAC/D,OAAO,GAAG,CAAC,IAAI,EAAyB,CAAA;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA,CAAC,6BAA6B;QACxD,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,SAAS,CAAC,UAAkB,EAAE,YAAoB;QACtD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,UAAU,YAAY,EAAE;gBACtD,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;aACvC,CAAC,CAAA;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,qEAAqE;QACvE,CAAC;IACH,CAAC;CACF;AAED,uFAAuF;AACvF,MAAM,UAAU,gBAAgB,CAAC,OAA2B;IAC1D,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,CAAA;AAChC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@happ-cli/sdk",
3
+ "version": "0.1.0",
4
+ "description": "Happ feature entitlement SDK — framework-agnostic core",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "engines": {
20
+ "node": ">=18"
21
+ },
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "scripts": {
26
+ "build": "tsc",
27
+ "dev": "tsc --watch"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^20.14.0",
31
+ "typescript": "^5.4.5"
32
+ }
33
+ }