@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
|
@@ -1192,10 +1192,16 @@ var attentive = {
|
|
|
1192
1192
|
// `redact` marks a secret — never returned by the api, and blank on save means
|
|
1193
1193
|
// keep the stored value. `required` drives the live check.
|
|
1194
1194
|
provider : {
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1195
|
+
vendors : {
|
|
1196
|
+
attentive : {
|
|
1197
|
+
fields : [
|
|
1198
|
+
{ 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 },
|
|
1199
|
+
{ 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 }
|
|
1200
|
+
],
|
|
1201
|
+
icon: icon$4,
|
|
1202
|
+
name : 'Attentive'
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1199
1205
|
},
|
|
1200
1206
|
requires : [
|
|
1201
1207
|
'ATTENTIVE_OAUTH_CLIENT_ID',
|
|
@@ -1591,6 +1597,60 @@ var icon$3 = `<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xm
|
|
|
1591
1597
|
</defs>
|
|
1592
1598
|
</svg>`;
|
|
1593
1599
|
|
|
1600
|
+
// HubSpot's sprocket, drawn by hand on the vendor's orange rather than exported
|
|
1601
|
+
// from a brand kit — there was no export to hand. Replace with the kit's own
|
|
1602
|
+
// mark when one is available; the shape and the fill are the recognisable
|
|
1603
|
+
// parts and both are here.
|
|
1604
|
+
//
|
|
1605
|
+
// A .js wrapper around otherwise untouched SVG so `node --test` can run against
|
|
1606
|
+
// lib/ directly. A bare .svg import would need a bundler loader and force the
|
|
1607
|
+
// tests onto dist/, which is a worse trade than one line of wrapper.
|
|
1608
|
+
var hubspotIcon = `<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
1609
|
+
<rect width="500" height="500" fill="#FF7A59"/>
|
|
1610
|
+
<circle cx="290" cy="290" r="78" stroke="white" stroke-width="30"/>
|
|
1611
|
+
<circle cx="290" cy="290" r="18" fill="white"/>
|
|
1612
|
+
<path d="M290 212V150" stroke="white" stroke-width="30" stroke-linecap="round"/>
|
|
1613
|
+
<circle cx="290" cy="125" r="28" fill="white"/>
|
|
1614
|
+
<path d="M270 137L172 96" stroke="white" stroke-width="22" stroke-linecap="round"/>
|
|
1615
|
+
<circle cx="160" cy="91" r="20" fill="white"/>
|
|
1616
|
+
<path d="M233 347L184 396" stroke="white" stroke-width="26" stroke-linecap="round"/>
|
|
1617
|
+
<circle cx="172" cy="408" r="22" fill="white"/>
|
|
1618
|
+
</svg>`;
|
|
1619
|
+
|
|
1620
|
+
// Sendgrid, exported from the brand kit and left as authored — the
|
|
1621
|
+
// fills are the vendor's own mark, not a recolour.
|
|
1622
|
+
//
|
|
1623
|
+
// A .js wrapper around otherwise untouched SVG so `node --test` can run against
|
|
1624
|
+
// lib/ directly. A bare .svg import would need a bundler loader and force the
|
|
1625
|
+
// tests onto dist/, which is a worse trade than one line of wrapper.
|
|
1626
|
+
var sendgridIcon = `<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
1627
|
+
<rect width="500" height="500" fill="#F1FAFF"/>
|
|
1628
|
+
<g clip-path="url(#clip0_1011_2743)">
|
|
1629
|
+
<path d="M366.676 134.534V289.172H289.203V366.487H134.258L134.258 289.171L134.256 289.172V211.851H211.729V134.534H366.676Z" fill="#9DD6E3"/>
|
|
1630
|
+
<path d="M134.258 366.488H211.731V289.169H134.258V366.488Z" fill="#3F72AB"/>
|
|
1631
|
+
<path d="M289.203 289.172H366.677V211.852H289.203V289.172Z" fill="#00A9D1"/>
|
|
1632
|
+
<path d="M211.729 211.853H289.202V134.534H211.729V211.853Z" fill="#00A9D1"/>
|
|
1633
|
+
<path d="M211.729 289.17H289.202V211.852H211.729V289.17Z" fill="#2191C4"/>
|
|
1634
|
+
<path d="M289.203 211.853H366.677V134.534H289.203V211.853Z" fill="#3F72AB"/>
|
|
1635
|
+
</g>
|
|
1636
|
+
<defs>
|
|
1637
|
+
<clipPath id="clip0_1011_2743">
|
|
1638
|
+
<rect width="232.42" height="231.956" fill="white" transform="translate(134.256 134.534)"/>
|
|
1639
|
+
</clipPath>
|
|
1640
|
+
</defs>
|
|
1641
|
+
</svg>`;
|
|
1642
|
+
|
|
1643
|
+
// Twilio, exported from the brand kit and left as authored — the
|
|
1644
|
+
// fills are the vendor's own mark, not a recolour.
|
|
1645
|
+
//
|
|
1646
|
+
// A .js wrapper around otherwise untouched SVG so `node --test` can run against
|
|
1647
|
+
// lib/ directly. A bare .svg import would need a bundler loader and force the
|
|
1648
|
+
// tests onto dist/, which is a worse trade than one line of wrapper.
|
|
1649
|
+
var twilioIcon = `<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
1650
|
+
<rect width="500" height="500" fill="#F22F46"/>
|
|
1651
|
+
<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"/>
|
|
1652
|
+
</svg>`;
|
|
1653
|
+
|
|
1594
1654
|
// TWILIO'S OWN KEYWORD LISTS. STOP-family withdraws consent for the number —
|
|
1595
1655
|
// platform-wide, because every organization sends from the same platform number,
|
|
1596
1656
|
// so a withdrawal cannot be scoped narrower than the number it was sent to.
|
|
@@ -2396,37 +2456,56 @@ var drawbridge = {
|
|
|
2396
2456
|
//
|
|
2397
2457
|
// They belong on THIS manifest because this is the connection that sends: the
|
|
2398
2458
|
// email, sms and segment hooks below are the only things that spend them, and
|
|
2399
|
-
// a private connection is still where a vendor fact lives.
|
|
2459
|
+
// a private connection is still where a vendor fact lives. They are THREE
|
|
2460
|
+
// VENDORS, not one, and each is its own row and its own admin card — so
|
|
2461
|
+
// SendGrid reads live on a deployment that never configured Twilio, instead
|
|
2462
|
+
// of one card for all three that could only ever be as live as its weakest.
|
|
2400
2463
|
//
|
|
2401
2464
|
// UNLIKE every public vendor, none of these appear in `requires` — see the
|
|
2402
2465
|
// comment there. Availability and configuration are different questions, and a
|
|
2403
2466
|
// missing CRM token must not take every base workflow step away.
|
|
2467
|
+
//
|
|
2468
|
+
// ponytail: the field keys keep the names they had on the shared row
|
|
2469
|
+
// (smsSid, hubspotToken) so the readers destructuring vendorSettings() did
|
|
2470
|
+
// not move. Rename them to sid/token when those readers are next touched.
|
|
2404
2471
|
provider : {
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
// mail are email. Requiring Twilio here made the whole drawbridge
|
|
2419
|
-
// provider read not-live on a deployment that never sends SMS, over a
|
|
2420
|
-
// channel it was not using.
|
|
2421
|
-
//
|
|
2472
|
+
vendors : {
|
|
2473
|
+
sendgrid : {
|
|
2474
|
+
fields : [
|
|
2475
|
+
{ input : 'email', key : 'accountSender', credential : 'SENDGRID_FROM_ADDRESS', label : 'Account sender', message : 'Verification codes and security alerts send from here.', required : true },
|
|
2476
|
+
{ input : 'password', key : 'apiKey', credential : 'SENDGRID_API_KEY', label : 'SendGrid API key', message : 'SendGrid dashboard, Settings, API Keys — full access for sends.', redact : true, required : true },
|
|
2477
|
+
// Optional: SENDGRID_SEND_FROM_ADDRESS is not boot-required in sync
|
|
2478
|
+
// either. Unset, it degrades to the account sender rather than
|
|
2479
|
+
// refusing to start.
|
|
2480
|
+
{ 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.' }
|
|
2481
|
+
],
|
|
2482
|
+
icon : sendgridIcon,
|
|
2483
|
+
name : 'SendGrid'
|
|
2484
|
+
},
|
|
2422
2485
|
// Missing, SMS is LOCKED DOWN rather than broken: every send path
|
|
2423
2486
|
// refuses cleanly (queue/notification.js cancels, the otc stream skips
|
|
2424
2487
|
// and logs), the number-purchase routes deny, and identityDefaults.sms
|
|
2425
|
-
// hides the org's SMS settings entirely.
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2488
|
+
// hides the org's SMS settings entirely. The card reads not live, which
|
|
2489
|
+
// is the truth about Twilio and says nothing about email.
|
|
2490
|
+
twilio : {
|
|
2491
|
+
fields : [
|
|
2492
|
+
{ input : 'text', key : 'smsFrom', credential : 'TWILIO_ACCOUNT_FROM', label : 'SMS number', message : 'Without Twilio credentials, SMS is disabled across the platform.', required : true },
|
|
2493
|
+
{ input : 'password', key : 'smsSid', credential : 'TWILIO_ACCOUNT_SID', label : 'Twilio account SID', message : 'Twilio console home — the AC… identifier on the account dashboard.', redact : true, required : true },
|
|
2494
|
+
{ 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 }
|
|
2495
|
+
],
|
|
2496
|
+
icon : twilioIcon,
|
|
2497
|
+
name : 'Twilio'
|
|
2498
|
+
},
|
|
2499
|
+
// The CRM sync is best-effort internal tooling and no-ops without a
|
|
2500
|
+
// token. Not live here costs nothing a merchant can see.
|
|
2501
|
+
hubspot : {
|
|
2502
|
+
fields : [
|
|
2503
|
+
{ input : 'password', key : 'hubspotToken', credential : 'HUBSPOT_ACCESS_TOKEN', label : 'HubSpot access token', message : 'Drawbridge\'s own CRM portal. Internal — no merchant sees this.', redact : true, required : true }
|
|
2504
|
+
],
|
|
2505
|
+
icon : hubspotIcon,
|
|
2506
|
+
name : 'HubSpot'
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2430
2509
|
},
|
|
2431
2510
|
// NOTHING, and HUBSPOT_ACCESS_TOKEN in particular must not be here.
|
|
2432
2511
|
//
|
|
@@ -3176,12 +3255,20 @@ var klaviyo = {
|
|
|
3176
3255
|
// DRAWBRIDGE'S OWN CREDENTIALS for this vendor, as opposed to a merchant's —
|
|
3177
3256
|
// what an admin types on the provider screen. Declared here rather than in a
|
|
3178
3257
|
// table in lib/providers.js, so a vendor's credentials sit beside the
|
|
3179
|
-
// `requires` that names the same variables.
|
|
3258
|
+
// `requires` that names the same variables. Keyed by the VENDOR that holds
|
|
3259
|
+
// them — here the vendor and the connection are the same thing, which is not
|
|
3260
|
+
// true of every manifest.
|
|
3180
3261
|
provider : {
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3262
|
+
vendors : {
|
|
3263
|
+
klaviyo : {
|
|
3264
|
+
fields : [
|
|
3265
|
+
{ 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 },
|
|
3266
|
+
{ 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 }
|
|
3267
|
+
],
|
|
3268
|
+
icon: icon$2,
|
|
3269
|
+
name : 'Klaviyo'
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3185
3272
|
},
|
|
3186
3273
|
requires : [
|
|
3187
3274
|
'KLAVIYO_OAUTH_CLIENT_ID',
|
|
@@ -3642,10 +3729,16 @@ var mailchimp = {
|
|
|
3642
3729
|
// what an admin types on the provider screen, beside the `requires` naming the
|
|
3643
3730
|
// same variables.
|
|
3644
3731
|
provider : {
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3732
|
+
vendors : {
|
|
3733
|
+
mailchimp : {
|
|
3734
|
+
fields : [
|
|
3735
|
+
{ input : 'text', key : 'clientId', credential : 'MAILCHIMP_OAUTH_CLIENT_ID', label : 'Client ID', message : 'From our registered app — Mailchimp account, Extras, Registered apps.', required : true },
|
|
3736
|
+
{ 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 }
|
|
3737
|
+
],
|
|
3738
|
+
icon: icon$1,
|
|
3739
|
+
name : 'Mailchimp'
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3649
3742
|
},
|
|
3650
3743
|
// The OAuth client this deployment registered. Without both, the vendor drops
|
|
3651
3744
|
// out of availableConnections rather than offering a Connect button that
|
|
@@ -4931,6 +5024,9 @@ var shopify = {
|
|
|
4931
5024
|
let metered;
|
|
4932
5025
|
let shopCurrency = null;
|
|
4933
5026
|
let billingProbeFailed = false;
|
|
5027
|
+
// Per-boundary reconciliation results, surfaced in the step
|
|
5028
|
+
// response — the operator's ledger view of charged vs billed.
|
|
5029
|
+
let reconciliation = null;
|
|
4934
5030
|
|
|
4935
5031
|
try {
|
|
4936
5032
|
|
|
@@ -4971,71 +5067,162 @@ var shopify = {
|
|
|
4971
5067
|
// THE REAL METER SIGNAL — Partner API accrual. A CHARGE_USAGE
|
|
4972
5068
|
// event for this shop is proof billing works: its charge id
|
|
4973
5069
|
// becomes the stamp (overriding the line-item probe, which
|
|
4974
|
-
// managed pricing keeps blind)
|
|
4975
|
-
//
|
|
4976
|
-
//
|
|
4977
|
-
//
|
|
4978
|
-
// entirely until the provider row carries the credentials.
|
|
5070
|
+
// managed pricing keeps blind), and each charge row is a
|
|
5071
|
+
// billing BOUNDARY the reconciler below balances the books
|
|
5072
|
+
// against. Off entirely until the provider row carries the
|
|
5073
|
+
// credentials.
|
|
4979
5074
|
const partnerRow = read?.get ? await read.get({ collection : 'provider', query : { slug : 'shopify' } }) : null;
|
|
4980
5075
|
const partner = partnerRow?.settings ? decrypt( partnerRow.settings ) : {};
|
|
4981
5076
|
|
|
4982
5077
|
if( partner.partnerToken && partner.partnerOrgId && partner.partnerAppId && source?.id && shopify.partner?.getUsageChargeEvents ){
|
|
4983
5078
|
|
|
5079
|
+
const ledgerFloor = new Date( Date.now() - 30 * 24 * 60 * 60 * 1000 );
|
|
5080
|
+
|
|
4984
5081
|
const accruals = await shopify.partner.getUsageChargeEvents({
|
|
4985
5082
|
appId : 'gid://shopify/App/' + partner.partnerAppId,
|
|
4986
|
-
first :
|
|
4987
|
-
occurredAtMin :
|
|
5083
|
+
first : 10,
|
|
5084
|
+
occurredAtMin : ledgerFloor.toISOString(),
|
|
4988
5085
|
organizationId : partner.partnerOrgId,
|
|
4989
5086
|
partnerToken : partner.partnerToken,
|
|
4990
5087
|
shopId : 'gid://shopify/Shop/' + source.id
|
|
4991
5088
|
});
|
|
4992
5089
|
|
|
5090
|
+
// Oldest first: each charge row is a boundary that closes
|
|
5091
|
+
// the window since the row before it.
|
|
5092
|
+
const boundaries = accruals
|
|
5093
|
+
.filter( ( event ) => event.occurredAt )
|
|
5094
|
+
.sort( ( a, b ) => new Date( a.occurredAt ) - new Date( b.occurredAt ) );
|
|
5095
|
+
|
|
4993
5096
|
// A usage ROW exists per subscription even before anything
|
|
4994
5097
|
// accrues (measured: a pre-meter approval shows qty 0), so
|
|
4995
5098
|
// the row is not the proof — a POSITIVE usageQuantity is.
|
|
4996
|
-
|
|
5099
|
+
// The LATEST positive row is the stamp.
|
|
5100
|
+
const accrued = [ ...boundaries ].reverse().find( ( event ) => Number( event.usageQuantity ) > 0 );
|
|
5101
|
+
|
|
5102
|
+
if( accrued ) metered = String( accrued.chargeId || accrued.id );
|
|
5103
|
+
|
|
5104
|
+
// THE BOUNDARY RECONCILER. Money truth lives at boundaries:
|
|
5105
|
+
// each charge row states what Shopify actually charged for
|
|
5106
|
+
// the window since the previous row, and the orders billed
|
|
5107
|
+
// inside that window state what we asked for — in the same
|
|
5108
|
+
// cents (billed.amount IS the value the usage event
|
|
5109
|
+
// carried). Balanced and positive → the window's orders get
|
|
5110
|
+
// the charge id as billed.receipt (their invoice bucket).
|
|
5111
|
+
// Mismatched → an alarm with the diff, and NO stamp: a
|
|
5112
|
+
// short window is money silently lost, which is the one
|
|
5113
|
+
// thing this whole chain exists to make loud.
|
|
5114
|
+
//
|
|
5115
|
+
// The stored stamp is the CURSOR: rows at or before the
|
|
5116
|
+
// charge already stamped were judged on the run that
|
|
5117
|
+
// stamped it, and re-judging them would page daily on the
|
|
5118
|
+
// pre-meter era's known losses. (A store with losses and no
|
|
5119
|
+
// positive row yet has no cursor, so its mismatch DOES
|
|
5120
|
+
// re-alarm daily — deliberately: that nag is what drives
|
|
5121
|
+
// the admin write-off, and voided orders leave the sums.)
|
|
5122
|
+
const stampedAt = boundaries.find(
|
|
5123
|
+
( event ) => String( event.chargeId || event.id ) === String( connection.source?.metered || '' )
|
|
5124
|
+
)?.occurredAt;
|
|
5125
|
+
|
|
5126
|
+
reconciliation = [];
|
|
5127
|
+
|
|
5128
|
+
let windowStart = ledgerFloor;
|
|
5129
|
+
|
|
5130
|
+
for( const boundary of boundaries ){
|
|
5131
|
+
|
|
5132
|
+
const windowEnd = new Date( boundary.occurredAt );
|
|
5133
|
+
const windowOpensAt = windowStart;
|
|
5134
|
+
|
|
5135
|
+
windowStart = windowEnd;
|
|
5136
|
+
|
|
5137
|
+
if( stampedAt && windowEnd <= new Date( stampedAt ) ) continue;
|
|
5138
|
+
|
|
5139
|
+
const charged = Math.round( Number( boundary.usageQuantity ) || 0 );
|
|
5140
|
+
|
|
5141
|
+
// Voided orders are written off — known unrecoverable,
|
|
5142
|
+
// excluded so they stop swaying every future boundary.
|
|
5143
|
+
// Already-receipted orders still COUNT in the sum (an
|
|
5144
|
+
// early stamp must not read as missing money) but are
|
|
5145
|
+
// never restamped.
|
|
5146
|
+
const [ tally ] = await read.aggregate({
|
|
5147
|
+
collection : 'order',
|
|
5148
|
+
pipeline : [
|
|
5149
|
+
{ $match : {
|
|
5150
|
+
'billed.date' : { $gt : windowOpensAt, $lte : windowEnd },
|
|
5151
|
+
'billed.void' : { $in : [ null ] },
|
|
5152
|
+
organization : workflow.organization,
|
|
5153
|
+
'provider.slug' : 'shopify'
|
|
5154
|
+
} },
|
|
5155
|
+
{ $group : {
|
|
5156
|
+
_id : null,
|
|
5157
|
+
billed : { $sum : '$billed.amount' },
|
|
5158
|
+
orders : { $push : '$id' }
|
|
5159
|
+
} }
|
|
5160
|
+
]
|
|
5161
|
+
}) || [];
|
|
5162
|
+
|
|
5163
|
+
const billed = tally?.billed || 0;
|
|
5164
|
+
const orders = tally?.orders || [];
|
|
5165
|
+
const charge = String( boundary.chargeId || boundary.id );
|
|
5166
|
+
const balanced = charged === billed;
|
|
5167
|
+
|
|
5168
|
+
if( ! ( charged || billed ) ) continue;
|
|
5169
|
+
|
|
5170
|
+
if( balanced ){
|
|
5171
|
+
|
|
5172
|
+
writes.push({
|
|
5173
|
+
collection : 'order',
|
|
5174
|
+
data : {
|
|
5175
|
+
$set : { 'billed.receipt' : charge }
|
|
5176
|
+
},
|
|
5177
|
+
// Top-level, not an option — the controller takes
|
|
5178
|
+
// `multiple` beside `options`, and the shell
|
|
5179
|
+
// forwards it as such.
|
|
5180
|
+
multiple : true,
|
|
5181
|
+
operation : 'update',
|
|
5182
|
+
query : {
|
|
5183
|
+
'billed.date' : { $gt : windowOpensAt, $lte : windowEnd },
|
|
5184
|
+
'billed.receipt' : { $in : [ null ] },
|
|
5185
|
+
'billed.void' : { $in : [ null ] },
|
|
5186
|
+
organization : workflow.organization,
|
|
5187
|
+
'provider.slug' : 'shopify'
|
|
5188
|
+
}
|
|
5189
|
+
});
|
|
5190
|
+
|
|
5191
|
+
} else {
|
|
5192
|
+
|
|
5193
|
+
logger?.error?.( new Error(
|
|
5194
|
+
'shopify.usage.reconciliation.mismatch on shop ' + connection.shop
|
|
5195
|
+
+ ': charge ' + charge + ' charged ' + charged + ' cents but its window holds '
|
|
5196
|
+
+ billed + ' cents across ' + orders.length + ' billed orders'
|
|
5197
|
+
+ ' — each cent of difference is commission that never charged'
|
|
5198
|
+
), {
|
|
5199
|
+
extra : {
|
|
5200
|
+
billed,
|
|
5201
|
+
charge,
|
|
5202
|
+
charged,
|
|
5203
|
+
orders : orders.slice( 0, 50 ),
|
|
5204
|
+
shop : connection.shop,
|
|
5205
|
+
windowEnd,
|
|
5206
|
+
windowStart : windowOpensAt
|
|
5207
|
+
}
|
|
5208
|
+
});
|
|
4997
5209
|
|
|
4998
|
-
|
|
5210
|
+
}
|
|
4999
5211
|
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
// THE BUCKET STAMPS ITS ORDERS. Partner accrual rows
|
|
5003
|
-
// are per-subscription, not per-order, so the charge id
|
|
5004
|
-
// is the invoice bucket every order billed since that
|
|
5005
|
-
// subscription's approval rolled into — written onto
|
|
5006
|
-
// billed.receipt (many-update; $in [null] matches both
|
|
5007
|
-
// null and never-stamped). Orders billed before the
|
|
5008
|
-
// window belong to earlier, possibly unbillable
|
|
5009
|
-
// approvals and honestly stay unstamped.
|
|
5010
|
-
writes.push({
|
|
5011
|
-
collection : 'order',
|
|
5012
|
-
data : {
|
|
5013
|
-
$set : { 'billed.receipt' : metered }
|
|
5014
|
-
},
|
|
5015
|
-
// Top-level, not an option — the controller takes
|
|
5016
|
-
// `multiple` beside `options`, and the shell
|
|
5017
|
-
// forwards it as such.
|
|
5018
|
-
multiple : true,
|
|
5019
|
-
operation : 'update',
|
|
5020
|
-
query : {
|
|
5021
|
-
'billed.date' : { $gte : new Date( accrued.occurredAt || 0 ) },
|
|
5022
|
-
'billed.receipt' : { $in : [ null ] },
|
|
5023
|
-
organization : workflow.organization,
|
|
5024
|
-
'provider.slug' : 'shopify'
|
|
5025
|
-
}
|
|
5026
|
-
});
|
|
5212
|
+
reconciliation.push({ balanced, billed, charge, charged, occurredAt : boundary.occurredAt, orders : orders.length });
|
|
5027
5213
|
|
|
5028
5214
|
}
|
|
5029
5215
|
|
|
5030
|
-
// NO
|
|
5031
|
-
//
|
|
5032
|
-
//
|
|
5033
|
-
//
|
|
5034
|
-
//
|
|
5035
|
-
//
|
|
5036
|
-
//
|
|
5037
|
-
//
|
|
5038
|
-
//
|
|
5216
|
+
// DELIBERATELY NO null verdict from a lossy boundary. A
|
|
5217
|
+
// zero-charged window full of billed orders IS proof money
|
|
5218
|
+
// was lost — but it cannot say whether the CURRENT approval
|
|
5219
|
+
// is broken: the exact same shape appears the moment a
|
|
5220
|
+
// merchant fixes their store (the release charges out qty 0
|
|
5221
|
+
// and its window holds the pre-meter losses), and re-arming
|
|
5222
|
+
// "Fix order billing" on a store that was just fixed sends
|
|
5223
|
+
// the merchant through the flow again for nothing. The
|
|
5224
|
+
// mismatch alarm above owns that truth for the operator,
|
|
5225
|
+
// and the write-off drains it.
|
|
5039
5226
|
|
|
5040
5227
|
}
|
|
5041
5228
|
|
|
@@ -5154,6 +5341,9 @@ var shopify = {
|
|
|
5154
5341
|
metered : metered ?? null,
|
|
5155
5342
|
meterProbed : metered !== undefined,
|
|
5156
5343
|
pingedAt : new Date(),
|
|
5344
|
+
// One entry per boundary charge judged this run: what
|
|
5345
|
+
// Shopify charged vs what the window's orders billed.
|
|
5346
|
+
...( reconciliation && { reconciliation }),
|
|
5157
5347
|
refreshTokenExpiresAt : refreshTokenExpiresAt || null,
|
|
5158
5348
|
refreshTokenRotated,
|
|
5159
5349
|
scopesMissing,
|
|
@@ -5334,28 +5524,34 @@ var shopify = {
|
|
|
5334
5524
|
// name required by the manifest and enterable nowhere is a vendor that can
|
|
5335
5525
|
// never go live from the admin screen.
|
|
5336
5526
|
provider : {
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5527
|
+
vendors : {
|
|
5528
|
+
shopify : {
|
|
5529
|
+
fields : [
|
|
5530
|
+
{ input : 'text', key : 'apiKey', credential : 'SHOPIFY_API_KEY', label : 'API key', message : 'The app\'s Client ID — Dev Dashboard, the app, Settings.', required : true },
|
|
5531
|
+
{ 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 },
|
|
5532
|
+
{ 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 },
|
|
5533
|
+
{ input : 'text', key : 'listingUrl', credential : 'SHOPIFY_APP_LISTING_URL', label : 'App listing URL', message : 'Where "View on the Shopify App Store" sends merchants — the public listing, or the Dev Dashboard install link for the dev app.', required : true },
|
|
5534
|
+
// The usage meter's event handle, when the plan config's meter ever
|
|
5535
|
+
// changes: stored value overrides the built-in default
|
|
5536
|
+
// (events.order.handle) at the mint point, and `format : 'slug'`
|
|
5537
|
+
// normalizes it on save — a handle can carry no spaces or capitals,
|
|
5538
|
+
// and classification against it is case-sensitive. Optional: empty
|
|
5539
|
+
// means the default, and the sender still refuses any handle that
|
|
5540
|
+
// disagrees with the transaction it minted.
|
|
5541
|
+
{ format : 'slug', input : 'text', key : 'orderEventHandle', label : 'Order event handle', message : 'Must match the usage meter\'s Handle field on the plan exactly — lowercase and hyphens. Leave empty to use drawbridge-orders.', required : false, setting : true },
|
|
5542
|
+
// The Partner API credential set — the ONE place meter billing is
|
|
5543
|
+
// observable (CHARGE_USAGE accrual events). Org id and numeric app
|
|
5544
|
+
// id come from the Partner Dashboard; the token from Settings →
|
|
5545
|
+
// Partner API clients (Manage-apps permission). All optional: the
|
|
5546
|
+
// accrual probe simply stays off until they exist.
|
|
5547
|
+
{ input : 'text', key : 'partnerOrgId', label : 'Partner organization ID', message : 'The number in the Partner Dashboard URL — partners.shopify.com/<this number>/… — NOT the admin organization_id from the app listing link.', required : false, setting : true },
|
|
5548
|
+
{ input : 'text', key : 'partnerAppId', label : 'Partner app ID (numeric)', message : 'The numeric id in the app\'s Partner Dashboard URL — not the Client ID.', required : false, setting : true },
|
|
5549
|
+
{ input : 'password', key : 'partnerToken', label : 'Partner API token', message : 'Partner Dashboard, Settings, Partner API clients — create one with Manage apps and paste the prtapi_ token.', redact : true, required : false, setting : true }
|
|
5550
|
+
],
|
|
5551
|
+
icon,
|
|
5552
|
+
name : 'Shopify'
|
|
5553
|
+
}
|
|
5554
|
+
}
|
|
5359
5555
|
},
|
|
5360
5556
|
// A pre-launch integration: it only surfaces once the App Store listing
|
|
5361
5557
|
// exists and the app is fully configured. Requiring all four means it can
|
|
@@ -5784,6 +5980,34 @@ const leaves = ( node, path = [] ) => Object.entries( node || {} ).flatMap(
|
|
|
5784
5980
|
)
|
|
5785
5981
|
);
|
|
5786
5982
|
|
|
5983
|
+
// Checked for the three things that actually break a card:
|
|
5984
|
+
// it must be an svg a filename here means the file is elsewhere again
|
|
5985
|
+
// it must carry a viewBox without one it will not scale into the 50px tile
|
|
5986
|
+
// it must not wrap a raster Figma exports a placed bitmap inside an <svg>
|
|
5987
|
+
// shell, which looks like a vector, weighs 90KB and
|
|
5988
|
+
// blurs exactly like the png it actually is
|
|
5989
|
+
const checkIcon = ( owner, icon ) => {
|
|
5990
|
+
|
|
5991
|
+
if( typeof icon !== 'string' || ! icon.includes( '<svg' ) ){
|
|
5992
|
+
|
|
5993
|
+
throw new Error( owner + ' needs an icon — the svg markup itself, not a path to one' );
|
|
5994
|
+
|
|
5995
|
+
}
|
|
5996
|
+
|
|
5997
|
+
if( ! icon.includes( 'viewBox' ) ){
|
|
5998
|
+
|
|
5999
|
+
throw new Error( owner + ' icon has no viewBox, so it cannot scale' );
|
|
6000
|
+
|
|
6001
|
+
}
|
|
6002
|
+
|
|
6003
|
+
if( icon.includes( '<image' ) ){
|
|
6004
|
+
|
|
6005
|
+
throw new Error( owner + ' icon wraps a raster — re-export it as vector shapes' );
|
|
6006
|
+
|
|
6007
|
+
}
|
|
6008
|
+
|
|
6009
|
+
};
|
|
6010
|
+
|
|
5787
6011
|
// WHAT A CONNECTION MUST DECLARE. Thrown at import rather than discovered by a
|
|
5788
6012
|
// merchant looking at a broken card, or by a workflow that accepted a step it
|
|
5789
6013
|
// could never run.
|
|
@@ -5883,9 +6107,18 @@ const build = ( manifest ) => {
|
|
|
5883
6107
|
|
|
5884
6108
|
}
|
|
5885
6109
|
|
|
5886
|
-
// DRAWBRIDGE'S OWN CREDENTIALS
|
|
5887
|
-
//
|
|
5888
|
-
//
|
|
6110
|
+
// DRAWBRIDGE'S OWN CREDENTIALS, declared under the VENDOR that holds them.
|
|
6111
|
+
//
|
|
6112
|
+
// A provider is a vendor Drawbridge has an account with; a connection is a
|
|
6113
|
+
// thing a merchant turns on. They are not one-to-one in either direction:
|
|
6114
|
+
// `drawbridge` spends SendGrid, Twilio and HubSpot, and a future Google row
|
|
6115
|
+
// would be spent by analytics, geocoding and whatever comes after. So a
|
|
6116
|
+
// manifest declares the fields IT spends, subkeyed by vendor, and mergeVendors
|
|
6117
|
+
// below unions every manifest's declarations into one vendor each. Nobody
|
|
6118
|
+
// owns a vendor — removing a manifest removes its fields and nothing else.
|
|
6119
|
+
//
|
|
6120
|
+
// A connection with no third party behind it — webhook — declares no block at
|
|
6121
|
+
// all, which is what keeps it off the provider screen.
|
|
5889
6122
|
//
|
|
5890
6123
|
// Every field is checked harder than a merchant field: it must be editable
|
|
5891
6124
|
// (an admin cannot type into a read-only descriptor, and a credential nobody
|
|
@@ -5894,56 +6127,69 @@ const build = ( manifest ) => {
|
|
|
5894
6127
|
// providerFields is what the api's redaction derives from, so a password field
|
|
5895
6128
|
// that forgot `redact : true` is a platform secret handed back over the wire
|
|
5896
6129
|
// to every admin screen that asks.
|
|
5897
|
-
|
|
6130
|
+
if( manifest.provider?.fields ){
|
|
5898
6131
|
|
|
5899
|
-
|
|
6132
|
+
throw new Error( manifest.slug + ' declares provider.fields — fields live under provider.vendors.<vendor> now, keyed by the vendor that holds them' );
|
|
5900
6133
|
|
|
5901
|
-
|
|
6134
|
+
}
|
|
5902
6135
|
|
|
5903
|
-
|
|
6136
|
+
const declaredKeys = new Set();
|
|
5904
6137
|
|
|
5905
|
-
|
|
6138
|
+
for( const [ vendor, declared ] of Object.entries( manifest.provider?.vendors || {} ) ){
|
|
5906
6139
|
|
|
5907
|
-
|
|
6140
|
+
if( ! declared?.name ) throw new Error( manifest.slug + ' declares vendor ' + vendor + ' with no name' );
|
|
5908
6141
|
|
|
5909
|
-
|
|
6142
|
+
// The admin card shows the vendor's own mark, which is not always the
|
|
6143
|
+
// connection's — SendGrid's card is not the Drawbridge logo.
|
|
6144
|
+
checkIcon( manifest.slug + ' vendor ' + vendor, declared.icon );
|
|
5910
6145
|
|
|
5911
|
-
if(
|
|
6146
|
+
if( ! Array.isArray( declared.fields ) || ! declared.fields.length ){
|
|
5912
6147
|
|
|
5913
|
-
throw new Error( manifest.slug + '
|
|
6148
|
+
throw new Error( manifest.slug + ' declares vendor ' + vendor + ' with no fields — a vendor nothing is typed into holds nothing' );
|
|
5914
6149
|
|
|
5915
6150
|
}
|
|
5916
6151
|
|
|
5917
|
-
|
|
6152
|
+
for( const field of declared.fields ){
|
|
5918
6153
|
|
|
5919
|
-
|
|
5920
|
-
// added — which is what the old arrangement allowed, with the markup in one
|
|
5921
|
-
// repo and the file in another.
|
|
5922
|
-
//
|
|
5923
|
-
// Checked for the three things that actually break a card:
|
|
5924
|
-
// it must be an svg a filename here means the file is elsewhere again
|
|
5925
|
-
// it must carry a viewBox without one it will not scale into the 50px tile
|
|
5926
|
-
// it must not wrap a raster Figma exports a placed bitmap inside an <svg>
|
|
5927
|
-
// shell, which looks like a vector, weighs 90KB and
|
|
5928
|
-
// blurs exactly like the png it actually is
|
|
5929
|
-
if( typeof manifest?.icon !== 'string' || ! manifest.icon.includes( '<svg' ) ){
|
|
6154
|
+
if( ! field?.key || ! field?.label ){
|
|
5930
6155
|
|
|
5931
|
-
|
|
6156
|
+
throw new Error( manifest.slug + ' declares a provider field with no key or label' );
|
|
5932
6157
|
|
|
5933
|
-
|
|
6158
|
+
}
|
|
5934
6159
|
|
|
5935
|
-
|
|
6160
|
+
if( ! INPUTS.includes( field.input ) ){
|
|
5936
6161
|
|
|
5937
|
-
|
|
6162
|
+
throw new Error( manifest.slug + '.provider.' + field.key + ' needs an input the admin form can render — one of ' + INPUTS.join( ', ' ) );
|
|
5938
6163
|
|
|
5939
|
-
|
|
6164
|
+
}
|
|
6165
|
+
|
|
6166
|
+
if( field.input === 'password' && ! field.redact ){
|
|
6167
|
+
|
|
6168
|
+
throw new Error( manifest.slug + '.provider.' + field.key + ' is a password and must declare redact : true — the api would hand the value back' );
|
|
6169
|
+
|
|
6170
|
+
}
|
|
6171
|
+
|
|
6172
|
+
// ONE KEY PER CONNECTION across its vendors. vendorSettings() hands a
|
|
6173
|
+
// connection every vendor's settings merged into one object, so two
|
|
6174
|
+
// vendors on one manifest sharing a key would be one value silently
|
|
6175
|
+
// winning over the other.
|
|
6176
|
+
if( declaredKeys.has( field.key ) ){
|
|
5940
6177
|
|
|
5941
|
-
|
|
6178
|
+
throw new Error( manifest.slug + ' declares ' + field.key + ' under two vendors — vendorSettings() merges them and one would win' );
|
|
5942
6179
|
|
|
5943
|
-
|
|
6180
|
+
}
|
|
6181
|
+
|
|
6182
|
+
declaredKeys.add( field.key );
|
|
6183
|
+
|
|
6184
|
+
}
|
|
5944
6185
|
|
|
5945
6186
|
}
|
|
5946
6187
|
|
|
6188
|
+
// THE ICON RIDES WITH THE MANIFEST, so a vendor cannot name an asset nobody
|
|
6189
|
+
// added — which is what the old arrangement allowed, with the markup in one
|
|
6190
|
+
// repo and the file in another.
|
|
6191
|
+
checkIcon( manifest.slug, manifest?.icon );
|
|
6192
|
+
|
|
5947
6193
|
// `group` does the actual grouping on the connections page, and `category`
|
|
5948
6194
|
// was a second word for the same fact — so it is gone rather than kept as an
|
|
5949
6195
|
// alias nobody could tell apart from this one.
|
|
@@ -6220,6 +6466,96 @@ const connections = Object.freeze({
|
|
|
6220
6466
|
webhook : build( webhook )
|
|
6221
6467
|
});
|
|
6222
6468
|
|
|
6469
|
+
// A field declaration, with its keys in a fixed order, so two manifests that
|
|
6470
|
+
// declare the same field can be compared as strings whatever order they wrote
|
|
6471
|
+
// the keys in.
|
|
6472
|
+
const canonical = ( field ) => JSON.stringify( Object.fromEntries( Object.entries( field ).sort() ) );
|
|
6473
|
+
|
|
6474
|
+
// DRAWBRIDGE'S OWN VENDOR ACCOUNTS, one entry per vendor, unioned from every
|
|
6475
|
+
// manifest that spends one. This is what the provider screen lists and what
|
|
6476
|
+
// the `provider` collection is keyed by: one row per key here.
|
|
6477
|
+
//
|
|
6478
|
+
// Exported as a function over a catalog, and not only as the merged constant,
|
|
6479
|
+
// so the guards can be exercised against a catalog that breaks them — the real
|
|
6480
|
+
// one is built to pass.
|
|
6481
|
+
//
|
|
6482
|
+
// THREE GUARDS, and each one is a way two files could disagree about one fact:
|
|
6483
|
+
// the vendor's name and icon the same vendor drawn two ways is two cards
|
|
6484
|
+
// a field two manifests both the same key declared differently is a form
|
|
6485
|
+
// declare that renders whichever manifest loaded last
|
|
6486
|
+
// a credential name the join between `requires` and a row — two
|
|
6487
|
+
// fields answering one name is one value silently
|
|
6488
|
+
// winning, which is exactly the drift a row per
|
|
6489
|
+
// vendor exists to end
|
|
6490
|
+
const mergeVendors = ( catalog ) => {
|
|
6491
|
+
|
|
6492
|
+
const merged = {};
|
|
6493
|
+
const claimed = new Map();
|
|
6494
|
+
|
|
6495
|
+
for( const [ slug, manifest ] of Object.entries( catalog ) ){
|
|
6496
|
+
|
|
6497
|
+
for( const [ vendor, declared ] of Object.entries( manifest.provider?.vendors || {} ) ){
|
|
6498
|
+
|
|
6499
|
+
const held = merged[ vendor ] || ( merged[ vendor ] = { fields : [], icon : declared.icon, name : declared.name } );
|
|
6500
|
+
|
|
6501
|
+
if( held.name !== declared.name ){
|
|
6502
|
+
|
|
6503
|
+
throw new Error( slug + ' names vendor ' + vendor + ' "' + declared.name + '" but another manifest names it "' + held.name + '"' );
|
|
6504
|
+
|
|
6505
|
+
}
|
|
6506
|
+
|
|
6507
|
+
if( held.icon !== declared.icon ){
|
|
6508
|
+
|
|
6509
|
+
throw new Error( slug + ' declares a different icon for vendor ' + vendor + ' than another manifest — one vendor, one mark' );
|
|
6510
|
+
|
|
6511
|
+
}
|
|
6512
|
+
|
|
6513
|
+
for( const field of declared.fields ){
|
|
6514
|
+
|
|
6515
|
+
const existing = held.fields.find( ( entry ) => entry.key === field.key );
|
|
6516
|
+
|
|
6517
|
+
if( existing ){
|
|
6518
|
+
|
|
6519
|
+
if( canonical( existing ) !== canonical( field ) ){
|
|
6520
|
+
|
|
6521
|
+
throw new Error( slug + ' declares ' + vendor + '.' + field.key + ' differently from another manifest — a field two connections spend is declared identically in both' );
|
|
6522
|
+
|
|
6523
|
+
}
|
|
6524
|
+
|
|
6525
|
+
continue;
|
|
6526
|
+
|
|
6527
|
+
}
|
|
6528
|
+
|
|
6529
|
+
if( field.credential ){
|
|
6530
|
+
|
|
6531
|
+
const owner = claimed.get( field.credential );
|
|
6532
|
+
|
|
6533
|
+
if( owner ){
|
|
6534
|
+
|
|
6535
|
+
throw new Error( field.credential + ' is claimed by both ' + owner + ' and ' + vendor + '.' + field.key + ' — one would silently overwrite the other' );
|
|
6536
|
+
|
|
6537
|
+
}
|
|
6538
|
+
|
|
6539
|
+
claimed.set( field.credential, vendor + '.' + field.key );
|
|
6540
|
+
|
|
6541
|
+
}
|
|
6542
|
+
|
|
6543
|
+
held.fields.push( field );
|
|
6544
|
+
|
|
6545
|
+
}
|
|
6546
|
+
|
|
6547
|
+
}
|
|
6548
|
+
|
|
6549
|
+
}
|
|
6550
|
+
|
|
6551
|
+
return Object.freeze( Object.fromEntries(
|
|
6552
|
+
Object.entries( merged ).map( ( [ vendor, held ] ) => [ vendor, Object.freeze({ ...held, fields : Object.freeze( held.fields ) }) ] )
|
|
6553
|
+
) );
|
|
6554
|
+
|
|
6555
|
+
};
|
|
6556
|
+
|
|
6557
|
+
const vendors = mergeVendors( connections );
|
|
6558
|
+
|
|
6223
6559
|
// A STEP TYPE MAY BE SHARED, BUT NOT ITS QUEUE.
|
|
6224
6560
|
//
|
|
6225
6561
|
// A step type belongs to the CAPABILITY rather than to whoever implements it —
|
|
@@ -6622,4 +6958,4 @@ const resolveConnection = ( item, data, env = {} ) => {
|
|
|
6622
6958
|
|
|
6623
6959
|
};
|
|
6624
6960
|
|
|
6625
|
-
export { AUTH_TYPES, GROUPS, HOOKS, HOOK_EFFECTS, HOOK_NAMES, HOOK_OPTIONS, HOOK_PROPS, INPUTS, OAUTH_FIELDS, OUTCOMES, RETIRED, STATUSES, STEPS, STEP_TYPES, WRITE_OPERATIONS, accessToken, authToken, availableConnections, build, catalogConnections, connectFields, connectionSteps, connections, effectsOf, hookSupport, isStale, mergeSettings, projectConnection, publicConnectionKeys, publicSettingsBySlug, redactSettings, resolveConnection, runHook, scopesMessage, stepLabels, stepQueues, stepRoutes, tokenSettings };
|
|
6961
|
+
export { AUTH_TYPES, GROUPS, HOOKS, HOOK_EFFECTS, HOOK_NAMES, HOOK_OPTIONS, HOOK_PROPS, INPUTS, OAUTH_FIELDS, OUTCOMES, RETIRED, STATUSES, STEPS, STEP_TYPES, WRITE_OPERATIONS, accessToken, authToken, availableConnections, build, catalogConnections, connectFields, connectionSteps, connections, effectsOf, hookSupport, isStale, mergeSettings, mergeVendors, projectConnection, publicConnectionKeys, publicSettingsBySlug, redactSettings, resolveConnection, runHook, scopesMessage, stepLabels, stepQueues, stepRoutes, tokenSettings, vendors };
|