Compare commits
2 Commits
main
...
feat/oura-
Author | SHA1 | Date | |
---|---|---|---|
13bbbbb78a | |||
c4f3badcee |
|
@ -5,6 +5,7 @@
|
|||
# - GOOGLE CALENDAR/MEET/LOGIN
|
||||
# - HUBSPOT
|
||||
# - OFFICE 365
|
||||
# - OURA RING
|
||||
# - SLACK
|
||||
# - STRIPE
|
||||
# - TANDEM
|
||||
|
@ -53,6 +54,12 @@ HUBSPOT_CLIENT_SECRET=""
|
|||
MS_GRAPH_CLIENT_ID=
|
||||
MS_GRAPH_CLIENT_SECRET=
|
||||
|
||||
# - OURA RING
|
||||
# Used for the Oura Ring integration
|
||||
# @see https://github.com/calcom/cal.com/#Obtaining-Oura-Ring-Client-ID-and-Secret
|
||||
OURA_RING_CLIENT_ID=
|
||||
OURA_RING_CLIENT_SECRET=
|
||||
|
||||
# - SLACK
|
||||
# @see https://github.com/calcom/cal.com/#obtaining-slack-client-id-and-secret-and-signing-secret
|
||||
SLACK_SIGNING_SECRET=
|
||||
|
|
|
@ -475,6 +475,14 @@ following
|
|||
9. Click the "Save" button at the bottom footer.
|
||||
10. You're good to go. Now you can see any booking in Cal.com created as a meeting in HubSpot for your contacts.
|
||||
|
||||
## Obtaining Oura Ring Client ID and Secret
|
||||
1. Go to [Oura Ring Developer portal](https://cloud.ouraring.com/user/sign-in?next=%2Foauth%2Fapplications)
|
||||
2. Click button "New application"
|
||||
3. Fill in the information for your new Cal.com Oura Ring application
|
||||
7. Set the Redirect URL to `<Cal.com URL>/api/integrations/ouraring/callback` replacing Cal.com URL with the URI at which your application runs.
|
||||
9. Click the "Save" button at the bottom.
|
||||
6. Now copy the Client ID and Client Secret to your `.env` file into the `OURA_RING_CLIENT_ID` and `OURA_RING_CLIENT_SECRET` fields.
|
||||
|
||||
### Obtaining Webex Client ID and Secret
|
||||
|
||||
[See Webex Readme](./packages/app-store/webex/)
|
||||
|
|
|
@ -31,6 +31,7 @@ import mirotalk_config_json from "./mirotalk/config.json";
|
|||
import n8n_config_json from "./n8n/config.json";
|
||||
import { metadata as office365calendar__metadata_ts } from "./office365calendar/_metadata";
|
||||
import office365video_config_json from "./office365video/config.json";
|
||||
import ouraring_config_json from "./ouraring/config.json";
|
||||
import ping_config_json from "./ping/config.json";
|
||||
import pipedream_config_json from "./pipedream/config.json";
|
||||
import plausible_config_json from "./plausible/config.json";
|
||||
|
@ -96,6 +97,7 @@ export const appStoreMetadata = {
|
|||
n8n: n8n_config_json,
|
||||
office365calendar: office365calendar__metadata_ts,
|
||||
office365video: office365video_config_json,
|
||||
ouraring: ouraring_config_json,
|
||||
ping: ping_config_json,
|
||||
pipedream: pipedream_config_json,
|
||||
plausible: plausible_config_json,
|
||||
|
|
|
@ -31,6 +31,7 @@ export const apiHandlers = {
|
|||
n8n: import("./n8n/api"),
|
||||
office365calendar: import("./office365calendar/api"),
|
||||
office365video: import("./office365video/api"),
|
||||
ouraring: import("./ouraring/api"),
|
||||
ping: import("./ping/api"),
|
||||
pipedream: import("./pipedream/api"),
|
||||
plausible: import("./plausible/api"),
|
||||
|
|
|
@ -13,6 +13,7 @@ const appStore = {
|
|||
larkcalendar: () => import("./larkcalendar"),
|
||||
office365calendar: () => import("./office365calendar"),
|
||||
office365video: () => import("./office365video"),
|
||||
ouraring: () => import("./ouraring"),
|
||||
plausible: () => import("./plausible"),
|
||||
salesforce: () => import("./salesforce"),
|
||||
zohocrm: () => import("./zohocrm"),
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
items:
|
||||
- 1.jpeg
|
||||
---
|
||||
|
||||
{DESCRIPTION}
|
|
@ -0,0 +1,31 @@
|
|||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { stringify } from "querystring";
|
||||
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
|
||||
import { encodeOAuthState } from "../../_utils/encodeOAuthState";
|
||||
import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
|
||||
|
||||
const scopes = ["daily"];
|
||||
|
||||
let client_id = "";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (req.method !== "GET") {
|
||||
return res.status(400).send("Method not allowed");
|
||||
}
|
||||
const appKeys = await getAppKeysFromSlug("ouraring");
|
||||
if (typeof appKeys.client_id === "string") client_id = appKeys.client_id;
|
||||
if (!client_id) return res.status(400).json({ message: "Oura Ring client id missing." });
|
||||
const state = encodeOAuthState(req);
|
||||
const params = {
|
||||
response_type: "code",
|
||||
scope: scopes.join(" "),
|
||||
client_id,
|
||||
redirect_uri: WEBAPP_URL + "/api/integrations/ouraring/callback",
|
||||
state,
|
||||
};
|
||||
const query = stringify(params);
|
||||
const url = `https://cloud.ouraring.com/oauth/authorize?${query}`;
|
||||
res.status(200).json({ url });
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
import { decodeOAuthState } from "../../_utils/decodeOAuthState";
|
||||
import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
|
||||
import getInstalledAppPath from "../../_utils/getInstalledAppPath";
|
||||
|
||||
const scopes = ["daily"];
|
||||
|
||||
let client_id = "";
|
||||
let client_secret = "";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { code } = req.query;
|
||||
if (typeof code !== "string") {
|
||||
res.status(400).json({ message: "No code returned" });
|
||||
return;
|
||||
}
|
||||
|
||||
const appKeys = await getAppKeysFromSlug("ouraring");
|
||||
if (typeof appKeys.client_id === "string") client_id = appKeys.client_id;
|
||||
if (typeof appKeys.client_secret === "string") client_secret = appKeys.client_secret;
|
||||
if (!client_id) return res.status(400).json({ message: "Oura Ring client id missing." });
|
||||
if (!client_secret) return res.status(400).json({ message: "Oura Ring client secret missing." });
|
||||
|
||||
const toUrlEncoded = (payload: Record<string, string>) =>
|
||||
Object.keys(payload)
|
||||
.map((key) => key + "=" + encodeURIComponent(payload[key]))
|
||||
.join("&");
|
||||
|
||||
const body = toUrlEncoded({
|
||||
client_id,
|
||||
grant_type: "authorization_code",
|
||||
code,
|
||||
scope: scopes.join(" "),
|
||||
redirect_uri: WEBAPP_URL + "/api/integrations/ouraring/callback",
|
||||
client_secret,
|
||||
});
|
||||
|
||||
const response = await fetch("https://cloud.ouraring.com/oauth/token", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
||||
},
|
||||
body,
|
||||
});
|
||||
|
||||
const responseBody = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
return res.redirect("/apps/installed?error=" + JSON.stringify(responseBody));
|
||||
}
|
||||
|
||||
responseBody.expires_in = Math.round(+new Date() / 1000 + responseBody.expires_in);
|
||||
|
||||
await prisma.credential.create({
|
||||
data: {
|
||||
type: "oura_ring_other_calendar",
|
||||
key: responseBody,
|
||||
userId: req.session?.user.id,
|
||||
appId: "ouraring",
|
||||
},
|
||||
});
|
||||
const state = decodeOAuthState(req);
|
||||
return res.redirect(
|
||||
getSafeRedirectUrl(state?.returnTo.replace("http:/", "http://")) ??
|
||||
getInstalledAppPath({ variant: "other", slug: "ouraring" })
|
||||
);
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
export { default as add } from "./add";
|
||||
export { default as callback } from "./callback";
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "Oura Ring",
|
||||
"slug": "ouraring",
|
||||
"type": "oura_ring_other_calendar",
|
||||
"logo": "icon.svg",
|
||||
"url": "https://ouraring.com",
|
||||
"variant": "other",
|
||||
"categories": ["other"],
|
||||
"publisher": "Leo Giovanetti",
|
||||
"email": "hello@leog.me",
|
||||
"description": "Rely on your Oura Ring Readiness score to allow bookings on the days you have data loaded from your ring. A score equal to or greater than 70, means you've recovered well enough so bookings are not blocked, below that, you need to pay attention as you're not fully recovered, so bookings will be blocked. No data means bookings are not blocked.",
|
||||
"isTemplate": false,
|
||||
"__createdUsingCli": true,
|
||||
"__template": "general-app-settings",
|
||||
"dirName": "ouraring"
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
export * as api from "./api";
|
||||
export * as lib from "./lib";
|
|
@ -0,0 +1,180 @@
|
|||
import { z } from "zod";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { handleErrorsJson } from "@calcom/lib/errors";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import prisma from "@calcom/prisma";
|
||||
import type {
|
||||
Calendar,
|
||||
EventBusyDate,
|
||||
IntegrationCalendar,
|
||||
NewCalendarEventType,
|
||||
} from "@calcom/types/Calendar";
|
||||
import type { CredentialPayload } from "@calcom/types/Credential";
|
||||
|
||||
import getParsedAppKeysFromSlug from "../../_utils/getParsedAppKeysFromSlug";
|
||||
|
||||
export type OuraRingAuthCredentials = {
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
expires_in: number;
|
||||
};
|
||||
|
||||
type OuraRingReturnedData = {
|
||||
day: string;
|
||||
score: number;
|
||||
};
|
||||
|
||||
const toUrlEncoded = (payload: Record<string, string>) =>
|
||||
Object.keys(payload)
|
||||
.map((key) => key + "=" + encodeURIComponent(payload[key]))
|
||||
.join("&");
|
||||
|
||||
const ouraRingAppKeysSchema = z.object({
|
||||
client_id: z.string(),
|
||||
client_secret: z.string(),
|
||||
});
|
||||
|
||||
export const getOuraRingAppKeys = async () => {
|
||||
return getParsedAppKeysFromSlug("ouraring", ouraRingAppKeysSchema);
|
||||
};
|
||||
|
||||
const refreshTokenResponseSchema = z.object({
|
||||
access_token: z.string(),
|
||||
expires_in: z
|
||||
.number()
|
||||
.transform((currentTimeOffsetInSeconds) => Math.round(+new Date() / 1000 + currentTimeOffsetInSeconds)),
|
||||
refresh_token: z.string().optional(),
|
||||
});
|
||||
|
||||
export default class OuraRingCalendarService implements Calendar {
|
||||
private integrationName = "";
|
||||
private log: typeof logger;
|
||||
private accessToken: string | null = null;
|
||||
auth: { getToken: () => Promise<string> };
|
||||
private apiUrl = "https://api.ouraring.com/v2";
|
||||
|
||||
constructor(credential: CredentialPayload) {
|
||||
this.integrationName = "oura_ring_other_calendar";
|
||||
this.auth = this.ouraRingAuth(credential);
|
||||
this.log = logger.getChildLogger({ prefix: [`[[lib] ${this.integrationName}`] });
|
||||
}
|
||||
|
||||
createEvent(): Promise<NewCalendarEventType | undefined> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
updateEvent(): Promise<NewCalendarEventType | NewCalendarEventType[]> {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
deleteEvent(): Promise<unknown> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
async getAvailability(dateFrom: string, dateTo: string): Promise<EventBusyDate[]> {
|
||||
try {
|
||||
const response = await this.fetcher(
|
||||
`/usercollection/daily_readiness?${toUrlEncoded({
|
||||
start_date: dayjs(dateFrom).format("YYYY-MM-DD"),
|
||||
end_date: dayjs(dateTo).format("YYYY-MM-DD"),
|
||||
})}`
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
return Promise.reject([]);
|
||||
}
|
||||
|
||||
const ouraInfo = (await response.json()) as { data: OuraRingReturnedData[] };
|
||||
|
||||
if (ouraInfo.data.length > 0) {
|
||||
const result = ouraInfo.data.flatMap((data: OuraRingReturnedData) => {
|
||||
if (data.score < 70) {
|
||||
return [
|
||||
{
|
||||
start: dayjs(data.day).startOf("day").toISOString(),
|
||||
end: dayjs(data.day).endOf("day").toISOString(),
|
||||
source: "Oura Ring",
|
||||
},
|
||||
];
|
||||
}
|
||||
return [];
|
||||
});
|
||||
return Promise.resolve(result);
|
||||
} else {
|
||||
this.log.debug("No data returned");
|
||||
}
|
||||
|
||||
return Promise.resolve([]);
|
||||
} catch (err) {
|
||||
this.log.error(err);
|
||||
return Promise.reject([]);
|
||||
}
|
||||
}
|
||||
|
||||
async listCalendars(): Promise<IntegrationCalendar[]> {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
private ouraRingAuth = (credential: CredentialPayload) => {
|
||||
const isExpired = (expiryDate: number) => {
|
||||
if (!expiryDate) {
|
||||
return true;
|
||||
} else {
|
||||
return expiryDate < Math.round(+new Date() / 1000);
|
||||
}
|
||||
};
|
||||
const ouraRingCredentials = credential.key as OuraRingAuthCredentials;
|
||||
|
||||
const refreshAccessToken = async (ouraRingCredentials: OuraRingAuthCredentials) => {
|
||||
const { client_id, client_secret } = await getOuraRingAppKeys();
|
||||
const response = await fetch("https://cloud.ouraring.com/oauth/token", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
body: toUrlEncoded({
|
||||
client_id,
|
||||
refresh_token: ouraRingCredentials.refresh_token,
|
||||
grant_type: "refresh_token",
|
||||
client_secret,
|
||||
}),
|
||||
});
|
||||
const responseJson = await handleErrorsJson(response);
|
||||
const tokenResponse = refreshTokenResponseSchema.safeParse(responseJson);
|
||||
ouraRingCredentials = { ...ouraRingCredentials, ...(tokenResponse.success && tokenResponse.data) };
|
||||
if (!tokenResponse.success) {
|
||||
this.log.error(
|
||||
"Oura Ring error grabbing new tokens ~ zodError:",
|
||||
tokenResponse.error,
|
||||
"API response:",
|
||||
responseJson
|
||||
);
|
||||
}
|
||||
await prisma.credential.update({
|
||||
where: {
|
||||
id: credential.id,
|
||||
},
|
||||
data: {
|
||||
key: ouraRingCredentials,
|
||||
},
|
||||
});
|
||||
return ouraRingCredentials.access_token;
|
||||
};
|
||||
|
||||
return {
|
||||
getToken: () =>
|
||||
isExpired(ouraRingCredentials.expires_in)
|
||||
? refreshAccessToken(ouraRingCredentials)
|
||||
: Promise.resolve(ouraRingCredentials.access_token),
|
||||
};
|
||||
};
|
||||
|
||||
private fetcher = async (endpoint: string, init?: RequestInit | undefined) => {
|
||||
this.accessToken = await this.auth.getToken();
|
||||
return fetch(`${this.apiUrl}${endpoint}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: "Bearer " + this.accessToken,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
...init,
|
||||
});
|
||||
};
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export { default as CalendarService } from "./CalendarService";
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"name": "@calcom/ouraring",
|
||||
"version": "0.0.0",
|
||||
"main": "./index.ts",
|
||||
"dependencies": {
|
||||
"@calcom/lib": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@calcom/types": "*"
|
||||
},
|
||||
"description": "Rely on your Oura Ring Readiness score to allow bookings on the days you have data loaded from your ring. A score equal to or greater than 70, means you've recovered well enough so bookings are not blocked, below that, you need to pay attention as you're not fully recovered, so bookings will be blocked. No data means bookings are not blocked."
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 630 KiB |
|
@ -0,0 +1,7 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="800px" height="800px" viewBox="0 0 48 48">
|
||||
<defs>
|
||||
<style>.a{fill:none;stroke:#929292;stroke-linecap:round;stroke-linejoin:round;stroke-width: 3;}</style>
|
||||
</defs>
|
||||
<circle class="a" cx="24" cy="27" r="16.5" />
|
||||
<line class="a" x1="16.2171" y1="4.5" x2="31.7829" y2="4.5" />
|
||||
</svg>
|
After Width: | Height: | Size: 337 B |
|
@ -150,7 +150,7 @@ export const getCachedResults = async (
|
|||
* TODO: Migrate credential type or appId
|
||||
*/
|
||||
const passedSelectedCalendars = selectedCalendars.filter((sc) => sc.integration === type);
|
||||
if (!passedSelectedCalendars.length) return [];
|
||||
if (!passedSelectedCalendars.length && type.indexOf("other_calendar") === -1) return [];
|
||||
/** We extract external Ids so we don't cache too much */
|
||||
const selectedCalendarIds = passedSelectedCalendars.map((sc) => sc.externalId);
|
||||
/** If we don't then we actually fetch external calendars (which can be very slow) */
|
||||
|
|
|
@ -23,7 +23,7 @@ const getCalendarsEvents = async (
|
|||
* TODO: Migrate credential type or appId
|
||||
*/
|
||||
const passedSelectedCalendars = selectedCalendars.filter((sc) => sc.integration === type);
|
||||
if (!passedSelectedCalendars.length) return [];
|
||||
if (!passedSelectedCalendars.length && type.indexOf("other_calendar") === -1) return [];
|
||||
/** We extract external Ids so we don't cache too much */
|
||||
const selectedCalendarIds = passedSelectedCalendars.map((sc) => sc.externalId);
|
||||
/** If we don't then we actually fetch external calendars (which can be very slow) */
|
||||
|
|
|
@ -7,7 +7,10 @@ export const getSafeRedirectUrl = (url = "") => {
|
|||
}
|
||||
|
||||
//It is important that this fn is given absolute URL because urls that don't start with HTTP can still deceive browser into redirecting to another domain
|
||||
if (url.search(/^https?:\/\//) === -1) {
|
||||
if (
|
||||
(process.env.NODE_ENV === "production" && url.search(/^https?:/) === -1) ||
|
||||
(process.env.NODE_ENV !== "production" && url.search(/^http?:/) === -1)
|
||||
) {
|
||||
throw new Error("Pass an absolute URL");
|
||||
}
|
||||
|
||||
|
|
|
@ -279,6 +279,12 @@ export default async function main() {
|
|||
base_url: (process.env.TANDEM_BASE_URL as string) || "https://tandem.chat",
|
||||
});
|
||||
}
|
||||
if (process.env.OURA_RING_CLIENT_ID && process.env.OURA_RING_CLIENT_SECRET) {
|
||||
await createApp("ouraring", "ouraring", ["other"], "oura_ring_other_calendar", {
|
||||
client_id: process.env.OURA_RING_CLIENT_ID as string,
|
||||
client_secret: process.env.OURA_RING_CLIENT_SECRET as string,
|
||||
});
|
||||
}
|
||||
if (process.env.ZOOM_CLIENT_ID && process.env.ZOOM_CLIENT_SECRET) {
|
||||
await createApp("zoom", "zoomvideo", ["conferencing"], "zoom_video", {
|
||||
client_id: process.env.ZOOM_CLIENT_ID,
|
||||
|
|
|
@ -215,7 +215,7 @@ export interface IntegrationCalendar extends Ensure<Partial<SelectedCalendar>, "
|
|||
}
|
||||
|
||||
export interface Calendar {
|
||||
createEvent(event: CalendarEvent): Promise<NewCalendarEventType>;
|
||||
createEvent(event: CalendarEvent): Promise<NewCalendarEventType | undefined>;
|
||||
|
||||
updateEvent(
|
||||
uid: string,
|
||||
|
|
|
@ -239,6 +239,8 @@
|
|||
"NEXTAUTH_URL",
|
||||
"NODE_ENV",
|
||||
"ORGANIZATIONS_ENABLED",
|
||||
"OURA_RING_CLIENT_ID",
|
||||
"OURA_RING_CLIENT_SECRET",
|
||||
"PAYMENT_FEE_FIXED",
|
||||
"PAYMENT_FEE_PERCENTAGE",
|
||||
"PLAYWRIGHT_HEADLESS",
|
||||
|
|
175
yarn.lock
175
yarn.lock
|
@ -3839,15 +3839,13 @@ __metadata:
|
|||
"@calcom/ui": "*"
|
||||
"@types/node": 16.9.1
|
||||
"@types/react": 18.0.26
|
||||
"@types/react-dom": ^18.0.9
|
||||
"@types/react-dom": 18.0.9
|
||||
eslint: ^8.34.0
|
||||
eslint-config-next: ^13.2.1
|
||||
next: ^13.4.6
|
||||
next: ^13.2.1
|
||||
next-auth: ^4.20.1
|
||||
postcss: ^8.4.18
|
||||
react: ^18.2.0
|
||||
react-dom: ^18.2.0
|
||||
tailwindcss: ^3.3.1
|
||||
typescript: ^4.9.4
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
@ -4354,6 +4352,15 @@ __metadata:
|
|||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@calcom/ouraring@workspace:packages/app-store/ouraring":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@calcom/ouraring@workspace:packages/app-store/ouraring"
|
||||
dependencies:
|
||||
"@calcom/lib": "*"
|
||||
"@calcom/types": "*"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@calcom/ping@workspace:packages/app-store/ping":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@calcom/ping@workspace:packages/app-store/ping"
|
||||
|
@ -4884,6 +4891,7 @@ __metadata:
|
|||
"@types/node": 16.9.1
|
||||
"@types/react": 18.0.26
|
||||
"@types/react-gtm-module": ^2.0.1
|
||||
"@types/xml2js": ^0.4.11
|
||||
"@vercel/analytics": ^0.1.6
|
||||
"@vercel/edge-functions-ui": ^0.2.1
|
||||
"@vercel/og": ^0.5.0
|
||||
|
@ -4927,12 +4935,14 @@ __metadata:
|
|||
react-merge-refs: 1.1.0
|
||||
react-twemoji: ^0.3.0
|
||||
react-use-measure: ^2.1.1
|
||||
react-wrap-balancer: ^1.0.0
|
||||
remark: ^14.0.2
|
||||
remark-html: ^14.0.1
|
||||
stripe: ^9.16.0
|
||||
tailwindcss: ^3.3.1
|
||||
typescript: ^4.9.4
|
||||
wait-on: ^7.0.1
|
||||
xml2js: ^0.6.0
|
||||
zod: ^3.20.2
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
@ -7382,6 +7392,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/env@npm:13.4.9":
|
||||
version: 13.4.9
|
||||
resolution: "@next/env@npm:13.4.9"
|
||||
checksum: 625f01571ac5dabbb90567a987fdc16eca86cd9cfd592edabf7d3e3024972cd83564d53202293163851b85fa643f846a5757c8696a3c44315c98dae5b634f122
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/eslint-plugin-next@npm:13.2.1":
|
||||
version: 13.2.1
|
||||
resolution: "@next/eslint-plugin-next@npm:13.2.1"
|
||||
|
@ -7398,6 +7415,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-darwin-arm64@npm:13.4.9":
|
||||
version: 13.4.9
|
||||
resolution: "@next/swc-darwin-arm64@npm:13.4.9"
|
||||
conditions: os=darwin & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-darwin-x64@npm:13.4.6":
|
||||
version: 13.4.6
|
||||
resolution: "@next/swc-darwin-x64@npm:13.4.6"
|
||||
|
@ -7405,6 +7429,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-darwin-x64@npm:13.4.9":
|
||||
version: 13.4.9
|
||||
resolution: "@next/swc-darwin-x64@npm:13.4.9"
|
||||
conditions: os=darwin & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-linux-arm64-gnu@npm:13.4.6":
|
||||
version: 13.4.6
|
||||
resolution: "@next/swc-linux-arm64-gnu@npm:13.4.6"
|
||||
|
@ -7412,6 +7443,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-linux-arm64-gnu@npm:13.4.9":
|
||||
version: 13.4.9
|
||||
resolution: "@next/swc-linux-arm64-gnu@npm:13.4.9"
|
||||
conditions: os=linux & cpu=arm64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-linux-arm64-musl@npm:13.4.6":
|
||||
version: 13.4.6
|
||||
resolution: "@next/swc-linux-arm64-musl@npm:13.4.6"
|
||||
|
@ -7419,6 +7457,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-linux-arm64-musl@npm:13.4.9":
|
||||
version: 13.4.9
|
||||
resolution: "@next/swc-linux-arm64-musl@npm:13.4.9"
|
||||
conditions: os=linux & cpu=arm64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-linux-x64-gnu@npm:13.4.6":
|
||||
version: 13.4.6
|
||||
resolution: "@next/swc-linux-x64-gnu@npm:13.4.6"
|
||||
|
@ -7426,6 +7471,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-linux-x64-gnu@npm:13.4.9":
|
||||
version: 13.4.9
|
||||
resolution: "@next/swc-linux-x64-gnu@npm:13.4.9"
|
||||
conditions: os=linux & cpu=x64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-linux-x64-musl@npm:13.4.6":
|
||||
version: 13.4.6
|
||||
resolution: "@next/swc-linux-x64-musl@npm:13.4.6"
|
||||
|
@ -7433,6 +7485,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-linux-x64-musl@npm:13.4.9":
|
||||
version: 13.4.9
|
||||
resolution: "@next/swc-linux-x64-musl@npm:13.4.9"
|
||||
conditions: os=linux & cpu=x64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-win32-arm64-msvc@npm:13.4.6":
|
||||
version: 13.4.6
|
||||
resolution: "@next/swc-win32-arm64-msvc@npm:13.4.6"
|
||||
|
@ -7440,6 +7499,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-win32-arm64-msvc@npm:13.4.9":
|
||||
version: 13.4.9
|
||||
resolution: "@next/swc-win32-arm64-msvc@npm:13.4.9"
|
||||
conditions: os=win32 & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-win32-ia32-msvc@npm:13.4.6":
|
||||
version: 13.4.6
|
||||
resolution: "@next/swc-win32-ia32-msvc@npm:13.4.6"
|
||||
|
@ -7447,6 +7513,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-win32-ia32-msvc@npm:13.4.9":
|
||||
version: 13.4.9
|
||||
resolution: "@next/swc-win32-ia32-msvc@npm:13.4.9"
|
||||
conditions: os=win32 & cpu=ia32
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-win32-x64-msvc@npm:13.4.6":
|
||||
version: 13.4.6
|
||||
resolution: "@next/swc-win32-x64-msvc@npm:13.4.6"
|
||||
|
@ -7454,6 +7527,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-win32-x64-msvc@npm:13.4.9":
|
||||
version: 13.4.9
|
||||
resolution: "@next/swc-win32-x64-msvc@npm:13.4.9"
|
||||
conditions: os=win32 & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@node-ipc/js-queue@npm:2.0.3":
|
||||
version: 2.0.3
|
||||
resolution: "@node-ipc/js-queue@npm:2.0.3"
|
||||
|
@ -12790,6 +12870,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/xml2js@npm:^0.4.11":
|
||||
version: 0.4.11
|
||||
resolution: "@types/xml2js@npm:0.4.11"
|
||||
dependencies:
|
||||
"@types/node": "*"
|
||||
checksum: d0e7d2a8c7e0a53147c777a6756bdea0eb26a3de0d3c1f42c17e63332e4e2dc532f687c19ca26a0518336f5de60e1ec4345d7ef71e7ba0656fcf3390c9388eb5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/yargs-parser@npm:*":
|
||||
version: 21.0.0
|
||||
resolution: "@types/yargs-parser@npm:21.0.0"
|
||||
|
@ -26998,6 +27087,65 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"next@npm:^13.2.1":
|
||||
version: 13.4.9
|
||||
resolution: "next@npm:13.4.9"
|
||||
dependencies:
|
||||
"@next/env": 13.4.9
|
||||
"@next/swc-darwin-arm64": 13.4.9
|
||||
"@next/swc-darwin-x64": 13.4.9
|
||||
"@next/swc-linux-arm64-gnu": 13.4.9
|
||||
"@next/swc-linux-arm64-musl": 13.4.9
|
||||
"@next/swc-linux-x64-gnu": 13.4.9
|
||||
"@next/swc-linux-x64-musl": 13.4.9
|
||||
"@next/swc-win32-arm64-msvc": 13.4.9
|
||||
"@next/swc-win32-ia32-msvc": 13.4.9
|
||||
"@next/swc-win32-x64-msvc": 13.4.9
|
||||
"@swc/helpers": 0.5.1
|
||||
busboy: 1.6.0
|
||||
caniuse-lite: ^1.0.30001406
|
||||
postcss: 8.4.14
|
||||
styled-jsx: 5.1.1
|
||||
watchpack: 2.4.0
|
||||
zod: 3.21.4
|
||||
peerDependencies:
|
||||
"@opentelemetry/api": ^1.1.0
|
||||
fibers: ">= 3.1.0"
|
||||
react: ^18.2.0
|
||||
react-dom: ^18.2.0
|
||||
sass: ^1.3.0
|
||||
dependenciesMeta:
|
||||
"@next/swc-darwin-arm64":
|
||||
optional: true
|
||||
"@next/swc-darwin-x64":
|
||||
optional: true
|
||||
"@next/swc-linux-arm64-gnu":
|
||||
optional: true
|
||||
"@next/swc-linux-arm64-musl":
|
||||
optional: true
|
||||
"@next/swc-linux-x64-gnu":
|
||||
optional: true
|
||||
"@next/swc-linux-x64-musl":
|
||||
optional: true
|
||||
"@next/swc-win32-arm64-msvc":
|
||||
optional: true
|
||||
"@next/swc-win32-ia32-msvc":
|
||||
optional: true
|
||||
"@next/swc-win32-x64-msvc":
|
||||
optional: true
|
||||
peerDependenciesMeta:
|
||||
"@opentelemetry/api":
|
||||
optional: true
|
||||
fibers:
|
||||
optional: true
|
||||
sass:
|
||||
optional: true
|
||||
bin:
|
||||
next: dist/bin/next
|
||||
checksum: 7adb9919dc50598e53bf32da2d0f90da325b66a23cb16c291c276d0683f81bade0bb21aeaeede10154ef53eabcb4953bf883f4d752852a62a6bd7be42021aef9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"next@npm:^13.4.6":
|
||||
version: 13.4.6
|
||||
resolution: "next@npm:13.4.6"
|
||||
|
@ -30755,6 +30903,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-wrap-balancer@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "react-wrap-balancer@npm:1.0.0"
|
||||
peerDependencies:
|
||||
react: ">=16.8.0 || ^17.0.0 || ^18"
|
||||
checksum: a3e263528b940e555894914d3a0d474cf60594578b4f2a34646d36ea5d9603f75270ec30ff1a7df3a9637cf014d6414c75a70abb82b0c4223bfbeda557ac19ca
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react@npm:^18.2.0":
|
||||
version: 18.2.0
|
||||
resolution: "react@npm:18.2.0"
|
||||
|
@ -37328,6 +37485,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"xml2js@npm:^0.6.0":
|
||||
version: 0.6.0
|
||||
resolution: "xml2js@npm:0.6.0"
|
||||
dependencies:
|
||||
sax: ">=0.6.0"
|
||||
xmlbuilder: ~11.0.0
|
||||
checksum: 437f353fd66d367bf158e9555a0625df9965d944e499728a5c6bc92a54a2763179b144f14b7e1c725040f56bbd22b0fa6cfcb09ec4faf39c45ce01efe631f40b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"xml@npm:=1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "xml@npm:1.0.1"
|
||||
|
|
Loading…
Reference in New Issue
Block a user