npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@foundrynorth/google-ads

v0.1.0

Published

Typed Google Ads API client for Foundry North SEM publishing

Readme

@foundrynorth/google-ads

Typed Google Ads API client for Foundry North SEM publishing. Wraps the google-ads-api Node client with:

  • Strongly-typed mutate builders for every entity we publish (campaigns, budgets, ad groups, keywords, RSAs, geo/language/device targeting, conversion actions and links).
  • Atomic mutate executor with partial_failure: false, explicit retry classification, and a per-customer circuit breaker from @foundrynorth/core/resilience.
  • GAQL query wrapper exposing a streaming AsyncIterable plus a bounded buffered queryAll.
  • Sandbox-vs-production mode routing that fails closed before any network I/O if a foreign customer ID leaks into sandbox mode.

Environment variables

| Variable | Required | Description | |---|---|---| | GOOGLE_ADS_DEVELOPER_TOKEN | always | Google Ads developer token (approved via Google Ads API Center). | | GOOGLE_ADS_CLIENT_ID | always | OAuth 2.0 client ID. | | GOOGLE_ADS_CLIENT_SECRET | always | OAuth 2.0 client secret. | | GOOGLE_ADS_REFRESH_TOKEN | always | Long-lived refresh token obtained during OAuth consent. | | GOOGLE_ADS_MCC_CUSTOMER_ID | always | 10-digit Manager Account (MCC) ID; dashes are stripped. | | GOOGLE_ADS_LOGIN_CUSTOMER_ID | optional | Overrides the login-customer-id header; defaults to the MCC. | | GOOGLE_ADS_MODE | optional | sandbox (default) or prod. | | GOOGLE_ADS_CUSTOMER_ID_SANDBOX | when mode=sandbox | 10-digit sandbox customer ID. | | GOOGLE_ADS_CUSTOMER_ID_PROD | when mode=prod | 10-digit production customer ID. | | GOOGLE_ADS_SANDBOX_ALLOWLIST | optional | Comma-separated extra sandbox customer IDs (dashes stripped). |

Missing any required variable throws GoogleAdsConfigError immediately — there is no "disabled" no-op mode. Publishing must fail closed.

Subpath exports

| Import | Surface | |---|---| | @foundrynorth/google-ads/auth | createGoogleAdsClient, readGoogleAdsEnv, resolveActiveCustomerId, normalizeCustomerId. | | @foundrynorth/google-ads/mutate | All 9 builders + buildMutateBatch + entity-ordering table. | | @foundrynorth/google-ads/execute | executeMutateAtomic, dryRunMutate. | | @foundrynorth/google-ads/query | query<T> AsyncIterable + queryAll<T>. | | @foundrynorth/google-ads/sandbox | assertSandboxSafety. | | @foundrynorth/google-ads/errors | Error taxonomy (GoogleAdsError + 6 subclasses). | | @foundrynorth/google-ads/types | Spec types + branded IDs + ExecuteResult. |

Minimal example

import { createGoogleAdsClient } from "@foundrynorth/google-ads/auth";
import {
  buildCampaignBudgetMutate,
  buildCampaignMutate,
  buildMutateBatch,
} from "@foundrynorth/google-ads/mutate";
import { executeMutateAtomic } from "@foundrynorth/google-ads/execute";

const client = createGoogleAdsClient();

const budget = buildCampaignBudgetMutate({
  resourceName: `customers/${client.config.activeCustomerId}/campaignBudgets/-1`,
  name: "Brand Search Budget",
  amountMicros: 50_000_000,
  deliveryMethod: "STANDARD",
});

const campaign = buildCampaignMutate({
  name: "Brand Search",
  advertisingChannelType: "SEARCH",
  status: "PAUSED",
  campaignBudgetResourceName: budget.resource.resource_name!,
  biddingStrategy: { kind: "manual_cpc", enhancedCpcEnabled: false },
});

const result = await executeMutateAtomic(
  client.config.activeCustomerId,
  buildMutateBatch([campaign, budget]),
);
console.log(result.resourceNames);

Sandbox safety

When GOOGLE_ADS_MODE=sandbox, assertSandboxSafety (invoked at the top of executeMutateAtomic, dryRunMutate, and query) rejects any customer ID that is neither GOOGLE_ADS_CUSTOMER_ID_SANDBOX nor present in GOOGLE_ADS_SANDBOX_ALLOWLIST. A stray production ID in a dev environment throws GoogleAdsSandboxViolation before any network traffic.

Retry and breaker behavior

  • Retried (GoogleAdsTransientError): DEADLINE_EXCEEDED, UNAVAILABLE, INTERNAL, ABORTED, RESOURCE_EXHAUSTED, rate-limit, quota, concurrent-modification. Retries use the API_RETRY preset from @foundrynorth/core/resilience (3 attempts, 2s–15s exponential backoff, jitter).
  • Thrown immediately (GoogleAdsRequestError): INVALID_ARGUMENT, FAILED_PRECONDITION, NOT_FOUND, and any other client-side spec violation. Fix the input; no retry helps.
  • Thrown immediately (GoogleAdsAuthError): UNAUTHENTICATED, PERMISSION_DENIED, INVALID_CREDENTIALS. Likely refresh-token rot; the Trigger task orchestrator should alert the on-call.
  • Circuit breaker is keyed per customer ID (google-ads:<id>). One exhausted customer cannot block mutate traffic on unrelated customers. Defaults: 5 failures / 60s reset / 5-minute monitoring window.

query uses INTEGRATION_RETRY (5 attempts, 1s–30s) instead of API_RETRY — reporting volume justifies a longer retry budget.

What is NOT in this package (deferred)

  • Performance Max asset groups — out of scope for the Search launch phase. Add alongside Performance Max rollout.
  • Smart Shopping / Demand Gen / DV360 — different API surfaces.
  • Recommendation / Experiment / Asset Library mutations — Track C/D will read via query but not mutate in phase 1.
  • Offline Conversion uploads — separate endpoint; will land when offline-conversion import ships.
  • CSV export — already provided by fn-flux; no duplicate here.
  • LLM routing — this package is pure Google Ads; callLLM belongs in the orchestration layer.

Until Google approves the Foundry North developer token and GOOGLE_ADS_DEVELOPER_TOKEN is populated in the target environment, every surface fails closed with GoogleAdsConfigError. The integration test in __tests__/integration/auth.integration.test.ts automatically skips when the variable is absent.

Publish workflow

pnpm build
pnpm test
npm version patch
npm publish

pnpm build runs tsc; pnpm test runs vitest with v8 coverage at the thresholds lines=80 / functions=80 / branches=70 / statements=80. npm publish is gated on prepublishOnly running pnpm build first.