Skip to content

Fix #60: restore root use.{js,cjs,mjs} CDN mirrors broken by 8.14.0 src/ move#61

Merged
konard merged 8 commits into
mainfrom
issue-60-4e765a3f3c78
Jun 16, 2026
Merged

Fix #60: restore root use.{js,cjs,mjs} CDN mirrors broken by 8.14.0 src/ move#61
konard merged 8 commits into
mainfrom
issue-60-4e765a3f3c78

Conversation

@konard

@konard konard commented Jun 15, 2026

Copy link
Copy Markdown
Member

Summary

Fixes #60. After [email protected] moved the entry files into src/, the historical CDN bootstrap URL https://unpkg.com/use-m/use.js started returning 404. Consumers that eval() the response body without checking the HTTP status evaluated the 404 text as JavaScript and crashed with the cryptic:

SyntaxError: Unexpected identifier 'found'

This PR restores the root-level use.js / use.cjs / use.mjs files while keeping src/ as the single source of truth, so every existing consumer keeps working unchanged after upgrading — and documents how to fix affected projects on any version.

Root cause

  • 8.14.0 (PR Resilient CDN loading: shared loadWithFallback engine + per-package fallback + robust use-m/load bootstrap (#58) #59, commit ecee09e) moved use.{js,cjs,mjs} and friends into src/ and updated the package.json exports map.
  • unpkg and jsDelivr serve raw published files and ignore the exports map. A bare URL like https://unpkg.com/use-m/use.js only resolves if a real file lives at the package root. After the move, that path 404'd:
    • unpkg body: Not found: /[email protected]/use.jseval()SyntaxError: Unexpected identifier 'found'
  • The long-standing bootstrap (e.g. @link-assistant/hive-mind's use-m-bootstrap.lib.mjs) does eval(await (await fetch(url)).text()), so the 404 body was eval'd directly.

Reproduced end-to-end in experiments/issue60-root-url-regression.mjs.

CDN behavior, verified against the live services (2026-06-15)

URL Result
https://unpkg.com/use-m/use.js 404 Not found
https://unpkg.com/[email protected]/use.js 404 Not found
https://cdn.jsdelivr.net/npm/[email protected]/use.js 404 Couldn't find the requested file
https://cdn.jsdelivr.net/npm/use-m/use.js (unversioned) 200, but stale pre-src/ copy ⚠️
https://unpkg.com/use-m/src/use.js 200, valid current JS
https://cdn.jsdelivr.net/npm/use-m/src/use.js 200, valid current JS

⚠️ The reason the maintainer's analysis saw jsDelivr return 200 for the unversioned bare URL: jsDelivr falls back to an older cached version that still had a root use.js, so it silently serves outdated code. The pinned @8.14.0 bare URL correctly 404s. The only reliable cross-CDN workaround is the explicit /src/ path.

The fix

  • Restored root mirrors use.js, use.cjs, use.mjs as full copies of src/use.*, generated by scripts/sync-root-entries.mjs (npm run sync:entries). src/ remains canonical; the mirrors carry an AUTO-GENERATED header.
  • Why full copies, not require('./src/use.js') shims? (The shim was the maintainer's first suggestion.) The bootstrap fetches and eval()s use.js directly, so a shim runs in the consumer's scope, where it just swaps one cryptic error for another instead of returning a working use. Verified experimentally:
    • In the real ESM consumer (use-m-bootstrap.lib.mjs): ReferenceError: module is not defined
    • In a browser: ReferenceError: require is not defined
    • In CJS with the wrong CWD: Cannot find module './src/use.js'
    • Even if it resolved, src/use.js has no module.exports (it ends in a bare makeUse(...) expression), so a shim yields {} — never a working use.
  • package.json: added the three mirrors to files[], bumped 8.14.08.14.1 (triggers the release workflow), and added the sync:entries script. The exports map still maps ./use.cjs and ./use.mjs to src/ (real modules); use.js stays eval-only and is intentionally not advertised as an importable subpath (matching historical behavior).
  • Drift guard: tests/root-entries.test.mjs (14 cases) asserts the mirrors exist, stay byte-for-byte in sync with src/, eval/import to a working use, and ship in the npm tarball — and pins the regression (the 404 body throws SyntaxError; the mirror does not).

Confirmed via npm pack --dry-run that the published tarball now contains both the root mirrors and src/ (15 files).

How to fix projects that use use-m (also in the README)

A new README subsection — Troubleshooting: SyntaxError: Unexpected identifier 'found' — documents three remedies:

  1. Recommended — upgrade. Ensure your CDN URL resolves to 8.14.1+ (e.g. pin https://unpkg.com/[email protected]/use.js). No code change needed.
  2. One-line workaround (works on every version, incl. 8.14.0). Point the URL at the src/ path on any CDN:
    • https://unpkg.com/use-m/use.jshttps://unpkg.com/use-m/src/use.js
    • https://cdn.jsdelivr.net/npm/use-m/use.jshttps://cdn.jsdelivr.net/npm/use-m/src/use.js
  3. Future-proof. Replace the naive eval(await (await fetch(url)).text()) with a guarded loader that checks response.ok, rejects non-JS bodies, and falls back across mirrors — the packaged use-m/load helper or the self-contained snippet in the README. This prevents any future 404/redirect from being silently eval'd.

Testing

All three runtimes pass, including the new regression test:

Runtime Result
Node (Jest) 44 suites, 330 tests passed
Bun (bun test) 330 pass, 0 fail
Deno (deno test …) 31 passed (173 steps), 0 failed

CI (node/bun/deno × ubuntu/macos) is green on the latest commit.

Files changed

  • experiments/issue60-root-url-regression.mjs — reproduction (incl. shim-failure demo)
  • scripts/sync-root-entries.mjs — mirror generator (npm run sync:entries)
  • use.js, use.cjs, use.mjs — restored root mirrors (auto-generated)
  • package.jsonfiles[], version 8.14.1, sync:entries script
  • tests/root-entries.test.mjs — drift + regression guard
  • README.md, CHANGELOG.md, CONTRIBUTING.md — docs

Closes #60

Adding .gitkeep for PR creation (default mode).
This file will be removed when the task is complete.

Issue: #60
@konard konard self-assigned this Jun 15, 2026
konard added 4 commits June 15, 2026 09:02
The 8.14.0 release moved use.js/use.cjs/use.mjs under src/, which 404'd
the historical bootstrap URL https://unpkg.com/use-m/use.js. unpkg/jsdelivr
serve raw files and ignore package.json "exports", so the bare path could
no longer resolve. A naive eval() of the 404 body ("Not found: /[email protected]/use.js")
throws the cryptic "SyntaxError: Unexpected identifier 'found'" from the issue.

This script reproduces the failure end to end and confirms src/use.js still
eval()s cleanly, pinpointing the fix: restore root-level mirrors of src/.
Republish root-level use.js/use.cjs/use.mjs so the long-standing CDN
bootstrap URLs (https://unpkg.com/use-m/use.js, .cjs, .mjs) resolve again
after the 8.14.0 move to src/. unpkg/jsdelivr serve raw published files and
ignore package.json "exports", so a bare /use-m/use.js only resolves when a
real file lives at the package root.

The mirrors are FULL copies, not require('./src/use.js') shims: the historical
bootstrap fetches and eval()s use.js directly, and a require/import shim cannot
work in an eval() context (require is undefined; the relative base is the caller,
not use-m). src/ stays the single source of truth.

- scripts/sync-root-entries.mjs regenerates the mirrors (npm run sync:entries)
- package.json: add mirrors to files[], bump 8.14.0 -> 8.14.1, add sync:entries
- exports map keeps ./use.cjs and ./use.mjs (real modules); use.js stays
  eval-only and is intentionally not advertised as an importable subpath

Refs #60
tests/root-entries.test.mjs asserts the root use.{js,cjs,mjs} mirrors exist,
stay byte-for-byte in sync with src/ (via scripts/sync-root-entries.mjs),
eval()/import() to a working use function, and are shipped in the npm package
while src/ remains canonical. It also pins the core regression: eval()'ing the
404 "Not found" body throws a SyntaxError, whereas the restored mirror does not.

Passes across all three runtimes (Jest/Node, Bun, Deno) via the cross-runtime
test adapter.

Refs #60
- README: add a "Troubleshooting: SyntaxError: Unexpected identifier 'found'"
  subsection explaining the 8.14.0 regression and three remedies (upgrade to
  8.14.1, switch the URL to the /src/ path on any CDN, or harden the bootstrap
  with use-m/load). Verified against the live CDNs that the bare /use.js 404s on
  unpkg AND jsDelivr on 8.14.0, so switching host alone does not help.
- CHANGELOG: add a CRITICAL Fixed entry for the restored root mirrors and note
  on the src/ move that bare URLs work again via the mirrors.
- CONTRIBUTING: document the generated root mirrors, the npm run sync:entries
  step after editing src/use.*, and the guarding test.

Refs #60
@konard konard changed the title [WIP] After latest release multiple projects which used use-m failed Fix #60: restore root use.{js,cjs,mjs} CDN mirrors broken by 8.14.0 src/ move Jun 15, 2026
@konard konard marked this pull request as ready for review June 15, 2026 09:03
Verified against the live CDNs and runtimes:
- jsDelivr's *unversioned* bare URL returns 200 but serves a STALE pre-src/-move
  copy (silently outdated code); the *pinned* @8.14.0/use.js 404s. unpkg 404s for
  both. So README now recommends the /src/ path as the reliable cross-CDN fix and
  warns about jsDelivr's stale unversioned response, instead of claiming the bare
  path uniformly 404s.
- The maintainer-suggested shim (module.exports = require('./src/use.js')) cannot
  fix the eval bootstrap: in the real ESM consumer it throws "ReferenceError:
  module is not defined"; in a browser "require is not defined"; in CJS with the
  wrong CWD "Cannot find module"; and src/use.js has no module.exports so it would
  yield {} anyway. The experiment now demonstrates this, justifying full copies.

Refs #60
@konard

konard commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

🤖 Solution Draft Log

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost estimation:

  • Public pricing estimate: $7.648964
  • Calculated by Anthropic: $8.625078
  • Difference: $0.976114 (+12.76%)

📊 Context and tokens usage:

Claude Opus 4.8: (2 sub-sessions)

  1. 116.0K / 1M (12%) input tokens, 49.7K / 128K (39%) output tokens
  2. 93.0K / 1M (9%) input tokens, 35.2K / 128K (28%) output tokens

Total: (20.1K new + 262.2K cache writes + 7.3M cache reads) input tokens, 90.5K output tokens, $7.648964 cost

🤖 Models used:

  • Tool: Anthropic Claude Code
  • Requested: opus
  • Model: Claude Opus 4.8 (claude-opus-4-8)

📎 Log file uploaded as Gist (3339KB)


Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard

konard commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

✅ Ready to merge

This pull request is now ready to be merged:

  • All CI checks have passed
  • No merge conflicts
  • No pending changes

Monitored by hive-mind with --auto-restart-until-mergeable flag

@konard

konard commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

We need to fix CI/CD, and double check all changes are correct, and requirements from the issue fully addressed.

@konard konard marked this pull request as draft June 15, 2026 12:30
@konard

konard commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

🤖 AI Work Session Started

Starting automated work session at 2026-06-15T12:30:42.459Z

The PR has been converted to draft mode while work is in progress.

This comment marks the beginning of an AI work session. Please wait for the session to finish, and provide your feedback.

@konard konard marked this pull request as ready for review June 15, 2026 12:34
@konard

konard commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

✅ CI fixed, changes re-verified

CI/CD

The only failing check was Test on deno (ubuntu-latest), and the failure was a transient external-CDN flake — not a regression from this PR:

[mjs module] typescript ... use typescript
error: Failed to import 'typescript' from any CDN mirror.
  - deno   (1/1): Failed to import from 'https://esm.sh/typescript@latest'
  - jspm   (1/1): Failed to import from 'https://jspm.dev/typescript'
  - skypack(1/1): Failed to import from 'https://cdn.skypack.dev/typescript'

Evidence it's a flake, not our code:

  • The previous run on the exact same commit passed (run 27535790422, all green).
  • deno (macos-latest) passed the same typescript test in the same run; only the ubuntu shard hit the CDN hiccup.
  • The typescript test imports a third-party package live from public CDNs and is untouched by this PR.

I re-ran the failed job and all 7 checks are now green.

Re-verification of the fix (requirements from #60)

  1. Root cause fixed while keeping src/ — root use.{js,cjs,mjs} are restored as full mirrors of src/ (src/ stays the single source of truth). Confirmed no drift: npm run sync:entries produces no changes.
  2. Downstream-fix instructions — README troubleshooting section documents upgrade / /src/ URL workaround / guarded loader.

Local verification on this branch:

  • npm test44 suites, 330 tests passed.
  • tests/root-entries.test.mjs14/14 passed (mirrors exist, byte-for-byte in sync with src/, eval/import to a working use, ship in the tarball, and the 404-body regression is pinned).
  • experiments/issue60-root-url-regression.mjs reproduces the original SyntaxError: Unexpected identifier 'found' and confirms the restored mirrors eval() to a working use.
  • main is merged into the branch; working tree clean.

Ready for review.

@konard

konard commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

Working session summary

I've resolved the situation. Here's the summary:

What was wrong

The PR (#61) for issue #60 had one failing check: Test on deno (ubuntu-latest).

Root cause of the CI failure

A transient external-CDN flake in the typescript test — unrelated to the PR's changes. The test imports the typescript package live from public CDNs (esm.sh, jspm, skypack), and all three momentarily failed on the ubuntu deno shard. Proof it was a flake, not a regression:

  • The prior run on the identical commit had passed (all green).
  • deno (macos-latest) passed the same test in the same run.
  • I re-ran the failed job → all 7 checks now green.

Verification of the fix itself (issue #60 requirements)

I re-checked both requirements:

  1. Root cause fixed while keeping src/ — root use.{js,cjs,mjs} restored as full mirrors of src/, with src/ remaining canonical. npm run sync:entries produces zero drift.
  2. Downstream-fix instructions — documented in README (upgrade / /src/ URL workaround / guarded loader).

Local results on the branch:

  • npm test44 suites, 330 tests passed
  • tests/root-entries.test.mjs14/14 passed
  • The reproduction experiment confirms the original SyntaxError: Unexpected identifier 'found' and that the restored mirrors eval() to a working use.
  • main merged into branch; working tree clean.

Actions taken

I deliberately did not modify the flaky typescript test — it's an external-CDN reliability matter outside the scope of issue #60, and touching the core loader's retry behavior for all packages would add unwarranted risk to an otherwise clean, focused fix. If you'd like, I can open a separate change to harden that test (e.g. more retries or skipping on network errors) so the flake stops recurring in CI.


This summary was automatically extracted from the AI working session output.

@konard

konard commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

🤖 Solution Draft Log

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost estimation:

  • Public pricing estimate: $0.916566
  • Calculated by Anthropic: $1.058702
  • Difference: $0.142136 (+15.51%)

📊 Context and tokens usage:

  • 50.3K / 1M (5%) input tokens, 9.1K / 128K (7%) output tokens

Total: (3.3K new + 37.9K cache writes + 873.6K cache reads) input tokens, 9.1K output tokens, $0.916566 cost

🤖 Models used:

  • Tool: Anthropic Claude Code
  • Requested: opus
  • Model: Claude Opus 4.8 (claude-opus-4-8)

📎 Log file uploaded as Gist (755KB)


Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard

konard commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

🔄 Auto-restart 1/5

Detected uncommitted changes from previous run. Starting new session to review and commit or discard them.

Uncommitted files:

M package-lock.json

Auto-restart will stop after changes are committed or discarded, or after 4 more iterations. Please wait until working session will end and give your feedback.

@konard

konard commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

Working session summary

Working tree is clean, the lockfile is committed, and main is already merged into the branch.

The uncommitted change has been resolved. Here's the summary:

What the uncommitted change was

package-lock.json still showed 8.14.0 while package.json had already been bumped to 8.14.1 (committed in 850d4c6 as the release trigger for the #60 fix). The lockfile simply needed to be synced to match.

Action taken

Committed the lockfile sync and pushed:

Verification

  • git status → clean working tree, no uncommitted changes
  • main is confirmed merged into the branch

This was a legitimate, focused change (lockfile version alignment for the release), not stray work — so committing rather than reverting was correct. The PR (#61) state is otherwise unchanged and remains the fix for issue #60.


This summary was automatically extracted from the AI working session output.

@konard

konard commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

🔄 Auto-restart 1/5 Log

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost estimation:

  • Public pricing estimate: $0.225594
  • Calculated by Anthropic: $1.352347
  • Difference: $1.126753 (+499.46%)

📊 Context and tokens usage:

  • 30.6K / 1M (3%) input tokens, 1.5K / 128K (1%) output tokens

Total: (2.9K new + 18.1K cache writes + 120.4K cache reads) input tokens, 1.5K output tokens, $0.225594 cost

🤖 Models used:

  • Tool: Anthropic Claude Code
  • Requested: opus
  • Model: Claude Opus 4.8 (claude-opus-4-8)

📎 Log file uploaded as Gist (1109KB)


Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard

konard commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

✅ Ready to merge

This pull request is now ready to be merged:

  • All CI checks have passed
  • No merge conflicts
  • No pending changes

Monitored by hive-mind with --auto-restart-until-mergeable flag

@konard konard merged commit ec33e83 into main Jun 16, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

After latest release multiple projects which used use-m failed

1 participant