@ar-agents/mercadopago 0.7.0 → 0.9.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/CHANGELOG.md +125 -0
- package/README.md +162 -2
- package/cookbook/01-checkout-pro-basic.ts +99 -0
- package/cookbook/02-saas-subscription.ts +137 -0
- package/cookbook/03-webhook-handler.ts +162 -0
- package/cookbook/04-marketplace-split.ts +194 -0
- package/cookbook/05-qr-in-store.ts +142 -0
- package/cookbook/06-3ds-challenge.ts +139 -0
- package/cookbook/07-auth-only-order.ts +127 -0
- package/cookbook/08-recovery-patterns.ts +191 -0
- package/cookbook/README.md +36 -0
- package/dist/index.cjs +407 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +278 -50
- package/dist/index.d.ts +278 -50
- package/dist/index.js +404 -35
- package/dist/index.js.map +1 -1
- package/dist/state-C6Wzb_XX.d.cts +106 -0
- package/dist/state-C6Wzb_XX.d.ts +106 -0
- package/dist/vercel-kv.cjs +92 -0
- package/dist/vercel-kv.cjs.map +1 -0
- package/dist/vercel-kv.d.cts +107 -0
- package/dist/vercel-kv.d.ts +107 -0
- package/dist/vercel-kv.js +88 -0
- package/dist/vercel-kv.js.map +1 -0
- package/package.json +32 -3
- package/tools.manifest.json +1 -1
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory record of a subscription. The lib persists the MP-side fields
|
|
3
|
+
* needed to reason about a subscription without hitting the API every time
|
|
4
|
+
* (status, last webhook info, customer email, etc.) plus a free-form metadata
|
|
5
|
+
* bag for callers to attach business context (tenant id, plan name, etc.).
|
|
6
|
+
*/
|
|
7
|
+
interface SubscriptionStateRecord {
|
|
8
|
+
status?: string;
|
|
9
|
+
payerEmail?: string;
|
|
10
|
+
amount?: number;
|
|
11
|
+
currency?: string;
|
|
12
|
+
frequency?: number;
|
|
13
|
+
frequencyType?: string;
|
|
14
|
+
initPoint?: string;
|
|
15
|
+
externalReference?: string;
|
|
16
|
+
createdAt?: string;
|
|
17
|
+
cancelledAt?: string;
|
|
18
|
+
lastWebhookStatus?: string;
|
|
19
|
+
lastWebhookAt?: string;
|
|
20
|
+
metadata?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Persistence surface for subscription state. Implementations may back this
|
|
24
|
+
* with Upstash Redis, Vercel KV, Postgres, in-memory, or anything that
|
|
25
|
+
* supports the three operations. The default `InMemoryStateAdapter` is
|
|
26
|
+
* provided for tests and trivial single-process deployments; production
|
|
27
|
+
* setups should plug in a durable store.
|
|
28
|
+
*/
|
|
29
|
+
interface SubscriptionStateAdapter {
|
|
30
|
+
set(id: string, state: Partial<SubscriptionStateRecord>): Promise<void>;
|
|
31
|
+
get(id: string): Promise<SubscriptionStateRecord | null>;
|
|
32
|
+
list?(): Promise<string[]>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Volatile, single-process state adapter. Useful for tests and demos. Do not
|
|
36
|
+
* use in production: state is lost on restart and is not safe across tenants.
|
|
37
|
+
*/
|
|
38
|
+
declare class InMemoryStateAdapter implements SubscriptionStateAdapter {
|
|
39
|
+
private readonly store;
|
|
40
|
+
set(id: string, state: Partial<SubscriptionStateRecord>): Promise<void>;
|
|
41
|
+
get(id: string): Promise<SubscriptionStateRecord | null>;
|
|
42
|
+
list(): Promise<string[]>;
|
|
43
|
+
/** Test helper: drop everything. Not part of the adapter interface. */
|
|
44
|
+
reset(): void;
|
|
45
|
+
}
|
|
46
|
+
interface OAuthTokenRecord {
|
|
47
|
+
user_id: string;
|
|
48
|
+
access_token: string;
|
|
49
|
+
refresh_token: string;
|
|
50
|
+
/** Unix-ms timestamp when access_token expires. */
|
|
51
|
+
expires_at: number;
|
|
52
|
+
/** OAuth scope granted, if any. */
|
|
53
|
+
scope?: string;
|
|
54
|
+
/** Optional: any business metadata you want to attach (tenant id, etc.). */
|
|
55
|
+
metadata?: Record<string, unknown>;
|
|
56
|
+
}
|
|
57
|
+
interface OAuthTokenStore {
|
|
58
|
+
/** Persist (or update) the token for `user_id`. */
|
|
59
|
+
set(userId: string, token: OAuthTokenRecord): Promise<void>;
|
|
60
|
+
/** Fetch the stored token, or null if no token registered for that seller. */
|
|
61
|
+
get(userId: string): Promise<OAuthTokenRecord | null>;
|
|
62
|
+
/** Forget a seller's token (e.g., they revoked the app). */
|
|
63
|
+
delete(userId: string): Promise<void>;
|
|
64
|
+
/** Optional: enumerate all sellers (useful for batch refresh jobs). */
|
|
65
|
+
list?(): Promise<string[]>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Volatile, single-process OAuth token store. NOT for production marketplace
|
|
69
|
+
* setups — tokens are lost on restart. Plug in `VercelKVOAuthTokenStore`
|
|
70
|
+
* (from `@ar-agents/mercadopago/vercel-kv`) or your own Postgres-backed
|
|
71
|
+
* implementation.
|
|
72
|
+
*/
|
|
73
|
+
declare class InMemoryOAuthTokenStore implements OAuthTokenStore {
|
|
74
|
+
private readonly store;
|
|
75
|
+
set(userId: string, token: OAuthTokenRecord): Promise<void>;
|
|
76
|
+
get(userId: string): Promise<OAuthTokenRecord | null>;
|
|
77
|
+
delete(userId: string): Promise<void>;
|
|
78
|
+
list(): Promise<string[]>;
|
|
79
|
+
/** Test helper. */
|
|
80
|
+
reset(): void;
|
|
81
|
+
}
|
|
82
|
+
interface IdempotencyCache {
|
|
83
|
+
/** Get the cached response for a key, or null if not present / expired. */
|
|
84
|
+
get<T>(key: string): Promise<T | null>;
|
|
85
|
+
/**
|
|
86
|
+
* Store a response under `key`. `ttlSeconds` defaults to 24h — match MP's
|
|
87
|
+
* own idempotency window so the cache becomes irrelevant once MP would
|
|
88
|
+
* forget anyway.
|
|
89
|
+
*/
|
|
90
|
+
set<T>(key: string, value: T, ttlSeconds?: number): Promise<void>;
|
|
91
|
+
/** Forget a cache entry (force a re-fetch on next call). */
|
|
92
|
+
delete(key: string): Promise<void>;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Volatile, single-process idempotency cache. Tests + dev only.
|
|
96
|
+
*/
|
|
97
|
+
declare class InMemoryIdempotencyCache implements IdempotencyCache {
|
|
98
|
+
private readonly store;
|
|
99
|
+
get<T>(key: string): Promise<T | null>;
|
|
100
|
+
set<T>(key: string, value: T, ttlSeconds?: number): Promise<void>;
|
|
101
|
+
delete(key: string): Promise<void>;
|
|
102
|
+
/** Test helper. */
|
|
103
|
+
reset(): void;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export { type IdempotencyCache as I, type OAuthTokenRecord as O, type SubscriptionStateAdapter as S, InMemoryIdempotencyCache as a, InMemoryOAuthTokenStore as b, InMemoryStateAdapter as c, type OAuthTokenStore as d, type SubscriptionStateRecord as e };
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory record of a subscription. The lib persists the MP-side fields
|
|
3
|
+
* needed to reason about a subscription without hitting the API every time
|
|
4
|
+
* (status, last webhook info, customer email, etc.) plus a free-form metadata
|
|
5
|
+
* bag for callers to attach business context (tenant id, plan name, etc.).
|
|
6
|
+
*/
|
|
7
|
+
interface SubscriptionStateRecord {
|
|
8
|
+
status?: string;
|
|
9
|
+
payerEmail?: string;
|
|
10
|
+
amount?: number;
|
|
11
|
+
currency?: string;
|
|
12
|
+
frequency?: number;
|
|
13
|
+
frequencyType?: string;
|
|
14
|
+
initPoint?: string;
|
|
15
|
+
externalReference?: string;
|
|
16
|
+
createdAt?: string;
|
|
17
|
+
cancelledAt?: string;
|
|
18
|
+
lastWebhookStatus?: string;
|
|
19
|
+
lastWebhookAt?: string;
|
|
20
|
+
metadata?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Persistence surface for subscription state. Implementations may back this
|
|
24
|
+
* with Upstash Redis, Vercel KV, Postgres, in-memory, or anything that
|
|
25
|
+
* supports the three operations. The default `InMemoryStateAdapter` is
|
|
26
|
+
* provided for tests and trivial single-process deployments; production
|
|
27
|
+
* setups should plug in a durable store.
|
|
28
|
+
*/
|
|
29
|
+
interface SubscriptionStateAdapter {
|
|
30
|
+
set(id: string, state: Partial<SubscriptionStateRecord>): Promise<void>;
|
|
31
|
+
get(id: string): Promise<SubscriptionStateRecord | null>;
|
|
32
|
+
list?(): Promise<string[]>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Volatile, single-process state adapter. Useful for tests and demos. Do not
|
|
36
|
+
* use in production: state is lost on restart and is not safe across tenants.
|
|
37
|
+
*/
|
|
38
|
+
declare class InMemoryStateAdapter implements SubscriptionStateAdapter {
|
|
39
|
+
private readonly store;
|
|
40
|
+
set(id: string, state: Partial<SubscriptionStateRecord>): Promise<void>;
|
|
41
|
+
get(id: string): Promise<SubscriptionStateRecord | null>;
|
|
42
|
+
list(): Promise<string[]>;
|
|
43
|
+
/** Test helper: drop everything. Not part of the adapter interface. */
|
|
44
|
+
reset(): void;
|
|
45
|
+
}
|
|
46
|
+
interface OAuthTokenRecord {
|
|
47
|
+
user_id: string;
|
|
48
|
+
access_token: string;
|
|
49
|
+
refresh_token: string;
|
|
50
|
+
/** Unix-ms timestamp when access_token expires. */
|
|
51
|
+
expires_at: number;
|
|
52
|
+
/** OAuth scope granted, if any. */
|
|
53
|
+
scope?: string;
|
|
54
|
+
/** Optional: any business metadata you want to attach (tenant id, etc.). */
|
|
55
|
+
metadata?: Record<string, unknown>;
|
|
56
|
+
}
|
|
57
|
+
interface OAuthTokenStore {
|
|
58
|
+
/** Persist (or update) the token for `user_id`. */
|
|
59
|
+
set(userId: string, token: OAuthTokenRecord): Promise<void>;
|
|
60
|
+
/** Fetch the stored token, or null if no token registered for that seller. */
|
|
61
|
+
get(userId: string): Promise<OAuthTokenRecord | null>;
|
|
62
|
+
/** Forget a seller's token (e.g., they revoked the app). */
|
|
63
|
+
delete(userId: string): Promise<void>;
|
|
64
|
+
/** Optional: enumerate all sellers (useful for batch refresh jobs). */
|
|
65
|
+
list?(): Promise<string[]>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Volatile, single-process OAuth token store. NOT for production marketplace
|
|
69
|
+
* setups — tokens are lost on restart. Plug in `VercelKVOAuthTokenStore`
|
|
70
|
+
* (from `@ar-agents/mercadopago/vercel-kv`) or your own Postgres-backed
|
|
71
|
+
* implementation.
|
|
72
|
+
*/
|
|
73
|
+
declare class InMemoryOAuthTokenStore implements OAuthTokenStore {
|
|
74
|
+
private readonly store;
|
|
75
|
+
set(userId: string, token: OAuthTokenRecord): Promise<void>;
|
|
76
|
+
get(userId: string): Promise<OAuthTokenRecord | null>;
|
|
77
|
+
delete(userId: string): Promise<void>;
|
|
78
|
+
list(): Promise<string[]>;
|
|
79
|
+
/** Test helper. */
|
|
80
|
+
reset(): void;
|
|
81
|
+
}
|
|
82
|
+
interface IdempotencyCache {
|
|
83
|
+
/** Get the cached response for a key, or null if not present / expired. */
|
|
84
|
+
get<T>(key: string): Promise<T | null>;
|
|
85
|
+
/**
|
|
86
|
+
* Store a response under `key`. `ttlSeconds` defaults to 24h — match MP's
|
|
87
|
+
* own idempotency window so the cache becomes irrelevant once MP would
|
|
88
|
+
* forget anyway.
|
|
89
|
+
*/
|
|
90
|
+
set<T>(key: string, value: T, ttlSeconds?: number): Promise<void>;
|
|
91
|
+
/** Forget a cache entry (force a re-fetch on next call). */
|
|
92
|
+
delete(key: string): Promise<void>;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Volatile, single-process idempotency cache. Tests + dev only.
|
|
96
|
+
*/
|
|
97
|
+
declare class InMemoryIdempotencyCache implements IdempotencyCache {
|
|
98
|
+
private readonly store;
|
|
99
|
+
get<T>(key: string): Promise<T | null>;
|
|
100
|
+
set<T>(key: string, value: T, ttlSeconds?: number): Promise<void>;
|
|
101
|
+
delete(key: string): Promise<void>;
|
|
102
|
+
/** Test helper. */
|
|
103
|
+
reset(): void;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export { type IdempotencyCache as I, type OAuthTokenRecord as O, type SubscriptionStateAdapter as S, InMemoryIdempotencyCache as a, InMemoryOAuthTokenStore as b, InMemoryStateAdapter as c, type OAuthTokenStore as d, type SubscriptionStateRecord as e };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var kv = require('@vercel/kv');
|
|
4
|
+
|
|
5
|
+
// src/vercel-kv.ts
|
|
6
|
+
var DEFAULT_SUBSCRIPTION_PREFIX = "mp:sub:";
|
|
7
|
+
var DEFAULT_OAUTH_PREFIX = "mp:oauth:";
|
|
8
|
+
var DEFAULT_IDEMPOTENCY_PREFIX = "mp:idem:";
|
|
9
|
+
var VercelKVSubscriptionStateAdapter = class {
|
|
10
|
+
kv;
|
|
11
|
+
prefix;
|
|
12
|
+
indexKey;
|
|
13
|
+
constructor(options = {}) {
|
|
14
|
+
this.kv = options.kv ?? kv.kv;
|
|
15
|
+
this.prefix = options.prefix ?? DEFAULT_SUBSCRIPTION_PREFIX;
|
|
16
|
+
this.indexKey = `${this.prefix}__index`;
|
|
17
|
+
}
|
|
18
|
+
key(id) {
|
|
19
|
+
return `${this.prefix}${id}`;
|
|
20
|
+
}
|
|
21
|
+
async set(id, state) {
|
|
22
|
+
const existing = await this.kv.get(this.key(id)) ?? {};
|
|
23
|
+
await this.kv.set(this.key(id), { ...existing, ...state });
|
|
24
|
+
await this.kv.sadd(this.indexKey, id);
|
|
25
|
+
}
|
|
26
|
+
async get(id) {
|
|
27
|
+
return await this.kv.get(this.key(id)) ?? null;
|
|
28
|
+
}
|
|
29
|
+
async list() {
|
|
30
|
+
const ids = await this.kv.smembers(this.indexKey);
|
|
31
|
+
return ids.map(String);
|
|
32
|
+
}
|
|
33
|
+
/** Forget a subscription record. NOT part of the adapter interface. */
|
|
34
|
+
async delete(id) {
|
|
35
|
+
await this.kv.del(this.key(id));
|
|
36
|
+
await this.kv.srem(this.indexKey, id);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
var VercelKVOAuthTokenStore = class {
|
|
40
|
+
kv;
|
|
41
|
+
prefix;
|
|
42
|
+
indexKey;
|
|
43
|
+
constructor(options = {}) {
|
|
44
|
+
this.kv = options.kv ?? kv.kv;
|
|
45
|
+
this.prefix = options.prefix ?? DEFAULT_OAUTH_PREFIX;
|
|
46
|
+
this.indexKey = `${this.prefix}__index`;
|
|
47
|
+
}
|
|
48
|
+
key(userId) {
|
|
49
|
+
return `${this.prefix}${userId}`;
|
|
50
|
+
}
|
|
51
|
+
async set(userId, token) {
|
|
52
|
+
await this.kv.set(this.key(userId), token);
|
|
53
|
+
await this.kv.sadd(this.indexKey, userId);
|
|
54
|
+
}
|
|
55
|
+
async get(userId) {
|
|
56
|
+
return await this.kv.get(this.key(userId)) ?? null;
|
|
57
|
+
}
|
|
58
|
+
async delete(userId) {
|
|
59
|
+
await this.kv.del(this.key(userId));
|
|
60
|
+
await this.kv.srem(this.indexKey, userId);
|
|
61
|
+
}
|
|
62
|
+
async list() {
|
|
63
|
+
const ids = await this.kv.smembers(this.indexKey);
|
|
64
|
+
return ids.map(String);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var VercelKVIdempotencyCache = class {
|
|
68
|
+
kv;
|
|
69
|
+
prefix;
|
|
70
|
+
constructor(options = {}) {
|
|
71
|
+
this.kv = options.kv ?? kv.kv;
|
|
72
|
+
this.prefix = options.prefix ?? DEFAULT_IDEMPOTENCY_PREFIX;
|
|
73
|
+
}
|
|
74
|
+
key(k) {
|
|
75
|
+
return `${this.prefix}${k}`;
|
|
76
|
+
}
|
|
77
|
+
async get(key) {
|
|
78
|
+
return await this.kv.get(this.key(key)) ?? null;
|
|
79
|
+
}
|
|
80
|
+
async set(key, value, ttlSeconds = 86400) {
|
|
81
|
+
await this.kv.set(this.key(key), value, { ex: ttlSeconds });
|
|
82
|
+
}
|
|
83
|
+
async delete(key) {
|
|
84
|
+
await this.kv.del(this.key(key));
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
exports.VercelKVIdempotencyCache = VercelKVIdempotencyCache;
|
|
89
|
+
exports.VercelKVOAuthTokenStore = VercelKVOAuthTokenStore;
|
|
90
|
+
exports.VercelKVSubscriptionStateAdapter = VercelKVSubscriptionStateAdapter;
|
|
91
|
+
//# sourceMappingURL=vercel-kv.cjs.map
|
|
92
|
+
//# sourceMappingURL=vercel-kv.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/vercel-kv.ts"],"names":["defaultKv"],"mappings":"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","file":"vercel-kv.cjs","sourcesContent":["/**\n * Vercel KV adapters — drop-in `SubscriptionStateAdapter`,\n * `OAuthTokenStore`, and `IdempotencyCache` implementations backed by\n * [Vercel KV](https://vercel.com/docs/storage/vercel-kv) (Upstash Redis).\n *\n * # Why a separate subpath?\n *\n * `@vercel/kv` is a peer dependency — only consumers who actually use Vercel\n * KV install it. Importing from `@ar-agents/mercadopago/vercel-kv` is\n * lazy: the main `@ar-agents/mercadopago` bundle stays tiny for callers who\n * use the in-memory adapters or a different store.\n *\n * # Setup\n *\n * 1. Create a KV store at https://vercel.com/dashboard/stores\n * 2. Connect it to your project — Vercel auto-injects `KV_*` env vars\n * 3. `pnpm add @vercel/kv`\n * 4. Wire the adapters:\n *\n * ```ts\n * import { mercadoPagoTools, MercadoPagoClient } from \"@ar-agents/mercadopago\";\n * import {\n * VercelKVSubscriptionStateAdapter,\n * VercelKVOAuthTokenStore,\n * } from \"@ar-agents/mercadopago/vercel-kv\";\n *\n * const tools = mercadoPagoTools(client, {\n * state: new VercelKVSubscriptionStateAdapter(),\n * backUrl: \"https://mysite.com/done\",\n * // ... oauth, webhookSecret, etc.\n * });\n *\n * // For marketplace flows, also wire the OAuth token store:\n * const oauthStore = new VercelKVOAuthTokenStore();\n * await oauthStore.set(token.user_id, {\n * user_id: token.user_id,\n * access_token: token.access_token,\n * refresh_token: token.refresh_token!,\n * expires_at: Date.now() + (token.expires_in ?? 21600) * 1000,\n * });\n * ```\n *\n * # Edge Runtime\n *\n * `@vercel/kv` works in Vercel Edge Runtime, Node.js, and any environment\n * with `fetch` (it's a thin REST client over Upstash). All adapters here\n * are async and Edge-safe.\n *\n * # Key namespacing\n *\n * Each adapter uses its own prefix so multiple adapters can share the same\n * KV store without collisions:\n * - Subscriptions: `mp:sub:{id}`\n * - OAuth tokens: `mp:oauth:{userId}`\n * - Idempotency: `mp:idem:{key}`\n *\n * Pass a custom prefix via the constructor if you need to share the store\n * with other apps.\n */\n\nimport { kv as defaultKv } from \"@vercel/kv\";\nimport type { VercelKV } from \"@vercel/kv\";\nimport type {\n IdempotencyCache,\n OAuthTokenRecord,\n OAuthTokenStore,\n SubscriptionStateAdapter,\n SubscriptionStateRecord,\n} from \"./state\";\n\nconst DEFAULT_SUBSCRIPTION_PREFIX = \"mp:sub:\";\nconst DEFAULT_OAUTH_PREFIX = \"mp:oauth:\";\nconst DEFAULT_IDEMPOTENCY_PREFIX = \"mp:idem:\";\n\ninterface VercelKVAdapterOptions {\n /**\n * Custom KV client. If omitted, uses the default `kv` export from\n * `@vercel/kv` (which reads `KV_REST_API_URL` + `KV_REST_API_TOKEN` from\n * env — auto-injected when you connect a KV store to your Vercel project).\n */\n kv?: VercelKV;\n /** Override the key prefix. */\n prefix?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// SubscriptionStateAdapter\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class VercelKVSubscriptionStateAdapter\n implements SubscriptionStateAdapter\n{\n private readonly kv: VercelKV;\n private readonly prefix: string;\n private readonly indexKey: string;\n\n constructor(options: VercelKVAdapterOptions = {}) {\n this.kv = options.kv ?? defaultKv;\n this.prefix = options.prefix ?? DEFAULT_SUBSCRIPTION_PREFIX;\n this.indexKey = `${this.prefix}__index`;\n }\n\n private key(id: string): string {\n return `${this.prefix}${id}`;\n }\n\n async set(\n id: string,\n state: Partial<SubscriptionStateRecord>,\n ): Promise<void> {\n const existing = (await this.kv.get<SubscriptionStateRecord>(this.key(id))) ?? {};\n await this.kv.set(this.key(id), { ...existing, ...state });\n await this.kv.sadd(this.indexKey, id);\n }\n\n async get(id: string): Promise<SubscriptionStateRecord | null> {\n return (await this.kv.get<SubscriptionStateRecord>(this.key(id))) ?? null;\n }\n\n async list(): Promise<string[]> {\n const ids = await this.kv.smembers(this.indexKey);\n return ids.map(String);\n }\n\n /** Forget a subscription record. NOT part of the adapter interface. */\n async delete(id: string): Promise<void> {\n await this.kv.del(this.key(id));\n await this.kv.srem(this.indexKey, id);\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// OAuthTokenStore (per-seller marketplace token persistence)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class VercelKVOAuthTokenStore implements OAuthTokenStore {\n private readonly kv: VercelKV;\n private readonly prefix: string;\n private readonly indexKey: string;\n\n constructor(options: VercelKVAdapterOptions = {}) {\n this.kv = options.kv ?? defaultKv;\n this.prefix = options.prefix ?? DEFAULT_OAUTH_PREFIX;\n this.indexKey = `${this.prefix}__index`;\n }\n\n private key(userId: string): string {\n return `${this.prefix}${userId}`;\n }\n\n async set(userId: string, token: OAuthTokenRecord): Promise<void> {\n await this.kv.set(this.key(userId), token);\n await this.kv.sadd(this.indexKey, userId);\n }\n\n async get(userId: string): Promise<OAuthTokenRecord | null> {\n return (await this.kv.get<OAuthTokenRecord>(this.key(userId))) ?? null;\n }\n\n async delete(userId: string): Promise<void> {\n await this.kv.del(this.key(userId));\n await this.kv.srem(this.indexKey, userId);\n }\n\n async list(): Promise<string[]> {\n const ids = await this.kv.smembers(this.indexKey);\n return ids.map(String);\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// IdempotencyCache (KV-backed dedup of agent retries)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class VercelKVIdempotencyCache implements IdempotencyCache {\n private readonly kv: VercelKV;\n private readonly prefix: string;\n\n constructor(options: VercelKVAdapterOptions = {}) {\n this.kv = options.kv ?? defaultKv;\n this.prefix = options.prefix ?? DEFAULT_IDEMPOTENCY_PREFIX;\n }\n\n private key(k: string): string {\n return `${this.prefix}${k}`;\n }\n\n async get<T>(key: string): Promise<T | null> {\n return (await this.kv.get<T>(this.key(key))) ?? null;\n }\n\n async set<T>(key: string, value: T, ttlSeconds = 86_400): Promise<void> {\n // Vercel KV's `set` supports a TTL in seconds via the `ex` option.\n await this.kv.set(this.key(key), value, { ex: ttlSeconds });\n }\n\n async delete(key: string): Promise<void> {\n await this.kv.del(this.key(key));\n }\n}\n"]}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { VercelKV } from '@vercel/kv';
|
|
2
|
+
import { I as IdempotencyCache, d as OAuthTokenStore, O as OAuthTokenRecord, S as SubscriptionStateAdapter, e as SubscriptionStateRecord } from './state-C6Wzb_XX.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Vercel KV adapters — drop-in `SubscriptionStateAdapter`,
|
|
6
|
+
* `OAuthTokenStore`, and `IdempotencyCache` implementations backed by
|
|
7
|
+
* [Vercel KV](https://vercel.com/docs/storage/vercel-kv) (Upstash Redis).
|
|
8
|
+
*
|
|
9
|
+
* # Why a separate subpath?
|
|
10
|
+
*
|
|
11
|
+
* `@vercel/kv` is a peer dependency — only consumers who actually use Vercel
|
|
12
|
+
* KV install it. Importing from `@ar-agents/mercadopago/vercel-kv` is
|
|
13
|
+
* lazy: the main `@ar-agents/mercadopago` bundle stays tiny for callers who
|
|
14
|
+
* use the in-memory adapters or a different store.
|
|
15
|
+
*
|
|
16
|
+
* # Setup
|
|
17
|
+
*
|
|
18
|
+
* 1. Create a KV store at https://vercel.com/dashboard/stores
|
|
19
|
+
* 2. Connect it to your project — Vercel auto-injects `KV_*` env vars
|
|
20
|
+
* 3. `pnpm add @vercel/kv`
|
|
21
|
+
* 4. Wire the adapters:
|
|
22
|
+
*
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { mercadoPagoTools, MercadoPagoClient } from "@ar-agents/mercadopago";
|
|
25
|
+
* import {
|
|
26
|
+
* VercelKVSubscriptionStateAdapter,
|
|
27
|
+
* VercelKVOAuthTokenStore,
|
|
28
|
+
* } from "@ar-agents/mercadopago/vercel-kv";
|
|
29
|
+
*
|
|
30
|
+
* const tools = mercadoPagoTools(client, {
|
|
31
|
+
* state: new VercelKVSubscriptionStateAdapter(),
|
|
32
|
+
* backUrl: "https://mysite.com/done",
|
|
33
|
+
* // ... oauth, webhookSecret, etc.
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* // For marketplace flows, also wire the OAuth token store:
|
|
37
|
+
* const oauthStore = new VercelKVOAuthTokenStore();
|
|
38
|
+
* await oauthStore.set(token.user_id, {
|
|
39
|
+
* user_id: token.user_id,
|
|
40
|
+
* access_token: token.access_token,
|
|
41
|
+
* refresh_token: token.refresh_token!,
|
|
42
|
+
* expires_at: Date.now() + (token.expires_in ?? 21600) * 1000,
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* # Edge Runtime
|
|
47
|
+
*
|
|
48
|
+
* `@vercel/kv` works in Vercel Edge Runtime, Node.js, and any environment
|
|
49
|
+
* with `fetch` (it's a thin REST client over Upstash). All adapters here
|
|
50
|
+
* are async and Edge-safe.
|
|
51
|
+
*
|
|
52
|
+
* # Key namespacing
|
|
53
|
+
*
|
|
54
|
+
* Each adapter uses its own prefix so multiple adapters can share the same
|
|
55
|
+
* KV store without collisions:
|
|
56
|
+
* - Subscriptions: `mp:sub:{id}`
|
|
57
|
+
* - OAuth tokens: `mp:oauth:{userId}`
|
|
58
|
+
* - Idempotency: `mp:idem:{key}`
|
|
59
|
+
*
|
|
60
|
+
* Pass a custom prefix via the constructor if you need to share the store
|
|
61
|
+
* with other apps.
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
interface VercelKVAdapterOptions {
|
|
65
|
+
/**
|
|
66
|
+
* Custom KV client. If omitted, uses the default `kv` export from
|
|
67
|
+
* `@vercel/kv` (which reads `KV_REST_API_URL` + `KV_REST_API_TOKEN` from
|
|
68
|
+
* env — auto-injected when you connect a KV store to your Vercel project).
|
|
69
|
+
*/
|
|
70
|
+
kv?: VercelKV;
|
|
71
|
+
/** Override the key prefix. */
|
|
72
|
+
prefix?: string;
|
|
73
|
+
}
|
|
74
|
+
declare class VercelKVSubscriptionStateAdapter implements SubscriptionStateAdapter {
|
|
75
|
+
private readonly kv;
|
|
76
|
+
private readonly prefix;
|
|
77
|
+
private readonly indexKey;
|
|
78
|
+
constructor(options?: VercelKVAdapterOptions);
|
|
79
|
+
private key;
|
|
80
|
+
set(id: string, state: Partial<SubscriptionStateRecord>): Promise<void>;
|
|
81
|
+
get(id: string): Promise<SubscriptionStateRecord | null>;
|
|
82
|
+
list(): Promise<string[]>;
|
|
83
|
+
/** Forget a subscription record. NOT part of the adapter interface. */
|
|
84
|
+
delete(id: string): Promise<void>;
|
|
85
|
+
}
|
|
86
|
+
declare class VercelKVOAuthTokenStore implements OAuthTokenStore {
|
|
87
|
+
private readonly kv;
|
|
88
|
+
private readonly prefix;
|
|
89
|
+
private readonly indexKey;
|
|
90
|
+
constructor(options?: VercelKVAdapterOptions);
|
|
91
|
+
private key;
|
|
92
|
+
set(userId: string, token: OAuthTokenRecord): Promise<void>;
|
|
93
|
+
get(userId: string): Promise<OAuthTokenRecord | null>;
|
|
94
|
+
delete(userId: string): Promise<void>;
|
|
95
|
+
list(): Promise<string[]>;
|
|
96
|
+
}
|
|
97
|
+
declare class VercelKVIdempotencyCache implements IdempotencyCache {
|
|
98
|
+
private readonly kv;
|
|
99
|
+
private readonly prefix;
|
|
100
|
+
constructor(options?: VercelKVAdapterOptions);
|
|
101
|
+
private key;
|
|
102
|
+
get<T>(key: string): Promise<T | null>;
|
|
103
|
+
set<T>(key: string, value: T, ttlSeconds?: number): Promise<void>;
|
|
104
|
+
delete(key: string): Promise<void>;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export { VercelKVIdempotencyCache, VercelKVOAuthTokenStore, VercelKVSubscriptionStateAdapter };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { VercelKV } from '@vercel/kv';
|
|
2
|
+
import { I as IdempotencyCache, d as OAuthTokenStore, O as OAuthTokenRecord, S as SubscriptionStateAdapter, e as SubscriptionStateRecord } from './state-C6Wzb_XX.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Vercel KV adapters — drop-in `SubscriptionStateAdapter`,
|
|
6
|
+
* `OAuthTokenStore`, and `IdempotencyCache` implementations backed by
|
|
7
|
+
* [Vercel KV](https://vercel.com/docs/storage/vercel-kv) (Upstash Redis).
|
|
8
|
+
*
|
|
9
|
+
* # Why a separate subpath?
|
|
10
|
+
*
|
|
11
|
+
* `@vercel/kv` is a peer dependency — only consumers who actually use Vercel
|
|
12
|
+
* KV install it. Importing from `@ar-agents/mercadopago/vercel-kv` is
|
|
13
|
+
* lazy: the main `@ar-agents/mercadopago` bundle stays tiny for callers who
|
|
14
|
+
* use the in-memory adapters or a different store.
|
|
15
|
+
*
|
|
16
|
+
* # Setup
|
|
17
|
+
*
|
|
18
|
+
* 1. Create a KV store at https://vercel.com/dashboard/stores
|
|
19
|
+
* 2. Connect it to your project — Vercel auto-injects `KV_*` env vars
|
|
20
|
+
* 3. `pnpm add @vercel/kv`
|
|
21
|
+
* 4. Wire the adapters:
|
|
22
|
+
*
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { mercadoPagoTools, MercadoPagoClient } from "@ar-agents/mercadopago";
|
|
25
|
+
* import {
|
|
26
|
+
* VercelKVSubscriptionStateAdapter,
|
|
27
|
+
* VercelKVOAuthTokenStore,
|
|
28
|
+
* } from "@ar-agents/mercadopago/vercel-kv";
|
|
29
|
+
*
|
|
30
|
+
* const tools = mercadoPagoTools(client, {
|
|
31
|
+
* state: new VercelKVSubscriptionStateAdapter(),
|
|
32
|
+
* backUrl: "https://mysite.com/done",
|
|
33
|
+
* // ... oauth, webhookSecret, etc.
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* // For marketplace flows, also wire the OAuth token store:
|
|
37
|
+
* const oauthStore = new VercelKVOAuthTokenStore();
|
|
38
|
+
* await oauthStore.set(token.user_id, {
|
|
39
|
+
* user_id: token.user_id,
|
|
40
|
+
* access_token: token.access_token,
|
|
41
|
+
* refresh_token: token.refresh_token!,
|
|
42
|
+
* expires_at: Date.now() + (token.expires_in ?? 21600) * 1000,
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* # Edge Runtime
|
|
47
|
+
*
|
|
48
|
+
* `@vercel/kv` works in Vercel Edge Runtime, Node.js, and any environment
|
|
49
|
+
* with `fetch` (it's a thin REST client over Upstash). All adapters here
|
|
50
|
+
* are async and Edge-safe.
|
|
51
|
+
*
|
|
52
|
+
* # Key namespacing
|
|
53
|
+
*
|
|
54
|
+
* Each adapter uses its own prefix so multiple adapters can share the same
|
|
55
|
+
* KV store without collisions:
|
|
56
|
+
* - Subscriptions: `mp:sub:{id}`
|
|
57
|
+
* - OAuth tokens: `mp:oauth:{userId}`
|
|
58
|
+
* - Idempotency: `mp:idem:{key}`
|
|
59
|
+
*
|
|
60
|
+
* Pass a custom prefix via the constructor if you need to share the store
|
|
61
|
+
* with other apps.
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
interface VercelKVAdapterOptions {
|
|
65
|
+
/**
|
|
66
|
+
* Custom KV client. If omitted, uses the default `kv` export from
|
|
67
|
+
* `@vercel/kv` (which reads `KV_REST_API_URL` + `KV_REST_API_TOKEN` from
|
|
68
|
+
* env — auto-injected when you connect a KV store to your Vercel project).
|
|
69
|
+
*/
|
|
70
|
+
kv?: VercelKV;
|
|
71
|
+
/** Override the key prefix. */
|
|
72
|
+
prefix?: string;
|
|
73
|
+
}
|
|
74
|
+
declare class VercelKVSubscriptionStateAdapter implements SubscriptionStateAdapter {
|
|
75
|
+
private readonly kv;
|
|
76
|
+
private readonly prefix;
|
|
77
|
+
private readonly indexKey;
|
|
78
|
+
constructor(options?: VercelKVAdapterOptions);
|
|
79
|
+
private key;
|
|
80
|
+
set(id: string, state: Partial<SubscriptionStateRecord>): Promise<void>;
|
|
81
|
+
get(id: string): Promise<SubscriptionStateRecord | null>;
|
|
82
|
+
list(): Promise<string[]>;
|
|
83
|
+
/** Forget a subscription record. NOT part of the adapter interface. */
|
|
84
|
+
delete(id: string): Promise<void>;
|
|
85
|
+
}
|
|
86
|
+
declare class VercelKVOAuthTokenStore implements OAuthTokenStore {
|
|
87
|
+
private readonly kv;
|
|
88
|
+
private readonly prefix;
|
|
89
|
+
private readonly indexKey;
|
|
90
|
+
constructor(options?: VercelKVAdapterOptions);
|
|
91
|
+
private key;
|
|
92
|
+
set(userId: string, token: OAuthTokenRecord): Promise<void>;
|
|
93
|
+
get(userId: string): Promise<OAuthTokenRecord | null>;
|
|
94
|
+
delete(userId: string): Promise<void>;
|
|
95
|
+
list(): Promise<string[]>;
|
|
96
|
+
}
|
|
97
|
+
declare class VercelKVIdempotencyCache implements IdempotencyCache {
|
|
98
|
+
private readonly kv;
|
|
99
|
+
private readonly prefix;
|
|
100
|
+
constructor(options?: VercelKVAdapterOptions);
|
|
101
|
+
private key;
|
|
102
|
+
get<T>(key: string): Promise<T | null>;
|
|
103
|
+
set<T>(key: string, value: T, ttlSeconds?: number): Promise<void>;
|
|
104
|
+
delete(key: string): Promise<void>;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export { VercelKVIdempotencyCache, VercelKVOAuthTokenStore, VercelKVSubscriptionStateAdapter };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { kv } from '@vercel/kv';
|
|
2
|
+
|
|
3
|
+
// src/vercel-kv.ts
|
|
4
|
+
var DEFAULT_SUBSCRIPTION_PREFIX = "mp:sub:";
|
|
5
|
+
var DEFAULT_OAUTH_PREFIX = "mp:oauth:";
|
|
6
|
+
var DEFAULT_IDEMPOTENCY_PREFIX = "mp:idem:";
|
|
7
|
+
var VercelKVSubscriptionStateAdapter = class {
|
|
8
|
+
kv;
|
|
9
|
+
prefix;
|
|
10
|
+
indexKey;
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
this.kv = options.kv ?? kv;
|
|
13
|
+
this.prefix = options.prefix ?? DEFAULT_SUBSCRIPTION_PREFIX;
|
|
14
|
+
this.indexKey = `${this.prefix}__index`;
|
|
15
|
+
}
|
|
16
|
+
key(id) {
|
|
17
|
+
return `${this.prefix}${id}`;
|
|
18
|
+
}
|
|
19
|
+
async set(id, state) {
|
|
20
|
+
const existing = await this.kv.get(this.key(id)) ?? {};
|
|
21
|
+
await this.kv.set(this.key(id), { ...existing, ...state });
|
|
22
|
+
await this.kv.sadd(this.indexKey, id);
|
|
23
|
+
}
|
|
24
|
+
async get(id) {
|
|
25
|
+
return await this.kv.get(this.key(id)) ?? null;
|
|
26
|
+
}
|
|
27
|
+
async list() {
|
|
28
|
+
const ids = await this.kv.smembers(this.indexKey);
|
|
29
|
+
return ids.map(String);
|
|
30
|
+
}
|
|
31
|
+
/** Forget a subscription record. NOT part of the adapter interface. */
|
|
32
|
+
async delete(id) {
|
|
33
|
+
await this.kv.del(this.key(id));
|
|
34
|
+
await this.kv.srem(this.indexKey, id);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
var VercelKVOAuthTokenStore = class {
|
|
38
|
+
kv;
|
|
39
|
+
prefix;
|
|
40
|
+
indexKey;
|
|
41
|
+
constructor(options = {}) {
|
|
42
|
+
this.kv = options.kv ?? kv;
|
|
43
|
+
this.prefix = options.prefix ?? DEFAULT_OAUTH_PREFIX;
|
|
44
|
+
this.indexKey = `${this.prefix}__index`;
|
|
45
|
+
}
|
|
46
|
+
key(userId) {
|
|
47
|
+
return `${this.prefix}${userId}`;
|
|
48
|
+
}
|
|
49
|
+
async set(userId, token) {
|
|
50
|
+
await this.kv.set(this.key(userId), token);
|
|
51
|
+
await this.kv.sadd(this.indexKey, userId);
|
|
52
|
+
}
|
|
53
|
+
async get(userId) {
|
|
54
|
+
return await this.kv.get(this.key(userId)) ?? null;
|
|
55
|
+
}
|
|
56
|
+
async delete(userId) {
|
|
57
|
+
await this.kv.del(this.key(userId));
|
|
58
|
+
await this.kv.srem(this.indexKey, userId);
|
|
59
|
+
}
|
|
60
|
+
async list() {
|
|
61
|
+
const ids = await this.kv.smembers(this.indexKey);
|
|
62
|
+
return ids.map(String);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
var VercelKVIdempotencyCache = class {
|
|
66
|
+
kv;
|
|
67
|
+
prefix;
|
|
68
|
+
constructor(options = {}) {
|
|
69
|
+
this.kv = options.kv ?? kv;
|
|
70
|
+
this.prefix = options.prefix ?? DEFAULT_IDEMPOTENCY_PREFIX;
|
|
71
|
+
}
|
|
72
|
+
key(k) {
|
|
73
|
+
return `${this.prefix}${k}`;
|
|
74
|
+
}
|
|
75
|
+
async get(key) {
|
|
76
|
+
return await this.kv.get(this.key(key)) ?? null;
|
|
77
|
+
}
|
|
78
|
+
async set(key, value, ttlSeconds = 86400) {
|
|
79
|
+
await this.kv.set(this.key(key), value, { ex: ttlSeconds });
|
|
80
|
+
}
|
|
81
|
+
async delete(key) {
|
|
82
|
+
await this.kv.del(this.key(key));
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export { VercelKVIdempotencyCache, VercelKVOAuthTokenStore, VercelKVSubscriptionStateAdapter };
|
|
87
|
+
//# sourceMappingURL=vercel-kv.js.map
|
|
88
|
+
//# sourceMappingURL=vercel-kv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/vercel-kv.ts"],"names":["defaultKv"],"mappings":"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","file":"vercel-kv.js","sourcesContent":["/**\n * Vercel KV adapters — drop-in `SubscriptionStateAdapter`,\n * `OAuthTokenStore`, and `IdempotencyCache` implementations backed by\n * [Vercel KV](https://vercel.com/docs/storage/vercel-kv) (Upstash Redis).\n *\n * # Why a separate subpath?\n *\n * `@vercel/kv` is a peer dependency — only consumers who actually use Vercel\n * KV install it. Importing from `@ar-agents/mercadopago/vercel-kv` is\n * lazy: the main `@ar-agents/mercadopago` bundle stays tiny for callers who\n * use the in-memory adapters or a different store.\n *\n * # Setup\n *\n * 1. Create a KV store at https://vercel.com/dashboard/stores\n * 2. Connect it to your project — Vercel auto-injects `KV_*` env vars\n * 3. `pnpm add @vercel/kv`\n * 4. Wire the adapters:\n *\n * ```ts\n * import { mercadoPagoTools, MercadoPagoClient } from \"@ar-agents/mercadopago\";\n * import {\n * VercelKVSubscriptionStateAdapter,\n * VercelKVOAuthTokenStore,\n * } from \"@ar-agents/mercadopago/vercel-kv\";\n *\n * const tools = mercadoPagoTools(client, {\n * state: new VercelKVSubscriptionStateAdapter(),\n * backUrl: \"https://mysite.com/done\",\n * // ... oauth, webhookSecret, etc.\n * });\n *\n * // For marketplace flows, also wire the OAuth token store:\n * const oauthStore = new VercelKVOAuthTokenStore();\n * await oauthStore.set(token.user_id, {\n * user_id: token.user_id,\n * access_token: token.access_token,\n * refresh_token: token.refresh_token!,\n * expires_at: Date.now() + (token.expires_in ?? 21600) * 1000,\n * });\n * ```\n *\n * # Edge Runtime\n *\n * `@vercel/kv` works in Vercel Edge Runtime, Node.js, and any environment\n * with `fetch` (it's a thin REST client over Upstash). All adapters here\n * are async and Edge-safe.\n *\n * # Key namespacing\n *\n * Each adapter uses its own prefix so multiple adapters can share the same\n * KV store without collisions:\n * - Subscriptions: `mp:sub:{id}`\n * - OAuth tokens: `mp:oauth:{userId}`\n * - Idempotency: `mp:idem:{key}`\n *\n * Pass a custom prefix via the constructor if you need to share the store\n * with other apps.\n */\n\nimport { kv as defaultKv } from \"@vercel/kv\";\nimport type { VercelKV } from \"@vercel/kv\";\nimport type {\n IdempotencyCache,\n OAuthTokenRecord,\n OAuthTokenStore,\n SubscriptionStateAdapter,\n SubscriptionStateRecord,\n} from \"./state\";\n\nconst DEFAULT_SUBSCRIPTION_PREFIX = \"mp:sub:\";\nconst DEFAULT_OAUTH_PREFIX = \"mp:oauth:\";\nconst DEFAULT_IDEMPOTENCY_PREFIX = \"mp:idem:\";\n\ninterface VercelKVAdapterOptions {\n /**\n * Custom KV client. If omitted, uses the default `kv` export from\n * `@vercel/kv` (which reads `KV_REST_API_URL` + `KV_REST_API_TOKEN` from\n * env — auto-injected when you connect a KV store to your Vercel project).\n */\n kv?: VercelKV;\n /** Override the key prefix. */\n prefix?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// SubscriptionStateAdapter\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class VercelKVSubscriptionStateAdapter\n implements SubscriptionStateAdapter\n{\n private readonly kv: VercelKV;\n private readonly prefix: string;\n private readonly indexKey: string;\n\n constructor(options: VercelKVAdapterOptions = {}) {\n this.kv = options.kv ?? defaultKv;\n this.prefix = options.prefix ?? DEFAULT_SUBSCRIPTION_PREFIX;\n this.indexKey = `${this.prefix}__index`;\n }\n\n private key(id: string): string {\n return `${this.prefix}${id}`;\n }\n\n async set(\n id: string,\n state: Partial<SubscriptionStateRecord>,\n ): Promise<void> {\n const existing = (await this.kv.get<SubscriptionStateRecord>(this.key(id))) ?? {};\n await this.kv.set(this.key(id), { ...existing, ...state });\n await this.kv.sadd(this.indexKey, id);\n }\n\n async get(id: string): Promise<SubscriptionStateRecord | null> {\n return (await this.kv.get<SubscriptionStateRecord>(this.key(id))) ?? null;\n }\n\n async list(): Promise<string[]> {\n const ids = await this.kv.smembers(this.indexKey);\n return ids.map(String);\n }\n\n /** Forget a subscription record. NOT part of the adapter interface. */\n async delete(id: string): Promise<void> {\n await this.kv.del(this.key(id));\n await this.kv.srem(this.indexKey, id);\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// OAuthTokenStore (per-seller marketplace token persistence)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class VercelKVOAuthTokenStore implements OAuthTokenStore {\n private readonly kv: VercelKV;\n private readonly prefix: string;\n private readonly indexKey: string;\n\n constructor(options: VercelKVAdapterOptions = {}) {\n this.kv = options.kv ?? defaultKv;\n this.prefix = options.prefix ?? DEFAULT_OAUTH_PREFIX;\n this.indexKey = `${this.prefix}__index`;\n }\n\n private key(userId: string): string {\n return `${this.prefix}${userId}`;\n }\n\n async set(userId: string, token: OAuthTokenRecord): Promise<void> {\n await this.kv.set(this.key(userId), token);\n await this.kv.sadd(this.indexKey, userId);\n }\n\n async get(userId: string): Promise<OAuthTokenRecord | null> {\n return (await this.kv.get<OAuthTokenRecord>(this.key(userId))) ?? null;\n }\n\n async delete(userId: string): Promise<void> {\n await this.kv.del(this.key(userId));\n await this.kv.srem(this.indexKey, userId);\n }\n\n async list(): Promise<string[]> {\n const ids = await this.kv.smembers(this.indexKey);\n return ids.map(String);\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// IdempotencyCache (KV-backed dedup of agent retries)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class VercelKVIdempotencyCache implements IdempotencyCache {\n private readonly kv: VercelKV;\n private readonly prefix: string;\n\n constructor(options: VercelKVAdapterOptions = {}) {\n this.kv = options.kv ?? defaultKv;\n this.prefix = options.prefix ?? DEFAULT_IDEMPOTENCY_PREFIX;\n }\n\n private key(k: string): string {\n return `${this.prefix}${k}`;\n }\n\n async get<T>(key: string): Promise<T | null> {\n return (await this.kv.get<T>(this.key(key))) ?? null;\n }\n\n async set<T>(key: string, value: T, ttlSeconds = 86_400): Promise<void> {\n // Vercel KV's `set` supports a TTL in seconds via the `ex` option.\n await this.kv.set(this.key(key), value, { ex: ttlSeconds });\n }\n\n async delete(key: string): Promise<void> {\n await this.kv.del(this.key(key));\n }\n}\n"]}
|