@drawbridge/drawbridge-utils 0.0.42 → 0.0.44

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.
@@ -0,0 +1,381 @@
1
+ // lib/oauth/server.js
2
+ import OAuth2Server from "@node-oauth/oauth2-server";
3
+
4
+ // lib/token.js
5
+ import crypto from "crypto";
6
+ var generate = (bytes = 32, encoding = "base64url") => {
7
+ const buf = crypto.randomBytes(bytes);
8
+ return encoding ? buf.toString(encoding) : buf;
9
+ };
10
+ var hash = (value, key) => {
11
+ if (!value) return null;
12
+ if (key) {
13
+ return crypto.createHmac("sha256", key).update(String(value)).digest("base64url");
14
+ }
15
+ return crypto.createHash("sha256").update(String(value)).digest("base64url");
16
+ };
17
+ var compare = (a, b) => {
18
+ if (typeof a !== "string" || typeof b !== "string") return false;
19
+ if (a.length !== b.length) return false;
20
+ return crypto.timingSafeEqual(
21
+ Buffer.from(a),
22
+ Buffer.from(b)
23
+ );
24
+ };
25
+
26
+ // lib/oauth/index.js
27
+ var scopes = [
28
+ "profile:read",
29
+ "profile:write",
30
+ "organization:read",
31
+ "organization:write",
32
+ "campaigns:read",
33
+ "campaigns:write",
34
+ "contacts:read",
35
+ "contacts:write",
36
+ "workflows:read",
37
+ "workflows:write",
38
+ "connections:read",
39
+ "connections:write",
40
+ "billing:read",
41
+ "billing:write",
42
+ "admin"
43
+ ];
44
+ var developer = [
45
+ "profile:read",
46
+ "organization:read",
47
+ "campaigns:read",
48
+ "campaigns:write",
49
+ "contacts:read",
50
+ "contacts:write",
51
+ "workflows:read",
52
+ "workflows:write",
53
+ "connections:read",
54
+ "connections:write"
55
+ ];
56
+ var parse = (raw) => {
57
+ if (!raw) return [];
58
+ if (Array.isArray(raw)) return raw;
59
+ return String(raw).split(/\s+/).filter(Boolean);
60
+ };
61
+ var intersect = (requested, allowed) => {
62
+ const r = parse(requested);
63
+ const a = parse(allowed);
64
+ return r.filter((scope) => a.includes(scope));
65
+ };
66
+ var scopeLabels = {
67
+ "profile:read": "Read own profile",
68
+ "profile:write": "Update own profile",
69
+ "organization:read": "Read organization info",
70
+ "organization:write": "Manage organization",
71
+ "campaigns:read": "Read campaigns",
72
+ "campaigns:write": "Manage campaigns",
73
+ "contacts:read": "Read contacts",
74
+ "contacts:write": "Manage contacts",
75
+ "workflows:read": "Read workflows",
76
+ "workflows:write": "Manage workflows",
77
+ "connections:read": "Read integrations",
78
+ "connections:write": "Manage integrations",
79
+ "billing:read": "Read billing",
80
+ "billing:write": "Manage billing",
81
+ admin: "Admin access"
82
+ };
83
+ var toOption = (scope) => ({
84
+ key: scopeLabels[scope] || scope,
85
+ value: scope
86
+ });
87
+ var developerOptions = developer.map(toOption);
88
+ var scopeOptions = scopes.map(toOption);
89
+ var hashToken = (raw) => hash(raw, process.env.OAUTH_TOKEN_HMAC_KEY);
90
+ var generateToken = () => generate(32, "base64url");
91
+
92
+ // lib/oauth/server.js
93
+ var ACCESS_TOKEN_LIFETIME_SECONDS = 60 * 60;
94
+ var REFRESH_TOKEN_LIFETIME_SECONDS = 60 * 60 * 24 * 30;
95
+ var AUTHORIZATION_CODE_LIFETIME_MS = 10 * 60 * 1e3;
96
+ var createOAuthModel = ({ controller }) => ({
97
+ getClient: async (clientId, clientSecret) => {
98
+ var _a;
99
+ const client = await controller.get({
100
+ collection: "oauth",
101
+ query: {
102
+ clientId,
103
+ status: "active"
104
+ }
105
+ });
106
+ if (!client) return false;
107
+ if (clientSecret) {
108
+ const validSecret = (_a = client.clientSecretHashes) == null ? void 0 : _a.some((entry) => {
109
+ if (entry.retiredAt) return false;
110
+ return compare(hashToken(clientSecret), entry.hash);
111
+ });
112
+ if (!validSecret) return false;
113
+ }
114
+ ;
115
+ return {
116
+ clientId: client.clientId,
117
+ grants: ["authorization_code", "client_credentials", "refresh_token"],
118
+ id: client.id,
119
+ redirectUris: client.redirectUris ?? [],
120
+ scopes: client.allowedScopes ?? []
121
+ };
122
+ },
123
+ saveAuthorizationCode: async (code, client, user) => {
124
+ const rawCode = code.authorizationCode;
125
+ const tokenHash = hashToken(rawCode);
126
+ const expiresAt = new Date(Date.now() + AUTHORIZATION_CODE_LIFETIME_MS);
127
+ await controller.create({
128
+ collection: "otc",
129
+ data: {
130
+ client: client.clientId,
131
+ codeChallenge: code.codeChallenge ?? null,
132
+ codeChallengeMethod: code.codeChallengeMethod ?? null,
133
+ consumedAt: null,
134
+ context: "oauth.authorize",
135
+ expiresAt,
136
+ redirectUri: code.redirectUri ?? null,
137
+ scopes: code.scope ?? [],
138
+ tokenHash,
139
+ user: user.id
140
+ }
141
+ });
142
+ return {
143
+ authorizationCode: rawCode,
144
+ client: { id: client.id },
145
+ expiresAt,
146
+ redirectUri: code.redirectUri,
147
+ scope: code.scope,
148
+ user: { id: user.id }
149
+ };
150
+ },
151
+ getAuthorizationCode: async (authorizationCode) => {
152
+ const tokenHash = hashToken(authorizationCode);
153
+ const now = /* @__PURE__ */ new Date();
154
+ const record = await controller.get({
155
+ collection: "otc",
156
+ query: {
157
+ consumedAt: null,
158
+ context: "oauth.authorize",
159
+ expiresAt: { $gt: now },
160
+ tokenHash
161
+ }
162
+ });
163
+ if (!record) return false;
164
+ return {
165
+ authorizationCode,
166
+ client: { id: record.client },
167
+ codeChallenge: record.codeChallenge,
168
+ codeChallengeMethod: record.codeChallengeMethod,
169
+ expiresAt: record.expiresAt,
170
+ redirectUri: record.redirectUri,
171
+ scope: record.scopes,
172
+ user: { id: record.user }
173
+ };
174
+ },
175
+ revokeAuthorizationCode: async (code) => {
176
+ const tokenHash = hashToken(code.authorizationCode);
177
+ await controller.update({
178
+ collection: "otc",
179
+ data: {
180
+ $set: {
181
+ consumedAt: /* @__PURE__ */ new Date()
182
+ }
183
+ },
184
+ query: {
185
+ context: "oauth.authorize",
186
+ tokenHash
187
+ }
188
+ });
189
+ return true;
190
+ },
191
+ saveToken: async (token, client, user) => {
192
+ const now = /* @__PURE__ */ new Date();
193
+ const accessRaw = token.accessToken;
194
+ const accessHash = hashToken(accessRaw);
195
+ const accessExpiresAt = token.accessTokenExpiresAt ?? new Date(now.getTime() + ACCESS_TOKEN_LIFETIME_SECONDS * 1e3);
196
+ await controller.create({
197
+ collection: "token",
198
+ data: {
199
+ client: client.clientId,
200
+ consumedAt: null,
201
+ data: {},
202
+ email: null,
203
+ expiresAt: accessExpiresAt,
204
+ lastUsedAt: null,
205
+ name: null,
206
+ revoked: false,
207
+ revokedAt: null,
208
+ rotatedFrom: null,
209
+ scopes: token.scope ?? [],
210
+ tokenHash: accessHash,
211
+ type: "access",
212
+ user: (user == null ? void 0 : user.id) ?? null
213
+ }
214
+ });
215
+ const result = {
216
+ accessToken: accessRaw,
217
+ accessTokenExpiresAt: accessExpiresAt,
218
+ client: { id: client.id },
219
+ scope: token.scope,
220
+ user: { id: (user == null ? void 0 : user.id) ?? null }
221
+ };
222
+ if (token.refreshToken) {
223
+ const refreshRaw = token.refreshToken;
224
+ const refreshHash = hashToken(refreshRaw);
225
+ const refreshExpiresAt = token.refreshTokenExpiresAt ?? new Date(now.getTime() + REFRESH_TOKEN_LIFETIME_SECONDS * 1e3);
226
+ await controller.create({
227
+ collection: "token",
228
+ data: {
229
+ client: client.clientId,
230
+ consumedAt: null,
231
+ data: {},
232
+ email: null,
233
+ expiresAt: refreshExpiresAt,
234
+ lastUsedAt: null,
235
+ name: null,
236
+ revoked: false,
237
+ revokedAt: null,
238
+ rotatedFrom: null,
239
+ scopes: token.scope ?? [],
240
+ tokenHash: refreshHash,
241
+ type: "refresh",
242
+ user: (user == null ? void 0 : user.id) ?? null
243
+ }
244
+ });
245
+ result.refreshToken = refreshRaw;
246
+ result.refreshTokenExpiresAt = refreshExpiresAt;
247
+ }
248
+ ;
249
+ return result;
250
+ },
251
+ getAccessToken: async (accessToken) => {
252
+ const tokenHash = hashToken(accessToken);
253
+ const now = /* @__PURE__ */ new Date();
254
+ const record = await controller.get({
255
+ collection: "token",
256
+ query: {
257
+ expiresAt: { $gt: now },
258
+ revoked: false,
259
+ tokenHash,
260
+ type: "access"
261
+ }
262
+ });
263
+ if (!record) return false;
264
+ return {
265
+ accessToken,
266
+ accessTokenExpiresAt: record.expiresAt,
267
+ client: { id: record.client },
268
+ scope: record.scopes,
269
+ user: { id: record.user }
270
+ };
271
+ },
272
+ getRefreshToken: async (refreshToken) => {
273
+ const tokenHash = hashToken(refreshToken);
274
+ const record = await controller.get({
275
+ collection: "token",
276
+ query: {
277
+ revoked: false,
278
+ tokenHash,
279
+ type: "refresh"
280
+ }
281
+ });
282
+ if (!record) return false;
283
+ return {
284
+ client: { id: record.client },
285
+ refreshToken,
286
+ refreshTokenExpiresAt: record.expiresAt,
287
+ scope: record.scopes,
288
+ user: { id: record.user }
289
+ };
290
+ },
291
+ revokeToken: async (token) => {
292
+ const rawToken = token.refreshToken ?? token.accessToken;
293
+ const tokenHash = hashToken(rawToken);
294
+ await controller.update({
295
+ collection: "token",
296
+ data: {
297
+ $set: {
298
+ revoked: true,
299
+ revokedAt: /* @__PURE__ */ new Date()
300
+ }
301
+ },
302
+ query: { tokenHash }
303
+ });
304
+ return true;
305
+ },
306
+ verifyScope: (token, scope) => {
307
+ const granted = intersect(scope, token.scope ?? []);
308
+ return granted.length > 0;
309
+ },
310
+ // client_credentials grant: treat the client as the resource owner.
311
+ getUserFromClient: (client) => ({ id: client.id, type: "client" })
312
+ });
313
+ var createOAuthServer = ({ controller }) => new OAuth2Server({
314
+ accessTokenLifetime: ACCESS_TOKEN_LIFETIME_SECONDS,
315
+ allowBearerTokensInQueryString: false,
316
+ model: createOAuthModel({ controller }),
317
+ refreshTokenLifetime: REFRESH_TOKEN_LIFETIME_SECONDS,
318
+ requireClientAuthentication: {
319
+ authorization_code: true,
320
+ client_credentials: true,
321
+ refresh_token: true
322
+ }
323
+ });
324
+ var createClient = async ({
325
+ allowedScopes = [],
326
+ controller,
327
+ createdBy = null,
328
+ description = null,
329
+ grantTypes = ["authorization_code"],
330
+ name,
331
+ organization = null,
332
+ redirectUris = [],
333
+ webOrigins = []
334
+ }) => {
335
+ const invalid = allowedScopes.filter((scope) => !developer.includes(scope));
336
+ if (invalid.length > 0) {
337
+ const error = new Error("Scopes not permitted for developer apps: " + invalid.join(", "));
338
+ error.status = 400;
339
+ throw error;
340
+ }
341
+ ;
342
+ const rawClientId = generateToken();
343
+ const clientId = "drawbridge." + rawClientId;
344
+ const rawSecret = generateToken();
345
+ const secretHash = hashToken(rawSecret);
346
+ const now = /* @__PURE__ */ new Date();
347
+ const doc = await controller.insert({
348
+ collection: "oauth",
349
+ data: {
350
+ allowedScopes,
351
+ clientId,
352
+ clientSecretHashes: [
353
+ {
354
+ createdAt: now,
355
+ hash: secretHash,
356
+ retiredAt: null
357
+ }
358
+ ],
359
+ clientType: "confidential",
360
+ createdBy,
361
+ description,
362
+ grantTypes,
363
+ name,
364
+ organization,
365
+ rateLimitOverrides: null,
366
+ redirectUris,
367
+ status: "active",
368
+ tokenEndpointAuthMethod: "client_secret_basic",
369
+ webOrigins
370
+ }
371
+ });
372
+ return {
373
+ client: doc,
374
+ rawSecret
375
+ };
376
+ };
377
+ export {
378
+ createClient,
379
+ createOAuthModel,
380
+ createOAuthServer
381
+ };
package/dist/orders.cjs CHANGED
@@ -16,7 +16,7 @@ var __copyProps = (to, from, except, desc) => {
16
16
  };
