@koda-sl/baker-cli 0.307.0-dev.25b4836d3 → 0.307.0-dev.48246ea89
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/cli.js +66 -16
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4816,6 +4816,8 @@ var chatChangeTypeSchema = z13.enum([
|
|
|
4816
4816
|
"conversion-sends",
|
|
4817
4817
|
// The company's sources: a HubSpot event made or changed. Staged, applied at Publish.
|
|
4818
4818
|
"event-sources",
|
|
4819
|
+
// What the company counts as a conversion, when an agent changed it. Staged, applied at Publish.
|
|
4820
|
+
"conversions",
|
|
4819
4821
|
// An A/B test between a landing and its alternative. Staged, not immediate.
|
|
4820
4822
|
"experiment",
|
|
4821
4823
|
"briefs"
|
|
@@ -14950,7 +14952,9 @@ var analyticsConversionRowSchema = z30.object({
|
|
|
14950
14952
|
* The doors its events came in by in the window, in
|
|
14951
14953
|
* {@link ANALYTICS_EVENT_DOORS} order. Empty when it has not fired.
|
|
14952
14954
|
*/
|
|
14953
|
-
doors: z30.array(z30.enum(ANALYTICS_EVENT_DOORS)).default([])
|
|
14955
|
+
doors: z30.array(z30.enum(ANALYTICS_EVENT_DOORS)).default([]),
|
|
14956
|
+
/** Proposed in this chat: counted from when it is published, so every report reads it as nothing until then. */
|
|
14957
|
+
staged: z30.boolean().optional()
|
|
14954
14958
|
});
|
|
14955
14959
|
var analyticsEventCatalogRowSchema = z30.object({
|
|
14956
14960
|
/** `form:…` | `submit:…` | `page:…` | `exit:…` — parse with `parseEventKey`. */
|
|
@@ -15558,14 +15562,24 @@ var analyticsConversionsRequestSchema = z30.object({
|
|
|
15558
15562
|
var analyticsConversionsResponseSchema = z30.object({
|
|
15559
15563
|
ok: z30.literal(true),
|
|
15560
15564
|
data: z30.object({
|
|
15565
|
+
/**
|
|
15566
|
+
* What the company counts — inside a chat, with this chat's proposal on
|
|
15567
|
+
* top: `staged` marks a definition that applies when the chat is published.
|
|
15568
|
+
*/
|
|
15561
15569
|
definitions: z30.array(
|
|
15562
15570
|
z30.object({
|
|
15563
15571
|
eventKey: z30.string(),
|
|
15564
15572
|
name: z30.string(),
|
|
15565
15573
|
countMode: z30.string(),
|
|
15566
|
-
updatedAt: z30.number()
|
|
15574
|
+
updatedAt: z30.number(),
|
|
15575
|
+
staged: z30.boolean().optional()
|
|
15567
15576
|
})
|
|
15568
|
-
)
|
|
15577
|
+
),
|
|
15578
|
+
/** This chat's proposal, when it has one: counted from Publish, and gone if the chat is discarded. */
|
|
15579
|
+
staged: z30.object({
|
|
15580
|
+
set: z30.array(z30.object({ eventKey: z30.string(), name: z30.string() })),
|
|
15581
|
+
remove: z30.array(z30.string())
|
|
15582
|
+
}).optional()
|
|
15569
15583
|
}),
|
|
15570
15584
|
hints: z30.array(z30.string()).optional()
|
|
15571
15585
|
});
|
|
@@ -29278,6 +29292,14 @@ function namingHints(definitions) {
|
|
|
29278
29292
|
`Names in use here: ${names.join(", ")}. Reuse one EXACTLY when another Form produces the same outcome \u2014 that is what makes them one number \u2014 and use \`--rename "<old>" --to "<existing>"\` to merge two that should always have been one.`
|
|
29279
29293
|
];
|
|
29280
29294
|
}
|
|
29295
|
+
function stagedHints(definitions) {
|
|
29296
|
+
const staged = definitions.filter((row) => row.staged === true);
|
|
29297
|
+
if (staged.length === 0) return [];
|
|
29298
|
+
return [
|
|
29299
|
+
`Staged on this chat, not counted yet: ${staged.map((row) => `"${row.name}"`).join(", ")}. The person sees ${staged.length === 1 ? "it" : "them"} under Conversions in the chat's changes, and ${staged.length === 1 ? "it counts" : "they count"} \u2014 history included \u2014 from when they publish the chat; discarding the chat drops ${staged.length === 1 ? "it" : "them"}. Tell them that, not that it is already counted.`,
|
|
29300
|
+
'When you talk to the person, call each event by its name ("Appointment scheduled"), never its key (`page:Appointment scheduled`): the key is how Baker spells it internally, and the `page:` part means nothing to them.'
|
|
29301
|
+
];
|
|
29302
|
+
}
|
|
29281
29303
|
|
|
29282
29304
|
// src/commands/analytics/conversionList.ts
|
|
29283
29305
|
function mergeStoredWithCounts(stored, reported) {
|
|
@@ -29303,7 +29325,8 @@ function mergeStoredWithCounts(stored, reported) {
|
|
|
29303
29325
|
conversions: counted?.conversions ?? 0,
|
|
29304
29326
|
convertingVisits: counted?.convertingVisits ?? 0,
|
|
29305
29327
|
lastSeen: counted?.lastSeen ?? null,
|
|
29306
|
-
doors: counted?.doors ?? []
|
|
29328
|
+
doors: counted?.doors ?? [],
|
|
29329
|
+
...rows.some((row) => row.staged === true) ? { staged: true } : {}
|
|
29307
29330
|
};
|
|
29308
29331
|
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
29309
29332
|
}
|
|
@@ -31430,6 +31453,7 @@ function conversionsEnvelope(args4, definitions, candidates) {
|
|
|
31430
31453
|
const hints2 = [
|
|
31431
31454
|
...flow ? flowConversionHints(flow.slug, flow.endings, flow.noConversionReason) : [],
|
|
31432
31455
|
...flowCoverageHints(coverage),
|
|
31456
|
+
...stagedHints(definitions),
|
|
31433
31457
|
...page.hints,
|
|
31434
31458
|
...namingHints(definitions)
|
|
31435
31459
|
];
|
|
@@ -31448,7 +31472,7 @@ var conversionsCommand3 = (() => {
|
|
|
31448
31472
|
const args4 = {
|
|
31449
31473
|
event: {
|
|
31450
31474
|
type: "string",
|
|
31451
|
-
description: "The event to start counting, from the `candidates` or `--flow` list: form:<form>:<step>:<trigger>, submit:<form>, page:<name>, or exit:<host>. Requires --name. REPEATABLE \u2014 pass it several times with one --name to count all of them as that single outcome, which is the whole point of the name.
|
|
31475
|
+
description: "The event to start counting, from the `candidates` or `--flow` list: form:<form>:<step>:<trigger>, submit:<form>, page:<name>, or exit:<host>. Requires --name. REPEATABLE \u2014 pass it several times with one --name to count all of them as that single outcome, which is the whole point of the name. Inside a chat it is STAGED: the chat lists it under Conversions and it counts from when the chat is published \u2014 then it scores the WHOLE history, not just from then on, so counting the right event also corrects last month's reports. An event a client's system sends (the API, HubSpot) counts here too: it is credited to the LAST LANDING its person viewed, up to 90 days before, so a deal won weeks later is that landing's conversion. A person who never saw a landing is not counted at all. Such an event is usually also a funnel step (`baker funnel`) and a send (`baker analytics sends`)",
|
|
31452
31476
|
required: false
|
|
31453
31477
|
},
|
|
31454
31478
|
name: {
|
|
@@ -31505,7 +31529,7 @@ var conversionsCommand3 = (() => {
|
|
|
31505
31529
|
return defineCommand97({
|
|
31506
31530
|
meta: {
|
|
31507
31531
|
name: "conversions",
|
|
31508
|
-
description: "What this company counts as a conversion
|
|
31532
|
+
description: "What this company counts as a conversion \u2014 the numerator of every conversion rate on Measure. A page event (a Form ending, a data-baker-* event) and an event one of the client's systems sends (a HubSpot event made with `baker analytics sources --create-hubspot`, an order the server posts) both count; a system's event is credited to the last landing its person viewed. A HubSpot event is `page:<its name>` here \u2014 `--event 'page:Deal won' --name 'Deal won'`. Run with no flags first: an empty `definitions` list means every conversion number in every other report is zero \u2014 not because nobody converted, but because nothing is marked as converting, and Baker never guesses. Add `--candidates` to see everything these pages actually do (every Form step, every data-baker-* event, every outbound destination) with counts and whether it is already counted; that list is where an event key comes from, so never invent one. Then `--event <key> --name 'Booked a call'` to count it. Inside a chat a change is STAGED: the person sees it under Conversions in the chat's changes and it applies when they publish, retroactively \u2014 the whole history is rescored, so a company that has been running for a month gets a month of correct numbers. A read shows a proposal marked `staged`. Call an event by its name when you talk to the person, never by its key. Two events given the SAME --name are one row and one number. `--remove <key>` stops counting one. `--flow <slug>` answers the other direction \u2014 what one Form counts, with every ending it has already spelled as an event key, read from the Form itself so it works before a single visitor has seen it. Run it whenever you create or edit a Form: a Form nobody named an outcome for is published converting nobody, and no report says so."
|
|
31509
31533
|
},
|
|
31510
31534
|
args: args4,
|
|
31511
31535
|
run: async ({ args: raw }) => {
|
|
@@ -31536,12 +31560,8 @@ var conversionsCommand3 = (() => {
|
|
|
31536
31560
|
}
|
|
31537
31561
|
});
|
|
31538
31562
|
})();
|
|
31539
|
-
var SYSTEM_EVENTS_HINT = "Events your systems send are not listed: a system's event is a funnel step (`baker funnel`) and is sent to an ad platform with `baker analytics sends`, never counted as a page conversion.";
|
|
31540
31563
|
function pageCandidates(definitions, catalogue, listing) {
|
|
31541
|
-
|
|
31542
|
-
const hints2 = conversionHints(definitions, candidates, listing);
|
|
31543
|
-
if (listing && candidates.length < catalogue.length) hints2.push(SYSTEM_EVENTS_HINT);
|
|
31544
|
-
return { candidates, hints: hints2 };
|
|
31564
|
+
return { candidates: [...catalogue], hints: conversionHints(definitions, catalogue, listing) };
|
|
31545
31565
|
}
|
|
31546
31566
|
function parsePairs(raw) {
|
|
31547
31567
|
if (raw === void 0) return void 0;
|
|
@@ -45288,12 +45308,37 @@ function splitParts(raw) {
|
|
|
45288
45308
|
}
|
|
45289
45309
|
return parts.map((part) => part.trim()).filter((part) => part.length > 0);
|
|
45290
45310
|
}
|
|
45311
|
+
var TRANSFORM_REFUSAL = "A value cannot run code. Map the field as it is (firstname=full_name), or split it in the step with a codeExecution that exposes first_name and last_name and map code:first_name.";
|
|
45312
|
+
var METHOD_CALL = /\.\s*[A-Za-z_$][\w$]*\s*\(/;
|
|
45313
|
+
var TRAILING_INDEX = /\[[^\]]*\]$/;
|
|
45314
|
+
function mentionPart(inner) {
|
|
45315
|
+
if (inner.startsWith("tracking.")) return { kind: "tracking", name: inner.slice("tracking.".length) };
|
|
45316
|
+
if (inner.startsWith("global.")) return { kind: "global", name: inner.slice("global.".length) };
|
|
45317
|
+
return referencePart(inner);
|
|
45318
|
+
}
|
|
45319
|
+
function referencePart(ref) {
|
|
45320
|
+
return /\.codeExecution\.[^.]+$/.test(ref) ? { kind: "code", ref } : { kind: "field", ref };
|
|
45321
|
+
}
|
|
45291
45322
|
function parsePart(part) {
|
|
45292
45323
|
if (part.startsWith('"') && part.endsWith('"') && part.length >= 2) {
|
|
45293
45324
|
return { kind: "text", text: part.slice(1, -1) };
|
|
45294
45325
|
}
|
|
45326
|
+
if (METHOD_CALL.test(part) || TRAILING_INDEX.test(part)) {
|
|
45327
|
+
throw new MappingError(TRANSFORM_REFUSAL);
|
|
45328
|
+
}
|
|
45329
|
+
if (part.startsWith("{{")) {
|
|
45330
|
+
const close = part.indexOf("}}");
|
|
45331
|
+
if (close < 0) {
|
|
45332
|
+
throw new MappingError(
|
|
45333
|
+
`Unclosed \`{{\` in value \`${part}\`.`,
|
|
45334
|
+
"A mention is written {{<stepId>.customForm.<field>}}."
|
|
45335
|
+
);
|
|
45336
|
+
}
|
|
45337
|
+
if (part.slice(close + 2).trim()) throw new MappingError(TRANSFORM_REFUSAL);
|
|
45338
|
+
return mentionPart(part.slice(2, close).trim());
|
|
45339
|
+
}
|
|
45295
45340
|
const colon = part.indexOf(":");
|
|
45296
|
-
if (colon <= 0) return
|
|
45341
|
+
if (colon <= 0) return referencePart(part);
|
|
45297
45342
|
const prefix = part.slice(0, colon);
|
|
45298
45343
|
const rest = part.slice(colon + 1).trim();
|
|
45299
45344
|
if (!PREFIXES.includes(prefix)) {
|
|
@@ -45351,7 +45396,12 @@ function fieldOwners(tree) {
|
|
|
45351
45396
|
];
|
|
45352
45397
|
});
|
|
45353
45398
|
}
|
|
45354
|
-
function splitRef(ref) {
|
|
45399
|
+
function splitRef(ref, segment) {
|
|
45400
|
+
const marker = `.${segment}.`;
|
|
45401
|
+
const at = ref.lastIndexOf(marker);
|
|
45402
|
+
if (at > 0 && !ref.slice(at + marker.length).includes(".")) {
|
|
45403
|
+
return { step: ref.slice(0, at), name: ref.slice(at + marker.length) };
|
|
45404
|
+
}
|
|
45355
45405
|
const dot = ref.lastIndexOf(".");
|
|
45356
45406
|
if (dot <= 0) return { name: ref };
|
|
45357
45407
|
return { step: ref.slice(0, dot), name: ref.slice(dot + 1) };
|
|
@@ -45363,7 +45413,7 @@ function titleize(value) {
|
|
|
45363
45413
|
return value.split(/[_-]+/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
45364
45414
|
}
|
|
45365
45415
|
function resolveField(tree, ref) {
|
|
45366
|
-
const { step, name } = splitRef(ref);
|
|
45416
|
+
const { step, name } = splitRef(ref, "customForm");
|
|
45367
45417
|
const owners = fieldOwners(tree);
|
|
45368
45418
|
const inventory = owners.map((owner2) => ` ${owner2.name} (${owner2.id}): ${owner2.fields.join(", ") || "(no fields)"}`).join("\n");
|
|
45369
45419
|
const candidates = owners.filter(
|
|
@@ -45406,7 +45456,7 @@ function resolveGlobal(tree, name) {
|
|
|
45406
45456
|
return mention(id, id);
|
|
45407
45457
|
}
|
|
45408
45458
|
function resolveCode(tree, ref) {
|
|
45409
|
-
const { step, name } = splitRef(ref);
|
|
45459
|
+
const { step, name } = splitRef(ref, "codeExecution");
|
|
45410
45460
|
const owners = walkNodes(tree).flatMap((node) => {
|
|
45411
45461
|
const exposed = externalOf(node).codeExecution?.exposedValues ?? [];
|
|
45412
45462
|
const names = exposed.map((value) => value?.name).filter((value) => typeof value === "string");
|
|
@@ -45950,7 +46000,7 @@ function describeDestination(chosen) {
|
|
|
45950
46000
|
// two different questions with two different answers, and the type is the
|
|
45951
46001
|
// one an agent does not know it is being asked until a mapping is refused.
|
|
45952
46002
|
...slots.flatMap((slot) => slot.fieldTypes ? [`${slot.slot} field types \u2014 ${slot.fieldTypes}`] : []),
|
|
45953
|
-
'Values (right of the `=`) are the same for every slot: a form field, tracking:<var>, global:<name>, code:<name>, a "literal", or those joined with +.'
|
|
46003
|
+
'Values (right of the `=`) are the same for every slot: a form field, tracking:<var>, global:<name>, code:<name>, a "literal", or those joined with +. A field may also be written as the file stores it, {{<stepId>.customForm.<field>}}. A value cannot run code (no .split(), .slice(), [0]).'
|
|
45954
46004
|
]
|
|
45955
46005
|
});
|
|
45956
46006
|
}
|