diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1014ba7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..eb31d86 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,44 @@ +name: Bug report +description: Report a reproducible problem in StackRunway +title: "[Bug]: " +labels: [bug] +body: + - type: markdown + attributes: + value: Thanks for helping improve StackRunway. Do not include private financial data or exported JSON contents. + - type: textarea + id: problem + attributes: + label: What happened? + description: Describe the problem and what you expected instead. + validations: + required: true + - type: textarea + id: reproduce + attributes: + label: Steps to reproduce + placeholder: "1. Open…\n2. Add…\n3. Select…" + validations: + required: true + - type: input + id: browser + attributes: + label: Browser and version + placeholder: Safari 26.0, Chrome 140, Firefox 142 + validations: + required: true + - type: dropdown + id: edition + attributes: + label: Edition + options: + - GitHub Pages + - Downloaded index.html + - Forked copy + validations: + required: true + - type: textarea + id: evidence + attributes: + label: Additional context + description: Add a screenshot if useful. Remove account balances and other private information first. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..d7f5835 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Security concern + url: https://github.com/Skylab008/StackRunway/security/policy + about: Read the security policy before reporting a vulnerability. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..5503691 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,28 @@ +name: Feature request +description: Propose a focused improvement to StackRunway +title: "[Idea]: " +labels: [enhancement] +body: + - type: textarea + id: problem + attributes: + label: Developer problem + description: What problem should StackRunway solve? + validations: + required: true + - type: textarea + id: outcome + attributes: + label: Desired outcome + description: Describe the result rather than prescribing an implementation. + validations: + required: true + - type: checkboxes + id: principles + attributes: + label: Project fit + options: + - label: This can preserve local-only data and the no-login model. + required: true + - label: This can preserve the downloadable single-file edition. + required: true diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..6375891 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,16 @@ +## What changed + + + +## Verification + + + +- [ ] `npm test` passes +- [ ] The app still opens directly from `index.html` +- [ ] No analytics, tracking, backend or runtime dependency was introduced +- [ ] User-facing changes are recorded in `CHANGELOG.md` + +## Privacy impact + + diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml new file mode 100644 index 0000000..e5f3081 --- /dev/null +++ b/.github/workflows/quality.yml @@ -0,0 +1,22 @@ +name: Quality + +on: + pull_request: + push: + branches: [main] + +permissions: + contents: read + +jobs: + verify: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + - name: Verify standalone application + run: npm test diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..cbb4467 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,20 @@ +# Changelog + +Meaningful user-facing changes to StackRunway are recorded here. + +This project follows [Semantic Versioning](https://semver.org/). + +## [1.0.0] — 2026-08-25 + +### Added + +- Date-aware 30, 60 and 90-day developer expense runway calculations +- Minimum opening balance statement with exact coverage dates +- Most-expensive-week and first-funding-gap insights +- Annual-renewal exposure with monthly-equivalent comparison +- Pause modelling for subscriptions and other expenses +- Weekly, fortnightly, monthly, quarterly, annual and one-off schedules +- Local browser autosave +- Portable JSON export and restore +- Responsive, dependency-free single-file interface +- GitHub Pages edition and MIT Licence diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..2969eb4 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,9 @@ +# Code of Conduct + +StackRunway welcomes thoughtful participation from people of every background and experience level. + +Be respectful, specific and constructive. Discuss the work rather than the person. Harassment, intimidation, discriminatory language, deliberate disruption and the publication of another person's private information are not acceptable. + +Maintainers may edit or remove contributions that breach these standards and may temporarily or permanently restrict participation when necessary to protect the project and its community. + +If conduct needs attention, raise it privately with the repository maintainer where possible. Do not use a public issue to disclose personal or sensitive information. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a172b20 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,34 @@ +# Contributing to StackRunway + +StackRunway is deliberately small: a private, local-first developer tool that runs from one HTML file. Contributions are welcome when they preserve that clarity. + +## Before opening a pull request + +Search existing issues first. For a material feature or behavioural change, open a feature request before writing code so the direction can be agreed without wasting anyone's time. + +Bug fixes, accessibility improvements, calculation corrections and careful interface refinements are especially valuable. + +## Project principles + +- Keep the application usable without an account, backend or build step. +- Keep user financial data inside the browser. +- Do not add analytics, advertising, tracking or bank connections. +- Avoid runtime dependencies unless there is a compelling security or accessibility reason. +- Preserve the downloadable single-file `index.html` edition. +- Use clear language and accessible interaction patterns. + +## Development + +Open `index.html` directly in a modern browser or serve the repository with any static server. + +Run the repository checks before submitting: + +```bash +npm test +``` + +## Pull requests + +Keep each pull request focused. Explain the user problem, the change and how it was tested. Include screenshots for visible interface changes. Update `CHANGELOG.md` when the change affects users. + +By contributing, you agree that your contribution may be distributed under the repository's [MIT Licence](LICENSE). diff --git a/README.md b/README.md index 5b8e585..6ddc5f6 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ --- +![StackRunway developer subscription runway](assets/stackrunway-preview.webp) + Developer expenses rarely arrive in a neat monthly bundle. Hosting renews on Tuesday. API credits run out on Friday. Three annual tools land in the same week and the account balance that looked comfortable suddenly is not. **StackRunway answers the question ordinary expense trackers miss:** @@ -69,4 +71,6 @@ Useful software does not always need a gate, a funnel or another subscription. Released under the [MIT Licence](LICENSE). +[Contributing](CONTRIBUTING.md) · [Security](SECURITY.md) · [Code of Conduct](CODE_OF_CONDUCT.md) · [Changelog](CHANGELOG.md) + Planning estimates only. StackRunway does not predict taxes, exchange-rate movements, price changes or usage-based overages. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..8d5ed4a --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,17 @@ +# Security policy + +## Supported version + +The latest version on the `main` branch is supported. + +## Reporting a vulnerability + +Please do not publish exploit details in a public issue. + +Use GitHub's private vulnerability reporting option for this repository when it is available. If it is not available, open a minimal issue stating that you need to report a security concern privately, without including sensitive details or a proof of concept. + +Reports should identify the affected behaviour, the potential impact, reproducible conditions and any suggested remediation. Acknowledgement should be expected within seven days. + +## Security model + +StackRunway has no server, account system, bank connection, analytics or runtime dependency. Expense data is stored in the user's browser and can be exported to a local JSON file. Users are responsible for protecting exported backups and devices on which browser data is stored. diff --git a/assets/stackrunway-preview.webp b/assets/stackrunway-preview.webp new file mode 100644 index 0000000..ac6604f Binary files /dev/null and b/assets/stackrunway-preview.webp differ diff --git a/package.json b/package.json new file mode 100644 index 0000000..aecb827 --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "name": "stackrunway", + "version": "1.0.0", + "private": true, + "description": "A private, local-first developer subscription runway calculator.", + "scripts": { + "test": "node scripts/check.mjs" + }, + "license": "MIT" +} diff --git a/scripts/check.mjs b/scripts/check.mjs new file mode 100644 index 0000000..cc4ee1c --- /dev/null +++ b/scripts/check.mjs @@ -0,0 +1,23 @@ +import assert from "node:assert/strict"; +import { readFile } from "node:fs/promises"; +import vm from "node:vm"; + +const html = await readFile(new URL("../index.html", import.meta.url), "utf8"); + +assert.match(html, /^/i, "index.html must declare HTML5"); +assert.match(html, /StackRunway/, "document title is missing"); +assert.match(html, /id="startDate"/, "runway start control is missing"); +assert.match(html, /data-days="30"/, "30-day runway option is missing"); +assert.match(html, /data-days="60"/, "60-day runway option is missing"); +assert.match(html, /data-days="90"/, "90-day runway option is missing"); +assert.match(html, /Export JSON/, "JSON export control is missing"); +assert.match(html, /Restore JSON/, "JSON restore control is missing"); +assert.doesNotMatch(html, /<script[^>]+src=/i, "runtime script dependencies are not permitted"); +assert.doesNotMatch(html, /<link[^>]+rel=["']stylesheet/i, "runtime stylesheet dependencies are not permitted"); +assert.doesNotMatch(html, /\b(fetch|XMLHttpRequest|WebSocket)\s*\(/, "network calls are not permitted"); + +const scripts = [...html.matchAll(/<script>([\s\S]*?)<\/script>/gi)]; +assert.equal(scripts.length, 1, "exactly one inline application script is expected"); +new vm.Script(scripts[0][1], { filename: "index.html:inline-script" }); + +console.log("StackRunway checks passed: structure, privacy boundaries and JavaScript syntax.");