THROWAWAY BRANCH — evidence for the Phase 1 contract, never merged. See
modules/uo/SPIKE.md and docs/website/MODULE_API.md Part 7.
The six public spawn-atlas routes now live in modules/uo/, reached only through
the ctx/register surface, with the client half loading as a prebuilt ESM chunk.
All three exit criteria met:
• zero internal-file imports from the module into core; the built chunk has
zero bare import specifiers and bundles no React
• routes.manifest.json AND routes.guards.json are byte-identical
• /uo/atlas renders from /modules/uo/entry.js under script-src 'self' with
zero CSP violation reports
729 core tests and 81 module tests pass. Verified end to end against the real
database: the schema fragment replays after core's, onBoot runs the atlas
refresh, and the six API URLs answer unchanged.
Two things the spike changed in the contract:
• ctx.express / ctx.validator. A module lives outside server/, so Node never
reaches server/node_modules and require('express') fails outright — the
server-side twin of the one-React rule, which §2.6 had only for the client.
• window.__rg.jsxRuntime, so a module can build with the automatic JSX
runtime its tooling already assumes rather than being forced to classic.
And it confirmed §6.1 empirically: regenerating the OpenAPI spec silently
deleted all 361 lines of the atlas paths with "Swagger-autogen: Success", while
the route manifest kept all six in the same run. That is exactly the
static-analysis-vs-runtime split the fragment merge exists to prevent.
Co-Authored-By: Claude <noreply@anthropic.com>
44 lines
1.8 KiB
JavaScript
44 lines
1.8 KiB
JavaScript
import { defineConfig } from 'vite'
|
|
import react from '@vitejs/plugin-react'
|
|
import path from 'path'
|
|
|
|
// Library mode: one prebuilt ESM chunk, published by CI and dropped onto the
|
|
// operator's volume. The operator never builds anything (MODULE_SYSTEM.md §2.5).
|
|
//
|
|
// The four externals are the whole contract with core. Declaring them external
|
|
// alone is not enough, though: rollup would emit bare `import 'react'`
|
|
// specifiers, which a browser cannot resolve without an import map — and CSP
|
|
// forbids the inline <script type="importmap"> that would provide one. So each
|
|
// is ALIASED to a two-line shim that re-exports from window.__rg, and the
|
|
// external list then only has to stop Vite from following them into node_modules
|
|
// this package does not have.
|
|
const shim = (f) => path.resolve(import.meta.dirname, 'src/shim', f)
|
|
|
|
export default defineConfig({
|
|
plugins: [react()],
|
|
resolve: {
|
|
// EXACT matches, via the array form. Vite's object form does PREFIX
|
|
// replacement, so a plain `react` key also rewrote `react/jsx-runtime` into
|
|
// `src/shim/react.js/jsx-runtime` — a path that does not exist, and the
|
|
// first thing this build hit.
|
|
alias: [
|
|
{ find: /^react$/, replacement: shim('react.js') },
|
|
{ find: /^react\/jsx-runtime$/, replacement: shim('react-jsx-runtime.js') },
|
|
{ find: /^react-dom\/client$/, replacement: shim('react-dom-client.js') },
|
|
{ find: /^react-router-dom$/, replacement: shim('react-router-dom.js') },
|
|
],
|
|
},
|
|
build: {
|
|
lib: {
|
|
entry: path.resolve(import.meta.dirname, 'src/entry.jsx'),
|
|
formats: ['es'],
|
|
fileName: () => 'entry.js',
|
|
},
|
|
outDir: 'dist',
|
|
emptyOutDir: true,
|
|
// Same reason as core's client: no inline bootstrap script for
|
|
// `script-src 'self'` to trip on.
|
|
modulePreload: { polyfill: false },
|
|
},
|
|
})
|