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

@suphark/config-client

v0.15.0

Published

Pi Config CLI/SDK: ดึง env ของแอปจาก Pi Config มาเขียน .env.local ตอน build (pi-config pull)

Readme

@suphark/config-client

CLI และ SDK ของ Pi Config — deployment ของแอปถือความลับตัวเดียวคือ PI_CONFIG_TOKEN ค่าที่เหลือดึงตอน build แล้วเขียนลง .env.local ให้

pnpm add -D @suphark/config-client   # npm i -D … / yarn add -D … / bun add -d …

แพ็กเกจไม่ผูกกับตัวจัดการแพ็กเกจไหน — dependency ตอนรันมีแค่ zod

ใช้ในเครื่อง

วาง token ไว้บรรทัดแรกของ .env.local (ไฟล์ที่ gitignore อยู่แล้ว) แล้ว pull

PI_CONFIG_TOKEN=pct_…
pnpm pi-config pull

ค่าจะถูกต่อท้ายไฟล์ในช่วง marker — ไม่ต้องตั้งตัวแปรใน terminal (ถ้าตั้ง PI_CONFIG_TOKEN ใน environment ไว้ด้วย ตัวใน environment ชนะ)

ใช้กับ Vercel / CI

ตั้ง env สองตัวที่ deployment (คนละ token ต่อ environment)

PI_CONFIG_TOKEN=pct_…
PI_CONFIG_URL=https://config.suphark.com   # ไม่ตั้งก็ได้ ค่าเริ่มต้นเป็นตัวนี้

แล้วเรียกก่อน build

{ "buildCommand": "pnpm pi-config pull && pnpm next build" }

ตัวอื่น: npx pi-config pull && npx next build · yarn pi-config pull && yarn next build · bunx pi-config pull && bunx next build

ถ้าใช้ pull --replace: ไฟล์เดิมจะถูกสำรองเป็น .env.local.bak ซึ่งมีความลับชุดเดียวกัน .gitignore มาตรฐานของ Next มี .env*.local ซึ่ง ไม่ ครอบไฟล์นี้ — เติม .env*.bak ด้วย (CLI ตั้งแต่ 0.5.0 ปฏิเสธการสำรองจนกว่าจะเติม หรือสั่ง --no-backup)

ลบ env เดิมออกจาก dashboard ให้หมด — process.env ชนะไฟล์ .env* เสมอ ถ้าไม่ลบ การย้ายจะดู "สำเร็จ" ทั้งที่ยังใช้ค่าเดิม

คีย์ที่ค่าจริงต้องอยู่ที่ Vercel

คีย์ที่ Vercel เองเป็นคนอ่านหรือตั้ง จาก env ของโปรเจกต์ Vercel — ไม่ใช่แอปของคุณ ค่าจริงต้องตั้งที่ Vercel เสมอ

เก็บไว้ใน Pi Config เป็นเอกสาร (ว่าแอปนี้ต้องมีคีย์นี้) ได้ ถ้าติ๊ก "ไม่เขียนค่านี้ลงไฟล์ .env" — Pi Config จะไม่ส่งค่ามาให้ pull เขียนทับ แม้แต่ CLI รุ่นเก่า (CLI ≥ 0.11.0 บอกชื่อคีย์พวกนี้ใน build log และเตือนบน Vercel ถ้าลืมตั้งที่ Vercel env):

| คีย์ | ทำไม | |---|---| | CRON_SECRET | Vercel Cron แนบ Authorization: Bearer <CRON_SECRET> จาก env ของโปรเจกต์ Vercel ถ้าเก็บใน Pi Config ด้วย ค่าที่ pull ลง .env.local ตอน build จะทับค่าในฟังก์ชัน → cron ได้ 401 ทุกครั้งแม้ redeploy (เจอจริงที่ pi-done) · ตั้งค่าที่ Vercel แล้วติ๊ก "ไม่เขียนลงไฟล์" ใน Pi Config → redeploy → 200 | | VERCEL_* | system env ที่ Vercel ตั้งให้ทุก deployment (VERCEL_URL, VERCEL_ENV, …) ทับแล้วข้อมูลของ deployment ผิด | | PI_CONFIG_TOKEN, PI_CONFIG_URL | ตัวที่ใช้ดึงค่าเอง — วนลูป |

