@canonry/canonry 7.46.0 → 7.46.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-2UQ6WVWP.js → chunk-7R2HFAG4.js} +5 -5
- package/dist/{chunk-ZOTA3MOL.js → chunk-IBRZ23VQ.js} +225 -105
- package/dist/{chunk-AYMS4LCP.js → chunk-OO64BWNB.js} +3 -3
- package/dist/{chunk-KPOZR6MV.js → chunk-QWLKYIQ4.js} +1 -1
- package/dist/{cli-commands-GYWXFE3P.js → cli-commands-JEN27DWN.js} +4 -4
- package/dist/cli.js +4 -4
- package/dist/{demo-server-7YFRG3JM.js → demo-server-5TX4MJIS.js} +2 -2
- package/dist/index.js +3 -3
- package/dist/{intelligence-service-P3UJNCVS.js → intelligence-service-5BYJSFLW.js} +1 -1
- package/package.json +14 -14
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
trackFeatureCompleted,
|
|
32
32
|
trackIntegrationConnection,
|
|
33
33
|
waitForServerRuntimeStartup
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-OO64BWNB.js";
|
|
35
35
|
import {
|
|
36
36
|
clearDashboardPassword,
|
|
37
37
|
configExists,
|
|
@@ -122,7 +122,7 @@ import {
|
|
|
122
122
|
rawEventSamples,
|
|
123
123
|
readMarketCompetitorNames,
|
|
124
124
|
runs
|
|
125
|
-
} from "./chunk-
|
|
125
|
+
} from "./chunk-IBRZ23VQ.js";
|
|
126
126
|
import {
|
|
127
127
|
AdsDeliverySnapshotStatuses,
|
|
128
128
|
AdsHistoricalCampaignRollupStatuses,
|
|
@@ -586,7 +586,7 @@ function parseDemoTrustedProxies(values) {
|
|
|
586
586
|
async function demoCommand(options) {
|
|
587
587
|
const { host, port } = parseDemoListenOptions(options);
|
|
588
588
|
const trustProxy = parseDemoTrustedProxies(options.trustProxy);
|
|
589
|
-
const { createDemoServer } = await import("./demo-server-
|
|
589
|
+
const { createDemoServer } = await import("./demo-server-5TX4MJIS.js");
|
|
590
590
|
const app = await createDemoServer({ trustProxy });
|
|
591
591
|
let stopping = false;
|
|
592
592
|
const stop = () => {
|
|
@@ -1000,7 +1000,7 @@ No DB writes performed. Re-run without --dry-run to apply.`);
|
|
|
1000
1000
|
}
|
|
1001
1001
|
}
|
|
1002
1002
|
async function backfillInsightsCommand(project2, opts) {
|
|
1003
|
-
const { IntelligenceService } = await import("./intelligence-service-
|
|
1003
|
+
const { IntelligenceService } = await import("./intelligence-service-5BYJSFLW.js");
|
|
1004
1004
|
const config = loadConfig();
|
|
1005
1005
|
const db = createClient(config.database);
|
|
1006
1006
|
migrate(db);
|
|
@@ -19378,7 +19378,7 @@ var SYSTEM_CLI_COMMANDS = [
|
|
|
19378
19378
|
options: { access: stringOption() },
|
|
19379
19379
|
run: async (input) => {
|
|
19380
19380
|
if (input.positionals.length > 1) throw usageError("Expected at most one command group");
|
|
19381
|
-
const [{ printCliCatalog }, { REGISTERED_CLI_COMMANDS: REGISTERED_CLI_COMMANDS2 }] = await Promise.all([import("./cli-catalog-TFCCKIE4.js"), import("./cli-commands-
|
|
19381
|
+
const [{ printCliCatalog }, { REGISTERED_CLI_COMMANDS: REGISTERED_CLI_COMMANDS2 }] = await Promise.all([import("./cli-catalog-TFCCKIE4.js"), import("./cli-commands-JEN27DWN.js")]);
|
|
19382
19382
|
printCliCatalog(REGISTERED_CLI_COMMANDS2, input.format, input.positionals[0], getString(input.values, "access"));
|
|
19383
19383
|
}
|
|
19384
19384
|
},
|
|
@@ -888,6 +888,7 @@ import {
|
|
|
888
888
|
unsupportedKind,
|
|
889
889
|
upgradeCaveatFor,
|
|
890
890
|
usableBrandAliases,
|
|
891
|
+
usageSurfaceSchema,
|
|
891
892
|
userDtoSchema,
|
|
892
893
|
userListDtoSchema,
|
|
893
894
|
validationError,
|
|
@@ -46517,6 +46518,7 @@ var routeCatalog = [
|
|
|
46517
46518
|
// TODO: Add `GaConnectResponse` Zod schema in contracts.
|
|
46518
46519
|
200: rawJsonResponse("GA4 connection established.", looseObjectSchema),
|
|
46519
46520
|
400: errorResponse("Invalid GA4 connection request."),
|
|
46521
|
+
403: errorResponse("Google refused the property (`details.reason` is `ga4-permission-missing`). Retrying will not help until access is granted; repeated refusals are answered without asking Google again until `details.nextCheckAt`."),
|
|
46520
46522
|
404: errorResponse("Project not found.")
|
|
46521
46523
|
}
|
|
46522
46524
|
},
|
|
@@ -60100,11 +60102,76 @@ async function adsRoutes(app, opts) {
|
|
|
60100
60102
|
}
|
|
60101
60103
|
|
|
60102
60104
|
// ../api-routes/src/bing.ts
|
|
60103
|
-
import
|
|
60105
|
+
import crypto43 from "crypto";
|
|
60104
60106
|
import { eq as eq66, and as and54, desc as desc32 } from "drizzle-orm";
|
|
60105
60107
|
|
|
60106
|
-
// ../
|
|
60108
|
+
// ../api-routes/src/connection-verify-cache.ts
|
|
60107
60109
|
import crypto41 from "crypto";
|
|
60110
|
+
var CONNECTION_VERIFY_TTL_MS = 15 * 60 * 1e3;
|
|
60111
|
+
var REFUSALS_BEFORE_BACKOFF = 3;
|
|
60112
|
+
var FIRST_BACKOFF_MS = 5 * 60 * 1e3;
|
|
60113
|
+
var MAX_BACKOFF_MS = 60 * 60 * 1e3;
|
|
60114
|
+
var MAX_ENTRIES = 500;
|
|
60115
|
+
function remember(entries, key, value) {
|
|
60116
|
+
entries.delete(key);
|
|
60117
|
+
entries.set(key, value);
|
|
60118
|
+
if (entries.size > MAX_ENTRIES) entries.delete(entries.keys().next().value);
|
|
60119
|
+
}
|
|
60120
|
+
function isInteractiveCaller() {
|
|
60121
|
+
const attribution = currentOutcomeAttribution();
|
|
60122
|
+
const label = usageSurfaceSchema.safeParse(attribution?.surfaceLabel);
|
|
60123
|
+
if (label.success) return label.data === "cli" || label.data === "dashboard";
|
|
60124
|
+
const ua = attribution?.userAgent ?? "";
|
|
60125
|
+
return ua.startsWith("canonry-cli") || ua.startsWith("Mozilla/");
|
|
60126
|
+
}
|
|
60127
|
+
function verifyCacheKey(...parts) {
|
|
60128
|
+
return crypto41.createHash("sha256").update(parts.join("\0")).digest("hex");
|
|
60129
|
+
}
|
|
60130
|
+
function createVerifyCache(ttlMs = CONNECTION_VERIFY_TTL_MS) {
|
|
60131
|
+
const entries = /* @__PURE__ */ new Map();
|
|
60132
|
+
return {
|
|
60133
|
+
get(key) {
|
|
60134
|
+
const entry = entries.get(key);
|
|
60135
|
+
if (!entry) return void 0;
|
|
60136
|
+
if (Date.now() - entry.at < ttlMs) return entry.value;
|
|
60137
|
+
entries.delete(key);
|
|
60138
|
+
return void 0;
|
|
60139
|
+
},
|
|
60140
|
+
set(key, value) {
|
|
60141
|
+
remember(entries, key, { value, at: Date.now() });
|
|
60142
|
+
}
|
|
60143
|
+
};
|
|
60144
|
+
}
|
|
60145
|
+
function createRefusalBackoff() {
|
|
60146
|
+
const entries = /* @__PURE__ */ new Map();
|
|
60147
|
+
return {
|
|
60148
|
+
/** The refusal to answer with instead of asking the upstream, while this key is backing off. */
|
|
60149
|
+
replay(key) {
|
|
60150
|
+
const entry = entries.get(key);
|
|
60151
|
+
if (!entry || entry.count < REFUSALS_BEFORE_BACKOFF) return void 0;
|
|
60152
|
+
const wait = Math.min(FIRST_BACKOFF_MS * 2 ** (entry.count - REFUSALS_BEFORE_BACKOFF), MAX_BACKOFF_MS);
|
|
60153
|
+
const nextCheckAt = entry.at + wait;
|
|
60154
|
+
if (Date.now() >= nextCheckAt) return void 0;
|
|
60155
|
+
const { error } = entry;
|
|
60156
|
+
return {
|
|
60157
|
+
error: new AppError(error.code, error.message, error.statusCode, {
|
|
60158
|
+
...error.details,
|
|
60159
|
+
nextCheckAt: new Date(nextCheckAt).toISOString()
|
|
60160
|
+
}),
|
|
60161
|
+
cause: entry.cause
|
|
60162
|
+
};
|
|
60163
|
+
},
|
|
60164
|
+
refused(key, error, cause) {
|
|
60165
|
+
remember(entries, key, { count: (entries.get(key)?.count ?? 0) + 1, at: Date.now(), error, cause });
|
|
60166
|
+
},
|
|
60167
|
+
cleared(key) {
|
|
60168
|
+
entries.delete(key);
|
|
60169
|
+
}
|
|
60170
|
+
};
|
|
60171
|
+
}
|
|
60172
|
+
|
|
60173
|
+
// ../integration-bing/src/bing-client.ts
|
|
60174
|
+
import crypto42 from "crypto";
|
|
60108
60175
|
|
|
60109
60176
|
// ../integration-bing/src/constants.ts
|
|
60110
60177
|
var BING_WMT_API_BASE = "https://ssl.bing.com/webmaster/api.svc/json";
|
|
@@ -60259,7 +60326,7 @@ async function bingFetchOnce(apiKey, endpoint, opts) {
|
|
|
60259
60326
|
var throttleCooldownUntil = /* @__PURE__ */ new Map();
|
|
60260
60327
|
var cooldownNow = () => Date.now();
|
|
60261
60328
|
function cooldownKey(apiKey) {
|
|
60262
|
-
return
|
|
60329
|
+
return crypto42.createHash("sha256").update(apiKey).digest("hex").slice(0, 12);
|
|
60263
60330
|
}
|
|
60264
60331
|
function throttleCooldownRemainingMs(apiKey) {
|
|
60265
60332
|
const until = throttleCooldownUntil.get(cooldownKey(apiKey));
|
|
@@ -60403,6 +60470,7 @@ async function loadBlockingCrawlIssues(apiKey, siteUrl, domain) {
|
|
|
60403
60470
|
return blockedUrls;
|
|
60404
60471
|
}
|
|
60405
60472
|
async function bingRoutes(app, opts) {
|
|
60473
|
+
const keyChecks = createVerifyCache();
|
|
60406
60474
|
function requireConnectionStore() {
|
|
60407
60475
|
if (opts.bingConnectionStore) return opts.bingConnectionStore;
|
|
60408
60476
|
throw validationError("Bing connection storage is not configured for this deployment");
|
|
@@ -60421,16 +60489,21 @@ async function bingRoutes(app, opts) {
|
|
|
60421
60489
|
throw validationError("apiKey is required");
|
|
60422
60490
|
}
|
|
60423
60491
|
const project = resolveProject(app.db, request.params.name);
|
|
60424
|
-
|
|
60425
|
-
|
|
60426
|
-
|
|
60427
|
-
|
|
60428
|
-
|
|
60429
|
-
|
|
60430
|
-
|
|
60431
|
-
|
|
60432
|
-
|
|
60492
|
+
const keyCheck = verifyCacheKey("bing-key", apiKey);
|
|
60493
|
+
let sites = isInteractiveCaller() ? void 0 : keyChecks.get(keyCheck);
|
|
60494
|
+
if (!sites) {
|
|
60495
|
+
try {
|
|
60496
|
+
sites = await getSites(apiKey);
|
|
60497
|
+
keyChecks.set(keyCheck, sites);
|
|
60498
|
+
bingLog("info", "connect.verify-key", { domain: project.canonicalDomain, siteCount: sites.length });
|
|
60499
|
+
} catch (e) {
|
|
60500
|
+
const msg = describeError(e);
|
|
60501
|
+
bingLog("error", "connect.verify-key-failed", { domain: project.canonicalDomain, error: msg });
|
|
60502
|
+
attempt.failed(e);
|
|
60503
|
+
throw validationError(`Failed to verify Bing API key: ${msg}`);
|
|
60504
|
+
}
|
|
60433
60505
|
}
|
|
60506
|
+
const availableSites = sites.map((s) => ({ url: s.Url, verified: s.IsVerified ?? false }));
|
|
60434
60507
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
60435
60508
|
const existing = store.getConnection(project.canonicalDomain);
|
|
60436
60509
|
if (existing) attempt.update({ action: "reauth" });
|
|
@@ -60440,6 +60513,9 @@ async function bingRoutes(app, opts) {
|
|
|
60440
60513
|
`This domain already has a Bing connection owned by another project. Disconnect it from that project first before re-connecting from "${project.name}".`
|
|
60441
60514
|
);
|
|
60442
60515
|
}
|
|
60516
|
+
if (existing?.apiKey === apiKey && existing.createdByProjectId) {
|
|
60517
|
+
return { connected: true, domain: project.canonicalDomain, siteUrl: existing.siteUrl ?? null, availableSites };
|
|
60518
|
+
}
|
|
60443
60519
|
store.upsertConnection({
|
|
60444
60520
|
domain: project.canonicalDomain,
|
|
60445
60521
|
apiKey,
|
|
@@ -60459,7 +60535,7 @@ async function bingRoutes(app, opts) {
|
|
|
60459
60535
|
connected: true,
|
|
60460
60536
|
domain: project.canonicalDomain,
|
|
60461
60537
|
siteUrl: existing?.siteUrl ?? null,
|
|
60462
|
-
availableSites
|
|
60538
|
+
availableSites
|
|
60463
60539
|
};
|
|
60464
60540
|
}));
|
|
60465
60541
|
app.delete("/projects/:name/bing/disconnect", connectionRoute(app, () => ({ integration: "bing", action: "disconnect" }), async (request, reply, attempt) => {
|
|
@@ -60589,7 +60665,7 @@ async function bingRoutes(app, opts) {
|
|
|
60589
60665
|
const snapshotDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
60590
60666
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
60591
60667
|
app.db.insert(bingCoverageSnapshots).values({
|
|
60592
|
-
id:
|
|
60668
|
+
id: crypto43.randomUUID(),
|
|
60593
60669
|
projectId: project.id,
|
|
60594
60670
|
syncRunId: snapshotRunId,
|
|
60595
60671
|
date: snapshotDate,
|
|
@@ -60664,7 +60740,7 @@ async function bingRoutes(app, opts) {
|
|
|
60664
60740
|
throw validationError("url is required");
|
|
60665
60741
|
}
|
|
60666
60742
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
60667
|
-
const runId =
|
|
60743
|
+
const runId = crypto43.randomUUID();
|
|
60668
60744
|
app.db.insert(runs).values({
|
|
60669
60745
|
id: runId,
|
|
60670
60746
|
projectId: project.id,
|
|
@@ -60685,7 +60761,7 @@ async function bingRoutes(app, opts) {
|
|
|
60685
60761
|
discoveryDate: result.DiscoveryDate ?? null
|
|
60686
60762
|
});
|
|
60687
60763
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
60688
|
-
const id =
|
|
60764
|
+
const id = crypto43.randomUUID();
|
|
60689
60765
|
const httpCode = result.HttpStatus ?? result.HttpCode ?? null;
|
|
60690
60766
|
const lastCrawledDate = parseBingDate(result.LastCrawledDate);
|
|
60691
60767
|
const inIndexDate = parseBingDate(result.InIndexDate);
|
|
@@ -60762,7 +60838,7 @@ async function bingRoutes(app, opts) {
|
|
|
60762
60838
|
throw validationError('No Bing site configured. Run "canonry bing set-site <project> <url>" first.');
|
|
60763
60839
|
}
|
|
60764
60840
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
60765
|
-
const runId =
|
|
60841
|
+
const runId = crypto43.randomUUID();
|
|
60766
60842
|
app.db.insert(runs).values({
|
|
60767
60843
|
id: runId,
|
|
60768
60844
|
projectId: project.id,
|
|
@@ -61060,7 +61136,7 @@ async function cdpRoutes(app, opts) {
|
|
|
61060
61136
|
}
|
|
61061
61137
|
|
|
61062
61138
|
// ../api-routes/src/ga.ts
|
|
61063
|
-
import
|
|
61139
|
+
import crypto45 from "crypto";
|
|
61064
61140
|
import { eq as eq69, desc as desc34, and as and57, sql as sql27 } from "drizzle-orm";
|
|
61065
61141
|
|
|
61066
61142
|
// ../api-routes/src/ga-session-history.ts
|
|
@@ -61120,14 +61196,14 @@ function findBiggestMover(current, prior) {
|
|
|
61120
61196
|
}
|
|
61121
61197
|
|
|
61122
61198
|
// ../api-routes/src/ga-search-landing-pages.ts
|
|
61123
|
-
import
|
|
61199
|
+
import crypto44 from "crypto";
|
|
61124
61200
|
import { and as and56, asc as asc13, count as count4, desc as desc33, eq as eq68 } from "drizzle-orm";
|
|
61125
61201
|
function persistSearchLandingSnapshot(db, input) {
|
|
61126
61202
|
db.transaction((tx) => {
|
|
61127
61203
|
deleteSearchLandingRows(tx, input.projectId);
|
|
61128
61204
|
for (const window of input.windows) {
|
|
61129
61205
|
tx.insert(gaSearchLandingWindows).values({
|
|
61130
|
-
id:
|
|
61206
|
+
id: crypto44.randomUUID(),
|
|
61131
61207
|
projectId: input.projectId,
|
|
61132
61208
|
propertyId: input.propertyId,
|
|
61133
61209
|
windowKey: window.window,
|
|
@@ -61149,7 +61225,7 @@ function persistSearchLandingSnapshot(db, input) {
|
|
|
61149
61225
|
}).run();
|
|
61150
61226
|
for (const row of window.rows) {
|
|
61151
61227
|
tx.insert(gaSearchLandingPages).values({
|
|
61152
|
-
id:
|
|
61228
|
+
id: crypto44.randomUUID(),
|
|
61153
61229
|
projectId: input.projectId,
|
|
61154
61230
|
windowKey: window.window,
|
|
61155
61231
|
landingPage: row.landingPage,
|
|
@@ -61381,7 +61457,7 @@ function persistAcquisitionMeasurement(db, input) {
|
|
|
61381
61457
|
)).run();
|
|
61382
61458
|
for (const row of input.report.rows) {
|
|
61383
61459
|
tx.insert(gaAcquisitionDaily).values({
|
|
61384
|
-
id:
|
|
61460
|
+
id: crypto45.randomUUID(),
|
|
61385
61461
|
projectId: input.projectId,
|
|
61386
61462
|
date: row.date,
|
|
61387
61463
|
channelGroup: row.channelGroup,
|
|
@@ -61423,7 +61499,7 @@ function persistLeadMeasurement(db, input) {
|
|
|
61423
61499
|
)).run();
|
|
61424
61500
|
for (const row of input.report.rows) {
|
|
61425
61501
|
tx.insert(gaLeadEventsDaily).values({
|
|
61426
|
-
id:
|
|
61502
|
+
id: crypto45.randomUUID(),
|
|
61427
61503
|
projectId: input.projectId,
|
|
61428
61504
|
date: row.date,
|
|
61429
61505
|
eventName: row.eventName,
|
|
@@ -61533,6 +61609,18 @@ function ga4ErrorToAppError(err, context) {
|
|
|
61533
61609
|
}
|
|
61534
61610
|
return providerError(`${context}: ${describeError(err)}`);
|
|
61535
61611
|
}
|
|
61612
|
+
function ga4ConnectError(err, propertyId, principal, context) {
|
|
61613
|
+
if (err instanceof GA4ApiError && err.status === 403) {
|
|
61614
|
+
return forbidden(
|
|
61615
|
+
`Google refused access to GA4 property ${propertyId} for ${principal}. Add ${principal} as a Viewer in Google Analytics (Admin > Property access management), then connect again. Retrying will not help until access is granted.`,
|
|
61616
|
+
{ reason: "ga4-permission-missing", propertyId, retryable: false }
|
|
61617
|
+
);
|
|
61618
|
+
}
|
|
61619
|
+
return validationError(`${context}: ${describeError(err)}`);
|
|
61620
|
+
}
|
|
61621
|
+
function isPermissionRefusal(error) {
|
|
61622
|
+
return error.details?.reason === "ga4-permission-missing";
|
|
61623
|
+
}
|
|
61536
61624
|
async function resolveGa4AccessToken(opts, projectName, canonicalDomain) {
|
|
61537
61625
|
const saConn = opts.ga4CredentialStore?.getConnection(projectName);
|
|
61538
61626
|
if (saConn?.clientEmail && saConn?.privateKey && saConn?.propertyId) {
|
|
@@ -61583,6 +61671,25 @@ function requireGa4Connection(opts, projectName, canonicalDomain) {
|
|
|
61583
61671
|
}
|
|
61584
61672
|
}
|
|
61585
61673
|
async function ga4Routes(app, opts) {
|
|
61674
|
+
const connectPasses = createVerifyCache();
|
|
61675
|
+
const connectRefusals = createRefusalBackoff();
|
|
61676
|
+
async function checkGa4Access(key, verify, toError) {
|
|
61677
|
+
if (!isInteractiveCaller()) {
|
|
61678
|
+
if (connectPasses.get(key)) return { ok: true };
|
|
61679
|
+
const replay = connectRefusals.replay(key);
|
|
61680
|
+
if (replay) return { ok: false, ...replay };
|
|
61681
|
+
}
|
|
61682
|
+
try {
|
|
61683
|
+
await verify();
|
|
61684
|
+
connectPasses.set(key, true);
|
|
61685
|
+
connectRefusals.cleared(key);
|
|
61686
|
+
return { ok: true };
|
|
61687
|
+
} catch (cause) {
|
|
61688
|
+
const error = toError(cause);
|
|
61689
|
+
if (isPermissionRefusal(error)) connectRefusals.refused(key, error, cause);
|
|
61690
|
+
return { ok: false, error, cause };
|
|
61691
|
+
}
|
|
61692
|
+
}
|
|
61586
61693
|
app.post("/projects/:name/ga/connect", connectionRoute(app, (request) => {
|
|
61587
61694
|
const body = request.body;
|
|
61588
61695
|
return { integration: "ga4", action: typeof body?.keyJson === "string" && body.keyJson ? "connect" : "select" };
|
|
@@ -61607,18 +61714,23 @@ async function ga4Routes(app, opts) {
|
|
|
61607
61714
|
}
|
|
61608
61715
|
const clientEmail = parsed.client_email;
|
|
61609
61716
|
const privateKey = parsed.private_key;
|
|
61610
|
-
|
|
61611
|
-
|
|
61612
|
-
|
|
61613
|
-
|
|
61614
|
-
|
|
61615
|
-
|
|
61616
|
-
|
|
61617
|
-
|
|
61717
|
+
const check3 = await checkGa4Access(
|
|
61718
|
+
verifyCacheKey("ga4-service-account", project.id, propertyId, clientEmail, privateKey),
|
|
61719
|
+
() => verifyConnection(clientEmail, privateKey, propertyId),
|
|
61720
|
+
(err) => ga4ConnectError(err, propertyId, clientEmail, "Failed to verify GA4 credentials")
|
|
61721
|
+
);
|
|
61722
|
+
if (!check3.ok) {
|
|
61723
|
+
gaLog("error", "connect.verify-failed", { projectId: project.id, propertyId, error: check3.error.message });
|
|
61724
|
+
attempt.failed(check3.cause);
|
|
61725
|
+
throw check3.error;
|
|
61618
61726
|
}
|
|
61727
|
+
gaLog("info", "connect.verified.service-account", { projectId: project.id, propertyId });
|
|
61619
61728
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
61620
61729
|
const existing = opts.ga4CredentialStore.getConnection(project.name);
|
|
61621
61730
|
if (existing) attempt.update({ action: "reauth" });
|
|
61731
|
+
if (existing?.clientEmail === clientEmail && existing.privateKey === privateKey && existing.propertyId === propertyId) {
|
|
61732
|
+
return { connected: true, propertyId, authMethod: "service-account", clientEmail };
|
|
61733
|
+
}
|
|
61622
61734
|
const previousPropertyId2 = resolveCurrentGa4PropertyId(opts, project.name, project.canonicalDomain);
|
|
61623
61735
|
opts.ga4CredentialStore.upsertConnection({
|
|
61624
61736
|
projectName: project.name,
|
|
@@ -61655,19 +61767,27 @@ async function ga4Routes(app, opts) {
|
|
|
61655
61767
|
'No GA4 OAuth token found. Run "canonry google connect <project> --type ga4" first, or pass --key-file to use a service account.'
|
|
61656
61768
|
);
|
|
61657
61769
|
}
|
|
61658
|
-
const accessToken
|
|
61659
|
-
|
|
61660
|
-
|
|
61661
|
-
|
|
61662
|
-
|
|
61663
|
-
|
|
61664
|
-
|
|
61665
|
-
|
|
61666
|
-
|
|
61667
|
-
|
|
61668
|
-
|
|
61669
|
-
|
|
61670
|
-
|
|
61770
|
+
const { accessToken: storedAccessToken, refreshToken } = oauthConn;
|
|
61771
|
+
const check2 = await checkGa4Access(
|
|
61772
|
+
verifyCacheKey("ga4-oauth", project.id, propertyId, refreshToken),
|
|
61773
|
+
async () => {
|
|
61774
|
+
const accessToken = await refreshOAuthTokenIfNeeded(googleStore, authConfig, project.canonicalDomain, {
|
|
61775
|
+
accessToken: storedAccessToken,
|
|
61776
|
+
refreshToken,
|
|
61777
|
+
tokenExpiresAt: oauthConn.tokenExpiresAt
|
|
61778
|
+
});
|
|
61779
|
+
return verifyConnectionWithToken(accessToken, propertyId);
|
|
61780
|
+
},
|
|
61781
|
+
(err) => err instanceof AppError ? err : ga4ConnectError(err, propertyId, "the connected Google account", "Failed to verify GA4 access")
|
|
61782
|
+
);
|
|
61783
|
+
if (!check2.ok) {
|
|
61784
|
+
gaLog("error", "connect.verify-failed.oauth", { projectId: project.id, propertyId, error: check2.error.message });
|
|
61785
|
+
attempt.failed(check2.cause);
|
|
61786
|
+
throw check2.error;
|
|
61787
|
+
}
|
|
61788
|
+
gaLog("info", "connect.verified.oauth", { projectId: project.id, propertyId });
|
|
61789
|
+
if (oauthConn.propertyId === propertyId) {
|
|
61790
|
+
return { connected: true, propertyId, authMethod: "oauth" };
|
|
61671
61791
|
}
|
|
61672
61792
|
const previousPropertyId = resolveCurrentGa4PropertyId(opts, project.name, project.canonicalDomain);
|
|
61673
61793
|
const connectedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -61777,7 +61897,7 @@ async function ga4Routes(app, opts) {
|
|
|
61777
61897
|
const leadDays = measurementState?.leadSyncedAt != null ? days : GA4_MAX_SYNC_DAYS;
|
|
61778
61898
|
const leadEventNames = project.measurement.leadEventNames;
|
|
61779
61899
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
61780
|
-
const runId =
|
|
61900
|
+
const runId = crypto45.randomUUID();
|
|
61781
61901
|
app.db.insert(runs).values({
|
|
61782
61902
|
id: runId,
|
|
61783
61903
|
projectId: project.id,
|
|
@@ -61837,7 +61957,7 @@ async function ga4Routes(app, opts) {
|
|
|
61837
61957
|
).run();
|
|
61838
61958
|
for (const row of rows) {
|
|
61839
61959
|
tx.insert(gaTrafficSnapshots).values({
|
|
61840
|
-
id:
|
|
61960
|
+
id: crypto45.randomUUID(),
|
|
61841
61961
|
projectId: project.id,
|
|
61842
61962
|
date: row.date,
|
|
61843
61963
|
landingPage: row.landingPage,
|
|
@@ -61860,7 +61980,7 @@ async function ga4Routes(app, opts) {
|
|
|
61860
61980
|
).run();
|
|
61861
61981
|
for (const row of dailyTotals) {
|
|
61862
61982
|
tx.insert(gaDailyTotals).values({
|
|
61863
|
-
id:
|
|
61983
|
+
id: crypto45.randomUUID(),
|
|
61864
61984
|
projectId: project.id,
|
|
61865
61985
|
date: row.date,
|
|
61866
61986
|
sessions: row.sessions,
|
|
@@ -61886,7 +62006,7 @@ async function ga4Routes(app, opts) {
|
|
|
61886
62006
|
).run();
|
|
61887
62007
|
for (const row of aiReferrals) {
|
|
61888
62008
|
tx.insert(gaAiReferrals).values({
|
|
61889
|
-
id:
|
|
62009
|
+
id: crypto45.randomUUID(),
|
|
61890
62010
|
projectId: project.id,
|
|
61891
62011
|
date: row.date,
|
|
61892
62012
|
source: row.source,
|
|
@@ -61918,7 +62038,7 @@ async function ga4Routes(app, opts) {
|
|
|
61918
62038
|
).run();
|
|
61919
62039
|
for (const row of socialReferrals) {
|
|
61920
62040
|
tx.insert(gaSocialReferrals).values({
|
|
61921
|
-
id:
|
|
62041
|
+
id: crypto45.randomUUID(),
|
|
61922
62042
|
projectId: project.id,
|
|
61923
62043
|
date: row.date,
|
|
61924
62044
|
source: row.source,
|
|
@@ -61934,7 +62054,7 @@ async function ga4Routes(app, opts) {
|
|
|
61934
62054
|
if (syncSummary) {
|
|
61935
62055
|
tx.delete(gaTrafficSummaries).where(eq69(gaTrafficSummaries.projectId, project.id)).run();
|
|
61936
62056
|
tx.insert(gaTrafficSummaries).values({
|
|
61937
|
-
id:
|
|
62057
|
+
id: crypto45.randomUUID(),
|
|
61938
62058
|
projectId: project.id,
|
|
61939
62059
|
periodStart: summary3.periodStart,
|
|
61940
62060
|
periodEnd: summary3.periodEnd,
|
|
@@ -61947,7 +62067,7 @@ async function ga4Routes(app, opts) {
|
|
|
61947
62067
|
tx.delete(gaTrafficWindowSummaries).where(eq69(gaTrafficWindowSummaries.projectId, project.id)).run();
|
|
61948
62068
|
for (const ws of windowSummaries) {
|
|
61949
62069
|
tx.insert(gaTrafficWindowSummaries).values({
|
|
61950
|
-
id:
|
|
62070
|
+
id: crypto45.randomUUID(),
|
|
61951
62071
|
projectId: project.id,
|
|
61952
62072
|
windowKey: ws.windowKey,
|
|
61953
62073
|
periodStart: ws.periodStart,
|
|
@@ -62744,7 +62864,7 @@ function parseSchemaPageEntry(entry) {
|
|
|
62744
62864
|
}
|
|
62745
62865
|
|
|
62746
62866
|
// ../integration-wordpress/src/wordpress-client.ts
|
|
62747
|
-
import
|
|
62867
|
+
import crypto46 from "crypto";
|
|
62748
62868
|
function validateUsername(username) {
|
|
62749
62869
|
if (!username || typeof username !== "string" || username.trim().length === 0) {
|
|
62750
62870
|
throw new WordpressApiError("AUTH_INVALID", "Username is required and must be a non-empty string", 400);
|
|
@@ -62964,7 +63084,7 @@ function buildSnippet(content) {
|
|
|
62964
63084
|
return `${text2.slice(0, 157)}...`;
|
|
62965
63085
|
}
|
|
62966
63086
|
function contentHash(content) {
|
|
62967
|
-
return
|
|
63087
|
+
return crypto46.createHash("sha256").update(content).digest("hex");
|
|
62968
63088
|
}
|
|
62969
63089
|
function buildAmbiguousSlugMessage(slug, pages) {
|
|
62970
63090
|
const candidates = pages.map((page3) => {
|
|
@@ -64457,7 +64577,7 @@ async function wordpressRoutes(app, opts) {
|
|
|
64457
64577
|
}
|
|
64458
64578
|
|
|
64459
64579
|
// ../api-routes/src/backlinks.ts
|
|
64460
|
-
import
|
|
64580
|
+
import crypto47 from "crypto";
|
|
64461
64581
|
import { and as and61, asc as asc15, desc as desc36, eq as eq72, sql as sql29 } from "drizzle-orm";
|
|
64462
64582
|
|
|
64463
64583
|
// ../integration-commoncrawl/src/constants.ts
|
|
@@ -65110,7 +65230,7 @@ async function backlinksRoutes(app, opts) {
|
|
|
65110
65230
|
const refreshed = app.db.select().from(ccReleaseSyncs).where(eq72(ccReleaseSyncs.id, existing.id)).get();
|
|
65111
65231
|
return reply.status(200).send(mapSyncRow(refreshed));
|
|
65112
65232
|
}
|
|
65113
|
-
const id =
|
|
65233
|
+
const id = crypto47.randomUUID();
|
|
65114
65234
|
app.db.insert(ccReleaseSyncs).values({
|
|
65115
65235
|
id,
|
|
65116
65236
|
release,
|
|
@@ -65167,7 +65287,7 @@ async function backlinksRoutes(app, opts) {
|
|
|
65167
65287
|
throw validationError("Invalid release id");
|
|
65168
65288
|
}
|
|
65169
65289
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
65170
|
-
const runId =
|
|
65290
|
+
const runId = crypto47.randomUUID();
|
|
65171
65291
|
app.db.insert(runs).values({
|
|
65172
65292
|
id: runId,
|
|
65173
65293
|
projectId: project.id,
|
|
@@ -65256,7 +65376,7 @@ async function backlinksRoutes(app, opts) {
|
|
|
65256
65376
|
}
|
|
65257
65377
|
|
|
65258
65378
|
// ../api-routes/src/traffic.ts
|
|
65259
|
-
import
|
|
65379
|
+
import crypto50 from "crypto";
|
|
65260
65380
|
import { isIP } from "net";
|
|
65261
65381
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
65262
65382
|
import { and as and65, desc as desc37, eq as eq76, gte as gte18, inArray as inArray31, lte as lte20, sql as sql32 } from "drizzle-orm";
|
|
@@ -65284,7 +65404,7 @@ function resolveVercelSyncDeadlineMs(env = process.env) {
|
|
|
65284
65404
|
}
|
|
65285
65405
|
|
|
65286
65406
|
// ../integration-cloud-run/src/auth.ts
|
|
65287
|
-
import
|
|
65407
|
+
import crypto48 from "crypto";
|
|
65288
65408
|
var GOOGLE_TOKEN_URL3 = "https://oauth2.googleapis.com/token";
|
|
65289
65409
|
var CLOUD_LOGGING_READ_SCOPE = "https://www.googleapis.com/auth/logging.read";
|
|
65290
65410
|
var TOKEN_REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -65315,7 +65435,7 @@ function createServiceAccountJwt2(clientEmail, privateKey, scope) {
|
|
|
65315
65435
|
const headerB64 = encode(header2);
|
|
65316
65436
|
const payloadB64 = encode(payload);
|
|
65317
65437
|
const signingInput = `${headerB64}.${payloadB64}`;
|
|
65318
|
-
const sign =
|
|
65438
|
+
const sign = crypto48.createSign("RSA-SHA256");
|
|
65319
65439
|
sign.update(signingInput);
|
|
65320
65440
|
const signature = sign.sign(privateKey, "base64url");
|
|
65321
65441
|
return `${signingInput}.${signature}`;
|
|
@@ -81924,7 +82044,7 @@ async function ackCloudflareQueueMessages(client, options) {
|
|
|
81924
82044
|
}
|
|
81925
82045
|
|
|
81926
82046
|
// ../api-routes/src/traffic-event-ingest.ts
|
|
81927
|
-
import
|
|
82047
|
+
import crypto49 from "crypto";
|
|
81928
82048
|
import { and as and63, eq as eq74, gte as gte17, lt as lt12, lte as lte18, sql as sql31 } from "drizzle-orm";
|
|
81929
82049
|
|
|
81930
82050
|
// ../api-routes/src/raw-event-sample-retention.ts
|
|
@@ -82223,7 +82343,7 @@ function writeTrafficEventBatch(opts) {
|
|
|
82223
82343
|
}
|
|
82224
82344
|
})();
|
|
82225
82345
|
tx.insert(rawEventSamples).values({
|
|
82226
|
-
id:
|
|
82346
|
+
id: crypto49.randomUUID(),
|
|
82227
82347
|
projectId: opts.projectId,
|
|
82228
82348
|
sourceId: opts.sourceId,
|
|
82229
82349
|
ts: sampleTimestamp,
|
|
@@ -82324,7 +82444,7 @@ var WORDPRESS_SYNC_LEASE_TTL_MS = 5 * 6e4;
|
|
|
82324
82444
|
var DEFAULT_CLOUDFLARE_INGEST_RATE_LIMIT_MAX = 6e3;
|
|
82325
82445
|
var SHA256_HEX_PATTERN = /^[0-9a-f]{64}$/i;
|
|
82326
82446
|
function sha256Hex3(value) {
|
|
82327
|
-
return
|
|
82447
|
+
return crypto50.createHash("sha256").update(value).digest("hex");
|
|
82328
82448
|
}
|
|
82329
82449
|
function hashCloudflareBearerToken(value) {
|
|
82330
82450
|
return sha256Hex3(value);
|
|
@@ -82334,7 +82454,7 @@ function timingSafeEqualHex(a, b) {
|
|
|
82334
82454
|
if (!SHA256_HEX_PATTERN.test(a) || !SHA256_HEX_PATTERN.test(b)) return false;
|
|
82335
82455
|
const left = Buffer.from(a, "hex");
|
|
82336
82456
|
const right = Buffer.from(b, "hex");
|
|
82337
|
-
return left.length === right.length &&
|
|
82457
|
+
return left.length === right.length && crypto50.timingSafeEqual(left, right);
|
|
82338
82458
|
}
|
|
82339
82459
|
function parseDirectPushCloudflareSourceConfig(config) {
|
|
82340
82460
|
const parsed = cloudflareTrafficSourceConfigSchema.safeParse(config);
|
|
@@ -82652,7 +82772,7 @@ function bindTrafficSyncSchedule(tx, projectId, sourceId, now, createIfMissing =
|
|
|
82652
82772
|
}
|
|
82653
82773
|
if (!createIfMissing) return { changed: false, created: false };
|
|
82654
82774
|
tx.insert(schedules).values({
|
|
82655
|
-
id:
|
|
82775
|
+
id: crypto50.randomUUID(),
|
|
82656
82776
|
projectId,
|
|
82657
82777
|
kind: SchedulableRunKinds["traffic-sync"],
|
|
82658
82778
|
cronExpr: DEFAULT_TRAFFIC_SYNC_CRON,
|
|
@@ -82973,7 +83093,7 @@ async function runBackfillTask(options) {
|
|
|
82973
83093
|
}
|
|
82974
83094
|
})();
|
|
82975
83095
|
tx.insert(rawEventSamples).values({
|
|
82976
|
-
id:
|
|
83096
|
+
id: crypto50.randomUUID(),
|
|
82977
83097
|
projectId: project.id,
|
|
82978
83098
|
sourceId: sourceRow2.id,
|
|
82979
83099
|
ts: sampleTimestamp,
|
|
@@ -83164,7 +83284,7 @@ async function trafficRoutes(app, opts) {
|
|
|
83164
83284
|
const fallbackName = displayName ?? `Cloud Run \xB7 ${gcpProjectId}${serviceName ? ` / ${serviceName}` : ""}`;
|
|
83165
83285
|
const { sourceRow: sourceRow2, scheduleChanged } = app.db.transaction((tx) => {
|
|
83166
83286
|
const activeSource = tx.select().from(trafficSources).where(eq76(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes["cloud-run"] && row.status !== TrafficSourceStatuses.archived);
|
|
83167
|
-
const sourceId = activeSource?.id ??
|
|
83287
|
+
const sourceId = activeSource?.id ?? crypto50.randomUUID();
|
|
83168
83288
|
const sourceStatus = trafficConnectStatus(tx, project.id, activeSource);
|
|
83169
83289
|
if (activeSource) {
|
|
83170
83290
|
tx.update(trafficSources).set({
|
|
@@ -83256,7 +83376,7 @@ async function trafficRoutes(app, opts) {
|
|
|
83256
83376
|
const { sourceRow: sourceRow2, scheduleChanged } = app.db.transaction((tx) => {
|
|
83257
83377
|
const activeSource = tx.select().from(trafficSources).where(eq76(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.wordpress && row.status !== TrafficSourceStatuses.archived);
|
|
83258
83378
|
const endpointChanged = activeSource !== void 0 && wordpressBaseUrlIdentity(activeSource.configJson.baseUrl) !== wordpressBaseUrlIdentity(baseUrl);
|
|
83259
|
-
const sourceId = endpointChanged ?
|
|
83379
|
+
const sourceId = endpointChanged ? crypto50.randomUUID() : activeSource?.id ?? crypto50.randomUUID();
|
|
83260
83380
|
const sourceStatus = trafficConnectStatus(tx, project.id, activeSource);
|
|
83261
83381
|
if (activeSource && !endpointChanged) {
|
|
83262
83382
|
tx.update(trafficSources).set({
|
|
@@ -83368,7 +83488,7 @@ async function trafficRoutes(app, opts) {
|
|
|
83368
83488
|
}).where(eq76(trafficSources.id, activeSource.id)).run();
|
|
83369
83489
|
row = tx.select().from(trafficSources).where(eq76(trafficSources.id, activeSource.id)).get();
|
|
83370
83490
|
} else {
|
|
83371
|
-
const newId =
|
|
83491
|
+
const newId = crypto50.randomUUID();
|
|
83372
83492
|
tx.insert(trafficSources).values({
|
|
83373
83493
|
id: newId,
|
|
83374
83494
|
projectId: project.id,
|
|
@@ -83438,7 +83558,7 @@ async function trafficRoutes(app, opts) {
|
|
|
83438
83558
|
const cloudflareSources = app.db.select().from(trafficSources).where(eq76(trafficSources.projectId, project.id)).all().filter((row) => row.sourceType === TrafficSourceTypes.cloudflare && row.status !== TrafficSourceStatuses.archived);
|
|
83439
83559
|
const sameModeSource = cloudflareSources.find((row) => parseQueuePullCloudflareSourceConfig(row.configJson));
|
|
83440
83560
|
if (sameModeSource) attempt.update({ action: "reauth" });
|
|
83441
|
-
const sourceId2 = sameModeSource?.id ??
|
|
83561
|
+
const sourceId2 = sameModeSource?.id ?? crypto50.randomUUID();
|
|
83442
83562
|
const previousCredential2 = credentialStore.getConnectionBySourceId(sourceId2);
|
|
83443
83563
|
const workerVersion2 = CURRENT_CLOUDFLARE_WORKER_VERSION;
|
|
83444
83564
|
const workerScript2 = generateWorkerScript({
|
|
@@ -83574,7 +83694,7 @@ async function trafficRoutes(app, opts) {
|
|
|
83574
83694
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
83575
83695
|
const activeSource = app.db.select().from(trafficSources).where(eq76(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.cloudflare && row.status !== TrafficSourceStatuses.archived && parseDirectPushCloudflareSourceConfig(row.configJson) !== null);
|
|
83576
83696
|
if (activeSource) attempt.update({ action: "reauth" });
|
|
83577
|
-
const sourceId = activeSource?.id ??
|
|
83697
|
+
const sourceId = activeSource?.id ?? crypto50.randomUUID();
|
|
83578
83698
|
const parsedActiveConfig = activeSource ? parseDirectPushCloudflareSourceConfig(activeSource.configJson) : null;
|
|
83579
83699
|
if (activeSource && !parsedActiveConfig) {
|
|
83580
83700
|
throw validationError("Existing Cloudflare source is not configured for direct-push delivery");
|
|
@@ -83590,8 +83710,8 @@ async function trafficRoutes(app, opts) {
|
|
|
83590
83710
|
const previousAccountId = reusableCredential?.accountId ?? parsedActiveConfig?.accountId ?? null;
|
|
83591
83711
|
const effectiveZoneId = zoneId ?? previousZoneId;
|
|
83592
83712
|
const effectiveAccountId = accountId ?? previousAccountId;
|
|
83593
|
-
const bearerToken = reusableCredential?.bearerToken ?? `cnry_cfw_${
|
|
83594
|
-
const hmacSecret = reusableCredential?.hmacSecret ??
|
|
83713
|
+
const bearerToken = reusableCredential?.bearerToken ?? `cnry_cfw_${crypto50.randomBytes(32).toString("hex")}`;
|
|
83714
|
+
const hmacSecret = reusableCredential?.hmacSecret ?? crypto50.randomBytes(32).toString("hex");
|
|
83595
83715
|
const ingestTokenHash = sha256Hex3(bearerToken);
|
|
83596
83716
|
const ingestUrl = opts.cloudflareTrafficIngestUrl.replace("{name}", encodeURIComponent(project.name));
|
|
83597
83717
|
assertCloudflareIngestUrlIsPublicHttps(ingestUrl);
|
|
@@ -83937,7 +84057,7 @@ async function trafficRoutes(app, opts) {
|
|
|
83937
84057
|
apiToken: credential.apiToken
|
|
83938
84058
|
};
|
|
83939
84059
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
83940
|
-
const leaseOwner =
|
|
84060
|
+
const leaseOwner = crypto50.randomUUID();
|
|
83941
84061
|
if (!tryClaimTrafficSyncLease({
|
|
83942
84062
|
db: app.db,
|
|
83943
84063
|
sourceId: sourceRow2.id,
|
|
@@ -83947,7 +84067,7 @@ async function trafficRoutes(app, opts) {
|
|
|
83947
84067
|
})) {
|
|
83948
84068
|
throw operationInProgress("Cloudflare Queue source sync is already in progress", { sourceId: sourceRow2.id });
|
|
83949
84069
|
}
|
|
83950
|
-
const runId =
|
|
84070
|
+
const runId = crypto50.randomUUID();
|
|
83951
84071
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
83952
84072
|
const startedMs = Date.now();
|
|
83953
84073
|
try {
|
|
@@ -84187,7 +84307,7 @@ async function trafficRoutes(app, opts) {
|
|
|
84187
84307
|
if (sourceRow2.status === TrafficSourceStatuses.paused || sourceRow2.status === TrafficSourceStatuses.archived || hasConnectedSibling) {
|
|
84188
84308
|
throw validationError("Traffic source must be active before it can sync");
|
|
84189
84309
|
}
|
|
84190
|
-
const wordpressLeaseOwner = sourceRow2.sourceType === TrafficSourceTypes.wordpress ?
|
|
84310
|
+
const wordpressLeaseOwner = sourceRow2.sourceType === TrafficSourceTypes.wordpress ? crypto50.randomUUID() : void 0;
|
|
84191
84311
|
if (wordpressLeaseOwner && !tryClaimTrafficSyncLease({
|
|
84192
84312
|
db: app.db,
|
|
84193
84313
|
sourceId: sourceRow2.id,
|
|
@@ -84202,7 +84322,7 @@ async function trafficRoutes(app, opts) {
|
|
|
84202
84322
|
let windowEnd = syncWindowEnd;
|
|
84203
84323
|
const startedAt = syncWindowEnd.toISOString();
|
|
84204
84324
|
const syncStartedAtMs = syncWindowEnd.getTime();
|
|
84205
|
-
const runId =
|
|
84325
|
+
const runId = crypto50.randomUUID();
|
|
84206
84326
|
app.db.insert(runs).values({
|
|
84207
84327
|
id: runId,
|
|
84208
84328
|
projectId: project.id,
|
|
@@ -84751,7 +84871,7 @@ async function trafficRoutes(app, opts) {
|
|
|
84751
84871
|
}
|
|
84752
84872
|
})();
|
|
84753
84873
|
tx.insert(rawEventSamples).values({
|
|
84754
|
-
id:
|
|
84874
|
+
id: crypto50.randomUUID(),
|
|
84755
84875
|
projectId: project.id,
|
|
84756
84876
|
sourceId: sourceRow2.id,
|
|
84757
84877
|
ts: sampleTimestamp,
|
|
@@ -84983,7 +85103,7 @@ async function trafficRoutes(app, opts) {
|
|
|
84983
85103
|
};
|
|
84984
85104
|
}
|
|
84985
85105
|
const startedAt = windowEnd.toISOString();
|
|
84986
|
-
const runId =
|
|
85106
|
+
const runId = crypto50.randomUUID();
|
|
84987
85107
|
app.db.transaction((tx) => {
|
|
84988
85108
|
const latestSource = tx.select().from(trafficSources).where(eq76(trafficSources.id, sourceRow2.id)).get();
|
|
84989
85109
|
if (!latestSource || !isAuthoritativeTrafficSource(tx, latestSource) || !isSameTrafficSourceGeneration(latestSource, sourceRow2)) {
|
|
@@ -85864,7 +85984,7 @@ var REFERRAL_ASSESSMENT_CHECKS = [{
|
|
|
85864
85984
|
}];
|
|
85865
85985
|
|
|
85866
85986
|
// ../api-routes/src/doctor/checks/agent.ts
|
|
85867
|
-
import
|
|
85987
|
+
import crypto51 from "crypto";
|
|
85868
85988
|
import fs6 from "fs";
|
|
85869
85989
|
import path7 from "path";
|
|
85870
85990
|
var REQUIRED_SKILLS = ["canonry", "aero"];
|
|
@@ -86098,7 +86218,7 @@ function isInstalled(dir) {
|
|
|
86098
86218
|
}
|
|
86099
86219
|
function hashInstalledFile(filePath) {
|
|
86100
86220
|
try {
|
|
86101
|
-
return
|
|
86221
|
+
return crypto51.createHash("sha256").update(fs6.readFileSync(filePath)).digest("hex");
|
|
86102
86222
|
} catch {
|
|
86103
86223
|
return void 0;
|
|
86104
86224
|
}
|
|
@@ -89981,7 +90101,7 @@ async function statusRoutes(app, opts) {
|
|
|
89981
90101
|
}
|
|
89982
90102
|
|
|
89983
90103
|
// ../api-routes/src/discovery/routes.ts
|
|
89984
|
-
import
|
|
90104
|
+
import crypto52 from "crypto";
|
|
89985
90105
|
import { and as and76, desc as desc42, eq as eq92, gte as gte23, inArray as inArray37, isNull as isNull17, or as or21 } from "drizzle-orm";
|
|
89986
90106
|
var MAX_INFLIGHT_DISCOVERY_AGE_MS = 2 * 60 * 60 * 1e3;
|
|
89987
90107
|
async function discoveryRoutes(app, opts) {
|
|
@@ -90047,8 +90167,8 @@ async function discoveryRoutes(app, opts) {
|
|
|
90047
90167
|
if (existing && existing.runId) {
|
|
90048
90168
|
return { reused: true, sessionId: existing.id, runId: existing.runId };
|
|
90049
90169
|
}
|
|
90050
|
-
const sessionId =
|
|
90051
|
-
const runId =
|
|
90170
|
+
const sessionId = crypto52.randomUUID();
|
|
90171
|
+
const runId = crypto52.randomUUID();
|
|
90052
90172
|
tx.insert(discoverySessions).values({
|
|
90053
90173
|
id: sessionId,
|
|
90054
90174
|
projectId: project.id,
|
|
@@ -90303,7 +90423,7 @@ async function discoveryRoutes(app, opts) {
|
|
|
90303
90423
|
const marketNames = beginMarketNameWrite(tx, project.id);
|
|
90304
90424
|
for (const query of promotedQueries) {
|
|
90305
90425
|
tx.insert(queries).values({
|
|
90306
|
-
id:
|
|
90426
|
+
id: crypto52.randomUUID(),
|
|
90307
90427
|
projectId: project.id,
|
|
90308
90428
|
query,
|
|
90309
90429
|
provenance,
|
|
@@ -90484,7 +90604,7 @@ function planCompetitorPromotion(input) {
|
|
|
90484
90604
|
}
|
|
90485
90605
|
|
|
90486
90606
|
// ../api-routes/src/discovery/orchestrate.ts
|
|
90487
|
-
import
|
|
90607
|
+
import crypto53 from "crypto";
|
|
90488
90608
|
import { eq as eq93 } from "drizzle-orm";
|
|
90489
90609
|
var DEFAULT_MAX_PROBES = 100;
|
|
90490
90610
|
var ABSOLUTE_MAX_PROBES = 500;
|
|
@@ -90650,7 +90770,7 @@ async function executeDiscovery(opts) {
|
|
|
90650
90770
|
probeRows.push({ citedDomains: probe.citedDomains, bucket });
|
|
90651
90771
|
buckets[bucket]++;
|
|
90652
90772
|
return {
|
|
90653
|
-
id:
|
|
90773
|
+
id: crypto53.randomUUID(),
|
|
90654
90774
|
sessionId: opts.sessionId,
|
|
90655
90775
|
projectId: opts.project.id,
|
|
90656
90776
|
query: probedCanonicals[index2],
|
|
@@ -90702,7 +90822,7 @@ function upsertDomainClassifications(db, projectId, sessionId, competitorMap) {
|
|
|
90702
90822
|
const domain = hostOf(entry.domain) ?? "";
|
|
90703
90823
|
if (!domain) continue;
|
|
90704
90824
|
db.insert(domainClassifications).values({
|
|
90705
|
-
id:
|
|
90825
|
+
id: crypto53.randomUUID(),
|
|
90706
90826
|
projectId,
|
|
90707
90827
|
domain,
|
|
90708
90828
|
competitorType: entry.competitorType,
|
|
@@ -90742,7 +90862,7 @@ function dedupeStrings(input) {
|
|
|
90742
90862
|
}
|
|
90743
90863
|
|
|
90744
90864
|
// ../api-routes/src/technical-aeo.ts
|
|
90745
|
-
import
|
|
90865
|
+
import crypto54 from "crypto";
|
|
90746
90866
|
import { and as and78, asc as asc18, count as count7, desc as desc43, eq as eq95, gte as gte24, inArray as inArray38, isNotNull as isNotNull9, isNull as isNull18, lt as lt13, or as or22, sql as sql35 } from "drizzle-orm";
|
|
90747
90867
|
import { alias } from "drizzle-orm/sqlite-core";
|
|
90748
90868
|
|
|
@@ -92735,7 +92855,7 @@ async function technicalAeoRoutes(app, opts) {
|
|
|
92735
92855
|
const healthState = parseSiteHealthState(request.query.healthState);
|
|
92736
92856
|
const limit = parseBoundedLimit(request.query.limit, 100, 200);
|
|
92737
92857
|
const sort = ["score-desc", "score-asc", "path"].includes(request.query.sort ?? "") ? request.query.sort : "url";
|
|
92738
|
-
const selectionKey =
|
|
92858
|
+
const selectionKey = crypto54.createHash("sha256").update(JSON.stringify({
|
|
92739
92859
|
projectId: project.id,
|
|
92740
92860
|
runId: snapshot.runId,
|
|
92741
92861
|
attemptId: snapshot.attemptId,
|
|
@@ -93235,7 +93355,7 @@ async function technicalAeoRoutes(app, opts) {
|
|
|
93235
93355
|
);
|
|
93236
93356
|
}
|
|
93237
93357
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
93238
|
-
const runId =
|
|
93358
|
+
const runId = crypto54.randomUUID();
|
|
93239
93359
|
tx.insert(runs).values({
|
|
93240
93360
|
id: runId,
|
|
93241
93361
|
projectId: project.id,
|
|
@@ -93272,7 +93392,7 @@ async function technicalAeoRoutes(app, opts) {
|
|
|
93272
93392
|
}
|
|
93273
93393
|
|
|
93274
93394
|
// ../api-routes/src/research.ts
|
|
93275
|
-
import
|
|
93395
|
+
import crypto55 from "crypto";
|
|
93276
93396
|
import { z as z5 } from "zod";
|
|
93277
93397
|
import { and as and79, count as count8, desc as desc44, eq as eq96, gte as gte25, lt as lt14, or as or23, sql as sql36 } from "drizzle-orm";
|
|
93278
93398
|
var sameLocation = (a, b) => a.label === b.label && a.city === b.city && a.region === b.region && a.country === b.country && a.timezone === b.timezone;
|
|
@@ -93342,9 +93462,9 @@ async function researchRoutes(app, opts) {
|
|
|
93342
93462
|
throw researchDailyLimitExceeded(project.name, viewerDailyRunLimit, date);
|
|
93343
93463
|
}
|
|
93344
93464
|
}
|
|
93345
|
-
const id =
|
|
93465
|
+
const id = crypto55.randomUUID();
|
|
93346
93466
|
tx.insert(researchRuns).values({ id, projectId: project.id, status: ResearchRunStatuses.queued, provider: providerName, requestedModel: requestedModel2, resolvedModel, location: location ?? null, totalQueries: input.queries.length, scope, template, idempotencyKey: input.idempotencyKey ?? null, requestHash: input.idempotencyKey ? requestHash2 : null, initiatedBy, createdAt: now }).run();
|
|
93347
|
-
for (const [position, query] of input.queries.entries()) tx.insert(researchRunQueries).values({ id:
|
|
93467
|
+
for (const [position, query] of input.queries.entries()) tx.insert(researchRunQueries).values({ id: crypto55.randomUUID(), researchRunId: id, position, queryText: query, queryClass: queryClasses[position] ?? null, status: ResearchQueryStatuses.queued, requestedModel: requestedModel2, resolvedModel, groundingSources: [], citedDomains: [], searchQueries: [], createdAt: now }).run();
|
|
93348
93468
|
writeAuditLog(tx, { projectId: project.id, actor: "api", action: "research.created", entityType: "research_run", entityId: id });
|
|
93349
93469
|
return { reused: false, id, shouldDispatch: true };
|
|
93350
93470
|
});
|
|
@@ -93397,7 +93517,7 @@ async function researchRoutes(app, opts) {
|
|
|
93397
93517
|
}
|
|
93398
93518
|
const ids = [];
|
|
93399
93519
|
for (const [index2, run] of prepared.entries()) {
|
|
93400
|
-
const id =
|
|
93520
|
+
const id = crypto55.randomUUID();
|
|
93401
93521
|
ids.push(id);
|
|
93402
93522
|
tx.insert(researchRuns).values({
|
|
93403
93523
|
id,
|
|
@@ -93416,7 +93536,7 @@ async function researchRoutes(app, opts) {
|
|
|
93416
93536
|
createdAt: now
|
|
93417
93537
|
}).run();
|
|
93418
93538
|
for (const [position, query] of run.input.queries.entries()) tx.insert(researchRunQueries).values({
|
|
93419
|
-
id:
|
|
93539
|
+
id: crypto55.randomUUID(),
|
|
93420
93540
|
researchRunId: id,
|
|
93421
93541
|
position,
|
|
93422
93542
|
queryText: query,
|
|
@@ -93475,7 +93595,7 @@ async function researchRoutes(app, opts) {
|
|
|
93475
93595
|
var DIRECT_REQUEST_HASH_VERSION = "direct-v2:";
|
|
93476
93596
|
function directResearchRequestHash(input) {
|
|
93477
93597
|
const { idempotencyKey: _key, ...request } = input;
|
|
93478
|
-
return DIRECT_REQUEST_HASH_VERSION +
|
|
93598
|
+
return DIRECT_REQUEST_HASH_VERSION + crypto55.createHash("sha256").update(JSON.stringify(request)).digest("hex");
|
|
93479
93599
|
}
|
|
93480
93600
|
function assertSameDirectResearchRequest(existing, input, hash2) {
|
|
93481
93601
|
if (existing.requestHash?.startsWith(DIRECT_REQUEST_HASH_VERSION)) {
|
|
@@ -93493,7 +93613,7 @@ function assertSameDirectResearchRequest(existing, input, hash2) {
|
|
|
93493
93613
|
...scope ? { scope } : {},
|
|
93494
93614
|
...template ? { template } : {}
|
|
93495
93615
|
};
|
|
93496
|
-
if (
|
|
93616
|
+
if (crypto55.createHash("sha256").update(JSON.stringify(normalized)).digest("hex") !== existing.requestHash) throw alreadyExists("Research idempotency key", input.idempotencyKey);
|
|
93497
93617
|
}
|
|
93498
93618
|
var researchCursorSchema = z5.object({ projectId: z5.string().min(1), createdAt: z5.string().datetime({ offset: true }), id: z5.string().min(1).max(256) }).strict();
|
|
93499
93619
|
function parseResearchCursor(value, projectId) {
|
|
@@ -93513,11 +93633,11 @@ function resolveStoredResearchScope(stored, selection, idempotencyKey) {
|
|
|
93513
93633
|
return stored;
|
|
93514
93634
|
}
|
|
93515
93635
|
function batchChildReceiptKey(rootKey, index2) {
|
|
93516
|
-
const derived =
|
|
93636
|
+
const derived = crypto55.createHash("sha256").update(`${rootKey}\0${index2}`).digest("hex");
|
|
93517
93637
|
return `${BATCH_RECEIPT_PREFIX}${derived}:${index2}`;
|
|
93518
93638
|
}
|
|
93519
93639
|
function researchBatchRequestHash(input) {
|
|
93520
|
-
return
|
|
93640
|
+
return crypto55.createHash("sha256").update(JSON.stringify({ runs: input.runs })).digest("hex");
|
|
93521
93641
|
}
|
|
93522
93642
|
function prepareBatchRun(app, opts, project, active, input) {
|
|
93523
93643
|
const adapters = opts.providerAdapters ?? [];
|
|
@@ -93789,7 +93909,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
93789
93909
|
}
|
|
93790
93910
|
|
|
93791
93911
|
// ../api-routes/src/simple-measurement-definitions.ts
|
|
93792
|
-
import
|
|
93912
|
+
import crypto56 from "crypto";
|
|
93793
93913
|
import { and as and81, eq as eq98 } from "drizzle-orm";
|
|
93794
93914
|
function captureSimpleMeasurementDefinition(db, input) {
|
|
93795
93915
|
return db.transaction((tx) => {
|
|
@@ -93831,11 +93951,11 @@ function captureSimpleMeasurementDefinition(db, input) {
|
|
|
93831
93951
|
...frozen.identity.qualifiedAliases?.length ? { qualifiedAliases: frozen.identity.qualifiedAliases } : {}
|
|
93832
93952
|
}
|
|
93833
93953
|
};
|
|
93834
|
-
const replayChecksum =
|
|
93954
|
+
const replayChecksum = crypto56.createHash("sha256").update(canonicalSimpleMeasurementDefinitionJson(replayDefinition)).digest("hex");
|
|
93835
93955
|
if (existing.checksum !== replayChecksum) {
|
|
93836
93956
|
if (frozen.competitors === void 0) {
|
|
93837
93957
|
const { competitors: _currentCompetitors, ...legacyReplayDefinition } = replayDefinition;
|
|
93838
|
-
const legacyReplayChecksum =
|
|
93958
|
+
const legacyReplayChecksum = crypto56.createHash("sha256").update(canonicalSimpleMeasurementDefinitionJson(legacyReplayDefinition)).digest("hex");
|
|
93839
93959
|
if (existing.checksum === legacyReplayChecksum) return frozen;
|
|
93840
93960
|
}
|
|
93841
93961
|
throw validationError("This run already has a captured measurement definition. Start a new run for changed inputs.");
|
|
@@ -93859,7 +93979,7 @@ function captureSimpleMeasurementDefinition(db, input) {
|
|
|
93859
93979
|
throw validationError("Captured competitors must exactly match the project competitors dispatched for this run.");
|
|
93860
93980
|
}
|
|
93861
93981
|
}
|
|
93862
|
-
const checksum =
|
|
93982
|
+
const checksum = crypto56.createHash("sha256").update(canonicalSimpleMeasurementDefinitionJson(definition2)).digest("hex");
|
|
93863
93983
|
tx.insert(simpleMeasurementDefinitions).values({
|
|
93864
93984
|
projectId: input.projectId,
|
|
93865
93985
|
runId: input.runId,
|
|
@@ -94474,7 +94594,7 @@ function buildTrafficSourceValidators(opts) {
|
|
|
94474
94594
|
}
|
|
94475
94595
|
|
|
94476
94596
|
// src/intelligence-service.ts
|
|
94477
|
-
import
|
|
94597
|
+
import crypto57 from "crypto";
|
|
94478
94598
|
|
|
94479
94599
|
// ../api-routes/src/runtime-logger.ts
|
|
94480
94600
|
import { format } from "util";
|
|
@@ -95276,7 +95396,7 @@ var IntelligenceService = class {
|
|
|
95276
95396
|
}).run();
|
|
95277
95397
|
}
|
|
95278
95398
|
tx.insert(healthSnapshots).values({
|
|
95279
|
-
id:
|
|
95399
|
+
id: crypto57.randomUUID(),
|
|
95280
95400
|
projectId,
|
|
95281
95401
|
runId,
|
|
95282
95402
|
overallCitedRate: String(result.health.overallCitedRate),
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
persistSiteCrawlGraphLayout,
|
|
29
29
|
prepareSiteCrawlGraphLayout,
|
|
30
30
|
recordedSharePct
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-QWLKYIQ4.js";
|
|
32
32
|
import {
|
|
33
33
|
CanonryMcpToolNames,
|
|
34
34
|
PACKAGE_VERSION,
|
|
@@ -240,7 +240,7 @@ import {
|
|
|
240
240
|
webhookResponseReason,
|
|
241
241
|
webhookTargetRefusalReason,
|
|
242
242
|
writeAuditLog
|
|
243
|
-
} from "./chunk-
|
|
243
|
+
} from "./chunk-IBRZ23VQ.js";
|
|
244
244
|
import {
|
|
245
245
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
246
246
|
AGENT_NONE,
|
|
@@ -8379,7 +8379,7 @@ function nonBlank(value) {
|
|
|
8379
8379
|
return trimmed ? trimmed : void 0;
|
|
8380
8380
|
}
|
|
8381
8381
|
function resolveBuildCommit(env = process.env) {
|
|
8382
|
-
const embedded = true ? "
|
|
8382
|
+
const embedded = true ? "d2f4f0349b43d56e3cb425dfe64da3dcefa565ea" : void 0;
|
|
8383
8383
|
return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
|
|
8384
8384
|
}
|
|
8385
8385
|
function resolveInstanceIdentity(env = process.env) {
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
REGISTERED_CLI_COMMANDS
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-7R2HFAG4.js";
|
|
4
|
+
import "./chunk-OO64BWNB.js";
|
|
5
5
|
import "./chunk-WMG365JI.js";
|
|
6
6
|
import "./chunk-QBZPKXFW.js";
|
|
7
7
|
import "./chunk-RB2F4D3Z.js";
|
|
8
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-QWLKYIQ4.js";
|
|
9
9
|
import "./chunk-PCZXWOYX.js";
|
|
10
10
|
import "./chunk-HGMYEDAQ.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-IBRZ23VQ.js";
|
|
12
12
|
import "./chunk-V4TITINC.js";
|
|
13
13
|
export {
|
|
14
14
|
REGISTERED_CLI_COMMANDS
|
package/dist/cli.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
buildSetupState,
|
|
14
14
|
consumePendingServeHandoff,
|
|
15
15
|
serveCommand
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-7R2HFAG4.js";
|
|
17
17
|
import {
|
|
18
18
|
checkLatestVersionForCli,
|
|
19
19
|
cliRuntimeContext,
|
|
@@ -27,13 +27,13 @@ import {
|
|
|
27
27
|
showFirstRunNotice,
|
|
28
28
|
trackCliCommandFinished,
|
|
29
29
|
trackEvent
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-OO64BWNB.js";
|
|
31
31
|
import "./chunk-WMG365JI.js";
|
|
32
32
|
import {
|
|
33
33
|
resolveCliAccess
|
|
34
34
|
} from "./chunk-QBZPKXFW.js";
|
|
35
35
|
import "./chunk-RB2F4D3Z.js";
|
|
36
|
-
import "./chunk-
|
|
36
|
+
import "./chunk-QWLKYIQ4.js";
|
|
37
37
|
import "./chunk-PCZXWOYX.js";
|
|
38
38
|
import {
|
|
39
39
|
CliError,
|
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
printCliError,
|
|
43
43
|
usageError
|
|
44
44
|
} from "./chunk-HGMYEDAQ.js";
|
|
45
|
-
import "./chunk-
|
|
45
|
+
import "./chunk-IBRZ23VQ.js";
|
|
46
46
|
import {
|
|
47
47
|
OperationAccesses
|
|
48
48
|
} from "./chunk-V4TITINC.js";
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
computeFactorAverages,
|
|
6
6
|
layoutSiteCrawlGraphInput,
|
|
7
7
|
persistSiteCrawlGraphLayout
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-QWLKYIQ4.js";
|
|
9
9
|
import {
|
|
10
10
|
PACKAGE_VERSION,
|
|
11
11
|
canonryMcpTools
|
|
@@ -86,7 +86,7 @@ import {
|
|
|
86
86
|
siteCrawlPages,
|
|
87
87
|
siteCrawlSnapshots,
|
|
88
88
|
trafficSources
|
|
89
|
-
} from "./chunk-
|
|
89
|
+
} from "./chunk-IBRZ23VQ.js";
|
|
90
90
|
import {
|
|
91
91
|
RunKinds,
|
|
92
92
|
SiteCrawlFetchStates,
|
package/dist/index.js
CHANGED
|
@@ -3,14 +3,14 @@ import {
|
|
|
3
3
|
createGoogleMarketingCredentialStore,
|
|
4
4
|
createGoogleMarketingRuntime,
|
|
5
5
|
createServer
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-OO64BWNB.js";
|
|
7
7
|
import {
|
|
8
8
|
loadConfig
|
|
9
9
|
} from "./chunk-WMG365JI.js";
|
|
10
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-QWLKYIQ4.js";
|
|
11
11
|
import "./chunk-PCZXWOYX.js";
|
|
12
12
|
import "./chunk-HGMYEDAQ.js";
|
|
13
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-IBRZ23VQ.js";
|
|
14
14
|
import "./chunk-V4TITINC.js";
|
|
15
15
|
export {
|
|
16
16
|
GoogleMarketingRuntimeError,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonry/canonry",
|
|
3
|
-
"version": "7.46.
|
|
3
|
+
"version": "7.46.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Self-hosted AI visibility (AEO) platform: track how ChatGPT, Claude, Gemini, and Perplexity cite your domain, join it with Search Console, GA4, server-side traffic, and paid media, and fix what you find through agent tools (CLI, REST, MCP). Local SQLite.",
|
|
6
6
|
"keywords": [
|
|
@@ -86,33 +86,33 @@
|
|
|
86
86
|
"tsup": "^8.5.1",
|
|
87
87
|
"tsx": "^4.19.0",
|
|
88
88
|
"@canonry/canonry-web": "0.0.0",
|
|
89
|
-
"@canonry/canonry-contracts": "0.0.0",
|
|
90
|
-
"@canonry/canonry-config": "0.0.0",
|
|
91
89
|
"@canonry/canonry-api-client": "0.0.0",
|
|
92
|
-
"@canonry/canonry-db": "0.0.0",
|
|
93
90
|
"@canonry/canonry-api-routes": "0.0.0",
|
|
91
|
+
"@canonry/canonry-config": "0.0.0",
|
|
92
|
+
"@canonry/canonry-contracts": "0.0.0",
|
|
93
|
+
"@canonry/canonry-db": "0.0.0",
|
|
94
94
|
"@canonry/canonry-integration-bing": "0.0.0",
|
|
95
|
-
"@canonry/canonry-integration-
|
|
95
|
+
"@canonry/canonry-integration-cloud-run": "0.0.0",
|
|
96
96
|
"@canonry/canonry-integration-cloudflare-queue": "0.0.0",
|
|
97
|
+
"@canonry/canonry-integration-cloudflare-worker": "0.0.0",
|
|
97
98
|
"@canonry/canonry-integration-commoncrawl": "0.0.0",
|
|
98
|
-
"@canonry/canonry-integration-google": "0.0.0",
|
|
99
|
-
"@canonry/canonry-integration-cloud-run": "0.0.0",
|
|
100
|
-
"@canonry/canonry-integration-google-places": "0.0.0",
|
|
101
99
|
"@canonry/canonry-integration-google-ads": "0.0.0",
|
|
102
100
|
"@canonry/canonry-integration-google-business-profile": "0.0.0",
|
|
103
101
|
"@canonry/canonry-integration-google-tag-manager": "0.0.0",
|
|
102
|
+
"@canonry/canonry-integration-google": "0.0.0",
|
|
103
|
+
"@canonry/canonry-integration-google-places": "0.0.0",
|
|
104
|
+
"@canonry/canonry-integration-traffic": "0.0.0",
|
|
105
|
+
"@canonry/canonry-integration-wordpress": "0.0.0",
|
|
104
106
|
"@canonry/canonry-integration-openai-ads": "0.0.0",
|
|
105
107
|
"@canonry/canonry-intelligence": "0.0.0",
|
|
106
108
|
"@canonry/canonry-provider-cdp": "0.0.0",
|
|
107
|
-
"@canonry/canonry-integration-wordpress": "0.0.0",
|
|
108
|
-
"@canonry/canonry-integration-traffic": "0.0.0",
|
|
109
109
|
"@canonry/canonry-provider-claude": "0.0.0",
|
|
110
|
-
"@canonry/canonry-provider-muse": "0.0.0",
|
|
111
110
|
"@canonry/canonry-provider-gemini": "0.0.0",
|
|
112
|
-
"@canonry/canonry-provider-
|
|
111
|
+
"@canonry/canonry-provider-muse": "0.0.0",
|
|
113
112
|
"@canonry/canonry-provider-local": "0.0.0",
|
|
114
|
-
"@canonry/canonry-provider-
|
|
115
|
-
"@canonry/canonry-integration-typesafe": "0.0.0"
|
|
113
|
+
"@canonry/canonry-provider-openai": "0.0.0",
|
|
114
|
+
"@canonry/canonry-integration-typesafe": "0.0.0",
|
|
115
|
+
"@canonry/canonry-provider-perplexity": "0.0.0"
|
|
116
116
|
},
|
|
117
117
|
"scripts": {
|
|
118
118
|
"build": "pnpm run build:cli && pnpm run build:web",
|