Compare commits

...
Author SHA1 Message Date
emrysal 2a0991c259 chore: release v5.6.1
Post release / release (push) Canceled after 0s
2025-08-18 23:55:48 +00:00
Eunjae LeeandDevin AI 2e454c9723 refactor: rename insights routing service files (#22917)
- Rename insightsRoutingDI.ts -> InsightsRoutingDIService.ts
- Rename insightsRoutingBase.ts -> InsightsRoutingBaseService.ts
- Update all import statements to use new file names
- No functional changes, only file renames and import updates

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-08-18 15:15:21 +00:00
Anik Dhabal Babu 7e6d94c8e0 fix: team admin/owner can't change others member role (#23161)
* fix: team admin/owner can't change member role

* update

* Update role-management.factory.ts

* address review
2025-08-18 14:57:22 +00:00
Volnei Munhoz e18b790ccd chore: Upgrade Turborepo from 1 to 2.5 (#23101)
* upgrade turborepo from 1 to 2.5

* Fix tsconfig.json paths path

* chore: fix root path on tsconfig.paths

* chore: add baseUrl to tsconfig.json

* chore: tsconfig.json issue

* try: CI without tsconfig paths

* chore: add only local paths on tsconfig.json

* chore: Fix docs

* Fix missing env var on turbo.json
2025-08-18 11:17:27 -03:00
Morgan 0274417ad1 fix: api v2 workflow controller trigger validation (#23159) 2025-08-18 16:15:40 +03:00
a8a3a93ace fix: redirect to existing payment session on paid booking retry (#23096)
* fix: redirect to existing payment page on booking retry

- Extract existing payment UID from unpaid bookings
- Return paymentUid and paymentId when payment form should be shown
- Prevents payment form bypass by redirecting to original payment session
- Maintains payment continuity without creating duplicate bookings

Fixes issue where canceled payments showed success page on rebooking

Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>

* test: add unit test for payment retry scenario

- Test verifies existing payment UID is returned on booking retry
- Ensures no duplicate bookings are created in database
- Validates paymentRequired flag is set correctly for unpaid bookings
- Covers the payment redirect fix in handleNewBooking.ts

Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>

* update

* fix: restore calendar event deletion for round robin reassignment

- Add deleteEventsAndMeetings call back to EventManager.reschedule() when changedOrganizer is true
- Add unit test to verify calendar deletion during round robin reassignment
- Fixes regression where calendar events weren't deleted from original host's calendar

Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>

* Update roundRobinReassignment.test.ts

* Update EventManager.ts

* test: update payment retry test title to single descriptive line

- Changed from numbered list format to concise single line description
- Test functionality remains unchanged and passes successfully

Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-08-18 11:44:40 +00:00
88bbab216e fix: Consistently remove a member from organization/sub-team/team - through all the APIs and webapp actions (#22806)
* Fix membership deletion handling and add tests

* fixes

* revert api-v2 specific changes

* Add more tests

* refactor: Move removeMember function into TeamService as private static methods

- Moved removeMember and all related helper functions from separate file into TeamService class
- Made all functions private static methods instead of exporting them
- Deleted the original removeMember.ts file since it's no longer needed
- Updated imports to use the new location
- All integration tests pass successfully

* refactor: Rename memberId to userId in TeamService methods

- Renamed memberId parameter to userId in removeMember private method
- Renamed memberIds parameter to userIds in removeMembers public method
- Updated all calls to removeMembers to use userIds instead of memberIds
- Parameter names now accurately reflect that they are user IDs, not membership IDs

* test: add service unit tests and simplify e2e tests for membership deletion

- Add unit tests for membership deletion services
- Remove redundant deletion behavior tests from controllers
- Keep only happy path tests in e2e controller tests
- Fix unused imports and variables

* package.json version

* fix unit teswtes

* No specs file

* fix unit tests

* fix: Add platform-libraries build step to E2E API v2 workflow

The E2E API v2 tests were failing with TypeScript compilation errors because
TeamService imports from @calcom/platform-libraries require the package to be
built first to generate the dist/ folder with compiled exports.

This adds the same build step that was added to unit-tests.yml to resolve
the module resolution errors.

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* Making tests clearer and easier to undestand

* chore: bump platform libs

* chore: bump platform libs

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
Co-authored-by: cal.com <morgan@cal.com>
2025-08-18 08:38:32 -03:00
Chinmay Patil f74434a282 fix: show temporarily out-of-office message on all screen sizes (#23130) 2025-08-18 16:56:49 +05:30
Joe Au-Yeung ed6f9d8252 fix: Add cal.eu to IS_CALCOM (#23111)
* Add cal.eu to `IS_CALCOM`

* Create `getCalcomUrl` function

* Use `getCalcomURl` in generate metadata

* Use `getCalcomURl` in `buildCanonical`

* Linting fix
2025-08-18 16:04:48 +05:30
Benny Joo a19ccab16a perf: use promise.all in /apps/installed (#23146) 2025-08-18 10:11:22 +05:30
Benny Joo 77369a13f1 chore: remove unused orgMigrations files (#23056) 2025-08-17 22:18:14 -03:00
Anik Dhabal Babu 8231356d2a test: E2E test to verify event multiple duration works correctly (#23144)
* fix: multiple booking duration isn't working

* followup test
2025-08-17 20:26:14 +01:00
Anik Dhabal Babu 469a942b5a fix: multiple booking duration isn't working (#23140) 2025-08-17 17:08:27 +00:00
keithwillcode 0afe6cf5d1 chore: release v5.6.0
Post release / release (push) Canceled after 0s
2025-08-15 15:32:31 +00:00
56 changed files with 1928 additions and 836 deletions

No files matched your search

+2 -2
View File
@@ -724,7 +724,7 @@ ${we.map(pe=>`Not found: ${lo(pe)}
To understand the dependency tree requiring vulnerable packages, check the raw report with the \`--json\` flag or use \`yarn why <package>\` to get more information as to who depends on them.
`,examples:[["Checks for known security issues with the installed packages. The output is a list of known issues.","yarn npm audit"],["Audit dependencies in all workspaces","yarn npm audit --all"],["Limit auditing to `dependencies` (excludes `devDependencies`)","yarn npm audit --environment production"],["Show audit report as valid JSON","yarn npm audit --json"],["Audit all direct and transitive dependencies","yarn npm audit --recursive"],["Output moderate (or more severe) vulnerabilities","yarn npm audit --severity moderate"],["Exclude certain packages","yarn npm audit --exclude package1 --exclude package2"],["Ignore specific advisories","yarn npm audit --ignore 1234567 --ignore 7654321"]]});var TM=Pe(Xr()),OM=J("util"),Yu=class extends De{constructor(){super(...arguments);this.fields=z.String("-f,--fields",{description:"A comma-separated list of manifest fields that should be displayed"});this.json=z.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.packages=z.Rest()}async execute(){let t=await ye.find(this.context.cwd,this.context.plugins),{project:i}=await je.find(t,this.context.cwd),n=typeof this.fields<"u"?new Set(["name",...this.fields.split(/\s*,\s*/)]):null,s=[],o=!1,a=await Ge.start({configuration:t,includeFooter:!1,json:this.json,stdout:this.context.stdout},async l=>{for(let c of this.packages){let u;if(c==="."){let L=i.topLevelWorkspace;if(!L.manifest.name)throw new be(`Missing ${ee.pretty(t,"name",ee.Type.CODE)} field in ${K.fromPortablePath(x.join(L.cwd,xt.manifest))}`);u=P.makeDescriptor(L.manifest.name,"unknown")}else u=P.parseDescriptor(c);let g=Mt.getIdentUrl(u),f=MM(await Mt.get(g,{configuration:t,ident:u,jsonResponse:!0,customErrorMessage:Mt.customPackageError})),h=Object.keys(f.versions).sort(TM.default.compareLoose),C=f["dist-tags"].latest||h[h.length-1],y=vt.validRange(u.range);if(y){let L=TM.default.maxSatisfying(h,y);L!==null?C=L:(l.reportWarning(0,`Unmet range ${P.prettyRange(t,u.range)}; falling back to the latest version`),o=!0)}else Object.prototype.hasOwnProperty.call(f["dist-tags"],u.range)?C=f["dist-tags"][u.range]:u.range!=="unknown"&&(l.reportWarning(0,`Unknown tag ${P.prettyRange(t,u.range)}; falling back to the latest version`),o=!0);let B=f.versions[C],v={...f,...B,version:C,versions:h},D;if(n!==null){D={};for(let L of n){let H=v[L];if(typeof H<"u")D[L]=H;else{l.reportWarning(1,`The ${ee.pretty(t,L,ee.Type.CODE)} field doesn't exist inside ${P.prettyIdent(t,u)}'s information`),o=!0;continue}}}else this.json||(delete v.dist,delete v.readme,delete v.users),D=v;l.reportJson(D),this.json||s.push(D)}});OM.inspect.styles.name="cyan";for(let l of s)(l!==s[0]||o)&&this.context.stdout.write(`
`),this.context.stdout.write(`${(0,OM.inspect)(l,{depth:1/0,colors:!0,compact:!1})}
`);return a.exitCode()}};Yu.paths=[["npm","info"]],Yu.usage=ve.Usage({category:"Npm-related commands",description:"show information about a package",details:"\n This command fetches information about a package from the npm registry and prints it in a tree format.\n\n The package does not have to be installed locally, but needs to have been published (in particular, local changes will be ignored even for workspaces).\n\n Append `@<range>` to the package argument to provide information specific to the latest version that satisfies the range or to the corresponding tagged version. If the range is invalid or if there is no version satisfying the range, the command will print a warning and fall back to the latest version.\n\n If the `-f,--fields` option is set, it's a comma-separated list of fields which will be used to only display part of the package information.\n\n By default, this command won't return the `dist`, `readme`, and `users` fields, since they are often very long. To explicitly request those fields, explicitly list them with the `--fields` flag or request the output in JSON mode.\n ",examples:[["Show all available information about react (except the `dist`, `readme`, and `users` fields)","yarn npm info react"],["Show all available information about react as valid JSON (including the `dist`, `readme`, and `users` fields)","yarn npm info react --json"],["Show all available information about react@16.12.0","yarn npm info react@16.12.0"],["Show all available information about react@next","yarn npm info react@next"],["Show the description of react","yarn npm info react --fields description"],["Show all available versions of react","yarn npm info react --fields versions"],["Show the readme of react","yarn npm info react --fields readme"],["Show a few fields of react","yarn npm info react --fields homepage,repository"]]});function MM(r){if(Array.isArray(r)){let e=[];for(let t of r)t=MM(t),t&&e.push(t);return e}else if(typeof r=="object"&&r!==null){let e={};for(let t of Object.keys(r)){if(t.startsWith("_"))continue;let i=MM(r[t]);i&&(e[t]=i)}return e}else return r||null}var sue=Pe(Pm()),ju=class extends De{constructor(){super(...arguments);this.scope=z.String("-s,--scope",{description:"Login to the registry configured for a given scope"});this.publish=z.Boolean("--publish",!1,{description:"Login to the publish registry"})}async execute(){let t=await ye.find(this.context.cwd,this.context.plugins),i=await Qb({configuration:t,cwd:this.context.cwd,publish:this.publish,scope:this.scope});return(await Ge.start({configuration:t,stdout:this.context.stdout,includeFooter:!1},async s=>{let o=await zVe({configuration:t,registry:i,report:s,stdin:this.context.stdin,stdout:this.context.stdout}),a=`/-/user/org.couchdb.user:${encodeURIComponent(o.name)}`,l=await Mt.put(a,o,{attemptedAs:o.name,configuration:t,registry:i,jsonResponse:!0,authType:Mt.AuthType.NO_AUTH});return await WVe(i,l.token,{configuration:t,scope:this.scope}),s.reportInfo(0,"Successfully logged in")})).exitCode()}};ju.paths=[["npm","login"]],ju.usage=ve.Usage({category:"Npm-related commands",description:"store new login info to access the npm registry",details:"\n This command will ask you for your username, password, and 2FA One-Time-Password (when it applies). It will then modify your local configuration (in your home folder, never in the project itself) to reference the new tokens thus generated.\n\n Adding the `-s,--scope` flag will cause the authentication to be done against whatever registry is configured for the associated scope (see also `npmScopes`).\n\n Adding the `--publish` flag will cause the authentication to be done against the registry used when publishing the package (see also `publishConfig.registry` and `npmPublishRegistry`).\n ",examples:[["Login to the default registry","yarn npm login"],["Login to the registry linked to the @my-scope registry","yarn npm login --scope my-scope"],["Login to the publish registry for the current package","yarn npm login --publish"]]});async function Qb({scope:r,publish:e,configuration:t,cwd:i}){return r&&e?or.getScopeRegistry(r,{configuration:t,type:or.RegistryType.PUBLISH_REGISTRY}):r?or.getScopeRegistry(r,{configuration:t}):e?or.getPublishRegistry((await Ph(t,i)).manifest,{configuration:t}):or.getDefaultRegistry({configuration:t})}async function WVe(r,e,{configuration:t,scope:i}){let n=o=>a=>{let l=Ie.isIndexableObject(a)?a:{},c=l[o],u=Ie.isIndexableObject(c)?c:{};return{...l,[o]:{...u,npmAuthToken:e}}},s=i?{npmScopes:n(i)}:{npmRegistries:n(r)};return await ye.updateHomeConfiguration(s)}async function zVe({configuration:r,registry:e,report:t,stdin:i,stdout:n}){t.reportInfo(0,`Logging in to ${ee.pretty(r,e,ee.Type.URL)}`);let s=!1;if(e.match(/^https:\/\/npm\.pkg\.github\.com(\/|$)/)&&(t.reportInfo(0,"You seem to be using the GitHub Package Registry. Tokens must be generated with the 'repo', 'write:packages', and 'read:packages' permissions."),s=!0),t.reporLine truncated
`);return a.exitCode()}};Yu.paths=[["npm","info"]],Yu.usage=ve.Usage({category:"Npm-related commands",description:"show information about a package",details:"\n This command fetches information about a package from the npm registry and prints it in a tree format.\n\n The package does not have to be installed locally, but needs to have been published (in particular, local changes will be ignored even for workspaces).\n\n Append `@<range>` to the package argument to provide information specific to the latest version that satisfies the range or to the corresponding tagged version. If the range is invalid or if there is no version satisfying the range, the command will print a warning and fall back to the latest version.\n\n If the `-f,--fields` option is set, it's a comma-separated list of fields which will be used to only display part of the package information.\n\n By default, this command won't return the `dist`, `readme`, and `users` fields, since they are often very long. To explicitly request those fields, explicitly list them with the `--fields` flag or request the output in JSON mode.\n ",examples:[["Show all available information about react (except the `dist`, `readme`, and `users` fields)","yarn npm info react"],["Show all available information about react as valid JSON (including the `dist`, `readme`, and `users` fields)","yarn npm info react --json"],["Show all available information about react@16.12.0","yarn npm info react@16.12.0"],["Show all available information about react@next","yarn npm info react@next"],["Show the description of react","yarn npm info react --fields description"],["Show all available versions of react","yarn npm info react --fields versions"],["Show the readme of react","yarn npm info react --fields readme"],["Show a few fields of react","yarn npm info react --fields homepage,repository"]]});function MM(r){if(Array.isArray(r)){let e=[];for(let t of r)t=MM(t),t&&e.push(t);return e}else if(typeof r=="object"&&r!==null){let e={};for(let t of Object.keys(r)){if(t.startsWith("_"))continue;let i=MM(r[t]);i&&(e[t]=i)}return e}else return r||null}var sue=Pe(Pm()),ju=class extends De{constructor(){super(...arguments);this.scope=z.String("-s,--filter",{description:"Login to the registry configured for a given scope"});this.publish=z.Boolean("--publish",!1,{description:"Login to the publish registry"})}async execute(){let t=await ye.find(this.context.cwd,this.context.plugins),i=await Qb({configuration:t,cwd:this.context.cwd,publish:this.publish,scope:this.scope});return(await Ge.start({configuration:t,stdout:this.context.stdout,includeFooter:!1},async s=>{let o=await zVe({configuration:t,registry:i,report:s,stdin:this.context.stdin,stdout:this.context.stdout}),a=`/-/user/org.couchdb.user:${encodeURIComponent(o.name)}`,l=await Mt.put(a,o,{attemptedAs:o.name,configuration:t,registry:i,jsonResponse:!0,authType:Mt.AuthType.NO_AUTH});return await WVe(i,l.token,{configuration:t,scope:this.scope}),s.reportInfo(0,"Successfully logged in")})).exitCode()}};ju.paths=[["npm","login"]],ju.usage=ve.Usage({category:"Npm-related commands",description:"store new login info to access the npm registry",details:"\n This command will ask you for your username, password, and 2FA One-Time-Password (when it applies). It will then modify your local configuration (in your home folder, never in the project itself) to reference the new tokens thus generated.\n\n Adding the `-s,--filter` flag will cause the authentication to be done against whatever registry is configured for the associated scope (see also `npmScopes`).\n\n Adding the `--publish` flag will cause the authentication to be done against the registry used when publishing the package (see also `publishConfig.registry` and `npmPublishRegistry`).\n ",examples:[["Login to the default registry","yarn npm login"],["Login to the registry linked to the @my-scope registry","yarn npm login --filter my-scope"],["Login to the publish registry for the current package","yarn npm login --publish"]]});async function Qb({scope:r,publish:e,configuration:t,cwd:i}){return r&&e?or.getScopeRegistry(r,{configuration:t,type:or.RegistryType.PUBLISH_REGISTRY}):r?or.getScopeRegistry(r,{configuration:t}):e?or.getPublishRegistry((await Ph(t,i)).manifest,{configuration:t}):or.getDefaultRegistry({configuration:t})}async function WVe(r,e,{configuration:t,scope:i}){let n=o=>a=>{let l=Ie.isIndexableObject(a)?a:{},c=l[o],u=Ie.isIndexableObject(c)?c:{};return{...l,[o]:{...u,npmAuthToken:e}}},s=i?{npmScopes:n(i)}:{npmRegistries:n(r)};return await ye.updateHomeConfiguration(s)}async function zVe({configuration:r,registry:e,report:t,stdin:i,stdout:n}){t.reportInfo(0,`Logging in to ${ee.pretty(r,e,ee.Type.URL)}`);let s=!1;if(e.match(/^https:\/\/npm\.pkg\.github\.com(\/|$)/)&&(t.reportInfo(0,"You seem to be using the GitHub Package Registry. Tokens must be generated with the 'repo', 'write:packages', and 'read:packages' permissions."),s=!0),t.reLine truncated
This command will list all tags of a package from the npm registry.
If the package is not specified, Yarn will default to the current workspace.
@@ -732,7 +732,7 @@ ${we.map(pe=>`Not found: ${lo(pe)}
This command will add a tag to the npm registry for a specific version of a package. If the tag already exists, it will be overwritten.
`,examples:[["Add a `beta` tag for version `2.3.4-beta.4` of package `my-pkg`","yarn npm tag add my-pkg@2.3.4-beta.4 beta"]]});var Vu=class extends De{constructor(){super(...arguments);this.package=z.String();this.tag=z.String()}async execute(){if(this.tag==="latest")throw new be("The 'latest' tag cannot be removed.");let t=await ye.find(this.context.cwd,this.context.plugins),{project:i,workspace:n}=await je.find(t,this.context.cwd);if(!n)throw new ct(i.cwd,this.context.cwd);let s=P.parseIdent(this.package),o=or.getPublishRegistry(n.manifest,{configuration:t}),a=ee.pretty(t,this.tag,ee.Type.CODE),l=ee.pretty(t,s,ee.Type.IDENT),c=await IE(s,t);if(!Object.prototype.hasOwnProperty.call(c,this.tag))throw new be(`${a} is not a tag of package ${l}`);return(await Ge.start({configuration:t,stdout:this.context.stdout},async g=>{let f=`/-/package${Mt.getIdentUrl(s)}/dist-tags/${encodeURIComponent(this.tag)}`;await Mt.del(f,{configuration:t,registry:o,ident:s,jsonResponse:!0}),g.reportInfo(0,`Tag ${a} removed from package ${l}`)})).exitCode()}};Vu.paths=[["npm","tag","remove"]],Vu.usage=ve.Usage({category:"Npm-related commands",description:"remove a tag from a package",details:`
This command will remove a tag from a package from the npm registry.
`,examples:[["Remove the `beta` tag from package `my-pkg`","yarn npm tag remove my-pkg beta"]]});var Xu=class extends De{constructor(){super(...arguments);this.scope=z.String("-s,--scope",{description:"Print username for the registry configured for a given scope"});this.publish=z.Boolean("--publish",!1,{description:"Print username for the publish registry"})}async execute(){let t=await ye.find(this.context.cwd,this.context.plugins),i;return this.scope&&this.publish?i=or.getScopeRegistry(this.scope,{configuration:t,type:or.RegistryType.PUBLISH_REGISTRY}):this.scope?i=or.getScopeRegistry(this.scope,{configuration:t}):this.publish?i=or.getPublishRegistry((await Ph(t,this.context.cwd)).manifest,{configuration:t}):i=or.getDefaultRegistry({configuration:t}),(await Ge.start({configuration:t,stdout:this.context.stdout},async s=>{var a,l;let o;try{o=await Mt.get("/-/whoami",{configuration:t,registry:i,authType:Mt.AuthType.ALWAYS_AUTH,jsonResponse:!0,ident:this.scope?P.makeIdent(this.scope,""):void 0})}catch(c){if(((a=c.response)==null?void 0:a.statusCode)===401||((l=c.response)==null?void 0:l.statusCode)===403){s.reportError(41,"Authentication failed - your credentials may have expired");return}else throw c}s.reportInfo(0,o.username)})).exitCode()}};Xu.paths=[["npm","whoami"]],Xu.usage=ve.Usage({category:"Npm-related commands",description:"display the name of the authenticated user",details:"\n Print the username associated with the current authentication settings to the standard output.\n\n When using `-s,--scope`, the username printed will be the one that matches the authentication settings of the registry associated with the given scope (those settings can be overriden using the `npmRegistries` map, and the registry associated with the scope is configured via the `npmScopes` map).\n\n When using `--publish`, the registry we'll select will by default be the one used when publishing packages (`publishConfig.registry` or `npmPublishRegistry` if available, otherwise we'll fallback to the regular `npmRegistryServer`).\n ",examples:[["Print username for the default registry","yarn npm whoami"],["Print username for the registry on a given scope","yarn npm whoami --scope company"]]});var _Ve={configuration:{npmPublishAccess:{description:"Default access of the published packages",type:"STRING",default:null},npmAuditExcludePackages:{description:"Array of glob patterns of packages to exclude from npm audit",type:"STRING",default:[],isArray:!0},npmAuditIgnoreAdvisories:{description:"Array of glob patterns of advisory IDs to exclude from npm audit",type:"STRING",default:[],isArray:!0}},commands:[Gu,Yu,ju,qu,Ju,zu,Wu,Vu,Xu]},ZVe=_Ve;var zM={};ut(zM,{default:()=>h6e,patchUtils:()=>_u});var _u={};ut(_u,{applyPatchFile:()=>Sb,diffFolders:()=>JM,ensureUnpatchedDescriptor:()=>HM,extractPackageToDisk:()=>qM,extractPatchFlags:()=>hue,isParentRequired:()=>jM,loadPatchFiles:()=>QE,makeDescriptor:()=>GM,makeLocator:()=>YM,makePatchHash:()=>WM,parseDescriptor:()=>wE,parseLocator:()=>BE,parsePatchFile:()=>yE});var $Ve=/^@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@.*/;function Ap(r){return x.relative(Oe.root,x.resolve(Oe.root,K.toPortablePath(r)))}function e6e(r){let e=r.trim().match($Ve);if(!e)throw new Error(`Bad header line: '${r}'`);return{original:{start:Math.max(Number(e[1]),1),length:Number(e[3]||1)},patched:{start:Math.max(Number(e[4]),1),length:Number(e[6]||1)}}}var t6e=420,r6e=493;var Aue=()=>({semverExclusivity:null,diffLineFromPath:null,diffLineToPath:null,oldMode:null,newMode:null,deletedFileMode:null,newFileMode:null,renameFrom:null,renameTo:null,beforeHash:null,afterHash:null,fromPath:null,toPath:null,hunks:null}),i6e=r=>({header:e6e(r),parts:[]}),n6e={["@"]:"header",["-"]:"deletion",["+"]:"insertion",[" "]:"context",["\\"]:"pragma",undefined:"context"};function s6e(r){let e=[],t=Aue(),i="parsing header",n=null,s=null;function o(){n&&(s&&(n.parts.push(s),s=null),t.hunks.push(n),n=null)}function a(){o(),e.push(t),t=Aue()}for(let l=0;l<r.length;l++){let c=r[l];if(i==="parsing header")if(c.startsWith("@@"))i="parsing hunks",t.hunks=[],l-=1;else if(c.startsWith("diff --git ")){t&&t.diffLineFromPath&&a();let u=c.match(/^diff --git a\/(.*?) b\/(.*?)\s*$/);if(!u)throw new Error(`Bad diff line: ${c}`);t.diffLineFromPath=u[1],t.diffLineToPath=u[2]}else if(c.startsWith("old mode "))t.oldMode=c.slice(9).trim();else if(c.startsWith("new mode "))t.newMode=c.slice(9).trim();else if(c.startsWith("deleted file mode "))t.deletedFileMode=c.slice(18).trim();else if(c.startsWith("new file mode "))t.newFileMode=c.slice(14).trim();else if(c.startsWith("rename from "))t.renameFrom=c.slice(12).trim();else if(c.startsWith("rename to "))t.renameTo=c.slice(10).trim();else if(c.startsWith("index ")){let u=c.match(/(\w+)\.\.(\w+)/);if(!u)continue;t.beforeHash=u[1],t.afterHash=u[2]}else c.startsWith("semver exclusivity ")?t.semverExclusivity=c.slice(19).trim():c.startsWith("--- ")?t.fromPath=c.slice(6).trim():c.startsWith("+++ ")&&(t.toP Line truncated
`,examples:[["Remove the `beta` tag from package `my-pkg`","yarn npm tag remove my-pkg beta"]]});var Xu=class extends De{constructor(){super(...arguments);this.scope=z.String("-s,--filter",{description:"Print username for the registry configured for a given scope"});this.publish=z.Boolean("--publish",!1,{description:"Print username for the publish registry"})}async execute(){let t=await ye.find(this.context.cwd,this.context.plugins),i;return this.scope&&this.publish?i=or.getScopeRegistry(this.scope,{configuration:t,type:or.RegistryType.PUBLISH_REGISTRY}):this.scope?i=or.getScopeRegistry(this.scope,{configuration:t}):this.publish?i=or.getPublishRegistry((await Ph(t,this.context.cwd)).manifest,{configuration:t}):i=or.getDefaultRegistry({configuration:t}),(await Ge.start({configuration:t,stdout:this.context.stdout},async s=>{var a,l;let o;try{o=await Mt.get("/-/whoami",{configuration:t,registry:i,authType:Mt.AuthType.ALWAYS_AUTH,jsonResponse:!0,ident:this.scope?P.makeIdent(this.scope,""):void 0})}catch(c){if(((a=c.response)==null?void 0:a.statusCode)===401||((l=c.response)==null?void 0:l.statusCode)===403){s.reportError(41,"Authentication failed - your credentials may have expired");return}else throw c}s.reportInfo(0,o.username)})).exitCode()}};Xu.paths=[["npm","whoami"]],Xu.usage=ve.Usage({category:"Npm-related commands",description:"display the name of the authenticated user",details:"\n Print the username associated with the current authentication settings to the standard output.\n\n When using `-s,--filter`, the username printed will be the one that matches the authentication settings of the registry associated with the given scope (those settings can be overriden using the `npmRegistries` map, and the registry associated with the scope is configured via the `npmScopes` map).\n\n When using `--publish`, the registry we'll select will by default be the one used when publishing packages (`publishConfig.registry` or `npmPublishRegistry` if available, otherwise we'll fallback to the regular `npmRegistryServer`).\n ",examples:[["Print username for the default registry","yarn npm whoami"],["Print username for the registry on a given scope","yarn npm whoami --filter company"]]});var _Ve={configuration:{npmPublishAccess:{description:"Default access of the published packages",type:"STRING",default:null},npmAuditExcludePackages:{description:"Array of glob patterns of packages to exclude from npm audit",type:"STRING",default:[],isArray:!0},npmAuditIgnoreAdvisories:{description:"Array of glob patterns of advisory IDs to exclude from npm audit",type:"STRING",default:[],isArray:!0}},commands:[Gu,Yu,ju,qu,Ju,zu,Wu,Vu,Xu]},ZVe=_Ve;var zM={};ut(zM,{default:()=>h6e,patchUtils:()=>_u});var _u={};ut(_u,{applyPatchFile:()=>Sb,diffFolders:()=>JM,ensureUnpatchedDescriptor:()=>HM,extractPackageToDisk:()=>qM,extractPatchFlags:()=>hue,isParentRequired:()=>jM,loadPatchFiles:()=>QE,makeDescriptor:()=>GM,makeLocator:()=>YM,makePatchHash:()=>WM,parseDescriptor:()=>wE,parseLocator:()=>BE,parsePatchFile:()=>yE});var $Ve=/^@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@.*/;function Ap(r){return x.relative(Oe.root,x.resolve(Oe.root,K.toPortablePath(r)))}function e6e(r){let e=r.trim().match($Ve);if(!e)throw new Error(`Bad header line: '${r}'`);return{original:{start:Math.max(Number(e[1]),1),length:Number(e[3]||1)},patched:{start:Math.max(Number(e[4]),1),length:Number(e[6]||1)}}}var t6e=420,r6e=493;var Aue=()=>({semverExclusivity:null,diffLineFromPath:null,diffLineToPath:null,oldMode:null,newMode:null,deletedFileMode:null,newFileMode:null,renameFrom:null,renameTo:null,beforeHash:null,afterHash:null,fromPath:null,toPath:null,hunks:null}),i6e=r=>({header:e6e(r),parts:[]}),n6e={["@"]:"header",["-"]:"deletion",["+"]:"insertion",[" "]:"context",["\\"]:"pragma",undefined:"context"};function s6e(r){let e=[],t=Aue(),i="parsing header",n=null,s=null;function o(){n&&(s&&(n.parts.push(s),s=null),t.hunks.push(n),n=null)}function a(){o(),e.push(t),t=Aue()}for(let l=0;l<r.length;l++){let c=r[l];if(i==="parsing header")if(c.startsWith("@@"))i="parsing hunks",t.hunks=[],l-=1;else if(c.startsWith("diff --git ")){t&&t.diffLineFromPath&&a();let u=c.match(/^diff --git a\/(.*?) b\/(.*?)\s*$/);if(!u)throw new Error(`Bad diff line: ${c}`);t.diffLineFromPath=u[1],t.diffLineToPath=u[2]}else if(c.startsWith("old mode "))t.oldMode=c.slice(9).trim();else if(c.startsWith("new mode "))t.newMode=c.slice(9).trim();else if(c.startsWith("deleted file mode "))t.deletedFileMode=c.slice(18).trim();else if(c.startsWith("new file mode "))t.newFileMode=c.slice(14).trim();else if(c.startsWith("rename from "))t.renameFrom=c.slice(12).trim();else if(c.startsWith("rename to "))t.renameTo=c.slice(10).trim();else if(c.startsWith("index ")){let u=c.match(/(\w+)\.\.(\w+)/);if(!u)continue;t.beforeHash=u[1],t.afterHash=u[2]}else c.startsWith("semver exclusivity ")?t.semverExclusivity=c.slice(19).trim():c.startsWith("--- ")?t.fromPath=c.slice(6).trim():c.startsWith("+++ ")&&(t. Line truncated
`)+(n.hunk.parts[0].noNewlineAtEndOfFile?"":`
`):"";await e.mkdirpPromise(x.dirname(n.path),{chmod:493,utimes:[xr.SAFE_TIME,xr.SAFE_TIME]}),await e.writeFilePromise(n.path,s,{mode:n.mode}),await e.utimesPromise(n.path,xr.SAFE_TIME,xr.SAFE_TIME)}break;case"patch":await cp(e,n.path,async()=>{await c6e(n,{baseFs:e,dryRun:t})});break;case"mode change":{let o=(await e.statPromise(n.path)).mode;if(lue(n.newMode)!==lue(o))continue;await cp(e,n.path,async()=>{await e.chmodPromise(n.path,n.newMode)})}break;default:Ie.assertNever(n);break}}function lue(r){return(r&64)>0}function cue(r){return r.replace(/\s+$/,"")}function l6e(r,e){return cue(r)===cue(e)}async function c6e({hunks:r,path:e},{baseFs:t,dryRun:i=!1}){let n=await t.statSync(e).mode,o=(await t.readFileSync(e,"utf8")).split(/\n/),a=[],l=0,c=0;for(let g of r){let f=Math.max(c,g.header.patched.start+l),h=Math.max(0,f-c),p=Math.max(0,o.length-f-g.header.original.length),C=Math.max(h,p),y=0,B=0,v=null;for(;y<=C;){if(y<=h&&(B=f-y,v=uue(g,o,B),v!==null)){y=-y;break}if(y<=p&&(B=f+y,v=uue(g,o,B),v!==null))break;y+=1}if(v===null)throw new lp(r.indexOf(g),g);a.push(v),l+=y,c=B+g.header.original.length}if(i)return;let u=0;for(let g of a)for(let f of g)switch(f.type){case"splice":{let h=f.index+u;o.splice(h,f.numToDelete,...f.linesToInsert),u+=f.linesToInsert.length-f.numToDelete}break;case"pop":o.pop();break;case"push":o.push(f.line);break;default:Ie.assertNever(f);break}await t.writeFilePromise(e,o.join(`
`),{mode:n})}function uue(r,e,t){let i=[];for(let n of r.parts)switch(n.type){case"context":case"deletion":{for(let s of n.lines){let o=e[t];if(o==null||!l6e(o,s))return null;t+=1}n.type==="deletion"&&(i.push({type:"splice",index:t-n.lines.length,numToDelete:n.lines.length,linesToInsert:[]}),n.noNewlineAtEndOfFile&&i.push({type:"push",line:""}))}break;case"insertion":i.push({type:"splice",index:t,numToDelete:0,linesToInsert:n.lines}),n.noNewlineAtEndOfFile&&i.push({type:"pop"});break;default:Ie.assertNever(n.type);break}return i}var g6e=/^builtin<([^>]+)>$/;function UM(r,e){let{source:t,selector:i,params:n}=P.parseRange(r);if(t===null)throw new Error("Patch locators must explicitly define their source");let s=i?i.split(/&/).map(c=>K.toPortablePath(c)):[],o=n&&typeof n.locator=="string"?P.parseLocator(n.locator):null,a=n&&typeof n.version=="string"?n.version:null,l=e(t);return{parentLocator:o,sourceItem:l,patchPaths:s,sourceVersion:a}}function wE(r){let{sourceItem:e,...t}=UM(r.range,P.parseDescriptor);return{...t,sourceDescriptor:e}}function BE(r){let{sourceItem:e,...t}=UM(r.reference,P.parseLocator);return{...t,sourceLocator:e}}function HM(r){if(!r.range.startsWith("patch:"))return r;let{sourceItem:e}=UM(r.range,P.parseDescriptor);return e}function gue({parentLocator:r,sourceItem:e,patchPaths:t,sourceVersion:i,patchHash:n},s){let o=r!==null?{locator:P.stringifyLocator(r)}:{},a=typeof i<"u"?{version:i}:{},l=typeof n<"u"?{hash:n}:{};return P.makeRange({protocol:"patch:",source:s(e),selector:t.join("&"),params:{...a,...l,...o}})}function GM(r,{parentLocator:e,sourceDescriptor:t,patchPaths:i}){return P.makeDescriptor(r,gue({parentLocator:e,sourceItem:t,patchPaths:i},P.stringifyDescriptor))}function YM(r,{parentLocator:e,sourcePackage:t,patchPaths:i,patchHash:n}){return P.makeLocator(r,gue({parentLocator:e,sourceItem:t,sourceVersion:t.version,patchPaths:i,patchHash:n},P.stringifyLocator))}function fue({onAbsolute:r,onRelative:e,onBuiltin:t},i){i.startsWith("~")&&(i=i.slice(1));let s=i.match(g6e);return s!==null?t(s[1]):x.isAbsolute(i)?r(i):e(i)}function hue(r){let e=r.startsWith("~");return e&&(r=r.slice(1)),{optional:e}}function jM(r){return fue({onAbsolute:()=>!1,onRelative:()=>!0,onBuiltin:()=>!1},r)}async function QE(r,e,t){let i=r!==null?await t.fetcher.fetch(r,t):null,n=i&&i.localPath?{packageFs:new jt(Oe.root),prefixPath:x.relative(Oe.root,i.localPath)}:i;i&&i!==n&&i.releaseFs&&i.releaseFs();let s=await Ie.releaseAfterUseAsync(async()=>await Promise.all(e.map(async o=>{let a=hue(o),l=await fue({onAbsolute:async()=>await M.readFilePromise(o,"utf8"),onRelative:async()=>{if(n===null)throw new Error("Assertion failed: The parent locator should have been fetched");return await n.packageFs.readFilePromise(x.join(n.prefixPath,o),"utf8")},onBuiltin:async c=>await t.project.configuration.firstHook(u=>u.getBuiltinPatch,t.project,c)},o);return{...a,source:l}})));for(let o of s)typeof o.source=="string"&&(o.source=o.source.replace(/\r\n?/g,`
+2
View File
@@ -0,0 +1,2 @@
undecided:
- "@calcom/prisma"
+2
View File
@@ -0,0 +1,2 @@
undecided:
- "@calcom/prisma"
+1 -1
View File
@@ -199,7 +199,7 @@ There's some settings that you'll need to setup.
Under Vercel > Your API Project > Settings
In General > Build & Development Settings
BUILD COMMAND: `yarn turbo run build --scope=@calcom/api --include-dependencies --no-deps`
BUILD COMMAND: `yarn turbo run build --filter=@calcom/api --no-deps`
OUTPUT DIRECTORY: `apps/api/.next`
In Git > Ignored Build Step
+1 -1
View File
@@ -38,7 +38,7 @@
"@axiomhq/winston": "^1.2.0",
"@calcom/platform-constants": "*",
"@calcom/platform-enums": "*",
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.312",
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.314",
"@calcom/platform-types": "*",
"@calcom/platform-utils": "*",
"@calcom/prisma": "*",
@@ -2,6 +2,8 @@ import { CreateOrgMembershipDto } from "@/modules/organizations/memberships/inpu
import { OrganizationsMembershipRepository } from "@/modules/organizations/memberships/organizations-membership.repository";
import { Injectable, NotFoundException } from "@nestjs/common";
import { TeamService } from "@calcom/platform-libraries";
import { UpdateOrgMembershipDto } from "../inputs/update-organization-membership.input";
import { OrganizationsMembershipOutputService } from "./organizations-membership-output.service";
@@ -62,10 +64,24 @@ export class OrganizationsMembershipService {
}
async deleteOrgMembership(organizationId: number, membershipId: number) {
const membership = await this.organizationsMembershipRepository.deleteOrgMembership(
// Get the membership first to get the userId
const membership = await this.organizationsMembershipRepository.findOrgMembership(
organizationId,
membershipId
);
if (!membership) {
throw new NotFoundException(
`Membership with id ${membershipId} within organization id ${organizationId} not found`
);
}
await TeamService.removeMembers({
teamIds: [organizationId],
userIds: [membership.userId],
isOrg: true,
});
return this.organizationsMembershipOutputService.getOrgMembershipOutput(membership);
}
@@ -20,7 +20,6 @@ import {
OrgTeamMembershipOutputResponseDto,
} from "@/modules/organizations/teams/memberships/outputs/organization-teams-memberships.output";
import { OrganizationsTeamsMembershipsService } from "@/modules/organizations/teams/memberships/services/organizations-teams-memberships.service";
import { TeamsEventTypesService } from "@/modules/teams/event-types/services/teams-event-types.service";
import { TeamMembershipOutput } from "@/modules/teams/memberships/outputs/team-membership.output";
import {
Controller,
@@ -59,7 +58,6 @@ export class OrganizationsTeamsMembershipsController {
constructor(
private organizationsTeamsMembershipsService: OrganizationsTeamsMembershipsService,
private teamsEventTypesService: TeamsEventTypesService,
private readonly organizationsRepository: OrganizationsRepository
) {}
@@ -127,8 +125,6 @@ export class OrganizationsTeamsMembershipsController {
membershipId
);
await this.teamsEventTypesService.deleteUserTeamEventTypesAndHosts(membership.userId, teamId);
return {
status: SUCCESS_STATUS,
data: plainToClass(TeamMembershipOutput, membership, { strategy: "excludeAll" }),
@@ -3,6 +3,8 @@ import { UpdateOrgTeamMembershipDto } from "@/modules/organizations/teams/member
import { OrganizationsTeamsMembershipsRepository } from "@/modules/organizations/teams/memberships/organizations-teams-memberships.repository";
import { Injectable, NotFoundException } from "@nestjs/common";
import { TeamService } from "@calcom/platform-libraries";
@Injectable()
export class OrganizationsTeamsMembershipsService {
constructor(
@@ -58,11 +60,21 @@ export class OrganizationsTeamsMembershipsService {
}
async deleteOrgTeamMembership(organizationId: number, teamId: number, membershipId: number) {
const teamMembership = await this.organizationsTeamsMembershipsRepository.deleteOrgTeamMembershipById(
// First get the membership to get the userId
const teamMembership = await this.organizationsTeamsMembershipsRepository.findOrgTeamMembership(
organizationId,
teamId,
membershipId
);
if (!teamMembership) {
throw new NotFoundException(
`Membership with id ${membershipId} not found in team ${teamId} of organization ${organizationId}`
);
}
await TeamService.removeMembers({ teamIds: [teamId], userIds: [teamMembership.userId], isOrg: false });
return teamMembership;
}
}
@@ -12,7 +12,12 @@ import {
EMAIL,
WorkflowEmailAttendeeStepDto,
} from "@/modules/workflows/inputs/workflow-step.input";
import { BEFORE_EVENT, DAY } from "@/modules/workflows/inputs/workflow-trigger.input";
import {
BEFORE_EVENT,
DAY,
OnAfterEventTriggerDto,
OnBeforeEventTriggerDto,
} from "@/modules/workflows/inputs/workflow-trigger.input";
// Adjust path if needed
import { GetWorkflowOutput, GetWorkflowsOutput } from "@/modules/workflows/outputs/workflow.output";
import { INestApplication } from "@nestjs/common";
@@ -255,6 +260,10 @@ describe("OrganizationsTeamsWorkflowsController (E2E)", () => {
"+37255555555"
);
expect(responseBody.data.steps.find((step) => step.stepNumber === 3)?.email).toEqual(authEmail);
const trigger = sampleCreateWorkflowDto.trigger as OnBeforeEventTriggerDto;
expect(responseBody.data.trigger?.offset?.value).toEqual(trigger.offset.value);
expect(responseBody.data.trigger?.offset?.unit).toEqual(trigger.offset.unit);
createdWorkflowId = responseBody.data.id;
createdWorkflow = responseBody.data;
});
@@ -330,6 +339,13 @@ describe("OrganizationsTeamsWorkflowsController (E2E)", () => {
expect(step1).toBeDefined();
const partialUpdateDto: Partial<CreateWorkflowDto> = {
name: updatedName,
trigger: {
type: "afterEvent",
offset: {
unit: "minute",
value: 10,
},
},
steps: step1 ? [{ ...step1, sender: "updatedSender" } as WorkflowEmailAttendeeStepDto] : [],
};
expect(createdWorkflowId).toBeDefined();
@@ -348,6 +364,10 @@ describe("OrganizationsTeamsWorkflowsController (E2E)", () => {
step1 && expect(responseBody.data.steps[0].id).toEqual(step1.id);
expect(responseBody.data.steps[0].sender).toEqual("updatedSender");
expect(responseBody.data.steps[1]?.id).toBeUndefined();
const trigger = partialUpdateDto.trigger as OnAfterEventTriggerDto;
expect(responseBody.data.trigger?.type).toEqual(trigger.type);
expect(responseBody.data.trigger?.offset?.value).toEqual(trigger.offset.value);
expect(responseBody.data.trigger?.offset?.unit).toEqual(trigger.offset.unit);
});
});
@@ -156,17 +156,4 @@ export class TeamsEventTypesService {
return this.eventTypesRepository.deleteEventType(eventTypeId);
}
async deleteUserTeamEventTypesAndHosts(userId: number, teamId: number) {
try {
await this.teamsEventTypesRepository.deleteUserManagedTeamEventTypes(userId, teamId);
await this.teamsEventTypesRepository.removeUserFromTeamEventTypesHosts(userId, teamId);
} catch (err) {
this.logger.error("Could not remove user from all team event-types.", {
error: err,
userId,
teamId,
});
}
}
}
@@ -3,7 +3,6 @@ import { API_KEY_HEADER } from "@/lib/docs/headers";
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
import { RolesGuard } from "@/modules/auth/guards/roles/roles.guard";
import { TeamsEventTypesService } from "@/modules/teams/event-types/services/teams-event-types.service";
import { CreateTeamMembershipInput } from "@/modules/teams/memberships/inputs/create-team-membership.input";
import { UpdateTeamMembershipInput } from "@/modules/teams/memberships/inputs/update-team-membership.input";
import { CreateTeamMembershipOutput } from "@/modules/teams/memberships/outputs/create-team-membership.output";
@@ -45,10 +44,7 @@ import { SkipTakePagination } from "@calcom/platform-types";
export class TeamsMembershipsController {
private logger = new Logger("TeamsMembershipsController");
constructor(
private teamsMembershipsService: TeamsMembershipsService,
private teamsEventTypesService: TeamsEventTypesService
) {}
constructor(private teamsMembershipsService: TeamsMembershipsService) {}
@Roles("TEAM_ADMIN")
@Post("/")
@@ -127,7 +123,6 @@ export class TeamsMembershipsController {
membershipId,
body
);
if (!currentMembership.accepted && updatedMembership.accepted) {
try {
await updateNewTeamMemberEventTypes(updatedMembership.userId, teamId);
@@ -151,8 +146,6 @@ export class TeamsMembershipsController {
): Promise<DeleteTeamMembershipOutput> {
const membership = await this.teamsMembershipsService.deleteTeamMembership(teamId, membershipId);
await this.teamsEventTypesService.deleteUserTeamEventTypesAndHosts(membership.userId, teamId);
return {
status: SUCCESS_STATUS,
data: plainToClass(TeamMembershipOutput, membership, { strategy: "excludeAll" }),
@@ -3,6 +3,8 @@ import { UpdateTeamMembershipInput } from "@/modules/teams/memberships/inputs/up
import { TeamsMembershipsRepository } from "@/modules/teams/memberships/teams-memberships.repository";
import { Injectable, NotFoundException } from "@nestjs/common";
import { TeamService } from "@calcom/platform-libraries";
@Injectable()
export class TeamsMembershipsService {
constructor(private readonly teamsMembershipsRepository: TeamsMembershipsRepository) {}
@@ -41,10 +43,15 @@ export class TeamsMembershipsService {
}
async deleteTeamMembership(teamId: number, membershipId: number) {
const teamMembership = await this.teamsMembershipsRepository.deleteTeamMembershipById(
teamId,
membershipId
);
// First get the membership to get the userId
const teamMembership = await this.teamsMembershipsRepository.findTeamMembership(teamId, membershipId);
if (!teamMembership) {
throw new NotFoundException(`Membership with id ${membershipId} not found in team ${teamId}`);
}
await TeamService.removeMembers({ teamIds: [teamId], userIds: [teamMembership.userId], isOrg: false });
return teamMembership;
}
}
@@ -1,7 +1,7 @@
import { ApiProperty } from "@nestjs/swagger";
import { TimeUnit, WorkflowTriggerEvents } from "@prisma/client";
import { Type } from "class-transformer";
import { IsNumber, ValidateNested } from "class-validator";
import { IsIn, IsNumber, IsString, ValidateNested } from "class-validator";
export const BEFORE_EVENT = "beforeEvent";
export const EVENT_CANCELLED = "eventCancelled";
@@ -68,6 +68,8 @@ export class WorkflowTriggerOffsetDto {
value!: number;
@ApiProperty({ description: "Unit for the offset time", example: HOUR })
@IsString()
@IsIn(TIME_UNITS)
unit!: TimeUnitType;
}
@@ -75,6 +77,8 @@ export class BaseWorkflowTriggerDto {
@ApiProperty({
description: "Trigger type for the workflow",
})
@IsString()
@IsIn([WORKFLOW_TRIGGER_TYPES])
type!: WorkflowTriggerType;
}
@@ -82,6 +86,8 @@ export class OnCreationTriggerDto {
@ApiProperty({
description: "Trigger type for the workflow",
})
@IsString()
@IsIn([NEW_EVENT])
type: typeof NEW_EVENT = NEW_EVENT;
}
@@ -89,12 +95,16 @@ export class OnRescheduleTriggerDto {
@ApiProperty({
description: "Trigger type for the workflow",
})
@IsString()
@IsIn([RESCHEDULE_EVENT])
type: typeof RESCHEDULE_EVENT = RESCHEDULE_EVENT;
}
export class OnCancelTriggerDto {
@ApiProperty({
description: "Trigger type for the workflow",
})
@IsString()
@IsIn([EVENT_CANCELLED])
type: typeof EVENT_CANCELLED = EVENT_CANCELLED;
}
@@ -113,6 +123,8 @@ export class OnBeforeEventTriggerDto extends TriggerOffsetDTO {
description: "Trigger type for the workflow",
example: BEFORE_EVENT,
})
@IsString()
@IsIn([BEFORE_EVENT])
type: typeof BEFORE_EVENT = BEFORE_EVENT;
}
@@ -121,6 +133,8 @@ export class OnAfterEventTriggerDto extends TriggerOffsetDTO {
description: "Trigger type for the workflow",
example: AFTER_EVENT,
})
@IsString()
@IsIn([AFTER_EVENT])
type: typeof AFTER_EVENT = AFTER_EVENT;
}
@@ -129,6 +143,8 @@ export class OnAfterCalVideoGuestsNoShowTriggerDto extends TriggerOffsetDTO {
description: "Trigger type for the workflow",
example: AFTER_GUESTS_CAL_VIDEO_NO_SHOW,
})
@IsString()
@IsIn([AFTER_GUESTS_CAL_VIDEO_NO_SHOW])
type: typeof AFTER_GUESTS_CAL_VIDEO_NO_SHOW = AFTER_GUESTS_CAL_VIDEO_NO_SHOW;
}
@@ -137,5 +153,7 @@ export class OnAfterCalVideoHostsNoShowTriggerDto extends TriggerOffsetDTO {
description: "Trigger type for the workflow",
example: AFTER_HOSTS_CAL_VIDEO_NO_SHOW,
})
@IsString()
@IsIn([AFTER_HOSTS_CAL_VIDEO_NO_SHOW])
type: typeof AFTER_HOSTS_CAL_VIDEO_NO_SHOW = AFTER_HOSTS_CAL_VIDEO_NO_SHOW;
}
@@ -59,4 +59,8 @@ export class EventTypesRepositoryFixture {
async delete(eventTypeId: EventType["id"]) {
return this.prismaWriteClient.eventType.delete({ where: { id: eventTypeId } });
}
async findById(eventTypeId: EventType["id"]) {
return this.prismaReadClient.eventType.findUnique({ where: { id: eventTypeId } });
}
}
@@ -35,4 +35,8 @@ export class MembershipRepositoryFixture {
await this.prismaWriteClient.user.update({ where: { id: user.id }, data: { organizationId: org.id } });
return membership;
}
async findById(membershipId: Membership["id"]) {
return this.prismaReadClient.membership.findUnique({ where: { id: membershipId } });
}
}
@@ -45,11 +45,13 @@ const InstalledAppsWrapper = async ({ params }: PageProps) => {
createRouterCaller(appsRouter),
]);
const connectedCalendars = await calendarsCaller.connectedCalendars();
const installedCalendars = await appsCaller.integrations({
variant: "calendar",
onlyInstalled: true,
});
const [connectedCalendars, installedCalendars] = await Promise.all([
calendarsCaller.connectedCalendars(),
appsCaller.integrations({
variant: "calendar",
onlyInstalled: true,
}),
]);
return (
<InstalledApps
@@ -1,4 +1,3 @@
import { _generateMetadata } from "app/_utils";
import { notFound } from "next/navigation";
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
+2 -1
View File
@@ -5,6 +5,7 @@ import { getLocale } from "@calcom/features/auth/lib/getLocale";
import type { AppImageProps, MeetingImageProps } from "@calcom/lib/OgImages";
import { constructAppImage, constructGenericImage, constructMeetingImage } from "@calcom/lib/OgImages";
import { IS_CALCOM, WEBAPP_URL, APP_NAME, SEO_IMG_OGIMG, CAL_URL } from "@calcom/lib/constants";
import { getCalcomUrl } from "@calcom/lib/getCalcomUrl";
import { buildCanonical } from "@calcom/lib/next-seo.config";
import { getTranslation } from "@calcom/lib/server/i18n";
import { truncateOnWord } from "@calcom/lib/text";
@@ -32,7 +33,7 @@ const _generateMetadataWithoutImage = async (
const description = getDescription(t);
const titleSuffix = `| ${APP_NAME}`;
const displayedTitle = title.includes(titleSuffix) || hideBranding ? title : `${title} ${titleSuffix}`;
const metadataBase = new URL(IS_CALCOM ? "https://cal.com" : WEBAPP_URL);
const metadataBase = new URL(IS_CALCOM ? getCalcomUrl() : WEBAPP_URL);
return {
title: title.length === 0 ? APP_NAME : displayedTitle,
+2 -1
View File
@@ -9,6 +9,7 @@ import Script from "next/script";
import "@calcom/embed-core/src/embed-iframe";
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
import { IS_CALCOM, WEBAPP_URL } from "@calcom/lib/constants";
import { getCalcomUrl } from "@calcom/lib/getCalcomUrl";
import { buildCanonical } from "@calcom/lib/next-seo.config";
import { IconSprites } from "@calcom/ui/components/icon";
@@ -64,7 +65,7 @@ function PageWrapper(props: AppProps) {
// Set canonical to https://cal.com or self-hosted URL
canonical={
IS_CALCOM
? buildCanonical({ path, origin: "https://cal.com" }) // cal.com & .dev
? buildCanonical({ path, origin: getCalcomUrl() }) // cal.com & .dev
: buildCanonical({ path, origin: WEBAPP_URL }) // self-hosted
}
{...seoConfig.defaultNextSeo}
@@ -1,34 +0,0 @@
import type { GetServerSidePropsContext } from "next";
import { getSession } from "next-auth/react";
import { UserPermissionRole } from "@calcom/prisma/enums";
export async function getServerSideProps(ctx: GetServerSidePropsContext) {
const session = await getSession(ctx);
if (!session || !session.user) {
return {
redirect: {
destination: "/login",
permanent: false,
},
};
}
const isAdmin = session.user.role === UserPermissionRole.ADMIN;
if (!isAdmin) {
return {
redirect: {
destination: "/",
permanent: false,
},
};
}
return {
props: {
error: null,
migrated: null,
userId: session.user.id,
username: session.user.username,
},
};
}
@@ -1,29 +0,0 @@
import type { GetServerSidePropsContext } from "next";
import { getSession } from "next-auth/react";
import { UserPermissionRole } from "@calcom/prisma/enums";
export async function getServerSideProps(ctx: GetServerSidePropsContext) {
const session = await getSession(ctx);
if (!session || !session.user) {
return {
redirect: {
destination: "/login",
permanent: false,
},
};
}
const isAdmin = session.user.role === UserPermissionRole.ADMIN;
if (!isAdmin) {
return {
redirect: {
destination: "/",
permanent: false,
},
};
}
return {
props: {},
};
}
@@ -1,29 +0,0 @@
import type { GetServerSidePropsContext } from "next";
import { getSession } from "next-auth/react";
import { UserPermissionRole } from "@calcom/prisma/enums";
export async function getServerSideProps(ctx: GetServerSidePropsContext) {
const session = await getSession(ctx);
if (!session || !session.user) {
return {
redirect: {
destination: "/login",
permanent: false,
},
};
}
const isAdmin = session.user.role === UserPermissionRole.ADMIN;
if (!isAdmin) {
return {
redirect: {
destination: "/",
permanent: false,
},
};
}
return {
props: {},
};
}
@@ -1,29 +0,0 @@
import type { GetServerSidePropsContext } from "next";
import { getSession } from "next-auth/react";
import { UserPermissionRole } from "@calcom/prisma/enums";
export async function getServerSideProps(ctx: GetServerSidePropsContext) {
const session = await getSession(ctx);
if (!session || !session.user) {
return {
redirect: {
destination: "/login",
permanent: false,
},
};
}
const isAdmin = session.user.role === UserPermissionRole.ADMIN;
if (!isAdmin) {
return {
redirect: {
destination: "/",
permanent: false,
},
};
}
return {
props: {},
};
}
@@ -156,7 +156,7 @@ export function AvailabilityList({ availabilities }: AvailabilityListProps) {
))}
</ul>
</div>
<div className="text-default mb-16 mt-4 hidden text-center text-sm md:block">
<div className="text-default mb-16 mt-4 block text-center text-sm">
{t("temporarily_out_of_office")}{" "}
<Link href="settings/my-account/out-of-office" className="underline">
{t("add_a_redirect")}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@calcom/web",
"version": "5.5.18",
"version": "5.6.1",
"private": true,
"scripts": {
"analyze": "ANALYZE=true next build",
@@ -6,6 +6,7 @@ import { test } from "./lib/fixtures";
import {
bookTimeSlot,
confirmReschedule,
confirmBooking,
doOnOrgDomain,
selectFirstAvailableTimeSlotNextMonth,
selectSecondAvailableTimeSlotNextMonth,
@@ -98,6 +99,58 @@ test("dynamic booking info prefilled by query params", async ({ page, users }) =
activeState = await listItemLocator.getAttribute("data-active");
expect(activeState).toEqual("false");
});
test("multiple duration selection updates event length correctly", async ({ page, users }) => {
const user = await users.create();
await user.apiLogin();
await test.step("update event title to include duration placeholder", async () => {
await page.goto("/event-types");
await page.waitForSelector('[data-testid="event-types"]');
await page.click(`text=Multiple duration`);
await page.waitForSelector('[data-testid="event-title"]');
await expect(page.getByTestId("vertical-tab-event_setup_tab_title")).toHaveAttribute(
"aria-current",
"page"
);
await page.getByTestId("vertical-tab-event_advanced_tab_title").click();
await page.fill('[name="eventName"]', "{Event duration} event btwn {Organiser} {Scheduler}");
await page.locator('[data-testid="update-eventtype"]').click();
await page.waitForResponse("/api/trpc/eventTypes/update?batch=1");
});
await page.goto(`/${user.username}/multiple-duration`);
await page.locator('[data-testid="multiple-choice-30mins"]').waitFor({ state: "visible" });
await test.step("verify default 30min duration is selected", async () => {
const duration30 = page.getByTestId("multiple-choice-30mins");
const activeState = await duration30.getAttribute("data-active");
expect(activeState).toEqual("true");
});
await test.step("book with 90min duration and verify title", async () => {
await page.getByTestId("multiple-choice-90mins").click();
const duration90 = page.getByTestId("multiple-choice-90mins");
const activeState = await duration90.getAttribute("data-active");
expect(activeState).toEqual("true");
await selectFirstAvailableTimeSlotNextMonth(page);
await page.fill('[name="name"]', "Test User");
await page.fill('[name="email"]', "test@example.com");
await confirmBooking(page);
await expect(page.locator("[data-testid=success-page]")).toBeVisible();
const bookingTitle = page.locator('[data-testid="booking-title"]');
await expect(bookingTitle).toBeVisible();
const titleText = await bookingTitle.textContent();
expect(titleText).toContain("90");
});
});
// eslint-disable-next-line playwright/no-skipped-test
test.skip("it contains the right event details", async ({ page }) => {
const response = await page.goto(`http://acme.cal.local:3000/owner1+member1`);
+1 -1
View File
@@ -76,7 +76,7 @@ Sometimes, yarn install might fail during deployment on Vercel, in which case, y
<Step title="Override the build command">
Override to:
```
cd ../.. && yarn build --include-dependencies --no-deps
cd ../.. && yarn build --no-deps
```
</Step>
<Step title="Hit Deploy">
+2 -2
View File
@@ -33,8 +33,8 @@ RUN set -eux; \
apt-get clean && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives && \
yarn config set httpTimeout 1200000 && \
npx turbo prune --scope=@calcom/web --docker && \
npx turbo prune --scope=@calcom/api --docker && \
npx turbo prune --filter=@calcom/web --docker && \
npx turbo prune --filter=@calcom/api --docker && \
yarn install && \
yarn turbo run build --filter=@calcom/api
+13 -13
View File
@@ -28,23 +28,23 @@
"edit-app-template": "yarn app-store edit-template",
"delete-app-template": "yarn app-store delete-template",
"build": "turbo run build --filter=@calcom/web...",
"build:ai": "turbo run build --scope=\"@calcom/ai\"",
"build:ai": "turbo run build --filter=\"@calcom/ai\"",
"clean": "find . -name node_modules -o -name .next -o -name .turbo -o -name dist -type d -prune | xargs rm -rf",
"db-deploy": "turbo run db-deploy",
"db-seed": "turbo run db-seed",
"db-studio": "yarn prisma studio",
"deploy": "turbo run deploy",
"dev:all": "turbo run dev --scope=\"@calcom/web\" --scope=\"@calcom/website\" --scope=\"@calcom/console\"",
"dev:ai": "turbo run dev --scope=\"@calcom/web\" --scope=\"@calcom/api-proxy\" --scope=\"@calcom/api\" --scope=\"@calcom/ai\"",
"dev:api": "turbo run dev --scope=\"@calcom/web\" --scope=\"@calcom/api-proxy\" --scope=\"@calcom/api\"",
"dev:api:console": "turbo run dev --scope=\"@calcom/web\" --scope=\"@calcom/api-proxy\" --scope=\"@calcom/api\" --scope=\"@calcom/console\"",
"dev:console": "turbo run dev --scope=\"@calcom/web\" --scope=\"@calcom/console\"",
"dev:swagger": "turbo run dev --scope=\"@calcom/api-proxy\" --scope=\"@calcom/api\" --scope=\"@calcom/swagger\"",
"dev:website": "turbo run dev --scope=\"@calcom/web\" --scope=\"@calcom/website\"",
"dev": "turbo run dev --scope=\"@calcom/web\"",
"build-storybook": "turbo run build --scope=\"@calcom/storybook\"",
"dev:all": "turbo run dev --filter=\"@calcom/web\" --filter=\"@calcom/website\" --filter=\"@calcom/console\"",
"dev:ai": "turbo run dev --filter=\"@calcom/web\" --filter=\"@calcom/api-proxy\" --filter=\"@calcom/api\" --filter=\"@calcom/ai\"",
"dev:api": "turbo run dev --filter=\"@calcom/web\" --filter=\"@calcom/api-proxy\" --filter=\"@calcom/api\"",
"dev:api:console": "turbo run dev --filter=\"@calcom/web\" --filter=\"@calcom/api-proxy\" --filter=\"@calcom/api\" --filter=\"@calcom/console\"",
"dev:console": "turbo run dev --filter=\"@calcom/web\" --filter=\"@calcom/console\"",
"dev:swagger": "turbo run dev --filter=\"@calcom/api-proxy\" --filter=\"@calcom/api\" --filter=\"@calcom/swagger\"",
"dev:website": "turbo run dev --filter=\"@calcom/web\" --filter=\"@calcom/website\"",
"dev": "turbo run dev --filter=\"@calcom/web\"",
"build-storybook": "turbo run build --filter=\"@calcom/storybook\"",
"dx": "turbo run dx",
"i-dev": "infisical run -- turbo run dev --scope=\"@calcom/web\"",
"i-dev": "infisical run -- turbo run dev --filter=\"@calcom/web\"",
"i-dx": "infisical run -- turbo run dx",
"i-gen-web-example-env": "infisical secrets generate-example-env --tags=web > .env.example",
"i-gen-app-store-example-env": "infisical secrets generate-example-env --tags=appstore > .env.appStore.example",
@@ -61,7 +61,7 @@
"pre-commit": "lint-staged",
"predev": "echo 'Checking env files'",
"prisma": "yarn workspace @calcom/prisma prisma",
"start": "turbo run start --scope=\"@calcom/web\"",
"start": "turbo run start --filter=\"@calcom/web\"",
"tdd": "vitest watch",
"e2e": "NEXT_PUBLIC_IS_E2E=1 yarn playwright test --project=@calcom/web",
"e2e:app-store": "QUICK=true yarn playwright test --project=@calcom/app-store",
@@ -124,7 +124,7 @@
"date-fns-tz": "^3.2.0",
"eslint": "^8.34.0",
"p-limit": "^6.2.0",
"turbo": "^1.10.1"
"turbo": "^2.5.5"
},
"resolutions": {
"types-ramda": "0.29.4",
@@ -55,7 +55,7 @@ export const EventMeta = ({
timeZones,
children,
selectedTimeslot,
roundRobinHideOrgAndTeam
roundRobinHideOrgAndTeam,
}: {
event?: Pick<
BookerEvent,
@@ -4,7 +4,7 @@ import { useEffect, useRef } from "react";
import { useIsPlatform } from "@calcom/atoms/hooks/useIsPlatform";
import { useIsEmbed } from "@calcom/embed-core/embed-iframe";
import { useShouldShowArrows } from "@calcom/features/apps/components/AllApps";
import { useBookerStore } from "@calcom/features/bookings/Booker/store";
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
import type { BookerEvent } from "@calcom/features/bookings/types";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import classNames from "@calcom/ui/classNames";
@@ -45,7 +45,7 @@ export const EventDuration = ({
const { t } = useLocale();
const itemRefs = useRef<(HTMLLIElement | null)[]>([]);
const isPlatform = useIsPlatform();
const [selectedDuration, setSelectedDuration, state] = useBookerStore((state) => [
const [selectedDuration, setSelectedDuration, state] = useBookerStoreContext((state) => [
state.selectedDuration,
state.setSelectedDuration,
state.state,
@@ -550,6 +550,9 @@ async function handler(
eventType.schedulingType === SchedulingType.ROUND_ROBIN
) {
const bookingRepo = new BookingRepository(prisma);
const requiresPayment = !Number.isNaN(paymentAppData.price) && paymentAppData.price > 0;
const existingBooking = await bookingRepo.getValidBookingFromEventTypeForAttendee({
eventTypeId,
bookerEmail,
@@ -559,13 +562,20 @@ async function handler(
});
if (existingBooking) {
const hasPayments = existingBooking.payment.length > 0;
const isPaidBooking = existingBooking.paid || !hasPayments;
const shouldShowPaymentForm = requiresPayment && !isPaidBooking;
const firstPayment = shouldShowPaymentForm ? existingBooking.payment[0] : undefined;
const bookingResponse = {
...existingBooking,
user: {
...existingBooking.user,
email: null,
},
paymentRequired: false,
paymentRequired: shouldShowPaymentForm,
seatReferenceUid: "",
};
@@ -574,8 +584,8 @@ async function handler(
luckyUsers: bookingResponse.userId ? [bookingResponse.userId] : [],
isDryRun,
...(isDryRun ? { troubleshooterData } : {}),
paymentUid: undefined,
paymentId: undefined,
paymentUid: firstPayment?.uid,
paymentId: firstPayment?.id,
};
}
}
@@ -7,6 +7,8 @@
*
* They don't intend to test what the apps logic should do, but rather test if the apps are called with the correct data. For testing that, once should write tests within each app.
*/
import prismaMock from "../../../../../../tests/libs/__mocks__/prisma";
import {
createBookingScenario,
getDate,
@@ -3277,6 +3279,122 @@ describe("handleNewBooking", () => {
},
timeout
);
test(
`Payment retry scenario - should return existing payment UID and prevent duplicate bookings when retrying canceled payment`,
async ({ emails }) => {
const handleNewBooking = (await import("@calcom/features/bookings/lib/handleNewBooking")).default;
const booker = getBooker({
email: "booker@example.com",
name: "Booker",
});
const organizer = getOrganizer({
name: "Organizer",
email: "organizer@example.com",
id: 101,
schedules: [TestData.schedules.IstWorkHours],
credentials: [getGoogleCalendarCredential(), getStripeAppCredential()],
selectedCalendars: [TestData.selectedCalendars.google],
});
const scenarioData = getScenarioData({
eventTypes: [
{
id: 1,
title: "Paid Event",
description: "It's a test Paid Event",
slotInterval: 30,
requiresConfirmation: false,
metadata: {
apps: {
stripe: {
price: 100,
enabled: true,
currency: "usd",
},
},
},
length: 30,
users: [
{
id: 101,
},
],
},
],
organizer,
apps: [TestData.apps["stripe-payment"]],
});
await createBookingScenario(scenarioData);
mockSuccessfulVideoMeetingCreation({
metadataLookupKey: "dailyvideo",
});
const { paymentUid } = mockPaymentApp({
metadataLookupKey: "stripe",
appStoreLookupKey: "stripepayment",
});
mockCalendarToHaveNoBusySlots("googlecalendar");
const mockBookingData = getMockRequestDataForBooking({
data: {
eventTypeId: 1,
responses: {
email: booker.email,
name: booker.name,
location: { optionValue: "", value: "New York" },
},
},
});
const firstBooking = await handleNewBooking({
bookingData: mockBookingData,
});
expect(firstBooking).toEqual(
expect.objectContaining({
paymentUid: expect.any(String),
paymentRequired: true,
uid: expect.any(String),
})
);
const firstBookingInDb = await prismaMock.booking.findUnique({
where: { uid: firstBooking.uid },
include: { payment: true },
});
expect(firstBookingInDb).toBeTruthy();
expect(firstBookingInDb?.payment).toHaveLength(1);
expect(firstBookingInDb?.paid).toBe(false);
const secondBooking = await handleNewBooking({
bookingData: mockBookingData,
});
expect(secondBooking.uid).toBe(firstBooking.uid);
expect(secondBooking.paymentUid).toBe(firstBooking.paymentUid);
expect(secondBooking.paymentRequired).toBe(true);
const bookingsInDb = await prismaMock.booking.findMany({
where: {
eventTypeId: 1,
attendees: {
some: {
email: booker.email,
},
},
},
include: { payment: true },
});
expect(bookingsInDb).toHaveLength(1);
expect(bookingsInDb[0].payment).toHaveLength(1);
expect(bookingsInDb[0].uid).toBe(firstBooking.uid);
},
timeout
);
});
});
@@ -1,11 +1,7 @@
import removeMember from "@calcom/features/ee/teams/lib/removeMember";
import { TeamService } from "@calcom/lib/server/service/teamService";
const removeUserFromOrg = async ({ userId, orgId }: { userId: number; orgId: number }) => {
return removeMember({
memberId: userId,
teamId: orgId,
isOrg: true,
});
return TeamService.removeMembers({ teamIds: [orgId], userIds: [userId], isOrg: true });
};
export default removeUserFromOrg;
@@ -1,141 +0,0 @@
import logger from "@calcom/lib/logger";
import { ProfileRepository } from "@calcom/lib/server/repository/profile";
import prisma from "@calcom/prisma";
import { TRPCError } from "@trpc/server";
import { deleteWorkfowRemindersOfRemovedMember } from "./deleteWorkflowRemindersOfRemovedMember";
const log = logger.getSubLogger({ prefix: ["removeMember"] });
const removeMember = async ({
memberId,
teamId,
isOrg,
}: {
memberId: number;
teamId: number;
isOrg: boolean;
}) => {
const [membership] = await prisma.$transaction([
prisma.membership.delete({
where: {
userId_teamId: { userId: memberId, teamId: teamId },
},
include: {
user: true,
team: true,
},
}),
// remove user as host from team events associated with this membership
prisma.host.deleteMany({
where: {
userId: memberId,
eventType: {
teamId: teamId,
},
},
}),
]);
const team = await prisma.team.findUnique({
where: { id: teamId },
select: {
isOrganization: true,
organizationSettings: true,
id: true,
metadata: true,
activeOrgWorkflows: true,
parentId: true,
},
});
const foundUser = await prisma.user.findUnique({
where: { id: memberId },
select: {
id: true,
movedToProfileId: true,
email: true,
username: true,
completedOnboarding: true,
teams: {
select: {
team: {
select: {
id: true,
parentId: true,
},
},
},
},
},
});
if (!team || !foundUser) throw new TRPCError({ code: "NOT_FOUND" });
if (isOrg) {
log.debug("Removing a member from the organization");
// Deleting membership from all child teams
// Delete all sub-team memberships where this team is the organization
await prisma.membership.deleteMany({
where: {
team: {
parentId: teamId,
},
userId: membership.userId,
},
});
const userToDeleteMembershipOf = foundUser;
const profileToDelete = await ProfileRepository.findByUserIdAndOrgId({
userId: userToDeleteMembershipOf.id,
organizationId: team.id,
});
if (
userToDeleteMembershipOf.username &&
userToDeleteMembershipOf.movedToProfileId === profileToDelete?.id
) {
log.debug("Cleaning up tempOrgRedirect for user", userToDeleteMembershipOf.username);
await prisma.tempOrgRedirect.deleteMany({
where: {
from: userToDeleteMembershipOf.username,
},
});
}
await prisma.$transaction([
prisma.user.update({
where: { id: membership.userId },
data: { organizationId: null },
}),
ProfileRepository.delete({
userId: membership.userId,
organizationId: team.id,
}),
prisma.host.deleteMany({
where: {
userId: memberId,
eventType: {
team: {
parentId: teamId,
},
},
},
}),
]);
}
// Deleted managed event types from this team from this member
await prisma.eventType.deleteMany({
where: { parent: { teamId: teamId }, userId: membership.userId },
});
await deleteWorkfowRemindersOfRemovedMember(team, memberId, isOrg);
return { membership };
};
export default removeMember;
@@ -110,13 +110,15 @@ describe("RoleManagementFactory", () => {
it("should allow role change when user has permission", async () => {
mockPermissionCheckService.checkPermission.mockResolvedValue(true);
const manager = await factory.createRoleManager(organizationId);
await expect(manager.checkPermissionToChangeRole(userId, organizationId)).resolves.not.toThrow();
await expect(
manager.checkPermissionToChangeRole(userId, organizationId, "org")
).resolves.not.toThrow();
});
it("should throw UNAUTHORIZED when user lacks permission", async () => {
mockPermissionCheckService.checkPermission.mockResolvedValue(false);
const manager = await factory.createRoleManager(organizationId);
await expect(manager.checkPermissionToChangeRole(userId, organizationId)).rejects.toThrow(
await expect(manager.checkPermissionToChangeRole(userId, organizationId, "org")).rejects.toThrow(
new RoleManagementError(
"You do not have permission to change roles",
RoleManagementErrorCode.UNAUTHORIZED
@@ -193,13 +195,15 @@ describe("RoleManagementFactory", () => {
customRoleId: null,
});
const manager = await factory.createRoleManager(organizationId);
await expect(manager.checkPermissionToChangeRole(userId, organizationId)).resolves.not.toThrow();
await expect(
manager.checkPermissionToChangeRole(userId, organizationId, "org")
).resolves.not.toThrow();
});
it("should throw UNAUTHORIZED when user is not owner", async () => {
vi.mocked(isOrganisationAdmin).mockResolvedValue(false);
const manager = await factory.createRoleManager(organizationId);
await expect(manager.checkPermissionToChangeRole(userId, organizationId)).rejects.toThrow(
await expect(manager.checkPermissionToChangeRole(userId, organizationId, "org")).rejects.toThrow(
new RoleManagementError(
"Only owners or admin can update roles",
RoleManagementErrorCode.UNAUTHORIZED
@@ -1,5 +1,6 @@
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
import { isOrganisationAdmin } from "@calcom/lib/server/queries/organisations";
import { isTeamAdmin } from "@calcom/lib/server/queries/teams";
import { prisma } from "@calcom/prisma";
import { MembershipRole } from "@calcom/prisma/enums";
@@ -10,7 +11,7 @@ import { RoleService } from "./role.service";
interface IRoleManager {
isPBACEnabled: boolean;
checkPermissionToChangeRole(userId: number, organizationId: number): Promise<void>;
checkPermissionToChangeRole(userId: number, targetId: number, scope: "org" | "team"): Promise<void>;
assignRole(
userId: number,
organizationId: number,
@@ -29,11 +30,11 @@ class PBACRoleManager implements IRoleManager {
private readonly permissionCheckService: PermissionCheckService
) {}
async checkPermissionToChangeRole(userId: number, organizationId: number): Promise<void> {
async checkPermissionToChangeRole(userId: number, targetId: number, scope: "org" | "team"): Promise<void> {
const hasPermission = await this.permissionCheckService.checkPermission({
userId,
teamId: organizationId,
permission: "organization.changeMemberRole",
teamId: targetId,
permission: scope === "team" ? "team.changeMemberRole" : "organization.changeMemberRole",
fallbackRoles: [MembershipRole.OWNER, MembershipRole.ADMIN],
});
@@ -95,9 +96,11 @@ class PBACRoleManager implements IRoleManager {
class LegacyRoleManager implements IRoleManager {
public isPBACEnabled = false;
async checkPermissionToChangeRole(userId: number, organizationId: number): Promise<void> {
const membership = await isOrganisationAdmin(userId, organizationId);
async checkPermissionToChangeRole(userId: number, targetId: number, scope: "org" | "team"): Promise<void> {
const membership =
scope === "team"
? !!(await isTeamAdmin(userId, targetId))
: !!(await isOrganisationAdmin(userId, targetId));
// Only OWNER/ADMIN can update role
if (!membership) {
throw new RoleManagementError(
+2 -1
View File
@@ -45,7 +45,8 @@ export const IS_CALCOM =
(new URL(WEBAPP_URL).hostname.endsWith("cal.com") ||
new URL(WEBAPP_URL).hostname.endsWith("cal.dev") ||
new URL(WEBAPP_URL).hostname.endsWith("cal.qa") ||
new URL(WEBAPP_URL).hostname.endsWith("cal-staging.com"));
new URL(WEBAPP_URL).hostname.endsWith("cal-staging.com") ||
new URL(WEBAPP_URL).hostname.endsWith("cal.eu"));
export const CONSOLE_URL =
new URL(WEBAPP_URL).hostname.endsWith(".cal.dev") ||
@@ -5,8 +5,8 @@ import type {
InsightsRoutingServicePublicOptions,
InsightsRoutingServiceFilterOptions,
InsightsRoutingBaseService,
} from "@calcom/lib/server/service/insightsRoutingBase";
import type { InsightsRoutingService } from "@calcom/lib/server/service/insightsRoutingDI";
} from "@calcom/lib/server/service/InsightsRoutingBaseService";
import type { InsightsRoutingService } from "@calcom/lib/server/service/InsightsRoutingDIService";
import { prismaModule } from "@calcom/prisma/prisma.module";
import { insightsRoutingModule } from "../modules/InsightsRouting";
+2 -2
View File
@@ -1,7 +1,7 @@
import { createModule } from "@evyweb/ioctopus";
import type { IInsightsRoutingService } from "@calcom/lib/server/service/insightsRoutingDI";
import { InsightsRoutingService } from "@calcom/lib/server/service/insightsRoutingDI";
import type { IInsightsRoutingService } from "@calcom/lib/server/service/InsightsRoutingDIService";
import { InsightsRoutingService } from "@calcom/lib/server/service/InsightsRoutingDIService";
import { DI_TOKENS } from "../tokens";
+8
View File
@@ -0,0 +1,8 @@
import { WEBAPP_URL, IS_CALCOM } from "./constants";
export const getCalcomUrl = () => {
if (IS_CALCOM) {
return new URL(WEBAPP_URL).hostname.endsWith("cal.eu") ? "https://cal.eu" : "https://cal.com";
}
return WEBAPP_URL;
};
@@ -828,6 +828,7 @@ export class BookingRepository {
attendees: true,
references: true,
user: true,
payment: true,
},
});
}
@@ -4,7 +4,7 @@ import {
InsightsRoutingBaseService,
type InsightsRoutingServicePublicOptions,
type InsightsRoutingServiceFilterOptions,
} from "./insightsRoutingBase";
} from "./InsightsRoutingBaseService";
export interface IInsightsRoutingService {
prisma: typeof readonlyPrisma;
@@ -7,7 +7,7 @@ import { ColumnFilterType } from "@calcom/features/data-table/lib/types";
import prisma from "@calcom/prisma";
import { BookingStatus, MembershipRole } from "@calcom/prisma/enums";
import { InsightsRoutingBaseService as InsightsRoutingService } from "../../service/insightsRoutingBase";
import { InsightsRoutingBaseService as InsightsRoutingService } from "../../service/InsightsRoutingBaseService";
// SQL condition constants for testing
const NOTHING_CONDITION = Prisma.sql`1=0`;
File diff suppressed because it is too large. Load diff
+263 -6
View File
@@ -1,16 +1,52 @@
import { Prisma } from "@prisma/client";
import { TeamBilling } from "@calcom/features/ee/billing/teams";
import removeMember from "@calcom/features/ee/teams/lib/removeMember";
import { deleteWorkfowRemindersOfRemovedMember } from "@calcom/features/ee/teams/lib/deleteWorkflowRemindersOfRemovedMember";
import { deleteDomain } from "@calcom/lib/domainManager/organization";
import logger from "@calcom/lib/logger";
import { ProfileRepository } from "@calcom/lib/server/repository/profile";
import { TeamRepository } from "@calcom/lib/server/repository/team";
import { WorkflowService } from "@calcom/lib/server/service/workflows";
import prisma from "@calcom/prisma";
import type { Membership } from "@calcom/prisma/client";
import { MembershipRole } from "@calcom/prisma/enums";
import { TRPCError } from "@trpc/server";
const log = logger.getSubLogger({ prefix: ["TeamService"] });
type MembershipWithRelations = Pick<
Membership,
"id" | "userId" | "teamId" | "role" | "accepted" | "disableImpersonation"
>;
type TeamWithSettings = {
id: number;
isOrganization: boolean | null;
organizationSettings: unknown;
metadata: unknown;
activeOrgWorkflows: unknown;
parentId: number | null;
};
type UserWithTeams = {
id: number;
movedToProfileId: number | null;
email: string;
username: string | null;
completedOnboarding: boolean;
teams: {
team: {
id: number;
parentId: number | null;
};
}[];
};
export type RemoveMemberResult = {
membership: MembershipWithRelations;
};
export class TeamService {
/**
* Deletes a team and all its associated data in a safe, transactional order.
@@ -45,15 +81,23 @@ export class TeamService {
return deletedTeam;
}
static async removeMembers(teamIds: number[], memberIds: number[], isOrg = false) {
const deleteMembershipPromises = [];
static async removeMembers({
teamIds,
userIds,
isOrg = false,
}: {
teamIds: number[];
userIds: number[];
isOrg?: boolean;
}) {
const deleteMembershipPromises: Promise<RemoveMemberResult>[] = [];
for (const memberId of memberIds) {
for (const userId of userIds) {
for (const teamId of teamIds) {
deleteMembershipPromises.push(
removeMember({
TeamService.removeMember({
teamId,
memberId,
userId,
isOrg,
})
);
@@ -121,4 +165,217 @@ export class TeamService {
const teamBilling = await TeamBilling.findAndInit(teamId);
return teamBilling.publish();
}
private static async removeMember({
userId,
teamId,
isOrg,
}: {
userId: number;
teamId: number;
isOrg: boolean;
}) {
const membership = await TeamService.fetchMembershipOrThrow(userId, teamId);
const team = await TeamService.fetchTeamOrThrow(teamId);
const user = await TeamService.fetchUserOrThrow(userId);
if (isOrg) {
log.debug("Removing a member from the organization");
await TeamService.removeFromOrganization(membership, team, user);
} else {
log.debug("Removing a member from a team");
await TeamService.removeFromTeam(membership, teamId);
}
await deleteWorkfowRemindersOfRemovedMember(team, userId, isOrg);
return { membership };
}
// TODO: Needs to be moved to repository
private static async fetchMembershipOrThrow(
userId: number,
teamId: number
): Promise<MembershipWithRelations> {
const membership = await prisma.membership.findUnique({
where: {
userId_teamId: { userId: userId, teamId: teamId },
},
select: {
id: true,
userId: true,
teamId: true,
role: true,
accepted: true,
disableImpersonation: true,
},
});
if (!membership) {
throw new TRPCError({ code: "NOT_FOUND", message: "Membership not found" });
}
return membership;
}
// TODO: Needs to be moved to repository
private static async fetchTeamOrThrow(teamId: number): Promise<TeamWithSettings> {
const team = await prisma.team.findUnique({
where: { id: teamId },
select: {
isOrganization: true,
organizationSettings: true,
id: true,
metadata: true,
activeOrgWorkflows: true,
parentId: true,
},
});
if (!team) {
throw new TRPCError({ code: "NOT_FOUND", message: "Team not found" });
}
return team;
}
// TODO: Needs to be moved to repository
private static async fetchUserOrThrow(userId: number): Promise<UserWithTeams> {
const user = await prisma.user.findUnique({
where: { id: userId },
select: {
id: true,
movedToProfileId: true,
email: true,
username: true,
completedOnboarding: true,
teams: {
select: {
team: {
select: {
id: true,
parentId: true,
},
},
},
},
},
});
if (!user) {
throw new TRPCError({ code: "NOT_FOUND", message: "User not found" });
}
return user;
}
// TODO: Needs to be moved to repository
private static async cleanupTempOrgRedirect(user: UserWithTeams, team: TeamWithSettings) {
const profileToDelete = await ProfileRepository.findByUserIdAndOrgId({
userId: user.id,
organizationId: team.id,
});
if (user.username && user.movedToProfileId === profileToDelete?.id) {
log.debug("Cleaning up tempOrgRedirect for user", user.username);
await prisma.tempOrgRedirect.deleteMany({
where: {
from: user.username,
},
});
}
}
private static async removeFromOrganization(
membership: MembershipWithRelations,
team: TeamWithSettings,
user: UserWithTeams
) {
await TeamService.cleanupTempOrgRedirect(user, team);
const newUsername = generateNewUsername(user);
await prisma.$transaction([
// Remove user from all sub-teams event type hosts
prisma.host.deleteMany({
where: {
userId: membership.userId,
eventType: {
team: {
parentId: team.id,
},
},
},
}),
// Delete managed child events in sub-teams
prisma.eventType.deleteMany({
where: {
userId: membership.userId,
parent: {
team: {
parentId: team.id,
},
},
},
}),
// Remove organizationId from the user
prisma.user.update({
where: { id: membership.userId },
data: {
organizationId: null,
username: newUsername,
},
}),
// Delete the profile of the user from the organization
ProfileRepository.delete({
userId: membership.userId,
organizationId: team.id,
}),
// Delete all sub-team memberships where this team is the organization
prisma.membership.deleteMany({
where: {
team: {
parentId: team.id,
},
userId: membership.userId,
},
}),
// Delete the membership of the user from the organization
prisma.membership.delete({
where: {
userId_teamId: { userId: membership.userId, teamId: team.id },
},
}),
]);
// Generate new username for user leaving organization
function generateNewUsername(user: UserWithTeams): string | null {
// We ensure that new username would be unique across all users in the global namespace outside any organization
return user.username != null ? `${user.username}-${user.id}` : null;
}
}
// Remove member from regular team
private static async removeFromTeam(membership: MembershipWithRelations, teamId: number) {
await prisma.$transaction([
// Remove user from all team event types' hosts
prisma.host.deleteMany({
where: {
userId: membership.userId,
eventType: {
teamId: teamId,
},
},
}),
// Deleted managed event types from this team for this member
prisma.eventType.deleteMany({
where: { parent: { teamId: teamId }, userId: membership.userId },
}),
// Delete the membership of the user from the team
prisma.membership.delete({
where: {
userId_teamId: { userId: membership.userId, teamId: teamId },
},
}),
]);
}
}
+1
View File
@@ -120,4 +120,5 @@ export { checkAdminOrOwner } from "@calcom/features/auth/lib/checkAdminOrOwner";
export { verifyPhoneNumber, sendVerificationCode };
export { TeamService } from "@calcom/lib/server/service/teamService";
export { CacheService } from "@calcom/features/calendar-cache/lib/getShouldServeCache";
Binary file not shown.
@@ -47,7 +47,7 @@ export const updateUserHandler = async ({ ctx, input }: UpdateUserOptions) => {
const roleManager = await RoleManagementFactory.getInstance().createRoleManager(organizationId);
try {
await roleManager.checkPermissionToChangeRole(userId, organizationId);
await roleManager.checkPermissionToChangeRole(userId, organizationId, "org");
} catch (error) {
if (error instanceof RoleManagementError) {
throw new TRPCError({ code: "UNAUTHORIZED", message: error.message });
@@ -1,5 +1,5 @@
import { RoleManagementFactory } from "@calcom/features/pbac/services/role-management.factory";
import { isTeamAdmin, isTeamOwner } from "@calcom/lib/server/queries/teams";
import { isTeamOwner } from "@calcom/lib/server/queries/teams";
import { TeamRepository } from "@calcom/lib/server/repository/team";
import { prisma } from "@calcom/prisma";
import { MembershipRole } from "@calcom/prisma/enums";
@@ -32,7 +32,7 @@ export const changeMemberRoleHandler = async ({ ctx, input }: ChangeMemberRoleOp
// Check permission to change roles
try {
await roleManager.checkPermissionToChangeRole(ctx.user.id, organizationId);
await roleManager.checkPermissionToChangeRole(ctx.user.id, input.teamId, "team");
} catch (error) {
throw new TRPCError({
code: "UNAUTHORIZED",
@@ -45,8 +45,6 @@ export const changeMemberRoleHandler = async ({ ctx, input }: ChangeMemberRoleOp
typeof input.role === "string" &&
Object.values(MembershipRole).includes(input.role as MembershipRole)
) {
// Traditional role assignment logic
if (!(await isTeamAdmin(ctx.user?.id, input.teamId))) throw new TRPCError({ code: "UNAUTHORIZED" });
// Only owners can award owner role.
if (input.role === MembershipRole.OWNER && !(await isTeamOwner(ctx.user?.id, input.teamId)))
throw new TRPCError({ code: "UNAUTHORIZED" });
@@ -59,7 +59,7 @@ export const removeMemberHandler = async ({ ctx, input }: RemoveMemberOptions) =
message: "You can not remove yourself from a team you own.",
});
await TeamService.removeMembers(teamIds, memberIds, isOrg);
await TeamService.removeMembers({ teamIds, userIds: memberIds, isOrg });
};
export default removeMemberHandler;
@@ -1,167 +0,0 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-nocheck
// TODO: Bring this test back with the correct setup (no illegal imports)
import prismaMock from "../../../../../../tests/libs/__mocks__/prisma";
import { describe, test, expect } from "vitest";
import { SchedulingType, MembershipRole } from "@calcom/prisma/enums";
import type { TrpcSessionUser } from "../../../types";
import removeMember from "./removeMember.handler";
describe.skip("removeMember", () => {
describe("should remove a member from a team", () => {
test(`1) Should remove a member from a team
2) Should remove the member from hosts
`, async () => {
const org = await createOrganization({
name: "Test Org",
slug: "testorg",
});
// Create the child team
const childTeam = {
id: 202,
name: "Team 1",
slug: "team-1",
parentId: org.id,
};
const organizer = getOrganizer({
name: "Organizer",
email: "organizer@example.com",
id: 101,
organizationId: org.id,
schedules: [TestData.schedules.IstWorkHours],
teams: [
{
membership: {
accepted: true,
role: MembershipRole.ADMIN,
},
team: {
id: org.id,
name: "Test Org",
slug: "testorg",
},
},
{
membership: {
accepted: true,
role: MembershipRole.ADMIN,
},
team: {
id: childTeam.id,
name: "Team 1",
slug: "team-1",
parentId: org.id,
},
},
],
});
const otherTeamMembers = [
{
name: "Other Team Member 1",
username: "other-team-member-1",
timeZone: Timezones["+5:30"],
defaultScheduleId: null,
email: "other-team-member-1@example.com",
id: 102,
organizationId: org.id,
schedules: [TestData.schedules.IstEveningShift],
teams: [
{
membership: {
accepted: true,
role: MembershipRole.MEMBER,
},
team: {
id: org.id,
name: "Test Org",
slug: "testorg",
},
},
{
membership: {
accepted: true,
role: MembershipRole.MEMBER,
},
team: {
id: childTeam.id,
name: "Team 1",
slug: "team-1",
parentId: org.id,
},
},
],
},
];
await createBookingScenario(
getScenarioData(
{
eventTypes: [
{
id: 1,
teamId: childTeam.id,
schedulingType: SchedulingType.ROUND_ROBIN,
length: 30,
hosts: [
{
userId: 101,
isFixed: false,
},
{
userId: 102,
isFixed: false,
},
],
},
],
organizer,
usersApartFromOrganizer: otherTeamMembers,
apps: [TestData.apps["daily-video"]],
},
org
)
);
//Logged in user is admin of the org
const ctx = {
user: {
id: organizer.id,
name: organizer.name,
} as NonNullable<TrpcSessionUser>,
};
await removeMember({
ctx,
input: {
teamIds: [org.id],
memberIds: [102],
isOrg: true,
},
});
//Check if the remaining memberships are correct
const remainingMemberships = await prismaMock.membership.count({
where: {
teamId: childTeam.id,
},
});
expect(remainingMemberships).toBe(1);
//Check if the event type has the correct hosts
const remainingHostsCount = await prismaMock.host.count({
where: {
eventTypeId: 1,
},
});
expect(remainingHostsCount).toBe(1);
});
});
});
+4 -15
View File
@@ -1,19 +1,5 @@
{
"extends": "@calcom/tsconfig/react-library.json",
"paths": {
"~/*": ["/*"],
"@calcom/app-store": ["../app-store"],
"@calcom/app-store/*": ["../app-store/*"],
"@calcom/dayjs": ["../dayjs"],
"@calcom/dayjs/*": ["../dayjs/*"],
"@calcom/features": ["../features"],
"@calcom/features/*": ["../features/*"],
"@calcom/lib": ["../lib"],
"@calcom/lib/*": ["../lib/*"],
"@calcom/prisma": ["../prisma"],
"@calcom/prisma/*": ["../prisma/*"],
"@calcom/repository/*": ["../lib/server/repository/*"]
},
"include": [
"../types/@wojtekmaj__react-daterange-picker.d.ts",
"../types/business-days-plugin.d.ts",
@@ -33,6 +19,9 @@
"declarationMap": false,
"resolveJsonModule": true,
"declarationDir": "types/server",
"moduleResolution": "node"
"moduleResolution": "node",
"paths": {
"~/*": ["./*"]
}
}
}
+254 -253
View File
@@ -1,6 +1,257 @@
{
"$schema": "https://turborepo.org/schema.json",
"pipeline": {
"globalDependencies": ["yarn.lock"],
"globalEnv": [
"ALLOWED_HOSTNAMES",
"ANALYZE",
"API_KEY_PREFIX",
"APP_USER_NAME",
"ATOMS_E2E_API_URL",
"ATOMS_E2E_OAUTH_CLIENT_ID",
"ATOMS_E2E_OAUTH_CLIENT_ID_BOOKER_EMBED",
"ATOMS_E2E_OAUTH_CLIENT_SECRET",
"ATOMS_E2E_ORG_ID",
"BASECAMP3_CLIENT_ID",
"BASECAMP3_CLIENT_SECRET",
"BASECAMP3_USER_AGENT",
"BLACKLISTED_GUEST_EMAILS",
"AUTH_BEARER_TOKEN_VERCEL",
"BUILD_ID",
"CAL_SIGNATURE_TOKEN",
"CALCOM_PRIVATE_API_ROUTE",
"CALCOM_APP_CREDENTIAL_ENCRYPTION_KEY",
"CALCOM_CREDENTIAL_SYNC_ENDPOINT",
"CALCOM_CREDENTIAL_SYNC_SECRET",
"CALCOM_CREDENTIAL_SYNC_SECRET_HEADER_NAME",
"CALCOM_ENV",
"CALCOM_LICENSE_KEY",
"CALCOM_QA_EMAIL",
"CALCOM_QA_PASSWORD",
"CALCOM_TELEMETRY_DISABLED",
"CALCOM_CREDENTIAL_SYNC_HEADER_NAME",
"CALENDSO_ENCRYPTION_KEY",
"CALCOM_CREDENTIAL_SYNC_SECRET",
"CI",
"CLOSECOM_CLIENT_ID",
"CLOSECOM_CLIENT_SECRET",
"CRON_API_KEY",
"CRON_SECRET",
"CRON_ENABLE_APP_SYNC",
"CLOUDFLARE_TURNSTILE_SECRET",
"DAILY_API_KEY",
"DAILY_SCALE_PLAN",
"DAILY_WEBHOOK_SECRET",
"DAILY_MEETING_ENDED_WEBHOOK_SECRET",
"DAILY_VIDEO_REGION",
"DATABASE_CHUNK_SIZE",
"DATABASE_DIRECT_URL",
"DATABASE_URL",
"DEBUG",
"DUB_API_KEY",
"NEXT_PUBLIC_DUB_PROGRAM_ID",
"E2E_TEST_APPLE_CALENDAR_EMAIL",
"E2E_TEST_APPLE_CALENDAR_PASSWORD",
"E2E_TEST_CALCOM_QA_EMAIL",
"E2E_TEST_CALCOM_QA_PASSWORD",
"E2E_TEST_CALCOM_QA_GCAL_CREDENTIALS",
"E2E_TEST_CALCOM_GCAL_KEYS",
"E2E_TEST_MAILHOG_ENABLED",
"E2E_TEST_OIDC_CLIENT_ID",
"E2E_TEST_OIDC_CLIENT_SECRET",
"E2E_TEST_OIDC_PROVIDER_DOMAIN",
"E2E_TEST_OIDC_USER_EMAIL",
"E2E_TEST_OIDC_USER_PASSWORD",
"E2E_TEST_SAML_ADMIN_EMAIL",
"E2E_TEST_SAML_ADMIN_PASSWORD",
"EMAIL_FROM",
"EMAIL_FROM_NAME",
"EMAIL_SERVER_HOST",
"EMAIL_SERVER_PASSWORD",
"EMAIL_SERVER_PORT",
"EMAIL_SERVER_USER",
"EMAIL_SERVER",
"ENABLE_EXPANDED_CACHE",
"EXCHANGE_DEFAULT_EWS_URL",
"FORMBRICKS_FEEDBACK_SURVEY_ID",
"AVATARAPI_USERNAME",
"AVATARAPI_PASSWORD",
"GIPHY_API_KEY",
"GITHUB_API_REPO_TOKEN",
"GOOGLE_API_CREDENTIALS",
"GOOGLE_LOGIN_ENABLED",
"GOOGLE_WEBHOOK_TOKEN",
"HEROKU_APP_NAME",
"HUBSPOT_CLIENT_ID",
"HUBSPOT_CLIENT_SECRET",
"IFFY_API_KEY",
"INTEGRATION_TEST_MODE",
"IS_E2E",
"INTERCOM_SECRET",
"INTERCOM_SECRET",
"INSIGHTS_DATABASE_URL",
"IP_BANLIST",
"LARK_OPEN_APP_ID",
"LARK_OPEN_APP_SECRET",
"LARK_OPEN_VERIFICATION_TOKEN",
"MOCK_PAYMENT_APP_ENABLED",
"MS_GRAPH_CLIENT_ID",
"MS_GRAPH_CLIENT_SECRET",
"NEXT_PUBLIC_API_URL",
"NEXT_PUBLIC_APP_NAME",
"NEXT_PUBLIC_AUTH_URL",
"NEXT_PUBLIC_CALCOM_VERSION",
"NEXT_PUBLIC_COMPANY_NAME",
"NEXT_PUBLIC_CONSOLE_URL",
"NEXT_PUBLIC_LOGGER_LEVEL",
"NEXT_PUBLIC_DISABLE_SIGNUP",
"NEXT_PUBLIC_EMBED_LIB_URL",
"NEXT_PUBLIC_FORMBRICKS_HOST_URL",
"NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID",
"NEXT_PUBLIC_HOSTED_CAL_FEATURES",
"NEXT_PUBLIC_MINUTES_TO_BOOK",
"NEXT_PUBLIC_ORG_SELF_SERVE_ENABLED",
"NEXT_PUBLIC_SENDER_ID",
"NEXT_PUBLIC_SENDGRID_SENDER_NAME",
"NEXT_PUBLIC_STRIPE_PRICING_TABLE_ID",
"NEXT_PUBLIC_STRIPE_PRICING_TABLE_PUBLISHABLE_KEY",
"NEXT_PUBLIC_SUPPORT_MAIL_ADDRESS",
"NEXT_PUBLIC_SWAGGER_DOCS_URL",
"NEXT_PUBLIC_TEAM_IMPERSONATION",
"NEXT_PUBLIC_VERCEL_URL",
"NEXT_PUBLIC_CLOUDFLARE_SITEKEY",
"NEXT_PUBLIC_CLOUDFLARE_USE_TURNSTILE_IN_BOOKER",
"NEXT_PUBLIC_VERCEL_ENV",
"NEXT_PUBLIC_VERCEL_BRANCH_URL",
"NEXT_PUBLIC_GTM_ID",
"NEXT_PUBLIC_ORGANIZATIONS_MIN_SELF_SERVE_SEATS",
"NEXT_PUBLIC_ORGANIZATIONS_SELF_SERVE_PRICE_NEW",
"NEXT_PUBLIC_WEBSITE_PRIVACY_POLICY_URL",
"NEXT_PUBLIC_WEBSITE_TERMS_URL",
"NEXT_RUNTIME",
"NEXTAUTH_COOKIE_DOMAIN",
"NEXTAUTH_SECRET",
"NEXTAUTH_URL",
"NODE_ENV",
"ORGANIZATIONS_ENABLED",
"ORGANIZATIONS_AUTOLINK",
"PAYMENT_FEE_FIXED",
"PAYMENT_FEE_PERCENTAGE",
"PLAYWRIGHT_HEADLESS",
"PLAYWRIGHT_TEST_BASE_URL",
"PRISMA_FIELD_ENCRYPTION_KEY",
"PRISMA_GENERATE_DATAPROXY",
"PROJECT_ID_VERCEL",
"QUICK",
"RAILWAY_STATIC_URL",
"RENDER_EXTERNAL_URL",
"RESERVED_SUBDOMAINS",
"RETELL_AI_KEY",
"SALESFORCE_CONSUMER_KEY",
"SALESFORCE_CONSUMER_SECRET",
"SAML_ADMINS",
"SAML_CLIENT_SECRET_VERIFIER",
"SAML_DATABASE_URL",
"SEND_FEEDBACK_EMAIL",
"SENDGRID_API_KEY",
"SENDGRID_EMAIL",
"SENDGRID_SYNC_API_KEY",
"SKIP_DB_MIGRATIONS",
"SLACK_CLIENT_ID",
"SLACK_CLIENT_SECRET",
"SLACK_SIGNING_SECRET",
"STRIPE_CLIENT_ID",
"STRIPE_PRIVATE_KEY",
"STRIPE_PRODUCT_ID_ESSENTIALS",
"STRIPE_PRODUCT_ID_SCALE",
"STRIPE_PRODUCT_ID_STARTER",
"STRIPE_WEBHOOK_SECRET",
"STRIPE_WEBHOOK_SECRET_APPS",
"STRIPE_WEBHOOK_SECRET_BILLING",
"STRIPE_TEAM_MONTHLY_PRICE_ID",
"TANDEM_BASE_URL",
"TANDEM_CLIENT_ID",
"TANDEM_CLIENT_SECRET",
"TASKER_ENABLE_WEBHOOKS",
"TEAM_ID_VERCEL",
"TELEMETRY_DEBUG",
"TWILIO_MESSAGING_SID",
"TWILIO_OPT_OUT_ENABLED",
"TWILIO_PHONE_NUMBER",
"TWILIO_WHATSAPP_PHONE_NUMBER",
"TWILIO_SID",
"TWILIO_TOKEN",
"TWILIO_VERIFY_SID",
"TWILIO_WHATSAPP_REMINDER_CONTENT_SID",
"TWILIO_WHATSAPP_CANCELLED_CONTENT_SID",
"TWILIO_WHATSAPP_RESCHEDULED_CONTENT_SID",
"TWILIO_WHATSAPP_COMPLETED_CONTENT_SID",
"UPSTASH_REDIS_REST_TOKEN",
"UPSTASH_REDIS_REST_URL",
"UNKEY_ROOT_KEY",
"USERNAME_BLACKLIST_URL",
"VERCEL_ENV",
"VERCEL_URL",
"VITAL_API_KEY",
"VITAL_DEVELOPMENT_MODE",
"VITAL_REGION",
"VITAL_WEBHOOK_SECRET",
"ZAPIER_INVITE_LINK",
"ZOHOCRM_CLIENT_ID",
"ZOHOCRM_CLIENT_SECRET",
"ZOOM_CLIENT_ID",
"ZOOM_CLIENT_SECRET",
"REVERT_API_KEY",
"REVERT_API_URL",
"REVERT_PUBLIC_TOKEN",
"RESEND_API_KEY",
"LOCAL_TESTING_DOMAIN_VERCEL",
"AUTH_BEARER_TOKEN_CLOUDFLARE",
"CLOUDFLARE_ZONE_ID",
"NEXT_PUBLIC_MINTLIFY_CHAT_API_KEY",
"NEXT_PUBLIC_CHAT_API_URL",
"NEXT_PUBLIC_DOCS_URL",
"CLOUDFLARE_VERCEL_CNAME",
"CLOUDFLARE_DNS",
"EMBED_PUBLIC_EMBED_FINGER_PRINT",
"EMBED_PUBLIC_EMBED_VERSION",
"EMBED_PUBLIC_WEBAPP_URL",
"EMBED_PUBLIC_VERCEL_URL",
"EMBED_PUBLIC_EMBED_LIB_URL",
"NEXT_PUBLIC_ENABLE_PROFILE_SWITCHER",
"NEXT_PUBLIC_QUERY_AVAILABLE_SLOTS_INTERVAL_SECONDS",
"NEXT_PUBLIC_QUERY_RESERVATION_INTERVAL_SECONDS",
"NEXT_PUBLIC_QUERY_RESERVATION_STALE_TIME_SECONDS",
"NEXT_PUBLIC_INVALIDATE_AVAILABLE_SLOTS_ON_BOOKING_FORM",
"NEXT_PUBLIC_QUICK_AVAILABILITY_ROLLOUT",
"NEXT_PUBLIC_HEAD_SCRIPTS",
"NEXT_PUBLIC_BODY_SCRIPTS",
"NEXT_PUBLIC_ORG_SELF_SERVE_ENABLED",
"NEXT_PUBLIC_API_V2_ROOT_URL",
"NEXT_PUBLIC_VAPID_PUBLIC_KEY",
"VAPID_PRIVATE_KEY",
"CAL_VIDEO_BUCKET_NAME",
"CAL_VIDEO_BUCKET_REGION",
"CAL_VIDEO_ASSUME_ROLE_ARN",
"CAL_VIDEO_MEETING_LINK_FOR_TESTING",
"NEXT_PUBLIC_POSTHOG_KEY",
"NEXT_PUBLIC_POSTHOG_HOST",
"VAPID_PRIVATE_KEY",
"HUDDLE01_API_TOKEN",
"LINGO_DOT_DEV_API_KEY",
"DIRECTORY_IDS_TO_LOG",
"NEXT_PUBLIC_SINGLE_ORG_SLUG",
"GOOGLE_REFRESH_TOKEN",
"GOOGLE_CLIENT_ID",
"GOOGLE_CLIENT_SECRET",
"ZOOM_REFRESH_TOKEN",
"CALCOM_ADMIN_API_KEY",
"NEXT_PUBLIC_SINGLE_ORG_MODE_ENABLED",
"CALCOM_SERVICE_ACCOUNT_ENCRYPTION_KEY",
"CAL_VIDEO_RECORDING_TOKEN_SECRET",
"ORGANIZER_EMAIL_EXEMPT_DOMAINS",
"SLOTS_CACHE_TTL"
],
"tasks": {
"@calcom/prisma#build": {
"cache": false,
"dependsOn": ["post-install"]
@@ -207,7 +458,7 @@
},
"@calcom/app-store-cli#build": {
"cache": false,
"inputs": ["../../app-store/**/**"],
"inputs": ["../../app-store/**"],
"outputs": ["../../app-store/apps.server.generated.ts", "../../app-store/apps.browser.generated.tsx"]
},
"@calcom/embed-react#type-check": {
@@ -247,255 +498,5 @@
"//#test": {
"cache": false
}
},
"globalDependencies": ["yarn.lock"],
"globalEnv": [
"ALLOWED_HOSTNAMES",
"ANALYZE",
"API_KEY_PREFIX",
"APP_USER_NAME",
"ATOMS_E2E_API_URL",
"ATOMS_E2E_OAUTH_CLIENT_ID",
"ATOMS_E2E_OAUTH_CLIENT_ID_BOOKER_EMBED",
"ATOMS_E2E_OAUTH_CLIENT_SECRET",
"ATOMS_E2E_ORG_ID",
"BASECAMP3_CLIENT_ID",
"BASECAMP3_CLIENT_SECRET",
"BASECAMP3_USER_AGENT",
"BLACKLISTED_GUEST_EMAILS",
"AUTH_BEARER_TOKEN_VERCEL",
"BUILD_ID",
"CAL_SIGNATURE_TOKEN",
"CALCOM_PRIVATE_API_ROUTE",
"CALCOM_APP_CREDENTIAL_ENCRYPTION_KEY",
"CALCOM_CREDENTIAL_SYNC_ENDPOINT",
"CALCOM_CREDENTIAL_SYNC_SECRET",
"CALCOM_CREDENTIAL_SYNC_SECRET_HEADER_NAME",
"CALCOM_ENV",
"CALCOM_LICENSE_KEY",
"CALCOM_QA_EMAIL",
"CALCOM_QA_PASSWORD",
"CALCOM_TELEMETRY_DISABLED",
"CALCOM_CREDENTIAL_SYNC_HEADER_NAME",
"CALENDSO_ENCRYPTION_KEY",
"CALCOM_CREDENTIAL_SYNC_SECRET",
"CI",
"CLOSECOM_CLIENT_ID",
"CLOSECOM_CLIENT_SECRET",
"CRON_API_KEY",
"CRON_SECRET",
"CRON_ENABLE_APP_SYNC",
"CLOUDFLARE_TURNSTILE_SECRET",
"DAILY_API_KEY",
"DAILY_SCALE_PLAN",
"DAILY_WEBHOOK_SECRET",
"DAILY_MEETING_ENDED_WEBHOOK_SECRET",
"DAILY_VIDEO_REGION",
"DATABASE_CHUNK_SIZE",
"DATABASE_DIRECT_URL",
"DATABASE_URL",
"DEBUG",
"DUB_API_KEY",
"NEXT_PUBLIC_DUB_PROGRAM_ID",
"E2E_TEST_APPLE_CALENDAR_EMAIL",
"E2E_TEST_APPLE_CALENDAR_PASSWORD",
"E2E_TEST_CALCOM_QA_EMAIL",
"E2E_TEST_CALCOM_QA_PASSWORD",
"E2E_TEST_CALCOM_QA_GCAL_CREDENTIALS",
"E2E_TEST_CALCOM_GCAL_KEYS",
"E2E_TEST_MAILHOG_ENABLED",
"E2E_TEST_OIDC_CLIENT_ID",
"E2E_TEST_OIDC_CLIENT_SECRET",
"E2E_TEST_OIDC_PROVIDER_DOMAIN",
"E2E_TEST_OIDC_USER_EMAIL",
"E2E_TEST_OIDC_USER_PASSWORD",
"E2E_TEST_SAML_ADMIN_EMAIL",
"E2E_TEST_SAML_ADMIN_PASSWORD",
"EMAIL_FROM",
"EMAIL_FROM_NAME",
"EMAIL_SERVER_HOST",
"EMAIL_SERVER_PASSWORD",
"EMAIL_SERVER_PORT",
"EMAIL_SERVER_USER",
"EMAIL_SERVER",
"ENABLE_EXPANDED_CACHE",
"EXCHANGE_DEFAULT_EWS_URL",
"FORMBRICKS_FEEDBACK_SURVEY_ID",
"AVATARAPI_USERNAME",
"AVATARAPI_PASSWORD",
"GIPHY_API_KEY",
"GITHUB_API_REPO_TOKEN",
"GOOGLE_API_CREDENTIALS",
"GOOGLE_LOGIN_ENABLED",
"GOOGLE_WEBHOOK_TOKEN",
"HEROKU_APP_NAME",
"HUBSPOT_CLIENT_ID",
"HUBSPOT_CLIENT_SECRET",
"IFFY_API_KEY",
"INTEGRATION_TEST_MODE",
"IS_E2E",
"INTERCOM_SECRET",
"INTERCOM_SECRET",
"INSIGHTS_DATABASE_URL",
"IP_BANLIST",
"LARK_OPEN_APP_ID",
"LARK_OPEN_APP_SECRET",
"LARK_OPEN_VERIFICATION_TOKEN",
"MOCK_PAYMENT_APP_ENABLED",
"MS_GRAPH_CLIENT_ID",
"MS_GRAPH_CLIENT_SECRET",
"NEXT_PUBLIC_API_URL",
"NEXT_PUBLIC_APP_NAME",
"NEXT_PUBLIC_AUTH_URL",
"NEXT_PUBLIC_CALCOM_VERSION",
"NEXT_PUBLIC_COMPANY_NAME",
"NEXT_PUBLIC_CONSOLE_URL",
"NEXT_PUBLIC_LOGGER_LEVEL",
"NEXT_PUBLIC_DISABLE_SIGNUP",
"NEXT_PUBLIC_EMBED_LIB_URL",
"NEXT_PUBLIC_FORMBRICKS_HOST_URL",
"NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID",
"NEXT_PUBLIC_HOSTED_CAL_FEATURES",
"NEXT_PUBLIC_MINUTES_TO_BOOK",
"NEXT_PUBLIC_ORG_SELF_SERVE_ENABLED",
"NEXT_PUBLIC_SENDER_ID",
"NEXT_PUBLIC_SENDGRID_SENDER_NAME",
"NEXT_PUBLIC_STRIPE_PRICING_TABLE_ID",
"NEXT_PUBLIC_STRIPE_PRICING_TABLE_PUBLISHABLE_KEY",
"NEXT_PUBLIC_SUPPORT_MAIL_ADDRESS",
"NEXT_PUBLIC_SWAGGER_DOCS_URL",
"NEXT_PUBLIC_TEAM_IMPERSONATION",
"NEXT_PUBLIC_VERCEL_URL",
"NEXT_PUBLIC_CLOUDFLARE_SITEKEY",
"NEXT_PUBLIC_CLOUDFLARE_USE_TURNSTILE_IN_BOOKER",
"NEXT_PUBLIC_VERCEL_ENV",
"NEXT_PUBLIC_VERCEL_BRANCH_URL",
"NEXT_PUBLIC_GTM_ID",
"NEXT_PUBLIC_ORGANIZATIONS_MIN_SELF_SERVE_SEATS",
"NEXT_PUBLIC_ORGANIZATIONS_SELF_SERVE_PRICE_NEW",
"NEXT_PUBLIC_WEBSITE_PRIVACY_POLICY_URL",
"NEXT_PUBLIC_WEBSITE_TERMS_URL",
"NEXT_RUNTIME",
"NEXTAUTH_COOKIE_DOMAIN",
"NEXTAUTH_SECRET",
"NEXTAUTH_URL",
"NODE_ENV",
"ORGANIZATIONS_ENABLED",
"ORGANIZATIONS_AUTOLINK",
"PAYMENT_FEE_FIXED",
"PAYMENT_FEE_PERCENTAGE",
"PLAYWRIGHT_HEADLESS",
"PLAYWRIGHT_TEST_BASE_URL",
"PRISMA_FIELD_ENCRYPTION_KEY",
"PRISMA_GENERATE_DATAPROXY",
"PROJECT_ID_VERCEL",
"QUICK",
"RAILWAY_STATIC_URL",
"RENDER_EXTERNAL_URL",
"RESERVED_SUBDOMAINS",
"RETELL_AI_KEY",
"SALESFORCE_CONSUMER_KEY",
"SALESFORCE_CONSUMER_SECRET",
"SAML_ADMINS",
"SAML_CLIENT_SECRET_VERIFIER",
"SAML_DATABASE_URL",
"SEND_FEEDBACK_EMAIL",
"SENDGRID_API_KEY",
"SENDGRID_EMAIL",
"SENDGRID_SYNC_API_KEY",
"SKIP_DB_MIGRATIONS",
"SLACK_CLIENT_ID",
"SLACK_CLIENT_SECRET",
"SLACK_SIGNING_SECRET",
"STRIPE_CLIENT_ID",
"STRIPE_PRIVATE_KEY",
"STRIPE_PRODUCT_ID_ESSENTIALS",
"STRIPE_PRODUCT_ID_SCALE",
"STRIPE_PRODUCT_ID_STARTER",
"STRIPE_WEBHOOK_SECRET",
"STRIPE_WEBHOOK_SECRET_APPS",
"STRIPE_WEBHOOK_SECRET_BILLING",
"TANDEM_BASE_URL",
"TANDEM_CLIENT_ID",
"TANDEM_CLIENT_SECRET",
"TASKER_ENABLE_WEBHOOKS",
"TEAM_ID_VERCEL",
"TELEMETRY_DEBUG",
"TWILIO_MESSAGING_SID",
"TWILIO_OPT_OUT_ENABLED",
"TWILIO_PHONE_NUMBER",
"TWILIO_WHATSAPP_PHONE_NUMBER",
"TWILIO_SID",
"TWILIO_TOKEN",
"TWILIO_VERIFY_SID",
"TWILIO_WHATSAPP_REMINDER_CONTENT_SID",
"TWILIO_WHATSAPP_CANCELLED_CONTENT_SID",
"TWILIO_WHATSAPP_RESCHEDULED_CONTENT_SID",
"TWILIO_WHATSAPP_COMPLETED_CONTENT_SID",
"UPSTASH_REDIS_REST_TOKEN",
"UPSTASH_REDIS_REST_URL",
"UNKEY_ROOT_KEY",
"USERNAME_BLACKLIST_URL",
"VERCEL_ENV",
"VERCEL_URL",
"VITAL_API_KEY",
"VITAL_DEVELOPMENT_MODE",
"VITAL_REGION",
"VITAL_WEBHOOK_SECRET",
"ZAPIER_INVITE_LINK",
"ZOHOCRM_CLIENT_ID",
"ZOHOCRM_CLIENT_SECRET",
"ZOOM_CLIENT_ID",
"ZOOM_CLIENT_SECRET",
"REVERT_API_KEY",
"REVERT_API_URL",
"REVERT_PUBLIC_TOKEN",
"RESEND_API_KEY",
"LOCAL_TESTING_DOMAIN_VERCEL",
"AUTH_BEARER_TOKEN_CLOUDFLARE",
"CLOUDFLARE_ZONE_ID",
"NEXT_PUBLIC_MINTLIFY_CHAT_API_KEY",
"NEXT_PUBLIC_CHAT_API_URL",
"NEXT_PUBLIC_DOCS_URL",
"CLOUDFLARE_VERCEL_CNAME",
"CLOUDFLARE_DNS",
"EMBED_PUBLIC_EMBED_FINGER_PRINT",
"EMBED_PUBLIC_EMBED_VERSION",
"EMBED_PUBLIC_WEBAPP_URL",
"EMBED_PUBLIC_VERCEL_URL",
"EMBED_PUBLIC_EMBED_LIB_URL",
"NEXT_PUBLIC_ENABLE_PROFILE_SWITCHER",
"NEXT_PUBLIC_QUERY_AVAILABLE_SLOTS_INTERVAL_SECONDS",
"NEXT_PUBLIC_QUERY_RESERVATION_INTERVAL_SECONDS",
"NEXT_PUBLIC_QUERY_RESERVATION_STALE_TIME_SECONDS",
"NEXT_PUBLIC_INVALIDATE_AVAILABLE_SLOTS_ON_BOOKING_FORM",
"NEXT_PUBLIC_QUICK_AVAILABILITY_ROLLOUT",
"NEXT_PUBLIC_HEAD_SCRIPTS",
"NEXT_PUBLIC_BODY_SCRIPTS",
"NEXT_PUBLIC_ORG_SELF_SERVE_ENABLED",
"NEXT_PUBLIC_API_V2_ROOT_URL",
"NEXT_PUBLIC_VAPID_PUBLIC_KEY",
"VAPID_PRIVATE_KEY",
"CAL_VIDEO_BUCKET_NAME",
"CAL_VIDEO_BUCKET_REGION",
"CAL_VIDEO_ASSUME_ROLE_ARN",
"CAL_VIDEO_MEETING_LINK_FOR_TESTING",
"NEXT_PUBLIC_POSTHOG_KEY",
"NEXT_PUBLIC_POSTHOG_HOST",
"VAPID_PRIVATE_KEY",
"HUDDLE01_API_TOKEN",
"LINGO_DOT_DEV_API_KEY",
"DIRECTORY_IDS_TO_LOG",
"NEXT_PUBLIC_SINGLE_ORG_SLUG",
"GOOGLE_REFRESH_TOKEN",
"GOOGLE_CLIENT_ID",
"GOOGLE_CLIENT_SECRET",
"ZOOM_REFRESH_TOKEN",
"CALCOM_ADMIN_API_KEY",
"NEXT_PUBLIC_SINGLE_ORG_MODE_ENABLED",
"CALCOM_SERVICE_ACCOUNT_ENCRYPTION_KEY",
"CAL_VIDEO_RECORDING_TOKEN_SECRET",
"ORGANIZER_EMAIL_EXEMPT_DOMAINS",
"SLOTS_CACHE_TTL"
]
}
}
+34 -34
View File
@@ -2500,7 +2500,7 @@ __metadata:
"@axiomhq/winston": ^1.2.0
"@calcom/platform-constants": "*"
"@calcom/platform-enums": "*"
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.312"
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.314"
"@calcom/platform-types": "*"
"@calcom/platform-utils": "*"
"@calcom/prisma": "*"
@@ -3559,13 +3559,13 @@ __metadata:
languageName: unknown
linkType: soft
"@calcom/platform-libraries@npm:@calcom/platform-libraries@0.0.312":
version: 0.0.312
resolution: "@calcom/platform-libraries@npm:0.0.312"
"@calcom/platform-libraries@npm:@calcom/platform-libraries@0.0.314":
version: 0.0.314
resolution: "@calcom/platform-libraries@npm:0.0.314"
dependencies:
"@calcom/features": "*"
"@calcom/lib": "*"
checksum: 46520737404a61f3aa96c6f1adfa81e67f645fd984f495662ca7c63184b0310117a1dad57ddbe7ea487f2e5a0c9cbe62749a9a43b3197a021b19e3ccadc1f5b2
checksum: b59eb591273334ed7d7d07a8f0518eb8cb40255883dff49626f85d3bd804db4da357bf77cd4f7b14ae0743d0a699647232b39956af9123bdc5e7b2458cab30eb
languageName: node
linkType: hard
@@ -21694,7 +21694,7 @@ __metadata:
prismock: ^1.33.4
resize-observer-polyfill: ^1.5.1
tsc-absolute: ^1.0.0
turbo: ^1.10.1
turbo: ^2.5.5
typescript: 5.9.0-beta
vitest: ^2.1.1
vitest-fetch-mock: ^0.3.0
@@ -44926,58 +44926,58 @@ __metadata:
languageName: node
linkType: hard
"turbo-darwin-64@npm:1.10.1":
version: 1.10.1
resolution: "turbo-darwin-64@npm:1.10.1"
"turbo-darwin-64@npm:2.5.5":
version: 2.5.5
resolution: "turbo-darwin-64@npm:2.5.5"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"turbo-darwin-arm64@npm:1.10.1":
version: 1.10.1
resolution: "turbo-darwin-arm64@npm:1.10.1"
"turbo-darwin-arm64@npm:2.5.5":
version: 2.5.5
resolution: "turbo-darwin-arm64@npm:2.5.5"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"turbo-linux-64@npm:1.10.1":
version: 1.10.1
resolution: "turbo-linux-64@npm:1.10.1"
"turbo-linux-64@npm:2.5.5":
version: 2.5.5
resolution: "turbo-linux-64@npm:2.5.5"
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
"turbo-linux-arm64@npm:1.10.1":
version: 1.10.1
resolution: "turbo-linux-arm64@npm:1.10.1"
"turbo-linux-arm64@npm:2.5.5":
version: 2.5.5
resolution: "turbo-linux-arm64@npm:2.5.5"
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
"turbo-windows-64@npm:1.10.1":
version: 1.10.1
resolution: "turbo-windows-64@npm:1.10.1"
"turbo-windows-64@npm:2.5.5":
version: 2.5.5
resolution: "turbo-windows-64@npm:2.5.5"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"turbo-windows-arm64@npm:1.10.1":
version: 1.10.1
resolution: "turbo-windows-arm64@npm:1.10.1"
"turbo-windows-arm64@npm:2.5.5":
version: 2.5.5
resolution: "turbo-windows-arm64@npm:2.5.5"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"turbo@npm:^1.10.1":
version: 1.10.1
resolution: "turbo@npm:1.10.1"
"turbo@npm:^2.5.5":
version: 2.5.5
resolution: "turbo@npm:2.5.5"
dependencies:
turbo-darwin-64: 1.10.1
turbo-darwin-arm64: 1.10.1
turbo-linux-64: 1.10.1
turbo-linux-arm64: 1.10.1
turbo-windows-64: 1.10.1
turbo-windows-arm64: 1.10.1
turbo-darwin-64: 2.5.5
turbo-darwin-arm64: 2.5.5
turbo-linux-64: 2.5.5
turbo-linux-arm64: 2.5.5
turbo-windows-64: 2.5.5
turbo-windows-arm64: 2.5.5
dependenciesMeta:
turbo-darwin-64:
optional: true
@@ -44993,7 +44993,7 @@ __metadata:
optional: true
bin:
turbo: bin/turbo
checksum: 3551c57be67e833583de549d74d5ec991e0f1e38047e25600b8acc085a1a8a4007992259a015eb9f2c0abd68156545800bd34e7b5ad337d1998b32461952d3fb
checksum: 8da6525097ba17bb59571eb512f216362b046bbf88765ddb05fb9937a44e04eeba3bb883c55fca53a7e8a104e02366b4f6f99ef90d12078d0dc95d4659004af6
languageName: node
linkType: hard