payweave 0.0.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/LICENSE +21 -0
- package/README.md +272 -0
- package/dist/_tsup-dts-rollup.d.ts +19470 -0
- package/dist/chunk-4R5OGOCC.js +94 -0
- package/dist/chunk-4R5OGOCC.js.map +1 -0
- package/dist/chunk-535Q3GUN.js +175 -0
- package/dist/chunk-535Q3GUN.js.map +1 -0
- package/dist/chunk-67NRYGRC.js +192 -0
- package/dist/chunk-67NRYGRC.js.map +1 -0
- package/dist/chunk-CGQ73I4I.js +568 -0
- package/dist/chunk-CGQ73I4I.js.map +1 -0
- package/dist/chunk-CJEYDHY6.js +18 -0
- package/dist/chunk-CJEYDHY6.js.map +1 -0
- package/dist/chunk-CR63JAAS.js +3 -0
- package/dist/chunk-CR63JAAS.js.map +1 -0
- package/dist/chunk-E633MKVX.js +1507 -0
- package/dist/chunk-E633MKVX.js.map +1 -0
- package/dist/chunk-FBPOCXRZ.js +1135 -0
- package/dist/chunk-FBPOCXRZ.js.map +1 -0
- package/dist/chunk-HTRGMZFH.js +267 -0
- package/dist/chunk-HTRGMZFH.js.map +1 -0
- package/dist/chunk-IUVGM3K7.js +765 -0
- package/dist/chunk-IUVGM3K7.js.map +1 -0
- package/dist/chunk-JD4OZHRI.js +326 -0
- package/dist/chunk-JD4OZHRI.js.map +1 -0
- package/dist/chunk-NV3MZF7L.js +139 -0
- package/dist/chunk-NV3MZF7L.js.map +1 -0
- package/dist/chunk-NXQBP35U.js +13 -0
- package/dist/chunk-NXQBP35U.js.map +1 -0
- package/dist/chunk-QJHXDZYC.js +412 -0
- package/dist/chunk-QJHXDZYC.js.map +1 -0
- package/dist/chunk-TWZK62TZ.js +83 -0
- package/dist/chunk-TWZK62TZ.js.map +1 -0
- package/dist/cli/index.js +35381 -0
- package/dist/core/index.d.ts +85 -0
- package/dist/core/index.js +6 -0
- package/dist/core/index.js.map +1 -0
- package/dist/db/drizzle/index.d.ts +6 -0
- package/dist/db/drizzle/index.js +1532 -0
- package/dist/db/drizzle/index.js.map +1 -0
- package/dist/db/index.d.ts +43 -0
- package/dist/db/index.js +3 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/mongodb/index.d.ts +3 -0
- package/dist/db/mongodb/index.js +934 -0
- package/dist/db/mongodb/index.js.map +1 -0
- package/dist/db/mysql/index.d.ts +1 -0
- package/dist/db/mysql/index.js +12 -0
- package/dist/db/mysql/index.js.map +1 -0
- package/dist/db/postgres/index.d.ts +5 -0
- package/dist/db/postgres/index.js +791 -0
- package/dist/db/postgres/index.js.map +1 -0
- package/dist/db/prisma/index.d.ts +1 -0
- package/dist/db/prisma/index.js +12 -0
- package/dist/db/prisma/index.js.map +1 -0
- package/dist/db/sqlite/index.d.ts +5 -0
- package/dist/db/sqlite/index.js +750 -0
- package/dist/db/sqlite/index.js.map +1 -0
- package/dist/express/index.d.ts +1 -0
- package/dist/express/index.js +3 -0
- package/dist/express/index.js.map +1 -0
- package/dist/fastify/index.d.ts +1 -0
- package/dist/fastify/index.js +3 -0
- package/dist/fastify/index.js.map +1 -0
- package/dist/flutterwave/index.d.ts +27 -0
- package/dist/flutterwave/index.js +9 -0
- package/dist/flutterwave/index.js.map +1 -0
- package/dist/index.d.ts +85 -0
- package/dist/index.js +3677 -0
- package/dist/index.js.map +1 -0
- package/dist/next/index.d.ts +1 -0
- package/dist/next/index.js +3 -0
- package/dist/next/index.js.map +1 -0
- package/dist/paystack/index.d.ts +33 -0
- package/dist/paystack/index.js +9 -0
- package/dist/paystack/index.js.map +1 -0
- package/dist/products/index.d.ts +22 -0
- package/dist/products/index.js +7 -0
- package/dist/products/index.js.map +1 -0
- package/dist/testing/index.d.ts +11 -0
- package/dist/testing/index.js +69 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/unified/index.d.ts +41 -0
- package/dist/unified/index.js +6 -0
- package/dist/unified/index.js.map +1 -0
- package/dist/webhooks/index.d.ts +8 -0
- package/dist/webhooks/index.js +8 -0
- package/dist/webhooks/index.js.map +1 -0
- package/package.json +164 -0
|
@@ -0,0 +1,750 @@
|
|
|
1
|
+
import { applyMigrations, planMigrations } from '../../chunk-QJHXDZYC.js';
|
|
2
|
+
import { PW_ACTIVE_SUBSCRIPTION_STATUSES, PW_TABLES, DEFAULT_STALE_CLAIM_AFTER_MS } from '../../chunk-NV3MZF7L.js';
|
|
3
|
+
import { currentPeriod } from '../../chunk-4R5OGOCC.js';
|
|
4
|
+
import { PayweaveConfigError, PayweaveNotFoundError, PayweaveError, PayweaveValidationError } from '../../chunk-HTRGMZFH.js';
|
|
5
|
+
import { randomInt } from 'crypto';
|
|
6
|
+
|
|
7
|
+
// src/db/sqlite/errors.ts
|
|
8
|
+
var DRIVER_PACKAGE = {
|
|
9
|
+
"better-sqlite3": "better-sqlite3",
|
|
10
|
+
libsql: "@libsql/client"
|
|
11
|
+
};
|
|
12
|
+
function installHintError(driver, url, cause) {
|
|
13
|
+
const pkg = DRIVER_PACKAGE[driver];
|
|
14
|
+
return new PayweaveConfigError(
|
|
15
|
+
`payweave/db/sqlite: opening ${JSON.stringify(url)} requires the "${pkg}" package, which is not installed (it is an optional peerDependency of "payweave"). Run \`npm install ${pkg}\` (or your package manager's equivalent) and try again.`,
|
|
16
|
+
{ cause }
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
function isConstraintViolation(error) {
|
|
20
|
+
const code = error?.code;
|
|
21
|
+
return typeof code === "string" && code.startsWith("SQLITE_CONSTRAINT");
|
|
22
|
+
}
|
|
23
|
+
function wrapDriverError(error, message) {
|
|
24
|
+
if (error instanceof PayweaveError) return error;
|
|
25
|
+
if (isConstraintViolation(error)) {
|
|
26
|
+
return new PayweaveValidationError(message, { cause: error });
|
|
27
|
+
}
|
|
28
|
+
return new PayweaveError(message, { cause: error, isRetryable: false });
|
|
29
|
+
}
|
|
30
|
+
var ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
31
|
+
var ENCODING_LEN = ENCODING.length;
|
|
32
|
+
var TIME_LEN = 10;
|
|
33
|
+
var RANDOM_LEN = 16;
|
|
34
|
+
function encodeTime(timeMs, len) {
|
|
35
|
+
if (!Number.isSafeInteger(timeMs) || timeMs < 0) {
|
|
36
|
+
throw new RangeError(`ulid: time must be a non-negative safe integer \u2014 got ${timeMs}`);
|
|
37
|
+
}
|
|
38
|
+
let remaining = timeMs;
|
|
39
|
+
let out = "";
|
|
40
|
+
for (let i = 0; i < len; i++) {
|
|
41
|
+
const mod = remaining % ENCODING_LEN;
|
|
42
|
+
out = ENCODING.charAt(mod) + out;
|
|
43
|
+
remaining = Math.floor(remaining / ENCODING_LEN);
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
function encodeRandom(len) {
|
|
48
|
+
let out = "";
|
|
49
|
+
for (let i = 0; i < len; i++) {
|
|
50
|
+
out += ENCODING.charAt(randomInt(0, ENCODING_LEN));
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
function ulid(timeMs = Date.now()) {
|
|
55
|
+
return encodeTime(timeMs, TIME_LEN) + encodeRandom(RANDOM_LEN);
|
|
56
|
+
}
|
|
57
|
+
function generatePwId(timeMs = Date.now()) {
|
|
58
|
+
return `pwv_${ulid(timeMs)}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/db/sqlite/migrations-executor.ts
|
|
62
|
+
var SqliteMigrationExecutor = class _SqliteMigrationExecutor {
|
|
63
|
+
#runner;
|
|
64
|
+
constructor(runner) {
|
|
65
|
+
this.#runner = runner;
|
|
66
|
+
}
|
|
67
|
+
async query(sql, params = []) {
|
|
68
|
+
const { rows } = await this.#runner.execute(sql, params);
|
|
69
|
+
return { rows };
|
|
70
|
+
}
|
|
71
|
+
async transaction(fn) {
|
|
72
|
+
return this.#runner.transaction((tx) => fn(new _SqliteMigrationExecutor(tx)));
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// src/db/sqlite/rows.ts
|
|
77
|
+
function requireColumn(row, column) {
|
|
78
|
+
if (!(column in row)) {
|
|
79
|
+
throw new Error(`sqlite adapter: row is missing expected column "${column}"`);
|
|
80
|
+
}
|
|
81
|
+
return row[column];
|
|
82
|
+
}
|
|
83
|
+
function str(row, column) {
|
|
84
|
+
const value = requireColumn(row, column);
|
|
85
|
+
if (typeof value !== "string") {
|
|
86
|
+
throw new Error(`sqlite adapter: expected column "${column}" to be a string, got ${typeof value}`);
|
|
87
|
+
}
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
function nullableStr(row, column) {
|
|
91
|
+
const value = requireColumn(row, column);
|
|
92
|
+
if (value === null || value === void 0) return null;
|
|
93
|
+
if (typeof value !== "string") {
|
|
94
|
+
throw new Error(`sqlite adapter: expected column "${column}" to be a string or null, got ${typeof value}`);
|
|
95
|
+
}
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
function num(row, column) {
|
|
99
|
+
const value = requireColumn(row, column);
|
|
100
|
+
if (typeof value === "bigint") return Number(value);
|
|
101
|
+
if (typeof value !== "number") {
|
|
102
|
+
throw new Error(`sqlite adapter: expected column "${column}" to be a number, got ${typeof value}`);
|
|
103
|
+
}
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
function bool(row, column) {
|
|
107
|
+
return num(row, column) !== 0;
|
|
108
|
+
}
|
|
109
|
+
function dateOf(row, column) {
|
|
110
|
+
return new Date(num(row, column));
|
|
111
|
+
}
|
|
112
|
+
function json(row, column) {
|
|
113
|
+
return JSON.parse(str(row, column));
|
|
114
|
+
}
|
|
115
|
+
var encodeDate = (date) => date.getTime();
|
|
116
|
+
var encodeBool = (value) => value ? 1 : 0;
|
|
117
|
+
var encodeJson = (value) => JSON.stringify(value);
|
|
118
|
+
function rowToCustomer(row) {
|
|
119
|
+
return {
|
|
120
|
+
id: str(row, "id"),
|
|
121
|
+
externalId: str(row, "external_id"),
|
|
122
|
+
providerIds: json(row, "provider_ids"),
|
|
123
|
+
email: nullableStr(row, "email"),
|
|
124
|
+
createdAt: dateOf(row, "created_at"),
|
|
125
|
+
updatedAt: dateOf(row, "updated_at")
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function rowToPlanVersion(row) {
|
|
129
|
+
return {
|
|
130
|
+
id: str(row, "id"),
|
|
131
|
+
planId: str(row, "plan_id"),
|
|
132
|
+
version: num(row, "version"),
|
|
133
|
+
group: str(row, "group"),
|
|
134
|
+
isDefault: bool(row, "is_default"),
|
|
135
|
+
name: nullableStr(row, "name"),
|
|
136
|
+
priceMinor: (() => {
|
|
137
|
+
const v = requireColumn(row, "price_minor");
|
|
138
|
+
return v === null ? null : num(row, "price_minor");
|
|
139
|
+
})(),
|
|
140
|
+
priceCurrency: nullableStr(row, "price_currency"),
|
|
141
|
+
priceInterval: nullableStr(row, "price_interval"),
|
|
142
|
+
features: json(row, "features"),
|
|
143
|
+
providerRefs: json(row, "provider_refs"),
|
|
144
|
+
pushedAt: dateOf(row, "pushed_at")
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function rowToSubscription(row) {
|
|
148
|
+
return {
|
|
149
|
+
id: str(row, "id"),
|
|
150
|
+
customerId: str(row, "customer_id"),
|
|
151
|
+
planId: str(row, "plan_id"),
|
|
152
|
+
planVersion: num(row, "plan_version"),
|
|
153
|
+
group: str(row, "group"),
|
|
154
|
+
status: str(row, "status"),
|
|
155
|
+
provider: nullableStr(row, "provider"),
|
|
156
|
+
providerSubscriptionRef: nullableStr(row, "provider_subscription_ref"),
|
|
157
|
+
currentPeriodStart: dateOf(row, "current_period_start"),
|
|
158
|
+
currentPeriodEnd: dateOf(row, "current_period_end"),
|
|
159
|
+
cancelAtPeriodEnd: bool(row, "cancel_at_period_end"),
|
|
160
|
+
createdAt: dateOf(row, "created_at"),
|
|
161
|
+
updatedAt: dateOf(row, "updated_at")
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function rowToFeatureBalance(row) {
|
|
165
|
+
return {
|
|
166
|
+
id: str(row, "id"),
|
|
167
|
+
customerId: str(row, "customer_id"),
|
|
168
|
+
featureId: str(row, "feature_id"),
|
|
169
|
+
group: str(row, "group"),
|
|
170
|
+
used: num(row, "used"),
|
|
171
|
+
limit: num(row, "limit"),
|
|
172
|
+
resetInterval: str(row, "reset_interval"),
|
|
173
|
+
anchor: dateOf(row, "anchor"),
|
|
174
|
+
periodStart: dateOf(row, "period_start"),
|
|
175
|
+
periodEnd: dateOf(row, "period_end"),
|
|
176
|
+
planId: str(row, "plan_id"),
|
|
177
|
+
planVersion: num(row, "plan_version"),
|
|
178
|
+
updatedAt: dateOf(row, "updated_at")
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/db/sqlite/adapter.ts
|
|
183
|
+
var ACTIVE_STATUS_PLACEHOLDERS = PW_ACTIVE_SUBSCRIPTION_STATUSES.map(() => "?").join(", ");
|
|
184
|
+
function stableStringify(value) {
|
|
185
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
186
|
+
if (value !== null && typeof value === "object") {
|
|
187
|
+
const entries = Object.entries(value).sort(
|
|
188
|
+
([a], [b]) => a < b ? -1 : a > b ? 1 : 0
|
|
189
|
+
);
|
|
190
|
+
return `{${entries.map(([key, val]) => `${JSON.stringify(key)}:${stableStringify(val)}`).join(",")}}`;
|
|
191
|
+
}
|
|
192
|
+
return JSON.stringify(value);
|
|
193
|
+
}
|
|
194
|
+
var planContent = (p) => ({
|
|
195
|
+
group: p.group,
|
|
196
|
+
isDefault: p.isDefault,
|
|
197
|
+
name: p.name,
|
|
198
|
+
priceMinor: p.priceMinor,
|
|
199
|
+
priceCurrency: p.priceCurrency,
|
|
200
|
+
priceInterval: p.priceInterval,
|
|
201
|
+
features: p.features,
|
|
202
|
+
providerRefs: p.providerRefs
|
|
203
|
+
});
|
|
204
|
+
function planContentEquals(active, input) {
|
|
205
|
+
return stableStringify(planContent(active)) === stableStringify(planContent(input));
|
|
206
|
+
}
|
|
207
|
+
var SUBSCRIPTION_PATCH_FIELDS = [
|
|
208
|
+
{ key: "planId", column: "plan_id", encode: (v) => v },
|
|
209
|
+
{ key: "planVersion", column: "plan_version", encode: (v) => v },
|
|
210
|
+
{ key: "status", column: "status", encode: (v) => v },
|
|
211
|
+
{ key: "provider", column: "provider", encode: (v) => v },
|
|
212
|
+
{ key: "providerSubscriptionRef", column: "provider_subscription_ref", encode: (v) => v },
|
|
213
|
+
{ key: "currentPeriodStart", column: "current_period_start", encode: (v) => encodeDate(v) },
|
|
214
|
+
{ key: "currentPeriodEnd", column: "current_period_end", encode: (v) => encodeDate(v) },
|
|
215
|
+
{ key: "cancelAtPeriodEnd", column: "cancel_at_period_end", encode: (v) => encodeBool(v) }
|
|
216
|
+
];
|
|
217
|
+
function buildAdapter(runner) {
|
|
218
|
+
return {
|
|
219
|
+
dialect: "sqlite",
|
|
220
|
+
customers: {
|
|
221
|
+
getByExternalId: (externalId) => customersGetByExternalId(runner, externalId),
|
|
222
|
+
upsert: (input) => customersUpsert(runner, input),
|
|
223
|
+
linkProviderRef: (externalId, provider, ref) => customersLinkProviderRef(runner, externalId, provider, ref)
|
|
224
|
+
},
|
|
225
|
+
plans: {
|
|
226
|
+
getActiveVersion: (planId) => plansGetActiveVersion(runner, planId),
|
|
227
|
+
listActive: () => plansListActive(runner),
|
|
228
|
+
pushVersion: (plan) => plansPushVersion(runner, plan)
|
|
229
|
+
},
|
|
230
|
+
subscriptions: {
|
|
231
|
+
getActive: (customerId, group) => subscriptionsGetActive(runner, customerId, group),
|
|
232
|
+
create: (input) => subscriptionsCreate(runner, input),
|
|
233
|
+
update: (id, patch) => subscriptionsUpdate(runner, id, patch)
|
|
234
|
+
},
|
|
235
|
+
balances: {
|
|
236
|
+
get: (customerId, featureId, group) => balancesGet(runner, customerId, featureId, group),
|
|
237
|
+
consume: (input) => balancesConsume(runner, input),
|
|
238
|
+
resetTo: (customerId, featureId, group, init) => balancesResetTo(runner, customerId, featureId, group, init)
|
|
239
|
+
},
|
|
240
|
+
webhookEvents: {
|
|
241
|
+
claim: (dedupeKey, meta) => webhookEventsClaim(runner, dedupeKey, meta),
|
|
242
|
+
markApplied: (dedupeKey) => webhookEventsMarkApplied(runner, dedupeKey)
|
|
243
|
+
},
|
|
244
|
+
migrations: {
|
|
245
|
+
status: () => planMigrations(new SqliteMigrationExecutor(runner), "sqlite"),
|
|
246
|
+
apply: () => applyMigrations(new SqliteMigrationExecutor(runner), "sqlite")
|
|
247
|
+
},
|
|
248
|
+
transaction: (fn) => runner.transaction((tx) => fn(buildAdapter(tx)))
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
async function customersGetByExternalId(runner, externalId) {
|
|
252
|
+
const { rows } = await runner.execute(
|
|
253
|
+
`SELECT * FROM ${PW_TABLES.customers} WHERE external_id = ?`,
|
|
254
|
+
[externalId]
|
|
255
|
+
);
|
|
256
|
+
const row = rows[0];
|
|
257
|
+
return row ? rowToCustomer(row) : null;
|
|
258
|
+
}
|
|
259
|
+
async function customersUpsert(runner, input) {
|
|
260
|
+
const now = /* @__PURE__ */ new Date();
|
|
261
|
+
const id = generatePwId(now.getTime());
|
|
262
|
+
try {
|
|
263
|
+
const { rows } = await runner.execute(
|
|
264
|
+
`INSERT INTO ${PW_TABLES.customers} (id, external_id, provider_ids, email, created_at, updated_at)
|
|
265
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
266
|
+
ON CONFLICT(external_id) DO UPDATE SET
|
|
267
|
+
email = COALESCE(excluded.email, ${PW_TABLES.customers}.email),
|
|
268
|
+
updated_at = excluded.updated_at
|
|
269
|
+
RETURNING *`,
|
|
270
|
+
[id, input.externalId, encodeJson({}), input.email ?? null, encodeDate(now), encodeDate(now)]
|
|
271
|
+
);
|
|
272
|
+
const row = rows[0];
|
|
273
|
+
if (!row) throw new Error("sqlite adapter: customers.upsert returned no row");
|
|
274
|
+
return rowToCustomer(row);
|
|
275
|
+
} catch (error) {
|
|
276
|
+
throw wrapDriverError(
|
|
277
|
+
error,
|
|
278
|
+
`payweave/db/sqlite: customers.upsert(${JSON.stringify(input.externalId)}) failed.`
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async function customersLinkProviderRef(runner, externalId, provider, ref) {
|
|
283
|
+
await runner.transaction(async (tx) => {
|
|
284
|
+
const { rows } = await tx.execute(
|
|
285
|
+
`SELECT * FROM ${PW_TABLES.customers} WHERE external_id = ?`,
|
|
286
|
+
[externalId]
|
|
287
|
+
);
|
|
288
|
+
const row = rows[0];
|
|
289
|
+
if (!row) {
|
|
290
|
+
throw new PayweaveNotFoundError(
|
|
291
|
+
`payweave/db/sqlite: customers.linkProviderRef: no customer with externalId ${JSON.stringify(externalId)}.`
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
const customer = rowToCustomer(row);
|
|
295
|
+
const providerIds = { ...customer.providerIds, [provider]: ref };
|
|
296
|
+
await tx.execute(
|
|
297
|
+
`UPDATE ${PW_TABLES.customers} SET provider_ids = ?, updated_at = ? WHERE external_id = ?`,
|
|
298
|
+
[encodeJson(providerIds), encodeDate(/* @__PURE__ */ new Date()), externalId]
|
|
299
|
+
);
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
async function plansGetActiveVersion(runner, planId) {
|
|
303
|
+
const { rows } = await runner.execute(
|
|
304
|
+
`SELECT * FROM ${PW_TABLES.plans} WHERE plan_id = ? ORDER BY version DESC LIMIT 1`,
|
|
305
|
+
[planId]
|
|
306
|
+
);
|
|
307
|
+
const row = rows[0];
|
|
308
|
+
return row ? rowToPlanVersion(row) : null;
|
|
309
|
+
}
|
|
310
|
+
async function plansListActive(runner) {
|
|
311
|
+
const { rows } = await runner.execute(
|
|
312
|
+
`SELECT p.* FROM ${PW_TABLES.plans} p
|
|
313
|
+
INNER JOIN (
|
|
314
|
+
SELECT plan_id, MAX(version) AS version FROM ${PW_TABLES.plans} GROUP BY plan_id
|
|
315
|
+
) latest ON latest.plan_id = p.plan_id AND latest.version = p.version`
|
|
316
|
+
);
|
|
317
|
+
return rows.map(rowToPlanVersion);
|
|
318
|
+
}
|
|
319
|
+
async function plansPushVersion(runner, plan) {
|
|
320
|
+
return runner.transaction(async (tx) => {
|
|
321
|
+
const { rows } = await tx.execute(
|
|
322
|
+
`SELECT * FROM ${PW_TABLES.plans} WHERE plan_id = ? ORDER BY version DESC LIMIT 1`,
|
|
323
|
+
[plan.planId]
|
|
324
|
+
);
|
|
325
|
+
const activeRow = rows[0];
|
|
326
|
+
const active = activeRow ? rowToPlanVersion(activeRow) : null;
|
|
327
|
+
if (active && planContentEquals(active, plan)) {
|
|
328
|
+
return active;
|
|
329
|
+
}
|
|
330
|
+
const now = /* @__PURE__ */ new Date();
|
|
331
|
+
const id = generatePwId(now.getTime());
|
|
332
|
+
const nextVersion = (active?.version ?? 0) + 1;
|
|
333
|
+
const { rows: inserted } = await tx.execute(
|
|
334
|
+
`INSERT INTO ${PW_TABLES.plans}
|
|
335
|
+
(id, plan_id, version, "group", is_default, name, price_minor, price_currency, price_interval, features, provider_refs, pushed_at)
|
|
336
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
337
|
+
RETURNING *`,
|
|
338
|
+
[
|
|
339
|
+
id,
|
|
340
|
+
plan.planId,
|
|
341
|
+
nextVersion,
|
|
342
|
+
plan.group,
|
|
343
|
+
encodeBool(plan.isDefault),
|
|
344
|
+
plan.name,
|
|
345
|
+
plan.priceMinor,
|
|
346
|
+
plan.priceCurrency,
|
|
347
|
+
plan.priceInterval,
|
|
348
|
+
encodeJson(plan.features),
|
|
349
|
+
encodeJson(plan.providerRefs),
|
|
350
|
+
encodeDate(now)
|
|
351
|
+
]
|
|
352
|
+
);
|
|
353
|
+
const row = inserted[0];
|
|
354
|
+
if (!row) throw new Error("sqlite adapter: plans.pushVersion returned no row");
|
|
355
|
+
return rowToPlanVersion(row);
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
async function subscriptionsGetActive(runner, customerId, group) {
|
|
359
|
+
const { rows } = await runner.execute(
|
|
360
|
+
`SELECT * FROM ${PW_TABLES.subscriptions}
|
|
361
|
+
WHERE customer_id = ? AND "group" = ? AND status IN (${ACTIVE_STATUS_PLACEHOLDERS})`,
|
|
362
|
+
[customerId, group, ...PW_ACTIVE_SUBSCRIPTION_STATUSES]
|
|
363
|
+
);
|
|
364
|
+
const row = rows[0];
|
|
365
|
+
return row ? rowToSubscription(row) : null;
|
|
366
|
+
}
|
|
367
|
+
async function subscriptionsCreate(runner, input) {
|
|
368
|
+
const now = /* @__PURE__ */ new Date();
|
|
369
|
+
const id = generatePwId(now.getTime());
|
|
370
|
+
try {
|
|
371
|
+
const { rows } = await runner.execute(
|
|
372
|
+
`INSERT INTO ${PW_TABLES.subscriptions}
|
|
373
|
+
(id, customer_id, plan_id, plan_version, "group", status, provider, provider_subscription_ref,
|
|
374
|
+
current_period_start, current_period_end, cancel_at_period_end, created_at, updated_at)
|
|
375
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
376
|
+
RETURNING *`,
|
|
377
|
+
[
|
|
378
|
+
id,
|
|
379
|
+
input.customerId,
|
|
380
|
+
input.planId,
|
|
381
|
+
input.planVersion,
|
|
382
|
+
input.group,
|
|
383
|
+
input.status,
|
|
384
|
+
input.provider,
|
|
385
|
+
input.providerSubscriptionRef,
|
|
386
|
+
encodeDate(input.currentPeriodStart),
|
|
387
|
+
encodeDate(input.currentPeriodEnd),
|
|
388
|
+
encodeBool(input.cancelAtPeriodEnd),
|
|
389
|
+
encodeDate(now),
|
|
390
|
+
encodeDate(now)
|
|
391
|
+
]
|
|
392
|
+
);
|
|
393
|
+
const row = rows[0];
|
|
394
|
+
if (!row) throw new Error("sqlite adapter: subscriptions.create returned no row");
|
|
395
|
+
return rowToSubscription(row);
|
|
396
|
+
} catch (error) {
|
|
397
|
+
throw wrapDriverError(
|
|
398
|
+
error,
|
|
399
|
+
`payweave/db/sqlite: subscriptions.create for customer ${JSON.stringify(input.customerId)} group ${JSON.stringify(input.group)} violated a uniqueness rule (an active-set subscription already exists for this customer/group, or another constraint failed).`
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
async function subscriptionsUpdate(runner, id, patch) {
|
|
404
|
+
const sets = [];
|
|
405
|
+
const params = [];
|
|
406
|
+
const patchRecord = patch;
|
|
407
|
+
for (const field of SUBSCRIPTION_PATCH_FIELDS) {
|
|
408
|
+
if (!(field.key in patch) || patchRecord[field.key] === void 0) continue;
|
|
409
|
+
sets.push(`${field.column} = ?`);
|
|
410
|
+
params.push(field.encode(patchRecord[field.key]));
|
|
411
|
+
}
|
|
412
|
+
sets.push("updated_at = ?");
|
|
413
|
+
params.push(encodeDate(/* @__PURE__ */ new Date()));
|
|
414
|
+
params.push(id);
|
|
415
|
+
try {
|
|
416
|
+
const { rows } = await runner.execute(
|
|
417
|
+
`UPDATE ${PW_TABLES.subscriptions} SET ${sets.join(", ")} WHERE id = ? RETURNING *`,
|
|
418
|
+
params
|
|
419
|
+
);
|
|
420
|
+
const row = rows[0];
|
|
421
|
+
if (!row) {
|
|
422
|
+
throw new PayweaveNotFoundError(
|
|
423
|
+
`payweave/db/sqlite: subscriptions.update: no subscription with id ${JSON.stringify(id)}.`
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
return rowToSubscription(row);
|
|
427
|
+
} catch (error) {
|
|
428
|
+
if (error instanceof PayweaveNotFoundError) throw error;
|
|
429
|
+
throw wrapDriverError(error, `payweave/db/sqlite: subscriptions.update(${JSON.stringify(id)}) failed.`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
async function balancesGet(runner, customerId, featureId, group) {
|
|
433
|
+
const { rows } = await runner.execute(
|
|
434
|
+
`SELECT * FROM ${PW_TABLES.featureBalances} WHERE customer_id = ? AND feature_id = ? AND "group" = ?`,
|
|
435
|
+
[customerId, featureId, group]
|
|
436
|
+
);
|
|
437
|
+
const row = rows[0];
|
|
438
|
+
return row ? rowToFeatureBalance(row) : null;
|
|
439
|
+
}
|
|
440
|
+
async function balancesConsume(runner, input) {
|
|
441
|
+
return runner.transaction(async (tx) => {
|
|
442
|
+
const { rows } = await tx.execute(
|
|
443
|
+
`SELECT * FROM ${PW_TABLES.featureBalances} WHERE customer_id = ? AND feature_id = ? AND "group" = ?`,
|
|
444
|
+
[input.customerId, input.featureId, input.group]
|
|
445
|
+
);
|
|
446
|
+
const existingRow = rows[0];
|
|
447
|
+
const nowMs = input.now.getTime();
|
|
448
|
+
if (!existingRow) {
|
|
449
|
+
const period2 = currentPeriod(input.init.anchor.getTime(), input.init.resetInterval, nowMs);
|
|
450
|
+
const remaining2 = input.init.limit;
|
|
451
|
+
const applied2 = input.conditional !== true || remaining2 >= input.amount;
|
|
452
|
+
const used = applied2 ? input.amount : 0;
|
|
453
|
+
const id = generatePwId(nowMs);
|
|
454
|
+
const { rows: inserted } = await tx.execute(
|
|
455
|
+
`INSERT INTO ${PW_TABLES.featureBalances}
|
|
456
|
+
(id, customer_id, feature_id, "group", used, "limit", reset_interval, anchor, period_start, period_end, plan_id, plan_version, updated_at)
|
|
457
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
458
|
+
RETURNING *`,
|
|
459
|
+
[
|
|
460
|
+
id,
|
|
461
|
+
input.customerId,
|
|
462
|
+
input.featureId,
|
|
463
|
+
input.group,
|
|
464
|
+
used,
|
|
465
|
+
input.init.limit,
|
|
466
|
+
input.init.resetInterval,
|
|
467
|
+
encodeDate(input.init.anchor),
|
|
468
|
+
period2.start,
|
|
469
|
+
period2.end,
|
|
470
|
+
input.init.planId,
|
|
471
|
+
input.init.planVersion,
|
|
472
|
+
encodeDate(input.now)
|
|
473
|
+
]
|
|
474
|
+
);
|
|
475
|
+
const row = inserted[0];
|
|
476
|
+
if (!row) throw new Error("sqlite adapter: balances.consume (create) returned no row");
|
|
477
|
+
return { ...rowToFeatureBalance(row), applied: applied2 };
|
|
478
|
+
}
|
|
479
|
+
const existing = rowToFeatureBalance(existingRow);
|
|
480
|
+
const resetDue = nowMs >= existing.periodEnd.getTime();
|
|
481
|
+
const baseUsed = resetDue ? 0 : existing.used;
|
|
482
|
+
const period = resetDue ? currentPeriod(existing.anchor.getTime(), existing.resetInterval, nowMs) : { start: existing.periodStart.getTime(), end: existing.periodEnd.getTime() };
|
|
483
|
+
const remaining = existing.limit - baseUsed;
|
|
484
|
+
const applied = input.conditional !== true || remaining >= input.amount;
|
|
485
|
+
if (!applied && !resetDue) {
|
|
486
|
+
return { ...existing, applied: false };
|
|
487
|
+
}
|
|
488
|
+
const finalUsed = applied ? baseUsed + input.amount : baseUsed;
|
|
489
|
+
const { rows: updatedRows } = await tx.execute(
|
|
490
|
+
`UPDATE ${PW_TABLES.featureBalances}
|
|
491
|
+
SET used = ?, period_start = ?, period_end = ?, updated_at = ?
|
|
492
|
+
WHERE id = ?
|
|
493
|
+
RETURNING *`,
|
|
494
|
+
[finalUsed, period.start, period.end, encodeDate(input.now), existing.id]
|
|
495
|
+
);
|
|
496
|
+
const updatedRow = updatedRows[0];
|
|
497
|
+
if (!updatedRow) throw new Error("sqlite adapter: balances.consume (update) returned no row");
|
|
498
|
+
return { ...rowToFeatureBalance(updatedRow), applied };
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
async function balancesResetTo(runner, customerId, featureId, group, init) {
|
|
502
|
+
const now = /* @__PURE__ */ new Date();
|
|
503
|
+
const period = currentPeriod(init.anchor.getTime(), init.resetInterval, now.getTime());
|
|
504
|
+
const id = generatePwId(now.getTime());
|
|
505
|
+
await runner.execute(
|
|
506
|
+
`INSERT INTO ${PW_TABLES.featureBalances}
|
|
507
|
+
(id, customer_id, feature_id, "group", used, "limit", reset_interval, anchor, period_start, period_end, plan_id, plan_version, updated_at)
|
|
508
|
+
VALUES (?, ?, ?, ?, 0, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
509
|
+
ON CONFLICT(customer_id, feature_id, "group") DO UPDATE SET
|
|
510
|
+
used = 0,
|
|
511
|
+
"limit" = excluded."limit",
|
|
512
|
+
reset_interval = excluded.reset_interval,
|
|
513
|
+
anchor = excluded.anchor,
|
|
514
|
+
period_start = excluded.period_start,
|
|
515
|
+
period_end = excluded.period_end,
|
|
516
|
+
plan_id = excluded.plan_id,
|
|
517
|
+
plan_version = excluded.plan_version,
|
|
518
|
+
updated_at = excluded.updated_at`,
|
|
519
|
+
[
|
|
520
|
+
id,
|
|
521
|
+
customerId,
|
|
522
|
+
featureId,
|
|
523
|
+
group,
|
|
524
|
+
init.limit,
|
|
525
|
+
init.resetInterval,
|
|
526
|
+
encodeDate(init.anchor),
|
|
527
|
+
period.start,
|
|
528
|
+
period.end,
|
|
529
|
+
init.planId,
|
|
530
|
+
init.planVersion,
|
|
531
|
+
encodeDate(now)
|
|
532
|
+
]
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
async function webhookEventsClaim(runner, dedupeKey, meta) {
|
|
536
|
+
const staleClaimAfterMs = meta.staleClaimAfterMs ?? DEFAULT_STALE_CLAIM_AFTER_MS;
|
|
537
|
+
const nowMs = meta.now.getTime();
|
|
538
|
+
const staleThreshold = nowMs - staleClaimAfterMs;
|
|
539
|
+
const { rows } = await runner.execute(
|
|
540
|
+
`INSERT INTO ${PW_TABLES.webhookEvents} (dedupe_key, provider, type, received_at, claimed_at, applied_at)
|
|
541
|
+
VALUES (?, ?, ?, ?, ?, NULL)
|
|
542
|
+
ON CONFLICT(dedupe_key) DO UPDATE SET claimed_at = excluded.claimed_at
|
|
543
|
+
WHERE ${PW_TABLES.webhookEvents}.applied_at IS NULL
|
|
544
|
+
AND ${PW_TABLES.webhookEvents}.claimed_at <= ?
|
|
545
|
+
RETURNING dedupe_key`,
|
|
546
|
+
[dedupeKey, meta.provider, meta.type, nowMs, nowMs, staleThreshold]
|
|
547
|
+
);
|
|
548
|
+
return rows.length > 0;
|
|
549
|
+
}
|
|
550
|
+
async function webhookEventsMarkApplied(runner, dedupeKey) {
|
|
551
|
+
await runner.execute(
|
|
552
|
+
`UPDATE ${PW_TABLES.webhookEvents} SET applied_at = ? WHERE dedupe_key = ?`,
|
|
553
|
+
[Date.now(), dedupeKey]
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// src/db/sqlite/drivers/better-sqlite3.ts
|
|
558
|
+
var BetterSqlite3Raw = class {
|
|
559
|
+
#db;
|
|
560
|
+
constructor(db) {
|
|
561
|
+
this.#db = db;
|
|
562
|
+
}
|
|
563
|
+
async exec(sql, params = []) {
|
|
564
|
+
const statement = this.#db.prepare(sql);
|
|
565
|
+
if (statement.reader) {
|
|
566
|
+
return { rows: statement.all(...params) };
|
|
567
|
+
}
|
|
568
|
+
statement.run(...params);
|
|
569
|
+
return { rows: [] };
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
function sqliteFileUrlToPath(url) {
|
|
573
|
+
const withoutScheme = url.slice("file:".length);
|
|
574
|
+
return withoutScheme.startsWith("//") ? withoutScheme.slice(2) : withoutScheme;
|
|
575
|
+
}
|
|
576
|
+
function openBetterSqlite3(DatabaseCtor, url) {
|
|
577
|
+
const path = url === ":memory:" ? ":memory:" : url.startsWith("file:") ? sqliteFileUrlToPath(url) : url;
|
|
578
|
+
const db = new DatabaseCtor(path);
|
|
579
|
+
if (path !== ":memory:") {
|
|
580
|
+
db.pragma("journal_mode = WAL");
|
|
581
|
+
db.pragma("busy_timeout = 5000");
|
|
582
|
+
}
|
|
583
|
+
return db;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// src/db/sqlite/drivers/libsql.ts
|
|
587
|
+
var LibsqlRaw = class {
|
|
588
|
+
#client;
|
|
589
|
+
constructor(client) {
|
|
590
|
+
this.#client = client;
|
|
591
|
+
}
|
|
592
|
+
async exec(sql, params = []) {
|
|
593
|
+
const result = await this.#client.execute({ sql, args: params });
|
|
594
|
+
return { rows: result.rows };
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
// src/db/sqlite/runner.ts
|
|
599
|
+
var AutoQueueRunner = class {
|
|
600
|
+
#queue = Promise.resolve();
|
|
601
|
+
#getDriver;
|
|
602
|
+
constructor(getDriver) {
|
|
603
|
+
this.#getDriver = getDriver;
|
|
604
|
+
}
|
|
605
|
+
#enqueue(fn) {
|
|
606
|
+
const result = this.#queue.then(fn);
|
|
607
|
+
this.#queue = result.then(
|
|
608
|
+
() => void 0,
|
|
609
|
+
() => void 0
|
|
610
|
+
);
|
|
611
|
+
return result;
|
|
612
|
+
}
|
|
613
|
+
execute(sql, params = []) {
|
|
614
|
+
return this.#enqueue(async () => {
|
|
615
|
+
const driver = await this.#getDriver();
|
|
616
|
+
return driver.exec(sql, params);
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
transaction(fn) {
|
|
620
|
+
return this.#enqueue(async () => {
|
|
621
|
+
const driver = await this.#getDriver();
|
|
622
|
+
await driver.exec("BEGIN IMMEDIATE", []);
|
|
623
|
+
const txRunner = new TransactionRunner(driver);
|
|
624
|
+
try {
|
|
625
|
+
const result = await fn(txRunner);
|
|
626
|
+
await driver.exec("COMMIT", []);
|
|
627
|
+
return result;
|
|
628
|
+
} catch (error) {
|
|
629
|
+
await driver.exec("ROLLBACK", []).catch(() => void 0);
|
|
630
|
+
throw error;
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
var TransactionRunner = class {
|
|
636
|
+
#driver;
|
|
637
|
+
constructor(driver) {
|
|
638
|
+
this.#driver = driver;
|
|
639
|
+
}
|
|
640
|
+
execute(sql, params = []) {
|
|
641
|
+
return this.#driver.exec(sql, params);
|
|
642
|
+
}
|
|
643
|
+
// Reentrant: already inside one atomic unit, so nested `transaction()`
|
|
644
|
+
// calls (e.g. a user's `db.transaction(tx => tx.balances.consume(...))`)
|
|
645
|
+
// just run inline rather than attempting a nested BEGIN.
|
|
646
|
+
transaction(fn) {
|
|
647
|
+
return fn(this);
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
// src/db/sqlite/url.ts
|
|
652
|
+
var LIBSQL_URL_SCHEMES = ["libsql://", "wss://", "https://", "http://"];
|
|
653
|
+
function classifySqliteUrl(url) {
|
|
654
|
+
if (typeof url !== "string" || url.length === 0) {
|
|
655
|
+
throw new PayweaveConfigError(
|
|
656
|
+
`payweave/db/sqlite: sqliteAdapter({ url }) requires a non-empty string url \u2014 got ${JSON.stringify(url)}.`
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
if (url === ":memory:" || url.startsWith("file:")) {
|
|
660
|
+
return "better-sqlite3";
|
|
661
|
+
}
|
|
662
|
+
if (LIBSQL_URL_SCHEMES.some((scheme) => url.startsWith(scheme))) {
|
|
663
|
+
return "libsql";
|
|
664
|
+
}
|
|
665
|
+
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(url)) {
|
|
666
|
+
throw new PayweaveConfigError(
|
|
667
|
+
`payweave/db/sqlite: unsupported URL scheme in ${JSON.stringify(url)} \u2014 expected ":memory:", "file:...", a bare filesystem path, or a libSQL/Turso URL ("libsql://", "wss://", "https://", "http://").`
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
return "better-sqlite3";
|
|
671
|
+
}
|
|
672
|
+
function looksLikeBetterSqlite3(value) {
|
|
673
|
+
const candidate = value;
|
|
674
|
+
return typeof candidate.prepare === "function" && typeof candidate.pragma === "function";
|
|
675
|
+
}
|
|
676
|
+
function looksLikeLibsqlClient(value) {
|
|
677
|
+
const candidate = value;
|
|
678
|
+
return typeof candidate.execute === "function" && typeof candidate.batch === "function";
|
|
679
|
+
}
|
|
680
|
+
function resolveSqliteInput(input) {
|
|
681
|
+
if (input === null || typeof input !== "object") {
|
|
682
|
+
throw new PayweaveConfigError(
|
|
683
|
+
`payweave/db/sqlite: sqliteAdapter(...) expects { url } (a file:/:memory:/bare-path/libSQL URL), a better-sqlite3 Database instance, or an @libsql/client Client instance \u2014 got ${input === null ? "null" : typeof input}.`
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
if ("url" in input) {
|
|
687
|
+
const url = input.url;
|
|
688
|
+
if (typeof url !== "string") {
|
|
689
|
+
throw new PayweaveConfigError(
|
|
690
|
+
`payweave/db/sqlite: sqliteAdapter({ url }) requires url to be a string \u2014 got ${typeof url}.`
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
return { kind: "url", driver: classifySqliteUrl(url), url };
|
|
694
|
+
}
|
|
695
|
+
if (looksLikeBetterSqlite3(input)) {
|
|
696
|
+
return { kind: "better-sqlite3-instance", database: input };
|
|
697
|
+
}
|
|
698
|
+
if (looksLikeLibsqlClient(input)) {
|
|
699
|
+
return { kind: "libsql-instance", client: input };
|
|
700
|
+
}
|
|
701
|
+
throw new PayweaveConfigError(
|
|
702
|
+
"payweave/db/sqlite: sqliteAdapter(...) expects { url } (a file:/:memory:/bare-path/libSQL URL), a better-sqlite3 Database instance (has .prepare()/.pragma()), or an @libsql/client Client instance (has .execute()/.batch()) \u2014 got an object matching none of these shapes."
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// src/db/sqlite/index.ts
|
|
707
|
+
async function openBetterSqlite3Driver(url) {
|
|
708
|
+
let mod;
|
|
709
|
+
try {
|
|
710
|
+
mod = await import('better-sqlite3');
|
|
711
|
+
} catch (cause) {
|
|
712
|
+
throw installHintError("better-sqlite3", url, cause);
|
|
713
|
+
}
|
|
714
|
+
const database = openBetterSqlite3(mod.default, url);
|
|
715
|
+
return new BetterSqlite3Raw(database);
|
|
716
|
+
}
|
|
717
|
+
async function openLibsqlDriver(url) {
|
|
718
|
+
let mod;
|
|
719
|
+
try {
|
|
720
|
+
mod = await import('@libsql/client');
|
|
721
|
+
} catch (cause) {
|
|
722
|
+
throw installHintError("libsql", url, cause);
|
|
723
|
+
}
|
|
724
|
+
const client = mod.createClient({ url });
|
|
725
|
+
return new LibsqlRaw(client);
|
|
726
|
+
}
|
|
727
|
+
function connect(target) {
|
|
728
|
+
switch (target.kind) {
|
|
729
|
+
case "better-sqlite3-instance":
|
|
730
|
+
return Promise.resolve(new BetterSqlite3Raw(target.database));
|
|
731
|
+
case "libsql-instance":
|
|
732
|
+
return Promise.resolve(new LibsqlRaw(target.client));
|
|
733
|
+
case "url":
|
|
734
|
+
return target.driver === "better-sqlite3" ? openBetterSqlite3Driver(target.url) : openLibsqlDriver(target.url);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
function sqliteAdapter(input) {
|
|
738
|
+
const target = resolveSqliteInput(input);
|
|
739
|
+
let driverPromise;
|
|
740
|
+
const getDriver = () => {
|
|
741
|
+
driverPromise ??= connect(target);
|
|
742
|
+
return driverPromise;
|
|
743
|
+
};
|
|
744
|
+
const runner = new AutoQueueRunner(getDriver);
|
|
745
|
+
return buildAdapter(runner);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
export { sqliteAdapter };
|
|
749
|
+
//# sourceMappingURL=index.js.map
|
|
750
|
+
//# sourceMappingURL=index.js.map
|