@onesub/server 0.19.0 → 0.20.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/README.md +1 -0
- package/dist/__tests__/apple-jws-cache.test.d.ts +16 -0
- package/dist/__tests__/apple-jws-cache.test.d.ts.map +1 -0
- package/dist/__tests__/metrics-aggregate.test.d.ts +10 -0
- package/dist/__tests__/metrics-aggregate.test.d.ts.map +1 -0
- package/dist/__tests__/metrics-cache.test.d.ts +11 -0
- package/dist/__tests__/metrics-cache.test.d.ts.map +1 -0
- package/dist/__tests__/verified-key-cache.test.d.ts +18 -0
- package/dist/__tests__/verified-key-cache.test.d.ts.map +1 -0
- package/dist/http.d.ts.map +1 -1
- package/dist/index.cjs +287 -190
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +288 -192
- package/dist/index.js.map +1 -1
- package/dist/metrics-aggregate.d.ts +82 -0
- package/dist/metrics-aggregate.d.ts.map +1 -0
- package/dist/metrics-cache.d.ts +20 -0
- package/dist/metrics-cache.d.ts.map +1 -0
- package/dist/providers/apple.d.ts +2 -0
- package/dist/providers/apple.d.ts.map +1 -1
- package/dist/providers/verified-key-cache.d.ts +75 -0
- package/dist/providers/verified-key-cache.d.ts.map +1 -0
- package/dist/routes/admin.d.ts.map +1 -1
- package/dist/routes/entitlements.d.ts +18 -1
- package/dist/routes/entitlements.d.ts.map +1 -1
- package/dist/routes/metrics.d.ts +8 -3
- package/dist/routes/metrics.d.ts.map +1 -1
- package/dist/store.d.ts +3 -2
- package/dist/store.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { MetricsActiveResponse, MetricsBucket, MetricsGroupBy, PurchaseInfo, SubscriptionInfo } from '@onesub/shared';
|
|
2
|
+
/**
|
|
3
|
+
* In-memory reduction of store records into the metrics responses.
|
|
4
|
+
*
|
|
5
|
+
* Split out of the route handlers for two reasons. It was duplicated four times
|
|
6
|
+
* — each range endpoint re-implemented the same window filter, the same
|
|
7
|
+
* product/platform tallies, and the same zero-filled daily bucketing — and none
|
|
8
|
+
* of it was reachable by a test without standing up an HTTP server. Both
|
|
9
|
+
* problems go away when the reduction is a pure function of the records.
|
|
10
|
+
*
|
|
11
|
+
* This is deliberately store-agnostic. Aggregating in the application process
|
|
12
|
+
* still costs one full read per query, which is the remaining scaling limit for
|
|
13
|
+
* large deployments; pushing the aggregation down into SQL requires per-store
|
|
14
|
+
* support and is separate work. What lives here is the semantics every store
|
|
15
|
+
* must agree on, so a pushdown implementation has something to be tested
|
|
16
|
+
* against.
|
|
17
|
+
*/
|
|
18
|
+
/** Minimum shape the tallies need. Both `SubscriptionInfo` and `PurchaseInfo` satisfy it. */
|
|
19
|
+
interface Countable {
|
|
20
|
+
productId: string;
|
|
21
|
+
platform: string;
|
|
22
|
+
}
|
|
23
|
+
/** The count-shaped part of a metrics response; the route adds `from`/`to`. */
|
|
24
|
+
export interface RangeAggregate {
|
|
25
|
+
total: number;
|
|
26
|
+
byProduct: Record<string, number>;
|
|
27
|
+
byPlatform: Record<string, number>;
|
|
28
|
+
/** Present only when `groupBy: 'day'` was requested. */
|
|
29
|
+
buckets?: MetricsBucket[];
|
|
30
|
+
}
|
|
31
|
+
export interface RangeAggregateOptions<T> {
|
|
32
|
+
/** Inclusive window bounds, ms-since-epoch. */
|
|
33
|
+
fromMs: number;
|
|
34
|
+
toMs: number;
|
|
35
|
+
groupBy: MetricsGroupBy;
|
|
36
|
+
/**
|
|
37
|
+
* ISO timestamp that places a record in the window and its daily bucket —
|
|
38
|
+
* `purchasedAt` for "started", `expiresAt` for "expired".
|
|
39
|
+
*/
|
|
40
|
+
anchor: (record: T) => string;
|
|
41
|
+
/** Records this rejects are ignored entirely (status / type gates). */
|
|
42
|
+
include?: (record: T) => boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* UTC `YYYY-MM-DD` for an epoch-ms instant.
|
|
46
|
+
*
|
|
47
|
+
* UTC keeps bucket assignment deterministic regardless of server timezone:
|
|
48
|
+
* Postgres `updated_at` is UTC and `SubscriptionInfo` dates carry explicit zone
|
|
49
|
+
* offsets, so a local-time key would silently shift every boundary when the
|
|
50
|
+
* process moved between regions.
|
|
51
|
+
*/
|
|
52
|
+
export declare function utcDateKey(ms: number): string;
|
|
53
|
+
/**
|
|
54
|
+
* Zero-filled daily series across `[fromMs, toMs]`, inclusive of both boundary
|
|
55
|
+
* days. Zero-filling is what lets a chart render a flat stretch as flat rather
|
|
56
|
+
* than as a gap.
|
|
57
|
+
*/
|
|
58
|
+
export declare function emptyDailyBuckets(fromMs: number, toMs: number): MetricsBucket[];
|
|
59
|
+
/**
|
|
60
|
+
* Tally records whose anchor timestamp falls inside the window.
|
|
61
|
+
*
|
|
62
|
+
* Records outside the window, and records rejected by `include`, contribute
|
|
63
|
+
* nothing — not to `total`, not to the distributions, not to a bucket.
|
|
64
|
+
*/
|
|
65
|
+
export declare function aggregateRange<T extends Countable>(records: readonly T[], opts: RangeAggregateOptions<T>): RangeAggregate;
|
|
66
|
+
/** A subscription counts as currently entitled: allowed status AND not yet expired. */
|
|
67
|
+
export declare function isActiveSubscription(sub: SubscriptionInfo, nowMs: number): boolean;
|
|
68
|
+
/** A subscription counts as ended: the store recorded it expired or canceled. */
|
|
69
|
+
export declare function isEndedSubscription(sub: SubscriptionInfo): boolean;
|
|
70
|
+
/** Non-consumables grant an ongoing right; consumables are a spent resource. */
|
|
71
|
+
export declare function isNonConsumable(purchase: PurchaseInfo): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Point-in-time entitlement snapshot.
|
|
74
|
+
*
|
|
75
|
+
* `byProduct` is subscriptions-only and `byProductPurchases` is
|
|
76
|
+
* non-consumables-only, so a dashboard can show "subscription mix" and
|
|
77
|
+
* "lifetime mix" as separate panels. `byPlatform` deliberately spans both —
|
|
78
|
+
* it answers "where are my paying users", which is not a per-kind question.
|
|
79
|
+
*/
|
|
80
|
+
export declare function aggregateActive(subs: readonly SubscriptionInfo[], purchases: readonly PurchaseInfo[], nowMs: number): MetricsActiveResponse;
|
|
81
|
+
export {};
|
|
82
|
+
//# sourceMappingURL=metrics-aggregate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics-aggregate.d.ts","sourceRoot":"","sources":["../src/metrics-aggregate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,aAAa,EACb,cAAc,EACd,YAAY,EACZ,gBAAgB,EACjB,MAAM,gBAAgB,CAAC;AAGxB;;;;;;;;;;;;;;;GAeG;AAEH,6FAA6F;AAC7F,UAAU,SAAS;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,+EAA+E;AAC/E,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,wDAAwD;IACxD,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB,CAAC,CAAC;IACtC,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,cAAc,CAAC;IACxB;;;OAGG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,CAAC;IAC9B,uEAAuE;IACvE,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC;CAClC;AAQD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAM7C;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE,CAW/E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,SAAS,EAChD,OAAO,EAAE,SAAS,CAAC,EAAE,EACrB,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAC7B,cAAc,CA0BhB;AAED,uFAAuF;AACvF,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAIlF;AAED,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAIlE;AAED,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAE/D;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,SAAS,gBAAgB,EAAE,EACjC,SAAS,EAAE,SAAS,YAAY,EAAE,EAClC,KAAK,EAAE,MAAM,GACZ,qBAAqB,CAgCvB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { CacheAdapter } from './cache.js';
|
|
2
|
+
export interface MetricsCache {
|
|
3
|
+
/**
|
|
4
|
+
* Return the value for `keyParts`, computing it via `produce` on a miss.
|
|
5
|
+
*
|
|
6
|
+
* `keyParts` must not contain a raw request timestamp — see
|
|
7
|
+
* `quantizeToWindow`.
|
|
8
|
+
*/
|
|
9
|
+
resolve<T>(keyParts: readonly (string | number)[], produce: () => Promise<T>): Promise<T>;
|
|
10
|
+
/** Snap a millisecond timestamp onto the TTL grid, for use in a cache key. */
|
|
11
|
+
quantizeToWindow(ms: number): number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* @param ttlSeconds Freshness bound. `0` (or less) disables the cache.
|
|
15
|
+
* @param storage Backing store. Defaults to storage private to this
|
|
16
|
+
* instance — see the note above on why this is not the shared
|
|
17
|
+
* adapter. Injectable for tests.
|
|
18
|
+
*/
|
|
19
|
+
export declare function createMetricsCache(ttlSeconds: number, storage?: CacheAdapter): MetricsCache;
|
|
20
|
+
//# sourceMappingURL=metrics-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics-cache.d.ts","sourceRoot":"","sources":["../src/metrics-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAkC/C,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC1F,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;CACtC;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,YAAyC,GACjD,YAAY,CAgDd"}
|
|
@@ -20,6 +20,8 @@ type AppleConfig = NonNullable<OneSubServerConfig['apple']>;
|
|
|
20
20
|
* Exported for unit testing.
|
|
21
21
|
*/
|
|
22
22
|
export declare function assertIssuerCanSign(cert: Pick<X509Certificate, 'ca'>, index: number): void;
|
|
23
|
+
/** Test/diagnostic hook — drop every memoised chain verification. */
|
|
24
|
+
export declare function clearAppleCertCache(): void;
|
|
23
25
|
/**
|
|
24
26
|
* Decode and verify a StoreKit 2 signed transaction JWS.
|
|
25
27
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apple.d.ts","sourceRoot":"","sources":["../../src/providers/apple.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAA0B,MAAM,aAAa,CAAC;AACtE,OAAO,KAAK,EACV,gBAAgB,EAChB,wBAAwB,EACxB,uBAAuB,EACvB,kBAAkB,EACnB,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"apple.d.ts","sourceRoot":"","sources":["../../src/providers/apple.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memo for "this certificate chain was verified, and here is the key it
|
|
3
|
+
* authorises" — the result of an expensive, purely local verification.
|
|
4
|
+
*
|
|
5
|
+
* Why this exists: Apple's StoreKit JWS verification re-parses the x5c chain,
|
|
6
|
+
* re-checks every issuer signature against a bundled Apple root, and re-imports
|
|
7
|
+
* the leaf public key on EVERY receipt and EVERY webhook. All of it is
|
|
8
|
+
* synchronous CPU work on the event loop, and Apple's leaf certificates are
|
|
9
|
+
* stable for weeks — so the same chain is verified over and over with the same
|
|
10
|
+
* outcome.
|
|
11
|
+
*
|
|
12
|
+
* Why this is NOT the pluggable `CacheAdapter`: that adapter exists to share
|
|
13
|
+
* *rate-limited network* results (Apple API JWTs, Google OAuth tokens) between
|
|
14
|
+
* cluster nodes, and it serialises values as JSON. An imported key is not
|
|
15
|
+
* JSON-serialisable — a Redis-backed adapter would silently round-trip it to
|
|
16
|
+
* `{}` — and what is being avoided here is local CPU, not a network call or a
|
|
17
|
+
* quota. A Redis round-trip would plausibly cost more than the verification it
|
|
18
|
+
* replaces. So this cache is deliberately process-local.
|
|
19
|
+
*
|
|
20
|
+
* Security invariants, all enforced below and covered by tests:
|
|
21
|
+
*
|
|
22
|
+
* - The entry key covers EVERY input to the verification, including the
|
|
23
|
+
* algorithm the key was imported for. A different chain, or the same chain
|
|
24
|
+
* claiming a different `alg`, is a different entry — it can never reuse a
|
|
25
|
+
* key that was authorised for something else.
|
|
26
|
+
* - A failed verification is never stored. A rejected `produce` propagates and
|
|
27
|
+
* leaves the cache untouched, so a bad chain is re-verified (and re-rejected)
|
|
28
|
+
* every time rather than being remembered either way.
|
|
29
|
+
* - No entry outlives the chain that produced it. `notAfter` (the earliest
|
|
30
|
+
* expiry in the verified chain) is a hard ceiling on the entry's lifetime,
|
|
31
|
+
* so an expired certificate can never be served from cache.
|
|
32
|
+
*/
|
|
33
|
+
/** What a successful verification yields: the key, and when it stops being valid. */
|
|
34
|
+
export interface VerifiedKey<K> {
|
|
35
|
+
key: K;
|
|
36
|
+
/**
|
|
37
|
+
* Earliest `notAfter` across the verified chain, as ms-since-epoch. The cache
|
|
38
|
+
* entry is dropped at or before this instant, never after it.
|
|
39
|
+
*/
|
|
40
|
+
notAfter: number;
|
|
41
|
+
}
|
|
42
|
+
export interface VerifiedKeyCacheOptions {
|
|
43
|
+
/**
|
|
44
|
+
* Upper bound on entries. Only successfully verified chains are stored, so in
|
|
45
|
+
* practice this holds a handful (Apple production + sandbox leaves, across a
|
|
46
|
+
* rotation). The cap is defence in depth against unbounded growth.
|
|
47
|
+
*/
|
|
48
|
+
maxEntries?: number;
|
|
49
|
+
/** Upper bound on an entry's lifetime, independent of certificate expiry. */
|
|
50
|
+
maxTtlMs?: number;
|
|
51
|
+
/** Injectable clock. Tests drive expiry with it; production uses `Date.now`. */
|
|
52
|
+
now?: () => number;
|
|
53
|
+
}
|
|
54
|
+
export declare class VerifiedKeyCache<K> {
|
|
55
|
+
private readonly entries;
|
|
56
|
+
private readonly maxEntries;
|
|
57
|
+
private readonly maxTtlMs;
|
|
58
|
+
private readonly now;
|
|
59
|
+
constructor(opts?: VerifiedKeyCacheOptions);
|
|
60
|
+
/**
|
|
61
|
+
* Return the key for `parts`, verifying via `produce` only on a miss.
|
|
62
|
+
*
|
|
63
|
+
* `parts` must contain every input the verification depends on — for Apple
|
|
64
|
+
* that is the JWS `alg` plus the full x5c chain. Anything left out would let
|
|
65
|
+
* one input's result be served for a different input.
|
|
66
|
+
*/
|
|
67
|
+
resolve(parts: readonly string[], produce: () => Promise<VerifiedKey<K>>): Promise<K>;
|
|
68
|
+
/** Entries currently held, including any not yet pruned. Test/diagnostic use. */
|
|
69
|
+
get size(): number;
|
|
70
|
+
/** Drop everything. Test/diagnostic use. */
|
|
71
|
+
clear(): void;
|
|
72
|
+
/** Make room: expired entries first, then the oldest insertions. */
|
|
73
|
+
private prune;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=verified-key-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verified-key-cache.d.ts","sourceRoot":"","sources":["../../src/providers/verified-key-cache.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,qFAAqF;AACrF,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,GAAG,EAAE,CAAC,CAAC;IACP;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAWD,qBAAa,gBAAgB,CAAC,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IACvD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEvB,IAAI,GAAE,uBAA4B;IAM9C;;;;;;OAMG;IACG,OAAO,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IA0B3F,iFAAiF;IACjF,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,4CAA4C;IAC5C,KAAK,IAAI,IAAI;IAIb,oEAAoE;IACpE,OAAO,CAAC,KAAK;CAWd"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../src/routes/admin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,OAAO,KAAK,EAGV,kBAAkB,EAKnB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAQxD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,kBAAkB,EAC1B,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE,iBAAiB,EACxB,YAAY,CAAC,EAAE,YAAY,GAC1B,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../src/routes/admin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,OAAO,KAAK,EAGV,kBAAkB,EAKnB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAQxD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,kBAAkB,EAC1B,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE,iBAAiB,EACxB,YAAY,CAAC,EAAE,YAAY,GAC1B,MAAM,GAAG,IAAI,CAmWf"}
|
|
@@ -2,7 +2,12 @@ import { Router } from 'express';
|
|
|
2
2
|
import type { Entitlement, EntitlementStatus, OneSubServerConfig, PurchaseInfo, SubscriptionInfo } from '@onesub/shared';
|
|
3
3
|
import type { PurchaseStore, SubscriptionStore } from '../store.js';
|
|
4
4
|
/**
|
|
5
|
-
* Evaluate
|
|
5
|
+
* Evaluate one entitlement against records the caller already holds.
|
|
6
|
+
*
|
|
7
|
+
* This is the whole decision, with no store access — so a caller evaluating N
|
|
8
|
+
* entitlements for one user reads that user's records ONCE and calls this N
|
|
9
|
+
* times, instead of paying 2N store round-trips. `evaluateEntitlement` below
|
|
10
|
+
* is the convenience wrapper for the single-entitlement case.
|
|
6
11
|
*
|
|
7
12
|
* A user is entitled when EITHER condition holds for any productId in
|
|
8
13
|
* `entitlement.productIds`:
|
|
@@ -16,6 +21,18 @@ import type { PurchaseStore, SubscriptionStore } from '../store.js';
|
|
|
16
21
|
* Subscription is preferred over purchase when both match (subs carry an
|
|
17
22
|
* expiry; non-consumables are forever) so the source field skews toward the
|
|
18
23
|
* more "interesting" signal for ops/analytics.
|
|
24
|
+
*
|
|
25
|
+
* `now` is injected so a batch evaluation scores every entitlement against one
|
|
26
|
+
* consistent instant rather than drifting across awaits.
|
|
27
|
+
*/
|
|
28
|
+
export declare function evaluateEntitlementFrom(subs: readonly SubscriptionInfo[], purchases: readonly PurchaseInfo[], entitlement: Entitlement, now?: number): EntitlementStatus;
|
|
29
|
+
/**
|
|
30
|
+
* Evaluate a single entitlement for a user, reading the records it needs.
|
|
31
|
+
*
|
|
32
|
+
* Thin wrapper over `evaluateEntitlementFrom` — see there for the entitlement
|
|
33
|
+
* rules. Evaluating several entitlements for the same user through this
|
|
34
|
+
* function costs 2 store reads each; prefer reading once and calling
|
|
35
|
+
* `evaluateEntitlementFrom` directly in that case.
|
|
19
36
|
*/
|
|
20
37
|
export declare function evaluateEntitlement(userId: string, entitlement: Entitlement, store: SubscriptionStore, purchaseStore: PurchaseStore): Promise<EntitlementStatus>;
|
|
21
38
|
export declare function createEntitlementRouter(config: OneSubServerConfig, store: SubscriptionStore, purchaseStore: PurchaseStore): Router | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entitlements.d.ts","sourceRoot":"","sources":["../../src/routes/entitlements.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,OAAO,KAAK,EACV,WAAW,EAEX,iBAAiB,EAGjB,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EACjB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIpE
|
|
1
|
+
{"version":3,"file":"entitlements.d.ts","sourceRoot":"","sources":["../../src/routes/entitlements.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,OAAO,KAAK,EACV,WAAW,EAEX,iBAAiB,EAGjB,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EACjB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIpE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,SAAS,gBAAgB,EAAE,EACjC,SAAS,EAAE,SAAS,YAAY,EAAE,EAClC,WAAW,EAAE,WAAW,EACxB,GAAG,GAAE,MAAmB,GACvB,iBAAiB,CA+BnB;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE,iBAAiB,EACxB,aAAa,EAAE,aAAa,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CAW5B;AAKD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,iBAAiB,EACxB,aAAa,EAAE,aAAa,GAC3B,MAAM,GAAG,IAAI,CAgFf;AAID,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,YAAY,EAAE,gBAAgB,EAAE,CAAC"}
|
package/dist/routes/metrics.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
2
|
import type { OneSubServerConfig, PurchaseInfo, SubscriptionInfo } from '@onesub/shared';
|
|
3
3
|
import type { PurchaseStore, SubscriptionStore } from '../store.js';
|
|
4
|
+
/** Freshness bound when the host does not configure one. */
|
|
5
|
+
export declare const DEFAULT_METRICS_CACHE_TTL_SECONDS = 30;
|
|
4
6
|
/**
|
|
5
7
|
* Read-only aggregate metrics endpoints — gated behind `config.adminSecret`.
|
|
6
8
|
* Returns count-based metrics only (active count, started count, expired count).
|
|
@@ -9,9 +11,12 @@ import type { PurchaseStore, SubscriptionStore } from '../store.js';
|
|
|
9
11
|
* server doesn't currently track; deferred to a follow-up release that adds
|
|
10
12
|
* `config.products: { 'pro_monthly': { price: 9.99, currency: 'USD' } }`.
|
|
11
13
|
*
|
|
12
|
-
* Aggregation strategy:
|
|
13
|
-
*
|
|
14
|
-
*
|
|
14
|
+
* Aggregation strategy: reads every record via `store.listAll()` and reduces in
|
|
15
|
+
* memory (see `metrics-aggregate.ts` for the reduction, which is where the
|
|
16
|
+
* semantics are tested). That is one full read per computed response, so
|
|
17
|
+
* responses are cached for `config.metricsCacheTtlSeconds` to bound how often
|
|
18
|
+
* it happens — see `metrics-cache.ts`. Removing the per-response scan entirely
|
|
19
|
+
* needs SQL-side aggregation, which is per-store work and still pending.
|
|
15
20
|
*/
|
|
16
21
|
export declare function createMetricsRouter(config: OneSubServerConfig, store: SubscriptionStore, purchaseStore: PurchaseStore): Router | null;
|
|
17
22
|
export type { PurchaseInfo, SubscriptionInfo };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/routes/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,OAAO,KAAK,EAIV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EACjB,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/routes/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,OAAO,KAAK,EAIV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EACjB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAepE,4DAA4D;AAC5D,eAAO,MAAM,iCAAiC,KAAK,CAAC;AAEpD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,iBAAiB,EACxB,aAAa,EAAE,aAAa,GAC3B,MAAM,GAAG,IAAI,CAuKf;AAED,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,CAAC"}
|
package/dist/store.d.ts
CHANGED
|
@@ -54,8 +54,9 @@ export interface SubscriptionStore {
|
|
|
54
54
|
* see all of a user's active subscriptions to decide whether any of them
|
|
55
55
|
* grants the requested entitlement.
|
|
56
56
|
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
57
|
+
* All three built-in implementations return the user's full set, one record
|
|
58
|
+
* per `originalTransactionId`. "Most-recent-first" is last-written-first for
|
|
59
|
+
* the in-memory store and `updated_at DESC` for Postgres/Redis.
|
|
59
60
|
*/
|
|
60
61
|
getAllByUserId(userId: string): Promise<SubscriptionInfo[]>;
|
|
61
62
|
}
|
package/dist/store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAEnG,8EAA8E;AAC9E,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,+EAA+E;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,gDAAgD;AAChD,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C;;;;OAIG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC9D,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACnE;;;;OAIG;IACH,OAAO,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACvC;;;;;;;;OAQG;IACH,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACrE
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onesub/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Server-side receipt validation middleware for react-native-iap. Apple StoreKit 2 + Google Play Billing. One line.",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
"license": "MIT",
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@onesub/shared": "0.
|
|
58
|
+
"@onesub/shared": "0.15.0",
|
|
59
59
|
"jose": "^6.2.3",
|
|
60
60
|
"zod": "^4.4.3"
|
|
61
61
|
},
|