@sudobility/consumables_service 0.0.6 → 0.0.7
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/helpers/ConsumablesHelper.d.ts +14 -1
- package/dist/helpers/ConsumablesHelper.d.ts.map +1 -1
- package/dist/helpers/ConsumablesHelper.js +59 -28
- package/dist/helpers/ConsumablesHelper.js.map +1 -1
- package/dist/helpers/WebhookHelper.d.ts +1 -0
- package/dist/helpers/WebhookHelper.d.ts.map +1 -1
- package/dist/helpers/WebhookHelper.js +8 -2
- package/dist/helpers/WebhookHelper.js.map +1 -1
- package/dist/helpers/index.d.ts +1 -1
- package/dist/helpers/index.d.ts.map +1 -1
- package/dist/helpers/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -6,6 +6,17 @@
|
|
|
6
6
|
import type { ConsumablesSchemaResult } from "../schema";
|
|
7
7
|
import type { ConsumableBalanceResponse, ConsumablePurchaseRequest, ConsumableSource, ConsumableUseResponse } from "@sudobility/types";
|
|
8
8
|
import type { ConsumablePurchase, ConsumableUsage, ConsumablesConfig } from "../types";
|
|
9
|
+
/**
|
|
10
|
+
* Minimal typed interface for the Drizzle database instance.
|
|
11
|
+
* Keeps `any` return types to avoid drizzle-orm version coupling,
|
|
12
|
+
* while ensuring the db object has the expected shape.
|
|
13
|
+
*/
|
|
14
|
+
export interface DrizzleDb {
|
|
15
|
+
select: (...args: any[]) => any;
|
|
16
|
+
insert: (...args: any[]) => any;
|
|
17
|
+
update: (...args: any[]) => any;
|
|
18
|
+
transaction: <T>(fn: (tx: any) => Promise<T>) => Promise<T>;
|
|
19
|
+
}
|
|
9
20
|
/**
|
|
10
21
|
* Core helper class for managing consumable credits.
|
|
11
22
|
* Handles balance get-or-create, purchase recording with atomic increments,
|
|
@@ -15,7 +26,7 @@ export declare class ConsumablesHelper {
|
|
|
15
26
|
private db;
|
|
16
27
|
private tables;
|
|
17
28
|
private config;
|
|
18
|
-
constructor(db:
|
|
29
|
+
constructor(db: DrizzleDb, tables: ConsumablesSchemaResult, config: ConsumablesConfig);
|
|
19
30
|
/**
|
|
20
31
|
* Gets or creates a balance record for the given user.
|
|
21
32
|
* On first access, grants initialFreeCredits and records a "free" purchase audit entry.
|
|
@@ -26,9 +37,11 @@ export declare class ConsumablesHelper {
|
|
|
26
37
|
/**
|
|
27
38
|
* Records a purchase and atomically increments the user's credit balance.
|
|
28
39
|
* Ensures the balance row exists via getBalance() before updating.
|
|
40
|
+
* Wrapped in a database transaction for consistency.
|
|
29
41
|
* @param userId - The user's unique identifier.
|
|
30
42
|
* @param request - Purchase details including credits, source, and optional metadata.
|
|
31
43
|
* @returns The updated balance after the purchase.
|
|
44
|
+
* @throws Error if credits is not a positive integer or source is invalid.
|
|
32
45
|
*/
|
|
33
46
|
recordPurchase(userId: string, request: ConsumablePurchaseRequest): Promise<ConsumableBalanceResponse>;
|
|
34
47
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConsumablesHelper.d.ts","sourceRoot":"","sources":["../../src/helpers/ConsumablesHelper.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,KAAK,EACV,yBAAyB,EACzB,yBAAyB,EACzB,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EACV,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EAClB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"ConsumablesHelper.d.ts","sourceRoot":"","sources":["../../src/helpers/ConsumablesHelper.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,KAAK,EACV,yBAAyB,EACzB,yBAAyB,EACzB,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EACV,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAKlB;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC;IAChC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC;IAChC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC;IAChC,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CAC7D;AAED;;;;GAIG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,EAAE,CAAY;IACtB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,MAAM,CAAoB;gBAGhC,EAAE,EAAE,SAAS,EACb,MAAM,EAAE,uBAAuB,EAC/B,MAAM,EAAE,iBAAiB;IAO3B;;;;;OAKG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC;IAkCpE;;;;;;;;OAQG;IACG,cAAc,CAClB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,yBAAyB,CAAC;IAoErC;;;;;;OAMG;IACG,WAAW,CACf,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,qBAAqB,CAAC;IA8BjC;;;;;;OAMG;IACG,kBAAkB,CACtB,MAAM,EAAE,MAAM,EACd,KAAK,SAAK,EACV,MAAM,SAAI,GACT,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAWhC;;;;;;OAMG;IACG,eAAe,CACnB,MAAM,EAAE,MAAM,EACd,KAAK,SAAK,EACV,MAAM,SAAI,GACT,OAAO,CAAC,eAAe,EAAE,CAAC;IAW7B;;;;;;;;;;;;OAYG;IACG,yBAAyB,CAC7B,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,gBAAgB,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAyB3D"}
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* idempotent webhook processing with atomic database operations.
|
|
5
5
|
*/
|
|
6
6
|
import { eq, desc, sql } from "drizzle-orm";
|
|
7
|
+
/** Valid ConsumableSource values for input validation. */
|
|
8
|
+
const VALID_SOURCES = ["web", "apple", "google", "free"];
|
|
7
9
|
/**
|
|
8
10
|
* Core helper class for managing consumable credits.
|
|
9
11
|
* Handles balance get-or-create, purchase recording with atomic increments,
|
|
@@ -53,41 +55,70 @@ export class ConsumablesHelper {
|
|
|
53
55
|
/**
|
|
54
56
|
* Records a purchase and atomically increments the user's credit balance.
|
|
55
57
|
* Ensures the balance row exists via getBalance() before updating.
|
|
58
|
+
* Wrapped in a database transaction for consistency.
|
|
56
59
|
* @param userId - The user's unique identifier.
|
|
57
60
|
* @param request - Purchase details including credits, source, and optional metadata.
|
|
58
61
|
* @returns The updated balance after the purchase.
|
|
62
|
+
* @throws Error if credits is not a positive integer or source is invalid.
|
|
59
63
|
*/
|
|
60
64
|
async recordPurchase(userId, request) {
|
|
65
|
+
// Validate inputs
|
|
66
|
+
if (!Number.isInteger(request.credits) || request.credits <= 0) {
|
|
67
|
+
throw new Error("credits must be a positive integer");
|
|
68
|
+
}
|
|
69
|
+
if (!VALID_SOURCES.includes(request.source)) {
|
|
70
|
+
throw new Error("source must be one of: web, apple, google, free");
|
|
71
|
+
}
|
|
61
72
|
const { consumableBalances, consumablePurchases } = this.tables;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
return this.db.transaction(async (tx) => {
|
|
74
|
+
// Ensure balance record exists (idempotent) — uses tx for reads/writes
|
|
75
|
+
const existing = await tx
|
|
76
|
+
.select()
|
|
77
|
+
.from(consumableBalances)
|
|
78
|
+
.where(eq(consumableBalances.user_id, userId));
|
|
79
|
+
if (existing.length === 0) {
|
|
80
|
+
const freeCredits = this.config.initialFreeCredits;
|
|
81
|
+
await tx.insert(consumableBalances).values({
|
|
82
|
+
user_id: userId,
|
|
83
|
+
balance: freeCredits,
|
|
84
|
+
initial_credits: freeCredits,
|
|
85
|
+
});
|
|
86
|
+
if (freeCredits > 0) {
|
|
87
|
+
await tx.insert(consumablePurchases).values({
|
|
88
|
+
user_id: userId,
|
|
89
|
+
credits: freeCredits,
|
|
90
|
+
source: "free",
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Insert purchase record
|
|
95
|
+
await tx.insert(consumablePurchases).values({
|
|
96
|
+
user_id: userId,
|
|
97
|
+
credits: request.credits,
|
|
98
|
+
source: request.source,
|
|
99
|
+
transaction_ref_id: request.transaction_ref_id ?? null,
|
|
100
|
+
product_id: request.product_id ?? null,
|
|
101
|
+
price_cents: request.price_cents ?? null,
|
|
102
|
+
currency: request.currency ?? null,
|
|
103
|
+
});
|
|
104
|
+
// Atomically increment balance
|
|
105
|
+
await tx
|
|
106
|
+
.update(consumableBalances)
|
|
107
|
+
.set({
|
|
108
|
+
balance: sql `${consumableBalances.balance} + ${request.credits}`,
|
|
109
|
+
updated_at: new Date(),
|
|
110
|
+
})
|
|
111
|
+
.where(eq(consumableBalances.user_id, userId));
|
|
112
|
+
// Return updated balance
|
|
113
|
+
const updated = await tx
|
|
114
|
+
.select()
|
|
115
|
+
.from(consumableBalances)
|
|
116
|
+
.where(eq(consumableBalances.user_id, userId));
|
|
117
|
+
return {
|
|
118
|
+
balance: updated[0].balance,
|
|
119
|
+
initial_credits: updated[0].initial_credits,
|
|
120
|
+
};
|
|
73
121
|
});
|
|
74
|
-
// Atomically increment balance
|
|
75
|
-
await this.db
|
|
76
|
-
.update(consumableBalances)
|
|
77
|
-
.set({
|
|
78
|
-
balance: sql `${consumableBalances.balance} + ${request.credits}`,
|
|
79
|
-
updated_at: new Date(),
|
|
80
|
-
})
|
|
81
|
-
.where(eq(consumableBalances.user_id, userId));
|
|
82
|
-
// Return updated balance
|
|
83
|
-
const updated = await this.db
|
|
84
|
-
.select()
|
|
85
|
-
.from(consumableBalances)
|
|
86
|
-
.where(eq(consumableBalances.user_id, userId));
|
|
87
|
-
return {
|
|
88
|
-
balance: updated[0].balance,
|
|
89
|
-
initial_credits: updated[0].initial_credits,
|
|
90
|
-
};
|
|
91
122
|
}
|
|
92
123
|
/**
|
|
93
124
|
* Records a credit usage and atomically decrements the balance by 1.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConsumablesHelper.js","sourceRoot":"","sources":["../../src/helpers/ConsumablesHelper.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAc5C;;;;GAIG;AACH,MAAM,OAAO,iBAAiB;IAK5B,YACE,
|
|
1
|
+
{"version":3,"file":"ConsumablesHelper.js","sourceRoot":"","sources":["../../src/helpers/ConsumablesHelper.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import type { RevenueCatWebhookEvent } from "../types";
|
|
7
7
|
/**
|
|
8
8
|
* Validates a RevenueCat webhook HMAC-SHA256 signature.
|
|
9
|
+
* Uses timing-safe comparison to prevent timing attacks.
|
|
9
10
|
* @param rawBody - The raw request body string.
|
|
10
11
|
* @param signature - The signature from the webhook header.
|
|
11
12
|
* @param secret - The shared webhook secret.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebhookHelper.d.ts","sourceRoot":"","sources":["../../src/helpers/WebhookHelper.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAEvD
|
|
1
|
+
{"version":3,"file":"WebhookHelper.d.ts","sourceRoot":"","sources":["../../src/helpers/WebhookHelper.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAEvD;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAaT;AAQD;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,sBAAsB,GAC5B;IACD,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,IAAI,CAeP"}
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* Handles HMAC-SHA256 signature verification and extraction of purchase data
|
|
4
4
|
* from webhook payloads.
|
|
5
5
|
*/
|
|
6
|
-
import { createHmac } from "crypto";
|
|
6
|
+
import { createHmac, timingSafeEqual } from "crypto";
|
|
7
7
|
/**
|
|
8
8
|
* Validates a RevenueCat webhook HMAC-SHA256 signature.
|
|
9
|
+
* Uses timing-safe comparison to prevent timing attacks.
|
|
9
10
|
* @param rawBody - The raw request body string.
|
|
10
11
|
* @param signature - The signature from the webhook header.
|
|
11
12
|
* @param secret - The shared webhook secret.
|
|
@@ -15,7 +16,12 @@ export function validateWebhookSignature(rawBody, signature, secret) {
|
|
|
15
16
|
const hmac = createHmac("sha256", secret);
|
|
16
17
|
hmac.update(rawBody);
|
|
17
18
|
const expected = hmac.digest("hex");
|
|
18
|
-
|
|
19
|
+
const expectedBuffer = Buffer.from(expected, "hex");
|
|
20
|
+
const signatureBuffer = Buffer.from(signature, "hex");
|
|
21
|
+
if (expectedBuffer.length !== signatureBuffer.length) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
return timingSafeEqual(expectedBuffer, signatureBuffer);
|
|
19
25
|
}
|
|
20
26
|
const STORE_TO_SOURCE = {
|
|
21
27
|
STRIPE: "web",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebhookHelper.js","sourceRoot":"","sources":["../../src/helpers/WebhookHelper.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"WebhookHelper.js","sourceRoot":"","sources":["../../src/helpers/WebhookHelper.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAGrD;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CACtC,OAAe,EACf,SAAiB,EACjB,MAAc;IAEd,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACrB,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAEpC,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACpD,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAEtD,IAAI,cAAc,CAAC,MAAM,KAAK,eAAe,CAAC,MAAM,EAAE,CAAC;QACrD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,eAAe,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,eAAe,GAA2B;IAC9C,MAAM,EAAE,KAAK;IACb,SAAS,EAAE,OAAO;IAClB,UAAU,EAAE,QAAQ;CACrB,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,4BAA4B,CAC1C,KAA6B;IAS7B,MAAM,UAAU,GAAG,CAAC,uBAAuB,EAAE,kBAAkB,CAAC,CAAC;IAEjE,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW;QAC/B,aAAa,EAAE,KAAK,CAAC,KAAK,CAAC,cAAc;QACzC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,UAAU;QACjC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,2BAA2B,GAAG,GAAG,CAAC;QACrE,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,QAAQ;QAC9B,KAAK,EAAE,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK;KAC/D,CAAC;AACJ,CAAC"}
|
package/dist/helpers/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/helpers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/helpers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,iBAAiB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/helpers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/helpers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAkB,MAAM,qBAAqB,CAAC;AACxE,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,iBAAiB,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export type { ConsumableBalance, ConsumablePurchase, ConsumableUsage, RevenueCatWebhookEvent, ConsumablesConfig, } from "./types";
|
|
2
2
|
export { createConsumablesSchema, type ConsumablesSchemaResult, } from "./schema";
|
|
3
|
-
export { ConsumablesHelper, validateWebhookSignature, parseConsumablePurchaseEvent, } from "./helpers";
|
|
3
|
+
export { ConsumablesHelper, type DrizzleDb, validateWebhookSignature, parseConsumablePurchaseEvent, } from "./helpers";
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,uBAAuB,EACvB,KAAK,uBAAuB,GAC7B,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,uBAAuB,EACvB,KAAK,uBAAuB,GAC7B,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,iBAAiB,EACjB,KAAK,SAAS,EACd,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,WAAW,CAAC"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,SAAS;AACT,OAAO,EACL,uBAAuB,GAExB,MAAM,UAAU,CAAC;AAElB,UAAU;AACV,OAAO,EACL,iBAAiB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,SAAS;AACT,OAAO,EACL,uBAAuB,GAExB,MAAM,UAAU,CAAC;AAElB,UAAU;AACV,OAAO,EACL,iBAAiB,EAEjB,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,WAAW,CAAC"}
|