@zanfia/cli 0.6.4 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +541 -64
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5249,9 +5249,9 @@ var require_lodash7 = __commonJS({
|
|
|
5249
5249
|
}
|
|
5250
5250
|
});
|
|
5251
5251
|
|
|
5252
|
-
// ../../node_modules/
|
|
5252
|
+
// ../../node_modules/lodash.uniqby/index.js
|
|
5253
5253
|
var require_lodash8 = __commonJS({
|
|
5254
|
-
"../../node_modules/
|
|
5254
|
+
"../../node_modules/lodash.uniqby/index.js"(exports, module) {
|
|
5255
5255
|
"use strict";
|
|
5256
5256
|
var baseIteratee = require_lodash4();
|
|
5257
5257
|
var baseUniq = require_lodash7();
|
|
@@ -7380,7 +7380,8 @@ var mockBackend = {
|
|
|
7380
7380
|
status: "draft",
|
|
7381
7381
|
isFree: false,
|
|
7382
7382
|
createdAt: now(),
|
|
7383
|
-
clientCount: 0
|
|
7383
|
+
clientCount: 0,
|
|
7384
|
+
language: input.language ?? null
|
|
7384
7385
|
};
|
|
7385
7386
|
products.push(product);
|
|
7386
7387
|
return toSummary(product);
|
|
@@ -7390,6 +7391,7 @@ var mockBackend = {
|
|
|
7390
7391
|
if (!product) return null;
|
|
7391
7392
|
if (input.name !== void 0) product.name = input.name;
|
|
7392
7393
|
if (input.description !== void 0) product.description = input.description;
|
|
7394
|
+
if (input.language !== void 0) product.language = input.language;
|
|
7393
7395
|
return { productId: id };
|
|
7394
7396
|
},
|
|
7395
7397
|
archiveProduct(id) {
|
|
@@ -7897,6 +7899,9 @@ function createMockClient() {
|
|
|
7897
7899
|
async listClientTags() {
|
|
7898
7900
|
return { count: 0, tags: [] };
|
|
7899
7901
|
},
|
|
7902
|
+
async listClientCustomFields() {
|
|
7903
|
+
return { count: 0, fields: [] };
|
|
7904
|
+
},
|
|
7900
7905
|
async createClientTag() {
|
|
7901
7906
|
throw new ApiError(501, "client tag writes are not available in mock mode");
|
|
7902
7907
|
},
|
|
@@ -8119,6 +8124,7 @@ function createMockClient() {
|
|
|
8119
8124
|
przelewy24: false,
|
|
8120
8125
|
paypal: false,
|
|
8121
8126
|
dodo: false,
|
|
8127
|
+
tubapay: false,
|
|
8122
8128
|
manual: null
|
|
8123
8129
|
},
|
|
8124
8130
|
sms: { defaultProvider: null, twilio: null, smsapi: null }
|
|
@@ -9248,6 +9254,7 @@ function createHttpClient(config) {
|
|
|
9248
9254
|
),
|
|
9249
9255
|
changeClientEmail: (id, newEmail) => request("POST", `/clients/${encodeURIComponent(id)}/change-email`, { newEmail }),
|
|
9250
9256
|
listClientTags: () => request("GET", "/clients/tags"),
|
|
9257
|
+
listClientCustomFields: (includeArchived) => request("GET", `/clients/custom-fields${includeArchived ? "?includeArchived=true" : ""}`),
|
|
9251
9258
|
createClientTag: (input) => request("POST", "/clients/tags", input),
|
|
9252
9259
|
updateClientTag: (tagId, input) => request("PATCH", `/clients/tags/${encodeURIComponent(tagId)}`, input),
|
|
9253
9260
|
deleteClientTag: (tagId) => request("DELETE", `/clients/tags/${encodeURIComponent(tagId)}`),
|
|
@@ -10157,6 +10164,27 @@ function parseStorefrontVisibility(value) {
|
|
|
10157
10164
|
}
|
|
10158
10165
|
return visibility;
|
|
10159
10166
|
}
|
|
10167
|
+
var PRODUCT_LANGUAGES = ["pl", "en"];
|
|
10168
|
+
var PRODUCT_LANGUAGE_NONE = "none";
|
|
10169
|
+
function parseProductLanguage(value) {
|
|
10170
|
+
const language = PRODUCT_LANGUAGES.find((candidate) => candidate === value.trim().toLowerCase());
|
|
10171
|
+
if (language === void 0) {
|
|
10172
|
+
throw new Error(
|
|
10173
|
+
`Invalid --language: ${value} (expected one of: ${PRODUCT_LANGUAGES.join(", ")})`
|
|
10174
|
+
);
|
|
10175
|
+
}
|
|
10176
|
+
return language;
|
|
10177
|
+
}
|
|
10178
|
+
function parseProductLanguageOrNone(value) {
|
|
10179
|
+
if (value.trim().toLowerCase() === PRODUCT_LANGUAGE_NONE) return PRODUCT_LANGUAGE_NONE;
|
|
10180
|
+
const language = PRODUCT_LANGUAGES.find((candidate) => candidate === value.trim().toLowerCase());
|
|
10181
|
+
if (language === void 0) {
|
|
10182
|
+
throw new Error(
|
|
10183
|
+
`Invalid --language: ${value} (expected one of: ${PRODUCT_LANGUAGES.join(", ")}, ${PRODUCT_LANGUAGE_NONE})`
|
|
10184
|
+
);
|
|
10185
|
+
}
|
|
10186
|
+
return language;
|
|
10187
|
+
}
|
|
10160
10188
|
function parseCommaList(flag) {
|
|
10161
10189
|
return (value) => {
|
|
10162
10190
|
const items = [
|
|
@@ -10324,38 +10352,39 @@ var listCertificateTemplatesColumns = ["id", "name", "version", "isDefault", "up
|
|
|
10324
10352
|
function registerCertificatesCommands(program2) {
|
|
10325
10353
|
const certificates = program2.command("certificates");
|
|
10326
10354
|
certificates.description(
|
|
10327
|
-
"Course-completion certificates \u2014 issue for course clients, revoke/reinstate, resend the email, and manage designer templates (`certificates templates \u2026`)"
|
|
10355
|
+
"Course-completion and event-participation certificates \u2014 issue for course or event clients, revoke/reinstate, resend the email, and manage designer templates (`certificates templates \u2026`)"
|
|
10328
10356
|
);
|
|
10329
10357
|
certificates.command("list").summary("List certificates").description(
|
|
10330
|
-
"Issued
|
|
10331
|
-
).option("--product <value>", "Course product id \u2014 omit for all
|
|
10358
|
+
"Issued certificates of the workspace (course completion and event participation), newest first, optionally filtered to one product (`productId`); `limit` caps the rows per page (service default applies) and `nextCursor` (pass it back as `cursor`) continues the list. Each row is the credential: public `certificateId` (`cert_\u2026`, the id every certificate write takes), `productType` (`course` / `event`), recipient name/email, product (`courseName`) and issuer names, completion/issued dates, `issuedBy` (manual vs automatic), template id/version, PDF and verification URLs, `status` (active/revoked), `expiresAt` and delivery/engagement signals (email status, views, downloads, LinkedIn add). Also the polling endpoint after issue-certificates \u2014 a newly enqueued client shows up here once its PDF has been rendered."
|
|
10359
|
+
).option("--product <value>", "Course or event product id \u2014 omit for all products.").option(
|
|
10332
10360
|
"--limit <n>",
|
|
10333
|
-
"Max rows, newest first (max 500; the service default applies when omitted).",
|
|
10361
|
+
"Max rows per page, newest first (max 500; the service default applies when omitted).",
|
|
10334
10362
|
parseNumberOption("--limit", NumberFormat.Integer)
|
|
10335
|
-
).action(async (options, command) => {
|
|
10363
|
+
).option("--cursor <value>", "`nextCursor` of the previous page; omit for the first page.").action(async (options, command) => {
|
|
10336
10364
|
const flags = getGlobalFlags(command);
|
|
10337
10365
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
10338
10366
|
const params = {};
|
|
10339
10367
|
const payload = {
|
|
10340
10368
|
productId: options.product,
|
|
10341
|
-
limit: options.limit
|
|
10369
|
+
limit: options.limit,
|
|
10370
|
+
cursor: options.cursor
|
|
10342
10371
|
};
|
|
10343
10372
|
const result = await client.call("GET", "/certificates", params, payload, void 0);
|
|
10344
10373
|
const rows = tableRowsOf(result, "certificates");
|
|
10345
10374
|
emit(flags, result ?? {}, () => printTable(rows, listCertificatesColumns));
|
|
10346
10375
|
});
|
|
10347
10376
|
certificates.command("issue").summary("Issue certificates (async)").description(
|
|
10348
|
-
'Issues
|
|
10349
|
-
).argument("<productId>", "Course product id (
|
|
10377
|
+
'Issues certificates to the listed clients of ONE product: a course (completion certificate) or an event \u2014 webinar, workshop, also one held outside Zanfia (participation certificate, "for taking part in", no attendance check). The call validates the batch (404 unknown product or `templateId`, 400 when the product is neither a course nor an event), skips clients who already hold a certificate for it (one certificate per client per product, ever) and enqueues one issuance task per remaining client \u2014 PDF rendering and the "certificate issued" email happen asynchronously, so the response is 202 with `enqueued` / `skippedExistingCertificate` counts, not the certificates. Poll list-certificates (`productId` filter) a little later for the issued rows. A single issuance is this call with one `clientId`. With `onlyCompleted: true` a client only gets a certificate if their course progress is completed (checked per client at issuance time; courses only, 400 for an event). `clientIds` is capped at 1000 and de-duplicated. Every issuance EMAILS the student \u2014 the emails cannot be recalled.'
|
|
10378
|
+
).argument("<productId>", "Course or event product id (other product types answer 400).").requiredOption(
|
|
10350
10379
|
"--client <a,b,...>",
|
|
10351
10380
|
"Workspace client ids; one id = single issuance. Max 1000, de-duplicated.",
|
|
10352
10381
|
parseCommaList("--client")
|
|
10353
10382
|
).option(
|
|
10354
10383
|
"--only-completed",
|
|
10355
|
-
"
|
|
10384
|
+
"Course products only (400 for an event): skip clients who have not completed the course (checked per client at issuance time). Default false = issue to everyone listed."
|
|
10356
10385
|
).option("--no-only-completed", "Send `onlyCompleted: false` explicitly.").option(
|
|
10357
10386
|
"--template <value>",
|
|
10358
|
-
"Explicit template id; omitted = the
|
|
10387
|
+
"Explicit template id; omitted = the product template, else the workspace default."
|
|
10359
10388
|
).option(
|
|
10360
10389
|
"--reissue-revoked",
|
|
10361
10390
|
"Re-issue clients whose current certificate is REVOKED (new document in the current template, new number and verification link, the revoked one stays revoked). ACTIVE certificates are still skipped. Default false = any existing certificate skips the client."
|
|
@@ -10593,7 +10622,10 @@ function registerBlogCommands(program2) {
|
|
|
10593
10622
|
).option("--content <value>", "The body as Markdown (headings, lists, links, images by URL).").option(
|
|
10594
10623
|
"--excerpt <value>",
|
|
10595
10624
|
"Short plain-text summary (list, RSS, meta description); derived from the body on publish when empty."
|
|
10596
|
-
).option("--cover <value>", "Cover image URL (Media Library).").option("--tags <a,b,...>", "Lowercase labels, at most 10.", parseCommaList("--tags")).option("--seo <json|file>", "seo", parseJsonObjectFileOrInline("--seo")).option("--publish", "Publish right away (needs a non-empty body); default false = draft.").option("--no-publish", "Send `publish: false` explicitly.").
|
|
10625
|
+
).option("--cover <value>", "Cover image URL (Media Library).").option("--tags <a,b,...>", "Lowercase labels, at most 10.", parseCommaList("--tags")).option("--seo <json|file>", "seo", parseJsonObjectFileOrInline("--seo")).option("--publish", "Publish right away (needs a non-empty body); default false = draft.").option("--no-publish", "Send `publish: false` explicitly.").option(
|
|
10626
|
+
"--published-at <value>",
|
|
10627
|
+
"The post's date, ISO 8601 date (2025-10-16) or date-time, not in the future - e.g. the original date of an imported post. Omitted = the moment of the first publication."
|
|
10628
|
+
).action(async (options, command) => {
|
|
10597
10629
|
const flags = getGlobalFlags(command);
|
|
10598
10630
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
10599
10631
|
const params = {};
|
|
@@ -10605,7 +10637,8 @@ function registerBlogCommands(program2) {
|
|
|
10605
10637
|
coverImageUrl: options.cover,
|
|
10606
10638
|
tags: options.tags,
|
|
10607
10639
|
seo: options.seo,
|
|
10608
|
-
publish: options.publish
|
|
10640
|
+
publish: options.publish,
|
|
10641
|
+
publishedAt: options.publishedAt
|
|
10609
10642
|
};
|
|
10610
10643
|
const result = await client.call("POST", "/blog-posts", params, void 0, payload);
|
|
10611
10644
|
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
@@ -10633,6 +10666,9 @@ function registerBlogCommands(program2) {
|
|
|
10633
10666
|
"--seo <json|file>",
|
|
10634
10667
|
"Replaces the whole SEO section.",
|
|
10635
10668
|
parseJsonObjectFileOrInline("--seo")
|
|
10669
|
+
).option(
|
|
10670
|
+
"--published-at <value>",
|
|
10671
|
+
"The post's date (ISO 8601, not in the future); null clears it on a draft - a published post keeps one."
|
|
10636
10672
|
).action(async (id, options, command) => {
|
|
10637
10673
|
const flags = getGlobalFlags(command);
|
|
10638
10674
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
@@ -10646,7 +10682,8 @@ function registerBlogCommands(program2) {
|
|
|
10646
10682
|
excerpt: options.excerpt,
|
|
10647
10683
|
coverImageUrl: options.cover,
|
|
10648
10684
|
tags: options.tags,
|
|
10649
|
-
seo: options.seo
|
|
10685
|
+
seo: options.seo,
|
|
10686
|
+
publishedAt: options.publishedAt
|
|
10650
10687
|
};
|
|
10651
10688
|
const result = await client.call("PATCH", "/blog-posts/:postId", params, void 0, payload);
|
|
10652
10689
|
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
@@ -10691,10 +10728,270 @@ function registerBlogCommands(program2) {
|
|
|
10691
10728
|
});
|
|
10692
10729
|
}
|
|
10693
10730
|
|
|
10731
|
+
// src/commands/_generated/site-redirects.ts
|
|
10732
|
+
var listSiteRedirectsColumns = ["from", "to", "kind"];
|
|
10733
|
+
function registerSiteRedirectsCommands(program2) {
|
|
10734
|
+
const siteRedirects = program2.command("site-redirects");
|
|
10735
|
+
siteRedirects.description(
|
|
10736
|
+
"The site's 301 redirect map (Your site \u2192 Redirects): old addresses and where they go; checked by the same rules as the dashboard"
|
|
10737
|
+
);
|
|
10738
|
+
siteRedirects.command("list").summary("List site redirects").description(
|
|
10739
|
+
"The site's 301 redirect map (dashboard: Your site \u2192 Redirects): old addresses of a site moved to Zanfia and where they go now, in map order, plus `max` (the cap). A line fires only when nothing on the site answers the address. `kind` = `exact` (one address) or `folder` (`/news/*`, every address under it). The `from` of a line is its id for update-site-redirect and delete-site-redirect."
|
|
10740
|
+
).action(async (_options, command) => {
|
|
10741
|
+
const flags = getGlobalFlags(command);
|
|
10742
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
10743
|
+
const params = {};
|
|
10744
|
+
const result = await client.call("GET", "/site/redirects", params, void 0, void 0);
|
|
10745
|
+
const rows = tableRowsOf(result, "redirects");
|
|
10746
|
+
emit(flags, result ?? {}, () => printTable(rows, listSiteRedirectsColumns));
|
|
10747
|
+
});
|
|
10748
|
+
siteRedirects.command("create").summary("Add a site redirect").description(
|
|
10749
|
+
"Adds one line at the end of the site's 301 map; live at once. Refused (400 `site-redirects-refused`, `issues[]` = index, from, to, issue) exactly like the dashboard save: a malformed address (`invalid-from` / `invalid-to`), the home page or an address Zanfia owns (`/blog`, `/p/\u2026`, `/app`: `reserved-from`), the member area (`portal-from`), a published page (`page-exists`), a `from` the map already has (`duplicate`), a chain or a loop (`chain`), `/*` in `to` without a folder `from` (`rest-without-folder`), over 500 lines (`too-many-entries`). Returns the line as stored."
|
|
10750
|
+
).argument(
|
|
10751
|
+
"<from>",
|
|
10752
|
+
"The old address on the site: a path (`/o-nas`), a full address on the site (`https://example.com/o-nas/`, stored as its path) or a folder ending with `/*` (`/news/*`, every address under it). Query string and `#\u2026` are dropped; case is kept."
|
|
10753
|
+
).argument(
|
|
10754
|
+
"<to>",
|
|
10755
|
+
"Where it goes: a path on the site (`/o-mnie`; a folder line may end with `/*` to keep the rest of the old address: `/news/*` \u2192 `/blog/*`) or an absolute `https://` address."
|
|
10756
|
+
).action(async (from, to, _options, command) => {
|
|
10757
|
+
const flags = getGlobalFlags(command);
|
|
10758
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
10759
|
+
const params = {};
|
|
10760
|
+
const payload = {
|
|
10761
|
+
from,
|
|
10762
|
+
to
|
|
10763
|
+
};
|
|
10764
|
+
const result = await client.call("POST", "/site/redirects", params, void 0, payload);
|
|
10765
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
10766
|
+
});
|
|
10767
|
+
siteRedirects.command("update").summary("Change a site redirect").description(
|
|
10768
|
+
"Changes one line of the 301 map, picked by its current `from`: `newFrom`, `to` or both; a field not given keeps its value and the line keeps its place. Live at once. 404 `site-redirect-not-found` when no line has that `from`; refused like create-site-redirect (400 `site-redirects-refused` with `issues[]`). Returns the line as stored."
|
|
10769
|
+
).argument(
|
|
10770
|
+
"<from>",
|
|
10771
|
+
"The line to change, by its current `from` (as list-site-redirects shows it; `/o-nas/` or a full address on the site also match)."
|
|
10772
|
+
).option(
|
|
10773
|
+
"--new-from <value>",
|
|
10774
|
+
"The old address on the site: a path (`/o-nas`), a full address on the site (`https://example.com/o-nas/`, stored as its path) or a folder ending with `/*` (`/news/*`, every address under it). Query string and `#\u2026` are dropped; case is kept."
|
|
10775
|
+
).option(
|
|
10776
|
+
"--to <value>",
|
|
10777
|
+
"Where it goes: a path on the site (`/o-mnie`; a folder line may end with `/*` to keep the rest of the old address: `/news/*` \u2192 `/blog/*`) or an absolute `https://` address."
|
|
10778
|
+
).action(async (from, options, command) => {
|
|
10779
|
+
const flags = getGlobalFlags(command);
|
|
10780
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
10781
|
+
const params = {};
|
|
10782
|
+
const payload = {
|
|
10783
|
+
from,
|
|
10784
|
+
newFrom: options.newFrom,
|
|
10785
|
+
to: options.to
|
|
10786
|
+
};
|
|
10787
|
+
const result = await client.call("PATCH", "/site/redirects", params, void 0, payload);
|
|
10788
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
10789
|
+
});
|
|
10790
|
+
siteRedirects.command("delete").summary("Remove a site redirect").description(
|
|
10791
|
+
"Removes one line of the 301 map, picked by its `from` (sent in the JSON body). The old address answers 404 again at once (links to it in search results and old posts break). 404 `site-redirect-not-found` when no line has that `from`. Returns the removed line."
|
|
10792
|
+
).argument(
|
|
10793
|
+
"<from>",
|
|
10794
|
+
"The line to change, by its current `from` (as list-site-redirects shows it; `/o-nas/` or a full address on the site also match)."
|
|
10795
|
+
).option(
|
|
10796
|
+
"--confirm",
|
|
10797
|
+
"required \u2014 acknowledge that you want to the old address answers 404 again"
|
|
10798
|
+
).action(async (from, options, command) => {
|
|
10799
|
+
const flags = getGlobalFlags(command);
|
|
10800
|
+
requireConfirm(options.confirm, "the old address answers 404 again");
|
|
10801
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
10802
|
+
const params = {};
|
|
10803
|
+
const payload = {
|
|
10804
|
+
from
|
|
10805
|
+
};
|
|
10806
|
+
const result = await client.call("DELETE", "/site/redirects", params, void 0, payload);
|
|
10807
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
10808
|
+
});
|
|
10809
|
+
}
|
|
10810
|
+
|
|
10811
|
+
// src/commands/_generated/popups.ts
|
|
10812
|
+
var listPopupsColumns = ["id", "name", "status", "priority", "updatedAt"];
|
|
10813
|
+
function registerPopupsCommands(program2) {
|
|
10814
|
+
const popups = program2.command("popups");
|
|
10815
|
+
popups.description(
|
|
10816
|
+
"Pop-ups on the workspace site (Your site \u2192 Pop-ups): a signup form shown on entry or after scrolling, after the cookie decision, never again after a signup"
|
|
10817
|
+
);
|
|
10818
|
+
popups.command("list").summary("List pop-ups").description(
|
|
10819
|
+
"Every pop-up of the workspace site (drafts, active, paused) in priority order: copy, form content, triggers per device, targeting, frequency and schedule. The id source for every other pop-up tool."
|
|
10820
|
+
).action(async (_options, command) => {
|
|
10821
|
+
const flags = getGlobalFlags(command);
|
|
10822
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
10823
|
+
const params = {};
|
|
10824
|
+
const result = await client.call("GET", "/popups", params, void 0, void 0);
|
|
10825
|
+
const rows = tableRowsOf(result, "popups");
|
|
10826
|
+
emit(flags, result ?? {}, () => printTable(rows, listPopupsColumns));
|
|
10827
|
+
});
|
|
10828
|
+
popups.command("create").summary("Create a pop-up").description(
|
|
10829
|
+
"Creates a pop-up as a DRAFT holding a signup form. Without `content.formId` a new standalone signup form named after the pop-up is created (visible in Forms). Defaults: desktop after 10 s on the page, phones after 40 % scroll as a bottom sheet, again after 7 days, all hosted pages except thank-you / download / member pages, hidden for subscribers. Activate it with set-popup-status once it has a title and a button text. Requires the pop-ups feature on the plan (403 otherwise)."
|
|
10830
|
+
).requiredOption(
|
|
10831
|
+
"--name <value>",
|
|
10832
|
+
"Internal name (panel only); also the name of a provisioned form."
|
|
10833
|
+
).option(
|
|
10834
|
+
"--priority <n>",
|
|
10835
|
+
"Lower shows first when several pop-ups match a page; default = after the others.",
|
|
10836
|
+
parseNumberOption("--priority", NumberFormat.Integer)
|
|
10837
|
+
).addOption(
|
|
10838
|
+
new Option(
|
|
10839
|
+
"--language <pl|en|default>",
|
|
10840
|
+
"Built-in labels (close, errors): `pl`, `en` or `default` (= the page language)."
|
|
10841
|
+
).choices(["pl", "en", "default"])
|
|
10842
|
+
).option(
|
|
10843
|
+
"--copy <json|file>",
|
|
10844
|
+
"Headline, text, button and image.",
|
|
10845
|
+
parseJsonObjectFileOrInline("--copy")
|
|
10846
|
+
).option(
|
|
10847
|
+
"--content <json|file>",
|
|
10848
|
+
"What the pop-up holds: a signup form.",
|
|
10849
|
+
parseJsonObjectFileOrInline("--content")
|
|
10850
|
+
).option("--design <json|file>", "design", parseJsonObjectFileOrInline("--design")).option(
|
|
10851
|
+
"--trigger <json|file>",
|
|
10852
|
+
"When it opens, per device.",
|
|
10853
|
+
parseJsonObjectFileOrInline("--trigger")
|
|
10854
|
+
).option(
|
|
10855
|
+
"--targeting <json|file>",
|
|
10856
|
+
"Where and for whom.",
|
|
10857
|
+
parseJsonObjectFileOrInline("--targeting")
|
|
10858
|
+
).option(
|
|
10859
|
+
"--frequency <json|file>",
|
|
10860
|
+
"How often one visitor sees it; never again after a signup. Default every 7 days.",
|
|
10861
|
+
parseJsonObjectFileOrInline("--frequency")
|
|
10862
|
+
).option(
|
|
10863
|
+
"--schedule <json|file>",
|
|
10864
|
+
"From / to (applied within a few minutes).",
|
|
10865
|
+
parseJsonObjectFileOrInline("--schedule")
|
|
10866
|
+
).action(async (options, command) => {
|
|
10867
|
+
const flags = getGlobalFlags(command);
|
|
10868
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
10869
|
+
const params = {};
|
|
10870
|
+
const payload = {
|
|
10871
|
+
name: options.name,
|
|
10872
|
+
priority: options.priority,
|
|
10873
|
+
language: options.language,
|
|
10874
|
+
copy: options.copy,
|
|
10875
|
+
content: options.content,
|
|
10876
|
+
design: options.design,
|
|
10877
|
+
trigger: options.trigger,
|
|
10878
|
+
targeting: options.targeting,
|
|
10879
|
+
frequency: options.frequency,
|
|
10880
|
+
schedule: options.schedule
|
|
10881
|
+
};
|
|
10882
|
+
const result = await client.call("POST", "/popups", params, void 0, payload);
|
|
10883
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
10884
|
+
});
|
|
10885
|
+
popups.command("get").summary("Pop-up detail").description(
|
|
10886
|
+
"One pop-up in full: status, copy, form content, triggers, targeting, frequency, schedule."
|
|
10887
|
+
).argument("<id>", "Pop-up id (`popup_\u2026`) from list-popups.").action(async (id, _options, command) => {
|
|
10888
|
+
const flags = getGlobalFlags(command);
|
|
10889
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
10890
|
+
const params = {
|
|
10891
|
+
popupId: id
|
|
10892
|
+
};
|
|
10893
|
+
const result = await client.call("GET", "/popups/:popupId", params, void 0, void 0);
|
|
10894
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
10895
|
+
});
|
|
10896
|
+
popups.command("update").summary("Update a pop-up").description(
|
|
10897
|
+
"Partial update of a pop-up: each section sent merges into the current one (copy, content, design, targeting, schedule field by field; a trigger rule, the page list and the frequency are replaced whole). An active pop-up must keep its title, button and form (400 popup-incomplete). Live sites pick changes up within a few minutes."
|
|
10898
|
+
).argument("<id>", "Pop-up id (`popup_\u2026`) from list-popups.").option("--name <value>", "Internal name (panel only).").option(
|
|
10899
|
+
"--priority <n>",
|
|
10900
|
+
"Lower shows first when several pop-ups match a page; default = after the others.",
|
|
10901
|
+
parseNumberOption("--priority", NumberFormat.Integer)
|
|
10902
|
+
).addOption(
|
|
10903
|
+
new Option(
|
|
10904
|
+
"--language <pl|en|default>",
|
|
10905
|
+
"Built-in labels (close, errors): `pl`, `en` or `default` (= the page language)."
|
|
10906
|
+
).choices(["pl", "en", "default"])
|
|
10907
|
+
).option(
|
|
10908
|
+
"--copy <json|file>",
|
|
10909
|
+
"Headline, text, button and image.",
|
|
10910
|
+
parseJsonObjectFileOrInline("--copy")
|
|
10911
|
+
).option(
|
|
10912
|
+
"--content <json|file>",
|
|
10913
|
+
"What the pop-up holds: a signup form.",
|
|
10914
|
+
parseJsonObjectFileOrInline("--content")
|
|
10915
|
+
).option("--design <json|file>", "design", parseJsonObjectFileOrInline("--design")).option(
|
|
10916
|
+
"--trigger <json|file>",
|
|
10917
|
+
"When it opens, per device.",
|
|
10918
|
+
parseJsonObjectFileOrInline("--trigger")
|
|
10919
|
+
).option(
|
|
10920
|
+
"--targeting <json|file>",
|
|
10921
|
+
"Where and for whom.",
|
|
10922
|
+
parseJsonObjectFileOrInline("--targeting")
|
|
10923
|
+
).option(
|
|
10924
|
+
"--frequency <json|file>",
|
|
10925
|
+
"How often one visitor sees it; never again after a signup. Default every 7 days.",
|
|
10926
|
+
parseJsonObjectFileOrInline("--frequency")
|
|
10927
|
+
).option(
|
|
10928
|
+
"--schedule <json|file>",
|
|
10929
|
+
"From / to (applied within a few minutes).",
|
|
10930
|
+
parseJsonObjectFileOrInline("--schedule")
|
|
10931
|
+
).action(async (id, options, command) => {
|
|
10932
|
+
const flags = getGlobalFlags(command);
|
|
10933
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
10934
|
+
const params = {
|
|
10935
|
+
popupId: id
|
|
10936
|
+
};
|
|
10937
|
+
const payload = {
|
|
10938
|
+
name: options.name,
|
|
10939
|
+
priority: options.priority,
|
|
10940
|
+
language: options.language,
|
|
10941
|
+
copy: options.copy,
|
|
10942
|
+
content: options.content,
|
|
10943
|
+
design: options.design,
|
|
10944
|
+
trigger: options.trigger,
|
|
10945
|
+
targeting: options.targeting,
|
|
10946
|
+
frequency: options.frequency,
|
|
10947
|
+
schedule: options.schedule
|
|
10948
|
+
};
|
|
10949
|
+
const result = await client.call("PATCH", "/popups/:popupId", params, void 0, payload);
|
|
10950
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
10951
|
+
});
|
|
10952
|
+
popups.command("status").summary("Activate or pause a pop-up").description(
|
|
10953
|
+
"Activates or pauses a pop-up. Activation needs a title, a button text and an existing signup form (400 popup-incomplete / 404 form-not-found), the pop-ups feature on the plan (403) and a free slot: at most 20 active pop-ups (409 too-many-active-popups). Visitors see it after their cookie decision, by its trigger, once per page view."
|
|
10954
|
+
).argument("<id>", "Pop-up id (`popup_\u2026`) from list-popups.").argument(
|
|
10955
|
+
"<status>",
|
|
10956
|
+
"`active` shows it on the site (within a few minutes), `paused` takes it off."
|
|
10957
|
+
).action(async (id, status, _options, command) => {
|
|
10958
|
+
const flags = getGlobalFlags(command);
|
|
10959
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
10960
|
+
const params = {
|
|
10961
|
+
popupId: id
|
|
10962
|
+
};
|
|
10963
|
+
const payload = {
|
|
10964
|
+
status
|
|
10965
|
+
};
|
|
10966
|
+
const result = await client.call(
|
|
10967
|
+
"POST",
|
|
10968
|
+
"/popups/:popupId/status",
|
|
10969
|
+
params,
|
|
10970
|
+
void 0,
|
|
10971
|
+
payload
|
|
10972
|
+
);
|
|
10973
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
10974
|
+
});
|
|
10975
|
+
popups.command("delete").summary("Delete a pop-up").description(
|
|
10976
|
+
"Deletes a pop-up for good; an active one leaves the site within a few minutes. Its form and the signups stay. No undo - prefer pausing."
|
|
10977
|
+
).argument("<id>", "Pop-up id (`popup_\u2026`) from list-popups.").option("--confirm", "required \u2014 acknowledge that you want to deletes the pop-up for good").action(async (id, options, command) => {
|
|
10978
|
+
const flags = getGlobalFlags(command);
|
|
10979
|
+
requireConfirm(options.confirm, "deletes the pop-up for good");
|
|
10980
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
10981
|
+
const params = {
|
|
10982
|
+
popupId: id
|
|
10983
|
+
};
|
|
10984
|
+
const result = await client.call("DELETE", "/popups/:popupId", params, void 0, void 0);
|
|
10985
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
10986
|
+
});
|
|
10987
|
+
}
|
|
10988
|
+
|
|
10694
10989
|
// src/commands/_generated/index.ts
|
|
10695
10990
|
function registerGeneratedCommands(program2) {
|
|
10696
10991
|
registerCertificatesCommands(program2);
|
|
10697
10992
|
registerBlogCommands(program2);
|
|
10993
|
+
registerSiteRedirectsCommands(program2);
|
|
10994
|
+
registerPopupsCommands(program2);
|
|
10698
10995
|
}
|
|
10699
10996
|
|
|
10700
10997
|
// src/commands/ads/index.ts
|
|
@@ -13248,6 +13545,11 @@ function buildConsentInput(options) {
|
|
|
13248
13545
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "fs";
|
|
13249
13546
|
|
|
13250
13547
|
// src/client/types.ts
|
|
13548
|
+
var GRANT_PAYMENT_METHODS = [
|
|
13549
|
+
"bankTransfer",
|
|
13550
|
+
"cash",
|
|
13551
|
+
"other"
|
|
13552
|
+
];
|
|
13251
13553
|
var AIImageAspectRatio = /* @__PURE__ */ ((AIImageAspectRatio2) => {
|
|
13252
13554
|
AIImageAspectRatio2["Square"] = "1:1";
|
|
13253
13555
|
AIImageAspectRatio2["Portrait"] = "4:5";
|
|
@@ -13292,7 +13594,8 @@ var CREDENTIAL_PAYMENT_GATEWAYS = [
|
|
|
13292
13594
|
"przelewy24",
|
|
13293
13595
|
"paynow",
|
|
13294
13596
|
"paypal",
|
|
13295
|
-
"dodo"
|
|
13597
|
+
"dodo",
|
|
13598
|
+
"tubapay"
|
|
13296
13599
|
];
|
|
13297
13600
|
var COMMUNITY_EVENT_LOCATION_TYPES = [
|
|
13298
13601
|
"online",
|
|
@@ -13776,6 +14079,40 @@ function registerClientTagsCommands(clients2) {
|
|
|
13776
14079
|
var IMPORT_CHUNK_SIZE = 100;
|
|
13777
14080
|
var SCHEDULER_UNAVAILABLE_STATUS = 503;
|
|
13778
14081
|
var MAX_BULK_FILTERED_PRODUCTS = 10;
|
|
14082
|
+
function parseGrantPayment(options) {
|
|
14083
|
+
const { paid, currency, paidAt, paymentMethod, paymentNote } = options;
|
|
14084
|
+
if (paid === void 0) {
|
|
14085
|
+
if (currency || paidAt || paymentMethod || paymentNote) {
|
|
14086
|
+
throw new Error("--currency, --paid-at, --payment-method and --payment-note need --paid.");
|
|
14087
|
+
}
|
|
14088
|
+
return void 0;
|
|
14089
|
+
}
|
|
14090
|
+
if (!/^\d+([.,]\d{1,2})?$/.test(paid)) {
|
|
14091
|
+
throw new Error(`Invalid --paid amount: ${paid} (expected e.g. 99 or 99.50)`);
|
|
14092
|
+
}
|
|
14093
|
+
const amountCents = Math.round(Number(paid.replace(",", ".")) * 100);
|
|
14094
|
+
if (amountCents <= 0) {
|
|
14095
|
+
throw new Error("--paid must be above 0.");
|
|
14096
|
+
}
|
|
14097
|
+
if (!currency) {
|
|
14098
|
+
throw new Error("--paid needs --currency (pln, eur, usd or gbp).");
|
|
14099
|
+
}
|
|
14100
|
+
if (paidAt !== void 0 && Number.isNaN(Date.parse(paidAt))) {
|
|
14101
|
+
throw new Error(`Invalid --paid-at date: ${paidAt} (expected ISO, e.g. 2026-10-14)`);
|
|
14102
|
+
}
|
|
14103
|
+
if (paymentMethod !== void 0 && !GRANT_PAYMENT_METHODS.includes(paymentMethod)) {
|
|
14104
|
+
throw new Error(
|
|
14105
|
+
`Invalid --payment-method: ${paymentMethod} (one of ${GRANT_PAYMENT_METHODS.join(", ")})`
|
|
14106
|
+
);
|
|
14107
|
+
}
|
|
14108
|
+
return {
|
|
14109
|
+
amountCents,
|
|
14110
|
+
currency: currency.toLowerCase(),
|
|
14111
|
+
...paidAt !== void 0 ? { paidAt: new Date(paidAt).toISOString() } : {},
|
|
14112
|
+
...paymentMethod !== void 0 ? { method: paymentMethod } : {},
|
|
14113
|
+
...paymentNote !== void 0 ? { note: paymentNote } : {}
|
|
14114
|
+
};
|
|
14115
|
+
}
|
|
13779
14116
|
function requireConfirm4(confirm, what) {
|
|
13780
14117
|
if (!confirm) {
|
|
13781
14118
|
throw new Error(`Refusing to ${what} without --confirm. Re-run with --confirm to proceed.`);
|
|
@@ -14129,6 +14466,33 @@ Next page: --cursor ${page.nextCursor}
|
|
|
14129
14466
|
process.stdout.write(` Previous emails: ${detail.previousEmails.join(", ")}
|
|
14130
14467
|
`);
|
|
14131
14468
|
}
|
|
14469
|
+
const customFields = Object.entries(detail.customFields ?? {});
|
|
14470
|
+
if (customFields.length > 0) {
|
|
14471
|
+
process.stdout.write(" Custom fields:\n");
|
|
14472
|
+
for (const [key, value] of customFields) {
|
|
14473
|
+
process.stdout.write(` ${key}: ${String(value)}
|
|
14474
|
+
`);
|
|
14475
|
+
}
|
|
14476
|
+
}
|
|
14477
|
+
});
|
|
14478
|
+
});
|
|
14479
|
+
clients2.command("fields").description(
|
|
14480
|
+
"List the custom contact fields (key, name, type, options); set values with `clients update --field key=value`"
|
|
14481
|
+
).option("--include-archived", "also list archived fields (their values are read-only)").action(async (options, command) => {
|
|
14482
|
+
const flags = getGlobalFlags(command);
|
|
14483
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
14484
|
+
const result = await client.listClientCustomFields(options.includeArchived === true);
|
|
14485
|
+
emit(flags, result, () => {
|
|
14486
|
+
printTable(
|
|
14487
|
+
result.fields.map((field) => ({
|
|
14488
|
+
key: field.key,
|
|
14489
|
+
name: field.name,
|
|
14490
|
+
type: field.type,
|
|
14491
|
+
options: field.options.filter((option) => !option.archived).map((option) => `${option.key}=${option.label}`).join(", "),
|
|
14492
|
+
archived: field.archived ? "yes" : ""
|
|
14493
|
+
})),
|
|
14494
|
+
["key", "name", "type", "options", "archived"]
|
|
14495
|
+
);
|
|
14132
14496
|
});
|
|
14133
14497
|
});
|
|
14134
14498
|
registerClientTagsCommands(clients2);
|
|
@@ -14242,12 +14606,27 @@ Next page: --cursor ${page.nextCursor}
|
|
|
14242
14606
|
`);
|
|
14243
14607
|
});
|
|
14244
14608
|
});
|
|
14245
|
-
clients2.command("update").description("Update a client's profile (name, phone, language)").argument("<id>", "workspace client id (wcid)").option("--first-name <name>", "new first name").option("--last-name <name>", "new last name").option("--phone <number>", "new phone number").option("--language <code>", "new preferred language code (e.g. en, pl)").
|
|
14609
|
+
clients2.command("update").description("Update a client's profile (name, phone, language) and custom field values").argument("<id>", "workspace client id (wcid)").option("--first-name <name>", "new first name").option("--last-name <name>", "new last name").option("--phone <number>", "new phone number").option("--language <code>", "new preferred language code (e.g. en, pl)").option(
|
|
14610
|
+
"--field <key=value>",
|
|
14611
|
+
"set a custom field value (repeatable; keys from `clients fields`, e.g. --field plan=premium)",
|
|
14612
|
+
collectRepeatable2,
|
|
14613
|
+
[]
|
|
14614
|
+
).option("--clear-field <key>", "clear a custom field value (repeatable)", collectRepeatable2, []).action(
|
|
14246
14615
|
async (id, options, command) => {
|
|
14247
14616
|
const flags = getGlobalFlags(command);
|
|
14248
|
-
|
|
14617
|
+
const customFields = {};
|
|
14618
|
+
for (const entry of options.field) {
|
|
14619
|
+
const separator = entry.indexOf("=");
|
|
14620
|
+
if (separator <= 0) {
|
|
14621
|
+
throw new Error(`Invalid --field "${entry}": use key=value.`);
|
|
14622
|
+
}
|
|
14623
|
+
customFields[entry.slice(0, separator).trim()] = entry.slice(separator + 1);
|
|
14624
|
+
}
|
|
14625
|
+
for (const key of options.clearField) customFields[key.trim()] = null;
|
|
14626
|
+
const hasCustomFields = Object.keys(customFields).length > 0;
|
|
14627
|
+
if (options.firstName === void 0 && options.lastName === void 0 && options.phone === void 0 && options.language === void 0 && !hasCustomFields) {
|
|
14249
14628
|
throw new Error(
|
|
14250
|
-
"Nothing to update \u2014 pass at least one of --first-name, --last-name, --phone, --language."
|
|
14629
|
+
"Nothing to update \u2014 pass at least one of --first-name, --last-name, --phone, --language, --field, --clear-field."
|
|
14251
14630
|
);
|
|
14252
14631
|
}
|
|
14253
14632
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
@@ -14255,7 +14634,8 @@ Next page: --cursor ${page.nextCursor}
|
|
|
14255
14634
|
firstName: options.firstName,
|
|
14256
14635
|
lastName: options.lastName,
|
|
14257
14636
|
phoneNumber: options.phone,
|
|
14258
|
-
language: options.language
|
|
14637
|
+
language: options.language,
|
|
14638
|
+
...hasCustomFields ? { customFields } : {}
|
|
14259
14639
|
});
|
|
14260
14640
|
emit(flags, result, () => {
|
|
14261
14641
|
process.stdout.write(`Updated client ${result.clientId}.
|
|
@@ -14263,7 +14643,13 @@ Next page: --cursor ${page.nextCursor}
|
|
|
14263
14643
|
});
|
|
14264
14644
|
}
|
|
14265
14645
|
);
|
|
14266
|
-
clients2.command("grant").description("Grant a client access to a product without a purchase").argument("<productId>", "product id").requiredOption("--email <email>", "client email").option("--first-name <name>", "client first name").option("--last-name <name>", "client last name").option("--price <priceId>", "enroll under an existing price without billing").option("--plan <planId>", "plan to enroll in (multi-plan products)").option("--until <date>", "ISO date the access ends (free grants only); omit for lifetime").option("--welcome-email", "send the welcome/sign-in email (default: grant silently)").
|
|
14646
|
+
clients2.command("grant").description("Grant a client access to a product without a purchase").argument("<productId>", "product id").requiredOption("--email <email>", "client email").option("--first-name <name>", "client first name").option("--last-name <name>", "client last name").option("--price <priceId>", "enroll under an existing price without billing").option("--plan <planId>", "plan to enroll in (multi-plan products)").option("--until <date>", "ISO date the access ends (free grants only); omit for lifetime").option("--welcome-email", "send the welcome/sign-in email (default: grant silently)").option(
|
|
14647
|
+
"--run-purchase-automations",
|
|
14648
|
+
"also run the product's purchase automations, marked as a grant (default: off)"
|
|
14649
|
+
).option(
|
|
14650
|
+
"--paid <amount>",
|
|
14651
|
+
"the client already paid outside Zanfia (e.g. 99.50): records one paid order"
|
|
14652
|
+
).option("--currency <code>", "currency of --paid: pln, eur, usd or gbp").option("--paid-at <date>", "ISO date the client paid (default: now; not in the future)").option("--payment-method <method>", `how they paid: ${GRANT_PAYMENT_METHODS.join(" | ")}`).option("--payment-note <text>", "note on the payment, e.g. the proforma number (max 300)").action(
|
|
14267
14653
|
async (productId, options, command) => {
|
|
14268
14654
|
const flags = getGlobalFlags(command);
|
|
14269
14655
|
if (options.until !== void 0 && Number.isNaN(Date.parse(options.until))) {
|
|
@@ -14272,6 +14658,7 @@ Next page: --cursor ${page.nextCursor}
|
|
|
14272
14658
|
if (options.until !== void 0 && options.price !== void 0) {
|
|
14273
14659
|
throw new Error("--until applies to free grants only \u2014 omit --price.");
|
|
14274
14660
|
}
|
|
14661
|
+
const payment = parseGrantPayment(options);
|
|
14275
14662
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
14276
14663
|
const result = await client.grantClientAccess(productId, {
|
|
14277
14664
|
email: options.email,
|
|
@@ -14280,13 +14667,14 @@ Next page: --cursor ${page.nextCursor}
|
|
|
14280
14667
|
priceId: options.price,
|
|
14281
14668
|
planId: options.plan,
|
|
14282
14669
|
accessUntil: options.until,
|
|
14283
|
-
sendWelcomeEmail: options.welcomeEmail
|
|
14670
|
+
sendWelcomeEmail: options.welcomeEmail,
|
|
14671
|
+
runPurchaseAutomations: options.runPurchaseAutomations,
|
|
14672
|
+
...payment ? { payment } : {}
|
|
14284
14673
|
});
|
|
14285
14674
|
emit(flags, result, () => {
|
|
14286
14675
|
const kind = result.isFree ? "free" : "paid";
|
|
14287
14676
|
process.stdout.write(
|
|
14288
|
-
`Granted ${options.email} ${kind} access to product ${productId}
|
|
14289
|
-
`
|
|
14677
|
+
`Granted ${options.email} ${kind} access to product ${productId}` + (payment ? " and recorded the payment as a paid order" : "") + ".\n"
|
|
14290
14678
|
);
|
|
14291
14679
|
});
|
|
14292
14680
|
}
|
|
@@ -18615,7 +19003,7 @@ function printOverview(overview) {
|
|
|
18615
19003
|
const p = overview.payments;
|
|
18616
19004
|
const on = (value) => value ? "on" : "off";
|
|
18617
19005
|
process.stdout.write(
|
|
18618
|
-
`Payments: stripe=${on(p.stripe)} payU=${on(p.payU)} tpay=${on(p.tpay)} payNow=${on(p.payNow)} przelewy24=${on(p.przelewy24)} paypal=${on(p.paypal)} dodo=${on(p.dodo)} manual=${p.manual ? `on (${p.manual.name})` : "off"}
|
|
19006
|
+
`Payments: stripe=${on(p.stripe)} payU=${on(p.payU)} tpay=${on(p.tpay)} payNow=${on(p.payNow)} przelewy24=${on(p.przelewy24)} paypal=${on(p.paypal)} dodo=${on(p.dodo)} tubapay=${on(p.tubapay)} manual=${p.manual ? `on (${p.manual.name})` : "off"}
|
|
18619
19007
|
`
|
|
18620
19008
|
);
|
|
18621
19009
|
}
|
|
@@ -18890,7 +19278,7 @@ function readSecret2(label, value, fromStdin, fromEnv) {
|
|
|
18890
19278
|
function printPayments(payments) {
|
|
18891
19279
|
const on = (value) => value ? "on" : "off";
|
|
18892
19280
|
process.stdout.write(
|
|
18893
|
-
`Payments: stripe=${on(payments.stripe)} payu=${on(payments.payU)} tpay=${on(payments.tpay)} paynow=${on(payments.payNow)} przelewy24=${on(payments.przelewy24)} paypal=${on(payments.paypal)} dodo=${on(payments.dodo)} manual=${payments.manual ? "on" : "off"}
|
|
19281
|
+
`Payments: stripe=${on(payments.stripe)} payu=${on(payments.payU)} tpay=${on(payments.tpay)} paynow=${on(payments.payNow)} przelewy24=${on(payments.przelewy24)} paypal=${on(payments.paypal)} dodo=${on(payments.dodo)} tubapay=${on(payments.tubapay)} manual=${payments.manual ? "on" : "off"}
|
|
18894
19282
|
`
|
|
18895
19283
|
);
|
|
18896
19284
|
}
|
|
@@ -18969,6 +19357,11 @@ function buildGatewayInput(gateway, o) {
|
|
|
18969
19357
|
case "dodo":
|
|
18970
19358
|
input.apiKey = secret(o.apiKey, "--api-key");
|
|
18971
19359
|
break;
|
|
19360
|
+
case "tubapay":
|
|
19361
|
+
input.clientId = need(o.clientId, "--client-id");
|
|
19362
|
+
input.clientSecret = secret(o.clientSecret, "--client-secret");
|
|
19363
|
+
input.callbackSecret = secret(o.callbackSecret, "--callback-secret");
|
|
19364
|
+
break;
|
|
18972
19365
|
}
|
|
18973
19366
|
return input;
|
|
18974
19367
|
}
|
|
@@ -19009,11 +19402,11 @@ function registerIntegrationProviderCommands(integrations) {
|
|
|
19009
19402
|
}
|
|
19010
19403
|
);
|
|
19011
19404
|
const payments = integrations.command("payments").description(
|
|
19012
|
-
"Credential payment gateways: payu, tpay, przelewy24 (p24), paynow, paypal, dodo. Stripe connects in the dashboard (OAuth); the transfer method is `integrations manual-payment`."
|
|
19405
|
+
"Credential payment gateways: payu, tpay, przelewy24 (p24), paynow, paypal, dodo, tubapay. Stripe connects in the dashboard (OAuth); the transfer method is `integrations manual-payment`."
|
|
19013
19406
|
);
|
|
19014
19407
|
payments.command("connect <gateway>").description(
|
|
19015
19408
|
"Connect a gateway \u2014 credentials are verified with the gateway first. With --secrets-env every secret flag takes an environment variable NAME instead of the value."
|
|
19016
|
-
).requiredOption("--name <name>", "creator-facing label").option("--pos-id <id>", "payu / tpay / przelewy24").option("--client-id <id>", "payu / tpay / paypal").option("--client-secret <secret>", "payu / tpay / paypal").option("--second-key <key>", "payu: the second (MD5) key").option("--safety-key <key>", "tpay: the security key").option("--api-key <key>", "przelewy24 / paynow / dodo").option("--signature-key <key>", "paynow").option("--merchant-id <id>", "przelewy24").option("--crc-key <key>", "przelewy24").option("--secrets-env", "treat every secret flag value as an environment variable name").action(async (gatewayArg, options, command) => {
|
|
19409
|
+
).requiredOption("--name <name>", "creator-facing label").option("--pos-id <id>", "payu / tpay / przelewy24").option("--client-id <id>", "payu / tpay / paypal; tubapay: the Partner ID").option("--client-secret <secret>", "payu / tpay / paypal / tubapay").option("--second-key <key>", "payu: the second (MD5) key").option("--safety-key <key>", "tpay: the security key").option("--api-key <key>", "przelewy24 / paynow / dodo").option("--signature-key <key>", "paynow").option("--merchant-id <id>", "przelewy24").option("--crc-key <key>", "przelewy24").option("--callback-secret <key>", "tubapay: the key TubaPay signs its notifications with").option("--secrets-env", "treat every secret flag value as an environment variable name").action(async (gatewayArg, options, command) => {
|
|
19017
19410
|
const flags = getGlobalFlags(command);
|
|
19018
19411
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
19019
19412
|
const status = await client.connectPaymentGateway(
|
|
@@ -19591,15 +19984,28 @@ join again to refresh it.
|
|
|
19591
19984
|
// src/commands/liveRooms/index.ts
|
|
19592
19985
|
var minutes = (seconds) => `${Math.round(seconds / 60)} min`;
|
|
19593
19986
|
var percent = (part, whole) => whole > 0 ? `${Math.round(part / whole * 100)}%` : "-";
|
|
19987
|
+
var countOrDash = (value) => value === null || value === void 0 ? "-" : String(value);
|
|
19594
19988
|
var WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
|
19989
|
+
function describeExclusions(schedule) {
|
|
19990
|
+
const parts = [];
|
|
19991
|
+
if (schedule.kind !== "daily" && schedule.nightWindow) {
|
|
19992
|
+
parts.push(`none ${schedule.nightWindow.from}-${schedule.nightWindow.to}`);
|
|
19993
|
+
}
|
|
19994
|
+
if (schedule.blackoutDates && schedule.blackoutDates.length > 0) {
|
|
19995
|
+
const days = schedule.blackoutDates.map((range) => range.from === range.to ? range.from : `${range.from}..${range.to}`).join(", ");
|
|
19996
|
+
parts.push(`none on ${days}`);
|
|
19997
|
+
}
|
|
19998
|
+
return parts.length > 0 ? `; ${parts.join("; ")}` : "";
|
|
19999
|
+
}
|
|
19595
20000
|
function describeSchedule(room) {
|
|
19596
20001
|
if (!room.schedule) return "fixed";
|
|
19597
20002
|
const until = room.schedule.until ? ` until ${room.schedule.until}` : "";
|
|
20003
|
+
const exclusions = describeExclusions(room.schedule);
|
|
19598
20004
|
if (room.schedule.kind === "daily") {
|
|
19599
20005
|
const days = room.schedule.weekdays ? room.schedule.weekdays.map((day) => WEEKDAYS[day - 1] ?? String(day)).join(", ") : "daily";
|
|
19600
|
-
return `${days} at ${room.schedule.times.join(", ")} (${room.timezone})${until}`;
|
|
20006
|
+
return `${days} at ${room.schedule.times.join(", ")} (${room.timezone})${until}${exclusions}`;
|
|
19601
20007
|
}
|
|
19602
|
-
return `every ${room.schedule.everyMinutes} min${until}`;
|
|
20008
|
+
return `every ${room.schedule.everyMinutes} min${until}${exclusions}`;
|
|
19603
20009
|
}
|
|
19604
20010
|
function registerLiveRoomsCommands(program2) {
|
|
19605
20011
|
const liveRooms = program2.command("live-rooms").description(
|
|
@@ -19632,6 +20038,7 @@ function registerLiveRoomsCommands(program2) {
|
|
|
19632
20038
|
printTable(
|
|
19633
20039
|
result.sessions.map((session) => ({
|
|
19634
20040
|
session: session.sessionStartAt,
|
|
20041
|
+
registered: countOrDash(session.registered),
|
|
19635
20042
|
attendees: session.attendees,
|
|
19636
20043
|
identified: session.identified,
|
|
19637
20044
|
avgWatched: `${minutes(session.averageWatchedSeconds)} (${percent(
|
|
@@ -19640,10 +20047,29 @@ function registerLiveRoomsCommands(program2) {
|
|
|
19640
20047
|
)})`,
|
|
19641
20048
|
toEnd: `${session.watchedToEnd} (${percent(session.watchedToEnd, session.attendees)})`,
|
|
19642
20049
|
sawOffer: session.sawOffer,
|
|
19643
|
-
clickedOffer: session.clickedOffer
|
|
20050
|
+
clickedOffer: session.clickedOffer,
|
|
20051
|
+
bought: countOrDash(session.purchased)
|
|
19644
20052
|
})),
|
|
19645
|
-
[
|
|
20053
|
+
[
|
|
20054
|
+
"session",
|
|
20055
|
+
"registered",
|
|
20056
|
+
"attendees",
|
|
20057
|
+
"identified",
|
|
20058
|
+
"avgWatched",
|
|
20059
|
+
"toEnd",
|
|
20060
|
+
"sawOffer",
|
|
20061
|
+
"clickedOffer",
|
|
20062
|
+
"bought"
|
|
20063
|
+
]
|
|
19646
20064
|
);
|
|
20065
|
+
const totals = result.totals;
|
|
20066
|
+
if (totals) {
|
|
20067
|
+
process.stdout.write(
|
|
20068
|
+
`
|
|
20069
|
+
Room total (${totals.sessions} started sessions): registered ${countOrDash(totals.registered)} \u2192 attended ${totals.attendees} \u2192 saw the offer ${totals.sawOffer} \u2192 clicked ${totals.clickedOffer} \u2192 bought ${countOrDash(totals.purchased)}${totals.capped ? " (at least: a read hit its cap)" : ""}
|
|
20070
|
+
`
|
|
20071
|
+
);
|
|
20072
|
+
}
|
|
19647
20073
|
if (options.session !== void 0) {
|
|
19648
20074
|
process.stdout.write(`
|
|
19649
20075
|
Viewers of ${options.session}:
|
|
@@ -19905,7 +20331,7 @@ var NoopUrlStorage = /* @__PURE__ */ (function() {
|
|
|
19905
20331
|
}]);
|
|
19906
20332
|
})();
|
|
19907
20333
|
|
|
19908
|
-
// ../../node_modules/
|
|
20334
|
+
// ../../node_modules/js-base64/base64.mjs
|
|
19909
20335
|
var version = "3.7.8";
|
|
19910
20336
|
var VERSION = version;
|
|
19911
20337
|
var _hasBuffer = typeof Buffer === "function";
|
|
@@ -25028,6 +25454,7 @@ var PAGE_INTENTS = [
|
|
|
25028
25454
|
var SOURCE_SITE_MODES = ["rebuild", "inspired"];
|
|
25029
25455
|
var EVENT_MODES2 = ["live", "on_demand"];
|
|
25030
25456
|
var OPT_IN_MODES2 = ["single", "double"];
|
|
25457
|
+
var TRACKING_MODES = ["inherit", "selected", "none"];
|
|
25031
25458
|
var SCHEDULER_UNAVAILABLE_STATUS3 = 503;
|
|
25032
25459
|
function parseChoice5(value, allowed, flag) {
|
|
25033
25460
|
const match = allowed.find((candidate) => candidate === value);
|
|
@@ -25164,6 +25591,11 @@ function registerPagesCommands(program2) {
|
|
|
25164
25591
|
`);
|
|
25165
25592
|
process.stdout.write(` Cookie banner: ${page.hideCookieBanner ? "hidden" : "shown"}
|
|
25166
25593
|
`);
|
|
25594
|
+
const trackingIds = page.tracking?.integrationIds ?? [];
|
|
25595
|
+
process.stdout.write(
|
|
25596
|
+
` Tracking: ${page.tracking?.mode ?? "inherit"}${trackingIds.length > 0 ? ` (${trackingIds.join(", ")})` : ""}
|
|
25597
|
+
`
|
|
25598
|
+
);
|
|
25167
25599
|
});
|
|
25168
25600
|
});
|
|
25169
25601
|
pages.command("create").description("Create a hosted page from an HTML file or inline HTML").requiredOption("--slug <slug>", "public URL slug (served at /page/<slug>)").requiredOption("--title <title>", "page title").option("--file <path>", "path to an .html file to host").option("--html <html>", "inline HTML body (alternative to --file)").option("--publish", "publish immediately instead of saving as draft").action(async (options, command) => {
|
|
@@ -25185,17 +25617,33 @@ function registerPagesCommands(program2) {
|
|
|
25185
25617
|
`);
|
|
25186
25618
|
});
|
|
25187
25619
|
});
|
|
25188
|
-
pages.command("update").description(
|
|
25620
|
+
pages.command("update").description(
|
|
25621
|
+
"Update a page (slug, title, HTML body, cookie-banner visibility, or its tracking tools)"
|
|
25622
|
+
).argument("<id>", "page id").option("--slug <slug>", "new slug").option("--title <title>", "new title").option("--file <path>", "path to a new .html file").option("--html <html>", "new inline HTML body").option("--cookie-banner", "show the workspace cookie banner on this page again").option(
|
|
25189
25623
|
"--no-cookie-banner",
|
|
25190
25624
|
"hide the workspace cookie banner on this page (pixels still wait for consent given elsewhere)"
|
|
25625
|
+
).option(
|
|
25626
|
+
"--tracking <mode>",
|
|
25627
|
+
"tracking tools on this page: inherit (workspace defaults), selected (--tracking-integrations) or none"
|
|
25628
|
+
).option(
|
|
25629
|
+
"--tracking-integrations <ids>",
|
|
25630
|
+
"comma-separated tracking integration ids for --tracking selected (one per platform)"
|
|
25191
25631
|
).action(async (id, options, command) => {
|
|
25192
25632
|
const flags = getGlobalFlags(command);
|
|
25193
25633
|
const html = await resolveHtml2(options);
|
|
25634
|
+
const trackingIds = options.trackingIntegrations?.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
|
|
25635
|
+
const trackingMode = options.tracking !== void 0 ? parseChoice5(options.tracking, TRACKING_MODES, "--tracking") : trackingIds !== void 0 ? "selected" : void 0;
|
|
25194
25636
|
const input = {
|
|
25195
25637
|
slug: options.slug,
|
|
25196
25638
|
title: options.title,
|
|
25197
25639
|
html,
|
|
25198
|
-
...options.cookieBanner !== void 0 ? { hideCookieBanner: !options.cookieBanner } : {}
|
|
25640
|
+
...options.cookieBanner !== void 0 ? { hideCookieBanner: !options.cookieBanner } : {},
|
|
25641
|
+
...trackingMode !== void 0 ? {
|
|
25642
|
+
tracking: {
|
|
25643
|
+
mode: trackingMode,
|
|
25644
|
+
...trackingIds !== void 0 ? { integrationIds: trackingIds } : {}
|
|
25645
|
+
}
|
|
25646
|
+
} : {}
|
|
25199
25647
|
};
|
|
25200
25648
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
25201
25649
|
const result = await client.updatePage(id, input);
|
|
@@ -25599,6 +26047,10 @@ function registerProductsCommands(program2) {
|
|
|
25599
26047
|
`);
|
|
25600
26048
|
if (product.storefrontVisibility) {
|
|
25601
26049
|
process.stdout.write(` Visibility: ${product.storefrontVisibility}
|
|
26050
|
+
`);
|
|
26051
|
+
}
|
|
26052
|
+
if (product.language !== void 0) {
|
|
26053
|
+
process.stdout.write(` Language: ${product.language ?? "none (platform language)"}
|
|
25602
26054
|
`);
|
|
25603
26055
|
}
|
|
25604
26056
|
process.stdout.write(` Clients: ${product.clientCount}
|
|
@@ -25618,6 +26070,10 @@ function registerProductsCommands(program2) {
|
|
|
25618
26070
|
"--period <period>",
|
|
25619
26071
|
"billing period for --amount: one_time, monthly or yearly (default one_time)"
|
|
25620
26072
|
).option("--description-file <path>", "read the product description from a file").option(
|
|
26073
|
+
"--language <pl|en>",
|
|
26074
|
+
"the product's language (buyer-facing pages and e-mails; omitted = the platform's language). Also picks the --amount currency when --currency is omitted (pl \u2192 pln, else usd)",
|
|
26075
|
+
parseProductLanguage
|
|
26076
|
+
).option(
|
|
25621
26077
|
"--cover <pathOrUrl>",
|
|
25622
26078
|
"cover image: a local file (uploaded to the media library first) or an https URL"
|
|
25623
26079
|
).option(
|
|
@@ -25638,7 +26094,8 @@ function registerProductsCommands(program2) {
|
|
|
25638
26094
|
name: options.name,
|
|
25639
26095
|
productType: options.type,
|
|
25640
26096
|
description,
|
|
25641
|
-
offer
|
|
26097
|
+
offer,
|
|
26098
|
+
language: options.language
|
|
25642
26099
|
});
|
|
25643
26100
|
const outcome = {};
|
|
25644
26101
|
try {
|
|
@@ -25687,21 +26144,28 @@ function registerProductsCommands(program2) {
|
|
|
25687
26144
|
});
|
|
25688
26145
|
}
|
|
25689
26146
|
);
|
|
25690
|
-
products2.command("update").description("Update a product's name, description and/or
|
|
26147
|
+
products2.command("update").description("Update a product's name, description, storefront visibility and/or language").argument("<id>", "product id").option("--name <name>", "new product name").option("--description <description>", "new product description").option(
|
|
25691
26148
|
"--visibility <public|unlisted|private>",
|
|
25692
26149
|
"where buyers find it once published: public (store + product page), unlisted (product page by link only), private (checkout links only)",
|
|
25693
26150
|
parseStorefrontVisibility
|
|
26151
|
+
).option(
|
|
26152
|
+
"--language <pl|en|none>",
|
|
26153
|
+
"the product's language (buyer-facing pages and e-mails); none clears it (the platform's language applies)",
|
|
26154
|
+
parseProductLanguageOrNone
|
|
25694
26155
|
).action(
|
|
25695
26156
|
async (id, options, command) => {
|
|
25696
|
-
if (options.name === void 0 && options.description === void 0 && options.visibility === void 0) {
|
|
25697
|
-
throw new Error(
|
|
26157
|
+
if (options.name === void 0 && options.description === void 0 && options.visibility === void 0 && options.language === void 0) {
|
|
26158
|
+
throw new Error(
|
|
26159
|
+
"Nothing to update: pass --name, --description, --visibility and/or --language."
|
|
26160
|
+
);
|
|
25698
26161
|
}
|
|
25699
26162
|
const flags = getGlobalFlags(command);
|
|
25700
26163
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
25701
26164
|
const result = await client.updateProduct(id, {
|
|
25702
26165
|
name: options.name,
|
|
25703
26166
|
description: options.description,
|
|
25704
|
-
storefrontVisibility: options.visibility
|
|
26167
|
+
storefrontVisibility: options.visibility,
|
|
26168
|
+
language: options.language === PRODUCT_LANGUAGE_NONE ? null : options.language
|
|
25705
26169
|
});
|
|
25706
26170
|
emit(flags, result, () => {
|
|
25707
26171
|
process.stdout.write(`Updated product ${result.productId}
|
|
@@ -26711,7 +27175,7 @@ function buildUpdateInput2(options) {
|
|
|
26711
27175
|
}
|
|
26712
27176
|
function registerReferralsCommands(program2) {
|
|
26713
27177
|
const referrals = program2.command("referrals").description(
|
|
26714
|
-
|
|
27178
|
+
'Referral programs: "Nagrody za polecenia" (kind referral, reward ladders for your customers) and "Program partnerski" (kind affiliate, partner commission), both from the Growth plan. Create programs over your products, enroll clients, approve or reject earned rewards, and retry failed reward deliveries'
|
|
26715
27179
|
);
|
|
26716
27180
|
referrals.command("list").description("List referral programs (kind, status, products, headline stats)").action(async (_, command) => {
|
|
26717
27181
|
const flags = getGlobalFlags(command);
|
|
@@ -26810,7 +27274,7 @@ Failed deliveries (${result.failedDeliveries.length})
|
|
|
26810
27274
|
});
|
|
26811
27275
|
});
|
|
26812
27276
|
referrals.command("create").description(
|
|
26813
|
-
"Create a program over one or more products. Deep config (milestones, commission, attribution) goes in --json; flat flags win over --json keys."
|
|
27277
|
+
"Create a program over one or more products (both kinds need the Growth plan). Deep config (milestones, commission, attribution) goes in --json; flat flags win over --json keys."
|
|
26814
27278
|
).requiredOption("--kind <kind>", `program kind (${PROGRAM_KINDS.join(" | ")})`).requiredOption("--name <name>", "program name").requiredOption("--product <ids>", "comma-separated product ids whose purchases count").option("--enrollment <mode>", `how participants join (${ENROLLMENT_MODES.join(" | ")})`).option("--status <status>", `initial status (${WRITABLE_STATUSES.join(" | ")})`).option(
|
|
26815
27279
|
"--json <fileOrJson>",
|
|
26816
27280
|
"inline JSON object or .json path with `milestones`, `commission` and/or `attribution` (write it as --json=<value> \u2014 a bare --json is the global JSON-output flag)"
|
|
@@ -26825,7 +27289,7 @@ Failed deliveries (${result.failedDeliveries.length})
|
|
|
26825
27289
|
});
|
|
26826
27290
|
});
|
|
26827
27291
|
referrals.command("update").description(
|
|
26828
|
-
"Update a program. --product and --json `milestones` are FULL replaces (read the program first); flat flags win over --json keys. Use `referrals archive` to archive."
|
|
27292
|
+
"Update a program. --product and --json `milestones` are FULL replaces (read the program first); flat flags win over --json keys. Activating (--status active) needs the Growth plan. Use `referrals archive` to archive."
|
|
26829
27293
|
).argument("<programId>", "program id").option("--name <name>", "program name").option("--status <status>", `status (${WRITABLE_STATUSES.join(" | ")})`).option("--product <ids>", "comma-separated product ids (full replace)").option("--enrollment <mode>", `how participants join (${ENROLLMENT_MODES.join(" | ")})`).option(
|
|
26830
27294
|
"--json <fileOrJson>",
|
|
26831
27295
|
"inline JSON object or .json path with `milestones`, `commission` and/or `attribution` (write it as --json=<value> \u2014 a bare --json is the global JSON-output flag)"
|
|
@@ -26853,7 +27317,7 @@ Failed deliveries (${result.failedDeliveries.length})
|
|
|
26853
27317
|
});
|
|
26854
27318
|
});
|
|
26855
27319
|
referrals.command("enroll").description(
|
|
26856
|
-
"Enroll one client as a participant (idempotent \u2014 re-enrolling reports `alreadyEnrolled`). Prints the ref code the client shares."
|
|
27320
|
+
"Enroll one client as a participant (idempotent \u2014 re-enrolling reports `alreadyEnrolled`). Prints the ref code the client shares. Needs the Growth plan."
|
|
26857
27321
|
).argument("<programId>", "program id").requiredOption("--client <clientId>", "workspace client id (wcid)").requiredOption("--email <email>", "the client's email").option("--first-name <name>", "first name (optional)").option("--last-name <name>", "last name (optional)").action(async (programId, options, command) => {
|
|
26858
27322
|
const flags = getGlobalFlags(command);
|
|
26859
27323
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
@@ -26872,7 +27336,7 @@ Failed deliveries (${result.failedDeliveries.length})
|
|
|
26872
27336
|
});
|
|
26873
27337
|
});
|
|
26874
27338
|
referrals.command("enroll-all").description(
|
|
26875
|
-
|
|
27339
|
+
'Enroll EVERY client with access to one of the program products ("Nagrody za polecenia" only: a "Program partnerski" is refused, partners join by invite link). Needs the Growth plan. Requires --confirm. Reports enrolled / alreadyEnrolled / skippedWithoutAccount counts.'
|
|
26876
27340
|
).argument("<programId>", "program id").option("--confirm", "required \u2014 acknowledge the bulk enrollment").action(async (programId, options, command) => {
|
|
26877
27341
|
const flags = getGlobalFlags(command);
|
|
26878
27342
|
requireConfirm13(options.confirm, `enroll all clients in program ${programId}`);
|
|
@@ -28365,7 +28829,8 @@ var CONVERSION_EVENT_STATUSES = [
|
|
|
28365
28829
|
"sent",
|
|
28366
28830
|
"delivered",
|
|
28367
28831
|
"failed",
|
|
28368
|
-
"consent_blocked"
|
|
28832
|
+
"consent_blocked",
|
|
28833
|
+
"test_skipped"
|
|
28369
28834
|
];
|
|
28370
28835
|
var CONVERSION_TRACKING_CLIENTS = [
|
|
28371
28836
|
"metaConversionApi",
|
|
@@ -28468,12 +28933,12 @@ function registerStatsCommands(program2) {
|
|
|
28468
28933
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
28469
28934
|
const report = await client.statsSales(range);
|
|
28470
28935
|
emit(flags, report, () => {
|
|
28471
|
-
const c = report.
|
|
28936
|
+
const c = report.currency;
|
|
28472
28937
|
process.stdout.write(
|
|
28473
28938
|
`Sales (${report.period.from} \u2192 ${report.period.to}), currency ${c}
|
|
28474
|
-
Net revenue: ${money(report.
|
|
28939
|
+
Net revenue: ${money(report.totalRevenueCents)} ${c}
|
|
28475
28940
|
Transactions: ${report.transactions} Refunds: ${report.refunds}
|
|
28476
|
-
AOV: ${money(report.
|
|
28941
|
+
AOV: ${money(report.averageOrderValueCents)} ${c} New subscribers: ${report.newSubscribers}
|
|
28477
28942
|
|
|
28478
28943
|
`
|
|
28479
28944
|
);
|
|
@@ -28483,7 +28948,7 @@ function registerStatsCommands(program2) {
|
|
|
28483
28948
|
report.perProduct.map((r) => ({
|
|
28484
28949
|
id: r.id,
|
|
28485
28950
|
name: r.name,
|
|
28486
|
-
revenue: money(r.
|
|
28951
|
+
revenue: money(r.revenueCents),
|
|
28487
28952
|
transactions: r.transactions,
|
|
28488
28953
|
refunds: r.refunds
|
|
28489
28954
|
})),
|
|
@@ -28497,11 +28962,11 @@ function registerStatsCommands(program2) {
|
|
|
28497
28962
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
28498
28963
|
const report = await client.statsRecurring(options.currency);
|
|
28499
28964
|
emit(flags, report, () => {
|
|
28500
|
-
const c = report.
|
|
28965
|
+
const c = report.currency;
|
|
28501
28966
|
process.stdout.write(
|
|
28502
28967
|
`Recurring revenue (currency ${c})
|
|
28503
|
-
MRR: ${money(report.
|
|
28504
|
-
Active subscriptions: ${report.
|
|
28968
|
+
MRR: ${money(report.mrrCents)} ${c} ARR: ${money(report.arrCents)} ${c}
|
|
28969
|
+
Active subscriptions: ${report.activeSubscriptions} Ending soon: ${report.endingSoonCount}
|
|
28505
28970
|
|
|
28506
28971
|
`
|
|
28507
28972
|
);
|
|
@@ -28510,8 +28975,8 @@ function registerStatsCommands(program2) {
|
|
|
28510
28975
|
report.perProduct.map((r) => ({
|
|
28511
28976
|
id: r.productId,
|
|
28512
28977
|
name: r.productName,
|
|
28513
|
-
mrr: money(r.
|
|
28514
|
-
subs: r.
|
|
28978
|
+
mrr: money(r.mrrCents),
|
|
28979
|
+
subs: r.activeSubscriptions
|
|
28515
28980
|
})),
|
|
28516
28981
|
["id", "name", "mrr", "subs"]
|
|
28517
28982
|
);
|
|
@@ -28526,13 +28991,13 @@ function registerStatsCommands(program2) {
|
|
|
28526
28991
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
28527
28992
|
const report = await client.statsMrrHistory(range);
|
|
28528
28993
|
emit(flags, report, () => {
|
|
28529
|
-
process.stdout.write(`MRR history (currency ${report.
|
|
28994
|
+
process.stdout.write(`MRR history (currency ${report.currency})
|
|
28530
28995
|
`);
|
|
28531
28996
|
printTable(
|
|
28532
28997
|
report.points.map((p) => ({
|
|
28533
28998
|
date: p.date,
|
|
28534
28999
|
mrr: money(p.mrrCents),
|
|
28535
|
-
subs: p.
|
|
29000
|
+
subs: p.activeSubscriptions,
|
|
28536
29001
|
new: money(p.newMrrCents),
|
|
28537
29002
|
churned: money(p.churnedMrrCents),
|
|
28538
29003
|
churnRate: `${(p.churnRate * 100).toFixed(1)}%`
|
|
@@ -28612,11 +29077,23 @@ function registerStatsCommands(program2) {
|
|
|
28612
29077
|
checkout: s.checkoutId,
|
|
28613
29078
|
stage: s.furthestStage,
|
|
28614
29079
|
status: s.status,
|
|
28615
|
-
|
|
28616
|
-
source: s.source ?? "",
|
|
29080
|
+
converted: s.converted ? "yes" : "",
|
|
29081
|
+
source: s.source.utmSource ?? s.source.referrer ?? "",
|
|
29082
|
+
country: s.country ?? "",
|
|
29083
|
+
declined: s.paymentFailure?.reason ?? "",
|
|
28617
29084
|
startedAt: s.startedAt
|
|
28618
29085
|
})),
|
|
28619
|
-
[
|
|
29086
|
+
[
|
|
29087
|
+
"id",
|
|
29088
|
+
"checkout",
|
|
29089
|
+
"stage",
|
|
29090
|
+
"status",
|
|
29091
|
+
"converted",
|
|
29092
|
+
"source",
|
|
29093
|
+
"country",
|
|
29094
|
+
"declined",
|
|
29095
|
+
"startedAt"
|
|
29096
|
+
]
|
|
28620
29097
|
);
|
|
28621
29098
|
});
|
|
28622
29099
|
});
|
|
@@ -28625,7 +29102,7 @@ function registerStatsCommands(program2) {
|
|
|
28625
29102
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
28626
29103
|
const report = await client.statsProduct(productId, options.currency);
|
|
28627
29104
|
emit(flags, report, () => {
|
|
28628
|
-
const c = report.
|
|
29105
|
+
const c = report.currency;
|
|
28629
29106
|
process.stdout.write(
|
|
28630
29107
|
`Product ${report.productId} (${report.window.startDate} \u2192 ${report.window.endDate}), currency ${c}
|
|
28631
29108
|
Revenue: ${money(report.revenue.totalCents)} ${c} (prev ${money(report.revenue.previousTotalCents)} ${c})
|
|
@@ -30286,8 +30763,8 @@ function applyShortHelp(program2) {
|
|
|
30286
30763
|
|
|
30287
30764
|
// src/program.ts
|
|
30288
30765
|
function resolveCliVersion() {
|
|
30289
|
-
if ("0.
|
|
30290
|
-
return "0.
|
|
30766
|
+
if ("0.10.0".length > 0) {
|
|
30767
|
+
return "0.10.0";
|
|
30291
30768
|
}
|
|
30292
30769
|
try {
|
|
30293
30770
|
const raw = readFileSync28(new URL("../package.json", import.meta.url), "utf8");
|