A place to input, store, and display JSON. Paste or type JSON (or JSONC) on the left and it is parsed, formatted, and rendered as a searchable tree on the right. Anonymous use is the default - signing in unlocks persistent, shareable links, history, and formatting rules (planned).
Production site: https://jotjson.com (target). Preview: https://ambitious-pond-000670a0f.7.azurestaticapps.net/
DESIGN_SPEC.md is the authoritative product and
architecture spec.
| Layer | Choice |
|---|---|
| Frontend | Angular 19 (standalone + Signals), Angular Material, SCSS |
| Editor | Monaco (lazy-loaded), jsonc-parser for JSON/JSONC parsing |
| Backend | Azure Functions v4, TypeScript (Node 24) |
| Data | Azure Cosmos DB (serverless, NoSQL) |
| Auth | MSAL Angular + Microsoft Entra External ID |
| Hosting | Azure Static Web Apps with managed Functions |
| IaC | Bicep (/infra) |
| Testing | Vitest browser mode + Playwright Chromium (frontend); Jest (API) |
| CI/CD | GitHub Actions (build, test, Bicep validate, SWA deploy) |
Node 24 is required locally and in CI (pinned via .nvmrc, engines, and
workflow node-version).
src/ Angular app (standalone components under core/shared/features)
styles/ Global SCSS (Material theme, design tokens)
environments/ environment.ts (gitignored) + example + interface + prod
api/ Azure Functions (TypeScript)
infra/ Bicep templates
public/ Static assets copied as-is at build
scripts/ Repo tooling (check-ascii.mjs, generate-icons.mjs, ...)
.github/ Workflows (ci.yml, cd.yml, infra.yml) + dependabot.yml
.vscode/ Launch + tasks configs (ng serve in Chrome)
DESIGN_SPEC.md Product + architecture source of truth
AGENTS.md Coding/AI-agent instructions (linted against by humans too)
CONTRIBUTING.md Contributor guide
vitest.config.mts Vitest config (browser mode, ChromeHeadless via Playwright)
vitest.perf.config.mts Vitest config for the L2 perf bench (perf:l2)
proxy.conf.json ng serve proxy that forwards /api/* to func start on :7071
- Node 24 (pinned via
.nvmrc; works withnvm useorfnm use) - npm 10+
- For API local dev: Azure Functions Core Tools v4
- For infra: Azure CLI + Bicep
- A Chromium-family browser on PATH for
npm test
Detailed install instructions (by OS, with verification steps and troubleshooting) are in PREREQUISITES.md.
npm install
(cd api && npm install)
# Web env: copy the template and fill in Entra values for local sign-in.
cp src/environments/environment.example.ts src/environments/environment.ts
# API env: copy the template and fill in Cosmos + Entra values.
cp api/local.settings.sample.json api/local.settings.jsonenvironment.ts and api/local.settings.json are gitignored. For CI, the
workflow copies environment.example.ts over environment.ts unchanged -
production builds source their real values from environment.prod.ts, which
CD bakes from GitHub secrets.
The Angular dev server proxies /api/* to the local Functions host
(proxy.conf.json -> http://localhost:7071), so both pieces need to be
running to exercise the full stack.
Quick start (Windows):
.\scripts\dev.ps1scripts/dev.ps1 checks prereqs, runs npm install if needed, verifies
your env files exist, and opens a Windows Terminal with three tabs:
web (ng serve), api (func start), and tests (vitest + jest
--watch split pane). Use -SkipTests to skip the tests tab.
If a previous run left zombies on dev ports 4200 or 7071, run
scripts/dev-stop.ps1 to free them. scripts/dev.ps1 also pre-flight checks
those ports on launch and tells you to run dev-stop.ps1 first if any are
already in use.
Manual (any OS):
# Terminal 1 - Functions API on :7071
cd api
npm start # runs `func start` (prestart = tsc)
# Terminal 2 - Angular SPA on :4200
npm start # ng serve, proxies /api/* to :7071Then open http://localhost:4200.
Signing in locally requires the Entra External ID app registration to
include http://localhost:4200/ as a redirect URI for both SPA and profile,
and the matching client/tenant IDs filled into environment.ts and
api/local.settings.json. See infra/README.md for the one-time manual
Entra setup steps.
Custom auth header. The SPA sends its bearer token as
X-Jotjson-Authorization (Azure Static Web Apps' managed-Functions runtime
rewrites the standard Authorization header; the API reads both but SPA
clients must use X-Jotjson-Authorization). See
DESIGN_SPEC.md #Auth forwarding.
.vscode/launch.json includes one config:
ng serve- launches Chrome againsthttp://localhost:4200/and runsnpm: startas a preLaunchTask. Set breakpoints in.tsfiles and they bind through the Angular source map.
To debug Vitest specs, install the
Vitest VS Code extension
(or run npm run test:watch in a terminal). The extension reads
vitest.config.mts directly; no extra launch config is needed.
For the Functions API, run cd api; npm start in a terminal and attach
VS Code's Node debugger to the spawned func process (Attach to Node
Process), or install the Azure Functions extension and use its "Attach to
Node Functions" config.
npm start # ng serve on http://localhost:4200 (proxies /api/*)
npm run build # production build to dist/jotjson
npm run lint # tsc + ASCII + spec/prod patterns + prettier
npm run lint:all # root lint + api workspace lint (CI-equivalent)
npm run lint:ascii # fail if non-allowlisted non-ASCII sneaks in
npm test # Vitest (browser mode, ChromeHeadless via Playwright), single run
npm run test:ci # Same, with coverage reporter (CI profile)
npm run test:watch # Vitest in watch modeTests are co-located as *.test.ts. Run a single file with
npx vitest run src/app/path/to/file.test.ts or focus interactively
with Vitest's describe.only / it.only.
Coverage reports land in coverage/jotjson/ (index.html for browse,
lcov.info for tooling).
cd api
npm run build # tsc
npm start # func start on :7071 (runs build via prestart)
npm run lint # tsc --noEmit
npm test # Jest (mocked Cosmos + Blob clients)api/local.settings.sample.json documents required settings - copy to
local.settings.json (gitignored) and fill in real values from Azure.
Infrastructure is defined under infra/ and validated by the infra.yml
workflow. See DESIGN_SPEC.md #Azure Infrastructure
and infra/README.md for resource layout and the
one-time Entra app-registration walkthrough.
Three workflows run on push and PR:
- CI (
ci.yml) - Web build + type-check, API build & test, Bicep validate, web unit tests with coverage artifact. - CD (
cd.yml) - Deploys the web app + managed Functions to Azure Static Web Apps. Gated onAZURE_STATIC_WEB_APPS_API_TOKENbeing configured. - Infra (
infra.yml) -az deployment group what-if/ apply for Bicep changes. Gated onvars.AZURE_CLIENT_ID(OIDC federated credentials).
Dependencies are kept current by Dependabot (.github/dependabot.yml:
weekly npm /, npm /api, and github-actions).
Milestone boundaries are captured as annotated git tags. List them with
git tag -l 'm*-complete' or view details with git show m3-complete:
m1-complete- project scaffolding (SPA, Functions, Bicep, CI/CD).m2-complete- core editor experience (Monaco, tree view, search, theming, PWA, i18n).m3-complete- Entra External ID sign-in + cloud-synced preferences.
Read CONTRIBUTING.md and AGENTS.md
before opening a PR. Key rules:
- Changes must align with
DESIGN_SPEC.md(update it in the same PR if behavior or architecture changes). - Tests are required for logic changes.
npm run lint,npm test, andnpm run buildmust pass before merge (enforced by CI). - Strict TypeScript; no
any. Standalone Angular components,OnPush,inject(), Signals. Kebab-case filenames. - Use
jsonc-parser- neverJSON.parse- for user JSON/JSONC input. - Tracked source files are ASCII-only (see
scripts/check-ascii.mjsandAGENTS.md#ASCII-only repository). - Never log or transmit clipboard/editor contents. No secrets in source.
AI-assisted commits include:
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
To report a security vulnerability, please use GitHub's Private
Vulnerability Reporting rather than a public issue. See
SECURITY.md for the policy, scope, and
response-time expectations.