@onesub/server 0.22.0 → 0.23.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/__tests__/postgres-store.test.d.ts +16 -0
- package/dist/__tests__/postgres-store.test.d.ts.map +1 -0
- package/dist/index.cjs +168 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +168 -26
- package/dist/index.js.map +1 -1
- package/dist/metrics-aggregate.d.ts +27 -8
- package/dist/metrics-aggregate.d.ts.map +1 -1
- package/dist/routes/metrics.d.ts +18 -6
- package/dist/routes/metrics.d.ts.map +1 -1
- package/dist/store.d.ts +65 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/stores/postgres.d.ts +21 -1
- package/dist/stores/postgres.d.ts.map +1 -1
- package/package.json +2 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { MetricsActiveResponse, MetricsBucket, MetricsGroupBy, PurchaseInfo, SubscriptionInfo } from '@onesub/shared';
|
|
2
|
+
import type { ActiveSubscriptionAggregate, MetricsRangeAggregate, NonConsumablePurchaseAggregate } from './store.js';
|
|
2
3
|
/**
|
|
3
4
|
* In-memory reduction of store records into the metrics responses.
|
|
4
5
|
*
|
|
@@ -20,14 +21,13 @@ interface Countable {
|
|
|
20
21
|
productId: string;
|
|
21
22
|
platform: string;
|
|
22
23
|
}
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
24
|
+
/**
|
|
25
|
+
* The count-shaped part of a metrics response; the route adds `from`/`to`.
|
|
26
|
+
*
|
|
27
|
+
* Aliased to the store contract rather than declared separately, so the SQL
|
|
28
|
+
* implementations and this reducer cannot drift into different shapes.
|
|
29
|
+
*/
|
|
30
|
+
export type RangeAggregate = MetricsRangeAggregate;
|
|
31
31
|
export interface RangeAggregateOptions<T> {
|
|
32
32
|
/** Inclusive window bounds, ms-since-epoch. */
|
|
33
33
|
fromMs: number;
|
|
@@ -78,5 +78,24 @@ export declare function isNonConsumable(purchase: PurchaseInfo): boolean;
|
|
|
78
78
|
* it answers "where are my paying users", which is not a per-kind question.
|
|
79
79
|
*/
|
|
80
80
|
export declare function aggregateActive(subs: readonly SubscriptionInfo[], purchases: readonly PurchaseInfo[], nowMs: number): MetricsActiveResponse;
|
|
81
|
+
/**
|
|
82
|
+
* Subscription half of the active snapshot.
|
|
83
|
+
*
|
|
84
|
+
* Split from the purchase half so the metrics route can take each from its own
|
|
85
|
+
* store's SQL aggregate, or fall back to this, independently — a deployment can
|
|
86
|
+
* have a Postgres subscription store and a custom purchase store.
|
|
87
|
+
*/
|
|
88
|
+
export declare function aggregateActiveSubscriptions(subs: readonly SubscriptionInfo[], nowMs: number): ActiveSubscriptionAggregate;
|
|
89
|
+
/** Purchase half of the active snapshot. */
|
|
90
|
+
export declare function aggregateNonConsumablePurchases(purchases: readonly PurchaseInfo[]): NonConsumablePurchaseAggregate;
|
|
91
|
+
/**
|
|
92
|
+
* Assemble the wire response from the two halves.
|
|
93
|
+
*
|
|
94
|
+
* `byProduct` stays subscriptions-only and `byProductPurchases`
|
|
95
|
+
* non-consumables-only, so a dashboard can show "subscription mix" and "lifetime
|
|
96
|
+
* mix" as separate panels. `byPlatform` deliberately spans both — it answers
|
|
97
|
+
* "where are my paying users", which is not a per-kind question.
|
|
98
|
+
*/
|
|
99
|
+
export declare function composeActiveResponse(subs: ActiveSubscriptionAggregate, purchases: NonConsumablePurchaseAggregate): MetricsActiveResponse;
|
|
81
100
|
export {};
|
|
82
101
|
//# sourceMappingURL=metrics-aggregate.d.ts.map
|
|
@@ -1 +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;
|
|
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;AAExB,OAAO,KAAK,EACV,2BAA2B,EAC3B,qBAAqB,EACrB,8BAA8B,EAC/B,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;GAeG;AAEH,6FAA6F;AAC7F,UAAU,SAAS;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,qBAAqB,CAAC;AAEnD,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,CAKvB;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,SAAS,gBAAgB,EAAE,EACjC,KAAK,EAAE,MAAM,GACZ,2BAA2B,CAe7B;AAED,4CAA4C;AAC5C,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,SAAS,YAAY,EAAE,GACjC,8BAA8B,CAahC;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,2BAA2B,EACjC,SAAS,EAAE,8BAA8B,GACxC,qBAAqB,CAevB"}
|
package/dist/routes/metrics.d.ts
CHANGED
|
@@ -11,12 +11,24 @@ export declare const DEFAULT_METRICS_CACHE_TTL_SECONDS = 30;
|
|
|
11
11
|
* server doesn't currently track; deferred to a follow-up release that adds
|
|
12
12
|
* `config.products: { 'pro_monthly': { price: 9.99, currency: 'USD' } }`.
|
|
13
13
|
*
|
|
14
|
-
* Aggregation strategy
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
14
|
+
* Aggregation strategy, in order of preference:
|
|
15
|
+
*
|
|
16
|
+
* 1. The store's own aggregate (`aggregateActive`, `aggregateStarted`,
|
|
17
|
+
* `aggregateExpired`, `aggregateNonConsumable`) when it has one. Postgres
|
|
18
|
+
* answers with a `GROUP BY`, so the work is bounded by
|
|
19
|
+
* products × platforms × days rather than by row count.
|
|
20
|
+
* 2. Otherwise `listAll()` plus the in-memory reduction in
|
|
21
|
+
* `metrics-aggregate.ts` — the same answer at O(rows), on the event loop.
|
|
22
|
+
* That is the path for the in-memory and Redis stores, which have no
|
|
23
|
+
* server-side grouping to push into, and for custom stores that predate the
|
|
24
|
+
* optional methods.
|
|
25
|
+
*
|
|
26
|
+
* `metrics-aggregate.ts` is the definition of what these numbers mean either
|
|
27
|
+
* way; `postgres-store.test.ts` runs both paths over the same rows and asserts
|
|
28
|
+
* they agree.
|
|
29
|
+
*
|
|
30
|
+
* Responses are additionally cached for `config.metricsCacheTtlSeconds`, which
|
|
31
|
+
* bounds how often either path runs at all — see `metrics-cache.ts`.
|
|
20
32
|
*/
|
|
21
33
|
export declare function createMetricsRouter(config: OneSubServerConfig, store: SubscriptionStore, purchaseStore: PurchaseStore): Router | null;
|
|
22
34
|
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;AAExB,OAAO,KAAK,
|
|
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,EAAqB,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAiBvF,4DAA4D;AAC5D,eAAO,MAAM,iCAAiC,KAAK,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,iBAAiB,EACxB,aAAa,EAAE,aAAa,GAC3B,MAAM,GAAG,IAAI,CAuMf;AAED,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,CAAC"}
|
package/dist/store.d.ts
CHANGED
|
@@ -18,6 +18,41 @@ export interface ListFilteredResult {
|
|
|
18
18
|
limit: number;
|
|
19
19
|
offset: number;
|
|
20
20
|
}
|
|
21
|
+
/** One day of a daily series. `date` is a UTC `YYYY-MM-DD`. */
|
|
22
|
+
export interface MetricsDayBucket {
|
|
23
|
+
date: string;
|
|
24
|
+
count: number;
|
|
25
|
+
}
|
|
26
|
+
/** Window to aggregate over. Both bounds are inclusive. */
|
|
27
|
+
export interface MetricsRangeQuery {
|
|
28
|
+
from: Date;
|
|
29
|
+
to: Date;
|
|
30
|
+
/** `'day'` additionally fills a zero-filled bucket per UTC day in the window. */
|
|
31
|
+
groupBy: 'none' | 'day';
|
|
32
|
+
}
|
|
33
|
+
/** Counts for a windowed query. */
|
|
34
|
+
export interface MetricsRangeAggregate {
|
|
35
|
+
total: number;
|
|
36
|
+
byProduct: Record<string, number>;
|
|
37
|
+
byPlatform: Record<string, number>;
|
|
38
|
+
/** Present only for `groupBy: 'day'`; zero-filled across the whole window. */
|
|
39
|
+
buckets?: MetricsDayBucket[];
|
|
40
|
+
}
|
|
41
|
+
/** Point-in-time counts for subscriptions that currently grant entitlement. */
|
|
42
|
+
export interface ActiveSubscriptionAggregate {
|
|
43
|
+
/** status active|grace_period AND not yet expired. */
|
|
44
|
+
active: number;
|
|
45
|
+
/** The grace_period subset of `active` — the at-risk cohort. */
|
|
46
|
+
gracePeriod: number;
|
|
47
|
+
byProduct: Record<string, number>;
|
|
48
|
+
byPlatform: Record<string, number>;
|
|
49
|
+
}
|
|
50
|
+
/** Counts for non-consumable purchases (lifetime products). */
|
|
51
|
+
export interface NonConsumablePurchaseAggregate {
|
|
52
|
+
total: number;
|
|
53
|
+
byProduct: Record<string, number>;
|
|
54
|
+
byPlatform: Record<string, number>;
|
|
55
|
+
}
|
|
21
56
|
/**
|
|
22
57
|
* Pluggable subscription store interface.
|
|
23
58
|
* Default is in-memory. Replace with a PostgreSQL/Redis implementation
|
|
@@ -59,6 +94,24 @@ export interface SubscriptionStore {
|
|
|
59
94
|
* the in-memory store and `updated_at DESC` for Postgres/Redis.
|
|
60
95
|
*/
|
|
61
96
|
getAllByUserId(userId: string): Promise<SubscriptionInfo[]>;
|
|
97
|
+
/**
|
|
98
|
+
* OPTIONAL. Counts of subscriptions currently granting entitlement, as of
|
|
99
|
+
* `now`. Equivalent to `aggregateActiveSubscriptions(await listAll(), now)`.
|
|
100
|
+
*/
|
|
101
|
+
aggregateActive?(now: Date): Promise<ActiveSubscriptionAggregate>;
|
|
102
|
+
/**
|
|
103
|
+
* OPTIONAL. Subscriptions whose `purchasedAt` falls in the window, whatever
|
|
104
|
+
* their current status — a cohort-start count. Equivalent to
|
|
105
|
+
* `aggregateRange(await listAll(), { anchor: purchasedAt, ... })`.
|
|
106
|
+
*/
|
|
107
|
+
aggregateStarted?(query: MetricsRangeQuery): Promise<MetricsRangeAggregate>;
|
|
108
|
+
/**
|
|
109
|
+
* OPTIONAL. Subscriptions that are currently expired or canceled AND whose
|
|
110
|
+
* `expiresAt` falls in the window. A still-active record does not count even
|
|
111
|
+
* if its expiry lands inside the window. Equivalent to
|
|
112
|
+
* `aggregateRange(await listAll(), { anchor: expiresAt, include: isEnded })`.
|
|
113
|
+
*/
|
|
114
|
+
aggregateExpired?(query: MetricsRangeQuery): Promise<MetricsRangeAggregate>;
|
|
62
115
|
}
|
|
63
116
|
/**
|
|
64
117
|
* In-memory implementation — suitable for development and testing.
|
|
@@ -129,6 +182,18 @@ export interface PurchaseStore {
|
|
|
129
182
|
* Returns true if a row was updated, false if the transactionId was not found.
|
|
130
183
|
*/
|
|
131
184
|
reassignPurchase(transactionId: string, newUserId: string): Promise<boolean>;
|
|
185
|
+
/**
|
|
186
|
+
* OPTIONAL. Counts of non-consumable purchases. Consumables are excluded —
|
|
187
|
+
* they grant a spent resource, not an ongoing right. Equivalent to
|
|
188
|
+
* `aggregateNonConsumablePurchases(await listAll())`.
|
|
189
|
+
*/
|
|
190
|
+
aggregateNonConsumable?(): Promise<NonConsumablePurchaseAggregate>;
|
|
191
|
+
/**
|
|
192
|
+
* OPTIONAL. Non-consumable purchases whose `purchasedAt` falls in the window.
|
|
193
|
+
* Equivalent to
|
|
194
|
+
* `aggregateRange(await listAll(), { anchor: purchasedAt, include: isNonConsumable })`.
|
|
195
|
+
*/
|
|
196
|
+
aggregateStarted?(query: MetricsRangeQuery): Promise<MetricsRangeAggregate>;
|
|
132
197
|
}
|
|
133
198
|
/**
|
|
134
199
|
* In-memory implementation of PurchaseStore — suitable for development and testing.
|
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;;;;;;;;;OASG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SubscriptionInfo, PurchaseInfo } from '@onesub/shared';
|
|
2
|
-
import type { SubscriptionStore, PurchaseStore, ListFilteredOptions, ListFilteredResult } from '../store.js';
|
|
2
|
+
import type { SubscriptionStore, PurchaseStore, ListFilteredOptions, ListFilteredResult, ActiveSubscriptionAggregate, MetricsRangeAggregate, MetricsRangeQuery, NonConsumablePurchaseAggregate } from '../store.js';
|
|
3
3
|
/**
|
|
4
4
|
* PostgreSQL-backed subscription store.
|
|
5
5
|
*
|
|
@@ -52,6 +52,22 @@ export declare class PostgresSubscriptionStore implements SubscriptionStore {
|
|
|
52
52
|
getByTransactionId(txId: string): Promise<SubscriptionInfo | null>;
|
|
53
53
|
listAll(): Promise<SubscriptionInfo[]>;
|
|
54
54
|
listFiltered(opts: ListFilteredOptions): Promise<ListFilteredResult>;
|
|
55
|
+
/**
|
|
56
|
+
* Counts of subscriptions currently granting entitlement.
|
|
57
|
+
*
|
|
58
|
+
* One `GROUP BY` instead of reading every row into the process. Grouping by
|
|
59
|
+
* status as well as product/platform is what lets the grace-period subset come
|
|
60
|
+
* out of the same query.
|
|
61
|
+
*/
|
|
62
|
+
aggregateActive(now: Date): Promise<ActiveSubscriptionAggregate>;
|
|
63
|
+
/** Cohort-start counts: subscriptions whose `purchased_at` is in the window. */
|
|
64
|
+
aggregateStarted(query: MetricsRangeQuery): Promise<MetricsRangeAggregate>;
|
|
65
|
+
/**
|
|
66
|
+
* Subscriptions that have ended AND whose `expires_at` is in the window. A
|
|
67
|
+
* still-active record does not count even if its expiry lands inside it — see
|
|
68
|
+
* `isEndedSubscription`, which this mirrors.
|
|
69
|
+
*/
|
|
70
|
+
aggregateExpired(query: MetricsRangeQuery): Promise<MetricsRangeAggregate>;
|
|
55
71
|
/** Gracefully close the underlying connection pool. */
|
|
56
72
|
close(): Promise<void>;
|
|
57
73
|
}
|
|
@@ -96,6 +112,10 @@ export declare class PostgresPurchaseStore implements PurchaseStore {
|
|
|
96
112
|
deletePurchases(userId: string, productId: string): Promise<number>;
|
|
97
113
|
deletePurchaseByTransactionId(transactionId: string): Promise<boolean>;
|
|
98
114
|
listAll(): Promise<PurchaseInfo[]>;
|
|
115
|
+
/** Counts of non-consumable purchases. Consumables are excluded. */
|
|
116
|
+
aggregateNonConsumable(): Promise<NonConsumablePurchaseAggregate>;
|
|
117
|
+
/** Non-consumable purchases whose `purchased_at` is in the window. */
|
|
118
|
+
aggregateStarted(query: MetricsRangeQuery): Promise<MetricsRangeAggregate>;
|
|
99
119
|
/** Gracefully close the underlying connection pool. */
|
|
100
120
|
close(): Promise<void>;
|
|
101
121
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/stores/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/stores/postgres.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.23.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",
|
|
@@ -88,6 +88,7 @@
|
|
|
88
88
|
"@types/pg": "^8.20.0",
|
|
89
89
|
"express": "^5.2.1",
|
|
90
90
|
"ioredis-mock": "^8.9.0",
|
|
91
|
+
"pg": "^8.22.0",
|
|
91
92
|
"size-limit": "^12.1.0",
|
|
92
93
|
"tsup": "^8.3.5",
|
|
93
94
|
"typescript": "^5.7.0"
|