@gscdump/sdk 0.11.4

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Harlan Wilton
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # @gscdump/sdk
2
+
3
+ Consumer SDK for hosted gscdump.com integrations.
4
+
5
+ This package is for partner applications that consume gscdump.com APIs,
6
+ webhooks, and realtime events. Callers inject the HTTP transport and auth they
7
+ want to use; route construction stays inside the hosted adapter.
8
+
9
+ ```ts
10
+ import { createGscdumpClient } from '@gscdump/sdk'
11
+
12
+ const gscdump = createGscdumpClient({
13
+ apiBase: 'https://example.com/api',
14
+ apiKey: process.env.GSCDUMP_API_KEY,
15
+ fetch: $fetch,
16
+ })
17
+
18
+ const { sites } = await gscdump.getUserSites(userId)
19
+ ```
20
+
21
+ ## Scope
22
+
23
+ - Partner user lifecycle
24
+ - Partner site lifecycle
25
+ - Data/detail queries
26
+ - Analysis presets
27
+ - Sitemap and indexing reads
28
+ - Webhook receiver contracts and HMAC verification helpers
29
+ - Shared request/response types re-exported from `@gscdump/contracts`
30
+
31
+ Analyzer Source dispatch, browser DuckDB-WASM boot, R2 parquet attach, and
32
+ Nuxt composables remain in `@gscdump/nuxt-analytics`. Nuxt apps should install
33
+ `@gscdump/nuxt-analytics` for dashboard reads; that layer uses
34
+ `createAnalyticsClient()` internally.
35
+
36
+ ## Boundary
37
+
38
+ `@gscdump/sdk` is a consumer SDK. It must not own gscdump.com producer
39
+ behavior.
40
+
41
+ Belongs here:
42
+
43
+ - request/response types and schemas for partner apps
44
+ - route builders and pluggable HTTP clients
45
+ - websocket client and event schemas
46
+ - webhook header constants, event schemas, parsing, normalization, and signature
47
+ verification for receivers
48
+
49
+ Does not belong here:
50
+
51
+ - deciding when gscdump.com emits webhooks
52
+ - queueing, retries, backoff, idempotency, or activity logging
53
+ - resolving public user/site IDs from gscdump.com storage
54
+ - generating or storing production webhook secrets
55
+ - creating production webhook delivery IDs or envelopes
56
+ - partner subscription filtering for outgoing deliveries
57
+
58
+ ## Webhooks
59
+
60
+ `@gscdump/contracts` owns the webhook payload schema and event constants.
61
+ `@gscdump/sdk` provides receiver-side helpers to parse the payload, verify the
62
+ signature, and normalize event names. gscdump.com owns webhook production and
63
+ delivery.
64
+
65
+ ```ts
66
+ import { parseWebhookPayload } from '@gscdump/sdk'
67
+
68
+ const envelope = await parseWebhookPayload(rawJson, {
69
+ secret: webhookSecret,
70
+ signature: request.headers.get('x-gscdump-signature'),
71
+ })
72
+ ```
@@ -0,0 +1,113 @@
1
+ import { AddPartnerTeamMemberParams, AnalyticsClient, AnalyticsClient as AnalyticsClient$1, BackfillRange, BackfillResponse, BindPartnerSiteTeamParams, BuilderState, BulkRegisterPartnerSiteResult, BulkRegisterPartnerSitesParams, BulkRegisterPartnerSitesResponse, CANONICAL_WEBHOOK_EVENTS, CanonicalWebhookEventType, CanonicalWebhookEventType as CanonicalWebhookEventType$1, CreatePartnerTeamParams, CreateWebhookEnvelopeOptions, DataDetailOptions, DataQueryOptions, DeletePartnerUserResponse, GscComparisonFilter, GscdumpAnalysisParams, GscdumpAnalysisPreset, GscdumpAnalysisResponse, GscdumpAnalysisSourcesResponse, GscdumpAvailableSite, GscdumpCanonicalMismatchesResponse, GscdumpDataDetailResponse, GscdumpDataResponse, GscdumpDataRow, GscdumpDateRangeParams, GscdumpIndexPercentResponse, GscdumpIndexingDiagnosticsResponse, GscdumpIndexingResponse, GscdumpIndexingUrlStatus, GscdumpIndexingUrlsResponse, GscdumpKeywordSparklinesParams, GscdumpKeywordSparklinesResponse, GscdumpMeta, GscdumpPermissionRecovery, GscdumpQueryTrendParams, GscdumpQueryTrendResponse, GscdumpSiteRegistration, GscdumpSitemap, GscdumpSitemapChangesResponse, GscdumpSitemapHistory, GscdumpSitemapsResponse, GscdumpSyncStatusResponse, GscdumpTeamMemberRow, GscdumpTeamRow, GscdumpTopAssociationParams, GscdumpTopAssociationResponse, GscdumpTotals, GscdumpUserRegistration, GscdumpUserSettings, GscdumpUserSite, GscdumpUserStatus, GscdumpUserTokenUpdate, IndexingUrlsParams, InspectionHistoryResponse, InspectionIndex, LEGACY_WEBHOOK_EVENTS, LegacyWebhookEventType, PartnerClient, PartnerClient as PartnerClient$1, PartnerLifecycleAccount, PartnerLifecycleResponse, PartnerLifecycleSite, PartnerRealtimeEvent, PartnerRealtimeEvent as PartnerRealtimeEvent$1, PartnerRealtimeEventType, PartnerRealtimeMessage, PartnerRealtimeMessage as PartnerRealtimeMessage$1, PartnerWebhookData, PartnerWebhookHeaders, PartnerWebhookHeaders as PartnerWebhookHeaders$1, RealtimeAuthFailedEvent, RealtimeAuthRequiredMessage, RealtimeConnectedMessage, RealtimeEnrichmentCompleteEvent, RealtimeErrorMessage, RealtimeJobFailedEvent, RealtimeNeedsReauthEvent, RealtimePongMessage, RealtimeSiteAddedEvent, RealtimeSiteRemovedEvent, RealtimeSubscribedMessage, RealtimeSyncCompleteEvent, RealtimeSyncFailedEvent, RealtimeSyncJobCompleteEvent, RealtimeSyncProgressEvent, RealtimeSyncSiteCompleteEvent, RegisterPartnerSiteParams, RegisterPartnerUserParams, RollupEnvelope, UpdatePartnerUserTokensParams, VALID_WEBHOOK_EVENTS, WEBHOOK_CONTRACT_VERSION, WEBHOOK_CONTRACT_VERSION_HEADER, WEBHOOK_DELIVERY_HEADER, WEBHOOK_EVENT_ALIASES, WEBHOOK_EVENT_HEADER, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_TIMESTAMP_HEADER, WebhookEnvelope, WebhookEnvelope as WebhookEnvelope$1, WebhookEventType, WebhookEventType as WebhookEventType$1, WhoamiResponse } from "@gscdump/contracts";
2
+ export * from "@gscdump/contracts";
3
+ type PartnerFetch = <T = unknown>(request: string, options?: PartnerFetchOptions) => Promise<T>;
4
+ type PartnerHeaders = HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
5
+ interface PartnerFetchOptions {
6
+ method?: string;
7
+ headers?: HeadersInit;
8
+ query?: Record<string, unknown>;
9
+ body?: unknown;
10
+ [key: string]: unknown;
11
+ }
12
+ interface PartnerClientOptions {
13
+ /**
14
+ * Origin API base. Use `/api` for same-origin Nitro routes, or pass a full
15
+ * remote origin base from the host app. The client has no baked-in origin.
16
+ */
17
+ apiBase?: string;
18
+ /** Convenience auth hook. Equivalent to supplying `headers: { 'x-api-key': apiKey }`. */
19
+ apiKey?: string;
20
+ /** Any ofetch-compatible instance: global `$fetch`, `useGscFetch()`, or a custom test fake. */
21
+ fetch?: PartnerFetch;
22
+ /** Static or lazy headers. Lazy headers are resolved on every operation. */
23
+ headers?: PartnerHeaders;
24
+ /** Validate request payloads and response bodies with exported Zod schemas. */
25
+ validate?: boolean | 'request' | 'response';
26
+ }
27
+ declare function createPartnerClient(options?: PartnerClientOptions): PartnerClient$1;
28
+ declare const createGscdumpClient: typeof createPartnerClient;
29
+ type AnalyticsFetch = PartnerFetch;
30
+ type AnalyticsHeaders = PartnerHeaders;
31
+ type AnalyticsFetchOptions = PartnerFetchOptions;
32
+ interface AnalyticsClientOptions {
33
+ apiBase?: string;
34
+ fetch?: AnalyticsFetch;
35
+ headers?: AnalyticsHeaders;
36
+ validate?: boolean | 'request' | 'response';
37
+ }
38
+ declare function createAnalyticsClient(options?: AnalyticsClientOptions): AnalyticsClient$1;
39
+ type PartnerErrorKind = 'auth' | 'rate-limit' | 'provisioning' | 'permission' | 'network' | 'validation' | 'not-found' | 'server' | 'unknown';
40
+ interface PartnerErrorInfo {
41
+ kind: PartnerErrorKind;
42
+ statusCode?: number;
43
+ message: string;
44
+ data?: unknown;
45
+ }
46
+ declare class PartnerApiError extends Error {
47
+ readonly kind: PartnerErrorKind;
48
+ readonly statusCode?: number;
49
+ readonly data?: unknown;
50
+ constructor(info: PartnerErrorInfo);
51
+ }
52
+ declare function toPartnerError(error: unknown): PartnerApiError;
53
+ declare function toCanonicalWebhookEvent(event: WebhookEventType$1): CanonicalWebhookEventType$1;
54
+ declare function serializeWebhookPayload(payload: string | object): string;
55
+ declare function verifyWebhookSignature(payload: string | object, signature: string | null | undefined, secret: string): Promise<boolean>;
56
+ declare function parseWebhookPayload<TData extends Record<string, unknown> = Record<string, unknown>>(payload: string | object, options?: {
57
+ secret?: string;
58
+ signature?: string | null;
59
+ headers?: PartnerWebhookHeaders$1 | Headers;
60
+ validateSignature?: boolean;
61
+ }): Promise<WebhookEnvelope$1<TData>>;
62
+ declare function readWebhookHeaders(headers: Headers | PartnerWebhookHeaders$1 | null | undefined): Required<PartnerWebhookHeaders$1>;
63
+ type PartnerRealtimeScope = 'partner' | 'user';
64
+ type PartnerRealtimeStatus = 'idle' | 'connecting' | 'open' | 'authenticated' | 'closed' | 'error';
65
+ type PartnerRealtimeHandler<T> = (value: T) => void;
66
+ interface PartnerWebSocketLike {
67
+ readyState: number;
68
+ onopen: ((event: unknown) => void) | null;
69
+ onmessage: ((event: {
70
+ data: unknown;
71
+ }) => void) | null;
72
+ onerror: ((event: unknown) => void) | null;
73
+ onclose: ((event: unknown) => void) | null;
74
+ send: (data: string) => void;
75
+ close: (code?: number, reason?: string) => void;
76
+ }
77
+ interface PartnerWebSocketConstructor {
78
+ new (url: string, protocols?: string | string[]): PartnerWebSocketLike;
79
+ }
80
+ interface PartnerRealtimeOptions {
81
+ /**
82
+ * HTTP API base used only to derive a same-origin websocket base when `wsBase`
83
+ * is omitted. Prefer passing `wsBase` when the websocket origin differs.
84
+ */
85
+ apiBase?: string;
86
+ /** WebSocket origin base, for example `wss://origin.example`. */
87
+ wsBase?: string;
88
+ /** Partner or user API key sent in the first auth message. */
89
+ apiKey: string;
90
+ /** `/ws/partner` or `/ws/user`. User scope supports partner key + user id. */
91
+ scope?: PartnerRealtimeScope;
92
+ /** Required by the hosted `/ws/user` route when authenticating with a partner API key. */
93
+ userId?: number | string;
94
+ /** Optional initial site filter sent after authentication succeeds. */
95
+ siteIds?: string[];
96
+ protocols?: string | string[];
97
+ WebSocket?: PartnerWebSocketConstructor;
98
+ }
99
+ interface PartnerRealtimeClient {
100
+ readonly status: PartnerRealtimeStatus;
101
+ readonly socket: PartnerWebSocketLike | null;
102
+ connect: () => PartnerWebSocketLike;
103
+ close: (code?: number, reason?: string) => void;
104
+ ping: () => void;
105
+ subscribe: (siteIds: string[]) => void;
106
+ onStatus: (handler: PartnerRealtimeHandler<PartnerRealtimeStatus>) => () => void;
107
+ onMessage: (handler: PartnerRealtimeHandler<PartnerRealtimeMessage$1>) => () => void;
108
+ onEvent: (handler: PartnerRealtimeHandler<PartnerRealtimeEvent$1>) => () => void;
109
+ onError: (handler: PartnerRealtimeHandler<unknown>) => () => void;
110
+ }
111
+ declare function createPartnerRealtimeClient(options: PartnerRealtimeOptions): PartnerRealtimeClient;
112
+ declare const createGscdumpRealtimeClient: typeof createPartnerRealtimeClient;
113
+ export { type AddPartnerTeamMemberParams, type AnalyticsClient, type AnalyticsClientOptions, type AnalyticsFetch, type AnalyticsFetchOptions, type AnalyticsHeaders, type BackfillRange, type BackfillResponse, type BindPartnerSiteTeamParams, type BuilderState, type BulkRegisterPartnerSiteResult, type BulkRegisterPartnerSitesParams, type BulkRegisterPartnerSitesResponse, CANONICAL_WEBHOOK_EVENTS, type CanonicalWebhookEventType, type CreatePartnerTeamParams, type CreateWebhookEnvelopeOptions, type DataDetailOptions, type DataQueryOptions, type DeletePartnerUserResponse, type GscComparisonFilter, type GscdumpAnalysisParams, type GscdumpAnalysisPreset, type GscdumpAnalysisResponse, type GscdumpAnalysisSourcesResponse, type GscdumpAvailableSite, type GscdumpCanonicalMismatchesResponse, type GscdumpDataDetailResponse, type GscdumpDataResponse, type GscdumpDataRow, type GscdumpDateRangeParams, type GscdumpIndexPercentResponse, type GscdumpIndexingDiagnosticsResponse, type GscdumpIndexingResponse, type GscdumpIndexingUrlStatus, type GscdumpIndexingUrlsResponse, type GscdumpKeywordSparklinesParams, type GscdumpKeywordSparklinesResponse, type GscdumpMeta, type GscdumpPermissionRecovery, type GscdumpQueryTrendParams, type GscdumpQueryTrendResponse, type GscdumpSiteRegistration, type GscdumpSitemap, type GscdumpSitemapChangesResponse, type GscdumpSitemapHistory, type GscdumpSitemapsResponse, type GscdumpSyncStatusResponse, type GscdumpTeamMemberRow, type GscdumpTeamRow, type GscdumpTopAssociationParams, type GscdumpTopAssociationResponse, type GscdumpTotals, type GscdumpUserRegistration, type GscdumpUserSettings, type GscdumpUserSite, type GscdumpUserStatus, type GscdumpUserTokenUpdate, type IndexingUrlsParams, type InspectionHistoryResponse, type InspectionIndex, LEGACY_WEBHOOK_EVENTS, type LegacyWebhookEventType, PartnerApiError, type PartnerClient, type PartnerClientOptions, type PartnerErrorInfo, type PartnerErrorKind, type PartnerFetch, type PartnerFetchOptions, type PartnerHeaders, type PartnerLifecycleAccount, type PartnerLifecycleResponse, type PartnerLifecycleSite, type PartnerRealtimeClient, type PartnerRealtimeEvent, type PartnerRealtimeEventType, type PartnerRealtimeHandler, type PartnerRealtimeMessage, type PartnerRealtimeOptions, type PartnerRealtimeScope, type PartnerRealtimeStatus, type PartnerWebSocketConstructor, type PartnerWebSocketLike, type PartnerWebhookData, type PartnerWebhookHeaders, type RealtimeAuthFailedEvent, type RealtimeAuthRequiredMessage, type RealtimeConnectedMessage, type RealtimeEnrichmentCompleteEvent, type RealtimeErrorMessage, type RealtimeJobFailedEvent, type RealtimeNeedsReauthEvent, type RealtimePongMessage, type RealtimeSiteAddedEvent, type RealtimeSiteRemovedEvent, type RealtimeSubscribedMessage, type RealtimeSyncCompleteEvent, type RealtimeSyncFailedEvent, type RealtimeSyncJobCompleteEvent, type RealtimeSyncProgressEvent, type RealtimeSyncSiteCompleteEvent, type RegisterPartnerSiteParams, type RegisterPartnerUserParams, type RollupEnvelope, type UpdatePartnerUserTokensParams, VALID_WEBHOOK_EVENTS, WEBHOOK_CONTRACT_VERSION, WEBHOOK_CONTRACT_VERSION_HEADER, WEBHOOK_DELIVERY_HEADER, WEBHOOK_EVENT_ALIASES, WEBHOOK_EVENT_HEADER, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_TIMESTAMP_HEADER, type WebhookEnvelope, type WebhookEventType, type WhoamiResponse, createAnalyticsClient, createGscdumpClient, createGscdumpRealtimeClient, createPartnerClient, createPartnerRealtimeClient, parseWebhookPayload, readWebhookHeaders, serializeWebhookPayload, toCanonicalWebhookEvent, toPartnerError, verifyWebhookSignature };
package/dist/index.mjs ADDED
@@ -0,0 +1,684 @@
1
+ import { ofetch } from "ofetch";
2
+ import { CANONICAL_WEBHOOK_EVENTS, LEGACY_WEBHOOK_EVENTS, VALID_WEBHOOK_EVENTS, WEBHOOK_CONTRACT_VERSION, WEBHOOK_CONTRACT_VERSION_HEADER, WEBHOOK_CONTRACT_VERSION_HEADER as WEBHOOK_CONTRACT_VERSION_HEADER$1, WEBHOOK_DELIVERY_HEADER, WEBHOOK_DELIVERY_HEADER as WEBHOOK_DELIVERY_HEADER$1, WEBHOOK_EVENT_ALIASES, WEBHOOK_EVENT_ALIASES as WEBHOOK_EVENT_ALIASES$1, WEBHOOK_EVENT_HEADER, WEBHOOK_EVENT_HEADER as WEBHOOK_EVENT_HEADER$1, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_SIGNATURE_HEADER as WEBHOOK_SIGNATURE_HEADER$1, WEBHOOK_TIMESTAMP_HEADER, WEBHOOK_TIMESTAMP_HEADER as WEBHOOK_TIMESTAMP_HEADER$1, analyticsRoutes, partnerEndpointSchemas, partnerRoutes, partnerWebhookEnvelopeSchema } from "@gscdump/contracts";
3
+ export * from "@gscdump/contracts";
4
+ var PartnerApiError = class extends Error {
5
+ kind;
6
+ statusCode;
7
+ data;
8
+ constructor(info) {
9
+ super(info.message);
10
+ this.name = "PartnerApiError";
11
+ this.kind = info.kind;
12
+ this.statusCode = info.statusCode;
13
+ this.data = info.data;
14
+ }
15
+ };
16
+ function statusOf(error) {
17
+ const rec = error;
18
+ return rec.statusCode ?? rec.status ?? rec.response?.status;
19
+ }
20
+ function messageOf(error) {
21
+ const rec = error;
22
+ return rec.data?.message ?? rec.data?.statusMessage ?? rec.message ?? rec.statusMessage ?? String(error);
23
+ }
24
+ function kindOf(status, message) {
25
+ if (status === 401 || status === 403) return "auth";
26
+ if (status === 404) return "not-found";
27
+ if (status === 409 || /provision/i.test(message)) return "provisioning";
28
+ if (status === 429) return "rate-limit";
29
+ if (status === 400 || status === 422) return "validation";
30
+ if (/permission|reauth|access/i.test(message)) return "permission";
31
+ if (status && status >= 500) return "server";
32
+ if (!status) return "network";
33
+ return "unknown";
34
+ }
35
+ function toPartnerError(error) {
36
+ if (error instanceof PartnerApiError) return error;
37
+ const statusCode = statusOf(error);
38
+ const message = messageOf(error);
39
+ const data = error?.data;
40
+ return new PartnerApiError({
41
+ kind: kindOf(statusCode, message),
42
+ statusCode,
43
+ message,
44
+ data
45
+ });
46
+ }
47
+ const TRAILING_SLASH_RE$2 = /\/+$/;
48
+ const LEADING_SLASH_RE$1 = /^\/+/;
49
+ function trimApiBase$1(apiBase) {
50
+ return (apiBase ?? "/api").replace(TRAILING_SLASH_RE$2, "");
51
+ }
52
+ function buildPath$1(apiBase, path) {
53
+ if (!apiBase) return `/${path.replace(LEADING_SLASH_RE$1, "")}`;
54
+ return `${apiBase}/${path.replace(LEADING_SLASH_RE$1, "")}`;
55
+ }
56
+ function mergeHeaders$1(base, extra) {
57
+ const headers = new Headers(base);
58
+ if (extra) for (const [key, value] of new Headers(extra).entries()) headers.set(key, value);
59
+ return headers;
60
+ }
61
+ async function resolveHeaders$1(options) {
62
+ const resolved = typeof options.headers === "function" ? await options.headers() : options.headers;
63
+ if (!options.apiKey) return resolved;
64
+ return mergeHeaders$1(resolved, { "x-api-key": options.apiKey });
65
+ }
66
+ function dataQuery(state, options) {
67
+ const query = { q: JSON.stringify(state) };
68
+ if (options?.comparison) query.qc = JSON.stringify(options.comparison);
69
+ if (options?.filter) query.filter = options.filter;
70
+ return query;
71
+ }
72
+ function dataDetailQuery(state, options) {
73
+ const query = { q: JSON.stringify(state) };
74
+ if (options?.comparison) query.qc = JSON.stringify(options.comparison);
75
+ return query;
76
+ }
77
+ function assertAnalysisParams(params) {
78
+ if ((params.preset === "non-brand" || params.preset === "brand-only") && !params.brandTerms?.trim()) throw new Error("brandTerms is required for brand/non-brand presets");
79
+ }
80
+ function analysisQuery(params) {
81
+ const query = {
82
+ preset: params.preset,
83
+ startDate: params.startDate,
84
+ endDate: params.endDate
85
+ };
86
+ if (params.prevStartDate) query.prevStartDate = params.prevStartDate;
87
+ if (params.prevEndDate) query.prevEndDate = params.prevEndDate;
88
+ if (params.brandTerms) query.brandTerms = params.brandTerms;
89
+ if (params.limit != null) query.limit = params.limit;
90
+ if (params.offset != null) query.offset = params.offset;
91
+ if (params.search) query.search = params.search;
92
+ if (params.minImpressions != null) query.minImpressions = params.minImpressions;
93
+ if (params.minPosition != null) query.minPosition = params.minPosition;
94
+ if (params.maxPosition != null) query.maxPosition = params.maxPosition;
95
+ if (params.maxCtr != null) query.maxCtr = params.maxCtr;
96
+ return query;
97
+ }
98
+ function indexingUrlsQuery$1(params = {}) {
99
+ const query = {};
100
+ if (params.limit != null) query.limit = params.limit;
101
+ if (params.offset != null) query.offset = params.offset;
102
+ if (params.status) query.status = params.status;
103
+ if (params.issue) query.issue = params.issue;
104
+ if (params.search) query.search = params.search;
105
+ return query;
106
+ }
107
+ function tablesQuery$1(tables) {
108
+ if (!tables) return void 0;
109
+ return { tables: Array.isArray(tables) ? tables.join(",") : tables };
110
+ }
111
+ function dateRangeQuery(params) {
112
+ return {
113
+ startDate: params.startDate,
114
+ endDate: params.endDate
115
+ };
116
+ }
117
+ function queryTrendQuery(params) {
118
+ const query = {
119
+ startDate: params.startDate,
120
+ endDate: params.endDate
121
+ };
122
+ if (params.prevStartDate) query.prevStartDate = params.prevStartDate;
123
+ if (params.prevEndDate) query.prevEndDate = params.prevEndDate;
124
+ return query;
125
+ }
126
+ function shouldValidate$1(options, phase) {
127
+ return options.validate === true || options.validate === phase;
128
+ }
129
+ function parseWith$1(schema, value) {
130
+ return schema ? schema.parse(value) : value;
131
+ }
132
+ function sleep(ms) {
133
+ return new Promise((resolve) => setTimeout(resolve, ms));
134
+ }
135
+ function createPartnerClient(options = {}) {
136
+ const fetchImpl = options.fetch ?? ofetch;
137
+ const apiBase = trimApiBase$1(options.apiBase);
138
+ async function request(path, init = {}, responseSchema) {
139
+ const headers = mergeHeaders$1(await resolveHeaders$1(options), init.headers);
140
+ try {
141
+ const out = await fetchImpl(buildPath$1(apiBase, path), {
142
+ ...init,
143
+ headers
144
+ });
145
+ return shouldValidate$1(options, "response") ? parseWith$1(responseSchema, out) : out;
146
+ } catch (error) {
147
+ throw toPartnerError(error);
148
+ }
149
+ }
150
+ return {
151
+ registerUser(params) {
152
+ const body = shouldValidate$1(options, "request") ? partnerEndpointSchemas.registerUser.body.parse(params) : params;
153
+ return request(partnerRoutes.users.register, {
154
+ method: "POST",
155
+ body
156
+ }, partnerEndpointSchemas.registerUser.response);
157
+ },
158
+ updateUserTokens(userId, params) {
159
+ const body = shouldValidate$1(options, "request") ? partnerEndpointSchemas.updateUserTokens.body.parse(params) : params;
160
+ return request(partnerRoutes.users.tokens(userId), {
161
+ method: "PATCH",
162
+ body
163
+ }, partnerEndpointSchemas.updateUserTokens.response);
164
+ },
165
+ getUserStatus(userId) {
166
+ return request(partnerRoutes.users.status(userId), {}, partnerEndpointSchemas.getUserStatus.response);
167
+ },
168
+ getUserLifecycle(userId) {
169
+ return request(partnerRoutes.users.lifecycle(userId));
170
+ },
171
+ async waitForUserReady(userId, waitOptions = {}) {
172
+ const attempts = waitOptions.attempts ?? 12;
173
+ const intervalMs = waitOptions.intervalMs ?? 1e3;
174
+ let latest = null;
175
+ for (let attempt = 0; attempt < attempts; attempt++) {
176
+ latest = await request(partnerRoutes.users.status(userId));
177
+ if (latest.status === "ready") return latest;
178
+ if (attempt < attempts - 1) await sleep(intervalMs);
179
+ }
180
+ const err = /* @__PURE__ */ new Error("gscdump user database is still provisioning");
181
+ err.data = latest;
182
+ throw err;
183
+ },
184
+ getUserSites(userId) {
185
+ return request(partnerRoutes.users.sites(userId), {}, partnerEndpointSchemas.getUserSites.response);
186
+ },
187
+ getAvailableSites(userId) {
188
+ return request(partnerRoutes.users.availableSites(userId), {}, partnerEndpointSchemas.getAvailableSites.response);
189
+ },
190
+ registerSite(params) {
191
+ const body = shouldValidate$1(options, "request") ? partnerEndpointSchemas.registerSite.body.parse(params) : params;
192
+ return request(partnerRoutes.partner.sites.register, {
193
+ method: "POST",
194
+ body
195
+ }, partnerEndpointSchemas.registerSite.response);
196
+ },
197
+ bulkRegisterSites(params) {
198
+ const body = shouldValidate$1(options, "request") ? partnerEndpointSchemas.bulkRegisterSites.body.parse(params) : params;
199
+ return request(partnerRoutes.partner.sites.bulkRegister, {
200
+ method: "POST",
201
+ body
202
+ }, partnerEndpointSchemas.bulkRegisterSites.response);
203
+ },
204
+ deleteUser(userId) {
205
+ return request(partnerRoutes.partner.users.byId(userId), { method: "DELETE" }, partnerEndpointSchemas.deleteUser.response);
206
+ },
207
+ deleteSite(siteId) {
208
+ return request(partnerRoutes.sites.byId(siteId), { method: "DELETE" });
209
+ },
210
+ getAnalysisSources(siteId, tables) {
211
+ return request(partnerRoutes.sites.analysisSources(siteId), { query: tablesQuery$1(tables) }, partnerEndpointSchemas.getAnalysisSources.response);
212
+ },
213
+ getSiteSyncStatus(siteId) {
214
+ return request(partnerRoutes.sites.syncStatus(siteId));
215
+ },
216
+ getData(siteId, state, queryOptions) {
217
+ if (shouldValidate$1(options, "request")) {
218
+ partnerEndpointSchemas.getData.state.parse(state);
219
+ partnerEndpointSchemas.getData.options.parse(queryOptions);
220
+ }
221
+ return request(partnerRoutes.sites.data(siteId), { query: dataQuery(state, queryOptions) }, partnerEndpointSchemas.getData.response);
222
+ },
223
+ getDataDetail(siteId, state, queryOptions) {
224
+ if (shouldValidate$1(options, "request")) {
225
+ partnerEndpointSchemas.getDataDetail.state.parse(state);
226
+ partnerEndpointSchemas.getDataDetail.options.parse(queryOptions);
227
+ }
228
+ return request(partnerRoutes.sites.dataDetail(siteId), { query: dataDetailQuery(state, queryOptions) }, partnerEndpointSchemas.getDataDetail.response);
229
+ },
230
+ getAnalysis(siteId, params) {
231
+ assertAnalysisParams(params);
232
+ const query = shouldValidate$1(options, "request") ? partnerEndpointSchemas.getAnalysis.query.parse(params) : params;
233
+ return request(partnerRoutes.sites.analysis(siteId), { query: analysisQuery(query) }, partnerEndpointSchemas.getAnalysis.response);
234
+ },
235
+ getSitemaps(siteId) {
236
+ return request(partnerRoutes.sites.sitemaps(siteId), {}, partnerEndpointSchemas.getSitemaps.response);
237
+ },
238
+ getSitemapChanges(siteId, days = 28) {
239
+ return request(partnerRoutes.sites.sitemapChanges(siteId), { query: { days } }, partnerEndpointSchemas.getSitemapChanges.response);
240
+ },
241
+ submitSitemap(siteId, sitemapUrl, action = "submit") {
242
+ return request(partnerRoutes.sites.sitemaps(siteId), {
243
+ method: "POST",
244
+ body: {
245
+ sitemapUrl,
246
+ action
247
+ }
248
+ });
249
+ },
250
+ refreshSitemaps(siteId) {
251
+ return request(partnerRoutes.sites.sitemaps(siteId), {
252
+ method: "POST",
253
+ body: { action: "refresh" }
254
+ });
255
+ },
256
+ getIndexing(siteId, days = 28) {
257
+ return request(partnerRoutes.sites.indexing(siteId), { query: { days } }, partnerEndpointSchemas.getIndexing.response);
258
+ },
259
+ getIndexingUrls(siteId, params = {}) {
260
+ const parsed = shouldValidate$1(options, "request") ? partnerEndpointSchemas.getIndexingUrls.query.parse(params) : params;
261
+ return request(partnerRoutes.sites.indexingUrls(siteId), { query: indexingUrlsQuery$1(parsed) }, partnerEndpointSchemas.getIndexingUrls.response);
262
+ },
263
+ getIndexingDiagnostics(siteId) {
264
+ return request(partnerRoutes.sites.indexingDiagnostics(siteId), {}, partnerEndpointSchemas.getIndexingDiagnostics.response);
265
+ },
266
+ getUserSettings() {
267
+ return request(partnerRoutes.settings.user, {}, partnerEndpointSchemas.getUserSettings.response);
268
+ },
269
+ patchUserSettings(body) {
270
+ const parsed = shouldValidate$1(options, "request") ? partnerEndpointSchemas.patchUserSettings.body.parse(body) : body;
271
+ return request(partnerRoutes.settings.user, {
272
+ method: "PATCH",
273
+ body: parsed
274
+ }, partnerEndpointSchemas.patchUserSettings.response);
275
+ },
276
+ recoverPermission(siteId) {
277
+ return request(partnerRoutes.sites.recoverPermission(siteId), { method: "POST" }, partnerEndpointSchemas.recoverPermission.response);
278
+ },
279
+ getTopAssociation(siteId, params) {
280
+ const query = shouldValidate$1(options, "request") ? partnerEndpointSchemas.getTopAssociation.query.parse(params) : params;
281
+ return request(partnerRoutes.sites.topAssociation(siteId), { query }, partnerEndpointSchemas.getTopAssociation.response);
282
+ },
283
+ getKeywordSparklines(siteId, params) {
284
+ const body = shouldValidate$1(options, "request") ? partnerEndpointSchemas.getKeywordSparklines.body.parse(params) : params;
285
+ return request(partnerRoutes.sites.keywordSparklines(siteId), {
286
+ method: "POST",
287
+ body
288
+ }, partnerEndpointSchemas.getKeywordSparklines.response);
289
+ },
290
+ getQueryTrend(siteId, params) {
291
+ const query = shouldValidate$1(options, "request") ? partnerEndpointSchemas.getQueryTrend.query.parse(params) : params;
292
+ return request(partnerRoutes.sites.queryTrend(siteId), { query: queryTrendQuery(query) }, partnerEndpointSchemas.getQueryTrend.response);
293
+ },
294
+ getCanonicalMismatches(siteId) {
295
+ return request(partnerRoutes.sites.canonicalMismatches(siteId), {}, partnerEndpointSchemas.getCanonicalMismatches.response);
296
+ },
297
+ getContentVelocity(siteId, days) {
298
+ return request(partnerRoutes.sites.contentVelocity(siteId), { query: days == null ? void 0 : { days } });
299
+ },
300
+ getCtrCurve(siteId, params) {
301
+ const query = shouldValidate$1(options, "request") ? partnerEndpointSchemas.getDateRangeInsight.query.parse(params) : params;
302
+ return request(partnerRoutes.sites.ctrCurve(siteId), { query: dateRangeQuery(query) });
303
+ },
304
+ getDarkTraffic(siteId, params) {
305
+ const query = shouldValidate$1(options, "request") ? partnerEndpointSchemas.getDateRangeInsight.query.parse(params) : params;
306
+ return request(partnerRoutes.sites.darkTraffic(siteId), { query: dateRangeQuery(query) });
307
+ },
308
+ getDeviceGap(siteId, params) {
309
+ const query = shouldValidate$1(options, "request") ? partnerEndpointSchemas.getDateRangeInsight.query.parse(params) : params;
310
+ return request(partnerRoutes.sites.deviceGap(siteId), { query: dateRangeQuery(query) });
311
+ },
312
+ getIndexPercent(siteId, params = {}) {
313
+ const query = shouldValidate$1(options, "request") ? partnerEndpointSchemas.getIndexPercent.query.parse(params) : params;
314
+ return request(partnerRoutes.sites.indexPercent(siteId), { query }, partnerEndpointSchemas.getIndexPercent.response);
315
+ },
316
+ getKeywordBreadth(siteId, params) {
317
+ const query = shouldValidate$1(options, "request") ? partnerEndpointSchemas.getDateRangeInsight.query.parse(params) : params;
318
+ return request(partnerRoutes.sites.keywordBreadth(siteId), { query: dateRangeQuery(query) });
319
+ },
320
+ getPositionDistribution(siteId, params) {
321
+ const query = shouldValidate$1(options, "request") ? partnerEndpointSchemas.getDateRangeInsight.query.parse(params) : params;
322
+ return request(partnerRoutes.sites.positionDistribution(siteId), { query: dateRangeQuery(query) });
323
+ },
324
+ createTeam(params) {
325
+ const body = shouldValidate$1(options, "request") ? partnerEndpointSchemas.createTeam.body.parse(params) : params;
326
+ return request(partnerRoutes.teams.create, {
327
+ method: "POST",
328
+ body
329
+ }, partnerEndpointSchemas.createTeam.response);
330
+ },
331
+ renameTeam(teamId, params) {
332
+ return request(partnerRoutes.teams.byId(teamId), {
333
+ method: "PATCH",
334
+ body: params
335
+ });
336
+ },
337
+ deleteTeam(teamId) {
338
+ return request(partnerRoutes.teams.byId(teamId), { method: "DELETE" });
339
+ },
340
+ listTeamMembers(teamId) {
341
+ return request(partnerRoutes.teams.members(teamId), {}, partnerEndpointSchemas.listTeamMembers.response);
342
+ },
343
+ addTeamMember(teamId, params) {
344
+ const body = shouldValidate$1(options, "request") ? partnerEndpointSchemas.addTeamMember.body.parse(params) : params;
345
+ return request(partnerRoutes.teams.members(teamId), {
346
+ method: "POST",
347
+ body
348
+ }, partnerEndpointSchemas.addTeamMember.response);
349
+ },
350
+ updateTeamMemberRole(teamId, userId, params) {
351
+ return request(partnerRoutes.teams.member(teamId, userId), {
352
+ method: "PATCH",
353
+ body: params
354
+ });
355
+ },
356
+ removeTeamMember(teamId, userId) {
357
+ return request(partnerRoutes.teams.member(teamId, userId), { method: "DELETE" });
358
+ },
359
+ bindSiteToTeam(userId, siteId, params) {
360
+ const body = shouldValidate$1(options, "request") ? partnerEndpointSchemas.bindSiteToTeam.body.parse(params) : params;
361
+ return request(partnerRoutes.partner.users.siteTeam(userId, siteId), {
362
+ method: "PATCH",
363
+ body
364
+ }, partnerEndpointSchemas.bindSiteToTeam.response);
365
+ }
366
+ };
367
+ }
368
+ const createGscdumpClient = createPartnerClient;
369
+ const TRAILING_SLASH_RE$1 = /\/+$/;
370
+ const LEADING_SLASH_RE = /^\/+/;
371
+ function trimApiBase(apiBase) {
372
+ return (apiBase ?? "").replace(TRAILING_SLASH_RE$1, "");
373
+ }
374
+ function buildPath(apiBase, path) {
375
+ if (!apiBase) return path.startsWith("/") ? path : `/${path}`;
376
+ return `${apiBase}/${path.replace(LEADING_SLASH_RE, "")}`;
377
+ }
378
+ function mergeHeaders(base, extra) {
379
+ const headers = new Headers(base);
380
+ if (extra) for (const [key, value] of new Headers(extra).entries()) headers.set(key, value);
381
+ return headers;
382
+ }
383
+ async function resolveHeaders(options) {
384
+ return typeof options.headers === "function" ? await options.headers() : options.headers;
385
+ }
386
+ function shouldValidate(options, phase) {
387
+ return options.validate === true || options.validate === phase;
388
+ }
389
+ function parseWith(schema, value) {
390
+ return schema ? schema.parse(value) : value;
391
+ }
392
+ function tablesQuery(tables) {
393
+ if (!tables) return void 0;
394
+ return { tables: Array.isArray(tables) ? tables.join(",") : tables };
395
+ }
396
+ function indexingUrlsQuery(params = {}) {
397
+ const query = {};
398
+ if (params.limit != null) query.limit = params.limit;
399
+ if (params.offset != null) query.offset = params.offset;
400
+ if (params.status) query.status = params.status;
401
+ if (params.issue) query.issue = params.issue;
402
+ if (params.search) query.search = params.search;
403
+ return query;
404
+ }
405
+ function createAnalyticsClient(options = {}) {
406
+ const fetchImpl = options.fetch ?? ofetch;
407
+ const apiBase = trimApiBase(options.apiBase);
408
+ async function request(path, init = {}, responseSchema) {
409
+ const headers = mergeHeaders(await resolveHeaders(options), init.headers);
410
+ try {
411
+ const out = await fetchImpl(buildPath(apiBase, path), {
412
+ ...init,
413
+ headers
414
+ });
415
+ return shouldValidate(options, "response") ? parseWith(responseSchema, out) : out;
416
+ } catch (error) {
417
+ throw toPartnerError(error);
418
+ }
419
+ }
420
+ return {
421
+ whoami() {
422
+ return request(analyticsRoutes.whoami, {}, partnerEndpointSchemas.analyticsWhoami.response);
423
+ },
424
+ listSites() {
425
+ return request(analyticsRoutes.sites, {}, partnerEndpointSchemas.analyticsSites.response);
426
+ },
427
+ getSourceInfo(siteId) {
428
+ return request(analyticsRoutes.site.sourceInfo(siteId), {}, partnerEndpointSchemas.analyticsSourceInfo.response);
429
+ },
430
+ getAnalysisSources(siteId, tables) {
431
+ return request(analyticsRoutes.site.analysisSources(siteId), { query: tablesQuery(tables) }, partnerEndpointSchemas.analyticsAnalysisSources.response);
432
+ },
433
+ analyze(siteId, params) {
434
+ return request(analyticsRoutes.site.analyze(siteId), {
435
+ method: "POST",
436
+ body: params
437
+ });
438
+ },
439
+ queryRows(siteId, state) {
440
+ return request(analyticsRoutes.site.rows(siteId), {
441
+ method: "POST",
442
+ body: state
443
+ }, partnerEndpointSchemas.analyticsRows.response);
444
+ },
445
+ getRollup(siteId, rollupId, params) {
446
+ return request(analyticsRoutes.site.rollup(siteId, rollupId), { query: params }, partnerEndpointSchemas.analyticsRollup.response);
447
+ },
448
+ requestBackfill(siteId, range) {
449
+ const body = shouldValidate(options, "request") ? partnerEndpointSchemas.analyticsBackfill.body.parse(range) : range;
450
+ return request(analyticsRoutes.site.backfill(siteId), {
451
+ method: "POST",
452
+ body
453
+ }, partnerEndpointSchemas.analyticsBackfill.response);
454
+ },
455
+ getSitemaps(siteId) {
456
+ return request(analyticsRoutes.site.sitemaps(siteId), {}, partnerEndpointSchemas.analyticsSitemaps.response);
457
+ },
458
+ getSitemapHistory(siteId, hash) {
459
+ return request(analyticsRoutes.site.sitemapHistory(siteId, hash), {}, partnerEndpointSchemas.analyticsSitemapHistory.response);
460
+ },
461
+ getSitemapChanges(siteId, params = {}) {
462
+ return request(analyticsRoutes.site.sitemapChanges(siteId), { query: params }, partnerEndpointSchemas.analyticsSitemapChanges.response);
463
+ },
464
+ getInspections(siteId) {
465
+ return request(analyticsRoutes.site.inspections(siteId), {}, partnerEndpointSchemas.analyticsInspections.response);
466
+ },
467
+ getInspectionHistory(siteId, hash) {
468
+ return request(analyticsRoutes.site.inspectionHistory(siteId, hash), {}, partnerEndpointSchemas.analyticsInspectionHistory.response);
469
+ },
470
+ getIndexingUrls(siteId, params = {}) {
471
+ return request(analyticsRoutes.site.indexingUrls(siteId), { query: indexingUrlsQuery(params) }, partnerEndpointSchemas.analyticsIndexingUrls.response);
472
+ },
473
+ getIndexingDiagnostics(siteId) {
474
+ return request(analyticsRoutes.site.indexingDiagnostics(siteId), {}, partnerEndpointSchemas.analyticsIndexingDiagnostics.response);
475
+ }
476
+ };
477
+ }
478
+ const encoder = new TextEncoder();
479
+ function toPayloadString(payload) {
480
+ return typeof payload === "string" ? payload : JSON.stringify(payload);
481
+ }
482
+ function hexToBytes(hex) {
483
+ if (!/^[\da-f]+$/i.test(hex) || hex.length % 2 !== 0) return null;
484
+ const bytes = new Uint8Array(hex.length / 2);
485
+ for (let i = 0; i < bytes.length; i++) bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
486
+ return bytes;
487
+ }
488
+ function constantTimeEqual(a, b) {
489
+ if (a.length !== b.length) return false;
490
+ let diff = 0;
491
+ for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
492
+ return diff === 0;
493
+ }
494
+ function signatureHex(signature) {
495
+ const trimmed = signature.trim();
496
+ return trimmed.startsWith("sha256=") ? trimmed.slice(7) : null;
497
+ }
498
+ async function hmacSha256(payload, secret) {
499
+ const key = await crypto.subtle.importKey("raw", encoder.encode(secret), {
500
+ name: "HMAC",
501
+ hash: "SHA-256"
502
+ }, false, ["sign"]);
503
+ return crypto.subtle.sign("HMAC", key, encoder.encode(payload));
504
+ }
505
+ function toCanonicalWebhookEvent(event) {
506
+ return WEBHOOK_EVENT_ALIASES$1[event] ?? event;
507
+ }
508
+ function serializeWebhookPayload(payload) {
509
+ return toPayloadString(payload);
510
+ }
511
+ async function verifyWebhookSignature(payload, signature, secret) {
512
+ if (!signature) return false;
513
+ const hex = signatureHex(signature);
514
+ if (!hex) return false;
515
+ const received = hexToBytes(hex);
516
+ if (!received) return false;
517
+ return constantTimeEqual(new Uint8Array(await hmacSha256(toPayloadString(payload), secret)), received);
518
+ }
519
+ async function parseWebhookPayload(payload, options = {}) {
520
+ const payloadString = toPayloadString(payload);
521
+ const signature = options.signature ?? readWebhookHeaders(options.headers).signature;
522
+ if (options.secret && (options.validateSignature ?? true)) {
523
+ if (!await verifyWebhookSignature(payloadString, signature, options.secret)) throw new PartnerApiError({
524
+ kind: "auth",
525
+ statusCode: 401,
526
+ message: "Invalid webhook signature"
527
+ });
528
+ }
529
+ const parsed = typeof payload === "string" ? JSON.parse(payload) : payload;
530
+ return partnerWebhookEnvelopeSchema.parse(parsed);
531
+ }
532
+ function readWebhookHeaders(headers) {
533
+ if (!headers) return {
534
+ event: null,
535
+ delivery: null,
536
+ contractVersion: null,
537
+ timestamp: null,
538
+ signature: null
539
+ };
540
+ if (headers instanceof Headers) return {
541
+ event: headers.get(WEBHOOK_EVENT_HEADER$1),
542
+ delivery: headers.get(WEBHOOK_DELIVERY_HEADER$1),
543
+ contractVersion: headers.get(WEBHOOK_CONTRACT_VERSION_HEADER$1),
544
+ timestamp: headers.get(WEBHOOK_TIMESTAMP_HEADER$1),
545
+ signature: headers.get(WEBHOOK_SIGNATURE_HEADER$1)
546
+ };
547
+ return {
548
+ event: headers.event ?? null,
549
+ delivery: headers.delivery ?? null,
550
+ contractVersion: headers.contractVersion ?? null,
551
+ timestamp: headers.timestamp ?? null,
552
+ signature: headers.signature ?? null
553
+ };
554
+ }
555
+ const HTTP_PROTOCOL_RE = /^http/i;
556
+ const API_SUFFIX_RE = /\/api\/?$/;
557
+ const TRAILING_SLASH_RE = /\/+$/;
558
+ function inferWsBase(options) {
559
+ if (options.wsBase) return options.wsBase.replace(TRAILING_SLASH_RE, "");
560
+ if (!options.apiBase) return "";
561
+ return options.apiBase.replace(API_SUFFIX_RE, "").replace(TRAILING_SLASH_RE, "").replace(HTTP_PROTOCOL_RE, (m) => m.toLowerCase() === "https" ? "wss" : "ws");
562
+ }
563
+ function wsPath(scope) {
564
+ return scope === "partner" ? partnerRoutes.realtime.partner : partnerRoutes.realtime.user;
565
+ }
566
+ function buildWsUrl(options) {
567
+ const scope = options.scope ?? "partner";
568
+ const base = inferWsBase(options);
569
+ if (!base) return wsPath(scope);
570
+ return `${base}${wsPath(scope)}`;
571
+ }
572
+ function parseMessage(raw) {
573
+ if (typeof raw !== "string") return null;
574
+ try {
575
+ return JSON.parse(raw);
576
+ } catch {
577
+ return null;
578
+ }
579
+ }
580
+ function isRealtimeEvent(message) {
581
+ return "event" in message && message.event !== "auth.required" && message.event !== "connected";
582
+ }
583
+ function getWebSocketCtor(options) {
584
+ const ctor = options.WebSocket ?? globalThis.WebSocket;
585
+ if (!ctor) throw new Error("WebSocket is not available; pass options.WebSocket");
586
+ return ctor;
587
+ }
588
+ function createPartnerRealtimeClient(options) {
589
+ let socket = null;
590
+ let status = "idle";
591
+ const statusHandlers = /* @__PURE__ */ new Set();
592
+ const messageHandlers = /* @__PURE__ */ new Set();
593
+ const eventHandlers = /* @__PURE__ */ new Set();
594
+ const errorHandlers = /* @__PURE__ */ new Set();
595
+ function setStatus(next) {
596
+ status = next;
597
+ for (const handler of statusHandlers) handler(next);
598
+ }
599
+ function sendJson(payload) {
600
+ if (!socket) throw new Error("Partner realtime client is not connected");
601
+ socket.send(JSON.stringify(payload));
602
+ }
603
+ function authPayload() {
604
+ const payload = {
605
+ type: "auth",
606
+ apiKey: options.apiKey
607
+ };
608
+ if (options.userId != null) payload.userId = options.userId;
609
+ return payload;
610
+ }
611
+ function subscribe(siteIds) {
612
+ sendJson({
613
+ type: "subscribe",
614
+ siteIds
615
+ });
616
+ }
617
+ return {
618
+ get status() {
619
+ return status;
620
+ },
621
+ get socket() {
622
+ return socket;
623
+ },
624
+ connect() {
625
+ if (socket) return socket;
626
+ socket = new (getWebSocketCtor(options))(buildWsUrl(options), options.protocols);
627
+ setStatus("connecting");
628
+ socket.onopen = () => {
629
+ setStatus("open");
630
+ sendJson(authPayload());
631
+ };
632
+ socket.onmessage = (event) => {
633
+ const message = parseMessage(event.data);
634
+ if (!message) return;
635
+ if ("event" in message && message.event === "connected") {
636
+ setStatus("authenticated");
637
+ if (options.siteIds?.length) subscribe(options.siteIds);
638
+ }
639
+ for (const handler of messageHandlers) handler(message);
640
+ if (isRealtimeEvent(message)) for (const handler of eventHandlers) handler(message);
641
+ };
642
+ socket.onerror = (event) => {
643
+ setStatus("error");
644
+ for (const handler of errorHandlers) handler(event);
645
+ };
646
+ socket.onclose = (event) => {
647
+ socket = null;
648
+ setStatus("closed");
649
+ for (const handler of messageHandlers) handler({
650
+ type: "error",
651
+ message: `WebSocket closed: ${JSON.stringify(event)}`
652
+ });
653
+ };
654
+ return socket;
655
+ },
656
+ close(code, reason) {
657
+ socket?.close(code, reason);
658
+ socket = null;
659
+ setStatus("closed");
660
+ },
661
+ ping() {
662
+ sendJson({ type: "ping" });
663
+ },
664
+ subscribe,
665
+ onStatus(handler) {
666
+ statusHandlers.add(handler);
667
+ return () => statusHandlers.delete(handler);
668
+ },
669
+ onMessage(handler) {
670
+ messageHandlers.add(handler);
671
+ return () => messageHandlers.delete(handler);
672
+ },
673
+ onEvent(handler) {
674
+ eventHandlers.add(handler);
675
+ return () => eventHandlers.delete(handler);
676
+ },
677
+ onError(handler) {
678
+ errorHandlers.add(handler);
679
+ return () => errorHandlers.delete(handler);
680
+ }
681
+ };
682
+ }
683
+ const createGscdumpRealtimeClient = createPartnerRealtimeClient;
684
+ export { CANONICAL_WEBHOOK_EVENTS, LEGACY_WEBHOOK_EVENTS, PartnerApiError, VALID_WEBHOOK_EVENTS, WEBHOOK_CONTRACT_VERSION, WEBHOOK_CONTRACT_VERSION_HEADER, WEBHOOK_DELIVERY_HEADER, WEBHOOK_EVENT_ALIASES, WEBHOOK_EVENT_HEADER, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_TIMESTAMP_HEADER, createAnalyticsClient, createGscdumpClient, createGscdumpRealtimeClient, createPartnerClient, createPartnerRealtimeClient, parseWebhookPayload, readWebhookHeaders, serializeWebhookPayload, toCanonicalWebhookEvent, toPartnerError, verifyWebhookSignature };
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@gscdump/sdk",
3
+ "type": "module",
4
+ "version": "0.11.4",
5
+ "description": "Consumer SDK for hosted gscdump.com integrations.",
6
+ "author": {
7
+ "name": "Harlan Wilton",
8
+ "email": "harlan@harlanzw.com",
9
+ "url": "https://harlanzw.com/"
10
+ },
11
+ "license": "MIT",
12
+ "funding": "https://github.com/sponsors/harlan-zw",
13
+ "homepage": "https://github.com/harlan-zw/gscdump/tree/main/packages/sdk#readme",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/harlan-zw/gscdump.git",
17
+ "directory": "packages/sdk"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/harlan-zw/gscdump/issues"
21
+ },
22
+ "sideEffects": false,
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.mts",
26
+ "import": "./dist/index.mjs",
27
+ "default": "./dist/index.mjs"
28
+ }
29
+ },
30
+ "main": "./dist/index.mjs",
31
+ "types": "./dist/index.d.mts",
32
+ "files": [
33
+ "dist"
34
+ ],
35
+ "engines": {
36
+ "node": ">=18"
37
+ },
38
+ "dependencies": {
39
+ "ofetch": "^1.5.1",
40
+ "zod": "^4.4.3",
41
+ "@gscdump/contracts": "0.11.4"
42
+ },
43
+ "devDependencies": {
44
+ "typescript": "^6.0.3",
45
+ "vitest": "^4.1.5"
46
+ },
47
+ "scripts": {
48
+ "build": "obuild",
49
+ "dev": "obuild --stub",
50
+ "typecheck": "tsc --noEmit",
51
+ "test": "vitest"
52
+ }
53
+ }