meetschedify 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Raj
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,355 @@
1
+ # meetSchedify
2
+
3
+ Enterprise-ready, multi-provider video meeting SDK for Node.js and TypeScript.
4
+
5
+ Today: **Google Meet** via Google Calendar.
6
+ Tomorrow: **Zoom, Microsoft Teams, Calendly** and more – with one unified API.
7
+
8
+ The goal is simple: after reading this README, any backend engineer should be able to plug in Google Meet scheduling end‑to‑end in under an hour.
9
+
10
+ ---
11
+
12
+ ## 1. What problem does meetSchedify solve?
13
+
14
+ - **One API** to schedule video meetings across different platforms.
15
+ - You decide **how to store tokens** (DB, Vault, etc.) – the library never touches your database directly.
16
+ - Built for **multi-tenant apps** (SaaS with many employers/customers).
17
+ - Clean separation:
18
+ - Frontend just calls your REST APIs.
19
+ - Backend calls `meetSchedify` to talk to Google/Zoom/Teams/etc.
20
+
21
+ ---
22
+
23
+ ## 2. Installation
24
+
25
+ Inside your backend project:
26
+
27
+ ```bash
28
+ npm install meetschedify googleapis
29
+ ```
30
+
31
+ > **Note**: `meetschedify` is the NPM package name. You can still refer to the concept as “meetSchedify” in your code and docs.
32
+
33
+ ---
34
+
35
+ ## 3. Prerequisites (Google Meet)
36
+
37
+ 1. **Node.js 18+**
38
+ 2. **Google Cloud project** with Calendar API enabled.
39
+ 3. **OAuth 2.0 Client ID** of type “Web application”.
40
+ 4. **Authorized redirect URI**, e.g.:
41
+ - `https://your-backend.com/api/v1/calendar/oauth/callback`
42
+ 5. Add these environment variables in your backend:
43
+
44
+ ```bash
45
+ GOOGLE_CLIENT_ID=your-google-oauth-client-id
46
+ GOOGLE_CLIENT_SECRET=your-google-oauth-client-secret
47
+ GOOGLE_REDIRECT_URI=https://your-backend.com/api/v1/calendar/oauth/callback
48
+ ```
49
+
50
+ You can name the variables differently if you want; just use the same names when you configure `GoogleMeetProvider`.
51
+
52
+ ---
53
+
54
+ ## 4. Core concepts (very short)
55
+
56
+ - **Tenant**: one customer/employer in your system. Identified by a stable `tenantId` (string).
57
+ - **Provider**: a video platform (Google Meet, Zoom, Teams, Calendly, …).
58
+ - **TokenStore**: your implementation that securely stores provider OAuth tokens per tenant.
59
+ - **MeetSchedifyClient**: main entry; it knows which providers are available and routes calls to them.
60
+
61
+ ---
62
+
63
+ ## 5. Backend integration – step by step
64
+
65
+ This section shows one concrete, end‑to‑end backend integration using **Express** and **Google Meet**.
66
+ You can easily adapt to NestJS, Fastify, etc.
67
+
68
+ ### Step 1 – Implement `TokenStore`
69
+
70
+ Create a file like `src/meetSchedifyTokenStore.ts` and implement the interface.
71
+
72
+ Example skeleton (you should connect this to your real DB model):
73
+
74
+ ```ts
75
+ import type {
76
+ TokenStore,
77
+ TokenStoreContext,
78
+ TokenRecord,
79
+ } from "meetschedify";
80
+
81
+ // Example only. Replace this with your real DB calls.
82
+ export class DbTokenStore implements TokenStore {
83
+ async getTokens(ctx: TokenStoreContext): Promise<TokenRecord | null> {
84
+ // 1. Query DB by ctx.tenantId + ctx.providerId
85
+ // 2. Return TokenRecord or null if not configured.
86
+ return null;
87
+ }
88
+
89
+ async saveTokens(ctx: TokenStoreContext, tokens: TokenRecord): Promise<void> {
90
+ // 1. Upsert into DB for ctx.tenantId + ctx.providerId.
91
+ // 2. Consider encrypting tokens at rest.
92
+ }
93
+ }
94
+ ```
95
+
96
+ **Production tip**: always encrypt `refreshToken` in the database (KMS, Vault, or field‑level encryption).
97
+
98
+ ---
99
+
100
+ ### Step 2 – Bootstrap meetSchedify
101
+
102
+ Create `src/meetSchedify.ts` (or similar) and initialise the client once:
103
+
104
+ ```ts
105
+ import { GoogleMeetProvider, MeetSchedifyClient } from "meetschedify";
106
+ import { DbTokenStore } from "./meetSchedifyTokenStore";
107
+
108
+ const tokenStore = new DbTokenStore();
109
+
110
+ const googleProvider = new GoogleMeetProvider(
111
+ {
112
+ clientId: process.env.GOOGLE_CLIENT_ID!,
113
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
114
+ redirectUri: process.env.GOOGLE_REDIRECT_URI!,
115
+ logger: (msg, meta) => {
116
+ // Avoid logging any PII or raw tokens here.
117
+ console.log(`[meetSchedify] ${msg}`, meta);
118
+ },
119
+ },
120
+ tokenStore
121
+ );
122
+
123
+ export const meetSchedify = new MeetSchedifyClient({
124
+ providers: [googleProvider],
125
+ });
126
+ ```
127
+
128
+ If you later add Zoom/Teams/Calendly providers, you will add them to this `providers: [...]` array.
129
+
130
+ ---
131
+
132
+ ### Step 3 – Add REST endpoints (Express)
133
+
134
+ Below are typical endpoints you would expose from your backend so the frontend can:
135
+ - check if calendar is connected,
136
+ - start OAuth connection to Google,
137
+ - handle the OAuth callback,
138
+ - schedule an interview/meeting.
139
+
140
+ All examples assume:
141
+ - You have auth middleware that puts the logged-in user on `req.user`.
142
+ - Your `tenantId` is `String(req.user._id)` (change as needed).
143
+
144
+ #### 3.1 – Check connection status
145
+
146
+ ```ts
147
+ import type { Request, Response } from "express";
148
+ import { meetSchedify } from "../meetSchedify";
149
+
150
+ export async function getCalendarConnectionStatus(req: Request, res: Response) {
151
+ const tenantId = String((req as any).user._id);
152
+
153
+ const status = await meetSchedify.getConnectionStatus(
154
+ tenantId,
155
+ "google-meet"
156
+ );
157
+
158
+ res.json({
159
+ connected: status.connected,
160
+ providerId: status.providerId,
161
+ });
162
+ }
163
+ ```
164
+
165
+ #### 3.2 – Get Google OAuth URL
166
+
167
+ ```ts
168
+ import type { Request, Response } from "express";
169
+ import { meetSchedify } from "../meetSchedify";
170
+
171
+ export async function getGoogleCalendarAuthUrl(req: Request, res: Response) {
172
+ const tenantId = String((req as any).user._id);
173
+ const returnUrl =
174
+ typeof req.query.returnUrl === "string"
175
+ ? req.query.returnUrl.trim()
176
+ : undefined;
177
+
178
+ const result = await meetSchedify.getAuthorizationUrl({
179
+ providerId: "google-meet",
180
+ tenantId,
181
+ returnUrl,
182
+ });
183
+
184
+ res.status(200).json({ url: result.url });
185
+ }
186
+ ```
187
+
188
+ #### 3.3 – OAuth callback route
189
+
190
+ ```ts
191
+ import type { Request, Response } from "express";
192
+ import { meetSchedify } from "../meetSchedify";
193
+
194
+ export async function googleCalendarOAuthCallback(
195
+ req: Request,
196
+ res: Response
197
+ ) {
198
+ const { code, state } = req.query;
199
+
200
+ if (!code || !state) {
201
+ return res.status(400).json({
202
+ message: "Missing code or state for Google OAuth",
203
+ });
204
+ }
205
+
206
+ const result = await meetSchedify.handleOAuthCallback("google-meet", {
207
+ code: String(code),
208
+ state: typeof state === "string" ? state : undefined,
209
+ });
210
+
211
+ // Build a safe redirect URL back to your frontend.
212
+ const fallbackOrigin = `${req.protocol}://${req.get("host")}`;
213
+ const base = (process.env.FRONTEND_URL || fallbackOrigin).replace(/\/$/, "");
214
+
215
+ // Optionally read a "returnUrl" from the state you encoded earlier.
216
+ let returnUrl = "/dashboard";
217
+ try {
218
+ const parsed = JSON.parse(String(state));
219
+ if (parsed.returnUrl && typeof parsed.returnUrl === "string") {
220
+ returnUrl = parsed.returnUrl.startsWith("/")
221
+ ? parsed.returnUrl
222
+ : `/${parsed.returnUrl}`;
223
+ }
224
+ } catch {
225
+ // ignore; fall back to default.
226
+ }
227
+
228
+ const sep = returnUrl.includes("?") ? "&" : "?";
229
+ const redirectUrl = `${base}${returnUrl}${sep}googleCalendar=connected&provider=${result.providerId}`;
230
+
231
+ return res.redirect(redirectUrl);
232
+ }
233
+ ```
234
+
235
+ #### 3.4 – Schedule an interview/meeting
236
+
237
+ This example mirrors a typical “schedule interview” payload (`date`, `startTime`, `endTime`, `timezone`, `locationType`, `ccEmails`, etc.).
238
+
239
+ ```ts
240
+ import type { Request, Response } from "express";
241
+ import { meetSchedify } from "../meetSchedify";
242
+
243
+ export async function scheduleInterviewWithCalendar(
244
+ req: Request,
245
+ res: Response
246
+ ) {
247
+ const tenantId = String((req as any).user._id);
248
+
249
+ const {
250
+ date,
251
+ startTime,
252
+ endTime,
253
+ location,
254
+ locationType, // "online" | "offline"
255
+ subject,
256
+ description,
257
+ ccEmails = [],
258
+ timezone = "Asia/Dubai",
259
+ addGoogleMeet = true,
260
+ candidateEmail,
261
+ candidateName,
262
+ } = req.body;
263
+
264
+ const startDateTimeLocal = `${date}T${startTime}:00`;
265
+ const endDateTimeLocal = `${date}T${endTime}:00`;
266
+
267
+ const meeting = await meetSchedify.createMeeting({
268
+ providerId: "google-meet",
269
+ tenantId,
270
+ title: subject,
271
+ description,
272
+ time: {
273
+ start: startDateTimeLocal,
274
+ end: endDateTimeLocal,
275
+ timezone,
276
+ },
277
+ location: {
278
+ type: locationType === "offline" ? "offline" : "online",
279
+ value: location,
280
+ },
281
+ primaryParticipant: candidateEmail
282
+ ? {
283
+ email: candidateEmail,
284
+ displayName: candidateName,
285
+ }
286
+ : undefined,
287
+ additionalParticipants: (ccEmails as string[]).map((email) => ({
288
+ email,
289
+ })),
290
+ addConferenceLink: addGoogleMeet !== false,
291
+ });
292
+
293
+ res.status(200).json({
294
+ status: "interview_scheduled",
295
+ meeting,
296
+ });
297
+ }
298
+ ```
299
+
300
+ You can still store any extra interview metadata (status, notes, etc.) in your own database – `meeting` just gives you the calendar/meeting details to save alongside.
301
+
302
+ ---
303
+
304
+ ## 6. Frontend usage (high level)
305
+
306
+ `meetSchedify` runs entirely on the backend. Your frontend just talks to your REST API.
307
+
308
+ Typical React flow:
309
+
310
+ 1. **Connect calendar**
311
+ - Call `GET /api/calendar/status` – if `connected === false`, show “Connect Google Calendar” button.
312
+ - Button calls `GET /api/calendar/auth-url` to get `url`.
313
+ - Redirect browser to that `url` to complete OAuth.
314
+ 2. **Schedule interview**
315
+ - Use your existing schedule modal (date, time, timezone, location type, CC emails, subject, description).
316
+ - Post to `POST /api/job-applicants/:applicationId/interview/schedule`.
317
+ - Backend calls `meetSchedify.createMeeting` and returns the meeting details and/or link.
318
+
319
+ No Google SDK is required on the frontend – everything is handled by your backend + `meetSchedify`.
320
+
321
+ ---
322
+
323
+ ## 7. Type reference (core)
324
+
325
+ Key exported types:
326
+
327
+ - `CreateMeetingRequest`
328
+ - `CreatedMeeting`
329
+ - `MeetingTimeRange`
330
+ - `MeetingLocation`
331
+ - `MeetingParticipant`
332
+ - `ProviderConnectionStatus`
333
+ - `TokenStore`, `TokenStoreContext`, `TokenRecord`
334
+ - `VideoMeetingProvider`
335
+ - `GoogleMeetProvider`
336
+ - `MeetSchedifyClient`
337
+
338
+ Refer to the TypeScript definitions in `src/` or compiled `.d.ts` in `dist/` for full detail.
339
+
340
+ ---
341
+
342
+ ## 8. Security and best practices
343
+
344
+ - **Never** log raw access/refresh tokens or full OAuth credentials.
345
+ - Use a secure **`TokenStore` implementation** (DB + KMS, Vault, or encrypted columns).
346
+ - Scope everything by a stable `tenantId`; avoid using emails or other PII as identifiers.
347
+ - Lock down your Google OAuth client in the Google Cloud console (allowed redirect URIs, JS origins, etc.).
348
+ - Rotate client secrets and use environment variables for configuration – never hardcode secrets.
349
+
350
+ ---
351
+
352
+ ## 9. License
353
+
354
+ MIT © Raj (WATNXT) and contributors
355
+
@@ -0,0 +1,15 @@
1
+ import type { AuthUrlResult, CreateMeetingRequest, CreatedMeeting, GetAuthUrlOptions, OAuthCallbackPayload, OAuthCallbackResult, ProviderConnectionStatus, ProviderId } from "./types";
2
+ import type { VideoMeetingProvider } from "./providers/BaseProvider";
3
+ export interface MeetSchedifyClientOptions {
4
+ providers: VideoMeetingProvider[];
5
+ }
6
+ export declare class MeetSchedifyClient {
7
+ private readonly providers;
8
+ constructor(options: MeetSchedifyClientOptions);
9
+ private getProvider;
10
+ getConnectionStatus(tenantId: string, providerId: ProviderId): Promise<ProviderConnectionStatus>;
11
+ getAuthorizationUrl(options: GetAuthUrlOptions): Promise<AuthUrlResult>;
12
+ handleOAuthCallback(providerId: ProviderId, payload: OAuthCallbackPayload): Promise<OAuthCallbackResult>;
13
+ createMeeting(req: CreateMeetingRequest): Promise<CreatedMeeting>;
14
+ }
15
+ //# sourceMappingURL=MeetSchedifyClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MeetSchedifyClient.d.ts","sourceRoot":"","sources":["../src/MeetSchedifyClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,oBAAoB,EACpB,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,UAAU,EACX,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAErE,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,oBAAoB,EAAE,CAAC;CACnC;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwC;gBAEtD,OAAO,EAAE,yBAAyB;IAS9C,OAAO,CAAC,WAAW;IAQb,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAKhG,mBAAmB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,aAAa,CAAC;IASvE,mBAAmB,CACvB,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC;IAQzB,aAAa,CAAC,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,cAAc,CAAC;CAIxE"}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MeetSchedifyClient = void 0;
4
+ class MeetSchedifyClient {
5
+ constructor(options) {
6
+ if (!options.providers.length) {
7
+ throw new Error("MeetSchedifyClient requires at least one provider");
8
+ }
9
+ this.providers = new Map(options.providers.map((p) => [p.id, p]));
10
+ }
11
+ getProvider(id) {
12
+ const provider = this.providers.get(id);
13
+ if (!provider) {
14
+ throw new Error(`Provider '${id}' is not registered in MeetSchedifyClient`);
15
+ }
16
+ return provider;
17
+ }
18
+ async getConnectionStatus(tenantId, providerId) {
19
+ const provider = this.getProvider(providerId);
20
+ return provider.isConnected({ tenantId });
21
+ }
22
+ async getAuthorizationUrl(options) {
23
+ const provider = this.getProvider(options.providerId);
24
+ if (!provider.getAuthorizationUrl) {
25
+ throw new Error(`Provider '${options.providerId}' does not support OAuth authorization URLs`);
26
+ }
27
+ const result = await provider.getAuthorizationUrl(options);
28
+ return result;
29
+ }
30
+ async handleOAuthCallback(providerId, payload) {
31
+ const provider = this.getProvider(providerId);
32
+ if (!provider.handleOAuthCallback) {
33
+ throw new Error(`Provider '${providerId}' does not support OAuth callbacks`);
34
+ }
35
+ return provider.handleOAuthCallback(payload);
36
+ }
37
+ async createMeeting(req) {
38
+ const provider = this.getProvider(req.providerId);
39
+ return provider.createMeeting(req);
40
+ }
41
+ }
42
+ exports.MeetSchedifyClient = MeetSchedifyClient;
43
+ //# sourceMappingURL=MeetSchedifyClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MeetSchedifyClient.js","sourceRoot":"","sources":["../src/MeetSchedifyClient.ts"],"names":[],"mappings":";;;AAgBA,MAAa,kBAAkB;IAG7B,YAAY,OAAkC;QAC5C,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACvE,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,IAAI,GAAG,CACtB,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CACxC,CAAC;IACJ,CAAC;IAEO,WAAW,CAAC,EAAc;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,aAAa,EAAE,2CAA2C,CAAC,CAAC;QAC9E,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,QAAgB,EAAE,UAAsB;QAChE,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;QAC9C,OAAO,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,OAA0B;QAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACtD,IAAI,CAAC,QAAQ,CAAC,mBAAmB,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,aAAa,OAAO,CAAC,UAAU,6CAA6C,CAAC,CAAC;QAChG,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAC3D,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,mBAAmB,CACvB,UAAsB,EACtB,OAA6B;QAE7B,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;QAC9C,IAAI,CAAC,QAAQ,CAAC,mBAAmB,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,aAAa,UAAU,oCAAoC,CAAC,CAAC;QAC/E,CAAC;QACD,OAAO,QAAQ,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,GAAyB;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAClD,OAAO,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;CACF;AAjDD,gDAiDC"}
@@ -0,0 +1,6 @@
1
+ export * from "./types";
2
+ export * from "./store/TokenStore";
3
+ export * from "./providers/BaseProvider";
4
+ export * from "./providers/google/GoogleMeetProvider";
5
+ export * from "./MeetSchedifyClient";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,uCAAuC,CAAC;AACtD,cAAc,sBAAsB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./types"), exports);
18
+ __exportStar(require("./store/TokenStore"), exports);
19
+ __exportStar(require("./providers/BaseProvider"), exports);
20
+ __exportStar(require("./providers/google/GoogleMeetProvider"), exports);
21
+ __exportStar(require("./MeetSchedifyClient"), exports);
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,qDAAmC;AACnC,2DAAyC;AACzC,wEAAsD;AACtD,uDAAqC"}
@@ -0,0 +1,11 @@
1
+ import type { AuthUrlResult, CreateMeetingRequest, CreatedMeeting, GetAuthUrlOptions, OAuthCallbackPayload, OAuthCallbackResult, ProviderConnectionStatus, ProviderId } from "../types";
2
+ export interface VideoMeetingProvider {
3
+ readonly id: ProviderId;
4
+ isConnected(ctx: {
5
+ tenantId: string;
6
+ }): Promise<ProviderConnectionStatus>;
7
+ getAuthorizationUrl?(options: GetAuthUrlOptions): Promise<AuthUrlResult> | AuthUrlResult;
8
+ handleOAuthCallback?(payload: OAuthCallbackPayload): Promise<OAuthCallbackResult>;
9
+ createMeeting(request: CreateMeetingRequest): Promise<CreatedMeeting>;
10
+ }
11
+ //# sourceMappingURL=BaseProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseProvider.d.ts","sourceRoot":"","sources":["../../src/providers/BaseProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,oBAAoB,EACpB,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,UAAU,EACX,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC;IAExB,WAAW,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAE1E,mBAAmB,CAAC,CAClB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,aAAa,CAAC,GAAG,aAAa,CAAC;IAE1C,mBAAmB,CAAC,CAClB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAEhC,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CACvE"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=BaseProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseProvider.js","sourceRoot":"","sources":["../../src/providers/BaseProvider.ts"],"names":[],"mappings":""}
@@ -0,0 +1,28 @@
1
+ import type { AuthUrlResult, CreateMeetingRequest, CreatedMeeting, GetAuthUrlOptions, OAuthCallbackPayload, OAuthCallbackResult, ProviderConnectionStatus } from "../../types";
2
+ import type { TokenStore } from "../../store/TokenStore";
3
+ import type { VideoMeetingProvider } from "../BaseProvider";
4
+ export interface GoogleMeetProviderConfig {
5
+ clientId: string;
6
+ clientSecret: string;
7
+ redirectUri: string;
8
+ /**
9
+ * Optional function used only for logging/sanitised audit trails.
10
+ * Never log raw tokens or PII here.
11
+ */
12
+ logger?: (msg: string, meta?: Record<string, unknown>) => void;
13
+ }
14
+ export declare class GoogleMeetProvider implements VideoMeetingProvider {
15
+ readonly id: "google-meet";
16
+ private readonly tokenStore;
17
+ private readonly cfg;
18
+ constructor(cfg: GoogleMeetProviderConfig, tokenStore: TokenStore);
19
+ private getOAuthClient;
20
+ private getAuthorizedClient;
21
+ isConnected(ctx: {
22
+ tenantId: string;
23
+ }): Promise<ProviderConnectionStatus>;
24
+ getAuthorizationUrl(options: GetAuthUrlOptions): AuthUrlResult;
25
+ handleOAuthCallback(payload: OAuthCallbackPayload): Promise<OAuthCallbackResult>;
26
+ createMeeting(req: CreateMeetingRequest): Promise<CreatedMeeting>;
27
+ }
28
+ //# sourceMappingURL=GoogleMeetProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GoogleMeetProvider.d.ts","sourceRoot":"","sources":["../../../src/providers/google/GoogleMeetProvider.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,aAAa,EACb,oBAAoB,EACpB,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACzB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,UAAU,EAAqB,MAAM,wBAAwB,CAAC;AAC5E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CAChE;AAID,qBAAa,kBAAmB,YAAW,oBAAoB;IAC7D,SAAgB,EAAE,EAAG,aAAa,CAAU;IAE5C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA2B;gBAEnC,GAAG,EAAE,wBAAwB,EAAE,UAAU,EAAE,UAAU;IAKjE,OAAO,CAAC,cAAc;YAQR,mBAAmB;IAoC3B,WAAW,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAY/E,mBAAmB,CAAC,OAAO,EAAE,iBAAiB,GAAG,aAAa;IA0BxD,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAsChF,aAAa,CAAC,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,cAAc,CAAC;CA2GxE"}
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GoogleMeetProvider = void 0;
4
+ const googleapis_1 = require("googleapis");
5
+ const crypto_1 = require("crypto");
6
+ const SCOPES = ["https://www.googleapis.com/auth/calendar"];
7
+ class GoogleMeetProvider {
8
+ constructor(cfg, tokenStore) {
9
+ this.id = "google-meet";
10
+ this.cfg = cfg;
11
+ this.tokenStore = tokenStore;
12
+ }
13
+ getOAuthClient() {
14
+ return new googleapis_1.google.auth.OAuth2(this.cfg.clientId, this.cfg.clientSecret, this.cfg.redirectUri);
15
+ }
16
+ async getAuthorizedClient(ctx) {
17
+ var _a, _b, _c, _d, _e, _f;
18
+ const existing = await this.tokenStore.getTokens(ctx);
19
+ if (!existing || !existing.refreshToken) {
20
+ const err = new Error("GOOGLE_NOT_CONNECTED");
21
+ err.code = "GOOGLE_NOT_CONNECTED";
22
+ throw err;
23
+ }
24
+ const oauth2Client = this.getOAuthClient();
25
+ oauth2Client.setCredentials({
26
+ access_token: (_a = existing.accessToken) !== null && _a !== void 0 ? _a : undefined,
27
+ refresh_token: (_b = existing.refreshToken) !== null && _b !== void 0 ? _b : undefined,
28
+ expiry_date: (_c = existing.expiryDate) !== null && _c !== void 0 ? _c : undefined,
29
+ });
30
+ const now = Date.now();
31
+ const fiveMinutesMs = 5 * 60 * 1000;
32
+ const needsRefresh = !existing.accessToken ||
33
+ !existing.expiryDate ||
34
+ existing.expiryDate <= now + fiveMinutesMs;
35
+ if (needsRefresh) {
36
+ const { credentials } = await oauth2Client.refreshAccessToken();
37
+ await this.tokenStore.saveTokens(ctx, {
38
+ accessToken: (_d = credentials.access_token) !== null && _d !== void 0 ? _d : null,
39
+ refreshToken: (_e = credentials.refresh_token) !== null && _e !== void 0 ? _e : existing.refreshToken,
40
+ expiryDate: (_f = credentials.expiry_date) !== null && _f !== void 0 ? _f : null,
41
+ raw: credentials,
42
+ });
43
+ oauth2Client.setCredentials(credentials);
44
+ }
45
+ return oauth2Client;
46
+ }
47
+ async isConnected(ctx) {
48
+ const tokens = await this.tokenStore.getTokens({
49
+ tenantId: ctx.tenantId,
50
+ providerId: this.id,
51
+ });
52
+ return {
53
+ tenantId: ctx.tenantId,
54
+ providerId: this.id,
55
+ connected: !!(tokens && tokens.refreshToken),
56
+ };
57
+ }
58
+ getAuthorizationUrl(options) {
59
+ var _a, _b, _c, _d;
60
+ const oauth2Client = this.getOAuthClient();
61
+ const statePayload = {
62
+ tenantId: options.tenantId,
63
+ returnUrl: (_a = options.returnUrl) !== null && _a !== void 0 ? _a : "",
64
+ customState: (_b = options.customState) !== null && _b !== void 0 ? _b : {},
65
+ };
66
+ const state = JSON.stringify(statePayload);
67
+ const url = oauth2Client.generateAuthUrl({
68
+ access_type: "offline",
69
+ prompt: "consent",
70
+ scope: SCOPES,
71
+ state,
72
+ });
73
+ (_d = (_c = this.cfg).logger) === null || _d === void 0 ? void 0 : _d.call(_c, "google-meet.auth-url.generated", {
74
+ tenantId: options.tenantId,
75
+ });
76
+ return {
77
+ providerId: this.id,
78
+ url,
79
+ };
80
+ }
81
+ async handleOAuthCallback(payload) {
82
+ var _a, _b, _c, _d, _e;
83
+ if (!payload.code || !payload.state) {
84
+ throw new Error("Missing code or state for Google OAuth callback");
85
+ }
86
+ let parsedState;
87
+ try {
88
+ parsedState = JSON.parse(payload.state);
89
+ }
90
+ catch {
91
+ throw new Error("Invalid state payload for Google OAuth callback");
92
+ }
93
+ const oauth2Client = this.getOAuthClient();
94
+ const { tokens } = await oauth2Client.getToken(payload.code);
95
+ const ctx = {
96
+ tenantId: parsedState.tenantId,
97
+ providerId: this.id,
98
+ };
99
+ await this.tokenStore.saveTokens(ctx, {
100
+ accessToken: (_a = tokens.access_token) !== null && _a !== void 0 ? _a : null,
101
+ refreshToken: (_b = tokens.refresh_token) !== null && _b !== void 0 ? _b : null,
102
+ expiryDate: (_c = tokens.expiry_date) !== null && _c !== void 0 ? _c : null,
103
+ raw: tokens,
104
+ });
105
+ (_e = (_d = this.cfg).logger) === null || _e === void 0 ? void 0 : _e.call(_d, "google-meet.oauth.tokens-saved", {
106
+ tenantId: parsedState.tenantId,
107
+ hasRefreshToken: !!tokens.refresh_token,
108
+ });
109
+ return {
110
+ tenantId: parsedState.tenantId,
111
+ providerId: this.id,
112
+ };
113
+ }
114
+ async createMeeting(req) {
115
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
116
+ const { tenantId, title, description, time, location, primaryParticipant, additionalParticipants, addConferenceLink = true } = req;
117
+ if (!(time === null || time === void 0 ? void 0 : time.start) || !(time === null || time === void 0 ? void 0 : time.end)) {
118
+ throw new Error("time.start and time.end are required");
119
+ }
120
+ const ctx = { tenantId, providerId: this.id };
121
+ const auth = await this.getAuthorizedClient(ctx);
122
+ const calendar = googleapis_1.google.calendar({ version: "v3", auth });
123
+ const attendees = [];
124
+ const pushParticipant = (p) => {
125
+ var _a;
126
+ const email = (_a = p === null || p === void 0 ? void 0 : p.email) === null || _a === void 0 ? void 0 : _a.trim().toLowerCase();
127
+ if (!email)
128
+ return;
129
+ attendees.push({
130
+ email,
131
+ displayName: p === null || p === void 0 ? void 0 : p.displayName,
132
+ responseStatus: "needsAction",
133
+ });
134
+ };
135
+ pushParticipant(primaryParticipant !== null && primaryParticipant !== void 0 ? primaryParticipant : undefined);
136
+ (additionalParticipants !== null && additionalParticipants !== void 0 ? additionalParticipants : []).forEach((p) => pushParticipant(p));
137
+ const event = {
138
+ summary: title,
139
+ description: description !== null && description !== void 0 ? description : "",
140
+ location: addConferenceLink ? undefined : location === null || location === void 0 ? void 0 : location.value,
141
+ start: {
142
+ dateTime: time.start,
143
+ timeZone: (_a = time.timezone) !== null && _a !== void 0 ? _a : "UTC",
144
+ },
145
+ end: {
146
+ dateTime: time.end,
147
+ timeZone: (_b = time.timezone) !== null && _b !== void 0 ? _b : "UTC",
148
+ },
149
+ attendees,
150
+ ...(addConferenceLink
151
+ ? {
152
+ conferenceData: {
153
+ createRequest: {
154
+ requestId: (0, crypto_1.randomUUID)(),
155
+ conferenceSolutionKey: {
156
+ type: "hangoutsMeet",
157
+ },
158
+ },
159
+ },
160
+ }
161
+ : {}),
162
+ };
163
+ const { data: created } = await calendar.events.insert({
164
+ calendarId: "primary",
165
+ requestBody: event,
166
+ sendUpdates: "all",
167
+ ...(addConferenceLink ? { conferenceDataVersion: 1 } : {}),
168
+ });
169
+ const links = [];
170
+ const entryPoints = (_d = (_c = created.conferenceData) === null || _c === void 0 ? void 0 : _c.entryPoints) !== null && _d !== void 0 ? _d : [];
171
+ for (const ep of entryPoints) {
172
+ if (!ep.uri)
173
+ continue;
174
+ const type = ep.entryPointType === "video"
175
+ ? "video"
176
+ : ep.entryPointType === "phone"
177
+ ? "phone"
178
+ : ep.entryPointType === "sip"
179
+ ? "sip"
180
+ : "custom";
181
+ links.push({ type, uri: ep.uri });
182
+ }
183
+ const primaryVideoLink = (_g = (_f = (_e = links.find((l) => l.type === "video")) === null || _e === void 0 ? void 0 : _e.uri) !== null && _f !== void 0 ? _f : created.hangoutLink) !== null && _g !== void 0 ? _g : null;
184
+ const result = {
185
+ providerId: this.id,
186
+ providerEventId: (_h = created.id) !== null && _h !== void 0 ? _h : "",
187
+ title: (_j = created.summary) !== null && _j !== void 0 ? _j : title,
188
+ description: (_k = created.description) !== null && _k !== void 0 ? _k : description,
189
+ time: {
190
+ start: (_m = (_l = created.start) === null || _l === void 0 ? void 0 : _l.dateTime) !== null && _m !== void 0 ? _m : time.start,
191
+ end: (_p = (_o = created.end) === null || _o === void 0 ? void 0 : _o.dateTime) !== null && _p !== void 0 ? _p : time.end,
192
+ timezone: (_q = time.timezone) !== null && _q !== void 0 ? _q : "UTC",
193
+ },
194
+ location: addConferenceLink
195
+ ? { type: "online", value: primaryVideoLink !== null && primaryVideoLink !== void 0 ? primaryVideoLink : undefined }
196
+ : location,
197
+ calendarUrl: (_r = created.htmlLink) !== null && _r !== void 0 ? _r : null,
198
+ conferenceLink: primaryVideoLink,
199
+ conferenceLinks: links,
200
+ raw: created,
201
+ };
202
+ (_t = (_s = this.cfg).logger) === null || _t === void 0 ? void 0 : _t.call(_s, "google-meet.meeting.created", {
203
+ tenantId,
204
+ eventId: result.providerEventId,
205
+ hasConferenceLink: !!result.conferenceLink,
206
+ });
207
+ return result;
208
+ }
209
+ }
210
+ exports.GoogleMeetProvider = GoogleMeetProvider;
211
+ //# sourceMappingURL=GoogleMeetProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GoogleMeetProvider.js","sourceRoot":"","sources":["../../../src/providers/google/GoogleMeetProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { ProviderId, TenantContext, TokenRecord } from "../types";
2
+ export interface TokenStoreContext extends TenantContext {
3
+ providerId: ProviderId;
4
+ }
5
+ /**
6
+ * Abstraction for securely persisting provider credentials.
7
+ * You will plug in your own implementation (DB, KMS, Vault, etc.).
8
+ */
9
+ export interface TokenStore {
10
+ getTokens(ctx: TokenStoreContext): Promise<TokenRecord | null>;
11
+ saveTokens(ctx: TokenStoreContext, tokens: TokenRecord): Promise<void>;
12
+ }
13
+ //# sourceMappingURL=TokenStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TokenStore.d.ts","sourceRoot":"","sources":["../../src/store/TokenStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvE,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,UAAU,EAAE,UAAU,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC/D,UAAU,CAAC,GAAG,EAAE,iBAAiB,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxE"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=TokenStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TokenStore.js","sourceRoot":"","sources":["../../src/store/TokenStore.ts"],"names":[],"mappings":""}
@@ -0,0 +1,104 @@
1
+ export type ProviderId = "google-meet" | "zoom" | "microsoft-teams" | "calendly" | string;
2
+ export interface TenantContext {
3
+ /**
4
+ * Stable, non-PII identifier for the tenant / employer / account.
5
+ * You own and control this id – we never assume DB structure.
6
+ */
7
+ tenantId: string;
8
+ }
9
+ export interface MeetingParticipant {
10
+ email: string;
11
+ displayName?: string;
12
+ optional?: boolean;
13
+ }
14
+ export interface MeetingTimeRange {
15
+ start: string;
16
+ end: string;
17
+ timezone?: string;
18
+ }
19
+ export type LocationType = "online" | "offline";
20
+ export interface MeetingLocation {
21
+ type: LocationType;
22
+ /**
23
+ * For offline meetings, this is the physical address.
24
+ * For online meetings, providers typically attach the URL themselves,
25
+ * but you can still pass a custom value if needed.
26
+ */
27
+ value?: string;
28
+ }
29
+ export interface CreateMeetingRequest extends TenantContext {
30
+ providerId: ProviderId;
31
+ title: string;
32
+ description?: string;
33
+ time: MeetingTimeRange;
34
+ location?: MeetingLocation;
35
+ primaryParticipant?: MeetingParticipant | null;
36
+ additionalParticipants?: MeetingParticipant[];
37
+ /**
38
+ * Whether to attach a video-conference link if supported
39
+ * (e.g. Google Meet, Teams, Zoom).
40
+ */
41
+ addConferenceLink?: boolean;
42
+ /**
43
+ * Arbitrary metadata that you want to round-trip.
44
+ * Never logged or parsed by the SDK.
45
+ */
46
+ metadata?: Record<string, unknown>;
47
+ }
48
+ export interface MeetingLink {
49
+ type: "video" | "phone" | "sip" | "custom";
50
+ uri: string;
51
+ }
52
+ export interface CreatedMeeting {
53
+ providerId: ProviderId;
54
+ providerEventId: string;
55
+ title: string;
56
+ description?: string;
57
+ time: MeetingTimeRange;
58
+ location?: MeetingLocation;
59
+ calendarUrl?: string | null;
60
+ conferenceLink?: string | null;
61
+ conferenceLinks?: MeetingLink[];
62
+ raw?: unknown;
63
+ }
64
+ export interface ProviderConnectionStatus extends TenantContext {
65
+ providerId: ProviderId;
66
+ connected: boolean;
67
+ }
68
+ export interface GetAuthUrlOptions extends TenantContext {
69
+ /**
70
+ * Which video provider to use, e.g. "google-meet".
71
+ */
72
+ providerId: ProviderId;
73
+ /**
74
+ * Optional return URL you can encode in provider state and
75
+ * use to redirect the user back to a specific frontend route.
76
+ */
77
+ returnUrl?: string;
78
+ /**
79
+ * Opaque state payload you want to round-trip through the provider.
80
+ * Not all providers will support full custom state.
81
+ */
82
+ customState?: Record<string, unknown>;
83
+ }
84
+ export interface AuthUrlResult {
85
+ providerId: ProviderId;
86
+ url: string;
87
+ }
88
+ export interface OAuthCallbackPayload {
89
+ code: string;
90
+ state?: string | null;
91
+ }
92
+ export interface OAuthCallbackResult extends TenantContext {
93
+ providerId: ProviderId;
94
+ }
95
+ export interface TokenRecord {
96
+ accessToken: string | null;
97
+ refreshToken: string | null;
98
+ expiryDate: number | null;
99
+ /**
100
+ * Raw provider-specific credentials object (e.g. Google OAuth2 credentials).
101
+ */
102
+ raw?: unknown;
103
+ }
104
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "meetschedify",
3
+ "version": "0.1.0",
4
+ "description": "Enterprise-ready multi-provider video meeting scheduling SDK (Google Meet today, Zoom/Teams/Calendly and more tomorrow).",
5
+ "license": "MIT",
6
+ "author": "Raj Kumar Singha",
7
+ "keywords": [
8
+ "meeting",
9
+ "scheduler",
10
+ "video",
11
+ "google-meet",
12
+ "zoom",
13
+ "microsoft-teams",
14
+ "calendly",
15
+ "calendar",
16
+ "enterprise"
17
+ ],
18
+ "repository": {
19
+ "type": "git",
20
+ "url": ""
21
+ },
22
+ "bugs": {
23
+ "url": ""
24
+ },
25
+ "homepage": "",
26
+ "main": "dist/index.cjs",
27
+ "module": "dist/index.mjs",
28
+ "types": "dist/index.d.ts",
29
+ "exports": {
30
+ ".": {
31
+ "import": "./dist/index.mjs",
32
+ "require": "./dist/index.cjs",
33
+ "types": "./dist/index.d.ts"
34
+ }
35
+ },
36
+ "sideEffects": false,
37
+ "engines": {
38
+ "node": ">=18.0.0"
39
+ },
40
+ "files": ["dist", "README.md", "LICENSE"],
41
+ "scripts": {
42
+ "build": "tsc -p tsconfig.build.json",
43
+ "clean": "rimraf dist",
44
+ "lint": "eslint \"src/**/*.{ts,tsx}\"",
45
+ "prepublishOnly": "npm run clean && npm run build && npm test",
46
+ "test": "echo \"Add tests for meetSchedify here\""
47
+ },
48
+ "dependencies": {
49
+ "googleapis": "^131.0.0"
50
+ },
51
+ "peerDependencies": {
52
+ "googleapis": ">=100.0.0"
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "^22.0.0",
56
+ "typescript": "^5.6.0",
57
+ "rimraf": "^5.0.0",
58
+ "eslint": "^9.0.0"
59
+ }
60
+ }