@funnelsgrove/cli 0.1.292 → 0.1.294
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 +2 -6
- package/dist/analyticsOutput.d.ts +9 -51
- package/dist/analyticsOutput.js +45 -71
- package/dist/cli.d.ts +1 -11
- package/dist/cli.js +4 -100
- package/funnel-contract-compatibility.json +28 -28
- package/package.json +1 -1
- package/template_docs/.funnelsgrove-docs.json +3 -3
- package/template_docs/docs/funnelsgrove/migrations/step-contract-v3.md +1 -1
- package/template_docs/funnel-docs.config.json +1 -1
- package/template_scaffold/.funnelsgrove-docs.json +3 -3
- package/template_scaffold/.funnelsgrove-scaffold.json +5 -5
- package/template_scaffold/docs/funnelsgrove/migrations/step-contract-v3.md +1 -1
- package/template_scaffold/funnel-docs.config.json +1 -1
package/README.md
CHANGED
|
@@ -174,7 +174,6 @@ Analytics workflow:
|
|
|
174
174
|
fgrove analytics conversions --project claimbee --funnel claimbee-ios --date 2026-06-11
|
|
175
175
|
fgrove analytics funnel-path --project claimbee --funnel claimbee-ios --date 2026-06-11
|
|
176
176
|
fgrove analytics transitions --project claimbee --funnel claimbee-ios --date 2026-06-11
|
|
177
|
-
fgrove analytics versions --project claimbee --funnel claimbee-ios
|
|
178
177
|
fgrove analytics cohort --project claimbee --date 2026-06-11
|
|
179
178
|
```
|
|
180
179
|
|
|
@@ -186,15 +185,12 @@ fgrove analytics conversions --project claimbee --funnel claimbee-ios --date 202
|
|
|
186
185
|
|
|
187
186
|
`conversions` downloads conversion totals, primary conversion metrics, full funnel path rows, and step transitions. `funnel-path` focuses on the ordered path report. `transitions` focuses on step-by-step advanced/drop-off counts. `cohort` downloads synced marketing cohort economics for the day. Its annual predictions use the same saved-range midpoint as Cohort Marketing and daily reports. Missing valid prediction ranges yield null JSON values and empty table cells; legacy model points are never a fallback. Cohort Marketing must be enabled for the project. If the requested period has no synced data, or cohort source data is incomplete, the CLI exits non-zero with a human-readable explanation.
|
|
188
187
|
|
|
189
|
-
`conversions`, `funnel-path`, and `transitions` report one day with `--date`, or a range with `--from` and `--to` (inclusive, totals for the whole range). They
|
|
188
|
+
`conversions`, `funnel-path`, and `transitions` report one day with `--date`, or a range with `--from` and `--to` (inclusive, totals for the whole range). They use the selected date range across all published funnel versions that had traffic. `funnel-path` and `transitions` follow current published flow order. Steps with selected-period traffic that are absent from the current flow appear at the end under “Not in current funnel”; JSON rows mark them with `section: "not-in-current-funnel"`.
|
|
190
189
|
|
|
191
190
|
```bash
|
|
192
|
-
fgrove analytics
|
|
193
|
-
fgrove analytics transitions --project claimbee --funnel claimbee-ios --from 2026-06-01 --to 2026-06-14 --journey-version <journey-version-id>
|
|
191
|
+
fgrove analytics transitions --project claimbee --funnel claimbee-ios --from 2026-06-01 --to 2026-06-14
|
|
194
192
|
```
|
|
195
193
|
|
|
196
|
-
`versions` lists each Journey Version newest first with its id, start time, active days, and visitors on those days. Active days are dates in `--timezone` (the API default, America/Los_Angeles, when omitted); the day a version replaced its predecessor belongs to both, and each version counts only its own visitors. An explicit version reports its own metrics and its own step structure. `--journey-version all` counts every version and orders `funnel-path` by the current published flow, so steps that exist only in older versions appear in `transitions` but not in `funnel-path`. JSON reports include `from`, `to`, and a `journeyVersion` object (`id`, `seq`, `label`, `startedAt`, `isCurrent`); `date` is set only for a one-day report. Table titles end with the reported version, for example `Journey 2 (current)`. The `overview` block of `conversions` always covers all Journey Versions.
|
|
197
|
-
|
|
198
194
|
The package also keeps the longer `funnelsgrove` command as a compatibility alias.
|
|
199
195
|
|
|
200
196
|
Edit profitability expenses through a local JSON file:
|
|
@@ -53,8 +53,8 @@ export type AnalyticsStepDropoffResponse = {
|
|
|
53
53
|
slug: string;
|
|
54
54
|
};
|
|
55
55
|
version: {
|
|
56
|
-
selected:
|
|
57
|
-
options:
|
|
56
|
+
selected: null;
|
|
57
|
+
options: [];
|
|
58
58
|
};
|
|
59
59
|
totals: Record<string, number>;
|
|
60
60
|
metrics: Array<{
|
|
@@ -71,30 +71,6 @@ export type AnalyticsStepDropoffResponse = {
|
|
|
71
71
|
steps: AnalyticsStepTransition[];
|
|
72
72
|
flow: AnalyticsFunnelFlow | null;
|
|
73
73
|
};
|
|
74
|
-
export type AnalyticsJourneyVersionOption = {
|
|
75
|
-
id: string;
|
|
76
|
-
seq: number;
|
|
77
|
-
message: string | null;
|
|
78
|
-
publishedAt: string | null;
|
|
79
|
-
isCurrent: boolean;
|
|
80
|
-
};
|
|
81
|
-
export type AnalyticsReportJourneyVersion = {
|
|
82
|
-
id: string;
|
|
83
|
-
seq: number | null;
|
|
84
|
-
label: string;
|
|
85
|
-
startedAt: string | null;
|
|
86
|
-
isCurrent: boolean;
|
|
87
|
-
};
|
|
88
|
-
export type AnalyticsJourneyVersionRow = {
|
|
89
|
-
id: string;
|
|
90
|
-
seq: number;
|
|
91
|
-
label: string;
|
|
92
|
-
isCurrent: boolean;
|
|
93
|
-
startedAt: string | null;
|
|
94
|
-
activeFrom: string | null;
|
|
95
|
-
activeTo: string | null;
|
|
96
|
-
visitors: number | null;
|
|
97
|
-
};
|
|
98
74
|
export type AnalyticsStepTransition = {
|
|
99
75
|
stepId: string;
|
|
100
76
|
stepName: string | null;
|
|
@@ -171,7 +147,6 @@ type ReportContext = {
|
|
|
171
147
|
};
|
|
172
148
|
type FunnelReportContext = {
|
|
173
149
|
period: AnalyticsPeriod;
|
|
174
|
-
journeyVersion: string;
|
|
175
150
|
workspaceId: string;
|
|
176
151
|
project: AnalyticsProjectSummary;
|
|
177
152
|
stepDropoff: AnalyticsStepDropoffResponse;
|
|
@@ -199,6 +174,7 @@ export declare const assertHasCohortData: (date: string, report: MarketingCohort
|
|
|
199
174
|
export declare const buildTransitionRows: (stepDropoff: AnalyticsStepDropoffResponse) => {
|
|
200
175
|
stepId: string;
|
|
201
176
|
step: string;
|
|
177
|
+
section: string | null;
|
|
202
178
|
nextStep: string;
|
|
203
179
|
entrants: number;
|
|
204
180
|
advancedToNextStep: number;
|
|
@@ -209,13 +185,13 @@ export declare const buildFunnelPathRows: (stepDropoff: AnalyticsStepDropoffResp
|
|
|
209
185
|
stepId: string;
|
|
210
186
|
path: string;
|
|
211
187
|
label: string;
|
|
188
|
+
section: string;
|
|
212
189
|
users: number;
|
|
213
190
|
advancedToNextStep: number;
|
|
214
191
|
conversionToNextStep: number;
|
|
215
192
|
dropoffs: number;
|
|
216
193
|
dropoffRate: number;
|
|
217
194
|
}[];
|
|
218
|
-
export declare const journeyVersionLabel: (version: Pick<AnalyticsJourneyVersionOption, "seq">) => string;
|
|
219
195
|
export declare const buildConversionReportPayload: (input: FunnelReportContext & {
|
|
220
196
|
overview: AnalyticsOverviewResponse;
|
|
221
197
|
pathFlow: AnalyticsFunnelFlow | null;
|
|
@@ -237,6 +213,7 @@ export declare const buildConversionReportPayload: (input: FunnelReportContext &
|
|
|
237
213
|
stepId: string;
|
|
238
214
|
path: string;
|
|
239
215
|
label: string;
|
|
216
|
+
section: string;
|
|
240
217
|
users: number;
|
|
241
218
|
advancedToNextStep: number;
|
|
242
219
|
conversionToNextStep: number;
|
|
@@ -246,6 +223,7 @@ export declare const buildConversionReportPayload: (input: FunnelReportContext &
|
|
|
246
223
|
transitions: {
|
|
247
224
|
stepId: string;
|
|
248
225
|
step: string;
|
|
226
|
+
section: string | null;
|
|
249
227
|
nextStep: string;
|
|
250
228
|
entrants: number;
|
|
251
229
|
advancedToNextStep: number;
|
|
@@ -263,7 +241,6 @@ export declare const buildConversionReportPayload: (input: FunnelReportContext &
|
|
|
263
241
|
name: string;
|
|
264
242
|
slug: string;
|
|
265
243
|
};
|
|
266
|
-
journeyVersion: AnalyticsReportJourneyVersion | null;
|
|
267
244
|
};
|
|
268
245
|
export declare const buildFunnelPathReportPayload: (input: FunnelReportContext & {
|
|
269
246
|
pathFlow: AnalyticsFunnelFlow | null;
|
|
@@ -272,6 +249,7 @@ export declare const buildFunnelPathReportPayload: (input: FunnelReportContext &
|
|
|
272
249
|
stepId: string;
|
|
273
250
|
path: string;
|
|
274
251
|
label: string;
|
|
252
|
+
section: string;
|
|
275
253
|
users: number;
|
|
276
254
|
advancedToNextStep: number;
|
|
277
255
|
conversionToNextStep: number;
|
|
@@ -289,12 +267,12 @@ export declare const buildFunnelPathReportPayload: (input: FunnelReportContext &
|
|
|
289
267
|
name: string;
|
|
290
268
|
slug: string;
|
|
291
269
|
};
|
|
292
|
-
journeyVersion: AnalyticsReportJourneyVersion | null;
|
|
293
270
|
};
|
|
294
271
|
export declare const buildTransitionReportPayload: (input: FunnelReportContext) => {
|
|
295
272
|
transitions: {
|
|
296
273
|
stepId: string;
|
|
297
274
|
step: string;
|
|
275
|
+
section: string | null;
|
|
298
276
|
nextStep: string;
|
|
299
277
|
entrants: number;
|
|
300
278
|
advancedToNextStep: number;
|
|
@@ -312,7 +290,6 @@ export declare const buildTransitionReportPayload: (input: FunnelReportContext)
|
|
|
312
290
|
name: string;
|
|
313
291
|
slug: string;
|
|
314
292
|
};
|
|
315
|
-
journeyVersion: AnalyticsReportJourneyVersion | null;
|
|
316
293
|
};
|
|
317
294
|
export declare const buildCohortReportPayload: (input: ReportContext & {
|
|
318
295
|
cohort: MarketingCohortPerformanceReport;
|
|
@@ -328,7 +305,7 @@ export declare const buildCohortReportPayload: (input: ReportContext & {
|
|
|
328
305
|
} | null;
|
|
329
306
|
};
|
|
330
307
|
export declare const formatAnalyticsJson: (payload: unknown) => string;
|
|
331
|
-
type FunnelReportTableInput = Pick<FunnelReportContext, 'period' | '
|
|
308
|
+
type FunnelReportTableInput = Pick<FunnelReportContext, 'period' | 'stepDropoff'>;
|
|
332
309
|
export declare const formatTransitionsTable: (input: FunnelReportTableInput) => string;
|
|
333
310
|
export declare const formatFunnelPathTable: (input: FunnelReportTableInput & {
|
|
334
311
|
pathFlow: AnalyticsFunnelFlow | null;
|
|
@@ -337,25 +314,6 @@ export declare const formatConversionsTable: (input: FunnelReportTableInput & {
|
|
|
337
314
|
overview: AnalyticsOverviewResponse;
|
|
338
315
|
pathFlow: AnalyticsFunnelFlow | null;
|
|
339
316
|
}) => string;
|
|
340
|
-
type JourneyVersionsReport = {
|
|
341
|
-
funnel: AnalyticsStepDropoffResponse['funnel'];
|
|
342
|
-
versions: AnalyticsJourneyVersionRow[];
|
|
343
|
-
};
|
|
344
|
-
export declare const buildJourneyVersionsReportPayload: (input: JourneyVersionsReport & {
|
|
345
|
-
workspaceId: string;
|
|
346
|
-
project: AnalyticsProjectSummary;
|
|
347
|
-
}) => {
|
|
348
|
-
generatedAt: string;
|
|
349
|
-
workspaceId: string;
|
|
350
|
-
project: AnalyticsProjectSummary;
|
|
351
|
-
funnel: {
|
|
352
|
-
id: string;
|
|
353
|
-
name: string;
|
|
354
|
-
slug: string;
|
|
355
|
-
};
|
|
356
|
-
versions: AnalyticsJourneyVersionRow[];
|
|
357
|
-
};
|
|
358
|
-
export declare const formatJourneyVersionsTable: (input: JourneyVersionsReport) => string;
|
|
359
317
|
export declare const formatCohortTable: (input: {
|
|
360
318
|
date: string;
|
|
361
319
|
cohort: MarketingCohortPerformanceReport;
|
package/dist/analyticsOutput.js
CHANGED
|
@@ -66,9 +66,6 @@ const hasPositiveNumber = (value) => {
|
|
|
66
66
|
}
|
|
67
67
|
return false;
|
|
68
68
|
};
|
|
69
|
-
const olderJourneyVersionHint = (stepDropoff) => (stepDropoff.version.selected?.isCurrent && stepDropoff.version.options.length > 1
|
|
70
|
-
? ' Traffic on these dates may belong to an older journey version: list them with `fgrove analytics versions` and pass --journey-version.'
|
|
71
|
-
: '');
|
|
72
69
|
export const assertHasConversionData = (date, overview, stepDropoff) => {
|
|
73
70
|
if (hasPositiveNumber(overview.totals) ||
|
|
74
71
|
hasPositiveNumber(overview.byDay) ||
|
|
@@ -76,19 +73,19 @@ export const assertHasConversionData = (date, overview, stepDropoff) => {
|
|
|
76
73
|
hasPositiveNumber(stepDropoff.steps)) {
|
|
77
74
|
return;
|
|
78
75
|
}
|
|
79
|
-
throw new Error(`No synced conversion data found for ${date}. Analytics may not have synced yet, or the funnel had no tracked traffic in that period
|
|
76
|
+
throw new Error(`No synced conversion data found for ${date}. Analytics may not have synced yet, or the funnel had no tracked traffic in that period.`);
|
|
80
77
|
};
|
|
81
78
|
export const assertHasTransitionData = (date, stepDropoff) => {
|
|
82
79
|
if (stepDropoff.steps.length > 0 && hasPositiveNumber(stepDropoff.steps)) {
|
|
83
80
|
return;
|
|
84
81
|
}
|
|
85
|
-
throw new Error(`No synced step transition data found for ${date}. Analytics may not have synced yet, or the funnel had no tracked step traffic in that period
|
|
82
|
+
throw new Error(`No synced step transition data found for ${date}. Analytics may not have synced yet, or the funnel had no tracked step traffic in that period.`);
|
|
86
83
|
};
|
|
87
84
|
export const assertHasFunnelPathData = (date, stepDropoff, pathFlow) => {
|
|
88
85
|
if (buildFunnelPathRows(stepDropoff, pathFlow).length > 0) {
|
|
89
86
|
return;
|
|
90
87
|
}
|
|
91
|
-
throw new Error(`No synced full funnel path data found for ${date}. Analytics may not have synced yet, or the funnel had no tracked path traffic in that period
|
|
88
|
+
throw new Error(`No synced full funnel path data found for ${date}. Analytics may not have synced yet, or the funnel had no tracked path traffic in that period.`);
|
|
92
89
|
};
|
|
93
90
|
export const assertHasCohortData = (date, report) => {
|
|
94
91
|
if (report.sourceHealth && !report.sourceHealth.complete) {
|
|
@@ -114,31 +111,45 @@ const formatCurrency = (value, currency = 'USD') => new Intl.NumberFormat('en-US
|
|
|
114
111
|
}).format(value);
|
|
115
112
|
const formatMinorCurrency = (value) => formatCurrency((Number(value) || 0) / 100);
|
|
116
113
|
const stepDisplayName = (step) => step.stepName || step.stepId;
|
|
117
|
-
export const buildTransitionRows = (stepDropoff) =>
|
|
118
|
-
|
|
119
|
-
.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
114
|
+
export const buildTransitionRows = (stepDropoff) => {
|
|
115
|
+
const currentOrder = new Map(stepDropoff.flow?.steps.map(step => [step.stepId, step.stepOrder]));
|
|
116
|
+
return stepDropoff.steps
|
|
117
|
+
.filter(step => step.entrants > 0)
|
|
118
|
+
.sort((left, right) => ((currentOrder.get(left.stepId) ?? Number.MAX_SAFE_INTEGER)
|
|
119
|
+
- (currentOrder.get(right.stepId) ?? Number.MAX_SAFE_INTEGER)
|
|
120
|
+
|| left.stepOrder - right.stepOrder
|
|
121
|
+
|| left.stepId.localeCompare(right.stepId)))
|
|
122
|
+
.map(step => ({
|
|
123
|
+
stepId: step.stepId,
|
|
124
|
+
step: stepDisplayName(step),
|
|
125
|
+
section: !stepDropoff.flow ? null : currentOrder.has(step.stepId) ? 'current' : 'not-in-current-funnel',
|
|
126
|
+
nextStep: step.nextStepId || '',
|
|
127
|
+
entrants: step.entrants,
|
|
128
|
+
advancedToNextStep: step.advancedToNextStep,
|
|
129
|
+
dropoffs: step.dropoffs,
|
|
130
|
+
dropoffRate: step.dropoffRate,
|
|
131
|
+
}));
|
|
132
|
+
};
|
|
128
133
|
export const buildFunnelPathRows = (stepDropoff, pathFlow) => {
|
|
129
|
-
if (!pathFlow)
|
|
134
|
+
if (!pathFlow)
|
|
130
135
|
return [];
|
|
131
|
-
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
.
|
|
135
|
-
.
|
|
136
|
+
const transitionsByStepId = new Map(stepDropoff.steps.map(step => [step.stepId, step]));
|
|
137
|
+
const currentStepIds = new Set(pathFlow.steps.map(step => step.stepId));
|
|
138
|
+
const removedSteps = stepDropoff.steps
|
|
139
|
+
.filter(step => step.entrants > 0 && !currentStepIds.has(step.stepId))
|
|
140
|
+
.sort((left, right) => left.stepOrder - right.stepOrder || left.stepId.localeCompare(right.stepId));
|
|
141
|
+
const orderedSteps = [
|
|
142
|
+
...[...pathFlow.steps].sort((left, right) => left.stepOrder - right.stepOrder || left.stepId.localeCompare(right.stepId)),
|
|
143
|
+
...removedSteps.map(step => ({ ...step, stepTitle: null, stepPath: null })),
|
|
144
|
+
];
|
|
145
|
+
return orderedSteps.map(flowStep => {
|
|
136
146
|
const step = transitionsByStepId.get(flowStep.stepId);
|
|
137
147
|
const entrants = step?.entrants ?? 0;
|
|
138
148
|
return {
|
|
139
149
|
stepId: flowStep.stepId,
|
|
140
150
|
path: flowStep.stepPath || flowStep.stepId,
|
|
141
151
|
label: flowStep.stepTitle || flowStep.stepName || flowStep.stepId,
|
|
152
|
+
section: currentStepIds.has(flowStep.stepId) ? 'current' : 'not-in-current-funnel',
|
|
142
153
|
users: entrants,
|
|
143
154
|
advancedToNextStep: step?.advancedToNextStep ?? 0,
|
|
144
155
|
conversionToNextStep: entrants > 0 ? (step?.advancedToNextStep ?? 0) / entrants : 0,
|
|
@@ -147,22 +158,6 @@ export const buildFunnelPathRows = (stepDropoff, pathFlow) => {
|
|
|
147
158
|
};
|
|
148
159
|
});
|
|
149
160
|
};
|
|
150
|
-
export const journeyVersionLabel = (version) => `Journey ${version.seq}`;
|
|
151
|
-
const describeReportJourneyVersion = (input) => {
|
|
152
|
-
if (input.journeyVersion === 'all') {
|
|
153
|
-
return { id: 'all', seq: null, label: 'All journey versions', startedAt: null, isCurrent: false };
|
|
154
|
-
}
|
|
155
|
-
const selected = input.stepDropoff.version.selected;
|
|
156
|
-
return selected
|
|
157
|
-
? {
|
|
158
|
-
id: selected.id,
|
|
159
|
-
seq: selected.seq,
|
|
160
|
-
label: journeyVersionLabel(selected),
|
|
161
|
-
startedAt: selected.publishedAt,
|
|
162
|
-
isCurrent: selected.isCurrent,
|
|
163
|
-
}
|
|
164
|
-
: null;
|
|
165
|
-
};
|
|
166
161
|
const buildFunnelReportHeader = (input) => ({
|
|
167
162
|
generatedAt: new Date().toISOString(),
|
|
168
163
|
date: input.period.date,
|
|
@@ -171,16 +166,9 @@ const buildFunnelReportHeader = (input) => ({
|
|
|
171
166
|
workspaceId: input.workspaceId,
|
|
172
167
|
project: input.project,
|
|
173
168
|
funnel: input.stepDropoff.funnel,
|
|
174
|
-
journeyVersion: describeReportJourneyVersion(input),
|
|
175
169
|
});
|
|
176
170
|
const formatFunnelReportTitle = (title, input) => {
|
|
177
|
-
|
|
178
|
-
return [
|
|
179
|
-
title,
|
|
180
|
-
input.period.label,
|
|
181
|
-
input.stepDropoff.funnel.name,
|
|
182
|
-
...(version ? [version.isCurrent ? `${version.label} (current)` : version.label] : []),
|
|
183
|
-
].join('\t');
|
|
171
|
+
return [title, input.period.label, input.stepDropoff.funnel.name].join('\t');
|
|
184
172
|
};
|
|
185
173
|
export const buildConversionReportPayload = (input) => ({
|
|
186
174
|
...buildFunnelReportHeader(input),
|
|
@@ -212,7 +200,12 @@ export const formatTransitionsTable = (input) => {
|
|
|
212
200
|
formatFunnelReportTitle('Step transitions', input),
|
|
213
201
|
'step\tnextStep\tentrants\tadvanced\tdropoffs\tdropoffRate',
|
|
214
202
|
];
|
|
203
|
+
let removedSectionStarted = false;
|
|
215
204
|
for (const row of buildTransitionRows(input.stepDropoff)) {
|
|
205
|
+
if (row.section === 'not-in-current-funnel' && !removedSectionStarted) {
|
|
206
|
+
lines.push('Not in current funnel');
|
|
207
|
+
removedSectionStarted = true;
|
|
208
|
+
}
|
|
216
209
|
lines.push([
|
|
217
210
|
row.step,
|
|
218
211
|
row.nextStep,
|
|
@@ -229,7 +222,12 @@ export const formatFunnelPathTable = (input) => {
|
|
|
229
222
|
formatFunnelReportTitle('Full funnel path', input),
|
|
230
223
|
'step\tpath\tusers\tadvanced\tconversionToNext\tdropoffs\tdropoffRate',
|
|
231
224
|
];
|
|
225
|
+
let removedSectionStarted = false;
|
|
232
226
|
for (const row of buildFunnelPathRows(input.stepDropoff, input.pathFlow)) {
|
|
227
|
+
if (row.section === 'not-in-current-funnel' && !removedSectionStarted) {
|
|
228
|
+
lines.push('Not in current funnel');
|
|
229
|
+
removedSectionStarted = true;
|
|
230
|
+
}
|
|
233
231
|
lines.push([
|
|
234
232
|
row.label,
|
|
235
233
|
row.path,
|
|
@@ -272,30 +270,6 @@ export const formatConversionsTable = (input) => {
|
|
|
272
270
|
lines.push('', formatFunnelPathTable(input).trimEnd());
|
|
273
271
|
return `${lines.join('\n')}\n`;
|
|
274
272
|
};
|
|
275
|
-
export const buildJourneyVersionsReportPayload = (input) => ({
|
|
276
|
-
generatedAt: new Date().toISOString(),
|
|
277
|
-
workspaceId: input.workspaceId,
|
|
278
|
-
project: input.project,
|
|
279
|
-
funnel: input.funnel,
|
|
280
|
-
versions: input.versions,
|
|
281
|
-
});
|
|
282
|
-
export const formatJourneyVersionsTable = (input) => {
|
|
283
|
-
const lines = [
|
|
284
|
-
`Journey versions\t${input.funnel.name}`,
|
|
285
|
-
'version\tid\tstartedAt\tactiveFrom\tactiveTo\tvisitors',
|
|
286
|
-
];
|
|
287
|
-
for (const version of input.versions) {
|
|
288
|
-
lines.push([
|
|
289
|
-
version.isCurrent ? `${version.label} (current)` : version.label,
|
|
290
|
-
version.id,
|
|
291
|
-
version.startedAt || '',
|
|
292
|
-
version.activeFrom || '',
|
|
293
|
-
version.activeTo || '',
|
|
294
|
-
version.visitors === null ? '' : formatNumber(version.visitors),
|
|
295
|
-
].join('\t'));
|
|
296
|
-
}
|
|
297
|
-
return `${lines.join('\n')}\n`;
|
|
298
|
-
};
|
|
299
273
|
const flattenCohortRows = (rows) => rows.flatMap((row) => [row, ...(row.segments || []), ...(row.children || [])]);
|
|
300
274
|
const normalizeCurrency = (value) => {
|
|
301
275
|
if (typeof value !== 'string') {
|
package/dist/cli.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { type DocsExitCode } from './docsOutput.js';
|
|
|
8
8
|
import { type DocsDiagnostic, type DocsStatus } from './docsTransaction.js';
|
|
9
9
|
import { type FunnelValidationExitCode, type FunnelValidationResult } from './diagnosticOutput.js';
|
|
10
10
|
import { type StepContractMigrationApplyResult, type StepContractMigrationCheckResult, type StepContractMigrationTargetVersion, type StepContractMigrationValidationResult } from './stepContractMigration.js';
|
|
11
|
-
import { type AnalyticsFunnelFlow, type
|
|
11
|
+
import { type AnalyticsFunnelFlow, type AnalyticsPeriod, type AnalyticsOutputFormat, type AnalyticsStepDropoffResponse } from './analyticsOutput.js';
|
|
12
12
|
export declare const CANONICAL_CREATE_TEMPLATE_SLUG = "template";
|
|
13
13
|
export declare const SCAFFOLD_TRANSIENT_PATHS: readonly ["node_modules", ".next", "out", "tsconfig.tsbuildinfo", ".funnelsgrove-scaffold.json"];
|
|
14
14
|
export declare class CliContractError extends Error {
|
|
@@ -149,7 +149,6 @@ type FunnelAnalyticsCommandInput = {
|
|
|
149
149
|
};
|
|
150
150
|
format: AnalyticsOutputFormat;
|
|
151
151
|
timezone?: string;
|
|
152
|
-
journeyVersion: string;
|
|
153
152
|
};
|
|
154
153
|
type SyncTargetIdInput = {
|
|
155
154
|
explicitWorkspaceId?: string;
|
|
@@ -174,7 +173,6 @@ export declare function buildFunnelAnalyticsCommandInput(options: {
|
|
|
174
173
|
date?: string;
|
|
175
174
|
from?: string;
|
|
176
175
|
to?: string;
|
|
177
|
-
journeyVersion?: string;
|
|
178
176
|
format?: string;
|
|
179
177
|
timezone?: string;
|
|
180
178
|
}): FunnelAnalyticsCommandInput;
|
|
@@ -312,12 +310,4 @@ export declare const fetchFunnelPathAnalytics: (input: FunnelPathAnalyticsInput,
|
|
|
312
310
|
stepDropoff: AnalyticsStepDropoffResponse;
|
|
313
311
|
pathFlow: AnalyticsFunnelFlow | null;
|
|
314
312
|
}>;
|
|
315
|
-
type JourneyVersionsInput = Pick<FunnelPathAnalyticsInput, 'token' | 'workspaceId' | 'projectId' | 'funnelId'> & {
|
|
316
|
-
timezone?: string;
|
|
317
|
-
today: string;
|
|
318
|
-
};
|
|
319
|
-
export declare const fetchJourneyVersions: (input: JourneyVersionsInput, apiCall?: AnalyticsApiCall) => Promise<{
|
|
320
|
-
funnel: AnalyticsStepDropoffResponse["funnel"];
|
|
321
|
-
versions: AnalyticsJourneyVersionRow[];
|
|
322
|
-
}>;
|
|
323
313
|
export {};
|
package/dist/cli.js
CHANGED
|
@@ -32,7 +32,7 @@ import { applyTemplateDocsUpdate, inspectTemplateDocs, loadBundledDocs, planTemp
|
|
|
32
32
|
import { renderValidationHuman, renderValidationJson, } from './diagnosticOutput.js';
|
|
33
33
|
import { validateFunnel } from './funnelValidation.js';
|
|
34
34
|
import { StepContractMigrationError, applyStepContractV2Migration, checkStepContractV2Migration, recoverStepContractV2Migration, resolveStepContractMigrationMode, stepContractMigrationReportPath, } from './stepContractMigration.js';
|
|
35
|
-
import { assertHasCohortData, assertHasConversionData, assertHasFunnelPathData, assertHasTransitionData, buildCohortReportPayload, buildConversionReportPayload, buildFunnelPathReportPayload,
|
|
35
|
+
import { assertHasCohortData, assertHasConversionData, assertHasFunnelPathData, assertHasTransitionData, buildCohortReportPayload, buildConversionReportPayload, buildFunnelPathReportPayload, buildTransitionReportPayload, formatAnalyticsJson, formatCohortTable, formatConversionsTable, formatFunnelPathTable, formatTransitionsTable, normalizeAnalyticsOutputFormat, parseAnalyticsDate, parseAnalyticsPeriod, } from './analyticsOutput.js';
|
|
36
36
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
37
37
|
const toKebabCase = (value) => value
|
|
38
38
|
.trim()
|
|
@@ -1118,19 +1118,11 @@ export function buildAnalyticsCommandInput(options) {
|
|
|
1118
1118
|
timezone,
|
|
1119
1119
|
};
|
|
1120
1120
|
}
|
|
1121
|
-
const parseAnalyticsJourneyVersion = (value) => {
|
|
1122
|
-
const normalized = (value || 'current').trim().toLowerCase();
|
|
1123
|
-
if (normalized === 'current' || normalized === 'all' || UUID_PATTERN.test(normalized)) {
|
|
1124
|
-
return normalized;
|
|
1125
|
-
}
|
|
1126
|
-
throw new Error('--journey-version must be current, all, or a journey version id from `fgrove analytics versions`.');
|
|
1127
|
-
};
|
|
1128
1121
|
export function buildFunnelAnalyticsCommandInput(options) {
|
|
1129
1122
|
return {
|
|
1130
1123
|
period: parseAnalyticsPeriod(options),
|
|
1131
1124
|
format: normalizeAnalyticsOutputFormat(options.format),
|
|
1132
1125
|
timezone: options.timezone?.trim() || undefined,
|
|
1133
|
-
journeyVersion: parseAnalyticsJourneyVersion(options.journeyVersion),
|
|
1134
1126
|
};
|
|
1135
1127
|
}
|
|
1136
1128
|
export function buildPatchSourceInput(input) {
|
|
@@ -2335,7 +2327,6 @@ const analyticsCommand = addExamples(program.command('analytics').description('D
|
|
|
2335
2327
|
'fgrove analytics conversions --project claimbee --funnel claimbee-ios --date 2026-06-11 --format json --out analytics.json',
|
|
2336
2328
|
'fgrove analytics funnel-path --project claimbee --funnel claimbee-ios --date 2026-06-11',
|
|
2337
2329
|
'fgrove analytics transitions --project claimbee --funnel claimbee-ios --date 2026-06-11',
|
|
2338
|
-
'fgrove analytics versions --project claimbee --funnel claimbee-ios',
|
|
2339
2330
|
'fgrove analytics cohort --project claimbee --date 2026-06-11 --format json',
|
|
2340
2331
|
]);
|
|
2341
2332
|
const resolveFunnelAnalyticsTarget = async (options) => {
|
|
@@ -2389,7 +2380,6 @@ const fetchStepDropoffAnalytics = async (input, apiCall = callApi) => {
|
|
|
2389
2380
|
workspaceId: input.workspaceId,
|
|
2390
2381
|
projectId: input.projectId,
|
|
2391
2382
|
funnelId: input.funnelId,
|
|
2392
|
-
...(input.commandInput.journeyVersion === 'current' ? {} : { versionId: input.commandInput.journeyVersion }),
|
|
2393
2383
|
from: input.commandInput.period.fromIso,
|
|
2394
2384
|
to: input.commandInput.period.toIso,
|
|
2395
2385
|
timezone: input.commandInput.timezone,
|
|
@@ -2409,13 +2399,6 @@ const fetchPublishedFunnelFlow = async (input, apiCall = callApi) => {
|
|
|
2409
2399
|
});
|
|
2410
2400
|
};
|
|
2411
2401
|
export const fetchFunnelPathAnalytics = async (input, apiCall = callApi) => {
|
|
2412
|
-
const { journeyVersion } = input.commandInput;
|
|
2413
|
-
if (journeyVersion !== 'current' && journeyVersion !== 'all') {
|
|
2414
|
-
// An explicit Journey Version reports its own structure and is never
|
|
2415
|
-
// paired with the current published path.
|
|
2416
|
-
const stepDropoff = await fetchStepDropoffAnalytics(input, apiCall);
|
|
2417
|
-
return { stepDropoff, pathFlow: stepDropoff.flow };
|
|
2418
|
-
}
|
|
2419
2402
|
const [stepDropoff, publishedFunnelFlow] = await Promise.all([
|
|
2420
2403
|
fetchStepDropoffAnalytics(input, apiCall),
|
|
2421
2404
|
fetchPublishedFunnelFlow(input, apiCall),
|
|
@@ -2425,46 +2408,6 @@ export const fetchFunnelPathAnalytics = async (input, apiCall = callApi) => {
|
|
|
2425
2408
|
pathFlow: publishedFunnelFlow.status === 'ready' ? publishedFunnelFlow.flow : null,
|
|
2426
2409
|
};
|
|
2427
2410
|
};
|
|
2428
|
-
// The API reads analytics dates in this timezone when a request names none.
|
|
2429
|
-
const DEFAULT_ANALYTICS_TIMEZONE = 'America/Los_Angeles';
|
|
2430
|
-
const toAnalyticsDate = (value, timezone) => new Intl.DateTimeFormat('en-CA', { timeZone: timezone || DEFAULT_ANALYTICS_TIMEZONE }).format(value);
|
|
2431
|
-
export const fetchJourneyVersions = async (input, apiCall = callApi) => {
|
|
2432
|
-
const { today, timezone, ...target } = input;
|
|
2433
|
-
const current = await fetchStepDropoffAnalytics({
|
|
2434
|
-
...target,
|
|
2435
|
-
commandInput: buildFunnelAnalyticsCommandInput({ date: today, timezone }),
|
|
2436
|
-
}, apiCall);
|
|
2437
|
-
const newestFirst = [...current.version.options].sort((left, right) => right.seq - left.seq);
|
|
2438
|
-
// A version is active from its start day until the next version starts;
|
|
2439
|
-
// version-scoped counts make the shared switch day safe to include in both.
|
|
2440
|
-
const startDate = (startedAt) => (startedAt ? toAnalyticsDate(new Date(startedAt), timezone) : null);
|
|
2441
|
-
const versions = await Promise.all(newestFirst.map(async (version, index) => {
|
|
2442
|
-
const activeFrom = startDate(version.publishedAt);
|
|
2443
|
-
const activeTo = index === 0 ? null : startDate(newestFirst[index - 1].publishedAt);
|
|
2444
|
-
const report = activeFrom
|
|
2445
|
-
? await fetchStepDropoffAnalytics({
|
|
2446
|
-
...target,
|
|
2447
|
-
commandInput: buildFunnelAnalyticsCommandInput({
|
|
2448
|
-
from: activeFrom,
|
|
2449
|
-
to: activeTo ?? today,
|
|
2450
|
-
journeyVersion: version.id,
|
|
2451
|
-
timezone,
|
|
2452
|
-
}),
|
|
2453
|
-
}, apiCall)
|
|
2454
|
-
: null;
|
|
2455
|
-
return {
|
|
2456
|
-
id: version.id,
|
|
2457
|
-
seq: version.seq,
|
|
2458
|
-
label: journeyVersionLabel(version),
|
|
2459
|
-
isCurrent: version.isCurrent,
|
|
2460
|
-
startedAt: version.publishedAt,
|
|
2461
|
-
activeFrom,
|
|
2462
|
-
activeTo,
|
|
2463
|
-
visitors: report ? report.totals.visitors ?? 0 : null,
|
|
2464
|
-
};
|
|
2465
|
-
}));
|
|
2466
|
-
return { funnel: current.funnel, versions };
|
|
2467
|
-
};
|
|
2468
2411
|
const addFunnelReportOptions = (command) => command
|
|
2469
2412
|
.option('--workspace <id-or-slug-or-name>', 'Workspace id, slug, or name')
|
|
2470
2413
|
.option('--project <id-or-slug-or-name>', 'Project id, slug, or name')
|
|
@@ -2472,7 +2415,6 @@ const addFunnelReportOptions = (command) => command
|
|
|
2472
2415
|
.option('--date <yyyy-mm-dd>', 'Analytics date; use instead of --from/--to for one day')
|
|
2473
2416
|
.option('--from <yyyy-mm-dd>', 'First analytics date of a range, used with --to')
|
|
2474
2417
|
.option('--to <yyyy-mm-dd>', 'Last analytics date of a range, used with --from')
|
|
2475
|
-
.option('--journey-version <id|current|all>', 'Journey version: current, all, or an id from `fgrove analytics versions`', 'current')
|
|
2476
2418
|
.option('--format <table-or-json>', 'Output format: table or json', 'table')
|
|
2477
2419
|
.option('--out <path>', 'Write output to a file')
|
|
2478
2420
|
.option('--timezone <iana-timezone>', 'Analytics timezone, for example America/Los_Angeles');
|
|
@@ -2481,7 +2423,7 @@ addExamples(addFunnelReportOptions(analyticsCommand
|
|
|
2481
2423
|
.description('Download conversion data, full funnel path, and step transitions for a day or date range')), [
|
|
2482
2424
|
'fgrove analytics conversions --project claimbee --funnel claimbee-ios --date 2026-06-11',
|
|
2483
2425
|
'fgrove analytics conversions --project claimbee --funnel claimbee-ios --date 2026-06-11 --format json --out analytics.json',
|
|
2484
|
-
'fgrove analytics conversions --project claimbee --funnel claimbee-ios --from 2026-06-01 --to 2026-06-14
|
|
2426
|
+
'fgrove analytics conversions --project claimbee --funnel claimbee-ios --from 2026-06-01 --to 2026-06-14',
|
|
2485
2427
|
])
|
|
2486
2428
|
.action(async (options) => {
|
|
2487
2429
|
const context = await loadFunnelAnalyticsContext(options);
|
|
@@ -2512,7 +2454,6 @@ addExamples(addFunnelReportOptions(analyticsCommand
|
|
|
2512
2454
|
assertHasConversionData(context.commandInput.period.label, overview, stepDropoff);
|
|
2513
2455
|
const report = {
|
|
2514
2456
|
period: context.commandInput.period,
|
|
2515
|
-
journeyVersion: context.commandInput.journeyVersion,
|
|
2516
2457
|
stepDropoff,
|
|
2517
2458
|
overview,
|
|
2518
2459
|
pathFlow,
|
|
@@ -2533,7 +2474,7 @@ addExamples(addFunnelReportOptions(analyticsCommand
|
|
|
2533
2474
|
.description('Download the full funnel path report for a day or date range')), [
|
|
2534
2475
|
'fgrove analytics funnel-path --project claimbee --funnel claimbee-ios --date 2026-06-11',
|
|
2535
2476
|
'fgrove analytics funnel-path --project claimbee --funnel claimbee-ios --date 2026-06-11 --format json',
|
|
2536
|
-
'fgrove analytics funnel-path --project claimbee --funnel claimbee-ios --from 2026-06-01 --to 2026-06-14
|
|
2477
|
+
'fgrove analytics funnel-path --project claimbee --funnel claimbee-ios --from 2026-06-01 --to 2026-06-14',
|
|
2537
2478
|
])
|
|
2538
2479
|
.action(async (options) => {
|
|
2539
2480
|
const context = await loadFunnelAnalyticsContext(options);
|
|
@@ -2547,7 +2488,6 @@ addExamples(addFunnelReportOptions(analyticsCommand
|
|
|
2547
2488
|
assertHasFunnelPathData(context.commandInput.period.label, stepDropoff, pathFlow);
|
|
2548
2489
|
const report = {
|
|
2549
2490
|
period: context.commandInput.period,
|
|
2550
|
-
journeyVersion: context.commandInput.journeyVersion,
|
|
2551
2491
|
stepDropoff,
|
|
2552
2492
|
pathFlow,
|
|
2553
2493
|
};
|
|
@@ -2567,7 +2507,7 @@ addExamples(addFunnelReportOptions(analyticsCommand
|
|
|
2567
2507
|
.description('Download step transitions and drop-offs for a day or date range')), [
|
|
2568
2508
|
'fgrove analytics transitions --project claimbee --funnel claimbee-ios --date 2026-06-11',
|
|
2569
2509
|
'fgrove analytics transitions --project claimbee --funnel claimbee-ios --date 2026-06-11 --format json',
|
|
2570
|
-
'fgrove analytics transitions --project claimbee --funnel claimbee-ios --from 2026-06-01 --to 2026-06-14
|
|
2510
|
+
'fgrove analytics transitions --project claimbee --funnel claimbee-ios --from 2026-06-01 --to 2026-06-14',
|
|
2571
2511
|
])
|
|
2572
2512
|
.action(async (options) => {
|
|
2573
2513
|
const context = await loadFunnelAnalyticsContext(options);
|
|
@@ -2581,7 +2521,6 @@ addExamples(addFunnelReportOptions(analyticsCommand
|
|
|
2581
2521
|
assertHasTransitionData(context.commandInput.period.label, stepDropoff);
|
|
2582
2522
|
const report = {
|
|
2583
2523
|
period: context.commandInput.period,
|
|
2584
|
-
journeyVersion: context.commandInput.journeyVersion,
|
|
2585
2524
|
stepDropoff,
|
|
2586
2525
|
};
|
|
2587
2526
|
await writeOrPrintAnalyticsOutput({
|
|
@@ -2595,41 +2534,6 @@ addExamples(addFunnelReportOptions(analyticsCommand
|
|
|
2595
2534
|
out: options.out,
|
|
2596
2535
|
});
|
|
2597
2536
|
});
|
|
2598
|
-
addExamples(analyticsCommand
|
|
2599
|
-
.command('versions')
|
|
2600
|
-
.description('List journey versions with their active days and visitors')
|
|
2601
|
-
.option('--workspace <id-or-slug-or-name>', 'Workspace id, slug, or name')
|
|
2602
|
-
.option('--project <id-or-slug-or-name>', 'Project id, slug, or name')
|
|
2603
|
-
.option('--funnel <id-or-slug-or-name>', 'Funnel id, slug, or name')
|
|
2604
|
-
.option('--format <table-or-json>', 'Output format: table or json', 'table')
|
|
2605
|
-
.option('--out <path>', 'Write output to a file')
|
|
2606
|
-
.option('--timezone <iana-timezone>', 'Analytics timezone, for example America/Los_Angeles'), [
|
|
2607
|
-
'fgrove analytics versions --project claimbee --funnel claimbee-ios',
|
|
2608
|
-
'fgrove analytics versions --project claimbee --funnel claimbee-ios --format json',
|
|
2609
|
-
])
|
|
2610
|
-
.action(async (options) => {
|
|
2611
|
-
const format = normalizeAnalyticsOutputFormat(options.format);
|
|
2612
|
-
const timezone = options.timezone?.trim() || undefined;
|
|
2613
|
-
const target = await resolveFunnelAnalyticsTarget(options);
|
|
2614
|
-
const report = await fetchJourneyVersions({
|
|
2615
|
-
token: target.token,
|
|
2616
|
-
workspaceId: target.workspace.id,
|
|
2617
|
-
projectId: target.project.id,
|
|
2618
|
-
funnelId: target.funnel.id,
|
|
2619
|
-
timezone,
|
|
2620
|
-
today: toAnalyticsDate(new Date(), timezone),
|
|
2621
|
-
});
|
|
2622
|
-
await writeOrPrintAnalyticsOutput({
|
|
2623
|
-
format,
|
|
2624
|
-
payload: buildJourneyVersionsReportPayload({
|
|
2625
|
-
...report,
|
|
2626
|
-
workspaceId: target.workspace.id,
|
|
2627
|
-
project: target.project,
|
|
2628
|
-
}),
|
|
2629
|
-
table: formatJourneyVersionsTable(report),
|
|
2630
|
-
out: options.out,
|
|
2631
|
-
});
|
|
2632
|
-
});
|
|
2633
2537
|
addExamples(analyticsCommand
|
|
2634
2538
|
.command('cohort')
|
|
2635
2539
|
.description('Download one-day cohort marketing data')
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
"minimumCliVersion": "0.1.20",
|
|
5
5
|
"entries": [
|
|
6
6
|
{
|
|
7
|
-
"repositoryCliVersion": "0.1.
|
|
7
|
+
"repositoryCliVersion": "0.1.294",
|
|
8
8
|
"manifest": {
|
|
9
9
|
"schemaVersion": 1,
|
|
10
|
-
"bundleVersion": "2.0.
|
|
10
|
+
"bundleVersion": "2.0.287",
|
|
11
11
|
"stepContractVersion": 3,
|
|
12
12
|
"contractHash": "d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf",
|
|
13
13
|
"managedFiles": [
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
"path": "docs/funnelsgrove/migrations/step-contract-v3.md",
|
|
48
|
-
"sha256": "
|
|
48
|
+
"sha256": "1f7f3d76856b2096ebde3bb0dd83dfabd131823476c8be151849e3bf57c91c28"
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
51
|
"path": "docs/funnelsgrove/qa/analytics.md",
|
|
@@ -145,16 +145,16 @@
|
|
|
145
145
|
},
|
|
146
146
|
{
|
|
147
147
|
"path": "funnel-docs.config.json",
|
|
148
|
-
"sha256": "
|
|
148
|
+
"sha256": "8470d2655720e6c3bb6e210b98d61fb7222deeef9607db6d03cca40e3b06fec7"
|
|
149
149
|
}
|
|
150
150
|
]
|
|
151
151
|
}
|
|
152
152
|
},
|
|
153
153
|
{
|
|
154
|
-
"repositoryCliVersion": "0.1.
|
|
154
|
+
"repositoryCliVersion": "0.1.293",
|
|
155
155
|
"manifest": {
|
|
156
156
|
"schemaVersion": 1,
|
|
157
|
-
"bundleVersion": "2.0.
|
|
157
|
+
"bundleVersion": "2.0.286",
|
|
158
158
|
"stepContractVersion": 3,
|
|
159
159
|
"contractHash": "d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf",
|
|
160
160
|
"managedFiles": [
|
|
@@ -192,7 +192,7 @@
|
|
|
192
192
|
},
|
|
193
193
|
{
|
|
194
194
|
"path": "docs/funnelsgrove/migrations/step-contract-v3.md",
|
|
195
|
-
"sha256": "
|
|
195
|
+
"sha256": "f1831876670eae6a6029f8f48d12038de408d4fb31a3332f7cec5cb3c7e4ff1d"
|
|
196
196
|
},
|
|
197
197
|
{
|
|
198
198
|
"path": "docs/funnelsgrove/qa/analytics.md",
|
|
@@ -292,16 +292,16 @@
|
|
|
292
292
|
},
|
|
293
293
|
{
|
|
294
294
|
"path": "funnel-docs.config.json",
|
|
295
|
-
"sha256": "
|
|
295
|
+
"sha256": "53e951dfdadfc634fa7bbd53615a5e2e3019cd1bce24b3a4d667e50557268dd7"
|
|
296
296
|
}
|
|
297
297
|
]
|
|
298
298
|
}
|
|
299
299
|
},
|
|
300
300
|
{
|
|
301
|
-
"repositoryCliVersion": "0.1.
|
|
301
|
+
"repositoryCliVersion": "0.1.292",
|
|
302
302
|
"manifest": {
|
|
303
303
|
"schemaVersion": 1,
|
|
304
|
-
"bundleVersion": "2.0.
|
|
304
|
+
"bundleVersion": "2.0.285",
|
|
305
305
|
"stepContractVersion": 3,
|
|
306
306
|
"contractHash": "d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf",
|
|
307
307
|
"managedFiles": [
|
|
@@ -339,7 +339,7 @@
|
|
|
339
339
|
},
|
|
340
340
|
{
|
|
341
341
|
"path": "docs/funnelsgrove/migrations/step-contract-v3.md",
|
|
342
|
-
"sha256": "
|
|
342
|
+
"sha256": "9f91fab6b6af7456e614811d75fc4007e35b0c26086135ddd6dbf5d0491253e7"
|
|
343
343
|
},
|
|
344
344
|
{
|
|
345
345
|
"path": "docs/funnelsgrove/qa/analytics.md",
|
|
@@ -439,16 +439,16 @@
|
|
|
439
439
|
},
|
|
440
440
|
{
|
|
441
441
|
"path": "funnel-docs.config.json",
|
|
442
|
-
"sha256": "
|
|
442
|
+
"sha256": "2c4bd0e6e5677772fcb6889d63833d9fcdd9d66da167571219aa5145567b335f"
|
|
443
443
|
}
|
|
444
444
|
]
|
|
445
445
|
}
|
|
446
446
|
},
|
|
447
447
|
{
|
|
448
|
-
"repositoryCliVersion": "0.1.
|
|
448
|
+
"repositoryCliVersion": "0.1.291",
|
|
449
449
|
"manifest": {
|
|
450
450
|
"schemaVersion": 1,
|
|
451
|
-
"bundleVersion": "2.0.
|
|
451
|
+
"bundleVersion": "2.0.284",
|
|
452
452
|
"stepContractVersion": 3,
|
|
453
453
|
"contractHash": "d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf",
|
|
454
454
|
"managedFiles": [
|
|
@@ -486,7 +486,7 @@
|
|
|
486
486
|
},
|
|
487
487
|
{
|
|
488
488
|
"path": "docs/funnelsgrove/migrations/step-contract-v3.md",
|
|
489
|
-
"sha256": "
|
|
489
|
+
"sha256": "32c4dc6493b9195c1d0c601e593975bfbd029eb348e986c87f9823b7d390e406"
|
|
490
490
|
},
|
|
491
491
|
{
|
|
492
492
|
"path": "docs/funnelsgrove/qa/analytics.md",
|
|
@@ -586,16 +586,16 @@
|
|
|
586
586
|
},
|
|
587
587
|
{
|
|
588
588
|
"path": "funnel-docs.config.json",
|
|
589
|
-
"sha256": "
|
|
589
|
+
"sha256": "bac474b7e4180ebc96f0b0ed2f1391b291d7065e3c626f90a7dac4aed4c567d8"
|
|
590
590
|
}
|
|
591
591
|
]
|
|
592
592
|
}
|
|
593
593
|
},
|
|
594
594
|
{
|
|
595
|
-
"repositoryCliVersion": "0.1.
|
|
595
|
+
"repositoryCliVersion": "0.1.290",
|
|
596
596
|
"manifest": {
|
|
597
597
|
"schemaVersion": 1,
|
|
598
|
-
"bundleVersion": "2.0.
|
|
598
|
+
"bundleVersion": "2.0.283",
|
|
599
599
|
"stepContractVersion": 3,
|
|
600
600
|
"contractHash": "d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf",
|
|
601
601
|
"managedFiles": [
|
|
@@ -633,7 +633,7 @@
|
|
|
633
633
|
},
|
|
634
634
|
{
|
|
635
635
|
"path": "docs/funnelsgrove/migrations/step-contract-v3.md",
|
|
636
|
-
"sha256": "
|
|
636
|
+
"sha256": "ca5a4836a51320df2216d317108e39384aa732dd92bd8f9085b41d88e4cfcc9d"
|
|
637
637
|
},
|
|
638
638
|
{
|
|
639
639
|
"path": "docs/funnelsgrove/qa/analytics.md",
|
|
@@ -733,16 +733,16 @@
|
|
|
733
733
|
},
|
|
734
734
|
{
|
|
735
735
|
"path": "funnel-docs.config.json",
|
|
736
|
-
"sha256": "
|
|
736
|
+
"sha256": "f999b566c6188943f384dc6fac60fcbb26ffc071d316c78bd264ef0f04319e5b"
|
|
737
737
|
}
|
|
738
738
|
]
|
|
739
739
|
}
|
|
740
740
|
},
|
|
741
741
|
{
|
|
742
|
-
"repositoryCliVersion": "0.1.
|
|
742
|
+
"repositoryCliVersion": "0.1.289",
|
|
743
743
|
"manifest": {
|
|
744
744
|
"schemaVersion": 1,
|
|
745
|
-
"bundleVersion": "2.0.
|
|
745
|
+
"bundleVersion": "2.0.282",
|
|
746
746
|
"stepContractVersion": 3,
|
|
747
747
|
"contractHash": "d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf",
|
|
748
748
|
"managedFiles": [
|
|
@@ -780,7 +780,7 @@
|
|
|
780
780
|
},
|
|
781
781
|
{
|
|
782
782
|
"path": "docs/funnelsgrove/migrations/step-contract-v3.md",
|
|
783
|
-
"sha256": "
|
|
783
|
+
"sha256": "474e05021e90bef7f781a96542c45c409e04b97d13fb8027d3d4b760d90ae336"
|
|
784
784
|
},
|
|
785
785
|
{
|
|
786
786
|
"path": "docs/funnelsgrove/qa/analytics.md",
|
|
@@ -880,16 +880,16 @@
|
|
|
880
880
|
},
|
|
881
881
|
{
|
|
882
882
|
"path": "funnel-docs.config.json",
|
|
883
|
-
"sha256": "
|
|
883
|
+
"sha256": "7b8e363b1ad3d48aebcfa1d4c2606a61d8e18a809b42ff19ceffcb800bc6e3fd"
|
|
884
884
|
}
|
|
885
885
|
]
|
|
886
886
|
}
|
|
887
887
|
},
|
|
888
888
|
{
|
|
889
|
-
"repositoryCliVersion": "0.1.
|
|
889
|
+
"repositoryCliVersion": "0.1.288",
|
|
890
890
|
"manifest": {
|
|
891
891
|
"schemaVersion": 1,
|
|
892
|
-
"bundleVersion": "2.0.
|
|
892
|
+
"bundleVersion": "2.0.281",
|
|
893
893
|
"stepContractVersion": 3,
|
|
894
894
|
"contractHash": "d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf",
|
|
895
895
|
"managedFiles": [
|
|
@@ -927,7 +927,7 @@
|
|
|
927
927
|
},
|
|
928
928
|
{
|
|
929
929
|
"path": "docs/funnelsgrove/migrations/step-contract-v3.md",
|
|
930
|
-
"sha256": "
|
|
930
|
+
"sha256": "bed96c808dba9780b3ea3cefba296e650e08d8d30708e5daecee2b00b633c4ef"
|
|
931
931
|
},
|
|
932
932
|
{
|
|
933
933
|
"path": "docs/funnelsgrove/qa/analytics.md",
|
|
@@ -1027,7 +1027,7 @@
|
|
|
1027
1027
|
},
|
|
1028
1028
|
{
|
|
1029
1029
|
"path": "funnel-docs.config.json",
|
|
1030
|
-
"sha256": "
|
|
1030
|
+
"sha256": "616b91c1bd60184c3ca34f4b9e0aecd5773667998a50fcd50dc7d374e0b71d71"
|
|
1031
1031
|
}
|
|
1032
1032
|
]
|
|
1033
1033
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"bundleVersion": "2.0.
|
|
3
|
+
"bundleVersion": "2.0.287",
|
|
4
4
|
"stepContractVersion": 3,
|
|
5
5
|
"contractHash": "d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf",
|
|
6
6
|
"managedFiles": [
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
"path": "docs/funnelsgrove/migrations/step-contract-v3.md",
|
|
41
|
-
"sha256": "
|
|
41
|
+
"sha256": "1f7f3d76856b2096ebde3bb0dd83dfabd131823476c8be151849e3bf57c91c28"
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
44
|
"path": "docs/funnelsgrove/qa/analytics.md",
|
|
@@ -138,7 +138,7 @@
|
|
|
138
138
|
},
|
|
139
139
|
{
|
|
140
140
|
"path": "funnel-docs.config.json",
|
|
141
|
-
"sha256": "
|
|
141
|
+
"sha256": "8470d2655720e6c3bb6e210b98d61fb7222deeef9607db6d03cca40e3b06fec7"
|
|
142
142
|
}
|
|
143
143
|
]
|
|
144
144
|
}
|
|
@@ -17,7 +17,7 @@ Supported read versions: `1`, `2`, `3`. Authoring and publish target version `3`
|
|
|
17
17
|
|
|
18
18
|
### Package release order
|
|
19
19
|
|
|
20
|
-
Release `@funnelsgrove/sdk` `0.4.0` first, then `@funnelsgrove/runtime` `0.
|
|
20
|
+
Release `@funnelsgrove/sdk` `0.4.0` first, then `@funnelsgrove/runtime` `0.33.0`, then `@funnelsgrove/analytics` `0.15.0`, then `@funnelsgrove/payments` `0.42.0`. The production deploy verifies the zero-traffic API candidate, publishes and verifies `@funnelsgrove/cli` `0.1.294`, and only then promotes the candidate to production traffic. The serving API must never advertise an unpublished preferred CLI. Publishing packages and deploying production remain separately approved operational actions.
|
|
21
21
|
<!-- funnelsgrove:generated:end contract-v3/migration/step-contract-v3 -->
|
|
22
22
|
|
|
23
23
|
## Version-last policy
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"bundleVersion": "2.0.
|
|
3
|
+
"bundleVersion": "2.0.287",
|
|
4
4
|
"stepContractVersion": 3,
|
|
5
5
|
"contractHash": "d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf",
|
|
6
6
|
"managedFiles": [
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
"path": "docs/funnelsgrove/migrations/step-contract-v3.md",
|
|
41
|
-
"sha256": "
|
|
41
|
+
"sha256": "1f7f3d76856b2096ebde3bb0dd83dfabd131823476c8be151849e3bf57c91c28"
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
44
|
"path": "docs/funnelsgrove/qa/analytics.md",
|
|
@@ -138,7 +138,7 @@
|
|
|
138
138
|
},
|
|
139
139
|
{
|
|
140
140
|
"path": "funnel-docs.config.json",
|
|
141
|
-
"sha256": "
|
|
141
|
+
"sha256": "8470d2655720e6c3bb6e210b98d61fb7222deeef9607db6d03cca40e3b06fec7"
|
|
142
142
|
}
|
|
143
143
|
]
|
|
144
144
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"sourceTreeHash": "
|
|
3
|
+
"sourceTreeHash": "bb9ed94c9474a273d5ac43c78cd99d6a886282f7e63eec2f44ee5f3405373e59",
|
|
4
4
|
"stepContractVersion": 3,
|
|
5
|
-
"docsBundleVersion": "2.0.
|
|
5
|
+
"docsBundleVersion": "2.0.287",
|
|
6
6
|
"files": [
|
|
7
7
|
{
|
|
8
8
|
"path": ".env.example",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
"path": ".funnelsgrove-docs.json",
|
|
19
|
-
"sha256": "
|
|
19
|
+
"sha256": "d3dcbf392c105f17cb2902f5f6637974fc8bd7db515f2b50ab2b7b4adb0df73b",
|
|
20
20
|
"mode": "100644"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
},
|
|
77
77
|
{
|
|
78
78
|
"path": "docs/funnelsgrove/migrations/step-contract-v3.md",
|
|
79
|
-
"sha256": "
|
|
79
|
+
"sha256": "1f7f3d76856b2096ebde3bb0dd83dfabd131823476c8be151849e3bf57c91c28",
|
|
80
80
|
"mode": "100644"
|
|
81
81
|
},
|
|
82
82
|
{
|
|
@@ -206,7 +206,7 @@
|
|
|
206
206
|
},
|
|
207
207
|
{
|
|
208
208
|
"path": "funnel-docs.config.json",
|
|
209
|
-
"sha256": "
|
|
209
|
+
"sha256": "8470d2655720e6c3bb6e210b98d61fb7222deeef9607db6d03cca40e3b06fec7",
|
|
210
210
|
"mode": "100644"
|
|
211
211
|
},
|
|
212
212
|
{
|
|
@@ -17,7 +17,7 @@ Supported read versions: `1`, `2`, `3`. Authoring and publish target version `3`
|
|
|
17
17
|
|
|
18
18
|
### Package release order
|
|
19
19
|
|
|
20
|
-
Release `@funnelsgrove/sdk` `0.4.0` first, then `@funnelsgrove/runtime` `0.
|
|
20
|
+
Release `@funnelsgrove/sdk` `0.4.0` first, then `@funnelsgrove/runtime` `0.33.0`, then `@funnelsgrove/analytics` `0.15.0`, then `@funnelsgrove/payments` `0.42.0`. The production deploy verifies the zero-traffic API candidate, publishes and verifies `@funnelsgrove/cli` `0.1.294`, and only then promotes the candidate to production traffic. The serving API must never advertise an unpublished preferred CLI. Publishing packages and deploying production remain separately approved operational actions.
|
|
21
21
|
<!-- funnelsgrove:generated:end contract-v3/migration/step-contract-v3 -->
|
|
22
22
|
|
|
23
23
|
## Version-last policy
|