diff --git a/.github/ISSUE_TEMPLATE/website-issue.md b/.github/ISSUE_TEMPLATE/website-issue.md
index ec583e6..e8a7fbe 100644
--- a/.github/ISSUE_TEMPLATE/website-issue.md
+++ b/.github/ISSUE_TEMPLATE/website-issue.md
@@ -5,4 +5,3 @@ title: ''
labels: ''
assignees: ''
---
-
diff --git a/.github/workflows/begin-deploy.yml b/.github/workflows/begin-deploy.yml
index da17f7c..25b62c4 100644
--- a/.github/workflows/begin-deploy.yml
+++ b/.github/workflows/begin-deploy.yml
@@ -1,7 +1,7 @@
name: Node CI
# Push tests pushes; PR tests merges
-on: [ push, pull_request ]
+on: [push, pull_request]
defaults:
run:
diff --git a/README.md b/README.md
index a8375a8..93f1859 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
## Platform and Services
-This website is built on [Enhance](https://enhance.dev) and deploys to [Begin](https://begin.com).
+This website is built on [Enhance](https://enhance.dev) and deploys to [Begin](https://begin.com).
It currently depends on the following external services:
@@ -10,13 +10,13 @@ It currently depends on the following external services:
## Enhance Structure
-Enhance is an opinionated HTML web framework that renders pages on the server and supports [progressive enhancement](https://enhance.dev/docs/learn/why-enhance) on the client for SPA-like experiences.
+Enhance is an opinionated HTML web framework that renders pages on the server and supports [progressive enhancement](https://enhance.dev/docs/learn/why-enhance) on the client for SPA-like experiences.
-An Enhace app has two top-level directories: `app` and `public`.
+An Enhace app has two top-level directories: `app` and `public`.
### The `app` Directory
-The `app` directory contains all the code for our application, in addition to Markdown files that power many of the static webpages.
+The `app` directory contains all the code for our application, in addition to Markdown files that power many of the static webpages.
Disclaimer: Enhance is a new and rapidly changing web framework. We will do our best to keep this README up to date, but please reference the Enhance docs as the source of truth.
@@ -29,13 +29,13 @@ Here's a brief rundown of each subdirectory and its purpose:
### The `public` Directory
-This contains static files (images, css, etc).
+This contains static files (images, css, etc).
Enhance makes all of these files available via the special `/_public/` path off the root. In addition, this project has turned on fingerprinting, so when you deploy a file to the cloud (i.e. `/images/logo.png`), its filename is re-written with a unique hash attached (i.e. `/images/logo-abc123.png`) and when you reference this file in your markup (using just `/images/logo.png`), Enhance will re-write the response to the client on-the-fly to reference the filename with the attached hash.
## App Design
-For the most part, file-based routing makes it pretty clear what paths are supported by our app. For instance, the existence of `pages/speakers.mjs` tells you that the app supports a `/speakers` web page.
+For the most part, file-based routing makes it pretty clear what paths are supported by our app. For instance, the existence of `pages/speakers.mjs` tells you that the app supports a `/speakers` web page.
Pages that require data to render are paired with an identically names JS file in the `api` directory. At the moment, all of the data is being stored as static JS data in the API files, but this may change in the future.
@@ -46,7 +46,6 @@ There are two files named `$stub.mjs` that live in the `api` and `pages` directo
If there is no Markdown file that matches the path, a 404 error will be returned.
-
## Install
- clone this repo
diff --git a/app/data/conf-2023.json b/app/data/conf-2023.json
index 3047ec7..2487ec9 100644
--- a/app/data/conf-2023.json
+++ b/app/data/conf-2023.json
@@ -208,50 +208,50 @@
],
"workshops": [
{
- "id": "managing-data-in-a-react-application-aug-7",
- "title": "Managing Data in a React Application (Aug 7)",
- "abstract": "Fast user experiences are a requirement, and the developer tools for managing data handling have never been better! In this hands-on workshop, Eve Porcello will take a look at some of the preeminent patterns for building amazing user experiences with React. She'll also explore the latest features of React Server Components and how they can be leveraged with modern frameworks.\n\nTopics include: Data Loading Overview and Historical Tour, Understanding Streams, Suspense, React Server Components , GraphQL (Relay Fragments and Apollo Client), Exploring Next.js, Remix, and other frameworks",
- "short": "In this hands-on workshop, Eve Porcello will take a look at some of the preeminent patterns for building amazing user experiences with React. She'll also explore the latest features of React Server Components and how they can be leveraged with modern frameworks",
- "topics": ["react.js", "next.js", "remix", "react server components"],
- "speaker": {
- "id": "eve-porcello",
- "name": "Eve Porcello",
- "twitter": "EvePorcello",
- "company": "Moon Highway",
- "photo": "eve-porcello.jpg",
- "pronouns": "she/her",
- "location": "Bend, OR"
- }
- },
- {
- "id": "introduction-to-typescript-aug-9",
- "title": "Introduction to TypeScript (Aug 9)",
- "abstract": "Join Josh Goldberg in understanding the foundations of TypeScript! He'll cover how its type system works, how it lets you model user code, and -best of all- the benefits it can give you as a developer. And he'll show some great IDE tips & tricks enabled by TypeScript. β‘",
- "topics": ["typescript"],
- "speaker": {
- "id": "josh-goldberg",
- "name": "Josh Goldberg",
- "twitter": "JoshuaKGoldberg",
- "company": "typescript-eslint",
- "photo": "josh-goldberg.jpg",
- "location": "Philadelphia, PA"
- }
- },
- {
- "id": "building-ai-apps-aug-10",
- "title": "Building AI Apps (Aug 10)",
- "short": "Join Brook Riggio In this hands-on workshop on building AI app! Learn about how to integrate generative AI capabilities into new or existing projects, within your own UI, fully utilizing your own domain knowledge. Whether you want to launch your own AI-powered product, or incorporate cutting-edge functionality into your existing offerings, this workshop will give you the practical details and a vision of what's even possible to inspire your next generation of applications.",
- "abstract": "The power of Large Language Models (LLMs) is reshaping what our apps are capable of, π€ and how we build them. Get hands-on experience in this workshop, where you will build 2 distinct apps that leverage AI's strengths... and we'll see how to use AI tools to help us code more effectively!\n\nWe will leverage open source and best-of-breed options to:\n\n- Create an interactive customer service chat agent for your website, equipped to respond with your deep domain knowledge to boost conversions.\n- Deploy open models to AWS, for even more control over the data flow, and system prompts. π©\n\nWhether you're looking to launch π a new AI-driven product or elevate your current offerings, this workshop equips you with both practical experience and a vision for the future to inspire your next generation of applications. π",
- "topics": ["AI"],
- "speaker": {
- "id": "brook-riggio",
- "name": "Brook Riggio",
- "linkedin": "brookr",
- "company": "Code Fellows",
- "photo": "brook-riggio.jpg",
- "location": "Seattle, WA"
- }
+ "id": "managing-data-in-a-react-application-aug-7",
+ "title": "Managing Data in a React Application (Aug 7)",
+ "abstract": "Fast user experiences are a requirement, and the developer tools for managing data handling have never been better! In this hands-on workshop, Eve Porcello will take a look at some of the preeminent patterns for building amazing user experiences with React. She'll also explore the latest features of React Server Components and how they can be leveraged with modern frameworks.\n\nTopics include: Data Loading Overview and Historical Tour, Understanding Streams, Suspense, React Server Components , GraphQL (Relay Fragments and Apollo Client), Exploring Next.js, Remix, and other frameworks",
+ "short": "In this hands-on workshop, Eve Porcello will take a look at some of the preeminent patterns for building amazing user experiences with React. She'll also explore the latest features of React Server Components and how they can be leveraged with modern frameworks",
+ "topics": ["react.js", "next.js", "remix", "react server components"],
+ "speaker": {
+ "id": "eve-porcello",
+ "name": "Eve Porcello",
+ "twitter": "EvePorcello",
+ "company": "Moon Highway",
+ "photo": "eve-porcello.jpg",
+ "pronouns": "she/her",
+ "location": "Bend, OR"
+ }
+ },
+ {
+ "id": "introduction-to-typescript-aug-9",
+ "title": "Introduction to TypeScript (Aug 9)",
+ "abstract": "Join Josh Goldberg in understanding the foundations of TypeScript! He'll cover how its type system works, how it lets you model user code, and -best of all- the benefits it can give you as a developer. And he'll show some great IDE tips & tricks enabled by TypeScript. β‘",
+ "topics": ["typescript"],
+ "speaker": {
+ "id": "josh-goldberg",
+ "name": "Josh Goldberg",
+ "twitter": "JoshuaKGoldberg",
+ "company": "typescript-eslint",
+ "photo": "josh-goldberg.jpg",
+ "location": "Philadelphia, PA"
}
+ },
+ {
+ "id": "building-ai-apps-aug-10",
+ "title": "Building AI Apps (Aug 10)",
+ "short": "Join Brook Riggio In this hands-on workshop on building AI app! Learn about how to integrate generative AI capabilities into new or existing projects, within your own UI, fully utilizing your own domain knowledge. Whether you want to launch your own AI-powered product, or incorporate cutting-edge functionality into your existing offerings, this workshop will give you the practical details and a vision of what's even possible to inspire your next generation of applications.",
+ "abstract": "The power of Large Language Models (LLMs) is reshaping what our apps are capable of, π€ and how we build them. Get hands-on experience in this workshop, where you will build 2 distinct apps that leverage AI's strengths... and we'll see how to use AI tools to help us code more effectively!\n\nWe will leverage open source and best-of-breed options to:\n\n- Create an interactive customer service chat agent for your website, equipped to respond with your deep domain knowledge to boost conversions.\n- Deploy open models to AWS, for even more control over the data flow, and system prompts. π©\n\nWhether you're looking to launch π a new AI-driven product or elevate your current offerings, this workshop equips you with both practical experience and a vision for the future to inspire your next generation of applications. π",
+ "topics": ["AI"],
+ "speaker": {
+ "id": "brook-riggio",
+ "name": "Brook Riggio",
+ "linkedin": "brookr",
+ "company": "Code Fellows",
+ "photo": "brook-riggio.jpg",
+ "location": "Seattle, WA"
+ }
+ }
],
"organizers": [
{
diff --git a/app/data/events.json b/app/data/events.json
index 8d731c8..0d89474 100644
--- a/app/data/events.json
+++ b/app/data/events.json
@@ -1,378 +1,280 @@
[
- {
- "id": "may-2022",
- "link": "https://ti.to/event-loop/seattlejs-may-2022",
- "title": "SeattleJS May 2022",
- "date": "2022-05-11",
- "sponsors": [
- "collective-seattle"
- ],
- "talks": [
- "amber-hoak-may-2022"
- ]
- },
- {
- "id": "june-2022",
- "link": "https://ti.to/event-loop/seattlejs-june-2022",
- "title": "SeattleJS June 2022",
- "date": "2022-06-08",
- "sponsors": [
- "collective-seattle"
- ],
- "talks": [
- "jamund-ferguson-june-2022"
- ]
- },
- {
- "id": "july-2022",
- "link": "https://ti.to/event-loop/seattlejs-july-2022",
- "title": "SeattleJS July 2022",
- "date": "2022-07-13",
- "sponsors": [
- "collective-seattle"
- ],
- "talks": []
- },
- {
- "id": "august-2022",
- "link": "https://ti.to/event-loop/seattlejs-august-2022",
- "title": "SeattleJS August 2022",
- "date": "2022-08-10",
- "sponsors": [
- "collective-seattle"
- ],
- "talks": []
- },
- {
- "id": "september-2022",
- "link": "https://ti.to/event-loop/seattlejs-september-2022",
- "title": "SeattleJS September 2022",
- "date": "2022-09-14",
- "sponsors": [
- "collective-seattle"
- ],
- "talks": []
- },
- {
- "id": "october-2022",
- "link": "https://ti.to/event-loop/seattlejs-october-2022",
- "title": "SeattleJS October 2022",
- "date": "2022-10-12",
- "sponsors": [
- "svb"
- ],
- "talks": [
- "rachel-lee-nabors-october-2022"
- ]
- },
- {
- "id": "november-2022",
- "link": "https://ti.to/event-loop/seattlejs-november-2022",
- "title": "SeattleJS November 2022",
- "date": "2022-11-09",
- "sponsors": [
- "collective-seattle"
- ],
- "talks": [
- "matthew-bauer-november-2022",
- "brian-gershon-november-2022"
- ]
- },
- {
- "id": "december-2022",
- "link": "https://ti.to/event-loop/seattlejs-december-2022",
- "title": "SeattleJS December 2022 - Holiday Party Edition",
- "date": "2022-12-14",
- "sponsors": [
- "collective-seattle"
- ],
- "talks": []
- },
- {
- "id": "january-2023",
- "link": "https://ti.to/event-loop/seattlejs-january-2023",
- "title": "SeattleJS January 2023",
- "date": "2023-01-11",
- "sponsors": [
- "aws-skills-center-seattle"
- ],
- "talks": [
- "calvin-kipperman-january-2023",
- "josh-scotland-january-2023",
- "tim-obrien-january-2023"
- ],
- "description": "Join us and celebrate a great start to 2023 with your fellow web devs!\n\nTHANK YOU to the folks at AWS Skills Center Seattle for sponsoring this month! β€οΈ\n\nTickets are $5 and go up to $10 the day of, so don't delay!"
- },
- {
- "id": "february-2023",
- "link": "https://ti.to/event-loop/seattlejs-february-2023",
- "title": "SeattleJS February 2023",
- "date": "2023-02-08",
- "sponsors": [
- "collective-seattle",
- "remix"
- ],
- "talks": [
- "brian-tran-february-2023",
- "lupe-canaviri-maydana-februrary-2023",
- "jacob-ebey-february-2023"
- ],
- "description": "Join us and celebrate a great start to 2023 with your fellow web devs!\n\nTickets are $5 and go up to $10 the day of, so don't delay!"
- },
- {
- "id": "march-2023",
- "link": "https://ti.to/event-loop/seattlejs-march-2023",
- "title": "SeattleJS March 2023",
- "date": "2023-03-08",
- "sponsors": [
- "appwrite"
- ],
- "talks": [
- "aaroh-mankad-march-2023",
- "mason-lynass-march-2023",
- "chris-griffing-march-2023"
- ],
- "description": "Join your fellow web devs for an evening of talks, networking and fun! Tickets are only $5 but go up to $10 the day of, so don't delay!"
- },
- {
- "id": "april-2023",
- "link": "https://ti.to/event-loop/seattlejs-april-2023",
- "title": "SeattleJS April 2023",
- "date": "2023-04-12",
- "sponsors": [
- "amplication"
- ],
- "talks": [
- "michael-solati-april-2023",
- "philip-swan-april-2023",
- "catherine-johnson-april-2023"
- ],
- "description": "Join your fellow web devs for an evening of talks, networking and fun! Tickets are only $5 but go up to $10 the day of, so don't delay!"
- },
- {
- "id": "may-2023",
- "link": "https://ti.to/event-loop/seattlejs-may-2023",
- "title": "SeattleJS May 2023",
- "date": "2023-05-10",
- "sponsors": [],
- "talks": [
- "spenser-solys-may-2023",
- "tiger-oakes-may-2023"
- ],
- "description": "Join your fellow web devs for an evening of talks, networking and fun! Tickets are only $5 but go up to $10 the day of, so don't delay!"
- },
- {
- "id": "june-2023",
- "link": "https://ti.to/event-loop/seattlejs-june-2023",
- "title": "SeattleJS June 2023",
- "date": "2023-06-14",
- "sponsors": [
- "courier"
- ],
- "talks": [
- "cristina-rodriguez-june-2023",
- "aiden-bai-june-2023",
- "dm-liao-june-2023"
- ],
- "description": "Join your fellow web devs for an evening of talks, networking and fun! Tickets are only $5 but go up to $10 the day of, so don't delay!"
- },
- {
- "id": "july-2023",
- "link": "https://ti.to/event-loop/seattlejs-july-2023",
- "title": "SeattleJS July 2023",
- "date": "2023-07-12",
- "sponsors": [
- "twilio"
- ],
- "talks": [
- "chris-griffing-july-2023",
- "michael-fitzgerald-july-2023",
- "geoff-rich-july-2023"
- ],
- "description": ""
- },
- {
- "id": "september-2023",
- "link": "https://ti.to/event-loop/seattlejs-september-2023",
- "title": "SeattleJS September 2023",
- "date": "2023-09-13",
- "sponsors": [
- "runme.dev"
- ],
- "talks": [
- "jan-miksovsky-september-2023",
- "peli-de-halleux-september-2023",
- "ben-lower-september-2023"
- ],
- "description": ""
- },
- {
- "id": "october-2023",
- "link": "https://ti.to/event-loop/seattlejs-october-2023",
- "title": "SeattleJS October 2023",
- "date": "2023-10-11",
- "sponsors": [
- "courier"
- ],
- "talks": [
- "caleb-diehl-october-2023",
- "alan-gonzalez-october-2023"
- ],
- "description": "Thanks to [Gearhouse](https://joingearhouse.com) for hosting us this month. We will be at their Capitol Hill location for October.\n\n π [800 E Thomas St, Seattle, WA 98102](https://maps.app.goo.gl/LRVEp4yudBrHHRue7)\n\n Join your fellow web devs for an evening of talks, networking and fun! Tickets are only $5 but go up to $10 the day of, so don't delay!"
- },
- {
- "id": "november-2023",
- "link": "https://ti.to/event-loop/seattlejs-november-2023",
- "title": "SeattleJS November 2023",
- "date": "2023-11-08",
- "sponsors": [
- "polaris"
- ],
- "talks": [
- "mike-ryan-november-2023",
- "doug-wade-november-2023"
- ],
- "description": "Join your fellow web devs for an evening of talks, networking and fun! Tickets are only $5 but go up to $10 the day of, so don't delay!\n\nπ [The Collective Seattle, 400 Dexter Ave N, Seattle, WA 98109](https://maps.app.goo.gl/iFCSeqBsv7gFKEQ78)"
- },
- {
- "id": "holiday-party-2023",
- "link": "https://ti.to/event-loop/seattlejs-holiday-party-2023",
- "title": "SeattleJS Holiday Party 2023",
- "date": "2023-12-13",
- "sponsors": [
- "seekout"
- ],
- "talks": [],
- "description": "Join us for our annual SeattleJS Holiday Party! Come hang out with your fellow web developers and celebrate the holidays in style at The Collective. Consider bringing a plus one, the more the merrier!\n Tickets are $5 and go up to $10 the day of, so don't delay!"
- },
- {
- "id": "january-2024",
- "link": "https://ti.to/event-loop/seattlejs-january-2024",
- "title": "SeattleJS January 2024",
- "date": "2024-01-10",
- "sponsors": [],
- "talks": [],
- "description": "Join your fellow web devs for an evening of networking and fun!\n\nπ
Wednesday, January 10\n\nπ 5:30pm - 8pm\n\nπ [The Collective Seattle, 400 Dexter Ave N, Seattle, WA 98109](https://maps.app.goo.gl/iFCSeqBsv7gFKEQ78)"
- },
- {
- "id": "february-2024",
- "link": "https://ti.to/event-loop/seattlejs-february-2024",
- "title": "SeattleJS February 2024",
- "date": "2024-02-14",
- "sponsors": [
- "datastax"
- ],
- "talks": [
- "dm-liao-february-2024",
- "fx-wood-february-2024"
- ],
- "description": "Join your fellow web devs for an evening of talks, networking and fun! Tickets are only $5 but go up to $10 the day of, so don't delay!\n\nπ [The Collective Seattle, 400 Dexter Ave N, Seattle, WA 98109](https://maps.app.goo.gl/iFCSeqBsv7gFKEQ78)"
- },
- {
- "id": "march-2024",
- "link": "https://ti.to/event-loop/seattlejs-march-2024",
- "title": "SeattleJS March 2024",
- "date": "2024-03-13",
- "sponsors": [
- "fireproof"
- ],
- "talks": [
- "shruti-kapoor-march-2024",
- "eric-jensen-march-2024"
- ],
- "description": "Join your fellow web devs for an evening of talks, networking and fun! Tickets are only $5 but go up to $10 the day of, so don't delay!\n\nπ [The Collective Seattle, 400 Dexter Ave N, Seattle, WA 98109](https://maps.app.goo.gl/iFCSeqBsv7gFKEQ78)"
- },
- {
- "id": "april-2024",
- "link": "https://ti.to/event-loop/seattlejs-april-2024",
- "title": "SeattleJS April 2024",
- "date": "2024-04-10",
- "sponsors": [],
- "talks": [
- "herrington-darkholme-april-2024",
- "john-pham-april-2024"
- ],
- "description": "Join your fellow web devs for an evening of talks, networking and fun! Register ahead of time at the link below. Drink tickets are only $5 but go up to $10 the day of, so don't delay!\n\nπ [The Collective Seattle, 400 Dexter Ave N, Seattle, WA 98109](https://maps.app.goo.gl/iFCSeqBsv7gFKEQ78)"
- },
- {
- "id": "may-2024",
- "link": "https://ti.to/event-loop/seattlejs-may-2024",
- "title": "SeattleJS May 2024",
- "date": "2024-05-08",
- "sponsors": [
- "sentry"
- ],
- "talks": [
- "allan-deutsch-may-2024",
- "sarah-guthals-phd-may-2024"
- ],
- "description": "Join your fellow web devs for an evening of talks, networking and fun! Register ahead of time at the link below. Drink tickets are only $5 but go up to $10 the day of, so don't delay!\n\nπ [The Collective Seattle, 400 Dexter Ave N, Seattle, WA 98109](https://maps.app.goo.gl/iFCSeqBsv7gFKEQ78)"
- },
- {
- "id": "july-2024",
- "link": "https://lu.ma/0ouxs9r2",
- "title": "SeattleJS July 2024",
- "date": "2024-07-10",
- "sponsors": [],
- "talks": [
- "chris-griffing-july-2024"
- ],
- "description": "Join your fellow web devs for an evening of talks, networking and fun! Register ahead of time at the link below.\n\nNOTE: We are at Silicon Valley Bank this time, NOT our previous location!\n\n"
- },
- {
- "id": "august-2024",
- "link": "https://lu.ma/510fwjo4",
- "title": "SeattleJS August 2024",
- "date": "2024-08-14",
- "sponsors": [],
- "talks": [
- "ben-van-citters-august-2024",
- "allan-deutsch-august-2024"
- ],
- "description": "Join your fellow web devs for an evening of talks, networking and fun! Register ahead of time at the link below.\n\nNOTE: We are at Silicon Valley Bank this month!\n\n"
- },
- {
- "id": "september-2024",
- "title": "SeattleJS September 2024",
- "date": "2024-09-11",
- "sponsors": [],
- "talks": [
- "nicholas-patti-september-2024"
- ],
- "description": "Join your fellow web devs for an evening of talks, networking and fun! Register ahead of time at the link below.\n\nNOTE: We are at Startup Hall on UW campus this month!\n\n"
- },
- {
- "id": "october-2024",
- "title": "SeattleJS October 2024",
- "date": "2024-10-09",
- "sponsors": [],
- "talks": [
- "erik-marks-october-2024"
- ],
- "description": "Join your fellow web devs for an evening of talks, networking and fun! Register ahead of time at the link below.\n\nNOTE: We are at Silicon Valley Bank this month!\n\n"
- },
- {
- "id": "november-2024",
- "title": "SeattleJS November 2024",
- "date": "2024-11-13",
- "sponsors": [],
- "talks": [
- "cat-johnson-november-2024",
- "stacy-davis1-november-2024"
- ],
- "description": "Join your fellow web devs for an evening of talks, networking and fun! Register ahead of time at the link below.\n\nNOTE: We are at Startup Hall on UW campus this month!\n\n"
- },
- {
- "id": "january-2025",
- "link": "https://lu.ma/s4s4mqxy",
- "title": "SeattleJS January 2025",
- "date": "2025-01-08",
- "sponsors": [],
- "talks": [
- "justin-castilla-january-2025",
- "peli-de-halleux-january-2025",
- "andrew-enyeart-january-2025"
- ],
- "description": "Join your fellow web devs for an evening of talks, networking and fun! Register ahead of time at the link below.\n\nNOTE: We are at Just The Tap in Belltown this month!\n\n"
- }
-]
\ No newline at end of file
+ {
+ "id": "may-2022",
+ "link": "https://ti.to/event-loop/seattlejs-may-2022",
+ "title": "SeattleJS May 2022",
+ "date": "2022-05-11",
+ "sponsors": ["collective-seattle"],
+ "talks": ["amber-hoak-may-2022"]
+ },
+ {
+ "id": "june-2022",
+ "link": "https://ti.to/event-loop/seattlejs-june-2022",
+ "title": "SeattleJS June 2022",
+ "date": "2022-06-08",
+ "sponsors": ["collective-seattle"],
+ "talks": ["jamund-ferguson-june-2022"]
+ },
+ {
+ "id": "july-2022",
+ "link": "https://ti.to/event-loop/seattlejs-july-2022",
+ "title": "SeattleJS July 2022",
+ "date": "2022-07-13",
+ "sponsors": ["collective-seattle"],
+ "talks": []
+ },
+ {
+ "id": "august-2022",
+ "link": "https://ti.to/event-loop/seattlejs-august-2022",
+ "title": "SeattleJS August 2022",
+ "date": "2022-08-10",
+ "sponsors": ["collective-seattle"],
+ "talks": []
+ },
+ {
+ "id": "september-2022",
+ "link": "https://ti.to/event-loop/seattlejs-september-2022",
+ "title": "SeattleJS September 2022",
+ "date": "2022-09-14",
+ "sponsors": ["collective-seattle"],
+ "talks": []
+ },
+ {
+ "id": "october-2022",
+ "link": "https://ti.to/event-loop/seattlejs-october-2022",
+ "title": "SeattleJS October 2022",
+ "date": "2022-10-12",
+ "sponsors": ["svb"],
+ "talks": ["rachel-lee-nabors-october-2022"]
+ },
+ {
+ "id": "november-2022",
+ "link": "https://ti.to/event-loop/seattlejs-november-2022",
+ "title": "SeattleJS November 2022",
+ "date": "2022-11-09",
+ "sponsors": ["collective-seattle"],
+ "talks": ["matthew-bauer-november-2022", "brian-gershon-november-2022"]
+ },
+ {
+ "id": "december-2022",
+ "link": "https://ti.to/event-loop/seattlejs-december-2022",
+ "title": "SeattleJS December 2022 - Holiday Party Edition",
+ "date": "2022-12-14",
+ "sponsors": ["collective-seattle"],
+ "talks": []
+ },
+ {
+ "id": "january-2023",
+ "link": "https://ti.to/event-loop/seattlejs-january-2023",
+ "title": "SeattleJS January 2023",
+ "date": "2023-01-11",
+ "sponsors": ["aws-skills-center-seattle"],
+ "talks": [
+ "calvin-kipperman-january-2023",
+ "josh-scotland-january-2023",
+ "tim-obrien-january-2023"
+ ],
+ "description": "Join us and celebrate a great start to 2023 with your fellow web devs!\n\nTHANK YOU to the folks at AWS Skills Center Seattle for sponsoring this month! β€οΈ\n\nTickets are $5 and go up to $10 the day of, so don't delay!"
+ },
+ {
+ "id": "february-2023",
+ "link": "https://ti.to/event-loop/seattlejs-february-2023",
+ "title": "SeattleJS February 2023",
+ "date": "2023-02-08",
+ "sponsors": ["collective-seattle", "remix"],
+ "talks": [
+ "brian-tran-february-2023",
+ "lupe-canaviri-maydana-februrary-2023",
+ "jacob-ebey-february-2023"
+ ],
+ "description": "Join us and celebrate a great start to 2023 with your fellow web devs!\n\nTickets are $5 and go up to $10 the day of, so don't delay!"
+ },
+ {
+ "id": "march-2023",
+ "link": "https://ti.to/event-loop/seattlejs-march-2023",
+ "title": "SeattleJS March 2023",
+ "date": "2023-03-08",
+ "sponsors": ["appwrite"],
+ "talks": [
+ "aaroh-mankad-march-2023",
+ "mason-lynass-march-2023",
+ "chris-griffing-march-2023"
+ ],
+ "description": "Join your fellow web devs for an evening of talks, networking and fun! Tickets are only $5 but go up to $10 the day of, so don't delay!"
+ },
+ {
+ "id": "april-2023",
+ "link": "https://ti.to/event-loop/seattlejs-april-2023",
+ "title": "SeattleJS April 2023",
+ "date": "2023-04-12",
+ "sponsors": ["amplication"],
+ "talks": [
+ "michael-solati-april-2023",
+ "philip-swan-april-2023",
+ "catherine-johnson-april-2023"
+ ],
+ "description": "Join your fellow web devs for an evening of talks, networking and fun! Tickets are only $5 but go up to $10 the day of, so don't delay!"
+ },
+ {
+ "id": "may-2023",
+ "link": "https://ti.to/event-loop/seattlejs-may-2023",
+ "title": "SeattleJS May 2023",
+ "date": "2023-05-10",
+ "sponsors": [],
+ "talks": ["spenser-solys-may-2023", "tiger-oakes-may-2023"],
+ "description": "Join your fellow web devs for an evening of talks, networking and fun! Tickets are only $5 but go up to $10 the day of, so don't delay!"
+ },
+ {
+ "id": "june-2023",
+ "link": "https://ti.to/event-loop/seattlejs-june-2023",
+ "title": "SeattleJS June 2023",
+ "date": "2023-06-14",
+ "sponsors": ["courier"],
+ "talks": [
+ "cristina-rodriguez-june-2023",
+ "aiden-bai-june-2023",
+ "dm-liao-june-2023"
+ ],
+ "description": "Join your fellow web devs for an evening of talks, networking and fun! Tickets are only $5 but go up to $10 the day of, so don't delay!"
+ },
+ {
+ "id": "july-2023",
+ "link": "https://ti.to/event-loop/seattlejs-july-2023",
+ "title": "SeattleJS July 2023",
+ "date": "2023-07-12",
+ "sponsors": ["twilio"],
+ "talks": [
+ "chris-griffing-july-2023",
+ "michael-fitzgerald-july-2023",
+ "geoff-rich-july-2023"
+ ],
+ "description": ""
+ },
+ {
+ "id": "september-2023",
+ "link": "https://ti.to/event-loop/seattlejs-september-2023",
+ "title": "SeattleJS September 2023",
+ "date": "2023-09-13",
+ "sponsors": ["runme.dev"],
+ "talks": [
+ "jan-miksovsky-september-2023",
+ "peli-de-halleux-september-2023",
+ "ben-lower-september-2023"
+ ],
+ "description": ""
+ },
+ {
+ "id": "october-2023",
+ "link": "https://ti.to/event-loop/seattlejs-october-2023",
+ "title": "SeattleJS October 2023",
+ "date": "2023-10-11",
+ "sponsors": ["courier"],
+ "talks": ["caleb-diehl-october-2023", "alan-gonzalez-october-2023"],
+ "description": "Thanks to [Gearhouse](https://joingearhouse.com) for hosting us this month. We will be at their Capitol Hill location for October.\n\n π [800 E Thomas St, Seattle, WA 98102](https://maps.app.goo.gl/LRVEp4yudBrHHRue7)\n\n Join your fellow web devs for an evening of talks, networking and fun! Tickets are only $5 but go up to $10 the day of, so don't delay!"
+ },
+ {
+ "id": "november-2023",
+ "link": "https://ti.to/event-loop/seattlejs-november-2023",
+ "title": "SeattleJS November 2023",
+ "date": "2023-11-08",
+ "sponsors": ["polaris"],
+ "talks": ["mike-ryan-november-2023", "doug-wade-november-2023"],
+ "description": "Join your fellow web devs for an evening of talks, networking and fun! Tickets are only $5 but go up to $10 the day of, so don't delay!\n\nπ [The Collective Seattle, 400 Dexter Ave N, Seattle, WA 98109](https://maps.app.goo.gl/iFCSeqBsv7gFKEQ78)"
+ },
+ {
+ "id": "holiday-party-2023",
+ "link": "https://ti.to/event-loop/seattlejs-holiday-party-2023",
+ "title": "SeattleJS Holiday Party 2023",
+ "date": "2023-12-13",
+ "sponsors": ["seekout"],
+ "talks": [],
+ "description": "Join us for our annual SeattleJS Holiday Party! Come hang out with your fellow web developers and celebrate the holidays in style at The Collective. Consider bringing a plus one, the more the merrier!\n Tickets are $5 and go up to $10 the day of, so don't delay!"
+ },
+ {
+ "id": "january-2024",
+ "link": "https://ti.to/event-loop/seattlejs-january-2024",
+ "title": "SeattleJS January 2024",
+ "date": "2024-01-10",
+ "sponsors": [],
+ "talks": [],
+ "description": "Join your fellow web devs for an evening of networking and fun!\n\nπ
Wednesday, January 10\n\nπ 5:30pm - 8pm\n\nπ [The Collective Seattle, 400 Dexter Ave N, Seattle, WA 98109](https://maps.app.goo.gl/iFCSeqBsv7gFKEQ78)"
+ },
+ {
+ "id": "february-2024",
+ "link": "https://ti.to/event-loop/seattlejs-february-2024",
+ "title": "SeattleJS February 2024",
+ "date": "2024-02-14",
+ "sponsors": ["datastax"],
+ "talks": ["dm-liao-february-2024", "fx-wood-february-2024"],
+ "description": "Join your fellow web devs for an evening of talks, networking and fun! Tickets are only $5 but go up to $10 the day of, so don't delay!\n\nπ [The Collective Seattle, 400 Dexter Ave N, Seattle, WA 98109](https://maps.app.goo.gl/iFCSeqBsv7gFKEQ78)"
+ },
+ {
+ "id": "march-2024",
+ "link": "https://ti.to/event-loop/seattlejs-march-2024",
+ "title": "SeattleJS March 2024",
+ "date": "2024-03-13",
+ "sponsors": ["fireproof"],
+ "talks": ["shruti-kapoor-march-2024", "eric-jensen-march-2024"],
+ "description": "Join your fellow web devs for an evening of talks, networking and fun! Tickets are only $5 but go up to $10 the day of, so don't delay!\n\nπ [The Collective Seattle, 400 Dexter Ave N, Seattle, WA 98109](https://maps.app.goo.gl/iFCSeqBsv7gFKEQ78)"
+ },
+ {
+ "id": "april-2024",
+ "link": "https://ti.to/event-loop/seattlejs-april-2024",
+ "title": "SeattleJS April 2024",
+ "date": "2024-04-10",
+ "sponsors": [],
+ "talks": ["herrington-darkholme-april-2024", "john-pham-april-2024"],
+ "description": "Join your fellow web devs for an evening of talks, networking and fun! Register ahead of time at the link below. Drink tickets are only $5 but go up to $10 the day of, so don't delay!\n\nπ [The Collective Seattle, 400 Dexter Ave N, Seattle, WA 98109](https://maps.app.goo.gl/iFCSeqBsv7gFKEQ78)"
+ },
+ {
+ "id": "may-2024",
+ "link": "https://ti.to/event-loop/seattlejs-may-2024",
+ "title": "SeattleJS May 2024",
+ "date": "2024-05-08",
+ "sponsors": ["sentry"],
+ "talks": ["allan-deutsch-may-2024", "sarah-guthals-phd-may-2024"],
+ "description": "Join your fellow web devs for an evening of talks, networking and fun! Register ahead of time at the link below. Drink tickets are only $5 but go up to $10 the day of, so don't delay!\n\nπ [The Collective Seattle, 400 Dexter Ave N, Seattle, WA 98109](https://maps.app.goo.gl/iFCSeqBsv7gFKEQ78)"
+ },
+ {
+ "id": "july-2024",
+ "link": "https://lu.ma/0ouxs9r2",
+ "title": "SeattleJS July 2024",
+ "date": "2024-07-10",
+ "sponsors": [],
+ "talks": ["chris-griffing-july-2024"],
+ "description": "Join your fellow web devs for an evening of talks, networking and fun! Register ahead of time at the link below.\n\nNOTE: We are at Silicon Valley Bank this time, NOT our previous location!\n\n"
+ },
+ {
+ "id": "august-2024",
+ "link": "https://lu.ma/510fwjo4",
+ "title": "SeattleJS August 2024",
+ "date": "2024-08-14",
+ "sponsors": [],
+ "talks": ["ben-van-citters-august-2024", "allan-deutsch-august-2024"],
+ "description": "Join your fellow web devs for an evening of talks, networking and fun! Register ahead of time at the link below.\n\nNOTE: We are at Silicon Valley Bank this month!\n\n"
+ },
+ {
+ "id": "september-2024",
+ "title": "SeattleJS September 2024",
+ "date": "2024-09-11",
+ "sponsors": [],
+ "talks": ["nicholas-patti-september-2024"],
+ "description": "Join your fellow web devs for an evening of talks, networking and fun! Register ahead of time at the link below.\n\nNOTE: We are at Startup Hall on UW campus this month!\n\n"
+ },
+ {
+ "id": "october-2024",
+ "title": "SeattleJS October 2024",
+ "date": "2024-10-09",
+ "sponsors": [],
+ "talks": ["erik-marks-october-2024"],
+ "description": "Join your fellow web devs for an evening of talks, networking and fun! Register ahead of time at the link below.\n\nNOTE: We are at Silicon Valley Bank this month!\n\n"
+ },
+ {
+ "id": "november-2024",
+ "title": "SeattleJS November 2024",
+ "date": "2024-11-13",
+ "sponsors": [],
+ "talks": ["cat-johnson-november-2024", "stacy-davis1-november-2024"],
+ "description": "Join your fellow web devs for an evening of talks, networking and fun! Register ahead of time at the link below.\n\nNOTE: We are at Startup Hall on UW campus this month!\n\n"
+ }
+]
diff --git a/app/data/speakers.json b/app/data/speakers.json
index 78ce047..fe847d4 100644
--- a/app/data/speakers.json
+++ b/app/data/speakers.json
@@ -1,367 +1,351 @@
[
- {
- "id": "amber-hoak",
- "name": "Amber Hoak",
- "company": "Microsoft Research",
- "twitter": "amber_hoak",
- "photo": "amber-hoak.jpg"
- },
- {
- "id": "jamund-ferguson",
- "name": "Jamund Ferguson",
- "company": "PayPal",
- "twitter": "xjamundx",
- "photo": "jamund-ferguson.jpg"
- },
- {
- "id": "rachel-lee-nabors",
- "name": "Rachel Lee Nabors",
- "company": "AWS Amplify",
- "twitter": "rachelnabors",
- "photo": "rachel-lee-nabors.jpg"
- },
- {
- "id": "matthew-bauer",
- "name": "Matthew Bauer",
- "company": "WebPinata",
- "twitter": "LotusBladeStorm",
- "photo": "matthew-bauer.jpg"
- },
- {
- "id": "brian-gershon",
- "name": "Brian Gershon",
- "company": "Freelance",
- "twitter": "brianfive",
- "photo": "brian-gershon.jpg"
- },
- {
- "id": "calvin-kipperman",
- "name": "Calvin Kipperman",
- "company": "Lyft",
- "twitter": "emnudge",
- "photo": "calvin-kipperman.jpg"
- },
- {
- "id": "tim-obrien",
- "name": "Tim O\"Brien",
- "company": "Wagner Custom Skis",
- "twitter": "tobrien",
- "photo": "tim-obrien.jpg"
- },
- {
- "id": "josh-scotland",
- "name": "Josh Scotland",
- "company": "Startup",
- "photo": "josh-scotland.jpg"
- },
- {
- "id": "brian-tran",
- "name": "Brian Tran",
- "company": "REMAX",
- "photo": "brian-tran.jpg",
- "twitter": "_briantran_"
- },
- {
- "id": "lupe-canaviri-maydana",
- "name": "Lupe Canaviri Maydana",
- "company": "Microsoft",
- "photo": "lupe-canaviri-maydana.png",
- "twitter": "luucamay_"
- },
- {
- "id": "jacob-ebey",
- "name": "Jacob Ebey",
- "company": "Remix / Shopify",
- "photo": "jacob-ebey.jpg",
- "twitter": "ebey_jacob"
- },
- {
- "id": "aaroh-mankad",
- "name": "Aaroh Mankad",
- "company": "Plaid",
- "photo": "aaroh-mankad.jpg",
- "twitter": "aarohmankad"
- },
- {
- "id": "chris-griffing",
- "name": "Chris Griffing",
- "company": "GitKraken",
- "photo": "chris-griffing.jpg",
- "twitter": "cmgriffing"
- },
- {
- "id": "mason-lynass",
- "name": "Mason Lynass",
- "company": "Freelance",
- "photo": "mason-lynass.jpg"
- },
- {
- "id": "catherine-johnson",
- "name": "Catherine Johnson",
- "company": "Microsoft",
- "photo": "catherine-johnson.jpg",
- "twitter": ""
- },
- {
- "id": "philip-swan",
- "name": "Philip Swan",
- "company": "The Atlantis Project",
- "photo": "philip-swan.png",
- "twitter": "tetheredring"
- },
- {
- "id": "michael-solati",
- "name": "Michael Solati",
- "company": "Amplication",
- "photo": "michael-solati.jpg",
- "twitter": "michaelsolati"
- },
- {
- "id": "spenser-solys",
- "name": "Spenser Solys",
- "company": "Project Archer",
- "photo": "spenser-solys.jpg",
- "twitter": "scub3d"
- },
- {
- "id": "tiger-oakes",
- "name": "Tiger Oakes",
- "company": "Microsoft",
- "photo": "tiger-oakes.jpg",
- "twitter": "Not_Woods"
- },
- {
- "id": "dm-liao",
- "name": "DM Liao",
- "company": "n/a",
- "photo": "dm-liao.jpg",
- "url": "https://amorphic.space",
- "pronouns": "they/them/theirs"
- },
- {
- "id": "aiden-bai",
- "name": "Aiden Bai",
- "company": "Dimension.dev",
- "photo": "aiden-bai.jpg",
- "twitter": "aidenybai",
- "pronouns": "he/him/his",
- "url": "https://aidenybai.com"
- },
- {
- "id": "cristina-rodriguez",
- "name": "Cristina Rodriguez",
- "company": "Techtonica.org",
- "photo": "cristina-rodriguez.jpg",
- "twitter": "yosola",
- "pronouns": "she/her/ella",
- "url": "https://www.linkedin.com/in/crissrodriguez/"
- },
- {
- "id": "geoff-rich",
- "name": "Geoff Rich",
- "company": "Ordergroove",
- "twitter": "geoffrich_",
- "pronouns": "he/him",
- "photo": "geoff-rich.jpg"
- },
- {
- "id": "alan-balasundaram",
- "name": "Alan Balasundaram",
- "company": "Expert Opinion MD",
- "twitter": "",
- "pronouns": "he/him",
- "photo": "alan-balasundaram.jpg"
- },
- {
- "id": "michael-fitzgerald",
- "name": "Michael Fitzgerald",
- "company": "Fresh Consulting",
- "twitter": "fitzgerald1337",
- "pronouns": "he/him/his",
- "photo": "michael-fitzgerald.png"
- },
- {
- "id": "jan-miksovsky",
- "name": "Jan Miksovsky",
- "company": "Independent",
- "mastadon": "https://fosstodon.org/@JanMiksovsky",
- "pronouns": "he/him",
- "photo": "jan-miksovsky.jpg"
- },
- {
- "id": "peli-de-halleux",
- "name": "Peli de Halleux",
- "company": "Microsoft",
- "twitter": "",
- "pronouns": "he/him",
- "photo": "peli-de-halleux.jpg"
- },
- {
- "id": "ben-lower",
- "name": "ben lower",
- "company": "Fixie",
- "twitter": "benlower",
- "pronouns": "he/him/his",
- "photo": "ben-lower.jpg"
- },
- {
- "id": "caleb-diehl",
- "name": "Caleb Diehl",
- "company": "The Associated Press",
- "twitter": "",
- "pronouns": "he/him",
- "photo": "caleb-diehl.jpg"
- },
- {
- "id": "alan-gonzalez",
- "name": "Alan Gonzalez",
- "company": "DevMatch",
- "twitter": "_alanboy",
- "pronouns": "he/him",
- "photo": "alan-gonzalez.jpg"
- },
- {
- "id": "mike-ryan",
- "name": "Mike Ryan",
- "company": "Polaris",
- "twitter": "mikeryandev",
- "pronouns": "he/him",
- "photo": "mike-ryan.jpg"
- },
- {
- "id": "doug-wade",
- "name": "Doug Wade",
- "company": "Skilljar",
- "twitter": "",
- "pronouns": "he/him",
- "photo": "doug-wade.jpg"
- },
- {
- "id": "fx-wood",
- "name": "FX Wood",
- "company": "Looking for Opportunities",
- "twitter": "",
- "pronouns": "he/him",
- "photo": "fx-wood.jpg"
- },
- {
- "id": "shruti-kapoor",
- "name": "Shruti Kapoor",
- "company": "Slack",
- "twitter": "shrutikapoor08",
- "pronouns": "she/her",
- "photo": "shruti-kapoor.jpg"
- },
- {
- "id": "j-chris-anderson",
- "name": "J Chris Anderson",
- "company": "Fireproof",
- "twitter": "jchris",
- "pronouns": "he/him",
- "photo": "j-chris-anderson.jpg"
- },
- {
- "id": "eric-jensen",
- "name": "Eric Jensen",
- "company": "Fireproof ",
- "twitter": "",
- "pronouns": "he/him",
- "photo": "eric-jensen.jpg"
- },
- {
- "id": "herrington-darkholme",
- "name": "Herrington Darkholme",
- "company": "Freelance",
- "twitter": "hd_nvim",
- "pronouns": "he/him/his",
- "photo": "herrington-darkholme.jpg"
- },
- {
- "id": "john-pham",
- "name": "John Pham",
- "company": "Vercel",
- "twitter": "johnphamous",
- "pronouns": "he/him/his",
- "photo": "john-pham.jpg"
- },
- {
- "id": "allan-deutsch",
- "name": "Allan Deutsch",
- "company": "The Startup",
- "twitter": "allandeutsch",
- "pronouns": "he/him",
- "photo": "allan-deutsch.png"
- },
- {
- "id": "sarah-guthals-phd",
- "name": "Sarah Guthals, PhD",
- "company": "Sentry",
- "twitter": "drguthals",
- "pronouns": "she/her/hers",
- "photo": "sarah-guthals-phd.png"
- },
- {
- "id": "josh-franklin",
- "name": "Josh Franklin",
- "company": "N/A",
- "twitter": "joshfranklin26",
- "pronouns": "he/him/his",
- "photo": "josh-franklin.jpg"
- },
- {
- "id": "ben-van-citters",
- "name": "Ben Van Citters",
- "company": "Algorithms & Beauty",
- "twitter": "",
- "pronouns": "He/him",
- "photo": "ben-van-citters.jpg"
- },
- {
- "id": "nicholas-patti",
- "name": "Nicholas Patti",
- "company": "Freelance",
- "twitter": "nickspatties",
- "pronouns": "he/him/his",
- "photo": "nicholas-patti.jpg"
- },
- {
- "id": "stacy-davis1",
- "name": "Stacy Davis",
- "company": "Species360",
- "twitter": "",
- "pronouns": "she/her/hers",
- "photo": "stacy-davis1.jpg"
- },
- {
- "id": "erik-marks",
- "name": "Erik Marks",
- "company": "Consensys / MetaMask",
- "twitter": "rekmarks",
- "pronouns": "he/they",
- "photo": "erik-marks.jpg"
- },
- {
- "id": "cat-johnson",
- "name": "Cat Johnson",
- "company": "Khan Academy",
- "twitter": "cat_themachines",
- "pronouns": "she/her",
- "photo": "cat-johnson.jpg"
- },
- {
- "id": "justin-castilla",
- "name": "Justin Castilla",
- "company": "Elastic",
- "twitter": "CastillaCodes",
- "pronouns": "He/Him/His",
- "photo": "justin-castilla.png"
- },
- {
- "id": "andrew-enyeart",
- "name": "Andrew Enyeart",
- "company": "22days",
- "twitter": "andrew_enyeart",
- "pronouns": "he/him",
- "photo": "andrew-enyeart.png"
- }
-]
\ No newline at end of file
+ {
+ "id": "amber-hoak",
+ "name": "Amber Hoak",
+ "company": "Microsoft Research",
+ "twitter": "amber_hoak",
+ "photo": "amber-hoak.jpg"
+ },
+ {
+ "id": "jamund-ferguson",
+ "name": "Jamund Ferguson",
+ "company": "PayPal",
+ "twitter": "xjamundx",
+ "photo": "jamund-ferguson.jpg"
+ },
+ {
+ "id": "rachel-lee-nabors",
+ "name": "Rachel Lee Nabors",
+ "company": "AWS Amplify",
+ "twitter": "rachelnabors",
+ "photo": "rachel-lee-nabors.jpg"
+ },
+ {
+ "id": "matthew-bauer",
+ "name": "Matthew Bauer",
+ "company": "WebPinata",
+ "twitter": "LotusBladeStorm",
+ "photo": "matthew-bauer.jpg"
+ },
+ {
+ "id": "brian-gershon",
+ "name": "Brian Gershon",
+ "company": "Freelance",
+ "twitter": "brianfive",
+ "photo": "brian-gershon.jpg"
+ },
+ {
+ "id": "calvin-kipperman",
+ "name": "Calvin Kipperman",
+ "company": "Lyft",
+ "twitter": "emnudge",
+ "photo": "calvin-kipperman.jpg"
+ },
+ {
+ "id": "tim-obrien",
+ "name": "Tim O\"Brien",
+ "company": "Wagner Custom Skis",
+ "twitter": "tobrien",
+ "photo": "tim-obrien.jpg"
+ },
+ {
+ "id": "josh-scotland",
+ "name": "Josh Scotland",
+ "company": "Startup",
+ "photo": "josh-scotland.jpg"
+ },
+ {
+ "id": "brian-tran",
+ "name": "Brian Tran",
+ "company": "REMAX",
+ "photo": "brian-tran.jpg",
+ "twitter": "_briantran_"
+ },
+ {
+ "id": "lupe-canaviri-maydana",
+ "name": "Lupe Canaviri Maydana",
+ "company": "Microsoft",
+ "photo": "lupe-canaviri-maydana.png",
+ "twitter": "luucamay_"
+ },
+ {
+ "id": "jacob-ebey",
+ "name": "Jacob Ebey",
+ "company": "Remix / Shopify",
+ "photo": "jacob-ebey.jpg",
+ "twitter": "ebey_jacob"
+ },
+ {
+ "id": "aaroh-mankad",
+ "name": "Aaroh Mankad",
+ "company": "Plaid",
+ "photo": "aaroh-mankad.jpg",
+ "twitter": "aarohmankad"
+ },
+ {
+ "id": "chris-griffing",
+ "name": "Chris Griffing",
+ "company": "GitKraken",
+ "photo": "chris-griffing.jpg",
+ "twitter": "cmgriffing"
+ },
+ {
+ "id": "mason-lynass",
+ "name": "Mason Lynass",
+ "company": "Freelance",
+ "photo": "mason-lynass.jpg"
+ },
+ {
+ "id": "catherine-johnson",
+ "name": "Catherine Johnson",
+ "company": "Microsoft",
+ "photo": "catherine-johnson.jpg",
+ "twitter": ""
+ },
+ {
+ "id": "philip-swan",
+ "name": "Philip Swan",
+ "company": "The Atlantis Project",
+ "photo": "philip-swan.png",
+ "twitter": "tetheredring"
+ },
+ {
+ "id": "michael-solati",
+ "name": "Michael Solati",
+ "company": "Amplication",
+ "photo": "michael-solati.jpg",
+ "twitter": "michaelsolati"
+ },
+ {
+ "id": "spenser-solys",
+ "name": "Spenser Solys",
+ "company": "Project Archer",
+ "photo": "spenser-solys.jpg",
+ "twitter": "scub3d"
+ },
+ {
+ "id": "tiger-oakes",
+ "name": "Tiger Oakes",
+ "company": "Microsoft",
+ "photo": "tiger-oakes.jpg",
+ "twitter": "Not_Woods"
+ },
+ {
+ "id": "dm-liao",
+ "name": "DM Liao",
+ "company": "n/a",
+ "photo": "dm-liao.jpg",
+ "url": "https://amorphic.space",
+ "pronouns": "they/them/theirs"
+ },
+ {
+ "id": "aiden-bai",
+ "name": "Aiden Bai",
+ "company": "Dimension.dev",
+ "photo": "aiden-bai.jpg",
+ "twitter": "aidenybai",
+ "pronouns": "he/him/his",
+ "url": "https://aidenybai.com"
+ },
+ {
+ "id": "cristina-rodriguez",
+ "name": "Cristina Rodriguez",
+ "company": "Techtonica.org",
+ "photo": "cristina-rodriguez.jpg",
+ "twitter": "yosola",
+ "pronouns": "she/her/ella",
+ "url": "https://www.linkedin.com/in/crissrodriguez/"
+ },
+ {
+ "id": "geoff-rich",
+ "name": "Geoff Rich",
+ "company": "Ordergroove",
+ "twitter": "geoffrich_",
+ "pronouns": "he/him",
+ "photo": "geoff-rich.jpg"
+ },
+ {
+ "id": "alan-balasundaram",
+ "name": "Alan Balasundaram",
+ "company": "Expert Opinion MD",
+ "twitter": "",
+ "pronouns": "he/him",
+ "photo": "alan-balasundaram.jpg"
+ },
+ {
+ "id": "michael-fitzgerald",
+ "name": "Michael Fitzgerald",
+ "company": "Fresh Consulting",
+ "twitter": "fitzgerald1337",
+ "pronouns": "he/him/his",
+ "photo": "michael-fitzgerald.png"
+ },
+ {
+ "id": "jan-miksovsky",
+ "name": "Jan Miksovsky",
+ "company": "Independent",
+ "mastadon": "https://fosstodon.org/@JanMiksovsky",
+ "pronouns": "he/him",
+ "photo": "jan-miksovsky.jpg"
+ },
+ {
+ "id": "peli-de-halleux",
+ "name": "Peli de Halleux",
+ "company": "Microsoft",
+ "twitter": "",
+ "pronouns": "he/him",
+ "photo": "peli-de-halleux.jpg"
+ },
+ {
+ "id": "ben-lower",
+ "name": "ben lower",
+ "company": "Fixie",
+ "twitter": "benlower",
+ "pronouns": "he/him/his",
+ "photo": "ben-lower.jpg"
+ },
+ {
+ "id": "caleb-diehl",
+ "name": "Caleb Diehl",
+ "company": "The Associated Press",
+ "twitter": "",
+ "pronouns": "he/him",
+ "photo": "caleb-diehl.jpg"
+ },
+ {
+ "id": "alan-gonzalez",
+ "name": "Alan Gonzalez",
+ "company": "DevMatch",
+ "twitter": "_alanboy",
+ "pronouns": "he/him",
+ "photo": "alan-gonzalez.jpg"
+ },
+ {
+ "id": "mike-ryan",
+ "name": "Mike Ryan",
+ "company": "Polaris",
+ "twitter": "mikeryandev",
+ "pronouns": "he/him",
+ "photo": "mike-ryan.jpg"
+ },
+ {
+ "id": "doug-wade",
+ "name": "Doug Wade",
+ "company": "Skilljar",
+ "twitter": "",
+ "pronouns": "he/him",
+ "photo": "doug-wade.jpg"
+ },
+ {
+ "id": "fx-wood",
+ "name": "FX Wood",
+ "company": "Looking for Opportunities",
+ "twitter": "",
+ "pronouns": "he/him",
+ "photo": "fx-wood.jpg"
+ },
+ {
+ "id": "shruti-kapoor",
+ "name": "Shruti Kapoor",
+ "company": "Slack",
+ "twitter": "shrutikapoor08",
+ "pronouns": "she/her",
+ "photo": "shruti-kapoor.jpg"
+ },
+ {
+ "id": "j-chris-anderson",
+ "name": "J Chris Anderson",
+ "company": "Fireproof",
+ "twitter": "jchris",
+ "pronouns": "he/him",
+ "photo": "j-chris-anderson.jpg"
+ },
+ {
+ "id": "eric-jensen",
+ "name": "Eric Jensen",
+ "company": "Fireproof ",
+ "twitter": "",
+ "pronouns": "he/him",
+ "photo": "eric-jensen.jpg"
+ },
+ {
+ "id": "herrington-darkholme",
+ "name": "Herrington Darkholme",
+ "company": "Freelance",
+ "twitter": "hd_nvim",
+ "pronouns": "he/him/his",
+ "photo": "herrington-darkholme.jpg"
+ },
+ {
+ "id": "john-pham",
+ "name": "John Pham",
+ "company": "Vercel",
+ "twitter": "johnphamous",
+ "pronouns": "he/him/his",
+ "photo": "john-pham.jpg"
+ },
+ {
+ "id": "allan-deutsch",
+ "name": "Allan Deutsch",
+ "company": "The Startup",
+ "twitter": "allandeutsch",
+ "pronouns": "he/him",
+ "photo": "allan-deutsch.png"
+ },
+ {
+ "id": "sarah-guthals-phd",
+ "name": "Sarah Guthals, PhD",
+ "company": "Sentry",
+ "twitter": "drguthals",
+ "pronouns": "she/her/hers",
+ "photo": "sarah-guthals-phd.png"
+ },
+ {
+ "id": "josh-franklin",
+ "name": "Josh Franklin",
+ "company": "N/A",
+ "twitter": "joshfranklin26",
+ "pronouns": "he/him/his",
+ "photo": "josh-franklin.jpg"
+ },
+ {
+ "id": "ben-van-citters",
+ "name": "Ben Van Citters",
+ "company": "Algorithms & Beauty",
+ "twitter": "",
+ "pronouns": "He/him",
+ "photo": "ben-van-citters.jpg"
+ },
+ {
+ "id": "nicholas-patti",
+ "name": "Nicholas Patti",
+ "company": "Freelance",
+ "twitter": "nickspatties",
+ "pronouns": "he/him/his",
+ "photo": "nicholas-patti.jpg"
+ },
+ {
+ "id": "stacy-davis1",
+ "name": "Stacy Davis",
+ "company": "Species360",
+ "twitter": "",
+ "pronouns": "she/her/hers",
+ "photo": "stacy-davis1.jpg"
+ },
+ {
+ "id": "erik-marks",
+ "name": "Erik Marks",
+ "company": "Consensys / MetaMask",
+ "twitter": "rekmarks",
+ "pronouns": "he/they",
+ "photo": "erik-marks.jpg"
+ },
+ {
+ "id": "cat-johnson",
+ "name": "Cat Johnson",
+ "company": "Khan Academy",
+ "twitter": "cat_themachines",
+ "pronouns": "she/her",
+ "photo": "cat-johnson.jpg"
+ }
+]
diff --git a/app/data/sponsors.json b/app/data/sponsors.json
index ba7758b..c8fa879 100644
--- a/app/data/sponsors.json
+++ b/app/data/sponsors.json
@@ -1,108 +1,108 @@
[
- {
- "id": "collective-seattle",
- "url": "https://www.collectiveseattle.com",
- "image": "collective.webp"
- },
- {
- "id": "svb",
- "url": "https://www.svb.com/startup-banking",
- "image": "svb.svg"
- },
- {
- "id": "aws-skills-center-seattle",
- "url": "https://aws.amazon.com/training/skills-centers/seattle-skills-center/",
- "image": "aws-skills-center-seattle.png"
- },
- {
- "id": "formidable",
- "url": "https://formidable.com",
- "image": "formidable.svg"
- },
- {
- "id": "customer-io",
- "url": "https://customer.io/",
- "image": "customer-io.svg"
- },
- {
- "id": "remix",
- "url": "https://remix.run/",
- "image": "remix.png"
- },
- {
- "id": "appwrite",
- "name": "Appwrite",
- "url": "https://appwrite.io",
- "image": "appwrite.jpg",
- "copy": "Appwrite is a secure open-source backend server provides the core APIs required to build web and mobile applications. Appwrite provides authentication, database, storage, functions, and advanced real-time capabilities."
- },
- {
- "id": "amplication",
- "name": "Amplication",
- "url": "https://amplication.com",
- "image": "amplication.png",
- "copy": "Amplication is an opensource based SaaS backend development solution that empowers professional developers to accelerate the development of Node.js applications. By using Amplication to build repetitive backend services, development teams will yield significant increases in developer productivity, reliability, and consistency of delivering critical applications to production."
- },
- {
- "id": "twilio",
- "name": "Twilio",
- "url": "https://www.twilio.com/en-us",
- "image": "twilio.png",
- "copy": "Twilio is the customer layer for the internet, powering the most engaging interactions companies build for their customers. We provide simple tools that solve hard problems, delivered as a developer-first cloud platform with global reach and no shenanigans pricing."
- },
- {
- "id": "aws",
- "name": "AWS",
- "url": "https://docs.amplify.aws/",
- "image": "aws.png",
- "copy": ""
- },
- {
- "id": "courier",
- "name": "Courier",
- "url": "https://courier.com?utm_campaign=devrel-meetups&utm_source=meetups&utm_medium=website",
- "image": "courier.png",
- "copy": ""
- },
- {
- "id": "runme.dev",
- "name": "Runme",
- "url": "https://runme.dev",
- "image": "runme.png",
- "copy": ""
- },
- {
- "id": "polaris",
- "url": "https://getpolaris.ai",
- "copy": "Polaris is an AI-powered site reliability tool that helps you detect incidents in real-time. Its tiny JavaScript SDK lets you instrument key user workflows to create indicators of reliability and latency. Apply thresholds to your indicators to define performance objectives and be alerted when your app begins to experience degradations.",
- "image": "polaris.png"
- },
- {
- "id": "seekout",
- "name": "SeekOut",
- "url": "https://www.seekout.com/",
- "copy": "Grow the talent you have.\nFind the talent you need.\nSeekOut empowers great people and companies to grow togetherβwith actionable insights at every step of your talent journey. ",
- "image": "seekout.png"
- },
- {
- "id": "datastax",
- "name": "DataStax",
- "url": "https://www.datastax.com/",
- "copy": "DataStax is your one-stop shop for building on GenAI.",
- "image": "datastax.png"
- },
- {
- "id": "fireproof",
- "name": "Fireproof",
- "url": "https://fireproof.storage/",
- "copy": "Fireproof is an [embedded database for collaborative applications](https://use-fireproof.com/). Install it in your front-end app, or use it in any serverless cloud or edge function. Fireproofβs document API includes live updates, flexible queries, binary attachments, encrypted block replication, and multi-user sync. Fireproof enables developers to ship interactive features faster in any deployment environment.\n\nDeploy your app however you like. Install the Fireproof module into your browser or server app code and sync using remixable open-source adapters. Experience live updates across peers so everyone can collaborate together, with any backend. Render dynamic HTML and interact with APIs from the edge. Connect with the community to add support for more backends.",
- "image": "fireproof.png"
- },
- {
- "id": "sentry",
- "name": "Sentry",
- "url": "https://sentry.io/welcome/",
- "copy": "Sentry is the application monitoring platform for development teams to holistically monitor their code health from the frontend to the backend to see clearer, solve quicker, and learn continuously.",
- "image": "sentry.png"
- }
-]
\ No newline at end of file
+ {
+ "id": "collective-seattle",
+ "url": "https://www.collectiveseattle.com",
+ "image": "collective.webp"
+ },
+ {
+ "id": "svb",
+ "url": "https://www.svb.com/startup-banking",
+ "image": "svb.svg"
+ },
+ {
+ "id": "aws-skills-center-seattle",
+ "url": "https://aws.amazon.com/training/skills-centers/seattle-skills-center/",
+ "image": "aws-skills-center-seattle.png"
+ },
+ {
+ "id": "formidable",
+ "url": "https://formidable.com",
+ "image": "formidable.svg"
+ },
+ {
+ "id": "customer-io",
+ "url": "https://customer.io/",
+ "image": "customer-io.svg"
+ },
+ {
+ "id": "remix",
+ "url": "https://remix.run/",
+ "image": "remix.png"
+ },
+ {
+ "id": "appwrite",
+ "name": "Appwrite",
+ "url": "https://appwrite.io",
+ "image": "appwrite.jpg",
+ "copy": "Appwrite is a secure open-source backend server provides the core APIs required to build web and mobile applications. Appwrite provides authentication, database, storage, functions, and advanced real-time capabilities."
+ },
+ {
+ "id": "amplication",
+ "name": "Amplication",
+ "url": "https://amplication.com",
+ "image": "amplication.png",
+ "copy": "Amplication is an opensource based SaaS backend development solution that empowers professional developers to accelerate the development of Node.js applications. By using Amplication to build repetitive backend services, development teams will yield significant increases in developer productivity, reliability, and consistency of delivering critical applications to production."
+ },
+ {
+ "id": "twilio",
+ "name": "Twilio",
+ "url": "https://www.twilio.com/en-us",
+ "image": "twilio.png",
+ "copy": "Twilio is the customer layer for the internet, powering the most engaging interactions companies build for their customers. We provide simple tools that solve hard problems, delivered as a developer-first cloud platform with global reach and no shenanigans pricing."
+ },
+ {
+ "id": "aws",
+ "name": "AWS",
+ "url": "https://docs.amplify.aws/",
+ "image": "aws.png",
+ "copy": ""
+ },
+ {
+ "id": "courier",
+ "name": "Courier",
+ "url": "https://courier.com?utm_campaign=devrel-meetups&utm_source=meetups&utm_medium=website",
+ "image": "courier.png",
+ "copy": ""
+ },
+ {
+ "id": "runme.dev",
+ "name": "Runme",
+ "url": "https://runme.dev",
+ "image": "runme.png",
+ "copy": ""
+ },
+ {
+ "id": "polaris",
+ "url": "https://getpolaris.ai",
+ "copy": "Polaris is an AI-powered site reliability tool that helps you detect incidents in real-time. Its tiny JavaScript SDK lets you instrument key user workflows to create indicators of reliability and latency. Apply thresholds to your indicators to define performance objectives and be alerted when your app begins to experience degradations.",
+ "image": "polaris.png"
+ },
+ {
+ "id": "seekout",
+ "name": "SeekOut",
+ "url": "https://www.seekout.com/",
+ "copy": "Grow the talent you have.\nFind the talent you need.\nSeekOut empowers great people and companies to grow togetherβwith actionable insights at every step of your talent journey. ",
+ "image": "seekout.png"
+ },
+ {
+ "id": "datastax",
+ "name": "DataStax",
+ "url": "https://www.datastax.com/",
+ "copy": "DataStax is your one-stop shop for building on GenAI.",
+ "image": "datastax.png"
+ },
+ {
+ "id": "fireproof",
+ "name": "Fireproof",
+ "url": "https://fireproof.storage/",
+ "copy": "Fireproof is an [embedded database for collaborative applications](https://use-fireproof.com/). Install it in your front-end app, or use it in any serverless cloud or edge function. Fireproofβs document API includes live updates, flexible queries, binary attachments, encrypted block replication, and multi-user sync. Fireproof enables developers to ship interactive features faster in any deployment environment.\n\nDeploy your app however you like. Install the Fireproof module into your browser or server app code and sync using remixable open-source adapters. Experience live updates across peers so everyone can collaborate together, with any backend. Render dynamic HTML and interact with APIs from the edge. Connect with the community to add support for more backends.",
+ "image": "fireproof.png"
+ },
+ {
+ "id": "sentry",
+ "name": "Sentry",
+ "url": "https://sentry.io/welcome/",
+ "copy": "Sentry is the application monitoring platform for development teams to holistically monitor their code health from the frontend to the backend to see clearer, solve quicker, and learn continuously.",
+ "image": "sentry.png"
+ }
+]
diff --git a/app/data/talks.json b/app/data/talks.json
index 76c09ff..4602c7d 100644
--- a/app/data/talks.json
+++ b/app/data/talks.json
@@ -1,621 +1,477 @@
[
- {
- "id": "amber-hoak-may-2022",
- "speaker_id": "amber-hoak",
- "event_id": "may-2022",
- "title": "Stumbling through ML in JavaScript: the good, the bad, and the computationally intensive",
- "abstract": "Does Tensorflow make you tense? Let's unpack how you can get started with ML in the browser, no math required. Learn how to build models on the browser, why we would use client-side ML, and what ML tooling (and lack of tooling) is in the js ecosystem."
- },
- {
- "id": "jamund-ferguson-june-2022",
- "speaker_id": "jamund-ferguson",
- "event_id": "june-2022",
- "title": "π§ Web Performance Horror Storiesπ§",
- "abstract": "Experience the cringe inducing horror caused by excessively slow web performance. Be shocked to hear about megabytes of CSS going unused, endlessly duplicated polyfills, input fields freezing the main thread for 50ms per keystroke and assorted other terrors. Together we'll dissect these performance poltergeists and vanquish them from our apps forever."
- },
- {
- "id": "rachel-lee-nabors-october-2022",
- "speaker_id": "rachel-lee-nabors",
- "event_id": "october-2022",
- "title": "The Human API",
- "abstract": "The only thing harder than writing scalable, usable code is teaching others how to wield it. As the sum of human knowledge continues to grow, so too do the challenges of teaching each other what we need to know not only to build toward the future but also to contribute to the tools of its construction. If only it were possible to transfer knowledge from one engineer directly into the minds of other engineers, like a human RAID! Rachel Lee Nabors loves building such mechanisms for knowledge transfer, from video guides to documentation to curricula. In this talk, they will discuss what doesn't work, what has worked on projects like React and React Native, and what can work for any other open source project today. Teaching is hard. But there are solid ways to approach knowledge transfer at scale."
- },
- {
- "id": "matthew-bauer-november-2022",
- "speaker_id": "matthew-bauer",
- "event_id": "november-2022",
- "title": "Two left feet and an earthquake in Vue: Composition API or Options API?",
- "abstract": "Vue 3 is controversial. There were many people drawn to vue 2 and not all of them are able to find footing inside Vue 3, despite that being the main version pushed forward. With that being said Evan and the team at Vue have decided to include the options API & composition API as both valid approaches to writing vue applications, but what does that mean for the average user or the user at the end of the bell curve?"
- },
- {
- "id": "brian-gershon-november-2022",
- "speaker_id": "brian-gershon",
- "event_id": "november-2022",
- "title": "Tales of a Transition to Tailwind CSS",
- "abstract": "After some doubts, I gave Tailwind CSS a try and discovered a new love for styling my web applications. I'll discuss some reasons you'll like it, and also introduce component frameworks built on Tailwind."
- },
- {
- "id": "calvin-kipperman-january-2023",
- "speaker_id": "calvin-kipperman",
- "event_id": "january-2023",
- "title": "React Is Holding Me Hostage",
- "abstract": "A love & horror story - why React isn't a good model for building interactive applications and why I'm still a React developer."
- },
- {
- "id": "tim-obrien-january-2023",
- "speaker_id": "tim-obrien",
- "event_id": "january-2023",
- "title": "Were Your Skis Cut with Javascript?",
- "type": "lightning",
- "abstract": "Just a quick snapshot of how you can use React and Express and Javascript to manufacture skis. What are the pros and cons of using Javascript to generate CNC files and what's worked and not worked? Looking to show a quick demo and also looking for people interested in contributing."
- },
- {
- "id": "josh-scotland-january-2023",
- "speaker_id": "josh-scotland",
- "event_id": "january-2023",
- "title": "Supercharge Your Coding With ChatGPT",
- "type": "lightning",
- "abstract": "Learn how to use ChatGPT, a powerful AI programming tool, to enhance your coding skills and boost your productivity. In this demo, we'll explore the capabilities and limitations of ChatGPT to see firsthand how it might revolutionize your workflow."
- },
- {
- "id": "brian-tran-february-2023",
- "speaker_id": "brian-tran",
- "event_id": "february-2023",
- "title": "I want my data and I want it now!",
- "type": "lightning",
- "abstract": "A quick talk about stream processing using NodeJS and some other use cases including displaying the data in real-time over WebSockets and React."
- },
- {
- "id": "lupe-canaviri-maydana-februrary-2023",
- "speaker_id": "lupe-canaviri-maydana",
- "event_id": "february-2023",
- "title": "Playwright, everyone can write test",
- "type": "lightning",
- "abstract": "In 5 mins I will show you how to create your first end to end test with Playwright"
- },
- {
- "id": "jacob-ebey-february-2023",
- "speaker_id": "jacob-ebey",
- "event_id": "february-2023",
- "title": "\"Promises\" over the wire",
- "abstract": "Learn how Remix's `defer()` feature utilizes native language features, a single HTTP request and a few react tricks to deliver, what I believe will be, the next big trend in \"full stack frameworks\"."
- },
- {
- "id": "aaroh-mankad-march-2023",
- "speaker_id": "aaroh-mankad",
- "event_id": "march-2023",
- "title": "Tinkering with Framer Motion",
- "type": "lightning",
- "abstract": "I've been learning how to use Framer Motion recently, and wanted to walk through how it works, what you can use it for, and go through a couple of the examples from the documentation!"
- },
- {
- "id": "mason-lynass-march-2023",
- "speaker_id": "mason-lynass",
- "event_id": "march-2023",
- "title": "useSound - a creative, versatile tool to use audio & sound effects in React",
- "type": "lightning",
- "abstract": "useSound is a custom React hook developed by Josh Comeau, designed to creatively implement audio assets in React applications. I'll talk about installation and setup, differences between useSound and HTMLAudioElement, and quickly showcase a simple musical instrument built in React!"
- },
- {
- "id": "chris-griffing-march-2023",
- "speaker_id": "chris-griffing",
- "event_id": "march-2023",
- "title": "Porting Boring Avatars using Mitosis",
- "abstract": "boringavatars.com is an awesome avatars library, but it's only for React. People have ported it to other frameworks but they are one-offs. I decided to use Mitosis from builder.io to make \"one repo to rule them all\". In this talk, I will go over the process and some of the issues I encountered along the way."
- },
- {
- "id": "philip-swan-april-2023",
- "speaker_id": "philip-swan",
- "event_id": "april-2023",
- "title": "The Tethered Ring Space Infrastructure Interactive 3D Architecture Model in three.js",
- "abstract": "The Tethered Ring a game-changing architecture for making space safe, sustainable, and affordable for everyone - which clearly is not going to happen if we keep on using chemical rockets. An on-line interactive three.js-based JavaScript model captures the engineering, science, and economics behind the technology. Now anyone with internet access can review the code and contribute to the project. Come and be inspired!",
- "type": "regular"
- },
- {
- "id": "catherine-johnson-april-2023",
- "speaker_id": "catherine-johnson",
- "event_id": "april-2023",
- "title": "How to Build Accessible React Components",
- "abstract": "Hope you are strapped in for some accessibility! In this session we will breakdown how you can use built-in React tools and web accessibility guidelines to create flexible and accessible components for your website.",
- "type": "regular"
- },
- {
- "id": "michael-solati-april-2023",
- "speaker_id": "michael-solati",
- "event_id": "april-2023",
- "title": "0 to 100 with Lighthouse",
- "abstract": "There are a slew of tools to help developers improve their web applications: one of them is Google's Lighthouse. In this session you'll learn about what Lighthouse is, and see how you can use Lighthouse CI as part of your build and testing process.",
- "type": "lightning"
- },
- {
- "id": "spenser-solys-may-2023",
- "speaker_id": "spenser-solys",
- "event_id": "may-2023",
- "title": "Augmented Reality Business Card",
- "abstract": "I made a \"business card\" using AFrame + AR.js. Just want to show it off",
- "type": "lightning"
- },
- {
- "id": "tiger-oakes-may-2023",
- "speaker_id": "tiger-oakes",
- "event_id": "may-2023",
- "title": "Hacking an e-reader to show my tea menu",
- "abstract": "E-Readers are great and use so little power! I wanted to have a tea menu to show guests and for my own reference. I'll share how to generate an e-book with all your drinks in Deno, as well as rendering a custom cover page with SVG. Wow your friends by turning an old device into a smart home tea menu.",
- "type": "lightning"
- },
- {
- "id": "aiden-bai-june-2023",
- "speaker_id": "aiden-bai",
- "event_id": "june-2023",
- "title": "Virtual DOM: Back in Block",
- "abstract": "Is the Virtual DOM pure overhead? In this tech talk, Aiden Bai explores the performance implications of the Virtual DOM in frameworks like React and presents an alternative approach called the \"block virtual DOM.\" Aiden delves into the origins of the Virtual DOM, its purpose in addressing performance issues, and the process of diffing and reconciliation. The talk introduces the Block virtual DOM, which takes a different approach to diffing by using static analysis and dirty checking.",
- "topics": [
- "reactjs",
- "millionjs",
- "nextjs",
- "performance",
- "rendering",
- "javascript",
- "typescript"
- ],
- "type": "lightning"
- },
- {
- "id": "dm-liao-june-2023",
- "speaker_id": "dm-liao",
- "event_id": "june-2023",
- "title": "Using the web to make interactive fanfiction",
- "abstract": "Over the pandemic, people developed a wide variety of hobbies to fill the time, and for me, that was writing a ridiculous amount of fanfiction. One of the most ambitious pieces was an interactive, choice-based and animated story written using Ink and 'set' with Preact and InkJS, and this talk goes through some of the creative and technical decisions I made to get them to play nicely with each other.",
- "topics": [
- "preactjs",
- "inkjs",
- "immutable data",
- "text parsing",
- "interactive fiction"
- ],
- "type": "regular"
- },
- {
- "id": "cristina-rodriguez-june-2023",
- "speaker_id": "cristina-rodriguez",
- "event_id": "june-2023",
- "title": "Overcoming Blank Page Syndrome with your Template",
- "abstract": "How many times have you thought of a great idea for a project but were not quite sure how to start coding that project? Using templates can help beginning engineers overcome the overwhelm and fear that can come with starting projects. The goal of this talk is to share with everyone who is self-learning to code that templates and automation are their friends, especially for that tedious work of your initial setup. There is no need to start every project from scratch.",
- "topics": [
- "javascript",
- "templates",
- "projects",
- "front-end",
- "backend",
- "react",
- "vite"
- ],
- "type": "lightning"
- },
- {
- "id": "geoff-rich-july-2023",
- "speaker_id": "geoff-rich",
- "event_id": "july-2023",
- "title": "Web development, streamlined: an introduction to SvelteKit",
- "abstract": "SvelteKit is an exciting new web framework that recently launched version 1.0. It combines the well-loved Svelte JavaScript framework with everything you need to build a modern web app: routing, type-safe data loading, progressively-enhanced forms, a speedy Vite-powered dev experience, and more. In this talk, Iβll give a crash course on SvelteKit and how you can use it to build fast, resilient web apps of all shapes and sizes.",
- "topics": [
- "javascript",
- " svelte",
- " sveltekit "
- ],
- "type": "regular"
- },
- {
- "id": "chris-griffing-july-2023",
- "speaker_id": "chris-griffing",
- "event_id": "july-2023",
- "title": "Protecting the Environment (Variables) via AST",
- "abstract": "How can we use the AST to detect unused env vars. Geller is a simple tool to do that. Let's dig into how it works and how it crawls the JS/TS AST.",
- "topics": [
- "AST",
- " CI/CD",
- " Dev Tools"
- ],
- "type": "lightning"
- },
- {
- "id": "michael-fitzgerald-july-2023",
- "speaker_id": "michael-fitzgerald",
- "event_id": "july-2023",
- "title": "Promise Concurrency in JavaScript",
- "abstract": "Exploring ways to optimize resolving promises in JavasScript. A few slides with concepts and execution of code snippets with barebones performance measurement. Presenting to spark further discussion and generate feedback / input from others, not to lecture as the expert on the matter.",
- "topics": [
- "javascript",
- " promises",
- " performance",
- " concurrency"
- ],
- "type": "lightning"
- },
- {
- "id": "jan-miksovsky-september-2023",
- "speaker_id": "jan-miksovsky",
- "event_id": "september-2023",
- "title": "Graphs All the Way Down",
- "abstract": "Distinct APIs for objects, files, sites, and resources obscure the fact that they're all just graphs. I'll introduce a research project leveraging recent JS features and a new coding pattern for graphs to let you quickly transform graphs of anything into a site or other artifact in a natural, expressive way.",
- "topics": [
- "front-end development",
- "static site generators",
- "documentation pipelines"
- ],
- "type": "regular"
- },
- {
- "id": "peli-de-halleux-september-2023",
- "speaker_id": "peli-de-halleux",
- "event_id": "september-2023",
- "title": "DeviceScript - TypeScript for Tiny IoT Devices",
- "abstract": "Bring your TypeScript skillz to embedded (ESP32, RP2040) using DeviceScript.",
- "topics": [
- "embedded",
- "typescript"
- ],
- "type": "regular"
- },
- {
- "id": "ben-lower-september-2023",
- "speaker_id": "ben-lower",
- "event_id": "september-2023",
- "title": "Giving LLMs Tools and Ability to do Generative UI",
- "abstract": "I will show how to enable LLMs to take actions using tools defined using JSX components. Additionally, I will show how to give the LLM the ability to create UI dynamically at runtime to move beyond simple text-based chat experiences.",
- "topics": [
- "AI",
- "LLM",
- "JavaScript",
- "React",
- "JSX"
- ],
- "type": "lightning"
- },
- {
- "id": "caleb-diehl-october-2023",
- "speaker_id": "caleb-diehl",
- "event_id": "october-2023",
- "title": "Tracking Wildfires for The Associated Press with Maplibre and the ESRI API",
- "abstract": "Millions of readers rely on the Associated Press's wildfire tracker to get live information about large wildfires across the United States. I am a data visualization developer at the AP and will go over some design, architecture, and data viz decisions we made during a major UI overhaul (which goes live at the end of June). I'll discuss how attendees can build similar interactive maps in React / TypeScript using the same free, open-source mapping libraries and APIs. ",
- "topics": [
- "Data visualization",
- "interactive mapping"
- ],
- "type": "lightning"
- },
- {
- "id": "alan-gonzalez-october-2023",
- "speaker_id": "alan-gonzalez",
- "event_id": "october-2023",
- "title": "A new type of coding interviews",
- "abstract": "Technical interviews are either inapplicable leet-code questions or time-consuming take-home projects. This talk presents a new approach that brings the best of both worlds, allowing people to showcase applicable skills. Software Engineering interviews in the browser!",
- "topics": [
- "hiring",
- "coding interviews",
- "leet-code",
- "take-homes",
- "webassembly"
- ],
- "type": "lightning"
- },
- {
- "id": "mike-ryan-november-2023",
- "speaker_id": "mike-ryan",
- "event_id": "november-2023",
- "title": "The User Experience of Site Reliability",
- "abstract": "From CPU-pegged servers to misconfigured load balancers, outages in our systems all result in the same thing: degraded user experience. Let's explore strategies for measuring, managing, and improving reliability in our applications from the perspective of end users.",
- "topics": [
- "site reliability",
- "observability",
- "user experience"
- ],
- "type": "regular"
- },
- {
- "id": "doug-wade-november-2023",
- "speaker_id": "doug-wade",
- "event_id": "november-2023",
- "title": "Dual publishing JavaScript packages to deno and node",
- "abstract": "Learn how take a JavaScript module written for deno and build, test, type check, and publish it for both deno and nodejs using dnt, the Deno to Node Transform build tool",
- "topics": [
- "Deno",
- "nodejs",
- "npm",
- "package management"
- ],
- "type": "regular"
- },
- {
- "id": "fx-wood-february-2024",
- "speaker_id": "fx-wood",
- "event_id": "february-2024",
- "title": "Why You Should Use A DateTime Library (Maybe)",
- "abstract": "There are some interesting complexities when it comes to working with time. Also some fun stuff about timestamps.",
- "topics": [
- "luxon",
- "datetime",
- "DateTimeFormat"
- ],
- "type": "lightning"
- },
- {
- "id": "dm-liao-february-2024",
- "speaker_id": "dm-liao",
- "event_id": "february-2024",
- "title": "Using React with the Godot game engine",
- "abstract": "Godot is a game engine and editor that is written in C++ and allows you to script games using its custom GDScript language, or C#. So, naturally, I decided that I wanted to make game UI in Javascript using React, and it turns out that that's not that uncommon of an idea! And also not that terrible of an idea! This talk will go through some of the nitty gritty of getting C# and JS to talk to each other, and then creating a custom React renderer to render Godot UI elements.",
- "topics": [
- "React",
- "Godot",
- "game engine"
- ],
- "type": "regular"
- },
- {
- "id": "shruti-kapoor-march-2024",
- "speaker_id": "shruti-kapoor",
- "event_id": "march-2024",
- "title": "Building an AI Web App with LangChain and OpenAI",
- "abstract": "Learn how to build a web app with LangChain and OpenAI. Build the front-end, back-end and create an AI service that integrates with OpenAI to power a web app. ",
- "topics": [
- "front-end",
- "back-end",
- "AI "
- ],
- "type": "regular"
- },
- {
- "id": "j-chris-anderson-march-2024",
- "speaker_id": "j-chris-anderson",
- "event_id": "march-2024",
- "title": "Local-first database application starter kits",
- "abstract": "I'm building a browser-database based on what I learned from CouchDB. It empowers front-end devs to ship data-driven features without getting tied to a backend. Later you can connect almost any cloud for multi-user live collaboration.",
- "topics": [
- "database",
- "react",
- "sync",
- "crdt",
- "local-first"
- ],
- "type": "regular"
- },
- {
- "id": "eric-jensen-march-2024",
- "speaker_id": "eric-jensen",
- "event_id": "march-2024",
- "title": "Fireproof: A JavaScript database for web applications",
- "abstract": "Fireproof is a pure JS database with zero setup. Build without constraints, plug in and launch in one step. Come learn about the newest way to create web apps!",
- "topics": [
- "database",
- "sync",
- "local-first"
- ],
- "type": "regular"
- },
- {
- "id": "herrington-darkholme-april-2024",
- "speaker_id": "herrington-darkholme",
- "event_id": "april-2024",
- "title": "Benchmark Rusty Parsers: demystify native tooling performance in JavaScript",
- "abstract": "Native parsers used in JavaScript are not always faster due to extra work across languages. This talk will review several parsers written in JS/Rust and demonstrate the perf characteristics of native programs in NodeJS.\nAvoiding Rust overhead and using multi-core are crucial for performance.\n\nhttps://rs-perf-talk.vercel.app/",
- "topics": [
- "javascript",
- "napi",
- "rust"
- ],
- "type": "regular"
- },
- {
- "id": "john-pham-april-2024",
- "speaker_id": "john-pham",
- "event_id": "april-2024",
- "title": "Delightful Design",
- "abstract": "In this talk, we'll explore how to craft delightful experiences that enhance user engagement, focusing on performance, accessibility, and impressing the user, as seen in products like Vercel, Linear, and Raycast. You'll learn that anyone, regardless of design expertise, can achieve such delightful design.",
- "topics": [
- "design"
- ],
- "type": "regular"
- },
- {
- "id": "allan-deutsch-may-2024",
- "speaker_id": "allan-deutsch",
- "event_id": "may-2024",
- "title": "Adding nominal type safety to your TS code using branded types",
- "abstract": "Typescript and JavaScript are *structurally* typed - objects of the same shape are assignable. Nominal types can only be assigned to other objects of the same *named type*. Type branding is a technique that brings the safety of nominal typing to typescript.",
- "topics": [
- "Typescript",
- "type safety",
- "validation"
- ],
- "type": "lightning"
- },
- {
- "id": "sarah-guthals-phd-may-2024",
- "speaker_id": "sarah-guthals-phd",
- "event_id": "may-2024",
- "title": "Deconstructing Distributed Tracing",
- "abstract": "Distributed Tracing is a powerful and modern debugging technique that allows you to track the flow and timing of requests as they navigate through every part of your system. Learn the reason why Distributed Tracing exists and how to leverage it.",
- "topics": [
- "performance",
- "tracing",
- "monitoring"
- ],
- "type": "regular"
- },
- {
- "id": "josh-franklin-july-2024",
- "speaker_id": "josh-franklin",
- "event_id": "july-2024",
- "title": "\"Efficiently\" Sharing Data Through URLs",
- "abstract": "What is the most efficient way to store data in a URL? Let's deep dive into how URLs work and write some custom encoding algorithms to find out how much data can be stored in them. Knowing this may allow you to replace your database entirely for some use cases.",
- "topics": [
- "How URLs work",
- "encoding/decoding algorithms",
- "when to store data in URLs vs. a backend"
- ],
- "type": "regular"
- },
- {
- "id": "chris-griffing-july-2024",
- "speaker_id": "chris-griffing",
- "event_id": "july-2024",
- "title": "A Different Kind of Serverless: A History of WASM and case study for SQLite and Whisper.cpp running purely in the browser with no server-side logic",
- "abstract": "You can build applications with a local-first focus. We will cover what WASM is, how it came to be and some of its history, as well as a case study focused on an app I made for myself. In the case study, we dig into how you might use WASM to run Whisper.cpp for Speech-to-text and also use WASM to host a local SQLite database. The only reason we need a network connection at all for this application is to get the initial JS payload and to download the Whisper models from a remote store since they can get quite large.",
- "topics": [
- "WASM",
- "JS",
- "SQLite",
- "Speech-to-text"
- ],
- "type": "regular"
- },
- {
- "id": "ben-van-citters-august-2024",
- "speaker_id": "ben-van-citters",
- "event_id": "august-2024",
- "title": "Undo/Redo a postmortem",
- "abstract": "Some thoughts about distributed undo/redo architecture. I did it and you can too!",
- "topics": [
- "JavaScript",
- "undo",
- "patterns",
- "rest",
- "crud"
- ],
- "type": "regular"
- },
- {
- "id": "allan-deutsch-august-2024",
- "speaker_id": "allan-deutsch",
- "event_id": "august-2024",
- "title": "Classy coding: JavaScript OOP through the ages",
- "abstract": "Embark on a journey through the history of JavaScript to learn how a variety of OOP challenges were overcome in the past. We will conclude our journey with clean modern syntax and a deeper understanding of classes, objects, and the chain of changes that helped us get here.",
- "topics": [
- "javascript",
- "inheritance",
- "prototype chains",
- "JS runtime",
- "classes",
- "OOP",
- "object-oriented programming"
- ],
- "type": "lightning"
- },
- {
- "id": "nicholas-patti-september-2024",
- "speaker_id": "nicholas-patti",
- "event_id": "september-2024",
- "title": "Planning the ultimate Limp Bizkit experience with Playwright",
- "abstract": "How can my friend and I get on stage to perform guitar and bass with Limp Bizkit? The answer lies in the data, and Playwright can help us grab it!",
- "topics": [
- "Playwright",
- "NodeJS",
- "data scraping",
- "data analysis"
- ],
- "type": "lightning"
- },
- {
- "id": "stacy-davis1-october-2024",
- "speaker_id": "stacy-davis1",
- "event_id": "october-2024",
- "title": "Translation: Tokens of Clarity",
- "abstract": "How does someone even get started with translating? What are some of the details involved? Join me to learn more about translation and localization in a React app, and insights taken from experience leading a translation initiative.",
- "topics": [
- "Translation",
- "localization",
- "React"
- ],
- "type": "regular"
- },
- {
- "id": "erik-marks-october-2024",
- "speaker_id": "erik-marks",
- "event_id": "october-2024",
- "title": "How to use eval() in production (and remain gainfully employed)",
- "abstract": "If you know anything about eval(), it's probably that you should avoid using it at all costs. However, what if using eval() could make your JavaScript program more secure? This is what Hardened JavaScript does, and in this talk we will explore how.",
- "topics": [
- "JavaScript",
- "security",
- "extensibility",
- "plugins",
- "Hardened JavaScript",
- "Secure EcmaScript",
- "SES"
- ],
- "type": "lightning"
- },
- {
- "id": "cat-johnson-november-2024",
- "speaker_id": "cat-johnson",
- "event_id": "november-2024",
- "title": "Nested Interactive Elements: A Nightmare in Accessibility",
- "abstract": "There have been numerous remarkable new UX experiences developed over the years. However, few are aware of the challenges involved in building structures with nested interactive elements. We will explore some of these prevalent web UX patterns and delve into the hidden challenges they present. While we may be able to mitigate some of these issues, others serve as horror stories in accessibility.",
- "topics": [
- "Accessibility",
- "HTML",
- "CSS"
- ],
- "type": "lightning"
- },
- {
- "id": "stacy-davis1-november-2024",
- "speaker_id": "stacy-davis1",
- "event_id": "november-2024",
- "title": "Translation: Tokens of Clarity",
- "abstract": "How does someone even get started with translating? What are some of the details involved? Join me to learn more about translation and localization in a React app, and insights taken from experience leading a translation initiative.",
- "topics": [
- "Translation",
- "localization",
- "React"
- ],
- "type": "lightning"
- },
- {
- "id": "justin-castilla-january-2025",
- "speaker_id": "justin-castilla",
- "event_id": "january-2025",
- "title": "Observability now: Open Telemetry is here and we should know about it.",
- "abstract": "Observability provides a wrangling of logs, traces, and metrics from your application into an organized analytics engine. We need to understand the basics and opportunities it provides. With open source software, we can get it up and running in no time!",
- "topics": [
- "observability",
- "data collection",
- "analytics",
- "performance",
- "AI",
- "data storage",
- "machine learning"
- ],
- "type": "lightning"
- },
- {
- "id": "peli-de-halleux-january-2025",
- "speaker_id": "peli-de-halleux",
- "event_id": "january-2025",
- "title": "GenAIScript - Generative AI Scripting",
- "abstract": "GenAIScript is a JavaScript-ish environment with convenient tooling for file ingestion, prompt development and structured data extraction. We will be building LLM scripts of all sorts during the talk. https://microsoft.github.io/genaiscript ",
- "topics": [
- "genai",
- "llm"
- ],
- "type": "lightning"
- },
- {
- "id": "andrew-enyeart-january-2025",
- "speaker_id": "andrew-enyeart",
- "event_id": "january-2025",
- "title": "Hard Things & Magical Incantations of Overcoming",
- "abstract": "We all face hard things. Wielding simple, powerful phrasesβmagical incantations, if you willβcan help us take action in the face of discomfort and fear. Learn how to harness identity and habit to tackle your own unique challenges with courage.",
- "topics": [
- "job hunting",
- "courage",
- "mindset",
- "identity",
- "discomfort",
- "personal growth",
- "persistence"
- ],
- "type": "lightning"
- }
-]
\ No newline at end of file
+ {
+ "id": "amber-hoak-may-2022",
+ "speaker_id": "amber-hoak",
+ "event_id": "may-2022",
+ "title": "Stumbling through ML in JavaScript: the good, the bad, and the computationally intensive",
+ "abstract": "Does Tensorflow make you tense? Let's unpack how you can get started with ML in the browser, no math required. Learn how to build models on the browser, why we would use client-side ML, and what ML tooling (and lack of tooling) is in the js ecosystem."
+ },
+ {
+ "id": "jamund-ferguson-june-2022",
+ "speaker_id": "jamund-ferguson",
+ "event_id": "june-2022",
+ "title": "π§ Web Performance Horror Storiesπ§",
+ "abstract": "Experience the cringe inducing horror caused by excessively slow web performance. Be shocked to hear about megabytes of CSS going unused, endlessly duplicated polyfills, input fields freezing the main thread for 50ms per keystroke and assorted other terrors. Together we'll dissect these performance poltergeists and vanquish them from our apps forever."
+ },
+ {
+ "id": "rachel-lee-nabors-october-2022",
+ "speaker_id": "rachel-lee-nabors",
+ "event_id": "october-2022",
+ "title": "The Human API",
+ "abstract": "The only thing harder than writing scalable, usable code is teaching others how to wield it. As the sum of human knowledge continues to grow, so too do the challenges of teaching each other what we need to know not only to build toward the future but also to contribute to the tools of its construction. If only it were possible to transfer knowledge from one engineer directly into the minds of other engineers, like a human RAID! Rachel Lee Nabors loves building such mechanisms for knowledge transfer, from video guides to documentation to curricula. In this talk, they will discuss what doesn't work, what has worked on projects like React and React Native, and what can work for any other open source project today. Teaching is hard. But there are solid ways to approach knowledge transfer at scale."
+ },
+ {
+ "id": "matthew-bauer-november-2022",
+ "speaker_id": "matthew-bauer",
+ "event_id": "november-2022",
+ "title": "Two left feet and an earthquake in Vue: Composition API or Options API?",
+ "abstract": "Vue 3 is controversial. There were many people drawn to vue 2 and not all of them are able to find footing inside Vue 3, despite that being the main version pushed forward. With that being said Evan and the team at Vue have decided to include the options API & composition API as both valid approaches to writing vue applications, but what does that mean for the average user or the user at the end of the bell curve?"
+ },
+ {
+ "id": "brian-gershon-november-2022",
+ "speaker_id": "brian-gershon",
+ "event_id": "november-2022",
+ "title": "Tales of a Transition to Tailwind CSS",
+ "abstract": "After some doubts, I gave Tailwind CSS a try and discovered a new love for styling my web applications. I'll discuss some reasons you'll like it, and also introduce component frameworks built on Tailwind."
+ },
+ {
+ "id": "calvin-kipperman-january-2023",
+ "speaker_id": "calvin-kipperman",
+ "event_id": "january-2023",
+ "title": "React Is Holding Me Hostage",
+ "abstract": "A love & horror story - why React isn't a good model for building interactive applications and why I'm still a React developer."
+ },
+ {
+ "id": "tim-obrien-january-2023",
+ "speaker_id": "tim-obrien",
+ "event_id": "january-2023",
+ "title": "Were Your Skis Cut with Javascript?",
+ "type": "lightning",
+ "abstract": "Just a quick snapshot of how you can use React and Express and Javascript to manufacture skis. What are the pros and cons of using Javascript to generate CNC files and what's worked and not worked? Looking to show a quick demo and also looking for people interested in contributing."
+ },
+ {
+ "id": "josh-scotland-january-2023",
+ "speaker_id": "josh-scotland",
+ "event_id": "january-2023",
+ "title": "Supercharge Your Coding With ChatGPT",
+ "type": "lightning",
+ "abstract": "Learn how to use ChatGPT, a powerful AI programming tool, to enhance your coding skills and boost your productivity. In this demo, we'll explore the capabilities and limitations of ChatGPT to see firsthand how it might revolutionize your workflow."
+ },
+ {
+ "id": "brian-tran-february-2023",
+ "speaker_id": "brian-tran",
+ "event_id": "february-2023",
+ "title": "I want my data and I want it now!",
+ "type": "lightning",
+ "abstract": "A quick talk about stream processing using NodeJS and some other use cases including displaying the data in real-time over WebSockets and React."
+ },
+ {
+ "id": "lupe-canaviri-maydana-februrary-2023",
+ "speaker_id": "lupe-canaviri-maydana",
+ "event_id": "february-2023",
+ "title": "Playwright, everyone can write test",
+ "type": "lightning",
+ "abstract": "In 5 mins I will show you how to create your first end to end test with Playwright"
+ },
+ {
+ "id": "jacob-ebey-february-2023",
+ "speaker_id": "jacob-ebey",
+ "event_id": "february-2023",
+ "title": "\"Promises\" over the wire",
+ "abstract": "Learn how Remix's `defer()` feature utilizes native language features, a single HTTP request and a few react tricks to deliver, what I believe will be, the next big trend in \"full stack frameworks\"."
+ },
+ {
+ "id": "aaroh-mankad-march-2023",
+ "speaker_id": "aaroh-mankad",
+ "event_id": "march-2023",
+ "title": "Tinkering with Framer Motion",
+ "type": "lightning",
+ "abstract": "I've been learning how to use Framer Motion recently, and wanted to walk through how it works, what you can use it for, and go through a couple of the examples from the documentation!"
+ },
+ {
+ "id": "mason-lynass-march-2023",
+ "speaker_id": "mason-lynass",
+ "event_id": "march-2023",
+ "title": "useSound - a creative, versatile tool to use audio & sound effects in React",
+ "type": "lightning",
+ "abstract": "useSound is a custom React hook developed by Josh Comeau, designed to creatively implement audio assets in React applications. I'll talk about installation and setup, differences between useSound and HTMLAudioElement, and quickly showcase a simple musical instrument built in React!"
+ },
+ {
+ "id": "chris-griffing-march-2023",
+ "speaker_id": "chris-griffing",
+ "event_id": "march-2023",
+ "title": "Porting Boring Avatars using Mitosis",
+ "abstract": "boringavatars.com is an awesome avatars library, but it's only for React. People have ported it to other frameworks but they are one-offs. I decided to use Mitosis from builder.io to make \"one repo to rule them all\". In this talk, I will go over the process and some of the issues I encountered along the way."
+ },
+ {
+ "id": "philip-swan-april-2023",
+ "speaker_id": "philip-swan",
+ "event_id": "april-2023",
+ "title": "The Tethered Ring Space Infrastructure Interactive 3D Architecture Model in three.js",
+ "abstract": "The Tethered Ring a game-changing architecture for making space safe, sustainable, and affordable for everyone - which clearly is not going to happen if we keep on using chemical rockets. An on-line interactive three.js-based JavaScript model captures the engineering, science, and economics behind the technology. Now anyone with internet access can review the code and contribute to the project. Come and be inspired!",
+ "type": "regular"
+ },
+ {
+ "id": "catherine-johnson-april-2023",
+ "speaker_id": "catherine-johnson",
+ "event_id": "april-2023",
+ "title": "How to Build Accessible React Components",
+ "abstract": "Hope you are strapped in for some accessibility! In this session we will breakdown how you can use built-in React tools and web accessibility guidelines to create flexible and accessible components for your website.",
+ "type": "regular"
+ },
+ {
+ "id": "michael-solati-april-2023",
+ "speaker_id": "michael-solati",
+ "event_id": "april-2023",
+ "title": "0 to 100 with Lighthouse",
+ "abstract": "There are a slew of tools to help developers improve their web applications: one of them is Google's Lighthouse. In this session you'll learn about what Lighthouse is, and see how you can use Lighthouse CI as part of your build and testing process.",
+ "type": "lightning"
+ },
+ {
+ "id": "spenser-solys-may-2023",
+ "speaker_id": "spenser-solys",
+ "event_id": "may-2023",
+ "title": "Augmented Reality Business Card",
+ "abstract": "I made a \"business card\" using AFrame + AR.js. Just want to show it off",
+ "type": "lightning"
+ },
+ {
+ "id": "tiger-oakes-may-2023",
+ "speaker_id": "tiger-oakes",
+ "event_id": "may-2023",
+ "title": "Hacking an e-reader to show my tea menu",
+ "abstract": "E-Readers are great and use so little power! I wanted to have a tea menu to show guests and for my own reference. I'll share how to generate an e-book with all your drinks in Deno, as well as rendering a custom cover page with SVG. Wow your friends by turning an old device into a smart home tea menu.",
+ "type": "lightning"
+ },
+ {
+ "id": "aiden-bai-june-2023",
+ "speaker_id": "aiden-bai",
+ "event_id": "june-2023",
+ "title": "Virtual DOM: Back in Block",
+ "abstract": "Is the Virtual DOM pure overhead? In this tech talk, Aiden Bai explores the performance implications of the Virtual DOM in frameworks like React and presents an alternative approach called the \"block virtual DOM.\" Aiden delves into the origins of the Virtual DOM, its purpose in addressing performance issues, and the process of diffing and reconciliation. The talk introduces the Block virtual DOM, which takes a different approach to diffing by using static analysis and dirty checking.",
+ "topics": [
+ "reactjs",
+ "millionjs",
+ "nextjs",
+ "performance",
+ "rendering",
+ "javascript",
+ "typescript"
+ ],
+ "type": "lightning"
+ },
+ {
+ "id": "dm-liao-june-2023",
+ "speaker_id": "dm-liao",
+ "event_id": "june-2023",
+ "title": "Using the web to make interactive fanfiction",
+ "abstract": "Over the pandemic, people developed a wide variety of hobbies to fill the time, and for me, that was writing a ridiculous amount of fanfiction. One of the most ambitious pieces was an interactive, choice-based and animated story written using Ink and 'set' with Preact and InkJS, and this talk goes through some of the creative and technical decisions I made to get them to play nicely with each other.",
+ "topics": [
+ "preactjs",
+ "inkjs",
+ "immutable data",
+ "text parsing",
+ "interactive fiction"
+ ],
+ "type": "regular"
+ },
+ {
+ "id": "cristina-rodriguez-june-2023",
+ "speaker_id": "cristina-rodriguez",
+ "event_id": "june-2023",
+ "title": "Overcoming Blank Page Syndrome with your Template",
+ "abstract": "How many times have you thought of a great idea for a project but were not quite sure how to start coding that project? Using templates can help beginning engineers overcome the overwhelm and fear that can come with starting projects. The goal of this talk is to share with everyone who is self-learning to code that templates and automation are their friends, especially for that tedious work of your initial setup. There is no need to start every project from scratch.",
+ "topics": [
+ "javascript",
+ "templates",
+ "projects",
+ "front-end",
+ "backend",
+ "react",
+ "vite"
+ ],
+ "type": "lightning"
+ },
+ {
+ "id": "geoff-rich-july-2023",
+ "speaker_id": "geoff-rich",
+ "event_id": "july-2023",
+ "title": "Web development, streamlined: an introduction to SvelteKit",
+ "abstract": "SvelteKit is an exciting new web framework that recently launched version 1.0. It combines the well-loved Svelte JavaScript framework with everything you need to build a modern web app: routing, type-safe data loading, progressively-enhanced forms, a speedy Vite-powered dev experience, and more. In this talk, Iβll give a crash course on SvelteKit and how you can use it to build fast, resilient web apps of all shapes and sizes.",
+ "topics": ["javascript", " svelte", " sveltekit "],
+ "type": "regular"
+ },
+ {
+ "id": "chris-griffing-july-2023",
+ "speaker_id": "chris-griffing",
+ "event_id": "july-2023",
+ "title": "Protecting the Environment (Variables) via AST",
+ "abstract": "How can we use the AST to detect unused env vars. Geller is a simple tool to do that. Let's dig into how it works and how it crawls the JS/TS AST.",
+ "topics": ["AST", " CI/CD", " Dev Tools"],
+ "type": "lightning"
+ },
+ {
+ "id": "michael-fitzgerald-july-2023",
+ "speaker_id": "michael-fitzgerald",
+ "event_id": "july-2023",
+ "title": "Promise Concurrency in JavaScript",
+ "abstract": "Exploring ways to optimize resolving promises in JavasScript. A few slides with concepts and execution of code snippets with barebones performance measurement. Presenting to spark further discussion and generate feedback / input from others, not to lecture as the expert on the matter.",
+ "topics": ["javascript", " promises", " performance", " concurrency"],
+ "type": "lightning"
+ },
+ {
+ "id": "jan-miksovsky-september-2023",
+ "speaker_id": "jan-miksovsky",
+ "event_id": "september-2023",
+ "title": "Graphs All the Way Down",
+ "abstract": "Distinct APIs for objects, files, sites, and resources obscure the fact that they're all just graphs. I'll introduce a research project leveraging recent JS features and a new coding pattern for graphs to let you quickly transform graphs of anything into a site or other artifact in a natural, expressive way.",
+ "topics": [
+ "front-end development",
+ "static site generators",
+ "documentation pipelines"
+ ],
+ "type": "regular"
+ },
+ {
+ "id": "peli-de-halleux-september-2023",
+ "speaker_id": "peli-de-halleux",
+ "event_id": "september-2023",
+ "title": "DeviceScript - TypeScript for Tiny IoT Devices",
+ "abstract": "Bring your TypeScript skillz to embedded (ESP32, RP2040) using DeviceScript.",
+ "topics": ["embedded", "typescript"],
+ "type": "regular"
+ },
+ {
+ "id": "ben-lower-september-2023",
+ "speaker_id": "ben-lower",
+ "event_id": "september-2023",
+ "title": "Giving LLMs Tools and Ability to do Generative UI",
+ "abstract": "I will show how to enable LLMs to take actions using tools defined using JSX components. Additionally, I will show how to give the LLM the ability to create UI dynamically at runtime to move beyond simple text-based chat experiences.",
+ "topics": ["AI", "LLM", "JavaScript", "React", "JSX"],
+ "type": "lightning"
+ },
+ {
+ "id": "caleb-diehl-october-2023",
+ "speaker_id": "caleb-diehl",
+ "event_id": "october-2023",
+ "title": "Tracking Wildfires for The Associated Press with Maplibre and the ESRI API",
+ "abstract": "Millions of readers rely on the Associated Press's wildfire tracker to get live information about large wildfires across the United States. I am a data visualization developer at the AP and will go over some design, architecture, and data viz decisions we made during a major UI overhaul (which goes live at the end of June). I'll discuss how attendees can build similar interactive maps in React / TypeScript using the same free, open-source mapping libraries and APIs. ",
+ "topics": ["Data visualization", "interactive mapping"],
+ "type": "lightning"
+ },
+ {
+ "id": "alan-gonzalez-october-2023",
+ "speaker_id": "alan-gonzalez",
+ "event_id": "october-2023",
+ "title": "A new type of coding interviews",
+ "abstract": "Technical interviews are either inapplicable leet-code questions or time-consuming take-home projects. This talk presents a new approach that brings the best of both worlds, allowing people to showcase applicable skills. Software Engineering interviews in the browser!",
+ "topics": [
+ "hiring",
+ "coding interviews",
+ "leet-code",
+ "take-homes",
+ "webassembly"
+ ],
+ "type": "lightning"
+ },
+ {
+ "id": "mike-ryan-november-2023",
+ "speaker_id": "mike-ryan",
+ "event_id": "november-2023",
+ "title": "The User Experience of Site Reliability",
+ "abstract": "From CPU-pegged servers to misconfigured load balancers, outages in our systems all result in the same thing: degraded user experience. Let's explore strategies for measuring, managing, and improving reliability in our applications from the perspective of end users.",
+ "topics": ["site reliability", "observability", "user experience"],
+ "type": "regular"
+ },
+ {
+ "id": "doug-wade-november-2023",
+ "speaker_id": "doug-wade",
+ "event_id": "november-2023",
+ "title": "Dual publishing JavaScript packages to deno and node",
+ "abstract": "Learn how take a JavaScript module written for deno and build, test, type check, and publish it for both deno and nodejs using dnt, the Deno to Node Transform build tool",
+ "topics": ["Deno", "nodejs", "npm", "package management"],
+ "type": "regular"
+ },
+ {
+ "id": "fx-wood-february-2024",
+ "speaker_id": "fx-wood",
+ "event_id": "february-2024",
+ "title": "Why You Should Use A DateTime Library (Maybe)",
+ "abstract": "There are some interesting complexities when it comes to working with time. Also some fun stuff about timestamps.",
+ "topics": ["luxon", "datetime", "DateTimeFormat"],
+ "type": "lightning"
+ },
+ {
+ "id": "dm-liao-february-2024",
+ "speaker_id": "dm-liao",
+ "event_id": "february-2024",
+ "title": "Using React with the Godot game engine",
+ "abstract": "Godot is a game engine and editor that is written in C++ and allows you to script games using its custom GDScript language, or C#. So, naturally, I decided that I wanted to make game UI in Javascript using React, and it turns out that that's not that uncommon of an idea! And also not that terrible of an idea! This talk will go through some of the nitty gritty of getting C# and JS to talk to each other, and then creating a custom React renderer to render Godot UI elements.",
+ "topics": ["React", "Godot", "game engine"],
+ "type": "regular"
+ },
+ {
+ "id": "shruti-kapoor-march-2024",
+ "speaker_id": "shruti-kapoor",
+ "event_id": "march-2024",
+ "title": "Building an AI Web App with LangChain and OpenAI",
+ "abstract": "Learn how to build a web app with LangChain and OpenAI. Build the front-end, back-end and create an AI service that integrates with OpenAI to power a web app. ",
+ "topics": ["front-end", "back-end", "AI "],
+ "type": "regular"
+ },
+ {
+ "id": "j-chris-anderson-march-2024",
+ "speaker_id": "j-chris-anderson",
+ "event_id": "march-2024",
+ "title": "Local-first database application starter kits",
+ "abstract": "I'm building a browser-database based on what I learned from CouchDB. It empowers front-end devs to ship data-driven features without getting tied to a backend. Later you can connect almost any cloud for multi-user live collaboration.",
+ "topics": ["database", "react", "sync", "crdt", "local-first"],
+ "type": "regular"
+ },
+ {
+ "id": "eric-jensen-march-2024",
+ "speaker_id": "eric-jensen",
+ "event_id": "march-2024",
+ "title": "Fireproof: A JavaScript database for web applications",
+ "abstract": "Fireproof is a pure JS database with zero setup. Build without constraints, plug in and launch in one step. Come learn about the newest way to create web apps!",
+ "topics": ["database", "sync", "local-first"],
+ "type": "regular"
+ },
+ {
+ "id": "herrington-darkholme-april-2024",
+ "speaker_id": "herrington-darkholme",
+ "event_id": "april-2024",
+ "title": "Benchmark Rusty Parsers: demystify native tooling performance in JavaScript",
+ "abstract": "Native parsers used in JavaScript are not always faster due to extra work across languages. This talk will review several parsers written in JS/Rust and demonstrate the perf characteristics of native programs in NodeJS.\nAvoiding Rust overhead and using multi-core are crucial for performance.\n\nhttps://rs-perf-talk.vercel.app/",
+ "topics": ["javascript", "napi", "rust"],
+ "type": "regular"
+ },
+ {
+ "id": "john-pham-april-2024",
+ "speaker_id": "john-pham",
+ "event_id": "april-2024",
+ "title": "Delightful Design",
+ "abstract": "In this talk, we'll explore how to craft delightful experiences that enhance user engagement, focusing on performance, accessibility, and impressing the user, as seen in products like Vercel, Linear, and Raycast. You'll learn that anyone, regardless of design expertise, can achieve such delightful design.",
+ "topics": ["design"],
+ "type": "regular"
+ },
+ {
+ "id": "allan-deutsch-may-2024",
+ "speaker_id": "allan-deutsch",
+ "event_id": "may-2024",
+ "title": "Adding nominal type safety to your TS code using branded types",
+ "abstract": "Typescript and JavaScript are *structurally* typed - objects of the same shape are assignable. Nominal types can only be assigned to other objects of the same *named type*. Type branding is a technique that brings the safety of nominal typing to typescript.",
+ "topics": ["Typescript", "type safety", "validation"],
+ "type": "lightning"
+ },
+ {
+ "id": "sarah-guthals-phd-may-2024",
+ "speaker_id": "sarah-guthals-phd",
+ "event_id": "may-2024",
+ "title": "Deconstructing Distributed Tracing",
+ "abstract": "Distributed Tracing is a powerful and modern debugging technique that allows you to track the flow and timing of requests as they navigate through every part of your system. Learn the reason why Distributed Tracing exists and how to leverage it.",
+ "topics": ["performance", "tracing", "monitoring"],
+ "type": "regular"
+ },
+ {
+ "id": "josh-franklin-july-2024",
+ "speaker_id": "josh-franklin",
+ "event_id": "july-2024",
+ "title": "\"Efficiently\" Sharing Data Through URLs",
+ "abstract": "What is the most efficient way to store data in a URL? Let's deep dive into how URLs work and write some custom encoding algorithms to find out how much data can be stored in them. Knowing this may allow you to replace your database entirely for some use cases.",
+ "topics": [
+ "How URLs work",
+ "encoding/decoding algorithms",
+ "when to store data in URLs vs. a backend"
+ ],
+ "type": "regular"
+ },
+ {
+ "id": "chris-griffing-july-2024",
+ "speaker_id": "chris-griffing",
+ "event_id": "july-2024",
+ "title": "A Different Kind of Serverless: A History of WASM and case study for SQLite and Whisper.cpp running purely in the browser with no server-side logic",
+ "abstract": "You can build applications with a local-first focus. We will cover what WASM is, how it came to be and some of its history, as well as a case study focused on an app I made for myself. In the case study, we dig into how you might use WASM to run Whisper.cpp for Speech-to-text and also use WASM to host a local SQLite database. The only reason we need a network connection at all for this application is to get the initial JS payload and to download the Whisper models from a remote store since they can get quite large.",
+ "topics": ["WASM", "JS", "SQLite", "Speech-to-text"],
+ "type": "regular"
+ },
+ {
+ "id": "ben-van-citters-august-2024",
+ "speaker_id": "ben-van-citters",
+ "event_id": "august-2024",
+ "title": "Undo/Redo a postmortem",
+ "abstract": "Some thoughts about distributed undo/redo architecture. I did it and you can too!",
+ "topics": ["JavaScript", "undo", "patterns", "rest", "crud"],
+ "type": "regular"
+ },
+ {
+ "id": "allan-deutsch-august-2024",
+ "speaker_id": "allan-deutsch",
+ "event_id": "august-2024",
+ "title": "Classy coding: JavaScript OOP through the ages",
+ "abstract": "Embark on a journey through the history of JavaScript to learn how a variety of OOP challenges were overcome in the past. We will conclude our journey with clean modern syntax and a deeper understanding of classes, objects, and the chain of changes that helped us get here.",
+ "topics": [
+ "javascript",
+ "inheritance",
+ "prototype chains",
+ "JS runtime",
+ "classes",
+ "OOP",
+ "object-oriented programming"
+ ],
+ "type": "lightning"
+ },
+ {
+ "id": "nicholas-patti-september-2024",
+ "speaker_id": "nicholas-patti",
+ "event_id": "september-2024",
+ "title": "Planning the ultimate Limp Bizkit experience with Playwright",
+ "abstract": "How can my friend and I get on stage to perform guitar and bass with Limp Bizkit? The answer lies in the data, and Playwright can help us grab it!",
+ "topics": ["Playwright", "NodeJS", "data scraping", "data analysis"],
+ "type": "lightning"
+ },
+ {
+ "id": "stacy-davis1-october-2024",
+ "speaker_id": "stacy-davis1",
+ "event_id": "october-2024",
+ "title": "Translation: Tokens of Clarity",
+ "abstract": "How does someone even get started with translating? What are some of the details involved? Join me to learn more about translation and localization in a React app, and insights taken from experience leading a translation initiative.",
+ "topics": ["Translation", "localization", "React"],
+ "type": "regular"
+ },
+ {
+ "id": "erik-marks-october-2024",
+ "speaker_id": "erik-marks",
+ "event_id": "october-2024",
+ "title": "How to use eval() in production (and remain gainfully employed)",
+ "abstract": "If you know anything about eval(), it's probably that you should avoid using it at all costs. However, what if using eval() could make your JavaScript program more secure? This is what Hardened JavaScript does, and in this talk we will explore how.",
+ "topics": [
+ "JavaScript",
+ "security",
+ "extensibility",
+ "plugins",
+ "Hardened JavaScript",
+ "Secure EcmaScript",
+ "SES"
+ ],
+ "type": "lightning"
+ },
+ {
+ "id": "cat-johnson-november-2024",
+ "speaker_id": "cat-johnson",
+ "event_id": "november-2024",
+ "title": "Nested Interactive Elements: A Nightmare in Accessibility",
+ "abstract": "There have been numerous remarkable new UX experiences developed over the years. However, few are aware of the challenges involved in building structures with nested interactive elements. We will explore some of these prevalent web UX patterns and delve into the hidden challenges they present. While we may be able to mitigate some of these issues, others serve as horror stories in accessibility.",
+ "topics": ["Accessibility", "HTML", "CSS"],
+ "type": "lightning"
+ },
+ {
+ "id": "stacy-davis1-november-2024",
+ "speaker_id": "stacy-davis1",
+ "event_id": "november-2024",
+ "title": "Translation: Tokens of Clarity",
+ "abstract": "How does someone even get started with translating? What are some of the details involved? Join me to learn more about translation and localization in a React app, and insights taken from experience leading a translation initiative.",
+ "topics": ["Translation", "localization", "React"],
+ "type": "lightning"
+ }
+]
diff --git a/app/md/about-us.md b/app/md/about-us.md
index 7278552..01f8229 100644
--- a/app/md/about-us.md
+++ b/app/md/about-us.md
@@ -2,11 +2,12 @@
title: About Us
excerpt: SeattleJS is a safe and inclusive event for everyone
---
+

-SeattleJS is the largest JavaScript and web developer meetup in Seattle.
+SeattleJS is the largest JavaScript and web developer meetup in Seattle.
-We are an inclusive community and welcome everyone, including folks who are just getting started in tech.
+We are an inclusive community and welcome everyone, including folks who are just getting started in tech.
Our mission is to help folks:
@@ -18,4 +19,4 @@ Our mission is to help folks:
Advocate for the open web
-Our meetings are the 2nd Wednesday of each month, we hope you can join us!
\ No newline at end of file
+Our meetings are the 2nd Wednesday of each month, we hope you can join us!
diff --git a/app/md/code-of-conduct.md b/app/md/code-of-conduct.md
index 48f180d..7205343 100644
--- a/app/md/code-of-conduct.md
+++ b/app/md/code-of-conduct.md
@@ -2,6 +2,7 @@
title: Code of Conduct
excerpt: SeattleJS is a safe and inclusive event for everyone
---
+
SeattleJS is dedicated to providing a harassment-free experience for everyone, regardless of gender, gender identity, gender expression, sexual orientation, disability, physical appearance, body size, race, ability, ethnicity, socioeconomic status, or religion (or lack thereof). SeattleJS does not tolerate harassment of conference participants or staff at any time nor in any form.
Anyone violating these guidelines may be removed from the event and/or our platforms at our sole sole discretion.
@@ -10,7 +11,7 @@ Thank you for helping make SeattleJS a welcoming, friendly event for all.
## Expected Behavior
-All communication at our events (in-person or virtual) or on our platforms (Discord, Meetup, etc) should be appropriate for a professional audience including people of many different backgrounds. Please be kind to others.
+All communication at our events (in-person or virtual) or on our platforms (Discord, Meetup, etc) should be appropriate for a professional audience including people of many different backgrounds. Please be kind to others.
Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community.
@@ -35,7 +36,7 @@ If an attendee or other participant engages in unacceptable behavior, the commun
## If You Witness or Are Subject to Unacceptable Behavior
-If you are subject to or witness unacceptable behavior, or have any other concerns, please notify an Organizer immediately.
+If you are subject to or witness unacceptable behavior, or have any other concerns, please notify an Organizer immediately.
If you are experiencing a life threatening emergency, dial 9-1-1.
diff --git a/app/md/conf/promo-info.md b/app/md/conf/promo-info.md
index 256197e..9b1d0cf 100644
--- a/app/md/conf/promo-info.md
+++ b/app/md/conf/promo-info.md
@@ -1,6 +1,7 @@
---
title: Promo Information
---
+
## One Sentence
SeattleJS Conf is an amazing 1-day conference covering the latest in web development being hosted by AWS on August 8 in downtown Seattle.
@@ -16,10 +17,9 @@ SeattleJS Conf is a very special 1-day conference bringing together ~300 web dev
- Training Workshops on React & TypeScript
- Closing party & karaoke
-The talks, from industry exports, will cover the latest and greatest in web technologies, from CSS View Transitions to Deno 2.0 to AI.Β
-
-The event is being hosted by AWS and will feature a post-conference karaoke party at Optimism Brewing that you won't want to miss!Β
+The talks, from industry exports, will cover the latest and greatest in web technologies, from CSS View Transitions to Deno 2.0 to AI.
+The event is being hosted by AWS and will feature a post-conference karaoke party at Optimism Brewing that you won't want to miss!
## LogoΒ
@@ -28,6 +28,3 @@ https://seattlejs.com/_public/images/seattlejsconf-2023-logo-a274507057.png
## Website
https://seattlejs.com/conf
-
-
-
diff --git a/app/md/conf/speaker-info.md b/app/md/conf/speaker-info.md
index 8707442..c2ab2a7 100644
--- a/app/md/conf/speaker-info.md
+++ b/app/md/conf/speaker-info.md
@@ -1,6 +1,7 @@
---
title: Speaker Information
---
+
Here is a high-level To Do list for speakers at SeattleJS Conf:
diff --git a/app/md/organizer-notes/2023-01-04.md b/app/md/organizer-notes/2023-01-04.md
index 246809f..eb2f3ce 100644
--- a/app/md/organizer-notes/2023-01-04.md
+++ b/app/md/organizer-notes/2023-01-04.md
@@ -4,7 +4,7 @@
### Lu.ma page
-https://lu.ma/seattlejs
+https://lu.ma/seattlejs
We use this for ticketing and RSVPs. It's a good way to track who is coming to the event.
diff --git a/app/md/signup-next-steps.md b/app/md/signup-next-steps.md
index 328593f..11a5ea9 100644
--- a/app/md/signup-next-steps.md
+++ b/app/md/signup-next-steps.md
@@ -1,11 +1,11 @@
---
title: Please Confirm Your Email Address
---
-Thanks! We have sent an email to the address you provided.
+
+Thanks! We have sent an email to the address you provided.
- Please click the link in order to confirm that the email address belongs to you.
- You may need to check your "Promotions" or "Updates" folders.
- To ensure you recieve our emails, please filter emails from info@cascadiajs.com to your Primary inbox.
-
diff --git a/app/md/speak.md b/app/md/speak.md
index 1ed3616..d86cea6 100644
--- a/app/md/speak.md
+++ b/app/md/speak.md
@@ -2,11 +2,12 @@
title: Speak at SeattleJS
excerpt: SeattleJS is a safe and inclusive event for everyone building on the web
---
+

## Submitting an Idea
-Whether you are new to web development or a seasoned pro, you can give a talk at SeattleJS.
+Whether you are new to web development or a seasoned pro, you can give a talk at SeattleJS.
Don't let the name SeattleJS fool you - we welcome people and presentations regardless of which technologies you are excited about using to build on the web. Talks about technologies like the browser platform, CSS, NodeJS, and other languages and frameworks like Ruby on Rails, Django, and PHP are all welcome here!
@@ -16,9 +17,9 @@ SeattleJS is a friendly and welcoming group. If you've never presented before or
SeattleJS is equipped to support a few different presentation formats:
-- **Lightning talks** are *5-10 minute* sessions that briefly cover a topic. They tend to be more casual, and are a great fit for sharing something new you learned, and interesting problem you solved recently, and other topics that don't require a ton of depth.
-- **Tech sessions** are *20-30 minutes* and cover a technical topic in depth. Full sessions are usually accompanied by slides or demos. These should cover a topic in reasonable depth. These are also a great way to test out a topic before submitting or presenting at a conference.
-- **Workshops** are *interactive* sessions that are *45 minutes or longer*. In a workshop, the presenter guides the audience through one or more **hands-on coding activities** that they complete on their own computers.
+- **Lightning talks** are _5-10 minute_ sessions that briefly cover a topic. They tend to be more casual, and are a great fit for sharing something new you learned, and interesting problem you solved recently, and other topics that don't require a ton of depth.
+- **Tech sessions** are _20-30 minutes_ and cover a technical topic in depth. Full sessions are usually accompanied by slides or demos. These should cover a topic in reasonable depth. These are also a great way to test out a topic before submitting or presenting at a conference.
+- **Workshops** are _interactive_ sessions that are _45 minutes or longer_. In a workshop, the presenter guides the audience through one or more **hands-on coding activities** that they complete on their own computers.
If you've learned something new or have some hard-won wisdom to share, you'll find SeattleJS a warm and welcoming place to speak.
@@ -26,6 +27,6 @@ Fill-out this f
## Delivering Your Talk
-Please plan on being at the venue at 5PM - 30 minutes before doors open. We will use this time to do a tech check and make sure you are setup for a smooth presentation.
+Please plan on being at the venue at 5PM - 30 minutes before doors open. We will use this time to do a tech check and make sure you are setup for a smooth presentation.
-Please plan on plugging your laptop directly into an **HDMI port** and bring an adapter if your laptop doesn't have an HDMI port.
+Please plan on plugging your laptop directly into an **HDMI port** and bring an adapter if your laptop doesn't have an HDMI port.
diff --git a/app/md/volunteer-guide.md b/app/md/volunteer-guide.md
index ef92d34..eeb2dd7 100644
--- a/app/md/volunteer-guide.md
+++ b/app/md/volunteer-guide.md
@@ -2,6 +2,7 @@
title: Volunteer Guide
excerpt: The ins and outs of running SeattleJS
---
+
## Organzing a new (upcoming) meetup
- Log-in to Tito and click "Create a New Event"
@@ -10,4 +11,3 @@ excerpt: The ins and outs of running SeattleJS
- Update the event info in Settings / Basic
- Update Settings / Dates & Times
- Update Settings / Location (if necessary)
-
diff --git a/app/md/welcome.md b/app/md/welcome.md
index fa2be8b..1f96336 100644
--- a/app/md/welcome.md
+++ b/app/md/welcome.md
@@ -1,7 +1,8 @@
---
title: Welcome to SeattleJS!
---
-Your email address is confirmed, welcome to our community!
+
+Your email address is confirmed, welcome to our community!
Here are some other ways you can connect with us:
diff --git a/app/pages/index.html b/app/pages/index.html
index ab7fb54..b9d7326 100644
--- a/app/pages/index.html
+++ b/app/pages/index.html
@@ -1,23 +1,36 @@
-
-
-
Upcoming Meetups
-
-
-
Speaking at the SeattleJS Meetup
-
No matter who you are and what your experience level is, you can give a talk at SeattleJS. Please consider submitting an idea to our Call for Presenters!
-
Sponsors
-
-
If your company would like to sponsor an upcoming meetup, please email us at info@seattlejs.com.
-
Organizers
-
-
+
+
+
Upcoming Meetups
+
+
+
Speaking at the SeattleJS Meetup
+
+ No matter who you are and what your experience level is, you can give a
+ talk at SeattleJS. Please consider submitting an idea to our
+ Call for Presenters!
+
+
Sponsors
+
+
+ If your company would like to sponsor an upcoming meetup, please email us
+ at info@seattlejs.com.
+
+
+
Organizers
+
+
diff --git a/public/scripts/app.js b/public/scripts/app.js
index 04b31ee..03616c3 100644
--- a/public/scripts/app.js
+++ b/public/scripts/app.js
@@ -1,101 +1,114 @@
-(() => {
+;(() => {
// node_modules/@liveblocks/core/dist/index.mjs
- var PKG_NAME = "@liveblocks/core";
- var PKG_VERSION = "1.1.8";
- var PKG_FORMAT = "esm";
- var g = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {};
- var crossLinkedDocs = "https://liveblocks.io/docs/errors/cross-linked";
- var dupesDocs = "https://liveblocks.io/docs/errors/dupes";
- var SPACE = " ";
+ var PKG_NAME = '@liveblocks/core'
+ var PKG_VERSION = '1.1.8'
+ var PKG_FORMAT = 'esm'
+ var g =
+ typeof globalThis !== 'undefined'
+ ? globalThis
+ : typeof window !== 'undefined'
+ ? window
+ : typeof global !== 'undefined'
+ ? global
+ : {}
+ var crossLinkedDocs = 'https://liveblocks.io/docs/errors/cross-linked'
+ var dupesDocs = 'https://liveblocks.io/docs/errors/dupes'
+ var SPACE = ' '
function error(msg) {
if (false) {
- console.error(msg);
+ console.error(msg)
} else {
- throw new Error(msg);
+ throw new Error(msg)
}
}
function detectDupes(pkgName, pkgVersion, pkgFormat) {
- const pkgId = Symbol.for(pkgName);
- const pkgBuildInfo = pkgFormat ? `${pkgVersion || "dev"} (${pkgFormat})` : pkgVersion || "dev";
+ const pkgId = Symbol.for(pkgName)
+ const pkgBuildInfo = pkgFormat
+ ? `${pkgVersion || 'dev'} (${pkgFormat})`
+ : pkgVersion || 'dev'
if (!g[pkgId]) {
- g[pkgId] = pkgBuildInfo;
+ g[pkgId] = pkgBuildInfo
} else if (g[pkgId] === pkgBuildInfo) {
} else {
const msg = [
- `Multiple copies of Liveblocks are being loaded in your project. This will cause issues! See ${dupesDocs + SPACE}`,
- "",
- "Conflicts:",
+ `Multiple copies of Liveblocks are being loaded in your project. This will cause issues! See ${
+ dupesDocs + SPACE
+ }`,
+ '',
+ 'Conflicts:',
`- ${pkgName} ${g[pkgId]} (already loaded)`,
`- ${pkgName} ${pkgBuildInfo} (trying to load this now)`
- ].join("\n");
- error(msg);
+ ].join('\n')
+ error(msg)
}
if (pkgVersion && PKG_VERSION && pkgVersion !== PKG_VERSION) {
error(
[
- `Cross-linked versions of Liveblocks found, which will cause issues! See ${crossLinkedDocs + SPACE}`,
- "",
- "Conflicts:",
+ `Cross-linked versions of Liveblocks found, which will cause issues! See ${
+ crossLinkedDocs + SPACE
+ }`,
+ '',
+ 'Conflicts:',
`- ${PKG_NAME} is at ${PKG_VERSION}`,
`- ${pkgName} is at ${pkgVersion}`,
- "",
- "Always upgrade all Liveblocks packages to the same version number."
- ].join("\n")
- );
+ '',
+ 'Always upgrade all Liveblocks packages to the same version number.'
+ ].join('\n')
+ )
}
}
function makeEventSource() {
- const _onetimeObservers = /* @__PURE__ */ new Set();
- const _observers = /* @__PURE__ */ new Set();
- let _buffer = null;
+ const _onetimeObservers = /* @__PURE__ */ new Set()
+ const _observers = /* @__PURE__ */ new Set()
+ let _buffer = null
function pause() {
- _buffer = [];
+ _buffer = []
}
function unpause() {
if (_buffer === null) {
- return;
+ return
}
for (const event of _buffer) {
- notify(event);
+ notify(event)
}
- _buffer = null;
+ _buffer = null
}
function subscribe(callback) {
- _observers.add(callback);
- return () => _observers.delete(callback);
+ _observers.add(callback)
+ return () => _observers.delete(callback)
}
function subscribeOnce(callback) {
- _onetimeObservers.add(callback);
- return () => _onetimeObservers.delete(callback);
+ _onetimeObservers.add(callback)
+ return () => _onetimeObservers.delete(callback)
}
async function waitUntil(predicate) {
- let unsub;
- return new Promise((res) => {
- unsub = subscribe((event) => {
+ let unsub
+ return new Promise(res => {
+ unsub = subscribe(event => {
if (predicate === void 0 || predicate(event)) {
- res(event);
+ res(event)
}
- });
- }).finally(() => unsub?.());
+ })
+ }).finally(() => unsub?.())
}
function notifyOrBuffer(event) {
if (_buffer !== null) {
- _buffer.push(event);
+ _buffer.push(event)
} else {
- notify(event);
+ notify(event)
}
}
function notify(event) {
- _onetimeObservers.forEach((callback) => callback(event));
- _onetimeObservers.clear();
- _observers.forEach((callback) => callback(event));
+ _onetimeObservers.forEach(callback => callback(event))
+ _onetimeObservers.clear()
+ _observers.forEach(callback => callback(event))
}
function clear() {
- _onetimeObservers.clear();
- _observers.clear();
+ _onetimeObservers.clear()
+ _observers.clear()
}
function count() {
- return _onetimeObservers.size + _observers.size;
+ return _onetimeObservers.size + _observers.size
}
return {
// Private/internal control over event emission
@@ -113,73 +126,76 @@
subscribeOnce,
waitUntil
}
- };
+ }
}
- var _bridgeActive = false;
+ var _bridgeActive = false
function activateBridge(allowed) {
- _bridgeActive = allowed;
+ _bridgeActive = allowed
}
function sendToPanel(message, options) {
- if (typeof window === "undefined") {
- return;
+ if (typeof window === 'undefined') {
+ return
}
const fullMsg = {
...message,
- source: "liveblocks-devtools-client"
- };
+ source: 'liveblocks-devtools-client'
+ }
if (!(options?.force || _bridgeActive)) {
- return;
+ return
}
- window.postMessage(fullMsg, "*");
+ window.postMessage(fullMsg, '*')
}
- var eventSource = makeEventSource();
- if (typeof window !== "undefined") {
- window.addEventListener("message", (event) => {
- if (event.source === window && event.data?.source === "liveblocks-devtools-panel") {
- eventSource.notify(event.data);
+ var eventSource = makeEventSource()
+ if (typeof window !== 'undefined') {
+ window.addEventListener('message', event => {
+ if (
+ event.source === window &&
+ event.data?.source === 'liveblocks-devtools-panel'
+ ) {
+ eventSource.notify(event.data)
} else {
}
- });
+ })
}
- var onMessageFromPanel = eventSource.observable;
- var VERSION = PKG_VERSION || "dev";
- var _devtoolsSetupHasRun = false;
+ var onMessageFromPanel = eventSource.observable
+ var VERSION = PKG_VERSION || 'dev'
+ var _devtoolsSetupHasRun = false
function setupDevTools(getAllRooms) {
- if (typeof window === "undefined") {
- return;
+ if (typeof window === 'undefined') {
+ return
}
if (_devtoolsSetupHasRun) {
- return;
+ return
}
- _devtoolsSetupHasRun = true;
- onMessageFromPanel.subscribe((msg) => {
+ _devtoolsSetupHasRun = true
+ onMessageFromPanel.subscribe(msg => {
switch (msg.msg) {
- case "connect": {
- activateBridge(true);
+ case 'connect': {
+ activateBridge(true)
for (const roomId of getAllRooms()) {
sendToPanel({
- msg: "room::available",
+ msg: 'room::available',
roomId,
clientVersion: VERSION
- });
+ })
}
- break;
+ break
}
}
- });
- sendToPanel({ msg: "wake-up-devtools" }, { force: true });
+ })
+ sendToPanel({ msg: 'wake-up-devtools' }, { force: true })
}
- var unsubsByRoomId = /* @__PURE__ */ new Map();
+ var unsubsByRoomId = /* @__PURE__ */ new Map()
function stopSyncStream(roomId) {
- const unsubs = unsubsByRoomId.get(roomId) ?? [];
- unsubsByRoomId.delete(roomId);
+ const unsubs = unsubsByRoomId.get(roomId) ?? []
+ unsubsByRoomId.delete(roomId)
for (const unsub of unsubs) {
- unsub();
+ unsub()
}
}
function startSyncStream(room) {
- stopSyncStream(room.id);
- fullSync(room);
+ stopSyncStream(room.id)
+ fullSync(room)
unsubsByRoomId.set(room.id, [
// When the connection status changes
room.events.status.subscribe(() => partialSyncConnection(room)),
@@ -190,202 +206,205 @@
// Any time "me" or "others" updates, send the new values accordingly
room.events.self.subscribe(() => partialSyncMe(room)),
room.events.others.subscribe(() => partialSyncOthers(room))
- ]);
+ ])
}
function partialSyncConnection(room) {
sendToPanel({
- msg: "room::sync::partial",
+ msg: 'room::sync::partial',
roomId: room.id,
status: room.getStatus()
- });
+ })
}
function partialSyncStorage(room) {
- const root = room.getStorageSnapshot();
+ const root = room.getStorageSnapshot()
if (root) {
sendToPanel({
- msg: "room::sync::partial",
+ msg: 'room::sync::partial',
roomId: room.id,
- storage: root.toTreeNode("root").payload
- });
+ storage: root.toTreeNode('root').payload
+ })
}
}
function partialSyncMe(room) {
- const me = room.__internal.getSelf_forDevTools();
+ const me = room.__internal.getSelf_forDevTools()
if (me) {
sendToPanel({
- msg: "room::sync::partial",
+ msg: 'room::sync::partial',
roomId: room.id,
me
- });
+ })
}
}
function partialSyncOthers(room) {
- const others = room.__internal.getOthers_forDevTools();
+ const others = room.__internal.getOthers_forDevTools()
if (others) {
sendToPanel({
- msg: "room::sync::partial",
+ msg: 'room::sync::partial',
roomId: room.id,
others
- });
+ })
}
}
function fullSync(room) {
- const root = room.getStorageSnapshot();
- const me = room.__internal.getSelf_forDevTools();
- const others = room.__internal.getOthers_forDevTools();
+ const root = room.getStorageSnapshot()
+ const me = room.__internal.getSelf_forDevTools()
+ const others = room.__internal.getOthers_forDevTools()
sendToPanel({
- msg: "room::sync::full",
+ msg: 'room::sync::full',
roomId: room.id,
status: room.getStatus(),
- storage: root?.toTreeNode("root").payload ?? null,
+ storage: root?.toTreeNode('root').payload ?? null,
me,
others
- });
+ })
}
- var roomChannelListeners = /* @__PURE__ */ new Map();
+ var roomChannelListeners = /* @__PURE__ */ new Map()
function stopRoomChannelListener(roomId) {
- const listener = roomChannelListeners.get(roomId);
- roomChannelListeners.delete(roomId);
+ const listener = roomChannelListeners.get(roomId)
+ roomChannelListeners.delete(roomId)
if (listener) {
- listener();
+ listener()
}
}
function linkDevTools(roomId, room) {
- if (typeof window === "undefined") {
- return;
+ if (typeof window === 'undefined') {
+ return
}
- sendToPanel({ msg: "room::available", roomId, clientVersion: VERSION });
- stopRoomChannelListener(roomId);
+ sendToPanel({ msg: 'room::available', roomId, clientVersion: VERSION })
+ stopRoomChannelListener(roomId)
roomChannelListeners.set(
roomId,
// Returns the unsubscribe callback, that we store in the
// roomChannelListeners registry
- onMessageFromPanel.subscribe((msg) => {
+ onMessageFromPanel.subscribe(msg => {
switch (msg.msg) {
- case "room::subscribe": {
+ case 'room::subscribe': {
if (msg.roomId === roomId) {
- startSyncStream(room);
+ startSyncStream(room)
}
- break;
+ break
}
- case "room::unsubscribe": {
+ case 'room::unsubscribe': {
if (msg.roomId === roomId) {
- stopSyncStream(roomId);
+ stopSyncStream(roomId)
}
- break;
+ break
}
}
})
- );
+ )
}
function unlinkDevTools(roomId) {
- if (typeof window === "undefined") {
- return;
+ if (typeof window === 'undefined') {
+ return
}
- stopSyncStream(roomId);
- stopRoomChannelListener(roomId);
+ stopSyncStream(roomId)
+ stopRoomChannelListener(roomId)
sendToPanel({
- msg: "room::unavailable",
+ msg: 'room::unavailable',
roomId
- });
+ })
}
- var badge = "background:#0e0d12;border-radius:9999px;color:#fff;padding:3px 7px;font-family:sans-serif;font-weight:600;";
- var bold = "font-weight:600";
+ var badge =
+ 'background:#0e0d12;border-radius:9999px;color:#fff;padding:3px 7px;font-family:sans-serif;font-weight:600;'
+ var bold = 'font-weight:600'
function wrap(method) {
- return typeof window === "undefined" || false ? console[method] : (
- /* istanbul ignore next */
- (message, ...args) => console[method]("%cLiveblocks", badge, message, ...args)
- );
+ return typeof window === 'undefined' || false
+ ? console[method]
+ : /* istanbul ignore next */
+ (message, ...args) =>
+ console[method]('%cLiveblocks', badge, message, ...args)
}
- var warn = wrap("warn");
- var error2 = wrap("error");
+ var warn = wrap('warn')
+ var error2 = wrap('error')
function wrapWithTitle(method) {
- return typeof window === "undefined" || false ? console[method] : (
- /* istanbul ignore next */
- (title, message, ...args) => console[method](
- `%cLiveblocks%c ${title}`,
- badge,
- bold,
- message,
- ...args
- )
- );
+ return typeof window === 'undefined' || false
+ ? console[method]
+ : /* istanbul ignore next */
+ (title, message, ...args) =>
+ console[method](
+ `%cLiveblocks%c ${title}`,
+ badge,
+ bold,
+ message,
+ ...args
+ )
}
- var warnWithTitle = wrapWithTitle("warn");
- var errorWithTitle = wrapWithTitle("error");
- var _emittedDeprecationWarnings = /* @__PURE__ */ new Set();
+ var warnWithTitle = wrapWithTitle('warn')
+ var errorWithTitle = wrapWithTitle('error')
+ var _emittedDeprecationWarnings = /* @__PURE__ */ new Set()
function deprecate(message, key = message) {
if (true) {
if (!_emittedDeprecationWarnings.has(key)) {
- _emittedDeprecationWarnings.add(key);
- errorWithTitle("Deprecation warning", message);
+ _emittedDeprecationWarnings.add(key)
+ errorWithTitle('Deprecation warning', message)
}
}
}
function deprecateIf(condition, message, key = message) {
if (true) {
if (condition) {
- deprecate(message, key);
+ deprecate(message, key)
}
}
}
function assertNever(_value, errmsg) {
- throw new Error(errmsg);
+ throw new Error(errmsg)
}
function assert(condition, errmsg) {
if (true) {
if (!condition) {
- const err = new Error(errmsg);
- err.name = "Assertion failure";
- throw err;
+ const err = new Error(errmsg)
+ err.name = 'Assertion failure'
+ throw err
}
}
}
- function nn(value, errmsg = "Expected value to be non-nullable") {
- assert(value !== null && value !== void 0, errmsg);
- return value;
+ function nn(value, errmsg = 'Expected value to be non-nullable') {
+ assert(value !== null && value !== void 0, errmsg)
+ return value
}
function distance(state1, state2) {
if (state1 === state2) {
- return [0, 0];
+ return [0, 0]
}
- const chunks1 = state1.split(".");
- const chunks2 = state2.split(".");
- const minLen = Math.min(chunks1.length, chunks2.length);
- let shared = 0;
+ const chunks1 = state1.split('.')
+ const chunks2 = state2.split('.')
+ const minLen = Math.min(chunks1.length, chunks2.length)
+ let shared = 0
for (; shared < minLen; shared++) {
if (chunks1[shared] !== chunks2[shared]) {
- break;
+ break
}
}
- const up = chunks1.length - shared;
- const down = chunks2.length - shared;
- return [up, down];
+ const up = chunks1.length - shared
+ const down = chunks2.length - shared
+ return [up, down]
}
function patterns(targetState, levels) {
- const parts = targetState.split(".");
+ const parts = targetState.split('.')
if (levels < 1 || levels > parts.length + 1) {
- throw new Error("Invalid number of levels");
+ throw new Error('Invalid number of levels')
}
- const result = [];
+ const result = []
if (levels > parts.length) {
- result.push("*");
+ result.push('*')
}
for (let i = parts.length - levels + 1; i < parts.length; i++) {
- const slice = parts.slice(0, i);
+ const slice = parts.slice(0, i)
if (slice.length > 0) {
- result.push(slice.join(".") + ".*");
+ result.push(slice.join('.') + '.*')
}
}
- result.push(targetState);
- return result;
+ result.push(targetState)
+ return result
}
var SafeContext = class {
constructor(initialContext) {
- this.curr = initialContext;
+ this.curr = initialContext
}
get current() {
- return this.curr;
+ return this.curr
}
/**
* Call a callback function that allows patching of the context, by
@@ -393,64 +412,64 @@
* of this window.
*/
allowPatching(callback) {
- const self = this;
- let allowed = true;
+ const self = this
+ let allowed = true
const patchableContext = {
...this.curr,
patch(patch) {
if (allowed) {
- self.curr = Object.assign({}, self.curr, patch);
+ self.curr = Object.assign({}, self.curr, patch)
for (const pair of Object.entries(patch)) {
- const [key, value] = pair;
- if (key !== "patch") {
- this[key] = value;
+ const [key, value] = pair
+ if (key !== 'patch') {
+ this[key] = value
}
}
} else {
- throw new Error("Can no longer patch stale context");
+ throw new Error('Can no longer patch stale context')
}
}
- };
- callback(patchableContext);
- allowed = false;
- return;
+ }
+ callback(patchableContext)
+ allowed = false
+ return
}
- };
- var nextId = 1;
+ }
+ var nextId = 1
var FSM = class {
/**
* Returns the initial state, which is defined by the first call made to
* .addState().
*/
get initialState() {
- const result = this.states.values()[Symbol.iterator]().next();
+ const result = this.states.values()[Symbol.iterator]().next()
if (result.done) {
- throw new Error("No states defined yet");
+ throw new Error('No states defined yet')
} else {
- return result.value;
+ return result.value
}
}
get currentState() {
if (this.currentStateOrNull === null) {
if (this.runningState === 0) {
- throw new Error("Not started yet");
+ throw new Error('Not started yet')
} else {
- throw new Error("Already stopped");
+ throw new Error('Already stopped')
}
}
- return this.currentStateOrNull;
+ return this.currentStateOrNull
}
/**
* Starts the machine by entering the initial state.
*/
start() {
if (this.runningState !== 0) {
- throw new Error("State machine has already started");
+ throw new Error('State machine has already started')
}
- this.runningState = 1;
- this.currentStateOrNull = this.initialState;
- this.enter(null);
- return this;
+ this.runningState = 1
+ this.currentStateOrNull = this.initialState
+ this.enter(null)
+ return this
}
/**
* Stops the state machine. Stopping the state machine will call exit
@@ -458,109 +477,109 @@
*/
stop() {
if (this.runningState !== 1) {
- throw new Error("Cannot stop a state machine that hasn't started yet");
+ throw new Error("Cannot stop a state machine that hasn't started yet")
}
- this.exit(null);
- this.runningState = 2;
- this.currentStateOrNull = null;
+ this.exit(null)
+ this.runningState = 2
+ this.currentStateOrNull = null
}
constructor(initialContext) {
- this.id = nextId++;
- this.runningState = 0;
- this.currentStateOrNull = null;
- this.states = /* @__PURE__ */ new Set();
- this.enterFns = /* @__PURE__ */ new Map();
- this.cleanupStack = [];
- this.knownEventTypes = /* @__PURE__ */ new Set();
- this.allowedTransitions = /* @__PURE__ */ new Map();
- this.currentContext = new SafeContext(initialContext);
+ this.id = nextId++
+ this.runningState = 0
+ this.currentStateOrNull = null
+ this.states = /* @__PURE__ */ new Set()
+ this.enterFns = /* @__PURE__ */ new Map()
+ this.cleanupStack = []
+ this.knownEventTypes = /* @__PURE__ */ new Set()
+ this.allowedTransitions = /* @__PURE__ */ new Map()
+ this.currentContext = new SafeContext(initialContext)
this.eventHub = {
didReceiveEvent: makeEventSource(),
willTransition: makeEventSource(),
didIgnoreEvent: makeEventSource(),
willExitState: makeEventSource(),
didEnterState: makeEventSource()
- };
+ }
this.events = {
didReceiveEvent: this.eventHub.didReceiveEvent.observable,
willTransition: this.eventHub.willTransition.observable,
didIgnoreEvent: this.eventHub.didIgnoreEvent.observable,
willExitState: this.eventHub.willExitState.observable,
didEnterState: this.eventHub.didEnterState.observable
- };
+ }
}
get context() {
- return this.currentContext.current;
+ return this.currentContext.current
}
/**
* Define an explicit finite state in the state machine.
*/
addState(state) {
if (this.runningState !== 0) {
- throw new Error("Already started");
+ throw new Error('Already started')
}
- this.states.add(state);
- return this;
+ this.states.add(state)
+ return this
}
onEnter(nameOrPattern, enterFn) {
if (this.runningState !== 0) {
- throw new Error("Already started");
+ throw new Error('Already started')
} else if (this.enterFns.has(nameOrPattern)) {
throw new Error(
// TODO We _currently_ don't support multiple .onEnters() for the same
// state, but this is not a fundamental limitation. Just not
// implemented yet. If we wanted to, we could make this an array.
`enter/exit function for ${nameOrPattern} already exists`
- );
+ )
}
- this.enterFns.set(nameOrPattern, enterFn);
- return this;
+ this.enterFns.set(nameOrPattern, enterFn)
+ return this
}
onEnterAsync(nameOrPattern, promiseFn, onOK, onError) {
return this.onEnter(nameOrPattern, () => {
- let cancelled = false;
+ let cancelled = false
void promiseFn(this.currentContext.current).then(
// On OK
- (data) => {
+ data => {
if (!cancelled) {
- this.transition({ type: "ASYNC_OK", data }, onOK);
+ this.transition({ type: 'ASYNC_OK', data }, onOK)
}
},
// On Error
- (reason) => {
+ reason => {
if (!cancelled) {
- this.transition({ type: "ASYNC_ERROR", reason }, onError);
+ this.transition({ type: 'ASYNC_ERROR', reason }, onError)
}
}
- );
+ )
return () => {
- cancelled = true;
- };
- });
+ cancelled = true
+ }
+ })
}
getStatesMatching(nameOrPattern) {
- const matches = [];
- if (nameOrPattern === "*") {
+ const matches = []
+ if (nameOrPattern === '*') {
for (const state of this.states) {
- matches.push(state);
+ matches.push(state)
}
- } else if (nameOrPattern.endsWith(".*")) {
- const prefix = nameOrPattern.slice(0, -1);
+ } else if (nameOrPattern.endsWith('.*')) {
+ const prefix = nameOrPattern.slice(0, -1)
for (const state of this.states) {
if (state.startsWith(prefix)) {
- matches.push(state);
+ matches.push(state)
}
}
} else {
- const name = nameOrPattern;
+ const name = nameOrPattern
if (this.states.has(name)) {
- matches.push(name);
+ matches.push(name)
}
}
if (matches.length === 0) {
- throw new Error(`No states match ${JSON.stringify(nameOrPattern)}`);
+ throw new Error(`No states match ${JSON.stringify(nameOrPattern)}`)
}
- return matches;
+ return matches
}
/**
* Define all allowed outgoing transitions for a state.
@@ -576,29 +595,30 @@
*/
addTransitions(nameOrPattern, mapping) {
if (this.runningState !== 0) {
- throw new Error("Already started");
+ throw new Error('Already started')
}
for (const srcState of this.getStatesMatching(nameOrPattern)) {
- let map = this.allowedTransitions.get(srcState);
+ let map = this.allowedTransitions.get(srcState)
if (map === void 0) {
- map = /* @__PURE__ */ new Map();
- this.allowedTransitions.set(srcState, map);
+ map = /* @__PURE__ */ new Map()
+ this.allowedTransitions.set(srcState, map)
}
for (const [type, target_] of Object.entries(mapping)) {
if (map.has(type)) {
throw new Error(
`Trying to set transition "${type}" on "${srcState}" (via "${nameOrPattern}"), but a transition already exists there.`
- );
+ )
}
- const target = target_;
- this.knownEventTypes.add(type);
+ const target = target_
+ this.knownEventTypes.add(type)
if (target !== void 0) {
- const targetFn = typeof target === "function" ? target : () => target;
- map.set(type, targetFn);
+ const targetFn =
+ typeof target === 'function' ? target : () => target
+ map.set(type, targetFn)
}
}
}
- return this;
+ return this
}
/**
* Like `.addTransition()`, but takes an (anonymous) transition whenever the
@@ -612,17 +632,20 @@
*/
addTimedTransition(stateOrPattern, after2, target) {
return this.onEnter(stateOrPattern, () => {
- const ms = typeof after2 === "function" ? after2(this.currentContext.current) : after2;
+ const ms =
+ typeof after2 === 'function'
+ ? after2(this.currentContext.current)
+ : after2
const timeoutID = setTimeout(() => {
- this.transition({ type: "TIMER" }, target);
- }, ms);
+ this.transition({ type: 'TIMER' }, target)
+ }, ms)
return () => {
- clearTimeout(timeoutID);
- };
- });
+ clearTimeout(timeoutID)
+ }
+ })
}
getTargetFn(eventName) {
- return this.allowedTransitions.get(this.currentState)?.get(eventName);
+ return this.allowedTransitions.get(this.currentState)?.get(eventName)
}
/**
* Exits the current state, and executes any necessary cleanup functions.
@@ -635,13 +658,13 @@
* If `null`, it will exit all levels.
*/
exit(levels) {
- this.eventHub.willExitState.notify(this.currentState);
- this.currentContext.allowPatching((patchableContext) => {
- levels = levels ?? this.cleanupStack.length;
+ this.eventHub.willExitState.notify(this.currentState)
+ this.currentContext.allowPatching(patchableContext => {
+ levels = levels ?? this.cleanupStack.length
for (let i = 0; i < levels; i++) {
- this.cleanupStack.pop()?.(patchableContext);
+ this.cleanupStack.pop()?.(patchableContext)
}
- });
+ })
}
/**
* Enters the current state, and executes any necessary onEnter handlers.
@@ -650,20 +673,20 @@
enter(levels) {
const enterPatterns = patterns(
this.currentState,
- levels ?? this.currentState.split(".").length + 1
- );
- this.currentContext.allowPatching((patchableContext) => {
+ levels ?? this.currentState.split('.').length + 1
+ )
+ this.currentContext.allowPatching(patchableContext => {
for (const pattern of enterPatterns) {
- const enterFn = this.enterFns.get(pattern);
- const cleanupFn = enterFn?.(patchableContext);
- if (typeof cleanupFn === "function") {
- this.cleanupStack.push(cleanupFn);
+ const enterFn = this.enterFns.get(pattern)
+ const cleanupFn = enterFn?.(patchableContext)
+ if (typeof cleanupFn === 'function') {
+ this.cleanupStack.push(cleanupFn)
} else {
- this.cleanupStack.push(null);
+ this.cleanupStack.push(null)
}
}
- });
- this.eventHub.didEnterState.notify(this.currentState);
+ })
+ this.eventHub.didEnterState.notify(this.currentState)
}
/**
* Sends an event to the machine, which may cause an internal state
@@ -671,566 +694,608 @@
*/
send(event) {
if (!this.knownEventTypes.has(event.type)) {
- throw new Error(`Invalid event ${JSON.stringify(event.type)}`);
+ throw new Error(`Invalid event ${JSON.stringify(event.type)}`)
}
if (this.runningState === 2) {
- return;
+ return
}
- const targetFn = this.getTargetFn(event.type);
+ const targetFn = this.getTargetFn(event.type)
if (targetFn !== void 0) {
- return this.transition(event, targetFn);
+ return this.transition(event, targetFn)
} else {
- this.eventHub.didIgnoreEvent.notify(event);
+ this.eventHub.didIgnoreEvent.notify(event)
}
}
transition(event, target) {
- this.eventHub.didReceiveEvent.notify(event);
- const oldState = this.currentState;
- const targetFn = typeof target === "function" ? target : () => target;
- const nextTarget = targetFn(event, this.currentContext.current);
- let nextState;
- let effects = void 0;
+ this.eventHub.didReceiveEvent.notify(event)
+ const oldState = this.currentState
+ const targetFn = typeof target === 'function' ? target : () => target
+ const nextTarget = targetFn(event, this.currentContext.current)
+ let nextState
+ let effects = void 0
if (nextTarget === null) {
- this.eventHub.didIgnoreEvent.notify(event);
- return;
+ this.eventHub.didIgnoreEvent.notify(event)
+ return
}
- if (typeof nextTarget === "string") {
- nextState = nextTarget;
+ if (typeof nextTarget === 'string') {
+ nextState = nextTarget
} else {
- nextState = nextTarget.target;
- effects = Array.isArray(nextTarget.effect) ? nextTarget.effect : [nextTarget.effect];
+ nextState = nextTarget.target
+ effects = Array.isArray(nextTarget.effect)
+ ? nextTarget.effect
+ : [nextTarget.effect]
}
if (!this.states.has(nextState)) {
- throw new Error(`Invalid next state name: ${JSON.stringify(nextState)}`);
+ throw new Error(`Invalid next state name: ${JSON.stringify(nextState)}`)
}
- this.eventHub.willTransition.notify({ from: oldState, to: nextState });
- const [up, down] = distance(this.currentState, nextState);
+ this.eventHub.willTransition.notify({ from: oldState, to: nextState })
+ const [up, down] = distance(this.currentState, nextState)
if (up > 0) {
- this.exit(up);
+ this.exit(up)
}
- this.currentStateOrNull = nextState;
+ this.currentStateOrNull = nextState
if (effects !== void 0) {
- const effectsToRun = effects;
- this.currentContext.allowPatching((patchableContext) => {
+ const effectsToRun = effects
+ this.currentContext.allowPatching(patchableContext => {
for (const effect of effectsToRun) {
- if (typeof effect === "function") {
- effect(patchableContext, event);
+ if (typeof effect === 'function') {
+ effect(patchableContext, event)
} else {
- patchableContext.patch(effect);
+ patchableContext.patch(effect)
}
}
- });
+ })
}
if (down > 0) {
- this.enter(down);
+ this.enter(down)
}
}
- };
+ }
function isPlainObject(blob) {
- return blob !== null && typeof blob === "object" && Object.prototype.toString.call(blob) === "[object Object]";
+ return (
+ blob !== null &&
+ typeof blob === 'object' &&
+ Object.prototype.toString.call(blob) === '[object Object]'
+ )
}
function entries(obj) {
- return Object.entries(obj);
+ return Object.entries(obj)
}
function tryParseJson(rawMessage) {
try {
- return JSON.parse(rawMessage);
+ return JSON.parse(rawMessage)
} catch (e) {
- return void 0;
+ return void 0
}
}
function deepClone(items) {
- return JSON.parse(JSON.stringify(items));
+ return JSON.parse(JSON.stringify(items))
}
function b64decode(b64value) {
try {
- const formattedValue = b64value.replace(/-/g, "+").replace(/_/g, "/");
+ const formattedValue = b64value.replace(/-/g, '+').replace(/_/g, '/')
const decodedValue = decodeURIComponent(
- atob(formattedValue).split("").map(function(c) {
- return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
- }).join("")
- );
- return decodedValue;
+ atob(formattedValue)
+ .split('')
+ .map(function (c) {
+ return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
+ })
+ .join('')
+ )
+ return decodedValue
} catch (err) {
- return atob(b64value);
+ return atob(b64value)
}
}
function compact(items) {
- return items.filter(
- (item) => item !== null && item !== void 0
- );
+ return items.filter(item => item !== null && item !== void 0)
}
function compactObject(obj) {
- const newObj = { ...obj };
- Object.keys(obj).forEach((k) => {
- const key = k;
+ const newObj = { ...obj }
+ Object.keys(obj).forEach(k => {
+ const key = k
if (newObj[key] === void 0) {
- delete newObj[key];
+ delete newObj[key]
}
- });
- return newObj;
+ })
+ return newObj
}
- async function withTimeout(promise, millis, errmsg = "Timed out") {
- let timerID;
+ async function withTimeout(promise, millis, errmsg = 'Timed out') {
+ let timerID
const timer$ = new Promise((_, reject) => {
timerID = setTimeout(() => {
- reject(new Error(errmsg));
- }, millis);
- });
- return Promise.race([promise, timer$]).finally(() => clearTimeout(timerID));
+ reject(new Error(errmsg))
+ }, millis)
+ })
+ return Promise.race([promise, timer$]).finally(() => clearTimeout(timerID))
}
function newToLegacyStatus(status) {
switch (status) {
- case "connecting":
- return "connecting";
- case "connected":
- return "open";
- case "reconnecting":
- return "unavailable";
- case "disconnected":
- return "failed";
- case "initial":
- return "closed";
+ case 'connecting':
+ return 'connecting'
+ case 'connected':
+ return 'open'
+ case 'reconnecting':
+ return 'unavailable'
+ case 'disconnected':
+ return 'failed'
+ case 'initial':
+ return 'closed'
default:
- return "closed";
+ return 'closed'
}
}
function toNewConnectionStatus(machine) {
- const state = machine.currentState;
+ const state = machine.currentState
switch (state) {
- case "@ok.connected":
- case "@ok.awaiting-pong":
- return "connected";
- case "@idle.initial":
- return "initial";
- case "@auth.busy":
- case "@auth.backoff":
- case "@connecting.busy":
- case "@connecting.backoff":
- return machine.context.successCount > 0 ? "reconnecting" : "connecting";
- case "@idle.failed":
- return "disconnected";
+ case '@ok.connected':
+ case '@ok.awaiting-pong':
+ return 'connected'
+ case '@idle.initial':
+ return 'initial'
+ case '@auth.busy':
+ case '@auth.backoff':
+ case '@connecting.busy':
+ case '@connecting.backoff':
+ return machine.context.successCount > 0 ? 'reconnecting' : 'connecting'
+ case '@idle.failed':
+ return 'disconnected'
default:
- return assertNever(state, "Unknown state");
+ return assertNever(state, 'Unknown state')
}
}
- var BACKOFF_DELAYS = [250, 500, 1e3, 2e3, 4e3, 8e3, 1e4];
- var RESET_DELAY = BACKOFF_DELAYS[0] - 1;
- var BACKOFF_DELAYS_SLOW = [2e3, 3e4, 6e4, 3e5];
- var HEARTBEAT_INTERVAL = 3e4;
- var PONG_TIMEOUT = 2e3;
- var AUTH_TIMEOUT = 1e4;
- var SOCKET_CONNECT_TIMEOUT = 1e4;
+ var BACKOFF_DELAYS = [250, 500, 1e3, 2e3, 4e3, 8e3, 1e4]
+ var RESET_DELAY = BACKOFF_DELAYS[0] - 1
+ var BACKOFF_DELAYS_SLOW = [2e3, 3e4, 6e4, 3e5]
+ var HEARTBEAT_INTERVAL = 3e4
+ var PONG_TIMEOUT = 2e3
+ var AUTH_TIMEOUT = 1e4
+ var SOCKET_CONNECT_TIMEOUT = 1e4
var StopRetrying = class extends Error {
constructor(reason) {
- super(reason);
+ super(reason)
}
- };
+ }
var LiveblocksError = class extends Error {
constructor(message, code) {
- super(message);
- this.code = code;
+ super(message)
+ this.code = code
}
- };
+ }
function nextBackoffDelay(currentDelay, delays = BACKOFF_DELAYS) {
- return delays.find((delay) => delay > currentDelay) ?? delays[delays.length - 1];
+ return (
+ delays.find(delay => delay > currentDelay) ?? delays[delays.length - 1]
+ )
}
function increaseBackoffDelay(context) {
- context.patch({ backoffDelay: nextBackoffDelay(context.backoffDelay) });
+ context.patch({ backoffDelay: nextBackoffDelay(context.backoffDelay) })
}
function increaseBackoffDelayAggressively(context) {
context.patch({
backoffDelay: nextBackoffDelay(context.backoffDelay, BACKOFF_DELAYS_SLOW)
- });
+ })
}
function resetSuccessCount(context) {
- context.patch({ successCount: 0 });
+ context.patch({ successCount: 0 })
}
function log(level, message) {
- const logger = level === 2 ? error2 : level === 1 ? warn : (
- /* black hole */
- () => {
- }
- );
+ const logger =
+ level === 2
+ ? error2
+ : level === 1
+ ? warn
+ : /* black hole */
+ () => {}
return () => {
- logger(message);
- };
+ logger(message)
+ }
}
function logPrematureErrorOrCloseEvent(e) {
- const conn = "Connection to Liveblocks websocket server";
- return (ctx) => {
+ const conn = 'Connection to Liveblocks websocket server'
+ return ctx => {
if (e instanceof Error) {
- warn(`${conn} could not be established. ${String(e)}`);
+ warn(`${conn} could not be established. ${String(e)}`)
} else {
warn(
- isCloseEvent(e) ? `${conn} closed prematurely (code: ${e.code}). Retrying in ${ctx.backoffDelay}ms.` : `${conn} could not be established.`
- );
+ isCloseEvent(e)
+ ? `${conn} closed prematurely (code: ${e.code}). Retrying in ${ctx.backoffDelay}ms.`
+ : `${conn} could not be established.`
+ )
}
- };
+ }
}
function logCloseEvent(event) {
- return (ctx) => {
+ return ctx => {
warn(
`Connection to Liveblocks websocket server closed (code: ${event.code}). Retrying in ${ctx.backoffDelay}ms.`
- );
- };
+ )
+ }
}
var logPermanentClose = log(
1,
"Connection to WebSocket closed permanently. Won't retry."
- );
+ )
function isCloseEvent(error3) {
- return !(error3 instanceof Error) && error3.type === "close";
+ return !(error3 instanceof Error) && error3.type === 'close'
}
function isCustomCloseEvent(error3) {
- return isCloseEvent(error3) && error3.code >= 4e3 && error3.code < 4100;
+ return isCloseEvent(error3) && error3.code >= 4e3 && error3.code < 4100
}
function enableTracing(machine) {
- const start = (/* @__PURE__ */ new Date()).getTime();
+ const start = /* @__PURE__ */ new Date().getTime()
function log2(...args) {
warn(
- `${(((/* @__PURE__ */ new Date()).getTime() - start) / 1e3).toFixed(2)} [FSM #${machine.id}]`,
+ `${((/* @__PURE__ */ new Date().getTime() - start) / 1e3).toFixed(
+ 2
+ )} [FSM #${machine.id}]`,
...args
- );
+ )
}
const unsubs = [
- machine.events.didReceiveEvent.subscribe((e) => log2(`Event ${e.type}`)),
- machine.events.willTransition.subscribe(
- ({ from, to }) => log2("Transitioning", from, "\u2192", to)
+ machine.events.didReceiveEvent.subscribe(e => log2(`Event ${e.type}`)),
+ machine.events.willTransition.subscribe(({ from, to }) =>
+ log2('Transitioning', from, '\u2192', to)
),
- machine.events.didIgnoreEvent.subscribe(
- (e) => log2("Ignored event", e.type, e, "(current state won't handle it)")
+ machine.events.didIgnoreEvent.subscribe(e =>
+ log2('Ignored event', e.type, e, "(current state won't handle it)")
)
// machine.events.willExitState.subscribe((s) => log("Exiting state", s)),
// machine.events.didEnterState.subscribe((s) => log("Entering state", s)),
- ];
+ ]
return () => {
for (const unsub of unsubs) {
- unsub();
+ unsub()
}
- };
+ }
}
function defineConnectivityEvents(machine) {
- const statusDidChange = makeEventSource();
- const didConnect = makeEventSource();
- const didDisconnect = makeEventSource();
- let lastStatus = null;
+ const statusDidChange = makeEventSource()
+ const didConnect = makeEventSource()
+ const didDisconnect = makeEventSource()
+ let lastStatus = null
const unsubscribe = machine.events.didEnterState.subscribe(() => {
- const currStatus = toNewConnectionStatus(machine);
+ const currStatus = toNewConnectionStatus(machine)
if (currStatus !== lastStatus) {
- statusDidChange.notify(currStatus);
+ statusDidChange.notify(currStatus)
}
- if (lastStatus === "connected" && currStatus !== "connected") {
- didDisconnect.notify();
- } else if (lastStatus !== "connected" && currStatus === "connected") {
- didConnect.notify();
+ if (lastStatus === 'connected' && currStatus !== 'connected') {
+ didDisconnect.notify()
+ } else if (lastStatus !== 'connected' && currStatus === 'connected') {
+ didConnect.notify()
}
- lastStatus = currStatus;
- });
+ lastStatus = currStatus
+ })
return {
statusDidChange: statusDidChange.observable,
didConnect: didConnect.observable,
didDisconnect: didDisconnect.observable,
unsubscribe
- };
+ }
}
- var assign = (patch) => (ctx) => ctx.patch(patch);
+ var assign = patch => ctx => ctx.patch(patch)
function createConnectionStateMachine(delegates, enableDebugLogging) {
- const onMessage = makeEventSource();
- onMessage.pause();
- const onLiveblocksError = makeEventSource();
+ const onMessage = makeEventSource()
+ onMessage.pause()
+ const onLiveblocksError = makeEventSource()
const initialContext = {
successCount: 0,
token: null,
socket: null,
backoffDelay: RESET_DELAY
- };
- const machine = new FSM(initialContext).addState("@idle.initial").addState("@idle.failed").addState("@auth.busy").addState("@auth.backoff").addState("@connecting.busy").addState("@connecting.backoff").addState("@ok.connected").addState("@ok.awaiting-pong");
- machine.addTransitions("*", {
+ }
+ const machine = new FSM(initialContext)
+ .addState('@idle.initial')
+ .addState('@idle.failed')
+ .addState('@auth.busy')
+ .addState('@auth.backoff')
+ .addState('@connecting.busy')
+ .addState('@connecting.backoff')
+ .addState('@ok.connected')
+ .addState('@ok.awaiting-pong')
+ machine.addTransitions('*', {
RECONNECT: {
- target: "@auth.backoff",
+ target: '@auth.backoff',
effect: [increaseBackoffDelay, resetSuccessCount]
},
- DISCONNECT: "@idle.initial"
- });
- machine.onEnter("@idle.*", resetSuccessCount).addTransitions("@idle.*", {
- CONNECT: (_, ctx) => (
+ DISCONNECT: '@idle.initial'
+ })
+ machine.onEnter('@idle.*', resetSuccessCount).addTransitions('@idle.*', {
+ CONNECT: (_, ctx) =>
// If we still have a known token, try to reconnect to the socket directly,
// otherwise, try to obtain a new token
- ctx.token !== null ? "@connecting.busy" : "@auth.busy"
+ ctx.token !== null ? '@connecting.busy' : '@auth.busy'
+ })
+ machine
+ .addTransitions('@auth.backoff', {
+ NAVIGATOR_ONLINE: {
+ target: '@auth.busy',
+ effect: assign({ backoffDelay: RESET_DELAY })
+ }
+ })
+ .addTimedTransition(
+ '@auth.backoff',
+ ctx => ctx.backoffDelay,
+ '@auth.busy'
)
- });
- machine.addTransitions("@auth.backoff", {
- NAVIGATOR_ONLINE: {
- target: "@auth.busy",
- effect: assign({ backoffDelay: RESET_DELAY })
- }
- }).addTimedTransition(
- "@auth.backoff",
- (ctx) => ctx.backoffDelay,
- "@auth.busy"
- ).onEnterAsync(
- "@auth.busy",
- () => withTimeout(delegates.authenticate(), AUTH_TIMEOUT),
- // On successful authentication
- (okEvent) => ({
- target: "@connecting.busy",
- effect: assign({
- token: okEvent.data,
- backoffDelay: RESET_DELAY
- })
- }),
- // Auth failed
- (failedEvent) => {
- if (failedEvent.reason instanceof StopRetrying) {
+ .onEnterAsync(
+ '@auth.busy',
+ () => withTimeout(delegates.authenticate(), AUTH_TIMEOUT),
+ // On successful authentication
+ okEvent => ({
+ target: '@connecting.busy',
+ effect: assign({
+ token: okEvent.data,
+ backoffDelay: RESET_DELAY
+ })
+ }),
+ // Auth failed
+ failedEvent => {
+ if (failedEvent.reason instanceof StopRetrying) {
+ return {
+ target: '@idle.failed',
+ effect: log(2, failedEvent.reason.message)
+ }
+ }
return {
- target: "@idle.failed",
- effect: log(2, failedEvent.reason.message)
- };
+ target: '@auth.backoff',
+ effect: [
+ increaseBackoffDelay,
+ log(
+ 2,
+ `Authentication failed: ${
+ failedEvent.reason instanceof Error
+ ? failedEvent.reason.message
+ : String(failedEvent.reason)
+ }`
+ )
+ ]
+ }
}
- return {
- target: "@auth.backoff",
- effect: [
- increaseBackoffDelay,
- log(
- 2,
- `Authentication failed: ${failedEvent.reason instanceof Error ? failedEvent.reason.message : String(failedEvent.reason)}`
- )
- ]
- };
- }
- );
- const onSocketError = (event) => machine.send({ type: "EXPLICIT_SOCKET_ERROR", event });
- const onSocketClose = (event) => machine.send({ type: "EXPLICIT_SOCKET_CLOSE", event });
- const onSocketMessage = (event) => event.data === "pong" ? machine.send({ type: "PONG" }) : onMessage.notify(event);
+ )
+ const onSocketError = event =>
+ machine.send({ type: 'EXPLICIT_SOCKET_ERROR', event })
+ const onSocketClose = event =>
+ machine.send({ type: 'EXPLICIT_SOCKET_CLOSE', event })
+ const onSocketMessage = event =>
+ event.data === 'pong'
+ ? machine.send({ type: 'PONG' })
+ : onMessage.notify(event)
function teardownSocket(socket) {
if (socket) {
- socket.removeEventListener("error", onSocketError);
- socket.removeEventListener("close", onSocketClose);
- socket.removeEventListener("message", onSocketMessage);
- socket.close();
- }
- }
- machine.addTransitions("@connecting.backoff", {
- NAVIGATOR_ONLINE: {
- target: "@connecting.busy",
- effect: assign({ backoffDelay: RESET_DELAY })
- }
- }).addTimedTransition(
- "@connecting.backoff",
- (ctx) => ctx.backoffDelay,
- "@connecting.busy"
- ).onEnterAsync(
- "@connecting.busy",
- //
- // Use the "createSocket" delegate function (provided to the
- // ManagedSocket) to create the actual WebSocket connection instance.
- // Then, set up all the necessary event listeners, and wait for the
- // "open" event to occur.
- //
- // When the "open" event happens, we're ready to transition to the
- // OK state. This is done by resolving the Promise.
- //
- async (ctx) => {
- let capturedPrematureEvent = null;
- const connect$ = new Promise(
- (resolve, rej) => {
+ socket.removeEventListener('error', onSocketError)
+ socket.removeEventListener('close', onSocketClose)
+ socket.removeEventListener('message', onSocketMessage)
+ socket.close()
+ }
+ }
+ machine
+ .addTransitions('@connecting.backoff', {
+ NAVIGATOR_ONLINE: {
+ target: '@connecting.busy',
+ effect: assign({ backoffDelay: RESET_DELAY })
+ }
+ })
+ .addTimedTransition(
+ '@connecting.backoff',
+ ctx => ctx.backoffDelay,
+ '@connecting.busy'
+ )
+ .onEnterAsync(
+ '@connecting.busy',
+ //
+ // Use the "createSocket" delegate function (provided to the
+ // ManagedSocket) to create the actual WebSocket connection instance.
+ // Then, set up all the necessary event listeners, and wait for the
+ // "open" event to occur.
+ //
+ // When the "open" event happens, we're ready to transition to the
+ // OK state. This is done by resolving the Promise.
+ //
+ async ctx => {
+ let capturedPrematureEvent = null
+ const connect$ = new Promise((resolve, rej) => {
if (ctx.token === null) {
- throw new Error("No auth token");
+ throw new Error('No auth token')
}
- const socket = delegates.createSocket(ctx.token);
+ const socket = delegates.createSocket(ctx.token)
function reject(event) {
- capturedPrematureEvent = event;
- socket.removeEventListener("message", onSocketMessage);
- rej(event);
+ capturedPrematureEvent = event
+ socket.removeEventListener('message', onSocketMessage)
+ rej(event)
}
- socket.addEventListener("message", onSocketMessage);
- socket.addEventListener("error", reject);
- socket.addEventListener("close", reject);
- socket.addEventListener("open", () => {
- socket.addEventListener("error", onSocketError);
- socket.addEventListener("close", onSocketClose);
+ socket.addEventListener('message', onSocketMessage)
+ socket.addEventListener('error', reject)
+ socket.addEventListener('close', reject)
+ socket.addEventListener('open', () => {
+ socket.addEventListener('error', onSocketError)
+ socket.addEventListener('close', onSocketClose)
const unsub = () => {
- socket.removeEventListener("error", reject);
- socket.removeEventListener("close", reject);
- };
- resolve([socket, unsub]);
- });
+ socket.removeEventListener('error', reject)
+ socket.removeEventListener('close', reject)
+ }
+ resolve([socket, unsub])
+ })
+ })
+ return withTimeout(connect$, SOCKET_CONNECT_TIMEOUT).then(
+ //
+ // Part 3:
+ // By now, our "open" event has fired, and the promise has been
+ // resolved. Two possible scenarios:
+ //
+ // 1. The happy path. Most likely.
+ // 2. Uh-oh. A premature close/error event has been observed. Let's
+ // reject the promise after all.
+ //
+ // Any close/error event that will get scheduled after this point
+ // onwards, will be caught in the OK state, and dealt with
+ // accordingly.
+ //
+ ([socket, unsub]) => {
+ unsub()
+ if (capturedPrematureEvent) {
+ throw capturedPrematureEvent
+ }
+ return socket
+ }
+ )
+ },
+ // Only transition to OK state after a successfully opened WebSocket connection
+ okEvent => ({
+ target: '@ok.connected',
+ effect: assign({
+ socket: okEvent.data,
+ backoffDelay: RESET_DELAY
+ })
+ }),
+ // If the WebSocket connection cannot be established
+ failure => {
+ const err = failure.reason
+ if (err instanceof StopRetrying) {
+ return {
+ target: '@idle.failed',
+ effect: log(2, err.message)
+ }
}
- );
- return withTimeout(connect$, SOCKET_CONNECT_TIMEOUT).then(
- //
- // Part 3:
- // By now, our "open" event has fired, and the promise has been
- // resolved. Two possible scenarios:
- //
- // 1. The happy path. Most likely.
- // 2. Uh-oh. A premature close/error event has been observed. Let's
- // reject the promise after all.
- //
- // Any close/error event that will get scheduled after this point
- // onwards, will be caught in the OK state, and dealt with
- // accordingly.
- //
- ([socket, unsub]) => {
- unsub();
- if (capturedPrematureEvent) {
- throw capturedPrematureEvent;
+ if (isCloseEvent(err) && err.code === 4999) {
+ return {
+ target: '@idle.failed',
+ effect: log(2, err.reason)
+ }
+ }
+ if (isCustomCloseEvent(err) && err.code !== 4001) {
+ return {
+ target: '@connecting.backoff',
+ effect: [
+ increaseBackoffDelayAggressively,
+ logPrematureErrorOrCloseEvent(err)
+ ]
}
- return socket;
}
- );
- },
- // Only transition to OK state after a successfully opened WebSocket connection
- (okEvent) => ({
- target: "@ok.connected",
- effect: assign({
- socket: okEvent.data,
- backoffDelay: RESET_DELAY
- })
- }),
- // If the WebSocket connection cannot be established
- (failure) => {
- const err = failure.reason;
- if (err instanceof StopRetrying) {
- return {
- target: "@idle.failed",
- effect: log(2, err.message)
- };
- }
- if (isCloseEvent(err) && err.code === 4999) {
- return {
- target: "@idle.failed",
- effect: log(2, err.reason)
- };
- }
- if (isCustomCloseEvent(err) && err.code !== 4001) {
return {
- target: "@connecting.backoff",
- effect: [
- increaseBackoffDelayAggressively,
- logPrematureErrorOrCloseEvent(err)
- ]
- };
+ target: '@auth.backoff',
+ effect: [increaseBackoffDelay, logPrematureErrorOrCloseEvent(err)]
+ }
}
- return {
- target: "@auth.backoff",
- effect: [increaseBackoffDelay, logPrematureErrorOrCloseEvent(err)]
- };
- }
- );
- const sendHeartbeat = {
- target: "@ok.awaiting-pong",
- effect: (ctx) => {
- ctx.socket?.send("ping");
- }
- };
- machine.addTimedTransition("@ok.connected", HEARTBEAT_INTERVAL, sendHeartbeat).addTransitions("@ok.connected", {
- NAVIGATOR_OFFLINE: sendHeartbeat,
- // Don't take the browser's word for it when it says it's offline. Do a ping/pong to make sure.
- WINDOW_GOT_FOCUS: sendHeartbeat
- });
- machine.onEnter("@ok.*", (ctx) => {
- ctx.patch({ successCount: ctx.successCount + 1 });
- const timerID = setTimeout(
- // On the next tick, start delivering all messages that have already
- // been received, and continue synchronous delivery of all future
- // incoming messages.
- onMessage.unpause,
- 0
- );
- return (ctx2) => {
- teardownSocket(ctx2.socket);
- ctx2.patch({ socket: null });
- clearTimeout(timerID);
- onMessage.pause();
- };
- }).addTransitions("@ok.awaiting-pong", { PONG: "@ok.connected" }).addTimedTransition("@ok.awaiting-pong", PONG_TIMEOUT, {
- target: "@connecting.busy",
- // Log implicit connection loss and drop the current open socket
- effect: log(
- 1,
- "Received no pong from server, assume implicit connection loss."
)
- }).addTransitions("@ok.*", {
- // When a socket receives an error, this can cause the closing of the
- // socket, or not. So always check to see if the socket is still OPEN or
- // not. When still OPEN, don't transition.
- EXPLICIT_SOCKET_ERROR: (_, context) => {
- if (context.socket?.readyState === 1) {
- return null;
+ const sendHeartbeat = {
+ target: '@ok.awaiting-pong',
+ effect: ctx => {
+ ctx.socket?.send('ping')
+ }
+ }
+ machine
+ .addTimedTransition('@ok.connected', HEARTBEAT_INTERVAL, sendHeartbeat)
+ .addTransitions('@ok.connected', {
+ NAVIGATOR_OFFLINE: sendHeartbeat,
+ // Don't take the browser's word for it when it says it's offline. Do a ping/pong to make sure.
+ WINDOW_GOT_FOCUS: sendHeartbeat
+ })
+ machine
+ .onEnter('@ok.*', ctx => {
+ ctx.patch({ successCount: ctx.successCount + 1 })
+ const timerID = setTimeout(
+ // On the next tick, start delivering all messages that have already
+ // been received, and continue synchronous delivery of all future
+ // incoming messages.
+ onMessage.unpause,
+ 0
+ )
+ return ctx2 => {
+ teardownSocket(ctx2.socket)
+ ctx2.patch({ socket: null })
+ clearTimeout(timerID)
+ onMessage.pause()
}
- return {
- target: "@connecting.backoff",
- effect: increaseBackoffDelay
- };
- },
- EXPLICIT_SOCKET_CLOSE: (e) => {
- if (e.event.code === 4999) {
+ })
+ .addTransitions('@ok.awaiting-pong', { PONG: '@ok.connected' })
+ .addTimedTransition('@ok.awaiting-pong', PONG_TIMEOUT, {
+ target: '@connecting.busy',
+ // Log implicit connection loss and drop the current open socket
+ effect: log(
+ 1,
+ 'Received no pong from server, assume implicit connection loss.'
+ )
+ })
+ .addTransitions('@ok.*', {
+ // When a socket receives an error, this can cause the closing of the
+ // socket, or not. So always check to see if the socket is still OPEN or
+ // not. When still OPEN, don't transition.
+ EXPLICIT_SOCKET_ERROR: (_, context) => {
+ if (context.socket?.readyState === 1) {
+ return null
+ }
return {
- target: "@idle.failed",
- effect: logPermanentClose
- };
- }
- if (e.event.code === 4001) {
+ target: '@connecting.backoff',
+ effect: increaseBackoffDelay
+ }
+ },
+ EXPLICIT_SOCKET_CLOSE: e => {
+ if (e.event.code === 4999) {
+ return {
+ target: '@idle.failed',
+ effect: logPermanentClose
+ }
+ }
+ if (e.event.code === 4001) {
+ return {
+ target: '@auth.backoff',
+ effect: [increaseBackoffDelay, logCloseEvent(e.event)]
+ }
+ }
+ if (isCustomCloseEvent(e.event)) {
+ return {
+ target: '@connecting.backoff',
+ effect: [
+ increaseBackoffDelayAggressively,
+ logCloseEvent(e.event),
+ () => {
+ const err = new LiveblocksError(e.event.reason, e.event.code)
+ onLiveblocksError.notify(err)
+ }
+ ]
+ }
+ }
return {
- target: "@auth.backoff",
+ target: '@connecting.backoff',
effect: [increaseBackoffDelay, logCloseEvent(e.event)]
- };
- }
- if (isCustomCloseEvent(e.event)) {
- return {
- target: "@connecting.backoff",
- effect: [
- increaseBackoffDelayAggressively,
- logCloseEvent(e.event),
- () => {
- const err = new LiveblocksError(e.event.reason, e.event.code);
- onLiveblocksError.notify(err);
- }
- ]
- };
+ }
}
- return {
- target: "@connecting.backoff",
- effect: [increaseBackoffDelay, logCloseEvent(e.event)]
- };
- }
- });
- if (typeof document !== "undefined") {
- const doc = typeof document !== "undefined" ? document : void 0;
- const win = typeof window !== "undefined" ? window : void 0;
- const root = win ?? doc;
- machine.onEnter("*", (ctx) => {
+ })
+ if (typeof document !== 'undefined') {
+ const doc = typeof document !== 'undefined' ? document : void 0
+ const win = typeof window !== 'undefined' ? window : void 0
+ const root = win ?? doc
+ machine.onEnter('*', ctx => {
function onNetworkOffline() {
- machine.send({ type: "NAVIGATOR_OFFLINE" });
+ machine.send({ type: 'NAVIGATOR_OFFLINE' })
}
function onNetworkBackOnline() {
- machine.send({ type: "NAVIGATOR_ONLINE" });
+ machine.send({ type: 'NAVIGATOR_ONLINE' })
}
function onVisibilityChange() {
- if (doc?.visibilityState === "visible") {
- machine.send({ type: "WINDOW_GOT_FOCUS" });
+ if (doc?.visibilityState === 'visible') {
+ machine.send({ type: 'WINDOW_GOT_FOCUS' })
}
}
- win?.addEventListener("online", onNetworkBackOnline);
- win?.addEventListener("offline", onNetworkOffline);
- root?.addEventListener("visibilitychange", onVisibilityChange);
+ win?.addEventListener('online', onNetworkBackOnline)
+ win?.addEventListener('offline', onNetworkOffline)
+ root?.addEventListener('visibilitychange', onVisibilityChange)
return () => {
- root?.removeEventListener("visibilitychange", onVisibilityChange);
- win?.removeEventListener("online", onNetworkBackOnline);
- win?.removeEventListener("offline", onNetworkOffline);
- teardownSocket(ctx.socket);
- };
- });
- }
- const cleanups = [];
- const { statusDidChange, didConnect, didDisconnect, unsubscribe } = defineConnectivityEvents(machine);
- cleanups.push(unsubscribe);
+ root?.removeEventListener('visibilitychange', onVisibilityChange)
+ win?.removeEventListener('online', onNetworkBackOnline)
+ win?.removeEventListener('offline', onNetworkOffline)
+ teardownSocket(ctx.socket)
+ }
+ })
+ }
+ const cleanups = []
+ const { statusDidChange, didConnect, didDisconnect, unsubscribe } =
+ defineConnectivityEvents(machine)
+ cleanups.push(unsubscribe)
if (enableDebugLogging) {
- cleanups.push(enableTracing(machine));
+ cleanups.push(enableTracing(machine))
}
- machine.start();
+ machine.start()
return {
machine,
cleanups,
@@ -1242,54 +1307,54 @@
onMessage: onMessage.observable,
onLiveblocksError: onLiveblocksError.observable
}
- };
+ }
}
var ManagedSocket = class {
constructor(delegates, enableDebugLogging = false) {
const { machine, events, cleanups } = createConnectionStateMachine(
delegates,
enableDebugLogging
- );
- this.machine = machine;
- this.events = events;
- this.cleanups = cleanups;
+ )
+ this.machine = machine
+ this.events = events
+ this.cleanups = cleanups
}
getLegacyStatus() {
- return newToLegacyStatus(this.getStatus());
+ return newToLegacyStatus(this.getStatus())
}
getStatus() {
try {
- return toNewConnectionStatus(this.machine);
+ return toNewConnectionStatus(this.machine)
} catch {
- return "initial";
+ return 'initial'
}
}
/**
* Returns the current auth token.
*/
get token() {
- return this.machine.context.token;
+ return this.machine.context.token
}
/**
* Call this method to try to connect to a WebSocket. This only has an effect
* if the machine is idle at the moment, otherwise this is a no-op.
*/
connect() {
- this.machine.send({ type: "CONNECT" });
+ this.machine.send({ type: 'CONNECT' })
}
/**
* If idle, will try to connect. Otherwise, it will attempt to reconnect to
* the socket, potentially obtaining a new token first, if needed.
*/
reconnect() {
- this.machine.send({ type: "RECONNECT" });
+ this.machine.send({ type: 'RECONNECT' })
}
/**
* Call this method to disconnect from the current WebSocket. Is going to be
* a no-op if there is no active connection.
*/
disconnect() {
- this.machine.send({ type: "DISCONNECT" });
+ this.machine.send({ type: 'DISCONNECT' })
}
/**
* Call this to stop the machine and run necessary cleanup functions. After
@@ -1297,10 +1362,10 @@
* letting the instance get garbage collected.
*/
destroy() {
- this.machine.stop();
- let cleanup;
- while (cleanup = this.cleanups.pop()) {
- cleanup();
+ this.machine.stop()
+ let cleanup
+ while ((cleanup = this.cleanups.pop())) {
+ cleanup()
}
}
/**
@@ -1308,13 +1373,13 @@
* message if this is somehow impossible.
*/
send(data) {
- const socket = this.machine.context?.socket;
+ const socket = this.machine.context?.socket
if (socket === null) {
- warn("Cannot send: not connected yet", data);
+ warn('Cannot send: not connected yet', data)
} else if (socket.readyState !== 1) {
- warn("Cannot send: WebSocket no longer open", data);
+ warn('Cannot send: WebSocket no longer open', data)
} else {
- socket.send(data);
+ socket.send(data)
}
}
/**
@@ -1322,262 +1387,264 @@
* Not ideal to keep exposed :(
*/
_privateSendMachineEvent(event) {
- this.machine.send(event);
- }
- };
- var MIN_CODE = 32;
- var MAX_CODE = 126;
- var NUM_DIGITS = MAX_CODE - MIN_CODE + 1;
- var ZERO = nthDigit(0);
- var ONE = nthDigit(1);
- var ZERO_NINE = ZERO + nthDigit(-1);
+ this.machine.send(event)
+ }
+ }
+ var MIN_CODE = 32
+ var MAX_CODE = 126
+ var NUM_DIGITS = MAX_CODE - MIN_CODE + 1
+ var ZERO = nthDigit(0)
+ var ONE = nthDigit(1)
+ var ZERO_NINE = ZERO + nthDigit(-1)
function nthDigit(n) {
- const code = MIN_CODE + (n < 0 ? NUM_DIGITS + n : n);
+ const code = MIN_CODE + (n < 0 ? NUM_DIGITS + n : n)
if (code < MIN_CODE || code > MAX_CODE) {
- throw new Error(`Invalid n value: ${n}`);
+ throw new Error(`Invalid n value: ${n}`)
}
- return String.fromCharCode(code);
+ return String.fromCharCode(code)
}
function makePosition(x, y) {
if (x !== void 0 && y !== void 0) {
- return between(x, y);
+ return between(x, y)
} else if (x !== void 0) {
- return after(x);
+ return after(x)
} else if (y !== void 0) {
- return before(y);
+ return before(y)
} else {
- return ONE;
+ return ONE
}
}
function before(pos) {
- const lastIndex = pos.length - 1;
+ const lastIndex = pos.length - 1
for (let i = 0; i <= lastIndex; i++) {
- const code = pos.charCodeAt(i);
+ const code = pos.charCodeAt(i)
if (code <= MIN_CODE) {
- continue;
+ continue
}
if (i === lastIndex) {
if (code === MIN_CODE + 1) {
- return pos.substring(0, i) + ZERO_NINE;
+ return pos.substring(0, i) + ZERO_NINE
} else {
- return pos.substring(0, i) + String.fromCharCode(code - 1);
+ return pos.substring(0, i) + String.fromCharCode(code - 1)
}
} else {
- return pos.substring(0, i + 1);
+ return pos.substring(0, i + 1)
}
}
- return ONE;
+ return ONE
}
function after(pos) {
for (let i = 0; i <= pos.length - 1; i++) {
- const code = pos.charCodeAt(i);
+ const code = pos.charCodeAt(i)
if (code >= MAX_CODE) {
- continue;
+ continue
}
- return pos.substring(0, i) + String.fromCharCode(code + 1);
+ return pos.substring(0, i) + String.fromCharCode(code + 1)
}
- return pos + ONE;
+ return pos + ONE
}
function between(lo, hi) {
if (lo < hi) {
- return _between(lo, hi);
+ return _between(lo, hi)
} else if (lo > hi) {
- return _between(hi, lo);
+ return _between(hi, lo)
} else {
- throw new Error("Cannot compute value between two equal positions");
+ throw new Error('Cannot compute value between two equal positions')
}
}
function _between(lo, hi) {
- let index = 0;
- const loLen = lo.length;
- const hiLen = hi.length;
+ let index = 0
+ const loLen = lo.length
+ const hiLen = hi.length
while (true) {
- const loCode = index < loLen ? lo.charCodeAt(index) : MIN_CODE;
- const hiCode = index < hiLen ? hi.charCodeAt(index) : MAX_CODE;
+ const loCode = index < loLen ? lo.charCodeAt(index) : MIN_CODE
+ const hiCode = index < hiLen ? hi.charCodeAt(index) : MAX_CODE
if (loCode === hiCode) {
- index++;
- continue;
+ index++
+ continue
}
if (hiCode - loCode === 1) {
- const prefix = lo.substring(0, index + 1);
- const suffix = lo.substring(index + 1);
- const nines = "";
- return prefix + _between(suffix, nines);
+ const prefix = lo.substring(0, index + 1)
+ const suffix = lo.substring(index + 1)
+ const nines = ''
+ return prefix + _between(suffix, nines)
} else {
- return takeN(lo, index) + String.fromCharCode(hiCode + loCode >> 1);
+ return takeN(lo, index) + String.fromCharCode((hiCode + loCode) >> 1)
}
}
}
function takeN(pos, n) {
- return n < pos.length ? pos.substring(0, n) : pos + ZERO.repeat(n - pos.length);
+ return n < pos.length
+ ? pos.substring(0, n)
+ : pos + ZERO.repeat(n - pos.length)
}
- var MIN_NON_ZERO_CODE = MIN_CODE + 1;
+ var MIN_NON_ZERO_CODE = MIN_CODE + 1
function isPos(str) {
- if (str === "") {
- return false;
+ if (str === '') {
+ return false
}
- const lastIdx = str.length - 1;
- const last = str.charCodeAt(lastIdx);
+ const lastIdx = str.length - 1
+ const last = str.charCodeAt(lastIdx)
if (last < MIN_NON_ZERO_CODE || last > MAX_CODE) {
- return false;
+ return false
}
for (let i = 0; i < lastIdx; i++) {
- const code = str.charCodeAt(i);
+ const code = str.charCodeAt(i)
if (code < MIN_CODE || code > MAX_CODE) {
- return false;
+ return false
}
}
- return true;
+ return true
}
function convertToPos(str) {
- const codes = [];
+ const codes = []
for (let i = 0; i < str.length; i++) {
- const code = str.charCodeAt(i);
- codes.push(code < MIN_CODE ? MIN_CODE : code > MAX_CODE ? MAX_CODE : code);
+ const code = str.charCodeAt(i)
+ codes.push(code < MIN_CODE ? MIN_CODE : code > MAX_CODE ? MAX_CODE : code)
}
while (codes.length > 0 && codes[codes.length - 1] === MIN_CODE) {
- codes.length--;
+ codes.length--
}
- return codes.length > 0 ? String.fromCharCode(...codes) : (
- // Edge case: the str was a 0-only string, which is invalid. Default back to .1
- ONE
- );
+ return codes.length > 0
+ ? String.fromCharCode(...codes)
+ : // Edge case: the str was a 0-only string, which is invalid. Default back to .1
+ ONE
}
function asPos(str) {
- return isPos(str) ? str : convertToPos(str);
+ return isPos(str) ? str : convertToPos(str)
}
function isAckOp(op) {
- return op.type === 5 && op.id === "ACK";
+ return op.type === 5 && op.id === 'ACK'
}
function crdtAsLiveNode(value) {
- return value;
+ return value
}
function HasParent(node, key, pos = asPos(key)) {
- return Object.freeze({ type: "HasParent", node, key, pos });
+ return Object.freeze({ type: 'HasParent', node, key, pos })
}
- var NoParent = Object.freeze({ type: "NoParent" });
+ var NoParent = Object.freeze({ type: 'NoParent' })
function Orphaned(oldKey, oldPos = asPos(oldKey)) {
- return Object.freeze({ type: "Orphaned", oldKey, oldPos });
+ return Object.freeze({ type: 'Orphaned', oldKey, oldPos })
}
var AbstractCrdt = class {
constructor() {
- this._parent = NoParent;
+ this._parent = NoParent
}
/** @internal */
_getParentKeyOrThrow() {
switch (this.parent.type) {
- case "HasParent":
- return this.parent.key;
- case "NoParent":
- throw new Error("Parent key is missing");
- case "Orphaned":
- return this.parent.oldKey;
+ case 'HasParent':
+ return this.parent.key
+ case 'NoParent':
+ throw new Error('Parent key is missing')
+ case 'Orphaned':
+ return this.parent.oldKey
default:
- return assertNever(this.parent, "Unknown state");
+ return assertNever(this.parent, 'Unknown state')
}
}
/** @internal */
get _parentPos() {
switch (this.parent.type) {
- case "HasParent":
- return this.parent.pos;
- case "NoParent":
- throw new Error("Parent key is missing");
- case "Orphaned":
- return this.parent.oldPos;
+ case 'HasParent':
+ return this.parent.pos
+ case 'NoParent':
+ throw new Error('Parent key is missing')
+ case 'Orphaned':
+ return this.parent.oldPos
default:
- return assertNever(this.parent, "Unknown state");
+ return assertNever(this.parent, 'Unknown state')
}
}
/** @internal */
get _pool() {
- return this.__pool;
+ return this.__pool
}
get roomId() {
- return this.__pool ? this.__pool.roomId : null;
+ return this.__pool ? this.__pool.roomId : null
}
/** @internal */
get _id() {
- return this.__id;
+ return this.__id
}
/** @internal */
get parent() {
- return this._parent;
+ return this._parent
}
/** @internal */
get _parentKey() {
switch (this.parent.type) {
- case "HasParent":
- return this.parent.key;
- case "NoParent":
- return null;
- case "Orphaned":
- return this.parent.oldKey;
+ case 'HasParent':
+ return this.parent.key
+ case 'NoParent':
+ return null
+ case 'Orphaned':
+ return this.parent.oldKey
default:
- return assertNever(this.parent, "Unknown state");
+ return assertNever(this.parent, 'Unknown state')
}
}
/** @internal */
_apply(op, _isLocal) {
switch (op.type) {
case 5: {
- if (this.parent.type === "HasParent") {
- return this.parent.node._detachChild(crdtAsLiveNode(this));
+ if (this.parent.type === 'HasParent') {
+ return this.parent.node._detachChild(crdtAsLiveNode(this))
}
- return { modified: false };
+ return { modified: false }
}
}
- return { modified: false };
+ return { modified: false }
}
/** @internal */
_setParentLink(newParentNode, newParentKey) {
switch (this.parent.type) {
- case "HasParent":
+ case 'HasParent':
if (this.parent.node !== newParentNode) {
- throw new Error("Cannot set parent: node already has a parent");
+ throw new Error('Cannot set parent: node already has a parent')
} else {
- this._parent = HasParent(newParentNode, newParentKey);
- return;
+ this._parent = HasParent(newParentNode, newParentKey)
+ return
}
- case "Orphaned":
- case "NoParent": {
- this._parent = HasParent(newParentNode, newParentKey);
- return;
+ case 'Orphaned':
+ case 'NoParent': {
+ this._parent = HasParent(newParentNode, newParentKey)
+ return
}
default:
- return assertNever(this.parent, "Unknown state");
+ return assertNever(this.parent, 'Unknown state')
}
}
/** @internal */
_attach(id, pool) {
if (this.__id || this.__pool) {
- throw new Error("Cannot attach node: already attached");
+ throw new Error('Cannot attach node: already attached')
}
- pool.addNode(id, crdtAsLiveNode(this));
- this.__id = id;
- this.__pool = pool;
+ pool.addNode(id, crdtAsLiveNode(this))
+ this.__id = id
+ this.__pool = pool
}
/** @internal */
_detach() {
if (this.__pool && this.__id) {
- this.__pool.deleteNode(this.__id);
+ this.__pool.deleteNode(this.__id)
}
switch (this.parent.type) {
- case "HasParent": {
- this._parent = Orphaned(this.parent.key, this.parent.pos);
- break;
+ case 'HasParent': {
+ this._parent = Orphaned(this.parent.key, this.parent.pos)
+ break
}
- case "NoParent": {
- this._parent = NoParent;
- break;
+ case 'NoParent': {
+ this._parent = NoParent
+ break
}
- case "Orphaned": {
- break;
+ case 'Orphaned': {
+ break
}
default:
- assertNever(this.parent, "Unknown state");
+ assertNever(this.parent, 'Unknown state')
}
- this.__pool = void 0;
+ this.__pool = void 0
}
/**
* @internal
@@ -1588,10 +1655,10 @@
*/
invalidate() {
if (this._cachedImmutable !== void 0 || this._cachedTreeNode !== void 0) {
- this._cachedImmutable = void 0;
- this._cachedTreeNode = void 0;
- if (this.parent.type === "HasParent") {
- this.parent.node.invalidate();
+ this._cachedImmutable = void 0
+ this._cachedTreeNode = void 0
+ if (this.parent.type === 'HasParent') {
+ this.parent.node.invalidate()
}
}
}
@@ -1602,55 +1669,55 @@
*/
toTreeNode(key) {
if (this._cachedTreeNode === void 0 || this._cachedTreeNodeKey !== key) {
- this._cachedTreeNodeKey = key;
- this._cachedTreeNode = this._toTreeNode(key);
+ this._cachedTreeNodeKey = key
+ this._cachedTreeNode = this._toTreeNode(key)
}
- return this._cachedTreeNode;
+ return this._cachedTreeNode
}
/**
* Return an immutable snapshot of this Live node and its children.
*/
toImmutable() {
if (this._cachedImmutable === void 0) {
- this._cachedImmutable = this._toImmutable();
+ this._cachedImmutable = this._toImmutable()
}
- return this._cachedImmutable;
+ return this._cachedImmutable
}
- };
+ }
function isRootCrdt(crdt) {
- return crdt.type === 0 && !isChildCrdt(crdt);
+ return crdt.type === 0 && !isChildCrdt(crdt)
}
function isChildCrdt(crdt) {
- return crdt.parentId !== void 0 && crdt.parentKey !== void 0;
+ return crdt.parentId !== void 0 && crdt.parentKey !== void 0
}
function nanoid(length = 7) {
- const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,./;[]~!@#$%&*()_+=-";
- const len = alphabet.length;
- return Array.from(
- { length },
- () => alphabet.charAt(Math.floor(Math.random() * len))
- ).join("");
+ const alphabet =
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,./;[]~!@#$%&*()_+=-'
+ const len = alphabet.length
+ return Array.from({ length }, () =>
+ alphabet.charAt(Math.floor(Math.random() * len))
+ ).join('')
}
var LiveRegister = class _LiveRegister extends AbstractCrdt {
constructor(data) {
- super();
- this._data = data;
+ super()
+ this._data = data
}
get data() {
- return this._data;
+ return this._data
}
/** @internal */
static _deserialize([id, item], _parentToChildren, pool) {
- const register = new _LiveRegister(item.data);
- register._attach(id, pool);
- return register;
+ const register = new _LiveRegister(item.data)
+ register._attach(id, pool)
+ return register
}
/** @internal */
_toOps(parentId, parentKey, pool) {
if (this._id === void 0) {
throw new Error(
- "Cannot serialize register if parentId or parentKey is undefined"
- );
+ 'Cannot serialize register if parentId or parentKey is undefined'
+ )
}
return [
{
@@ -1661,99 +1728,99 @@
parentKey,
data: this.data
}
- ];
+ ]
}
/** @internal */
_serialize() {
- if (this.parent.type !== "HasParent") {
- throw new Error("Cannot serialize LiveRegister if parent is missing");
+ if (this.parent.type !== 'HasParent') {
+ throw new Error('Cannot serialize LiveRegister if parent is missing')
}
return {
type: 3,
- parentId: nn(this.parent.node._id, "Parent node expected to have ID"),
+ parentId: nn(this.parent.node._id, 'Parent node expected to have ID'),
parentKey: this.parent.key,
data: this.data
- };
+ }
}
/** @internal */
_attachChild(_op) {
- throw new Error("Method not implemented.");
+ throw new Error('Method not implemented.')
}
/** @internal */
_detachChild(_crdt) {
- throw new Error("Method not implemented.");
+ throw new Error('Method not implemented.')
}
/** @internal */
_apply(op, isLocal) {
- return super._apply(op, isLocal);
+ return super._apply(op, isLocal)
}
/** @internal */
_toTreeNode(key) {
return {
- type: "Json",
+ type: 'Json',
id: this._id ?? nanoid(),
key,
payload: this._data
- };
+ }
}
/** @internal */
_toImmutable() {
- return this._data;
+ return this._data
}
- };
+ }
function compareNodePosition(itemA, itemB) {
- const posA = itemA._parentPos;
- const posB = itemB._parentPos;
- return posA === posB ? 0 : posA < posB ? -1 : 1;
+ const posA = itemA._parentPos
+ const posB = itemB._parentPos
+ return posA === posB ? 0 : posA < posB ? -1 : 1
}
var LiveList = class _LiveList extends AbstractCrdt {
constructor(items = []) {
- super();
- this._items = [];
- this._implicitlyDeletedItems = /* @__PURE__ */ new WeakSet();
- this._unacknowledgedSets = /* @__PURE__ */ new Map();
- let position = void 0;
+ super()
+ this._items = []
+ this._implicitlyDeletedItems = /* @__PURE__ */ new WeakSet()
+ this._unacknowledgedSets = /* @__PURE__ */ new Map()
+ let position = void 0
for (const item of items) {
- const newPosition = makePosition(position);
- const node = lsonToLiveNode(item);
- node._setParentLink(this, newPosition);
- this._items.push(node);
- position = newPosition;
+ const newPosition = makePosition(position)
+ const node = lsonToLiveNode(item)
+ node._setParentLink(this, newPosition)
+ this._items.push(node)
+ position = newPosition
}
}
/** @internal */
static _deserialize([id], parentToChildren, pool) {
- const list = new _LiveList();
- list._attach(id, pool);
- const children = parentToChildren.get(id);
+ const list = new _LiveList()
+ list._attach(id, pool)
+ const children = parentToChildren.get(id)
if (children === void 0) {
- return list;
+ return list
}
for (const [id2, crdt] of children) {
- const child = deserialize([id2, crdt], parentToChildren, pool);
- child._setParentLink(list, crdt.parentKey);
- list._insertAndSort(child);
+ const child = deserialize([id2, crdt], parentToChildren, pool)
+ child._setParentLink(list, crdt.parentKey)
+ list._insertAndSort(child)
}
- return list;
+ return list
}
/** @internal */
_toOps(parentId, parentKey, pool) {
if (this._id === void 0) {
- throw new Error("Cannot serialize item is not attached");
+ throw new Error('Cannot serialize item is not attached')
}
- const ops = [];
+ const ops = []
const op = {
id: this._id,
opId: pool?.generateOpId(),
type: 2,
parentId,
parentKey
- };
- ops.push(op);
+ }
+ ops.push(op)
for (const item of this._items) {
- ops.push(...item._toOps(this._id, item._getParentKeyOrThrow(), pool));
+ ops.push(...item._toOps(this._id, item._getParentKeyOrThrow(), pool))
}
- return ops;
+ return ops
}
/**
* @internal
@@ -1761,165 +1828,171 @@
* Adds a new item into the sorted list, in the correct position.
*/
_insertAndSort(item) {
- this._items.push(item);
- this._sortItems();
+ this._items.push(item)
+ this._sortItems()
}
/** @internal */
_sortItems() {
- this._items.sort(compareNodePosition);
- this.invalidate();
+ this._items.sort(compareNodePosition)
+ this.invalidate()
}
/** @internal */
_indexOfPosition(position) {
return this._items.findIndex(
- (item) => item._getParentKeyOrThrow() === position
- );
+ item => item._getParentKeyOrThrow() === position
+ )
}
/** @internal */
_attach(id, pool) {
- super._attach(id, pool);
+ super._attach(id, pool)
for (const item of this._items) {
- item._attach(pool.generateId(), pool);
+ item._attach(pool.generateId(), pool)
}
}
/** @internal */
_detach() {
- super._detach();
+ super._detach()
for (const item of this._items) {
- item._detach();
+ item._detach()
}
}
/** @internal */
_applySetRemote(op) {
if (this._pool === void 0) {
- throw new Error("Can't attach child if managed pool is not present");
- }
- const { id, parentKey: key } = op;
- const child = creationOpToLiveNode(op);
- child._attach(id, this._pool);
- child._setParentLink(this, key);
- const deletedId = op.deletedId;
- const indexOfItemWithSamePosition = this._indexOfPosition(key);
+ throw new Error("Can't attach child if managed pool is not present")
+ }
+ const { id, parentKey: key } = op
+ const child = creationOpToLiveNode(op)
+ child._attach(id, this._pool)
+ child._setParentLink(this, key)
+ const deletedId = op.deletedId
+ const indexOfItemWithSamePosition = this._indexOfPosition(key)
if (indexOfItemWithSamePosition !== -1) {
- const itemWithSamePosition = this._items[indexOfItemWithSamePosition];
+ const itemWithSamePosition = this._items[indexOfItemWithSamePosition]
if (itemWithSamePosition._id === deletedId) {
- itemWithSamePosition._detach();
- this._items[indexOfItemWithSamePosition] = child;
+ itemWithSamePosition._detach()
+ this._items[indexOfItemWithSamePosition] = child
return {
modified: makeUpdate(this, [
setDelta(indexOfItemWithSamePosition, child)
]),
reverse: []
- };
+ }
} else {
- this._implicitlyDeletedItems.add(itemWithSamePosition);
- this._items[indexOfItemWithSamePosition] = child;
- const delta = [
- setDelta(indexOfItemWithSamePosition, child)
- ];
+ this._implicitlyDeletedItems.add(itemWithSamePosition)
+ this._items[indexOfItemWithSamePosition] = child
+ const delta = [setDelta(indexOfItemWithSamePosition, child)]
const deleteDelta2 = this._detachItemAssociatedToSetOperation(
op.deletedId
- );
+ )
if (deleteDelta2) {
- delta.push(deleteDelta2);
+ delta.push(deleteDelta2)
}
return {
modified: makeUpdate(this, delta),
reverse: []
- };
+ }
}
} else {
- const updates = [];
+ const updates = []
const deleteDelta2 = this._detachItemAssociatedToSetOperation(
op.deletedId
- );
+ )
if (deleteDelta2) {
- updates.push(deleteDelta2);
+ updates.push(deleteDelta2)
}
- this._insertAndSort(child);
- updates.push(insertDelta(this._indexOfPosition(key), child));
+ this._insertAndSort(child)
+ updates.push(insertDelta(this._indexOfPosition(key), child))
return {
reverse: [],
modified: makeUpdate(this, updates)
- };
+ }
}
}
/** @internal */
_applySetAck(op) {
if (this._pool === void 0) {
- throw new Error("Can't attach child if managed pool is not present");
+ throw new Error("Can't attach child if managed pool is not present")
}
- const delta = [];
- const deletedDelta = this._detachItemAssociatedToSetOperation(op.deletedId);
+ const delta = []
+ const deletedDelta = this._detachItemAssociatedToSetOperation(
+ op.deletedId
+ )
if (deletedDelta) {
- delta.push(deletedDelta);
+ delta.push(deletedDelta)
}
- const unacknowledgedOpId = this._unacknowledgedSets.get(op.parentKey);
+ const unacknowledgedOpId = this._unacknowledgedSets.get(op.parentKey)
if (unacknowledgedOpId !== void 0) {
if (unacknowledgedOpId !== op.opId) {
- return delta.length === 0 ? { modified: false } : { modified: makeUpdate(this, delta), reverse: [] };
+ return delta.length === 0
+ ? { modified: false }
+ : { modified: makeUpdate(this, delta), reverse: [] }
} else {
- this._unacknowledgedSets.delete(op.parentKey);
+ this._unacknowledgedSets.delete(op.parentKey)
}
}
- const indexOfItemWithSamePosition = this._indexOfPosition(op.parentKey);
- const existingItem = this._items.find((item) => item._id === op.id);
+ const indexOfItemWithSamePosition = this._indexOfPosition(op.parentKey)
+ const existingItem = this._items.find(item => item._id === op.id)
if (existingItem !== void 0) {
if (existingItem._parentKey === op.parentKey) {
return {
modified: delta.length > 0 ? makeUpdate(this, delta) : false,
reverse: []
- };
+ }
}
if (indexOfItemWithSamePosition !== -1) {
this._implicitlyDeletedItems.add(
this._items[indexOfItemWithSamePosition]
- );
- this._items.splice(indexOfItemWithSamePosition, 1);
- delta.push(deleteDelta(indexOfItemWithSamePosition));
- }
- const previousIndex = this._items.indexOf(existingItem);
- existingItem._setParentLink(this, op.parentKey);
- this._sortItems();
- const newIndex = this._items.indexOf(existingItem);
+ )
+ this._items.splice(indexOfItemWithSamePosition, 1)
+ delta.push(deleteDelta(indexOfItemWithSamePosition))
+ }
+ const previousIndex = this._items.indexOf(existingItem)
+ existingItem._setParentLink(this, op.parentKey)
+ this._sortItems()
+ const newIndex = this._items.indexOf(existingItem)
if (newIndex !== previousIndex) {
- delta.push(moveDelta(previousIndex, newIndex, existingItem));
+ delta.push(moveDelta(previousIndex, newIndex, existingItem))
}
return {
modified: delta.length > 0 ? makeUpdate(this, delta) : false,
reverse: []
- };
+ }
} else {
- const orphan = this._pool.getNode(op.id);
+ const orphan = this._pool.getNode(op.id)
if (orphan && this._implicitlyDeletedItems.has(orphan)) {
- orphan._setParentLink(this, op.parentKey);
- this._implicitlyDeletedItems.delete(orphan);
- this._insertAndSort(orphan);
- const recreatedItemIndex = this._items.indexOf(orphan);
+ orphan._setParentLink(this, op.parentKey)
+ this._implicitlyDeletedItems.delete(orphan)
+ this._insertAndSort(orphan)
+ const recreatedItemIndex = this._items.indexOf(orphan)
return {
modified: makeUpdate(this, [
// If there is an item at this position, update is a set, else it's an insert
- indexOfItemWithSamePosition === -1 ? insertDelta(recreatedItemIndex, orphan) : setDelta(recreatedItemIndex, orphan),
+ indexOfItemWithSamePosition === -1
+ ? insertDelta(recreatedItemIndex, orphan)
+ : setDelta(recreatedItemIndex, orphan),
...delta
]),
reverse: []
- };
+ }
} else {
if (indexOfItemWithSamePosition !== -1) {
- this._items.splice(indexOfItemWithSamePosition, 1);
+ this._items.splice(indexOfItemWithSamePosition, 1)
}
const { newItem, newIndex } = this._createAttachItemAndSort(
op,
op.parentKey
- );
+ )
return {
modified: makeUpdate(this, [
// If there is an item at this position, update is a set, else it's an insert
- indexOfItemWithSamePosition === -1 ? insertDelta(newIndex, newItem) : setDelta(newIndex, newItem),
+ indexOfItemWithSamePosition === -1
+ ? insertDelta(newIndex, newItem)
+ : setDelta(newIndex, newItem),
...delta
]),
reverse: []
- };
+ }
}
}
}
@@ -1929,318 +2002,322 @@
*/
_detachItemAssociatedToSetOperation(deletedId) {
if (deletedId === void 0 || this._pool === void 0) {
- return null;
+ return null
}
- const deletedItem = this._pool.getNode(deletedId);
+ const deletedItem = this._pool.getNode(deletedId)
if (deletedItem === void 0) {
- return null;
+ return null
}
- const result = this._detachChild(deletedItem);
+ const result = this._detachChild(deletedItem)
if (result.modified === false) {
- return null;
+ return null
}
- return result.modified.updates[0];
+ return result.modified.updates[0]
}
/** @internal */
_applyRemoteInsert(op) {
if (this._pool === void 0) {
- throw new Error("Can't attach child if managed pool is not present");
+ throw new Error("Can't attach child if managed pool is not present")
}
- const key = asPos(op.parentKey);
- const existingItemIndex = this._indexOfPosition(key);
+ const key = asPos(op.parentKey)
+ const existingItemIndex = this._indexOfPosition(key)
if (existingItemIndex !== -1) {
- this._shiftItemPosition(existingItemIndex, key);
+ this._shiftItemPosition(existingItemIndex, key)
}
- const { newItem, newIndex } = this._createAttachItemAndSort(op, key);
+ const { newItem, newIndex } = this._createAttachItemAndSort(op, key)
return {
modified: makeUpdate(this, [insertDelta(newIndex, newItem)]),
reverse: []
- };
+ }
}
/** @internal */
_applyInsertAck(op) {
- const existingItem = this._items.find((item) => item._id === op.id);
- const key = asPos(op.parentKey);
- const itemIndexAtPosition = this._indexOfPosition(key);
+ const existingItem = this._items.find(item => item._id === op.id)
+ const key = asPos(op.parentKey)
+ const itemIndexAtPosition = this._indexOfPosition(key)
if (existingItem) {
if (existingItem._parentKey === key) {
return {
modified: false
- };
+ }
} else {
- const oldPositionIndex = this._items.indexOf(existingItem);
+ const oldPositionIndex = this._items.indexOf(existingItem)
if (itemIndexAtPosition !== -1) {
- this._shiftItemPosition(itemIndexAtPosition, key);
+ this._shiftItemPosition(itemIndexAtPosition, key)
}
- existingItem._setParentLink(this, key);
- this._sortItems();
- const newIndex = this._indexOfPosition(key);
+ existingItem._setParentLink(this, key)
+ this._sortItems()
+ const newIndex = this._indexOfPosition(key)
if (newIndex === oldPositionIndex) {
- return { modified: false };
+ return { modified: false }
}
return {
modified: makeUpdate(this, [
moveDelta(oldPositionIndex, newIndex, existingItem)
]),
reverse: []
- };
+ }
}
} else {
- const orphan = nn(this._pool).getNode(op.id);
+ const orphan = nn(this._pool).getNode(op.id)
if (orphan && this._implicitlyDeletedItems.has(orphan)) {
- orphan._setParentLink(this, key);
- this._implicitlyDeletedItems.delete(orphan);
- this._insertAndSort(orphan);
- const newIndex = this._indexOfPosition(key);
+ orphan._setParentLink(this, key)
+ this._implicitlyDeletedItems.delete(orphan)
+ this._insertAndSort(orphan)
+ const newIndex = this._indexOfPosition(key)
return {
modified: makeUpdate(this, [insertDelta(newIndex, orphan)]),
reverse: []
- };
+ }
} else {
if (itemIndexAtPosition !== -1) {
- this._shiftItemPosition(itemIndexAtPosition, key);
+ this._shiftItemPosition(itemIndexAtPosition, key)
}
- const { newItem, newIndex } = this._createAttachItemAndSort(op, key);
+ const { newItem, newIndex } = this._createAttachItemAndSort(op, key)
return {
modified: makeUpdate(this, [insertDelta(newIndex, newItem)]),
reverse: []
- };
+ }
}
}
}
/** @internal */
_applyInsertUndoRedo(op) {
- const { id, parentKey: key } = op;
- const child = creationOpToLiveNode(op);
+ const { id, parentKey: key } = op
+ const child = creationOpToLiveNode(op)
if (this._pool?.getNode(id) !== void 0) {
- return { modified: false };
+ return { modified: false }
}
- child._attach(id, nn(this._pool));
- child._setParentLink(this, key);
- const existingItemIndex = this._indexOfPosition(key);
- let newKey = key;
+ child._attach(id, nn(this._pool))
+ child._setParentLink(this, key)
+ const existingItemIndex = this._indexOfPosition(key)
+ let newKey = key
if (existingItemIndex !== -1) {
- const before2 = this._items[existingItemIndex]?._parentPos;
- const after2 = this._items[existingItemIndex + 1]?._parentPos;
- newKey = makePosition(before2, after2);
- child._setParentLink(this, newKey);
+ const before2 = this._items[existingItemIndex]?._parentPos
+ const after2 = this._items[existingItemIndex + 1]?._parentPos
+ newKey = makePosition(before2, after2)
+ child._setParentLink(this, newKey)
}
- this._insertAndSort(child);
- const newIndex = this._indexOfPosition(newKey);
+ this._insertAndSort(child)
+ const newIndex = this._indexOfPosition(newKey)
return {
modified: makeUpdate(this, [insertDelta(newIndex, child)]),
reverse: [{ type: 5, id }]
- };
+ }
}
/** @internal */
_applySetUndoRedo(op) {
- const { id, parentKey: key } = op;
- const child = creationOpToLiveNode(op);
+ const { id, parentKey: key } = op
+ const child = creationOpToLiveNode(op)
if (this._pool?.getNode(id) !== void 0) {
- return { modified: false };
+ return { modified: false }
}
- this._unacknowledgedSets.set(key, nn(op.opId));
- const indexOfItemWithSameKey = this._indexOfPosition(key);
- child._attach(id, nn(this._pool));
- child._setParentLink(this, key);
- const newKey = key;
+ this._unacknowledgedSets.set(key, nn(op.opId))
+ const indexOfItemWithSameKey = this._indexOfPosition(key)
+ child._attach(id, nn(this._pool))
+ child._setParentLink(this, key)
+ const newKey = key
if (indexOfItemWithSameKey !== -1) {
- const existingItem = this._items[indexOfItemWithSameKey];
- existingItem._detach();
- this._items[indexOfItemWithSameKey] = child;
+ const existingItem = this._items[indexOfItemWithSameKey]
+ existingItem._detach()
+ this._items[indexOfItemWithSameKey] = child
const reverse = HACK_addIntentAndDeletedIdToOperation(
existingItem._toOps(nn(this._id), key, this._pool),
op.id
- );
- const delta = [setDelta(indexOfItemWithSameKey, child)];
+ )
+ const delta = [setDelta(indexOfItemWithSameKey, child)]
const deletedDelta = this._detachItemAssociatedToSetOperation(
op.deletedId
- );
+ )
if (deletedDelta) {
- delta.push(deletedDelta);
+ delta.push(deletedDelta)
}
return {
modified: makeUpdate(this, delta),
reverse
- };
+ }
} else {
- this._insertAndSort(child);
- this._detachItemAssociatedToSetOperation(op.deletedId);
- const newIndex = this._indexOfPosition(newKey);
+ this._insertAndSort(child)
+ this._detachItemAssociatedToSetOperation(op.deletedId)
+ const newIndex = this._indexOfPosition(newKey)
return {
reverse: [{ type: 5, id }],
modified: makeUpdate(this, [insertDelta(newIndex, child)])
- };
+ }
}
}
/** @internal */
_attachChild(op, source) {
if (this._pool === void 0) {
- throw new Error("Can't attach child if managed pool is not present");
+ throw new Error("Can't attach child if managed pool is not present")
}
- let result;
- if (op.intent === "set") {
+ let result
+ if (op.intent === 'set') {
if (source === 1) {
- result = this._applySetRemote(op);
+ result = this._applySetRemote(op)
} else if (source === 2) {
- result = this._applySetAck(op);
+ result = this._applySetAck(op)
} else {
- result = this._applySetUndoRedo(op);
+ result = this._applySetUndoRedo(op)
}
} else {
if (source === 1) {
- result = this._applyRemoteInsert(op);
+ result = this._applyRemoteInsert(op)
} else if (source === 2) {
- result = this._applyInsertAck(op);
+ result = this._applyInsertAck(op)
} else {
- result = this._applyInsertUndoRedo(op);
+ result = this._applyInsertUndoRedo(op)
}
}
if (result.modified !== false) {
- this.invalidate();
+ this.invalidate()
}
- return result;
+ return result
}
/** @internal */
_detachChild(child) {
if (child) {
- const parentKey = nn(child._parentKey);
- const reverse = child._toOps(nn(this._id), parentKey, this._pool);
- const indexToDelete = this._items.indexOf(child);
+ const parentKey = nn(child._parentKey)
+ const reverse = child._toOps(nn(this._id), parentKey, this._pool)
+ const indexToDelete = this._items.indexOf(child)
if (indexToDelete === -1) {
return {
modified: false
- };
+ }
}
- this._items.splice(indexToDelete, 1);
- this.invalidate();
- child._detach();
+ this._items.splice(indexToDelete, 1)
+ this.invalidate()
+ child._detach()
return {
modified: makeUpdate(this, [deleteDelta(indexToDelete)]),
reverse
- };
+ }
}
- return { modified: false };
+ return { modified: false }
}
/** @internal */
_applySetChildKeyRemote(newKey, child) {
if (this._implicitlyDeletedItems.has(child)) {
- this._implicitlyDeletedItems.delete(child);
- child._setParentLink(this, newKey);
- this._insertAndSort(child);
- const newIndex = this._items.indexOf(child);
+ this._implicitlyDeletedItems.delete(child)
+ child._setParentLink(this, newKey)
+ this._insertAndSort(child)
+ const newIndex = this._items.indexOf(child)
return {
modified: makeUpdate(this, [insertDelta(newIndex, child)]),
reverse: []
- };
+ }
}
- const previousKey = child._parentKey;
+ const previousKey = child._parentKey
if (newKey === previousKey) {
return {
modified: false
- };
+ }
}
- const existingItemIndex = this._indexOfPosition(newKey);
+ const existingItemIndex = this._indexOfPosition(newKey)
if (existingItemIndex === -1) {
- const previousIndex = this._items.indexOf(child);
- child._setParentLink(this, newKey);
- this._sortItems();
- const newIndex = this._items.indexOf(child);
+ const previousIndex = this._items.indexOf(child)
+ child._setParentLink(this, newKey)
+ this._sortItems()
+ const newIndex = this._items.indexOf(child)
if (newIndex === previousIndex) {
return {
modified: false
- };
+ }
}
return {
- modified: makeUpdate(this, [moveDelta(previousIndex, newIndex, child)]),
+ modified: makeUpdate(this, [
+ moveDelta(previousIndex, newIndex, child)
+ ]),
reverse: []
- };
+ }
} else {
this._items[existingItemIndex]._setParentLink(
this,
makePosition(newKey, this._items[existingItemIndex + 1]?._parentPos)
- );
- const previousIndex = this._items.indexOf(child);
- child._setParentLink(this, newKey);
- this._sortItems();
- const newIndex = this._items.indexOf(child);
+ )
+ const previousIndex = this._items.indexOf(child)
+ child._setParentLink(this, newKey)
+ this._sortItems()
+ const newIndex = this._items.indexOf(child)
if (newIndex === previousIndex) {
return {
modified: false
- };
+ }
}
return {
- modified: makeUpdate(this, [moveDelta(previousIndex, newIndex, child)]),
+ modified: makeUpdate(this, [
+ moveDelta(previousIndex, newIndex, child)
+ ]),
reverse: []
- };
+ }
}
}
/** @internal */
_applySetChildKeyAck(newKey, child) {
- const previousKey = nn(child._parentKey);
+ const previousKey = nn(child._parentKey)
if (this._implicitlyDeletedItems.has(child)) {
- const existingItemIndex = this._indexOfPosition(newKey);
- this._implicitlyDeletedItems.delete(child);
+ const existingItemIndex = this._indexOfPosition(newKey)
+ this._implicitlyDeletedItems.delete(child)
if (existingItemIndex !== -1) {
this._items[existingItemIndex]._setParentLink(
this,
makePosition(newKey, this._items[existingItemIndex + 1]?._parentPos)
- );
+ )
}
- child._setParentLink(this, newKey);
- this._insertAndSort(child);
+ child._setParentLink(this, newKey)
+ this._insertAndSort(child)
return {
modified: false
- };
+ }
} else {
if (newKey === previousKey) {
return {
modified: false
- };
+ }
}
- const previousIndex = this._items.indexOf(child);
- const existingItemIndex = this._indexOfPosition(newKey);
+ const previousIndex = this._items.indexOf(child)
+ const existingItemIndex = this._indexOfPosition(newKey)
if (existingItemIndex !== -1) {
this._items[existingItemIndex]._setParentLink(
this,
makePosition(newKey, this._items[existingItemIndex + 1]?._parentPos)
- );
+ )
}
- child._setParentLink(this, newKey);
- this._sortItems();
- const newIndex = this._items.indexOf(child);
+ child._setParentLink(this, newKey)
+ this._sortItems()
+ const newIndex = this._items.indexOf(child)
if (previousIndex === newIndex) {
return {
modified: false
- };
+ }
} else {
return {
modified: makeUpdate(this, [
moveDelta(previousIndex, newIndex, child)
]),
reverse: []
- };
+ }
}
}
}
/** @internal */
_applySetChildKeyUndoRedo(newKey, child) {
- const previousKey = nn(child._parentKey);
- const previousIndex = this._items.indexOf(child);
- const existingItemIndex = this._indexOfPosition(newKey);
+ const previousKey = nn(child._parentKey)
+ const previousIndex = this._items.indexOf(child)
+ const existingItemIndex = this._indexOfPosition(newKey)
if (existingItemIndex !== -1) {
this._items[existingItemIndex]._setParentLink(
this,
makePosition(newKey, this._items[existingItemIndex + 1]?._parentPos)
- );
+ )
}
- child._setParentLink(this, newKey);
- this._sortItems();
- const newIndex = this._items.indexOf(child);
+ child._setParentLink(this, newKey)
+ this._sortItems()
+ const newIndex = this._items.indexOf(child)
if (previousIndex === newIndex) {
return {
modified: false
- };
+ }
}
return {
modified: makeUpdate(this, [moveDelta(previousIndex, newIndex, child)]),
@@ -2251,46 +2328,46 @@
parentKey: previousKey
}
]
- };
+ }
}
/** @internal */
_setChildKey(newKey, child, source) {
if (source === 1) {
- return this._applySetChildKeyRemote(newKey, child);
+ return this._applySetChildKeyRemote(newKey, child)
} else if (source === 2) {
- return this._applySetChildKeyAck(newKey, child);
+ return this._applySetChildKeyAck(newKey, child)
} else {
- return this._applySetChildKeyUndoRedo(newKey, child);
+ return this._applySetChildKeyUndoRedo(newKey, child)
}
}
/** @internal */
_apply(op, isLocal) {
- return super._apply(op, isLocal);
+ return super._apply(op, isLocal)
}
/** @internal */
_serialize() {
- if (this.parent.type !== "HasParent") {
- throw new Error("Cannot serialize LiveList if parent is missing");
+ if (this.parent.type !== 'HasParent') {
+ throw new Error('Cannot serialize LiveList if parent is missing')
}
return {
type: 1,
- parentId: nn(this.parent.node._id, "Parent node expected to have ID"),
+ parentId: nn(this.parent.node._id, 'Parent node expected to have ID'),
parentKey: this.parent.key
- };
+ }
}
/**
* Returns the number of elements.
*/
get length() {
- return this._items.length;
+ return this._items.length
}
/**
* Adds one element to the end of the LiveList.
* @param element The element to add to the end of the LiveList.
*/
push(element) {
- this._pool?.assertStorageIsWritable();
- return this.insert(element, this.length);
+ this._pool?.assertStorageIsWritable()
+ return this.insert(element, this.length)
}
/**
* Inserts one element at a specified index.
@@ -2298,28 +2375,30 @@
* @param index The index at which you want to insert the element.
*/
insert(element, index) {
- this._pool?.assertStorageIsWritable();
+ this._pool?.assertStorageIsWritable()
if (index < 0 || index > this._items.length) {
throw new Error(
`Cannot insert list item at index "${index}". index should be between 0 and ${this._items.length}`
- );
- }
- const before2 = this._items[index - 1] ? this._items[index - 1]._parentPos : void 0;
- const after2 = this._items[index] ? this._items[index]._parentPos : void 0;
- const position = makePosition(before2, after2);
- const value = lsonToLiveNode(element);
- value._setParentLink(this, position);
- this._insertAndSort(value);
+ )
+ }
+ const before2 = this._items[index - 1]
+ ? this._items[index - 1]._parentPos
+ : void 0
+ const after2 = this._items[index] ? this._items[index]._parentPos : void 0
+ const position = makePosition(before2, after2)
+ const value = lsonToLiveNode(element)
+ value._setParentLink(this, position)
+ this._insertAndSort(value)
if (this._pool && this._id) {
- const id = this._pool.generateId();
- value._attach(id, this._pool);
+ const id = this._pool.generateId()
+ value._attach(id, this._pool)
this._pool.dispatch(
value._toOps(this._id, position, this._pool),
[{ type: 5, id }],
/* @__PURE__ */ new Map([
[this._id, makeUpdate(this, [insertDelta(index, value)])]
])
- );
+ )
}
}
/**
@@ -2328,39 +2407,43 @@
* @param targetIndex The index where the element should be after moving.
*/
move(index, targetIndex) {
- this._pool?.assertStorageIsWritable();
+ this._pool?.assertStorageIsWritable()
if (targetIndex < 0) {
- throw new Error("targetIndex cannot be less than 0");
+ throw new Error('targetIndex cannot be less than 0')
}
if (targetIndex >= this._items.length) {
throw new Error(
- "targetIndex cannot be greater or equal than the list length"
- );
+ 'targetIndex cannot be greater or equal than the list length'
+ )
}
if (index < 0) {
- throw new Error("index cannot be less than 0");
+ throw new Error('index cannot be less than 0')
}
if (index >= this._items.length) {
- throw new Error("index cannot be greater or equal than the list length");
+ throw new Error('index cannot be greater or equal than the list length')
}
- let beforePosition = null;
- let afterPosition = null;
+ let beforePosition = null
+ let afterPosition = null
if (index < targetIndex) {
- afterPosition = targetIndex === this._items.length - 1 ? void 0 : this._items[targetIndex + 1]._parentPos;
- beforePosition = this._items[targetIndex]._parentPos;
+ afterPosition =
+ targetIndex === this._items.length - 1
+ ? void 0
+ : this._items[targetIndex + 1]._parentPos
+ beforePosition = this._items[targetIndex]._parentPos
} else {
- afterPosition = this._items[targetIndex]._parentPos;
- beforePosition = targetIndex === 0 ? void 0 : this._items[targetIndex - 1]._parentPos;
- }
- const position = makePosition(beforePosition, afterPosition);
- const item = this._items[index];
- const previousPosition = item._getParentKeyOrThrow();
- item._setParentLink(this, position);
- this._sortItems();
+ afterPosition = this._items[targetIndex]._parentPos
+ beforePosition =
+ targetIndex === 0 ? void 0 : this._items[targetIndex - 1]._parentPos
+ }
+ const position = makePosition(beforePosition, afterPosition)
+ const item = this._items[index]
+ const previousPosition = item._getParentKeyOrThrow()
+ item._setParentLink(this, position)
+ this._sortItems()
if (this._pool && this._id) {
const storageUpdates = /* @__PURE__ */ new Map([
[this._id, makeUpdate(this, [moveDelta(index, targetIndex, item)])]
- ]);
+ ])
this._pool.dispatch(
[
{
@@ -2378,7 +2461,7 @@
}
],
storageUpdates
- );
+ )
}
}
/**
@@ -2386,24 +2469,26 @@
* @param index The index of the element to delete
*/
delete(index) {
- this._pool?.assertStorageIsWritable();
+ this._pool?.assertStorageIsWritable()
if (index < 0 || index >= this._items.length) {
throw new Error(
- `Cannot delete list item at index "${index}". index should be between 0 and ${this._items.length - 1}`
- );
+ `Cannot delete list item at index "${index}". index should be between 0 and ${
+ this._items.length - 1
+ }`
+ )
}
- const item = this._items[index];
- item._detach();
- this._items.splice(index, 1);
- this.invalidate();
+ const item = this._items[index]
+ item._detach()
+ this._items.splice(index, 1)
+ this.invalidate()
if (this._pool) {
- const childRecordId = item._id;
+ const childRecordId = item._id
if (childRecordId) {
- const storageUpdates = /* @__PURE__ */ new Map();
+ const storageUpdates = /* @__PURE__ */ new Map()
storageUpdates.set(
nn(this._id),
makeUpdate(this, [deleteDelta(index)])
- );
+ )
this._pool.dispatch(
[
{
@@ -2415,74 +2500,76 @@
],
item._toOps(nn(this._id), item._getParentKeyOrThrow()),
storageUpdates
- );
+ )
}
}
}
clear() {
- this._pool?.assertStorageIsWritable();
+ this._pool?.assertStorageIsWritable()
if (this._pool) {
- const ops = [];
- const reverseOps = [];
- const updateDelta = [];
+ const ops = []
+ const reverseOps = []
+ const updateDelta = []
for (const item of this._items) {
- item._detach();
- const childId = item._id;
+ item._detach()
+ const childId = item._id
if (childId) {
ops.push({
type: 5,
id: childId,
opId: this._pool.generateOpId()
- });
+ })
reverseOps.push(
...item._toOps(nn(this._id), item._getParentKeyOrThrow())
- );
- updateDelta.push(deleteDelta(0));
+ )
+ updateDelta.push(deleteDelta(0))
}
}
- this._items = [];
- this.invalidate();
- const storageUpdates = /* @__PURE__ */ new Map();
- storageUpdates.set(nn(this._id), makeUpdate(this, updateDelta));
- this._pool.dispatch(ops, reverseOps, storageUpdates);
+ this._items = []
+ this.invalidate()
+ const storageUpdates = /* @__PURE__ */ new Map()
+ storageUpdates.set(nn(this._id), makeUpdate(this, updateDelta))
+ this._pool.dispatch(ops, reverseOps, storageUpdates)
} else {
for (const item of this._items) {
- item._detach();
+ item._detach()
}
- this._items = [];
- this.invalidate();
+ this._items = []
+ this.invalidate()
}
}
set(index, item) {
- this._pool?.assertStorageIsWritable();
+ this._pool?.assertStorageIsWritable()
if (index < 0 || index >= this._items.length) {
throw new Error(
- `Cannot set list item at index "${index}". index should be between 0 and ${this._items.length - 1}`
- );
- }
- const existingItem = this._items[index];
- const position = existingItem._getParentKeyOrThrow();
- const existingId = existingItem._id;
- existingItem._detach();
- const value = lsonToLiveNode(item);
- value._setParentLink(this, position);
- this._items[index] = value;
- this.invalidate();
+ `Cannot set list item at index "${index}". index should be between 0 and ${
+ this._items.length - 1
+ }`
+ )
+ }
+ const existingItem = this._items[index]
+ const position = existingItem._getParentKeyOrThrow()
+ const existingId = existingItem._id
+ existingItem._detach()
+ const value = lsonToLiveNode(item)
+ value._setParentLink(this, position)
+ this._items[index] = value
+ this.invalidate()
if (this._pool && this._id) {
- const id = this._pool.generateId();
- value._attach(id, this._pool);
- const storageUpdates = /* @__PURE__ */ new Map();
- storageUpdates.set(this._id, makeUpdate(this, [setDelta(index, value)]));
+ const id = this._pool.generateId()
+ value._attach(id, this._pool)
+ const storageUpdates = /* @__PURE__ */ new Map()
+ storageUpdates.set(this._id, makeUpdate(this, [setDelta(index, value)]))
const ops = HACK_addIntentAndDeletedIdToOperation(
value._toOps(this._id, position, this._pool),
existingId
- );
- this._unacknowledgedSets.set(position, nn(ops[0].opId));
+ )
+ this._unacknowledgedSets.set(position, nn(ops[0].opId))
const reverseOps = HACK_addIntentAndDeletedIdToOperation(
existingItem._toOps(this._id, position, void 0),
id
- );
- this._pool.dispatch(ops, reverseOps, storageUpdates);
+ )
+ this._pool.dispatch(ops, reverseOps, storageUpdates)
}
}
/**
@@ -2490,10 +2577,10 @@
*/
toArray() {
return this._items.map(
- (entry) => liveNodeToLson(entry)
+ entry => liveNodeToLson(entry)
// ^^^^^^^^
// FIXME! This isn't safe.
- );
+ )
}
/**
* Tests whether all elements pass the test implemented by the provided function.
@@ -2501,7 +2588,7 @@
* @returns true if the predicate function returns a truthy value for every element. Otherwise, false.
*/
every(predicate) {
- return this.toArray().every(predicate);
+ return this.toArray().every(predicate)
}
/**
* Creates an array with all elements that pass the test implemented by the provided function.
@@ -2509,7 +2596,7 @@
* @returns An array with the elements that pass the test.
*/
filter(predicate) {
- return this.toArray().filter(predicate);
+ return this.toArray().filter(predicate)
}
/**
* Returns the first element that satisfies the provided testing function.
@@ -2517,7 +2604,7 @@
* @returns The value of the first element in the LiveList that satisfies the provided testing function. Otherwise, undefined is returned.
*/
find(predicate) {
- return this.toArray().find(predicate);
+ return this.toArray().find(predicate)
}
/**
* Returns the index of the first element in the LiveList that satisfies the provided testing function.
@@ -2525,14 +2612,14 @@
* @returns The index of the first element in the LiveList that passes the test. Otherwise, -1.
*/
findIndex(predicate) {
- return this.toArray().findIndex(predicate);
+ return this.toArray().findIndex(predicate)
}
/**
* Executes a provided function once for each element.
* @param callbackfn Function to execute on each element.
*/
forEach(callbackfn) {
- return this.toArray().forEach(callbackfn);
+ return this.toArray().forEach(callbackfn)
}
/**
* Get the element at the specified index.
@@ -2541,9 +2628,9 @@
*/
get(index) {
if (index < 0 || index >= this._items.length) {
- return void 0;
+ return void 0
}
- return liveNodeToLson(this._items[index]);
+ return liveNodeToLson(this._items[index])
}
/**
* Returns the first index at which a given element can be found in the LiveList, or -1 if it is not present.
@@ -2552,7 +2639,7 @@
* @returns The first index of the element in the LiveList; -1 if not found.
*/
indexOf(searchElement, fromIndex) {
- return this.toArray().indexOf(searchElement, fromIndex);
+ return this.toArray().indexOf(searchElement, fromIndex)
}
/**
* Returns the last index at which a given element can be found in the LiveList, or -1 if it is not present. The LiveLsit is searched backwards, starting at fromIndex.
@@ -2561,7 +2648,7 @@
* @returns
*/
lastIndexOf(searchElement, fromIndex) {
- return this.toArray().lastIndexOf(searchElement, fromIndex);
+ return this.toArray().lastIndexOf(searchElement, fromIndex)
}
/**
* Creates an array populated with the results of calling a provided function on every element.
@@ -2569,14 +2656,14 @@
* @returns An array with each element being the result of the callback function.
*/
map(callback) {
- return this._items.map(
- (entry, i) => callback(
+ return this._items.map((entry, i) =>
+ callback(
liveNodeToLson(entry),
// ^^^^^^^^
// FIXME! This isn't safe.
i
)
- );
+ )
}
/**
* Tests whether at least one element in the LiveList passes the test implemented by the provided function.
@@ -2584,134 +2671,136 @@
* @returns true if the callback function returns a truthy value for at least one element. Otherwise, false.
*/
some(predicate) {
- return this.toArray().some(predicate);
+ return this.toArray().some(predicate)
}
[Symbol.iterator]() {
- return new LiveListIterator(this._items);
+ return new LiveListIterator(this._items)
}
/** @internal */
_createAttachItemAndSort(op, key) {
- const newItem = creationOpToLiveNode(op);
- newItem._attach(op.id, nn(this._pool));
- newItem._setParentLink(this, key);
- this._insertAndSort(newItem);
- const newIndex = this._indexOfPosition(key);
- return { newItem, newIndex };
+ const newItem = creationOpToLiveNode(op)
+ newItem._attach(op.id, nn(this._pool))
+ newItem._setParentLink(this, key)
+ this._insertAndSort(newItem)
+ const newIndex = this._indexOfPosition(key)
+ return { newItem, newIndex }
}
/** @internal */
_shiftItemPosition(index, key) {
const shiftedPosition = makePosition(
key,
- this._items.length > index + 1 ? this._items[index + 1]?._parentPos : void 0
- );
- this._items[index]._setParentLink(this, shiftedPosition);
+ this._items.length > index + 1
+ ? this._items[index + 1]?._parentPos
+ : void 0
+ )
+ this._items[index]._setParentLink(this, shiftedPosition)
}
/** @internal */
_toTreeNode(key) {
return {
- type: "LiveList",
+ type: 'LiveList',
id: this._id ?? nanoid(),
key,
- payload: this._items.map(
- (item, index) => item.toTreeNode(index.toString())
+ payload: this._items.map((item, index) =>
+ item.toTreeNode(index.toString())
)
- };
+ }
}
toImmutable() {
- return super.toImmutable();
+ return super.toImmutable()
}
/** @internal */
_toImmutable() {
- const result = this._items.map((node) => node.toImmutable());
- return false ? result : Object.freeze(result);
+ const result = this._items.map(node => node.toImmutable())
+ return false ? result : Object.freeze(result)
}
- };
+ }
var LiveListIterator = class {
constructor(items) {
- this._innerIterator = items[Symbol.iterator]();
+ this._innerIterator = items[Symbol.iterator]()
}
[Symbol.iterator]() {
- return this;
+ return this
}
next() {
- const result = this._innerIterator.next();
+ const result = this._innerIterator.next()
if (result.done) {
return {
done: true,
value: void 0
- };
+ }
}
- const value = liveNodeToLson(result.value);
- return { value };
+ const value = liveNodeToLson(result.value)
+ return { value }
}
- };
+ }
function makeUpdate(liveList, deltaUpdates) {
return {
node: liveList,
- type: "LiveList",
+ type: 'LiveList',
updates: deltaUpdates
- };
+ }
}
function setDelta(index, item) {
return {
index,
- type: "set",
+ type: 'set',
item: item instanceof LiveRegister ? item.data : item
- };
+ }
}
function deleteDelta(index) {
return {
index,
- type: "delete"
- };
+ type: 'delete'
+ }
}
function insertDelta(index, item) {
return {
index,
- type: "insert",
+ type: 'insert',
item: item instanceof LiveRegister ? item.data : item
- };
+ }
}
function moveDelta(previousIndex, index, item) {
return {
index,
- type: "move",
+ type: 'move',
previousIndex,
item: item instanceof LiveRegister ? item.data : item
- };
+ }
}
function HACK_addIntentAndDeletedIdToOperation(ops, deletedId) {
return ops.map((op, index) => {
if (index === 0) {
- const firstOp = op;
+ const firstOp = op
return {
...firstOp,
- intent: "set",
+ intent: 'set',
deletedId
- };
+ }
} else {
- return op;
+ return op
}
- });
+ })
}
- var freeze = false ? (
- /* istanbul ignore next */
- (x) => x
- ) : Object.freeze;
+ var freeze = false
+ ? /* istanbul ignore next */
+ x => x
+ : Object.freeze
var LiveMap = class _LiveMap extends AbstractCrdt {
constructor(entries2) {
- super();
- this.unacknowledgedSet = /* @__PURE__ */ new Map();
+ super()
+ this.unacknowledgedSet = /* @__PURE__ */ new Map()
if (entries2) {
- const mappedEntries = [];
+ const mappedEntries = []
for (const entry of entries2) {
- const value = lsonToLiveNode(entry[1]);
- value._setParentLink(this, entry[0]);
- mappedEntries.push([entry[0], value]);
+ const value = lsonToLiveNode(entry[1])
+ value._setParentLink(this, entry[0])
+ mappedEntries.push([entry[0], value])
}
- this._map = new Map(mappedEntries);
+ this._map = new Map(mappedEntries)
} else {
- this._map = /* @__PURE__ */ new Map();
+ this._map = /* @__PURE__ */ new Map()
}
}
/**
@@ -2719,48 +2808,48 @@
*/
_toOps(parentId, parentKey, pool) {
if (this._id === void 0) {
- throw new Error("Cannot serialize item is not attached");
+ throw new Error('Cannot serialize item is not attached')
}
- const ops = [];
+ const ops = []
const op = {
id: this._id,
opId: pool?.generateOpId(),
type: 7,
parentId,
parentKey
- };
- ops.push(op);
+ }
+ ops.push(op)
for (const [key, value] of this._map) {
- ops.push(...value._toOps(this._id, key, pool));
+ ops.push(...value._toOps(this._id, key, pool))
}
- return ops;
+ return ops
}
/**
* @internal
*/
static _deserialize([id, _item], parentToChildren, pool) {
- const map = new _LiveMap();
- map._attach(id, pool);
- const children = parentToChildren.get(id);
+ const map = new _LiveMap()
+ map._attach(id, pool)
+ const children = parentToChildren.get(id)
if (children === void 0) {
- return map;
+ return map
}
for (const [id2, crdt] of children) {
- const child = deserialize([id2, crdt], parentToChildren, pool);
- child._setParentLink(map, crdt.parentKey);
- map._map.set(crdt.parentKey, child);
- map.invalidate();
+ const child = deserialize([id2, crdt], parentToChildren, pool)
+ child._setParentLink(map, crdt.parentKey)
+ map._map.set(crdt.parentKey, child)
+ map.invalidate()
}
- return map;
+ return map
}
/**
* @internal
*/
_attach(id, pool) {
- super._attach(id, pool);
+ super._attach(id, pool)
for (const [_key, value] of this._map) {
if (isLiveNode(value)) {
- value._attach(pool.generateId(), pool);
+ value._attach(pool.generateId(), pool)
}
}
}
@@ -2769,89 +2858,89 @@
*/
_attachChild(op, source) {
if (this._pool === void 0) {
- throw new Error("Can't attach child if managed pool is not present");
+ throw new Error("Can't attach child if managed pool is not present")
}
- const { id, parentKey, opId } = op;
- const key = parentKey;
- const child = creationOpToLiveNode(op);
+ const { id, parentKey, opId } = op
+ const key = parentKey
+ const child = creationOpToLiveNode(op)
if (this._pool.getNode(id) !== void 0) {
- return { modified: false };
+ return { modified: false }
}
if (source === 2) {
- const lastUpdateOpId = this.unacknowledgedSet.get(key);
+ const lastUpdateOpId = this.unacknowledgedSet.get(key)
if (lastUpdateOpId === opId) {
- this.unacknowledgedSet.delete(key);
- return { modified: false };
+ this.unacknowledgedSet.delete(key)
+ return { modified: false }
} else if (lastUpdateOpId !== void 0) {
- return { modified: false };
+ return { modified: false }
}
} else if (source === 1) {
- this.unacknowledgedSet.delete(key);
+ this.unacknowledgedSet.delete(key)
}
- const previousValue = this._map.get(key);
- let reverse;
+ const previousValue = this._map.get(key)
+ let reverse
if (previousValue) {
- const thisId = nn(this._id);
- reverse = previousValue._toOps(thisId, key);
- previousValue._detach();
+ const thisId = nn(this._id)
+ reverse = previousValue._toOps(thisId, key)
+ previousValue._detach()
} else {
- reverse = [{ type: 5, id }];
+ reverse = [{ type: 5, id }]
}
- child._setParentLink(this, key);
- child._attach(id, this._pool);
- this._map.set(key, child);
- this.invalidate();
+ child._setParentLink(this, key)
+ child._attach(id, this._pool)
+ this._map.set(key, child)
+ this.invalidate()
return {
modified: {
node: this,
- type: "LiveMap",
- updates: { [key]: { type: "update" } }
+ type: 'LiveMap',
+ updates: { [key]: { type: 'update' } }
},
reverse
- };
+ }
}
/**
* @internal
*/
_detach() {
- super._detach();
+ super._detach()
for (const item of this._map.values()) {
- item._detach();
+ item._detach()
}
}
/**
* @internal
*/
_detachChild(child) {
- const id = nn(this._id);
- const parentKey = nn(child._parentKey);
- const reverse = child._toOps(id, parentKey, this._pool);
+ const id = nn(this._id)
+ const parentKey = nn(child._parentKey)
+ const reverse = child._toOps(id, parentKey, this._pool)
for (const [key, value] of this._map) {
if (value === child) {
- this._map.delete(key);
- this.invalidate();
+ this._map.delete(key)
+ this.invalidate()
}
}
- child._detach();
+ child._detach()
const storageUpdate = {
node: this,
- type: "LiveMap",
- updates: { [parentKey]: { type: "delete" } }
- };
- return { modified: storageUpdate, reverse };
+ type: 'LiveMap',
+ updates: { [parentKey]: { type: 'delete' } }
+ }
+ return { modified: storageUpdate, reverse }
}
/**
* @internal
*/
_serialize() {
- if (this.parent.type !== "HasParent") {
- throw new Error("Cannot serialize LiveMap if parent is missing");
+ if (this.parent.type !== 'HasParent') {
+ throw new Error('Cannot serialize LiveMap if parent is missing')
}
return {
type: 2,
- parentId: nn(this.parent.node._id, "Parent node expected to have ID"),
+ parentId: nn(this.parent.node._id, 'Parent node expected to have ID'),
parentKey: this.parent.key
- };
+ }
}
/**
* Returns a specified element from the LiveMap.
@@ -2859,11 +2948,11 @@
* @returns The element associated with the specified key, or undefined if the key can't be found in the LiveMap.
*/
get(key) {
- const value = this._map.get(key);
+ const value = this._map.get(key)
if (value === void 0) {
- return void 0;
+ return void 0
}
- return liveNodeToLson(value);
+ return liveNodeToLson(value)
}
/**
* Adds or updates an element with a specified key and a value.
@@ -2871,45 +2960,45 @@
* @param value The value of the element to add. Should be serializable to JSON.
*/
set(key, value) {
- this._pool?.assertStorageIsWritable();
- const oldValue = this._map.get(key);
+ this._pool?.assertStorageIsWritable()
+ const oldValue = this._map.get(key)
if (oldValue) {
- oldValue._detach();
+ oldValue._detach()
}
- const item = lsonToLiveNode(value);
- item._setParentLink(this, key);
- this._map.set(key, item);
- this.invalidate();
+ const item = lsonToLiveNode(value)
+ item._setParentLink(this, key)
+ this._map.set(key, item)
+ this.invalidate()
if (this._pool && this._id) {
- const id = this._pool.generateId();
- item._attach(id, this._pool);
- const storageUpdates = /* @__PURE__ */ new Map();
+ const id = this._pool.generateId()
+ item._attach(id, this._pool)
+ const storageUpdates = /* @__PURE__ */ new Map()
storageUpdates.set(this._id, {
node: this,
- type: "LiveMap",
- updates: { [key]: { type: "update" } }
- });
- const ops = item._toOps(this._id, key, this._pool);
- this.unacknowledgedSet.set(key, nn(ops[0].opId));
+ type: 'LiveMap',
+ updates: { [key]: { type: 'update' } }
+ })
+ const ops = item._toOps(this._id, key, this._pool)
+ this.unacknowledgedSet.set(key, nn(ops[0].opId))
this._pool.dispatch(
item._toOps(this._id, key, this._pool),
oldValue ? oldValue._toOps(this._id, key) : [{ type: 5, id }],
storageUpdates
- );
+ )
}
}
/**
* Returns the number of elements in the LiveMap.
*/
get size() {
- return this._map.size;
+ return this._map.size
}
/**
* Returns a boolean indicating whether an element with the specified key exists or not.
* @param key The key of the element to test for presence.
*/
has(key) {
- return this._map.has(key);
+ return this._map.has(key)
}
/**
* Removes the specified element by key.
@@ -2917,22 +3006,22 @@
* @returns true if an element existed and has been removed, or false if the element does not exist.
*/
delete(key) {
- this._pool?.assertStorageIsWritable();
- const item = this._map.get(key);
+ this._pool?.assertStorageIsWritable()
+ const item = this._map.get(key)
if (item === void 0) {
- return false;
+ return false
}
- item._detach();
- this._map.delete(key);
- this.invalidate();
+ item._detach()
+ this._map.delete(key)
+ this.invalidate()
if (this._pool && item._id) {
- const thisId = nn(this._id);
- const storageUpdates = /* @__PURE__ */ new Map();
+ const thisId = nn(this._id)
+ const storageUpdates = /* @__PURE__ */ new Map()
storageUpdates.set(thisId, {
node: this,
- type: "LiveMap",
- updates: { [key]: { type: "delete" } }
- });
+ type: 'LiveMap',
+ updates: { [key]: { type: 'delete' } }
+ })
this._pool.dispatch(
[
{
@@ -2943,69 +3032,69 @@
],
item._toOps(thisId, key),
storageUpdates
- );
+ )
}
- return true;
+ return true
}
/**
* Returns a new Iterator object that contains the [key, value] pairs for each element.
*/
entries() {
- const innerIterator = this._map.entries();
+ const innerIterator = this._map.entries()
return {
[Symbol.iterator]() {
- return this;
+ return this
},
next() {
- const iteratorValue = innerIterator.next();
+ const iteratorValue = innerIterator.next()
if (iteratorValue.done) {
return {
done: true,
value: void 0
- };
+ }
}
- const entry = iteratorValue.value;
- const key = entry[0];
- const value = liveNodeToLson(iteratorValue.value[1]);
+ const entry = iteratorValue.value
+ const key = entry[0]
+ const value = liveNodeToLson(iteratorValue.value[1])
return {
value: [key, value]
- };
+ }
}
- };
+ }
}
/**
* Same function object as the initial value of the entries method.
*/
[Symbol.iterator]() {
- return this.entries();
+ return this.entries()
}
/**
* Returns a new Iterator object that contains the keys for each element.
*/
keys() {
- return this._map.keys();
+ return this._map.keys()
}
/**
* Returns a new Iterator object that contains the values for each element.
*/
values() {
- const innerIterator = this._map.values();
+ const innerIterator = this._map.values()
return {
[Symbol.iterator]() {
- return this;
+ return this
},
next() {
- const iteratorValue = innerIterator.next();
+ const iteratorValue = innerIterator.next()
if (iteratorValue.done) {
return {
done: true,
value: void 0
- };
+ }
}
- const value = liveNodeToLson(iteratorValue.value);
- return { value };
+ const value = liveNodeToLson(iteratorValue.value)
+ return { value }
}
- };
+ }
}
/**
* Executes a provided function once per each key/value pair in the Map object, in insertion order.
@@ -3013,166 +3102,164 @@
*/
forEach(callback) {
for (const entry of this) {
- callback(entry[1], entry[0], this);
+ callback(entry[1], entry[0], this)
}
}
/** @internal */
_toTreeNode(key) {
return {
- type: "LiveMap",
+ type: 'LiveMap',
id: this._id ?? nanoid(),
key,
- payload: Array.from(this._map.entries()).map(
- ([key2, val]) => val.toTreeNode(key2)
+ payload: Array.from(this._map.entries()).map(([key2, val]) =>
+ val.toTreeNode(key2)
)
- };
+ }
}
toImmutable() {
- return super.toImmutable();
+ return super.toImmutable()
}
/** @internal */
_toImmutable() {
- const result = /* @__PURE__ */ new Map();
+ const result = /* @__PURE__ */ new Map()
for (const [key, value] of this._map) {
- result.set(key, value.toImmutable());
+ result.set(key, value.toImmutable())
}
- return freeze(result);
+ return freeze(result)
}
- };
+ }
var LiveObject = class _LiveObject extends AbstractCrdt {
constructor(obj = {}) {
- super();
- this._propToLastUpdate = /* @__PURE__ */ new Map();
+ super()
+ this._propToLastUpdate = /* @__PURE__ */ new Map()
for (const key in obj) {
- const value = obj[key];
+ const value = obj[key]
if (value === void 0) {
- continue;
+ continue
} else if (isLiveNode(value)) {
- value._setParentLink(this, key);
+ value._setParentLink(this, key)
}
}
- this._map = new Map(Object.entries(obj));
+ this._map = new Map(Object.entries(obj))
}
/** @internal */
static _buildRootAndParentToChildren(items) {
- const parentToChildren = /* @__PURE__ */ new Map();
- let root = null;
+ const parentToChildren = /* @__PURE__ */ new Map()
+ let root = null
for (const [id, crdt] of items) {
if (isRootCrdt(crdt)) {
- root = [id, crdt];
+ root = [id, crdt]
} else {
- const tuple = [id, crdt];
- const children = parentToChildren.get(crdt.parentId);
+ const tuple = [id, crdt]
+ const children = parentToChildren.get(crdt.parentId)
if (children !== void 0) {
- children.push(tuple);
+ children.push(tuple)
} else {
- parentToChildren.set(crdt.parentId, [tuple]);
+ parentToChildren.set(crdt.parentId, [tuple])
}
}
}
if (root === null) {
- throw new Error("Root can't be null");
+ throw new Error("Root can't be null")
}
- return [root, parentToChildren];
+ return [root, parentToChildren]
}
/** @internal */
static _fromItems(items, pool) {
- const [root, parentToChildren] = _LiveObject._buildRootAndParentToChildren(items);
- return _LiveObject._deserialize(
- root,
- parentToChildren,
- pool
- );
+ const [root, parentToChildren] =
+ _LiveObject._buildRootAndParentToChildren(items)
+ return _LiveObject._deserialize(root, parentToChildren, pool)
}
/** @internal */
_toOps(parentId, parentKey, pool) {
if (this._id === void 0) {
- throw new Error("Cannot serialize item is not attached");
- }
- const opId = pool?.generateOpId();
- const ops = [];
- const op = parentId !== void 0 && parentKey !== void 0 ? {
- type: 4,
- id: this._id,
- opId,
- parentId,
- parentKey,
- data: {}
- } : (
- // Root object
- { type: 4, id: this._id, opId, data: {} }
- );
- ops.push(op);
+ throw new Error('Cannot serialize item is not attached')
+ }
+ const opId = pool?.generateOpId()
+ const ops = []
+ const op =
+ parentId !== void 0 && parentKey !== void 0
+ ? {
+ type: 4,
+ id: this._id,
+ opId,
+ parentId,
+ parentKey,
+ data: {}
+ }
+ : // Root object
+ { type: 4, id: this._id, opId, data: {} }
+ ops.push(op)
for (const [key, value] of this._map) {
if (isLiveNode(value)) {
- ops.push(...value._toOps(this._id, key, pool));
+ ops.push(...value._toOps(this._id, key, pool))
} else {
- op.data[key] = value;
+ op.data[key] = value
}
}
- return ops;
+ return ops
}
/** @internal */
static _deserialize([id, item], parentToChildren, pool) {
- const liveObj = new _LiveObject(item.data);
- liveObj._attach(id, pool);
- return this._deserializeChildren(liveObj, parentToChildren, pool);
+ const liveObj = new _LiveObject(item.data)
+ liveObj._attach(id, pool)
+ return this._deserializeChildren(liveObj, parentToChildren, pool)
}
/** @internal */
static _deserializeChildren(liveObj, parentToChildren, pool) {
- const children = parentToChildren.get(nn(liveObj._id));
+ const children = parentToChildren.get(nn(liveObj._id))
if (children === void 0) {
- return liveObj;
+ return liveObj
}
for (const [id, crdt] of children) {
- const child = deserializeToLson([id, crdt], parentToChildren, pool);
+ const child = deserializeToLson([id, crdt], parentToChildren, pool)
if (isLiveStructure(child)) {
- child._setParentLink(liveObj, crdt.parentKey);
+ child._setParentLink(liveObj, crdt.parentKey)
}
- liveObj._map.set(crdt.parentKey, child);
- liveObj.invalidate();
+ liveObj._map.set(crdt.parentKey, child)
+ liveObj.invalidate()
}
- return liveObj;
+ return liveObj
}
/** @internal */
_attach(id, pool) {
- super._attach(id, pool);
+ super._attach(id, pool)
for (const [_key, value] of this._map) {
if (isLiveNode(value)) {
- value._attach(pool.generateId(), pool);
+ value._attach(pool.generateId(), pool)
}
}
}
/** @internal */
_attachChild(op, source) {
if (this._pool === void 0) {
- throw new Error("Can't attach child if managed pool is not present");
+ throw new Error("Can't attach child if managed pool is not present")
}
- const { id, opId, parentKey: key } = op;
- const child = creationOpToLson(op);
+ const { id, opId, parentKey: key } = op
+ const child = creationOpToLson(op)
if (this._pool.getNode(id) !== void 0) {
if (this._propToLastUpdate.get(key) === opId) {
- this._propToLastUpdate.delete(key);
+ this._propToLastUpdate.delete(key)
}
- return { modified: false };
+ return { modified: false }
}
if (source === 0) {
- this._propToLastUpdate.set(key, nn(opId));
+ this._propToLastUpdate.set(key, nn(opId))
} else if (this._propToLastUpdate.get(key) === void 0) {
} else if (this._propToLastUpdate.get(key) === opId) {
- this._propToLastUpdate.delete(key);
- return { modified: false };
+ this._propToLastUpdate.delete(key)
+ return { modified: false }
} else {
- return { modified: false };
+ return { modified: false }
}
- const thisId = nn(this._id);
- const previousValue = this._map.get(key);
- let reverse;
+ const thisId = nn(this._id)
+ const previousValue = this._map.get(key)
+ let reverse
if (isLiveNode(previousValue)) {
- reverse = previousValue._toOps(thisId, key);
- previousValue._detach();
+ reverse = previousValue._toOps(thisId, key)
+ previousValue._detach()
} else if (previousValue === void 0) {
- reverse = [{ type: 6, id: thisId, key }];
+ reverse = [{ type: 6, id: thisId, key }]
} else {
reverse = [
{
@@ -3180,164 +3267,166 @@
id: thisId,
data: { [key]: previousValue }
}
- ];
+ ]
}
- this._map.set(key, child);
- this.invalidate();
+ this._map.set(key, child)
+ this.invalidate()
if (isLiveStructure(child)) {
- child._setParentLink(this, key);
- child._attach(id, this._pool);
+ child._setParentLink(this, key)
+ child._attach(id, this._pool)
}
return {
reverse,
modified: {
node: this,
- type: "LiveObject",
- updates: { [key]: { type: "update" } }
+ type: 'LiveObject',
+ updates: { [key]: { type: 'update' } }
}
- };
+ }
}
/** @internal */
_detachChild(child) {
if (child) {
- const id = nn(this._id);
- const parentKey = nn(child._parentKey);
- const reverse = child._toOps(id, parentKey, this._pool);
+ const id = nn(this._id)
+ const parentKey = nn(child._parentKey)
+ const reverse = child._toOps(id, parentKey, this._pool)
for (const [key, value] of this._map) {
if (value === child) {
- this._map.delete(key);
- this.invalidate();
+ this._map.delete(key)
+ this.invalidate()
}
}
- child._detach();
+ child._detach()
const storageUpdate = {
node: this,
- type: "LiveObject",
+ type: 'LiveObject',
updates: {
- [parentKey]: { type: "delete" }
+ [parentKey]: { type: 'delete' }
}
- };
- return { modified: storageUpdate, reverse };
+ }
+ return { modified: storageUpdate, reverse }
}
- return { modified: false };
+ return { modified: false }
}
/**
* @internal
*/
_detach() {
- super._detach();
+ super._detach()
for (const value of this._map.values()) {
if (isLiveNode(value)) {
- value._detach();
+ value._detach()
}
}
}
/** @internal */
_apply(op, isLocal) {
if (op.type === 3) {
- return this._applyUpdate(op, isLocal);
+ return this._applyUpdate(op, isLocal)
} else if (op.type === 6) {
- return this._applyDeleteObjectKey(op, isLocal);
+ return this._applyDeleteObjectKey(op, isLocal)
}
- return super._apply(op, isLocal);
+ return super._apply(op, isLocal)
}
/**
* @internal
*/
_serialize() {
- const data = {};
+ const data = {}
for (const [key, value] of this._map) {
if (!isLiveNode(value)) {
- data[key] = value;
+ data[key] = value
}
}
- if (this.parent.type === "HasParent" && this.parent.node._id) {
+ if (this.parent.type === 'HasParent' && this.parent.node._id) {
return {
type: 0,
parentId: this.parent.node._id,
parentKey: this.parent.key,
data
- };
+ }
} else {
return {
type: 0,
data
- };
+ }
}
}
/** @internal */
_applyUpdate(op, isLocal) {
- let isModified = false;
- const id = nn(this._id);
- const reverse = [];
+ let isModified = false
+ const id = nn(this._id)
+ const reverse = []
const reverseUpdate = {
type: 3,
id,
data: {}
- };
+ }
for (const key in op.data) {
- const oldValue = this._map.get(key);
+ const oldValue = this._map.get(key)
if (isLiveNode(oldValue)) {
- reverse.push(...oldValue._toOps(id, key));
- oldValue._detach();
+ reverse.push(...oldValue._toOps(id, key))
+ oldValue._detach()
} else if (oldValue !== void 0) {
- reverseUpdate.data[key] = oldValue;
+ reverseUpdate.data[key] = oldValue
} else if (oldValue === void 0) {
- reverse.push({ type: 6, id, key });
+ reverse.push({ type: 6, id, key })
}
}
- const updateDelta = {};
+ const updateDelta = {}
for (const key in op.data) {
- const value = op.data[key];
+ const value = op.data[key]
if (value === void 0) {
- continue;
+ continue
}
if (isLocal) {
- this._propToLastUpdate.set(key, nn(op.opId));
+ this._propToLastUpdate.set(key, nn(op.opId))
} else if (this._propToLastUpdate.get(key) === void 0) {
- isModified = true;
+ isModified = true
} else if (this._propToLastUpdate.get(key) === op.opId) {
- this._propToLastUpdate.delete(key);
- continue;
+ this._propToLastUpdate.delete(key)
+ continue
} else {
- continue;
+ continue
}
- const oldValue = this._map.get(key);
+ const oldValue = this._map.get(key)
if (isLiveNode(oldValue)) {
- oldValue._detach();
+ oldValue._detach()
}
- isModified = true;
- updateDelta[key] = { type: "update" };
- this._map.set(key, value);
- this.invalidate();
+ isModified = true
+ updateDelta[key] = { type: 'update' }
+ this._map.set(key, value)
+ this.invalidate()
}
if (Object.keys(reverseUpdate.data).length !== 0) {
- reverse.unshift(reverseUpdate);
- }
- return isModified ? {
- modified: {
- node: this,
- type: "LiveObject",
- updates: updateDelta
- },
- reverse
- } : { modified: false };
+ reverse.unshift(reverseUpdate)
+ }
+ return isModified
+ ? {
+ modified: {
+ node: this,
+ type: 'LiveObject',
+ updates: updateDelta
+ },
+ reverse
+ }
+ : { modified: false }
}
/** @internal */
_applyDeleteObjectKey(op, isLocal) {
- const key = op.key;
+ const key = op.key
if (this._map.has(key) === false) {
- return { modified: false };
+ return { modified: false }
}
if (!isLocal && this._propToLastUpdate.get(key) !== void 0) {
- return { modified: false };
+ return { modified: false }
}
- const oldValue = this._map.get(key);
- const id = nn(this._id);
- let reverse = [];
+ const oldValue = this._map.get(key)
+ const id = nn(this._id)
+ let reverse = []
if (isLiveNode(oldValue)) {
- reverse = oldValue._toOps(id, op.key);
- oldValue._detach();
+ reverse = oldValue._toOps(id, op.key)
+ oldValue._detach()
} else if (oldValue !== void 0) {
reverse = [
{
@@ -3345,24 +3434,24 @@
id,
data: { [key]: oldValue }
}
- ];
+ ]
}
- this._map.delete(key);
- this.invalidate();
+ this._map.delete(key)
+ this.invalidate()
return {
modified: {
node: this,
- type: "LiveObject",
- updates: { [op.key]: { type: "delete" } }
+ type: 'LiveObject',
+ updates: { [op.key]: { type: 'delete' } }
},
reverse
- };
+ }
}
/**
* Transform the LiveObject into a javascript object
*/
toObject() {
- return Object.fromEntries(this._map);
+ return Object.fromEntries(this._map)
}
/**
* Adds or updates a property with a specified key and a value.
@@ -3370,39 +3459,39 @@
* @param value The value of the property to add
*/
set(key, value) {
- this._pool?.assertStorageIsWritable();
- this.update({ [key]: value });
+ this._pool?.assertStorageIsWritable()
+ this.update({ [key]: value })
}
/**
* Returns a specified property from the LiveObject.
* @param key The key of the property to get
*/
get(key) {
- return this._map.get(key);
+ return this._map.get(key)
}
/**
* Deletes a key from the LiveObject
* @param key The key of the property to delete
*/
delete(key) {
- this._pool?.assertStorageIsWritable();
- const keyAsString = key;
- const oldValue = this._map.get(keyAsString);
+ this._pool?.assertStorageIsWritable()
+ const keyAsString = key
+ const oldValue = this._map.get(keyAsString)
if (oldValue === void 0) {
- return;
+ return
}
if (this._pool === void 0 || this._id === void 0) {
if (isLiveNode(oldValue)) {
- oldValue._detach();
+ oldValue._detach()
}
- this._map.delete(keyAsString);
- this.invalidate();
- return;
+ this._map.delete(keyAsString)
+ this.invalidate()
+ return
}
- let reverse;
+ let reverse
if (isLiveNode(oldValue)) {
- oldValue._detach();
- reverse = oldValue._toOps(this._id, keyAsString);
+ oldValue._detach()
+ reverse = oldValue._toOps(this._id, keyAsString)
} else {
reverse = [
{
@@ -3410,16 +3499,16 @@
data: { [keyAsString]: oldValue },
id: this._id
}
- ];
+ ]
}
- this._map.delete(keyAsString);
- this.invalidate();
- const storageUpdates = /* @__PURE__ */ new Map();
+ this._map.delete(keyAsString)
+ this.invalidate()
+ const storageUpdates = /* @__PURE__ */ new Map()
storageUpdates.set(this._id, {
node: this,
- type: "LiveObject",
- updates: { [key]: { type: "delete" } }
- });
+ type: 'LiveObject',
+ updates: { [key]: { type: 'delete' } }
+ })
this._pool.dispatch(
[
{
@@ -3431,77 +3520,77 @@
],
reverse,
storageUpdates
- );
+ )
}
/**
* Adds or updates multiple properties at once with an object.
* @param patch The object used to overrides properties
*/
update(patch) {
- this._pool?.assertStorageIsWritable();
+ this._pool?.assertStorageIsWritable()
if (this._pool === void 0 || this._id === void 0) {
for (const key in patch) {
- const newValue = patch[key];
+ const newValue = patch[key]
if (newValue === void 0) {
- continue;
+ continue
}
- const oldValue = this._map.get(key);
+ const oldValue = this._map.get(key)
if (isLiveNode(oldValue)) {
- oldValue._detach();
+ oldValue._detach()
}
if (isLiveNode(newValue)) {
- newValue._setParentLink(this, key);
+ newValue._setParentLink(this, key)
}
- this._map.set(key, newValue);
- this.invalidate();
+ this._map.set(key, newValue)
+ this.invalidate()
}
- return;
+ return
}
- const ops = [];
- const reverseOps = [];
- const opId = this._pool.generateOpId();
- const updatedProps = {};
+ const ops = []
+ const reverseOps = []
+ const opId = this._pool.generateOpId()
+ const updatedProps = {}
const reverseUpdateOp = {
id: this._id,
type: 3,
data: {}
- };
- const updateDelta = {};
+ }
+ const updateDelta = {}
for (const key in patch) {
- const newValue = patch[key];
+ const newValue = patch[key]
if (newValue === void 0) {
- continue;
+ continue
}
- const oldValue = this._map.get(key);
+ const oldValue = this._map.get(key)
if (isLiveNode(oldValue)) {
- reverseOps.push(...oldValue._toOps(this._id, key));
- oldValue._detach();
+ reverseOps.push(...oldValue._toOps(this._id, key))
+ oldValue._detach()
} else if (oldValue === void 0) {
- reverseOps.push({ type: 6, id: this._id, key });
+ reverseOps.push({ type: 6, id: this._id, key })
} else {
- reverseUpdateOp.data[key] = oldValue;
+ reverseUpdateOp.data[key] = oldValue
}
if (isLiveNode(newValue)) {
- newValue._setParentLink(this, key);
- newValue._attach(this._pool.generateId(), this._pool);
- const newAttachChildOps = newValue._toOps(this._id, key, this._pool);
+ newValue._setParentLink(this, key)
+ newValue._attach(this._pool.generateId(), this._pool)
+ const newAttachChildOps = newValue._toOps(this._id, key, this._pool)
const createCrdtOp = newAttachChildOps.find(
- (op) => op.parentId === this._id
- );
+ op => op.parentId === this._id
+ )
if (createCrdtOp) {
- this._propToLastUpdate.set(key, nn(createCrdtOp.opId));
+ this._propToLastUpdate.set(key, nn(createCrdtOp.opId))
}
- ops.push(...newAttachChildOps);
+ ops.push(...newAttachChildOps)
} else {
- updatedProps[key] = newValue;
- this._propToLastUpdate.set(key, opId);
+ updatedProps[key] = newValue
+ this._propToLastUpdate.set(key, opId)
}
- this._map.set(key, newValue);
- this.invalidate();
- updateDelta[key] = { type: "update" };
+ this._map.set(key, newValue)
+ this.invalidate()
+ updateDelta[key] = { type: 'update' }
}
if (Object.keys(reverseUpdateOp.data).length !== 0) {
- reverseOps.unshift(reverseUpdateOp);
+ reverseOps.unshift(reverseUpdateOp)
}
if (Object.keys(updatedProps).length !== 0) {
ops.unshift({
@@ -3509,170 +3598,188 @@
id: this._id,
type: 3,
data: updatedProps
- });
+ })
}
- const storageUpdates = /* @__PURE__ */ new Map();
+ const storageUpdates = /* @__PURE__ */ new Map()
storageUpdates.set(this._id, {
node: this,
- type: "LiveObject",
+ type: 'LiveObject',
updates: updateDelta
- });
- this._pool.dispatch(ops, reverseOps, storageUpdates);
+ })
+ this._pool.dispatch(ops, reverseOps, storageUpdates)
}
toImmutable() {
- return super.toImmutable();
+ return super.toImmutable()
}
/** @internal */
toTreeNode(key) {
- return super.toTreeNode(key);
+ return super.toTreeNode(key)
}
/** @internal */
_toTreeNode(key) {
- const nodeId = this._id ?? nanoid();
+ const nodeId = this._id ?? nanoid()
return {
- type: "LiveObject",
+ type: 'LiveObject',
id: nodeId,
key,
- payload: Array.from(this._map.entries()).map(
- ([key2, value]) => isLiveNode(value) ? value.toTreeNode(key2) : { type: "Json", id: `${nodeId}:${key2}`, key: key2, payload: value }
+ payload: Array.from(this._map.entries()).map(([key2, value]) =>
+ isLiveNode(value)
+ ? value.toTreeNode(key2)
+ : {
+ type: 'Json',
+ id: `${nodeId}:${key2}`,
+ key: key2,
+ payload: value
+ }
)
- };
+ }
}
/** @internal */
_toImmutable() {
- const result = {};
+ const result = {}
for (const [key, val] of this._map) {
- result[key] = isLiveStructure(val) ? val.toImmutable() : val;
+ result[key] = isLiveStructure(val) ? val.toImmutable() : val
}
- return false ? result : Object.freeze(result);
+ return false ? result : Object.freeze(result)
}
- };
+ }
function creationOpToLiveNode(op) {
- return lsonToLiveNode(creationOpToLson(op));
+ return lsonToLiveNode(creationOpToLson(op))
}
function creationOpToLson(op) {
switch (op.type) {
case 8:
- return op.data;
+ return op.data
case 4:
- return new LiveObject(op.data);
+ return new LiveObject(op.data)
case 7:
- return new LiveMap();
+ return new LiveMap()
case 2:
- return new LiveList();
+ return new LiveList()
default:
- return assertNever(op, "Unknown creation Op");
+ return assertNever(op, 'Unknown creation Op')
}
}
function isSameNodeOrChildOf(node, parent) {
if (node === parent) {
- return true;
+ return true
}
- if (node.parent.type === "HasParent") {
- return isSameNodeOrChildOf(node.parent.node, parent);
+ if (node.parent.type === 'HasParent') {
+ return isSameNodeOrChildOf(node.parent.node, parent)
}
- return false;
+ return false
}
function deserialize([id, crdt], parentToChildren, pool) {
switch (crdt.type) {
case 0: {
- return LiveObject._deserialize([id, crdt], parentToChildren, pool);
+ return LiveObject._deserialize([id, crdt], parentToChildren, pool)
}
case 1: {
- return LiveList._deserialize([id, crdt], parentToChildren, pool);
+ return LiveList._deserialize([id, crdt], parentToChildren, pool)
}
case 2: {
- return LiveMap._deserialize([id, crdt], parentToChildren, pool);
+ return LiveMap._deserialize([id, crdt], parentToChildren, pool)
}
case 3: {
- return LiveRegister._deserialize([id, crdt], parentToChildren, pool);
+ return LiveRegister._deserialize([id, crdt], parentToChildren, pool)
}
default: {
- throw new Error("Unexpected CRDT type");
+ throw new Error('Unexpected CRDT type')
}
}
}
function deserializeToLson([id, crdt], parentToChildren, pool) {
switch (crdt.type) {
case 0: {
- return LiveObject._deserialize([id, crdt], parentToChildren, pool);
+ return LiveObject._deserialize([id, crdt], parentToChildren, pool)
}
case 1: {
- return LiveList._deserialize([id, crdt], parentToChildren, pool);
+ return LiveList._deserialize([id, crdt], parentToChildren, pool)
}
case 2: {
- return LiveMap._deserialize([id, crdt], parentToChildren, pool);
+ return LiveMap._deserialize([id, crdt], parentToChildren, pool)
}
case 3: {
- return crdt.data;
+ return crdt.data
}
default: {
- throw new Error("Unexpected CRDT type");
+ throw new Error('Unexpected CRDT type')
}
}
}
function isLiveStructure(value) {
- return isLiveList(value) || isLiveMap(value) || isLiveObject(value);
+ return isLiveList(value) || isLiveMap(value) || isLiveObject(value)
}
function isLiveNode(value) {
- return isLiveStructure(value) || isLiveRegister(value);
+ return isLiveStructure(value) || isLiveRegister(value)
}
function isLiveList(value) {
- return value instanceof LiveList;
+ return value instanceof LiveList
}
function isLiveMap(value) {
- return value instanceof LiveMap;
+ return value instanceof LiveMap
}
function isLiveObject(value) {
- return value instanceof LiveObject;
+ return value instanceof LiveObject
}
function isLiveRegister(value) {
- return value instanceof LiveRegister;
+ return value instanceof LiveRegister
}
function liveNodeToLson(obj) {
if (obj instanceof LiveRegister) {
- return obj.data;
- } else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject) {
- return obj;
+ return obj.data
+ } else if (
+ obj instanceof LiveList ||
+ obj instanceof LiveMap ||
+ obj instanceof LiveObject
+ ) {
+ return obj
} else {
- return assertNever(obj, "Unknown AbstractCrdt");
+ return assertNever(obj, 'Unknown AbstractCrdt')
}
}
function lsonToLiveNode(value) {
- if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList) {
- return value;
+ if (
+ value instanceof LiveObject ||
+ value instanceof LiveMap ||
+ value instanceof LiveList
+ ) {
+ return value
} else {
- return new LiveRegister(value);
+ return new LiveRegister(value)
}
}
function getTreesDiffOperations(currentItems, newItems) {
- const ops = [];
+ const ops = []
currentItems.forEach((_, id) => {
if (!newItems.get(id)) {
ops.push({
type: 5,
id
- });
+ })
}
- });
+ })
newItems.forEach((crdt, id) => {
- const currentCrdt = currentItems.get(id);
+ const currentCrdt = currentItems.get(id)
if (currentCrdt) {
if (crdt.type === 0) {
- if (currentCrdt.type !== 0 || JSON.stringify(crdt.data) !== JSON.stringify(currentCrdt.data)) {
+ if (
+ currentCrdt.type !== 0 ||
+ JSON.stringify(crdt.data) !== JSON.stringify(currentCrdt.data)
+ ) {
ops.push({
type: 3,
id,
data: crdt.data
- });
+ })
}
}
if (crdt.parentKey !== currentCrdt.parentKey) {
ops.push({
type: 1,
id,
- parentKey: nn(crdt.parentKey, "Parent key must not be missing")
- });
+ parentKey: nn(crdt.parentKey, 'Parent key must not be missing')
+ })
}
} else {
switch (crdt.type) {
@@ -3683,223 +3790,233 @@
parentId: crdt.parentId,
parentKey: crdt.parentKey,
data: crdt.data
- });
- break;
+ })
+ break
case 1:
ops.push({
type: 2,
id,
parentId: crdt.parentId,
parentKey: crdt.parentKey
- });
- break;
+ })
+ break
case 0:
ops.push(
- crdt.parentId ? {
- type: 4,
- id,
- parentId: crdt.parentId,
- parentKey: crdt.parentKey,
- data: crdt.data
- } : (
- // Root object
- { type: 4, id, data: crdt.data }
- )
- );
- break;
+ crdt.parentId
+ ? {
+ type: 4,
+ id,
+ parentId: crdt.parentId,
+ parentKey: crdt.parentKey,
+ data: crdt.data
+ }
+ : // Root object
+ { type: 4, id, data: crdt.data }
+ )
+ break
case 2:
ops.push({
type: 7,
id,
parentId: crdt.parentId,
parentKey: crdt.parentKey
- });
- break;
+ })
+ break
}
}
- });
- return ops;
+ })
+ return ops
}
function mergeObjectStorageUpdates(first, second) {
- const updates = first.updates;
+ const updates = first.updates
for (const [key, value] of entries(second.updates)) {
- updates[key] = value;
+ updates[key] = value
}
return {
...second,
updates
- };
+ }
}
function mergeMapStorageUpdates(first, second) {
- const updates = first.updates;
+ const updates = first.updates
for (const [key, value] of entries(second.updates)) {
- updates[key] = value;
+ updates[key] = value
}
return {
...second,
updates
- };
+ }
}
function mergeListStorageUpdates(first, second) {
- const updates = first.updates;
+ const updates = first.updates
return {
...second,
updates: updates.concat(second.updates)
- };
+ }
}
function mergeStorageUpdates(first, second) {
if (first === void 0) {
- return second;
- }
- if (first.type === "LiveObject" && second.type === "LiveObject") {
- return mergeObjectStorageUpdates(first, second);
- } else if (first.type === "LiveMap" && second.type === "LiveMap") {
- return mergeMapStorageUpdates(first, second);
- } else if (first.type === "LiveList" && second.type === "LiveList") {
- return mergeListStorageUpdates(first, second);
+ return second
+ }
+ if (first.type === 'LiveObject' && second.type === 'LiveObject') {
+ return mergeObjectStorageUpdates(first, second)
+ } else if (first.type === 'LiveMap' && second.type === 'LiveMap') {
+ return mergeMapStorageUpdates(first, second)
+ } else if (first.type === 'LiveList' && second.type === 'LiveList') {
+ return mergeListStorageUpdates(first, second)
} else {
}
- return second;
+ return second
}
function captureStackTrace(msg, traceRoot) {
- const errorLike = { name: msg };
- if (typeof Error.captureStackTrace !== "function") {
- return void 0;
+ const errorLike = { name: msg }
+ if (typeof Error.captureStackTrace !== 'function') {
+ return void 0
}
- Error.captureStackTrace(errorLike, traceRoot);
- return errorLike.stack;
+ Error.captureStackTrace(errorLike, traceRoot)
+ return errorLike.stack
}
function isJsonScalar(data) {
- return data === null || typeof data === "string" || typeof data === "number" || typeof data === "boolean";
+ return (
+ data === null ||
+ typeof data === 'string' ||
+ typeof data === 'number' ||
+ typeof data === 'boolean'
+ )
}
function isJsonArray(data) {
- return Array.isArray(data);
+ return Array.isArray(data)
}
function isJsonObject(data) {
- return !isJsonScalar(data) && !isJsonArray(data);
+ return !isJsonScalar(data) && !isJsonArray(data)
}
function isStringList(value) {
- return Array.isArray(value) && value.every((i) => typeof i === "string");
+ return Array.isArray(value) && value.every(i => typeof i === 'string')
}
function isMinimalTokenPayload(data) {
- return isPlainObject(data) && typeof data.actor === "number" && (data.id === void 0 || typeof data.id === "string") && isStringList(data.scopes);
+ return (
+ isPlainObject(data) &&
+ typeof data.actor === 'number' &&
+ (data.id === void 0 || typeof data.id === 'string') &&
+ isStringList(data.scopes)
+ )
}
function parseAuthToken(rawTokenString) {
- const tokenParts = rawTokenString.split(".");
+ const tokenParts = rawTokenString.split('.')
if (tokenParts.length !== 3) {
- throw new Error("Authentication error: invalid JWT token");
+ throw new Error('Authentication error: invalid JWT token')
}
- const payload = tryParseJson(b64decode(tokenParts[1]));
+ const payload = tryParseJson(b64decode(tokenParts[1]))
if (!(payload && isMinimalTokenPayload(payload))) {
throw new Error(
- "Authentication error: we expected a room token but did not get one. Hint: if you are using a callback, ensure the room is passed when creating the token. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientCallback"
- );
+ 'Authentication error: we expected a room token but did not get one. Hint: if you are using a callback, ensure the room is passed when creating the token. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientCallback'
+ )
}
return {
raw: rawTokenString,
parsed: payload
- };
+ }
}
function asArrayWithLegacyMethods(arr) {
- Object.defineProperty(arr, "count", {
+ Object.defineProperty(arr, 'count', {
value: arr.length,
enumerable: false
- });
- Object.defineProperty(arr, "toArray", {
+ })
+ Object.defineProperty(arr, 'toArray', {
value: () => arr,
enumerable: false
- });
- return freeze(arr);
+ })
+ return freeze(arr)
}
function merge(target, patch) {
- let updated = false;
- const newValue = { ...target };
- Object.keys(patch).forEach((k) => {
- const key = k;
- const val = patch[key];
+ let updated = false
+ const newValue = { ...target }
+ Object.keys(patch).forEach(k => {
+ const key = k
+ const val = patch[key]
if (newValue[key] !== val) {
if (val === void 0) {
- delete newValue[key];
+ delete newValue[key]
} else {
- newValue[key] = val;
+ newValue[key] = val
}
- updated = true;
+ updated = true
}
- });
- return updated ? newValue : target;
+ })
+ return updated ? newValue : target
}
var ImmutableRef = class {
constructor() {
- this._ev = makeEventSource();
+ this._ev = makeEventSource()
}
get didInvalidate() {
- return this._ev.observable;
+ return this._ev.observable
}
invalidate() {
if (this._cache !== void 0) {
- this._cache = void 0;
- this._ev.notify();
+ this._cache = void 0
+ this._ev.notify()
}
}
get current() {
- return this._cache ?? (this._cache = this._toImmutable());
+ return this._cache ?? (this._cache = this._toImmutable())
}
- };
+ }
function makeUser(conn, presence) {
- return freeze(compactObject({ ...conn, presence }));
+ return freeze(compactObject({ ...conn, presence }))
}
var OthersRef = class extends ImmutableRef {
//
// --------------------------------------------------------------
//
constructor() {
- super();
- this._connections = {};
- this._presences = {};
- this._users = {};
+ super()
+ this._connections = {}
+ this._presences = {}
+ this._users = {}
}
/** @internal */
_toImmutable() {
const users = compact(
- Object.keys(this._presences).map(
- (connectionId) => this.getUser(Number(connectionId))
+ Object.keys(this._presences).map(connectionId =>
+ this.getUser(Number(connectionId))
)
- );
- return asArrayWithLegacyMethods(users);
+ )
+ return asArrayWithLegacyMethods(users)
}
clearOthers() {
- this._connections = {};
- this._presences = {};
- this._users = {};
- this.invalidate();
+ this._connections = {}
+ this._presences = {}
+ this._users = {}
+ this.invalidate()
}
/** @internal */
_getUser(connectionId) {
- const conn = this._connections[connectionId];
- const presence = this._presences[connectionId];
+ const conn = this._connections[connectionId]
+ const presence = this._presences[connectionId]
if (conn !== void 0 && presence !== void 0) {
- return makeUser(conn, presence);
+ return makeUser(conn, presence)
}
- return void 0;
+ return void 0
}
getUser(connectionId) {
- const cachedUser = this._users[connectionId];
+ const cachedUser = this._users[connectionId]
if (cachedUser) {
- return cachedUser;
+ return cachedUser
}
- const computedUser = this._getUser(connectionId);
+ const computedUser = this._getUser(connectionId)
if (computedUser) {
- this._users[connectionId] = computedUser;
- return computedUser;
+ this._users[connectionId] = computedUser
+ return computedUser
}
- return void 0;
+ return void 0
}
/** @internal */
_invalidateUser(connectionId) {
if (this._users[connectionId] !== void 0) {
- delete this._users[connectionId];
+ delete this._users[connectionId]
}
- this.invalidate();
+ this.invalidate()
}
/**
* Records a known connection. This records the connection ID and the
@@ -3911,9 +4028,9 @@
id: metaUserId,
info: metaUserInfo,
isReadOnly: metaIsReadonly
- });
+ })
if (this._presences[connectionId] !== void 0) {
- this._invalidateUser(connectionId);
+ this._invalidateUser(connectionId)
}
}
/**
@@ -3921,18 +4038,18 @@
* the presence information.
*/
removeConnection(connectionId) {
- delete this._connections[connectionId];
- delete this._presences[connectionId];
- this._invalidateUser(connectionId);
+ delete this._connections[connectionId]
+ delete this._presences[connectionId]
+ this._invalidateUser(connectionId)
}
/**
* Stores a new user from a full presence update. If the user already exists,
* its known presence data is overwritten.
*/
setOther(connectionId, presence) {
- this._presences[connectionId] = freeze(compactObject(presence));
+ this._presences[connectionId] = freeze(compactObject(presence))
if (this._connections[connectionId] !== void 0) {
- this._invalidateUser(connectionId);
+ this._invalidateUser(connectionId)
}
}
/**
@@ -3941,86 +4058,90 @@
* full .setOther() call first.
*/
patchOther(connectionId, patch) {
- const oldPresence = this._presences[connectionId];
+ const oldPresence = this._presences[connectionId]
if (oldPresence === void 0) {
- return;
+ return
}
- const newPresence = merge(oldPresence, patch);
+ const newPresence = merge(oldPresence, patch)
if (oldPresence !== newPresence) {
- this._presences[connectionId] = freeze(newPresence);
- this._invalidateUser(connectionId);
+ this._presences[connectionId] = freeze(newPresence)
+ this._invalidateUser(connectionId)
}
}
- };
+ }
var PatchableRef = class extends ImmutableRef {
constructor(data) {
- super();
- this._data = freeze(compactObject(data));
+ super()
+ this._data = freeze(compactObject(data))
}
/** @internal */
_toImmutable() {
- return this._data;
+ return this._data
}
/**
* Patches the current object.
*/
patch(patch) {
- const oldData = this._data;
- const newData = merge(oldData, patch);
+ const oldData = this._data
+ const newData = merge(oldData, patch)
if (oldData !== newData) {
- this._data = freeze(newData);
- this.invalidate();
+ this._data = freeze(newData)
+ this.invalidate()
}
}
- };
+ }
var ValueRef = class extends ImmutableRef {
constructor(initialValue) {
- super();
- this._value = freeze(initialValue);
+ super()
+ this._value = freeze(initialValue)
}
/** @internal */
_toImmutable() {
- return this._value;
+ return this._value
}
set(newValue) {
- this._value = freeze(newValue);
- this.invalidate();
+ this._value = freeze(newValue)
+ this.invalidate()
}
- };
+ }
var DerivedRef = class extends ImmutableRef {
constructor(...args) {
- super();
- const transformFn = args.pop();
- const otherRefs = args;
- this._refs = otherRefs;
- this._refs.forEach((ref) => {
- ref.didInvalidate.subscribe(() => this.invalidate());
- });
- this._transform = transformFn;
+ super()
+ const transformFn = args.pop()
+ const otherRefs = args
+ this._refs = otherRefs
+ this._refs.forEach(ref => {
+ ref.didInvalidate.subscribe(() => this.invalidate())
+ })
+ this._transform = transformFn
}
/** @internal */
_toImmutable() {
- return this._transform(
- ...this._refs.map((ref) => ref.current)
- );
+ return this._transform(...this._refs.map(ref => ref.current))
}
- };
- var MAX_MESSAGE_SIZE = 1024 * 1024 - 128;
+ }
+ var MAX_MESSAGE_SIZE = 1024 * 1024 - 128
function makeIdFactory(connectionId) {
- let count = 0;
- return () => `${connectionId}:${count++}`;
+ let count = 0
+ return () => `${connectionId}:${count++}`
}
function userToTreeNode(key, user) {
return {
- type: "User",
+ type: 'User',
id: `${user.connectionId}`,
key,
payload: user
- };
+ }
}
function createRoom(options, config) {
- const initialPresence = typeof options.initialPresence === "function" ? options.initialPresence(config.roomId) : options.initialPresence;
- const initialStorage = typeof options.initialStorage === "function" ? options.initialStorage(config.roomId) : options.initialStorage;
+ const initialPresence =
+ typeof options.initialPresence === 'function'
+ ? options.initialPresence(config.roomId)
+ : options.initialPresence
+ const initialStorage =
+ typeof options.initialStorage === 'function'
+ ? options.initialStorage(config.roomId)
+ : options.initialStorage
const delegates = config.delegates ?? {
authenticate: makeAuthDelegateForRoom(
config.roomId,
@@ -4031,22 +4152,21 @@
config.liveblocksServer,
config.polyfills?.WebSocket
)
- };
+ }
const managedSocket = new ManagedSocket(
delegates,
config.enableDebugLogging
- );
+ )
const context = {
buffer: {
flushTimerID: void 0,
lastFlushedAt: 0,
- presenceUpdates: (
+ presenceUpdates:
// Queue up the initial presence message as a Full Presenceβ’ update
{
- type: "full",
+ type: 'full',
data: initialPresence
- }
- ),
+ },
messages: [],
storageOperations: []
},
@@ -4067,12 +4187,12 @@
unacknowledgedOps: /* @__PURE__ */ new Map(),
// Debug
opStackTraces: true ? /* @__PURE__ */ new Map() : void 0
- };
- const doNotBatchUpdates = (cb) => cb();
- const batchUpdates = config.unstable_batchedUpdates ?? doNotBatchUpdates;
- let lastToken;
+ }
+ const doNotBatchUpdates = cb => cb()
+ const batchUpdates = config.unstable_batchedUpdates ?? doNotBatchUpdates
+ let lastToken
function onStatusDidChange(newStatus) {
- const token = managedSocket.token?.parsed;
+ const token = managedSocket.token?.parsed
if (token !== void 0 && token !== lastToken) {
context.sessionInfo.set({
userInfo: token.info,
@@ -4080,102 +4200,104 @@
// NOTE: In the future, these fields will get assigned in the connection phase
actor: token.actor,
isReadOnly: isStorageReadOnly(token.scopes)
- });
- lastToken = token;
+ })
+ lastToken = token
}
batchUpdates(() => {
- eventHub.status.notify(newStatus);
- eventHub.connection.notify(newToLegacyStatus(newStatus));
- notifySelfChanged(doNotBatchUpdates);
- });
+ eventHub.status.notify(newStatus)
+ eventHub.connection.notify(newToLegacyStatus(newStatus))
+ notifySelfChanged(doNotBatchUpdates)
+ })
}
- let _connectionLossTimerId;
- let _hasLostConnection = false;
+ let _connectionLossTimerId
+ let _hasLostConnection = false
function handleConnectionLossEvent(newStatus) {
- if (newStatus === "reconnecting") {
+ if (newStatus === 'reconnecting') {
_connectionLossTimerId = setTimeout(() => {
batchUpdates(() => {
- eventHub.lostConnection.notify("lost");
- _hasLostConnection = true;
- context.others.clearOthers();
- notify({ others: [{ type: "reset" }] }, doNotBatchUpdates);
- });
- }, config.lostConnectionTimeout);
+ eventHub.lostConnection.notify('lost')
+ _hasLostConnection = true
+ context.others.clearOthers()
+ notify({ others: [{ type: 'reset' }] }, doNotBatchUpdates)
+ })
+ }, config.lostConnectionTimeout)
} else {
- clearTimeout(_connectionLossTimerId);
+ clearTimeout(_connectionLossTimerId)
if (_hasLostConnection) {
- if (newStatus === "disconnected") {
+ if (newStatus === 'disconnected') {
batchUpdates(() => {
- eventHub.lostConnection.notify("failed");
- });
+ eventHub.lostConnection.notify('failed')
+ })
} else {
batchUpdates(() => {
- eventHub.lostConnection.notify("restored");
- });
+ eventHub.lostConnection.notify('restored')
+ })
}
- _hasLostConnection = false;
+ _hasLostConnection = false
}
}
}
function onDidConnect() {
- const sessionInfo = context.sessionInfo.current;
+ const sessionInfo = context.sessionInfo.current
if (sessionInfo === null) {
- throw new Error("Unexpected missing session info");
+ throw new Error('Unexpected missing session info')
}
context.buffer.presenceUpdates = {
- type: "full",
- data: (
+ type: 'full',
+ data:
// Because context.me.current is a readonly object, we'll have to
// make a copy here. Otherwise, type errors happen later when
// "patching" my presence.
{ ...context.me.current }
- )
- };
- context.idFactory = makeIdFactory(sessionInfo.actor);
+ }
+ context.idFactory = makeIdFactory(sessionInfo.actor)
if (_getStorage$ !== null) {
- refreshStorage({ flush: false });
+ refreshStorage({ flush: false })
}
- flushNowOrSoon();
+ flushNowOrSoon()
}
function onDidDisconnect() {
- clearTimeout(context.buffer.flushTimerID);
- }
- managedSocket.events.onMessage.subscribe(handleServerMessage);
- managedSocket.events.statusDidChange.subscribe(onStatusDidChange);
- managedSocket.events.statusDidChange.subscribe(handleConnectionLossEvent);
- managedSocket.events.didConnect.subscribe(onDidConnect);
- managedSocket.events.didDisconnect.subscribe(onDidDisconnect);
- managedSocket.events.onLiveblocksError.subscribe((err) => {
+ clearTimeout(context.buffer.flushTimerID)
+ }
+ managedSocket.events.onMessage.subscribe(handleServerMessage)
+ managedSocket.events.statusDidChange.subscribe(onStatusDidChange)
+ managedSocket.events.statusDidChange.subscribe(handleConnectionLossEvent)
+ managedSocket.events.didConnect.subscribe(onDidConnect)
+ managedSocket.events.didDisconnect.subscribe(onDidDisconnect)
+ managedSocket.events.onLiveblocksError.subscribe(err => {
batchUpdates(() => {
if (true) {
error2(
`Connection to websocket server closed. Reason: ${err.message} (code: ${err.code}).`
- );
+ )
}
- eventHub.error.notify(err);
- });
- });
+ eventHub.error.notify(err)
+ })
+ })
const pool = {
roomId: config.roomId,
- getNode: (id) => context.nodes.get(id),
+ getNode: id => context.nodes.get(id),
addNode: (id, node) => void context.nodes.set(id, node),
- deleteNode: (id) => void context.nodes.delete(id),
+ deleteNode: id => void context.nodes.delete(id),
generateId: () => `${getConnectionId()}:${context.clock++}`,
generateOpId: () => `${getConnectionId()}:${context.opClock++}`,
dispatch(ops, reverse, storageUpdates) {
- const activeBatch = context.activeBatch;
+ const activeBatch = context.activeBatch
if (true) {
- const stackTrace = captureStackTrace("Storage mutation", this.dispatch);
+ const stackTrace = captureStackTrace(
+ 'Storage mutation',
+ this.dispatch
+ )
if (stackTrace) {
for (const op of ops) {
if (op.opId) {
- nn(context.opStackTraces).set(op.opId, stackTrace);
+ nn(context.opStackTraces).set(op.opId, stackTrace)
}
}
}
}
if (activeBatch) {
- activeBatch.ops.push(...ops);
+ activeBatch.ops.push(...ops)
for (const [key, value] of storageUpdates) {
activeBatch.updates.storageUpdates.set(
key,
@@ -4183,26 +4305,26 @@
activeBatch.updates.storageUpdates.get(key),
value
)
- );
+ )
}
- activeBatch.reverseOps.unshift(...reverse);
+ activeBatch.reverseOps.unshift(...reverse)
} else {
batchUpdates(() => {
- addToUndoStack(reverse, doNotBatchUpdates);
- context.redoStack = [];
- dispatchOps(ops);
- notify({ storageUpdates }, doNotBatchUpdates);
- });
+ addToUndoStack(reverse, doNotBatchUpdates)
+ context.redoStack = []
+ dispatchOps(ops)
+ notify({ storageUpdates }, doNotBatchUpdates)
+ })
}
},
assertStorageIsWritable: () => {
if (context.sessionInfo.current?.isReadOnly) {
throw new Error(
- "Cannot write to storage with a read only user, please ensure the user has write permissions"
- );
+ 'Cannot write to storage with a read only user, please ensure the user has write permissions'
+ )
}
}
- };
+ }
const eventHub = {
connection: makeEventSource(),
// Old/deprecated API
@@ -4219,190 +4341,196 @@
storageDidLoad: makeEventSource(),
storageStatus: makeEventSource(),
ydoc: makeEventSource()
- };
+ }
function sendMessages(messageOrMessages) {
- const message = JSON.stringify(messageOrMessages);
+ const message = JSON.stringify(messageOrMessages)
if (config.unstable_fallbackToHTTP) {
- const size = new TextEncoder().encode(message).length;
- if (size > MAX_MESSAGE_SIZE && managedSocket.token?.raw && config.httpSendEndpoint) {
+ const size = new TextEncoder().encode(message).length
+ if (
+ size > MAX_MESSAGE_SIZE &&
+ managedSocket.token?.raw &&
+ config.httpSendEndpoint
+ ) {
void httpSend(
message,
managedSocket.token.raw,
config.httpSendEndpoint,
config.polyfills?.fetch
- ).then((resp) => {
+ ).then(resp => {
if (!resp.ok && resp.status === 403) {
- managedSocket.reconnect();
+ managedSocket.reconnect()
}
- });
+ })
warn(
- "Message was too large for websockets and sent over HTTP instead"
- );
- return;
- }
- }
- managedSocket.send(message);
- }
- const self = new DerivedRef(
- context.sessionInfo,
- context.me,
- (info, me) => {
- return info !== null ? {
- connectionId: info.actor,
- id: info.userId,
- info: info.userInfo,
- presence: me,
- isReadOnly: info.isReadOnly
- } : null;
- }
- );
- let _lastSelf;
+ 'Message was too large for websockets and sent over HTTP instead'
+ )
+ return
+ }
+ }
+ managedSocket.send(message)
+ }
+ const self = new DerivedRef(context.sessionInfo, context.me, (info, me) => {
+ return info !== null
+ ? {
+ connectionId: info.actor,
+ id: info.userId,
+ info: info.userInfo,
+ presence: me,
+ isReadOnly: info.isReadOnly
+ }
+ : null
+ })
+ let _lastSelf
function notifySelfChanged(batchedUpdatesWrapper) {
- const currSelf = self.current;
+ const currSelf = self.current
if (currSelf !== null && currSelf !== _lastSelf) {
batchedUpdatesWrapper(() => {
- eventHub.self.notify(currSelf);
- });
- _lastSelf = currSelf;
+ eventHub.self.notify(currSelf)
+ })
+ _lastSelf = currSelf
}
}
- const selfAsTreeNode = new DerivedRef(
- self,
- (me) => me !== null ? userToTreeNode("Me", me) : null
- );
+ const selfAsTreeNode = new DerivedRef(self, me =>
+ me !== null ? userToTreeNode('Me', me) : null
+ )
function createOrUpdateRootFromMessage(message, batchedUpdatesWrapper) {
if (message.items.length === 0) {
- throw new Error("Internal error: cannot load storage without items");
+ throw new Error('Internal error: cannot load storage without items')
}
if (context.root !== void 0) {
- updateRoot(message.items, batchedUpdatesWrapper);
+ updateRoot(message.items, batchedUpdatesWrapper)
} else {
- context.root = LiveObject._fromItems(message.items, pool);
+ context.root = LiveObject._fromItems(message.items, pool)
}
for (const key in context.initialStorage) {
if (context.root.get(key) === void 0) {
- context.root.set(key, context.initialStorage[key]);
+ context.root.set(key, context.initialStorage[key])
}
}
}
function updateRoot(items, batchedUpdatesWrapper) {
if (context.root === void 0) {
- return;
+ return
}
- const currentItems = /* @__PURE__ */ new Map();
+ const currentItems = /* @__PURE__ */ new Map()
for (const [id, node] of context.nodes) {
- currentItems.set(id, node._serialize());
+ currentItems.set(id, node._serialize())
}
- const ops = getTreesDiffOperations(currentItems, new Map(items));
- const result = applyOps(ops, false);
- notify(result.updates, batchedUpdatesWrapper);
+ const ops = getTreesDiffOperations(currentItems, new Map(items))
+ const result = applyOps(ops, false)
+ notify(result.updates, batchedUpdatesWrapper)
}
function _addToRealUndoStack(historyOps, batchedUpdatesWrapper) {
if (context.undoStack.length >= 50) {
- context.undoStack.shift();
+ context.undoStack.shift()
}
- context.undoStack.push(historyOps);
- onHistoryChange(batchedUpdatesWrapper);
+ context.undoStack.push(historyOps)
+ onHistoryChange(batchedUpdatesWrapper)
}
function addToUndoStack(historyOps, batchedUpdatesWrapper) {
if (context.pausedHistory !== null) {
- context.pausedHistory.unshift(...historyOps);
+ context.pausedHistory.unshift(...historyOps)
} else {
- _addToRealUndoStack(historyOps, batchedUpdatesWrapper);
+ _addToRealUndoStack(historyOps, batchedUpdatesWrapper)
}
}
- function notify({
- storageUpdates = /* @__PURE__ */ new Map(),
- presence = false,
- others: otherEvents = []
- }, batchedUpdatesWrapper) {
+ function notify(
+ {
+ storageUpdates = /* @__PURE__ */ new Map(),
+ presence = false,
+ others: otherEvents = []
+ },
+ batchedUpdatesWrapper
+ ) {
batchedUpdatesWrapper(() => {
if (otherEvents.length > 0) {
- const others = context.others.current;
+ const others = context.others.current
for (const event of otherEvents) {
- eventHub.others.notify({ others, event });
+ eventHub.others.notify({ others, event })
}
}
if (presence) {
- notifySelfChanged(doNotBatchUpdates);
- eventHub.myPresence.notify(context.me.current);
+ notifySelfChanged(doNotBatchUpdates)
+ eventHub.myPresence.notify(context.me.current)
}
if (storageUpdates.size > 0) {
- const updates = Array.from(storageUpdates.values());
- eventHub.storage.notify(updates);
+ const updates = Array.from(storageUpdates.values())
+ eventHub.storage.notify(updates)
}
- notifyStorageStatus();
- });
+ notifyStorageStatus()
+ })
}
function getConnectionId() {
- const info = context.sessionInfo.current;
+ const info = context.sessionInfo.current
if (info) {
- return info.actor;
+ return info.actor
}
throw new Error(
- "Internal. Tried to get connection id but connection was never open"
- );
+ 'Internal. Tried to get connection id but connection was never open'
+ )
}
function applyOps(rawOps, isLocal) {
const output = {
reverse: [],
storageUpdates: /* @__PURE__ */ new Map(),
presence: false
- };
- const createdNodeIds = /* @__PURE__ */ new Set();
- const ops = rawOps.map((op) => {
- if (op.type !== "presence" && !op.opId) {
- return { ...op, opId: pool.generateOpId() };
+ }
+ const createdNodeIds = /* @__PURE__ */ new Set()
+ const ops = rawOps.map(op => {
+ if (op.type !== 'presence' && !op.opId) {
+ return { ...op, opId: pool.generateOpId() }
} else {
- return op;
+ return op
}
- });
+ })
for (const op of ops) {
- if (op.type === "presence") {
+ if (op.type === 'presence') {
const reverse = {
- type: "presence",
+ type: 'presence',
data: {}
- };
+ }
for (const key in op.data) {
- reverse.data[key] = context.me.current[key];
+ reverse.data[key] = context.me.current[key]
}
- context.me.patch(op.data);
+ context.me.patch(op.data)
if (context.buffer.presenceUpdates === null) {
- context.buffer.presenceUpdates = { type: "partial", data: op.data };
+ context.buffer.presenceUpdates = { type: 'partial', data: op.data }
} else {
for (const key in op.data) {
- context.buffer.presenceUpdates.data[key] = op.data[key];
+ context.buffer.presenceUpdates.data[key] = op.data[key]
}
}
- output.reverse.unshift(reverse);
- output.presence = true;
+ output.reverse.unshift(reverse)
+ output.presence = true
} else {
- let source;
+ let source
if (isLocal) {
- source = 0;
+ source = 0
} else {
- const opId = nn(op.opId);
+ const opId = nn(op.opId)
if (true) {
- nn(context.opStackTraces).delete(opId);
+ nn(context.opStackTraces).delete(opId)
}
- const deleted = context.unacknowledgedOps.delete(opId);
- source = deleted ? 2 : 1;
+ const deleted = context.unacknowledgedOps.delete(opId)
+ source = deleted ? 2 : 1
}
- const applyOpResult = applyOp(op, source);
+ const applyOpResult = applyOp(op, source)
if (applyOpResult.modified) {
- const nodeId = applyOpResult.modified.node._id;
+ const nodeId = applyOpResult.modified.node._id
if (!(nodeId && createdNodeIds.has(nodeId))) {
output.storageUpdates.set(
nn(applyOpResult.modified.node._id),
mergeStorageUpdates(
- output.storageUpdates.get(nn(applyOpResult.modified.node._id)),
+ output.storageUpdates.get(
+ nn(applyOpResult.modified.node._id)
+ ),
applyOpResult.modified
)
- );
- output.reverse.unshift(...applyOpResult.reverse);
+ )
+ output.reverse.unshift(...applyOpResult.reverse)
}
if (op.type === 2 || op.type === 7 || op.type === 4) {
- createdNodeIds.add(nn(op.id));
+ createdNodeIds.add(nn(op.id))
}
}
}
@@ -4414,164 +4542,171 @@
storageUpdates: output.storageUpdates,
presence: output.presence
}
- };
+ }
}
function applyOp(op, source) {
if (isAckOp(op)) {
- return { modified: false };
+ return { modified: false }
}
switch (op.type) {
case 6:
case 3:
case 5: {
- const node = context.nodes.get(op.id);
+ const node = context.nodes.get(op.id)
if (node === void 0) {
- return { modified: false };
+ return { modified: false }
}
return node._apply(
op,
source === 0
/* UNDOREDO_RECONNECT */
- );
+ )
}
case 1: {
- const node = context.nodes.get(op.id);
+ const node = context.nodes.get(op.id)
if (node === void 0) {
- return { modified: false };
+ return { modified: false }
}
- if (node.parent.type === "HasParent" && isLiveList(node.parent.node)) {
+ if (
+ node.parent.type === 'HasParent' &&
+ isLiveList(node.parent.node)
+ ) {
return node.parent.node._setChildKey(
asPos(op.parentKey),
node,
source
- );
+ )
}
- return { modified: false };
+ return { modified: false }
}
case 4:
case 2:
case 7:
case 8: {
if (op.parentId === void 0) {
- return { modified: false };
+ return { modified: false }
}
- const parentNode = context.nodes.get(op.parentId);
+ const parentNode = context.nodes.get(op.parentId)
if (parentNode === void 0) {
- return { modified: false };
+ return { modified: false }
}
- return parentNode._attachChild(op, source);
+ return parentNode._attachChild(op, source)
}
}
}
function updatePresence(patch, options2) {
- const oldValues = {};
+ const oldValues = {}
if (context.buffer.presenceUpdates === null) {
context.buffer.presenceUpdates = {
- type: "partial",
+ type: 'partial',
data: {}
- };
+ }
}
for (const key in patch) {
- const overrideValue = patch[key];
+ const overrideValue = patch[key]
if (overrideValue === void 0) {
- continue;
+ continue
}
- context.buffer.presenceUpdates.data[key] = overrideValue;
- oldValues[key] = context.me.current[key];
+ context.buffer.presenceUpdates.data[key] = overrideValue
+ oldValues[key] = context.me.current[key]
}
- context.me.patch(patch);
+ context.me.patch(patch)
if (context.activeBatch) {
if (options2?.addToHistory) {
context.activeBatch.reverseOps.unshift({
- type: "presence",
+ type: 'presence',
data: oldValues
- });
+ })
}
- context.activeBatch.updates.presence = true;
+ context.activeBatch.updates.presence = true
} else {
- flushNowOrSoon();
+ flushNowOrSoon()
batchUpdates(() => {
if (options2?.addToHistory) {
addToUndoStack(
- [{ type: "presence", data: oldValues }],
+ [{ type: 'presence', data: oldValues }],
doNotBatchUpdates
- );
+ )
}
- notify({ presence: true }, doNotBatchUpdates);
- });
+ notify({ presence: true }, doNotBatchUpdates)
+ })
}
}
function isStorageReadOnly(scopes) {
- return scopes.includes(
- "room:read"
- /* Read */
- ) && scopes.includes(
- "room:presence:write"
- /* PresenceWrite */
- ) && !scopes.includes(
- "room:write"
- /* Write */
- );
+ return (
+ scopes.includes(
+ 'room:read'
+ /* Read */
+ ) &&
+ scopes.includes(
+ 'room:presence:write'
+ /* PresenceWrite */
+ ) &&
+ !scopes.includes(
+ 'room:write'
+ /* Write */
+ )
+ )
}
function onUpdatePresenceMessage(message) {
if (message.targetActor !== void 0) {
- const oldUser = context.others.getUser(message.actor);
- context.others.setOther(message.actor, message.data);
- const newUser = context.others.getUser(message.actor);
+ const oldUser = context.others.getUser(message.actor)
+ context.others.setOther(message.actor, message.data)
+ const newUser = context.others.getUser(message.actor)
if (oldUser === void 0 && newUser !== void 0) {
- return { type: "enter", user: newUser };
+ return { type: 'enter', user: newUser }
}
} else {
- context.others.patchOther(message.actor, message.data), message;
+ context.others.patchOther(message.actor, message.data), message
}
- const user = context.others.getUser(message.actor);
+ const user = context.others.getUser(message.actor)
if (user) {
return {
- type: "update",
+ type: 'update',
updates: message.data,
user
- };
+ }
} else {
- return void 0;
+ return void 0
}
}
function onUserLeftMessage(message) {
- const user = context.others.getUser(message.actor);
+ const user = context.others.getUser(message.actor)
if (user) {
- context.others.removeConnection(message.actor);
- return { type: "leave", user };
+ context.others.removeConnection(message.actor)
+ return { type: 'leave', user }
}
- return null;
+ return null
}
function onRoomStateMessage(message) {
for (const connectionId in context.others._connections) {
- const user = message.users[connectionId];
+ const user = message.users[connectionId]
if (user === void 0) {
- context.others.removeConnection(Number(connectionId));
+ context.others.removeConnection(Number(connectionId))
}
}
for (const key in message.users) {
- const user = message.users[key];
- const connectionId = Number(key);
+ const user = message.users[key]
+ const connectionId = Number(key)
context.others.setConnection(
connectionId,
user.id,
user.info,
isStorageReadOnly(user.scopes)
- );
+ )
}
- return { type: "reset" };
+ return { type: 'reset' }
}
function canUndo() {
- return context.undoStack.length > 0;
+ return context.undoStack.length > 0
}
function canRedo() {
- return context.redoStack.length > 0;
+ return context.redoStack.length > 0
}
function onHistoryChange(batchedUpdatesWrapper) {
batchedUpdatesWrapper(() => {
- eventHub.history.notify({ canUndo: canUndo(), canRedo: canRedo() });
- });
+ eventHub.history.notify({ canUndo: canUndo(), canRedo: canRedo() })
+ })
}
function onUserJoinedMessage(message) {
context.others.setConnection(
@@ -4579,337 +4714,344 @@
message.id,
message.info,
isStorageReadOnly(message.scopes)
- );
+ )
context.buffer.messages.push({
type: 100,
data: context.me.current,
targetActor: message.actor
- });
- flushNowOrSoon();
- const user = context.others.getUser(message.actor);
- return user ? { type: "enter", user } : void 0;
+ })
+ flushNowOrSoon()
+ const user = context.others.getUser(message.actor)
+ return user ? { type: 'enter', user } : void 0
}
function parseServerMessage(data) {
if (!isJsonObject(data)) {
- return null;
+ return null
}
- return data;
+ return data
}
function parseServerMessages(text) {
- const data = tryParseJson(text);
+ const data = tryParseJson(text)
if (data === void 0) {
- return null;
+ return null
} else if (isJsonArray(data)) {
- return compact(data.map((item) => parseServerMessage(item)));
+ return compact(data.map(item => parseServerMessage(item)))
} else {
- return compact([parseServerMessage(data)]);
+ return compact([parseServerMessage(data)])
}
}
function applyAndSendOps(offlineOps, batchedUpdatesWrapper) {
if (offlineOps.size === 0) {
- return;
+ return
}
- const messages = [];
- const ops = Array.from(offlineOps.values());
- const result = applyOps(ops, true);
+ const messages = []
+ const ops = Array.from(offlineOps.values())
+ const result = applyOps(ops, true)
messages.push({
type: 201,
ops: result.ops
- });
- notify(result.updates, batchedUpdatesWrapper);
- sendMessages(messages);
+ })
+ notify(result.updates, batchedUpdatesWrapper)
+ sendMessages(messages)
}
function handleServerMessage(event) {
- if (typeof event.data !== "string") {
- return;
+ if (typeof event.data !== 'string') {
+ return
}
- const messages = parseServerMessages(event.data);
+ const messages = parseServerMessages(event.data)
if (messages === null || messages.length === 0) {
- return;
+ return
}
const updates = {
storageUpdates: /* @__PURE__ */ new Map(),
others: []
- };
+ }
batchUpdates(() => {
for (const message of messages) {
switch (message.type) {
case 101: {
- const userJoinedUpdate = onUserJoinedMessage(message);
+ const userJoinedUpdate = onUserJoinedMessage(message)
if (userJoinedUpdate) {
- updates.others.push(userJoinedUpdate);
+ updates.others.push(userJoinedUpdate)
}
- break;
+ break
}
case 100: {
- const othersPresenceUpdate = onUpdatePresenceMessage(message);
+ const othersPresenceUpdate = onUpdatePresenceMessage(message)
if (othersPresenceUpdate) {
- updates.others.push(othersPresenceUpdate);
+ updates.others.push(othersPresenceUpdate)
}
- break;
+ break
}
case 103: {
eventHub.customEvent.notify({
connectionId: message.actor,
event: message.event
- });
- break;
+ })
+ break
}
case 102: {
- const event2 = onUserLeftMessage(message);
+ const event2 = onUserLeftMessage(message)
if (event2) {
- updates.others.push(event2);
+ updates.others.push(event2)
}
- break;
+ break
}
case 300: {
- eventHub.ydoc.notify(message);
- break;
+ eventHub.ydoc.notify(message)
+ break
}
case 104: {
- updates.others.push(onRoomStateMessage(message));
- break;
+ updates.others.push(onRoomStateMessage(message))
+ break
}
case 200: {
- const unacknowledgedOps = new Map(context.unacknowledgedOps);
- createOrUpdateRootFromMessage(message, doNotBatchUpdates);
- applyAndSendOps(unacknowledgedOps, doNotBatchUpdates);
- _resolveStoragePromise?.();
- notifyStorageStatus();
- eventHub.storageDidLoad.notify();
- break;
+ const unacknowledgedOps = new Map(context.unacknowledgedOps)
+ createOrUpdateRootFromMessage(message, doNotBatchUpdates)
+ applyAndSendOps(unacknowledgedOps, doNotBatchUpdates)
+ _resolveStoragePromise?.()
+ notifyStorageStatus()
+ eventHub.storageDidLoad.notify()
+ break
}
case 201: {
- const applyResult = applyOps(message.ops, false);
+ const applyResult = applyOps(message.ops, false)
for (const [key, value] of applyResult.updates.storageUpdates) {
updates.storageUpdates.set(
key,
mergeStorageUpdates(updates.storageUpdates.get(key), value)
- );
+ )
}
- break;
+ break
}
case 299: {
- errorWithTitle(
- "Storage mutation rejection error",
- message.reason
- );
+ errorWithTitle('Storage mutation rejection error', message.reason)
if (true) {
- const traces = /* @__PURE__ */ new Set();
+ const traces = /* @__PURE__ */ new Set()
for (const opId of message.opIds) {
- const trace = context.opStackTraces?.get(opId);
+ const trace = context.opStackTraces?.get(opId)
if (trace) {
- traces.add(trace);
+ traces.add(trace)
}
}
if (traces.size > 0) {
warnWithTitle(
- "The following function calls caused the rejected storage mutations:",
+ 'The following function calls caused the rejected storage mutations:',
`
-${Array.from(traces).join("\n\n")}`
- );
+${Array.from(traces).join('\n\n')}`
+ )
}
throw new Error(
`Storage mutations rejected by server: ${message.reason}`
- );
+ )
}
- break;
+ break
}
}
}
- notify(updates, doNotBatchUpdates);
- });
+ notify(updates, doNotBatchUpdates)
+ })
}
function flushNowOrSoon() {
- const storageOps = context.buffer.storageOperations;
+ const storageOps = context.buffer.storageOperations
if (storageOps.length > 0) {
for (const op of storageOps) {
- context.unacknowledgedOps.set(nn(op.opId), op);
+ context.unacknowledgedOps.set(nn(op.opId), op)
}
- notifyStorageStatus();
+ notifyStorageStatus()
}
- if (managedSocket.getStatus() !== "connected") {
- context.buffer.storageOperations = [];
- return;
+ if (managedSocket.getStatus() !== 'connected') {
+ context.buffer.storageOperations = []
+ return
}
- const now = Date.now();
- const elapsedMillis = now - context.buffer.lastFlushedAt;
+ const now = Date.now()
+ const elapsedMillis = now - context.buffer.lastFlushedAt
if (elapsedMillis > config.throttleDelay) {
- const messagesToFlush = serializeBuffer();
+ const messagesToFlush = serializeBuffer()
if (messagesToFlush.length === 0) {
- return;
+ return
}
- sendMessages(messagesToFlush);
+ sendMessages(messagesToFlush)
context.buffer = {
flushTimerID: void 0,
lastFlushedAt: now,
messages: [],
storageOperations: [],
presenceUpdates: null
- };
+ }
} else {
- clearTimeout(context.buffer.flushTimerID);
+ clearTimeout(context.buffer.flushTimerID)
context.buffer.flushTimerID = setTimeout(
flushNowOrSoon,
config.throttleDelay - elapsedMillis
- );
+ )
}
}
function serializeBuffer() {
- const messages = [];
+ const messages = []
if (context.buffer.presenceUpdates) {
messages.push(
- context.buffer.presenceUpdates.type === "full" ? {
- type: 100,
- // Populating the `targetActor` field turns this message into
- // a Full Presenceβ’ update message (not a patch), which will get
- // interpreted by other clients as such.
- targetActor: -1,
- data: context.buffer.presenceUpdates.data
- } : {
- type: 100,
- data: context.buffer.presenceUpdates.data
- }
- );
+ context.buffer.presenceUpdates.type === 'full'
+ ? {
+ type: 100,
+ // Populating the `targetActor` field turns this message into
+ // a Full Presenceβ’ update message (not a patch), which will get
+ // interpreted by other clients as such.
+ targetActor: -1,
+ data: context.buffer.presenceUpdates.data
+ }
+ : {
+ type: 100,
+ data: context.buffer.presenceUpdates.data
+ }
+ )
}
for (const event of context.buffer.messages) {
- messages.push(event);
+ messages.push(event)
}
if (context.buffer.storageOperations.length > 0) {
messages.push({
type: 201,
ops: context.buffer.storageOperations
- });
+ })
}
- return messages;
+ return messages
}
function updateYDoc(update) {
context.buffer.messages.push({
type: 301,
update
- });
- flushNowOrSoon();
+ })
+ flushNowOrSoon()
}
- function broadcastEvent(event, options2 = {
- shouldQueueEventIfNotReady: false
- }) {
- if (managedSocket.getStatus() !== "connected" && !options2.shouldQueueEventIfNotReady) {
- return;
+ function broadcastEvent(
+ event,
+ options2 = {
+ shouldQueueEventIfNotReady: false
+ }
+ ) {
+ if (
+ managedSocket.getStatus() !== 'connected' &&
+ !options2.shouldQueueEventIfNotReady
+ ) {
+ return
}
context.buffer.messages.push({
type: 103,
event
- });
- flushNowOrSoon();
+ })
+ flushNowOrSoon()
}
function dispatchOps(ops) {
- context.buffer.storageOperations.push(...ops);
- flushNowOrSoon();
+ context.buffer.storageOperations.push(...ops)
+ flushNowOrSoon()
}
- let _getStorage$ = null;
- let _resolveStoragePromise = null;
+ let _getStorage$ = null
+ let _resolveStoragePromise = null
function refreshStorage(options2) {
- const messages = context.buffer.messages;
- if (!messages.some(
- (msg) => msg.type === 200
- /* FETCH_STORAGE */
- )) {
+ const messages = context.buffer.messages
+ if (
+ !messages.some(
+ msg => msg.type === 200
+ /* FETCH_STORAGE */
+ )
+ ) {
messages.push({
type: 200
/* FETCH_STORAGE */
- });
+ })
}
if (options2.flush) {
- flushNowOrSoon();
+ flushNowOrSoon()
}
}
function startLoadingStorage() {
if (_getStorage$ === null) {
- refreshStorage({ flush: true });
- _getStorage$ = new Promise((resolve) => {
- _resolveStoragePromise = resolve;
- });
- notifyStorageStatus();
+ refreshStorage({ flush: true })
+ _getStorage$ = new Promise(resolve => {
+ _resolveStoragePromise = resolve
+ })
+ notifyStorageStatus()
}
- return _getStorage$;
+ return _getStorage$
}
function getStorageSnapshot() {
- const root = context.root;
+ const root = context.root
if (root !== void 0) {
- return root;
+ return root
} else {
- void startLoadingStorage();
- return null;
+ void startLoadingStorage()
+ return null
}
}
async function getStorage() {
if (context.root !== void 0) {
return Promise.resolve({
root: context.root
- });
+ })
}
- await startLoadingStorage();
+ await startLoadingStorage()
return {
root: nn(context.root)
- };
+ }
}
function fetchYDoc(vector) {
context.buffer.messages.push({
type: 300,
vector
- });
- flushNowOrSoon();
+ })
+ flushNowOrSoon()
}
function undo() {
if (context.activeBatch) {
- throw new Error("undo is not allowed during a batch");
+ throw new Error('undo is not allowed during a batch')
}
- const historyOps = context.undoStack.pop();
+ const historyOps = context.undoStack.pop()
if (historyOps === void 0) {
- return;
+ return
}
- context.pausedHistory = null;
- const result = applyOps(historyOps, true);
+ context.pausedHistory = null
+ const result = applyOps(historyOps, true)
batchUpdates(() => {
- notify(result.updates, doNotBatchUpdates);
- context.redoStack.push(result.reverse);
- onHistoryChange(doNotBatchUpdates);
- });
+ notify(result.updates, doNotBatchUpdates)
+ context.redoStack.push(result.reverse)
+ onHistoryChange(doNotBatchUpdates)
+ })
for (const op of result.ops) {
- if (op.type !== "presence") {
- context.buffer.storageOperations.push(op);
+ if (op.type !== 'presence') {
+ context.buffer.storageOperations.push(op)
}
}
- flushNowOrSoon();
+ flushNowOrSoon()
}
function redo() {
if (context.activeBatch) {
- throw new Error("redo is not allowed during a batch");
+ throw new Error('redo is not allowed during a batch')
}
- const historyOps = context.redoStack.pop();
+ const historyOps = context.redoStack.pop()
if (historyOps === void 0) {
- return;
+ return
}
- context.pausedHistory = null;
- const result = applyOps(historyOps, true);
+ context.pausedHistory = null
+ const result = applyOps(historyOps, true)
batchUpdates(() => {
- notify(result.updates, doNotBatchUpdates);
- context.undoStack.push(result.reverse);
- onHistoryChange(doNotBatchUpdates);
- });
+ notify(result.updates, doNotBatchUpdates)
+ context.undoStack.push(result.reverse)
+ onHistoryChange(doNotBatchUpdates)
+ })
for (const op of result.ops) {
- if (op.type !== "presence") {
- context.buffer.storageOperations.push(op);
+ if (op.type !== 'presence') {
+ context.buffer.storageOperations.push(op)
}
}
- flushNowOrSoon();
+ flushNowOrSoon()
}
function batch(callback) {
if (context.activeBatch) {
- return callback();
+ return callback()
}
- let returnValue = void 0;
+ let returnValue = void 0
batchUpdates(() => {
context.activeBatch = {
ops: [],
@@ -4919,56 +5061,57 @@ ${Array.from(traces).join("\n\n")}`
others: []
},
reverseOps: []
- };
+ }
try {
- returnValue = callback();
+ returnValue = callback()
} finally {
- const currentBatch = context.activeBatch;
- context.activeBatch = null;
+ const currentBatch = context.activeBatch
+ context.activeBatch = null
if (currentBatch.reverseOps.length > 0) {
- addToUndoStack(currentBatch.reverseOps, doNotBatchUpdates);
+ addToUndoStack(currentBatch.reverseOps, doNotBatchUpdates)
}
if (currentBatch.ops.length > 0) {
- context.redoStack = [];
+ context.redoStack = []
}
if (currentBatch.ops.length > 0) {
- dispatchOps(currentBatch.ops);
+ dispatchOps(currentBatch.ops)
}
- notify(currentBatch.updates, doNotBatchUpdates);
- flushNowOrSoon();
+ notify(currentBatch.updates, doNotBatchUpdates)
+ flushNowOrSoon()
}
- });
- return returnValue;
+ })
+ return returnValue
}
function pauseHistory() {
- context.pausedHistory = [];
+ context.pausedHistory = []
}
function resumeHistory() {
- const historyOps = context.pausedHistory;
- context.pausedHistory = null;
+ const historyOps = context.pausedHistory
+ context.pausedHistory = null
if (historyOps !== null && historyOps.length > 0) {
- _addToRealUndoStack(historyOps, batchUpdates);
+ _addToRealUndoStack(historyOps, batchUpdates)
}
}
function getStorageStatus() {
if (context.root === void 0) {
- return _getStorage$ === null ? "not-loaded" : "loading";
+ return _getStorage$ === null ? 'not-loaded' : 'loading'
} else {
- return context.unacknowledgedOps.size === 0 ? "synchronized" : "synchronizing";
+ return context.unacknowledgedOps.size === 0
+ ? 'synchronized'
+ : 'synchronizing'
}
}
- let _lastStorageStatus = getStorageStatus();
+ let _lastStorageStatus = getStorageStatus()
function notifyStorageStatus() {
- const storageStatus = getStorageStatus();
+ const storageStatus = getStorageStatus()
if (_lastStorageStatus !== storageStatus) {
- _lastStorageStatus = storageStatus;
- eventHub.storageStatus.notify(storageStatus);
+ _lastStorageStatus = storageStatus
+ eventHub.storageStatus.notify(storageStatus)
}
}
- const others_forDevTools = new DerivedRef(
- context.others,
- (others) => others.map((other, index) => userToTreeNode(`Other ${index}`, other))
- );
+ const others_forDevTools = new DerivedRef(context.others, others =>
+ others.map((other, index) => userToTreeNode(`Other ${index}`, other))
+ )
const events = {
connection: eventHub.connection.observable,
// Old/deprecated API
@@ -4985,13 +5128,13 @@ ${Array.from(traces).join("\n\n")}`
storageDidLoad: eventHub.storageDidLoad.observable,
storageStatus: eventHub.storageStatus.observable,
ydoc: eventHub.ydoc.observable
- };
+ }
return Object.defineProperty(
{
/* NOTE: Exposing __internal here only to allow testing implementation details in unit tests */
__internal: {
get presenceBuffer() {
- return deepClone(context.buffer.presenceUpdates?.data ?? null);
+ return deepClone(context.buffer.presenceUpdates?.data ?? null)
},
// prettier-ignore
get undoStack() {
@@ -5048,243 +5191,261 @@ ${Array.from(traces).join("\n\n")}`
},
// Explictly make the __internal field non-enumerable, to avoid aggressive
// freezing when used with Immer
- "__internal",
+ '__internal',
{ enumerable: false }
- );
+ )
}
function makeClassicSubscribeFn(events) {
function subscribeToLiveStructureDeeply(node, callback) {
- return events.storage.subscribe((updates) => {
- const relatedUpdates = updates.filter(
- (update) => isSameNodeOrChildOf(update.node, node)
- );
+ return events.storage.subscribe(updates => {
+ const relatedUpdates = updates.filter(update =>
+ isSameNodeOrChildOf(update.node, node)
+ )
if (relatedUpdates.length > 0) {
- callback(relatedUpdates);
+ callback(relatedUpdates)
}
- });
+ })
}
function subscribeToLiveStructureShallowly(node, callback) {
- return events.storage.subscribe((updates) => {
+ return events.storage.subscribe(updates => {
for (const update of updates) {
if (update.node._id === node._id) {
- callback(update.node);
+ callback(update.node)
}
}
- });
+ })
}
function subscribe(first, second, options) {
- if (typeof first === "string" && isRoomEventName(first)) {
- if (typeof second !== "function") {
- throw new Error("Second argument must be a callback function");
+ if (typeof first === 'string' && isRoomEventName(first)) {
+ if (typeof second !== 'function') {
+ throw new Error('Second argument must be a callback function')
}
- const callback = second;
+ const callback = second
switch (first) {
- case "event":
- return events.customEvent.subscribe(
- callback
- );
- case "my-presence":
- return events.myPresence.subscribe(callback);
- case "others": {
- const cb = callback;
- return events.others.subscribe(
- ({ others, event }) => cb(others, event)
- );
+ case 'event':
+ return events.customEvent.subscribe(callback)
+ case 'my-presence':
+ return events.myPresence.subscribe(callback)
+ case 'others': {
+ const cb = callback
+ return events.others.subscribe(({ others, event }) =>
+ cb(others, event)
+ )
}
- case "error":
- return events.error.subscribe(callback);
- case "connection":
- return events.connection.subscribe(
- callback
- );
- case "status":
- return events.status.subscribe(callback);
- case "lost-connection":
- return events.lostConnection.subscribe(
- callback
- );
- case "history":
- return events.history.subscribe(callback);
- case "storage-status":
- return events.storageStatus.subscribe(
- callback
- );
+ case 'error':
+ return events.error.subscribe(callback)
+ case 'connection':
+ return events.connection.subscribe(callback)
+ case 'status':
+ return events.status.subscribe(callback)
+ case 'lost-connection':
+ return events.lostConnection.subscribe(callback)
+ case 'history':
+ return events.history.subscribe(callback)
+ case 'storage-status':
+ return events.storageStatus.subscribe(callback)
default:
- return assertNever(first, "Unknown event");
+ return assertNever(first, 'Unknown event')
}
}
- if (second === void 0 || typeof first === "function") {
- if (typeof first === "function") {
- const storageCallback = first;
- return events.storage.subscribe(storageCallback);
+ if (second === void 0 || typeof first === 'function') {
+ if (typeof first === 'function') {
+ const storageCallback = first
+ return events.storage.subscribe(storageCallback)
} else {
- throw new Error("Please specify a listener callback");
+ throw new Error('Please specify a listener callback')
}
}
if (isLiveNode(first)) {
- const node = first;
+ const node = first
if (options?.isDeep) {
- const storageCallback = second;
- return subscribeToLiveStructureDeeply(node, storageCallback);
+ const storageCallback = second
+ return subscribeToLiveStructureDeeply(node, storageCallback)
} else {
- const nodeCallback = second;
- return subscribeToLiveStructureShallowly(node, nodeCallback);
+ const nodeCallback = second
+ return subscribeToLiveStructureShallowly(node, nodeCallback)
}
}
- throw new Error(`"${String(first)}" is not a valid event name`);
+ throw new Error(`"${String(first)}" is not a valid event name`)
}
- return subscribe;
+ return subscribe
}
function isRoomEventName(value) {
- return value === "my-presence" || value === "others" || value === "event" || value === "error" || value === "history" || value === "status" || value === "storage-status" || value === "lost-connection" || value === "connection";
+ return (
+ value === 'my-presence' ||
+ value === 'others' ||
+ value === 'event' ||
+ value === 'error' ||
+ value === 'history' ||
+ value === 'status' ||
+ value === 'storage-status' ||
+ value === 'lost-connection' ||
+ value === 'connection'
+ )
}
- function makeCreateSocketDelegateForRoom(liveblocksServer, WebSocketPolyfill) {
- return (richToken) => {
- const ws = WebSocketPolyfill ?? (typeof WebSocket === "undefined" ? void 0 : WebSocket);
+ function makeCreateSocketDelegateForRoom(
+ liveblocksServer,
+ WebSocketPolyfill
+ ) {
+ return richToken => {
+ const ws =
+ WebSocketPolyfill ??
+ (typeof WebSocket === 'undefined' ? void 0 : WebSocket)
if (ws === void 0) {
throw new StopRetrying(
- "To use Liveblocks client in a non-dom environment, you need to provide a WebSocket polyfill."
- );
+ 'To use Liveblocks client in a non-dom environment, you need to provide a WebSocket polyfill.'
+ )
}
- const token = richToken.raw;
+ const token = richToken.raw
return new ws(
- `${liveblocksServer}/?token=${token}&version=${PKG_VERSION || "dev"}`
- );
- };
+ `${liveblocksServer}/?token=${token}&version=${PKG_VERSION || 'dev'}`
+ )
+ }
}
async function httpSend(message, token, endpoint, fetchPolyfill) {
- const fetcher = fetchPolyfill || /* istanbul ignore next */
- fetch;
+ const fetcher = fetchPolyfill /* istanbul ignore next */ || fetch
return fetcher(endpoint, {
- method: "POST",
+ method: 'POST',
headers: {
- "Content-Type": "application/json",
+ 'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
},
body: message
- });
+ })
}
function makeAuthDelegateForRoom(roomId, authentication, fetchPolyfill) {
- const fetcher = fetchPolyfill ?? (typeof window === "undefined" ? void 0 : window.fetch);
- if (authentication.type === "public") {
+ const fetcher =
+ fetchPolyfill ?? (typeof window === 'undefined' ? void 0 : window.fetch)
+ if (authentication.type === 'public') {
return async () => {
if (fetcher === void 0) {
throw new StopRetrying(
- "To use Liveblocks client in a non-dom environment with a publicApiKey, you need to provide a fetch polyfill."
- );
+ 'To use Liveblocks client in a non-dom environment with a publicApiKey, you need to provide a fetch polyfill.'
+ )
}
return fetchAuthEndpoint(fetcher, authentication.url, {
room: roomId,
publicApiKey: authentication.publicApiKey
- }).then(({ token }) => parseAuthToken(token));
- };
- } else if (authentication.type === "private") {
+ }).then(({ token }) => parseAuthToken(token))
+ }
+ } else if (authentication.type === 'private') {
return async () => {
if (fetcher === void 0) {
throw new StopRetrying(
- "To use Liveblocks client in a non-dom environment with a url as auth endpoint, you need to provide a fetch polyfill."
- );
+ 'To use Liveblocks client in a non-dom environment with a url as auth endpoint, you need to provide a fetch polyfill.'
+ )
}
return fetchAuthEndpoint(fetcher, authentication.url, {
room: roomId
- }).then(({ token }) => parseAuthToken(token));
- };
- } else if (authentication.type === "custom") {
+ }).then(({ token }) => parseAuthToken(token))
+ }
+ } else if (authentication.type === 'custom') {
return async () => {
- const response = await authentication.callback(roomId);
- if (!response || typeof response !== "object") {
+ const response = await authentication.callback(roomId)
+ if (!response || typeof response !== 'object') {
throw new Error(
'We expect the authentication callback to return a token, but it does not. Hint: the return value should look like: { token: "..." }'
- );
- }
- if (typeof response.token === "string") {
- return parseAuthToken(response.token);
- } else if (typeof response.error === "string") {
- const reason = `Authentication failed: ${"reason" in response && typeof response.reason === "string" ? response.reason : "Forbidden"}`;
- if (response.error === "forbidden") {
- throw new StopRetrying(reason);
+ )
+ }
+ if (typeof response.token === 'string') {
+ return parseAuthToken(response.token)
+ } else if (typeof response.error === 'string') {
+ const reason = `Authentication failed: ${
+ 'reason' in response && typeof response.reason === 'string'
+ ? response.reason
+ : 'Forbidden'
+ }`
+ if (response.error === 'forbidden') {
+ throw new StopRetrying(reason)
} else {
- throw new Error(reason);
+ throw new Error(reason)
}
} else {
throw new Error(
'We expect the authentication callback to return a token, but it does not. Hint: the return value should look like: { token: "..." }'
- );
+ )
}
- };
+ }
} else {
- throw new Error("Internal error. Unexpected authentication type");
+ throw new Error('Internal error. Unexpected authentication type')
}
}
async function fetchAuthEndpoint(fetch2, endpoint, body) {
const res = await fetch2(endpoint, {
- method: "POST",
+ method: 'POST',
headers: {
- "Content-Type": "application/json"
+ 'Content-Type': 'application/json'
},
// Credentials are needed to support authentication with cookies
- credentials: "include",
+ credentials: 'include',
body: JSON.stringify(body)
- });
+ })
if (!res.ok) {
- const reason = `${(await res.text()).trim() || "reason not provided in auth response"} (${res.status} returned by POST ${endpoint})`;
+ const reason = `${
+ (await res.text()).trim() || 'reason not provided in auth response'
+ } (${res.status} returned by POST ${endpoint})`
if (res.status === 401 || res.status === 403) {
- throw new StopRetrying(`Unauthorized: ${reason}`);
+ throw new StopRetrying(`Unauthorized: ${reason}`)
} else {
- throw new Error(`Failed to authenticate: ${reason}`);
+ throw new Error(`Failed to authenticate: ${reason}`)
}
}
- let data;
+ let data
try {
- data = await res.json();
+ data = await res.json()
} catch (er) {
throw new Error(
`Expected a JSON response when doing a POST request on "${endpoint}". ${String(
er
)}`
- );
+ )
}
- if (!isPlainObject(data) || typeof data.token !== "string") {
+ if (!isPlainObject(data) || typeof data.token !== 'string') {
throw new Error(
`Expected a JSON response of the form \`{ token: "..." }\` when doing a POST request on "${endpoint}", but got ${JSON.stringify(
data
)}`
- );
- }
- const { token } = data;
- return { token };
- }
- var MIN_THROTTLE = 16;
- var MAX_THROTTLE = 1e3;
- var DEFAULT_THROTTLE = 100;
- var MIN_LOST_CONNECTION_TIMEOUT = 200;
- var RECOMMENDED_MIN_LOST_CONNECTION_TIMEOUT = 1e3;
- var MAX_LOST_CONNECTION_TIMEOUT = 3e4;
- var DEFAULT_LOST_CONNECTION_TIMEOUT = 5e3;
+ )
+ }
+ const { token } = data
+ return { token }
+ }
+ var MIN_THROTTLE = 16
+ var MAX_THROTTLE = 1e3
+ var DEFAULT_THROTTLE = 100
+ var MIN_LOST_CONNECTION_TIMEOUT = 200
+ var RECOMMENDED_MIN_LOST_CONNECTION_TIMEOUT = 1e3
+ var MAX_LOST_CONNECTION_TIMEOUT = 3e4
+ var DEFAULT_LOST_CONNECTION_TIMEOUT = 5e3
function getServerFromClientOptions(clientOptions) {
- const rawOptions = clientOptions;
- return typeof rawOptions.liveblocksServer === "string" ? rawOptions.liveblocksServer : "wss://api.liveblocks.io/v6";
+ const rawOptions = clientOptions
+ return typeof rawOptions.liveblocksServer === 'string'
+ ? rawOptions.liveblocksServer
+ : 'wss://api.liveblocks.io/v6'
}
function createClient(options) {
- const clientOptions = options;
- const throttleDelay = getThrottle(clientOptions.throttle ?? DEFAULT_THROTTLE);
+ const clientOptions = options
+ const throttleDelay = getThrottle(
+ clientOptions.throttle ?? DEFAULT_THROTTLE
+ )
const lostConnectionTimeout = getLostConnectionTimeout(
clientOptions.lostConnectionTimeout ?? DEFAULT_LOST_CONNECTION_TIMEOUT
- );
- const rooms = /* @__PURE__ */ new Map();
+ )
+ const rooms = /* @__PURE__ */ new Map()
function getRoom(roomId) {
- const room = rooms.get(roomId);
- return room ? room : null;
+ const room = rooms.get(roomId)
+ return room ? room : null
}
function enter(roomId, options2) {
- const existingRoom = rooms.get(roomId);
+ const existingRoom = rooms.get(roomId)
if (existingRoom !== void 0) {
- return existingRoom;
+ return existingRoom
}
deprecateIf(
- options2.initialPresence === null || options2.initialPresence === void 0,
- "Please provide an initial presence value for the current user when entering the room."
- );
+ options2.initialPresence === null ||
+ options2.initialPresence === void 0,
+ 'Please provide an initial presence value for the current user when entering the room.'
+ )
const newRoom = createRoom(
{
initialPresence: options2.initialPresence ?? {},
@@ -5306,194 +5467,201 @@ ${Array.from(traces).join("\n\n")}`
),
unstable_fallbackToHTTP: !!clientOptions.unstable_fallbackToHTTP
}
- );
- rooms.set(roomId, newRoom);
- setupDevTools(() => Array.from(rooms.keys()));
- linkDevTools(roomId, newRoom);
- const shouldConnect = options2.shouldInitiallyConnect ?? true;
+ )
+ rooms.set(roomId, newRoom)
+ setupDevTools(() => Array.from(rooms.keys()))
+ linkDevTools(roomId, newRoom)
+ const shouldConnect = options2.shouldInitiallyConnect ?? true
if (shouldConnect) {
- if (typeof atob === "undefined") {
+ if (typeof atob === 'undefined') {
if (clientOptions.polyfills?.atob === void 0) {
throw new Error(
- "You need to polyfill atob to use the client in your environment. Please follow the instructions at https://liveblocks.io/docs/errors/liveblocks-client/atob-polyfill"
- );
+ 'You need to polyfill atob to use the client in your environment. Please follow the instructions at https://liveblocks.io/docs/errors/liveblocks-client/atob-polyfill'
+ )
}
- global.atob = clientOptions.polyfills.atob;
+ global.atob = clientOptions.polyfills.atob
}
- newRoom.connect();
+ newRoom.connect()
}
- return newRoom;
+ return newRoom
}
function leave(roomId) {
- unlinkDevTools(roomId);
- const room = rooms.get(roomId);
+ unlinkDevTools(roomId)
+ const room = rooms.get(roomId)
if (room !== void 0) {
- room.destroy();
- rooms.delete(roomId);
+ room.destroy()
+ rooms.delete(roomId)
}
}
return {
getRoom,
enter,
leave
- };
+ }
}
function checkBounds(option, value, min, max, recommendedMin) {
- if (typeof value !== "number" || value < min || value > max) {
+ if (typeof value !== 'number' || value < min || value > max) {
throw new Error(
- `${option} should be a number between ${recommendedMin ?? min} and ${max}.`
- );
+ `${option} should be a number between ${
+ recommendedMin ?? min
+ } and ${max}.`
+ )
}
- return value;
+ return value
}
function getThrottle(value) {
- return checkBounds("throttle", value, MIN_THROTTLE, MAX_THROTTLE);
+ return checkBounds('throttle', value, MIN_THROTTLE, MAX_THROTTLE)
}
function getLostConnectionTimeout(value) {
return checkBounds(
- "lostConnectionTimeout",
+ 'lostConnectionTimeout',
value,
MIN_LOST_CONNECTION_TIMEOUT,
MAX_LOST_CONNECTION_TIMEOUT,
RECOMMENDED_MIN_LOST_CONNECTION_TIMEOUT
- );
+ )
}
function prepareAuthentication(clientOptions, roomId) {
- const { publicApiKey, authEndpoint } = clientOptions;
+ const { publicApiKey, authEndpoint } = clientOptions
if (authEndpoint !== void 0 && publicApiKey !== void 0) {
throw new Error(
- "You cannot use both publicApiKey and authEndpoint. Please use either publicApiKey or authEndpoint, but not both. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClient"
- );
+ 'You cannot use both publicApiKey and authEndpoint. Please use either publicApiKey or authEndpoint, but not both. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClient'
+ )
}
- if (typeof publicApiKey === "string") {
- if (publicApiKey.startsWith("sk_")) {
+ if (typeof publicApiKey === 'string') {
+ if (publicApiKey.startsWith('sk_')) {
throw new Error(
- "Invalid publicApiKey. You are using the secret key which is not supported. Please use the public key instead. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientPublicKey"
- );
- } else if (!publicApiKey.startsWith("pk_")) {
+ 'Invalid publicApiKey. You are using the secret key which is not supported. Please use the public key instead. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientPublicKey'
+ )
+ } else if (!publicApiKey.startsWith('pk_')) {
throw new Error(
- "Invalid key. Please use the public key format: pk_. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientPublicKey"
- );
+ 'Invalid key. Please use the public key format: pk_. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientPublicKey'
+ )
}
return {
- type: "public",
+ type: 'public',
publicApiKey,
url: buildLiveblocksPublicAuthorizeEndpoint(clientOptions, roomId)
- };
+ }
}
- if (typeof authEndpoint === "string") {
+ if (typeof authEndpoint === 'string') {
return {
- type: "private",
+ type: 'private',
url: authEndpoint
- };
- } else if (typeof authEndpoint === "function") {
+ }
+ } else if (typeof authEndpoint === 'function') {
return {
- type: "custom",
+ type: 'custom',
callback: authEndpoint
- };
+ }
} else if (authEndpoint !== void 0) {
throw new Error(
- "authEndpoint must be a string or a function. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientAuthEndpoint"
- );
+ 'authEndpoint must be a string or a function. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientAuthEndpoint'
+ )
}
throw new Error(
- "Invalid Liveblocks client options. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClient"
- );
+ 'Invalid Liveblocks client options. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClient'
+ )
}
function buildLiveblocksHttpSendEndpoint(options, roomId) {
if (options.httpSendEndpoint) {
- return options.httpSendEndpoint.replace("{roomId}", roomId);
+ return options.httpSendEndpoint.replace('{roomId}', roomId)
}
return `https://api.liveblocks.io/v2/rooms/${encodeURIComponent(
roomId
- )}/send-message`;
+ )}/send-message`
}
function buildLiveblocksPublicAuthorizeEndpoint(options, roomId) {
if (options.publicAuthorizeEndpoint) {
- return options.publicAuthorizeEndpoint.replace("{roomId}", roomId);
+ return options.publicAuthorizeEndpoint.replace('{roomId}', roomId)
}
return `https://api.liveblocks.io/v2/rooms/${encodeURIComponent(
roomId
- )}/public/authorize`;
+ )}/public/authorize`
}
- detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);
+ detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT)
// node_modules/@liveblocks/client/dist/index.mjs
- var PKG_NAME2 = "@liveblocks/client";
- var PKG_VERSION2 = "1.1.8";
- var PKG_FORMAT2 = "esm";
- detectDupes(PKG_NAME2, PKG_VERSION2, PKG_FORMAT2);
+ var PKG_NAME2 = '@liveblocks/client'
+ var PKG_VERSION2 = '1.1.8'
+ var PKG_FORMAT2 = 'esm'
+ detectDupes(PKG_NAME2, PKG_VERSION2, PKG_FORMAT2)
// src/app.js
function generateRandomId() {
- return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
+ return (
+ Math.random().toString(36).substring(2, 15) +
+ Math.random().toString(36).substring(2, 15)
+ )
}
function removeStaleReactions() {
window.reactions.remoteReactions = window.reactions.remoteReactions.filter(
- (reaction) => {
- const delta = ((/* @__PURE__ */ new Date()).getTime() - reaction.timestamp) / 1e3;
- return delta < 2 + 2;
+ reaction => {
+ const delta =
+ (/* @__PURE__ */ new Date().getTime() - reaction.timestamp) / 1e3
+ return delta < 2 + 2
}
- );
+ )
}
var generateRandomCurveForReaction = () => {
- const randomX1 = `100%`;
- const randomY1 = `100%`;
- const randomX2 = `${Math.random() * 5}%`;
- const randomY2 = `${Math.random() * 50 + 50}%`;
- return `cubic-bezier(${randomX1}, ${randomY1}, ${randomX2}, ${randomY2})`;
- };
+ const randomX1 = `100%`
+ const randomY1 = `100%`
+ const randomX2 = `${Math.random() * 5}%`
+ const randomY2 = `${Math.random() * 50 + 50}%`
+ return `cubic-bezier(${randomX1}, ${randomY1}, ${randomX2}, ${randomY2})`
+ }
var getStartingAngleForReaction = () => {
- const direction = Math.random() < 0.5 ? 1 : -1;
- const startingAngle = Math.floor(Math.random() * 90);
- return direction * startingAngle;
- };
+ const direction = Math.random() < 0.5 ? 1 : -1
+ const startingAngle = Math.floor(Math.random() * 90)
+ return direction * startingAngle
+ }
var client = createClient({
- publicApiKey: "pk_prod_nQme4lxwwAyBuCvk2CQP0Tve9kBh1KxeN_FUdQQqrc24qH9qYA2anmqOToNCpFyA"
- });
+ publicApiKey:
+ 'pk_prod_nQme4lxwwAyBuCvk2CQP0Tve9kBh1KxeN_FUdQQqrc24qH9qYA2anmqOToNCpFyA'
+ })
function run() {
- const room = client.enter("javascript-todo-app", {
+ const room = client.enter('javascript-todo-app', {
initialPresence: {}
- });
+ })
window.reactions = {
react: void 0,
remoteReactions: [],
getStartingAngleForReaction,
generateRandomCurveForReaction
- };
- window.reactions.react = (id) => {
- let emoji = "";
+ }
+ window.reactions.react = id => {
+ let emoji = ''
switch (id) {
- case "heart":
- emoji = "\u2764\uFE0F";
- break;
- case "fire":
- emoji = "\u{1F525}";
- break;
- case "octopus":
- emoji = "\u{1F419}";
- break;
- case "rocket":
- emoji = "\u{1F680}";
- break;
- }
- if (emoji !== "")
- room.broadcastEvent({ type: "reaction", emoji, emojiId: id });
- };
- room.subscribe("event", ({ event }) => {
- removeStaleReactions();
- if (event.type === "reaction") {
+ case 'heart':
+ emoji = '\u2764\uFE0F'
+ break
+ case 'fire':
+ emoji = '\u{1F525}'
+ break
+ case 'octopus':
+ emoji = '\u{1F419}'
+ break
+ case 'rocket':
+ emoji = '\u{1F680}'
+ break
+ }
+ if (emoji !== '')
+ room.broadcastEvent({ type: 'reaction', emoji, emojiId: id })
+ }
+ room.subscribe('event', ({ event }) => {
+ removeStaleReactions()
+ if (event.type === 'reaction') {
window.reactions.remoteReactions.push({
id: generateRandomId(),
emojiId: event.emojiId,
type: event.emoji,
shown: false,
- timestamp: (/* @__PURE__ */ new Date()).getTime(),
+ timestamp: /* @__PURE__ */ new Date().getTime(),
curve: generateRandomCurveForReaction(),
startingAngle: getStartingAngleForReaction()
- });
+ })
}
- });
+ })
}
- run();
-})();
+ run()
+})()
diff --git a/public/styles.css b/public/styles.css
index 21fac85..c4548ff 100644
--- a/public/styles.css
+++ b/public/styles.css
@@ -1,7 +1,5 @@
-
/* ----- THEME ----- */
-
/* RESET */
*,
*::before,
@@ -9,9 +7,9 @@
box-sizing: border-box;
}
* {
- margin: 0;
- padding: 0;
- border: 0 solid transparent;
+ margin: 0;
+ padding: 0;
+ border: 0 solid transparent;
}
html:focus-within {
scroll-behavior: smooth;
@@ -48,7 +46,7 @@ a:not([class]) {
text-decoration-skip-ink: auto;
}
button,
-[role="button"] {
+[role='button'] {
cursor: pointer;
background-color: transparent;
}
@@ -67,7 +65,7 @@ h6 {
}
ul[role='list'],
ol[role='list'] {
- list-style: none;
+ list-style: none;
}
table {
border-collapse: collapse;
@@ -79,7 +77,7 @@ textarea {
}
@media (prefers-reduced-motion: reduce) {
html:focus-within {
- scroll-behavior: auto;
+ scroll-behavior: auto;
}
*,
*::before,
@@ -91,14 +89,22 @@ textarea {
}
}
-
/* TYPEFACE */
-html {font-size: 18px;}
+html {
+ font-size: 18px;
+}
/* FAMILY */
-.font-sans{font-family: system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif;}
-.font-serif{font-family: Georgia,Cambria,Times New Roman,Times,serif;}
-.font-mono{font-family: Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;}
+.font-sans {
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
+ Helvetica Neue, Arial, Noto Sans, sans-serif;
+}
+.font-serif {
+ font-family: Georgia, Cambria, Times New Roman, Times, serif;
+}
+.font-mono {
+ font-family: Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
+}
body {
font-weight: normal;
@@ -108,108 +114,245 @@ body {
-moz-osx-font-smoothing: grayscale;
}
-
/* BACKGROUND */
-.bg-fixed{background-attachment:fixed;}
-.bg-local{background-attachment:local;}
-.bg-scroll{background-attachment:scroll;}
-.bg-bottom{background-position:bottom;}
-.bg-center{background-position:center;}
-.bg-left{background-position:left;}
-.bg-left-bottom{background-position:left bottom;}
-.bg-left-top{background-position:left top;}
-.bg-right{background-position:right;}
-.bg-right-bottom{background-position:right bottom;}
-.bg-right-top{background-position:right top;}
-.bg-top{background-position:top;}
-.bg-repeat{background-repeat:repeat;}
-.bg-no-repeat{background-repeat:no-repeat;}
-.bg-repeat-x{background-repeat:repeat-x;}
-.bg-repeat-y{background-repeat:repeat-y;}
-.bg-repeat-round{background-repeat:round;}
-.bg-repeat-space{background-repeat:space;}
-.bg-auto{background-size:auto;}
-.bg-cover{background-size:cover;}
-.bg-contain{background-size:contain;}
-.bg-unset{background-color:unset;}
-.bg-clip-border{background-clip:border-box;}
-.bg-clip-content{background-clip:content-box;}
-.bg-clip-padding{background-clip:padding-box;}
-.bg-clip-text{
- background-clip:text;
- -webkit-background-clip:text;
- color:transparent;
+.bg-fixed {
+ background-attachment: fixed;
+}
+.bg-local {
+ background-attachment: local;
+}
+.bg-scroll {
+ background-attachment: scroll;
+}
+.bg-bottom {
+ background-position: bottom;
+}
+.bg-center {
+ background-position: center;
+}
+.bg-left {
+ background-position: left;
+}
+.bg-left-bottom {
+ background-position: left bottom;
+}
+.bg-left-top {
+ background-position: left top;
+}
+.bg-right {
+ background-position: right;
+}
+.bg-right-bottom {
+ background-position: right bottom;
+}
+.bg-right-top {
+ background-position: right top;
+}
+.bg-top {
+ background-position: top;
+}
+.bg-repeat {
+ background-repeat: repeat;
+}
+.bg-no-repeat {
+ background-repeat: no-repeat;
+}
+.bg-repeat-x {
+ background-repeat: repeat-x;
+}
+.bg-repeat-y {
+ background-repeat: repeat-y;
+}
+.bg-repeat-round {
+ background-repeat: round;
+}
+.bg-repeat-space {
+ background-repeat: space;
+}
+.bg-auto {
+ background-size: auto;
+}
+.bg-cover {
+ background-size: cover;
+}
+.bg-contain {
+ background-size: contain;
+}
+.bg-unset {
+ background-color: unset;
+}
+.bg-clip-border {
+ background-clip: border-box;
+}
+.bg-clip-content {
+ background-clip: content-box;
+}
+.bg-clip-padding {
+ background-clip: padding-box;
+}
+.bg-clip-text {
+ background-clip: text;
+ -webkit-background-clip: text;
+ color: transparent;
}
-
/* BORDER */
-.border-solid{border-style:solid;}
-.border-dashed{border-style:dashed;}
-.border-dotted{border-style:dotted;}
-.border-double{border-style:double;}
-.border-none{border-style:none;}
-.border-t-none{border-top:none;}
-.border-r-none{border-right:none;}
-.border-b-none{border-bottom:none;}
-.border-l-none{border-left:none;}
-
-.border0{border-width:0px;}
-.border-t0{border-top-width:0px;}
-.border-r0{border-right-width:0px;}
-.border-b0{border-bottom-width:0px;}
-.border-l0{border-left-width:0px;}
-
-.border1{border-width:1px;}
-.border-t1{border-top-width:1px;}
-.border-r1{border-right-width:1px;}
-.border-b1{border-bottom-width:1px;}
-.border-l1{border-left-width:1px;}
-
-.border2{border-width:2px;}
-.border-t2{border-top-width:2px;}
-.border-r2{border-right-width:2px;}
-.border-b2{border-bottom-width:2px;}
-.border-l2{border-left-width:2px;}
-
-.border3{border-width:4px;}
-.border-t3{border-top-width:4px;}
-.border-r3{border-right-width:4px;}
-.border-b3{border-bottom-width:4px;}
-.border-l3{border-left-width:4px;}
+.border-solid {
+ border-style: solid;
+}
+.border-dashed {
+ border-style: dashed;
+}
+.border-dotted {
+ border-style: dotted;
+}
+.border-double {
+ border-style: double;
+}
+.border-none {
+ border-style: none;
+}
+.border-t-none {
+ border-top: none;
+}
+.border-r-none {
+ border-right: none;
+}
+.border-b-none {
+ border-bottom: none;
+}
+.border-l-none {
+ border-left: none;
+}
+
+.border0 {
+ border-width: 0px;
+}
+.border-t0 {
+ border-top-width: 0px;
+}
+.border-r0 {
+ border-right-width: 0px;
+}
+.border-b0 {
+ border-bottom-width: 0px;
+}
+.border-l0 {
+ border-left-width: 0px;
+}
+
+.border1 {
+ border-width: 1px;
+}
+.border-t1 {
+ border-top-width: 1px;
+}
+.border-r1 {
+ border-right-width: 1px;
+}
+.border-b1 {
+ border-bottom-width: 1px;
+}
+.border-l1 {
+ border-left-width: 1px;
+}
+
+.border2 {
+ border-width: 2px;
+}
+.border-t2 {
+ border-top-width: 2px;
+}
+.border-r2 {
+ border-right-width: 2px;
+}
+.border-b2 {
+ border-bottom-width: 2px;
+}
+.border-l2 {
+ border-left-width: 2px;
+}
+.border3 {
+ border-width: 4px;
+}
+.border-t3 {
+ border-top-width: 4px;
+}
+.border-r3 {
+ border-right-width: 4px;
+}
+.border-b3 {
+ border-bottom-width: 4px;
+}
+.border-l3 {
+ border-left-width: 4px;
+}
/* RADIUS */
-.radius-none{border-radius:0;}
-.radius-100{border-radius:100%;}
-.radius-pill{border-radius:9999px;}
+.radius-none {
+ border-radius: 0;
+}
+.radius-100 {
+ border-radius: 100%;
+}
+.radius-pill {
+ border-radius: 9999px;
+}
.radius-t-none,
.radius-r-none,
-.radius-tr-none{border-top-right-radius:0;}
+.radius-tr-none {
+ border-top-right-radius: 0;
+}
.radius-b-none,
.radius-r-none,
-.radius-br-none{border-bottom-right-radius:0;}
+.radius-br-none {
+ border-bottom-right-radius: 0;
+}
.radius-t-none,
.radius-l-none,
-.radius-tl-none{border-top-left-radius:0;}
+.radius-tl-none {
+ border-top-left-radius: 0;
+}
.radius-b-none,
.radius-l-none,
-.radius-bl-none{border-bottom-left-radius:0;}
-.radius0{border-radius:2px;}
-.radius1{border-radius:8px;}
-.radius2{border-radius:16px;}
-.radius3{border-radius:9999px;}
-
+.radius-bl-none {
+ border-bottom-left-radius: 0;
+}
+.radius0 {
+ border-radius: 2px;
+}
+.radius1 {
+ border-radius: 8px;
+}
+.radius2 {
+ border-radius: 16px;
+}
+.radius3 {
+ border-radius: 9999px;
+}
/* COLOR */
-.text-current{color:currentColor}/* current color */
-.text-transparent{color:transparent}/* transparent */
-
-.border-current{border-color:currentColor}/* current color */
-.border-transparent{border-color:transparent}/* transparent */
-
-.background-current{background-color:currentColor}/* current color */
-.background-transparent{background-color:transparent}/* transparent */
-
+.text-current {
+ color: currentColor;
+} /* current color */
+.text-transparent {
+ color: transparent;
+} /* transparent */
+
+.border-current {
+ border-color: currentColor;
+} /* current color */
+.border-transparent {
+ border-color: transparent;
+} /* transparent */
+
+.background-current {
+ background-color: currentColor;
+} /* current color */
+.background-transparent {
+ background-color: transparent;
+} /* transparent */
/* ----- THEME COLORS ----- */
:root {
@@ -225,7 +368,6 @@ body {
--primary-700: hsl(211, 100%, 30%);
--primary-800: hsl(211, 100%, 20%);
--primary-900: hsl(211, 100%, 10%);
-
--secondary-100: hsl(208, 7.3%, 85%);
--secondary-200: hsl(208, 7.3%, 75%);
@@ -236,7 +378,6 @@ body {
--secondary-700: hsl(208, 7.3%, 25%);
--secondary-800: hsl(208, 7.3%, 15%);
--secondary-900: hsl(208, 7.3%, 5%);
-
--success-100: hsl(134, 61.4%, 80%);
--success-200: hsl(134, 61.4%, 70%);
@@ -247,7 +388,6 @@ body {
--success-700: hsl(134, 61.4%, 20%);
--success-800: hsl(134, 61.4%, 10%);
--success-900: hsl(134, 61.4%, 0%);
-
--info-100: hsl(188, 77.8%, 80%);
--info-200: hsl(188, 77.8%, 70%);
@@ -258,7 +398,6 @@ body {
--info-700: hsl(188, 77.8%, 20%);
--info-800: hsl(188, 77.8%, 10%);
--info-900: hsl(188, 77.8%, 0%);
-
--warning-100: hsl(45, 100%, 91%);
--warning-200: hsl(45, 100%, 81%);
@@ -269,7 +408,6 @@ body {
--warning-700: hsl(45, 100%, 31%);
--warning-800: hsl(45, 100%, 21%);
--warning-900: hsl(45, 100%, 11%);
-
--error-100: hsl(354, 70.5%, 93%);
--error-200: hsl(354, 70.5%, 83%);
@@ -280,7 +418,6 @@ body {
--error-700: hsl(354, 70.5%, 33%);
--error-800: hsl(354, 70.5%, 23%);
--error-900: hsl(354, 70.5%, 13%);
-
--grey-100: hsl(0, 0%, 86%);
--grey-200: hsl(0, 0%, 76%);
@@ -291,1030 +428,2540 @@ body {
--grey-700: hsl(0, 0%, 26%);
--grey-800: hsl(0, 0%, 16%);
--grey-900: hsl(0, 0%, 6%);
-
+
--crimson: #eb0052;
--muted: #6c757d;
--white: #fff;
}
/* FILL */
-.fill-none{fill:none}
-.fill-current{fill:currentColor}
-
+.fill-none {
+ fill: none;
+}
+.fill-current {
+ fill: currentColor;
+}
/* STROKE */
-.stroke-none{stroke:none;}
-.stroke-current{stroke:currentColor;}
-
-
-
+.stroke-none {
+ stroke: none;
+}
+.stroke-current {
+ stroke: currentColor;
+}
/* SIZES */
-.text6{font-size:5.61rem;}/* 100.984px */
-.text5{font-size:4.209rem;}/* 75.757px */
-.text4{font-size:3.157rem;}/* 56.832px */
-.text3{font-size:2.369rem;}/* 42.635px */
-.text2{font-size:1.777rem;}/* 31.984px */
-.text1{font-size:1.333rem;}/* 23.994px */
-.text0{font-size:1rem;}/* 18px */
-.text-1{font-size:0.75rem;}/* 13.503px */
-.text-2{font-size:0.563rem;}/* 10.13px */
-.text-3{font-size:0.422rem;}/* 7.599px */
-.text-4{font-size:0.317rem;}/* 5.701px */
-.text-5{font-size:0.238rem;}/* 4.277px */
-.text-6{font-size:0.178rem;}/* 3.208px */
-
+.text6 {
+ font-size: 5.61rem;
+} /* 100.984px */
+.text5 {
+ font-size: 4.209rem;
+} /* 75.757px */
+.text4 {
+ font-size: 3.157rem;
+} /* 56.832px */
+.text3 {
+ font-size: 2.369rem;
+} /* 42.635px */
+.text2 {
+ font-size: 1.777rem;
+} /* 31.984px */
+.text1 {
+ font-size: 1.333rem;
+} /* 23.994px */
+.text0 {
+ font-size: 1rem;
+} /* 18px */
+.text-1 {
+ font-size: 0.75rem;
+} /* 13.503px */
+.text-2 {
+ font-size: 0.563rem;
+} /* 10.13px */
+.text-3 {
+ font-size: 0.422rem;
+} /* 7.599px */
+.text-4 {
+ font-size: 0.317rem;
+} /* 5.701px */
+.text-5 {
+ font-size: 0.238rem;
+} /* 4.277px */
+.text-6 {
+ font-size: 0.178rem;
+} /* 3.208px */
/* Style */
-.italic{font-style:italic;}
-.not-italic{font-style:normal;}
-
+.italic {
+ font-style: italic;
+}
+.not-italic {
+ font-style: normal;
+}
/* LINE HEIGHT */
-.leading5{line-height: 2;}
-.leading4{line-height: 1.625;}
-.leading3{line-height: 1.5;}
-.leading2{line-height: 1.375;}
-.leading1{line-height: 1.25;}
+.leading5 {
+ line-height: 2;
+}
+.leading4 {
+ line-height: 1.625;
+}
+.leading3 {
+ line-height: 1.5;
+}
+.leading2 {
+ line-height: 1.375;
+}
+.leading1 {
+ line-height: 1.25;
+}
.leading0,
-.leading-none{line-height:1;}
-
+.leading-none {
+ line-height: 1;
+}
/* TRACKING */
-.tracking3{letter-spacing: 0.1em;}
-.tracking2{letter-spacing: 0.05em;}
-.tracking1{letter-spacing: 0.025em;}
-.tracking0{letter-spacing: 0;}
-.tracking-1{letter-spacing: -0.025em;}
-.tracking-2{letter-spacing: -0.05em;}
-
+.tracking3 {
+ letter-spacing: 0.1em;
+}
+.tracking2 {
+ letter-spacing: 0.05em;
+}
+.tracking1 {
+ letter-spacing: 0.025em;
+}
+.tracking0 {
+ letter-spacing: 0;
+}
+.tracking-1 {
+ letter-spacing: -0.025em;
+}
+.tracking-2 {
+ letter-spacing: -0.05em;
+}
/* WEIGHTS */
-.font-hairline{font-weight:100;}
-.font-thin{font-weight:200;}
-.font-light{font-weight:300;}
-.font-normal{font-weight:400;}
-.font-medium{font-weight:500;}
-.font-semibold{font-weight:600;}
-.font-bold{font-weight:700;}
-.font-extrabold{font-weight:800;}
-.font-black{font-weight:900;}
-
+.font-hairline {
+ font-weight: 100;
+}
+.font-thin {
+ font-weight: 200;
+}
+.font-light {
+ font-weight: 300;
+}
+.font-normal {
+ font-weight: 400;
+}
+.font-medium {
+ font-weight: 500;
+}
+.font-semibold {
+ font-weight: 600;
+}
+.font-bold {
+ font-weight: 700;
+}
+.font-extrabold {
+ font-weight: 800;
+}
+.font-black {
+ font-weight: 900;
+}
/* TEXT TRANSFORM */
-.uppercase{text-transform:uppercase;}
-.lowercase{text-transform:lowercase;}
-.capitalize{text-transform:capitalize;}
-.normal-case{text-transform:none;}
-
+.uppercase {
+ text-transform: uppercase;
+}
+.lowercase {
+ text-transform: lowercase;
+}
+.capitalize {
+ text-transform: capitalize;
+}
+.normal-case {
+ text-transform: none;
+}
/* ALIGN */
-.text-inherit{text-align:inherit;}
-.text-center{text-align:center;}
-.text-left{text-align:left;}
-.text-right{text-align:right;}
-
+.text-inherit {
+ text-align: inherit;
+}
+.text-center {
+ text-align: center;
+}
+.text-left {
+ text-align: left;
+}
+.text-right {
+ text-align: right;
+}
/* DECORATION */
-.no-underline{text-decoration:none;}
-.underline{text-decoration:underline;}
-.line-through{text-decoration:line-through;}
-
+.no-underline {
+ text-decoration: none;
+}
+.underline {
+ text-decoration: underline;
+}
+.line-through {
+ text-decoration: line-through;
+}
/* LIST */
-.list-none{list-style:none;}
-.list-disc{list-style:disc;}
-.list-decimal{list-style:decimal;}
-
+.list-none {
+ list-style: none;
+}
+.list-disc {
+ list-style: disc;
+}
+.list-decimal {
+ list-style: decimal;
+}
/* WHITESPACE */
-.whitespace-normal{white-space:normal;}
+.whitespace-normal {
+ white-space: normal;
+}
.truncate,
-.whitespace-no-wrap{white-space:nowrap;}
-.whitespace-pre{white-space:pre;}
-.whitespace-pre-line{white-space:pre-line;}
-.whitespace-pre-wrap{white-space:pre-wrap;}
-
+.whitespace-no-wrap {
+ white-space: nowrap;
+}
+.whitespace-pre {
+ white-space: pre;
+}
+.whitespace-pre-line {
+ white-space: pre-line;
+}
+.whitespace-pre-wrap {
+ white-space: pre-wrap;
+}
/* WORDBREAK */
-.break-normal{word-break:normal}
+.break-normal {
+ word-break: normal;
+}
.break-normal,
-.break-word{overflow-wrap:normal}
-.break-all{word-break:break-all}
-.break-keep{word-break:keep-all}
+.break-word {
+ overflow-wrap: normal;
+}
+.break-all {
+ word-break: break-all;
+}
+.break-keep {
+ word-break: keep-all;
+}
.truncate,
-.ellipsis{text-overflow:ellipsis}
-
-
+.ellipsis {
+ text-overflow: ellipsis;
+}
/* ----- LAYOUT ----- */
-
/* POSITION */
-.sticky{position:sticky;}
-.static{position:static;}
-.absolute{position:absolute;}
-.relative{position:relative;}
-.fixed{position:fixed;}
-
-
- /* POSITIONING */
- .trbl,
-.top0{top:0;}
- .trbl,
-.right0{right:0;}
- .trbl,
-.bottom0{bottom:0;}
- .trbl,
-.left0{left:0;}
-
+.sticky {
+ position: sticky;
+}
+.static {
+ position: static;
+}
+.absolute {
+ position: absolute;
+}
+.relative {
+ position: relative;
+}
+.fixed {
+ position: fixed;
+}
-/* DISPLAY */
-.hidden{display:none;}
-.block{display:block;}
-.inline{display:inline;}
-.inline-block{display:inline-block;}
-.flex{display:flex;}
-.inline-flex{display:inline-flex;}
-.grid{display:grid;}
-.inline-grid{display:inline-grid;}
+/* POSITIONING */
+.trbl,
+.top0 {
+ top: 0;
+}
+.trbl,
+.right0 {
+ right: 0;
+}
+.trbl,
+.bottom0 {
+ bottom: 0;
+}
+.trbl,
+.left0 {
+ left: 0;
+}
+/* DISPLAY */
+.hidden {
+ display: none;
+}
+.block {
+ display: block;
+}
+.inline {
+ display: inline;
+}
+.inline-block {
+ display: inline-block;
+}
+.flex {
+ display: flex;
+}
+.inline-flex {
+ display: inline-flex;
+}
+.grid {
+ display: grid;
+}
+.inline-grid {
+ display: inline-grid;
+}
/* WIDTH */
-.w-0{width:0}
-.w-full{width:100%}
-.w-screen{width:100vw}
-.min-w-0{min-width:0}
-.min-w-full{min-width:100%}
-.max-width-none{max-width:none}
-.max-w-full{max-width:100%}
-
+.w-0 {
+ width: 0;
+}
+.w-full {
+ width: 100%;
+}
+.w-screen {
+ width: 100vw;
+}
+.min-w-0 {
+ min-width: 0;
+}
+.min-w-full {
+ min-width: 100%;
+}
+.max-width-none {
+ max-width: none;
+}
+.max-w-full {
+ max-width: 100%;
+}
/* HEIGHT */
-.h-0{height:0;}
-.h-full{height:100%;}
-.h-screen{height:100vh;}
-.min-h-0{min-height:0;}
-.min-h-full{min-height:100%;}
-.min-h-screen{min-height:100vh;}
-.max-h-full{max-height:100%;}
-.max-h-screen{max-height:100vh;}
-
+.h-0 {
+ height: 0;
+}
+.h-full {
+ height: 100%;
+}
+.h-screen {
+ height: 100vh;
+}
+.min-h-0 {
+ min-height: 0;
+}
+.min-h-full {
+ min-height: 100%;
+}
+.min-h-screen {
+ min-height: 100vh;
+}
+.max-h-full {
+ max-height: 100%;
+}
+.max-h-screen {
+ max-height: 100vh;
+}
/* FLEX */
-.flex-1{flex: 1 1 0%;}
-.flex-auto{flex: 1 1 auto;}
-.flex-initial{flex: 0 1 auto;}
-.flex-none{flex:none;}
-.flex-row{flex-direction:row;}
-.flex-row-reverse{flex-direction:row-reverse;}
-.flex-col{flex-direction:column;}
-.flex-col-reverse{flex-direction:column-reverse;}
-.items-stretch{align-items:stretch;}
-.items-start{align-items:flex-start;}
-.items-end{align-items:flex-end;}
-.items-center{align-items:center;}
-.content-start{align-content:start;}
-.content-center{align-content:center;}
-.content-end{align-content:end;}
-.content-between{align-content:space-between;}
-.content-around{align-content:space-around;}
-.self-auto{align-self:auto;}
-.self-start{align-self:flex-start;}
-.self-end{align-self:flex-end;}
-.self-center{align-self:center;}
-.self-stretch{align-self:stretch;}
-.justify-start{justify-content:flex-start;}
-.justify-end{justify-content: flex-end;}
-.justify-around{justify-content:space-around;}
-.justify-between{justify-content:space-between;}
-.justify-center{justify-content:center;}
-.flex-grow{flex-grow:1;}
-.flex-grow-0{flex-grow:0;}
-.flex-shrink{flex-shrink:1;}
-.flex-shrink-0{flex-shrink:0;}
-.flex-wrap{flex-wrap:wrap;}
-.flex-wrap-reverse{flex-wrap:wrap-reverse;}
-.flex-no-wrap{flex-wrap:nowrap;}
-.order-first{order:-9999;}
-.order-last{order:9999;}
-.order-none{order:0;}
-.order-1{order:1;}
-.order-2{order:2;}
-.order-3{order:3;}
-.order-4{order:4;}
-.order-5{order:5;}
-.order-6{order:6;}
-
+.flex-1 {
+ flex: 1 1 0%;
+}
+.flex-auto {
+ flex: 1 1 auto;
+}
+.flex-initial {
+ flex: 0 1 auto;
+}
+.flex-none {
+ flex: none;
+}
+.flex-row {
+ flex-direction: row;
+}
+.flex-row-reverse {
+ flex-direction: row-reverse;
+}
+.flex-col {
+ flex-direction: column;
+}
+.flex-col-reverse {
+ flex-direction: column-reverse;
+}
+.items-stretch {
+ align-items: stretch;
+}
+.items-start {
+ align-items: flex-start;
+}
+.items-end {
+ align-items: flex-end;
+}
+.items-center {
+ align-items: center;
+}
+.content-start {
+ align-content: start;
+}
+.content-center {
+ align-content: center;
+}
+.content-end {
+ align-content: end;
+}
+.content-between {
+ align-content: space-between;
+}
+.content-around {
+ align-content: space-around;
+}
+.self-auto {
+ align-self: auto;
+}
+.self-start {
+ align-self: flex-start;
+}
+.self-end {
+ align-self: flex-end;
+}
+.self-center {
+ align-self: center;
+}
+.self-stretch {
+ align-self: stretch;
+}
+.justify-start {
+ justify-content: flex-start;
+}
+.justify-end {
+ justify-content: flex-end;
+}
+.justify-around {
+ justify-content: space-around;
+}
+.justify-between {
+ justify-content: space-between;
+}
+.justify-center {
+ justify-content: center;
+}
+.flex-grow {
+ flex-grow: 1;
+}
+.flex-grow-0 {
+ flex-grow: 0;
+}
+.flex-shrink {
+ flex-shrink: 1;
+}
+.flex-shrink-0 {
+ flex-shrink: 0;
+}
+.flex-wrap {
+ flex-wrap: wrap;
+}
+.flex-wrap-reverse {
+ flex-wrap: wrap-reverse;
+}
+.flex-no-wrap {
+ flex-wrap: nowrap;
+}
+.order-first {
+ order: -9999;
+}
+.order-last {
+ order: 9999;
+}
+.order-none {
+ order: 0;
+}
+.order-1 {
+ order: 1;
+}
+.order-2 {
+ order: 2;
+}
+.order-3 {
+ order: 3;
+}
+.order-4 {
+ order: 4;
+}
+.order-5 {
+ order: 5;
+}
+.order-6 {
+ order: 6;
+}
/* GRID */
-.flow-row{grid-auto-flow:row;}
-.flow-col{grid-auto-flow:column;}
-.flow-row-dense{grid-auto-flow:row dense;}
-.flow-column-dense{grid-auto-flow:column dense;}
-.row-auto{grid-row:auto;}
-.col-auto{grid-column:auto;}
-.col-end-auto{grid-column-end: auto;}
-.rows-end-auto{grid-row-end:auto;}
-.rows-none{grid-template-rows:none;}
-.col-1{grid-template-columns:repeat(1, minmax(0, 1fr));}
-.col-span-1{grid-column: span 1 / span 1;}
-.col-start-1{grid-column-start: 1;}
-.row-start-1{grid-row-start: 1;}
-.col-end-1{grid-column-end: 1;}
-.row-end-1{grid-row-end: 1;}
-.row-1{grid-template-rows: repeat(1, minmax(0, 1fr));}
-.col-2{grid-template-columns:repeat(2, minmax(0, 1fr));}
-.col-span-2{grid-column: span 2 / span 2;}
-.col-start-2{grid-column-start: 2;}
-.row-start-2{grid-row-start: 2;}
-.col-end-2{grid-column-end: 2;}
-.row-end-2{grid-row-end: 2;}
-.row-2{grid-template-rows: repeat(2, minmax(0, 1fr));}
-.col-3{grid-template-columns:repeat(3, minmax(0, 1fr));}
-.col-span-3{grid-column: span 3 / span 3;}
-.col-start-3{grid-column-start: 3;}
-.row-start-3{grid-row-start: 3;}
-.col-end-3{grid-column-end: 3;}
-.row-end-3{grid-row-end: 3;}
-.row-3{grid-template-rows: repeat(3, minmax(0, 1fr));}
-.col-4{grid-template-columns:repeat(4, minmax(0, 1fr));}
-.col-span-4{grid-column: span 4 / span 4;}
-.col-start-4{grid-column-start: 4;}
-.row-start-4{grid-row-start: 4;}
-.col-end-4{grid-column-end: 4;}
-.row-end-4{grid-row-end: 4;}
-.row-4{grid-template-rows: repeat(4, minmax(0, 1fr));}
-.col-5{grid-template-columns:repeat(5, minmax(0, 1fr));}
-.col-span-5{grid-column: span 5 / span 5;}
-.col-start-5{grid-column-start: 5;}
-.row-start-5{grid-row-start: 5;}
-.col-end-5{grid-column-end: 5;}
-.row-end-5{grid-row-end: 5;}
-.row-5{grid-template-rows: repeat(5, minmax(0, 1fr));}
-.col-6{grid-template-columns:repeat(6, minmax(0, 1fr));}
-.col-span-6{grid-column: span 6 / span 6;}
-.col-start-6{grid-column-start: 6;}
-.row-start-6{grid-row-start: 6;}
-.col-end-6{grid-column-end: 6;}
-.row-end-6{grid-row-end: 6;}
-.row-6{grid-template-rows: repeat(6, minmax(0, 1fr));}
-.gap5{gap:5.61rem;}
-.gap4{gap:4.209rem;}
-.gap3{gap:3.157rem;}
-.gap2{gap:2.369rem;}
-.gap1{gap:1.777rem;}
-.gap0{gap:1.333rem;}
-.gap-1{gap:1rem;}
-.gap-2{gap:0.75rem;}
-.gap-3{gap:0.563rem;}
-.gap-4{gap:0.422rem;}
-.gap-5{gap:0.317rem;}
-.gap-6{gap:0.238rem;}
-.gap-7{gap:0.178rem;}
-
-
-/* Z-INDEX */
-.z-auto{z-index:auto;}
-.z1{z-index:1;}
-.z0{z-index:0;}
-.z-1{z-index:-1;}
-
-
-
-/* MARGIN */
-.m-none{margin:0}
-.my-none,
-.mt-none{margin-top:0}
-.mx-none,
-.mr-none{margin-right:0}
-.my-none,
-.mb-none{margin-bottom:0}
-.mx-none,
-.ml-none{margin-left:0}
-.m-auto{margin-right:auto;margin-left:auto;}
-.mr-auto{margin-right:auto}
-.ml-auto{margin-left:auto}
-.m6{margin:5.61rem}
-.mt6{margin-top:5.61rem}
-.mr6{margin-right:5.61rem}
-.mb6{margin-bottom:5.61rem}
-.ml6{margin-left:5.61rem}
-.m5{margin:4.209rem}
-.mt5{margin-top:4.209rem}
-.mr5{margin-right:4.209rem}
-.mb5{margin-bottom:4.209rem}
-.ml5{margin-left:4.209rem}
-.m4{margin:3.157rem}
-.mt4{margin-top:3.157rem}
-.mr4{margin-right:3.157rem}
-.mb4{margin-bottom:3.157rem}
-.ml4{margin-left:3.157rem}
-.m3{margin:2.369rem}
-.mt3{margin-top:2.369rem}
-.mr3{margin-right:2.369rem}
-.mb3{margin-bottom:2.369rem}
-.ml3{margin-left:2.369rem}
-.m2{margin:1.777rem}
-.mt2{margin-top:1.777rem}
-.mr2{margin-right:1.777rem}
-.mb2{margin-bottom:1.777rem}
-.ml2{margin-left:1.777rem}
-.m1{margin:1.333rem}
-.mt1{margin-top:1.333rem}
-.mr1{margin-right:1.333rem}
-.mb1{margin-bottom:1.333rem}
-.ml1{margin-left:1.333rem}
-.m0{margin:1rem}
-.mt0{margin-top:1rem}
-.mr0{margin-right:1rem}
-.mb0{margin-bottom:1rem}
-.ml0{margin-left:1rem}
-.m-1{margin:0.75rem}
-.mt-1{margin-top:0.75rem}
-.mr-1{margin-right:0.75rem}
-.mb-1{margin-bottom:0.75rem}
-.ml-1{margin-left:0.75rem}
-.m-2{margin:0.563rem}
-.mt-2{margin-top:0.563rem}
-.mr-2{margin-right:0.563rem}
-.mb-2{margin-bottom:0.563rem}
-.ml-2{margin-left:0.563rem}
-.m-3{margin:0.422rem}
-.mt-3{margin-top:0.422rem}
-.mr-3{margin-right:0.422rem}
-.mb-3{margin-bottom:0.422rem}
-.ml-3{margin-left:0.422rem}
-.m-4{margin:0.317rem}
-.mt-4{margin-top:0.317rem}
-.mr-4{margin-right:0.317rem}
-.mb-4{margin-bottom:0.317rem}
-.ml-4{margin-left:0.317rem}
-.m-5{margin:0.238rem}
-.mt-5{margin-top:0.238rem}
-.mr-5{margin-right:0.238rem}
-.mb-5{margin-bottom:0.238rem}
-.ml-5{margin-left:0.238rem}
-.m-6{margin:0.178rem}
-.mt-6{margin-top:0.178rem}
-.mr-6{margin-right:0.178rem}
-.mb-6{margin-bottom:0.178rem}
-.ml-6{margin-left:0.178rem}
-
-
-/* PADDING */
-.p-none{padding:0;}
-.py-none,
-.pt-none{padding-top:0;}
-.px-none,
-.pr-none{padding-right:0;}
-.py-none,
-.pb-none{padding-bottom:0;}
-.px-none,
-.pl-none{padding-left:0;}
-.p6{padding:5.61rem;}
-.pt6{padding-top:5.61rem;}
-.pr6{padding-right:5.61rem;}
-.pb6{padding-bottom:5.61rem;}
-.pl6{padding-left:5.61rem;}
-.p5{padding:4.209rem;}
-.pt5{padding-top:4.209rem;}
-.pr5{padding-right:4.209rem;}
-.pb5{padding-bottom:4.209rem;}
-.pl5{padding-left:4.209rem;}
-.p4{padding:3.157rem;}
-.pt4{padding-top:3.157rem;}
-.pr4{padding-right:3.157rem;}
-.pb4{padding-bottom:3.157rem;}
-.pl4{padding-left:3.157rem;}
-.p3{padding:2.369rem;}
-.pt3{padding-top:2.369rem;}
-.pr3{padding-right:2.369rem;}
-.pb3{padding-bottom:2.369rem;}
-.pl3{padding-left:2.369rem;}
-.p2{padding:1.777rem;}
-.pt2{padding-top:1.777rem;}
-.pr2{padding-right:1.777rem;}
-.pb2{padding-bottom:1.777rem;}
-.pl2{padding-left:1.777rem;}
-.p1{padding:1.333rem;}
-.pt1{padding-top:1.333rem;}
-.pr1{padding-right:1.333rem;}
-.pb1{padding-bottom:1.333rem;}
-.pl1{padding-left:1.333rem;}
-.p0{padding:1rem;}
-.pt0{padding-top:1rem;}
-.pr0{padding-right:1rem;}
-.pb0{padding-bottom:1rem;}
-.pl0{padding-left:1rem;}
-.p-1{padding:0.75rem;}
-.pt-1{padding-top:0.75rem;}
-.pr-1{padding-right:0.75rem;}
-.pb-1{padding-bottom:0.75rem;}
-.pl-1{padding-left:0.75rem;}
-.p-2{padding:0.563rem;}
-.pt-2{padding-top:0.563rem;}
-.pr-2{padding-right:0.563rem;}
-.pb-2{padding-bottom:0.563rem;}
-.pl-2{padding-left:0.563rem;}
-.p-3{padding:0.422rem;}
-.pt-3{padding-top:0.422rem;}
-.pr-3{padding-right:0.422rem;}
-.pb-3{padding-bottom:0.422rem;}
-.pl-3{padding-left:0.422rem;}
-.p-4{padding:0.317rem;}
-.pt-4{padding-top:0.317rem;}
-.pr-4{padding-right:0.317rem;}
-.pb-4{padding-bottom:0.317rem;}
-.pl-4{padding-left:0.317rem;}
-.p-5{padding:0.238rem;}
-.pt-5{padding-top:0.238rem;}
-.pr-5{padding-right:0.238rem;}
-.pb-5{padding-bottom:0.238rem;}
-.pl-5{padding-left:0.238rem;}
-.p-6{padding:0.178rem;}
-.pt-6{padding-top:0.178rem;}
-.pr-6{padding-right:0.178rem;}
-.pb-6{padding-bottom:0.178rem;}
-.pl-6{padding-left:0.178rem;}
-
-
-/* OVERFLOW */
-.overflow-auto{overflow:auto;}
-.truncate,
-.overflow-hidden{overflow:hidden;}
-.overflow-visible{overflow:visible;}
-.overflow-scroll{overflow:scroll;}
-.overflow-x-auto{overflow-x:auto;}
-.overflow-y-auto{overflow-y:auto;}
-.overflow-x-scroll{overflow-x:scroll;}
-.overflow-x-hidden{overflow-x:hidden;}
-.overflow-y-scroll{overflow-y:scroll;}
-.overflow-y-hidden{overflow-y:hidden;}
-.scrolling-touch{-webkit-overflow-scrolling:touch;}
-.scrolling-auto{-webkit-overflow-scrolling:auto;}
-
-
-/* VISIBILITY */
-.invisible{visibility:hidden;}
-.visible{visibility:visible;}
-
-
-/* OBJECT FIT */
-.object-contain{object-fit:contain;}
-.object-cover{object-fit:cover;}
-.object-fill{object-fit:fill;}
-.object-none{object-fit:none;}
-.object-scale-down{object-fit:scale-down;}
-
-
-/* OBJECT POSITION */
-.object-b{object-position:bottom;}
-.object-c{object-position:center;}
-.object-t{object-position:top;}
-.object-r{object-position:right;}
-.object-rt{object-position:right top;}
-.object-rb{object-position:right bottom;}
-.object-l{object-position:left;}
-.object-lt{object-position:left top;}
-.object-lb{object-position:left bottom;}
-
-
-/* OUTLINE */
-.outline-none{outline:0;}
-
-
-/* OPACITY */
-.opacity-0{opacity:0;}
-.opacity-25{opacity:0.25;}
-.opacity-50{opacity:0.5;}
-.opacity-75{opacity:0.75;}
-.opacity-100{opacity:1.0;}
-
-
-/* CURSOR */
-.cursor-auto{cursor:auto;}
-.cursor-default{cursor:default;}
-.cursor-pointer{cursor:pointer;}
-.cursor-wait{cursor:wait;}
-.cursor-text{cursor:text;}
-.cursor-move{cursor:move;}
-.cursor-not-allowed{cursor:not-allowed;}
-.cursor-grab{cursor:grab;}
-.cursor-grabbing{cursor:grabbing;}
-
-
-/* USER SELECT */
-.select-none{user-select:none;}
-.select-text{user-select:text;}
-.select-all{user-select:all;}
-.select-auto{user-select:auto;}
-
-
-@media only screen and (min-width:48em) {
-
-
-
-/* SIZES */
-.text6-lg{font-size:5.61rem;}/* 100.984px */
-.text5-lg{font-size:4.209rem;}/* 75.757px */
-.text4-lg{font-size:3.157rem;}/* 56.832px */
-.text3-lg{font-size:2.369rem;}/* 42.635px */
-.text2-lg{font-size:1.777rem;}/* 31.984px */
-.text1-lg{font-size:1.333rem;}/* 23.994px */
-.text0-lg{font-size:1rem;}/* 18px */
-.text-1-lg{font-size:0.75rem;}/* 13.503px */
-.text-2-lg{font-size:0.563rem;}/* 10.13px */
-.text-3-lg{font-size:0.422rem;}/* 7.599px */
-.text-4-lg{font-size:0.317rem;}/* 5.701px */
-.text-5-lg{font-size:0.238rem;}/* 4.277px */
-.text-6-lg{font-size:0.178rem;}/* 3.208px */
-
-
-/* Style */
-.italic-lg{font-style:italic;}
-.not-italic-lg{font-style:normal;}
-
-
-/* LINE HEIGHT */
-.leading5-lg{line-height: 2;}
-.leading4-lg{line-height: 1.625;}
-.leading3-lg{line-height: 1.5;}
-.leading2-lg{line-height: 1.375;}
-.leading1-lg{line-height: 1.25;}
-.leading0-lg,
-.leading-none-lg{line-height:1;}
-
-
-/* TRACKING */
-.tracking3-lg{letter-spacing: 0.1em;}
-.tracking2-lg{letter-spacing: 0.05em;}
-.tracking1-lg{letter-spacing: 0.025em;}
-.tracking0-lg{letter-spacing: 0;}
-.tracking-1-lg{letter-spacing: -0.025em;}
-.tracking-2-lg{letter-spacing: -0.05em;}
-
-
-/* WEIGHTS */
-.font-hairline-lg{font-weight:100;}
-.font-thin-lg{font-weight:200;}
-.font-light-lg{font-weight:300;}
-.font-normal-lg{font-weight:400;}
-.font-medium-lg{font-weight:500;}
-.font-semibold-lg{font-weight:600;}
-.font-bold-lg{font-weight:700;}
-.font-extrabold-lg{font-weight:800;}
-.font-black-lg{font-weight:900;}
-
-
-/* TEXT TRANSFORM */
-.uppercase-lg{text-transform:uppercase;}
-.lowercase-lg{text-transform:lowercase;}
-.capitalize-lg{text-transform:capitalize;}
-.normal-case-lg{text-transform:none;}
-
+.flow-row {
+ grid-auto-flow: row;
+}
+.flow-col {
+ grid-auto-flow: column;
+}
+.flow-row-dense {
+ grid-auto-flow: row dense;
+}
+.flow-column-dense {
+ grid-auto-flow: column dense;
+}
+.row-auto {
+ grid-row: auto;
+}
+.col-auto {
+ grid-column: auto;
+}
+.col-end-auto {
+ grid-column-end: auto;
+}
+.rows-end-auto {
+ grid-row-end: auto;
+}
+.rows-none {
+ grid-template-rows: none;
+}
+.col-1 {
+ grid-template-columns: repeat(1, minmax(0, 1fr));
+}
+.col-span-1 {
+ grid-column: span 1 / span 1;
+}
+.col-start-1 {
+ grid-column-start: 1;
+}
+.row-start-1 {
+ grid-row-start: 1;
+}
+.col-end-1 {
+ grid-column-end: 1;
+}
+.row-end-1 {
+ grid-row-end: 1;
+}
+.row-1 {
+ grid-template-rows: repeat(1, minmax(0, 1fr));
+}
+.col-2 {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+}
+.col-span-2 {
+ grid-column: span 2 / span 2;
+}
+.col-start-2 {
+ grid-column-start: 2;
+}
+.row-start-2 {
+ grid-row-start: 2;
+}
+.col-end-2 {
+ grid-column-end: 2;
+}
+.row-end-2 {
+ grid-row-end: 2;
+}
+.row-2 {
+ grid-template-rows: repeat(2, minmax(0, 1fr));
+}
+.col-3 {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+}
+.col-span-3 {
+ grid-column: span 3 / span 3;
+}
+.col-start-3 {
+ grid-column-start: 3;
+}
+.row-start-3 {
+ grid-row-start: 3;
+}
+.col-end-3 {
+ grid-column-end: 3;
+}
+.row-end-3 {
+ grid-row-end: 3;
+}
+.row-3 {
+ grid-template-rows: repeat(3, minmax(0, 1fr));
+}
+.col-4 {
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+}
+.col-span-4 {
+ grid-column: span 4 / span 4;
+}
+.col-start-4 {
+ grid-column-start: 4;
+}
+.row-start-4 {
+ grid-row-start: 4;
+}
+.col-end-4 {
+ grid-column-end: 4;
+}
+.row-end-4 {
+ grid-row-end: 4;
+}
+.row-4 {
+ grid-template-rows: repeat(4, minmax(0, 1fr));
+}
+.col-5 {
+ grid-template-columns: repeat(5, minmax(0, 1fr));
+}
+.col-span-5 {
+ grid-column: span 5 / span 5;
+}
+.col-start-5 {
+ grid-column-start: 5;
+}
+.row-start-5 {
+ grid-row-start: 5;
+}
+.col-end-5 {
+ grid-column-end: 5;
+}
+.row-end-5 {
+ grid-row-end: 5;
+}
+.row-5 {
+ grid-template-rows: repeat(5, minmax(0, 1fr));
+}
+.col-6 {
+ grid-template-columns: repeat(6, minmax(0, 1fr));
+}
+.col-span-6 {
+ grid-column: span 6 / span 6;
+}
+.col-start-6 {
+ grid-column-start: 6;
+}
+.row-start-6 {
+ grid-row-start: 6;
+}
+.col-end-6 {
+ grid-column-end: 6;
+}
+.row-end-6 {
+ grid-row-end: 6;
+}
+.row-6 {
+ grid-template-rows: repeat(6, minmax(0, 1fr));
+}
+.gap5 {
+ gap: 5.61rem;
+}
+.gap4 {
+ gap: 4.209rem;
+}
+.gap3 {
+ gap: 3.157rem;
+}
+.gap2 {
+ gap: 2.369rem;
+}
+.gap1 {
+ gap: 1.777rem;
+}
+.gap0 {
+ gap: 1.333rem;
+}
+.gap-1 {
+ gap: 1rem;
+}
+.gap-2 {
+ gap: 0.75rem;
+}
+.gap-3 {
+ gap: 0.563rem;
+}
+.gap-4 {
+ gap: 0.422rem;
+}
+.gap-5 {
+ gap: 0.317rem;
+}
+.gap-6 {
+ gap: 0.238rem;
+}
+.gap-7 {
+ gap: 0.178rem;
+}
-/* ALIGN */
-.text-inherit-lg{text-align:inherit;}
-.text-center-lg{text-align:center;}
-.text-left-lg{text-align:left;}
-.text-right-lg{text-align:right;}
-
+/* Z-INDEX */
+.z-auto {
+ z-index: auto;
+}
+.z1 {
+ z-index: 1;
+}
+.z0 {
+ z-index: 0;
+}
+.z-1 {
+ z-index: -1;
+}
-/* DECORATION */
-.no-underline-lg{text-decoration:none;}
-.underline-lg{text-decoration:underline;}
-.line-through-lg{text-decoration:line-through;}
-
+/* MARGIN */
+.m-none {
+ margin: 0;
+}
+.my-none,
+.mt-none {
+ margin-top: 0;
+}
+.mx-none,
+.mr-none {
+ margin-right: 0;
+}
+.my-none,
+.mb-none {
+ margin-bottom: 0;
+}
+.mx-none,
+.ml-none {
+ margin-left: 0;
+}
+.m-auto {
+ margin-right: auto;
+ margin-left: auto;
+}
+.mr-auto {
+ margin-right: auto;
+}
+.ml-auto {
+ margin-left: auto;
+}
+.m6 {
+ margin: 5.61rem;
+}
+.mt6 {
+ margin-top: 5.61rem;
+}
+.mr6 {
+ margin-right: 5.61rem;
+}
+.mb6 {
+ margin-bottom: 5.61rem;
+}
+.ml6 {
+ margin-left: 5.61rem;
+}
+.m5 {
+ margin: 4.209rem;
+}
+.mt5 {
+ margin-top: 4.209rem;
+}
+.mr5 {
+ margin-right: 4.209rem;
+}
+.mb5 {
+ margin-bottom: 4.209rem;
+}
+.ml5 {
+ margin-left: 4.209rem;
+}
+.m4 {
+ margin: 3.157rem;
+}
+.mt4 {
+ margin-top: 3.157rem;
+}
+.mr4 {
+ margin-right: 3.157rem;
+}
+.mb4 {
+ margin-bottom: 3.157rem;
+}
+.ml4 {
+ margin-left: 3.157rem;
+}
+.m3 {
+ margin: 2.369rem;
+}
+.mt3 {
+ margin-top: 2.369rem;
+}
+.mr3 {
+ margin-right: 2.369rem;
+}
+.mb3 {
+ margin-bottom: 2.369rem;
+}
+.ml3 {
+ margin-left: 2.369rem;
+}
+.m2 {
+ margin: 1.777rem;
+}
+.mt2 {
+ margin-top: 1.777rem;
+}
+.mr2 {
+ margin-right: 1.777rem;
+}
+.mb2 {
+ margin-bottom: 1.777rem;
+}
+.ml2 {
+ margin-left: 1.777rem;
+}
+.m1 {
+ margin: 1.333rem;
+}
+.mt1 {
+ margin-top: 1.333rem;
+}
+.mr1 {
+ margin-right: 1.333rem;
+}
+.mb1 {
+ margin-bottom: 1.333rem;
+}
+.ml1 {
+ margin-left: 1.333rem;
+}
+.m0 {
+ margin: 1rem;
+}
+.mt0 {
+ margin-top: 1rem;
+}
+.mr0 {
+ margin-right: 1rem;
+}
+.mb0 {
+ margin-bottom: 1rem;
+}
+.ml0 {
+ margin-left: 1rem;
+}
+.m-1 {
+ margin: 0.75rem;
+}
+.mt-1 {
+ margin-top: 0.75rem;
+}
+.mr-1 {
+ margin-right: 0.75rem;
+}
+.mb-1 {
+ margin-bottom: 0.75rem;
+}
+.ml-1 {
+ margin-left: 0.75rem;
+}
+.m-2 {
+ margin: 0.563rem;
+}
+.mt-2 {
+ margin-top: 0.563rem;
+}
+.mr-2 {
+ margin-right: 0.563rem;
+}
+.mb-2 {
+ margin-bottom: 0.563rem;
+}
+.ml-2 {
+ margin-left: 0.563rem;
+}
+.m-3 {
+ margin: 0.422rem;
+}
+.mt-3 {
+ margin-top: 0.422rem;
+}
+.mr-3 {
+ margin-right: 0.422rem;
+}
+.mb-3 {
+ margin-bottom: 0.422rem;
+}
+.ml-3 {
+ margin-left: 0.422rem;
+}
+.m-4 {
+ margin: 0.317rem;
+}
+.mt-4 {
+ margin-top: 0.317rem;
+}
+.mr-4 {
+ margin-right: 0.317rem;
+}
+.mb-4 {
+ margin-bottom: 0.317rem;
+}
+.ml-4 {
+ margin-left: 0.317rem;
+}
+.m-5 {
+ margin: 0.238rem;
+}
+.mt-5 {
+ margin-top: 0.238rem;
+}
+.mr-5 {
+ margin-right: 0.238rem;
+}
+.mb-5 {
+ margin-bottom: 0.238rem;
+}
+.ml-5 {
+ margin-left: 0.238rem;
+}
+.m-6 {
+ margin: 0.178rem;
+}
+.mt-6 {
+ margin-top: 0.178rem;
+}
+.mr-6 {
+ margin-right: 0.178rem;
+}
+.mb-6 {
+ margin-bottom: 0.178rem;
+}
+.ml-6 {
+ margin-left: 0.178rem;
+}
-/* LIST */
-.list-none-lg{list-style:none;}
-.list-disc-lg{list-style:disc;}
-.list-decimal-lg{list-style:decimal;}
+/* PADDING */
+.p-none {
+ padding: 0;
+}
+.py-none,
+.pt-none {
+ padding-top: 0;
+}
+.px-none,
+.pr-none {
+ padding-right: 0;
+}
+.py-none,
+.pb-none {
+ padding-bottom: 0;
+}
+.px-none,
+.pl-none {
+ padding-left: 0;
+}
+.p6 {
+ padding: 5.61rem;
+}
+.pt6 {
+ padding-top: 5.61rem;
+}
+.pr6 {
+ padding-right: 5.61rem;
+}
+.pb6 {
+ padding-bottom: 5.61rem;
+}
+.pl6 {
+ padding-left: 5.61rem;
+}
+.p5 {
+ padding: 4.209rem;
+}
+.pt5 {
+ padding-top: 4.209rem;
+}
+.pr5 {
+ padding-right: 4.209rem;
+}
+.pb5 {
+ padding-bottom: 4.209rem;
+}
+.pl5 {
+ padding-left: 4.209rem;
+}
+.p4 {
+ padding: 3.157rem;
+}
+.pt4 {
+ padding-top: 3.157rem;
+}
+.pr4 {
+ padding-right: 3.157rem;
+}
+.pb4 {
+ padding-bottom: 3.157rem;
+}
+.pl4 {
+ padding-left: 3.157rem;
+}
+.p3 {
+ padding: 2.369rem;
+}
+.pt3 {
+ padding-top: 2.369rem;
+}
+.pr3 {
+ padding-right: 2.369rem;
+}
+.pb3 {
+ padding-bottom: 2.369rem;
+}
+.pl3 {
+ padding-left: 2.369rem;
+}
+.p2 {
+ padding: 1.777rem;
+}
+.pt2 {
+ padding-top: 1.777rem;
+}
+.pr2 {
+ padding-right: 1.777rem;
+}
+.pb2 {
+ padding-bottom: 1.777rem;
+}
+.pl2 {
+ padding-left: 1.777rem;
+}
+.p1 {
+ padding: 1.333rem;
+}
+.pt1 {
+ padding-top: 1.333rem;
+}
+.pr1 {
+ padding-right: 1.333rem;
+}
+.pb1 {
+ padding-bottom: 1.333rem;
+}
+.pl1 {
+ padding-left: 1.333rem;
+}
+.p0 {
+ padding: 1rem;
+}
+.pt0 {
+ padding-top: 1rem;
+}
+.pr0 {
+ padding-right: 1rem;
+}
+.pb0 {
+ padding-bottom: 1rem;
+}
+.pl0 {
+ padding-left: 1rem;
+}
+.p-1 {
+ padding: 0.75rem;
+}
+.pt-1 {
+ padding-top: 0.75rem;
+}
+.pr-1 {
+ padding-right: 0.75rem;
+}
+.pb-1 {
+ padding-bottom: 0.75rem;
+}
+.pl-1 {
+ padding-left: 0.75rem;
+}
+.p-2 {
+ padding: 0.563rem;
+}
+.pt-2 {
+ padding-top: 0.563rem;
+}
+.pr-2 {
+ padding-right: 0.563rem;
+}
+.pb-2 {
+ padding-bottom: 0.563rem;
+}
+.pl-2 {
+ padding-left: 0.563rem;
+}
+.p-3 {
+ padding: 0.422rem;
+}
+.pt-3 {
+ padding-top: 0.422rem;
+}
+.pr-3 {
+ padding-right: 0.422rem;
+}
+.pb-3 {
+ padding-bottom: 0.422rem;
+}
+.pl-3 {
+ padding-left: 0.422rem;
+}
+.p-4 {
+ padding: 0.317rem;
+}
+.pt-4 {
+ padding-top: 0.317rem;
+}
+.pr-4 {
+ padding-right: 0.317rem;
+}
+.pb-4 {
+ padding-bottom: 0.317rem;
+}
+.pl-4 {
+ padding-left: 0.317rem;
+}
+.p-5 {
+ padding: 0.238rem;
+}
+.pt-5 {
+ padding-top: 0.238rem;
+}
+.pr-5 {
+ padding-right: 0.238rem;
+}
+.pb-5 {
+ padding-bottom: 0.238rem;
+}
+.pl-5 {
+ padding-left: 0.238rem;
+}
+.p-6 {
+ padding: 0.178rem;
+}
+.pt-6 {
+ padding-top: 0.178rem;
+}
+.pr-6 {
+ padding-right: 0.178rem;
+}
+.pb-6 {
+ padding-bottom: 0.178rem;
+}
+.pl-6 {
+ padding-left: 0.178rem;
+}
+/* OVERFLOW */
+.overflow-auto {
+ overflow: auto;
+}
+.truncate,
+.overflow-hidden {
+ overflow: hidden;
+}
+.overflow-visible {
+ overflow: visible;
+}
+.overflow-scroll {
+ overflow: scroll;
+}
+.overflow-x-auto {
+ overflow-x: auto;
+}
+.overflow-y-auto {
+ overflow-y: auto;
+}
+.overflow-x-scroll {
+ overflow-x: scroll;
+}
+.overflow-x-hidden {
+ overflow-x: hidden;
+}
+.overflow-y-scroll {
+ overflow-y: scroll;
+}
+.overflow-y-hidden {
+ overflow-y: hidden;
+}
+.scrolling-touch {
+ -webkit-overflow-scrolling: touch;
+}
+.scrolling-auto {
+ -webkit-overflow-scrolling: auto;
+}
-/* WHITESPACE */
-.whitespace-normal-lg{white-space:normal;}
-.truncate-lg,
-.whitespace-no-wrap-lg{white-space:nowrap;}
-.whitespace-pre-lg{white-space:pre;}
-.whitespace-pre-line-lg{white-space:pre-line;}
-.whitespace-pre-wrap-lg{white-space:pre-wrap;}
-
+/* VISIBILITY */
+.invisible {
+ visibility: hidden;
+}
+.visible {
+ visibility: visible;
+}
-/* WORDBREAK */
-.break-normal-lg{word-break:normal}
-.break-normal-lg,
-.break-word-lg{overflow-wrap:normal}
-.break-all-lg{word-break:break-all}
-.break-keep-lg{word-break:keep-all}
-.truncate-lg,
-.ellipsis-lg{text-overflow:ellipsis}
-
-
+/* OBJECT FIT */
+.object-contain {
+ object-fit: contain;
+}
+.object-cover {
+ object-fit: cover;
+}
+.object-fill {
+ object-fit: fill;
+}
+.object-none {
+ object-fit: none;
+}
+.object-scale-down {
+ object-fit: scale-down;
+}
-/* ----- LAYOUT ----- */
+/* OBJECT POSITION */
+.object-b {
+ object-position: bottom;
+}
+.object-c {
+ object-position: center;
+}
+.object-t {
+ object-position: top;
+}
+.object-r {
+ object-position: right;
+}
+.object-rt {
+ object-position: right top;
+}
+.object-rb {
+ object-position: right bottom;
+}
+.object-l {
+ object-position: left;
+}
+.object-lt {
+ object-position: left top;
+}
+.object-lb {
+ object-position: left bottom;
+}
+/* OUTLINE */
+.outline-none {
+ outline: 0;
+}
-/* POSITION */
-.sticky-lg{position:sticky;}
-.static-lg{position:static;}
-.absolute-lg{position:absolute;}
-.relative-lg{position:relative;}
-.fixed-lg{position:fixed;}
-
-
- /* POSITIONING */
- .trbl-lg,
-.top0-lg{top:0;}
- .trbl-lg,
-.right0-lg{right:0;}
- .trbl-lg,
-.bottom0-lg{bottom:0;}
- .trbl-lg,
-.left0-lg{left:0;}
-
+/* OPACITY */
+.opacity-0 {
+ opacity: 0;
+}
+.opacity-25 {
+ opacity: 0.25;
+}
+.opacity-50 {
+ opacity: 0.5;
+}
+.opacity-75 {
+ opacity: 0.75;
+}
+.opacity-100 {
+ opacity: 1;
+}
-/* DISPLAY */
-.hidden-lg{display:none;}
-.block-lg{display:block;}
-.inline-lg{display:inline;}
-.inline-block-lg{display:inline-block;}
-.flex-lg{display:flex;}
-.inline-flex-lg{display:inline-flex;}
-.grid-lg{display:grid;}
-.inline-grid-lg{display:inline-grid;}
+/* CURSOR */
+.cursor-auto {
+ cursor: auto;
+}
+.cursor-default {
+ cursor: default;
+}
+.cursor-pointer {
+ cursor: pointer;
+}
+.cursor-wait {
+ cursor: wait;
+}
+.cursor-text {
+ cursor: text;
+}
+.cursor-move {
+ cursor: move;
+}
+.cursor-not-allowed {
+ cursor: not-allowed;
+}
+.cursor-grab {
+ cursor: grab;
+}
+.cursor-grabbing {
+ cursor: grabbing;
+}
+/* USER SELECT */
+.select-none {
+ user-select: none;
+}
+.select-text {
+ user-select: text;
+}
+.select-all {
+ user-select: all;
+}
+.select-auto {
+ user-select: auto;
+}
-/* WIDTH */
-.w-0-lg{width:0}
-.w-full-lg{width:100%}
-.w-screen-lg{width:100vw}
-.min-w-0-lg{min-width:0}
-.min-w-full-lg{min-width:100%}
-.max-width-none-lg{max-width:none}
-.max-w-full-lg{max-width:100%}
+@media only screen and (min-width: 48em) {
+ /* SIZES */
+ .text6-lg {
+ font-size: 5.61rem;
+ } /* 100.984px */
+ .text5-lg {
+ font-size: 4.209rem;
+ } /* 75.757px */
+ .text4-lg {
+ font-size: 3.157rem;
+ } /* 56.832px */
+ .text3-lg {
+ font-size: 2.369rem;
+ } /* 42.635px */
+ .text2-lg {
+ font-size: 1.777rem;
+ } /* 31.984px */
+ .text1-lg {
+ font-size: 1.333rem;
+ } /* 23.994px */
+ .text0-lg {
+ font-size: 1rem;
+ } /* 18px */
+ .text-1-lg {
+ font-size: 0.75rem;
+ } /* 13.503px */
+ .text-2-lg {
+ font-size: 0.563rem;
+ } /* 10.13px */
+ .text-3-lg {
+ font-size: 0.422rem;
+ } /* 7.599px */
+ .text-4-lg {
+ font-size: 0.317rem;
+ } /* 5.701px */
+ .text-5-lg {
+ font-size: 0.238rem;
+ } /* 4.277px */
+ .text-6-lg {
+ font-size: 0.178rem;
+ } /* 3.208px */
+
+ /* Style */
+ .italic-lg {
+ font-style: italic;
+ }
+ .not-italic-lg {
+ font-style: normal;
+ }
+ /* LINE HEIGHT */
+ .leading5-lg {
+ line-height: 2;
+ }
+ .leading4-lg {
+ line-height: 1.625;
+ }
+ .leading3-lg {
+ line-height: 1.5;
+ }
+ .leading2-lg {
+ line-height: 1.375;
+ }
+ .leading1-lg {
+ line-height: 1.25;
+ }
+ .leading0-lg,
+ .leading-none-lg {
+ line-height: 1;
+ }
-/* HEIGHT */
-.h-0-lg{height:0;}
-.h-full-lg{height:100%;}
-.h-screen-lg{height:100vh;}
-.min-h-0-lg{min-height:0;}
-.min-h-full-lg{min-height:100%;}
-.min-h-screen-lg{min-height:100vh;}
-.max-h-full-lg{max-height:100%;}
-.max-h-screen-lg{max-height:100vh;}
+ /* TRACKING */
+ .tracking3-lg {
+ letter-spacing: 0.1em;
+ }
+ .tracking2-lg {
+ letter-spacing: 0.05em;
+ }
+ .tracking1-lg {
+ letter-spacing: 0.025em;
+ }
+ .tracking0-lg {
+ letter-spacing: 0;
+ }
+ .tracking-1-lg {
+ letter-spacing: -0.025em;
+ }
+ .tracking-2-lg {
+ letter-spacing: -0.05em;
+ }
+ /* WEIGHTS */
+ .font-hairline-lg {
+ font-weight: 100;
+ }
+ .font-thin-lg {
+ font-weight: 200;
+ }
+ .font-light-lg {
+ font-weight: 300;
+ }
+ .font-normal-lg {
+ font-weight: 400;
+ }
+ .font-medium-lg {
+ font-weight: 500;
+ }
+ .font-semibold-lg {
+ font-weight: 600;
+ }
+ .font-bold-lg {
+ font-weight: 700;
+ }
+ .font-extrabold-lg {
+ font-weight: 800;
+ }
+ .font-black-lg {
+ font-weight: 900;
+ }
-/* FLEX */
-.flex-1-lg{flex: 1 1 0%;}
-.flex-auto-lg{flex: 1 1 auto;}
-.flex-initial-lg{flex: 0 1 auto;}
-.flex-none-lg{flex:none;}
-.flex-row-lg{flex-direction:row;}
-.flex-row-reverse-lg{flex-direction:row-reverse;}
-.flex-col-lg{flex-direction:column;}
-.flex-col-reverse-lg{flex-direction:column-reverse;}
-.items-stretch-lg{align-items:stretch;}
-.items-start-lg{align-items:flex-start;}
-.items-end-lg{align-items:flex-end;}
-.items-center-lg{align-items:center;}
-.content-start-lg{align-content:start;}
-.content-center-lg{align-content:center;}
-.content-end-lg{align-content:end;}
-.content-between-lg{align-content:space-between;}
-.content-around-lg{align-content:space-around;}
-.self-auto-lg{align-self:auto;}
-.self-start-lg{align-self:flex-start;}
-.self-end-lg{align-self:flex-end;}
-.self-center-lg{align-self:center;}
-.self-stretch-lg{align-self:stretch;}
-.justify-start-lg{justify-content:flex-start;}
-.justify-end-lg{justify-content: flex-end;}
-.justify-around-lg{justify-content:space-around;}
-.justify-between-lg{justify-content:space-between;}
-.justify-center-lg{justify-content:center;}
-.flex-grow-lg{flex-grow:1;}
-.flex-grow-0-lg{flex-grow:0;}
-.flex-shrink-lg{flex-shrink:1;}
-.flex-shrink-0-lg{flex-shrink:0;}
-.flex-wrap-lg{flex-wrap:wrap;}
-.flex-wrap-reverse-lg{flex-wrap:wrap-reverse;}
-.flex-no-wrap-lg{flex-wrap:nowrap;}
-.order-first-lg{order:-9999;}
-.order-last-lg{order:9999;}
-.order-none-lg{order:0;}
-.order-1-lg{order:1;}
-.order-2-lg{order:2;}
-.order-3-lg{order:3;}
-.order-4-lg{order:4;}
-.order-5-lg{order:5;}
-.order-6-lg{order:6;}
+ /* TEXT TRANSFORM */
+ .uppercase-lg {
+ text-transform: uppercase;
+ }
+ .lowercase-lg {
+ text-transform: lowercase;
+ }
+ .capitalize-lg {
+ text-transform: capitalize;
+ }
+ .normal-case-lg {
+ text-transform: none;
+ }
+ /* ALIGN */
+ .text-inherit-lg {
+ text-align: inherit;
+ }
+ .text-center-lg {
+ text-align: center;
+ }
+ .text-left-lg {
+ text-align: left;
+ }
+ .text-right-lg {
+ text-align: right;
+ }
-/* GRID */
-.flow-row-lg{grid-auto-flow:row;}
-.flow-col-lg{grid-auto-flow:column;}
-.flow-row-dense-lg{grid-auto-flow:row dense;}
-.flow-column-dense-lg{grid-auto-flow:column dense;}
-.row-auto-lg{grid-row:auto;}
-.col-auto-lg{grid-column:auto;}
-.col-end-auto-lg{grid-column-end: auto;}
-.rows-end-auto-lg{grid-row-end:auto;}
-.rows-none-lg{grid-template-rows:none;}
-.col-1-lg{grid-template-columns:repeat(1, minmax(0, 1fr));}
-.col-span-1-lg{grid-column: span 1 / span 1;}
-.col-start-1-lg{grid-column-start: 1;}
-.row-start-1-lg{grid-row-start: 1;}
-.col-end-1-lg{grid-column-end: 1;}
-.row-end-1-lg{grid-row-end: 1;}
-.row-1-lg{grid-template-rows: repeat(1, minmax(0, 1fr));}
-.col-2-lg{grid-template-columns:repeat(2, minmax(0, 1fr));}
-.col-span-2-lg{grid-column: span 2 / span 2;}
-.col-start-2-lg{grid-column-start: 2;}
-.row-start-2-lg{grid-row-start: 2;}
-.col-end-2-lg{grid-column-end: 2;}
-.row-end-2-lg{grid-row-end: 2;}
-.row-2-lg{grid-template-rows: repeat(2, minmax(0, 1fr));}
-.col-3-lg{grid-template-columns:repeat(3, minmax(0, 1fr));}
-.col-span-3-lg{grid-column: span 3 / span 3;}
-.col-start-3-lg{grid-column-start: 3;}
-.row-start-3-lg{grid-row-start: 3;}
-.col-end-3-lg{grid-column-end: 3;}
-.row-end-3-lg{grid-row-end: 3;}
-.row-3-lg{grid-template-rows: repeat(3, minmax(0, 1fr));}
-.col-4-lg{grid-template-columns:repeat(4, minmax(0, 1fr));}
-.col-span-4-lg{grid-column: span 4 / span 4;}
-.col-start-4-lg{grid-column-start: 4;}
-.row-start-4-lg{grid-row-start: 4;}
-.col-end-4-lg{grid-column-end: 4;}
-.row-end-4-lg{grid-row-end: 4;}
-.row-4-lg{grid-template-rows: repeat(4, minmax(0, 1fr));}
-.col-5-lg{grid-template-columns:repeat(5, minmax(0, 1fr));}
-.col-span-5-lg{grid-column: span 5 / span 5;}
-.col-start-5-lg{grid-column-start: 5;}
-.row-start-5-lg{grid-row-start: 5;}
-.col-end-5-lg{grid-column-end: 5;}
-.row-end-5-lg{grid-row-end: 5;}
-.row-5-lg{grid-template-rows: repeat(5, minmax(0, 1fr));}
-.col-6-lg{grid-template-columns:repeat(6, minmax(0, 1fr));}
-.col-span-6-lg{grid-column: span 6 / span 6;}
-.col-start-6-lg{grid-column-start: 6;}
-.row-start-6-lg{grid-row-start: 6;}
-.col-end-6-lg{grid-column-end: 6;}
-.row-end-6-lg{grid-row-end: 6;}
-.row-6-lg{grid-template-rows: repeat(6, minmax(0, 1fr));}
-.gap5-lg{gap:5.61rem;}
-.gap4-lg{gap:4.209rem;}
-.gap3-lg{gap:3.157rem;}
-.gap2-lg{gap:2.369rem;}
-.gap1-lg{gap:1.777rem;}
-.gap0-lg{gap:1.333rem;}
-.gap-1-lg{gap:1rem;}
-.gap-2-lg{gap:0.75rem;}
-.gap-3-lg{gap:0.563rem;}
-.gap-4-lg{gap:0.422rem;}
-.gap-5-lg{gap:0.317rem;}
-.gap-6-lg{gap:0.238rem;}
-.gap-7-lg{gap:0.178rem;}
+ /* DECORATION */
+ .no-underline-lg {
+ text-decoration: none;
+ }
+ .underline-lg {
+ text-decoration: underline;
+ }
+ .line-through-lg {
+ text-decoration: line-through;
+ }
+ /* LIST */
+ .list-none-lg {
+ list-style: none;
+ }
+ .list-disc-lg {
+ list-style: disc;
+ }
+ .list-decimal-lg {
+ list-style: decimal;
+ }
-/* Z-INDEX */
-.z-auto-lg{z-index:auto;}
-.z1-lg{z-index:1;}
-.z0-lg{z-index:0;}
-.z-1-lg{z-index:-1;}
+ /* WHITESPACE */
+ .whitespace-normal-lg {
+ white-space: normal;
+ }
+ .truncate-lg,
+ .whitespace-no-wrap-lg {
+ white-space: nowrap;
+ }
+ .whitespace-pre-lg {
+ white-space: pre;
+ }
+ .whitespace-pre-line-lg {
+ white-space: pre-line;
+ }
+ .whitespace-pre-wrap-lg {
+ white-space: pre-wrap;
+ }
+ /* WORDBREAK */
+ .break-normal-lg {
+ word-break: normal;
+ }
+ .break-normal-lg,
+ .break-word-lg {
+ overflow-wrap: normal;
+ }
+ .break-all-lg {
+ word-break: break-all;
+ }
+ .break-keep-lg {
+ word-break: keep-all;
+ }
+ .truncate-lg,
+ .ellipsis-lg {
+ text-overflow: ellipsis;
+ }
+ /* ----- LAYOUT ----- */
-/* MARGIN */
-.m-none-lg{margin:0}
-.my-none-lg,
-.mt-none-lg{margin-top:0}
-.mx-none-lg,
-.mr-none-lg{margin-right:0}
-.my-none-lg,
-.mb-none-lg{margin-bottom:0}
-.mx-none-lg,
-.ml-none-lg{margin-left:0}
-.m-auto-lg{margin-right:auto;margin-left:auto;}
-.mr-auto-lg{margin-right:auto}
-.ml-auto-lg{margin-left:auto}
-.m6-lg{margin:5.61rem}
-.mt6-lg{margin-top:5.61rem}
-.mr6-lg{margin-right:5.61rem}
-.mb6-lg{margin-bottom:5.61rem}
-.ml6-lg{margin-left:5.61rem}
-.m5-lg{margin:4.209rem}
-.mt5-lg{margin-top:4.209rem}
-.mr5-lg{margin-right:4.209rem}
-.mb5-lg{margin-bottom:4.209rem}
-.ml5-lg{margin-left:4.209rem}
-.m4-lg{margin:3.157rem}
-.mt4-lg{margin-top:3.157rem}
-.mr4-lg{margin-right:3.157rem}
-.mb4-lg{margin-bottom:3.157rem}
-.ml4-lg{margin-left:3.157rem}
-.m3-lg{margin:2.369rem}
-.mt3-lg{margin-top:2.369rem}
-.mr3-lg{margin-right:2.369rem}
-.mb3-lg{margin-bottom:2.369rem}
-.ml3-lg{margin-left:2.369rem}
-.m2-lg{margin:1.777rem}
-.mt2-lg{margin-top:1.777rem}
-.mr2-lg{margin-right:1.777rem}
-.mb2-lg{margin-bottom:1.777rem}
-.ml2-lg{margin-left:1.777rem}
-.m1-lg{margin:1.333rem}
-.mt1-lg{margin-top:1.333rem}
-.mr1-lg{margin-right:1.333rem}
-.mb1-lg{margin-bottom:1.333rem}
-.ml1-lg{margin-left:1.333rem}
-.m0-lg{margin:1rem}
-.mt0-lg{margin-top:1rem}
-.mr0-lg{margin-right:1rem}
-.mb0-lg{margin-bottom:1rem}
-.ml0-lg{margin-left:1rem}
-.m-1-lg{margin:0.75rem}
-.mt-1-lg{margin-top:0.75rem}
-.mr-1-lg{margin-right:0.75rem}
-.mb-1-lg{margin-bottom:0.75rem}
-.ml-1-lg{margin-left:0.75rem}
-.m-2-lg{margin:0.563rem}
-.mt-2-lg{margin-top:0.563rem}
-.mr-2-lg{margin-right:0.563rem}
-.mb-2-lg{margin-bottom:0.563rem}
-.ml-2-lg{margin-left:0.563rem}
-.m-3-lg{margin:0.422rem}
-.mt-3-lg{margin-top:0.422rem}
-.mr-3-lg{margin-right:0.422rem}
-.mb-3-lg{margin-bottom:0.422rem}
-.ml-3-lg{margin-left:0.422rem}
-.m-4-lg{margin:0.317rem}
-.mt-4-lg{margin-top:0.317rem}
-.mr-4-lg{margin-right:0.317rem}
-.mb-4-lg{margin-bottom:0.317rem}
-.ml-4-lg{margin-left:0.317rem}
-.m-5-lg{margin:0.238rem}
-.mt-5-lg{margin-top:0.238rem}
-.mr-5-lg{margin-right:0.238rem}
-.mb-5-lg{margin-bottom:0.238rem}
-.ml-5-lg{margin-left:0.238rem}
-.m-6-lg{margin:0.178rem}
-.mt-6-lg{margin-top:0.178rem}
-.mr-6-lg{margin-right:0.178rem}
-.mb-6-lg{margin-bottom:0.178rem}
-.ml-6-lg{margin-left:0.178rem}
+ /* POSITION */
+ .sticky-lg {
+ position: sticky;
+ }
+ .static-lg {
+ position: static;
+ }
+ .absolute-lg {
+ position: absolute;
+ }
+ .relative-lg {
+ position: relative;
+ }
+ .fixed-lg {
+ position: fixed;
+ }
+ /* POSITIONING */
+ .trbl-lg,
+ .top0-lg {
+ top: 0;
+ }
+ .trbl-lg,
+ .right0-lg {
+ right: 0;
+ }
+ .trbl-lg,
+ .bottom0-lg {
+ bottom: 0;
+ }
+ .trbl-lg,
+ .left0-lg {
+ left: 0;
+ }
-/* PADDING */
-.p-none-lg{padding:0;}
-.py-none-lg,
-.pt-none-lg{padding-top:0;}
-.px-none-lg,
-.pr-none-lg{padding-right:0;}
-.py-none-lg,
-.pb-none-lg{padding-bottom:0;}
-.px-none-lg,
-.pl-none-lg{padding-left:0;}
-.p6-lg{padding:5.61rem;}
-.pt6-lg{padding-top:5.61rem;}
-.pr6-lg{padding-right:5.61rem;}
-.pb6-lg{padding-bottom:5.61rem;}
-.pl6-lg{padding-left:5.61rem;}
-.p5-lg{padding:4.209rem;}
-.pt5-lg{padding-top:4.209rem;}
-.pr5-lg{padding-right:4.209rem;}
-.pb5-lg{padding-bottom:4.209rem;}
-.pl5-lg{padding-left:4.209rem;}
-.p4-lg{padding:3.157rem;}
-.pt4-lg{padding-top:3.157rem;}
-.pr4-lg{padding-right:3.157rem;}
-.pb4-lg{padding-bottom:3.157rem;}
-.pl4-lg{padding-left:3.157rem;}
-.p3-lg{padding:2.369rem;}
-.pt3-lg{padding-top:2.369rem;}
-.pr3-lg{padding-right:2.369rem;}
-.pb3-lg{padding-bottom:2.369rem;}
-.pl3-lg{padding-left:2.369rem;}
-.p2-lg{padding:1.777rem;}
-.pt2-lg{padding-top:1.777rem;}
-.pr2-lg{padding-right:1.777rem;}
-.pb2-lg{padding-bottom:1.777rem;}
-.pl2-lg{padding-left:1.777rem;}
-.p1-lg{padding:1.333rem;}
-.pt1-lg{padding-top:1.333rem;}
-.pr1-lg{padding-right:1.333rem;}
-.pb1-lg{padding-bottom:1.333rem;}
-.pl1-lg{padding-left:1.333rem;}
-.p0-lg{padding:1rem;}
-.pt0-lg{padding-top:1rem;}
-.pr0-lg{padding-right:1rem;}
-.pb0-lg{padding-bottom:1rem;}
-.pl0-lg{padding-left:1rem;}
-.p-1-lg{padding:0.75rem;}
-.pt-1-lg{padding-top:0.75rem;}
-.pr-1-lg{padding-right:0.75rem;}
-.pb-1-lg{padding-bottom:0.75rem;}
-.pl-1-lg{padding-left:0.75rem;}
-.p-2-lg{padding:0.563rem;}
-.pt-2-lg{padding-top:0.563rem;}
-.pr-2-lg{padding-right:0.563rem;}
-.pb-2-lg{padding-bottom:0.563rem;}
-.pl-2-lg{padding-left:0.563rem;}
-.p-3-lg{padding:0.422rem;}
-.pt-3-lg{padding-top:0.422rem;}
-.pr-3-lg{padding-right:0.422rem;}
-.pb-3-lg{padding-bottom:0.422rem;}
-.pl-3-lg{padding-left:0.422rem;}
-.p-4-lg{padding:0.317rem;}
-.pt-4-lg{padding-top:0.317rem;}
-.pr-4-lg{padding-right:0.317rem;}
-.pb-4-lg{padding-bottom:0.317rem;}
-.pl-4-lg{padding-left:0.317rem;}
-.p-5-lg{padding:0.238rem;}
-.pt-5-lg{padding-top:0.238rem;}
-.pr-5-lg{padding-right:0.238rem;}
-.pb-5-lg{padding-bottom:0.238rem;}
-.pl-5-lg{padding-left:0.238rem;}
-.p-6-lg{padding:0.178rem;}
-.pt-6-lg{padding-top:0.178rem;}
-.pr-6-lg{padding-right:0.178rem;}
-.pb-6-lg{padding-bottom:0.178rem;}
-.pl-6-lg{padding-left:0.178rem;}
+ /* DISPLAY */
+ .hidden-lg {
+ display: none;
+ }
+ .block-lg {
+ display: block;
+ }
+ .inline-lg {
+ display: inline;
+ }
+ .inline-block-lg {
+ display: inline-block;
+ }
+ .flex-lg {
+ display: flex;
+ }
+ .inline-flex-lg {
+ display: inline-flex;
+ }
+ .grid-lg {
+ display: grid;
+ }
+ .inline-grid-lg {
+ display: inline-grid;
+ }
+ /* WIDTH */
+ .w-0-lg {
+ width: 0;
+ }
+ .w-full-lg {
+ width: 100%;
+ }
+ .w-screen-lg {
+ width: 100vw;
+ }
+ .min-w-0-lg {
+ min-width: 0;
+ }
+ .min-w-full-lg {
+ min-width: 100%;
+ }
+ .max-width-none-lg {
+ max-width: none;
+ }
+ .max-w-full-lg {
+ max-width: 100%;
+ }
-/* OVERFLOW */
-.overflow-auto-lg{overflow:auto;}
-.truncate-lg,
-.overflow-hidden-lg{overflow:hidden;}
-.overflow-visible-lg{overflow:visible;}
-.overflow-scroll-lg{overflow:scroll;}
-.overflow-x-auto-lg{overflow-x:auto;}
-.overflow-y-auto-lg{overflow-y:auto;}
-.overflow-x-scroll-lg{overflow-x:scroll;}
-.overflow-x-hidden-lg{overflow-x:hidden;}
-.overflow-y-scroll-lg{overflow-y:scroll;}
-.overflow-y-hidden-lg{overflow-y:hidden;}
-.scrolling-touch-lg{-webkit-overflow-scrolling:touch;}
-.scrolling-auto-lg{-webkit-overflow-scrolling:auto;}
+ /* HEIGHT */
+ .h-0-lg {
+ height: 0;
+ }
+ .h-full-lg {
+ height: 100%;
+ }
+ .h-screen-lg {
+ height: 100vh;
+ }
+ .min-h-0-lg {
+ min-height: 0;
+ }
+ .min-h-full-lg {
+ min-height: 100%;
+ }
+ .min-h-screen-lg {
+ min-height: 100vh;
+ }
+ .max-h-full-lg {
+ max-height: 100%;
+ }
+ .max-h-screen-lg {
+ max-height: 100vh;
+ }
+ /* FLEX */
+ .flex-1-lg {
+ flex: 1 1 0%;
+ }
+ .flex-auto-lg {
+ flex: 1 1 auto;
+ }
+ .flex-initial-lg {
+ flex: 0 1 auto;
+ }
+ .flex-none-lg {
+ flex: none;
+ }
+ .flex-row-lg {
+ flex-direction: row;
+ }
+ .flex-row-reverse-lg {
+ flex-direction: row-reverse;
+ }
+ .flex-col-lg {
+ flex-direction: column;
+ }
+ .flex-col-reverse-lg {
+ flex-direction: column-reverse;
+ }
+ .items-stretch-lg {
+ align-items: stretch;
+ }
+ .items-start-lg {
+ align-items: flex-start;
+ }
+ .items-end-lg {
+ align-items: flex-end;
+ }
+ .items-center-lg {
+ align-items: center;
+ }
+ .content-start-lg {
+ align-content: start;
+ }
+ .content-center-lg {
+ align-content: center;
+ }
+ .content-end-lg {
+ align-content: end;
+ }
+ .content-between-lg {
+ align-content: space-between;
+ }
+ .content-around-lg {
+ align-content: space-around;
+ }
+ .self-auto-lg {
+ align-self: auto;
+ }
+ .self-start-lg {
+ align-self: flex-start;
+ }
+ .self-end-lg {
+ align-self: flex-end;
+ }
+ .self-center-lg {
+ align-self: center;
+ }
+ .self-stretch-lg {
+ align-self: stretch;
+ }
+ .justify-start-lg {
+ justify-content: flex-start;
+ }
+ .justify-end-lg {
+ justify-content: flex-end;
+ }
+ .justify-around-lg {
+ justify-content: space-around;
+ }
+ .justify-between-lg {
+ justify-content: space-between;
+ }
+ .justify-center-lg {
+ justify-content: center;
+ }
+ .flex-grow-lg {
+ flex-grow: 1;
+ }
+ .flex-grow-0-lg {
+ flex-grow: 0;
+ }
+ .flex-shrink-lg {
+ flex-shrink: 1;
+ }
+ .flex-shrink-0-lg {
+ flex-shrink: 0;
+ }
+ .flex-wrap-lg {
+ flex-wrap: wrap;
+ }
+ .flex-wrap-reverse-lg {
+ flex-wrap: wrap-reverse;
+ }
+ .flex-no-wrap-lg {
+ flex-wrap: nowrap;
+ }
+ .order-first-lg {
+ order: -9999;
+ }
+ .order-last-lg {
+ order: 9999;
+ }
+ .order-none-lg {
+ order: 0;
+ }
+ .order-1-lg {
+ order: 1;
+ }
+ .order-2-lg {
+ order: 2;
+ }
+ .order-3-lg {
+ order: 3;
+ }
+ .order-4-lg {
+ order: 4;
+ }
+ .order-5-lg {
+ order: 5;
+ }
+ .order-6-lg {
+ order: 6;
+ }
-/* VISIBILITY */
-.invisible-lg{visibility:hidden;}
-.visible-lg{visibility:visible;}
+ /* GRID */
+ .flow-row-lg {
+ grid-auto-flow: row;
+ }
+ .flow-col-lg {
+ grid-auto-flow: column;
+ }
+ .flow-row-dense-lg {
+ grid-auto-flow: row dense;
+ }
+ .flow-column-dense-lg {
+ grid-auto-flow: column dense;
+ }
+ .row-auto-lg {
+ grid-row: auto;
+ }
+ .col-auto-lg {
+ grid-column: auto;
+ }
+ .col-end-auto-lg {
+ grid-column-end: auto;
+ }
+ .rows-end-auto-lg {
+ grid-row-end: auto;
+ }
+ .rows-none-lg {
+ grid-template-rows: none;
+ }
+ .col-1-lg {
+ grid-template-columns: repeat(1, minmax(0, 1fr));
+ }
+ .col-span-1-lg {
+ grid-column: span 1 / span 1;
+ }
+ .col-start-1-lg {
+ grid-column-start: 1;
+ }
+ .row-start-1-lg {
+ grid-row-start: 1;
+ }
+ .col-end-1-lg {
+ grid-column-end: 1;
+ }
+ .row-end-1-lg {
+ grid-row-end: 1;
+ }
+ .row-1-lg {
+ grid-template-rows: repeat(1, minmax(0, 1fr));
+ }
+ .col-2-lg {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+ .col-span-2-lg {
+ grid-column: span 2 / span 2;
+ }
+ .col-start-2-lg {
+ grid-column-start: 2;
+ }
+ .row-start-2-lg {
+ grid-row-start: 2;
+ }
+ .col-end-2-lg {
+ grid-column-end: 2;
+ }
+ .row-end-2-lg {
+ grid-row-end: 2;
+ }
+ .row-2-lg {
+ grid-template-rows: repeat(2, minmax(0, 1fr));
+ }
+ .col-3-lg {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ }
+ .col-span-3-lg {
+ grid-column: span 3 / span 3;
+ }
+ .col-start-3-lg {
+ grid-column-start: 3;
+ }
+ .row-start-3-lg {
+ grid-row-start: 3;
+ }
+ .col-end-3-lg {
+ grid-column-end: 3;
+ }
+ .row-end-3-lg {
+ grid-row-end: 3;
+ }
+ .row-3-lg {
+ grid-template-rows: repeat(3, minmax(0, 1fr));
+ }
+ .col-4-lg {
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ }
+ .col-span-4-lg {
+ grid-column: span 4 / span 4;
+ }
+ .col-start-4-lg {
+ grid-column-start: 4;
+ }
+ .row-start-4-lg {
+ grid-row-start: 4;
+ }
+ .col-end-4-lg {
+ grid-column-end: 4;
+ }
+ .row-end-4-lg {
+ grid-row-end: 4;
+ }
+ .row-4-lg {
+ grid-template-rows: repeat(4, minmax(0, 1fr));
+ }
+ .col-5-lg {
+ grid-template-columns: repeat(5, minmax(0, 1fr));
+ }
+ .col-span-5-lg {
+ grid-column: span 5 / span 5;
+ }
+ .col-start-5-lg {
+ grid-column-start: 5;
+ }
+ .row-start-5-lg {
+ grid-row-start: 5;
+ }
+ .col-end-5-lg {
+ grid-column-end: 5;
+ }
+ .row-end-5-lg {
+ grid-row-end: 5;
+ }
+ .row-5-lg {
+ grid-template-rows: repeat(5, minmax(0, 1fr));
+ }
+ .col-6-lg {
+ grid-template-columns: repeat(6, minmax(0, 1fr));
+ }
+ .col-span-6-lg {
+ grid-column: span 6 / span 6;
+ }
+ .col-start-6-lg {
+ grid-column-start: 6;
+ }
+ .row-start-6-lg {
+ grid-row-start: 6;
+ }
+ .col-end-6-lg {
+ grid-column-end: 6;
+ }
+ .row-end-6-lg {
+ grid-row-end: 6;
+ }
+ .row-6-lg {
+ grid-template-rows: repeat(6, minmax(0, 1fr));
+ }
+ .gap5-lg {
+ gap: 5.61rem;
+ }
+ .gap4-lg {
+ gap: 4.209rem;
+ }
+ .gap3-lg {
+ gap: 3.157rem;
+ }
+ .gap2-lg {
+ gap: 2.369rem;
+ }
+ .gap1-lg {
+ gap: 1.777rem;
+ }
+ .gap0-lg {
+ gap: 1.333rem;
+ }
+ .gap-1-lg {
+ gap: 1rem;
+ }
+ .gap-2-lg {
+ gap: 0.75rem;
+ }
+ .gap-3-lg {
+ gap: 0.563rem;
+ }
+ .gap-4-lg {
+ gap: 0.422rem;
+ }
+ .gap-5-lg {
+ gap: 0.317rem;
+ }
+ .gap-6-lg {
+ gap: 0.238rem;
+ }
+ .gap-7-lg {
+ gap: 0.178rem;
+ }
+ /* Z-INDEX */
+ .z-auto-lg {
+ z-index: auto;
+ }
+ .z1-lg {
+ z-index: 1;
+ }
+ .z0-lg {
+ z-index: 0;
+ }
+ .z-1-lg {
+ z-index: -1;
+ }
-/* OBJECT FIT */
-.object-contain-lg{object-fit:contain;}
-.object-cover-lg{object-fit:cover;}
-.object-fill-lg{object-fit:fill;}
-.object-none-lg{object-fit:none;}
-.object-scale-down-lg{object-fit:scale-down;}
+ /* MARGIN */
+ .m-none-lg {
+ margin: 0;
+ }
+ .my-none-lg,
+ .mt-none-lg {
+ margin-top: 0;
+ }
+ .mx-none-lg,
+ .mr-none-lg {
+ margin-right: 0;
+ }
+ .my-none-lg,
+ .mb-none-lg {
+ margin-bottom: 0;
+ }
+ .mx-none-lg,
+ .ml-none-lg {
+ margin-left: 0;
+ }
+ .m-auto-lg {
+ margin-right: auto;
+ margin-left: auto;
+ }
+ .mr-auto-lg {
+ margin-right: auto;
+ }
+ .ml-auto-lg {
+ margin-left: auto;
+ }
+ .m6-lg {
+ margin: 5.61rem;
+ }
+ .mt6-lg {
+ margin-top: 5.61rem;
+ }
+ .mr6-lg {
+ margin-right: 5.61rem;
+ }
+ .mb6-lg {
+ margin-bottom: 5.61rem;
+ }
+ .ml6-lg {
+ margin-left: 5.61rem;
+ }
+ .m5-lg {
+ margin: 4.209rem;
+ }
+ .mt5-lg {
+ margin-top: 4.209rem;
+ }
+ .mr5-lg {
+ margin-right: 4.209rem;
+ }
+ .mb5-lg {
+ margin-bottom: 4.209rem;
+ }
+ .ml5-lg {
+ margin-left: 4.209rem;
+ }
+ .m4-lg {
+ margin: 3.157rem;
+ }
+ .mt4-lg {
+ margin-top: 3.157rem;
+ }
+ .mr4-lg {
+ margin-right: 3.157rem;
+ }
+ .mb4-lg {
+ margin-bottom: 3.157rem;
+ }
+ .ml4-lg {
+ margin-left: 3.157rem;
+ }
+ .m3-lg {
+ margin: 2.369rem;
+ }
+ .mt3-lg {
+ margin-top: 2.369rem;
+ }
+ .mr3-lg {
+ margin-right: 2.369rem;
+ }
+ .mb3-lg {
+ margin-bottom: 2.369rem;
+ }
+ .ml3-lg {
+ margin-left: 2.369rem;
+ }
+ .m2-lg {
+ margin: 1.777rem;
+ }
+ .mt2-lg {
+ margin-top: 1.777rem;
+ }
+ .mr2-lg {
+ margin-right: 1.777rem;
+ }
+ .mb2-lg {
+ margin-bottom: 1.777rem;
+ }
+ .ml2-lg {
+ margin-left: 1.777rem;
+ }
+ .m1-lg {
+ margin: 1.333rem;
+ }
+ .mt1-lg {
+ margin-top: 1.333rem;
+ }
+ .mr1-lg {
+ margin-right: 1.333rem;
+ }
+ .mb1-lg {
+ margin-bottom: 1.333rem;
+ }
+ .ml1-lg {
+ margin-left: 1.333rem;
+ }
+ .m0-lg {
+ margin: 1rem;
+ }
+ .mt0-lg {
+ margin-top: 1rem;
+ }
+ .mr0-lg {
+ margin-right: 1rem;
+ }
+ .mb0-lg {
+ margin-bottom: 1rem;
+ }
+ .ml0-lg {
+ margin-left: 1rem;
+ }
+ .m-1-lg {
+ margin: 0.75rem;
+ }
+ .mt-1-lg {
+ margin-top: 0.75rem;
+ }
+ .mr-1-lg {
+ margin-right: 0.75rem;
+ }
+ .mb-1-lg {
+ margin-bottom: 0.75rem;
+ }
+ .ml-1-lg {
+ margin-left: 0.75rem;
+ }
+ .m-2-lg {
+ margin: 0.563rem;
+ }
+ .mt-2-lg {
+ margin-top: 0.563rem;
+ }
+ .mr-2-lg {
+ margin-right: 0.563rem;
+ }
+ .mb-2-lg {
+ margin-bottom: 0.563rem;
+ }
+ .ml-2-lg {
+ margin-left: 0.563rem;
+ }
+ .m-3-lg {
+ margin: 0.422rem;
+ }
+ .mt-3-lg {
+ margin-top: 0.422rem;
+ }
+ .mr-3-lg {
+ margin-right: 0.422rem;
+ }
+ .mb-3-lg {
+ margin-bottom: 0.422rem;
+ }
+ .ml-3-lg {
+ margin-left: 0.422rem;
+ }
+ .m-4-lg {
+ margin: 0.317rem;
+ }
+ .mt-4-lg {
+ margin-top: 0.317rem;
+ }
+ .mr-4-lg {
+ margin-right: 0.317rem;
+ }
+ .mb-4-lg {
+ margin-bottom: 0.317rem;
+ }
+ .ml-4-lg {
+ margin-left: 0.317rem;
+ }
+ .m-5-lg {
+ margin: 0.238rem;
+ }
+ .mt-5-lg {
+ margin-top: 0.238rem;
+ }
+ .mr-5-lg {
+ margin-right: 0.238rem;
+ }
+ .mb-5-lg {
+ margin-bottom: 0.238rem;
+ }
+ .ml-5-lg {
+ margin-left: 0.238rem;
+ }
+ .m-6-lg {
+ margin: 0.178rem;
+ }
+ .mt-6-lg {
+ margin-top: 0.178rem;
+ }
+ .mr-6-lg {
+ margin-right: 0.178rem;
+ }
+ .mb-6-lg {
+ margin-bottom: 0.178rem;
+ }
+ .ml-6-lg {
+ margin-left: 0.178rem;
+ }
+ /* PADDING */
+ .p-none-lg {
+ padding: 0;
+ }
+ .py-none-lg,
+ .pt-none-lg {
+ padding-top: 0;
+ }
+ .px-none-lg,
+ .pr-none-lg {
+ padding-right: 0;
+ }
+ .py-none-lg,
+ .pb-none-lg {
+ padding-bottom: 0;
+ }
+ .px-none-lg,
+ .pl-none-lg {
+ padding-left: 0;
+ }
+ .p6-lg {
+ padding: 5.61rem;
+ }
+ .pt6-lg {
+ padding-top: 5.61rem;
+ }
+ .pr6-lg {
+ padding-right: 5.61rem;
+ }
+ .pb6-lg {
+ padding-bottom: 5.61rem;
+ }
+ .pl6-lg {
+ padding-left: 5.61rem;
+ }
+ .p5-lg {
+ padding: 4.209rem;
+ }
+ .pt5-lg {
+ padding-top: 4.209rem;
+ }
+ .pr5-lg {
+ padding-right: 4.209rem;
+ }
+ .pb5-lg {
+ padding-bottom: 4.209rem;
+ }
+ .pl5-lg {
+ padding-left: 4.209rem;
+ }
+ .p4-lg {
+ padding: 3.157rem;
+ }
+ .pt4-lg {
+ padding-top: 3.157rem;
+ }
+ .pr4-lg {
+ padding-right: 3.157rem;
+ }
+ .pb4-lg {
+ padding-bottom: 3.157rem;
+ }
+ .pl4-lg {
+ padding-left: 3.157rem;
+ }
+ .p3-lg {
+ padding: 2.369rem;
+ }
+ .pt3-lg {
+ padding-top: 2.369rem;
+ }
+ .pr3-lg {
+ padding-right: 2.369rem;
+ }
+ .pb3-lg {
+ padding-bottom: 2.369rem;
+ }
+ .pl3-lg {
+ padding-left: 2.369rem;
+ }
+ .p2-lg {
+ padding: 1.777rem;
+ }
+ .pt2-lg {
+ padding-top: 1.777rem;
+ }
+ .pr2-lg {
+ padding-right: 1.777rem;
+ }
+ .pb2-lg {
+ padding-bottom: 1.777rem;
+ }
+ .pl2-lg {
+ padding-left: 1.777rem;
+ }
+ .p1-lg {
+ padding: 1.333rem;
+ }
+ .pt1-lg {
+ padding-top: 1.333rem;
+ }
+ .pr1-lg {
+ padding-right: 1.333rem;
+ }
+ .pb1-lg {
+ padding-bottom: 1.333rem;
+ }
+ .pl1-lg {
+ padding-left: 1.333rem;
+ }
+ .p0-lg {
+ padding: 1rem;
+ }
+ .pt0-lg {
+ padding-top: 1rem;
+ }
+ .pr0-lg {
+ padding-right: 1rem;
+ }
+ .pb0-lg {
+ padding-bottom: 1rem;
+ }
+ .pl0-lg {
+ padding-left: 1rem;
+ }
+ .p-1-lg {
+ padding: 0.75rem;
+ }
+ .pt-1-lg {
+ padding-top: 0.75rem;
+ }
+ .pr-1-lg {
+ padding-right: 0.75rem;
+ }
+ .pb-1-lg {
+ padding-bottom: 0.75rem;
+ }
+ .pl-1-lg {
+ padding-left: 0.75rem;
+ }
+ .p-2-lg {
+ padding: 0.563rem;
+ }
+ .pt-2-lg {
+ padding-top: 0.563rem;
+ }
+ .pr-2-lg {
+ padding-right: 0.563rem;
+ }
+ .pb-2-lg {
+ padding-bottom: 0.563rem;
+ }
+ .pl-2-lg {
+ padding-left: 0.563rem;
+ }
+ .p-3-lg {
+ padding: 0.422rem;
+ }
+ .pt-3-lg {
+ padding-top: 0.422rem;
+ }
+ .pr-3-lg {
+ padding-right: 0.422rem;
+ }
+ .pb-3-lg {
+ padding-bottom: 0.422rem;
+ }
+ .pl-3-lg {
+ padding-left: 0.422rem;
+ }
+ .p-4-lg {
+ padding: 0.317rem;
+ }
+ .pt-4-lg {
+ padding-top: 0.317rem;
+ }
+ .pr-4-lg {
+ padding-right: 0.317rem;
+ }
+ .pb-4-lg {
+ padding-bottom: 0.317rem;
+ }
+ .pl-4-lg {
+ padding-left: 0.317rem;
+ }
+ .p-5-lg {
+ padding: 0.238rem;
+ }
+ .pt-5-lg {
+ padding-top: 0.238rem;
+ }
+ .pr-5-lg {
+ padding-right: 0.238rem;
+ }
+ .pb-5-lg {
+ padding-bottom: 0.238rem;
+ }
+ .pl-5-lg {
+ padding-left: 0.238rem;
+ }
+ .p-6-lg {
+ padding: 0.178rem;
+ }
+ .pt-6-lg {
+ padding-top: 0.178rem;
+ }
+ .pr-6-lg {
+ padding-right: 0.178rem;
+ }
+ .pb-6-lg {
+ padding-bottom: 0.178rem;
+ }
+ .pl-6-lg {
+ padding-left: 0.178rem;
+ }
-/* OBJECT POSITION */
-.object-b-lg{object-position:bottom;}
-.object-c-lg{object-position:center;}
-.object-t-lg{object-position:top;}
-.object-r-lg{object-position:right;}
-.object-rt-lg{object-position:right top;}
-.object-rb-lg{object-position:right bottom;}
-.object-l-lg{object-position:left;}
-.object-lt-lg{object-position:left top;}
-.object-lb-lg{object-position:left bottom;}
-
+ /* OVERFLOW */
+ .overflow-auto-lg {
+ overflow: auto;
+ }
+ .truncate-lg,
+ .overflow-hidden-lg {
+ overflow: hidden;
+ }
+ .overflow-visible-lg {
+ overflow: visible;
+ }
+ .overflow-scroll-lg {
+ overflow: scroll;
+ }
+ .overflow-x-auto-lg {
+ overflow-x: auto;
+ }
+ .overflow-y-auto-lg {
+ overflow-y: auto;
+ }
+ .overflow-x-scroll-lg {
+ overflow-x: scroll;
+ }
+ .overflow-x-hidden-lg {
+ overflow-x: hidden;
+ }
+ .overflow-y-scroll-lg {
+ overflow-y: scroll;
+ }
+ .overflow-y-hidden-lg {
+ overflow-y: hidden;
+ }
+ .scrolling-touch-lg {
+ -webkit-overflow-scrolling: touch;
+ }
+ .scrolling-auto-lg {
+ -webkit-overflow-scrolling: auto;
+ }
-/* OUTLINE */
-.outline-none-lg{outline:0;}
-
+ /* VISIBILITY */
+ .invisible-lg {
+ visibility: hidden;
+ }
+ .visible-lg {
+ visibility: visible;
+ }
-/* OPACITY */
-.opacity-0-lg{opacity:0;}
-.opacity-25-lg{opacity:0.25;}
-.opacity-50-lg{opacity:0.5;}
-.opacity-75-lg{opacity:0.75;}
-.opacity-100-lg{opacity:1.0;}
+ /* OBJECT FIT */
+ .object-contain-lg {
+ object-fit: contain;
+ }
+ .object-cover-lg {
+ object-fit: cover;
+ }
+ .object-fill-lg {
+ object-fit: fill;
+ }
+ .object-none-lg {
+ object-fit: none;
+ }
+ .object-scale-down-lg {
+ object-fit: scale-down;
+ }
+ /* OBJECT POSITION */
+ .object-b-lg {
+ object-position: bottom;
+ }
+ .object-c-lg {
+ object-position: center;
+ }
+ .object-t-lg {
+ object-position: top;
+ }
+ .object-r-lg {
+ object-position: right;
+ }
+ .object-rt-lg {
+ object-position: right top;
+ }
+ .object-rb-lg {
+ object-position: right bottom;
+ }
+ .object-l-lg {
+ object-position: left;
+ }
+ .object-lt-lg {
+ object-position: left top;
+ }
+ .object-lb-lg {
+ object-position: left bottom;
+ }
-/* CURSOR */
-.cursor-auto-lg{cursor:auto;}
-.cursor-default-lg{cursor:default;}
-.cursor-pointer-lg{cursor:pointer;}
-.cursor-wait-lg{cursor:wait;}
-.cursor-text-lg{cursor:text;}
-.cursor-move-lg{cursor:move;}
-.cursor-not-allowed-lg{cursor:not-allowed;}
-.cursor-grab-lg{cursor:grab;}
-.cursor-grabbing-lg{cursor:grabbing;}
+ /* OUTLINE */
+ .outline-none-lg {
+ outline: 0;
+ }
+ /* OPACITY */
+ .opacity-0-lg {
+ opacity: 0;
+ }
+ .opacity-25-lg {
+ opacity: 0.25;
+ }
+ .opacity-50-lg {
+ opacity: 0.5;
+ }
+ .opacity-75-lg {
+ opacity: 0.75;
+ }
+ .opacity-100-lg {
+ opacity: 1;
+ }
-/* USER SELECT */
-.select-none-lg{user-select:none;}
-.select-text-lg{user-select:text;}
-.select-all-lg{user-select:all;}
-.select-auto-lg{user-select:auto;}
-
+ /* CURSOR */
+ .cursor-auto-lg {
+ cursor: auto;
+ }
+ .cursor-default-lg {
+ cursor: default;
+ }
+ .cursor-pointer-lg {
+ cursor: pointer;
+ }
+ .cursor-wait-lg {
+ cursor: wait;
+ }
+ .cursor-text-lg {
+ cursor: text;
+ }
+ .cursor-move-lg {
+ cursor: move;
+ }
+ .cursor-not-allowed-lg {
+ cursor: not-allowed;
+ }
+ .cursor-grab-lg {
+ cursor: grab;
+ }
+ .cursor-grabbing-lg {
+ cursor: grabbing;
+ }
+ /* USER SELECT */
+ .select-none-lg {
+ user-select: none;
+ }
+ .select-text-lg {
+ user-select: text;
+ }
+ .select-all-lg {
+ user-select: all;
+ }
+ .select-auto-lg {
+ user-select: auto;
+ }
}
diff --git a/public/styles/main.css b/public/styles/main.css
index 4c2e152..7ecc706 100644
--- a/public/styles/main.css
+++ b/public/styles/main.css
@@ -1,122 +1,139 @@
-html, body {
- width: 100%;
- height: 100%;
- margin: 0;
- line-height: 1.5em;
+html,
+body {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ line-height: 1.5em;
}
body {
- font-family: canada-type-gibson, sans-serif;
+ font-family: canada-type-gibson, sans-serif;
}
-h1, h2, h3, h4, h5, h6 {
- font-family: headline-gothic-atf-round, sans-serif;
- font-weight: 400;
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-family: headline-gothic-atf-round, sans-serif;
+ font-weight: 400;
}
-h1 { font-size: 210% }
-h2 { font-size: 180%; color: #052460 }
-h3 { font-size: 150% }
-h4 { font-size: 120% }
+h1 {
+ font-size: 210%;
+}
+h2 {
+ font-size: 180%;
+ color: #052460;
+}
+h3 {
+ font-size: 150%;
+}
+h4 {
+ font-size: 120%;
+}
-a, a:visited {
- color: blue;
- text-decoration: none;
+a,
+a:visited {
+ color: blue;
+ text-decoration: none;
}
a:hover {
- text-decoration: underline;
+ text-decoration: underline;
}
pre {
- white-space: pre-wrap;
+ white-space: pre-wrap;
}
img {
- max-width: 100%;
- object-fit: contain;
+ max-width: 100%;
+ object-fit: contain;
}
input {
- display: block;
- margin-bottom: 8px;
+ display: block;
+ margin-bottom: 8px;
}
i {
- width: 25px;
+ width: 25px;
}
-a[target="_blank"]:after {
- display: inline-block;
- text-rendering: auto;
- -webkit-font-smoothing: antialiased;
- font: var(--fa-font-solid);
- font-size: 10px;
- margin:0 8px;
- content: "\f35d";
+a[target='_blank']:after {
+ display: inline-block;
+ text-rendering: auto;
+ -webkit-font-smoothing: antialiased;
+ font: var(--fa-font-solid);
+ font-size: 10px;
+ margin: 0 8px;
+ content: '\f35d';
}
#page {
- padding: 8px 24px;
+ padding: 8px 24px;
}
.hero-image {
- height: auto;
+ height: auto;
}
.cta {
- font-family: headline-gothic-atf-round, sans-serif;
- background-color: #0033FF;
- border: 4px solid #0033FF;
- font-weight: 500;
+ font-family: headline-gothic-atf-round, sans-serif;
+ background-color: #0033ff;
+ border: 4px solid #0033ff;
+ font-weight: 500;
}
div.cta {
- font-size: 120%;
- margin: 16px 0;
- display: inline-block;
+ font-size: 120%;
+ margin: 16px 0;
+ display: inline-block;
}
span.cta {
- font-size: 18px;
- padding: 8px;
+ font-size: 18px;
+ padding: 8px;
}
-.cta a, .cta a:visited {
- display: inline-block;
- padding: 8px 16px;
- color: #fff5cc;
- text-decoration: none;
+.cta a,
+.cta a:visited {
+ display: inline-block;
+ padding: 8px 16px;
+ color: #fff5cc;
+ text-decoration: none;
}
.cta:hover {
- background-color: #112378;
- border: 4px solid #112378;
+ background-color: #112378;
+ border: 4px solid #112378;
}
.cta.secondary {
- color: #0033FF;
- background: none;
- border: 4px solid #0033FF;
+ color: #0033ff;
+ background: none;
+ border: 4px solid #0033ff;
}
-
-.cta.secondary a, .cta.secondary a:visited {
- color: #0033FF;
- text-decoration: none;
+.cta.secondary a,
+.cta.secondary a:visited {
+ color: #0033ff;
+ text-decoration: none;
}
.cta.secondary:hover {
- border: 4px solid #112378;
+ border: 4px solid #112378;
}
.cta.secondary:hover a {
- color: #112378;
+ color: #112378;
}
@media only screen and (min-width: 768px) {
- #page {
- width: 60vw;
- margin: 0 auto;
- }
-}
\ No newline at end of file
+ #page {
+ width: 60vw;
+ margin: 0 auto;
+ }
+}
diff --git a/scripts/live-stream.js b/scripts/live-stream.js
index 373dc22..6de34b6 100644
--- a/scripts/live-stream.js
+++ b/scripts/live-stream.js
@@ -16,76 +16,75 @@ async function updatePlaybackId(playbackId) {
let env = process.argv[2]
let url
if (env === 'testing') {
- url = 'http://localhost:3333'
- }
- else {
- url = `https://${ env === 'staging' ? 'staging.' : '' }seattlejs.com`
+ url = 'http://localhost:3333'
+ } else {
+ url = `https://${env === 'staging' ? 'staging.' : ''}seattlejs.com`
}
// update the app setting
let params = new URLSearchParams()
params.append('secret', process.env.ADMIN_SECRET)
params.append('playbackId', playbackId)
await fetch(`${url}/admin`, {
- method: 'POST',
- body: params,
- // redirect: 'manual'
+ method: 'POST',
+ body: params
+ // redirect: 'manual'
})
console.log('Setting Updated: playbackId: ', playbackId)
}
-
async function createLivestream() {
- // create a payload for the REST API call that will initialize a livestream and simulcast it to both Twitter and Twitch
- let payload = {
- playback_policy: [
- "public"
- ],
- new_asset_settings: {
- playback_policy: [
- "public"
- ]
- },
- generated_subtitles: [
- {
- name: "English CC (auto)",
- passthrough: "English closed captions (auto-generated)",
- language_code: "en-US",
- transcription_vocabulary_ids: []
- }
- ],
- simulcast_targets : [
- {
- url : "rtmps://or.pscp.tv:443/x",
- stream_key : process.env.TWITTER_STREAM_KEY,
- passthrough : "SeattleJS Conf 2023 Livestream on Twitter"
- },
- {
- url : "rtmp://live.twitch.tv/app/",
- stream_key : process.env.TWITCH_STREAM_KEY,
- passthrough : "SeattleJS Conf 2023 Livestream on Twitch"
- },
- {
- url : "rtmp://a.rtmp.youtube.com/live2",
- stream_key : process.env.YOUTUBE_STREAM_KEY,
- passthrough : "SeattleJS Conf 2023 Livestream on YouTube"
- }
- ]
+ // create a payload for the REST API call that will initialize a livestream and simulcast it to both Twitter and Twitch
+ let payload = {
+ playback_policy: ['public'],
+ new_asset_settings: {
+ playback_policy: ['public']
+ },
+ generated_subtitles: [
+ {
+ name: 'English CC (auto)',
+ passthrough: 'English closed captions (auto-generated)',
+ language_code: 'en-US',
+ transcription_vocabulary_ids: []
+ }
+ ],
+ simulcast_targets: [
+ {
+ url: 'rtmps://or.pscp.tv:443/x',
+ stream_key: process.env.TWITTER_STREAM_KEY,
+ passthrough: 'SeattleJS Conf 2023 Livestream on Twitter'
+ },
+ {
+ url: 'rtmp://live.twitch.tv/app/',
+ stream_key: process.env.TWITCH_STREAM_KEY,
+ passthrough: 'SeattleJS Conf 2023 Livestream on Twitch'
+ },
+ {
+ url: 'rtmp://a.rtmp.youtube.com/live2',
+ stream_key: process.env.YOUTUBE_STREAM_KEY,
+ passthrough: 'SeattleJS Conf 2023 Livestream on YouTube'
}
+ ]
+ }
- // call MUX API
- let response = await fetch(`https://api.mux.com/video/v1/live-streams`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': 'Basic ' + Buffer.from(`${ process.env.MUX_TOKEN_ID }:${ process.env.MUX_TOKEN_SECRET }`, 'binary').toString('base64')
- },
- body: JSON.stringify(payload),
- })
- let result = await response.json()
- console.log("Mux Stream Key: ", result.data.stream_key)
- let playbackId = result.data.playback_ids[0].id
- console.log("Mux playbackId: ", playbackId)
- await updatePlaybackId(playbackId)
+ // call MUX API
+ let response = await fetch(`https://api.mux.com/video/v1/live-streams`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization:
+ 'Basic ' +
+ Buffer.from(
+ `${process.env.MUX_TOKEN_ID}:${process.env.MUX_TOKEN_SECRET}`,
+ 'binary'
+ ).toString('base64')
+ },
+ body: JSON.stringify(payload)
+ })
+ let result = await response.json()
+ console.log('Mux Stream Key: ', result.data.stream_key)
+ let playbackId = result.data.playback_ids[0].id
+ console.log('Mux playbackId: ', playbackId)
+ await updatePlaybackId(playbackId)
}
createLivestream()