spike(modules): carry /public/atlas/* behind the proposed module surface
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>
This commit is contained in:
43
modules/uo/client/vite.config.js
Normal file
43
modules/uo/client/vite.config.js
Normal file
@@ -0,0 +1,43 @@
|
||||
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 },
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user