mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
* perf(orcad): keep the server bundle ASCII so V8 stores it one byte per character 129 non-ASCII characters in regex literals (and three in a String.raw plugin template) made V8 hold the whole ~11 MB orcad-server.js source as a two-byte string. Write them as \uXXXX escapes and fail the build if any return. Co-Authored-By: Claude <noreply@anthropic.com> * fix(orcad): keep license comments ASCII and name the module the guard trips on esbuild gathers legal comments at the end of the bundle; escape that comment so a dependency's license text cannot fail the ASCII guard, while licenses still ship with the code. The guard now reports the offset and, from an unminified rebuild, the module the character came from. A dark-switch build is covered. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
131 lines
4.6 KiB
JavaScript
131 lines
4.6 KiB
JavaScript
import { build } from 'esbuild'
|
|
import { mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { afterAll, beforeAll, expect, it } from 'vitest'
|
|
import {
|
|
buildOrcadEntry,
|
|
escapeLegalCommentTail,
|
|
findNonAsciiSource,
|
|
moduleAtOffset,
|
|
ORCAD_COMMONJS_MODULE_OPTIONS
|
|
} from './orcad-entry-build.mjs'
|
|
import { runProcessSync } from '@orca/process-host'
|
|
|
|
const directory = mkdtempSync(join(tmpdir(), 'orca-structured-provider-build-'))
|
|
const bundle = join(directory, 'orcad-server.js')
|
|
|
|
beforeAll(async () => {
|
|
await buildOrcadEntry(bundle)
|
|
}, 60_000)
|
|
|
|
afterAll(() => {
|
|
rmSync(directory, { recursive: true, force: true })
|
|
})
|
|
|
|
it('loads the structured Claude SDK through the packaged server under plain Node', () => {
|
|
const loaded = runProcessSync({
|
|
program: process.execPath,
|
|
args: [bundle, '--orcad-structured-provider-load-check'],
|
|
cwd: directory,
|
|
env: { ORCA_BACKGROUND_LAUNCH: '1' },
|
|
timeoutMs: 30_000
|
|
})
|
|
expect(loaded.code, loaded.stderr.slice(-2_000)).toBe(0)
|
|
}, 40_000)
|
|
|
|
it('resolves bundle-relative requires after deployment to a path with URL-special characters', async () => {
|
|
const buildDirectory = join(directory, 'build')
|
|
mkdirSync(buildDirectory)
|
|
const outfile = join(buildDirectory, 'entry.cjs')
|
|
await build({
|
|
...ORCAD_COMMONJS_MODULE_OPTIONS,
|
|
stdin: {
|
|
contents: `import { createRequire } from 'node:module';
|
|
console.log(createRequire(import.meta.url)('./asset.cjs'));`,
|
|
resolveDir: directory
|
|
},
|
|
bundle: true,
|
|
platform: 'node',
|
|
target: 'node18',
|
|
format: 'cjs',
|
|
outfile,
|
|
minify: true,
|
|
logLevel: 'error'
|
|
})
|
|
writeFileSync(join(buildDirectory, 'asset.cjs'), 'module.exports = "deployed asset"')
|
|
const deployedDirectory = join(directory, 'deployed # 100%')
|
|
renameSync(buildDirectory, deployedDirectory)
|
|
const loaded = runProcessSync({
|
|
program: process.execPath,
|
|
args: [join(deployedDirectory, 'entry.cjs')],
|
|
cwd: directory,
|
|
env: { ORCA_BACKGROUND_LAUNCH: '1' },
|
|
timeoutMs: 30_000
|
|
})
|
|
expect(loaded.code, loaded.stderr).toBe(0)
|
|
expect(loaded.stdout.trim()).toBe('deployed asset')
|
|
})
|
|
|
|
it.each(['import.meta', 'import.meta.dirname', 'import.meta.filename', 'import.meta.resolve'])(
|
|
'rejects unsupported %s instead of silently replacing it with an empty object',
|
|
async (expression) => {
|
|
await expect(
|
|
build({
|
|
...ORCAD_COMMONJS_MODULE_OPTIONS,
|
|
stdin: { contents: `console.log(${expression})`, resolveDir: directory },
|
|
bundle: true,
|
|
platform: 'node',
|
|
format: 'cjs',
|
|
write: false,
|
|
logLevel: 'silent'
|
|
})
|
|
).rejects.toThrow('"import.meta" is not available')
|
|
}
|
|
)
|
|
|
|
it('keeps the server bundle ASCII, so V8 stores its source one byte per character', () => {
|
|
expect(findNonAsciiSource(readFileSync(bundle, 'utf8'))).toBeNull()
|
|
})
|
|
|
|
it('names the line of a non-ASCII character a regex literal or tagged template left', () => {
|
|
expect(findNonAsciiSource('a=1\nb=/[\\u2726]/')).toBeNull()
|
|
expect(findNonAsciiSource('a=1\nb=/[\u2726]/')).toMatchObject({ offset: 8, line: 2 })
|
|
})
|
|
|
|
it('escapes only the trailing license comment, never code', () => {
|
|
const code = 'a=/[\u2726]/;'
|
|
const escaped = escapeLegalCommentTail(
|
|
`${code}\n/*! Bundled license information:\n (c) Jos\u00e9 \u{1F600}\n*/\n`
|
|
)
|
|
expect(escaped.startsWith(code)).toBe(true)
|
|
expect(escaped).toContain('(c) Jos\\u{e9} \\u{1f600}')
|
|
expect(findNonAsciiSource(escaped.slice(code.length))).toBeNull()
|
|
expect(escapeLegalCommentTail(code)).toBe(code)
|
|
})
|
|
|
|
it('names the module an unminified build placed an offset in', () => {
|
|
const source = 'x\n// src/a.ts\nvar a = 1;\n// node_modules/b/index.js\nvar b = /\u2726/;\n'
|
|
expect(moduleAtOffset(source, source.indexOf('\u2726'))).toBe('node_modules/b/index.js')
|
|
expect(moduleAtOffset(source, source.indexOf('var a'))).toBe('src/a.ts')
|
|
})
|
|
|
|
it('keeps a build with every dark switch turned on ASCII', () => {
|
|
const darkBundle = join(directory, 'orcad-server-dark.js')
|
|
const built = runProcessSync({
|
|
program: process.execPath,
|
|
args: [
|
|
'--input-type=module',
|
|
'-e',
|
|
`const { buildOrcadEntry } = await import(${JSON.stringify(
|
|
new URL('./orcad-entry-build.mjs', import.meta.url).href
|
|
)}); await buildOrcadEntry(${JSON.stringify(darkBundle)})`
|
|
],
|
|
cwd: directory,
|
|
env: { ...process.env, ORCA_LAYOUT_OWNER: 'main', ORCA_SIDEBAR_INPUTS: 'main' },
|
|
timeoutMs: 120_000
|
|
})
|
|
expect(built.code, built.stderr.slice(-2_000)).toBe(0)
|
|
expect(findNonAsciiSource(readFileSync(darkBundle, 'utf8'))).toBeNull()
|
|
}, 150_000)
|