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

@we-made/pointer-assets-client

v0.1.0

Published

Browser SDK for Pointer asset uploads and CDN-protected downloads

Readme

@we-made/pointer-assets-client

Framework-independent browser SDK for uploading and downloading Pointer/P2 portal assets.

Uploads use short-lived GCS PUT URLs authorized by the Pointer backend. Downloads use stable Cloud CDN URLs protected by an HttpOnly signed cookie. This package never creates signed download URLs and never accepts bucket names or object paths.

Install

npm install @we-made/pointer-assets-client @we-made/firebase-auth-client

Usage

import { FirebaseAuthClient } from "@we-made/firebase-auth-client";
import { PointerAssetsClient } from "@we-made/pointer-assets-client";

const apiBaseUrl = "https://pointer.example.com";

const authClient = new FirebaseAuthClient(
  apiBaseUrl,
  import.meta.env.VITE_POINTER_PORTAL_CONFIG_KEY,
);

await authClient.build();

const assets = new PointerAssetsClient({
  apiBaseUrl,
  tenantId: "your-pointer-tenant-id",
  authClient,
  getCustomerId: () => selectedCustomerId,
});

const file = new File(["hello"], "hello.txt", { type: "text/plain" });
const asset = await assets.createAndUpload({ data: file });

const downloaded = await assets.download(asset.id);

FirebaseAuthClient is not a runtime dependency. Any auth client with this structural interface can be used:

interface AssetAuthClient {
  getAccessToken(): Promise<string | null | undefined>;
}

The authenticated user must have the Pointer r:cportal-user role.

API

new PointerAssetsClient(options)

const assets = new PointerAssetsClient({
  apiBaseUrl: "https://pointer.example.com",
  tenantId: "tenant-id",
  authClient,
});

Options:

  • apiBaseUrl: Pointer API origin/base URL.
  • tenantId: Pointer tenant ID. It is URL-encoded by the SDK.
  • authClient: structural auth client exposing getAccessToken().
  • getCustomerId: optional callback evaluated before every API request. Admin and developer-admin portals should return the currently selected customer ID; portal contacts can omit it because the backend derives their customer ownership.
  • fetch: optional fetch implementation, primarily for controlled runtimes and tests.

The customer ID is sent only as an encoded customerId query parameter. It is never included in create JSON or asset metadata. The backend validates that the authenticated admin can access the selected customer.

refreshSession()

const session = await assets.refreshSession();
console.log(session.expiresAt);

Requests a fresh HttpOnly Cloud CDN cookie using credentials: "include".

create(input)

const created = await assets.create({
  name: file.name,
  mimeType: file.type,
  size: file.size,
  entityType: "projects",
  entityId: "project-123",
});

await assets.upload(created.upload, file);

Returns asset metadata and an opaque, short-lived upload target. size must be between 1 byte and 50 MiB (MAX_UPLOAD_SIZE_BYTES). entityType and entityId are optional, but must be provided together. The backend validates access to the referenced entity and resolves customer ownership; the SDK does not expose ownership or relation internals.

upload(target, data)

Uploads a Blob, ArrayBuffer, or typed-array view directly to the backend-authorized URL. The SDK sends exactly the signed headers returned by the backend, uses credentials: "omit", and never forwards the Pointer access token.

createAndUpload(input)

const asset = await assets.createAndUpload({
  data: file,
  entityType: "projects",
  entityId: "project-123",
});

For a browser File, name, mimeType, and size are inferred. For other binary values, provide name and mimeType:

const asset = await assets.createAndUpload({
  data: arrayBuffer,
  name: "report.pdf",
  mimeType: "application/pdf",
});

get(assetId)

Returns metadata for an owned asset.

download(assetId)

Gets current metadata, refreshes the CDN cookie, and downloads the stable relative asset URL with credentials: "include". The bearer token is not sent to the CDN URL.

delete(assetId)

Deletes the asset. Backend deletion is idempotent and does not disclose whether an asset belongs to another customer.

Errors

Failures are thrown, never returned as successful values:

  • PointerAssetsAuthenticationError: no access token is available.
  • PointerAssetsApiError: a Pointer API request returned a non-2xx response.
  • PointerAssetsUploadError: the signed GCS upload failed.
  • PointerAssetsDownloadError: the CDN download failed.
  • PointerAssetsNetworkError: fetch failed before receiving a response.
  • PointerAssetsInvalidResponseError: a successful backend response did not match the contract.
import {
  PointerAssetsApiError,
  PointerAssetsError,
} from "@we-made/pointer-assets-client";

try {
  await assets.get("asset-id");
} catch (error) {
  if (error instanceof PointerAssetsApiError) {
    console.error(error.statusCode, error.backendError, error.requestId);
  } else if (error instanceof PointerAssetsError) {
    console.error(error.code, error.message);
  }
}

Browser and server configuration

  • The API and CDN must allow the browser origin and credentialed requests.
  • Cookie issuance and CDN downloads require credentials: "include".
  • The Cloud CDN signed cookie is HttpOnly and path-scoped by the backend.
  • Direct uploads require GCS CORS to allow PUT and the backend-issued content-type header.
  • The package uses standard browser fetch, Blob, URL, and File APIs.

Security model

  • Ownership, customer, bucket, and object path are resolved by the backend.
  • The public SDK accepts no bucket or object-path controls.
  • Upload URLs are short-lived and backend-authorized.
  • Asset url values are stable same-origin relative CDN URLs, not signed URLs.
  • API bearer credentials are never sent to upload or CDN requests.

License

MIT