@drawbridge/drawbridge-utils 0.0.136 → 0.0.139
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 +175 -29
- package/dist/connections/index.d.cts +230 -15
- package/dist/connections/index.d.ts +230 -15
- package/dist/connections/index.js +175 -29
- package/dist/providers.cjs +181 -35
- package/dist/providers.d.cts +2 -0
- package/dist/providers.d.ts +2 -0
- package/dist/providers.js +181 -35
- package/package.json +1 -1
|
@@ -639,6 +639,9 @@ var attentive_default2 = {
|
|
|
639
639
|
},
|
|
640
640
|
// A contact destination, like Klaviyo and Mailchimp — a merchant could
|
|
641
641
|
// reasonably keep several up to date at once.
|
|
642
|
+
// No vendor-facing event identities — declared empty so the contract stays
|
|
643
|
+
// explicit: a vendor that gains one adds it here, never as a side channel.
|
|
644
|
+
events: {},
|
|
642
645
|
exclusive: false,
|
|
643
646
|
feature: "organization:connection:attentive",
|
|
644
647
|
fields: [
|
|
@@ -1819,6 +1822,9 @@ var drawbridge_default2 = {
|
|
|
1819
1822
|
"Nothing to do. These steps are available in every workflow builder."
|
|
1820
1823
|
]
|
|
1821
1824
|
},
|
|
1825
|
+
// No vendor-facing event identities — declared empty so the contract stays
|
|
1826
|
+
// explicit: a vendor that gains one adds it here, never as a side channel.
|
|
1827
|
+
events: {},
|
|
1822
1828
|
exclusive: false,
|
|
1823
1829
|
fields: [],
|
|
1824
1830
|
group: "developer",
|
|
@@ -2603,6 +2609,9 @@ var klaviyo_default2 = {
|
|
|
2603
2609
|
// current — so the exclusivity that once applied when these were SENDERS is
|
|
2604
2610
|
// deliberately gone. That was removed once already; declaring it out loud is
|
|
2605
2611
|
// what stops it coming back by inference.
|
|
2612
|
+
// No vendor-facing event identities — declared empty so the contract stays
|
|
2613
|
+
// explicit: a vendor that gains one adds it here, never as a side channel.
|
|
2614
|
+
events: {},
|
|
2606
2615
|
exclusive: false,
|
|
2607
2616
|
feature: "organization:connection:klaviyo",
|
|
2608
2617
|
fields: [
|
|
@@ -3079,6 +3088,9 @@ var mailchimp_default2 = {
|
|
|
3079
3088
|
// picking two providers to send the same mail was meaningless. As contact
|
|
3080
3089
|
// syncs they are destinations, and a merchant could reasonably keep several
|
|
3081
3090
|
// up to date, so the exclusivity is gone.
|
|
3091
|
+
// No vendor-facing event identities — declared empty so the contract stays
|
|
3092
|
+
// explicit: a vendor that gains one adds it here, never as a side channel.
|
|
3093
|
+
events: {},
|
|
3082
3094
|
exclusive: false,
|
|
3083
3095
|
feature: "organization:connection:mailchimp",
|
|
3084
3096
|
fields: [
|
|
@@ -3354,6 +3366,22 @@ var toCanonicalEmail = (value) => {
|
|
|
3354
3366
|
return local + "@" + domain;
|
|
3355
3367
|
};
|
|
3356
3368
|
|
|
3369
|
+
// lib/slugify.js
|
|
3370
|
+
var import_slugify = __toESM(require("slugify"), 1);
|
|
3371
|
+
var slugify = (value, nochars = false) => {
|
|
3372
|
+
const remove = nochars ? /[-?*+~.()'"!:@#^_{}\[\];,/\\]/g : /[?*+~.()'"!:@#^_{}\[\];,/\\]/g;
|
|
3373
|
+
const replacement = nochars ? "" : "-";
|
|
3374
|
+
return (0, import_slugify.default)(
|
|
3375
|
+
value,
|
|
3376
|
+
{
|
|
3377
|
+
remove,
|
|
3378
|
+
replacement,
|
|
3379
|
+
lower: true,
|
|
3380
|
+
trim: true
|
|
3381
|
+
}
|
|
3382
|
+
);
|
|
3383
|
+
};
|
|
3384
|
+
|
|
3357
3385
|
// lib/connections/providers/shopify.js
|
|
3358
3386
|
var toLine = ({
|
|
3359
3387
|
price,
|
|
@@ -3389,6 +3417,7 @@ var attributeLineItems = (lineItems = []) => lineItems.reduce(
|
|
|
3389
3417
|
{ attrMap: {}, attributedGross: 0, attributedLines: [] }
|
|
3390
3418
|
);
|
|
3391
3419
|
var generateDiscountCode = (0, import_nanoid2.customAlphabet)("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", 8);
|
|
3420
|
+
var ORDER_EVENT_HANDLE = slugify("drawbridge-orders");
|
|
3392
3421
|
var REFRESH_TOKEN_FRESHNESS_BUFFER_MS = 5 * 24 * 60 * 60 * 1e3;
|
|
3393
3422
|
var OAUTH_ERROR_SOURCE = "oauth";
|
|
3394
3423
|
var OAUTH_GRANT_REVOKED_CODES = ["application_cannot_be_found", "invalid_grant"];
|
|
@@ -3446,7 +3475,7 @@ var shopify_default2 = {
|
|
|
3446
3475
|
guide: [
|
|
3447
3476
|
"Open the Drawbridge listing on the Shopify App Store.",
|
|
3448
3477
|
"Install the app on the store you want to connect. It opens in Shopify admin and stays there.",
|
|
3449
|
-
"
|
|
3478
|
+
"Approve the Drawbridge plan when prompted \u2014 during install, or from the connection page here. The connection shows Pending until you do, then Active.",
|
|
3450
3479
|
"Come back here \u2014 the connections list updates on its own once the install lands."
|
|
3451
3480
|
],
|
|
3452
3481
|
// Names where the link GOES rather than what it does: installing happens on
|
|
@@ -3460,6 +3489,29 @@ var shopify_default2 = {
|
|
|
3460
3489
|
// ONE STORE PER ORGANIZATION. Two Shopify stores on one org would give every
|
|
3461
3490
|
// attributed order two possible sources.
|
|
3462
3491
|
exclusive: true,
|
|
3492
|
+
// VENDOR-FACING EVENT IDENTITY, published so readers outside this package
|
|
3493
|
+
// (drawbridge-sync's usage queue, an operator checking the Dev Dashboard)
|
|
3494
|
+
// see the same handle the order hook mints into transactions and enqueues.
|
|
3495
|
+
events: {
|
|
3496
|
+
order: {
|
|
3497
|
+
handle: ORDER_EVENT_HANDLE,
|
|
3498
|
+
// THE SUBSCRIPTION THE APP MINTS FOR ITSELF (Billing API, not the
|
|
3499
|
+
// managed-pricing meter system): a $0 recurring line plus this usage
|
|
3500
|
+
// line, approved by the org owner at Shopify's confirmation screen.
|
|
3501
|
+
// `terms` is the merchant-visible consent copy; "up to" because the
|
|
3502
|
+
// actual rate is plan-derived (conversionRate) and must never exceed
|
|
3503
|
+
// what was approved. cappedAmount bounds one 30-day cycle's usage
|
|
3504
|
+
// charges — a record beyond it errors until the merchant raises it.
|
|
3505
|
+
// ponytail: $1,000 cap ≈ $66k attributed revenue/cycle at 1.5%; raise
|
|
3506
|
+
// via a re-approved subscription when a merchant outgrows it.
|
|
3507
|
+
pricing: {
|
|
3508
|
+
cappedAmount: 1e3,
|
|
3509
|
+
currency: "usd",
|
|
3510
|
+
name: "Drawbridge Orders",
|
|
3511
|
+
terms: "Up to 1.5% of revenue from orders attributed to Drawbridge campaigns"
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
},
|
|
3463
3515
|
feature: "organization:connection:shopify",
|
|
3464
3516
|
fields: [
|
|
3465
3517
|
{
|
|
@@ -3645,7 +3697,7 @@ var shopify_default2 = {
|
|
|
3645
3697
|
// for one purchase. The hook used to loop for this itself; describing
|
|
3646
3698
|
// the writes moved the retry to the queue, with the same guarantee.
|
|
3647
3699
|
order: async ({ connection: connection2, context }, { logger: logger2, mintId, read } = {}) => {
|
|
3648
|
-
var _a, _b, _c, _d, _e, _f;
|
|
3700
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
3649
3701
|
const {
|
|
3650
3702
|
advertisement,
|
|
3651
3703
|
created_at: createdAt,
|
|
@@ -3887,8 +3939,26 @@ var shopify_default2 = {
|
|
|
3887
3939
|
});
|
|
3888
3940
|
}
|
|
3889
3941
|
}
|
|
3890
|
-
const
|
|
3942
|
+
const billable = (org == null ? void 0 : org.billingProvider) === "shopify" && fee > 0 && !backfill;
|
|
3943
|
+
if (billable && !((_f = connection2 == null ? void 0 : connection2.source) == null ? void 0 : _f.id)) {
|
|
3944
|
+
(_g = logger2 == null ? void 0 : logger2.error) == null ? void 0 : _g.call(logger2, new Error(
|
|
3945
|
+
"shopify.usage.billing.skipped on order " + orderId + ": " + Math.round(fee * 100) + " cents not billed \u2014 connection " + (connection2 == null ? void 0 : connection2.id) + " has no source.id"
|
|
3946
|
+
), {
|
|
3947
|
+
extra: {
|
|
3948
|
+
connectionId: connection2 == null ? void 0 : connection2.id,
|
|
3949
|
+
fee,
|
|
3950
|
+
orderDocId,
|
|
3951
|
+
orderId: String(orderId)
|
|
3952
|
+
}
|
|
3953
|
+
});
|
|
3954
|
+
}
|
|
3955
|
+
const enqueues = billable && ((_h = connection2 == null ? void 0 : connection2.source) == null ? void 0 : _h.id) ? [{
|
|
3891
3956
|
data: {
|
|
3957
|
+
// The meter handle travels WITH the job so the sender can
|
|
3958
|
+
// verify it against the transaction prefix and refuse a
|
|
3959
|
+
// drifted pair — idempotency keys are permanent, so a
|
|
3960
|
+
// mistraceable event can never be resent under its own key.
|
|
3961
|
+
handle: ORDER_EVENT_HANDLE,
|
|
3892
3962
|
idempotencyKey: String(orderId),
|
|
3893
3963
|
orderDocId,
|
|
3894
3964
|
orderId: String(orderId),
|
|
@@ -3898,7 +3968,7 @@ var shopify_default2 = {
|
|
|
3898
3968
|
// here — the event handle plus the order id — and sent as the
|
|
3899
3969
|
// event's `reference`. queue/usage.js stamps the same id onto
|
|
3900
3970
|
// the order as billed.transaction.
|
|
3901
|
-
transaction: "
|
|
3971
|
+
transaction: ORDER_EVENT_HANDLE + "." + orderId,
|
|
3902
3972
|
value: Math.round(fee * 100)
|
|
3903
3973
|
},
|
|
3904
3974
|
name: "billing",
|
|
@@ -4041,7 +4111,8 @@ var shopify_default2 = {
|
|
|
4041
4111
|
// access token still works, reconciles the scopes the store granted
|
|
4042
4112
|
// against the ones the app now needs, and queues a webhook
|
|
4043
4113
|
// reconciliation.
|
|
4044
|
-
health: async ({ connection: connection2, workflow }, { adminToken, read, reconcileScopes, resolveSettings, rotateToken, shopify } = {}) => {
|
|
4114
|
+
health: async ({ connection: connection2, workflow }, { adminToken, logger: logger2, read, reconcileScopes, resolveSettings, rotateToken, shopify } = {}) => {
|
|
4115
|
+
var _a, _b;
|
|
4045
4116
|
const request2 = {
|
|
4046
4117
|
connectionId: workflow.connection,
|
|
4047
4118
|
organizationId: workflow.organization,
|
|
@@ -4060,7 +4131,50 @@ var shopify_default2 = {
|
|
|
4060
4131
|
}
|
|
4061
4132
|
await shopify.oauth.ping({ adminAccessToken, domain: connection2.shop });
|
|
4062
4133
|
const scopesMissing = await reconcileScopes({ shop: connection2.shop });
|
|
4134
|
+
const writes = [];
|
|
4135
|
+
let source = connection2.source || null;
|
|
4136
|
+
let sourceRepaired = false;
|
|
4137
|
+
let metered = (source == null ? void 0 : source.metered) ?? null;
|
|
4138
|
+
let shopCurrency = null;
|
|
4139
|
+
let billingProbeFailed = false;
|
|
4140
|
+
try {
|
|
4141
|
+
if (!(source == null ? void 0 : source.id)) {
|
|
4142
|
+
const shopData = await shopify.oauth.getShop({ adminAccessToken, shop: connection2.shop });
|
|
4143
|
+
source = {
|
|
4144
|
+
domain: shopData.myshopifyDomain,
|
|
4145
|
+
id: String(shopData.id),
|
|
4146
|
+
label: shopData.name
|
|
4147
|
+
};
|
|
4148
|
+
shopCurrency = String(shopData.currency || "").toLowerCase() || null;
|
|
4149
|
+
sourceRepaired = true;
|
|
4150
|
+
}
|
|
4151
|
+
const { subscriptions } = await shopify.admin.getActiveAppSubscriptions({
|
|
4152
|
+
adminAccessToken,
|
|
4153
|
+
domain: connection2.shop
|
|
4154
|
+
});
|
|
4155
|
+
if (subscriptions.length && subscriptions.every((subscription) => typeof subscription.metered === "boolean")) {
|
|
4156
|
+
metered = subscriptions.some((subscription) => subscription.metered);
|
|
4157
|
+
}
|
|
4158
|
+
} catch (probeError) {
|
|
4159
|
+
billingProbeFailed = true;
|
|
4160
|
+
(_a = logger2 == null ? void 0 : logger2.warn) == null ? void 0 : _a.call(logger2, "shopify.health.billing.probe.failed", {
|
|
4161
|
+
connectionId: connection2.id,
|
|
4162
|
+
message: probeError == null ? void 0 : probeError.message,
|
|
4163
|
+
shop: connection2.shop
|
|
4164
|
+
});
|
|
4165
|
+
}
|
|
4166
|
+
if (sourceRepaired || metered !== null && metered !== (((_b = connection2.source) == null ? void 0 : _b.metered) ?? null)) {
|
|
4167
|
+
writes.push({
|
|
4168
|
+
collection: "connection",
|
|
4169
|
+
data: {
|
|
4170
|
+
$set: sourceRepaired ? { source: { ...source, ...metered !== null && { metered } } } : { "source.metered": metered }
|
|
4171
|
+
},
|
|
4172
|
+
operation: "update",
|
|
4173
|
+
query: { id: connection2.id }
|
|
4174
|
+
});
|
|
4175
|
+
}
|
|
4063
4176
|
return {
|
|
4177
|
+
writes,
|
|
4064
4178
|
enqueues: [{
|
|
4065
4179
|
data: {
|
|
4066
4180
|
data: {
|
|
@@ -4073,13 +4187,19 @@ var shopify_default2 = {
|
|
|
4073
4187
|
options: { jobId: "connection.update.register." + workflow.connection + "." + (0, import_node_crypto5.randomUUID)() },
|
|
4074
4188
|
queue: "connection"
|
|
4075
4189
|
}],
|
|
4076
|
-
message: (scopesMissing == null ? void 0 : scopesMissing.length) ? "Health check: ping ok, webhooks reconciled \u2014 connection errored, granted scopes are missing: " + scopesMissing.join(", ") + "." : refreshTokenRotated ? "Health check passed \u2014 refresh token rotated, ping ok, webhooks reconciled." : "Health check passed \u2014 ping ok, webhooks reconciled.",
|
|
4190
|
+
message: ((scopesMissing == null ? void 0 : scopesMissing.length) ? "Health check: ping ok, webhooks reconciled \u2014 connection errored, granted scopes are missing: " + scopesMissing.join(", ") + "." : refreshTokenRotated ? "Health check passed \u2014 refresh token rotated, ping ok, webhooks reconciled." : "Health check passed \u2014 ping ok, webhooks reconciled.") + (sourceRepaired ? " Billing source repaired from the live shop." : "") + (metered === false ? " No usage line on the store's approved plan \u2014 order billing needs the merchant to approve the updated plan." : ""),
|
|
4077
4191
|
request: request2,
|
|
4078
4192
|
response: {
|
|
4193
|
+
billingProbeFailed,
|
|
4194
|
+
metered,
|
|
4079
4195
|
pingedAt: /* @__PURE__ */ new Date(),
|
|
4080
4196
|
refreshTokenExpiresAt: refreshTokenExpiresAt || null,
|
|
4081
4197
|
refreshTokenRotated,
|
|
4082
4198
|
scopesMissing,
|
|
4199
|
+
// The live settlement currency, for an operator — never
|
|
4200
|
+
// written, see above.
|
|
4201
|
+
...shopCurrency && { shopCurrency },
|
|
4202
|
+
sourceRepaired,
|
|
4083
4203
|
webhookReconciliationQueued: true
|
|
4084
4204
|
}
|
|
4085
4205
|
};
|
|
@@ -4347,12 +4467,35 @@ var shopify_default2 = {
|
|
|
4347
4467
|
//
|
|
4348
4468
|
// Scope drift is NOT here: drawbridge-sync writes it onto the connection
|
|
4349
4469
|
// document, and the document's own warnings render beside these.
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4470
|
+
// Both cards carry `action : 'billing'`: the dashboard renders it as a real
|
|
4471
|
+
// button that mints the corrective subscription (POST .../billing) and sends
|
|
4472
|
+
// the owner to Shopify's confirmation screen — the fix lives in OUR
|
|
4473
|
+
// dashboard, and the one Shopify-hosted step is the charge consent no app
|
|
4474
|
+
// can perform for a merchant.
|
|
4475
|
+
tasks: (data2) => {
|
|
4476
|
+
var _a;
|
|
4477
|
+
return [
|
|
4478
|
+
...(data2 == null ? void 0 : data2.status) === "pending" ? [
|
|
4479
|
+
{
|
|
4480
|
+
action: "billing",
|
|
4481
|
+
message: "Approve the Drawbridge plan to activate this connection. You'll confirm the pricing on Shopify, then everything else happens here.",
|
|
4482
|
+
title: "Approve your plan"
|
|
4483
|
+
}
|
|
4484
|
+
] : [],
|
|
4485
|
+
// The store's ACTIVE approval carries no usage component — stamped by
|
|
4486
|
+
// the daily health check and the approval webhook (`source.metered`).
|
|
4487
|
+
// Approved pricing is never retroactive, so the fix is a fresh approval.
|
|
4488
|
+
// Gated on an EXPLICIT false — a connection the probe hasn't reached
|
|
4489
|
+
// (or couldn't read) shows nothing rather than nagging on silence.
|
|
4490
|
+
...(data2 == null ? void 0 : data2.status) === "active" && ((_a = data2 == null ? void 0 : data2.source) == null ? void 0 : _a.metered) === false ? [
|
|
4491
|
+
{
|
|
4492
|
+
action: "billing",
|
|
4493
|
+
message: "Order billing isn't set up for this store, so campaign-attributed orders aren't being charged. Approve the updated plan to fix it \u2014 one click here, one confirmation on Shopify.",
|
|
4494
|
+
title: "Fix order billing"
|
|
4495
|
+
}
|
|
4496
|
+
] : []
|
|
4497
|
+
];
|
|
4498
|
+
},
|
|
4356
4499
|
title: "Shopify"
|
|
4357
4500
|
};
|
|
4358
4501
|
|
|
@@ -4526,6 +4669,9 @@ var webhook_default = {
|
|
|
4526
4669
|
// Nothing to be exclusive with — there is no second webhook vendor, and a
|
|
4527
4670
|
// merchant with two endpoints is a step-level choice rather than a second
|
|
4528
4671
|
// connection.
|
|
4672
|
+
// No vendor-facing event identities — declared empty so the contract stays
|
|
4673
|
+
// explicit: a vendor that gains one adds it here, never as a side channel.
|
|
4674
|
+
events: {},
|
|
4529
4675
|
exclusive: false,
|
|
4530
4676
|
feature: "organization:connection:webhook",
|
|
4531
4677
|
fields: [
|
|
@@ -4817,16 +4963,16 @@ var connections = Object.freeze({
|
|
|
4817
4963
|
(() => {
|
|
4818
4964
|
var _a;
|
|
4819
4965
|
const routes = {};
|
|
4820
|
-
for (const [
|
|
4966
|
+
for (const [slug2, manifest] of Object.entries(connections)) {
|
|
4821
4967
|
for (const [name, step] of leaves(manifest.steps)) {
|
|
4822
4968
|
const type = "step." + name;
|
|
4823
4969
|
const queue = (_a = step({})) == null ? void 0 : _a.queue;
|
|
4824
4970
|
if (routes[type] && routes[type].queue !== queue) {
|
|
4825
4971
|
throw new Error(
|
|
4826
|
-
"Step " + type + " routes to " + routes[type].queue + " for " + routes[type].slug + " and " + queue + " for " +
|
|
4972
|
+
"Step " + type + " routes to " + routes[type].queue + " for " + routes[type].slug + " and " + queue + " for " + slug2 + " \u2014 one of them would be enqueued nowhere"
|
|
4827
4973
|
);
|
|
4828
4974
|
}
|
|
4829
|
-
routes[type] = { queue, slug };
|
|
4975
|
+
routes[type] = { queue, slug: slug2 };
|
|
4830
4976
|
}
|
|
4831
4977
|
}
|
|
4832
4978
|
})();
|
|
@@ -4839,21 +4985,21 @@ var catalogConnections = (env = {}) => Object.fromEntries(
|
|
|
4839
4985
|
Object.entries(availableConnections(env)).filter(([, manifest]) => !manifest.private)
|
|
4840
4986
|
);
|
|
4841
4987
|
var publicSettingsBySlug = Object.fromEntries(
|
|
4842
|
-
Object.entries(connections).map(([
|
|
4843
|
-
|
|
4988
|
+
Object.entries(connections).map(([slug2, manifest]) => [
|
|
4989
|
+
slug2,
|
|
4844
4990
|
manifest.fields.filter((field2) => !field2.redact).map((field2) => field2.key)
|
|
4845
4991
|
])
|
|
4846
4992
|
);
|
|
4847
4993
|
var connectionSteps = (env = {}) => Object.entries(availableConnections(env)).flatMap(
|
|
4848
|
-
([
|
|
4994
|
+
([slug2, manifest]) => leaves(manifest.steps).map(([name, step]) => ({ ...step({}), slug: slug2, type: "step." + name }))
|
|
4849
4995
|
);
|
|
4850
4996
|
var hookSupport = (name) => ({
|
|
4851
|
-
no: Object.keys(connections).filter((
|
|
4852
|
-
yes: Object.keys(connections).filter((
|
|
4997
|
+
no: Object.keys(connections).filter((slug2) => !implemented(connections[slug2].hooks, name)),
|
|
4998
|
+
yes: Object.keys(connections).filter((slug2) => implemented(connections[slug2].hooks, name))
|
|
4853
4999
|
});
|
|
4854
|
-
var connectFields = (
|
|
5000
|
+
var connectFields = (slug2) => {
|
|
4855
5001
|
var _a;
|
|
4856
|
-
return (((_a = connections[
|
|
5002
|
+
return (((_a = connections[slug2]) == null ? void 0 : _a.fields) || []).map(({ copy, from, hook, input, key, label, message, options, placeholder, redact, required, search }) => ({
|
|
4857
5003
|
...copy && { copy: true },
|
|
4858
5004
|
...hook && { hook },
|
|
4859
5005
|
...from && { from },
|
|
@@ -4873,18 +5019,18 @@ var connectFields = (slug) => {
|
|
|
4873
5019
|
secret: Boolean(redact)
|
|
4874
5020
|
}));
|
|
4875
5021
|
};
|
|
4876
|
-
var runHook = async (
|
|
4877
|
-
const manifest = connections[
|
|
4878
|
-
if (!manifest) return { outcome: OUTCOMES.unsupported, reason: "no such connection: " +
|
|
5022
|
+
var runHook = async (slug2, name, props = {}, options = {}) => {
|
|
5023
|
+
const manifest = connections[slug2];
|
|
5024
|
+
if (!manifest) return { outcome: OUTCOMES.unsupported, reason: "no such connection: " + slug2 };
|
|
4879
5025
|
const hook = name.split(".").reduce(
|
|
4880
5026
|
(node, key) => node && typeof node === "object" && Object.hasOwn(node, key) ? node[key] : void 0,
|
|
4881
5027
|
manifest.hooks
|
|
4882
5028
|
);
|
|
4883
5029
|
if (hook === false || hook == null) {
|
|
4884
|
-
return { outcome: OUTCOMES.unsupported, reason:
|
|
5030
|
+
return { outcome: OUTCOMES.unsupported, reason: slug2 + " does not implement " + name };
|
|
4885
5031
|
}
|
|
4886
5032
|
if (typeof hook !== "function") {
|
|
4887
|
-
return { outcome: OUTCOMES.unimplemented, reason:
|
|
5033
|
+
return { outcome: OUTCOMES.unimplemented, reason: slug2 + " implements " + name + " outside this package" };
|
|
4888
5034
|
}
|
|
4889
5035
|
try {
|
|
4890
5036
|
const result = await hook({ ...props, manifest }, options);
|
|
@@ -4919,9 +5065,9 @@ var mergeSettings = ({ existing, incoming }) => {
|
|
|
4919
5065
|
;
|
|
4920
5066
|
return merged;
|
|
4921
5067
|
};
|
|
4922
|
-
var redactSettings = ({ slug, settings }) => {
|
|
5068
|
+
var redactSettings = ({ slug: slug2, settings }) => {
|
|
4923
5069
|
if (!settings || typeof settings !== "object") return settings;
|
|
4924
|
-
const allowed = publicSettingsBySlug[
|
|
5070
|
+
const allowed = publicSettingsBySlug[slug2] || [];
|
|
4925
5071
|
return Object.fromEntries(
|
|
4926
5072
|
Object.entries(settings).filter(([key]) => allowed.includes(key))
|
|
4927
5073
|
);
|