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

pi-kiosk-shared

v2.3.25

Published

Shared types, API contracts, and error classes for Pi Kiosk system

Readme

pi-kiosk-shared

Shared types, API contracts, and error classes for the Pi Kiosk system.

Entries

  • Main entry pi-kiosk-shared: Node-safe contracts, types, and API helpers for backend and frontends. Does not re-export React UI.
  • pi-kiosk-shared/ui: React primitives and hooks (Button, useSubmitCooldown, DatabaseUnavailable, CatalogImagePlaceholder, ProviderIcon, …). Requires the React peer. Backends that never import /ui may omit React only after they consume a Node-safe main barrel — see Temporary retention and up-backend/docs/DEPLOYMENT/DEPLOY_SEPARATE_REPOS.md.

Temporary retention

up-backend keeps production react / react-dom until cold/start paths consume a Node-safe published or overlaid main barrel. Do not drop those deps until that consume path is proven on the version actually installed (registry tarball from npm view, or monorepo overlay of sibling ../shared 2.3.10). Same caveat as up-backend/docs/DEPLOYMENT/DEPLOY_SEPARATE_REPOS.md.

Installation

npm install pi-kiosk-shared

Usage

Types

import type { Product, ApiResponse, KioskStatus, TransactionStatus } from 'pi-kiosk-shared';

const product: Product = {
  id: 1,
  name: 'Coffee',
  price: 25.0,
  description: 'Fresh coffee',
  image: '☕',
  clickedOn: 0,
  numberOfPurchases: 0,
};

API Client

import { APIClient, createAPIClient, API_ENDPOINTS } from 'pi-kiosk-shared';

const apiClient = createAPIClient('http://localhost:3015');
const products = await apiClient.get(API_ENDPOINTS.PRODUCTS);

Error Classes

import { NetworkError, ValidationError, AppError, getErrorMessage } from 'pi-kiosk-shared';

try {
  // ... some operation
} catch (error) {
  if (error instanceof NetworkError) {
    console.error('Network error:', getErrorMessage(error));
  }
}

React UI (pi-kiosk-shared/ui)

import { Button, useSubmitCooldown } from 'pi-kiosk-shared/ui';

Frontends import React modules from /ui. The target main barrel (local overlay / published Node-safe releases) is Node-safe. Until that barrel is what cold/start paths consume, see Temporary retention.

What's in this package vs apps

  • pi-kiosk-shared: contracts, types, API helpers — shared by backend and frontends.
  • pi-kiosk-shared/ui: cross-app source for React primitives and hooks.
  • App-local clones: some apps may still keep local copies of primitives under src/shared/ui/ or src/shared/components/. Prefer /ui for new shared UI; migrate leftovers when touching those files.

Local monorepo overlay

