Skip to content

enhance: bump to Takumi 0.70#491

Merged
harlan-zw merged 3 commits intonuxt-modules:mainfrom
kane50613:main
Mar 2, 2026
Merged

enhance: bump to Takumi 0.70#491
harlan-zw merged 3 commits intonuxt-modules:mainfrom
kane50613:main

Conversation

@kane50613
Copy link
Contributor

@kane50613 kane50613 commented Mar 2, 2026

🔗 Linked issue

❓ Type of change

  • 📖 Documentation (updates to the documentation or readme)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

Trying to address the memory issue mentioned in npmx-dev/npmx.dev#1770 npmx-dev/npmx.dev#1654 by getting rid of asUint8Array completely and hope it resolves the issue.

@kane50613 kane50613 marked this pull request as draft March 2, 2026 10:00
@harlan-zw
Copy link
Collaborator

Ignore the ecosystem tests trying to get them running still

@kane50613 kane50613 marked this pull request as ready for review March 2, 2026 10:31
@kane50613
Copy link
Contributor Author

kane50613/takumi@36fba31 eliminates the need to rewrite svgs

@harlan-zw
Copy link
Collaborator

Test timeouts are on main so i'll merge this for now and get that fixed.

@harlan-zw harlan-zw merged commit 1acd7f3 into nuxt-modules:main Mar 2, 2026
0 of 4 checks passed
@harlan-zw
Copy link
Collaborator

harlan-zw commented Mar 2, 2026

This seems to reproduce the issue immediately

/**
 * Reproduces "double free or corruption (fasttop)" crash in @takumi-rs/core.
 *
 * The native Renderer is not safe for concurrent loadFont + render calls on
 * the same instance. This simulates what happens when a Nuxt server handles
 * multiple OG image requests concurrently — each request loads fonts and
 * renders on the shared Renderer instance.
 *
 * Usage: node test/repro-double-free.mjs
 */
import { readFileSync } from 'node:fs'
import { Renderer } from '@takumi-rs/core'

const CONCURRENCY = 50
const ROUNDS = 20

const fontPath = 'test/fixtures/app-dir/.output/public/_og-static-fonts/inter-400-latin.ttf'
const fontData = new Uint8Array(readFileSync(fontPath))

const nodes = {
  type: 'container',
  tw: 'flex w-full h-full bg-slate-900 items-center justify-center',
  children: [
    { type: 'text', text: 'Hello World', tw: 'text-white text-5xl' },
    {
      type: 'container',
      tw: 'flex flex-col gap-2 p-4',
      children: Array.from({ length: 10 }, (_, i) => ({
        type: 'text',
        text: `Item ${i}${'x'.repeat(50)}`,
        tw: 'text-gray-300 text-lg',
      })),
    },
  ],
}
const opts = { width: 1200, height: 630, format: 'png' }

// Simulate concurrent requests: each "request" creates/reuses a renderer,
// loads a font subset, and renders — exactly like the Nuxt server does.
const renderer = new Renderer()

async function simulateRequest(id) {
  // Each request loads a "new" font subset (unique name) then renders
  await renderer.loadFont({
    name: `inter-subset-${id}`,
    data: fontData,
    weight: 400,
    style: 'normal',
  })
  return renderer.render(nodes, opts)
}

for (let round = 1; round <= ROUNDS; round++) {
  console.log(`Round ${round}/${ROUNDS}: ${CONCURRENCY} concurrent loadFont+render...`)
  await Promise.all(Array.from({ length: CONCURRENCY }, (_, i) =>
    simulateRequest(round * CONCURRENCY + i),
  ))
}
console.log(`Completed ${ROUNDS * CONCURRENCY} renders without crash.`)
❯ node test/repro-double-free.mjs
Round 1/20: 50 concurrent loadFont+render...
double free or corruption (fasttop)
[1]    353291 IOT instruction (core dumped)  node test/repro-double-free.mjs
og-image main* ❯                                                                          

@kane50613
Copy link
Contributor Author

Thanks for catching this! I thought napi-rs would have guardrails around accessing to prevent race-conditions like this 😅
fixed in kane50613/takumi@7270512

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.

2 participants