@iann29/rastro 0.10.0 → 0.10.1
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/agent/manifest.json +1 -1
- package/dist/client/index.d.ts +6 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +9 -3
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/component.d.ts +1 -0
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/meta.d.ts +11 -5
- package/dist/component/meta.d.ts.map +1 -1
- package/dist/component/meta.js +81 -25
- package/dist/component/meta.js.map +1 -1
- package/dist/component/pipeline.d.ts +1 -0
- package/dist/component/pipeline.d.ts.map +1 -1
- package/dist/component/pipeline.js +18 -5
- package/dist/component/pipeline.js.map +1 -1
- package/dist/tracker/generated.d.ts +1 -1
- package/dist/tracker/generated.js +1 -1
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +3 -2
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/integrations/AccountPicker.d.ts.map +1 -1
- package/dist/ui/integrations/AccountPicker.js +6 -2
- package/dist/ui/integrations/AccountPicker.js.map +1 -1
- package/dist/ui/integrations/ClientCards.d.ts.map +1 -1
- package/dist/ui/integrations/ClientCards.js +6 -2
- package/dist/ui/integrations/ClientCards.js.map +1 -1
- package/dist/ui/integrations/ClientsTable.d.ts.map +1 -1
- package/dist/ui/integrations/ClientsTable.js +26 -10
- package/dist/ui/integrations/ClientsTable.js.map +1 -1
- package/dist/ui/integrations/Connect.d.ts +8 -2
- package/dist/ui/integrations/Connect.d.ts.map +1 -1
- package/dist/ui/integrations/Connect.js +18 -5
- package/dist/ui/integrations/Connect.js.map +1 -1
- package/dist/ui/integrations/Connection.d.ts.map +1 -1
- package/dist/ui/integrations/Connection.js +18 -4
- package/dist/ui/integrations/Connection.js.map +1 -1
- package/dist/ui/integrations/Dialogs.d.ts +4 -1
- package/dist/ui/integrations/Dialogs.d.ts.map +1 -1
- package/dist/ui/integrations/Dialogs.js +13 -4
- package/dist/ui/integrations/Dialogs.js.map +1 -1
- package/dist/ui/integrations/EventsFeed.d.ts.map +1 -1
- package/dist/ui/integrations/EventsFeed.js +5 -1
- package/dist/ui/integrations/EventsFeed.js.map +1 -1
- package/dist/ui/integrations/EventsSheet.d.ts +9 -3
- package/dist/ui/integrations/EventsSheet.d.ts.map +1 -1
- package/dist/ui/integrations/EventsSheet.js +20 -5
- package/dist/ui/integrations/EventsSheet.js.map +1 -1
- package/dist/ui/integrations/Icons.d.ts +2 -0
- package/dist/ui/integrations/Icons.d.ts.map +1 -1
- package/dist/ui/integrations/Icons.js +2 -0
- package/dist/ui/integrations/Icons.js.map +1 -1
- package/dist/ui/integrations/IntegrationsView.d.ts +10 -9
- package/dist/ui/integrations/IntegrationsView.d.ts.map +1 -1
- package/dist/ui/integrations/IntegrationsView.js +43 -15
- package/dist/ui/integrations/IntegrationsView.js.map +1 -1
- package/dist/ui/integrations/Overlay.js +1 -1
- package/dist/ui/integrations/Overlay.js.map +1 -1
- package/dist/ui/integrations/Partner.d.ts +14 -0
- package/dist/ui/integrations/Partner.d.ts.map +1 -0
- package/dist/ui/integrations/Partner.js +38 -0
- package/dist/ui/integrations/Partner.js.map +1 -0
- package/dist/ui/integrations/RowMenu.js +1 -1
- package/dist/ui/integrations/RowMenu.js.map +1 -1
- package/dist/ui/integrations/audience.d.ts +12 -0
- package/dist/ui/integrations/audience.d.ts.map +1 -0
- package/dist/ui/integrations/audience.js +12 -0
- package/dist/ui/integrations/audience.js.map +1 -0
- package/dist/ui/integrations/index.d.ts +2 -2
- package/dist/ui/integrations/index.d.ts.map +1 -1
- package/dist/ui/integrations/index.js +1 -1
- package/dist/ui/integrations/index.js.map +1 -1
- package/dist/ui/integrations/{overlay.d.ts → modal.d.ts} +1 -1
- package/dist/ui/integrations/modal.d.ts.map +1 -0
- package/dist/ui/integrations/{overlay.js → modal.js} +1 -1
- package/dist/ui/integrations/modal.js.map +1 -0
- package/dist/ui/integrations/model.d.ts +18 -3
- package/dist/ui/integrations/model.d.ts.map +1 -1
- package/dist/ui/integrations/model.js +35 -2
- package/dist/ui/integrations/model.js.map +1 -1
- package/dist/ui/integrations/types.d.ts +53 -3
- package/dist/ui/integrations/types.d.ts.map +1 -1
- package/dist/ui/sample.d.ts +62 -0
- package/dist/ui/sample.d.ts.map +1 -1
- package/dist/ui/sample.js +21 -0
- package/dist/ui/sample.js.map +1 -1
- package/dist/ui/ui.css +73 -5
- package/docs/meta-ads.md +53 -4
- package/docs/upgrading.md +21 -0
- package/package.json +1 -1
- package/src/component/_generated/component.ts +1 -0
- package/src/component/meta.ts +95 -31
- package/src/component/pipeline.ts +23 -5
- package/src/tracker/generated.ts +1 -1
- package/dist/ui/integrations/overlay.d.ts.map +0 -1
- package/dist/ui/integrations/overlay.js.map +0 -1
package/docs/meta-ads.md
CHANGED
|
@@ -348,8 +348,12 @@ app.use(rastro, {
|
|
|
348
348
|
|
|
349
349
|
- On the federated path a dashboard never takes or undoes what another owner on
|
|
350
350
|
the host made: linking an account another owner linked to the site, unlinking
|
|
351
|
-
another owner's link,
|
|
352
|
-
|
|
351
|
+
another owner's link, replacing or removing another owner's pixel, and saving
|
|
352
|
+
the event map of a site whose pixel another owner set are each `CONFLICT`.
|
|
353
|
+
Whoever set the pixel decides which columns reach it. The host's own `Rastro`
|
|
354
|
+
methods can do all of it, or refuse the same way when asked: `onlyOwn` on
|
|
355
|
+
`linkSiteAdAccount` and `setSitePixel`, an `ownerKey` on
|
|
356
|
+
`unlinkSiteAdAccount`, `removeSitePixel` and `setEventMap`.
|
|
353
357
|
- A lead's ad click id stays in the host's deployment, as before; no report
|
|
354
358
|
returns it.
|
|
355
359
|
- Each conversion carries an `eventId` derived from the lead and the event, so a
|
|
@@ -386,7 +390,10 @@ await rastro.linkSiteAdAccount(ctx, {
|
|
|
386
390
|
and `META_TOKEN_INVALID` when Meta refuses the token.
|
|
387
391
|
- `listMetaAdAccounts` (action) reads the accounts from Meta again and refreshes
|
|
388
392
|
the listing a link is checked against. `removeMetaConnection` forgets the
|
|
389
|
-
|
|
393
|
+
owner: its token, every account it fed, and every pixel it set, the ones with
|
|
394
|
+
their own token (plan B, `setBy`) included. An owner with only plan B pixels
|
|
395
|
+
and no connection loses those pixels all the same; `removed` says whether
|
|
396
|
+
anything went.
|
|
390
397
|
- `linkSiteAdAccount` (mutation) links a site to one of the listed accounts, at
|
|
391
398
|
most 20 per site and 500 per connection. **An ad account feeds one site**: its
|
|
392
399
|
spend is that client's, so linking it to a second site fails with `CONFLICT`
|
|
@@ -574,7 +581,10 @@ const map = await rastro.eventMap(ctx, { siteId, now: Date.now() });
|
|
|
574
581
|
- `setEventMap` replaces the whole map: each declared column maps to `Lead`,
|
|
575
582
|
`QualifiedLead`, `Schedule` or null. A key the pipeline does not declare is
|
|
576
583
|
`INVALID_ARGUMENT`. `sendLeadEvent` turns `Lead` on for every new lead and is
|
|
577
|
-
kept on the pixel too.
|
|
584
|
+
kept on the pixel too. With `ownerKey`, a site whose pixel another owner (or
|
|
585
|
+
the host, without an owner) set is a `CONFLICT`: the pixel's owner decides its
|
|
586
|
+
events. A site without a pixel takes any owner's map. The federated
|
|
587
|
+
`setMetaEventMap` always passes the dashboard connection's owner.
|
|
578
588
|
- `eventMap({ siteId, now? })` answers the columns with `key`, `name`, `color`,
|
|
579
589
|
`group`, `event` and `monthCount`: the distinct leads that entered the column
|
|
580
590
|
in the 30 UTC days up to `now` (a lead that leaves and comes back counts
|
|
@@ -744,3 +754,42 @@ hash of `br`), `custom_data.value` and `currency` on `Purchase`, and the pixel's
|
|
|
744
754
|
|
|
745
755
|
Under `RASTRO_META_FAKE=true` every send is accepted (a token containing
|
|
746
756
|
`invalid` is refused like an expired one) and nothing leaves the deployment.
|
|
757
|
+
|
|
758
|
+
## The Integrações screen (`IntegrationsView`)
|
|
759
|
+
|
|
760
|
+
`@iann29/rastro/ui` ships the screen every surface embeds: the Meta login, each
|
|
761
|
+
site's ad account and pixel, and the events sheet that maps board columns to
|
|
762
|
+
Meta events. It is pure, like the other views: the host passes `connection`
|
|
763
|
+
(`metaConnection`), `sites`, `status` (`meta.status` with `ownerKey`, so each
|
|
764
|
+
link and the pixel carry `own`), `eventMaps` and `conversions`, and turns every
|
|
765
|
+
callback (`onConnect`, `onLinkAccount`, `onSetPixel`, `onSaveEventMap`, …) into
|
|
766
|
+
its own call. `@iann29/rastro/ui/sample` has every state in
|
|
767
|
+
`sampleIntegrations`.
|
|
768
|
+
|
|
769
|
+
It speaks to two readers through `audience`:
|
|
770
|
+
|
|
771
|
+
- `"agency"` (the default): one Meta login for many clients, a table of clients
|
|
772
|
+
with a filter, the feed across every client's pixel. The Rastro dashboard and
|
|
773
|
+
uze.ai's partner portal render it.
|
|
774
|
+
- `"business"`: one company connecting its own Meta ("Conexão Meta"). It shows
|
|
775
|
+
the first site only, as a one-row "Sua conta de anúncio" (no filter, no
|
|
776
|
+
currency column), and its copy names the company and never an agency or
|
|
777
|
+
clients. `productName` says who reads the spend and warns the pixel ("o uze lê
|
|
778
|
+
o gasto"; "Rastro" by default). With no site yet it shows the login alone, for
|
|
779
|
+
a host that makes the company's site on connecting.
|
|
780
|
+
|
|
781
|
+
Whatever another owner of the site linked (`own: false`) shows a lock, "Ligado
|
|
782
|
+
pelo <otherOwnerLabel>", and its row offers no unlink or pixel swap; its events
|
|
783
|
+
sheet reads "Ver eventos" and is read only, since the pixel's owner decides them
|
|
784
|
+
(`setMetaEventMap` refuses with `CONFLICT`). In business mode a `partner`
|
|
785
|
+
(`{ name, allowed, onAllowedChange?, busy? }`) draws the partner's card on top,
|
|
786
|
+
"O parceiro Gio cuida dos seus anúncios na Meta", with the company's checkbox
|
|
787
|
+
"Deixar Gio ver as campanhas" when `onAllowedChange` is given, and, unless the
|
|
788
|
+
host passes its own `otherOwnerLabel`, the locks read "Ligado pelo parceiro
|
|
789
|
+
Gio".
|
|
790
|
+
|
|
791
|
+
In agency mode, `onCopyEventMap(siteIds, map)` adds "Usar o mesmo mapa nos
|
|
792
|
+
outros clientes" to the events sheet. Saving with it checked calls
|
|
793
|
+
`onSaveEventMap` for the client, then `onCopyEventMap` with the other clients
|
|
794
|
+
whose board declares the same columns and whose pixel is not another owner's
|
|
795
|
+
(`eventMapCopyTargets` computes them).
|
package/docs/upgrading.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Upgrading Amage Rastro
|
|
2
2
|
|
|
3
|
+
## The business screen and the owner checks (0.10.1)
|
|
4
|
+
|
|
5
|
+
No schema change, env var or re-export. Install `@iann29/rastro@0.10.1` and
|
|
6
|
+
deploy the host. Two behaviors change on purpose:
|
|
7
|
+
|
|
8
|
+
- `setMetaEventMap` (federated) refuses with `CONFLICT` on a site whose pixel
|
|
9
|
+
another owner set: the dashboard no longer rewrites the map of a pixel the
|
|
10
|
+
host's own owner linked. The host's `setEventMap` is unchanged unless it
|
|
11
|
+
passes the new optional `ownerKey`.
|
|
12
|
+
- `removeMetaConnection` (and `removeFederatedMetaConnection`, so revoking a
|
|
13
|
+
dashboard connection) now also removes the pixels the owner set with their own
|
|
14
|
+
token (plan B), not only those of its connection. A host that sets plan B
|
|
15
|
+
pixels for an owner should pass that owner as `setBy` in `setSitePixel`, so
|
|
16
|
+
removing the owner reaches them.
|
|
17
|
+
|
|
18
|
+
Hosts embedding `IntegrationsView` can render it for one company with
|
|
19
|
+
`audience="business"` (and `partner`, `productName`), and offer "Usar o mesmo
|
|
20
|
+
mapa nos outros clientes" with `onCopyEventMap`; see
|
|
21
|
+
[the Integrações screen](meta-ads.md#the-integrações-screen-integrationsview).
|
|
22
|
+
Nothing changes for a host that passes neither.
|
|
23
|
+
|
|
3
24
|
## Meta ads, conversions and the Integrações screen (0.10.0)
|
|
4
25
|
|
|
5
26
|
Install `@iann29/rastro@0.10.0` and deploy the consuming host. The release is
|
package/package.json
CHANGED
|
@@ -762,6 +762,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
762
762
|
"internal",
|
|
763
763
|
{
|
|
764
764
|
columns: Record<string, "Lead" | "QualifiedLead" | "Schedule" | null>;
|
|
765
|
+
ownerKey?: string;
|
|
765
766
|
sendLeadEvent: boolean;
|
|
766
767
|
siteId: string;
|
|
767
768
|
},
|
package/src/component/meta.ts
CHANGED
|
@@ -79,7 +79,8 @@ const TOKEN_EXCHANGE_FAILED =
|
|
|
79
79
|
/** How long before a token dies the screens ask for a new login. */
|
|
80
80
|
const EXPIRY_WARNING_MS = 7 * 86_400_000;
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
/** An owner key as stored: the host's opaque id, checked. */
|
|
83
|
+
export function ownerKeyOf(value: string): string {
|
|
83
84
|
try {
|
|
84
85
|
return sanitizeOpaqueId(value, "ownerKey");
|
|
85
86
|
} catch (error) {
|
|
@@ -699,59 +700,122 @@ async function unlinkRow(
|
|
|
699
700
|
}
|
|
700
701
|
|
|
701
702
|
/**
|
|
702
|
-
* Forgets the owner
|
|
703
|
-
*
|
|
704
|
-
* batches.
|
|
703
|
+
* Forgets the owner: its token, every account it fed, and every pixel it set,
|
|
704
|
+
* the ones with their own token (plan B) included. The token goes at once (no
|
|
705
|
+
* run can load it after this), its links and pixels in scheduled batches. An
|
|
706
|
+
* owner with plan B pixels and no connection still loses those pixels.
|
|
705
707
|
*/
|
|
706
708
|
export const removeConnection = mutation({
|
|
707
709
|
args: { ownerKey: v.string() },
|
|
708
710
|
returns: v.object({ removed: v.boolean() }),
|
|
709
711
|
handler: async (ctx, args) => {
|
|
712
|
+
const ownerKey = ownerKeyOf(args.ownerKey);
|
|
710
713
|
const connection = await ctx.db
|
|
711
714
|
.query("metaConnections")
|
|
712
|
-
.withIndex("by_ownerKey", (range) =>
|
|
713
|
-
range.eq("ownerKey", ownerKeyOf(args.ownerKey)),
|
|
714
|
-
)
|
|
715
|
+
.withIndex("by_ownerKey", (range) => range.eq("ownerKey", ownerKey))
|
|
715
716
|
.unique();
|
|
716
|
-
if (
|
|
717
|
-
await ctx
|
|
718
|
-
|
|
719
|
-
|
|
717
|
+
if (connection) await ctx.db.delete("metaConnections", connection._id);
|
|
718
|
+
const removedPixels = await removeOwnerBatch(ctx, {
|
|
719
|
+
connectionId: connection?._id,
|
|
720
|
+
ownerKey,
|
|
721
|
+
before: Date.now(),
|
|
722
|
+
});
|
|
723
|
+
return { removed: connection !== null || removedPixels > 0 };
|
|
720
724
|
},
|
|
721
725
|
});
|
|
722
726
|
|
|
723
|
-
|
|
727
|
+
/**
|
|
728
|
+
* One batch of a removed owner: its connection's links and pixels, and the
|
|
729
|
+
* pixels it set (`setBy`) last changed by `before`, so a pixel the same owner
|
|
730
|
+
* sets again after the removal stays. The owner's pixels are walked in
|
|
731
|
+
* creation order from `after`, so every batch moves on even past the ones it
|
|
732
|
+
* keeps. Schedules the next batch while any is left and answers how many
|
|
733
|
+
* pixels this one removed.
|
|
734
|
+
*/
|
|
735
|
+
async function removeOwnerBatch(
|
|
724
736
|
ctx: MutationCtx,
|
|
725
|
-
|
|
737
|
+
owner: {
|
|
738
|
+
connectionId?: Id<"metaConnections"> | undefined;
|
|
739
|
+
ownerKey?: string | undefined;
|
|
740
|
+
before?: number | undefined;
|
|
741
|
+
after?: number | undefined;
|
|
742
|
+
},
|
|
726
743
|
) {
|
|
727
|
-
const
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
744
|
+
const { connectionId, ownerKey, before, after } = owner;
|
|
745
|
+
const links = connectionId
|
|
746
|
+
? await ctx.db
|
|
747
|
+
.query("siteAdAccounts")
|
|
748
|
+
.withIndex("by_connectionId", (range) =>
|
|
749
|
+
range.eq("connectionId", connectionId),
|
|
750
|
+
)
|
|
751
|
+
.take(UNLINK_BATCH)
|
|
752
|
+
: [];
|
|
733
753
|
for (const link of links) await unlinkRow(ctx, link);
|
|
734
754
|
// A pixel picked through the connection sent with its token: it goes too.
|
|
735
|
-
const
|
|
736
|
-
.
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
755
|
+
const connectionPixels = connectionId
|
|
756
|
+
? await ctx.db
|
|
757
|
+
.query("sitePixels")
|
|
758
|
+
.withIndex("by_connectionId", (range) =>
|
|
759
|
+
range.eq("connectionId", connectionId),
|
|
760
|
+
)
|
|
761
|
+
.take(UNLINK_BATCH)
|
|
762
|
+
: [];
|
|
763
|
+
for (const pixel of connectionPixels) {
|
|
764
|
+
await ctx.db.delete("sitePixels", pixel._id);
|
|
765
|
+
}
|
|
766
|
+
// So does every pixel the owner set with its own token (plan B), unless it
|
|
767
|
+
// was set again after the removal (`updatedAt` past `before`).
|
|
768
|
+
const ownerPixels =
|
|
769
|
+
ownerKey === undefined || before === undefined
|
|
770
|
+
? []
|
|
771
|
+
: await ctx.db
|
|
772
|
+
.query("sitePixels")
|
|
773
|
+
.withIndex("by_setBy", (range) =>
|
|
774
|
+
after === undefined
|
|
775
|
+
? range.eq("setBy", ownerKey)
|
|
776
|
+
: range.eq("setBy", ownerKey).gt("_creationTime", after),
|
|
777
|
+
)
|
|
778
|
+
.take(UNLINK_BATCH);
|
|
779
|
+
// `<=`: a pixel set in the removal's own millisecond, before it, must go;
|
|
780
|
+
// one set again in that same millisecond after it goes too (the rarer case).
|
|
781
|
+
const stale = ownerPixels.filter((pixel) => pixel.updatedAt <= before!);
|
|
782
|
+
for (const pixel of stale) await ctx.db.delete("sitePixels", pixel._id);
|
|
783
|
+
const cursor = ownerPixels.at(-1)?._creationTime;
|
|
784
|
+
if (
|
|
785
|
+
links.length === UNLINK_BATCH ||
|
|
786
|
+
connectionPixels.length === UNLINK_BATCH ||
|
|
787
|
+
ownerPixels.length === UNLINK_BATCH
|
|
788
|
+
) {
|
|
743
789
|
await ctx.scheduler.runAfter(0, internal.meta.unlinkConnection, {
|
|
744
|
-
connectionId,
|
|
790
|
+
...(connectionId ? { connectionId } : {}),
|
|
791
|
+
...(ownerKey === undefined || before === undefined
|
|
792
|
+
? {}
|
|
793
|
+
: { ownerKey, before }),
|
|
794
|
+
// A full owner batch goes on from its last row; otherwise from where
|
|
795
|
+
// this one started.
|
|
796
|
+
...(ownerPixels.length === UNLINK_BATCH && cursor !== undefined
|
|
797
|
+
? { after: cursor }
|
|
798
|
+
: after === undefined
|
|
799
|
+
? {}
|
|
800
|
+
: { after }),
|
|
745
801
|
});
|
|
746
802
|
}
|
|
803
|
+
return connectionPixels.length + stale.length;
|
|
747
804
|
}
|
|
748
805
|
|
|
749
|
-
/** The next batch of a removed
|
|
806
|
+
/** The next batch of a removed owner's links and pixels. */
|
|
750
807
|
export const unlinkConnection = internalMutation({
|
|
751
|
-
args: {
|
|
808
|
+
args: {
|
|
809
|
+
connectionId: v.optional(v.id("metaConnections")),
|
|
810
|
+
ownerKey: v.optional(v.string()),
|
|
811
|
+
/** The removal's time: the owner's pixels as they were then. */
|
|
812
|
+
before: v.optional(v.number()),
|
|
813
|
+
/** The `_creationTime` of the last owner pixel already walked. */
|
|
814
|
+
after: v.optional(v.number()),
|
|
815
|
+
},
|
|
752
816
|
returns: v.null(),
|
|
753
817
|
handler: async (ctx, args) => {
|
|
754
|
-
await
|
|
818
|
+
await removeOwnerBatch(ctx, args);
|
|
755
819
|
return null;
|
|
756
820
|
},
|
|
757
821
|
});
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type QueryCtx,
|
|
8
8
|
} from "./_generated/server.js";
|
|
9
9
|
import { fail } from "./errors.js";
|
|
10
|
+
import { ownerKeyOf } from "./meta.js";
|
|
10
11
|
import { enqueueColumnEvent, type MetaUserData } from "./metaCapi.js";
|
|
11
12
|
import { cleanString } from "./sanitize.js";
|
|
12
13
|
import {
|
|
@@ -139,10 +140,30 @@ export const setEventMap = mutation({
|
|
|
139
140
|
siteId: v.id("sites"),
|
|
140
141
|
columns: v.record(v.string(), v.union(metaColumnEventValidator, v.null())),
|
|
141
142
|
sendLeadEvent: v.boolean(),
|
|
143
|
+
/**
|
|
144
|
+
* The owner saving the map. The map feeds the site's pixel, so whoever
|
|
145
|
+
* set the pixel decides it: with `ownerKey`, a pixel another owner (or
|
|
146
|
+
* the host) set is a `CONFLICT`. A site without a pixel takes anyone's.
|
|
147
|
+
*/
|
|
148
|
+
ownerKey: v.optional(v.string()),
|
|
142
149
|
},
|
|
143
150
|
returns: v.null(),
|
|
144
151
|
handler: async (ctx, args) => {
|
|
145
152
|
await requireSite(ctx, args.siteId);
|
|
153
|
+
const pixel = await ctx.db
|
|
154
|
+
.query("sitePixels")
|
|
155
|
+
.withIndex("by_siteId", (range) => range.eq("siteId", args.siteId))
|
|
156
|
+
.unique();
|
|
157
|
+
if (
|
|
158
|
+
args.ownerKey !== undefined &&
|
|
159
|
+
pixel &&
|
|
160
|
+
pixel.setBy !== ownerKeyOf(args.ownerKey)
|
|
161
|
+
) {
|
|
162
|
+
fail(
|
|
163
|
+
"CONFLICT",
|
|
164
|
+
"the site's pixel was set by another owner on this host; change its events there",
|
|
165
|
+
);
|
|
166
|
+
}
|
|
146
167
|
const existing = await pipelineOf(ctx, args.siteId);
|
|
147
168
|
const declared = new Set((existing?.columns ?? []).map((row) => row.key));
|
|
148
169
|
for (const key of Object.keys(args.columns)) {
|
|
@@ -174,14 +195,11 @@ export const setEventMap = mutation({
|
|
|
174
195
|
updatedAt: now,
|
|
175
196
|
});
|
|
176
197
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
.withIndex("by_siteId", (range) => range.eq("siteId", args.siteId))
|
|
180
|
-
.unique();
|
|
198
|
+
// `updatedAt` stays: it says when the pixel itself was set, which is what
|
|
199
|
+
// removing its owner compares against.
|
|
181
200
|
if (pixel && pixel.sendLeadEvent !== args.sendLeadEvent) {
|
|
182
201
|
await ctx.db.patch("sitePixels", pixel._id, {
|
|
183
202
|
sendLeadEvent: args.sendLeadEvent,
|
|
184
|
-
updatedAt: now,
|
|
185
203
|
});
|
|
186
204
|
}
|
|
187
205
|
return null;
|
package/src/tracker/generated.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated by scripts/build-tracker.mjs. Do not edit.
|
|
2
|
-
export const RASTRO_VERSION = "0.10.
|
|
2
|
+
export const RASTRO_VERSION = "0.10.1";
|
|
3
3
|
export const TRACKER_SOURCE = "(()=>{const t=document.currentScript,e=t?.dataset.site,i=t?.dataset.endpoint||t?.src&&new URL(\"events\",t.src).href;if(!e||!i)return;const n=()=>crypto.randomUUID?.()||Math.random()+\"\",r=new URLSearchParams(location.search),s=r.get(\"ref\")?.slice(0,64);let o;for(const t of[\"source\",\"medium\",\"campaign\",\"term\",\"content\"]){const e=r.get(\"utm_\"+t)?.slice(0,64);e&&((o??={})[\"utm_\"+t]=e)}const a=\"gclid gbraid wbraid msclkid ttclid twclid li_fat_id ScCid rdt_cid dclid epik srsltid mc_cid mc_eid _hsenc igsh igshid fbclid\".split(\" \").filter(t=>r.has(t)).slice(0,8);let c,d,l,h,f=t.dataset.visitor||\"\",p=Boolean(f);try{f||=localStorage._rv||=n()}catch{}try{d=sessionStorage,l=d._r,c=d._ri==f&&d._rt,h=s||d._a,h&&(d._a=h),o||=d._u&&JSON.parse(d._u),o&&(d._u=JSON.stringify(o))}catch{h=s}let v,g=0;const u=[];let m=document.referrer.split(\"/\",3).join(\"/\")||void 0;const _=t=>{const n=u.splice(0,50);if(!n.length)return;const r=()=>new Blob([JSON.stringify({siteId:e,events:n,sentAt:Date.now()})]);let s=r();for(;s.size>64e3&&n.length>1;)u.unshift(n.pop()),s=r();t&&navigator.sendBeacon(i,s)||fetch(i,{method:\"POST\",body:s,keepalive:1}).catch(()=>{}),u.length&&_(t)},y=(t,e,i)=>{const r=Date.now(),s=r-c;c=r,s<18e5||(l=n(),\"pageview\"!=t&&(S=\"\",w()));try{d._r=l,d._ri=f,d._rt=r}catch{}u.push({eventId:`${l}.${r.toString(36)}.${g}`,sessionId:l,visitorId:f||l,type:t,name:e,path:location.pathname.slice(0,256),referrer:m,timestamp:r,sequence:g++,affiliateSlug:h,...i}),clearTimeout(v),v=setTimeout(_,1200)};let S;const w=()=>{const t=location.pathname;t!=S&&(S=t,y(\"pageview\",void 0,{properties:o,clid:a.length?a:void 0}))};window.rastro=(e,i,r)=>{if(\"context\"==e)return{sessionId:l,visitorId:f||l,identified:p};if(\"identify\"==e||\"reset\"==e){if(\"identify\"==e&&(!i||i.length>128||!/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/.test(i)))return;if(\"identify\"==e&&i==f)return;_();const r=\"reset\"==e||p;if(p=\"identify\"==e,f=\"identify\"==e?i:n(),t.dataset.visitor=\"identify\"==e?f:\"\",r){h=void 0,o=void 0,m=void 0,a.length=0;try{\"reset\"==e&&(localStorage._rv=f),d._a=\"\",d._u=\"\"}catch{}}c=0,g=0,S=\"\",w()}\"event\"==e&&y(\"custom\",`${i||\"event\"}`.slice(0,80),r&&JSON.stringify(r).length>2e4?void 0:{properties:r})},document.addEventListener(\"click\",t=>{const e=t.target?.closest(\"a,button,[data-rastro-event]\");if(!e)return;const i=e.dataset.rastroEvent,n=e.href,r=n&&new URL(n),s=/^https?:$/.test(r?.protocol),o=s&&r.origin!=location.origin;y(i?\"custom\":o?\"outbound\":\"click\",i,{target:(e.dataset.rastroLabel||e.innerText||e.tagName).slice(0,64),href:s?((o?r.origin:\"\")+r.pathname).slice(0,256):void 0})},1);for(const t of[\"pushState\",\"replaceState\"]){const e=history[t];history[t]=(...t)=>{e.apply(history,t),w()}}addEventListener(\"popstate\",w);let b=0;const I=()=>{y(\"heartbeat\"),_()},L=()=>{b&&(clearInterval(b),b=0,y(\"leave\"),_(1))},E=()=>{document.hidden?L():b||(I(),b=setInterval(I,2e4))};addEventListener(\"pagehide\",L),addEventListener(\"pageshow\",E),document.addEventListener(\"visibilitychange\",E),b=setInterval(I,2e4),w(),E()})();";
|
|
4
4
|
export const TRACKER_GZIP_BASE64 = "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";
|
|
5
5
|
export const TRACKER_RAW_BYTES = 2999;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../../src/ui/integrations/overlay.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAKpE;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,GAAG,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EAClC,OAAO,EAAE,MAAM,IAAI,EACnB,OAAO,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,QA2CxC;AAED,yEAAyE;AACzE,wBAAgB,UAAU,CACxB,IAAI,EAAE,OAAO,EACb,IAAI,EAAE,aAAa,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC,EAClD,OAAO,EAAE,MAAM,IAAI,QAsBpB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"overlay.js","sourceRoot":"","sources":["../../../src/ui/integrations/overlay.ts"],"names":[],"mappings":"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"}
|