diff --git a/.bazelignore b/.bazelignore index 4247355..e8f43fd 100644 --- a/.bazelignore +++ b/.bazelignore @@ -7,3 +7,4 @@ coverage dist dist-types node_modules +.svelte-check diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff122fc..7334264 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: npm_publish_mode: disabled node_versions: '["22"]' publish_node_version: "22" - pnpm_version: "9.15.9" + pnpm_version: "10.13.1" metadata_check_command: pnpm run check:release-metadata typecheck_command: pnpm run check unit_test_command: pnpm run test diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 54026ba..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Publish to npm - -on: - push: - tags: - - 'v*' - -permissions: - actions: read - contents: read - id-token: write - packages: write - -jobs: - package: - uses: xoxd-ai/ci-templates/.github/workflows/js-bazel-package.yml@v2.14.1 - with: - runner_mode: repo_owned - runner_labels_json: ${{ vars.PRIMARY_LINUX_RUNNER_LABELS_JSON }} - workspace_mode: isolated - publish_mode: hosted_exception - npm_publish_mode: disabled - node_versions: '["22"]' - publish_node_version: "22" - pnpm_version: "9.15.9" - metadata_check_command: pnpm run check:release-metadata - typecheck_command: pnpm run check - unit_test_command: pnpm run test - build_command: pnpm run build - package_check_command: "pnpm run check:package && pnpm run check:bundle-size" - bazel_targets: "//:pkg //:package_consumer_check //:bundle_size_check //:typecheck //:test" - package_dir: ./bazel-bin/pkg - npm_access: public - # GitHub Packages channel retired 2026-07-25 (TIN-3165); github_package_name intentionally unset. - dry_run: false - secrets: inherit diff --git a/.gitignore b/.gitignore index 6eb7bfd..7b18bd5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ coverage/ node_modules/ bazel-* *.tsbuildinfo +.svelte-check/ diff --git a/.pnpmfile.cjs b/.pnpmfile.cjs new file mode 100644 index 0000000..531cc69 --- /dev/null +++ b/.pnpmfile.cjs @@ -0,0 +1,40 @@ +// RU13 (TypeScript 7.0.2 as `typescript`): see patches/README.md. +// +// TypeScript 7 ships no in-process compiler API, but the packages below parse +// TypeScript in-process and declare `typescript` as a peer. pnpm resolves a +// peer from the root, where `typescript` is 7.0.2, and neither pnpm +// `overrides` nor `packageExtensions` can redirect a peer. This hook turns +// that peer into a regular dependency on Microsoft's TypeScript 6 API package +// (`@typescript/typescript6`), so only these packages load it; the root +// `typescript`, every `tsc` and every type check stay on 7.0.2. pnpm records +// the result in pnpm-lock.yaml (`pnpmfileChecksum`), which rules_js reads. +// +// SvelteKit is not listed: patches/@sveltejs__kit@3.0.1.patch teaches it to +// find the companion itself. Remove a name once its upstream supports +// TypeScript 7. + +const COMPANION = 'npm:@typescript/typescript6@6.0.2'; + +const TS_API_CONSUMERS = new Set([ + 'svelte-check', + 'ts-api-utils', + 'typescript-eslint', + '@typescript-eslint/eslint-plugin', + '@typescript-eslint/parser', + '@typescript-eslint/project-service', + '@typescript-eslint/tsconfig-utils', + '@typescript-eslint/type-utils', + '@typescript-eslint/typescript-estree', + '@typescript-eslint/utils', +]); + +function readPackage(pkg) { + if (TS_API_CONSUMERS.has(pkg.name) && pkg.peerDependencies?.typescript) { + delete pkg.peerDependencies.typescript; + if (pkg.peerDependenciesMeta) delete pkg.peerDependenciesMeta.typescript; + pkg.dependencies = { ...pkg.dependencies, typescript: COMPANION }; + } + return pkg; +} + +module.exports = { hooks: { readPackage }, TS_API_CONSUMERS, COMPANION }; diff --git a/BUILD.bazel b/BUILD.bazel index 84549a8..4fd0ef6 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -14,10 +14,9 @@ Build strategy: - vitest for PBT tests (property-based tests using fast-check) """ -load("@aspect_rules_js//js:defs.bzl", "js_binary", "js_library", "js_run_binary") +load("@aspect_rules_js//js:defs.bzl", "js_binary", "js_library", "js_run_binary", "js_test") load("@aspect_rules_js//npm:defs.bzl", "npm_package") load("@tummycrypt_tinyvectors_npm//:defs.bzl", "npm_link_all_packages") -load("@tummycrypt_tinyvectors_npm//:svelte-check/package_json.bzl", svelte_check_bin = "bin") load("@tummycrypt_tinyvectors_npm//:vitest/package_json.bzl", vitest_bin = "bin") # ============================================================================= @@ -26,7 +25,18 @@ load("@tummycrypt_tinyvectors_npm//:vitest/package_json.bzl", vitest_bin = "bin" npm_link_all_packages(name = "node_modules") -svelte_check_bin.svelte_check_binary(name = "svelte_check") +# RU13: svelte-check runs in its TypeScript 7 native mode (--tsgo) through a +# runner that checks a writable scratch copy (see the script header). +js_binary( + name = "svelte_check", + data = [ + ":node_modules", + ":node_modules/svelte", + ":node_modules/svelte-check", + ":node_modules/typescript", + ], + entry_point = "scripts/bazel/run-svelte-check.mjs", +) js_binary( name = "build_declarations", @@ -149,7 +159,7 @@ npm_package( ":tinyvectors", ], package = "@tummycrypt/tinyvectors", - version = "0.3.7", + version = "1.0.0", visibility = ["//visibility:public"], ) @@ -226,7 +236,8 @@ vitest_bin.vitest_test( # # Uses svelte-check for type validation of .svelte and .ts files. # This is preferred over plain tsc for packages containing Svelte components, -# as it understands .svelte file boundaries and runes syntax. +# as it understands .svelte file boundaries and runes syntax. RU13: it runs as +# `svelte-check --tsgo`, so TypeScript 7.0.2 does the checking. # ============================================================================= js_run_binary( @@ -241,13 +252,35 @@ js_run_binary( ":node_modules", ], args = [ - "--tsconfig", "$(execpath tsconfig.json)", ], stdout = "typecheck.log", visibility = ["//visibility:public"], ) +# Seeded-error canary for the gate above: under --tsgo a tsconfig that checks +# nothing reports 0 errors (sveltejs/language-tools#3136), so this test fails +# unless a seeded .ts and a seeded .svelte type error are both reported. +js_test( + name = "typecheck_canary_test", + args = [ + "$(rootpath tsconfig.json)", + "--canary", + ], + data = glob([ + "src/**/*.ts", + "src/**/*.svelte", + ]) + [ + "package.json", + "tsconfig.json", + ":node_modules", + ":node_modules/svelte", + ":node_modules/svelte-check", + ":node_modules/typescript", + ], + entry_point = "scripts/bazel/run-svelte-check.mjs", +) + # ============================================================================= # Exported files for downstream consumers # ============================================================================= diff --git a/CHANGELOG.md b/CHANGELOG.md index d7bdf45..70260ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,71 @@ # Changelog -> **Distribution note (2026-07-25):** the npm channel is retired. npm -> publishes stop at 0.3.0; 0.3.1+ ship as git tags + entries in -> [tinyland-inc/bazel-registry](https://github.com/tinyland-inc/bazel-registry) -> only. See the README's Install section for the sanctioned consumption paths. +> **Distribution (RU6/RU8, 2026-10-08):** this package is distributed only +> through Bazel: signed git tags plus entries in +> [xoxd-ai/bazel-registry](https://github.com/xoxd-ai/bazel-registry). The npm +> channel stopped at 0.3.0 (retired 2026-07-25) and the package is now +> `private`; nothing is published to npmjs or GitHub Packages. + +## 1.0.0 - 2026-10-14 + +Stack uplift to the estate RU1 stack. The runtime API is unchanged: every +export, prop, default and physics constant is the same as 0.3.7. It is a major +version because the Svelte peer, the toolchain and the distribution contract +change (RU10). + +### Breaking changes + +- **Svelte peer is exact `5.57.2`** (was `>=5.20.0`), per the estate pin policy + (RS11/RU5). Consumers must be on Svelte 5.57.2. +- **Bazel-only distribution.** `package.json` is `private: true`; + `publishConfig`, the `prepublishOnly` hook and `.github/workflows/publish.yml` + are removed (RU8). Consume `tummycrypt_tinyvectors` 1.0.0 from + xoxd-ai/bazel-registry. +- **Build toolchain:** TypeScript 7.0.2 (native compiler), Vite 8.3.3 + (Rolldown, Oxc minifier), @sveltejs/vite-plugin-svelte 7.3.1, vitest 5.0.3, + svelte-check 4.7.6, pnpm 10.13.1, Node toolchain 22.22.0 (engines + `>=22.12.0`). A consumer that builds this module from source (Bazel + `@tummycrypt_tinyvectors//:pkg`, or a tag-archive `prepare` hook) needs + pnpm 10 and Node >= 22.12. +- `@sveltejs/package` is dropped from devDependencies (it was not used by any + build step; declarations come from `tsc` plus the hand-written + `*.svelte.d.ts` files). + +### Migration + +1. Bazel consumers: `bazel_dep(name = "tummycrypt_tinyvectors", version = "1.0.0")` + and refresh `MODULE.bazel.lock`. Keep linking `@tummycrypt_tinyvectors//:pkg` + with `npm_link_package`; target names are unchanged. +2. Pin `svelte` to exactly `5.57.2` in the consuming app. +3. Remove any `@tummycrypt/tinyvectors` npm or tag-tarball specifier from + `package.json`; the registry module is the only supported source. +4. No source changes are needed: imports, component props, the + `themes/css` entry and all exported names are identical to 0.3.7. + +### Tooling details + +- TypeScript 7 (RU13): `tsc` is resolved through `typescript/package.json` + `bin` because the TypeScript 7 exports map no longer exposes + `lib/tsc.js`. `tsconfig.json` drops `baseUrl`/`paths` (removed in + TypeScript 7; no source used `$lib`). +- Type checking is `svelte-check --tsgo` (TypeScript 7 does the checking). The + RU13 patch set is copied verbatim from xoxd-ai/site.scaffold (`patches/`, + `.pnpmfile.cjs`); only `patches/svelte-check@4.7.6.patch` is wired here, + since this package uses neither SvelteKit nor devalue. svelte-check's + in-process svelte2tsx parser loads `@typescript/typescript6` through the + `.pnpmfile.cjs` hook (operator ruling RU13b); nothing type-checks with it. +- Bazel: `//:typecheck` runs svelte-check `--tsgo` in a writable scratch copy + (`scripts/bazel/run-svelte-check.mjs`), and the new `//:typecheck_canary_test` + proves the gate reports a seeded `.ts` and `.svelte` error + (sveltejs/language-tools#3136). +- Vite 8: `build.rolldownOptions` replaces the deprecated `rollupOptions` + alias, and the default Oxc minifier replaces esbuild in both the library + build and the bundle-size gate. +- fast-check 4, publint 0.3. +- `scripts/check-release-metadata.mjs` now asserts the RU8 contract + (`private`, no `publishConfig`, no `prepublishOnly`, no `publish.yml`) + instead of the old publish-workflow parity checks. +- Repository URLs point at xoxd-ai. ## 0.3.7 - 2026-09-01 diff --git a/MODULE.bazel b/MODULE.bazel index 972b4dc..7d15dbd 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,13 +1,13 @@ """ MODULE.bazel for standalone Bazel consumption of @tummycrypt/tinyvectors. -Usage from external repo: - bazel_dep(name = "tummycrypt_tinyvectors", version = "0.3.0") +Usage from external repo (xoxd-ai/bazel-registry): + bazel_dep(name = "tummycrypt_tinyvectors", version = "1.0.0") """ module( name = "tummycrypt_tinyvectors", - version = "0.3.7", + version = "1.0.0", compatibility_level = 1, ) @@ -18,12 +18,14 @@ bazel_dep(name = "aspect_bazel_lib", version = "2.22.5") bazel_dep(name = "rules_nodejs", version = "6.7.3") node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node") -node.toolchain(node_version = "22.13.1") +# Node 22.22.0: Vite 8 needs Node >= 22.12 (engines in package.json); 22.22.0 +# is the newest 22.x that rules_nodejs 6.7.3 knows (matches site.scaffold). +node.toolchain(node_version = "22.22.0") pnpm = use_extension("@aspect_rules_js//npm:extensions.bzl", "pnpm") pnpm.pnpm( name = "pnpm", - pnpm_version = "9.15.9", + pnpm_version = "10.13.1", ) use_repo(pnpm, "pnpm") @@ -31,7 +33,13 @@ npm = use_extension("@aspect_rules_js//npm:extensions.bzl", "npm") npm.npm_translate_lock( name = "tummycrypt_tinyvectors_npm", pnpm_lock = "//:pnpm-lock.yaml", - data = ["//:package.json"], + # RU13: npm_translate_lock applies the pnpm `patchedDependencies` and the + # .pnpmfile.cjs rewrite recorded in pnpm-lock.yaml; both are inputs. + data = [ + "//:.pnpmfile.cjs", + "//:package.json", + "//:patches/svelte-check@4.7.6.patch", + ], # TIN-2099: skip esbuild's `node install.js` postinstall so no build # action needs network or a platform binary outside declared inputs # (breaks under strict sandbox/worker strategies). Safe because vite diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 9d1030a..28bfefa 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -566,7 +566,7 @@ "@@rules_nodejs+//nodejs:extensions.bzl%node": { "general": { "bzlTransitiveDigest": "4pUxCNc22K4I+6+4Nxu52Hur12tFRfa1JMsN5mdDv60=", - "usagesDigest": "KpXNMFWTH8CIVa1mXsm5UJf0ufKBzQeErb1QMx4bkPE=", + "usagesDigest": "1SqN6zNCitfF03CmHBrKw29GL5dFS5fZ1NZLKUbpUN4=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, @@ -579,7 +579,7 @@ "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "22.13.1", + "node_version": "22.22.0", "include_headers": false, "platform": "linux_amd64" } @@ -592,7 +592,7 @@ "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "22.13.1", + "node_version": "22.22.0", "include_headers": false, "platform": "linux_arm64" } @@ -605,7 +605,7 @@ "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "22.13.1", + "node_version": "22.22.0", "include_headers": false, "platform": "linux_s390x" } @@ -618,7 +618,7 @@ "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "22.13.1", + "node_version": "22.22.0", "include_headers": false, "platform": "linux_ppc64le" } @@ -631,7 +631,7 @@ "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "22.13.1", + "node_version": "22.22.0", "include_headers": false, "platform": "darwin_amd64" } @@ -644,7 +644,7 @@ "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "22.13.1", + "node_version": "22.22.0", "include_headers": false, "platform": "darwin_arm64" } @@ -657,7 +657,7 @@ "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "22.13.1", + "node_version": "22.22.0", "include_headers": false, "platform": "windows_amd64" } @@ -670,7 +670,7 @@ "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "22.13.1", + "node_version": "22.22.0", "include_headers": false, "platform": "windows_arm64" } diff --git a/README.md b/README.md index 46f3b47..743bf25 100644 --- a/README.md +++ b/README.md @@ -6,29 +6,27 @@ This package is what powers the moving background layer on `transscendsurvival.o ## Install -> **npm channel retired (2026-07-25).** Versions on registry.npmjs.org stop -> at 0.3.0 and receive no further publishes. Consume this package through the -> [tinyland-inc/bazel-registry](https://github.com/tinyland-inc/bazel-registry) -> graph instead — either as a `bazel_dep` (`tummycrypt_tinyvectors`), or by -> pinning the exact GitHub tag archive the registry's `source.json` names and -> building `dist/` from source in a `prepare` hook that verifies the archive -> sha256 against the registry pin (reference implementations: -> `scripts/build-tinyvectors.mjs` in `Jesssullivan/dsa-woodshed.space` and -> `Great-Falls-Tool-Bus/greatfallstoolbus.org`). - -```bash -# Bazel (MODULE.bazel), resolved via the tinyland registry chain: -bazel_dep(name = "tummycrypt_tinyvectors", version = "0.3.5") +This package is distributed through Bazel only (RU6/RU8): signed git tags plus +the `tummycrypt_tinyvectors` module in +[xoxd-ai/bazel-registry](https://github.com/xoxd-ai/bazel-registry). The npm +channel stopped at 0.3.0 and nothing newer is published there. + +```starlark +# MODULE.bazel +bazel_dep(name = "tummycrypt_tinyvectors", version = "1.0.0") ``` -```bash -# pnpm, pinned to the registry-named tag archive: -pnpm add @tummycrypt/tinyvectors@https://github.com/tinyland-inc/tinyvectors/archive/refs/tags/v0.3.5.tar.gz +```starlark +# BUILD.bazel: link the Bazel-built package into node_modules +npm_link_package( + name = "node_modules/@tummycrypt/tinyvectors", + src = "@tummycrypt_tinyvectors//:pkg", +) ``` Peer dependency: -- `svelte@>=5.20.0` +- `svelte@5.57.2` (exact) ## Quick Start @@ -106,8 +104,8 @@ Use the top-level entry point when you just want the packaged component surface. ## Local Development ```bash -pnpm install -pnpm check +pnpm install # pnpm 10.13.1 via packageManager / corepack +pnpm check # svelte-check --tsgo (TypeScript 7.0.2) pnpm test pnpm build ``` @@ -123,7 +121,7 @@ Useful extra commands: - `pnpm check:bundle-size` measures the tree-shaken `{ TinyVectors }` consumer bundle with Svelte externalized - `pnpm check:package-consumer` validates the Bazel-built package from `./bazel-bin/pkg` in a temporary consumer workspace -The Bazel-to-npm release flow is documented in [docs/release-flow.md](./docs/release-flow.md). +The Bazel release flow is documented in [docs/release-flow.md](./docs/release-flow.md). The physics interaction direction is documented in [docs/physics-feel-contract.md](./docs/physics-feel-contract.md). The dev app includes a browser/device harness for interaction work: @@ -135,9 +133,8 @@ The dev app includes a browser/device harness for interaction work: ## Release Truth -The supported consumer paths are the bazel-registry graph and the -registry-named git tag archive (see the Install section's retirement notice); -the npm channel is retired at 0.3.0. +The supported consumer path is the xoxd-ai/bazel-registry graph (see +Install); the npm channel is retired at 0.3.0 and the package is private. This repo carries Bazel metadata as a first-class release surface: `//:pkg` is the publishable package, and the bazel-registry entry (byte-identical diff --git a/docs/release-flow.md b/docs/release-flow.md index bd487a7..b8b4afd 100644 --- a/docs/release-flow.md +++ b/docs/release-flow.md @@ -1,9 +1,10 @@ # Release Flow This repo releases through signed git tags and the -[tinyland-inc/bazel-registry](https://github.com/tinyland-inc/bazel-registry) -graph. The npm channel is retired (2026-07-25): registry.npmjs.org stops at -0.3.0 and receives no further publishes. Bazel exists here to produce and +[xoxd-ai/bazel-registry](https://github.com/xoxd-ai/bazel-registry) +graph (RU6). The npm channel is retired (2026-07-25): registry.npmjs.org stops at +0.3.0 and receives no further publishes, and since 1.0.0 the package is +`private` with no publish workflow (RU8). Bazel exists here to produce and validate the package shape that downstream consumers resolve through the registry or build from the tag archive. @@ -17,8 +18,9 @@ registry or build from the tag archive. `tsc`, and assembles `//:pkg` with `npm_package`. 4. `.bazelversion` pins the Bazel runtime. Local Nix exposes `bazel` through Bazelisk so the dev shell follows that pin. -5. `.github/workflows/ci.yml` and `.github/workflows/publish.yml` call the same - pinned reusable package workflow with `npm_publish_mode: disabled`. +5. `.github/workflows/ci.yml` calls the pinned reusable package workflow with + `npm_publish_mode: disabled` and `dry_run: true`. There is no + tag-triggered publish workflow (removed in 1.0.0, RU8). 6. The signed git tag `vX.Y.Z` on the release merge commit is the release artifact: its GitHub tag archive is what consumers pin and build. 7. The bazel-registry entry for that version is the publication step. There is @@ -27,10 +29,9 @@ registry or build from the tag archive. `pnpm run check:release-metadata` verifies these surfaces stay aligned before CI or Bazel steps run. It pins, among other things, the version/name agreement across `package.json`, `MODULE.bazel`, and `BUILD.bazel`; the pnpm and Node -pins; the identical pinned reusable-workflow commit in both workflows; the -shared workflow inputs, including `publish_mode: hosted_exception` and -`npm_publish_mode: disabled`; and `dry_run: true` on CI versus -`dry_run: false` on tags. +pins; the pinned reusable workflow and its inputs, including +`npm_publish_mode: disabled` and `dry_run: true`; and the RU8 contract: +`private: true`, no `publishConfig`, no `prepublishOnly`, no `publish.yml`. ## Pre-Release Gates @@ -44,6 +45,7 @@ pnpm run build pnpm run check:package pnpm run check:bundle-size nix develop . --command bazel build //:pkg //:package_consumer_check //:bundle_size_check //:typecheck //:test --verbose_failures +nix develop . --command bazel test //... # includes //:typecheck_canary_test pnpm run check:package-consumer npm pack --dry-run ./bazel-bin/pkg ``` @@ -80,8 +82,8 @@ rewrite. ## CI Flow Pull requests and pushes to `main` run `Verify`, which calls -`tinyland-inc/ci-templates/.github/workflows/js-bazel-package.yml` at a pinned -commit. The reusable workflow: +`xoxd-ai/ci-templates/.github/workflows/js-bazel-package.yml` at a pinned +release. The reusable workflow: - installs the configured pnpm and Node major; - runs metadata, typecheck, test, build, package, and bundle-size checks; @@ -102,39 +104,26 @@ release after it. gate fails otherwise), updates `CHANGELOG.md`, and passes all pre-release gates above. 2. **Sign and push the git tag.** Tag the merge commit `vX.Y.Z` with a signed - tag and push it. The tag archive - `https://github.com/tinyland-inc/tinyvectors/archive/refs/tags/vX.Y.Z.tar.gz` - is the release artifact. The tag push runs `publish.yml` with - `dry_run: false` and `npm_publish_mode: disabled`: the same validation - lane as CI against the tagged release candidate. registry.npmjs.org is - never published to (the template's `publish-npm` job is fully gated off by - `npm_publish_mode: disabled`). The GitHub Packages channel - (`npm.pkg.github.com`, published as `@tinyland-inc/tinyvectors` through - v0.3.4; the v0.3.5 attempt failed E401) was retired 2026-07-25 per - TIN-3165: `github_package_name` is removed from both workflow callers and - `check-release-metadata.mjs` asserts it stays absent, so the template's - `publish-github` job can no longer fire. v0.3.3/v0.3.4 on GitHub Packages - remain historical artifacts. The git tag plus the bazel-registry entry are - the only authoritative artifacts. + annotated tag and push it, then create the GitHub release with the + CHANGELOG section as notes. The tag archive + `https://github.com/xoxd-ai/tinyvectors/archive/refs/tags/vX.Y.Z.tar.gz` + is the release artifact. No workflow runs on tags and nothing is published + to registry.npmjs.org or GitHub Packages (v0.3.3/v0.3.4 on GitHub Packages + remain historical artifacts). 3. **Publish the bazel-registry entry.** Add `modules/tummycrypt_tinyvectors/X.Y.Z/` to - [tinyland-inc/bazel-registry](https://github.com/tinyland-inc/bazel-registry): + [xoxd-ai/bazel-registry](https://github.com/xoxd-ai/bazel-registry): - a `MODULE.bazel` snapshot that is byte-identical to the `MODULE.bazel` inside the tag archive; - a `source.json` whose URL names that tag archive and whose integrity - field carries the archive's sha256 in SRI form; + field carries the sha256 of the downloaded archive in SRI form; - an append of `X.Y.Z` to the module's `metadata.json` versions list. -4. **Update consumers.** Migrated pnpm consumers — - `Jesssullivan/dsa-woodshed.space` and - `Great-Falls-Tool-Bus/greatfallstoolbus.org` today, with - `Jesssullivan/jesssullivan.github.io` in review — pin the new tag tarball - and record the archive's sha256 in their prepare-hook SRI maps - (`PINNED_INTEGRITY` in each repo's `scripts/build-tinyvectors.mjs`), which - rebuild `dist/` from source and refuse archives that do not match the - registry pin. Bazel consumers bump - `bazel_dep(name = "tummycrypt_tinyvectors", version = "X.Y.Z")`. - Known unmigrated consumer: `tinyland-inc/transcendsurvival.org` still pins - 0.2.5 from registry.npmjs.org with no verified hook. +4. **Update consumers.** Bazel consumers bump + `bazel_dep(name = "tummycrypt_tinyvectors", version = "X.Y.Z")` and refresh + `MODULE.bazel.lock`. Consumers that still pin a tag tarball through a + `prepare` hook (for example `Jesssullivan/dsa-woodshed.space` and + `Great-Falls-Tool-Bus/greatfallstoolbus.org`) move to the registry module + (RU6). A release is not done until the registry entry exists and the consumer pins match it; the tag alone is necessary but not sufficient. @@ -143,13 +132,10 @@ match it; the tag alone is necessary but not sufficient. The npm channel is retired. The last version on registry.npmjs.org is 0.3.0; 0.3.1+ are authoritative as git tags plus bazel-registry entries (v0.3.3 and -v0.3.4 additionally landed on GitHub Packages via the template's -`publish-github` job — see the Release Ritual note above). `package.json` -keeps its `publishConfig` (including `provenance`, which the metadata gate -ties to the workflow's `id-token: write` permission) and its `prepublishOnly` -guard so that release metadata stays internally consistent and any exceptional -future publish would still run the full gate chain — but none is planned, and -`npm_publish_mode: disabled` in both workflows is the enforced default. +v0.3.4 additionally landed on GitHub Packages). Since 1.0.0 the package is +`private`, has no `publishConfig` or `prepublishOnly`, and the repo has no +publish workflow (RU8). Deprecating the old npmjs and GitHub Packages versions +with a pointer to the Bazel module is an operator step (RV18/RV19). ## FlakeHub Status diff --git a/flake.nix b/flake.nix index 3657547..0379a28 100644 --- a/flake.nix +++ b/flake.nix @@ -24,7 +24,7 @@ bazel pkgs.bazelisk pkgs.nodejs_22 - (pkgs.pnpm_9 or pkgs.pnpm) + (pkgs.pnpm_10 or pkgs.pnpm) ]; shellHook = '' echo "tinyvectors dev shell" diff --git a/package.json b/package.json index 490d44c..e55bad7 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,22 @@ { "name": "@tummycrypt/tinyvectors", - "version": "0.3.7", + "version": "1.0.0", + "private": true, "description": "Animated vector blob backgrounds with physics simulation for Svelte 5", "type": "module", - "packageManager": "pnpm@9.15.9", + "packageManager": "pnpm@10.13.1", "license": "Zlib", "author": { "name": "Jess Sullivan", "email": "jess@sulliwood.org" }, - "homepage": "https://github.com/tinyland-inc/tinyvectors#readme", + "homepage": "https://github.com/xoxd-ai/tinyvectors#readme", "bugs": { - "url": "https://github.com/tinyland-inc/tinyvectors/issues" + "url": "https://github.com/xoxd-ai/tinyvectors/issues" }, "repository": { "type": "git", - "url": "git+https://github.com/tinyland-inc/tinyvectors.git" + "url": "git+https://github.com/xoxd-ai/tinyvectors.git" }, "keywords": [ "svelte", @@ -71,7 +72,7 @@ "build": "vite build && node scripts/build-declarations.mjs", "dev": "vite --config vite.dev.config.ts", "dev:watch": "vite build --watch", - "check": "svelte-check --tsconfig ./tsconfig.json", + "check": "svelte-check --tsgo --tsconfig ./tsconfig.json", "check:release-metadata": "node scripts/check-release-metadata.mjs", "check:package": "publint", "check:bundle-size": "node scripts/check-bundle-size.mjs", @@ -79,38 +80,34 @@ "test": "vitest run", "test:browser:motion": "node scripts/probe-motion-cdp.mjs", "test:watch": "vitest", - "test:pbt": "vitest run --testNamePattern='INVARIANT'", - "prepublishOnly": "pnpm run check:release-metadata && pnpm run build && pnpm run check:package && pnpm run check:bundle-size" + "test:pbt": "vitest run --testNamePattern='INVARIANT'" }, "peerDependencies": { - "svelte": ">=5.20.0" + "svelte": "5.57.2" }, "devDependencies": { - "@sveltejs/package": "^2.5.7", - "@sveltejs/vite-plugin-svelte": "^5.1.1", - "@types/node": "^22.0.0", - "fast-check": "^3.23.0", - "publint": "^0.2.0", - "svelte": "^5.55.4", - "svelte-check": "^4.4.6", - "typescript": "^5.0.0", - "vite": "^6.4.2", - "vitest": "^4.1.4" - }, - "publishConfig": { - "registry": "https://registry.npmjs.org/", - "access": "public", - "provenance": true + "@sveltejs/vite-plugin-svelte": "7.3.1", + "@types/node": "^22.20.1", + "fast-check": "^4.10.2", + "publint": "^0.3.25", + "svelte": "5.57.2", + "svelte-check": "4.7.6", + "typescript": "7.0.2", + "vite": "8.3.3", + "vitest": "5.0.3" }, "pnpm": { "overrides": { "picomatch": "^4.0.4" }, + "patchedDependencies": { + "svelte-check@4.7.6": "patches/svelte-check@4.7.6.patch" + }, "onlyBuiltDependencies": [ "esbuild" ] }, "engines": { - "node": ">=22.0.0" + "node": ">=22.12.0" } } diff --git a/patches/@sveltejs__kit@3.0.1.patch b/patches/@sveltejs__kit@3.0.1.patch new file mode 100644 index 0000000..11319d7 --- /dev/null +++ b/patches/@sveltejs__kit@3.0.1.patch @@ -0,0 +1,116 @@ +# Upstream: https://github.com/sveltejs/kit (packages/kit), tag @sveltejs/kit@3.0.1 +# Files: src/core/sync/ts.js, src/core/sync/write_tsconfig/index.js, +# src/core/sync/write_types/index.js +# Reason: RU13 (TypeScript 7.0.2 as `typescript`). TS 7 ships no in-process +# compiler API (`require('typescript')` is only { version, +# versionMajorMinor }), so `svelte-kit sync` crashed in +# write_tsconfig (`ts.sys.readFile` undefined). Kit now loads the +# API from `typescript` when present, else from Microsoft's +# `@typescript/typescript6` companion; `$types` generation needs only +# an installed `typescript`, and the API-only steps (load-function +# proxies, tsconfig validation) skip cleanly when no API exists. +# Upstream-ready diff: patches/upstream/sveltejs-kit-sync-typescript7.diff +# Remove when Kit supports TypeScript 7 natively. +diff --git a/src/core/sync/ts.js b/src/core/sync/ts.js +index 7f161e6..90b9759 100644 +--- a/src/core/sync/ts.js ++++ b/src/core/sync/ts.js +@@ -1,6 +1,24 @@ + /** @type {typeof import('typescript')} */ + // @ts-ignore + export let ts = undefined; ++ ++/** ++ * Whether a `typescript` package is installed at all. TypeScript 7 (the native ++ * port) installs as `typescript` but ships no in-process compiler API, so this ++ * can be true while `ts` stays undefined. ++ */ ++export let ts_installed = false; ++ + try { +- ts = (await import('typescript')).default; ++ const mod = (await import('typescript')).default; ++ ts_installed = true; ++ if (typeof mod?.createSourceFile === 'function') ts = mod; + } catch {} ++ ++if (ts_installed && !ts) { ++ // TypeScript 7: load the TypeScript 6 API that Microsoft publishes as ++ // `@typescript/typescript6` for tools that need it beside TypeScript 7. ++ try { ++ ts = (await import('@typescript/typescript6')).default; ++ } catch {} ++} +diff --git a/src/core/sync/write_tsconfig/index.js b/src/core/sync/write_tsconfig/index.js +index a543762..802ea8c 100644 +--- a/src/core/sync/write_tsconfig/index.js ++++ b/src/core/sync/write_tsconfig/index.js +@@ -14,14 +14,9 @@ import * as e from '../../../messages/build-errors.js'; + import * as w from '../../../messages/build-warnings.js'; + import { posixify } from '../../../utils/os.js'; + import { bullet_list } from '../../../utils/format.js'; +- +-/** @type {typeof import('typescript')} */ +-let ts; +-try { +- ts = await import('typescript'); +-} catch { +- // The user has not installed TypeScript. +-} ++// `ts` is undefined when TypeScript is not installed, or when only TypeScript 7 ++// is installed without the `@typescript/typescript6` API package. ++import { ts } from '../ts.js'; + + /** + * Generates the tsconfig that the user's tsconfig inherits from. +@@ -110,7 +105,7 @@ function write_parent_tsconfig(root, id, config, transform) { + */ + function validate_config(dir, options) { + if (!ts) { +- // The user has not installed TypeScript. Skip validation of config. ++ // No TypeScript compiler API is available. Skip validation of config. + return; + } + +diff --git a/src/core/sync/write_types/index.js b/src/core/sync/write_types/index.js +index a542b76..78010dc 100644 +--- a/src/core/sync/write_types/index.js ++++ b/src/core/sync/write_types/index.js +@@ -4,7 +4,7 @@ import MagicString from 'magic-string'; + import { walk, resolve_entry } from '../../../utils/filesystem.js'; + import { compact } from '../../../utils/array.js'; + import { posixify } from '../../../utils/os.js'; +-import { ts } from '../ts.js'; ++import { ts, ts_installed } from '../ts.js'; + import { is_page_route } from '../create_manifest_data/index.js'; + const remove_relative_parent_traversals = (/** @type {string} */ path) => + path.replace(/\.\.\//g, ''); +@@ -33,7 +33,7 @@ const is_whitespace = (/** @type {string} */ char) => /\s/.test(char); + * @param {string} root The project root directory + */ + export function write_all_types(config, manifest_data, root) { +- if (!ts) return; ++ if (!ts_installed) return; + + const types_dir = `${config.outDir}/types`; + +@@ -141,7 +141,7 @@ export function write_all_types(config, manifest_data, root) { + * @param {string} root The project root directory + */ + export function write_types(config, manifest_data, file, root) { +- if (!ts) return; ++ if (!ts_installed) return; + + if (!path.basename(file).startsWith('+')) { + // Not a route file +@@ -631,6 +631,10 @@ function generate_params_type(params, outdir, config) { + export function tweak_types(content, is_server) { + const names = new Set(is_server ? ['load', 'actions'] : ['load']); + ++ // Without a compiler API (TypeScript 7 with no `@typescript/typescript6`), ++ // skip the proxy: route types still generate, untyped `load` stays untyped. ++ if (!ts) return null; ++ + try { + let modified = false; + diff --git a/patches/README.md b/patches/README.md new file mode 100644 index 0000000..e5bf49f --- /dev/null +++ b/patches/README.md @@ -0,0 +1,107 @@ +# TypeScript 7 patch set (RU13) + +RU13 (operator ruling, 2026-10-08): the estate runs TypeScript 7.0.2 as the +`typescript` package together with SvelteKit 3.0.1. There is no fallback to +TypeScript 6 as `typescript`. Where a tool needs a change to work with +TypeScript 7, the change is a small patch applied here. site.scaffold is the +home of this patch set; other repos copy `patches/`, `.pnpmfile.cjs` and the +`pnpm` block of `package.json` from site.scaffold `main` instead of writing +their own. + +## Why tools need help + +TypeScript 7.0.2 is the native (Go) compiler. Its npm package ships `tsc` and +an IPC API under `typescript/unstable/*`, but its main entry point exports only +`{ version, versionMajorMinor }`. Tools that parse TypeScript in-process with +`ts.createSourceFile` (SvelteKit's `svelte-kit sync`, svelte2tsx inside +svelte-check, typescript-eslint) therefore cannot use it. Microsoft publishes +the TypeScript 6 API as [`@typescript/typescript6`](https://www.npmjs.com/package/@typescript/typescript6) +for exactly this case: tools load it beside TypeScript 7. It is a parser and +API library here; nothing type-checks with it. + +Type checking runs on TypeScript 7.0.2 everywhere: `svelte-check --tsgo` +(Bazel `//:svelte_check_test`), plain `tsc`, and rules_ts. + +## What is applied, and how + +| Tool | Mechanism | File | +|---|---|---| +| `@sveltejs/kit` 3.0.1 | pnpm `patchedDependencies` | `@sveltejs__kit@3.0.1.patch` | +| `svelte-check` 4.7.6 (`--tsgo` finds TypeScript 7 as `typescript`; a fresh run clears its stale emit directory) | pnpm `patchedDependencies` | `svelte-check@4.7.6.patch` | +| `svelte-check` 4.7.6 (in-process parser) | pnpm `readPackage` hook: peer `typescript` becomes a dependency on the companion | `.pnpmfile.cjs` | +| typescript-eslint 8.71.1 (all `@typescript-eslint/*` that load TS) and `ts-api-utils` | the same hook | `.pnpmfile.cjs` | +| `@sveltejs/kit` 3.0.1 (companion lookup) | pnpm `packageExtensions`: optional peer `@typescript/typescript6` | `package.json` | +| `devalue` 5.9.4 (not TypeScript 7: house rule) | pnpm `patchedDependencies`; the only em dash that survives into the client bundle, caught by `//:leak_scan_test` | `devalue@5.9.4.patch` | + +Each `*.patch` file starts with a header naming the upstream repository, the +files and the reason. pnpm applies them at install (`patchedDependencies` in +`package.json`); Bazel applies the same files because `npm_translate_lock` +reads `patchedDependencies` from `pnpm-lock.yaml` and the patch files are in +its `data`. + +These packages declare `typescript` as a peer, and pnpm resolves a peer from +the root, where it is 7.0.2. Neither pnpm `overrides` nor `packageExtensions` +can redirect a peer (tested 2026-10-08: both still linked 7.0.2), so +`.pnpmfile.cjs` rewrites those manifests: the `typescript` peer becomes a +dependency on `npm:@typescript/typescript6@6.0.2`. Only the listed packages +see it; the root `typescript` (and every `tsc` binary on the path) stays +7.0.2. pnpm records the rewritten graph and `pnpmfileChecksum` in +`pnpm-lock.yaml`, which is what rules_js reads; the hook file is also in +`npm_translate_lock`'s `data`. The estate manifest (`estate/versions.json`) +keeps `typescript` pinned at 7.0.2 and forbids the `@typescript/native` +alias. + +The companion is a shim, not a separate parser. `@typescript/typescript6` +6.0.2 is `module.exports = require("@typescript/old")` with a dependency +`"@typescript/old": "npm:typescript@^6"`, which both lockfiles resolve to +`typescript@6.0.3`. So the in-process parser that Kit sync, svelte2tsx and +typescript-eslint load is TypeScript 6.0.3 code. pnpm links that copy only as +`@typescript/old` inside the companion; no package resolves it as +`typescript`, no `tsc` binary on the path is 6.x, and nothing type-checks with +it. Whether a TypeScript 6 parser inside the companion is within RU13 ("do not +fall back to TypeScript 6.0.3") is an operator ruling, requested by U1. The +estate manifest does not yet allow or forbid `@typescript/typescript6` +explicitly, and its alias check reads `package.json` only, so it does not see +the hook's rewrite. + +## Gaps (exact) + +- **typescript-eslint 8.71.1** declares `typescript >=4.8.4 <6.1.0` and imports + `typescript` in about 180 modules. There is no TypeScript 7 mode, and + patching every import is not a minimal change, so its parser runs on the + companion through the hook above. Lint rules that need type information + would also run on the companion; the scaffold's ESLint config is not + type-aware, so no type-aware rule runs today. +- **svelte-check** has no in-process TypeScript 7 path: svelte2tsx needs a JS + parser. `--tsgo` is the native mode: svelte2tsx output is written to disk and + TypeScript 7.0.2 (`typescript/bin/tsc`) checks it. Under `--tsgo`, a tsconfig + without `include` reports 0 errors (sveltejs/language-tools#3136); the + scaffold's tsconfig lists `include`, and `//:svelte_check_canary_test` + proves a seeded error still fails. +- **Type exports from `.svelte` files.** Under `--tsgo`, a `.svelte` file + imported through a package.json `imports` subpath (`#lib/...`) resolves to + the ambient `*.svelte` module declaration, so named type exports from + `\n\n
{canary}
\n", + ); +} + +const child = spawn( + process.execPath, + [ + svelteCheckCli, + '--tsgo', + '--tsconfig', + './tsconfig.json', + ...(canary ? ['--output', 'machine'] : []), + ], + { cwd: scratch, stdio: canary ? ['ignore', 'pipe', 'inherit'] : 'inherit' }, +); + +let output = ''; +child.stdout?.on('data', (chunk) => { + output += chunk; + process.stdout.write(chunk); +}); + +child.on('error', (error) => { + cleanup(); + console.error(error); + process.exit(1); +}); + +child.on('exit', (code) => { + cleanup(); + if (!canary) process.exit(code ?? 1); + const errorLines = output.split('\n').filter((line) => / ERROR /u.test(line)); + const missing = [CANARY_TS, CANARY_SVELTE].filter( + (file) => !errorLines.some((line) => line.includes(file)), + ); + if (code === 0 || missing.length > 0) { + console.error( + `svelte-check --tsgo canary FAILED: exit ${code}; seeded errors not reported in ${missing.join(', ') || '(none)'}`, + ); + process.exit(1); + } + console.log('svelte-check --tsgo canary: both seeded type errors were reported, the gate is live'); + process.exit(0); +}); diff --git a/scripts/build-declarations.mjs b/scripts/build-declarations.mjs index e71f716..3ea96b1 100644 --- a/scripts/build-declarations.mjs +++ b/scripts/build-declarations.mjs @@ -1,10 +1,19 @@ import { spawnSync } from 'node:child_process'; +import { readFileSync } from 'node:fs'; import { createRequire } from 'node:module'; +import { dirname, join } from 'node:path'; const require = createRequire(import.meta.url); const packageRoot = new URL('../', import.meta.url); const declarationsConfig = new URL('../tsconfig.declarations.json', import.meta.url); -const tscPath = require.resolve('typescript/lib/tsc.js'); +// TypeScript 7 exposes only package.json and its version stub through the +// exports map, so resolve the `tsc` bin through package.json (RU13). +const tscPackageJson = require.resolve('typescript/package.json'); +const tscManifest = JSON.parse(readFileSync(tscPackageJson, 'utf8')); +const tscPath = join( + dirname(tscPackageJson), + typeof tscManifest.bin === 'string' ? tscManifest.bin : tscManifest.bin.tsc, +); const result = spawnSync(process.execPath, [tscPath, '-p', declarationsConfig.pathname], { cwd: packageRoot, stdio: 'inherit', diff --git a/scripts/check-bundle-size.mjs b/scripts/check-bundle-size.mjs index 7365b0d..520676c 100644 --- a/scripts/check-bundle-size.mjs +++ b/scripts/check-bundle-size.mjs @@ -43,14 +43,14 @@ console.log(TinyVectors); logLevel: 'silent', build: { write: false, - minify: 'esbuild', + minify: true, target: 'es2022', - rollupOptions: { + rolldownOptions: { input: entry, external: (id) => id === 'svelte' || id.startsWith('svelte/'), output: { format: 'es', - inlineDynamicImports: true, + codeSplitting: false, }, }, }, diff --git a/scripts/check-package-consumer.mjs b/scripts/check-package-consumer.mjs index b3a9784..a1bb025 100644 --- a/scripts/check-package-consumer.mjs +++ b/scripts/check-package-consumer.mjs @@ -1,5 +1,5 @@ import { spawnSync } from 'node:child_process'; -import { existsSync } from 'node:fs'; +import { existsSync, readFileSync } from 'node:fs'; import { mkdir, mkdtemp, rm, symlink, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { dirname, join, resolve } from 'node:path'; @@ -10,7 +10,14 @@ const require = createRequire(import.meta.url); const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..'); const packageDir = resolve(process.cwd(), process.argv[2] ?? './bazel-bin/pkg'); const svelteDir = resolve(repoRoot, 'node_modules/svelte'); -const tscPath = require.resolve('typescript/lib/tsc.js'); +// TypeScript 7 exposes only package.json and its version stub through the +// exports map, so resolve the `tsc` bin through package.json (RU13). +const tscPackageJson = require.resolve('typescript/package.json'); +const tscManifest = JSON.parse(readFileSync(tscPackageJson, 'utf8')); +const tscPath = join( + dirname(tscPackageJson), + typeof tscManifest.bin === 'string' ? tscManifest.bin : tscManifest.bin.tsc, +); if (!existsSync(resolve(packageDir, 'package.json'))) { console.error(`Package directory is missing package.json: ${packageDir}`); diff --git a/scripts/check-release-metadata.mjs b/scripts/check-release-metadata.mjs index 75a2a02..4efe14e 100644 --- a/scripts/check-release-metadata.mjs +++ b/scripts/check-release-metadata.mjs @@ -1,4 +1,4 @@ -import { readFileSync } from 'node:fs'; +import { existsSync, readFileSync } from 'node:fs'; const read = (relativePath) => readFileSync(new URL(relativePath, import.meta.url), 'utf8'); @@ -7,7 +7,6 @@ const packageJson = JSON.parse(read('../package.json')); const moduleBazel = read('../MODULE.bazel'); const buildBazel = read('../BUILD.bazel'); const ciWorkflow = read('../.github/workflows/ci.yml'); -const publishWorkflow = read('../.github/workflows/publish.yml'); const changelog = read('../CHANGELOG.md'); const expectedPnpmVersion = packageJson.packageManager?.replace(/^pnpm@/, ''); const expectedNodeMajor = packageJson.engines?.node?.match(/>=\s*(\d+)/)?.[1]; @@ -16,8 +15,6 @@ const expectedBazelTargets = const expectedPackageDir = './bazel-bin/pkg'; const expectedPackageConsumerCommand = 'node scripts/check-package-consumer.mjs ./bazel-bin/pkg'; const expectedPackageCheckCommand = 'pnpm run check:package && pnpm run check:bundle-size'; -const expectedPrepublishOnlyCommand = - 'pnpm run check:release-metadata && pnpm run build && pnpm run check:package && pnpm run check:bundle-size'; const expectedSharedWorkflowInputs = { runner_mode: 'repo_owned', runner_labels_json: '${{ vars.PRIMARY_LINUX_RUNNER_LABELS_JSON }}', @@ -125,8 +122,10 @@ const checks = [ }, { label: 'CI reusable package workflow', - actual: sharedPackageWorkflow(ciWorkflow, 'CI reusable workflow'), - expected: sharedPackageWorkflow(publishWorkflow, 'publish reusable workflow'), + actual: /^(?:tinyland-inc|xoxd-ai)\//.test(sharedPackageWorkflow(ciWorkflow, 'CI reusable workflow')) + ? 'pinned' + : 'unpinned', + expected: 'pinned', }, { label: 'package consumer check script', @@ -138,59 +137,54 @@ const checks = [ actual: packageJson.scripts?.['check:bundle-size'], expected: 'node scripts/check-bundle-size.mjs', }, + // RU6/RU8 (2026-10-08): Bazel is the only distribution path. The package + // is private, carries no publish config or publish hook, and the repo has + // no tag-triggered publish workflow. + { + label: 'package.json private', + actual: String(packageJson.private === true), + expected: 'true', + }, + { + label: 'package.json publishConfig', + actual: packageJson.publishConfig ? 'present' : 'absent', + expected: 'absent', + }, { label: 'prepublishOnly script', - actual: packageJson.scripts?.prepublishOnly, - expected: expectedPrepublishOnlyCommand, + actual: packageJson.scripts?.prepublishOnly ? 'present' : 'absent', + expected: 'absent', + }, + { + label: '.github/workflows/publish.yml', + actual: existsSync(new URL('../.github/workflows/publish.yml', import.meta.url)) + ? 'present' + : 'absent', + expected: 'absent', }, { label: 'CI publish dry-run', actual: extractWorkflowValue(ciWorkflow, 'dry_run', 'CI dry_run'), expected: 'true', }, - { - label: 'tag publish dry-run', - actual: extractWorkflowValue(publishWorkflow, 'dry_run', 'publish dry_run'), - expected: 'false', - }, ]; for (const [key, expected] of Object.entries(expectedSharedWorkflowInputs)) { - checks.push( - { - label: `CI ${key}`, - actual: extractWorkflowValue(ciWorkflow, key, `CI ${key}`), - expected, - }, - { - label: `publish ${key}`, - actual: extractWorkflowValue(publishWorkflow, key, `publish ${key}`), - expected, - }, - ); -} - -// GitHub Packages channel retired 2026-07-25 (TIN-3165); the input must stay absent in both workflows. -for (const [label, source] of [ - ['CI', ciWorkflow], - ['publish', publishWorkflow], -]) { checks.push({ - label: `${label} github_package_name`, - actual: /^\s*github_package_name:/m.test(source) ? 'present' : 'absent', - expected: 'absent', + label: `CI ${key}`, + actual: extractWorkflowValue(ciWorkflow, key, `CI ${key}`), + expected, }); } -const failures = checks.filter((check) => check.actual !== check.expected); +// GitHub Packages channel retired 2026-07-25 (TIN-3165); the input must stay absent. +checks.push({ + label: 'CI github_package_name', + actual: /^\s*github_package_name:/m.test(ciWorkflow) ? 'present' : 'absent', + expected: 'absent', +}); -if (packageJson.publishConfig?.provenance && !/id-token:\s*write/.test(publishWorkflow)) { - failures.push({ - label: 'publish workflow id-token permission', - actual: 'missing', - expected: 'write', - }); -} +const failures = checks.filter((check) => check.actual !== check.expected); if (failures.length > 0) { for (const failure of failures) { diff --git a/tests/unit/themes.test.ts b/tests/unit/themes.test.ts new file mode 100644 index 0000000..d963465 --- /dev/null +++ b/tests/unit/themes.test.ts @@ -0,0 +1,69 @@ +import { describe, expect, it } from 'vitest'; +import { + getThemeCatalog, + getThemeCatalogEntry, + getThemePreviewColors, + getThemeVectorColors, + getThemePreset, +} from '../../src/themes/index.js'; + +describe('theme helpers', () => { + it('returns a vector preset when it exists', () => { + const preset = getThemePreset('tinyland'); + + expect(preset?.name).toBe('tinyland'); + expect(preset?.hasVectors).toBe(true); + }); + + it('returns preview swatches for vector themes', () => { + expect(getThemePreviewColors('trans')).toEqual([ + 'rgba(91, 206, 250, 0.60)', + 'rgba(245, 169, 184, 0.65)', + 'rgba(242, 242, 245, 0.50)', + ]); + }); + + it('returns the richer rendered palette for vector themes', () => { + expect(getThemeVectorColors('tinyland')).toEqual([ + 'rgba(139, 92, 246, 0.55)', + 'rgba(59, 130, 246, 0.55)', + 'rgba(236, 72, 153, 0.50)', + 'rgba(242, 242, 245, 0.45)', + ]); + }); + + it('returns no vector palette for non-vector themes', () => { + expect(getThemeVectorColors('high-contrast')).toEqual([]); + expect(getThemePreviewColors('high-contrast')).toEqual([]); + }); + + it('returns package-owned metadata for vector themes', () => { + expect(getThemeCatalogEntry('tinyland')).toMatchObject({ + name: 'tinyland', + label: 'Tinyland', + description: 'Soft violet, blue, and pink glow', + source: 'tinyvectors', + hasVectors: true, + }); + }); + + it('returns package-owned metadata for non-vector themes', () => { + expect(getThemeCatalogEntry('high-contrast')).toMatchObject({ + name: 'high-contrast', + label: 'High Contrast', + description: 'WCAG AAA compliant for maximum readability', + previewColors: ['#000000', '#FFFFFF', '#0040FF'], + vectorColors: [], + hasVectors: false, + }); + }); + + it('returns the package theme catalog in display order', () => { + expect(getThemeCatalog().map((theme) => theme.name)).toEqual([ + 'tinyland', + 'trans', + 'pride', + 'high-contrast', + ]); + }); +}); diff --git a/tsconfig.declarations.json b/tsconfig.declarations.json index 5c050e6..9e2755b 100644 --- a/tsconfig.declarations.json +++ b/tsconfig.declarations.json @@ -6,6 +6,7 @@ "declaration": true, "declarationMap": true, "outDir": "dist-types", + "rootDir": "./src", "skipLibCheck": true, "types": ["svelte", "node"] } diff --git a/tsconfig.json b/tsconfig.json index 1cd0fc6..267fcf7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,10 +12,6 @@ "isolatedModules": true, "declaration": true, "declarationMap": true, - "baseUrl": ".", - "paths": { - "$lib/*": ["src/*"] - }, "types": ["svelte"] }, "include": ["src/**/*"], diff --git a/vite.config.ts b/vite.config.ts index 557e8c2..789893b 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -24,16 +24,17 @@ export default defineConfig({ build: { lib: { entry: { - index: resolve(__dirname, 'src/index.ts'), - 'core/index': resolve(__dirname, 'src/core/index.ts'), - 'motion/index': resolve(__dirname, 'src/motion/index.ts'), - 'themes/index': resolve(__dirname, 'src/themes/index.ts'), - 'svelte/index': resolve(__dirname, 'src/svelte/index.ts'), + index: resolve(import.meta.dirname, 'src/index.ts'), + 'core/index': resolve(import.meta.dirname, 'src/core/index.ts'), + 'motion/index': resolve(import.meta.dirname, 'src/motion/index.ts'), + 'themes/index': resolve(import.meta.dirname, 'src/themes/index.ts'), + 'svelte/index': resolve(import.meta.dirname, 'src/svelte/index.ts'), }, formats: ['es'], }, - rollupOptions: { + // Vite 8 bundles with Rolldown; `rollupOptions` is only a deprecated alias. + rolldownOptions: { external: [/^svelte($|\/)/], output: { preserveModules: true, @@ -61,13 +62,14 @@ export default defineConfig({ target: 'es2022', - minify: 'esbuild', + // Vite 8 default minifier (Oxc); esbuild is no longer a Vite dependency. + minify: true, }, resolve: { alias: { - $lib: resolve(__dirname, 'src'), + $lib: resolve(import.meta.dirname, 'src'), }, }, }); diff --git a/vite.dev.config.ts b/vite.dev.config.ts index 514c272..d0dfbdf 100644 --- a/vite.dev.config.ts +++ b/vite.dev.config.ts @@ -6,7 +6,7 @@ const devServerPort = Number(process.env.TINYVECTORS_VITE_PORT ?? 5175); const hmrPort = Number(process.env.TINYVECTORS_VITE_HMR_PORT ?? 24679); export default defineConfig({ - root: resolve(__dirname, 'dev'), + root: resolve(import.meta.dirname, 'dev'), plugins: [ svelte({ @@ -18,7 +18,7 @@ export default defineConfig({ resolve: { alias: { - $lib: resolve(__dirname, 'src'), + $lib: resolve(import.meta.dirname, 'src'), }, },