├── .changeset └── config.json ├── .cspell └── custom-dictionary-workspace.txt ├── .github └── workflows │ ├── auth-sever-sdb-deploy.yaml │ └── main.yaml ├── .gitignore ├── .vscode ├── settings.json └── tasks.json ├── LICENSE ├── README.md ├── cypress.config.ts ├── e2e └── cypress │ ├── support │ ├── commands.js │ └── e2e.js │ └── tests │ ├── basic.cy.js │ ├── editor.cy.js │ ├── interoperability.cy.js │ ├── local-first.cy.js │ ├── multi-room.cy.js │ └── synced-store.cy.js ├── examples ├── example-basic │ ├── .eslintrc.cjs │ ├── CHANGELOG.md │ ├── LICENSE │ ├── example.env │ ├── index.html │ ├── netlify.toml │ ├── package.json │ ├── src │ │ ├── AppBasic.tsx │ │ ├── config.ts │ │ └── main.tsx │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── vite-env.d.ts │ └── vite.config.ts └── react-native │ ├── .gitignore │ ├── .watchmanconfig │ ├── App.tsx │ ├── CHANGELOG.md │ ├── app.json │ ├── assets │ ├── adaptive-icon.png │ ├── favicon.png │ ├── icon.png │ └── splash.png │ ├── babel.config.js │ ├── package-lock.json │ ├── package.json │ ├── polyfills.ts │ ├── src │ ├── AppReactNative.tsx │ ├── LoginButton.tsx │ ├── StatusBar.tsx │ ├── config.ts │ └── styles.ts │ └── tsconfig.json ├── notes.md ├── old-examples ├── db copy │ ├── .eslintrc.cjs │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── src │ │ ├── collections │ │ │ ├── documentBase.ts │ │ │ ├── flashcard.ts │ │ │ ├── index.ts │ │ │ ├── note.ts │ │ │ └── profile.ts │ │ ├── examples │ │ │ ├── dbShape.ts │ │ │ └── extendingDb.ts │ │ ├── index.test.ts │ │ ├── index.ts │ │ ├── methods │ │ │ ├── addTempDocToRoom.test.ts │ │ │ ├── addTempDocToRoom.ts │ │ │ ├── connectRegistry.test.ts │ │ │ ├── connectRegistry.ts │ │ │ ├── connectRoom.test.ts │ │ │ ├── connectRoom.ts │ │ │ ├── createAndConnectRoom.test.ts │ │ │ ├── createAndConnectRoom.ts │ │ │ ├── createOfflineRoom.test.ts │ │ │ ├── createOfflineRoom.ts │ │ │ ├── deleteRoom.test.ts │ │ │ ├── deleteRoom.ts │ │ │ ├── disconnectRoom.test.ts │ │ │ ├── disconnectRoom.ts │ │ │ ├── getDocumentByRef.test.ts │ │ │ ├── getDocumentByRef.ts │ │ │ ├── getDocuments.test.ts │ │ │ ├── getDocuments.ts │ │ │ ├── load.test.ts │ │ │ ├── load.ts │ │ │ ├── loadAndConnectRoom.test.ts │ │ │ ├── loadAndConnectRoom.ts │ │ │ ├── login.test.ts │ │ │ ├── login.ts │ │ │ ├── logout.test.ts │ │ │ ├── logout.ts │ │ │ ├── on.test.ts │ │ │ ├── on.ts │ │ │ ├── signup.test.ts │ │ │ └── signup.ts │ │ ├── setupTests.ts │ │ ├── test-utils │ │ │ ├── index.ts │ │ │ ├── matrixGuestClient.ts │ │ │ ├── matrixRoomManagement.ts │ │ │ ├── matrixTestUtil.ts │ │ │ └── matrixTestUtilServer.ts │ │ ├── types.ts │ │ └── utils │ │ │ ├── connection │ │ │ ├── aliasHelpers.test.ts │ │ │ ├── aliasHelpers.ts │ │ │ ├── autoReconnect.test.ts │ │ │ ├── autoReconnect.ts │ │ │ ├── awaitOnline.test.ts │ │ │ ├── awaitOnline.ts │ │ │ ├── checkServerConnection.test.ts │ │ │ ├── checkServerConnection.ts │ │ │ ├── connectMatrixProvider.test.ts │ │ │ ├── connectMatrixProvider.ts │ │ │ ├── connectWebRtc.test.ts │ │ │ ├── connectWebRtc.ts │ │ │ ├── createMatricClient.test.ts │ │ │ ├── createMatrixClient.ts │ │ │ ├── createRoom.test.ts │ │ │ ├── createRoom.ts │ │ │ ├── getOrCreateRegistryAndSpace.test.ts │ │ │ ├── getOrCreateRegistryAndSpace.ts │ │ │ ├── getRoomId.ts │ │ │ ├── index.ts │ │ │ ├── initializeDoc.test.ts │ │ │ ├── initializeDoc.ts │ │ │ ├── joinRoomIfNotJoined.ts │ │ │ ├── loadRoom.test.ts │ │ │ ├── loadRoom.ts │ │ │ ├── newMatrixProvider.ts │ │ │ ├── pingServer.test.ts │ │ │ ├── pingServer.ts │ │ │ ├── pollConnection.test.ts │ │ │ ├── pollConnection.ts │ │ │ ├── populateRegistry.test.ts │ │ │ ├── populateRegistry.ts │ │ │ ├── registerRoomToSpace.ts │ │ │ └── saveRoomToRegistry.ts │ │ │ ├── db │ │ │ ├── buildRef.test.ts │ │ │ ├── buildRef.ts │ │ │ ├── index.ts │ │ │ ├── localStorageService.ts │ │ │ ├── newDocument.ts │ │ │ ├── newEmptyRoom.test.ts │ │ │ ├── newEmptyRoom.ts │ │ │ ├── parseRef.test.ts │ │ │ ├── parseRef.ts │ │ │ ├── populateInitialValues.ts │ │ │ ├── validation.test.ts │ │ │ └── validation.ts │ │ │ ├── index.test.ts │ │ │ └── index.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── vite.config.js ├── example-editor │ ├── .eslintrc.cjs │ ├── LICENSE │ ├── example.env │ ├── index.html │ ├── netlify.toml │ ├── package.json │ ├── src │ │ ├── AppEditor.tsx │ │ ├── Editor.tsx │ │ ├── config.ts │ │ └── main.tsx │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── vite-env.d.ts │ └── vite.config.ts ├── example-interop-flashcards │ ├── .eslintrc.cjs │ ├── LICENSE │ ├── example.env │ ├── index.html │ ├── netlify.toml │ ├── package.json │ ├── src │ │ ├── AppInteropFlashcards.tsx │ │ ├── config.ts │ │ └── main.tsx │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── vite-env.d.ts │ └── vite.config.ts ├── example-interop-notes │ ├── .eslintrc.cjs │ ├── LICENSE │ ├── example.env │ ├── index.html │ ├── netlify.toml │ ├── package.json │ ├── src │ │ ├── AppInteropNotes.tsx │ │ ├── config.ts │ │ └── main.tsx │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── vite-env.d.ts │ └── vite.config.ts ├── example-multi-room │ ├── .eslintrc.cjs │ ├── LICENSE │ ├── example.env │ ├── index.html │ ├── netlify.toml │ ├── package.json │ ├── src │ │ ├── AppMultiRoom.tsx │ │ ├── config.ts │ │ └── main.tsx │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── vite-env.d.ts │ └── vite.config.ts ├── example-offline-first │ ├── .eslintrc.cjs │ ├── LICENSE │ ├── example.env │ ├── index.html │ ├── netlify.toml │ ├── package.json │ ├── src │ │ ├── AppOfflineFirst.tsx │ │ ├── config.ts │ │ └── main.tsx │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── vite-env.d.ts │ └── vite.config.ts └── example-synced-store │ ├── .eslintrc.cjs │ ├── LICENSE │ ├── example.env │ ├── index.html │ ├── netlify.toml │ ├── package.json │ ├── src │ ├── AppSyncedStore.tsx │ ├── config.ts │ └── main.tsx │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── vite-env.d.ts │ └── vite.config.ts ├── package-lock.json ├── package.json ├── packages ├── aggregator │ ├── .env-example │ ├── .eslintrc.cjs │ ├── .npmrc │ ├── CHANGELOG.md │ ├── README.md │ ├── docker-compose-mongo.yml │ ├── nixpacks.toml │ ├── package.json │ ├── src │ │ ├── api │ │ │ └── join-room-get.ts │ │ ├── constants.ts │ │ ├── helpers.ts │ │ ├── main.test.ts │ │ ├── main.ts │ │ ├── matrix.ts │ │ ├── mongo-helpers.ts │ │ ├── rooms.ts │ │ ├── seed.ts │ │ └── server.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── vitest.config.ts ├── auth-server │ ├── .eslintrc.json │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── components.json │ ├── docs │ │ └── images │ │ │ ├── enable-provider.png │ │ │ └── site-url.png │ ├── example.env │ ├── next.config.mjs │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ ├── eweser-db-logo-head.png │ │ ├── eweser-db-logo-head.svg │ │ ├── eweser-db-logo-small.svg │ │ ├── eweser-db-logo.png │ │ ├── eweser-db-logo.svg │ │ └── favicon.ico │ ├── src │ │ ├── app │ │ │ ├── access-grant │ │ │ │ ├── accept-room-invite │ │ │ │ │ └── page.tsx │ │ │ │ ├── create-room-invite │ │ │ │ │ └── route.ts │ │ │ │ ├── get-rooms │ │ │ │ │ └── route.ts │ │ │ │ ├── permission │ │ │ │ │ ├── actions.tsx │ │ │ │ │ └── page.tsx │ │ │ │ ├── ping │ │ │ │ │ └── route.ts │ │ │ │ ├── refresh-y-sweet-token │ │ │ │ │ └── [roomId] │ │ │ │ │ │ └── route.ts │ │ │ │ ├── sync-registry │ │ │ │ │ └── route.ts │ │ │ │ └── update-room │ │ │ │ │ └── [roomId] │ │ │ │ │ └── route.ts │ │ │ ├── actions.ts │ │ │ ├── auth │ │ │ │ ├── await-confirm │ │ │ │ │ └── page.tsx │ │ │ │ ├── callback │ │ │ │ │ ├── [next] │ │ │ │ │ │ └── route.ts │ │ │ │ │ └── route.ts │ │ │ │ ├── confirm │ │ │ │ │ └── route.ts │ │ │ │ └── sign-out │ │ │ │ │ └── page.tsx │ │ │ ├── error │ │ │ │ └── page.tsx │ │ │ ├── home │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── page.tsx │ │ │ └── statement │ │ │ │ ├── privacy │ │ │ │ └── page.tsx │ │ │ │ └── terms-of-service │ │ │ │ └── page.tsx │ │ ├── frontend │ │ │ ├── components │ │ │ │ ├── access-grant │ │ │ │ │ ├── accepting-invite-loading.tsx │ │ │ │ │ ├── permission-form-accordion.tsx │ │ │ │ │ ├── permission-form.tsx │ │ │ │ │ └── permissions-unchanged-redirect.tsx │ │ │ │ ├── auth │ │ │ │ │ ├── oauth-form.tsx │ │ │ │ │ ├── password-form.tsx │ │ │ │ │ └── user-auth-form.tsx │ │ │ │ ├── landing-page-hero.tsx │ │ │ │ ├── library │ │ │ │ │ ├── accordion.tsx │ │ │ │ │ ├── badge.tsx │ │ │ │ │ ├── button.tsx │ │ │ │ │ ├── card.tsx │ │ │ │ │ ├── checkbox.tsx │ │ │ │ │ ├── form.tsx │ │ │ │ │ ├── icons.tsx │ │ │ │ │ ├── input.tsx │ │ │ │ │ ├── label.tsx │ │ │ │ │ ├── select.tsx │ │ │ │ │ ├── skeleton.tsx │ │ │ │ │ ├── typography-blockquote.tsx │ │ │ │ │ ├── typography-h1.tsx │ │ │ │ │ ├── typography-h2.tsx │ │ │ │ │ ├── typography-h3.tsx │ │ │ │ │ ├── typography-h4.tsx │ │ │ │ │ ├── typography-inline-code.tsx │ │ │ │ │ ├── typography-large.tsx │ │ │ │ │ ├── typography-lead.tsx │ │ │ │ │ ├── typography-list.tsx │ │ │ │ │ ├── typography-muted.tsx │ │ │ │ │ ├── typography-p.tsx │ │ │ │ │ └── typography-small.tsx │ │ │ │ ├── main-nav.tsx │ │ │ │ ├── profile │ │ │ │ │ ├── profile-view-frontend.tsx │ │ │ │ │ └── profile-view.tsx │ │ │ │ ├── site-header.tsx │ │ │ │ └── theme-toggle.tsx │ │ │ ├── config │ │ │ │ └── site.ts │ │ │ ├── eweser-db-provider.tsx │ │ │ ├── styles │ │ │ │ ├── fonts.ts │ │ │ │ └── globals.css │ │ │ ├── theme-provider.tsx │ │ │ ├── utils.ts │ │ │ └── utils │ │ │ │ ├── debounce.ts │ │ │ │ ├── frontend-url-utils.ts │ │ │ │ └── local-storage.ts │ │ ├── middleware.ts │ │ ├── model │ │ │ ├── access_grants.ts │ │ │ ├── apps.ts │ │ │ ├── rooms │ │ │ │ ├── calls.ts │ │ │ │ ├── helpers.ts │ │ │ │ ├── schema.ts │ │ │ │ └── validation.ts │ │ │ └── users.ts │ │ ├── modules │ │ │ ├── account │ │ │ │ ├── access-grant │ │ │ │ │ ├── accept-room-invite.ts │ │ │ │ │ ├── create-room-invite-link.ts │ │ │ │ │ ├── create-third-party-app-permissions.ts │ │ │ │ │ ├── create-token-from-grant.ts │ │ │ │ │ └── roomNotFoundError.ts │ │ │ │ ├── create-new-user-rooms-and-auth-server-access.ts │ │ │ │ ├── get-login-status.ts │ │ │ │ ├── get-session-user.ts │ │ │ │ ├── oauth │ │ │ │ │ ├── hooks │ │ │ │ │ │ └── use-oauth-login.ts │ │ │ │ │ └── login-callback.ts │ │ │ │ ├── password │ │ │ │ │ ├── check-email.ts │ │ │ │ │ ├── sign-in.ts │ │ │ │ │ └── sign-up.ts │ │ │ │ ├── protect-page.ts │ │ │ │ └── verify-otp.ts │ │ │ └── rooms │ │ │ │ ├── get-or-create-rooms.ts │ │ │ │ ├── refresh-token-save-to-room.ts │ │ │ │ └── sync-rooms-with-client.ts │ │ ├── services │ │ │ ├── database │ │ │ │ ├── drizzle │ │ │ │ │ └── init.ts │ │ │ │ ├── index.ts │ │ │ │ └── supabase │ │ │ │ │ ├── backend-client-init.ts │ │ │ │ │ ├── backend-config.ts │ │ │ │ │ ├── frontend-client-init.ts │ │ │ │ │ ├── frontend-config.ts │ │ │ │ │ ├── middleware-client-init.ts │ │ │ │ │ └── middleware.ts │ │ │ └── y-sweet │ │ │ │ ├── config.ts │ │ │ │ └── get-or-create-token.ts │ │ └── shared │ │ │ ├── constants.ts │ │ │ ├── server-constants.ts │ │ │ └── utils.ts │ ├── supabase │ │ ├── .gitignore │ │ ├── config.toml │ │ ├── migrations │ │ │ ├── 20240213053705_remote_schema.sql │ │ │ ├── 20240213144643_public_users_table.sql │ │ │ ├── 20240213144645_user_trigger.sql │ │ │ ├── 20240213145345_config_table.sql │ │ │ ├── 20240213145912_rooms_table.sql │ │ │ ├── 20240213154147_access_grants_table.sql │ │ │ ├── 20240213161126_fix_ids_should_be_text_not_uuid_and_finish_access_grants.sql │ │ │ ├── 20240214082500_add_creator_id_to_room.sql │ │ │ ├── 20240214131046_remove_doc_id_from_room.sql │ │ │ ├── 20240215112831_fix_up_ids.sql │ │ │ ├── 20240215144645_user_trigger_update.sql │ │ │ ├── 20240215150955_add default rooms array.sql │ │ │ ├── 20240215170446_save_ysweet_url_to_room.sql │ │ │ ├── 20240216021805_link_user_id_to_auth_id.sql │ │ │ ├── 20240216170712_edit_access_grants_table.sql │ │ │ ├── 20240216171937_edit_access_grants_table_expires_keys.sql │ │ │ ├── 20240217103308_add_owner_id_to_access_grant.sql │ │ │ ├── 20240220154616_add_room_deleted_flag.sql │ │ │ ├── 20240222135756__deleted_nullable.sql │ │ │ ├── 20240301131143_drop_config_table.sql │ │ │ ├── 20240301131302_create_apps_table.sql │ │ │ ├── 20240313144238_add_token_expiry_column.sql │ │ │ └── 20241216151304_change-y-sweet-token-columns.sql │ │ └── seed.sql │ ├── tailwind.config.ts │ ├── tsconfig.json │ └── vercel.json ├── db │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── dist │ │ ├── db.js │ │ └── db.umd.cjs │ ├── package.json │ ├── src │ │ ├── events.ts │ │ ├── examples │ │ │ └── dbShape.ts │ │ ├── index.ts │ │ ├── methods │ │ │ ├── connection │ │ │ │ ├── generateLoginUrl.ts │ │ │ │ ├── generateShareRoomLink.ts │ │ │ │ ├── getAccessGrantTokenFromUrl.ts │ │ │ │ ├── getToken.ts │ │ │ │ ├── loadRoom.ts │ │ │ │ ├── loadRooms.ts │ │ │ │ ├── login.ts │ │ │ │ ├── logout.ts │ │ │ │ ├── refreshYSweetToken.ts │ │ │ │ └── syncRegistry.ts │ │ │ ├── getRegistry.ts │ │ │ ├── log.ts │ │ │ └── newRoom.ts │ │ ├── room.ts │ │ ├── setupTests.ts │ │ ├── types.ts │ │ └── utils │ │ │ ├── connection │ │ │ ├── checkServerConnection.ts │ │ │ ├── initializeDoc.ts │ │ │ ├── pingServer.ts │ │ │ ├── pollConnection.ts │ │ │ └── serverFetch.ts │ │ │ ├── getDocuments.ts │ │ │ ├── index.ts │ │ │ ├── localStorageService.ts │ │ │ └── yjsWrapper.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ ├── types │ │ ├── events.d.ts │ │ ├── index.d.ts │ │ ├── methods │ │ │ ├── connection │ │ │ │ ├── generateLoginUrl.d.ts │ │ │ │ ├── generateShareRoomLink.d.ts │ │ │ │ ├── getAccessGrantTokenFromUrl.d.ts │ │ │ │ ├── getToken.d.ts │ │ │ │ ├── loadRoom.d.ts │ │ │ │ ├── loadRooms.d.ts │ │ │ │ ├── login.d.ts │ │ │ │ ├── logout.d.ts │ │ │ │ ├── refreshYSweetToken.d.ts │ │ │ │ └── syncRegistry.d.ts │ │ │ ├── getRegistry.d.ts │ │ │ ├── log.d.ts │ │ │ └── newRoom.d.ts │ │ ├── room.d.ts │ │ ├── types.d.ts │ │ └── utils │ │ │ ├── connection │ │ │ ├── checkServerConnection.d.ts │ │ │ ├── initializeDoc.d.ts │ │ │ ├── pingServer.d.ts │ │ │ ├── pollConnection.d.ts │ │ │ └── serverFetch.d.ts │ │ │ ├── getDocuments.d.ts │ │ │ ├── index.d.ts │ │ │ ├── localStorageService.d.ts │ │ │ └── yjsWrapper.d.ts │ └── vite.config.js ├── eslint-config-react-ts │ ├── .eslintrc.js │ ├── CHANGELOG.md │ ├── LICENSE │ ├── Test.tsx │ ├── index.js │ ├── package.json │ └── tsconfig.json ├── eslint-config-ts │ ├── .eslintrc.cjs │ ├── CHANGELOG.md │ ├── LICENSE │ ├── index.js │ └── package.json ├── examples-components │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── CHANGELOG.md │ ├── LICENSE │ ├── index.html │ ├── package.json │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── components │ │ │ ├── EweserIcon.tsx │ │ │ ├── LoginButton.tsx │ │ │ ├── StatusBar.tsx │ │ │ ├── helpers.tsx │ │ │ ├── index.ts │ │ │ └── styles.ts │ │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts └── shared │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── CHANGELOG.md │ ├── dist │ ├── api │ │ ├── accept-room-invite.js │ │ ├── accept-room-invite.js.map │ │ ├── create-room-invite.js │ │ ├── create-room-invite.js.map │ │ ├── index.js │ │ ├── index.js.map │ │ ├── login-queries.js │ │ ├── login-queries.js.map │ │ ├── refresh-y-sweet-token.js │ │ ├── refresh-y-sweet-token.js.map │ │ ├── registry-sync.js │ │ ├── registry-sync.js.map │ │ ├── update-room.js │ │ └── update-room.js.map │ ├── collections │ │ ├── documentBase.js │ │ ├── documentBase.js.map │ │ ├── flashcard.js │ │ ├── flashcard.js.map │ │ ├── index.js │ │ ├── index.js.map │ │ ├── note.js │ │ ├── note.js.map │ │ ├── profile.js │ │ └── profile.js.map │ ├── index.js │ ├── index.js.map │ └── utils │ │ ├── index.js │ │ └── index.js.map │ ├── package.json │ ├── src │ ├── api │ │ ├── accept-room-invite.ts │ │ ├── create-room-invite.ts │ │ ├── index.ts │ │ ├── login-queries.ts │ │ ├── refresh-y-sweet-token.ts │ │ ├── registry-sync.ts │ │ └── update-room.ts │ ├── collections │ │ ├── documentBase.ts │ │ ├── flashcard.ts │ │ ├── index.ts │ │ ├── note.ts │ │ └── profile.ts │ ├── index.ts │ └── utils │ │ ├── index.test.ts │ │ └── index.ts │ ├── tsconfig.json │ └── types │ ├── api │ ├── accept-room-invite.d.ts │ ├── create-room-invite.d.ts │ ├── index.d.ts │ ├── login-queries.d.ts │ ├── refresh-y-sweet-token.d.ts │ ├── registry-sync.d.ts │ └── update-room.d.ts │ ├── collections │ ├── documentBase.d.ts │ ├── flashcard.d.ts │ ├── index.d.ts │ ├── note.d.ts │ └── profile.d.ts │ ├── index.d.ts │ └── utils │ └── index.d.ts ├── scripts ├── create-default-changeset.js └── release.js ├── test-rpc-server ├── package-lock.json ├── package.json └── server.js └── tsconfig.json /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.0.5/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "restricted", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } 12 | -------------------------------------------------------------------------------- /.cspell/custom-dictionary-workspace.txt: -------------------------------------------------------------------------------- 1 | Eweser 2 | -------------------------------------------------------------------------------- /.github/workflows/auth-sever-sdb-deploy.yaml: -------------------------------------------------------------------------------- 1 | name: Deploy Migrations to Auth Server Production 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-latest 12 | 13 | env: 14 | SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} 15 | SUPABASE_DB_PASSWORD: ${{ secrets.PRODUCTION_DB_PASSWORD }} 16 | SUPABASE_PROJECT_ID: ${{ secrets.PRODUCTION_PROJECT_ID }} 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | 21 | - uses: supabase/setup-cli@v1 22 | with: 23 | version: latest 24 | 25 | - run: cd packages/auth-server; supabase link --project-ref=$SUPABASE_PROJECT_ID 26 | - run: cd packages/auth-server; supabase db push 27 | -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | name: Release Workflow 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | release: 10 | name: Create and Publish Release 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | # Step 1: Checkout code 15 | - name: Checkout repository 16 | uses: actions/checkout@v3 17 | 18 | # Step 2: Set up Node.js 19 | - name: Set up Node.js 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: 20 23 | cache: npm 24 | 25 | # Step 3: Authenticate with npm 26 | - name: Authenticate with npm 27 | run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc 28 | 29 | # Step 4: Install dependencies 30 | - name: Install dependencies 31 | run: npm ci 32 | 33 | # Step 5: Create a default changeset if none exists 34 | - name: Create default changeset (if needed) 35 | run: npm run default-changeset 36 | 37 | # Step 6: Run the release script 38 | - name: Run release script 39 | env: 40 | CI: true 41 | run: npm run release 42 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": ["supabase"], 3 | "cSpell.customDictionaries": { 4 | "custom-dictionary-workspace": { 5 | "name": "custom-dictionary-workspace", 6 | "path": "${workspaceFolder:eweser-db}/.cspell/custom-dictionary-workspace.txt", 7 | "addWords": true, 8 | "scope": "workspace" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 EduVault 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | export default defineConfig({ 4 | projectId: 'obyc2w', 5 | fixturesFolder: 'e2e/cypress/fixtures', 6 | screenshotsFolder: 'e2e/cypress/screenshots', 7 | videosFolder: 'e2e/cypress/videos', 8 | e2e: { 9 | supportFile: 'e2e/cypress/support/e2e.js', 10 | specPattern: 'e2e/cypress/**/*.cy.{js,jsx,ts,tsx}', 11 | baseUrl: 'http://localhost:8000', 12 | setupNodeEvents(on, config) { 13 | // implement node event listeners here 14 | }, 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /e2e/cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | import '@testing-library/cypress/add-commands'; 2 | export {}; 3 | -------------------------------------------------------------------------------- /e2e/cypress/support/e2e.js: -------------------------------------------------------------------------------- 1 | import './commands'; 2 | 3 | export {}; 4 | -------------------------------------------------------------------------------- /examples/example-basic/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@eweser/eslint-config-react-ts'], 3 | ignorePatterns: [ 4 | 'vite.config.*', 5 | 'types/**/*.d.ts', 6 | 'dist', 7 | 'setupTests.ts', 8 | '.eslintrc.cjs', 9 | ], 10 | }; 11 | -------------------------------------------------------------------------------- /examples/example-basic/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @eweser/example-basic 2 | 3 | ## 1.14.0 4 | 5 | ### Minor Changes 6 | 7 | - align versions 8 | 9 | ### Patch Changes 10 | 11 | - Updated dependencies 12 | - @eweser/db@1.14.0 13 | - @eweser/examples-components@2.0.0 14 | 15 | ## 1.13.5 16 | 17 | ### Patch Changes 18 | 19 | - bump 20 | - Updated dependencies 21 | - @eweser/db@1.13.6 22 | - @eweser/examples-components@1.13.6 23 | 24 | ## 1.13.4 25 | 26 | ### Patch Changes 27 | 28 | - registry sync 29 | - Updated dependencies 30 | - @eweser/db@1.13.5 31 | - @eweser/examples-components@1.13.5 32 | 33 | ## 1.13.3 34 | 35 | ### Patch Changes 36 | 37 | - add rolling sync 38 | - Updated dependencies 39 | - @eweser/db@1.13.4 40 | - @eweser/examples-components@1.13.4 41 | -------------------------------------------------------------------------------- /examples/example-basic/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 EduVault 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /examples/example-basic/example.env: -------------------------------------------------------------------------------- 1 | VITE_DEV_USERNAME=my-test-user 2 | VITE_DEV_PASSWORD=my-test-password123! 3 | VITE_AUTH_SERVER=http://localhost:8888 4 | -------------------------------------------------------------------------------- /examples/example-basic/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Eweser DB Example App 7 | 10 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /examples/example-basic/netlify.toml: -------------------------------------------------------------------------------- 1 | [[redirects]] 2 | from = "/*" 3 | to = "/index.html" 4 | status = 200 -------------------------------------------------------------------------------- /examples/example-basic/src/config.ts: -------------------------------------------------------------------------------- 1 | const dummyUserName = 'dummy-user123'; 2 | const dummyUserPass = 'dumdum'; 3 | 4 | export const AUTH_SERVER = 5 | import.meta.env.VITE_AUTH_SERVER ?? 'http://172.31.42.92:3000'; // 'https://eweser.com'; 6 | 7 | export const env = 8 | import.meta.env.VITE_CI === 'true' 9 | ? 'ci' 10 | : import.meta.env.DEV 11 | ? 'dev' 12 | : 'prod'; 13 | 14 | export const dev = env === 'dev'; 15 | export const ci = env === 'ci'; 16 | 17 | export const showSignup = env !== 'prod'; 18 | 19 | export const DEV_USERNAME = 20 | import.meta.env.VITE_DEV_USERNAME ?? dev ? dummyUserName : ''; 21 | export const DEV_PASSWORD = 22 | import.meta.env.VITE_DEV_PASSWORD ?? dev ? dummyUserPass : ''; 23 | 24 | const localWebRtcServer = 'ws://localhost:4444'; 25 | export const WEB_RTC_PEERS = dev || ci ? [localWebRtcServer] : undefined; 26 | -------------------------------------------------------------------------------- /examples/example-basic/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './AppBasic'; 4 | 5 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 6 | 7 | 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /examples/example-basic/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "baseUrl": "./src" 19 | }, 20 | 21 | "include": ["vite.config.ts", ".eslintrc.cjs", "src", "vite-env.d.ts"], 22 | "exclude": ["vite.config.ts"], 23 | "references": [{ "path": "./tsconfig.node.json" }] 24 | } 25 | -------------------------------------------------------------------------------- /examples/example-basic/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "exclude": ["vite.config.ts"], 9 | "include": ["vite.config.ts", "vite-env.d.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /examples/example-basic/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/example-basic/vite.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { defineConfig } from 'vite'; 3 | import react from '@vitejs/plugin-react'; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react()], 8 | 9 | build: { 10 | minify: false, 11 | }, 12 | define: { 13 | global: 'window', 14 | }, 15 | preview: { 16 | port: 8000, 17 | }, 18 | server: { 19 | port: 8000, 20 | }, 21 | }); 22 | -------------------------------------------------------------------------------- /examples/react-native/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules/ 3 | 4 | # Expo 5 | .expo/ 6 | dist/ 7 | web-build/ 8 | .idea/ 9 | android/ 10 | ios/ 11 | 12 | # Native 13 | *.orig.* 14 | *.jks 15 | *.p8 16 | *.p12 17 | *.key 18 | *.mobileprovision 19 | 20 | # Metro 21 | .metro-health-check* 22 | 23 | # debug 24 | npm-debug.* 25 | yarn-debug.* 26 | yarn-error.* 27 | 28 | # macOS 29 | .DS_Store 30 | *.pem 31 | 32 | # local env files 33 | .env*.local 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | -------------------------------------------------------------------------------- /examples/react-native/.watchmanconfig: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /examples/react-native/App.tsx: -------------------------------------------------------------------------------- 1 | import './polyfills'; 2 | 3 | import Main from './src/AppReactNative'; 4 | 5 | export default function App() { 6 | return
; 7 | } 8 | -------------------------------------------------------------------------------- /examples/react-native/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @eweser/example-react-native 2 | 3 | ## 1.14.0 4 | 5 | ### Minor Changes 6 | 7 | - align versions 8 | 9 | ### Patch Changes 10 | 11 | - Updated dependencies 12 | - @eweser/db@1.14.0 13 | 14 | ## 1.13.5 15 | 16 | ### Patch Changes 17 | 18 | - bump 19 | - Updated dependencies 20 | - @eweser/db@1.13.6 21 | 22 | ## 1.13.4 23 | 24 | ### Patch Changes 25 | 26 | - registry sync 27 | - Updated dependencies 28 | - @eweser/db@1.13.5 29 | 30 | ## 1.13.3 31 | 32 | ### Patch Changes 33 | 34 | - add rolling sync 35 | - Updated dependencies 36 | - @eweser/db@1.13.4 37 | -------------------------------------------------------------------------------- /examples/react-native/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "packagerOpts": { 4 | "sourceExts": ["js", "json", "ts", "tsx", "cjs"] 5 | }, 6 | "name": "ewe-note-native", 7 | "slug": "ewe-note-native", 8 | "version": "1.0.0", 9 | "orientation": "portrait", 10 | "icon": "./assets/icon.png", 11 | "userInterfaceStyle": "light", 12 | "splash": { 13 | "image": "./assets/splash.png", 14 | "resizeMode": "contain", 15 | "backgroundColor": "#ffffff" 16 | }, 17 | "assetBundlePatterns": ["**/*"], 18 | "ios": { 19 | "supportsTablet": true, 20 | "bundleIdentifier": "com.jacobcoro.ewenotenativey" 21 | }, 22 | "android": { 23 | "adaptiveIcon": { 24 | "foregroundImage": "./assets/adaptive-icon.png", 25 | "backgroundColor": "#ffffff" 26 | }, 27 | "package": "com.jacobcoro.ewenotenative" 28 | }, 29 | "web": { 30 | "favicon": "./assets/favicon.png" 31 | }, 32 | "extra": { 33 | "eas": { 34 | "projectId": "7d8d1b5c-17f9-428e-9c35-29d3b332f5a5" 35 | } 36 | }, 37 | "owner": "jacobcoro" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/react-native/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eweser/eweser-db/00565867c8c2f36cda7dea91e9247915783a497a/examples/react-native/assets/adaptive-icon.png -------------------------------------------------------------------------------- /examples/react-native/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eweser/eweser-db/00565867c8c2f36cda7dea91e9247915783a497a/examples/react-native/assets/favicon.png -------------------------------------------------------------------------------- /examples/react-native/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eweser/eweser-db/00565867c8c2f36cda7dea91e9247915783a497a/examples/react-native/assets/icon.png -------------------------------------------------------------------------------- /examples/react-native/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eweser/eweser-db/00565867c8c2f36cda7dea91e9247915783a497a/examples/react-native/assets/splash.png -------------------------------------------------------------------------------- /examples/react-native/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /examples/react-native/polyfills.ts: -------------------------------------------------------------------------------- 1 | import { polyfillWebCrypto } from 'expo-standard-web-crypto'; 2 | 3 | polyfillWebCrypto(); 4 | 5 | import SyncStorage from 'sync-storage'; 6 | 7 | export const localStoragePolyfill = { 8 | getItem: (key: string) => { 9 | const test = SyncStorage.get(key); 10 | console.log({ test }); 11 | return test as any; 12 | }, 13 | setItem: (key: string, value: string) => { 14 | const set = SyncStorage.set(key, value).then(console.log); 15 | console.log({ set }); 16 | }, 17 | removeItem: (key: string) => SyncStorage.remove(key), 18 | length: SyncStorage.getAllKeys().length, 19 | clear: () => 20 | SyncStorage.getAllKeys().forEach((key) => SyncStorage.remove(key)), 21 | key: (index: number) => SyncStorage.getAllKeys()[index], 22 | }; 23 | -------------------------------------------------------------------------------- /examples/react-native/src/config.ts: -------------------------------------------------------------------------------- 1 | const dummyUserName = 'dummy-user123'; 2 | const dummyUserPass = 'dumdum'; 3 | 4 | export const AUTH_SERVER = 5 | process.env.VITE_AUTH_SERVER ?? 'http://172.31.42.92:3000'; 6 | 7 | export const env = 8 | process.env.VITE_CI === 'true' ? 'ci' : process.env.DEV ? 'dev' : 'prod'; 9 | 10 | export const dev = env === 'dev'; 11 | export const ci = env === 'ci'; 12 | 13 | export const showSignup = env !== 'prod'; 14 | 15 | export const DEV_USERNAME = 16 | process.env.VITE_DEV_USERNAME ?? dev ? dummyUserName : ''; 17 | export const DEV_PASSWORD = 18 | process.env.VITE_DEV_PASSWORD ?? dev ? dummyUserPass : ''; 19 | 20 | const localWebRtcServer = 'ws://localhost:4444'; 21 | export const WEB_RTC_PEERS = dev || ci ? [localWebRtcServer] : undefined; 22 | -------------------------------------------------------------------------------- /examples/react-native/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo/tsconfig.base", 3 | "compilerOptions": { 4 | "strict": true 5 | } 6 | } -------------------------------------------------------------------------------- /notes.md: -------------------------------------------------------------------------------- 1 | # Notes 2 | 3 | ## TODO 4 | 5 | - [ ] 6 | 7 | ## Ideas / musings 8 | 9 | - Should we make a separate pingger, and online status with listeners for webrtc? so `db.rtcOnline` and `db.emit({event: 'rtcOnlineChange' })` etc. 10 | - `getCollectionRoom` could return a class with some helpers that manipulate the ydoc. e.g. `getCollectionRoom('collection-name').getDoc('doc-id).update({title: 'new title'})` to make crud ops easier. 11 | - set up links in the markdown. e.g. `[[collection-name:doc-id]]`. but how would we intercept the link click and distinguish it from a normal link? 12 | - check out what synced store is doing behind the hood and see if just a simple hook that updates the state on ydoc change would work just as well. 13 | - use a dendrite server with wasm for even stronger user ownership of their data. NOTE: not possible yet, but keep an eye out. 14 | -------------------------------------------------------------------------------- /old-examples/db copy/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@eweser/eslint-config-ts'], 3 | ignorePatterns: [ 4 | 'vite.config.*', 5 | 'types/**/*.d.ts', 6 | 'dist', 7 | 'setupTests.ts', 8 | '.eslintrc.cjs', 9 | ], 10 | }; 11 | -------------------------------------------------------------------------------- /old-examples/db copy/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 EduVault 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /old-examples/db copy/src/collections/documentBase.ts: -------------------------------------------------------------------------------- 1 | export type DocumentBase = { 2 | /** 3 | * @example .. == `notes.5.1` 4 | */ 5 | _ref: string; 6 | /** uuid, matches outer id */ 7 | _id: string; 8 | /** epoch time created with new Date().getTime() */ 9 | _created: number; 10 | /** epoch time updated with new Date().getTime() */ 11 | _updated: number; 12 | _deleted?: boolean; 13 | /** time to live. an epoch time set when deleted flag is set. recommend one month from now 14 | * new Date().getTime() + 1000 * 60 * 60 * 24 * 30 15 | */ 16 | _ttl?: number; 17 | }; 18 | -------------------------------------------------------------------------------- /old-examples/db copy/src/collections/flashcard.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentBase } from './documentBase'; 2 | 3 | export type FlashcardBase = { 4 | frontText: string; 5 | backText: string; 6 | noteRefs?: string[]; 7 | }; 8 | export type Flashcard = DocumentBase & FlashcardBase; 9 | -------------------------------------------------------------------------------- /old-examples/db copy/src/collections/index.ts: -------------------------------------------------------------------------------- 1 | export * from './note'; 2 | export * from './flashcard'; 3 | export * from './profile'; 4 | export * from './documentBase'; 5 | 6 | /** We don't include registry because we use this after login to get all non-registry collections. */ 7 | export const COLLECTION_KEYS = ['notes', 'flashcards', 'profiles'] as const; 8 | -------------------------------------------------------------------------------- /old-examples/db copy/src/collections/note.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentBase } from './documentBase'; 2 | 3 | export type NoteBase = { 4 | text: string; 5 | flashcardRefs?: string[]; 6 | }; 7 | 8 | export type Note = DocumentBase & NoteBase; 9 | -------------------------------------------------------------------------------- /old-examples/db copy/src/collections/profile.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentBase } from './documentBase'; 2 | 3 | export type ProfileBase = { 4 | firstName?: any; 5 | lastName?: string; 6 | avatarUrl?: string; 7 | }; 8 | 9 | export type Profile = DocumentBase & ProfileBase; 10 | -------------------------------------------------------------------------------- /old-examples/db copy/src/examples/extendingDb.ts: -------------------------------------------------------------------------------- 1 | import { Database } from '..'; 2 | 3 | // To make interoperable collections submit a pull request with the following changes: 4 | // 1. Add collection model to collections/your-collection.ts 5 | // 2. In collections/index.ts add your collection to: 6 | // `CollectionKey` enum 7 | // `collectionKeys` array 8 | // `Collections` interface 9 | // `collections` object 10 | 11 | // to extend the DB locally: 12 | const db = new Database(); 13 | 14 | //@ts-ignore 15 | db.collections['newCollection'] = {}; 16 | //@ts-ignore 17 | db.collectionKeys.push('newCollection'); 18 | 19 | //TODO: make example ts shims. 20 | -------------------------------------------------------------------------------- /old-examples/db copy/src/methods/connectRegistry.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect, beforeAll } from 'vitest'; 2 | import { Database } from '..'; 3 | import { baseUrl, userLoginInfo } from '../test-utils'; 4 | import { loginToMatrix } from './login'; 5 | import { ensureMatrixIsRunning } from '../test-utils/matrixTestUtilServer'; 6 | import { createMatrixUser } from '../test-utils/matrixTestUtil'; 7 | 8 | const loginInfo = userLoginInfo(); 9 | const { userId, password } = loginInfo; 10 | beforeAll(async () => { 11 | await ensureMatrixIsRunning(); 12 | await createMatrixUser(userId, password); 13 | }, 60000); 14 | 15 | describe('connectRegistry', () => { 16 | ensureMatrixIsRunning(); 17 | it('can connect, or create registry', async () => { 18 | const db = new Database({ baseUrl }); 19 | await loginToMatrix(db, loginInfo); 20 | await db.connectRegistry(); 21 | const registryRoom = db.collections.registry[0]; 22 | expect(registryRoom.ydoc?.store).toBeDefined(); 23 | expect(registryRoom.connectStatus).toEqual('ok'); 24 | expect(registryRoom.matrixProvider?.roomId).toEqual(registryRoom.roomId); 25 | }, 10000); 26 | }); 27 | -------------------------------------------------------------------------------- /old-examples/db copy/src/methods/createOfflineRoom.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | 3 | import { Database, randomString } from '..'; 4 | import { CollectionKey } from '../types'; 5 | 6 | describe('createOfflineRoom', () => { 7 | it('should create a room', async () => { 8 | const roomId = 'test' + randomString(12); 9 | const collectionKey = 'flashcards'; 10 | const db = new Database({ offlineOnly: true }); 11 | const room = await db.createOfflineRoom({ roomId, collectionKey }); 12 | expect(room?.roomId).toEqual(`#${roomId}~${collectionKey}~${db.userId}`); 13 | expect(room?.ydoc).toBeDefined(); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /old-examples/db copy/src/methods/deleteRoom.ts: -------------------------------------------------------------------------------- 1 | import type { CollectionKey, Database } from '..'; 2 | import { getRegistry } from '..'; 3 | 4 | /** Leaves the matrix room and deletes entries from the database registry */ 5 | export const deleteRoom = 6 | (_db: Database) => 7 | async ({ 8 | roomId, 9 | collectionKey, 10 | }: { 11 | roomId: string; 12 | collectionKey: CollectionKey; 13 | }) => { 14 | if (!_db.matrixClient) { 15 | throw new Error('matrixClient not found'); 16 | } 17 | 18 | const registry = getRegistry(_db); 19 | const registryDoc = registry.get('0'); 20 | if (!registryDoc) { 21 | throw new Error('registry document not found'); 22 | } 23 | const roomId = registryDoc[collectionKey][roomId]?.roomId; 24 | if (!roomId) { 25 | throw new Error('collection roomId not found in registry'); 26 | } 27 | await _db.matrixClient?.leave(roomId); 28 | 29 | const updatedRegistry = { 30 | ...registryDoc, 31 | }; 32 | delete updatedRegistry[collectionKey][roomId]; 33 | registry.set('0', updatedRegistry); 34 | 35 | delete _db.collections[collectionKey][roomId]; 36 | }; 37 | -------------------------------------------------------------------------------- /old-examples/db copy/src/methods/disconnectRoom.ts: -------------------------------------------------------------------------------- 1 | import type { Database, CollectionKey } from '..'; 2 | import { autoReconnectListenerName } from '../utils'; 3 | export type DisconnectRoomOptions = { 4 | collectionKey: CollectionKey; 5 | roomId: string; 6 | removeReconnectListener?: boolean; 7 | }; 8 | export const disconnectRoom = 9 | (_db: Database) => 10 | ({ 11 | collectionKey, 12 | roomId, 13 | removeReconnectListener = true, 14 | }: DisconnectRoomOptions) => { 15 | const room = _db.collections[collectionKey][roomId]; 16 | if (!room) return; 17 | _db.emit({ 18 | event: 'disconnectRoom', 19 | message: 'disconnecting room', 20 | data: { roomId: room.roomId }, 21 | }); 22 | room.connectStatus = 'disconnected'; 23 | 24 | room.matrixProvider?.dispose(); 25 | 26 | if (room.webRtcProvider) { 27 | room.webRtcProvider.disconnect(); 28 | room.webRtcProvider.destroy(); 29 | } 30 | if (removeReconnectListener) { 31 | _db.off(autoReconnectListenerName(room.roomId)); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /old-examples/db copy/src/methods/loadAndConnectRoom.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it, vitest } from 'vitest'; 2 | import { CollectionKey, Database } from '..'; 3 | 4 | describe('loadAndConnectRoom', () => { 5 | it('loads offline room and calls callback, and then connects remote', async () => { 6 | const db = new Database(); 7 | const loadRoomMock = vitest.fn(); 8 | const connectRoomMock = vitest.fn(); 9 | const loadRoomCallback = vitest.fn(); 10 | db.loadRoom = loadRoomMock; 11 | loadRoomMock.mockImplementationOnce(() => ({ 12 | roomId: 'seed', 13 | })); 14 | db.connectRoom = connectRoomMock; 15 | await db.loadAndConnectRoom( 16 | { collectionKey: 'flashcards', roomId: 'seed' }, 17 | loadRoomCallback 18 | ); 19 | 20 | expect(loadRoomMock).toHaveBeenCalledTimes(1); 21 | expect(connectRoomMock).toHaveBeenCalledTimes(1); 22 | expect(loadRoomCallback).toHaveBeenCalledTimes(1); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /old-examples/db copy/src/methods/loadAndConnectRoom.ts: -------------------------------------------------------------------------------- 1 | import type { CollectionKey, Database, Document, Room } from '..'; 2 | 3 | /** First tries to connect to the offline version of the room. If successful, calls `onLoad` with the room. Then It tries to connect to the remote room and finally returns the remote connected room. */ 4 | export const loadAndConnectRoom = 5 | (db: Database) => 6 | async ( 7 | { 8 | collectionKey, 9 | roomId, 10 | }: { 11 | collectionKey: CollectionKey; 12 | roomId: string; 13 | }, 14 | onLoad?: (room: Room) => void 15 | ) => { 16 | const offlineRoom = await db.loadRoom({ 17 | collectionKey, 18 | roomId, 19 | }); 20 | if (!offlineRoom) { 21 | throw new Error('could not load room'); 22 | } 23 | if (onLoad) onLoad(offlineRoom); 24 | 25 | // then connect the online version of the room 26 | const room = await db.connectRoom({ 27 | collectionKey, 28 | roomId, 29 | }); 30 | if (typeof room === 'string') { 31 | throw new Error(room); 32 | } 33 | return room; 34 | }; 35 | -------------------------------------------------------------------------------- /old-examples/db copy/src/methods/on.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '..'; 2 | import type { DBEvent, DBEventEmitter } from '../types'; 3 | 4 | export const on = 5 | (_db: Database) => (label: string, listener: DBEventEmitter) => { 6 | _db.listeners[label || 'default'] = listener; 7 | }; 8 | 9 | export const emit = (_db: Database) => (event: DBEvent) => { 10 | for (const listener of Object.values(_db.listeners)) { 11 | if (!event.level) event.level = 'info'; 12 | listener(event); 13 | } 14 | }; 15 | 16 | export const off = (_db: Database) => (label: string) => { 17 | delete _db.listeners[label || 'default']; 18 | }; 19 | -------------------------------------------------------------------------------- /old-examples/db copy/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | // in case we run into these issues too https://github.com/developit/microbundle/issues/708, otherwise vscode-lib fails 3 | import 'regenerator-runtime/runtime.js'; 4 | import 'lodash'; 5 | import 'fake-indexeddb/auto'; 6 | 7 | const { randomFillSync, subtle } = require('crypto'); 8 | (global as any).Olm = require('@matrix-org/olm'); 9 | // const { Crypto } = require("@peculiar/webcrypto"); 10 | // const crypto = new Crypto(); 11 | 12 | Object.defineProperty(globalThis, 'crypto', { 13 | value: { 14 | getRandomValues: randomFillSync, 15 | subtle, 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /old-examples/db copy/src/test-utils/matrixGuestClient.ts: -------------------------------------------------------------------------------- 1 | import { createClient, MemoryStore } from 'matrix-js-sdk'; 2 | 3 | export async function createMatrixGuestClient(config: { baseUrl: string }) { 4 | const tmpClient = await createClient(config); 5 | const { user_id, device_id, access_token } = await tmpClient.registerGuest( 6 | {} 7 | ); 8 | const matrixClient = createClient({ 9 | baseUrl: config.baseUrl, 10 | accessToken: access_token, 11 | userId: user_id, 12 | deviceId: device_id, 13 | store: new MemoryStore() as any, 14 | }); 15 | 16 | // hardcoded overwrites 17 | (matrixClient as any).canSupportVoip = false; 18 | (matrixClient as any).clientOpts = { 19 | lazyLoadMembers: true, 20 | }; 21 | 22 | matrixClient.setGuest(true); 23 | await matrixClient.initCrypto(); 24 | // don't use startClient (because it will sync periodically), when we're in guest / readonly mode 25 | // in guest mode we only use the matrixclient to fetch initial room state, but receive updates via WebRTCProvider 26 | 27 | // matrixClient.startClient({ lazyLoadMembers: true }); 28 | 29 | return matrixClient; 30 | } 31 | -------------------------------------------------------------------------------- /old-examples/db copy/src/utils/connection/awaitOnline.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..'; 2 | import { checkServerConnection } from './checkServerConnection'; 3 | 4 | /** Waits 3 seconds by default before failing */ 5 | export const awaitOnline = async (_db: Database, timeoutMs = 5000) => { 6 | if (_db.online) return true; 7 | 8 | return new Promise((resolve) => { 9 | // eslint-disable-next-line prefer-const 10 | let timeout: NodeJS.Timeout; 11 | 12 | const listener = (event: any) => { 13 | if (event.event === 'onlineChange' && event.data.online === true) { 14 | clearTimeout(timeout); 15 | _db.off('online-change'); 16 | resolve(true); 17 | } 18 | }; 19 | 20 | _db.on('online-change', listener); 21 | checkServerConnection(_db); 22 | timeout = setTimeout(() => { 23 | resolve(false); 24 | }, timeoutMs); 25 | }); 26 | }; 27 | -------------------------------------------------------------------------------- /old-examples/db copy/src/utils/connection/checkServerConnection.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..'; 2 | import { pingServer } from './pingServer'; 3 | 4 | /** pings the matrix server and sets the result to db.online. emits an event on change */ 5 | export const checkServerConnection = async (_db: Database) => { 6 | const res = await pingServer(_db); 7 | if (res) { 8 | if (_db.online) return; 9 | _db.online = true; 10 | _db.emit({ 11 | event: 'onlineChange', 12 | data: { online: true }, 13 | }); 14 | } else { 15 | if (!_db.online) return; 16 | _db.online = false; 17 | _db.emit({ 18 | event: 'onlineChange', 19 | data: { online: false }, 20 | }); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /old-examples/db copy/src/utils/connection/getRoomId.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..'; 2 | 3 | export const getRoomId = async (_db: Database, alias: string) => { 4 | let existingRoom: { room_id: string } | null = null; 5 | const logger = (message: string, data?: any) => 6 | _db.emit({ 7 | event: 'getRoomId', 8 | message, 9 | data: { roomId: alias, raw: data }, 10 | }); 11 | try { 12 | logger('starting getRoomId'); 13 | if (!_db.matrixClient) throw new Error('client not found'); 14 | // console.time('getRoomIdForAlias'); 15 | existingRoom = await _db.matrixClient.getRoomIdForAlias(alias); 16 | } catch (error) { 17 | logger('error getting room id', error); 18 | } 19 | if (existingRoom?.room_id) { 20 | logger('got room id', existingRoom); 21 | return existingRoom.room_id; 22 | } else { 23 | logger('no room id found'); 24 | return false; 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /old-examples/db copy/src/utils/connection/index.ts: -------------------------------------------------------------------------------- 1 | export * from './aliasHelpers'; 2 | export * from './autoReconnect'; 3 | export * from './awaitOnline'; 4 | export * from './checkServerConnection'; 5 | export * from './connectMatrixProvider'; 6 | export * from './connectWebRtc'; 7 | export * from './createMatrixClient'; 8 | export * from './createRoom'; 9 | export * from './getOrCreateRegistryAndSpace'; 10 | export * from './getRoomId'; 11 | export * from './initializeDoc'; 12 | export * from './joinRoomIfNotJoined'; 13 | export * from './joinRoomIfNotJoined'; 14 | export * from './loadRoom'; 15 | export * from './newMatrixProvider'; 16 | export * from './pingServer'; 17 | export * from './pollConnection'; 18 | export * from './populateRegistry'; 19 | export * from './registerRoomToSpace'; 20 | export * from './saveRoomToRegistry'; 21 | -------------------------------------------------------------------------------- /old-examples/db copy/src/utils/connection/initializeDoc.ts: -------------------------------------------------------------------------------- 1 | import { IndexeddbPersistence } from 'y-indexeddb'; 2 | import { Doc } from 'yjs'; 3 | 4 | import type { Document, YDoc } from '../../types'; 5 | 6 | export const initializeDocAndLocalProvider = async ( 7 | roomId: string, 8 | existingDoc?: YDoc 9 | ): Promise<{ ydoc: YDoc; localProvider: IndexeddbPersistence }> => { 10 | const ydoc = existingDoc || (new Doc() as YDoc); 11 | if (!ydoc) throw new Error('could not create doc'); 12 | 13 | const localProvider = new IndexeddbPersistence(roomId, ydoc as Doc); 14 | if (localProvider.synced) return { ydoc, localProvider }; 15 | 16 | const synced = await localProvider.whenSynced; 17 | if (synced.synced) return { ydoc, localProvider }; 18 | else throw new Error('could not sync doc'); 19 | }; 20 | -------------------------------------------------------------------------------- /old-examples/db copy/src/utils/connection/loadRoom.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect, beforeEach } from 'vitest'; 2 | 3 | import { CollectionKey, Database } from '../..'; 4 | 5 | describe('loadRoom', () => { 6 | let db: Database; 7 | 8 | beforeEach(() => { 9 | db = new Database(); 10 | }); 11 | 12 | it('should load a room and return it', async () => { 13 | db.userId = 'testUserId'; 14 | const collectionKey = 'notes'; 15 | const roomId = 'testroomId'; 16 | const room = await db.loadRoom({ collectionKey, roomId }); 17 | expect(room).toBeDefined(); 18 | expect(room.ydoc?.store).toBeDefined(); 19 | }); 20 | 21 | it('should load the registry and return it', async () => { 22 | const collectionKey = 'registry'; 23 | const roomId = ''; 24 | const room = await db.loadRoom({ collectionKey, roomId }); 25 | expect(room).toBeDefined(); 26 | expect(room.ydoc?.store).toBeDefined(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /old-examples/db copy/src/utils/connection/pingServer.test.ts: -------------------------------------------------------------------------------- 1 | import { it, describe, expect } from 'vitest'; 2 | import { pingServer } from './pingServer'; 3 | import { Database } from '../..'; 4 | import { baseUrl } from '../../test-utils'; 5 | 6 | describe('pingServer', () => { 7 | it('should return true', async () => { 8 | const DB = new Database({ baseUrl }); 9 | const result = await pingServer(DB); 10 | expect(result).toBe(true); 11 | }); 12 | it('should return false', async () => { 13 | const DB = new Database({ baseUrl: 'http://localhost:123' }); 14 | const result = await pingServer(DB); 15 | expect(result).toBe(false); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /old-examples/db copy/src/utils/connection/pingServer.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..'; 2 | 3 | export const pingServer = async (_db: Database) => { 4 | const MATRIX_HOME_URL = new URL(_db.baseUrl); 5 | MATRIX_HOME_URL.pathname = '_matrix/federation/v1/version'; 6 | try { 7 | const res = await fetch(MATRIX_HOME_URL.toString()); 8 | return res.status === 200; 9 | } catch (e) { 10 | // console.error(e); 11 | return false; 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /old-examples/db copy/src/utils/connection/pollConnection.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..'; 2 | import { checkServerConnection } from './checkServerConnection'; 3 | 4 | /** by default polls often (2000ms) trying to check for return of connection after connection loss, and less often (10000ms) checking to make sure connection is still there */ 5 | export const pollConnection = ( 6 | _db: Database, 7 | offlineInterval = 2000, 8 | onlineInterval = 10000 9 | ) => { 10 | setInterval(() => { 11 | if (!_db.online) checkServerConnection(_db); 12 | }, offlineInterval); 13 | 14 | setInterval(() => { 15 | if (_db.online) checkServerConnection(_db); 16 | }, onlineInterval); 17 | }; 18 | -------------------------------------------------------------------------------- /old-examples/db copy/src/utils/connection/registerRoomToSpace.ts: -------------------------------------------------------------------------------- 1 | import type { MatrixClient } from 'matrix-js-sdk'; 2 | 3 | import { buildSpaceroomId } from './aliasHelpers'; 4 | 5 | export const registerRoomToSpace = async ( 6 | matrixClient: MatrixClient, 7 | roomId: string 8 | ) => { 9 | const userId = matrixClient.getUserId(); 10 | if (!userId) throw new Error('userId not found'); 11 | const spaceAlias = buildSpaceroomId(userId); 12 | const spaceIdRes = await matrixClient.getRoomIdForAlias(spaceAlias); 13 | if (!spaceIdRes) throw new Error('space not found'); 14 | const spaceId = spaceIdRes.room_id; 15 | 16 | const host = spaceId.split(':')[1]; 17 | 18 | // const registerToSpaceRes = 19 | await matrixClient.sendStateEvent( 20 | spaceId, 21 | 'm.space.child', 22 | { via: host }, 23 | roomId 24 | ); 25 | // console.log({ registerToSpaceRes }); 26 | // const hierarchy = await matrixClient.getRoomHierarchy(spaceId); 27 | // console.log({ hierarchy }); 28 | }; 29 | -------------------------------------------------------------------------------- /old-examples/db copy/src/utils/db/buildRef.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | import { CollectionKey } from '../../types'; 3 | import { buildRef } from './buildRef'; 4 | 5 | describe('buildRef', () => { 6 | const collectionKey = 'flashcards'; 7 | const roomId = 'seed'; 8 | const documentId = 'doc-id'; 9 | it('builds a valid ref', () => { 10 | const expected = `${collectionKey}.${roomId}.${documentId}`; 11 | const actual = buildRef({ collectionKey, roomId, documentId }); 12 | expect(actual).toEqual(expected); 13 | }); 14 | it('throws if documentId includes .', () => { 15 | expect(() => 16 | buildRef({ collectionKey, roomId, documentId: 'doc.id' }) 17 | ).toThrow(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /old-examples/db copy/src/utils/db/buildRef.ts: -------------------------------------------------------------------------------- 1 | import type { CollectionKey } from '../../types'; 2 | 3 | /** 4 | * 5 | * @param collection e.g. `'flashcards'` "flashcards" 6 | * @param roomId format `|` @example 'https://eweser.com|uuid' 7 | * @param documentId any number/string what doesn't include `|` 8 | * @returns `${collection}|${roomId}|${documentId}` @example 'flashcards.https://eweser.com|uuid.doc-id' 9 | */ 10 | export const buildRef = ({ 11 | collectionKey, 12 | roomId, 13 | documentId, 14 | }: { 15 | collectionKey: CollectionKey; 16 | roomId: string; 17 | documentId: string | number; 18 | }) => { 19 | if (documentId.toString().includes('|')) { 20 | throw new Error('documentId cannot include |'); 21 | } 22 | // roomId should have one | 23 | if (roomId.split('|').length !== 2) { 24 | throw new Error('roomId must have exactly one |'); 25 | } 26 | return `${collectionKey}|${roomId}|${documentId}`; 27 | }; 28 | -------------------------------------------------------------------------------- /old-examples/db copy/src/utils/db/index.ts: -------------------------------------------------------------------------------- 1 | export * from './newEmptyRoom'; 2 | export * from './buildRef'; 3 | export * from './newDocument'; 4 | export * from './parseRef'; 5 | export * from './validation'; 6 | -------------------------------------------------------------------------------- /old-examples/db copy/src/utils/db/localStorageService.ts: -------------------------------------------------------------------------------- 1 | export enum LocalStorageKey { 2 | loginData = 'loginData', 3 | } 4 | export const localStorageSet = (key: LocalStorageKey, value: any) => { 5 | localStorage.setItem('ewe_' + key, JSON.stringify(value)); 6 | }; 7 | export const localStorageGet = (key: LocalStorageKey): T | null => { 8 | const value = localStorage.getItem('ewe_' + key); 9 | if (!value) return null; 10 | return JSON.parse(value) as T; 11 | }; 12 | -------------------------------------------------------------------------------- /old-examples/db copy/src/utils/db/newDocument.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentBase, DocumentWithoutBase, Document } from '../../types'; 2 | 3 | /** Sets the metadata like created and updated for the doc */ 4 | export const newDocument = ( 5 | _ref: string, 6 | doc: DocumentWithoutBase 7 | ): T => { 8 | const _id = _ref.split('.').pop(); 9 | if (!_id) throw new Error('no _id found in ref'); 10 | 11 | const now = new Date().getTime(); 12 | const base: DocumentBase = { 13 | _created: now, 14 | _id, 15 | _ref, 16 | _updated: now, 17 | _deleted: false, 18 | _ttl: undefined, 19 | }; 20 | // @ts-ignore 21 | return { ...base, ...doc }; 22 | }; 23 | -------------------------------------------------------------------------------- /old-examples/db copy/src/utils/db/newEmptyRoom.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import { CollectionKey, Database } from '../..'; 3 | import { baseUrl } from '../../test-utils'; 4 | import { newEmptyRoom } from '..'; 5 | 6 | describe('newEmptyRoom', () => { 7 | it('makes a new empty room', () => { 8 | const db = new Database({ baseUrl }); 9 | db.userId = 'user'; 10 | const room = newEmptyRoom(db, 'notes', 'test'); 11 | expect(room.collectionKey).toBe('notes'); 12 | expect(room.roomId).toBe('#test~notes~user'); 13 | expect(room.connectStatus).toBe('initial'); 14 | expect(room.created).toBeDefined(); 15 | expect(room.tempDocs).toEqual({}); 16 | expect(room.matrixProvider).toBeNull(); 17 | expect(room.webRtcProvider).toBeNull(); 18 | expect(room.indexeddbProvider).toBeNull(); 19 | expect(room.created).toBeDefined(); 20 | expect(room.roomId).not.toBeDefined(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /old-examples/db copy/src/utils/db/newEmptyRoom.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..'; 2 | import type { CollectionKey, Document, Room } from '../../types'; 3 | 4 | export const newEmptyRoom = ( 5 | _db: Database, 6 | collectionKey: CollectionKey, 7 | roomId: string 8 | ) => { 9 | const room: Room = { 10 | connectStatus: 'initial', 11 | collectionKey, 12 | y: null, 13 | webRtcProvider: null, 14 | indexeddbProvider: null, 15 | created: new Date(), 16 | roomId, 17 | tempDocs: {}, 18 | }; 19 | return room; 20 | }; 21 | -------------------------------------------------------------------------------- /old-examples/db copy/src/utils/db/parseRef.ts: -------------------------------------------------------------------------------- 1 | import { CollectionKey } from '../../types'; 2 | import { roomIdValidation } from '../connection'; 3 | 4 | /** 5 | * 6 | * @param ref valid ref e.g. `collectionKey.roomId.doc-id` 7 | * Validates the ref and returns the parts 8 | */ 9 | export const parseRef = (ref: string) => { 10 | const parts = ref.split('.'); 11 | if (parts.length !== 3) throw new Error('ref must have 3 parts'); 12 | const [collectionKey, roomId, documentId] = parts; 13 | if (!collectionKey || !roomId || !documentId) 14 | throw new Error('ref must have 3 parts'); 15 | 16 | roomIdValidation(roomId); 17 | 18 | // check that collectionKey is a valid collectionKey 19 | if (!Object.values(CollectionKey).includes(collectionKey as CollectionKey)) { 20 | throw new Error('collectionKey is not a valid collectionKey'); 21 | } 22 | 23 | return { 24 | collectionKey: collectionKey as CollectionKey, 25 | roomId, 26 | documentId, 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /old-examples/db copy/src/utils/index.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | import { buildFullUserId, extractUserIdLocalPart } from '.'; 3 | 4 | describe('buildFullUserId', () => { 5 | it('adds homeserver to user local part', () => { 6 | const homserver = 'https://matrix.org'; 7 | const username = 'jacob'; 8 | const fullUserId = buildFullUserId(username, homserver); 9 | expect(fullUserId).toBe('@jacob:matrix.org'); 10 | }); 11 | it('can handle http, or homeserver without protocol', () => { 12 | const homserver = 'matrix.org'; 13 | const username = 'jacob'; 14 | const fullUserId = buildFullUserId(username, homserver); 15 | expect(fullUserId).toBe('@jacob:matrix.org'); 16 | 17 | const homserver2 = 'http://matrix.org'; 18 | const fullUserId2 = buildFullUserId(username, homserver2); 19 | expect(fullUserId2).toBe('@jacob:matrix.org'); 20 | }); 21 | }); 22 | 23 | describe('extractUserIdLocalPart', () => { 24 | it('extracts local part from full user id', () => { 25 | const userId = '@jacob:matrix.org'; 26 | const localPart = extractUserIdLocalPart(userId); 27 | expect(localPart).toBe('jacob'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /old-examples/db copy/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": [ 4 | "**/*.test.ts", 5 | "src/setupTests.ts", 6 | "src/test-utils", 7 | "src/examples" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /old-examples/db copy/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": false, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "module": "commonjs", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": false, 17 | "declaration": true, 18 | "declarationDir": "types", 19 | "sourceMap": true, 20 | "downlevelIteration": true, 21 | "outDir": "dist", 22 | "useUnknownInCatchVariables": false 23 | }, 24 | "include": ["src"], 25 | "exclude": ["src/setupTests.ts"] 26 | } 27 | -------------------------------------------------------------------------------- /old-examples/db copy/vite.config.js: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path'; 2 | import { defineConfig } from 'vitest/config'; 3 | 4 | export default defineConfig({ 5 | build: { 6 | lib: { 7 | name: 'eweser-db', 8 | entry: resolve(__dirname, 'src/index.ts'), 9 | }, 10 | rollupOptions: { 11 | // make sure to externalize deps that shouldn't be bundled 12 | // into your library 13 | external: [ 14 | // 15 | ], 16 | }, 17 | }, 18 | test: { 19 | environment: 'jsdom', 20 | setupFiles: 'src/setupTests.ts', 21 | // need to slow down the tests or the server will reject the requests 22 | maxConcurrency: 2, 23 | maxThreads: 2, 24 | minThreads: 1, 25 | // coverage: { 26 | // reporter: ['text', 'json', 'html', 'lcov'], 27 | // }, 28 | }, 29 | }); 30 | -------------------------------------------------------------------------------- /old-examples/example-editor/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@eweser/eslint-config-react-ts'], 3 | ignorePatterns: [ 4 | 'vite.config.*', 5 | 'types/**/*.d.ts', 6 | 'dist', 7 | 'setupTests.ts', 8 | '.eslintrc.cjs', 9 | ], 10 | }; 11 | -------------------------------------------------------------------------------- /old-examples/example-editor/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 EduVault 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /old-examples/example-editor/example.env: -------------------------------------------------------------------------------- 1 | VITE_DEV_USERNAME=my-test-user 2 | VITE_DEV_PASSWORD=my-test-password123! 3 | VITE_MATRIX_SERVER=http://localhost:8888 4 | -------------------------------------------------------------------------------- /old-examples/example-editor/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Eweser DB Example App 7 | 10 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /old-examples/example-editor/netlify.toml: -------------------------------------------------------------------------------- 1 | [[redirects]] 2 | from = "/*" 3 | to = "/index.html" 4 | status = 200 -------------------------------------------------------------------------------- /old-examples/example-editor/src/config.ts: -------------------------------------------------------------------------------- 1 | const dummyUserName = 'dummy-user'; 2 | const dummyUserPass = 'dumdum'; 3 | 4 | export const MATRIX_SERVER = 5 | import.meta.env.VITE_MATRIX_SERVER ?? 'https://matrix.org'; 6 | 7 | export const env = 8 | import.meta.env.VITE_CI === 'true' 9 | ? 'ci' 10 | : import.meta.env.DEV 11 | ? 'dev' 12 | : 'prod'; 13 | 14 | export const dev = env === 'dev'; 15 | export const ci = env === 'ci'; 16 | 17 | export const showSignup = env !== 'prod'; 18 | 19 | export const DEV_USERNAME = 20 | import.meta.env.VITE_DEV_USERNAME ?? dev ? dummyUserName : ''; 21 | export const DEV_PASSWORD = 22 | import.meta.env.VITE_DEV_PASSWORD ?? dev ? dummyUserPass : ''; 23 | 24 | const localWebRtcServer = 'ws://localhost:4444'; 25 | export const WEB_RTC_PEERS = dev || ci ? [localWebRtcServer] : undefined; 26 | -------------------------------------------------------------------------------- /old-examples/example-editor/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './AppEditor'; 4 | import '@milkdown/theme-nord/style.css'; 5 | 6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /old-examples/example-editor/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "baseUrl": "./src" 19 | }, 20 | 21 | "include": ["vite.config.ts", ".eslintrc.cjs", "src"], 22 | "references": [{ "path": "./tsconfig.node.json" }] 23 | } 24 | -------------------------------------------------------------------------------- /old-examples/example-editor/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /old-examples/example-editor/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /old-examples/example-editor/vite.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import nodePolyfills from 'rollup-plugin-polyfill-node'; 3 | import { defineConfig } from 'vite'; 4 | import react from '@vitejs/plugin-react'; 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [react()], 9 | 10 | build: { 11 | minify: false, 12 | rollupOptions: { 13 | plugins: [nodePolyfills()], 14 | }, 15 | }, 16 | define: { 17 | global: 'window', 18 | }, 19 | preview: { 20 | port: 8100, 21 | }, 22 | server: { 23 | port: 8100, 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /old-examples/example-interop-flashcards/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@eweser/eslint-config-react-ts'], 3 | ignorePatterns: [ 4 | 'vite.config.*', 5 | 'types/**/*.d.ts', 6 | 'dist', 7 | 'setupTests.ts', 8 | '.eslintrc.cjs', 9 | ], 10 | }; 11 | -------------------------------------------------------------------------------- /old-examples/example-interop-flashcards/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 EduVault 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /old-examples/example-interop-flashcards/example.env: -------------------------------------------------------------------------------- 1 | VITE_DEV_USERNAME=my-test-user 2 | VITE_DEV_PASSWORD=my-test-password123! 3 | VITE_MATRIX_SERVER=http://localhost:8888 4 | -------------------------------------------------------------------------------- /old-examples/example-interop-flashcards/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Eweser DB Example App 7 | 10 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /old-examples/example-interop-flashcards/netlify.toml: -------------------------------------------------------------------------------- 1 | [[redirects]] 2 | from = "/*" 3 | to = "/index.html" 4 | status = 200 -------------------------------------------------------------------------------- /old-examples/example-interop-flashcards/src/config.ts: -------------------------------------------------------------------------------- 1 | const dummyUserName = 'dummy-user'; 2 | const dummyUserPass = 'dumdum'; 3 | 4 | export const MATRIX_SERVER = 5 | import.meta.env.VITE_MATRIX_SERVER ?? 'https://matrix.org'; 6 | 7 | export const env = 8 | import.meta.env.VITE_CI === 'true' 9 | ? 'ci' 10 | : import.meta.env.DEV 11 | ? 'dev' 12 | : 'prod'; 13 | 14 | export const dev = env === 'dev'; 15 | export const ci = env === 'ci'; 16 | 17 | export const showSignup = env !== 'prod'; 18 | 19 | export const DEV_USERNAME = 20 | import.meta.env.VITE_DEV_USERNAME ?? dev ? dummyUserName : ''; 21 | export const DEV_PASSWORD = 22 | import.meta.env.VITE_DEV_PASSWORD ?? dev ? dummyUserPass : ''; 23 | 24 | const localWebRtcServer = 'ws://localhost:4444'; 25 | export const WEB_RTC_PEERS = dev || ci ? [localWebRtcServer] : undefined; 26 | -------------------------------------------------------------------------------- /old-examples/example-interop-flashcards/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './AppInteropFlashcards'; 4 | 5 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 6 | 7 | 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /old-examples/example-interop-flashcards/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "baseUrl": "./src" 19 | }, 20 | 21 | "include": ["vite.config.ts", ".eslintrc.cjs", "src"], 22 | "references": [{ "path": "./tsconfig.node.json" }] 23 | } 24 | -------------------------------------------------------------------------------- /old-examples/example-interop-flashcards/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /old-examples/example-interop-flashcards/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /old-examples/example-interop-flashcards/vite.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import nodePolyfills from 'rollup-plugin-polyfill-node'; 3 | import { defineConfig } from 'vite'; 4 | import react from '@vitejs/plugin-react'; 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [react()], 9 | 10 | build: { 11 | minify: false, 12 | rollupOptions: { 13 | plugins: [nodePolyfills()], 14 | }, 15 | }, 16 | define: { 17 | global: 'window', 18 | }, 19 | preview: { 20 | port: 8500, 21 | }, 22 | server: { 23 | port: 8500, 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /old-examples/example-interop-notes/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@eweser/eslint-config-react-ts'], 3 | ignorePatterns: [ 4 | 'vite.config.*', 5 | 'types/**/*.d.ts', 6 | 'dist', 7 | 'setupTests.ts', 8 | '.eslintrc.cjs', 9 | ], 10 | }; 11 | -------------------------------------------------------------------------------- /old-examples/example-interop-notes/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 EduVault 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /old-examples/example-interop-notes/example.env: -------------------------------------------------------------------------------- 1 | VITE_DEV_USERNAME=my-test-user 2 | VITE_DEV_PASSWORD=my-test-password123! 3 | VITE_MATRIX_SERVER=http://localhost:8888 4 | -------------------------------------------------------------------------------- /old-examples/example-interop-notes/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Eweser DB Example App 7 | 10 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /old-examples/example-interop-notes/netlify.toml: -------------------------------------------------------------------------------- 1 | [[redirects]] 2 | from = "/*" 3 | to = "/index.html" 4 | status = 200 -------------------------------------------------------------------------------- /old-examples/example-interop-notes/src/config.ts: -------------------------------------------------------------------------------- 1 | const dummyUserName = 'dummy-user'; 2 | const dummyUserPass = 'dumdum'; 3 | 4 | export const MATRIX_SERVER = 5 | import.meta.env.VITE_MATRIX_SERVER ?? 'https://matrix.org'; 6 | 7 | export const env = 8 | import.meta.env.VITE_CI === 'true' 9 | ? 'ci' 10 | : import.meta.env.DEV 11 | ? 'dev' 12 | : 'prod'; 13 | 14 | export const dev = env === 'dev'; 15 | export const ci = env === 'ci'; 16 | 17 | export const showSignup = env !== 'prod'; 18 | 19 | export const DEV_USERNAME = 20 | import.meta.env.VITE_DEV_USERNAME ?? dev ? dummyUserName : ''; 21 | export const DEV_PASSWORD = 22 | import.meta.env.VITE_DEV_PASSWORD ?? dev ? dummyUserPass : ''; 23 | 24 | const localWebRtcServer = 'ws://localhost:4444'; 25 | export const WEB_RTC_PEERS = dev || ci ? [localWebRtcServer] : undefined; 26 | -------------------------------------------------------------------------------- /old-examples/example-interop-notes/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './AppInteropNotes'; 4 | 5 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 6 | 7 | 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /old-examples/example-interop-notes/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "baseUrl": "./src" 19 | }, 20 | 21 | "include": ["vite.config.ts", ".eslintrc.cjs", "src"], 22 | "references": [{ "path": "./tsconfig.node.json" }] 23 | } 24 | -------------------------------------------------------------------------------- /old-examples/example-interop-notes/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /old-examples/example-interop-notes/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /old-examples/example-interop-notes/vite.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import nodePolyfills from 'rollup-plugin-polyfill-node'; 3 | import { defineConfig } from 'vite'; 4 | import react from '@vitejs/plugin-react'; 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [react()], 9 | 10 | build: { 11 | minify: false, 12 | rollupOptions: { 13 | plugins: [nodePolyfills()], 14 | }, 15 | }, 16 | define: { 17 | global: 'window', 18 | }, 19 | preview: { 20 | port: 8400, 21 | }, 22 | server: { 23 | port: 8400, 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /old-examples/example-multi-room/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@eweser/eslint-config-react-ts'], 3 | ignorePatterns: [ 4 | 'vite.config.*', 5 | 'types/**/*.d.ts', 6 | 'dist', 7 | 'setupTests.ts', 8 | '.eslintrc.cjs', 9 | ], 10 | }; 11 | -------------------------------------------------------------------------------- /old-examples/example-multi-room/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 EduVault 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /old-examples/example-multi-room/example.env: -------------------------------------------------------------------------------- 1 | VITE_DEV_USERNAME=my-test-user 2 | VITE_DEV_PASSWORD=my-test-password123! 3 | VITE_MATRIX_SERVER=http://localhost:8888 4 | -------------------------------------------------------------------------------- /old-examples/example-multi-room/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Eweser DB Example App 7 | 10 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /old-examples/example-multi-room/netlify.toml: -------------------------------------------------------------------------------- 1 | [[redirects]] 2 | from = "/*" 3 | to = "/index.html" 4 | status = 200 -------------------------------------------------------------------------------- /old-examples/example-multi-room/src/config.ts: -------------------------------------------------------------------------------- 1 | const dummyUserName = 'dummy-user'; 2 | const dummyUserPass = 'dumdum'; 3 | 4 | export const MATRIX_SERVER = 5 | import.meta.env.VITE_MATRIX_SERVER ?? 'https://matrix.org'; 6 | 7 | export const env = 8 | import.meta.env.VITE_CI === 'true' 9 | ? 'ci' 10 | : import.meta.env.DEV 11 | ? 'dev' 12 | : 'prod'; 13 | 14 | export const dev = env === 'dev'; 15 | export const ci = env === 'ci'; 16 | 17 | export const showSignup = env !== 'prod'; 18 | 19 | export const DEV_USERNAME = 20 | import.meta.env.VITE_DEV_USERNAME ?? dev ? dummyUserName : ''; 21 | export const DEV_PASSWORD = 22 | import.meta.env.VITE_DEV_PASSWORD ?? dev ? dummyUserPass : ''; 23 | 24 | const localWebRtcServer = 'ws://localhost:4444'; 25 | export const WEB_RTC_PEERS = dev || ci ? [localWebRtcServer] : undefined; 26 | -------------------------------------------------------------------------------- /old-examples/example-multi-room/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './AppMultiRoom'; 4 | 5 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 6 | 7 | 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /old-examples/example-multi-room/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "baseUrl": "./src" 19 | }, 20 | 21 | "include": ["vite.config.ts", ".eslintrc.cjs", "src"], 22 | "references": [{ "path": "./tsconfig.node.json" }] 23 | } 24 | -------------------------------------------------------------------------------- /old-examples/example-multi-room/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /old-examples/example-multi-room/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /old-examples/example-multi-room/vite.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import nodePolyfills from 'rollup-plugin-polyfill-node'; 3 | import { defineConfig } from 'vite'; 4 | import react from '@vitejs/plugin-react'; 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [react()], 9 | 10 | build: { 11 | minify: false, 12 | rollupOptions: { 13 | plugins: [nodePolyfills()], 14 | }, 15 | }, 16 | define: { 17 | global: 'window', 18 | }, 19 | preview: { 20 | port: 8300, 21 | }, 22 | server: { 23 | port: 8300, 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /old-examples/example-offline-first/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@eweser/eslint-config-react-ts'], 3 | ignorePatterns: [ 4 | 'vite.config.*', 5 | 'types/**/*.d.ts', 6 | 'dist', 7 | 'setupTests.ts', 8 | '.eslintrc.cjs', 9 | ], 10 | }; 11 | -------------------------------------------------------------------------------- /old-examples/example-offline-first/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 EduVault 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /old-examples/example-offline-first/example.env: -------------------------------------------------------------------------------- 1 | VITE_DEV_USERNAME=my-test-user 2 | VITE_DEV_PASSWORD=my-test-password123! 3 | VITE_MATRIX_SERVER=http://localhost:8888 4 | -------------------------------------------------------------------------------- /old-examples/example-offline-first/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Eweser DB Example App 7 | 10 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /old-examples/example-offline-first/netlify.toml: -------------------------------------------------------------------------------- 1 | [[redirects]] 2 | from = "/*" 3 | to = "/index.html" 4 | status = 200 -------------------------------------------------------------------------------- /old-examples/example-offline-first/src/config.ts: -------------------------------------------------------------------------------- 1 | const dummyUserName = 'dummy-user123'; 2 | const dummyUserPass = 'dumdum'; 3 | 4 | export const MATRIX_SERVER = 5 | import.meta.env.VITE_MATRIX_SERVER ?? 'https://matrix.org'; 6 | 7 | export const env = 8 | import.meta.env.VITE_CI === 'true' 9 | ? 'ci' 10 | : import.meta.env.DEV 11 | ? 'dev' 12 | : 'prod'; 13 | 14 | export const dev = env === 'dev'; 15 | export const ci = env === 'ci'; 16 | 17 | export const showSignup = env !== 'prod'; 18 | 19 | export const DEV_USERNAME = 20 | import.meta.env.VITE_DEV_USERNAME ?? dev ? dummyUserName : ''; 21 | export const DEV_PASSWORD = 22 | import.meta.env.VITE_DEV_PASSWORD ?? dev ? dummyUserPass : ''; 23 | 24 | const localWebRtcServer = 'ws://localhost:4444'; 25 | export const WEB_RTC_PEERS = dev || ci ? [localWebRtcServer] : undefined; 26 | -------------------------------------------------------------------------------- /old-examples/example-offline-first/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './AppOfflineFirst'; 4 | 5 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 6 | 7 | 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /old-examples/example-offline-first/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "baseUrl": "./src" 19 | }, 20 | 21 | "include": ["vite.config.ts", ".eslintrc.cjs", "src"], 22 | "references": [{ "path": "./tsconfig.node.json" }] 23 | } 24 | -------------------------------------------------------------------------------- /old-examples/example-offline-first/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /old-examples/example-offline-first/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /old-examples/example-offline-first/vite.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import nodePolyfills from 'rollup-plugin-polyfill-node'; 3 | import { defineConfig } from 'vite'; 4 | import react from '@vitejs/plugin-react'; 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [react()], 9 | 10 | build: { 11 | minify: false, 12 | rollupOptions: { 13 | plugins: [nodePolyfills()], 14 | }, 15 | }, 16 | define: { 17 | global: 'window', 18 | }, 19 | preview: { 20 | port: 8600, 21 | }, 22 | server: { 23 | port: 8600, 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /old-examples/example-synced-store/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@eweser/eslint-config-react-ts'], 3 | ignorePatterns: [ 4 | 'vite.config.*', 5 | 'types/**/*.d.ts', 6 | 'dist', 7 | 'setupTests.ts', 8 | '.eslintrc.cjs', 9 | ], 10 | }; 11 | -------------------------------------------------------------------------------- /old-examples/example-synced-store/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 EduVault 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /old-examples/example-synced-store/example.env: -------------------------------------------------------------------------------- 1 | VITE_DEV_USERNAME=my-test-user 2 | VITE_DEV_PASSWORD=my-test-password123! 3 | VITE_MATRIX_SERVER=http://localhost:8888 4 | -------------------------------------------------------------------------------- /old-examples/example-synced-store/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Eweser DB Example App 7 | 10 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /old-examples/example-synced-store/netlify.toml: -------------------------------------------------------------------------------- 1 | [[redirects]] 2 | from = "/*" 3 | to = "/index.html" 4 | status = 200 -------------------------------------------------------------------------------- /old-examples/example-synced-store/src/config.ts: -------------------------------------------------------------------------------- 1 | const dummyUserName = 'dummy-user'; 2 | const dummyUserPass = 'dumdum'; 3 | 4 | export const MATRIX_SERVER = 5 | import.meta.env.VITE_MATRIX_SERVER ?? 'https://matrix.org'; 6 | 7 | export const env = 8 | import.meta.env.VITE_CI === 'true' 9 | ? 'ci' 10 | : import.meta.env.DEV 11 | ? 'dev' 12 | : 'prod'; 13 | 14 | export const dev = env === 'dev'; 15 | export const ci = env === 'ci'; 16 | 17 | export const showSignup = env !== 'prod'; 18 | 19 | export const DEV_USERNAME = 20 | import.meta.env.VITE_DEV_USERNAME ?? dev ? dummyUserName : ''; 21 | export const DEV_PASSWORD = 22 | import.meta.env.VITE_DEV_PASSWORD ?? dev ? dummyUserPass : ''; 23 | 24 | const localWebRtcServer = 'ws://localhost:4444'; 25 | export const WEB_RTC_PEERS = dev || ci ? [localWebRtcServer] : undefined; 26 | -------------------------------------------------------------------------------- /old-examples/example-synced-store/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './AppSyncedStore'; 4 | 5 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 6 | 7 | 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /old-examples/example-synced-store/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "baseUrl": "./src" 19 | }, 20 | 21 | "include": ["vite.config.ts", ".eslintrc.cjs", "src"], 22 | "references": [{ "path": "./tsconfig.node.json" }] 23 | } 24 | -------------------------------------------------------------------------------- /old-examples/example-synced-store/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /old-examples/example-synced-store/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /old-examples/example-synced-store/vite.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import nodePolyfills from 'rollup-plugin-polyfill-node'; 3 | import { defineConfig } from 'vite'; 4 | import react from '@vitejs/plugin-react'; 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [react()], 9 | 10 | build: { 11 | minify: false, 12 | rollupOptions: { 13 | plugins: [nodePolyfills()], 14 | }, 15 | }, 16 | define: { 17 | global: 'window', 18 | }, 19 | preview: { 20 | port: 8200, 21 | }, 22 | server: { 23 | port: 8200, 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /packages/aggregator/.env-example: -------------------------------------------------------------------------------- 1 | PORT=80 2 | 3 | # from docker-compose-mongo.yml 4 | MONGO_URL=mongodb://root:dev@localhost:27017a 5 | 6 | BASE_URL=http://localhost:8888 7 | # an account on the BASE_URL's matrix server 8 | USER_ID=@testAggregatorm87axorh:localhost:8888 9 | PASSWORD=password123! -------------------------------------------------------------------------------- /packages/aggregator/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@eweser/eslint-config-ts'], 3 | ignorePatterns: ['dist', 'setupTests.ts', '.eslintrc.cjs', 'vite.config.js'], 4 | }; 5 | -------------------------------------------------------------------------------- /packages/aggregator/.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org/ 2 | @matrix-org:registry=https://gitlab.matrix.org/api/v4/packages/npm/ 3 | -------------------------------------------------------------------------------- /packages/aggregator/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @eweser/aggregator 2 | 3 | ## 1.14.0 4 | 5 | ### Minor Changes 6 | 7 | - align versions 8 | 9 | ### Patch Changes 10 | 11 | - Updated dependencies 12 | - @eweser/db@1.14.0 13 | 14 | ## 1.13.5 15 | 16 | ### Patch Changes 17 | 18 | - bump 19 | - Updated dependencies 20 | - @eweser/db@1.13.6 21 | 22 | ## 1.13.4 23 | 24 | ### Patch Changes 25 | 26 | - registry sync 27 | - Updated dependencies 28 | - @eweser/db@1.13.5 29 | 30 | ## 1.13.3 31 | 32 | ### Patch Changes 33 | 34 | - add rolling sync 35 | - Updated dependencies 36 | - @eweser/db@1.13.4 37 | -------------------------------------------------------------------------------- /packages/aggregator/docker-compose-mongo.yml: -------------------------------------------------------------------------------- 1 | version: '3.1' 2 | 3 | services: 4 | db: 5 | image: mongo 6 | restart: always 7 | environment: 8 | MONGO_INITDB_ROOT_USERNAME: root 9 | MONGO_INITDB_ROOT_PASSWORD: dev 10 | ports: 11 | - '27017:27017' 12 | -------------------------------------------------------------------------------- /packages/aggregator/nixpacks.toml: -------------------------------------------------------------------------------- 1 | [phases.setup] 2 | nixPkgs = ['nodejs'] 3 | 4 | [phases.build] 5 | cmds = ['npm run build'] 6 | 7 | [start] 8 | cmd = 'npm run start' -------------------------------------------------------------------------------- /packages/aggregator/src/api/join-room-get.ts: -------------------------------------------------------------------------------- 1 | import type { Request, Response } from 'express'; 2 | import type { MatrixClient } from 'matrix-js-sdk'; 3 | 4 | import { handleJoinRoom } from '../rooms.js'; 5 | 6 | export interface ApiParamsJoinRoomGet { 7 | roomId: string; 8 | userId: string; 9 | } 10 | export const joinRoomGetHandler = async ( 11 | req: Request, 12 | res: Response, 13 | matrixClient: MatrixClient 14 | ) => { 15 | const { roomId, userId } = req.params as unknown as ApiParamsJoinRoomGet; 16 | const result = await handleJoinRoom(matrixClient, roomId); 17 | }; 18 | -------------------------------------------------------------------------------- /packages/aggregator/src/constants.ts: -------------------------------------------------------------------------------- 1 | import dotenv from 'dotenv'; 2 | dotenv.config(); 3 | 4 | export const dev = process.env.NODE_ENV !== 'production'; 5 | 6 | const password = process.env.PASSWORD || ''; 7 | const userId = process.env.USER_ID || ''; 8 | const baseUrl = process.env.BASE_URL || 'http://localhost:8888'; 9 | 10 | if (!password) { 11 | throw new Error('PASSWORD env var is required'); 12 | } 13 | if (!userId) { 14 | throw new Error('USER_ID env var is required'); 15 | } 16 | 17 | export const MATRIX_CONFIG = { 18 | password, 19 | userId, 20 | baseUrl, 21 | }; 22 | 23 | export const MONGO_URL = process.env.MONGO_URL || ''; 24 | if (!MONGO_URL) throw new Error('missing MONGO_URL env var'); 25 | 26 | export const PORT = dev ? 3333 : process.env.PORT; 27 | if (!PORT) throw new Error('missing PORT env var'); 28 | -------------------------------------------------------------------------------- /packages/aggregator/src/helpers.ts: -------------------------------------------------------------------------------- 1 | export const logger = (data: any, level: 'info' | 'error' = 'info') => { 2 | // eslint-disable-next-line no-console 3 | level === 'info' ? console.log(data) : console.error(data); 4 | }; 5 | 6 | export const wait = (ms: number) => 7 | new Promise((resolve) => setTimeout(resolve, ms)); 8 | -------------------------------------------------------------------------------- /packages/aggregator/src/main.test.ts: -------------------------------------------------------------------------------- 1 | import { it, describe, expect } from 'vitest'; 2 | import request from 'supertest'; 3 | import app from './main'; 4 | 5 | describe('GET /ping', () => { 6 | it('ping returns pong', async () => { 7 | const response = await request(app.server).get('/ping'); 8 | expect(response.status).toBe(200); 9 | expect(response.text).toBe('pong'); 10 | }); 11 | 12 | it('on startup joins a'); 13 | 14 | it('when a user creates a public room, they invite this aggregator server to the room and the aggregator server syncs the rooms data to the aggregator database', () => { 15 | const request = {}; 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/aggregator/src/main.ts: -------------------------------------------------------------------------------- 1 | import { MATRIX_CONFIG } from './constants.js'; 2 | import { startMatrixClient } from './matrix.js'; 3 | import { startServer } from './server.js'; 4 | import { connectAllJoinedRooms } from './rooms.js'; 5 | import { joinRoomGetHandler } from './api/join-room-get.js'; 6 | 7 | export const startApp = async () => { 8 | const server = startServer(); 9 | 10 | const matrixClient = await startMatrixClient(MATRIX_CONFIG); 11 | 12 | await connectAllJoinedRooms(matrixClient); 13 | 14 | // endpoints 15 | server.use((req, res) => joinRoomGetHandler(req, res, matrixClient)); 16 | 17 | return { server, matrixClient }; 18 | }; 19 | 20 | const app = await startApp(); 21 | 22 | export default app; 23 | -------------------------------------------------------------------------------- /packages/aggregator/src/seed.ts: -------------------------------------------------------------------------------- 1 | export const seed = () => { 2 | // 3 | }; 4 | -------------------------------------------------------------------------------- /packages/aggregator/src/server.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { PORT } from './constants'; 3 | 4 | export const startServer = () => { 5 | const server = express(); 6 | 7 | server.use(express.json()); 8 | 9 | server.get('/ping', (_req, res) => { 10 | res.send('pong'); 11 | }); 12 | 13 | server.listen(PORT, () => { 14 | // eslint-disable-next-line no-console 15 | return console.log(`Server started on port: ${PORT}`); 16 | }); 17 | return server; 18 | }; 19 | -------------------------------------------------------------------------------- /packages/aggregator/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./src" 6 | }, 7 | "exclude": [ 8 | "node_modules", 9 | "dist", 10 | "vitest.config.ts", 11 | "**/*.spec.ts", 12 | "**/*.test.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /packages/aggregator/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "outDir": "./dist", 6 | "strict": true, 7 | "esModuleInterop": true, 8 | "moduleResolution": "node", 9 | "skipLibCheck": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "rootDir": "./src" 12 | }, 13 | "include": ["./src/**/*.ts"] 14 | } 15 | -------------------------------------------------------------------------------- /packages/aggregator/vitest.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: ['vitest-plugin-ts'], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/auth-server/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "@eweser/eslint-config-react-ts"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/auth-server/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /packages/auth-server/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # auth-server 2 | 3 | ## 1.14.0 4 | 5 | ### Minor Changes 6 | 7 | - align versions 8 | 9 | ### Patch Changes 10 | 11 | - Updated dependencies 12 | - @eweser/db@1.14.0 13 | - @eweser/eslint-config-react-ts@1.14.0 14 | - @eweser/shared@1.14.0 15 | 16 | ## 1.13.5 17 | 18 | ### Patch Changes 19 | 20 | - bump 21 | - Updated dependencies 22 | - @eweser/db@1.13.6 23 | - @eweser/eslint-config-react-ts@1.13.6 24 | - @eweser/shared@1.13.6 25 | 26 | ## 1.13.4 27 | 28 | ### Patch Changes 29 | 30 | - registry sync 31 | - Updated dependencies 32 | - @eweser/db@1.13.5 33 | - @eweser/eslint-config-react-ts@1.13.5 34 | - @eweser/shared@1.13.5 35 | 36 | ## 1.13.3 37 | 38 | ### Patch Changes 39 | 40 | - add rolling sync 41 | - Updated dependencies 42 | - @eweser/db@1.13.4 43 | - @eweser/eslint-config-react-ts@1.13.4 44 | - @eweser/shared@1.13.4 45 | -------------------------------------------------------------------------------- /packages/auth-server/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /packages/auth-server/docs/images/enable-provider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eweser/eweser-db/00565867c8c2f36cda7dea91e9247915783a497a/packages/auth-server/docs/images/enable-provider.png -------------------------------------------------------------------------------- /packages/auth-server/docs/images/site-url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eweser/eweser-db/00565867c8c2f36cda7dea91e9247915783a497a/packages/auth-server/docs/images/site-url.png -------------------------------------------------------------------------------- /packages/auth-server/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | transpilePackages: ['@eweser/shared', '@eweser/db'], 4 | webpack: (config, { dev }) => { 5 | if (dev) { 6 | config.devtool = 'source-map'; 7 | } 8 | return config; 9 | }, 10 | headers: async () => { 11 | return [ 12 | { 13 | source: '/(.*)', 14 | headers: [ 15 | { key: 'Access-Control-Allow-Credentials', value: 'true' }, 16 | { key: 'Access-Control-Allow-Origin', value: '*' }, 17 | { 18 | key: 'Access-Control-Allow-Methods', 19 | value: 'GET,OPTIONS,PATCH,DELETE,POST,PUT', 20 | }, 21 | { 22 | key: 'Access-Control-Allow-Headers', 23 | value: 24 | 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, Authorization, Dnt, Referer, User-Agent', 25 | }, 26 | ], 27 | }, 28 | ]; 29 | }, 30 | }; 31 | 32 | export default nextConfig; 33 | -------------------------------------------------------------------------------- /packages/auth-server/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /packages/auth-server/public/eweser-db-logo-head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eweser/eweser-db/00565867c8c2f36cda7dea91e9247915783a497a/packages/auth-server/public/eweser-db-logo-head.png -------------------------------------------------------------------------------- /packages/auth-server/public/eweser-db-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eweser/eweser-db/00565867c8c2f36cda7dea91e9247915783a497a/packages/auth-server/public/eweser-db-logo.png -------------------------------------------------------------------------------- /packages/auth-server/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eweser/eweser-db/00565867c8c2f36cda7dea91e9247915783a497a/packages/auth-server/public/favicon.ico -------------------------------------------------------------------------------- /packages/auth-server/src/app/access-grant/permission/actions.tsx: -------------------------------------------------------------------------------- 1 | 'use server'; 2 | 3 | import type { ThirdPartyAppPermissions } from '../../../modules/account/access-grant/create-third-party-app-permissions'; 4 | import { createOrUpdateThirdPartyAppPermissions } from '../../../modules/account/access-grant/create-third-party-app-permissions'; 5 | 6 | export async function submitPermissionsChange( 7 | permissions: ThirdPartyAppPermissions, 8 | redirect: string 9 | ) { 10 | const token = await createOrUpdateThirdPartyAppPermissions(permissions); 11 | const redirectUrl = new URL(redirect); 12 | redirectUrl.searchParams.set('token', token); 13 | // console.log({ token, redirectUrl: redirectUrl.toString() }); 14 | return redirectUrl.toString(); 15 | } 16 | -------------------------------------------------------------------------------- /packages/auth-server/src/app/access-grant/ping/route.ts: -------------------------------------------------------------------------------- 1 | export async function GET() { 2 | return Response.json({ reply: 'pong' }); 3 | } 4 | 5 | export function OPTIONS() { 6 | return new Response('ok', { 7 | status: 200, 8 | headers: { 9 | 'Access-Control-Allow-Origin': '*', 10 | 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 11 | 'Access-Control-Allow-Headers': 'Content-Type, Authorization', 12 | }, 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /packages/auth-server/src/app/access-grant/sync-registry/route.ts: -------------------------------------------------------------------------------- 1 | import { syncRoomsWithClient } from '../../../modules/rooms/sync-rooms-with-client'; 2 | import { authTokenFromHeaders, serverRouteError } from '../../../shared/utils'; 3 | import type { 4 | RegistrySyncRequestBody, 5 | RegistrySyncResponse, 6 | } from '@eweser/shared'; 7 | 8 | export async function POST(request: Request) { 9 | const token = authTokenFromHeaders(request.headers); 10 | if (!token) { 11 | return serverRouteError('No token provided', 401); 12 | } 13 | 14 | // TODO: add validation 15 | const { rooms } = (await request.json()) as RegistrySyncRequestBody; 16 | const { 17 | rooms: newRooms, 18 | token: newToken, 19 | userId, 20 | } = await syncRoomsWithClient(token, rooms); 21 | const response: RegistrySyncResponse = { 22 | rooms: newRooms, 23 | token: newToken, 24 | userId, 25 | }; 26 | 27 | return Response.json(response); 28 | } 29 | 30 | export function OPTIONS() { 31 | return new Response('ok', { status: 200 }); 32 | } 33 | -------------------------------------------------------------------------------- /packages/auth-server/src/app/auth/await-confirm/page.tsx: -------------------------------------------------------------------------------- 1 | function AwaitConfirmEmailPage() { 2 | return ( 3 |
4 |