17
17
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
18
 
19
- // orders.js
19
+ // lib/orders.js
20
20
  var orders_exports = {};
21
21
  __export(orders_exports, {
22
22
  byCurrency: () => byCurrency
package/dist/orders.js CHANGED
@@ -1,4 +1,4 @@
1
- // orders.js
1
+ // lib/orders.js
2
2
  var byCurrency = ({ organization, period, onlyUnbilled = false } = {}) => {
3
3
  const match = {
4
4
  organization,
package/dist/redirect.cjs CHANGED
@@ -16,7 +16,7 @@ var __copyProps = (to, from, except, desc) => {
16
16
  };
17
17
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
18
 
19
- // redirect.js
19
+ // lib/redirect.js
20
20
  var redirect_exports = {};
21
21
  __export(redirect_exports, {
22
22
  safeRedirect: () => safeRedirect
package/dist/redirect.js CHANGED
@@ -1,4 +1,4 @@
1
- // redirect.js
1
+ // lib/redirect.js
2
2
  var SAFE = /^\/(?!\/)[^\\\r\n]*$/;
3
3
  var safeRedirect = (raw, fallback = "/") => {
4
4
  if (typeof raw !== "string") return fallback;
package/dist/shopify.cjs CHANGED
@@ -26,7 +26,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  ));
27
27
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
28
 
29
- // shopify.js
29
+ // lib/shopify.js
30
30
  var shopify_exports = {};
31
31
  __export(shopify_exports, {
32
32
  getAdminToken: () => getAdminToken,
@@ -40,17 +40,17 @@ __export(shopify_exports, {
40
40
  module.exports = __toCommonJS(shopify_exports);
41
41
  var import_crypto3 = __toESM(require("crypto"), 1);
42
42
 
43
- // encrypt.js
43
+ // lib/encrypt.js
44
44
  var import_crypto2 = __toESM(require("crypto"), 1);
45
45
 
46
- // token.js
46
+ // lib/token.js
47
47
  var import_crypto = __toESM(require("crypto"), 1);
48
48
  var generate = (bytes = 32, encoding = "base64url") => {
49
49
  const buf = import_crypto.default.randomBytes(bytes);
50
50
  return encoding ? buf.toString(encoding) : buf;
51
51
  };
52
52
 
53
- // encrypt.js
53
+ // lib/encrypt.js
54
54
  var ALGORITHM = "aes-256-gcm";
55
55
  var getKey = () => Buffer.from(process.env.ENCRYPT_CONNECTION_SECRET, "hex");
56
56
  var encrypt = (value) => {
@@ -79,7 +79,7 @@ var decrypt = (value) => {
79
79
  return JSON.parse(result.toString("utf8"));
80
80
  };
81
81
 
82
- // shopify.js
82
+ // lib/shopify.js
83
83
  var SHOPIFY_ADMIN_API_VERSION = "2025-01";
84
84
  var REFRESH_TOKEN_LIFETIME_MS = 90 * 24 * 60 * 60 * 1e3;
85
85
  var ACCESS_TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1e3;
package/dist/shopify.js CHANGED
@@ -1,11 +1,46 @@
1
- import {
2
- decrypt,
3
- encrypt
4
- } from "./chunk-2DRAIKGZ.js";
5
- import "./chunk-OS2AHUWX.js";
1
+ // lib/shopify.js
2
+ import crypto3 from "crypto";
6
3
 
7
- // shopify.js
4
+ // lib/encrypt.js
5
+ import crypto2 from "crypto";
6
+
7
+ // lib/token.js
8
8
  import crypto from "crypto";
9
+ var generate = (bytes = 32, encoding = "base64url") => {
10
+ const buf = crypto.randomBytes(bytes);
11
+ return encoding ? buf.toString(encoding) : buf;
12
+ };
13
+
14
+ // lib/encrypt.js
15
+ var ALGORITHM = "aes-256-gcm";
16
+ var getKey = () => Buffer.from(process.env.ENCRYPT_CONNECTION_SECRET, "hex");
17
+ var encrypt = (value) => {
18
+ const iv = generate(12, null);
19
+ const cipher = crypto2.createCipheriv(ALGORITHM, getKey(), iv);
20
+ const data = Buffer.concat([
21
+ cipher.update(JSON.stringify(value), "utf8"),
22
+ cipher.final()
23
+ ]);
24
+ const tag = cipher.getAuthTag();
25
+ return [iv, tag, data].map((b) => b.toString("hex")).join(":");
26
+ };
27
+ var decrypt = (value) => {
28
+ if (typeof value !== "string") return value;
29
+ const [ivHex, tagHex, dataHex] = value.split(":");
30
+ const decipher = crypto2.createDecipheriv(
31
+ ALGORITHM,
32
+ getKey(),
33
+ Buffer.from(ivHex, "hex")
34
+ );
35
+ decipher.setAuthTag(Buffer.from(tagHex, "hex"));
36
+ const result = Buffer.concat([
37
+ decipher.update(Buffer.from(dataHex, "hex")),
38
+ decipher.final()
39
+ ]);
40
+ return JSON.parse(result.toString("utf8"));
41
+ };
42
+
43
+ // lib/shopify.js
9
44
  var SHOPIFY_ADMIN_API_VERSION = "2025-01";
10
45
  var REFRESH_TOKEN_LIFETIME_MS = 90 * 24 * 60 * 60 * 1e3;
11
46
  var ACCESS_TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1e3;
@@ -16,16 +51,16 @@ var signOAuthState = ({ organizationId, shop }) => {
16
51
  organizationId,
17
52
  shop
18
53
  })).toString("base64url");
19
- const signature = crypto.createHmac("sha256", process.env.SHOPIFY_API_SECRET).update(payload).digest("base64url");
54
+ const signature = crypto3.createHmac("sha256", process.env.SHOPIFY_API_SECRET).update(payload).digest("base64url");
20
55
  return payload + "." + signature;
21
56
  };
22
57
  var verifyOAuthState = (raw) => {
23
58
  if (!raw) return null;
24
59
  const [payload, signature] = raw.split(".");
25
60
  if (!payload || !signature) return null;
26
- const expected = crypto.createHmac("sha256", process.env.SHOPIFY_API_SECRET).update(payload).digest("base64url");
61
+ const expected = crypto3.createHmac("sha256", process.env.SHOPIFY_API_SECRET).update(payload).digest("base64url");
27
62
  if (signature.length !== expected.length) return null;
28
- const valid = crypto.timingSafeEqual(
63
+ const valid = crypto3.timingSafeEqual(
29
64
  Buffer.from(signature),
30
65
  Buffer.from(expected)
31
66
  );
@@ -46,10 +81,10 @@ var verifyOAuthCallbackHmac = ({ hmac, rawQuery }) => {
46
81
  const nameB = b.split("=")[0];
47
82
  return nameA < nameB ? -1 : nameA > nameB ? 1 : 0;
48
83
  }).join("&");
49
- const digest = crypto.createHmac("sha256", process.env.SHOPIFY_API_SECRET).update(message).digest("hex");
84
+ const digest = crypto3.createHmac("sha256", process.env.SHOPIFY_API_SECRET).update(message).digest("hex");
50
85
  const digestBuf = Buffer.from(digest);
51
86
  const hmacBuf = Buffer.from(hmac || "");
52
- return digestBuf.length === hmacBuf.length && crypto.timingSafeEqual(digestBuf, hmacBuf);
87
+ return digestBuf.length === hmacBuf.length && crypto3.timingSafeEqual(digestBuf, hmacBuf);
53
88
  };
54
89
  var shopifyOAuthFetch = async (url, body) => {
55
90
  const response = await fetch(url, {
package/dist/slugify.cjs CHANGED
@@ -26,7 +26,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  ));
27
27
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
28
 
29
- // slugify.js
29
+ // lib/slugify.js
30
30
  var slugify_exports = {};
31
31
  __export(slugify_exports, {
32
32
  slugify: () => slugify
package/dist/slugify.js CHANGED
@@ -1,4 +1,4 @@
1
- // slugify.js
1
+ // lib/slugify.js
2
2
  import slug from "slugify";
3
3
  var slugify = (value, nochars = false) => {
4
4
  const remove = nochars ? /[-?*+~.()'"!:@#^_{}\[\];,/\\]/g : /[?*+~.()'"!:@#^_{}\[\];,/\\]/g;
package/dist/token.cjs CHANGED
@@ -26,7 +26,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  ));
27
27
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
28
 
29
- // token.js
29
+ // lib/token.js
30
30
  var token_exports = {};
31
31
  __export(token_exports, {
32
32
  compare: () => compare,
package/dist/token.js CHANGED
@@ -1,8 +1,24 @@
1
- import {
2
- compare,
3
- generate,
4
- hash
5
- } from "./chunk-OS2AHUWX.js";
1
+ // lib/token.js
2
+ import crypto from "crypto";
3
+ var generate = (bytes = 32, encoding = "base64url") => {
4
+ const buf = crypto.randomBytes(bytes);
5
+ return encoding ? buf.toString(encoding) : buf;
6
+ };
7
+ var hash = (value, key) => {
8
+ if (!value) return null;
9
+ if (key) {
10
+ return crypto.createHmac("sha256", key).update(String(value)).digest("base64url");
11
+ }
12
+ return crypto.createHash("sha256").update(String(value)).digest("base64url");
13
+ };
14
+ var compare = (a, b) => {
15
+ if (typeof a !== "string" || typeof b !== "string") return false;
16
+ if (a.length !== b.length) return false;
17
+ return crypto.timingSafeEqual(
18
+ Buffer.from(a),
19
+ Buffer.from(b)
20
+ );
21
+ };
6
22
  export {
7
23
  compare,
8
24
  generate,
@@ -16,7 +16,7 @@ var __copyProps = (to, from, except, desc) => {
16
16
  };
17
17
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
18
 
19
- // transactions.js
19
+ // lib/transactions.js
20
20
  var transactions_exports = {};
21
21
  __export(transactions_exports, {
22
22
  credit: () => credit,