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

@c6fc/spellcraft-gcp-auth

v2.0.0

Published

GCP credentials with impersonation, the googleapis client, and service enablement that resolves your project properly.

Readme

@c6fc/spellcraft-gcp-auth

GCP credentials and the googleapis client for SpellCraft, reachable directly from Jsonnet.

NPM version License

This is what lets a manifest ask GCP a question while it renders — which project it is bound to, which organization that project sits under, which services are live — instead of being handed an answer someone pasted in.

npm install --save @c6fc/spellcraft-gcp-auth
local gcp = import "@c6fc/spellcraft-gcp-auth/module.libsonnet";

{
	"project.json": gcp.getProjectMetadata(),
}

Credentials and the bound project

Authentication uses Application Default Credentials — gcloud auth application-default login, a service account key, or the ambient credentials of whatever you're running on.

The project is resolved separately, in this order:

  1. GOOGLE_CLOUD_PROJECT
  2. GCLOUD_PROJECT
  3. the quota project recorded in your ADC file
  4. gcloud config get-value project

ADC frequently carries no project even when gcloud has one configured, which is why gcloud's own setting is consulted last rather than not at all. If none of the four yields a project, the render stops and names all four fixes.

$ npx spellcraft gcp-identity
{
    "identity": "[email protected]",
    "projectId": "my-project-1234",
    "authType": "User/Authorized Account",
    ...
}

Impersonation

Set SPELLFRAME_GCP_IMPERSONATE to a service account address and the plugin wraps the resolved credentials to act as it, for the whole render.

export SPELLFRAME_GCP_IMPERSONATE="[email protected]"

One identity per process

A process authenticates as exactly one GCP identity, ever. The first successful resolution locks it in (project id plus impersonation target, if any); any later attempt — a second SpellFrame, or the same environment resolving differently on a second call — that would authenticate as a different identity throws, rather than silently replacing the credentials everything else in the process is depending on. The same identity resolving again is a no-op, not an error.

This isn't a technical ceiling so much as a deliberate one: a spell needing a different GCP project under the same identity should reach for providerAliases() (the same mechanism that already covers multiple regions), not re-authenticate. A genuinely different identity needs a separate process — which, for credentials, is the stronger isolation boundary anyway.

Pinning the project

{
	assertions:: gcp.assertProject("my-project-1234"),

	"main.tf.json": { ... },
}

assertions:: is a SpellCraft convention, not just a hidden field that happens to be read somewhere: SpellFrame forces it to evaluate before anything is written, whether or not any other field references it. See core's README for how and why.

Enabling services during a render

Terraform cannot enable the API that a resource it is creating depends on — the provider needs it live before it can plan. enableServices() closes that gap by turning services on while the manifest evaluates.

You mostly won't call it directly. api(), and listBuckets()/listInstances() built on it, enable their own service internally before making their call:

{ "instances.json": gcp.listInstances({ project: gcp.getProjectId(), zone: "us-west1-b" }) }

No enableServices() call, no threading a return value through — this works with nothing else in the manifest. Every one of these calls, from every function, checks a process-wide cache first, so the same service being needed by ten different calls costs one real check, not ten.

The one real cost, and it's a property of the underlying GCP API rather than of this plugin: activating a service that's never been enabled before waits ~15 seconds for IAM/quota propagation, once per call that activates something new. A manifest touching many different never-before-enabled services across many separate calls pays that wait once per service rather than once overall — noticeable on a cold first run against a project, free on every run after, since confirmed services stay confirmed for the life of the process.

For a native function that doesn't self-enable this way — a different plugin's, or a hand-written one — call enableServices() yourself first, and thread the result through, since Jsonnet's evaluation order guarantees nothing here:

local ready = gcp.enableServices(["compute.googleapis.com"]);
{ "instances.json": if ready then someOtherPlugin.rawThing() else null }

For services needed at apply time rather than render time, @c6fc/spellcraft-gcp-terraform collects them during evaluation and flushes them on @c6fc/spellcraft-terraform:pre-apply, so the bootstrap orders itself.

Calling any API

api() reaches the whole of googleapis by dot-delimited path — service, version, method:

gcp.api("cloudresourcemanager.v1.projects.list", {})
gcp.api("storage.v1.buckets.list", { project: gcp.getProjectId() })

listBuckets() and listInstances() are shorthands over it. All three default params to { project: getProjectId() }, and supplying your own params replaces that default — so pass project alongside anything else the method needs.

CLI Commands

  • spellcraft gcp-identity Display the GCP identity of the SpellCraft execution context

What it contributes to a SpellFrame

  • init() — resolves the project, builds the auth client, applies impersonation, and sets the client as the googleapis default.
  • functionContext.google — the authenticated googleapis module, available as this.google inside any plugin's native functions. This is the seam @c6fc/spellcraft-gcp-terraform uses to reuse these credentials rather than authenticating again.

API Reference

getProjectId()

The project this render is bound to.

Resolved once during init() from GOOGLE_CLOUD_PROJECT, GCLOUD_PROJECT, the ADC file's quota project, then gcloud's configured project — so it costs no API call.

  • returns {string} the project id

Examples:

local gcp = import "@c6fc/spellcraft-gcp-auth/module.libsonnet";