Confirm your email

5 |

6 | We have sent you an email with a link to confirm your email address. 7 |

8 |

9 | Please check your inbox and click on the link to confirm your email. 10 |

11 |
12 | ); 13 | } 14 | 15 | export default AwaitConfirmEmailPage; 16 | -------------------------------------------------------------------------------- /packages/auth-server/src/app/auth/callback/[next]/route.ts: -------------------------------------------------------------------------------- 1 | import type { NextRequest } from 'next/server'; 2 | import { NextResponse } from 'next/server'; 3 | import { handleServerErrorRedirect } from '../../../../shared/utils'; 4 | import { oAuthLoginCallback } from '../../../../modules/account/oauth/login-callback'; 5 | 6 | export async function GET( 7 | request: NextRequest, 8 | { params }: { params: { next?: string } } 9 | ) { 10 | const { searchParams } = new URL(request.url); 11 | const code = searchParams.get('code'); 12 | // if "next" is in param, use it as the redirect URL 13 | const next = params.next ?? '/home'; 14 | 15 | const redirectTo = request.nextUrl.clone(); 16 | redirectTo.pathname = next; 17 | redirectTo.searchParams.delete('code'); 18 | 19 | const { error } = await oAuthLoginCallback({ code }); 20 | if (error) { 21 | return handleServerErrorRedirect(error, redirectTo); 22 | } 23 | 24 | return NextResponse.redirect(redirectTo); 25 | } 26 | -------------------------------------------------------------------------------- /packages/auth-server/src/app/auth/callback/route.ts: -------------------------------------------------------------------------------- 1 | import type { NextRequest } from 'next/server'; 2 | import { NextResponse } from 'next/server'; 3 | import { handleServerErrorRedirect } from '../../../shared/utils'; 4 | 5 | import { oAuthLoginCallback } from '../../../modules/account/oauth/login-callback'; 6 | 7 | export async function GET(request: NextRequest) { 8 | const { searchParams } = new URL(request.url); 9 | const code = searchParams.get('code'); 10 | const next = '/home'; 11 | 12 | const redirectTo = request.nextUrl.clone(); 13 | redirectTo.pathname = next; 14 | redirectTo.searchParams.delete('code'); 15 | 16 | const { error } = await oAuthLoginCallback({ code }); 17 | if (error) { 18 | return handleServerErrorRedirect(error, redirectTo); 19 | } 20 | 21 | return NextResponse.redirect(redirectTo); 22 | } 23 | -------------------------------------------------------------------------------- /packages/auth-server/src/app/auth/confirm/route.ts: -------------------------------------------------------------------------------- 1 | 'use server'; 2 | import { verifyOtp } from '../../../modules/account/verify-otp'; 3 | import { handleServerErrorRedirect } from '../../../shared/utils'; 4 | import { type EmailOtpType } from '@supabase/supabase-js'; 5 | import { type NextRequest, NextResponse } from 'next/server'; 6 | 7 | export async function GET(request: NextRequest) { 8 | const { searchParams } = new URL(request.url); 9 | const token_hash = searchParams.get('token_hash'); 10 | const type = searchParams.get('type') as EmailOtpType | null; 11 | const next = searchParams.get('next') ?? '/home'; 12 | 13 | const redirectTo = request.nextUrl.clone(); 14 | redirectTo.pathname = next; 15 | redirectTo.searchParams.delete('token_hash'); 16 | redirectTo.searchParams.delete('type'); 17 | 18 | const { error } = await verifyOtp({ token_hash, type }); 19 | 20 | if (error) { 21 | return handleServerErrorRedirect(error, redirectTo); 22 | } 23 | redirectTo.searchParams.delete('next'); 24 | return NextResponse.redirect(redirectTo); 25 | } 26 | -------------------------------------------------------------------------------- /packages/auth-server/src/app/error/page.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from '../../frontend/components/library/button'; 2 | import Link from 'next/link'; 3 | 4 | export default function ErrorPage({ 5 | searchParams, 6 | }: { 7 | searchParams: { message?: string }; 8 | }) { 9 | const message = searchParams.message; 10 | 11 | return ( 12 |
13 |

Error

14 |
15 | {message || 'An error occurred.'} 16 |
17 | 20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /packages/auth-server/src/app/home/page.tsx: -------------------------------------------------------------------------------- 1 | import { ProfileView } from '../../frontend/components/profile/profile-view'; 2 | import { protectPage } from '../../modules/account/protect-page'; 3 | 4 | import type { Metadata } from 'next'; 5 | 6 | export const metadata: Metadata = { 7 | title: 'Home', 8 | }; 9 | 10 | export default async function Home() { 11 | const user = await protectPage(); 12 | 13 | return ; 14 | } 15 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/components/access-grant/permissions-unchanged-redirect.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | import { useRouter } from 'next/navigation'; 3 | import { useEffect } from 'react'; 4 | import H2 from '../library/typography-h2'; 5 | import { Icons } from '../library/icons'; 6 | 7 | export function PermissionsUnchangedRedirect({ 8 | redirectUrl, 9 | }: { 10 | redirectUrl: string; 11 | }) { 12 | const router = useRouter(); 13 | useEffect(() => { 14 | setTimeout(() => { 15 | router.replace(redirectUrl); 16 | }, 1000); 17 | }); 18 | return ( 19 |
20 |

Permission already granted.

21 |

Redirecting you now...

22 | 23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/components/landing-page-hero.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | 3 | export default function LandingPageHero() { 4 | return ( 5 | <> 6 |
7 | eweser-db-logo 13 |
14 | 15 |

A user-owned database. Just for ewe

16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/components/library/input.tsx: -------------------------------------------------------------------------------- 1 | 'use-client'; 2 | import * as React from 'react'; 3 | 4 | import { cn } from '../../../shared/utils'; 5 | 6 | export interface InputProps 7 | extends React.InputHTMLAttributes { 8 | className?: string; 9 | } 10 | 11 | const Input = React.forwardRef( 12 | ({ className, type, ...props }, ref) => { 13 | return ( 14 | 23 | ); 24 | } 25 | ); 26 | Input.displayName = 'Input'; 27 | 28 | export { Input }; 29 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/components/library/label.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import * as React from 'react'; 4 | import * as LabelPrimitive from '@radix-ui/react-label'; 5 | import { cva, type VariantProps } from 'class-variance-authority'; 6 | 7 | import { cn } from '../../../shared/utils'; 8 | 9 | const labelVariants = cva( 10 | 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70' 11 | ); 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & 16 | VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 23 | )); 24 | Label.displayName = LabelPrimitive.Root.displayName; 25 | 26 | export { Label }; 27 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/components/library/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from '../../../shared/utils'; 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ); 13 | } 14 | 15 | export { Skeleton }; 16 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/components/library/typography-blockquote.tsx: -------------------------------------------------------------------------------- 1 | export interface BlockquoteProps 2 | extends React.HTMLAttributes { 3 | children: React.ReactNode; 4 | } 5 | 6 | export default function Blockquote({ children, className }: BlockquoteProps) { 7 | return ( 8 |
9 | {children} 10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/components/library/typography-h1.tsx: -------------------------------------------------------------------------------- 1 | export default function H1({ 2 | children, 3 | className, 4 | ...props 5 | }: React.HTMLAttributes) { 6 | return ( 7 |

11 | {children} 12 |

13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/components/library/typography-h2.tsx: -------------------------------------------------------------------------------- 1 | export default function H2({ children }: { children: React.ReactNode }) { 2 | return ( 3 |

4 | {children} 5 |

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/components/library/typography-h3.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from '../../../shared/utils'; 2 | 3 | export default function H3({ 4 | children, 5 | className, 6 | }: { 7 | children: React.ReactNode; 8 | className?: string; 9 | }) { 10 | return ( 11 |

17 | {children}{' '} 18 |

19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/components/library/typography-h4.tsx: -------------------------------------------------------------------------------- 1 | export default function H4({ children }: { children: React.ReactNode }) { 2 | return ( 3 |

4 | {children}{' '} 5 |

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/components/library/typography-inline-code.tsx: -------------------------------------------------------------------------------- 1 | export default function InlineCode({ 2 | children, 3 | }: { 4 | children: React.ReactNode; 5 | }) { 6 | return ( 7 | 8 | {children} 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/components/library/typography-large.tsx: -------------------------------------------------------------------------------- 1 | export default function Large({ children }: { children: React.ReactNode }) { 2 | return
{children}
; 3 | } 4 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/components/library/typography-lead.tsx: -------------------------------------------------------------------------------- 1 | export default function Lead({ children }: { children: React.ReactNode }) { 2 | return

{children}

; 3 | } 4 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/components/library/typography-list.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * supply with a list of
  • elements like @example <>
  • item 1
  • item 2
  • 3 | */ 4 | export default function List({ children }: { children: React.ReactNode }) { 5 | return
      {children}
    ; 6 | } 7 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/components/library/typography-muted.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from '../../../shared/utils'; 2 | 3 | export default function Muted({ 4 | children, 5 | className, 6 | }: { 7 | children: React.ReactNode; 8 | className?: string; 9 | }) { 10 | return ( 11 |

    12 | {' '} 13 | {children} 14 |

    15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/components/library/typography-p.tsx: -------------------------------------------------------------------------------- 1 | export default function P({ 2 | children, 3 | className, 4 | ...props 5 | }: React.HTMLAttributes) { 6 | return ( 7 |

    11 | {children} 12 |

    13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/components/library/typography-small.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from '../../../shared/utils'; 2 | 3 | export default function Small({ 4 | children, 5 | className, 6 | }: { 7 | children: React.ReactNode; 8 | className?: string; 9 | }) { 10 | return ( 11 | 12 | {children} 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/components/theme-toggle.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import * as React from 'react'; 4 | import { Moon, Sun } from 'lucide-react'; 5 | import { useTheme } from 'next-themes'; 6 | 7 | import { Button } from './library/button'; 8 | 9 | export function ThemeToggle() { 10 | const { setTheme, theme } = useTheme(); 11 | 12 | return ( 13 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/config/site.ts: -------------------------------------------------------------------------------- 1 | export type SiteConfig = typeof siteConfig; 2 | const name = 'Eweser DB'; 3 | export const siteConfig = { 4 | name, 5 | pageName: (page: string) => `${page} | ${name}`, 6 | description: 'EweserDB, the user-owned database. Just for ewe 🐑', 7 | mainNav: [ 8 | { 9 | title: 'Home', 10 | href: '/home', 11 | }, 12 | ], 13 | links: { 14 | github: 'https://github.com/eweser/eweser-db', 15 | }, 16 | icons: { 17 | icon: '/favicon.ico', 18 | shortcut: '/eweser-db-logo.png', 19 | apple: '/eweser-db-logo-small.svg', 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/styles/fonts.ts: -------------------------------------------------------------------------------- 1 | import { 2 | JetBrains_Mono as FontMono, 3 | Inter as FontSans, 4 | } from 'next/font/google'; 5 | 6 | export const fontSans = FontSans({ 7 | subsets: ['latin'], 8 | variable: '--font-sans', 9 | }); 10 | 11 | export const fontMono = FontMono({ 12 | subsets: ['latin'], 13 | variable: '--font-mono', 14 | }); 15 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/theme-provider.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import * as React from 'react'; 4 | import { 5 | ThemeProvider as NextThemesProvider, 6 | ThemeProviderProps, 7 | } from 'next-themes'; 8 | 9 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) { 10 | return {children}; 11 | } 12 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/utils.ts: -------------------------------------------------------------------------------- 1 | export function capitalizeFirstLetter(str: string): string { 2 | return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); 3 | } 4 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/utils/debounce.ts: -------------------------------------------------------------------------------- 1 | export function debounce(func: (...args: any[]) => void, wait: number) { 2 | let timeout: NodeJS.Timeout; 3 | 4 | const debouncedFunction = function (...args: any[]) { 5 | clearTimeout(timeout); 6 | 7 | timeout = setTimeout(() => func(...args), wait); 8 | }; 9 | 10 | debouncedFunction.cancel = () => { 11 | clearTimeout(timeout); 12 | }; 13 | 14 | return debouncedFunction; 15 | } 16 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/utils/frontend-url-utils.ts: -------------------------------------------------------------------------------- 1 | import { 2 | loginOptionsToQueryParams, 3 | type LoginQueryOptions, 4 | } from '@eweser/shared'; 5 | import { AUTH_SERVER_URL } from '../../shared/constants'; 6 | 7 | export function loginOptionsToPermissionPageUrl( 8 | loginQueryOptions: LoginQueryOptions 9 | ) { 10 | const redirectUrl = new URL(AUTH_SERVER_URL + '/access-grant/permission'); 11 | const queryParams = loginOptionsToQueryParams(loginQueryOptions); 12 | Object.entries(queryParams).forEach(([key, value]) => { 13 | redirectUrl.searchParams.append(key, value); 14 | }); 15 | return redirectUrl.toString(); 16 | } 17 | -------------------------------------------------------------------------------- /packages/auth-server/src/frontend/utils/local-storage.ts: -------------------------------------------------------------------------------- 1 | import type { LoginQueryOptions } from '@eweser/shared'; 2 | import { loginOptionsToQueryParams } from '@eweser/shared'; 3 | import { validateLoginQueryOptions } from '../../shared/utils'; 4 | 5 | const LOGIN_QUERY_KEY = 'loginquery'; 6 | export function setLocalStorageLoginQuery(query: LoginQueryOptions) { 7 | localStorage.setItem( 8 | LOGIN_QUERY_KEY, 9 | JSON.stringify(loginOptionsToQueryParams(query)) 10 | ); 11 | } 12 | export function getLocalStorageLoginQuery(): LoginQueryOptions | null { 13 | const queryParamsString = localStorage.getItem(LOGIN_QUERY_KEY); 14 | const params = queryParamsString ? JSON.parse(queryParamsString) : null; 15 | return validateLoginQueryOptions(params); 16 | } 17 | export function clearLocalStorageLoginQuery() { 18 | localStorage.removeItem(LOGIN_QUERY_KEY); 19 | } 20 | -------------------------------------------------------------------------------- /packages/auth-server/src/model/apps.ts: -------------------------------------------------------------------------------- 1 | import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'; 2 | import { db } from '../services/database'; 3 | 4 | export const apps = pgTable('apps', { 5 | id: uuid('id').primaryKey().notNull(), 6 | createdAt: timestamp('created_at', { 7 | withTimezone: true, 8 | mode: 'string', 9 | }) 10 | .notNull() 11 | .defaultNow(), 12 | domain: text('domain').notNull().unique(), 13 | }); 14 | 15 | export async function getAllAppDomains(): Promise { 16 | const allApps = await db().select({ domain: apps.domain }).from(apps); 17 | return allApps.map((app) => app.domain); 18 | } 19 | -------------------------------------------------------------------------------- /packages/auth-server/src/model/rooms/helpers.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_ROOM_TTL_PERIOD = 1000 * 60 * 60 * 24 * 30; // 30 days 2 | export const generateTTLDate: (ttlMs?: number) => string = (ttlMs) => 3 | new Date( 4 | new Date().getTime() + (ttlMs || DEFAULT_ROOM_TTL_PERIOD) 5 | ).toISOString(); 6 | -------------------------------------------------------------------------------- /packages/auth-server/src/model/rooms/validation.ts: -------------------------------------------------------------------------------- 1 | import { createInsertSchema } from 'drizzle-zod'; 2 | import { z } from 'zod'; 3 | import { rooms } from './schema'; 4 | 5 | export const roomInsertSchema = createInsertSchema(rooms, { 6 | // drizzle-zod doesn't handle arrays well, have to manually define them 7 | readAccess: z.string().array(), 8 | writeAccess: z.string().array(), 9 | adminAccess: z.string().array(), 10 | }); 11 | export type RoomInsert = typeof roomInsertSchema._type; 12 | 13 | export const roomUpdateSchema = roomInsertSchema.partial().extend({ 14 | id: z.string(), 15 | }); 16 | 17 | export type RoomUpdate = typeof roomUpdateSchema._type; 18 | 19 | export async function validateRoomInsert(insert: RoomInsert) { 20 | const res = roomInsertSchema.safeParse(insert); 21 | if (!res.success) { 22 | throw new Error(res.error.toString()); 23 | } 24 | return res.data; 25 | } 26 | 27 | export async function validateRoomUpdate(update: RoomUpdate) { 28 | const res = roomUpdateSchema.safeParse(update); 29 | if (!res.success) { 30 | throw new Error(res.error.toString()); 31 | } 32 | return res.data; 33 | } 34 | -------------------------------------------------------------------------------- /packages/auth-server/src/modules/account/access-grant/create-room-invite-link.ts: -------------------------------------------------------------------------------- 1 | import jwt from 'jsonwebtoken'; 2 | import { SERVER_SECRET } from '../../../shared/server-constants'; 3 | import type { CreateRoomInviteBody } from '@eweser/shared'; 4 | import { AUTH_SERVER_DOMAIN } from '../../../shared/constants'; 5 | 6 | export function createRoomInviteLink(options: CreateRoomInviteBody) { 7 | const inviteToken = jwt.sign(options, SERVER_SECRET); 8 | const url = new URL( 9 | '/access-grant/accept-room-invite', 10 | `http${ 11 | process.env.NODE_ENV === 'development' ? '' : 's' 12 | }://${AUTH_SERVER_DOMAIN}` 13 | ); 14 | url.searchParams.set('token', inviteToken); 15 | return url.toString(); 16 | } 17 | -------------------------------------------------------------------------------- /packages/auth-server/src/modules/account/access-grant/roomNotFoundError.ts: -------------------------------------------------------------------------------- 1 | export const roomNotFoundError = 'Room not found'; 2 | export const notAdminOfRoomError = 'You are not an admin of this room'; 3 | -------------------------------------------------------------------------------- /packages/auth-server/src/modules/account/get-login-status.ts: -------------------------------------------------------------------------------- 1 | import { backendSupabase } from '../../services/database/supabase/backend-client-init'; 2 | 3 | /** 4 | * Can only be used backend 5 | */ 6 | export async function getLoginStatus() { 7 | try { 8 | const supabase = await backendSupabase(); 9 | const { data } = await supabase.auth.getUser(); 10 | const loggedIn = !!data?.user?.id; 11 | return loggedIn; 12 | } catch (error) { 13 | return false; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/auth-server/src/modules/account/get-session-user.ts: -------------------------------------------------------------------------------- 1 | import { backendSupabase } from '../../services/database/supabase/backend-client-init'; 2 | 3 | export async function getSessionUser() { 4 | const supabase = await backendSupabase(); 5 | 6 | const { data, error } = await supabase.auth.getUser(); 7 | if (error) { 8 | return { error }; 9 | } 10 | if (!data.user) { 11 | return { error: new Error('Could not find user') }; 12 | } 13 | return { user: data.user }; 14 | } 15 | -------------------------------------------------------------------------------- /packages/auth-server/src/modules/account/oauth/hooks/use-oauth-login.ts: -------------------------------------------------------------------------------- 1 | import { frontendSupabase } from '../../../../services/database/supabase/frontend-client-init'; 2 | 3 | export const OAUTH_PROVIDERS = ['google', 'github'] as const; 4 | 5 | export type Provider = (typeof OAUTH_PROVIDERS)[number]; 6 | 7 | export function useOauthLogin({ 8 | setIsLoading, 9 | }: { 10 | setIsLoading: (loading: boolean) => void; 11 | }) { 12 | const supabase = frontendSupabase(); 13 | 14 | const handleOauthLogin = async (provider: Provider) => { 15 | setIsLoading(true); 16 | const redirectTo = `${window.location.origin}/auth/callback`; 17 | try { 18 | await supabase.auth.signInWithOAuth({ 19 | provider, 20 | options: { redirectTo }, 21 | }); 22 | } catch (error: any) { 23 | alert(error.message || 'An error occurred. Please try again.'); 24 | } 25 | setIsLoading(false); 26 | }; 27 | return { handleOauthLogin }; 28 | } 29 | -------------------------------------------------------------------------------- /packages/auth-server/src/modules/account/password/check-email.ts: -------------------------------------------------------------------------------- 1 | import { getUserByEmail } from '../../../model/users'; 2 | 3 | /** 4 | * Check if an email is already in use 5 | */ 6 | export async function checkEmail(email: string) { 7 | try { 8 | const user = await getUserByEmail(email); 9 | if (user) { 10 | return true; 11 | } 12 | } catch (e) { 13 | // User not found 14 | } 15 | return false; 16 | } 17 | -------------------------------------------------------------------------------- /packages/auth-server/src/modules/account/password/sign-in.ts: -------------------------------------------------------------------------------- 1 | import { backendSupabase } from '../../../services/database/supabase/backend-client-init'; 2 | 3 | export async function passwordSignIn(formData: FormData) { 4 | const supabase = await backendSupabase(); 5 | // type-casting here for convenience 6 | // in practice, you should validate your inputs 7 | const loginData = { 8 | email: formData.get('email') as string, 9 | password: formData.get('password') as string, 10 | }; 11 | if (!loginData.email || !loginData.password) { 12 | return { error: new Error('Email and password are required') }; 13 | } 14 | return await supabase.auth.signInWithPassword(loginData); 15 | } 16 | -------------------------------------------------------------------------------- /packages/auth-server/src/modules/account/password/sign-up.ts: -------------------------------------------------------------------------------- 1 | import { cookies } from 'next/headers'; 2 | import { backendSupabase } from '../../../services/database/supabase/backend-client-init'; 3 | 4 | export async function passwordSignUp(formData: FormData) { 5 | const supabase = await backendSupabase(); 6 | 7 | // type-casting here for convenience 8 | // in practice, you should validate your inputs 9 | const signupData = { 10 | email: formData.get('email') as string, 11 | password: formData.get('password') as string, 12 | }; 13 | return await supabase.auth.signUp(signupData); 14 | } 15 | -------------------------------------------------------------------------------- /packages/auth-server/src/modules/account/protect-page.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from 'next/navigation'; 2 | import { getSessionUser } from './get-session-user'; 3 | 4 | export async function protectPage() { 5 | const { error, user } = await getSessionUser(); 6 | 7 | if (error) { 8 | return redirect( 9 | `/error?message=${error?.message?.toString() || 'unauthenticated'}` 10 | ); 11 | } 12 | return user; 13 | } 14 | -------------------------------------------------------------------------------- /packages/auth-server/src/modules/account/verify-otp.ts: -------------------------------------------------------------------------------- 1 | 'use server'; 2 | 3 | import type { EmailOtpType } from '@supabase/supabase-js'; 4 | import { createNewUserRoomsAndAuthServerAccess } from './create-new-user-rooms-and-auth-server-access'; 5 | import { backendSupabase } from '../../services/database/supabase/backend-client-init'; 6 | import { logger } from '../../shared/utils'; 7 | 8 | export async function verifyOtp({ 9 | token_hash, 10 | type, 11 | }: { 12 | token_hash: string | null; 13 | type: EmailOtpType | null; 14 | }) { 15 | if (!token_hash || !type) { 16 | return { error: new Error('Invalid token_hash or type') }; 17 | } 18 | const supabase = await backendSupabase(); 19 | 20 | const { error, data } = await supabase.auth.verifyOtp({ 21 | type, 22 | token_hash, 23 | }); 24 | if (error) { 25 | return { error }; 26 | } else if (!data.session?.user.id) { 27 | return { error: new Error('Invalid session') }; 28 | } 29 | try { 30 | await createNewUserRoomsAndAuthServerAccess(data.session.user.id); 31 | } catch (error) { 32 | logger(error); 33 | } 34 | return {}; 35 | } 36 | -------------------------------------------------------------------------------- /packages/auth-server/src/modules/rooms/get-or-create-rooms.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * check database for room record. 3 | * return room token if found 4 | * create room if not found 5 | * get room token from y-sweet if token is not found in room 6 | * save room token to database 7 | * return room token 8 | */ 9 | export async function getOrCreateRoom() { 10 | // const existingRoom = await getRoomsByUserId(userId); 11 | } 12 | -------------------------------------------------------------------------------- /packages/auth-server/src/services/database/index.ts: -------------------------------------------------------------------------------- 1 | // The default database client uses Drizzle because the Supabase DB client relies on an unreliable API. Generally, the Supabase client should only be used for authentication and storage operations. For database CRUD operations, use Drizzle. 2 | export { db } from './drizzle/init'; 3 | -------------------------------------------------------------------------------- /packages/auth-server/src/services/database/supabase/backend-client-init.ts: -------------------------------------------------------------------------------- 1 | import { 2 | NEXT_PUBLIC_SUPABASE_ANON_KEY, 3 | NEXT_PUBLIC_SUPABASE_URL, 4 | } from '../../../services/database/supabase/frontend-config'; 5 | import { createServerClient } from '@supabase/ssr'; 6 | import { cookies } from 'next/headers'; 7 | 8 | export async function backendSupabase() { 9 | const cookieStore = await cookies(); 10 | 11 | return createServerClient( 12 | NEXT_PUBLIC_SUPABASE_URL, 13 | NEXT_PUBLIC_SUPABASE_ANON_KEY, 14 | { 15 | cookies: { 16 | getAll() { 17 | return cookieStore.getAll(); 18 | }, 19 | setAll(cookiesToSet) { 20 | try { 21 | cookiesToSet.forEach(({ name, value, options }) => 22 | cookieStore.set(name, value, options) 23 | ); 24 | } catch { 25 | // The `setAll` method was called from a Server Component. 26 | // This can be ignored if you have middleware refreshing 27 | // user sessions. 28 | } 29 | }, 30 | }, 31 | } 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /packages/auth-server/src/services/database/supabase/backend-config.ts: -------------------------------------------------------------------------------- 1 | const SUPABASE_CONNECTION_URL = process.env.SUPABASE_CONNECTION_URL ?? ''; 2 | 3 | if (!SUPABASE_CONNECTION_URL) { 4 | throw new Error('SUPABASE_CONNECTION_URL not set'); 5 | } 6 | 7 | const SUPABASE_SERVICE_ROLE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY ?? ''; 8 | 9 | if (!SUPABASE_SERVICE_ROLE_KEY) { 10 | throw new Error('SUPABASE_SERVICE_ROLE_KEY not set'); 11 | } 12 | 13 | export { SUPABASE_CONNECTION_URL, SUPABASE_SERVICE_ROLE_KEY }; 14 | -------------------------------------------------------------------------------- /packages/auth-server/src/services/database/supabase/frontend-client-init.ts: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { 4 | NEXT_PUBLIC_SUPABASE_URL, 5 | NEXT_PUBLIC_SUPABASE_ANON_KEY, 6 | } from '../../../services/database/supabase/frontend-config'; 7 | import { createBrowserClient } from '@supabase/ssr'; 8 | 9 | export function frontendSupabase() { 10 | return createBrowserClient( 11 | NEXT_PUBLIC_SUPABASE_URL, 12 | NEXT_PUBLIC_SUPABASE_ANON_KEY 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /packages/auth-server/src/services/database/supabase/frontend-config.ts: -------------------------------------------------------------------------------- 1 | const NEXT_PUBLIC_SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL ?? ''; 2 | const NEXT_PUBLIC_SUPABASE_ANON_KEY = 3 | process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY ?? ''; 4 | 5 | if (!NEXT_PUBLIC_SUPABASE_URL) { 6 | throw new Error('NEXT_PUBLIC_SUPABASE_URL is not defined'); 7 | } 8 | if (!NEXT_PUBLIC_SUPABASE_ANON_KEY) { 9 | throw new Error('NEXT_PUBLIC_SUPABASE_ANON_KEY is not defined'); 10 | } 11 | 12 | export { NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY }; 13 | -------------------------------------------------------------------------------- /packages/auth-server/src/services/database/supabase/middleware-client-init.ts: -------------------------------------------------------------------------------- 1 | import type { NextRequest, NextResponse } from 'next/server'; 2 | import { 3 | SUPABASE_CONNECTION_URL, 4 | SUPABASE_SERVICE_ROLE_KEY, 5 | } from './backend-config'; 6 | import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'; 7 | 8 | export function middlewareClient(req: NextRequest, res: NextResponse) { 9 | return createMiddlewareClient( 10 | { req, res }, 11 | { 12 | supabaseUrl: SUPABASE_CONNECTION_URL, 13 | supabaseKey: SUPABASE_SERVICE_ROLE_KEY, 14 | } 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/auth-server/src/services/y-sweet/config.ts: -------------------------------------------------------------------------------- 1 | const Y_SWEET_CONNECTION_STRING = process.env.Y_SWEET_CONNECTION_STRING ?? ''; 2 | if (!Y_SWEET_CONNECTION_STRING) { 3 | throw new Error('Y_SWEET_CONNECTION_STRING not set'); 4 | } 5 | 6 | export { Y_SWEET_CONNECTION_STRING }; 7 | -------------------------------------------------------------------------------- /packages/auth-server/src/shared/constants.ts: -------------------------------------------------------------------------------- 1 | export { COLLECTION_KEYS } from '@eweser/shared'; 2 | 3 | export const REQUESTER_TYPES = ['app', 'user'] as const; 4 | 5 | export const AUTH_SERVER_URL = process.env.NEXT_PUBLIC_AUTH_SERVER_URL ?? ''; 6 | 7 | if (!AUTH_SERVER_URL) { 8 | throw new Error('AUTH_SERVER_URL is not defined'); 9 | } 10 | export const AUTH_SERVER_DOMAIN = 11 | process.env.NEXT_PUBLIC_AUTH_SERVER_DOMAIN ?? ''; 12 | 13 | if (!AUTH_SERVER_DOMAIN) { 14 | throw new Error('AUTH_SERVER_DOMAIN is not defined'); 15 | } 16 | -------------------------------------------------------------------------------- /packages/auth-server/src/shared/server-constants.ts: -------------------------------------------------------------------------------- 1 | export const SERVER_SECRET = process.env.SERVER_SECRET ?? ''; 2 | if (!SERVER_SECRET) { 3 | throw new Error('SERVER_SECRET is not defined'); 4 | } 5 | -------------------------------------------------------------------------------- /packages/auth-server/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | 5 | # Local env files 6 | .env 7 | -------------------------------------------------------------------------------- /packages/auth-server/supabase/migrations/20240213144645_user_trigger.sql: -------------------------------------------------------------------------------- 1 | create 2 | or replace function public.handle_new_user () returns trigger as $$ 3 | declare 4 | _auth_server_url text; 5 | begin 6 | SELECT value INTO _auth_server_url FROM public.config WHERE key ='auth_server_url'; 7 | 8 | insert into public.users(id, auth_id, email) 9 | values (_auth_server_url||'|'||cast(new.id as text), new.id, new.email); 10 | return new; 11 | end; 12 | $$ language plpgsql security definer; 13 | 14 | create 15 | or replace trigger on_auth_user_created 16 | after 17 | insert on auth.users for each row 18 | execute 19 | procedure public.handle_new_user (); 20 | -------------------------------------------------------------------------------- /packages/auth-server/supabase/migrations/20240214082500_add_creator_id_to_room.sql: -------------------------------------------------------------------------------- 1 | alter table "public"."rooms" add column "creator" text not null; 2 | 3 | alter table "public"."rooms" add constraint "rooms_creator_fkey" FOREIGN KEY (creator) REFERENCES users(id) ON DELETE CASCADE not valid; 4 | 5 | alter table "public"."rooms" validate constraint "rooms_creator_fkey"; 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/auth-server/supabase/migrations/20240214131046_remove_doc_id_from_room.sql: -------------------------------------------------------------------------------- 1 | alter table "public"."rooms" drop column "doc_id"; 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/auth-server/supabase/migrations/20240215112831_fix_up_ids.sql: -------------------------------------------------------------------------------- 1 | alter table "public"."access_grants" drop constraint "access_grants_owner_id_fkey"; 2 | 3 | alter table "public"."rooms" drop constraint "rooms_creator_fkey"; 4 | 5 | alter table "public"."users" drop constraint "users_auth_id_fkey"; 6 | 7 | alter table "public"."access_grants" drop column "owner_id"; 8 | 9 | alter table "public"."rooms" drop column "creator"; 10 | 11 | alter table "public"."rooms" drop column "public"; 12 | 13 | alter table "public"."rooms" add column "public_access" text not null default 'private'::text; 14 | 15 | alter table "public"."rooms" alter column "id" set default gen_random_uuid(); 16 | 17 | alter table "public"."rooms" alter column "id" set data type uuid using "id"::uuid; 18 | 19 | alter table "public"."users" drop column "auth_id"; 20 | 21 | alter table "public"."users" add column "rooms" uuid[] not null; 22 | 23 | alter table "public"."users" alter column "id" set data type uuid using "id"::uuid; 24 | 25 | 26 | -------------------------------------------------------------------------------- /packages/auth-server/supabase/migrations/20240215144645_user_trigger_update.sql: -------------------------------------------------------------------------------- 1 | create 2 | or replace function public.handle_new_user () returns trigger as $$ 3 | begin 4 | insert into public.users(id, email) 5 | values (new.id, new.email); 6 | return new; 7 | end; 8 | $$ language plpgsql security definer; 9 | 10 | create 11 | or replace trigger on_auth_user_created 12 | after 13 | insert on auth.users for each row 14 | execute 15 | procedure public.handle_new_user (); 16 | -------------------------------------------------------------------------------- /packages/auth-server/supabase/migrations/20240215150955_add default rooms array.sql: -------------------------------------------------------------------------------- 1 | alter table "public"."users" alter column "rooms" set default '{}'::uuid[]; 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/auth-server/supabase/migrations/20240215170446_save_ysweet_url_to_room.sql: -------------------------------------------------------------------------------- 1 | alter table "public"."rooms" add column "y_sweet_url" text; 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/auth-server/supabase/migrations/20240216021805_link_user_id_to_auth_id.sql: -------------------------------------------------------------------------------- 1 | alter table "public"."users" add constraint "users_id_fkey" FOREIGN KEY (id) REFERENCES auth.users(id) ON DELETE CASCADE not valid; 2 | 3 | alter table "public"."users" validate constraint "users_id_fkey"; 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/auth-server/supabase/migrations/20240216170712_edit_access_grants_table.sql: -------------------------------------------------------------------------------- 1 | alter table "public"."access_grants" drop column "jwt_id"; 2 | 3 | alter table "public"."access_grants" add column "collections" text[] not null default '{}'::text[]; 4 | 5 | alter table "public"."access_grants" alter column "room_ids" set default '{}'::uuid[]; 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/auth-server/supabase/migrations/20240216171937_edit_access_grants_table_expires_keys.sql: -------------------------------------------------------------------------------- 1 | alter table "public"."access_grants" drop column "expires"; 2 | 3 | alter table "public"."access_grants" alter column "keep_alive_days" set data type bigint using "keep_alive_days"::bigint; 4 | 5 | alter table "public"."access_grants" alter column "keep_alive_days" set default '1'::bigint; 6 | 7 | -------------------------------------------------------------------------------- /packages/auth-server/supabase/migrations/20240217103308_add_owner_id_to_access_grant.sql: -------------------------------------------------------------------------------- 1 | alter table "public"."access_grants" add column "owner_id" uuid; 2 | 3 | alter table "public"."access_grants" add constraint "access_grants_owner_id_fkey" FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE CASCADE not valid; 4 | 5 | alter table "public"."access_grants" validate constraint "access_grants_owner_id_fkey"; 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/auth-server/supabase/migrations/20240220154616_add_room_deleted_flag.sql: -------------------------------------------------------------------------------- 1 | alter table "public"."rooms" add column "_deleted" boolean not null; 2 | 3 | alter table "public"."rooms" add column "_ttl" timestamp with time zone; 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/auth-server/supabase/migrations/20240222135756__deleted_nullable.sql: -------------------------------------------------------------------------------- 1 | alter table "public"."rooms" alter column "_deleted" drop not null; 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/auth-server/supabase/migrations/20240313144238_add_token_expiry_column.sql: -------------------------------------------------------------------------------- 1 | alter table "public"."rooms" add column "token_expiry" timestamp with time zone; 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/auth-server/supabase/migrations/20241216151304_change-y-sweet-token-columns.sql: -------------------------------------------------------------------------------- 1 | alter table "public"."rooms" drop column "token"; 2 | 3 | alter table "public"."rooms" add column "y_sweet_base_url" text; 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/auth-server/supabase/seed.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO apps (id, domain) VALUES (gen_random_uuid(), 'eweser.com'); 2 | INSERT INTO apps (id, domain) VALUES (gen_random_uuid(), 'eweser-db-example-basic.netlify.app'); -------------------------------------------------------------------------------- /packages/auth-server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "sourceMap": true, 16 | "plugins": [{ "name": "next" }], 17 | "typeRoots": ["./node_modules/@types"] 18 | }, 19 | "include": [ 20 | "next-env.d.ts", 21 | "**/*.ts", 22 | "**/*.tsx", 23 | ".next/types/**/*.ts", 24 | "../shared/src/utils/utils.test.ts" 25 | ], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /packages/auth-server/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "headers": [ 3 | { 4 | "source": "/access-grant/(.*)", 5 | "headers": [ 6 | { "key": "Access-Control-Allow-Credentials", "value": "true" }, 7 | { "key": "Access-Control-Allow-Origin", "value": "*" }, 8 | { 9 | "key": "Access-Control-Allow-Methods", 10 | "value": "GET,OPTIONS,PATCH,DELETE,POST,PUT" 11 | }, 12 | { 13 | "key": "Access-Control-Allow-Headers", 14 | "value": "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" 15 | } 16 | ] 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /packages/db/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@eweser/eslint-config-ts'], 3 | ignorePatterns: [ 4 | 'vite.config.*', 5 | 'types/**/*.d.ts', 6 | 'dist', 7 | 'setupTests.ts', 8 | '.eslintrc.cjs', 9 | ], 10 | }; 11 | -------------------------------------------------------------------------------- /packages/db/.gitignore: -------------------------------------------------------------------------------- 1 | !dist 2 | !types -------------------------------------------------------------------------------- /packages/db/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @eweser/db 2 | 3 | ## 1.14.0 4 | 5 | ### Minor Changes 6 | 7 | - align versions 8 | 9 | ### Patch Changes 10 | 11 | - Updated dependencies 12 | - @eweser/shared@1.14.0 13 | 14 | ## 1.13.6 15 | 16 | ### Patch Changes 17 | 18 | - bump 19 | - Updated dependencies 20 | - @eweser/shared@1.13.6 21 | 22 | ## 1.13.5 23 | 24 | ### Patch Changes 25 | 26 | - registry sync 27 | - Updated dependencies 28 | - @eweser/shared@1.13.5 29 | 30 | ## 1.13.4 31 | 32 | ### Patch Changes 33 | 34 | - add rolling sync 35 | - Updated dependencies 36 | - @eweser/shared@1.13.4 37 | 38 | ## 1.13.3 39 | 40 | ### Patch Changes 41 | 42 | - Default patch bump for all packages. 43 | - Updated dependencies 44 | - @eweser/shared@1.13.3 45 | -------------------------------------------------------------------------------- /packages/db/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 EduVault 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/db/src/methods/connection/getAccessGrantTokenFromUrl.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..'; 2 | import { setLocalAccessGrantToken } from '../../utils/localStorageService'; 3 | 4 | export const getAccessGrantTokenFromUrl = 5 | (db: Database) => 6 | /** 7 | * Pulls the access grant token from the url query params, clears the url query params, and saves the token to local storage 8 | */ 9 | () => { 10 | const query = new URLSearchParams(window?.location?.search ?? ''); 11 | const token = query.get('token'); 12 | if (token && typeof token === 'string') { 13 | setLocalAccessGrantToken(db)(token); 14 | } 15 | // remove from url 16 | if (window?.location?.search) { 17 | const url = new URL(window.location.href); 18 | for (const key of url.searchParams.keys()) { 19 | url.searchParams.delete(key); 20 | } 21 | window.history.replaceState({}, '', url.toString()); 22 | } 23 | return token; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/db/src/methods/connection/getToken.ts: -------------------------------------------------------------------------------- 1 | import { getLocalAccessGrantToken } from '../../utils/localStorageService'; 2 | import type { Database } from '../..'; 3 | 4 | export const getToken = 5 | (db: Database) => 6 | /** 7 | * Looks for the access grant token first in the DB class, then in local storage, then in the url query params 8 | */ 9 | () => { 10 | const urlToken = db.getAccessGrantTokenFromUrl(); 11 | if (urlToken) { 12 | db.accessGrantToken = urlToken; 13 | return urlToken; 14 | } 15 | if (db.accessGrantToken) { 16 | return db.accessGrantToken; 17 | } 18 | const savedToken = getLocalAccessGrantToken(db)(); 19 | if (savedToken) { 20 | db.accessGrantToken = savedToken; 21 | return savedToken; 22 | } 23 | return null; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/db/src/methods/connection/refreshYSweetToken.ts: -------------------------------------------------------------------------------- 1 | import type { Room } from '../../types'; 2 | import type { RefreshYSweetTokenRouteResponse } from '@eweser/shared'; 3 | import type { Database } from '../..'; 4 | 5 | export const refreshYSweetToken = 6 | (db: Database) => 7 | async (room: Room): Promise => { 8 | const { data: refreshed } = 9 | await db.serverFetch( 10 | `/access-grant/refresh-y-sweet-token/${room.id}`, 11 | undefined, 12 | room.connectAbortController 13 | ); 14 | 15 | return refreshed; 16 | }; 17 | -------------------------------------------------------------------------------- /packages/db/src/methods/getRegistry.ts: -------------------------------------------------------------------------------- 1 | import { getLocalRegistry } from '../utils/localStorageService'; 2 | import type { Database } from '..'; 3 | 4 | /** if the registry doesn't exist, look for it in the localstorage */ 5 | export const getRegistry = (db: Database) => () => { 6 | if (db.registry.length > 0) { 7 | return db.registry; 8 | } else { 9 | const localRegistry = getLocalRegistry(db)(); 10 | if (localRegistry) { 11 | db.registry = localRegistry; 12 | } 13 | return db.registry; 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /packages/db/src/methods/log.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '..'; 2 | import type { DatabaseEvents } from '../events'; 3 | 4 | export const log: (db: Database) => DatabaseEvents['log'] = 5 | (db) => 6 | (level, ...message) => { 7 | if (level >= db.logLevel) { 8 | db.emit('log', level, ...message); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /packages/db/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | // in case we run into these issues too https://github.com/developit/microbundle/issues/708, otherwise vscode-lib fails 3 | import 'regenerator-runtime/runtime.js'; 4 | import 'fake-indexeddb/auto'; 5 | -------------------------------------------------------------------------------- /packages/db/src/utils/connection/checkServerConnection.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..'; 2 | 3 | /** pings the matrix server and sets the result to db.online. emits an event on change */ 4 | export const checkServerConnection = async (db: Database) => { 5 | const success = await db.pingServer(); 6 | if (success) { 7 | if (db.online) { 8 | return; 9 | } 10 | db.debug('Server is online'); 11 | db.online = true; 12 | db.emit('onlineChange', true); 13 | } else { 14 | if (!db.online) { 15 | return; 16 | } 17 | db.error('Server is offline'); 18 | db.online = false; 19 | db.emit('onlineChange', false); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /packages/db/src/utils/connection/initializeDoc.ts: -------------------------------------------------------------------------------- 1 | import { IndexeddbPersistence } from 'y-indexeddb'; 2 | import { Doc } from '../../utils/yjsWrapper'; 3 | import type { Doc as YDocType } from 'yjs'; 4 | import type { EweDocument, YDoc, indexedDBProviderPolyfill } from '../../types'; 5 | 6 | export const initializeDocAndLocalProvider = async ( 7 | roomId: string, 8 | existingDoc?: YDoc | null, 9 | provider?: indexedDBProviderPolyfill 10 | ): Promise<{ yDoc: YDoc; localProvider: IndexeddbPersistence }> => { 11 | const yDoc = existingDoc || (new Doc() as YDoc); 12 | if (!yDoc) throw new Error('could not create doc'); 13 | 14 | const localProvider = provider 15 | ? provider(roomId, yDoc as YDocType) 16 | : new IndexeddbPersistence(roomId, yDoc as YDocType); 17 | if (localProvider.synced) return { yDoc: yDoc, localProvider }; 18 | 19 | const synced = await localProvider.whenSynced; 20 | if (synced.synced) return { yDoc, localProvider }; 21 | else throw new Error('could not sync doc'); 22 | }; 23 | -------------------------------------------------------------------------------- /packages/db/src/utils/connection/pingServer.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..'; 2 | 3 | export const pingServer = (db: Database) => async () => { 4 | const { data, error } = await db.serverFetch<{ reply: string }>( 5 | '/access-grant/ping' 6 | ); 7 | if (error) { 8 | db.error('Error pinging server', error); 9 | return false; 10 | } else { 11 | db.debug('Server pinged', data); 12 | return data?.reply && data.reply === 'pong'; 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /packages/db/src/utils/connection/pollConnection.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..'; 2 | import { checkServerConnection } from './checkServerConnection'; 3 | 4 | /** by default polls often (2000ms) trying to check for return of connection after connection loss, and less often (10000ms) checking to make sure connection is still there */ 5 | export const pollConnection = ( 6 | db: Database, 7 | offlineInterval = 2000, 8 | onlineInterval = 10000 9 | ) => { 10 | if (db.isPolling) { 11 | db.info('Already polling connection'); 12 | return; 13 | } 14 | db.isPolling = true; 15 | setInterval(() => { 16 | if (!db.online) { 17 | checkServerConnection(db); 18 | } 19 | }, offlineInterval); 20 | 21 | setInterval(() => { 22 | if (db.online) { 23 | checkServerConnection(db); 24 | } 25 | }, onlineInterval); 26 | }; 27 | -------------------------------------------------------------------------------- /packages/db/src/utils/yjsWrapper.ts: -------------------------------------------------------------------------------- 1 | import { Doc as DocImport } from 'yjs'; 2 | 3 | export const Doc = DocImport; 4 | -------------------------------------------------------------------------------- /packages/db/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": [ 4 | "**/*.test.ts", 5 | "src/setupTests.ts", 6 | "src/test-utils", 7 | "src/examples" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/db/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": false, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "module": "commonjs", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": false, 17 | "declaration": true, 18 | "declarationDir": "types", 19 | "sourceMap": true, 20 | "downlevelIteration": true, 21 | "outDir": "dist", 22 | "useUnknownInCatchVariables": false 23 | }, 24 | "include": ["src", "../shared/collections"], 25 | "exclude": ["src/setupTests.ts"] 26 | } 27 | -------------------------------------------------------------------------------- /packages/db/types/methods/connection/generateLoginUrl.d.ts: -------------------------------------------------------------------------------- 1 | import type { LoginQueryOptions } from '@eweser/shared'; 2 | import type { Database } from '../..'; 3 | export declare const generateLoginUrl: (db: Database) => (options: Partial & { 4 | name: string; 5 | }) => string; 6 | -------------------------------------------------------------------------------- /packages/db/types/methods/connection/generateShareRoomLink.d.ts: -------------------------------------------------------------------------------- 1 | import type { LoginQueryOptions, RoomAccessType } from '@eweser/shared'; 2 | import { Database } from '../..'; 3 | export declare const generateShareRoomLink: (db: Database) => ({ roomId, invitees, redirectUrl, redirectQueries, expiry, accessType, appName, domain, collections, }: Partial & { 4 | roomId: string; 5 | invitees?: string[]; 6 | redirectUrl?: string; 7 | redirectQueries?: Record; 8 | expiry?: string; 9 | accessType: RoomAccessType; 10 | appName: string; 11 | }) => Promise; 12 | -------------------------------------------------------------------------------- /packages/db/types/methods/connection/getAccessGrantTokenFromUrl.d.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..'; 2 | export declare const getAccessGrantTokenFromUrl: (db: Database) => () => string | null; 3 | -------------------------------------------------------------------------------- /packages/db/types/methods/connection/getToken.d.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..'; 2 | export declare const getToken: (db: Database) => () => string | null; 3 | -------------------------------------------------------------------------------- /packages/db/types/methods/connection/loadRoom.d.ts: -------------------------------------------------------------------------------- 1 | import { Room } from '../../room'; 2 | import type { ServerRoom } from '@eweser/shared'; 3 | import type { Database } from '../..'; 4 | export declare function loadYSweet(db: Database, room: Room, withAwareness?: boolean, awaitConnection?: boolean, maxWait?: number): Promise; 5 | export type RemoteLoadOptions = { 6 | awaitLoadRemote?: boolean; 7 | loadRemote?: boolean; 8 | loadRemoteMaxWait?: number; 9 | /** use Awareness, false by default */ 10 | withAwareness?: boolean; 11 | }; 12 | export declare const loadRoom: (db: Database) => (serverRoom: ServerRoom, remoteLoadOptions?: RemoteLoadOptions) => Promise>; 13 | -------------------------------------------------------------------------------- /packages/db/types/methods/connection/loadRooms.d.ts: -------------------------------------------------------------------------------- 1 | import type { Registry } from '../../types'; 2 | import type { Database } from '../..'; 3 | /** in order not to overwhelm the requests for remote server collect, loading the server connections will be staggered with a default 1 second gap */ 4 | export declare const loadRooms: (db: Database) => (rooms: Registry, loadRemotes?: boolean, staggerMs?: number) => Promise; 5 | -------------------------------------------------------------------------------- /packages/db/types/methods/connection/login.d.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..'; 2 | export declare const login: (db: Database) => (options: { 3 | loadAllRooms?: boolean; 4 | } | undefined) => Promise; 5 | -------------------------------------------------------------------------------- /packages/db/types/methods/connection/logout.d.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..'; 2 | export declare const logout: (db: Database) => () => void; 3 | export declare const logoutAndClear: (db: Database) => () => void; 4 | -------------------------------------------------------------------------------- /packages/db/types/methods/connection/refreshYSweetToken.d.ts: -------------------------------------------------------------------------------- 1 | import type { Room } from '../../types'; 2 | import type { RefreshYSweetTokenRouteResponse } from '@eweser/shared'; 3 | import type { Database } from '../..'; 4 | export declare const refreshYSweetToken: (db: Database) => (room: Room) => Promise; 5 | -------------------------------------------------------------------------------- /packages/db/types/methods/connection/syncRegistry.d.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..'; 2 | export declare const syncRegistry: (db: Database) => () => Promise; 3 | -------------------------------------------------------------------------------- /packages/db/types/methods/getRegistry.d.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '..'; 2 | /** if the registry doesn't exist, look for it in the localstorage */ 3 | export declare const getRegistry: (db: Database) => () => import("..").Registry; 4 | -------------------------------------------------------------------------------- /packages/db/types/methods/log.d.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '..'; 2 | import type { DatabaseEvents } from '../events'; 3 | export declare const log: (db: Database) => DatabaseEvents['log']; 4 | -------------------------------------------------------------------------------- /packages/db/types/methods/newRoom.d.ts: -------------------------------------------------------------------------------- 1 | import type { EweDocument } from '@eweser/shared'; 2 | import type { Database } from '..'; 3 | import type { NewRoomOptions } from '../room'; 4 | import { Room } from '../room'; 5 | type NewRoomHelperOptions = Omit, 'db'>; 6 | export declare const newRoom: (db: Database) => (options: NewRoomHelperOptions) => Room; 7 | export {}; 8 | -------------------------------------------------------------------------------- /packages/db/types/utils/connection/checkServerConnection.d.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..'; 2 | /** pings the matrix server and sets the result to db.online. emits an event on change */ 3 | export declare const checkServerConnection: (db: Database) => Promise; 4 | -------------------------------------------------------------------------------- /packages/db/types/utils/connection/initializeDoc.d.ts: -------------------------------------------------------------------------------- 1 | import { IndexeddbPersistence } from 'y-indexeddb'; 2 | import type { EweDocument, YDoc, indexedDBProviderPolyfill } from '../../types'; 3 | export declare const initializeDocAndLocalProvider: (roomId: string, existingDoc?: YDoc | null, provider?: indexedDBProviderPolyfill) => Promise<{ 4 | yDoc: YDoc; 5 | localProvider: IndexeddbPersistence; 6 | }>; 7 | -------------------------------------------------------------------------------- /packages/db/types/utils/connection/pingServer.d.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..'; 2 | export declare const pingServer: (db: Database) => () => Promise; 3 | -------------------------------------------------------------------------------- /packages/db/types/utils/connection/pollConnection.d.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..'; 2 | /** by default polls often (2000ms) trying to check for return of connection after connection loss, and less often (10000ms) checking to make sure connection is still there */ 3 | export declare const pollConnection: (db: Database, offlineInterval?: number, onlineInterval?: number) => void; 4 | -------------------------------------------------------------------------------- /packages/db/types/utils/connection/serverFetch.d.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..'; 2 | export type Options = Omit & { 3 | body: any; 4 | }; 5 | export declare const serverFetch: (_db: Database) => (path: string, _options?: Options, abortController?: AbortController) => Promise<{ 6 | error: unknown; 7 | data: null; 8 | } | { 9 | error: null; 10 | data: ReturnType; 11 | }>; 12 | -------------------------------------------------------------------------------- /packages/db/types/utils/getDocuments.d.ts: -------------------------------------------------------------------------------- 1 | import type { YMapEvent, Transaction } from 'yjs'; 2 | import type { Database } from '..'; 3 | import type { EweDocument, DocumentWithoutBase, Room, Documents } from '../types'; 4 | import type { TypedMap } from 'yjs-types'; 5 | export interface GetDocuments { 6 | documents: TypedMap>; 7 | get: (id: string) => T | undefined; 8 | set: (doc: T) => T; 9 | new: (doc: DocumentWithoutBase, id?: string) => T; 10 | delete: (id: string, timeToLiveMs?: number) => T; 11 | getAll: () => Documents; 12 | getAllToArray: () => T[]; 13 | getUndeleted: () => Documents; 14 | getUndeletedToArray: () => T[]; 15 | toArray: (docs: Documents) => T[]; 16 | onChange: (callback: (event: YMapEvent, transaction: Transaction) => void) => void; 17 | sortByRecent: (docs: Documents) => Documents; 18 | } 19 | export declare const getDocuments: (_db: Database) => (room: Room) => GetDocuments; 20 | -------------------------------------------------------------------------------- /packages/db/types/utils/yjsWrapper.d.ts: -------------------------------------------------------------------------------- 1 | import { Doc as DocImport } from 'yjs'; 2 | export declare const Doc: typeof DocImport; 3 | -------------------------------------------------------------------------------- /packages/db/vite.config.js: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path'; 2 | import { defineConfig } from 'vitest/config'; 3 | 4 | export default defineConfig({ 5 | build: { 6 | lib: { 7 | name: 'eweser-db', 8 | entry: resolve(__dirname, 'src/index.ts'), 9 | }, 10 | rollupOptions: { 11 | // make sure to externalize deps that shouldn't be bundled 12 | // into your library 13 | external: ['yjs'], 14 | }, 15 | minify: false, // for now while in development 16 | }, 17 | test: { 18 | environment: 'jsdom', 19 | setupFiles: 'src/setupTests.ts', 20 | // need to slow down the tests or the server will reject the requests 21 | maxConcurrency: 2, 22 | maxThreads: 2, 23 | minThreads: 1, 24 | // coverage: { 25 | // reporter: ['text', 'json', 'html', 'lcov'], 26 | // }, 27 | }, 28 | }); 29 | -------------------------------------------------------------------------------- /packages/eslint-config-react-ts/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['./index.js'], 3 | env: { 4 | node: true, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /packages/eslint-config-react-ts/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @eweser/eslint-config-react-ts 2 | 3 | ## 1.14.0 4 | 5 | ### Minor Changes 6 | 7 | - align versions 8 | 9 | ### Patch Changes 10 | 11 | - Updated dependencies 12 | - @eweser/eslint-config-ts@1.14.0 13 | 14 | ## 1.13.6 15 | 16 | ### Patch Changes 17 | 18 | - bump 19 | - Updated dependencies 20 | - @eweser/eslint-config-ts@1.13.6 21 | 22 | ## 1.13.5 23 | 24 | ### Patch Changes 25 | 26 | - registry sync 27 | - Updated dependencies 28 | - @eweser/eslint-config-ts@1.13.5 29 | 30 | ## 1.13.4 31 | 32 | ### Patch Changes 33 | 34 | - add rolling sync 35 | - Updated dependencies 36 | - @eweser/eslint-config-ts@1.13.4 37 | 38 | ## 1.13.3 39 | 40 | ### Patch Changes 41 | 42 | - Default patch bump for all packages. 43 | - Updated dependencies 44 | - @eweser/eslint-config-ts@1.13.3 45 | -------------------------------------------------------------------------------- /packages/eslint-config-react-ts/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 EduVault 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/eslint-config-react-ts/Test.tsx: -------------------------------------------------------------------------------- 1 | const App = () => { 2 | const unused = 'asdf'; // yay works 3 | return ( 4 |
    5 |

    hi

    6 |
    7 | ); 8 | }; 9 | 10 | export default App; 11 | -------------------------------------------------------------------------------- /packages/eslint-config-react-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@eweser/eslint-config-react-ts", 3 | "version": "1.14.0", 4 | "description": "> TODO: description", 5 | "author": "jacobcoro", 6 | "homepage": "https://github.com/eweser/eweser-db#readme", 7 | "license": "MIT", 8 | "main": "./index.js", 9 | "files": [ 10 | "./index.js" 11 | ], 12 | "publishConfig": { 13 | "access": "public" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/eweser/eweser-db.git" 18 | }, 19 | "dependencies": { 20 | "@eweser/eslint-config-ts": "^1.14.0", 21 | "@typescript-eslint/eslint-plugin": "^5.57.1", 22 | "@typescript-eslint/parser": "^5.57.1", 23 | "eslint": "^8.38.0", 24 | "eslint-config-prettier": "^8.8.0", 25 | "eslint-plugin-import": "^2.27.5", 26 | "eslint-plugin-n": "^15.7.0", 27 | "eslint-plugin-prettier": "^4.2.1", 28 | "eslint-plugin-promise": "^6.1.1", 29 | "eslint-plugin-react": "^7.32.2", 30 | "eslint-plugin-react-hooks": "^4.6.0", 31 | "prettier": "^2.8.7", 32 | "typescript": "^5.0.4" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/eslint-config-react-ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "baseUrl": "./" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/eslint-config-ts/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | node: true, 4 | }, 5 | extends: ['./index.js'], 6 | }; 7 | -------------------------------------------------------------------------------- /packages/eslint-config-ts/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @eweser/eslint-config-ts 2 | 3 | ## 1.14.0 4 | 5 | ### Minor Changes 6 | 7 | - align versions 8 | 9 | ## 1.13.6 10 | 11 | ### Patch Changes 12 | 13 | - bump 14 | 15 | ## 1.13.5 16 | 17 | ### Patch Changes 18 | 19 | - registry sync 20 | 21 | ## 1.13.4 22 | 23 | ### Patch Changes 24 | 25 | - add rolling sync 26 | 27 | ## 1.13.3 28 | 29 | ### Patch Changes 30 | 31 | - Default patch bump for all packages. 32 | -------------------------------------------------------------------------------- /packages/eslint-config-ts/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 EduVault 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/eslint-config-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@eweser/eslint-config-ts", 3 | "version": "1.14.0", 4 | "description": "> TODO: description", 5 | "author": "jacobcoro", 6 | "homepage": "https://github.com/eweser/eweser-db#readme", 7 | "license": "MIT", 8 | "main": "./index.js", 9 | "files": [ 10 | "./index.js" 11 | ], 12 | "publishConfig": { 13 | "access": "public" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/eweser/eweser-db.git" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/eweser/eweser-db/issues" 21 | }, 22 | "devDependencies": { 23 | "@typescript-eslint/eslint-plugin": "^5.57.1", 24 | "eslint": "^8.37.0", 25 | "eslint-config-prettier": "^8.8.0", 26 | "eslint-plugin-prettier": "^4.2.1", 27 | "prettier": "^2.8.7" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/examples-components/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@eweser/eslint-config-react-ts'], 3 | ignorePatterns: [ 4 | 'vite.config.*', 5 | 'types/**/*.d.ts', 6 | 'dist', 7 | 'setupTests.ts', 8 | '.eslintrc.cjs', 9 | ], 10 | }; 11 | -------------------------------------------------------------------------------- /packages/examples-components/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | 9 | node_modules 10 | dist 11 | dist-ssr 12 | *.local 13 | 14 | # Editor directories and files 15 | .vscode/* 16 | !.vscode/extensions.json 17 | .idea 18 | .DS_Store 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /packages/examples-components/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @eweser/examples-components 2 | 3 | ## 2.0.0 4 | 5 | ### Minor Changes 6 | 7 | - align versions 8 | 9 | ### Patch Changes 10 | 11 | - Updated dependencies 12 | - @eweser/db@1.14.0 13 | 14 | ## 1.13.6 15 | 16 | ### Patch Changes 17 | 18 | - bump 19 | - Updated dependencies 20 | - @eweser/db@1.13.6 21 | 22 | ## 1.13.5 23 | 24 | ### Patch Changes 25 | 26 | - registry sync 27 | - Updated dependencies 28 | - @eweser/db@1.13.5 29 | 30 | ## 1.13.4 31 | 32 | ### Patch Changes 33 | 34 | - add rolling sync 35 | - Updated dependencies 36 | - @eweser/db@1.13.4 37 | 38 | ## 1.13.3 39 | 40 | ### Patch Changes 41 | 42 | - Default patch bump for all packages. 43 | - Updated dependencies 44 | - @eweser/db@1.13.3 45 | -------------------------------------------------------------------------------- /packages/examples-components/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 EduVault 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/examples-components/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/examples-components/src/components/LoginButton.tsx: -------------------------------------------------------------------------------- 1 | import { EweserIcon } from './EweserIcon'; 2 | import { useHover } from './helpers'; 3 | import * as styles from './styles'; 4 | 5 | export const LoginButton = ({ 6 | loginUrl, 7 | size = 'small', 8 | }: { 9 | loginUrl: string; 10 | size?: 'small' | 'large'; 11 | }) => { 12 | const style = useHover(styles.loginButtonHover, styles.loginButton); 13 | const textStyle = 14 | size === 'small' ? styles.loginButtonTextSmall : styles.loginButtonText; 15 | const logoSize = size === 'small' ? 25 : 35; 16 | const logoSizing = { 17 | width: logoSize, 18 | height: logoSize, 19 | }; 20 | const loginButtonText = size === 'small' ? 'Login' : 'Login with Eweser'; 21 | return ( 22 | 28 | 32 | 33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /packages/examples-components/src/components/helpers.tsx: -------------------------------------------------------------------------------- 1 | import type { CSSProperties } from 'react'; 2 | import { useState } from 'react'; 3 | 4 | export function useHover( 5 | styleOnHover: CSSProperties, 6 | styleOnNotHover: CSSProperties = {} 7 | ) { 8 | const [style, setStyle] = useState(styleOnNotHover); 9 | 10 | const onMouseEnter = () => setStyle(styleOnHover); 11 | const onMouseLeave = () => setStyle(styleOnNotHover); 12 | 13 | return { style, onMouseEnter, onMouseLeave }; 14 | } 15 | -------------------------------------------------------------------------------- /packages/examples-components/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * as styles from './styles'; 2 | export * from './StatusBar'; 3 | export * from './LoginButton'; 4 | -------------------------------------------------------------------------------- /packages/examples-components/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/examples-components/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "module": "ESNext", 11 | "moduleResolution": "Node", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | "declaration": true, 17 | "skipLibCheck": true, 18 | "esModuleInterop": true, 19 | "declarationMap": true, 20 | "baseUrl": ".", 21 | "types": ["vite/client"], 22 | "paths": { 23 | "react-vite-library": ["src/components/index.ts"] 24 | }, 25 | "typeRoots": ["node_modules/@types", "src/components/index.d.ts"] 26 | }, 27 | "include": ["src"], 28 | "references": [{ "path": "./tsconfig.node.json" }] 29 | } 30 | -------------------------------------------------------------------------------- /packages/examples-components/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true, 7 | "resolveJsonModule": true 8 | }, 9 | "include": ["vite.config.ts", "package.json"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/examples-components/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'node:path'; 2 | 3 | import react from '@vitejs/plugin-react'; 4 | import { defineConfig } from 'vite'; 5 | import dts from 'vite-plugin-dts'; 6 | import * as packageJson from './package.json'; 7 | // https://vitejs.dev/config/ 8 | export default defineConfig((configEnv) => ({ 9 | plugins: [ 10 | react(), 11 | dts({ 12 | include: ['src/components/'], 13 | }) as any, 14 | ], 15 | build: { 16 | lib: { 17 | entry: resolve('src', 'components/index.ts'), 18 | name: 'ReactViteLibrary', 19 | formats: ['es', 'umd'], 20 | fileName: (format) => `examples-components.${format}.js`, 21 | }, 22 | rollupOptions: { 23 | external: [...Object.keys(packageJson.peerDependencies)], 24 | }, 25 | }, 26 | })); 27 | -------------------------------------------------------------------------------- /packages/shared/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@eweser/eslint-config-ts'], 3 | ignorePatterns: [ 4 | 'vite.config.*', 5 | 'types/**/*.d.ts', 6 | 'dist', 7 | 'setupTests.ts', 8 | '.eslintrc.cjs', 9 | ], 10 | }; 11 | -------------------------------------------------------------------------------- /packages/shared/.gitignore: -------------------------------------------------------------------------------- 1 | !dist 2 | !types -------------------------------------------------------------------------------- /packages/shared/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @eweser/shared 2 | 3 | ## 1.14.0 4 | 5 | ### Minor Changes 6 | 7 | - align versions 8 | 9 | ## 1.13.6 10 | 11 | ### Patch Changes 12 | 13 | - bump 14 | 15 | ## 1.13.5 16 | 17 | ### Patch Changes 18 | 19 | - registry sync 20 | 21 | ## 1.13.4 22 | 23 | ### Patch Changes 24 | 25 | - add rolling sync 26 | 27 | ## 1.13.3 28 | 29 | ### Patch Changes 30 | 31 | - Default patch bump for all packages. 32 | -------------------------------------------------------------------------------- /packages/shared/dist/api/accept-room-invite.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=accept-room-invite.js.map -------------------------------------------------------------------------------- /packages/shared/dist/api/accept-room-invite.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"accept-room-invite.js","sourceRoot":"","sources":["../../src/api/accept-room-invite.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /packages/shared/dist/api/create-room-invite.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=create-room-invite.js.map -------------------------------------------------------------------------------- /packages/shared/dist/api/create-room-invite.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"create-room-invite.js","sourceRoot":"","sources":["../../src/api/create-room-invite.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /packages/shared/dist/api/index.js: -------------------------------------------------------------------------------- 1 | export * from './login-queries'; 2 | export * from './registry-sync'; 3 | export * from './refresh-y-sweet-token'; 4 | export * from './create-room-invite'; 5 | export * from './accept-room-invite'; 6 | export * from './update-room'; 7 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /packages/shared/dist/api/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC"} -------------------------------------------------------------------------------- /packages/shared/dist/api/login-queries.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=login-queries.js.map -------------------------------------------------------------------------------- /packages/shared/dist/api/login-queries.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"login-queries.js","sourceRoot":"","sources":["../../src/api/login-queries.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /packages/shared/dist/api/refresh-y-sweet-token.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=refresh-y-sweet-token.js.map -------------------------------------------------------------------------------- /packages/shared/dist/api/refresh-y-sweet-token.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"refresh-y-sweet-token.js","sourceRoot":"","sources":["../../src/api/refresh-y-sweet-token.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /packages/shared/dist/api/registry-sync.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=registry-sync.js.map -------------------------------------------------------------------------------- /packages/shared/dist/api/registry-sync.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"registry-sync.js","sourceRoot":"","sources":["../../src/api/registry-sync.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /packages/shared/dist/api/update-room.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=update-room.js.map -------------------------------------------------------------------------------- /packages/shared/dist/api/update-room.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"update-room.js","sourceRoot":"","sources":["../../src/api/update-room.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /packages/shared/dist/collections/documentBase.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=documentBase.js.map -------------------------------------------------------------------------------- /packages/shared/dist/collections/documentBase.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"documentBase.js","sourceRoot":"","sources":["../../src/collections/documentBase.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /packages/shared/dist/collections/flashcard.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=flashcard.js.map -------------------------------------------------------------------------------- /packages/shared/dist/collections/flashcard.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"flashcard.js","sourceRoot":"","sources":["../../src/collections/flashcard.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /packages/shared/dist/collections/index.js: -------------------------------------------------------------------------------- 1 | export * from './note'; 2 | export * from './flashcard'; 3 | export * from './profile'; 4 | export * from './documentBase'; 5 | /** We don't include registry because we use this after login to get all non-registry collections. */ 6 | export const COLLECTION_KEYS = ['notes', 'flashcards', 'profiles']; 7 | export const COLLECTION_KEYS_OR_ALL = [...COLLECTION_KEYS, 'all']; 8 | export const PUBLIC_ACCESS_TYPES = ['private', 'read', 'write']; 9 | export const ROOM_ACCESS_TYPES = ['read', 'write', 'admin']; 10 | export const collectionKeys = COLLECTION_KEYS.map((key) => key); 11 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /packages/shared/dist/collections/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/collections/index.ts"],"names":[],"mappings":"AAIA,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAE/B,qGAAqG;AACrG,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,YAAY,EAAE,UAAU,CAAU,CAAC;AAE5E,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,GAAG,eAAe,EAAE,KAAK,CAAU,CAAC;AAE3E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAU,CAAC;AAGzE,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAU,CAAC;AAGrE,MAAM,CAAC,MAAM,cAAc,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC"} -------------------------------------------------------------------------------- /packages/shared/dist/collections/note.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=note.js.map -------------------------------------------------------------------------------- /packages/shared/dist/collections/note.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"note.js","sourceRoot":"","sources":["../../src/collections/note.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /packages/shared/dist/collections/profile.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=profile.js.map -------------------------------------------------------------------------------- /packages/shared/dist/collections/profile.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"profile.js","sourceRoot":"","sources":["../../src/collections/profile.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /packages/shared/dist/index.js: -------------------------------------------------------------------------------- 1 | export * from './collections'; 2 | export * from './utils'; 3 | export * from './api'; 4 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /packages/shared/dist/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,eAAe,CAAC;AAC9B,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC"} -------------------------------------------------------------------------------- /packages/shared/dist/utils/index.js: -------------------------------------------------------------------------------- 1 | export function loginOptionsToQueryParams({ collections, ...rest }) { 2 | const _collections = collections.length === 0 3 | ? 'all' 4 | : collections.length === 1 5 | ? collections[0] 6 | : collections.join('|'); 7 | const params = { 8 | collections: _collections, 9 | ...rest, 10 | }; 11 | return params; 12 | } 13 | /** checks if the token is already expired or will be expired within the next (2) minutes */ 14 | export const isTokenExpired = (tokenExpiry, 15 | /* mark the token as expired even this many minutes before it actually expires */ 16 | bufferMinutes = 2) => { 17 | const expiry = new Date(tokenExpiry).getTime(); 18 | const now = new Date().getTime() + bufferMinutes * 60 * 1000; 19 | return expiry < now; 20 | }; 21 | export const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); 22 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /packages/shared/dist/utils/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,yBAAyB,CAAC,EACxC,WAAW,EACX,GAAG,IAAI,EACW;IAClB,MAAM,YAAY,GAChB,WAAW,CAAC,MAAM,KAAK,CAAC;QACtB,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC;YAC1B,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;YAChB,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,MAAM,GAAqB;QAC/B,WAAW,EAAE,YAAY;QACzB,GAAG,IAAI;KACR,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,4FAA4F;AAC5F,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,WAAmB;AACnB,iFAAiF;AACjF,aAAa,GAAG,CAAC,EACjB,EAAE;IACF,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC;IAC/C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,aAAa,GAAG,EAAE,GAAG,IAAI,CAAC;IAC7D,OAAO,MAAM,GAAG,GAAG,CAAC;AACtB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,EAAU,EAAE,EAAE,CACjC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC"} -------------------------------------------------------------------------------- /packages/shared/src/api/accept-room-invite.ts: -------------------------------------------------------------------------------- 1 | import type { ServerRoom } from '..'; 2 | 3 | export type AcceptRoomInviteQueries = { 4 | token: string; 5 | }; 6 | 7 | export type AcceptRoomInviteResponse = ServerRoom; 8 | -------------------------------------------------------------------------------- /packages/shared/src/api/create-room-invite.ts: -------------------------------------------------------------------------------- 1 | import type { RoomAccessType } from '../collections'; 2 | import type { LoginQueryParams } from './login-queries'; 3 | 4 | export type CreateRoomInviteBody = LoginQueryParams & { 5 | inviterId?: string; 6 | invitees: string[]; 7 | roomId: string; 8 | accessType: RoomAccessType; 9 | redirectQueries?: Record; 10 | expiry?: string; 11 | }; 12 | export type CreateRoomInviteResponse = { 13 | link: string; 14 | }; 15 | -------------------------------------------------------------------------------- /packages/shared/src/api/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login-queries'; 2 | export * from './registry-sync'; 3 | export * from './refresh-y-sweet-token'; 4 | export * from './create-room-invite'; 5 | export * from './accept-room-invite'; 6 | export * from './update-room'; 7 | -------------------------------------------------------------------------------- /packages/shared/src/api/login-queries.ts: -------------------------------------------------------------------------------- 1 | import type { CollectionKeyOrAll } from '../collections'; 2 | 3 | export type LoginQueryOptions = { 4 | redirect: string; 5 | domain: string; 6 | collections: CollectionKeyOrAll[]; 7 | /** app name */ 8 | name: string; 9 | }; 10 | 11 | export type LoginQueryParams = { 12 | redirect: string; 13 | domain: string; 14 | /** CollectionOrAll array string joined with '|' */ 15 | collections: string; 16 | /** app name */ 17 | name: string; 18 | }; 19 | -------------------------------------------------------------------------------- /packages/shared/src/api/refresh-y-sweet-token.ts: -------------------------------------------------------------------------------- 1 | export type RefreshYSweetTokenRouteParams = { 2 | roomId: string; 3 | }; 4 | export type RefreshYSweetTokenRouteResponse = { 5 | ySweetBaseUrl: string; 6 | ySweetUrl: string; 7 | tokenExpiry: string; 8 | }; 9 | -------------------------------------------------------------------------------- /packages/shared/src/api/registry-sync.ts: -------------------------------------------------------------------------------- 1 | import type { ServerRoom } from '..'; 2 | 3 | export type RegistrySyncRequestBody = { 4 | rooms: ServerRoom[]; 5 | }; 6 | 7 | export type RegistrySyncResponse = { 8 | rooms: ServerRoom[]; 9 | token: string; 10 | userId: string; 11 | }; 12 | -------------------------------------------------------------------------------- /packages/shared/src/api/update-room.ts: -------------------------------------------------------------------------------- 1 | import type { ServerRoom } from '..'; 2 | 3 | export type UpdateRoomRouteParams = { 4 | roomId: string; 5 | }; 6 | export type UpdateRoomPostBody = { 7 | newName: string; 8 | }; 9 | export type UpdateRoomResponse = ServerRoom; 10 | -------------------------------------------------------------------------------- /packages/shared/src/collections/documentBase.ts: -------------------------------------------------------------------------------- 1 | export type DocumentBase = { 2 | /** 3 | * @example .. == `notes.5.1` 4 | */ 5 | _ref: string; 6 | /** uuid, matches outer id */ 7 | _id: string; 8 | /** epoch time created with new Date().getTime() */ 9 | _created: number; 10 | /** epoch time updated with new Date().getTime() */ 11 | _updated: number; 12 | _deleted?: boolean; 13 | /** time to live. an epoch time set when deleted flag is set. recommend one month from now 14 | * new Date().getTime() + 1000 * 60 * 60 * 24 * 30 15 | */ 16 | _ttl?: number; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/shared/src/collections/flashcard.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentBase } from './documentBase'; 2 | 3 | export type FlashcardBase = { 4 | frontText: string; 5 | backText: string; 6 | noteRefs?: string[]; 7 | }; 8 | export type Flashcard = DocumentBase & FlashcardBase; 9 | -------------------------------------------------------------------------------- /packages/shared/src/collections/note.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentBase } from './documentBase'; 2 | 3 | export type NoteBase = { 4 | text: string; 5 | flashcardRefs?: string[]; 6 | }; 7 | 8 | export type Note = DocumentBase & NoteBase; 9 | -------------------------------------------------------------------------------- /packages/shared/src/collections/profile.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentBase } from './documentBase'; 2 | 3 | export type ProfileBase = { 4 | firstName?: any; 5 | lastName?: string; 6 | avatarUrl?: string; 7 | }; 8 | 9 | export type Profile = DocumentBase & ProfileBase; 10 | -------------------------------------------------------------------------------- /packages/shared/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { CollectionKey, PublicAccessType } from './collections'; 2 | 3 | export * from './collections'; 4 | export * from './utils'; 5 | export * from './api'; 6 | 7 | /** Should match the rooms schema in the auth-server. Unfortunately we can't see the null values as undefined or else drizzle types will be out of sync. */ 8 | export type ServerRoom = { 9 | id: string; 10 | name: string; 11 | collectionKey: CollectionKey; 12 | tokenExpiry: string | null; 13 | ySweetUrl: string | null; 14 | ySweetBaseUrl: string | null; 15 | publicAccess: PublicAccessType; 16 | readAccess: string[]; 17 | writeAccess: string[]; 18 | adminAccess: string[]; 19 | createdAt: string | null; 20 | updatedAt: string | null; 21 | _deleted: boolean | null; 22 | _ttl: string | null; 23 | }; 24 | -------------------------------------------------------------------------------- /packages/shared/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import type { LoginQueryOptions, LoginQueryParams } from '..'; 2 | 3 | export function loginOptionsToQueryParams({ 4 | collections, 5 | ...rest 6 | }: LoginQueryOptions) { 7 | const _collections = 8 | collections.length === 0 9 | ? 'all' 10 | : collections.length === 1 11 | ? collections[0] 12 | : collections.join('|'); 13 | const params: LoginQueryParams = { 14 | collections: _collections, 15 | ...rest, 16 | }; 17 | return params; 18 | } 19 | 20 | /** checks if the token is already expired or will be expired within the next (2) minutes */ 21 | export const isTokenExpired = ( 22 | tokenExpiry: string, 23 | /* mark the token as expired even this many minutes before it actually expires */ 24 | bufferMinutes = 2 25 | ) => { 26 | const expiry = new Date(tokenExpiry).getTime(); 27 | const now = new Date().getTime() + bufferMinutes * 60 * 1000; 28 | return expiry < now; 29 | }; 30 | 31 | export const wait = (ms: number) => 32 | new Promise((resolve) => setTimeout(resolve, ms)); 33 | -------------------------------------------------------------------------------- /packages/shared/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": false, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "module": "ESNext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": false, 17 | "declaration": true, 18 | "declarationDir": "types", 19 | "sourceMap": true, 20 | "downlevelIteration": true, 21 | "outDir": "dist", 22 | "useUnknownInCatchVariables": false 23 | }, 24 | "include": ["src"], 25 | "exclude": ["**/*.test.ts"] 26 | } 27 | -------------------------------------------------------------------------------- /packages/shared/types/api/accept-room-invite.d.ts: -------------------------------------------------------------------------------- 1 | import type { ServerRoom } from '..'; 2 | export type AcceptRoomInviteQueries = { 3 | token: string; 4 | }; 5 | export type AcceptRoomInviteResponse = ServerRoom; 6 | -------------------------------------------------------------------------------- /packages/shared/types/api/create-room-invite.d.ts: -------------------------------------------------------------------------------- 1 | import type { RoomAccessType } from '../collections'; 2 | import type { LoginQueryParams } from './login-queries'; 3 | export type CreateRoomInviteBody = LoginQueryParams & { 4 | inviterId?: string; 5 | invitees: string[]; 6 | roomId: string; 7 | accessType: RoomAccessType; 8 | redirectQueries?: Record; 9 | expiry?: string; 10 | }; 11 | export type CreateRoomInviteResponse = { 12 | link: string; 13 | }; 14 | -------------------------------------------------------------------------------- /packages/shared/types/api/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from './login-queries'; 2 | export * from './registry-sync'; 3 | export * from './refresh-y-sweet-token'; 4 | export * from './create-room-invite'; 5 | export * from './accept-room-invite'; 6 | export * from './update-room'; 7 | -------------------------------------------------------------------------------- /packages/shared/types/api/login-queries.d.ts: -------------------------------------------------------------------------------- 1 | import type { CollectionKeyOrAll } from '../collections'; 2 | export type LoginQueryOptions = { 3 | redirect: string; 4 | domain: string; 5 | collections: CollectionKeyOrAll[]; 6 | /** app name */ 7 | name: string; 8 | }; 9 | export type LoginQueryParams = { 10 | redirect: string; 11 | domain: string; 12 | /** CollectionOrAll array string joined with '|' */ 13 | collections: string; 14 | /** app name */ 15 | name: string; 16 | }; 17 | -------------------------------------------------------------------------------- /packages/shared/types/api/refresh-y-sweet-token.d.ts: -------------------------------------------------------------------------------- 1 | export type RefreshYSweetTokenRouteParams = { 2 | roomId: string; 3 | }; 4 | export type RefreshYSweetTokenRouteResponse = { 5 | ySweetBaseUrl: string; 6 | ySweetUrl: string; 7 | tokenExpiry: string; 8 | }; 9 | -------------------------------------------------------------------------------- /packages/shared/types/api/registry-sync.d.ts: -------------------------------------------------------------------------------- 1 | import type { ServerRoom } from '..'; 2 | export type RegistrySyncRequestBody = { 3 | rooms: ServerRoom[]; 4 | }; 5 | export type RegistrySyncResponse = { 6 | rooms: ServerRoom[]; 7 | token: string; 8 | userId: string; 9 | }; 10 | -------------------------------------------------------------------------------- /packages/shared/types/api/update-room.d.ts: -------------------------------------------------------------------------------- 1 | import type { ServerRoom } from '..'; 2 | export type UpdateRoomRouteParams = { 3 | roomId: string; 4 | }; 5 | export type UpdateRoomPostBody = { 6 | newName: string; 7 | }; 8 | export type UpdateRoomResponse = ServerRoom; 9 | -------------------------------------------------------------------------------- /packages/shared/types/collections/documentBase.d.ts: -------------------------------------------------------------------------------- 1 | export type DocumentBase = { 2 | /** 3 | * @example .. == `notes.5.1` 4 | */ 5 | _ref: string; 6 | /** uuid, matches outer id */ 7 | _id: string; 8 | /** epoch time created with new Date().getTime() */ 9 | _created: number; 10 | /** epoch time updated with new Date().getTime() */ 11 | _updated: number; 12 | _deleted?: boolean; 13 | /** time to live. an epoch time set when deleted flag is set. recommend one month from now 14 | * new Date().getTime() + 1000 * 60 * 60 * 24 * 30 15 | */ 16 | _ttl?: number; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/shared/types/collections/flashcard.d.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentBase } from './documentBase'; 2 | export type FlashcardBase = { 3 | frontText: string; 4 | backText: string; 5 | noteRefs?: string[]; 6 | }; 7 | export type Flashcard = DocumentBase & FlashcardBase; 8 | -------------------------------------------------------------------------------- /packages/shared/types/collections/note.d.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentBase } from './documentBase'; 2 | export type NoteBase = { 3 | text: string; 4 | flashcardRefs?: string[]; 5 | }; 6 | export type Note = DocumentBase & NoteBase; 7 | -------------------------------------------------------------------------------- /packages/shared/types/collections/profile.d.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentBase } from './documentBase'; 2 | export type ProfileBase = { 3 | firstName?: any; 4 | lastName?: string; 5 | avatarUrl?: string; 6 | }; 7 | export type Profile = DocumentBase & ProfileBase; 8 | -------------------------------------------------------------------------------- /packages/shared/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import type { CollectionKey, PublicAccessType } from './collections'; 2 | export * from './collections'; 3 | export * from './utils'; 4 | export * from './api'; 5 | /** Should match the rooms schema in the auth-server. Unfortunately we can't see the null values as undefined or else drizzle types will be out of sync. */ 6 | export type ServerRoom = { 7 | id: string; 8 | name: string; 9 | collectionKey: CollectionKey; 10 | tokenExpiry: string | null; 11 | ySweetUrl: string | null; 12 | ySweetBaseUrl: string | null; 13 | publicAccess: PublicAccessType; 14 | readAccess: string[]; 15 | writeAccess: string[]; 16 | adminAccess: string[]; 17 | createdAt: string | null; 18 | updatedAt: string | null; 19 | _deleted: boolean | null; 20 | _ttl: string | null; 21 | }; 22 | -------------------------------------------------------------------------------- /packages/shared/types/utils/index.d.ts: -------------------------------------------------------------------------------- 1 | import type { LoginQueryOptions, LoginQueryParams } from '..'; 2 | export declare function loginOptionsToQueryParams({ collections, ...rest }: LoginQueryOptions): LoginQueryParams; 3 | /** checks if the token is already expired or will be expired within the next (2) minutes */ 4 | export declare const isTokenExpired: (tokenExpiry: string, bufferMinutes?: number) => boolean; 5 | export declare const wait: (ms: number) => Promise; 6 | -------------------------------------------------------------------------------- /scripts/create-default-changeset.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require('child_process'); 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | 5 | const changesetDir = path.resolve(__dirname, '../.changeset'); 6 | if (!fs.existsSync(changesetDir)) { 7 | fs.mkdirSync(changesetDir); 8 | } 9 | const changesetFiles = fs 10 | .readdirSync(changesetDir) 11 | .filter((file) => file.endsWith('.md')); 12 | 13 | if (changesetFiles.length === 0) { 14 | console.log( 15 | 'No changesets found. Creating a default patch changeset for all packages...' 16 | ); 17 | const changeset = ` 18 | --- 19 | "@eweser/db": patch 20 | "@eweser/eslint-config-react-ts": patch 21 | "@eweser/eslint-config-ts": patch 22 | "@eweser/examples-components": patch 23 | "@eweser/shared": patch 24 | --- 25 | 26 | Default patch bump for all packages. 27 | `.trim(); 28 | 29 | const changesetFilename = `${Date.now()}-default.md`; 30 | fs.writeFileSync(path.join(changesetDir, changesetFilename), changeset); 31 | console.log(`Created changeset: ${changesetFilename}`); 32 | } else { 33 | console.log('Changeset already exists. Skipping creation.'); 34 | } 35 | -------------------------------------------------------------------------------- /test-rpc-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-rpc-server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "serve": "node server.js" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "lib0": "^0.2.73" 16 | }, 17 | "devDependencies": { 18 | "http-server": "^14.1.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": {}, 3 | "extends": "expo/tsconfig.base" 4 | } 5 | --------------------------------------------------------------------------------