multiboard
This commit is contained in:
parent
6747c5df02
commit
572608f878
|
|
@ -0,0 +1,30 @@
|
||||||
|
const urls = new Set();
|
||||||
|
|
||||||
|
function checkURL(request, init) {
|
||||||
|
const url =
|
||||||
|
request instanceof URL
|
||||||
|
? request
|
||||||
|
: new URL(
|
||||||
|
(typeof request === "string"
|
||||||
|
? new Request(request, init)
|
||||||
|
: request
|
||||||
|
).url
|
||||||
|
);
|
||||||
|
if (url.port && url.port !== "443" && url.protocol === "https:") {
|
||||||
|
if (!urls.has(url.toString())) {
|
||||||
|
urls.add(url.toString());
|
||||||
|
console.warn(
|
||||||
|
`WARNING: known issue with \`fetch()\` requests to custom HTTPS ports in published Workers:\n` +
|
||||||
|
` - ${url.toString()} - the custom port will be ignored when the Worker is published using the \`wrangler deploy\` command.\n`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
globalThis.fetch = new Proxy(globalThis.fetch, {
|
||||||
|
apply(target, thisArg, argArray) {
|
||||||
|
const [request, init] = argArray;
|
||||||
|
checkURL(request, init);
|
||||||
|
return Reflect.apply(target, thisArg, argArray);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import worker, * as OTHER_EXPORTS from "C:\\Users\\jeffe\\Documents\\GitHub\\canvas-website\\worker\\worker.ts";
|
||||||
|
import * as __MIDDLEWARE_0__ from "C:\\Users\\jeffe\\Documents\\GitHub\\canvas-website\\node_modules\\wrangler\\templates\\middleware\\middleware-ensure-req-body-drained.ts";
|
||||||
|
import * as __MIDDLEWARE_1__ from "C:\\Users\\jeffe\\Documents\\GitHub\\canvas-website\\node_modules\\wrangler\\templates\\middleware\\middleware-miniflare3-json-error.ts";
|
||||||
|
|
||||||
|
export * from "C:\\Users\\jeffe\\Documents\\GitHub\\canvas-website\\worker\\worker.ts";
|
||||||
|
|
||||||
|
export const __INTERNAL_WRANGLER_MIDDLEWARE__ = [
|
||||||
|
|
||||||
|
__MIDDLEWARE_0__.default,__MIDDLEWARE_1__.default
|
||||||
|
]
|
||||||
|
export default worker;
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
// This loads all middlewares exposed on the middleware object and then starts
|
||||||
|
// the invocation chain. The big idea is that we can add these to the middleware
|
||||||
|
// export dynamically through wrangler, or we can potentially let users directly
|
||||||
|
// add them as a sort of "plugin" system.
|
||||||
|
|
||||||
|
import ENTRY, { __INTERNAL_WRANGLER_MIDDLEWARE__ } from "C:\\Users\\jeffe\\Documents\\GitHub\\canvas-website\\.wrangler\\tmp\\bundle-VlWfGj\\middleware-insertion-facade.js";
|
||||||
|
import { __facade_invoke__, __facade_register__, Dispatcher } from "C:\\Users\\jeffe\\Documents\\GitHub\\canvas-website\\node_modules\\wrangler\\templates\\middleware\\common.ts";
|
||||||
|
import type { WorkerEntrypointConstructor } from "C:\\Users\\jeffe\\Documents\\GitHub\\canvas-website\\.wrangler\\tmp\\bundle-VlWfGj\\middleware-insertion-facade.js";
|
||||||
|
|
||||||
|
// Preserve all the exports from the worker
|
||||||
|
export * from "C:\\Users\\jeffe\\Documents\\GitHub\\canvas-website\\.wrangler\\tmp\\bundle-VlWfGj\\middleware-insertion-facade.js";
|
||||||
|
|
||||||
|
class __Facade_ScheduledController__ implements ScheduledController {
|
||||||
|
readonly #noRetry: ScheduledController["noRetry"];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly scheduledTime: number,
|
||||||
|
readonly cron: string,
|
||||||
|
noRetry: ScheduledController["noRetry"]
|
||||||
|
) {
|
||||||
|
this.#noRetry = noRetry;
|
||||||
|
}
|
||||||
|
|
||||||
|
noRetry() {
|
||||||
|
if (!(this instanceof __Facade_ScheduledController__)) {
|
||||||
|
throw new TypeError("Illegal invocation");
|
||||||
|
}
|
||||||
|
// Need to call native method immediately in case uncaught error thrown
|
||||||
|
this.#noRetry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrapExportedHandler(worker: ExportedHandler): ExportedHandler {
|
||||||
|
// If we don't have any middleware defined, just return the handler as is
|
||||||
|
if (
|
||||||
|
__INTERNAL_WRANGLER_MIDDLEWARE__ === undefined ||
|
||||||
|
__INTERNAL_WRANGLER_MIDDLEWARE__.length === 0
|
||||||
|
) {
|
||||||
|
return worker;
|
||||||
|
}
|
||||||
|
// Otherwise, register all middleware once
|
||||||
|
for (const middleware of __INTERNAL_WRANGLER_MIDDLEWARE__) {
|
||||||
|
__facade_register__(middleware);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchDispatcher: ExportedHandlerFetchHandler = function (
|
||||||
|
request,
|
||||||
|
env,
|
||||||
|
ctx
|
||||||
|
) {
|
||||||
|
if (worker.fetch === undefined) {
|
||||||
|
throw new Error("Handler does not export a fetch() function.");
|
||||||
|
}
|
||||||
|
return worker.fetch(request, env, ctx);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...worker,
|
||||||
|
fetch(request, env, ctx) {
|
||||||
|
const dispatcher: Dispatcher = function (type, init) {
|
||||||
|
if (type === "scheduled" && worker.scheduled !== undefined) {
|
||||||
|
const controller = new __Facade_ScheduledController__(
|
||||||
|
Date.now(),
|
||||||
|
init.cron ?? "",
|
||||||
|
() => {}
|
||||||
|
);
|
||||||
|
return worker.scheduled(controller, env, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return __facade_invoke__(request, env, ctx, dispatcher, fetchDispatcher);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrapWorkerEntrypoint(
|
||||||
|
klass: WorkerEntrypointConstructor
|
||||||
|
): WorkerEntrypointConstructor {
|
||||||
|
// If we don't have any middleware defined, just return the handler as is
|
||||||
|
if (
|
||||||
|
__INTERNAL_WRANGLER_MIDDLEWARE__ === undefined ||
|
||||||
|
__INTERNAL_WRANGLER_MIDDLEWARE__.length === 0
|
||||||
|
) {
|
||||||
|
return klass;
|
||||||
|
}
|
||||||
|
// Otherwise, register all middleware once
|
||||||
|
for (const middleware of __INTERNAL_WRANGLER_MIDDLEWARE__) {
|
||||||
|
__facade_register__(middleware);
|
||||||
|
}
|
||||||
|
|
||||||
|
// `extend`ing `klass` here so other RPC methods remain callable
|
||||||
|
return class extends klass {
|
||||||
|
#fetchDispatcher: ExportedHandlerFetchHandler<Record<string, unknown>> = (
|
||||||
|
request,
|
||||||
|
env,
|
||||||
|
ctx
|
||||||
|
) => {
|
||||||
|
this.env = env;
|
||||||
|
this.ctx = ctx;
|
||||||
|
if (super.fetch === undefined) {
|
||||||
|
throw new Error("Entrypoint class does not define a fetch() function.");
|
||||||
|
}
|
||||||
|
return super.fetch(request);
|
||||||
|
};
|
||||||
|
|
||||||
|
#dispatcher: Dispatcher = (type, init) => {
|
||||||
|
if (type === "scheduled" && super.scheduled !== undefined) {
|
||||||
|
const controller = new __Facade_ScheduledController__(
|
||||||
|
Date.now(),
|
||||||
|
init.cron ?? "",
|
||||||
|
() => {}
|
||||||
|
);
|
||||||
|
return super.scheduled(controller);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch(request: Request<unknown, IncomingRequestCfProperties>) {
|
||||||
|
return __facade_invoke__(
|
||||||
|
request,
|
||||||
|
this.env,
|
||||||
|
this.ctx,
|
||||||
|
this.#dispatcher,
|
||||||
|
this.#fetchDispatcher
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let WRAPPED_ENTRY: ExportedHandler | WorkerEntrypointConstructor | undefined;
|
||||||
|
if (typeof ENTRY === "object") {
|
||||||
|
WRAPPED_ENTRY = wrapExportedHandler(ENTRY);
|
||||||
|
} else if (typeof ENTRY === "function") {
|
||||||
|
WRAPPED_ENTRY = wrapWorkerEntrypoint(ENTRY);
|
||||||
|
}
|
||||||
|
export default WRAPPED_ENTRY;
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
|
@ -27,7 +27,7 @@ function App() {
|
||||||
<Route path="/" element={<Home />} />
|
<Route path="/" element={<Home />} />
|
||||||
<Route path="/card/contact" element={<Contact />} />
|
<Route path="/card/contact" element={<Contact />} />
|
||||||
<Route path="/posts/:slug" element={<Post />} />
|
<Route path="/posts/:slug" element={<Post />} />
|
||||||
<Route path="/board" element={<Board />} />
|
<Route path="/board/:slug" element={<Board />} />
|
||||||
<Route path="/inbox" element={<Inbox />} />
|
<Route path="/inbox" element={<Inbox />} />
|
||||||
<Route path="/books" element={<Books />} />
|
<Route path="/books" element={<Books />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,20 @@ import {
|
||||||
Tldraw,
|
Tldraw,
|
||||||
uniqueId,
|
uniqueId,
|
||||||
} from 'tldraw'
|
} from 'tldraw'
|
||||||
|
import { useParams } from 'react-router-dom' // Add this import
|
||||||
|
|
||||||
const WORKER_URL = `https://jeffemmett-canvas.jeffemmett.workers.dev`
|
const WORKER_URL = `https://jeffemmett-canvas.jeffemmett.workers.dev`
|
||||||
|
|
||||||
// In this example, the room ID is hard-coded. You can set this however you like though.
|
|
||||||
const roomId = 'test-room'
|
|
||||||
|
|
||||||
export function Board() {
|
export function Board() {
|
||||||
|
// Extract the slug from the URL
|
||||||
|
const { slug } = useParams<{ slug: string }>()
|
||||||
|
|
||||||
|
// Use the slug as the roomId, or fallback to 'default-room' if not provided
|
||||||
|
const roomId = slug || 'default-room'
|
||||||
|
|
||||||
// Create a store connected to multiplayer.
|
// Create a store connected to multiplayer.
|
||||||
const store = useSync({
|
const store = useSync({
|
||||||
// We need to know the websocket's URI...
|
// Use the dynamic roomId in the URI
|
||||||
uri: `${WORKER_URL}/connect/${roomId}`,
|
uri: `${WORKER_URL}/connect/${roomId}`,
|
||||||
// ...and how to handle static assets like images & videos
|
// ...and how to handle static assets like images & videos
|
||||||
assets: multiplayerAssets,
|
assets: multiplayerAssets,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue