Skip to content

0.3.0: fork protection, honest costs, and a fifth theme

Choose a tag to compare

@LucideLarp LucideLarp released this 20 Jul 15:05

Ninety-one commits since 0.2.0. Most of this release is defects found by using the engine as a stranger would, then fixing what broke.

If you fork this, read these first

A store that kept the vendor's checkout links can no longer deploy. Editing only the fields the setup guide listed used to produce a working store that sold someone else's product into someone else's Stripe account, and the build exited 0. The guard now keys on store identity and names every leftover by file and field. It also survives a cosmetic edit: a query string, a fragment, a trailing slash or a case change no longer walks past it.

Generated payment links have promotion codes off by default. A link that accepts typed codes, plus any live 100%-off coupon, is a free copy of your product to anyone who guesses the code. Turn it on deliberately for a campaign; docs/setup.md §7 shows how to run a $0 test without it.

$0/month is now arithmetic instead of an assurance. The shipped poll is */30, which is 1,488 billable minutes against the 2,000 GitHub Free includes for private repos. docs/setup.md §6 carries the table, the per-job rounding rule, and the rate, so you can check the claim rather than trust it.

Fulfillment

  • Every GitHub response is now distinguishable in the log, including statuses we do not recognise. An unrecognised status counts as not delivered, never as a sale.
  • A paid session that matches no fulfillment grant used to be dropped with new_paid=0 and a green exit. It warns now, because money arriving with nothing delivered is the failure you most need to hear about.
  • Requests carry a deadline. Node's fetch has none, so one unanswered connection could stall an entire cycle and everyone queued behind it.
  • Transient invite failures retry inside the run, honouring GitHub's retry-after, instead of waiting for the next poll. A primary rate limit is declined rather than slept through.
  • The Stripe session list retries on 429 and 5xx. It is the first call of every run, so one failure there used to mean nobody was delivered that cycle.
  • A zero-cost fulfilment says so. A coupon-covered order is a delivery, not a sale, and your revenue line should not blur them.
  • fulfill.js and fulfill-core.js are checked in CI against the engine, so a vendored copy cannot silently drift from the code you are reading.

Setup

init.js accepted --price=2900 and then said --price is required; discarded a typo'd flag and blamed a different one; printed a prompt and exited 0 having created nothing when stdin was not a terminal, which any script reads as success; demanded a Stripe key for --dry-run, which uses none; and treated --dry-run=true as not a dry run, creating live Stripe objects from a command that asked for a preview. Switches now refuse a value rather than guessing at one. A Stripe call that never answers now fails by name instead of hanging forever.

Store and docs

  • The docs are published as real pages, so the guides the FAQ points at now exist on the site.
  • Five themes. atrium is new: one flush-left axis, a single spacing unit, one orchestrated entrance and then stillness. editorial, brutalist and midnight were reworked; terminal was wearing another theme's theme-color.
  • Markdown: tables render, nested lists nest, code spans are inert, links keep their parens and titles, and a long identifier no longer widens the page on a phone.
  • Terms, refunds and license pages print legibly instead of blank or grey.
  • Showcase images are sized for the box they occupy, lossless: about 21% lighter.
  • One URL gate for every href, so config text cannot rewrite the page, and a tab can no longer smuggle an off-origin authority past it.

Tests

147, up from 91. Anything guarding a money path is mutation-checked: the guard is broken deliberately and the test has to go red, because a test that cannot fail is decoration.