pi-config pull เตือนใน build log เมื่อเจอคีย์เหล่านี้ที่ยังไม่ได้ติ๊ก (CLI ≥ 0.10.1) และหน้าเว็บเตือนตอนเพิ่มคีย์

คำสั่ง

| คำสั่ง | ทำอะไร | |---|---| | pi-config pull | เขียนค่าลง .env.local เฉพาะช่วงที่ pi-config ดูแล (บันทึก local cache เข้ารหัสเสมอ) | | pi-config pull --offline | ดึงค่าจาก local cache โดยไม่ออกเน็ต (หรือตั้ง PI_CONFIG_OFFLINE=1) | | pi-config pull --check | ไม่เขียนไฟล์ ออก exit 1 ถ้าไฟล์ไม่ตรงกับค่าล่าสุด (ใช้ใน CI) | | pi-config pull --print | พิมพ์ออก stdout แทนการเขียนไฟล์ — มีค่าความลับอยู่ในนั้น | | pi-config pull --replace | เขียนทับทั้งไฟล์เมื่อไฟล์เดิมไม่มี marker (สำรองเป็น .bak ให้ก่อน) | | pi-config pull --file <path> | เปลี่ยนไฟล์ปลายทาง (หรือ --file=<path>) | | pi-config pull --cache-path <p> | กำหนดที่อยู่ไฟล์ local cache (ค่าเริ่มต้น .next/cache/pi-config/cache.bin) | | pi-config pull --replace --no-backup | เหมือน --replace แต่ไม่สำรอง .bak (ไฟล์เดิมหายถาวร) | | pi-config init [--dry-run] | ตั้งค่าแอปใหม่ครั้งเดียว: .gitignore, PI_CONFIG_TOKEN=, build (ไม่ต้องมี token) | | pi-config types | สร้าง pi-config-env.d.ts ให้ process.env.X มี type (มีเฉพาะชื่อคีย์) | | pi-config run -- <คำสั่ง> | รันคำสั่งโดยใส่ค่าที่ pull ไว้ลง env (ดูหัวข้อ "รันคำสั่งที่ต้องใช้ค่า") | | pi-config run --only production -- <คำสั่ง> | เหมือนบน แต่รันเฉพาะ build ของ production (VERCEL_ENV=production) | | pi-config --help / --version | วิธีใช้ / รุ่นของ CLI | | pi-config status | token นี้ผูกกับโปรเจกต์/environment ไหน มีกี่คีย์ เวอร์ชันอะไร (รองรับ --offline) | | pi-config diff | บอก ชื่อ คีย์ที่ต่างจากไฟล์ (ไม่แสดงค่า, รองรับ --offline) |

exit code: 0 เรียบร้อย · 1 ไฟล์ไม่ตรงกับค่าล่าสุด · 2 ใช้คำสั่งผิดหรือไม่มี token · 3 เซิร์ฟเวอร์/เครือข่ายมีปัญหา

ตั้งค่าแอปใหม่ครั้งเดียว (pi-config init)

npx pi-config init --dry-run   # ดูก่อนว่าจะแก้อะไร (ไม่เขียนอะไร)
npx pi-config init

ไม่ต้องมี token — ตั้งค่าก่อนออก token ได้ เติมเฉพาะของที่ขาด ไม่ทับของที่มี และรันซ้ำได้โดยไม่เปลี่ยนอะไร:

| ไฟล์ | ทำอะไร | |---|---| | .gitignore | เติม .env*.local, .env*.bak (กับดัก: --replace สำรองเป็น .env.local.bak ซึ่ง .gitignore มาตรฐานของ Next ไม่ครอบ), .next/cache/pi-config/, pi-config-env.d.ts — ถาม git check-ignore ก่อน ถ้า ignore อยู่แล้วด้วย pattern อื่น (เช่น .env* หรือที่ .gitignore ของรากใน monorepo) จะไม่เติมซ้ำ | | .env.local | เพิ่ม PI_CONFIG_TOKEN= (ว่าง — วาง token ที่ออกจากหน้า environment) · มีอยู่แล้วไม่แตะ | | package.json | ให้ build เรียก pi-config pull นำหน้า (แก้เฉพาะบรรทัดนั้น คงรูปแบบเดิมของไฟล์) · ถ้ามีแล้วหรือรูปแบบไม่ตรง จะบอกให้ทำเอง · ถ้า vercel.json มี buildCommand จะไม่แตะ (Vercel ไม่ใช้ scripts.build) — ดูว่า buildCommand เรียก pi-config อยู่ไหม รวมถึงผ่าน script เช่น bun run config:pull |