Honesty (SSOT): Live monorepo source is shared/package.json 2.3.10. Monorepo consumers pin file:../shared/pi-kiosk-shared-2.3.10.tgz (or overlay sibling ../shared). Registry / app-only clones should pin ^2.3.10 — versions < 2.3.10 lack adaptive add-to-cart CQ rules (.rp-add-cta--always-long, @container rp-add-cta at 14rem) in src/tokens/responsive.css. Committed locks resolve the npm registry tarball (https://registry.npmjs.org/pi-kiosk-shared/-/pi-kiosk-shared-2.3.10.tgz after refresh) — that is what Railway / app-only npm ci installs when not using monorepo overlay.

Local monorepo dev does not use committed tarballs. When sibling ../shared exists, postinstall/prepare → overlaySharedIfPresent.mjs → ensureDist.mjs copies shared/dist into each consumer's node_modules/pi-kiosk-shared. You work against live source; registry version in node_modules is overwritten on install.

| Context | Source of pi-kiosk-shared | |---------|----------------------------| | Local monorepo | Sibling ../shared overlay (ensureDist.mjs) | | Railway / app-only clone | npm registry via package-lock.json (npm ci) |

After every shared version bump: npm publish (or your release pipeline), then in each consumer run npm update pi-kiosk-shared and commit the lock refresh. Confirm with npm view pi-kiosk-shared version.

Do not commit file:../shared/pi-kiosk-shared-*.tgz pins — stale registry locks (e.g. 2.3.2) ship pre-rewrite outage UI on Railway until locks are refreshed.

Monorepo tarball refresh (local): from shared/, run npm run publish:local to build and pack pi-kiosk-shared-<version>.tgz; reinstall in consumers with npm install file:../shared/pi-kiosk-shared-<version>.tgz.

Distribution paths

| Path | When | How rewrite reaches consumers | |------|------|-------------------------------| | Local overlay | Monorepo with sibling shared/ | npm ci / npm install hooks → ensureDist.mjs | | Registry ^2.3.10 | Railway, app-only clones | npm ci → registry tarball from lockfile (≥ 2.3.10 for adaptive-label CSS) |

Verify publish contents (maintainers only):

cd shared; npm run build; npm pack
tar -xOf pi-kiosk-shared-2.3.10.tgz package/src/tokens/responsive.css | Select-String "rp-add-cta--always-long"

Documented cold path (monorepo): npm ci / npm install in each app runs lifecycle hooks → scripts/overlaySharedIfPresent.mjs → shared/scripts/ensureDist.mjs when sibling ../shared exists:

| Package | Install hook (cold path) | |---------|----------------| | up-backend | prepare → overlaySharedIfPresent.mjs (+ husky) → ensureDist.mjs | | admin-app, rpapp-kiosk, rpapp-customer, rpapp-pickup | postinstall → overlaySharedIfPresent.mjs (+ patches) → ensureDist.mjs |

That script no-ops (exit 0) only when the sibling ../shared directory is absent (true app-only / Railway clones). If ../shared exists as a directory but is incomplete (missing package.json and/or scripts/ensureDist.mjs), the overlay fails exit 1 with recovery — empty/stub shared trees are not silent. When the layout is complete it runs shared/scripts/ensureDist.mjs (with ENSURE_DIST_ALLOW_MISSING_CONSUMERS=1 so single-package install does not fail siblings), which compiles this package and copies package.json + dist into each consumer node_modules/pi-kiosk-shared so Node/tsx/tsc load the Node-safe barrel and remapped /ui exports.

Policy — half-tree / one-app install: any frontend prebuild / predev path that invokes ensureDist.mjs (admin/customer/pickup prebuildShared.mjs, kiosk ensure-shared-consume.mjs) must also pass ENSURE_DIST_ALLOW_MISSING_CONSUMERS=1, matching overlaySharedIfPresent / postinstall. Bare ensureDist hard-fails when sibling consumers lack node_modules; with the allow flag, one-app monorepo install stays green through prebuild.

Secondary (not the cold path): up-backend predev / prebuild / prestart (ensure-shared → same overlaySharedIfPresent.mjs); frontends predev / prebuild (prebuildShared.mjs, kiosk ensure-shared-consume.mjs) also refresh the overlay before dev/build.

# Documented cold path — from any consumer (runs prepare/postinstall overlay)
npm ci
# or: npm install

# Optional manual / shared-root rebuild + overlay all five consumers
cd ../shared   # from a consumer, or start in shared/
node scripts/ensureDist.mjs

# Prove the compiled main barrel does not import React — checks shared/dist
# AND each consumer's node_modules/pi-kiosk-shared/dist/index.js (up-backend,
# admin-app, rpapp-kiosk, rpapp-customer, rpapp-pickup). Also smokes
# import('pi-kiosk-shared') from up-backend cwd. Missing consumer install
# fails by default; opt-out: ENSURE_DIST_SKIP_MISSING_CONSUMERS=1,
# GATE_ALLOW_MISSING_CONSUMERS=1, or ENSURE_DIST_ALLOW_MISSING_CONSUMERS=1.
npm run gate:main-barrel-node-safe

Cold overlay proof

From shared/, prove the documented cold path:

  1. DIAGNOSTIC — npm pack a known Node-safe registry tarball (COLD_VERSION in prove-pi-kiosk-shared-cold-overlay.mjs, currently 2.2.82 — intentional historical diagnostic fixture, not the live pin) in a temp dir and assert COLD_BAD markers (never installs into a consumer with --ignore-scripts). Live monorepo source is shared/package.json 2.3.10.
  2. PASS — wipe that consumer’s node_modules/pi-kiosk-shared, then npm install with scripts on (no package args) so prepare / postinstall overlays during install; assert Node-safe barrel + NODE_IMPORT_OK.
npm run prove:cold-overlay -- up-backend
npm run prove:cold-overlay -- --all   # serial across all consumers (never parallel)

Do not use --ignore-scripts then hand npm run prepare as the heal. Evidence is overwritten as markdown under .cursor/artifacts/pi-kiosk-shared-cold-overlay-proof*.md (not .log).

Barcode camera scanner (runbook)

Cross-app live camera + still-image decode lives in pi-kiosk-shared/barcode-scanner (useBarcodeScanner, snap APIs, ZBar WASM boot). Backend barcode contracts and admin/pickup routes: up-backend/docs/BARCODE/BARCODE_IMPLEMENTATION.md.

Human QA subset (fixture EAN 8593807360153): docs/SCANNER_QA.md. Manual UI skill: rpapp-manual-ui-testing.

Boot — initZbarWasm first import

Each camera surface must configure ZBar WASM before any useBarcodeScanner session:

| App | Boot module | Entry | |-----|-------------|-------| | admin-app | src/initZbarWasm.ts | first import in src/main.tsx | | rpapp-customer | src/initZbarWasm.ts | first import in src/main.tsx | | rpapp-pickup | src/initZbarWasm.ts | first import in src/main.tsx |

Pattern (all three apps):

import './initZbarWasm'; // must be first import in main.tsx

initZbarWasm.ts calls setZbarWasmUrl(wasmUrl) with Vite-resolved @undecaf/zbar-wasm/dist/zbar.wasm?url so the binary is same-origin and CSP-safe. Do not lazy-load this module on first scan — boot failure surfaces as degradedMode or zbarBootstrapFailed copy.

Decode engines

| Engine | Role | |--------|------| | zbar-wasm | Primary when boot succeeds — ZBar C/C++ WASM (@undecaf/zbar-wasm); retail EAN/UPC/Code128 + QR per formatProfile. | | zxing | Parallel assist (@zxing/browser) starting on frame 0 while ZBar runs; also primary live path in degraded boot. Distance optical zoom still waits 1.5s (SCANNER_DISTANCE_ZOOM_DELAY_MS). | | native-detector | Chromium BarcodeDetector parallel assist only — never the sole engine. |

Engine type: ScannerEngine in selectEngine.ts. Selection is internal to useBarcodeScanner; surfaces read engine, zxingAssistActive, and degradedMode — not engine alone.

degradedMode contract (G-P4)

UseBarcodeScannerReturn.degradedMode: boolean:

| Value | Meaning | |-------|---------| | false | ZBar WASM URL configured and ZBar loop started — full mode. @zxing / native may still run as assist (zxingAssistActive === true does not imply degraded). | | true | ZBar URL unset or ZBar boot failed — live decode continues on @zxing (+ native assist when available). |

UI rule: show runningDegraded i18n when degradedMode === true. Do not infer degraded from engine === 'zxing' or zxingAssistActive alone.

Still-image snap APIs

Exported from pi-kiosk-shared/barcode-scanner (decodeStillImage.ts):

| API | Use | |-----|-----| | decodeBarcodeFromImageFile(file, formatProfile?) | File upload / gallery pick — preprocessing (grayscale, Otsu, optional 2× upscale) then ZBar → @zxing → native. | | decodeBarcodeFromVideoFrame(video, formatProfile?, canvas?) | Preview-frame grab (admin/customer/pickup snap CTAs) — same engine cascade on a single frame. |

Both return StillDecodeResult | null (payload, engine). Payloads pass through prepareScanPayloadForEmit (trim, control chars, GTIN check digit, UPC-A↔EAN-13). Golden CI fixture: shared/src/__tests__/fixtures/ean-8593807360153.png.

WASM network-only precache policy (v1)

zbar.wasm is network-only — not precached by the service worker.

  • Vite PWA globPatterns in admin, customer, and pickup intentionally omit *.wasm (e.g. **/*.{js,css,html,ico,png,svg,woff2} only).
  • WASM is fetched on the first scan session after initZbarWasm sets the URL — not at install time.
  • Deploy acceptance: DevTools → Application → Cache Storage → no precached zbar.wasm; Network tab → HTTP 200 on first camera open per origin.
  • Single WASM source: @undecaf/zbar-wasm via setZbarWasmUrl — no second WASM from a polyfill package.

Ops failure recovery

| Symptom | Likely cause | Recovery | |---------|--------------|----------| | cameraZbarBootstrapFailed / scannerZbarBootstrapFailed | WASM 404, CSP block, or corrupt cache | Hard refresh (Ctrl+F5); Network tab → zbar.wasm 200; clear site data if SW served stale shell. | | degradedMode: true, slow decode | ZBar boot failed | Same as above; user can still decode via @zxing or preview/file snap. | | Camera permission denied | NotAllowedError | Manual entry; preview hidden on deny (row 14 matrix). | | Blank preview, no error | Insecure context (http:// non-localhost) | Serve over HTTPS or localhost. |

Deploy checklist (camera apps)

Applies to admin-app, rpapp-customer, rpapp-pickup (kiosk HID wedge unchanged).

  1. Bump shared/package.json; npm run build in shared; publish per project workflow.
  2. Bump pi-kiosk-shared in admin, customer, pickup; redeploy all three on Railway.
  3. Verify built bundle exports decodeBarcodeFromImageFile / decodeBarcodeFromVideoFrame (grep dist or smoke import).
  4. CSP per origin — meta Content-Security-Policy must allow WASM compile:
    • script-src includes 'wasm-unsafe-eval' (all three apps).
    • Admin also keeps 'unsafe-inline' for bootstrap scripts; production connect-src rewritten from VITE_API_URL / VITE_WS_URL.
    • Deploy HTTP headers may tighten further; WASM MIME must be application/wasm.
  5. zbar.wasm HTTP 200 on each deployed origin (admin, customer, pickup) — open scanner once, confirm in Network.
  6. No precached zbar.wasm in Cache Storage (network-only policy above).

Development

# Install dependencies
npm install

# Build the package
npm run build

# Run tests
npm test

# Watch mode for development
npm run dev

License

MIT