wip-devsec
This commit is contained in:
parent
7d4da13721
commit
d01e19c90c
3 changed files with 26 additions and 11 deletions
|
|
@ -41,14 +41,15 @@ export function resolveDevAsset(rel, root = REPO_ROOT) {
|
|||
const isFile = (p) => existsSync(p) && statSync(p).isFile()
|
||||
// The built copy lives INSIDE the output folder (public/<tree>/assets) — public/ holds nothing else
|
||||
// since BUILD-OUTPUT-IS-ONE-FOLDER. The data/ fallback below keeps a fresh clone working unbuilt.
|
||||
const shipped = resolve(root, 'public', TREE_NAME, 'assets', rel)
|
||||
if (isFile(shipped)) return shipped
|
||||
// data/<rel> is the TRACKED source of truth public/assets mirrors (see .gitignore): generate-tree
|
||||
// writes the mirror, so on a fresh clone — which is what DX receives — public/assets holds almost
|
||||
// nothing and every table, boundary and people-groups file 404'd under the dev server until the
|
||||
// first full build. Measured 2026-09-25 across the studio: 13 plugins mounted with empty data.
|
||||
// data/<rel> FIRST: it is the tracked source of truth that the built copy mirrors. Serving the built copy
|
||||
// first meant a data/ edit showed OLD data under the dev server until someone rebuilt (73 files have
|
||||
// both; found by the multiplayer dev-server spike, 2026-09-26). A dev server shows the source.
|
||||
const source = resolve(root, 'data', rel)
|
||||
if (isFile(source)) return source
|
||||
// The built copy (public/<tree>/assets) still answers for what exists only there: files generate-tree
|
||||
// derives rather than copies.
|
||||
const shipped = resolve(root, 'public', TREE_NAME, 'assets', rel)
|
||||
if (isFile(shipped)) return shipped
|
||||
const profiles = resolve(root, 'plugin-designs')
|
||||
for (const name of readdirSync(profiles)) {
|
||||
if (name.startsWith('_') || name.startsWith('.')) continue
|
||||
|
|
@ -239,6 +240,12 @@ export function autoSpaDevPlugin(token = '') {
|
|||
configureServer(server) {
|
||||
server.middlewares.use((req, res, next) => {
|
||||
const rawPath = (req.url || '').split('?')[0]
|
||||
// NEVER THE REPOSITORY ITSELF. Vite serves the project root, so a reachable dev server answered
|
||||
// /.git/… with 200 — the whole history, and any credential in the remote URL (multiplayer spike,
|
||||
// 2026-09-26). These routes run before Vite's own checks, so the refusal is here.
|
||||
let _decoded = rawPath
|
||||
try { _decoded = decodeURIComponent(rawPath) } catch { /* keep raw */ }
|
||||
if (/(^|\/)\.(git|env)(\/|$)|(^|\/)\.(merge-queue|main-capture)(\/|$)/i.test(_decoded)) { res.statusCode = 404; return res.end() }
|
||||
// A production embed URL (`<bundle>/<profile>[/<instance>]/index.html`) is rewritten onto
|
||||
// the `/__leaf/` route BEFORE any branch runs, so the existing leaf renderer answers it
|
||||
// unchanged — see leafRewrite above for why the dev server must know this shape at all.
|
||||
|
|
|
|||
|
|
@ -28,11 +28,13 @@ import { getCurrentInstance, onMounted } from 'vue'
|
|||
* @param {string} [label] - Optional label for the <style> tag (for debugging)
|
||||
*/
|
||||
export function useShadowStyles(css, label) {
|
||||
// Generate a stable key from the label or a hash of the CSS
|
||||
const key = label || hashCode(css)
|
||||
// Captured in setup (onMounted's callback cannot see it reliably).
|
||||
const instance = getCurrentInstance()
|
||||
// A stable key: the label; else, under the dev server, the component's HMR id (so an edited stylesheet
|
||||
// REPLACES its old copy instead of piling up next to it); else a hash of the CSS (builds: unchanged).
|
||||
const key = label || (instance && instance.type && instance.type.__hmrId ? 'hmr-' + instance.type.__hmrId : hashCode(css))
|
||||
|
||||
onMounted(() => {
|
||||
const instance = getCurrentInstance()
|
||||
if (!instance) return
|
||||
|
||||
// Walk up from the component's root element to find the shadow root
|
||||
|
|
@ -42,8 +44,10 @@ export function useShadowStyles(css, label) {
|
|||
const root = el.getRootNode()
|
||||
if (!(root instanceof ShadowRoot)) return // light DOM — no injection needed
|
||||
|
||||
// Skip if already injected (idempotent)
|
||||
if (root.querySelector(`style[data-shadow-styles="${key}"]`)) return
|
||||
// Already injected: keep it — unless the CSS changed (a hot update), then replace its text. The early
|
||||
// return here used to keep the OLD CSS after an edit, under a label.
|
||||
const existing = root.querySelector(`style[data-shadow-styles="${key}"]`)
|
||||
if (existing) { if (existing.textContent !== css) existing.textContent = css; return }
|
||||
|
||||
const style = document.createElement('style')
|
||||
style.setAttribute('data-shadow-styles', key)
|
||||
|
|
|
|||
|
|
@ -94,6 +94,10 @@ export default defineConfig(({ command, mode }) => {
|
|||
// the maintainer's internal platform and must NOT exist in public
|
||||
// artifacts. Default FALSE strips it from every bundle (dead-branch +
|
||||
// treeshake); an explicitly-flagged platform build sets DOXA_QA=1.
|
||||
// The dev server listens on THIS MACHINE ONLY. A reachable one serves the source tree, node_modules
|
||||
// and the token baked into /; a remote viewer reaches it through an ssh tunnel (ssh -L), never a public
|
||||
// port. `--host` on the command line still overrides this for a deliberate LAN session.
|
||||
server: { host: '127.0.0.1' },
|
||||
define: {
|
||||
__DOXA_QA__: JSON.stringify(process.env.DOXA_QA === '1'),
|
||||
// The output tree's name (vite.doxa-maps-build-config.json `name`, the same value OUT_ROOT uses),
|
||||
|
|
|
|||
Loading…
Reference in a new issue