{ "project.json": { id: gcp.getProjectId() } }

getProjectMetadata()

The project's place in the resource hierarchy, and how it is billed.

Enables cloudbilling.googleapis.com on the project if it isn't already, so that the billing account can be read back.

  • returns {object} { projectId, quotaProject, organizationId, organizationDomain, directoryId, billingAccount } — the organization and billing fields are false when the project has none

Examples:

local gcp = import "@c6fc/spellcraft-gcp-auth/module.libsonnet";

{ "billing.json": gcp.getProjectMetadata() }

getCallerIdentity()

The identity this render is authenticated as.

  • returns {object} { identity, projectId, scopes, expiresIn, authType, impersonatedBy }

Examples:

local gcp = import "@c6fc/spellcraft-gcp-auth/module.libsonnet";

{ "identity.json": gcp.getCallerIdentity() }

enableServices(services)

Enables API services during the render, so they are live before any tool runs. This is the answer to the stage-zero problem: Terraform cannot enable the API that a resource it is creating depends on.

api(), listBuckets() and listInstances() already call this internally for their own service, so you don't need to call it yourself before using them. Reach for this directly when a manifest calls a native function from elsewhere -- a different plugin, or a hand-written one -- that doesn't self-enable the way this plugin's own functions do. In that case, Jsonnet evaluates lazily and in no guaranteed field order, so the call that needs the service enabled must depend on the result rather than merely follow it -- thread the return value through:

local ready = gcp.enableServices(["compute.googleapis.com"]);
{ "instances.json": if ready then someOtherPlugin.rawThing() else null }

Already-enabled services are left alone, and confirmed ones are cached for the life of the process, so calling this -- from as many places as you like, including indirectly through api() -- is cheap and safe to repeat. The one cost worth knowing: activating a service that's never been enabled before waits ~15s for IAM/quota propagation, once per call that activates something new -- so many separate calls each activating one new service pay that wait separately, rather than once. This only ever affects the first time a given process touches a given service.

  • param {string[]} services - fully qualified service names
  • returns {boolean} true once every requested service is enabled

Examples:

local gcp = import "@c6fc/spellcraft-gcp-auth/module.libsonnet";

{ "instances.json": gcp.listInstances({
    project: gcp.getProjectId(),
    zone: "us-west1-b",
  }) }

api(fullpath, params={ project: gcp.getProjectId() })

Calls any googleapis method and returns its response.

The path is dot-delimited: service, version, then the method path — so compute.v1.instances.list or storage.v1.buckets.list.

Enables <service>.googleapis.com first, best-effort -- that matches Google's own naming convention for the overwhelming majority of services, so most calls need nothing else. It isn't guaranteed for every service, but a wrong or nonexistent guess never blocks the call itself, and a correct one is free after the first time (see enableServices()). Call enableServices() yourself first for anything this guess doesn't cover.

Passing params replaces the default entirely, so add project back when the method needs it alongside anything else you supply.

  • param {string} fullpath - <service>.<version>.<...method>
  • param {object} [params={ project: gcp.getProjectId() }] - request parameters
  • returns {object} the API response body

Examples:

local gcp = import "@c6fc/spellcraft-gcp-auth/module.libsonnet";

{ "projects.json": gcp.api("cloudresourcemanager.v1.projects.list", {}) }

listBuckets(params={ project: gcp.getProjectId() })

Cloud Storage buckets in the project.

  • param {object} [params={ project: gcp.getProjectId() }] - request parameters
  • returns {object} a storage#buckets response

Examples:

local gcp = import "@c6fc/spellcraft-gcp-auth/module.libsonnet";

{ "buckets.json": gcp.listBuckets() }

listInstances(params={ project: gcp.getProjectId() })

Compute instances in one zone.

A zone is required, and supplying it replaces the default params — so pass project as well. Enables compute.googleapis.com itself, via api() -- nothing to enable yourself first.

  • param {object} [params={ project: gcp.getProjectId() }] - must include zone
  • returns {object} a compute#instanceList response

Examples:

local gcp = import "@c6fc/spellcraft-gcp-auth/module.libsonnet";

{ "instances.json": gcp.listInstances({
    project: gcp.getProjectId(),
    zone: "us-west1-b",
  }) }

assertProject(expectedId)

Aborts the render unless the bound project matches.

A guard for spells that must only ever run against one project.

  • param {string} expectedId - the project the render requires
  • returns {string} the same id, so it can be bound or discarded

Examples:

local gcp = import "@c6fc/spellcraft-gcp-auth/module.libsonnet";

// `assertions::` is a SpellCraft convention: SpellFrame forces it to
// evaluate before anything is written, whether or not anything else
// in the manifest references it.
{
    assertions:: gcp.assertProject("my-project-1234"),

    "main.tf.json": { ... },
}

Development

npm test        # renders test.jsonnet through a real SpellFrame
npm run cli     # exercises this plugin's CLI commands
npm run doc     # regenerates the two sections above from source comments

npm test needs GCP credentials and a bound project. It writes in one respect: getProjectMetadata() enables cloudbilling.googleapis.com so the billing account can be read, and the fixture enables compute.googleapis.com.

License

MIT © Brad Woodward