@quickflo/entitlement-schema 0.1.3

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/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # @quickflo/entitlement-schema
2
+
3
+ Zod schema + TypeScript types for the per-app entitlement record QuickFlo
4
+ uses to gate access to apps it ships (list-scrub, zoom-sms, future
5
+ verticals).
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ pnpm add @quickflo/entitlement-schema zod
11
+ ```
12
+
13
+ `zod` is a peer dependency.
14
+
15
+ ## Use
16
+
17
+ ```ts
18
+ import {
19
+ entitlementSchema,
20
+ type Entitlement,
21
+ type EntitlementStatus,
22
+ type EntitlementSource,
23
+ makeEntitlementKey,
24
+ ENTITLEMENTS_TABLE_NAME,
25
+ } from '@quickflo/entitlement-schema';
26
+
27
+ const parsed: Entitlement = entitlementSchema.parse(someValue);
28
+
29
+ // Composite key for the platform-org `app_entitlements` table
30
+ const key = makeEntitlementKey('org_123', 'list-scrub');
31
+ // → "org_123:list-scrub"
32
+ ```
33
+
34
+ ## What an entitlement represents
35
+
36
+ Each record answers: "does this customer org have access to this app, and
37
+ at what tier?"
38
+
39
+ - `active` — drives the paywall/refusal decision
40
+ - `tier` — opaque per-app string (`free`, `pro`, etc.)
41
+ - `limits` — numeric per-app limits (rows/month, runs/month, storage)
42
+ - `features` — named feature flags
43
+ - `expiresAt` — ISO-8601 expiration, null when no expiration
44
+ - `status` — lifecycle marker (`free` / `trialing` / `active` / `past_due` /
45
+ `canceled` / `incomplete`)
46
+ - `source` — origin of the record (`seed` / `stripe` / `admin`)
47
+ - `stripeCustomerId`, `stripeSubscriptionId` — Stripe IDs when present
48
+ - `onboardedAt`, `updatedAt` — ISO-8601 timestamps
49
+
50
+ Records are seeded by the QuickFlo onboarding endpoint and updated by the
51
+ Stripe webhook workflows. Records are never deleted; presence of a record
52
+ means the org has onboarded into the app at some point.
53
+
54
+ ## License
55
+
56
+ MIT
@@ -0,0 +1,110 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * The structured entitlement record stored in the platform org's
4
+ * `app_entitlements` data-store, keyed by `${customerOrgId}:${appId}`.
5
+ * Single source for "does this org have access to this app, and at what
6
+ * tier?" — read by the @RequiresApp guard, the workflow trigger pipeline,
7
+ * and the per-app UIs (e.g. list-scrub paywall).
8
+ *
9
+ * Records are seeded by the onboarding endpoint and updated by the
10
+ * Stripe webhook workflows. Records are never deleted; presence of a
11
+ * record means the org has onboarded into the app at some point.
12
+ */
13
+ export declare const entitlementSchema: z.ZodObject<{
14
+ /**
15
+ * Whether the app's gated surfaces are usable right now. Drives the
16
+ * paywall/refusal decision. Set to `false` when no Stripe subscription
17
+ * is active (free tier seed) or after a `subscription.deleted` webhook.
18
+ */
19
+ active: z.ZodBoolean;
20
+ /**
21
+ * Coarse tier label (e.g. `free`, `starter`, `pro`, `enterprise`). The
22
+ * exact set is per-app and lives in app code that interprets the tier
23
+ * for feature gating; this layer treats it as an opaque string.
24
+ */
25
+ tier: z.ZodString;
26
+ /**
27
+ * Numeric per-app limits (rows scanned per month, monthly runs, MB of
28
+ * storage, etc.). Keys are app-defined; consumers look up the keys they
29
+ * care about. Empty for the free seed.
30
+ */
31
+ limits: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodNumber>>;
32
+ /**
33
+ * Named feature flags toggled on for this entitlement (e.g.
34
+ * `ai-dedupe`, `bulk-export`). Used by `@RequiresFeature(...)` and
35
+ * inline UI gating.
36
+ */
37
+ features: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
38
+ /**
39
+ * ISO-8601 timestamp marking when the entitlement should be treated as
40
+ * expired (e.g. trial end). Null when there's no expiration.
41
+ * Consumers compare against `new Date()` at read time.
42
+ */
43
+ expiresAt: z.ZodDefault<z.ZodNullable<z.ZodString>>;
44
+ /**
45
+ * Lifecycle marker independent of `active`. Distinguishes "never
46
+ * subscribed" (default seed) from "was subscribed, then canceled"
47
+ * (deleted webhook) from "active subscription" — useful for upgrade
48
+ * UX even when both states have `active: false`.
49
+ */
50
+ status: z.ZodEnum<["free", "trialing", "active", "past_due", "canceled", "incomplete"]>;
51
+ /**
52
+ * Where this entitlement record came from. `seed` = onboarding endpoint
53
+ * wrote the default; `stripe` = webhook from Stripe set the values;
54
+ * `admin` = manual override by a platform operator.
55
+ */
56
+ source: z.ZodEnum<["seed", "stripe", "admin"]>;
57
+ /** Stripe customer ID, when one has been provisioned. */
58
+ stripeCustomerId: z.ZodOptional<z.ZodString>;
59
+ /** Stripe subscription ID for the active subscription, when present. */
60
+ stripeSubscriptionId: z.ZodOptional<z.ZodString>;
61
+ /**
62
+ * ISO-8601 timestamp marking when the org first onboarded into this app.
63
+ * Set on the very first `set()` for the (org, app) pair and preserved
64
+ * across subsequent updates. Drives the "member since" affordance.
65
+ */
66
+ onboardedAt: z.ZodString;
67
+ /**
68
+ * ISO-8601 timestamp rolled forward on every `set()`. Used by the Stripe
69
+ * webhook idempotency check to skip out-of-order events.
70
+ */
71
+ updatedAt: z.ZodString;
72
+ }, "strip", z.ZodTypeAny, {
73
+ active: boolean;
74
+ tier: string;
75
+ status: "active" | "free" | "trialing" | "past_due" | "canceled" | "incomplete";
76
+ limits: Record<string, number>;
77
+ features: string[];
78
+ expiresAt: string | null;
79
+ source: "seed" | "stripe" | "admin";
80
+ onboardedAt: string;
81
+ updatedAt: string;
82
+ stripeCustomerId?: string | undefined;
83
+ stripeSubscriptionId?: string | undefined;
84
+ }, {
85
+ active: boolean;
86
+ tier: string;
87
+ status: "active" | "free" | "trialing" | "past_due" | "canceled" | "incomplete";
88
+ source: "seed" | "stripe" | "admin";
89
+ onboardedAt: string;
90
+ updatedAt: string;
91
+ limits?: Record<string, number> | undefined;
92
+ features?: string[] | undefined;
93
+ expiresAt?: string | null | undefined;
94
+ stripeCustomerId?: string | undefined;
95
+ stripeSubscriptionId?: string | undefined;
96
+ }>;
97
+ export type Entitlement = z.infer<typeof entitlementSchema>;
98
+ /** Lifecycle status values an entitlement can take. */
99
+ export type EntitlementStatus = Entitlement['status'];
100
+ /** Origin of an entitlement record. */
101
+ export type EntitlementSource = Entitlement['source'];
102
+ /**
103
+ * Composite key shape for the platform-org `app_entitlements` table.
104
+ * Centralized so the service and any ad-hoc consumers (admin scripts,
105
+ * Stripe workflows authored in the platform org) compute keys identically.
106
+ */
107
+ export declare function makeEntitlementKey(customerOrgId: string, appId: string): string;
108
+ /** Reserved data-store table on the platform org that holds entitlement records. */
109
+ export declare const ENTITLEMENTS_TABLE_NAME = "app_entitlements";
110
+ //# sourceMappingURL=entitlement.schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entitlement.schema.d.ts","sourceRoot":"","sources":["../src/entitlement.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB;IAC5B;;;;OAIG;;IAGH;;;;OAIG;;IAGH;;;;OAIG;;IAGH;;;;OAIG;;IAGH;;;;OAIG;;IAGH;;;;;OAKG;;IAUH;;;;OAIG;;IAGH,yDAAyD;;IAGzD,wEAAwE;;IAGxE;;;;OAIG;;IAGH;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;EAEH,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,uDAAuD;AACvD,MAAM,MAAM,iBAAiB,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;AAEtD,uCAAuC;AACvC,MAAM,MAAM,iBAAiB,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;AAEtD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,MAAM,GACZ,MAAM,CAER;AAED,oFAAoF;AACpF,eAAO,MAAM,uBAAuB,qBAAqB,CAAC"}
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ENTITLEMENTS_TABLE_NAME = exports.entitlementSchema = void 0;
4
+ exports.makeEntitlementKey = makeEntitlementKey;
5
+ const zod_1 = require("zod");
6
+ /**
7
+ * The structured entitlement record stored in the platform org's
8
+ * `app_entitlements` data-store, keyed by `${customerOrgId}:${appId}`.
9
+ * Single source for "does this org have access to this app, and at what
10
+ * tier?" — read by the @RequiresApp guard, the workflow trigger pipeline,
11
+ * and the per-app UIs (e.g. list-scrub paywall).
12
+ *
13
+ * Records are seeded by the onboarding endpoint and updated by the
14
+ * Stripe webhook workflows. Records are never deleted; presence of a
15
+ * record means the org has onboarded into the app at some point.
16
+ */
17
+ exports.entitlementSchema = zod_1.z.object({
18
+ /**
19
+ * Whether the app's gated surfaces are usable right now. Drives the
20
+ * paywall/refusal decision. Set to `false` when no Stripe subscription
21
+ * is active (free tier seed) or after a `subscription.deleted` webhook.
22
+ */
23
+ active: zod_1.z.boolean(),
24
+ /**
25
+ * Coarse tier label (e.g. `free`, `starter`, `pro`, `enterprise`). The
26
+ * exact set is per-app and lives in app code that interprets the tier
27
+ * for feature gating; this layer treats it as an opaque string.
28
+ */
29
+ tier: zod_1.z.string(),
30
+ /**
31
+ * Numeric per-app limits (rows scanned per month, monthly runs, MB of
32
+ * storage, etc.). Keys are app-defined; consumers look up the keys they
33
+ * care about. Empty for the free seed.
34
+ */
35
+ limits: zod_1.z.record(zod_1.z.string(), zod_1.z.number()).default({}),
36
+ /**
37
+ * Named feature flags toggled on for this entitlement (e.g.
38
+ * `ai-dedupe`, `bulk-export`). Used by `@RequiresFeature(...)` and
39
+ * inline UI gating.
40
+ */
41
+ features: zod_1.z.array(zod_1.z.string()).default([]),
42
+ /**
43
+ * ISO-8601 timestamp marking when the entitlement should be treated as
44
+ * expired (e.g. trial end). Null when there's no expiration.
45
+ * Consumers compare against `new Date()` at read time.
46
+ */
47
+ expiresAt: zod_1.z.string().datetime().nullable().default(null),
48
+ /**
49
+ * Lifecycle marker independent of `active`. Distinguishes "never
50
+ * subscribed" (default seed) from "was subscribed, then canceled"
51
+ * (deleted webhook) from "active subscription" — useful for upgrade
52
+ * UX even when both states have `active: false`.
53
+ */
54
+ status: zod_1.z.enum([
55
+ 'free',
56
+ 'trialing',
57
+ 'active',
58
+ 'past_due',
59
+ 'canceled',
60
+ 'incomplete',
61
+ ]),
62
+ /**
63
+ * Where this entitlement record came from. `seed` = onboarding endpoint
64
+ * wrote the default; `stripe` = webhook from Stripe set the values;
65
+ * `admin` = manual override by a platform operator.
66
+ */
67
+ source: zod_1.z.enum(['seed', 'stripe', 'admin']),
68
+ /** Stripe customer ID, when one has been provisioned. */
69
+ stripeCustomerId: zod_1.z.string().optional(),
70
+ /** Stripe subscription ID for the active subscription, when present. */
71
+ stripeSubscriptionId: zod_1.z.string().optional(),
72
+ /**
73
+ * ISO-8601 timestamp marking when the org first onboarded into this app.
74
+ * Set on the very first `set()` for the (org, app) pair and preserved
75
+ * across subsequent updates. Drives the "member since" affordance.
76
+ */
77
+ onboardedAt: zod_1.z.string().datetime(),
78
+ /**
79
+ * ISO-8601 timestamp rolled forward on every `set()`. Used by the Stripe
80
+ * webhook idempotency check to skip out-of-order events.
81
+ */
82
+ updatedAt: zod_1.z.string().datetime(),
83
+ });
84
+ /**
85
+ * Composite key shape for the platform-org `app_entitlements` table.
86
+ * Centralized so the service and any ad-hoc consumers (admin scripts,
87
+ * Stripe workflows authored in the platform org) compute keys identically.
88
+ */
89
+ function makeEntitlementKey(customerOrgId, appId) {
90
+ return `${customerOrgId}:${appId}`;
91
+ }
92
+ /** Reserved data-store table on the platform org that holds entitlement records. */
93
+ exports.ENTITLEMENTS_TABLE_NAME = 'app_entitlements';
@@ -0,0 +1,2 @@
1
+ export * from './entitlement.schema';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./entitlement.schema"), exports);
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@quickflo/entitlement-schema",
3
+ "version": "0.1.3",
4
+ "description": "Zod schema and types for QuickFlo per-app entitlement records. Shared between the QuickFlo backend and consumer apps (micro-apps, app-sdk).",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/QuickFlo/packages.git",
9
+ "directory": "packages/entitlement-schema"
10
+ },
11
+ "keywords": [
12
+ "quickflo",
13
+ "entitlement",
14
+ "zod",
15
+ "schema"
16
+ ],
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ "./package.json": "./package.json",
20
+ ".": {
21
+ "development": "./src/index.ts",
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js",
24
+ "default": "./dist/index.js"
25
+ }
26
+ },
27
+ "files": [
28
+ "dist/**/*",
29
+ "!*.tsbuildinfo"
30
+ ],
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "peerDependencies": {
35
+ "zod": "^3.25.76"
36
+ },
37
+ "dependencies": {},
38
+ "main": "./index.js",
39
+ "type": "commonjs"
40
+ }
@@ -0,0 +1 @@
1
+ {"version":"5.8.3"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@quickflo/entitlement-schema",
3
+ "version": "0.1.3",
4
+ "description": "Zod schema and types for QuickFlo per-app entitlement records. Shared between the QuickFlo backend and consumer apps (micro-apps, app-sdk).",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/QuickFlo/packages.git",
9
+ "directory": "packages/entitlement-schema"
10
+ },
11
+ "keywords": [
12
+ "quickflo",
13
+ "entitlement",
14
+ "zod",
15
+ "schema"
16
+ ],
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ "./package.json": "./package.json",
20
+ ".": {
21
+ "development": "./src/index.ts",
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js",
24
+ "default": "./dist/index.js"
25
+ }
26
+ },
27
+ "files": [
28
+ "dist/**/*",
29
+ "!*.tsbuildinfo"
30
+ ],
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "peerDependencies": {
35
+ "zod": "^3.25.76"
36
+ },
37
+ "dependencies": {}
38
+ }