@p11-core/cli 0.0.10 → 0.0.11

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 (2) hide show
  1. package/dist/index.js +209 -9
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3466,8 +3466,11 @@ var require_commander = __commonJS({
3466
3466
  });
3467
3467
 
3468
3468
  // src/index.ts
3469
+ import { randomUUID } from "crypto";
3469
3470
  import { realpathSync } from "fs";
3470
3471
  import { chmod, mkdir, mkdtemp, readFile, readdir, rename, rm, stat, writeFile } from "fs/promises";
3472
+ import http from "http";
3473
+ import https from "https";
3471
3474
  import { createRequire } from "module";
3472
3475
  import { homedir, tmpdir } from "os";
3473
3476
  import path from "path";
@@ -3503,11 +3506,16 @@ var nodeRequire = createRequire(import.meta.url);
3503
3506
  var packageJson = nodeRequire("../package.json");
3504
3507
  var builtDefaultApiUrl = "https://p11.rarexlabs.com";
3505
3508
  var defaultApiUrl = process.env.P11_API_URL || builtDefaultApiUrl;
3509
+ var builtPostHogKey = "phc_CftZMyBB3bMZQRj3CmJovHB3kvzwJmQhXSwUBgzUTUi6";
3510
+ var builtPostHogHost = "https://us.i.posthog.com";
3511
+ var builtAppEnv = "production";
3506
3512
  var generate = nodeRequire("@babel/generator").default;
3507
3513
  var traverse = nodeRequire("@babel/traverse").default;
3508
3514
  var updateCheckIntervalMs = 30 * 60 * 1e3;
3509
3515
  var updateCheckTimeoutMs = 800;
3516
+ var telemetryTimeoutMs = 300;
3510
3517
  var P11_HISTORY_FILE = "history.json";
3518
+ var P11_TELEMETRY_FILE = "telemetry.json";
3511
3519
  var P11_HISTORY_DIR_MODE = 448;
3512
3520
  var P11_HISTORY_FILE_MODE = 384;
3513
3521
  var P11_HISTORY_LOCK_STALE_MS = 3e4;
@@ -3659,37 +3667,79 @@ Examples:
3659
3667
 
3660
3668
  Environment:
3661
3669
  P11_API_URL Defaults to ${builtDefaultApiUrl}
3662
- P11_BUILD_DIR Directory for temporary share build files. Defaults to the OS temp directory.`
3670
+ P11_BUILD_DIR Directory for temporary share build files. Defaults to the OS temp directory.
3671
+ P11_TELEMETRY_DISABLED=1 disables anonymous usage analytics.`
3663
3672
  );
3664
3673
  const shareCommand = program2.command("share").description("Share a review link for a page.").argument("[page.tsx]").option("--json", "Print the API response as JSON.").option("--edit-url [url]", "Share a new version using an edit URL or edit id.").option("--api-url [url]", "Override the p11 API URL.").option("--build-dir [dir]", "Directory for temporary build files.").action(async (input, options2) => {
3665
- await publish(input, normalizePublishOptions(options2));
3674
+ await runCliAction(
3675
+ "share",
3676
+ {
3677
+ has_input: Boolean(input),
3678
+ json: Boolean(options2.json),
3679
+ edit_url: typeof options2.editUrl === "string",
3680
+ api_url_override: typeof options2.apiUrl === "string",
3681
+ build_dir_override: typeof options2.buildDir === "string"
3682
+ },
3683
+ () => publish(input, normalizePublishOptions(options2))
3684
+ );
3666
3685
  });
3667
3686
  allowLegacyParserBehavior(shareCommand);
3668
3687
  const commentsCommand = program2.command("comments").description("Fetch exported comments for a read/edit URL or id.").argument("[readUrl|editUrl|readId|editId]").option("--json", "Print comments as JSON.").option("--output [file]", "Write comments JSON to a file.").option("--version [n]", "Fetch comments for a specific page version.").option("--api-url [url]", "Override the p11 API URL.").action(async (target, options2) => {
3669
- await comments(target, normalizeCommentsOptions(options2));
3688
+ await runCliAction(
3689
+ "comments",
3690
+ {
3691
+ has_target: Boolean(target),
3692
+ json: Boolean(options2.json),
3693
+ output: typeof options2.output === "string",
3694
+ version: typeof options2.version === "string",
3695
+ api_url_override: typeof options2.apiUrl === "string"
3696
+ },
3697
+ () => comments(target, normalizeCommentsOptions(options2))
3698
+ );
3670
3699
  });
3671
3700
  allowLegacyParserBehavior(commentsCommand);
3672
3701
  const deleteCommand = program2.command("delete").description("Delete a document and all of its versions and comments.").argument("[editUrl|editId]").option("--json", "Print the API response as JSON.").option("--api-url [url]", "Override the p11 API URL.").action(async (target, options2) => {
3673
- await deleteDocument(target, normalizeDeleteOptions(options2));
3702
+ await runCliAction(
3703
+ "delete",
3704
+ {
3705
+ has_target: Boolean(target),
3706
+ json: Boolean(options2.json),
3707
+ api_url_override: typeof options2.apiUrl === "string"
3708
+ },
3709
+ () => deleteDocument(target, normalizeDeleteOptions(options2))
3710
+ );
3674
3711
  });
