@aglyn/plugins-outreach 1.0.0-beta.196 → 1.0.0-beta.197
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/package.json +14 -14
- package/src/lib/components/enroll-dialog.js +60 -1
- package/src/lib/components/enroll-dialog.js.map +1 -1
- package/src/lib/components/mailbox-card.js +11 -2
- package/src/lib/components/mailbox-card.js.map +1 -1
- package/src/lib/components/sequence-detail.js +1 -0
- package/src/lib/components/sequence-detail.js.map +1 -1
- package/src/lib/components/sequence-editor.d.ts +2 -0
- package/src/lib/components/sequence-editor.js +26 -2
- package/src/lib/components/sequence-editor.js.map +1 -1
- package/src/lib/components/sequence-step-card.d.ts +7 -0
- package/src/lib/components/sequence-step-card.js +37 -14
- package/src/lib/components/sequence-step-card.js.map +1 -1
- package/src/lib/components/sequences-section.js +12 -3
- package/src/lib/components/sequences-section.js.map +1 -1
- package/src/lib/components/step-test-dialog.d.ts +31 -0
- package/src/lib/components/step-test-dialog.js +247 -0
- package/src/lib/components/step-test-dialog.js.map +1 -0
- package/src/lib/components/use-outreach-api.d.ts +8 -1
- package/src/lib/components/use-outreach-api.js +4 -0
- package/src/lib/components/use-outreach-api.js.map +1 -1
- package/src/lib/components/use-outreach-data.d.ts +6 -1
- package/src/lib/components/use-outreach-data.js +6 -3
- package/src/lib/components/use-outreach-data.js.map +1 -1
- package/src/lib/constants/api-routes.d.ts +7 -0
- package/src/lib/constants/api-routes.js +6 -0
- package/src/lib/constants/api-routes.js.map +1 -1
- package/src/lib/model/outreach-api.d.ts +21 -1
- package/src/lib/model/outreach-api.js.map +1 -1
- package/src/lib/model/outreach.types.d.ts +6 -0
- package/src/lib/model/outreach.types.js.map +1 -1
- package/src/lib/model/step-preview.d.ts +7 -1
- package/src/lib/model/step-preview.js +3 -2
- package/src/lib/model/step-preview.js.map +1 -1
- package/src/lib/routes/platform-deps.d.ts +2 -2
- package/src/lib/routes/platform-deps.js +2 -2
- package/src/lib/routes/platform-deps.js.map +1 -1
- package/src/lib/routes/preview-routes.d.ts +24 -0
- package/src/lib/routes/preview-routes.js +99 -95
- package/src/lib/routes/preview-routes.js.map +1 -1
- package/src/lib/routes/register-routes.d.ts +7 -0
- package/src/lib/routes/register-routes.js +24 -0
- package/src/lib/routes/register-routes.js.map +1 -1
- package/src/lib/routes/step-test-routes.d.ts +88 -0
- package/src/lib/routes/step-test-routes.js +275 -0
- package/src/lib/routes/step-test-routes.js.map +1 -0
- package/src/lib/runtime/click-events.js +10 -0
- package/src/lib/runtime/click-events.js.map +1 -1
- package/src/lib/runtime/click-link.d.ts +11 -0
- package/src/lib/runtime/click-link.js +9 -4
- package/src/lib/runtime/click-link.js.map +1 -1
- package/src/lib/runtime/click-route.js +3 -0
- package/src/lib/runtime/click-route.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/runtime/click-route.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PluginWebApiHandler } from '@aglyn/aglyn/server'\nimport { OUTREACH_COLLECTIONS } from '../model/outreach.types'\nimport { recordOutreachSequenceTouch } from './campaign-credit'\nimport {\n isOutreachLinkId,\n readOutreachClickToken,\n readOutreachStoredLink,\n type OutreachClickTarget,\n} from './click-link'\nimport { recordOutreachClick } from './click-events'\nimport type { OutreachRuntimeDeps } from './runtime-deps'\n// The plain page both recipient-facing routes answer with: no theme, no\n// session, the reader's own colors. It lives beside the first route that\n// needed one rather than being copied for the second.\nimport { outreachUnsubscribePage } from './unsubscribe-route'\n\n/**\n * A LINK IN A TRACKED SEQUENCE EMAIL (AGL-3239, AGL-3297): the short\n * `GET /api/outreach/l/<id>` every send carries now, and the signed\n * `GET /api/outreach/click?t=…` the emails sent before it carry.\n *\n * ## The redirect happens first, and it happens for everyone\n *\n * The signed link carries its destination and needs no read; the short one\n * needs exactly one, of the document that names it. Then the 302 goes out. Recording is awaited after\n * the answer is built and never gates it — a Firestore that is slow or down\n * costs a number, not a visit.\n *\n * A link scanner is redirected exactly as a person is. Refusing one, or\n * answering it differently, is how a security gateway learns to report the\n * link as broken and the message as suspicious; it is counted apart instead\n * (`../engine/click-tracking.ts`).\n *\n * ## No session, and no release gate\n *\n * Registered as a recipient link, like the one-click unsubscribe. The\n * signature in the token is the whole of the authority, and a link that is\n * already in somebody's inbox has to keep working whether or not Outreach is\n * released to that organization today.\n *\n * ## A token that does not verify is not followed\n *\n * There is nowhere to send them: the destination WAS the thing that failed\n * the check. So it answers the same plain page the unsubscribe route\n * answers, rather than guessing — an endpoint that redirects to an unsigned\n * URL is an open redirect, whatever it is called.\n *\n * ## What the recipient's browser is told\n *\n * `no-store`, so a gateway's copy is never served to the person after it;\n * `no-referrer`, so the destination never learns the token; and `noindex`,\n * because a tracking link that reached a crawler should not be in an index.\n */\n\nconst REDIRECT_HEADERS = {\n 'Cache-Control': 'no-store, no-cache, must-revalidate',\n 'Referrer-Policy': 'no-referrer',\n 'X-Robots-Tag': 'noindex, nofollow',\n}\n\ntype ClickRouteDeps = Pick<OutreachRuntimeDeps, 'firestore' | 'now' | 'timeline' | 'campaignCredit'>\n\nconst methodNotAllowed = () =>\n new Response('Method not allowed', { status: 405, headers: { Allow: 'GET, HEAD' } })\n\nconst brokenLink = () =>\n outreachUnsubscribePage(\n {\n title: 'This link doesn’t work',\n body: 'This link isn’t valid, or it has been changed since it was sent. Try opening it from the original email.',\n },\n 400,\n )\n\n/**\n * The redirect, then the recording — the half both routes share once they\n * know where the link goes.\n */\nasync function followOutreachClick(\n deps: ClickRouteDeps,\n request: Request,\n target: OutreachClickTarget,\n): Promise<Response> {\n /*\n * Built by hand rather than with `Response.redirect`, which seals its\n * headers: the three above are the point of the answer as much as the\n * `Location` is. `target.url` is `URL.href` of an http(s) URL — both\n * readers return nothing else — so it is a header value that cannot carry\n * a newline into the response.\n */\n const redirect = new Response(null, {\n status: 302,\n headers: { ...REDIRECT_HEADERS, Location: target.url },\n })\n try {\n const outcome = await recordOutreachClick(deps, {\n target,\n method: request.method,\n userAgent: request.headers.get('user-agent'),\n })\n /*\n * A person's click — never a scanner's — on a sequence in a campaign\n * is their last campaign touch on the site (AGL-3254), so the booking\n * or the form they go on to make is credited to the sequence's\n * campaign by the door that credits every other one.\n */\n if (outcome.human && outcome.enrollment) {\n await recordOutreachSequenceTouch(deps, { enrollment: outcome.enrollment, atMs: deps.now() })\n }\n } catch (error) {\n // The visit already has its answer. A click we failed to count is a\n // missing number; a redirect we failed to send is a broken email.\n console.error('[outreach] a click could not be recorded', error)\n }\n return redirect\n}\n\n/** The signed link, `?t=…` — every tracked email sent before AGL-3297. */\nexport function createOutreachClickRoute(deps: ClickRouteDeps): PluginWebApiHandler {\n return async (request) => {\n // `HEAD` is answered because a gateway often sends one before the `GET`\n // a person makes; it is redirected and counted as the machine it is.\n if (request.method !== 'GET' && request.method !== 'HEAD') return methodNotAllowed()\n const target = readOutreachClickToken(new URL(request.url).searchParams.get('t'))\n if (!target) return brokenLink()\n return followOutreachClick(deps, request, target)\n }\n}\n\n/**\n * THE SHORT LINK (AGL-3297): `GET /api/outreach/l/<id>`.\n *\n * One read of `outreachLinks/<id>` stands where the signature check stood,\n * and everything after it is the signed route's: the same redirect, the same\n * headers, the same recording, scanners redirected and counted apart.\n *\n * The destination comes only from that document. An id that is malformed or\n * names nothing is answered with the plain page, never followed; a read that\n * FAILS is answered with a page saying to try again, because there is\n * nowhere honest to send them either.\n */\nexport function createOutreachShortLinkRoute(deps: ClickRouteDeps): PluginWebApiHandler {\n return async (request) => {\n if (request.method !== 'GET' && request.method !== 'HEAD') return methodNotAllowed()\n // The id is the last path segment: `/api/outreach/l/<id>`.\n const linkId = new URL(request.url).pathname.split('/').pop()\n if (!isOutreachLinkId(linkId)) return brokenLink()\n let target: OutreachClickTarget | null\n try {\n const snapshot = await deps.firestore().collection(OUTREACH_COLLECTIONS.links).doc(linkId).get()\n target = snapshot.exists ? readOutreachStoredLink(snapshot.data()) : null\n } catch (error) {\n console.error('[outreach] a short link could not be read', error)\n return outreachUnsubscribePage(\n {\n title: 'This link isn’t working right now',\n body: 'Something went wrong on our side. Try the link again in a minute.',\n },\n 503,\n )\n }\n if (!target) return brokenLink()\n return followOutreachClick(deps, request, target)\n }\n}\n"],"names":["OUTREACH_COLLECTIONS","recordOutreachSequenceTouch","isOutreachLinkId","readOutreachClickToken","readOutreachStoredLink","recordOutreachClick","outreachUnsubscribePage","REDIRECT_HEADERS","methodNotAllowed","Response","status","headers","Allow","brokenLink","title","body","followOutreachClick","deps","request","target","redirect","Location","url","outcome","method","userAgent","get","human","enrollment","atMs","now","error","console","createOutreachClickRoute","URL","searchParams","createOutreachShortLinkRoute","linkId","pathname","split","pop","snapshot","firestore","collection","links","doc","exists","data"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAGD,SAASA,oBAAoB,QAAQ,6BAAyB;AAC9D,SAASC,2BAA2B,QAAQ,uBAAmB;AAC/D,SACEC,gBAAgB,EAChBC,sBAAsB,EACtBC,sBAAsB,QAEjB,kBAAc;AACrB,SAASC,mBAAmB,QAAQ,oBAAgB;AAEpD,wEAAwE;AACxE,yEAAyE;AACzE,sDAAsD;AACtD,SAASC,uBAAuB,QAAQ,yBAAqB;AAE7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCC,GAED,MAAMC,mBAAmB;IACvB,iBAAiB;IACjB,mBAAmB;IACnB,gBAAgB;AAClB;AAIA,MAAMC,mBAAmB,IACvB,IAAIC,SAAS,sBAAsB;QAAEC,QAAQ;QAAKC,SAAS;YAAEC,OAAO;QAAY;IAAE;AAEpF,MAAMC,aAAa,IACjBP,wBACE;QACEQ,OAAO;QACPC,MAAM;IACR,GACA;AAGJ;;;CAGC,GACD,eAAeC,oBACbC,IAAoB,EACpBC,OAAgB,EAChBC,MAA2B;IAE3B;;;;;;GAMC,GACD,MAAMC,WAAW,IAAIX,SAAS,MAAM;QAClCC,QAAQ;QACRC,SAAS,aAAKJ;YAAkBc,UAAUF,OAAOG,GAAG;;IACtD;IACA,IAAI;QACF,MAAMC,UAAU,MAAMlB,oBAAoBY,MAAM;YAC9CE;YACAK,QAAQN,QAAQM,MAAM;YACtBC,WAAWP,QAAQP,OAAO,CAACe,GAAG,CAAC;QACjC;QACA;;;;;KAKC,GACD,IAAIH,QAAQI,KAAK,IAAIJ,QAAQK,UAAU,EAAE;YACvC,MAAM3B,4BAA4BgB,MAAM;gBAAEW,YAAYL,QAAQK,UAAU;gBAAEC,MAAMZ,KAAKa,GAAG;YAAG;QAC7F;IACF,EAAE,OAAOC,OAAO;QACd,oEAAoE;QACpE,kEAAkE;QAClEC,QAAQD,KAAK,CAAC,4CAA4CA;IAC5D;IACA,OAAOX;AACT;AAEA,wEAAwE,GACxE,OAAO,SAASa,yBAAyBhB,IAAoB;IAC3D,OAAO,OAAOC;QACZ,wEAAwE;QACxE,qEAAqE;QACrE,IAAIA,QAAQM,MAAM,KAAK,SAASN,QAAQM,MAAM,KAAK,QAAQ,OAAOhB;QAClE,MAAMW,SAAShB,uBAAuB,IAAI+B,IAAIhB,QAAQI,GAAG,EAAEa,YAAY,CAACT,GAAG,CAAC;QAC5E,IAAI,CAACP,QAAQ,OAAON;QACpB,OAAOG,oBAAoBC,MAAMC,SAASC;IAC5C;AACF;AAEA;;;;;;;;;;;CAWC,GACD,OAAO,SAASiB,6BAA6BnB,IAAoB;IAC/D,OAAO,OAAOC;QACZ,IAAIA,QAAQM,MAAM,KAAK,SAASN,QAAQM,MAAM,KAAK,QAAQ,OAAOhB;QAClE,2DAA2D;QAC3D,MAAM6B,SAAS,IAAIH,IAAIhB,QAAQI,GAAG,EAAEgB,QAAQ,CAACC,KAAK,CAAC,KAAKC,GAAG;QAC3D,IAAI,CAACtC,iBAAiBmC,SAAS,OAAOxB;QACtC,IAAIM;QACJ,IAAI;YACF,MAAMsB,WAAW,MAAMxB,KAAKyB,SAAS,GAAGC,UAAU,CAAC3C,qBAAqB4C,KAAK,EAAEC,GAAG,CAACR,QAAQX,GAAG;YAC9FP,SAASsB,SAASK,MAAM,GAAG1C,uBAAuBqC,SAASM,IAAI,MAAM;QACvE,EAAE,OAAOhB,OAAO;YACdC,QAAQD,KAAK,CAAC,6CAA6CA;YAC3D,OAAOzB,wBACL;gBACEQ,OAAO;gBACPC,MAAM;YACR,GACA;QAEJ;QACA,IAAI,CAACI,QAAQ,OAAON;QACpB,OAAOG,oBAAoBC,MAAMC,SAASC;IAC5C;AACF"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/runtime/click-route.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PluginWebApiHandler } from '@aglyn/aglyn/server'\nimport { OUTREACH_COLLECTIONS } from '../model/outreach.types'\nimport { recordOutreachSequenceTouch } from './campaign-credit'\nimport {\n isOutreachLinkId,\n readOutreachClickToken,\n readOutreachStoredLink,\n type OutreachClickTarget,\n} from './click-link'\nimport { recordOutreachClick } from './click-events'\nimport type { OutreachRuntimeDeps } from './runtime-deps'\n// The plain page both recipient-facing routes answer with: no theme, no\n// session, the reader's own colors. It lives beside the first route that\n// needed one rather than being copied for the second.\nimport { outreachUnsubscribePage } from './unsubscribe-route'\n\n/**\n * A LINK IN A TRACKED SEQUENCE EMAIL (AGL-3239, AGL-3297): the short\n * `GET /api/outreach/l/<id>` every send carries now, and the signed\n * `GET /api/outreach/click?t=…` the emails sent before it carry.\n *\n * ## The redirect happens first, and it happens for everyone\n *\n * The signed link carries its destination and needs no read; the short one\n * needs exactly one, of the document that names it. Then the 302 goes out. Recording is awaited after\n * the answer is built and never gates it — a Firestore that is slow or down\n * costs a number, not a visit.\n *\n * A link scanner is redirected exactly as a person is. Refusing one, or\n * answering it differently, is how a security gateway learns to report the\n * link as broken and the message as suspicious; it is counted apart instead\n * (`../engine/click-tracking.ts`).\n *\n * ## No session, and no release gate\n *\n * Registered as a recipient link, like the one-click unsubscribe. The\n * signature in the token is the whole of the authority, and a link that is\n * already in somebody's inbox has to keep working whether or not Outreach is\n * released to that organization today.\n *\n * ## A token that does not verify is not followed\n *\n * There is nowhere to send them: the destination WAS the thing that failed\n * the check. So it answers the same plain page the unsubscribe route\n * answers, rather than guessing — an endpoint that redirects to an unsigned\n * URL is an open redirect, whatever it is called.\n *\n * ## What the recipient's browser is told\n *\n * `no-store`, so a gateway's copy is never served to the person after it;\n * `no-referrer`, so the destination never learns the token; and `noindex`,\n * because a tracking link that reached a crawler should not be in an index.\n */\n\nconst REDIRECT_HEADERS = {\n 'Cache-Control': 'no-store, no-cache, must-revalidate',\n 'Referrer-Policy': 'no-referrer',\n 'X-Robots-Tag': 'noindex, nofollow',\n}\n\ntype ClickRouteDeps = Pick<OutreachRuntimeDeps, 'firestore' | 'now' | 'timeline' | 'campaignCredit'>\n\nconst methodNotAllowed = () =>\n new Response('Method not allowed', { status: 405, headers: { Allow: 'GET, HEAD' } })\n\nconst brokenLink = () =>\n outreachUnsubscribePage(\n {\n title: 'This link doesn’t work',\n body: 'This link isn’t valid, or it has been changed since it was sent. Try opening it from the original email.',\n },\n 400,\n )\n\n/**\n * The redirect, then the recording — the half both routes share once they\n * know where the link goes.\n */\nasync function followOutreachClick(\n deps: ClickRouteDeps,\n request: Request,\n target: OutreachClickTarget,\n): Promise<Response> {\n /*\n * Built by hand rather than with `Response.redirect`, which seals its\n * headers: the three above are the point of the answer as much as the\n * `Location` is. `target.url` is `URL.href` of an http(s) URL — both\n * readers return nothing else — so it is a header value that cannot carry\n * a newline into the response.\n */\n const redirect = new Response(null, {\n status: 302,\n headers: { ...REDIRECT_HEADERS, Location: target.url },\n })\n // A link in a test of a step (AGL-3325) is followed and never counted:\n // the member clicking their own test is not a recipient acting.\n if (target.test) return redirect\n try {\n const outcome = await recordOutreachClick(deps, {\n target,\n method: request.method,\n userAgent: request.headers.get('user-agent'),\n })\n /*\n * A person's click — never a scanner's — on a sequence in a campaign\n * is their last campaign touch on the site (AGL-3254), so the booking\n * or the form they go on to make is credited to the sequence's\n * campaign by the door that credits every other one.\n */\n if (outcome.human && outcome.enrollment) {\n await recordOutreachSequenceTouch(deps, { enrollment: outcome.enrollment, atMs: deps.now() })\n }\n } catch (error) {\n // The visit already has its answer. A click we failed to count is a\n // missing number; a redirect we failed to send is a broken email.\n console.error('[outreach] a click could not be recorded', error)\n }\n return redirect\n}\n\n/** The signed link, `?t=…` — every tracked email sent before AGL-3297. */\nexport function createOutreachClickRoute(deps: ClickRouteDeps): PluginWebApiHandler {\n return async (request) => {\n // `HEAD` is answered because a gateway often sends one before the `GET`\n // a person makes; it is redirected and counted as the machine it is.\n if (request.method !== 'GET' && request.method !== 'HEAD') return methodNotAllowed()\n const target = readOutreachClickToken(new URL(request.url).searchParams.get('t'))\n if (!target) return brokenLink()\n return followOutreachClick(deps, request, target)\n }\n}\n\n/**\n * THE SHORT LINK (AGL-3297): `GET /api/outreach/l/<id>`.\n *\n * One read of `outreachLinks/<id>` stands where the signature check stood,\n * and everything after it is the signed route's: the same redirect, the same\n * headers, the same recording, scanners redirected and counted apart.\n *\n * The destination comes only from that document. An id that is malformed or\n * names nothing is answered with the plain page, never followed; a read that\n * FAILS is answered with a page saying to try again, because there is\n * nowhere honest to send them either.\n */\nexport function createOutreachShortLinkRoute(deps: ClickRouteDeps): PluginWebApiHandler {\n return async (request) => {\n if (request.method !== 'GET' && request.method !== 'HEAD') return methodNotAllowed()\n // The id is the last path segment: `/api/outreach/l/<id>`.\n const linkId = new URL(request.url).pathname.split('/').pop()\n if (!isOutreachLinkId(linkId)) return brokenLink()\n let target: OutreachClickTarget | null\n try {\n const snapshot = await deps.firestore().collection(OUTREACH_COLLECTIONS.links).doc(linkId).get()\n target = snapshot.exists ? readOutreachStoredLink(snapshot.data()) : null\n } catch (error) {\n console.error('[outreach] a short link could not be read', error)\n return outreachUnsubscribePage(\n {\n title: 'This link isn’t working right now',\n body: 'Something went wrong on our side. Try the link again in a minute.',\n },\n 503,\n )\n }\n if (!target) return brokenLink()\n return followOutreachClick(deps, request, target)\n }\n}\n"],"names":["OUTREACH_COLLECTIONS","recordOutreachSequenceTouch","isOutreachLinkId","readOutreachClickToken","readOutreachStoredLink","recordOutreachClick","outreachUnsubscribePage","REDIRECT_HEADERS","methodNotAllowed","Response","status","headers","Allow","brokenLink","title","body","followOutreachClick","deps","request","target","redirect","Location","url","test","outcome","method","userAgent","get","human","enrollment","atMs","now","error","console","createOutreachClickRoute","URL","searchParams","createOutreachShortLinkRoute","linkId","pathname","split","pop","snapshot","firestore","collection","links","doc","exists","data"],"mappings":"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"}
|