@debugbundle/cli 1.12.0 → 1.13.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.
Files changed (3) hide show
  1. package/README.md +13 -0
  2. package/dist/main.cjs +276 -8
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -65,6 +65,19 @@ The CLI reads local project configuration from `.debugbundle/` and can use membe
65
65
 
66
66
  ## AnalyticsBundle
67
67
 
68
+ Public project flows support customer-defined acquisition and activation steps across sites,
69
+ auth pages and apps. Read definitions and aggregate reports with:
70
+
71
+ ```bash
72
+ debugbundle analytics flows list --project <project-id> --json
73
+ debugbundle analytics flows report --project <project-id> --key onboarding --window 30d --json
74
+ ```
75
+
76
+ Owners/admins can use `analytics flows save --project <project-id> --definition-json '<JSON>'`
77
+ and `analytics flows archive --project <project-id> --key onboarding`. See the
78
+ [flow contract](https://debugbundle.com/docs/api/analytics/#public-acquisition-and-activation-flows)
79
+ for definitions, capture, full UTC day report windows, and partial coverage.
80
+
68
81
  Analytics commands require connected member authentication and a project with AnalyticsBundle enabled. They query aggregate rollups or request an asynchronous analysis artifact; they do not read raw analytics events.
69
82
 
70
83
  ```sh
package/dist/main.cjs CHANGED
@@ -26224,7 +26224,7 @@ var require_codegen = __commonJS({
26224
26224
  return `${this.lhs} ${this.op}= ${this.rhs};` + _n;
26225
26225
  }
26226
26226
  };
26227
- var Label = class extends Node {
26227
+ var Label2 = class extends Node {
26228
26228
  constructor(label) {
26229
26229
  super();
26230
26230
  this.label = label;
@@ -26655,7 +26655,7 @@ var require_codegen = __commonJS({
26655
26655
  }
26656
26656
  // `label` statement
26657
26657
  label(label) {
26658
- return this._leafNode(new Label(label));
26658
+ return this._leafNode(new Label2(label));
26659
26659
  }
26660
26660
  // `break` statement
26661
26661
  break(label) {
@@ -73259,6 +73259,91 @@ var PROJECT_COLOR_TAG_VALUES = [
73259
73259
  ];
73260
73260
  var ProjectColorTagSchema = external_exports.enum(PROJECT_COLOR_TAG_VALUES);
73261
73261
 
73262
+ // ../../packages/shared-types/src/analytics-flows.ts
73263
+ var AnalyticsFlowKeySchema = external_exports.string().trim().regex(/^[a-z][a-z0-9_.-]{0,63}$/);
73264
+ var AnalyticsFlowOriginSchema = external_exports.string().max(255).refine((value) => {
73265
+ try {
73266
+ const url = new URL(value);
73267
+ return url.origin === value && !url.username && !url.password && (url.protocol === "https:" || url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname));
73268
+ } catch {
73269
+ return false;
73270
+ }
73271
+ }, "Use an exact HTTPS origin (HTTP is allowed for localhost).");
73272
+ var AnalyticsFlowDefinitionInputSchema = external_exports.object({
73273
+ flow_key: AnalyticsFlowKeySchema,
73274
+ display_name: external_exports.string().trim().min(1).max(120),
73275
+ kind: external_exports.enum(["acquisition", "activation"]),
73276
+ timeout_minutes: external_exports.number().int().min(10).max(1440).default(60),
73277
+ steps: external_exports.array(
73278
+ external_exports.object({
73279
+ step_key: AnalyticsFlowKeySchema,
73280
+ display_name: external_exports.string().trim().min(1).max(120),
73281
+ origin: AnalyticsFlowOriginSchema
73282
+ }).strict()
73283
+ ).min(2).max(8)
73284
+ }).strict().refine(
73285
+ (value) => new Set(value.steps.map((step) => step.step_key)).size === value.steps.length,
73286
+ "Step keys must be unique."
73287
+ );
73288
+ var AnalyticsFlowDefinitionSchema = AnalyticsFlowDefinitionInputSchema.innerType().extend({
73289
+ id: external_exports.string().uuid(),
73290
+ project_id: external_exports.string().uuid(),
73291
+ version: external_exports.number().int().positive(),
73292
+ archived_at: external_exports.string().datetime().nullable()
73293
+ }).refine(
73294
+ (value) => new Set(value.steps.map((step) => step.step_key)).size === value.steps.length,
73295
+ "Step keys must be unique."
73296
+ );
73297
+ var Count = external_exports.number().int().nonnegative();
73298
+ var AnalyticsFlowReportSchema = external_exports.object({
73299
+ flow: AnalyticsFlowDefinitionSchema,
73300
+ window: external_exports.object({
73301
+ from: external_exports.string().datetime(),
73302
+ to: external_exports.string().datetime(),
73303
+ previous_from: external_exports.string().datetime()
73304
+ }),
73305
+ starts: Count,
73306
+ previous_starts: Count,
73307
+ completions: Count,
73308
+ steps: external_exports.array(
73309
+ external_exports.object({
73310
+ step_key: AnalyticsFlowKeySchema,
73311
+ display_name: external_exports.string(),
73312
+ reached: Count,
73313
+ previous_reached: Count,
73314
+ unlinked: Count,
73315
+ dropoff: Count,
73316
+ average_seconds: external_exports.number().nonnegative().nullable()
73317
+ })
73318
+ ).min(2).max(8),
73319
+ sources: external_exports.array(
73320
+ external_exports.object({ source: external_exports.string(), campaign: external_exports.string(), starts: Count, completions: Count })
73321
+ ).max(50),
73322
+ coverage: external_exports.object({
73323
+ observation: external_exports.literal("explicit_integration_signals"),
73324
+ incomplete: external_exports.literal(true),
73325
+ sources_truncated: external_exports.boolean()
73326
+ })
73327
+ });
73328
+ var AnalyticsFlowsResponseSchema = external_exports.object({
73329
+ flows: external_exports.array(AnalyticsFlowDefinitionSchema).max(100)
73330
+ });
73331
+ var AnalyticsFlowResponseSchema = external_exports.object({ flow: AnalyticsFlowDefinitionSchema });
73332
+ var Secret = external_exports.string().regex(/^[A-Za-z0-9_-]{43}$/);
73333
+ var Label = external_exports.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._~+-]{0,99}$/);
73334
+ var Context = external_exports.object({ context: Secret, consent: external_exports.boolean().optional() });
73335
+ var AnalyticsFlowCaptureSchemas = {
73336
+ start: Context.extend({
73337
+ step_key: AnalyticsFlowKeySchema,
73338
+ source: Label.optional(),
73339
+ campaign: Label.optional()
73340
+ }).strict(),
73341
+ step: Context.extend({ step_key: AnalyticsFlowKeySchema }).strict(),
73342
+ handoff: Context.extend({ step_key: AnalyticsFlowKeySchema, token: Secret }).strict(),
73343
+ arrive: Context.extend({ token: Secret }).strict(),
73344
+ withdraw: Context.strict()
73345
+ };
73346
+
73262
73347
  // ../../packages/shared-types/src/index.ts
73263
73348
  var SeveritySchema = external_exports.enum(["low", "medium", "high", "critical"]);
73264
73349
  var BundleSdkSchema = external_exports.object({
@@ -100733,8 +100818,52 @@ function defineStorageSchemaMigration(input3) {
100733
100818
  };
100734
100819
  }
100735
100820
 
100736
- // ../../packages/storage/src/browser-recovery-schema.ts
100821
+ // ../../packages/storage/src/analytics-flow-schema.ts
100737
100822
  var statements = [
100823
+ `CREATE TABLE IF NOT EXISTS analytics_flow_definitions (
100824
+ id uuid PRIMARY KEY, project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
100825
+ flow_key text NOT NULL, version integer NOT NULL DEFAULT 1, definition jsonb NOT NULL,
100826
+ archived_at timestamptz, created_at timestamptz NOT NULL, updated_at timestamptz NOT NULL,
100827
+ UNIQUE(project_id, flow_key)
100828
+ )`,
100829
+ `CREATE TABLE IF NOT EXISTS analytics_flow_runs (
100830
+ id uuid PRIMARY KEY, flow_id uuid NOT NULL REFERENCES analytics_flow_definitions(id) ON DELETE CASCADE,
100831
+ version integer NOT NULL, context_hash text NOT NULL UNIQUE, origin text NOT NULL,
100832
+ start_index integer NOT NULL, last_index integer NOT NULL,
100833
+ source text NOT NULL DEFAULT 'unknown', campaign text NOT NULL DEFAULT '',
100834
+ started_at timestamptz NOT NULL, last_step_at timestamptz NOT NULL, expires_at timestamptz NOT NULL,
100835
+ CHECK (start_index BETWEEN 0 AND 7 AND last_index BETWEEN start_index AND 7),
100836
+ CHECK (expires_at > started_at AND expires_at <= started_at + interval '24 hours')
100837
+ )`,
100838
+ `CREATE INDEX IF NOT EXISTS analytics_flow_runs_expiry_idx ON analytics_flow_runs(expires_at, id)`,
100839
+ `CREATE TABLE IF NOT EXISTS analytics_flow_handoffs (
100840
+ id uuid PRIMARY KEY, run_id uuid NOT NULL REFERENCES analytics_flow_runs(id) ON DELETE CASCADE,
100841
+ token_hash text NOT NULL UNIQUE, step_index integer NOT NULL, expires_at timestamptz NOT NULL,
100842
+ redeemed_context_hash text, UNIQUE(run_id, step_index)
100843
+ )`,
100844
+ `CREATE INDEX IF NOT EXISTS analytics_flow_handoffs_expiry_idx ON analytics_flow_handoffs(expires_at, id)`,
100845
+ `CREATE TABLE IF NOT EXISTS analytics_flow_rollups (
100846
+ flow_id uuid NOT NULL REFERENCES analytics_flow_definitions(id) ON DELETE CASCADE,
100847
+ version integer NOT NULL, cohort_date date NOT NULL, step_index integer NOT NULL,
100848
+ source text NOT NULL, campaign text NOT NULL, reached bigint NOT NULL DEFAULT 0,
100849
+ unlinked bigint NOT NULL DEFAULT 0, elapsed_ms double precision NOT NULL DEFAULT 0,
100850
+ elapsed_count bigint NOT NULL DEFAULT 0,
100851
+ PRIMARY KEY(flow_id, version, cohort_date, step_index, source, campaign)
100852
+ )`,
100853
+ `CREATE INDEX IF NOT EXISTS analytics_flow_rollups_expiry_idx ON analytics_flow_rollups(cohort_date, flow_id)`
100854
+ ];
100855
+ var ANALYTICS_FLOW_BOOTSTRAP_STATEMENTS = statements.map(
100856
+ (statement) => statement.replaceAll(" IF NOT EXISTS", "")
100857
+ );
100858
+ var migration = {
100859
+ id: "202610030001_add_public_analytics_flows",
100860
+ description: "Add customer project flow definitions, bounded continuity and aggregate reports.",
100861
+ statements
100862
+ };
100863
+ var ANALYTICS_FLOW_SCHEMA_MIGRATIONS = [defineStorageSchemaMigration(migration)];
100864
+
100865
+ // ../../packages/storage/src/browser-recovery-schema.ts
100866
+ var statements2 = [
100738
100867
  `ALTER TABLE alert_deliveries ADD COLUMN IF NOT EXISTS coalescing_key text`,
100739
100868
  `CREATE INDEX IF NOT EXISTS alert_deliveries_coalescing_idx ON alert_deliveries (alert_id, coalescing_key, created_at) WHERE coalescing_key IS NOT NULL`,
100740
100869
  `CREATE TABLE IF NOT EXISTS browser_recovery_events (
@@ -100756,10 +100885,10 @@ var BROWSER_RECOVERY_MIGRATIONS = [
100756
100885
  defineStorageSchemaMigration({
100757
100886
  id: "202609220001_add_browser_recovery_context",
100758
100887
  description: "Index browser recovery context and separate burst suppression from alert cooldown identity",
100759
- statements
100888
+ statements: statements2
100760
100889
  })
100761
100890
  ];
100762
- var BROWSER_RECOVERY_BOOTSTRAP_STATEMENTS = statements.filter((statement) => !statement.startsWith("ALTER TABLE")).map((statement) => statement.replaceAll(" IF NOT EXISTS", ""));
100891
+ var BROWSER_RECOVERY_BOOTSTRAP_STATEMENTS = statements2.filter((statement) => !statement.startsWith("ALTER TABLE")).map((statement) => statement.replaceAll(" IF NOT EXISTS", ""));
100763
100892
 
100764
100893
  // ../../packages/storage/src/availability-check-bootstrap-statements.ts
100765
100894
  var AVAILABILITY_CHECK_BOOTSTRAP_STATEMENTS = [
@@ -102734,7 +102863,8 @@ var STORAGE_BOOTSTRAP_STATEMENTS2 = [
102734
102863
  ...OPENAI_OAUTH_BOOTSTRAP_STATEMENTS,
102735
102864
  ...AVAILABILITY_CHECK_BOOTSTRAP_STATEMENTS,
102736
102865
  ...ANALYTICS_BOOTSTRAP_STATEMENTS,
102737
- ...WORKER_JOB_BOOTSTRAP_STATEMENTS
102866
+ ...WORKER_JOB_BOOTSTRAP_STATEMENTS,
102867
+ ...ANALYTICS_FLOW_BOOTSTRAP_STATEMENTS
102738
102868
  ];
102739
102869
 
102740
102870
  // ../../packages/storage/src/migrations.ts
@@ -104948,7 +105078,8 @@ var STORAGE_SCHEMA_MIGRATIONS2 = [
104948
105078
  ...AVAILABILITY_CHECK_STORAGE_SCHEMA_MIGRATIONS,
104949
105079
  ...ANALYTICS_STORAGE_SCHEMA_MIGRATIONS,
104950
105080
  ...OPENAI_OAUTH_STORAGE_SCHEMA_MIGRATIONS,
104951
- ...WORKER_JOB_SCHEMA_MIGRATIONS
105081
+ ...WORKER_JOB_SCHEMA_MIGRATIONS,
105082
+ ...ANALYTICS_FLOW_SCHEMA_MIGRATIONS
104952
105083
  ].sort((left, right) => left.id.localeCompare(right.id));
104953
105084
 
104954
105085
  // src/local-retrieval-store.ts
@@ -115211,6 +115342,10 @@ var CLI_USAGE_LINES = [
115211
115342
  " debugbundle analytics saved-funnels create --project <id> --key <key> --name <name> --steps-json <json> [--auth-file <path>] [--json]",
115212
115343
  " debugbundle analytics saved-funnels update <key> --project <id> [--name <name>] [--steps-json <json>] [--auth-file <path>] [--json]",
115213
115344
  " debugbundle analytics saved-funnels archive <key> --project <id> [--auth-file <path>] [--json]",
115345
+ " debugbundle analytics flows list --project <id> [--auth-file <path>] [--json]",
115346
+ " debugbundle analytics flows save --project <id> --definition-json <json> [--auth-file <path>] [--json]",
115347
+ " debugbundle analytics flows report --project <id> --key <key> [--window <7d|30d|90d>] [--auth-file <path>] [--json]",
115348
+ " debugbundle analytics flows archive --project <id> --key <key> [--auth-file <path>] [--json]",
115214
115349
  " debugbundle analytics summary --project <id> [--from <ISO8601>] [--to <ISO8601>] [--last <duration>] [--granularity <hour|day>] [--service <name>] [--environment <name>] [--limit <n>] [--auth-file <path>] [--json]",
115215
115350
  " debugbundle analytics routes --project <id> [--from <ISO8601>] [--to <ISO8601>] [--last <duration>] [--granularity <hour|day>] [--service <name>] [--environment <name>] [--limit <n>] [--auth-file <path>] [--json]",
115216
115351
  " debugbundle analytics journeys --project <id> [--from <ISO8601>] [--to <ISO8601>] [--last <duration>] [--granularity <hour|day>] [--service <name>] [--environment <name>] [--limit <n>] [--auth-file <path>] [--json]",
@@ -117261,6 +117396,103 @@ async function getAnalyticsJourneySampleWithAuthCommand(input3, dependencies) {
117261
117396
  });
117262
117397
  }
117263
117398
 
117399
+ // src/analytics-flow-commands.ts
117400
+ var AnalyticsFlowApiError = class extends Error {
117401
+ constructor(status, message3) {
117402
+ super(message3);
117403
+ this.status = status;
117404
+ }
117405
+ };
117406
+ function createAnalyticsFlowApi(http) {
117407
+ const path = (input3) => `/v1/projects/${encodeURIComponent(input3.projectId)}/analytics/flows`;
117408
+ async function request(input3, schema) {
117409
+ const response = await http.request(input3);
117410
+ if (response.status !== 200) {
117411
+ const error2 = external_exports.object({ error: external_exports.string() }).safeParse(response.body);
117412
+ throw new AnalyticsFlowApiError(
117413
+ response.status,
117414
+ error2.success ? error2.data.error : "Flow request failed."
117415
+ );
117416
+ }
117417
+ const parsed = schema.safeParse(response.body);
117418
+ if (!parsed.success) throw new AnalyticsFlowApiError(500, "Invalid flow response.");
117419
+ return parsed.data;
117420
+ }
117421
+ return {
117422
+ list: (input3) => request(
117423
+ { method: "GET", path: path(input3), bearerToken: input3.bearerToken },
117424
+ AnalyticsFlowsResponseSchema
117425
+ ),
117426
+ save: (input3) => {
117427
+ const definition = AnalyticsFlowDefinitionInputSchema.parse(input3.definition);
117428
+ return request(
117429
+ {
117430
+ method: "PUT",
117431
+ path: `${path(input3)}/${definition.flow_key}`,
117432
+ bearerToken: input3.bearerToken,
117433
+ body: definition
117434
+ },
117435
+ AnalyticsFlowResponseSchema
117436
+ );
117437
+ },
117438
+ archive: (input3) => request(
117439
+ {
117440
+ method: "DELETE",
117441
+ path: `${path(input3)}/${encodeURIComponent(AnalyticsFlowKeySchema.parse(input3.flowKey))}`,
117442
+ bearerToken: input3.bearerToken
117443
+ },
117444
+ external_exports.object({ archived: external_exports.literal(true) })
117445
+ ),
117446
+ report: (input3) => request(
117447
+ {
117448
+ method: "GET",
117449
+ path: `${path(input3)}/${encodeURIComponent(input3.flowKey)}/report?window=${input3.window ?? "30d"}`,
117450
+ bearerToken: input3.bearerToken
117451
+ },
117452
+ AnalyticsFlowReportSchema
117453
+ )
117454
+ };
117455
+ }
117456
+ async function analyticsFlowWithAuthCommand(input3, dependencies) {
117457
+ return runAuthenticatedCliCommand(input3, {
117458
+ dependencies,
117459
+ async createApi(authInput, deps) {
117460
+ const authState = await (deps?.readAuthState ?? readCliAuthState)(authInput);
117461
+ return {
117462
+ authState,
117463
+ api: createAnalyticsFlowApi(
117464
+ (deps?.createHttpClient ?? createCliHttpClient)({ baseUrl: authState.base_url })
117465
+ )
117466
+ };
117467
+ },
117468
+ async runCommand(auth, api) {
117469
+ try {
117470
+ const scope = { projectId: input3.projectId, bearerToken: auth.bearer_token };
117471
+ let result;
117472
+ if (input3.action === "list") result = await api.list(scope);
117473
+ else if (input3.action === "save")
117474
+ result = await api.save({
117475
+ ...scope,
117476
+ definition: AnalyticsFlowDefinitionInputSchema.parse(input3.definition)
117477
+ });
117478
+ else {
117479
+ const item = { ...scope, flowKey: AnalyticsFlowKeySchema.parse(input3.flowKey) };
117480
+ result = input3.action === "archive" ? await api.archive(item) : await api.report({
117481
+ ...item,
117482
+ ...input3.window === void 0 ? {} : { window: input3.window }
117483
+ });
117484
+ }
117485
+ return { exitCode: 0, output: JSON.stringify(result, null, input3.json ? void 0 : 2) };
117486
+ } catch (error2) {
117487
+ return {
117488
+ exitCode: error2 instanceof AnalyticsFlowApiError ? error2.status === 401 ? 2 : error2.status === 404 ? 3 : [400, 403, 409].includes(error2.status) ? 4 : 1 : 1,
117489
+ output: error2 instanceof Error ? error2.message : "Flow request failed."
117490
+ };
117491
+ }
117492
+ }
117493
+ });
117494
+ }
117495
+
117264
117496
  // src/management-analytics-command-handlers.ts
117265
117497
  function readFloatOption(parsedArgv, optionName) {
117266
117498
  const rawValue = readStringOption(parsedArgv, optionName);
@@ -117467,6 +117699,42 @@ function buildAnalyticsSettingsUpdate(parsedArgv) {
117467
117699
  }
117468
117700
  async function handleAnalyticsCommand(parsedArgv, dependencies) {
117469
117701
  const resource = requirePositional(parsedArgv, 1, "analytics resource");
117702
+ if (resource === "flows") {
117703
+ const action2 = requirePositional(parsedArgv, 2, "flows action");
117704
+ if (!["list", "save", "archive", "report"].includes(action2))
117705
+ throw new CliInputError("Unknown analytics flows command.");
117706
+ expectNoUnknownOptions(parsedArgv, [
117707
+ "project",
117708
+ "project-id",
117709
+ "key",
117710
+ "definition-json",
117711
+ "window",
117712
+ "auth-file",
117713
+ "json"
117714
+ ]);
117715
+ ensureNoExtraPositionals(parsedArgv, 3);
117716
+ const projectId = readProjectOption(parsedArgv);
117717
+ if (!projectId) throw new CliInputError("Missing required option --project.");
117718
+ const flowKey = readStringOption(parsedArgv, "key");
117719
+ if ((action2 === "report" || action2 === "archive") && !flowKey)
117720
+ throw new CliInputError("Missing required option --key.");
117721
+ const window2 = readStringOption(parsedArgv, "window");
117722
+ if (window2 !== void 0 && window2 !== "7d" && window2 !== "30d" && window2 !== "90d")
117723
+ throw new CliInputError("Invalid value for --window.");
117724
+ const definition = action2 === "save" ? AnalyticsFlowDefinitionInputSchema.safeParse(
117725
+ readJsonOption(parsedArgv, "definition-json")
117726
+ ) : void 0;
117727
+ if (definition && !definition.success) throw new CliInputError("Invalid flow definition.");
117728
+ return await (dependencies.analyticsFlowCommand ?? analyticsFlowWithAuthCommand)(
117729
+ appendCommonAuthOptions(parsedArgv, {
117730
+ action: action2,
117731
+ projectId,
117732
+ ...flowKey === void 0 ? {} : { flowKey },
117733
+ ...window2 === void 0 ? {} : { window: window2 },
117734
+ ...definition?.success ? { definition: definition.data } : {}
117735
+ })
117736
+ );
117737
+ }
117470
117738
  if (resource === "summary" || resource === "routes" || resource === "journeys" || resource === "devices" || resource === "referrers" || resource === "actions" || resource === "funnels") {
117471
117739
  expectNoUnknownOptions(parsedArgv, [
117472
117740
  "project",
@@ -124072,7 +124340,7 @@ async function handleCaptureRuleCommand2(parsedArgv, dependencies) {
124072
124340
  // package.json
124073
124341
  var package_default = {
124074
124342
  name: "@debugbundle/cli",
124075
- version: "1.12.0",
124343
+ version: "1.13.0",
124076
124344
  private: false,
124077
124345
  description: "Command-line interface for DebugBundle",
124078
124346
  license: "Apache-2.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@debugbundle/cli",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "private": false,
5
5
  "description": "Command-line interface for DebugBundle",
6
6
  "license": "Apache-2.0",