@drawbridge/drawbridge-utils 0.0.151 → 0.0.153
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/connections/index.cjs +312 -112
- package/dist/connections/index.d.cts +471 -135
- package/dist/connections/index.d.ts +471 -135
- package/dist/connections/index.js +309 -111
- package/dist/providers.cjs +323 -117
- package/dist/providers.d.cts +37 -15
- package/dist/providers.d.ts +37 -15
- package/dist/providers.js +320 -116
- package/package.json +1 -1
|
@@ -785,10 +785,16 @@ var attentive_default2 = {
|
|
|
785
785
|
// `redact` marks a secret — never returned by the api, and blank on save means
|
|
786
786
|
// keep the stored value. `required` drives the live check.
|
|
787
787
|
provider: {
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
788
|
+
vendors: {
|
|
789
|
+
attentive: {
|
|
790
|
+
fields: [
|
|
791
|
+
{ input: "text", key: "clientId", credential: "ATTENTIVE_OAUTH_CLIENT_ID", label: "Client ID", message: "From our app's registration in the Attentive developer portal.", required: true },
|
|
792
|
+
{ input: "password", key: "clientSecret", credential: "ATTENTIVE_OAUTH_CLIENT_SECRET", label: "Client secret", message: "Beside the Client ID in the Attentive developer portal.", redact: true, required: true }
|
|
793
|
+
],
|
|
794
|
+
icon: attentive_default,
|
|
795
|
+
name: "Attentive"
|
|
796
|
+
}
|
|
797
|
+
}
|
|
792
798
|
},
|
|
793
799
|
requires: [
|
|
794
800
|
"ATTENTIVE_OAUTH_CLIENT_ID",
|
|
@@ -1054,6 +1060,43 @@ var drawbridge_default = `<svg width="500" height="500" viewBox="0 0 500 500" fi
|
|
|
1054
1060
|
</defs>
|
|
1055
1061
|
</svg>`;
|
|
1056
1062
|
|
|
1063
|
+
// lib/connections/icons/hubspot.js
|
|
1064
|
+
var hubspot_default = `<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
1065
|
+
<rect width="500" height="500" fill="#FF7A59"/>
|
|
1066
|
+
<circle cx="290" cy="290" r="78" stroke="white" stroke-width="30"/>
|
|
1067
|
+
<circle cx="290" cy="290" r="18" fill="white"/>
|
|
1068
|
+
<path d="M290 212V150" stroke="white" stroke-width="30" stroke-linecap="round"/>
|
|
1069
|
+
<circle cx="290" cy="125" r="28" fill="white"/>
|
|
1070
|
+
<path d="M270 137L172 96" stroke="white" stroke-width="22" stroke-linecap="round"/>
|
|
1071
|
+
<circle cx="160" cy="91" r="20" fill="white"/>
|
|
1072
|
+
<path d="M233 347L184 396" stroke="white" stroke-width="26" stroke-linecap="round"/>
|
|
1073
|
+
<circle cx="172" cy="408" r="22" fill="white"/>
|
|
1074
|
+
</svg>`;
|
|
1075
|
+
|
|
1076
|
+
// lib/connections/icons/sendgrid.js
|
|
1077
|
+
var sendgrid_default = `<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
1078
|
+
<rect width="500" height="500" fill="#F1FAFF"/>
|
|
1079
|
+
<g clip-path="url(#clip0_1011_2743)">
|
|
1080
|
+
<path d="M366.676 134.534V289.172H289.203V366.487H134.258L134.258 289.171L134.256 289.172V211.851H211.729V134.534H366.676Z" fill="#9DD6E3"/>
|
|
1081
|
+
<path d="M134.258 366.488H211.731V289.169H134.258V366.488Z" fill="#3F72AB"/>
|
|
1082
|
+
<path d="M289.203 289.172H366.677V211.852H289.203V289.172Z" fill="#00A9D1"/>
|
|
1083
|
+
<path d="M211.729 211.853H289.202V134.534H211.729V211.853Z" fill="#00A9D1"/>
|
|
1084
|
+
<path d="M211.729 289.17H289.202V211.852H211.729V289.17Z" fill="#2191C4"/>
|
|
1085
|
+
<path d="M289.203 211.853H366.677V134.534H289.203V211.853Z" fill="#3F72AB"/>
|
|
1086
|
+
</g>
|
|
1087
|
+
<defs>
|
|
1088
|
+
<clipPath id="clip0_1011_2743">
|
|
1089
|
+
<rect width="232.42" height="231.956" fill="white" transform="translate(134.256 134.534)"/>
|
|
1090
|
+
</clipPath>
|
|
1091
|
+
</defs>
|
|
1092
|
+
</svg>`;
|
|
1093
|
+
|
|
1094
|
+
// lib/connections/icons/twilio.js
|
|
1095
|
+
var twilio_default = `<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
1096
|
+
<rect width="500" height="500" fill="#F22F46"/>
|
|
1097
|
+
<path d="M250 134.954C186.341 134.954 134.954 186.341 134.954 250C134.954 313.659 186.341 365.047 250 365.047C313.659 365.047 365.047 313.659 365.047 250C365.047 186.341 313.659 134.954 250 134.954ZM250 334.368C203.215 334.368 165.633 296.786 165.633 250C165.633 203.215 203.215 165.633 250 165.633C296.786 165.633 334.368 203.215 334.368 250C334.368 296.786 296.786 334.368 250 334.368ZM302.155 221.622C302.155 234.661 291.417 245.399 278.378 245.399C265.34 245.399 254.602 234.661 254.602 221.622C254.602 208.584 265.34 197.846 278.378 197.846C291.417 197.846 302.155 208.584 302.155 221.622ZM302.155 278.378C302.155 291.417 291.417 302.155 278.378 302.155C265.34 302.155 254.602 291.417 254.602 278.378C254.602 265.34 265.34 254.602 278.378 254.602C291.417 254.602 302.155 265.34 302.155 278.378ZM245.399 278.378C245.399 291.417 234.661 302.155 221.622 302.155C208.584 302.155 197.846 291.417 197.846 278.378C197.846 265.34 208.584 254.602 221.622 254.602C234.661 254.602 245.399 265.34 245.399 278.378ZM245.399 221.622C245.399 234.661 234.661 245.399 221.622 245.399C208.584 245.399 197.846 234.661 197.846 221.622C197.846 208.584 208.584 197.846 221.622 197.846C234.661 197.846 245.399 208.584 245.399 221.622Z" fill="white"/>
|
|
1098
|
+
</svg>`;
|
|
1099
|
+
|
|
1057
1100
|
// lib/transactions.js
|
|
1058
1101
|
import { currentTraceId } from "@drawbridge/drawbridge-telemetry";
|
|
1059
1102
|
|
|
@@ -2220,37 +2263,56 @@ var drawbridge_default2 = {
|
|
|
2220
2263
|
//
|
|
2221
2264
|
// They belong on THIS manifest because this is the connection that sends: the
|
|
2222
2265
|
// email, sms and segment hooks below are the only things that spend them, and
|
|
2223
|
-
// a private connection is still where a vendor fact lives.
|
|
2266
|
+
// a private connection is still where a vendor fact lives. They are THREE
|
|
2267
|
+
// VENDORS, not one, and each is its own row and its own admin card — so
|
|
2268
|
+
// SendGrid reads live on a deployment that never configured Twilio, instead
|
|
2269
|
+
// of one card for all three that could only ever be as live as its weakest.
|
|
2224
2270
|
//
|
|
2225
2271
|
// UNLIKE every public vendor, none of these appear in `requires` — see the
|
|
2226
2272
|
// comment there. Availability and configuration are different questions, and a
|
|
2227
2273
|
// missing CRM token must not take every base workflow step away.
|
|
2274
|
+
//
|
|
2275
|
+
// ponytail: the field keys keep the names they had on the shared row
|
|
2276
|
+
// (smsSid, hubspotToken) so the readers destructuring vendorSettings() did
|
|
2277
|
+
// not move. Rename them to sid/token when those readers are next touched.
|
|
2228
2278
|
provider: {
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
// mail are email. Requiring Twilio here made the whole drawbridge
|
|
2243
|
-
// provider read not-live on a deployment that never sends SMS, over a
|
|
2244
|
-
// channel it was not using.
|
|
2245
|
-
//
|
|
2279
|
+
vendors: {
|
|
2280
|
+
sendgrid: {
|
|
2281
|
+
fields: [
|
|
2282
|
+
{ input: "email", key: "accountSender", credential: "SENDGRID_FROM_ADDRESS", label: "Account sender", message: "Verification codes and security alerts send from here.", required: true },
|
|
2283
|
+
{ input: "password", key: "apiKey", credential: "SENDGRID_API_KEY", label: "SendGrid API key", message: "SendGrid dashboard, Settings, API Keys \u2014 full access for sends.", redact: true, required: true },
|
|
2284
|
+
// Optional: SENDGRID_SEND_FROM_ADDRESS is not boot-required in sync
|
|
2285
|
+
// either. Unset, it degrades to the account sender rather than
|
|
2286
|
+
// refusing to start.
|
|
2287
|
+
{ input: "email", key: "leadSender", credential: "SENDGRID_SEND_FROM_ADDRESS", label: "Lead sender", message: "The default for lead-facing mail when a merchant has not verified their own domain." }
|
|
2288
|
+
],
|
|
2289
|
+
icon: sendgrid_default,
|
|
2290
|
+
name: "SendGrid"
|
|
2291
|
+
},
|
|
2246
2292
|
// Missing, SMS is LOCKED DOWN rather than broken: every send path
|
|
2247
2293
|
// refuses cleanly (queue/notification.js cancels, the otc stream skips
|
|
2248
2294
|
// and logs), the number-purchase routes deny, and identityDefaults.sms
|
|
2249
|
-
// hides the org's SMS settings entirely.
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2295
|
+
// hides the org's SMS settings entirely. The card reads not live, which
|
|
2296
|
+
// is the truth about Twilio and says nothing about email.
|
|
2297
|
+
twilio: {
|
|
2298
|
+
fields: [
|
|
2299
|
+
{ input: "text", key: "smsFrom", credential: "TWILIO_ACCOUNT_FROM", label: "SMS number", message: "Without Twilio credentials, SMS is disabled across the platform.", required: true },
|
|
2300
|
+
{ input: "password", key: "smsSid", credential: "TWILIO_ACCOUNT_SID", label: "Twilio account SID", message: "Twilio console home \u2014 the AC\u2026 identifier on the account dashboard.", redact: true, required: true },
|
|
2301
|
+
{ input: "password", key: "smsToken", credential: "TWILIO_AUTH_TOKEN", label: "Twilio auth token", message: "Beside the account SID on the Twilio console dashboard.", redact: true, required: true }
|
|
2302
|
+
],
|
|
2303
|
+
icon: twilio_default,
|
|
2304
|
+
name: "Twilio"
|
|
2305
|
+
},
|
|
2306
|
+
// The CRM sync is best-effort internal tooling and no-ops without a
|
|
2307
|
+
// token. Not live here costs nothing a merchant can see.
|
|
2308
|
+
hubspot: {
|
|
2309
|
+
fields: [
|
|
2310
|
+
{ input: "password", key: "hubspotToken", credential: "HUBSPOT_ACCESS_TOKEN", label: "HubSpot access token", message: "Drawbridge's own CRM portal. Internal \u2014 no merchant sees this.", redact: true, required: true }
|
|
2311
|
+
],
|
|
2312
|
+
icon: hubspot_default,
|
|
2313
|
+
name: "HubSpot"
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2254
2316
|
},
|
|
2255
2317
|
// NOTHING, and HUBSPOT_ACCESS_TOKEN in particular must not be here.
|
|
2256
2318
|
//
|
|
@@ -2836,12 +2898,20 @@ var klaviyo_default2 = {
|
|
|
2836
2898
|
// DRAWBRIDGE'S OWN CREDENTIALS for this vendor, as opposed to a merchant's —
|
|
2837
2899
|
// what an admin types on the provider screen. Declared here rather than in a
|
|
2838
2900
|
// table in lib/providers.js, so a vendor's credentials sit beside the
|
|
2839
|
-
// `requires` that names the same variables.
|
|
2901
|
+
// `requires` that names the same variables. Keyed by the VENDOR that holds
|
|
2902
|
+
// them — here the vendor and the connection are the same thing, which is not
|
|
2903
|
+
// true of every manifest.
|
|
2840
2904
|
provider: {
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2905
|
+
vendors: {
|
|
2906
|
+
klaviyo: {
|
|
2907
|
+
fields: [
|
|
2908
|
+
{ input: "text", key: "clientId", credential: "KLAVIYO_OAUTH_CLIENT_ID", label: "Client ID", message: "From our app in the Klaviyo developer portal (developers.klaviyo.com).", required: true },
|
|
2909
|
+
{ input: "password", key: "clientSecret", credential: "KLAVIYO_OAUTH_CLIENT_SECRET", label: "Client secret", message: "Beside the Client ID on the Klaviyo app.", redact: true, required: true }
|
|
2910
|
+
],
|
|
2911
|
+
icon: klaviyo_default,
|
|
2912
|
+
name: "Klaviyo"
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2845
2915
|
},
|
|
2846
2916
|
requires: [
|
|
2847
2917
|
"KLAVIYO_OAUTH_CLIENT_ID",
|
|
@@ -3184,10 +3254,16 @@ var mailchimp_default2 = {
|
|
|
3184
3254
|
// what an admin types on the provider screen, beside the `requires` naming the
|
|
3185
3255
|
// same variables.
|
|
3186
3256
|
provider: {
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3257
|
+
vendors: {
|
|
3258
|
+
mailchimp: {
|
|
3259
|
+
fields: [
|
|
3260
|
+
{ input: "text", key: "clientId", credential: "MAILCHIMP_OAUTH_CLIENT_ID", label: "Client ID", message: "From our registered app \u2014 Mailchimp account, Extras, Registered apps.", required: true },
|
|
3261
|
+
{ input: "password", key: "clientSecret", credential: "MAILCHIMP_OAUTH_CLIENT_SECRET", label: "Client secret", message: "Beside the Client ID on the registered app.", redact: true, required: true }
|
|
3262
|
+
],
|
|
3263
|
+
icon: mailchimp_default,
|
|
3264
|
+
name: "Mailchimp"
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3191
3267
|
},
|
|
3192
3268
|
// The OAuth client this deployment registered. Without both, the vendor drops
|
|
3193
3269
|
// out of availableConnections rather than offering a Connect button that
|
|
@@ -4073,7 +4149,7 @@ var shopify_default2 = {
|
|
|
4073
4149
|
// against the ones the app now needs, and queues a webhook
|
|
4074
4150
|
// reconciliation.
|
|
4075
4151
|
health: async ({ connection: connection2, workflow }, { adminToken, logger: logger2, read, reconcileScopes, resolveSettings, rotateToken, shopify } = {}) => {
|
|
4076
|
-
var _a, _b, _c, _d;
|
|
4152
|
+
var _a, _b, _c, _d, _e, _f;
|
|
4077
4153
|
const request2 = {
|
|
4078
4154
|
connectionId: workflow.connection,
|
|
4079
4155
|
organizationId: workflow.organization,
|
|
@@ -4098,6 +4174,7 @@ var shopify_default2 = {
|
|
|
4098
4174
|
let metered;
|
|
4099
4175
|
let shopCurrency = null;
|
|
4100
4176
|
let billingProbeFailed = false;
|
|
4177
|
+
let reconciliation = null;
|
|
4101
4178
|
try {
|
|
4102
4179
|
if (!(source == null ? void 0 : source.id)) {
|
|
4103
4180
|
const shopData = await shopify.oauth.getShop({ adminAccessToken, shop: connection2.shop });
|
|
@@ -4118,45 +4195,99 @@ var shopify_default2 = {
|
|
|
4118
4195
|
const partnerRow = (read == null ? void 0 : read.get) ? await read.get({ collection: "provider", query: { slug: "shopify" } }) : null;
|
|
4119
4196
|
const partner = (partnerRow == null ? void 0 : partnerRow.settings) ? decrypt(partnerRow.settings) : {};
|
|
4120
4197
|
if (partner.partnerToken && partner.partnerOrgId && partner.partnerAppId && (source == null ? void 0 : source.id) && ((_b = shopify.partner) == null ? void 0 : _b.getUsageChargeEvents)) {
|
|
4198
|
+
const ledgerFloor = new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
|
|
4121
4199
|
const accruals = await shopify.partner.getUsageChargeEvents({
|
|
4122
4200
|
appId: "gid://shopify/App/" + partner.partnerAppId,
|
|
4123
|
-
first:
|
|
4124
|
-
occurredAtMin:
|
|
4201
|
+
first: 10,
|
|
4202
|
+
occurredAtMin: ledgerFloor.toISOString(),
|
|
4125
4203
|
organizationId: partner.partnerOrgId,
|
|
4126
4204
|
partnerToken: partner.partnerToken,
|
|
4127
4205
|
shopId: "gid://shopify/Shop/" + source.id
|
|
4128
4206
|
});
|
|
4129
|
-
const
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4207
|
+
const boundaries = accruals.filter((event) => event.occurredAt).sort((a, b) => new Date(a.occurredAt) - new Date(b.occurredAt));
|
|
4208
|
+
const accrued = [...boundaries].reverse().find((event) => Number(event.usageQuantity) > 0);
|
|
4209
|
+
if (accrued) metered = String(accrued.chargeId || accrued.id);
|
|
4210
|
+
const stampedAt = (_c = boundaries.find(
|
|
4211
|
+
(event) => {
|
|
4212
|
+
var _a2;
|
|
4213
|
+
return String(event.chargeId || event.id) === String(((_a2 = connection2.source) == null ? void 0 : _a2.metered) || "");
|
|
4214
|
+
}
|
|
4215
|
+
)) == null ? void 0 : _c.occurredAt;
|
|
4216
|
+
reconciliation = [];
|
|
4217
|
+
let windowStart = ledgerFloor;
|
|
4218
|
+
for (const boundary of boundaries) {
|
|
4219
|
+
const windowEnd = new Date(boundary.occurredAt);
|
|
4220
|
+
const windowOpensAt = windowStart;
|
|
4221
|
+
windowStart = windowEnd;
|
|
4222
|
+
if (stampedAt && windowEnd <= new Date(stampedAt)) continue;
|
|
4223
|
+
const charged = Math.round(Number(boundary.usageQuantity) || 0);
|
|
4224
|
+
const [tally] = await read.aggregate({
|
|
4133
4225
|
collection: "order",
|
|
4134
|
-
|
|
4135
|
-
$
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4226
|
+
pipeline: [
|
|
4227
|
+
{ $match: {
|
|
4228
|
+
"billed.date": { $gt: windowOpensAt, $lte: windowEnd },
|
|
4229
|
+
"billed.void": { $in: [null] },
|
|
4230
|
+
organization: workflow.organization,
|
|
4231
|
+
"provider.slug": "shopify"
|
|
4232
|
+
} },
|
|
4233
|
+
{ $group: {
|
|
4234
|
+
_id: null,
|
|
4235
|
+
billed: { $sum: "$billed.amount" },
|
|
4236
|
+
orders: { $push: "$id" }
|
|
4237
|
+
} }
|
|
4238
|
+
]
|
|
4239
|
+
}) || [];
|
|
4240
|
+
const billed = (tally == null ? void 0 : tally.billed) || 0;
|
|
4241
|
+
const orders = (tally == null ? void 0 : tally.orders) || [];
|
|
4242
|
+
const charge = String(boundary.chargeId || boundary.id);
|
|
4243
|
+
const balanced = charged === billed;
|
|
4244
|
+
if (!(charged || billed)) continue;
|
|
4245
|
+
if (balanced) {
|
|
4246
|
+
writes.push({
|
|
4247
|
+
collection: "order",
|
|
4248
|
+
data: {
|
|
4249
|
+
$set: { "billed.receipt": charge }
|
|
4250
|
+
},
|
|
4251
|
+
// Top-level, not an option — the controller takes
|
|
4252
|
+
// `multiple` beside `options`, and the shell
|
|
4253
|
+
// forwards it as such.
|
|
4254
|
+
multiple: true,
|
|
4255
|
+
operation: "update",
|
|
4256
|
+
query: {
|
|
4257
|
+
"billed.date": { $gt: windowOpensAt, $lte: windowEnd },
|
|
4258
|
+
"billed.receipt": { $in: [null] },
|
|
4259
|
+
"billed.void": { $in: [null] },
|
|
4260
|
+
organization: workflow.organization,
|
|
4261
|
+
"provider.slug": "shopify"
|
|
4262
|
+
}
|
|
4263
|
+
});
|
|
4264
|
+
} else {
|
|
4265
|
+
(_d = logger2 == null ? void 0 : logger2.error) == null ? void 0 : _d.call(logger2, new Error(
|
|
4266
|
+
"shopify.usage.reconciliation.mismatch on shop " + connection2.shop + ": charge " + charge + " charged " + charged + " cents but its window holds " + billed + " cents across " + orders.length + " billed orders \u2014 each cent of difference is commission that never charged"
|
|
4267
|
+
), {
|
|
4268
|
+
extra: {
|
|
4269
|
+
billed,
|
|
4270
|
+
charge,
|
|
4271
|
+
charged,
|
|
4272
|
+
orders: orders.slice(0, 50),
|
|
4273
|
+
shop: connection2.shop,
|
|
4274
|
+
windowEnd,
|
|
4275
|
+
windowStart: windowOpensAt
|
|
4276
|
+
}
|
|
4277
|
+
});
|
|
4278
|
+
}
|
|
4279
|
+
reconciliation.push({ balanced, billed, charge, charged, occurredAt: boundary.occurredAt, orders: orders.length });
|
|
4149
4280
|
}
|
|
4150
4281
|
}
|
|
4151
4282
|
} catch (probeError) {
|
|
4152
4283
|
billingProbeFailed = true;
|
|
4153
|
-
(
|
|
4284
|
+
(_e = logger2 == null ? void 0 : logger2.warn) == null ? void 0 : _e.call(logger2, "shopify.health.billing.probe.failed", {
|
|
4154
4285
|
connectionId: connection2.id,
|
|
4155
4286
|
message: probeError == null ? void 0 : probeError.message,
|
|
4156
4287
|
shop: connection2.shop
|
|
4157
4288
|
});
|
|
4158
4289
|
}
|
|
4159
|
-
if (sourceRepaired || metered !== void 0 && metered !== (((
|
|
4290
|
+
if (sourceRepaired || metered !== void 0 && metered !== (((_f = connection2.source) == null ? void 0 : _f.metered) ?? void 0)) {
|
|
4160
4291
|
writes.push({
|
|
4161
4292
|
collection: "connection",
|
|
4162
4293
|
data: {
|
|
@@ -4227,6 +4358,9 @@ var shopify_default2 = {
|
|
|
4227
4358
|
metered: metered ?? null,
|
|
4228
4359
|
meterProbed: metered !== void 0,
|
|
4229
4360
|
pingedAt: /* @__PURE__ */ new Date(),
|
|
4361
|
+
// One entry per boundary charge judged this run: what
|
|
4362
|
+
// Shopify charged vs what the window's orders billed.
|
|
4363
|
+
...reconciliation && { reconciliation },
|
|
4230
4364
|
refreshTokenExpiresAt: refreshTokenExpiresAt || null,
|
|
4231
4365
|
refreshTokenRotated,
|
|
4232
4366
|
scopesMissing,
|
|
@@ -4366,28 +4500,34 @@ var shopify_default2 = {
|
|
|
4366
4500
|
// name required by the manifest and enterable nowhere is a vendor that can
|
|
4367
4501
|
// never go live from the admin screen.
|
|
4368
4502
|
provider: {
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4503
|
+
vendors: {
|
|
4504
|
+
shopify: {
|
|
4505
|
+
fields: [
|
|
4506
|
+
{ input: "text", key: "apiKey", credential: "SHOPIFY_API_KEY", label: "API key", message: "The app's Client ID \u2014 Dev Dashboard, the app, Settings.", required: true },
|
|
4507
|
+
{ input: "password", key: "apiSecret", credential: "SHOPIFY_API_SECRET", label: "API secret", message: "The app's Client secret, beside the Client ID in the app's settings.", redact: true, required: true },
|
|
4508
|
+
{ input: "text", key: "appHandle", credential: "SHOPIFY_APP_HANDLE", label: "App handle", message: "The app's URL handle (for example drawbridge-development), from the app's configuration.", required: true },
|
|
4509
|
+
{ input: "text", key: "listingUrl", credential: "SHOPIFY_APP_LISTING_URL", label: "App listing URL", message: 'Where "View on the Shopify App Store" sends merchants \u2014 the public listing, or the Dev Dashboard install link for the dev app.', required: true },
|
|
4510
|
+
// The usage meter's event handle, when the plan config's meter ever
|
|
4511
|
+
// changes: stored value overrides the built-in default
|
|
4512
|
+
// (events.order.handle) at the mint point, and `format : 'slug'`
|
|
4513
|
+
// normalizes it on save — a handle can carry no spaces or capitals,
|
|
4514
|
+
// and classification against it is case-sensitive. Optional: empty
|
|
4515
|
+
// means the default, and the sender still refuses any handle that
|
|
4516
|
+
// disagrees with the transaction it minted.
|
|
4517
|
+
{ format: "slug", input: "text", key: "orderEventHandle", label: "Order event handle", message: "Must match the usage meter's Handle field on the plan exactly \u2014 lowercase and hyphens. Leave empty to use drawbridge-orders.", required: false, setting: true },
|
|
4518
|
+
// The Partner API credential set — the ONE place meter billing is
|
|
4519
|
+
// observable (CHARGE_USAGE accrual events). Org id and numeric app
|
|
4520
|
+
// id come from the Partner Dashboard; the token from Settings →
|
|
4521
|
+
// Partner API clients (Manage-apps permission). All optional: the
|
|
4522
|
+
// accrual probe simply stays off until they exist.
|
|
4523
|
+
{ input: "text", key: "partnerOrgId", label: "Partner organization ID", message: "The number in the Partner Dashboard URL \u2014 partners.shopify.com/<this number>/\u2026 \u2014 NOT the admin organization_id from the app listing link.", required: false, setting: true },
|
|
4524
|
+
{ input: "text", key: "partnerAppId", label: "Partner app ID (numeric)", message: "The numeric id in the app's Partner Dashboard URL \u2014 not the Client ID.", required: false, setting: true },
|
|
4525
|
+
{ input: "password", key: "partnerToken", label: "Partner API token", message: "Partner Dashboard, Settings, Partner API clients \u2014 create one with Manage apps and paste the prtapi_ token.", redact: true, required: false, setting: true }
|
|
4526
|
+
],
|
|
4527
|
+
icon: shopify_default,
|
|
4528
|
+
name: "Shopify"
|
|
4529
|
+
}
|
|
4530
|
+
}
|
|
4391
4531
|
},
|
|
4392
4532
|
// A pre-launch integration: it only surfaces once the App Store listing
|
|
4393
4533
|
// exists and the app is fully configured. Requiring all four means it can
|
|
@@ -4868,8 +5008,19 @@ var implemented = (hooks, path) => {
|
|
|
4868
5008
|
var leaves = (node, path = []) => Object.entries(node || {}).flatMap(
|
|
4869
5009
|
([key, value]) => typeof value === "function" ? [[[...path, key].join("."), value]] : value && typeof value === "object" ? leaves(value, [...path, key]) : []
|
|
4870
5010
|
);
|
|
5011
|
+
var checkIcon = (owner, icon) => {
|
|
5012
|
+
if (typeof icon !== "string" || !icon.includes("<svg")) {
|
|
5013
|
+
throw new Error(owner + " needs an icon \u2014 the svg markup itself, not a path to one");
|
|
5014
|
+
}
|
|
5015
|
+
if (!icon.includes("viewBox")) {
|
|
5016
|
+
throw new Error(owner + " icon has no viewBox, so it cannot scale");
|
|
5017
|
+
}
|
|
5018
|
+
if (icon.includes("<image")) {
|
|
5019
|
+
throw new Error(owner + " icon wraps a raster \u2014 re-export it as vector shapes");
|
|
5020
|
+
}
|
|
5021
|
+
};
|
|
4871
5022
|
var build = (manifest) => {
|
|
4872
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
|
|
5023
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
|
|
4873
5024
|
if (!(manifest == null ? void 0 : manifest.slug)) throw new Error("A connection needs a slug");
|
|
4874
5025
|
if (!(manifest == null ? void 0 : manifest.title)) throw new Error(manifest.slug + " needs a title");
|
|
4875
5026
|
if (!(manifest == null ? void 0 : manifest.private) && !(manifest == null ? void 0 : manifest.feature)) throw new Error(manifest.slug + " needs a plan feature key");
|
|
@@ -4899,46 +5050,53 @@ var build = (manifest) => {
|
|
|
4899
5050
|
}
|
|
4900
5051
|
}
|
|
4901
5052
|
}
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
5053
|
+
if ((_c = manifest.provider) == null ? void 0 : _c.fields) {
|
|
5054
|
+
throw new Error(manifest.slug + " declares provider.fields \u2014 fields live under provider.vendors.<vendor> now, keyed by the vendor that holds them");
|
|
5055
|
+
}
|
|
5056
|
+
const declaredKeys = /* @__PURE__ */ new Set();
|
|
5057
|
+
for (const [vendor, declared] of Object.entries(((_d = manifest.provider) == null ? void 0 : _d.vendors) || {})) {
|
|
5058
|
+
if (!(declared == null ? void 0 : declared.name)) throw new Error(manifest.slug + " declares vendor " + vendor + " with no name");
|
|
5059
|
+
checkIcon(manifest.slug + " vendor " + vendor, declared.icon);
|
|
5060
|
+
if (!Array.isArray(declared.fields) || !declared.fields.length) {
|
|
5061
|
+
throw new Error(manifest.slug + " declares vendor " + vendor + " with no fields \u2014 a vendor nothing is typed into holds nothing");
|
|
4908
5062
|
}
|
|
4909
|
-
|
|
4910
|
-
|
|
5063
|
+
for (const field2 of declared.fields) {
|
|
5064
|
+
if (!(field2 == null ? void 0 : field2.key) || !(field2 == null ? void 0 : field2.label)) {
|
|
5065
|
+
throw new Error(manifest.slug + " declares a provider field with no key or label");
|
|
5066
|
+
}
|
|
5067
|
+
if (!INPUTS.includes(field2.input)) {
|
|
5068
|
+
throw new Error(manifest.slug + ".provider." + field2.key + " needs an input the admin form can render \u2014 one of " + INPUTS.join(", "));
|
|
5069
|
+
}
|
|
5070
|
+
if (field2.input === "password" && !field2.redact) {
|
|
5071
|
+
throw new Error(manifest.slug + ".provider." + field2.key + " is a password and must declare redact : true \u2014 the api would hand the value back");
|
|
5072
|
+
}
|
|
5073
|
+
if (declaredKeys.has(field2.key)) {
|
|
5074
|
+
throw new Error(manifest.slug + " declares " + field2.key + " under two vendors \u2014 vendorSettings() merges them and one would win");
|
|
5075
|
+
}
|
|
5076
|
+
declaredKeys.add(field2.key);
|
|
4911
5077
|
}
|
|
4912
5078
|
}
|
|
4913
|
-
|
|
4914
|
-
throw new Error(manifest.slug + " needs an icon \u2014 the svg markup itself, not a path to one");
|
|
4915
|
-
}
|
|
4916
|
-
if (!manifest.icon.includes("viewBox")) {
|
|
4917
|
-
throw new Error(manifest.slug + " icon has no viewBox, so it cannot scale");
|
|
4918
|
-
}
|
|
4919
|
-
if (manifest.icon.includes("<image")) {
|
|
4920
|
-
throw new Error(manifest.slug + " icon wraps a raster \u2014 re-export it as vector shapes");
|
|
4921
|
-
}
|
|
5079
|
+
checkIcon(manifest.slug, manifest == null ? void 0 : manifest.icon);
|
|
4922
5080
|
if (!GROUPS.includes(manifest == null ? void 0 : manifest.group)) {
|
|
4923
5081
|
throw new Error(manifest.slug + " needs a group \u2014 one of " + GROUPS.join(", "));
|
|
4924
5082
|
}
|
|
4925
|
-
if ((
|
|
5083
|
+
if ((_e = manifest == null ? void 0 : manifest.connect) == null ? void 0 : _e.type) {
|
|
4926
5084
|
throw new Error(manifest.slug + " declares connect.type \u2014 that is auth.type now");
|
|
4927
5085
|
}
|
|
4928
|
-
if (!AUTH_TYPES.includes((
|
|
5086
|
+
if (!AUTH_TYPES.includes((_f = manifest == null ? void 0 : manifest.auth) == null ? void 0 : _f.type)) {
|
|
4929
5087
|
throw new Error(manifest.slug + " needs auth.type \u2014 one of " + AUTH_TYPES.join(", "));
|
|
4930
5088
|
}
|
|
4931
5089
|
if (manifest.auth.type === "oauth") {
|
|
4932
5090
|
for (const field2 of OAUTH_FIELDS) {
|
|
4933
|
-
if (!((
|
|
5091
|
+
if (!((_g = manifest.auth.oauth) == null ? void 0 : _g[field2])) {
|
|
4934
5092
|
throw new Error(manifest.slug + " is oauth and must declare auth.oauth." + field2);
|
|
4935
5093
|
}
|
|
4936
5094
|
}
|
|
4937
|
-
if (typeof ((
|
|
5095
|
+
if (typeof ((_i = (_h = manifest.hooks) == null ? void 0 : _h.auth) == null ? void 0 : _i.token) !== "function") {
|
|
4938
5096
|
throw new Error(manifest.slug + " is oauth and must implement hooks.auth.token \u2014 point it at authToken() or wrap it");
|
|
4939
5097
|
}
|
|
4940
5098
|
for (const url of OAUTH_URLS) {
|
|
4941
|
-
if (!((
|
|
5099
|
+
if (!((_k = (_j = manifest.auth.oauth) == null ? void 0 : _j.urls) == null ? void 0 : _k[url])) {
|
|
4942
5100
|
throw new Error(manifest.slug + " is oauth and must declare auth.oauth.urls." + url);
|
|
4943
5101
|
}
|
|
4944
5102
|
}
|
|
@@ -4948,16 +5106,16 @@ var build = (manifest) => {
|
|
|
4948
5106
|
);
|
|
4949
5107
|
}
|
|
4950
5108
|
}
|
|
4951
|
-
if (implemented(manifest.hooks, "inbound.event") && typeof ((
|
|
5109
|
+
if (implemented(manifest.hooks, "inbound.event") && typeof ((_m = (_l = manifest.hooks) == null ? void 0 : _l.inbound) == null ? void 0 : _m.event) !== "function" && !((_o = (_n = manifest.inbound) == null ? void 0 : _n.headers) == null ? void 0 : _o.event)) {
|
|
4952
5110
|
throw new Error(manifest.slug + " implements inbound.event but declares no inbound.headers.event");
|
|
4953
5111
|
}
|
|
4954
|
-
if (implemented(manifest.hooks, "inbound.verify") && !((
|
|
5112
|
+
if (implemented(manifest.hooks, "inbound.verify") && !((_q = (_p = manifest.inbound) == null ? void 0 : _p.headers) == null ? void 0 : _q.signature)) {
|
|
4955
5113
|
throw new Error(manifest.slug + " implements inbound.verify but declares no inbound.headers.signature");
|
|
4956
5114
|
}
|
|
4957
5115
|
if (typeof (manifest == null ? void 0 : manifest.status) !== "function") {
|
|
4958
5116
|
throw new Error(manifest.slug + " must declare status( data ) \u2014 return null to accept the connection's own status, or { message, status } to override it");
|
|
4959
5117
|
}
|
|
4960
|
-
if (!Array.isArray((
|
|
5118
|
+
if (!Array.isArray((_r = manifest == null ? void 0 : manifest.content) == null ? void 0 : _r.guide) || !manifest.content.guide.length) {
|
|
4961
5119
|
throw new Error(manifest.slug + " needs content.guide \u2014 an array of steps for its page");
|
|
4962
5120
|
}
|
|
4963
5121
|
if (typeof (manifest == null ? void 0 : manifest.status) !== "function") {
|
|
@@ -4969,8 +5127,8 @@ var build = (manifest) => {
|
|
|
4969
5127
|
}
|
|
4970
5128
|
for (const [domain, verbs] of Object.entries(HOOKS)) {
|
|
4971
5129
|
for (const verb of verbs) {
|
|
4972
|
-
const hook = (
|
|
4973
|
-
if (((
|
|
5130
|
+
const hook = (_t = (_s = manifest.hooks) == null ? void 0 : _s[domain]) == null ? void 0 : _t[verb];
|
|
5131
|
+
if (((_u = manifest.hooks) == null ? void 0 : _u[domain]) === false) continue;
|
|
4974
5132
|
if (hook !== false && !implemented({ [domain]: { [verb]: hook } }, domain + "." + verb)) {
|
|
4975
5133
|
throw new Error(manifest.slug + " must answer hooks." + domain + "." + verb + " \u2014 false, a function, or {} if another repo implements it");
|
|
4976
5134
|
}
|
|
@@ -5020,6 +5178,44 @@ var connections = Object.freeze({
|
|
|
5020
5178
|
shopify: build(shopify_default2),
|
|
5021
5179
|
webhook: build(webhook_default)
|
|
5022
5180
|
});
|
|
5181
|
+
var canonical = (field2) => JSON.stringify(Object.fromEntries(Object.entries(field2).sort()));
|
|
5182
|
+
var mergeVendors = (catalog) => {
|
|
5183
|
+
var _a;
|
|
5184
|
+
const merged = {};
|
|
5185
|
+
const claimed = /* @__PURE__ */ new Map();
|
|
5186
|
+
for (const [slug2, manifest] of Object.entries(catalog)) {
|
|
5187
|
+
for (const [vendor, declared] of Object.entries(((_a = manifest.provider) == null ? void 0 : _a.vendors) || {})) {
|
|
5188
|
+
const held = merged[vendor] || (merged[vendor] = { fields: [], icon: declared.icon, name: declared.name });
|
|
5189
|
+
if (held.name !== declared.name) {
|
|
5190
|
+
throw new Error(slug2 + " names vendor " + vendor + ' "' + declared.name + '" but another manifest names it "' + held.name + '"');
|
|
5191
|
+
}
|
|
5192
|
+
if (held.icon !== declared.icon) {
|
|
5193
|
+
throw new Error(slug2 + " declares a different icon for vendor " + vendor + " than another manifest \u2014 one vendor, one mark");
|
|
5194
|
+
}
|
|
5195
|
+
for (const field2 of declared.fields) {
|
|
5196
|
+
const existing = held.fields.find((entry) => entry.key === field2.key);
|
|
5197
|
+
if (existing) {
|
|
5198
|
+
if (canonical(existing) !== canonical(field2)) {
|
|
5199
|
+
throw new Error(slug2 + " declares " + vendor + "." + field2.key + " differently from another manifest \u2014 a field two connections spend is declared identically in both");
|
|
5200
|
+
}
|
|
5201
|
+
continue;
|
|
5202
|
+
}
|
|
5203
|
+
if (field2.credential) {
|
|
5204
|
+
const owner = claimed.get(field2.credential);
|
|
5205
|
+
if (owner) {
|
|
5206
|
+
throw new Error(field2.credential + " is claimed by both " + owner + " and " + vendor + "." + field2.key + " \u2014 one would silently overwrite the other");
|
|
5207
|
+
}
|
|
5208
|
+
claimed.set(field2.credential, vendor + "." + field2.key);
|
|
5209
|
+
}
|
|
5210
|
+
held.fields.push(field2);
|
|
5211
|
+
}
|
|
5212
|
+
}
|
|
5213
|
+
}
|
|
5214
|
+
return Object.freeze(Object.fromEntries(
|
|
5215
|
+
Object.entries(merged).map(([vendor, held]) => [vendor, Object.freeze({ ...held, fields: Object.freeze(held.fields) })])
|
|
5216
|
+
));
|
|
5217
|
+
};
|
|
5218
|
+
var vendors = mergeVendors(connections);
|
|
5023
5219
|
(() => {
|
|
5024
5220
|
var _a;
|
|
5025
5221
|
const routes = {};
|
|
@@ -5219,6 +5415,7 @@ export {
|
|
|
5219
5415
|
hookSupport,
|
|
5220
5416
|
isStale,
|
|
5221
5417
|
mergeSettings,
|
|
5418
|
+
mergeVendors,
|
|
5222
5419
|
pkcePair,
|
|
5223
5420
|
projectConnection,
|
|
5224
5421
|
publicConnectionKeys,
|
|
@@ -5230,5 +5427,6 @@ export {
|
|
|
5230
5427
|
stepLabels,
|
|
5231
5428
|
stepQueues,
|
|
5232
5429
|
stepRoutes,
|
|
5233
|
-
tokenSettings
|
|
5430
|
+
tokenSettings,
|
|
5431
|
+
vendors
|
|
5234
5432
|
};
|