@bash-app/bash-common 30.372.0 → 30.374.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.
@@ -5,34 +5,72 @@ describe("organizationHasVanitySubdomain", () => {
5
5
  expect(organizationHasVanitySubdomain(null)).toBe(false);
6
6
  expect(organizationHasVanitySubdomain(undefined)).toBe(false);
7
7
  });
8
- test("non-Enterprise tiers are never entitled", () => {
9
- for (const tier of ["Starter", "Growth", "Church", "Legacy", null]) {
8
+ test("below-Growth tiers are never entitled", () => {
9
+ for (const tier of ["Starter", "Church", "Legacy", null]) {
10
10
  expect(organizationHasVanitySubdomain({
11
11
  subscriptionTier: tier,
12
12
  subscriptionStatus: "Active",
13
13
  })).toBe(false);
14
14
  }
15
15
  });
16
- test("Enterprise with active or trialing status is entitled", () => {
17
- expect(organizationHasVanitySubdomain({
18
- subscriptionTier: "Enterprise",
16
+ test("Growth or Enterprise with active or trialing status is entitled", () => {
17
+ for (const tier of ["Growth", "Enterprise"]) {
18
+ expect(organizationHasVanitySubdomain({
19
+ subscriptionTier: tier,
20
+ subscriptionStatus: "Active",
21
+ })).toBe(true);
22
+ expect(organizationHasVanitySubdomain({
23
+ subscriptionTier: tier,
24
+ subscriptionStatus: "Trialing",
25
+ })).toBe(true);
26
+ }
27
+ });
28
+ test("Growth or Enterprise with a missing status is treated as active (legacy/ops orgs)", () => {
29
+ for (const tier of ["Growth", "Enterprise"]) {
30
+ expect(organizationHasVanitySubdomain({ subscriptionTier: tier })).toBe(true);
31
+ expect(organizationHasVanitySubdomain({
32
+ subscriptionTier: tier,
33
+ subscriptionStatus: null,
34
+ })).toBe(true);
35
+ }
36
+ });
37
+ test("Growth or Enterprise with a lapsed status is not entitled", () => {
38
+ for (const tier of ["Growth", "Enterprise"]) {
39
+ for (const status of ["Canceled", "PastDue", "Canceling"]) {
40
+ expect(organizationHasVanitySubdomain({
41
+ subscriptionTier: tier,
42
+ subscriptionStatus: status,
43
+ })).toBe(false);
44
+ }
45
+ }
46
+ });
47
+ });
48
+ describe("organizationHasCustomDomainAccess", () => {
49
+ test("null / undefined return false", () => {
50
+ expect(organizationHasCustomDomainAccess(null)).toBe(false);
51
+ expect(organizationHasCustomDomainAccess(undefined)).toBe(false);
52
+ });
53
+ test("Growth is not entitled — custom domains stay Enterprise-only", () => {
54
+ expect(organizationHasCustomDomainAccess({
55
+ subscriptionTier: "Growth",
19
56
  subscriptionStatus: "Active",
20
- })).toBe(true);
21
- expect(organizationHasVanitySubdomain({
57
+ })).toBe(false);
58
+ });
59
+ test("Enterprise with active or trialing status is entitled, regardless of enterpriseSalesProvisioned", () => {
60
+ expect(organizationHasCustomDomainAccess({
22
61
  subscriptionTier: "Enterprise",
23
- subscriptionStatus: "Trialing",
62
+ subscriptionStatus: "Active",
63
+ enterpriseSalesProvisioned: true,
24
64
  })).toBe(true);
25
- });
26
- test("Enterprise with a missing status is treated as active (legacy/ops orgs)", () => {
27
- expect(organizationHasVanitySubdomain({ subscriptionTier: "Enterprise" })).toBe(true);
28
- expect(organizationHasVanitySubdomain({
65
+ expect(organizationHasCustomDomainAccess({
29
66
  subscriptionTier: "Enterprise",
30
- subscriptionStatus: null,
67
+ subscriptionStatus: "Active",
68
+ enterpriseSalesProvisioned: false,
31
69
  })).toBe(true);
32
70
  });
33
71
  test("Enterprise with a lapsed status is not entitled", () => {
34
72
  for (const status of ["Canceled", "PastDue", "Canceling"]) {
35
- expect(organizationHasVanitySubdomain({
73
+ expect(organizationHasCustomDomainAccess({
36
74
  subscriptionTier: "Enterprise",
37
75
  subscriptionStatus: status,
38
76
  })).toBe(false);
@@ -47,8 +85,9 @@ describe("enterprise platform gates (audit logs, SSO, procurement, API access)",
47
85
  subscriptionStatus: "Active",
48
86
  enterpriseSalesProvisioned: true,
49
87
  };
50
- // Self-serve: bought via Stripe checkout, never grants the platform surface.
51
- const selfServeEnterprise = {
88
+ // Not sales-provisioned (e.g. legacy data, or ops hasn't flipped the flag
89
+ // yet): Enterprise tier alone never grants the sales-gated platform surface.
90
+ const nonSalesProvisionedEnterprise = {
52
91
  subscriptionTier: "Enterprise",
53
92
  subscriptionStatus: "Active",
54
93
  enterpriseSalesProvisioned: false,
@@ -56,7 +95,7 @@ describe("enterprise platform gates (audit logs, SSO, procurement, API access)",
56
95
  const growthActive = { subscriptionTier: "Growth", subscriptionStatus: "Active" };
57
96
  test("organizationHasEnterprisePlatformAccess requires enterpriseSalesProvisioned", () => {
58
97
  expect(organizationHasEnterprisePlatformAccess(salesProvisionedEnterprise)).toBe(true);
59
- expect(organizationHasEnterprisePlatformAccess(selfServeEnterprise)).toBe(false);
98
+ expect(organizationHasEnterprisePlatformAccess(nonSalesProvisionedEnterprise)).toBe(false);
60
99
  expect(organizationHasEnterprisePlatformAccess(growthActive)).toBe(false);
61
100
  expect(organizationHasEnterprisePlatformAccess(null)).toBe(false);
62
101
  expect(organizationHasEnterprisePlatformAccess(undefined)).toBe(false);
@@ -69,40 +108,40 @@ describe("enterprise platform gates (audit logs, SSO, procurement, API access)",
69
108
  });
70
109
  test("organizationHasAuditLogAccess follows the shared enterprise gate", () => {
71
110
  expect(organizationHasAuditLogAccess(salesProvisionedEnterprise)).toBe(true);
72
- expect(organizationHasAuditLogAccess(selfServeEnterprise)).toBe(false);
111
+ expect(organizationHasAuditLogAccess(nonSalesProvisionedEnterprise)).toBe(false);
73
112
  expect(organizationHasAuditLogAccess(growthActive)).toBe(false);
74
113
  });
75
114
  test("organizationHasSsoAccess follows the shared enterprise gate", () => {
76
115
  expect(organizationHasSsoAccess(salesProvisionedEnterprise)).toBe(true);
77
- expect(organizationHasSsoAccess(selfServeEnterprise)).toBe(false);
116
+ expect(organizationHasSsoAccess(nonSalesProvisionedEnterprise)).toBe(false);
78
117
  expect(organizationHasSsoAccess(growthActive)).toBe(false);
79
118
  });
80
119
  test("organizationHasProcurementAccess follows the shared enterprise gate", () => {
81
120
  expect(organizationHasProcurementAccess(salesProvisionedEnterprise)).toBe(true);
82
- expect(organizationHasProcurementAccess(selfServeEnterprise)).toBe(false);
121
+ expect(organizationHasProcurementAccess(nonSalesProvisionedEnterprise)).toBe(false);
83
122
  expect(organizationHasProcurementAccess(growthActive)).toBe(false);
84
123
  });
85
124
  test("organizationHasApiAccess follows the shared enterprise gate", () => {
86
125
  expect(organizationHasApiAccess(salesProvisionedEnterprise)).toBe(true);
87
- expect(organizationHasApiAccess(selfServeEnterprise)).toBe(false);
126
+ expect(organizationHasApiAccess(nonSalesProvisionedEnterprise)).toBe(false);
88
127
  expect(organizationHasApiAccess(growthActive)).toBe(false);
89
128
  });
90
129
  test("organizationHasComplianceAccess follows the shared enterprise gate", () => {
91
130
  expect(organizationHasComplianceAccess(salesProvisionedEnterprise)).toBe(true);
92
- expect(organizationHasComplianceAccess(selfServeEnterprise)).toBe(false);
131
+ expect(organizationHasComplianceAccess(nonSalesProvisionedEnterprise)).toBe(false);
93
132
  expect(organizationHasComplianceAccess(growthActive)).toBe(false);
94
133
  });
95
- test("organizationHasCustomDomainAccess (white-label) is self-serve-eligible no sales flag required", () => {
134
+ test("organizationHasCustomDomainAccess does not require the sales flag, but does require Enterprise (not Growth)", () => {
96
135
  expect(organizationHasCustomDomainAccess(salesProvisionedEnterprise)).toBe(true);
97
- expect(organizationHasCustomDomainAccess(selfServeEnterprise)).toBe(true);
136
+ expect(organizationHasCustomDomainAccess(nonSalesProvisionedEnterprise)).toBe(true);
98
137
  expect(organizationHasCustomDomainAccess(growthActive)).toBe(false);
99
138
  expect(organizationHasCustomDomainAccess(null)).toBe(false);
100
139
  expect(organizationHasCustomDomainAccess(undefined)).toBe(false);
101
140
  });
102
- test("organizationHasVanitySubdomain is self-serve-eligible no sales flag required", () => {
141
+ test("organizationHasVanitySubdomain does not require the sales flag, and Growth is entitled too", () => {
103
142
  expect(organizationHasVanitySubdomain(salesProvisionedEnterprise)).toBe(true);
104
- expect(organizationHasVanitySubdomain(selfServeEnterprise)).toBe(true);
105
- expect(organizationHasVanitySubdomain(growthActive)).toBe(false);
143
+ expect(organizationHasVanitySubdomain(nonSalesProvisionedEnterprise)).toBe(true);
144
+ expect(organizationHasVanitySubdomain(growthActive)).toBe(true);
106
145
  });
107
146
  });
108
147
  //# sourceMappingURL=organizationEntitlements.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"organizationEntitlements.test.js","sourceRoot":"","sources":["../../../src/utils/__tests__/organizationEntitlements.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"organizationEntitlements.test.js","sourceRoot":"","sources":["../../../src/utils/__tests__/organizationEntitlements.test.ts"],"names":[],"mappings":"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"}
@@ -21,11 +21,12 @@ export interface OrganizationEntitlementInput {
21
21
  enterpriseSalesProvisioned?: boolean | null;
22
22
  }
23
23
  /**
24
- * Vanity subdomains ({slug}.bash.community) and custom domains (white-label)
25
- * come bundled with any Enterprise plan Enterprise is sales-led (custom
26
- * quote), so this is always a sales-negotiated deal today, but the check
27
- * intentionally doesn't require `enterpriseSalesProvisioned` in case self-serve
28
- * Enterprise checkout returns as a fast-follow.
24
+ * Vanity subdomains ({slug}.bash.community) are a Growth-tier-and-up perk, not
25
+ * Enterprise-only: unlike custom domains, granting one costs nothing extra
26
+ * (a slug lookup against the existing bash.community CloudFront distribution,
27
+ * no per-org AWS resources) — it's a cheap, visible upgrade incentive to dangle
28
+ * in front of Starter orgs. See {@link organizationHasCustomDomainAccess} for
29
+ * the pricier, Enterprise-only white-label surface.
29
30
  */
30
31
  export declare function organizationHasVanitySubdomain(org: OrganizationEntitlementInput | null | undefined): boolean;
31
32
  /**
@@ -50,13 +51,14 @@ export declare function organizationHasComplianceAccess(org: OrganizationEntitle
50
51
  /**
51
52
  * Custom domains (white-label tier): real DNS (e.g. members.acme.com) provisioned
52
53
  * via AWS CloudFront SaaS Manager Distribution Tenants, plus the "Powered by Bash"
53
- * removal toggle. Same Enterprise + active/trialing gate as
54
- * {@link organizationHasVanitySubdomain} for MVP reuse the existing tier rather
55
- * than invent a new pricing tier now. A dedicated "White-label" tier is a
56
- * fast-follow if sales needs separate pricing. Does NOT require
54
+ * removal toggle. Unlike {@link organizationHasVanitySubdomain}, this needs a real
55
+ * per-org CloudFront Distribution Tenant + managed cert + the org's own DNS work —
56
+ * actual infra cost and complexity per org so it stays Enterprise-only (today
57
+ * always sales-provisioned, since self-serve Enterprise checkout is blocked; see
58
+ * docs/ENTERPRISE-B2B-SALES-PLAYBOOK.md). Does NOT require
57
59
  * {@link organizationHasEnterprisePlatformAccess}'s `enterpriseSalesProvisioned`
58
- * flag, so a fast-follow self-serve Enterprise checkout could unlock this
59
- * without the sales-gated platform surface.
60
+ * flag, so a fast-follow self-serve Enterprise checkout could unlock this without
61
+ * the sales-gated platform surface (SSO, audit logs, procurement, compliance, API).
60
62
  */
61
63
  export declare function organizationHasCustomDomainAccess(org: OrganizationEntitlementInput | null | undefined): boolean;
62
64
  //# sourceMappingURL=organizationEntitlements.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"organizationEntitlements.d.ts","sourceRoot":"","sources":["../../src/utils/organizationEntitlements.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,2EAA2E;AAC3E,eAAO,MAAM,gCAAgC,iCAGnC,CAAC;AAEX,MAAM,WAAW,4BAA4B;IAC3C,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC;;;;;;;OAOG;IACH,0BAA0B,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC7C;AAgBD;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,4BAA4B,GAAG,IAAI,GAAG,SAAS,GACnD,OAAO,CAET;AAED;;;;;;;GAOG;AACH,wBAAgB,uCAAuC,CACrD,GAAG,EAAE,4BAA4B,GAAG,IAAI,GAAG,SAAS,GACnD,OAAO,CAET;AAED,+CAA+C;AAC/C,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,4BAA4B,GAAG,IAAI,GAAG,SAAS,GACnD,OAAO,CAET;AAED,0BAA0B;AAC1B,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,4BAA4B,GAAG,IAAI,GAAG,SAAS,GACnD,OAAO,CAET;AAED,+DAA+D;AAC/D,wBAAgB,gCAAgC,CAC9C,GAAG,EAAE,4BAA4B,GAAG,IAAI,GAAG,SAAS,GACnD,OAAO,CAET;AAED,iEAAiE;AACjE,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,4BAA4B,GAAG,IAAI,GAAG,SAAS,GACnD,OAAO,CAET;AAED,+EAA+E;AAC/E,wBAAgB,+BAA+B,CAC7C,GAAG,EAAE,4BAA4B,GAAG,IAAI,GAAG,SAAS,GACnD,OAAO,CAET;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iCAAiC,CAC/C,GAAG,EAAE,4BAA4B,GAAG,IAAI,GAAG,SAAS,GACnD,OAAO,CAET"}
1
+ {"version":3,"file":"organizationEntitlements.d.ts","sourceRoot":"","sources":["../../src/utils/organizationEntitlements.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,2EAA2E;AAC3E,eAAO,MAAM,gCAAgC,iCAGnC,CAAC;AAEX,MAAM,WAAW,4BAA4B;IAC3C,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC;;;;;;;OAOG;IACH,0BAA0B,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC7C;AA4BD;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,4BAA4B,GAAG,IAAI,GAAG,SAAS,GACnD,OAAO,CAET;AAED;;;;;;;GAOG;AACH,wBAAgB,uCAAuC,CACrD,GAAG,EAAE,4BAA4B,GAAG,IAAI,GAAG,SAAS,GACnD,OAAO,CAET;AAED,+CAA+C;AAC/C,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,4BAA4B,GAAG,IAAI,GAAG,SAAS,GACnD,OAAO,CAET;AAED,0BAA0B;AAC1B,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,4BAA4B,GAAG,IAAI,GAAG,SAAS,GACnD,OAAO,CAET;AAED,+DAA+D;AAC/D,wBAAgB,gCAAgC,CAC9C,GAAG,EAAE,4BAA4B,GAAG,IAAI,GAAG,SAAS,GACnD,OAAO,CAET;AAED,iEAAiE;AACjE,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,4BAA4B,GAAG,IAAI,GAAG,SAAS,GACnD,OAAO,CAET;AAED,+EAA+E;AAC/E,wBAAgB,+BAA+B,CAC7C,GAAG,EAAE,4BAA4B,GAAG,IAAI,GAAG,SAAS,GACnD,OAAO,CAET;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iCAAiC,CAC/C,GAAG,EAAE,4BAA4B,GAAG,IAAI,GAAG,SAAS,GACnD,OAAO,CAET"}
@@ -10,27 +10,39 @@ export const ACTIVE_ORG_SUBSCRIPTION_STATUSES = [
10
10
  "Active",
11
11
  "Trialing",
12
12
  ];
13
+ // Treat a missing status as active: legacy/ops-provisioned orgs may predate
14
+ // subscriptionStatus being populated, and we don't want to silently drop
15
+ // their entitlements.
16
+ function isActiveOrTrialingStatus(status) {
17
+ if (status == null)
18
+ return true;
19
+ return ACTIVE_ORG_SUBSCRIPTION_STATUSES.includes(status);
20
+ }
13
21
  function hasActiveEnterpriseTier(org) {
14
22
  if (!org)
15
23
  return false;
16
24
  if (org.subscriptionTier !== "Enterprise")
17
25
  return false;
18
- // Treat a missing status as active: legacy/ops-provisioned Enterprise orgs may
19
- // predate subscriptionStatus being populated, and we don't want to silently
20
- // drop their vanity domain.
21
- if (org.subscriptionStatus == null)
22
- return true;
23
- return ACTIVE_ORG_SUBSCRIPTION_STATUSES.includes(org.subscriptionStatus);
26
+ return isActiveOrTrialingStatus(org.subscriptionStatus);
27
+ }
28
+ function hasActiveGrowthOrAboveTier(org) {
29
+ if (!org)
30
+ return false;
31
+ if (org.subscriptionTier !== "Growth" && org.subscriptionTier !== "Enterprise") {
32
+ return false;
33
+ }
34
+ return isActiveOrTrialingStatus(org.subscriptionStatus);
24
35
  }
25
36
  /**
26
- * Vanity subdomains ({slug}.bash.community) and custom domains (white-label)
27
- * come bundled with any Enterprise plan Enterprise is sales-led (custom
28
- * quote), so this is always a sales-negotiated deal today, but the check
29
- * intentionally doesn't require `enterpriseSalesProvisioned` in case self-serve
30
- * Enterprise checkout returns as a fast-follow.
37
+ * Vanity subdomains ({slug}.bash.community) are a Growth-tier-and-up perk, not
38
+ * Enterprise-only: unlike custom domains, granting one costs nothing extra
39
+ * (a slug lookup against the existing bash.community CloudFront distribution,
40
+ * no per-org AWS resources) — it's a cheap, visible upgrade incentive to dangle
41
+ * in front of Starter orgs. See {@link organizationHasCustomDomainAccess} for
42
+ * the pricier, Enterprise-only white-label surface.
31
43
  */
32
44
  export function organizationHasVanitySubdomain(org) {
33
- return hasActiveEnterpriseTier(org);
45
+ return hasActiveGrowthOrAboveTier(org);
34
46
  }
35
47
  /**
36
48
  * Shared gate for the enterprise platform surface: org-scoped audit logs,
@@ -66,15 +78,16 @@ export function organizationHasComplianceAccess(org) {
66
78
  /**
67
79
  * Custom domains (white-label tier): real DNS (e.g. members.acme.com) provisioned
68
80
  * via AWS CloudFront SaaS Manager Distribution Tenants, plus the "Powered by Bash"
69
- * removal toggle. Same Enterprise + active/trialing gate as
70
- * {@link organizationHasVanitySubdomain} for MVP reuse the existing tier rather
71
- * than invent a new pricing tier now. A dedicated "White-label" tier is a
72
- * fast-follow if sales needs separate pricing. Does NOT require
81
+ * removal toggle. Unlike {@link organizationHasVanitySubdomain}, this needs a real
82
+ * per-org CloudFront Distribution Tenant + managed cert + the org's own DNS work —
83
+ * actual infra cost and complexity per org so it stays Enterprise-only (today
84
+ * always sales-provisioned, since self-serve Enterprise checkout is blocked; see
85
+ * docs/ENTERPRISE-B2B-SALES-PLAYBOOK.md). Does NOT require
73
86
  * {@link organizationHasEnterprisePlatformAccess}'s `enterpriseSalesProvisioned`
74
- * flag, so a fast-follow self-serve Enterprise checkout could unlock this
75
- * without the sales-gated platform surface.
87
+ * flag, so a fast-follow self-serve Enterprise checkout could unlock this without
88
+ * the sales-gated platform surface (SSO, audit logs, procurement, compliance, API).
76
89
  */
77
90
  export function organizationHasCustomDomainAccess(org) {
78
- return organizationHasVanitySubdomain(org);
91
+ return hasActiveEnterpriseTier(org);
79
92
  }
80
93
  //# sourceMappingURL=organizationEntitlements.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"organizationEntitlements.js","sourceRoot":"","sources":["../../src/utils/organizationEntitlements.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,2EAA2E;AAC3E,MAAM,CAAC,MAAM,gCAAgC,GAAG;IAC9C,QAAQ;IACR,UAAU;CACF,CAAC;AAgBX,SAAS,uBAAuB,CAC9B,GAAoD;IAEpD,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IACvB,IAAI,GAAG,CAAC,gBAAgB,KAAK,YAAY;QAAE,OAAO,KAAK,CAAC;IACxD,+EAA+E;IAC/E,4EAA4E;IAC5E,4BAA4B;IAC5B,IAAI,GAAG,CAAC,kBAAkB,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAChD,OAAQ,gCAAsD,CAAC,QAAQ,CACrE,GAAG,CAAC,kBAAkB,CACvB,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,8BAA8B,CAC5C,GAAoD;IAEpD,OAAO,uBAAuB,CAAC,GAAG,CAAC,CAAC;AACtC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,uCAAuC,CACrD,GAAoD;IAEpD,OAAO,uBAAuB,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,0BAA0B,KAAK,IAAI,CAAC;AAClF,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,6BAA6B,CAC3C,GAAoD;IAEpD,OAAO,uCAAuC,CAAC,GAAG,CAAC,CAAC;AACtD,CAAC;AAED,0BAA0B;AAC1B,MAAM,UAAU,wBAAwB,CACtC,GAAoD;IAEpD,OAAO,uCAAuC,CAAC,GAAG,CAAC,CAAC;AACtD,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,gCAAgC,CAC9C,GAAoD;IAEpD,OAAO,uCAAuC,CAAC,GAAG,CAAC,CAAC;AACtD,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,wBAAwB,CACtC,GAAoD;IAEpD,OAAO,uCAAuC,CAAC,GAAG,CAAC,CAAC;AACtD,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,+BAA+B,CAC7C,GAAoD;IAEpD,OAAO,uCAAuC,CAAC,GAAG,CAAC,CAAC;AACtD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iCAAiC,CAC/C,GAAoD;IAEpD,OAAO,8BAA8B,CAAC,GAAG,CAAC,CAAC;AAC7C,CAAC"}
1
+ {"version":3,"file":"organizationEntitlements.js","sourceRoot":"","sources":["../../src/utils/organizationEntitlements.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,2EAA2E;AAC3E,MAAM,CAAC,MAAM,gCAAgC,GAAG;IAC9C,QAAQ;IACR,UAAU;CACF,CAAC;AAgBX,4EAA4E;AAC5E,yEAAyE;AACzE,sBAAsB;AACtB,SAAS,wBAAwB,CAAC,MAAsB;IACtD,IAAI,MAAM,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAChC,OAAQ,gCAAsD,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAClF,CAAC;AAED,SAAS,uBAAuB,CAC9B,GAAoD;IAEpD,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IACvB,IAAI,GAAG,CAAC,gBAAgB,KAAK,YAAY;QAAE,OAAO,KAAK,CAAC;IACxD,OAAO,wBAAwB,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,0BAA0B,CACjC,GAAoD;IAEpD,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IACvB,IAAI,GAAG,CAAC,gBAAgB,KAAK,QAAQ,IAAI,GAAG,CAAC,gBAAgB,KAAK,YAAY,EAAE,CAAC;QAC/E,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,wBAAwB,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,8BAA8B,CAC5C,GAAoD;IAEpD,OAAO,0BAA0B,CAAC,GAAG,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,uCAAuC,CACrD,GAAoD;IAEpD,OAAO,uBAAuB,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,0BAA0B,KAAK,IAAI,CAAC;AAClF,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,6BAA6B,CAC3C,GAAoD;IAEpD,OAAO,uCAAuC,CAAC,GAAG,CAAC,CAAC;AACtD,CAAC;AAED,0BAA0B;AAC1B,MAAM,UAAU,wBAAwB,CACtC,GAAoD;IAEpD,OAAO,uCAAuC,CAAC,GAAG,CAAC,CAAC;AACtD,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,gCAAgC,CAC9C,GAAoD;IAEpD,OAAO,uCAAuC,CAAC,GAAG,CAAC,CAAC;AACtD,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,wBAAwB,CACtC,GAAoD;IAEpD,OAAO,uCAAuC,CAAC,GAAG,CAAC,CAAC;AACtD,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,+BAA+B,CAC7C,GAAoD;IAEpD,OAAO,uCAAuC,CAAC,GAAG,CAAC,CAAC;AACtD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,iCAAiC,CAC/C,GAAoD;IAEpD,OAAO,uBAAuB,CAAC,GAAG,CAAC,CAAC;AACtC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bash-app/bash-common",
3
- "version": "30.372.0",
3
+ "version": "30.374.0",
4
4
  "description": "Common data and scripts to use on the frontend and backend",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -6026,13 +6026,16 @@ model Organization {
6026
6026
  /// "Standard" | "Priority" | "Dedicated" — drives named-AM banner + support routing copy.
6027
6027
  supportTier String @default("Standard")
6028
6028
  /// True only when ops set subscriptionTier="Enterprise" via the admin
6029
- /// enterprise-provisioning endpoint (a signed deal). Self-serve Stripe checkout
6030
- /// (organizationSubscriptionUtils.ts) can also set subscriptionTier="Enterprise"
6031
- /// but never sets this flag. organizationHasEnterprisePlatformAccess() and the
6032
- /// SSO/audit-log/procurement/compliance/API entitlements below require this flag
6033
- /// in addition to the Enterprise tier self-serve Enterprise unlocks white-label
6034
- /// (organizationHasCustomDomainAccess/organizationHasVanitySubdomain do NOT
6035
- /// require this flag) but not the sales-negotiated platform surface.
6029
+ /// enterprise-provisioning endpoint (a signed deal) the only way to reach
6030
+ /// subscriptionTier="Enterprise" today, since self-serve Enterprise checkout
6031
+ /// is blocked (see docs/ENTERPRISE-B2B-SALES-PLAYBOOK.md).
6032
+ /// organizationHasEnterprisePlatformAccess() and the SSO/audit-log/procurement/
6033
+ /// compliance/API entitlements below require this flag in addition to the
6034
+ /// Enterprise tier. organizationHasCustomDomainAccess() (custom domains,
6035
+ /// "Powered by Bash" removal) requires the Enterprise tier but NOT this flag,
6036
+ /// so a fast-follow self-serve Enterprise checkout could unlock white-label
6037
+ /// without the sales-gated platform surface. organizationHasVanitySubdomain()
6038
+ /// is a separate, cheaper perk available to Growth tier and up.
6036
6039
  enterpriseSalesProvisioned Boolean @default(false)
6037
6040
 
6038
6041
  /// SSO / identity (Organization Enterprise only). `ssoEnforced` blocks password
@@ -17,8 +17,8 @@ describe("organizationHasVanitySubdomain", () => {
17
17
  expect(organizationHasVanitySubdomain(undefined)).toBe(false);
18
18
  });
19
19
 
20
- test("non-Enterprise tiers are never entitled", () => {
21
- for (const tier of ["Starter", "Growth", "Church", "Legacy", null]) {
20
+ test("below-Growth tiers are never entitled", () => {
21
+ for (const tier of ["Starter", "Church", "Legacy", null]) {
22
22
  expect(
23
23
  organizationHasVanitySubdomain({
24
24
  subscriptionTier: tier,
@@ -28,29 +28,77 @@ describe("organizationHasVanitySubdomain", () => {
28
28
  }
29
29
  });
30
30
 
31
- test("Enterprise with active or trialing status is entitled", () => {
31
+ test("Growth or Enterprise with active or trialing status is entitled", () => {
32
+ for (const tier of ["Growth", "Enterprise"]) {
33
+ expect(
34
+ organizationHasVanitySubdomain({
35
+ subscriptionTier: tier,
36
+ subscriptionStatus: "Active",
37
+ })
38
+ ).toBe(true);
39
+ expect(
40
+ organizationHasVanitySubdomain({
41
+ subscriptionTier: tier,
42
+ subscriptionStatus: "Trialing",
43
+ })
44
+ ).toBe(true);
45
+ }
46
+ });
47
+
48
+ test("Growth or Enterprise with a missing status is treated as active (legacy/ops orgs)", () => {
49
+ for (const tier of ["Growth", "Enterprise"]) {
50
+ expect(organizationHasVanitySubdomain({ subscriptionTier: tier })).toBe(true);
51
+ expect(
52
+ organizationHasVanitySubdomain({
53
+ subscriptionTier: tier,
54
+ subscriptionStatus: null,
55
+ })
56
+ ).toBe(true);
57
+ }
58
+ });
59
+
60
+ test("Growth or Enterprise with a lapsed status is not entitled", () => {
61
+ for (const tier of ["Growth", "Enterprise"]) {
62
+ for (const status of ["Canceled", "PastDue", "Canceling"]) {
63
+ expect(
64
+ organizationHasVanitySubdomain({
65
+ subscriptionTier: tier,
66
+ subscriptionStatus: status,
67
+ })
68
+ ).toBe(false);
69
+ }
70
+ }
71
+ });
72
+ });
73
+
74
+ describe("organizationHasCustomDomainAccess", () => {
75
+ test("null / undefined return false", () => {
76
+ expect(organizationHasCustomDomainAccess(null)).toBe(false);
77
+ expect(organizationHasCustomDomainAccess(undefined)).toBe(false);
78
+ });
79
+
80
+ test("Growth is not entitled — custom domains stay Enterprise-only", () => {
32
81
  expect(
33
- organizationHasVanitySubdomain({
34
- subscriptionTier: "Enterprise",
82
+ organizationHasCustomDomainAccess({
83
+ subscriptionTier: "Growth",
35
84
  subscriptionStatus: "Active",
36
85
  })
37
- ).toBe(true);
38
- expect(
39
- organizationHasVanitySubdomain({
40
- subscriptionTier: "Enterprise",
41
- subscriptionStatus: "Trialing",
42
- })
43
- ).toBe(true);
86
+ ).toBe(false);
44
87
  });
45
88
 
46
- test("Enterprise with a missing status is treated as active (legacy/ops orgs)", () => {
89
+ test("Enterprise with active or trialing status is entitled, regardless of enterpriseSalesProvisioned", () => {
47
90
  expect(
48
- organizationHasVanitySubdomain({ subscriptionTier: "Enterprise" })
91
+ organizationHasCustomDomainAccess({
92
+ subscriptionTier: "Enterprise",
93
+ subscriptionStatus: "Active",
94
+ enterpriseSalesProvisioned: true,
95
+ })
49
96
  ).toBe(true);
50
97
  expect(
51
- organizationHasVanitySubdomain({
98
+ organizationHasCustomDomainAccess({
52
99
  subscriptionTier: "Enterprise",
53
- subscriptionStatus: null,
100
+ subscriptionStatus: "Active",
101
+ enterpriseSalesProvisioned: false,
54
102
  })
55
103
  ).toBe(true);
56
104
  });
@@ -58,7 +106,7 @@ describe("organizationHasVanitySubdomain", () => {
58
106
  test("Enterprise with a lapsed status is not entitled", () => {
59
107
  for (const status of ["Canceled", "PastDue", "Canceling"]) {
60
108
  expect(
61
- organizationHasVanitySubdomain({
109
+ organizationHasCustomDomainAccess({
62
110
  subscriptionTier: "Enterprise",
63
111
  subscriptionStatus: status,
64
112
  })
@@ -75,8 +123,9 @@ describe("enterprise platform gates (audit logs, SSO, procurement, API access)",
75
123
  subscriptionStatus: "Active",
76
124
  enterpriseSalesProvisioned: true,
77
125
  };
78
- // Self-serve: bought via Stripe checkout, never grants the platform surface.
79
- const selfServeEnterprise = {
126
+ // Not sales-provisioned (e.g. legacy data, or ops hasn't flipped the flag
127
+ // yet): Enterprise tier alone never grants the sales-gated platform surface.
128
+ const nonSalesProvisionedEnterprise = {
80
129
  subscriptionTier: "Enterprise",
81
130
  subscriptionStatus: "Active",
82
131
  enterpriseSalesProvisioned: false,
@@ -85,7 +134,7 @@ describe("enterprise platform gates (audit logs, SSO, procurement, API access)",
85
134
 
86
135
  test("organizationHasEnterprisePlatformAccess requires enterpriseSalesProvisioned", () => {
87
136
  expect(organizationHasEnterprisePlatformAccess(salesProvisionedEnterprise)).toBe(true);
88
- expect(organizationHasEnterprisePlatformAccess(selfServeEnterprise)).toBe(false);
137
+ expect(organizationHasEnterprisePlatformAccess(nonSalesProvisionedEnterprise)).toBe(false);
89
138
  expect(organizationHasEnterprisePlatformAccess(growthActive)).toBe(false);
90
139
  expect(organizationHasEnterprisePlatformAccess(null)).toBe(false);
91
140
  expect(organizationHasEnterprisePlatformAccess(undefined)).toBe(false);
@@ -102,45 +151,45 @@ describe("enterprise platform gates (audit logs, SSO, procurement, API access)",
102
151
 
103
152
  test("organizationHasAuditLogAccess follows the shared enterprise gate", () => {
104
153
  expect(organizationHasAuditLogAccess(salesProvisionedEnterprise)).toBe(true);
105
- expect(organizationHasAuditLogAccess(selfServeEnterprise)).toBe(false);
154
+ expect(organizationHasAuditLogAccess(nonSalesProvisionedEnterprise)).toBe(false);
106
155
  expect(organizationHasAuditLogAccess(growthActive)).toBe(false);
107
156
  });
108
157
 
109
158
  test("organizationHasSsoAccess follows the shared enterprise gate", () => {
110
159
  expect(organizationHasSsoAccess(salesProvisionedEnterprise)).toBe(true);
111
- expect(organizationHasSsoAccess(selfServeEnterprise)).toBe(false);
160
+ expect(organizationHasSsoAccess(nonSalesProvisionedEnterprise)).toBe(false);
112
161
  expect(organizationHasSsoAccess(growthActive)).toBe(false);
113
162
  });
114
163
 
115
164
  test("organizationHasProcurementAccess follows the shared enterprise gate", () => {
116
165
  expect(organizationHasProcurementAccess(salesProvisionedEnterprise)).toBe(true);
117
- expect(organizationHasProcurementAccess(selfServeEnterprise)).toBe(false);
166
+ expect(organizationHasProcurementAccess(nonSalesProvisionedEnterprise)).toBe(false);
118
167
  expect(organizationHasProcurementAccess(growthActive)).toBe(false);
119
168
  });
120
169
 
121
170
  test("organizationHasApiAccess follows the shared enterprise gate", () => {
122
171
  expect(organizationHasApiAccess(salesProvisionedEnterprise)).toBe(true);
123
- expect(organizationHasApiAccess(selfServeEnterprise)).toBe(false);
172
+ expect(organizationHasApiAccess(nonSalesProvisionedEnterprise)).toBe(false);
124
173
  expect(organizationHasApiAccess(growthActive)).toBe(false);
125
174
  });
126
175
 
127
176
  test("organizationHasComplianceAccess follows the shared enterprise gate", () => {
128
177
  expect(organizationHasComplianceAccess(salesProvisionedEnterprise)).toBe(true);
129
- expect(organizationHasComplianceAccess(selfServeEnterprise)).toBe(false);
178
+ expect(organizationHasComplianceAccess(nonSalesProvisionedEnterprise)).toBe(false);
130
179
  expect(organizationHasComplianceAccess(growthActive)).toBe(false);
131
180
  });
132
181
 
133
- test("organizationHasCustomDomainAccess (white-label) is self-serve-eligible no sales flag required", () => {
182
+ test("organizationHasCustomDomainAccess does not require the sales flag, but does require Enterprise (not Growth)", () => {
134
183
  expect(organizationHasCustomDomainAccess(salesProvisionedEnterprise)).toBe(true);
135
- expect(organizationHasCustomDomainAccess(selfServeEnterprise)).toBe(true);
184
+ expect(organizationHasCustomDomainAccess(nonSalesProvisionedEnterprise)).toBe(true);
136
185
  expect(organizationHasCustomDomainAccess(growthActive)).toBe(false);
137
186
  expect(organizationHasCustomDomainAccess(null)).toBe(false);
138
187
  expect(organizationHasCustomDomainAccess(undefined)).toBe(false);
139
188
  });
140
189
 
141
- test("organizationHasVanitySubdomain is self-serve-eligible no sales flag required", () => {
190
+ test("organizationHasVanitySubdomain does not require the sales flag, and Growth is entitled too", () => {
142
191
  expect(organizationHasVanitySubdomain(salesProvisionedEnterprise)).toBe(true);
143
- expect(organizationHasVanitySubdomain(selfServeEnterprise)).toBe(true);
144
- expect(organizationHasVanitySubdomain(growthActive)).toBe(false);
192
+ expect(organizationHasVanitySubdomain(nonSalesProvisionedEnterprise)).toBe(true);
193
+ expect(organizationHasVanitySubdomain(growthActive)).toBe(true);
145
194
  });
146
195
  });
@@ -26,31 +26,44 @@ export interface OrganizationEntitlementInput {
26
26
  enterpriseSalesProvisioned?: boolean | null;
27
27
  }
28
28
 
29
+ // Treat a missing status as active: legacy/ops-provisioned orgs may predate
30
+ // subscriptionStatus being populated, and we don't want to silently drop
31
+ // their entitlements.
32
+ function isActiveOrTrialingStatus(status?: string | null): boolean {
33
+ if (status == null) return true;
34
+ return (ACTIVE_ORG_SUBSCRIPTION_STATUSES as readonly string[]).includes(status);
35
+ }
36
+
29
37
  function hasActiveEnterpriseTier(
30
38
  org: OrganizationEntitlementInput | null | undefined
31
39
  ): boolean {
32
40
  if (!org) return false;
33
41
  if (org.subscriptionTier !== "Enterprise") return false;
34
- // Treat a missing status as active: legacy/ops-provisioned Enterprise orgs may
35
- // predate subscriptionStatus being populated, and we don't want to silently
36
- // drop their vanity domain.
37
- if (org.subscriptionStatus == null) return true;
38
- return (ACTIVE_ORG_SUBSCRIPTION_STATUSES as readonly string[]).includes(
39
- org.subscriptionStatus
40
- );
42
+ return isActiveOrTrialingStatus(org.subscriptionStatus);
43
+ }
44
+
45
+ function hasActiveGrowthOrAboveTier(
46
+ org: OrganizationEntitlementInput | null | undefined
47
+ ): boolean {
48
+ if (!org) return false;
49
+ if (org.subscriptionTier !== "Growth" && org.subscriptionTier !== "Enterprise") {
50
+ return false;
51
+ }
52
+ return isActiveOrTrialingStatus(org.subscriptionStatus);
41
53
  }
42
54
 
43
55
  /**
44
- * Vanity subdomains ({slug}.bash.community) and custom domains (white-label)
45
- * come bundled with any Enterprise plan Enterprise is sales-led (custom
46
- * quote), so this is always a sales-negotiated deal today, but the check
47
- * intentionally doesn't require `enterpriseSalesProvisioned` in case self-serve
48
- * Enterprise checkout returns as a fast-follow.
56
+ * Vanity subdomains ({slug}.bash.community) are a Growth-tier-and-up perk, not
57
+ * Enterprise-only: unlike custom domains, granting one costs nothing extra
58
+ * (a slug lookup against the existing bash.community CloudFront distribution,
59
+ * no per-org AWS resources) — it's a cheap, visible upgrade incentive to dangle
60
+ * in front of Starter orgs. See {@link organizationHasCustomDomainAccess} for
61
+ * the pricier, Enterprise-only white-label surface.
49
62
  */
50
63
  export function organizationHasVanitySubdomain(
51
64
  org: OrganizationEntitlementInput | null | undefined
52
65
  ): boolean {
53
- return hasActiveEnterpriseTier(org);
66
+ return hasActiveGrowthOrAboveTier(org);
54
67
  }
55
68
 
56
69
  /**
@@ -105,16 +118,17 @@ export function organizationHasComplianceAccess(
105
118
  /**
106
119
  * Custom domains (white-label tier): real DNS (e.g. members.acme.com) provisioned
107
120
  * via AWS CloudFront SaaS Manager Distribution Tenants, plus the "Powered by Bash"
108
- * removal toggle. Same Enterprise + active/trialing gate as
109
- * {@link organizationHasVanitySubdomain} for MVP reuse the existing tier rather
110
- * than invent a new pricing tier now. A dedicated "White-label" tier is a
111
- * fast-follow if sales needs separate pricing. Does NOT require
121
+ * removal toggle. Unlike {@link organizationHasVanitySubdomain}, this needs a real
122
+ * per-org CloudFront Distribution Tenant + managed cert + the org's own DNS work —
123
+ * actual infra cost and complexity per org so it stays Enterprise-only (today
124
+ * always sales-provisioned, since self-serve Enterprise checkout is blocked; see
125
+ * docs/ENTERPRISE-B2B-SALES-PLAYBOOK.md). Does NOT require
112
126
  * {@link organizationHasEnterprisePlatformAccess}'s `enterpriseSalesProvisioned`
113
- * flag, so a fast-follow self-serve Enterprise checkout could unlock this
114
- * without the sales-gated platform surface.
127
+ * flag, so a fast-follow self-serve Enterprise checkout could unlock this without
128
+ * the sales-gated platform surface (SSO, audit logs, procurement, compliance, API).
115
129
  */
116
130
  export function organizationHasCustomDomainAccess(
117
131
  org: OrganizationEntitlementInput | null | undefined
118
132
  ): boolean {
119
- return organizationHasVanitySubdomain(org);
133
+ return hasActiveEnterpriseTier(org);
120
134
  }