From d7002057d7165ab980d378154cfe6083d7515669 Mon Sep 17 00:00:00 2001 From: Steve Ruiz Date: Tue, 16 Jan 2024 14:38:05 +0000 Subject: [PATCH] unbrivate, dot com in (#2475) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR moves the tldraw.com app into the public repo. ### Change Type - [x] `internal` — Any other changes that don't affect the published package[^2] --------- Co-authored-by: Dan Groshev Co-authored-by: alex --- .dockerignore | 75 + .eslintignore | 5 +- .eslintrc.js | 25 +- .github/actions/setup/action.yml | 19 + .github/workflows/checks.yml | 34 +- .github/workflows/deploy.yml | 76 + .../workflows/playwright-update-snapshots.yml | 1 - .github/workflows/playwright.yml | 1 - .github/workflows/prune-preview-deploys.yml | 36 + .github/workflows/publish-canary.yml | 1 - .github/workflows/publish-manual.yml | 1 - .github/workflows/publish-new.yml | 1 - .../workflows/trigger-production-build.yml | 111 + .gitignore | 10 +- .husky/pre-commit | 7 - .prettierignore | 9 +- .yarnrc.yml | 1 - apps/dotcom-asset-upload/.gitignore | 1 + apps/dotcom-asset-upload/CHANGELOG.md | 85 + apps/dotcom-asset-upload/package.json | 37 + apps/dotcom-asset-upload/src/index.ts | 180 + apps/dotcom-asset-upload/src/types.ts | 6 + apps/dotcom-asset-upload/tsconfig.json | 9 + apps/dotcom-asset-upload/wrangler.toml | 51 + apps/dotcom-bookmark-extractor/.gitignore | 1 + apps/dotcom-bookmark-extractor/README.md | 3 + apps/dotcom-bookmark-extractor/api/_cors.ts | 35 + .../dotcom-bookmark-extractor/api/bookmark.ts | 25 + apps/dotcom-bookmark-extractor/package.json | 24 + apps/dotcom-bookmark-extractor/tsconfig.json | 34 + apps/dotcom-worker/.gitignore | 2 + apps/dotcom-worker/CHANGELOG.md | 146 + apps/dotcom-worker/README.md | 12 + apps/dotcom-worker/package.json | 53 + apps/dotcom-worker/scripts/cron.ts | 10 + apps/dotcom-worker/scripts/dev-wrap.ts | 73 + apps/dotcom-worker/scripts/report-size.js | 45 + .../src/lib/AlarmScheduler.test.ts | 259 + apps/dotcom-worker/src/lib/AlarmScheduler.ts | 115 + .../src/lib/TLDrawDurableObject.ts | 398 ++ apps/dotcom-worker/src/lib/config.ts | 5 + apps/dotcom-worker/src/lib/r2.ts | 3 + .../src/lib/routes/createRoom.ts | 45 + .../src/lib/routes/createRoomSnapshot.ts | 47 + .../src/lib/routes/forwardRoomRequest.ts | 16 + .../src/lib/routes/getRoomHistory.ts | 39 + .../src/lib/routes/getRoomHistorySnapshot.ts | 30 + .../src/lib/routes/getRoomSnapshot.ts | 36 + .../src/lib/routes/joinExistingRoom.ts | 20 + apps/dotcom-worker/src/lib/types.ts | 29 + .../src/lib/utils/createSupabaseClient.ts | 12 + .../dotcom-worker/src/lib/utils/fourOhFour.ts | 5 + .../src/lib/utils/getSnapshotsTable.ts | 10 + .../src/lib/utils/roomIdIsTooLong.ts | 9 + apps/dotcom-worker/src/lib/utils/throttle.ts | 19 + .../src/lib/utils/validateSnapshot.ts | 50 + apps/dotcom-worker/src/lib/worker.ts | 103 + apps/dotcom-worker/tsconfig.json | 16 + apps/dotcom-worker/wrangler.toml | 116 + apps/dotcom/.gitignore | 42 + apps/dotcom/CHANGELOG.md | 221 + apps/dotcom/README.md | 80 + apps/dotcom/decs.d.ts | 17 + apps/dotcom/index.html | 49 + apps/dotcom/package.json | 64 + apps/dotcom/public/404-Sad-tldraw.svg | 5 + .../public/Shantell_Sans-Tldrawish.woff2 | Bin 0 -> 152980 bytes apps/dotcom/public/android-chrome-192x192.png | Bin 0 -> 2362 bytes apps/dotcom/public/android-chrome-512x512.png | Bin 0 -> 6676 bytes .../android-chrome-maskable-192x192.png | Bin 0 -> 1852 bytes .../android-chrome-maskable-512x512.png | Bin 0 -> 5363 bytes .../android-chrome-maskable-beta-512x512.png | Bin 0 -> 66436 bytes apps/dotcom/public/apple-touch-icon.png | Bin 0 -> 9408 bytes apps/dotcom/public/favicon-16x16.png | Bin 0 -> 281 bytes apps/dotcom/public/favicon-32x32.png | Bin 0 -> 425 bytes apps/dotcom/public/favicon.ico | Bin 0 -> 15406 bytes apps/dotcom/public/favicon.svg | 11 + apps/dotcom/public/flat.png | Bin 0 -> 6621 bytes apps/dotcom/public/github-hero-dark.png | Bin 0 -> 224488 bytes apps/dotcom/public/github-hero-light.png | Bin 0 -> 224246 bytes apps/dotcom/public/robots.txt | 11 + apps/dotcom/public/site.webmanifest | 11 + apps/dotcom/public/social-image.png | Bin 0 -> 9546 bytes apps/dotcom/public/social-og.png | Bin 0 -> 10903 bytes apps/dotcom/public/social-twitter.png | Bin 0 -> 9546 bytes apps/dotcom/public/staging-favicon-16.png | Bin 0 -> 311 bytes apps/dotcom/public/staging-favicon-32.png | Bin 0 -> 541 bytes apps/dotcom/public/staging-favicon.svg | 12 + apps/dotcom/public/tldraw-white-on-black.svg | 15 + apps/dotcom/public/tldraw.svg | 11 + apps/dotcom/scripts/build.ts | 59 + apps/dotcom/scripts/dev-app.ts | 41 + apps/dotcom/scripts/vercel-output-config.d.ts | 111 + apps/dotcom/sentry-release-name.ts | 3 + apps/dotcom/sentry.client.config.ts | 57 + apps/dotcom/sentry.properties | 4 + apps/dotcom/setupTests.js | 4 + .../BoardHistoryLog/BoardHistoryLog.tsx | 43 + .../BoardHistorySnapshot.tsx | 77 + .../src/components/CursorChatBubble.tsx | 207 + .../DefaultErrorFallback.tsx | 13 + .../components/EmbeddedInIFrameWarning.tsx | 85 + .../src/components/ErrorPage/ErrorPage.tsx | 28 + apps/dotcom/src/components/ExportMenu.tsx | 98 + apps/dotcom/src/components/Head/Head.tsx | 27 + apps/dotcom/src/components/LocalEditor.tsx | 68 + .../src/components/MultiplayerEditor.tsx | 130 + .../src/components/PeopleMenu/PeopleMenu.tsx | 83 + .../PeopleMenu/PeopleMenuAvatar.tsx | 18 + .../components/PeopleMenu/PeopleMenuItem.tsx | 63 + .../components/PeopleMenu/PeopleMenuMore.tsx | 3 + .../PeopleMenu/UserPresenceColorPicker.tsx | 131 + .../PeopleMenu/UserPresenceEditor.tsx | 75 + apps/dotcom/src/components/ShareMenu.tsx | 243 + .../dotcom/src/components/SnapshotsEditor.tsx | 53 + .../src/components/SneakyOnDropOverride.ts | 32 + .../src/components/StoreErrorScreen.tsx | 39 + .../components/ThemeUpdater/ThemeUpdater.tsx | 12 + apps/dotcom/src/hooks/useLocalStore.ts | 34 + apps/dotcom/src/hooks/useMultiplayerAssets.ts | 67 + apps/dotcom/src/hooks/usePreviousRoute.tsx | 30 + apps/dotcom/src/hooks/useRemoteSyncClient.ts | 115 + apps/dotcom/src/hooks/useShareMenuOpen.ts | 28 + apps/dotcom/src/hooks/useUrl.ts | 7 + apps/dotcom/src/hooks/useUrlState.ts | 91 + apps/dotcom/src/main.tsx | 82 + apps/dotcom/src/pages/history-snapshot.tsx | 29 + apps/dotcom/src/pages/history.tsx | 24 + apps/dotcom/src/pages/not-found.tsx | 14 + apps/dotcom/src/pages/public-multiplayer.tsx | 8 + apps/dotcom/src/pages/public-readonly.tsx | 8 + apps/dotcom/src/pages/public-snapshot.tsx | 23 + apps/dotcom/src/pages/root.tsx | 6 + apps/dotcom/src/utils/assetUrls.ts | 39 + apps/dotcom/src/utils/cloneAssetForShare.ts | 28 + apps/dotcom/src/utils/config.ts | 17 + apps/dotcom/src/utils/createAssetFromFile.ts | 62 + apps/dotcom/src/utils/createAssetFromUrl.ts | 78 + apps/dotcom/src/utils/defineLoader.tsx | 26 + apps/dotcom/src/utils/env.ts | 35 + apps/dotcom/src/utils/errorReporting.ts | 11 + apps/dotcom/src/utils/links.ts | 56 + .../src/utils/migration/DebugMenuItems.tsx | 30 + .../src/utils/migration/LocalMigration.tsx | 37 + .../utils/migration/MigrationAnnouncement.tsx | 124 + apps/dotcom/src/utils/migration/migration.tsx | 118 + .../utils/migration/writeV1ContentsToIdb.tsx | 18 + apps/dotcom/src/utils/qrcode.ts | 9 + .../ClientWebSocketAdapter.test.ts | 184 + .../remote-sync/ClientWebSocketAdapter.ts | 235 + .../src/utils/remote-sync/remote-sync.ts | 19 + .../src/utils/scratch-persistence-key.ts | 17 + apps/dotcom/src/utils/sharing.ts | 273 ++ apps/dotcom/src/utils/shouldClearDocument.tsx | 76 + .../src/utils/shouldLeaveSharedProject.tsx | 82 + .../src/utils/shouldOverrideDocument.tsx | 76 + apps/dotcom/src/utils/trackAnalyticsEvent.ts | 5 + apps/dotcom/src/utils/useCursorChat.ts | 63 + apps/dotcom/src/utils/useFileSystem.tsx | 155 + apps/dotcom/src/utils/useHandleUiEvent.tsx | 7 + apps/dotcom/src/utils/userPreferences.ts | 59 + apps/dotcom/styles/core.css | 200 + apps/dotcom/styles/globals.css | 14 + apps/dotcom/styles/z-board.css | 304 ++ apps/dotcom/tsconfig.json | 32 + apps/dotcom/vite.config.ts | 116 + apps/huppy/.gitignore | 42 + apps/huppy/Dockerfile | 40 + apps/huppy/README.md | 56 + apps/huppy/fly.toml | 35 + apps/huppy/hacky.Dockerfile | 12 + apps/huppy/next.config.js | 25 + apps/huppy/package.json | 39 + apps/huppy/pages/_app.tsx | 13 + apps/huppy/pages/api/dev/getDelivery.ts | 17 + apps/huppy/pages/api/dev/redeliver.ts | 17 + apps/huppy/pages/api/dev/simulate.ts | 37 + apps/huppy/pages/api/github-event.ts | 44 + apps/huppy/pages/api/on-release.ts | 35 + apps/huppy/pages/deliveries.tsx | 164 + apps/huppy/src/Queue.ts | 15 + apps/huppy/src/comment.tsx | 48 + apps/huppy/src/config.tsx | 7 + apps/huppy/src/ctx.tsx | 8 + apps/huppy/src/flow.tsx | 54 + apps/huppy/src/flows/collectClaSignatures.tsx | 234 + apps/huppy/src/flows/enforcePrLabels.tsx | 119 + apps/huppy/src/flows/index.tsx | 5 + .../src/flows/standaloneExamplesBranch.tsx | 107 + apps/huppy/src/getCtxForOrg.tsx | 21 + apps/huppy/src/octokit.ts | 67 + apps/huppy/src/repo.ts | 143 + apps/huppy/src/reportError.tsx | 22 + apps/huppy/src/requestWrapper.tsx | 16 + apps/huppy/src/utils.ts | 50 + apps/huppy/tsconfig.json | 23 + config/eslint-preset-react.js | 2 +- config/eslint-preset.js | 2 +- lazy.config.ts | 56 +- package.json | 12 +- packages/state/LICENSE.md | 1 + packages/store/LICENSE.md | 1 + packages/tlsync/CHANGELOG.md | 168 + packages/tlsync/LICENSE.md | 1 + packages/tlsync/README.md | 1 + packages/tlsync/api-extractor.json | 4 + packages/tlsync/api-report.md | 296 ++ packages/tlsync/package.json | 68 + packages/tlsync/setupJest.js | 13 + packages/tlsync/src/index.ts | 35 + packages/tlsync/src/lib/RoomSession.ts | 36 + .../tlsync/src/lib/ServerSocketAdapter.ts | 20 + packages/tlsync/src/lib/TLServer.ts | 267 + packages/tlsync/src/lib/TLSyncClient.ts | 590 +++ packages/tlsync/src/lib/TLSyncRoom.ts | 955 ++++ packages/tlsync/src/lib/chunk.ts | 78 + packages/tlsync/src/lib/diff.ts | 260 + packages/tlsync/src/lib/interval.ts | 4 + packages/tlsync/src/lib/protocol.ts | 80 + .../src/lib/requestAnimationFrame.polyfill.ts | 7 + packages/tlsync/src/lib/schema.ts | 86 + packages/tlsync/src/lib/serializeMessage.ts | 22 + packages/tlsync/src/lib/server-types.ts | 12 + packages/tlsync/src/test/FuzzEditor.ts | 394 ++ packages/tlsync/src/test/RandomSource.ts | 45 + packages/tlsync/src/test/TLServer.test.ts | 165 + packages/tlsync/src/test/TLSyncRoom.test.ts | 156 + packages/tlsync/src/test/TestServer.ts | 24 + packages/tlsync/src/test/TestSocketPair.ts | 102 + packages/tlsync/src/test/chunk.test.ts | 161 + packages/tlsync/src/test/diff.test.ts | 189 + packages/tlsync/src/test/schema.test.ts | 9 + packages/tlsync/src/test/syncFuzz.test.ts | 290 ++ .../tlsync/src/test/upgradeDowngrade.test.ts | 839 ++++ packages/tlsync/src/test/validation.test.ts | 194 + packages/tlsync/tsconfig.json | 16 + scripts/check-scripts.ts | 3 +- scripts/clean.sh | 10 +- scripts/deploy.ts | 486 ++ scripts/lib/file.ts | 16 +- scripts/lib/makeEnv.ts | 18 + scripts/lib/nicelog.ts | 1 - scripts/lib/publishing.ts | 6 +- scripts/package.json | 7 +- scripts/prune-preview-deploys.ts | 84 + scripts/publish-new.ts | 4 +- scripts/refresh-assets.ts | 20 +- public-yarn.lock => yarn.lock | 4344 ++++++++++++++++- 248 files changed, 20084 insertions(+), 245 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/actions/setup/action.yml create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/prune-preview-deploys.yml create mode 100644 .github/workflows/trigger-production-build.yml create mode 100644 apps/dotcom-asset-upload/.gitignore create mode 100644 apps/dotcom-asset-upload/CHANGELOG.md create mode 100644 apps/dotcom-asset-upload/package.json create mode 100644 apps/dotcom-asset-upload/src/index.ts create mode 100644 apps/dotcom-asset-upload/src/types.ts create mode 100644 apps/dotcom-asset-upload/tsconfig.json create mode 100644 apps/dotcom-asset-upload/wrangler.toml create mode 100644 apps/dotcom-bookmark-extractor/.gitignore create mode 100644 apps/dotcom-bookmark-extractor/README.md create mode 100644 apps/dotcom-bookmark-extractor/api/_cors.ts create mode 100644 apps/dotcom-bookmark-extractor/api/bookmark.ts create mode 100644 apps/dotcom-bookmark-extractor/package.json create mode 100644 apps/dotcom-bookmark-extractor/tsconfig.json create mode 100644 apps/dotcom-worker/.gitignore create mode 100644 apps/dotcom-worker/CHANGELOG.md create mode 100644 apps/dotcom-worker/README.md create mode 100644 apps/dotcom-worker/package.json create mode 100644 apps/dotcom-worker/scripts/cron.ts create mode 100644 apps/dotcom-worker/scripts/dev-wrap.ts create mode 100644 apps/dotcom-worker/scripts/report-size.js create mode 100644 apps/dotcom-worker/src/lib/AlarmScheduler.test.ts create mode 100644 apps/dotcom-worker/src/lib/AlarmScheduler.ts create mode 100644 apps/dotcom-worker/src/lib/TLDrawDurableObject.ts create mode 100644 apps/dotcom-worker/src/lib/config.ts create mode 100644 apps/dotcom-worker/src/lib/r2.ts create mode 100644 apps/dotcom-worker/src/lib/routes/createRoom.ts create mode 100644 apps/dotcom-worker/src/lib/routes/createRoomSnapshot.ts create mode 100644 apps/dotcom-worker/src/lib/routes/forwardRoomRequest.ts create mode 100644 apps/dotcom-worker/src/lib/routes/getRoomHistory.ts create mode 100644 apps/dotcom-worker/src/lib/routes/getRoomHistorySnapshot.ts create mode 100644 apps/dotcom-worker/src/lib/routes/getRoomSnapshot.ts create mode 100644 apps/dotcom-worker/src/lib/routes/joinExistingRoom.ts create mode 100644 apps/dotcom-worker/src/lib/types.ts create mode 100644 apps/dotcom-worker/src/lib/utils/createSupabaseClient.ts create mode 100644 apps/dotcom-worker/src/lib/utils/fourOhFour.ts create mode 100644 apps/dotcom-worker/src/lib/utils/getSnapshotsTable.ts create mode 100644 apps/dotcom-worker/src/lib/utils/roomIdIsTooLong.ts create mode 100644 apps/dotcom-worker/src/lib/utils/throttle.ts create mode 100644 apps/dotcom-worker/src/lib/utils/validateSnapshot.ts create mode 100644 apps/dotcom-worker/src/lib/worker.ts create mode 100644 apps/dotcom-worker/tsconfig.json create mode 100644 apps/dotcom-worker/wrangler.toml create mode 100644 apps/dotcom/.gitignore create mode 100644 apps/dotcom/CHANGELOG.md create mode 100644 apps/dotcom/README.md create mode 100644 apps/dotcom/decs.d.ts create mode 100644 apps/dotcom/index.html create mode 100644 apps/dotcom/package.json create mode 100644 apps/dotcom/public/404-Sad-tldraw.svg create mode 100644 apps/dotcom/public/Shantell_Sans-Tldrawish.woff2 create mode 100644 apps/dotcom/public/android-chrome-192x192.png create mode 100644 apps/dotcom/public/android-chrome-512x512.png create mode 100644 apps/dotcom/public/android-chrome-maskable-192x192.png create mode 100644 apps/dotcom/public/android-chrome-maskable-512x512.png create mode 100644 apps/dotcom/public/android-chrome-maskable-beta-512x512.png create mode 100644 apps/dotcom/public/apple-touch-icon.png create mode 100644 apps/dotcom/public/favicon-16x16.png create mode 100644 apps/dotcom/public/favicon-32x32.png create mode 100644 apps/dotcom/public/favicon.ico create mode 100644 apps/dotcom/public/favicon.svg create mode 100644 apps/dotcom/public/flat.png create mode 100644 apps/dotcom/public/github-hero-dark.png create mode 100644 apps/dotcom/public/github-hero-light.png create mode 100644 apps/dotcom/public/robots.txt create mode 100644 apps/dotcom/public/site.webmanifest create mode 100644 apps/dotcom/public/social-image.png create mode 100644 apps/dotcom/public/social-og.png create mode 100644 apps/dotcom/public/social-twitter.png create mode 100644 apps/dotcom/public/staging-favicon-16.png create mode 100644 apps/dotcom/public/staging-favicon-32.png create mode 100644 apps/dotcom/public/staging-favicon.svg create mode 100644 apps/dotcom/public/tldraw-white-on-black.svg create mode 100644 apps/dotcom/public/tldraw.svg create mode 100644 apps/dotcom/scripts/build.ts create mode 100644 apps/dotcom/scripts/dev-app.ts create mode 100644 apps/dotcom/scripts/vercel-output-config.d.ts create mode 100644 apps/dotcom/sentry-release-name.ts create mode 100644 apps/dotcom/sentry.client.config.ts create mode 100644 apps/dotcom/sentry.properties create mode 100644 apps/dotcom/setupTests.js create mode 100644 apps/dotcom/src/components/BoardHistoryLog/BoardHistoryLog.tsx create mode 100644 apps/dotcom/src/components/BoardHistorySnapshot/BoardHistorySnapshot.tsx create mode 100644 apps/dotcom/src/components/CursorChatBubble.tsx create mode 100644 apps/dotcom/src/components/DefaultErrorFallback/DefaultErrorFallback.tsx create mode 100644 apps/dotcom/src/components/EmbeddedInIFrameWarning.tsx create mode 100644 apps/dotcom/src/components/ErrorPage/ErrorPage.tsx create mode 100644 apps/dotcom/src/components/ExportMenu.tsx create mode 100644 apps/dotcom/src/components/Head/Head.tsx create mode 100644 apps/dotcom/src/components/LocalEditor.tsx create mode 100644 apps/dotcom/src/components/MultiplayerEditor.tsx create mode 100644 apps/dotcom/src/components/PeopleMenu/PeopleMenu.tsx create mode 100644 apps/dotcom/src/components/PeopleMenu/PeopleMenuAvatar.tsx create mode 100644 apps/dotcom/src/components/PeopleMenu/PeopleMenuItem.tsx create mode 100644 apps/dotcom/src/components/PeopleMenu/PeopleMenuMore.tsx create mode 100644 apps/dotcom/src/components/PeopleMenu/UserPresenceColorPicker.tsx create mode 100644 apps/dotcom/src/components/PeopleMenu/UserPresenceEditor.tsx create mode 100644 apps/dotcom/src/components/ShareMenu.tsx create mode 100644 apps/dotcom/src/components/SnapshotsEditor.tsx create mode 100644 apps/dotcom/src/components/SneakyOnDropOverride.ts create mode 100644 apps/dotcom/src/components/StoreErrorScreen.tsx create mode 100644 apps/dotcom/src/components/ThemeUpdater/ThemeUpdater.tsx create mode 100644 apps/dotcom/src/hooks/useLocalStore.ts create mode 100644 apps/dotcom/src/hooks/useMultiplayerAssets.ts create mode 100644 apps/dotcom/src/hooks/usePreviousRoute.tsx create mode 100644 apps/dotcom/src/hooks/useRemoteSyncClient.ts create mode 100644 apps/dotcom/src/hooks/useShareMenuOpen.ts create mode 100644 apps/dotcom/src/hooks/useUrl.ts create mode 100644 apps/dotcom/src/hooks/useUrlState.ts create mode 100644 apps/dotcom/src/main.tsx create mode 100644 apps/dotcom/src/pages/history-snapshot.tsx create mode 100644 apps/dotcom/src/pages/history.tsx create mode 100644 apps/dotcom/src/pages/not-found.tsx create mode 100644 apps/dotcom/src/pages/public-multiplayer.tsx create mode 100644 apps/dotcom/src/pages/public-readonly.tsx create mode 100644 apps/dotcom/src/pages/public-snapshot.tsx create mode 100644 apps/dotcom/src/pages/root.tsx create mode 100644 apps/dotcom/src/utils/assetUrls.ts create mode 100644 apps/dotcom/src/utils/cloneAssetForShare.ts create mode 100644 apps/dotcom/src/utils/config.ts create mode 100644 apps/dotcom/src/utils/createAssetFromFile.ts create mode 100644 apps/dotcom/src/utils/createAssetFromUrl.ts create mode 100644 apps/dotcom/src/utils/defineLoader.tsx create mode 100644 apps/dotcom/src/utils/env.ts create mode 100644 apps/dotcom/src/utils/errorReporting.ts create mode 100644 apps/dotcom/src/utils/links.ts create mode 100644 apps/dotcom/src/utils/migration/DebugMenuItems.tsx create mode 100644 apps/dotcom/src/utils/migration/LocalMigration.tsx create mode 100644 apps/dotcom/src/utils/migration/MigrationAnnouncement.tsx create mode 100644 apps/dotcom/src/utils/migration/migration.tsx create mode 100644 apps/dotcom/src/utils/migration/writeV1ContentsToIdb.tsx create mode 100644 apps/dotcom/src/utils/qrcode.ts create mode 100644 apps/dotcom/src/utils/remote-sync/ClientWebSocketAdapter.test.ts create mode 100644 apps/dotcom/src/utils/remote-sync/ClientWebSocketAdapter.ts create mode 100644 apps/dotcom/src/utils/remote-sync/remote-sync.ts create mode 100644 apps/dotcom/src/utils/scratch-persistence-key.ts create mode 100644 apps/dotcom/src/utils/sharing.ts create mode 100644 apps/dotcom/src/utils/shouldClearDocument.tsx create mode 100644 apps/dotcom/src/utils/shouldLeaveSharedProject.tsx create mode 100644 apps/dotcom/src/utils/shouldOverrideDocument.tsx create mode 100644 apps/dotcom/src/utils/trackAnalyticsEvent.ts create mode 100644 apps/dotcom/src/utils/useCursorChat.ts create mode 100644 apps/dotcom/src/utils/useFileSystem.tsx create mode 100644 apps/dotcom/src/utils/useHandleUiEvent.tsx create mode 100644 apps/dotcom/src/utils/userPreferences.ts create mode 100644 apps/dotcom/styles/core.css create mode 100644 apps/dotcom/styles/globals.css create mode 100644 apps/dotcom/styles/z-board.css create mode 100644 apps/dotcom/tsconfig.json create mode 100644 apps/dotcom/vite.config.ts create mode 100644 apps/huppy/.gitignore create mode 100644 apps/huppy/Dockerfile create mode 100644 apps/huppy/README.md create mode 100644 apps/huppy/fly.toml create mode 100644 apps/huppy/hacky.Dockerfile create mode 100644 apps/huppy/next.config.js create mode 100644 apps/huppy/package.json create mode 100644 apps/huppy/pages/_app.tsx create mode 100644 apps/huppy/pages/api/dev/getDelivery.ts create mode 100644 apps/huppy/pages/api/dev/redeliver.ts create mode 100644 apps/huppy/pages/api/dev/simulate.ts create mode 100644 apps/huppy/pages/api/github-event.ts create mode 100644 apps/huppy/pages/api/on-release.ts create mode 100644 apps/huppy/pages/deliveries.tsx create mode 100644 apps/huppy/src/Queue.ts create mode 100644 apps/huppy/src/comment.tsx create mode 100644 apps/huppy/src/config.tsx create mode 100644 apps/huppy/src/ctx.tsx create mode 100644 apps/huppy/src/flow.tsx create mode 100644 apps/huppy/src/flows/collectClaSignatures.tsx create mode 100644 apps/huppy/src/flows/enforcePrLabels.tsx create mode 100644 apps/huppy/src/flows/index.tsx create mode 100644 apps/huppy/src/flows/standaloneExamplesBranch.tsx create mode 100644 apps/huppy/src/getCtxForOrg.tsx create mode 100644 apps/huppy/src/octokit.ts create mode 100644 apps/huppy/src/repo.ts create mode 100644 apps/huppy/src/reportError.tsx create mode 100644 apps/huppy/src/requestWrapper.tsx create mode 100644 apps/huppy/src/utils.ts create mode 100644 apps/huppy/tsconfig.json create mode 100644 packages/state/LICENSE.md create mode 100644 packages/store/LICENSE.md create mode 100644 packages/tlsync/CHANGELOG.md create mode 100644 packages/tlsync/LICENSE.md create mode 100644 packages/tlsync/README.md create mode 100644 packages/tlsync/api-extractor.json create mode 100644 packages/tlsync/api-report.md create mode 100644 packages/tlsync/package.json create mode 100644 packages/tlsync/setupJest.js create mode 100644 packages/tlsync/src/index.ts create mode 100644 packages/tlsync/src/lib/RoomSession.ts create mode 100644 packages/tlsync/src/lib/ServerSocketAdapter.ts create mode 100644 packages/tlsync/src/lib/TLServer.ts create mode 100644 packages/tlsync/src/lib/TLSyncClient.ts create mode 100644 packages/tlsync/src/lib/TLSyncRoom.ts create mode 100644 packages/tlsync/src/lib/chunk.ts create mode 100644 packages/tlsync/src/lib/diff.ts create mode 100644 packages/tlsync/src/lib/interval.ts create mode 100644 packages/tlsync/src/lib/protocol.ts create mode 100644 packages/tlsync/src/lib/requestAnimationFrame.polyfill.ts create mode 100644 packages/tlsync/src/lib/schema.ts create mode 100644 packages/tlsync/src/lib/serializeMessage.ts create mode 100644 packages/tlsync/src/lib/server-types.ts create mode 100644 packages/tlsync/src/test/FuzzEditor.ts create mode 100644 packages/tlsync/src/test/RandomSource.ts create mode 100644 packages/tlsync/src/test/TLServer.test.ts create mode 100644 packages/tlsync/src/test/TLSyncRoom.test.ts create mode 100644 packages/tlsync/src/test/TestServer.ts create mode 100644 packages/tlsync/src/test/TestSocketPair.ts create mode 100644 packages/tlsync/src/test/chunk.test.ts create mode 100644 packages/tlsync/src/test/diff.test.ts create mode 100644 packages/tlsync/src/test/schema.test.ts create mode 100644 packages/tlsync/src/test/syncFuzz.test.ts create mode 100644 packages/tlsync/src/test/upgradeDowngrade.test.ts create mode 100644 packages/tlsync/src/test/validation.test.ts create mode 100644 packages/tlsync/tsconfig.json create mode 100644 scripts/deploy.ts create mode 100644 scripts/lib/makeEnv.ts create mode 100644 scripts/prune-preview-deploys.ts rename public-yarn.lock => yarn.lock (84%) diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..979539967 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,75 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +**/node_modules + +.git +**/.git + +dist +dist-cjs +dist-esm +.tsbuild* +.lazy +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# turborepo +.turbo + +coverage + +**/*.env +**/*.tsbuildinfo + +**/*.css.map +**/*.js.map +apps/webdriver/www/index.js +apps/webdriver/www/index.css +apps/dotcom-worker/.dev.vars +nohup.out + +packages/*/package +packages/*/*.tgz + +tsconfig.build.json +.vercel + +api-json +api-md + +apps/webdriver/www +!apps/webdriver/www/index.html + +# yarn v2 +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions + +packages/*/api +apps/examples/www/index.css +apps/examples/www/index.js +.tsbuild +packages/dotcom-worker/.dev.vars diff --git a/.eslintignore b/.eslintignore index 2790821f8..ba793d8a1 100644 --- a/.eslintignore +++ b/.eslintignore @@ -22,4 +22,7 @@ apps/examples/www apps/docs/api-content.json apps/docs/content.json apps/vscode/extension/editor/index.js -apps/vscode/extension/editor/tldraw-assets.json \ No newline at end of file +apps/vscode/extension/editor/tldraw-assets.json +**/sentry.server.config.js +**/scripts/upload-sourcemaps.js +**/coverage/**/* diff --git a/.eslintrc.js b/.eslintrc.js index 72ecf8c17..97d940e71 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -90,11 +90,24 @@ module.exports = { 'import/no-internal-modules': 'off', }, }, - // { - // files: ['packages/tldraw/src/test/**/*'], - // rules: { - // 'import/no-internal-modules': 'off', - // }, - // }, + { + files: ['apps/huppy/**/*', 'scripts/**/*'], + rules: { + 'no-console': 'off', + }, + }, + { + files: ['apps/dotcom/**/*'], + rules: { + 'no-restricted-properties': [ + 2, + { + object: 'crypto', + property: 'randomUUID', + message: 'Please use the makeUUID util instead.', + }, + ], + }, + }, ], } diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml new file mode 100644 index 000000000..3c26214eb --- /dev/null +++ b/.github/actions/setup/action.yml @@ -0,0 +1,19 @@ +name: Setup tldraw/tldraw +description: Set up node & yarn + +runs: + using: composite + steps: + - name: Enable corepack + run: corepack enable + shell: bash + + - name: Setup Node.js Environment + uses: actions/setup-node@v3 + with: + node-version: 18.18.2 + cache: 'yarn' + + - name: Install dependencies + run: yarn install --immutable + shell: bash diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 7e010baa2..b74943f74 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -15,8 +15,8 @@ defaults: shell: bash jobs: - build: - name: 'Build and run checks' + test: + name: 'Tests & checks' timeout-minutes: 15 runs-on: ubuntu-latest-16-cores-open # TODO: this should probably run on multiple OSes @@ -24,18 +24,7 @@ jobs: - name: Check out code uses: actions/checkout@v3 - - name: Setup Node.js environment - uses: actions/setup-node@v3 - with: - node-version: 18.18.2 - cache: 'yarn' - cache-dependency-path: 'public-yarn.lock' - - - name: Enable corepack - run: corepack enable - - - name: Install dependencies - run: yarn + - uses: ./.github/actions/setup - name: Typecheck run: yarn build-types @@ -49,13 +38,24 @@ jobs: - name: Check API declarations and docs work as intended run: yarn api-check + - name: Test + run: yarn test + + build: + name: 'Build all projects' + timeout-minutes: 15 + runs-on: ubuntu-latest-16-cores-open + + steps: + - name: Check out code + uses: actions/checkout@v3 + + - uses: ./.github/actions/setup + - name: Build all projects # the sed pipe makes sure that github annotations come through without # turbo's prefix run: "yarn build | sed -E 's/^.*? ::/::/'" - - name: Test - run: yarn test - - name: Pack public packages run: "yarn lazy pack-tarball | sed -E 's/^.*? ::/::/'" diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000..1315b3aa3 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,76 @@ +name: Deploy + +on: + pull_request: + push: + branches: + - main + - production + +env: + CI: 1 + PRINT_GITHUB_ANNOTATIONS: 1 + TLDRAW_ENV: ${{ (github.event.ref == 'refs/heads/production' && 'production') || (github.event.ref == 'refs/heads/main' && 'staging') || 'preview' }} +defaults: + run: + shell: bash + +jobs: + deploy: + name: Deploy to ${{ (github.event.ref == 'refs/heads/production' && 'production') || (github.event.ref == 'refs/heads/main' && 'staging') || 'preview' }} + timeout-minutes: 15 + runs-on: ubuntu-latest-16-cores-open + environment: ${{ github.event.ref == 'refs/heads/production' && 'deploy-production' || 'deploy-staging' }} + concurrency: ${{ github.event.ref == 'refs/heads/production' && 'deploy-production' || github.event.ref }} + + steps: + - name: Notify initial start + uses: MineBartekSA/discord-webhook@v2 + if: github.event.ref == 'refs/heads/production' + with: + webhook: ${{ secrets.DISCORD_DEPLOY_WEBHOOK_URL }} + content: 'Preparing ${{ env.TLDRAW_ENV }} deploy: ${{ github.event.head_commit.message }} by ${{ github.event.head_commit.author.name }}' + component: | + { + "type": 2, + "style": 5, + "label": "Open in GitHub", + "url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + } + + - name: Check out code + uses: actions/checkout@v3 + with: + submodules: true + + - uses: ./.github/actions/setup + + - name: Build types + run: yarn build-types + + - name: Deploy + run: yarn tsx scripts/deploy.ts + env: + RELEASE_COMMIT_HASH: ${{ github.sha }} + GH_TOKEN: ${{ github.token }} + + ASSET_UPLOAD: ${{ vars.ASSET_UPLOAD }} + MULTIPLAYER_SERVER: ${{ vars.MULTIPLAYER_SERVER }} + SUPABASE_LITE_URL: ${{ vars.SUPABASE_LITE_URL }} + VERCEL_PROJECT_ID: ${{ vars.VERCEL_DOTCOM_PROJECT_ID }} + VERCEL_ORG_ID: ${{ vars.VERCEL_ORG_ID }} + + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + DISCORD_DEPLOY_WEBHOOK_URL: ${{ secrets.DISCORD_DEPLOY_WEBHOOK_URL }} + GC_MAPS_API_KEY: ${{ secrets.GC_MAPS_API_KEY }} + WORKER_SENTRY_DSN: ${{ secrets.WORKER_SENTRY_DSN }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_DSN: ${{ secrets.SENTRY_DSN }} + SUPABASE_LITE_ANON_KEY: ${{ secrets.SUPABASE_LITE_ANON_KEY }} + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + + R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} + R2_ACCESS_KEY_SECRET: ${{ secrets.R2_ACCESS_KEY_SECRET }} + + APP_ORIGIN: ${{ vars.APP_ORIGIN }} diff --git a/.github/workflows/playwright-update-snapshots.yml b/.github/workflows/playwright-update-snapshots.yml index 33535d911..b1c6fdeec 100644 --- a/.github/workflows/playwright-update-snapshots.yml +++ b/.github/workflows/playwright-update-snapshots.yml @@ -56,7 +56,6 @@ jobs: with: node-version: 18.18.2 cache: 'yarn' - cache-dependency-path: 'public-yarn.lock' - name: Enable corepack run: corepack enable diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index b589206d8..5f54906e4 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -29,7 +29,6 @@ jobs: with: node-version: 18.18.2 cache: 'yarn' - cache-dependency-path: 'public-yarn.lock' - name: Enable corepack run: corepack enable diff --git a/.github/workflows/prune-preview-deploys.yml b/.github/workflows/prune-preview-deploys.yml new file mode 100644 index 000000000..9d3b34d43 --- /dev/null +++ b/.github/workflows/prune-preview-deploys.yml @@ -0,0 +1,36 @@ +name: Prune preview deploys + +on: + schedule: + # run once per day at midnight or whatever + - cron: '0 0 * * *' + +env: + CI: 1 + PRINT_GITHUB_ANNOTATIONS: 1 +defaults: + run: + shell: bash + +jobs: + deploy: + name: Prune preview deploys + timeout-minutes: 15 + runs-on: ubuntu-latest-16-cores + environment: deploy-staging + + steps: + - name: Check out code + uses: actions/checkout@v3 + with: + submodules: true + fetch-depth: 0 + + - uses: ./.github/actions/setup + + - name: Prune preview deploys + run: yarn tsx scripts/prune-preview-deploys.ts + env: + GH_TOKEN: ${{ github.token }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} diff --git a/.github/workflows/publish-canary.yml b/.github/workflows/publish-canary.yml index ed11efec5..ae56dc0ae 100644 --- a/.github/workflows/publish-canary.yml +++ b/.github/workflows/publish-canary.yml @@ -22,7 +22,6 @@ jobs: with: node-version: 18.18.2 cache: 'yarn' - cache-dependency-path: 'public-yarn.lock' - name: Enable corepack run: corepack enable diff --git a/.github/workflows/publish-manual.yml b/.github/workflows/publish-manual.yml index bc13a8056..b3bd76530 100644 --- a/.github/workflows/publish-manual.yml +++ b/.github/workflows/publish-manual.yml @@ -21,7 +21,6 @@ jobs: with: node-version: 18.18.2 cache: 'yarn' - cache-dependency-path: 'public-yarn.lock' - name: Enable corepack run: corepack enable diff --git a/.github/workflows/publish-new.yml b/.github/workflows/publish-new.yml index 731c43bdd..d6f0ef81c 100644 --- a/.github/workflows/publish-new.yml +++ b/.github/workflows/publish-new.yml @@ -33,7 +33,6 @@ jobs: with: node-version: 18.18.2 cache: 'yarn' - cache-dependency-path: 'public-yarn.lock' - name: Enable corepack run: corepack enable diff --git a/.github/workflows/trigger-production-build.yml b/.github/workflows/trigger-production-build.yml new file mode 100644 index 000000000..29b203ad7 --- /dev/null +++ b/.github/workflows/trigger-production-build.yml @@ -0,0 +1,111 @@ +name: Trigger production build + +on: + push: + branches: + - hotfixes + workflow_dispatch: + inputs: + target: + description: 'Target ref to deploy' + required: true + default: 'main' + +defaults: + run: + shell: bash + +env: + TARGET: ${{ github.event.inputs.target }} + +jobs: + trigger: + name: ${{ github.event_name == 'workflow_dispatch' && 'Manual trigger' || 'Hotfix' }} + runs-on: ubuntu-latest-16-cores-open + concurrency: trigger-production + + steps: + - name: Generate a token + id: generate_token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ vars.HUPPY_APP_ID }} + private-key: ${{ secrets.HUPPY_PRIVATE_KEY }} + + - uses: actions/checkout@v3 + with: + token: ${{ steps.generate_token.outputs.token }} + ref: refs/heads/production + fetch-depth: 0 + + - name: Get target commit hash (manual dispatch) + if: github.event_name == 'workflow_dispatch' + run: | + set -x + + # if the target exists on its own use that + if git rev-parse "$TARGET" --quiet; then + target_hash=$(git rev-parse "$TARGET") + fi + # if not try prefixed with origin: + if [ -z "$target_hash" ]; then + target_hash=$(git rev-parse "origin/$TARGET") + fi + + echo "SHOULD_DEPLOY=true" >> $GITHUB_ENV + echo "TARGET_HASH=$target_hash" >> $GITHUB_ENV + + - name: Get target commit hash (hotfix) + if: github.event_name == 'push' + run: | + set -x + echo "TARGET_HASH=$GITHUB_SHA" >> $GITHUB_ENV + echo "TARGET=hotfix" >> $GITHUB_ENV + # is the hotfix sha already on production? + if git merge-base --is-ancestor "$GITHUB_SHA" production; then + echo "SHOULD_DEPLOY=false" >> $GITHUB_ENV + else + echo "SHOULD_DEPLOY=true" >> $GITHUB_ENV + fi + + - name: Author setup (manual dispatch) + if: github.event_name == 'workflow_dispatch' + run: | + set -x + git config --global user.name "${{ github.actor }}" + git config --global user.email 'huppy+${{ github.actor }}@tldraw.com' + + - name: Author setup (hotfix) + if: github.event_name == 'push' + run: | + set -x + commit_author_name=$(git log -1 --pretty=format:%cn "$TARGET_HASH") + commit_author_email=$(git log -1 --pretty=format:%ce "$TARGET_HASH") + git config --global user.name "$commit_author_name" + git config --global user.email "$commit_author_email" + + - name: Get target tree hash + run: | + set -x + tree_hash=$(git show --quiet --pretty=format:%T "$TARGET_HASH") + echo "TREE_HASH=$tree_hash" >> $GITHUB_ENV + + - name: Create commit & update production branch + run: | + set -eux + now=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + message="Deploy from $TARGET ($TARGET_HASH) at $now" + current_prod_hash=$(git rev-parse HEAD) + + commit=$(git commit-tree -m "$message" -p "$current_prod_hash" -p "$TARGET_HASH" "$TREE_HASH") + + git update-ref refs/heads/production "$commit" + git checkout production + + - name: Push commit to trigger deployment + if: env.SHOULD_DEPLOY == 'true' + run: | + set -x + git push origin production + # reset hotfixes to the latest production + git push origin production:hotfixes --force diff --git a/.gitignore b/.gitignore index 476753db4..2ad0eb54d 100644 --- a/.gitignore +++ b/.gitignore @@ -83,4 +83,12 @@ apps/examples/build.esbuild.json apps/examples/e2e/test-results apps/examples/playwright-report -docs/gen \ No newline at end of file +docs/gen + +.dev.vars +.env.local +.env.development.local +.env* + +.wrangler +/vercel.json \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit index d2fde70c3..cdcc9d17e 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,13 +1,6 @@ #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" -# if the folder we're in is called bublic, it means we're a submodule in the brivate repo. -# We need to grab .envrc to set up yarn correctly. -current_file="$(readlink -f "$0")" -if [[ $current_file == */bublic/.husky/pre-commit ]]; then - source "$(dirname -- "$0")/../../.envrc" -fi - npx lazy run build-api git add packages/*/api-report.md git add packages/*/api/api.json diff --git a/.prettierignore b/.prettierignore index f5e256403..2088ec33e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -15,4 +15,11 @@ apps/docs/content.json apps/vscode/extension/editor/* apps/examples/www content.json -apps/docs/utils/vector-db/index.json \ No newline at end of file +apps/docs/utils/vector-db/index.json +**/gen/**/*.md + +**/.vercel/* +**/.wrangler/* +**/.out/* +**/.temp/* +apps/dotcom/public/**/*.* \ No newline at end of file diff --git a/.yarnrc.yml b/.yarnrc.yml index d89fb3ac9..db4ecfa4d 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,4 +1,3 @@ enableInlineBuilds: true -lockfileFilename: public-yarn.lock nodeLinker: node-modules yarnPath: .yarn/releases/yarn-3.5.0.cjs diff --git a/apps/dotcom-asset-upload/.gitignore b/apps/dotcom-asset-upload/.gitignore new file mode 100644 index 000000000..6512d9d80 --- /dev/null +++ b/apps/dotcom-asset-upload/.gitignore @@ -0,0 +1 @@ +tmp-assets \ No newline at end of file diff --git a/apps/dotcom-asset-upload/CHANGELOG.md b/apps/dotcom-asset-upload/CHANGELOG.md new file mode 100644 index 000000000..dd2389843 --- /dev/null +++ b/apps/dotcom-asset-upload/CHANGELOG.md @@ -0,0 +1,85 @@ +# asset-upload + +## 2.0.0-alpha.8 + +### Patch Changes + +- Release day! + +## 2.0.0-alpha.7 + +### Patch Changes + +- Bug fixes. + +## 2.0.0-alpha.6 + +### Patch Changes + +- Add licenses. + +## 2.0.0-alpha.5 + +### Patch Changes + +- Add CSS files to tldraw/tldraw. + +## 2.0.0-alpha.4 + +### Patch Changes + +- Add children to tldraw/tldraw + +## 2.0.0-alpha.3 + +### Patch Changes + +- Change permissions. + +## 2.0.0-alpha.2 + +### Patch Changes + +- Add tldraw, editor + +## 0.1.0-alpha.11 + +### Patch Changes + +- Fix stale reactors. + +## 0.1.0-alpha.10 + +### Patch Changes + +- Fix type export bug. + +## 0.1.0-alpha.9 + +### Patch Changes + +- Fix import bugs. + +## 0.1.0-alpha.8 + +### Patch Changes + +- Changes validation requirements, exports validation helpers. + +## 0.1.0-alpha.7 + +### Patch Changes + +- - Pre-pre-release update + +## 0.0.2-alpha.1 + +### Patch Changes + +- Fix error with HMR + +## 0.0.2-alpha.0 + +### Patch Changes + +- Initial release diff --git a/apps/dotcom-asset-upload/package.json b/apps/dotcom-asset-upload/package.json new file mode 100644 index 000000000..b8d47d2a5 --- /dev/null +++ b/apps/dotcom-asset-upload/package.json @@ -0,0 +1,37 @@ +{ + "name": "dotcom-asset-upload", + "description": "A Cloudflare Worker to upload and serve images", + "version": "2.0.0-alpha.8", + "private": true, + "packageManager": "yarn@3.5.0", + "author": { + "name": "tldraw GB Ltd.", + "email": "hello@tldraw.com" + }, + "main": "src/index.ts", + "scripts": { + "dev": "cross-env NODE_ENV=development wrangler dev --log-level info --persist-to tmp-assets", + "test": "lazy inherit --passWithNoTests", + "test-coverage": "lazy inherit --passWithNoTests", + "lint": "yarn run -T tsx ../../scripts/lint.ts" + }, + "dependencies": { + "itty-cors": "^0.3.4", + "itty-router": "^2.6.6" + }, + "devDependencies": { + "@cloudflare/workers-types": "^4.20230821.0", + "@types/ws": "^8.5.3", + "lazyrepo": "0.0.0-alpha.27", + "wrangler": "3.16.0" + }, + "jest": { + "preset": "config/jest/node", + "moduleNameMapper": { + "^~(.*)": "/src/$1" + }, + "transformIgnorePatterns": [ + "node_modules/(?!(nanoid|escape-string-regexp)/)" + ] + } +} diff --git a/apps/dotcom-asset-upload/src/index.ts b/apps/dotcom-asset-upload/src/index.ts new file mode 100644 index 000000000..d0b5f9ef7 --- /dev/null +++ b/apps/dotcom-asset-upload/src/index.ts @@ -0,0 +1,180 @@ +/// +/// + +import { createCors } from 'itty-cors' +import { Router } from 'itty-router' + +const { preflight, corsify } = createCors({ origins: ['*'] }) + +interface Env { + UPLOADS: R2Bucket +} + +function parseRange( + encoded: string | null +): undefined | { offset: number; end: number; length: number } { + if (encoded === null) { + return + } + + const parts = (encoded.split('bytes=')[1]?.split('-') ?? []).filter(Boolean) + if (parts.length !== 2) { + console.error('Not supported to skip specifying the beginning/ending byte at this time') + return + } + + return { + offset: Number(parts[0]), + end: Number(parts[1]), + length: Number(parts[1]) + 1 - Number(parts[0]), + } +} + +function objectNotFound(objectName: string): Response { + return new Response(`R2 object "${objectName}" not found`, { + status: 404, + headers: { + 'content-type': 'text/html; charset=UTF-8', + }, + }) +} + +const router = Router() + +router + .all('*', preflight) + .get('/uploads/list', async (request, env: Env) => { + // we need to protect this behind auth + const url = new URL(request.url) + const options: R2ListOptions = { + prefix: url.searchParams.get('prefix') ?? undefined, + delimiter: url.searchParams.get('delimiter') ?? undefined, + cursor: url.searchParams.get('cursor') ?? undefined, + } + + const listing = await env.UPLOADS.list(options) + return Response.json(listing) + }) + .get('/uploads/:objectName', async (request: Request, env: Env, ctx: ExecutionContext) => { + const url = new URL(request.url) + + const range = parseRange(request.headers.get('range')) + + // NOTE: caching will only work when this is deployed to + // a custom domain, not a workers.dev domain. It's a no-op + // otherwise. + + // Construct the cache key from the cache URL + const cacheKey = new Request(url.toString(), request) + const cache = caches.default as Cache + + // Check whether the value is already available in the cache + // if not, you will need to fetch it from R2, and store it in the cache + // for future access + let cachedResponse + if (!range) { + cachedResponse = await cache.match(cacheKey) + + if (cachedResponse) { + return cachedResponse + } + } + + const ifNoneMatch = request.headers.get('if-none-match') + let hs = request.headers + if (ifNoneMatch?.startsWith('W/')) { + hs = new Headers(request.headers) + hs.set('if-none-match', ifNoneMatch.slice(2)) + } + + // TODO: infer types from path + // @ts-expect-error + const object = await env.UPLOADS.get(request.params.objectName, { + range, + onlyIf: hs, + }) + + if (object === null) { + // TODO: infer types from path + // @ts-expect-error + return objectNotFound(request.params.objectName) + } + + const headers = new Headers() + object.writeHttpMetadata(headers) + headers.set('etag', object.httpEtag) + if (range) { + headers.set('content-range', `bytes ${range.offset}-${range.end}/${object.size}`) + } + + // Cache API respects Cache-Control headers. Setting s-max-age to 7 days + // Any changes made to the response here will be reflected in the cached value + headers.append('Cache-Control', 's-maxage=604800') + + const hasBody = 'body' in object && object.body + const status = hasBody ? (range ? 206 : 200) : 304 + const response = new Response(hasBody ? object.body : undefined, { + headers, + status, + }) + + // Store the response in the cache for future access + if (!range) { + ctx.waitUntil(cache.put(cacheKey, response.clone())) + } + + return response + }) + .head('/uploads/:objectName', async (request: Request, env: Env) => { + // TODO: infer types from path + // @ts-expect-error + const object = await env.UPLOADS.head(request.params.objectName) + + if (object === null) { + // TODO: infer types from path + // @ts-expect-error + return objectNotFound(request.params.objectName) + } + + const headers = new Headers() + object.writeHttpMetadata(headers) + headers.set('etag', object.httpEtag) + return new Response(null, { + headers, + }) + }) + .post('/uploads/:objectName', async (request: Request, env: Env) => { + // TODO: infer types from path + // @ts-expect-error + const object = await env.UPLOADS.put(request.params.objectName, request.body, { + httpMetadata: request.headers, + }) + return new Response(null, { + headers: { + etag: object.httpEtag, + }, + }) + }) + .delete('/uploads/:objectName', async (request: Request, env: Env) => { + // Not sure if this is necessary, might be dangerous to expose + // TODO: infer types from path + // @ts-expect-error + await env.UPLOADS.delete(request.params.objectName) + return new Response() + }) + .get('*', () => new Response('Not found', { status: 404 })) + +const Worker = { + async fetch(request: Request, env: Env, ctx: ExecutionContext) { + return router + .handle(request, env, ctx) + .catch((err) => { + // eslint-disable-next-line no-console + console.log(err, err.stack) + return new Response((err as Error).message, { status: 500 }) + }) + .then(corsify) + }, +} + +export default Worker diff --git a/apps/dotcom-asset-upload/src/types.ts b/apps/dotcom-asset-upload/src/types.ts new file mode 100644 index 000000000..5131eacb8 --- /dev/null +++ b/apps/dotcom-asset-upload/src/types.ts @@ -0,0 +1,6 @@ +export interface Env { + UPLOADS: R2Bucket + + KV: KVNamespace + ASSET_UPLOADER_AUTH_TOKEN: string | undefined +} diff --git a/apps/dotcom-asset-upload/tsconfig.json b/apps/dotcom-asset-upload/tsconfig.json new file mode 100644 index 000000000..051903fbc --- /dev/null +++ b/apps/dotcom-asset-upload/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../config/tsconfig.base.json", + "include": ["src"], + "exclude": ["node_modules", "dist", ".tsbuild*"], + "compilerOptions": { + "noEmit": true, + "emitDeclarationOnly": false + } +} diff --git a/apps/dotcom-asset-upload/wrangler.toml b/apps/dotcom-asset-upload/wrangler.toml new file mode 100644 index 000000000..8679d676d --- /dev/null +++ b/apps/dotcom-asset-upload/wrangler.toml @@ -0,0 +1,51 @@ +name = "tldraw-assets" +main = "src/index.ts" +compatibility_date = "2022-09-22" + +[dev] +port = 8788 + +[[r2_buckets]] +binding = 'UPLOADS' +bucket_name = 'uploads' +preview_bucket_name = 'uploads-preview' + +[[analytics_engine_datasets]] +binding = "MEASURE" + +# staging settings +[env.staging] +name = "main-tldraw-assets" + +[[env.staging.r2_buckets]] +binding = 'UPLOADS' +bucket_name = 'uploads' +preview_bucket_name = 'uploads-preview' + +[[env.staging.unsafe.bindings]] +type = "analytics_engine" +name = "MEASURE" + + +# production settings +[env.production] +name = "tldraw-assets" + +[[env.production.routes]] +pattern = 'assets.tldraw.xyz' +custom_domain = true +zone_name = 'tldraw.xyz' + +[[env.production.r2_buckets]] +binding = 'UPLOADS' +bucket_name = 'uploads' +preview_bucket_name = 'uploads-preview' + +[[env.production.unsafe.bindings]] +type = "analytics_engine" +name = "MEASURE" + +[[env.preview.r2_buckets]] +binding = 'UPLOADS' +bucket_name = 'uploads' +preview_bucket_name = 'uploads-preview' \ No newline at end of file diff --git a/apps/dotcom-bookmark-extractor/.gitignore b/apps/dotcom-bookmark-extractor/.gitignore new file mode 100644 index 000000000..e985853ed --- /dev/null +++ b/apps/dotcom-bookmark-extractor/.gitignore @@ -0,0 +1 @@ +.vercel diff --git a/apps/dotcom-bookmark-extractor/README.md b/apps/dotcom-bookmark-extractor/README.md new file mode 100644 index 000000000..0ec89ed1a --- /dev/null +++ b/apps/dotcom-bookmark-extractor/README.md @@ -0,0 +1,3 @@ +# @tldraw/bookmark-extractor + +Deploy this manually with `vercel deploy --prod`. diff --git a/apps/dotcom-bookmark-extractor/api/_cors.ts b/apps/dotcom-bookmark-extractor/api/_cors.ts new file mode 100644 index 000000000..2b7cf6e46 --- /dev/null +++ b/apps/dotcom-bookmark-extractor/api/_cors.ts @@ -0,0 +1,35 @@ +import Cors from 'cors' + +const whitelist = [ + 'http://localhost:3000', + 'http://localhost:4000', + 'http://localhost:5420', + 'https://www.tldraw.com', + 'https://staging.tldraw.com', + process.env.NEXT_PUBLIC_VERCEL_URL, + 'vercel.app', +] + +export const cors = Cors({ + methods: ['POST'], + origin: function (origin, callback) { + if (origin?.endsWith('.tldraw.com')) { + callback(null, true) + } else if (origin?.endsWith('-tldraw.vercel.app')) { + callback(null, true) + } else if (origin && whitelist.includes(origin)) { + callback(null, true) + } else { + callback(new Error(`Not allowed by CORS (${origin})`)) + } + }, +}) + +export function runCorsMiddleware(req: any, res: any) { + return new Promise((resolve, reject) => { + cors(req, res, (result) => { + if (result instanceof Error) return reject(result) + return resolve(result) + }) + }) +} diff --git a/apps/dotcom-bookmark-extractor/api/bookmark.ts b/apps/dotcom-bookmark-extractor/api/bookmark.ts new file mode 100644 index 000000000..8c65cb3b5 --- /dev/null +++ b/apps/dotcom-bookmark-extractor/api/bookmark.ts @@ -0,0 +1,25 @@ +// @ts-expect-error +import grabity from 'grabity' +import { runCorsMiddleware } from './_cors' + +interface RequestBody { + url: string +} + +interface ResponseBody { + title?: string + description?: string + image?: string +} + +export default async function handler(req: any, res: any) { + try { + await runCorsMiddleware(req, res) + const { url } = typeof req.body === 'string' ? JSON.parse(req.body) : (req.body as RequestBody) + const it = await grabity.grabIt(url) + res.send(it) + } catch (error: any) { + console.error(error) + res.status(500).send(error.message) + } +} diff --git a/apps/dotcom-bookmark-extractor/package.json b/apps/dotcom-bookmark-extractor/package.json new file mode 100644 index 000000000..49167bb94 --- /dev/null +++ b/apps/dotcom-bookmark-extractor/package.json @@ -0,0 +1,24 @@ +{ + "name": "@tldraw/bookmark-extractor", + "description": "A tiny little drawing app (merge server).", + "version": "2.0.0-alpha.11", + "private": true, + "packageManager": "yarn@3.5.0", + "author": { + "name": "tldraw GB Ltd.", + "email": "hello@tldraw.com" + }, + "scripts": { + "lint": "yarn run -T tsx ../../scripts/lint.ts" + }, + "dependencies": { + "@types/cors": "^2.8.15", + "cors": "^2.8.5", + "grabity": "^1.0.5", + "tslib": "^2.6.2" + }, + "devDependencies": { + "lazyrepo": "0.0.0-alpha.27", + "typescript": "^5.0.2" + } +} diff --git a/apps/dotcom-bookmark-extractor/tsconfig.json b/apps/dotcom-bookmark-extractor/tsconfig.json new file mode 100644 index 000000000..97c028379 --- /dev/null +++ b/apps/dotcom-bookmark-extractor/tsconfig.json @@ -0,0 +1,34 @@ +{ + "include": ["api"], + "exclude": ["node_modules", "dist", ".tsbuild*", ".vercel"], + "compilerOptions": { + "composite": true, + "declaration": true, + "declarationMap": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "importHelpers": true, + "resolveJsonModule": true, + "incremental": true, + "jsx": "react-jsx", + "lib": ["dom", "DOM.Iterable", "esnext"], + "experimentalDecorators": true, + "module": "CommonJS", + "target": "esnext", + "moduleResolution": "node", + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "skipLibCheck": true, + "strict": true, + "strictFunctionTypes": true, + "strictNullChecks": true, + "useDefineForClassFields": true, + "noImplicitOverride": true, + "noEmit": true + }, + "references": [] +} diff --git a/apps/dotcom-worker/.gitignore b/apps/dotcom-worker/.gitignore new file mode 100644 index 000000000..b3e55cbea --- /dev/null +++ b/apps/dotcom-worker/.gitignore @@ -0,0 +1,2 @@ +build +.wrangler \ No newline at end of file diff --git a/apps/dotcom-worker/CHANGELOG.md b/apps/dotcom-worker/CHANGELOG.md new file mode 100644 index 000000000..cacc52d1d --- /dev/null +++ b/apps/dotcom-worker/CHANGELOG.md @@ -0,0 +1,146 @@ +# @tldraw/tlsync-worker + +## 2.0.0-alpha.11 + +### Patch Changes + +- @tldraw/tlsync@2.0.0-alpha.11 + +## 2.0.0-alpha.10 + +### Patch Changes + +- @tldraw/tlsync@2.0.0-alpha.10 + +## 2.0.0-alpha.9 + +### Patch Changes + +- Release day! +- Updated dependencies + - @tldraw/tlsync@2.0.0-alpha.9 + +## 2.0.0-alpha.8 + +### Patch Changes + +- Updated dependencies [23dd81cfe] + - @tldraw/tlsync@2.0.0-alpha.8 + - @tldraw/tlsync-server@2.0.0-alpha.8 + +## 2.0.0-alpha.7 + +### Patch Changes + +- Bug fixes. +- Updated dependencies + - @tldraw/tlsync@2.0.0-alpha.7 + - @tldraw/tlsync-server@2.0.0-alpha.7 + +## 2.0.0-alpha.6 + +### Patch Changes + +- Add licenses. +- Updated dependencies + - @tldraw/tlsync@2.0.0-alpha.6 + - @tldraw/tlsync-server@2.0.0-alpha.6 + +## 2.0.0-alpha.5 + +### Patch Changes + +- Add CSS files to tldraw/tldraw. +- Updated dependencies + - @tldraw/tlsync@2.0.0-alpha.5 + - @tldraw/tlsync-server@2.0.0-alpha.5 + +## 2.0.0-alpha.4 + +### Patch Changes + +- Add children to tldraw/tldraw +- Updated dependencies + - @tldraw/tlsync@2.0.0-alpha.4 + - @tldraw/tlsync-server@2.0.0-alpha.4 + +## 2.0.0-alpha.3 + +### Patch Changes + +- Change permissions. +- Updated dependencies + - @tldraw/tlsync@2.0.0-alpha.3 + - @tldraw/tlsync-server@2.0.0-alpha.3 + +## 2.0.0-alpha.2 + +### Patch Changes + +- Add tldraw, editor +- Updated dependencies + - @tldraw/tlsync@2.0.0-alpha.2 + - @tldraw/tlsync-server@2.0.0-alpha.2 + +## 0.1.0-alpha.11 + +### Patch Changes + +- Fix stale reactors. +- Updated dependencies + - @tldraw/tlsync@0.1.0-alpha.11 + - @tldraw/tlsync-server@0.1.0-alpha.11 + +## 0.1.0-alpha.10 + +### Patch Changes + +- Fix type export bug. +- Updated dependencies + - @tldraw/tlsync@0.1.0-alpha.10 + - @tldraw/tlsync-server@0.1.0-alpha.10 + +## 0.1.0-alpha.9 + +### Patch Changes + +- Fix import bugs. +- Updated dependencies + - @tldraw/tlsync@0.1.0-alpha.9 + - @tldraw/tlsync-server@0.1.0-alpha.9 + +## 0.1.0-alpha.8 + +### Patch Changes + +- Changes validation requirements, exports validation helpers. +- Updated dependencies + - @tldraw/tlsync@0.1.0-alpha.8 + - @tldraw/tlsync-server@0.1.0-alpha.8 + +## 0.1.0-alpha.7 + +### Patch Changes + +- - Pre-pre-release update +- Updated dependencies + - @tldraw/tlsync@0.1.0-alpha.7 + - @tldraw/tlsync-server@0.1.0-alpha.7 + +## 0.0.2-alpha.1 + +### Patch Changes + +- Fix error with HMR +- Updated dependencies + - @tldraw/tlsync@0.0.2-alpha.1 + - @tldraw/tlsync-server@0.0.2-alpha.1 + +## 0.0.2-alpha.0 + +### Patch Changes + +- Initial release +- Updated dependencies + - @tldraw/tlsync@0.0.2-alpha.0 + - @tldraw/tlsync-server@0.0.2-alpha.0 diff --git a/apps/dotcom-worker/README.md b/apps/dotcom-worker/README.md new file mode 100644 index 000000000..8c5f2adce --- /dev/null +++ b/apps/dotcom-worker/README.md @@ -0,0 +1,12 @@ +# @tldraw/tlsync-worker + +## Enable database persistence for local dev + +The values for `env.SUPABASE_KEY` and `env.SUPABASE_URL` are stored in the Cloudflare Workers dashboard for this worker. However we use `--local` mode for local development, which doesn't read these values from the dashboard. + +To workaround this, create a file called `.dev.vars` under `merge-server` with the required values (which you can currently find at https://app.supabase.com/project/bfcjbbjqflgfzxhskwct/settings/api). This will be read by `wrangler dev --local` and used to populate the environment variables. + +``` +SUPABASE_URL= +SUPABASE_KEY= +``` diff --git a/apps/dotcom-worker/package.json b/apps/dotcom-worker/package.json new file mode 100644 index 000000000..f9b5f02d1 --- /dev/null +++ b/apps/dotcom-worker/package.json @@ -0,0 +1,53 @@ +{ + "name": "@tldraw/dotcom-worker", + "description": "A tiny little drawing app (merge server).", + "version": "2.0.0-alpha.11", + "private": true, + "packageManager": "yarn@3.5.0", + "author": { + "name": "tldraw GB Ltd.", + "email": "hello@tldraw.com" + }, + "main": "./src/lib/worker.ts", + "/* GOTCHA */": "files will include ./dist and index.d.ts by default, add any others you want to include in here", + "files": [], + "scripts": { + "dev": "concurrently --kill-others yarn:dev-cron yarn:dev-wrangler yarn:report-size", + "dev-cron": "yarn run -T tsx ./scripts/cron.ts", + "dev-wrangler": "yarn run -T tsx ./scripts/dev-wrap.ts", + "report-size": "node scripts/report-size.js", + "test": "lazy inherit", + "test-coverage": "lazy inherit", + "lint": "yarn run -T tsx ../../scripts/lint.ts" + }, + "dependencies": { + "@supabase/auth-helpers-remix": "^0.2.2", + "@supabase/supabase-js": "^2.33.2", + "@tldraw/store": "workspace:*", + "@tldraw/tlschema": "workspace:*", + "@tldraw/tlsync": "workspace:*", + "@tldraw/utils": "workspace:*", + "esbuild": "^0.18.4", + "itty-router": "^4.0.13", + "nanoid": "4.0.2", + "strip-ansi": "^7.1.0", + "toucan-js": "^2.7.0" + }, + "devDependencies": { + "@cloudflare/workers-types": "^4.20230821.0", + "concurrently": "^8.2.1", + "lazyrepo": "0.0.0-alpha.27", + "picocolors": "^1.0.0", + "typescript": "^5.0.2", + "wrangler": "3.16.0" + }, + "jest": { + "preset": "config/jest/node", + "moduleNameMapper": { + "^~(.*)": "/src/$1" + }, + "transformIgnorePatterns": [ + "node_modules/(?!(nanoid|escape-string-regexp)/)" + ] + } +} diff --git a/apps/dotcom-worker/scripts/cron.ts b/apps/dotcom-worker/scripts/cron.ts new file mode 100644 index 000000000..417c32eb5 --- /dev/null +++ b/apps/dotcom-worker/scripts/cron.ts @@ -0,0 +1,10 @@ +const CRON_INTERVAL_MS = 10_000 + +setInterval(async () => { + try { + await fetch('http://127.0.0.1:8787/__scheduled') + } catch (err) { + // eslint-disable-next-line no-console + console.log('Error triggering cron:', err) + } +}, CRON_INTERVAL_MS) diff --git a/apps/dotcom-worker/scripts/dev-wrap.ts b/apps/dotcom-worker/scripts/dev-wrap.ts new file mode 100644 index 000000000..0e79b198f --- /dev/null +++ b/apps/dotcom-worker/scripts/dev-wrap.ts @@ -0,0 +1,73 @@ +// at the time of writing, workerd will regularly crash with a segfault +// but the error is not caught by the process, so it will just hang +// this script wraps the process, tailing the logs and restarting the process +// if we encounter the string 'Segmentation fault' + +import { ChildProcessWithoutNullStreams, spawn } from 'child_process' +import stripAnsi from 'strip-ansi' + +// eslint-disable-next-line no-console +const log = console.log + +class MiniflareMonitor { + private process: ChildProcessWithoutNullStreams | null = null + + constructor( + private command: string, + private args: string[] = [] + ) {} + + public start(): void { + this.stop() // Ensure any existing process is stopped + log(`Starting wrangler...`) + this.process = spawn(this.command, this.args, { + env: { + NODE_ENV: 'development', + ...process.env, + }, + }) + + this.process.stdout.on('data', (data: Buffer) => { + this.handleOutput(stripAnsi(data.toString().replace('\r', '').trim())) + }) + + this.process.stderr.on('data', (data: Buffer) => { + this.handleOutput(stripAnsi(data.toString().replace('\r', '').trim()), true) + }) + } + + private handleOutput(output: string, err = false): void { + if (!output) return + if (output.includes('Segmentation fault')) { + console.error('Segmentation fault detected. Restarting Miniflare...') + this.restart() + } else if (!err) { + log(output.replace('[mf:inf]', '')) // or handle the output differently + } + } + + private restart(): void { + log('Restarting wrangler...') + this.stop() + setTimeout(() => this.start(), 3000) // Restart after a short delay + } + + private stop(): void { + if (this.process) { + this.process.kill() + this.process = null + } + } +} + +const monitor = new MiniflareMonitor('wrangler', [ + 'dev', + '--env', + 'dev', + '--test-scheduled', + '--log-level', + 'info', + '--var', + 'IS_LOCAL:true', +]) +monitor.start() diff --git a/apps/dotcom-worker/scripts/report-size.js b/apps/dotcom-worker/scripts/report-size.js new file mode 100644 index 000000000..1378231eb --- /dev/null +++ b/apps/dotcom-worker/scripts/report-size.js @@ -0,0 +1,45 @@ +/* eslint-disable no-undef */ +/* eslint-disable @typescript-eslint/no-var-requires */ +const { spawn } = require('child_process') +const colors = require('picocolors') + +class Monitor { + lastLineTime = Date.now() + nextTick = 0 + + size = 0 + + start() { + console.log('Spawning') + const proc = spawn('npx', ['esbuild', 'src/lib/worker.ts', '--bundle', '--minify', '--watch']) + // listen for lines on stdin + proc.stdout.on('data', (data) => { + this.size += data.length + this.lastLineTime = Date.now() + clearTimeout(this.nextTick) + this.nextTick = setTimeout(() => { + console.log( + colors.bold(colors.yellow('dotcom-worker')), + 'is roughly', + colors.bold(colors.cyan(Math.floor(this.size / 1024) + 'kb')), + '(minified)\n' + ) + this.size = 0 + }, 10) + }) + process.on('SIGINT', () => { + console.log('Int') + proc.kill() + }) + process.on('SIGTERM', () => { + console.log('Term') + proc.kill() + }) + process.on('exit', () => { + console.log('Exiting') + proc.kill() + }) + } +} + +new Monitor().start() diff --git a/apps/dotcom-worker/src/lib/AlarmScheduler.test.ts b/apps/dotcom-worker/src/lib/AlarmScheduler.test.ts new file mode 100644 index 000000000..012fd8a20 --- /dev/null +++ b/apps/dotcom-worker/src/lib/AlarmScheduler.test.ts @@ -0,0 +1,259 @@ +import { noop } from '@tldraw/utils' +import { AlarmScheduler } from './AlarmScheduler' + +jest.useFakeTimers() + +function makeMockAlarmScheduler(alarms: { + [K in Key]: jest.Mock, []> +}) { + const data = new Map() + let scheduledAlarm: number | null = null + + const storage = { + getAlarm: async () => scheduledAlarm, + setAlarm: jest.fn((time: number | Date) => { + scheduledAlarm = typeof time === 'number' ? time : time.getTime() + }), + get: async (key: string) => data.get(key), + list: async () => new Map(data), + delete: async (keys: string[]) => { + let count = 0 + for (const key of keys) { + if (data.delete(key)) count++ + } + return count + }, + put: async (entries: Record) => { + for (const [key, value] of Object.entries(entries)) { + data.set(key, value) + } + }, + asObject: () => Object.fromEntries(data), + } + + const scheduler = new AlarmScheduler({ + alarms, + storage: () => storage, + }) + + const advanceTime = async (time: number) => { + jest.advanceTimersByTime(time) + if (scheduledAlarm !== null && scheduledAlarm <= Date.now()) { + scheduledAlarm = null + await scheduler.onAlarm() + // process the alarms that were scheduled during the onAlarm call: + if (scheduledAlarm) await advanceTime(0) + } + } + + return { + scheduler, + storage, + alarms, + advanceTime, + } +} + +describe('AlarmScheduler', () => { + beforeEach(() => { + jest.setSystemTime(1_000_000) + }) + afterEach(() => { + jest.resetAllMocks() + }) + + test('scheduling alarms', async () => { + const { scheduler, storage } = makeMockAlarmScheduler({ + one: jest.fn(), + two: jest.fn(), + three: jest.fn(), + }) + + // when no alarms are scheduled, we always call storage.setAlarm + await scheduler.scheduleAlarmAfter('one', 1000, { overwrite: 'always' }) + expect(storage.setAlarm).toHaveBeenCalledTimes(1) + expect(storage.setAlarm).toHaveBeenLastCalledWith(1_001_000) + expect(storage.asObject()).toStrictEqual({ 'alarm-one': 1_001_000 }) + + // if a later alarm is scheduled, we don't call storage.setAlarm + await scheduler.scheduleAlarmAfter('two', 2000, { overwrite: 'always' }) + expect(storage.setAlarm).toHaveBeenCalledTimes(1) + expect(storage.asObject()).toStrictEqual({ 'alarm-one': 1_001_000, 'alarm-two': 1_002_000 }) + + // if a sooner alarm is scheduled, we call storage.setAlarm again + await scheduler.scheduleAlarmAfter('three', 500, { overwrite: 'always' }) + expect(storage.setAlarm).toHaveBeenCalledTimes(2) + expect(storage.setAlarm).toHaveBeenLastCalledWith(1_000_500) + expect(storage.asObject()).toStrictEqual({ + 'alarm-one': 1_001_000, + 'alarm-two': 1_002_000, + 'alarm-three': 1_000_500, + }) + + // if the soonest alarm is scheduled later, we don't call storage.setAlarm with a later time - we + // just let it no-op and reschedule when the alarm is actually triggered: + await scheduler.scheduleAlarmAfter('three', 1000, { overwrite: 'always' }) + expect(storage.setAlarm).toHaveBeenCalledTimes(2) + expect(storage.asObject()).toStrictEqual({ + 'alarm-one': 1_001_000, + 'alarm-two': 1_002_000, + 'alarm-three': 1_001_000, + }) + }) + + test('onAlarm - basic function', async () => { + const { scheduler, alarms, storage, advanceTime } = makeMockAlarmScheduler({ + one: jest.fn(), + two: jest.fn(), + three: jest.fn(), + }) + + // schedule some alarms: + await scheduler.scheduleAlarmAfter('one', 1000, { overwrite: 'always' }) + await scheduler.scheduleAlarmAfter('two', 1000, { overwrite: 'always' }) + await scheduler.scheduleAlarmAfter('three', 2000, { overwrite: 'always' }) + expect(storage.setAlarm).toHaveBeenCalledTimes(1) + expect(storage.asObject()).toStrictEqual({ + 'alarm-one': 1_001_000, + 'alarm-two': 1_001_000, + 'alarm-three': 1_002_000, + }) + + // firing the alarm calls the appropriate alarm functions... + await advanceTime(1000) + expect(alarms.one).toHaveBeenCalledTimes(1) + expect(alarms.two).toHaveBeenCalledTimes(1) + expect(alarms.three).not.toHaveBeenCalled() + // ...deletes the called alarms... + expect(storage.asObject()).toStrictEqual({ 'alarm-three': 1_002_000 }) + // ...and reschedules the next alarm: + expect(storage.setAlarm).toHaveBeenCalledTimes(2) + expect(storage.setAlarm).toHaveBeenLastCalledWith(1_002_000) + + // firing the alarm again calls the next alarm and doesn't reschedule: + await advanceTime(1000) + expect(alarms.one).toHaveBeenCalledTimes(1) + expect(alarms.two).toHaveBeenCalledTimes(1) + expect(alarms.three).toHaveBeenCalledTimes(1) + expect(storage.asObject()).toStrictEqual({}) + expect(storage.setAlarm).toHaveBeenCalledTimes(2) + }) + + test('can schedule an alarm within an alarm', async () => { + const { scheduler, storage, advanceTime, alarms } = makeMockAlarmScheduler({ + a: jest.fn(async () => { + scheduler.scheduleAlarmAfter('b', 1000, { overwrite: 'always' }) + }), + b: jest.fn(), + c: jest.fn(), + }) + + // sequence should be a -> c -> b: + await scheduler.scheduleAlarmAfter('a', 1000, { overwrite: 'always' }) + await scheduler.scheduleAlarmAfter('c', 1500, { overwrite: 'always' }) + expect(storage.setAlarm).toHaveBeenCalledTimes(1) + + // a... + await advanceTime(1000) + expect(alarms.a).toBeCalledTimes(1) + expect(alarms.b).toBeCalledTimes(0) + expect(alarms.c).toBeCalledTimes(0) + // called for b, then a again to reschedule c: + expect(storage.setAlarm).toHaveBeenCalledTimes(3) + expect(storage.setAlarm).toHaveBeenLastCalledWith(1_001_500) + + // ...b... + await advanceTime(500) + expect(alarms.a).toBeCalledTimes(1) + expect(alarms.b).toBeCalledTimes(0) + expect(alarms.c).toBeCalledTimes(1) + expect(storage.setAlarm).toHaveBeenCalledTimes(4) + expect(storage.setAlarm).toHaveBeenLastCalledWith(1_002_000) + + // ...c + await advanceTime(500) + expect(alarms.a).toBeCalledTimes(1) + expect(alarms.b).toBeCalledTimes(1) + expect(alarms.c).toBeCalledTimes(1) + expect(storage.setAlarm).toHaveBeenCalledTimes(4) + + // sequence should be a -> b -> c: + await scheduler.scheduleAlarmAfter('a', 1000, { overwrite: 'always' }) + await scheduler.scheduleAlarmAfter('c', 3000, { overwrite: 'always' }) + expect(storage.setAlarm).toHaveBeenCalledTimes(5) + expect(storage.setAlarm).toHaveBeenLastCalledWith(1_003_000) + + // a... + await advanceTime(1000) + expect(alarms.a).toBeCalledTimes(2) + expect(alarms.b).toBeCalledTimes(1) + expect(alarms.c).toBeCalledTimes(1) + // called for b, not needed to reschedule c: + expect(storage.setAlarm).toHaveBeenCalledTimes(6) + expect(storage.setAlarm).toHaveBeenLastCalledWith(1_004_000) + + // ...b... + await advanceTime(1000) + expect(alarms.a).toBeCalledTimes(2) + expect(alarms.b).toBeCalledTimes(2) + expect(alarms.c).toBeCalledTimes(1) + expect(storage.setAlarm).toHaveBeenCalledTimes(7) + expect(storage.setAlarm).toHaveBeenLastCalledWith(1_005_000) + + // ...c + await advanceTime(1000) + expect(alarms.a).toBeCalledTimes(2) + expect(alarms.b).toBeCalledTimes(2) + expect(alarms.c).toBeCalledTimes(2) + expect(storage.setAlarm).toHaveBeenCalledTimes(7) + }) + + test('can schedule the same alarm within an alarm', async () => { + const { scheduler, storage, advanceTime, alarms } = makeMockAlarmScheduler({ + a: jest.fn(async () => { + scheduler.scheduleAlarmAfter('a', 1000, { overwrite: 'always' }) + }), + }) + + await scheduler.scheduleAlarmAfter('a', 1000, { overwrite: 'always' }) + expect(storage.setAlarm).toHaveBeenCalledTimes(1) + + await advanceTime(1000) + expect(alarms.a).toHaveBeenCalledTimes(1) + expect(storage.setAlarm).toHaveBeenCalledTimes(2) + expect(storage.setAlarm).toHaveBeenLastCalledWith(1_002_000) + expect(storage.asObject()).toStrictEqual({ 'alarm-a': 1_002_000 }) + + await advanceTime(1000) + expect(alarms.a).toHaveBeenCalledTimes(2) + expect(storage.setAlarm).toHaveBeenCalledTimes(3) + expect(storage.setAlarm).toHaveBeenLastCalledWith(1_003_000) + expect(storage.asObject()).toStrictEqual({ 'alarm-a': 1_003_000 }) + }) + + test('handles retries', async () => { + const { scheduler, advanceTime, storage, alarms } = await makeMockAlarmScheduler({ + error: jest.fn(async () => { + throw new Error('something went wrong') + }), + ok: jest.fn(), + }) + + await scheduler.scheduleAlarmAfter('error', 1000, { overwrite: 'always' }) + await scheduler.scheduleAlarmAfter('ok', 1000, { overwrite: 'always' }) + expect(storage.asObject()).toStrictEqual({ + 'alarm-error': 1_001_000, + 'alarm-ok': 1_001_000, + }) + + jest.spyOn(console, 'log').mockImplementation(noop) + await expect(async () => advanceTime(1000)).rejects.toThrowError( + 'Some alarms failed to fire, scheduling retry' + ) + expect(alarms.error).toHaveBeenCalledTimes(1) + expect(alarms.ok).toHaveBeenCalledTimes(1) + expect(storage.asObject()).toStrictEqual({ + 'alarm-error': 1_001_000, + }) + }) +}) diff --git a/apps/dotcom-worker/src/lib/AlarmScheduler.ts b/apps/dotcom-worker/src/lib/AlarmScheduler.ts new file mode 100644 index 000000000..9a8ed4360 --- /dev/null +++ b/apps/dotcom-worker/src/lib/AlarmScheduler.ts @@ -0,0 +1,115 @@ +import { exhaustiveSwitchError, hasOwnProperty } from '@tldraw/utils' + +type AlarmOpts = { + overwrite: 'always' | 'if-sooner' +} + +export class AlarmScheduler { + storage: () => { + getAlarm(): Promise + setAlarm(scheduledTime: number | Date): void + get(key: string): Promise + list(options: { prefix: string }): Promise> + delete(keys: string[]): Promise + put(entries: Record): Promise + } + alarms: { [K in Key]: () => Promise } + + constructor(opts: Pick, 'storage' | 'alarms'>) { + this.storage = opts.storage + this.alarms = opts.alarms + } + + _alarmsScheduledDuringCurrentOnAlarmCall: Set | null = null + async onAlarm() { + if (this._alarmsScheduledDuringCurrentOnAlarmCall !== null) { + // i _think_ DOs alarms are one-at-a-time, but throwing here will cause a retry + throw new Error('onAlarm called before previous call finished') + } + this._alarmsScheduledDuringCurrentOnAlarmCall = new Set() + try { + const alarms = await this.storage().list({ prefix: 'alarm-' }) + const successfullyExecutedAlarms = new Set() + let shouldRetry = false + let nextAlarmTime = null + + for (const [key, requestedTime] of alarms) { + const cleanedKey = key.replace(/^alarm-/, '') as Key + if (!hasOwnProperty(this.alarms, cleanedKey)) continue + if (requestedTime > Date.now()) { + if (nextAlarmTime === null || requestedTime < nextAlarmTime) { + nextAlarmTime = requestedTime + } + continue + } + const alarm = this.alarms[cleanedKey] + try { + await alarm() + successfullyExecutedAlarms.add(cleanedKey) + } catch (err) { + // eslint-disable-next-line no-console + console.log(`Error firing alarm ${cleanedKey}:`, err) + shouldRetry = true + } + } + + const keysToDelete = [] + for (const key of successfullyExecutedAlarms) { + if (this._alarmsScheduledDuringCurrentOnAlarmCall.has(key)) continue + keysToDelete.push(`alarm-${key}`) + } + if (keysToDelete.length > 0) { + await this.storage().delete(keysToDelete) + } + + if (shouldRetry) { + throw new Error('Some alarms failed to fire, scheduling retry') + } else if (nextAlarmTime !== null) { + await this.setCoreAlarmIfNeeded(nextAlarmTime) + } + } finally { + this._alarmsScheduledDuringCurrentOnAlarmCall = null + } + } + + private async setCoreAlarmIfNeeded(targetAlarmTime: number) { + const currentAlarmTime = await this.storage().getAlarm() + if (currentAlarmTime === null || targetAlarmTime < currentAlarmTime) { + await this.storage().setAlarm(targetAlarmTime) + } + } + + async scheduleAlarmAt(key: Key, time: number | Date, opts: AlarmOpts) { + const targetTime = typeof time === 'number' ? time : time.getTime() + if (this._alarmsScheduledDuringCurrentOnAlarmCall !== null) { + this._alarmsScheduledDuringCurrentOnAlarmCall.add(key) + } + switch (opts.overwrite) { + case 'always': + await this.storage().put({ [`alarm-${key}`]: targetTime }) + break + case 'if-sooner': { + const currentScheduled = await this.storage().get(`alarm-${key}`) + if (!currentScheduled || currentScheduled > targetTime) { + await this.storage().put({ [`alarm-${key}`]: targetTime }) + } + break + } + default: + exhaustiveSwitchError(opts.overwrite) + } + await this.setCoreAlarmIfNeeded(targetTime) + } + + async scheduleAlarmAfter(key: Key, delayMs: number, opts: AlarmOpts) { + await this.scheduleAlarmAt(key, Date.now() + delayMs, opts) + } + + async getAlarm(key: Key): Promise { + return (await this.storage().get(`alarm-${key}`)) ?? null + } + + async deleteAlarm(key: Key): Promise { + await this.storage().delete([`alarm-${key}`]) + } +} diff --git a/apps/dotcom-worker/src/lib/TLDrawDurableObject.ts b/apps/dotcom-worker/src/lib/TLDrawDurableObject.ts new file mode 100644 index 000000000..8cad7356e --- /dev/null +++ b/apps/dotcom-worker/src/lib/TLDrawDurableObject.ts @@ -0,0 +1,398 @@ +/// +/// + +import { SupabaseClient } from '@supabase/supabase-js' +import { + RoomSnapshot, + TLServer, + TLSyncRoom, + type DBLoadResult, + type PersistedRoomSnapshotForSupabase, + type RoomState, +} from '@tldraw/tlsync' +import { assert, assertExists } from '@tldraw/utils' +import { IRequest, Router } from 'itty-router' +import Toucan from 'toucan-js' +import { AlarmScheduler } from './AlarmScheduler' +import { PERSIST_INTERVAL_MS } from './config' +import { getR2KeyForRoom } from './r2' +import { Analytics, Environment } from './types' +import { createSupabaseClient } from './utils/createSupabaseClient' +import { throttle } from './utils/throttle' + +const MAX_CONNECTIONS = 50 + +// increment this any time you make a change to this type +const CURRENT_DOCUMENT_INFO_VERSION = 0 +type DocumentInfo = { + version: number + slug: string +} + +export class TLDrawDurableObject extends TLServer { + // A unique identifier for this instance of the Durable Object + id: DurableObjectId + + // For TLSyncRoom + _roomState: RoomState | undefined + + // For storage + storage: DurableObjectStorage + + // For persistence + supabaseClient: SupabaseClient | void + + // For analytics + measure: Analytics | undefined + + // For error tracking + sentryDSN: string | undefined + + readonly supabaseTable: string + readonly r2: { + readonly rooms: R2Bucket + readonly versionCache: R2Bucket + } + + _documentInfo: DocumentInfo | null = null + + constructor( + private controller: DurableObjectState, + private env: Environment + ) { + super() + + this.id = controller.id + this.storage = controller.storage + this.sentryDSN = env.SENTRY_DSN + this.measure = env.MEASURE + this.supabaseClient = createSupabaseClient(env) + + this.supabaseTable = env.TLDRAW_ENV === 'production' ? 'drawings' : 'drawings_staging' + this.r2 = { + rooms: env.ROOMS, + versionCache: env.ROOMS_HISTORY_EPHEMERAL, + } + + controller.blockConcurrencyWhile(async () => { + const existingDocumentInfo = (await this.storage.get('documentInfo')) as DocumentInfo | null + if (existingDocumentInfo?.version !== CURRENT_DOCUMENT_INFO_VERSION) { + this._documentInfo = null + } else { + this._documentInfo = existingDocumentInfo + } + }) + } + + readonly router = Router() + .get( + '/r/:roomId', + (req) => this.extractDocumentInfoFromRequest(req), + (req) => this.onRequest(req) + ) + .post( + '/r/:roomId/restore', + (req) => this.extractDocumentInfoFromRequest(req), + (req) => this.onRestore(req) + ) + .all('*', () => new Response('Not found', { status: 404 })) + + readonly scheduler = new AlarmScheduler({ + storage: () => this.storage, + alarms: { + persist: async () => { + const room = this.getRoomForPersistenceKey(this.documentInfo.slug) + if (!room) return + this.persistToDatabase(room.persistenceKey) + }, + }, + }) + + // eslint-disable-next-line no-restricted-syntax + get documentInfo() { + return assertExists(this._documentInfo, 'documentInfo must be present') + } + extractDocumentInfoFromRequest = async (req: IRequest) => { + const slug = assertExists(req.params.roomId, 'roomId must be present') + if (this._documentInfo) { + assert(this._documentInfo.slug === slug, 'slug must match') + } else { + this._documentInfo = { + version: CURRENT_DOCUMENT_INFO_VERSION, + slug, + } + } + } + + // Handle a request to the Durable Object. + async fetch(req: IRequest) { + const sentry = new Toucan({ + dsn: this.sentryDSN, + request: req, + allowedHeaders: ['user-agent'], + allowedSearchParams: /(.*)/, + }) + + try { + return await this.router.handle(req).catch((err) => { + console.error(err) + sentry.captureException(err) + + return new Response('Something went wrong', { + status: 500, + statusText: 'Internal Server Error', + }) + }) + } catch (err) { + sentry.captureException(err) + return new Response('Something went wrong', { + status: 500, + statusText: 'Internal Server Error', + }) + } + } + + async onRestore(req: IRequest) { + const roomId = this.documentInfo.slug + const roomKey = getR2KeyForRoom(roomId) + const timestamp = ((await req.json()) as any).timestamp + if (!timestamp) { + return new Response('Missing timestamp', { status: 400 }) + } + const data = await this.r2.versionCache.get(`${roomKey}/${timestamp}`) + if (!data) { + return new Response('Version not found', { status: 400 }) + } + const dataText = await data.text() + await this.r2.rooms.put(roomKey, dataText) + const roomState = this.getRoomForPersistenceKey(roomId) + if (!roomState) { + // nothing else to do because the room is not currently in use + return new Response() + } + const snapshot: RoomSnapshot = JSON.parse(dataText) + const oldRoom = roomState.room + const oldIds = oldRoom.getSnapshot().documents.map((d) => d.state.id) + const newIds = new Set(snapshot.documents.map((d) => d.state.id)) + const removedIds = oldIds.filter((id) => !newIds.has(id)) + + const tombstones = { ...snapshot.tombstones } + removedIds.forEach((id) => { + tombstones[id] = oldRoom.clock + 1 + }) + newIds.forEach((id) => { + delete tombstones[id] + }) + + const newRoom = new TLSyncRoom(roomState.room.schema, { + clock: oldRoom.clock + 1, + documents: snapshot.documents.map((d) => ({ + lastChangedClock: oldRoom.clock + 1, + state: d.state, + })), + schema: snapshot.schema, + tombstones, + }) + + // replace room with new one and kick out all the clients + this.setRoomState(this.documentInfo.slug, { ...roomState, room: newRoom }) + oldRoom.close() + + return new Response() + } + + async onRequest(req: IRequest) { + // extract query params from request, should include instanceId + const url = new URL(req.url) + const params = Object.fromEntries(url.searchParams.entries()) + let { sessionKey, storeId } = params + + // handle legacy param names + sessionKey ??= params.instanceId + storeId ??= params.localClientId + + // Don't connect if we're already at max connections + const roomState = this.getRoomForPersistenceKey(this.documentInfo.slug) + if (roomState !== undefined) { + if (roomState.room.sessions.size >= MAX_CONNECTIONS) { + return new Response('Room is full', { + status: 403, + }) + } + } + + // Create the websocket pair for the client + const { 0: clientWebSocket, 1: serverWebSocket } = new WebSocketPair() + + // Handle the connection (see TLServer) + try { + // block concurrency while initializing the room if that needs to happen + await this.controller.blockConcurrencyWhile(() => + this.handleConnection({ + socket: serverWebSocket as any, + persistenceKey: this.documentInfo.slug!, + sessionKey, + storeId, + }) + ) + } catch (e: any) { + console.error(e) + return new Response(e.message, { status: 500 }) + } + + // Accept the websocket connection + serverWebSocket.accept() + serverWebSocket.addEventListener( + 'message', + throttle(() => { + this.schedulePersist() + }, 2000) + ) + serverWebSocket.addEventListener('close', () => { + this.schedulePersist() + }) + + return new Response(null, { status: 101, webSocket: clientWebSocket }) + } + + logEvent( + event: + | { + type: 'client' + roomId: string + name: string + clientId: string + instanceId: string + localClientId: string + } + | { + type: 'room' + roomId: string + name: string + } + ) { + switch (event.type) { + case 'room': { + this.measure?.writeDataPoint({ + blobs: [event.name, event.roomId], // we would add user/connection ids here if we could + }) + + break + } + case 'client': { + this.measure?.writeDataPoint({ + blobs: [event.name, event.roomId, event.clientId, event.instanceId], // we would add user/connection ids here if we could + indexes: [event.localClientId], + }) + + break + } + } + } + + getRoomForPersistenceKey(_persistenceKey: string): RoomState | undefined { + return this._roomState // only one room per worker + } + + setRoomState(_persistenceKey: string, roomState: RoomState): void { + this.deleteRoomState() + this._roomState = roomState + } + + deleteRoomState(): void { + this._roomState = undefined + } + + // Load the room's drawing data from supabase + override async loadFromDatabase(persistenceKey: string): Promise { + try { + const key = getR2KeyForRoom(persistenceKey) + // when loading, prefer to fetch documents from the bucket + const roomFromBucket = await this.r2.rooms.get(key) + if (roomFromBucket) { + return { type: 'room_found', snapshot: await roomFromBucket.json() } + } + + // if we don't have a room in the bucket, try to load from supabase + if (!this.supabaseClient) return { type: 'room_not_found' } + const { data, error } = await this.supabaseClient + .from(this.supabaseTable) + .select('*') + .eq('slug', persistenceKey) + + if (error) { + this.logEvent({ type: 'room', roomId: persistenceKey, name: 'failed_load_from_db' }) + + console.error('failed to retrieve document', persistenceKey, error) + return { type: 'error', error: new Error(error.message) } + } + // if it didn't find a document, data will be an empty array + if (data.length === 0) { + return { type: 'room_not_found' } + } + + const roomFromSupabase = data[0] as PersistedRoomSnapshotForSupabase + return { type: 'room_found', snapshot: roomFromSupabase.drawing } + } catch (error) { + this.logEvent({ type: 'room', roomId: persistenceKey, name: 'failed_load_from_db' }) + + console.error('failed to fetch doc', persistenceKey, error) + return { type: 'error', error: error as Error } + } + } + + _isPersisting = false + _lastPersistedClock: number | null = null + + // Save the room to supabase + async persistToDatabase(persistenceKey: string) { + if (this._isPersisting) { + setTimeout(() => { + this.schedulePersist() + }, 5000) + return + } + + try { + this._isPersisting = true + + const roomState = this.getRoomForPersistenceKey(persistenceKey) + if (!roomState) { + // room was closed + return + } + + const { room } = roomState + const { clock } = room + if (this._lastPersistedClock === clock) return + + try { + const snapshot = JSON.stringify(room.getSnapshot()) + + const key = getR2KeyForRoom(persistenceKey) + await Promise.all([ + this.r2.rooms.put(key, snapshot), + this.r2.versionCache.put(key + `/` + new Date().toISOString(), snapshot), + ]) + this._lastPersistedClock = clock + } catch (error) { + this.logEvent({ type: 'room', roomId: persistenceKey, name: 'failed_persist_to_db' }) + console.error('failed to persist document', persistenceKey, error) + throw error + } + } finally { + this._isPersisting = false + } + } + + async schedulePersist() { + await this.scheduler.scheduleAlarmAfter('persist', PERSIST_INTERVAL_MS, { + overwrite: 'if-sooner', + }) + } + + // Will be called automatically when the alarm ticks. + async alarm() { + await this.scheduler.onAlarm() + } +} diff --git a/apps/dotcom-worker/src/lib/config.ts b/apps/dotcom-worker/src/lib/config.ts new file mode 100644 index 000000000..b0c41ee95 --- /dev/null +++ b/apps/dotcom-worker/src/lib/config.ts @@ -0,0 +1,5 @@ +/** + * How often we the document to R2? + * 10 seconds. + */ +export const PERSIST_INTERVAL_MS = 10_000 diff --git a/apps/dotcom-worker/src/lib/r2.ts b/apps/dotcom-worker/src/lib/r2.ts new file mode 100644 index 000000000..706680de6 --- /dev/null +++ b/apps/dotcom-worker/src/lib/r2.ts @@ -0,0 +1,3 @@ +export function getR2KeyForRoom(persistenceKey: string) { + return `public_rooms/${persistenceKey}` +} diff --git a/apps/dotcom-worker/src/lib/routes/createRoom.ts b/apps/dotcom-worker/src/lib/routes/createRoom.ts new file mode 100644 index 000000000..c6a450ce4 --- /dev/null +++ b/apps/dotcom-worker/src/lib/routes/createRoom.ts @@ -0,0 +1,45 @@ +import { SerializedSchema, SerializedStore } from '@tldraw/store' +import { TLRecord } from '@tldraw/tlschema' +import { RoomSnapshot, schema } from '@tldraw/tlsync' +import { IRequest } from 'itty-router' +import { nanoid } from 'nanoid' +import { getR2KeyForRoom } from '../r2' +import { Environment } from '../types' +import { validateSnapshot } from '../utils/validateSnapshot' + +type SnapshotRequestBody = { + schema: SerializedSchema + snapshot: SerializedStore +} + +// Sets up a new room based on a provided snapshot, e.g. when a user clicks the "Share" buttons or the "Fork project" buttons. +export async function createRoom(request: IRequest, env: Environment): Promise { + // The data sent from the client will include the data for the new room + const data = (await request.json()) as SnapshotRequestBody + + // There's a chance the data will be invalid, so we check it first + const snapshotResult = validateSnapshot(data) + if (!snapshotResult.ok) { + return Response.json({ error: true, message: snapshotResult.error }, { status: 400 }) + } + + // Create a new slug for the room + const slug = nanoid() + + // Create the new snapshot + const snapshot: RoomSnapshot = { + schema: schema.serialize(), + clock: 0, + documents: Object.values(snapshotResult.value).map((r) => ({ + state: r, + lastChangedClock: 0, + })), + tombstones: {}, + } + + // Bang that snapshot into the database + await env.ROOMS.put(getR2KeyForRoom(slug), JSON.stringify(snapshot)) + + // Send back the slug so that the client can redirect to the new room + return new Response(JSON.stringify({ error: false, slug })) +} diff --git a/apps/dotcom-worker/src/lib/routes/createRoomSnapshot.ts b/apps/dotcom-worker/src/lib/routes/createRoomSnapshot.ts new file mode 100644 index 000000000..0307d5303 --- /dev/null +++ b/apps/dotcom-worker/src/lib/routes/createRoomSnapshot.ts @@ -0,0 +1,47 @@ +import { SerializedSchema, SerializedStore } from '@tldraw/store' +import { TLRecord } from '@tldraw/tlschema' +import { IRequest } from 'itty-router' +import { nanoid } from 'nanoid' +import { Environment } from '../types' +import { createSupabaseClient, noSupabaseSorry } from '../utils/createSupabaseClient' +import { getSnapshotsTable } from '../utils/getSnapshotsTable' +import { validateSnapshot } from '../utils/validateSnapshot' + +type CreateSnapshotRequestBody = { + schema: SerializedSchema + snapshot: SerializedStore + parent_slug?: string | string[] | undefined +} + +export async function createRoomSnapshot(request: IRequest, env: Environment): Promise { + const data = (await request.json()) as CreateSnapshotRequestBody + + const snapshotResult = validateSnapshot(data) + if (!snapshotResult.ok) { + return Response.json({ error: true, message: snapshotResult.error }, { status: 400 }) + } + + const roomId = `v2_c_${nanoid()}` + + const persistedRoomSnapshot = { + parent_slug: data.parent_slug, + slug: roomId, + drawing: { + schema: data.schema, + clock: 0, + documents: Object.values(data.snapshot).map((r) => ({ + state: r, + lastChangedClock: 0, + })), + tombstones: {}, + }, + } + + const supabase = createSupabaseClient(env) + if (!supabase) return noSupabaseSorry() + + const supabaseTable = getSnapshotsTable(env) + await supabase.from(supabaseTable).insert(persistedRoomSnapshot) + + return new Response(JSON.stringify({ error: false, roomId })) +} diff --git a/apps/dotcom-worker/src/lib/routes/forwardRoomRequest.ts b/apps/dotcom-worker/src/lib/routes/forwardRoomRequest.ts new file mode 100644 index 000000000..fc3befcc4 --- /dev/null +++ b/apps/dotcom-worker/src/lib/routes/forwardRoomRequest.ts @@ -0,0 +1,16 @@ +import { IRequest } from 'itty-router' +import { Environment } from '../types' +import { fourOhFour } from '../utils/fourOhFour' +import { isRoomIdTooLong, roomIdIsTooLong } from '../utils/roomIdIsTooLong' + +// Forwards a room request to the durable object associated with that room +export async function forwardRoomRequest(request: IRequest, env: Environment): Promise { + const roomId = request.params.roomId + + if (!roomId) return fourOhFour() + if (isRoomIdTooLong(roomId)) return roomIdIsTooLong() + + // Set up the durable object for this room + const id = env.TLDR_DOC.idFromName(`/r/${roomId}`) + return env.TLDR_DOC.get(id).fetch(request) +} diff --git a/apps/dotcom-worker/src/lib/routes/getRoomHistory.ts b/apps/dotcom-worker/src/lib/routes/getRoomHistory.ts new file mode 100644 index 000000000..33c99045d --- /dev/null +++ b/apps/dotcom-worker/src/lib/routes/getRoomHistory.ts @@ -0,0 +1,39 @@ +import { IRequest } from 'itty-router' +import { getR2KeyForRoom } from '../r2' +import { Environment } from '../types' +import { fourOhFour } from '../utils/fourOhFour' +import { isRoomIdTooLong, roomIdIsTooLong } from '../utils/roomIdIsTooLong' + +// Returns the history of a room as a list of objects with timestamps +export async function getRoomHistory(request: IRequest, env: Environment): Promise { + const roomId = request.params.roomId + + if (!roomId) return fourOhFour() + if (isRoomIdTooLong(roomId)) return roomIdIsTooLong() + + const versionCacheBucket = env.ROOMS_HISTORY_EPHEMERAL + const bucketKey = getR2KeyForRoom(roomId) + + let batch = await versionCacheBucket.list({ + prefix: bucketKey, + }) + const result = [...batch.objects.map((o) => o.key)] + + // ✅ - use the truncated property to check if there are more + // objects to be returned + while (batch.truncated) { + const next = await versionCacheBucket.list({ + cursor: batch.cursor, + }) + result.push(...next.objects.map((o) => o.key)) + + batch = next + } + + // these are ISO timestamps, so they sort lexicographically + result.sort() + + return new Response(JSON.stringify(result), { + headers: { 'content-type': 'application/json' }, + }) +} diff --git a/apps/dotcom-worker/src/lib/routes/getRoomHistorySnapshot.ts b/apps/dotcom-worker/src/lib/routes/getRoomHistorySnapshot.ts new file mode 100644 index 000000000..1541d97ca --- /dev/null +++ b/apps/dotcom-worker/src/lib/routes/getRoomHistorySnapshot.ts @@ -0,0 +1,30 @@ +import { IRequest } from 'itty-router' +import { getR2KeyForRoom } from '../r2' +import { Environment } from '../types' +import { fourOhFour } from '../utils/fourOhFour' +import { isRoomIdTooLong, roomIdIsTooLong } from '../utils/roomIdIsTooLong' + +// Get a snapshot of the room at a given point in time +export async function getRoomHistorySnapshot( + request: IRequest, + env: Environment +): Promise { + const roomId = request.params.roomId + + if (!roomId) return fourOhFour() + if (isRoomIdTooLong(roomId)) return roomIdIsTooLong() + + const timestamp = request.params.timestamp + + const versionCacheBucket = env.ROOMS_HISTORY_EPHEMERAL + + const result = await versionCacheBucket.get(getR2KeyForRoom(roomId) + '/' + timestamp) + + if (!result) { + return new Response('Not found', { status: 404 }) + } + + return new Response(result.body, { + headers: { 'content-type': 'application/json' }, + }) +} diff --git a/apps/dotcom-worker/src/lib/routes/getRoomSnapshot.ts b/apps/dotcom-worker/src/lib/routes/getRoomSnapshot.ts new file mode 100644 index 000000000..1ec5b2c42 --- /dev/null +++ b/apps/dotcom-worker/src/lib/routes/getRoomSnapshot.ts @@ -0,0 +1,36 @@ +import { RoomSnapshot } from '@tldraw/tlsync' +import { IRequest } from 'itty-router' +import { Environment } from '../types' +import { createSupabaseClient, noSupabaseSorry } from '../utils/createSupabaseClient' +import { fourOhFour } from '../utils/fourOhFour' +import { getSnapshotsTable } from '../utils/getSnapshotsTable' + +// Returns a snapshot of the room at a given point in time +export async function getRoomSnapshot(request: IRequest, env: Environment): Promise { + const roomId = request.params.roomId + if (!roomId) return fourOhFour() + + // Create a supabase client + const supabase = createSupabaseClient(env) + if (!supabase) return noSupabaseSorry() + + // Get the snapshot from the table + const supabaseTable = getSnapshotsTable(env) + const result = await supabase + .from(supabaseTable) + .select('drawing') + .eq('slug', roomId) + .maybeSingle() + const data = result.data?.drawing as RoomSnapshot + + if (!data) return fourOhFour() + + // Send back the snapshot! + return new Response( + JSON.stringify({ + records: data.documents.map((d) => d.state), + schema: data.schema, + error: false, + }) + ) +} diff --git a/apps/dotcom-worker/src/lib/routes/joinExistingRoom.ts b/apps/dotcom-worker/src/lib/routes/joinExistingRoom.ts new file mode 100644 index 000000000..99569fe99 --- /dev/null +++ b/apps/dotcom-worker/src/lib/routes/joinExistingRoom.ts @@ -0,0 +1,20 @@ +import { IRequest } from 'itty-router' +import { Environment } from '../types' +import { fourOhFour } from '../utils/fourOhFour' +import { isRoomIdTooLong, roomIdIsTooLong } from '../utils/roomIdIsTooLong' + +// This is the entry point for joining an existing room +export async function joinExistingRoom(request: IRequest, env: Environment): Promise { + const roomId = request.params.roomId + if (!roomId) return fourOhFour() + if (isRoomIdTooLong(roomId)) return roomIdIsTooLong() + + // This needs to be a websocket request! + if (request.headers.get('upgrade')?.toLowerCase() === 'websocket') { + // Set up the durable object for this room + const id = env.TLDR_DOC.idFromName(`/r/${roomId}`) + return env.TLDR_DOC.get(id).fetch(request) + } + + return fourOhFour() +} diff --git a/apps/dotcom-worker/src/lib/types.ts b/apps/dotcom-worker/src/lib/types.ts new file mode 100644 index 000000000..b454677d6 --- /dev/null +++ b/apps/dotcom-worker/src/lib/types.ts @@ -0,0 +1,29 @@ +// https://developers.cloudflare.com/analytics/analytics-engine/ + +// This type isn't available in @cloudflare/workers-types yet +export type Analytics = { + writeDataPoint(data: { + blobs?: string[] + doubles?: number[] + indexes?: [string] // only one here + }): void +} + +export interface Environment { + // bindings + TLDR_DOC: DurableObjectNamespace + MEASURE: Analytics | undefined + + ROOMS: R2Bucket + ROOMS_HISTORY_EPHEMERAL: R2Bucket + + // env vars + SUPABASE_URL: string | undefined + SUPABASE_KEY: string | undefined + + APP_ORIGIN: string | undefined + + TLDRAW_ENV: string | undefined + SENTRY_DSN: string | undefined + IS_LOCAL: string | undefined +} diff --git a/apps/dotcom-worker/src/lib/utils/createSupabaseClient.ts b/apps/dotcom-worker/src/lib/utils/createSupabaseClient.ts new file mode 100644 index 000000000..f9b404839 --- /dev/null +++ b/apps/dotcom-worker/src/lib/utils/createSupabaseClient.ts @@ -0,0 +1,12 @@ +import { createClient } from '@supabase/supabase-js' +import { Environment } from '../types' + +export function createSupabaseClient(env: Environment) { + return env.SUPABASE_URL && env.SUPABASE_KEY + ? createClient(env.SUPABASE_URL, env.SUPABASE_KEY) + : console.warn('No supabase credentials, loading from supabase disabled') +} + +export function noSupabaseSorry() { + return new Response(JSON.stringify({ error: true, message: 'Could not create supabase client' })) +} diff --git a/apps/dotcom-worker/src/lib/utils/fourOhFour.ts b/apps/dotcom-worker/src/lib/utils/fourOhFour.ts new file mode 100644 index 000000000..c443e0c52 --- /dev/null +++ b/apps/dotcom-worker/src/lib/utils/fourOhFour.ts @@ -0,0 +1,5 @@ +export async function fourOhFour() { + return new Response('Not found', { + status: 404, + }) +} diff --git a/apps/dotcom-worker/src/lib/utils/getSnapshotsTable.ts b/apps/dotcom-worker/src/lib/utils/getSnapshotsTable.ts new file mode 100644 index 000000000..6a6d5ab13 --- /dev/null +++ b/apps/dotcom-worker/src/lib/utils/getSnapshotsTable.ts @@ -0,0 +1,10 @@ +import { Environment } from '../types' + +export function getSnapshotsTable(env: Environment) { + if (env.TLDRAW_ENV === 'production') { + return 'snapshots' + } else if (env.TLDRAW_ENV === 'staging' || env.TLDRAW_ENV === 'preview') { + return 'snapshots_staging' + } + return 'snapshots_dev' +} diff --git a/apps/dotcom-worker/src/lib/utils/roomIdIsTooLong.ts b/apps/dotcom-worker/src/lib/utils/roomIdIsTooLong.ts new file mode 100644 index 000000000..5fb33b731 --- /dev/null +++ b/apps/dotcom-worker/src/lib/utils/roomIdIsTooLong.ts @@ -0,0 +1,9 @@ +const MAX_ROOM_ID_LENGTH = 128 + +export function isRoomIdTooLong(roomId: string) { + return roomId.length > MAX_ROOM_ID_LENGTH +} + +export function roomIdIsTooLong() { + return new Response('Room ID too long', { status: 400 }) +} diff --git a/apps/dotcom-worker/src/lib/utils/throttle.ts b/apps/dotcom-worker/src/lib/utils/throttle.ts new file mode 100644 index 000000000..4adca0ef4 --- /dev/null +++ b/apps/dotcom-worker/src/lib/utils/throttle.ts @@ -0,0 +1,19 @@ +export function throttle(fn: () => void, limit: number) { + let waiting = false + let invokeOnTail = false + return () => { + if (!waiting) { + fn() + waiting = true + setTimeout(() => { + waiting = false + if (invokeOnTail) { + invokeOnTail = false + fn() + } + }, limit) + } else { + invokeOnTail = true + } + } +} diff --git a/apps/dotcom-worker/src/lib/utils/validateSnapshot.ts b/apps/dotcom-worker/src/lib/utils/validateSnapshot.ts new file mode 100644 index 000000000..ac37b9554 --- /dev/null +++ b/apps/dotcom-worker/src/lib/utils/validateSnapshot.ts @@ -0,0 +1,50 @@ +import { SerializedSchema, SerializedStore } from '@tldraw/store' +import { TLRecord } from '@tldraw/tlschema' +import { schema } from '@tldraw/tlsync' +import { Result, objectMapEntries } from '@tldraw/utils' + +type SnapshotRequestBody = { + schema: SerializedSchema + snapshot: SerializedStore +} + +export function validateSnapshot( + body: SnapshotRequestBody +): Result, string> { + // Migrate the snapshot using the provided schema + const migrationResult = schema.migrateStoreSnapshot({ store: body.snapshot, schema: body.schema }) + if (migrationResult.type === 'error') { + return Result.err(migrationResult.reason) + } + + try { + for (const [id, record] of objectMapEntries(migrationResult.value)) { + // Throw if any records have mis-matched ids + if (id !== record.id) { + throw new Error(`Record id ${id} does not match record id ${record.id}`) + } + + // Get the corresponding record type from the provided schema + const recordType = schema.types[record.typeName] + + // Throw if any records have missing record type definitions + if (!recordType) { + throw new Error(`Missing definition for record type ${record.typeName}`) + } + + // Remove all records whose record type scopes are not 'document'. + // This is legacy cleanup code. + if (recordType.scope !== 'document') { + delete migrationResult.value[id] + continue + } + + // Validate the record + recordType.validate(record) + } + } catch (e: any) { + return Result.err(e.message) + } + + return Result.ok(migrationResult.value) +} diff --git a/apps/dotcom-worker/src/lib/worker.ts b/apps/dotcom-worker/src/lib/worker.ts new file mode 100644 index 000000000..9c415186c --- /dev/null +++ b/apps/dotcom-worker/src/lib/worker.ts @@ -0,0 +1,103 @@ +/// +/// +import { Router, createCors } from 'itty-router' +import { env } from 'process' +import Toucan from 'toucan-js' +import { createRoom } from './routes/createRoom' +import { createRoomSnapshot } from './routes/createRoomSnapshot' +import { forwardRoomRequest } from './routes/forwardRoomRequest' +import { getRoomHistory } from './routes/getRoomHistory' +import { getRoomHistorySnapshot } from './routes/getRoomHistorySnapshot' +import { getRoomSnapshot } from './routes/getRoomSnapshot' +import { joinExistingRoom } from './routes/joinExistingRoom' +import { Environment } from './types' +import { fourOhFour } from './utils/fourOhFour' +export { TLDrawDurableObject } from './TLDrawDurableObject' + +const { preflight, corsify } = createCors({ + origins: Object.assign([], { includes: (origin: string) => isAllowedOrigin(origin) }), +}) + +const router = Router() + .all('*', preflight) + .all('*', blockUnknownOrigins) + .post('/new-room', createRoom) + .post('/snapshots', createRoomSnapshot) + .get('/snapshot/:roomId', getRoomSnapshot) + .get('/r/:roomId', joinExistingRoom) + .get('/r/:roomId/history', getRoomHistory) + .get('/r/:roomId/history/:timestamp', getRoomHistorySnapshot) + .post('/r/:roomId/restore', forwardRoomRequest) + .all('*', fourOhFour) + +const Worker = { + fetch(request: Request, env: Environment, context: ExecutionContext) { + const sentry = new Toucan({ + dsn: env.SENTRY_DSN, + context, // Includes 'waitUntil', which is essential for Sentry logs to be delivered. Modules workers do not include 'request' in context -- you'll need to set it separately. + request, // request is not included in 'context', so we set it here. + allowedHeaders: ['user-agent'], + allowedSearchParams: /(.*)/, + }) + + return router + .handle(request, env, context) + .catch((err) => { + console.error(err) + sentry.captureException(err) + + return new Response('Something went wrong', { + status: 500, + statusText: 'Internal Server Error', + }) + }) + .then((response) => { + const setCookies = response.headers.getAll('set-cookie') + // unfortunately corsify mishandles the set-cookie header, so + // we need to manually add it back in + const result = corsify(response) + if ([...setCookies].length === 0) { + return result + } + const newResponse = new Response(result.body, result) + newResponse.headers.delete('set-cookie') + // add cookies from original response + for (const cookie of setCookies) { + newResponse.headers.append('set-cookie', cookie) + } + return newResponse + }) + }, +} + +function isAllowedOrigin(origin: string) { + if (origin === 'http://localhost:3000') return true + if (origin === 'http://localhost:5420') return true + if (origin.endsWith('.tldraw.com')) return true + if (origin.endsWith('-tldraw.vercel.app')) return true + return false +} + +async function blockUnknownOrigins(request: Request) { + // allow requests for the same origin (new rewrite routing for SPA) + if (request.headers.get('sec-fetch-site') === 'same-origin') { + return undefined + } + + if (new URL(request.url).pathname === '/auth/callback') { + // allow auth callback because we use the special cookie to verify + // the request + return undefined + } + + const origin = request.headers.get('origin') + if (env.IS_LOCAL !== 'true' && (!origin || !isAllowedOrigin(origin))) { + console.error('Attempting to connect from an invalid origin:', origin, env, request) + return new Response('Not allowed', { status: 403 }) + } + + // origin doesn't match, so we can continue + return undefined +} + +export default Worker diff --git a/apps/dotcom-worker/tsconfig.json b/apps/dotcom-worker/tsconfig.json new file mode 100644 index 000000000..050479e54 --- /dev/null +++ b/apps/dotcom-worker/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../config/tsconfig.base.json", + "include": ["src", "scripts"], + "exclude": ["node_modules", "dist", ".tsbuild*"], + "compilerOptions": { + "noEmit": true, + "emitDeclarationOnly": false + }, + "references": [ + { "path": "../../packages/tlsync" }, + { "path": "../../packages/tlschema" }, + { "path": "../../packages/validate" }, + { "path": "../../packages/store" }, + { "path": "../../packages/utils" } + ] +} diff --git a/apps/dotcom-worker/wrangler.toml b/apps/dotcom-worker/wrangler.toml new file mode 100644 index 000000000..98368c816 --- /dev/null +++ b/apps/dotcom-worker/wrangler.toml @@ -0,0 +1,116 @@ +main = "src/lib/worker.ts" +compatibility_date = "2023-10-16" + +[dev] +port = 8787 + +# these migrations are append-only. you can't change them. if you do need to change something, do so +# by creating new migrations +[[migrations]] +tag = "v1" # Should be unique for each entry +new_classes = ["TLDrawDurableObject"] + +[[migrations]] +tag = "v2" +new_classes = ["TLProWorkspaceDurableObject"] + +[[migrations]] +tag = "v3" +deleted_classes = ["TLProWorkspaceDurableObject"] + +[[analytics_engine_datasets]] +binding = "MEASURE" + +#################### Environment names #################### +# dev should never actually get deployed anywhere +[env.dev] +name = "dev-tldraw-multiplayer" + +# we don't have a hard-coded name for preview. we instead have to generate it at build time and append it to this file. + +# staging is the same as a preview on main: +[env.staging] +name = "main-tldraw-multiplayer" + +# production gets the proper name +[env.production] +name = "tldraw-multiplayer" + +#################### Durable objects #################### +# durable objects have the same configuration in all environments: +[[env.dev.durable_objects.bindings]] +name = "TLDR_DOC" +class_name = "TLDrawDurableObject" + +[durable_objects] +bindings = [ + { name = "TLDR_DOC", class_name = "TLDrawDurableObject" }, +] + +[[env.preview.durable_objects.bindings]] +name = "TLDR_DOC" +class_name = "TLDrawDurableObject" + +[[env.staging.durable_objects.bindings]] +name = "TLDR_DOC" +class_name = "TLDrawDurableObject" + +[[env.production.durable_objects.bindings]] +name = "TLDR_DOC" +class_name = "TLDrawDurableObject" + +#################### Analytics engine #################### +# durable objects have the same configuration in all environments: +[[env.dev.analytics_engine_datasets]] +binding = "MEASURE" + +[[env.preview.analytics_engine_datasets]] +binding = "MEASURE" + +[[env.staging.analytics_engine_datasets]] +binding = "MEASURE" + +[[env.production.analytics_engine_datasets]] +binding = "MEASURE" + +#################### Rooms R2 bucket #################### +# in dev, we write to the preview bucket and need a `preview_bucket_name` +[[env.dev.r2_buckets]] +binding = "ROOMS" +bucket_name = "rooms-preview" +preview_bucket_name = "rooms-preview" + +# in preview and staging we write to the preview bucket +[[env.preview.r2_buckets]] +binding = "ROOMS" +bucket_name = "rooms-preview" + +[[env.staging.r2_buckets]] +binding = "ROOMS" +bucket_name = "rooms-preview" + +# in production, we write to the main bucket +[[env.production.r2_buckets]] +binding = "ROOMS" +bucket_name = "rooms" + +#################### Rooms History bucket #################### +# in dev, we write to the preview bucket and need a `preview_bucket_name` +[[env.dev.r2_buckets]] +binding = "ROOMS_HISTORY_EPHEMERAL" +bucket_name = "rooms-history-ephemeral-preview" +preview_bucket_name = "rooms-history-ephemeral-preview" + +# in preview and staging we write to the preview bucket +[[env.preview.r2_buckets]] +binding = "ROOMS_HISTORY_EPHEMERAL" +bucket_name = "rooms-history-ephemeral-preview" + +[[env.staging.r2_buckets]] +binding = "ROOMS_HISTORY_EPHEMERAL" +bucket_name = "rooms-history-ephemeral-preview" + +# in production, we write to the main bucket +[[env.production.r2_buckets]] +binding = "ROOMS_HISTORY_EPHEMERAL" +bucket_name = "rooms-history-ephemeral" diff --git a/apps/dotcom/.gitignore b/apps/dotcom/.gitignore new file mode 100644 index 000000000..e125cc140 --- /dev/null +++ b/apps/dotcom/.gitignore @@ -0,0 +1,42 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# PWA build artifacts +/public/*.js +/dev-dist + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo + +# Sentry +.sentryclirc diff --git a/apps/dotcom/CHANGELOG.md b/apps/dotcom/CHANGELOG.md new file mode 100644 index 000000000..d3f40ce68 --- /dev/null +++ b/apps/dotcom/CHANGELOG.md @@ -0,0 +1,221 @@ +# app + +## 2.0.0-alpha.11 + +### Patch Changes + +- Updated dependencies + - @tldraw/editor@2.0.0-alpha.11 + - @tldraw/polyfills@2.0.0-alpha.10 + - @tldraw/tlsync-client@2.0.0-alpha.11 + - @tldraw/tlvalidate@2.0.0-alpha.10 + - @tldraw/ui@2.0.0-alpha.11 + - @tldraw/utils@2.0.0-alpha.10 + - @tldraw/app-shared@2.0.0-alpha.11 + +## 2.0.0-alpha.10 + +### Patch Changes + +- Updated dependencies [4b4399b6e] + - @tldraw/polyfills@2.0.0-alpha.9 + - @tldraw/tlsync-client@2.0.0-alpha.10 + - @tldraw/tlvalidate@2.0.0-alpha.9 + - @tldraw/ui@2.0.0-alpha.10 + - @tldraw/utils@2.0.0-alpha.9 + - @tldraw/editor@2.0.0-alpha.10 + - @tldraw/app-shared@2.0.0-alpha.10 + +## 2.0.0-alpha.9 + +### Patch Changes + +- Release day! +- Updated dependencies + - @tldraw/app-shared@2.0.0-alpha.9 + - @tldraw/editor@2.0.0-alpha.9 + - @tldraw/polyfills@2.0.0-alpha.8 + - @tldraw/tlsync-client@2.0.0-alpha.9 + - @tldraw/tlvalidate@2.0.0-alpha.8 + - @tldraw/ui@2.0.0-alpha.9 + - @tldraw/utils@2.0.0-alpha.8 + +## 2.0.0-alpha.8 + +### Patch Changes + +- Updated dependencies [23dd81cfe] + - @tldraw/editor@2.0.0-alpha.8 + - @tldraw/tlsync-client@2.0.0-alpha.8 + - @tldraw/ui@2.0.0-alpha.8 + - @tldraw/app-shared@2.0.0-alpha.8 + +## 2.0.0-alpha.7 + +### Patch Changes + +- Bug fixes. +- Updated dependencies + - @tldraw/app-shared@2.0.0-alpha.7 + - @tldraw/editor@2.0.0-alpha.7 + - @tldraw/polyfills@2.0.0-alpha.7 + - @tldraw/tlsync-client@2.0.0-alpha.7 + - @tldraw/tlvalidate@2.0.0-alpha.7 + - @tldraw/ui@2.0.0-alpha.7 + - @tldraw/utils@2.0.0-alpha.7 + +## 2.0.0-alpha.6 + +### Patch Changes + +- Add licenses. +- Updated dependencies + - @tldraw/app-shared@2.0.0-alpha.6 + - @tldraw/editor@2.0.0-alpha.6 + - @tldraw/polyfills@2.0.0-alpha.6 + - @tldraw/tlsync-client@2.0.0-alpha.6 + - @tldraw/tlvalidate@2.0.0-alpha.6 + - @tldraw/ui@2.0.0-alpha.6 + - @tldraw/utils@2.0.0-alpha.6 + +## 2.0.0-alpha.5 + +### Patch Changes + +- Add CSS files to tldraw/tldraw. +- Updated dependencies + - @tldraw/app-shared@2.0.0-alpha.5 + - @tldraw/editor@2.0.0-alpha.5 + - @tldraw/polyfills@2.0.0-alpha.5 + - @tldraw/tlsync-client@2.0.0-alpha.5 + - @tldraw/tlvalidate@2.0.0-alpha.5 + - @tldraw/ui@2.0.0-alpha.5 + - @tldraw/utils@2.0.0-alpha.5 + +## 2.0.0-alpha.4 + +### Patch Changes + +- Add children to tldraw/tldraw +- Updated dependencies + - @tldraw/app-shared@2.0.0-alpha.4 + - @tldraw/editor@2.0.0-alpha.4 + - @tldraw/polyfills@2.0.0-alpha.4 + - @tldraw/tlsync-client@2.0.0-alpha.4 + - @tldraw/tlvalidate@2.0.0-alpha.4 + - @tldraw/ui@2.0.0-alpha.4 + - @tldraw/utils@2.0.0-alpha.4 + +## 2.0.0-alpha.3 + +### Patch Changes + +- Change permissions. +- Updated dependencies + - @tldraw/app-shared@2.0.0-alpha.3 + - @tldraw/editor@2.0.0-alpha.3 + - @tldraw/polyfills@2.0.0-alpha.3 + - @tldraw/tlsync-client@2.0.0-alpha.3 + - @tldraw/tlvalidate@2.0.0-alpha.3 + - @tldraw/ui@2.0.0-alpha.3 + - @tldraw/utils@2.0.0-alpha.3 + +## 2.0.0-alpha.2 + +### Patch Changes + +- Add tldraw, editor +- Updated dependencies + - @tldraw/app-shared@2.0.0-alpha.2 + - @tldraw/editor@2.0.0-alpha.2 + - @tldraw/polyfills@2.0.0-alpha.2 + - @tldraw/tlsync-client@2.0.0-alpha.2 + - @tldraw/tlvalidate@2.0.0-alpha.2 + - @tldraw/ui@2.0.0-alpha.2 + - @tldraw/utils@2.0.0-alpha.2 + +## 0.1.0-alpha.11 + +### Patch Changes + +- Fix stale reactors. +- Updated dependencies + - @tldraw/app-shared@0.1.0-alpha.11 + - @tldraw/polyfills@0.1.0-alpha.11 + - @tldraw/tldraw-beta@0.1.0-alpha.11 + - @tldraw/tlsync-client@0.1.0-alpha.11 + - @tldraw/tlvalidate@0.1.0-alpha.11 + - @tldraw/ui@0.1.0-alpha.11 + - @tldraw/utils@0.1.0-alpha.11 + +## 0.1.0-alpha.10 + +### Patch Changes + +- Fix type export bug. +- Updated dependencies + - @tldraw/app-shared@0.1.0-alpha.10 + - @tldraw/polyfills@0.1.0-alpha.10 + - @tldraw/tldraw-beta@0.1.0-alpha.10 + - @tldraw/tlsync-client@0.1.0-alpha.10 + - @tldraw/tlvalidate@0.1.0-alpha.10 + - @tldraw/ui@0.1.0-alpha.10 + - @tldraw/utils@0.1.0-alpha.10 + +## 0.1.0-alpha.9 + +### Patch Changes + +- Fix import bugs. +- Updated dependencies + - @tldraw/app-shared@0.1.0-alpha.9 + - @tldraw/polyfills@0.1.0-alpha.9 + - @tldraw/tldraw-beta@0.1.0-alpha.9 + - @tldraw/tlsync-client@0.1.0-alpha.9 + - @tldraw/tlvalidate@0.1.0-alpha.9 + - @tldraw/ui@0.1.0-alpha.9 + - @tldraw/utils@0.1.0-alpha.9 + +## 0.1.0-alpha.8 + +### Patch Changes + +- Changes validation requirements, exports validation helpers. +- Updated dependencies + - @tldraw/app-shared@0.1.0-alpha.8 + - @tldraw/polyfills@0.1.0-alpha.8 + - @tldraw/tldraw-beta@0.1.0-alpha.8 + - @tldraw/tlsync-client@0.1.0-alpha.8 + - @tldraw/tlvalidate@0.1.0-alpha.8 + - @tldraw/ui@0.1.0-alpha.8 + - @tldraw/utils@0.1.0-alpha.8 + +## 0.1.0-alpha.7 + +### Patch Changes + +- - Pre-pre-release update +- Updated dependencies + - @tldraw/app-shared@0.1.0-alpha.7 + - @tldraw/polyfills@0.1.0-alpha.7 + - @tldraw/tldraw-beta@0.1.0-alpha.7 + - @tldraw/tlsync-client@0.1.0-alpha.7 + - @tldraw/tlvalidate@0.1.0-alpha.7 + - @tldraw/ui@0.1.0-alpha.7 + - @tldraw/utils@0.1.0-alpha.7 + +## 0.0.2-alpha.1 + +### Patch Changes + +- Fix error with HMR +- Updated dependencies + - @tldraw/polyfills@0.0.2-alpha.1 + +## 0.0.2-alpha.0 + +### Patch Changes + +- Initial release +- Updated dependencies + - @tldraw/polyfills@0.0.2-alpha.0 diff --git a/apps/dotcom/README.md b/apps/dotcom/README.md new file mode 100644 index 000000000..bc69357d2 --- /dev/null +++ b/apps/dotcom/README.md @@ -0,0 +1,80 @@ +# Project overview + +This project is a Next.js application which contains the **tldraw free** as well as the **tldraw pro** applications. We are currently using the Next.js 13 option of having both `pages` (tldraw free) and `app` (tldraw pro) directory inside the same app. We did this since the free offering is the continuation of a Next.js version 12 app and it allowed us to combine it with the new App router option from Next.js 13 for tldraw pro without having to do a full migration to App router. + +We also split the supabase into two projects: + +- `tldraw-v2` for tldraw free where we mainly store the snapshots data +- `tldraw-pro` for tldraw pro which holds all the relational data that the pro version requires + +On top of that we also use R2 for storing the documents data. + +# How to run the project + +## Tldraw pro + +The development of tldraw pro happens against a local supabase instance. To set that up, you'll +first need to [install & start docker](https://www.docker.com/products/docker-desktop/). + +Once docker is started & you've run `yarn` to install tldraw's dependencies, the rest should be +handled automatically. Running `yarn dev-app` will: + +1. Start a local instance of supabase +2. Run any database migrations +3. Update your .env.local file with credentials for your local supabase instance +4. Start tldraw + +The [supabase local development docs](https://supabase.com/docs/guides/cli/local-development) are a +good reference. When working on tldraw, the `supabase` command is available by running `yarn +supabase` in the `apps/app` directory e.g. `yarn supabase status`. + +When you're finished, we don't stop supabase because it takes a while each time we start and stop +it. Run `yarn supabase stop` to stop it manually. + +If you write any new database migrations, you can apply those with `yarn supabase migration up`. + +## Some helpers + +1. You can see your db schema at the `Studio URL` printed out in the step 2. +2. If you ever need to reset your local supabase instance you can run `supabase db reset` in the root of `apps/app` project. +3. The production version of Supabase sends out emails for certain events (email confirmation link, password reset link, etc). In local development you can find these emails at the `Inbucket URL` printed out in the step 2. + +## Tldraw free + +The development of tldraw free happens against the production supabase instance. We only store snapshots data to one of the three tables, depending on the environment. The tables are: + +- `snapshots` - for production +- `snapshots_staging` - for staging +- `snapshots_dev` - for development + +For local development you need to add the following env variables to `.env.local`: + +- `SUPABASE_URL` - use the production supabase url +- `SUPABASE_KEY` - use the production supabase anon key + +Once you have the environment variables set up you can run `yarn dev-app` from the root folder of our repo to start developing. + +## Running database tests + +You need to have a psql client [installed](https://www.timescale.com/blog/how-to-install-psql-on-mac-ubuntu-debian-windows/). You can then run `yarn test-supabase` to run [db tests](https://supabase.com/docs/guides/database/extensions/pgtap). + +## Sending emails + +We are using [Resend](https://resend.com/) for sending emails. It allows us to write emails as React components. Emails live in a separate app `apps/tl-emails`. + +Right now we are only using Resend via Supabase, but in the future we will probably also include Resend in our application and send emails directly. + +The development workflow is as follows: + +### 1. Creating / updating an email template + To start the development server for email run `yarn dev-email` from the root folder of our repo. You can then open [http://localhost:3333](http://localhost:3333) to see the result. This allows for quick local development of email templates. + +Any images you want to use in the email should be uploaded to supabase to the `email` bucket. + +Supabase provides some custom params (like the magic link url) that we can insert into our email, [check their website](https://supabase.com/dashboard/project/faafybhoymfftncjttyq/auth/templates) for more info. + +### 2. Generating the `html` version of the email +Once you are happy with the email template you can run `yarn build-email` from the root folder of our repo. This will generate the `html` version of the email and place it in `apps/tl-emails/out` folder. + +### 3. Updating the template in Supabase +Once you have the `html` version of the email you can copy it into the Supabase template editor. You can find the templates [here](https://supabase.com/dashboard/project/faafybhoymfftncjttyq/auth/templates). diff --git a/apps/dotcom/decs.d.ts b/apps/dotcom/decs.d.ts new file mode 100644 index 000000000..430c960b1 --- /dev/null +++ b/apps/dotcom/decs.d.ts @@ -0,0 +1,17 @@ +declare namespace React { + interface HTMLAttributes { + /** + * Indicates the browser should ignore the element and its contents in terms of interaction. + * This is a boolean attribute but isn't properly supported by react yet - pass "" to enable + * it, or undefined to disable it. + * + * https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inert + */ + inert?: '' + } +} + +declare module '*.svg' { + const content: React.FunctionComponent> + export default content +} diff --git a/apps/dotcom/index.html b/apps/dotcom/index.html new file mode 100644 index 000000000..537481bcb --- /dev/null +++ b/apps/dotcom/index.html @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tldraw + + + +
+ + + + diff --git a/apps/dotcom/package.json b/apps/dotcom/package.json new file mode 100644 index 000000000..cfb6900a6 --- /dev/null +++ b/apps/dotcom/package.json @@ -0,0 +1,64 @@ +{ + "name": "dotcom", + "description": "The production app for tldraw.", + "version": "2.0.0-alpha.11", + "private": true, + "packageManager": "yarn@3.5.0", + "author": { + "name": "tldraw GB Ltd.", + "email": "hello@tldraw.com" + }, + "browserslist": [ + "defaults" + ], + "scripts": { + "dev": "yarn run -T tsx scripts/dev-app.ts", + "build": "yarn run -T tsx scripts/build.ts", + "start": "VITE_PREVIEW=1 yarn run -T tsx scripts/dev-app.ts", + "lint": "yarn run -T tsx ../../scripts/lint.ts", + "test": "lazy inherit" + }, + "dependencies": { + "@radix-ui/react-popover": "1.0.6-rc.5", + "@sentry/integrations": "^7.34.0", + "@sentry/react": "^7.77.0", + "@tldraw/assets": "workspace:*", + "@tldraw/tldraw": "workspace:*", + "@tldraw/tlsync": "workspace:*", + "@vercel/analytics": "^1.0.1", + "browser-fs-access": "^0.33.0", + "idb": "^7.1.1", + "nanoid": "4.0.2", + "qrcode": "^1.5.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-helmet-async": "^1.3.0", + "react-router-dom": "^6.17.0" + }, + "devDependencies": { + "@sentry/cli": "^2.25.0", + "@types/qrcode": "^1.5.0", + "@types/react": "^18.2.33", + "@typescript-eslint/utils": "^5.59.0", + "@vitejs/plugin-react-swc": "^3.5.0", + "dotenv": "^16.3.1", + "fast-glob": "^3.3.1", + "lazyrepo": "0.0.0-alpha.27", + "vite": "^5.0.0", + "vite-plugin-pwa": "^0.17.0", + "ws": "^8.13.0" + }, + "jest": { + "preset": "config/jest/node", + "roots": [ + "" + ], + "testEnvironment": "jsdom", + "transformIgnorePatterns": [ + "node_modules/(?!(nanoid|nanoevents)/)" + ], + "setupFiles": [ + "./setupTests.js" + ] + } +} diff --git a/apps/dotcom/public/404-Sad-tldraw.svg b/apps/dotcom/public/404-Sad-tldraw.svg new file mode 100644 index 000000000..5e0ee36aa --- /dev/null +++ b/apps/dotcom/public/404-Sad-tldraw.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/apps/dotcom/public/Shantell_Sans-Tldrawish.woff2 b/apps/dotcom/public/Shantell_Sans-Tldrawish.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..7a50fc98d5cbd79a159213125db296f6510d1c18 GIT binary patch literal 152980 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 Z)rDD`keSCLe1g9^fS0?ETOBDP{T~1a6_)@2 literal 0 HcmV?d00001 diff --git a/apps/dotcom/public/android-chrome-512x512.png b/apps/dotcom/public/android-chrome-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..b637f9153b758d908a737413861ecafe2799df8d GIT binary patch literal 6676 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 literal 0 HcmV?d00001 diff --git a/apps/dotcom/public/android-chrome-maskable-192x192.png b/apps/dotcom/public/android-chrome-maskable-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..7928cb53787b9d09e2a7d927b70fa302a1ca184e GIT binary patch literal 1852 zcmcgt`&Uy}7QP8LkOTq=V1Y<5AShB%=-^{X3HJst5Q9VvRTzVzLJK}qSt5>0IDY!{Y{Kl{zzsN` zc_-wageb@(KXh@G1~7pOGGGCZnWkWeHiGpmyx^Pv5%7m)QR>%UZ`rkL*XwuhzFPY6 zL3~_X+)@mmPkS&hIH>FCiG=-+yofQZtX#PF^Uqrc2M1rz&d%xrn;IGpq?|vm>+aqU z^Y1@Wka^{q7yYR37qbHiQL>Sdzk3CRj2=7kOKU4UJUVKHgG@%9PWO6gsafeFLV~HQ zC(oRboR~1KW3gD;MXgqgq*-O!*3{IHuAK2En!%lILeBy@w8M-}Y#knshPQdaNKQ_U zif|h4y=7x#kXD&!_ZO{**1getr$Ra)i~4r@j}a#YJ@nx9njkta z!`UhmF(p;cOJkoteJXds6dv1cU!j4z*H)|PAg1wV@Xhzn%GSx~%Eh1|wZn}N86E0X z2T%}a*>j62+M{Ur0|B6^v^0Jy*qTY!`-9b#G4y6k<4o9F_ltslmCqDZ4U@7H?FDCJ>6dY)=P) z|4x^`IrypVCQ3_7)05A=`HwrUT%DmHU8d*HMn|ur1aIfRRQXKL&RTc) zoHb8cUkn|}X3VhEhNDk5yf0L%)i1b5`?S8fisF(Il8vx^KD^4jn8UZ}@cB%bmiFn9 zfr4|A;iXzL+E~t>N-*0Vh>}v<7Zrxx=7yyi}`n(n)X?m@b?mP z3%73HeseTpA?kid?(HMVKF(}q92b?*5Bwcwmz;Tsav(~;xpHTl#hB=(qZge28FB%H z`aBPk&XN&e$K!yK($bF}QxIwAwJfVDbB!c38p3a?u&5v8PMoKH>nbO65~*1F*3vc# zLP(e)T`s!jvl=gZp1HDSxo3&N^id@hm2C!nWdf#5kU~ZXNoFHIf$8MO==6n!C+19Y zePd(PoRNY6C3s&_klRi}8wv%aiON!`7i~CsHGS}Lc4Nu?FO$0@FUE%0B~aNTRDAz% zC^GJ5=0l{jbwPI=iRkO@n*BMwEA3gXV73T^Gnd@t?af%tS1CfFK0b4NWMq9%IY52I z?%OyKYG1OAUnpmS^gK5AMpf%lYD&2JjmP!sNgi#n7U$2&(>Cqldj3?K53_28wtOqC z%KDB=4CxXcvS#WJ5BRq>u8=m%lnuG$O03>;%oV(n;9L-C#HX_S+DQq1ZB^LgSp^yG z%?-J|FDtSCt^`RT5%oV1$JDld6?PTs5zk8OIWB?f_zZF)V48YS(h}W8--o|-IR6FB zwUUF;yOiNiJ^A8Xvx_`v9X~c!ekf($$V(RuL5B|P1x%A`b8`<<_AjNad(X!TCyc)9&#o>uyxs(cndWX2gUQl`#9X_Cli6IG zhvZ6c>MQqUQ}*)GzcTdq_kZVwtmfT? z6Dhd>$HEIP51jd^Ut5Rs!%1=VldaQSkC=)3EiD_YhkMNGwnUV$JG6mf)aMO$z@;sb lQZn%Wvi-o8SfXJ1sGzv}R^2C~-soosgoW%6u4M|4{{T#`9JBxc literal 0 HcmV?d00001 diff --git a/apps/dotcom/public/android-chrome-maskable-512x512.png b/apps/dotcom/public/android-chrome-maskable-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..db17ee0099c3268efb168bfa11a3a5597c8e7267 GIT binary patch literal 5363 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 literal 0 HcmV?d00001 diff --git a/apps/dotcom/public/android-chrome-maskable-beta-512x512.png b/apps/dotcom/public/android-chrome-maskable-beta-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..d746be636e3051c0bda8d2919c2d09cd5dceb672 GIT binary patch literal 66436 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

l< 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≠`f)9n%!9!!N~Uh(Z1Xl$Bkj zG~YECLN4AFeIW24RgEQj@E#)|J~o!{-{x%t4DO_%q0wQtdsG4QvJKc5x!Y19Sg!c) zO9sd!%yyUq)sve($ThzxZUcDD&6rp!UqJY?^pTx& zIdO(Jh~$~BNT`W)=D$C`)f=p{&SCL*w?FK<3L(5(y%w%-F^11-Zee-!81tE(Cmw5C%K%D?6?A7zYHK ziSzP~ln#KSM!Wu+DPhYlZo{iump@!qz_r9Cck@MySdYeWvG#xna`#oC@N<7GL+t(; zE%Ji>&W2(lYikqEZJZ8PWBNFzJd5mdG|~y)C+rx>Yf5U|G;(xry~vxyuixR{?vsFk~A^s;u5UuveW>!OADjd{fvf9baqW0&n=F&s&x= zNC8g}%S-!es2}^Lo(E2YR{qaEl~>+O!yKGkKLRMmedd0t!kgQ6YW>p5Fo>|b{Y<=@ba6kI4ROn-pTyn9lBa7KztJ#-(1g@yfB)(bda z4^nt`;thMuTs-bgnt;)XYF`=O=PFlfAJMg2(u%)GncQAAC;z%mN*yN69DeV^f(7F` z*)G)ods0#mqkmrH^DO?=O&L6#j@UL0!D%XK8mU^=#J2yq}kfa$H9x_C0 z{0Mes6t*KRq2OVnSN|Fg9g$9Z8LKV%6-)kr#}e}<`^TwtVeh2OM`B^yZ7D*tOHaG) zr8q8DW-JRaZy_cMEAlI*vEg);opWESJrW*!03k9(41E;*x^Q5lHr28}&@uP)Qb_u% z#3DjP38^J}pU-ps-zxBN^x*Sm0$%N@p89!F#M}$iRV%${e;ifgyRn_ZxQnR2O(snS zTxYVHJhVyV;Zb%8<_liz=x=Lo(arEuhffA$={sohkLRrQ%JtMth>Tt261&3W2(W~C zzW$VqR0a#_?7KJIhaohj?U>tOR!o~xp7Ry>35dr^TF zXC!7D8np9fnAIv_FC{8s}KXpo*^Hd9iB@~I{a~0+DjX7{DQoG0g*U?o5 zG~spqjqZ>J0Rich?v(B>X_1r;$J_kSv&;5+Q0d97gQ{rT^E$YHEe0jSK3lJD5jN;hQdiRs{H_*aD; ze59g-1|Eht7lJ6$PyQU~6YEMJ9)Tv|8dn0czwNY^(lnUg%x8B=?DICS5}S;>)w|ae zE85Z<;8092k@@@S*`n64_3ezqF+redsSwmBg@J*$I;q!sFOiIAJ&em@Gv}?vcU|`Y zQbbNUS6S_EuKt3UZ*GXOu?E1Io@$9rwI7SA(-Z14nC4b7RyH4PXY`QTQsD>lI1`O{ z(!5t>2`a>c#GcBfEyA!(f5iF`y$9B;dCUE&oJGZH3t*wLFC9O+Ed{4G@NF-I1J{>_ zEYvlGFW0ijH}gp1|CZ8OT!-q@rHAEv17at|uJg{8s<`0hgO84{(4Mf`S92E%a2Nz= zHab<&6?)g+oG7eRX=Zg0aL15p=K$bkd>2=`y5z7ECeOT;@$T;iip?%qWJfsf0-l

K-Dtq9L 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 P*hgMkt5f=l}k% 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 f>OtS8=-UooO>3LdSO* + + + + + + + + + + diff --git a/apps/dotcom/public/flat.png b/apps/dotcom/public/flat.png new file mode 100644 index 0000000000000000000000000000000000000000..bfffe50eaa0033c7bfc4d39805f92bcbab622182 GIT binary patch literal 6621 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 literal 0 HcmV?d00001 diff --git a/apps/dotcom/public/github-hero-dark.png b/apps/dotcom/public/github-hero-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..6a0acdfc4c64263443421b6724d146065d9012ad GIT binary patch literal 224488 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

-2sdI6`B{Wcs`8 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%0DQ?IPf; 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 literal 0 HcmV?d00001 diff --git a/apps/dotcom/public/github-hero-light.png b/apps/dotcom/public/github-hero-light.png new file mode 100644 index 0000000000000000000000000000000000000000..855c7d6e466bc54f8cf2b84bc216d0ceb7e6e14e GIT binary patch literal 224246 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Ⓢg;#W_g-yE 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

`p-FKkubU(ha73mGOTrlHgR) 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

Yk&4_^a5Jm^1FS&+Px zYaQKo0ZM!;p?iM#Y#Iu1E!w@q$uB?@APNv*^)!KK1gKL4wc+Y~K!92#R<_xxlc=8l-tH72 zWWq*(-%#bA<74BnK%ue+*!WLfbn`DI1^C|1{a+e0zzb_p66}MODJwt}ARqxj3ps}o zKAJPNe#n)a;ZV=1bad)ks6Xb&!&!0sBu6DSUs7GY3O<4h=009XI728#fGeB&5t+#)0w72PBVRkz&_{nh1 zun{0zOs3dgcySjB5ZbaUA9h!a+!hw3D$!WdY=&?CE40$}T;S!M`m@3eaOHaB)7Vp1 zfG9vf0)o*kGm6M5dA;ZUq@;P53p(t>?P|j}0Ob0wG$sj6o#K1)C%aUDI0>wTJ}~Y$ zjkDxV0W*0Dmi6VI>>VVLmZHF^kO!V|$72 zESL$|#;3@xMTxde+LC_~KYp@EU8@N%!1Y+cg95zFlNbev0tDO(m>$KL1xRJP7k#}F z~=kiJ08r^zj=BFTjmXbekwZpY`Mx zAPNxR_45RQXE~y4+9QmBmkfv;?~gzu7Cs-zU2OH#%`# zM**S$QGn?IuM=!3EQeyMn08l58|_P%SvPctdggVbWDk@{tje`c zR>}7}H4>XIP5l}$)0Mq)aDmK;neNjL+1e4>-UEM0X})X&%6fTflO z=}zemsYQ?wB&BQVZY7jh8U&=IL{cfG8$nn=N?Pe|knVFQe&9YF&kXCiERJ)I>YbiQHNIGNj&1s!iBIhOVzO1UY<8YRc-**);1ta@uA8JO`ZuXqZ+O>I`Kv6XbL5&D z!tB=NSepTrzE;hM|9waoQ-6f?YGgM}ge0qS&YJSQJ6#4_-UJQblT@W2BFVwVewi0b zI^)8?J{QXST&u|s4YltS|*;#pd!Wci+{X~iA}UGfE^H+%#>$Ic)Ui- zr)DqXh|a6q0=GIf8&;wpuy@!dk9z88?jeR<^r-!)@I}a%!7J$x0SSS+!V&tTh$ITQ zynbQ%z#v*L^QJAd5u8Y}5M2zg(l*hk;C|bi+RdhR?LsI2>5vygC}<;Y^E={PvCf3V zZ46z04(e0h_ePjG{X@UvX485`h-zIl;q>T5o39u^e-8c*If8TNE!83@pOEUrvexx` z@@<~;!Ia*Q-eyr|22D>Yz0hz|tkV~`MHo3`!?re?Uy@T4r(SFsIq@aseqn6^Olc#I z&4TFu!GHq_TnYD^)#tqyP7rR8y)0iu>G0k_H8sZpMDw2pR7mAWS*5=HiW7?d-t4kG zrx(<6290;heg<6SshZ~cCn!qL0Y2e;@OFnX{f~|6w+Gi}EWQhxjRsvldYz!745iO! zD}R=uD3!7C684*y)lXV@*_o<{1vm_u(k;pLFY*PXZ>!B29qt zi(A9GWc_c*Xn7*&A!&rq4@T@a@2(9T;^Rp0@1s(HJX@OGV>EI03nmiqIOzvzi~Trypr&7;>(CSB!Z?0~cSsmWkXOl{6- z*=FwAL=zob4WncwDWN8S_uZV9=KH|?tQbDzrOz^IoD(Uoh2A)j*@zZ{-~{P6jnlyl z1#|fnh}XD9nLjAMC0fkX^wxYIb4dM6k>^WMr;zwbDxYmEd-U_mLUIvRmp{>!)z6k6 zN$Cx4Mc9*=A)K=%#IzB#Xn9{TTnvYm#=Fd2s)5GdO~-W;y{KuTWWr%J&HN=WJx{U> zG}oa!n%>gaj+4=y_dA*L%eDTkmh9ffk!nt^s#24`srT_Yf3GC@(kt=bJ8rI}CMGR3 zs%1;@%CE#J!?(my^;5Lh5*?ACFL7crUI}!;Y)fdE z{sG~H(+>9A&dEbmAb$3Y4pK)->XpIYs?|k+#%Z4g=V%3lCOqiR>e^^ycW23ZiddY; zjYRvBG9B;B9QOZqB#j-b?k@b0%OVpb{FvDKcTqN%e;McM=I++55~Hz8&Gf!Ay4zrUyUln`~+9u#d}1X6^|eNAWcotCz$I2AUHGPIRMsG&DE!Pq#Xtm>sy5pVkZ5D0dhp zrCpFbVA>L;C{+0Xe$~7lF=IBE6})GMy#wh1dGO>QHVrNGuoMq*cfpxu?R%H!(S@GB zxm_AxW*AgttZ{BW#L`5#kXU`=^7!FanzMNwhucI4*N-z}9zeSD945XcfKG;H969Ek zuddzJf4^D=%I>%O6a$$H-C_a#=!R2ZBHqP&V0qo>zv{$xMPyv8QzELBy(cdIq_a^I zV+5oDSF1$uk$Xo6COkI=J*vn`z-q{Bw#?-ZT)#ND)C(NNe`=`vL6v-ROKT*90yq4Zn6a}v;Wtt< zcBQR+S#JFIJMk}=h?m8Y%A?Y(h!A@8(*R%layFA)&np7m&kKg`vj!Sq*kR{Wp(2K7 z5dO%>%qZNb*I@-T46r)|aT-RHd7?|mzXgLPHq{1THFnfy0R(`pWq3Ns%p0tIB|CjB zQ2ueN16)WbpAM(1ixrp4GL@IRd#+B|a)~kGNAe=sts>X5T8h8GO*kRA%}JA=6VG(7pVGgIuMn4qldp27#^>&jNxWRtmCd7BYAA}pMhc-*d+qyYyO|V z=Dm0i3cZ!9D+Aaf2oOd&BRdiF?c*?jAVeAflujSwgBH;`&q?QG8L=D$TMLgaq2v~Q z%7M(?6;zNH|63a~31T8l0Rr9>vOGk>MCK5FMz_vE7r85mAumb7WUZ17C?_`)|r+fUJcz0#%-cPt@#WghsO_ zp9$lf_W!1YBKWl7A#X>wj80d^_kdC(-Uel=4_EbeZN~s8gokceR-DH%m~;Q8IJHFn zF^R|P4ch2RE^%9aJBr?z7w^N%6WYhs3CD|~x|sARE!u9re?xb&N`amW(4Iw&&K5m0 zjAJQ}4+~^z2+XQ^C*>oN_h1(l8?tVVfobeNv-c_#sQz~ zpzj4G|8AZ1okeqjgRzG^7_tQiWuH-wkEc!5z4&=m`Vi<#!|2t`i~c-l{@e6_b7_Yt z&yY5b;*Wre9)+Y+?Y<*7$`siQE{kBQm215#@ft96j%Os5_SMC}K0o@!?u%jk=9}7T zo~%p;u~*nPY1v3SzZi&wiwKOY%d*D?Y3agoT`_=9B-yARJ($kl4{*JDoc?_Z;6208 zBn*iTwp&RU%*`>+HnX6JjbIo(JE6d(=w7`(pO|oz%?(tOz?A40CW9s6HZ|gXQuO$g zi)mNfyT$(UI*T$l2lGpZL}qjK81b;@#4Bi(YJ(xi1#6&;`10?-(Jk!`gWU*(xkGkH z(5G`35J4CYrr>VNNFTr=pYlP+8qxj}dgQy{hEQP|u-IV*2l&n~AATPKcps|SZ&5^e zo!`Iq47%W3+J|Jw$VgQ_Wa4#yOPOp>;3zJ14k&sYv3fpnn=)_G!qf)Xn%3({;kv_Ds?QE5 zcj%d+m5#GYPg5yGi^0KjKOlYi8Z)bGRE)9nnF#)tSg3%>_lnC1GBpk}XT){wtlW8b z%cJqbotw~0tT&!P=ZllD#4D0dYcbN;)IXE88p2pZQY2lrT*WshzMj$&J({uq1p1&W zhoeWJfW*Z*aW+52S_>>cLO$nL@!M7y3Z@*5yT6U-kKa!J{y3z)4Gf7Uqkj0q<@-7& z5x7}Heq!+S`C5dItaF5E77hGiaD7g@=~qJt`mYx$&Ud@okLrB{eVI98m=|@nw&%)O z_Ds5u;cZLm&aaR#B>pSW5Y`Wy$s78{^6Yu|G0RQz!q{=A0dZFZg`-2*r=~KuSxAVx z?zArh9i3|fYp6Wl55^TOlvbi1;bc$Lih-riT;J)=#uW;6o-GJ_1v?E!CIL4qa2u}_ z-Yl_0`cPuc!%G!FJHc}2$WEM*+}p3sv&}@83b<2;iZ&s|-Co{Q>oKEP6pJQ7U~`Fc z@t&z=Nn2@GbwV_v(B&i)g&@>KC)igxuUK1NWZI=z&8++lE1j@h>>j+U+ zLq`4T6D6`6y{m#}R|orT_1-i2RC+eu#Au0PUShujU{1;I4-r2B?fjU=qHw(%3Ygl2WF)b`-j}!u-muo^*}A9eVJ*6{ zVbDfgFzXme3gLx6GZj}bW6d2t{{4=kfXEA7SM#mYQOlJAQryk}T8n=~?(-pojsxl} zJ4E3-3`p{lFA8uBM}wFhY~!J~`NV6v6_|q?6m(Hg&(D?K&L@?8U{ANt|Ah2*xhB3n zvk;v@XjXyk@s}zxmW#-0;kZ9wd?_ z_^_x&Jn+@{pWSa{Vb@m3^yx~ID2p0=5619e-z8~PT5LVJfpoO;+$92q3iyPOq~nb5 zDMz)zJkfz|{_j(_+&>WyNZxW+D`!?>%1MkIXU)**E!z(%BWNPe4jtJ^P2kx#Y6WIq z;fSXB&oUVdl@+wW0;Y<(-wV6Db6%xg*|v>yQ;!+86CPZ-a<;u(i|#`Z!88&QU&q52 zkn*E-G!o&ElC6lKW)&i8-TNzpf2O)pO}Y)DKx2|zWvUc5TO+NjLLoPrku~(#Xueet&i?*8J1k;sr1P0XAJ5U@ze&B@Exij8T9?s2Hj8 zUWYM)2akIT*n07g)JuSNc(md;?^7_>E5xGpLy@73`ofZ{ zJo0Eeu^}MvQP~kEA~(UBby=(G_+;67?+X5I(c9I+<0_01Jsfz-r(|Hm;^TLwlCFp? zW_$sI<)*)BC?|=5C!}xi9(+q{TzMC}eX_Bl|K#NwAKZ`LEd9wwm(rtU?2$Ncqh%?sp7%N0v-a}=OJl8hixA;Y>nKRH&tc-4H{TqZCP#&ihn31gq?gP*%o?~3Qo4goZsAo&^y7S zgCn1W|YvL`8VU8f576A1>OnsnJ#aJ=n@>67t_ zQ<)cc}I!Byo z72v%pEFluvfT40z^y24km*42l7dg2lRoMK2rG`oy(Q6OG(;Gw&hk^})7lyXUGDs~2 zr|6sz6xnuYKKv>7prU1$PO)ID^Qw;gs_{w8@xSw zCTlLN=kg_DG1*^W!=kTntMBYeVSkG@7J=yit|~4*#;}(Y^A*Wh+e@*SKl_aGFL&l_ z$Phu-&%};)?dba00y!q`vUI`gnfmAO^aI>oC06y6sh9uSZc#CM*TaE{O=0AAQ3vjpbvAa1Y zR39<)(6DL`0jx$!{Ov)3QqM*fc5x{2J#^Cv#Tt*qf1}Uul9>RL+7-OjMDF2Zdk(1aVfQui6gqmlU-9o5|K#}2zxP$55Lla2IWtcNk>np z{?^~8+jW)_MDMH*j1nXq7mS_~zz#aIzrROC{LfDsjrlu$JuHm;HZUv(BYmb&b5S?_ z2!q>sct(FY_;8*lLF&4e#)A(`@-DrPr)&{>gBg1kKT6TAM?VOD zx6;*&bC5qcy(s9;j7T}LRB(9IX`heD!pOapCV%(^w$GeEpU~N+#*xS;0Hzq8p#Zgw zMXwsi1FcMs0^uaB9J@?Zj8D-paro9rR)dDf%+n|;#&%HJJ|#{d^OG_uPqawH;ldds z_c4ywQyy3R;Hr|IH=4Hfv85oJM7iZ-kDzaUn;)QOs({;0W$q)i_F?nono0CN``{@S(=;D*&9$_VMqZL`u3j=CZ5`yX=Y<`YeK1i(D6DuB!e zpxHo)h<)`l&C9w0V{}~GrZ^wGz<5qUfm*}q!;wWN?101akVdWb|*IhqG^75dp zwhi!sfh618rP8Atm5aXcW{w#`)7Bm3u|KD}o9`1C&E20~Z7}XEjh>1E{u|Voo_OsO zk7gEV)hIb6dXcvb9=rbR()Cj;9(j^gi&$u0wcv@I^g}N!^8u>55v(BX)}-s7IzvE< zsXZuKj!2-_x9ZA$!DO8PQ?oJl7qGz3*mgGk{0s;^^P3pe6e4qbn{oPCv^l>ehQAxj zwCwRQ?H~Ni%370<%pdJ~Pe-Og85RCHoDfd)!s*toL-#qfNnZ0A?JO>HUdB@5WK+;j zW&5t_FB=X4e{`>h{&Erlx)7uWEuHJFi8V9ubLNrYx?)WV&F$H2C*hd#iAxCdK605?t?t2UH+5a0K<#t(T%HcnSijmswu^frf4@S_q8u zg&T-d-WN;Wi^q|#x3m8b!FA#w#R@NXZ~JsQOT-kN8=^1ObqL9}(ynAg6I4~$u!yb0 zVwbBoCujH&wNI}-v$IM5J7dmnOfQ-9S2|#!Rv9J1+V&A zBB$)juD?_1ntkL1e11`2ev*w5Ku~{|d@lrriV&mlWw1df&i^e?3kwXR{Xt9|=~Ya_ zm|Xvj>Pr18VI{kYpQOL>RC@cFf~U{p$cHIJ+}HS)vi=aN)>?Lqd9dZ6x?#dDGj~el z!F!7r>Y{KL3<<^$AIoPt6J4QK&~&vpX0Q4On(cG37E&9UE9^)c?G{2&`0FOBgCD=iF(HRaX(Nto$_-sbQ;!s(B(Y*OrP+ zLp8t5aftppv{#P�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 aKK~!G%5@14pi=Ju0000b{Bq literal 0 HcmV?d00001 diff --git a/apps/dotcom/public/robots.txt b/apps/dotcom/public/robots.txt new file mode 100644 index 000000000..302ba5af8 --- /dev/null +++ b/apps/dotcom/public/robots.txt @@ -0,0 +1,11 @@ +User-agent: * +Disallow: /r + +User-agent: * +Disallow: /v + +User-agent: * +Disallow: /s + +User-agent: * +Allow: / diff --git a/apps/dotcom/public/site.webmanifest b/apps/dotcom/public/site.webmanifest new file mode 100644 index 000000000..52a2fe3f6 --- /dev/null +++ b/apps/dotcom/public/site.webmanifest @@ -0,0 +1,11 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, + { "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/apps/dotcom/public/social-image.png b/apps/dotcom/public/social-image.png new file mode 100644 index 0000000000000000000000000000000000000000..d2863ddaa59b083c6a06193c21935d996021d42e GIT binary patch literal 9546 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 NU;Ca`xyF-M{{wFfU?%_o literal 0 HcmV?d00001 diff --git a/apps/dotcom/public/social-og.png b/apps/dotcom/public/social-og.png new file mode 100644 index 0000000000000000000000000000000000000000..b58dbf96a400515fd6d6e056d544976fadc79210 GIT binary patch literal 10903 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 NU;Ca`xyF-M{{wFfU?%_o literal 0 HcmV?d00001 diff --git a/apps/dotcom/public/staging-favicon-16.png b/apps/dotcom/public/staging-favicon-16.png new file mode 100644 index 0000000000000000000000000000000000000000..7f2c10e1c62349c226be576122a5c2254c5f7a12 GIT binary patch literal 311 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eB{uxgf$B+ufwUZcmn+*h9^Me`qCN!{4Vc>hU zU?r0Z1J{HG7L@?@wawA*g*djDe0@HTidqfu9y82lo}bNq|=pjy~l8k?`4r3rwI$zWuMs8^PJ7r`f>28j3x=+%X5NU zh5vXZESS<{bG}k;9}PMc z8$FG6_qxN{O_MEnn7@lU9Wej%R3o~AK~lq`*=~*9YmS;t1&nenKz}iKy85}Sb4q9e E019?;E&u=k literal 0 HcmV?d00001 diff --git a/apps/dotcom/public/staging-favicon-32.png b/apps/dotcom/public/staging-favicon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..a59972275b842516a01b17824bc65ec2a20e6d62 GIT binary patch literal 541 zcmV+&0^%oo1ML7hfDWXC+7F zC;M48n-DQV2$x4J+9T%SAoknWO+ek?!$8P4W9o(k1_X7;J9W(eFc3u%`M#fxBuTV7 zyj!hSncf=XdWmDy$hQ%Op;GI4UiN-47>L{LCTuWf0AF3^a5yNn9Jt7YPV;aE7K=qC z1GCvosl`|Kdc918YaFErB^?EIM;_Tn6 z<#MU~0yHEQ^2ViW*JZ345hj-R1jlidIy}!csGY_Plqc71$GJ(J!5_AF zbvm7_&jMBkFloNU@-8Tv=EGE^SOuI?8jVKTwP{|*@;_l9q3(!a!1DO|E8Jsq9inr@ fqCH|B9%6q2OE8N*Q$)+T00000NkvXXu0mjf>?h?P literal 0 HcmV?d00001 diff --git a/apps/dotcom/public/staging-favicon.svg b/apps/dotcom/public/staging-favicon.svg new file mode 100644 index 000000000..1f0cf7e07 --- /dev/null +++ b/apps/dotcom/public/staging-favicon.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/apps/dotcom/public/tldraw-white-on-black.svg b/apps/dotcom/public/tldraw-white-on-black.svg new file mode 100644 index 000000000..57069fdfb --- /dev/null +++ b/apps/dotcom/public/tldraw-white-on-black.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/apps/dotcom/public/tldraw.svg b/apps/dotcom/public/tldraw.svg new file mode 100644 index 000000000..f0c9e1f94 --- /dev/null +++ b/apps/dotcom/public/tldraw.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/apps/dotcom/scripts/build.ts b/apps/dotcom/scripts/build.ts new file mode 100644 index 000000000..fa2828c04 --- /dev/null +++ b/apps/dotcom/scripts/build.ts @@ -0,0 +1,59 @@ +import glob from 'fast-glob' +import { mkdirSync, writeFileSync } from 'fs' +import { exec } from '../../../scripts/lib/exec' +import { Config } from './vercel-output-config' + +import { config } from 'dotenv' +import { nicelog } from '../../../scripts/lib/nicelog' +config({ + path: './.env.local', +}) + +nicelog('The multiplayer server is', process.env.MULTIPLAYER_SERVER) + +async function build() { + await exec('vite', ['build', '--emptyOutDir']) + await exec('yarn', ['run', '-T', 'sentry-cli', 'sourcemaps', 'inject', 'dist/assets']) + // Clear output static folder (in case we are running locally and have already built the app once before) + await exec('rm', ['-rf', '.vercel/output']) + mkdirSync('.vercel/output', { recursive: true }) + await exec('cp', ['-r', 'dist', '.vercel/output/static']) + await exec('rm', ['-rf', ...glob.sync('.vercel/output/static/**/*.js.map')]) + writeFileSync( + '.vercel/output/config.json', + JSON.stringify( + { + version: 3, + routes: [ + // rewrite api calls to the multiplayer server + { + src: '^/api(/(.*))?$', + dest: `${ + process.env.MULTIPLAYER_SERVER?.replace(/^ws/, 'http') ?? 'http://127.0.0.1:8787' + }$1`, + check: true, + }, + // cache static assets immutably + { + src: '^/assets/(.*)$', + headers: { 'Cache-Control': 'public, max-age=31536000, immutable' }, + }, + // serve static files + { + handle: 'filesystem', + }, + // finally handle SPA routing + { + src: '.*', + dest: '/index.html', + }, + ], + overrides: {}, + } satisfies Config, + null, + 2 + ) + ) +} + +build() diff --git a/apps/dotcom/scripts/dev-app.ts b/apps/dotcom/scripts/dev-app.ts new file mode 100644 index 000000000..0594ef17a --- /dev/null +++ b/apps/dotcom/scripts/dev-app.ts @@ -0,0 +1,41 @@ +import { writeFileSync } from 'fs' +import { exec } from '../../../scripts/lib/exec' +import { readFileIfExists } from '../../../scripts/lib/file' +import { nicelog } from '../../../scripts/lib/nicelog' + +async function main() { + await writeEnvFileVars('../dotcom-worker/.dev.vars', { + APP_ORIGIN: 'http://localhost:3000', + }) + if (process.env.VITE_PREVIEW === '1') { + await exec('vite', ['preview', '--host', '--port', '3000']) + } else { + await exec('vite', ['dev', '--host', '--port', '3000']) + } +} + +async function writeEnvFileVars(filePath: string, vars: Record) { + nicelog(`Writing env vars to ${filePath}: ${Object.keys(vars).join(', ')}`) + let envFileContents = (await readFileIfExists(filePath)) ?? '' + + const KEYS_TO_SKIP: string[] = [] + + for (const key of Object.keys(vars)) { + envFileContents = envFileContents.replace(new RegExp(`(\n|^)${key}=.*(?:\n|$)`), '$1') + } + + if (envFileContents && !envFileContents.endsWith('\n')) envFileContents += '\n' + + for (const [key, value] of Object.entries(vars)) { + if (KEYS_TO_SKIP.includes(key)) { + continue + } + envFileContents += `${key}=${value}\n` + } + + writeFileSync(filePath, envFileContents) + + nicelog(`Wrote env vars to ${filePath}`) +} + +main() diff --git a/apps/dotcom/scripts/vercel-output-config.d.ts b/apps/dotcom/scripts/vercel-output-config.d.ts new file mode 100644 index 000000000..a7dff7bcd --- /dev/null +++ b/apps/dotcom/scripts/vercel-output-config.d.ts @@ -0,0 +1,111 @@ +// copied from https://github.com/vercel/vercel/blob/f8c893bb156d12284866c801dcd3e5fe3ef08e20/packages/gatsby-plugin-vercel-builder/src/types.d.ts#L4 +// seems like vercel don't export a good version of this type anywhere at the time of writing + +import type { Images } from '@vercel/build-utils' + +export type Config = { + version: 3 + routes?: Route[] + images?: Images + wildcard?: WildcardConfig + overrides?: OverrideConfig + cache?: string[] +} + +type Route = Source | Handler + +type Source = { + src: string + dest?: string + headers?: Record + methods?: string[] + continue?: boolean + caseSensitive?: boolean + check?: boolean + status?: number + has?: Array + missing?: Array + locale?: Locale + middlewarePath?: string +} + +type Locale = { + redirect?: Record + cookie?: string +} + +type HostHasField = { + type: 'host' + value: string +} + +type HeaderHasField = { + type: 'header' + key: string + value?: string +} + +type CookieHasField = { + type: 'cookie' + key: string + value?: string +} + +type QueryHasField = { + type: 'query' + key: string + value?: string +} + +type HandleValue = + | 'rewrite' + | 'filesystem' // check matches after the filesystem misses + | 'resource' + | 'miss' // check matches after every filesystem miss + | 'hit' + | 'error' // check matches after error (500, 404, etc.) + +type Handler = { + handle: HandleValue + src?: string + dest?: string + status?: number +} + +type WildCard = { + domain: string + value: string +} + +type WildcardConfig = Array + +type Override = { + path?: string + contentType?: string +} + +type OverrideConfig = Record + +type ServerlessFunctionConfig = { + handler: string + runtime: string + memory?: number + maxDuration?: number + environment?: Record[] + allowQuery?: string[] + regions?: string[] +} + +export type NodejsServerlessFunctionConfig = ServerlessFunctionConfig & { + launcherType: 'Nodejs' + shouldAddHelpers?: boolean // default: false + shouldAddSourceMapSupport?: boolean // default: false +} + +export type PrerenderFunctionConfig = { + expiration: number | false + group?: number + bypassToken?: string + fallback?: string + allowQuery?: string[] +} diff --git a/apps/dotcom/sentry-release-name.ts b/apps/dotcom/sentry-release-name.ts new file mode 100644 index 000000000..98fa27118 --- /dev/null +++ b/apps/dotcom/sentry-release-name.ts @@ -0,0 +1,3 @@ +// This file is replaced during deployments to point to a meaningful release name in Sentry. +// DO NOT MESS WITH THIS LINE OR THE ONE BELOW IT. I WILL FIND YOU +export const sentryReleaseName = 'local' diff --git a/apps/dotcom/sentry.client.config.ts b/apps/dotcom/sentry.client.config.ts new file mode 100644 index 000000000..13417f2e9 --- /dev/null +++ b/apps/dotcom/sentry.client.config.ts @@ -0,0 +1,57 @@ +// This file configures the initialization of Sentry on the browser. +// The config you add here will be used whenever a page is visited. +// https://docs.sentry.io/platforms/javascript/guides/nextjs/ + +import { ExtraErrorData } from '@sentry/integrations' +import * as Sentry from '@sentry/react' +import { Editor, getErrorAnnotations } from '@tldraw/tldraw' +import { sentryReleaseName } from './sentry-release-name' +import { env } from './src/utils/env' +import { setGlobalErrorReporter } from './src/utils/errorReporting' + +function requireSentryDsn() { + if (!process.env.SENTRY_DSN) { + throw new Error('SENTRY_DSN is required') + } + return process.env.SENTRY_DSN as string +} + +Sentry.init({ + dsn: env === 'development' ? undefined : requireSentryDsn(), + // Adjust this value in production, or use tracesSampler for greater control + tracesSampleRate: 1.0, + release: sentryReleaseName, + environment: env, + integrations: [new ExtraErrorData({ depth: 10 }) as any], + // ... + // Note: if you want to override the automatic release value, do not set a + // `release` value here - use the environment variable `SENTRY_RELEASE`, so + // that it will also get attached to your source maps + + beforeSend: (event, hint) => { + if (env === 'development') { + console.error('[SentryDev]', hint.originalException ?? hint.syntheticException) + return null + } + // todo: re-evaulate use of window here? + const editor: Editor | undefined = (window as any).editor + const appErrorAnnotations = editor?.createErrorAnnotations('unknown', 'unknown') + const errorAnnotations = getErrorAnnotations(hint.originalException as any) + + event.tags = { + ...appErrorAnnotations?.tags, + ...errorAnnotations.tags, + ...event.tags, + } + + event.extra = { + ...appErrorAnnotations?.extras, + ...errorAnnotations.extras, + ...event.extra, + } + + return event + }, +}) + +setGlobalErrorReporter((error) => Sentry.captureException(error)) diff --git a/apps/dotcom/sentry.properties b/apps/dotcom/sentry.properties new file mode 100644 index 000000000..c5a9f609f --- /dev/null +++ b/apps/dotcom/sentry.properties @@ -0,0 +1,4 @@ +defaults.url=https://sentry.io/ +defaults.org=tldraw +defaults.project=lite +cli.executable=../../node_modules/@sentry/cli/bin/sentry-cli diff --git a/apps/dotcom/setupTests.js b/apps/dotcom/setupTests.js new file mode 100644 index 000000000..88d3a65a1 --- /dev/null +++ b/apps/dotcom/setupTests.js @@ -0,0 +1,4 @@ +global.crypto ??= new (require('@peculiar/webcrypto').Crypto)() + +process.env.MULTIPLAYER_SERVER = 'https://localhost:8787' +process.env.ASSET_UPLOAD = 'https://localhost:8788' diff --git a/apps/dotcom/src/components/BoardHistoryLog/BoardHistoryLog.tsx b/apps/dotcom/src/components/BoardHistoryLog/BoardHistoryLog.tsx new file mode 100644 index 000000000..9d9be6851 --- /dev/null +++ b/apps/dotcom/src/components/BoardHistoryLog/BoardHistoryLog.tsx @@ -0,0 +1,43 @@ +import { Link } from 'react-router-dom' +import '../../../styles/core.css' + +// todo: remove tailwind + +export function BoardHistoryLog({ data }: { data: string[] }) { + if (data.length === 0) { + return ( +
+

{'No history found'}

+
+ ) + } + + return ( +
+
    + {data.map((v, i) => { + const timeStamp = v.split('/').pop() + return ( +
  • + + {formatDate(timeStamp!)} + +
  • + ) + })} +
+
+ ) +} + +function formatDate(dateISOString: string) { + const date = new Date(dateISOString) + return Intl.DateTimeFormat('en-GB', { + year: 'numeric', + month: 'short', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + }).format(date) +} diff --git a/apps/dotcom/src/components/BoardHistorySnapshot/BoardHistorySnapshot.tsx b/apps/dotcom/src/components/BoardHistorySnapshot/BoardHistorySnapshot.tsx new file mode 100644 index 000000000..e4f06174f --- /dev/null +++ b/apps/dotcom/src/components/BoardHistorySnapshot/BoardHistorySnapshot.tsx @@ -0,0 +1,77 @@ +import { Tldraw, createTLStore, defaultShapeUtils } from '@tldraw/tldraw' +import { RoomSnapshot } from '@tldraw/tlsync' +import { useCallback, useState } from 'react' +import '../../../styles/core.css' +import { assetUrls } from '../../utils/assetUrls' +import { useFileSystem } from '../../utils/useFileSystem' + +export function BoardHistorySnapshot({ + data, + roomId, + timestamp, + token, +}: { + data: RoomSnapshot + roomId: string + timestamp: string + token?: string +}) { + const [store] = useState(() => { + const store = createTLStore({ shapeUtils: defaultShapeUtils }) + store.loadSnapshot({ + schema: data.schema!, + store: Object.fromEntries(data.documents.map((doc) => [doc.state.id, doc.state])) as any, + }) + return store + }) + + const fileSystemUiOverrides = useFileSystem({ isMultiplayer: true }) + + const restoreVersion = useCallback(async () => { + const sure = window.confirm('Are you sure?') + if (!sure) return + + const res = await fetch(`/api/r/${roomId}/restore`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + ...(token + ? { + Authorization: 'Bearer ' + token, + } + : {}), + }, + body: JSON.stringify({ timestamp }), + }) + + if (!res.ok) { + window.alert('Something went wrong!') + return + } + + window.alert('done') + }, [roomId, timestamp, token]) + + return ( + <> +
+ { + editor.updateInstanceState({ isReadonly: true }) + setTimeout(() => { + editor.setCurrentTool('hand') + }) + }} + overrides={[fileSystemUiOverrides]} + inferDarkMode + autoFocus + /> +
+
+ +
+ + ) +} diff --git a/apps/dotcom/src/components/CursorChatBubble.tsx b/apps/dotcom/src/components/CursorChatBubble.tsx new file mode 100644 index 000000000..a7fa62b62 --- /dev/null +++ b/apps/dotcom/src/components/CursorChatBubble.tsx @@ -0,0 +1,207 @@ +import { preventDefault, track, useContainer, useEditor, useTranslation } from '@tldraw/tldraw' +import { + ChangeEvent, + ClipboardEvent, + KeyboardEvent, + RefObject, + useCallback, + useEffect, + useLayoutEffect, + useRef, + useState, +} from 'react' + +// todo: +// - not cleaning up +const CHAT_MESSAGE_TIMEOUT_CLOSING = 2000 +const CHAT_MESSAGE_TIMEOUT_CHATTING = 5000 + +export const CursorChatBubble = track(function CursorChatBubble() { + const editor = useEditor() + const container = useContainer() + const { isChatting, chatMessage } = editor.getInstanceState() + + const rTimeout = useRef(-1) + const [value, setValue] = useState('') + + useEffect(() => { + const closingUp = !isChatting && chatMessage + if (closingUp || isChatting) { + const duration = isChatting ? CHAT_MESSAGE_TIMEOUT_CHATTING : CHAT_MESSAGE_TIMEOUT_CLOSING + rTimeout.current = setTimeout(() => { + editor.updateInstanceState({ chatMessage: '', isChatting: false }) + setValue('') + container.focus() + }, duration) + } + + return () => { + clearTimeout(rTimeout.current) + } + }, [container, editor, chatMessage, isChatting]) + + if (isChatting) + return + + return chatMessage.trim() ? : null +}) + +function usePositionBubble(ref: RefObject) { + const editor = useEditor() + + useLayoutEffect(() => { + const elm = ref.current + if (!elm) return + + const { x, y } = editor.inputs.currentScreenPoint + ref.current?.style.setProperty('transform', `translate(${x}px, ${y}px)`) + + // Positioning the chat bubble + function positionChatBubble(e: PointerEvent) { + ref.current?.style.setProperty('transform', `translate(${e.clientX}px, ${e.clientY}px)`) + } + + window.addEventListener('pointermove', positionChatBubble) + + return () => { + window.removeEventListener('pointermove', positionChatBubble) + } + }, [ref, editor]) +} + +const NotEditingChatMessage = ({ chatMessage }: { chatMessage: string }) => { + const editor = useEditor() + const ref = useRef(null) + + usePositionBubble(ref) + + return ( +
+ {chatMessage} +
+ ) +} + +const CursorChatInput = track(function CursorChatInput({ + chatMessage, + value, + setValue, +}: { + chatMessage: string + value: string + setValue: (value: string) => void +}) { + const editor = useEditor() + const msg = useTranslation() + const container = useContainer() + + const ref = useRef(null) + const placeholder = chatMessage || msg('cursor-chat.type-to-chat') + + usePositionBubble(ref) + + useLayoutEffect(() => { + const elm = ref.current + if (!elm) return + + const textMeasurement = editor.textMeasure.measureText(value || placeholder, { + fontFamily: 'var(--font-body)', + fontSize: 12, + fontWeight: '500', + fontStyle: 'normal', + maxWidth: null, + lineHeight: 1, + padding: '6px', + }) + + elm.style.setProperty('width', textMeasurement.w + 'px') + }, [editor, value, placeholder]) + + useLayoutEffect(() => { + // Focus the editor + let raf = requestAnimationFrame(() => { + raf = requestAnimationFrame(() => { + ref.current?.focus() + }) + }) + + return () => { + cancelAnimationFrame(raf) + } + }, [editor]) + + const stopChatting = useCallback(() => { + editor.updateInstanceState({ isChatting: false }) + container.focus() + }, [editor, container]) + + // Update the chat message as the user types + const handleChange = useCallback( + (e: ChangeEvent) => { + const { value } = e.target + setValue(value.slice(0, 64)) + editor.updateInstanceState({ chatMessage: value }) + }, + [editor, setValue] + ) + + // Handle some keyboard shortcuts + const handleKeyDown = useCallback( + (e: KeyboardEvent) => { + const elm = ref.current + if (!elm) return + + // get this from the element so that this hook doesn't depend on value + const { value: currentValue } = elm + + switch (e.key) { + case 'Enter': { + preventDefault(e) + e.stopPropagation() + + // If the user hasn't typed anything, stop chatting + if (!currentValue) { + stopChatting() + return + } + + // Otherwise, 'send' the message + setValue('') + break + } + case 'Escape': { + preventDefault(e) + e.stopPropagation() + stopChatting() + break + } + } + }, + [stopChatting, setValue] + ) + + const handlePaste = useCallback((e: ClipboardEvent) => { + // todo: figure out what's an acceptable / sanitized paste + preventDefault(e) + e.stopPropagation() + }, []) + + return ( + + ) +}) diff --git a/apps/dotcom/src/components/DefaultErrorFallback/DefaultErrorFallback.tsx b/apps/dotcom/src/components/DefaultErrorFallback/DefaultErrorFallback.tsx new file mode 100644 index 000000000..8f2df915b --- /dev/null +++ b/apps/dotcom/src/components/DefaultErrorFallback/DefaultErrorFallback.tsx @@ -0,0 +1,13 @@ +import { captureException } from '@sentry/react' +import { DefaultErrorFallback as ErrorFallback } from '@tldraw/tldraw' +import { useEffect } from 'react' +import { useRouteError } from 'react-router-dom' +import '../../../styles/globals.css' + +export function DefaultErrorFallback() { + const error = useRouteError() + useEffect(() => { + captureException(error) + }, [error]) + return +} diff --git a/apps/dotcom/src/components/EmbeddedInIFrameWarning.tsx b/apps/dotcom/src/components/EmbeddedInIFrameWarning.tsx new file mode 100644 index 000000000..a194a3b06 --- /dev/null +++ b/apps/dotcom/src/components/EmbeddedInIFrameWarning.tsx @@ -0,0 +1,85 @@ +import React from 'react' +import { useUrl } from '../hooks/useUrl' + +export function EmbeddedInIFrameWarning() { + // check if this still works + const url = useUrl() + + const [copied, setCopied] = React.useState(false) + const rTimeout = React.useRef(0) + + const handleCopy = React.useCallback(() => { + setCopied(true) + clearTimeout(rTimeout.current) + rTimeout.current = setTimeout(() => { + setCopied(false) + }, 1200) + + const textarea = document.createElement('textarea') + textarea.setAttribute('position', 'fixed') + textarea.setAttribute('top', '0') + textarea.setAttribute('readonly', 'true') + textarea.setAttribute('contenteditable', 'true') + textarea.style.position = 'fixed' + textarea.value = url + document.body.appendChild(textarea) + textarea.focus() + textarea.select() + try { + const range = document.createRange() + range.selectNodeContents(textarea) + const sel = window.getSelection() + if (sel) { + sel.removeAllRanges() + sel.addRange(range) + textarea.setSelectionRange(0, textarea.value.length) + } + // eslint-disable-next-line deprecation/deprecation + document.execCommand('copy') + } catch (err) { + null // Could not copy to clipboard + } finally { + document.body.removeChild(textarea) + } + }, [url]) + + return ( +
+ ) +} diff --git a/apps/dotcom/src/components/ErrorPage/ErrorPage.tsx b/apps/dotcom/src/components/ErrorPage/ErrorPage.tsx new file mode 100644 index 000000000..9a275d8dd --- /dev/null +++ b/apps/dotcom/src/components/ErrorPage/ErrorPage.tsx @@ -0,0 +1,28 @@ +import { Link } from 'react-router-dom' + +export function ErrorPage({ + icon, + messages, +}: { + icon?: boolean + messages: { header: string; para1: string; para2?: string } + redirectTo?: string +}) { + return ( +
+
+ {icon && ( + {'Not + )} +
+

{messages.header}

+

{messages.para1}

+ {messages.para2 &&

{messages.para2}

} +
+ + Take me home. + +
+
+ ) +} diff --git a/apps/dotcom/src/components/ExportMenu.tsx b/apps/dotcom/src/components/ExportMenu.tsx new file mode 100644 index 000000000..5a51fc88d --- /dev/null +++ b/apps/dotcom/src/components/ExportMenu.tsx @@ -0,0 +1,98 @@ +import * as Popover from '@radix-ui/react-popover' +import { + Button, + useActions, + useBreakpoint, + useContainer, + useEditor, + useTranslation, +} from '@tldraw/tldraw' +import React, { useState } from 'react' +import { useShareMenuIsOpen } from '../hooks/useShareMenuOpen' +import { SHARE_PROJECT_ACTION, SHARE_SNAPSHOT_ACTION } from '../utils/sharing' +import { getSaveFileCopyAction } from '../utils/useFileSystem' +import { useHandleUiEvents } from '../utils/useHandleUiEvent' + +export const ExportMenu = React.memo(function ExportMenu() { + const { [SHARE_PROJECT_ACTION]: shareProject, [SHARE_SNAPSHOT_ACTION]: shareSnapshot } = + useActions() + const container = useContainer() + const msg = useTranslation() + const breakpoint = useBreakpoint() + const handleUiEvent = useHandleUiEvents() + const showIcon = breakpoint < 5 + const editor = useEditor() + const saveFileCopyAction = getSaveFileCopyAction(editor, handleUiEvent) + const [didCopySnapshotLink, setDidCopySnapshotLink] = useState(false) + const [isUploadingSnapshot, setIsUploadingSnapshot] = useState(false) + + const [isOpen, onOpenChange] = useShareMenuIsOpen() + + return ( + + + + + + +
+
+ +
+ {userIds.length > 0 && ( +
+ {userIds.map((userId) => { + return + })} +
+ )} + {!hideShareMenu && ( +
+
+ )} +
+
+
+
+ ) +}) diff --git a/apps/dotcom/src/components/PeopleMenu/PeopleMenuAvatar.tsx b/apps/dotcom/src/components/PeopleMenu/PeopleMenuAvatar.tsx new file mode 100644 index 000000000..b864cf636 --- /dev/null +++ b/apps/dotcom/src/components/PeopleMenu/PeopleMenuAvatar.tsx @@ -0,0 +1,18 @@ +import { usePresence } from '@tldraw/tldraw' + +export function PeopleMenuAvatar({ userId }: { userId: string }) { + const presence = usePresence(userId) + + if (!presence) return null + return ( +
+ {presence.userName === 'New User' ? '' : presence.userName[0] ?? ''} +
+ ) +} diff --git a/apps/dotcom/src/components/PeopleMenu/PeopleMenuItem.tsx b/apps/dotcom/src/components/PeopleMenu/PeopleMenuItem.tsx new file mode 100644 index 000000000..ddddd32e4 --- /dev/null +++ b/apps/dotcom/src/components/PeopleMenu/PeopleMenuItem.tsx @@ -0,0 +1,63 @@ +import { + Button, + Icon, + track, + useEditor, + usePresence, + useTranslation, + useUiEvents, +} from '@tldraw/tldraw' +import { useCallback } from 'react' +import { UI_OVERRIDE_TODO_EVENT } from '../../utils/useHandleUiEvent' + +export const PeopleMenuItem = track(function PeopleMenuItem({ userId }: { userId: string }) { + const editor = useEditor() + const msg = useTranslation() + const trackEvent = useUiEvents() + + const presence = usePresence(userId) + + const handleFollowClick = useCallback(() => { + if (editor.getInstanceState().followingUserId === userId) { + editor.stopFollowingUser() + trackEvent('stop-following', { source: 'people-menu' }) + } else { + editor.startFollowingUser(userId) + trackEvent('start-following' as UI_OVERRIDE_TODO_EVENT, { source: 'people-menu' }) + } + }, [editor, userId, trackEvent]) + + const theyAreFollowingYou = presence?.followingUserId === editor.user.getId() + const youAreFollowingThem = editor.getInstanceState().followingUserId === userId + + if (!presence) return null + + return ( +
+ +
+ ) +}) diff --git a/apps/dotcom/src/components/PeopleMenu/PeopleMenuMore.tsx b/apps/dotcom/src/components/PeopleMenu/PeopleMenuMore.tsx new file mode 100644 index 000000000..eaa861d37 --- /dev/null +++ b/apps/dotcom/src/components/PeopleMenu/PeopleMenuMore.tsx @@ -0,0 +1,3 @@ +export function PeopleMenuMore({ count }: { count: number }) { + return
{'+' + Math.abs(count)}
+} diff --git a/apps/dotcom/src/components/PeopleMenu/UserPresenceColorPicker.tsx b/apps/dotcom/src/components/PeopleMenu/UserPresenceColorPicker.tsx new file mode 100644 index 000000000..1da5ad676 --- /dev/null +++ b/apps/dotcom/src/components/PeopleMenu/UserPresenceColorPicker.tsx @@ -0,0 +1,131 @@ +import * as Popover from '@radix-ui/react-popover' +import { + Button, + USER_COLORS, + track, + useContainer, + useEditor, + useTranslation, + useUiEvents, +} from '@tldraw/tldraw' +import React, { useCallback, useRef, useState } from 'react' +import { UI_OVERRIDE_TODO_EVENT } from '../../utils/useHandleUiEvent' + +export const UserPresenceColorPicker = track(function UserPresenceColorPicker() { + const editor = useEditor() + const container = useContainer() + const msg = useTranslation() + const trackEvent = useUiEvents() + + const rPointing = useRef(false) + + const [isOpen, setIsOpen] = useState(false) + const handleOpenChange = useCallback((isOpen: boolean) => { + setIsOpen(isOpen) + }, []) + + const value = editor.user.getColor() + + const onValueChange = useCallback( + (item: string) => { + editor.user.updateUserPreferences({ color: item }) + trackEvent('set-color' as UI_OVERRIDE_TODO_EVENT, { source: 'people-menu' }) + }, + [editor, trackEvent] + ) + + const { + handleButtonClick, + handleButtonPointerDown, + handleButtonPointerEnter, + handleButtonPointerUp, + } = React.useMemo(() => { + const handlePointerUp = () => { + rPointing.current = false + window.removeEventListener('pointerup', handlePointerUp) + } + + const handleButtonClick = (e: React.PointerEvent) => { + const { id } = e.currentTarget.dataset + if (!id) return + if (value === id) return + + onValueChange(id) + } + + const handleButtonPointerDown = (e: React.PointerEvent) => { + const { id } = e.currentTarget.dataset + if (!id) return + + onValueChange(id) + + rPointing.current = true + window.addEventListener('pointerup', handlePointerUp) // see TLD-658 + } + + const handleButtonPointerEnter = (e: React.PointerEvent) => { + if (!rPointing.current) return + + const { id } = e.currentTarget.dataset + if (!id) return + onValueChange(id) + } + + const handleButtonPointerUp = (e: React.PointerEvent) => { + const { id } = e.currentTarget.dataset + if (!id) return + onValueChange(id) + } + + return { + handleButtonClick, + handleButtonPointerDown, + handleButtonPointerEnter, + handleButtonPointerUp, + } + }, [value, onValueChange]) + + return ( + + + + + + + + ) +} diff --git a/apps/dotcom/src/utils/migration/migration.tsx b/apps/dotcom/src/utils/migration/migration.tsx new file mode 100644 index 000000000..01ef22728 --- /dev/null +++ b/apps/dotcom/src/utils/migration/migration.tsx @@ -0,0 +1,118 @@ +import { Editor, LegacyTldrawDocument, buildFromV1Document } from '@tldraw/tldraw' +import { openDB } from 'idb' + +export function isEditorEmpty(editor: Editor) { + const hasAnyShapes = editor.store.allRecords().some((r) => r.typeName === 'shape') + return !hasAnyShapes +} + +export async function findV1ContentFromIdb(): Promise<{ + document: LegacyTldrawDocument + clear: () => Promise +} | null> { + try { + const db = await openDB('keyval-store', 1) + const tx = db.transaction('keyval', 'readonly') + const store = tx.objectStore('keyval') + + const home: unknown = await store.get('home') + await tx.done + + if ( + home && + typeof home === 'object' && + 'document' in home && + home.document && + typeof home.document === 'object' && + 'version' in home.document && + typeof home.document.version === 'number' + ) { + return { + document: home.document as LegacyTldrawDocument, + clear: async () => { + try { + const tx = db.transaction('keyval', 'readwrite') + const store = tx.objectStore('keyval') + store.delete('home') + await tx.done + return + } catch { + // eh + } + }, + } + } + + return null + } catch { + return null + } +} + +export async function importFromV1LocalRoom( + editor: Editor, + didCancel: () => boolean +): Promise<{ didImport: false } | { didImport: true; document: LegacyTldrawDocument }> { + const v1Doc = await findV1ContentFromIdb() + if (didCancel() || !v1Doc) return { didImport: false } + + const hasAnyShapes = Object.values(v1Doc.document.pages).some( + (page) => Object.values(page.shapes).length > 0 + ) + if (!hasAnyShapes) return { didImport: false } + + buildFromV1Document(editor, v1Doc.document) + v1Doc.clear() + + if (isEditorEmpty(editor)) { + return { didImport: false } + } + + return { didImport: true, document: v1Doc.document } +} + +export async function importFromV1MultiplayerRoom( + editor: Editor, + roomSlug: string, + didCancel: () => boolean +): Promise<{ didImport: false } | { didImport: true; document: LegacyTldrawDocument }> { + const response = await fetch(`/api/static-legacy-multiplayer?roomSlug=${roomSlug}`) + if (!response.ok || didCancel()) { + return { didImport: false } + } + + const data = await response.json() + if (!data.room || didCancel()) { + return { didImport: false } + } + + // TODO: handle weird data formats (TLD-1605) & v1 migrations (TLD-1638) + const { assets, bindings, shapes, version } = data.room.storage.data + const PAGE_ID = 'page' + const document: LegacyTldrawDocument = { + id: 'doc', + name: roomSlug, + version, + pages: { + [PAGE_ID]: { + id: PAGE_ID, + name: 'Page 1', + childIndex: 1, + shapes: shapes?.data, + bindings: bindings?.data, + }, + }, + pageStates: { + [PAGE_ID]: { + id: PAGE_ID, + selectedIds: [], + camera: { point: [0, 0], zoom: 1 }, + }, + }, + assets: assets?.data ?? {}, + } + buildFromV1Document(editor, document) + + if (isEditorEmpty(editor)) return { didImport: false } + return { didImport: true, document } +} diff --git a/apps/dotcom/src/utils/migration/writeV1ContentsToIdb.tsx b/apps/dotcom/src/utils/migration/writeV1ContentsToIdb.tsx new file mode 100644 index 000000000..34777b749 --- /dev/null +++ b/apps/dotcom/src/utils/migration/writeV1ContentsToIdb.tsx @@ -0,0 +1,18 @@ +import { openDB } from 'idb' + +const v1Contents = { + home: JSON.parse( + '{"settings":{"isCadSelectMode":false,"isPenMode":false,"isDarkMode":true,"isZoomSnap":false,"isFocusMode":false,"isSnapping":false,"isDebugMode":false,"isReadonlyMode":false,"keepStyleMenuOpen":false,"nudgeDistanceLarge":16,"nudgeDistanceSmall":1,"showRotateHandles":true,"showBindingHandles":true,"showCloneHandles":false,"showGrid":false,"language":"en","dockPosition":"bottom","exportBackground":"transparent"},"appState":{"status":"idle","activeTool":"draw","currentPageId":"page","currentStyle":{"color":"black","size":"small","isFilled":false,"dash":"draw","scale":1},"isToolLocked":false,"isMenuOpen":false,"isEmptyCanvas":false,"eraseLine":[],"snapLines":[],"isLoading":false,"disableAssets":false,"selectByContain":false},"document":{"id":"doc","name":"New Document","version":15.5,"pages":{"page":{"id":"page","name":"Page 1","childIndex":1,"shapes":{"9172e03a-bd79-4c2b-3abe-d16b4e3cf33f":{"id":"9172e03a-bd79-4c2b-3abe-d16b4e3cf33f","type":"draw","name":"Draw","parentId":"page","childIndex":1,"point":[431.69,168.94],"rotation":0,"style":{"color":"black","size":"small","isFilled":false,"dash":"draw","scale":1},"points":[[0,0,0.5],[0,0,0.5],[0,0.83,0.5],[0,3.31,0.5],[0,7.94,0.5],[0,16.67,0.5],[0,29.04,0.5],[0,43.27,0.5],[0,59.95,0.5],[0,78.62,0.5],[0,97.95,0.5],[0,116.69,0.5],[0,131.06,0.5],[0,145.44,0.5],[0,161.37,0.5],[0,172.49,0.5],[0,182.38,0.5],[0,191.3,0.5],[0,196.31,0.5],[0,200.31,0.5],[0,204.09,0.5],[0,206.54,0.5],[0,208.03,0.5],[0,208.77,0.5],[0,209.04,0.5],[0,209.15,0.5],[0,208.94,0.5],[0,207.75,0.5],[0,205.52,0.5],[0,202.47,0.5],[0,195.49,0.5],[0,192.03,0.5],[0,183.13,0.5],[0.25,172.63,0.5],[2.66,162.29,0.5],[7.19,152.27,0.5],[11.76,144.66,0.5],[17.02,137.91,0.5],[23.26,130.71,0.5],[28.88,125.43,0.5],[32.85,122.74,0.5],[36.32,121.08,0.5],[40.33,119.78,0.5],[43.67,119.28,0.5],[45.89,119.15,0.5],[47.84,119.58,0.5],[49.47,121.23,0.5],[51.03,124.94,0.5],[52.9,130.48,0.5],[54.69,136.93,0.5],[56.46,144.14,0.5],[58.32,152.38,0.5],[59.89,159.79,0.5],[61.09,166.49,0.5],[61.97,172.42,0.5],[62.54,177.6,0.5],[63.08,183.22,0.5],[63.56,187.41,0.5],[63.98,190.15,0.5],[64.33,191.82,0.5],[64.47,192.74,0.5],[64.47,193.29,0.5]],"isComplete":true},"9cfd08a9-0e26-4cbc-1a6f-8507ac58840e":{"id":"9cfd08a9-0e26-4cbc-1a6f-8507ac58840e","type":"draw","name":"Draw","parentId":"page","childIndex":2,"point":[496.61,282.12],"rotation":0,"style":{"color":"black","size":"small","isFilled":false,"dash":"draw","scale":1},"points":[[23.03,46.08,0.5],[23.03,46.08,0.5],[23.25,46.08,0.5],[24.04,46.08,0.5],[26.3,46.08,0.5],[29.46,46.08,0.5],[32.5,46.08,0.5],[37.44,46.08,0.5],[44.54,46.08,0.5],[50.28,46.08,0.5],[54.64,46.08,0.5],[59.52,45.59,0.5],[64.01,44.39,0.5],[67.72,42.79,0.5],[71.12,40.77,0.5],[73.91,38.75,0.5],[75.69,36.93,0.5],[76.96,34.58,0.5],[78,31.96,0.5],[78.84,29.48,0.5],[79.5,26.55,0.5],[79.75,23.36,0.5],[79.77,20.04,0.5],[79.77,16.68,0.5],[79.74,13.24,0.5],[79.47,9.78,0.5],[78.85,7.65,0.5],[77.8,5.95,0.5],[76.32,3.88,0.5],[74.46,2.29,0.5],[71.89,0.92,0.5],[69.07,0.12,0.5],[66.18,0,0.5],[62.91,0,0.5],[59.16,0,0.5],[54.92,0.38,0.5],[49.27,2.6,0.5],[42.42,6.97,0.5],[35.97,12.44,0.5],[29.71,18.82,0.5],[23.27,25.81,0.5],[18.09,31.78,0.5],[13.78,37.7,0.5],[9.19,44.64,0.5],[5.5,51.15,0.5],[3.46,55.72,0.5],[2.21,59.28,0.5],[0.97,63.44,0.5],[0.21,67.06,0.5],[0,69.8,0.5],[0,72.32,0.5],[0.02,75.09,0.5],[0.41,77.8,0.5],[1.89,80.11,0.5],[4.15,82.33,0.5],[6.73,84.55,0.5],[9.87,86.85,0.5],[13.57,89.18,0.5],[17.74,91.21,0.5],[23.4,93.11,0.5],[30.11,94.9,0.5],[36.06,96.09,0.5],[42.04,96.93,0.5],[48.39,97.63,0.5],[54.27,98.17,0.5],[58.77,98.53,0.5],[63.26,98.61,0.5],[68.19,98.61,0.5],[72.31,98.27,0.5],[76.22,97.05,0.5],[79.93,95.29,0.5],[83.83,92.89,0.5],[88.14,89.74,0.5],[91.92,86.01,0.5]],"isComplete":true},"a4d8e089-da35-4691-33b1-cca4291f3ddb":{"id":"a4d8e089-da35-4691-33b1-cca4291f3ddb","type":"draw","name":"Draw","parentId":"page","childIndex":3,"point":[625.08,260.15],"rotation":0,"style":{"color":"black","size":"small","isFilled":false,"dash":"draw","scale":1},"points":[[0,20.72,0.5],[0,20.72,0.5],[0,21.19,0.5],[0,22.55,0.5],[0,25.3,0.5],[0,29.71,0.5],[0,34.96,0.5],[0,41.62,0.5],[0,50.79,0.5],[0,60.42,0.5],[0,68.86,0.5],[0,75.54,0.5],[0,81.74,0.5],[0,88.59,0.5],[0,94.09,0.5],[0,97.64,0.5],[0,100.67,0.5],[0,103.31,0.5],[0,104.98,0.5],[0,106.08,0.5],[0,106.58,0.5],[0,106.59,0.5],[0,105.88,0.5],[0,103.95,0.5],[0,101.12,0.5],[0,96.28,0.5],[1.43,88.45,0.5],[4.55,79.33,0.5],[8.81,68.87,0.5],[14.98,56.77,0.5],[22.46,44.42,0.5],[30.55,32.89,0.5],[38.67,23.33,0.5],[45.37,16.98,0.5],[51.08,12.18,0.5],[55.92,8.42,0.5],[60.24,5.55,0.5],[64.79,2.94,0.5],[68.77,1.23,0.5],[72.22,0.25,0.5],[75.03,0,0.5],[77.22,0,0.5],[78.88,0,0.5],[80.29,0,0.5],[81.56,0,0.5],[82.62,0,0.5],[83.44,0.1,0.5]],"isComplete":true},"f54ec6aa-8e04-429a-288b-b22c5066c083":{"id":"f54ec6aa-8e04-429a-288b-b22c5066c083","type":"draw","name":"Draw","parentId":"page","childIndex":4,"point":[688.7,288.06],"rotation":0,"style":{"color":"black","size":"small","isFilled":false,"dash":"draw","scale":1},"points":[[14.44,38.57,0.5],[14.44,38.57,0.5],[14.93,38.57,0.5],[17.05,38.57,0.5],[20.85,38.57,0.5],[25.65,38.57,0.5],[30.17,38.57,0.5],[34.68,38.57,0.5],[39.75,38.57,0.5],[44.45,38.57,0.5],[48.79,38.57,0.5],[52.6,38.35,0.5],[55.7,37.71,0.5],[58.21,36.63,0.5],[60.4,34.86,0.5],[62.46,32.22,0.5],[64.19,29.15,0.5],[65.32,25.53,0.5],[65.94,21.45,0.5],[66.23,17.88,0.5],[66.28,14.19,0.5],[66.28,10.73,0.5],[66.28,8.77,0.5],[66.28,7.29,0.5],[65.57,5.27,0.5],[64.44,3.62,0.5],[63.07,2.75,0.5],[61.32,1.82,0.5],[59.15,1.02,0.5],[56.03,0.39,0.5],[53.03,0,0.5],[50.22,0,0.5],[46.57,0,0.5],[40.79,0.33,0.5],[34.33,1.74,0.5],[29.01,3.85,0.5],[23.29,6.16,0.5],[17.14,8.72,0.5],[12.24,10.84,0.5],[8.89,12.32,0.5],[6.07,13.74,0.5],[3.55,15.23,0.5],[1.74,16.62,0.5],[0.74,17.88,0.5],[0.27,19.09,0.5],[0.05,20.64,0.5],[0,22.48,0.5],[0,25.13,0.5],[0,28.65,0.5],[0,32.96,0.5],[0,38.33,0.5],[0.8,44.54,0.5],[2.4,51.09,0.5],[4.34,57.48,0.5],[6.71,63.83,0.5],[9.53,69.63,0.5],[12.69,74.54,0.5],[15.86,78.53,0.5],[19.43,81.67,0.5],[23.48,84.29,0.5],[27.5,86.04,0.5],[31.44,86.85,0.5],[35.82,87.24,0.5],[41,87.39,0.5],[47.53,87.09,0.5],[54.47,86.01,0.5],[61.31,83.49,0.5],[68.22,79.99,0.5]],"isComplete":true},"640adf82-c8fd-4e93-18c4-2d8ae7817181":{"id":"640adf82-c8fd-4e93-18c4-2d8ae7817181","type":"draw","name":"Draw","parentId":"page","childIndex":5,"point":[880.73,291.19],"rotation":0,"style":{"color":"black","size":"small","isFilled":false,"dash":"draw","scale":1},"points":[[0,0,0.5],[0,0,0.5],[0,0.78,0.5],[0,3.52,0.5],[0,8.19,0.5],[0,14.22,0.5],[0,21.85,0.5],[0,30.52,0.5],[0,39.61,0.5],[0,48.76,0.5],[0,56.85,0.5],[0,62.9,0.5],[0,68.78,0.5],[0,75.83,0.5],[0,82.39,0.5],[0,88.28,0.5],[0,93.72,0.5],[0,97.54,0.5],[0,100.06,0.5],[0,102.63,0.5]],"isComplete":true},"018da8ea-206c-45f7-035c-575e3458cc70":{"id":"018da8ea-206c-45f7-035c-575e3458cc70","type":"draw","name":"Draw","parentId":"page","childIndex":6,"point":[879.72,270.57],"rotation":0,"style":{"color":"black","size":"small","isFilled":false,"dash":"draw","scale":1},"points":[[0,0.13,0.5],[0,0.13,0.5],[0,0,0.5]],"isComplete":true},"89f2a71a-bb44-4bf5-0501-a313d2aaf166":{"id":"89f2a71a-bb44-4bf5-0501-a313d2aaf166","type":"draw","name":"Draw","parentId":"page","childIndex":7,"point":[900.76,273.82],"rotation":0,"style":{"color":"black","size":"small","isFilled":false,"dash":"draw","scale":1},"points":[[39.03,0,0.5],[39.03,0,0.5],[38.27,0,0.5],[36.36,0.05,0.5],[34.06,0.43,0.5],[31.29,1.43,0.5],[27.58,3.04,0.5],[24.47,4.56,0.5],[21.41,6.37,0.5],[17.77,8.52,0.5],[15.42,9.94,0.5],[13.58,11.49,0.5],[11.6,13.11,0.5],[10.36,14.37,0.5],[9.7,15.52,0.5],[9.28,16.25,0.5],[9.15,16.64,0.5],[9.15,16.95,0.5],[9.15,17.37,0.5],[9.15,17.87,0.5],[9.37,18.73,0.5],[10.38,20.22,0.5],[12.26,22.21,0.5],[14.58,24.54,0.5],[17.08,27.14,0.5],[19.44,29.72,0.5],[21.45,31.83,0.5],[23.79,34.34,0.5],[26.57,37.37,0.5],[28.66,39.68,0.5],[30.38,41.58,0.5],[32.24,43.65,0.5],[33.91,45.68,0.5],[35.55,47.73,0.5],[37.01,49.6,0.5],[38.14,51.26,0.5],[39.19,52.98,0.5],[39.98,54.7,0.5],[40.59,56.23,0.5],[41.06,57.31,0.5],[41.21,58.27,0.5],[41.23,59.11,0.5],[41.23,59.7,0.5],[41.23,60.3,0.5],[41.15,60.9,0.5],[40.65,61.45,0.5],[39.45,62.01,0.5],[37.61,62.83,0.5],[35.31,63.84,0.5],[32.71,64.96,0.5],[29.47,66.3,0.5],[25.34,67.75,0.5],[20.62,69.27,0.5],[16.31,70.5,0.5],[12.85,71.32,0.5],[9.62,72.12,0.5],[6.69,72.84,0.5],[4.28,73.32,0.5],[2.44,73.7,0.5],[1.29,73.91,0.5],[0.62,73.95,0.5],[0.17,73.95,0.5],[0,73.95,0.5]],"isComplete":true},"7d248229-1136-41d2-1cfd-717896f81fc6":{"id":"7d248229-1136-41d2-1cfd-717896f81fc6","type":"draw","name":"Draw","parentId":"page","childIndex":8,"point":[484.11,450.09],"rotation":0,"style":{"color":"black","size":"small","isFilled":false,"dash":"draw","scale":1},"points":[[13.92,8.39,0.5],[13.92,8.39,0.5],[13.79,8.27,0.5],[13.32,8.14,0.5],[12.38,8.14,0.5],[10.83,8.14,0.5],[8.93,8.14,0.5],[7.02,8.14,0.5],[5.5,8.14,0.5],[3.85,8.29,0.5],[2.12,8.99,0.5],[1.15,10.1,0.5],[0.52,11.47,0.5],[0.11,13.24,0.5],[0,15.16,0.5],[0,17.06,0.5],[0,18.96,0.5],[0,21.04,0.5],[0,23.39,0.5],[0,25.69,0.5],[0,27.9,0.5],[0,29.75,0.5],[0,32,0.5],[0,34.16,0.5],[0,35.41,0.5],[0,36.69,0.5],[0,37.91,0.5],[0,39.09,0.5],[0,40.09,0.5],[0.13,40.47,0.5],[0.42,40.77,0.5],[0.87,41.18,0.5],[1.46,41.32,0.5],[2.06,41.32,0.5],[2.65,41.32,0.5],[3.28,41.32,0.5],[4.19,41.16,0.5],[5.55,40.26,0.5],[7.23,38.58,0.5],[9.15,36.72,0.5],[11.46,34.43,0.5],[14.25,31.28,0.5],[17.27,27.77,0.5],[20.12,24.14,0.5],[22.86,20.41,0.5],[25.07,17.3,0.5],[26.78,14.63,0.5],[28.46,11.86,0.5],[29.89,9.24,0.5],[31.09,6.97,0.5],[31.94,5,0.5],[32.53,3.29,0.5],[33.05,2.02,0.5],[33.38,1.18,0.5],[33.67,0.58,0.5],[34.09,0,0.5],[34.21,0,0.5],[34.46,0,0.5],[34.73,0.02,0.5],[34.98,0.28,0.5],[35.12,1.18,0.5],[35.33,2.4,0.5],[35.67,3.64,0.5],[36.02,5.14,0.5],[36.41,7.12,0.5],[36.9,9.71,0.5],[37.61,12.34,0.5],[38.53,14.82,0.5],[39.82,17.49,0.5],[41.21,20.04,0.5],[42.37,22.41,0.5],[43.69,24.86,0.5],[45.12,27.05,0.5],[46.34,28.98,0.5],[47.28,30.52,0.5],[47.97,31.57,0.5],[48.48,32.39,0.5],[48.81,32.91,0.5],[49.08,33.2,0.5],[49.23,33.35,0.5]],"isComplete":true},"40a6d973-88ac-4c50-168c-3c2b30abdf85":{"id":"40a6d973-88ac-4c50-168c-3c2b30abdf85","type":"draw","name":"Draw","parentId":"page","childIndex":9,"point":[607.9,419.71],"rotation":0,"style":{"color":"black","size":"small","isFilled":false,"dash":"draw","scale":1},"points":[[0.78,27.73,0.5],[0.78,27.73,0.5],[0.78,28.31,0.5],[0.78,29.73,0.5],[0.78,31.43,0.5],[0.78,34.65,0.5],[0.78,39.75,0.5],[0.78,43.98,0.5],[0.78,48.71,0.5],[0.78,54.72,0.5],[0.78,59.82,0.5],[0.78,65.32,0.5],[0.78,71.11,0.5],[0.78,75.25,0.5],[0.78,78,0.5],[0.78,80.75,0.5],[0.78,83.33,0.5],[0.78,85.05,0.5],[0.78,86.02,0.5],[0.78,86.45,0.5],[0.77,86.27,0.5],[0.6,85.28,0.5],[0.41,83.55,0.5],[0.19,81.04,0.5],[0,77.7,0.5],[0,72.77,0.5],[0.14,66.51,0.5],[0.94,59.77,0.5],[2.48,52.7,0.5],[4.4,46.31,0.5],[6.98,39.53,0.5],[10.19,31.95,0.5],[13.69,24.59,0.5],[17.27,17.7,0.5],[20.83,11.84,0.5],[24.29,7.33,0.5],[27.07,4.53,0.5],[29.41,2.85,0.5],[31.66,1.41,0.5],[33.89,0.42,0.5],[35.9,0.06,0.5],[37.64,0,0.5],[39.27,0,0.5],[40.5,0,0.5],[41.78,0.52,0.5],[43,1.47,0.5],[44.42,3.3,0.5],[45.93,5.44,0.5],[46.91,7.42,0.5],[47.89,9.65,0.5],[48.9,12.19,0.5],[49.71,14.86,0.5],[50.26,16.86,0.5],[50.66,19.22,0.5],[51.02,21.94,0.5],[51.17,24.4,0.5],[51.17,26.76,0.5],[51.17,28.85,0.5],[51.17,30.66,0.5],[50.92,32.13,0.5],[49.89,33.43,0.5],[48.12,34.79,0.5],[46.17,36.06,0.5],[43.87,37.13,0.5],[40.78,38.24,0.5],[37.14,39.4,0.5],[33.47,40.38,0.5],[29.51,41.14,0.5],[25.05,41.9,0.5],[21.58,42.6,0.5],[18.68,43.07,0.5],[15.54,43.32,0.5],[12.83,43.51,0.5],[10.44,43.7,0.5],[8.62,43.74,0.5],[7.44,43.74,0.5],[6.68,43.74,0.5],[6.26,43.72,0.5],[6.02,43.58,0.5]],"isComplete":true},"b7b78106-962a-4623-21a3-23afa9f04365":{"id":"b7b78106-962a-4623-21a3-23afa9f04365","type":"draw","name":"Draw","parentId":"page","childIndex":10,"point":[671.95,431.84],"rotation":0,"style":{"color":"black","size":"small","isFilled":false,"dash":"draw","scale":1},"points":[[0,24.97,0.5],[0,24.97,0.5],[0,24.86,0.5],[0,24.9,0.5],[0,25.71,0.5],[0,27.33,0.5],[0,29.36,0.5],[0,31.85,0.5],[0,34.62,0.5],[0,36.71,0.5],[0,38.11,0.5],[0,39.6,0.5],[0,40.96,0.5],[0,41.87,0.5],[0,42.3,0.5],[0,42.23,0.5],[0,41.37,0.5],[0,39.96,0.5],[0,38.48,0.5],[0.03,36.54,0.5],[0.41,33.44,0.5],[1.59,29.23,0.5],[3.53,25.43,0.5],[6.5,21.89,0.5],[10.23,18.07,0.5],[14.22,14.44,0.5],[18.58,10.96,0.5],[22.99,8.06,0.5],[27.27,5.68,0.5],[30.93,3.89,0.5],[33.78,2.68,0.5],[36.63,1.6,0.5],[39.51,0.79,0.5],[41.87,0.3,0.5],[43.68,0.05,0.5],[45,0,0.5],[45.87,0,0.5],[46.37,0,0.5],[46.67,0,0.5],[46.92,0,0.5],[47.06,0,0.5]],"isComplete":true},"90453293-c9a0-45fd-11af-0f8d6778a15d":{"id":"90453293-c9a0-45fd-11af-0f8d6778a15d","type":"draw","name":"Draw","parentId":"page","childIndex":11,"point":[719.75,449.17],"rotation":0,"style":{"color":"black","size":"small","isFilled":false,"dash":"draw","scale":1},"points":[[12.37,0,0.5],[12.37,0,0.5],[12.37,0.23,0.5],[12.05,0.89,0.5],[10.49,3.3,0.5],[8.2,7.48,0.5],[6.5,11.12,0.5],[5.37,14.15,0.5],[3.96,18.24,0.5],[2.4,23.34,0.5],[1.2,28.98,0.5],[0.36,33.94,0.5],[0.05,37.95,0.5],[0,42.19,0.5],[0,45.85,0.5],[0,48.12,0.5],[0,50.13,0.5],[0,52.55,0.5],[0.02,54.45,0.5],[0.29,56.11,0.5],[1.1,57.75,0.5],[2.25,59.02,0.5],[3.59,60.03,0.5],[5.14,60.74,0.5],[6.98,61.3,0.5],[9.28,61.75,0.5],[11.64,61.88,0.5],[13.83,61.88,0.5],[16.16,61.77,0.5],[18.52,61.14,0.5],[20.82,59.63,0.5],[23.19,57.55,0.5],[25.52,55.02,0.5],[28.06,51.85,0.5],[30.59,48.05,0.5],[32.93,43.68,0.5],[35.48,38.76,0.5],[37.47,34.53,0.5],[38.98,30.91,0.5],[40.44,26.82,0.5],[41.59,23.41,0.5],[42.45,20.63,0.5],[42.94,18.24,0.5],[43.32,16.33,0.5],[43.5,14.93,0.5],[43.5,14.16,0.5],[43.5,13.59,0.5],[43.47,13.19,0.5],[43.27,12.91,0.5],[42.8,12.62,0.5],[42.2,12.37,0.5],[41.6,12.21,0.5],[40.93,12,0.5],[40.07,11.68,0.5],[38.89,11.26,0.5],[37.35,10.62,0.5],[35.67,9.88,0.5],[33.86,9.01,0.5],[32.09,8.08,0.5],[30.41,7.26,0.5],[28.6,6.38,0.5],[26.7,5.44,0.5],[25.13,4.65,0.5],[24.02,4.11,0.5],[23.04,3.74,0.5],[22.25,3.44,0.5],[21.77,3.27,0.5],[21.47,3.24,0.5],[21.2,3.24,0.5],[20.92,3.24,0.5],[20.65,3.24,0.5],[20.48,3.24,0.5],[20.27,3.24,0.5],[19.84,3.24,0.5],[19.57,3.26,0.5],[19.3,3.42,0.5],[19.03,3.65,0.5],[18.76,3.75,0.5],[18.49,3.75,0.5],[18.27,3.8,0.5],[18.13,3.93,0.5],[17.94,4,0.5],[17.67,4,0.5],[17.4,4,0.5],[17.21,4,0.5],[17.06,4,0.5],[16.94,4,0.5],[16.8,4,0.5],[16.68,4,0.5],[16.66,4.11,0.5]],"isComplete":true},"7a17b436-ac36-4c40-30e7-83c7b1bb482c":{"id":"7a17b436-ac36-4c40-30e7-83c7b1bb482c","type":"draw","name":"Draw","parentId":"page","childIndex":12,"point":[771.25,444.22],"rotation":0,"style":{"color":"black","size":"small","isFilled":false,"dash":"draw","scale":1},"points":[[16.77,0,0.5],[16.77,0,0.5],[16.77,0.59,0.5],[16.77,2.22,0.5],[16.77,4.96,0.5],[16.77,9.02,0.5],[16.77,12.88,0.5],[16.77,16.62,0.5],[16.77,21.1,0.5],[16.77,25.72,0.5],[16.77,30.46,0.5],[16.77,34.85,0.5],[16.77,39.14,0.5],[16.77,43.43,0.5],[16.77,47.18,0.5],[16.77,49.92,0.5],[16.77,52.18,0.5],[16.77,54.74,0.5],[16.77,57.14,0.5],[16.77,58.95,0.5],[16.77,60.27,0.5],[16.77,61.13,0.5],[16.5,61.92,0.5],[16.07,62.44,0.5],[15.66,62.62,0.5],[15.12,62.88,0.5],[14.29,63.05,0.5],[13.29,63.07,0.5],[12.32,63.07,0.5],[11.1,63.04,0.5],[9.7,62.82,0.5],[8.51,62.3,0.5],[7.29,61.62,0.5],[5.92,60.91,0.5],[4.76,60.23,0.5],[3.79,59.64,0.5],[2.89,59.16,0.5],[2.09,58.69,0.5],[1.3,58.22,0.5],[0.59,57.77,0.5],[0,57.39,0.5]],"isComplete":true},"b62f199c-4c4c-4c19-178f-5222c676d797":{"id":"b62f199c-4c4c-4c19-178f-5222c676d797","type":"draw","name":"Draw","parentId":"page","childIndex":13,"point":[789.25,428.83],"rotation":0,"style":{"color":"black","size":"small","isFilled":false,"dash":"draw","scale":1},"points":[[0,0,0.5],[0,0,0.5]],"isComplete":true},"5dd77884-e1ef-413e-0538-09ba647dddff":{"id":"5dd77884-e1ef-413e-0538-09ba647dddff","type":"draw","name":"Draw","parentId":"page","childIndex":14,"point":[797.87,439.15],"rotation":0,"style":{"color":"black","size":"small","isFilled":false,"dash":"draw","scale":1},"points":[[0,24.51,0.5],[0,24.51,0.5],[0.12,24.51,0.5],[0.61,24.51,0.5],[1.66,24.51,0.5],[3.1,24.51,0.5],[5.11,24.51,0.5],[7.57,24.51,0.5],[10.02,24.51,0.5],[12.43,24.51,0.5],[14.47,24.51,0.5],[15.93,24.51,0.5],[17.43,24.51,0.5],[19.01,24.51,0.5],[20.52,24.51,0.5],[22.08,24.44,0.5],[23.22,24.14,0.5],[24.16,23.47,0.5],[25.04,22.61,0.5],[25.7,21.75,0.5],[26.49,20.13,0.5],[27.24,17.81,0.5],[27.7,15.52,0.5],[27.96,13.68,0.5],[28.15,12.16,0.5],[28.34,10.26,0.5],[28.38,8.36,0.5],[28.38,6.6,0.5],[28.38,4.94,0.5],[28.38,3.63,0.5],[28.38,2.56,0.5],[28.36,1.77,0.5],[28.04,1.18,0.5],[27.44,0.75,0.5],[26.62,0.46,0.5],[25.63,0.29,0.5],[24.67,0.13,0.5],[23.5,0,0.5],[21.72,0,0.5],[19.74,0,0.5],[17.82,0,0.5],[15.87,0,0.5],[13.83,0.09,0.5],[11.75,0.46,0.5],[9.93,1.23,0.5],[8.33,2.16,0.5],[7,3.07,0.5],[5.89,3.93,0.5],[5.01,4.76,0.5],[4.31,5.68,0.5],[3.77,6.66,0.5],[3.4,7.64,0.5],[3.09,8.45,0.5],[2.91,9.2,0.5],[2.88,10.15,0.5],[2.88,11.1,0.5],[2.88,12.26,0.5],[2.88,13.72,0.5],[2.88,15.15,0.5],[2.88,16.58,0.5],[2.88,18.35,0.5],[2.88,20.79,0.5],[2.88,22.92,0.5],[2.88,24.7,0.5],[2.88,26.92,0.5],[2.88,29.03,0.5],[2.95,30.93,0.5],[3.34,32.83,0.5],[4.25,34.67,0.5],[5.49,36.32,0.5],[6.92,37.85,0.5],[8.74,39.44,0.5],[11.09,41.01,0.5],[13.59,42.23,0.5],[16.04,43.26,0.5],[18.73,44.15,0.5],[21.71,44.88,0.5],[25.04,45.54,0.5],[28.74,46.06,0.5],[32.38,46.34,0.5],[36.11,46.38,0.5],[40.3,46.38,0.5],[44.76,46.38,0.5],[48.86,45.71,0.5],[54.63,42.61,0.5],[55.97,41.65,0.5],[59.95,37.24,0.5]],"isComplete":true},"c09756a6-bac3-4b27-36fb-156810148e2f":{"id":"c09756a6-bac3-4b27-36fb-156810148e2f","type":"draw","name":"Draw","parentId":"page","childIndex":15,"point":[876.3,434.91],"rotation":0,"style":{"color":"black","size":"small","isFilled":false,"dash":"draw","scale":1},"points":[[14.44,0,0.5],[14.44,0,0.5],[13.97,0,0.5],[12.47,0,0.5],[10.91,0,0.5],[8.94,0.38,0.5],[6.2,1.37,0.5],[4.72,2,0.5],[3.77,2.61,0.5],[1.66,4.56,0.5],[0.16,6.61,0.5],[0,7.74,0.5],[0,8.4,0.5],[0,9.5,0.5],[0,11.74,0.5],[0,13.88,0.5],[0,15.63,0.5],[0,17.73,0.5],[0,20.42,0.5],[0.21,23.92,0.5],[1.22,27.95,0.5],[3.13,31.36,0.5],[5.75,34.45,0.5],[9.25,38.14,0.5],[13.29,41.59,0.5],[16.49,43.73,0.5],[18.97,45.3,0.5],[21.89,46.86,0.5],[24.58,48.05,0.5],[26.63,48.87,0.5],[28.33,49.31,0.5],[29.75,49.52,0.5],[31.09,49.54,0.5],[32.79,49.54,0.5],[34.53,49.53,0.5],[36.81,48.4,0.5]],"isComplete":true},"b7cfd422-2dc9-4a7e-03ff-af2b7b7f900a":{"id":"b7cfd422-2dc9-4a7e-03ff-af2b7b7f900a","type":"draw","name":"Draw","parentId":"page","childIndex":16,"point":[931.81,414.66],"rotation":0,"style":{"color":"black","size":"small","isFilled":false,"dash":"draw","scale":1},"points":[[0,0,0.5],[0,0,0.5],[0,0.59,0.5],[0,2.43,0.5],[0,5.25,0.5],[0,9.29,0.5],[0,14.57,0.5],[0,21.09,0.5],[0,25.83,0.5],[0,29.73,0.5],[0,33.41,0.5],[0,36.05,0.5],[0,39.06,0.5],[0,41.14,0.5],[0,42.64,0.5],[0,43.36,0.5],[0,43.59,0.5],[0.33,43.68,0.5],[0.97,43.68,0.5],[1.88,43.68,0.5],[3.07,43.68,0.5],[4.27,43.68,0.5],[5.78,43.68,0.5],[7.44,43.68,0.5],[8.89,43.68,0.5],[10.3,43.68,0.5],[11.68,43.54,0.5],[13.53,43.19,0.5],[15.61,42.8,0.5],[17.87,42.2,0.5],[19.92,41.59,0.5],[21.58,41.22,0.5],[23.78,40.61,0.5],[25.92,40,0.5],[27.53,39.47,0.5],[28.89,38.95,0.5],[29.99,38.46,0.5],[30.75,38.01,0.5],[31.3,37.71,0.5],[31.63,37.43,0.5]],"isComplete":true},"866e0da3-b1cd-44aa-3365-fd74d9ca2088":{"id":"866e0da3-b1cd-44aa-3365-fd74d9ca2088","type":"draw","name":"Draw","parentId":"page","childIndex":17,"point":[920.87,441.08],"rotation":0,"style":{"color":"black","size":"small","isFilled":false,"dash":"draw","scale":1},"points":[[0,0,0.5],[0,0,0.5],[0.47,0,0.5],[1.42,0,0.5],[3.15,0,0.5],[5.63,0,0.5],[8.38,0,0.5],[11.44,0,0.5],[14.83,0,0.5],[18.27,0,0.5],[20.33,0,0.5],[22.12,0,0.5],[24.03,0,0.5],[24.97,0,0.5],[25.68,0,0.5],[26.11,0,0.5]],"isComplete":true}},"bindings":{}}},"pageStates":{"page":{"id":"page","selectedIds":[],"camera":{"point":[0,0],"zoom":1},"editingId":null}},"assets":{}}}' + ), + home_version: 15.5, +} + +export async function writeV1ContentsToIdb() { + const db = await openDB('keyval-store', 1) + const tx = db.transaction('keyval', 'readwrite') + const store = tx.objectStore('keyval') + for (const [key, value] of Object.entries(v1Contents)) { + store.put(value, key) + } + await tx.done +} diff --git a/apps/dotcom/src/utils/qrcode.ts b/apps/dotcom/src/utils/qrcode.ts new file mode 100644 index 000000000..b2a5c4d24 --- /dev/null +++ b/apps/dotcom/src/utils/qrcode.ts @@ -0,0 +1,9 @@ +export async function createQRCodeImageDataString(url: string) { + const QRCode = await import('qrcode') + return await new Promise((res, rej) => { + QRCode.toDataURL(url, (err, str) => { + if (err) rej(err) + else res(str) + }) + }) +} diff --git a/apps/dotcom/src/utils/remote-sync/ClientWebSocketAdapter.test.ts b/apps/dotcom/src/utils/remote-sync/ClientWebSocketAdapter.test.ts new file mode 100644 index 000000000..1739509d5 --- /dev/null +++ b/apps/dotcom/src/utils/remote-sync/ClientWebSocketAdapter.test.ts @@ -0,0 +1,184 @@ +import { TLSYNC_PROTOCOL_VERSION } from '@tldraw/tlsync' +import * as ws from 'ws' +import { ClientWebSocketAdapter } from './ClientWebSocketAdapter' + +async function waitFor(predicate: () => boolean) { + let safety = 0 + while (!predicate()) { + if (safety++ > 1000) { + throw new Error('waitFor predicate timed out') + } + try { + jest.runAllTimers() + jest.useRealTimers() + await new Promise((resolve) => setTimeout(resolve, 10)) + } finally { + jest.useFakeTimers() + } + } +} + +jest.useFakeTimers() + +// TODO: unskip this test. It accidentally got disabled a long time ago when we moved this file into +// the dotcom folder which didn't have testing set up at the time. We need to spend some time fixing +// it before it can be re-enabled. +describe.skip(ClientWebSocketAdapter, () => { + let adapter: ClientWebSocketAdapter + let wsServer: ws.Server + let connectedWs: ws.WebSocket + const connectMock = jest.fn((socket) => { + connectedWs = socket + }) + beforeEach(() => { + adapter = new ClientWebSocketAdapter(() => 'ws://localhost:2233') + wsServer = new ws.Server({ port: 2233 }) + wsServer.on('connection', connectMock) + }) + afterEach(() => { + adapter.close() + wsServer.close() + connectMock.mockClear() + }) + + it('should be able to be constructed', () => { + expect(adapter).toBeTruthy() + }) + it('should start with connectionStatus=offline', () => { + expect(adapter.connectionStatus).toBe('offline') + }) + it('should start with connectionStatus=offline', () => { + expect(adapter.connectionStatus).toBe('offline') + }) + it('should respond to onopen events by setting connectionStatus=online', async () => { + await waitFor(() => adapter._ws?.readyState === WebSocket.OPEN) + expect(adapter.connectionStatus).toBe('online') + }) + it('should respond to onerror events by setting connectionStatus=error', async () => { + await waitFor(() => adapter._ws?.readyState === WebSocket.OPEN) + adapter._ws?.onerror?.({} as any) + expect(adapter.connectionStatus).toBe('error') + }) + it('should try to reopen the connection if there was an error', () => { + const prevWes = adapter._ws + adapter._ws?.onerror?.({} as any) + jest.advanceTimersByTime(1000) + expect(adapter._ws).not.toBe(prevWes) + expect(adapter._ws?.readyState).toBe(WebSocket.CONNECTING) + }) + it('should transition to online if a retry succeeds', async () => { + adapter._ws?.onerror?.({} as any) + await waitFor(() => adapter.connectionStatus === 'online') + expect(adapter.connectionStatus).toBe('online') + }) + it('should call .close on the underlying socket if .close is called before the socket opens', async () => { + const closeSpy = jest.spyOn(adapter._ws!, 'close') + adapter.close() + await waitFor(() => closeSpy.mock.calls.length > 0) + expect(closeSpy).toHaveBeenCalled() + }) + it('should transition to offline if the server disconnects', async () => { + await waitFor(() => adapter._ws?.readyState === WebSocket.OPEN) + connectedWs.terminate() + await waitFor(() => adapter._ws?.readyState === WebSocket.CLOSED) + expect(adapter.connectionStatus).toBe('offline') + }) + it('retries to connect if the server disconnects', async () => { + await waitFor(() => adapter._ws?.readyState === WebSocket.OPEN) + connectedWs.terminate() + await waitFor(() => adapter._ws?.readyState === WebSocket.CLOSED) + expect(adapter.connectionStatus).toBe('offline') + await waitFor(() => adapter._ws?.readyState === WebSocket.OPEN) + expect(adapter.connectionStatus).toBe('online') + connectedWs.terminate() + await waitFor(() => adapter._ws?.readyState === WebSocket.CLOSED) + expect(adapter.connectionStatus).toBe('offline') + await waitFor(() => adapter._ws?.readyState === WebSocket.OPEN) + expect(adapter.connectionStatus).toBe('online') + }) + + it('closes the socket if the window goes offline and attempts to reconnect', async () => { + await waitFor(() => adapter._ws?.readyState === WebSocket.OPEN) + const closeSpy = jest.spyOn(adapter._ws!, 'close') + window.dispatchEvent(new Event('offline')) + expect(closeSpy).toHaveBeenCalled() + await waitFor(() => adapter._ws?.readyState === WebSocket.OPEN) + }) + + it('attempts to reconnect early if the window comes back online', async () => { + await waitFor(() => adapter._ws?.readyState === WebSocket.OPEN) + wsServer.close() + window.dispatchEvent(new Event('offline')) + adapter._reconnectTimeout.intervalLength = 50000 + window.dispatchEvent(new Event('online')) + expect(adapter._reconnectTimeout.intervalLength).toBeLessThan(1000) + }) + + it('supports receiving messages', async () => { + const onMessage = jest.fn() + adapter.onReceiveMessage(onMessage) + connectMock.mockImplementationOnce((ws) => { + ws.send('{ "type": "message", "data": "hello" }') + }) + + await waitFor(() => onMessage.mock.calls.length === 1) + expect(onMessage).toHaveBeenCalledWith({ type: 'message', data: 'hello' }) + }) + + // TODO: this is failing on github actions, investigate + it.skip('supports sending messages', async () => { + const onMessage = jest.fn() + connectMock.mockImplementationOnce((ws) => { + ws.on('message', onMessage) + }) + + await waitFor(() => adapter._ws?.readyState === WebSocket.OPEN) + + adapter.sendMessage({ + type: 'connect', + connectRequestId: 'test', + schema: { schemaVersion: 0, storeVersion: 0, recordVersions: {} }, + protocolVersion: TLSYNC_PROTOCOL_VERSION, + lastServerClock: 0, + }) + + await waitFor(() => onMessage.mock.calls.length === 1) + + expect(onMessage.mock.calls[0][0].toString()).toBe( + '{"type":"connect","instanceId":"test","lastServerClock":0}' + ) + }) + + it('signals status changes', async () => { + const onStatusChange = jest.fn() + adapter.onStatusChange(onStatusChange) + await waitFor(() => adapter._ws?.readyState === WebSocket.OPEN) + expect(onStatusChange).toHaveBeenCalledWith('online') + connectedWs.terminate() + await waitFor(() => adapter._ws?.readyState === WebSocket.CLOSED) + expect(onStatusChange).toHaveBeenCalledWith('offline') + await waitFor(() => adapter._ws?.readyState === WebSocket.OPEN) + expect(onStatusChange).toHaveBeenCalledWith('online') + connectedWs.terminate() + await waitFor(() => adapter._ws?.readyState === WebSocket.CLOSED) + expect(onStatusChange).toHaveBeenCalledWith('offline') + await waitFor(() => adapter._ws?.readyState === WebSocket.OPEN) + expect(onStatusChange).toHaveBeenCalledWith('online') + adapter._ws?.onerror?.({} as any) + expect(onStatusChange).toHaveBeenCalledWith('error') + }) + + it('signals status changes while restarting', async () => { + const onStatusChange = jest.fn() + await waitFor(() => adapter._ws?.readyState === WebSocket.OPEN) + + adapter.onStatusChange(onStatusChange) + + adapter.restart() + + await waitFor(() => onStatusChange.mock.calls.length === 2) + + expect(onStatusChange).toHaveBeenCalledWith('offline') + expect(onStatusChange).toHaveBeenCalledWith('online') + }) +}) diff --git a/apps/dotcom/src/utils/remote-sync/ClientWebSocketAdapter.ts b/apps/dotcom/src/utils/remote-sync/ClientWebSocketAdapter.ts new file mode 100644 index 000000000..2b12560ba --- /dev/null +++ b/apps/dotcom/src/utils/remote-sync/ClientWebSocketAdapter.ts @@ -0,0 +1,235 @@ +import { atom, Atom, TLRecord } from '@tldraw/tldraw' +import { + chunk, + serializeMessage, + TLPersistentClientSocket, + TLPersistentClientSocketStatus, + TLSocketClientSentEvent, + TLSocketServerSentEvent, +} from '@tldraw/tlsync' + +function windowListen(...args: Parameters) { + window.addEventListener(...args) + return () => { + window.removeEventListener(...args) + } +} + +function debug(...args: any[]) { + // @ts-ignore + if (typeof window !== 'undefined' && window.__tldraw_socket_debug) { + // eslint-disable-next-line no-console + console.log(...args, new Error().stack) + } +} + +export class ClientWebSocketAdapter implements TLPersistentClientSocket { + _ws: WebSocket | null = null + + wasManuallyClosed = false + + disposables: (() => void)[] = [] + + close() { + this.wasManuallyClosed = true + this.disposables.forEach((d) => d()) + this._reconnectTimeout.clear() + if (this._ws?.readyState === WebSocket.OPEN) { + debug('close d') + this._ws.close() + } + } + + constructor(private getUri: () => Promise | string) { + this.disposables.push( + windowListen('online', () => { + debug('window online') + if (this.connectionStatus !== 'online') { + this._reconnectTimeout.clear() + this._attemptReconnect() + } + }), + windowListen('offline', () => { + debug('window offline') + if (this.connectionStatus === 'online') { + this._ws?.close() + this._ws?.onclose?.(null as any) + } + }), + windowListen('pointermove', () => { + // if the pointer moves while we are offline, we should try to reconnect more + // often than every 5 mins! + if (this.connectionStatus !== 'online') { + this._reconnectTimeout.userInteractionOccurred() + } + }), + windowListen('keydown', () => { + // if the user pressed a key while we are offline, we should try to reconnect more + // often than every 5 mins! + if (this.connectionStatus !== 'online') { + this._reconnectTimeout.userInteractionOccurred() + } + }) + ) + this._reconnectTimeout.run() + } + + private handleDisconnect(status: Exclude) { + debug('handleDisconnect', status, this.connectionStatus) + if ( + // if the status is the same as before, don't do anything + this.connectionStatus === status || + // if we receive an error we only care about it while we're in the initial state + (status === 'error' && this.connectionStatus === 'offline') + ) { + this._attemptReconnect() + return + } + this._connectionStatus.set(status) + this.statusListeners.forEach((cb) => cb(status)) + this._reconnectTimeout.clear() + this._attemptReconnect() + } + + private configureSocket() { + const ws = this._ws + if (!ws) return + ws.onopen = () => { + debug('ws.onopen') + // ws might be opened multiple times so need to check that it wasn't already supplanted + if (this._ws !== ws || this.wasManuallyClosed) { + if (ws.readyState === WebSocket.OPEN) { + debug('close a') + ws.close() + } + return + } + this._connectionStatus.set('online') + this.statusListeners.forEach((cb) => cb(this.connectionStatus)) + this._reconnectTimeout.clear() + } + ws.onclose = () => { + debug('ws.onclose') + this.handleDisconnect('offline') + } + ws.onerror = () => { + debug('ws.onerror') + this.handleDisconnect('error') + } + ws.onmessage = (ev) => { + const parsed = JSON.parse(ev.data.toString()) + this.messageListeners.forEach((cb) => cb(parsed)) + } + } + + readonly _reconnectTimeout = new ExponentialBackoffTimeout(async () => { + debug('close b') + this._ws?.close() + this._ws = new WebSocket(await this.getUri()) + this.configureSocket() + }) + + _attemptReconnect() { + debug('_attemptReconnect', this.wasManuallyClosed) + if (this.wasManuallyClosed) { + return + } + this._reconnectTimeout.run() + } + + _connectionStatus: Atom = atom( + 'websocket connection status', + 'initial' + ) + + // eslint-disable-next-line no-restricted-syntax + get connectionStatus(): TLPersistentClientSocketStatus { + const status = this._connectionStatus.get() + return status === 'initial' ? 'offline' : status + } + + sendMessage(msg: TLSocketClientSentEvent) { + if (!this._ws) return + if (this.connectionStatus === 'online') { + const chunks = chunk(serializeMessage(msg)) + for (const part of chunks) { + this._ws.send(part) + } + } else { + console.warn('Tried to send message while ' + this.connectionStatus) + } + } + + private messageListeners = new Set<(msg: TLSocketServerSentEvent) => void>() + onReceiveMessage(cb: (val: TLSocketServerSentEvent) => void) { + this.messageListeners.add(cb) + return () => { + this.messageListeners.delete(cb) + } + } + + private statusListeners = new Set<(status: TLPersistentClientSocketStatus) => void>() + onStatusChange(cb: (val: TLPersistentClientSocketStatus) => void) { + this.statusListeners.add(cb) + return () => { + this.statusListeners.delete(cb) + } + } + + restart() { + debug('close c') + this.close() + this.wasManuallyClosed = false + this._reconnectTimeout.clear() + this._reconnectTimeout.runNow() + } +} + +class ExponentialBackoffTimeout { + private timeout: NodeJS.Timeout | null = null + private nextScheduledRunTimestamp = 0 + intervalLength: number + + constructor( + private cb: () => Promise, + // five mins + private readonly maxIdleIntervalLength: number = 1000 * 60 * 5, + // five seconds + private readonly maxInteractiveIntervalLength: number = 1000, + private startIntervalLength: number = 500 + ) { + this.intervalLength = startIntervalLength + } + + runNow() { + this.cb() + } + + run() { + if (this.timeout) return + this.timeout = setTimeout(() => { + this.cb() + this.intervalLength = Math.min(this.intervalLength * 2, this.maxIdleIntervalLength) + if (this.timeout) { + clearTimeout(this.timeout) + this.timeout = null + } + }, this.intervalLength) + this.nextScheduledRunTimestamp = Date.now() + this.intervalLength + } + + clear() { + this.intervalLength = this.startIntervalLength + if (this.timeout) { + clearTimeout(this.timeout) + this.timeout = null + } + } + + userInteractionOccurred() { + if (Date.now() + this.maxInteractiveIntervalLength < this.nextScheduledRunTimestamp) { + this.clear() + this.run() + } + } +} diff --git a/apps/dotcom/src/utils/remote-sync/remote-sync.ts b/apps/dotcom/src/utils/remote-sync/remote-sync.ts new file mode 100644 index 000000000..bd111518d --- /dev/null +++ b/apps/dotcom/src/utils/remote-sync/remote-sync.ts @@ -0,0 +1,19 @@ +import { Signal, TLStoreSnapshot, TLUserPreferences } from '@tldraw/tldraw' +import { TLIncompatibilityReason } from '@tldraw/tlsync' + +/** @public */ +export class RemoteSyncError extends Error { + override name = 'RemoteSyncError' + constructor(public readonly reason: TLIncompatibilityReason) { + super(`remote sync error: ${reason}`) + } +} + +/** @public */ +export type UseSyncClientConfig = { + uri: string + roomId?: string + userPreferences?: Signal + snapshotForNewRoomRef?: { current: null | TLStoreSnapshot } + getAccessToken?: () => Promise | string | undefined | null +} diff --git a/apps/dotcom/src/utils/scratch-persistence-key.ts b/apps/dotcom/src/utils/scratch-persistence-key.ts new file mode 100644 index 000000000..9f2ec1b83 --- /dev/null +++ b/apps/dotcom/src/utils/scratch-persistence-key.ts @@ -0,0 +1,17 @@ +/** + * What is going on in this file? + * + * We had some bad early assumptions about how we would store documents. + * Which ended up with us generating random persistenceKey strings for the + * 'scratch' document for each user (i.e. each browser context), and storing it in localStorage. + * + * Many users still have that random string in their localStorage so we need to load it. But for new + * users it does not need to be unique and we can just use a constant. + */ +// DO NOT CHANGE THESE WITHOUT ADDING MIGRATION LOGIC. DOING SO WOULD WIPE ALL EXISTING LOCAL DATA. +const defaultDocumentKey = 'TLDRAW_DEFAULT_DOCUMENT_NAME_v2' +const w = typeof window === 'undefined' ? undefined : window + +export const SCRATCH_PERSISTENCE_KEY = + (w?.localStorage.getItem(defaultDocumentKey) as any) ?? 'tldraw_document_v3' +w?.localStorage.setItem(defaultDocumentKey, SCRATCH_PERSISTENCE_KEY) diff --git a/apps/dotcom/src/utils/sharing.ts b/apps/dotcom/src/utils/sharing.ts new file mode 100644 index 000000000..6bf21ad8c --- /dev/null +++ b/apps/dotcom/src/utils/sharing.ts @@ -0,0 +1,273 @@ +import { + AssetRecordType, + Editor, + SerializedSchema, + SerializedStore, + TLAsset, + TLAssetId, + TLRecord, + TLShape, + TLShapeId, + TLUiEventHandler, + TLUiOverrides, + TLUiToastsContextType, + TLUiTranslationKey, + assert, + findMenuItem, + isShape, + menuGroup, + menuItem, +} from '@tldraw/tldraw' +import { useMemo } from 'react' +import { useNavigate, useSearchParams } from 'react-router-dom' +import { useMultiplayerAssets } from '../hooks/useMultiplayerAssets' +import { getViewportUrlQuery } from '../hooks/useUrlState' +import { cloneAssetForShare } from './cloneAssetForShare' +import { ASSET_UPLOADER_URL } from './config' +import { shouldLeaveSharedProject } from './shouldLeaveSharedProject' +import { trackAnalyticsEvent } from './trackAnalyticsEvent' +import { UI_OVERRIDE_TODO_EVENT, useHandleUiEvents } from './useHandleUiEvent' + +export const SHARE_PROJECT_ACTION = 'share-project' as const +export const SHARE_SNAPSHOT_ACTION = 'share-snapshot' as const +const LEAVE_SHARED_PROJECT_ACTION = 'leave-shared-project' as const +export const FORK_PROJECT_ACTION = 'fork-project' as const +const CREATE_SNAPSHOT_ENDPOINT = `/api/snapshots` +const SNAPSHOT_UPLOAD_URL = `/api/new-room` + +type SnapshotRequestBody = { + schema: SerializedSchema + snapshot: SerializedStore +} + +type CreateSnapshotRequestBody = { + schema: SerializedSchema + snapshot: SerializedStore + parent_slug?: string | string[] | undefined +} + +type CreateSnapshotResponseBody = + | { + error: false + roomId: string + } + | { + error: true + message: string + } + +async function getSnapshotLink( + source: string, + editor: Editor, + handleUiEvent: TLUiEventHandler, + addToast: TLUiToastsContextType['addToast'], + msg: (id: TLUiTranslationKey) => string, + uploadFileToAsset: (file: File) => Promise, + parentSlug: string | undefined +) { + handleUiEvent('share-snapshot' as UI_OVERRIDE_TODO_EVENT, { source } as UI_OVERRIDE_TODO_EVENT) + const data = await getRoomData(editor, addToast, msg, uploadFileToAsset) + if (!data) return '' + + const res = await fetch(CREATE_SNAPSHOT_ENDPOINT, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + snapshot: data, + schema: editor.store.schema.serialize(), + parent_slug: parentSlug, + } satisfies CreateSnapshotRequestBody), + }) + const response = (await res.json()) as CreateSnapshotResponseBody + + if (!res.ok || response.error) { + console.error(await res.text()) + return '' + } + const paramsToUse = getViewportUrlQuery(editor) + const params = paramsToUse ? `?${new URLSearchParams(paramsToUse).toString()}` : '' + return new Blob([`${window.location.origin}/s/${response.roomId}${params}`], { + type: 'text/plain', + }) +} + +export function useSharing({ isMultiplayer }: { isMultiplayer: boolean }): TLUiOverrides { + const navigate = useNavigate() + const id = useSearchParams()[0].get('id') ?? undefined + const uploadFileToAsset = useMultiplayerAssets(ASSET_UPLOADER_URL) + const handleUiEvent = useHandleUiEvents() + + return useMemo( + (): TLUiOverrides => ({ + actions(editor, actions, { addToast, msg, addDialog }) { + actions[LEAVE_SHARED_PROJECT_ACTION] = { + id: LEAVE_SHARED_PROJECT_ACTION, + label: 'action.leave-shared-project', + readonlyOk: true, + onSelect: async () => { + const shouldLeave = await shouldLeaveSharedProject(addDialog) + if (!shouldLeave) return + + handleUiEvent('leave-shared-project', {}) + + navigate('/') + }, + } + actions[SHARE_PROJECT_ACTION] = { + id: SHARE_PROJECT_ACTION, + label: 'action.share-project', + readonlyOk: true, + onSelect: async (source) => { + try { + handleUiEvent('share-project', { source }) + const data = await getRoomData(editor, addToast, msg, uploadFileToAsset) + if (!data) return + + const res = await fetch(SNAPSHOT_UPLOAD_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + schema: editor.store.schema.serialize(), + snapshot: data, + } satisfies SnapshotRequestBody), + }) + + const response = (await res.json()) as { error: boolean; slug?: string } + if (!res.ok || response.error) { + console.error(await res.text()) + throw new Error('Failed to upload snapshot') + } + + const query = getViewportUrlQuery(editor) + + navigate(`/r/${response.slug}?${new URLSearchParams(query ?? {}).toString()}`) + } catch (error) { + console.error(error) + addToast({ + title: 'Error', + description: msg('share-menu.upload-failed'), + }) + } + }, + } + actions[SHARE_SNAPSHOT_ACTION] = { + id: SHARE_SNAPSHOT_ACTION, + label: 'share-menu.create-snapshot-link', + readonlyOk: true, + onSelect: async (source) => { + const result = getSnapshotLink( + source, + editor, + handleUiEvent, + addToast, + msg, + uploadFileToAsset, + id + ) + if (navigator?.clipboard?.write) { + await navigator.clipboard.write([ + new ClipboardItem({ + 'text/plain': result, + }), + ]) + } else if (navigator?.clipboard?.writeText) { + const link = await result + if (link === '') return + navigator.clipboard.writeText(await link.text()) + } + }, + } + actions[FORK_PROJECT_ACTION] = { + ...actions[SHARE_PROJECT_ACTION], + id: FORK_PROJECT_ACTION, + label: 'action.fork-project', + } + return actions + }, + menu(editor, menu, { actions }) { + const fileMenu = findMenuItem(menu, ['menu', 'file']) + assert(fileMenu.type === 'submenu') + if (isMultiplayer) { + fileMenu.children.unshift( + menuGroup( + 'share', + menuItem(actions[FORK_PROJECT_ACTION]), + menuItem(actions[LEAVE_SHARED_PROJECT_ACTION]) + )! + ) + } else { + fileMenu.children.unshift(menuGroup('share', menuItem(actions[SHARE_PROJECT_ACTION]))!) + } + return menu + }, + }), + [handleUiEvent, navigate, uploadFileToAsset, id, isMultiplayer] + ) +} + +async function getRoomData( + editor: Editor, + addToast: TLUiToastsContextType['addToast'], + msg: (id: TLUiTranslationKey) => string, + uploadFileToAsset: (file: File) => Promise +) { + const rawData = editor.store.serialize() + + // rawData contains a cache of previously added assets, + // which we don't want included in the shared document. + // So let's strip it out. + + // our final object that holds the data that we'll persist to a stash + const data: Record = {} + + // let's get all the assets/shapes in data + const shapes = new Map() + const assets = new Map() + + for (const record of Object.values(rawData)) { + if (AssetRecordType.isInstance(record)) { + // collect assets separately, don't add them to the proper doc yet + assets.set(record.id, record) + continue + } + data[record.id] = record + if (isShape(record)) { + shapes.set(record.id, record) + } + } + + // now add only those assets that are referenced in shapes + for (const shape of shapes.values()) { + if ('assetId' in shape.props) { + const asset = assets.get(shape.props.assetId as TLAssetId) + // if we can't find the asset it either means + // somethings gone wrong or we've already + // processed it + if (!asset) continue + + data[asset.id] = await cloneAssetForShare(asset, uploadFileToAsset) + // remove the asset after processing so we don't clone it multiple times + assets.delete(asset.id) + } + } + + const size = new Blob([JSON.stringify(data)]).size + + if (size > 3999999) { + addToast({ + title: 'Too big!', + description: msg('share-menu.project-too-large'), + }) + + trackAnalyticsEvent('shared-fail-too-big', { + size: size.toString(), + }) + + return null + } + return data +} diff --git a/apps/dotcom/src/utils/shouldClearDocument.tsx b/apps/dotcom/src/utils/shouldClearDocument.tsx new file mode 100644 index 000000000..def4f44fd --- /dev/null +++ b/apps/dotcom/src/utils/shouldClearDocument.tsx @@ -0,0 +1,76 @@ +import { Button, Dialog, TLUiDialogsContextType, useTranslation } from '@tldraw/tldraw' +import { useState } from 'react' +import { userPreferences } from './userPreferences' + +export async function shouldClearDocument(addDialog: TLUiDialogsContextType['addDialog']) { + if (userPreferences.showFileClearWarning.get()) { + const shouldContinue = await new Promise((resolve) => { + addDialog({ + component: ({ onClose }) => ( + { + resolve(false) + onClose() + }} + onContinue={() => { + resolve(true) + onClose() + }} + /> + ), + onClose: () => { + resolve(false) + }, + }) + }) + + return shouldContinue + } + return true +} + +function ConfirmClearDialog({ + onCancel, + onContinue, +}: { + onCancel: () => void + onContinue: () => void +}) { + const msg = useTranslation() + const [dontShowAgain, setDontShowAgain] = useState(false) + return ( + <> + + {msg('file-system.confirm-clear.title')} + + + + {msg('file-system.confirm-clear.description')} + + + + + + + + ) +} diff --git a/apps/dotcom/src/utils/shouldLeaveSharedProject.tsx b/apps/dotcom/src/utils/shouldLeaveSharedProject.tsx new file mode 100644 index 000000000..cd9fd8df5 --- /dev/null +++ b/apps/dotcom/src/utils/shouldLeaveSharedProject.tsx @@ -0,0 +1,82 @@ +import { + Button, + Dialog, + TLUiDialogsContextType, + useLocalStorageState, + useTranslation, +} from '@tldraw/tldraw' +import { userPreferences } from './userPreferences' + +export async function shouldLeaveSharedProject(addDialog: TLUiDialogsContextType['addDialog']) { + if (userPreferences.showFileOpenWarning.get()) { + const shouldContinue = await new Promise((resolve) => { + addDialog({ + component: ({ onClose }) => ( + { + resolve(false) + onClose() + }} + onContinue={() => { + resolve(true) + onClose() + }} + /> + ), + onClose: () => { + resolve(false) + }, + }) + }) + + return shouldContinue + } + return true +} + +function ConfirmLeaveDialog({ + onCancel, + onContinue, +}: { + onCancel: () => void + onContinue: () => void +}) { + const msg = useTranslation() + const [dontShowAgain, setDontShowAgain] = useLocalStorageState('confirm-leave', false) + + return ( + <> + + {msg('sharing.confirm-leave.title')} + + + + {msg('sharing.confirm-leave.description')} + + + + + + + + ) +} diff --git a/apps/dotcom/src/utils/shouldOverrideDocument.tsx b/apps/dotcom/src/utils/shouldOverrideDocument.tsx new file mode 100644 index 000000000..bbfdd5d59 --- /dev/null +++ b/apps/dotcom/src/utils/shouldOverrideDocument.tsx @@ -0,0 +1,76 @@ +import { Button, Dialog, TLUiDialogsContextType, useTranslation } from '@tldraw/tldraw' +import { useState } from 'react' +import { userPreferences } from './userPreferences' + +export async function shouldOverrideDocument(addDialog: TLUiDialogsContextType['addDialog']) { + if (userPreferences.showFileOpenWarning.get()) { + const shouldContinue = await new Promise((resolve) => { + addDialog({ + component: ({ onClose }) => ( + { + resolve(false) + onClose() + }} + onContinue={() => { + resolve(true) + onClose() + }} + /> + ), + onClose: () => { + resolve(false) + }, + }) + }) + + return shouldContinue + } + return true +} + +function ConfirmOpenDialog({ + onCancel, + onContinue, +}: { + onCancel: () => void + onContinue: () => void +}) { + const msg = useTranslation() + const [dontShowAgain, setDontShowAgain] = useState(false) + return ( + <> + + {msg('file-system.confirm-open.title')} + + + + {msg('file-system.confirm-open.description')} + + + + + + + + ) +} diff --git a/apps/dotcom/src/utils/trackAnalyticsEvent.ts b/apps/dotcom/src/utils/trackAnalyticsEvent.ts new file mode 100644 index 000000000..ce1710628 --- /dev/null +++ b/apps/dotcom/src/utils/trackAnalyticsEvent.ts @@ -0,0 +1,5 @@ +import va from '@vercel/analytics' + +export function trackAnalyticsEvent(name: string, data: { [key: string]: any }) { + va.track(name, data) +} diff --git a/apps/dotcom/src/utils/useCursorChat.ts b/apps/dotcom/src/utils/useCursorChat.ts new file mode 100644 index 000000000..75bd743bd --- /dev/null +++ b/apps/dotcom/src/utils/useCursorChat.ts @@ -0,0 +1,63 @@ +import { TLUiOverrides, menuGroup, menuItem } from '@tldraw/tldraw' +import { useMemo } from 'react' +import { useHandleUiEvents } from './useHandleUiEvent' + +export const CURSOR_CHAT_ACTION = 'open-cursor-chat' as const + +export function useCursorChat(): TLUiOverrides { + const handleUiEvent = useHandleUiEvents() + return useMemo( + (): TLUiOverrides => ({ + actions(editor, actions) { + actions[CURSOR_CHAT_ACTION] = { + id: 'open-cursor-chat', + label: 'action.open-cursor-chat', + readonlyOk: true, + kbd: '/', + onSelect(source: any) { + handleUiEvent('open-cursor-chat', { source }) + + // Don't open cursor chat if we're on a touch device + if (editor.getInstanceState().isCoarsePointer) { + return + } + + editor.updateInstanceState({ isChatting: true }) + }, + } + return actions + }, + contextMenu(editor, contextMenu, { actions }) { + if (editor.getSelectedShapes().length > 0 || editor.getInstanceState().isCoarsePointer) { + return contextMenu + } + + const cursorChatGroup = menuGroup('cursor-chat', menuItem(actions[CURSOR_CHAT_ACTION])) + if (!cursorChatGroup) { + return contextMenu + } + + const clipboardGroupIndex = contextMenu.findIndex((group) => group.id === 'clipboard-group') + if (clipboardGroupIndex === -1) { + contextMenu.push(cursorChatGroup) + return contextMenu + } + + contextMenu.splice(clipboardGroupIndex + 1, 0, cursorChatGroup) + return contextMenu + }, + keyboardShortcutsMenu(editor, keyboardShortcutsMenu, { actions }) { + const group = menuGroup( + 'shortcuts-dialog.collaboration', + menuItem(actions[CURSOR_CHAT_ACTION]) + ) + if (!group) { + return keyboardShortcutsMenu + } + keyboardShortcutsMenu.push(group) + return keyboardShortcutsMenu + }, + }), + [handleUiEvent] + ) +} diff --git a/apps/dotcom/src/utils/useFileSystem.tsx b/apps/dotcom/src/utils/useFileSystem.tsx new file mode 100644 index 000000000..9639a4552 --- /dev/null +++ b/apps/dotcom/src/utils/useFileSystem.tsx @@ -0,0 +1,155 @@ +import { + Editor, + TLDRAW_FILE_EXTENSION, + TLStore, + TLUiActionItem, + TLUiEventHandler, + TLUiOverrides, + assert, + findMenuItem, + menuGroup, + menuItem, + parseAndLoadDocument, + serializeTldrawJsonBlob, + transact, +} from '@tldraw/tldraw' +import { fileOpen, fileSave } from 'browser-fs-access' +import { useMemo } from 'react' +import { shouldClearDocument } from './shouldClearDocument' +import { shouldOverrideDocument } from './shouldOverrideDocument' +import { useHandleUiEvents } from './useHandleUiEvent' + +const SAVE_FILE_COPY_ACTION = 'save-file-copy' +const OPEN_FILE_ACTION = 'open-file' +const NEW_PROJECT_ACTION = 'new-file' + +const saveFileNames = new WeakMap() + +export function useFileSystem({ isMultiplayer }: { isMultiplayer: boolean }): TLUiOverrides { + const handleUiEvent = useHandleUiEvents() + + return useMemo((): TLUiOverrides => { + return { + actions(editor, actions, { addToast, msg, addDialog }) { + actions[SAVE_FILE_COPY_ACTION] = getSaveFileCopyAction(editor, handleUiEvent) + actions[OPEN_FILE_ACTION] = { + id: OPEN_FILE_ACTION, + label: 'action.open-file', + readonlyOk: true, + kbd: '$o', + async onSelect(source) { + handleUiEvent('open-file', { source }) + // open in multiplayer is not currently supported + if (isMultiplayer) { + addToast({ + title: msg('file-system.shared-document-file-open-error.title'), + description: msg('file-system.shared-document-file-open-error.description'), + }) + return + } + + const shouldOverride = await shouldOverrideDocument(addDialog) + if (!shouldOverride) return + + let file + try { + file = await fileOpen({ + extensions: [TLDRAW_FILE_EXTENSION], + multiple: false, + description: 'tldraw project', + }) + } catch (e) { + // user cancelled + return + } + + await parseAndLoadDocument(editor, await file.text(), msg, addToast) + }, + } + actions[NEW_PROJECT_ACTION] = { + id: NEW_PROJECT_ACTION, + label: 'action.new-project', + readonlyOk: true, + async onSelect(source) { + handleUiEvent('create-new-project', { source }) + const shouldOverride = await shouldClearDocument(addDialog) + if (!shouldOverride) return + + transact(() => { + const isFocused = editor.getInstanceState().isFocused + editor.store.clear() + editor.store.ensureStoreIsUsable() + editor.history.clear() + editor.updateViewportScreenBounds() + editor.updateRenderingBounds() + editor.updateInstanceState({ isFocused }) + }) + }, + } + return actions + }, + menu(editor, menu, { actions }) { + const fileMenu = findMenuItem(menu, ['menu', 'file']) + assert(fileMenu.type === 'submenu') + + const saveItem = menuItem(actions[SAVE_FILE_COPY_ACTION]) + const openItem = menuItem(actions[OPEN_FILE_ACTION]) + const newItem = menuItem(actions[NEW_PROJECT_ACTION]) + const group = isMultiplayer + ? // open is not currently supported in multiplayer + menuGroup('filesystem', saveItem) + : menuGroup('filesystem', newItem, openItem, saveItem) + fileMenu.children.unshift(group!) + + return menu + }, + keyboardShortcutsMenu(editor, menu, { actions }) { + const fileItems = findMenuItem(menu, ['shortcuts-dialog.file']) + assert(fileItems.type === 'group') + fileItems.children.unshift(menuItem(actions[SAVE_FILE_COPY_ACTION])) + if (!isMultiplayer) { + fileItems.children.unshift(menuItem(actions[OPEN_FILE_ACTION])) + } + + return menu + }, + } + }, [isMultiplayer, handleUiEvent]) +} + +export function getSaveFileCopyAction( + editor: Editor, + handleUiEvent: TLUiEventHandler +): TLUiActionItem { + return { + id: SAVE_FILE_COPY_ACTION, + label: 'action.save-copy', + readonlyOk: true, + kbd: '$s', + async onSelect(source) { + handleUiEvent('save-project-to-file', { source }) + const defaultName = saveFileNames.get(editor.store) || `Untitled${TLDRAW_FILE_EXTENSION}` + + const blobToSave = serializeTldrawJsonBlob(editor.store) + let handle + try { + handle = await fileSave(blobToSave, { + fileName: defaultName, + extensions: [TLDRAW_FILE_EXTENSION], + description: 'tldraw project', + }) + } catch (e) { + // user cancelled + return + } + + if (handle) { + // we deliberately don't store the handle for re-use + // next time. we always want to save a copy, but to + // help the user out we'll remember the last name + // they used + saveFileNames.set(editor.store, handle.name) + } + }, + } +} diff --git a/apps/dotcom/src/utils/useHandleUiEvent.tsx b/apps/dotcom/src/utils/useHandleUiEvent.tsx new file mode 100644 index 000000000..b78f2ea48 --- /dev/null +++ b/apps/dotcom/src/utils/useHandleUiEvent.tsx @@ -0,0 +1,7 @@ +import { trackAnalyticsEvent } from './trackAnalyticsEvent' + +export type UI_OVERRIDE_TODO_EVENT = any + +export function useHandleUiEvents() { + return trackAnalyticsEvent +} diff --git a/apps/dotcom/src/utils/userPreferences.ts b/apps/dotcom/src/utils/userPreferences.ts new file mode 100644 index 000000000..a4900719c --- /dev/null +++ b/apps/dotcom/src/utils/userPreferences.ts @@ -0,0 +1,59 @@ +import { T, atom } from '@tldraw/tldraw' + +const channel = + typeof BroadcastChannel !== 'undefined' ? new BroadcastChannel('tldrawUserPreferences') : null + +export const userPreferences = { + showFileOpenWarning: createPreference('showFileOpenWarning', T.boolean, true), + showFileClearWarning: createPreference('showFileClearWarning', T.boolean, true), +} + +if (typeof window !== 'undefined') { + ;(window as any).userPreferences = userPreferences +} + +function createPreference(key: string, validator: T.Validator, defaultValue: Type) { + const preferenceAtom = atom( + `userPreferences.${key}`, + loadItemFromStorage(key, validator) ?? defaultValue + ) + + channel?.addEventListener('message', (event) => { + if (event.data.key === key) { + preferenceAtom.set(event.data.value) + } + }) + + return { + get() { + return preferenceAtom.get() + }, + set(newValue: Type) { + preferenceAtom.set(newValue) + saveItemToStorage(key, newValue) + channel?.postMessage({ key, value: newValue }) + }, + } +} + +function loadItemFromStorage(key: string, validator: T.Validator): Type | null { + if (typeof localStorage === 'undefined' || !localStorage) return null + + const item = localStorage.getItem(`tldrawUserPreferences.${key}`) + if (item == null) return null + try { + return validator.validate(JSON.parse(item)) + } catch (e) { + return null + } +} + +function saveItemToStorage(key: string, value: unknown): void { + if (typeof localStorage === 'undefined' || !localStorage) return + + try { + localStorage.setItem(`tldrawUserPreferences.${key}`, JSON.stringify(value)) + } catch (e) { + // not a big deal + } +} diff --git a/apps/dotcom/styles/core.css b/apps/dotcom/styles/core.css new file mode 100644 index 000000000..d1614ccbb --- /dev/null +++ b/apps/dotcom/styles/core.css @@ -0,0 +1,200 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@500;600;800&display=swap'); + +:root { + font-family: Inter, -apple-system, 'system-ui', 'Segoe UI', 'Noto Sans', Helvetica, Arial, + sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji'; + + font-size: 12px; + font-weight: 500; + color: var(--text-color-0); + line-height: 1.6; + overscroll-behavior: none; + touch-action: none; +} + +/* + 1. Use a more-intuitive box-sizing model. +*/ +*, +*::before, +*::after { + box-sizing: border-box; +} +/* + 2. Remove default margin +*/ +* { + margin: 0; +} +/* + 5. Improve media defaults +*/ +img, +picture, +video, +canvas, +svg { + display: block; + max-width: 100%; +} +/* + 6. Remove built-in form typography styles +*/ +input, +button, +textarea, +select { + font: inherit; +} +/* + 7. Avoid text overflows +*/ +p, +h1, +h2, +h3, +h4, +h5, +h6 { + overflow-wrap: break-word; +} + +html { + height: 100%; +} + +html, +body { + overscroll-behavior-x: none; +} + +body { + display: flex; + height: 100%; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-smooth: antialiased; + text-rendering: optimizeLegibility; +} + +div { + box-sizing: border-box; +} + +a { + color: inherit; + text-decoration: none; +} + +.site-wrapper { + display: flex; + flex-direction: column; + flex-grow: 1; + height: 100%; +} + +.icon { + flex-shrink: 0; + width: 20px; + height: 20px; + background-color: currentColor; +} + +.scroll-light { + scrollbar-width: thin; +} +.scroll-light::-webkit-scrollbar { + display: block; + width: 8px; + height: 8px; + position: absolute; + top: 0; + left: 0; + background-color: inherit; +} +.scroll-light::-webkit-scrollbar-button { + display: none; + width: 0; + height: 10px; +} +.scroll-light::-webkit-scrollbar-thumb { + background-clip: padding-box; + width: 0; + min-height: 36px; + border: 2px solid transparent; + border-radius: 6px; + background-color: rgba(0, 0, 0, 0.25); +} +.scroll-light::-webkit-scrollbar-thumb:hover { + background-color: rgba(0, 0, 0, 0.3); +} + +/* ------------------- Error Page ------------------- */ + +.error-page { + display: flex; + inset: 0px; + position: absolute; + align-items: center; + justify-content: center; +} + +.error-page__container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 30px; +} + +.error-page__content { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; +} + +/* text-header mb-sm */ +.error-page__content h1 { + font-size: 20px; + font-weight: 800; + margin-bottom: 0.5rem; +} + +/* text-primary-bold text-grey */ +.error-page__content p { + font-size: 14px; + color: var(--text-color-2); +} + +/* text-primary-bold text-grey */ +.error-page__container a { + font-size: 14px; + font-weight: 500; + color: var(--text-color-2); + padding: 12px 4px; +} + +/* ------------------ Board history ----------------- */ + +.board-history__list { + padding: 8px 8px 8px 24px; + display: flex; + flex-direction: column; + gap: 8px; +} + +.board-history__list a { + padding: 8px 8px 8px 0px; +} + +.board-history__list a:hover { + text-decoration: underline; +} + +.board-history__restore { + position: fixed; + top: 8px; + right: 8px; +} diff --git a/apps/dotcom/styles/globals.css b/apps/dotcom/styles/globals.css new file mode 100644 index 000000000..b7eab9337 --- /dev/null +++ b/apps/dotcom/styles/globals.css @@ -0,0 +1,14 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500&family=Plus+Jakarta+Sans:wght@600;800&display=swap'); +@import url('@tldraw/tldraw/tldraw.css'); +@import url('./z-board.css'); + +.tldraw__editor { + position: fixed; + top: 0px; + left: 0px; + bottom: 0px; + right: 0px; + width: 100%; + height: 100%; + overflow: hidden; +} diff --git a/apps/dotcom/styles/z-board.css b/apps/dotcom/styles/z-board.css new file mode 100644 index 000000000..936057cfd --- /dev/null +++ b/apps/dotcom/styles/z-board.css @@ -0,0 +1,304 @@ +/* ------------------ Da Share Zone ----------------- */ + +.tlui-share-zone { + padding: 0px 0px 0px 0px; + display: flex; + height: 40px; + flex-direction: row; + justify-content: flex-end; + z-index: var(--layer-panels); + align-items: center; +} + +.tlui-share-zone__connection-status { + width: 8px; + height: 100%; + position: relative; + display: flex; + align-items: center; +} + +.tlui-share-zone__connection-status::after { + content: ''; + width: 8px; + height: 8px; + background-color: currentColor; + border-radius: 100%; +} + +.tlui-share-zone__button { + font-family: inherit; + font-size: inherit; + border: 4px solid var(--color-background); + border-radius: 8px; + background-color: var(--color-selected); + color: var(--color-selected-contrast); + text-shadow: none; + pointer-events: all; + position: relative; +} + +.tlui-share-zone__button::before { + position: absolute; + display: block; + content: ''; + inset: -4px; + background-color: var(--color-background); + border-top-left-radius: var(--radius-3); + border-bottom-right-radius: var(--radius-3); + border-bottom-left-radius: var(--radius-3); + z-index: -1; +} + +.tlui-share-zone__button:active { + color: var(--color-selected-contrast); +} + +@media (hover: hover) { + .tlui-share-zone__button:hover { + color: var(--color-selected-contrast); + } + + .tlui-share-zone__button:not(:disabled, :focus-visible):hover { + color: var(--color-selected-contrast); + } +} + +.tlui-share-zone__popover { + font-size: 12px; + font-weight: inherit; + width: 200px; + max-width: 100%; + max-height: 100%; + position: relative; +} + +.tlui-share-zone__qr-code { + width: 200px; + height: 200px; + cursor: pointer; + background: none; + background-color: var(--color-muted-2); + background-size: cover; + border: none; +} + +.tlui-share-zone__spinner { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +.tlui-share-zone__details { + font-size: 11px; + font-weight: 400; + padding: var(--space-4); + color: var(--color-text-1); + line-height: 1.5; + margin: 0px; +} + +.tlui-share-zone__status { + height: 100%; + display: flex; + align-items: center; + justify-content: center; + padding: 4px; + position: relative; + left: -4px; +} + +.tlui-share-zone__status > div { + width: 8px; + height: 8px; + border-radius: 100%; +} + +/* ------------------- People Menu ------------------- */ + +.tlui-people-menu__avatars-button { + display: flex; + align-items: center; + justify-content: flex-end; + background: none; + border: none; + cursor: pointer; + pointer-events: all; + border-radius: var(--radius-1); + padding-right: 1px; + height: 36px; +} + +.tlui-people-menu__avatars { + display: flex; + flex-direction: row; +} + +.tlui-people-menu__avatar { + height: 22px; + width: 22px; + border: 3px solid var(--color-background); + background-color: var(--color-low); + border-radius: 100%; + display: flex; + align-items: center; + justify-content: center; + position: relative; + font-size: 10px; + font-weight: bold; + color: var(--color-selected-contrast); + z-index: 2; +} + +.tlui-people-menu__avatar:nth-of-type(n + 2) { + margin-left: -10px; +} + +.tlui-people-menu__avatars-button[data-state='open'] { + opacity: 1; +} + +@media (hover: hover) { + .tlui-people-menu__avatars-button:hover .tlui-people-menu__avatar { + border-color: var(--color-low); + } +} + +.tlui-people-menu__more { + min-width: 0px; + font-size: 11px; + font-weight: 600; + color: var(--color-text-1); + font-family: inherit; + padding: 0px 4px; + letter-spacing: 1.5; +} +.tlui-people-menu__more::after { + border-radius: var(--radius-2); + inset: 0px; +} + +.tlui-people-menu__wrapper { + position: relative; + display: flex; + flex-direction: column; + width: 220px; + height: fit-content; + max-height: 50vh; +} + +.tlui-people-menu__section { + position: relative; + touch-action: auto; + flex-direction: column; + max-height: 100%; + overflow-x: hidden; + overflow-y: auto; + touch-action: auto; +} + +.tlui-people-menu__section:not(:last-child) { + border-bottom: 1px solid var(--color-divider); +} + +.tlui-people-menu__user { + display: flex; + justify-content: flex-start; + align-items: center; +} + +.tlui-people-menu__user__color-picker { + z-index: var(--layer-overlays); +} + +.tlui-people-menu__user__color { + flex-shrink: 0; +} + +.tlui-people-menu__user__name { + text-align: left; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 12px; + color: var(--color-text-1); + max-width: 100%; + flex-grow: 1; + flex-shrink: 100; +} + +.tlui-people-menu__user__label { + text-align: left; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 12px; + color: var(--color-text-3); + flex-grow: 100; + flex-shrink: 0; + margin-left: 4px; +} + +.tlui-people-menu__user__input { + flex-grow: 2; + height: 100%; + padding: 0px; + margin: 0px; +} + +.tlui-people-menu__user > .tlui-input__wrapper { + width: auto; + display: flex; + align-items: auto; + flex-grow: 2; + gap: 8px; + height: 100%; + padding: 0px; +} + +.tlui-people-menu__item { + display: flex; + justify-content: flex-start; + width: 100%; +} + +.tlui-people-menu__item__button { + padding: 0 11px; +} + +.tlui-people-menu__item > .tlui-button__menu { + width: auto; + display: flex; + align-items: auto; + justify-content: flex-start; + flex-grow: 2; + gap: 11px; +} + +.tlui-people-menu__item__follow { + min-width: 44px; +} + +.tlui-people-menu__item__follow[data-active='true'] .tlui-icon { + opacity: 1; +} + +.tlui-people-menu__item__follow:focus-visible .tlui-icon { + opacity: 1; +} + +@media (hover: hover) { + .tlui-people-menu__item__follow .tlui-icon { + opacity: 0; + } + + .tlui-people-menu__item__follow:hover .tlui-icon { + opacity: 1; + } +} + +.tlui-layout[data-breakpoint='0'] .tlui-offline-indicator { + margin-top: 4px; +} diff --git a/apps/dotcom/tsconfig.json b/apps/dotcom/tsconfig.json new file mode 100644 index 000000000..d8a56ee6a --- /dev/null +++ b/apps/dotcom/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "experimentalDecorators": true, + "downlevelIteration": true, + "plugins": [ + { + "name": "next" + } + ] + }, + "include": ["**/*.ts", "**/*.tsx"], + "exclude": ["node_modules", "_archive"], + "references": [ + { "path": "../../packages/tlsync" }, + { "path": "../../packages/tldraw" }, + { "path": "../../packages/assets" } + ] +} diff --git a/apps/dotcom/vite.config.ts b/apps/dotcom/vite.config.ts new file mode 100644 index 000000000..2d2f01239 --- /dev/null +++ b/apps/dotcom/vite.config.ts @@ -0,0 +1,116 @@ +import react from '@vitejs/plugin-react-swc' +import { config } from 'dotenv' +import { defineConfig } from 'vite' +import { VitePWA, VitePWAOptions } from 'vite-plugin-pwa' + +config({ + path: './.env.local', +}) + +export const getMultiplayerServerURL = () => { + return process.env.MULTIPLAYER_SERVER?.replace(/^ws/, 'http') ?? 'http://127.0.0.1:8787' +} + +const pwaConfig: Partial = { + registerType: 'autoUpdate', + // Make sure the service worker doesn't try to handle API requests + workbox: { + navigateFallbackDenylist: [/^\/api/], + runtimeCaching: [{ handler: 'NetworkFirst', urlPattern: /\/.*/ }], + }, + // Uncomment this to test the PWA install flow locally + // devOptions: { enabled: true }, + manifest: { + name: 'tldraw', + short_name: 'tldraw', + description: 'a very good free whiteboard', + + icons: [ + { + src: '/android-chrome-512x512.png', + sizes: '512x512', + type: 'image/png', + purpose: 'any', + }, + { + src: '/android-chrome-maskable-512x512.png', + sizes: '512x512', + type: 'image/png', + purpose: 'any maskable', + }, + { + src: '/android-chrome-192x192.png', + sizes: '192x192', + type: 'image/png', + purpose: 'any', + }, + { + src: '/android-chrome-maskable-192x192.png', + sizes: '192x192', + type: 'image/png', + purpose: 'any maskable', + }, + ], + theme_color: '#ffffff', + background_color: '#ffffff', + start_url: '/', + display: 'standalone', + orientation: 'any', + }, +} + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react({ tsDecorators: true }), VitePWA(pwaConfig)], + publicDir: './public', + build: { + // output source maps to .map files and include //sourceMappingURL comments in JavaScript files + // these get uploaded to Sentry and can be used for debugging + sourcemap: true, + + // our svg icons break if we use data urls, so disable inline assets for now + assetsInlineLimit: 0, + }, + // add backwards-compatible support for NEXT_PUBLIC_ env vars + define: { + ...Object.fromEntries( + Object.entries(process.env) + .filter(([key]) => key.startsWith('NEXT_PUBLIC_')) + .map(([key, value]) => [`process.env.${key}`, JSON.stringify(value)]) + ), + 'process.env.MULTIPLAYER_SERVER': JSON.stringify(getMultiplayerServerURL()), + 'process.env.ASSET_UPLOAD': JSON.stringify(process.env.ASSET_UPLOAD ?? 'http://127.0.0.1:8788'), + 'process.env.TLDRAW_ENV': JSON.stringify(process.env.TLDRAW_ENV ?? 'development'), + // Fall back to staging DSN for local develeopment, although you still need to + // modify the env check in 'sentry.client.config.ts' to get it reporting errors + 'process.env.SENTRY_DSN': JSON.stringify( + process.env.SENTRY_DSN ?? + 'https://4adc43773d07854d8a60e119505182cc@o578706.ingest.sentry.io/4506178821881856' + ), + }, + server: { + proxy: { + '/api': { + target: getMultiplayerServerURL(), + rewrite: (path) => path.replace(/^\/api/, ''), + ws: false, // we talk to the websocket directly via workers.dev + // Useful for debugging proxy issues + // configure: (proxy, _options) => { + // proxy.on('error', (err, _req, _res) => { + // console.log('[proxy] proxy error', err) + // }) + // proxy.on('proxyReq', (proxyReq, req, _res) => { + // console.log('[proxy] Sending Request to the Target:', req.method, req.url) + // }) + // proxy.on('proxyRes', (proxyRes, req, _res) => { + // console.log( + // '[proxy] Received Response from the Target:', + // proxyRes.statusCode, + // req.url + // ) + // }) + // }, + }, + }, + }, +}) diff --git a/apps/huppy/.gitignore b/apps/huppy/.gitignore new file mode 100644 index 000000000..e81fc5106 --- /dev/null +++ b/apps/huppy/.gitignore @@ -0,0 +1,42 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# PWA build artifacts +/public/*.js + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +# Sentry +.sentryclirc diff --git a/apps/huppy/Dockerfile b/apps/huppy/Dockerfile new file mode 100644 index 000000000..8a35cf16f --- /dev/null +++ b/apps/huppy/Dockerfile @@ -0,0 +1,40 @@ +# Install dependencies only when needed +FROM node:18.12.1-alpine AS builder +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk add --no-cache libc6-compat + +RUN corepack enable +WORKDIR /app +COPY . . + +RUN --mount=type=cache,target=/root/.yarn YARN_CACHE_FOLDER=/root/.yarn \ + yarn install --immutable + +ENV NEXT_TELEMETRY_DISABLED 1 + +WORKDIR /app/apps/huppy +RUN --mount=type=cache,target=/root/.yarn YARN_CACHE_FOLDER=/root/.yarn yarn build + +# Production image, copy all the files and run next +FROM node:18.12.1-alpine AS runner + +RUN apk update && apk upgrade && \ + apk add --no-cache bash git openssh + +WORKDIR /app + +RUN corepack enable + +ENV NODE_ENV production +ENV NEXT_TELEMETRY_DISABLED 1 + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +COPY --from=builder /app ./ + +USER nextjs + +WORKDIR /app/apps/huppy +CMD ["yarn", "start"] + diff --git a/apps/huppy/README.md b/apps/huppy/README.md new file mode 100644 index 000000000..91a71a655 --- /dev/null +++ b/apps/huppy/README.md @@ -0,0 +1,56 @@ +# Repo-tools + +Repo-tools is responsible for the huppy-bot app. + +## Development + +To develop huppy-bot, you'll need to create a .env file that looks like this: + +``` +REPO_SYNC_PRIVATE_KEY_B64= +REPO_SYNC_HOOK_SECRET= +``` + +DM alex to get hold of these credentials. + +To start the server, run `yarn dev-repo-sync`. Once running, you can go to +https://localhost:3000/deliveries to get to a list of github webhook event +deliveries. To test your code, pick an event that does roughly what you want and +hit 'simulate'. You can also ask GitHub to re-deliver events to the production +version of repo-sync through this UI. + +Huppy-bot isn't currently deployed automatically. To deploy, use: + +```sh +fly deploy --config apps/repo-sync/fly.toml --dockerfile apps/repo-sync/Dockerfile +``` + +from the repo root. + +## How it works + +Huppy runs on a server with persistent disk storage attached. It maintains local +mirrors of both our github repos on that disk. When events come in that mean we +need to do some work in a repo, it updates the local mirror, then clones them to +a temporary directory for work. This sort of pull + local clone is _much_ faster +(~1s) than normal from-scratch clones (~1m). + +Huppy's reponsibilities are organized into "flows". These are defined in +`src/flows`. A flow is an object with webhook handlers that implement some +complete set of functionality. Right now there aren't many, but we could add more! + +There's an alternative universe where huppy would exist as a set of github +actions instead. We didn't pursue this route for three reasons: + +1. Huppy needs to operate over multiple github repos at once, which isn't well + supported by actions. +2. Giving actions in our public repo access to our private repo could be a + security risk. We'd have to grant permission to OSS contributors to run + certain actions, which could mean accidentally giving them access to more + than we intend. +3. Having access to the full range of webhook & API options provided by GitHub + means we can create a better DX than would be possible with plain actions + (e.g. the "Fix" button when huppy detects that bublic is out of date). + +It also lets us make use of that local-clone trick, which means huppy responds +to requests in seconds rather than minutes. diff --git a/apps/huppy/fly.toml b/apps/huppy/fly.toml new file mode 100644 index 000000000..38936b4be --- /dev/null +++ b/apps/huppy/fly.toml @@ -0,0 +1,35 @@ +# fly.toml file generated for tldraw-repo-sync on 2023-04-25T14:25:01+01:00 +app = "tldraw-repo-sync" +kill_signal = "SIGINT" +kill_timeout = 5 +mounts = [] +primary_region = "lhr" +processes = [] + +[build] + +[env] +PORT = "8080" + +[mounts] +source = "git_store" +destination = "/tldraw_repo_sync_data" + +[[services]] +internal_port = 8080 +processes = ["app"] +protocol = "tcp" + +[services.concurrency] +hard_limit = 25 +soft_limit = 20 +type = "connections" + +[[services.ports]] +force_https = true +handlers = ["http"] +port = 80 + +[[services.ports]] +handlers = ["tls", "http"] +port = 443 diff --git a/apps/huppy/hacky.Dockerfile b/apps/huppy/hacky.Dockerfile new file mode 100644 index 000000000..62b55026b --- /dev/null +++ b/apps/huppy/hacky.Dockerfile @@ -0,0 +1,12 @@ +# this is extremely hacky and will only work for this one time :) + +# it seems that fly.io CLI somehow builds the image differently from +# pure docker and just hangs, consuming one full core; so instead of +# building and deploying, build separately through docker and then +# just reuse the image +# current workflow: +# docker build --progress plain -f apps/huppy/Dockerfile -t dgroshev/huppy --platform linux/amd64 . +# docker push dgroshev/huppy +# [adjust the image hash below] +# fly deploy --config apps/huppy/fly.toml --dockerfile apps/huppy/hacky.Dockerfile --local-only +FROM dgroshev/huppy@sha256:3dd947e860cb919ba8b5147f51b286ee413057c12bc973d021fbe8313f28401c \ No newline at end of file diff --git a/apps/huppy/next.config.js b/apps/huppy/next.config.js new file mode 100644 index 000000000..d1105fd60 --- /dev/null +++ b/apps/huppy/next.config.js @@ -0,0 +1,25 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, + swcMinify: true, + transpilePackages: [], + productionBrowserSourceMaps: true, + webpack: (config, context) => { + config.module.rules.push({ + test: /\.(svg|json|woff2)$/, + type: 'asset/resource', + }) + return config + }, + redirects: async () => { + return [{ source: '/', destination: 'https://www.tldraw.com/', permanent: false }] + }, + eslint: { + ignoreDuringBuilds: true, + }, + typescript: { + ignoreBuildErrors: true, + }, +} + +module.exports = nextConfig diff --git a/apps/huppy/package.json b/apps/huppy/package.json new file mode 100644 index 000000000..73b403d70 --- /dev/null +++ b/apps/huppy/package.json @@ -0,0 +1,39 @@ +{ + "name": "huppy", + "description": "Tools for managing our public and private repos", + "version": "2.0.0-alpha.10", + "private": true, + "packageManager": "yarn@3.5.0", + "author": { + "name": "tldraw GB Ltd.", + "email": "hello@tldraw.com" + }, + "homepage": "https://tldraw.dev", + "browserslist": [ + "defaults" + ], + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "yarn run -T tsx ../../scripts/lint.ts" + }, + "dependencies": { + "@octokit/core": "^5.0.1", + "@octokit/plugin-retry": "^6.0.1", + "@octokit/webhooks-types": "^6.11.0", + "@tldraw/utils": "workspace:*", + "@tldraw/validate": "workspace:*", + "@types/jsonwebtoken": "^9.0.1", + "json5": "^2.2.3", + "jsonwebtoken": "^9.0.0", + "next": "^13.2.3", + "octokit": "^3.1.1", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "eslint-config-next": "12.2.5", + "lazyrepo": "0.0.0-alpha.27" + } +} diff --git a/apps/huppy/pages/_app.tsx b/apps/huppy/pages/_app.tsx new file mode 100644 index 000000000..227178e9e --- /dev/null +++ b/apps/huppy/pages/_app.tsx @@ -0,0 +1,13 @@ +import type { AppProps } from 'next/app' +import Head from 'next/head' + +export default function MyApp({ Component, pageProps }: AppProps) { + return ( + <> + + + + + + ) +} diff --git a/apps/huppy/pages/api/dev/getDelivery.ts b/apps/huppy/pages/api/dev/getDelivery.ts new file mode 100644 index 000000000..530583a71 --- /dev/null +++ b/apps/huppy/pages/api/dev/getDelivery.ts @@ -0,0 +1,17 @@ +import { assert } from '@tldraw/utils' +import { NextApiRequest, NextApiResponse } from 'next' +import { getAppOctokit } from '../../../src/octokit' + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + assert(process.env.NODE_ENV !== 'production') + assert(req.method === 'GET') + const id = req.query.id + assert(typeof id === 'string') + + const gh = getAppOctokit() + const { data: delivery } = await gh.octokit.rest.apps.getWebhookDelivery({ + delivery_id: Number(id), + }) + + return res.json(delivery) +} diff --git a/apps/huppy/pages/api/dev/redeliver.ts b/apps/huppy/pages/api/dev/redeliver.ts new file mode 100644 index 000000000..9e2a9725e --- /dev/null +++ b/apps/huppy/pages/api/dev/redeliver.ts @@ -0,0 +1,17 @@ +import { assert } from '@tldraw/utils' +import { NextApiRequest, NextApiResponse } from 'next' +import { getAppOctokit } from '../../../src/octokit' + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + assert(process.env.NODE_ENV !== 'production') + assert(req.method === 'POST') + const deliveryId = req.body.id as number + assert(typeof deliveryId === 'number') + + const gh = getAppOctokit() + await gh.octokit.rest.apps.redeliverWebhookDelivery({ + delivery_id: deliveryId, + }) + + return res.json({ ok: true }) +} diff --git a/apps/huppy/pages/api/dev/simulate.ts b/apps/huppy/pages/api/dev/simulate.ts new file mode 100644 index 000000000..575b7f66f --- /dev/null +++ b/apps/huppy/pages/api/dev/simulate.ts @@ -0,0 +1,37 @@ +import { assert, assertExists } from '@tldraw/utils' +import { NextApiRequest, NextApiResponse } from 'next' +import { Ctx } from '../../../src/ctx' +import { NamedEvent, onGithubEvent } from '../../../src/flow' +import { getAppOctokit, getInstallationToken } from '../../../src/octokit' + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + assert(process.env.NODE_ENV !== 'production') + assert(req.method === 'POST') + const deliveryId = req.body.id as number + assert(typeof deliveryId === 'number') + + const app = getAppOctokit() + + const { data: delivery } = await app.octokit.rest.apps.getWebhookDelivery({ + delivery_id: deliveryId, + }) + + const installationId = assertExists(delivery.installation_id) + const ctx: Ctx = { + app, + installationId, + octokit: await app.getInstallationOctokit(installationId), + installationToken: await getInstallationToken(app, installationId), + } + + try { + const messages = await onGithubEvent(ctx, { + name: delivery.event, + payload: delivery.request.payload, + } as NamedEvent) + return res.json({ message: JSON.stringify(messages, null, 2) }) + } catch (err: any) { + console.log(err.stack) + return res.json({ message: err.message }) + } +} diff --git a/apps/huppy/pages/api/github-event.ts b/apps/huppy/pages/api/github-event.ts new file mode 100644 index 000000000..57c81c5ce --- /dev/null +++ b/apps/huppy/pages/api/github-event.ts @@ -0,0 +1,44 @@ +import type { WebhookEventName } from '@octokit/webhooks-types' +import { assert } from '@tldraw/utils' +import { NextApiRequest, NextApiResponse } from 'next' +import { Ctx } from '../../src/ctx' +import { NamedEvent, onGithubEvent } from '../../src/flow' +import { getAppOctokit, getInstallationToken } from '../../src/octokit' +import { wrapRequest } from '../../src/requestWrapper' +import { header } from '../../src/utils' + +const handler = wrapRequest( + '/api/github-event', + async function handler(req: NextApiRequest, res: NextApiResponse) { + const app = getAppOctokit() + const eventName = header(req, 'x-github-event') as WebhookEventName + await app.webhooks.verifyAndReceive({ + id: header(req, 'x-github-delivery'), + name: eventName, + signature: header(req, 'x-hub-signature-256'), + payload: JSON.stringify(req.body), + }) + + const event = { name: eventName, payload: req.body } as NamedEvent + assert( + 'installation' in event.payload && event.payload.installation, + 'event must have installation' + ) + + const installationId = event.payload.installation.id + const ctx: Ctx = { + app, + octokit: await app.getInstallationOctokit(Number(installationId)), + installationId: installationId, + installationToken: await getInstallationToken(app, installationId), + } + + // we deliberately don't await this so that the response is sent + // immediately. we'll process the event in the background. + onGithubEvent(ctx, event) + + return res.json({ ok: true }) + } +) + +export default handler diff --git a/apps/huppy/pages/api/on-release.ts b/apps/huppy/pages/api/on-release.ts new file mode 100644 index 000000000..ee06bda94 --- /dev/null +++ b/apps/huppy/pages/api/on-release.ts @@ -0,0 +1,35 @@ +import { assert } from '@tldraw/utils' +import { T } from '@tldraw/validate' +import { NextApiRequest, NextApiResponse } from 'next' +import { TLDRAW_ORG } from '../../src/config' +import { standaloneExamplesBranch } from '../../src/flows/standaloneExamplesBranch' +import { getCtxForOrg } from '../../src/getCtxForOrg' +import { wrapRequest } from '../../src/requestWrapper' + +const bodySchema = T.object({ + tagToRelease: T.string, + apiKey: T.string, + canary: T.boolean.optional(), +}) + +const handler = wrapRequest( + '/api/on-release', + async function handler(req: NextApiRequest, res: NextApiResponse) { + assert(req.method === 'POST') + const body = bodySchema.validate(req.body) + assert(typeof process.env.DEVELOPER_ACCESS_KEY === 'string') + if (body.apiKey !== process.env.DEVELOPER_ACCESS_KEY) { + res.status(401).send('Bad api key') + return + } + + const { tagToRelease } = body + await standaloneExamplesBranch.onCustomHook(await getCtxForOrg(TLDRAW_ORG), { + tagToRelease, + canary: !!body.canary, + }) + res.send('Created standalone examples branch') + } +) + +export default handler diff --git a/apps/huppy/pages/deliveries.tsx b/apps/huppy/pages/deliveries.tsx new file mode 100644 index 000000000..c23d314fa --- /dev/null +++ b/apps/huppy/pages/deliveries.tsx @@ -0,0 +1,164 @@ +import { assert } from '@tldraw/utils' +import { GetServerSideProps } from 'next' +import { useEffect, useState } from 'react' +import { getAppOctokit } from '../src/octokit' + +type Props = { + deliveries: { + id: number + guid: string + delivered_at: string + redelivery: boolean + duration: number + status: string + status_code: number + event: string + action: string | null + installation_id: number | null + repository_id: number | null + }[] + cursor: string | null +} + +export const getServerSideProps: GetServerSideProps = async (context) => { + assert(process.env.NODE_ENV !== 'production') + + const gh = getAppOctokit() + const deliveries = await gh.octokit.rest.apps.listWebhookDeliveries({ + per_page: 100, + cursor: (context.query.cursor as string) ?? undefined, + }) + + const nextLinkMatch = deliveries.headers.link?.match(/(?<=<)([\S]*)(?=>; rel="Next")/i) + let cursor: string | null = null + if (nextLinkMatch) { + const url = new URL(nextLinkMatch[0]) + cursor = url.searchParams.get('cursor') + } + + return { props: { deliveries: deliveries.data, cursor } } +} + +type SelectedDelivery = { + id: number + data?: unknown +} + +export default function Deliveries({ deliveries, cursor }: Props) { + const [selectedDelivery, setSelectedDelivery] = useState(null) + const [isSimulating, setIsSimulating] = useState(false) + const [isRedelivering, setIsRedelivering] = useState(false) + + useEffect(() => { + if (!selectedDelivery || (selectedDelivery && selectedDelivery.data)) return + + let cancelled = false + ;(async () => { + const response = await fetch(`/api/dev/getDelivery?id=${selectedDelivery.id}`) + const data = await response.json() + if (cancelled) return + setSelectedDelivery({ id: selectedDelivery.id, data }) + })() + + return () => { + cancelled = true + } + }) + + return ( +
+

Deliveries

+
+
    + {deliveries.map((delivery) => ( +
  1. setSelectedDelivery({ id: delivery.id })} + > +
    + {delivery.event} + {delivery.action && `.${delivery.action}`} +
    +
    {formatDate(new Date(delivery.delivered_at))}
    +
  2. + ))} +
  3. + + Load more... + +
  4. +
+ {selectedDelivery && selectedDelivery.data ? ( +
+
+							{JSON.stringify(selectedDelivery.data, null, 2)}
+						
+
+ + +
+
+ ) : selectedDelivery ? ( +
+ loading... +
+ ) : null} +
+
+ ) +} + +function formatDate(date: Date) { + const intl = new Intl.DateTimeFormat('en-GB', { + dateStyle: 'short', + timeStyle: 'short', + }) + + return intl.format(date) +} diff --git a/apps/huppy/src/Queue.ts b/apps/huppy/src/Queue.ts new file mode 100644 index 000000000..749e21f3e --- /dev/null +++ b/apps/huppy/src/Queue.ts @@ -0,0 +1,15 @@ +export class Queue { + currentTask = Promise.resolve() + + enqueue(task: () => Promise): Promise { + return new Promise((resolve, reject) => { + this.currentTask = this.currentTask.then(async () => { + try { + resolve(await task()) + } catch (err) { + reject(err) + } + }) + }) + } +} diff --git a/apps/huppy/src/comment.tsx b/apps/huppy/src/comment.tsx new file mode 100644 index 000000000..5ce40a402 --- /dev/null +++ b/apps/huppy/src/comment.tsx @@ -0,0 +1,48 @@ +import { APP_USER_NAME, TLDRAW_ORG, TLDRAW_PUBLIC_REPO } from './config' +import { Ctx } from './ctx' + +export async function findHuppyCommentIfExists(ctx: Ctx, prNumber: number) { + const { data: comments } = await ctx.octokit.rest.issues.listComments({ + owner: TLDRAW_ORG, + repo: TLDRAW_PUBLIC_REPO, + issue_number: prNumber, + per_page: 100, + sort: 'created', + direction: 'asc', + }) + + const foundComment = comments.find((comment) => comment.user?.login === APP_USER_NAME) + + return foundComment ?? null +} + +export async function updateHuppyCommentIfExists(ctx: Ctx, prNumber: number, body: string) { + const foundComment = await findHuppyCommentIfExists(ctx, prNumber) + if (foundComment) { + await ctx.octokit.rest.issues.updateComment({ + owner: TLDRAW_ORG, + repo: TLDRAW_PUBLIC_REPO, + comment_id: foundComment.id, + body, + }) + } +} + +export async function createOrUpdateHuppyComment(ctx: Ctx, prNumber: number, body: string) { + const foundComment = await findHuppyCommentIfExists(ctx, prNumber) + if (foundComment) { + await ctx.octokit.rest.issues.updateComment({ + owner: TLDRAW_ORG, + repo: TLDRAW_PUBLIC_REPO, + comment_id: foundComment.id, + body, + }) + } else { + await ctx.octokit.rest.issues.createComment({ + owner: TLDRAW_ORG, + repo: TLDRAW_PUBLIC_REPO, + issue_number: prNumber, + body, + }) + } +} diff --git a/apps/huppy/src/config.tsx b/apps/huppy/src/config.tsx new file mode 100644 index 000000000..6a5a75b97 --- /dev/null +++ b/apps/huppy/src/config.tsx @@ -0,0 +1,7 @@ +export const APP_USER_EMAIL = '128400622+huppy-bot[bot]@users.noreply.github.com' +export const APP_USER_NAME = 'huppy-bot[bot]' +export const APP_ID = '307634' + +export const TLDRAW_ORG = 'tldraw' +export const TLDRAW_PUBLIC_REPO = 'tldraw' +export const TLDRAW_PUBLIC_REPO_MAIN_BRANCH = 'main' diff --git a/apps/huppy/src/ctx.tsx b/apps/huppy/src/ctx.tsx new file mode 100644 index 000000000..6ef387b7a --- /dev/null +++ b/apps/huppy/src/ctx.tsx @@ -0,0 +1,8 @@ +import { App, Octokit } from 'octokit' + +export type Ctx = { + app: App + octokit: Octokit + installationToken: string + installationId: number +} diff --git a/apps/huppy/src/flow.tsx b/apps/huppy/src/flow.tsx new file mode 100644 index 000000000..8dbd499f0 --- /dev/null +++ b/apps/huppy/src/flow.tsx @@ -0,0 +1,54 @@ +import { WebhookEventMap } from '@octokit/webhooks-types' +import { Ctx } from './ctx' +import { allFlows } from './flows' +import { reportError } from './reportError' +import { camelCase, capitalize, elapsed } from './utils' + +type CamelCase = S extends `${infer P1}_${infer P2}${infer P3}` + ? `${Lowercase}${Uppercase}${CamelCase}` + : Lowercase + +export type NamedEvent = { + [Name in keyof WebhookEventMap]: { name: Name; payload: WebhookEventMap[Name] } +}[keyof WebhookEventMap] + +export type Flow = { + name: string + onCustomHook?: (ctx: Ctx, payload: CustomHookPayload) => Promise +} & { + [Name in keyof WebhookEventMap as `on${Capitalize>}`]?: ( + ctx: Ctx, + payload: WebhookEventMap[Name] + ) => Promise +} + +export async function onGithubEvent(ctx: Ctx, event: NamedEvent) { + let nameString = event.name + if ('action' in event.payload) { + nameString += `.${event.payload.action}` + } + console.log('Starting event:', nameString) + const handlerName = `on${capitalize(camelCase(event.name))}` as `on${Capitalize< + CamelCase + >}` + + const results: Record = {} + + for (const flow of allFlows) { + if (handlerName in flow) { + const actionName = `${flow.name}.${handlerName}` + const start = Date.now() + try { + console.log(`===== Starting ${actionName} =====`) + await (flow as any)[handlerName](ctx, event.payload) + console.log(`===== Finished ${actionName} in ${elapsed(start)} =====`) + results[actionName] = `ok in ${elapsed(start)}` + } catch (err: any) { + results[actionName] = err.message + await reportError(`Error in ${flow.name}.${handlerName}`, err) + } + } + } + + return results +} diff --git a/apps/huppy/src/flows/collectClaSignatures.tsx b/apps/huppy/src/flows/collectClaSignatures.tsx new file mode 100644 index 000000000..01a95daa1 --- /dev/null +++ b/apps/huppy/src/flows/collectClaSignatures.tsx @@ -0,0 +1,234 @@ +import { IssueCommentEvent } from '@octokit/webhooks-types' +import { assert } from '@tldraw/utils' +import { createOrUpdateHuppyComment, updateHuppyCommentIfExists } from '../comment' +import { TLDRAW_ORG, TLDRAW_PUBLIC_REPO } from '../config' +import { Ctx } from '../ctx' +import { Flow } from '../flow' + +const TARGET_REPO = TLDRAW_PUBLIC_REPO +const CLA_URL = + 'https://tldraw.notion.site/Contributor-License-Agreement-4d529dd5e4b3438b90cdf2a2f9d7e7e6?pvs=4' +const SIGNING_MESSAGE = 'I have read and agree to the Contributor License Agreement.' +const RE_CHECK_MESSAGE = '/huppy check cla' +const CLA_SIGNATURES_BRANCH = 'cla-signees' + +const pullRequestActionsToCheck = ['opened', 'synchronize', 'reopened', 'edited'] + +type Signing = { + githubId: number + signedAt: string + signedVersion: 1 + signingComment: string +} +type SigneeInfo = { + unsigned: Set + signees: Map + total: number +} + +export const collectClaSignatures: Flow = { + name: 'collectClaSignatures', + + onPullRequest: async (ctx, event) => { + if (event.repository.full_name !== `${TLDRAW_ORG}/${TARGET_REPO}`) return + if (!pullRequestActionsToCheck.includes(event.action)) return + + await checkAllContributorsHaveSignedCla(ctx, event.pull_request) + }, + + onIssueComment: async (ctx, event) => { + if (event.repository.full_name !== `${TLDRAW_ORG}/${TARGET_REPO}`) return + if (event.issue.pull_request === undefined) return + + switch (event.comment.body.trim().toLowerCase()) { + case SIGNING_MESSAGE.toLowerCase(): + await addSignatureFromComment(ctx, event) + break + case RE_CHECK_MESSAGE.toLowerCase(): { + const pr = await ctx.octokit.rest.pulls.get({ + owner: TLDRAW_ORG, + repo: TARGET_REPO, + pull_number: event.issue.number, + }) + await checkAllContributorsHaveSignedCla(ctx, pr.data) + await ctx.octokit.rest.reactions.createForIssueComment({ + owner: TLDRAW_ORG, + repo: TARGET_REPO, + comment_id: event.comment.id, + content: '+1', + }) + break + } + } + }, +} + +async function addSignatureFromComment(ctx: Ctx, event: IssueCommentEvent) { + const existingSignature = await getClaSigneeInfo(ctx, event.comment.user.login.toLowerCase()) + if (existingSignature) { + await ctx.octokit.rest.reactions.createForIssueComment({ + owner: TLDRAW_ORG, + repo: TARGET_REPO, + comment_id: event.comment.id, + content: 'heart', + }) + return + } + + const newSigning: Signing = { + githubId: event.comment.user.id, + signedAt: event.comment.created_at, + signedVersion: 1, + signingComment: event.comment.html_url, + } + + await ctx.octokit.rest.repos.createOrUpdateFileContents({ + owner: TLDRAW_ORG, + repo: TLDRAW_PUBLIC_REPO, + path: `${event.comment.user.login.toLowerCase()}.json`, + branch: CLA_SIGNATURES_BRANCH, + content: Buffer.from(JSON.stringify(newSigning, null, '\t')).toString('base64'), + message: `Add CLA signature for ${event.comment.user.login}`, + }) + + const pr = await ctx.octokit.rest.pulls.get({ + owner: TLDRAW_ORG, + repo: TARGET_REPO, + pull_number: event.issue.number, + }) + await checkAllContributorsHaveSignedCla(ctx, pr.data) + + await ctx.octokit.rest.reactions.createForIssueComment({ + owner: TLDRAW_ORG, + repo: TARGET_REPO, + comment_id: event.comment.id, + content: 'heart', + }) +} + +async function checkAllContributorsHaveSignedCla( + ctx: Ctx, + pr: { head: { sha: string }; number: number } +) { + const info = await getClaSigneesFromPr(ctx, pr) + + if (info.unsigned.size === 0) { + await ctx.octokit.rest.repos.createCommitStatus({ + owner: TLDRAW_ORG, + repo: TARGET_REPO, + sha: pr.head.sha, + state: 'success', + context: 'CLA Signatures', + description: getStatusDescription(info), + }) + await updateHuppyCommentIfExists(ctx, pr.number, getHuppyCommentContents(info)) + return + } + + await ctx.octokit.rest.repos.createCommitStatus({ + owner: TLDRAW_ORG, + repo: TARGET_REPO, + sha: pr.head.sha, + state: 'failure', + context: 'CLA Signatures', + description: getStatusDescription(info), + }) + + await createOrUpdateHuppyComment(ctx, pr.number, getHuppyCommentContents(info)) +} + +async function getClaSigneesFromPr(ctx: Ctx, pr: { number: number }): Promise { + const allAuthors = new Set() + + const commits = await ctx.octokit.paginate( + 'GET /repos/{owner}/{repo}/pulls/{pull_number}/commits', + { + owner: TLDRAW_ORG, + repo: TARGET_REPO, + pull_number: pr.number, + } + ) + + for (const commit of commits) { + if (commit.author && !commit.author.login.endsWith('[bot]')) { + allAuthors.add(commit.author.login.toLowerCase()) + } + } + + const signees = new Map() + const unsigned = new Set() + for (const author of [...allAuthors].sort()) { + const signeeInfo = await getClaSigneeInfo(ctx, author) + if (signeeInfo) { + signees.set(author, signeeInfo) + } else { + unsigned.add(author) + } + } + + return { signees, unsigned, total: allAuthors.size } +} + +async function getClaSigneeInfo(ctx: Ctx, authorName: string) { + try { + const response = await ctx.octokit.rest.repos.getContent({ + owner: TLDRAW_ORG, + repo: TLDRAW_PUBLIC_REPO, + path: `${authorName}.json`, + ref: 'cla-signees', + }) + assert(!Array.isArray(response.data), 'Expected a file, not a directory') + assert(response.data.type === 'file', 'Expected a file, not a directory') + return { + signing: JSON.parse( + Buffer.from(response.data.content, 'base64').toString('utf-8') + ) as Signing, + fileSha: response.data.sha, + } + } catch (err: any) { + if (err.status === 404) { + return null + } + throw err + } +} + +function getHuppyCommentContents(info: SigneeInfo) { + if (info.signees.size > 1) { + let listing = `**${info.signees.size}** out of **${info.total}** ${ + info.total === 1 ? 'authors has' : 'authors have' + } signed the [CLA](${CLA_URL}).\n\n` + + for (const author of info.unsigned) { + listing += `- [ ] @${author}\n` + } + for (const author of info.signees.keys()) { + listing += `- [x] @${author}\n` + } + + if (info.unsigned.size === 0) { + return `${listing}\n\nThanks!` + } + + return `Hey, thanks for your pull request! Before we can merge your PR, each author will need to sign our [Contributor License Agreement](${CLA_URL}) by posting a comment that reads: + +> ${SIGNING_MESSAGE} +--- + +${listing}` + } else { + const author = [...info.signees.keys()][0] + + if (info.unsigned.size === 0) { + return `**${author}** has signed the [Contributor License Agreement](${CLA_URL}). Thanks!` + } + + return `Hey, thanks for your pull request! Before we can merge your PR, you will need to sign our [Contributor License Agreement](${CLA_URL}) by posting a comment that reads: + +> ${SIGNING_MESSAGE}` + } +} + +function getStatusDescription(info: SigneeInfo) { + return `${info.signees.size}/${info.total} signed. Comment '${RE_CHECK_MESSAGE}' to re-check.` +} diff --git a/apps/huppy/src/flows/enforcePrLabels.tsx b/apps/huppy/src/flows/enforcePrLabels.tsx new file mode 100644 index 000000000..dd760902f --- /dev/null +++ b/apps/huppy/src/flows/enforcePrLabels.tsx @@ -0,0 +1,119 @@ +import { TLDRAW_ORG, TLDRAW_PUBLIC_REPO, TLDRAW_PUBLIC_REPO_MAIN_BRANCH } from '../config' +import { Flow } from '../flow' + +export const enforcePrLabels: Flow = { + name: 'enforcePrLabels', + async onPullRequest(ctx, event) { + if (event.repository.full_name !== `${TLDRAW_ORG}/${TLDRAW_PUBLIC_REPO}`) return + if (event.pull_request.base.ref !== TLDRAW_PUBLIC_REPO_MAIN_BRANCH) return + + const fail = async (message: string) => { + await ctx.octokit.rest.repos.createCommitStatus({ + owner: event.repository.owner.login, + repo: event.repository.name, + sha: event.pull_request.head.sha, + state: 'failure', + description: message, + context: 'Release Label', + }) + } + + const succeed = async (message: string) => { + await ctx.octokit.rest.repos.createCommitStatus({ + owner: event.repository.owner.login, + repo: event.repository.name, + sha: event.pull_request.head.sha, + state: 'success', + description: message, + context: 'Release Label', + }) + } + + const pull = event.pull_request + + if (pull.draft) { + return await succeed('Draft PR, skipping label check') + } + + if (pull.closed_at || pull.merged_at) { + return await succeed('Closed PR, skipping label check') + } + + const currentReleaseLabels = pull.labels + .map((label) => label.name) + .filter((label) => VALID_LABELS.includes(label)) + + if (currentReleaseLabels.length > 1 && !allHaveSameBumpType(currentReleaseLabels)) { + return fail(`PR has multiple release labels: ${currentReleaseLabels.join(', ')}`) + } + + const prBody = pull.body + + const selectedReleaseLabels = VALID_LABELS.filter((label) => + prBody?.match(new RegExp(`^\\s*?-\\s*\\[\\s*x\\s*\\]\\s+\`${label}\``, 'm')) + ) as (keyof typeof LABEL_TYPES)[] + + if (selectedReleaseLabels.length > 1 && !allHaveSameBumpType(selectedReleaseLabels)) { + return await fail( + `PR has multiple checked labels: ${selectedReleaseLabels.join( + ', ' + )}. Please select only one` + ) + } + + const [current] = currentReleaseLabels + const [selected] = selectedReleaseLabels + + if (!current && !selected) { + return await fail( + `Please assign one of the following release labels to this PR: ${VALID_LABELS.join(', ')}` + ) + } + + if (current === selected || (current && !selected)) { + return succeed(`PR has label: ${current}`) + } + + // otherwise the label has changed or is being set for the first time + // from the pr body + if (current) { + await ctx.octokit.rest.issues.removeLabel({ + issue_number: event.number, + name: current, + owner: 'ds300', + repo: 'lazyrepo', + }) + } + + console.log('adding labels') + await ctx.octokit.rest.issues.addLabels({ + issue_number: pull.number, + owner: event.repository.organization ?? event.repository.owner.login, + repo: event.repository.name, + labels: [selected], + } as any) + + return await succeed(`PR label set to: ${selected}`) + }, +} + +const LABEL_TYPES = { + tests: 'none', + internal: 'none', + documentation: 'none', + dependencies: 'patch', + major: 'major', + minor: 'minor', + patch: 'patch', +} + +const VALID_LABELS = Object.keys(LABEL_TYPES) + +function allHaveSameBumpType(labels: string[]) { + const [first] = labels + return labels.every( + (label) => + LABEL_TYPES[label as keyof typeof LABEL_TYPES] === + LABEL_TYPES[first as keyof typeof LABEL_TYPES] + ) +} diff --git a/apps/huppy/src/flows/index.tsx b/apps/huppy/src/flows/index.tsx new file mode 100644 index 000000000..15ec9e47f --- /dev/null +++ b/apps/huppy/src/flows/index.tsx @@ -0,0 +1,5 @@ +import { collectClaSignatures } from './collectClaSignatures' +import { enforcePrLabels } from './enforcePrLabels' +import { standaloneExamplesBranch } from './standaloneExamplesBranch' + +export const allFlows = [enforcePrLabels, standaloneExamplesBranch, collectClaSignatures] diff --git a/apps/huppy/src/flows/standaloneExamplesBranch.tsx b/apps/huppy/src/flows/standaloneExamplesBranch.tsx new file mode 100644 index 000000000..f9b5b6a43 --- /dev/null +++ b/apps/huppy/src/flows/standaloneExamplesBranch.tsx @@ -0,0 +1,107 @@ +import { assert } from '@tldraw/utils' +import * as fs from 'fs/promises' +import * as path from 'path' +import { Flow } from '../flow' +import { withWorkingRepo } from '../repo' +import { readJsonIfExists } from '../utils' + +const filesToCopyFromRoot = ['.gitignore', '.prettierrc', 'LICENSE'] +const packageDepsToSyncFromRoot = ['typescript', '@types/react', '@types/react-dom'] + +export const standaloneExamplesBranch = { + name: 'standaloneExamplesBranch', + + onCustomHook: async (ctx, event) => { + await withWorkingRepo( + 'public', + ctx.installationToken, + event.tagToRelease, + async ({ git, repoPath }) => { + const standaloneExamplesWorkDir = path.join(repoPath, '.git', 'standalone-examples') + + const currentCommitHash = await git.trimmed('rev-parse', 'HEAD') + const branchName = `standalone-examples-${currentCommitHash}` + await git('checkout', '-b', branchName) + + // copy examples into new folder + console.log('Copying examples into new folder...') + for (const file of await git.lines('ls-files', 'apps/examples')) { + const relativePath = path.relative('apps/examples', file) + await fs.mkdir(path.join(standaloneExamplesWorkDir, path.dirname(relativePath)), { + recursive: true, + }) + await fs.copyFile( + path.join(repoPath, file), + path.join(standaloneExamplesWorkDir, relativePath) + ) + } + + for (const file of filesToCopyFromRoot) { + await fs.copyFile(path.join(repoPath, file), path.join(standaloneExamplesWorkDir, file)) + } + + console.log('Creation tsconfig.json...') + const tsconfig = await readJsonIfExists(path.join(repoPath, 'config/tsconfig.base.json')) + tsconfig.includes = ['src'] + await fs.writeFile( + path.join(standaloneExamplesWorkDir, 'tsconfig.json'), + JSON.stringify(tsconfig, null, '\t') + ) + + console.log('Creation package.json...') + const rootPackageJson = await readJsonIfExists(path.join(repoPath, 'package.json')) + const examplesPackageJson = await readJsonIfExists( + path.join(standaloneExamplesWorkDir, 'package.json') + ) + + for (const dep of packageDepsToSyncFromRoot) { + examplesPackageJson.dependencies[dep] = rootPackageJson.dependencies[dep] + } + + for (const name of Object.keys( + examplesPackageJson.dependencies as Record + )) { + if (!name.startsWith('@tldraw/')) continue + const packageJsonFile = await readJsonIfExists( + path.join(repoPath, 'packages', name.replace('@tldraw/', ''), 'package.json') + ) + assert(packageJsonFile, `package.json for ${name} must exist`) + if (event.canary) { + const baseVersion = packageJsonFile.version.replace(/-.*$/, '') + const canaryTag = `canary.${currentCommitHash.slice(0, 12)}` + examplesPackageJson.dependencies[name] = `${baseVersion}-${canaryTag}` + } else { + examplesPackageJson.dependencies[name] = packageJsonFile.version + } + } + + await fs.writeFile( + path.join(standaloneExamplesWorkDir, 'package.json'), + JSON.stringify(examplesPackageJson, null, '\t') + ) + + console.log('Deleting existing repo contents...') + for (const file of await fs.readdir(repoPath)) { + if (file === '.git') continue + await fs.rm(path.join(repoPath, file), { recursive: true, force: true }) + } + + console.log('Moving new repo contents into place...') + for (const file of await fs.readdir(standaloneExamplesWorkDir)) { + await fs.rename(path.join(standaloneExamplesWorkDir, file), path.join(repoPath, file)) + } + + await fs.rm(standaloneExamplesWorkDir, { recursive: true, force: true }) + + console.log('Committing & pushing changes...') + await git('add', '-A') + await git( + 'commit', + '-m', + `[automated] Update standalone examples from ${event.tagToRelease}` + ) + await git('push', '--force', 'origin', `${branchName}:examples`) + } + ) + }, +} satisfies Flow<{ tagToRelease: string; canary: boolean }> diff --git a/apps/huppy/src/getCtxForOrg.tsx b/apps/huppy/src/getCtxForOrg.tsx new file mode 100644 index 000000000..24ab4c2e7 --- /dev/null +++ b/apps/huppy/src/getCtxForOrg.tsx @@ -0,0 +1,21 @@ +import { Ctx } from './ctx' +import { getAppOctokit, getInstallationToken } from './octokit' + +export async function getCtxForOrg(orgName: string): Promise { + const app = getAppOctokit() + + for await (const { installation } of app.eachInstallation.iterator()) { + if (!installation.account) continue + if (!('login' in installation.account)) continue + if (installation.account.login !== orgName) continue + + return { + app, + installationId: installation.id, + octokit: await app.getInstallationOctokit(installation.id), + installationToken: await getInstallationToken(app, installation.id), + } + } + + throw new Error(`No installation found for org ${orgName}`) +} diff --git a/apps/huppy/src/octokit.ts b/apps/huppy/src/octokit.ts new file mode 100644 index 000000000..619ecaaed --- /dev/null +++ b/apps/huppy/src/octokit.ts @@ -0,0 +1,67 @@ +import { Octokit as OctokitCore } from '@octokit/core' +import { retry } from '@octokit/plugin-retry' +import { assert } from '@tldraw/utils' +import console from 'console' +import { App, Octokit } from 'octokit' +import { APP_ID } from './config' + +export function getGitHubAuth() { + const REPO_SYNC_PRIVATE_KEY_B64 = process.env.REPO_SYNC_PRIVATE_KEY_B64 + assert( + typeof REPO_SYNC_PRIVATE_KEY_B64 === 'string', + 'REPO_SYNC_PRIVATE_KEY_B64 must be a string' + ) + const REPO_SYNC_PRIVATE_KEY = Buffer.from(REPO_SYNC_PRIVATE_KEY_B64, 'base64').toString('utf-8') + + const REPO_SYNC_HOOK_SECRET = process.env.REPO_SYNC_HOOK_SECRET + assert(typeof REPO_SYNC_HOOK_SECRET === 'string', 'REPO_SYNC_HOOK_SECRET must be a string') + + return { + privateKey: REPO_SYNC_PRIVATE_KEY, + webhookSecret: REPO_SYNC_HOOK_SECRET, + } +} + +export async function getInstallationToken(gh: App, installationId: number) { + const { data } = await gh.octokit.rest.apps.createInstallationAccessToken({ + installation_id: installationId, + } as any) + return data.token +} + +function requestLogPlugin(octokit: OctokitCore) { + octokit.hook.wrap('request', async (request, options) => { + const url = options.url.replace(/{([^}]+)}/g, (_, key) => (options as any)[key]) + let info = `${options.method} ${url}` + if (options.request.retryCount) { + info += ` (retry ${options.request.retryCount})` + } + + try { + const result = await request(options) + console.log(`[gh] ${result.status} ${info}`) + return result + } catch (err: any) { + console.log(`[gh] ${err.status} ${info}`) + throw err + } + }) +} + +const OctokitWithRetry = Octokit.plugin(requestLogPlugin, retry).defaults({ + retry: { + // retry on 404s, which can occur if we make a request for a resource before it's ready + doNotRetry: [400, 401, 403, 422, 451], + }, +}) + +export function getAppOctokit() { + const { privateKey, webhookSecret } = getGitHubAuth() + return new App({ + Octokit: OctokitWithRetry, + appId: APP_ID, + privateKey, + webhooks: { secret: webhookSecret }, + log: console, + }) +} diff --git a/apps/huppy/src/repo.ts b/apps/huppy/src/repo.ts new file mode 100644 index 000000000..e45ee6055 --- /dev/null +++ b/apps/huppy/src/repo.ts @@ -0,0 +1,143 @@ +import * as fs from 'fs/promises' +import * as os from 'os' +import * as path from 'path' +import { exec } from '../../../scripts/lib/exec' +import { Queue } from './Queue' +import { APP_USER_EMAIL, APP_USER_NAME, TLDRAW_ORG, TLDRAW_PUBLIC_REPO } from './config' + +const globalGitQueue = new Queue() + +const repos = { + public: { + org: TLDRAW_ORG, + name: TLDRAW_PUBLIC_REPO, + path: 'tldraw-public', + queue: new Queue(), + }, +} as const + +export function prefixOutput(prefix: string) { + return { + processStdoutLine: (line: string) => process.stdout.write(`${prefix}${line}\n`), + processStderrLine: (line: string) => process.stderr.write(`${prefix}${line}\n`), + } +} + +export function createGit(pwd: string) { + const git = async (command: string, ...args: (string | null)[]) => + exec('git', [command, ...args], { pwd, ...prefixOutput(`[git ${command}] `) }) + + git.trimmed = async (command: string, ...args: (string | null)[]) => + (await git(command, ...args)).trim() + + git.lines = async (command: string, ...args: (string | null)[]) => + (await git(command, ...args)).trim().split('\n') + + git.cd = (dir: string) => createGit(path.join(pwd, dir)) + + return git +} + +export type Git = ReturnType + +export async function getPersistentDataPath() { + try { + await fs.writeFile('/tldraw_repo_sync_data/check', 'ok') + return '/tldraw_repo_sync_data' + } catch { + const tempPersistent = path.join(os.tmpdir(), 'tldraw_repo_sync_data') + await fs.mkdir(tempPersistent, { recursive: true }) + return tempPersistent + } +} + +async function initBaseRepo(repoKey: keyof typeof repos, installationToken: string) { + const repo = repos[repoKey] + + const persistentDataPath = await getPersistentDataPath() + const repoPath = path.join(persistentDataPath, repo.path) + + try { + await fs.rm(repoPath, { recursive: true, force: true }) + } catch { + // dw + } + + const repoUrl = `https://x-access-token:${installationToken}@github.com/${repo.org}/${repo.name}.git` + await globalGitQueue.enqueue(() => exec('git', ['clone', '--mirror', repoUrl, repoPath])) +} + +export async function getBaseRepo(repoKey: keyof typeof repos, installationToken: string) { + const repo = repos[repoKey] + + return await repo.queue.enqueue(async () => { + const persistentDataPath = await getPersistentDataPath() + const repoPath = path.join(persistentDataPath, repo.path) + const git = createGit(repoPath) + + try { + await fs.readFile(path.join(repoPath, 'HEAD')) + } catch { + await initBaseRepo(repoKey, installationToken) + return { repo, path: repoPath } + } + + const remote = await git.trimmed('remote', 'get-url', 'origin') + if (!remote.endsWith(`@github.com/${repo.org}/${repo.name}.git`)) { + await initBaseRepo(repoKey, installationToken) + return { repo, path: repoPath } + } + + // update remote with a fresh JWT: + await git( + 'remote', + 'set-url', + 'origin', + `https://x-access-token:${installationToken}@github.com/${repo.org}/${repo.name}.git` + ) + + // make sure we're up to date with origin: + await git('remote', 'update') + + return { repo, path: repoPath } + }) +} + +export async function withWorkingRepo( + repoKey: keyof typeof repos, + installationToken: string, + ref: string, + fn: (opts: { repoPath: string; git: Git }) => Promise +) { + const { repo, path: repoPath } = await getBaseRepo(repoKey, installationToken) + const workingDir = path.join( + os.tmpdir(), + `tldraw_repo_sync_${Math.random().toString(36).slice(2)}` + ) + + await globalGitQueue.enqueue(() => exec('git', ['clone', '--no-checkout', repoPath, workingDir])) + const git = createGit(workingDir) + + try { + // update remote with a fresh JWT: + await git( + 'remote', + 'set-url', + 'origin', + `https://x-access-token:${installationToken}@github.com/${repo.org}/${repo.name}.git` + ) + + await git('checkout', ref) + await setLocalAuthorInfo(workingDir) + + return await fn({ repoPath: workingDir, git }) + } finally { + await fs.rm(workingDir, { recursive: true }) + } +} + +export async function setLocalAuthorInfo(pwd: string) { + const git = createGit(pwd) + await git('config', '--local', 'user.name', APP_USER_NAME) + await git('config', '--local', 'user.email', APP_USER_EMAIL) +} diff --git a/apps/huppy/src/reportError.tsx b/apps/huppy/src/reportError.tsx new file mode 100644 index 000000000..2b5eeed03 --- /dev/null +++ b/apps/huppy/src/reportError.tsx @@ -0,0 +1,22 @@ +import os from 'os' + +const discordWebhookUrl = process.env.HUPPY_WEBHOOK_URL + +export async function reportError(context: string, error: Error) { + if (typeof discordWebhookUrl === 'undefined') { + throw new Error('HUPPY_WEBHOOK_URL not set') + } + + const body = JSON.stringify({ + content: `[${os.hostname}] ${context}:\n\`\`\`\n${error.stack}\n\`\`\``, + }) + console.log(context, error.stack) + if (process.env.NODE_ENV !== 'production') return + await fetch(discordWebhookUrl, { + method: 'POST', + body, + headers: { + 'Content-Type': 'application/json', + }, + }) +} diff --git a/apps/huppy/src/requestWrapper.tsx b/apps/huppy/src/requestWrapper.tsx new file mode 100644 index 000000000..6b22b66b2 --- /dev/null +++ b/apps/huppy/src/requestWrapper.tsx @@ -0,0 +1,16 @@ +import { NextApiRequest, NextApiResponse } from 'next' +import { reportError } from './reportError' + +export function wrapRequest( + name: string, + handler: (req: NextApiRequest, res: NextApiResponse) => Promise +) { + return async (req: NextApiRequest, res: NextApiResponse) => { + try { + await handler(req, res as NextApiResponse) + } catch (err: any) { + reportError(`Error in ${name}`, err) + res.status(500).json({ error: err.message }) + } + } +} diff --git a/apps/huppy/src/utils.ts b/apps/huppy/src/utils.ts new file mode 100644 index 000000000..3d086559a --- /dev/null +++ b/apps/huppy/src/utils.ts @@ -0,0 +1,50 @@ +import * as fs from 'fs/promises' +import json5 from 'json5' +import { NextApiRequest } from 'next' + +export function header(req: NextApiRequest, name: keyof NextApiRequest['headers']): string { + const value = req.headers[name] + if (!value) { + throw new Error(`Missing header: ${name}`) + } + if (Array.isArray(value)) { + throw new Error(`Header ${name} has multiple values`) + } + return value +} + +export function firstLine(str: string) { + return str.split('\n')[0] +} + +export function sleepMs(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)) +} + +export function camelCase(name: string) { + return name.replace(/[_-]([a-z0-9])/gi, (g) => g[1].toUpperCase()) +} + +export function capitalize(name: string) { + return name[0].toUpperCase() + name.slice(1) +} + +export function elapsed(start: number) { + return `${((Date.now() - start) / 1000).toFixed(2)}s` +} + +export async function readFileIfExists(file: string) { + try { + return await fs.readFile(file, 'utf8') + } catch { + return null + } +} + +export async function readJsonIfExists(file: string) { + const fileContents = await readFileIfExists(file) + if (fileContents === null) { + return null + } + return json5.parse(fileContents) +} diff --git a/apps/huppy/tsconfig.json b/apps/huppy/tsconfig.json new file mode 100644 index 000000000..24c60cfee --- /dev/null +++ b/apps/huppy/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "experimentalDecorators": true, + "downlevelIteration": true + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules", "_archive"], + "references": [{ "path": "../../packages/utils" }, { "path": "../../packages/validate" }] +} diff --git a/config/eslint-preset-react.js b/config/eslint-preset-react.js index b6a76291d..fd5257e8e 100644 --- a/config/eslint-preset-react.js +++ b/config/eslint-preset-react.js @@ -4,7 +4,7 @@ module.exports = { extends: ['prettier'], settings: { next: { - rootDir: ['apps/*/', 'packages/*/', 'bublic/apps/*/', 'bublic/packages/*/'], + rootDir: ['apps/*/', 'packages/*/'], }, }, rules: { diff --git a/config/eslint-preset.js b/config/eslint-preset.js index bd0cc958e..1fad2832a 100644 --- a/config/eslint-preset.js +++ b/config/eslint-preset.js @@ -4,7 +4,7 @@ module.exports = { extends: ['prettier'], settings: { next: { - rootDir: ['apps/*/', 'packages/*/', 'bublic/apps/*/', 'bublic/packages/*/'], + rootDir: ['apps/*/', 'packages/*/'], }, }, ignorePatterns: ['**/*.js'], diff --git a/lazy.config.ts b/lazy.config.ts index 82f638009..b39923632 100644 --- a/lazy.config.ts +++ b/lazy.config.ts @@ -1,13 +1,28 @@ import { LazyConfig } from 'lazyrepo' -export function generateSharedScripts(bublic: '' | '/bublic') { - return { +const config = { + baseCacheConfig: { + include: [ + '/package.json', + '/yarn.lock', + '/lazy.config.ts', + '/config/**/*', + '/scripts/**/*', + ], + exclude: [ + '/coverage/**/*', + '/dist*/**/*', + '**/*.tsbuildinfo', + '/docs/gen/**/*', + ], + }, + scripts: { build: { baseCommand: 'exit 0', runsAfter: { prebuild: {}, 'refresh-assets': {} }, workspaceOverrides: { - '{bublic/,}apps/vscode/*': { runsAfter: { 'refresh-assets': {} } }, - '{bublic/,}packages/*': { + 'apps/vscode/*': { runsAfter: { 'refresh-assets': {} } }, + 'packages/*': { runsAfter: { 'build-api': { in: 'self-only' }, prebuild: { in: 'self-only' } }, cache: { inputs: ['api/**/*', 'src/**/*'], @@ -20,7 +35,7 @@ export function generateSharedScripts(bublic: '' | '/bublic') runsAfter: { 'refresh-assets': {} }, cache: 'none', workspaceOverrides: { - '{bublic/,}apps/vscode/*': { runsAfter: { build: { in: 'self-only' } } }, + 'apps/vscode/*': { runsAfter: { build: { in: 'self-only' } } }, }, }, test: { @@ -50,14 +65,14 @@ export function generateSharedScripts(bublic: '' | '/bublic') }, 'refresh-assets': { execution: 'top-level', - baseCommand: `tsx ${bublic}/scripts/refresh-assets.ts`, + baseCommand: `tsx /scripts/refresh-assets.ts`, cache: { - inputs: ['package.json', `${bublic}/scripts/refresh-assets.ts`, `${bublic}/assets/**/*`], + inputs: ['package.json', `/scripts/refresh-assets.ts`, `/assets/**/*`], }, }, 'build-types': { execution: 'top-level', - baseCommand: `tsx ${bublic}/scripts/typecheck.ts`, + baseCommand: `tsx /scripts/typecheck.ts`, cache: { inputs: { include: ['/**/*.{ts,tsx}', '/tsconfig.json'], @@ -88,33 +103,12 @@ export function generateSharedScripts(bublic: '' | '/bublic') }, 'api-check': { execution: 'top-level', - baseCommand: `tsx ${bublic}/scripts/api-check.ts`, + baseCommand: `tsx /scripts/api-check.ts`, runsAfter: { 'build-api': {} }, cache: { - inputs: [`${bublic}/packages/*/api/public.d.ts`], + inputs: [`/packages/*/api/public.d.ts`], }, }, - } satisfies LazyConfig['scripts'] -} - -const config = { - baseCacheConfig: { - include: [ - '/{,bublic/}package.json', - '/{,bublic/}public-yarn.lock', - '/{,bublic/}lazy.config.ts', - '/{,bublic/}config/**/*', - '/{,bublic/}scripts/**/*', - ], - exclude: [ - '/coverage/**/*', - '/dist*/**/*', - '**/*.tsbuildinfo', - '/{,bublic/}docs/gen/**/*', - ], - }, - scripts: { - ...generateSharedScripts(''), }, } satisfies LazyConfig diff --git a/package.json b/package.json index 72b9bcdc2..210577b4a 100644 --- a/package.json +++ b/package.json @@ -37,9 +37,12 @@ "clean": "scripts/clean.sh", "postinstall": "husky install && yarn refresh-assets", "refresh-assets": "lazy refresh-assets", + "dev": "LAZYREPO_PRETTY_OUTPUT=0 lazy run dev --filter='apps/examples' --filter='packages/tldraw'", + "dev-vscode": "code ./apps/vscode/extension && lazy run dev --filter='apps/vscode/{extension,editor}'", + "dev-app": "LAZYREPO_PRETTY_OUTPUT=0 lazy run dev --filter='apps/{dotcom,dotcom-asset-upload,dotcom-worker}' --filter='packages/tldraw'", + "dev-huppy": "LAZYREPO_PRETTY_OUTPUT=0 lazy run dev --filter 'apps/huppy'", "build": "lazy build", - "dev": "LAZYREPO_PRETTY_OUTPUT=0 lazy run dev --filter='{,bublic/}apps/examples' --filter='{,bublic/}packages/tldraw'", - "dev-vscode": "code ./apps/vscode/extension && lazy run dev --filter='{,bublic/}apps/vscode/{extension,editor}'", + "build-app": "lazy run build --filter 'apps/dotcom'", "build-types": "lazy inherit", "build-api": "lazy build-api", "build-package": "lazy build-package", @@ -49,7 +52,8 @@ "check-scripts": "tsx scripts/check-scripts.ts", "api-check": "lazy api-check", "test": "lazy test", - "e2e": "lazy e2e --filter='{,bublic/}apps/examples'" + "test-coverage": "lazy test-coverage && node scripts/offer-coverage.mjs", + "e2e": "lazy e2e --filter='apps/examples'" }, "engines": { "npm": ">=7.0.0" @@ -100,6 +104,8 @@ "domino@^2.1.6": "patch:domino@npm%3A2.1.6#./.yarn/patches/domino-npm-2.1.6-b0dc3de857.patch" }, "dependencies": { + "@sentry/cli": "^2.25.0", + "cross-env": "^7.0.3", "purgecss": "^5.0.0", "svgo": "^3.0.2" } diff --git a/packages/state/LICENSE.md b/packages/state/LICENSE.md new file mode 100644 index 000000000..0ad7260cb --- /dev/null +++ b/packages/state/LICENSE.md @@ -0,0 +1 @@ +This code is licensed under the [tldraw license](https://github.com/tldraw/tldraw/blob/main/LICENSE.md) diff --git a/packages/store/LICENSE.md b/packages/store/LICENSE.md new file mode 100644 index 000000000..0ad7260cb --- /dev/null +++ b/packages/store/LICENSE.md @@ -0,0 +1 @@ +This code is licensed under the [tldraw license](https://github.com/tldraw/tldraw/blob/main/LICENSE.md) diff --git a/packages/tlsync/CHANGELOG.md b/packages/tlsync/CHANGELOG.md new file mode 100644 index 000000000..89b868bea --- /dev/null +++ b/packages/tlsync/CHANGELOG.md @@ -0,0 +1,168 @@ +# @tldraw/tlsync + +## 2.0.0-alpha.11 + +### Patch Changes + +- Updated dependencies + - @tldraw/tlschema@2.0.0-alpha.11 + - @tldraw/tlstore@2.0.0-alpha.11 + - @tldraw/utils@2.0.0-alpha.10 + +## 2.0.0-alpha.10 + +### Patch Changes + +- Updated dependencies [4b4399b6e] + - @tldraw/tlschema@2.0.0-alpha.10 + - @tldraw/tlstore@2.0.0-alpha.10 + - @tldraw/utils@2.0.0-alpha.9 + +## 2.0.0-alpha.9 + +### Patch Changes + +- Release day! +- Updated dependencies + - @tldraw/tlschema@2.0.0-alpha.9 + - @tldraw/tlstore@2.0.0-alpha.9 + - @tldraw/utils@2.0.0-alpha.8 + +## 2.0.0-alpha.8 + +### Patch Changes + +- 23dd81cfe: Make signia a peer dependency +- Updated dependencies [23dd81cfe] + - @tldraw/tlstore@2.0.0-alpha.8 + - @tldraw/tlschema@2.0.0-alpha.8 + +## 2.0.0-alpha.7 + +### Patch Changes + +- Bug fixes. +- Updated dependencies + - @tldraw/tlschema@2.0.0-alpha.7 + - @tldraw/tlstore@2.0.0-alpha.7 + - @tldraw/utils@2.0.0-alpha.7 + +## 2.0.0-alpha.6 + +### Patch Changes + +- Add licenses. +- Updated dependencies + - @tldraw/tlschema@2.0.0-alpha.6 + - @tldraw/tlstore@2.0.0-alpha.6 + - @tldraw/utils@2.0.0-alpha.6 + +## 2.0.0-alpha.5 + +### Patch Changes + +- Add CSS files to tldraw/tldraw. +- Updated dependencies + - @tldraw/tlschema@2.0.0-alpha.5 + - @tldraw/tlstore@2.0.0-alpha.5 + - @tldraw/utils@2.0.0-alpha.5 + +## 2.0.0-alpha.4 + +### Patch Changes + +- Add children to tldraw/tldraw +- Updated dependencies + - @tldraw/tlschema@2.0.0-alpha.4 + - @tldraw/tlstore@2.0.0-alpha.4 + - @tldraw/utils@2.0.0-alpha.4 + +## 2.0.0-alpha.3 + +### Patch Changes + +- Change permissions. +- Updated dependencies + - @tldraw/tlschema@2.0.0-alpha.3 + - @tldraw/tlstore@2.0.0-alpha.3 + - @tldraw/utils@2.0.0-alpha.3 + +## 2.0.0-alpha.2 + +### Patch Changes + +- Add tldraw, editor +- Updated dependencies + - @tldraw/tlschema@2.0.0-alpha.2 + - @tldraw/tlstore@2.0.0-alpha.2 + - @tldraw/utils@2.0.0-alpha.2 + +## 0.1.0-alpha.11 + +### Patch Changes + +- Fix stale reactors. +- Updated dependencies + - @tldraw/tlschema@0.1.0-alpha.11 + - @tldraw/tlstore@0.1.0-alpha.11 + - @tldraw/utils@0.1.0-alpha.11 + +## 0.1.0-alpha.10 + +### Patch Changes + +- Fix type export bug. +- Updated dependencies + - @tldraw/tlschema@0.1.0-alpha.10 + - @tldraw/tlstore@0.1.0-alpha.10 + - @tldraw/utils@0.1.0-alpha.10 + +## 0.1.0-alpha.9 + +### Patch Changes + +- Fix import bugs. +- Updated dependencies + - @tldraw/tlschema@0.1.0-alpha.9 + - @tldraw/tlstore@0.1.0-alpha.9 + - @tldraw/utils@0.1.0-alpha.9 + +## 0.1.0-alpha.8 + +### Patch Changes + +- Changes validation requirements, exports validation helpers. +- Updated dependencies + - @tldraw/tlschema@0.1.0-alpha.8 + - @tldraw/tlstore@0.1.0-alpha.8 + - @tldraw/utils@0.1.0-alpha.8 + +## 0.1.0-alpha.7 + +### Patch Changes + +- - Pre-pre-release update +- Updated dependencies + - @tldraw/tlschema@0.1.0-alpha.7 + - @tldraw/tlstore@0.1.0-alpha.7 + - @tldraw/utils@0.1.0-alpha.7 + +## 0.0.2-alpha.1 + +### Patch Changes + +- Fix error with HMR +- Updated dependencies + - @tldraw/tlschema@0.0.2-alpha.1 + - @tldraw/tlstore@0.0.2-alpha.1 + - @tldraw/utils@0.0.2-alpha.1 + +## 0.0.2-alpha.0 + +### Patch Changes + +- Initial release +- Updated dependencies + - @tldraw/tlschema@0.0.2-alpha.0 + - @tldraw/tlstore@0.0.2-alpha.0 + - @tldraw/utils@0.0.2-alpha.0 diff --git a/packages/tlsync/LICENSE.md b/packages/tlsync/LICENSE.md new file mode 100644 index 000000000..0ad7260cb --- /dev/null +++ b/packages/tlsync/LICENSE.md @@ -0,0 +1 @@ +This code is licensed under the [tldraw license](https://github.com/tldraw/tldraw/blob/main/LICENSE.md) diff --git a/packages/tlsync/README.md b/packages/tlsync/README.md new file mode 100644 index 000000000..07d091e48 --- /dev/null +++ b/packages/tlsync/README.md @@ -0,0 +1 @@ +# @tldraw/tlsync diff --git a/packages/tlsync/api-extractor.json b/packages/tlsync/api-extractor.json new file mode 100644 index 000000000..f1ed80e93 --- /dev/null +++ b/packages/tlsync/api-extractor.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "../../config/api-extractor.json" +} diff --git a/packages/tlsync/api-report.md b/packages/tlsync/api-report.md new file mode 100644 index 000000000..5e6f14679 --- /dev/null +++ b/packages/tlsync/api-report.md @@ -0,0 +1,296 @@ +## API Report File for "@tldraw/tlsync" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { Atom } from 'signia'; +import { BaseRecord } from '@tldraw/tlstore'; +import { MigrationFailureReason } from '@tldraw/tlstore'; +import { RecordsDiff } from '@tldraw/tlstore'; +import { RecordType } from '@tldraw/tlstore'; +import { Result } from '@tldraw/utils'; +import { SerializedSchema } from '@tldraw/tlstore'; +import { Signal } from 'signia'; +import { Store } from '@tldraw/tlstore'; +import { StoreSchema } from '@tldraw/tlstore'; +import * as WebSocket_2 from 'ws'; + +// @public (undocumented) +export type AppendOp = [type: ValueOpType.Append, values: unknown[], offset: number]; + +// @public (undocumented) +export function applyObjectDiff(object: T, objectDiff: ObjectDiff): T; + +// @public (undocumented) +export type DeleteOp = [type: ValueOpType.Delete]; + +// @public (undocumented) +export function diffRecord(prev: object, next: object): null | ObjectDiff; + +// @public +export const getNetworkDiff: >(diff: RecordsDiff) => NetworkDiff | null; + +// @public +export type NetworkDiff = { + [id: string]: RecordOp; +}; + +// @public (undocumented) +export type ObjectDiff = { + [k: string]: ValueOp; +}; + +// @public (undocumented) +export type PatchOp = [type: ValueOpType.Patch, diff: ObjectDiff]; + +// @public (undocumented) +export type PersistedRoomSnapshot = { + id: string; + slug: string; + drawing: RoomSnapshot; +}; + +// @public (undocumented) +export type PutOp = [type: ValueOpType.Put, value: unknown]; + +// @public (undocumented) +export type RecordOp = [RecordOpType.Patch, ObjectDiff] | [RecordOpType.Put, R] | [RecordOpType.Remove]; + +// @public (undocumented) +export enum RecordOpType { + // (undocumented) + Patch = "patch", + // (undocumented) + Put = "put", + // (undocumented) + Remove = "remove" +} + +// @public (undocumented) +export type RoomClient = { + serializedSchema: SerializedSchema; + socket: TLRoomSocket; + id: string; +}; + +// @public (undocumented) +export type RoomForId = { + id: string; + persistenceId?: string; + timeout?: any; + room: TLSyncRoom; + clients: Map>; +}; + +// @public (undocumented) +export type RoomSnapshot = { + clock: number; + documents: Array<{ + state: BaseRecord; + lastChangedClock: number; + }>; + tombstones?: Record; + schema?: SerializedSchema; +}; + +// @public +export function serializeMessage(message: Message): string; + +// @public (undocumented) +export type TLConnectRequest = { + type: 'connect'; + lastServerClock: number; + protocolVersion: number; + schema: SerializedSchema; + instanceId: string; +}; + +// @public (undocumented) +export enum TLIncompatibilityReason { + // (undocumented) + ClientTooOld = "clientTooOld", + // (undocumented) + InvalidOperation = "invalidOperation", + // (undocumented) + InvalidRecord = "invalidRecord", + // (undocumented) + ServerTooOld = "serverTooOld" +} + +// @public +export type TLPersistentClientSocket = { + connectionStatus: 'error' | 'offline' | 'online'; + sendMessage: (msg: TLSocketClientSentEvent) => void; + onReceiveMessage: SubscribingFn>; + onStatusChange: SubscribingFn; + restart: () => void; +}; + +// @public (undocumented) +export type TLPersistentClientSocketStatus = 'error' | 'offline' | 'online'; + +// @public (undocumented) +export type TLPingRequest = { + type: 'ping'; +}; + +// @public (undocumented) +export type TLPushRequest = { + type: 'push'; + clientClock: number; + diff: NetworkDiff; +} | { + type: 'push'; + clientClock: number; + presence: [RecordOpType.Patch, ObjectDiff] | [RecordOpType.Put, R]; +}; + +// @public (undocumented) +export type TLRoomSocket = { + isOpen: boolean; + sendMessage: (msg: TLSocketServerSentEvent) => void; +}; + +// @public +export abstract class TLServer { + abstract deleteRoomForId(roomId: string): void; + abstract getRoomForId(roomId: string): RoomForId | undefined; + handleConnection: (ws: WebSocket_2.WebSocket, roomId: string) => Promise; + loadFromDatabase?(roomId: string): Promise; + logEvent?(_event: { + roomId: string; + name: string; + clientId?: string; + }): void; + persistToDatabase?(roomId: string): Promise; + abstract setRoomForId(roomId: string, roomForId: RoomForId): void; +} + +// @public (undocumented) +export type TLSocketClientSentEvent = TLConnectRequest | TLPingRequest | TLPushRequest; + +// @public (undocumented) +export type TLSocketServerSentEvent = { + type: 'connect'; + hydrationType: 'wipe_all' | 'wipe_presence'; + protocolVersion: number; + schema: SerializedSchema; + diff: NetworkDiff; + serverClock: number; +} | { + type: 'error'; + error?: any; +} | { + type: 'incompatibility_error'; + reason: TLIncompatibilityReason; +} | { + type: 'patch'; + diff: NetworkDiff; + serverClock: number; +} | { + type: 'pong'; +} | { + type: 'push_result'; + clientClock: number; + serverClock: number; + action: 'commit' | 'discard' | { + rebaseWithDiff: NetworkDiff; + }; +}; + +// @public (undocumented) +export const TLSYNC_PROTOCOL_VERSION = 3; + +// @public +export class TLSyncClient = Store> { + constructor(config: { + store: S; + socket: TLPersistentClientSocket; + instanceId: string; + onLoad: (self: TLSyncClient) => void; + onLoadError: (error: Error) => void; + onSyncError: (reason: TLIncompatibilityReason) => void; + onAfterConnect?: (self: TLSyncClient) => void; + }); + // (undocumented) + close(): void; + // (undocumented) + incomingDiffBuffer: Extract, { + type: 'patch' | 'push_result'; + }>[]; + readonly instanceId: string; + // (undocumented) + isConnectedToRoom: boolean; + // (undocumented) + lastPushedPresenceState: null | R; + readonly onAfterConnect?: (self: TLSyncClient) => void; + // (undocumented) + readonly onSyncError: (reason: TLIncompatibilityReason) => void; + // (undocumented) + readonly presenceState: Signal; + // (undocumented) + readonly socket: TLPersistentClientSocket; + // (undocumented) + readonly store: S; +} + +// @public +export class TLSyncRoom { + constructor(schema: StoreSchema, snapshot?: RoomSnapshot); + addClient(client: RoomClient): void; + broadcastPatch({ diff, sourceClient }: { + diff: NetworkDiff; + sourceClient: RoomClient; + }): this; + // (undocumented) + clientIdsToInstanceIds: Map; + // (undocumented) + clients: Map>; + // (undocumented) + clock: number; + // (undocumented) + readonly documentTypes: Set; + // (undocumented) + getSnapshot(): RoomSnapshot; + handleClose: (client: RoomClient) => void; + handleConnection: (client: RoomClient) => this; + handleMessage: (client: RoomClient, message: TLSocketClientSentEvent) => Promise; + migrateDiffForClient(client: RoomClient, diff: NetworkDiff): Result, MigrationFailureReason>; + // (undocumented) + readonly presenceType: RecordType; + // (undocumented) + pruneTombstones(): void; + removeClient(client: RoomClient): void; + // (undocumented) + readonly schema: StoreSchema; + sendMessageToClient(client: RoomClient, message: TLSocketServerSentEvent): this; + // (undocumented) + readonly serializedSchema: SerializedSchema; + // (undocumented) + state: Atom<{ + documents: Record>; + tombstones: Record; + }, unknown>; + // (undocumented) + tombstoneHistoryStartsAtClock: number; +} + +// @public (undocumented) +export type ValueOp = AppendOp | DeleteOp | PatchOp | PutOp; + +// @public (undocumented) +export enum ValueOpType { + // (undocumented) + Append = "append", + // (undocumented) + Delete = "delete", + // (undocumented) + Patch = "patch", + // (undocumented) + Put = "put" +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/tlsync/package.json b/packages/tlsync/package.json new file mode 100644 index 000000000..bcf5a7c9c --- /dev/null +++ b/packages/tlsync/package.json @@ -0,0 +1,68 @@ +{ + "name": "@tldraw/tlsync", + "description": "A tiny little drawing app (multiplayer sync).", + "version": "2.0.0-alpha.11", + "private": true, + "packageManager": "yarn@3.5.0", + "author": { + "name": "tldraw GB Ltd.", + "email": "hello@tldraw.com" + }, + "homepage": "https://tldraw.dev", + "license": "SEE LICENSE IN LICENSE.md", + "repository": { + "type": "git", + "url": "https://github.com/tldraw/tldraw" + }, + "bugs": { + "url": "https://github.com/tldraw/tldraw/issues" + }, + "keywords": [ + "tldraw", + "drawing", + "app", + "development", + "whiteboard", + "canvas", + "infinite" + ], + "/* NOTE */": "These `main` and `types` fields are rewritten by the build script. They are not the actual values we publish", + "main": "./src/index.ts", + "types": "./.tsbuild/index.d.ts", + "/* GOTCHA */": "files will include ./dist and index.d.ts by default, add any others you want to include in here", + "files": [], + "scripts": { + "test": "lazy inherit", + "test-coverage": "lazy inherit", + "lint": "yarn run -T tsx ../../scripts/lint.ts" + }, + "devDependencies": { + "@tldraw/tldraw": "workspace:*", + "typescript": "^5.0.2", + "uuid-by-string": "^4.0.0", + "uuid-readable": "^0.0.2" + }, + "jest": { + "preset": "config/jest/node", + "testEnvironment": "jsdom", + "moduleNameMapper": { + "^~(.*)": "/src/$1" + }, + "transformIgnorePatterns": [ + "ignore everything. swc is fast enough to transform everything" + ], + "setupFiles": [ + "./setupJest.js" + ] + }, + "dependencies": { + "@tldraw/state": "workspace:*", + "@tldraw/store": "workspace:*", + "@tldraw/tlschema": "workspace:*", + "@tldraw/utils": "workspace:*", + "lodash.isequal": "^4.5.0", + "nanoevents": "^7.0.1", + "nanoid": "4.0.2", + "ws": "^8.16.0" + } +} diff --git a/packages/tlsync/setupJest.js b/packages/tlsync/setupJest.js new file mode 100644 index 000000000..b61be23d3 --- /dev/null +++ b/packages/tlsync/setupJest.js @@ -0,0 +1,13 @@ +require('fake-indexeddb/auto') +global.ResizeObserver = require('resize-observer-polyfill') +global.crypto ??= new (require('@peculiar/webcrypto').Crypto)() +global.FontFace = class FontFace { + load() { + return Promise.resolve() + } +} +document.fonts = { + add: () => {}, + delete: () => {}, + forEach: () => {}, +} diff --git a/packages/tlsync/src/index.ts b/packages/tlsync/src/index.ts new file mode 100644 index 000000000..417949046 --- /dev/null +++ b/packages/tlsync/src/index.ts @@ -0,0 +1,35 @@ +export { TLServer, type DBLoadResult } from './lib/TLServer' +export { + TLSyncClient, + type TLPersistentClientSocket, + type TLPersistentClientSocketStatus, +} from './lib/TLSyncClient' +export { TLSyncRoom, type RoomSnapshot, type TLRoomSocket } from './lib/TLSyncRoom' +export { chunk } from './lib/chunk' +export { + RecordOpType, + ValueOpType, + applyObjectDiff, + diffRecord, + getNetworkDiff, + type AppendOp, + type DeleteOp, + type NetworkDiff, + type ObjectDiff, + type PatchOp, + type PutOp, + type RecordOp, + type ValueOp, +} from './lib/diff' +export { + TLIncompatibilityReason, + TLSYNC_PROTOCOL_VERSION, + type TLConnectRequest, + type TLPingRequest, + type TLPushRequest, + type TLSocketClientSentEvent, + type TLSocketServerSentEvent, +} from './lib/protocol' +export { schema } from './lib/schema' +export { serializeMessage } from './lib/serializeMessage' +export type { PersistedRoomSnapshotForSupabase, RoomState as RoomState } from './lib/server-types' diff --git a/packages/tlsync/src/lib/RoomSession.ts b/packages/tlsync/src/lib/RoomSession.ts new file mode 100644 index 000000000..a9152f1fa --- /dev/null +++ b/packages/tlsync/src/lib/RoomSession.ts @@ -0,0 +1,36 @@ +import { SerializedSchema, UnknownRecord } from '@tldraw/store' +import { TLRoomSocket } from './TLSyncRoom' + +export enum RoomSessionState { + AWAITING_CONNECT_MESSAGE = 'awaiting-connect-message', + AWAITING_REMOVAL = 'awaiting-removal', + CONNECTED = 'connected', +} + +export const SESSION_START_WAIT_TIME = 10000 +export const SESSION_REMOVAL_WAIT_TIME = 10000 +export const SESSION_IDLE_TIMEOUT = 20000 + +export type RoomSession = + | { + state: RoomSessionState.AWAITING_CONNECT_MESSAGE + sessionKey: string + presenceId: string + socket: TLRoomSocket + sessionStartTime: number + } + | { + state: RoomSessionState.AWAITING_REMOVAL + sessionKey: string + presenceId: string + socket: TLRoomSocket + cancellationTime: number + } + | { + state: RoomSessionState.CONNECTED + sessionKey: string + presenceId: string + socket: TLRoomSocket + serializedSchema: SerializedSchema + lastInteractionTime: number + } diff --git a/packages/tlsync/src/lib/ServerSocketAdapter.ts b/packages/tlsync/src/lib/ServerSocketAdapter.ts new file mode 100644 index 000000000..9f6ed134b --- /dev/null +++ b/packages/tlsync/src/lib/ServerSocketAdapter.ts @@ -0,0 +1,20 @@ +import { UnknownRecord } from '@tldraw/store' +import ws from 'ws' +import { TLRoomSocket } from './TLSyncRoom' +import { TLSocketServerSentEvent } from './protocol' +import { serializeMessage } from './serializeMessage' + +/** @public */ +export class ServerSocketAdapter implements TLRoomSocket { + constructor(public readonly ws: WebSocket | ws.WebSocket) {} + // eslint-disable-next-line no-restricted-syntax + get isOpen(): boolean { + return this.ws.readyState === 1 // ready state open + } + sendMessage(msg: TLSocketServerSentEvent) { + this.ws.send(serializeMessage(msg)) + } + close() { + this.ws.close() + } +} diff --git a/packages/tlsync/src/lib/TLServer.ts b/packages/tlsync/src/lib/TLServer.ts new file mode 100644 index 000000000..5389a3410 --- /dev/null +++ b/packages/tlsync/src/lib/TLServer.ts @@ -0,0 +1,267 @@ +import { nanoid } from 'nanoid' +import * as WebSocket from 'ws' +import { ServerSocketAdapter } from './ServerSocketAdapter' +import { RoomSnapshot, TLSyncRoom } from './TLSyncRoom' +import { JsonChunkAssembler } from './chunk' +import { schema } from './schema' +import { RoomState } from './server-types' + +type LoadKind = 'new' | 'reopen' | 'open' +export type DBLoadResult = + | { + type: 'error' + error?: Error | undefined + } + | { + type: 'room_found' + snapshot: RoomSnapshot + } + | { + type: 'room_not_found' + } + +/** + * This class manages rooms for a websocket server. + * + * @public + */ +export abstract class TLServer { + schema = schema + + async getInitialRoomState(persistenceKey: string): Promise<[RoomState, LoadKind]> { + let roomState = this.getRoomForPersistenceKey(persistenceKey) + + let roomOpenKind = 'open' as 'open' | 'reopen' | 'new' + + // If no room exists for the id, create one + if (roomState === undefined) { + // Try to load a room from persistence + if (this.loadFromDatabase) { + const data = await this.loadFromDatabase(persistenceKey) + if (data.type === 'error') { + throw data.error + } + + if (data.type === 'room_found') { + roomOpenKind = 'reopen' + + roomState = { + persistenceKey, + room: new TLSyncRoom(this.schema, data.snapshot), + } + } + } + + // If we still don't have a room, create a new one + if (roomState === undefined) { + roomOpenKind = 'new' + + roomState = { + persistenceKey, + room: new TLSyncRoom(this.schema), + } + } + + const thisRoom = roomState.room + + roomState.room.events.on('room_became_empty', async () => { + // Record that the room is now empty + const roomState = this.getRoomForPersistenceKey(persistenceKey) + if (!roomState || roomState.room !== thisRoom) { + // room was already closed + return + } + this.logEvent({ type: 'room', roomId: persistenceKey, name: 'room_empty' }) + this.deleteRoomState(persistenceKey) + roomState.room.close() + + try { + await this.persistToDatabase?.(persistenceKey) + } catch (err) { + this.logEvent({ type: 'room', roomId: persistenceKey, name: 'fail_persist' }) + console.error('failed to save to storage', err) + } + }) + + // persist on an interval... + this.setRoomState(persistenceKey, roomState) + + // If we created a new room, then persist to the database again; + // we may have run migrations or cleanup, so let's make sure that + // the new data is put back into the database. + this.persistToDatabase?.(persistenceKey) + } + + return [roomState, roomOpenKind] + } + + /** + * When a connection comes in, set up the client and event listeners for the client's room. The + * roomId is the websocket's protocol. + * + * @param ws - The client's websocket connection. + * @public + */ + handleConnection = async ({ + socket, + persistenceKey, + sessionKey, + storeId, + }: { + socket: WebSocket.WebSocket + persistenceKey: string + sessionKey: string + storeId: string + }) => { + const clientId = nanoid() + const [roomState, roomOpenKind] = await this.getInitialRoomState(persistenceKey) + + roomState.room.handleNewSession(sessionKey, new ServerSocketAdapter(socket)) + + if (roomOpenKind === 'new' || roomOpenKind === 'reopen') { + // Record that the room is now active + this.logEvent({ type: 'room', roomId: persistenceKey, name: 'room_start' }) + + // Record what kind of room start event this is (why don't we extend the previous event? or even remove it?) + this.logEvent({ + type: 'client', + roomId: persistenceKey, + name: roomOpenKind === 'new' ? 'room_create' : 'room_reopen', + clientId, + instanceId: sessionKey, + localClientId: storeId, + }) + } + + // Record that the user entered the room + this.logEvent({ + type: 'client', + roomId: persistenceKey, + name: 'enter', + clientId, + instanceId: sessionKey, + localClientId: storeId, + }) + + // Handle a 'message' event from the server. + const assembler = new JsonChunkAssembler() + const handleMessageFromClient = (event: WebSocket.MessageEvent) => { + try { + if (typeof event.data === 'string') { + const res = assembler.handleMessage(event.data) + if (res?.data) { + roomState.room.handleMessage(sessionKey, res.data as any) + } + if (res?.error) { + console.warn('Error assembling message', res.error) + } + } else { + console.warn('Unknown message type', typeof event.data) + } + } catch (e) { + console.error(e) + socket.send(JSON.stringify({ type: 'error', error: e })) + socket.close(400) + } + } + + const handleCloseOrErrorFromClient = () => { + // Remove the client from the room and delete associated user data. + roomState?.room.handleClose(sessionKey) + } + + const unsub = roomState.room.events.on('session_removed', async (ev) => { + // Record who the last person to leave the room was + if (sessionKey !== ev.sessionKey) return + unsub() + this.logEvent({ + type: 'client', + roomId: persistenceKey, + name: 'leave', + clientId, + instanceId: sessionKey, + localClientId: storeId, + }) + this.logEvent({ + type: 'client', + roomId: persistenceKey, + name: 'last_out', + clientId, + instanceId: sessionKey, + localClientId: storeId, + }) + + socket.removeEventListener('message', handleMessageFromClient) + socket.removeEventListener('close', handleCloseOrErrorFromClient) + socket.removeEventListener('error', handleCloseOrErrorFromClient) + }) + + socket.addEventListener('message', handleMessageFromClient) + socket.addEventListener('close', handleCloseOrErrorFromClient) + socket.addEventListener('error', handleCloseOrErrorFromClient) + } + + /** + * Load data from a database. (Optional) + * + * @param roomId - The id of the room to load. + * @public + */ + abstract loadFromDatabase?(roomId: string): Promise + + /** + * Persist data to a database. (Optional) + * + * @param roomId - The id of the room to load. + * @public + */ + abstract persistToDatabase?(roomId: string): Promise + + /** + * Log an event. (Optional) + * + * @param event - The event to log. + * @public + */ + abstract logEvent( + event: + | { + type: 'client' + roomId: string + name: string + clientId: string + instanceId: string + localClientId: string + } + | { + type: 'room' + roomId: string + name: string + } + ): void + + /** + * Get a room by its id. + * + * @param persistenceKey - The id of the room to get. + * @public + */ + abstract getRoomForPersistenceKey(persistenceKey: string): RoomState | undefined + + /** + * Set a room to an id. + * + * @param persistenceKey - The id of the room to set. + * @param roomState - The room to set. + * @public + */ + abstract setRoomState(persistenceKey: string, roomState: RoomState): void + + /** + * Delete a room by its id. + * + * @param persistenceKey - The id of the room to delete. + * @public + */ + abstract deleteRoomState(persistenceKey: string): void +} diff --git a/packages/tlsync/src/lib/TLSyncClient.ts b/packages/tlsync/src/lib/TLSyncClient.ts new file mode 100644 index 000000000..133059d61 --- /dev/null +++ b/packages/tlsync/src/lib/TLSyncClient.ts @@ -0,0 +1,590 @@ +import { Signal, react, transact } from '@tldraw/state' +import { + RecordId, + RecordsDiff, + Store, + UnknownRecord, + reverseRecordsDiff, + squashRecordDiffs, +} from '@tldraw/store' +import { exhaustiveSwitchError, objectMapEntries, rafThrottle } from '@tldraw/utils' +import isEqual from 'lodash.isequal' +import { nanoid } from 'nanoid' +import { NetworkDiff, RecordOpType, applyObjectDiff, diffRecord, getNetworkDiff } from './diff' +import { interval } from './interval' +import { + TLIncompatibilityReason, + TLPushRequest, + TLSYNC_PROTOCOL_VERSION, + TLSocketClientSentEvent, + TLSocketServerSentEvent, +} from './protocol' +import './requestAnimationFrame.polyfill' + +type SubscribingFn = (cb: (val: T) => void) => () => void + +/** @public */ +export type TLPersistentClientSocketStatus = 'online' | 'offline' | 'error' +/** + * A socket that can be used to send and receive messages to the server. It should handle staying + * open and reconnecting when the connection is lost. In actual client code this will be a wrapper + * around a websocket or socket.io or something similar. + * + * @public + */ +export type TLPersistentClientSocket = { + /** Whether there is currently an open connection to the server. */ + connectionStatus: 'online' | 'offline' | 'error' + /** Send a message to the server */ + sendMessage: (msg: TLSocketClientSentEvent) => void + /** Attach a listener for messages sent by the server */ + onReceiveMessage: SubscribingFn> + /** Attach a listener for connection status changes */ + onStatusChange: SubscribingFn + /** Restart the connection */ + restart: () => void +} + +const PING_INTERVAL = 5000 +const MAX_TIME_TO_WAIT_FOR_SERVER_INTERACTION_BEFORE_RESETTING_CONNECTION = PING_INTERVAL * 2 + +// Should connect support chunking the response to allow for large payloads? + +/** + * TLSyncClient manages syncing data in a local Store with a remote server. + * + * It uses a git-style push/pull/rebase model. + * + * @public + */ +export class TLSyncClient = Store> { + /** The last clock time from the most recent server update */ + private lastServerClock = 0 + private lastServerInteractionTimestamp = Date.now() + + /** The queue of in-flight push requests that have not yet been acknowledged by the server */ + private pendingPushRequests: { request: TLPushRequest; sent: boolean }[] = [] + + /** + * The diff of 'unconfirmed', 'optimistic' changes that have been made locally by the user if we + * take this diff, reverse it, and apply that to the store, our store will match exactly the most + * recent state of the server that we know about + */ + private speculativeChanges: RecordsDiff = { + added: {} as any, + updated: {} as any, + removed: {} as any, + } + + private disposables: Array<() => void> = [] + + readonly store: S + readonly socket: TLPersistentClientSocket + + readonly presenceState: Signal | undefined + + // isOnline is true when we have an open socket connection and we have + // established a connection with the server room (i.e. we have received a 'connect' message) + isConnectedToRoom = false + + /** + * The client clock is essentially a counter for push requests Each time a push request is created + * the clock is incremented. This clock is sent with the push request to the server, and the + * server returns it with the response so that we can match up the response with the request. + * + * The clock may also be used at one point in the future to allow the client to re-send push + * requests idempotently (i.e. the server will keep track of each client's clock and not execute + * requests it has already handled), but at the time of writing this is neither needed nor + * implemented. + * + * @public + */ + private clientClock = 0 + + /** + * Called immediately after a connect acceptance has been received and processed Use this to make + * any changes to the store that are required to keep it operational + */ + public readonly onAfterConnect?: (self: TLSyncClient, isNew: boolean) => void + public readonly onSyncError: (reason: TLIncompatibilityReason) => void + + private isDebugging = false + private debug(...args: any[]) { + if (this.isDebugging) { + // eslint-disable-next-line no-console + console.debug(...args) + } + } + + private readonly presenceType: R['typeName'] + + didCancel?: () => boolean + + constructor(config: { + store: S + socket: TLPersistentClientSocket + presence: Signal + onLoad: (self: TLSyncClient) => void + onLoadError: (error: Error) => void + onSyncError: (reason: TLIncompatibilityReason) => void + onAfterConnect?: (self: TLSyncClient, isNew: boolean) => void + didCancel?: () => boolean + }) { + this.didCancel = config.didCancel + + this.presenceType = config.store.scopedTypes.presence.values().next().value + if (!this.presenceType || config.store.scopedTypes.presence.size > 1) { + throw new Error('Store must have exactly one presence type') + } + + if (typeof window !== 'undefined') { + ;(window as any).tlsync = this + } + this.store = config.store + this.socket = config.socket + this.onAfterConnect = config.onAfterConnect + this.onSyncError = config.onSyncError + + let didLoad = false + + this.presenceState = config.presence + + this.disposables.push( + // when local 'user' changes are made, send them to the server + // or stash them locally in offline mode + this.store.listen( + ({ changes }) => { + if (this.didCancel?.()) return this.close() + this.debug('received store changes', { changes }) + this.push(changes) + }, + { source: 'user', scope: 'document' } + ), + // when the server sends us events, handle them + this.socket.onReceiveMessage((msg) => { + if (this.didCancel?.()) return this.close() + this.debug('received message from server', msg) + this.handleServerEvent(msg) + // the first time we receive a message from the server, we should trigger + + // one of the load callbacks + if (!didLoad) { + didLoad = true + if (msg.type === 'error') { + config.onLoadError(msg.error) + } else { + config.onLoad(this) + } + } + }), + // handle switching between online and offline + this.socket.onStatusChange((status) => { + if (this.didCancel?.()) return this.close() + this.debug('socket status changed', status) + if (status === 'online') { + this.sendConnectMessage() + } else { + this.resetConnection() + // if we reached here before connecting to the server + // it's a socket error, mostly likely the server is down or + // it's the wrong url. + if (status === 'error' && !didLoad) { + didLoad = true + config.onLoadError(new Error('socket error')) + } + } + }), + // Send a ping every PING_INTERVAL ms while online + interval(() => { + if (this.didCancel?.()) return this.close() + this.debug('ping loop', { isConnectedToRoom: this.isConnectedToRoom }) + if (!this.isConnectedToRoom) return + try { + this.socket.sendMessage({ type: 'ping' }) + } catch (error) { + console.warn('ping failed, resetting', error) + this.resetConnection() + } + }, PING_INTERVAL), + // Check the server connection health, reset the connection if needed + interval(() => { + if (this.didCancel?.()) return this.close() + this.debug('health check loop', { isConnectedToRoom: this.isConnectedToRoom }) + if (!this.isConnectedToRoom) return + const timeSinceLastServerInteraction = Date.now() - this.lastServerInteractionTimestamp + + if ( + timeSinceLastServerInteraction < + MAX_TIME_TO_WAIT_FOR_SERVER_INTERACTION_BEFORE_RESETTING_CONNECTION + ) { + this.debug('health check passed', { timeSinceLastServerInteraction }) + // last ping was recent, so no need to take any action + return + } + + console.warn(`Haven't heard from the server in a while, resetting connection...`) + this.resetConnection() + }, PING_INTERVAL * 2) + ) + + if (this.presenceState) { + this.disposables.push( + react('pushPresence', () => { + if (this.didCancel?.()) return this.close() + this.pushPresence(this.presenceState!.get()) + }) + ) + } + // if the socket is already online before this client was instantiated + // then we should send a connect message right away + if (this.socket.connectionStatus === 'online') { + this.sendConnectMessage() + } + } + + latestConnectRequestId: string | null = null + + /** + * This is the first message that is sent over a newly established socket connection. And we need + * to wait for the response before this client can be used. + */ + private sendConnectMessage() { + if (this.isConnectedToRoom) { + console.error('sendConnectMessage called while already connected') + return + } + this.debug('sending connect message') + this.latestConnectRequestId = nanoid() + this.socket.sendMessage({ + type: 'connect', + connectRequestId: this.latestConnectRequestId, + schema: this.store.schema.serialize(), + protocolVersion: TLSYNC_PROTOCOL_VERSION, + lastServerClock: this.lastServerClock, + }) + } + + /** Switch to offline mode */ + private resetConnection(hard = false) { + this.debug('resetting connection') + if (hard) { + this.lastServerClock = 0 + } + // kill all presence state + this.store.remove(Object.keys(this.store.serialize('presence')) as any) + this.lastPushedPresenceState = null + this.isConnectedToRoom = false + this.pendingPushRequests = [] + this.incomingDiffBuffer = [] + if (this.socket.connectionStatus === 'online') { + this.socket.restart() + } + } + + /** + * Invoked when the socket connection comes online, either for the first time or as the result of + * a reconnect. The goal is to rebase on the server's state and fire off a new push request for + * any local changes that were made while offline. + */ + private didReconnect(event: Extract, { type: 'connect' }>) { + this.debug('did reconnect', event) + if (event.connectRequestId !== this.latestConnectRequestId) { + // ignore connect events for old connect requests + return + } + this.latestConnectRequestId = null + + if (this.isConnectedToRoom) { + console.error('didReconnect called while already connected') + this.resetConnection(true) + return + } + if (this.pendingPushRequests.length > 0) { + console.error('pendingPushRequests should already be empty when we reconnect') + this.resetConnection(true) + return + } + // at the end of this process we want to have at most one pending push request + // based on anything inside this.speculativeChanges + transact(() => { + // Now our goal is to rebase on the server's state. + // This means wiping away any peer presence data, which the server will replace in full on every connect. + // If the server does not have enough history to give us a partial document state hydration we will + // also need to wipe away all of our document state before hydrating with the server's state from scratch. + const stashedChanges = this.speculativeChanges + this.speculativeChanges = { added: {} as any, updated: {} as any, removed: {} as any } + + this.store.mergeRemoteChanges(() => { + // gather records to delete in a NetworkDiff + const wipeDiff: NetworkDiff = {} + const wipeAll = event.hydrationType === 'wipe_all' + if (!wipeAll) { + // if we're only wiping presence data, undo the speculative changes first + this.store.applyDiff(reverseRecordsDiff(stashedChanges), false) + } + + // now wipe all presence data and, if needed, all document data + for (const [id, record] of objectMapEntries(this.store.serialize('all'))) { + if ( + (wipeAll && this.store.scopedTypes.document.has(record.typeName)) || + record.typeName === this.presenceType + ) { + wipeDiff[id] = [RecordOpType.Remove] + } + } + + // then apply the upstream changes + this.applyNetworkDiff({ ...wipeDiff, ...event.diff }, true) + }) + + // now re-apply the speculative changes as a 'user' to trigger + // creating a new push request with the appropriate diff + this.isConnectedToRoom = true + this.store.applyDiff(stashedChanges) + + this.store.ensureStoreIsUsable() + // TODO: reinstate isNew + this.onAfterConnect?.(this, false) + }) + + this.lastServerClock = event.serverClock + } + + incomingDiffBuffer: Extract, { type: 'patch' | 'push_result' }>[] = [] + + /** Handle events received from the server */ + private handleServerEvent = (event: TLSocketServerSentEvent) => { + this.debug('received server event', event) + this.lastServerInteractionTimestamp = Date.now() + // always update the lastServerClock when it is present + switch (event.type) { + case 'connect': + this.didReconnect(event) + break + case 'error': + console.error('Server error', event.error) + console.error('Restarting socket') + this.socket.restart() + break + case 'patch': + case 'push_result': + // wait for a connect to succeed before processing more events + if (!this.isConnectedToRoom) break + this.incomingDiffBuffer.push(event) + this.scheduleRebase() + break + case 'incompatibility_error': + this.onSyncError(event.reason) + break + case 'pong': + // noop, we only use ping/pong to set lastSeverInteractionTimestamp + break + default: + exhaustiveSwitchError(event) + } + } + + close() { + this.debug('closing') + this.disposables.forEach((dispose) => dispose()) + } + + lastPushedPresenceState: R | null = null + + private pushPresence(nextPresence: R | null) { + if (!this.isConnectedToRoom) { + // if we're offline, don't do anything + return + } + let req: TLPushRequest | null = null + if (!this.lastPushedPresenceState && nextPresence) { + // we don't have a last presence state, so we need to push the full state + req = { + type: 'push', + presence: [RecordOpType.Put, nextPresence], + clientClock: this.clientClock++, + } + } else if (this.lastPushedPresenceState && nextPresence) { + // we have a last presence state, so we need to push a diff if there is one + const diff = diffRecord(this.lastPushedPresenceState, nextPresence) + if (diff) { + req = { + type: 'push', + presence: [RecordOpType.Patch, diff], + clientClock: this.clientClock++, + } + } + } + this.lastPushedPresenceState = nextPresence + if (req) { + this.pendingPushRequests.push({ request: req, sent: false }) + this.flushPendingPushRequests() + } + } + + /** Push a change to the server, or stash it locally if we're offline */ + private push(change: RecordsDiff) { + this.debug('push', change) + // the Store doesn't do deep equality checks when making changes + // so it's possible that the diff passed in here is actually a no-op. + // either way, we also don't want to send whole objects over the wire if + // only small parts of them have changed, so we'll do a shallow-ish diff + // which also uses deep equality checks to see if the change is actually + // a no-op. + const diff = getNetworkDiff(change) + if (!diff) return + + // the change is not a no-op so we'll send it to the server + // but first let's merge the records diff into the speculative changes + this.speculativeChanges = squashRecordDiffs([this.speculativeChanges, change]) + + if (!this.isConnectedToRoom) { + // don't sent push requests or even store them up while offline + // when we come back online we'll generate another push request from + // scratch based on the speculativeChanges diff + return + } + + const pushRequest: TLPushRequest = { + type: 'push', + diff, + clientClock: this.clientClock++, + } + + this.pendingPushRequests.push({ request: pushRequest, sent: false }) + + // immediately calling .send on the websocket here was causing some interaction + // slugishness when e.g. drawing or translating shapes. Seems like it blocks + // until the send completes. So instead we'll schedule a send to happen on some + // tick in the near future. + this.flushPendingPushRequests() + } + + /** Send any unsent push requests to the server */ + private flushPendingPushRequests = rafThrottle(() => { + this.debug('flushing pending push requests', { + isConnectedToRoom: this.isConnectedToRoom, + pendingPushRequests: this.pendingPushRequests, + }) + if (!this.isConnectedToRoom || this.store.isPossiblyCorrupted()) { + return + } + for (const pendingPushRequest of this.pendingPushRequests) { + if (!pendingPushRequest.sent) { + if (this.socket.connectionStatus !== 'online') { + // we went offline, so don't send anything + return + } + this.socket.sendMessage(pendingPushRequest.request) + pendingPushRequest.sent = true + } + } + }) + + /** + * Applies a 'network' diff to the store this does value-based equality checking so that if the + * data is the same (as opposed to merely identical with ===), then no change is made and no + * changes will be propagated back to store listeners + */ + private applyNetworkDiff(diff: NetworkDiff, runCallbacks: boolean) { + this.debug('applyNetworkDiff', diff) + const changes: RecordsDiff = { added: {} as any, updated: {} as any, removed: {} as any } + type k = keyof typeof changes.updated + let hasChanges = false + for (const [id, op] of objectMapEntries(diff)) { + if (op[0] === RecordOpType.Put) { + const existing = this.store.get(id as RecordId) + if (existing && !isEqual(existing, op[1])) { + hasChanges = true + changes.updated[id as k] = [existing, op[1]] + } else { + hasChanges = true + changes.added[id as k] = op[1] + } + } else if (op[0] === RecordOpType.Patch) { + const record = this.store.get(id as RecordId) + if (!record) { + // the record was removed upstream + continue + } + const patched = applyObjectDiff(record, op[1]) + hasChanges = true + changes.updated[id as k] = [record, patched] + } else if (op[0] === RecordOpType.Remove) { + if (this.store.has(id as RecordId)) { + hasChanges = true + changes.removed[id as k] = this.store.get(id as RecordId) + } + } + } + if (hasChanges) { + this.store.applyDiff(changes, runCallbacks) + } + } + + private rebase = () => { + // need to make sure that our speculative changes are in sync with the actual store instance before + // proceeding, to avoid inconsistency bugs. + this.store._flushHistory() + if (this.incomingDiffBuffer.length === 0) return + + const diffs = this.incomingDiffBuffer + this.incomingDiffBuffer = [] + + try { + this.store.mergeRemoteChanges(() => { + // first undo speculative changes + this.store.applyDiff(reverseRecordsDiff(this.speculativeChanges), false) + + // then apply network diffs on top of known-to-be-synced data + for (const diff of diffs) { + if (diff.type === 'patch') { + this.applyNetworkDiff(diff.diff, true) + continue + } + // handling push_result + if (this.pendingPushRequests.length === 0) { + throw new Error('Received push_result but there are no pending push requests') + } + if (this.pendingPushRequests[0].request.clientClock !== diff.clientClock) { + throw new Error( + 'Received push_result for a push request that is not at the front of the queue' + ) + } + if (diff.action === 'discard') { + this.pendingPushRequests.shift() + } else if (diff.action === 'commit') { + const { request } = this.pendingPushRequests.shift()! + if ('diff' in request) { + this.applyNetworkDiff(request.diff, true) + } + } else { + this.applyNetworkDiff(diff.action.rebaseWithDiff, true) + this.pendingPushRequests.shift() + } + } + // update the speculative diff while re-applying pending changes + try { + this.speculativeChanges = this.store.extractingChanges(() => { + for (const { request } of this.pendingPushRequests) { + if (!('diff' in request)) continue + this.applyNetworkDiff(request.diff, true) + } + }) + } catch (e) { + console.error(e) + // throw away the speculative changes and start over + this.speculativeChanges = { added: {} as any, updated: {} as any, removed: {} as any } + this.resetConnection() + } + }) + this.store.ensureStoreIsUsable() + this.lastServerClock = diffs.at(-1)?.serverClock ?? this.lastServerClock + } catch (e) { + console.error(e) + this.resetConnection() + } + } + + private scheduleRebase = rafThrottle(this.rebase) +} diff --git a/packages/tlsync/src/lib/TLSyncRoom.ts b/packages/tlsync/src/lib/TLSyncRoom.ts new file mode 100644 index 000000000..fce32750e --- /dev/null +++ b/packages/tlsync/src/lib/TLSyncRoom.ts @@ -0,0 +1,955 @@ +import { Atom, atom, transaction } from '@tldraw/state' +import { + IdOf, + MigrationFailureReason, + RecordType, + SerializedSchema, + StoreSchema, + UnknownRecord, + compareRecordVersions, + getRecordVersion, +} from '@tldraw/store' +import { DocumentRecordType, PageRecordType, TLDOCUMENT_ID } from '@tldraw/tlschema' +import { + Result, + assertExists, + exhaustiveSwitchError, + getOwnProperty, + hasOwnProperty, + objectMapEntries, + objectMapKeys, +} from '@tldraw/utils' +import isEqual from 'lodash.isequal' +import { createNanoEvents } from 'nanoevents' +import { + RoomSession, + RoomSessionState, + SESSION_IDLE_TIMEOUT, + SESSION_REMOVAL_WAIT_TIME, + SESSION_START_WAIT_TIME, +} from './RoomSession' +import { + NetworkDiff, + ObjectDiff, + RecordOp, + RecordOpType, + ValueOpType, + applyObjectDiff, + diffRecord, +} from './diff' +import { interval } from './interval' +import { + TLIncompatibilityReason, + TLSYNC_PROTOCOL_VERSION, + TLSocketClientSentEvent, + TLSocketServerSentEvent, +} from './protocol' + +/** @public */ +export type TLRoomSocket = { + isOpen: boolean + sendMessage: (msg: TLSocketServerSentEvent) => void + close: () => void +} + +// the max number of tombstones to keep in the store +export const MAX_TOMBSTONES = 3000 +// the number of tombstones to delete when the max is reached +export const TOMBSTONE_PRUNE_BUFFER_SIZE = 300 + +const timeSince = (time: number) => Date.now() - time + +class DocumentState { + _atom: Atom<{ state: R; lastChangedClock: number }> + + static createWithoutValidating( + state: R, + lastChangedClock: number, + recordType: RecordType + ): DocumentState { + return new DocumentState(state, lastChangedClock, recordType) + } + + static createAndValidate( + state: R, + lastChangedClock: number, + recordType: RecordType + ): Result, Error> { + try { + recordType.validate(state) + } catch (error: any) { + return Result.err(error) + } + return Result.ok(new DocumentState(state, lastChangedClock, recordType)) + } + + private constructor( + state: R, + lastChangedClock: number, + private readonly recordType: RecordType + ) { + this._atom = atom('document:' + state.id, { state, lastChangedClock }) + } + // eslint-disable-next-line no-restricted-syntax + get state() { + return this._atom.get().state + } + // eslint-disable-next-line no-restricted-syntax + get lastChangedClock() { + return this._atom.get().lastChangedClock + } + replaceState(state: R, clock: number): Result { + const diff = diffRecord(this.state, state) + if (!diff) return Result.ok(null) + try { + this.recordType.validate(state) + } catch (error: any) { + return Result.err(error) + } + this._atom.set({ state, lastChangedClock: clock }) + return Result.ok(diff) + } + mergeDiff(diff: ObjectDiff, clock: number): Result { + const newState = applyObjectDiff(this.state, diff) + return this.replaceState(newState, clock) + } +} + +/** @public */ +export type RoomSnapshot = { + clock: number + documents: Array<{ state: UnknownRecord; lastChangedClock: number }> + tombstones?: Record + schema?: SerializedSchema +} + +/** + * A room is a workspace for a group of clients. It allows clients to collaborate on documents + * within that workspace. + * + * @public + */ +export class TLSyncRoom { + // A table of connected clients + readonly sessions = new Map>() + + pruneSessions = () => { + for (const client of this.sessions.values()) { + switch (client.state) { + case RoomSessionState.CONNECTED: { + const hasTimedOut = timeSince(client.lastInteractionTime) > SESSION_IDLE_TIMEOUT + if (hasTimedOut || !client.socket.isOpen) { + this.cancelSession(client.sessionKey) + } + break + } + case RoomSessionState.AWAITING_CONNECT_MESSAGE: { + const hasTimedOut = timeSince(client.sessionStartTime) > SESSION_START_WAIT_TIME + if (hasTimedOut || !client.socket.isOpen) { + // remove immediately + this.removeSession(client.sessionKey) + } + break + } + case RoomSessionState.AWAITING_REMOVAL: { + const hasTimedOut = timeSince(client.cancellationTime) > SESSION_REMOVAL_WAIT_TIME + if (hasTimedOut) { + this.removeSession(client.sessionKey) + } + break + } + default: { + exhaustiveSwitchError(client) + } + } + } + } + + private disposables: Array<() => void> = [interval(this.pruneSessions, 2000)] + + close() { + this.disposables.forEach((d) => d()) + this.sessions.forEach((session) => { + session.socket.close() + }) + } + + readonly events = createNanoEvents<{ + room_became_empty: () => void + session_removed: (args: { sessionKey: string }) => void + }>() + + // Values associated with each uid (must be serializable). + state = atom<{ + documents: Record> + tombstones: Record + }>('room state', { + documents: {}, + tombstones: {}, + }) + + // this clock should start higher than the client, to make sure that clients who sync with their + // initial lastServerClock value get the full state + // in this case clients will start with 0, and the server will start with 1 + clock = 1 + tombstoneHistoryStartsAtClock = this.clock + // map from record id to clock upon deletion + + readonly serializedSchema: SerializedSchema + + readonly documentTypes: Set + readonly presenceType: RecordType + + constructor( + public readonly schema: StoreSchema, + snapshot?: RoomSnapshot + ) { + // do a json serialization cycle to make sure the schema has no 'undefined' values + this.serializedSchema = JSON.parse(JSON.stringify(schema.serialize())) + + this.documentTypes = new Set( + Object.values>(schema.types) + .filter((t) => t.scope === 'document') + .map((t) => t.typeName) + ) + + const presenceTypes = new Set( + Object.values>(schema.types).filter((t) => t.scope === 'presence') + ) + + if (presenceTypes.size != 1) { + throw new Error( + `TLSyncRoom: exactly one presence type is expected, but found ${presenceTypes.size}` + ) + } + + this.presenceType = presenceTypes.values().next().value + + if (!snapshot) { + snapshot = { + clock: 0, + documents: [ + { + state: DocumentRecordType.create({ id: TLDOCUMENT_ID }), + lastChangedClock: 0, + }, + { + state: PageRecordType.create({ name: 'Page 1', index: 'a1' }), + lastChangedClock: 0, + }, + ], + } + } + + this.clock = snapshot.clock + let didIncrementClock = false + const ensureClockDidIncrement = (_reason: string) => { + if (!didIncrementClock) { + didIncrementClock = true + this.clock++ + } + } + + const tombstones = { ...snapshot.tombstones } + const filteredDocuments = [] + for (const doc of snapshot.documents) { + if (this.documentTypes.has(doc.state.typeName)) { + filteredDocuments.push(doc) + } else { + ensureClockDidIncrement('doc type was not doc type') + tombstones[doc.state.id] = this.clock + } + } + + const documents: Record> = Object.fromEntries( + filteredDocuments.map((r) => [ + r.state.id, + DocumentState.createWithoutValidating( + r.state as R, + r.lastChangedClock, + assertExists(getOwnProperty(schema.types, r.state.typeName)) + ), + ]) + ) + + const migrationResult = schema.migrateStoreSnapshot({ + store: Object.fromEntries( + objectMapEntries(documents).map(([id, { state }]) => [id, state as R]) + ) as Record, R>, + schema: snapshot.schema ?? schema.serializeEarliestVersion(), + }) + + if (migrationResult.type === 'error') { + // TODO: Fault tolerance + throw new Error('Failed to migrate: ' + migrationResult.reason) + } + + for (const [id, r] of objectMapEntries(migrationResult.value)) { + const existing = documents[id] + if (!existing) { + // record was added during migration + ensureClockDidIncrement('record was added during migration') + documents[id] = DocumentState.createWithoutValidating( + r, + this.clock, + assertExists(getOwnProperty(schema.types, r.typeName)) as any + ) + } else if (!isEqual(existing.state, r)) { + // record was maybe updated during migration + ensureClockDidIncrement('record was maybe updated during migration') + existing.replaceState(r, this.clock) + } + } + + for (const id of objectMapKeys(documents)) { + if (!migrationResult.value[id as keyof typeof migrationResult.value]) { + // record was removed during migration + ensureClockDidIncrement('record was removed during migration') + tombstones[id] = this.clock + delete documents[id] + } + } + + this.state.set({ documents, tombstones }) + + this.pruneTombstones() + } + + private pruneTombstones = () => { + // avoid blocking any pending responses + this.state.update(({ tombstones, documents }) => { + const entries = Object.entries(this.state.get().tombstones) + if (entries.length > MAX_TOMBSTONES) { + // sort entries in ascending order by clock + entries.sort((a, b) => a[1] - b[1]) + // trim off the first bunch + const excessQuantity = entries.length - MAX_TOMBSTONES + tombstones = Object.fromEntries(entries.slice(excessQuantity + TOMBSTONE_PRUNE_BUFFER_SIZE)) + } + return { + documents, + tombstones, + } + }) + } + + private getDocument(id: string) { + return this.state.get().documents[id] + } + + private addDocument(id: string, state: R, clock: number): Result { + let { documents, tombstones } = this.state.get() + if (hasOwnProperty(tombstones, id)) { + tombstones = { ...tombstones } + delete tombstones[id] + } + const createResult = DocumentState.createAndValidate( + state, + clock, + assertExists(getOwnProperty(this.schema.types, state.typeName)) + ) + if (!createResult.ok) return createResult + documents = { ...documents, [id]: createResult.value } + this.state.set({ documents, tombstones }) + return Result.ok(undefined) + } + + private removeDocument(id: string, clock: number) { + this.state.update(({ documents, tombstones }) => { + documents = { ...documents } + delete documents[id] + tombstones = { ...tombstones, [id]: clock } + return { documents, tombstones } + }) + } + + getSnapshot(): RoomSnapshot { + const { documents, tombstones } = this.state.get() + return { + clock: this.clock, + tombstones, + schema: this.serializedSchema, + documents: Object.values(documents) + .map((doc) => ({ + state: doc.state, + lastChangedClock: doc.lastChangedClock, + })) + .filter((d) => this.documentTypes.has(d.state.typeName)), + } + } + + /** + * Send a message to a particular client. + * + * @param client - The client to send the message to. + * @param message - The message to send. + */ + private sendMessage(sessionKey: string, message: TLSocketServerSentEvent) { + const session = this.sessions.get(sessionKey) + if (!session) { + console.warn('Tried to send message to unknown session', message.type) + return + } + if (session.state !== RoomSessionState.CONNECTED) { + console.warn('Tried to send message to disconnected client', message.type) + return + } + if (session.socket.isOpen) { + session.socket.sendMessage(message) + } else { + this.cancelSession(session.sessionKey) + } + } + + private removeSession(sessionKey: string) { + const session = this.sessions.get(sessionKey) + if (!session) { + console.warn('Tried to remove unknown session') + return + } + + this.sessions.delete(sessionKey) + + const presence = this.getDocument(session.presenceId) + + try { + if (session.socket.isOpen) { + session.socket.close() + } + } catch (_e) { + // noop + } + + if (presence) { + this.state.update(({ tombstones, documents }) => { + documents = { ...documents } + delete documents[session.presenceId] + return { documents, tombstones } + }) + + this.broadcastPatch({ + diff: { [session.presenceId]: [RecordOpType.Remove] }, + sourceSessionKey: sessionKey, + }) + } + + this.events.emit('session_removed', { sessionKey }) + if (this.sessions.size === 0) { + this.events.emit('room_became_empty') + } + } + + private cancelSession(sessionKey: string) { + const session = this.sessions.get(sessionKey) + if (!session) { + return + } + + if (session.state === RoomSessionState.AWAITING_REMOVAL) { + console.warn('Tried to cancel session that is already awaiting removal') + return + } + + this.sessions.set(sessionKey, { + state: RoomSessionState.AWAITING_REMOVAL, + sessionKey, + presenceId: session.presenceId, + socket: session.socket, + cancellationTime: Date.now(), + }) + } + + /** + * Broadcast a message to all connected clients except the clientId provided. + * + * @param message - The message to broadcast. + * @param clientId - The client to exclude. + */ + broadcastPatch({ + diff, + sourceSessionKey: sourceSessionKey, + }: { + diff: NetworkDiff + sourceSessionKey: string + }) { + this.sessions.forEach((session) => { + if (session.state !== RoomSessionState.CONNECTED) return + if (sourceSessionKey === session.sessionKey) return + if (!session.socket.isOpen) { + this.cancelSession(session.sessionKey) + return + } + + const res = this.migrateDiffForSession(session.serializedSchema, diff) + + if (!res.ok) { + // disconnect client and send incompatibility error + this.rejectSession( + session, + res.error === MigrationFailureReason.TargetVersionTooNew + ? TLIncompatibilityReason.ServerTooOld + : TLIncompatibilityReason.ClientTooOld + ) + return + } + + this.sendMessage(session.sessionKey, { + type: 'patch', + diff: res.value, + serverClock: this.clock, + }) + }) + return this + } + + /** + * When a client connects to the room, add them to the list of clients and then merge the history + * down into the snapshots. + * + * @param client - The client that connected to the room. + */ + handleNewSession = (sessionKey: string, socket: TLRoomSocket) => { + const existing = this.sessions.get(sessionKey) + this.sessions.set(sessionKey, { + state: RoomSessionState.AWAITING_CONNECT_MESSAGE, + sessionKey, + socket, + presenceId: existing?.presenceId ?? this.presenceType.createId(), + sessionStartTime: Date.now(), + }) + return this + } + + /** + * When we send a diff to a client, if that client is on a lower version than us, we need to make + * the diff compatible with their version. At the moment this means migrating each affected record + * to the client's version and sending the whole record again. We can optimize this later by + * keeping the previous versions of records around long enough to recalculate these diffs for + * older client versions. + */ + private migrateDiffForSession( + serializedSchema: SerializedSchema, + diff: NetworkDiff + ): Result, MigrationFailureReason> { + // TODO: optimize this by recalculating patches using the previous versions of records + + // when the client connects we check whether the schema is identical and make sure + // to use the same object reference so that === works on this line + if (serializedSchema === this.serializedSchema) { + return Result.ok(diff) + } + + const result: NetworkDiff = {} + for (const [id, op] of Object.entries(diff)) { + if (op[0] === RecordOpType.Remove) { + result[id] = op + continue + } + + const migrationResult = this.schema.migratePersistedRecord( + this.getDocument(id).state, + serializedSchema, + 'down' + ) + + if (migrationResult.type === 'error') { + return Result.err(migrationResult.reason) + } + + result[id] = [RecordOpType.Put, migrationResult.value] + } + + return Result.ok(result) + } + + /** + * When the server receives a message from the clients Currently supports connect and patches. + * Invalid messages types log a warning. Currently doesn't validate data. + * + * @param client - The client that sent the message + * @param message - The message that was sent + */ + handleMessage = async (sessionKey: string, message: TLSocketClientSentEvent) => { + const session = this.sessions.get(sessionKey) + if (!session) { + console.warn('Received message from unknown session') + return + } + switch (message.type) { + case 'connect': { + return this.handleConnectRequest(session, message) + } + case 'push': { + return this.handlePushRequest(session, message) + } + case 'ping': { + if (session.state === RoomSessionState.CONNECTED) { + session.lastInteractionTime = Date.now() + } + return this.sendMessage(session.sessionKey, { type: 'pong' }) + } + default: { + exhaustiveSwitchError(message) + } + } + + return this + } + + /** If the client is out of date or we are out of date, we need to let them know */ + private rejectSession(session: RoomSession, reason: TLIncompatibilityReason) { + try { + if (session.socket.isOpen) { + session.socket.sendMessage({ + type: 'incompatibility_error', + reason, + }) + } + } catch (e) { + // noop + } finally { + this.removeSession(session.sessionKey) + } + } + + private handleConnectRequest( + session: RoomSession, + message: Extract, { type: 'connect' }> + ) { + // if the protocol versions don't match, disconnect the client + // we will eventually want to try to make our protocol backwards compatible to some degree + // and have a MIN_PROTOCOL_VERSION constant that the TLSyncRoom implements support for + if (message.protocolVersion == null || message.protocolVersion < TLSYNC_PROTOCOL_VERSION) { + this.rejectSession(session, TLIncompatibilityReason.ClientTooOld) + return + } else if (message.protocolVersion > TLSYNC_PROTOCOL_VERSION) { + this.rejectSession(session, TLIncompatibilityReason.ServerTooOld) + return + } + // If the client's store is at a different version to ours, it could cause corruption. + // We should disconnect the client and ask them to refresh. + if (message.schema == null || message.schema.storeVersion < this.schema.currentStoreVersion) { + this.rejectSession(session, TLIncompatibilityReason.ClientTooOld) + return + } else if (message.schema.storeVersion > this.schema.currentStoreVersion) { + this.rejectSession(session, TLIncompatibilityReason.ServerTooOld) + return + } + + const sessionSchema = isEqual(message.schema, this.serializedSchema) + ? this.serializedSchema + : message.schema + + const connect = (msg: TLSocketServerSentEvent) => { + this.sessions.set(session.sessionKey, { + state: RoomSessionState.CONNECTED, + sessionKey: session.sessionKey, + presenceId: session.presenceId, + socket: session.socket, + serializedSchema: sessionSchema, + lastInteractionTime: Date.now(), + }) + this.sendMessage(session.sessionKey, msg) + } + + transaction((rollback) => { + if ( + // if the client requests changes since a time before we have tombstone history, send them the full state + message.lastServerClock < this.tombstoneHistoryStartsAtClock || + // similarly, if they ask for a time we haven't reached yet, send them the full state + // this will only happen if the DB is reset (or there is no db) and the server restarts + // or if the server exits/crashes with unpersisted changes + message.lastServerClock > this.clock + ) { + const diff: NetworkDiff = {} + for (const [id, doc] of Object.entries(this.state.get().documents)) { + if (id !== session.presenceId) { + diff[id] = [RecordOpType.Put, doc.state] + } + } + const migrated = this.migrateDiffForSession(sessionSchema, diff) + if (!migrated.ok) { + rollback() + this.rejectSession( + session, + migrated.error === MigrationFailureReason.TargetVersionTooNew + ? TLIncompatibilityReason.ServerTooOld + : TLIncompatibilityReason.ClientTooOld + ) + return + } + connect({ + type: 'connect', + connectRequestId: message.connectRequestId, + hydrationType: 'wipe_all', + protocolVersion: TLSYNC_PROTOCOL_VERSION, + schema: this.schema.serialize(), + serverClock: this.clock, + diff: migrated.value, + }) + } else { + // calculate the changes since the time the client last saw + const diff: NetworkDiff = {} + const updatedDocs = Object.values(this.state.get().documents).filter( + (doc) => doc.lastChangedClock > message.lastServerClock + ) + const presenceDocs = Object.values(this.state.get().documents).filter( + (doc) => + this.presenceType.typeName === doc.state.typeName && doc.state.id !== session.presenceId + ) + const deletedDocsIds = Object.entries(this.state.get().tombstones) + .filter(([_id, deletedAtClock]) => deletedAtClock > message.lastServerClock) + .map(([id]) => id) + + for (const doc of updatedDocs) { + diff[doc.state.id] = [RecordOpType.Put, doc.state] + } + for (const doc of presenceDocs) { + diff[doc.state.id] = [RecordOpType.Put, doc.state] + } + + for (const docId of deletedDocsIds) { + diff[docId] = [RecordOpType.Remove] + } + const migrated = this.migrateDiffForSession(sessionSchema, diff) + if (!migrated.ok) { + rollback() + this.rejectSession( + session, + migrated.error === MigrationFailureReason.TargetVersionTooNew + ? TLIncompatibilityReason.ServerTooOld + : TLIncompatibilityReason.ClientTooOld + ) + return + } + + connect({ + type: 'connect', + connectRequestId: message.connectRequestId, + hydrationType: 'wipe_presence', + schema: this.schema.serialize(), + protocolVersion: TLSYNC_PROTOCOL_VERSION, + serverClock: this.clock, + diff: migrated.value, + }) + } + }) + } + + private handlePushRequest( + session: RoomSession, + message: Extract, { type: 'push' }> + ) { + const isPresencePush = 'presence' in message + const clientClock = message.clientClock + + if (session.state !== RoomSessionState.CONNECTED) { + return + } + session.lastInteractionTime = Date.now() + + // increment the clock for this push + this.clock++ + + transaction((rollback) => { + // collect actual ops that resulted from the push + // these will be broadcast to other users + let mergedChanges: NetworkDiff | null = null + const propagateOp = (id: string, op: RecordOp) => { + if (!mergedChanges) mergedChanges = {} + mergedChanges[id] = op + } + + const fail = (reason: TLIncompatibilityReason): Result => { + rollback() + this.rejectSession(session, reason) + if (typeof process !== 'undefined' && process.env.NODE_ENV !== 'test') { + console.error('failed to apply push', reason, message) + } + return Result.err(undefined) + } + + const addDocument = (id: string, _state: R): Result => { + const res = this.schema.migratePersistedRecord(_state, session.serializedSchema, 'up') + if (res.type === 'error') { + return fail( + res.reason === MigrationFailureReason.TargetVersionTooOld // target version is our version + ? TLIncompatibilityReason.ServerTooOld + : TLIncompatibilityReason.ClientTooOld + ) + } + const state = res.value + const doc = this.getDocument(id) + if (doc) { + // if we already have a document with this id, set it to the new value + // but propagate a diff rather than the entire value + const diff = doc.replaceState(state, this.clock) + if (!diff.ok) { + return fail(TLIncompatibilityReason.InvalidRecord) + } + if (diff.value) propagateOp(id, [RecordOpType.Patch, diff.value]) + } else { + // if we don't already have a document with this id, create it and propagate the put op + const result = this.addDocument(id, state, this.clock) + if (!result.ok) { + return fail(TLIncompatibilityReason.InvalidRecord) + } + propagateOp(id, [RecordOpType.Put, state]) + } + + return Result.ok(undefined) + } + + const patchDocument = (id: string, patch: ObjectDiff): Result => { + // if it was already deleted, there's no need to apply the patch + const doc = this.getDocument(id) + if (!doc) return Result.ok(undefined) + const theirVersion = getRecordVersion(doc.state, session.serializedSchema) + const ourVersion = getRecordVersion(doc.state, this.serializedSchema) + if (compareRecordVersions(ourVersion, theirVersion) === 1) { + // if the client's version of the record is older than ours, we apply the patch to the downgraded version of the record + const downgraded = this.schema.migratePersistedRecord( + doc.state, + session.serializedSchema, + 'down' + ) + if (downgraded.type === 'error') { + return fail(TLIncompatibilityReason.ClientTooOld) + } + const patched = applyObjectDiff(downgraded.value, patch) + // then upgrade the patched version and use that as the new state + const upgraded = this.schema.migratePersistedRecord( + patched, + session.serializedSchema, + 'up' + ) + if (upgraded.type === 'error') { + return fail(TLIncompatibilityReason.ClientTooOld) + } + const diff = doc.replaceState(upgraded.value, this.clock) + if (!diff.ok) { + return fail(TLIncompatibilityReason.InvalidRecord) + } + if (diff.value) propagateOp(id, [RecordOpType.Patch, diff.value]) + } else if (compareRecordVersions(ourVersion, theirVersion) === -1) { + // if the client's version of the record is newer than ours, we can't apply the patch + return fail(TLIncompatibilityReason.ServerTooOld) + } else { + // otherwise apply the patch and propagate the patch op if needed + const diff = doc.mergeDiff(patch, this.clock) + if (!diff.ok) { + return fail(TLIncompatibilityReason.InvalidRecord) + } + if (diff.value) propagateOp(id, [RecordOpType.Patch, diff.value]) + } + + return Result.ok(undefined) + } + + if (isPresencePush) { + const id = session.presenceId + const [type, val] = message.presence + if (type === RecordOpType.Put) { + if (!addDocument(id, { ...val, id, typeName: this.presenceType.typeName }).ok) return + } else { + if ( + !patchDocument(id, { + ...val, + id: [ValueOpType.Put, id], + typeName: [ValueOpType.Put, this.presenceType.typeName], + }).ok + ) + return + } + this.sendMessage(session.sessionKey, { + type: 'push_result', + clientClock, + action: 'commit', + serverClock: this.clock, + }) + } else { + const diff = message.diff + for (const [id, op] of Object.entries(diff)) { + if (op[0] === RecordOpType.Put) { + // if it's not a document record, fail + if (!this.documentTypes.has(op[1].typeName)) { + return fail(TLIncompatibilityReason.InvalidRecord) + } + if (!addDocument(id, op[1]).ok) return + } else if (op[0] === RecordOpType.Remove) { + // if it was already deleted, don't do anything, no need to propagate a delete op + const doc = this.getDocument(id) + if (!doc) continue + if (!this.documentTypes.has(doc.state.typeName)) { + return fail(TLIncompatibilityReason.InvalidOperation) + } + // otherwise delete the document and propagate the delete op + this.removeDocument(id, this.clock) + // schedule a pruneTombstones call to happen after we are done here + setTimeout(this.pruneTombstones, 0) + propagateOp(id, op) + } else if (op[0] === RecordOpType.Patch) { + if (!patchDocument(id, op[1]).ok) return + } + } + + if (!mergedChanges) { + // we applied the client's changes but they had no effect + // tell them to drop the diff + this.sendMessage(session.sessionKey, { + type: 'push_result', + serverClock: this.clock, + clientClock, + action: 'discard', + }) + } else if (isEqual(mergedChanges, diff)) { + // we applied the client's changes and they had the exact same effect + // on the server as they did on the client + // tell them to keep the diff + this.sendMessage(session.sessionKey, { + type: 'push_result', + serverClock: this.clock, + clientClock, + action: 'commit', + }) + } else { + // We applied the client's changes and they had a different non-empty effect + // on the server, so we need to tell the client to rebase with our gold standard diff + const migrateResult = this.migrateDiffForSession(session.serializedSchema, mergedChanges) + if (!migrateResult.ok) { + return fail( + migrateResult.error === MigrationFailureReason.TargetVersionTooNew + ? TLIncompatibilityReason.ServerTooOld + : TLIncompatibilityReason.ClientTooOld + ) + } + this.sendMessage(session.sessionKey, { + type: 'push_result', + serverClock: this.clock, + clientClock, + action: { rebaseWithDiff: migrateResult.value }, + }) + } + } + + if (mergedChanges) { + // let all other client know about the changes + this.broadcastPatch({ + sourceSessionKey: session.sessionKey, + diff: mergedChanges, + }) + } + + return + }) + } + + /** + * Handle the event when a client disconnects. + * + * @param client - The client that disconnected. + */ + handleClose = (sessionKey: string) => { + this.cancelSession(sessionKey) + } +} diff --git a/packages/tlsync/src/lib/chunk.ts b/packages/tlsync/src/lib/chunk.ts new file mode 100644 index 000000000..831d2963c --- /dev/null +++ b/packages/tlsync/src/lib/chunk.ts @@ -0,0 +1,78 @@ +// quarter of a megabyte, max possible utf-8 string size + +// cloudflare workers only accept messages of max 1mb +const MAX_CLIENT_SENT_MESSAGE_SIZE_BYTES = 1024 * 1024 +// utf-8 is max 4 bytes per char +const MAX_BYTES_PER_CHAR = 4 + +// in the (admittedly impossible) worst case, the max size is 1/4 of a megabyte +const MAX_SAFE_MESSAGE_SIZE = MAX_CLIENT_SENT_MESSAGE_SIZE_BYTES / MAX_BYTES_PER_CHAR + +export function chunk(msg: string, maxSafeMessageSize = MAX_SAFE_MESSAGE_SIZE) { + if (msg.length < maxSafeMessageSize) { + return [msg] + } else { + const chunks = [] + let chunkNumber = 0 + let offset = msg.length + while (offset > 0) { + const prefix = `${chunkNumber}_` + const chunkSize = Math.max(Math.min(maxSafeMessageSize - prefix.length, offset), 1) + chunks.unshift(prefix + msg.slice(offset - chunkSize, offset)) + offset -= chunkSize + chunkNumber++ + } + return chunks + } +} + +const chunkRe = /^(\d+)_(.*)$/ + +export class JsonChunkAssembler { + state: + | 'idle' + | { + chunksReceived: string[] + totalChunks: number + } = 'idle' + + handleMessage(msg: string): { data?: object; error?: Error } | null { + if (msg.startsWith('{')) { + const error = this.state === 'idle' ? undefined : new Error('Unexpected non-chunk message') + this.state = 'idle' + return { data: JSON.parse(msg), error } + } else { + const match = chunkRe.exec(msg)! + if (!match) { + this.state = 'idle' + return { error: new Error('Invalid chunk: ' + JSON.stringify(msg.slice(0, 20) + '...')) } + } + const numChunksRemaining = Number(match[1]) + const data = match[2] + + if (this.state === 'idle') { + this.state = { + chunksReceived: [data], + totalChunks: numChunksRemaining + 1, + } + } else { + this.state.chunksReceived.push(data) + if (numChunksRemaining !== this.state.totalChunks - this.state.chunksReceived.length) { + this.state = 'idle' + return { error: new Error(`Chunks received in wrong order`) } + } + } + if (this.state.chunksReceived.length === this.state.totalChunks) { + try { + const data = JSON.parse(this.state.chunksReceived.join('')) + return { data } + } catch (e) { + return { error: e as Error } + } finally { + this.state = 'idle' + } + } + return null + } + } +} diff --git a/packages/tlsync/src/lib/diff.ts b/packages/tlsync/src/lib/diff.ts new file mode 100644 index 000000000..4867ad51c --- /dev/null +++ b/packages/tlsync/src/lib/diff.ts @@ -0,0 +1,260 @@ +import { RecordsDiff, UnknownRecord } from '@tldraw/store' +import { objectMapEntries, objectMapValues } from '@tldraw/utils' +import isEqual from 'lodash.isequal' + +/** @public */ +export enum RecordOpType { + Put = 'put', + Patch = 'patch', + Remove = 'remove', +} + +/** @public */ +export type RecordOp = + | [RecordOpType.Put, R] + | [RecordOpType.Patch, ObjectDiff] + | [RecordOpType.Remove] + +/** + * A one-way (non-reversible) diff designed for small json footprint. These are mainly intended to + * be sent over the wire. Either as push requests from the client to the server, or as patch + * operations in the opposite direction. + * + * Each key in this object is the id of a record that has been added, updated, or removed. + * + * @public + */ +export type NetworkDiff = { + [id: string]: RecordOp +} + +/** + * Converts a (reversible, verbose) RecordsDiff into a (non-reversible, concise) NetworkDiff + * + * @public + */ +export const getNetworkDiff = ( + diff: RecordsDiff +): NetworkDiff | null => { + let res: NetworkDiff | null = null + + for (const [k, v] of objectMapEntries(diff.added)) { + if (!res) res = {} + res[k] = [RecordOpType.Put, v] + } + + for (const [from, to] of objectMapValues(diff.updated)) { + const diff = diffRecord(from, to) + if (diff) { + if (!res) res = {} + res[to.id] = [RecordOpType.Patch, diff] + } + } + + for (const removed of Object.keys(diff.removed)) { + if (!res) res = {} + res[removed] = [RecordOpType.Remove] + } + + return res +} + +/** @public */ +export enum ValueOpType { + Put = 'put', + Delete = 'delete', + Append = 'append', + Patch = 'patch', +} +/** @public */ +export type PutOp = [type: ValueOpType.Put, value: unknown] +/** @public */ +export type AppendOp = [type: ValueOpType.Append, values: unknown[], offset: number] +/** @public */ +export type PatchOp = [type: ValueOpType.Patch, diff: ObjectDiff] +/** @public */ +export type DeleteOp = [type: ValueOpType.Delete] + +/** @public */ +export type ValueOp = PutOp | AppendOp | PatchOp | DeleteOp + +/** @public */ +export type ObjectDiff = { + [k: string]: ValueOp +} + +/** @public */ +export function diffRecord(prev: object, next: object): ObjectDiff | null { + return diffObject(prev, next, new Set(['props'])) +} + +function diffObject(prev: object, next: object, nestedKeys?: Set): ObjectDiff | null { + if (prev === next) { + return null + } + let result: ObjectDiff | null = null + for (const key of Object.keys(prev)) { + // if key is not in next then it was deleted + if (!(key in next)) { + if (!result) result = {} + result[key] = [ValueOpType.Delete] + continue + } + // if key is in both places, then compare values + const prevVal = (prev as any)[key] + const nextVal = (next as any)[key] + if (!isEqual(prevVal, nextVal)) { + if (nestedKeys?.has(key) && prevVal && nextVal) { + const diff = diffObject(prevVal, nextVal) + if (diff) { + if (!result) result = {} + result[key] = [ValueOpType.Patch, diff] + } + } else if (Array.isArray(nextVal) && Array.isArray(prevVal)) { + const op = diffArray(prevVal, nextVal) + if (op) { + if (!result) result = {} + result[key] = op + } + } else { + if (!result) result = {} + result[key] = [ValueOpType.Put, nextVal] + } + } + } + for (const key of Object.keys(next)) { + // if key is in next but not in prev then it was added + if (!(key in prev)) { + if (!result) result = {} + result[key] = [ValueOpType.Put, (next as any)[key]] + } + } + return result +} + +function diffValue(valueA: unknown, valueB: unknown): ValueOp | null { + if (Object.is(valueA, valueB)) return null + if (Array.isArray(valueA) && Array.isArray(valueB)) { + return diffArray(valueA, valueB) + } else if (!valueA || !valueB || typeof valueA !== 'object' || typeof valueB !== 'object') { + return isEqual(valueA, valueB) ? null : [ValueOpType.Put, valueB] + } else { + const diff = diffObject(valueA, valueB) + return diff ? [ValueOpType.Patch, diff] : null + } +} + +function diffArray(prevArray: unknown[], nextArray: unknown[]): PutOp | AppendOp | PatchOp | null { + if (Object.is(prevArray, nextArray)) return null + // if lengths are equal, check for patch operation + if (prevArray.length === nextArray.length) { + // bail out if more than len/5 items need patching + const maxPatchIndexes = Math.max(prevArray.length / 5, 1) + const toPatchIndexes = [] + for (let i = 0; i < prevArray.length; i++) { + if (!isEqual(prevArray[i], nextArray[i])) { + toPatchIndexes.push(i) + if (toPatchIndexes.length > maxPatchIndexes) { + return [ValueOpType.Put, nextArray] + } + } + } + if (toPatchIndexes.length === 0) { + // same length and no items changed, so no diff + return null + } + const diff: ObjectDiff = {} + for (const i of toPatchIndexes) { + const prevItem = prevArray[i] + const nextItem = nextArray[i] + if (!prevItem || !nextItem) { + diff[i] = [ValueOpType.Put, nextItem] + } else if (typeof prevItem === 'object' && typeof nextItem === 'object') { + const op = diffValue(prevItem, nextItem) + if (op) { + diff[i] = op + } + } else { + diff[i] = [ValueOpType.Put, nextItem] + } + } + return [ValueOpType.Patch, diff] + } + + // if lengths are not equal, check for append operation, and bail out + // to replace whole array if any shared elems changed + for (let i = 0; i < prevArray.length; i++) { + if (!isEqual(prevArray[i], nextArray[i])) { + return [ValueOpType.Put, nextArray] + } + } + + return [ValueOpType.Append, nextArray.slice(prevArray.length), prevArray.length] +} + +/** @public */ +export function applyObjectDiff(object: T, objectDiff: ObjectDiff): T { + // don't patch nulls + if (!object || typeof object !== 'object') return object + const isArray = Array.isArray(object) + let newObject: any | undefined = undefined + const set = (k: any, v: any) => { + if (!newObject) { + if (isArray) { + newObject = [...object] + } else { + newObject = { ...object } + } + } + if (isArray) { + newObject[Number(k)] = v + } else { + newObject[k] = v + } + } + for (const [key, op] of Object.entries(objectDiff)) { + switch (op[0]) { + case ValueOpType.Put: { + const value = op[1] + if (!isEqual(object[key as keyof T], value)) { + set(key, value) + } + break + } + case ValueOpType.Append: { + const values = op[1] + const offset = op[2] + const arr = object[key as keyof T] + if (Array.isArray(arr) && arr.length === offset) { + set(key, [...arr, ...values]) + } + break + } + case ValueOpType.Patch: { + if (object[key as keyof T] && typeof object[key as keyof T] === 'object') { + const diff = op[1] + const patched = applyObjectDiff(object[key as keyof T] as object, diff) + if (patched !== object[key as keyof T]) { + set(key, patched) + } + } + break + } + case ValueOpType.Delete: { + if (key in object) { + if (!newObject) { + if (isArray) { + console.error("Can't delete array item yet (this should never happen)") + newObject = [...object] + } else { + newObject = { ...object } + } + } + delete newObject[key] + } + } + } + } + + return newObject ?? object +} diff --git a/packages/tlsync/src/lib/interval.ts b/packages/tlsync/src/lib/interval.ts new file mode 100644 index 000000000..2b38ec640 --- /dev/null +++ b/packages/tlsync/src/lib/interval.ts @@ -0,0 +1,4 @@ +export function interval(cb: () => void, timeout: number) { + const i = setInterval(cb, timeout) + return () => clearInterval(i) +} diff --git a/packages/tlsync/src/lib/protocol.ts b/packages/tlsync/src/lib/protocol.ts new file mode 100644 index 000000000..e1c7a4a5c --- /dev/null +++ b/packages/tlsync/src/lib/protocol.ts @@ -0,0 +1,80 @@ +import { SerializedSchema, UnknownRecord } from '@tldraw/store' +import { NetworkDiff, ObjectDiff, RecordOpType } from './diff' + +/** @public */ +export const TLSYNC_PROTOCOL_VERSION = 4 + +/** @public */ +export enum TLIncompatibilityReason { + ClientTooOld = 'clientTooOld', + ServerTooOld = 'serverTooOld', + InvalidRecord = 'invalidRecord', + InvalidOperation = 'invalidOperation', +} + +/** @public */ +export type TLSocketServerSentEvent = + | { + type: 'connect' + hydrationType: 'wipe_all' | 'wipe_presence' + connectRequestId: string + protocolVersion: number + schema: SerializedSchema + diff: NetworkDiff + serverClock: number + } + | { + type: 'incompatibility_error' + reason: TLIncompatibilityReason + } + | { + type: 'patch' + diff: NetworkDiff + serverClock: number + } + | { + type: 'error' + error?: any + } + | { + type: 'push_result' + clientClock: number + serverClock: number + action: 'discard' | 'commit' | { rebaseWithDiff: NetworkDiff } + } + | { + type: 'pong' + } + +/** @public */ +export type TLPushRequest = + | { + type: 'push' + clientClock: number + presence: [RecordOpType.Patch, ObjectDiff] | [RecordOpType.Put, R] + } + | { + type: 'push' + clientClock: number + diff: NetworkDiff + } + +/** @public */ +export type TLConnectRequest = { + type: 'connect' + connectRequestId: string + lastServerClock: number + protocolVersion: number + schema: SerializedSchema +} + +/** @public */ +export type TLPingRequest = { + type: 'ping' +} + +/** @public */ +export type TLSocketClientSentEvent = + | TLPushRequest + | TLConnectRequest + | TLPingRequest diff --git a/packages/tlsync/src/lib/requestAnimationFrame.polyfill.ts b/packages/tlsync/src/lib/requestAnimationFrame.polyfill.ts new file mode 100644 index 000000000..e2340d517 --- /dev/null +++ b/packages/tlsync/src/lib/requestAnimationFrame.polyfill.ts @@ -0,0 +1,7 @@ +globalThis.requestAnimationFrame = + globalThis.requestAnimationFrame || + function requestAnimationFrame(cb) { + return setTimeout(cb, 1000 / 60) + } + +export {} diff --git a/packages/tlsync/src/lib/schema.ts b/packages/tlsync/src/lib/schema.ts new file mode 100644 index 000000000..3dd462c84 --- /dev/null +++ b/packages/tlsync/src/lib/schema.ts @@ -0,0 +1,86 @@ +import { + arrowShapeMigrations, + arrowShapeProps, + bookmarkShapeMigrations, + bookmarkShapeProps, + createTLSchema, + drawShapeMigrations, + drawShapeProps, + embedShapeMigrations, + embedShapeProps, + frameShapeMigrations, + frameShapeProps, + geoShapeMigrations, + geoShapeProps, + groupShapeMigrations, + groupShapeProps, + highlightShapeMigrations, + highlightShapeProps, + imageShapeMigrations, + imageShapeProps, + lineShapeMigrations, + lineShapeProps, + noteShapeMigrations, + noteShapeProps, + textShapeMigrations, + textShapeProps, + videoShapeMigrations, + videoShapeProps, +} from '@tldraw/tlschema' + +export const schema = createTLSchema({ + shapes: { + group: { + props: groupShapeProps, + migrations: groupShapeMigrations, + }, + text: { + props: textShapeProps, + migrations: textShapeMigrations, + }, + bookmark: { + props: bookmarkShapeProps, + migrations: bookmarkShapeMigrations, + }, + draw: { + props: drawShapeProps, + migrations: drawShapeMigrations, + }, + geo: { + props: geoShapeProps, + migrations: geoShapeMigrations, + }, + note: { + props: noteShapeProps, + migrations: noteShapeMigrations, + }, + line: { + props: lineShapeProps, + migrations: lineShapeMigrations, + }, + frame: { + props: frameShapeProps, + migrations: frameShapeMigrations, + }, + arrow: { + props: arrowShapeProps, + migrations: arrowShapeMigrations, + }, + highlight: { + props: highlightShapeProps, + migrations: highlightShapeMigrations, + }, + embed: { + props: embedShapeProps, + migrations: embedShapeMigrations, + }, + image: { + props: imageShapeProps, + migrations: imageShapeMigrations, + }, + video: { + props: videoShapeProps, + migrations: videoShapeMigrations, + }, + }, +}) diff --git a/packages/tlsync/src/lib/serializeMessage.ts b/packages/tlsync/src/lib/serializeMessage.ts new file mode 100644 index 000000000..cc3ff11bc --- /dev/null +++ b/packages/tlsync/src/lib/serializeMessage.ts @@ -0,0 +1,22 @@ +import { TLSocketClientSentEvent, TLSocketServerSentEvent } from './protocol' + +type Message = TLSocketServerSentEvent | TLSocketClientSentEvent + +let _lastSentMessage: Message | null = null +let _lastSentMessageSerialized: string | null = null + +/** + * Serializes a message to a string. Caches the last serialized message to optimize for cases where + * the same message is broadcast to multiple places. + * + * @public + */ +export function serializeMessage(message: Message) { + if (message === _lastSentMessage) { + return _lastSentMessageSerialized as string + } else { + _lastSentMessage = message + _lastSentMessageSerialized = JSON.stringify(message) + return _lastSentMessageSerialized + } +} diff --git a/packages/tlsync/src/lib/server-types.ts b/packages/tlsync/src/lib/server-types.ts new file mode 100644 index 000000000..511ebf66c --- /dev/null +++ b/packages/tlsync/src/lib/server-types.ts @@ -0,0 +1,12 @@ +import { RoomSnapshot, TLSyncRoom } from './TLSyncRoom' + +/** @public */ +export type RoomState = { + // the slug of the room + persistenceKey: string + // the room + room: TLSyncRoom +} + +/** @public */ +export type PersistedRoomSnapshotForSupabase = { id: string; slug: string; drawing: RoomSnapshot } diff --git a/packages/tlsync/src/test/FuzzEditor.ts b/packages/tlsync/src/test/FuzzEditor.ts new file mode 100644 index 000000000..55f7dc418 --- /dev/null +++ b/packages/tlsync/src/test/FuzzEditor.ts @@ -0,0 +1,394 @@ +import { + Editor, + PageRecordType, + TLArrowShapeTerminal, + TLPage, + TLPageId, + TLShape, + TLShapeId, + TLStore, + createShapeId, + defaultShapeUtils, + defaultTools, +} from '@tldraw/tldraw' +import { RandomSource } from './RandomSource' + +export type Op = + | { + type: 'create-box' + parentId?: TLShapeId + x: number + y: number + width: number + height: number + } + | { + type: 'create-frame' + x: number + y: number + width: number + height: number + } + | { + type: 'group-selection' + } + | { + type: 'ungroup-selection' + } + | { + type: 'create-arrow' + start: TLArrowShapeTerminal + end: TLArrowShapeTerminal + } + | { + type: 'delete-shape' + id: TLShapeId + } + | { + type: 'create-page' + id: TLPageId + } + | { + type: 'delete-page' + id: TLPageId + } + | { + type: 'undo' + } + | { + type: 'redo' + } + | { + type: 'switch-page' + id: TLPageId + } + | { + type: 'select-shape' + id: TLShapeId + } + | { + type: 'deselect-shape' + id: TLShapeId + } + | { + type: 'move-selection' + dx: number + dy: number + } + | { + type: 'delete-selection' + } + | { + type: 'move-selected-shapes-to-page' + pageId: TLPageId + } + | { + type: 'mark-stopping-point' + } + +export class FuzzEditor extends RandomSource { + editor: Editor + + constructor( + public readonly id: string, + _seed: number, + public readonly store: TLStore + ) { + super(_seed) + this.editor = new Editor({ + shapeUtils: defaultShapeUtils, + tools: defaultTools, + initialState: 'select', + store, + getContainer: () => document.createElement('div'), + }) + } + + ops: Op[] = [] + + getRandomShapeId({ selected }: { selected?: boolean } = {}): TLShapeId | undefined { + return this.randomElement( + selected ? this.editor.getSelectedShapes() : this.editor.getCurrentPageShapes() + )?.id + } + + getRandomOp(): Op { + const op = this.randomAction( + [ + () => { + const x = this.randomInt(1000) + const y = this.randomInt(1000) + const width = this.randomInt(1, 1000) + const height = this.randomInt(1, 1000) + let parentId: TLShapeId | undefined + if (this.randomInt(2) === 0) { + parentId = this.randomElement( + this.editor.getCurrentPageShapes().filter((s) => s.type === 'frame') + )?.id + } + return { type: 'create-box', x, y, width, height, parentId } + }, + () => { + const x = this.randomInt(1000) + const y = this.randomInt(1000) + const width = this.randomInt(1, 1000) + const height = this.randomInt(1, 1000) + return { type: 'create-frame', x, y, width, height } + }, + // Need to disable arrows for the time being, the cleanup logic leads to state inconsistency. + // We need a better way to handle state updates. + // () => { + // let start: TLArrowTerminal = { + // type: 'point', + // x: this.randomInt(1000), + // y: this.randomInt(1000), + // } + // let end: TLArrowTerminal = { + // type: 'point', + // x: this.randomInt(1000), + // y: this.randomInt(1000), + // } + + // if (this.randomInt(2) === 0) { + // const boundShapeId = this.getRandomShapeId() + // if (boundShapeId) { + // start = { + // type: 'binding', + // boundShapeId: boundShapeId, + // isExact: true, + // normalizedAnchor: { x: 0.5, y: 0.5 }, + // } + // } + // } + + // if (this.randomInt(2) === 0) { + // const boundShapeId = this.getRandomShapeId() + // if (boundShapeId) { + // end = { + // type: 'binding', + // boundShapeId: boundShapeId, + // isExact: true, + // normalizedAnchor: { x: 0.5, y: 0.5 }, + // } + // } + // } + + // return { type: 'create-arrow', start, end } + // }, + () => { + const id = this.getRandomShapeId() + if (id) { + return { type: 'delete-shape', id } + } + return this.getRandomOp() + }, + () => { + return { type: 'create-page', id: PageRecordType.createId() } + }, + () => { + const id = this.randomElement(this.editor.getPages())?.id + if (id) { + return { type: 'delete-page', id } + } + return this.getRandomOp() + }, + () => { + return { type: 'undo' } + }, + () => { + return { type: 'redo' } + }, + () => { + return { type: 'mark-stopping-point' } + }, + () => { + if (this.editor.getSelectedShapes().length > 1) { + return { type: 'group-selection' } + } + return this.getRandomOp() + }, + () => { + if (this.editor.getSelectedShapes().some((s) => s.type === 'group')) { + return { type: 'ungroup-selection' } + } + return this.getRandomOp() + }, + () => { + const id = this.randomElement(this.editor.getPages())?.id + if (id) { + return { type: 'switch-page', id } + } + return this.getRandomOp() + }, + () => { + const id = this.getRandomShapeId() + if (id) { + return { type: 'select-shape', id } + } + return this.getRandomOp() + }, + () => { + const id = this.getRandomShapeId({ selected: true }) + if (id) { + return { type: 'deselect-shape', id } + } + return this.getRandomOp() + }, + () => { + if (this.editor.getSelectedShapes().length) { + const dx = this.randomInt(1000) + const dy = this.randomInt(1000) + return { type: 'move-selection', dx, dy } + } + return this.getRandomOp() + }, + () => { + if (this.editor.getSelectedShapes().length) { + return { type: 'delete-selection' } + } + return this.getRandomOp() + }, + () => { + if (this.editor.getSelectedShapes().length) { + const pageId = this.randomElement( + this.editor.getPages().filter((p) => p.id !== this.editor.getCurrentPageId()) + )?.id + if (pageId) { + return { type: 'move-selected-shapes-to-page', pageId } + } + } + return this.getRandomOp() + }, + ], + true + ) + this.ops.push(op) + return op + } + + applyOp(op: Op) { + switch (op.type) { + case 'create-box': { + this.editor.createShape({ + type: 'geo', + id: createShapeId(), + x: op.x, + y: op.y, + parentId: op.parentId, + props: { + w: op.width, + h: op.height, + }, + }) + break + } + + case 'create-frame': { + this.editor.createShape({ + type: 'frame', + id: createShapeId(), + x: op.x, + y: op.y, + props: { + w: op.width, + h: op.height, + }, + }) + break + } + + case 'create-arrow': { + this.editor.createShape({ + type: 'arrow', + id: createShapeId(), + x: 0, + y: 0, + props: { + start: op.start, + end: op.end, + }, + }) + break + } + + case 'delete-shape': { + this.editor.deleteShape(op.id) + break + } + + case 'create-page': { + this.editor.createPage({ id: op.id, name: op.id }) + break + } + + case 'delete-page': { + this.editor.deletePage(op.id) + break + } + + case 'undo': { + this.editor.undo() + break + } + + case 'redo': { + this.editor.redo() + break + } + + case 'group-selection': { + this.editor.groupShapes(this.editor.getSelectedShapeIds()) + break + } + + case 'ungroup-selection': { + this.editor.ungroupShapes(this.editor.getSelectedShapeIds()) + break + } + + case 'mark-stopping-point': { + this.editor.mark() + break + } + + case 'switch-page': { + this.editor.setCurrentPage(op.id) + break + } + + case 'select-shape': { + this.editor.select(op.id) + break + } + + case 'deselect-shape': { + this.editor.deselect(op.id) + break + } + + case 'move-selection': { + this.editor.updateShapes( + this.editor.getSelectedShapes().map((s) => ({ + ...s, + x: s.x + op.dx, + y: s.y + op.dy, + })) + ) + break + } + + case 'delete-selection': { + this.editor.deleteShapes(this.editor.getSelectedShapeIds()) + break + } + + case 'move-selected-shapes-to-page': { + this.editor.moveShapesToPage(this.editor.getSelectedShapeIds(), op.pageId) + break + } + + default: + throw new Error(`Unknown op type: ${JSON.stringify((op as any).type)}`) + } + } +} diff --git a/packages/tlsync/src/test/RandomSource.ts b/packages/tlsync/src/test/RandomSource.ts new file mode 100644 index 000000000..55136be2a --- /dev/null +++ b/packages/tlsync/src/test/RandomSource.ts @@ -0,0 +1,45 @@ +export class RandomSource { + constructor(private _seed: number) {} + + randomInt(): number + randomInt(lessThan: number): number + randomInt(fromInclusive: number, toExclusive: number): number + randomInt(lo?: number, hi?: number) { + if (lo === undefined) { + lo = Number.MAX_SAFE_INTEGER + } + if (hi === undefined) { + hi = lo + lo = 0 + } + this._seed = (this._seed * 9301 + 49297) % 233280 + // float is a number between 0 and 1 + const float = this._seed / 233280 + return lo + Math.floor(float * (hi - lo)) + } + + randomAction( + choices: Array<(() => Result) | { weight: number; do: () => any }>, + randomWeights?: boolean + ): Result { + type Choice = (typeof choices)[number] + const getWeightFromChoice = (choice: Choice) => + 'weight' in choice ? choice.weight : randomWeights ? this.randomInt(0, 10) : 1 + const weights = choices.map(getWeightFromChoice) + const totalWeight = weights.reduce((total, w) => total + w, 0) + const randomWeight = this.randomInt(totalWeight) + let weight = 0 + for (let i = 0; i < choices.length; i++) { + weight += weights[i] + const choice = choices[i] + if (randomWeight < weight) { + return 'do' in choice ? choice.do() : choice() + } + } + throw new Error('unreachable') + } + + randomElement(items: Elem[]): Elem | undefined { + return items[this.randomInt(items.length)] + } +} diff --git a/packages/tlsync/src/test/TLServer.test.ts b/packages/tlsync/src/test/TLServer.test.ts new file mode 100644 index 000000000..b645275ba --- /dev/null +++ b/packages/tlsync/src/test/TLServer.test.ts @@ -0,0 +1,165 @@ +import { TLRecord, createTLStore, defaultShapeUtils } from '@tldraw/tldraw' +import { type WebSocket } from 'ws' +import { RoomSessionState } from '../lib/RoomSession' +import { DBLoadResult, TLServer } from '../lib/TLServer' +import { chunk } from '../lib/chunk' +import { RecordOpType } from '../lib/diff' +import { TLSYNC_PROTOCOL_VERSION, TLSocketClientSentEvent } from '../lib/protocol' +import { RoomState } from '../lib/server-types' + +// Because we are using jsdom in this package, jest tries to load the 'browser' version of the ws library +// which doesn't do anything except throw an error. So we need to sneakily load the node version of ws. +const wsPath = require.resolve('ws').replace('/browser.js', '/index.js') +// eslint-disable-next-line @typescript-eslint/no-var-requires +const ws = require(wsPath) as typeof import('ws') + +const PORT = 23473 + +const disposables: (() => void)[] = [] + +class TLServerTestImpl extends TLServer { + wsServer = new ws.Server({ port: PORT }) + async close() { + await new Promise((resolve) => { + this.wsServer.close((err) => { + if (err) { + console.error(err) + } + resolve(err) + }) + }) + } + async createSocketPair() { + const connectionPromise = new Promise((resolve) => { + this.wsServer.on('connection', resolve) + }) + + const client = new ws.WebSocket('ws://localhost:' + PORT) + disposables.push(() => { + client.close() + }) + const openPromise = new Promise((resolve) => { + client.on('open', resolve) + }) + + const server = await connectionPromise + disposables.push(() => { + server.close() + }) + await openPromise + + return { + client, + server, + } + } + override async loadFromDatabase?(_roomId: string): Promise { + return { type: 'room_not_found' } + } + override async persistToDatabase?(_roomId: string): Promise { + return + } + override logEvent(_event: any): void { + return + } + roomState: RoomState | undefined = undefined + override getRoomForPersistenceKey(_persistenceKey: string): RoomState | undefined { + return this.roomState + } + override setRoomState(_persistenceKey: string, roomState: RoomState): void { + this.roomState = roomState + } + override deleteRoomState(_persistenceKey: string): void { + this.roomState = undefined + } +} +type UnpackPromise = T extends Promise ? U : T + +const schema = createTLStore({ shapeUtils: defaultShapeUtils }).schema.serialize() + +let server: TLServerTestImpl +let sockets: UnpackPromise> +beforeEach(async () => { + server = new TLServerTestImpl() + sockets = await server.createSocketPair() + expect(sockets.client.readyState).toBe(ws.OPEN) + expect(sockets.server.readyState).toBe(ws.OPEN) +}) + +const openConnection = async () => { + await server.handleConnection({ + persistenceKey: 'test-persistence-key', + sessionKey: 'test-session-key', + socket: sockets.server, + storeId: 'test-store-id', + }) +} + +afterEach(async () => { + disposables.forEach((d) => d()) + disposables.length = 0 + await server.close() +}) + +describe('TLServer', () => { + it('accepts new connections', async () => { + await openConnection() + + expect(server.roomState).not.toBeUndefined() + expect(server.roomState?.persistenceKey).toBe('test-persistence-key') + expect(server.roomState?.room.sessions.size).toBe(1) + expect(server.roomState?.room.sessions.get('test-session-key')?.state).toBe( + RoomSessionState.AWAITING_CONNECT_MESSAGE + ) + }) + + it('allows requests to be chunked', async () => { + await openConnection() + + const connectMsg: TLSocketClientSentEvent = { + type: 'connect', + lastServerClock: 0, + connectRequestId: 'test-connect-request-id', + protocolVersion: TLSYNC_PROTOCOL_VERSION, + schema, + } + + const chunks = chunk(JSON.stringify(connectMsg), 200) + expect(chunks.length).toBeGreaterThan(1) + + const onClientMessage = jest.fn() + const receivedPromise = new Promise((resolve) => { + onClientMessage.mockImplementationOnce(resolve) + }) + + sockets.client.on('message', onClientMessage) + + expect(server.roomState?.room.sessions.get('test-session-key')?.state).toBe( + RoomSessionState.AWAITING_CONNECT_MESSAGE + ) + + for (const chunk of chunks) { + sockets.client.send(chunk) + } + + await receivedPromise + + expect(server.roomState?.room.sessions.get('test-session-key')?.state).toBe( + RoomSessionState.CONNECTED + ) + + expect(onClientMessage).toHaveBeenCalledTimes(1) + expect(JSON.parse(onClientMessage.mock.calls[0][0])).toMatchObject({ + connectRequestId: 'test-connect-request-id', + hydrationType: 'wipe_all', + diff: { + 'document:document': [ + RecordOpType.Put, + { + /* ... */ + }, + ], + }, + }) + }) +}) diff --git a/packages/tlsync/src/test/TLSyncRoom.test.ts b/packages/tlsync/src/test/TLSyncRoom.test.ts new file mode 100644 index 000000000..2232492f8 --- /dev/null +++ b/packages/tlsync/src/test/TLSyncRoom.test.ts @@ -0,0 +1,156 @@ +import { SerializedSchema } from '@tldraw/store' +import { + CameraRecordType, + DocumentRecordType, + InstancePageStateRecordType, + PageRecordType, + TLArrowShape, + TLArrowShapeProps, + TLBaseShape, + TLRecord, + TLShapeId, + createTLSchema, +} from '@tldraw/tlschema' +import { sortById } from '@tldraw/utils' +import { + MAX_TOMBSTONES, + RoomSnapshot, + TLSyncRoom, + TOMBSTONE_PRUNE_BUFFER_SIZE, +} from '../lib/TLSyncRoom' +import { schema } from '../lib/schema' + +const compareById = (a: { id: string }, b: { id: string }) => a.id.localeCompare(b.id) + +const records = [ + DocumentRecordType.create({}), + PageRecordType.create({ index: 'a0', name: 'page 2' }), +].sort(compareById) + +const makeSnapshot = (records: TLRecord[], others: Partial = {}) => ({ + documents: records.map((r) => ({ state: r, lastChangedClock: 0 })), + clock: 0, + ...others, +}) + +const oldArrow: TLBaseShape<'arrow', Omit> = { + typeName: 'shape', + type: 'arrow', + id: 'shape:old_arrow' as TLShapeId, + index: 'a0', + isLocked: false, + parentId: PageRecordType.createId(), + rotation: 0, + x: 0, + y: 0, + opacity: 1, + props: { + dash: 'draw', + size: 'm', + fill: 'none', + color: 'black', + bend: 0, + start: { type: 'point', x: 0, y: 0 }, + end: { type: 'point', x: 0, y: 0 }, + arrowheadStart: 'none', + arrowheadEnd: 'arrow', + text: '', + font: 'draw', + }, + meta: {}, +} + +describe('TLSyncRoom', () => { + it('can be constructed with a schema alone', () => { + const room = new TLSyncRoom(schema) + + // we populate the store with a default document if none is given + expect(room.getSnapshot().documents.length).toBeGreaterThan(0) + }) + + it('can be constructed with a snapshot', () => { + const room = new TLSyncRoom(schema, makeSnapshot(records)) + + expect( + room + .getSnapshot() + .documents.map((r) => r.state) + .sort(sortById) + ).toEqual(records) + + expect(room.getSnapshot().documents.map((r) => r.lastChangedClock)).toEqual([0, 0]) + }) + + it('trims tombstones down if you pass too many in the snapshot', () => { + const room = new TLSyncRoom(schema, { + documents: [], + clock: MAX_TOMBSTONES + 100, + tombstones: Object.fromEntries( + Array.from({ length: MAX_TOMBSTONES + 100 }, (_, i) => [PageRecordType.createId(), i]) + ), + }) + + expect(Object.keys(room.getSnapshot().tombstones ?? {})).toHaveLength( + MAX_TOMBSTONES - TOMBSTONE_PRUNE_BUFFER_SIZE + ) + }) + + it('migrates the snapshot if it is dealing with old data', () => { + const serializedSchema = schema.serialize() + const oldSerializedSchema: SerializedSchema = { + ...serializedSchema, + recordVersions: { + ...serializedSchema.recordVersions, + shape: { + ...serializedSchema.recordVersions.shape, + subTypeVersions: { + ...('subTypeVersions' in serializedSchema.recordVersions.shape + ? serializedSchema.recordVersions.shape.subTypeVersions + : {}), + // we add a labelColor to arrow in v1 + arrow: 0, + }, + }, + }, + } + + const room = new TLSyncRoom( + schema, + makeSnapshot([...records, oldArrow], { + schema: oldSerializedSchema, + }) + ) + + const arrow = room.getSnapshot().documents.find((r) => r.state.id === oldArrow.id) + ?.state as TLArrowShape + expect(arrow.props.labelColor).toBe('black') + }) + + it('filters out instance state records', () => { + const schema = createTLSchema({ shapes: {} }) + const room = new TLSyncRoom( + schema, + makeSnapshot([ + ...records, + schema.types.instance.create({ + currentPageId: PageRecordType.createId('page_1'), + id: schema.types.instance.createId('instance_1'), + }), + InstancePageStateRecordType.create({ + id: InstancePageStateRecordType.createId(PageRecordType.createId('page_1')), + pageId: PageRecordType.createId('page_1'), + }), + CameraRecordType.create({ + id: CameraRecordType.createId('camera_1'), + }), + ]) + ) + + expect( + room + .getSnapshot() + .documents.map((r) => r.state) + .sort(sortById) + ).toEqual(records) + }) +}) diff --git a/packages/tlsync/src/test/TestServer.ts b/packages/tlsync/src/test/TestServer.ts new file mode 100644 index 000000000..66dabcffb --- /dev/null +++ b/packages/tlsync/src/test/TestServer.ts @@ -0,0 +1,24 @@ +import { StoreSchema, UnknownRecord } from '@tldraw/store' +import { RoomSnapshot, TLSyncRoom } from '../lib/TLSyncRoom' +import { TestSocketPair } from './TestSocketPair' + +export class TestServer { + room: TLSyncRoom + constructor(schema: StoreSchema, snapshot?: RoomSnapshot) { + this.room = new TLSyncRoom(schema, snapshot) + } + + connect(socketPair: TestSocketPair): void { + this.room.handleNewSession(socketPair.id, socketPair.roomSocket) + + socketPair.clientSocket.connectionStatus = 'online' + socketPair.didReceiveFromClient = (msg) => { + this.room.handleMessage(socketPair.id, msg) + } + socketPair.clientDisconnected = () => { + this.room.handleClose(socketPair.id) + } + + socketPair.callbacks.onStatusChange?.('online') + } +} diff --git a/packages/tlsync/src/test/TestSocketPair.ts b/packages/tlsync/src/test/TestSocketPair.ts new file mode 100644 index 000000000..db90039ca --- /dev/null +++ b/packages/tlsync/src/test/TestSocketPair.ts @@ -0,0 +1,102 @@ +import { UnknownRecord } from '@tldraw/store' +import { TLPersistentClientSocket, TLPersistentClientSocketStatus } from '../lib/TLSyncClient' +import { TLRoomSocket } from '../lib/TLSyncRoom' +import { TLSocketClientSentEvent, TLSocketServerSentEvent } from '../lib/protocol' +import { TestServer } from './TestServer' + +export class TestSocketPair { + clientSentEventQueue: TLSocketClientSentEvent[] = [] + serverSentEventQueue: TLSocketServerSentEvent[] = [] + flushServerSentEvents() { + const queue = this.serverSentEventQueue + this.serverSentEventQueue = [] + queue.forEach((msg) => { + this.callbacks.onReceiveMessage?.(msg) + }) + } + flushClientSentEvents() { + const queue = this.clientSentEventQueue + this.clientSentEventQueue = [] + queue.forEach((msg) => { + this.didReceiveFromClient?.(msg) + }) + } + + getNeedsFlushing() { + return this.serverSentEventQueue.length > 0 || this.clientSentEventQueue.length > 0 + } + + roomSocket: TLRoomSocket = { + close: () => { + this.flushServerSentEvents() + this.disconnect() + }, + get isOpen() { + return true + }, + sendMessage: (msg: TLSocketServerSentEvent) => { + if (!this.callbacks.onReceiveMessage) { + throw new Error('Socket is closed') + } + if (this.clientSocket.connectionStatus !== 'online') { + // client was closed, drop the packet + return + } + this.serverSentEventQueue.push(msg) + }, + } + didReceiveFromClient?: (msg: TLSocketClientSentEvent) => void = undefined + clientDisconnected?: () => void = undefined + clientSocket: TLPersistentClientSocket = { + connectionStatus: 'offline', + onStatusChange: (cb) => { + this.callbacks.onStatusChange = cb + return () => { + this.callbacks.onStatusChange = null + } + }, + onReceiveMessage: (cb) => { + this.callbacks.onReceiveMessage = cb + return () => { + this.callbacks.onReceiveMessage = null + } + }, + sendMessage: (msg: TLSocketClientSentEvent) => { + if (this.clientSocket.connectionStatus !== 'online') { + throw new Error('trying to send before open') + } + this.clientSentEventQueue.push(msg) + }, + restart: () => { + this.disconnect() + this.connect() + }, + } + + callbacks = { + onReceiveMessage: null as null | ((msg: TLSocketServerSentEvent) => void), + onStatusChange: null as null | ((status: TLPersistentClientSocketStatus) => void), + } + + // eslint-disable-next-line no-restricted-syntax + get isConnected() { + return this.clientSocket.connectionStatus === 'online' + } + + connect() { + this.server.connect(this) + } + + disconnect() { + this.clientSocket.connectionStatus = 'offline' + this.serverSentEventQueue = [] + this.clientSentEventQueue = [] + this.callbacks.onStatusChange?.('offline') + this.clientDisconnected?.() + } + + constructor( + public readonly id: string, + public readonly server: TestServer + ) {} +} diff --git a/packages/tlsync/src/test/chunk.test.ts b/packages/tlsync/src/test/chunk.test.ts new file mode 100644 index 000000000..e19edf082 --- /dev/null +++ b/packages/tlsync/src/test/chunk.test.ts @@ -0,0 +1,161 @@ +import { JsonChunkAssembler, chunk } from '../lib/chunk' + +describe('chunk', () => { + it('chunks a string', () => { + expect(chunk('hello there my good world', 5)).toMatchInlineSnapshot(` + Array [ + "8_h", + "7_ell", + "6_o t", + "5_her", + "4_e m", + "3_y g", + "2_ood", + "1_ wo", + "0_rld", + ] + `) + + expect(chunk('hello there my good world', 10)).toMatchInlineSnapshot(` + Array [ + "3_h", + "2_ello the", + "1_re my go", + "0_od world", + ] + `) + }) + + it('does not chunk the string if it is small enough', () => { + const chunks = chunk('hello', 100) + expect(chunks).toMatchInlineSnapshot(` + Array [ + "hello", + ] + `) + }) + + it('makes sure the chunk length does not exceed the given message size', () => { + const chunks = chunk('dark and stormy tonight', 4) + expect(chunks).toMatchInlineSnapshot(` + Array [ + "12_d", + "11_a", + "10_r", + "9_k ", + "8_an", + "7_d ", + "6_st", + "5_or", + "4_my", + "3_ t", + "2_on", + "1_ig", + "0_ht", + ] + `) + }) + + it('does its best if the chunk size is too small', () => { + const chunks = chunk('once upon a time', 1) + expect(chunks).toMatchInlineSnapshot(` + Array [ + "15_o", + "14_n", + "13_c", + "12_e", + "11_ ", + "10_u", + "9_p", + "8_o", + "7_n", + "6_ ", + "5_a", + "4_ ", + "3_t", + "2_i", + "1_m", + "0_e", + ] + `) + }) +}) + +const testObject = {} as any +for (let i = 0; i < 1000; i++) { + testObject['key_' + i] = 'value_' + i +} + +describe('json unchunker', () => { + it.each([1, 5, 20, 200])('unchunks a json string split at %s bytes', (size) => { + const chunks = chunk(JSON.stringify(testObject), size) + + const unchunker = new JsonChunkAssembler() + for (const chunk of chunks.slice(0, -1)) { + const result = unchunker.handleMessage(chunk) + expect(result).toBeNull() + } + expect(unchunker.handleMessage(chunks[chunks.length - 1])).toEqual({ data: testObject }) + + // and the next one should be fine + expect(unchunker.handleMessage('{"ok": true}')).toEqual({ data: { ok: true } }) + }) + + // todo: test error cases + it('returns an error if the json is whack', () => { + const chunks = chunk('{"hello": world"}', 5) + const unchunker = new JsonChunkAssembler() + for (const chunk of chunks.slice(0, -1)) { + const result = unchunker.handleMessage(chunk) + expect(result).toBeNull() + } + expect( + unchunker.handleMessage(chunks[chunks.length - 1])?.error?.message + ).toMatchInlineSnapshot(`"Unexpected token w in JSON at position 10"`) + + // and the next one should be fine + expect(unchunker.handleMessage('{"ok": true}')).toEqual({ data: { ok: true } }) + }) + it('returns an error if one of the chunks was missing', () => { + const chunks = chunk('{"hello": world"}', 10) + expect(chunks).toHaveLength(3) + + const unchunker = new JsonChunkAssembler() + expect(unchunker.handleMessage(chunks[0])).toBeNull() + expect(unchunker.handleMessage(chunks[2])?.error?.message).toMatchInlineSnapshot( + `"Chunks received in wrong order"` + ) + + // and the next one should be fine + expect(unchunker.handleMessage('{"ok": true}')).toEqual({ data: { ok: true } }) + }) + + it('returns an error if the chunk stream ends abruptly', () => { + const chunks = chunk('{"hello": world"}', 10) + expect(chunks).toHaveLength(3) + + const unchunker = new JsonChunkAssembler() + expect(unchunker.handleMessage(chunks[0])).toBeNull() + expect(unchunker.handleMessage(chunks[1])).toBeNull() + + // it should still return the data for the next message + // even if there was an unexpected end to the chunks stream + const res = unchunker.handleMessage('{"hello": "world"}') + expect(res?.data).toEqual({ hello: 'world' }) + expect(res?.error?.message).toMatchInlineSnapshot(`"Unexpected non-chunk message"`) + + // and the next one should be fine + expect(unchunker.handleMessage('{"ok": true}')).toEqual({ data: { ok: true } }) + }) + + it('returns an error if the chunk syntax is wrong', () => { + // it only likes json objects + const unchunker = new JsonChunkAssembler() + expect(unchunker.handleMessage('["yo"]')?.error?.message).toMatchInlineSnapshot( + `"Invalid chunk: \\"[\\\\\\"yo\\\\\\"]...\\""` + ) + + // and the next one should be fine + expect(unchunker.handleMessage('{"ok": true}')).toEqual({ data: { ok: true } }) + }) +}) diff --git a/packages/tlsync/src/test/diff.test.ts b/packages/tlsync/src/test/diff.test.ts new file mode 100644 index 000000000..e69373f27 --- /dev/null +++ b/packages/tlsync/src/test/diff.test.ts @@ -0,0 +1,189 @@ +import { applyObjectDiff, diffRecord } from '../lib/diff' + +describe('nested arrays', () => { + it('should be patchable at the end', () => { + const a = { + arr: [ + [1, 2, 3], + [4, 5, 6], + ], + } + const b = { + arr: [ + [1, 2, 3], + [4, 5, 6, 7, 8], + ], + } + + expect(diffRecord(a, b)).toMatchInlineSnapshot(` + Object { + "arr": Array [ + "patch", + Object { + "1": Array [ + "append", + Array [ + 7, + 8, + ], + 3, + ], + }, + ], + } + `) + }) + + it('should be patchable at the beginning', () => { + const a = { + arr: [ + [1, 2, 3], + [4, 5, 6], + ], + } + const b = { + arr: [ + [1, 2, 3, 4, 5, 6], + [4, 5, 6], + ], + } + + expect(diffRecord(a, b)).toMatchInlineSnapshot(` + Object { + "arr": Array [ + "patch", + Object { + "0": Array [ + "append", + Array [ + 4, + 5, + 6, + ], + 3, + ], + }, + ], + } + `) + }) +}) + +describe('objects inside arrays', () => { + it('should be patchable if only item changes', () => { + const a = { + arr: [ + { a: 1, b: 2, c: 3 }, + { a: 4, b: 5, c: 6 }, + ], + } + const b = { + arr: [ + { a: 1, b: 2, c: 3 }, + { a: 4, b: 5, c: 7 }, + ], + } + + expect(diffRecord(a, b)).toMatchInlineSnapshot(` + Object { + "arr": Array [ + "patch", + Object { + "1": Array [ + "patch", + Object { + "c": Array [ + "put", + 7, + ], + }, + ], + }, + ], + } + `) + }) + + it('should return a put op if many items change', () => { + const a = { + arr: [ + { a: 1, b: 2, c: 3 }, + { a: 4, b: 5, c: 6 }, + ], + } + const b = { + arr: [ + { a: 1, b: 2, c: 5 }, + { a: 4, b: 5, c: 7 }, + ], + } + + expect(diffRecord(a, b)).toMatchInlineSnapshot(` + Object { + "arr": Array [ + "put", + Array [ + Object { + "a": 1, + "b": 2, + "c": 5, + }, + Object { + "a": 4, + "b": 5, + "c": 7, + }, + ], + ], + } + `) + }) +}) + +test('deleting things from a record', () => { + const a = { + a: 1, + b: 2, + c: 3, + } + const b = { + a: 1, + b: 2, + } + + const patch = diffRecord(a, b) + expect(patch).toMatchInlineSnapshot(` + Object { + "c": Array [ + "delete", + ], + } + `) + + expect(applyObjectDiff(a, patch!)).toEqual(b) +}) + +test('adding things things to a record', () => { + const a = { + a: 1, + b: 2, + } + const b = { + a: 1, + b: 2, + c: 3, + } + + const patch = diffRecord(a, b) + + expect(patch).toMatchInlineSnapshot(` + Object { + "c": Array [ + "put", + 3, + ], + } + `) + + expect(applyObjectDiff(a, patch!)).toEqual(b) +}) diff --git a/packages/tlsync/src/test/schema.test.ts b/packages/tlsync/src/test/schema.test.ts new file mode 100644 index 000000000..45b1aca27 --- /dev/null +++ b/packages/tlsync/src/test/schema.test.ts @@ -0,0 +1,9 @@ +import { coreShapes, defaultShapeUtils } from '@tldraw/tldraw' +import { schema } from '../lib/schema' + +describe('schema', () => { + test('shape types match core+default shapes', () => { + const shapeTypes = Object.keys(schema.types.shape.migrations.subTypeMigrations!) + expect(shapeTypes).toEqual([...coreShapes, ...defaultShapeUtils].map((s) => s.type)) + }) +}) diff --git a/packages/tlsync/src/test/syncFuzz.test.ts b/packages/tlsync/src/test/syncFuzz.test.ts new file mode 100644 index 000000000..70f8166f6 --- /dev/null +++ b/packages/tlsync/src/test/syncFuzz.test.ts @@ -0,0 +1,290 @@ +import { + Editor, + TLArrowShape, + TLRecord, + TLStore, + computed, + createPresenceStateDerivation, + createTLStore, +} from '@tldraw/tldraw' +import isEqual from 'lodash.isequal' +import { nanoid } from 'nanoid' +import { TLSyncClient } from '../lib/TLSyncClient' +import { schema } from '../lib/schema' +import { FuzzEditor, Op } from './FuzzEditor' +import { RandomSource } from './RandomSource' +import { TestServer } from './TestServer' +import { TestSocketPair } from './TestSocketPair' + +jest.mock('@tldraw/editor/src/lib/editor/managers/TickManager.ts', () => { + return { + TickManager: class { + start() { + // noop + } + }, + } +}) + +// @ts-expect-error +global.requestAnimationFrame = (cb: () => any) => { + cb() +} + +jest.mock('nanoid', () => { + const { RandomSource } = jest.requireActual('./RandomSource') + let source = new RandomSource(0) + // eslint-disable-next-line @typescript-eslint/no-var-requires + const readable = require('uuid-readable') + // eslint-disable-next-line @typescript-eslint/no-var-requires + const uuid = require('uuid-by-string') + const nanoid = () => { + return readable.short(uuid(source.randomInt().toString(16))).replaceAll(' ', '_') + } + return { + nanoid, + default: nanoid, + __reseed(seed: number) { + source = new RandomSource(seed) + }, + } +}) + +const disposables: Array<() => void> = [] + +afterEach(() => { + for (const dispose of disposables) { + dispose() + } + disposables.length = 0 +}) + +class FuzzTestInstance extends RandomSource { + store: TLStore + editor: FuzzEditor | null = null + client: TLSyncClient + socketPair: TestSocketPair + id: string + + hasLoaded = false + + constructor( + public readonly seed: number, + server: TestServer + ) { + super(seed) + + this.store = createTLStore({ schema }) + this.id = nanoid() + this.socketPair = new TestSocketPair(this.id, server) + this.client = new TLSyncClient({ + store: this.store, + socket: this.socketPair.clientSocket, + onSyncError: (reason) => { + throw new Error('onSyncError:' + reason) + }, + onLoad: () => { + this.editor = new FuzzEditor(this.id, this.seed, this.store) + }, + onLoadError: (e) => { + throw new Error('onLoadError', e) + }, + presence: createPresenceStateDerivation( + computed('', () => ({ + id: this.id, + name: 'test', + color: 'red', + locale: 'en', + })) + )(this.store), + }) + + disposables.push(() => { + this.client.close() + }) + } +} + +let totalNumShapes = 0 +let totalNumPages = 0 + +function arrowsAreSound(editor: Editor) { + const arrows = editor.getCurrentPageShapes().filter((s) => s.type === 'arrow') as TLArrowShape[] + for (const arrow of arrows) { + for (const terminal of [arrow.props.start, arrow.props.end]) { + if (terminal.type === 'binding' && !editor.store.has(terminal.boundShapeId)) { + return false + } + } + } + return true +} + +function runTest(seed: number) { + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('nanoid').__reseed(seed) + const server = new TestServer(schema) + const instance = new FuzzTestInstance(seed, server) + + const peers = [instance, new FuzzTestInstance(instance.randomInt(), server)] + const numExtraPeers = instance.randomInt(MAX_PEERS - 2) + for (let i = 0; i < numExtraPeers; i++) { + peers.push(new FuzzTestInstance(instance.randomInt(), server)) + } + + const allOk = (when: string) => { + if (peers.some((p) => p.editor?.editor && !p.editor?.editor.getCurrentPage())) { + throw new Error(`not all peer editors have current page (${when})`) + } + if (peers.some((p) => p.editor?.editor && !p.editor?.editor.getCurrentPageState())) { + throw new Error(`not all peer editors have page states (${when})`) + } + if ( + peers.some( + (p) => p.client.isConnectedToRoom && p.socketPair.clientSocket.connectionStatus !== 'online' + ) + ) { + throw new Error(`peer client connection status mismatch (${when})`) + } + if (peers.some((p) => p.editor?.editor && !arrowsAreSound(p.editor.editor))) { + throw new Error(`peer editor arrows are not sound (${when})`) + } + const numOtherPeersConnected = peers.filter((p) => p.hasLoaded).length - 1 + if ( + peers.some( + (p) => + p.hasLoaded && + p.editor?.editor.store.query.ids('instance_presence').get().size !== + numOtherPeersConnected + ) + ) { + throw new Error(`not all peer editors have instance presence (${when})`) + } + } + + const ops: Array<{ peerId: string; op: Op; id: number }> = [] + try { + for (let i = 0; i < NUM_OPS_PER_TEST; i++) { + const peer = peers[instance.randomInt(peers.length)] + + if (peer.editor) { + const op = peer.editor.getRandomOp() + ops.push({ peerId: peer.id, op, id: ops.length }) + + allOk('before applyOp') + peer.editor.applyOp(op) + allOk('after applyOp') + + if (peer.socketPair.isConnected && peer.randomInt(6) === 0) { + // randomly disconnect a peer + peer.socketPair.disconnect() + allOk('disconnect') + } else if (!peer.socketPair.isConnected && peer.randomInt(2) === 0) { + // randomly reconnect a peer + peer.socketPair.connect() + allOk('connect') + } + } else if (!peer.socketPair.isConnected && peer.randomInt(2) === 0) { + peer.socketPair.connect() + allOk('connect 2') + } + + const peersThatNeedFlushing = peers.filter((p) => p.socketPair.getNeedsFlushing()) + for (const peer of peersThatNeedFlushing) { + if (peer.randomInt(10) < 4) { + allOk('before flush server ' + i) + peer.socketPair.flushServerSentEvents() + allOk('flush server ' + i) + } else if (peer.randomInt(10) < 2) { + peer.socketPair.flushClientSentEvents() + allOk('flush client') + } + } + } + + // bring all clients online and flush all messages to make sure everyone has seen all messages + while (peers.some((p) => !p.socketPair.isConnected)) { + for (const peer of peers) { + if (!peer.socketPair.isConnected && peer.randomInt(2) === 0) { + peer.socketPair.connect() + allOk('final connect') + } + } + } + + while (peers.some((p) => p.socketPair.getNeedsFlushing())) { + for (const peer of peers) { + if (peer.socketPair.getNeedsFlushing()) { + peer.socketPair.flushServerSentEvents() + allOk('final flushServer') + peer.socketPair.flushClientSentEvents() + allOk('final flushClient') + } + } + } + + const equalityResults = [] + for (let i = 0; i < peers.length; i++) { + const row = [] + for (let j = 0; j < peers.length; j++) { + row.push( + isEqual( + peers[i].editor?.store.serialize('document'), + peers[j].editor?.store.serialize('document') + ) + ) + } + equalityResults.push(row) + } + + const [first, ...rest] = peers.map((peer) => peer.editor?.store.serialize('document')) + + // writeFileSync(`./test-results.${seed}.json`, JSON.stringify(ops, null, 2)) + + expect(first).toEqual(rest[0]) + // all snapshots should be the same + expect(rest.every((other) => isEqual(other, first))).toBe(true) + totalNumPages += Object.values(first!).filter((v) => v.typeName === 'page').length + totalNumShapes += Object.values(first!).filter((v) => v.typeName === 'shape').length + } catch (e) { + console.error('seed', seed) + console.error( + 'peers', + JSON.stringify( + peers.map((p) => p.id), + null, + 2 + ) + ) + console.error('ops', JSON.stringify(ops, null, 2)) + throw e + } +} + +const NUM_TESTS = 50 +const NUM_OPS_PER_TEST = 100 +const MAX_PEERS = 4 + +// test.only('seed 8343632005032947', () => { +// runTest(8343632005032947) +// }) + +test('fuzzzzz', () => { + for (let i = 0; i < NUM_TESTS; i++) { + const seed = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) + try { + runTest(seed) + } catch (e) { + console.error('seed', seed) + throw e + } + } +}) + +test('totalNumPages', () => { + expect(totalNumPages).not.toBe(0) +}) + +test('totalNumShapes', () => { + expect(totalNumShapes).not.toBe(0) +}) diff --git a/packages/tlsync/src/test/upgradeDowngrade.test.ts b/packages/tlsync/src/test/upgradeDowngrade.test.ts new file mode 100644 index 000000000..d6f27f778 --- /dev/null +++ b/packages/tlsync/src/test/upgradeDowngrade.test.ts @@ -0,0 +1,839 @@ +import { computed } from '@tldraw/state' +import { + BaseRecord, + RecordId, + SerializedStore, + Store, + StoreSchema, + UnknownRecord, + createRecordType, + defineMigrations, +} from '@tldraw/store' +import { TLSyncClient } from '../lib/TLSyncClient' +import { RoomSnapshot, TLRoomSocket } from '../lib/TLSyncRoom' +import { RecordOpType, ValueOpType } from '../lib/diff' +import { + TLIncompatibilityReason, + TLSYNC_PROTOCOL_VERSION, + TLSocketServerSentEvent, +} from '../lib/protocol' +import { TestServer } from './TestServer' +import { TestSocketPair } from './TestSocketPair' + +function mockSocket(): TLRoomSocket { + return { + isOpen: true, + sendMessage: jest.fn(), + close() { + // noop + }, + } +} + +// @ts-expect-error +global.requestAnimationFrame = (cb: () => any) => { + cb() +} + +const disposables: Array<() => void> = [] + +afterEach(() => { + for (const dispose of disposables) { + dispose() + } + disposables.length = 0 +}) + +const UserVersions = { + ReplaceAgeWithBirthdate: 1, +} as const + +interface UserV1 extends BaseRecord<'user', RecordId> { + name: string + age: number +} +interface PresenceV1 extends BaseRecord<'presence', RecordId> { + name: string + age: number +} + +const PresenceV1 = createRecordType('presence', { + scope: 'presence', + validator: { validate: (value) => value as PresenceV1 }, +}) + +const UserV1 = createRecordType('user', { + scope: 'document', + migrations: defineMigrations({}), + validator: { validate: (value) => value as UserV1 }, +}) + +interface UserV2 extends BaseRecord<'user', RecordId> { + name: string + birthdate: string | null +} + +const UserV2 = createRecordType('user', { + scope: 'document', + migrations: defineMigrations({ + currentVersion: UserVersions.ReplaceAgeWithBirthdate, + migrators: { + [UserVersions.ReplaceAgeWithBirthdate]: { + up({ age: _age, ...user }) { + return { + ...user, + birthdate: null, + } + }, + down({ birthdate: _birthdate, ...user }) { + return { + ...user, + age: 0, + } + }, + }, + }, + }), + validator: { validate: (value) => value as UserV2 }, +}) + +type RV1 = UserV1 | PresenceV1 +type RV2 = UserV2 | PresenceV1 + +const schemaV1 = StoreSchema.create( + { user: UserV1, presence: PresenceV1 }, + { + snapshotMigrations: defineMigrations({}), + } +) + +const schemaV2 = StoreSchema.create( + { user: UserV2, presence: PresenceV1 }, + { + snapshotMigrations: defineMigrations({}), + } +) + +const schemaV3 = StoreSchema.create( + { user: UserV2, presence: PresenceV1 }, + { + snapshotMigrations: defineMigrations({ + currentVersion: 1, + migrators: { + 1: { + up(store: SerializedStore) { + // remove any users called joe + const result = Object.fromEntries( + Object.entries(store).filter(([_, r]) => r.typeName !== 'user' || r.name !== 'joe') + ) + // add a user called steve + const id = UserV2.createId('steve') + result[id] = UserV2.create({ + id, + name: 'steve', + birthdate: '2022-02-02', + }) + return result + }, + down(store: SerializedStore) { + return store + }, + }, + }, + }), + } +) + +class TestInstance { + server: TestServer + oldSocketPair: TestSocketPair + newSocketPair: TestSocketPair + oldClient: TLSyncClient + newClient: TLSyncClient + + hasLoaded = false + + constructor(snapshot?: RoomSnapshot, oldSchema = schemaV1, newSchema = schemaV2) { + this.server = new TestServer(newSchema, snapshot) + this.oldSocketPair = new TestSocketPair('test_upgrade_old', this.server) + this.newSocketPair = new TestSocketPair('test_upgrade_new', this.server) + + this.oldClient = new TLSyncClient({ + store: new Store({ schema: oldSchema, props: {} }), + socket: this.oldSocketPair.clientSocket as any, + onLoad: () => { + this.hasLoaded = true + }, + onLoadError: (e) => { + throw new Error('onLoadError', e) + }, + onSyncError: jest.fn((reason) => { + throw new Error('onSyncError: ' + reason) + }), + presence: computed('', () => null), + }) + + this.newClient = new TLSyncClient({ + store: new Store({ schema: newSchema, props: {} }), + socket: this.newSocketPair.clientSocket, + onLoad: () => { + this.hasLoaded = true + }, + onLoadError: (e) => { + throw new Error('onLoadError', e) + }, + onSyncError: jest.fn((reason) => { + throw new Error('onSyncError: ' + reason) + }), + presence: computed('', () => null), + }) + + disposables.push(() => { + this.oldClient.close() + this.newClient.close() + }) + } + + flush() { + while (this.oldSocketPair.getNeedsFlushing() || this.newSocketPair.getNeedsFlushing()) { + this.oldSocketPair.flushClientSentEvents() + this.oldSocketPair.flushServerSentEvents() + this.newSocketPair.flushClientSentEvents() + this.newSocketPair.flushServerSentEvents() + } + } +} + +test('the server can handle receiving v1 stuff from the client', () => { + const t = new TestInstance() + t.oldSocketPair.connect() + t.newSocketPair.connect() + + const user = UserV1.create({ name: 'bob', age: 10 }) + t.flush() + t.oldClient.store.put([user]) + t.flush() + + expect(t.server.room.state.get().documents[user.id].state).toMatchObject({ + name: 'bob', + birthdate: null, + }) + expect(t.server.room.state.get().documents[user.id].state).not.toMatchObject({ + name: 'bob', + age: 10, + }) + + expect(t.newClient.store.get(user.id as any)).toMatchObject({ + name: 'bob', + birthdate: null, + }) + expect(t.newClient.store.get(user.id as any)).not.toMatchObject({ name: 'bob', age: 10 }) +}) + +test('the server can send v2 stuff to the v1 client', () => { + const t = new TestInstance() + t.oldSocketPair.connect() + t.newSocketPair.connect() + + const user = UserV2.create({ name: 'bob', birthdate: '2022-01-09' }) + t.flush() + t.newClient.store.put([user]) + t.flush() + + expect(t.server.room.state.get().documents[user.id].state).toMatchObject({ + name: 'bob', + birthdate: '2022-01-09', + }) + + expect(t.oldClient.store.get(user.id as any)).toMatchObject({ + name: 'bob', + age: 0, + }) + expect(t.oldClient.store.get(user.id as any)).not.toMatchObject({ + name: 'bob', + birthdate: '2022-01-09', + }) +}) + +test('the server will run schema migrations on a snapshot', () => { + const bob = UserV1.create({ name: 'bob', age: 10 }) + // joe will be deleted + const joe = UserV1.create({ name: 'joe', age: 10 }) + const t = new TestInstance( + { + documents: [ + { state: bob, lastChangedClock: 5 }, + { state: joe, lastChangedClock: 5 }, + ], + clock: 10, + schema: schemaV1.serialize(), + tombstones: {}, + }, + schemaV1, + schemaV3 + ) + + expect(t.server.room.state.get().documents[bob.id].state).toMatchObject({ + name: 'bob', + birthdate: null, + }) + expect(t.server.room.state.get().documents[joe.id]).toBeUndefined() + + // there should be someone named steve + const snapshot = t.server.room.getSnapshot() + expect(snapshot.documents.find((u: any) => u.state.name === 'steve')).toBeDefined() +}) + +test('clients will receive updates from a snapshot migration upon connection', () => { + const t = new TestInstance() + t.oldSocketPair.connect() + t.newSocketPair.connect() + + const bob = UserV2.create({ name: 'bob', birthdate: '2022-01-09' }) + const joe = UserV2.create({ name: 'joe', birthdate: '2022-01-09' }) + t.flush() + t.newClient.store.put([bob, joe]) + t.flush() + + const snapshot = t.server.room.getSnapshot() + + t.oldSocketPair.disconnect() + t.newSocketPair.disconnect() + + const newServer = new TestServer(schemaV3, snapshot) + + const newClientSocketPair = new TestSocketPair('test_upgrade__brand_new', newServer) + + // need to set these two things to get the message through + newClientSocketPair.callbacks['onReceiveMessage'] = jest.fn() + newClientSocketPair.clientSocket.connectionStatus = 'online' + + const id = 'test_upgrade_brand_new' + const newClientSocket = mockSocket() + newServer.room.handleNewSession(id, newClientSocket) + newServer.room.handleMessage(id, { + type: 'connect', + connectRequestId: 'test', + lastServerClock: snapshot.clock, + protocolVersion: TLSYNC_PROTOCOL_VERSION, + schema: schemaV3.serialize(), + }) + + expect((newClientSocket.sendMessage as jest.Mock).mock.calls[0][0]).toMatchObject({ + // we should have added steve and deleted joe + diff: { + [joe.id]: [RecordOpType.Remove], + ['user:steve']: [RecordOpType.Put, { name: 'steve', birthdate: '2022-02-02' }], + }, + }) +}) + +test('out-of-date clients will receive incompatibility errors', () => { + const v3server = new TestServer(schemaV3) + + const id = 'test_upgrade_v2' + const socket = mockSocket() + + v3server.room.handleNewSession(id, socket) + v3server.room.handleMessage(id, { + type: 'connect', + connectRequestId: 'test', + lastServerClock: 0, + protocolVersion: TLSYNC_PROTOCOL_VERSION, + schema: schemaV2.serialize(), + }) + + expect(socket.sendMessage).toHaveBeenCalledWith({ + type: 'incompatibility_error', + reason: TLIncompatibilityReason.ClientTooOld, + }) +}) + +test('clients using an out-of-date protocol will receive compatibility errors', () => { + const v2server = new TestServer(schemaV2) + + const id = 'test_upgrade_v3' + const socket = mockSocket() + + v2server.room.handleNewSession(id, socket) + v2server.room.handleMessage(id, { + type: 'connect', + connectRequestId: 'test', + lastServerClock: 0, + protocolVersion: TLSYNC_PROTOCOL_VERSION - 1, + schema: schemaV2.serialize(), + }) + + expect(socket.sendMessage).toHaveBeenCalledWith({ + type: 'incompatibility_error', + reason: TLIncompatibilityReason.ClientTooOld, + }) +}) + +test('clients using a too-new protocol will receive compatibility errors', () => { + const v2server = new TestServer(schemaV2) + + const id = 'test_upgrade_v3' + const socket = mockSocket() + + v2server.room.handleNewSession(id, socket) + v2server.room.handleMessage(id, { + type: 'connect', + connectRequestId: 'test', + lastServerClock: 0, + protocolVersion: TLSYNC_PROTOCOL_VERSION + 1, + schema: schemaV2.serialize(), + }) + + expect(socket.sendMessage).toHaveBeenCalledWith({ + type: 'incompatibility_error', + reason: TLIncompatibilityReason.ServerTooOld, + }) +}) + +describe('when the client is too new', () => { + function setup() { + const steve = UserV1.create({ id: UserV1.createId('steve'), name: 'steve', age: 23 }) + const jeff = UserV1.create({ id: UserV1.createId('jeff'), name: 'jeff', age: 23 }) + const annie = UserV1.create({ id: UserV1.createId('annie'), name: 'annie', age: 23 }) + const v1Server = new TestServer(schemaV1, { + clock: 10, + documents: [ + { + state: steve, + lastChangedClock: 10, + }, + { + state: jeff, + lastChangedClock: 10, + }, + { + state: annie, + lastChangedClock: 10, + }, + ], + schema: schemaV1.serialize(), + tombstones: {}, + }) + + const v2_id = 'test_upgrade_v2' + const v2_socket = mockSocket() + + const v1_id = 'test_upgrade_v1' + const v1_socket = mockSocket() + + v1Server.room.handleNewSession(v1_id, v1_socket) + v1Server.room.handleMessage(v1_id, { + type: 'connect', + connectRequestId: 'test', + lastServerClock: 10, + protocolVersion: TLSYNC_PROTOCOL_VERSION, + schema: schemaV1.serialize(), + }) + + v1Server.room.handleNewSession(v2_id, v2_socket as any) + v1Server.room.handleMessage(v2_id as any, { + type: 'connect', + connectRequestId: 'test', + lastServerClock: 10, + protocolVersion: TLSYNC_PROTOCOL_VERSION, + schema: schemaV2.serialize(), + }) + + expect(v2_socket.sendMessage).toHaveBeenCalledWith({ + type: 'connect', + connectRequestId: 'test', + hydrationType: 'wipe_presence', + diff: {}, + protocolVersion: TLSYNC_PROTOCOL_VERSION, + schema: schemaV1.serialize(), + serverClock: 10, + } satisfies TLSocketServerSentEvent) + + expect(v1_socket.sendMessage).toHaveBeenCalledWith({ + type: 'connect', + connectRequestId: 'test', + hydrationType: 'wipe_presence', + diff: {}, + protocolVersion: TLSYNC_PROTOCOL_VERSION, + schema: schemaV1.serialize(), + serverClock: 10, + } satisfies TLSocketServerSentEvent) + ;(v2_socket.sendMessage as jest.Mock).mockClear() + ;(v1_socket.sendMessage as jest.Mock).mockClear() + + return { + v1Server, + v1_id, + v2_id, + v2SendMessage: v2_socket.sendMessage as jest.Mock, + v1SendMessage: v1_socket.sendMessage as jest.Mock, + steve, + jeff, + annie, + } + } + + let data: ReturnType + + beforeEach(() => { + data = setup() + }) + + it('allows deletions from v2 client', () => { + const { v1Server, v2_id, v2SendMessage, steve } = data + v1Server.room.handleMessage(v2_id as any, { + type: 'push', + clientClock: 1, + diff: { + [steve.id]: [RecordOpType.Remove], + }, + }) + + expect(v2SendMessage).toHaveBeenCalledWith({ + type: 'push_result', + action: 'commit', + clientClock: 1, + serverClock: 11, + } satisfies TLSocketServerSentEvent) + }) + + it('applies changes atomically', () => { + data.v1Server.room.handleMessage(data.v2_id, { + type: 'push', + clientClock: 1, + diff: { + [data.jeff.id]: [RecordOpType.Remove], + [data.steve.id]: [RecordOpType.Remove], + [data.annie.id]: [RecordOpType.Put, { ...data.annie, birthdate: '1999-02-21' } as any], + }, + }) + + expect(data.v2SendMessage).toHaveBeenCalledWith({ + type: 'incompatibility_error', + reason: TLIncompatibilityReason.ServerTooOld, + } satisfies TLSocketServerSentEvent) + + expect(data.v1SendMessage).not.toHaveBeenCalled() + expect(data.v1Server.room.state.get().documents[data.jeff.id]).toBeDefined() + expect(data.v1Server.room.state.get().documents[data.steve.id]).toBeDefined() + }) + + it('cannot send patches to v2 clients', () => { + data.v1Server.room.handleMessage(data.v1_id, { + type: 'push', + clientClock: 1, + diff: { + [data.steve.id]: [RecordOpType.Patch, { age: [ValueOpType.Put, 24] }], + }, + }) + + expect(data.v1SendMessage).toHaveBeenCalledWith({ + type: 'push_result', + action: 'commit', + clientClock: 1, + serverClock: 11, + } satisfies TLSocketServerSentEvent) + + expect(data.v2SendMessage).toHaveBeenCalledWith({ + type: 'incompatibility_error', + reason: TLIncompatibilityReason.ServerTooOld, + } satisfies TLSocketServerSentEvent) + }) + + it('cannot apply patches from v2 clients', () => { + data.v1Server.room.handleMessage(data.v2_id, { + type: 'push', + clientClock: 1, + diff: { + [data.steve.id]: [RecordOpType.Patch, { birthdate: [ValueOpType.Put, 'tomorrow'] }], + }, + }) + + expect(data.v2SendMessage).toHaveBeenCalledWith({ + type: 'incompatibility_error', + reason: TLIncompatibilityReason.ServerTooOld, + } satisfies TLSocketServerSentEvent) + + expect(data.v1SendMessage).not.toHaveBeenCalled() + }) + + it('cannot apply puts from v2 clients', () => { + data.v1Server.room.handleMessage(data.v2_id, { + type: 'push', + clientClock: 1, + diff: { + [data.steve.id]: [RecordOpType.Put, { ...data.steve, birthdate: 'today' } as any], + }, + }) + + expect(data.v2SendMessage).toHaveBeenCalledWith({ + type: 'incompatibility_error', + reason: TLIncompatibilityReason.ServerTooOld, + } satisfies TLSocketServerSentEvent) + + expect(data.v1SendMessage).not.toHaveBeenCalled() + }) +}) + +describe('when the client is too old', () => { + function setup() { + const steve = UserV2.create({ + id: UserV2.createId('steve'), + name: 'steve', + birthdate: null, + }) + const jeff = UserV2.create({ id: UserV2.createId('jeff'), name: 'jeff', birthdate: null }) + const annie = UserV2.create({ + id: UserV2.createId('annie'), + name: 'annie', + birthdate: null, + }) + const v2Server = new TestServer(schemaV2, { + clock: 10, + documents: [ + { + state: steve, + lastChangedClock: 10, + }, + { + state: jeff, + lastChangedClock: 10, + }, + { + state: annie, + lastChangedClock: 10, + }, + ], + schema: schemaV1.serialize(), + tombstones: {}, + }) + + const v2Id = 'test_upgrade_v2' + const v2Socket = mockSocket() + + const v2SendMessage = v2Socket.sendMessage as jest.Mock + + const v1Id = 'test_upgrade_v1' + const v1Socket = mockSocket() + + const v1SendMessage = v1Socket.sendMessage as jest.Mock + + v2Server.room.handleNewSession(v1Id, v1Socket as any) + v2Server.room.handleMessage(v1Id, { + type: 'connect', + connectRequestId: 'test', + lastServerClock: 10, + protocolVersion: TLSYNC_PROTOCOL_VERSION, + schema: schemaV1.serialize(), + }) + + v2Server.room.handleNewSession(v2Id, v2Socket) + v2Server.room.handleMessage(v2Id, { + type: 'connect', + connectRequestId: 'test', + lastServerClock: 10, + protocolVersion: TLSYNC_PROTOCOL_VERSION, + schema: schemaV2.serialize(), + }) + + expect(v2SendMessage).toHaveBeenCalledWith({ + type: 'connect', + connectRequestId: 'test', + hydrationType: 'wipe_presence', + diff: {}, + protocolVersion: TLSYNC_PROTOCOL_VERSION, + schema: schemaV2.serialize(), + serverClock: 10, + } satisfies TLSocketServerSentEvent) + + expect(v1SendMessage).toHaveBeenCalledWith({ + type: 'connect', + connectRequestId: 'test', + hydrationType: 'wipe_presence', + diff: {}, + protocolVersion: TLSYNC_PROTOCOL_VERSION, + schema: schemaV2.serialize(), + serverClock: 10, + } satisfies TLSocketServerSentEvent) + + v2SendMessage.mockClear() + v1SendMessage.mockClear() + + return { + v2Server, + v2Id, + v1Id, + v2SendMessage, + v1SendMessage, + steve, + jeff, + annie, + } + } + + let data: ReturnType + + beforeEach(() => { + data = setup() + }) + + it('allows deletions from v1 client', () => { + data.v2Server.room.handleMessage(data.v2Id, { + type: 'push', + clientClock: 1, + diff: { + [data.steve.id]: [RecordOpType.Remove], + }, + }) + + expect(data.v2SendMessage).toHaveBeenCalledWith({ + type: 'push_result', + action: 'commit', + clientClock: 1, + serverClock: 11, + } satisfies TLSocketServerSentEvent) + }) + + it('can handle patches from older clients', () => { + data.v2Server.room.handleMessage(data.v1Id, { + type: 'push', + clientClock: 1, + diff: { + [data.steve.id]: [RecordOpType.Patch, { name: [ValueOpType.Put, 'Jeff'] }], + }, + }) + + expect(data.v1SendMessage).toHaveBeenCalledWith({ + type: 'push_result', + action: 'commit', + clientClock: 1, + serverClock: 11, + } satisfies TLSocketServerSentEvent) + + expect(data.v2SendMessage).toHaveBeenCalledWith({ + type: 'patch', + diff: { + [data.steve.id]: [ + RecordOpType.Patch, + { + name: [ValueOpType.Put, 'Jeff'], + }, + ], + }, + serverClock: 11, + } satisfies TLSocketServerSentEvent) + }) +}) + +describe('when the client is the same version', () => { + function setup() { + const steve = UserV2.create({ + id: UserV2.createId('steve'), + name: 'steve', + birthdate: null, + }) + const v2Server = new TestServer(schemaV2, { + clock: 10, + documents: [ + { + state: steve, + lastChangedClock: 10, + }, + ], + schema: schemaV2.serialize(), + tombstones: {}, + }) + + const aId = 'v2ClientA' + const aSocket = mockSocket() + + const bId = 'v2ClientB' + const bSocket = mockSocket() + + v2Server.room.handleNewSession(aId, aSocket) + v2Server.room.handleMessage(aId, { + type: 'connect', + connectRequestId: 'test', + lastServerClock: 10, + protocolVersion: TLSYNC_PROTOCOL_VERSION, + schema: JSON.parse(JSON.stringify(schemaV2.serialize())), + }) + + v2Server.room.handleNewSession(bId, bSocket) + v2Server.room.handleMessage(bId, { + type: 'connect', + connectRequestId: 'test', + lastServerClock: 10, + protocolVersion: TLSYNC_PROTOCOL_VERSION, + schema: JSON.parse(JSON.stringify(schemaV2.serialize())), + }) + + expect(aSocket.sendMessage).toHaveBeenCalledWith({ + type: 'connect', + connectRequestId: 'test', + hydrationType: 'wipe_presence', + diff: {}, + protocolVersion: TLSYNC_PROTOCOL_VERSION, + schema: schemaV2.serialize(), + serverClock: 10, + } satisfies TLSocketServerSentEvent) + + expect(bSocket.sendMessage).toHaveBeenCalledWith({ + type: 'connect', + connectRequestId: 'test', + hydrationType: 'wipe_presence', + diff: {}, + protocolVersion: TLSYNC_PROTOCOL_VERSION, + schema: schemaV2.serialize(), + serverClock: 10, + } satisfies TLSocketServerSentEvent) + ;(aSocket.sendMessage as jest.Mock).mockClear() + ;(bSocket.sendMessage as jest.Mock).mockClear() + + return { + v2Server, + aId, + bId, + v2ClientASendMessage: aSocket.sendMessage as jest.Mock, + v2ClientBSendMessage: bSocket.sendMessage as jest.Mock, + steve, + } + } + + let data: ReturnType + + beforeEach(() => { + data = setup() + }) + + it('sends minimal patches', () => { + data.v2Server.room.handleMessage(data.aId, { + type: 'push', + clientClock: 1, + diff: { + [data.steve.id]: [RecordOpType.Patch, { name: [ValueOpType.Put, 'Jeff'] }], + }, + }) + + expect(data.v2ClientASendMessage).toHaveBeenCalledWith({ + type: 'push_result', + action: 'commit', + clientClock: 1, + serverClock: 11, + } satisfies TLSocketServerSentEvent) + + expect(data.v2ClientBSendMessage).toHaveBeenCalledWith({ + type: 'patch', + diff: { + [data.steve.id]: [ + RecordOpType.Patch, + { + name: [ValueOpType.Put, 'Jeff'], + }, + ], + }, + serverClock: 11, + } satisfies TLSocketServerSentEvent) + }) +}) diff --git a/packages/tlsync/src/test/validation.test.ts b/packages/tlsync/src/test/validation.test.ts new file mode 100644 index 000000000..bcac51cb5 --- /dev/null +++ b/packages/tlsync/src/test/validation.test.ts @@ -0,0 +1,194 @@ +import { computed } from '@tldraw/state' +import { + RecordId, + Store, + StoreSchema, + UnknownRecord, + createRecordType, + defineMigrations, +} from '@tldraw/store' +import { TLSyncClient } from '../lib/TLSyncClient' +import { RecordOpType } from '../lib/diff' +import { TestServer } from './TestServer' +import { TestSocketPair } from './TestSocketPair' + +// @ts-expect-error +global.requestAnimationFrame = (cb: () => any) => { + cb() +} + +interface Book { + typeName: 'book' + id: RecordId + title: string +} +const Book = createRecordType('book', { + scope: 'document', + validator: { + validate: (record: unknown): Book => { + if (typeof record !== 'object' || record === null) { + throw new Error('Expected object') + } + if (!('title' in record)) { + throw new Error('Expected title') + } + if (typeof record.title !== 'string') { + throw new Error('Expected title to be a string') + } + return record as Book + }, + }, +}) +const BookWithoutValidator = createRecordType('book', { + scope: 'document', + validator: { validate: (record) => record as Book }, +}) +type Presence = UnknownRecord & { typeName: 'presence' } +const presenceType = createRecordType('presence', { + scope: 'presence', + validator: { validate: (record) => record as Presence }, +}) + +const schema = StoreSchema.create( + { book: Book, presence: presenceType }, + { + snapshotMigrations: defineMigrations({}), + } +) +const schemaWithoutValidator = StoreSchema.create( + { book: BookWithoutValidator, presence: presenceType }, + { + snapshotMigrations: defineMigrations({}), + } +) + +const disposables: Array<() => void> = [] +afterEach(() => { + for (const dispose of disposables) { + dispose() + } + disposables.length = 0 +}) + +async function makeTestInstance() { + const server = new TestServer(schema) + const socketPair = new TestSocketPair('test', server) + socketPair.connect() + + const flush = async () => { + await Promise.resolve() + while (socketPair.getNeedsFlushing()) { + socketPair.flushClientSentEvents() + socketPair.flushServerSentEvents() + } + } + const onSyncError = jest.fn() + const client = await new Promise>((resolve, reject) => { + const client = new TLSyncClient({ + store: new Store({ schema: schemaWithoutValidator, props: {} }), + socket: socketPair.clientSocket as any, + onLoad: resolve, + onLoadError: reject, + onSyncError, + presence: computed('', () => null), + }) + disposables.push(() => client.close()) + flush() + }) + + return { + server, + socketPair, + client, + flush, + onSyncError, + } +} + +it('rejects invalid put operations that create a new document', async () => { + const { client, flush, onSyncError, server } = await makeTestInstance() + + const prevServerDocs = server.room.getSnapshot().documents + + client.store.put([ + { + typeName: 'book', + id: Book.createId('1'), + // @ts-expect-error - deliberate invalid data + title: 123 as string, + }, + ]) + await flush() + + expect(onSyncError).toHaveBeenCalledTimes(1) + expect(onSyncError).toHaveBeenLastCalledWith('invalidRecord') + expect(server.room.getSnapshot().documents).toStrictEqual(prevServerDocs) +}) + +it('rejects invalid put operations that replace an existing document', async () => { + const { client, flush, onSyncError, server } = await makeTestInstance() + + let prevServerDocs = server.room.getSnapshot().documents + const book: Book = { typeName: 'book', id: Book.createId('1'), title: 'Annihilation' } + client.store.put([book]) + await flush() + + expect(onSyncError).toHaveBeenCalledTimes(0) + expect(server.room.getSnapshot().documents).not.toStrictEqual(prevServerDocs) + prevServerDocs = server.room.getSnapshot().documents + + client.socket.sendMessage({ + type: 'push', + // @ts-expect-error clientClock is private + clientClock: client.clientClock++, + diff: { + [book.id]: [ + RecordOpType.Put, + { + ...book, + // @ts-expect-error - deliberate invalid data + title: 123 as string, + }, + ], + }, + }) + await flush() + + expect(onSyncError).toHaveBeenCalledTimes(1) + expect(onSyncError).toHaveBeenLastCalledWith('invalidRecord') + expect(server.room.getSnapshot().documents).toStrictEqual(prevServerDocs) +}) + +it('rejects invalid update operations', async () => { + const { client, flush, onSyncError, server } = await makeTestInstance() + + let prevServerDocs = server.room.getSnapshot().documents + + // create the book + client.store.put([ + { + typeName: 'book', + id: Book.createId('1'), + title: 'The silence of the girls', + }, + ]) + await flush() + + expect(onSyncError).toHaveBeenCalledTimes(0) + expect(server.room.getSnapshot().documents).not.toStrictEqual(prevServerDocs) + prevServerDocs = server.room.getSnapshot().documents + + // update the title to be wrong + client.store.put([ + { + typeName: 'book', + id: Book.createId('1'), + // @ts-expect-error - deliberate invalid data + title: 123 as string, + }, + ]) + await flush() + expect(onSyncError).toHaveBeenCalledTimes(1) + expect(onSyncError).toHaveBeenLastCalledWith('invalidRecord') + expect(server.room.getSnapshot().documents).toStrictEqual(prevServerDocs) +}) diff --git a/packages/tlsync/tsconfig.json b/packages/tlsync/tsconfig.json new file mode 100644 index 000000000..436778805 --- /dev/null +++ b/packages/tlsync/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../config/tsconfig.base.json", + "include": ["src"], + "exclude": ["node_modules", "docs", ".tsbuild*"], + "compilerOptions": { + "outDir": "./.tsbuild", + "rootDir": "src" + }, + "references": [ + { "path": "../tldraw" }, + { "path": "../tlschema" }, + { "path": "../state" }, + { "path": "../store" }, + { "path": "../utils" } + ] +} diff --git a/scripts/check-scripts.ts b/scripts/check-scripts.ts index 0f7a54249..0f0e52b51 100644 --- a/scripts/check-scripts.ts +++ b/scripts/check-scripts.ts @@ -65,8 +65,7 @@ async function main({ fix }: { fix?: boolean }) { const packageScripts = packageJson.scripts let expected = - name.startsWith('@tldraw/') && - (relativePath.startsWith('bublic/packages/') || relativePath.startsWith('packages/')) + name.startsWith('@tldraw/') && relativePath.startsWith('packages/') ? packageJson.private ? expectedPackageScripts : expectedPublishedPackageScripts diff --git a/scripts/clean.sh b/scripts/clean.sh index d434a8e42..38c92e4b3 100755 --- a/scripts/clean.sh +++ b/scripts/clean.sh @@ -29,11 +29,11 @@ goodbye .tsbuild-dev goodbye .tsbuild-api goodbye .next -rm -rf {packages,bublic/packages}/*/api -rm -rf {packages,apps,bublic/packages,bublic/apps}/*/*.tgz -rm -rf {packages,apps,bublic/packages,bublic/apps}/vscode/extension/temp -rm -rf {packages,apps,bublic/packages,bublic/apps}/vscode/extension/editor -rm -rf bublic/apps/docs/content.json +rm -rf packages/*/api +rm -rf {packages,apps}/*/*.tgz +rm -rf {packages,apps}/vscode/extension/temp +rm -rf {packages,apps}/vscode/extension/editor +rm -rf apps/docs/content.json # need to run yarn directly # because yarn messes with the PATH, aliasing itself to some tmp dir diff --git a/scripts/deploy.ts b/scripts/deploy.ts new file mode 100644 index 000000000..531c4dd22 --- /dev/null +++ b/scripts/deploy.ts @@ -0,0 +1,486 @@ +import * as github from '@actions/github' +import { GetObjectCommand, ListObjectsV2Command, S3Client } from '@aws-sdk/client-s3' +import { Upload } from '@aws-sdk/lib-storage' +import assert from 'assert' +import { execSync } from 'child_process' +import { appendFileSync, existsSync, readdirSync, writeFileSync } from 'fs' +import path, { join } from 'path' +import { PassThrough } from 'stream' +import tar from 'tar' +import { exec } from './lib/exec' +import { makeEnv } from './lib/makeEnv' +import { nicelog } from './lib/nicelog' + +const worker = path.relative(process.cwd(), path.resolve(__dirname, '../apps/dotcom-worker')) +const assetUpload = path.relative( + process.cwd(), + path.resolve(__dirname, '../apps/dotcom-asset-upload') +) +const dotcom = path.relative(process.cwd(), path.resolve(__dirname, '../apps/dotcom')) + +// Do not use `process.env` directly in this script. Add your variable to `makeEnv` and use it via +// `env` instead. This makes sure that all required env vars are present. +const env = makeEnv([ + 'APP_ORIGIN', + 'ASSET_UPLOAD', + 'CLOUDFLARE_ACCOUNT_ID', + 'CLOUDFLARE_API_TOKEN', + 'DISCORD_DEPLOY_WEBHOOK_URL', + 'GC_MAPS_API_KEY', + 'RELEASE_COMMIT_HASH', + 'SENTRY_AUTH_TOKEN', + 'SENTRY_DSN', + 'SUPABASE_LITE_ANON_KEY', + 'SUPABASE_LITE_URL', + 'TLDRAW_ENV', + 'VERCEL_PROJECT_ID', + 'VERCEL_ORG_ID', + 'VERCEL_TOKEN', + 'WORKER_SENTRY_DSN', + 'MULTIPLAYER_SERVER', + 'GH_TOKEN', + 'R2_ACCESS_KEY_ID', + 'R2_ACCESS_KEY_SECRET', +]) + +const githubPrNumber = process.env.GITHUB_REF?.match(/refs\/pull\/(\d+)\/merge/)?.[1] +function getPreviewId() { + if (env.TLDRAW_ENV !== 'preview') return undefined + if (githubPrNumber) return `pr-${githubPrNumber}` + return process.env.TLDRAW_PREVIEW_ID ?? undefined +} +const previewId = getPreviewId() + +if (env.TLDRAW_ENV === 'preview' && !previewId) { + throw new Error( + 'If running preview deploys from outside of a PR action, TLDRAW_PREVIEW_ID env var must be set' + ) +} +const sha = + // if the event is 'pull_request', github.context.sha is an ephemeral merge commit + // while the actual commit we want to create the deployment for is the 'head' of the PR. + github.context.eventName === 'pull_request' + ? github.context.payload.pull_request?.head.sha + : github.context.sha + +const sentryReleaseName = `${env.TLDRAW_ENV}-${previewId ? previewId + '-' : ''}-${sha}` + +async function main() { + assert( + env.TLDRAW_ENV === 'staging' || env.TLDRAW_ENV === 'production' || env.TLDRAW_ENV === 'preview', + 'TLDRAW_ENV must be staging or production or preview' + ) + + await discordMessage(`--- **${env.TLDRAW_ENV} deploy pre-flight** ---`) + + await discordStep('[1/6] setting up deploy', async () => { + // make sure the tldraw .css files are built: + await exec('yarn', ['lazy', 'prebuild']) + + // link to vercel and supabase projects: + await vercelCli('link', ['--project', env.VERCEL_PROJECT_ID]) + }) + + // deploy pre-flight steps: + // 1. get the dotcom app ready to go (env vars and pre-build) + await discordStep('[2/6] building dotcom app', async () => { + await createSentryRelease() + await prepareDotcomApp() + await uploadSourceMaps() + await coalesceWithPreviousAssets(`${dotcom}/.vercel/output/static/assets`) + }) + + await discordStep('[3/6] cloudflare deploy dry run', async () => { + await deployAssetUploadWorker({ dryRun: true }) + await deployTlsyncWorker({ dryRun: true }) + }) + + // --- point of no return! do the deploy for real --- // + + await discordMessage(`--- **pre-flight complete, starting real deploy** ---`) + + // 2. deploy the cloudflare workers: + await discordStep('[4/6] deploying asset uploader to cloudflare', async () => { + await deployAssetUploadWorker({ dryRun: false }) + }) + await discordStep('[5/6] deploying multiplayer worker to cloudflare', async () => { + await deployTlsyncWorker({ dryRun: false }) + }) + + // 3. deploy the pre-build dotcom app: + const { deploymentUrl, inspectUrl } = await discordStep( + '[6/6] deploying dotcom app to vercel', + async () => { + return await deploySpa() + } + ) + + let deploymentAlias = null as null | string + + if (previewId) { + const aliasDomain = `${previewId}-preview-deploy.tldraw.com` + await discordStep('[7/6] aliasing preview deployment', async () => { + await vercelCli('alias', ['set', deploymentUrl, aliasDomain]) + }) + + deploymentAlias = `https://${aliasDomain}` + } + + nicelog('Creating deployment for', deploymentUrl) + await createGithubDeployment(deploymentAlias ?? deploymentUrl, inspectUrl) + + await discordMessage(`**Deploy complete!**`) +} + +async function prepareDotcomApp() { + // pre-build the app: + await exec('yarn', ['build-app'], { + env: { + NEXT_PUBLIC_TLDRAW_RELEASE_INFO: `${env.RELEASE_COMMIT_HASH} ${new Date().toISOString()}`, + ASSET_UPLOAD: previewId + ? `https://${previewId}-tldraw-assets.tldraw.workers.dev` + : env.ASSET_UPLOAD, + MULTIPLAYER_SERVER: previewId + ? `https://${previewId}-tldraw-multiplayer.tldraw.workers.dev` + : env.MULTIPLAYER_SERVER, + NEXT_PUBLIC_CONTROL_SERVER: 'https://control.tldraw.com', + NEXT_PUBLIC_GC_API_KEY: env.GC_MAPS_API_KEY, + SENTRY_AUTH_TOKEN: env.SENTRY_AUTH_TOKEN, + SENTRY_ORG: 'tldraw', + SENTRY_PROJECT: 'lite', + SUPABASE_KEY: env.SUPABASE_LITE_ANON_KEY, + SUPABASE_URL: env.SUPABASE_LITE_URL, + TLDRAW_ENV: env.TLDRAW_ENV, + }, + }) +} + +let didUpdateAssetUploadWorker = false +async function deployAssetUploadWorker({ dryRun }: { dryRun: boolean }) { + if (previewId && !didUpdateAssetUploadWorker) { + appendFileSync( + join(assetUpload, 'wrangler.toml'), + ` +[env.preview] +name = "${previewId}-tldraw-assets"` + ) + didUpdateAssetUploadWorker = true + } + await exec('yarn', ['wrangler', 'deploy', dryRun ? '--dry-run' : null, '--env', env.TLDRAW_ENV], { + pwd: assetUpload, + env: { + NODE_ENV: 'production', + // wrangler needs CI=1 set to prevent it from trying to do interactive prompts + CI: '1', + }, + }) +} + +let didUpdateTlsyncWorker = false +async function deployTlsyncWorker({ dryRun }: { dryRun: boolean }) { + if (previewId && !didUpdateTlsyncWorker) { + appendFileSync( + join(worker, 'wrangler.toml'), + ` +[env.preview] +name = "${previewId}-tldraw-multiplayer"` + ) + didUpdateTlsyncWorker = true + } + await exec( + 'yarn', + [ + 'wrangler', + 'deploy', + dryRun ? '--dry-run' : null, + '--env', + env.TLDRAW_ENV, + '--var', + `SUPABASE_URL:${env.SUPABASE_LITE_URL}`, + '--var', + `SUPABASE_KEY:${env.SUPABASE_LITE_ANON_KEY}`, + '--var', + `SENTRY_DSN:${env.WORKER_SENTRY_DSN}`, + '--var', + `TLDRAW_ENV:${env.TLDRAW_ENV}`, + '--var', + `APP_ORIGIN:${env.APP_ORIGIN}`, + ], + { + pwd: worker, + env: { + NODE_ENV: 'production', + // wrangler needs CI=1 set to prevent it from trying to do interactive prompts + CI: '1', + }, + } + ) +} + +type ExecOpts = NonNullable[2]> +async function vercelCli(command: string, args: string[], opts?: ExecOpts) { + return exec( + 'yarn', + [ + 'run', + '-T', + 'vercel', + command, + '--token', + env.VERCEL_TOKEN, + '--scope', + env.VERCEL_ORG_ID, + '--yes', + ...args, + ], + { + ...opts, + env: { + // specify org id via args instead of via env vars because otherwise it gets upset + // that there's no project id either + VERCEL_ORG_ID: env.VERCEL_ORG_ID, + VERCEL_PROJECT_ID: env.VERCEL_PROJECT_ID, + ...opts?.env, + }, + } + ) +} + +function sanitizeVariables(errorOutput: string): string { + const regex = /(--var\s+(\w+):[^ \n]+)/g + + const sanitizedOutput = errorOutput.replace(regex, (_, match) => { + const [variable] = match.split(':') + return `${variable}:*` + }) + + return sanitizedOutput +} + +async function discord(method: string, url: string, body: unknown): Promise { + const response = await fetch(`${env.DISCORD_DEPLOY_WEBHOOK_URL}${url}`, { + method, + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }) + if (!response.ok) { + throw new Error(`Discord webhook request failed: ${response.status} ${response.statusText}`) + } + return response.json() +} + +const AT_TEAM_MENTION = '<@&959380625100513310>' +async function discordMessage(content: string, { always = false }: { always?: boolean } = {}) { + const shouldNotify = env.TLDRAW_ENV === 'production' || always + if (!shouldNotify) { + return { + edit: () => { + // noop + }, + } + } + + const message = await discord('POST', '?wait=true', { content: sanitizeVariables(content) }) + + return { + edit: async (newContent: string) => { + await discord('PATCH', `/messages/${message.id}`, { content: sanitizeVariables(newContent) }) + }, + } +} + +async function discordStep(content: string, cb: () => Promise): Promise { + const message = await discordMessage(`${content}...`) + try { + const result = await cb() + await message.edit(`${content} ✅`) + return result + } catch (err) { + await message.edit(`${content} ❌`) + throw err + } +} + +async function deploySpa(): Promise<{ deploymentUrl: string; inspectUrl: string }> { + // both 'staging' and 'production' are deployed to vercel as 'production' deploys + // in separate 'projects' + const prod = env.TLDRAW_ENV !== 'preview' + const out = await vercelCli('deploy', ['--prebuilt', ...(prod ? ['--prod'] : [])], { + pwd: dotcom, + }) + + const previewURL = out.match(/Preview: (https:\/\/\S*)/)?.[1] + const inspectUrl = out.match(/Inspect: (https:\/\/\S*)/)?.[1] + const productionURL = out.match(/Production: (https:\/\/\S*)/)?.[1] + const deploymentUrl = previewURL ?? productionURL + + if (!deploymentUrl) { + throw new Error('Could not find deployment URL in vercel output ' + out) + } + if (!inspectUrl) { + throw new Error('Could not find inspect URL in vercel output ' + out) + } + + return { deploymentUrl, inspectUrl } +} + +// Creates a github 'deployment', which creates a 'View Deployment' button in the PR timeline. +async function createGithubDeployment(deploymentUrl: string, inspectUrl: string) { + const client = github.getOctokit(env.GH_TOKEN) + + const deployment = await client.rest.repos.createDeployment({ + owner: 'tldraw', + repo: 'tldraw', + ref: sha, + payload: { web_url: deploymentUrl }, + environment: env.TLDRAW_ENV, + transient_environment: true, + required_contexts: [], + auto_merge: false, + task: 'deploy', + }) + + await client.rest.repos.createDeploymentStatus({ + owner: 'tldraw', + repo: 'tldraw', + deployment_id: (deployment.data as any).id, + state: 'success', + environment_url: deploymentUrl, + log_url: inspectUrl, + }) +} + +const sentryEnv = { + SENTRY_AUTH_TOKEN: env.SENTRY_AUTH_TOKEN, + SENTRY_ORG: 'tldraw', + SENTRY_PROJECT: 'lite', +} + +const execSentry = (command: string, args: string[]) => + exec(`yarn`, ['run', '-T', 'sentry-cli', command, ...args], { env: sentryEnv }) + +async function createSentryRelease() { + await execSentry('releases', ['new', sentryReleaseName]) + if (!existsSync(`${dotcom}/sentry-release-name.ts`)) { + throw new Error('sentry-release-name.ts does not exist') + } + writeFileSync( + `${dotcom}/sentry-release-name.ts`, + `// This file is replaced during deployments to point to a meaningful release name in Sentry.` + + `// DO NOT MESS WITH THIS LINE OR THE ONE BELOW IT. I WILL FIND YOU\n` + + `export const sentryReleaseName = '${sentryReleaseName}'` + ) +} + +async function uploadSourceMaps() { + const sourceMapDir = `${dotcom}/dist/assets` + + await execSentry('sourcemaps', ['upload', '--release', sentryReleaseName, sourceMapDir]) + execSync('rm -rf ./.next/static/chunks/**/*.map') +} + +const R2_URL = `https://c34edc4e76350954b63adebde86d5eb1.r2.cloudflarestorage.com` +const R2_BUCKET = `dotcom-deploy-assets-cache` + +const R2 = new S3Client({ + region: 'auto', + endpoint: R2_URL, + credentials: { + accessKeyId: env.R2_ACCESS_KEY_ID, + secretAccessKey: env.R2_ACCESS_KEY_SECRET, + }, +}) + +/** + * When we run a vite prod build it creates a folder in the output dir called assets in which + * every file includes a hash of its contents in the filename. These files include files that + * are 'imported' by the js bundle, e.g. svg and css files, along with the js bundle itself + * (split into chunks). + * + * By default, when we deploy a new version of the app it will replace the previous versions + * of the files with new versions. This is problematic when we make a new deploy because if + * existing users have tldraw open in tabs while we make the new deploy, they might still try + * to fetch .js chunks or images which are no longer present on the server and may not have + * been cached by vercel's CDN in their location (or at all). + * + * To avoid this, we keep track of the assets from previous deploys in R2 and include them in the + * new deploy. This way, if a user has an old version of the app open in a tab, they will still + * be able to fetch the old assets from the previous deploy. + */ +async function coalesceWithPreviousAssets(assetsDir: string) { + nicelog('Saving assets to R2 bucket') + const objectKey = `${previewId ?? env.TLDRAW_ENV}/${new Date().toISOString()}+${sha}.tar.gz` + const pack = tar.c({ gzip: true, cwd: assetsDir }, readdirSync(assetsDir)) + // Need to pipe through a PassThrough here because the tar stream is not a first class node stream + // and AWS's sdk expects a node stream (it checks `Body instanceof streams.Readable`) + const Body = new PassThrough() + pack.pipe(Body) + await new Upload({ + client: R2, + params: { + Bucket: R2_BUCKET, + Key: objectKey, + Body, + }, + }).done() + + nicelog('Extracting previous assets from R2 bucket') + const { Contents } = await R2.send( + new ListObjectsV2Command({ + Bucket: R2_BUCKET, + Prefix: `${previewId ?? env.TLDRAW_ENV}/`, + }) + ) + const [mostRecent, ...others] = + // filter out the one we just uploaded + Contents?.filter((obj) => obj.Key !== objectKey).sort( + (a, b) => (b.LastModified?.getTime() ?? 0) - (a.LastModified?.getTime() ?? 0) + ) ?? [] + + if (!mostRecent) { + nicelog('No previous assets found') + return + } + + // Always include the assets from the directly previous build, but also if there + // have been more deploys in the last two weeks, include those too. + const twoWeeks = 1000 * 60 * 60 * 24 * 14 + const recentOthers = others.filter( + (o) => (o.LastModified?.getTime() ?? 0) > Date.now() - twoWeeks + ) + const objectsToFetch = [mostRecent, ...recentOthers] + + nicelog( + `Fetching ${objectsToFetch.length} previous assets from R2 bucket:`, + objectsToFetch.map((k) => k.Key) + ) + for (const obj of objectsToFetch) { + const { Body } = await R2.send( + new GetObjectCommand({ + Bucket: R2_BUCKET, + Key: obj.Key, + }) + ) + if (!Body) { + throw new Error(`Could not fetch object ${obj.Key}`) + } + // pipe into untar + // `keep-existing` is important here because we don't want to overwrite the new assets + // if they have the same name as the old assets becuase they will have different sentry debugIds + // and it will mess up the inline source viewer on sentry errors. + const out = tar.x({ cwd: assetsDir, 'keep-existing': true }) + for await (const chunk of Body?.transformToWebStream() as any as AsyncIterable) { + out.write(chunk) + } + out.end() + } +} + +main().catch(async (err) => { + // don't notify discord on preview builds + if (env.TLDRAW_ENV !== 'preview') { + await discordMessage(`${AT_TEAM_MENTION} Deploy failed: ${err.stack}`, { always: true }) + } + console.error(err) + process.exit(1) +}) diff --git a/scripts/lib/file.ts b/scripts/lib/file.ts index 22d712575..468489ea2 100644 --- a/scripts/lib/file.ts +++ b/scripts/lib/file.ts @@ -1,15 +1,23 @@ +import { existsSync, readFileSync } from 'fs' import { readFile, writeFile as writeFileUnchecked } from 'fs/promises' import json5 from 'json5' -import { basename, dirname, join, relative } from 'path' +import { dirname, join, relative } from 'path' import prettier from 'prettier' import { fileURLToPath } from 'url' import { nicelog } from './nicelog' const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) -const isBublic = basename(join(__dirname, '../..')) === 'bublic' -export const REPO_ROOT = join(__dirname, isBublic ? '../../../' : '../..') -export const BUBLIC_ROOT = join(__dirname, '../..') +export const REPO_ROOT = join(__dirname, '../..') + +const _rootPackageJsonPath = join(REPO_ROOT, 'package.json') +if (!existsSync(_rootPackageJsonPath)) { + throw new Error('expected to find package.json in REPO_ROOT') +} +const _rootPackageJson = JSON.parse(readFileSync(_rootPackageJsonPath, 'utf8')) +if (_rootPackageJson['name'] !== '@tldraw/monorepo') { + throw new Error('expected to find @tldraw/monorepo in REPO_ROOT package.json') +} export async function readJsonIfExists(file: string) { const fileContents = await readFileIfExists(file) diff --git a/scripts/lib/makeEnv.ts b/scripts/lib/makeEnv.ts new file mode 100644 index 000000000..27f5bd3cc --- /dev/null +++ b/scripts/lib/makeEnv.ts @@ -0,0 +1,18 @@ +export function makeEnv( + keys: Keys +): Record { + const env = {} as Record + const missingVars = [] + for (const key of keys) { + const value = process.env[key] + if (value === undefined) { + missingVars.push(key) + continue + } + env[key] = value + } + if (missingVars.length > 0) { + throw new Error(`Missing environment variables: ${missingVars.join(', ')}`) + } + return env as Record +} diff --git a/scripts/lib/nicelog.ts b/scripts/lib/nicelog.ts index c02bc689b..72d89109e 100644 --- a/scripts/lib/nicelog.ts +++ b/scripts/lib/nicelog.ts @@ -1,4 +1,3 @@ export function nicelog(...args: any[]) { - // eslint-disable-next-line no-console console.log(...args) } diff --git a/scripts/lib/publishing.ts b/scripts/lib/publishing.ts index 4675f4d05..87fc114c9 100644 --- a/scripts/lib/publishing.ts +++ b/scripts/lib/publishing.ts @@ -4,7 +4,7 @@ import { existsSync, readFileSync, readdirSync, writeFileSync } from 'fs' import path, { join } from 'path' import { compare, parse } from 'semver' import { exec } from './exec' -import { BUBLIC_ROOT } from './file' +import { REPO_ROOT } from './file' import { nicelog } from './nicelog' export type PackageDetails = { @@ -34,9 +34,9 @@ function getPackageDetails(dir: string): PackageDetails | null { } export function getAllPackageDetails(): Record { - const dirs = readdirSync(join(BUBLIC_ROOT, 'packages')) + const dirs = readdirSync(join(REPO_ROOT, 'packages')) const results = dirs - .map((dir) => getPackageDetails(path.join(BUBLIC_ROOT, 'packages', dir))) + .map((dir) => getPackageDetails(path.join(REPO_ROOT, 'packages', dir))) .filter((x): x is PackageDetails => Boolean(x)) return Object.fromEntries(results.map((result) => [result.name, result])) diff --git a/scripts/package.json b/scripts/package.json index f7541b44b..1c1c90b0a 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -27,9 +27,13 @@ "infinite" ], "devDependencies": { + "@actions/github": "^6.0.0", "@auto-it/core": "^10.45.0", + "@aws-sdk/client-s3": "^3.440.0", + "@aws-sdk/lib-storage": "^3.440.0", "@types/is-ci": "^3.0.0", "@types/node": "^18.7.3", + "@types/tar": "^6.1.7", "@typescript-eslint/utils": "^5.59.0", "ast-types": "^0.14.2", "cross-fetch": "^3.1.5", @@ -50,6 +54,7 @@ "lint": "yarn run -T tsx lint.ts" }, "dependencies": { - "ignore": "^5.2.4" + "ignore": "^5.2.4", + "tar": "^6.2.0" } } diff --git a/scripts/prune-preview-deploys.ts b/scripts/prune-preview-deploys.ts new file mode 100644 index 000000000..36ec76d9c --- /dev/null +++ b/scripts/prune-preview-deploys.ts @@ -0,0 +1,84 @@ +import * as github from '@actions/github' +import { makeEnv } from './lib/makeEnv' +import { nicelog } from './lib/nicelog' + +// Do not use `process.env` directly in this script. Add your variable to `makeEnv` and use it via +// `env` instead. This makes sure that all required env vars are present. +const env = makeEnv(['CLOUDFLARE_ACCOUNT_ID', 'CLOUDFLARE_API_TOKEN', 'GH_TOKEN']) + +type ListWorkersResult = { + success: boolean + result: { id: string }[] +} + +const _isPrClosedCache = new Map() +async function isPrClosedForAWhile(prNumber: number) { + if (_isPrClosedCache.has(prNumber)) { + return _isPrClosedCache.get(prNumber)! + } + const prResult = await github.getOctokit(env.GH_TOKEN).rest.pulls.get({ + owner: 'tldraw', + repo: 'tldraw', + pull_number: prNumber, + }) + const twoDays = 1000 * 60 * 60 * 24 * 2 + const result = + prResult.data.state === 'closed' && + Date.now() - new Date(prResult.data.closed_at!).getTime() > twoDays + _isPrClosedCache.set(prNumber, result) + return result +} + +async function ListPreviewWorkerDeployments() { + const res = await fetch( + `https://api.cloudflare.com/client/v4/accounts/${env.CLOUDFLARE_ACCOUNT_ID}/workers/scripts`, + { + headers: { + Authorization: `Bearer ${env.CLOUDFLARE_API_TOKEN}`, + 'Content-Type': 'application/json', + }, + } + ) + + const data = (await res.json()) as ListWorkersResult + + if (!data.success) { + throw new Error('Failed to list workers ' + JSON.stringify(data)) + } + + return data.result.map((r) => r.id).filter((id) => id.match(/^pr-(\d+)-/)) +} + +async function deletePreviewWorkerDeployment(id: string) { + const res = await fetch( + `https://api.cloudflare.com/client/v4/accounts/${env.CLOUDFLARE_ACCOUNT_ID}/workers/scripts/${id}`, + { + method: 'DELETE', + headers: { + Authorization: `Bearer ${env.CLOUDFLARE_API_TOKEN}`, + 'Content-Type': 'application/json', + }, + } + ) + + if (!res.ok) { + throw new Error('Failed to delete worker ' + JSON.stringify(res)) + } +} + +async function main() { + const previewDeployments = await ListPreviewWorkerDeployments() + for (const deployment of previewDeployments) { + const prNumber = Number(deployment.match(/^pr-(\d+)-/)![1]) + if (await isPrClosedForAWhile(prNumber)) { + nicelog(`Deleting ${deployment} because PR is closed`) + await deletePreviewWorkerDeployment(deployment) + } else { + nicelog(`Skipping ${deployment} because PR is still open`) + } + } +} + +main() + +// clean up cloudflare preview deploys diff --git a/scripts/publish-new.ts b/scripts/publish-new.ts index 08efbd93b..7691b2cbb 100644 --- a/scripts/publish-new.ts +++ b/scripts/publish-new.ts @@ -3,7 +3,7 @@ import fetch from 'cross-fetch' import { assert } from 'node:console' import { parse } from 'semver' import { exec } from './lib/exec' -import { BUBLIC_ROOT } from './lib/file' +import { REPO_ROOT } from './lib/file' import { nicelog } from './lib/nicelog' import { getLatestVersion, publish, setAllVersions } from './lib/publishing' import { getAllWorkspacePackages } from './lib/workspace' @@ -62,7 +62,7 @@ async function main() { 'add', 'lerna.json', ...packageJsonFilesToAdd, - BUBLIC_ROOT + '/packages/*/src/**/version.ts', + REPO_ROOT + '/packages/*/src/**/version.ts', ]) // this creates a new commit diff --git a/scripts/refresh-assets.ts b/scripts/refresh-assets.ts index 7323069db..8942c96bc 100644 --- a/scripts/refresh-assets.ts +++ b/scripts/refresh-assets.ts @@ -2,8 +2,8 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from 'fs' import { join } from 'path' import { optimize } from 'svgo' import { - BUBLIC_ROOT, readJsonIfExists, + REPO_ROOT, writeCodeFile, writeFile, writeJsonFile, @@ -12,7 +12,7 @@ import { import { nicelog } from './lib/nicelog' // We'll need to copy the assets into these folders -const PUBLIC_FOLDER_PATHS = [join(BUBLIC_ROOT, 'packages', 'assets')] +const PUBLIC_FOLDER_PATHS = [join(REPO_ROOT, 'packages', 'assets')] const FONT_MAPPING: Record = { 'IBMPlexMono-Medium': 'monospace', @@ -21,7 +21,7 @@ const FONT_MAPPING: Record = { 'Shantell_Sans-Tldrawish': 'draw', } -const ASSETS_FOLDER_PATH = join(BUBLIC_ROOT, 'assets') +const ASSETS_FOLDER_PATH = join(REPO_ROOT, 'assets') const collectedAssetUrls: { fonts: Record @@ -92,7 +92,7 @@ async function copyIcons() { await writeCodeFile( 'scripts/refresh-assets.ts', 'typescript', - join(BUBLIC_ROOT, 'packages', 'tldraw', 'src', 'lib', 'ui', 'icon-types.ts'), + join(REPO_ROOT, 'packages', 'tldraw', 'src', 'lib', 'ui', 'icon-types.ts'), iconTypeFile ) @@ -202,7 +202,7 @@ async function copyTranslations() { // Create hardcoded files const uiPath = join( - BUBLIC_ROOT, + REPO_ROOT, 'packages', 'tldraw', 'src', @@ -219,7 +219,7 @@ async function copyTranslations() { /** @public */ export const LANGUAGES = ${JSON.stringify(languagesSource)} as const ` - const schemaPath = join(BUBLIC_ROOT, 'packages', 'tlschema', 'src', 'translations') + const schemaPath = join(REPO_ROOT, 'packages', 'tlschema', 'src', 'translations') const schemaLanguagesFilePath = join(schemaPath, 'languages.ts') await writeCodeFile( 'scripts/refresh-assets.ts', @@ -267,7 +267,7 @@ async function copyTranslations() { // 4. ASSET DECLARATION FILES async function writeUrlBasedAssetDeclarationFile() { - const codeFilePath = join(BUBLIC_ROOT, 'packages', 'assets', 'urls.js') + const codeFilePath = join(REPO_ROOT, 'packages', 'assets', 'urls.js') const codeFile = ` // eslint-disable-next-line @typescript-eslint/triple-slash-reference /// @@ -333,7 +333,7 @@ async function writeImportBasedAssetDeclarationFile(): Promise { } ` - const codeFilePath = join(BUBLIC_ROOT, 'packages', 'assets', 'imports.js') + const codeFilePath = join(REPO_ROOT, 'packages', 'assets', 'imports.js') await writeCodeFile( 'scripts/refresh-assets.ts', 'javascript', @@ -343,7 +343,7 @@ async function writeImportBasedAssetDeclarationFile(): Promise { } async function writeSelfHostedAssetDeclarationFile(): Promise { - const codeFilePath = join(BUBLIC_ROOT, 'packages', 'assets', 'selfHosted.js') + const codeFilePath = join(REPO_ROOT, 'packages', 'assets', 'selfHosted.js') const codeFile = ` // eslint-disable-next-line @typescript-eslint/triple-slash-reference /// @@ -391,7 +391,7 @@ async function writeAssetDeclarationDTSFile() { } ` - const assetDeclarationFilePath = join(BUBLIC_ROOT, 'packages', 'assets', 'types.d.ts') + const assetDeclarationFilePath = join(REPO_ROOT, 'packages', 'assets', 'types.d.ts') await writeCodeFile('scripts/refresh-assets.ts', 'typescript', assetDeclarationFilePath, dts) } diff --git a/public-yarn.lock b/yarn.lock similarity index 84% rename from public-yarn.lock rename to yarn.lock index 284655d04..25fd19340 100644 --- a/public-yarn.lock +++ b/yarn.lock @@ -12,6 +12,28 @@ __metadata: languageName: node linkType: hard +"@actions/github@npm:^6.0.0": + version: 6.0.0 + resolution: "@actions/github@npm:6.0.0" + dependencies: + "@actions/http-client": ^2.2.0 + "@octokit/core": ^5.0.1 + "@octokit/plugin-paginate-rest": ^9.0.0 + "@octokit/plugin-rest-endpoint-methods": ^10.0.0 + checksum: 81831a78377175d8825fc0b94247ff366c0e87ad1dfa48df9b30b8659506f216dcf1e2d3124fcd318839b92c24ba20165e238b3cc11a34db89c69c40825e9ccf + languageName: node + linkType: hard + +"@actions/http-client@npm:^2.2.0": + version: 2.2.0 + resolution: "@actions/http-client@npm:2.2.0" + dependencies: + tunnel: ^0.0.6 + undici: ^5.25.4 + checksum: 075fc21e8c05e865239bfc5cc91ce42aff7ac7877a5828145545cb27c572f74af8f96f90233f3ba2376525a9032bb8eadebd7221c007ce62459b99d5d2362f94 + languageName: node + linkType: hard + "@adobe/css-tools@npm:^4.0.1": version: 4.3.2 resolution: "@adobe/css-tools@npm:4.3.2" @@ -29,6 +51,19 @@ __metadata: languageName: node linkType: hard +"@apideck/better-ajv-errors@npm:^0.3.1": + version: 0.3.6 + resolution: "@apideck/better-ajv-errors@npm:0.3.6" + dependencies: + json-schema: ^0.4.0 + jsonpointer: ^5.0.0 + leven: ^3.1.0 + peerDependencies: + ajv: ">=8" + checksum: b70ec9aae3b30ba1ac06948e585cd96aabbfe7ef6a1c27dc51e56c425f01290a58e9beb19ed95ee64da9f32df3e9276cd1ea58e78792741d74a519cb56955491 + languageName: node + linkType: hard + "@auto-it/bot-list@npm:10.46.0": version: 10.46.0 resolution: "@auto-it/bot-list@npm:10.46.0" @@ -148,6 +183,684 @@ __metadata: languageName: node linkType: hard +"@aws-crypto/crc32@npm:3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/crc32@npm:3.0.0" + dependencies: + "@aws-crypto/util": ^3.0.0 + "@aws-sdk/types": ^3.222.0 + tslib: ^1.11.1 + checksum: 9fdb3e837fc54119b017ea34fd0a6d71d2c88075d99e1e818a5158e0ad30ced67ddbcc423a11ceeef6cc465ab5ffd91830acab516470b48237ca7abd51be9642 + languageName: node + linkType: hard + +"@aws-crypto/crc32c@npm:3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/crc32c@npm:3.0.0" + dependencies: + "@aws-crypto/util": ^3.0.0 + "@aws-sdk/types": ^3.222.0 + tslib: ^1.11.1 + checksum: 0a116b5d1c5b09a3dde65aab04a07b32f543e87b68f2d175081e3f4a1a17502343f223d691dd883ace1ddce65cd40093673e7c7415dcd99062202ba87ffb4038 + languageName: node + linkType: hard + +"@aws-crypto/ie11-detection@npm:^3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/ie11-detection@npm:3.0.0" + dependencies: + tslib: ^1.11.1 + checksum: 299b2ddd46eddac1f2d54d91386ceb37af81aef8a800669281c73d634ed17fd855dcfb8b3157f2879344b93a2666a6d602550eb84b71e4d7868100ad6da8f803 + languageName: node + linkType: hard + +"@aws-crypto/sha1-browser@npm:3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/sha1-browser@npm:3.0.0" + dependencies: + "@aws-crypto/ie11-detection": ^3.0.0 + "@aws-crypto/supports-web-crypto": ^3.0.0 + "@aws-crypto/util": ^3.0.0 + "@aws-sdk/types": ^3.222.0 + "@aws-sdk/util-locate-window": ^3.0.0 + "@aws-sdk/util-utf8-browser": ^3.0.0 + tslib: ^1.11.1 + checksum: 78c379e105a0c4e7b2ed745dffd8f55054d7dde8b350b61de682bbc3cd081a50e2f87861954fa9cd53c7ea711ebca1ca0137b14cb36483efc971f60f573cf129 + languageName: node + linkType: hard + +"@aws-crypto/sha256-browser@npm:3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/sha256-browser@npm:3.0.0" + dependencies: + "@aws-crypto/ie11-detection": ^3.0.0 + "@aws-crypto/sha256-js": ^3.0.0 + "@aws-crypto/supports-web-crypto": ^3.0.0 + "@aws-crypto/util": ^3.0.0 + "@aws-sdk/types": ^3.222.0 + "@aws-sdk/util-locate-window": ^3.0.0 + "@aws-sdk/util-utf8-browser": ^3.0.0 + tslib: ^1.11.1 + checksum: ca89456bf508db2e08060a7f656460db97ac9a15b11e39d6fa7665e2b156508a1758695bff8e82d0a00178d6ac5c36f35eb4bcfac2e48621265224ca14a19bd2 + languageName: node + linkType: hard + +"@aws-crypto/sha256-js@npm:3.0.0, @aws-crypto/sha256-js@npm:^3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/sha256-js@npm:3.0.0" + dependencies: + "@aws-crypto/util": ^3.0.0 + "@aws-sdk/types": ^3.222.0 + tslib: ^1.11.1 + checksum: 644ded32ea310237811afae873d3c7320739cb6f6cc39dced9c94801379e68e5ee2cca0c34f0384793fa9e750a7e0a5e2468f95754bd08e6fd72ab833c8fe23c + languageName: node + linkType: hard + +"@aws-crypto/supports-web-crypto@npm:^3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/supports-web-crypto@npm:3.0.0" + dependencies: + tslib: ^1.11.1 + checksum: 35479a1558db9e9a521df6877a99f95670e972c602f2a0349303477e5d638a5baf569fb037c853710e382086e6fd77e8ed58d3fb9b49f6e1186a9d26ce7be006 + languageName: node + linkType: hard + +"@aws-crypto/util@npm:^3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/util@npm:3.0.0" + dependencies: + "@aws-sdk/types": ^3.222.0 + "@aws-sdk/util-utf8-browser": ^3.0.0 + tslib: ^1.11.1 + checksum: d29d5545048721aae3d60b236708535059733019a105f8a64b4e4a8eab7cf8dde1546dc56bff7de20d36140a4d1f0f4693e639c5732a7059273a7b1e56354776 + languageName: node + linkType: hard + +"@aws-sdk/client-s3@npm:^3.440.0": + version: 3.490.0 + resolution: "@aws-sdk/client-s3@npm:3.490.0" + dependencies: + "@aws-crypto/sha1-browser": 3.0.0 + "@aws-crypto/sha256-browser": 3.0.0 + "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/client-sts": 3.490.0 + "@aws-sdk/core": 3.490.0 + "@aws-sdk/credential-provider-node": 3.490.0 + "@aws-sdk/middleware-bucket-endpoint": 3.489.0 + "@aws-sdk/middleware-expect-continue": 3.489.0 + "@aws-sdk/middleware-flexible-checksums": 3.489.0 + "@aws-sdk/middleware-host-header": 3.489.0 + "@aws-sdk/middleware-location-constraint": 3.489.0 + "@aws-sdk/middleware-logger": 3.489.0 + "@aws-sdk/middleware-recursion-detection": 3.489.0 + "@aws-sdk/middleware-sdk-s3": 3.489.0 + "@aws-sdk/middleware-signing": 3.489.0 + "@aws-sdk/middleware-ssec": 3.489.0 + "@aws-sdk/middleware-user-agent": 3.489.0 + "@aws-sdk/region-config-resolver": 3.489.0 + "@aws-sdk/signature-v4-multi-region": 3.489.0 + "@aws-sdk/types": 3.489.0 + "@aws-sdk/util-endpoints": 3.489.0 + "@aws-sdk/util-user-agent-browser": 3.489.0 + "@aws-sdk/util-user-agent-node": 3.489.0 + "@aws-sdk/xml-builder": 3.485.0 + "@smithy/config-resolver": ^2.0.23 + "@smithy/core": ^1.2.2 + "@smithy/eventstream-serde-browser": ^2.0.16 + "@smithy/eventstream-serde-config-resolver": ^2.0.16 + "@smithy/eventstream-serde-node": ^2.0.16 + "@smithy/fetch-http-handler": ^2.3.2 + "@smithy/hash-blob-browser": ^2.0.17 + "@smithy/hash-node": ^2.0.18 + "@smithy/hash-stream-node": ^2.0.18 + "@smithy/invalid-dependency": ^2.0.16 + "@smithy/md5-js": ^2.0.18 + "@smithy/middleware-content-length": ^2.0.18 + "@smithy/middleware-endpoint": ^2.3.0 + "@smithy/middleware-retry": ^2.0.26 + "@smithy/middleware-serde": ^2.0.16 + "@smithy/middleware-stack": ^2.0.10 + "@smithy/node-config-provider": ^2.1.9 + "@smithy/node-http-handler": ^2.2.2 + "@smithy/protocol-http": ^3.0.12 + "@smithy/smithy-client": ^2.2.1 + "@smithy/types": ^2.8.0 + "@smithy/url-parser": ^2.0.16 + "@smithy/util-base64": ^2.0.1 + "@smithy/util-body-length-browser": ^2.0.1 + "@smithy/util-body-length-node": ^2.1.0 + "@smithy/util-defaults-mode-browser": ^2.0.24 + "@smithy/util-defaults-mode-node": ^2.0.32 + "@smithy/util-endpoints": ^1.0.8 + "@smithy/util-retry": ^2.0.9 + "@smithy/util-stream": ^2.0.24 + "@smithy/util-utf8": ^2.0.2 + "@smithy/util-waiter": ^2.0.16 + fast-xml-parser: 4.2.5 + tslib: ^2.5.0 + checksum: 28fc1c384bdba8c374ac8f150350feb5b6e8afaef76aaba2705d8d06d60dec9a209869888a3e91e75c88454084940916b1bc6070cc422d50712af7dd20a66ccf + languageName: node + linkType: hard + +"@aws-sdk/client-sso@npm:3.490.0": + version: 3.490.0 + resolution: "@aws-sdk/client-sso@npm:3.490.0" + dependencies: + "@aws-crypto/sha256-browser": 3.0.0 + "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/core": 3.490.0 + "@aws-sdk/middleware-host-header": 3.489.0 + "@aws-sdk/middleware-logger": 3.489.0 + "@aws-sdk/middleware-recursion-detection": 3.489.0 + "@aws-sdk/middleware-user-agent": 3.489.0 + "@aws-sdk/region-config-resolver": 3.489.0 + "@aws-sdk/types": 3.489.0 + "@aws-sdk/util-endpoints": 3.489.0 + "@aws-sdk/util-user-agent-browser": 3.489.0 + "@aws-sdk/util-user-agent-node": 3.489.0 + "@smithy/config-resolver": ^2.0.23 + "@smithy/core": ^1.2.2 + "@smithy/fetch-http-handler": ^2.3.2 + "@smithy/hash-node": ^2.0.18 + "@smithy/invalid-dependency": ^2.0.16 + "@smithy/middleware-content-length": ^2.0.18 + "@smithy/middleware-endpoint": ^2.3.0 + "@smithy/middleware-retry": ^2.0.26 + "@smithy/middleware-serde": ^2.0.16 + "@smithy/middleware-stack": ^2.0.10 + "@smithy/node-config-provider": ^2.1.9 + "@smithy/node-http-handler": ^2.2.2 + "@smithy/protocol-http": ^3.0.12 + "@smithy/smithy-client": ^2.2.1 + "@smithy/types": ^2.8.0 + "@smithy/url-parser": ^2.0.16 + "@smithy/util-base64": ^2.0.1 + "@smithy/util-body-length-browser": ^2.0.1 + "@smithy/util-body-length-node": ^2.1.0 + "@smithy/util-defaults-mode-browser": ^2.0.24 + "@smithy/util-defaults-mode-node": ^2.0.32 + "@smithy/util-endpoints": ^1.0.8 + "@smithy/util-retry": ^2.0.9 + "@smithy/util-utf8": ^2.0.2 + tslib: ^2.5.0 + checksum: f09172f7af1de8371dc4bd03ef18f2a260bd9868db9460912d392d0f2bcf4101e8f78eed440db6bd99437a3b8d1c1a107fb3bc904f20f4a99eb0a262c0644b14 + languageName: node + linkType: hard + +"@aws-sdk/client-sts@npm:3.490.0": + version: 3.490.0 + resolution: "@aws-sdk/client-sts@npm:3.490.0" + dependencies: + "@aws-crypto/sha256-browser": 3.0.0 + "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/core": 3.490.0 + "@aws-sdk/credential-provider-node": 3.490.0 + "@aws-sdk/middleware-host-header": 3.489.0 + "@aws-sdk/middleware-logger": 3.489.0 + "@aws-sdk/middleware-recursion-detection": 3.489.0 + "@aws-sdk/middleware-user-agent": 3.489.0 + "@aws-sdk/region-config-resolver": 3.489.0 + "@aws-sdk/types": 3.489.0 + "@aws-sdk/util-endpoints": 3.489.0 + "@aws-sdk/util-user-agent-browser": 3.489.0 + "@aws-sdk/util-user-agent-node": 3.489.0 + "@smithy/config-resolver": ^2.0.23 + "@smithy/core": ^1.2.2 + "@smithy/fetch-http-handler": ^2.3.2 + "@smithy/hash-node": ^2.0.18 + "@smithy/invalid-dependency": ^2.0.16 + "@smithy/middleware-content-length": ^2.0.18 + "@smithy/middleware-endpoint": ^2.3.0 + "@smithy/middleware-retry": ^2.0.26 + "@smithy/middleware-serde": ^2.0.16 + "@smithy/middleware-stack": ^2.0.10 + "@smithy/node-config-provider": ^2.1.9 + "@smithy/node-http-handler": ^2.2.2 + "@smithy/protocol-http": ^3.0.12 + "@smithy/smithy-client": ^2.2.1 + "@smithy/types": ^2.8.0 + "@smithy/url-parser": ^2.0.16 + "@smithy/util-base64": ^2.0.1 + "@smithy/util-body-length-browser": ^2.0.1 + "@smithy/util-body-length-node": ^2.1.0 + "@smithy/util-defaults-mode-browser": ^2.0.24 + "@smithy/util-defaults-mode-node": ^2.0.32 + "@smithy/util-endpoints": ^1.0.8 + "@smithy/util-middleware": ^2.0.9 + "@smithy/util-retry": ^2.0.9 + "@smithy/util-utf8": ^2.0.2 + fast-xml-parser: 4.2.5 + tslib: ^2.5.0 + checksum: 0fffd52bfae00c2e1beacf2cc58924131097efb7022a72f2d8c00e93e9c00ceb584d085b92160a2798d761726c7ca8492da5a7e8665d103a658ad96f9633e04e + languageName: node + linkType: hard + +"@aws-sdk/core@npm:3.490.0": + version: 3.490.0 + resolution: "@aws-sdk/core@npm:3.490.0" + dependencies: + "@smithy/core": ^1.2.2 + "@smithy/protocol-http": ^3.0.12 + "@smithy/signature-v4": ^2.0.0 + "@smithy/smithy-client": ^2.2.1 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 8d711ba4373b4ee074f5a225642cb91cd0052daf1e2880da1f5bd1b38197ea5c7888a4181710f715393f83618aacc4465262f9a80de8f4faf2644d5832e455b5 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-env@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/credential-provider-env@npm:3.489.0" + dependencies: + "@aws-sdk/types": 3.489.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: ef1c3fb9e12cb3b62be41d87ce168b704d462b6fb27d08eab58b003940e3b24b043f122efab42cdc41d0bd632074114f39b474c480705dc61ed6e879690fa93f + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-ini@npm:3.490.0": + version: 3.490.0 + resolution: "@aws-sdk/credential-provider-ini@npm:3.490.0" + dependencies: + "@aws-sdk/credential-provider-env": 3.489.0 + "@aws-sdk/credential-provider-process": 3.489.0 + "@aws-sdk/credential-provider-sso": 3.490.0 + "@aws-sdk/credential-provider-web-identity": 3.489.0 + "@aws-sdk/types": 3.489.0 + "@smithy/credential-provider-imds": ^2.0.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/shared-ini-file-loader": ^2.0.6 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 5dd24af06b3a2977c1ebd47621619b18b8d9f9bed04e8ac5daae9faedabe66d6c6792a62da54513d668f0b11315cab4fd41a85637e35646cec46588742e36b1f + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-node@npm:3.490.0": + version: 3.490.0 + resolution: "@aws-sdk/credential-provider-node@npm:3.490.0" + dependencies: + "@aws-sdk/credential-provider-env": 3.489.0 + "@aws-sdk/credential-provider-ini": 3.490.0 + "@aws-sdk/credential-provider-process": 3.489.0 + "@aws-sdk/credential-provider-sso": 3.490.0 + "@aws-sdk/credential-provider-web-identity": 3.489.0 + "@aws-sdk/types": 3.489.0 + "@smithy/credential-provider-imds": ^2.0.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/shared-ini-file-loader": ^2.0.6 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 52820573a6aff0ea8c7ca0699fb374ba9a94a9e9eb777c2b1225d3565fdde7a65c70fcbe9ec887cf555c2df73cdc341641791f1358a102e052869d0f4978d4d0 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-process@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/credential-provider-process@npm:3.489.0" + dependencies: + "@aws-sdk/types": 3.489.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/shared-ini-file-loader": ^2.0.6 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 19eb75f0222176f33ad5fbeb5a02b312a37a48966b73ff5c1af9974d439dc4c6faeffe745c2a23b0abb67ab876f9e10099b4a22c2ee9d15be5290556331a7a9a + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-sso@npm:3.490.0": + version: 3.490.0 + resolution: "@aws-sdk/credential-provider-sso@npm:3.490.0" + dependencies: + "@aws-sdk/client-sso": 3.490.0 + "@aws-sdk/token-providers": 3.489.0 + "@aws-sdk/types": 3.489.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/shared-ini-file-loader": ^2.0.6 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: ea3c9d180f13f676b7717a8e0aabdca33b36f2dbf27128dfa6f8172040aa895437ef03e08802b9d6b104a783b8606b8af923193ff54f79e92118aa54babaa311 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-web-identity@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/credential-provider-web-identity@npm:3.489.0" + dependencies: + "@aws-sdk/types": 3.489.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 6da681688c9a61b6e100c71983a1ba19ef34bcbd8d1e4e85e3399d86e74cdc8d81f57b6da1983319596c87ef1600471e82c5243fb909f9752eff99a6ef26a8ea + languageName: node + linkType: hard + +"@aws-sdk/lib-storage@npm:^3.440.0": + version: 3.490.0 + resolution: "@aws-sdk/lib-storage@npm:3.490.0" + dependencies: + "@smithy/abort-controller": ^2.0.1 + "@smithy/middleware-endpoint": ^2.3.0 + "@smithy/smithy-client": ^2.2.1 + buffer: 5.6.0 + events: 3.3.0 + stream-browserify: 3.0.0 + tslib: ^2.5.0 + peerDependencies: + "@aws-sdk/client-s3": ^3.0.0 + checksum: 253dc2e4fd33efcf5ee8d3ad76bb3f2ed1d4573e27fa8be23dc3b9b60bcf22e769684f3ab639c3551a66282e160e4b1693527ac49062c6a42b55716e7aadc6df + languageName: node + linkType: hard + +"@aws-sdk/middleware-bucket-endpoint@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.489.0" + dependencies: + "@aws-sdk/types": 3.489.0 + "@aws-sdk/util-arn-parser": 3.465.0 + "@smithy/node-config-provider": ^2.1.9 + "@smithy/protocol-http": ^3.0.12 + "@smithy/types": ^2.8.0 + "@smithy/util-config-provider": ^2.1.0 + tslib: ^2.5.0 + checksum: 209278da47bf7e3b0e3cd6bc0f80d56bd6fc06400c12041474d1fdef7d227b8120bd7ac0b471fd15e03433e8c9e5e8c7a20d4f5f057dc23edfecaf4b61ad1378 + languageName: node + linkType: hard + +"@aws-sdk/middleware-expect-continue@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/middleware-expect-continue@npm:3.489.0" + dependencies: + "@aws-sdk/types": 3.489.0 + "@smithy/protocol-http": ^3.0.12 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 2e4bc714e5c410d2362ec274e2f36ffbb35dc826030ac64ec2d34bf9a89b1defa3c8cd1e0d92c4f41ddb64035039ddfac927069152ea70437061f2f078d43cc1 + languageName: node + linkType: hard + +"@aws-sdk/middleware-flexible-checksums@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/middleware-flexible-checksums@npm:3.489.0" + dependencies: + "@aws-crypto/crc32": 3.0.0 + "@aws-crypto/crc32c": 3.0.0 + "@aws-sdk/types": 3.489.0 + "@smithy/is-array-buffer": ^2.0.0 + "@smithy/protocol-http": ^3.0.12 + "@smithy/types": ^2.8.0 + "@smithy/util-utf8": ^2.0.2 + tslib: ^2.5.0 + checksum: 522f234ba9fdf5fe1476c72b7480158723a52e15db0560a4d79fa00dd43935633104648a4be2d0d741afa90f5b4f5ced3e99ffdece9b1aa75f4c08389dbe5f10 + languageName: node + linkType: hard + +"@aws-sdk/middleware-host-header@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/middleware-host-header@npm:3.489.0" + dependencies: + "@aws-sdk/types": 3.489.0 + "@smithy/protocol-http": ^3.0.12 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 15072d8409066de23ae815ce84c9ab9ce509e368654a3438b398e0f1bec5e215a52a59388ede1fe4c7a40cb639759c50aab3a3562ffe17352a24e6f92f2ddd1e + languageName: node + linkType: hard + +"@aws-sdk/middleware-location-constraint@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/middleware-location-constraint@npm:3.489.0" + dependencies: + "@aws-sdk/types": 3.489.0 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: d9499c897ef1f89afafdc2b4ebaa32cb31396cd7412ddaab545427820c33ef90e6f3e9f16b82607cc7a0d402994034b0c51255c06e2bd15f30d618d10f8bcbc6 + languageName: node + linkType: hard + +"@aws-sdk/middleware-logger@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/middleware-logger@npm:3.489.0" + dependencies: + "@aws-sdk/types": 3.489.0 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 10fa2538663512158f7a5889da693b0351f1f464457e45bc02199540adf6985ddfda81fe36dbdddabe1fe12709118fa106a365988e691fab834c9d93bf34329e + languageName: node + linkType: hard + +"@aws-sdk/middleware-recursion-detection@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/middleware-recursion-detection@npm:3.489.0" + dependencies: + "@aws-sdk/types": 3.489.0 + "@smithy/protocol-http": ^3.0.12 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: bf1592f0577e8a5be5857100c288c0de0895b98b00d1947403d9ff99936d366cd3e8ae4838560ee68ff0d5fb74f26c2f139f23fee8b72ce8f753a742b467bb02 + languageName: node + linkType: hard + +"@aws-sdk/middleware-sdk-s3@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/middleware-sdk-s3@npm:3.489.0" + dependencies: + "@aws-sdk/types": 3.489.0 + "@aws-sdk/util-arn-parser": 3.465.0 + "@smithy/node-config-provider": ^2.1.9 + "@smithy/protocol-http": ^3.0.12 + "@smithy/signature-v4": ^2.0.0 + "@smithy/smithy-client": ^2.2.1 + "@smithy/types": ^2.8.0 + "@smithy/util-config-provider": ^2.1.0 + tslib: ^2.5.0 + checksum: 4a1541d7e21172a270cf3965171abcfd1dcb1fc98eac982fb2d3f8dbc10cfecec5ea13bfb27ada66c1a40a6446d027399818edabf1180b340e496eb6826fa8d7 + languageName: node + linkType: hard + +"@aws-sdk/middleware-signing@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/middleware-signing@npm:3.489.0" + dependencies: + "@aws-sdk/types": 3.489.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/protocol-http": ^3.0.12 + "@smithy/signature-v4": ^2.0.0 + "@smithy/types": ^2.8.0 + "@smithy/util-middleware": ^2.0.9 + tslib: ^2.5.0 + checksum: bf50942d8ca7baf2d27b0a6d8fb06219971da9863ee0fa7a2ab56c85d344b0374724f608cec371b75a2ea2a0cd4da6d7d431f8bf1f3d9fe5f1797a4d15272962 + languageName: node + linkType: hard + +"@aws-sdk/middleware-ssec@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/middleware-ssec@npm:3.489.0" + dependencies: + "@aws-sdk/types": 3.489.0 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: c3666f606bcdcbe2c36a21e0460fd9514ea6f7a3faf09fb392457ca7fc9f41da81351a50f4432ce748a24daad5715736eddf7e59694f0fbbcc530b395c2bf12c + languageName: node + linkType: hard + +"@aws-sdk/middleware-user-agent@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/middleware-user-agent@npm:3.489.0" + dependencies: + "@aws-sdk/types": 3.489.0 + "@aws-sdk/util-endpoints": 3.489.0 + "@smithy/protocol-http": ^3.0.12 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: b5254863f2203b598199ad65ca87a5a3b92a3ecb51cabbb910819b0f03f61b26b553364e047b9b3329463f8dd4324a650d045deaf548cb5e7de2c08ed2f5dfd6 + languageName: node + linkType: hard + +"@aws-sdk/region-config-resolver@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/region-config-resolver@npm:3.489.0" + dependencies: + "@aws-sdk/types": 3.489.0 + "@smithy/node-config-provider": ^2.1.9 + "@smithy/types": ^2.8.0 + "@smithy/util-config-provider": ^2.1.0 + "@smithy/util-middleware": ^2.0.9 + tslib: ^2.5.0 + checksum: 2352d0b3409e6d5225fd3f6f5da81164fcb93bb528579eacefea75ef8760f8626434e377eb3c7785046ce996732209f300de70958905124acbd1eb45a192e24b + languageName: node + linkType: hard + +"@aws-sdk/signature-v4-multi-region@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/signature-v4-multi-region@npm:3.489.0" + dependencies: + "@aws-sdk/middleware-sdk-s3": 3.489.0 + "@aws-sdk/types": 3.489.0 + "@smithy/protocol-http": ^3.0.12 + "@smithy/signature-v4": ^2.0.0 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 613ab4f64682844dd9a6c6675f7db218185d3415a40b255e1346a0217576bad431156324739d148542421c67036462476b95ac801ea8c7f24f5abac96514ee23 + languageName: node + linkType: hard + +"@aws-sdk/token-providers@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/token-providers@npm:3.489.0" + dependencies: + "@aws-crypto/sha256-browser": 3.0.0 + "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/middleware-host-header": 3.489.0 + "@aws-sdk/middleware-logger": 3.489.0 + "@aws-sdk/middleware-recursion-detection": 3.489.0 + "@aws-sdk/middleware-user-agent": 3.489.0 + "@aws-sdk/region-config-resolver": 3.489.0 + "@aws-sdk/types": 3.489.0 + "@aws-sdk/util-endpoints": 3.489.0 + "@aws-sdk/util-user-agent-browser": 3.489.0 + "@aws-sdk/util-user-agent-node": 3.489.0 + "@smithy/config-resolver": ^2.0.23 + "@smithy/fetch-http-handler": ^2.3.2 + "@smithy/hash-node": ^2.0.18 + "@smithy/invalid-dependency": ^2.0.16 + "@smithy/middleware-content-length": ^2.0.18 + "@smithy/middleware-endpoint": ^2.3.0 + "@smithy/middleware-retry": ^2.0.26 + "@smithy/middleware-serde": ^2.0.16 + "@smithy/middleware-stack": ^2.0.10 + "@smithy/node-config-provider": ^2.1.9 + "@smithy/node-http-handler": ^2.2.2 + "@smithy/property-provider": ^2.0.0 + "@smithy/protocol-http": ^3.0.12 + "@smithy/shared-ini-file-loader": ^2.0.6 + "@smithy/smithy-client": ^2.2.1 + "@smithy/types": ^2.8.0 + "@smithy/url-parser": ^2.0.16 + "@smithy/util-base64": ^2.0.1 + "@smithy/util-body-length-browser": ^2.0.1 + "@smithy/util-body-length-node": ^2.1.0 + "@smithy/util-defaults-mode-browser": ^2.0.24 + "@smithy/util-defaults-mode-node": ^2.0.32 + "@smithy/util-endpoints": ^1.0.8 + "@smithy/util-retry": ^2.0.9 + "@smithy/util-utf8": ^2.0.2 + tslib: ^2.5.0 + checksum: cf2e14a09ead031e9ac3426fd0e5bc71656f1ce9ba470c860af26d8935dce65b28365973388175f982dd2d8fb6c470520dee426045b26174663a5adc6ac5928c + languageName: node + linkType: hard + +"@aws-sdk/types@npm:3.489.0, @aws-sdk/types@npm:^3.222.0": + version: 3.489.0 + resolution: "@aws-sdk/types@npm:3.489.0" + dependencies: + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: e4692f04daee278e4fc7faeadb8fee077aee5b1210a6c237c203b4688b714d1efc96c9f4574b71e264f71d8f4aad06b5728bcba13d12242c92f93dec81e3d3da + languageName: node + linkType: hard + +"@aws-sdk/util-arn-parser@npm:3.465.0": + version: 3.465.0 + resolution: "@aws-sdk/util-arn-parser@npm:3.465.0" + dependencies: + tslib: ^2.5.0 + checksum: ce2dd638e9b8ef3260ce1c1ae299a4e44cbaa28a07cda9f1033b763cea8b1d901b7a963338e8a172af17074d06811f09605f3f903318c4bd2bbf102e92d02546 + languageName: node + linkType: hard + +"@aws-sdk/util-endpoints@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/util-endpoints@npm:3.489.0" + dependencies: + "@aws-sdk/types": 3.489.0 + "@smithy/types": ^2.8.0 + "@smithy/util-endpoints": ^1.0.8 + tslib: ^2.5.0 + checksum: 5c225b12ce5c18ecd64079d2e4133374244728ac7c8055efb07ca5b274266cb0e2d6c88ce5ae4385d45d67b2999fcd5bbe5e8dd4299792542683682ac05950e8 + languageName: node + linkType: hard + +"@aws-sdk/util-locate-window@npm:^3.0.0": + version: 3.465.0 + resolution: "@aws-sdk/util-locate-window@npm:3.465.0" + dependencies: + tslib: ^2.5.0 + checksum: 3ec2c40bea7976bf403fc7f227e70180ed1016f5a76e33d57148fc6b6edb24b73b16a5e5c3e32003d6c0783339984c7e50fa997a54d414937c6de14180ee419a + languageName: node + linkType: hard + +"@aws-sdk/util-user-agent-browser@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/util-user-agent-browser@npm:3.489.0" + dependencies: + "@aws-sdk/types": 3.489.0 + "@smithy/types": ^2.8.0 + bowser: ^2.11.0 + tslib: ^2.5.0 + checksum: b24009655bd5a7755575d6ed5c955d6fcfa3a70248e1a9c9d4a58cc7ed4bb25936a276917a0ab60b6e11e7ed915d5dcfdd5e9112e373bd23b24d24963324e516 + languageName: node + linkType: hard + +"@aws-sdk/util-user-agent-node@npm:3.489.0": + version: 3.489.0 + resolution: "@aws-sdk/util-user-agent-node@npm:3.489.0" + dependencies: + "@aws-sdk/types": 3.489.0 + "@smithy/node-config-provider": ^2.1.9 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + peerDependencies: + aws-crt: ">=1.0.0" + peerDependenciesMeta: + aws-crt: + optional: true + checksum: 755845ce1cebdc78d3bb2bab058cf8e966658373daa7c62ae808fc0fc733248ed019042b1eed4ad3274ae08f23506cc000e1b0d41b6f01fd29cccad4275b3f8e + languageName: node + linkType: hard + +"@aws-sdk/util-utf8-browser@npm:^3.0.0": + version: 3.259.0 + resolution: "@aws-sdk/util-utf8-browser@npm:3.259.0" + dependencies: + tslib: ^2.3.1 + checksum: b6a1e580da1c9b62c749814182a7649a748ca4253edb4063aa521df97d25b76eae3359eb1680b86f71aac668e05cc05c514379bca39ebf4ba998ae4348412da8 + languageName: node + linkType: hard + +"@aws-sdk/xml-builder@npm:3.485.0": + version: 3.485.0 + resolution: "@aws-sdk/xml-builder@npm:3.485.0" + dependencies: + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 97eacc7fff1161876cb672051905156d6aec8116f76b35d5f2ca87b676b5b270fc6de0b9bed53792272a7712d835077ac64b4135fed7ea81c3e4cbf070f1be58 + languageName: node + linkType: hard + "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5": version: 7.23.5 resolution: "@babel/code-frame@npm:7.23.5" @@ -165,7 +878,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.18.6, @babel/core@npm:^7.20.7, @babel/core@npm:^7.23.5": +"@babel/core@npm:^7.11.1, @babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.18.6, @babel/core@npm:^7.20.7, @babel/core@npm:^7.23.5": version: 7.23.7 resolution: "@babel/core@npm:7.23.7" dependencies: @@ -313,7 +1026,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.22.15": +"@babel/helper-module-imports@npm:^7.10.4, @babel/helper-module-imports@npm:^7.22.15": version: 7.22.15 resolution: "@babel/helper-module-imports@npm:7.22.15" dependencies: @@ -1386,7 +2099,7 @@ __metadata: languageName: node linkType: hard -"@babel/preset-env@npm:^7.18.6": +"@babel/preset-env@npm:^7.11.0, @babel/preset-env@npm:^7.18.6": version: 7.23.8 resolution: "@babel/preset-env@npm:7.23.8" dependencies: @@ -1520,7 +2233,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": +"@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": version: 7.23.8 resolution: "@babel/runtime@npm:7.23.8" dependencies: @@ -1576,6 +2289,57 @@ __metadata: languageName: node linkType: hard +"@cloudflare/kv-asset-handler@npm:^0.2.0": + version: 0.2.0 + resolution: "@cloudflare/kv-asset-handler@npm:0.2.0" + dependencies: + mime: ^3.0.0 + checksum: bc6a02a9c80be6de90e46454ef4de09301e68726eaa4835de0e30216e50fffcc5612274a17dfb455916cf3418f0cb25fefd2b561a9d2282f4cc10d40527f0acb + languageName: node + linkType: hard + +"@cloudflare/workerd-darwin-64@npm:1.20231030.0": + version: 1.20231030.0 + resolution: "@cloudflare/workerd-darwin-64@npm:1.20231030.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@cloudflare/workerd-darwin-arm64@npm:1.20231030.0": + version: 1.20231030.0 + resolution: "@cloudflare/workerd-darwin-arm64@npm:1.20231030.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@cloudflare/workerd-linux-64@npm:1.20231030.0": + version: 1.20231030.0 + resolution: "@cloudflare/workerd-linux-64@npm:1.20231030.0" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@cloudflare/workerd-linux-arm64@npm:1.20231030.0": + version: 1.20231030.0 + resolution: "@cloudflare/workerd-linux-arm64@npm:1.20231030.0" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@cloudflare/workerd-windows-64@npm:1.20231030.0": + version: 1.20231030.0 + resolution: "@cloudflare/workerd-windows-64@npm:1.20231030.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@cloudflare/workers-types@npm:^4.20230821.0": + version: 4.20231218.0 + resolution: "@cloudflare/workers-types@npm:4.20231218.0" + checksum: b7e50a76ee8e9d662227bbb74798b93b6102acc224f1071a9c99a9adb419ad0b3bdabf7561e7e1b4a320a6a4616badeecdfb1848fbdaada197c7b37d845b8774 + languageName: node + linkType: hard + "@cspotcode/source-map-support@npm:^0.8.0": version: 0.8.1 resolution: "@cspotcode/source-map-support@npm:0.8.1" @@ -1645,6 +2409,15 @@ __metadata: languageName: node linkType: hard +"@esbuild-plugins/node-globals-polyfill@npm:^0.2.3": + version: 0.2.3 + resolution: "@esbuild-plugins/node-globals-polyfill@npm:0.2.3" + peerDependencies: + esbuild: "*" + checksum: f83eeaa382680b26a3b1cf6c396450332c41d2dc0f9fd935d3f4bacf5412bef7383d2aeb4246a858781435b7c005a570dadc81051f8a038f1ef2111f17d3d8b0 + languageName: node + linkType: hard + "@esbuild-plugins/node-modules-polyfill@npm:^0.1.4": version: 0.1.4 resolution: "@esbuild-plugins/node-modules-polyfill@npm:0.1.4" @@ -1657,6 +2430,18 @@ __metadata: languageName: node linkType: hard +"@esbuild-plugins/node-modules-polyfill@npm:^0.2.2": + version: 0.2.2 + resolution: "@esbuild-plugins/node-modules-polyfill@npm:0.2.2" + dependencies: + escape-string-regexp: ^4.0.0 + rollup-plugin-node-polyfills: ^0.2.1 + peerDependencies: + esbuild: "*" + checksum: 73c247a7559c68b7df080ab08dd3d0b0ab44b934840a4933df9626357b7183a9a5d8cf4ffa9c744f1bad8d7131bce0fde14a23203f7b262f9f14f7b3485bfdb1 + languageName: node + linkType: hard + "@esbuild/aix-ppc64@npm:0.19.11": version: 0.19.11 resolution: "@esbuild/aix-ppc64@npm:0.19.11" @@ -2483,6 +3268,20 @@ __metadata: languageName: node linkType: hard +"@fastify/busboy@npm:^2.0.0": + version: 2.1.0 + resolution: "@fastify/busboy@npm:2.1.0" + checksum: 3233abd10f73e50668cb4bb278a79b7b3fadd30215ac6458299b0e5a09a29c3586ec07597aae6bd93f5cbedfcef43a8aeea51829cd28fc13850cdbcd324c28d5 + languageName: node + linkType: hard + +"@floating-ui/core@npm:^0.7.3": + version: 0.7.3 + resolution: "@floating-ui/core@npm:0.7.3" + checksum: f48f9fb0d19dcbe7a68c38e8de7fabb11f0c0e6e0ef215ae60b5004900bacb1386e7b89cb377d91a90ff7d147ea1f06c2905136ecf34dea162d9696d8f448d5f + languageName: node + linkType: hard + "@floating-ui/core@npm:^1.5.3": version: 1.5.3 resolution: "@floating-ui/core@npm:1.5.3" @@ -2492,6 +3291,15 @@ __metadata: languageName: node linkType: hard +"@floating-ui/dom@npm:^0.5.3": + version: 0.5.4 + resolution: "@floating-ui/dom@npm:0.5.4" + dependencies: + "@floating-ui/core": ^0.7.3 + checksum: 9f9d8a51a828c6be5f187204aa6d293c6c9ef70d51dcc5891a4d85683745fceebf79ff8826d0f75ae41b45c3b138367d339756f27f41be87a8770742ebc0de42 + languageName: node + linkType: hard + "@floating-ui/dom@npm:^1.5.4": version: 1.5.4 resolution: "@floating-ui/dom@npm:1.5.4" @@ -2502,6 +3310,19 @@ __metadata: languageName: node linkType: hard +"@floating-ui/react-dom@npm:0.7.2": + version: 0.7.2 + resolution: "@floating-ui/react-dom@npm:0.7.2" + dependencies: + "@floating-ui/dom": ^0.5.3 + use-isomorphic-layout-effect: ^1.1.1 + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: bc3f2b5557f87f6f4bbccfe3e8d097abafad61a41083d3b79f3499f27590e273bcb3dc7136c2444841ee7a8c0d2a70cc1385458c16103fa8b70eade80c24af52 + languageName: node + linkType: hard + "@floating-ui/react-dom@npm:^2.0.0": version: 2.0.5 resolution: "@floating-ui/react-dom@npm:2.0.5" @@ -2923,6 +3744,16 @@ __metadata: languageName: node linkType: hard +"@jridgewell/source-map@npm:^0.3.3": + version: 0.3.5 + resolution: "@jridgewell/source-map@npm:0.3.5" + dependencies: + "@jridgewell/gen-mapping": ^0.3.0 + "@jridgewell/trace-mapping": ^0.3.9 + checksum: 1ad4dec0bdafbade57920a50acec6634f88a0eb735851e0dda906fa9894e7f0549c492678aad1a10f8e144bfe87f238307bf2a914a1bc85b7781d345417e9f6f + languageName: node + linkType: hard + "@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14": version: 1.4.15 resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" @@ -3091,6 +3922,13 @@ __metadata: languageName: node linkType: hard +"@next/env@npm:13.5.6": + version: 13.5.6 + resolution: "@next/env@npm:13.5.6" + checksum: 5e8f3f6f987a15dad3cd7b2bcac64a6382c2ec372d95d0ce6ab295eb59c9731222017eebf71ff3005932de2571f7543bce7e5c6a8c90030207fb819404138dc2 + languageName: node + linkType: hard + "@next/env@npm:14.0.4": version: 14.0.4 resolution: "@next/env@npm:14.0.4" @@ -3098,6 +3936,15 @@ __metadata: languageName: node linkType: hard +"@next/eslint-plugin-next@npm:12.2.5": + version: 12.2.5 + resolution: "@next/eslint-plugin-next@npm:12.2.5" + dependencies: + glob: 7.1.7 + checksum: 0d6faf895d4952fc2a5da3f2e86a9e1903f37b44201e7fabfcc994f6989dfceb974f354a7abb1779b318f14ada57925d82eb6c22628265c7f6b36f232edc93ee + languageName: node + linkType: hard + "@next/eslint-plugin-next@npm:13.2.4": version: 13.2.4 resolution: "@next/eslint-plugin-next@npm:13.2.4" @@ -3116,6 +3963,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-darwin-arm64@npm:13.5.6": + version: 13.5.6 + resolution: "@next/swc-darwin-arm64@npm:13.5.6" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@next/swc-darwin-arm64@npm:14.0.4": version: 14.0.4 resolution: "@next/swc-darwin-arm64@npm:14.0.4" @@ -3123,6 +3977,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-darwin-x64@npm:13.5.6": + version: 13.5.6 + resolution: "@next/swc-darwin-x64@npm:13.5.6" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@next/swc-darwin-x64@npm:14.0.4": version: 14.0.4 resolution: "@next/swc-darwin-x64@npm:14.0.4" @@ -3130,6 +3991,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-arm64-gnu@npm:13.5.6": + version: 13.5.6 + resolution: "@next/swc-linux-arm64-gnu@npm:13.5.6" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + "@next/swc-linux-arm64-gnu@npm:14.0.4": version: 14.0.4 resolution: "@next/swc-linux-arm64-gnu@npm:14.0.4" @@ -3137,6 +4005,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-arm64-musl@npm:13.5.6": + version: 13.5.6 + resolution: "@next/swc-linux-arm64-musl@npm:13.5.6" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + "@next/swc-linux-arm64-musl@npm:14.0.4": version: 14.0.4 resolution: "@next/swc-linux-arm64-musl@npm:14.0.4" @@ -3144,6 +4019,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-x64-gnu@npm:13.5.6": + version: 13.5.6 + resolution: "@next/swc-linux-x64-gnu@npm:13.5.6" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + "@next/swc-linux-x64-gnu@npm:14.0.4": version: 14.0.4 resolution: "@next/swc-linux-x64-gnu@npm:14.0.4" @@ -3151,6 +4033,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-x64-musl@npm:13.5.6": + version: 13.5.6 + resolution: "@next/swc-linux-x64-musl@npm:13.5.6" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + "@next/swc-linux-x64-musl@npm:14.0.4": version: 14.0.4 resolution: "@next/swc-linux-x64-musl@npm:14.0.4" @@ -3158,6 +4047,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-win32-arm64-msvc@npm:13.5.6": + version: 13.5.6 + resolution: "@next/swc-win32-arm64-msvc@npm:13.5.6" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@next/swc-win32-arm64-msvc@npm:14.0.4": version: 14.0.4 resolution: "@next/swc-win32-arm64-msvc@npm:14.0.4" @@ -3165,6 +4061,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-win32-ia32-msvc@npm:13.5.6": + version: 13.5.6 + resolution: "@next/swc-win32-ia32-msvc@npm:13.5.6" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@next/swc-win32-ia32-msvc@npm:14.0.4": version: 14.0.4 resolution: "@next/swc-win32-ia32-msvc@npm:14.0.4" @@ -3172,6 +4075,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-win32-x64-msvc@npm:13.5.6": + version: 13.5.6 + resolution: "@next/swc-win32-x64-msvc@npm:13.5.6" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@next/swc-win32-x64-msvc@npm:14.0.4": version: 14.0.4 resolution: "@next/swc-win32-x64-msvc@npm:14.0.4" @@ -3371,7 +4281,7 @@ __metadata: languageName: node linkType: hard -"@octokit/core@npm:^5.0.0": +"@octokit/core@npm:^5.0.0, @octokit/core@npm:^5.0.1": version: 5.0.2 resolution: "@octokit/core@npm:5.0.2" dependencies: @@ -3562,7 +4472,7 @@ __metadata: languageName: node linkType: hard -"@octokit/plugin-retry@npm:^6.0.0": +"@octokit/plugin-retry@npm:^6.0.0, @octokit/plugin-retry@npm:^6.0.1": version: 6.0.1 resolution: "@octokit/plugin-retry@npm:6.0.1" dependencies: @@ -3691,6 +4601,13 @@ __metadata: languageName: node linkType: hard +"@octokit/webhooks-types@npm:^6.11.0": + version: 6.11.0 + resolution: "@octokit/webhooks-types@npm:6.11.0" + checksum: af35ac7a3d8d95bf9906fb3a8f6075cf9cb10707c79444fa82df2d64596125f515a35a4995b4548b84ee042c7c1b1cc120e05ece4a197af541a52f154bf4bcce + languageName: node + linkType: hard + "@octokit/webhooks@npm:^12.0.4": version: 12.0.11 resolution: "@octokit/webhooks@npm:12.0.11" @@ -3763,6 +4680,15 @@ __metadata: languageName: node linkType: hard +"@radix-ui/primitive@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/primitive@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + checksum: 72996afaf346ec4f4c73422f14f6cb2d0de994801ba7cbb9a4a67b0050e0cd74625182c349ef8017ccae1406579d4b74a34a225ef2efe61e8e5337decf235deb + languageName: node + linkType: hard + "@radix-ui/primitive@npm:1.0.1": version: 1.0.1 resolution: "@radix-ui/primitive@npm:1.0.1" @@ -3797,6 +4723,19 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-arrow@npm:1.0.2": + version: 1.0.2 + resolution: "@radix-ui/react-arrow@npm:1.0.2" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-primitive": 1.0.2 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + checksum: d9c4a810376b686edfedfab15c3ef739bb2b388211fbf33191648149aba5064bfff8a5de05b264ad0b76c4a4df98fd8267002580e3515b7f5ad31b9495bfda21 + languageName: node + linkType: hard + "@radix-ui/react-arrow@npm:1.0.3": version: 1.0.3 resolution: "@radix-ui/react-arrow@npm:1.0.3" @@ -3840,6 +4779,17 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-compose-refs@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-compose-refs@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: fb98be2e275a1a758ccac647780ff5b04be8dcf25dcea1592db3b691fecf719c4c0700126da605b2f512dd89caa111352b9fad59528d736b4e0e9a0e134a74a1 + languageName: node + linkType: hard + "@radix-ui/react-compose-refs@npm:1.0.1": version: 1.0.1 resolution: "@radix-ui/react-compose-refs@npm:1.0.1" @@ -3880,6 +4830,17 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-context@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-context@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: 43c6b6f2183398161fe6b109e83fff240a6b7babbb27092b815932342a89d5ca42aa9806bfae5927970eed5ff90feed04c67aa29c6721f84ae826f17fcf34ce0 + languageName: node + linkType: hard + "@radix-ui/react-context@npm:1.0.1": version: 1.0.1 resolution: "@radix-ui/react-context@npm:1.0.1" @@ -3943,6 +4904,23 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-dismissable-layer@npm:1.0.3": + version: 1.0.3 + resolution: "@radix-ui/react-dismissable-layer@npm:1.0.3" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/primitive": 1.0.0 + "@radix-ui/react-compose-refs": 1.0.0 + "@radix-ui/react-primitive": 1.0.2 + "@radix-ui/react-use-callback-ref": 1.0.0 + "@radix-ui/react-use-escape-keydown": 1.0.2 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + checksum: cb2a38a65dd129d1fd58436bedee765f46f6a6edc2ec15d534a1499c10f768ae06ad874704e030c85869b3ee4b61103076a116dfdb7e0c761a8c8cdc30a5c951 + languageName: node + linkType: hard + "@radix-ui/react-dismissable-layer@npm:1.0.4": version: 1.0.4 resolution: "@radix-ui/react-dismissable-layer@npm:1.0.4" @@ -4017,6 +4995,17 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-focus-guards@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-focus-guards@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: 8c714e8caa6032f5402eecb0323addd7456d3496946dbad1b9ee8ebf5845943876945e7af9bca179e9f8ffe5100e61cb4ba54a185873949125c310c406be5aa4 + languageName: node + linkType: hard + "@radix-ui/react-focus-guards@npm:1.0.1": version: 1.0.1 resolution: "@radix-ui/react-focus-guards@npm:1.0.1" @@ -4032,6 +5021,21 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-focus-scope@npm:1.0.2": + version: 1.0.2 + resolution: "@radix-ui/react-focus-scope@npm:1.0.2" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-compose-refs": 1.0.0 + "@radix-ui/react-primitive": 1.0.2 + "@radix-ui/react-use-callback-ref": 1.0.0 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + checksum: f04f7412c8d9d2e0f431a0360ec10415f085a530322f693220e0ad36ad8ffd9f637c4b0d9bc35da09621ac0ad97ff33d382c84853c827825a9f9c924843fd339 + languageName: node + linkType: hard + "@radix-ui/react-focus-scope@npm:1.0.3": version: 1.0.3 resolution: "@radix-ui/react-focus-scope@npm:1.0.3" @@ -4076,6 +5080,18 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-id@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-id@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-use-layout-effect": 1.0.0 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: ba323cedd6a6df6f6e51ed1f7f7747988ce432b47fd94d860f962b14b342dcf049eae33f8ad0b72fd7df6329a7375542921132271fba64ab0a271c93f09c48d1 + languageName: node + linkType: hard + "@radix-ui/react-id@npm:1.0.1": version: 1.0.1 resolution: "@radix-ui/react-id@npm:1.0.1" @@ -4129,6 +5145,33 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-popover@npm:1.0.6-rc.5": + version: 1.0.6-rc.5 + resolution: "@radix-ui/react-popover@npm:1.0.6-rc.5" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/primitive": 1.0.0 + "@radix-ui/react-compose-refs": 1.0.0 + "@radix-ui/react-context": 1.0.0 + "@radix-ui/react-dismissable-layer": 1.0.3 + "@radix-ui/react-focus-guards": 1.0.0 + "@radix-ui/react-focus-scope": 1.0.2 + "@radix-ui/react-id": 1.0.0 + "@radix-ui/react-popper": 1.1.2-rc.5 + "@radix-ui/react-portal": 1.0.2 + "@radix-ui/react-presence": 1.0.0 + "@radix-ui/react-primitive": 1.0.2 + "@radix-ui/react-slot": 1.0.1 + "@radix-ui/react-use-controllable-state": 1.0.0 + aria-hidden: ^1.1.1 + react-remove-scroll: 2.5.5 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + checksum: a9d758eaf1b1e0714e331be5aa4221a870676960e1c89f2e55c399a09480125792145a3fe329040aae302e03ea9008f1d775f801a2fdf54281eb06a8b1bc63c0 + languageName: node + linkType: hard + "@radix-ui/react-popover@npm:^1.0.7": version: 1.0.7 resolution: "@radix-ui/react-popover@npm:1.0.7" @@ -4192,6 +5235,28 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-popper@npm:1.1.2-rc.5": + version: 1.1.2-rc.5 + resolution: "@radix-ui/react-popper@npm:1.1.2-rc.5" + dependencies: + "@babel/runtime": ^7.13.10 + "@floating-ui/react-dom": 0.7.2 + "@radix-ui/react-arrow": 1.0.2 + "@radix-ui/react-compose-refs": 1.0.0 + "@radix-ui/react-context": 1.0.0 + "@radix-ui/react-primitive": 1.0.2 + "@radix-ui/react-use-callback-ref": 1.0.0 + "@radix-ui/react-use-layout-effect": 1.0.0 + "@radix-ui/react-use-rect": 1.0.0 + "@radix-ui/react-use-size": 1.0.0 + "@radix-ui/rect": 1.0.0 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + checksum: 9a6a1773bec3ff7054e8cf815c89e490e1426ceb624e46ff3601dde18fa2a06e5ff9d0b5d54cce9a5a46ac3cf5344de76df757812c41cb733a5552c7a5273cf8 + languageName: node + linkType: hard + "@radix-ui/react-popper@npm:1.1.3": version: 1.1.3 resolution: "@radix-ui/react-popper@npm:1.1.3" @@ -4221,6 +5286,19 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-portal@npm:1.0.2": + version: 1.0.2 + resolution: "@radix-ui/react-portal@npm:1.0.2" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-primitive": 1.0.2 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + checksum: 1165b4bced8057021ea9ac4f568c6e0ea6f190936f07dc96780d67488b9222021444bbb8e04e506eea84d9219a5caacae8d0974e745182d4f398aa903b982e19 + languageName: node + linkType: hard + "@radix-ui/react-portal@npm:1.0.3": version: 1.0.3 resolution: "@radix-ui/react-portal@npm:1.0.3" @@ -4261,6 +5339,20 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-presence@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-presence@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-compose-refs": 1.0.0 + "@radix-ui/react-use-layout-effect": 1.0.0 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + checksum: a607d67795aa265e88f1765dcc7c18bebf6d88d116cb7f529ebe5a3fbbe751a42763aff0c1c89cdd8ce7f7664355936c4070fd3d4685774aff1a80fa95f4665b + languageName: node + linkType: hard + "@radix-ui/react-presence@npm:1.0.1": version: 1.0.1 resolution: "@radix-ui/react-presence@npm:1.0.1" @@ -4282,6 +5374,19 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-primitive@npm:1.0.2": + version: 1.0.2 + resolution: "@radix-ui/react-primitive@npm:1.0.2" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-slot": 1.0.1 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + checksum: 070b1770749eb629425ef959c4cdbd86957b83c5286ae4423e55ab1a89fa286a51f5aeee44e3a13eb6ca44771415ac1acbaeb0ba03013b49ecb5253e1a5a8996 + languageName: node + linkType: hard + "@radix-ui/react-primitive@npm:1.0.3": version: 1.0.3 resolution: "@radix-ui/react-primitive@npm:1.0.3" @@ -4400,6 +5505,18 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-slot@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-slot@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-compose-refs": 1.0.0 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: a20693f8ce532bd6cbff12ba543dfcf90d451f22923bd60b57dc9e639f6e53348915e182002b33444feb6ab753434e78e2a54085bf7092aadda4418f0423763f + languageName: node + linkType: hard + "@radix-ui/react-slot@npm:1.0.2": version: 1.0.2 resolution: "@radix-ui/react-slot@npm:1.0.2" @@ -4447,6 +5564,17 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-use-callback-ref@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-use-callback-ref@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: a8dda76ba0a26e23dc6ab5003831ad7439f59ba9d696a517643b9ee6a7fb06b18ae7a8f5a3c00c530d5c8104745a466a077b7475b99b4c0f5c15f5fc29474471 + languageName: node + linkType: hard + "@radix-ui/react-use-callback-ref@npm:1.0.1": version: 1.0.1 resolution: "@radix-ui/react-use-callback-ref@npm:1.0.1" @@ -4462,6 +5590,18 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-use-controllable-state@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-use-controllable-state@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-use-callback-ref": 1.0.0 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: 35f1e714bbe3fc9f5362a133339dd890fb96edb79b63168a99403c65dd5f2b63910e0c690255838029086719e31360fa92544a55bc902cfed4442bb3b55822e2 + languageName: node + linkType: hard + "@radix-ui/react-use-controllable-state@npm:1.0.1": version: 1.0.1 resolution: "@radix-ui/react-use-controllable-state@npm:1.0.1" @@ -4478,6 +5618,18 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-use-escape-keydown@npm:1.0.2": + version: 1.0.2 + resolution: "@radix-ui/react-use-escape-keydown@npm:1.0.2" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-use-callback-ref": 1.0.0 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: 5bec1b73ed6c38139bf1db3c626c0474ca6221ae55f154ef83f1c6429ea866280b2a0ba9436b807334d0215bb4389f0b492c65471cf565635957a8ee77cce98a + languageName: node + linkType: hard + "@radix-ui/react-use-escape-keydown@npm:1.0.3": version: 1.0.3 resolution: "@radix-ui/react-use-escape-keydown@npm:1.0.3" @@ -4494,6 +5646,17 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-use-layout-effect@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-use-layout-effect@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: fcdc8cfa79bd45766ebe3de11039c58abe3fed968cb39c12b2efce5d88013c76fe096ea4cee464d42576d02fe7697779b682b4268459bca3c4e48644f5b4ac5e + languageName: node + linkType: hard + "@radix-ui/react-use-layout-effect@npm:1.0.1": version: 1.0.1 resolution: "@radix-ui/react-use-layout-effect@npm:1.0.1" @@ -4524,6 +5687,18 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-use-rect@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-use-rect@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/rect": 1.0.0 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: c755cee1a8846a74d4f6f486c65134a552c65d0bfb934d1d3d4f69f331c32cfd8b279c08c8907d64fbb68388fc3683f854f336e4f9549e1816fba32156bb877b + languageName: node + linkType: hard + "@radix-ui/react-use-rect@npm:1.0.1": version: 1.0.1 resolution: "@radix-ui/react-use-rect@npm:1.0.1" @@ -4540,6 +5715,18 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-use-size@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-use-size@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-use-layout-effect": 1.0.0 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: b319564668512bb5c8c64530e3c12810c4b7c75c19a00d5ef758c246e8d85cd5015df19688e174db1cc44b0584c8d7f22411eb00af5f8ac6c2e789aa5c8e34f5 + languageName: node + linkType: hard + "@radix-ui/react-use-size@npm:1.0.1": version: 1.0.1 resolution: "@radix-ui/react-use-size@npm:1.0.1" @@ -4576,6 +5763,15 @@ __metadata: languageName: node linkType: hard +"@radix-ui/rect@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/rect@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + checksum: d5b54984148ac52e30c6a92834deb619cf74b4af02709a20eb43e7895f98fed098968b597a715bf5b5431ae186372e65499a801d93e835f53bbc39e3a549f664 + languageName: node + linkType: hard + "@radix-ui/rect@npm:1.0.1": version: 1.0.1 resolution: "@radix-ui/rect@npm:1.0.1" @@ -4682,6 +5878,64 @@ __metadata: languageName: node linkType: hard +"@rollup/plugin-babel@npm:^5.2.0": + version: 5.3.1 + resolution: "@rollup/plugin-babel@npm:5.3.1" + dependencies: + "@babel/helper-module-imports": ^7.10.4 + "@rollup/pluginutils": ^3.1.0 + peerDependencies: + "@babel/core": ^7.0.0 + "@types/babel__core": ^7.1.9 + rollup: ^1.20.0||^2.0.0 + peerDependenciesMeta: + "@types/babel__core": + optional: true + checksum: 220d71e4647330f252ef33d5f29700aef2e8284a0b61acfcceb47617a7f96208aa1ed16eae75619424bf08811ede5241e271a6d031f07026dee6b3a2bdcdc638 + languageName: node + linkType: hard + +"@rollup/plugin-node-resolve@npm:^11.2.1": + version: 11.2.1 + resolution: "@rollup/plugin-node-resolve@npm:11.2.1" + dependencies: + "@rollup/pluginutils": ^3.1.0 + "@types/resolve": 1.17.1 + builtin-modules: ^3.1.0 + deepmerge: ^4.2.2 + is-module: ^1.0.0 + resolve: ^1.19.0 + peerDependencies: + rollup: ^1.20.0||^2.0.0 + checksum: 6f3b3ecf9a0596a5db4212984bdeb13bb7612693602407e9457ada075dea5a5f2e4e124c592352cf27066a88b194de9b9a95390149b52cf335d5b5e17b4e265b + languageName: node + linkType: hard + +"@rollup/plugin-replace@npm:^2.4.1": + version: 2.4.2 + resolution: "@rollup/plugin-replace@npm:2.4.2" + dependencies: + "@rollup/pluginutils": ^3.1.0 + magic-string: ^0.25.7 + peerDependencies: + rollup: ^1.20.0 || ^2.0.0 + checksum: b2f1618ee5526d288e2f8ae328dcb326e20e8dc8bd1f60d3e14d6708a5832e4aa44811f7d493f4aed2deeadca86e3b6b0503cd39bf50cfb4b595bb9da027fad0 + languageName: node + linkType: hard + +"@rollup/pluginutils@npm:^3.1.0": + version: 3.1.0 + resolution: "@rollup/pluginutils@npm:3.1.0" + dependencies: + "@types/estree": 0.0.39 + estree-walker: ^1.0.1 + picomatch: ^2.2.2 + peerDependencies: + rollup: ^1.20.0||^2.0.0 + checksum: 8be16e27863c219edbb25a4e6ec2fe0e1e451d9e917b6a43cf2ae5bc025a6b8faaa40f82a6e53b66d0de37b58ff472c6c3d57a83037ae635041f8df959d6d9aa + languageName: node + linkType: hard + "@rollup/pluginutils@npm:^4.0.0": version: 4.2.1 resolution: "@rollup/pluginutils@npm:4.2.1" @@ -4692,6 +5946,97 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-android-arm-eabi@npm:4.9.5": + version: 4.9.5 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.9.5" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-android-arm64@npm:4.9.5": + version: 4.9.5 + resolution: "@rollup/rollup-android-arm64@npm:4.9.5" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-arm64@npm:4.9.5": + version: 4.9.5 + resolution: "@rollup/rollup-darwin-arm64@npm:4.9.5" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-x64@npm:4.9.5": + version: 4.9.5 + resolution: "@rollup/rollup-darwin-x64@npm:4.9.5" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-gnueabihf@npm:4.9.5": + version: 4.9.5 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.9.5" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-gnu@npm:4.9.5": + version: 4.9.5 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.9.5" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-musl@npm:4.9.5": + version: 4.9.5 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.9.5" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-gnu@npm:4.9.5": + version: 4.9.5 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.9.5" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-gnu@npm:4.9.5": + version: 4.9.5 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.9.5" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-musl@npm:4.9.5": + version: 4.9.5 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.9.5" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-win32-arm64-msvc@npm:4.9.5": + version: 4.9.5 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.9.5" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-ia32-msvc@npm:4.9.5": + version: 4.9.5 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.9.5" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-msvc@npm:4.9.5": + version: 4.9.5 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.9.5" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@rushstack/eslint-patch@npm:^1.1.3": version: 1.6.1 resolution: "@rushstack/eslint-patch@npm:1.6.1" @@ -4761,6 +6106,245 @@ __metadata: languageName: node linkType: hard +"@sentry-internal/feedback@npm:7.93.0": + version: 7.93.0 + resolution: "@sentry-internal/feedback@npm:7.93.0" + dependencies: + "@sentry/core": 7.93.0 + "@sentry/types": 7.93.0 + "@sentry/utils": 7.93.0 + checksum: 6a6aff87ce41c2882951e2ba70127dbc38b5c5c451736f4ef45edebf68c798b1b901c0ea412d2b4e2234fa39e4feeee5700fe3c46131ba0c01380fdb476396d1 + languageName: node + linkType: hard + +"@sentry-internal/tracing@npm:7.93.0": + version: 7.93.0 + resolution: "@sentry-internal/tracing@npm:7.93.0" + dependencies: + "@sentry/core": 7.93.0 + "@sentry/types": 7.93.0 + "@sentry/utils": 7.93.0 + checksum: d3e3536c2be747f6e02b844932ae251d52086a7c40bdcb4ddf32de5d9cee7d120f08cf494de887bda4228e9a43f9fccfc3610cba831a79eab147d607981e1388 + languageName: node + linkType: hard + +"@sentry/browser@npm:7.93.0": + version: 7.93.0 + resolution: "@sentry/browser@npm:7.93.0" + dependencies: + "@sentry-internal/feedback": 7.93.0 + "@sentry-internal/tracing": 7.93.0 + "@sentry/core": 7.93.0 + "@sentry/replay": 7.93.0 + "@sentry/types": 7.93.0 + "@sentry/utils": 7.93.0 + checksum: 420c9a76c55d72654b6afa767f72f25b50234b9e14724a6162c780b12e1eb14a1a52bba182bd73c17346a1bb5debac73cbfac497fc64d86e1fd1ec9c03b4fb44 + languageName: node + linkType: hard + +"@sentry/cli-darwin@npm:2.25.0": + version: 2.25.0 + resolution: "@sentry/cli-darwin@npm:2.25.0" + conditions: os=darwin + languageName: node + linkType: hard + +"@sentry/cli-linux-arm64@npm:2.25.0": + version: 2.25.0 + resolution: "@sentry/cli-linux-arm64@npm:2.25.0" + conditions: (os=linux | os=freebsd) & cpu=arm64 + languageName: node + linkType: hard + +"@sentry/cli-linux-arm@npm:2.25.0": + version: 2.25.0 + resolution: "@sentry/cli-linux-arm@npm:2.25.0" + conditions: (os=linux | os=freebsd) & cpu=arm + languageName: node + linkType: hard + +"@sentry/cli-linux-i686@npm:2.25.0": + version: 2.25.0 + resolution: "@sentry/cli-linux-i686@npm:2.25.0" + conditions: (os=linux | os=freebsd) & (cpu=x86 | cpu=ia32) + languageName: node + linkType: hard + +"@sentry/cli-linux-x64@npm:2.25.0": + version: 2.25.0 + resolution: "@sentry/cli-linux-x64@npm:2.25.0" + conditions: (os=linux | os=freebsd) & cpu=x64 + languageName: node + linkType: hard + +"@sentry/cli-win32-i686@npm:2.25.0": + version: 2.25.0 + resolution: "@sentry/cli-win32-i686@npm:2.25.0" + conditions: os=win32 & (cpu=x86 | cpu=ia32) + languageName: node + linkType: hard + +"@sentry/cli-win32-x64@npm:2.25.0": + version: 2.25.0 + resolution: "@sentry/cli-win32-x64@npm:2.25.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@sentry/cli@npm:^2.25.0": + version: 2.25.0 + resolution: "@sentry/cli@npm:2.25.0" + dependencies: + "@sentry/cli-darwin": 2.25.0 + "@sentry/cli-linux-arm": 2.25.0 + "@sentry/cli-linux-arm64": 2.25.0 + "@sentry/cli-linux-i686": 2.25.0 + "@sentry/cli-linux-x64": 2.25.0 + "@sentry/cli-win32-i686": 2.25.0 + "@sentry/cli-win32-x64": 2.25.0 + https-proxy-agent: ^5.0.0 + node-fetch: ^2.6.7 + progress: ^2.0.3 + proxy-from-env: ^1.1.0 + which: ^2.0.2 + dependenciesMeta: + "@sentry/cli-darwin": + optional: true + "@sentry/cli-linux-arm": + optional: true + "@sentry/cli-linux-arm64": + optional: true + "@sentry/cli-linux-i686": + optional: true + "@sentry/cli-linux-x64": + optional: true + "@sentry/cli-win32-i686": + optional: true + "@sentry/cli-win32-x64": + optional: true + bin: + sentry-cli: bin/sentry-cli + checksum: 44a6a7dd34a6553afad65a60f97533a5fe58a6166162bf7dac0651a62080f9dc5ed8046a404ddaba6a65936d377081289191f00154716107409e08344a310235 + languageName: node + linkType: hard + +"@sentry/core@npm:6.19.6": + version: 6.19.6 + resolution: "@sentry/core@npm:6.19.6" + dependencies: + "@sentry/hub": 6.19.6 + "@sentry/minimal": 6.19.6 + "@sentry/types": 6.19.6 + "@sentry/utils": 6.19.6 + tslib: ^1.9.3 + checksum: b714f12c8a59db845cbc05074818725dd37d0a5f31f2abdb12a07c1b7f0a759e8d876b1a6e58e5c4af8a593754bb8b7c66810d3e59a7cf7ead76f439b4802105 + languageName: node + linkType: hard + +"@sentry/core@npm:7.93.0": + version: 7.93.0 + resolution: "@sentry/core@npm:7.93.0" + dependencies: + "@sentry/types": 7.93.0 + "@sentry/utils": 7.93.0 + checksum: cbf9e944d985605bc38cc0d6b7ebeee10fa6d0f9ff9443d27c18a022e7ddcc1a70974c7ed9aafb2be094398f360c705e3464bf7359c3bdefd03724eac23048f4 + languageName: node + linkType: hard + +"@sentry/hub@npm:6.19.6": + version: 6.19.6 + resolution: "@sentry/hub@npm:6.19.6" + dependencies: + "@sentry/types": 6.19.6 + "@sentry/utils": 6.19.6 + tslib: ^1.9.3 + checksum: 150fdcb06c3107016aedf880e7c806aa8279a1d91c2651d9e3439bc752335ce5eec5ac8af4e515b73d29ec5a9f4bc717f7537245cb3675be89f15e01195a21a8 + languageName: node + linkType: hard + +"@sentry/integrations@npm:^7.34.0": + version: 7.93.0 + resolution: "@sentry/integrations@npm:7.93.0" + dependencies: + "@sentry/core": 7.93.0 + "@sentry/types": 7.93.0 + "@sentry/utils": 7.93.0 + localforage: ^1.8.1 + checksum: e37f6fa609d7d14ebee2018f2c409fe1b57a5ff0ea1aae2d6d5b8c53414030983c2f21ce7fbe952aae6928b551dc2084f025580a4e0d4b99704186a83094f734 + languageName: node + linkType: hard + +"@sentry/minimal@npm:6.19.6": + version: 6.19.6 + resolution: "@sentry/minimal@npm:6.19.6" + dependencies: + "@sentry/hub": 6.19.6 + "@sentry/types": 6.19.6 + tslib: ^1.9.3 + checksum: f6ee93095076b4ec2906fa6e796b91b461e33ed172222bc717e5621a7042c74eb2358e0b7c4b68079a60ae790c19de103f14207058a986eb0612fc79dcbba4c8 + languageName: node + linkType: hard + +"@sentry/react@npm:^7.77.0": + version: 7.93.0 + resolution: "@sentry/react@npm:7.93.0" + dependencies: + "@sentry/browser": 7.93.0 + "@sentry/core": 7.93.0 + "@sentry/types": 7.93.0 + "@sentry/utils": 7.93.0 + hoist-non-react-statics: ^3.3.2 + peerDependencies: + react: 15.x || 16.x || 17.x || 18.x + checksum: 3034e9ccb64e301e7963dcdddc60d3c760c06d0da5b6da2a455f842196708021160c04c0df2b1922d33bcb90126c6bdb7f1c1a6e942f4c15bdc487fdda9aa606 + languageName: node + linkType: hard + +"@sentry/replay@npm:7.93.0": + version: 7.93.0 + resolution: "@sentry/replay@npm:7.93.0" + dependencies: + "@sentry-internal/tracing": 7.93.0 + "@sentry/core": 7.93.0 + "@sentry/types": 7.93.0 + "@sentry/utils": 7.93.0 + checksum: 683d946da88ff530d4f964e63e58cf07e4d662710595bf903ac8ac742ffd7105efe38f612ff411248250095901ea47c24301c774f05e68e6f54433a6383fb35d + languageName: node + linkType: hard + +"@sentry/types@npm:6.19.6": + version: 6.19.6 + resolution: "@sentry/types@npm:6.19.6" + checksum: a1564dabb657bf44a6ee380eb58dabcc6ea1c21b4060127469f8d39c76376d0e3f8c6b3fa27193fc50d9f1415ce63312fc3926d271918bba3e75f1f39691223a + languageName: node + linkType: hard + +"@sentry/types@npm:7.93.0": + version: 7.93.0 + resolution: "@sentry/types@npm:7.93.0" + checksum: 43d4bc3215f7bf916404608907d51c0c1bb22b28065a4633e5fc7d244a3f66965857f9ea8464a71040dd752b93035b0fdcd61880f14908f75c6bbdc9d55b882f + languageName: node + linkType: hard + +"@sentry/utils@npm:6.19.6": + version: 6.19.6 + resolution: "@sentry/utils@npm:6.19.6" + dependencies: + "@sentry/types": 6.19.6 + tslib: ^1.9.3 + checksum: 73a582b6827ea519f8a352f3bdc8b0f22fbbced99506af56f566a6567ad0d6df2f8c3e39532f8588bdccb93f9bf03289681ae90404fd1dd9904c3175d314136a + languageName: node + linkType: hard + +"@sentry/utils@npm:7.93.0": + version: 7.93.0 + resolution: "@sentry/utils@npm:7.93.0" + dependencies: + "@sentry/types": 7.93.0 + checksum: d668bfce46b9ea58778a4c2cbc8df5d16c781187dde10459c61acc57cc1e86028e08fb7dd49506137ce71f5b5280c03e0c3bb94a32b215a713bf422f3d880f7d + languageName: node + linkType: hard + "@sinclair/typebox@npm:0.25.24": version: 0.25.24 resolution: "@sinclair/typebox@npm:0.25.24" @@ -4844,6 +6428,675 @@ __metadata: languageName: node linkType: hard +"@smithy/abort-controller@npm:^2.0.1, @smithy/abort-controller@npm:^2.0.16": + version: 2.0.16 + resolution: "@smithy/abort-controller@npm:2.0.16" + dependencies: + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: f91824aa8c8c39223b2d52c88fe123b36e5823acf8ce9316ce3b38245202cc6d31e1c172ebfec9fda47466d0b31f1df1add7e64d7161695fd27f96992037b18f + languageName: node + linkType: hard + +"@smithy/chunked-blob-reader-native@npm:^2.0.1": + version: 2.0.1 + resolution: "@smithy/chunked-blob-reader-native@npm:2.0.1" + dependencies: + "@smithy/util-base64": ^2.0.1 + tslib: ^2.5.0 + checksum: db13a380a51ace30c8ed5947ea1b9fa65f5f5d0dbb722b4abc4d19e4a1215979174f35365c692f9fe7d3c116eaaf90dd9fb58e1dcff4fe943fc76d86c7f1798d + languageName: node + linkType: hard + +"@smithy/chunked-blob-reader@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/chunked-blob-reader@npm:2.0.0" + dependencies: + tslib: ^2.5.0 + checksum: a47e5298f0b28e25eaa5825ea9737718f0e2b7cf0f03a49cca186eb5544dd20ac91a2d92069f9805e40e5f3ab34d32f8091853518672fdbca009411179dbeb2a + languageName: node + linkType: hard + +"@smithy/config-resolver@npm:^2.0.23": + version: 2.0.23 + resolution: "@smithy/config-resolver@npm:2.0.23" + dependencies: + "@smithy/node-config-provider": ^2.1.9 + "@smithy/types": ^2.8.0 + "@smithy/util-config-provider": ^2.1.0 + "@smithy/util-middleware": ^2.0.9 + tslib: ^2.5.0 + checksum: 16f6c9a492aca44acc3f2dbb9c92e9212daad741143b444333926befb373ebe355edb62e74cf4af6b54cea54e4b54614a985c5dcb51e127be02e59e35c8d8815 + languageName: node + linkType: hard + +"@smithy/core@npm:^1.2.2": + version: 1.2.2 + resolution: "@smithy/core@npm:1.2.2" + dependencies: + "@smithy/middleware-endpoint": ^2.3.0 + "@smithy/middleware-retry": ^2.0.26 + "@smithy/middleware-serde": ^2.0.16 + "@smithy/protocol-http": ^3.0.12 + "@smithy/smithy-client": ^2.2.1 + "@smithy/types": ^2.8.0 + "@smithy/util-middleware": ^2.0.9 + tslib: ^2.5.0 + checksum: 5688b08bf935f429ada15c1238b0e6046069418cfb1810981e04d4dc00a9552189cfe566e23f8a51579894e2de44dc3d8548aca4ff6a3e16f385a478e3fb2b18 + languageName: node + linkType: hard + +"@smithy/credential-provider-imds@npm:^2.0.0, @smithy/credential-provider-imds@npm:^2.1.5": + version: 2.1.5 + resolution: "@smithy/credential-provider-imds@npm:2.1.5" + dependencies: + "@smithy/node-config-provider": ^2.1.9 + "@smithy/property-provider": ^2.0.17 + "@smithy/types": ^2.8.0 + "@smithy/url-parser": ^2.0.16 + tslib: ^2.5.0 + checksum: 1df3be00235960bc3d6a1703c4d3446d2338ee3684e0f17d2cbefc115ca38e6c1015d0e17116551e59e5adfd02a5923a8dddb83a956e197fd5aa4308ab20acdd + languageName: node + linkType: hard + +"@smithy/eventstream-codec@npm:^2.0.16": + version: 2.0.16 + resolution: "@smithy/eventstream-codec@npm:2.0.16" + dependencies: + "@aws-crypto/crc32": 3.0.0 + "@smithy/types": ^2.8.0 + "@smithy/util-hex-encoding": ^2.0.0 + tslib: ^2.5.0 + checksum: 247b7ab35a49f27527124b5927bc04f7f9974e87a8533fe9b2909ab80fe71cebcd61f21835b16a3f4cbd1550f39d2290e90d5b0fa2a9a05eac11b0ec1c21e2b5 + languageName: node + linkType: hard + +"@smithy/eventstream-serde-browser@npm:^2.0.16": + version: 2.0.16 + resolution: "@smithy/eventstream-serde-browser@npm:2.0.16" + dependencies: + "@smithy/eventstream-serde-universal": ^2.0.16 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: d5517ccc486361f0a5f2b3fc1a68a8667c00fa29e1152390f1ca1fccc4c5c2d4bd7621a2bee642ae2375873bd51a2c0e4830540b074c7e754c260ff8ed898060 + languageName: node + linkType: hard + +"@smithy/eventstream-serde-config-resolver@npm:^2.0.16": + version: 2.0.16 + resolution: "@smithy/eventstream-serde-config-resolver@npm:2.0.16" + dependencies: + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 90f0d30c1a0c46261102c6be6884c70cccc6bbdc44267877884fc96c23f1ee1068e8dea5c8862ecab6bda7d1a889a9d886328b0f25551971ee87a9201409f405 + languageName: node + linkType: hard + +"@smithy/eventstream-serde-node@npm:^2.0.16": + version: 2.0.16 + resolution: "@smithy/eventstream-serde-node@npm:2.0.16" + dependencies: + "@smithy/eventstream-serde-universal": ^2.0.16 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 00f5eb3d4e66e0ad3986cd7e90cbf8f0bd965349073eda7fedd058fe6a638e34a107906308fa891fcf56cec45d0c369f07a7eb167dd055214f389c9f463c7747 + languageName: node + linkType: hard + +"@smithy/eventstream-serde-universal@npm:^2.0.16": + version: 2.0.16 + resolution: "@smithy/eventstream-serde-universal@npm:2.0.16" + dependencies: + "@smithy/eventstream-codec": ^2.0.16 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 89bac869391816b77746801b4acea4b4184d922c6bbfe6c24b5ac640c6769b7a2634193be940e9d651d4be9ceb88d28a40dc25a4b61b81461055b99c8856e336 + languageName: node + linkType: hard + +"@smithy/fetch-http-handler@npm:^2.3.2": + version: 2.3.2 + resolution: "@smithy/fetch-http-handler@npm:2.3.2" + dependencies: + "@smithy/protocol-http": ^3.0.12 + "@smithy/querystring-builder": ^2.0.16 + "@smithy/types": ^2.8.0 + "@smithy/util-base64": ^2.0.1 + tslib: ^2.5.0 + checksum: 883fcfae5ffcc616229dc982a48bf6c44984f652f2ef4ca9d233bfb3c4726fbb54e6a39d78fc410fda718c22a0a0e72a5207a4a4e202755c1ccd8760a0d1d821 + languageName: node + linkType: hard + +"@smithy/hash-blob-browser@npm:^2.0.17": + version: 2.0.17 + resolution: "@smithy/hash-blob-browser@npm:2.0.17" + dependencies: + "@smithy/chunked-blob-reader": ^2.0.0 + "@smithy/chunked-blob-reader-native": ^2.0.1 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 8f44c049fa5e246528660396d15bdec7c40d845fddb92158b36268e3d04b4867108762dbac40d4eccfde9a26d20d313688aa825ec27db324d5dd927801cea38e + languageName: node + linkType: hard + +"@smithy/hash-node@npm:^2.0.18": + version: 2.0.18 + resolution: "@smithy/hash-node@npm:2.0.18" + dependencies: + "@smithy/types": ^2.8.0 + "@smithy/util-buffer-from": ^2.0.0 + "@smithy/util-utf8": ^2.0.2 + tslib: ^2.5.0 + checksum: 1f40ae7b38808b1836c8e166f5182fcbc09423a833728943ab1edbc019dbd83323c9a08a29a2d73cd4ed5b3483e87158f8d4cc8ee5c6c88909c1dcde9a1b9826 + languageName: node + linkType: hard + +"@smithy/hash-stream-node@npm:^2.0.18": + version: 2.0.18 + resolution: "@smithy/hash-stream-node@npm:2.0.18" + dependencies: + "@smithy/types": ^2.8.0 + "@smithy/util-utf8": ^2.0.2 + tslib: ^2.5.0 + checksum: 7d2308b69110710cb24b21b9c6c9f6517fe9a0297043e17f3cccebfd124056a19e2f9b36a070de6e45301ffc934e1340eddbb4f9db33a4136231ce21fa9ceec7 + languageName: node + linkType: hard + +"@smithy/invalid-dependency@npm:^2.0.16": + version: 2.0.16 + resolution: "@smithy/invalid-dependency@npm:2.0.16" + dependencies: + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 1ddc4dfb3740fb6229d20d3074e0c59a6ebafd1f3b31f01eb630fc5ee360e60f495773cae6fc5d357873b6b34ab2d89d58b0887fea4e57f1e1f26d02ab234e5c + languageName: node + linkType: hard + +"@smithy/is-array-buffer@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/is-array-buffer@npm:2.0.0" + dependencies: + tslib: ^2.5.0 + checksum: 6d101cf509a7818667f42d297894f88f86ef41d3cc9d02eae38bbe5e69b16edf83b8e67eb691964d859a16a4e39db1aad323d83f6ae55ae4512a14ff6406c02d + languageName: node + linkType: hard + +"@smithy/md5-js@npm:^2.0.18": + version: 2.0.18 + resolution: "@smithy/md5-js@npm:2.0.18" + dependencies: + "@smithy/types": ^2.8.0 + "@smithy/util-utf8": ^2.0.2 + tslib: ^2.5.0 + checksum: 7cc58fa76c86cfe6d2e6ce8a0c70ee62594269c7d13eb3a4161d0aec8a6c6fa42393e0f02674089d0037414223daf61d31e99c3e9da0c00b5e9681861934ff1b + languageName: node + linkType: hard + +"@smithy/middleware-content-length@npm:^2.0.18": + version: 2.0.18 + resolution: "@smithy/middleware-content-length@npm:2.0.18" + dependencies: + "@smithy/protocol-http": ^3.0.12 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: bbbd3e69e065c1677150a61af2303c3fda35c7fce527fd594f63be00421daecb7fedfd135945b3ef8104cd5305367f7d8e235e704d6743c5fa5d6c0178a35de3 + languageName: node + linkType: hard + +"@smithy/middleware-endpoint@npm:^2.3.0": + version: 2.3.0 + resolution: "@smithy/middleware-endpoint@npm:2.3.0" + dependencies: + "@smithy/middleware-serde": ^2.0.16 + "@smithy/node-config-provider": ^2.1.9 + "@smithy/shared-ini-file-loader": ^2.2.8 + "@smithy/types": ^2.8.0 + "@smithy/url-parser": ^2.0.16 + "@smithy/util-middleware": ^2.0.9 + tslib: ^2.5.0 + checksum: 07512e57190dd9a063359934d6c688bfdc3849657a3d7b91a4194d4b19ca94ad67a5344f0418462147b105ce6d7d8adc895a1ac9d6aefc80937643cdea70e14e + languageName: node + linkType: hard + +"@smithy/middleware-retry@npm:^2.0.26": + version: 2.0.26 + resolution: "@smithy/middleware-retry@npm:2.0.26" + dependencies: + "@smithy/node-config-provider": ^2.1.9 + "@smithy/protocol-http": ^3.0.12 + "@smithy/service-error-classification": ^2.0.9 + "@smithy/smithy-client": ^2.2.1 + "@smithy/types": ^2.8.0 + "@smithy/util-middleware": ^2.0.9 + "@smithy/util-retry": ^2.0.9 + tslib: ^2.5.0 + uuid: ^8.3.2 + checksum: e33d87b539776398e1b5af99e0a0659534194f691c16aaafc22c8ef0ee6fcc2568e3e8bbcb79ad9f114c164f956530b96393f3b25ee15773a23c3f8a5df00e62 + languageName: node + linkType: hard + +"@smithy/middleware-serde@npm:^2.0.16": + version: 2.0.16 + resolution: "@smithy/middleware-serde@npm:2.0.16" + dependencies: + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 64cad569c02bfb53fda13189cb24db72e35e25de058a6d4b51d9e9c89bc97f7aba7373787fb48ad32226b3d3d012d1554378c7c22a3f162f61e8e3fc1d069f5e + languageName: node + linkType: hard + +"@smithy/middleware-stack@npm:^2.0.10": + version: 2.0.10 + resolution: "@smithy/middleware-stack@npm:2.0.10" + dependencies: + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: f2e491a10bf20d0605dfa0fd6e629b0fdcc821a863fb5b1f9ab7c02f2a3180869334da15739c4ad66fe8022c3f5ffb6868dec51023bff9b07977989db8164ebb + languageName: node + linkType: hard + +"@smithy/node-config-provider@npm:^2.1.9": + version: 2.1.9 + resolution: "@smithy/node-config-provider@npm:2.1.9" + dependencies: + "@smithy/property-provider": ^2.0.17 + "@smithy/shared-ini-file-loader": ^2.2.8 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 993c87c85b88671e8dab3d9443f7f832b585e34c5578f655168162e968b14f4f7f5dd04515364a9c7155aedd6dd175f7fb6a14c2318c81b01dd3245086b8e5f1 + languageName: node + linkType: hard + +"@smithy/node-http-handler@npm:^2.2.2": + version: 2.2.2 + resolution: "@smithy/node-http-handler@npm:2.2.2" + dependencies: + "@smithy/abort-controller": ^2.0.16 + "@smithy/protocol-http": ^3.0.12 + "@smithy/querystring-builder": ^2.0.16 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 891532bfb6c1d3f3aed710bf8ed922706270effc8d8d00d4133e6271f58525ed9ccf7d03e16c0baf7a4e8b2768d5e38b92beca23f9b6aac8e02e8bc7c6769a81 + languageName: node + linkType: hard + +"@smithy/property-provider@npm:^2.0.0, @smithy/property-provider@npm:^2.0.17": + version: 2.0.17 + resolution: "@smithy/property-provider@npm:2.0.17" + dependencies: + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: ecf2c909fd365cfe59bece32538131ffb2bcdc55a2a287722b1eefcac4c83de7f7f7dc92e4829cdbb74cc3c15f6fad8617eb97ec97ed1fc7ca9180ba7f758229 + languageName: node + linkType: hard + +"@smithy/protocol-http@npm:^3.0.12": + version: 3.0.12 + resolution: "@smithy/protocol-http@npm:3.0.12" + dependencies: + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 38899820a59ebcdf4784b16d6a02fa630441a76857f204e479bd8c59ec7c578e5107e995fc0b1b9dee444b9610bd9bdf00ccf7e1c806ff463c7e9402660748e1 + languageName: node + linkType: hard + +"@smithy/querystring-builder@npm:^2.0.16": + version: 2.0.16 + resolution: "@smithy/querystring-builder@npm:2.0.16" + dependencies: + "@smithy/types": ^2.8.0 + "@smithy/util-uri-escape": ^2.0.0 + tslib: ^2.5.0 + checksum: d88983a2088dd2d00dced8e122ee20c278edf00f80ff79485187efb178d3c1a00e679f5059c605d914d646dac37e35fd458bbc20a56da0e2ac6e168dc2a8f91b + languageName: node + linkType: hard + +"@smithy/querystring-parser@npm:^2.0.16": + version: 2.0.16 + resolution: "@smithy/querystring-parser@npm:2.0.16" + dependencies: + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: f00fcfa102838a32afa43b3e4e9dd706f1d79e09778767f089f97ee47809d47e9dd6681618dac0903913aa7ae64479ee7be9269c5e7e7e0b29527ea63cde3e26 + languageName: node + linkType: hard + +"@smithy/service-error-classification@npm:^2.0.9": + version: 2.0.9 + resolution: "@smithy/service-error-classification@npm:2.0.9" + dependencies: + "@smithy/types": ^2.8.0 + checksum: 76f4fcae188e6d318d09e9974d6b563cb1329d66788d6ada882974cf3c59046b174164b35d2a9239a8cc8747a07a81831e44b4032752ad220819cd8495962c90 + languageName: node + linkType: hard + +"@smithy/shared-ini-file-loader@npm:^2.0.6, @smithy/shared-ini-file-loader@npm:^2.2.8": + version: 2.2.8 + resolution: "@smithy/shared-ini-file-loader@npm:2.2.8" + dependencies: + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 768b57b0f783e7ce9df08e23de0cbfb85e686dcd7f0014cf78747696247941ded20a79df84ab30fe96a927ab51c937264b7e70d90a94d4ac32a44972569cb4f7 + languageName: node + linkType: hard + +"@smithy/signature-v4@npm:^2.0.0": + version: 2.0.19 + resolution: "@smithy/signature-v4@npm:2.0.19" + dependencies: + "@smithy/eventstream-codec": ^2.0.16 + "@smithy/is-array-buffer": ^2.0.0 + "@smithy/types": ^2.8.0 + "@smithy/util-hex-encoding": ^2.0.0 + "@smithy/util-middleware": ^2.0.9 + "@smithy/util-uri-escape": ^2.0.0 + "@smithy/util-utf8": ^2.0.2 + tslib: ^2.5.0 + checksum: 699855d6e0386767e956d65d43286fb2e75ee90ff592ec0176d1a10cd619aa53e55d7e3f1ef644e177d45f548b7ace5ce1eb53ffeba39f757718be1837323c21 + languageName: node + linkType: hard + +"@smithy/smithy-client@npm:^2.2.1": + version: 2.2.1 + resolution: "@smithy/smithy-client@npm:2.2.1" + dependencies: + "@smithy/middleware-endpoint": ^2.3.0 + "@smithy/middleware-stack": ^2.0.10 + "@smithy/protocol-http": ^3.0.12 + "@smithy/types": ^2.8.0 + "@smithy/util-stream": ^2.0.24 + tslib: ^2.5.0 + checksum: dda90afce6f3260217967c184065a3b07be699102a36e64357124394c7a075496e40d18e7b0d23f1204748777fcc08b7d51d8f0170e877a32dd306a4ff757748 + languageName: node + linkType: hard + +"@smithy/types@npm:^2.8.0": + version: 2.8.0 + resolution: "@smithy/types@npm:2.8.0" + dependencies: + tslib: ^2.5.0 + checksum: c0c85b1422f982635c8b5c6477f5d2b28a5afeaf21f6e877dc1f96e401673632b5abaf3b49f800f1859119c498151e5a59e0361c8f56945f79642c486ac68af8 + languageName: node + linkType: hard + +"@smithy/url-parser@npm:^2.0.16": + version: 2.0.16 + resolution: "@smithy/url-parser@npm:2.0.16" + dependencies: + "@smithy/querystring-parser": ^2.0.16 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 05d9cca95307f3acd53e3a31af96b83e2351e3f64b68672afdda32b5aa6d902c05f2567b2a683ed32132c152e0b8d38200c803f63f9e8c983b976ccf999fcdc4 + languageName: node + linkType: hard + +"@smithy/util-base64@npm:^2.0.1": + version: 2.0.1 + resolution: "@smithy/util-base64@npm:2.0.1" + dependencies: + "@smithy/util-buffer-from": ^2.0.0 + tslib: ^2.5.0 + checksum: 6320916b50a0f4048462564cbc413e619ee02747e188463721670ce554d0b1652517068a1aa066209101a2185b4f3d13afd0c173aac99c461ca685a1fa15f934 + languageName: node + linkType: hard + +"@smithy/util-body-length-browser@npm:^2.0.1": + version: 2.0.1 + resolution: "@smithy/util-body-length-browser@npm:2.0.1" + dependencies: + tslib: ^2.5.0 + checksum: 1d342acdba493047400a1aae9922e7274a2d4ba68f2980290ac4d44bd1a33a2a0a9d75b99c773924a7381d88c7b8cc612947e3adb442f7f67ac2edd4a4d3cf58 + languageName: node + linkType: hard + +"@smithy/util-body-length-node@npm:^2.1.0": + version: 2.1.0 + resolution: "@smithy/util-body-length-node@npm:2.1.0" + dependencies: + tslib: ^2.5.0 + checksum: e4635251898f12e1825f2848e0b7cc9d01ec6635b3f1f71b790734bb702b88e795f6c539d42d95472dad00e50e9ff13fcf396791092b131e5834069cb8f52ed0 + languageName: node + linkType: hard + +"@smithy/util-buffer-from@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-buffer-from@npm:2.0.0" + dependencies: + "@smithy/is-array-buffer": ^2.0.0 + tslib: ^2.5.0 + checksum: d33cbf3e488d23390c88705ddae71b08de7a87b6453e38b508cd37a22a02e8b5be9f0cd46c1347b496c3977a815a7399b18840544ecdc4cce8cf3dcd0f5bb009 + languageName: node + linkType: hard + +"@smithy/util-config-provider@npm:^2.1.0": + version: 2.1.0 + resolution: "@smithy/util-config-provider@npm:2.1.0" + dependencies: + tslib: ^2.5.0 + checksum: bd8b677fdf1891e5ec97f6fe0ab3e798ed005fd56c3868d6f529f523fbf077999f6af04295142be7f6d87551920ae0a455a6c74f3e4de972e8cd2070b569a5b1 + languageName: node + linkType: hard + +"@smithy/util-defaults-mode-browser@npm:^2.0.24": + version: 2.0.24 + resolution: "@smithy/util-defaults-mode-browser@npm:2.0.24" + dependencies: + "@smithy/property-provider": ^2.0.17 + "@smithy/smithy-client": ^2.2.1 + "@smithy/types": ^2.8.0 + bowser: ^2.11.0 + tslib: ^2.5.0 + checksum: 711f08ac4762b70ce91fd29592228d9c2a48b2a10ea04796a4340d9eae08981d82bea26730ee740dd77381cba59a6e449556417dbbb1fa8bf089d2c649a62af4 + languageName: node + linkType: hard + +"@smithy/util-defaults-mode-node@npm:^2.0.32": + version: 2.0.32 + resolution: "@smithy/util-defaults-mode-node@npm:2.0.32" + dependencies: + "@smithy/config-resolver": ^2.0.23 + "@smithy/credential-provider-imds": ^2.1.5 + "@smithy/node-config-provider": ^2.1.9 + "@smithy/property-provider": ^2.0.17 + "@smithy/smithy-client": ^2.2.1 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 3bccae4e22307a25c0f5c0718dec6d0a1349586a64948e6211f2ba05bd02e226db87949e653ac34333f0646cc169b4c330fdaf102b594ea95c191acba5a2cbef + languageName: node + linkType: hard + +"@smithy/util-endpoints@npm:^1.0.8": + version: 1.0.8 + resolution: "@smithy/util-endpoints@npm:1.0.8" + dependencies: + "@smithy/node-config-provider": ^2.1.9 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 8133e253f390ea1456e2f13f1853f258827497042109f2933f1c7fc47307f865e490ba7fafc22f6abacf609e10e17b67f2d62c0c48f8d88f3b9e94de4e88ff62 + languageName: node + linkType: hard + +"@smithy/util-hex-encoding@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-hex-encoding@npm:2.0.0" + dependencies: + tslib: ^2.5.0 + checksum: 884373e089d909e3c9805bdb78f367d1f3612e4e1e6d8f0263cc82a8b9689eddc0bc80b8b58aa711bd5b48d9cb124f9996906c172e951c9dac78984459e831cf + languageName: node + linkType: hard + +"@smithy/util-middleware@npm:^2.0.9": + version: 2.0.9 + resolution: "@smithy/util-middleware@npm:2.0.9" + dependencies: + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 0c34552d6845ef215441602a16ac6e02e2a5a8ab70e1b2ed0dc37a7acbf5de6c7f2de9ba09f303c2bfbfa77a0a4869f6660874c9f6daeed757392fb42466e01a + languageName: node + linkType: hard + +"@smithy/util-retry@npm:^2.0.9": + version: 2.0.9 + resolution: "@smithy/util-retry@npm:2.0.9" + dependencies: + "@smithy/service-error-classification": ^2.0.9 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 40385b48c846e2c8d79531789d6bbbd3c7342c607b7227a8c5e873b481e9425705927b26c65e7b71b20ff851e9b54d036b279a485c2a13a828359b7ef6d36fa4 + languageName: node + linkType: hard + +"@smithy/util-stream@npm:^2.0.24": + version: 2.0.24 + resolution: "@smithy/util-stream@npm:2.0.24" + dependencies: + "@smithy/fetch-http-handler": ^2.3.2 + "@smithy/node-http-handler": ^2.2.2 + "@smithy/types": ^2.8.0 + "@smithy/util-base64": ^2.0.1 + "@smithy/util-buffer-from": ^2.0.0 + "@smithy/util-hex-encoding": ^2.0.0 + "@smithy/util-utf8": ^2.0.2 + tslib: ^2.5.0 + checksum: 097e6be8f59d166a5611f09a7823b55a1cf42726ac7c48ac93d8a3d5f1f5423ee6e74fda1081f49e03802f2f788646d5db50ae74798e9644e4db642452dfa101 + languageName: node + linkType: hard + +"@smithy/util-uri-escape@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-uri-escape@npm:2.0.0" + dependencies: + tslib: ^2.5.0 + checksum: d201cee524ece997c406902463b5ea0b72599994f7b3ac1d923d5645497e9ef93126d146016f13dd4afafe33b9a3e92faf4e023cf0af510b270c1b9ce3d78da8 + languageName: node + linkType: hard + +"@smithy/util-utf8@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/util-utf8@npm:2.0.2" + dependencies: + "@smithy/util-buffer-from": ^2.0.0 + tslib: ^2.5.0 + checksum: e38fd6324ca2858f76fb6fce427c03faec599213acf95a5b18eb77b72cdf9327bd688e5a260dbccc0f512ea5426422ed200122a9542c00b14a6d9becc3f84c79 + languageName: node + linkType: hard + +"@smithy/util-waiter@npm:^2.0.16": + version: 2.0.16 + resolution: "@smithy/util-waiter@npm:2.0.16" + dependencies: + "@smithy/abort-controller": ^2.0.16 + "@smithy/types": ^2.8.0 + tslib: ^2.5.0 + checksum: 11164f94742987f19337e6f26335f1b39e17cf9eb4e7f3e1f772c3168df2b9a4ccb2f92a42666593646ba9ff1efa3391b3c697b944e4c282007c28f35b5369e3 + languageName: node + linkType: hard + +"@supabase/auth-helpers-remix@npm:^0.2.2": + version: 0.2.6 + resolution: "@supabase/auth-helpers-remix@npm:0.2.6" + dependencies: + "@supabase/auth-helpers-shared": 0.6.3 + peerDependencies: + "@supabase/supabase-js": ^2.19.0 + checksum: fc5c9ea3f4b07f724f5b1c5eb5ef94df6e1b7db2da5e262c40ce8052b579540af475064f97a9be1d8e8aafcb52868ebdccc61354775ad770ee041c7997a499d0 + languageName: node + linkType: hard + +"@supabase/auth-helpers-shared@npm:0.6.3": + version: 0.6.3 + resolution: "@supabase/auth-helpers-shared@npm:0.6.3" + dependencies: + jose: ^4.14.4 + peerDependencies: + "@supabase/supabase-js": ^2.19.0 + checksum: 6eb9a00c3b535ba0f278005b94a682e1b2b2c29dc86cc3abc7dd0f0545eb4b0d8606285644f624d11228e8536fa7a0c67be72cd6922f96ee9555c5737916b74e + languageName: node + linkType: hard + +"@supabase/functions-js@npm:^2.1.5": + version: 2.1.5 + resolution: "@supabase/functions-js@npm:2.1.5" + dependencies: + "@supabase/node-fetch": ^2.6.14 + checksum: f2ab8636af8d982270b61631a5120369ca10db101b4298da71be892e5d91a8ddaddcf7f51079ad0fe24731a15892b21bd7dbe41b997da9d4b90e4326d09632c8 + languageName: node + linkType: hard + +"@supabase/gotrue-js@npm:^2.60.0": + version: 2.62.0 + resolution: "@supabase/gotrue-js@npm:2.62.0" + dependencies: + "@supabase/node-fetch": ^2.6.14 + checksum: 7df7078c7eb2b99658cc924fa41ae95305ba9e29fd8a56541adbe6901133a370ecda1d40553e27bcd8e782fb8f4a1c13b79ff4422af51bdcb5d614a10779340b + languageName: node + linkType: hard + +"@supabase/node-fetch@npm:^2.6.14": + version: 2.6.15 + resolution: "@supabase/node-fetch@npm:2.6.15" + dependencies: + whatwg-url: ^5.0.0 + checksum: 9673b49236a56df49eb7ea5cb789cf4e8b1393069b84b4964ac052995e318a34872f428726d128f232139e17c3375a531e45e99edd3e96a25cce60d914b53879 + languageName: node + linkType: hard + +"@supabase/postgrest-js@npm:^1.9.0": + version: 1.9.2 + resolution: "@supabase/postgrest-js@npm:1.9.2" + dependencies: + "@supabase/node-fetch": ^2.6.14 + checksum: 9aefbdfc1c0d8a00b932b0939dbcbb5ec392b1324ad1b63b5e0486c6f9882a9c2292c80d3f803a0338938097372f08b3bcbdc3c4699d5bef13791ddc35d53b86 + languageName: node + linkType: hard + +"@supabase/realtime-js@npm:^2.9.3": + version: 2.9.3 + resolution: "@supabase/realtime-js@npm:2.9.3" + dependencies: + "@supabase/node-fetch": ^2.6.14 + "@types/phoenix": ^1.5.4 + "@types/ws": ^8.5.10 + ws: ^8.14.2 + checksum: 180a5084b94a4e324fc04041182bf8819c3c2545a731c276a56f9647f78078180b0460b68a0d6c568d29b2fa4aace0545bb71dcb89b547ec85781032dff74e71 + languageName: node + linkType: hard + +"@supabase/storage-js@npm:^2.5.4": + version: 2.5.5 + resolution: "@supabase/storage-js@npm:2.5.5" + dependencies: + "@supabase/node-fetch": ^2.6.14 + checksum: 4470499113c15e1124d99048eef0097c7ba431d728e351519ee26948775171d6c6bb41156f8ffb3860009b82b93809af01c9d075ece6000f783f59ce9fd00ee8 + languageName: node + linkType: hard + +"@supabase/supabase-js@npm:^2.33.2": + version: 2.39.3 + resolution: "@supabase/supabase-js@npm:2.39.3" + dependencies: + "@supabase/functions-js": ^2.1.5 + "@supabase/gotrue-js": ^2.60.0 + "@supabase/node-fetch": ^2.6.14 + "@supabase/postgrest-js": ^1.9.0 + "@supabase/realtime-js": ^2.9.3 + "@supabase/storage-js": ^2.5.4 + checksum: df8a5fe7a06cd26069a0add024af46021b04bf3f5101259932103f7d76b45eee3b4d7f3164335844ceebb9cef83601ded9ba95dfc7da7ce9bbf2708702f90deb + languageName: node + linkType: hard + +"@surma/rollup-plugin-off-main-thread@npm:^2.2.3": + version: 2.2.3 + resolution: "@surma/rollup-plugin-off-main-thread@npm:2.2.3" + dependencies: + ejs: ^3.1.6 + json5: ^2.2.0 + magic-string: ^0.25.0 + string.prototype.matchall: ^4.0.6 + checksum: 2c021349442e2e2cec96bb50fd82ec8bf8514d909bc73594f6cfc89b3b68f2feed909a8161d7d307d9455585c97e6b66853ce334db432626c7596836d4549c0c + languageName: node + linkType: hard + "@swc/core-darwin-arm64@npm:1.3.102": version: 1.3.102 resolution: "@swc/core-darwin-arm64@npm:1.3.102" @@ -4851,6 +7104,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-darwin-arm64@npm:1.3.103": + version: 1.3.103 + resolution: "@swc/core-darwin-arm64@npm:1.3.103" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@swc/core-darwin-x64@npm:1.3.102": version: 1.3.102 resolution: "@swc/core-darwin-x64@npm:1.3.102" @@ -4858,6 +7118,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-darwin-x64@npm:1.3.103": + version: 1.3.103 + resolution: "@swc/core-darwin-x64@npm:1.3.103" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@swc/core-linux-arm-gnueabihf@npm:1.3.102": version: 1.3.102 resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.102" @@ -4865,6 +7132,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-linux-arm-gnueabihf@npm:1.3.103": + version: 1.3.103 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.103" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@swc/core-linux-arm64-gnu@npm:1.3.102": version: 1.3.102 resolution: "@swc/core-linux-arm64-gnu@npm:1.3.102" @@ -4872,6 +7146,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-linux-arm64-gnu@npm:1.3.103": + version: 1.3.103 + resolution: "@swc/core-linux-arm64-gnu@npm:1.3.103" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + "@swc/core-linux-arm64-musl@npm:1.3.102": version: 1.3.102 resolution: "@swc/core-linux-arm64-musl@npm:1.3.102" @@ -4879,6 +7160,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-linux-arm64-musl@npm:1.3.103": + version: 1.3.103 + resolution: "@swc/core-linux-arm64-musl@npm:1.3.103" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + "@swc/core-linux-x64-gnu@npm:1.3.102": version: 1.3.102 resolution: "@swc/core-linux-x64-gnu@npm:1.3.102" @@ -4886,6 +7174,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-linux-x64-gnu@npm:1.3.103": + version: 1.3.103 + resolution: "@swc/core-linux-x64-gnu@npm:1.3.103" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + "@swc/core-linux-x64-musl@npm:1.3.102": version: 1.3.102 resolution: "@swc/core-linux-x64-musl@npm:1.3.102" @@ -4893,6 +7188,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-linux-x64-musl@npm:1.3.103": + version: 1.3.103 + resolution: "@swc/core-linux-x64-musl@npm:1.3.103" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + "@swc/core-win32-arm64-msvc@npm:1.3.102": version: 1.3.102 resolution: "@swc/core-win32-arm64-msvc@npm:1.3.102" @@ -4900,6 +7202,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-win32-arm64-msvc@npm:1.3.103": + version: 1.3.103 + resolution: "@swc/core-win32-arm64-msvc@npm:1.3.103" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@swc/core-win32-ia32-msvc@npm:1.3.102": version: 1.3.102 resolution: "@swc/core-win32-ia32-msvc@npm:1.3.102" @@ -4907,6 +7216,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-win32-ia32-msvc@npm:1.3.103": + version: 1.3.103 + resolution: "@swc/core-win32-ia32-msvc@npm:1.3.103" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@swc/core-win32-x64-msvc@npm:1.3.102": version: 1.3.102 resolution: "@swc/core-win32-x64-msvc@npm:1.3.102" @@ -4914,6 +7230,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-win32-x64-msvc@npm:1.3.103": + version: 1.3.103 + resolution: "@swc/core-win32-x64-msvc@npm:1.3.103" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@swc/core@npm:^1.3.55": version: 1.3.102 resolution: "@swc/core@npm:1.3.102" @@ -4960,6 +7283,52 @@ __metadata: languageName: node linkType: hard +"@swc/core@npm:^1.3.96": + version: 1.3.103 + resolution: "@swc/core@npm:1.3.103" + dependencies: + "@swc/core-darwin-arm64": 1.3.103 + "@swc/core-darwin-x64": 1.3.103 + "@swc/core-linux-arm-gnueabihf": 1.3.103 + "@swc/core-linux-arm64-gnu": 1.3.103 + "@swc/core-linux-arm64-musl": 1.3.103 + "@swc/core-linux-x64-gnu": 1.3.103 + "@swc/core-linux-x64-musl": 1.3.103 + "@swc/core-win32-arm64-msvc": 1.3.103 + "@swc/core-win32-ia32-msvc": 1.3.103 + "@swc/core-win32-x64-msvc": 1.3.103 + "@swc/counter": ^0.1.1 + "@swc/types": ^0.1.5 + peerDependencies: + "@swc/helpers": ^0.5.0 + dependenciesMeta: + "@swc/core-darwin-arm64": + optional: true + "@swc/core-darwin-x64": + optional: true + "@swc/core-linux-arm-gnueabihf": + optional: true + "@swc/core-linux-arm64-gnu": + optional: true + "@swc/core-linux-arm64-musl": + optional: true + "@swc/core-linux-x64-gnu": + optional: true + "@swc/core-linux-x64-musl": + optional: true + "@swc/core-win32-arm64-msvc": + optional: true + "@swc/core-win32-ia32-msvc": + optional: true + "@swc/core-win32-x64-msvc": + optional: true + peerDependenciesMeta: + "@swc/helpers": + optional: true + checksum: cff453fd8f186bcd12c8a2f727dcc578a75382b7ca851054021f625ab940ad5463073d90c8dd13a0f4e1577defa4de039d59da280dce88a6b582d15f46edeed8 + languageName: node + linkType: hard + "@swc/counter@npm:^0.1.1": version: 0.1.2 resolution: "@swc/counter@npm:0.1.2" @@ -5061,6 +7430,19 @@ __metadata: languageName: unknown linkType: soft +"@tldraw/bookmark-extractor@workspace:apps/dotcom-bookmark-extractor": + version: 0.0.0-use.local + resolution: "@tldraw/bookmark-extractor@workspace:apps/dotcom-bookmark-extractor" + dependencies: + "@types/cors": ^2.8.15 + cors: ^2.8.5 + grabity: ^1.0.5 + lazyrepo: 0.0.0-alpha.27 + tslib: ^2.6.2 + typescript: ^5.0.2 + languageName: unknown + linkType: soft + "@tldraw/docs@workspace:apps/docs": version: 0.0.0-use.local resolution: "@tldraw/docs@workspace:apps/docs" @@ -5107,6 +7489,30 @@ __metadata: languageName: unknown linkType: soft +"@tldraw/dotcom-worker@workspace:apps/dotcom-worker": + version: 0.0.0-use.local + resolution: "@tldraw/dotcom-worker@workspace:apps/dotcom-worker" + dependencies: + "@cloudflare/workers-types": ^4.20230821.0 + "@supabase/auth-helpers-remix": ^0.2.2 + "@supabase/supabase-js": ^2.33.2 + "@tldraw/store": "workspace:*" + "@tldraw/tlschema": "workspace:*" + "@tldraw/tlsync": "workspace:*" + "@tldraw/utils": "workspace:*" + concurrently: ^8.2.1 + esbuild: ^0.18.4 + itty-router: ^4.0.13 + lazyrepo: 0.0.0-alpha.27 + nanoid: 4.0.2 + picocolors: ^1.0.0 + strip-ansi: ^7.1.0 + toucan-js: ^2.7.0 + typescript: ^5.0.2 + wrangler: 3.16.0 + languageName: unknown + linkType: soft + "@tldraw/editor@workspace:*, @tldraw/editor@workspace:packages/editor": version: 0.0.0-use.local resolution: "@tldraw/editor@workspace:packages/editor" @@ -5153,6 +7559,7 @@ __metadata: dependencies: "@microsoft/api-extractor": ^7.35.4 "@next/eslint-plugin-next": ^13.3.0 + "@sentry/cli": ^2.25.0 "@swc/core": ^1.3.55 "@swc/jest": ^0.2.26 "@types/glob": ^8.1.0 @@ -5163,6 +7570,7 @@ __metadata: "@typescript-eslint/eslint-plugin": ^5.57.0 "@typescript-eslint/parser": ^5.57.0 auto: ^10.46.0 + cross-env: ^7.0.3 eslint: ^8.37.0 eslint-config-prettier: ^8.8.0 eslint-plugin-deprecation: ^2.0.0 @@ -5192,9 +7600,13 @@ __metadata: version: 0.0.0-use.local resolution: "@tldraw/scripts@workspace:scripts" dependencies: + "@actions/github": ^6.0.0 "@auto-it/core": ^10.45.0 + "@aws-sdk/client-s3": ^3.440.0 + "@aws-sdk/lib-storage": ^3.440.0 "@types/is-ci": ^3.0.0 "@types/node": ^18.7.3 + "@types/tar": ^6.1.7 "@typescript-eslint/utils": ^5.59.0 ast-types: ^0.14.2 cross-fetch: ^3.1.5 @@ -5210,6 +7622,7 @@ __metadata: rimraf: ^4.4.0 semver: ^7.3.8 svgo: ^3.0.2 + tar: ^6.2.0 typescript: ^5.2.2 languageName: unknown linkType: soft @@ -5291,6 +7704,25 @@ __metadata: languageName: unknown linkType: soft +"@tldraw/tlsync@workspace:*, @tldraw/tlsync@workspace:packages/tlsync": + version: 0.0.0-use.local + resolution: "@tldraw/tlsync@workspace:packages/tlsync" + dependencies: + "@tldraw/state": "workspace:*" + "@tldraw/store": "workspace:*" + "@tldraw/tldraw": "workspace:*" + "@tldraw/tlschema": "workspace:*" + "@tldraw/utils": "workspace:*" + lodash.isequal: ^4.5.0 + nanoevents: ^7.0.1 + nanoid: 4.0.2 + typescript: ^5.0.2 + uuid-by-string: ^4.0.0 + uuid-readable: ^0.0.2 + ws: ^8.16.0 + languageName: unknown + linkType: soft + "@tldraw/utils@workspace:*, @tldraw/utils@workspace:packages/utils": version: 0.0.0-use.local resolution: "@tldraw/utils@workspace:packages/utils" @@ -5526,6 +7958,13 @@ __metadata: languageName: node linkType: hard +"@types/cookie@npm:0.5.0": + version: 0.5.0 + resolution: "@types/cookie@npm:0.5.0" + checksum: c0ea731cfe2f08dbc8851fa27212e5b34dadb871c14892d309b0970a158d36bf3d20324847263e0698ce6b1c3a5f151bd4fe45c0f9fc3243d1c8115e41d0e1ce + languageName: node + linkType: hard + "@types/cookie@npm:^0.4.0": version: 0.4.1 resolution: "@types/cookie@npm:0.4.1" @@ -5540,6 +7979,15 @@ __metadata: languageName: node linkType: hard +"@types/cors@npm:^2.8.15": + version: 2.8.17 + resolution: "@types/cors@npm:2.8.17" + dependencies: + "@types/node": "*" + checksum: 469bd85e29a35977099a3745c78e489916011169a664e97c4c3d6538143b0a16e4cc72b05b407dc008df3892ed7bf595f9b7c0f1f4680e169565ee9d64966bde + languageName: node + linkType: hard + "@types/debug@npm:^4.0.0": version: 4.1.12 resolution: "@types/debug@npm:4.1.12" @@ -5567,13 +8015,20 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:^1.0.0": +"@types/estree@npm:*, @types/estree@npm:1.0.5, @types/estree@npm:^1.0.0": version: 1.0.5 resolution: "@types/estree@npm:1.0.5" checksum: dd8b5bed28e6213b7acd0fb665a84e693554d850b0df423ac8076cc3ad5823a6bc26b0251d080bdc545af83179ede51dd3f6fa78cad2c46ed1f29624ddf3e41a languageName: node linkType: hard +"@types/estree@npm:0.0.39": + version: 0.0.39 + resolution: "@types/estree@npm:0.0.39" + checksum: 412fb5b9868f2c418126451821833414189b75cc6bf84361156feed733e3d92ec220b9d74a89e52722e03d5e241b2932732711b7497374a404fad49087adc248 + languageName: node + linkType: hard + "@types/fs-extra@npm:^11.0.1": version: 11.0.4 resolution: "@types/fs-extra@npm:11.0.4" @@ -5751,7 +8206,7 @@ __metadata: languageName: node linkType: hard -"@types/jsonwebtoken@npm:^9.0.0": +"@types/jsonwebtoken@npm:^9.0.0, @types/jsonwebtoken@npm:^9.0.1": version: 9.0.5 resolution: "@types/jsonwebtoken@npm:9.0.5" dependencies: @@ -5866,6 +8321,15 @@ __metadata: languageName: node linkType: hard +"@types/node-forge@npm:^1.3.0": + version: 1.3.11 + resolution: "@types/node-forge@npm:1.3.11" + dependencies: + "@types/node": "*" + checksum: 1e86bd55b92a492eaafd75f6d01f31e7d86a5cdadd0c6bcdc0b1df4103b7f99bb75b832efd5217c7ddda5c781095dc086a868e20b9de00f5a427ddad4c296cd5 + languageName: node + linkType: hard + "@types/node@npm:*": version: 20.11.0 resolution: "@types/node@npm:20.11.0" @@ -5905,6 +8369,13 @@ __metadata: languageName: node linkType: hard +"@types/phoenix@npm:^1.5.4": + version: 1.6.4 + resolution: "@types/phoenix@npm:1.6.4" + checksum: 0f13849602db6d9a2a4b9d96386c45471acedf2bc3d6bf6b3289876fa73f0fe0e84c8466bd55c4f2763d33e142e5311c220820fed9ac2a21d9126f1f70a7338f + languageName: node + linkType: hard + "@types/prettier@npm:^2.1.5": version: 2.7.3 resolution: "@types/prettier@npm:2.7.3" @@ -5919,6 +8390,15 @@ __metadata: languageName: node linkType: hard +"@types/qrcode@npm:^1.5.0": + version: 1.5.5 + resolution: "@types/qrcode@npm:1.5.5" + dependencies: + "@types/node": "*" + checksum: d92c1d3e77406bf13a03ec521b2ffb1ac99b2e6ea3a17cad670f2610f62e1293554c57e4074bb2fd4e9369f475f863b69e0ae8c543cb049c4a3c1b0c2d92522a + languageName: node + linkType: hard + "@types/react-dom@npm:^18.0.0, @types/react-dom@npm:^18.2.18": version: 18.2.18 resolution: "@types/react-dom@npm:18.2.18" @@ -5969,6 +8449,26 @@ __metadata: languageName: node linkType: hard +"@types/react@npm:^18.2.33": + version: 18.2.48 + resolution: "@types/react@npm:18.2.48" + dependencies: + "@types/prop-types": "*" + "@types/scheduler": "*" + csstype: ^3.0.2 + checksum: c9ca43ed2995389b7e09492c24e6f911a8439bb8276dd17cc66a2fbebbf0b42daf7b2ad177043256533607c2ca644d7d928fdfce37a67af1f8646d2bac988900 + languageName: node + linkType: hard + +"@types/resolve@npm:1.17.1": + version: 1.17.1 + resolution: "@types/resolve@npm:1.17.1" + dependencies: + "@types/node": "*" + checksum: dc6a6df507656004e242dcb02c784479deca516d5f4b58a1707e708022b269ae147e1da0521f3e8ad0d63638869d87e0adc023f0bd5454aa6f72ac66c7525cf5 + languageName: node + linkType: hard + "@types/responselike@npm:^1.0.0": version: 1.0.3 resolution: "@types/responselike@npm:1.0.3" @@ -6022,6 +8522,16 @@ __metadata: languageName: node linkType: hard +"@types/tar@npm:^6.1.7": + version: 6.1.10 + resolution: "@types/tar@npm:6.1.10" + dependencies: + "@types/node": "*" + minipass: ^4.0.0 + checksum: 82d46f1246e830b98833ed94fbdfbcb3ffb8a5d7173609622d56c9326124523a3cc541607876c63a6ab9117eb59fb37ef8a6284b194164ab1d1d8c03a8ba59c6 + languageName: node + linkType: hard + "@types/testing-library__jest-dom@npm:^5.9.1": version: 5.14.9 resolution: "@types/testing-library__jest-dom@npm:5.14.9" @@ -6038,6 +8548,13 @@ __metadata: languageName: node linkType: hard +"@types/trusted-types@npm:^2.0.2": + version: 2.0.7 + resolution: "@types/trusted-types@npm:2.0.7" + checksum: 8e4202766a65877efcf5d5a41b7dd458480b36195e580a3b1085ad21e948bc417d55d6f8af1fd2a7ad008015d4117d5fdfe432731157da3c68678487174e4ba3 + languageName: node + linkType: hard + "@types/unist@npm:*, @types/unist@npm:^3.0.0": version: 3.0.2 resolution: "@types/unist@npm:3.0.2" @@ -6066,7 +8583,7 @@ __metadata: languageName: node linkType: hard -"@types/ws@npm:^8.5.9": +"@types/ws@npm:^8.5.10, @types/ws@npm:^8.5.3, @types/ws@npm:^8.5.9": version: 8.5.10 resolution: "@types/ws@npm:8.5.10" dependencies: @@ -6124,7 +8641,7 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:^5.10.2, @typescript-eslint/parser@npm:^5.42.0, @typescript-eslint/parser@npm:^5.57.0": +"@typescript-eslint/parser@npm:^5.10.2, @typescript-eslint/parser@npm:^5.21.0, @typescript-eslint/parser@npm:^5.42.0, @typescript-eslint/parser@npm:^5.57.0": version: 5.62.0 resolution: "@typescript-eslint/parser@npm:5.62.0" dependencies: @@ -6566,6 +9083,17 @@ __metadata: languageName: node linkType: hard +"@vitejs/plugin-react-swc@npm:^3.5.0": + version: 3.5.0 + resolution: "@vitejs/plugin-react-swc@npm:3.5.0" + dependencies: + "@swc/core": ^1.3.96 + peerDependencies: + vite: ^4 || ^5 + checksum: 3ee2e194aa3f582db912c44ac6e2625bfba1cb1252c3d4b8b60d480d36702a3c0ca86e5848f829e43266f0b6eccdb3c66b60b34094fb61139e4dcabd9afc34b6 + languageName: node + linkType: hard + "@vitejs/plugin-react@npm:^4.2.0": version: 4.2.1 resolution: "@vitejs/plugin-react@npm:4.2.1" @@ -6595,6 +9123,13 @@ __metadata: languageName: node linkType: hard +"abab@npm:^1.0.3": + version: 1.0.4 + resolution: "abab@npm:1.0.4" + checksum: 6551e127ec54095f18d5f0942cc75bc61c021ba152a2b235d1214cbda7816fe97202fc2bf0365d3e347c76e7dceb3394d767986db8986272306b3c62b0f895ab + languageName: node + linkType: hard + "abab@npm:^2.0.5, abab@npm:^2.0.6": version: 2.0.6 resolution: "abab@npm:2.0.6" @@ -6635,6 +9170,16 @@ __metadata: languageName: node linkType: hard +"acorn-globals@npm:^4.0.0": + version: 4.3.4 + resolution: "acorn-globals@npm:4.3.4" + dependencies: + acorn: ^6.0.1 + acorn-walk: ^6.0.1 + checksum: c31bfde102d8a104835e9591c31dd037ec771449f9c86a6b1d2ac3c7c336694f828cfabba7687525b094f896a854affbf1afe6e1b12c0d998be6bab5d49c9663 + languageName: node + linkType: hard + "acorn-globals@npm:^6.0.0": version: 6.0.0 resolution: "acorn-globals@npm:6.0.0" @@ -6664,6 +9209,13 @@ __metadata: languageName: node linkType: hard +"acorn-walk@npm:^6.0.1": + version: 6.2.0 + resolution: "acorn-walk@npm:6.2.0" + checksum: ea241a5d96338f1e8030aafae72a91ff0ec4360e2775e44a2fdb2eb618b07fc309e000a5126056631ac7f00fe8bd9bbd23fcb6d018eee4ba11086eb36c1b2e61 + languageName: node + linkType: hard + "acorn-walk@npm:^7.1.1": version: 7.2.0 resolution: "acorn-walk@npm:7.2.0" @@ -6678,6 +9230,24 @@ __metadata: languageName: node linkType: hard +"acorn@npm:^5.1.2": + version: 5.7.4 + resolution: "acorn@npm:5.7.4" + bin: + acorn: bin/acorn + checksum: f51392a4d25c7705fadb890f784c59cde4ac1c5452ccd569fa59bd2191b7951b4a6398348ab7ea08a54f0bc0a56c13776710f4e1bae9de441e4d33e2015ad1e0 + languageName: node + linkType: hard + +"acorn@npm:^6.0.1": + version: 6.4.2 + resolution: "acorn@npm:6.4.2" + bin: + acorn: bin/acorn + checksum: 44b07053729db7f44d28343eed32247ed56dc4a6ec6dff2b743141ecd6b861406bbc1c20bf9d4f143ea7dd08add5dc8c290582756539bc03a8db605050ce2fb4 + languageName: node + linkType: hard + "acorn@npm:^7.1.1": version: 7.4.1 resolution: "acorn@npm:7.4.1" @@ -6687,7 +9257,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.0.0, acorn@npm:^8.1.0, acorn@npm:^8.11.3, acorn@npm:^8.4.1, acorn@npm:^8.5.0, acorn@npm:^8.6.0, acorn@npm:^8.7.0, acorn@npm:^8.8.1, acorn@npm:^8.9.0": +"acorn@npm:^8.0.0, acorn@npm:^8.1.0, acorn@npm:^8.11.3, acorn@npm:^8.4.1, acorn@npm:^8.5.0, acorn@npm:^8.6.0, acorn@npm:^8.7.0, acorn@npm:^8.8.0, acorn@npm:^8.8.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": version: 8.11.3 resolution: "acorn@npm:8.11.3" bin: @@ -6757,6 +9327,18 @@ __metadata: languageName: node linkType: hard +"ajv@npm:^8.6.0": + version: 8.12.0 + resolution: "ajv@npm:8.12.0" + dependencies: + fast-deep-equal: ^3.1.1 + json-schema-traverse: ^1.0.0 + require-from-string: ^2.0.2 + uri-js: ^4.2.2 + checksum: 4dc13714e316e67537c8b31bc063f99a1d9d9a497eb4bbd55191ac0dcd5e4985bbb71570352ad6f1e76684fb6d790928f96ba3b2d4fd6e10024be9612fe3f001 + languageName: node + linkType: hard + "ansi-colors@npm:4.1.1": version: 4.1.1 resolution: "ansi-colors@npm:4.1.1" @@ -6988,6 +9570,13 @@ __metadata: languageName: node linkType: hard +"array-equal@npm:^1.0.0": + version: 1.0.2 + resolution: "array-equal@npm:1.0.2" + checksum: 5c37df0cad330516d1255663dfa4fa761fb0ea63878f535aa70dfefe5499853a8b372faf0a27b91781ca1230f4b4333bbeb751e9b1748527d96df2bee30032ea + languageName: node + linkType: hard + "array-flatten@npm:1.1.1": version: 1.1.1 resolution: "array-flatten@npm:1.1.1" @@ -7096,6 +9685,15 @@ __metadata: languageName: node linkType: hard +"as-table@npm:^1.0.36": + version: 1.0.55 + resolution: "as-table@npm:1.0.55" + dependencies: + printable-characters: ^1.0.42 + checksum: 341c99d9e99a702c315b3f0744d49b4764b26ef7ddd32bafb9e1706626560c0e599100521fc1b17f640e804bd0503ce70b2ba519c023da6edf06bdd9086dccd9 + languageName: node + linkType: hard + "asn1@npm:~0.2.3": version: 0.2.6 resolution: "asn1@npm:0.2.6" @@ -7200,6 +9798,13 @@ __metadata: languageName: node linkType: hard +"async@npm:^3.2.3": + version: 3.2.5 + resolution: "async@npm:3.2.5" + checksum: 5ec77f1312301dee02d62140a6b1f7ee0edd2a0f983b6fd2b0849b969f245225b990b47b8243e7b9ad16451a53e7f68e753700385b706198ced888beedba3af4 + languageName: node + linkType: hard + "asynciterator.prototype@npm:^1.0.0": version: 1.0.0 resolution: "asynciterator.prototype@npm:1.0.0" @@ -7466,7 +10071,7 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.3.1": +"base64-js@npm:^1.0.2, base64-js@npm:^1.3.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 @@ -7555,6 +10160,13 @@ __metadata: languageName: node linkType: hard +"blake3-wasm@npm:^2.1.5": + version: 2.1.5 + resolution: "blake3-wasm@npm:2.1.5" + checksum: 5088e929c722b52b9c28701c1760ab850a963692056a417b894c943030e3267f12138ae6409e79069b8d7d0401a411426147e8d812b65a49e303fa432af18871 + languageName: node + linkType: hard + "bluebird@npm:^2.3.5, bluebird@npm:^2.6.2, bluebird@npm:^2.8.1, bluebird@npm:^2.8.2": version: 2.11.0 resolution: "bluebird@npm:2.11.0" @@ -7596,6 +10208,13 @@ __metadata: languageName: node linkType: hard +"bowser@npm:^2.11.0": + version: 2.11.0 + resolution: "bowser@npm:2.11.0" + checksum: 29c3f01f22e703fa6644fc3b684307442df4240b6e10f6cfe1b61c6ca5721073189ca97cdeedb376081148c8518e33b1d818a57f781d70b0b70e1f31fb48814f + languageName: node + linkType: hard + "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -7657,6 +10276,13 @@ __metadata: languageName: node linkType: hard +"browser-fs-access@npm:^0.33.0": + version: 0.33.1 + resolution: "browser-fs-access@npm:0.33.1" + checksum: 2fb595c2d28e2da3a5772c0c4da01e8bd4ea2d812693a5ccc8675b59c7658ac82d3e16780e4b7746ca45350b0e873f09597256a524b9e9e80fe67c91bcddffa0 + languageName: node + linkType: hard + "browser-process-hrtime@npm:^1.0.0": version: 1.0.0 resolution: "browser-process-hrtime@npm:1.0.0" @@ -7731,6 +10357,16 @@ __metadata: languageName: node linkType: hard +"buffer@npm:5.6.0": + version: 5.6.0 + resolution: "buffer@npm:5.6.0" + dependencies: + base64-js: ^1.0.2 + ieee754: ^1.1.4 + checksum: d659494c5032dd39d03d2912e64179cc44c6340e7e9d1f68d3840e7ab4559989fbce92b4950174593c38d05268224235ba404f0878775cab2a616b6dcad9c23e + languageName: node + linkType: hard + "buffer@npm:^5.5.0": version: 5.7.1 resolution: "buffer@npm:5.7.1" @@ -7741,6 +10377,13 @@ __metadata: languageName: node linkType: hard +"builtin-modules@npm:^3.1.0": + version: 3.3.0 + resolution: "builtin-modules@npm:3.3.0" + checksum: db021755d7ed8be048f25668fe2117620861ef6703ea2c65ed2779c9e3636d5c3b82325bd912244293959ff3ae303afa3471f6a15bf5060c103e4cc3a839749d + languageName: node + linkType: hard + "busboy@npm:1.6.0": version: 1.6.0 resolution: "busboy@npm:1.6.0" @@ -7908,6 +10551,16 @@ __metadata: languageName: node linkType: hard +"capnp-ts@npm:^0.7.0": + version: 0.7.0 + resolution: "capnp-ts@npm:0.7.0" + dependencies: + debug: ^4.3.1 + tslib: ^2.2.0 + checksum: 9ab495a887c5d5fd56afa3cc930733cd8c6c0743c52c2e79c46675eb5c7753e5578f71348628a4b3d9f03e5269bc71f811e58af18d0b0557607c4ee56189cfbb + languageName: node + linkType: hard + "caseless@npm:~0.12.0": version: 0.12.0 resolution: "caseless@npm:0.12.0" @@ -7963,7 +10616,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": +"chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: @@ -8085,7 +10738,7 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:3.5.3, chokidar@npm:^3.5.1, chokidar@npm:^3.5.2": +"chokidar@npm:3.5.3, chokidar@npm:^3.5.1, chokidar@npm:^3.5.2, chokidar@npm:^3.5.3": version: 3.5.3 resolution: "chokidar@npm:3.5.3" dependencies: @@ -8215,6 +10868,17 @@ __metadata: languageName: node linkType: hard +"cliui@npm:^6.0.0": + version: 6.0.0 + resolution: "cliui@npm:6.0.0" + dependencies: + string-width: ^4.2.0 + strip-ansi: ^6.0.0 + wrap-ansi: ^6.2.0 + checksum: 4fcfd26d292c9f00238117f39fc797608292ae36bac2168cfee4c85923817d0607fe21b3329a8621e01aedf512c99b7eaa60e363a671ffd378df6649fb48ae42 + languageName: node + linkType: hard + "cliui@npm:^7.0.2": version: 7.0.4 resolution: "cliui@npm:7.0.4" @@ -8410,6 +11074,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^2.20.0": + version: 2.20.3 + resolution: "commander@npm:2.20.3" + checksum: ab8c07884e42c3a8dbc5dd9592c606176c7eb5c1ca5ff274bcf907039b2c41de3626f684ea75ccf4d361ba004bbaff1f577d5384c155f3871e456bdf27becf9e + languageName: node + linkType: hard + "commander@npm:^6.1.0": version: 6.2.1 resolution: "commander@npm:6.2.1" @@ -8431,6 +11102,13 @@ __metadata: languageName: node linkType: hard +"common-tags@npm:^1.8.0": + version: 1.8.2 + resolution: "common-tags@npm:1.8.2" + checksum: 767a6255a84bbc47df49a60ab583053bb29a7d9687066a18500a516188a062c4e4cd52de341f22de0b07062e699b1b8fe3cfa1cb55b241cb9301aeb4f45b4dff + languageName: node + linkType: hard + "concat-map@npm:0.0.1": version: 0.0.1 resolution: "concat-map@npm:0.0.1" @@ -8468,7 +11146,7 @@ __metadata: languageName: node linkType: hard -"concurrently@npm:^8.2.2": +"concurrently@npm:^8.2.1, concurrently@npm:^8.2.2": version: 8.2.2 resolution: "concurrently@npm:8.2.2" dependencies: @@ -8521,6 +11199,13 @@ __metadata: languageName: node linkType: hard +"content-type-parser@npm:^1.0.1": + version: 1.0.2 + resolution: "content-type-parser@npm:1.0.2" + checksum: a9afe2c02059b2b44257f073856c5a44b178fcb67b8a4d1b03046711ff7bb13bfa0c7629e70905c226faf8c0b0e13ade824717a4c974c7c6cdb0ae13774752df + languageName: node + linkType: hard + "content-type@npm:~1.0.4": version: 1.0.5 resolution: "content-type@npm:1.0.5" @@ -8556,7 +11241,7 @@ __metadata: languageName: node linkType: hard -"cookie@npm:0.5.0": +"cookie@npm:0.5.0, cookie@npm:^0.5.0": version: 0.5.0 resolution: "cookie@npm:0.5.0" checksum: 1f4bd2ca5765f8c9689a7e8954183f5332139eb72b6ff783d8947032ec1fdf43109852c178e21a953a30c0dd42257828185be01b49d1eb1a67fd054ca588a180 @@ -8600,6 +11285,16 @@ __metadata: languageName: node linkType: hard +"cors@npm:^2.8.5": + version: 2.8.5 + resolution: "cors@npm:2.8.5" + dependencies: + object-assign: ^4 + vary: ^1 + checksum: ced838404ccd184f61ab4fdc5847035b681c90db7ac17e428f3d81d69e2989d2b680cc254da0e2554f5ed4f8a341820a1ce3d1c16b499f6e2f47a1b9b07b5006 + languageName: node + linkType: hard + "cosmiconfig@npm:7.0.0": version: 7.0.0 resolution: "cosmiconfig@npm:7.0.0" @@ -8629,6 +11324,18 @@ __metadata: languageName: node linkType: hard +"cross-env@npm:^7.0.3": + version: 7.0.3 + resolution: "cross-env@npm:7.0.3" + dependencies: + cross-spawn: ^7.0.1 + bin: + cross-env: src/bin/cross-env.js + cross-env-shell: src/bin/cross-env-shell.js + checksum: 26f2f3ea2ab32617f57effb70d329c2070d2f5630adc800985d8b30b56e8bf7f5f439dd3a0358b79cee6f930afc23cf8e23515f17ccfb30092c6b62c6b630a79 + languageName: node + linkType: hard + "cross-fetch@npm:^3.1.5": version: 3.1.8 resolution: "cross-fetch@npm:3.1.8" @@ -8638,7 +11345,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" dependencies: @@ -8656,6 +11363,13 @@ __metadata: languageName: node linkType: hard +"crypto-random-string@npm:^2.0.0": + version: 2.0.0 + resolution: "crypto-random-string@npm:2.0.0" + checksum: 0283879f55e7c16fdceacc181f87a0a65c53bc16ffe1d58b9d19a6277adcd71900d02bb2c4843dd55e78c51e30e89b0fec618a7f170ebcc95b33182c28f05fd6 + languageName: node + linkType: hard + "css-select@npm:^5.1.0": version: 5.1.0 resolution: "css-select@npm:5.1.0" @@ -8728,6 +11442,13 @@ __metadata: languageName: node linkType: hard +"cssom@npm:0.3.x, cssom@npm:>= 0.3.2 < 0.4.0, cssom@npm:~0.3.6": + version: 0.3.8 + resolution: "cssom@npm:0.3.8" + checksum: 24beb3087c76c0d52dd458be9ee1fbc80ac771478a9baef35dd258cdeb527c68eb43204dd439692bb2b1ae5272fa5f2946d10946edab0d04f1078f85e06bc7f6 + languageName: node + linkType: hard + "cssom@npm:^0.5.0": version: 0.5.0 resolution: "cssom@npm:0.5.0" @@ -8735,10 +11456,12 @@ __metadata: languageName: node linkType: hard -"cssom@npm:~0.3.6": - version: 0.3.8 - resolution: "cssom@npm:0.3.8" - checksum: 24beb3087c76c0d52dd458be9ee1fbc80ac771478a9baef35dd258cdeb527c68eb43204dd439692bb2b1ae5272fa5f2946d10946edab0d04f1078f85e06bc7f6 +"cssstyle@npm:>= 0.2.37 < 0.3.0": + version: 0.2.37 + resolution: "cssstyle@npm:0.2.37" + dependencies: + cssom: 0.3.x + checksum: cc36921c7dbfc59b12ca3ab2dfc09cb71d437e721487b670fe1b513d4ddee97719ae4d76cf5c32ef7d6cf0188159a6657328e233fda668f4c52f61bb33c75f29 languageName: node linkType: hard @@ -8781,6 +11504,13 @@ __metadata: languageName: node linkType: hard +"data-uri-to-buffer@npm:^2.0.0": + version: 2.0.2 + resolution: "data-uri-to-buffer@npm:2.0.2" + checksum: 152bec5e77513ee253a7c686700a1723246f582dad8b614e8eaaaba7fa45a15c8671ae4b8f4843f4f3a002dae1d3e7a20f852f7d7bdc8b4c15cfe7adfdfb07f8 + languageName: node + linkType: hard + "data-urls@npm:^3.0.1, data-urls@npm:^3.0.2": version: 3.0.2 resolution: "data-urls@npm:3.0.2" @@ -9143,6 +11873,13 @@ __metadata: languageName: node linkType: hard +"dijkstrajs@npm:^1.0.1": + version: 1.0.3 + resolution: "dijkstrajs@npm:1.0.3" + checksum: 82ff2c6633f235dd5e6bed04ec62cdfb1f327b4d7534557bd52f18991313f864ee50654543072fff4384a92b643ada4d5452f006b7098dbdfad6c8744a8c9e08 + languageName: node + linkType: hard + "dir-glob@npm:^2.0.0": version: 2.2.2 resolution: "dir-glob@npm:2.2.2" @@ -9204,7 +11941,7 @@ __metadata: languageName: node linkType: hard -"domexception@npm:^1.0.1": +"domexception@npm:^1.0.0, domexception@npm:^1.0.1": version: 1.0.1 resolution: "domexception@npm:1.0.1" dependencies: @@ -9256,6 +11993,52 @@ __metadata: languageName: node linkType: hard +"dotcom-asset-upload@workspace:apps/dotcom-asset-upload": + version: 0.0.0-use.local + resolution: "dotcom-asset-upload@workspace:apps/dotcom-asset-upload" + dependencies: + "@cloudflare/workers-types": ^4.20230821.0 + "@types/ws": ^8.5.3 + itty-cors: ^0.3.4 + itty-router: ^2.6.6 + lazyrepo: 0.0.0-alpha.27 + wrangler: 3.16.0 + languageName: unknown + linkType: soft + +"dotcom@workspace:apps/dotcom": + version: 0.0.0-use.local + resolution: "dotcom@workspace:apps/dotcom" + dependencies: + "@radix-ui/react-popover": 1.0.6-rc.5 + "@sentry/cli": ^2.25.0 + "@sentry/integrations": ^7.34.0 + "@sentry/react": ^7.77.0 + "@tldraw/assets": "workspace:*" + "@tldraw/tldraw": "workspace:*" + "@tldraw/tlsync": "workspace:*" + "@types/qrcode": ^1.5.0 + "@types/react": ^18.2.33 + "@typescript-eslint/utils": ^5.59.0 + "@vercel/analytics": ^1.0.1 + "@vitejs/plugin-react-swc": ^3.5.0 + browser-fs-access: ^0.33.0 + dotenv: ^16.3.1 + fast-glob: ^3.3.1 + idb: ^7.1.1 + lazyrepo: 0.0.0-alpha.27 + nanoid: 4.0.2 + qrcode: ^1.5.1 + react: ^18.2.0 + react-dom: ^18.2.0 + react-helmet-async: ^1.3.0 + react-router-dom: ^6.17.0 + vite: ^5.0.0 + vite-plugin-pwa: ^0.17.0 + ws: ^8.13.0 + languageName: unknown + linkType: soft + "dotenv@npm:^16.0.0, dotenv@npm:^16.0.3, dotenv@npm:^16.3.1": version: 16.3.1 resolution: "dotenv@npm:16.3.1" @@ -9350,6 +12133,17 @@ __metadata: languageName: node linkType: hard +"ejs@npm:^3.1.6": + version: 3.1.9 + resolution: "ejs@npm:3.1.9" + dependencies: + jake: ^10.8.5 + bin: + ejs: bin/cli.js + checksum: af6f10eb815885ff8a8cfacc42c6b6cf87daf97a4884f87a30e0c3271fedd85d76a3a297d9c33a70e735b97ee632887f85e32854b9cdd3a2d97edf931519a35f + languageName: node + linkType: hard + "electron-to-chromium@npm:^1.4.601": version: 1.4.630 resolution: "electron-to-chromium@npm:1.4.630" @@ -9399,6 +12193,13 @@ __metadata: languageName: node linkType: hard +"encode-utf8@npm:^1.0.3": + version: 1.0.3 + resolution: "encode-utf8@npm:1.0.3" + checksum: 550224bf2a104b1d355458c8a82e9b4ea07f9fc78387bc3a49c151b940ad26473de8dc9e121eefc4e84561cb0b46de1e4cd2bc766f72ee145e9ea9541482817f + languageName: node + linkType: hard + "encodeurl@npm:~1.0.2": version: 1.0.2 resolution: "encodeurl@npm:1.0.2" @@ -9521,6 +12322,15 @@ __metadata: languageName: node linkType: hard +"error-stack-parser@npm:^2.0.6": + version: 2.1.4 + resolution: "error-stack-parser@npm:2.1.4" + dependencies: + stackframe: ^1.3.4 + checksum: 3b916d2d14c6682f287c8bfa28e14672f47eafe832701080e420e7cdbaebb2c50293868256a95706ac2330fe078cf5664713158b49bc30d7a5f2ac229ded0e18 + languageName: node + linkType: hard + "errors@npm:^0.2.0": version: 0.2.0 resolution: "errors@npm:0.2.0" @@ -9933,6 +12743,83 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:0.17.19, esbuild@npm:^0.17.15": + version: 0.17.19 + resolution: "esbuild@npm:0.17.19" + dependencies: + "@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 + dependenciesMeta: + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: ac11b1a5a6008e4e37ccffbd6c2c054746fc58d0ed4a2f9ee643bd030cfcea9a33a235087bc777def8420f2eaafb3486e76adb7bdb7241a9143b43a69a10afd8 + languageName: node + linkType: hard + "esbuild@npm:0.17.6": version: 0.17.6 resolution: "esbuild@npm:0.17.6" @@ -10010,83 +12897,6 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.17.15": - version: 0.17.19 - resolution: "esbuild@npm:0.17.19" - dependencies: - "@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 - dependenciesMeta: - "@esbuild/android-arm": - optional: true - "@esbuild/android-arm64": - optional: true - "@esbuild/android-x64": - optional: true - "@esbuild/darwin-arm64": - optional: true - "@esbuild/darwin-x64": - optional: true - "@esbuild/freebsd-arm64": - optional: true - "@esbuild/freebsd-x64": - optional: true - "@esbuild/linux-arm": - optional: true - "@esbuild/linux-arm64": - optional: true - "@esbuild/linux-ia32": - optional: true - "@esbuild/linux-loong64": - optional: true - "@esbuild/linux-mips64el": - optional: true - "@esbuild/linux-ppc64": - optional: true - "@esbuild/linux-riscv64": - optional: true - "@esbuild/linux-s390x": - optional: true - "@esbuild/linux-x64": - optional: true - "@esbuild/netbsd-x64": - optional: true - "@esbuild/openbsd-x64": - optional: true - "@esbuild/sunos-x64": - optional: true - "@esbuild/win32-arm64": - optional: true - "@esbuild/win32-ia32": - optional: true - "@esbuild/win32-x64": - optional: true - bin: - esbuild: bin/esbuild - checksum: ac11b1a5a6008e4e37ccffbd6c2c054746fc58d0ed4a2f9ee643bd030cfcea9a33a235087bc777def8420f2eaafb3486e76adb7bdb7241a9143b43a69a10afd8 - languageName: node - linkType: hard - "esbuild@npm:^0.18.10, esbuild@npm:^0.18.4, esbuild@npm:~0.18.20": version: 0.18.20 resolution: "esbuild@npm:0.18.20" @@ -10164,7 +12974,7 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:~0.19.10": +"esbuild@npm:^0.19.3, esbuild@npm:~0.19.10": version: 0.19.11 resolution: "esbuild@npm:0.19.11" dependencies: @@ -10286,7 +13096,7 @@ __metadata: languageName: node linkType: hard -"escodegen@npm:^1.8.1": +"escodegen@npm:^1.8.1, escodegen@npm:^1.9.0": version: 1.14.3 resolution: "escodegen@npm:1.14.3" dependencies: @@ -10323,6 +13133,29 @@ __metadata: languageName: node linkType: hard +"eslint-config-next@npm:12.2.5": + version: 12.2.5 + resolution: "eslint-config-next@npm:12.2.5" + dependencies: + "@next/eslint-plugin-next": 12.2.5 + "@rushstack/eslint-patch": ^1.1.3 + "@typescript-eslint/parser": ^5.21.0 + eslint-import-resolver-node: ^0.3.6 + eslint-import-resolver-typescript: ^2.7.1 + eslint-plugin-import: ^2.26.0 + eslint-plugin-jsx-a11y: ^6.5.1 + eslint-plugin-react: ^7.29.4 + eslint-plugin-react-hooks: ^4.5.0 + peerDependencies: + eslint: ^7.23.0 || ^8.0.0 + typescript: ">=3.3.1" + peerDependenciesMeta: + typescript: + optional: true + checksum: 21f14cda6c28670e09267c9fc28fe17f9480487c85a322928507bbf61f072c62401bb7d8e79992b176d6a0d64a86419cbd69e475098b083aa5089bb6b572b8e0 + languageName: node + linkType: hard + "eslint-config-next@npm:13.2.4": version: 13.2.4 resolution: "eslint-config-next@npm:13.2.4" @@ -10368,6 +13201,22 @@ __metadata: languageName: node linkType: hard +"eslint-import-resolver-typescript@npm:^2.7.1": + version: 2.7.1 + resolution: "eslint-import-resolver-typescript@npm:2.7.1" + dependencies: + debug: ^4.3.4 + glob: ^7.2.0 + is-glob: ^4.0.3 + resolve: ^1.22.0 + tsconfig-paths: ^3.14.1 + peerDependencies: + eslint: "*" + eslint-plugin-import: "*" + checksum: 1d81b657b1f73bf95b8f0b745c0305574b91630c1db340318f3ca8918e206fce20a933b95e7c419338cc4452cb80bb2b2d92acaf01b6aa315c78a332d832545c + languageName: node + linkType: hard + "eslint-import-resolver-typescript@npm:^3.5.2": version: 3.6.1 resolution: "eslint-import-resolver-typescript@npm:3.6.1" @@ -10512,7 +13361,7 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-react@npm:^7.31.7, eslint-plugin-react@npm:^7.32.2": +"eslint-plugin-react@npm:^7.29.4, eslint-plugin-react@npm:^7.31.7, eslint-plugin-react@npm:^7.32.2": version: 7.33.2 resolution: "eslint-plugin-react@npm:7.33.2" dependencies: @@ -10794,6 +13643,13 @@ __metadata: languageName: node linkType: hard +"estree-walker@npm:^1.0.1": + version: 1.0.1 + resolution: "estree-walker@npm:1.0.1" + checksum: 7e70da539691f6db03a08e7ce94f394ce2eef4180e136d251af299d41f92fb2d28ebcd9a6e393e3728d7970aeb5358705ddf7209d52fbcb2dd4693f95dcf925f + languageName: node + linkType: hard + "estree-walker@npm:^3.0.0": version: 3.0.3 resolution: "estree-walker@npm:3.0.3" @@ -10848,6 +13704,13 @@ __metadata: languageName: node linkType: hard +"events@npm:3.3.0": + version: 3.3.0 + resolution: "events@npm:3.3.0" + checksum: f6f487ad2198aa41d878fa31452f1a3c00958f46e9019286ff4787c84aac329332ab45c9cdc8c445928fc6d7ded294b9e005a7fce9426488518017831b272780 + languageName: node + linkType: hard + "examples.tldraw.com@workspace:apps/examples": version: 0.0.0-use.local resolution: "examples.tldraw.com@workspace:apps/examples" @@ -10906,7 +13769,7 @@ __metadata: languageName: node linkType: hard -"exit-hook@npm:2.2.1": +"exit-hook@npm:2.2.1, exit-hook@npm:^2.2.1": version: 2.2.1 resolution: "exit-hook@npm:2.2.1" checksum: 1aa8359b6c5590a012d6cadf9cd337d227291bfcaa8970dc585d73dffef0582af34ed8ac56f6164f8979979fb417cff1eb49f03cdfd782f9332a30c773f0ada0 @@ -11076,7 +13939,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.0.3, fast-glob@npm:^3.1.1, fast-glob@npm:^3.2.7, fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.1": +"fast-glob@npm:^3.0.3, fast-glob@npm:^3.1.1, fast-glob@npm:^3.2.7, fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.1, fast-glob@npm:^3.3.2": version: 3.3.2 resolution: "fast-glob@npm:3.3.2" dependencies: @@ -11110,6 +13973,17 @@ __metadata: languageName: node linkType: hard +"fast-xml-parser@npm:4.2.5": + version: 4.2.5 + resolution: "fast-xml-parser@npm:4.2.5" + dependencies: + strnum: ^1.0.5 + bin: + fxparser: src/cli/cli.js + checksum: d32b22005504eeb207249bf40dc82d0994b5bb9ca9dcc731d335a1f425e47fe085b3cace3cf9d32172dd1a5544193c49e8615ca95b4bf95a4a4920a226b06d80 + languageName: node + linkType: hard + "fastq@npm:^1.6.0": version: 1.16.0 resolution: "fastq@npm:1.16.0" @@ -11187,6 +14061,15 @@ __metadata: languageName: node linkType: hard +"filelist@npm:^1.0.4": + version: 1.0.4 + resolution: "filelist@npm:1.0.4" + dependencies: + minimatch: ^5.0.1 + checksum: a303573b0821e17f2d5e9783688ab6fbfce5d52aaac842790ae85e704a6f5e4e3538660a63183d6453834dedf1e0f19a9dadcebfa3e926c72397694ea11f5160 + languageName: node + linkType: hard + "fill-range@npm:^7.0.1": version: 7.0.1 resolution: "fill-range@npm:7.0.1" @@ -11475,7 +14358,7 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:^9.0.0": +"fs-extra@npm:^9.0.0, fs-extra@npm:^9.0.1": version: 9.1.0 resolution: "fs-extra@npm:9.1.0" dependencies: @@ -11689,6 +14572,13 @@ __metadata: languageName: node linkType: hard +"get-own-enumerable-property-symbols@npm:^3.0.0": + version: 3.0.2 + resolution: "get-own-enumerable-property-symbols@npm:3.0.2" + checksum: 8f0331f14159f939830884799f937343c8c0a2c330506094bc12cbee3665d88337fe97a4ea35c002cc2bdba0f5d9975ad7ec3abb925015cdf2a93e76d4759ede + languageName: node + linkType: hard + "get-package-type@npm:^0.1.0": version: 0.1.0 resolution: "get-package-type@npm:0.1.0" @@ -11703,6 +14593,16 @@ __metadata: languageName: node linkType: hard +"get-source@npm:^2.0.12": + version: 2.0.12 + resolution: "get-source@npm:2.0.12" + dependencies: + data-uri-to-buffer: ^2.0.0 + source-map: ^0.6.1 + checksum: c73368fee709594ba38682ec1a96872aac6f7d766396019611d3d2358b68186a7847765a773ea0db088c42781126cc6bc09e4b87f263951c74dae5dcea50ad42 + languageName: node + linkType: hard + "get-stream@npm:^5.1.0": version: 5.2.0 resolution: "get-stream@npm:5.2.0" @@ -11874,7 +14774,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.0.6, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4": +"glob@npm:^7.0.6, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.2.0": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -12017,6 +14917,15 @@ __metadata: languageName: node linkType: hard +"grabity@npm:^1.0.5": + version: 1.0.5 + resolution: "grabity@npm:1.0.5" + dependencies: + jsdom: 11.3.0 + checksum: 94d56728b6b12db1640f9131360701f04a0ddb78081532f2b043f07eb5e6a10bc3bb250d5296d54f7ef66a975413fccfb34a56c36cde591bb4e2a1eed0a2342b + languageName: node + linkType: hard + "graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" @@ -12394,6 +15303,15 @@ __metadata: languageName: node linkType: hard +"hoist-non-react-statics@npm:^3.3.2": + version: 3.3.2 + resolution: "hoist-non-react-statics@npm:3.3.2" + dependencies: + react-is: ^16.7.0 + checksum: b1538270429b13901ee586aa44f4cc3ecd8831c061d06cb8322e50ea17b3f5ce4d0e2e66394761e6c8e152cd8c34fb3b4b690116c6ce2bd45b18c746516cb9e8 + languageName: node + linkType: hard + "hosted-git-info@npm:^4.0.2": version: 4.1.0 resolution: "hosted-git-info@npm:4.1.0" @@ -12410,6 +15328,15 @@ __metadata: languageName: node linkType: hard +"html-encoding-sniffer@npm:^1.0.1": + version: 1.0.2 + resolution: "html-encoding-sniffer@npm:1.0.2" + dependencies: + whatwg-encoding: ^1.0.1 + checksum: b874df6750451b7642fbe8e998c6bdd2911b0f42ad2927814b717bf1f4b082b0904b6178a1bfbc40117bf5799777993b0825e7713ca0fca49844e5aec03aa0e2 + languageName: node + linkType: hard + "html-encoding-sniffer@npm:^3.0.0": version: 3.0.0 resolution: "html-encoding-sniffer@npm:3.0.0" @@ -12575,6 +15502,27 @@ __metadata: languageName: node linkType: hard +"huppy@workspace:apps/huppy": + version: 0.0.0-use.local + resolution: "huppy@workspace:apps/huppy" + dependencies: + "@octokit/core": ^5.0.1 + "@octokit/plugin-retry": ^6.0.1 + "@octokit/webhooks-types": ^6.11.0 + "@tldraw/utils": "workspace:*" + "@tldraw/validate": "workspace:*" + "@types/jsonwebtoken": ^9.0.1 + eslint-config-next: 12.2.5 + json5: ^2.2.3 + jsonwebtoken: ^9.0.0 + lazyrepo: 0.0.0-alpha.27 + next: ^13.2.3 + octokit: ^3.1.1 + react: ^18.2.0 + react-dom: ^18.2.0 + languageName: unknown + linkType: soft + "husky@npm:^8.0.0": version: 8.0.3 resolution: "husky@npm:8.0.3" @@ -12611,14 +15559,14 @@ __metadata: languageName: node linkType: hard -"idb@npm:^7.1.1": +"idb@npm:^7.0.1, idb@npm:^7.1.1": version: 7.1.1 resolution: "idb@npm:7.1.1" checksum: 1973c28d53c784b177bdef9f527ec89ec239ec7cf5fcbd987dae75a16c03f5b7dfcc8c6d3285716fd0309dd57739805390bd9f98ce23b1b7d8849a3b52de8d56 languageName: node linkType: hard -"ieee754@npm:^1.1.13": +"ieee754@npm:^1.1.13, ieee754@npm:^1.1.4": version: 1.2.1 resolution: "ieee754@npm:1.2.1" checksum: 5144c0c9815e54ada181d80a0b810221a253562422e7c6c3a60b1901154184f49326ec239d618c416c1c5945a2e197107aee8d986a3dd836b53dffefd99b5e7e @@ -12639,6 +15587,13 @@ __metadata: languageName: node linkType: hard +"immediate@npm:~3.0.5": + version: 3.0.6 + resolution: "immediate@npm:3.0.6" + checksum: f9b3486477555997657f70318cc8d3416159f208bec4cca3ff3442fd266bc23f50f0c9bd8547e1371a6b5e82b821ec9a7044a4f7b944798b25aa3cc6d5e63e62 + languageName: node + linkType: hard + "import-cwd@npm:^3.0.0": version: 3.0.0 resolution: "import-cwd@npm:3.0.0" @@ -12717,7 +15672,7 @@ __metadata: languageName: node linkType: hard -"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.1, inherits@npm:~2.0.3": +"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.1, inherits@npm:~2.0.3, inherits@npm:~2.0.4": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 @@ -13086,6 +16041,13 @@ __metadata: languageName: node linkType: hard +"is-module@npm:^1.0.0": + version: 1.0.0 + resolution: "is-module@npm:1.0.0" + checksum: 8cd5390730c7976fb4e8546dd0b38865ee6f7bacfa08dfbb2cc07219606755f0b01709d9361e01f13009bbbd8099fa2927a8ed665118a6105d66e40f1b838c3f + languageName: node + linkType: hard + "is-nan@npm:^1.3.2": version: 1.3.2 resolution: "is-nan@npm:1.3.2" @@ -13119,6 +16081,13 @@ __metadata: languageName: node linkType: hard +"is-obj@npm:^1.0.1": + version: 1.0.1 + resolution: "is-obj@npm:1.0.1" + checksum: 3ccf0efdea12951e0b9c784e2b00e77e87b2f8bd30b42a498548a8afcc11b3287342a2030c308e473e93a7a19c9ea7854c99a8832a476591c727df2a9c79796c + languageName: node + linkType: hard + "is-object@npm:^1.0.1": version: 1.0.2 resolution: "is-object@npm:1.0.2" @@ -13187,6 +16156,13 @@ __metadata: languageName: node linkType: hard +"is-regexp@npm:^1.0.0": + version: 1.0.0 + resolution: "is-regexp@npm:1.0.0" + checksum: be692828e24cba479ec33644326fa98959ec68ba77965e0291088c1a741feaea4919d79f8031708f85fd25e39de002b4520622b55460660b9c369e6f7187faef + languageName: node + linkType: hard + "is-set@npm:^2.0.1, is-set@npm:^2.0.2": version: 2.0.2 resolution: "is-set@npm:2.0.2" @@ -13414,6 +16390,27 @@ __metadata: languageName: node linkType: hard +"itty-cors@npm:^0.3.4": + version: 0.3.6 + resolution: "itty-cors@npm:0.3.6" + checksum: 8979093d2790dc9abbcfe858fea4b41f6075545a8ba2c39798e0469be880a52ce6c5cedb102c77d12f4148f0249828113d79cc03d88754c2739c31d0b4da2d0c + languageName: node + linkType: hard + +"itty-router@npm:^2.6.6": + version: 2.6.6 + resolution: "itty-router@npm:2.6.6" + checksum: 2117cba792e7a131f12af8e4f18ed7fddf643d632a440f235e4a3478eac4e4158272a859d160969e0d76e96f5a8659bd090f714afabdb95cffb7964c29d4a89f + languageName: node + linkType: hard + +"itty-router@npm:^4.0.13": + version: 4.0.27 + resolution: "itty-router@npm:4.0.27" + checksum: 323e159cca2a321c53ddd1ebc61572e777fe88665d9ab2eb090ae0a78e2e3885f9bab4008c646859cdbd049ddbf886f7c218fc24ea13c7caeb119833226945b6 + languageName: node + linkType: hard + "jackspeak@npm:^2.3.5": version: 2.3.6 resolution: "jackspeak@npm:2.3.6" @@ -13427,6 +16424,20 @@ __metadata: languageName: node linkType: hard +"jake@npm:^10.8.5": + version: 10.8.7 + resolution: "jake@npm:10.8.7" + dependencies: + async: ^3.2.3 + chalk: ^4.0.2 + filelist: ^1.0.4 + minimatch: ^3.1.2 + bin: + jake: bin/cli.js + checksum: a23fd2273fb13f0d0d845502d02c791fd55ef5c6a2d207df72f72d8e1eac6d2b8ffa6caf660bc8006b3242e0daaa88a3ecc600194d72b5c6016ad56e9cd43553 + languageName: node + linkType: hard + "java-properties@npm:^1.0.0": version: 1.0.2 resolution: "java-properties@npm:1.0.2" @@ -13970,6 +16981,17 @@ __metadata: languageName: node linkType: hard +"jest-worker@npm:^26.2.1": + version: 26.6.2 + resolution: "jest-worker@npm:26.6.2" + dependencies: + "@types/node": "*" + merge-stream: ^2.0.0 + supports-color: ^7.0.0 + checksum: f9afa3b88e3f12027901e4964ba3ff048285b5783b5225cab28fac25b4058cea8ad54001e9a1577ee2bed125fac3ccf5c80dc507b120300cc1bbcb368796533e + languageName: node + linkType: hard + "jest-worker@npm:^28.1.3": version: 28.1.3 resolution: "jest-worker@npm:28.1.3" @@ -14007,6 +17029,20 @@ __metadata: languageName: node linkType: hard +"jose@npm:^4.14.4": + version: 4.15.4 + resolution: "jose@npm:4.15.4" + checksum: dccad91cb3357f36423774a0b89ad830dd84b31090de65cd139b85488439f16a00f8c59c0773825e8a1adb0dd9d13ad725ad66e6ea33880ecb3959bb99e1ea5b + languageName: node + linkType: hard + +"js-md5@npm:^0.7.3": + version: 0.7.3 + resolution: "js-md5@npm:0.7.3" + checksum: 1ed9f7f23a2ad224fc159ba7e074617d20e6b501ea96319091ae4c7cfe722cc472c4e05366fd25708311fb66dc2deb1b82e51c730f9ccd910e64b67ba36d3a75 + languageName: node + linkType: hard + "js-sdsl@npm:^4.1.4": version: 4.4.2 resolution: "js-sdsl@npm:4.4.2" @@ -14014,6 +17050,13 @@ __metadata: languageName: node linkType: hard +"js-sha1@npm:^0.6.0": + version: 0.6.0 + resolution: "js-sha1@npm:0.6.0" + checksum: f12adcdac8da2b42eb40b9f83003bd085f647a8ca4364ac736462714e1b8681aab97efda09737889e001a7df569c8b35f28f35c638a3af93ef586281f92b2ca5 + languageName: node + linkType: hard + "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -14051,6 +17094,36 @@ __metadata: languageName: node linkType: hard +"jsdom@npm:11.3.0": + version: 11.3.0 + resolution: "jsdom@npm:11.3.0" + dependencies: + abab: ^1.0.3 + acorn: ^5.1.2 + acorn-globals: ^4.0.0 + array-equal: ^1.0.0 + content-type-parser: ^1.0.1 + cssom: ">= 0.3.2 < 0.4.0" + cssstyle: ">= 0.2.37 < 0.3.0" + domexception: ^1.0.0 + escodegen: ^1.9.0 + html-encoding-sniffer: ^1.0.1 + nwmatcher: ^1.4.1 + parse5: ^3.0.2 + pn: ^1.0.0 + request: ^2.83.0 + request-promise-native: ^1.0.3 + sax: ^1.2.1 + symbol-tree: ^3.2.1 + tough-cookie: ^2.3.3 + webidl-conversions: ^4.0.2 + whatwg-encoding: ^1.0.1 + whatwg-url: ^6.3.0 + xml-name-validator: ^2.0.1 + checksum: 97bc35095d2794ea91980c4dc5c004a851149355333303f4fcfcbc9ae1f618f407dc631be1539ce517cbcb6b4c92af0b39224d078ae4505971d726f043697741 + languageName: node + linkType: hard + "jsdom@npm:^19.0.0": version: 19.0.0 resolution: "jsdom@npm:19.0.0" @@ -14212,7 +17285,7 @@ __metadata: languageName: node linkType: hard -"json-schema@npm:0.4.0": +"json-schema@npm:0.4.0, json-schema@npm:^0.4.0": version: 0.4.0 resolution: "json-schema@npm:0.4.0" checksum: 66389434c3469e698da0df2e7ac5a3281bcff75e797a5c127db7c5b56270e01ae13d9afa3c03344f76e32e81678337a8c912bdbb75101c62e487dc3778461d72 @@ -14256,7 +17329,7 @@ __metadata: languageName: node linkType: hard -"json5@npm:^2.1.2, json5@npm:^2.2.2, json5@npm:^2.2.3": +"json5@npm:^2.1.2, json5@npm:^2.2.0, json5@npm:^2.2.2, json5@npm:^2.2.3": version: 2.2.3 resolution: "json5@npm:2.2.3" bin: @@ -14304,7 +17377,14 @@ __metadata: languageName: node linkType: hard -"jsonwebtoken@npm:^9.0.2": +"jsonpointer@npm:^5.0.0": + version: 5.0.1 + resolution: "jsonpointer@npm:5.0.1" + checksum: 0b40f712900ad0c846681ea2db23b6684b9d5eedf55807b4708c656f5894b63507d0e28ae10aa1bddbea551241035afe62b6df0800fc94c2e2806a7f3adecd7c + languageName: node + linkType: hard + +"jsonwebtoken@npm:^9.0.0, jsonwebtoken@npm:^9.0.2": version: 9.0.2 resolution: "jsonwebtoken@npm:9.0.2" dependencies: @@ -14486,6 +17566,15 @@ __metadata: languageName: node linkType: hard +"lie@npm:3.1.1": + version: 3.1.1 + resolution: "lie@npm:3.1.1" + dependencies: + immediate: ~3.0.5 + checksum: 6da9f2121d2dbd15f1eca44c0c7e211e66a99c7b326ec8312645f3648935bc3a658cf0e9fa7b5f10144d9e2641500b4f55bd32754607c3de945b5f443e50ddd1 + languageName: node + linkType: hard + "lilconfig@npm:3.0.0, lilconfig@npm:^3.0.0": version: 3.0.0 resolution: "lilconfig@npm:3.0.0" @@ -14590,6 +17679,15 @@ __metadata: languageName: node linkType: hard +"localforage@npm:^1.8.1": + version: 1.10.0 + resolution: "localforage@npm:1.10.0" + dependencies: + lie: 3.1.1 + checksum: f2978b434dafff9bcb0d9498de57d97eba165402419939c944412e179cab1854782830b5ec196212560b22712d1dd03918939f59cf1d4fc1d756fca7950086cf + languageName: node + linkType: hard + "locate-path@npm:^2.0.0": version: 2.0.0 resolution: "locate-path@npm:2.0.0" @@ -14726,6 +17824,13 @@ __metadata: languageName: node linkType: hard +"lodash.sortby@npm:^4.7.0": + version: 4.7.0 + resolution: "lodash.sortby@npm:4.7.0" + checksum: db170c9396d29d11fe9a9f25668c4993e0c1331bcb941ddbd48fb76f492e732add7f2a47cfdf8e9d740fa59ac41bbfaf931d268bc72aab3ab49e9f89354d718c + languageName: node + linkType: hard + "lodash.throttle@npm:^4.1.1": version: 4.1.1 resolution: "lodash.throttle@npm:4.1.1" @@ -14740,7 +17845,7 @@ __metadata: languageName: node linkType: hard -"lodash@npm:^4.17.15, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:^4.7.0, lodash@npm:~4.17.15": +"lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:^4.7.0, lodash@npm:~4.17.15": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 @@ -14857,7 +17962,7 @@ __metadata: languageName: node linkType: hard -"magic-string@npm:^0.25.3": +"magic-string@npm:^0.25.0, magic-string@npm:^0.25.3, magic-string@npm:^0.25.7": version: 0.25.9 resolution: "magic-string@npm:0.25.9" dependencies: @@ -15976,6 +19081,15 @@ __metadata: languageName: node linkType: hard +"mime@npm:^3.0.0": + version: 3.0.0 + resolution: "mime@npm:3.0.0" + bin: + mime: cli.js + checksum: f43f9b7bfa64534e6b05bd6062961681aeb406a5b53673b53b683f27fcc4e739989941836a355eef831f4478923651ecc739f4a5f6e20a76487b432bfd4db928 + languageName: node + linkType: hard + "mimic-fn@npm:^2.1.0": version: 2.1.0 resolution: "mimic-fn@npm:2.1.0" @@ -16011,6 +19125,28 @@ __metadata: languageName: node linkType: hard +"miniflare@npm:3.20231030.0": + version: 3.20231030.0 + resolution: "miniflare@npm:3.20231030.0" + dependencies: + 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 + source-map-support: 0.5.21 + stoppable: ^1.1.0 + undici: ^5.22.1 + workerd: 1.20231030.0 + ws: ^8.11.0 + youch: ^3.2.2 + zod: ^3.20.6 + bin: + miniflare: bootstrap.js + checksum: 506a143f86571cc5f870985ca76afa5b7f5b479c801a4ad04d562da47b1dc5edb556b7ae93e490c5d7a7b45ffee5e5dba28be699fb9ad497c394a6da779bb6c4 + languageName: node + linkType: hard + "minimatch@npm:4.2.1": version: 4.2.1 resolution: "minimatch@npm:4.2.1" @@ -16147,7 +19283,7 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^4.2.4": +"minipass@npm:^4.0.0, minipass@npm:^4.2.4": version: 4.2.8 resolution: "minipass@npm:4.2.8" checksum: 7f4914d5295a9a30807cae5227a37a926e6d910c03f315930fde52332cf0575dfbc20295318f91f0baf0e6bb11a6f668e30cde8027dea7a11b9d159867a3c830 @@ -16292,6 +19428,15 @@ __metadata: languageName: node linkType: hard +"mustache@npm:^4.2.0": + version: 4.2.0 + resolution: "mustache@npm:4.2.0" + bin: + mustache: bin/mustache + checksum: 928fcb63e3aa44a562bfe9b59ba202cccbe40a46da50be6f0dd831b495be1dd7e38ca4657f0ecab2c1a89dc7bccba0885eab7ee7c1b215830da765758c7e0506 + languageName: node + linkType: hard + "mute-stream@npm:0.0.8, mute-stream@npm:~0.0.4": version: 0.0.8 resolution: "mute-stream@npm:0.0.8" @@ -16299,6 +19444,13 @@ __metadata: languageName: node linkType: hard +"nanoevents@npm:^7.0.1": + version: 7.0.1 + resolution: "nanoevents@npm:7.0.1" + checksum: 5c0704cfeb7af9052a70b1ce53e556e3743cefbbd09758121e0062e4fac2cf70ebea1e6b1414f18a81975ee1bdf4af05e13c82e1b015975493ef8ef34737787d + languageName: node + linkType: hard + "nanoid@npm:3.3.1": version: 3.3.1 resolution: "nanoid@npm:3.3.1" @@ -16317,7 +19469,7 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.6, nanoid@npm:^3.3.7": +"nanoid@npm:^3.3.3, nanoid@npm:^3.3.6, nanoid@npm:^3.3.7": version: 3.3.7 resolution: "nanoid@npm:3.3.7" bin: @@ -16394,6 +19546,61 @@ __metadata: languageName: node linkType: hard +"next@npm:^13.2.3": + version: 13.5.6 + resolution: "next@npm:13.5.6" + dependencies: + "@next/env": 13.5.6 + "@next/swc-darwin-arm64": 13.5.6 + "@next/swc-darwin-x64": 13.5.6 + "@next/swc-linux-arm64-gnu": 13.5.6 + "@next/swc-linux-arm64-musl": 13.5.6 + "@next/swc-linux-x64-gnu": 13.5.6 + "@next/swc-linux-x64-musl": 13.5.6 + "@next/swc-win32-arm64-msvc": 13.5.6 + "@next/swc-win32-ia32-msvc": 13.5.6 + "@next/swc-win32-x64-msvc": 13.5.6 + "@swc/helpers": 0.5.2 + busboy: 1.6.0 + caniuse-lite: ^1.0.30001406 + postcss: 8.4.31 + styled-jsx: 5.1.1 + watchpack: 2.4.0 + peerDependencies: + "@opentelemetry/api": ^1.1.0 + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + dependenciesMeta: + "@next/swc-darwin-arm64": + optional: true + "@next/swc-darwin-x64": + optional: true + "@next/swc-linux-arm64-gnu": + optional: true + "@next/swc-linux-arm64-musl": + optional: true + "@next/swc-linux-x64-gnu": + optional: true + "@next/swc-linux-x64-musl": + optional: true + "@next/swc-win32-arm64-msvc": + optional: true + "@next/swc-win32-ia32-msvc": + optional: true + "@next/swc-win32-x64-msvc": + optional: true + peerDependenciesMeta: + "@opentelemetry/api": + optional: true + sass: + optional: true + bin: + next: dist/bin/next + checksum: c869b0014ae921ada3bf22301985027ec320aebcd6aa9c16e8afbded68bb8def5874cca034c680e8c351a79578f1e514971d02777f6f0a5a1d7290f25970ac0d + languageName: node + linkType: hard + "next@npm:^14.0.4": version: 14.0.4 resolution: "next@npm:14.0.4" @@ -16521,6 +19728,13 @@ __metadata: languageName: node linkType: hard +"node-forge@npm:^1": + version: 1.3.1 + resolution: "node-forge@npm:1.3.1" + checksum: 08fb072d3d670599c89a1704b3e9c649ff1b998256737f0e06fbd1a5bf41cae4457ccaee32d95052d80bbafd9ffe01284e078c8071f0267dc9744e51c5ed42a9 + languageName: node + linkType: hard + "node-gyp-build@npm:^4.2.2": version: 4.8.0 resolution: "node-gyp-build@npm:4.8.0" @@ -16700,6 +19914,13 @@ __metadata: languageName: node linkType: hard +"nwmatcher@npm:^1.4.1": + version: 1.4.4 + resolution: "nwmatcher@npm:1.4.4" + checksum: ab086276db7e93756a4e9704dab29bac06e3a58f8b7074735afbd3df1d428c802a71310e362ea596e9654bd344670070075e496e559ba2534d4c1a35f0be04c3 + languageName: node + linkType: hard + "nwsapi@npm:^2.2.0, nwsapi@npm:^2.2.2": version: 2.2.7 resolution: "nwsapi@npm:2.2.7" @@ -16721,7 +19942,7 @@ __metadata: languageName: node linkType: hard -"object-assign@npm:^4.1.1": +"object-assign@npm:^4, object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f @@ -17364,7 +20585,7 @@ __metadata: languageName: node linkType: hard -"path-to-regexp@npm:6.2.1": +"path-to-regexp@npm:6.2.1, path-to-regexp@npm:^6.2.0": version: 6.2.1 resolution: "path-to-regexp@npm:6.2.1" checksum: f0227af8284ea13300f4293ba111e3635142f976d4197f14d5ad1f124aebd9118783dd2e5f1fe16f7273743cc3dbeddfb7493f237bb27c10fdae07020cc9b698 @@ -17528,6 +20749,20 @@ __metadata: languageName: node linkType: hard +"pn@npm:^1.0.0": + version: 1.1.0 + resolution: "pn@npm:1.1.0" + checksum: e4654186dc92a187c8c7fe4ccda902f4d39dd9c10f98d1c5a08ce5fad5507ef1e33ddb091240c3950bee81bd201b4c55098604c433a33b5e8bdd97f38b732fa0 + languageName: node + linkType: hard + +"pngjs@npm:^5.0.0": + version: 5.0.0 + resolution: "pngjs@npm:5.0.0" + checksum: 04e912cc45fb9601564e2284efaf0c5d20d131d9b596244f8a6789fc6cdb6b18d2975a6bbf7a001858d7e159d5c5c5dd7b11592e97629b7137f7f5cef05904c8 + languageName: node + linkType: hard + "postcss-discard-duplicates@npm:^5.1.0": version: 5.1.0 resolution: "postcss-discard-duplicates@npm:5.1.0" @@ -17645,7 +20880,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.4.19, postcss@npm:^8.4.27, postcss@npm:^8.4.4": +"postcss@npm:^8.4.19, postcss@npm:^8.4.27, postcss@npm:^8.4.32, postcss@npm:^8.4.4": version: 8.4.33 resolution: "postcss@npm:8.4.33" dependencies: @@ -17734,13 +20969,20 @@ __metadata: languageName: node linkType: hard -"pretty-bytes@npm:5.6.0": +"pretty-bytes@npm:5.6.0, pretty-bytes@npm:^5.3.0": version: 5.6.0 resolution: "pretty-bytes@npm:5.6.0" checksum: 9c082500d1e93434b5b291bd651662936b8bd6204ec9fa17d563116a192d6d86b98f6d328526b4e8d783c07d5499e2614a807520249692da9ec81564b2f439cd languageName: node linkType: hard +"pretty-bytes@npm:^6.1.1": + version: 6.1.1 + resolution: "pretty-bytes@npm:6.1.1" + checksum: 43d29d909d2d88072da2c3d72f8fd0f2d2523c516bfa640aff6e31f596ea1004b6601f4cabc50d14b2cf10e82635ebe5b7d9378f3d5bae1c0067131829421b8a + languageName: node + linkType: hard + "pretty-format@npm:^27.0.2": version: 27.5.1 resolution: "pretty-format@npm:27.5.1" @@ -17784,6 +21026,13 @@ __metadata: languageName: node linkType: hard +"printable-characters@npm:^1.0.42": + version: 1.0.42 + resolution: "printable-characters@npm:1.0.42" + checksum: 2724aa02919d7085933af0f8f904bd0de67a6b53834f2e5b98fc7aa3650e20755c805e8c85bcf96c09f678cb16a58b55640dd3a2da843195fce06b1ccb0c8ce4 + languageName: node + linkType: hard + "proc-log@npm:^3.0.0": version: 3.0.0 resolution: "proc-log@npm:3.0.0" @@ -17805,6 +21054,13 @@ __metadata: languageName: node linkType: hard +"progress@npm:^2.0.3": + version: 2.0.3 + resolution: "progress@npm:2.0.3" + checksum: f67403fe7b34912148d9252cb7481266a354bd99ce82c835f79070643bb3c6583d10dbcfda4d41e04bbc1d8437e9af0fb1e1f2135727878f5308682a579429b7 + languageName: node + linkType: hard + "promise-inflight@npm:^1.0.1": version: 1.0.1 resolution: "promise-inflight@npm:1.0.1" @@ -17972,6 +21228,20 @@ __metadata: languageName: node linkType: hard +"qrcode@npm:^1.5.1": + version: 1.5.3 + resolution: "qrcode@npm:1.5.3" + dependencies: + dijkstrajs: ^1.0.1 + encode-utf8: ^1.0.3 + pngjs: ^5.0.0 + yargs: ^15.3.1 + bin: + qrcode: bin/qrcode + checksum: 9a8a20a0a9cb1d15de8e7b3ffa214e8b6d2a8b07655f25bd1b1d77f4681488f84d7bae569870c0652872d829d5f8ac4922c27a6bd14c13f0e197bf07b28dead7 + languageName: node + linkType: hard + "qs@npm:6.11.0": version: 6.11.0 resolution: "qs@npm:6.11.0" @@ -18093,6 +21363,29 @@ __metadata: languageName: node linkType: hard +"react-fast-compare@npm:^3.2.0": + version: 3.2.2 + resolution: "react-fast-compare@npm:3.2.2" + checksum: 2071415b4f76a3e6b55c84611c4d24dcb12ffc85811a2840b5a3f1ff2d1a99be1020d9437ee7c6e024c9f4cbb84ceb35e48cf84f28fcb00265ad2dfdd3947704 + languageName: node + linkType: hard + +"react-helmet-async@npm:^1.3.0": + version: 1.3.0 + resolution: "react-helmet-async@npm:1.3.0" + dependencies: + "@babel/runtime": ^7.12.5 + invariant: ^2.2.4 + prop-types: ^15.7.2 + react-fast-compare: ^3.2.0 + shallowequal: ^1.1.0 + peerDependencies: + react: ^16.6.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.6.0 || ^17.0.0 || ^18.0.0 + checksum: 7ca7e47f8af14ea186688b512a87ab912bf6041312b297f92516341b140b3f0f8aedf5a44d226d99e69ed067b0cc106e38aeb9c9b738ffcc63d10721c844db90 + languageName: node + linkType: hard + "react-hotkeys-hook@npm:^4.4.1": version: 4.4.4 resolution: "react-hotkeys-hook@npm:4.4.4" @@ -18110,7 +21403,7 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^16.13.1": +"react-is@npm:^16.13.1, react-is@npm:^16.7.0": version: 16.13.1 resolution: "react-is@npm:16.13.1" checksum: f7a19ac3496de32ca9ae12aa030f00f14a3d45374f1ceca0af707c831b2a6098ef0d6bdae51bd437b0a306d7f01d4677fcc8de7c0d331eb47ad0f46130e53c5f @@ -18166,7 +21459,7 @@ __metadata: languageName: node linkType: hard -"react-router-dom@npm:^6.9.0": +"react-router-dom@npm:^6.17.0, react-router-dom@npm:^6.9.0": version: 6.21.2 resolution: "react-router-dom@npm:6.21.2" dependencies: @@ -18277,7 +21570,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.0": +"readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0, readable-stream@npm:^3.6.0": version: 3.6.2 resolution: "readable-stream@npm:3.6.2" dependencies: @@ -18654,7 +21947,31 @@ __metadata: languageName: node linkType: hard -"request@npm:^2.88.0": +"request-promise-core@npm:1.1.4": + version: 1.1.4 + resolution: "request-promise-core@npm:1.1.4" + dependencies: + lodash: ^4.17.19 + peerDependencies: + request: ^2.34 + checksum: c798bafd552961e36fbf5023b1d081e81c3995ab390f1bc8ef38a711ba3fe4312eb94dbd61887073d7356c3499b9380947d7f62faa805797c0dc50f039425699 + languageName: node + linkType: hard + +"request-promise-native@npm:^1.0.3": + version: 1.0.9 + resolution: "request-promise-native@npm:1.0.9" + dependencies: + request-promise-core: 1.1.4 + stealthy-require: ^1.1.1 + tough-cookie: ^2.3.3 + peerDependencies: + request: ^2.34 + checksum: 3e2c694eefac88cb20beef8911ad57a275ab3ccbae0c4ca6c679fffb09d5fd502458aab08791f0814ca914b157adab2d4e472597c97a73be702918e41725ed69 + languageName: node + linkType: hard + +"request@npm:^2.83.0, request@npm:^2.88.0": version: 2.88.2 resolution: "request@npm:2.88.2" dependencies: @@ -18779,7 +22096,14 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.0.0, resolve@npm:^1.14.2, resolve@npm:^1.20.0, resolve@npm:^1.22.4, resolve@npm:~1.22.1": +"resolve.exports@npm:^2.0.2": + version: 2.0.2 + resolution: "resolve.exports@npm:2.0.2" + checksum: 1c7778ca1b86a94f8ab4055d196c7d87d1874b96df4d7c3e67bbf793140f0717fd506dcafd62785b079cd6086b9264424ad634fb904409764c3509c3df1653f2 + languageName: node + linkType: hard + +"resolve@npm:^1.0.0, resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.0, resolve@npm:^1.22.4, resolve@npm:~1.22.1": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -18824,7 +22148,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.0.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.4#~builtin, resolve@patch:resolve@~1.22.1#~builtin": +"resolve@patch:resolve@^1.0.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.0#~builtin, resolve@patch:resolve@^1.22.4#~builtin, resolve@patch:resolve@~1.22.1#~builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#~builtin::version=1.22.8&hash=c3c19d" dependencies: @@ -19001,6 +22325,20 @@ __metadata: languageName: node linkType: hard +"rollup-plugin-terser@npm:^7.0.0": + version: 7.0.2 + resolution: "rollup-plugin-terser@npm:7.0.2" + dependencies: + "@babel/code-frame": ^7.10.4 + jest-worker: ^26.2.1 + serialize-javascript: ^4.0.0 + terser: ^5.0.0 + peerDependencies: + rollup: ^2.0.0 + checksum: af84bb7a7a894cd00852b6486528dfb8653cf94df4c126f95f389a346f401d054b08c46bee519a2ab6a22b33804d1d6ac6d8c90b1b2bf8fffb097eed73fc3c72 + languageName: node + linkType: hard + "rollup-pluginutils@npm:^2.8.1": version: 2.8.2 resolution: "rollup-pluginutils@npm:2.8.2" @@ -19010,6 +22348,20 @@ __metadata: languageName: node linkType: hard +"rollup@npm:^2.43.1": + version: 2.79.1 + resolution: "rollup@npm:2.79.1" + dependencies: + fsevents: ~2.3.2 + dependenciesMeta: + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 6a2bf167b3587d4df709b37d149ad0300692cc5deb510f89ac7bdc77c8738c9546ae3de9322b0968e1ed2b0e984571f5f55aae28fa7de4cfcb1bc5402a4e2be6 + languageName: node + linkType: hard + "rollup@npm:^3.27.1": version: 3.29.4 resolution: "rollup@npm:3.29.4" @@ -19024,6 +22376,60 @@ __metadata: languageName: node linkType: hard +"rollup@npm:^4.2.0": + version: 4.9.5 + resolution: "rollup@npm:4.9.5" + dependencies: + "@rollup/rollup-android-arm-eabi": 4.9.5 + "@rollup/rollup-android-arm64": 4.9.5 + "@rollup/rollup-darwin-arm64": 4.9.5 + "@rollup/rollup-darwin-x64": 4.9.5 + "@rollup/rollup-linux-arm-gnueabihf": 4.9.5 + "@rollup/rollup-linux-arm64-gnu": 4.9.5 + "@rollup/rollup-linux-arm64-musl": 4.9.5 + "@rollup/rollup-linux-riscv64-gnu": 4.9.5 + "@rollup/rollup-linux-x64-gnu": 4.9.5 + "@rollup/rollup-linux-x64-musl": 4.9.5 + "@rollup/rollup-win32-arm64-msvc": 4.9.5 + "@rollup/rollup-win32-ia32-msvc": 4.9.5 + "@rollup/rollup-win32-x64-msvc": 4.9.5 + "@types/estree": 1.0.5 + fsevents: ~2.3.2 + dependenciesMeta: + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: a6bb721f2251a2299e99be2eb58b0949571545809b75571c42baa50e749437aa9ef40f0660644d992e2387ba7f0775271ab9388fe4fbb02c6c3fc5db6a8b9711 + languageName: node + linkType: hard + "run-async@npm:^2.4.0": version: 2.4.1 resolution: "run-async@npm:2.4.1" @@ -19111,7 +22517,7 @@ __metadata: languageName: node linkType: hard -"sax@npm:>=0.6.0": +"sax@npm:>=0.6.0, sax@npm:^1.2.1": version: 1.3.0 resolution: "sax@npm:1.3.0" checksum: 238ab3a9ba8c8f8aaf1c5ea9120386391f6ee0af52f1a6a40bbb6df78241dd05d782f2359d614ac6aae08c4c4125208b456548a6cf68625aa4fe178486e63ecd @@ -19155,6 +22561,16 @@ __metadata: languageName: node linkType: hard +"selfsigned@npm:^2.0.1": + version: 2.4.1 + resolution: "selfsigned@npm:2.4.1" + dependencies: + "@types/node-forge": ^1.3.0 + node-forge: ^1 + checksum: 38b91c56f1d7949c0b77f9bbe4545b19518475cae15e7d7f0043f87b1626710b011ce89879a88969651f650a19d213bb15b7d5b4c2877df9eeeff7ba8f8b9bfa + languageName: node + linkType: hard + "semver@npm:5.5.x": version: 5.5.1 resolution: "semver@npm:5.5.1" @@ -19243,6 +22659,15 @@ __metadata: languageName: node linkType: hard +"serialize-javascript@npm:^4.0.0": + version: 4.0.0 + resolution: "serialize-javascript@npm:4.0.0" + dependencies: + randombytes: ^2.1.0 + checksum: 3273b3394b951671fcf388726e9577021870dfbf85e742a1183fb2e91273e6101bdccea81ff230724f6659a7ee4cef924b0ff9baca32b79d9384ec37caf07302 + languageName: node + linkType: hard + "serve-static@npm:1.15.0": version: 1.15.0 resolution: "serve-static@npm:1.15.0" @@ -19307,6 +22732,13 @@ __metadata: languageName: node linkType: hard +"shallowequal@npm:^1.1.0": + version: 1.1.0 + resolution: "shallowequal@npm:1.1.0" + checksum: f4c1de0837f106d2dbbfd5d0720a5d059d1c66b42b580965c8f06bb1db684be8783538b684092648c981294bf817869f743a066538771dbecb293df78f765e00 + languageName: node + linkType: hard + "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" @@ -19539,7 +22971,7 @@ __metadata: languageName: node linkType: hard -"source-map-support@npm:^0.5.12, source-map-support@npm:^0.5.17, source-map-support@npm:^0.5.21": +"source-map-support@npm:0.5.21, source-map-support@npm:^0.5.12, source-map-support@npm:^0.5.17, source-map-support@npm:^0.5.21, source-map-support@npm:~0.5.20": version: 0.5.21 resolution: "source-map-support@npm:0.5.21" dependencies: @@ -19549,7 +22981,14 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.1": +"source-map@npm:0.5.6": + version: 0.5.6 + resolution: "source-map@npm:0.5.6" + checksum: 390b3f5165c9631a74fb6fb55ba61e62a7f9b7d4026ae0e2bfc2899c241d71c1bccb8731c496dc7f7cb79a5f523406eb03d8c5bebe8448ee3fc38168e2d209c8 + languageName: node + linkType: hard + +"source-map@npm:0.6.1, source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.1": version: 0.6.1 resolution: "source-map@npm:0.6.1" checksum: 59ce8640cf3f3124f64ac289012c2b8bd377c238e316fb323ea22fbfe83da07d81e000071d7242cad7a23cd91c7de98e4df8830ec3f133cb6133a5f6e9f67bc2 @@ -19563,6 +23002,15 @@ __metadata: languageName: node linkType: hard +"source-map@npm:^0.8.0-beta.0": + version: 0.8.0-beta.0 + resolution: "source-map@npm:0.8.0-beta.0" + dependencies: + whatwg-url: ^7.0.0 + checksum: e94169be6461ab0ac0913313ad1719a14c60d402bd22b0ad96f4a6cffd79130d91ab5df0a5336a326b04d2df131c1409f563c9dc0d21a6ca6239a44b6c8dbd92 + languageName: node + linkType: hard + "sourcemap-codec@npm:^1.4.8": version: 1.4.8 resolution: "sourcemap-codec@npm:1.4.8" @@ -19674,6 +23122,15 @@ __metadata: languageName: node linkType: hard +"stack-generator@npm:^2.0.5": + version: 2.0.10 + resolution: "stack-generator@npm:2.0.10" + dependencies: + stackframe: ^1.3.4 + checksum: 4fc3978a934424218a0aa9f398034e1f78153d5ff4f4ff9c62478c672debb47dd58de05b09fc3900530cbb526d72c93a6e6c9353bacc698e3b1c00ca3dda0c47 + languageName: node + linkType: hard + "stack-utils@npm:^2.0.3": version: 2.0.6 resolution: "stack-utils@npm:2.0.6" @@ -19683,6 +23140,44 @@ __metadata: languageName: node linkType: hard +"stackframe@npm:^1.3.4": + version: 1.3.4 + resolution: "stackframe@npm:1.3.4" + checksum: bae1596873595c4610993fa84f86a3387d67586401c1816ea048c0196800c0646c4d2da98c2ee80557fd9eff05877efe33b91ba6cd052658ed96ddc85d19067d + languageName: node + linkType: hard + +"stacktrace-gps@npm:^3.0.4": + version: 3.1.2 + resolution: "stacktrace-gps@npm:3.1.2" + dependencies: + source-map: 0.5.6 + stackframe: ^1.3.4 + checksum: 85daa232d138239b6ae0f4bcdd87d15d302a045d93625db17614030945b5314e204b5fbcf9bee5b6f4f9e6af5fca05f65c27fe910894b861ef6853b99470aa1c + languageName: node + linkType: hard + +"stacktrace-js@npm:2.0.2": + version: 2.0.2 + resolution: "stacktrace-js@npm:2.0.2" + dependencies: + error-stack-parser: ^2.0.6 + stack-generator: ^2.0.5 + stacktrace-gps: ^3.0.4 + checksum: 081e786d56188ac04ac6604c09cd863b3ca2b4300ec061366cf68c3e4ad9edaa34fb40deea03cc23a05f442aa341e9171f47313f19bd588f9bec6c505a396286 + languageName: node + linkType: hard + +"stacktracey@npm:^2.1.8": + version: 2.1.8 + resolution: "stacktracey@npm:2.1.8" + dependencies: + as-table: ^1.0.36 + get-source: ^2.0.12 + checksum: abd8316b4e120884108f5a47b2f61abdcaeaa118afd95f3c48317cb057fff43d697450ba00de3f9fe7fee61ee72644ccda4db990a8e4553706644f7c17522eab + languageName: node + linkType: hard + "statuses@npm:2.0.1": version: 2.0.1 resolution: "statuses@npm:2.0.1" @@ -19690,6 +23185,13 @@ __metadata: languageName: node linkType: hard +"stealthy-require@npm:^1.1.1": + version: 1.1.1 + resolution: "stealthy-require@npm:1.1.1" + checksum: 6805b857a9f3a6a1079fc6652278038b81011f2a5b22cbd559f71a6c02087e6f1df941eb10163e3fdc5391ab5807aa46758d4258547c1f5ede31e6d9bfda8dd3 + languageName: node + linkType: hard + "stop-iteration-iterator@npm:^1.0.0": version: 1.0.0 resolution: "stop-iteration-iterator@npm:1.0.0" @@ -19699,6 +23201,23 @@ __metadata: languageName: node linkType: hard +"stoppable@npm:^1.1.0": + version: 1.1.0 + resolution: "stoppable@npm:1.1.0" + checksum: 63104fcbdece130bc4906fd982061e763d2ef48065ed1ab29895e5ad00552c625f8a4c50c9cd2e3bfa805c8a2c3bfdda0f07c5ae39694bd2d5cb0bee1618d1e9 + languageName: node + linkType: hard + +"stream-browserify@npm:3.0.0": + version: 3.0.0 + resolution: "stream-browserify@npm:3.0.0" + dependencies: + inherits: ~2.0.4 + readable-stream: ^3.5.0 + checksum: 4c47ef64d6f03815a9ca3874e2319805e8e8a85f3550776c47ce523b6f4c6cd57f40e46ec6a9ab8ad260fde61863c2718f250d3bedb3fe9052444eb9abfd9921 + languageName: node + linkType: hard + "stream-combiner@npm:^0.2.1": version: 0.2.2 resolution: "stream-combiner@npm:0.2.2" @@ -19885,6 +23404,17 @@ __metadata: languageName: node linkType: hard +"stringify-object@npm:^3.3.0": + version: 3.3.0 + resolution: "stringify-object@npm:3.3.0" + dependencies: + get-own-enumerable-property-symbols: ^3.0.0 + is-obj: ^1.0.1 + is-regexp: ^1.0.0 + checksum: 6827a3f35975cfa8572e8cd3ed4f7b262def260af18655c6fde549334acdac49ddba69f3c861ea5a6e9c5a4990fe4ae870b9c0e6c31019430504c94a83b7a154 + languageName: node + linkType: hard + "strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" @@ -19953,6 +23483,13 @@ __metadata: languageName: node linkType: hard +"strip-comments@npm:^2.0.1": + version: 2.0.1 + resolution: "strip-comments@npm:2.0.1" + checksum: 36cd122e1c27b5be69df87e1687770a62fe183bdee9f3ff5cf85d30bbc98280afc012581f2fd50c7ad077c90f656f272560c9d2e520d28604b8b7ea3bc87d6f9 + languageName: node + linkType: hard + "strip-final-newline@npm:^2.0.0": version: 2.0.0 resolution: "strip-final-newline@npm:2.0.0" @@ -19990,6 +23527,13 @@ __metadata: languageName: node linkType: hard +"strnum@npm:^1.0.5": + version: 1.0.5 + resolution: "strnum@npm:1.0.5" + checksum: 651b2031db5da1bf4a77fdd2f116a8ac8055157c5420f5569f64879133825915ad461513e7202a16d7fec63c54fd822410d0962f8ca12385c4334891b9ae6dd2 + languageName: node + linkType: hard + "style-to-object@npm:^0.4.1": version: 0.4.4 resolution: "style-to-object@npm:0.4.4" @@ -20092,7 +23636,7 @@ __metadata: languageName: node linkType: hard -"symbol-tree@npm:^3.2.4": +"symbol-tree@npm:^3.2.1, symbol-tree@npm:^3.2.4": version: 3.2.4 resolution: "symbol-tree@npm:3.2.4" checksum: 6e8fc7e1486b8b54bea91199d9535bb72f10842e40c79e882fc94fb7b14b89866adf2fd79efa5ebb5b658bc07fb459ccce5ac0e99ef3d72f474e74aaf284029d @@ -20143,7 +23687,7 @@ __metadata: languageName: node linkType: hard -"tar@npm:^6.0.2, tar@npm:^6.1.11, tar@npm:^6.1.2": +"tar@npm:^6.0.2, tar@npm:^6.1.11, tar@npm:^6.1.2, tar@npm:^6.2.0": version: 6.2.0 resolution: "tar@npm:6.2.0" dependencies: @@ -20157,6 +23701,25 @@ __metadata: languageName: node linkType: hard +"temp-dir@npm:^2.0.0": + version: 2.0.0 + resolution: "temp-dir@npm:2.0.0" + checksum: cc4f0404bf8d6ae1a166e0e64f3f409b423f4d1274d8c02814a59a5529f07db6cd070a749664141b992b2c1af337fa9bb451a460a43bb9bcddc49f235d3115aa + languageName: node + linkType: hard + +"tempy@npm:^0.6.0": + version: 0.6.0 + resolution: "tempy@npm:0.6.0" + dependencies: + is-stream: ^2.0.0 + temp-dir: ^2.0.0 + type-fest: ^0.16.0 + unique-string: ^2.0.0 + checksum: dd09c8b6615e4b785ea878e9a18b17ac0bfe5dccf5a0e205ebd274bb356356aff3f5c90a6c917077d51c75efb7648b113a78b0492e2ffc81a7c9912eb872ac52 + languageName: node + linkType: hard + "terminal-link@npm:^2.0.0, terminal-link@npm:^2.1.1": version: 2.1.1 resolution: "terminal-link@npm:2.1.1" @@ -20167,6 +23730,20 @@ __metadata: languageName: node linkType: hard +"terser@npm:^5.0.0": + version: 5.26.0 + resolution: "terser@npm:5.26.0" + dependencies: + "@jridgewell/source-map": ^0.3.3 + acorn: ^8.8.2 + commander: ^2.20.0 + source-map-support: ~0.5.20 + bin: + terser: bin/terser + checksum: 02a9bb896f04df828025af8f0eced36c315d25d310b6c2418e7dad2bed19ddeb34a9cea9b34e7c24789830fa51e1b6a9be26679980987a9c817a7e6d9cd4154b + languageName: node + linkType: hard + "test-exclude@npm:^6.0.0": version: 6.0.0 resolution: "test-exclude@npm:6.0.0" @@ -20329,7 +23906,22 @@ __metadata: languageName: node linkType: hard -"tough-cookie@npm:^2.3.1, tough-cookie@npm:~2.5.0": +"toucan-js@npm:^2.7.0": + version: 2.7.0 + resolution: "toucan-js@npm:2.7.0" + dependencies: + "@sentry/core": 6.19.6 + "@sentry/hub": 6.19.6 + "@sentry/types": 6.19.6 + "@sentry/utils": 6.19.6 + "@types/cookie": 0.5.0 + cookie: 0.5.0 + stacktrace-js: 2.0.2 + checksum: 2ad055f9c09949605e0df2f301cd2d39581c1ffb3e37ccc5e387e4e2c14430d8d2efdcb0d871262b5eaf60183ae453f6abe86d733ab169677b681c2192dc0ee6 + languageName: node + linkType: hard + +"tough-cookie@npm:^2.3.1, tough-cookie@npm:^2.3.3, tough-cookie@npm:~2.5.0": version: 2.5.0 resolution: "tough-cookie@npm:2.5.0" dependencies: @@ -20351,6 +23943,15 @@ __metadata: languageName: node linkType: hard +"tr46@npm:^1.0.1": + version: 1.0.1 + resolution: "tr46@npm:1.0.1" + dependencies: + punycode: ^2.1.0 + checksum: 96d4ed46bc161db75dbf9247a236ea0bfcaf5758baae6749e92afab0bc5a09cb59af21788ede7e55080f2bf02dce3e4a8f2a484cc45164e29f4b5e68f7cbcc1a + languageName: node + linkType: hard + "tr46@npm:^2.1.0": version: 2.1.0 resolution: "tr46@npm:2.1.0" @@ -20572,7 +24173,7 @@ __metadata: languageName: node linkType: hard -"tsconfig-paths@npm:^3.15.0": +"tsconfig-paths@npm:^3.14.1, tsconfig-paths@npm:^3.15.0": version: 3.15.0 resolution: "tsconfig-paths@npm:3.15.0" dependencies: @@ -20621,14 +24222,14 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^1.8.1, tslib@npm:^1.9.0": +"tslib@npm:^1.11.1, tslib@npm:^1.8.1, tslib@npm:^1.9.0, tslib@npm:^1.9.3": version: 1.14.1 resolution: "tslib@npm:1.14.1" checksum: dbe628ef87f66691d5d2959b3e41b9ca0045c3ee3c7c7b906cc1e328b39f199bb1ad9e671c39025bd56122ac57dfbf7385a94843b1cc07c60a4db74795829acd languageName: node linkType: hard -"tslib@npm:^2, tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.1, tslib@npm:^2.6.2": +"tslib@npm:^2, tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.2.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.1, tslib@npm:^2.6.2": version: 2.6.2 resolution: "tslib@npm:2.6.2" checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad @@ -20688,7 +24289,7 @@ __metadata: languageName: node linkType: hard -"tunnel@npm:0.0.6": +"tunnel@npm:0.0.6, tunnel@npm:^0.0.6": version: 0.0.6 resolution: "tunnel@npm:0.0.6" checksum: c362948df9ad34b649b5585e54ce2838fa583aa3037091aaed66793c65b423a264e5229f0d7e9a95513a795ac2bd4cb72cda7e89a74313f182c1e9ae0b0994fa @@ -20736,6 +24337,13 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:^0.16.0": + version: 0.16.0 + resolution: "type-fest@npm:0.16.0" + checksum: 1a4102c06dc109db00418c753062e206cab65befd469d000ece4452ee649bf2a9cf57686d96fb42326bc9d918d9a194d4452897b486dcc41989e5c99e4e87094 + languageName: node + linkType: hard + "type-fest@npm:^0.20.2": version: 0.20.2 resolution: "type-fest@npm:0.20.2" @@ -20849,7 +24457,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.2.2": +"typescript@npm:^5.0.2, typescript@npm:^5.2.2": version: 5.3.3 resolution: "typescript@npm:5.3.3" bin: @@ -20879,7 +24487,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@^5.2.2#~builtin": +"typescript@patch:typescript@^5.0.2#~builtin, typescript@patch:typescript@^5.2.2#~builtin": version: 5.3.3 resolution: "typescript@patch:typescript@npm%3A5.3.3#~builtin::version=5.3.3&hash=85af82" bin: @@ -20971,6 +24579,15 @@ __metadata: languageName: node linkType: hard +"undici@npm:^5.22.1, undici@npm:^5.25.4": + version: 5.28.2 + resolution: "undici@npm:5.28.2" + dependencies: + "@fastify/busboy": ^2.0.0 + checksum: f9e9335803f962fff07c3c11c6d50bbc76248bacf97035047155adb29c3622a65bd6bff23a22218189740133149d22e63b68131d8c40e78ac6cb4b6d686a6dfa + languageName: node + linkType: hard + "unicode-canonical-property-names-ecmascript@npm:^2.0.0": version: 2.0.0 resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" @@ -21068,6 +24685,15 @@ __metadata: languageName: node linkType: hard +"unique-string@npm:^2.0.0": + version: 2.0.0 + resolution: "unique-string@npm:2.0.0" + dependencies: + crypto-random-string: ^2.0.0 + checksum: ef68f639136bcfe040cf7e3cd7a8dff076a665288122855148a6f7134092e6ed33bf83a7f3a9185e46c98dddc445a0da6ac25612afa1a7c38b8b654d6c02498e + languageName: node + linkType: hard + "unist-builder@npm:^3.0.0": version: 3.0.1 resolution: "unist-builder@npm:3.0.1" @@ -21254,6 +24880,13 @@ __metadata: languageName: node linkType: hard +"upath@npm:^1.2.0": + version: 1.2.0 + resolution: "upath@npm:1.2.0" + checksum: 4c05c094797cb733193a0784774dbea5b1889d502fc9f0572164177e185e4a59ba7099bf0b0adf945b232e2ac60363f9bf18aac9b2206fb99cbef971a8455445 + languageName: node + linkType: hard + "update-browserslist-db@npm:^1.0.13": version: 1.0.13 resolution: "update-browserslist-db@npm:1.0.13" @@ -21329,6 +24962,18 @@ __metadata: languageName: node linkType: hard +"use-isomorphic-layout-effect@npm:^1.1.1": + version: 1.1.2 + resolution: "use-isomorphic-layout-effect@npm:1.1.2" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: a6532f7fc9ae222c3725ff0308aaf1f1ddbd3c00d685ef9eee6714fd0684de5cb9741b432fbf51e61a784e2955424864f7ea9f99734a02f237b17ad3e18ea5cb + languageName: node + linkType: hard + "use-sidecar@npm:^1.1.2": version: 1.1.2 resolution: "use-sidecar@npm:1.1.2" @@ -21391,6 +25036,25 @@ __metadata: languageName: node linkType: hard +"uuid-by-string@npm:^4.0.0": + version: 4.0.0 + resolution: "uuid-by-string@npm:4.0.0" + dependencies: + js-md5: ^0.7.3 + js-sha1: ^0.6.0 + checksum: eff002658f86e8b9eef6346aff91ae26ba4c8cd1192d42f96b08490af2856a70010e1a1e4553f80bbb89eee0b2e4016917e195197a0c698da85e55f1fbbe8bef + languageName: node + linkType: hard + +"uuid-readable@npm:^0.0.2": + version: 0.0.2 + resolution: "uuid-readable@npm:0.0.2" + dependencies: + uuid: ^8.3.0 + checksum: 402a1df6e41ed502c8c85836d2d71843501c1a13d629b3c1c4b3808857dafb3ea91131adfae7b4d558e4a77d09e02360ea92c7fa4b57c38bca694b76a8aeb12f + languageName: node + linkType: hard + "uuid@npm:^2.0.1": version: 2.0.3 resolution: "uuid@npm:2.0.3" @@ -21407,6 +25071,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^8.3.0, uuid@npm:^8.3.2": + version: 8.3.2 + resolution: "uuid@npm:8.3.2" + bin: + uuid: dist/bin/uuid + checksum: 5575a8a75c13120e2f10e6ddc801b2c7ed7d8f3c8ac22c7ed0c7b2ba6383ec0abda88c905085d630e251719e0777045ae3236f04c812184b7c765f63a70e58df + languageName: node + linkType: hard + "uuid@npm:^9.0.0": version: 9.0.1 resolution: "uuid@npm:9.0.1" @@ -21455,7 +25128,7 @@ __metadata: languageName: node linkType: hard -"vary@npm:~1.1.2": +"vary@npm:^1, vary@npm:~1.1.2": version: 1.1.2 resolution: "vary@npm:1.1.2" checksum: ae0123222c6df65b437669d63dfa8c36cee20a504101b2fcd97b8bf76f91259c17f9f2b4d70a1e3c6bbcee7f51b28392833adb6b2770b23b01abec84e369660b @@ -21624,6 +25297,23 @@ __metadata: languageName: node linkType: hard +"vite-plugin-pwa@npm:^0.17.0": + version: 0.17.4 + resolution: "vite-plugin-pwa@npm:0.17.4" + dependencies: + debug: ^4.3.4 + fast-glob: ^3.3.2 + pretty-bytes: ^6.1.1 + workbox-build: ^7.0.0 + workbox-window: ^7.0.0 + peerDependencies: + vite: ^3.1.0 || ^4.0.0 || ^5.0.0 + workbox-build: ^7.0.0 + workbox-window: ^7.0.0 + checksum: aaa0247a435d0621b0c3078938156fde1a1abbd2cb0608968d79beec85f5348e50a2cc8e1bd75bee85f7b5385e20eaf388d4a710466252050f1d660b64245bec + languageName: node + linkType: hard + "vite@npm:^3.0.0 || ^4.0.0, vite@npm:^4.1.4, vite@npm:^4.3.4": version: 4.5.1 resolution: "vite@npm:4.5.1" @@ -21664,6 +25354,46 @@ __metadata: languageName: node linkType: hard +"vite@npm:^5.0.0": + version: 5.0.11 + resolution: "vite@npm:5.0.11" + dependencies: + esbuild: ^0.19.3 + fsevents: ~2.3.3 + postcss: ^8.4.32 + rollup: ^4.2.0 + peerDependencies: + "@types/node": ^18.0.0 || >=20.0.0 + less: "*" + lightningcss: ^1.21.0 + sass: "*" + stylus: "*" + sugarss: "*" + terser: ^5.4.0 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + bin: + vite: bin/vite.js + checksum: 262e41f25ce0cc5fc3c2065b1796f64ec115d3ac2d9625dbfb36d6628ba10e63684ef5515bb2ff1aa8e34c6f89e9c10e8211cb88f6c7f0da6869362851345437 + languageName: node + linkType: hard + "vm2@npm:^3.9.17": version: 3.9.19 resolution: "vm2@npm:3.9.19" @@ -21830,6 +25560,15 @@ __metadata: languageName: node linkType: hard +"whatwg-encoding@npm:^1.0.1": + version: 1.0.5 + resolution: "whatwg-encoding@npm:1.0.5" + dependencies: + iconv-lite: 0.4.24 + checksum: 5be4efe111dce29ddee3448d3915477fcc3b28f991d9cf1300b4e50d6d189010d47bca2f51140a844cf9b726e8f066f4aee72a04d687bfe4f2ee2767b2f5b1e6 + languageName: node + linkType: hard + "whatwg-encoding@npm:^2.0.0": version: 2.0.0 resolution: "whatwg-encoding@npm:2.0.0" @@ -21876,6 +25615,28 @@ __metadata: languageName: node linkType: hard +"whatwg-url@npm:^6.3.0": + version: 6.5.0 + resolution: "whatwg-url@npm:6.5.0" + dependencies: + lodash.sortby: ^4.7.0 + tr46: ^1.0.1 + webidl-conversions: ^4.0.2 + checksum: a10bd5e29f4382cd19789c2a7bbce25416e606b6fefc241c7fe34a2449de5bc5709c165bd13634eda433942d917ca7386a52841780b82dc37afa8141c31a8ebd + languageName: node + linkType: hard + +"whatwg-url@npm:^7.0.0": + version: 7.1.0 + resolution: "whatwg-url@npm:7.1.0" + dependencies: + lodash.sortby: ^4.7.0 + tr46: ^1.0.1 + webidl-conversions: ^4.0.2 + checksum: fecb07c87290b47d2ec2fb6d6ca26daad3c9e211e0e531dd7566e7ff95b5b3525a57d4f32640ad4adf057717e0c215731db842ad761e61d947e81010e05cf5fd + languageName: node + linkType: hard + "whatwg-url@npm:^8.4.0": version: 8.7.0 resolution: "whatwg-url@npm:8.7.0" @@ -22009,6 +25770,222 @@ __metadata: languageName: node linkType: hard +"workbox-background-sync@npm:7.0.0": + version: 7.0.0 + resolution: "workbox-background-sync@npm:7.0.0" + dependencies: + idb: ^7.0.1 + workbox-core: 7.0.0 + checksum: 79b64416563761d36b91342d6ce2618d1c984bebcd511ce56b80098127e42c676d4831dd566a0a80a6bb52a618ad815b277ce6b310e4a5c5043e7394829d30c6 + languageName: node + linkType: hard + +"workbox-broadcast-update@npm:7.0.0": + version: 7.0.0 + resolution: "workbox-broadcast-update@npm:7.0.0" + dependencies: + workbox-core: 7.0.0 + checksum: eee5c09fd78b3439348c7c92013f63700f14004d46161f19b0daf0d01303c6785f0953b746258cfb2627932108631370c8fa52ec5b526177cd528ae02530370e + languageName: node + linkType: hard + +"workbox-build@npm:^7.0.0": + version: 7.0.0 + resolution: "workbox-build@npm:7.0.0" + dependencies: + "@apideck/better-ajv-errors": ^0.3.1 + "@babel/core": ^7.11.1 + "@babel/preset-env": ^7.11.0 + "@babel/runtime": ^7.11.2 + "@rollup/plugin-babel": ^5.2.0 + "@rollup/plugin-node-resolve": ^11.2.1 + "@rollup/plugin-replace": ^2.4.1 + "@surma/rollup-plugin-off-main-thread": ^2.2.3 + ajv: ^8.6.0 + common-tags: ^1.8.0 + fast-json-stable-stringify: ^2.1.0 + fs-extra: ^9.0.1 + glob: ^7.1.6 + lodash: ^4.17.20 + pretty-bytes: ^5.3.0 + rollup: ^2.43.1 + rollup-plugin-terser: ^7.0.0 + source-map: ^0.8.0-beta.0 + stringify-object: ^3.3.0 + strip-comments: ^2.0.1 + tempy: ^0.6.0 + upath: ^1.2.0 + workbox-background-sync: 7.0.0 + workbox-broadcast-update: 7.0.0 + workbox-cacheable-response: 7.0.0 + workbox-core: 7.0.0 + workbox-expiration: 7.0.0 + workbox-google-analytics: 7.0.0 + workbox-navigation-preload: 7.0.0 + workbox-precaching: 7.0.0 + workbox-range-requests: 7.0.0 + workbox-recipes: 7.0.0 + workbox-routing: 7.0.0 + workbox-strategies: 7.0.0 + workbox-streams: 7.0.0 + workbox-sw: 7.0.0 + workbox-window: 7.0.0 + checksum: f230463833a8b6d1beadbfb4db5526d1b6b047ffa23abcd2afdc306510e1f3f942a74d1c59c76ee371a326bb2fe616ced05d0c53aefee5902c68a3f31faa27dc + languageName: node + linkType: hard + +"workbox-cacheable-response@npm:7.0.0": + version: 7.0.0 + resolution: "workbox-cacheable-response@npm:7.0.0" + dependencies: + workbox-core: 7.0.0 + checksum: c9d834b25564ee01dd4df17b1f27e61160a3b610f40c0e297a9973712878fe617e168e3b1541c7b70b0de3828cb4b62de3088424b4a2872ed5a106e7e777772f + languageName: node + linkType: hard + +"workbox-core@npm:7.0.0": + version: 7.0.0 + resolution: "workbox-core@npm:7.0.0" + checksum: ca64872f9ce59ee1f3f32a5ecbde36377081a221930c6f925e2c0d7fe39d3fdc309166c430d56d972eba4f7c40d2e7e91a0020699a0745790fbef578ff8f34f6 + languageName: node + linkType: hard + +"workbox-expiration@npm:7.0.0": + version: 7.0.0 + resolution: "workbox-expiration@npm:7.0.0" + dependencies: + idb: ^7.0.1 + workbox-core: 7.0.0 + checksum: 3d7cce573111bfb32f35d97ea95d5016ac42bdc0f3ab5096e5c0fd799dd466ccc3cbfdbdeab4e7158923ae3e406f2002add01e5c9369f9c3e2623e41bc04b324 + languageName: node + linkType: hard + +"workbox-google-analytics@npm:7.0.0": + version: 7.0.0 + resolution: "workbox-google-analytics@npm:7.0.0" + dependencies: + workbox-background-sync: 7.0.0 + workbox-core: 7.0.0 + workbox-routing: 7.0.0 + workbox-strategies: 7.0.0 + checksum: defb12c3f4cf924aef8c647724c32d1100042447aed20128702815eba0f6d55ba6dde6557036dc13d68c0ab0570188757136bd453823fe25f2fa541cb18b8e0c + languageName: node + linkType: hard + +"workbox-navigation-preload@npm:7.0.0": + version: 7.0.0 + resolution: "workbox-navigation-preload@npm:7.0.0" + dependencies: + workbox-core: 7.0.0 + checksum: 329018003ce44812d37f1e168960abe34c7ac4b8cd1c8f86da172e73919fb51ba94a63db3b4024614066bf1ea38e1a89839eafd46eed9a13015dd4cf6fcd056c + languageName: node + linkType: hard + +"workbox-precaching@npm:7.0.0": + version: 7.0.0 + resolution: "workbox-precaching@npm:7.0.0" + dependencies: + workbox-core: 7.0.0 + workbox-routing: 7.0.0 + workbox-strategies: 7.0.0 + checksum: 311b1c4a162e976e0a41e36e6a96eb64fea381eda538d8a9ae962d4f39c5ba420617753aac44e19105de19aef5242c9c68a09226d144ca3cf62738fc9f491f5d + languageName: node + linkType: hard + +"workbox-range-requests@npm:7.0.0": + version: 7.0.0 + resolution: "workbox-range-requests@npm:7.0.0" + dependencies: + workbox-core: 7.0.0 + checksum: 04f6d7921a8a4a024b0bf0049a592ebedcdd285a52d1b8714e0a53efc936339dac39c3a5b5b6db9a3356b9f3ed1876024403260ec426cf9dc65e3b7ba5464914 + languageName: node + linkType: hard + +"workbox-recipes@npm:7.0.0": + version: 7.0.0 + resolution: "workbox-recipes@npm:7.0.0" + dependencies: + workbox-cacheable-response: 7.0.0 + workbox-core: 7.0.0 + workbox-expiration: 7.0.0 + workbox-precaching: 7.0.0 + workbox-routing: 7.0.0 + workbox-strategies: 7.0.0 + checksum: 253d50a315855917ca6683d6a3e910ac3c6f8915a8bcc80a7f15f277db7f48dc288c0ec2d9cdc64390bdd50446e66910246f384ce19f46688db97c715b323123 + languageName: node + linkType: hard + +"workbox-routing@npm:7.0.0": + version: 7.0.0 + resolution: "workbox-routing@npm:7.0.0" + dependencies: + workbox-core: 7.0.0 + checksum: 9ea5b00fde5d90819e29ebf6d4aec3b84abec97854eb333c71b83548f1ba12b7f92d764a159f23cfa9e8164940e7b7136536fc0477784560cf2108d8dfe7f83b + languageName: node + linkType: hard + +"workbox-strategies@npm:7.0.0": + version: 7.0.0 + resolution: "workbox-strategies@npm:7.0.0" + dependencies: + workbox-core: 7.0.0 + checksum: 4f20604e762fb43b32a16d60e014d14c0933300083c109a95251c06c65c25c9d78ab16bbe638b64435911d4a01ae5f7c28c7e78d611a122ee6453be2c42a87dc + languageName: node + linkType: hard + +"workbox-streams@npm:7.0.0": + version: 7.0.0 + resolution: "workbox-streams@npm:7.0.0" + dependencies: + workbox-core: 7.0.0 + workbox-routing: 7.0.0 + checksum: e2975eb773bcf765c9cc8166936a9a2aaec2609fcddc178cbf6b2da54a113c4e2e62cbd257104861ea21b80c2a051936d62249f06d2414072405147f5181c0ef + languageName: node + linkType: hard + +"workbox-sw@npm:7.0.0": + version: 7.0.0 + resolution: "workbox-sw@npm:7.0.0" + checksum: f2673bc3f73ef5a54349eb7c4c63aefb7dfe6b6492947851ffa44079efdbfff07a26e68a0f7ea3801e03ab3fdc29acdc36cd315b9fbdb8a60963c7cb95f2de43 + languageName: node + linkType: hard + +"workbox-window@npm:7.0.0, workbox-window@npm:^7.0.0": + version: 7.0.0 + resolution: "workbox-window@npm:7.0.0" + dependencies: + "@types/trusted-types": ^2.0.2 + workbox-core: 7.0.0 + checksum: 486ceaf2c04953cd73fe04760929a9c42818b57fffbbaca3fc9065cfd6bf3f5a571d2ea78db177e548a98041c8752faa360dda8eaf0f10b8638ef3eb1b696b13 + languageName: node + linkType: hard + +"workerd@npm:1.20231030.0": + version: 1.20231030.0 + resolution: "workerd@npm:1.20231030.0" + dependencies: + "@cloudflare/workerd-darwin-64": 1.20231030.0 + "@cloudflare/workerd-darwin-arm64": 1.20231030.0 + "@cloudflare/workerd-linux-64": 1.20231030.0 + "@cloudflare/workerd-linux-arm64": 1.20231030.0 + "@cloudflare/workerd-windows-64": 1.20231030.0 + dependenciesMeta: + "@cloudflare/workerd-darwin-64": + optional: true + "@cloudflare/workerd-darwin-arm64": + optional: true + "@cloudflare/workerd-linux-64": + optional: true + "@cloudflare/workerd-linux-arm64": + optional: true + "@cloudflare/workerd-windows-64": + optional: true + bin: + workerd: bin/workerd + checksum: a0c7af094c05260ed07b1217fd8542789d39fef3e9b6ba4f97cc1902136c1d5a76f4870e2d04789570925761fc8ec65e7ddfbac53f35ec8c5f939e08d128f55b + languageName: node + linkType: hard + "workerpool@npm:6.2.0": version: 6.2.0 resolution: "workerpool@npm:6.2.0" @@ -22016,6 +25993,35 @@ __metadata: languageName: node linkType: hard +"wrangler@npm:3.16.0": + version: 3.16.0 + resolution: "wrangler@npm:3.16.0" + dependencies: + "@cloudflare/kv-asset-handler": ^0.2.0 + "@esbuild-plugins/node-globals-polyfill": ^0.2.3 + "@esbuild-plugins/node-modules-polyfill": ^0.2.2 + blake3-wasm: ^2.1.5 + chokidar: ^3.5.3 + esbuild: 0.17.19 + fsevents: ~2.3.2 + miniflare: 3.20231030.0 + nanoid: ^3.3.3 + path-to-regexp: ^6.2.0 + resolve.exports: ^2.0.2 + selfsigned: ^2.0.1 + source-map: 0.6.1 + source-map-support: 0.5.21 + xxhash-wasm: ^1.0.1 + dependenciesMeta: + fsevents: + optional: true + bin: + wrangler: bin/wrangler.js + wrangler2: bin/wrangler.js + checksum: 2b892b56ab18f4f0e2826c517e3785b959a851ba505edcc7ebe5d535460a52a277b078394e4be99e416aed14a29819ba6039a226ffecef2b0b2b8ce666707be3 + languageName: node + linkType: hard + "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": version: 7.0.0 resolution: "wrap-ansi@npm:7.0.0" @@ -22038,7 +26044,7 @@ __metadata: languageName: node linkType: hard -"wrap-ansi@npm:^6.0.1": +"wrap-ansi@npm:^6.0.1, wrap-ansi@npm:^6.2.0": version: 6.2.0 resolution: "wrap-ansi@npm:6.2.0" dependencies: @@ -22103,7 +26109,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.11.0, ws@npm:^8.14.2, ws@npm:^8.2.3": +"ws@npm:^8.11.0, ws@npm:^8.13.0, ws@npm:^8.14.2, ws@npm:^8.16.0, ws@npm:^8.2.3": version: 8.16.0 resolution: "ws@npm:8.16.0" peerDependencies: @@ -22151,6 +26157,13 @@ __metadata: languageName: node linkType: hard +"xml-name-validator@npm:^2.0.1": + version: 2.0.1 + resolution: "xml-name-validator@npm:2.0.1" + checksum: 648e8950d5abca736d2e77f016bdec06b6a27d8b7c2616590f7e726267c9315611bb2d909d7fd34d55bd88ac6ec0f3b5bfb1c1d4510f3fb19a7397eee6c7e66a + languageName: node + linkType: hard + "xml-name-validator@npm:^4.0.0": version: 4.0.0 resolution: "xml-name-validator@npm:4.0.0" @@ -22203,6 +26216,13 @@ __metadata: languageName: node linkType: hard +"xxhash-wasm@npm:^1.0.1": + version: 1.0.2 + resolution: "xxhash-wasm@npm:1.0.2" + checksum: 11fec6e6196e37ad96cc958b7a4477dc30caf5b4da889a02a84f6f663ab8cd3c9be6ae405e66f0af0404301f27c39375191c5254f0409a793020e2093afd1409 + languageName: node + linkType: hard + "y18n@npm:^4.0.0": version: 4.0.3 resolution: "y18n@npm:4.0.3" @@ -22276,6 +26296,16 @@ __metadata: languageName: node linkType: hard +"yargs-parser@npm:^18.1.2": + version: 18.1.3 + resolution: "yargs-parser@npm:18.1.3" + dependencies: + camelcase: ^5.0.0 + decamelize: ^1.2.0 + checksum: 60e8c7d1b85814594d3719300ecad4e6ae3796748b0926137bfec1f3042581b8646d67e83c6fc80a692ef08b8390f21ddcacb9464476c39bbdf52e34961dd4d9 + languageName: node + linkType: hard + "yargs-parser@npm:^20.2.2": version: 20.2.9 resolution: "yargs-parser@npm:20.2.9" @@ -22335,6 +26365,25 @@ __metadata: languageName: node linkType: hard +"yargs@npm:^15.3.1": + version: 15.4.1 + resolution: "yargs@npm:15.4.1" + dependencies: + cliui: ^6.0.0 + decamelize: ^1.2.0 + find-up: ^4.1.0 + get-caller-file: ^2.0.1 + require-directory: ^2.1.1 + require-main-filename: ^2.0.0 + set-blocking: ^2.0.0 + string-width: ^4.2.0 + which-module: ^2.0.0 + y18n: ^4.0.0 + yargs-parser: ^18.1.2 + checksum: 40b974f508d8aed28598087720e086ecd32a5fd3e945e95ea4457da04ee9bdb8bdd17fd91acff36dc5b7f0595a735929c514c40c402416bbb87c03f6fb782373 + languageName: node + linkType: hard + "yargs@npm:^17.3.1, yargs@npm:^17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" @@ -22383,6 +26432,17 @@ __metadata: languageName: node linkType: hard +"youch@npm:^3.2.2": + version: 3.3.3 + resolution: "youch@npm:3.3.3" + dependencies: + cookie: ^0.5.0 + mustache: ^4.2.0 + stacktracey: ^2.1.8 + checksum: 2b099d8f7b7579ef9d226023037d06c3c03ed6c663e03966d034505e11c026b7ea1359052d5402ded439a9e762cf72449b0db4170e6982ce8be24bbbda4e6b95 + languageName: node + linkType: hard + "z-schema@npm:~5.0.2": version: 5.0.5 resolution: "z-schema@npm:5.0.5" @@ -22409,7 +26469,7 @@ __metadata: languageName: node linkType: hard -"zod@npm:^3.21.4": +"zod@npm:^3.20.6, zod@npm:^3.21.4": version: 3.22.4 resolution: "zod@npm:3.22.4" checksum: 80bfd7f8039b24fddeb0718a2ec7c02aa9856e4838d6aa4864335a047b6b37a3273b191ef335bf0b2002e5c514ef261ffcda5a589fb084a48c336ffc4cdbab7f
+
+ + {'Visit this page on tldraw.com '} + + + + + +
+