netxops/scripts/build-client.mjs
oliver 69a4820369 Stop requiring dsh-client-store in the Plugins card bundle.
Shipped dsh web seed has no client-store entry; use a private snapshot store and only externalize platform seed modules.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-04 22:38:52 +08:00

83 lines
2.6 KiB
JavaScript

/**
* Build lib/client.js as a DSH ModuleLoader factory artifact using Bun's bundler.
* Usage: bun run scripts/build-client.mjs
*
* Externals must match the *installed* web shell seed table. Shipped
* `@deepseek-ai/dsh` 0.1.1-rc.2 seeds only:
* react, react/jsx-runtime, react-dom, react-dom/client,
* @deepseek-ai/cordis, dsh-client-ui-slots, dsh-client-ui-primitives
* Do not leave other @deepseek-ai packages as require() targets unless they
* are also listed in dsh.client.inject and arrive as graph factories.
*/
import { mkdirSync, writeFileSync } from 'node:fs'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
const root = join(dirname(fileURLToPath(import.meta.url)), '..')
const entry = join(root, 'src/client/index.ts')
const outFile = join(root, 'lib', 'client.js')
const id = 'dsh-netxops'
/** Exact PLATFORM_MODULES keys from shipped dsh-web-frontend seed. */
const external = [
'react',
'react/jsx-runtime',
'react-dom',
'react-dom/client',
'@deepseek-ai/cordis',
'@deepseek-ai/dsh-client-ui-slots',
'@deepseek-ai/dsh-client-ui-primitives',
]
const result = await Bun.build({
entrypoints: [entry],
target: 'browser',
format: 'cjs',
sourcemap: 'none',
minify: false,
define: {
'process.env.NODE_ENV': JSON.stringify('production'),
},
external,
})
if (!result.success) {
console.error(result.logs)
throw new Error('Bun.build failed')
}
const raw = await result.outputs[0].text()
if (raw.includes('react/jsx-dev-runtime')) {
throw new Error('client bundle still references react/jsx-dev-runtime; refuse to ship')
}
if (raw.includes('@deepseek-ai/dsh-client-store')) {
throw new Error('client bundle still requires dsh-client-store; refuse to ship')
}
const requires = [...raw.matchAll(/require\("([^"]+)"\)/g)].map(m => m[1])
const allowed = new Set(external)
for (const spec of requires) {
if (!allowed.has(spec)) {
throw new Error(`client bundle requires undeclared external "${spec}"; refuse to ship`)
}
}
const body = raw
.replace(/^"use strict";\s*/m, '')
.replace(/Object\.defineProperty\(exports,\s*"__esModule"[\s\S]*?;\s*/m, '')
const artifact = `window.__ModuleLoader__.load({
id: ${JSON.stringify(id)},
factory: (require) => {
var module = { exports: {} };
var exports = module.exports;
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
${body}
return module.exports;
}
});
`
mkdirSync(dirname(outFile), { recursive: true })
writeFileSync(outFile, artifact, 'utf8')
console.log(`wrote ${outFile} (${artifact.length} bytes); requires: ${[...new Set(requires)].join(', ') || '(none)'}`)