@koda-sl/baker-cli 0.313.0-dev.ca75dc08a → 0.313.0
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/README.md +3 -1
- package/dist/{chunk-PY7R67NE.js → chunk-WONWYE7B.js} +4 -4
- package/dist/chunk-WONWYE7B.js.map +1 -0
- package/dist/cli.js +93 -252
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-PY7R67NE.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -67,7 +67,7 @@ import {
|
|
|
67
67
|
validateCanvasDeep,
|
|
68
68
|
withoutCastName,
|
|
69
69
|
ytDlpBlockSignal
|
|
70
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-WONWYE7B.js";
|
|
71
71
|
import {
|
|
72
72
|
csvOrJson,
|
|
73
73
|
daysAgoIso,
|
|
@@ -222,6 +222,15 @@ function compactImage(image) {
|
|
|
222
222
|
imageUrl: String(image.imageUrl ?? ""),
|
|
223
223
|
description: String(image.description ?? ""),
|
|
224
224
|
tags: Array.isArray(image.tags) ? image.tags : [],
|
|
225
|
+
// Two numbers that are not about choosing the image but about placing it.
|
|
226
|
+
// A remote <BakerImage> with no width/height makes Astro DOWNLOAD the file
|
|
227
|
+
// at build time just to measure it — one request per image per build, on a
|
|
228
|
+
// build that is otherwise offline, and a single dropped connection there
|
|
229
|
+
// fails a client's whole verify. They are cheap enough to always carry, and
|
|
230
|
+
// burying them behind --full meant the agent placing the image never had
|
|
231
|
+
// them.
|
|
232
|
+
width: typeof image.width === "number" ? image.width : null,
|
|
233
|
+
height: typeof image.height === "number" ? image.height : null,
|
|
225
234
|
// The facts that decide a pick, so choosing does not need a second call:
|
|
226
235
|
// whose brand it is, who is in it, and what text is burned in.
|
|
227
236
|
subject: typeof analysis.subject === "string" ? analysis.subject : null,
|
|
@@ -235,8 +244,6 @@ function fullImage(image) {
|
|
|
235
244
|
const compact3 = compactImage(image);
|
|
236
245
|
return {
|
|
237
246
|
...compact3,
|
|
238
|
-
width: image.width ?? null,
|
|
239
|
-
height: image.height ?? null,
|
|
240
247
|
aspectRatio: formatAspectRatio(image.aspectRatio),
|
|
241
248
|
dominantColor: image.dominantColor ?? null,
|
|
242
249
|
imagePalette: Array.isArray(image.imagePalette) ? image.imagePalette : [],
|
|
@@ -10749,14 +10756,14 @@ function collectionLine(collection) {
|
|
|
10749
10756
|
}
|
|
10750
10757
|
function accountsInTree(nodes) {
|
|
10751
10758
|
const accounts = /* @__PURE__ */ new Set();
|
|
10752
|
-
const
|
|
10759
|
+
const walk2 = (node) => {
|
|
10753
10760
|
accounts.add(node.customerId);
|
|
10754
10761
|
for (const child of node.children) {
|
|
10755
|
-
|
|
10762
|
+
walk2(child);
|
|
10756
10763
|
}
|
|
10757
10764
|
};
|
|
10758
10765
|
for (const node of nodes) {
|
|
10759
|
-
|
|
10766
|
+
walk2(node);
|
|
10760
10767
|
}
|
|
10761
10768
|
return accounts;
|
|
10762
10769
|
}
|
|
@@ -21383,14 +21390,14 @@ function collectionLine2(collection) {
|
|
|
21383
21390
|
}
|
|
21384
21391
|
function accountsInTree2(nodes) {
|
|
21385
21392
|
const accounts = /* @__PURE__ */ new Set();
|
|
21386
|
-
const
|
|
21393
|
+
const walk2 = (node) => {
|
|
21387
21394
|
accounts.add(node.accountId);
|
|
21388
21395
|
for (const child of node.children) {
|
|
21389
|
-
|
|
21396
|
+
walk2(child);
|
|
21390
21397
|
}
|
|
21391
21398
|
};
|
|
21392
21399
|
for (const node of nodes) {
|
|
21393
|
-
|
|
21400
|
+
walk2(node);
|
|
21394
21401
|
}
|
|
21395
21402
|
return accounts;
|
|
21396
21403
|
}
|
|
@@ -26995,14 +27002,14 @@ function collectionLine3(collection) {
|
|
|
26995
27002
|
}
|
|
26996
27003
|
function accountsInTree3(nodes) {
|
|
26997
27004
|
const accounts = /* @__PURE__ */ new Set();
|
|
26998
|
-
const
|
|
27005
|
+
const walk2 = (node) => {
|
|
26999
27006
|
accounts.add(node.accountId);
|
|
27000
27007
|
for (const child of node.children) {
|
|
27001
|
-
|
|
27008
|
+
walk2(child);
|
|
27002
27009
|
}
|
|
27003
27010
|
};
|
|
27004
27011
|
for (const node of nodes) {
|
|
27005
|
-
|
|
27012
|
+
walk2(node);
|
|
27006
27013
|
}
|
|
27007
27014
|
return accounts;
|
|
27008
27015
|
}
|
|
@@ -28820,55 +28827,6 @@ Full guides: __tooling__/docs/tools/baker/ads-<platform>.md (google|meta|linkedi
|
|
|
28820
28827
|
// src/commands/analytics/index.ts
|
|
28821
28828
|
import { readFileSync as readFileSync12 } from "fs";
|
|
28822
28829
|
|
|
28823
|
-
// ../api/src/analytics/flowEventKeys.ts
|
|
28824
|
-
function flowNodes(node, acc = []) {
|
|
28825
|
-
if (!node || typeof node !== "object") return acc;
|
|
28826
|
-
const record = node;
|
|
28827
|
-
acc.push(record);
|
|
28828
|
-
if (Array.isArray(record.children)) for (const child of record.children) flowNodes(child, acc);
|
|
28829
|
-
return acc;
|
|
28830
|
-
}
|
|
28831
|
-
function flowEventKeys(tree, slug) {
|
|
28832
|
-
const primary = [];
|
|
28833
|
-
const secondary = [];
|
|
28834
|
-
const nodes = flowNodes(tree).filter((node) => typeof node.id === "string" && node.id !== "");
|
|
28835
|
-
if (nodes.length === 0) return [];
|
|
28836
|
-
for (const node of nodes) {
|
|
28837
|
-
const id = node.id;
|
|
28838
|
-
const triggers = flowNodeTriggers(node.nodeData);
|
|
28839
|
-
for (const triggerId of triggers.available) {
|
|
28840
|
-
const candidate = {
|
|
28841
|
-
eventKey: formEventKey(slug, id, triggerId),
|
|
28842
|
-
nodeId: id,
|
|
28843
|
-
nodeName: typeof node.name === "string" ? node.name : id,
|
|
28844
|
-
nodeType: triggers.type,
|
|
28845
|
-
triggerId,
|
|
28846
|
-
isCompletion: triggers.completion === triggerId
|
|
28847
|
-
};
|
|
28848
|
-
(candidate.isCompletion ? primary : secondary).push(candidate);
|
|
28849
|
-
}
|
|
28850
|
-
}
|
|
28851
|
-
return [
|
|
28852
|
-
...primary,
|
|
28853
|
-
...secondary,
|
|
28854
|
-
{
|
|
28855
|
-
eventKey: `submit:${slug}`,
|
|
28856
|
-
nodeId: null,
|
|
28857
|
-
nodeName: "the Form ran out of steps",
|
|
28858
|
-
nodeType: null,
|
|
28859
|
-
triggerId: null,
|
|
28860
|
-
// A Form reaching its last node is as primary as an outcome gets for a
|
|
28861
|
-
// Form with no widget in it — but it never fires on one that ends in a
|
|
28862
|
-
// booking widget or a redirect, so it does not lead the list.
|
|
28863
|
-
isCompletion: false
|
|
28864
|
-
}
|
|
28865
|
-
];
|
|
28866
|
-
}
|
|
28867
|
-
function suggestedEnding(endings) {
|
|
28868
|
-
const deepestFirst = [...endings].reverse();
|
|
28869
|
-
return deepestFirst.find((row) => row.isCompletion && row.nodeType !== "text") ?? deepestFirst.find((row) => row.isCompletion) ?? endings[endings.length - 1] ?? null;
|
|
28870
|
-
}
|
|
28871
|
-
|
|
28872
28830
|
// ../api/src/event-ingest/arrivals.ts
|
|
28873
28831
|
import { z as z33 } from "zod";
|
|
28874
28832
|
|
|
@@ -30048,14 +30006,56 @@ function mergeStoredWithCounts(stored, reported) {
|
|
|
30048
30006
|
}
|
|
30049
30007
|
|
|
30050
30008
|
// src/commands/analytics/flowConversionKeys.ts
|
|
30009
|
+
function walk(node, acc = []) {
|
|
30010
|
+
if (!node || typeof node !== "object") return acc;
|
|
30011
|
+
const record = node;
|
|
30012
|
+
acc.push(record);
|
|
30013
|
+
if (Array.isArray(record.children)) for (const child of record.children) walk(child, acc);
|
|
30014
|
+
return acc;
|
|
30015
|
+
}
|
|
30016
|
+
function flowConversionCandidates(tree, slug) {
|
|
30017
|
+
const primary = [];
|
|
30018
|
+
const secondary = [];
|
|
30019
|
+
for (const node of walk(tree)) {
|
|
30020
|
+
const id = typeof node.id === "string" ? node.id : null;
|
|
30021
|
+
if (!id) continue;
|
|
30022
|
+
const triggers = flowNodeTriggers(node.nodeData);
|
|
30023
|
+
for (const triggerId of triggers.available) {
|
|
30024
|
+
const candidate = {
|
|
30025
|
+
eventKey: formEventKey(slug, id, triggerId),
|
|
30026
|
+
nodeId: id,
|
|
30027
|
+
nodeName: typeof node.name === "string" ? node.name : id,
|
|
30028
|
+
nodeType: triggers.type,
|
|
30029
|
+
triggerId,
|
|
30030
|
+
isCompletion: triggers.completion === triggerId
|
|
30031
|
+
};
|
|
30032
|
+
(candidate.isCompletion ? primary : secondary).push(candidate);
|
|
30033
|
+
}
|
|
30034
|
+
}
|
|
30035
|
+
return [
|
|
30036
|
+
...primary,
|
|
30037
|
+
...secondary,
|
|
30038
|
+
{
|
|
30039
|
+
eventKey: `submit:${slug}`,
|
|
30040
|
+
nodeId: null,
|
|
30041
|
+
nodeName: "the Form ran out of steps",
|
|
30042
|
+
nodeType: null,
|
|
30043
|
+
triggerId: null,
|
|
30044
|
+
// A Form reaching its last node is as primary as an outcome gets for a
|
|
30045
|
+
// Form with no widget in it — but it never fires on one that ends in a
|
|
30046
|
+
// booking widget or a redirect, so it does not lead the list.
|
|
30047
|
+
isCompletion: false
|
|
30048
|
+
}
|
|
30049
|
+
];
|
|
30050
|
+
}
|
|
30051
30051
|
function formKeyRefusal(eventKey, tree, slug) {
|
|
30052
30052
|
const parsed = parseEventKey(eventKey);
|
|
30053
30053
|
if (!parsed || parsed.family !== "form" || parsed.flowSlug !== slug) return null;
|
|
30054
|
-
const node =
|
|
30054
|
+
const node = walk(tree).find((candidate) => candidate.id === parsed.nodeId);
|
|
30055
30055
|
if (!node) {
|
|
30056
30056
|
return {
|
|
30057
30057
|
message: `"${slug}" has no step with id "${parsed.nodeId}", so this key would be stored and count zero forever.`,
|
|
30058
|
-
fix: `Steps in this Form: ${
|
|
30058
|
+
fix: `Steps in this Form: ${walk(tree).map((n) => `${String(n.id)} (${String(n.name ?? "unnamed")})`).join(", ")}. \`baker analytics conversions --flow ${slug}\` prints each one already spelled as an event key.`
|
|
30059
30059
|
};
|
|
30060
30060
|
}
|
|
30061
30061
|
const triggers = flowNodeTriggers(node.nodeData);
|
|
@@ -30086,7 +30086,8 @@ function flowConversionHints(slug, rows, noConversionReason) {
|
|
|
30086
30086
|
`Nothing here is counted, and that is on purpose: this Form declares no outcome \u2014 "${noConversionReason}". Leave it alone unless that has stopped being true.`
|
|
30087
30087
|
];
|
|
30088
30088
|
}
|
|
30089
|
-
const
|
|
30089
|
+
const deepestFirst = [...rows].reverse();
|
|
30090
|
+
const suggestion = deepestFirst.find((row) => row.isCompletion && row.nodeType !== "text") ?? deepestFirst.find((row) => row.isCompletion) ?? rows[rows.length - 1];
|
|
30090
30091
|
return [
|
|
30091
30092
|
`Nothing on "${slug}" is counted, so it converts nobody on every report however many people finish it \u2014 Baker never guesses which ending is the point of a Form.`,
|
|
30092
30093
|
...suggestion ? [
|
|
@@ -30097,7 +30098,7 @@ function flowConversionHints(slug, rows, noConversionReason) {
|
|
|
30097
30098
|
}
|
|
30098
30099
|
function flowCoverage(forms, definitions) {
|
|
30099
30100
|
return forms.map(({ slug, tree }) => {
|
|
30100
|
-
const rows = joinFlowConversions(
|
|
30101
|
+
const rows = joinFlowConversions(flowConversionCandidates(tree, slug), definitions);
|
|
30101
30102
|
const declared = tree?.noConversion?.reason;
|
|
30102
30103
|
const seen = /* @__PURE__ */ new Set();
|
|
30103
30104
|
return {
|
|
@@ -32115,7 +32116,7 @@ function readFlowConversions2(slug, definitions) {
|
|
|
32115
32116
|
return {
|
|
32116
32117
|
slug,
|
|
32117
32118
|
noConversionReason: reason,
|
|
32118
|
-
endings: joinFlowConversions(
|
|
32119
|
+
endings: joinFlowConversions(flowConversionCandidates(tree, slug), definitions)
|
|
32119
32120
|
};
|
|
32120
32121
|
}
|
|
32121
32122
|
function conversionsEnvelope(args4, definitions, candidates) {
|
|
@@ -47232,63 +47233,8 @@ Full guide: __tooling__/docs/tools/baker/flows.md`
|
|
|
47232
47233
|
});
|
|
47233
47234
|
|
|
47234
47235
|
// src/commands/funnel/index.ts
|
|
47235
|
-
import { readFileSync as readFileSync15 } from "fs";
|
|
47236
47236
|
import { defineCommand as defineCommand129 } from "citty";
|
|
47237
47237
|
|
|
47238
|
-
// src/commands/funnel/formMoments.ts
|
|
47239
|
-
var MAX_NAMED_FORMS = 8;
|
|
47240
|
-
function formMoments(events, forms) {
|
|
47241
|
-
const outcomes = /* @__PURE__ */ new Map();
|
|
47242
|
-
for (const form of forms) {
|
|
47243
|
-
const outcome = suggestedEnding(flowEventKeys(form.tree, form.slug));
|
|
47244
|
-
if (outcome !== null) outcomes.set(outcome.eventKey, { slug: form.slug, outcome });
|
|
47245
|
-
}
|
|
47246
|
-
const marked = events.map((event) => {
|
|
47247
|
-
const found = outcomes.get(event.eventKey);
|
|
47248
|
-
if (found === void 0) return event;
|
|
47249
|
-
outcomes.delete(event.eventKey);
|
|
47250
|
-
return { ...event, step: found.outcome.nodeName, outcome: true };
|
|
47251
|
-
});
|
|
47252
|
-
const unfired = [...outcomes.values()].map(
|
|
47253
|
-
({ slug, outcome }) => ({
|
|
47254
|
-
eventKey: outcome.eventKey,
|
|
47255
|
-
origin: "site",
|
|
47256
|
-
flowSlug: slug,
|
|
47257
|
-
...outcome.nodeType === null ? {} : { nodeType: outcome.nodeType },
|
|
47258
|
-
events: 0,
|
|
47259
|
-
sessions: 0,
|
|
47260
|
-
step: outcome.nodeName,
|
|
47261
|
-
seen: false,
|
|
47262
|
-
outcome: true
|
|
47263
|
-
})
|
|
47264
|
-
);
|
|
47265
|
-
return [...marked, ...unfired];
|
|
47266
|
-
}
|
|
47267
|
-
function withFormMoments(moments, forms) {
|
|
47268
|
-
return { ...moments, events: formMoments(moments.events, forms) };
|
|
47269
|
-
}
|
|
47270
|
-
function formsNamedHere(forms) {
|
|
47271
|
-
return forms.filter((form) => suggestedEnding(flowEventKeys(form.tree, form.slug)) !== null).map((form) => form.slug);
|
|
47272
|
-
}
|
|
47273
|
-
function unplacedFormHints(steps, forms) {
|
|
47274
|
-
const onAStep = new Set(
|
|
47275
|
-
steps.flatMap((step) => (step.triggers ?? []).flatMap((trigger) => trigger.eventKey ? [trigger.eventKey] : []))
|
|
47276
|
-
);
|
|
47277
|
-
const unplaced = [];
|
|
47278
|
-
for (const form of forms) {
|
|
47279
|
-
const endings = flowEventKeys(form.tree, form.slug);
|
|
47280
|
-
if (endings.some((ending) => onAStep.has(ending.eventKey))) continue;
|
|
47281
|
-
const outcome = suggestedEnding(endings);
|
|
47282
|
-
if (outcome !== null) unplaced.push(`${form.slug} \u2192 \`${outcome.eventKey}\` ("${outcome.nodeName}")`);
|
|
47283
|
-
}
|
|
47284
|
-
if (unplaced.length === 0) return [];
|
|
47285
|
-
const shown = unplaced.slice(0, MAX_NAMED_FORMS);
|
|
47286
|
-
const rest = unplaced.length - shown.length;
|
|
47287
|
-
return [
|
|
47288
|
-
`${unplaced.length} Form(s) in this workspace reach no funnel step, so they move nobody along it however many people finish them: ${shown.join("; ")}${rest > 0 ? `; and ${rest} more` : ""}. Place one with \`baker funnel --add-trigger <step> --event <key>\`, which adds that one way in and leaves the rest of the step alone (\`baker funnel\` with no flags lists the step keys). If you also count a Form as a page conversion, count THAT SAME key, or the two numbers disagree for ever. A Form's other endings \u2014 a booking widget mid-flow, an embed's own signals \u2014 are listed by \`baker analytics conversions --flow <slug>\`.`
|
|
47289
|
-
];
|
|
47290
|
-
}
|
|
47291
|
-
|
|
47292
47238
|
// ../api/src/funnel/vocabulary.ts
|
|
47293
47239
|
var FUNNEL_STEP_CLASSES = [
|
|
47294
47240
|
/** Saw the offer: a landing opened, an ad clicked through. */
|
|
@@ -47581,18 +47527,6 @@ var funnelRequestSchema = z44.object({
|
|
|
47581
47527
|
step: funnelStepSchema.optional(),
|
|
47582
47528
|
/** One step taken out by key; whatever came after it moves up to whatever came before it. */
|
|
47583
47529
|
removeStep: z44.string().min(1).max(64).optional(),
|
|
47584
|
-
/**
|
|
47585
|
-
* ONE way in added to a step, leaving the rest of that step alone.
|
|
47586
|
-
*
|
|
47587
|
-
* `step` takes a whole step, so adding a way in through it means restating
|
|
47588
|
-
* the step's name, stage, `after[]` and every trigger it already had — and a
|
|
47589
|
-
* trigger left out of that restatement is silently removed. This is the door
|
|
47590
|
-
* for the thing that is actually being asked ("this Form's booking should
|
|
47591
|
-
* count here"), and it cannot drop anything.
|
|
47592
|
-
*/
|
|
47593
|
-
addTrigger: z44.object({ stepKey: z44.string().min(1).max(64), eventKey: z44.string().min(1).max(320) }).optional(),
|
|
47594
|
-
/** ONE way in taken off a step, by the event key it listens for. */
|
|
47595
|
-
removeTrigger: z44.object({ stepKey: z44.string().min(1).max(64), eventKey: z44.string().min(1).max(320) }).optional(),
|
|
47596
47530
|
/** One of the ready-made graphs, instead of stating steps. */
|
|
47597
47531
|
preset: z44.enum(FUNNEL_VERTICALS).optional(),
|
|
47598
47532
|
/** Also return the suggestion library and the presets. */
|
|
@@ -47604,14 +47538,7 @@ var funnelRequestSchema = z44.object({
|
|
|
47604
47538
|
*/
|
|
47605
47539
|
moments: z44.boolean().optional(),
|
|
47606
47540
|
/** How many days the counts beside each step cover. Default 30. */
|
|
47607
|
-
days: z44.number().int().min(1).max(400).optional()
|
|
47608
|
-
/**
|
|
47609
|
-
* The Forms, by slug, the caller names itself when no step counts them —
|
|
47610
|
-
* `baker funnel` reads its own workspace and names each with the key that
|
|
47611
|
-
* places it. The backend leaves them out of its own hint, so no Form is
|
|
47612
|
-
* named twice.
|
|
47613
|
-
*/
|
|
47614
|
-
namedByCaller: z44.array(z44.string().min(1).max(200)).max(1e3).optional()
|
|
47541
|
+
days: z44.number().int().min(1).max(400).optional()
|
|
47615
47542
|
});
|
|
47616
47543
|
var funnelResponseSchema = z44.object({
|
|
47617
47544
|
ok: z44.literal(true),
|
|
@@ -47677,30 +47604,7 @@ var funnelResponseSchema = z44.object({
|
|
|
47677
47604
|
events: z44.number(),
|
|
47678
47605
|
sessions: z44.number(),
|
|
47679
47606
|
/** The name it is counted under as a page conversion, when the company counts it. Informational. */
|
|
47680
|
-
countedAs: z44.string().optional()
|
|
47681
|
-
/**
|
|
47682
|
-
* The step of the Form this happens on, in the Form's own words —
|
|
47683
|
-
* never a node id. Present on a Form's ending read from the Form
|
|
47684
|
-
* itself, which is the only place the name is known.
|
|
47685
|
-
*/
|
|
47686
|
-
step: z44.string().optional(),
|
|
47687
|
-
/**
|
|
47688
|
-
* `true` on the ONE ending of a Form that is its outcome — the
|
|
47689
|
-
* booking on a Form that books, the submit on a Form that submits.
|
|
47690
|
-
* A Form raises several events and only one of them is the point;
|
|
47691
|
-
* without this the agent is handed five equal-looking keys and the
|
|
47692
|
-
* lifecycle noise a widget raises about itself sits among them.
|
|
47693
|
-
* Same rule as `baker analytics conversions` recommends, so the
|
|
47694
|
-
* conversion and the funnel step land on the same event.
|
|
47695
|
-
*/
|
|
47696
|
-
outcome: z44.boolean().optional(),
|
|
47697
|
-
/**
|
|
47698
|
-
* `false` on an ending a Form raises natively but nobody has fired
|
|
47699
|
-
* yet — derived from the Form, offered at zero, exactly as a
|
|
47700
|
-
* Source is offered before its first send. Absent means it came
|
|
47701
|
-
* from the event store and therefore happened.
|
|
47702
|
-
*/
|
|
47703
|
-
seen: z44.boolean().optional()
|
|
47607
|
+
countedAs: z44.string().optional()
|
|
47704
47608
|
})
|
|
47705
47609
|
),
|
|
47706
47610
|
/** The company's sources: a system of theirs that posts events to Baker, by the event name it raises. */
|
|
@@ -47756,15 +47660,9 @@ function parseFunnelArgs(raw) {
|
|
|
47756
47660
|
const step = parseStep(raw.step);
|
|
47757
47661
|
const removeStep = parseRemoveStep(raw["remove-step"] ?? raw.removeStep);
|
|
47758
47662
|
const preset = parsePreset(raw.preset);
|
|
47759
|
-
const
|
|
47760
|
-
const removeTrigger = parseWayIn(raw["remove-trigger"] ?? raw.removeTrigger, raw.event, "--remove-trigger");
|
|
47761
|
-
const doors = [steps, step, removeStep, preset, addTrigger, removeTrigger].filter(
|
|
47762
|
-
(door) => door !== void 0
|
|
47763
|
-
).length;
|
|
47663
|
+
const doors = [steps, step, removeStep, preset].filter((door) => door !== void 0).length;
|
|
47764
47664
|
if (doors > 1) {
|
|
47765
|
-
throw new FunnelArgsError(
|
|
47766
|
-
"Give one of --steps, --step, --remove-step, --preset, --add-trigger or --remove-trigger, not two."
|
|
47767
|
-
);
|
|
47665
|
+
throw new FunnelArgsError("Give one of --steps, --step, --remove-step or --preset, not two.");
|
|
47768
47666
|
}
|
|
47769
47667
|
const days = raw.days === void 0 ? void 0 : Number(raw.days);
|
|
47770
47668
|
if (days !== void 0 && (!Number.isInteger(days) || days <= 0 || days > 400)) {
|
|
@@ -47775,8 +47673,6 @@ function parseFunnelArgs(raw) {
|
|
|
47775
47673
|
...step === void 0 ? {} : { step },
|
|
47776
47674
|
...removeStep === void 0 ? {} : { removeStep },
|
|
47777
47675
|
...preset === void 0 ? {} : { preset },
|
|
47778
|
-
...addTrigger === void 0 ? {} : { addTrigger },
|
|
47779
|
-
...removeTrigger === void 0 ? {} : { removeTrigger },
|
|
47780
47676
|
templates: raw.templates === true,
|
|
47781
47677
|
moments: raw.moments === true,
|
|
47782
47678
|
...days === void 0 ? {} : { days }
|
|
@@ -47788,8 +47684,6 @@ function funnelWrite(args4) {
|
|
|
47788
47684
|
...args4.step === void 0 ? {} : { step: args4.step },
|
|
47789
47685
|
...args4.removeStep === void 0 ? {} : { removeStep: args4.removeStep },
|
|
47790
47686
|
...args4.preset === void 0 ? {} : { preset: args4.preset },
|
|
47791
|
-
...args4.addTrigger === void 0 ? {} : { addTrigger: args4.addTrigger },
|
|
47792
|
-
...args4.removeTrigger === void 0 ? {} : { removeTrigger: args4.removeTrigger },
|
|
47793
47687
|
...args4.templates ? { templates: true } : {},
|
|
47794
47688
|
...args4.moments ? { moments: true } : {},
|
|
47795
47689
|
...args4.days === void 0 ? {} : { days: args4.days }
|
|
@@ -47899,26 +47793,6 @@ function parsePreset(raw) {
|
|
|
47899
47793
|
}
|
|
47900
47794
|
return raw;
|
|
47901
47795
|
}
|
|
47902
|
-
function parseWayIn(stepKey, eventKey, flag) {
|
|
47903
|
-
if (stepKey === void 0) return void 0;
|
|
47904
|
-
if (typeof stepKey !== "string" || stepKey.trim() === "") {
|
|
47905
|
-
throw new FunnelArgsError(`${flag} takes the key of the step to change: \`${flag} lead\`.`);
|
|
47906
|
-
}
|
|
47907
|
-
if (typeof eventKey !== "string" || eventKey.trim() === "") {
|
|
47908
|
-
throw new FunnelArgsError(`${flag} needs --event, naming what gets somebody there.`, {
|
|
47909
|
-
action: "read_the_moments",
|
|
47910
|
-
explanation: `Run \`baker funnel --moments\` and copy an \`events[].eventKey\` \u2014 a Form's own outcome is listed there at zero before anybody has filled it in. Then: \`${flag} ${stepKey.trim()} --event "<key>"\`.`
|
|
47911
|
-
});
|
|
47912
|
-
}
|
|
47913
|
-
const key = eventKey.trim();
|
|
47914
|
-
if (flag === "--add-trigger" && parseEventKey(key) === null) {
|
|
47915
|
-
throw new FunnelArgsError(`"${key}" is not an event key, so nothing would ever reach that step through it.`, {
|
|
47916
|
-
action: "read_the_moments",
|
|
47917
|
-
explanation: "Run `baker funnel --moments` and copy an `events[].eventKey`. They look like form:<form>:<step>:<trigger>, submit:<form>, page:<name> or view:<landing>."
|
|
47918
|
-
});
|
|
47919
|
-
}
|
|
47920
|
-
return { stepKey: stepKey.trim(), eventKey: key };
|
|
47921
|
-
}
|
|
47922
47796
|
|
|
47923
47797
|
// src/commands/funnel/index.ts
|
|
47924
47798
|
var args3 = {
|
|
@@ -47932,21 +47806,6 @@ var args3 = {
|
|
|
47932
47806
|
description: 'ONE step as a JSON object, in the same shape as an entry of --steps. It is added to the funnel as it now stands, or replaces the step with the same `key`, and every other step is left exactly as it is \u2014 the door for adding or changing a stage without restating the rest. Example: --step \'{"key":"demo","displayName":"Demo held","stepClass":"engaged","after":["lead"],"triggers":[{"id":"t1","kind":"event","eventKey":"page:demo_held"}]}\'',
|
|
47933
47807
|
required: false
|
|
47934
47808
|
},
|
|
47935
|
-
"add-trigger": {
|
|
47936
|
-
type: "string",
|
|
47937
|
-
description: 'The key of ONE step to add a way in to, with `--event` naming what gets somebody there: `--add-trigger lead --event "submit:contact"`. Start here for "this Form should count as a lead" \u2014 it adds that one way in and leaves the step\'s name, stage, `after[]` and every other trigger exactly as they are. Prefer it to `--step`, which takes a WHOLE step and therefore silently removes any trigger the restatement forgets. Adding a key the step already listens for changes nothing. Take the key from `--moments`, never from memory',
|
|
47938
|
-
required: false
|
|
47939
|
-
},
|
|
47940
|
-
"remove-trigger": {
|
|
47941
|
-
type: "string",
|
|
47942
|
-
description: 'The key of ONE step to take a way in OFF, with `--event` naming which: `--remove-trigger lead --event "submit:contact"`. By event key rather than by trigger id, because the id is not something you ever saw. Everything else about the step is untouched',
|
|
47943
|
-
required: false
|
|
47944
|
-
},
|
|
47945
|
-
event: {
|
|
47946
|
-
type: "string",
|
|
47947
|
-
description: "The event key `--add-trigger` or `--remove-trigger` acts on \u2014 an `events[].eventKey` from `--moments`, never invented. A Form's own outcome is listed there at zero, marked `outcome: true`, before anybody has filled the Form in",
|
|
47948
|
-
required: false
|
|
47949
|
-
},
|
|
47950
47809
|
"remove-step": {
|
|
47951
47810
|
type: "string",
|
|
47952
47811
|
description: "The key of one step to take off the funnel. Whatever came after it moves up to whatever came before it, so the chain stays joined. The response lists it under `removed`",
|
|
@@ -47959,7 +47818,7 @@ var args3 = {
|
|
|
47959
47818
|
},
|
|
47960
47819
|
moments: {
|
|
47961
47820
|
type: "boolean",
|
|
47962
|
-
description: "Also return everything a trigger can name, so a trigger is PICKED rather than invented: `events` (every event this company's Forms, pages and systems produced in the window, with volume, the Form it belongs to, `origin` \u2014 `site` for a page or Form, `systems` for the client's own server, `both` \u2014 and `countedAs` when the company also counts it as a page conversion, which a trigger does not need
|
|
47821
|
+
description: "Also return everything a trigger can name, so a trigger is PICKED rather than invented: `events` (every event this company's Forms, pages and systems actually produced in the window, with volume, the Form it belongs to, `origin` \u2014 `site` for a page or Form, `systems` for the client's own server, `both` \u2014 and `countedAs` when the company also counts it as a page conversion, which a trigger does not need), `sources` (the client's own systems that post events to Baker, by the `page:<name>` key each raises, pickable before its first send arrives) and `landings` (its pages, by the id a `landing` trigger takes). Use this before writing any `triggers`: a key typed from memory saves, publishes and counts nobody forever",
|
|
47963
47822
|
required: false
|
|
47964
47823
|
},
|
|
47965
47824
|
days: {
|
|
@@ -47981,25 +47840,17 @@ registerSchema({
|
|
|
47981
47840
|
var funnelCommand2 = defineCommand129({
|
|
47982
47841
|
meta: {
|
|
47983
47842
|
name: "funnel",
|
|
47984
|
-
description: 'Start here: run with no flags to see the funnel this company has, with the last 30 days of PEOPLE counted beside every step \u2014 `people` reached it, `atLeast` got at least that far, `before` got as far as the step before, `share` is the ratio, and `worstStep` names where the most people are lost. Every company has one from the day it exists: Visited (any landing) \u2192 Became a lead \u2192 Became a customer, the last two with no way in until somebody gives them one. Then `--moments` for everything a trigger can name (every event the company\'s Forms, pages and systems produced, its sources, its landings,
|
|
47843
|
+
description: 'Start here: run with no flags to see the funnel this company has, with the last 30 days of PEOPLE counted beside every step \u2014 `people` reached it, `atLeast` got at least that far, `before` got as far as the step before, `share` is the ratio, and `worstStep` names where the most people are lost. Every company has one from the day it exists: Visited (any landing) \u2192 Became a lead \u2192 Became a customer, the last two with no way in until somebody gives them one. Then `--moments` for everything a trigger can name (every event the company\'s Forms, pages and systems actually produced, its sources, its landings), and `--step \'<json>\'` to add or change ONE step in the client\'s own words \u2014 it replaces the step with that key, so restate its `after` and the triggers it keeps. A step is reached by three kinds of thing and nothing else: an event by its own key (`{"id":"t1","kind":"event","eventKey":"<from moments.events or moments.sources>"}`), one landing being viewed (`{"id":"t2","kind":"landing","landingId":"<from moments.landings>"}`) or any landing (`{"id":"t3","kind":"any_landing"}`). Nothing has to be named first. Pick every trigger from `--moments`, never from memory. A step with no `triggers` advances NOBODY and reports zero forever without warning \u2014 that is what the hints keep pointing at. There is no default door: a Form is only counted where a trigger names it. Keep it true: a chat that adds a Form, a Source or an event that moves people forward places it on its step in the same chat. Each step comes back with `sends[]`, read-only: where the people who reach it are sent and what happened, read off the system events behind its ways in. A send is set on the event with `baker analytics sends`, never on a step, and once published it spends ad budget \u2014 a failed one is named in the hints. Everything here is STAGED: it becomes the company\'s funnel in one piece when the chat is published \u2014 the client sees it on the canvas in this chat and can edit it there too \u2014 and the previous version stays on file so what was already measured under it is not lost.'
|
|
47985
47844
|
},
|
|
47986
47845
|
args: args3,
|
|
47987
47846
|
run: async ({ args: raw }) => {
|
|
47988
47847
|
try {
|
|
47989
47848
|
const parsed = parseFunnelArgs(raw);
|
|
47990
|
-
const
|
|
47991
|
-
const namedByCaller = formsNamedHere(forms);
|
|
47992
|
-
const request = {
|
|
47993
|
-
...funnelWrite(parsed),
|
|
47994
|
-
...namedByCaller.length === 0 ? {} : { namedByCaller }
|
|
47995
|
-
};
|
|
47996
|
-
const response = await apiPost("/api/funnel", request);
|
|
47997
|
-
const data = response.data.moments === void 0 ? response.data : { ...response.data, moments: withFormMoments(response.data.moments, forms) };
|
|
47998
|
-
const hints2 = [...response.hints ?? [], ...unplacedFormHints(response.data.steps ?? [], forms)];
|
|
47849
|
+
const response = await apiPost("/api/funnel", funnelWrite(parsed));
|
|
47999
47850
|
writeJsonEnvelope({
|
|
48000
47851
|
ok: true,
|
|
48001
|
-
data,
|
|
48002
|
-
...
|
|
47852
|
+
data: response.data,
|
|
47853
|
+
...response.hints && response.hints.length > 0 ? { hints: response.hints } : {}
|
|
48003
47854
|
});
|
|
48004
47855
|
} catch (err) {
|
|
48005
47856
|
handleError4(err);
|
|
@@ -48024,16 +47875,6 @@ function handleError4(err) {
|
|
|
48024
47875
|
});
|
|
48025
47876
|
process.exit(1);
|
|
48026
47877
|
}
|
|
48027
|
-
function workspaceForms() {
|
|
48028
|
-
const forms = [];
|
|
48029
|
-
for (const slug of listFlowSlugs()) {
|
|
48030
|
-
try {
|
|
48031
|
-
forms.push({ slug, tree: JSON.parse(readFileSync15(flowDataPath(slug), "utf-8")) });
|
|
48032
|
-
} catch {
|
|
48033
|
-
}
|
|
48034
|
-
}
|
|
48035
|
-
return forms;
|
|
48036
|
-
}
|
|
48037
47878
|
|
|
48038
47879
|
// src/commands/ga4/index.ts
|
|
48039
47880
|
import { defineCommand as defineCommand136 } from "citty";
|
|
@@ -48163,7 +48004,7 @@ Examples:
|
|
|
48163
48004
|
import { defineCommand as defineCommand131 } from "citty";
|
|
48164
48005
|
|
|
48165
48006
|
// src/commands/ga4/shared.ts
|
|
48166
|
-
import { readFileSync as
|
|
48007
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
48167
48008
|
function failValidation3(message) {
|
|
48168
48009
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
48169
48010
|
process.exit(1);
|
|
@@ -48179,7 +48020,7 @@ function requireTarget5(args4, entity) {
|
|
|
48179
48020
|
function readJsonSource(args4, flag) {
|
|
48180
48021
|
const inline = args4[flag];
|
|
48181
48022
|
const file = args4.file;
|
|
48182
|
-
const raw = typeof file === "string" && file.length > 0 ?
|
|
48023
|
+
const raw = typeof file === "string" && file.length > 0 ? readFileSync15(file, "utf8") : typeof inline === "string" && inline.length > 0 ? inline : void 0;
|
|
48183
48024
|
if (raw === void 0) {
|
|
48184
48025
|
failValidation3(`pass --${flag} with inline JSON or --file with a path to a JSON file`);
|
|
48185
48026
|
}
|
|
@@ -48530,7 +48371,7 @@ Examples:
|
|
|
48530
48371
|
});
|
|
48531
48372
|
|
|
48532
48373
|
// src/commands/ga4/query.ts
|
|
48533
|
-
import { appendFileSync as appendFileSync2, existsSync as existsSync7, readFileSync as
|
|
48374
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync7, readFileSync as readFileSync16, writeFileSync as writeFileSync4 } from "fs";
|
|
48534
48375
|
import { resolve as resolve2 } from "path";
|
|
48535
48376
|
import { defineCommand as defineCommand134 } from "citty";
|
|
48536
48377
|
|
|
@@ -48621,7 +48462,7 @@ function writeRowsToFile2(filePath, rows, append) {
|
|
|
48621
48462
|
writeFileSync4(filePath, content, "utf-8");
|
|
48622
48463
|
}
|
|
48623
48464
|
} else if (append && existsSync7(filePath)) {
|
|
48624
|
-
const existing = JSON.parse(
|
|
48465
|
+
const existing = JSON.parse(readFileSync16(filePath, "utf-8"));
|
|
48625
48466
|
writeFileSync4(filePath, JSON.stringify([...existing, ...rows], null, 2), "utf-8");
|
|
48626
48467
|
} else {
|
|
48627
48468
|
writeFileSync4(filePath, JSON.stringify(rows, null, 2), "utf-8");
|
|
@@ -49473,7 +49314,7 @@ Full guide: __tooling__/docs/tools/baker/ga4.md`
|
|
|
49473
49314
|
import { defineCommand as defineCommand140 } from "citty";
|
|
49474
49315
|
|
|
49475
49316
|
// src/commands/gsc/query.ts
|
|
49476
|
-
import { appendFileSync as appendFileSync3, existsSync as existsSync8, readFileSync as
|
|
49317
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync8, readFileSync as readFileSync17, writeFileSync as writeFileSync5 } from "fs";
|
|
49477
49318
|
import { resolve as resolve3 } from "path";
|
|
49478
49319
|
import { defineCommand as defineCommand137 } from "citty";
|
|
49479
49320
|
|
|
@@ -49621,7 +49462,7 @@ function writeRowsToFile3(filePath, rows, append) {
|
|
|
49621
49462
|
writeFileSync5(filePath, content, "utf-8");
|
|
49622
49463
|
}
|
|
49623
49464
|
} else if (append && existsSync8(filePath)) {
|
|
49624
|
-
const existing = JSON.parse(
|
|
49465
|
+
const existing = JSON.parse(readFileSync17(filePath, "utf-8"));
|
|
49625
49466
|
writeFileSync5(filePath, JSON.stringify([...existing, ...rows], null, 2), "utf-8");
|
|
49626
49467
|
} else {
|
|
49627
49468
|
writeFileSync5(filePath, JSON.stringify(rows, null, 2), "utf-8");
|
|
@@ -56499,7 +56340,7 @@ var inPageCollectUsedCss = (selector) => {
|
|
|
56499
56340
|
}
|
|
56500
56341
|
return false;
|
|
56501
56342
|
};
|
|
56502
|
-
const
|
|
56343
|
+
const walk2 = (rules, sink) => {
|
|
56503
56344
|
for (const rule of Array.from(rules)) {
|
|
56504
56345
|
totalRules++;
|
|
56505
56346
|
if (rule instanceof CSSStyleRule) {
|
|
@@ -56520,7 +56361,7 @@ var inPageCollectUsedCss = (selector) => {
|
|
|
56520
56361
|
const grouping = rule instanceof CSSMediaRule || rule instanceof CSSSupportsRule || typeof CSSLayerBlockRule !== "undefined" && rule instanceof CSSLayerBlockRule || typeof CSSContainerRule !== "undefined" && rule instanceof CSSContainerRule;
|
|
56521
56362
|
if (grouping) {
|
|
56522
56363
|
const inner = [];
|
|
56523
|
-
|
|
56364
|
+
walk2(rule.cssRules, inner);
|
|
56524
56365
|
if (inner.length === 0) continue;
|
|
56525
56366
|
const condition = rule.conditionText ?? "";
|
|
56526
56367
|
const prelude = rule instanceof CSSMediaRule ? `@media ${condition}` : rule.cssText.split("{")[0]?.trim();
|
|
@@ -56534,7 +56375,7 @@ ${inner.join("\n")}
|
|
|
56534
56375
|
const owner = sheet.ownerNode;
|
|
56535
56376
|
if (owner?.hasAttribute?.("data-baker-freeze")) continue;
|
|
56536
56377
|
try {
|
|
56537
|
-
|
|
56378
|
+
walk2(sheet.cssRules, kept);
|
|
56538
56379
|
} catch {
|
|
56539
56380
|
if (sheet.href) unreadableHrefs.push(sheet.href);
|
|
56540
56381
|
}
|
|
@@ -56985,7 +56826,7 @@ var inPageCollectMotion = (selector) => {
|
|
|
56985
56826
|
else if (infinite) loop.push(effect);
|
|
56986
56827
|
else entrance.push(effect);
|
|
56987
56828
|
};
|
|
56988
|
-
const
|
|
56829
|
+
const walk2 = (rules, insideScrollTimeline) => {
|
|
56989
56830
|
for (const rule of Array.from(rules)) {
|
|
56990
56831
|
if (rule instanceof CSSKeyframesRule) {
|
|
56991
56832
|
keyframesByName.set(rule.name, rule.cssText);
|
|
@@ -57003,17 +56844,17 @@ var inPageCollectMotion = (selector) => {
|
|
|
57003
56844
|
respectsReducedMotion = true;
|
|
57004
56845
|
continue;
|
|
57005
56846
|
}
|
|
57006
|
-
|
|
56847
|
+
walk2(rule.cssRules, insideScrollTimeline);
|
|
57007
56848
|
continue;
|
|
57008
56849
|
}
|
|
57009
56850
|
const grouping = rule;
|
|
57010
|
-
if (grouping.cssRules)
|
|
56851
|
+
if (grouping.cssRules) walk2(grouping.cssRules, insideScrollTimeline);
|
|
57011
56852
|
}
|
|
57012
56853
|
};
|
|
57013
56854
|
for (const sheet of Array.from(document.styleSheets)) {
|
|
57014
56855
|
if (sheet.ownerNode?.hasAttribute?.("data-baker-freeze")) continue;
|
|
57015
56856
|
try {
|
|
57016
|
-
|
|
56857
|
+
walk2(sheet.cssRules, false);
|
|
57017
56858
|
} catch {
|
|
57018
56859
|
}
|
|
57019
56860
|
}
|
|
@@ -62852,7 +62693,7 @@ import { defineCommand as defineCommand217 } from "citty";
|
|
|
62852
62693
|
import { defineCommand as defineCommand214 } from "citty";
|
|
62853
62694
|
|
|
62854
62695
|
// src/commands/tag-manager/shared.ts
|
|
62855
|
-
import { readFileSync as
|
|
62696
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
62856
62697
|
function failValidation5(message) {
|
|
62857
62698
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
62858
62699
|
process.exit(1);
|
|
@@ -62868,7 +62709,7 @@ function requireTarget6(args4, entity) {
|
|
|
62868
62709
|
function loadJsonArg2(args4, flag = "json") {
|
|
62869
62710
|
const inline = args4[flag];
|
|
62870
62711
|
const file = args4.file;
|
|
62871
|
-
const raw = typeof file === "string" && file.length > 0 ?
|
|
62712
|
+
const raw = typeof file === "string" && file.length > 0 ? readFileSync18(file, "utf8") : typeof inline === "string" && inline.length > 0 ? inline : void 0;
|
|
62872
62713
|
if (raw === void 0) {
|
|
62873
62714
|
failValidation5(`pass --${flag} with inline JSON or --file with a path to a JSON file`);
|
|
62874
62715
|
}
|
|
@@ -66147,7 +65988,7 @@ function refuseUnknownFlags(root, argv) {
|
|
|
66147
65988
|
}
|
|
66148
65989
|
|
|
66149
65990
|
// src/version.ts
|
|
66150
|
-
import { readFileSync as
|
|
65991
|
+
import { readFileSync as readFileSync19 } from "fs";
|
|
66151
65992
|
function packageJsonUrl() {
|
|
66152
65993
|
return new URL("../package.json", import.meta.url);
|
|
66153
65994
|
}
|
|
@@ -66159,7 +66000,7 @@ function parsePackageVersion(raw) {
|
|
|
66159
66000
|
throw new Error("Invalid CLI package.json: missing version");
|
|
66160
66001
|
}
|
|
66161
66002
|
function getCliVersion() {
|
|
66162
|
-
return parsePackageVersion(
|
|
66003
|
+
return parsePackageVersion(readFileSync19(packageJsonUrl(), "utf8"));
|
|
66163
66004
|
}
|
|
66164
66005
|
|
|
66165
66006
|
// src/cli.ts
|