// SPDX-License-Identifier: CC-BY-SA-4.0
// SPDX-FileCopyrightText: 2026 hyperpolymath
= AffineScript Packaging & Distribution (INT-04)
:toc: macro
:icons: font

INT-04 (#181). This documents what is publish-ready, the order, and the
one open design question. *Nothing here publishes anything*: the
workflow is manual-only and the owner dispatches it after review.

toc::[]

== JSR-publishable units (Deno/JSR-first)

[cols="2,2,1,3"]
|===
|Package |Spec |Status |Notes

|`packages/affinescript-cli` |`@hyperpolymath/affinescript` |**LIVE
0.1.2 (2026-05-20)** |The ADR-019 compiler shim. Cross-runtime (Deno
+ Bun + Node — only repo package with a documented runtime carve-out
from the Deno-only policy). Licensed `MPL-2.0` (SPDX-recognised; the
only non-PMPL package — see `.claude/CLAUDE.md` Runtime/TS Exemptions
+ #301 for the wider relicense discussion).
|`packages/affine-js` |`@hyperpolymath/affine-js` |dry-run OK; not
yet published |Loader + marshal + runtime + ownership accessor.
Tests excluded via `publish.exclude`.
|`affinescript-tea` |`@hyperpolymath/affinescript-tea` |dry-run OK;
not yet published |TEA runtime. Depends on
`@hyperpolymath/affine-js/loader` (import map) — see order.
|===

*Publish order is load-bearing for the JS-runtime pair*: `affine-js`
**must** publish before `affinescript-tea` (the latter resolves
`@hyperpolymath/affine-js/loader`). `affinescript-cli` has no JSR-side
dependencies and publishes independently of either.

`affinescript-dom` is AffineScript source (`.affine`), not a JS package
— it is consumed by the compiler, not JSR; out of scope for JSR.

=== Slow types

The JS entrypoints emit JSR's `unsupported-javascript-entrypoint`
(slow-types) warning — expected for a JS-with-JSDoc package; it
publishes, only `@hyperpolymath/affine-js`'s fast-check is skipped.
`types.d.ts` already provides the consumer-facing contract. Converting
entrypoints is out of scope for INT-04 (tracked separately if desired).

== How to publish (owner, manual)

. Review `deno publish --dry-run` (CI: `Publish to JSR (manual)` with
  `dry_run: true`, or locally `cd <pkg> && deno publish --dry-run`).
. Dispatch `.github/workflows/publish-jsr.yml` for `affine-js` with
  `dry_run: false` (JSR OIDC via `id-token: write` — no token secret).
. Then dispatch it for `affinescript-tea`.

== npm tail

"then npm" (#181) is a *separate, later* step and is **not** wired by
this prep. If/when needed it follows the existing owner-sanctioned npm
pattern (`.github/workflows/affine-vscode-publish.yml`, `NPM_TOKEN`
scoped automation token) — a deliberate exception to Deno-first.

== The compiler itself — decided (ADR-019 / #260)

#181 says "publish *compiler* + runtime". The runtime is the JS
packages above. The **compiler is a native OCaml binary** — not a
JSR/npm package. The one-way-door fork (#260) was decided
(**ADR-019**): *Releases-canonical, dual-channel*. Full decision record:
link:decisions/0019-compiler-distribution.adoc[docs/decisions/0019-compiler-distribution.adoc].

* *Canonical artifact.* `.github/workflows/release.yml` (#260 S2), on a
  `v*` tag, builds `affinescript-{linux-x64,macos-x64,macos-arm64}`
  raw binaries + a `SHA256SUMS` manifest attached to the GitHub
  Release. (`windows-x64` is a tracked follow-up.) Asset names are the
  ADR-019 contract — do not rename without amending the ADR + the shim.
* *Ergonomic front door.* `packages/affinescript-cli`
  (`@hyperpolymath/affinescript`, #260 S3) — a thin Deno shim that
  downloads the host-triple binary from the Release pinned in
  `pins.js`, verifies it against the embedded SHA-256 (fail-closed: an
  unpinned/mismatched binary is never executed), caches it under
  `$AFFINESCRIPT_CACHE`/XDG, and execs it with the caller's argv. One
  compiler version + checksum per shim release (no floating fetch).
  Publish is owner-gated via `publish-jsr.yml` (the INT-04 manual
  workflow; choice `affinescript-cli`).
* *Later, additive over the same Release artifact (not separate
  producers):* Guix/Nix fetch-derivations; an npm tail only if an
  npm-native consumer needs it.

`affinescript-lsp` distribution (INT-10, #282) consumes the shim.
End-to-end live since 2026-05-20: v0.1.1 Release published (3 binaries
+ SHA256SUMS), `pins.js` finalised, shim 0.1.2 on JSR, LSP `SHIM_SPEC`
points at `jsr:@hyperpolymath/affinescript@0.1.2`.

=== First-time JSR publish gotchas

The first publish of a new `@hyperpolymath/*` package trips five
separate gates (in this order, each failing with an unhelpful error
the next time round):

. Claim the JSR scope (`@hyperpolymath` — one-time per scope, via
  `https://jsr.io` sign-in).
. Create the package record on JSR — `deno publish` errors *Following
  packages don't exist…* until done.
. Link a trusted GitHub repository at the package or scope level
  (`https://jsr.io/@<scope>/<package>/settings`) — without it, OIDC
  is `actorNotAuthorized`.
. SPDX-recognised licence in `deno.json` — custom licences need the
  `LicenseRef-*` prefix or a switch to a standard SPDX identifier
  (the shim went the standard route with `MPL-2.0`).
. Optional but recommended: add a sibling `mod.d.ts` AND a
  `/// <reference types="./mod.d.ts" />` triple-slash comment in
  `mod.js` so JSR fast-check finds the types. The `.d.ts` alone is
  not enough — JSR doesn't auto-detect.

Dry-run only catches step 4 and step 5; steps 2 and 3 fail at real
publish time. Subsequent version bumps after the first publish need
only the version-string change.
