mirror of
				https://github.com/excalidraw/excalidraw.git
				synced 2025-11-04 12:54:23 +01:00 
			
		
		
		
	Decouple do from package
This commit is contained in:
		@@ -1,154 +0,0 @@
 | 
			
		||||
import type {
 | 
			
		||||
  DeltasRepository,
 | 
			
		||||
  CLIENT_DELTA,
 | 
			
		||||
  SERVER_DELTA,
 | 
			
		||||
  SERVER_DELTA_STORAGE,
 | 
			
		||||
} from "../sync/protocol";
 | 
			
		||||
import { Network } from "../sync/utils";
 | 
			
		||||
 | 
			
		||||
// CFDO II: add senderId, possibly roomId as well
 | 
			
		||||
export class DurableDeltasRepository implements DeltasRepository {
 | 
			
		||||
  // there is a 2MB row limit, hence working with max payload size of 1.5 MB
 | 
			
		||||
  // and leaving a ~500kB buffer for other row metadata
 | 
			
		||||
  private static readonly MAX_PAYLOAD_SIZE = 1_500_000;
 | 
			
		||||
 | 
			
		||||
  constructor(private storage: DurableObjectStorage) {
 | 
			
		||||
    // #region DEV ONLY
 | 
			
		||||
    // this.storage.sql.exec(`DROP TABLE IF EXISTS deltas;`);
 | 
			
		||||
    // #endregion
 | 
			
		||||
 | 
			
		||||
    this.storage.sql.exec(`CREATE TABLE IF NOT EXISTS deltas(
 | 
			
		||||
			id            TEXT NOT NULL,
 | 
			
		||||
			version		    INTEGER NOT NULL,
 | 
			
		||||
      position      INTEGER NOT NULL,
 | 
			
		||||
			payload		    BLOB NOT NULL,
 | 
			
		||||
			createdAt	    TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
 | 
			
		||||
      PRIMARY KEY (id, version, position)
 | 
			
		||||
		);`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public save(delta: CLIENT_DELTA): SERVER_DELTA | null {
 | 
			
		||||
    return this.storage.transactionSync(() => {
 | 
			
		||||
      const existingDelta = this.getById(delta.id);
 | 
			
		||||
 | 
			
		||||
      // don't perist the same delta twice
 | 
			
		||||
      if (existingDelta) {
 | 
			
		||||
        return existingDelta;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        const payloadBuffer = Network.toBinary(delta);
 | 
			
		||||
        const payloadSize = payloadBuffer.byteLength;
 | 
			
		||||
        const nextVersion = this.getLastVersion() + 1;
 | 
			
		||||
        const chunksCount = Math.ceil(
 | 
			
		||||
          payloadSize / DurableDeltasRepository.MAX_PAYLOAD_SIZE,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        for (let position = 0; position < chunksCount; position++) {
 | 
			
		||||
          const start = position * DurableDeltasRepository.MAX_PAYLOAD_SIZE;
 | 
			
		||||
          const end = start + DurableDeltasRepository.MAX_PAYLOAD_SIZE;
 | 
			
		||||
          const chunkedPayload = payloadBuffer.subarray(start, end);
 | 
			
		||||
 | 
			
		||||
          this.storage.sql.exec(
 | 
			
		||||
            `INSERT INTO deltas (id, version, position, payload) VALUES (?, ?, ?, ?);`,
 | 
			
		||||
            delta.id,
 | 
			
		||||
            nextVersion,
 | 
			
		||||
            position,
 | 
			
		||||
            chunkedPayload,
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        // check if the delta has been already acknowledged
 | 
			
		||||
        // in case client for some reason did not receive acknowledgement
 | 
			
		||||
        // and reconnected while the we still have the delta in the worker
 | 
			
		||||
        // otherwise the client is doomed to full a restore
 | 
			
		||||
        if (e instanceof Error && e.message.includes("SQLITE_CONSTRAINT")) {
 | 
			
		||||
          // continue;
 | 
			
		||||
        } else {
 | 
			
		||||
          throw e;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const acknowledged = this.getById(delta.id);
 | 
			
		||||
      return acknowledged;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // CFDO: for versioning we need deletions, but not for the "snapshot" update;
 | 
			
		||||
  public getAllSinceVersion(version: number): Array<SERVER_DELTA> {
 | 
			
		||||
    const deltas = this.storage.sql
 | 
			
		||||
      .exec<SERVER_DELTA_STORAGE>(
 | 
			
		||||
        `SELECT id, payload, version, position FROM deltas WHERE version > (?) ORDER BY version, position, createdAt ASC;`,
 | 
			
		||||
        version,
 | 
			
		||||
      )
 | 
			
		||||
      .toArray();
 | 
			
		||||
 | 
			
		||||
    return this.restorePayloadChunks(deltas);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getLastVersion(): number {
 | 
			
		||||
    // CFDO: might be in memory to reduce number of rows read (or position on version at least, if btree affect rows read)
 | 
			
		||||
    const result = this.storage.sql
 | 
			
		||||
      .exec(`SELECT MAX(version) FROM deltas;`)
 | 
			
		||||
      .one();
 | 
			
		||||
 | 
			
		||||
    return result ? Number(result["MAX(version)"]) : 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getById(id: string): SERVER_DELTA | null {
 | 
			
		||||
    const deltas = this.storage.sql
 | 
			
		||||
      .exec<SERVER_DELTA_STORAGE>(
 | 
			
		||||
        `SELECT id, payload, version, position FROM deltas WHERE id = (?) ORDER BY position ASC`,
 | 
			
		||||
        id,
 | 
			
		||||
      )
 | 
			
		||||
      .toArray();
 | 
			
		||||
 | 
			
		||||
    if (!deltas.length) {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const restoredDeltas = this.restorePayloadChunks(deltas);
 | 
			
		||||
 | 
			
		||||
    if (restoredDeltas.length !== 1) {
 | 
			
		||||
      throw new Error(
 | 
			
		||||
        `Expected exactly one restored delta, but received "${restoredDeltas.length}".`,
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return restoredDeltas[0];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private restorePayloadChunks(
 | 
			
		||||
    deltas: Array<SERVER_DELTA_STORAGE>,
 | 
			
		||||
  ): Array<SERVER_DELTA> {
 | 
			
		||||
    return Array.from(
 | 
			
		||||
      deltas
 | 
			
		||||
        .reduce((acc, curr) => {
 | 
			
		||||
          const delta = acc.get(curr.version);
 | 
			
		||||
 | 
			
		||||
          if (delta) {
 | 
			
		||||
            const currentPayload = new Uint8Array(curr.payload);
 | 
			
		||||
            acc.set(curr.version, {
 | 
			
		||||
              ...delta,
 | 
			
		||||
              // glueing the chunks payload back
 | 
			
		||||
              payload: Uint8Array.from([...delta.payload, ...currentPayload]),
 | 
			
		||||
            });
 | 
			
		||||
          } else {
 | 
			
		||||
            // let's not unnecessarily expose more props than these (i.e. position)
 | 
			
		||||
            acc.set(curr.version, {
 | 
			
		||||
              id: curr.id,
 | 
			
		||||
              version: curr.version,
 | 
			
		||||
              payload: new Uint8Array(curr.payload),
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          return acc;
 | 
			
		||||
          // using Uint8Array instead of ArrayBuffer, as it has nicer methods
 | 
			
		||||
        }, new Map<number, Omit<SERVER_DELTA_STORAGE, "payload" | "position"> & { payload: Uint8Array }>())
 | 
			
		||||
        .values(),
 | 
			
		||||
    ).map((delta) => ({
 | 
			
		||||
      ...delta,
 | 
			
		||||
      payload: Network.fromBinary(delta.payload),
 | 
			
		||||
    }));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,60 +0,0 @@
 | 
			
		||||
import { DurableObject } from "cloudflare:workers";
 | 
			
		||||
import { DurableDeltasRepository } from "./repository";
 | 
			
		||||
import { ExcalidrawSyncServer } from "../sync/server";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Durable Object impl. of Excalidraw room.
 | 
			
		||||
 */
 | 
			
		||||
export class DurableRoom extends DurableObject {
 | 
			
		||||
  private roomId: string | null = null;
 | 
			
		||||
  private sync: ExcalidrawSyncServer;
 | 
			
		||||
 | 
			
		||||
  constructor(ctx: DurableObjectState, env: Env) {
 | 
			
		||||
    super(ctx, env);
 | 
			
		||||
 | 
			
		||||
    this.ctx.blockConcurrencyWhile(async () => {
 | 
			
		||||
      this.roomId = (await this.ctx.storage.get("roomId")) || null;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const repository = new DurableDeltasRepository(ctx.storage);
 | 
			
		||||
    this.sync = new ExcalidrawSyncServer(repository);
 | 
			
		||||
 | 
			
		||||
    // in case it hibernates, let's get take active connections
 | 
			
		||||
    for (const ws of this.ctx.getWebSockets()) {
 | 
			
		||||
      this.sync.onConnect(ws);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public fetch = async (request: Request): Promise<Response> =>
 | 
			
		||||
    this.connect(request);
 | 
			
		||||
 | 
			
		||||
  public webSocketMessage = (client: WebSocket, message: ArrayBuffer) =>
 | 
			
		||||
    this.sync.onMessage(client, message);
 | 
			
		||||
 | 
			
		||||
  public webSocketClose = (ws: WebSocket) => this.sync.onDisconnect(ws);
 | 
			
		||||
 | 
			
		||||
  private connect(request: Request) {
 | 
			
		||||
    if (!this.roomId) {
 | 
			
		||||
      const roomId = new URL(request.url).searchParams.get("roomId");
 | 
			
		||||
 | 
			
		||||
      if (!roomId) {
 | 
			
		||||
        return new Response(null, { status: 400 /* bad request */ });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this.ctx.blockConcurrencyWhile(async () => {
 | 
			
		||||
        await this.ctx.storage.put("roomId", roomId);
 | 
			
		||||
        this.roomId = roomId;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const { 0: client, 1: server } = new WebSocketPair();
 | 
			
		||||
 | 
			
		||||
    this.ctx.acceptWebSocket(client);
 | 
			
		||||
    this.sync.onConnect(client);
 | 
			
		||||
 | 
			
		||||
    return new Response(null, {
 | 
			
		||||
      status: 101 /* switching protocols */,
 | 
			
		||||
      webSocket: server,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
export { DurableRoom } from "./room";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Worker relay for Durable Room.
 | 
			
		||||
 */
 | 
			
		||||
export default {
 | 
			
		||||
  async fetch(
 | 
			
		||||
    request: Request,
 | 
			
		||||
    env: Env,
 | 
			
		||||
    ctx: ExecutionContext,
 | 
			
		||||
  ): Promise<Response> {
 | 
			
		||||
    // CFDO: only auth user should reach this
 | 
			
		||||
    const upgrade = request.headers.get("upgrade");
 | 
			
		||||
    if (!upgrade || upgrade !== "websocket") {
 | 
			
		||||
      return new Response(null, { status: 426 /* upgrade required */ });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (request.method !== "GET") {
 | 
			
		||||
      return new Response(null, { status: 405 /* method not allowed */ });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const url = new URL(request.url);
 | 
			
		||||
    if (url.pathname !== "/connect") {
 | 
			
		||||
      return new Response(null, { status: 403 /* forbidden */ });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // CFDO: double check that the scene exists
 | 
			
		||||
    const roomId = url.searchParams.get("roomId");
 | 
			
		||||
    if (!roomId) {
 | 
			
		||||
      return new Response(null, { status: 400 /* bad request */ });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const id: DurableObjectId = env.DURABLE_ROOM.idFromName(roomId);
 | 
			
		||||
    const room = env.DURABLE_ROOM.get(id);
 | 
			
		||||
 | 
			
		||||
    return room.fetch(request);
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
@@ -62,8 +62,6 @@
 | 
			
		||||
    "@excalidraw/random-username": "1.1.0",
 | 
			
		||||
    "@radix-ui/react-popover": "1.0.3",
 | 
			
		||||
    "@radix-ui/react-tabs": "1.0.2",
 | 
			
		||||
    "@types/async-lock": "^1.4.2",
 | 
			
		||||
    "async-lock": "^1.4.1",
 | 
			
		||||
    "browser-fs-access": "0.29.1",
 | 
			
		||||
    "canvas-roundrect-polyfill": "0.0.1",
 | 
			
		||||
    "clsx": "1.1.1",
 | 
			
		||||
@@ -74,9 +72,7 @@
 | 
			
		||||
    "image-blob-reduce": "3.0.1",
 | 
			
		||||
    "jotai": "2.11.0",
 | 
			
		||||
    "jotai-scope": "0.7.2",
 | 
			
		||||
    "lodash.debounce": "4.0.8",
 | 
			
		||||
    "lodash.throttle": "4.1.1",
 | 
			
		||||
    "msgpack-lite": "0.1.26",
 | 
			
		||||
    "nanoid": "3.3.3",
 | 
			
		||||
    "open-color": "1.9.1",
 | 
			
		||||
    "pako": "1.0.11",
 | 
			
		||||
@@ -87,7 +83,6 @@
 | 
			
		||||
    "png-chunks-extract": "1.0.0",
 | 
			
		||||
    "points-on-curve": "1.0.1",
 | 
			
		||||
    "pwacompat": "2.0.17",
 | 
			
		||||
    "reconnecting-websocket": "4.4.0",
 | 
			
		||||
    "roughjs": "4.6.4",
 | 
			
		||||
    "sass": "1.51.0",
 | 
			
		||||
    "tunnel-rat": "0.1.2"
 | 
			
		||||
@@ -101,13 +96,10 @@
 | 
			
		||||
    "@babel/preset-env": "7.24.5",
 | 
			
		||||
    "@babel/preset-react": "7.24.1",
 | 
			
		||||
    "@babel/preset-typescript": "7.24.1",
 | 
			
		||||
    "@cloudflare/workers-types": "4.20241112.0",
 | 
			
		||||
    "@size-limit/preset-big-lib": "9.0.0",
 | 
			
		||||
    "@testing-library/dom": "10.4.0",
 | 
			
		||||
    "@testing-library/jest-dom": "5.16.2",
 | 
			
		||||
    "@testing-library/react": "16.0.0",
 | 
			
		||||
    "@types/async-lock": "^1.4.2",
 | 
			
		||||
    "@types/msgpack-lite": "0.1.11",
 | 
			
		||||
    "@types/pako": "1.0.3",
 | 
			
		||||
    "@types/pica": "5.1.3",
 | 
			
		||||
    "@types/resize-observer-browser": "0.1.7",
 | 
			
		||||
@@ -132,8 +124,7 @@
 | 
			
		||||
    "size-limit": "9.0.0",
 | 
			
		||||
    "style-loader": "3.3.3",
 | 
			
		||||
    "ts-loader": "9.3.1",
 | 
			
		||||
    "typescript": "4.9.4",
 | 
			
		||||
    "wrangler": "^3.60.3"
 | 
			
		||||
    "typescript": "4.9.4"
 | 
			
		||||
  },
 | 
			
		||||
  "bugs": "https://github.com/excalidraw/excalidraw/issues",
 | 
			
		||||
  "homepage": "https://github.com/excalidraw/excalidraw/tree/master/packages/excalidraw",
 | 
			
		||||
@@ -143,9 +134,6 @@
 | 
			
		||||
    "pack": "yarn build:umd && yarn pack",
 | 
			
		||||
    "start": "node ../../scripts/buildExample.mjs && vite",
 | 
			
		||||
    "build:example": "node ../../scripts/buildExample.mjs",
 | 
			
		||||
    "size": "yarn build:umd && size-limit",
 | 
			
		||||
    "sync:deploy": "wrangler deploy",
 | 
			
		||||
    "sync:dev": "wrangler dev",
 | 
			
		||||
    "sync:typegen": "wrangler types --experimental-include-runtime=\"./worker-runtime.d.ts\""
 | 
			
		||||
    "size": "yarn build:umd && size-limit"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,6 @@ import type {
 | 
			
		||||
  OrderedExcalidrawElement,
 | 
			
		||||
  SceneElementsMap,
 | 
			
		||||
} from "./element/types";
 | 
			
		||||
import type { SERVER_DELTA } from "./sync/protocol";
 | 
			
		||||
import { arrayToMap, assertNever } from "./utils";
 | 
			
		||||
import { hashElementsVersion } from "./element";
 | 
			
		||||
import { syncMovedIndices } from "./fractionalIndex";
 | 
			
		||||
@@ -453,7 +452,7 @@ export class StoreDelta {
 | 
			
		||||
  public static load({
 | 
			
		||||
    id,
 | 
			
		||||
    elements: { added, removed, updated },
 | 
			
		||||
  }: SERVER_DELTA["payload"]) {
 | 
			
		||||
  }: DTO<StoreDelta>) {
 | 
			
		||||
    const elements = ElementsDelta.create(added, removed, updated, {
 | 
			
		||||
      shouldRedistribute: false,
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -1,548 +0,0 @@
 | 
			
		||||
import throttle from "lodash.throttle";
 | 
			
		||||
import ReconnectingWebSocket, {
 | 
			
		||||
  type Event,
 | 
			
		||||
  type CloseEvent,
 | 
			
		||||
} from "reconnecting-websocket";
 | 
			
		||||
import { Network, Utils } from "./utils";
 | 
			
		||||
import {
 | 
			
		||||
  LocalDeltasQueue,
 | 
			
		||||
  type MetadataRepository,
 | 
			
		||||
  type DeltasRepository,
 | 
			
		||||
} from "./queue";
 | 
			
		||||
import { StoreAction, StoreDelta } from "../store";
 | 
			
		||||
import type { StoreChange } from "../store";
 | 
			
		||||
import type { ExcalidrawImperativeAPI } from "../types";
 | 
			
		||||
import type { ExcalidrawElement, SceneElementsMap } from "../element/types";
 | 
			
		||||
import type {
 | 
			
		||||
  SERVER_DELTA,
 | 
			
		||||
  CLIENT_CHANGE,
 | 
			
		||||
  SERVER_MESSAGE,
 | 
			
		||||
  CLIENT_MESSAGE_BINARY,
 | 
			
		||||
} from "./protocol";
 | 
			
		||||
import { debounce } from "../utils";
 | 
			
		||||
import { randomId } from "../random";
 | 
			
		||||
import { orderByFractionalIndex } from "../fractionalIndex";
 | 
			
		||||
import { ENV } from "../constants";
 | 
			
		||||
 | 
			
		||||
class SocketMessage implements CLIENT_MESSAGE_BINARY {
 | 
			
		||||
  constructor(
 | 
			
		||||
    public readonly type: "relay" | "pull" | "push",
 | 
			
		||||
    public readonly payload: Uint8Array,
 | 
			
		||||
    public readonly chunkInfo?: {
 | 
			
		||||
      id: string;
 | 
			
		||||
      position: number;
 | 
			
		||||
      count: number;
 | 
			
		||||
    },
 | 
			
		||||
  ) {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class SocketClient {
 | 
			
		||||
  // Max size for outgoing messages is 1MiB (due to CFDO limits),
 | 
			
		||||
  // thus working with a slighter smaller limit of 800 kB (leaving 224kB for metadata)
 | 
			
		||||
  private static readonly MAX_MESSAGE_SIZE = 800_000;
 | 
			
		||||
 | 
			
		||||
  private isOffline = true;
 | 
			
		||||
  private socket: ReconnectingWebSocket | null = null;
 | 
			
		||||
 | 
			
		||||
  public get isDisconnected() {
 | 
			
		||||
    return !this.socket;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private readonly host: string,
 | 
			
		||||
    private readonly roomId: String,
 | 
			
		||||
    private readonly handlers: {
 | 
			
		||||
      onOpen: (event: Event) => void;
 | 
			
		||||
      onOnline: () => void;
 | 
			
		||||
      onMessage: (message: SERVER_MESSAGE) => void;
 | 
			
		||||
    },
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  private onOnline = () => {
 | 
			
		||||
    this.isOffline = false;
 | 
			
		||||
    this.handlers.onOnline();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private onOffline = () => {
 | 
			
		||||
    this.isOffline = true;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  public connect = throttle(
 | 
			
		||||
    () => {
 | 
			
		||||
      if (!this.isDisconnected && !this.isOffline) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      window.addEventListener("online", this.onOnline);
 | 
			
		||||
      window.addEventListener("offline", this.onOffline);
 | 
			
		||||
 | 
			
		||||
      // eslint-disable-next-line no-console
 | 
			
		||||
      console.debug(`Connecting to the room "${this.roomId}"...`);
 | 
			
		||||
      this.socket = new ReconnectingWebSocket(
 | 
			
		||||
        `${this.host}/connect?roomId=${this.roomId}`,
 | 
			
		||||
        [],
 | 
			
		||||
        {
 | 
			
		||||
          WebSocket: undefined, // WebSocket constructor, if none provided, defaults to global WebSocket
 | 
			
		||||
          maxReconnectionDelay: 10000, // max delay in ms between reconnections
 | 
			
		||||
          minReconnectionDelay: 1000, // min delay in ms between reconnections
 | 
			
		||||
          reconnectionDelayGrowFactor: 1.3, // how fast the reconnection delay grows
 | 
			
		||||
          minUptime: 5000, // min time in ms to consider connection as stable
 | 
			
		||||
          connectionTimeout: 4000, // retry connect if not connected after this time, in ms
 | 
			
		||||
          maxRetries: Infinity, // maximum number of retries
 | 
			
		||||
          maxEnqueuedMessages: 0, // maximum number of messages to buffer until reconnection
 | 
			
		||||
          startClosed: false, // start websocket in CLOSED state, call `.reconnect()` to connect
 | 
			
		||||
          debug: false, // enables debug output,
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
      this.socket.addEventListener("message", this.onMessage);
 | 
			
		||||
      this.socket.addEventListener("open", this.onOpen);
 | 
			
		||||
      this.socket.addEventListener("close", this.onClose);
 | 
			
		||||
      this.socket.addEventListener("error", this.onError);
 | 
			
		||||
    },
 | 
			
		||||
    1000,
 | 
			
		||||
    { leading: true, trailing: false },
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  public disconnect() {
 | 
			
		||||
    if (this.isDisconnected) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      window.removeEventListener("online", this.onOnline);
 | 
			
		||||
      window.removeEventListener("offline", this.onOffline);
 | 
			
		||||
 | 
			
		||||
      this.socket?.removeEventListener("message", this.onMessage);
 | 
			
		||||
      this.socket?.removeEventListener("open", this.onOpen);
 | 
			
		||||
      this.socket?.removeEventListener("close", this.onClose);
 | 
			
		||||
      this.socket?.removeEventListener("error", this.onError);
 | 
			
		||||
      this.socket?.close();
 | 
			
		||||
 | 
			
		||||
      // eslint-disable-next-line no-console
 | 
			
		||||
      console.debug(`Disconnected from the room "${this.roomId}".`);
 | 
			
		||||
    } finally {
 | 
			
		||||
      this.socket = null;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public send(message: {
 | 
			
		||||
    type: "relay" | "pull" | "push";
 | 
			
		||||
    payload: Record<string, unknown>;
 | 
			
		||||
  }): void {
 | 
			
		||||
    if (this.isOffline) {
 | 
			
		||||
      // connection opened, don't let the WS buffer the messages,
 | 
			
		||||
      // as we do explicitly buffer unacknowledged deltas
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this.isDisconnected) {
 | 
			
		||||
      this.connect();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const { type, payload } = message;
 | 
			
		||||
 | 
			
		||||
    const payloadBuffer = Network.toBinary(payload);
 | 
			
		||||
    const payloadSize = payloadBuffer.byteLength;
 | 
			
		||||
 | 
			
		||||
    if (import.meta.env.DEV || import.meta.env.MODE === ENV.TEST) {
 | 
			
		||||
      // eslint-disable-next-line no-console
 | 
			
		||||
      console.debug("send", message, payloadSize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (payloadSize < SocketClient.MAX_MESSAGE_SIZE) {
 | 
			
		||||
      const message = new SocketMessage(type, payloadBuffer);
 | 
			
		||||
      return this.sendMessage(message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const chunkId = randomId();
 | 
			
		||||
    const chunkSize = SocketClient.MAX_MESSAGE_SIZE;
 | 
			
		||||
    const chunksCount = Math.ceil(payloadSize / chunkSize);
 | 
			
		||||
 | 
			
		||||
    for (let position = 0; position < chunksCount; position++) {
 | 
			
		||||
      const start = position * chunkSize;
 | 
			
		||||
      const end = start + chunkSize;
 | 
			
		||||
      const chunkedPayload = payloadBuffer.subarray(start, end);
 | 
			
		||||
      const message = new SocketMessage(type, chunkedPayload, {
 | 
			
		||||
        id: chunkId,
 | 
			
		||||
        position,
 | 
			
		||||
        count: chunksCount,
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      this.sendMessage(message);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private onMessage = (event: MessageEvent) => {
 | 
			
		||||
    this.receiveMessage(event.data).then((message) => {
 | 
			
		||||
      if (!message) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (import.meta.env.DEV || import.meta.env.MODE === ENV.TEST) {
 | 
			
		||||
        // eslint-disable-next-line no-console
 | 
			
		||||
        console.debug("onMessage", message);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this.handlers.onMessage(message);
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private onOpen = (event: Event) => {
 | 
			
		||||
    // eslint-disable-next-line no-console
 | 
			
		||||
    console.debug(`Connection to the room "${this.roomId}" opened.`);
 | 
			
		||||
    this.isOffline = false;
 | 
			
		||||
    this.handlers.onOpen(event);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private onClose = (event: CloseEvent) => {
 | 
			
		||||
    // eslint-disable-next-line no-console
 | 
			
		||||
    console.debug(`Connection to the room "${this.roomId}" closed.`, event);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private onError = (event: Event) => {
 | 
			
		||||
    // eslint-disable-next-line no-console
 | 
			
		||||
    console.error(
 | 
			
		||||
      `Connection to the room "${this.roomId}" returned an error.`,
 | 
			
		||||
      event,
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private sendMessage = (message: CLIENT_MESSAGE_BINARY) => {
 | 
			
		||||
    this.socket?.send(Network.encodeClientMessage(message));
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // CFDO: should be (runtime) type-safe
 | 
			
		||||
  private async receiveMessage(
 | 
			
		||||
    message: Blob,
 | 
			
		||||
  ): Promise<SERVER_MESSAGE | undefined> {
 | 
			
		||||
    const arrayBuffer = await message.arrayBuffer();
 | 
			
		||||
    const uint8Array = new Uint8Array(arrayBuffer);
 | 
			
		||||
 | 
			
		||||
    const [decodedMessage, decodingError] = Utils.try<SERVER_MESSAGE>(() =>
 | 
			
		||||
      Network.fromBinary(uint8Array),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (decodingError) {
 | 
			
		||||
      console.error("Failed to decode message:", message);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return decodedMessage;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface AcknowledgedDelta {
 | 
			
		||||
  delta: StoreDelta;
 | 
			
		||||
  version: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SyncClient {
 | 
			
		||||
  private static readonly HOST_URL = import.meta.env.DEV
 | 
			
		||||
    ? "ws://localhost:8787"
 | 
			
		||||
    : "https://excalidraw-sync.marcel-529.workers.dev";
 | 
			
		||||
 | 
			
		||||
  private static readonly ROOM_ID = import.meta.env.DEV
 | 
			
		||||
    ? "test_room_x"
 | 
			
		||||
    : "test_room_prod";
 | 
			
		||||
 | 
			
		||||
  private readonly api: ExcalidrawImperativeAPI;
 | 
			
		||||
  private readonly localDeltas: LocalDeltasQueue;
 | 
			
		||||
  private readonly metadata: MetadataRepository;
 | 
			
		||||
  private readonly client: SocketClient;
 | 
			
		||||
 | 
			
		||||
  private relayedElementsVersionsCache = new Map<
 | 
			
		||||
    string,
 | 
			
		||||
    ExcalidrawElement["version"]
 | 
			
		||||
  >();
 | 
			
		||||
 | 
			
		||||
  // #region ACKNOWLEDGED DELTAS & METADATA
 | 
			
		||||
  // CFDO II: shouldn't be stateful, only request / response
 | 
			
		||||
  private readonly acknowledgedDeltasMap: Map<string, AcknowledgedDelta> =
 | 
			
		||||
    new Map();
 | 
			
		||||
 | 
			
		||||
  public get acknowledgedDeltas() {
 | 
			
		||||
    return Array.from(this.acknowledgedDeltasMap.values())
 | 
			
		||||
      .sort((a, b) => (a.version < b.version ? -1 : 1))
 | 
			
		||||
      .map((x) => x.delta);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _lastAcknowledgedVersion = 0;
 | 
			
		||||
 | 
			
		||||
  private get lastAcknowledgedVersion() {
 | 
			
		||||
    return this._lastAcknowledgedVersion;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private set lastAcknowledgedVersion(version: number) {
 | 
			
		||||
    this._lastAcknowledgedVersion = version;
 | 
			
		||||
    this.metadata.saveMetadata({ lastAcknowledgedVersion: version });
 | 
			
		||||
  }
 | 
			
		||||
  // #endregion
 | 
			
		||||
 | 
			
		||||
  private constructor(
 | 
			
		||||
    api: ExcalidrawImperativeAPI,
 | 
			
		||||
    repository: MetadataRepository,
 | 
			
		||||
    queue: LocalDeltasQueue,
 | 
			
		||||
    options: { host: string; roomId: string; lastAcknowledgedVersion: number },
 | 
			
		||||
  ) {
 | 
			
		||||
    this.api = api;
 | 
			
		||||
    this.metadata = repository;
 | 
			
		||||
    this.localDeltas = queue;
 | 
			
		||||
    this.lastAcknowledgedVersion = options.lastAcknowledgedVersion;
 | 
			
		||||
    this.client = new SocketClient(options.host, options.roomId, {
 | 
			
		||||
      onOpen: this.onOpen,
 | 
			
		||||
      onOnline: this.onOnline,
 | 
			
		||||
      onMessage: this.onMessage,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // #region SYNC_CLIENT FACTORY
 | 
			
		||||
  public static async create(
 | 
			
		||||
    api: ExcalidrawImperativeAPI,
 | 
			
		||||
    repository: DeltasRepository & MetadataRepository,
 | 
			
		||||
  ) {
 | 
			
		||||
    const queue = await LocalDeltasQueue.create(repository);
 | 
			
		||||
    // CFDO: temporary for custom roomId (though E+ will be similar)
 | 
			
		||||
    const roomId = window.location.pathname.split("/").at(-1);
 | 
			
		||||
 | 
			
		||||
    return new SyncClient(api, repository, queue, {
 | 
			
		||||
      host: SyncClient.HOST_URL,
 | 
			
		||||
      roomId: roomId ?? SyncClient.ROOM_ID,
 | 
			
		||||
      // CFDO II: temporary, so that all deltas are loaded and applied on init
 | 
			
		||||
      lastAcknowledgedVersion: 0,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  // #endregion
 | 
			
		||||
 | 
			
		||||
  // #region PUBLIC API METHODS
 | 
			
		||||
  public connect() {
 | 
			
		||||
    this.client.connect();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public disconnect() {
 | 
			
		||||
    this.client.disconnect();
 | 
			
		||||
    this.relayedElementsVersionsCache.clear();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public pull(sinceVersion?: number): void {
 | 
			
		||||
    this.client.send({
 | 
			
		||||
      type: "pull",
 | 
			
		||||
      payload: {
 | 
			
		||||
        lastAcknowledgedVersion: sinceVersion ?? this.lastAcknowledgedVersion,
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public push(delta?: StoreDelta): void {
 | 
			
		||||
    if (delta) {
 | 
			
		||||
      this.localDeltas.add(delta);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // re-send all already queued deltas
 | 
			
		||||
    for (const delta of this.localDeltas.getAll()) {
 | 
			
		||||
      this.client.send({
 | 
			
		||||
        type: "push",
 | 
			
		||||
        payload: {
 | 
			
		||||
          ...delta,
 | 
			
		||||
        },
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // CFDO: should be throttled! 16ms (60 fps) for live scenes, not needed at all for single player
 | 
			
		||||
  public relay(change: StoreChange): void {
 | 
			
		||||
    if (this.client.isDisconnected) {
 | 
			
		||||
      // don't reconnect if we're explicitly disconnected
 | 
			
		||||
      // otherwise versioning slider would trigger sync on every slider step
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let shouldRelay = false;
 | 
			
		||||
 | 
			
		||||
    for (const [id, element] of Object.entries(change.elements)) {
 | 
			
		||||
      const cachedElementVersion = this.relayedElementsVersionsCache.get(id);
 | 
			
		||||
 | 
			
		||||
      if (!cachedElementVersion || cachedElementVersion < element.version) {
 | 
			
		||||
        this.relayedElementsVersionsCache.set(id, element.version);
 | 
			
		||||
 | 
			
		||||
        if (!shouldRelay) {
 | 
			
		||||
          // it's enough that a single element is not cached or is outdated in cache
 | 
			
		||||
          // to relay the whole change, otherwise we skip the relay as we've already received this change
 | 
			
		||||
          shouldRelay = true;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!shouldRelay) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.client.send({
 | 
			
		||||
      type: "relay",
 | 
			
		||||
      payload: { ...change },
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  // #endregion
 | 
			
		||||
 | 
			
		||||
  // #region PRIVATE SOCKET MESSAGE HANDLERS
 | 
			
		||||
  private onOpen = (event: Event) => {
 | 
			
		||||
    // CFDO II: hack to pull everything for on init
 | 
			
		||||
    this.pull(0);
 | 
			
		||||
    this.push();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private onOnline = () => {
 | 
			
		||||
    // perform incremental sync
 | 
			
		||||
    this.pull();
 | 
			
		||||
    this.push();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private onMessage = (serverMessage: SERVER_MESSAGE) => {
 | 
			
		||||
    const { type, payload } = serverMessage;
 | 
			
		||||
 | 
			
		||||
    switch (type) {
 | 
			
		||||
      case "relayed":
 | 
			
		||||
        return this.handleRelayed(payload);
 | 
			
		||||
      case "acknowledged":
 | 
			
		||||
        return this.handleAcknowledged(payload);
 | 
			
		||||
      // case "rejected":
 | 
			
		||||
      //   return this.handleRejected(payload);
 | 
			
		||||
      default:
 | 
			
		||||
        console.error("Unknown message type:", type);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private handleRelayed = (payload: CLIENT_CHANGE) => {
 | 
			
		||||
    // CFDO I: retrieve the map already
 | 
			
		||||
    const nextElements = new Map(
 | 
			
		||||
      this.api.getSceneElementsIncludingDeleted().map((el) => [el.id, el]),
 | 
			
		||||
    ) as SceneElementsMap;
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      const { elements: relayedElements } = payload;
 | 
			
		||||
 | 
			
		||||
      for (const [id, relayedElement] of Object.entries(relayedElements)) {
 | 
			
		||||
        const existingElement = nextElements.get(id);
 | 
			
		||||
 | 
			
		||||
        if (
 | 
			
		||||
          !existingElement || // new element
 | 
			
		||||
          existingElement.version < relayedElement.version // updated element
 | 
			
		||||
        ) {
 | 
			
		||||
          nextElements.set(id, relayedElement);
 | 
			
		||||
          this.relayedElementsVersionsCache.set(id, relayedElement.version);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const orderedElements = orderByFractionalIndex(
 | 
			
		||||
        Array.from(nextElements.values()),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      this.api.updateScene({
 | 
			
		||||
        elements: orderedElements,
 | 
			
		||||
        storeAction: StoreAction.UPDATE,
 | 
			
		||||
      });
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      console.error("Failed to apply relayed change:", e);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // CFDO: refactor by applying all operations to store, not to the elements
 | 
			
		||||
  private handleAcknowledged = (payload: { deltas: Array<SERVER_DELTA> }) => {
 | 
			
		||||
    let prevSnapshot = this.api.store.snapshot;
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      const remoteDeltas = Array.from(payload.deltas);
 | 
			
		||||
      const applicableDeltas: Array<StoreDelta> = [];
 | 
			
		||||
      const appState = this.api.getAppState();
 | 
			
		||||
 | 
			
		||||
      let nextAcknowledgedVersion = this.lastAcknowledgedVersion;
 | 
			
		||||
      let nextElements = new Map(
 | 
			
		||||
        // CFDO: retrieve the map already
 | 
			
		||||
        this.api.getSceneElementsIncludingDeleted().map((el) => [el.id, el]),
 | 
			
		||||
      ) as SceneElementsMap;
 | 
			
		||||
 | 
			
		||||
      for (const { id, version, payload } of remoteDeltas) {
 | 
			
		||||
        // CFDO II: temporary to load all deltas on init
 | 
			
		||||
        this.acknowledgedDeltasMap.set(id, {
 | 
			
		||||
          delta: StoreDelta.load(payload),
 | 
			
		||||
          version,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // we've already applied this delta!
 | 
			
		||||
        if (version <= nextAcknowledgedVersion) {
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // CFDO: strictly checking for out of order deltas; might be relaxed if it becomes a problem
 | 
			
		||||
        if (version !== nextAcknowledgedVersion + 1) {
 | 
			
		||||
          throw new Error(
 | 
			
		||||
            `Received out of order delta, expected "${
 | 
			
		||||
              nextAcknowledgedVersion + 1
 | 
			
		||||
            }", but received "${version}"`,
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.localDeltas.has(id)) {
 | 
			
		||||
          // local delta does not have to be applied again
 | 
			
		||||
          this.localDeltas.remove(id);
 | 
			
		||||
        } else {
 | 
			
		||||
          // this is a new remote delta, adding it to the list of applicable deltas
 | 
			
		||||
          const remoteDelta = StoreDelta.load(payload);
 | 
			
		||||
          applicableDeltas.push(remoteDelta);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        nextAcknowledgedVersion = version;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // adding all yet unacknowledged local deltas
 | 
			
		||||
      const localDeltas = this.localDeltas.getAll();
 | 
			
		||||
      applicableDeltas.push(...localDeltas);
 | 
			
		||||
 | 
			
		||||
      for (const delta of applicableDeltas) {
 | 
			
		||||
        [nextElements] = this.api.store.applyDeltaTo(
 | 
			
		||||
          delta,
 | 
			
		||||
          nextElements,
 | 
			
		||||
          appState,
 | 
			
		||||
          {
 | 
			
		||||
            triggerIncrement: false,
 | 
			
		||||
            updateSnapshot: true,
 | 
			
		||||
          },
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        prevSnapshot = this.api.store.snapshot;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const orderedElements = orderByFractionalIndex(
 | 
			
		||||
        Array.from(nextElements.values()),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      // CFDO: might need to restore first due to potentially stale delta versions
 | 
			
		||||
      this.api.updateScene({
 | 
			
		||||
        elements: orderedElements,
 | 
			
		||||
        // even though the snapshot should be up-to-date already,
 | 
			
		||||
        // still some more updates might be triggered,
 | 
			
		||||
        // i.e. as a result from syncing invalid indices
 | 
			
		||||
        storeAction: StoreAction.UPDATE,
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      this.lastAcknowledgedVersion = nextAcknowledgedVersion;
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      console.error("Failed to apply acknowledged deltas:", e);
 | 
			
		||||
      // rollback to the previous snapshot, so that we don't end up in an incosistent state
 | 
			
		||||
      this.api.store.snapshot = prevSnapshot;
 | 
			
		||||
      // schedule another fresh pull in case of a failure
 | 
			
		||||
      this.schedulePull();
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private handleRejected = (payload: {
 | 
			
		||||
    ids: Array<string>;
 | 
			
		||||
    message: Uint8Array;
 | 
			
		||||
  }) => {
 | 
			
		||||
    // handle rejected deltas
 | 
			
		||||
    console.error("Rejected message received:", payload);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private schedulePull = debounce(() => this.pull(), 1000);
 | 
			
		||||
  // #endregion
 | 
			
		||||
}
 | 
			
		||||
@@ -1,68 +0,0 @@
 | 
			
		||||
import type { StoreChange, StoreDelta } from "../store";
 | 
			
		||||
import type { DTO } from "../utility-types";
 | 
			
		||||
 | 
			
		||||
export type CLIENT_DELTA = DTO<StoreDelta>;
 | 
			
		||||
export type CLIENT_CHANGE = DTO<StoreChange>;
 | 
			
		||||
 | 
			
		||||
export type RESTORE_PAYLOAD = {};
 | 
			
		||||
export type RELAY_PAYLOAD = CLIENT_CHANGE;
 | 
			
		||||
export type PUSH_PAYLOAD = CLIENT_DELTA;
 | 
			
		||||
export type PULL_PAYLOAD = { lastAcknowledgedVersion: number };
 | 
			
		||||
 | 
			
		||||
export type CHUNK_INFO = {
 | 
			
		||||
  id: string;
 | 
			
		||||
  position: number;
 | 
			
		||||
  count: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type CLIENT_MESSAGE = (
 | 
			
		||||
  | { type: "restore"; payload: RESTORE_PAYLOAD }
 | 
			
		||||
  | { type: "relay"; payload: RELAY_PAYLOAD }
 | 
			
		||||
  | { type: "pull"; payload: PULL_PAYLOAD }
 | 
			
		||||
  | { type: "push"; payload: PUSH_PAYLOAD }
 | 
			
		||||
) & { chunkInfo?: CHUNK_INFO };
 | 
			
		||||
 | 
			
		||||
export type CLIENT_MESSAGE_BINARY = {
 | 
			
		||||
  type: CLIENT_MESSAGE["type"];
 | 
			
		||||
  payload: Uint8Array;
 | 
			
		||||
  chunkInfo?: CHUNK_INFO;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type SERVER_DELTA = {
 | 
			
		||||
  id: CLIENT_DELTA["id"];
 | 
			
		||||
  version: number;
 | 
			
		||||
  payload: CLIENT_DELTA;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type SERVER_DELTA_STORAGE = {
 | 
			
		||||
  id: SERVER_DELTA["id"];
 | 
			
		||||
  version: SERVER_DELTA["version"];
 | 
			
		||||
  position: number;
 | 
			
		||||
  payload: ArrayBuffer;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type SERVER_MESSAGE =
 | 
			
		||||
  | {
 | 
			
		||||
      type: "relayed";
 | 
			
		||||
      payload: RELAY_PAYLOAD;
 | 
			
		||||
    }
 | 
			
		||||
  | { type: "acknowledged"; payload: { deltas: Array<SERVER_DELTA> } }
 | 
			
		||||
  | {
 | 
			
		||||
      type: "rejected";
 | 
			
		||||
      payload: { deltas: Array<CLIENT_DELTA>; message: string };
 | 
			
		||||
    }
 | 
			
		||||
  | { type: "restored"; payload: { elements: Array<ExcalidrawElement> } };
 | 
			
		||||
 | 
			
		||||
export interface DeltasRepository {
 | 
			
		||||
  save(delta: CLIENT_DELTA): SERVER_DELTA | null;
 | 
			
		||||
  getAllSinceVersion(version: number): Array<SERVER_DELTA>;
 | 
			
		||||
  getLastVersion(): number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CFDO: should come from the shared types package (might need a bigger refactor)
 | 
			
		||||
export type ExcalidrawElement = {
 | 
			
		||||
  id: string;
 | 
			
		||||
  type: any;
 | 
			
		||||
  version: number;
 | 
			
		||||
  [key: string]: any;
 | 
			
		||||
};
 | 
			
		||||
@@ -1,74 +0,0 @@
 | 
			
		||||
import throttle from "lodash.throttle";
 | 
			
		||||
import type { StoreDelta } from "../store";
 | 
			
		||||
 | 
			
		||||
export interface DeltasRepository {
 | 
			
		||||
  loadDeltas(): Promise<Array<StoreDelta> | null>;
 | 
			
		||||
  saveDeltas(params: StoreDelta[]): Promise<void>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface MetadataRepository {
 | 
			
		||||
  loadMetadata(): Promise<{ lastAcknowledgedVersion: number } | null>;
 | 
			
		||||
  saveMetadata(metadata: { lastAcknowledgedVersion: number }): Promise<void>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class LocalDeltasQueue {
 | 
			
		||||
  private readonly queue: Map<string, StoreDelta>;
 | 
			
		||||
  private readonly repository: DeltasRepository;
 | 
			
		||||
 | 
			
		||||
  private constructor(
 | 
			
		||||
    queue: Map<string, StoreDelta> = new Map(),
 | 
			
		||||
    repository: DeltasRepository,
 | 
			
		||||
  ) {
 | 
			
		||||
    this.queue = queue;
 | 
			
		||||
    this.repository = repository;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static async create(repository: DeltasRepository) {
 | 
			
		||||
    const deltas = await repository.loadDeltas();
 | 
			
		||||
 | 
			
		||||
    return new LocalDeltasQueue(
 | 
			
		||||
      new Map(deltas?.map((delta) => [delta.id, delta])),
 | 
			
		||||
      repository,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getAll() {
 | 
			
		||||
    return Array.from(this.queue.values());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public get(id: StoreDelta["id"]) {
 | 
			
		||||
    return this.queue.get(id);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public has(id: StoreDelta["id"]) {
 | 
			
		||||
    return this.queue.has(id);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public add(...deltas: StoreDelta[]) {
 | 
			
		||||
    for (const delta of deltas) {
 | 
			
		||||
      this.queue.set(delta.id, delta);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.persist();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public remove(...ids: StoreDelta["id"][]) {
 | 
			
		||||
    for (const id of ids) {
 | 
			
		||||
      this.queue.delete(id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.persist();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public persist = throttle(
 | 
			
		||||
    async () => {
 | 
			
		||||
      try {
 | 
			
		||||
        await this.repository.saveDeltas(this.getAll());
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        console.error("Failed to persist the sync queue:", e);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    1000,
 | 
			
		||||
    { leading: false, trailing: true },
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@@ -1,308 +0,0 @@
 | 
			
		||||
import AsyncLock from "async-lock";
 | 
			
		||||
import { Network, Utils } from "./utils";
 | 
			
		||||
 | 
			
		||||
import type {
 | 
			
		||||
  DeltasRepository,
 | 
			
		||||
  PULL_PAYLOAD,
 | 
			
		||||
  PUSH_PAYLOAD,
 | 
			
		||||
  SERVER_MESSAGE,
 | 
			
		||||
  SERVER_DELTA,
 | 
			
		||||
  CHUNK_INFO,
 | 
			
		||||
  RELAY_PAYLOAD,
 | 
			
		||||
  CLIENT_MESSAGE_BINARY,
 | 
			
		||||
  CLIENT_MESSAGE,
 | 
			
		||||
  ExcalidrawElement,
 | 
			
		||||
} from "./protocol";
 | 
			
		||||
import { StoreDelta } from "../store";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Core excalidraw sync logic.
 | 
			
		||||
 */
 | 
			
		||||
export class ExcalidrawSyncServer {
 | 
			
		||||
  private readonly lock: AsyncLock = new AsyncLock();
 | 
			
		||||
  private readonly sessions: Set<WebSocket> = new Set();
 | 
			
		||||
  private readonly chunks = new Map<
 | 
			
		||||
    CHUNK_INFO["id"],
 | 
			
		||||
    Map<CHUNK_INFO["position"], CLIENT_MESSAGE_BINARY["payload"]>
 | 
			
		||||
  >();
 | 
			
		||||
 | 
			
		||||
  // CFDO II: load from the db
 | 
			
		||||
  private elements = new Map<string, ExcalidrawElement>();
 | 
			
		||||
 | 
			
		||||
  constructor(private readonly repository: DeltasRepository) {
 | 
			
		||||
    // CFDO II: load from the db
 | 
			
		||||
    const deltas = this.repository.getAllSinceVersion(0);
 | 
			
		||||
 | 
			
		||||
    for (const delta of deltas) {
 | 
			
		||||
      const storeDelta = StoreDelta.load(delta.payload);
 | 
			
		||||
 | 
			
		||||
      // CFDO II: fix types (everywhere)
 | 
			
		||||
      const [nextElements] = storeDelta.elements.applyTo(this.elements as any);
 | 
			
		||||
 | 
			
		||||
      this.elements = nextElements;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // CFDO: optimize, should send a message about collaborators (no collaborators => no need to send ephemerals)
 | 
			
		||||
  public onConnect(client: WebSocket) {
 | 
			
		||||
    this.sessions.add(client);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public onDisconnect(client: WebSocket) {
 | 
			
		||||
    this.sessions.delete(client);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public onMessage(
 | 
			
		||||
    client: WebSocket,
 | 
			
		||||
    message: ArrayBuffer,
 | 
			
		||||
  ): Promise<void> | void {
 | 
			
		||||
    const [rawMessage, parsingError] = Utils.try<CLIENT_MESSAGE_BINARY>(() =>
 | 
			
		||||
      Network.decodeClientMessage(message),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (parsingError) {
 | 
			
		||||
      console.error(parsingError);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // if there is chunkInfo, there are more than 1 chunks => process them first
 | 
			
		||||
    if (rawMessage.chunkInfo) {
 | 
			
		||||
      return this.processChunks(client, {
 | 
			
		||||
        ...rawMessage,
 | 
			
		||||
        chunkInfo: rawMessage.chunkInfo,
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return this.processMessage(client, rawMessage);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Process chunks in case the client-side payload would overflow the 1MiB durable object WS message limit.
 | 
			
		||||
   */
 | 
			
		||||
  private processChunks(
 | 
			
		||||
    client: WebSocket,
 | 
			
		||||
    message: CLIENT_MESSAGE_BINARY &
 | 
			
		||||
      Required<Pick<CLIENT_MESSAGE_BINARY, "chunkInfo">>,
 | 
			
		||||
  ) {
 | 
			
		||||
    let shouldCleanupchunks = true;
 | 
			
		||||
    const {
 | 
			
		||||
      type,
 | 
			
		||||
      payload,
 | 
			
		||||
      chunkInfo: { id, position, count },
 | 
			
		||||
    } = message;
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      if (!this.chunks.has(id)) {
 | 
			
		||||
        this.chunks.set(id, new Map());
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const chunks = this.chunks.get(id);
 | 
			
		||||
 | 
			
		||||
      if (!chunks) {
 | 
			
		||||
        // defensive, shouldn't really happen
 | 
			
		||||
        throw new Error(`Coudn't find a relevant chunk with id "${id}"!`);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // set the buffer by order
 | 
			
		||||
      chunks.set(position, payload);
 | 
			
		||||
 | 
			
		||||
      if (chunks.size !== count) {
 | 
			
		||||
        // we don't have all the chunks, don't cleanup just yet!
 | 
			
		||||
        shouldCleanupchunks = false;
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // hopefully we can fit into the 128 MiB memory limit
 | 
			
		||||
      const restoredPayload = Array.from(chunks)
 | 
			
		||||
        .sort(([positionA], [positionB]) => (positionA <= positionB ? -1 : 1))
 | 
			
		||||
        .reduce(
 | 
			
		||||
          (acc, [_, payload]) => Uint8Array.from([...acc, ...payload]),
 | 
			
		||||
          new Uint8Array(),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
      const rawMessage = {
 | 
			
		||||
        type,
 | 
			
		||||
        payload: restoredPayload,
 | 
			
		||||
      } as Omit<CLIENT_MESSAGE_BINARY, "chunkInfo">;
 | 
			
		||||
 | 
			
		||||
      return this.processMessage(client, rawMessage);
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      console.error(`Error while processing chunk "${id}"`, error);
 | 
			
		||||
    } finally {
 | 
			
		||||
      // cleanup the chunks
 | 
			
		||||
      if (shouldCleanupchunks) {
 | 
			
		||||
        this.chunks.delete(id);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private processMessage(
 | 
			
		||||
    client: WebSocket,
 | 
			
		||||
    { type, payload }: Omit<CLIENT_MESSAGE_BINARY, "chunkInfo">,
 | 
			
		||||
  ) {
 | 
			
		||||
    const [parsedPayload, parsingError] = Utils.try<CLIENT_MESSAGE["payload"]>(
 | 
			
		||||
      () => Network.fromBinary(payload),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (parsingError) {
 | 
			
		||||
      console.error(parsingError);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (type) {
 | 
			
		||||
      case "restore":
 | 
			
		||||
        return this.restore(client);
 | 
			
		||||
      case "relay":
 | 
			
		||||
        return this.relay(client, parsedPayload as RELAY_PAYLOAD);
 | 
			
		||||
      case "pull":
 | 
			
		||||
        return this.pull(client, parsedPayload as PULL_PAYLOAD);
 | 
			
		||||
      case "push":
 | 
			
		||||
        // apply each one-by-one to avoid race conditions
 | 
			
		||||
        // CFDO: in theory we do not need to block ephemeral appState (for now we are not even using them)
 | 
			
		||||
        return this.lock.acquire("push", () =>
 | 
			
		||||
          this.push(client, parsedPayload as PUSH_PAYLOAD),
 | 
			
		||||
        );
 | 
			
		||||
      default:
 | 
			
		||||
        console.error(`Unknown message type: ${type}`);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private restore(client: WebSocket) {
 | 
			
		||||
    return this.send(client, {
 | 
			
		||||
      type: "restored",
 | 
			
		||||
      payload: {
 | 
			
		||||
        elements: Array.from(this.elements.values()),
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private relay(client: WebSocket, payload: RELAY_PAYLOAD) {
 | 
			
		||||
    // CFDO I: we should likely apply these to the snapshot
 | 
			
		||||
    return this.broadcast(
 | 
			
		||||
      {
 | 
			
		||||
        type: "relayed",
 | 
			
		||||
        payload,
 | 
			
		||||
      },
 | 
			
		||||
      client,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private pull(client: WebSocket, payload: PULL_PAYLOAD) {
 | 
			
		||||
    // CFDO: test for invalid payload
 | 
			
		||||
    const lastAcknowledgedClientVersion = payload.lastAcknowledgedVersion;
 | 
			
		||||
    const lastAcknowledgedServerVersion = this.repository.getLastVersion();
 | 
			
		||||
 | 
			
		||||
    const versionΔ =
 | 
			
		||||
      lastAcknowledgedServerVersion - lastAcknowledgedClientVersion;
 | 
			
		||||
 | 
			
		||||
    if (versionΔ < 0) {
 | 
			
		||||
      // CFDO II: restore the client from the snapshot / deltas?
 | 
			
		||||
      console.error(
 | 
			
		||||
        `Panic! Client claims to have higher acknowledged version than the latest one on the server!`,
 | 
			
		||||
      );
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const deltas: SERVER_DELTA[] = [];
 | 
			
		||||
 | 
			
		||||
    if (versionΔ > 0) {
 | 
			
		||||
      deltas.push(
 | 
			
		||||
        ...this.repository.getAllSinceVersion(lastAcknowledgedClientVersion),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.send(client, {
 | 
			
		||||
      type: "acknowledged",
 | 
			
		||||
      payload: {
 | 
			
		||||
        deltas,
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private push(client: WebSocket, delta: PUSH_PAYLOAD) {
 | 
			
		||||
    const [storeDelta, applyingError] = Utils.try(() => {
 | 
			
		||||
      // update the "deleted" delta according to the latest changes (in case of concurrent changes)
 | 
			
		||||
      const storeDelta = StoreDelta.applyLatestChanges(
 | 
			
		||||
        StoreDelta.load(delta),
 | 
			
		||||
        this.elements as any,
 | 
			
		||||
        "deleted",
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      // apply the delta to the elements snapshot
 | 
			
		||||
      const [nextElements] = storeDelta.elements.applyTo(this.elements as any);
 | 
			
		||||
 | 
			
		||||
      this.elements = nextElements;
 | 
			
		||||
 | 
			
		||||
      return storeDelta;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (applyingError) {
 | 
			
		||||
      // CFDO: everything should be automatically rolled-back in the db -> double-check
 | 
			
		||||
      return this.send(client, {
 | 
			
		||||
        type: "rejected",
 | 
			
		||||
        payload: {
 | 
			
		||||
          message: applyingError
 | 
			
		||||
            ? applyingError.message
 | 
			
		||||
            : "Couldn't apply the delta.",
 | 
			
		||||
          deltas: [delta],
 | 
			
		||||
        },
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const [acknowledged, savingError] = Utils.try(() => {
 | 
			
		||||
      return this.repository.save(storeDelta);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (savingError || !acknowledged) {
 | 
			
		||||
      // CFDO: everything should be automatically rolled-back in the db -> double-check
 | 
			
		||||
      return this.send(client, {
 | 
			
		||||
        type: "rejected",
 | 
			
		||||
        payload: {
 | 
			
		||||
          message: savingError
 | 
			
		||||
            ? savingError.message
 | 
			
		||||
            : "Coudn't persist the delta.",
 | 
			
		||||
          deltas: [storeDelta],
 | 
			
		||||
        },
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return this.broadcast({
 | 
			
		||||
      type: "acknowledged",
 | 
			
		||||
      payload: {
 | 
			
		||||
        deltas: [acknowledged],
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private send(ws: WebSocket, message: SERVER_MESSAGE) {
 | 
			
		||||
    const [encodedMessage, encodingError] = Utils.try(() =>
 | 
			
		||||
      Network.toBinary(message),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (encodingError) {
 | 
			
		||||
      console.error(encodingError);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ws.send(encodedMessage);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private broadcast(message: SERVER_MESSAGE, exclude?: WebSocket) {
 | 
			
		||||
    const [encodedMessage, encodingError] = Utils.try(() =>
 | 
			
		||||
      Network.toBinary(message),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (encodingError) {
 | 
			
		||||
      console.error(encodingError);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const ws of this.sessions) {
 | 
			
		||||
      if (ws === exclude) {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      ws.send(encodedMessage);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,68 +0,0 @@
 | 
			
		||||
import msgpack from "msgpack-lite";
 | 
			
		||||
 | 
			
		||||
import type { CLIENT_MESSAGE_BINARY } from "./protocol";
 | 
			
		||||
 | 
			
		||||
export const Utils = {
 | 
			
		||||
  try<T>(cb: () => T): [T, null] | [null, Error] {
 | 
			
		||||
    try {
 | 
			
		||||
      const result = cb();
 | 
			
		||||
      return [result, null];
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      if (error instanceof Error) {
 | 
			
		||||
        return [null, error];
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (typeof error === "string") {
 | 
			
		||||
        return [null, new Error(error)];
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return [null, new Error("Unknown error")];
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const Network = {
 | 
			
		||||
  toBinary: (payload: Record<string, unknown>) => {
 | 
			
		||||
    return new Uint8Array(msgpack.encode(payload));
 | 
			
		||||
  },
 | 
			
		||||
  fromBinary: (payload: Uint8Array) => {
 | 
			
		||||
    return msgpack.decode(payload);
 | 
			
		||||
  },
 | 
			
		||||
  encodeClientMessage: (message: CLIENT_MESSAGE_BINARY) => {
 | 
			
		||||
    const { payload, ...metadata } = message;
 | 
			
		||||
    const metadataBuffer = Network.toBinary(metadata);
 | 
			
		||||
 | 
			
		||||
    // contains the length of the rest of the message, so that we could chunk the payload and decode it server side
 | 
			
		||||
    const headerBuffer = new ArrayBuffer(4);
 | 
			
		||||
    new DataView(headerBuffer).setUint32(0, metadataBuffer.byteLength);
 | 
			
		||||
 | 
			
		||||
    // concatenate into [header(4 bytes)][metadata][payload]
 | 
			
		||||
    return Uint8Array.from([
 | 
			
		||||
      ...new Uint8Array(headerBuffer),
 | 
			
		||||
      ...metadataBuffer,
 | 
			
		||||
      ...message.payload,
 | 
			
		||||
    ]);
 | 
			
		||||
  },
 | 
			
		||||
  decodeClientMessage: (message: ArrayBuffer) => {
 | 
			
		||||
    const headerLength = 4;
 | 
			
		||||
    const header = new Uint8Array(message, 0, headerLength);
 | 
			
		||||
    const metadataLength = new DataView(
 | 
			
		||||
      header.buffer,
 | 
			
		||||
      header.byteOffset,
 | 
			
		||||
    ).getUint32(0);
 | 
			
		||||
 | 
			
		||||
    const metadata = new Uint8Array(
 | 
			
		||||
      message,
 | 
			
		||||
      headerLength,
 | 
			
		||||
      headerLength + metadataLength,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const payload = new Uint8Array(message, headerLength + metadataLength);
 | 
			
		||||
    const rawMessage = {
 | 
			
		||||
      ...Network.fromBinary(metadata),
 | 
			
		||||
      payload,
 | 
			
		||||
    } as CLIENT_MESSAGE_BINARY;
 | 
			
		||||
 | 
			
		||||
    return rawMessage;
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
// Generated by Wrangler by running `wrangler types --experimental-include-runtime=./worker-runtime.d.ts`
 | 
			
		||||
 | 
			
		||||
interface Env {
 | 
			
		||||
  DURABLE_ROOM: DurableObjectNamespace<
 | 
			
		||||
    import("./cloudflare/worker").DurableRoom
 | 
			
		||||
  >;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4691
									
								
								packages/excalidraw/worker-runtime.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4691
									
								
								packages/excalidraw/worker-runtime.d.ts
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,23 +0,0 @@
 | 
			
		||||
#:schema node_modules/wrangler/config-schema.json
 | 
			
		||||
name = "excalidraw-sync"
 | 
			
		||||
main = "cloudflare/worker.ts"
 | 
			
		||||
compatibility_date = "2024-11-12"
 | 
			
		||||
 | 
			
		||||
# Workers Logs
 | 
			
		||||
# Docs: https://developers.cloudflare.com/workers/observability/logs/workers-logs/
 | 
			
		||||
# Configuration: https://developers.cloudflare.com/workers/observability/logs/workers-logs/#enable-workers-logs
 | 
			
		||||
[observability]
 | 
			
		||||
enabled = true
 | 
			
		||||
 | 
			
		||||
# Bind a Durable Object. Durable objects are a scale-to-zero compute primitive based on the actor model.
 | 
			
		||||
# Durable Objects can live for as long as needed. Use these when you need a long-running "server", such as in realtime apps.
 | 
			
		||||
# Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#durable-objects
 | 
			
		||||
[[durable_objects.bindings]]
 | 
			
		||||
name = "DURABLE_ROOM"
 | 
			
		||||
class_name = "DurableRoom"
 | 
			
		||||
 | 
			
		||||
# Durable Object migrations.
 | 
			
		||||
# Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#migrations
 | 
			
		||||
[[migrations]]
 | 
			
		||||
tag = "v1"
 | 
			
		||||
new_sqlite_classes = ["DurableRoom"]
 | 
			
		||||
							
								
								
									
										555
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										555
									
								
								yarn.lock
									
									
									
									
									
								
							@@ -1506,76 +1506,11 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz#923ca57e173c6b232bbbb07347b1be982f03e783"
 | 
			
		||||
  integrity sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==
 | 
			
		||||
 | 
			
		||||
"@cloudflare/kv-asset-handler@0.3.4":
 | 
			
		||||
  version "0.3.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.3.4.tgz#5cc152847c8ae4d280ec5d7f4f6ba8c976b585c3"
 | 
			
		||||
  integrity sha512-YLPHc8yASwjNkmcDMQMY35yiWjoKAKnhUbPRszBRS0YgH+IXtsMp61j+yTcnCE3oO2DgP0U3iejLC8FTtKDC8Q==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    mime "^3.0.0"
 | 
			
		||||
 | 
			
		||||
"@cloudflare/workerd-darwin-64@1.20241106.1":
 | 
			
		||||
  version "1.20241106.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20241106.1.tgz#4f470f98ca12dbc3262ec8a432466e1c1525aad9"
 | 
			
		||||
  integrity sha512-zxvaToi1m0qzAScrxFt7UvFVqU8DxrCO2CinM1yQkv5no7pA1HolpIrwZ0xOhR3ny64Is2s/J6BrRjpO5dM9Zw==
 | 
			
		||||
 | 
			
		||||
"@cloudflare/workerd-darwin-arm64@1.20241106.1":
 | 
			
		||||
  version "1.20241106.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20241106.1.tgz#c34d6306afc50ae2eee3e538329af7192ae17dd0"
 | 
			
		||||
  integrity sha512-j3dg/42D/bPgfNP3cRUBxF+4waCKO/5YKwXNj+lnVOwHxDu+ne5pFw9TIkKYcWTcwn0ZUkbNZNM5rhJqRn4xbg==
 | 
			
		||||
 | 
			
		||||
"@cloudflare/workerd-linux-64@1.20241106.1":
 | 
			
		||||
  version "1.20241106.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20241106.1.tgz#42c425137c151348146a70d3f271e5f3293d3b75"
 | 
			
		||||
  integrity sha512-Ih+Ye8E1DMBXcKrJktGfGztFqHKaX1CeByqshmTbODnWKHt6O65ax3oTecUwyC0+abuyraOpAtdhHNpFMhUkmw==
 | 
			
		||||
 | 
			
		||||
"@cloudflare/workerd-linux-arm64@1.20241106.1":
 | 
			
		||||
  version "1.20241106.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20241106.1.tgz#f3bc7ab9424dafbf1816d8bc2e8aae24646ecad0"
 | 
			
		||||
  integrity sha512-mdQFPk4+14Yywn7n1xIzI+6olWM8Ybz10R7H3h+rk0XulMumCWUCy1CzIDauOx6GyIcSgKIibYMssVHZR30ObA==
 | 
			
		||||
 | 
			
		||||
"@cloudflare/workerd-windows-64@1.20241106.1":
 | 
			
		||||
  version "1.20241106.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20241106.1.tgz#9e2f1ec0d993c8b12c4cd7f9c5e6b953a0672707"
 | 
			
		||||
  integrity sha512-4rtcss31E/Rb/PeFocZfr+B9i1MdrkhsTBWizh8siNR4KMmkslU2xs2wPaH1z8+ErxkOsHrKRa5EPLh5rIiFeg==
 | 
			
		||||
 | 
			
		||||
"@cloudflare/workers-shared@0.7.1":
 | 
			
		||||
  version "0.7.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@cloudflare/workers-shared/-/workers-shared-0.7.1.tgz#cf32caaf58808d9e36f9ebc8baa84a9699b388f2"
 | 
			
		||||
  integrity sha512-46cP5FCrl3TrvHeoHLb5SRuiDMKH5kc9Yvo36SAfzt8dqJI/qJRoY1GP3ioHn/gP7v2QIoUOTAzIl7Ml7MnfrA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    mime "^3.0.0"
 | 
			
		||||
    zod "^3.22.3"
 | 
			
		||||
 | 
			
		||||
"@cloudflare/workers-types@4.20241112.0":
 | 
			
		||||
  version "4.20241112.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@cloudflare/workers-types/-/workers-types-4.20241112.0.tgz#174f8ccbfe1cc2e07b86622f82b84698f62d14c7"
 | 
			
		||||
  integrity sha512-Q4p9bAWZrX14bSCKY9to19xl0KMU7nsO5sJ2cTVspHoypsjPUMeQCsjHjmsO2C4Myo8/LPeDvmqFmkyNAPPYZw==
 | 
			
		||||
 | 
			
		||||
"@cspotcode/source-map-support@0.8.1":
 | 
			
		||||
  version "0.8.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
 | 
			
		||||
  integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@jridgewell/trace-mapping" "0.3.9"
 | 
			
		||||
 | 
			
		||||
"@discoveryjs/json-ext@^0.5.0":
 | 
			
		||||
  version "0.5.7"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
 | 
			
		||||
  integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
 | 
			
		||||
 | 
			
		||||
"@esbuild-plugins/node-globals-polyfill@^0.2.3":
 | 
			
		||||
  version "0.2.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.2.3.tgz#0e4497a2b53c9e9485e149bc92ddb228438d6bcf"
 | 
			
		||||
  integrity sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw==
 | 
			
		||||
 | 
			
		||||
"@esbuild-plugins/node-modules-polyfill@^0.2.2":
 | 
			
		||||
  version "0.2.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.2.2.tgz#cefa3dc0bd1c16277a8338b52833420c94987327"
 | 
			
		||||
  integrity sha512-LXV7QsWJxRuMYvKbiznh+U1ilIop3g2TeKRzUxOG5X3YITc8JyyTa90BmLwqqv0YnX4v32CSlG+vsziZp9dMvA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    escape-string-regexp "^4.0.0"
 | 
			
		||||
    rollup-plugin-node-polyfills "^0.2.1"
 | 
			
		||||
 | 
			
		||||
"@esbuild/aix-ppc64@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.10.tgz#fb3922a0183d27446de00cf60d4f7baaadf98d84"
 | 
			
		||||
@@ -1591,11 +1526,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz#a70f4ac11c6a1dfc18b8bbb13284155d933b9537"
 | 
			
		||||
  integrity sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==
 | 
			
		||||
 | 
			
		||||
"@esbuild/android-arm64@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd"
 | 
			
		||||
  integrity sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==
 | 
			
		||||
 | 
			
		||||
"@esbuild/android-arm64@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.10.tgz#ef31015416dd79398082409b77aaaa2ade4d531a"
 | 
			
		||||
@@ -1611,11 +1541,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz#db1c9202a5bc92ea04c7b6840f1bbe09ebf9e6b9"
 | 
			
		||||
  integrity sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==
 | 
			
		||||
 | 
			
		||||
"@esbuild/android-arm@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.19.tgz#5898f7832c2298bc7d0ab53701c57beb74d78b4d"
 | 
			
		||||
  integrity sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==
 | 
			
		||||
 | 
			
		||||
"@esbuild/android-arm@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.10.tgz#1c23c7e75473aae9fb323be5d9db225142f47f52"
 | 
			
		||||
@@ -1631,11 +1556,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz#3b488c49aee9d491c2c8f98a909b785870d6e995"
 | 
			
		||||
  integrity sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==
 | 
			
		||||
 | 
			
		||||
"@esbuild/android-x64@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.19.tgz#658368ef92067866d95fb268719f98f363d13ae1"
 | 
			
		||||
  integrity sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==
 | 
			
		||||
 | 
			
		||||
"@esbuild/android-x64@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.10.tgz#df6a4e6d6eb8da5595cfce16d4e3f6bc24464707"
 | 
			
		||||
@@ -1651,11 +1571,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz#3b1628029e5576249d2b2d766696e50768449f98"
 | 
			
		||||
  integrity sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==
 | 
			
		||||
 | 
			
		||||
"@esbuild/darwin-arm64@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz#584c34c5991b95d4d48d333300b1a4e2ff7be276"
 | 
			
		||||
  integrity sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==
 | 
			
		||||
 | 
			
		||||
"@esbuild/darwin-arm64@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.10.tgz#8462a55db07c1b2fad61c8244ce04469ef1043be"
 | 
			
		||||
@@ -1671,11 +1586,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz#6e8517a045ddd86ae30c6608c8475ebc0c4000bb"
 | 
			
		||||
  integrity sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==
 | 
			
		||||
 | 
			
		||||
"@esbuild/darwin-x64@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz#7751d236dfe6ce136cce343dce69f52d76b7f6cb"
 | 
			
		||||
  integrity sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==
 | 
			
		||||
 | 
			
		||||
"@esbuild/darwin-x64@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.10.tgz#d1de20bfd41bb75b955ba86a6b1004539e8218c1"
 | 
			
		||||
@@ -1691,11 +1601,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz#90ed098e1f9dd8a9381695b207e1cff45540a0d0"
 | 
			
		||||
  integrity sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==
 | 
			
		||||
 | 
			
		||||
"@esbuild/freebsd-arm64@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz#cacd171665dd1d500f45c167d50c6b7e539d5fd2"
 | 
			
		||||
  integrity sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==
 | 
			
		||||
 | 
			
		||||
"@esbuild/freebsd-arm64@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.10.tgz#16904879e34c53a2e039d1284695d2db3e664d57"
 | 
			
		||||
@@ -1711,11 +1616,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz#d71502d1ee89a1130327e890364666c760a2a911"
 | 
			
		||||
  integrity sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==
 | 
			
		||||
 | 
			
		||||
"@esbuild/freebsd-x64@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz#0769456eee2a08b8d925d7c00b79e861cb3162e4"
 | 
			
		||||
  integrity sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==
 | 
			
		||||
 | 
			
		||||
"@esbuild/freebsd-x64@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.10.tgz#8ad9e5ca9786ca3f1ef1411bfd10b08dcd9d4cef"
 | 
			
		||||
@@ -1731,11 +1631,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz#aa5ea58d9c1dd9af688b8b6f63ef0d3d60cea53c"
 | 
			
		||||
  integrity sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==
 | 
			
		||||
 | 
			
		||||
"@esbuild/linux-arm64@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz#38e162ecb723862c6be1c27d6389f48960b68edb"
 | 
			
		||||
  integrity sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==
 | 
			
		||||
 | 
			
		||||
"@esbuild/linux-arm64@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.10.tgz#d82cf2c590faece82d28bbf1cfbe36f22ae25bd2"
 | 
			
		||||
@@ -1751,11 +1646,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz#055b63725df678379b0f6db9d0fa85463755b2e5"
 | 
			
		||||
  integrity sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==
 | 
			
		||||
 | 
			
		||||
"@esbuild/linux-arm@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz#1a2cd399c50040184a805174a6d89097d9d1559a"
 | 
			
		||||
  integrity sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==
 | 
			
		||||
 | 
			
		||||
"@esbuild/linux-arm@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.10.tgz#477b8e7c7bcd34369717b04dd9ee6972c84f4029"
 | 
			
		||||
@@ -1771,11 +1661,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz#76b3b98cb1f87936fbc37f073efabad49dcd889c"
 | 
			
		||||
  integrity sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==
 | 
			
		||||
 | 
			
		||||
"@esbuild/linux-ia32@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz#e28c25266b036ce1cabca3c30155222841dc035a"
 | 
			
		||||
  integrity sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==
 | 
			
		||||
 | 
			
		||||
"@esbuild/linux-ia32@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.10.tgz#d55ff822cf5b0252a57112f86857ff23be6cab0e"
 | 
			
		||||
@@ -1791,11 +1676,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz#c0e5e787c285264e5dfc7a79f04b8b4eefdad7fa"
 | 
			
		||||
  integrity sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==
 | 
			
		||||
 | 
			
		||||
"@esbuild/linux-loong64@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz#0f887b8bb3f90658d1a0117283e55dbd4c9dcf72"
 | 
			
		||||
  integrity sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==
 | 
			
		||||
 | 
			
		||||
"@esbuild/linux-loong64@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.10.tgz#a9ad057d7e48d6c9f62ff50f6f208e331c4543c7"
 | 
			
		||||
@@ -1811,11 +1691,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz#a6184e62bd7cdc63e0c0448b83801001653219c5"
 | 
			
		||||
  integrity sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==
 | 
			
		||||
 | 
			
		||||
"@esbuild/linux-mips64el@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz#f5d2a0b8047ea9a5d9f592a178ea054053a70289"
 | 
			
		||||
  integrity sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==
 | 
			
		||||
 | 
			
		||||
"@esbuild/linux-mips64el@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.10.tgz#b011a96924773d60ebab396fbd7a08de66668179"
 | 
			
		||||
@@ -1831,11 +1706,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz#d08e39ce86f45ef8fc88549d29c62b8acf5649aa"
 | 
			
		||||
  integrity sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==
 | 
			
		||||
 | 
			
		||||
"@esbuild/linux-ppc64@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz#876590e3acbd9fa7f57a2c7d86f83717dbbac8c7"
 | 
			
		||||
  integrity sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==
 | 
			
		||||
 | 
			
		||||
"@esbuild/linux-ppc64@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.10.tgz#5d8b59929c029811e473f2544790ea11d588d4dd"
 | 
			
		||||
@@ -1851,11 +1721,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz#8d252f0b7756ffd6d1cbde5ea67ff8fd20437f20"
 | 
			
		||||
  integrity sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==
 | 
			
		||||
 | 
			
		||||
"@esbuild/linux-riscv64@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz#7f49373df463cd9f41dc34f9b2262d771688bf09"
 | 
			
		||||
  integrity sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==
 | 
			
		||||
 | 
			
		||||
"@esbuild/linux-riscv64@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.10.tgz#292b06978375b271bd8bc0a554e0822957508d22"
 | 
			
		||||
@@ -1871,11 +1736,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz#19f6dcdb14409dae607f66ca1181dd4e9db81300"
 | 
			
		||||
  integrity sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==
 | 
			
		||||
 | 
			
		||||
"@esbuild/linux-s390x@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz#e2afd1afcaf63afe2c7d9ceacd28ec57c77f8829"
 | 
			
		||||
  integrity sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==
 | 
			
		||||
 | 
			
		||||
"@esbuild/linux-s390x@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.10.tgz#d30af63530f8d4fa96930374c9dd0d62bf59e069"
 | 
			
		||||
@@ -1891,11 +1751,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz#3c830c90f1a5d7dd1473d5595ea4ebb920988685"
 | 
			
		||||
  integrity sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==
 | 
			
		||||
 | 
			
		||||
"@esbuild/linux-x64@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz#8a0e9738b1635f0c53389e515ae83826dec22aa4"
 | 
			
		||||
  integrity sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==
 | 
			
		||||
 | 
			
		||||
"@esbuild/linux-x64@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.10.tgz#898c72eeb74d9f2fb43acf316125b475548b75ce"
 | 
			
		||||
@@ -1911,11 +1766,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz#86eca35203afc0d9de0694c64ec0ab0a378f6fff"
 | 
			
		||||
  integrity sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==
 | 
			
		||||
 | 
			
		||||
"@esbuild/netbsd-x64@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz#c29fb2453c6b7ddef9a35e2c18b37bda1ae5c462"
 | 
			
		||||
  integrity sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==
 | 
			
		||||
 | 
			
		||||
"@esbuild/netbsd-x64@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.10.tgz#fd473a5ae261b43eab6dad4dbd5a3155906e6c91"
 | 
			
		||||
@@ -1931,11 +1781,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz#e771c8eb0e0f6e1877ffd4220036b98aed5915e6"
 | 
			
		||||
  integrity sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==
 | 
			
		||||
 | 
			
		||||
"@esbuild/openbsd-x64@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz#95e75a391403cb10297280d524d66ce04c920691"
 | 
			
		||||
  integrity sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==
 | 
			
		||||
 | 
			
		||||
"@esbuild/openbsd-x64@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.10.tgz#96eb8992e526717b5272321eaad3e21f3a608e46"
 | 
			
		||||
@@ -1951,11 +1796,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz#9a795ae4b4e37e674f0f4d716f3e226dd7c39baf"
 | 
			
		||||
  integrity sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==
 | 
			
		||||
 | 
			
		||||
"@esbuild/sunos-x64@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz#722eaf057b83c2575937d3ffe5aeb16540da7273"
 | 
			
		||||
  integrity sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==
 | 
			
		||||
 | 
			
		||||
"@esbuild/sunos-x64@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.10.tgz#c16ee1c167f903eaaa6acf7372bee42d5a89c9bc"
 | 
			
		||||
@@ -1971,11 +1811,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz#7df23b61a497b8ac189def6e25a95673caedb03f"
 | 
			
		||||
  integrity sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==
 | 
			
		||||
 | 
			
		||||
"@esbuild/win32-arm64@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz#9aa9dc074399288bdcdd283443e9aeb6b9552b6f"
 | 
			
		||||
  integrity sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==
 | 
			
		||||
 | 
			
		||||
"@esbuild/win32-arm64@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.10.tgz#7e417d1971dbc7e469b4eceb6a5d1d667b5e3dcc"
 | 
			
		||||
@@ -1991,11 +1826,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz#f1ae5abf9ca052ae11c1bc806fb4c0f519bacf90"
 | 
			
		||||
  integrity sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==
 | 
			
		||||
 | 
			
		||||
"@esbuild/win32-ia32@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz#95ad43c62ad62485e210f6299c7b2571e48d2b03"
 | 
			
		||||
  integrity sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==
 | 
			
		||||
 | 
			
		||||
"@esbuild/win32-ia32@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.10.tgz#2b52dfec6cd061ecb36171c13bae554888b439e5"
 | 
			
		||||
@@ -2011,11 +1841,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz#241fe62c34d8e8461cd708277813e1d0ba55ce23"
 | 
			
		||||
  integrity sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==
 | 
			
		||||
 | 
			
		||||
"@esbuild/win32-x64@0.17.19":
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz#8cfaf2ff603e9aabb910e9c0558c26cf32744061"
 | 
			
		||||
  integrity sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==
 | 
			
		||||
 | 
			
		||||
"@esbuild/win32-x64@0.19.10":
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.10.tgz#bd123a74f243d2f3a1f046447bb9b363ee25d072"
 | 
			
		||||
@@ -2097,11 +1922,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@excalidraw/random-username/-/random-username-1.1.0.tgz#6f388d6a9708cf655b8c9c6aa3fa569ee71ecf0f"
 | 
			
		||||
  integrity sha512-nULYsQxkWHnbmHvcs+efMkJ4/9TtvNyFeLyHdeGxW0zHs6P+jYVqcRff9A6Vq9w9JXeDRnRh2VKvTtS19GW2qA==
 | 
			
		||||
 | 
			
		||||
"@fastify/busboy@^2.0.0":
 | 
			
		||||
  version "2.1.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d"
 | 
			
		||||
  integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==
 | 
			
		||||
 | 
			
		||||
"@firebase/analytics-types@0.4.0":
 | 
			
		||||
  version "0.4.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@firebase/analytics-types/-/analytics-types-0.4.0.tgz#d6716f9fa36a6e340bc0ecfe68af325aa6f60508"
 | 
			
		||||
@@ -2438,7 +2258,7 @@
 | 
			
		||||
    "@jridgewell/sourcemap-codec" "^1.4.10"
 | 
			
		||||
    "@jridgewell/trace-mapping" "^0.3.24"
 | 
			
		||||
 | 
			
		||||
"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0":
 | 
			
		||||
"@jridgewell/resolve-uri@^3.1.0":
 | 
			
		||||
  version "3.1.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
 | 
			
		||||
  integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
 | 
			
		||||
@@ -2466,14 +2286,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
 | 
			
		||||
  integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
 | 
			
		||||
 | 
			
		||||
"@jridgewell/trace-mapping@0.3.9":
 | 
			
		||||
  version "0.3.9"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
 | 
			
		||||
  integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@jridgewell/resolve-uri" "^3.0.3"
 | 
			
		||||
    "@jridgewell/sourcemap-codec" "^1.4.10"
 | 
			
		||||
 | 
			
		||||
"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
 | 
			
		||||
  version "0.3.25"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
 | 
			
		||||
@@ -3273,11 +3085,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708"
 | 
			
		||||
  integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==
 | 
			
		||||
 | 
			
		||||
"@types/async-lock@^1.4.2":
 | 
			
		||||
  version "1.4.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/async-lock/-/async-lock-1.4.2.tgz#c2037ba1d6018de766c2505c3abe3b7b6b244ab4"
 | 
			
		||||
  integrity sha512-HlZ6Dcr205BmNhwkdXqrg2vkFMN2PluI7Lgr8In3B3wE5PiQHhjRqtW/lGdVU9gw+sM0JcIDx2AN+cW8oSWIcw==
 | 
			
		||||
 | 
			
		||||
"@types/chai@4.3.0":
 | 
			
		||||
  version "4.3.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.0.tgz#23509ebc1fa32f1b4d50d6a66c4032d5b8eaabdc"
 | 
			
		||||
@@ -3412,20 +3219,6 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433"
 | 
			
		||||
  integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==
 | 
			
		||||
 | 
			
		||||
"@types/msgpack-lite@0.1.11":
 | 
			
		||||
  version "0.1.11"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/msgpack-lite/-/msgpack-lite-0.1.11.tgz#f618e1fc469577f65f36c474ff3309407afef174"
 | 
			
		||||
  integrity sha512-cdCZS/gw+jIN22I4SUZUFf1ZZfVv5JM1//Br/MuZcI373sxiy3eSSoiyLu0oz+BPatTbGGGBO5jrcvd0siCdTQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@types/node" "*"
 | 
			
		||||
 | 
			
		||||
"@types/node-forge@^1.3.0":
 | 
			
		||||
  version "1.3.11"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da"
 | 
			
		||||
  integrity sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@types/node" "*"
 | 
			
		||||
 | 
			
		||||
"@types/node@*", "@types/node@>=13.7.0", "@types/node@^20":
 | 
			
		||||
  version "20.12.12"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.12.tgz#7cbecdf902085cec634fdb362172dfe12b8f2050"
 | 
			
		||||
@@ -4015,13 +3808,6 @@ acorn-walk@^8.0.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa"
 | 
			
		||||
  integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==
 | 
			
		||||
 | 
			
		||||
acorn-walk@^8.2.0:
 | 
			
		||||
  version "8.3.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7"
 | 
			
		||||
  integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    acorn "^8.11.0"
 | 
			
		||||
 | 
			
		||||
acorn@^7.4.0:
 | 
			
		||||
  version "7.4.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
 | 
			
		||||
@@ -4032,11 +3818,6 @@ acorn@^8.0.4, acorn@^8.7.1, acorn@^8.8.2:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a"
 | 
			
		||||
  integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==
 | 
			
		||||
 | 
			
		||||
acorn@^8.11.0, acorn@^8.8.0:
 | 
			
		||||
  version "8.14.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
 | 
			
		||||
  integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
 | 
			
		||||
 | 
			
		||||
agent-base@6:
 | 
			
		||||
  version "6.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
 | 
			
		||||
@@ -4280,13 +4061,6 @@ arraybuffer.prototype.slice@^1.0.3:
 | 
			
		||||
    is-array-buffer "^3.0.4"
 | 
			
		||||
    is-shared-array-buffer "^1.0.2"
 | 
			
		||||
 | 
			
		||||
as-table@^1.0.36:
 | 
			
		||||
  version "1.0.55"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/as-table/-/as-table-1.0.55.tgz#dc984da3937745de902cea1d45843c01bdbbec4f"
 | 
			
		||||
  integrity sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    printable-characters "^1.0.42"
 | 
			
		||||
 | 
			
		||||
assertion-error@^1.1.0:
 | 
			
		||||
  version "1.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
 | 
			
		||||
@@ -4307,11 +4081,6 @@ astral-regex@^2.0.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
 | 
			
		||||
  integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
 | 
			
		||||
 | 
			
		||||
async-lock@^1.4.1:
 | 
			
		||||
  version "1.4.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.4.1.tgz#56b8718915a9b68b10fce2f2a9a3dddf765ef53f"
 | 
			
		||||
  integrity sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==
 | 
			
		||||
 | 
			
		||||
async@^2.6.4:
 | 
			
		||||
  version "2.6.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221"
 | 
			
		||||
@@ -4580,11 +4349,6 @@ bl@^4.0.3:
 | 
			
		||||
    inherits "^2.0.4"
 | 
			
		||||
    readable-stream "^3.4.0"
 | 
			
		||||
 | 
			
		||||
blake3-wasm@^2.1.5:
 | 
			
		||||
  version "2.1.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/blake3-wasm/-/blake3-wasm-2.1.5.tgz#b22dbb84bc9419ed0159caa76af4b1b132e6ba52"
 | 
			
		||||
  integrity sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==
 | 
			
		||||
 | 
			
		||||
boolbase@^1.0.0:
 | 
			
		||||
  version "1.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
 | 
			
		||||
@@ -4721,14 +4485,6 @@ canvas-roundrect-polyfill@0.0.1:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/canvas-roundrect-polyfill/-/canvas-roundrect-polyfill-0.0.1.tgz#70bf107ebe2037f26d839d7f809a26f4a95f5696"
 | 
			
		||||
  integrity sha512-yWq+R3U3jE+coOeEb3a3GgE2j/0MMiDKM/QpLb6h9ihf5fGY9UXtvK9o4vNqjWXoZz7/3EaSVU3IX53TvFFUOw==
 | 
			
		||||
 | 
			
		||||
capnp-ts@^0.7.0:
 | 
			
		||||
  version "0.7.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/capnp-ts/-/capnp-ts-0.7.0.tgz#16fd8e76b667d002af8fcf4bf92bf15d1a7b54a9"
 | 
			
		||||
  integrity sha512-XKxXAC3HVPv7r674zP0VC3RTXz+/JKhfyw94ljvF80yynK6VkTnqE3jMuN8b3dUVmmc43TjyxjW4KTsmB3c86g==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    debug "^4.3.1"
 | 
			
		||||
    tslib "^2.2.0"
 | 
			
		||||
 | 
			
		||||
chai@4.3.6:
 | 
			
		||||
  version "4.3.6"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.6.tgz#ffe4ba2d9fa9d6680cc0b370adae709ec9011e9c"
 | 
			
		||||
@@ -4821,13 +4577,6 @@ check-error@^2.1.1:
 | 
			
		||||
  optionalDependencies:
 | 
			
		||||
    fsevents "~2.3.2"
 | 
			
		||||
 | 
			
		||||
chokidar@^4.0.1:
 | 
			
		||||
  version "4.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.1.tgz#4a6dff66798fb0f72a94f616abbd7e1a19f31d41"
 | 
			
		||||
  integrity sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    readdirp "^4.0.1"
 | 
			
		||||
 | 
			
		||||
chownr@^1.1.1:
 | 
			
		||||
  version "1.1.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
 | 
			
		||||
@@ -5002,11 +4751,6 @@ convert-source-map@^2.0.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
 | 
			
		||||
  integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
 | 
			
		||||
 | 
			
		||||
cookie@^0.7.1:
 | 
			
		||||
  version "0.7.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7"
 | 
			
		||||
  integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==
 | 
			
		||||
 | 
			
		||||
core-js-compat@^3.31.0, core-js-compat@^3.36.1:
 | 
			
		||||
  version "3.37.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.37.1.tgz#c844310c7852f4bdf49b8d339730b97e17ff09ee"
 | 
			
		||||
@@ -5457,11 +5201,6 @@ damerau-levenshtein@^1.0.8:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
 | 
			
		||||
  integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
 | 
			
		||||
 | 
			
		||||
data-uri-to-buffer@^2.0.0:
 | 
			
		||||
  version "2.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz#d296973d5a4897a5dbe31716d118211921f04770"
 | 
			
		||||
  integrity sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==
 | 
			
		||||
 | 
			
		||||
data-urls@^4.0.0:
 | 
			
		||||
  version "4.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-4.0.0.tgz#333a454eca6f9a5b7b0f1013ff89074c3f522dd4"
 | 
			
		||||
@@ -5498,11 +5237,6 @@ data-view-byte-offset@^1.0.0:
 | 
			
		||||
    es-errors "^1.3.0"
 | 
			
		||||
    is-data-view "^1.0.1"
 | 
			
		||||
 | 
			
		||||
date-fns@^4.1.0:
 | 
			
		||||
  version "4.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-4.1.0.tgz#64b3d83fff5aa80438f5b1a633c2e83b8a1c2d14"
 | 
			
		||||
  integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==
 | 
			
		||||
 | 
			
		||||
dayjs@^1.11.7:
 | 
			
		||||
  version "1.11.11"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.11.tgz#dfe0e9d54c5f8b68ccf8ca5f72ac603e7e5ed59e"
 | 
			
		||||
@@ -5593,11 +5327,6 @@ define-properties@^1.2.0, define-properties@^1.2.1:
 | 
			
		||||
    has-property-descriptors "^1.0.0"
 | 
			
		||||
    object-keys "^1.1.1"
 | 
			
		||||
 | 
			
		||||
defu@^6.1.4:
 | 
			
		||||
  version "6.1.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.4.tgz#4e0c9cf9ff68fe5f3d7f2765cc1a012dfdcb0479"
 | 
			
		||||
  integrity sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==
 | 
			
		||||
 | 
			
		||||
delaunator@5:
 | 
			
		||||
  version "5.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/delaunator/-/delaunator-5.0.1.tgz#39032b08053923e924d6094fe2cde1a99cc51278"
 | 
			
		||||
@@ -5995,34 +5724,6 @@ esbuild-sass-plugin@2.16.0:
 | 
			
		||||
    resolve "^1.22.6"
 | 
			
		||||
    sass "^1.7.3"
 | 
			
		||||
 | 
			
		||||
esbuild@0.17.19:
 | 
			
		||||
  version "0.17.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.19.tgz#087a727e98299f0462a3d0bcdd9cd7ff100bd955"
 | 
			
		||||
  integrity sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==
 | 
			
		||||
  optionalDependencies:
 | 
			
		||||
    "@esbuild/android-arm" "0.17.19"
 | 
			
		||||
    "@esbuild/android-arm64" "0.17.19"
 | 
			
		||||
    "@esbuild/android-x64" "0.17.19"
 | 
			
		||||
    "@esbuild/darwin-arm64" "0.17.19"
 | 
			
		||||
    "@esbuild/darwin-x64" "0.17.19"
 | 
			
		||||
    "@esbuild/freebsd-arm64" "0.17.19"
 | 
			
		||||
    "@esbuild/freebsd-x64" "0.17.19"
 | 
			
		||||
    "@esbuild/linux-arm" "0.17.19"
 | 
			
		||||
    "@esbuild/linux-arm64" "0.17.19"
 | 
			
		||||
    "@esbuild/linux-ia32" "0.17.19"
 | 
			
		||||
    "@esbuild/linux-loong64" "0.17.19"
 | 
			
		||||
    "@esbuild/linux-mips64el" "0.17.19"
 | 
			
		||||
    "@esbuild/linux-ppc64" "0.17.19"
 | 
			
		||||
    "@esbuild/linux-riscv64" "0.17.19"
 | 
			
		||||
    "@esbuild/linux-s390x" "0.17.19"
 | 
			
		||||
    "@esbuild/linux-x64" "0.17.19"
 | 
			
		||||
    "@esbuild/netbsd-x64" "0.17.19"
 | 
			
		||||
    "@esbuild/openbsd-x64" "0.17.19"
 | 
			
		||||
    "@esbuild/sunos-x64" "0.17.19"
 | 
			
		||||
    "@esbuild/win32-arm64" "0.17.19"
 | 
			
		||||
    "@esbuild/win32-ia32" "0.17.19"
 | 
			
		||||
    "@esbuild/win32-x64" "0.17.19"
 | 
			
		||||
 | 
			
		||||
esbuild@0.19.10:
 | 
			
		||||
  version "0.19.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.10.tgz#55e83e4a6b702e3498b9f872d84bfb4ebcb6d16e"
 | 
			
		||||
@@ -6420,11 +6121,6 @@ estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
 | 
			
		||||
  integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
 | 
			
		||||
 | 
			
		||||
estree-walker@^0.6.1:
 | 
			
		||||
  version "0.6.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362"
 | 
			
		||||
  integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==
 | 
			
		||||
 | 
			
		||||
estree-walker@^1.0.1:
 | 
			
		||||
  version "1.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700"
 | 
			
		||||
@@ -6447,11 +6143,6 @@ esutils@^2.0.2:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
 | 
			
		||||
  integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
 | 
			
		||||
 | 
			
		||||
event-lite@^0.1.1:
 | 
			
		||||
  version "0.1.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/event-lite/-/event-lite-0.1.3.tgz#3dfe01144e808ac46448f0c19b4ab68e403a901d"
 | 
			
		||||
  integrity sha512-8qz9nOz5VeD2z96elrEKD2U433+L3DWdUdDkOINLGOJvx1GsMBbMn0aCeu28y8/e85A6mCigBiFlYMnTBEGlSw==
 | 
			
		||||
 | 
			
		||||
eventemitter3@^4.0.0:
 | 
			
		||||
  version "4.0.7"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
 | 
			
		||||
@@ -6492,11 +6183,6 @@ execa@^8.0.1:
 | 
			
		||||
    signal-exit "^4.1.0"
 | 
			
		||||
    strip-final-newline "^3.0.0"
 | 
			
		||||
 | 
			
		||||
exit-hook@^2.2.1:
 | 
			
		||||
  version "2.2.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-2.2.1.tgz#007b2d92c6428eda2b76e7016a34351586934593"
 | 
			
		||||
  integrity sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==
 | 
			
		||||
 | 
			
		||||
expect@^29.0.0:
 | 
			
		||||
  version "29.7.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc"
 | 
			
		||||
@@ -6840,14 +6526,6 @@ get-own-enumerable-property-symbols@^3.0.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664"
 | 
			
		||||
  integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==
 | 
			
		||||
 | 
			
		||||
get-source@^2.0.12:
 | 
			
		||||
  version "2.0.12"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/get-source/-/get-source-2.0.12.tgz#0b47d57ea1e53ce0d3a69f4f3d277eb8047da944"
 | 
			
		||||
  integrity sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    data-uri-to-buffer "^2.0.0"
 | 
			
		||||
    source-map "^0.6.1"
 | 
			
		||||
 | 
			
		||||
get-stream@^5.1.0:
 | 
			
		||||
  version "5.2.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
 | 
			
		||||
@@ -7170,7 +6848,7 @@ idb@^7.0.1:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b"
 | 
			
		||||
  integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==
 | 
			
		||||
 | 
			
		||||
ieee754@^1.1.13, ieee754@^1.1.8:
 | 
			
		||||
ieee754@^1.1.13:
 | 
			
		||||
  version "1.2.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
 | 
			
		||||
  integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
 | 
			
		||||
@@ -7246,11 +6924,6 @@ inherits@2, inherits@^2.0.3, inherits@^2.0.4:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
 | 
			
		||||
  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
 | 
			
		||||
 | 
			
		||||
int64-buffer@^0.1.9:
 | 
			
		||||
  version "0.1.10"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/int64-buffer/-/int64-buffer-0.1.10.tgz#277b228a87d95ad777d07c13832022406a473423"
 | 
			
		||||
  integrity sha512-v7cSY1J8ydZ0GyjUHqF+1bshJ6cnEVLo9EnjB8p+4HDRPZc9N5jjmvUV7NvEsqQOKyH0pmIBFWXVQbiS0+OBbA==
 | 
			
		||||
 | 
			
		||||
internal-slot@^1.0.7:
 | 
			
		||||
  version "1.0.7"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802"
 | 
			
		||||
@@ -7513,11 +7186,6 @@ is-weakset@^2.0.3:
 | 
			
		||||
    call-bind "^1.0.7"
 | 
			
		||||
    get-intrinsic "^1.2.4"
 | 
			
		||||
 | 
			
		||||
isarray@^1.0.0:
 | 
			
		||||
  version "1.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
 | 
			
		||||
  integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
 | 
			
		||||
 | 
			
		||||
isarray@^2.0.5:
 | 
			
		||||
  version "2.0.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
 | 
			
		||||
@@ -7580,11 +7248,6 @@ iterator.prototype@^1.1.2:
 | 
			
		||||
    reflect.getprototypeof "^1.0.4"
 | 
			
		||||
    set-function-name "^2.0.1"
 | 
			
		||||
 | 
			
		||||
itty-time@^1.0.6:
 | 
			
		||||
  version "1.0.6"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/itty-time/-/itty-time-1.0.6.tgz#a6eeda619f19d2f4c480ceddd013b93acb05714d"
 | 
			
		||||
  integrity sha512-+P8IZaLLBtFv8hCkIjcymZOp4UJ+xW6bSlQsXGqrkmJh7vSiMFSlNne0mCYagEE0N7HDNR5jJBRxwN0oYv61Rw==
 | 
			
		||||
 | 
			
		||||
jackspeak@^3.1.2:
 | 
			
		||||
  version "3.4.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a"
 | 
			
		||||
@@ -7986,7 +7649,7 @@ lodash.camelcase@^4.3.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
 | 
			
		||||
  integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==
 | 
			
		||||
 | 
			
		||||
lodash.debounce@4.0.8, lodash.debounce@^4.0.8:
 | 
			
		||||
lodash.debounce@^4.0.8:
 | 
			
		||||
  version "4.0.8"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
 | 
			
		||||
  integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
 | 
			
		||||
@@ -8088,7 +7751,7 @@ lz-string@^1.5.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941"
 | 
			
		||||
  integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==
 | 
			
		||||
 | 
			
		||||
magic-string@^0.25.0, magic-string@^0.25.3, magic-string@^0.25.7:
 | 
			
		||||
magic-string@^0.25.0, magic-string@^0.25.7:
 | 
			
		||||
  version "0.25.9"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c"
 | 
			
		||||
  integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==
 | 
			
		||||
@@ -8412,11 +8075,6 @@ mime@^1.6.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
 | 
			
		||||
  integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
 | 
			
		||||
 | 
			
		||||
mime@^3.0.0:
 | 
			
		||||
  version "3.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7"
 | 
			
		||||
  integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==
 | 
			
		||||
 | 
			
		||||
mimic-fn@^2.1.0:
 | 
			
		||||
  version "2.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
 | 
			
		||||
@@ -8439,24 +8097,6 @@ mini-css-extract-plugin@2.6.1:
 | 
			
		||||
  dependencies:
 | 
			
		||||
    schema-utils "^4.0.0"
 | 
			
		||||
 | 
			
		||||
miniflare@3.20241106.1:
 | 
			
		||||
  version "3.20241106.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/miniflare/-/miniflare-3.20241106.1.tgz#2dea3b9bc789b35dde6694948385f3009057f9cd"
 | 
			
		||||
  integrity sha512-dM3RBlJE8rUFxnqlPCaFCq0E7qQqEQvKbYX7W/APGCK+rLcyLmEBzC4GQR/niXdNM/oV6gdg9AA50ghnn2ALuw==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@cspotcode/source-map-support" "0.8.1"
 | 
			
		||||
    acorn "^8.8.0"
 | 
			
		||||
    acorn-walk "^8.2.0"
 | 
			
		||||
    capnp-ts "^0.7.0"
 | 
			
		||||
    exit-hook "^2.2.1"
 | 
			
		||||
    glob-to-regexp "^0.4.1"
 | 
			
		||||
    stoppable "^1.1.0"
 | 
			
		||||
    undici "^5.28.4"
 | 
			
		||||
    workerd "1.20241106.1"
 | 
			
		||||
    ws "^8.18.0"
 | 
			
		||||
    youch "^3.2.2"
 | 
			
		||||
    zod "^3.22.3"
 | 
			
		||||
 | 
			
		||||
minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:
 | 
			
		||||
  version "3.1.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
 | 
			
		||||
@@ -8537,16 +8177,6 @@ ms@^2.1.1:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
 | 
			
		||||
  integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
 | 
			
		||||
 | 
			
		||||
msgpack-lite@0.1.26:
 | 
			
		||||
  version "0.1.26"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/msgpack-lite/-/msgpack-lite-0.1.26.tgz#dd3c50b26f059f25e7edee3644418358e2a9ad89"
 | 
			
		||||
  integrity sha512-SZ2IxeqZ1oRFGo0xFGbvBJWMp3yLIY9rlIJyxy8CGrwZn1f0ZK4r6jV/AM1r0FZMDUkWkglOk/eeKIL9g77Nxw==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    event-lite "^0.1.1"
 | 
			
		||||
    ieee754 "^1.1.8"
 | 
			
		||||
    int64-buffer "^0.1.9"
 | 
			
		||||
    isarray "^1.0.0"
 | 
			
		||||
 | 
			
		||||
multimath@^2.0.0:
 | 
			
		||||
  version "2.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/multimath/-/multimath-2.0.0.tgz#0d37acf67c328f30e3d8c6b0d3209e6082710302"
 | 
			
		||||
@@ -8555,11 +8185,6 @@ multimath@^2.0.0:
 | 
			
		||||
    glur "^1.1.2"
 | 
			
		||||
    object-assign "^4.1.1"
 | 
			
		||||
 | 
			
		||||
mustache@^4.2.0:
 | 
			
		||||
  version "4.2.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64"
 | 
			
		||||
  integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==
 | 
			
		||||
 | 
			
		||||
nanoid@3.3.3:
 | 
			
		||||
  version "3.3.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25"
 | 
			
		||||
@@ -8570,7 +8195,7 @@ nanoid@4.0.2:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-4.0.2.tgz#140b3c5003959adbebf521c170f282c5e7f9fb9e"
 | 
			
		||||
  integrity sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==
 | 
			
		||||
 | 
			
		||||
nanoid@^3.3.2, nanoid@^3.3.3, nanoid@^3.3.6, nanoid@^3.3.7:
 | 
			
		||||
nanoid@^3.3.2, nanoid@^3.3.6, nanoid@^3.3.7:
 | 
			
		||||
  version "3.3.7"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
 | 
			
		||||
  integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
 | 
			
		||||
@@ -8640,11 +8265,6 @@ node-fetch@2.6.7:
 | 
			
		||||
  dependencies:
 | 
			
		||||
    whatwg-url "^5.0.0"
 | 
			
		||||
 | 
			
		||||
node-forge@^1:
 | 
			
		||||
  version "1.3.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
 | 
			
		||||
  integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==
 | 
			
		||||
 | 
			
		||||
node-html-parser@^5.3.3:
 | 
			
		||||
  version "5.4.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-5.4.2.tgz#93e004038c17af80226c942336990a0eaed8136a"
 | 
			
		||||
@@ -8775,11 +8395,6 @@ object.values@^1.1.6, object.values@^1.1.7:
 | 
			
		||||
    define-properties "^1.2.1"
 | 
			
		||||
    es-object-atoms "^1.0.0"
 | 
			
		||||
 | 
			
		||||
ohash@^1.1.4:
 | 
			
		||||
  version "1.1.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/ohash/-/ohash-1.1.4.tgz#ae8d83014ab81157d2c285abf7792e2995fadd72"
 | 
			
		||||
  integrity sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g==
 | 
			
		||||
 | 
			
		||||
once@^1.3.0, once@^1.3.1, once@^1.4.0:
 | 
			
		||||
  version "1.4.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
 | 
			
		||||
@@ -8937,11 +8552,6 @@ path-scurry@^1.11.1:
 | 
			
		||||
    lru-cache "^10.2.0"
 | 
			
		||||
    minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
 | 
			
		||||
 | 
			
		||||
path-to-regexp@^6.3.0:
 | 
			
		||||
  version "6.3.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.3.0.tgz#2b6a26a337737a8e1416f9272ed0766b1c0389f4"
 | 
			
		||||
  integrity sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==
 | 
			
		||||
 | 
			
		||||
path-type@^4.0.0:
 | 
			
		||||
  version "4.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
 | 
			
		||||
@@ -9185,11 +8795,6 @@ pretty-format@^29.0.0, pretty-format@^29.7.0:
 | 
			
		||||
    ansi-styles "^5.0.0"
 | 
			
		||||
    react-is "^18.0.0"
 | 
			
		||||
 | 
			
		||||
printable-characters@^1.0.42:
 | 
			
		||||
  version "1.0.42"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/printable-characters/-/printable-characters-1.0.42.tgz#3f18e977a9bd8eb37fcc4ff5659d7be90868b3d8"
 | 
			
		||||
  integrity sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==
 | 
			
		||||
 | 
			
		||||
progress@2.0.3, progress@^2.0.0:
 | 
			
		||||
  version "2.0.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
 | 
			
		||||
@@ -9405,11 +9010,6 @@ readable-stream@^3.1.1, readable-stream@^3.4.0:
 | 
			
		||||
    string_decoder "^1.1.1"
 | 
			
		||||
    util-deprecate "^1.0.1"
 | 
			
		||||
 | 
			
		||||
readdirp@^4.0.1:
 | 
			
		||||
  version "4.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.0.2.tgz#388fccb8b75665da3abffe2d8f8ed59fe74c230a"
 | 
			
		||||
  integrity sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==
 | 
			
		||||
 | 
			
		||||
readdirp@~3.6.0:
 | 
			
		||||
  version "3.6.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
 | 
			
		||||
@@ -9434,11 +9034,6 @@ rechoir@^0.7.0:
 | 
			
		||||
  dependencies:
 | 
			
		||||
    resolve "^1.9.0"
 | 
			
		||||
 | 
			
		||||
reconnecting-websocket@4.4.0:
 | 
			
		||||
  version "4.4.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz#3b0e5b96ef119e78a03135865b8bb0af1b948783"
 | 
			
		||||
  integrity sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==
 | 
			
		||||
 | 
			
		||||
redent@^3.0.0:
 | 
			
		||||
  version "3.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f"
 | 
			
		||||
@@ -9560,12 +9155,7 @@ resolve-from@^5.0.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
 | 
			
		||||
  integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
 | 
			
		||||
 | 
			
		||||
resolve.exports@^2.0.2:
 | 
			
		||||
  version "2.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800"
 | 
			
		||||
  integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==
 | 
			
		||||
 | 
			
		||||
resolve@^1.14.2, resolve@^1.19.0, resolve@^1.22.1, resolve@^1.22.4, resolve@^1.22.6, resolve@^1.22.8, resolve@^1.9.0:
 | 
			
		||||
resolve@^1.14.2, resolve@^1.19.0, resolve@^1.22.1, resolve@^1.22.4, resolve@^1.22.6, resolve@^1.9.0:
 | 
			
		||||
  version "1.22.8"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
 | 
			
		||||
  integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
 | 
			
		||||
@@ -9620,29 +9210,6 @@ robust-predicates@^3.0.2:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.2.tgz#d5b28528c4824d20fc48df1928d41d9efa1ad771"
 | 
			
		||||
  integrity sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==
 | 
			
		||||
 | 
			
		||||
rollup-plugin-inject@^3.0.0:
 | 
			
		||||
  version "3.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz#e4233855bfba6c0c12a312fd6649dff9a13ee9f4"
 | 
			
		||||
  integrity sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    estree-walker "^0.6.1"
 | 
			
		||||
    magic-string "^0.25.3"
 | 
			
		||||
    rollup-pluginutils "^2.8.1"
 | 
			
		||||
 | 
			
		||||
rollup-plugin-node-polyfills@^0.2.1:
 | 
			
		||||
  version "0.2.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/rollup-plugin-node-polyfills/-/rollup-plugin-node-polyfills-0.2.1.tgz#53092a2744837164d5b8a28812ba5f3ff61109fd"
 | 
			
		||||
  integrity sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    rollup-plugin-inject "^3.0.0"
 | 
			
		||||
 | 
			
		||||
rollup-pluginutils@^2.8.1:
 | 
			
		||||
  version "2.8.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e"
 | 
			
		||||
  integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    estree-walker "^0.6.1"
 | 
			
		||||
 | 
			
		||||
rollup@^2.43.1:
 | 
			
		||||
  version "2.79.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7"
 | 
			
		||||
@@ -9828,14 +9395,6 @@ secure-compare@3.0.1:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/secure-compare/-/secure-compare-3.0.1.tgz#f1a0329b308b221fae37b9974f3d578d0ca999e3"
 | 
			
		||||
  integrity sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==
 | 
			
		||||
 | 
			
		||||
selfsigned@^2.0.1:
 | 
			
		||||
  version "2.4.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0"
 | 
			
		||||
  integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@types/node-forge" "^1.3.0"
 | 
			
		||||
    node-forge "^1"
 | 
			
		||||
 | 
			
		||||
semver@7.5.4:
 | 
			
		||||
  version "7.5.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
 | 
			
		||||
@@ -10088,24 +9647,11 @@ stackback@0.0.2:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b"
 | 
			
		||||
  integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==
 | 
			
		||||
 | 
			
		||||
stacktracey@^2.1.8:
 | 
			
		||||
  version "2.1.8"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/stacktracey/-/stacktracey-2.1.8.tgz#bf9916020738ce3700d1323b32bd2c91ea71199d"
 | 
			
		||||
  integrity sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    as-table "^1.0.36"
 | 
			
		||||
    get-source "^2.0.12"
 | 
			
		||||
 | 
			
		||||
std-env@^3.7.0:
 | 
			
		||||
  version "3.7.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2"
 | 
			
		||||
  integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==
 | 
			
		||||
 | 
			
		||||
stoppable@^1.1.0:
 | 
			
		||||
  version "1.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/stoppable/-/stoppable-1.1.0.tgz#32da568e83ea488b08e4d7ea2c3bcc9d75015d5b"
 | 
			
		||||
  integrity sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==
 | 
			
		||||
 | 
			
		||||
streamsearch@^1.1.0:
 | 
			
		||||
  version "1.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
 | 
			
		||||
@@ -10543,11 +10089,6 @@ tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
 | 
			
		||||
  integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
 | 
			
		||||
 | 
			
		||||
tslib@^2.2.0:
 | 
			
		||||
  version "2.8.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
 | 
			
		||||
  integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
 | 
			
		||||
 | 
			
		||||
tsutils@^3.21.0:
 | 
			
		||||
  version "3.21.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
 | 
			
		||||
@@ -10657,11 +10198,6 @@ typeson@^6.0.0, typeson@^6.1.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/typeson/-/typeson-6.1.0.tgz#5b2a53705a5f58ff4d6f82f965917cabd0d7448b"
 | 
			
		||||
  integrity sha512-6FTtyGr8ldU0pfbvW/eOZrEtEkczHRUtduBnA90Jh9kMPCiFNnXIon3vF41N0S4tV1HHQt4Hk1j4srpESziCaA==
 | 
			
		||||
 | 
			
		||||
ufo@^1.5.4:
 | 
			
		||||
  version "1.5.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.5.4.tgz#16d6949674ca0c9e0fbbae1fa20a71d7b1ded754"
 | 
			
		||||
  integrity sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==
 | 
			
		||||
 | 
			
		||||
unbox-primitive@^1.0.2:
 | 
			
		||||
  version "1.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
 | 
			
		||||
@@ -10685,23 +10221,6 @@ undici-types@~5.26.4:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
 | 
			
		||||
  integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
 | 
			
		||||
 | 
			
		||||
undici@^5.28.4:
 | 
			
		||||
  version "5.28.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068"
 | 
			
		||||
  integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@fastify/busboy" "^2.0.0"
 | 
			
		||||
 | 
			
		||||
"unenv@npm:unenv-nightly@2.0.0-20241111-080453-894aa31":
 | 
			
		||||
  version "2.0.0-20241111-080453-894aa31"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/unenv-nightly/-/unenv-nightly-2.0.0-20241111-080453-894aa31.tgz#a781d32583a6335a7c76bda694669aed373a61d8"
 | 
			
		||||
  integrity sha512-0W39QQOQ9VE8kVVUpGwEG+pZcsCXk5wqNG6rDPE6Gr+fiA69LR0qERM61hW5KCOkC1/ArCFrfCGjwHyyv/bI0Q==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    defu "^6.1.4"
 | 
			
		||||
    ohash "^1.1.4"
 | 
			
		||||
    pathe "^1.1.2"
 | 
			
		||||
    ufo "^1.5.4"
 | 
			
		||||
 | 
			
		||||
unicode-canonical-property-names-ecmascript@^2.0.0:
 | 
			
		||||
  version "2.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"
 | 
			
		||||
@@ -11490,44 +11009,6 @@ workbox-window@7.1.0, workbox-window@^7.0.0:
 | 
			
		||||
    "@types/trusted-types" "^2.0.2"
 | 
			
		||||
    workbox-core "7.1.0"
 | 
			
		||||
 | 
			
		||||
workerd@1.20241106.1:
 | 
			
		||||
  version "1.20241106.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/workerd/-/workerd-1.20241106.1.tgz#08f3d63f70cd750a1f2c0652cd76c4844fe06409"
 | 
			
		||||
  integrity sha512-1GdKl0kDw8rrirr/ThcK66Kbl4/jd4h8uHx5g7YHBrnenY5SX1UPuop2cnCzYUxlg55kPjzIqqYslz1muRFgFw==
 | 
			
		||||
  optionalDependencies:
 | 
			
		||||
    "@cloudflare/workerd-darwin-64" "1.20241106.1"
 | 
			
		||||
    "@cloudflare/workerd-darwin-arm64" "1.20241106.1"
 | 
			
		||||
    "@cloudflare/workerd-linux-64" "1.20241106.1"
 | 
			
		||||
    "@cloudflare/workerd-linux-arm64" "1.20241106.1"
 | 
			
		||||
    "@cloudflare/workerd-windows-64" "1.20241106.1"
 | 
			
		||||
 | 
			
		||||
wrangler@^3.60.3:
 | 
			
		||||
  version "3.89.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/wrangler/-/wrangler-3.89.0.tgz#d4a66d98545902b1c1e6fa8fa02efd10657f010d"
 | 
			
		||||
  integrity sha512-ix3Rir/cu9Cn6FklvPDIW1QwOMcRU8iPj3IrkBWGdB66K9z1uqyqoTP64UZZyXrBItfrU7SbQT4L5wJ5y10TPA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@cloudflare/kv-asset-handler" "0.3.4"
 | 
			
		||||
    "@cloudflare/workers-shared" "0.7.1"
 | 
			
		||||
    "@esbuild-plugins/node-globals-polyfill" "^0.2.3"
 | 
			
		||||
    "@esbuild-plugins/node-modules-polyfill" "^0.2.2"
 | 
			
		||||
    blake3-wasm "^2.1.5"
 | 
			
		||||
    chokidar "^4.0.1"
 | 
			
		||||
    date-fns "^4.1.0"
 | 
			
		||||
    esbuild "0.17.19"
 | 
			
		||||
    itty-time "^1.0.6"
 | 
			
		||||
    miniflare "3.20241106.1"
 | 
			
		||||
    nanoid "^3.3.3"
 | 
			
		||||
    path-to-regexp "^6.3.0"
 | 
			
		||||
    resolve "^1.22.8"
 | 
			
		||||
    resolve.exports "^2.0.2"
 | 
			
		||||
    selfsigned "^2.0.1"
 | 
			
		||||
    source-map "^0.6.1"
 | 
			
		||||
    unenv "npm:unenv-nightly@2.0.0-20241111-080453-894aa31"
 | 
			
		||||
    workerd "1.20241106.1"
 | 
			
		||||
    xxhash-wasm "^1.0.1"
 | 
			
		||||
  optionalDependencies:
 | 
			
		||||
    fsevents "~2.3.2"
 | 
			
		||||
 | 
			
		||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
 | 
			
		||||
  version "7.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
 | 
			
		||||
@@ -11584,11 +11065,6 @@ ws@^8.13.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.0.tgz#d145d18eca2ed25aaf791a183903f7be5e295fea"
 | 
			
		||||
  integrity sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==
 | 
			
		||||
 | 
			
		||||
ws@^8.18.0:
 | 
			
		||||
  version "8.18.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"
 | 
			
		||||
  integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==
 | 
			
		||||
 | 
			
		||||
ws@~8.11.0:
 | 
			
		||||
  version "8.11.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143"
 | 
			
		||||
@@ -11614,11 +11090,6 @@ xmlhttprequest@1.8.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc"
 | 
			
		||||
  integrity sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==
 | 
			
		||||
 | 
			
		||||
xxhash-wasm@^1.0.1:
 | 
			
		||||
  version "1.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz#ffe7f0b98220a4afac171e3fb9b6d1f8771f015e"
 | 
			
		||||
  integrity sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==
 | 
			
		||||
 | 
			
		||||
y18n@^5.0.5:
 | 
			
		||||
  version "5.0.8"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
 | 
			
		||||
@@ -11665,20 +11136,6 @@ yauzl@^2.10.0:
 | 
			
		||||
    buffer-crc32 "~0.2.3"
 | 
			
		||||
    fd-slicer "~1.1.0"
 | 
			
		||||
 | 
			
		||||
youch@^3.2.2:
 | 
			
		||||
  version "3.3.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/youch/-/youch-3.3.4.tgz#f13ee0966846c6200e7fb9ece89306d95df5e489"
 | 
			
		||||
  integrity sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    cookie "^0.7.1"
 | 
			
		||||
    mustache "^4.2.0"
 | 
			
		||||
    stacktracey "^2.1.8"
 | 
			
		||||
 | 
			
		||||
zod@^3.22.3:
 | 
			
		||||
  version "3.23.8"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
 | 
			
		||||
  integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==
 | 
			
		||||
 | 
			
		||||
zustand@^4.3.2:
 | 
			
		||||
  version "4.5.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.2.tgz#fddbe7cac1e71d45413b3682cdb47b48034c3848"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user