ไม่แตะ vercel.json (ไฟล์ตั้งค่า Vercel) — พิมพ์สิ่งที่ต้องทำเองที่ Vercel แทน (ตั้ง PI_CONFIG_TOKEN, ลบ env เดิม, ใส่ pi-config pull ใน buildCommand ถ้ายังไม่มี)

ให้ process.env.X มี type (pi-config types)

npx pi-config types

สร้าง pi-config-env.d.ts จากชื่อคีย์ของ environment ที่ token ผูกอยู่ — process.env.DATABASE_URL เป็น string (ไม่ใช่ string | undefined) และพิมพ์ชื่อผิด (DATABSE_URL) แล้ว typecheck ล้ม แทนที่จะเป็น undefined ตอนรัน

  • มีเฉพาะชื่อคีย์ ไม่มีค่า (แม้เป็นความลับ) และไม่เอาหมายเหตุของคีย์ไป
  • ไม่ commit — ใส่ไว้ใน .gitignore (pi-config init เติมให้): ไฟล์มาจาก environment ที่ token ในเครื่องนั้น ผูกอยู่ (มักเป็น development) แต่ละเครื่อง/แต่ละคนจึงได้ไม่เหมือนกัน commit แล้ว diff ไม่หยุด · ไม่มีไฟล์นี้ typecheck ยังผ่าน (แค่ไม่ได้ตัวช่วยจับชื่อผิด) — ทุกแอปของเราทำแบบนี้ตั้งแต่ 2026-10-23
  • ทำให้คีย์ในรายการเป็น string ไม่ optional → เทสต์ที่ delete process.env.X จะฟ้อง TS2790 ให้ใช้ Reflect.deleteProperty(process.env, "X")
  • ครอบทุกคีย์ที่แอปต้องมี: ที่ pull ลงไฟล์ + คีย์ที่ตั้งเองที่ deployment (externalKeys) + คีย์ที่แพลตฟอร์มฉีดให้ (unsyncedKeys)
  • เพิ่ม/ลบคีย์ใน Pi Config แล้วรันใหม่ · --file <path> เปลี่ยนที่อยู่ไฟล์ · ต้องมี pi-config-env.d.ts อยู่ใน include ของ tsconfig.json

รันคำสั่งที่ต้องใช้ค่า (prisma migrate ฯลฯ)

prisma, drizzle-kit และสคริปต์อื่นไม่รู้จักไฟล์ .env.local ที่ pull เขียน — ไม่ต้องเขียนตัวอ่านเอง ใช้ run:

{
  "scripts": {
    "build": "pi-config pull && pi-config run --only production -- prisma migrate deploy && next build",
    "db:migrate": "pi-config run -- prisma migrate deploy"
  }
}

prisma.config.ts เหลือแค่ url: process.env.DATABASE_URL ไม่ต้อง dotenv ไม่ต้องอ่านไฟล์เอง

  • ค่าตรงทุกไบต์: ใช้ตัวอ่านเดียวกับที่เขียนไฟล์ — ค่าที่มี $, ${…}, หลายบรรทัด, เครื่องหมายคำพูด, ภาษาไทย ถึงคำสั่งลูกครบ (dotenv ไม่แปลง \$ กลับเป็น $ จึงทำให้รหัสผ่านที่มี $ ผิดเงียบ ๆ)
  • env ที่ตั้งไว้แล้วชนะ: ค่าที่ Vercel/CI ตั้งเองไม่ถูกทับ · run ไม่ยิงเครือข่าย (ต้อง pull มาก่อน — ไม่พบค่าที่ pull = exit 2)
  • --only production: migrate ตอน build ของ branch ที่ยังไม่ merge (preview) ลงฐานที่ preview ชี้ — มักเป็นฐาน production จึงรันเฉพาะ VERCEL_ENV=production · preview/development ข้ามพร้อมบอกใน log · อยู่บน Vercel แต่ไม่มี VERCEL_ENV = หยุด (exit 2) ไม่ใช่ข้าม เพราะถ้า Vercel เปลี่ยนชื่อตัวแปร production จะข้าม migrate โดยไม่มีใครรู้
  • ไม่ผ่าน shell: argument ไม่ถูกตีความ · exit code ของคำสั่งลูกถูกส่งกลับ · Windows ที่เรียก .cmd (pnpm/npm/prisma) argument ที่มี % ถูกปฏิเสธ

