@siteoshq/cli 2.6.1 → 2.7.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/README.md +27 -1
- package/dist/cli.js +1223 -747
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -17,7 +17,7 @@ __export(cookie_verification_exports, {
|
|
|
17
17
|
verificationTarget: () => verificationTarget,
|
|
18
18
|
verifyCookieWebsite: () => verifyCookieWebsite
|
|
19
19
|
});
|
|
20
|
-
import { z as
|
|
20
|
+
import { z as z13 } from "zod";
|
|
21
21
|
function verificationTarget(websiteUrl, candidate) {
|
|
22
22
|
const website = new URL(websiteUrl);
|
|
23
23
|
const target = new URL(candidate ?? websiteUrl, website);
|
|
@@ -322,10 +322,10 @@ async function verifyCookieWebsite(input) {
|
|
|
322
322
|
detail: "The request limit was reached; this run is incomplete."
|
|
323
323
|
});
|
|
324
324
|
const config = envelope;
|
|
325
|
-
const
|
|
325
|
+
const status2 = issues.length ? "failed" : assessment.unknownOrigins.length || assessment.unknownStorage.length || config?.config.integrations.googleConsentMode === "advanced" ? "needs-review" : "passed";
|
|
326
326
|
return {
|
|
327
327
|
schemaVersion: 1,
|
|
328
|
-
status,
|
|
328
|
+
status: status2,
|
|
329
329
|
startedAt,
|
|
330
330
|
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
331
331
|
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1e3).toISOString(),
|
|
@@ -367,73 +367,73 @@ var Names, Service, Envelope, State, VerificationInstallation;
|
|
|
367
367
|
var init_cookie_verification = __esm({
|
|
368
368
|
"src/services/cookie-verification.ts"() {
|
|
369
369
|
"use strict";
|
|
370
|
-
Names =
|
|
371
|
-
exact:
|
|
372
|
-
prefixes:
|
|
370
|
+
Names = z13.object({
|
|
371
|
+
exact: z13.array(z13.string()),
|
|
372
|
+
prefixes: z13.array(z13.string())
|
|
373
373
|
});
|
|
374
|
-
Service =
|
|
375
|
-
key:
|
|
376
|
-
purposeKey:
|
|
377
|
-
lifecycle:
|
|
378
|
-
scriptOrigins:
|
|
379
|
-
iframeOrigins:
|
|
380
|
-
pixelOrigins:
|
|
374
|
+
Service = z13.object({
|
|
375
|
+
key: z13.string(),
|
|
376
|
+
purposeKey: z13.string(),
|
|
377
|
+
lifecycle: z13.object({
|
|
378
|
+
scriptOrigins: z13.array(z13.string()),
|
|
379
|
+
iframeOrigins: z13.array(z13.string()),
|
|
380
|
+
pixelOrigins: z13.array(z13.string()),
|
|
381
381
|
firstPartyCookies: Names,
|
|
382
382
|
localStorage: Names
|
|
383
383
|
}).optional()
|
|
384
384
|
});
|
|
385
|
-
Envelope =
|
|
386
|
-
publicKey:
|
|
387
|
-
revision:
|
|
388
|
-
ruleKey:
|
|
389
|
-
profileKey:
|
|
390
|
-
trustedSignals:
|
|
391
|
-
region:
|
|
392
|
-
countryCode:
|
|
393
|
-
subdivisionCode:
|
|
385
|
+
Envelope = z13.object({
|
|
386
|
+
publicKey: z13.string(),
|
|
387
|
+
revision: z13.number(),
|
|
388
|
+
ruleKey: z13.string(),
|
|
389
|
+
profileKey: z13.string(),
|
|
390
|
+
trustedSignals: z13.object({
|
|
391
|
+
region: z13.object({
|
|
392
|
+
countryCode: z13.string().nullable(),
|
|
393
|
+
subdivisionCode: z13.string().nullable()
|
|
394
394
|
}).optional()
|
|
395
395
|
}).optional(),
|
|
396
|
-
config:
|
|
397
|
-
services:
|
|
398
|
-
categories:
|
|
399
|
-
integrations:
|
|
396
|
+
config: z13.object({
|
|
397
|
+
services: z13.array(Service),
|
|
398
|
+
categories: z13.array(z13.object({ key: z13.string(), required: z13.boolean() })),
|
|
399
|
+
integrations: z13.object({ googleConsentMode: z13.string() })
|
|
400
400
|
})
|
|
401
401
|
});
|
|
402
|
-
State =
|
|
403
|
-
runtimeVersion:
|
|
404
|
-
preview:
|
|
405
|
-
resolved:
|
|
406
|
-
publicKey:
|
|
407
|
-
revision:
|
|
408
|
-
ruleKey:
|
|
409
|
-
profileKey:
|
|
402
|
+
State = z13.object({
|
|
403
|
+
runtimeVersion: z13.string(),
|
|
404
|
+
preview: z13.boolean(),
|
|
405
|
+
resolved: z13.object({
|
|
406
|
+
publicKey: z13.string(),
|
|
407
|
+
revision: z13.number(),
|
|
408
|
+
ruleKey: z13.string(),
|
|
409
|
+
profileKey: z13.string()
|
|
410
410
|
}).nullable(),
|
|
411
|
-
consent:
|
|
412
|
-
categories:
|
|
413
|
-
decision:
|
|
414
|
-
globalPrivacyControl:
|
|
415
|
-
privacyChoices:
|
|
416
|
-
saleOrShareOptOut:
|
|
417
|
-
targetedAdvertisingOptOut:
|
|
411
|
+
consent: z13.object({
|
|
412
|
+
categories: z13.array(z13.string()),
|
|
413
|
+
decision: z13.string(),
|
|
414
|
+
globalPrivacyControl: z13.boolean(),
|
|
415
|
+
privacyChoices: z13.object({
|
|
416
|
+
saleOrShareOptOut: z13.boolean(),
|
|
417
|
+
targetedAdvertisingOptOut: z13.boolean()
|
|
418
418
|
})
|
|
419
419
|
}),
|
|
420
|
-
services:
|
|
421
|
-
|
|
422
|
-
key:
|
|
423
|
-
allowed:
|
|
424
|
-
footprintPresent:
|
|
420
|
+
services: z13.array(
|
|
421
|
+
z13.object({
|
|
422
|
+
key: z13.string(),
|
|
423
|
+
allowed: z13.boolean(),
|
|
424
|
+
footprintPresent: z13.boolean()
|
|
425
425
|
})
|
|
426
426
|
)
|
|
427
427
|
});
|
|
428
|
-
VerificationInstallation =
|
|
429
|
-
publicKey:
|
|
430
|
-
currentPublishedRevision:
|
|
431
|
-
delivery:
|
|
432
|
-
runtimeUrl:
|
|
433
|
-
configUrl:
|
|
434
|
-
analyticsUrl:
|
|
435
|
-
receiptsUrl:
|
|
436
|
-
handshakeUrl:
|
|
428
|
+
VerificationInstallation = z13.object({
|
|
429
|
+
publicKey: z13.string(),
|
|
430
|
+
currentPublishedRevision: z13.number().int().positive(),
|
|
431
|
+
delivery: z13.object({
|
|
432
|
+
runtimeUrl: z13.string().url(),
|
|
433
|
+
configUrl: z13.string().url(),
|
|
434
|
+
analyticsUrl: z13.string().url().nullable(),
|
|
435
|
+
receiptsUrl: z13.string().url(),
|
|
436
|
+
handshakeUrl: z13.string().url()
|
|
437
437
|
})
|
|
438
438
|
});
|
|
439
439
|
}
|
|
@@ -1506,7 +1506,7 @@ var OverviewSchema = z4.object({
|
|
|
1506
1506
|
});
|
|
1507
1507
|
function createProjectApi(input) {
|
|
1508
1508
|
const origin = resolveSiteOSAuthBaseUrl(input.env);
|
|
1509
|
-
async function request(
|
|
1509
|
+
async function request(path31, schema, body, method) {
|
|
1510
1510
|
const scope = body === void 0 ? "projects:workspace:read" : "projects:workspace:write";
|
|
1511
1511
|
const grant = await input.grants.acquire({
|
|
1512
1512
|
audience: "siteos-projects",
|
|
@@ -1520,7 +1520,7 @@ function createProjectApi(input) {
|
|
|
1520
1520
|
message: "SiteOS API access is unavailable."
|
|
1521
1521
|
});
|
|
1522
1522
|
const response = await input.fetchImpl(
|
|
1523
|
-
`${origin}/api/projects/v1/projects${
|
|
1523
|
+
`${origin}/api/projects/v1/projects${path31}`,
|
|
1524
1524
|
{
|
|
1525
1525
|
method: method ?? (body === void 0 ? "GET" : "POST"),
|
|
1526
1526
|
headers: {
|
|
@@ -1552,26 +1552,26 @@ function createProjectApi(input) {
|
|
|
1552
1552
|
}
|
|
1553
1553
|
return {
|
|
1554
1554
|
origin,
|
|
1555
|
-
async update(
|
|
1555
|
+
async update(id2, details) {
|
|
1556
1556
|
const { value, organizationId } = await request(
|
|
1557
|
-
`/${encodeURIComponent(
|
|
1557
|
+
`/${encodeURIComponent(id2)}`,
|
|
1558
1558
|
z4.object({ project: ProjectSchema }),
|
|
1559
1559
|
details,
|
|
1560
1560
|
"PATCH"
|
|
1561
1561
|
);
|
|
1562
1562
|
checkOrganization(value.project, organizationId);
|
|
1563
|
-
if (value.project.id !==
|
|
1563
|
+
if (value.project.id !== id2) throw invalidResponse();
|
|
1564
1564
|
return value.project;
|
|
1565
1565
|
},
|
|
1566
|
-
async updateEnvironment(
|
|
1566
|
+
async updateEnvironment(id2, environmentId, details) {
|
|
1567
1567
|
const { value, organizationId } = await request(
|
|
1568
|
-
`/${encodeURIComponent(
|
|
1568
|
+
`/${encodeURIComponent(id2)}/environments/${encodeURIComponent(environmentId)}`,
|
|
1569
1569
|
z4.object({ project: ProjectSchema }),
|
|
1570
1570
|
details,
|
|
1571
1571
|
"PATCH"
|
|
1572
1572
|
);
|
|
1573
1573
|
checkOrganization(value.project, organizationId);
|
|
1574
|
-
if (value.project.id !==
|
|
1574
|
+
if (value.project.id !== id2) throw invalidResponse();
|
|
1575
1575
|
return value.project;
|
|
1576
1576
|
},
|
|
1577
1577
|
async list() {
|
|
@@ -1584,14 +1584,14 @@ function createProjectApi(input) {
|
|
|
1584
1584
|
);
|
|
1585
1585
|
return value.projects;
|
|
1586
1586
|
},
|
|
1587
|
-
async get(
|
|
1587
|
+
async get(id2, environmentSlug) {
|
|
1588
1588
|
const { value, organizationId } = await request(
|
|
1589
|
-
`/${encodeURIComponent(
|
|
1589
|
+
`/${encodeURIComponent(id2)}${environmentSlug ? `?env=${encodeURIComponent(environmentSlug)}` : ""}`,
|
|
1590
1590
|
OverviewSchema
|
|
1591
1591
|
);
|
|
1592
1592
|
checkOrganization(value.project, organizationId);
|
|
1593
|
-
if (value.project.id !==
|
|
1594
|
-
(service) => service.attachment && (service.attachment.projectId !==
|
|
1593
|
+
if (value.project.id !== id2 || value.services.some(
|
|
1594
|
+
(service) => service.attachment && (service.attachment.projectId !== id2 || service.attachment.service !== service.service)
|
|
1595
1595
|
))
|
|
1596
1596
|
throw invalidResponse();
|
|
1597
1597
|
return value;
|
|
@@ -1605,33 +1605,33 @@ function createProjectApi(input) {
|
|
|
1605
1605
|
checkOrganization(value.project, organizationId);
|
|
1606
1606
|
return value.project;
|
|
1607
1607
|
},
|
|
1608
|
-
async connect(
|
|
1608
|
+
async connect(id2, service, resourceId, environmentSlug) {
|
|
1609
1609
|
await request(
|
|
1610
|
-
`/${encodeURIComponent(
|
|
1610
|
+
`/${encodeURIComponent(id2)}/services/${service}${environmentSlug ? `?env=${encodeURIComponent(environmentSlug)}` : ""}`,
|
|
1611
1611
|
z4.object({
|
|
1612
1612
|
attachment: z4.object({
|
|
1613
1613
|
resourceId: z4.string(),
|
|
1614
|
-
projectId: z4.literal(
|
|
1614
|
+
projectId: z4.literal(id2),
|
|
1615
1615
|
service: z4.literal(service)
|
|
1616
1616
|
})
|
|
1617
1617
|
}),
|
|
1618
1618
|
resourceId ? { resourceId } : { create: true }
|
|
1619
1619
|
);
|
|
1620
|
-
return this.get(
|
|
1620
|
+
return this.get(id2, environmentSlug);
|
|
1621
1621
|
},
|
|
1622
|
-
async createEnvironment(
|
|
1622
|
+
async createEnvironment(id2, details) {
|
|
1623
1623
|
const { value, organizationId } = await request(
|
|
1624
|
-
`/${encodeURIComponent(
|
|
1624
|
+
`/${encodeURIComponent(id2)}/environments`,
|
|
1625
1625
|
z4.object({ project: ProjectSchema }),
|
|
1626
1626
|
details
|
|
1627
1627
|
);
|
|
1628
1628
|
checkOrganization(value.project, organizationId);
|
|
1629
|
-
if (value.project.id !==
|
|
1629
|
+
if (value.project.id !== id2) throw invalidResponse();
|
|
1630
1630
|
return value.project;
|
|
1631
1631
|
},
|
|
1632
|
-
async availableEnvironments(
|
|
1632
|
+
async availableEnvironments(id2, service) {
|
|
1633
1633
|
return (await request(
|
|
1634
|
-
`/${encodeURIComponent(
|
|
1634
|
+
`/${encodeURIComponent(id2)}/services/${service}/environments`,
|
|
1635
1635
|
z4.object({
|
|
1636
1636
|
environments: z4.array(
|
|
1637
1637
|
z4.object({ id: z4.string(), name: z4.string(), slug: z4.string() })
|
|
@@ -1639,18 +1639,18 @@ function createProjectApi(input) {
|
|
|
1639
1639
|
})
|
|
1640
1640
|
)).value.environments;
|
|
1641
1641
|
},
|
|
1642
|
-
async connectEnvironment(
|
|
1642
|
+
async connectEnvironment(id2, service, environmentId, resourceId) {
|
|
1643
1643
|
await request(
|
|
1644
|
-
`/${encodeURIComponent(
|
|
1644
|
+
`/${encodeURIComponent(id2)}/services/${service}/environments/${encodeURIComponent(environmentId)}`,
|
|
1645
1645
|
z4.object({
|
|
1646
1646
|
attachment: z4.object({
|
|
1647
|
-
projectId: z4.literal(
|
|
1647
|
+
projectId: z4.literal(id2),
|
|
1648
1648
|
service: z4.literal(service)
|
|
1649
1649
|
})
|
|
1650
1650
|
}),
|
|
1651
1651
|
resourceId ? { resourceId } : { create: true }
|
|
1652
1652
|
);
|
|
1653
|
-
return this.get(
|
|
1653
|
+
return this.get(id2);
|
|
1654
1654
|
}
|
|
1655
1655
|
};
|
|
1656
1656
|
}
|
|
@@ -2575,14 +2575,14 @@ function generateOpaqueValue() {
|
|
|
2575
2575
|
function sha256Base64Url(value) {
|
|
2576
2576
|
return createHash4("sha256").update(value, "utf-8").digest("base64url");
|
|
2577
2577
|
}
|
|
2578
|
-
function sessionResult(session, json,
|
|
2578
|
+
function sessionResult(session, json, status2) {
|
|
2579
2579
|
if (json) {
|
|
2580
2580
|
return { exitCode: 0, stdout: JSON.stringify(session, null, 2) };
|
|
2581
2581
|
}
|
|
2582
|
-
if (
|
|
2582
|
+
if (status2 === "expired") {
|
|
2583
2583
|
return { exitCode: 0, stdout: "SiteOS CLI session is expired." };
|
|
2584
2584
|
}
|
|
2585
|
-
if (
|
|
2585
|
+
if (status2 === "revoked") {
|
|
2586
2586
|
return { exitCode: 0, stdout: "SiteOS CLI session is revoked." };
|
|
2587
2587
|
}
|
|
2588
2588
|
return {
|
|
@@ -2692,126 +2692,571 @@ function isCliExitCode(value) {
|
|
|
2692
2692
|
return Object.values(CLI_EXIT_CODE).some((exitCode) => exitCode === value);
|
|
2693
2693
|
}
|
|
2694
2694
|
|
|
2695
|
+
// src/forms-workspace-command.ts
|
|
2696
|
+
import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
2697
|
+
import path9 from "path";
|
|
2698
|
+
import { parseArgs } from "util";
|
|
2699
|
+
import { z as z7 } from "zod";
|
|
2700
|
+
var id = z7.string().min(1).max(255);
|
|
2701
|
+
var revision = z7.number().int().positive();
|
|
2702
|
+
var status = z7.enum(["new", "read", "archived", "spam"]);
|
|
2703
|
+
var purpose = z7.enum(["regular", "test"]);
|
|
2704
|
+
var filters = z7.object({
|
|
2705
|
+
q: z7.string().max(200).optional(),
|
|
2706
|
+
formId: id.optional(),
|
|
2707
|
+
status: z7.enum(["allowed", "blocked", "all"]).optional(),
|
|
2708
|
+
source: z7.enum(["regular", "test", "spam", "all"]).optional(),
|
|
2709
|
+
from: z7.string().datetime().optional(),
|
|
2710
|
+
to: z7.string().datetime().optional(),
|
|
2711
|
+
offset: z7.coerce.number().int().min(0).max(1e5).optional()
|
|
2712
|
+
}).strict().refine((v) => !v.from || !v.to || v.from < v.to);
|
|
2713
|
+
var changes = z7.object({
|
|
2714
|
+
changes: z7.array(
|
|
2715
|
+
z7.object({
|
|
2716
|
+
id,
|
|
2717
|
+
formId: id,
|
|
2718
|
+
expectedStatus: status,
|
|
2719
|
+
expectedPurpose: purpose,
|
|
2720
|
+
status: status.optional(),
|
|
2721
|
+
purpose: purpose.optional()
|
|
2722
|
+
}).strict().refine(
|
|
2723
|
+
(v) => v.status !== void 0 !== (v.purpose !== void 0)
|
|
2724
|
+
)
|
|
2725
|
+
).min(1).max(100)
|
|
2726
|
+
}).strict().refine((v) => new Set(v.changes.map((c) => c.id)).size === v.changes.length);
|
|
2727
|
+
var inputs = {
|
|
2728
|
+
edit: z7.object({
|
|
2729
|
+
expectedRevision: revision,
|
|
2730
|
+
email: z7.string().email().max(254),
|
|
2731
|
+
name: z7.string().max(255),
|
|
2732
|
+
notes: z7.string().max(4e3)
|
|
2733
|
+
}).strict(),
|
|
2734
|
+
block: z7.object({
|
|
2735
|
+
expectedRevision: revision,
|
|
2736
|
+
blocked: z7.boolean(),
|
|
2737
|
+
reason: z7.string().max(500).optional()
|
|
2738
|
+
}).strict(),
|
|
2739
|
+
merge: z7.object({
|
|
2740
|
+
expectedRevision: revision,
|
|
2741
|
+
targetId: id,
|
|
2742
|
+
targetRevision: revision
|
|
2743
|
+
}).strict(),
|
|
2744
|
+
"mapping-set": z7.object({
|
|
2745
|
+
formId: id,
|
|
2746
|
+
expectedRevision: revision.min(0),
|
|
2747
|
+
emailField: id.nullable(),
|
|
2748
|
+
nameField: id.nullable()
|
|
2749
|
+
}).strict(),
|
|
2750
|
+
"protection-set": z7.object({ enabled: z7.boolean(), expectedRevision: revision.min(0) }).strict(),
|
|
2751
|
+
export: z7.object({
|
|
2752
|
+
filters,
|
|
2753
|
+
ids: z7.array(id).min(1).max(5e3).optional(),
|
|
2754
|
+
format: z7.enum(["csv", "xlsx"]),
|
|
2755
|
+
columns: z7.array(
|
|
2756
|
+
z7.enum([
|
|
2757
|
+
"email",
|
|
2758
|
+
"name",
|
|
2759
|
+
"forms",
|
|
2760
|
+
"submissions",
|
|
2761
|
+
"firstSubmittedAt",
|
|
2762
|
+
"lastSubmittedAt",
|
|
2763
|
+
"status",
|
|
2764
|
+
"notes"
|
|
2765
|
+
])
|
|
2766
|
+
).min(1).max(8).refine((v) => new Set(v).size === v.length)
|
|
2767
|
+
}).strict(),
|
|
2768
|
+
bulk: changes
|
|
2769
|
+
};
|
|
2770
|
+
var contact = z7.object({
|
|
2771
|
+
id,
|
|
2772
|
+
email: z7.string(),
|
|
2773
|
+
name: z7.string(),
|
|
2774
|
+
revision,
|
|
2775
|
+
blocked: z7.boolean()
|
|
2776
|
+
}).passthrough();
|
|
2777
|
+
var contactFilters = [
|
|
2778
|
+
"query",
|
|
2779
|
+
"form",
|
|
2780
|
+
"status",
|
|
2781
|
+
"source",
|
|
2782
|
+
"from",
|
|
2783
|
+
"to",
|
|
2784
|
+
"offset"
|
|
2785
|
+
];
|
|
2786
|
+
var submissionFilters = [
|
|
2787
|
+
"form",
|
|
2788
|
+
"query",
|
|
2789
|
+
"status",
|
|
2790
|
+
"purpose",
|
|
2791
|
+
"from",
|
|
2792
|
+
"to",
|
|
2793
|
+
"version"
|
|
2794
|
+
];
|
|
2795
|
+
var routes = {
|
|
2796
|
+
"contacts list": {
|
|
2797
|
+
flags: contactFilters,
|
|
2798
|
+
method: "GET",
|
|
2799
|
+
suffix: "",
|
|
2800
|
+
scope: "read"
|
|
2801
|
+
},
|
|
2802
|
+
"contacts read": {
|
|
2803
|
+
flags: ["contact", "offset"],
|
|
2804
|
+
method: "GET",
|
|
2805
|
+
suffix: "",
|
|
2806
|
+
scope: "read"
|
|
2807
|
+
},
|
|
2808
|
+
"contacts edit": {
|
|
2809
|
+
flags: ["contact", "input"],
|
|
2810
|
+
method: "PATCH",
|
|
2811
|
+
suffix: "",
|
|
2812
|
+
scope: "write"
|
|
2813
|
+
},
|
|
2814
|
+
"contacts block": {
|
|
2815
|
+
flags: ["contact", "input"],
|
|
2816
|
+
method: "POST",
|
|
2817
|
+
suffix: "/block",
|
|
2818
|
+
scope: "write"
|
|
2819
|
+
},
|
|
2820
|
+
"contacts merge": {
|
|
2821
|
+
flags: ["contact", "input"],
|
|
2822
|
+
method: "POST",
|
|
2823
|
+
suffix: "/merge",
|
|
2824
|
+
scope: "write"
|
|
2825
|
+
},
|
|
2826
|
+
"contacts import": {
|
|
2827
|
+
flags: [],
|
|
2828
|
+
method: "POST",
|
|
2829
|
+
suffix: "/index",
|
|
2830
|
+
scope: "write"
|
|
2831
|
+
},
|
|
2832
|
+
"contacts mappings": {
|
|
2833
|
+
flags: [],
|
|
2834
|
+
method: "GET",
|
|
2835
|
+
suffix: "/mappings",
|
|
2836
|
+
scope: "read"
|
|
2837
|
+
},
|
|
2838
|
+
"contacts mapping-set": {
|
|
2839
|
+
flags: ["input"],
|
|
2840
|
+
method: "PATCH",
|
|
2841
|
+
suffix: "/mappings",
|
|
2842
|
+
scope: "write"
|
|
2843
|
+
},
|
|
2844
|
+
"contacts protection": {
|
|
2845
|
+
flags: [],
|
|
2846
|
+
method: "GET",
|
|
2847
|
+
suffix: "/protection",
|
|
2848
|
+
scope: "read"
|
|
2849
|
+
},
|
|
2850
|
+
"contacts protection-set": {
|
|
2851
|
+
flags: ["input"],
|
|
2852
|
+
method: "PATCH",
|
|
2853
|
+
suffix: "/protection",
|
|
2854
|
+
scope: "write"
|
|
2855
|
+
},
|
|
2856
|
+
"contacts export": {
|
|
2857
|
+
flags: ["input", "output"],
|
|
2858
|
+
method: "POST",
|
|
2859
|
+
suffix: "/export",
|
|
2860
|
+
scope: "export"
|
|
2861
|
+
},
|
|
2862
|
+
"submissions bulk": {
|
|
2863
|
+
flags: ["input"],
|
|
2864
|
+
method: "POST",
|
|
2865
|
+
suffix: "/bulk",
|
|
2866
|
+
scope: "workspace:write"
|
|
2867
|
+
},
|
|
2868
|
+
"submissions export": {
|
|
2869
|
+
flags: [...submissionFilters, "output"],
|
|
2870
|
+
method: "GET",
|
|
2871
|
+
suffix: "/export",
|
|
2872
|
+
scope: "workspace:write"
|
|
2873
|
+
}
|
|
2874
|
+
};
|
|
2875
|
+
var FORMS_WORKSPACE_HELP = ` siteos forms contacts list [--query <text>] [--form <id>] [--status <allowed|blocked|all>] [--source <regular|test|spam|all>] [--from <ISO>] [--to <ISO>] [--offset <number>] [--environment <slug>] [--json]
|
|
2876
|
+
siteos forms contacts read --contact <id> [--offset <number>] [--environment <slug>] [--json]
|
|
2877
|
+
siteos forms contacts edit --contact <id> --input <json-file> [--environment <slug>] [--json]
|
|
2878
|
+
siteos forms contacts block --contact <id> --input <json-file> [--environment <slug>] [--json]
|
|
2879
|
+
siteos forms contacts merge --contact <id> --input <json-file> [--environment <slug>] [--json]
|
|
2880
|
+
siteos forms contacts import [--environment <slug>] [--json]
|
|
2881
|
+
siteos forms contacts mappings [--environment <slug>] [--json]
|
|
2882
|
+
siteos forms contacts mapping-set --input <json-file> [--environment <slug>] [--json]
|
|
2883
|
+
siteos forms contacts protection [--environment <slug>] [--json]
|
|
2884
|
+
siteos forms contacts protection-set --input <json-file> [--environment <slug>] [--json]
|
|
2885
|
+
siteos forms contacts export --input <json-file> --output <new-file> [--environment <slug>] [--json]
|
|
2886
|
+
siteos forms submissions bulk --input <json-file> [--environment <slug>] [--json]
|
|
2887
|
+
siteos forms submissions export --output <new-file> [--form <id>] [--query <text>] [--status <new|read|archived|spam|inbox>] [--purpose <regular|test|all>] [--from <ISO>] [--to <ISO>] [--version <id>] [--environment <slug>] [--json]
|
|
2888
|
+
|
|
2889
|
+
Contacts reads, writes and exports use separate grants. Writes require owner/admin.
|
|
2890
|
+
Import indexes one bounded batch of earlier submissions; inspect remaining before repeating.
|
|
2891
|
+
Block input: {"expectedRevision":1,"blocked":true,"reason":"Unwanted submissions"}; false unblocks.
|
|
2892
|
+
Protection input: {"expectedRevision":0,"enabled":true}. Enabling blocks disposable domains.
|
|
2893
|
+
Edit/merge/mapping input uses current revisions; reread conflicts before changing the request.
|
|
2894
|
+
Export input: {"filters":{"status":"all","source":"regular"},"format":"csv","columns":["email","name"]}.
|
|
2895
|
+
Bulk input: {"changes":[{"id":"submission-id","formId":"form-id","expectedStatus":"new","expectedPurpose":"regular","status":"archived"}]}.
|
|
2896
|
+
Each bulk item changes either status or purpose (regular/test). Results may contain conflicts.
|
|
2897
|
+
Export uses all matching records (up to 5000), never overwrites files, and does not grant marketing consent.`;
|
|
2898
|
+
async function runFormsWorkspaceCommand(options) {
|
|
2899
|
+
const json = options.args.includes("--json");
|
|
2900
|
+
try {
|
|
2901
|
+
const route = routes[options.args.slice(0, 2).join(" ")];
|
|
2902
|
+
if (!route)
|
|
2903
|
+
throw new Error(
|
|
2904
|
+
"Unknown Forms workspace command. Run siteos forms --help."
|
|
2905
|
+
);
|
|
2906
|
+
const { positionals, values } = parseArgs({
|
|
2907
|
+
args: options.args.slice(2),
|
|
2908
|
+
strict: true,
|
|
2909
|
+
allowPositionals: true,
|
|
2910
|
+
options: {
|
|
2911
|
+
...Object.fromEntries(
|
|
2912
|
+
["environment", ...route.flags].map((key) => [
|
|
2913
|
+
key,
|
|
2914
|
+
{ type: "string" }
|
|
2915
|
+
])
|
|
2916
|
+
),
|
|
2917
|
+
json: { type: "boolean" }
|
|
2918
|
+
}
|
|
2919
|
+
});
|
|
2920
|
+
if (positionals.length) throw new Error("Unexpected positional arguments.");
|
|
2921
|
+
const flags = values;
|
|
2922
|
+
const value = (key) => typeof flags[key] === "string" ? flags[key] : void 0;
|
|
2923
|
+
const action = options.args[1];
|
|
2924
|
+
const contacts = options.args[0] === "contacts";
|
|
2925
|
+
const selectedId = route.flags.includes("contact") ? id.parse(value("contact")) : void 0;
|
|
2926
|
+
const query = new URLSearchParams();
|
|
2927
|
+
if (contacts && action === "list") {
|
|
2928
|
+
const f = filters.parse({
|
|
2929
|
+
...Object.fromEntries(
|
|
2930
|
+
contactFilters.filter((key) => value(key) !== void 0).map((key) => [
|
|
2931
|
+
key === "query" ? "q" : key === "form" ? "formId" : key,
|
|
2932
|
+
value(key)
|
|
2933
|
+
])
|
|
2934
|
+
)
|
|
2935
|
+
});
|
|
2936
|
+
for (const [key, v] of Object.entries(f)) query.set(key, String(v));
|
|
2937
|
+
} else if (contacts && action === "read")
|
|
2938
|
+
query.set(
|
|
2939
|
+
"offset",
|
|
2940
|
+
String(
|
|
2941
|
+
z7.coerce.number().int().min(0).max(1e5).parse(value("offset") ?? 0)
|
|
2942
|
+
)
|
|
2943
|
+
);
|
|
2944
|
+
else if (!contacts && action === "export") {
|
|
2945
|
+
for (const key of submissionFilters)
|
|
2946
|
+
if (value(key) !== void 0)
|
|
2947
|
+
query.set(
|
|
2948
|
+
key === "form" ? "formId" : key === "version" ? "versionId" : key,
|
|
2949
|
+
value(key)
|
|
2950
|
+
);
|
|
2951
|
+
if (query.get("purpose") === "all") query.delete("purpose");
|
|
2952
|
+
z7.object({
|
|
2953
|
+
formId: id.optional(),
|
|
2954
|
+
query: z7.string().max(200).optional(),
|
|
2955
|
+
status: z7.enum(["new", "read", "archived", "spam", "inbox"]).optional(),
|
|
2956
|
+
purpose: purpose.optional(),
|
|
2957
|
+
from: z7.string().datetime({ offset: true }).optional(),
|
|
2958
|
+
to: z7.string().datetime({ offset: true }).optional(),
|
|
2959
|
+
versionId: id.optional()
|
|
2960
|
+
}).strict().refine(
|
|
2961
|
+
(v) => !v.from || !v.to || Date.parse(v.from) < Date.parse(v.to)
|
|
2962
|
+
).parse(Object.fromEntries(query));
|
|
2963
|
+
}
|
|
2964
|
+
let body;
|
|
2965
|
+
if (route.flags.includes("input")) {
|
|
2966
|
+
const file = value("input");
|
|
2967
|
+
if (!file)
|
|
2968
|
+
throw new Error("Provide --input with the reviewed JSON request.");
|
|
2969
|
+
const raw = await readFile4(
|
|
2970
|
+
path9.resolve(options.cwd ?? process.cwd(), file),
|
|
2971
|
+
"utf8"
|
|
2972
|
+
);
|
|
2973
|
+
if (Buffer.byteLength(raw) > 1024 * 1024)
|
|
2974
|
+
throw new Error("Input exceeds 1 MiB.");
|
|
2975
|
+
body = inputs[action].parse(JSON.parse(raw));
|
|
2976
|
+
}
|
|
2977
|
+
const output = route.flags.includes("output") ? value("output") : void 0;
|
|
2978
|
+
if (route.flags.includes("output") && !output)
|
|
2979
|
+
throw new Error("Provide --output with a new file path.");
|
|
2980
|
+
const context = await commonServiceContext(
|
|
2981
|
+
options,
|
|
2982
|
+
"forms",
|
|
2983
|
+
value("environment")
|
|
2984
|
+
);
|
|
2985
|
+
if (!context?.environmentBinding)
|
|
2986
|
+
throw new Error(
|
|
2987
|
+
"Select a Project and connect Forms in the intended environment."
|
|
2988
|
+
);
|
|
2989
|
+
const scope = contacts ? `forms:contacts:${route.scope}` : "forms:workspace:write";
|
|
2990
|
+
const grant = await commonProjectRuntime(options).grants.acquire({
|
|
2991
|
+
audience: "siteos-forms",
|
|
2992
|
+
scopes: [scope]
|
|
2993
|
+
});
|
|
2994
|
+
if (grant.grant.organizationId !== context.overview.project.organizationId || grant.grant.audience !== "siteos-forms" || !grant.grant.scopes.includes(scope))
|
|
2995
|
+
throw new Error(
|
|
2996
|
+
"The Forms grant does not match the selected Organization or required scope."
|
|
2997
|
+
);
|
|
2998
|
+
if (!options.fetchImpl)
|
|
2999
|
+
throw new Error("SiteOS API access is unavailable.");
|
|
3000
|
+
const url = new URL(
|
|
3001
|
+
`/api/v1/forms/environments/${encodeURIComponent(context.environmentBinding.resourceId)}/${contacts ? "contacts" : "submissions"}${selectedId ? `/${encodeURIComponent(selectedId)}` : ""}${route.suffix}`,
|
|
3002
|
+
context.api.origin
|
|
3003
|
+
);
|
|
3004
|
+
url.search = query.toString();
|
|
3005
|
+
const response = await options.fetchImpl(url.href, {
|
|
3006
|
+
method: route.method,
|
|
3007
|
+
headers: {
|
|
3008
|
+
Authorization: `Bearer ${grant.accessToken}`,
|
|
3009
|
+
...body === void 0 ? {} : { "Content-Type": "application/json" }
|
|
3010
|
+
},
|
|
3011
|
+
...body === void 0 ? {} : { body: JSON.stringify(body) },
|
|
3012
|
+
signal: AbortSignal.timeout(3e4)
|
|
3013
|
+
});
|
|
3014
|
+
if (!response.ok) {
|
|
3015
|
+
const data2 = await response.json().catch(() => null);
|
|
3016
|
+
throw new Error(
|
|
3017
|
+
`${data2?.error?.code ?? response.status}: ${data2?.error?.message ?? "Forms request failed. Reread current data before retrying a mutation."}`
|
|
3018
|
+
);
|
|
3019
|
+
}
|
|
3020
|
+
if (output) {
|
|
3021
|
+
if (!(response instanceof Response))
|
|
3022
|
+
throw new Error("Export requires a streaming HTTP response.");
|
|
3023
|
+
const contentType = response.headers.get("Content-Type") ?? "";
|
|
3024
|
+
const expectedType = contacts && body.format === "xlsx" ? "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" : "text/csv";
|
|
3025
|
+
if (!contentType.startsWith(expectedType))
|
|
3026
|
+
throw new Error("Unexpected export format.");
|
|
3027
|
+
const reader = response.body?.getReader();
|
|
3028
|
+
if (!reader) throw new Error("Missing export body.");
|
|
3029
|
+
const chunks = [];
|
|
3030
|
+
let size = 0;
|
|
3031
|
+
try {
|
|
3032
|
+
for (; ; ) {
|
|
3033
|
+
const chunk = await reader.read();
|
|
3034
|
+
if (chunk.done) break;
|
|
3035
|
+
size += chunk.value.byteLength;
|
|
3036
|
+
if (size > 25 * 1024 * 1024)
|
|
3037
|
+
throw new Error("Export exceeds 25 MiB.");
|
|
3038
|
+
chunks.push(chunk.value);
|
|
3039
|
+
}
|
|
3040
|
+
} finally {
|
|
3041
|
+
await reader.cancel();
|
|
3042
|
+
}
|
|
3043
|
+
const file = path9.resolve(options.cwd ?? process.cwd(), output);
|
|
3044
|
+
await writeFile4(file, Buffer.concat(chunks), { flag: "wx", mode: 384 });
|
|
3045
|
+
return {
|
|
3046
|
+
exitCode: 0,
|
|
3047
|
+
stdout: JSON.stringify({
|
|
3048
|
+
output: file,
|
|
3049
|
+
bytes: size,
|
|
3050
|
+
environment: context.environment.slug
|
|
3051
|
+
})
|
|
3052
|
+
};
|
|
3053
|
+
}
|
|
3054
|
+
const rawData = await response.json();
|
|
3055
|
+
const data = z7.record(z7.unknown()).parse(
|
|
3056
|
+
action === "mappings" ? {
|
|
3057
|
+
mappings: z7.array(
|
|
3058
|
+
z7.object({
|
|
3059
|
+
formId: id,
|
|
3060
|
+
revision: revision.min(0),
|
|
3061
|
+
emailField: z7.string().nullable(),
|
|
3062
|
+
nameField: z7.string().nullable()
|
|
3063
|
+
})
|
|
3064
|
+
).parse(rawData)
|
|
3065
|
+
} : rawData
|
|
3066
|
+
);
|
|
3067
|
+
if (contacts && action === "list")
|
|
3068
|
+
z7.object({
|
|
3069
|
+
contacts: z7.array(contact),
|
|
3070
|
+
total: z7.number().int().nonnegative(),
|
|
3071
|
+
nextOffset: z7.number().nullable()
|
|
3072
|
+
}).parse(data);
|
|
3073
|
+
if (contacts && action === "read")
|
|
3074
|
+
z7.object({
|
|
3075
|
+
contact,
|
|
3076
|
+
history: z7.array(z7.unknown()),
|
|
3077
|
+
nextOffset: z7.number().nullable()
|
|
3078
|
+
}).parse(data);
|
|
3079
|
+
if (["edit", "block", "mapping-set", "protection-set"].includes(action))
|
|
3080
|
+
z7.object({ success: z7.literal(true) }).parse(data);
|
|
3081
|
+
if (action === "merge") z7.object({ id }).parse(data);
|
|
3082
|
+
if (action === "import")
|
|
3083
|
+
z7.object({
|
|
3084
|
+
processed: z7.number().int().nonnegative(),
|
|
3085
|
+
linked: z7.number().int().nonnegative(),
|
|
3086
|
+
skipped: z7.number().int().nonnegative(),
|
|
3087
|
+
remaining: z7.number().int().nonnegative()
|
|
3088
|
+
}).parse(data);
|
|
3089
|
+
if (action === "protection")
|
|
3090
|
+
z7.object({
|
|
3091
|
+
enabled: z7.boolean(),
|
|
3092
|
+
revision: revision.min(0),
|
|
3093
|
+
domainCount: z7.number().int().nonnegative()
|
|
3094
|
+
}).parse(data);
|
|
3095
|
+
if (action === "read")
|
|
3096
|
+
z7.literal(selectedId).parse(data.contact.id);
|
|
3097
|
+
if (action === "bulk") {
|
|
3098
|
+
const result = z7.object({
|
|
3099
|
+
results: z7.array(
|
|
3100
|
+
z7.object({
|
|
3101
|
+
id,
|
|
3102
|
+
formId: id,
|
|
3103
|
+
outcome: z7.enum(["updated", "conflict", "not_found"])
|
|
3104
|
+
})
|
|
3105
|
+
)
|
|
3106
|
+
}).parse(data);
|
|
3107
|
+
const expected = body.changes;
|
|
3108
|
+
if (result.results.length !== expected.length || new Set(result.results.map((r) => r.id)).size !== expected.length || result.results.some(
|
|
3109
|
+
(r) => !expected.some((c) => c.id === r.id && c.formId === r.formId)
|
|
3110
|
+
))
|
|
3111
|
+
throw new Error(
|
|
3112
|
+
"Bulk response does not match the requested submissions. Reread before retrying."
|
|
3113
|
+
);
|
|
3114
|
+
return {
|
|
3115
|
+
exitCode: result.results.every((r) => r.outcome === "updated") ? 0 : 1,
|
|
3116
|
+
stdout: JSON.stringify(data, null, 2)
|
|
3117
|
+
};
|
|
3118
|
+
}
|
|
3119
|
+
return { exitCode: 0, stdout: JSON.stringify(data, null, 2) };
|
|
3120
|
+
} catch (cause) {
|
|
3121
|
+
const message = cause instanceof z7.ZodError ? "Invalid Forms input. Check the documented fields, filters and revisions." : cause instanceof Error ? cause.message : "Forms request failed.";
|
|
3122
|
+
return {
|
|
3123
|
+
exitCode: 2,
|
|
3124
|
+
...json ? { stdout: JSON.stringify({ error: { message } }) } : { stderr: message }
|
|
3125
|
+
};
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
|
|
2695
3129
|
// src/forms-command.ts
|
|
2696
|
-
import { readFile as
|
|
2697
|
-
import
|
|
3130
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
3131
|
+
import path11 from "path";
|
|
2698
3132
|
import { Ajv } from "ajv";
|
|
2699
3133
|
import addFormats from "ajv-formats";
|
|
2700
|
-
import { z as
|
|
3134
|
+
import { z as z9 } from "zod";
|
|
2701
3135
|
|
|
2702
3136
|
// src/siteos-forms-api.ts
|
|
2703
|
-
import { z as
|
|
2704
|
-
var opaqueIdentifierSchema =
|
|
2705
|
-
var formsEnvironmentSchema =
|
|
2706
|
-
createdAt:
|
|
3137
|
+
import { z as z8 } from "zod";
|
|
3138
|
+
var opaqueIdentifierSchema = z8.string().trim().min(1).max(255);
|
|
3139
|
+
var formsEnvironmentSchema = z8.object({
|
|
3140
|
+
createdAt: z8.string().datetime({ offset: true }),
|
|
2707
3141
|
id: opaqueIdentifierSchema,
|
|
2708
|
-
name:
|
|
3142
|
+
name: z8.string().trim().min(1).max(255),
|
|
2709
3143
|
projectId: opaqueIdentifierSchema,
|
|
2710
|
-
slug:
|
|
2711
|
-
updatedAt:
|
|
3144
|
+
slug: z8.string().trim().min(1).max(120).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/),
|
|
3145
|
+
updatedAt: z8.string().datetime({ offset: true })
|
|
2712
3146
|
}).strict();
|
|
2713
|
-
var formsEnvironmentListSchema =
|
|
2714
|
-
var formDefinitionSyncResponseSchema =
|
|
3147
|
+
var formsEnvironmentListSchema = z8.object({ environments: z8.array(formsEnvironmentSchema) }).strict();
|
|
3148
|
+
var formDefinitionSyncResponseSchema = z8.object({
|
|
2715
3149
|
activeVersionId: opaqueIdentifierSchema,
|
|
2716
|
-
createdForm:
|
|
2717
|
-
createdVersion:
|
|
3150
|
+
createdForm: z8.boolean(),
|
|
3151
|
+
createdVersion: z8.boolean(),
|
|
2718
3152
|
formId: opaqueIdentifierSchema,
|
|
2719
3153
|
formKey: opaqueIdentifierSchema,
|
|
2720
|
-
success:
|
|
2721
|
-
version:
|
|
3154
|
+
success: z8.literal(true),
|
|
3155
|
+
version: z8.number().int().positive()
|
|
2722
3156
|
}).strict();
|
|
2723
|
-
var definitionSchema =
|
|
3157
|
+
var definitionSchema = z8.object({
|
|
2724
3158
|
id: opaqueIdentifierSchema,
|
|
2725
3159
|
environmentId: opaqueIdentifierSchema,
|
|
2726
3160
|
formKey: opaqueIdentifierSchema,
|
|
2727
|
-
name:
|
|
2728
|
-
sourcePagePath:
|
|
2729
|
-
activeVersion:
|
|
3161
|
+
name: z8.string(),
|
|
3162
|
+
sourcePagePath: z8.string().nullable(),
|
|
3163
|
+
activeVersion: z8.number().int().positive(),
|
|
2730
3164
|
activeVersionId: opaqueIdentifierSchema,
|
|
2731
|
-
status:
|
|
2732
|
-
submissionCount:
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
3165
|
+
status: z8.enum(["active", "inactive"]),
|
|
3166
|
+
submissionCount: z8.number().int().nonnegative(),
|
|
3167
|
+
unreadCount: z8.number().int().nonnegative().optional(),
|
|
3168
|
+
lastSubmittedAt: z8.string().datetime({ offset: true }).nullable(),
|
|
3169
|
+
createdAt: z8.string().datetime({ offset: true }),
|
|
3170
|
+
updatedAt: z8.string().datetime({ offset: true })
|
|
2736
3171
|
});
|
|
2737
|
-
var definitionListSchema =
|
|
2738
|
-
definitions:
|
|
3172
|
+
var definitionListSchema = z8.object({
|
|
3173
|
+
definitions: z8.array(definitionSchema)
|
|
2739
3174
|
});
|
|
2740
|
-
var definitionContextSchema =
|
|
3175
|
+
var definitionContextSchema = z8.object({
|
|
2741
3176
|
definition: definitionSchema,
|
|
2742
|
-
revision:
|
|
2743
|
-
canManage:
|
|
2744
|
-
project:
|
|
2745
|
-
environment:
|
|
3177
|
+
revision: z8.number().int().positive(),
|
|
3178
|
+
canManage: z8.boolean(),
|
|
3179
|
+
project: z8.object({ id: opaqueIdentifierSchema, name: z8.string() }),
|
|
3180
|
+
environment: z8.object({
|
|
2746
3181
|
id: opaqueIdentifierSchema,
|
|
2747
|
-
name:
|
|
2748
|
-
slug:
|
|
3182
|
+
name: z8.string(),
|
|
3183
|
+
slug: z8.string()
|
|
2749
3184
|
})
|
|
2750
3185
|
});
|
|
2751
|
-
var definitionChangeResponseSchema =
|
|
2752
|
-
deleted:
|
|
3186
|
+
var definitionChangeResponseSchema = z8.object({
|
|
3187
|
+
deleted: z8.boolean(),
|
|
2753
3188
|
formId: opaqueIdentifierSchema
|
|
2754
3189
|
});
|
|
2755
|
-
var formSubmissionResponseSchema =
|
|
3190
|
+
var formSubmissionResponseSchema = z8.object({
|
|
2756
3191
|
formId: opaqueIdentifierSchema,
|
|
2757
|
-
receivedAt:
|
|
3192
|
+
receivedAt: z8.string().datetime({ offset: true }),
|
|
2758
3193
|
submissionId: opaqueIdentifierSchema,
|
|
2759
|
-
success:
|
|
3194
|
+
success: z8.literal(true)
|
|
2760
3195
|
}).strict();
|
|
2761
|
-
var formsSubmissionStatusSchema =
|
|
3196
|
+
var formsSubmissionStatusSchema = z8.enum([
|
|
2762
3197
|
"new",
|
|
2763
3198
|
"read",
|
|
2764
3199
|
"archived",
|
|
2765
3200
|
"spam"
|
|
2766
3201
|
]);
|
|
2767
|
-
var submissionSummarySchema =
|
|
3202
|
+
var submissionSummarySchema = z8.object({
|
|
2768
3203
|
id: opaqueIdentifierSchema,
|
|
2769
3204
|
formId: opaqueIdentifierSchema,
|
|
2770
|
-
normalized:
|
|
2771
|
-
|
|
3205
|
+
normalized: z8.record(
|
|
3206
|
+
z8.union([z8.string(), z8.number(), z8.boolean(), z8.null()])
|
|
2772
3207
|
),
|
|
2773
3208
|
status: formsSubmissionStatusSchema,
|
|
2774
|
-
|
|
3209
|
+
purpose: z8.enum(["regular", "test"]).optional(),
|
|
3210
|
+
formName: z8.string().optional(),
|
|
3211
|
+
formKey: z8.string().optional(),
|
|
3212
|
+
submittedAt: z8.string().datetime({ offset: true })
|
|
2775
3213
|
});
|
|
2776
3214
|
var submissionDetailSchema = submissionSummarySchema.extend({
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
3215
|
+
sourceUrl: z8.string().nullable().optional(),
|
|
3216
|
+
payload: z8.record(z8.unknown()),
|
|
3217
|
+
fields: z8.array(
|
|
3218
|
+
z8.object({
|
|
3219
|
+
key: z8.string(),
|
|
3220
|
+
label: z8.string().optional(),
|
|
3221
|
+
kind: z8.string().optional(),
|
|
3222
|
+
displayRole: z8.enum(["primary", "secondary"]).optional(),
|
|
3223
|
+
multiple: z8.boolean().optional(),
|
|
3224
|
+
options: z8.array(z8.object({ value: z8.string(), label: z8.string() })).optional()
|
|
2786
3225
|
})
|
|
2787
3226
|
),
|
|
2788
|
-
version:
|
|
3227
|
+
version: z8.number().int().positive(),
|
|
2789
3228
|
versionId: opaqueIdentifierSchema
|
|
2790
3229
|
});
|
|
2791
|
-
var submissionPageSchema =
|
|
2792
|
-
submissions:
|
|
2793
|
-
total:
|
|
2794
|
-
nextCursor:
|
|
3230
|
+
var submissionPageSchema = z8.object({
|
|
3231
|
+
submissions: z8.array(submissionSummarySchema),
|
|
3232
|
+
total: z8.number().int().nonnegative(),
|
|
3233
|
+
nextCursor: z8.string().nullable(),
|
|
3234
|
+
counts: z8.object({
|
|
3235
|
+
inbox: z8.number().int().nonnegative(),
|
|
3236
|
+
archived: z8.number().int().nonnegative(),
|
|
3237
|
+
spam: z8.number().int().nonnegative(),
|
|
3238
|
+
all: z8.number().int().nonnegative()
|
|
3239
|
+
}).optional()
|
|
2795
3240
|
});
|
|
2796
|
-
var submissionDetailResponseSchema =
|
|
3241
|
+
var submissionDetailResponseSchema = z8.object({
|
|
2797
3242
|
submission: submissionDetailSchema
|
|
2798
3243
|
});
|
|
2799
|
-
var formsCredentialMetadataSchema =
|
|
2800
|
-
createdAt:
|
|
2801
|
-
id:
|
|
2802
|
-
lastUsedAt:
|
|
2803
|
-
name:
|
|
2804
|
-
projectEnvironmentId:
|
|
2805
|
-
revokedAt:
|
|
2806
|
-
status:
|
|
3244
|
+
var formsCredentialMetadataSchema = z8.object({
|
|
3245
|
+
createdAt: z8.string().datetime({ offset: true }),
|
|
3246
|
+
id: z8.string().trim().min(1).max(255),
|
|
3247
|
+
lastUsedAt: z8.string().datetime({ offset: true }).nullable(),
|
|
3248
|
+
name: z8.string().trim().min(1).max(255),
|
|
3249
|
+
projectEnvironmentId: z8.string().trim().min(1).max(255),
|
|
3250
|
+
revokedAt: z8.string().datetime({ offset: true }).nullable(),
|
|
3251
|
+
status: z8.enum(["active", "revoked"])
|
|
2807
3252
|
}).strict();
|
|
2808
|
-
var formsCredentialListSchema =
|
|
2809
|
-
tokens:
|
|
3253
|
+
var formsCredentialListSchema = z8.object({
|
|
3254
|
+
tokens: z8.array(formsCredentialMetadataSchema)
|
|
2810
3255
|
}).strict();
|
|
2811
3256
|
var formsCredentialExchangeSchema = formsCredentialMetadataSchema.extend({
|
|
2812
|
-
token:
|
|
3257
|
+
token: z8.string().regex(/^pfs_[A-Za-z0-9_-]{22}$/)
|
|
2813
3258
|
});
|
|
2814
|
-
var formsErrorCodeSchema =
|
|
3259
|
+
var formsErrorCodeSchema = z8.enum([
|
|
2815
3260
|
"AUTHENTICATION_REQUIRED",
|
|
2816
3261
|
"FORBIDDEN",
|
|
2817
3262
|
"CONFLICT",
|
|
@@ -2859,10 +3304,10 @@ var safeFormsErrorMessages = {
|
|
|
2859
3304
|
SERVICE_UNAVAILABLE: "The Forms service is unavailable.",
|
|
2860
3305
|
INTERNAL_ERROR: "The Forms request could not be completed."
|
|
2861
3306
|
};
|
|
2862
|
-
var apiErrorResponseSchema =
|
|
2863
|
-
error:
|
|
3307
|
+
var apiErrorResponseSchema = z8.object({
|
|
3308
|
+
error: z8.object({
|
|
2864
3309
|
code: formsErrorCodeSchema,
|
|
2865
|
-
message:
|
|
3310
|
+
message: z8.string().trim().min(1).max(240)
|
|
2866
3311
|
}).strict()
|
|
2867
3312
|
}).strict();
|
|
2868
3313
|
var SiteOSFormsApiError = class extends Error {
|
|
@@ -2875,15 +3320,15 @@ var SiteOSFormsApiError = class extends Error {
|
|
|
2875
3320
|
this.status = options.status;
|
|
2876
3321
|
}
|
|
2877
3322
|
};
|
|
2878
|
-
var deploymentResponseSchema =
|
|
3323
|
+
var deploymentResponseSchema = z8.object({
|
|
2879
3324
|
environmentId: opaqueIdentifierSchema,
|
|
2880
|
-
forms:
|
|
2881
|
-
|
|
3325
|
+
forms: z8.array(
|
|
3326
|
+
z8.object({
|
|
2882
3327
|
formKey: opaqueIdentifierSchema,
|
|
2883
3328
|
formId: opaqueIdentifierSchema,
|
|
2884
|
-
contractVersion:
|
|
3329
|
+
contractVersion: z8.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
2885
3330
|
versionId: opaqueIdentifierSchema,
|
|
2886
|
-
version:
|
|
3331
|
+
version: z8.number().int().positive()
|
|
2887
3332
|
})
|
|
2888
3333
|
)
|
|
2889
3334
|
});
|
|
@@ -2932,7 +3377,7 @@ function createSiteOSFormsApiClient(options) {
|
|
|
2932
3377
|
fetchImpl: options.fetchImpl,
|
|
2933
3378
|
headers: managementHeaders(input),
|
|
2934
3379
|
method: "GET",
|
|
2935
|
-
path: `${inboxPath(input)}?${input.filters}`,
|
|
3380
|
+
path: `${input.formId ? inboxPath({ ...input, formId: input.formId }) : `/api/v1/forms/environments/${encodeURIComponent(input.environmentId)}/submissions`}?${input.filters}`,
|
|
2936
3381
|
responseSchema: submissionPageSchema
|
|
2937
3382
|
}),
|
|
2938
3383
|
readSubmission: (input) => requestJson2({
|
|
@@ -2960,7 +3405,7 @@ function createSiteOSFormsApiClient(options) {
|
|
|
2960
3405
|
method: "POST",
|
|
2961
3406
|
path: keyCollection(input),
|
|
2962
3407
|
responseSchema: options.deploymentKeys ? formsCredentialExchangeSchema.extend({
|
|
2963
|
-
token:
|
|
3408
|
+
token: z8.string().regex(/^pfd_[A-Za-z0-9_-]{43}$/)
|
|
2964
3409
|
}) : formsCredentialExchangeSchema
|
|
2965
3410
|
}),
|
|
2966
3411
|
listEnvironments: (input) => requestJson2({
|
|
@@ -2995,7 +3440,7 @@ function createSiteOSFormsApiClient(options) {
|
|
|
2995
3440
|
method: "POST",
|
|
2996
3441
|
path: credentialRotatePath(input),
|
|
2997
3442
|
responseSchema: options.deploymentKeys ? formsCredentialExchangeSchema.extend({
|
|
2998
|
-
token:
|
|
3443
|
+
token: z8.string().regex(/^pfd_[A-Za-z0-9_-]{43}$/)
|
|
2999
3444
|
}) : formsCredentialExchangeSchema
|
|
3000
3445
|
}),
|
|
3001
3446
|
submitForm: (input) => requestJson2({
|
|
@@ -3118,12 +3563,12 @@ import { randomUUID as randomUUID3 } from "crypto";
|
|
|
3118
3563
|
import {
|
|
3119
3564
|
chmod as chmod3,
|
|
3120
3565
|
lstat as lstat4,
|
|
3121
|
-
readFile as
|
|
3566
|
+
readFile as readFile5,
|
|
3122
3567
|
rename as rename4,
|
|
3123
3568
|
rm as rm3,
|
|
3124
|
-
writeFile as
|
|
3569
|
+
writeFile as writeFile5
|
|
3125
3570
|
} from "fs/promises";
|
|
3126
|
-
import
|
|
3571
|
+
import path10 from "path";
|
|
3127
3572
|
var SITEOS_FORMS_SUBMISSION_CREDENTIAL_ENV = "SITEOS_FORMS_SUBMISSION_CREDENTIAL";
|
|
3128
3573
|
var SITEOS_FORMS_DEPLOYMENT_KEY_ENV = "SITEOS_FORMS_DEPLOYMENT_KEY";
|
|
3129
3574
|
var SiteOSFormsCredentialStorageError = class extends Error {
|
|
@@ -3139,8 +3584,8 @@ var SiteOSFormsCredentialStorageError = class extends Error {
|
|
|
3139
3584
|
async function prepareSiteOSFormsCredentialInstallation(options) {
|
|
3140
3585
|
const variable = options.deployment ? SITEOS_FORMS_DEPLOYMENT_KEY_ENV : SITEOS_FORMS_SUBMISSION_CREDENTIAL_ENV;
|
|
3141
3586
|
const credentialPattern = options.deployment ? /^pfd_[A-Za-z0-9_-]{43}$/ : /^pfs_[A-Za-z0-9_-]{22}$/;
|
|
3142
|
-
const projectRoot =
|
|
3143
|
-
const dotenvPath =
|
|
3587
|
+
const projectRoot = path10.resolve(options.projectRoot);
|
|
3588
|
+
const dotenvPath = path10.join(projectRoot, ".env");
|
|
3144
3589
|
try {
|
|
3145
3590
|
await assertSafeProjectRoot(projectRoot);
|
|
3146
3591
|
if (!await isDotenvIgnored(projectRoot)) {
|
|
@@ -3202,7 +3647,7 @@ async function readSafeDotenv(dotenvPath) {
|
|
|
3202
3647
|
"FORMS_CREDENTIAL_STORAGE_INVALID"
|
|
3203
3648
|
);
|
|
3204
3649
|
}
|
|
3205
|
-
const content = dotenvStats ? await
|
|
3650
|
+
const content = dotenvStats ? await readFile5(dotenvPath, "utf8") : "";
|
|
3206
3651
|
if ([
|
|
3207
3652
|
SITEOS_FORMS_SUBMISSION_CREDENTIAL_ENV,
|
|
3208
3653
|
SITEOS_FORMS_DEPLOYMENT_KEY_ENV
|
|
@@ -3214,12 +3659,12 @@ async function readSafeDotenv(dotenvPath) {
|
|
|
3214
3659
|
return content;
|
|
3215
3660
|
}
|
|
3216
3661
|
async function writeCredentialAtomically(options) {
|
|
3217
|
-
const temporaryPath =
|
|
3662
|
+
const temporaryPath = path10.join(
|
|
3218
3663
|
options.projectRoot,
|
|
3219
3664
|
`.siteos-forms-credential.${process.pid}.${randomUUID3()}.tmp`
|
|
3220
3665
|
);
|
|
3221
3666
|
try {
|
|
3222
|
-
await
|
|
3667
|
+
await writeFile5(temporaryPath, options.content, {
|
|
3223
3668
|
encoding: "utf8",
|
|
3224
3669
|
flag: "wx",
|
|
3225
3670
|
mode: 384
|
|
@@ -3276,25 +3721,25 @@ var ENVIRONMENT_FLAG = "--environment";
|
|
|
3276
3721
|
var INSTALL_FLAG = "--install";
|
|
3277
3722
|
var NAME_FLAG = "--name";
|
|
3278
3723
|
var CREDENTIAL_FLAG = "--credential";
|
|
3279
|
-
var normalizedFieldOptionSchema =
|
|
3280
|
-
label:
|
|
3281
|
-
value:
|
|
3724
|
+
var normalizedFieldOptionSchema = z9.object({
|
|
3725
|
+
label: z9.string().trim().min(1).max(255),
|
|
3726
|
+
value: z9.string().trim().min(1).max(255)
|
|
3282
3727
|
});
|
|
3283
|
-
var normalizedFieldSchema =
|
|
3284
|
-
key:
|
|
3285
|
-
kind:
|
|
3286
|
-
label:
|
|
3287
|
-
displayRole:
|
|
3288
|
-
multiple:
|
|
3289
|
-
options:
|
|
3728
|
+
var normalizedFieldSchema = z9.object({
|
|
3729
|
+
key: z9.string().trim().min(1).max(255),
|
|
3730
|
+
kind: z9.string().trim().min(1).max(64).optional(),
|
|
3731
|
+
label: z9.string().trim().min(1).max(255).optional(),
|
|
3732
|
+
displayRole: z9.enum(["primary", "secondary"]).optional(),
|
|
3733
|
+
multiple: z9.boolean().optional(),
|
|
3734
|
+
options: z9.array(normalizedFieldOptionSchema).max(500).optional()
|
|
3290
3735
|
}).strict();
|
|
3291
|
-
var definitionSyncInputSchema =
|
|
3292
|
-
formKey:
|
|
3293
|
-
name:
|
|
3294
|
-
normalizedFieldsJson:
|
|
3295
|
-
schemaJson:
|
|
3296
|
-
sourceExportId:
|
|
3297
|
-
sourcePagePath:
|
|
3736
|
+
var definitionSyncInputSchema = z9.object({
|
|
3737
|
+
formKey: z9.string().trim().min(1).max(255),
|
|
3738
|
+
name: z9.string().trim().min(1).max(255),
|
|
3739
|
+
normalizedFieldsJson: z9.array(normalizedFieldSchema).min(1).max(500),
|
|
3740
|
+
schemaJson: z9.record(z9.unknown()),
|
|
3741
|
+
sourceExportId: z9.string().trim().min(1).max(255).optional(),
|
|
3742
|
+
sourcePagePath: z9.string().trim().min(1).max(2048).optional()
|
|
3298
3743
|
}).strict().superRefine((definition, context) => {
|
|
3299
3744
|
const ajv = new Ajv({
|
|
3300
3745
|
allErrors: true,
|
|
@@ -3306,7 +3751,7 @@ var definitionSyncInputSchema = z8.object({
|
|
|
3306
3751
|
ajv.compile(definition.schemaJson);
|
|
3307
3752
|
} catch {
|
|
3308
3753
|
context.addIssue({
|
|
3309
|
-
code:
|
|
3754
|
+
code: z9.ZodIssueCode.custom,
|
|
3310
3755
|
message: "schemaJson must be an executable JSON Schema supported by SiteOS Forms.",
|
|
3311
3756
|
path: ["schemaJson"]
|
|
3312
3757
|
});
|
|
@@ -3315,7 +3760,7 @@ var definitionSyncInputSchema = z8.object({
|
|
|
3315
3760
|
const duplicateKeys = fields.map((field) => field.key).filter((key, index, keys) => keys.indexOf(key) !== index);
|
|
3316
3761
|
for (const key of new Set(duplicateKeys)) {
|
|
3317
3762
|
context.addIssue({
|
|
3318
|
-
code:
|
|
3763
|
+
code: z9.ZodIssueCode.custom,
|
|
3319
3764
|
message: `Field key "${key}" is registered more than once.`,
|
|
3320
3765
|
path: ["normalizedFieldsJson"]
|
|
3321
3766
|
});
|
|
@@ -3325,7 +3770,7 @@ var definitionSyncInputSchema = z8.object({
|
|
|
3325
3770
|
);
|
|
3326
3771
|
if (primaryFields.length !== 1) {
|
|
3327
3772
|
context.addIssue({
|
|
3328
|
-
code:
|
|
3773
|
+
code: z9.ZodIssueCode.custom,
|
|
3329
3774
|
message: "Exactly one managed field must use the primary display role.",
|
|
3330
3775
|
path: ["normalizedFieldsJson"]
|
|
3331
3776
|
});
|
|
@@ -3333,7 +3778,7 @@ var definitionSyncInputSchema = z8.object({
|
|
|
3333
3778
|
const schemaProperties = definition.schemaJson.properties;
|
|
3334
3779
|
if (!isRecord(schemaProperties)) {
|
|
3335
3780
|
context.addIssue({
|
|
3336
|
-
code:
|
|
3781
|
+
code: z9.ZodIssueCode.custom,
|
|
3337
3782
|
message: "schemaJson.properties must define the managed form fields.",
|
|
3338
3783
|
path: ["schemaJson", "properties"]
|
|
3339
3784
|
});
|
|
@@ -3343,7 +3788,7 @@ var definitionSyncInputSchema = z8.object({
|
|
|
3343
3788
|
for (const key of Object.keys(schemaProperties)) {
|
|
3344
3789
|
if (!metadataKeys.has(key)) {
|
|
3345
3790
|
context.addIssue({
|
|
3346
|
-
code:
|
|
3791
|
+
code: z9.ZodIssueCode.custom,
|
|
3347
3792
|
message: `Schema field "${key}" is missing managed field metadata.`,
|
|
3348
3793
|
path: ["normalizedFieldsJson"]
|
|
3349
3794
|
});
|
|
@@ -3352,7 +3797,7 @@ var definitionSyncInputSchema = z8.object({
|
|
|
3352
3797
|
for (const [index, field] of fields.entries()) {
|
|
3353
3798
|
if (!Object.prototype.hasOwnProperty.call(schemaProperties, field.key)) {
|
|
3354
3799
|
context.addIssue({
|
|
3355
|
-
code:
|
|
3800
|
+
code: z9.ZodIssueCode.custom,
|
|
3356
3801
|
message: `Managed field "${field.key}" is missing from schemaJson.properties.`,
|
|
3357
3802
|
path: ["normalizedFieldsJson", index, "key"]
|
|
3358
3803
|
});
|
|
@@ -3362,26 +3807,26 @@ var definitionSyncInputSchema = z8.object({
|
|
|
3362
3807
|
const requiredFields = definition.schemaJson.required;
|
|
3363
3808
|
if (primaryField && (!Array.isArray(requiredFields) || !requiredFields.includes(primaryField.key))) {
|
|
3364
3809
|
context.addIssue({
|
|
3365
|
-
code:
|
|
3810
|
+
code: z9.ZodIssueCode.custom,
|
|
3366
3811
|
message: `Primary display field "${primaryField.key}" must be required by schemaJson.`,
|
|
3367
3812
|
path: ["schemaJson", "required"]
|
|
3368
3813
|
});
|
|
3369
3814
|
}
|
|
3370
3815
|
});
|
|
3371
|
-
var definitionManifestSchema =
|
|
3372
|
-
version:
|
|
3373
|
-
forms:
|
|
3816
|
+
var definitionManifestSchema = z9.object({
|
|
3817
|
+
version: z9.literal(1),
|
|
3818
|
+
forms: z9.array(z9.string().trim().min(1)).min(1)
|
|
3374
3819
|
});
|
|
3375
|
-
var submitInputSchema =
|
|
3376
|
-
formKey:
|
|
3377
|
-
idempotencyKey:
|
|
3378
|
-
ipAddress:
|
|
3379
|
-
payload:
|
|
3380
|
-
sourceUrl:
|
|
3381
|
-
userAgent:
|
|
3820
|
+
var submitInputSchema = z9.object({
|
|
3821
|
+
formKey: z9.string().trim().min(1),
|
|
3822
|
+
idempotencyKey: z9.string().trim().min(1),
|
|
3823
|
+
ipAddress: z9.string().trim().min(1).optional(),
|
|
3824
|
+
payload: z9.record(z9.unknown()),
|
|
3825
|
+
sourceUrl: z9.string().trim().min(1).optional(),
|
|
3826
|
+
userAgent: z9.string().trim().min(1).optional()
|
|
3382
3827
|
}).passthrough();
|
|
3383
|
-
var environmentNameSchema =
|
|
3384
|
-
var projectSlugSchema =
|
|
3828
|
+
var environmentNameSchema = z9.string().transform((value) => value.trim()).refine((value) => value.length >= 1 && value.length <= 255);
|
|
3829
|
+
var projectSlugSchema = z9.string().trim().min(1).max(120).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/);
|
|
3385
3830
|
var FORMS_HELP = `Usage:
|
|
3386
3831
|
siteos forms definition sync --environment <slug> --input <path> [--json]
|
|
3387
3832
|
siteos forms definition sync --environment <slug> --manifest <path> [--json]
|
|
@@ -3396,7 +3841,7 @@ var FORMS_HELP = `Usage:
|
|
|
3396
3841
|
siteos forms credential issue --environment <slug> --install [--name <name>] [--json]
|
|
3397
3842
|
siteos forms credential rotate --environment <slug> --install [--name <name>] [--json]
|
|
3398
3843
|
siteos forms credential revoke --environment <slug> --credential <credential-id> [--json]
|
|
3399
|
-
siteos forms submissions list --environment <slug> --form <form-id> [--query <text>] [--status <
|
|
3844
|
+
siteos forms submissions list --environment <slug> [--form <form-id>] [--query <text>] [--status <new|read|archived|spam|inbox|all>] [--purpose <regular|test|all>] [--version <id>] [--from <ISO>] [--to <ISO>] [--limit <1-100>] [--cursor <cursor>] [--json]
|
|
3400
3845
|
siteos forms submissions read --environment <slug> --form <form-id> --submission <id> [--json]
|
|
3401
3846
|
siteos forms submissions status --environment <slug> --form <form-id> --submission <id> --status <new|read|archived|spam> --expected-status <status> [--json]
|
|
3402
3847
|
siteos forms deploy --manifest <path> [--json]
|
|
@@ -3404,6 +3849,7 @@ var FORMS_HELP = `Usage:
|
|
|
3404
3849
|
siteos forms deployment-key list --environment <slug> [--json]
|
|
3405
3850
|
siteos forms deployment-key revoke --environment <slug> --credential <id> [--json]
|
|
3406
3851
|
siteos forms submit --input <path> [--json]
|
|
3852
|
+
${FORMS_WORKSPACE_HELP}
|
|
3407
3853
|
|
|
3408
3854
|
Manage SiteOS Forms Environments, definitions, credentials, and submission smoke tests.`;
|
|
3409
3855
|
async function runFormsCommand(options) {
|
|
@@ -3428,6 +3874,8 @@ async function runFormsCommand(options) {
|
|
|
3428
3874
|
);
|
|
3429
3875
|
if (args2[0] === "deploy")
|
|
3430
3876
|
return runDeployment({ ...options, args: args2.slice(1) });
|
|
3877
|
+
if (args2[0] === "contacts" || args2[0] === "submissions" && ["bulk", "export"].includes(args2[1] ?? ""))
|
|
3878
|
+
return runFormsWorkspaceCommand({ ...options, args: args2 });
|
|
3431
3879
|
if (args2[0] !== "submit") {
|
|
3432
3880
|
try {
|
|
3433
3881
|
args2 = await commonEnvironmentArguments(options, "forms", args2);
|
|
@@ -3478,7 +3926,16 @@ async function runSubmissionsCommand(options) {
|
|
|
3478
3926
|
"--environment",
|
|
3479
3927
|
"--form",
|
|
3480
3928
|
"--json",
|
|
3481
|
-
...action === "list" ? [
|
|
3929
|
+
...action === "list" ? [
|
|
3930
|
+
"--query",
|
|
3931
|
+
"--status",
|
|
3932
|
+
"--purpose",
|
|
3933
|
+
"--version",
|
|
3934
|
+
"--from",
|
|
3935
|
+
"--to",
|
|
3936
|
+
"--limit",
|
|
3937
|
+
"--cursor"
|
|
3938
|
+
] : [
|
|
3482
3939
|
"--submission",
|
|
3483
3940
|
...action === "status" ? ["--status", "--expected-status"] : []
|
|
3484
3941
|
]
|
|
@@ -3493,22 +3950,34 @@ async function runSubmissionsCommand(options) {
|
|
|
3493
3950
|
const environmentSlug = value("environment");
|
|
3494
3951
|
const formId = value("form");
|
|
3495
3952
|
const submissionId = value("submission");
|
|
3496
|
-
if (!environmentSlug || !isValidEnvironmentSlug(environmentSlug) || !formId || formId
|
|
3953
|
+
if (!environmentSlug || !isValidEnvironmentSlug(environmentSlug) || action !== "list" && !formId || (formId?.length ?? 0) > 255)
|
|
3497
3954
|
return usageError2("Provide a valid --environment and --form.");
|
|
3498
3955
|
if (action !== "list" && (!submissionId || submissionId.length > 255))
|
|
3499
3956
|
return usageError2("Provide --submission.");
|
|
3500
|
-
const
|
|
3957
|
+
const status2 = formsSubmissionStatusSchema.safeParse(value("status"));
|
|
3501
3958
|
const expected = formsSubmissionStatusSchema.safeParse(
|
|
3502
3959
|
value("expected-status")
|
|
3503
3960
|
);
|
|
3504
|
-
if (value("status") && !
|
|
3961
|
+
if (value("status") && !status2.success && !(action === "list" && ["inbox", "all"].includes(value("status"))) || action === "status" && (!status2.success || !expected.success))
|
|
3505
3962
|
return usageError2(
|
|
3506
3963
|
"Use new, read, archived or spam for --status and --expected-status."
|
|
3507
3964
|
);
|
|
3508
|
-
const
|
|
3509
|
-
|
|
3965
|
+
const filters3 = new URLSearchParams();
|
|
3966
|
+
if (value("purpose") && !["regular", "test", "all"].includes(value("purpose")))
|
|
3967
|
+
return usageError2("Use regular, test or all for --purpose.");
|
|
3968
|
+
if (value("version")) filters3.set("versionId", value("version"));
|
|
3969
|
+
for (const key of [
|
|
3970
|
+
"query",
|
|
3971
|
+
"status",
|
|
3972
|
+
"purpose",
|
|
3973
|
+
"from",
|
|
3974
|
+
"to",
|
|
3975
|
+
"limit",
|
|
3976
|
+
"cursor"
|
|
3977
|
+
]) {
|
|
3510
3978
|
const item = value(key);
|
|
3511
|
-
if (item
|
|
3979
|
+
if (item && !(["status", "purpose"].includes(key) && item === "all"))
|
|
3980
|
+
filters3.set(key, item);
|
|
3512
3981
|
}
|
|
3513
3982
|
const limit = Number(value("limit") ?? 50);
|
|
3514
3983
|
if (!Number.isInteger(limit) || limit < 1 || limit > 100)
|
|
@@ -3516,7 +3985,7 @@ async function runSubmissionsCommand(options) {
|
|
|
3516
3985
|
if ((value("query")?.length ?? 0) > 200 || (value("cursor")?.length ?? 0) > 2048)
|
|
3517
3986
|
return usageError2("The query or cursor is too long.");
|
|
3518
3987
|
for (const key of ["from", "to"])
|
|
3519
|
-
if (value(key) && !
|
|
3988
|
+
if (value(key) && !z9.string().datetime({ offset: true }).safeParse(value(key)).success)
|
|
3520
3989
|
return usageError2(
|
|
3521
3990
|
`--${key} must be an ISO timestamp including a timezone.`
|
|
3522
3991
|
);
|
|
@@ -3545,13 +4014,15 @@ async function runSubmissionsCommand(options) {
|
|
|
3545
4014
|
environmentId: environment.id,
|
|
3546
4015
|
formId
|
|
3547
4016
|
};
|
|
3548
|
-
const response = action === "list" ? await context.client.listSubmissions({ ...input, filters:
|
|
4017
|
+
const response = action === "list" ? await context.client.listSubmissions({ ...input, filters: filters3 }) : action === "read" ? await context.client.readSubmission({
|
|
3549
4018
|
...input,
|
|
4019
|
+
formId,
|
|
3550
4020
|
submissionId
|
|
3551
4021
|
}) : await context.client.updateSubmissionStatus({
|
|
3552
4022
|
...input,
|
|
4023
|
+
formId,
|
|
3553
4024
|
submissionId,
|
|
3554
|
-
status:
|
|
4025
|
+
status: status2.data,
|
|
3555
4026
|
expectedStatus: expected.data
|
|
3556
4027
|
});
|
|
3557
4028
|
return { exitCode: 0, stdout: stringifySafeJson(response) };
|
|
@@ -3781,14 +4252,14 @@ async function runDefinitionLifecycle(options) {
|
|
|
3781
4252
|
const formId = value("form");
|
|
3782
4253
|
if (!environmentSlug || !isValidEnvironmentSlug(environmentSlug) || action !== "list" && (!formId || formId.length > 255))
|
|
3783
4254
|
return usageError2("Provide a valid --environment and --form.");
|
|
3784
|
-
const
|
|
3785
|
-
if (!["active", "inactive", "all"].includes(
|
|
4255
|
+
const status2 = value("status") ?? "active";
|
|
4256
|
+
if (!["active", "inactive", "all"].includes(status2))
|
|
3786
4257
|
return usageError2("Use active, inactive or all for --status.");
|
|
3787
4258
|
const apply = parsed.flags.get("--apply") === true;
|
|
3788
|
-
const
|
|
4259
|
+
const revision2 = Number(value("expected-revision"));
|
|
3789
4260
|
const count = Number(value("expected-submissions"));
|
|
3790
4261
|
const confirmKey = value("confirm");
|
|
3791
|
-
if ((action === "archive" || action === "restore" || apply) && (!Number.isSafeInteger(
|
|
4262
|
+
if ((action === "archive" || action === "restore" || apply) && (!Number.isSafeInteger(revision2) || revision2 < 1))
|
|
3792
4263
|
return usageError2(
|
|
3793
4264
|
"Read the form first, then provide its --expected-revision."
|
|
3794
4265
|
);
|
|
@@ -3831,7 +4302,7 @@ async function runDefinitionLifecycle(options) {
|
|
|
3831
4302
|
stdout: stringifySafeJson(
|
|
3832
4303
|
await context.client.listDefinitions({
|
|
3833
4304
|
...input,
|
|
3834
|
-
status
|
|
4305
|
+
status: status2
|
|
3835
4306
|
})
|
|
3836
4307
|
)
|
|
3837
4308
|
};
|
|
@@ -3864,11 +4335,11 @@ async function runDefinitionLifecycle(options) {
|
|
|
3864
4335
|
change: action === "delete" ? {
|
|
3865
4336
|
action,
|
|
3866
4337
|
confirmKey,
|
|
3867
|
-
expectedRevision:
|
|
4338
|
+
expectedRevision: revision2,
|
|
3868
4339
|
expectedSubmissionCount: count
|
|
3869
4340
|
} : {
|
|
3870
4341
|
action,
|
|
3871
|
-
expectedRevision:
|
|
4342
|
+
expectedRevision: revision2
|
|
3872
4343
|
}
|
|
3873
4344
|
});
|
|
3874
4345
|
return {
|
|
@@ -4223,7 +4694,7 @@ async function loadFormDefinitions(options) {
|
|
|
4223
4694
|
ok: true
|
|
4224
4695
|
};
|
|
4225
4696
|
}
|
|
4226
|
-
const manifestPath =
|
|
4697
|
+
const manifestPath = path11.resolve(
|
|
4227
4698
|
options.cwd ?? process.cwd(),
|
|
4228
4699
|
options.source.path
|
|
4229
4700
|
);
|
|
@@ -4233,10 +4704,10 @@ async function loadFormDefinitions(options) {
|
|
|
4233
4704
|
schema: definitionManifestSchema
|
|
4234
4705
|
});
|
|
4235
4706
|
if (!manifest.ok) return manifest;
|
|
4236
|
-
const manifestDirectory =
|
|
4707
|
+
const manifestDirectory = path11.dirname(manifestPath);
|
|
4237
4708
|
const definitions = [];
|
|
4238
4709
|
for (const definitionPath of manifest.value.forms) {
|
|
4239
|
-
if (
|
|
4710
|
+
if (path11.isAbsolute(definitionPath)) {
|
|
4240
4711
|
return {
|
|
4241
4712
|
error: usageError2(
|
|
4242
4713
|
`Manifest definition paths must be relative: ${definitionPath}`
|
|
@@ -4244,9 +4715,9 @@ async function loadFormDefinitions(options) {
|
|
|
4244
4715
|
ok: false
|
|
4245
4716
|
};
|
|
4246
4717
|
}
|
|
4247
|
-
const resolvedPath =
|
|
4248
|
-
const relativePath =
|
|
4249
|
-
if (relativePath.startsWith(`..${
|
|
4718
|
+
const resolvedPath = path11.resolve(manifestDirectory, definitionPath);
|
|
4719
|
+
const relativePath = path11.relative(manifestDirectory, resolvedPath);
|
|
4720
|
+
if (relativePath.startsWith(`..${path11.sep}`) || relativePath === "..") {
|
|
4250
4721
|
return {
|
|
4251
4722
|
error: usageError2(
|
|
4252
4723
|
`Manifest definition paths must stay inside the manifest directory: ${definitionPath}`
|
|
@@ -4309,11 +4780,11 @@ function parseInputFlags(args2, options) {
|
|
|
4309
4780
|
};
|
|
4310
4781
|
}
|
|
4311
4782
|
async function readInputFile(options) {
|
|
4312
|
-
const resolvedInputPath =
|
|
4783
|
+
const resolvedInputPath = path11.resolve(
|
|
4313
4784
|
options.cwd ?? process.cwd(),
|
|
4314
4785
|
options.inputPath
|
|
4315
4786
|
);
|
|
4316
|
-
const rawInput = await
|
|
4787
|
+
const rawInput = await readFile6(resolvedInputPath, "utf-8").catch(
|
|
4317
4788
|
(error) => {
|
|
4318
4789
|
const message = error instanceof Error ? error.message : String(error);
|
|
4319
4790
|
return {
|
|
@@ -4612,11 +5083,11 @@ async function runDeployment(options) {
|
|
|
4612
5083
|
|
|
4613
5084
|
// src/package-metadata.ts
|
|
4614
5085
|
import { readFileSync } from "fs";
|
|
4615
|
-
import
|
|
5086
|
+
import path12 from "path";
|
|
4616
5087
|
import { fileURLToPath } from "url";
|
|
4617
5088
|
function readPackageMetadata() {
|
|
4618
|
-
const packageJsonPath =
|
|
4619
|
-
|
|
5089
|
+
const packageJsonPath = path12.resolve(
|
|
5090
|
+
path12.dirname(fileURLToPath(import.meta.url)),
|
|
4620
5091
|
"..",
|
|
4621
5092
|
"package.json"
|
|
4622
5093
|
);
|
|
@@ -4630,8 +5101,8 @@ function readPackageMetadata() {
|
|
|
4630
5101
|
}
|
|
4631
5102
|
|
|
4632
5103
|
// src/pulse-command.ts
|
|
4633
|
-
import
|
|
4634
|
-
import { parseArgs } from "util";
|
|
5104
|
+
import path23 from "path";
|
|
5105
|
+
import { parseArgs as parseArgs2 } from "util";
|
|
4635
5106
|
|
|
4636
5107
|
// src/core/errors.ts
|
|
4637
5108
|
function commandError2(input) {
|
|
@@ -4642,29 +5113,29 @@ function commandError2(input) {
|
|
|
4642
5113
|
}
|
|
4643
5114
|
|
|
4644
5115
|
// src/pulse/api-client.ts
|
|
4645
|
-
import { readFile as
|
|
4646
|
-
import { z as
|
|
4647
|
-
var projectSchema =
|
|
4648
|
-
baseUrl:
|
|
4649
|
-
checkCount:
|
|
4650
|
-
id:
|
|
4651
|
-
name:
|
|
4652
|
-
slug:
|
|
5116
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
5117
|
+
import { z as z10 } from "zod";
|
|
5118
|
+
var projectSchema = z10.object({
|
|
5119
|
+
baseUrl: z10.string().url().nullable(),
|
|
5120
|
+
checkCount: z10.number().int().min(0),
|
|
5121
|
+
id: z10.string().trim().min(1).max(120),
|
|
5122
|
+
name: z10.string().trim().min(1).max(120),
|
|
5123
|
+
slug: z10.string().trim().min(1).max(80).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/)
|
|
4653
5124
|
}).strict();
|
|
4654
|
-
var projectResponseSchema =
|
|
4655
|
-
var deploymentResponseSchema2 =
|
|
4656
|
-
checks:
|
|
4657
|
-
|
|
4658
|
-
checkId:
|
|
4659
|
-
created:
|
|
4660
|
-
deploymentId:
|
|
4661
|
-
slug:
|
|
4662
|
-
version:
|
|
5125
|
+
var projectResponseSchema = z10.object({ project: projectSchema }).strict();
|
|
5126
|
+
var deploymentResponseSchema2 = z10.object({
|
|
5127
|
+
checks: z10.array(
|
|
5128
|
+
z10.object({
|
|
5129
|
+
checkId: z10.string().min(1),
|
|
5130
|
+
created: z10.boolean(),
|
|
5131
|
+
deploymentId: z10.string().min(1),
|
|
5132
|
+
slug: z10.string().min(1),
|
|
5133
|
+
version: z10.number().int().positive()
|
|
4663
5134
|
}).strict()
|
|
4664
5135
|
),
|
|
4665
|
-
checksum:
|
|
4666
|
-
dashboardUrl:
|
|
4667
|
-
projectId:
|
|
5136
|
+
checksum: z10.string().min(1),
|
|
5137
|
+
dashboardUrl: z10.string().url().nullable().optional(),
|
|
5138
|
+
projectId: z10.string().min(1)
|
|
4668
5139
|
}).strict();
|
|
4669
5140
|
function createPulseApiClient(input) {
|
|
4670
5141
|
const fetchImpl = input.fetchImpl ?? fetch;
|
|
@@ -4724,7 +5195,7 @@ function createPulseApiClient(input) {
|
|
|
4724
5195
|
);
|
|
4725
5196
|
form.set(
|
|
4726
5197
|
"bundle",
|
|
4727
|
-
new Blob([await
|
|
5198
|
+
new Blob([await readFile7(options.manifest.archivePath)], {
|
|
4728
5199
|
type: "application/gzip"
|
|
4729
5200
|
}),
|
|
4730
5201
|
"checks-bundle.tgz"
|
|
@@ -4739,11 +5210,11 @@ function createPulseApiClient(input) {
|
|
|
4739
5210
|
}
|
|
4740
5211
|
};
|
|
4741
5212
|
}
|
|
4742
|
-
function pulseApiError(body,
|
|
5213
|
+
function pulseApiError(body, status2) {
|
|
4743
5214
|
const payload = body;
|
|
4744
5215
|
const candidate = payload?.error;
|
|
4745
|
-
const code = candidate && typeof candidate === "object" && typeof candidate.code === "string" ? candidate.code :
|
|
4746
|
-
const message = typeof candidate === "string" ? candidate : candidate && typeof candidate === "object" && typeof candidate.message === "string" ? candidate.message : `SiteOS Pulse returned ${
|
|
5216
|
+
const code = candidate && typeof candidate === "object" && typeof candidate.code === "string" ? candidate.code : status2 === 401 ? "UNAUTHORIZED" : status2 === 403 ? "FORBIDDEN" : status2 === 404 ? "NOT_FOUND" : status2 === 409 ? "CONFLICT" : "API_REJECTED";
|
|
5217
|
+
const message = typeof candidate === "string" ? candidate : candidate && typeof candidate === "object" && typeof candidate.message === "string" ? candidate.message : `SiteOS Pulse returned ${status2}.`;
|
|
4747
5218
|
return new CliError(code, message);
|
|
4748
5219
|
}
|
|
4749
5220
|
|
|
@@ -4752,18 +5223,18 @@ import { createHash as createHash6, randomUUID as randomUUID4 } from "crypto";
|
|
|
4752
5223
|
import {
|
|
4753
5224
|
access,
|
|
4754
5225
|
mkdir as mkdir3,
|
|
4755
|
-
readFile as
|
|
5226
|
+
readFile as readFile9,
|
|
4756
5227
|
rename as rename5,
|
|
4757
5228
|
rm as rm4,
|
|
4758
|
-
writeFile as
|
|
5229
|
+
writeFile as writeFile6
|
|
4759
5230
|
} from "fs/promises";
|
|
4760
|
-
import
|
|
5231
|
+
import path14 from "path";
|
|
4761
5232
|
import * as tar from "tar";
|
|
4762
5233
|
|
|
4763
5234
|
// src/pulse/project-files.ts
|
|
4764
5235
|
import { createHash as createHash5 } from "crypto";
|
|
4765
|
-
import { lstat as lstat5, readFile as
|
|
4766
|
-
import
|
|
5236
|
+
import { lstat as lstat5, readFile as readFile8 } from "fs/promises";
|
|
5237
|
+
import path13 from "path";
|
|
4767
5238
|
import fg from "fast-glob";
|
|
4768
5239
|
var MAX_BUNDLE_FILES = 5e3;
|
|
4769
5240
|
var MAX_BUNDLE_BYTES = 50 * 1024 * 1024;
|
|
@@ -4786,7 +5257,7 @@ function isSensitive(relativePath) {
|
|
|
4786
5257
|
return SENSITIVE_FILES.some((pattern) => pattern.test(segments.at(-1) ?? ""));
|
|
4787
5258
|
}
|
|
4788
5259
|
async function hashFile(filePath) {
|
|
4789
|
-
return createHash5("sha256").update(await
|
|
5260
|
+
return createHash5("sha256").update(await readFile8(filePath)).digest("hex");
|
|
4790
5261
|
}
|
|
4791
5262
|
async function discoverProjectFiles(projectRoot, config) {
|
|
4792
5263
|
const errors = [];
|
|
@@ -4859,9 +5330,9 @@ async function discoverProjectFiles(projectRoot, config) {
|
|
|
4859
5330
|
errors.push(`Sensitive path cannot be included: ${relativePath}`);
|
|
4860
5331
|
continue;
|
|
4861
5332
|
}
|
|
4862
|
-
const absolutePath =
|
|
4863
|
-
const relativeToRoot =
|
|
4864
|
-
if (relativeToRoot.startsWith("..") ||
|
|
5333
|
+
const absolutePath = path13.resolve(projectRoot, relativePath);
|
|
5334
|
+
const relativeToRoot = path13.relative(projectRoot, absolutePath);
|
|
5335
|
+
if (relativeToRoot.startsWith("..") || path13.isAbsolute(relativeToRoot)) {
|
|
4865
5336
|
errors.push(`Included path escapes the project root: ${relativePath}`);
|
|
4866
5337
|
continue;
|
|
4867
5338
|
}
|
|
@@ -4881,8 +5352,8 @@ async function discoverProjectFiles(projectRoot, config) {
|
|
|
4881
5352
|
if (totalBytes > MAX_BUNDLE_BYTES)
|
|
4882
5353
|
errors.push("The bundle exceeds the 50 MiB size limit.");
|
|
4883
5354
|
try {
|
|
4884
|
-
const packageSource = await
|
|
4885
|
-
|
|
5355
|
+
const packageSource = await readFile8(
|
|
5356
|
+
path13.join(projectRoot, "package.json"),
|
|
4886
5357
|
"utf8"
|
|
4887
5358
|
);
|
|
4888
5359
|
const packageJson = JSON.parse(packageSource);
|
|
@@ -4904,10 +5375,10 @@ async function discoverProjectFiles(projectRoot, config) {
|
|
|
4904
5375
|
|
|
4905
5376
|
// src/pulse/bundle.ts
|
|
4906
5377
|
async function hashArchive(archivePath) {
|
|
4907
|
-
return createHash6("sha256").update(await
|
|
5378
|
+
return createHash6("sha256").update(await readFile9(archivePath)).digest("hex");
|
|
4908
5379
|
}
|
|
4909
5380
|
async function createDeploymentBundle(input) {
|
|
4910
|
-
const projectRoot =
|
|
5381
|
+
const projectRoot = path14.resolve(input.projectRoot);
|
|
4911
5382
|
const config = await loadChecksConfig(projectRoot);
|
|
4912
5383
|
const configFile = await findChecksConfigFile(projectRoot) ?? CONFIG_FILE;
|
|
4913
5384
|
const report = await discoverProjectFiles(projectRoot, config);
|
|
@@ -4920,9 +5391,9 @@ async function createDeploymentBundle(input) {
|
|
|
4920
5391
|
}
|
|
4921
5392
|
);
|
|
4922
5393
|
}
|
|
4923
|
-
const outputDirectory = input.output ?
|
|
5394
|
+
const outputDirectory = input.output ? path14.dirname(path14.resolve(projectRoot, input.output)) : path14.join(projectRoot, ".siteos", "pulse", "deployments");
|
|
4924
5395
|
await mkdir3(outputDirectory, { recursive: true });
|
|
4925
|
-
const temporaryPath =
|
|
5396
|
+
const temporaryPath = path14.join(
|
|
4926
5397
|
outputDirectory,
|
|
4927
5398
|
`.siteos-pulse-${randomUUID4()}.tgz`
|
|
4928
5399
|
);
|
|
@@ -4941,7 +5412,7 @@ async function createDeploymentBundle(input) {
|
|
|
4941
5412
|
archiveFiles
|
|
4942
5413
|
);
|
|
4943
5414
|
const checksum2 = await hashArchive(temporaryPath);
|
|
4944
|
-
const archivePath = input.output ?
|
|
5415
|
+
const archivePath = input.output ? path14.resolve(projectRoot, input.output) : path14.join(outputDirectory, `siteos-pulse-${checksum2.slice(0, 16)}.tgz`);
|
|
4945
5416
|
try {
|
|
4946
5417
|
await access(archivePath);
|
|
4947
5418
|
if (input.output) {
|
|
@@ -4971,7 +5442,7 @@ async function createDeploymentBundle(input) {
|
|
|
4971
5442
|
files: report.files,
|
|
4972
5443
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4973
5444
|
};
|
|
4974
|
-
await
|
|
5445
|
+
await writeFile6(
|
|
4975
5446
|
`${archivePath}.json`,
|
|
4976
5447
|
`${JSON.stringify(manifest, null, 2)}
|
|
4977
5448
|
`,
|
|
@@ -4981,13 +5452,13 @@ async function createDeploymentBundle(input) {
|
|
|
4981
5452
|
}
|
|
4982
5453
|
|
|
4983
5454
|
// src/pulse/init.ts
|
|
4984
|
-
import { mkdir as mkdir4, readFile as
|
|
4985
|
-
import
|
|
5455
|
+
import { mkdir as mkdir4, readFile as readFile10, writeFile as writeFile7 } from "fs/promises";
|
|
5456
|
+
import path15 from "path";
|
|
4986
5457
|
var SITEOS_PLAYWRIGHT_CONFIG = "siteos.playwright.config.ts";
|
|
4987
5458
|
var PULSE_CACHE_DIRECTORY = ".siteos/pulse/";
|
|
4988
5459
|
async function exists(filePath) {
|
|
4989
5460
|
try {
|
|
4990
|
-
await
|
|
5461
|
+
await readFile10(filePath);
|
|
4991
5462
|
return true;
|
|
4992
5463
|
} catch (error) {
|
|
4993
5464
|
if (error.code === "ENOENT") return false;
|
|
@@ -5022,7 +5493,7 @@ export default defineConfig({
|
|
|
5022
5493
|
`;
|
|
5023
5494
|
}
|
|
5024
5495
|
async function createPulsePlaywrightConfig(input) {
|
|
5025
|
-
const configPath =
|
|
5496
|
+
const configPath = path15.join(input.projectRoot, SITEOS_PLAYWRIGHT_CONFIG);
|
|
5026
5497
|
if (await exists(configPath)) {
|
|
5027
5498
|
throw new CliError(
|
|
5028
5499
|
"PULSE_PLAYWRIGHT_CONFIG_EXISTS",
|
|
@@ -5032,34 +5503,34 @@ async function createPulsePlaywrightConfig(input) {
|
|
|
5032
5503
|
}
|
|
5033
5504
|
);
|
|
5034
5505
|
}
|
|
5035
|
-
await
|
|
5506
|
+
await writeFile7(configPath, pulsePlaywrightConfigSource(input), {
|
|
5036
5507
|
encoding: "utf8",
|
|
5037
5508
|
flag: "wx"
|
|
5038
5509
|
});
|
|
5039
5510
|
}
|
|
5040
5511
|
async function detectPackageName(projectRoot) {
|
|
5041
5512
|
try {
|
|
5042
|
-
const contents = await
|
|
5043
|
-
|
|
5513
|
+
const contents = await readFile10(
|
|
5514
|
+
path15.join(projectRoot, "package.json"),
|
|
5044
5515
|
"utf8"
|
|
5045
5516
|
);
|
|
5046
5517
|
const parsed = JSON.parse(contents);
|
|
5047
|
-
return typeof parsed.name === "string" ? parsed.name :
|
|
5518
|
+
return typeof parsed.name === "string" ? parsed.name : path15.basename(projectRoot);
|
|
5048
5519
|
} catch {
|
|
5049
|
-
return
|
|
5520
|
+
return path15.basename(projectRoot);
|
|
5050
5521
|
}
|
|
5051
5522
|
}
|
|
5052
5523
|
async function ensureGitignore(projectRoot) {
|
|
5053
|
-
const gitignorePath =
|
|
5524
|
+
const gitignorePath = path15.join(projectRoot, ".gitignore");
|
|
5054
5525
|
let contents = "";
|
|
5055
5526
|
try {
|
|
5056
|
-
contents = await
|
|
5527
|
+
contents = await readFile10(gitignorePath, "utf8");
|
|
5057
5528
|
} catch (error) {
|
|
5058
5529
|
if (error.code !== "ENOENT") throw error;
|
|
5059
5530
|
}
|
|
5060
5531
|
if (contents.split(/\r?\n/).includes(PULSE_CACHE_DIRECTORY)) return false;
|
|
5061
5532
|
const prefix = contents.length > 0 && !contents.endsWith("\n") ? "\n" : "";
|
|
5062
|
-
await
|
|
5533
|
+
await writeFile7(
|
|
5063
5534
|
gitignorePath,
|
|
5064
5535
|
`${contents}${prefix}${PULSE_CACHE_DIRECTORY}
|
|
5065
5536
|
`,
|
|
@@ -5068,21 +5539,21 @@ async function ensureGitignore(projectRoot) {
|
|
|
5068
5539
|
return true;
|
|
5069
5540
|
}
|
|
5070
5541
|
async function initializeProject(input) {
|
|
5071
|
-
const projectRoot =
|
|
5542
|
+
const projectRoot = path15.resolve(input.projectRoot);
|
|
5072
5543
|
const existingConfig = await findChecksConfigFile(projectRoot);
|
|
5073
5544
|
if (existingConfig) {
|
|
5074
5545
|
throw new CliError("CONFIG_EXISTS", `${existingConfig} already exists.`, {
|
|
5075
5546
|
hint: "Edit the existing file or run `siteos pulse validate`."
|
|
5076
5547
|
});
|
|
5077
5548
|
}
|
|
5078
|
-
const configPath =
|
|
5549
|
+
const configPath = path15.join(projectRoot, CONFIG_FILE);
|
|
5079
5550
|
const created = [];
|
|
5080
5551
|
const updated = [];
|
|
5081
5552
|
const testsDir = input.testsDir ?? "tests/siteos";
|
|
5082
5553
|
const packageName2 = await detectPackageName(projectRoot);
|
|
5083
5554
|
const projectName = projectDisplayName(input.project ?? packageName2);
|
|
5084
5555
|
const checkSlug = projectSlug(input.suite ?? "homepage");
|
|
5085
|
-
const exampleTest =
|
|
5556
|
+
const exampleTest = path15.posix.join(
|
|
5086
5557
|
testsDir.replaceAll("\\", "/"),
|
|
5087
5558
|
`${checkSlug}.spec.ts`
|
|
5088
5559
|
);
|
|
@@ -5119,11 +5590,11 @@ async function initializeProject(input) {
|
|
|
5119
5590
|
baseUrl: input.baseUrl
|
|
5120
5591
|
});
|
|
5121
5592
|
created.push(SITEOS_PLAYWRIGHT_CONFIG);
|
|
5122
|
-
const testDirectory =
|
|
5593
|
+
const testDirectory = path15.join(projectRoot, testsDir);
|
|
5123
5594
|
await mkdir4(testDirectory, { recursive: true });
|
|
5124
|
-
if (!await exists(
|
|
5125
|
-
await
|
|
5126
|
-
|
|
5595
|
+
if (!await exists(path15.join(projectRoot, exampleTest))) {
|
|
5596
|
+
await writeFile7(
|
|
5597
|
+
path15.join(projectRoot, exampleTest),
|
|
5127
5598
|
`import { expect, test } from "@playwright/test";
|
|
5128
5599
|
|
|
5129
5600
|
test(${JSON.stringify(`${projectDisplayName(input.suite ?? "homepage")} is reachable`)}, async ({ page }) => {
|
|
@@ -5135,7 +5606,7 @@ test(${JSON.stringify(`${projectDisplayName(input.suite ?? "homepage")} is reach
|
|
|
5135
5606
|
);
|
|
5136
5607
|
created.push(exampleTest);
|
|
5137
5608
|
}
|
|
5138
|
-
await
|
|
5609
|
+
await writeFile7(configPath, `${JSON.stringify(config, null, 2)}
|
|
5139
5610
|
`, {
|
|
5140
5611
|
encoding: "utf8",
|
|
5141
5612
|
flag: "wx"
|
|
@@ -5149,14 +5620,14 @@ test(${JSON.stringify(`${projectDisplayName(input.suite ?? "homepage")} is reach
|
|
|
5149
5620
|
import fg4 from "fast-glob";
|
|
5150
5621
|
|
|
5151
5622
|
// src/pulse/interactive/discovery.ts
|
|
5152
|
-
import { readFile as
|
|
5623
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
5153
5624
|
import { createRequire } from "module";
|
|
5154
|
-
import
|
|
5625
|
+
import path17 from "path";
|
|
5155
5626
|
import fg2 from "fast-glob";
|
|
5156
5627
|
|
|
5157
5628
|
// src/pulse/interactive/playwright-config.ts
|
|
5158
|
-
import { readFile as
|
|
5159
|
-
import
|
|
5629
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
5630
|
+
import path16 from "path";
|
|
5160
5631
|
|
|
5161
5632
|
// src/pulse/interactive/discovery.ts
|
|
5162
5633
|
var PLAYWRIGHT_SPEC_PATTERN = "**/*.{spec,test}.{js,cjs,mjs,ts,cts,mts,jsx,tsx}";
|
|
@@ -5201,11 +5672,11 @@ async function discoverPlaywrightSelectorMatches(projectRoot, selectors, testsDi
|
|
|
5201
5672
|
}
|
|
5202
5673
|
|
|
5203
5674
|
// src/pulse/sync-plan.ts
|
|
5204
|
-
import
|
|
5675
|
+
import path18 from "path";
|
|
5205
5676
|
import fg3 from "fast-glob";
|
|
5206
5677
|
var PLAYWRIGHT_SPEC_FILE = /\.(?:spec|test)\.(?:[cm]?[jt]sx?)$/;
|
|
5207
5678
|
function defaultNameFromSpec(specPath) {
|
|
5208
|
-
const basename =
|
|
5679
|
+
const basename = path18.posix.basename(specPath).replace(/\.(?:spec|test)\.[^.]+$/, "");
|
|
5209
5680
|
return basename.split(/[^A-Za-z0-9]+/).filter(Boolean).map((word) => `${word.charAt(0).toUpperCase()}${word.slice(1)}`).join(" ") || "Smoke";
|
|
5210
5681
|
}
|
|
5211
5682
|
function normalizedPaths(paths) {
|
|
@@ -5263,8 +5734,8 @@ function buildSyncPlan(input) {
|
|
|
5263
5734
|
|
|
5264
5735
|
// src/pulse/test-imports.ts
|
|
5265
5736
|
import { builtinModules } from "module";
|
|
5266
|
-
import { readFile as
|
|
5267
|
-
import
|
|
5737
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
5738
|
+
import path19 from "path";
|
|
5268
5739
|
import { build } from "esbuild";
|
|
5269
5740
|
var ALLOWED_PACKAGES = /* @__PURE__ */ new Set(["@playwright/test"]);
|
|
5270
5741
|
var BUILTINS = /* @__PURE__ */ new Set([
|
|
@@ -5278,9 +5749,9 @@ function packageName(importPath) {
|
|
|
5278
5749
|
return importPath.split("/")[0] ?? importPath;
|
|
5279
5750
|
}
|
|
5280
5751
|
function normalizedRelativePath(projectRoot, inputPath) {
|
|
5281
|
-
const absolute =
|
|
5282
|
-
const relative =
|
|
5283
|
-
if (!relative || relative.startsWith("../") ||
|
|
5752
|
+
const absolute = path19.isAbsolute(inputPath) ? inputPath : path19.resolve(projectRoot, inputPath);
|
|
5753
|
+
const relative = path19.relative(projectRoot, absolute).replaceAll("\\", "/");
|
|
5754
|
+
if (!relative || relative.startsWith("../") || path19.isAbsolute(relative)) {
|
|
5284
5755
|
throw new CliError(
|
|
5285
5756
|
"IMPORT_OUTSIDE_PROJECT",
|
|
5286
5757
|
`A selected test imports outside the project: ${inputPath}`
|
|
@@ -5289,7 +5760,7 @@ function normalizedRelativePath(projectRoot, inputPath) {
|
|
|
5289
5760
|
return relative;
|
|
5290
5761
|
}
|
|
5291
5762
|
async function analyzeTestImports(input) {
|
|
5292
|
-
const projectRoot =
|
|
5763
|
+
const projectRoot = path19.resolve(input.projectRoot);
|
|
5293
5764
|
const entryFiles = [
|
|
5294
5765
|
...new Set(
|
|
5295
5766
|
input.entryFiles.map((file) => normalizedRelativePath(projectRoot, file))
|
|
@@ -5319,7 +5790,7 @@ async function analyzeTestImports(input) {
|
|
|
5319
5790
|
}
|
|
5320
5791
|
);
|
|
5321
5792
|
}
|
|
5322
|
-
const
|
|
5793
|
+
const inputs2 = Object.keys(result.metafile?.inputs ?? {}).map(
|
|
5323
5794
|
(file) => normalizedRelativePath(projectRoot, file)
|
|
5324
5795
|
);
|
|
5325
5796
|
const entrySet = new Set(entryFiles);
|
|
@@ -5347,8 +5818,8 @@ async function analyzeTestImports(input) {
|
|
|
5347
5818
|
const environmentVariables = /* @__PURE__ */ new Set();
|
|
5348
5819
|
const compatibilityIssues = /* @__PURE__ */ new Set();
|
|
5349
5820
|
await Promise.all(
|
|
5350
|
-
|
|
5351
|
-
const source = await
|
|
5821
|
+
inputs2.map(async (file) => {
|
|
5822
|
+
const source = await readFile13(path19.join(projectRoot, file), "utf8").catch(
|
|
5352
5823
|
() => ""
|
|
5353
5824
|
);
|
|
5354
5825
|
for (const match of source.matchAll(ENVIRONMENT_PATTERN)) {
|
|
@@ -5365,7 +5836,7 @@ async function analyzeTestImports(input) {
|
|
|
5365
5836
|
})
|
|
5366
5837
|
);
|
|
5367
5838
|
return {
|
|
5368
|
-
supportingFiles:
|
|
5839
|
+
supportingFiles: inputs2.filter((file) => !entrySet.has(file)).sort(),
|
|
5369
5840
|
externalPackages: [...externalPackages].sort(),
|
|
5370
5841
|
externalImporters: Object.fromEntries(
|
|
5371
5842
|
[...externalImporters.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([dependency, importers]) => [dependency, [...importers].sort()])
|
|
@@ -5791,12 +6262,12 @@ async function runSyncCli(input) {
|
|
|
5791
6262
|
// src/pulse/playwright.ts
|
|
5792
6263
|
import { spawn as spawn3 } from "child_process";
|
|
5793
6264
|
import { createRequire as createRequire2 } from "module";
|
|
5794
|
-
import
|
|
6265
|
+
import path21 from "path";
|
|
5795
6266
|
|
|
5796
6267
|
// src/pulse/package-manager.ts
|
|
5797
6268
|
import { spawn as spawn2 } from "child_process";
|
|
5798
|
-
import { access as access2, readFile as
|
|
5799
|
-
import
|
|
6269
|
+
import { access as access2, readFile as readFile14 } from "fs/promises";
|
|
6270
|
+
import path20 from "path";
|
|
5800
6271
|
var LOCKFILES = [
|
|
5801
6272
|
{ packageManager: "pnpm", filenames: ["pnpm-lock.yaml"] },
|
|
5802
6273
|
{ packageManager: "yarn", filenames: ["yarn.lock"] },
|
|
@@ -5818,7 +6289,7 @@ async function fileExists2(filePath) {
|
|
|
5818
6289
|
async function declaredPackageManager(projectRoot) {
|
|
5819
6290
|
let contents;
|
|
5820
6291
|
try {
|
|
5821
|
-
contents = await
|
|
6292
|
+
contents = await readFile14(path20.join(projectRoot, "package.json"), "utf8");
|
|
5822
6293
|
} catch (error) {
|
|
5823
6294
|
if (error.code === "ENOENT") return null;
|
|
5824
6295
|
throw error;
|
|
@@ -5855,7 +6326,7 @@ async function declaredPackageManager(projectRoot) {
|
|
|
5855
6326
|
);
|
|
5856
6327
|
}
|
|
5857
6328
|
async function detectPackageManager(projectRoot) {
|
|
5858
|
-
const root =
|
|
6329
|
+
const root = path20.resolve(projectRoot);
|
|
5859
6330
|
const declared = await declaredPackageManager(root);
|
|
5860
6331
|
if (declared) return declared;
|
|
5861
6332
|
const matches = (await Promise.all(
|
|
@@ -5864,7 +6335,7 @@ async function detectPackageManager(projectRoot) {
|
|
|
5864
6335
|
filenames: (await Promise.all(
|
|
5865
6336
|
filenames2.map(async (filename) => ({
|
|
5866
6337
|
filename,
|
|
5867
|
-
exists: await fileExists2(
|
|
6338
|
+
exists: await fileExists2(path20.join(root, filename))
|
|
5868
6339
|
}))
|
|
5869
6340
|
)).filter((entry) => entry.exists).map((entry) => entry.filename)
|
|
5870
6341
|
}))
|
|
@@ -5925,12 +6396,12 @@ function isMissingBrowserDiagnostic(output) {
|
|
|
5925
6396
|
function resolvePlaywrightCli(projectRoot) {
|
|
5926
6397
|
try {
|
|
5927
6398
|
const requireFromProject = createRequire2(
|
|
5928
|
-
|
|
6399
|
+
path21.join(projectRoot, "package.json")
|
|
5929
6400
|
);
|
|
5930
6401
|
const packageJson = requireFromProject.resolve(
|
|
5931
6402
|
"@playwright/test/package.json"
|
|
5932
6403
|
);
|
|
5933
|
-
return
|
|
6404
|
+
return path21.join(path21.dirname(packageJson), "cli.js");
|
|
5934
6405
|
} catch {
|
|
5935
6406
|
throw new CliError(
|
|
5936
6407
|
"PLAYWRIGHT_NOT_INSTALLED",
|
|
@@ -5942,7 +6413,7 @@ function resolvePlaywrightCli(projectRoot) {
|
|
|
5942
6413
|
}
|
|
5943
6414
|
}
|
|
5944
6415
|
async function runProjectTests(input) {
|
|
5945
|
-
const projectRoot =
|
|
6416
|
+
const projectRoot = path21.resolve(input.projectRoot);
|
|
5946
6417
|
const config = await loadChecksConfig(projectRoot);
|
|
5947
6418
|
const report = await discoverProjectFiles(projectRoot, config);
|
|
5948
6419
|
if (!report.valid) {
|
|
@@ -5999,9 +6470,9 @@ async function runProjectTests(input) {
|
|
|
5999
6470
|
}
|
|
6000
6471
|
|
|
6001
6472
|
// src/pulse/validate.ts
|
|
6002
|
-
import
|
|
6473
|
+
import path22 from "path";
|
|
6003
6474
|
async function validateProject(projectRoot) {
|
|
6004
|
-
const root =
|
|
6475
|
+
const root = path22.resolve(projectRoot);
|
|
6005
6476
|
const config = await loadChecksConfig(root);
|
|
6006
6477
|
return discoverProjectFiles(root, config);
|
|
6007
6478
|
}
|
|
@@ -6034,7 +6505,7 @@ async function runPulseCommand(options) {
|
|
|
6034
6505
|
}
|
|
6035
6506
|
}
|
|
6036
6507
|
async function runInitCommand(options, args2) {
|
|
6037
|
-
const { values } =
|
|
6508
|
+
const { values } = parseArgs2({
|
|
6038
6509
|
args: args2,
|
|
6039
6510
|
options: {
|
|
6040
6511
|
"base-url": { type: "string" },
|
|
@@ -6063,7 +6534,7 @@ async function runInitCommand(options, args2) {
|
|
|
6063
6534
|
};
|
|
6064
6535
|
}
|
|
6065
6536
|
async function runValidateCommand(options, args2) {
|
|
6066
|
-
const { values } =
|
|
6537
|
+
const { values } = parseArgs2({
|
|
6067
6538
|
args: args2,
|
|
6068
6539
|
options: { json: { default: false, type: "boolean" } },
|
|
6069
6540
|
strict: true
|
|
@@ -6075,7 +6546,7 @@ async function runValidateCommand(options, args2) {
|
|
|
6075
6546
|
};
|
|
6076
6547
|
}
|
|
6077
6548
|
async function runSyncCommand(options, args2) {
|
|
6078
|
-
const { values } =
|
|
6549
|
+
const { values } = parseArgs2({
|
|
6079
6550
|
args: args2,
|
|
6080
6551
|
options: {
|
|
6081
6552
|
check: { default: false, type: "boolean" },
|
|
@@ -6108,7 +6579,7 @@ async function runTestCommand(options, args2) {
|
|
|
6108
6579
|
const separator = args2.indexOf("--");
|
|
6109
6580
|
const commandArgs = separator === -1 ? args2 : args2.slice(0, separator);
|
|
6110
6581
|
const passthrough = separator === -1 ? [] : args2.slice(separator + 1);
|
|
6111
|
-
const { values } =
|
|
6582
|
+
const { values } = parseArgs2({
|
|
6112
6583
|
args: commandArgs,
|
|
6113
6584
|
options: {
|
|
6114
6585
|
"base-url": { type: "string" },
|
|
@@ -6142,7 +6613,7 @@ async function optionalProjectContext(options) {
|
|
|
6142
6613
|
return context;
|
|
6143
6614
|
}
|
|
6144
6615
|
async function runDeployCommand(options, args2) {
|
|
6145
|
-
const { values } =
|
|
6616
|
+
const { values } = parseArgs2({
|
|
6146
6617
|
args: args2,
|
|
6147
6618
|
options: {
|
|
6148
6619
|
"dry-run": { default: false, type: "boolean" },
|
|
@@ -6151,7 +6622,7 @@ async function runDeployCommand(options, args2) {
|
|
|
6151
6622
|
},
|
|
6152
6623
|
strict: true
|
|
6153
6624
|
});
|
|
6154
|
-
const projectRoot =
|
|
6625
|
+
const projectRoot = path23.resolve(options.cwd ?? process.cwd());
|
|
6155
6626
|
const repositoryRoot = await findRepositoryRoot(projectRoot);
|
|
6156
6627
|
const common = await commonServiceContext(options, "pulse");
|
|
6157
6628
|
if (common && (!common.environmentBinding?.slug || !common.environment.url))
|
|
@@ -6175,7 +6646,7 @@ async function runDeployCommand(options, args2) {
|
|
|
6175
6646
|
values.json,
|
|
6176
6647
|
{ dryRun: true, manifest },
|
|
6177
6648
|
[
|
|
6178
|
-
`Bundle: ${
|
|
6649
|
+
`Bundle: ${path23.relative(projectRoot, manifest.archivePath)}`,
|
|
6179
6650
|
`Checksum: ${manifest.checksum}`,
|
|
6180
6651
|
`Files: ${manifest.fileCount}`,
|
|
6181
6652
|
"Dry run complete; nothing was uploaded."
|
|
@@ -6217,7 +6688,7 @@ async function runDeployCommand(options, args2) {
|
|
|
6217
6688
|
values.json,
|
|
6218
6689
|
{ deployment, dryRun: false, manifest },
|
|
6219
6690
|
[
|
|
6220
|
-
`Bundle: ${
|
|
6691
|
+
`Bundle: ${path23.relative(projectRoot, manifest.archivePath)}`,
|
|
6221
6692
|
`Checksum: ${manifest.checksum}`,
|
|
6222
6693
|
`Files: ${manifest.fileCount}`,
|
|
6223
6694
|
`Project ${deployment.projectId} deployed.`,
|
|
@@ -6357,63 +6828,63 @@ function exitCodeForPulseError(error) {
|
|
|
6357
6828
|
}
|
|
6358
6829
|
|
|
6359
6830
|
// src/siteos-search-api.ts
|
|
6360
|
-
import { z as
|
|
6361
|
-
var permissiveObjectSchema =
|
|
6362
|
-
var environmentSlugSchema =
|
|
6363
|
-
var searchCredentialMetadataSchema =
|
|
6364
|
-
createdAt:
|
|
6365
|
-
id:
|
|
6366
|
-
lastUsedAt:
|
|
6367
|
-
name:
|
|
6368
|
-
projectEnvironmentId:
|
|
6369
|
-
revokedAt:
|
|
6370
|
-
status:
|
|
6831
|
+
import { z as z11 } from "zod";
|
|
6832
|
+
var permissiveObjectSchema = z11.record(z11.unknown());
|
|
6833
|
+
var environmentSlugSchema = z11.string().min(2).max(30).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/);
|
|
6834
|
+
var searchCredentialMetadataSchema = z11.object({
|
|
6835
|
+
createdAt: z11.string().datetime({ offset: true }),
|
|
6836
|
+
id: z11.string().trim().min(1).max(255),
|
|
6837
|
+
lastUsedAt: z11.string().datetime({ offset: true }).nullable(),
|
|
6838
|
+
name: z11.string().trim().min(1).max(255),
|
|
6839
|
+
projectEnvironmentId: z11.string().trim().min(1).max(255),
|
|
6840
|
+
revokedAt: z11.string().datetime({ offset: true }).nullable(),
|
|
6841
|
+
status: z11.enum(["active", "revoked"])
|
|
6371
6842
|
}).strict();
|
|
6372
|
-
var searchCredentialListResponseSchema =
|
|
6843
|
+
var searchCredentialListResponseSchema = z11.object({
|
|
6373
6844
|
environmentSlug: environmentSlugSchema,
|
|
6374
|
-
success:
|
|
6375
|
-
tokens:
|
|
6845
|
+
success: z11.literal(true),
|
|
6846
|
+
tokens: z11.array(searchCredentialMetadataSchema)
|
|
6376
6847
|
}).strict();
|
|
6377
|
-
var searchCredentialExchangeResponseSchema =
|
|
6848
|
+
var searchCredentialExchangeResponseSchema = z11.object({
|
|
6378
6849
|
environmentSlug: environmentSlugSchema,
|
|
6379
|
-
success:
|
|
6850
|
+
success: z11.literal(true),
|
|
6380
6851
|
token: searchCredentialMetadataSchema.extend({
|
|
6381
|
-
token:
|
|
6852
|
+
token: z11.string().regex(/^psq_[A-Za-z0-9_-]{22}$/)
|
|
6382
6853
|
})
|
|
6383
6854
|
}).strict();
|
|
6384
|
-
var searchCredentialRevokeResponseSchema =
|
|
6855
|
+
var searchCredentialRevokeResponseSchema = z11.object({
|
|
6385
6856
|
environmentSlug: environmentSlugSchema,
|
|
6386
|
-
success:
|
|
6857
|
+
success: z11.literal(true),
|
|
6387
6858
|
token: searchCredentialMetadataSchema
|
|
6388
6859
|
}).strict();
|
|
6389
|
-
var searchIndexingCredentialListResponseSchema =
|
|
6390
|
-
credentials:
|
|
6860
|
+
var searchIndexingCredentialListResponseSchema = z11.object({
|
|
6861
|
+
credentials: z11.array(searchCredentialMetadataSchema),
|
|
6391
6862
|
environmentSlug: environmentSlugSchema,
|
|
6392
|
-
success:
|
|
6863
|
+
success: z11.literal(true)
|
|
6393
6864
|
}).strict();
|
|
6394
|
-
var searchIndexingCredentialExchangeResponseSchema =
|
|
6865
|
+
var searchIndexingCredentialExchangeResponseSchema = z11.object({
|
|
6395
6866
|
credential: searchCredentialMetadataSchema.extend({
|
|
6396
|
-
credential:
|
|
6867
|
+
credential: z11.string().regex(/^psi_[A-Za-z0-9_-]{22}$/)
|
|
6397
6868
|
}).strict(),
|
|
6398
6869
|
environmentSlug: environmentSlugSchema,
|
|
6399
|
-
success:
|
|
6870
|
+
success: z11.literal(true)
|
|
6400
6871
|
}).strict();
|
|
6401
|
-
var searchIndexingCredentialRevokeResponseSchema =
|
|
6872
|
+
var searchIndexingCredentialRevokeResponseSchema = z11.object({
|
|
6402
6873
|
credential: searchCredentialMetadataSchema,
|
|
6403
6874
|
environmentSlug: environmentSlugSchema,
|
|
6404
|
-
success:
|
|
6875
|
+
success: z11.literal(true)
|
|
6405
6876
|
}).strict();
|
|
6406
|
-
var nestedApiErrorResponseSchema =
|
|
6407
|
-
error:
|
|
6408
|
-
code:
|
|
6409
|
-
message:
|
|
6410
|
-
nextActions:
|
|
6877
|
+
var nestedApiErrorResponseSchema = z11.object({
|
|
6878
|
+
error: z11.object({
|
|
6879
|
+
code: z11.string().trim().min(1).optional(),
|
|
6880
|
+
message: z11.string().trim().min(1),
|
|
6881
|
+
nextActions: z11.array(z11.string()).optional()
|
|
6411
6882
|
})
|
|
6412
6883
|
}).passthrough();
|
|
6413
|
-
var directApiErrorResponseSchema =
|
|
6414
|
-
code:
|
|
6415
|
-
message:
|
|
6416
|
-
status:
|
|
6884
|
+
var directApiErrorResponseSchema = z11.object({
|
|
6885
|
+
code: z11.string().trim().min(1).optional(),
|
|
6886
|
+
message: z11.string().trim().min(1).optional(),
|
|
6887
|
+
status: z11.number().int().optional()
|
|
6417
6888
|
}).passthrough();
|
|
6418
6889
|
var SiteOSSearchApiError = class extends Error {
|
|
6419
6890
|
code;
|
|
@@ -6683,12 +7154,12 @@ import { randomUUID as randomUUID5 } from "crypto";
|
|
|
6683
7154
|
import {
|
|
6684
7155
|
chmod as chmod4,
|
|
6685
7156
|
lstat as lstat6,
|
|
6686
|
-
readFile as
|
|
7157
|
+
readFile as readFile15,
|
|
6687
7158
|
rename as rename6,
|
|
6688
7159
|
rm as rm5,
|
|
6689
|
-
writeFile as
|
|
7160
|
+
writeFile as writeFile8
|
|
6690
7161
|
} from "fs/promises";
|
|
6691
|
-
import
|
|
7162
|
+
import path24 from "path";
|
|
6692
7163
|
var SITEOS_SEARCH_QUERY_CREDENTIAL_ENV = "SITEOS_SEARCH_TOKEN";
|
|
6693
7164
|
var SITEOS_SEARCH_ENVIRONMENT_ENV = "SITEOS_SEARCH_ENV";
|
|
6694
7165
|
var SITEOS_SEARCH_INDEXING_CREDENTIAL_ENV = "SITEOS_SEARCH_INDEXING_CREDENTIAL";
|
|
@@ -6706,8 +7177,8 @@ var SiteOSSearchCredentialStorageError = class extends Error {
|
|
|
6706
7177
|
}
|
|
6707
7178
|
};
|
|
6708
7179
|
async function prepareSiteOSSearchCredentialInstallation(options) {
|
|
6709
|
-
const projectRoot =
|
|
6710
|
-
const dotenvPath =
|
|
7180
|
+
const projectRoot = path24.resolve(options.projectRoot);
|
|
7181
|
+
const dotenvPath = path24.join(projectRoot, ".env");
|
|
6711
7182
|
try {
|
|
6712
7183
|
await assertSafeProjectRoot2(projectRoot);
|
|
6713
7184
|
if (!await isDotenvIgnored2(projectRoot)) {
|
|
@@ -6765,8 +7236,8 @@ async function prepareSiteOSSearchCredentialInstallation(options) {
|
|
|
6765
7236
|
}
|
|
6766
7237
|
}
|
|
6767
7238
|
async function prepareSiteOSSearchIndexingCredentialInstallation(options) {
|
|
6768
|
-
const projectRoot =
|
|
6769
|
-
const dotenvPath =
|
|
7239
|
+
const projectRoot = path24.resolve(options.projectRoot);
|
|
7240
|
+
const dotenvPath = path24.join(projectRoot, ".env");
|
|
6770
7241
|
try {
|
|
6771
7242
|
await assertSafeProjectRoot2(projectRoot);
|
|
6772
7243
|
if (!await isDotenvIgnored2(projectRoot)) {
|
|
@@ -6842,7 +7313,7 @@ async function readSafeDotenv2(dotenvPath, managedEnvironmentNames) {
|
|
|
6842
7313
|
"SEARCH_CREDENTIAL_STORAGE_INVALID"
|
|
6843
7314
|
);
|
|
6844
7315
|
}
|
|
6845
|
-
const content = dotenvStats ? await
|
|
7316
|
+
const content = dotenvStats ? await readFile15(dotenvPath, "utf8") : "";
|
|
6846
7317
|
if (managedEnvironmentNames.some(
|
|
6847
7318
|
(name) => countManagedEntries(content, name) > 1
|
|
6848
7319
|
)) {
|
|
@@ -6853,12 +7324,12 @@ async function readSafeDotenv2(dotenvPath, managedEnvironmentNames) {
|
|
|
6853
7324
|
return content;
|
|
6854
7325
|
}
|
|
6855
7326
|
async function writeCredentialAtomically2(options) {
|
|
6856
|
-
const temporaryPath =
|
|
7327
|
+
const temporaryPath = path24.join(
|
|
6857
7328
|
options.projectRoot,
|
|
6858
7329
|
`.siteos-search-credential.${process.pid}.${randomUUID5()}.tmp`
|
|
6859
7330
|
);
|
|
6860
7331
|
try {
|
|
6861
|
-
await
|
|
7332
|
+
await writeFile8(temporaryPath, options.content, {
|
|
6862
7333
|
encoding: "utf8",
|
|
6863
7334
|
flag: "wx",
|
|
6864
7335
|
mode: 384
|
|
@@ -7562,7 +8033,7 @@ ${SEARCH_HELP}` };
|
|
|
7562
8033
|
}
|
|
7563
8034
|
|
|
7564
8035
|
// src/projects/project-command.ts
|
|
7565
|
-
import { parseArgs as
|
|
8036
|
+
import { parseArgs as parseArgs3 } from "util";
|
|
7566
8037
|
var PROJECT_HELP = `One Project for your website and all its services.
|
|
7567
8038
|
|
|
7568
8039
|
Usage:
|
|
@@ -7593,7 +8064,7 @@ async function runProjectCommand(options) {
|
|
|
7593
8064
|
action
|
|
7594
8065
|
))
|
|
7595
8066
|
throw new Error("Unknown Project command.");
|
|
7596
|
-
const parsed =
|
|
8067
|
+
const parsed = parseArgs3({
|
|
7597
8068
|
args: args2,
|
|
7598
8069
|
allowPositionals: true,
|
|
7599
8070
|
strict: true,
|
|
@@ -7717,7 +8188,7 @@ async function runEnvironmentCommand(options) {
|
|
|
7717
8188
|
action
|
|
7718
8189
|
))
|
|
7719
8190
|
throw new Error("Unknown Project environment command.");
|
|
7720
|
-
const { positionals, values } =
|
|
8191
|
+
const { positionals, values } = parseArgs3({
|
|
7721
8192
|
args: args2,
|
|
7722
8193
|
allowPositionals: true,
|
|
7723
8194
|
strict: true,
|
|
@@ -7824,14 +8295,14 @@ async function runEnvironmentCommand(options) {
|
|
|
7824
8295
|
}
|
|
7825
8296
|
|
|
7826
8297
|
// src/services/service-command.ts
|
|
7827
|
-
import { readFile as
|
|
7828
|
-
import
|
|
7829
|
-
import { parseArgs as
|
|
7830
|
-
import { z as
|
|
8298
|
+
import { readFile as readFile16, stat } from "fs/promises";
|
|
8299
|
+
import path25 from "path";
|
|
8300
|
+
import { parseArgs as parseArgs4 } from "util";
|
|
8301
|
+
import { z as z14 } from "zod";
|
|
7831
8302
|
|
|
7832
8303
|
// src/services/trace-explorer-command.ts
|
|
7833
|
-
import { z as
|
|
7834
|
-
var
|
|
8304
|
+
import { z as z12 } from "zod";
|
|
8305
|
+
var filters2 = [
|
|
7835
8306
|
"environment",
|
|
7836
8307
|
"provider",
|
|
7837
8308
|
"event",
|
|
@@ -7850,16 +8321,16 @@ var TRACE_EXPLORER_FLAGS = {
|
|
|
7850
8321
|
"ga4 show": ["environment"],
|
|
7851
8322
|
"notifications show": ["environment"],
|
|
7852
8323
|
"notifications destinations": ["environment", "cursor"],
|
|
7853
|
-
destinations:
|
|
7854
|
-
events:
|
|
7855
|
-
properties:
|
|
7856
|
-
issues: [...
|
|
7857
|
-
observations: [...
|
|
8324
|
+
destinations: filters2,
|
|
8325
|
+
events: filters2,
|
|
8326
|
+
properties: filters2,
|
|
8327
|
+
issues: [...filters2, "container-id", "tag-id"],
|
|
8328
|
+
observations: [...filters2, "container-id", "tag-id", "result"],
|
|
7858
8329
|
"issue show": ["environment", "id", "cursor"],
|
|
7859
8330
|
"observation show": ["environment", "batch-id", "event-id"]
|
|
7860
8331
|
};
|
|
7861
|
-
var FilterValues =
|
|
7862
|
-
provider:
|
|
8332
|
+
var FilterValues = z12.object({
|
|
8333
|
+
provider: z12.enum([
|
|
7863
8334
|
"siteos_analytics",
|
|
7864
8335
|
"ga4",
|
|
7865
8336
|
"google_ads",
|
|
@@ -7867,12 +8338,12 @@ var FilterValues = z11.object({
|
|
|
7867
8338
|
"meta",
|
|
7868
8339
|
"custom"
|
|
7869
8340
|
]).optional(),
|
|
7870
|
-
event:
|
|
7871
|
-
property:
|
|
7872
|
-
"page-view":
|
|
7873
|
-
"container-id":
|
|
7874
|
-
"tag-id":
|
|
7875
|
-
result:
|
|
8341
|
+
event: z12.string().min(1).max(160).optional(),
|
|
8342
|
+
property: z12.string().min(1).max(160).optional(),
|
|
8343
|
+
"page-view": z12.string().uuid().optional(),
|
|
8344
|
+
"container-id": z12.string().regex(/^GTM-[A-Z0-9]{4,20}$/u).optional(),
|
|
8345
|
+
"tag-id": z12.string().regex(/^[0-9]{1,20}$/u).optional(),
|
|
8346
|
+
result: z12.enum([
|
|
7876
8347
|
"success",
|
|
7877
8348
|
"failure",
|
|
7878
8349
|
"exception",
|
|
@@ -7885,8 +8356,8 @@ var FilterValues = z11.object({
|
|
|
7885
8356
|
"beacon_accepted",
|
|
7886
8357
|
"result_unknown"
|
|
7887
8358
|
]).optional(),
|
|
7888
|
-
search:
|
|
7889
|
-
state:
|
|
8359
|
+
search: z12.string().max(160).optional(),
|
|
8360
|
+
state: z12.enum([
|
|
7890
8361
|
"all",
|
|
7891
8362
|
"active",
|
|
7892
8363
|
"snoozed",
|
|
@@ -7896,8 +8367,8 @@ var FilterValues = z11.object({
|
|
|
7896
8367
|
"tracked",
|
|
7897
8368
|
"ignored"
|
|
7898
8369
|
]).optional(),
|
|
7899
|
-
rule:
|
|
7900
|
-
kind:
|
|
8370
|
+
rule: z12.string().min(1).max(80).optional(),
|
|
8371
|
+
kind: z12.enum([
|
|
7901
8372
|
"tag_execution",
|
|
7902
8373
|
"data_layer",
|
|
7903
8374
|
"fetch",
|
|
@@ -7908,8 +8379,8 @@ var FilterValues = z11.object({
|
|
|
7908
8379
|
"heartbeat",
|
|
7909
8380
|
"runtime_diagnostic"
|
|
7910
8381
|
]).optional(),
|
|
7911
|
-
period:
|
|
7912
|
-
cursor:
|
|
8382
|
+
period: z12.enum(["24h", "7d"]).optional(),
|
|
8383
|
+
cursor: z12.string().min(1).max(4096).optional()
|
|
7913
8384
|
});
|
|
7914
8385
|
function traceExplorerSuffix(route, values) {
|
|
7915
8386
|
if (!TRACE_EXPLORER_FLAGS[route]) return null;
|
|
@@ -7919,11 +8390,11 @@ function traceExplorerSuffix(route, values) {
|
|
|
7919
8390
|
for (const key of keys) {
|
|
7920
8391
|
const value = values[key];
|
|
7921
8392
|
const plain = key === "id" && typeof value === "string" ? value.replace(/^trc_incident_/u, "") : value;
|
|
7922
|
-
if (!
|
|
8393
|
+
if (!z12.string().uuid().safeParse(plain).success)
|
|
7923
8394
|
throw new Error(`Use --${key} with the UUID returned by Trace.`);
|
|
7924
8395
|
}
|
|
7925
8396
|
if (values.cursor !== void 0) {
|
|
7926
|
-
if (!
|
|
8397
|
+
if (!z12.string().uuid().safeParse(
|
|
7927
8398
|
typeof values.cursor === "string" ? values.cursor.replace(/^trc_finding_/u, "") : values.cursor
|
|
7928
8399
|
).success)
|
|
7929
8400
|
throw new Error(
|
|
@@ -8047,7 +8518,7 @@ async function runServiceCommand(service, options) {
|
|
|
8047
8518
|
if (!options.args.length || options.args.includes("--help") || options.args.includes("-h"))
|
|
8048
8519
|
return { exitCode: 0, stdout: SERVICE_HELP[service] };
|
|
8049
8520
|
try {
|
|
8050
|
-
const { positionals, values } =
|
|
8521
|
+
const { positionals, values } = parseArgs4({
|
|
8051
8522
|
args: options.args,
|
|
8052
8523
|
allowPositionals: true,
|
|
8053
8524
|
strict: true,
|
|
@@ -8195,8 +8666,8 @@ async function runServiceCommand(service, options) {
|
|
|
8195
8666
|
);
|
|
8196
8667
|
const result2 = await response.json();
|
|
8197
8668
|
if (!response.ok) {
|
|
8198
|
-
const error =
|
|
8199
|
-
error:
|
|
8669
|
+
const error = z14.object({
|
|
8670
|
+
error: z14.object({ code: z14.string(), message: z14.string().max(500) })
|
|
8200
8671
|
}).safeParse(result2);
|
|
8201
8672
|
throw new SiteOSAuthApiError({
|
|
8202
8673
|
code: error.success ? error.data.error.code : "SERVICE_REQUEST_FAILED",
|
|
@@ -8209,11 +8680,11 @@ async function runServiceCommand(service, options) {
|
|
|
8209
8680
|
async function inputFile() {
|
|
8210
8681
|
if (!values.input)
|
|
8211
8682
|
throw new Error("This operation requires --input <json-file>.");
|
|
8212
|
-
const filename =
|
|
8683
|
+
const filename = path25.resolve(options.cwd ?? process.cwd(), values.input);
|
|
8213
8684
|
const info = await stat(filename);
|
|
8214
8685
|
if (!info.isFile() || info.size > 512 * 1024)
|
|
8215
8686
|
throw new Error("Use a JSON file smaller than 512 KB.");
|
|
8216
|
-
return JSON.parse(await
|
|
8687
|
+
return JSON.parse(await readFile16(filename, "utf8"));
|
|
8217
8688
|
}
|
|
8218
8689
|
const site = common ? `/sites/${encodeURIComponent(common.resourceId)}` : "";
|
|
8219
8690
|
let result;
|
|
@@ -8233,9 +8704,9 @@ async function runServiceCommand(service, options) {
|
|
|
8233
8704
|
await inputFile(),
|
|
8234
8705
|
"cookie:workspace:read"
|
|
8235
8706
|
);
|
|
8236
|
-
const validation =
|
|
8237
|
-
valid:
|
|
8238
|
-
draftVersionMatches:
|
|
8707
|
+
const validation = z14.object({
|
|
8708
|
+
valid: z14.boolean(),
|
|
8709
|
+
draftVersionMatches: z14.boolean().optional()
|
|
8239
8710
|
}).parse(result);
|
|
8240
8711
|
if (!validation.valid || validation.draftVersionMatches === false)
|
|
8241
8712
|
return { exitCode: 2, stdout: JSON.stringify(result, null, 2) };
|
|
@@ -8274,11 +8745,11 @@ async function runServiceCommand(service, options) {
|
|
|
8274
8745
|
} else if (action === "installation")
|
|
8275
8746
|
result = await request(`${site}/installation`);
|
|
8276
8747
|
else if (action === "draft" && subaction === "get" && positionals.length === 2) {
|
|
8277
|
-
const response =
|
|
8278
|
-
name:
|
|
8279
|
-
hostname:
|
|
8280
|
-
draftVersion:
|
|
8281
|
-
draft:
|
|
8748
|
+
const response = z14.object({
|
|
8749
|
+
name: z14.string(),
|
|
8750
|
+
hostname: z14.string(),
|
|
8751
|
+
draftVersion: z14.number(),
|
|
8752
|
+
draft: z14.unknown()
|
|
8282
8753
|
}).parse(await request(site));
|
|
8283
8754
|
result = {
|
|
8284
8755
|
name: response.name,
|
|
@@ -8310,9 +8781,9 @@ async function runServiceCommand(service, options) {
|
|
|
8310
8781
|
else if (action === "environments" && positionals.length === 1)
|
|
8311
8782
|
result = await request(`${site}/environments`);
|
|
8312
8783
|
else if (action === "ga4" || action === "notifications" || action === "schema" || action === "report" || action === "installation" || action === "tracking-plan" || explorerSuffix !== null) {
|
|
8313
|
-
const response =
|
|
8314
|
-
environments:
|
|
8315
|
-
|
|
8784
|
+
const response = z14.object({
|
|
8785
|
+
environments: z14.array(
|
|
8786
|
+
z14.object({ id: z14.string(), slug: z14.string() })
|
|
8316
8787
|
)
|
|
8317
8788
|
}).parse(await request(`${site}/environments`));
|
|
8318
8789
|
const environment = common.overview.project.environments.find(
|
|
@@ -8351,11 +8822,11 @@ async function runServiceCommand(service, options) {
|
|
|
8351
8822
|
await inputFile()
|
|
8352
8823
|
);
|
|
8353
8824
|
} else if (action === "report" && positionals.length === 1) {
|
|
8354
|
-
const detail =
|
|
8355
|
-
site:
|
|
8356
|
-
id:
|
|
8357
|
-
environments:
|
|
8358
|
-
|
|
8825
|
+
const detail = z14.object({
|
|
8826
|
+
site: z14.object({
|
|
8827
|
+
id: z14.literal(common.resourceId),
|
|
8828
|
+
environments: z14.array(
|
|
8829
|
+
z14.object({ id: z14.string(), evidence: z14.unknown() })
|
|
8359
8830
|
)
|
|
8360
8831
|
})
|
|
8361
8832
|
}).parse(await request(site));
|
|
@@ -8413,35 +8884,35 @@ async function runServiceCommand(service, options) {
|
|
|
8413
8884
|
} catch (cause) {
|
|
8414
8885
|
return {
|
|
8415
8886
|
exitCode: cause instanceof SiteOSAuthApiError ? 1 : 2,
|
|
8416
|
-
stderr: cause instanceof SiteOSAuthApiError || !(cause instanceof
|
|
8887
|
+
stderr: cause instanceof SiteOSAuthApiError || !(cause instanceof z14.ZodError) && cause instanceof Error ? cause.message : "The service returned an invalid response."
|
|
8417
8888
|
};
|
|
8418
8889
|
}
|
|
8419
8890
|
}
|
|
8420
8891
|
|
|
8421
8892
|
// src/services/analytics-google-csv.ts
|
|
8422
|
-
import { z as
|
|
8423
|
-
var table =
|
|
8424
|
-
dimensions:
|
|
8425
|
-
metrics:
|
|
8426
|
-
rows:
|
|
8427
|
-
|
|
8893
|
+
import { z as z15 } from "zod";
|
|
8894
|
+
var table = z15.object({
|
|
8895
|
+
dimensions: z15.array(z15.string()),
|
|
8896
|
+
metrics: z15.array(z15.object({ name: z15.string() })),
|
|
8897
|
+
rows: z15.array(
|
|
8898
|
+
z15.object({ dimensions: z15.array(z15.string()), metrics: z15.array(z15.string()) })
|
|
8428
8899
|
)
|
|
8429
8900
|
});
|
|
8430
8901
|
function googleReportCsv(value, fetchedAt) {
|
|
8431
|
-
const report =
|
|
8432
|
-
request:
|
|
8433
|
-
kind:
|
|
8434
|
-
propertyId:
|
|
8435
|
-
streamId:
|
|
8436
|
-
startDate:
|
|
8437
|
-
endDate:
|
|
8438
|
-
filters:
|
|
8439
|
-
minuteRange:
|
|
8440
|
-
comparison:
|
|
8902
|
+
const report = z15.object({
|
|
8903
|
+
request: z15.object({
|
|
8904
|
+
kind: z15.string(),
|
|
8905
|
+
propertyId: z15.string(),
|
|
8906
|
+
streamId: z15.string(),
|
|
8907
|
+
startDate: z15.string(),
|
|
8908
|
+
endDate: z15.string(),
|
|
8909
|
+
filters: z15.array(z15.unknown()),
|
|
8910
|
+
minuteRange: z15.unknown().optional(),
|
|
8911
|
+
comparison: z15.object({ startDate: z15.string(), endDate: z15.string() }).optional()
|
|
8441
8912
|
}).passthrough(),
|
|
8442
|
-
timeZone:
|
|
8443
|
-
limitations:
|
|
8444
|
-
quality:
|
|
8913
|
+
timeZone: z15.string(),
|
|
8914
|
+
limitations: z15.array(z15.string()),
|
|
8915
|
+
quality: z15.unknown(),
|
|
8445
8916
|
table,
|
|
8446
8917
|
previous: table.optional()
|
|
8447
8918
|
}).parse(value);
|
|
@@ -8480,10 +8951,10 @@ function googleReportCsv(value, fetchedAt) {
|
|
|
8480
8951
|
}
|
|
8481
8952
|
|
|
8482
8953
|
// src/services/analytics-google-command.ts
|
|
8483
|
-
import { readFile as
|
|
8484
|
-
import
|
|
8485
|
-
import { parseArgs as
|
|
8486
|
-
import { z as
|
|
8954
|
+
import { readFile as readFile17, stat as stat2 } from "fs/promises";
|
|
8955
|
+
import path26 from "path";
|
|
8956
|
+
import { parseArgs as parseArgs5 } from "util";
|
|
8957
|
+
import { z as z16 } from "zod";
|
|
8487
8958
|
var GOOGLE_ANALYTICS_HELP = `Read GA4 reports in the selected Project environment.
|
|
8488
8959
|
|
|
8489
8960
|
Usage:
|
|
@@ -8505,7 +8976,7 @@ async function runAnalyticsGoogleCommand(options) {
|
|
|
8505
8976
|
if (options.args.length <= 1 || options.args.includes("--help"))
|
|
8506
8977
|
return { exitCode: 0, stdout: GOOGLE_ANALYTICS_HELP };
|
|
8507
8978
|
try {
|
|
8508
|
-
const { values, positionals } =
|
|
8979
|
+
const { values, positionals } = parseArgs5({
|
|
8509
8980
|
args: options.args.slice(1),
|
|
8510
8981
|
allowPositionals: true,
|
|
8511
8982
|
strict: true,
|
|
@@ -8590,8 +9061,8 @@ async function runAnalyticsGoogleCommand(options) {
|
|
|
8590
9061
|
);
|
|
8591
9062
|
const value = await response.json();
|
|
8592
9063
|
if (!response.ok) {
|
|
8593
|
-
const error =
|
|
8594
|
-
error:
|
|
9064
|
+
const error = z16.object({
|
|
9065
|
+
error: z16.object({ code: z16.string(), message: z16.string().max(500) })
|
|
8595
9066
|
}).safeParse(value);
|
|
8596
9067
|
throw new SiteOSAuthApiError({
|
|
8597
9068
|
code: error.success ? error.data.error.code : "ANALYTICS_REQUEST_FAILED",
|
|
@@ -8599,31 +9070,31 @@ async function runAnalyticsGoogleCommand(options) {
|
|
|
8599
9070
|
status: response.status
|
|
8600
9071
|
});
|
|
8601
9072
|
}
|
|
8602
|
-
return
|
|
8603
|
-
contractVersion:
|
|
8604
|
-
resourceId:
|
|
9073
|
+
return z16.object({
|
|
9074
|
+
contractVersion: z16.literal(1),
|
|
9075
|
+
resourceId: z16.literal(context.resourceId)
|
|
8605
9076
|
}).passthrough().parse(value);
|
|
8606
9077
|
};
|
|
8607
9078
|
const file = async () => {
|
|
8608
9079
|
if (!values.file)
|
|
8609
9080
|
throw new Error("Provide --file with a report definition.");
|
|
8610
|
-
const name =
|
|
9081
|
+
const name = path26.resolve(options.cwd ?? process.cwd(), values.file);
|
|
8611
9082
|
if ((await stat2(name)).size > 16384)
|
|
8612
9083
|
throw new Error("Definition exceeds 16 KiB.");
|
|
8613
|
-
const text = await
|
|
9084
|
+
const text = await readFile17(name, "utf8");
|
|
8614
9085
|
if (Buffer.byteLength(text) > 16384)
|
|
8615
9086
|
throw new Error("Definition exceeds 16 KiB.");
|
|
8616
|
-
return
|
|
9087
|
+
return z16.record(z16.unknown()).parse(JSON.parse(text));
|
|
8617
9088
|
};
|
|
8618
9089
|
let result;
|
|
8619
9090
|
if (action === "saved") {
|
|
8620
9091
|
result = sub === "list" ? await send("/saved") : sub === "save" ? await send("/saved", "POST", await file()) : await send("/saved", "DELETE", {
|
|
8621
|
-
id:
|
|
8622
|
-
revision:
|
|
9092
|
+
id: z16.string().uuid().parse(positionals[2]),
|
|
9093
|
+
revision: z16.number().int().positive().parse(Number(values.revision))
|
|
8623
9094
|
});
|
|
8624
9095
|
} else {
|
|
8625
9096
|
const base = await send("");
|
|
8626
|
-
const binding =
|
|
9097
|
+
const binding = z16.object({ revision: z16.number().int().positive(), state: z16.string() }).parse(base.binding);
|
|
8627
9098
|
if (binding.state === "disconnected")
|
|
8628
9099
|
throw new Error("Connect GA4 in Analytics Setup first.");
|
|
8629
9100
|
if (action === "report") {
|
|
@@ -8656,16 +9127,16 @@ async function runAnalyticsGoogleCommand(options) {
|
|
|
8656
9127
|
result = await send(`?${params}`);
|
|
8657
9128
|
}
|
|
8658
9129
|
} else if (action === "realtime") {
|
|
8659
|
-
const
|
|
9130
|
+
const filters3 = values.filters ? JSON.parse(values.filters) : [];
|
|
8660
9131
|
const params = new URLSearchParams({
|
|
8661
|
-
filters: JSON.stringify(
|
|
9132
|
+
filters: JSON.stringify(filters3)
|
|
8662
9133
|
});
|
|
8663
9134
|
result = await send(`/realtime?${params}`);
|
|
8664
9135
|
const live = result.realtime;
|
|
8665
9136
|
if (!live?.report || live.stale) {
|
|
8666
9137
|
await send("/realtime", "POST", {
|
|
8667
9138
|
revision: binding.revision,
|
|
8668
|
-
filters:
|
|
9139
|
+
filters: filters3
|
|
8669
9140
|
});
|
|
8670
9141
|
result = await send(`/realtime?${params}`);
|
|
8671
9142
|
}
|
|
@@ -8699,7 +9170,7 @@ async function runAnalyticsGoogleCommand(options) {
|
|
|
8699
9170
|
} catch (cause) {
|
|
8700
9171
|
const error = {
|
|
8701
9172
|
code: cause instanceof SiteOSAuthApiError ? cause.code : "ANALYTICS_COMMAND_FAILED",
|
|
8702
|
-
message: cause instanceof
|
|
9173
|
+
message: cause instanceof z16.ZodError ? "The input or server response does not match the GA4 contract." : cause instanceof Error ? cause.message : "GA4 request failed."
|
|
8703
9174
|
};
|
|
8704
9175
|
return {
|
|
8705
9176
|
exitCode: cause instanceof SiteOSAuthApiError ? 1 : 2,
|
|
@@ -8709,10 +9180,10 @@ async function runAnalyticsGoogleCommand(options) {
|
|
|
8709
9180
|
}
|
|
8710
9181
|
|
|
8711
9182
|
// src/services/analytics-command.ts
|
|
8712
|
-
import { readFile as
|
|
8713
|
-
import
|
|
8714
|
-
import { parseArgs as
|
|
8715
|
-
import { z as
|
|
9183
|
+
import { readFile as readFile18, stat as stat3 } from "fs/promises";
|
|
9184
|
+
import path27 from "path";
|
|
9185
|
+
import { parseArgs as parseArgs6 } from "util";
|
|
9186
|
+
import { z as z17 } from "zod";
|
|
8716
9187
|
var ANALYTICS_HELP = `Set up website Analytics in the selected Project environment.
|
|
8717
9188
|
|
|
8718
9189
|
Usage:
|
|
@@ -8744,27 +9215,27 @@ Settings require an owner/admin, the current revision and at least one change.
|
|
|
8744
9215
|
Catalog files are bounded JSON using the documented event/campaign/goal/funnel contracts.
|
|
8745
9216
|
Monitoring preparation creates a Trace draft only; publication remains a separate operation.
|
|
8746
9217
|
Analytics is Unlimited during early access. No synthetic events are sent by these commands.`;
|
|
8747
|
-
var EventInput =
|
|
8748
|
-
name:
|
|
8749
|
-
label:
|
|
8750
|
-
properties:
|
|
8751
|
-
|
|
8752
|
-
|
|
8753
|
-
|
|
9218
|
+
var EventInput = z17.object({
|
|
9219
|
+
name: z17.string().regex(/^[a-z][a-z0-9_]{0,63}$/u).refine((v) => v !== "pageview" && !v.startsWith("cookie_")),
|
|
9220
|
+
label: z17.string().trim().min(1).max(80),
|
|
9221
|
+
properties: z17.record(
|
|
9222
|
+
z17.string().regex(/^[a-z][a-z0-9_]{0,39}$/u),
|
|
9223
|
+
z17.array(
|
|
9224
|
+
z17.string().min(1).max(80).regex(/^[a-zA-Z0-9 _./:-]+$/u)
|
|
8754
9225
|
).min(1).max(20)
|
|
8755
9226
|
).refine((v) => Object.keys(v).length <= 8)
|
|
8756
9227
|
}).strict();
|
|
8757
|
-
var Resource =
|
|
8758
|
-
id:
|
|
8759
|
-
organizationId:
|
|
8760
|
-
name:
|
|
8761
|
-
slug:
|
|
8762
|
-
origin:
|
|
8763
|
-
publicKey:
|
|
8764
|
-
revision:
|
|
8765
|
-
enabled:
|
|
8766
|
-
cookieEvents:
|
|
8767
|
-
minimalRealtime:
|
|
9228
|
+
var Resource = z17.object({
|
|
9229
|
+
id: z17.string(),
|
|
9230
|
+
organizationId: z17.string(),
|
|
9231
|
+
name: z17.string(),
|
|
9232
|
+
slug: z17.string(),
|
|
9233
|
+
origin: z17.string().url(),
|
|
9234
|
+
publicKey: z17.string(),
|
|
9235
|
+
revision: z17.number().int().positive(),
|
|
9236
|
+
enabled: z17.boolean(),
|
|
9237
|
+
cookieEvents: z17.boolean(),
|
|
9238
|
+
minimalRealtime: z17.boolean()
|
|
8768
9239
|
});
|
|
8769
9240
|
var readKeys = [
|
|
8770
9241
|
"days",
|
|
@@ -8816,7 +9287,7 @@ async function runAnalyticsCommand(options) {
|
|
|
8816
9287
|
if (options.args[0] === "ga4") return runAnalyticsGoogleCommand(options);
|
|
8817
9288
|
const json = options.args.includes("--json");
|
|
8818
9289
|
try {
|
|
8819
|
-
const { positionals, values } =
|
|
9290
|
+
const { positionals, values } = parseArgs6({
|
|
8820
9291
|
args: options.args,
|
|
8821
9292
|
strict: true,
|
|
8822
9293
|
allowPositionals: true,
|
|
@@ -8898,7 +9369,7 @@ async function runAnalyticsCommand(options) {
|
|
|
8898
9369
|
throw new Error("Use --days 1, 7 or 28.");
|
|
8899
9370
|
if (values.country && !/^(?:[A-Z]{2}|unknown)$/u.test(values.country))
|
|
8900
9371
|
throw new Error("Use an uppercase ISO country code or unknown.");
|
|
8901
|
-
if (values.campaign)
|
|
9372
|
+
if (values.campaign) z17.string().uuid().parse(values.campaign);
|
|
8902
9373
|
const setting = route === "settings set", creating = positionals[1] === "create", archive = positionals[1] === "archive";
|
|
8903
9374
|
let body;
|
|
8904
9375
|
if (setting) {
|
|
@@ -8922,10 +9393,10 @@ async function runAnalyticsCommand(options) {
|
|
|
8922
9393
|
} else if (creating) {
|
|
8923
9394
|
if (!values.file)
|
|
8924
9395
|
throw new Error("Creation requires --file with a JSON definition.");
|
|
8925
|
-
const filename =
|
|
9396
|
+
const filename = path27.resolve(options.cwd ?? process.cwd(), values.file);
|
|
8926
9397
|
if ((await stat3(filename)).size > 16384)
|
|
8927
9398
|
throw new Error("The definition must not exceed 16 KiB.");
|
|
8928
|
-
const text = await
|
|
9399
|
+
const text = await readFile18(filename, "utf8");
|
|
8929
9400
|
if (Buffer.byteLength(text) > 16384)
|
|
8930
9401
|
throw new Error("The definition must not exceed 16 KiB.");
|
|
8931
9402
|
let definition;
|
|
@@ -8934,9 +9405,9 @@ async function runAnalyticsCommand(options) {
|
|
|
8934
9405
|
} catch {
|
|
8935
9406
|
throw new Error("The definition must contain valid JSON.");
|
|
8936
9407
|
}
|
|
8937
|
-
body =
|
|
9408
|
+
body = z17.record(z17.unknown()).parse(definition);
|
|
8938
9409
|
if (action === "events") body = EventInput.parse(body);
|
|
8939
|
-
} else if (archive) body = { id:
|
|
9410
|
+
} else if (archive) body = { id: z17.string().uuid().parse(positionals[2]) };
|
|
8940
9411
|
const context = await commonServiceContext(
|
|
8941
9412
|
options,
|
|
8942
9413
|
"analytics",
|
|
@@ -8983,8 +9454,8 @@ async function runAnalyticsCommand(options) {
|
|
|
8983
9454
|
);
|
|
8984
9455
|
const value = await response.json();
|
|
8985
9456
|
if (!response.ok) {
|
|
8986
|
-
const error =
|
|
8987
|
-
error:
|
|
9457
|
+
const error = z17.object({
|
|
9458
|
+
error: z17.object({ code: z17.string(), message: z17.string().max(500) })
|
|
8988
9459
|
}).safeParse(value);
|
|
8989
9460
|
throw new SiteOSAuthApiError({
|
|
8990
9461
|
code: error.success ? error.data.error.code : "ANALYTICS_REQUEST_FAILED",
|
|
@@ -8992,9 +9463,9 @@ async function runAnalyticsCommand(options) {
|
|
|
8992
9463
|
status: response.status
|
|
8993
9464
|
});
|
|
8994
9465
|
}
|
|
8995
|
-
const record =
|
|
8996
|
-
contractVersion:
|
|
8997
|
-
resourceId:
|
|
9466
|
+
const record = z17.object({
|
|
9467
|
+
contractVersion: z17.literal(1),
|
|
9468
|
+
resourceId: z17.literal(context.resourceId)
|
|
8998
9469
|
}).passthrough().parse(value);
|
|
8999
9470
|
let output;
|
|
9000
9471
|
if (setting) {
|
|
@@ -9013,16 +9484,16 @@ async function runAnalyticsCommand(options) {
|
|
|
9013
9484
|
throw new Error("The saved event does not match this definition.");
|
|
9014
9485
|
} else
|
|
9015
9486
|
output = {
|
|
9016
|
-
id:
|
|
9487
|
+
id: z17.string().uuid().parse(record.id),
|
|
9017
9488
|
...pick(
|
|
9018
9489
|
record,
|
|
9019
9490
|
action === "campaigns" ? ["label", "source", "medium", "campaign"] : action === "goals" ? ["label", "match"] : ["label", "steps"]
|
|
9020
9491
|
)
|
|
9021
9492
|
};
|
|
9022
9493
|
} else if (archive)
|
|
9023
|
-
output = { archived:
|
|
9494
|
+
output = { archived: z17.literal(true).parse(record.archived) };
|
|
9024
9495
|
else if (route === "monitoring prepare")
|
|
9025
|
-
output = { state:
|
|
9496
|
+
output = { state: z17.literal("prepared").parse(record.state) };
|
|
9026
9497
|
else if (action === "realtime") output = pick(record, liveKeys);
|
|
9027
9498
|
else {
|
|
9028
9499
|
const resource = Resource.parse(record.resource);
|
|
@@ -9034,8 +9505,8 @@ async function runAnalyticsCommand(options) {
|
|
|
9034
9505
|
output = {
|
|
9035
9506
|
resource,
|
|
9036
9507
|
lastReceivedAt: record.lastReceivedAt,
|
|
9037
|
-
consentSource:
|
|
9038
|
-
consentSource:
|
|
9508
|
+
consentSource: z17.object({
|
|
9509
|
+
consentSource: z17.enum([
|
|
9039
9510
|
"cookie",
|
|
9040
9511
|
"external",
|
|
9041
9512
|
"waiting_for_cookie",
|
|
@@ -9047,11 +9518,11 @@ async function runAnalyticsCommand(options) {
|
|
|
9047
9518
|
else if (action === "installation")
|
|
9048
9519
|
output = {
|
|
9049
9520
|
resource,
|
|
9050
|
-
installation:
|
|
9521
|
+
installation: z17.string().parse(record.installation),
|
|
9051
9522
|
configuration: record.configuration
|
|
9052
9523
|
};
|
|
9053
9524
|
else if (action === "events") {
|
|
9054
|
-
const definitions =
|
|
9525
|
+
const definitions = z17.array(EventInput).parse(record.definitions);
|
|
9055
9526
|
if (route === "events snippet") {
|
|
9056
9527
|
const definition = definitions.find((d) => d.name === positionals[2]);
|
|
9057
9528
|
if (!definition)
|
|
@@ -9065,13 +9536,13 @@ async function runAnalyticsCommand(options) {
|
|
|
9065
9536
|
};
|
|
9066
9537
|
} else output = { definitions, eventCounts: record.eventCounts };
|
|
9067
9538
|
} else
|
|
9068
|
-
output = action === "report" ? pick(record, readKeys) : { [action]:
|
|
9539
|
+
output = action === "report" ? pick(record, readKeys) : { [action]: z17.array(z17.unknown()).parse(record[action]) };
|
|
9069
9540
|
}
|
|
9070
9541
|
return { exitCode: 0, stdout: JSON.stringify(output, null, 2) };
|
|
9071
9542
|
} catch (cause) {
|
|
9072
9543
|
const error = cause instanceof SiteOSAuthApiError ? { code: cause.code, message: cause.message } : {
|
|
9073
9544
|
code: "ANALYTICS_COMMAND_FAILED",
|
|
9074
|
-
message: cause instanceof
|
|
9545
|
+
message: cause instanceof z17.ZodError ? "The Analytics input or response does not match the supported contract." : cause instanceof Error ? cause.message : "The Analytics command failed."
|
|
9075
9546
|
};
|
|
9076
9547
|
return {
|
|
9077
9548
|
exitCode: cause instanceof SiteOSAuthApiError ? 1 : 2,
|
|
@@ -9081,13 +9552,13 @@ async function runAnalyticsCommand(options) {
|
|
|
9081
9552
|
}
|
|
9082
9553
|
|
|
9083
9554
|
// src/services/seo-repair-command.ts
|
|
9084
|
-
import { parseArgs as
|
|
9085
|
-
import { z as
|
|
9555
|
+
import { parseArgs as parseArgs7 } from "util";
|
|
9556
|
+
import { z as z19 } from "zod";
|
|
9086
9557
|
|
|
9087
9558
|
// src/services/seo-report-client.ts
|
|
9088
|
-
import { open, writeFile as
|
|
9089
|
-
import
|
|
9090
|
-
import { z as
|
|
9559
|
+
import { open, writeFile as writeFile9 } from "fs/promises";
|
|
9560
|
+
import path28 from "path";
|
|
9561
|
+
import { z as z18 } from "zod";
|
|
9091
9562
|
async function seoReportClient(options, environment) {
|
|
9092
9563
|
const context = await commonServiceContext(options, "seo", environment);
|
|
9093
9564
|
if (!context)
|
|
@@ -9122,8 +9593,8 @@ async function seoReportClient(options, environment) {
|
|
|
9122
9593
|
}
|
|
9123
9594
|
);
|
|
9124
9595
|
if (!response.ok) {
|
|
9125
|
-
const result =
|
|
9126
|
-
error:
|
|
9596
|
+
const result = z18.object({
|
|
9597
|
+
error: z18.object({ code: z18.string(), message: z18.string().max(500) })
|
|
9127
9598
|
}).safeParse(await response.json());
|
|
9128
9599
|
throw new SiteOSAuthApiError({
|
|
9129
9600
|
code: result.success ? result.data.error.code : "SEO_REQUEST_FAILED",
|
|
@@ -9137,9 +9608,9 @@ async function seoReportClient(options, environment) {
|
|
|
9137
9608
|
}
|
|
9138
9609
|
};
|
|
9139
9610
|
}
|
|
9140
|
-
var versionedReport =
|
|
9611
|
+
var versionedReport = z18.object({ contractVersion: z18.literal(1) }).passthrough();
|
|
9141
9612
|
async function readSeoInput(cwd, filename) {
|
|
9142
|
-
const file = await open(
|
|
9613
|
+
const file = await open(path28.resolve(cwd ?? process.cwd(), filename), "r");
|
|
9143
9614
|
try {
|
|
9144
9615
|
const stat4 = await file.stat();
|
|
9145
9616
|
if (!stat4.isFile() || stat4.size > 32e3)
|
|
@@ -9163,14 +9634,14 @@ async function readSeoInput(cwd, filename) {
|
|
|
9163
9634
|
}
|
|
9164
9635
|
}
|
|
9165
9636
|
async function writeSeoReport(options, filename, content) {
|
|
9166
|
-
const output =
|
|
9167
|
-
await
|
|
9637
|
+
const output = path28.resolve(options.cwd ?? process.cwd(), filename);
|
|
9638
|
+
await writeFile9(output, content, { flag: "wx", mode: 384 });
|
|
9168
9639
|
return output;
|
|
9169
9640
|
}
|
|
9170
9641
|
function seoReportFailure(cause, json, idempotencyKey) {
|
|
9171
9642
|
const error = {
|
|
9172
9643
|
code: cause instanceof SiteOSAuthApiError ? cause.code : "SEO_COMMAND_FAILED",
|
|
9173
|
-
message: cause instanceof
|
|
9644
|
+
message: cause instanceof z18.ZodError ? "The SEO service returned an invalid response." : cause instanceof Error ? cause.message : "The SEO command failed."
|
|
9174
9645
|
};
|
|
9175
9646
|
return {
|
|
9176
9647
|
exitCode: cause instanceof SiteOSAuthApiError ? 1 : 2,
|
|
@@ -9189,7 +9660,7 @@ function seoReportFailure(cause, json, idempotencyKey) {
|
|
|
9189
9660
|
async function runSeoRepairCommand(options) {
|
|
9190
9661
|
const json = options.args.includes("--json");
|
|
9191
9662
|
try {
|
|
9192
|
-
const { positionals, values } =
|
|
9663
|
+
const { positionals, values } = parseArgs7({
|
|
9193
9664
|
args: options.args,
|
|
9194
9665
|
allowPositionals: true,
|
|
9195
9666
|
strict: true,
|
|
@@ -9234,32 +9705,32 @@ async function runSeoRepairCommand(options) {
|
|
|
9234
9705
|
});
|
|
9235
9706
|
const record = versionedReport.parse(await response.json());
|
|
9236
9707
|
if (recheck) {
|
|
9237
|
-
|
|
9238
|
-
id:
|
|
9239
|
-
resourceId:
|
|
9240
|
-
organizationId:
|
|
9241
|
-
state:
|
|
9708
|
+
z19.object({
|
|
9709
|
+
id: z19.string(),
|
|
9710
|
+
resourceId: z19.literal(client.resourceId),
|
|
9711
|
+
organizationId: z19.literal(client.organizationId),
|
|
9712
|
+
state: z19.literal("queued")
|
|
9242
9713
|
}).parse(record.audit);
|
|
9243
9714
|
return { exitCode: 0, stdout: JSON.stringify(record, null, 2) };
|
|
9244
9715
|
}
|
|
9245
|
-
const brief =
|
|
9246
|
-
schemaVersion:
|
|
9247
|
-
context:
|
|
9248
|
-
auditId:
|
|
9249
|
-
resourceId:
|
|
9250
|
-
organizationId:
|
|
9251
|
-
selectedUrls:
|
|
9252
|
-
ruleId:
|
|
9716
|
+
const brief = z19.object({
|
|
9717
|
+
schemaVersion: z19.literal(1),
|
|
9718
|
+
context: z19.object({
|
|
9719
|
+
auditId: z19.literal(values.audit),
|
|
9720
|
+
resourceId: z19.literal(client.resourceId),
|
|
9721
|
+
organizationId: z19.literal(client.organizationId),
|
|
9722
|
+
selectedUrls: z19.array(z19.string()).min(1).max(20),
|
|
9723
|
+
ruleId: z19.literal(values.rule ?? null)
|
|
9253
9724
|
}),
|
|
9254
|
-
evidence:
|
|
9255
|
-
|
|
9256
|
-
url:
|
|
9257
|
-
findings:
|
|
9725
|
+
evidence: z19.array(
|
|
9726
|
+
z19.object({
|
|
9727
|
+
url: z19.string(),
|
|
9728
|
+
findings: z19.array(z19.unknown()).max(12)
|
|
9258
9729
|
})
|
|
9259
9730
|
).min(1).max(20),
|
|
9260
|
-
suggestedGroups:
|
|
9261
|
-
verificationCommand:
|
|
9262
|
-
text:
|
|
9731
|
+
suggestedGroups: z19.array(z19.unknown()),
|
|
9732
|
+
verificationCommand: z19.string().min(1),
|
|
9733
|
+
text: z19.string().min(1)
|
|
9263
9734
|
}).parse(record);
|
|
9264
9735
|
const canonical = (value) => {
|
|
9265
9736
|
const url = new URL(value);
|
|
@@ -9279,16 +9750,16 @@ async function runSeoRepairCommand(options) {
|
|
|
9279
9750
|
}
|
|
9280
9751
|
|
|
9281
9752
|
// src/services/seo-command.ts
|
|
9282
|
-
import { writeFile as
|
|
9283
|
-
import
|
|
9284
|
-
import { parseArgs as
|
|
9285
|
-
import { z as
|
|
9753
|
+
import { writeFile as writeFile11 } from "fs/promises";
|
|
9754
|
+
import path30 from "path";
|
|
9755
|
+
import { parseArgs as parseArgs11 } from "util";
|
|
9756
|
+
import { z as z23 } from "zod";
|
|
9286
9757
|
|
|
9287
9758
|
// src/services/seo-research-command.ts
|
|
9288
9759
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
9289
9760
|
import { setTimeout as setTimeout2 } from "timers/promises";
|
|
9290
|
-
import { isDeepStrictEqual, parseArgs as
|
|
9291
|
-
import { z as
|
|
9761
|
+
import { isDeepStrictEqual, parseArgs as parseArgs8 } from "util";
|
|
9762
|
+
import { z as z20 } from "zod";
|
|
9292
9763
|
var RESEARCH_HELP = `
|
|
9293
9764
|
siteos seo research summary [--environment <slug>] [--json]
|
|
9294
9765
|
siteos seo research status --kind <kind> [--environment <slug>] [--json]
|
|
@@ -9335,11 +9806,11 @@ var kinds = [
|
|
|
9335
9806
|
];
|
|
9336
9807
|
var sections = [...kinds, "ai-rankings"];
|
|
9337
9808
|
var sectionOf = (request) => request.kind === "ai-visibility" && request.category ? "ai-rankings" : request.kind;
|
|
9338
|
-
var requestSchema =
|
|
9339
|
-
kind:
|
|
9340
|
-
target:
|
|
9809
|
+
var requestSchema = z20.object({
|
|
9810
|
+
kind: z20.enum(["domain", ...kinds]).transform((kind) => kind === "domain" ? "competitors" : kind),
|
|
9811
|
+
target: z20.string().min(1).max(253)
|
|
9341
9812
|
}).passthrough();
|
|
9342
|
-
var runStates =
|
|
9813
|
+
var runStates = z20.enum([
|
|
9343
9814
|
"queued",
|
|
9344
9815
|
"running",
|
|
9345
9816
|
"completed",
|
|
@@ -9355,7 +9826,7 @@ var csvCell = (value) => {
|
|
|
9355
9826
|
async function runSeoResearchCommand(options) {
|
|
9356
9827
|
let idempotencyKey;
|
|
9357
9828
|
try {
|
|
9358
|
-
const { values, positionals } =
|
|
9829
|
+
const { values, positionals } = parseArgs8({
|
|
9359
9830
|
args: options.args.slice(1),
|
|
9360
9831
|
strict: true,
|
|
9361
9832
|
allowPositionals: true,
|
|
@@ -9374,7 +9845,7 @@ async function runSeoResearchCommand(options) {
|
|
|
9374
9845
|
}
|
|
9375
9846
|
});
|
|
9376
9847
|
const action = ["saved", "serp"].includes(positionals[0] ?? "") ? positionals.slice(0, 2).join(" ") : positionals[0] ?? "";
|
|
9377
|
-
const
|
|
9848
|
+
const id2 = positionals[action.includes(" ") ? 2 : 1];
|
|
9378
9849
|
const operations = {
|
|
9379
9850
|
"serp show": { args: 3, flags: ["keyword"] },
|
|
9380
9851
|
"serp ensure": { args: 3, flags: ["keyword"] },
|
|
@@ -9437,26 +9908,26 @@ async function runSeoResearchCommand(options) {
|
|
|
9437
9908
|
);
|
|
9438
9909
|
}
|
|
9439
9910
|
const client = await seoReportClient(options, values.environment);
|
|
9440
|
-
const resourceSchema =
|
|
9441
|
-
id:
|
|
9442
|
-
organizationId:
|
|
9911
|
+
const resourceSchema = z20.object({
|
|
9912
|
+
id: z20.literal(client.resourceId),
|
|
9913
|
+
organizationId: z20.literal(client.organizationId)
|
|
9443
9914
|
}).passthrough();
|
|
9444
|
-
const runSchema =
|
|
9445
|
-
id:
|
|
9446
|
-
resourceId:
|
|
9447
|
-
organizationId:
|
|
9448
|
-
websiteUrl:
|
|
9915
|
+
const runSchema = z20.object({
|
|
9916
|
+
id: z20.string(),
|
|
9917
|
+
resourceId: z20.literal(client.resourceId),
|
|
9918
|
+
organizationId: z20.literal(client.organizationId),
|
|
9919
|
+
websiteUrl: z20.string(),
|
|
9449
9920
|
request: requestSchema,
|
|
9450
9921
|
state: runStates,
|
|
9451
|
-
createdAt:
|
|
9452
|
-
finishedAt:
|
|
9453
|
-
parts:
|
|
9454
|
-
|
|
9455
|
-
key:
|
|
9456
|
-
state:
|
|
9457
|
-
error:
|
|
9458
|
-
dataset:
|
|
9459
|
-
observedAt:
|
|
9922
|
+
createdAt: z20.string(),
|
|
9923
|
+
finishedAt: z20.string().nullable(),
|
|
9924
|
+
parts: z20.array(
|
|
9925
|
+
z20.object({
|
|
9926
|
+
key: z20.string(),
|
|
9927
|
+
state: z20.enum(["completed", "failed"]),
|
|
9928
|
+
error: z20.string().nullable(),
|
|
9929
|
+
dataset: z20.record(z20.unknown()).nullable(),
|
|
9930
|
+
observedAt: z20.string()
|
|
9460
9931
|
}).passthrough()
|
|
9461
9932
|
)
|
|
9462
9933
|
}).passthrough();
|
|
@@ -9470,41 +9941,41 @@ async function runSeoResearchCommand(options) {
|
|
|
9470
9941
|
const query = `?kind=${encodeURIComponent(values.kind ?? "rankings")}`;
|
|
9471
9942
|
let record;
|
|
9472
9943
|
if (action.startsWith("serp ")) {
|
|
9473
|
-
const snapshotSchema =
|
|
9474
|
-
id:
|
|
9475
|
-
organizationId:
|
|
9476
|
-
resourceId:
|
|
9477
|
-
sourceRunId:
|
|
9944
|
+
const snapshotSchema = z20.object({
|
|
9945
|
+
id: z20.string(),
|
|
9946
|
+
organizationId: z20.literal(client.organizationId),
|
|
9947
|
+
resourceId: z20.literal(client.resourceId),
|
|
9948
|
+
sourceRunId: z20.literal(id2),
|
|
9478
9949
|
request: requestSchema,
|
|
9479
|
-
keywordKey:
|
|
9950
|
+
keywordKey: z20.literal(
|
|
9480
9951
|
values.keyword.normalize("NFKC").trim().toLowerCase().replace(/\s+/gu, " ")
|
|
9481
9952
|
),
|
|
9482
|
-
state:
|
|
9953
|
+
state: z20.enum([
|
|
9483
9954
|
"queued",
|
|
9484
9955
|
"running",
|
|
9485
9956
|
"completed",
|
|
9486
9957
|
"failed",
|
|
9487
9958
|
"cancelled"
|
|
9488
9959
|
]),
|
|
9489
|
-
dataset:
|
|
9490
|
-
type:
|
|
9491
|
-
keyword:
|
|
9492
|
-
country:
|
|
9493
|
-
language:
|
|
9494
|
-
device:
|
|
9495
|
-
observedAt:
|
|
9496
|
-
rows:
|
|
9497
|
-
|
|
9498
|
-
position:
|
|
9499
|
-
title:
|
|
9500
|
-
url:
|
|
9501
|
-
domain:
|
|
9502
|
-
description:
|
|
9960
|
+
dataset: z20.object({
|
|
9961
|
+
type: z20.literal("serp"),
|
|
9962
|
+
keyword: z20.string(),
|
|
9963
|
+
country: z20.string(),
|
|
9964
|
+
language: z20.string(),
|
|
9965
|
+
device: z20.enum(["desktop", "mobile"]),
|
|
9966
|
+
observedAt: z20.string(),
|
|
9967
|
+
rows: z20.array(
|
|
9968
|
+
z20.object({
|
|
9969
|
+
position: z20.number(),
|
|
9970
|
+
title: z20.string(),
|
|
9971
|
+
url: z20.string(),
|
|
9972
|
+
domain: z20.string(),
|
|
9973
|
+
description: z20.string()
|
|
9503
9974
|
})
|
|
9504
9975
|
)
|
|
9505
9976
|
}).nullable()
|
|
9506
9977
|
}).passthrough();
|
|
9507
|
-
const suffix = `/runs/${encodeURIComponent(
|
|
9978
|
+
const suffix = `/runs/${encodeURIComponent(id2)}/serp`;
|
|
9508
9979
|
const read = () => get(`${suffix}?keyword=${encodeURIComponent(values.keyword)}`);
|
|
9509
9980
|
record = action === "serp ensure" ? await get(suffix, "run", { keyword: values.keyword }) : await read();
|
|
9510
9981
|
const deadline = Date.now() + timeout * 1e3;
|
|
@@ -9543,7 +10014,7 @@ async function runSeoResearchCommand(options) {
|
|
|
9543
10014
|
"description"
|
|
9544
10015
|
],
|
|
9545
10016
|
...(data?.rows.length ? data.rows : [null]).map((row) => [
|
|
9546
|
-
|
|
10017
|
+
id2,
|
|
9547
10018
|
values.keyword,
|
|
9548
10019
|
data?.country,
|
|
9549
10020
|
data?.language,
|
|
@@ -9560,7 +10031,7 @@ async function runSeoResearchCommand(options) {
|
|
|
9560
10031
|
const output = await writeSeoReport(options, values.output, content);
|
|
9561
10032
|
record = {
|
|
9562
10033
|
contractVersion: 1,
|
|
9563
|
-
runId:
|
|
10034
|
+
runId: id2,
|
|
9564
10035
|
keyword: values.keyword,
|
|
9565
10036
|
state: snapshot.state,
|
|
9566
10037
|
format: values.format,
|
|
@@ -9573,18 +10044,18 @@ async function runSeoResearchCommand(options) {
|
|
|
9573
10044
|
};
|
|
9574
10045
|
}
|
|
9575
10046
|
if (action === "retry") {
|
|
9576
|
-
const source = await get(`/runs/${encodeURIComponent(
|
|
10047
|
+
const source = await get(`/runs/${encodeURIComponent(id2)}`);
|
|
9577
10048
|
resourceSchema.parse(source.resource);
|
|
9578
10049
|
const original = runSchema.parse(source.run);
|
|
9579
|
-
if (original.id !==
|
|
10050
|
+
if (original.id !== id2 || ["queued", "running"].includes(original.state))
|
|
9580
10051
|
throw new Error("Choose the exact stopped research report to retry.");
|
|
9581
10052
|
record = await get("/runs", "run", {
|
|
9582
10053
|
request: original.request,
|
|
9583
|
-
retryOf:
|
|
10054
|
+
retryOf: id2,
|
|
9584
10055
|
idempotencyKey
|
|
9585
10056
|
});
|
|
9586
10057
|
const next = runSchema.parse(record.run);
|
|
9587
|
-
if (next.retryOf !==
|
|
10058
|
+
if (next.retryOf !== id2 || !isDeepStrictEqual(next.request, original.request))
|
|
9588
10059
|
throw new Error(
|
|
9589
10060
|
"The continued check does not match the original report."
|
|
9590
10061
|
);
|
|
@@ -9609,9 +10080,9 @@ async function runSeoResearchCommand(options) {
|
|
|
9609
10080
|
name: values.name.trim(),
|
|
9610
10081
|
...values.id ? { id: values.id } : {}
|
|
9611
10082
|
});
|
|
9612
|
-
const saved =
|
|
9613
|
-
id:
|
|
9614
|
-
name:
|
|
10083
|
+
const saved = z20.object({
|
|
10084
|
+
id: z20.string(),
|
|
10085
|
+
name: z20.literal(values.name.trim()),
|
|
9615
10086
|
request: requestSchema
|
|
9616
10087
|
}).parse(record.saved);
|
|
9617
10088
|
if (values.id && saved.id !== values.id || !isDeepStrictEqual(saved.request, plan.request))
|
|
@@ -9621,27 +10092,27 @@ async function runSeoResearchCommand(options) {
|
|
|
9621
10092
|
} else record = plan;
|
|
9622
10093
|
} else if (action === "cancel" || action === "saved remove") {
|
|
9623
10094
|
record = await get(
|
|
9624
|
-
`/${action === "cancel" ? "runs" : "saved"}/${encodeURIComponent(
|
|
10095
|
+
`/${action === "cancel" ? "runs" : "saved"}/${encodeURIComponent(id2)}/${action === "cancel" ? "cancel" : "remove"}`,
|
|
9625
10096
|
action === "cancel" ? "run" : "write",
|
|
9626
10097
|
{}
|
|
9627
10098
|
);
|
|
9628
|
-
|
|
10099
|
+
z20.literal(true).parse(
|
|
9629
10100
|
record[action === "cancel" ? "cancelled" : "removed"]
|
|
9630
10101
|
);
|
|
9631
10102
|
} else if (action === "summary") {
|
|
9632
10103
|
record = await get("/summary");
|
|
9633
10104
|
resourceSchema.parse(record.resource);
|
|
9634
|
-
|
|
10105
|
+
z20.array(z20.object({ kind: z20.enum(sections), summary: z20.unknown() })).parse(
|
|
9635
10106
|
record.checks
|
|
9636
10107
|
);
|
|
9637
10108
|
} else if (["status", "history", "saved list"].includes(action)) {
|
|
9638
10109
|
const view = await get(query);
|
|
9639
10110
|
resourceSchema.parse(view.resource);
|
|
9640
|
-
const runs =
|
|
9641
|
-
const saved =
|
|
9642
|
-
|
|
9643
|
-
id:
|
|
9644
|
-
name:
|
|
10111
|
+
const runs = z20.array(runSchema).parse(view.runs);
|
|
10112
|
+
const saved = z20.array(
|
|
10113
|
+
z20.object({
|
|
10114
|
+
id: z20.string(),
|
|
10115
|
+
name: z20.string(),
|
|
9645
10116
|
request: requestSchema
|
|
9646
10117
|
}).passthrough()
|
|
9647
10118
|
).parse(view.saved);
|
|
@@ -9663,10 +10134,10 @@ async function runSeoResearchCommand(options) {
|
|
|
9663
10134
|
} else {
|
|
9664
10135
|
const deadline = Date.now() + timeout * 1e3;
|
|
9665
10136
|
while (true) {
|
|
9666
|
-
record = await get(`/runs/${encodeURIComponent(
|
|
10137
|
+
record = await get(`/runs/${encodeURIComponent(id2)}`);
|
|
9667
10138
|
resourceSchema.parse(record.resource);
|
|
9668
10139
|
const run = runSchema.parse(record.run);
|
|
9669
|
-
if (run.id !==
|
|
10140
|
+
if (run.id !== id2)
|
|
9670
10141
|
throw new Error(
|
|
9671
10142
|
"The response does not match the selected research check."
|
|
9672
10143
|
);
|
|
@@ -9756,7 +10227,7 @@ async function runSeoResearchCommand(options) {
|
|
|
9756
10227
|
const output = await writeSeoReport(options, values.output, content);
|
|
9757
10228
|
record = {
|
|
9758
10229
|
contractVersion: 1,
|
|
9759
|
-
runId:
|
|
10230
|
+
runId: id2,
|
|
9760
10231
|
state: run.state,
|
|
9761
10232
|
format: values.format,
|
|
9762
10233
|
output
|
|
@@ -9776,8 +10247,8 @@ async function runSeoResearchCommand(options) {
|
|
|
9776
10247
|
}
|
|
9777
10248
|
|
|
9778
10249
|
// src/services/seo-gsc-command.ts
|
|
9779
|
-
import { parseArgs as
|
|
9780
|
-
import { z as
|
|
10250
|
+
import { parseArgs as parseArgs9 } from "util";
|
|
10251
|
+
import { z as z21 } from "zod";
|
|
9781
10252
|
var GSC_HELP = `
|
|
9782
10253
|
siteos seo gsc status [--environment <slug>] [--json]
|
|
9783
10254
|
siteos seo gsc report [--dataset <pages|queries>] [--query <text>] [--filter <all|issues|declining>] [--sort <clicks|change|impressions>] [--page <number>] [--url <page-url>] [--environment <slug>] [--json]
|
|
@@ -9795,7 +10266,7 @@ Bind/disconnect require a current revision and separate settings authority. Disc
|
|
|
9795
10266
|
`;
|
|
9796
10267
|
async function runSeoGscCommand(options) {
|
|
9797
10268
|
try {
|
|
9798
|
-
const { values, positionals } =
|
|
10269
|
+
const { values, positionals } = parseArgs9({
|
|
9799
10270
|
args: options.args.slice(1),
|
|
9800
10271
|
strict: true,
|
|
9801
10272
|
allowPositionals: true,
|
|
@@ -9877,13 +10348,13 @@ async function runSeoGscCommand(options) {
|
|
|
9877
10348
|
`seo:search:${action === "sync" ? "sync" : writing ? "write" : "read"}`,
|
|
9878
10349
|
body
|
|
9879
10350
|
);
|
|
9880
|
-
const runSchema =
|
|
9881
|
-
id:
|
|
9882
|
-
resourceId:
|
|
9883
|
-
organizationId:
|
|
9884
|
-
websiteUrl:
|
|
9885
|
-
siteUrl:
|
|
9886
|
-
state:
|
|
10351
|
+
const runSchema = z21.object({
|
|
10352
|
+
id: z21.string(),
|
|
10353
|
+
resourceId: z21.literal(client.resourceId),
|
|
10354
|
+
organizationId: z21.literal(client.organizationId),
|
|
10355
|
+
websiteUrl: z21.string(),
|
|
10356
|
+
siteUrl: z21.string(),
|
|
10357
|
+
state: z21.enum([
|
|
9887
10358
|
"queued",
|
|
9888
10359
|
"running",
|
|
9889
10360
|
"completed",
|
|
@@ -9891,32 +10362,32 @@ async function runSeoGscCommand(options) {
|
|
|
9891
10362
|
"failed",
|
|
9892
10363
|
"cancelled"
|
|
9893
10364
|
]),
|
|
9894
|
-
dates:
|
|
10365
|
+
dates: z21.unknown()
|
|
9895
10366
|
}).passthrough();
|
|
9896
|
-
const bindingSchema =
|
|
9897
|
-
resourceId:
|
|
9898
|
-
organizationId:
|
|
9899
|
-
websiteUrl:
|
|
9900
|
-
siteUrl:
|
|
9901
|
-
revision:
|
|
10367
|
+
const bindingSchema = z21.object({
|
|
10368
|
+
resourceId: z21.literal(client.resourceId),
|
|
10369
|
+
organizationId: z21.literal(client.organizationId),
|
|
10370
|
+
websiteUrl: z21.string(),
|
|
10371
|
+
siteUrl: z21.string(),
|
|
10372
|
+
revision: z21.number().int().positive()
|
|
9902
10373
|
}).passthrough();
|
|
9903
|
-
const metrics =
|
|
9904
|
-
clicks:
|
|
9905
|
-
impressions:
|
|
9906
|
-
ctr:
|
|
9907
|
-
position:
|
|
10374
|
+
const metrics = z21.object({
|
|
10375
|
+
clicks: z21.number(),
|
|
10376
|
+
impressions: z21.number(),
|
|
10377
|
+
ctr: z21.number(),
|
|
10378
|
+
position: z21.number()
|
|
9908
10379
|
}).passthrough().nullable();
|
|
9909
10380
|
const validateView = (data) => {
|
|
9910
10381
|
const record2 = versionedReport.parse(data);
|
|
9911
|
-
|
|
10382
|
+
z21.literal(client.resourceId).parse(record2.resourceId);
|
|
9912
10383
|
bindingSchema.nullable().parse(record2.binding);
|
|
9913
10384
|
runSchema.nullable().parse(record2.latest);
|
|
9914
10385
|
runSchema.nullable().parse(record2.report);
|
|
9915
|
-
|
|
9916
|
-
|
|
10386
|
+
z21.array(
|
|
10387
|
+
z21.object({ key: z21.string(), current: metrics, previous: metrics }).passthrough()
|
|
9917
10388
|
).parse(record2.rows);
|
|
9918
|
-
|
|
9919
|
-
|
|
10389
|
+
z21.number().int().nonnegative().parse(record2.total);
|
|
10390
|
+
z21.literal(values.dataset ?? "pages").parse(record2.dataset);
|
|
9920
10391
|
return record2;
|
|
9921
10392
|
};
|
|
9922
10393
|
if (action === "export") {
|
|
@@ -9932,9 +10403,9 @@ async function runSeoGscCommand(options) {
|
|
|
9932
10403
|
const text = await response.text();
|
|
9933
10404
|
if (values.format === "json") {
|
|
9934
10405
|
const report = validateView(JSON.parse(text));
|
|
9935
|
-
|
|
9936
|
-
|
|
9937
|
-
|
|
10406
|
+
z21.literal(reportId).parse(runSchema.parse(report.report).id);
|
|
10407
|
+
z21.array(z21.unknown()).length(rows).parse(report.rows);
|
|
10408
|
+
z21.literal(truncated === "true").parse(report.truncated);
|
|
9938
10409
|
}
|
|
9939
10410
|
const output = await writeSeoReport(options, values.output, text);
|
|
9940
10411
|
return {
|
|
@@ -9955,7 +10426,7 @@ async function runSeoGscCommand(options) {
|
|
|
9955
10426
|
};
|
|
9956
10427
|
}
|
|
9957
10428
|
const record = versionedReport.parse(await response.json());
|
|
9958
|
-
if (action === "disconnect")
|
|
10429
|
+
if (action === "disconnect") z21.literal(true).parse(record.unbound);
|
|
9959
10430
|
else if (writing) {
|
|
9960
10431
|
runSchema.parse(record.run);
|
|
9961
10432
|
if (action === "bind") {
|
|
@@ -9974,11 +10445,11 @@ async function runSeoGscCommand(options) {
|
|
|
9974
10445
|
|
|
9975
10446
|
// src/services/seo-performance-command.ts
|
|
9976
10447
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
9977
|
-
import { writeFile as
|
|
9978
|
-
import
|
|
10448
|
+
import { writeFile as writeFile10 } from "fs/promises";
|
|
10449
|
+
import path29 from "path";
|
|
9979
10450
|
import { setTimeout as setTimeout3 } from "timers/promises";
|
|
9980
|
-
import { parseArgs as
|
|
9981
|
-
import { z as
|
|
10451
|
+
import { parseArgs as parseArgs10 } from "util";
|
|
10452
|
+
import { z as z22 } from "zod";
|
|
9982
10453
|
var PERFORMANCE_HELP = `
|
|
9983
10454
|
siteos seo performance run --audit <id> --url <url> [--url <url>...] [--device <mobile|desktop>] [--idempotency-key <key>] [--environment <slug>] [--json]
|
|
9984
10455
|
siteos seo performance list [--device <mobile|desktop>] [--environment <slug>] [--json]
|
|
@@ -9995,7 +10466,7 @@ Reuse the returned idempotency key after an uncertain run response. Wait default
|
|
|
9995
10466
|
async function runSeoPerformanceCommand(options) {
|
|
9996
10467
|
let idempotencyKey;
|
|
9997
10468
|
try {
|
|
9998
|
-
const { values, positionals } =
|
|
10469
|
+
const { values, positionals } = parseArgs10({
|
|
9999
10470
|
args: options.args.slice(1),
|
|
10000
10471
|
strict: true,
|
|
10001
10472
|
allowPositionals: true,
|
|
@@ -10012,7 +10483,7 @@ async function runSeoPerformanceCommand(options) {
|
|
|
10012
10483
|
}
|
|
10013
10484
|
});
|
|
10014
10485
|
const action = positionals[0] ?? "";
|
|
10015
|
-
const
|
|
10486
|
+
const id2 = positionals[1];
|
|
10016
10487
|
const operations = {
|
|
10017
10488
|
run: { args: 1, flags: ["audit", "url", "device", "idempotency-key"] },
|
|
10018
10489
|
list: { args: 1, flags: ["device"] },
|
|
@@ -10066,12 +10537,12 @@ async function runSeoPerformanceCommand(options) {
|
|
|
10066
10537
|
const runtime = commonProjectRuntime(options);
|
|
10067
10538
|
const writing = ["run", "cancel"].includes(action);
|
|
10068
10539
|
const scope = writing ? "seo:audits:write" : "seo:workspace:read";
|
|
10069
|
-
const batchSchema =
|
|
10070
|
-
id:
|
|
10071
|
-
resourceId:
|
|
10072
|
-
organizationId:
|
|
10073
|
-
sourceAuditId:
|
|
10074
|
-
state:
|
|
10540
|
+
const batchSchema = z22.object({
|
|
10541
|
+
id: z22.string(),
|
|
10542
|
+
resourceId: z22.literal(context.resourceId),
|
|
10543
|
+
organizationId: z22.literal(context.overview.project.organizationId),
|
|
10544
|
+
sourceAuditId: z22.string(),
|
|
10545
|
+
state: z22.enum([
|
|
10075
10546
|
"queued",
|
|
10076
10547
|
"running",
|
|
10077
10548
|
"completed",
|
|
@@ -10079,13 +10550,13 @@ async function runSeoPerformanceCommand(options) {
|
|
|
10079
10550
|
"failed",
|
|
10080
10551
|
"cancelled"
|
|
10081
10552
|
]),
|
|
10082
|
-
device:
|
|
10083
|
-
urls:
|
|
10553
|
+
device: z22.enum(["mobile", "desktop"]),
|
|
10554
|
+
urls: z22.array(z22.string()).min(1).max(10)
|
|
10084
10555
|
}).passthrough();
|
|
10085
10556
|
const query = new URLSearchParams();
|
|
10086
10557
|
if (values.device) query.set("device", values.device);
|
|
10087
10558
|
if (!writing && values.url?.[0]) query.set("pageUrl", values.url[0]);
|
|
10088
|
-
const suffix =
|
|
10559
|
+
const suffix = id2 ? `/${encodeURIComponent(id2)}${action === "cancel" ? "/cancel" : action === "export" ? "/export" : ""}` : "";
|
|
10089
10560
|
if (action === "export") query.set("format", values.format);
|
|
10090
10561
|
const deadline = Date.now() + timeout * 1e3;
|
|
10091
10562
|
while (true) {
|
|
@@ -10120,7 +10591,7 @@ async function runSeoPerformanceCommand(options) {
|
|
|
10120
10591
|
}
|
|
10121
10592
|
);
|
|
10122
10593
|
if (action === "export" && response.ok) {
|
|
10123
|
-
if (!(response instanceof Response) || response.headers.get("X-SEO-Browser-Id") !==
|
|
10594
|
+
if (!(response instanceof Response) || response.headers.get("X-SEO-Browser-Id") !== id2 || !response.headers.get("Content-Type")?.startsWith(
|
|
10124
10595
|
values.format === "csv" ? "text/csv" : "application/json"
|
|
10125
10596
|
))
|
|
10126
10597
|
throw new Error(
|
|
@@ -10128,25 +10599,25 @@ async function runSeoPerformanceCommand(options) {
|
|
|
10128
10599
|
);
|
|
10129
10600
|
const text = await response.text();
|
|
10130
10601
|
if (values.format === "json") {
|
|
10131
|
-
const parsed =
|
|
10132
|
-
contractVersion:
|
|
10602
|
+
const parsed = z22.object({
|
|
10603
|
+
contractVersion: z22.literal(1),
|
|
10133
10604
|
batch: batchSchema,
|
|
10134
|
-
pages:
|
|
10605
|
+
pages: z22.array(z22.unknown())
|
|
10135
10606
|
}).parse(JSON.parse(text));
|
|
10136
|
-
if (parsed.batch.id !==
|
|
10607
|
+
if (parsed.batch.id !== id2)
|
|
10137
10608
|
throw new Error(
|
|
10138
10609
|
"The export response does not match the selected check."
|
|
10139
10610
|
);
|
|
10140
10611
|
}
|
|
10141
|
-
const output =
|
|
10612
|
+
const output = path29.resolve(
|
|
10142
10613
|
options.cwd ?? process.cwd(),
|
|
10143
10614
|
values.output
|
|
10144
10615
|
);
|
|
10145
|
-
await
|
|
10616
|
+
await writeFile10(output, text, { flag: "wx", mode: 384 });
|
|
10146
10617
|
return {
|
|
10147
10618
|
exitCode: 0,
|
|
10148
10619
|
stdout: JSON.stringify(
|
|
10149
|
-
{ batchId:
|
|
10620
|
+
{ batchId: id2, output, format: values.format },
|
|
10150
10621
|
null,
|
|
10151
10622
|
2
|
|
10152
10623
|
)
|
|
@@ -10154,8 +10625,8 @@ async function runSeoPerformanceCommand(options) {
|
|
|
10154
10625
|
}
|
|
10155
10626
|
const data = await response.json();
|
|
10156
10627
|
if (!response.ok) {
|
|
10157
|
-
const error =
|
|
10158
|
-
error:
|
|
10628
|
+
const error = z22.object({
|
|
10629
|
+
error: z22.object({ code: z22.string(), message: z22.string().max(500) })
|
|
10159
10630
|
}).safeParse(data);
|
|
10160
10631
|
throw new SiteOSAuthApiError({
|
|
10161
10632
|
code: error.success ? error.data.error.code : "SEO_REQUEST_FAILED",
|
|
@@ -10163,8 +10634,8 @@ async function runSeoPerformanceCommand(options) {
|
|
|
10163
10634
|
status: response.status
|
|
10164
10635
|
});
|
|
10165
10636
|
}
|
|
10166
|
-
const record =
|
|
10167
|
-
if (action === "cancel")
|
|
10637
|
+
const record = z22.object({ contractVersion: z22.literal(1) }).passthrough().parse(data);
|
|
10638
|
+
if (action === "cancel") z22.literal(true).parse(record.cancelled);
|
|
10168
10639
|
else if (action === "run") {
|
|
10169
10640
|
const accepted = batchSchema.parse(record.batch);
|
|
10170
10641
|
const normalize = (url) => {
|
|
@@ -10177,10 +10648,10 @@ async function runSeoPerformanceCommand(options) {
|
|
|
10177
10648
|
"The queued check does not match the requested source, device and URLs."
|
|
10178
10649
|
);
|
|
10179
10650
|
} else {
|
|
10180
|
-
|
|
10181
|
-
|
|
10651
|
+
z22.literal(context.resourceId).parse(record.resourceId);
|
|
10652
|
+
z22.array(batchSchema).parse(record.batches);
|
|
10182
10653
|
const selected = batchSchema.nullable().parse(record.batch);
|
|
10183
|
-
if (
|
|
10654
|
+
if (id2 && selected?.id !== id2)
|
|
10184
10655
|
throw new Error(
|
|
10185
10656
|
"The response does not match the selected performance check."
|
|
10186
10657
|
);
|
|
@@ -10205,7 +10676,7 @@ async function runSeoPerformanceCommand(options) {
|
|
|
10205
10676
|
} catch (cause) {
|
|
10206
10677
|
const error = {
|
|
10207
10678
|
code: cause instanceof SiteOSAuthApiError ? cause.code : "SEO_COMMAND_FAILED",
|
|
10208
|
-
message: cause instanceof
|
|
10679
|
+
message: cause instanceof z22.ZodError ? "The SEO service returned an invalid response." : cause instanceof Error ? cause.message : "The performance command failed."
|
|
10209
10680
|
};
|
|
10210
10681
|
return {
|
|
10211
10682
|
exitCode: cause instanceof SiteOSAuthApiError ? 1 : 2,
|
|
@@ -10245,7 +10716,7 @@ Usage:
|
|
|
10245
10716
|
siteos seo recheck --audit <id> --url <url> [--url <url>...] [--resource <expected-id>] [--environment <slug>] [--json]
|
|
10246
10717
|
siteos seo issue <ignore|restore> --audit <id> --url <url> --rule <id> --reason <text> --revision <number> [--environment <slug>] [--json]
|
|
10247
10718
|
siteos seo schedule show [--environment <slug>] [--json]
|
|
10248
|
-
siteos seo schedule set --enabled <true|false> --weekday <1-7> --time <HH:mm> --timezone <IANA> --revision <number> [--environment <slug>] [--json]
|
|
10719
|
+
siteos seo schedule set --enabled <true|false> (--days <1,2,3|daily|weekdays> | --weekday <1-7>) --time <HH:mm> --timezone <IANA> --revision <number> [--environment <slug>] [--json]
|
|
10249
10720
|
siteos seo notifications retry <notification-id> [--environment <slug>] [--json]
|
|
10250
10721
|
siteos seo notifications show [--environment <slug>] [--json]
|
|
10251
10722
|
siteos seo notifications destinations [--environment <slug>] [--json]
|
|
@@ -10277,7 +10748,7 @@ async function runSeoCommand(options) {
|
|
|
10277
10748
|
return runSeoRepairCommand(options);
|
|
10278
10749
|
const json = options.args.includes("--json");
|
|
10279
10750
|
try {
|
|
10280
|
-
const { positionals, values } =
|
|
10751
|
+
const { positionals, values } = parseArgs11({
|
|
10281
10752
|
args: options.args,
|
|
10282
10753
|
strict: true,
|
|
10283
10754
|
allowPositionals: true,
|
|
@@ -10294,6 +10765,7 @@ async function runSeoCommand(options) {
|
|
|
10294
10765
|
state: { type: "string" },
|
|
10295
10766
|
enabled: { type: "string" },
|
|
10296
10767
|
weekday: { type: "string" },
|
|
10768
|
+
days: { type: "string" },
|
|
10297
10769
|
time: { type: "string" },
|
|
10298
10770
|
timezone: { type: "string" },
|
|
10299
10771
|
destination: { type: "string" },
|
|
@@ -10310,7 +10782,7 @@ async function runSeoCommand(options) {
|
|
|
10310
10782
|
status: { flags: [], args: 1 },
|
|
10311
10783
|
"schedule show": { flags: [], args: 2 },
|
|
10312
10784
|
"schedule set": {
|
|
10313
|
-
flags: ["enabled", "weekday", "time", "timezone", "revision"],
|
|
10785
|
+
flags: ["enabled", "weekday", "days", "time", "timezone", "revision"],
|
|
10314
10786
|
args: 2
|
|
10315
10787
|
},
|
|
10316
10788
|
"notifications retry": { flags: [], args: 3 },
|
|
@@ -10375,9 +10847,12 @@ async function runSeoCommand(options) {
|
|
|
10375
10847
|
const setting = automation && positionals[1] === "set";
|
|
10376
10848
|
if (setting && (!values.revision || !["true", "false"].includes(values.enabled ?? "")))
|
|
10377
10849
|
throw new Error("Settings require --enabled true|false and --revision.");
|
|
10850
|
+
const days = values.days === "daily" ? [1, 2, 3, 4, 5, 6, 7] : values.days === "weekdays" ? [1, 2, 3, 4, 5] : values.days?.split(",").map(Number);
|
|
10378
10851
|
if (route === "schedule set") {
|
|
10379
|
-
if (!/^[1-7]$/u.test(values.weekday ?? "") || !/^([01]\d|2[0-3]):[0-5]\d$/u.test(values.time ?? "") || !values.timezone)
|
|
10380
|
-
throw new Error(
|
|
10852
|
+
if (values.days !== void 0 === (values.weekday !== void 0) || (days ? !days.length || days.some((day) => !Number.isInteger(day) || day < 1 || day > 7) || new Set(days).size !== days.length : !/^[1-7]$/u.test(values.weekday ?? "")) || !/^([01]\d|2[0-3]):[0-5]\d$/u.test(values.time ?? "") || !values.timezone)
|
|
10853
|
+
throw new Error(
|
|
10854
|
+
"Use --days daily, weekdays or distinct days 1\u20137 (or --weekday), HH:mm and an IANA time zone."
|
|
10855
|
+
);
|
|
10381
10856
|
try {
|
|
10382
10857
|
new Intl.DateTimeFormat("en", { timeZone: values.timezone });
|
|
10383
10858
|
} catch {
|
|
@@ -10439,7 +10914,7 @@ async function runSeoCommand(options) {
|
|
|
10439
10914
|
const suffix = retryNotification ? "/notification-retries" : automation ? setting ? `/${action}` : route === "notifications destinations" ? "/destinations" : "/automation" : action === "export" ? `/export?${query}` : route === "audit run" ? "/audits" : route === "audit cancel" ? `/audits/${encodeURIComponent(positionals[2])}/cancel` : action === "issue" ? "/dispositions" : `?${query}`;
|
|
10440
10915
|
const body = retryNotification ? { notificationId: positionals[2] } : route === "schedule set" ? {
|
|
10441
10916
|
enabled: values.enabled === "true",
|
|
10442
|
-
weekday: Number(values.weekday),
|
|
10917
|
+
...days ? { days: [...days].sort((a, b) => a - b) } : { weekday: Number(values.weekday) },
|
|
10443
10918
|
time: values.time,
|
|
10444
10919
|
timeZone: values.timezone,
|
|
10445
10920
|
expectedRevision: Number(values.revision)
|
|
@@ -10483,18 +10958,18 @@ async function runSeoCommand(options) {
|
|
|
10483
10958
|
);
|
|
10484
10959
|
const text = await response.text();
|
|
10485
10960
|
if (values.format === "json")
|
|
10486
|
-
|
|
10487
|
-
contractVersion:
|
|
10488
|
-
audit:
|
|
10489
|
-
id:
|
|
10490
|
-
resourceId:
|
|
10961
|
+
z23.object({
|
|
10962
|
+
contractVersion: z23.literal(1),
|
|
10963
|
+
audit: z23.object({
|
|
10964
|
+
id: z23.literal(values.audit),
|
|
10965
|
+
resourceId: z23.literal(context.resourceId)
|
|
10491
10966
|
}),
|
|
10492
|
-
kind:
|
|
10493
|
-
totalRows:
|
|
10494
|
-
rows:
|
|
10967
|
+
kind: z23.literal(values.kind),
|
|
10968
|
+
totalRows: z23.literal(rows),
|
|
10969
|
+
rows: z23.array(z23.unknown()).length(rows)
|
|
10495
10970
|
}).parse(JSON.parse(text));
|
|
10496
|
-
const output =
|
|
10497
|
-
await
|
|
10971
|
+
const output = path30.resolve(options.cwd ?? process.cwd(), values.output);
|
|
10972
|
+
await writeFile11(output, text, { flag: "wx", mode: 384 });
|
|
10498
10973
|
return {
|
|
10499
10974
|
exitCode: 0,
|
|
10500
10975
|
stdout: JSON.stringify(
|
|
@@ -10512,8 +10987,8 @@ async function runSeoCommand(options) {
|
|
|
10512
10987
|
}
|
|
10513
10988
|
const data = await response.json();
|
|
10514
10989
|
if (!response.ok) {
|
|
10515
|
-
const result =
|
|
10516
|
-
error:
|
|
10990
|
+
const result = z23.object({
|
|
10991
|
+
error: z23.object({ code: z23.string(), message: z23.string().max(500) })
|
|
10517
10992
|
}).safeParse(data);
|
|
10518
10993
|
throw new SiteOSAuthApiError({
|
|
10519
10994
|
code: result.success ? result.data.error.code : "SEO_REQUEST_FAILED",
|
|
@@ -10521,34 +10996,35 @@ async function runSeoCommand(options) {
|
|
|
10521
10996
|
status: response.status
|
|
10522
10997
|
});
|
|
10523
10998
|
}
|
|
10524
|
-
const record =
|
|
10999
|
+
const record = z23.object({ contractVersion: z23.literal(1) }).passthrough().parse(data);
|
|
10525
11000
|
if (automation) {
|
|
10526
|
-
|
|
10527
|
-
const schedule =
|
|
10528
|
-
enabled:
|
|
10529
|
-
weekday:
|
|
10530
|
-
|
|
10531
|
-
|
|
10532
|
-
|
|
10533
|
-
|
|
11001
|
+
z23.literal(context.resourceId).parse(record.resourceId);
|
|
11002
|
+
const schedule = z23.object({
|
|
11003
|
+
enabled: z23.boolean(),
|
|
11004
|
+
weekday: z23.number().int().min(1).max(7),
|
|
11005
|
+
days: z23.array(z23.number().int().min(1).max(7)).min(1).max(7).optional(),
|
|
11006
|
+
time: z23.string(),
|
|
11007
|
+
timeZone: z23.string(),
|
|
11008
|
+
revision: z23.number().int().min(0),
|
|
11009
|
+
nextRunAt: z23.string().nullable()
|
|
10534
11010
|
});
|
|
10535
|
-
const notificationRoute =
|
|
10536
|
-
enabled:
|
|
10537
|
-
minimumSeverity:
|
|
10538
|
-
includeFailures:
|
|
10539
|
-
revision:
|
|
10540
|
-
destinationId:
|
|
11011
|
+
const notificationRoute = z23.object({
|
|
11012
|
+
enabled: z23.boolean(),
|
|
11013
|
+
minimumSeverity: z23.enum(["error", "warning"]),
|
|
11014
|
+
includeFailures: z23.boolean(),
|
|
11015
|
+
revision: z23.number().int().min(0),
|
|
11016
|
+
destinationId: z23.string().nullable()
|
|
10541
11017
|
});
|
|
10542
11018
|
if (retryNotification) {
|
|
10543
|
-
|
|
10544
|
-
|
|
11019
|
+
z23.literal(true).parse(record.retryQueued);
|
|
11020
|
+
z23.literal(positionals[2]).parse(record.notificationId);
|
|
10545
11021
|
} else if (route === "notifications destinations")
|
|
10546
|
-
|
|
10547
|
-
candidates:
|
|
10548
|
-
|
|
10549
|
-
candidateId:
|
|
10550
|
-
label:
|
|
10551
|
-
availability:
|
|
11022
|
+
z23.object({
|
|
11023
|
+
candidates: z23.array(
|
|
11024
|
+
z23.object({
|
|
11025
|
+
candidateId: z23.string(),
|
|
11026
|
+
label: z23.string(),
|
|
11027
|
+
availability: z23.literal("available")
|
|
10552
11028
|
})
|
|
10553
11029
|
)
|
|
10554
11030
|
}).parse(record);
|
|
@@ -10561,46 +11037,46 @@ async function runSeoCommand(options) {
|
|
|
10561
11037
|
notificationRoute.parse(record.route);
|
|
10562
11038
|
}
|
|
10563
11039
|
} else if (!writing) {
|
|
10564
|
-
const validated =
|
|
10565
|
-
resource:
|
|
10566
|
-
id:
|
|
10567
|
-
organizationId:
|
|
11040
|
+
const validated = z23.object({
|
|
11041
|
+
resource: z23.object({
|
|
11042
|
+
id: z23.literal(context.resourceId),
|
|
11043
|
+
organizationId: z23.literal(context.overview.project.organizationId)
|
|
10568
11044
|
}),
|
|
10569
|
-
audits:
|
|
10570
|
-
audit:
|
|
10571
|
-
id:
|
|
10572
|
-
resourceId:
|
|
11045
|
+
audits: z23.array(z23.object({ id: z23.string() }).passthrough()),
|
|
11046
|
+
audit: z23.object({
|
|
11047
|
+
id: z23.string(),
|
|
11048
|
+
resourceId: z23.literal(context.resourceId)
|
|
10573
11049
|
}).passthrough().nullable(),
|
|
10574
|
-
pages:
|
|
10575
|
-
issues:
|
|
10576
|
-
changes:
|
|
10577
|
-
totalChanges:
|
|
10578
|
-
dispositions:
|
|
11050
|
+
pages: z23.array(z23.unknown()),
|
|
11051
|
+
issues: z23.array(z23.unknown()),
|
|
11052
|
+
changes: z23.array(z23.unknown()),
|
|
11053
|
+
totalChanges: z23.number(),
|
|
11054
|
+
dispositions: z23.array(z23.unknown())
|
|
10579
11055
|
}).passthrough().parse(record);
|
|
10580
11056
|
const selected = query.get("audit");
|
|
10581
11057
|
if (selected && validated.audit?.id !== selected)
|
|
10582
11058
|
throw new Error("The SEO response does not match the requested audit.");
|
|
10583
11059
|
} else if (record.audit)
|
|
10584
|
-
|
|
10585
|
-
id:
|
|
10586
|
-
resourceId:
|
|
10587
|
-
organizationId:
|
|
10588
|
-
state:
|
|
11060
|
+
z23.object({
|
|
11061
|
+
id: z23.string(),
|
|
11062
|
+
resourceId: z23.literal(context.resourceId),
|
|
11063
|
+
organizationId: z23.literal(context.overview.project.organizationId),
|
|
11064
|
+
state: z23.literal("queued")
|
|
10589
11065
|
}).parse(record.audit);
|
|
10590
|
-
else if (route === "audit cancel")
|
|
11066
|
+
else if (route === "audit cancel") z23.literal(true).parse(record.cancelled);
|
|
10591
11067
|
else if (action === "issue")
|
|
10592
|
-
|
|
10593
|
-
url:
|
|
10594
|
-
ruleId:
|
|
10595
|
-
ignored:
|
|
10596
|
-
revision:
|
|
11068
|
+
z23.object({
|
|
11069
|
+
url: z23.literal(values.url),
|
|
11070
|
+
ruleId: z23.literal(values.rule),
|
|
11071
|
+
ignored: z23.literal(positionals[1] === "ignore"),
|
|
11072
|
+
revision: z23.literal(Number(values.revision) + 1)
|
|
10597
11073
|
}).parse(record.disposition);
|
|
10598
11074
|
else throw new Error("The SEO service returned an invalid response.");
|
|
10599
11075
|
return { exitCode: 0, stdout: JSON.stringify(record, null, 2) };
|
|
10600
11076
|
} catch (cause) {
|
|
10601
11077
|
const error = {
|
|
10602
11078
|
code: cause instanceof SiteOSAuthApiError ? cause.code : "SEO_COMMAND_FAILED",
|
|
10603
|
-
message: cause instanceof
|
|
11079
|
+
message: cause instanceof z23.ZodError ? "The SEO service returned an invalid response." : cause instanceof Error ? cause.message : "The SEO command failed."
|
|
10604
11080
|
};
|
|
10605
11081
|
return {
|
|
10606
11082
|
exitCode: cause instanceof SiteOSAuthApiError ? 1 : 2,
|