3675
3712
  allowLegacyParserBehavior(deleteCommand);
3676
3713
  program2.command("version").description("Print the p11 CLI version.").action(async () => {
3677
- await warnBeforeAction({ force: true });
3678
- console.log(packageJson.version);
3714
+ await runCliAction("version", {}, async () => {
3715
+ await warnBeforeAction({ force: true });
3716
+ console.log(packageJson.version);
3717
+ });
3679
3718
  });
3680
3719
  program2.command("history").description("List saved read/edit links.").option("--json", "Print saved history as JSON.").action(async (options2) => {
3681
- await history(normalizeHistoryOptions(options2));
3720
+ await runCliAction("history", { json: Boolean(options2.json) }, () => history(normalizeHistoryOptions(options2)));
3682
3721
  });
3683
3722
  const docsCommand = program2.command("docs").description("Print p11 authoring docs.").argument("[topic]", "Docs topic: components or sharing.").action(async (topic) => {
3684
- await docs(topic);
3723
+ await runCliAction("docs", { has_topic: Boolean(topic), known_topic: isKnownDocsTopic(topic) }, () => docs(topic));
3685
3724
  });
3686
3725
  allowLegacyParserBehavior(docsCommand);
3687
3726
  const exampleCommand = program2.command("example").description("Print or write a p11 example document.").argument("[name]", "Example name: minimal or all.").option("--output [file]", "Write the example to a file.").action(async (name, options2) => {
3688
- await example(name, normalizeExampleOptions(options2));
3727
+ await runCliAction(
3728
+ "example",
3729
+ {
3730
+ has_example: Boolean(name),
3731
+ known_example: isKnownExampleName(name),
3732
+ output: typeof options2.output === "string"
3733
+ },
3734
+ () => example(name, normalizeExampleOptions(options2))
3735
+ );
3689
3736
  });
3690
3737
  allowLegacyParserBehavior(exampleCommand);
3691
3738
  return program2;
3692
3739
  }
3740
+ var __testing = {
3741
+ captureCliTelemetry: captureCliTelemetryNow
3742
+ };
3693
3743
  function isKnownTopLevelCommand(program2, command) {
3694
3744
  return command === "--help" || command === "-h" || isVersionFlag(command) || command === "help" || program2.commands.some((subcommand) => subcommand.name() === command);
3695
3745
  }
