mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
The root layout handed every page the plan catalog (lib/rooms.ts configure with lib/data/plans.json), and /pricing, /calculator, /customers and the benchmark pages bundled the 202 KB price list. lib/api is now the one reader: plans() GET /v1/billing/plans, pricing() /v1/pricing, summary(), served() /v1/models, templates(), blueprints() /v1/blueprint/sbom, one promise a path a page load, each drawn as loading, ready or "api.hanzo.ai is unreachable" (components/api) and never an older number. plans.json, pricing.json, models.json, templates.json, blueprints.json and sync-pricing, sync-models, sync-templates, sync-blueprints are gone. What a static export must know before anyone asks, which model pages exist and their titles, is read by the build itself (lib/api/build, Next's fetch cache keyed by the build's stamp: one answer per build) and renders no rate. lib/moved reads the retired model pages against the export, and the 153 pages the old snapshot kept for models the gateway dropped are in retired.json. Kai's price beside Jev's, Enso's tier rates, a model page's rate and the GPU table read the price list in the browser; the site's plan math no longer imports @hanzo/rooms/lib/plans, whose bundled @hanzo/plans catalog rode the home, /pricing and the picker. Home first load 452.7 -> 437.4 KB gzip (budget 440); pages over perf.budget.json 32 -> 9, the rest over for settings code, connectors and the commerce catalog. The gates serve invented answers (e2e/gates/api) and block them; e2e/gates/read.spec.ts holds that no script carries a plan, price or model table.
95 lines
3.4 KiB
TypeScript
95 lines
3.4 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
||
import { readdirSync, readFileSync, statSync } from 'node:fs'
|
||
import { join, relative } from 'node:path'
|
||
|
||
/**
|
||
* WHAT THE SITE MAY NOT SAY.
|
||
*
|
||
* Certifications have one answer here, at /trust: controls built to be
|
||
* compatible with SOC 2, audited continually, the report on request. A page
|
||
* that names a certification or a signed agreement Hanzo does not hold makes a
|
||
* claim a customer can rely on and a regulator can read — and pages kept
|
||
* growing them: a badge row on /security, then compliance lists on every
|
||
* industry page, a certification grid, diagrams, customer tags.
|
||
*
|
||
* And a Hanzo page describes Hanzo. A business that is not Hanzo's — a
|
||
* trading venue, a broker-dealer, a transfer agent — does not appear on it.
|
||
*
|
||
* And a plan is a subscription with usage limits. It mints no credit and its
|
||
* usage never draws the balance, so no page says a plan includes credit or that
|
||
* usage past it is billed from credit. A plan's own lines are commerce's rows,
|
||
* read when a page opens (lib/api), so a sentence there is fixed on the row at
|
||
* admin.hanzo.ai; this repo holds no copy of them to read.
|
||
*
|
||
* This reads the source, not a render, so it runs without a server and fails
|
||
* on the commit that adds the sentence.
|
||
*/
|
||
|
||
const ROOTS = ['app', 'components', 'lib']
|
||
const SOURCE = /\.(tsx?|json|mdx?)$/
|
||
|
||
// Each is a claim of a certification, attestation or signed agreement.
|
||
const UNHELD: RegExp[] = [
|
||
/SOC ?2 Type ?(II|2)\b/i,
|
||
/ISO[/ ]?(IEC )?27001/i,
|
||
/FedRAMP/i,
|
||
/PCI[- ]DSS/i,
|
||
/HIPAA (BAA|compliant)/i,
|
||
/BAA (signed|available|compatible)/i,
|
||
/\bIL[56]\b/,
|
||
]
|
||
|
||
// Terms of a business that is not Hanzo's.
|
||
const FOREIGN: RegExp[] = [/broker-dealer/i, /transfer[- ]agent/i, /alternative trading system/i, /\bATS, /]
|
||
|
||
// What a plan does not include.
|
||
const CREDIT: RegExp[] = [
|
||
/\$[\d.,]+ of (prepaid )?credit/i,
|
||
/\bincludes?\b[^.]{0,60}\bof (prepaid )?credit\b/i,
|
||
/credit included/i,
|
||
/included credit/i,
|
||
/billed at cost from prepaid credit/i,
|
||
]
|
||
|
||
// A file may explain why a sentence is gone. It says so in a comment, and the
|
||
// comment is the only place these words may stand.
|
||
const commentary = (line: string) => /^\s*(\*|\/\/|\/\*)/.test(line)
|
||
|
||
function files(dir: string, out: string[] = []): string[] {
|
||
for (const name of readdirSync(dir)) {
|
||
const path = join(dir, name)
|
||
if (statSync(path).isDirectory()) files(path, out)
|
||
else if (SOURCE.test(name)) out.push(path)
|
||
}
|
||
return out
|
||
}
|
||
|
||
function offenders(patterns: RegExp[]): string[] {
|
||
const found: string[] = []
|
||
for (const root of ROOTS) {
|
||
for (const path of files(root)) {
|
||
readFileSync(path, 'utf8')
|
||
.split('\n')
|
||
.forEach((line, i) => {
|
||
if (commentary(line)) return
|
||
for (const p of patterns) {
|
||
if (p.test(line)) found.push(`${relative('.', path)}:${i + 1}: ${line.trim().slice(0, 120)}`)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
return found
|
||
}
|
||
|
||
test('no page claims a certification Hanzo does not hold', () => {
|
||
expect(offenders(UNHELD), 'certification claims; the one answer lives at /trust').toEqual([])
|
||
})
|
||
|
||
test('no page describes a business that is not Hanzo’s', () => {
|
||
expect(offenders(FOREIGN), 'another business on a Hanzo page').toEqual([])
|
||
})
|
||
|
||
test('no page says a plan includes credit', () => {
|
||
expect(offenders(CREDIT), 'a plan is a subscription with usage limits and includes no credit').toEqual([])
|
||
})
|