Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* Invariant: the @assistant-ui dependency cluster agrees on one tap version.
|
||||
*
|
||||
* The Hermes desktop app (``apps/desktop``) is built from source on every
|
||||
* install/update via ``scripts/install.ps1`` → ``npm ci``/``npm install`` →
|
||||
* ``tsc -b && vite build``. The ``@assistant-ui`` packages share an internal
|
||||
* reactivity lib, ``@assistant-ui/tap``, and they only interoperate when they
|
||||
* all resolve the *same* tap version:
|
||||
*
|
||||
* - ``@assistant-ui/react@0.12.28`` and ``@assistant-ui/core`` pin
|
||||
* ``@assistant-ui/tap@^0.5.x`` (which exports ``.`` and ``./react``).
|
||||
* - ``@assistant-ui/store@0.2.18`` bumped its tap peer to ``^0.9.0`` and started
|
||||
* importing ``@assistant-ui/tap/react-shim`` — an entry point that only exists
|
||||
* in the tap ``0.9.x`` line.
|
||||
*
|
||||
* Because ``react@0.12.28`` requests ``store@^0.2.9`` (a caret range), a fresh
|
||||
* install silently floated ``store`` up to ``0.2.18``, which then could not find
|
||||
* ``./react-shim`` in the hoisted ``tap@0.5.x`` and crashed ``vite build`` with:
|
||||
*
|
||||
* "./react-shim" is not exported ... from package @assistant-ui/tap
|
||||
*
|
||||
* i.e. the opaque "apps/desktop build failed (exit 1)" every user hit when
|
||||
* updating. The fix pins ``@assistant-ui/store`` (via root ``overrides``) to the
|
||||
* last release that targets ``tap@^0.5.x``.
|
||||
*
|
||||
* This is a *contract* test, not a snapshot: it does not assert specific version
|
||||
* numbers, only that the cluster resolves a single shared tap (wherever npm
|
||||
* places it — hoisted to root, or nested under the ``apps/desktop`` workspace
|
||||
* since the 0.14 bump dropped the ``store`` override) and that this tap satisfies
|
||||
* every ``@assistant-ui/*`` package's declared requirement. It fails if any
|
||||
* future bump reintroduces a split tap version or requirement across the cluster.
|
||||
*/
|
||||
|
||||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
import { test } from 'vitest'
|
||||
|
||||
const REPO_ROOT = path.resolve(__dirname, '..')
|
||||
const LOCK_PATH = path.join(REPO_ROOT, 'package-lock.json')
|
||||
const TAP = '@assistant-ui/tap'
|
||||
|
||||
/**
|
||||
* Minimal npm semver check for the ranges this cluster actually uses.
|
||||
*
|
||||
* Supports exact versions, ``^x.y.z`` (with correct 0.x semantics), and
|
||||
* ``||`` unions. Pre-release tags are ignored (none are used here).
|
||||
*/
|
||||
function caretSatisfies(version: string, spec: string): boolean {
|
||||
function parse(v: string): [number, number, number] {
|
||||
const core = v.replace(/^[^0-9]+/, '').split('-')[0].split('+')[0]
|
||||
const parts = core.split('.').slice(0, 3)
|
||||
|
||||
while (parts.length < 3) {parts.push('0')}
|
||||
|
||||
return [parseInt(parts[0], 10), parseInt(parts[1], 10), parseInt(parts[2], 10)]
|
||||
}
|
||||
|
||||
const ver = parse(version)
|
||||
|
||||
for (const clause of spec.split('||')) {
|
||||
const trimmed = clause.trim()
|
||||
|
||||
if (!trimmed) {continue}
|
||||
|
||||
if (trimmed.startsWith('^')) {
|
||||
const lo = parse(trimmed)
|
||||
|
||||
if (ver[0] < lo[0] || (ver[0] === lo[0] && ver[1] < lo[1]) || (ver[0] === lo[0] && ver[1] === lo[1] && ver[2] < lo[2])) {continue}
|
||||
let hi: [number, number, number]
|
||||
|
||||
if (lo[0] > 0) {
|
||||
hi = [lo[0] + 1, 0, 0]
|
||||
} else if (lo[1] > 0) {
|
||||
hi = [0, lo[1] + 1, 0]
|
||||
} else {
|
||||
hi = [0, 0, lo[2] + 1]
|
||||
}
|
||||
|
||||
if (
|
||||
(ver[0] < hi[0]) ||
|
||||
(ver[0] === hi[0] && ver[1] < hi[1]) ||
|
||||
(ver[0] === hi[0] && ver[1] === hi[1] && ver[2] < hi[2])
|
||||
) {
|
||||
return true
|
||||
}
|
||||
} else if (trimmed[0].match(/\d/) || trimmed.startsWith('v')) {
|
||||
if (ver[0] === parse(trimmed)[0] && ver[1] === parse(trimmed)[1] && ver[2] === parse(trimmed)[2]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
interface LockPackage {
|
||||
version?: string
|
||||
dependencies?: Record<string, string>
|
||||
peerDependencies?: Record<string, string>
|
||||
peerDependenciesMeta?: Record<string, { optional?: boolean }>
|
||||
}
|
||||
|
||||
function lockPackages(): Record<string, LockPackage> {
|
||||
if (!fs.existsSync(LOCK_PATH)) {return {}}
|
||||
const lock = JSON.parse(fs.readFileSync(LOCK_PATH, 'utf-8'))
|
||||
|
||||
return (lock.packages ?? {}) as Record<string, LockPackage>
|
||||
}
|
||||
|
||||
function sharedTapVersion(packages: Record<string, LockPackage>): string {
|
||||
/** The one tap version every install site resolves to. */
|
||||
const versions = new Set<string>()
|
||||
|
||||
for (const [key, meta] of Object.entries(packages)) {
|
||||
const idx = key.lastIndexOf('node_modules/')
|
||||
const name = idx >= 0 ? key.slice(idx + 'node_modules/'.length) : key
|
||||
|
||||
if (name === TAP) {
|
||||
if (meta.version) {versions.add(meta.version)}
|
||||
}
|
||||
}
|
||||
|
||||
assert.ok(versions.size > 0, 'package-lock.json has no @assistant-ui/tap entry — the @assistant-ui cluster should resolve a single shared tap version.')
|
||||
assert.ok(versions.size === 1, `@assistant-ui/tap resolves to multiple versions ${[...versions].sort()} — the cluster must share one tap line (see this test's docstring).`)
|
||||
|
||||
return [...versions][0]!
|
||||
}
|
||||
|
||||
test('every @assistant-ui/* package\'s tap requirement is satisfiable', () => {
|
||||
const packages = lockPackages()
|
||||
|
||||
if (Object.keys(packages).length === 0) {return} // lockfile not materialized
|
||||
|
||||
const tapVersion = sharedTapVersion(packages)
|
||||
|
||||
const offenders: string[] = []
|
||||
|
||||
for (const [key, meta] of Object.entries(packages)) {
|
||||
const idx = key.lastIndexOf('node_modules/')
|
||||
const name = idx >= 0 ? key.slice(idx + 'node_modules/'.length) : key
|
||||
|
||||
if (!name.startsWith('@assistant-ui/') || name === TAP) {continue}
|
||||
const peerMeta = (meta.peerDependenciesMeta ?? {})[TAP]
|
||||
|
||||
if (peerMeta?.optional) {continue}
|
||||
const spec = (meta.dependencies ?? {})[TAP] || (meta.peerDependencies ?? {})[TAP]
|
||||
|
||||
if (!spec) {continue}
|
||||
|
||||
if (!caretSatisfies(tapVersion, spec)) {
|
||||
offenders.push(`${name} requires ${TAP}"${spec}"`)
|
||||
}
|
||||
}
|
||||
|
||||
assert.deepEqual(
|
||||
offenders,
|
||||
[],
|
||||
`Hoisted ${TAP}@${tapVersion} does not satisfy: ` +
|
||||
offenders.join('; ') +
|
||||
'. The @assistant-ui cluster has split tap requirements — pin the ' +
|
||||
'offending package (e.g. via root package.json `overrides`) so the ' +
|
||||
'whole cluster shares one tap line. See this test\'s module docstring.'
|
||||
)
|
||||
})
|
||||
|
||||
test('caretSatisfies helper', () => {
|
||||
assert.ok(caretSatisfies('0.5.14', '^0.5.10'))
|
||||
assert.ok(caretSatisfies('0.5.14', '^0.5.14'))
|
||||
assert.ok(!caretSatisfies('0.5.14', '^0.9.0'))
|
||||
assert.ok(!caretSatisfies('0.5.14', '^0.6.0'))
|
||||
assert.ok(caretSatisfies('1.2.5', '^1.2.0'))
|
||||
assert.ok(!caretSatisfies('2.0.0', '^1.2.0'))
|
||||
assert.ok(caretSatisfies('0.5.14', '^0.5.0 || ^0.9.0'))
|
||||
})
|
||||
Reference in New Issue
Block a user