การรับมือตอน Pi Config ล่ม / ใช้งานออฟไลน์

CLI จะบันทึก cache เข้ารหัส (AES-256-GCM ผูกกับ sha256(PI_CONFIG_TOKEN)) ไว้ที่ .next/cache/pi-config/cache.bin ทุกครั้งที่ pull สำเร็จ (Vercel build cache จะเก็บโฟลเดอร์นี้ไว้ข้าม build ให้เอง):

  • PI_CONFIG_STALE_CACHE=1: หาก Pi Config ล่มหรือเน็ตมีปัญหาตอน build (502/503/504 หรือเครือข่าย) จะ fallback ไปใช้ค่าจาก local cache อัตโนมัติ พร้อมเตือนชัดเจนใน build log
  • PI_CONFIG_OFFLINE=1 หรือ pi-config pull --offline: ทำงานออฟไลน์จาก cache โดยไม่ส่ง request ออกเน็ตเลย

ช่วงที่ pi-config ดูแล

เขียนเฉพาะระหว่าง marker — บรรทัดอื่นในไฟล์ไม่ถูกแตะ

PI_CONFIG_TOKEN='ของคุณเอง'          ← ไม่ถูกแตะ

# >>> pi-config pull — do not edit (project=ui env=development v=sha256:1f3a… at=2026-09-24T…)
APP_URL='http://localhost:3200'
DATABASE_URL='postgres://u:p\$ss@host/db'
# <<< pi-config pull

# บรรทัดของคุณท้ายไฟล์             ← ไม่ถูกแตะ

ถ้าไฟล์มีแค่ PI_CONFIG_TOKEN / PI_CONFIG_URL (และ comment) จะต่อท้ายให้เลย ถ้าไฟล์มีคีย์อื่นอยู่แล้วแต่ยังไม่มี marker คำสั่งจะปฏิเสธ จนกว่าจะใส่ --replace (สำรอง .bak ให้ก่อน) ค่าที่เหมือนเดิมทั้งชุดจะไม่ถูกเขียนซ้ำ ไฟล์จึงไม่เปลี่ยนทุก build

เรื่องที่ระวังให้แล้ว

  • $ ในค่า — @next/env รัน dotenv expand() ซึ่งจะแทน $VAR ด้วยค่าจาก process.env ตัว render จึง escape $ ให้เสมอ
  • \ ท้ายค่า / " ในค่า — เลือกเครื่องหมายคำพูดตามเนื้อค่า และปฏิเสธค่าที่เขียนแล้วอ่านกลับไม่ตรงตั้งแต่ตอนบันทึกในเว็บ
  • .env.production.local — ถ้ามีไฟล์นี้อยู่ Next จะอ่านก่อน .env.local ตอน build production → CLI เตือนให้
  • ไฟล์ไม่ถูก gitignore → เตือน
  • เขียนไฟล์ชั่วคราวแล้ว rename ไฟล์ปลายทางจึงไม่เหลือครึ่งเดียวถ้าดับกลางคัน
  • ไม่มีคำสั่งไหนพิมพ์ "ค่า" ออกมา ยกเว้น --print ที่สั่งเอง

ใช้เป็นไลบรารี

import { fetchConfig, fetchWhoami } from "@suphark/config-client";

const result = await fetchConfig({ token, url });
if (result.status === 200) {
  console.log(Object.keys(result.data.values), result.data.version);
}