@@ -3699,6 +3749,31 @@ function isVersionFlag(command) {
3699
3749
  function allowLegacyParserBehavior(command) {
3700
3750
  return command.allowUnknownOption().allowExcessArguments();
3701
3751
  }
3752
+ function isKnownDocsTopic(topic) {
3753
+ return docsTopics.has(topic ?? "index");
3754
+ }
3755
+ function isKnownExampleName(name) {
3756
+ return name === void 0 || exampleFiles.has(name);
3757
+ }
3758
+ async function runCliAction(command, properties, action) {
3759
+ const startedAt = Date.now();
3760
+ try {
3761
+ await action();
3762
+ captureCliTelemetry("p11_cli_command_succeeded", {
3763
+ command,
3764
+ duration_ms: Date.now() - startedAt,
3765
+ ...properties
3766
+ });
3767
+ } catch (error) {
3768
+ await captureCliTelemetryNow("p11_cli_command_failed", {
3769
+ command,
3770
+ duration_ms: Date.now() - startedAt,
3771
+ error_name: error instanceof Error ? error.name : "Error",
3772
+ ...properties
3773
+ });
3774
+ throw error;
3775
+ }
3776
+ }
3702
3777
  function normalizePublishOptions(options) {
3703
3778
  return {
3704
3779
  json: options.json,
@@ -4037,6 +4112,130 @@ function delay(ms) {
4037
4112
  function historyFilePath() {
4038
4113
  return path.join(homedir(), ".p11", P11_HISTORY_FILE);
4039
4114
  }
4115
+ function captureCliTelemetry(event, properties = {}) {
4116
+ scheduleTelemetry(() => captureCliTelemetryNow(event, properties, { keepAlive: false }));
4117
+ }
4118
+ async function captureCliTelemetryNow(event, properties = {}, options = {}) {
4119
+ const key = postHogKey();
4120
+ if (!key) return;
4121
+ try {
4122
+ const host = postHogHost();
4123
+ const { anonymousId, isNew } = await telemetryIdentity();
4124
+ const baseProperties = cliTelemetryBaseProperties();
4125
+ const events = [];
4126
+ if (isNew && event !== "p11_cli_activated") {
4127
+ events.push(sendPostHogEvent(host, key, "p11_cli_activated", anonymousId, baseProperties, options));
4128
+ }
4129
+ events.push(sendPostHogEvent(host, key, event, anonymousId, { ...baseProperties, ...properties }, options));
4130
+ await Promise.all(events);
4131
+ } catch {
4132
+ }
4133
+ }
4134
+ function scheduleTelemetry(task) {
4135
+ const run = () => {
4136
+ task().catch(() => {
4137
+ });
4138
+ };
4139
+ if (typeof setImmediate === "function") {
4140
+ setImmediate(run);
4141
+ return;
4142
+ }
4143
+ setTimeout(run, 0);
4144
+ }
4145
+ async function sendPostHogEvent(host, key, event, distinctId, properties, options = {}) {
4146
+ const payload = JSON.stringify({
4147
+ api_key: key,
4148
+ event,
4149
+ distinct_id: distinctId,
4150
+ properties: compactObject(properties)
4151
+ });
4152
+ const endpoint = new URL("/capture/", host);
4153
+ const client = endpoint.protocol === "http:" ? http : https;
4154
+ await new Promise((resolve, reject) => {
4155
+ const request = client.request(
4156
+ endpoint,
4157
+ {
4158
+ method: "POST",
4159
+ headers: {
4160
+ "content-type": "application/json",
4161
+ "content-length": Buffer.byteLength(payload)
4162
+ }
4163
+ },
4164
+ (response) => {
4165
+ response.resume();
4166
+ response.on("end", resolve);
4167
+ }
4168
+ );
4169
+ const timeout = setTimeout(() => {
4170
+ request.destroy(new Error("Telemetry request timed out."));
4171
+ }, telemetryTimeoutMs);
4172
+ if (options.keepAlive === false) timeout.unref?.();
4173
+ request.on("socket", (socket) => {
4174
+ if (options.keepAlive === false) socket.unref();
4175
+ });
4176
+ request.on("error", reject);
4177
+ request.on("close", () => clearTimeout(timeout));
4178
+ request.end(payload);
4179
+ });
4180
+ }
4181
+ function cliTelemetryBaseProperties() {
4182
+ return {
4183
+ product: "p11",
4184
+ surface: "cli",
4185
+ app_env: analyticsEnv(),
4186
+ cli_version: packageJson.version,
4187
+ node_version: process.versions.node,
4188
+ platform: process.platform,
4189
+ arch: process.arch
4190
+ };
4191
+ }
4192
+ function postHogKey() {
4193
+ if (isTruthyEnv(process.env.P11_TELEMETRY_DISABLED) || isTruthyEnv(process.env.DO_NOT_TRACK)) return "";
4194
+ return (process.env.P11_POSTHOG_KEY || builtPostHogKey || "").trim();
4195
+ }
4196
+ function postHogHost() {
4197
+ const rawHost = (process.env.P11_POSTHOG_HOST || builtPostHogHost || "https://us.i.posthog.com").trim();
4198
+ try {
4199
+ return new URL(rawHost.endsWith("/") ? rawHost : `${rawHost}/`);
4200
+ } catch {
4201
+ return new URL("https://us.i.posthog.com/");
4202
+ }
4203
+ }
4204
+ function isTruthyEnv(value) {
4205
+ return value === "1" || value === "true" || value === "yes";
4206
+ }
4207
+ function analyticsEnv() {
4208
+ return (process.env.APP_ENV || builtAppEnv || "production").trim() || "production";
4209
+ }
4210
+ async function telemetryIdentity() {
4211
+ const filePath = telemetryFilePath();
4212
+ const raw = await readFile(filePath, "utf8").catch(() => null);
4213
+ if (raw) {
4214
+ const parsed = JSON.parse(raw);
4215
+ if (typeof parsed.anonymousId === "string" && /^anon_[A-Za-z0-9-]{12,80}$/.test(parsed.anonymousId)) {
4216
+ return {
4217
+ anonymousId: parsed.anonymousId,
4218
+ isNew: false
4219
+ };
4220
+ }
4221
+ }
4222
+ const data = {
4223
+ anonymousId: `anon_${randomUUID()}`,
4224
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
4225
+ };
4226
+ const telemetryDir = path.dirname(filePath);
4227
+ await ensurePrivateHistoryDir(telemetryDir);
4228
+ await writeFile(filePath, `${JSON.stringify(data, null, 2)}
4229
+ `, { mode: P11_HISTORY_FILE_MODE });
4230
+ await chmod(filePath, P11_HISTORY_FILE_MODE);
4231
+ return {
4232
+ anonymousId: data.anonymousId,
4233
+ isNew: true
4234
+ };
4235
+ }
4236
+ function telemetryFilePath() {
4237
+ return path.join(homedir(), ".p11", P11_TELEMETRY_FILE);
4238
+ }
4040
4239
  function commentsExportJson(data) {
4041
4240
  return JSON.stringify(commentExportData(data), null, 2);
4042
4241
  }
@@ -4544,6 +4743,7 @@ function parseUrlVersion(value) {
4544
4743
  }
4545
4744
  }
4546
4745
  export {
4746
+ __testing,
4547
4747
  addSourceLineAttributes,
4548
4748
  appendPublishHistory,
4549
4749
  buildPageModule,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@p11-core/cli",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "bin": {