@tumbaland/backend-core 1.28.0 → 1.29.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/dist/entitlements/definitions.d.ts.map +1 -1
- package/dist/entitlements/definitions.js +14 -7
- package/dist/entitlements/definitions.js.map +1 -1
- package/package.json +1 -1
- package/src/entitlements/client.test.ts +6 -6
- package/src/entitlements/definitions.test.ts +8 -8
- package/src/entitlements/definitions.ts +14 -7
- package/src/entitlements/middleware.test.ts +5 -5
- package/src/errors/HttpError.test.ts +2 -2
- package/src/middleware/errorHandler.test.ts +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/entitlements/definitions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,MAAM,QAAQ,GAAG,cAAc,GAAG,QAAQ,GAAG,gBAAgB,GAAG,OAAO,CAAC;AAE9E;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,aAAa,CAAC;AAEvC,8FAA8F;AAC9F,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,OAAO,CAAC;AAE1C,2FAA2F;AAC3F,eAAO,MAAM,SAAS,KAAK,CAAC;AAE5B,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,QAAQ,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,oGAAoG;IACpG,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACxB,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,UAAU,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,iBAAiB,EAAE,SAAS,eAAe,EA6B9C,CAAC;AAEX,eAAO,MAAM,mBAAmB,EAAE,SAAS,iBAAiB,EAMlD,CAAC;AAEX,eAAO,MAAM,UAAU,EAAE,SAAS,QAAQ,EAAsC,CAAC;AACjF,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAAwC,CAAC;AAMvF,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,WAAW,CAExD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,QAAQ,CAE3D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,UAAU,CAE/D;AAED,MAAM,WAAW,UAAU;IACzB,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACtC,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,8EAA8E;AAC9E,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAID;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,
|
|
1
|
+
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/entitlements/definitions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,MAAM,QAAQ,GAAG,cAAc,GAAG,QAAQ,GAAG,gBAAgB,GAAG,OAAO,CAAC;AAE9E;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,aAAa,CAAC;AAEvC,8FAA8F;AAC9F,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,OAAO,CAAC;AAE1C,2FAA2F;AAC3F,eAAO,MAAM,SAAS,KAAK,CAAC;AAE5B,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,QAAQ,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,oGAAoG;IACpG,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACxB,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,UAAU,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,iBAAiB,EAAE,SAAS,eAAe,EA6B9C,CAAC;AAEX,eAAO,MAAM,mBAAmB,EAAE,SAAS,iBAAiB,EAMlD,CAAC;AAEX,eAAO,MAAM,UAAU,EAAE,SAAS,QAAQ,EAAsC,CAAC;AACjF,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAAwC,CAAC;AAMvF,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,WAAW,CAExD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,QAAQ,CAE3D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,UAAU,CAE/D;AAED,MAAM,WAAW,UAAU;IACzB,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACtC,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,8EAA8E;AAC9E,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAID;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAsCpE,CAAC;AAEF,iDAAiD;AACjD,eAAO,MAAM,UAAU,UAAU,CAAC;AAElC,eAAO,MAAM,aAAa,SAAS,CAAC;AAEpC;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAG3D;AAiBD;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,iBAAiB,GAAG,IAAI,GAAG,UAAU,CAc1F;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,CAAC,EAAE,iBAAiB,GAAG,IAAI,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAE/F;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,SAAI,GAAG,OAAO,CAG3E;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;CAa/B,CAAC"}
|
|
@@ -94,13 +94,20 @@ exports.DEFAULT_PLAN_LIMITS = {
|
|
|
94
94
|
features: { cleanExport: false },
|
|
95
95
|
retentionDays: 90
|
|
96
96
|
},
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
97
|
+
/**
|
|
98
|
+
* The single paid plan, and it is a *support* tier rather than a product
|
|
99
|
+
* edition. `personal` and `family` were consolidated into it: two paid tiers
|
|
100
|
+
* imply a business deciding what to withhold from which customer, which is
|
|
101
|
+
* the wrong shape for a project asking people to chip in. One tier that
|
|
102
|
+
* grants everything removes that question entirely.
|
|
103
|
+
*
|
|
104
|
+
* Limits are the old `family` numbers — pooled seats, unlimited retention —
|
|
105
|
+
* except storage, which is 50 GB rather than 250. 250 GB was priced for a
|
|
106
|
+
* household paying €12; at €5 it is more object storage than the support is
|
|
107
|
+
* covering.
|
|
108
|
+
*/
|
|
109
|
+
support: {
|
|
110
|
+
meters: { storageBytes: 50 * GB, aiJobs: 600, trackedTickers: 100, seats: 5 },
|
|
104
111
|
features: { cleanExport: true },
|
|
105
112
|
retentionDays: exports.UNLIMITED
|
|
106
113
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/entitlements/definitions.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;GAkBG;;;AA8EH,kCAEC;AAED,gCAEC;AAED,oCAEC;
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/entitlements/definitions.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -17,10 +17,10 @@ function jsonResponse(body: unknown, status = 200): Response {
|
|
|
17
17
|
function paidResponse() {
|
|
18
18
|
return jsonResponse({
|
|
19
19
|
success: true,
|
|
20
|
-
planCode: '
|
|
20
|
+
planCode: 'support',
|
|
21
21
|
status: 'active',
|
|
22
22
|
limits: { meters: { storageBytes: 12345 } },
|
|
23
|
-
upgradeTo: '
|
|
23
|
+
upgradeTo: 'support'
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -53,12 +53,12 @@ describe('getEntitlements', () => {
|
|
|
53
53
|
headers: expect.objectContaining({ 'x-internal-token': 'test-internal-token', 'x-service-id': 'album-service' })
|
|
54
54
|
})
|
|
55
55
|
);
|
|
56
|
-
expect(result.planCode).toBe('
|
|
57
|
-
expect(result.upgradeTo).toBe('
|
|
56
|
+
expect(result.planCode).toBe('support');
|
|
57
|
+
expect(result.upgradeTo).toBe('support');
|
|
58
58
|
expect(result.limits.meters.storageBytes).toBe(12345);
|
|
59
59
|
// Keys the payload omitted are filled from that plan's defaults rather than
|
|
60
60
|
// arriving undefined and comparing as NaN inside a quota check.
|
|
61
|
-
expect(result.limits.meters.seats).toBe(DEFAULT_PLAN_LIMITS.
|
|
61
|
+
expect(result.limits.meters.seats).toBe(DEFAULT_PLAN_LIMITS.support.meters.seats);
|
|
62
62
|
expect(result.stale).toBeUndefined();
|
|
63
63
|
});
|
|
64
64
|
|
|
@@ -135,7 +135,7 @@ describe('when payment-service cannot be reached', () => {
|
|
|
135
135
|
const recovered = await getEntitlements('user@example.com');
|
|
136
136
|
|
|
137
137
|
expect(failed.stale).toBe(true);
|
|
138
|
-
expect(recovered.planCode).toBe('
|
|
138
|
+
expect(recovered.planCode).toBe('support');
|
|
139
139
|
});
|
|
140
140
|
|
|
141
141
|
it('fails closed without calling out at all when the channel is unconfigured', async () => {
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
|
|
17
17
|
describe('resolveLimits', () => {
|
|
18
18
|
it('returns the defaults for a known plan code', () => {
|
|
19
|
-
expect(resolveLimits('
|
|
19
|
+
expect(resolveLimits('support')).toEqual(DEFAULT_PLAN_LIMITS.support);
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
it('falls back to free for an unknown code, never to unlimited', () => {
|
|
@@ -36,12 +36,12 @@ describe('resolveLimits', () => {
|
|
|
36
36
|
|
|
37
37
|
describe('normalizeLimits', () => {
|
|
38
38
|
it('applies stored values over the plan defaults', () => {
|
|
39
|
-
const limits = normalizeLimits({ meters: { storageBytes: 123 }, retentionDays: 30 }, '
|
|
39
|
+
const limits = normalizeLimits({ meters: { storageBytes: 123 }, retentionDays: 30 }, 'support');
|
|
40
40
|
|
|
41
41
|
expect(limits.meters.storageBytes).toBe(123);
|
|
42
42
|
expect(limits.retentionDays).toBe(30);
|
|
43
43
|
// Untouched keys keep the plan's defaults rather than resetting to free.
|
|
44
|
-
expect(limits.meters.trackedTickers).toBe(DEFAULT_PLAN_LIMITS.
|
|
44
|
+
expect(limits.meters.trackedTickers).toBe(DEFAULT_PLAN_LIMITS.support.meters.trackedTickers);
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
it('drops keys that no code enforces', () => {
|
|
@@ -66,17 +66,17 @@ describe('normalizeLimits', () => {
|
|
|
66
66
|
it('ignores non-numeric and non-boolean junk instead of producing NaN limits', () => {
|
|
67
67
|
const limits = normalizeLimits(
|
|
68
68
|
{ meters: { seats: 'lots' as unknown as number }, features: { cleanExport: 'yes' as unknown as boolean } },
|
|
69
|
-
'
|
|
69
|
+
'support'
|
|
70
70
|
);
|
|
71
71
|
|
|
72
|
-
expect(limits.meters.seats).toBe(DEFAULT_PLAN_LIMITS.
|
|
72
|
+
expect(limits.meters.seats).toBe(DEFAULT_PLAN_LIMITS.support.meters.seats);
|
|
73
73
|
expect(limits.features.cleanExport).toBe(true);
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
it('returns the plan defaults for an empty or missing payload', () => {
|
|
77
|
-
expect(normalizeLimits(undefined, '
|
|
78
|
-
expect(normalizeLimits(null, '
|
|
79
|
-
expect(normalizeLimits({}, '
|
|
77
|
+
expect(normalizeLimits(undefined, 'support')).toEqual(DEFAULT_PLAN_LIMITS.support);
|
|
78
|
+
expect(normalizeLimits(null, 'support')).toEqual(DEFAULT_PLAN_LIMITS.support);
|
|
79
|
+
expect(normalizeLimits({}, 'support')).toEqual(DEFAULT_PLAN_LIMITS.support);
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
it('resolves an unknown plan code to free even when limits are stored', () => {
|
|
@@ -138,13 +138,20 @@ export const DEFAULT_PLAN_LIMITS: Readonly<Record<string, PlanLimits>> = {
|
|
|
138
138
|
features: { cleanExport: false },
|
|
139
139
|
retentionDays: 90
|
|
140
140
|
},
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
141
|
+
/**
|
|
142
|
+
* The single paid plan, and it is a *support* tier rather than a product
|
|
143
|
+
* edition. `personal` and `family` were consolidated into it: two paid tiers
|
|
144
|
+
* imply a business deciding what to withhold from which customer, which is
|
|
145
|
+
* the wrong shape for a project asking people to chip in. One tier that
|
|
146
|
+
* grants everything removes that question entirely.
|
|
147
|
+
*
|
|
148
|
+
* Limits are the old `family` numbers — pooled seats, unlimited retention —
|
|
149
|
+
* except storage, which is 50 GB rather than 250. 250 GB was priced for a
|
|
150
|
+
* household paying €12; at €5 it is more object storage than the support is
|
|
151
|
+
* covering.
|
|
152
|
+
*/
|
|
153
|
+
support: {
|
|
154
|
+
meters: { storageBytes: 50 * GB, aiJobs: 600, trackedTickers: 100, seats: 5 },
|
|
148
155
|
features: { cleanExport: true },
|
|
149
156
|
retentionDays: UNLIMITED
|
|
150
157
|
},
|
|
@@ -25,7 +25,7 @@ function entitlements(overrides: Partial<Entitlements> = {}): Entitlements {
|
|
|
25
25
|
planCode: 'free',
|
|
26
26
|
status: null,
|
|
27
27
|
limits: normalizeLimits(undefined, 'free'),
|
|
28
|
-
upgradeTo: '
|
|
28
|
+
upgradeTo: 'support',
|
|
29
29
|
...overrides
|
|
30
30
|
};
|
|
31
31
|
}
|
|
@@ -68,7 +68,7 @@ describe('loadEntitlements', () => {
|
|
|
68
68
|
|
|
69
69
|
describe('requireEntitlement', () => {
|
|
70
70
|
it('passes a plan that includes the feature', async () => {
|
|
71
|
-
mockGetEntitlements.mockResolvedValue(entitlements({ planCode: '
|
|
71
|
+
mockGetEntitlements.mockResolvedValue(entitlements({ planCode: 'support', limits: normalizeLimits(undefined, 'support') }));
|
|
72
72
|
const next = jest.fn();
|
|
73
73
|
|
|
74
74
|
await requireEntitlement('cleanExport')(mockReq(), {} as Response, next);
|
|
@@ -84,7 +84,7 @@ describe('requireEntitlement', () => {
|
|
|
84
84
|
expect(next).toHaveBeenCalledWith(
|
|
85
85
|
expect.objectContaining({
|
|
86
86
|
statusCode: 402,
|
|
87
|
-
details: { code: 'FEATURE_NOT_IN_PLAN', feature: 'cleanExport', planCode: 'free', upgradeTo: '
|
|
87
|
+
details: { code: 'FEATURE_NOT_IN_PLAN', feature: 'cleanExport', planCode: 'free', upgradeTo: 'support' }
|
|
88
88
|
})
|
|
89
89
|
);
|
|
90
90
|
});
|
|
@@ -142,7 +142,7 @@ describe('retentionFloor', () => {
|
|
|
142
142
|
|
|
143
143
|
it('returns null for an unlimited plan, so no $match is added', async () => {
|
|
144
144
|
mockGetEntitlements.mockResolvedValue(
|
|
145
|
-
entitlements({ planCode: '
|
|
145
|
+
entitlements({ planCode: 'support', limits: normalizeLimits({ retentionDays: UNLIMITED }, 'support') })
|
|
146
146
|
);
|
|
147
147
|
|
|
148
148
|
expect(await retentionFloor('user@example.com', now)).toBeNull();
|
|
@@ -163,7 +163,7 @@ describe('retentionFloor across several subjects', () => {
|
|
|
163
163
|
it('takes the most generous floor, so a paid group lifts it for every member', () => {
|
|
164
164
|
mockGetEntitlements.mockImplementation(async (email: string) =>
|
|
165
165
|
email === 'owner@example.com'
|
|
166
|
-
? entitlements({ planCode: '
|
|
166
|
+
? entitlements({ planCode: 'support', limits: normalizeLimits({ retentionDays: UNLIMITED }, 'support') })
|
|
167
167
|
: entitlements()
|
|
168
168
|
);
|
|
169
169
|
|
|
@@ -26,7 +26,7 @@ describe('PaymentRequiredError', () => {
|
|
|
26
26
|
used: 10,
|
|
27
27
|
limit: 10,
|
|
28
28
|
planCode: 'free',
|
|
29
|
-
upgradeTo: '
|
|
29
|
+
upgradeTo: 'support'
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
expect(error.statusCode).toBe(402);
|
|
@@ -37,7 +37,7 @@ describe('PaymentRequiredError', () => {
|
|
|
37
37
|
used: 10,
|
|
38
38
|
limit: 10,
|
|
39
39
|
planCode: 'free',
|
|
40
|
-
upgradeTo: '
|
|
40
|
+
upgradeTo: 'support'
|
|
41
41
|
});
|
|
42
42
|
});
|
|
43
43
|
|
|
@@ -33,7 +33,7 @@ function buildApp() {
|
|
|
33
33
|
used: 1073741824,
|
|
34
34
|
limit: 1073741824,
|
|
35
35
|
planCode: 'free',
|
|
36
|
-
upgradeTo: '
|
|
36
|
+
upgradeTo: 'support'
|
|
37
37
|
})
|
|
38
38
|
)
|
|
39
39
|
);
|
|
@@ -172,7 +172,7 @@ describe('errorHandler', () => {
|
|
|
172
172
|
used: 1073741824,
|
|
173
173
|
limit: 1073741824,
|
|
174
174
|
planCode: 'free',
|
|
175
|
-
upgradeTo: '
|
|
175
|
+
upgradeTo: 'support'
|
|
176
176
|
});
|
|
177
177
|
});
|
|
178
178
|
|