142 lines
3.9 KiB
JavaScript
142 lines
3.9 KiB
JavaScript
import { M as Message, P as Peer, t as toBufferLike } from '../shared/crossws.DfCzGthR.mjs';
|
|
import { a as adapterUtils, A as AdapterHookable } from '../shared/crossws.D9ehKjSh.mjs';
|
|
import 'uncrypto';
|
|
|
|
const cloudflareDurableAdapter = (opts = {}) => {
|
|
const hooks = new AdapterHookable(opts);
|
|
const peers = /* @__PURE__ */ new Set();
|
|
const resolveDurableStub = opts.resolveDurableStub || ((_req, env, _context) => {
|
|
const bindingName = opts.bindingName || "$DurableObject";
|
|
const binding = env[bindingName];
|
|
if (!binding) {
|
|
throw new Error(
|
|
`Durable Object binding "${bindingName}" not available`
|
|
);
|
|
}
|
|
const instanceId = binding.idFromName(opts.instanceName || "crossws");
|
|
return binding.get(instanceId);
|
|
});
|
|
return {
|
|
...adapterUtils(peers),
|
|
handleUpgrade: async (req, env, context) => {
|
|
const stub = await resolveDurableStub(req, env, context);
|
|
return stub.fetch(req);
|
|
},
|
|
handleDurableInit: async (obj, state, env) => {
|
|
},
|
|
handleDurableUpgrade: async (obj, request) => {
|
|
const { upgradeHeaders, endResponse } = await hooks.upgrade(
|
|
request
|
|
);
|
|
if (endResponse) {
|
|
return endResponse;
|
|
}
|
|
const pair = new WebSocketPair();
|
|
const client = pair[0];
|
|
const server = pair[1];
|
|
const peer = CloudflareDurablePeer._restore(
|
|
obj,
|
|
server,
|
|
request
|
|
);
|
|
peers.add(peer);
|
|
obj.ctx.acceptWebSocket(server);
|
|
await hooks.callHook("open", peer);
|
|
return new Response(null, {
|
|
status: 101,
|
|
webSocket: client,
|
|
headers: upgradeHeaders
|
|
});
|
|
},
|
|
handleDurableMessage: async (obj, ws, message) => {
|
|
const peer = CloudflareDurablePeer._restore(obj, ws);
|
|
await hooks.callHook("message", peer, new Message(message, peer));
|
|
},
|
|
handleDurableClose: async (obj, ws, code, reason, wasClean) => {
|
|
const peer = CloudflareDurablePeer._restore(obj, ws);
|
|
peers.delete(peer);
|
|
const details = { code, reason, wasClean };
|
|
await hooks.callHook("close", peer, details);
|
|
}
|
|
};
|
|
};
|
|
class CloudflareDurablePeer extends Peer {
|
|
get peers() {
|
|
return new Set(
|
|
this.#getwebsockets().map(
|
|
(ws) => CloudflareDurablePeer._restore(this._internal.durable, ws)
|
|
)
|
|
);
|
|
}
|
|
#getwebsockets() {
|
|
return this._internal.durable.ctx.getWebSockets();
|
|
}
|
|
send(data) {
|
|
return this._internal.ws.send(toBufferLike(data));
|
|
}
|
|
subscribe(topic) {
|
|
super.subscribe(topic);
|
|
const state = getAttachedState(this._internal.ws);
|
|
if (!state.t) {
|
|
state.t = /* @__PURE__ */ new Set();
|
|
}
|
|
state.t.add(topic);
|
|
setAttachedState(this._internal.ws, state);
|
|
}
|
|
publish(topic, data) {
|
|
const websockets = this.#getwebsockets();
|
|
if (websockets.length < 2) {
|
|
return;
|
|
}
|
|
const dataBuff = toBufferLike(data);
|
|
for (const ws of websockets) {
|
|
if (ws === this._internal.ws) {
|
|
continue;
|
|
}
|
|
const state = getAttachedState(ws);
|
|
if (state.t?.has(topic)) {
|
|
ws.send(dataBuff);
|
|
}
|
|
}
|
|
}
|
|
close(code, reason) {
|
|
this._internal.ws.close(code, reason);
|
|
}
|
|
static _restore(durable, ws, request) {
|
|
let peer = ws._crosswsPeer;
|
|
if (peer) {
|
|
return peer;
|
|
}
|
|
const state = ws.deserializeAttachment() || {};
|
|
peer = ws._crosswsPeer = new CloudflareDurablePeer({
|
|
ws,
|
|
request: request || { url: state.u },
|
|
durable
|
|
});
|
|
if (state.i) {
|
|
peer._id = state.i;
|
|
}
|
|
if (request?.url) {
|
|
state.u = request.url;
|
|
}
|
|
state.i = peer.id;
|
|
setAttachedState(ws, state);
|
|
return peer;
|
|
}
|
|
}
|
|
function getAttachedState(ws) {
|
|
let state = ws._crosswsState;
|
|
if (state) {
|
|
return state;
|
|
}
|
|
state = ws.deserializeAttachment() || {};
|
|
ws._crosswsState = state;
|
|
return state;
|
|
}
|
|
function setAttachedState(ws, state) {
|
|
ws._crosswsState = state;
|
|
ws.serializeAttachment(state);
|
|
}
|
|
|
|
export { cloudflareDurableAdapter as default };
|