Enable routing to env variable

This commit is contained in:
snen 2021-09-29 16:00:39 -04:00
parent 8acfa11bf1
commit 79205efae3
3 changed files with 76 additions and 13 deletions

View File

@ -3,10 +3,10 @@ import useWebSocket from 'react-use-websocket'
import assertNever from '~/common/assertNever.ts' import assertNever from '~/common/assertNever.ts'
import { useUser } from '../user.tsx'
import { AsyncHandle, GameCommand, GameAction } from './types.ts' import { AsyncHandle, GameCommand, GameAction } from './types.ts'
const WS_URL = Deno.env.get("WS_URL")
const MY_ID = (function(){ const MY_ID = (function(){
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
@ -81,10 +81,9 @@ interface SocketHandle {
export default function useServerSocket( export default function useServerSocket(
onUpdate: (action: GameAction) => void onUpdate: (action: GameAction) => void
): AsyncHandle<SocketHandle> { ): AsyncHandle<SocketHandle> {
const profile = useUser()
const [state, dispatch] = React.useReducer(reducer, initialState) const [state, dispatch] = React.useReducer(reducer, initialState)
// prep socket /* prep socket */
const onMessage = React.useCallback((message: WebSocketEventMap['message']) => { const onMessage = React.useCallback((message: WebSocketEventMap['message']) => {
const data = JSON.parse(message.data) const data = JSON.parse(message.data)
@ -157,13 +156,8 @@ export default function useServerSocket(
dispatch({type: 'close'}) dispatch({type: 'close'})
}, []) }, [])
const url = React.useMemo(
() => `ws://localhost:7636/ws`,
[profile],
)
const {sendJsonMessage} = useWebSocket( const {sendJsonMessage} = useWebSocket(
url, WS_URL,
{onMessage, onOpen, onError, onClose, shouldReconnect}, {onMessage, onOpen, onError, onClose, shouldReconnect},
) )
@ -182,7 +176,7 @@ export default function useServerSocket(
}) })
}, [state, sendJson]) }, [state, sendJson])
// effects to push the coordinator along /* effects to push the coordinator along */
React.useEffect(() => { React.useEffect(() => {
if (state.status !== 'finding-game') return if (state.status !== 'finding-game') return
@ -194,7 +188,7 @@ export default function useServerSocket(
sendJson({command: 'join', player_id: state.playerId, match_id: state.matchId}) sendJson({command: 'join', player_id: state.playerId, match_id: state.matchId})
}, [sendJson, state]) }, [sendJson, state])
// return game command handler in wrapper /* return game command handler in wrapper */
const handle = React.useMemo<AsyncHandle<SocketHandle>>(() => { const handle = React.useMemo<AsyncHandle<SocketHandle>>(() => {
switch (state.status) { switch (state.status) {

1
run-local.sh Executable file
View File

@ -0,0 +1 @@
WS_URL=ws://localhost:7636 deno run --allow-read --allow-write --unstable --allow-env --allow-net --import-map import_map.json ./server/server.ts

View File

@ -5,6 +5,66 @@ import { Router } from 'oak'
// import App from '~/client/App.tsx' // import App from '~/client/App.tsx'
function indexOfAll(source: string, query: string): number[] {
let currentPosition = 0
const buffer: number[] = []
while (currentPosition !== -1) {
const lastPosition = currentPosition
currentPosition = source.indexOf(query, lastPosition + 1)
if (currentPosition !== -1) buffer.push(currentPosition)
}
// ensure it's sorted in case while loop betrays us
return buffer.sort()
}
interface ResolvedEnvVar {
callsiteBounds: [number, number]
varname: string
value?: string
}
const ENV_SEARCH_STRING = "Deno.env.get(\""
const ENV_SEARCH_END_STRING = "\")"
function resolveClientDenoEnvVars(source: string): string {
const startIndices = indexOfAll(source, ENV_SEARCH_STRING)
const resolved = startIndices.map((startIndex) => {
const endIndex = source.indexOf(ENV_SEARCH_END_STRING, startIndex)
const varname = source.slice(startIndex + ENV_SEARCH_STRING.length, endIndex)
const bounds = [startIndex, endIndex + ENV_SEARCH_END_STRING.length]
const value = Deno.env.get(varname)
return {
callsiteBounds: bounds,
varname,
value: `"${value}"`,
}
})
function replaceBounds(input: string, newText: string | undefined, start: number, end: number) {
return `${input.slice(0, start)}${newText}${input.slice(end)}`
}
// reassemble source code by reversing the order and replacing strings
const reassembledSource = resolved.reverse().reduce(
(currentSource, {callsiteBounds, value, varname}) => {
if (value === undefined)
throw new Error(`Deno env variable ${varname} found in client code but not provided to server.`)
return replaceBounds(currentSource, value, callsiteBounds[0], callsiteBounds[1])
},
source,
)
return reassembledSource
}
function mapDict<T, U>(
dict: Record<string, T>,
fn: (entry: [string, T], index: number) => [string, U],
): Record<string, U> {
return Object.fromEntries(
Object.entries(dict).map(fn)
)
}
async function createClientBundle() { async function createClientBundle() {
const {files} = await Deno.emit('server/client.tsx', { const {files} = await Deno.emit('server/client.tsx', {
bundle: 'module', bundle: 'module',
@ -16,7 +76,15 @@ async function createClientBundle() {
strictPropertyInitialization: false, strictPropertyInitialization: false,
}, },
}) })
return files const bundle = mapDict(files, ([path, source], i) => {
const resolved = resolveClientDenoEnvVars(source)
if (Deno.env.get("DEBUG")) {
Deno.writeTextFileSync(`debug/output${i}.js`, source)
Deno.writeTextFileSync(`debug/resolved${i}.js`, resolved)
}
return [path, resolved]
})
return bundle
} }
const bundle = await createClientBundle() const bundle = await createClientBundle()