chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
@@ -0,0 +1,33 @@
import { z } from 'zod'
import {
credentialWorkflowBodySchema,
definePostSelector,
idNameSchema,
} from '@/lib/api/contracts/selectors/shared'
import type { ContractJsonResponse } from '@/lib/api/contracts/types'
const airtableBaseSchema = idNameSchema
const airtableTableSchema = idNameSchema
export const airtableTablesBodySchema = credentialWorkflowBodySchema.extend({
baseId: z.string().min(1, 'Base ID is required'),
})
export const airtableBasesSelectorContract = definePostSelector(
'/api/tools/airtable/bases',
credentialWorkflowBodySchema.passthrough(),
z.object({ bases: z.array(airtableBaseSchema) })
)
export const airtableTablesSelectorContract = definePostSelector(
'/api/tools/airtable/tables',
airtableTablesBodySchema,
z.object({ tables: z.array(airtableTableSchema) })
)
export type AirtableBasesSelectorResponse = ContractJsonResponse<
typeof airtableBasesSelectorContract
>
export type AirtableTablesSelectorResponse = ContractJsonResponse<
typeof airtableTablesSelectorContract
>
@@ -0,0 +1,19 @@
import { z } from 'zod'
import {
credentialWorkflowBodySchema,
definePostSelector,
idNameSchema,
} from '@/lib/api/contracts/selectors/shared'
import type { ContractJsonResponse } from '@/lib/api/contracts/types'
const asanaWorkspaceSchema = idNameSchema
export const asanaWorkspacesSelectorContract = definePostSelector(
'/api/tools/asana/workspaces',
credentialWorkflowBodySchema,
z.object({ workspaces: z.array(asanaWorkspaceSchema) })
)
export type AsanaWorkspacesSelectorResponse = ContractJsonResponse<
typeof asanaWorkspacesSelectorContract
>
@@ -0,0 +1,25 @@
import { z } from 'zod'
import {
credentialWorkflowBodySchema,
definePostSelector,
idNameSchema,
} from '@/lib/api/contracts/selectors/shared'
import type { ContractJsonResponse } from '@/lib/api/contracts/types'
const attioObjectSchema = idNameSchema
const attioListSchema = idNameSchema
export const attioObjectsSelectorContract = definePostSelector(
'/api/tools/attio/objects',
credentialWorkflowBodySchema,
z.object({ objects: z.array(attioObjectSchema) })
)
export const attioListsSelectorContract = definePostSelector(
'/api/tools/attio/lists',
credentialWorkflowBodySchema,
z.object({ lists: z.array(attioListSchema) })
)
export type AttioObjectsSelectorResponse = ContractJsonResponse<typeof attioObjectsSelectorContract>
export type AttioListsSelectorResponse = ContractJsonResponse<typeof attioListsSelectorContract>
@@ -0,0 +1,57 @@
import { z } from 'zod'
import {
credentialWorkflowImpersonateBodySchema,
definePostSelector,
} from '@/lib/api/contracts/selectors/shared'
import type { ContractBodyInput, ContractJsonResponse } from '@/lib/api/contracts/types'
const bigQueryDatasetSchema = z
.object({
datasetReference: z
.object({
datasetId: z.string(),
projectId: z.string(),
})
.passthrough(),
friendlyName: z.string().optional(),
})
.passthrough()
const bigQueryTableSchema = z
.object({
tableReference: z.object({ tableId: z.string() }).passthrough(),
friendlyName: z.string().optional(),
})
.passthrough()
export const bigQueryDatasetsBodySchema = credentialWorkflowImpersonateBodySchema.extend({
projectId: z.string().min(1),
})
export const bigQueryTablesBodySchema = bigQueryDatasetsBodySchema.extend({
datasetId: z.string().min(1),
})
export const bigQueryDatasetsSelectorContract = definePostSelector(
'/api/tools/google_bigquery/datasets',
bigQueryDatasetsBodySchema,
z.object({ datasets: z.array(bigQueryDatasetSchema) })
)
export const bigQueryTablesSelectorContract = definePostSelector(
'/api/tools/google_bigquery/tables',
bigQueryTablesBodySchema,
z.object({ tables: z.array(bigQueryTableSchema) })
)
export type BigQueryDatasetsSelectorBody = ContractBodyInput<
typeof bigQueryDatasetsSelectorContract
>
export type BigQueryTablesSelectorBody = ContractBodyInput<typeof bigQueryTablesSelectorContract>
export type BigQueryDatasetsSelectorResponse = ContractJsonResponse<
typeof bigQueryDatasetsSelectorContract
>
export type BigQueryTablesSelectorResponse = ContractJsonResponse<
typeof bigQueryTablesSelectorContract
>
@@ -0,0 +1,30 @@
import { z } from 'zod'
import {
credentialWorkflowBodySchema,
definePostSelector,
idNameSchema,
} from '@/lib/api/contracts/selectors/shared'
import type { ContractJsonResponse } from '@/lib/api/contracts/types'
const calcomEventTypeSchema = z
.object({ id: z.string(), title: z.string(), slug: z.string() })
.passthrough()
export const calcomEventTypesSelectorContract = definePostSelector(
'/api/tools/calcom/event-types',
credentialWorkflowBodySchema,
z.object({ eventTypes: z.array(calcomEventTypeSchema) })
)
export const calcomSchedulesSelectorContract = definePostSelector(
'/api/tools/calcom/schedules',
credentialWorkflowBodySchema,
z.object({ schedules: z.array(idNameSchema) })
)
export type CalcomEventTypesSelectorResponse = ContractJsonResponse<
typeof calcomEventTypesSelectorContract
>
export type CalcomSchedulesSelectorResponse = ContractJsonResponse<
typeof calcomSchedulesSelectorContract
>
@@ -0,0 +1,84 @@
import { z } from 'zod'
import { definePostSelector, optionalString } from '@/lib/api/contracts/selectors/shared'
import type {
ContractBody,
ContractBodyInput,
ContractJsonResponse,
} from '@/lib/api/contracts/types'
import { validateAwsRegion } from '@/lib/core/security/input-validation'
const cloudwatchLogGroupSchema = z.object({ logGroupName: z.string() }).passthrough()
const cloudwatchLogStreamSchema = z.object({ logStreamName: z.string() }).passthrough()
/**
* AWS region with format validation. Matches the route-level check via
* `validateAwsRegion` (e.g. `us-east-1`, `eu-west-2`).
*/
const awsRegionSchema = z
.string()
.min(1, 'AWS region is required')
.refine((value) => validateAwsRegion(value).isValid, {
message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)',
})
/**
* Optional integer limit that accepts numbers, numeric strings, empty strings,
* and null. Empty/null/undefined → undefined (no limit).
*/
const optionalLimitSchema = z.preprocess(
(value) => (value === '' || value === undefined || value === null ? undefined : value),
z.coerce.number().int().positive().optional()
)
export const cloudwatchLogGroupsBodySchema = z.object({
accessKeyId: z.string().min(1, 'AWS access key ID is required'),
secretAccessKey: z.string().min(1, 'AWS secret access key is required'),
region: awsRegionSchema,
prefix: optionalString,
limit: optionalLimitSchema.optional(),
})
export const cloudwatchLogStreamsBodySchema = cloudwatchLogGroupsBodySchema.extend({
logGroupName: z.string().min(1, 'Log group name is required'),
})
export const cloudwatchLogGroupsSelectorContract = definePostSelector(
'/api/tools/cloudwatch/describe-log-groups',
cloudwatchLogGroupsBodySchema,
z
.object({
success: z.boolean().optional(),
output: z.object({ logGroups: z.array(cloudwatchLogGroupSchema) }),
})
.passthrough()
)
export const cloudwatchLogStreamsSelectorContract = definePostSelector(
'/api/tools/cloudwatch/describe-log-streams',
cloudwatchLogStreamsBodySchema,
z
.object({
success: z.boolean().optional(),
output: z.object({ logStreams: z.array(cloudwatchLogStreamSchema) }),
})
.passthrough()
)
export type CloudwatchLogGroupsSelectorResponse = ContractJsonResponse<
typeof cloudwatchLogGroupsSelectorContract
>
export type CloudwatchLogStreamsSelectorResponse = ContractJsonResponse<
typeof cloudwatchLogStreamsSelectorContract
>
export type CloudwatchLogGroupsSelectorRequest = ContractBodyInput<
typeof cloudwatchLogGroupsSelectorContract
>
export type CloudwatchLogGroupsSelectorBody = ContractBody<
typeof cloudwatchLogGroupsSelectorContract
>
export type CloudwatchLogStreamsSelectorRequest = ContractBodyInput<
typeof cloudwatchLogStreamsSelectorContract
>
export type CloudwatchLogStreamsSelectorBody = ContractBody<
typeof cloudwatchLogStreamsSelectorContract
>
@@ -0,0 +1,613 @@
import { z } from 'zod'
import {
credentialWorkflowDomainBodySchema,
definePostSelector,
fileOptionSchema,
optionalString,
} from '@/lib/api/contracts/selectors/shared'
import type { ContractBody, ContractJsonResponse, ContractQuery } from '@/lib/api/contracts/types'
import { defineRouteContract } from '@/lib/api/contracts/types'
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
const confluenceSpaceSchema = z
.object({
id: z.string(),
name: z.string(),
key: z.string(),
status: z.string().optional(),
})
.passthrough()
export const confluencePagesBodySchema = z.object({
domain: z.string().min(1, 'Domain is required'),
accessToken: z.string().min(1, 'Access token is required'),
cloudId: optionalString,
title: optionalString,
limit: z.number().int().positive().optional().default(50),
})
/**
* Refines a `pageId` field to match Confluence's alphanumeric format
* (max 255 chars). Used as a `superRefine` so multiple
* methods (POST/PUT/DELETE) on `/api/tools/confluence/page` can share it.
*/
export function refineConfluencePageId(data: { pageId: string }, ctx: z.RefinementCtx): void {
const validation = validateAlphanumericId(data.pageId, 'pageId', 255)
if (!validation.isValid) {
ctx.addIssue({
code: 'custom',
message: validation.error || 'Invalid page ID',
path: ['pageId'],
})
}
}
export const confluencePageBaseSchema = z.object({
domain: z.string().min(1, 'Domain is required'),
accessToken: z.string().min(1, 'Access token is required'),
cloudId: optionalString,
pageId: z.string().min(1, 'Page ID is required'),
})
export const confluencePageBodySchema = confluencePageBaseSchema.superRefine(refineConfluencePageId)
/** Body schema for `PUT /api/tools/confluence/page`. */
export const confluenceUpdatePageBodySchema = confluencePageBaseSchema
.extend({
title: optionalString,
body: z.object({ value: optionalString }).optional(),
version: z.object({ message: optionalString }).optional(),
})
.superRefine(refineConfluencePageId)
/** Body schema for `DELETE /api/tools/confluence/page`. */
export const confluenceDeletePageBodySchema = confluencePageBaseSchema
.extend({
purge: z.boolean().optional(),
})
.superRefine(refineConfluencePageId)
const confluenceBaseSchema = z.object({
domain: z.string({ error: 'Domain is required' }).min(1, 'Domain is required'),
accessToken: z.string({ error: 'Access token is required' }).min(1, 'Access token is required'),
cloudId: z.string().optional(),
})
const confluencePageScopedSchema = confluenceBaseSchema.extend({
pageId: z.string({ error: 'Page ID is required' }).min(1, 'Page ID is required'),
})
export const confluenceSpaceScopedSchema = confluenceBaseSchema.extend({
spaceId: z.string({ error: 'Space ID is required' }).min(1, 'Space ID is required'),
})
function addAlphanumericIdIssue(
data: Record<string, unknown>,
field: string,
label: string,
ctx: z.RefinementCtx
): void {
const value = data[field]
if (typeof value !== 'string') return
const validation = validateAlphanumericId(value, field, 255)
if (!validation.isValid) {
ctx.addIssue({
code: 'custom',
message: validation.error || `Invalid ${label}`,
path: [field],
})
}
}
// Keep the un-superRefined base separate so downstream schemas can .extend it.
// .superRefine returns a ZodEffects which has no .extend method, so extending
// the refined schema directly throws at module-init time (caught by bundlers
// like esbuild/Trigger.dev that eagerly evaluate; Next.js lazy-loads per-route
// and hides the issue).
const confluenceCommentScopedBaseSchema = confluenceBaseSchema.extend({
commentId: z.string().min(1, 'Comment ID is required'),
})
export const confluenceCommentScopedSchema = confluenceCommentScopedBaseSchema.superRefine(
(data, ctx) => addAlphanumericIdIssue(data, 'commentId', 'comment ID', ctx)
)
const confluenceBlogPostScopedBaseSchema = confluenceBaseSchema.extend({
blogPostId: z.string({ error: 'Blog post ID is required' }).min(1, 'Blog post ID is required'),
})
export const confluenceBlogPostScopedSchema = confluenceBlogPostScopedBaseSchema.superRefine(
(data, ctx) => addAlphanumericIdIssue(data, 'blogPostId', 'blog post ID', ctx)
)
export const confluenceDeleteAttachmentBodySchema = confluenceBaseSchema.extend({
attachmentId: z
.string({ error: 'Attachment ID is required' })
.min(1, 'Attachment ID is required'),
})
export const confluenceListAttachmentsQuerySchema = confluencePageScopedSchema.extend({
limit: z.string().optional().default('50'),
cursor: z.string().optional(),
})
export const confluenceCreateCommentBodySchema = confluencePageScopedSchema.extend({
comment: z.string({ error: 'Comment is required' }).min(1, 'Comment is required'),
})
export const confluenceListCommentsQuerySchema = confluencePageScopedSchema.extend({
limit: z.string().optional().default('25'),
bodyFormat: z.string().optional().default('storage'),
cursor: z.string().optional(),
})
export const confluenceUpdateCommentBodySchema = confluenceCommentScopedBaseSchema
.extend({
comment: z.string().min(1, 'Comment is required'),
})
.superRefine((data, ctx) => addAlphanumericIdIssue(data, 'commentId', 'comment ID', ctx))
export const confluenceCreatePageBodySchema = confluenceSpaceScopedSchema.extend({
title: z.string({ error: 'Title is required' }).min(1, 'Title is required'),
content: z.string({ error: 'Content is required' }).min(1, 'Content is required'),
parentId: z.string().optional(),
})
export const confluenceLabelMutationBodySchema = confluencePageScopedSchema.extend({
labelName: z.string({ error: 'Label name is required' }).min(1, 'Label name is required'),
prefix: z.string().optional(),
})
export const confluenceListLabelsQuerySchema = confluencePageScopedSchema.extend({
limit: z.string().optional().default('25'),
cursor: z.string().optional(),
})
export const confluenceListPagePropertiesQuerySchema = confluencePageScopedSchema.extend({
limit: z.string().optional().default('50'),
cursor: z.string().optional(),
})
export const confluenceCreatePagePropertyBodySchema = confluencePageScopedSchema.extend({
key: z.string({ error: 'Property key is required' }).min(1, 'Property key is required'),
value: z.unknown(),
})
export const confluenceUpdatePagePropertyBodySchema = confluenceCreatePagePropertyBodySchema.extend(
{
propertyId: z.string({ error: 'Property ID is required' }).min(1, 'Property ID is required'),
versionNumber: z.number().min(1).optional(),
}
)
export const confluenceDeletePagePropertyBodySchema = confluencePageScopedSchema.extend({
propertyId: z.string({ error: 'Property ID is required' }).min(1, 'Property ID is required'),
})
export const confluenceGetSpaceQuerySchema = confluenceSpaceScopedSchema
export const confluenceCreateSpaceBodySchema = confluenceBaseSchema.extend({
name: z.string({ error: 'Space name is required' }).min(1, 'Space name is required'),
key: z.string({ error: 'Space key is required' }).min(1, 'Space key is required'),
description: z.string().optional(),
})
export const confluenceUpdateSpaceBodySchema = confluenceSpaceScopedSchema.extend({
name: z.string().optional(),
description: z.string().optional(),
})
export const confluencePageChildrenBodySchema = confluencePageScopedSchema.extend({
limit: z.number().optional().default(50),
cursor: z.string().optional(),
})
export const confluencePageAncestorsBodySchema = confluencePageScopedSchema.extend({
limit: z.number().optional().default(25),
})
export const confluencePageVersionsBodySchema = confluencePageScopedSchema.extend({
versionNumber: z.union([z.string(), z.number()]).optional(),
limit: z.number().optional().default(50),
cursor: z.string().optional(),
})
export const confluencePagesByLabelQuerySchema = confluenceBaseSchema.extend({
labelId: z.string({ error: 'Label ID is required' }).min(1, 'Label ID is required'),
limit: z.string().optional().default('50'),
cursor: z.string().optional(),
})
export const confluenceSearchBodySchema = confluenceBaseSchema.extend({
query: z.string({ error: 'Search query is required' }).min(1, 'Search query is required'),
limit: z.number().optional().default(25),
})
export const confluenceSearchInSpaceBodySchema = confluenceBaseSchema.extend({
spaceKey: z.string({ error: 'Space key is required' }).min(1, 'Space key is required'),
query: z.string().optional(),
limit: z.number().optional().default(25),
contentType: z.string().optional(),
})
export const confluenceSpaceBlogPostsBodySchema = confluenceSpaceScopedSchema.extend({
limit: z.number().optional().default(25),
status: z.string().optional(),
bodyFormat: z.string().optional(),
cursor: z.string().optional(),
})
export const confluenceSpaceLabelsQuerySchema = confluenceSpaceScopedSchema.extend({
limit: z.string().optional().default('25'),
cursor: z.string().optional(),
})
export const confluenceSpacePagesBodySchema = confluenceSpaceScopedSchema.extend({
limit: z.number().optional().default(50),
status: z.string().optional(),
bodyFormat: z.string().optional(),
cursor: z.string().optional(),
})
export const confluenceSpacePermissionsBodySchema = confluenceSpaceScopedSchema.extend({
limit: z.number().optional().default(50),
cursor: z.string().optional(),
})
export const confluenceSpacePropertiesBodySchema = confluenceSpaceScopedSchema.extend({
action: z.string().optional(),
key: z.string().optional(),
value: z.unknown().optional(),
propertyId: z.string().optional(),
limit: z.number().optional().default(50),
cursor: z.string().optional(),
})
export const confluenceListSpacesQuerySchema = confluenceBaseSchema.extend({
limit: z.string().optional().default('25'),
cursor: z.string().optional(),
})
export const confluenceTasksBodySchema = confluenceBaseSchema.extend({
action: z.string().optional(),
taskId: z.string().optional(),
status: z.string().optional(),
pageId: z.string().optional(),
spaceId: z.string().optional(),
assignedTo: z.string().optional(),
limit: z.number().optional().default(50),
cursor: z.string().optional(),
})
export const confluenceUploadAttachmentBodySchema = confluencePageScopedSchema.extend({
file: z.unknown().refine((value) => Boolean(value), { message: 'File is required' }),
fileName: z.string().optional(),
comment: z.string().optional(),
})
export const confluenceUserBodySchema = confluenceBaseSchema.extend({
accountId: z.string({ error: 'Account ID is required' }).min(1, 'Account ID is required'),
})
export const confluenceGetBlogPostBodySchema = confluenceBlogPostScopedBaseSchema
.extend({
bodyFormat: z.string().optional(),
})
.superRefine((data, ctx) => addAlphanumericIdIssue(data, 'blogPostId', 'blog post ID', ctx))
export const confluenceCreateBlogPostBodySchema = confluenceSpaceScopedSchema.extend({
title: z.string({ error: 'Title is required' }).min(1, 'Title is required'),
content: z.string({ error: 'Content is required' }).min(1, 'Content is required'),
status: z.enum(['current', 'draft']).optional(),
})
export const confluenceBlogPostOperationBodySchema = z.union([
confluenceCreateBlogPostBodySchema,
confluenceGetBlogPostBodySchema,
])
export const confluenceListBlogPostsQuerySchema = confluenceBaseSchema.extend({
limit: z.string().optional().default('25'),
status: z.string().optional(),
sort: z.string().optional(),
cursor: z.string().optional(),
})
export const confluenceUpdateBlogPostBodySchema = confluenceBlogPostScopedBaseSchema
.extend({
title: z.string().optional(),
content: z.string().optional(),
})
.superRefine((data, ctx) => addAlphanumericIdIssue(data, 'blogPostId', 'blog post ID', ctx))
const defineConfluencePostContract = <TBody extends z.ZodType>(path: string, body: TBody) =>
defineRouteContract({
method: 'POST',
path,
body,
response: {
mode: 'json',
// untyped-response: shared helper for ~16 confluence POST routes, each forwarding a different Atlassian Confluence v2 payload (pages, comments, labels, blogposts, page properties, search, etc.) whose shapes diverge per resource and are version-dependent
schema: z.unknown(),
},
})
const defineConfluencePutContract = <TBody extends z.ZodType>(path: string, body: TBody) =>
defineRouteContract({
method: 'PUT',
path,
body,
response: {
mode: 'json',
// untyped-response: shared helper for confluence PUT routes (page, comment, blogpost, space, page-properties) that proxy raw Atlassian Confluence v2 update responses whose shape varies per resource
schema: z.unknown(),
},
})
const defineConfluenceDeleteContract = <TBody extends z.ZodType>(path: string, body: TBody) =>
defineRouteContract({
method: 'DELETE',
path,
body,
response: {
mode: 'json',
// untyped-response: shared helper for confluence DELETE routes returning either a normalized deleted marker, an empty body, or a forwarded Atlassian Confluence v2 response depending on the resource
schema: z.unknown(),
},
})
const defineConfluenceGetContract = <TQuery extends z.ZodType>(path: string, query: TQuery) =>
defineRouteContract({
method: 'GET',
path,
query,
response: {
mode: 'json',
// untyped-response: shared helper for confluence GET listing routes (attachments, blogposts, comments, labels, page-properties, space, spaces, space-labels, pages-by-label) each returning a different paginated Atlassian Confluence v2 shape
schema: z.unknown(),
},
})
export const confluenceSpacesSelectorBodySchema = credentialWorkflowDomainBodySchema.extend({
cursor: optionalString,
})
export const confluenceSpacesSelectorContract = definePostSelector(
'/api/tools/confluence/selector-spaces',
confluenceSpacesSelectorBodySchema,
z.object({
spaces: z.array(confluenceSpaceSchema),
nextCursor: optionalString,
})
)
export const confluencePagesSelectorContract = definePostSelector(
'/api/tools/confluence/pages',
confluencePagesBodySchema,
z.object({ files: z.array(fileOptionSchema) })
)
export const confluencePageSelectorContract = definePostSelector(
'/api/tools/confluence/page',
confluencePageBodySchema,
z.object({ id: z.string(), title: z.string() }).passthrough()
)
export const confluenceUpdatePageContract = defineConfluencePutContract(
'/api/tools/confluence/page',
confluenceUpdatePageBodySchema
)
export const confluenceDeletePageContract = defineConfluenceDeleteContract(
'/api/tools/confluence/page',
confluenceDeletePageBodySchema
)
export const confluenceDeleteAttachmentContract = defineConfluenceDeleteContract(
'/api/tools/confluence/attachment',
confluenceDeleteAttachmentBodySchema
)
export const confluenceListAttachmentsContract = defineConfluenceGetContract(
'/api/tools/confluence/attachments',
confluenceListAttachmentsQuerySchema
)
export const confluenceListBlogPostsContract = defineConfluenceGetContract(
'/api/tools/confluence/blogposts',
confluenceListBlogPostsQuerySchema
)
export const confluenceBlogPostOperationContract = defineConfluencePostContract(
'/api/tools/confluence/blogposts',
confluenceBlogPostOperationBodySchema
)
export const confluenceUpdateBlogPostContract = defineConfluencePutContract(
'/api/tools/confluence/blogposts',
confluenceUpdateBlogPostBodySchema
)
export const confluenceDeleteBlogPostContract = defineConfluenceDeleteContract(
'/api/tools/confluence/blogposts',
confluenceBlogPostScopedSchema
)
export const confluenceCreateCommentContract = defineConfluencePostContract(
'/api/tools/confluence/comments',
confluenceCreateCommentBodySchema
)
export const confluenceListCommentsContract = defineConfluenceGetContract(
'/api/tools/confluence/comments',
confluenceListCommentsQuerySchema
)
export const confluenceUpdateCommentContract = defineConfluencePutContract(
'/api/tools/confluence/comment',
confluenceUpdateCommentBodySchema
)
export const confluenceDeleteCommentContract = defineConfluenceDeleteContract(
'/api/tools/confluence/comment',
confluenceCommentScopedSchema
)
export const confluenceCreatePageContract = defineConfluencePostContract(
'/api/tools/confluence/create-page',
confluenceCreatePageBodySchema
)
export const confluenceLabelMutationContract = defineConfluencePostContract(
'/api/tools/confluence/labels',
confluenceLabelMutationBodySchema
)
export const confluenceListLabelsContract = defineConfluenceGetContract(
'/api/tools/confluence/labels',
confluenceListLabelsQuerySchema
)
export const confluenceDeleteLabelContract = defineConfluenceDeleteContract(
'/api/tools/confluence/labels',
confluenceLabelMutationBodySchema
)
export const confluenceListPagePropertiesContract = defineConfluenceGetContract(
'/api/tools/confluence/page-properties',
confluenceListPagePropertiesQuerySchema
)
export const confluenceCreatePagePropertyContract = defineConfluencePostContract(
'/api/tools/confluence/page-properties',
confluenceCreatePagePropertyBodySchema
)
export const confluenceUpdatePagePropertyContract = defineConfluencePutContract(
'/api/tools/confluence/page-properties',
confluenceUpdatePagePropertyBodySchema
)
export const confluenceDeletePagePropertyContract = defineConfluenceDeleteContract(
'/api/tools/confluence/page-properties',
confluenceDeletePagePropertyBodySchema
)
export const confluenceGetSpaceContract = defineConfluenceGetContract(
'/api/tools/confluence/space',
confluenceGetSpaceQuerySchema
)
export const confluenceCreateSpaceContract = defineConfluencePostContract(
'/api/tools/confluence/space',
confluenceCreateSpaceBodySchema
)
export const confluenceUpdateSpaceContract = defineConfluencePutContract(
'/api/tools/confluence/space',
confluenceUpdateSpaceBodySchema
)
export const confluenceDeleteSpaceContract = defineConfluenceDeleteContract(
'/api/tools/confluence/space',
confluenceSpaceScopedSchema
)
export const confluencePageChildrenContract = defineConfluencePostContract(
'/api/tools/confluence/page-children',
confluencePageChildrenBodySchema
)
export const confluencePageDescendantsContract = defineConfluencePostContract(
'/api/tools/confluence/page-descendants',
confluencePageChildrenBodySchema
)
export const confluencePageAncestorsContract = defineConfluencePostContract(
'/api/tools/confluence/page-ancestors',
confluencePageAncestorsBodySchema
)
export const confluencePageVersionsContract = defineConfluencePostContract(
'/api/tools/confluence/page-versions',
confluencePageVersionsBodySchema
)
export const confluencePagesByLabelContract = defineConfluenceGetContract(
'/api/tools/confluence/pages-by-label',
confluencePagesByLabelQuerySchema
)
export const confluenceSearchContract = defineConfluencePostContract(
'/api/tools/confluence/search',
confluenceSearchBodySchema
)
export const confluenceSearchInSpaceContract = defineConfluencePostContract(
'/api/tools/confluence/search-in-space',
confluenceSearchInSpaceBodySchema
)
export const confluenceSpaceBlogPostsContract = defineConfluencePostContract(
'/api/tools/confluence/space-blogposts',
confluenceSpaceBlogPostsBodySchema
)
export const confluenceSpaceLabelsContract = defineConfluenceGetContract(
'/api/tools/confluence/space-labels',
confluenceSpaceLabelsQuerySchema
)
export const confluenceSpacePagesContract = defineConfluencePostContract(
'/api/tools/confluence/space-pages',
confluenceSpacePagesBodySchema
)
export const confluenceSpacePermissionsContract = defineConfluencePostContract(
'/api/tools/confluence/space-permissions',
confluenceSpacePermissionsBodySchema
)
export const confluenceSpacePropertiesContract = defineConfluencePostContract(
'/api/tools/confluence/space-properties',
confluenceSpacePropertiesBodySchema
)
export const confluenceListSpacesContract = defineConfluenceGetContract(
'/api/tools/confluence/spaces',
confluenceListSpacesQuerySchema
)
export const confluenceTasksContract = defineConfluencePostContract(
'/api/tools/confluence/tasks',
confluenceTasksBodySchema
)
export const confluenceUploadAttachmentContract = defineConfluencePostContract(
'/api/tools/confluence/upload-attachment',
confluenceUploadAttachmentBodySchema
)
export const confluenceUserContract = defineConfluencePostContract(
'/api/tools/confluence/user',
confluenceUserBodySchema
)
export type ConfluencePagesBody = ContractBody<typeof confluencePagesSelectorContract>
export type ConfluencePageBody = ContractBody<typeof confluencePageSelectorContract>
export type ConfluenceUpdatePageBody = ContractBody<typeof confluenceUpdatePageContract>
export type ConfluenceDeletePageBody = ContractBody<typeof confluenceDeletePageContract>
export type ConfluenceDeleteAttachmentBody = ContractBody<typeof confluenceDeleteAttachmentContract>
export type ConfluenceListAttachmentsQuery = ContractQuery<typeof confluenceListAttachmentsContract>
export type ConfluenceListBlogPostsQuery = ContractQuery<typeof confluenceListBlogPostsContract>
export type ConfluenceBlogPostOperationBody = ContractBody<
typeof confluenceBlogPostOperationContract
>
export type ConfluenceUpdateBlogPostBody = ContractBody<typeof confluenceUpdateBlogPostContract>
export type ConfluenceDeleteBlogPostBody = ContractBody<typeof confluenceDeleteBlogPostContract>
export type ConfluenceCreateCommentBody = ContractBody<typeof confluenceCreateCommentContract>
export type ConfluenceListCommentsQuery = ContractQuery<typeof confluenceListCommentsContract>
export type ConfluenceUpdateCommentBody = ContractBody<typeof confluenceUpdateCommentContract>
export type ConfluenceDeleteCommentBody = ContractBody<typeof confluenceDeleteCommentContract>
export type ConfluenceCreatePageBody = ContractBody<typeof confluenceCreatePageContract>
export type ConfluenceLabelMutationBody = ContractBody<typeof confluenceLabelMutationContract>
export type ConfluenceListLabelsQuery = ContractQuery<typeof confluenceListLabelsContract>
export type ConfluenceDeleteLabelBody = ContractBody<typeof confluenceDeleteLabelContract>
export type ConfluenceListPagePropertiesQuery = ContractQuery<
typeof confluenceListPagePropertiesContract
>
export type ConfluenceCreatePagePropertyBody = ContractBody<
typeof confluenceCreatePagePropertyContract
>
export type ConfluenceUpdatePagePropertyBody = ContractBody<
typeof confluenceUpdatePagePropertyContract
>
export type ConfluenceDeletePagePropertyBody = ContractBody<
typeof confluenceDeletePagePropertyContract
>
export type ConfluenceGetSpaceQuery = ContractQuery<typeof confluenceGetSpaceContract>
export type ConfluenceCreateSpaceBody = ContractBody<typeof confluenceCreateSpaceContract>
export type ConfluenceUpdateSpaceBody = ContractBody<typeof confluenceUpdateSpaceContract>
export type ConfluenceDeleteSpaceBody = ContractBody<typeof confluenceDeleteSpaceContract>
export type ConfluencePageChildrenBody = ContractBody<typeof confluencePageChildrenContract>
export type ConfluencePageDescendantsBody = ContractBody<typeof confluencePageDescendantsContract>
export type ConfluencePageAncestorsBody = ContractBody<typeof confluencePageAncestorsContract>
export type ConfluencePageVersionsBody = ContractBody<typeof confluencePageVersionsContract>
export type ConfluencePagesByLabelQuery = ContractQuery<typeof confluencePagesByLabelContract>
export type ConfluenceSearchBody = ContractBody<typeof confluenceSearchContract>
export type ConfluenceSearchInSpaceBody = ContractBody<typeof confluenceSearchInSpaceContract>
export type ConfluenceSpaceBlogPostsBody = ContractBody<typeof confluenceSpaceBlogPostsContract>
export type ConfluenceSpaceLabelsQuery = ContractQuery<typeof confluenceSpaceLabelsContract>
export type ConfluenceSpacePagesBody = ContractBody<typeof confluenceSpacePagesContract>
export type ConfluenceSpacePermissionsBody = ContractBody<typeof confluenceSpacePermissionsContract>
export type ConfluenceSpacePropertiesBody = ContractBody<typeof confluenceSpacePropertiesContract>
export type ConfluenceListSpacesQuery = ContractQuery<typeof confluenceListSpacesContract>
export type ConfluenceTasksBody = ContractBody<typeof confluenceTasksContract>
export type ConfluenceUploadAttachmentBody = ContractBody<typeof confluenceUploadAttachmentContract>
export type ConfluenceUserBody = ContractBody<typeof confluenceUserContract>
export type ConfluenceSpacesSelectorResponse = ContractJsonResponse<
typeof confluenceSpacesSelectorContract
>
export type ConfluencePagesSelectorResponse = ContractJsonResponse<
typeof confluencePagesSelectorContract
>
export type ConfluencePageSelectorResponse = ContractJsonResponse<
typeof confluencePageSelectorContract
>
@@ -0,0 +1,136 @@
import { z } from 'zod'
import {
credentialIdQuerySchema,
credentialWorkflowImpersonateBodySchema,
defineGetSelector,
definePostSelector,
fileOptionSchema,
folderOptionSchema,
idNameSchema,
idTitleSchema,
optionalString,
} from '@/lib/api/contracts/selectors/shared'
import type {
ContractBodyInput,
ContractJsonResponse,
ContractQueryInput,
} from '@/lib/api/contracts/types'
const googleCalendarSchema = z.object({ id: z.string(), summary: z.string() }).passthrough()
const gmailLabelSchema = z
.object({
id: z.string(),
name: z.string(),
type: z.string().optional(),
messagesTotal: z.number().optional(),
messagesUnread: z.number().optional(),
})
.passthrough()
export const labelsQuerySchema = credentialIdQuerySchema.extend({
query: optionalString,
impersonateEmail: optionalString,
})
export const gmailLabelQuerySchema = credentialIdQuerySchema.extend({
labelId: z.string().min(1),
impersonateEmail: optionalString,
})
export const googleCalendarQuerySchema = credentialIdQuerySchema.extend({
workflowId: optionalString,
impersonateEmail: optionalString,
})
export const googleDriveFilesQuerySchema = credentialIdQuerySchema.extend({
mimeType: optionalString,
folderId: optionalString,
parentId: optionalString,
query: optionalString,
workflowId: optionalString,
impersonateEmail: optionalString,
})
export const googleDriveFileQuerySchema = credentialIdQuerySchema.extend({
fileId: z.string().min(1, 'File ID is required'),
workflowId: optionalString,
impersonateEmail: optionalString,
})
export const googleSheetsQuerySchema = credentialIdQuerySchema.extend({
spreadsheetId: z.string().min(1, 'Spreadsheet ID is required'),
workflowId: optionalString,
impersonateEmail: optionalString,
})
export const gmailLabelsSelectorContract = defineGetSelector(
'/api/tools/gmail/labels',
labelsQuerySchema,
z.object({ labels: z.array(folderOptionSchema) })
)
export const gmailLabelSelectorContract = defineGetSelector(
'/api/tools/gmail/label',
gmailLabelQuerySchema,
z.object({ label: gmailLabelSchema })
)
export const googleCalendarSelectorContract = defineGetSelector(
'/api/tools/google_calendar/calendars',
googleCalendarQuerySchema,
z.object({ calendars: z.array(googleCalendarSchema) })
)
export const googleTasksTaskListsSelectorContract = definePostSelector(
'/api/tools/google_tasks/task-lists',
credentialWorkflowImpersonateBodySchema,
z.object({ taskLists: z.array(idTitleSchema) })
)
export const googleDriveFilesSelectorContract = defineGetSelector(
'/api/tools/drive/files',
googleDriveFilesQuerySchema,
z.object({ files: z.array(fileOptionSchema) })
)
export const googleDriveFileSelectorContract = defineGetSelector(
'/api/tools/drive/file',
googleDriveFileQuerySchema,
z.object({ file: fileOptionSchema.optional() }).passthrough()
)
export const googleSheetsSelectorContract = defineGetSelector(
'/api/tools/google_sheets/sheets',
googleSheetsQuerySchema,
z.object({ sheets: z.array(idNameSchema) })
)
export type GmailLabelsSelectorQuery = ContractQueryInput<typeof gmailLabelsSelectorContract>
export type GmailLabelSelectorQuery = ContractQueryInput<typeof gmailLabelSelectorContract>
export type GoogleCalendarSelectorQuery = ContractQueryInput<typeof googleCalendarSelectorContract>
export type GoogleTasksTaskListsSelectorBody = ContractBodyInput<
typeof googleTasksTaskListsSelectorContract
>
export type GoogleDriveFilesSelectorQuery = ContractQueryInput<
typeof googleDriveFilesSelectorContract
>
export type GoogleDriveFileSelectorQuery = ContractQueryInput<
typeof googleDriveFileSelectorContract
>
export type GoogleSheetsSelectorQuery = ContractQueryInput<typeof googleSheetsSelectorContract>
export type GmailLabelsSelectorResponse = ContractJsonResponse<typeof gmailLabelsSelectorContract>
export type GmailLabelSelectorResponse = ContractJsonResponse<typeof gmailLabelSelectorContract>
export type GoogleCalendarSelectorResponse = ContractJsonResponse<
typeof googleCalendarSelectorContract
>
export type GoogleTasksTaskListsSelectorResponse = ContractJsonResponse<
typeof googleTasksTaskListsSelectorContract
>
export type GoogleDriveFilesSelectorResponse = ContractJsonResponse<
typeof googleDriveFilesSelectorContract
>
export type GoogleDriveFileSelectorResponse = ContractJsonResponse<
typeof googleDriveFileSelectorContract
>
export type GoogleSheetsSelectorResponse = ContractJsonResponse<typeof googleSheetsSelectorContract>
@@ -0,0 +1,110 @@
import { z } from 'zod'
import {
credentialIdQuerySchema,
defineGetSelector,
optionalString,
} from '@/lib/api/contracts/selectors/shared'
import type { ContractJsonResponse, ContractQueryInput } from '@/lib/api/contracts/types'
const hubspotPropertySchema = z
.object({
id: z.string(),
name: z.string(),
type: z.string().optional(),
fieldType: z.string().optional(),
groupName: z.string().optional(),
})
.passthrough()
const hubspotListSchema = z
.object({
id: z.string(),
name: z.string(),
objectType: z.string().optional(),
processingType: z.string().optional(),
})
.passthrough()
const hubspotPipelineSchema = z
.object({
id: z.string(),
name: z.string(),
stages: z.array(z.object({ id: z.string(), label: z.string() }).passthrough()).optional(),
})
.passthrough()
const hubspotOwnerSchema = z
.object({
id: z.string(),
name: z.string(),
email: z.string().optional(),
})
.passthrough()
const hubspotPropertiesQuerySchema = credentialIdQuerySchema.extend({
objectType: z
.string()
.min(1, 'objectType is required')
.describe('Built-in slug or custom object type id'),
query: optionalString,
})
const hubspotListsQuerySchema = credentialIdQuerySchema.extend({
objectTypeId: optionalString.describe('Limit to lists targeting this object type'),
query: optionalString,
})
const hubspotPipelinesQuerySchema = credentialIdQuerySchema.extend({
objectType: z
.string()
.min(1, 'objectType is required')
.describe("Object type for which to fetch pipelines (e.g., 'deal' or 'ticket')"),
})
const hubspotOwnersQuerySchema = credentialIdQuerySchema.extend({
query: optionalString,
})
export const hubspotPropertiesSelectorContract = defineGetSelector(
'/api/tools/hubspot/properties',
hubspotPropertiesQuerySchema,
z.object({ properties: z.array(hubspotPropertySchema) })
)
export const hubspotListsSelectorContract = defineGetSelector(
'/api/tools/hubspot/lists',
hubspotListsQuerySchema,
z.object({ lists: z.array(hubspotListSchema) })
)
export const hubspotPipelinesSelectorContract = defineGetSelector(
'/api/tools/hubspot/pipelines',
hubspotPipelinesQuerySchema,
z.object({ pipelines: z.array(hubspotPipelineSchema) })
)
export const hubspotOwnersSelectorContract = defineGetSelector(
'/api/tools/hubspot/owners',
hubspotOwnersQuerySchema,
z.object({ owners: z.array(hubspotOwnerSchema) })
)
export type HubspotPropertiesSelectorQuery = ContractQueryInput<
typeof hubspotPropertiesSelectorContract
>
export type HubspotListsSelectorQuery = ContractQueryInput<typeof hubspotListsSelectorContract>
export type HubspotPipelinesSelectorQuery = ContractQueryInput<
typeof hubspotPipelinesSelectorContract
>
export type HubspotOwnersSelectorQuery = ContractQueryInput<typeof hubspotOwnersSelectorContract>
export type HubspotPropertiesSelectorResponse = ContractJsonResponse<
typeof hubspotPropertiesSelectorContract
>
export type HubspotListsSelectorResponse = ContractJsonResponse<typeof hubspotListsSelectorContract>
export type HubspotPipelinesSelectorResponse = ContractJsonResponse<
typeof hubspotPipelinesSelectorContract
>
export type HubspotOwnersSelectorResponse = ContractJsonResponse<
typeof hubspotOwnersSelectorContract
>
@@ -0,0 +1,200 @@
import {
airtableBasesSelectorContract,
airtableTablesSelectorContract,
} from '@/lib/api/contracts/selectors/airtable'
import { asanaWorkspacesSelectorContract } from '@/lib/api/contracts/selectors/asana'
import {
attioListsSelectorContract,
attioObjectsSelectorContract,
} from '@/lib/api/contracts/selectors/attio'
import {
bigQueryDatasetsSelectorContract,
bigQueryTablesSelectorContract,
} from '@/lib/api/contracts/selectors/bigquery'
import {
calcomEventTypesSelectorContract,
calcomSchedulesSelectorContract,
} from '@/lib/api/contracts/selectors/calcom'
import {
cloudwatchLogGroupsSelectorContract,
cloudwatchLogStreamsSelectorContract,
} from '@/lib/api/contracts/selectors/cloudwatch'
import {
confluencePageSelectorContract,
confluencePagesSelectorContract,
confluenceSpacesSelectorContract,
} from '@/lib/api/contracts/selectors/confluence'
import {
gmailLabelSelectorContract,
gmailLabelsSelectorContract,
googleCalendarSelectorContract,
googleDriveFileSelectorContract,
googleDriveFilesSelectorContract,
googleSheetsSelectorContract,
googleTasksTaskListsSelectorContract,
} from '@/lib/api/contracts/selectors/google'
import {
hubspotListsSelectorContract,
hubspotOwnersSelectorContract,
hubspotPipelinesSelectorContract,
hubspotPropertiesSelectorContract,
} from '@/lib/api/contracts/selectors/hubspot'
import {
jiraIssueSelectorContract,
jiraIssuesSelectorContract,
jiraProjectSelectorContract,
jiraProjectsSelectorContract,
} from '@/lib/api/contracts/selectors/jira'
import {
jsmRequestTypesSelectorContract,
jsmServiceDesksSelectorContract,
} from '@/lib/api/contracts/selectors/jsm'
import {
linearProjectsSelectorContract,
linearTeamsSelectorContract,
} from '@/lib/api/contracts/selectors/linear'
import {
microsoftChannelsSelectorContract,
microsoftChatsSelectorContract,
microsoftExcelDriveSelectorContract,
microsoftExcelDrivesSelectorContract,
microsoftExcelSheetsSelectorContract,
microsoftFileSelectorContract,
microsoftFilesSelectorContract,
microsoftPlannerPlansSelectorContract,
microsoftPlannerTasksSelectorContract,
microsoftTeamsSelectorContract,
onedriveFilesSelectorContract,
onedriveFolderSelectorContract,
onedriveFoldersSelectorContract,
outlookFoldersSelectorContract,
} from '@/lib/api/contracts/selectors/microsoft'
import {
mondayBoardsSelectorContract,
mondayGroupsSelectorContract,
} from '@/lib/api/contracts/selectors/monday'
import {
notionDatabasesSelectorContract,
notionPagesSelectorContract,
} from '@/lib/api/contracts/selectors/notion'
import { pipedrivePipelinesSelectorContract } from '@/lib/api/contracts/selectors/pipedrive'
import {
sharepointListsSelectorContract,
sharepointSiteSelectorContract,
sharepointSitesSelectorContract,
} from '@/lib/api/contracts/selectors/sharepoint'
import {
slackChannelsSelectorContract,
slackUserSelectorContract,
slackUsersSelectorContract,
} from '@/lib/api/contracts/selectors/slack'
import { trelloBoardsSelectorContract } from '@/lib/api/contracts/selectors/trello'
import {
wealthboxItemContract,
wealthboxItemsSelectorContract,
wealthboxOAuthItemContract,
wealthboxOAuthItemsContract,
} from '@/lib/api/contracts/selectors/wealthbox'
import {
webflowCollectionsSelectorContract,
webflowItemsSelectorContract,
webflowSitesSelectorContract,
} from '@/lib/api/contracts/selectors/webflow'
import { zoomMeetingsSelectorContract } from '@/lib/api/contracts/selectors/zoom'
export * from '@/lib/api/contracts/selectors/airtable'
export * from '@/lib/api/contracts/selectors/asana'
export * from '@/lib/api/contracts/selectors/attio'
export * from '@/lib/api/contracts/selectors/bigquery'
export * from '@/lib/api/contracts/selectors/calcom'
export * from '@/lib/api/contracts/selectors/cloudwatch'
export * from '@/lib/api/contracts/selectors/confluence'
export * from '@/lib/api/contracts/selectors/google'
export * from '@/lib/api/contracts/selectors/hubspot'
export * from '@/lib/api/contracts/selectors/jira'
export * from '@/lib/api/contracts/selectors/jsm'
export * from '@/lib/api/contracts/selectors/knowledge'
export * from '@/lib/api/contracts/selectors/linear'
export * from '@/lib/api/contracts/selectors/microsoft'
export * from '@/lib/api/contracts/selectors/monday'
export * from '@/lib/api/contracts/selectors/notion'
export * from '@/lib/api/contracts/selectors/oauth'
export * from '@/lib/api/contracts/selectors/pipedrive'
export * from '@/lib/api/contracts/selectors/sharepoint'
export * from '@/lib/api/contracts/selectors/slack'
export * from '@/lib/api/contracts/selectors/trello'
export * from '@/lib/api/contracts/selectors/wealthbox'
export * from '@/lib/api/contracts/selectors/webflow'
export * from '@/lib/api/contracts/selectors/zoom'
export const selectorContractsByPath = {
'/api/tools/airtable/bases': airtableBasesSelectorContract,
'/api/tools/airtable/tables': airtableTablesSelectorContract,
'/api/tools/asana/workspaces': asanaWorkspacesSelectorContract,
'/api/tools/attio/objects': attioObjectsSelectorContract,
'/api/tools/attio/lists': attioListsSelectorContract,
'/api/tools/google_bigquery/datasets': bigQueryDatasetsSelectorContract,
'/api/tools/google_bigquery/tables': bigQueryTablesSelectorContract,
'/api/tools/calcom/event-types': calcomEventTypesSelectorContract,
'/api/tools/calcom/schedules': calcomSchedulesSelectorContract,
'/api/tools/confluence/selector-spaces': confluenceSpacesSelectorContract,
'/api/tools/jsm/selector-servicedesks': jsmServiceDesksSelectorContract,
'/api/tools/jsm/selector-requesttypes': jsmRequestTypesSelectorContract,
'/api/tools/google_tasks/task-lists': googleTasksTaskListsSelectorContract,
'/api/tools/microsoft_planner/plans': microsoftPlannerPlansSelectorContract,
'/api/tools/microsoft_planner/tasks': microsoftPlannerTasksSelectorContract,
'/api/tools/notion/databases': notionDatabasesSelectorContract,
'/api/tools/notion/pages': notionPagesSelectorContract,
'/api/tools/pipedrive/pipelines': pipedrivePipelinesSelectorContract,
'/api/tools/sharepoint/lists': sharepointListsSelectorContract,
'/api/tools/sharepoint/site': sharepointSiteSelectorContract,
'/api/tools/sharepoint/sites': sharepointSitesSelectorContract,
'/api/tools/trello/boards': trelloBoardsSelectorContract,
'/api/tools/zoom/meetings': zoomMeetingsSelectorContract,
'/api/tools/slack/channels': slackChannelsSelectorContract,
'/api/tools/slack/users': slackUsersSelectorContract,
'/api/tools/slack/users:detail': slackUserSelectorContract,
'/api/tools/gmail/labels': gmailLabelsSelectorContract,
'/api/tools/gmail/label': gmailLabelSelectorContract,
'/api/tools/hubspot/properties': hubspotPropertiesSelectorContract,
'/api/tools/hubspot/lists': hubspotListsSelectorContract,
'/api/tools/hubspot/pipelines': hubspotPipelinesSelectorContract,
'/api/tools/hubspot/owners': hubspotOwnersSelectorContract,
'/api/tools/outlook/folders': outlookFoldersSelectorContract,
'/api/tools/google_calendar/calendars': googleCalendarSelectorContract,
'/api/tools/microsoft-teams/teams': microsoftTeamsSelectorContract,
'/api/tools/microsoft-teams/chats': microsoftChatsSelectorContract,
'/api/tools/microsoft-teams/channels': microsoftChannelsSelectorContract,
'/api/tools/wealthbox/items': wealthboxItemsSelectorContract,
'/api/tools/wealthbox/item': wealthboxItemContract,
'/api/auth/oauth/wealthbox/items': wealthboxOAuthItemsContract,
'/api/auth/oauth/wealthbox/item': wealthboxOAuthItemContract,
'/api/tools/jira/projects': jiraProjectsSelectorContract,
'/api/tools/jira/projects:POST': jiraProjectSelectorContract,
'/api/tools/jira/issues': jiraIssuesSelectorContract,
'/api/tools/jira/issues:POST': jiraIssueSelectorContract,
'/api/tools/monday/boards': mondayBoardsSelectorContract,
'/api/tools/monday/groups': mondayGroupsSelectorContract,
'/api/tools/linear/teams': linearTeamsSelectorContract,
'/api/tools/linear/projects': linearProjectsSelectorContract,
'/api/tools/confluence/pages': confluencePagesSelectorContract,
'/api/tools/confluence/page': confluencePageSelectorContract,
'/api/tools/onedrive/files': onedriveFilesSelectorContract,
'/api/tools/onedrive/folder': onedriveFolderSelectorContract,
'/api/tools/onedrive/folders': onedriveFoldersSelectorContract,
'/api/tools/drive/files': googleDriveFilesSelectorContract,
'/api/tools/drive/file': googleDriveFileSelectorContract,
'/api/tools/google_sheets/sheets': googleSheetsSelectorContract,
'/api/tools/microsoft_excel/sheets': microsoftExcelSheetsSelectorContract,
'/api/tools/microsoft_excel/drives': microsoftExcelDrivesSelectorContract,
'/api/tools/microsoft_excel/drives:detail': microsoftExcelDriveSelectorContract,
'/api/auth/oauth/microsoft/file': microsoftFileSelectorContract,
'/api/auth/oauth/microsoft/files': microsoftFilesSelectorContract,
'/api/tools/webflow/sites': webflowSitesSelectorContract,
'/api/tools/webflow/collections': webflowCollectionsSelectorContract,
'/api/tools/webflow/items': webflowItemsSelectorContract,
'/api/tools/cloudwatch/describe-log-groups': cloudwatchLogGroupsSelectorContract,
'/api/tools/cloudwatch/describe-log-streams': cloudwatchLogStreamsSelectorContract,
} as const
export type SelectorContractPath = keyof typeof selectorContractsByPath
@@ -0,0 +1,258 @@
import { z } from 'zod'
import { idNameSchema, optionalString } from '@/lib/api/contracts/selectors/shared'
import type { ContractBody, ContractJsonResponse, ContractQuery } from '@/lib/api/contracts/types'
import { defineRouteContract } from '@/lib/api/contracts/types'
import { RawFileInputArraySchema } from '@/lib/uploads/utils/file-schemas'
const jiraIssueSectionSchema = z
.object({
issues: z.array(
z
.object({
id: z.string().optional(),
key: z.string().optional(),
summary: z.string().optional(),
})
.passthrough()
),
})
.passthrough()
export const jiraProjectsQuerySchema = z.object({
domain: z.string().trim().min(1, 'Domain is required'),
accessToken: z.string().min(1, 'Access token is required'),
cloudId: optionalString,
query: optionalString,
})
export const jiraProjectBodySchema = z.object({
domain: z.string().min(1, 'Domain is required'),
accessToken: z.string().min(1, 'Access token is required'),
cloudId: optionalString,
projectId: z.string().min(1, 'Project ID is required'),
})
/**
* GET `/api/tools/jira/issues` query.
*/
export const jiraIssuesQuerySchema = z.object({
domain: z.string().trim().min(1, 'Domain is required'),
accessToken: z.string().min(1, 'Access token is required'),
cloudId: optionalString,
projectId: optionalString,
manualProjectId: optionalString,
query: optionalString,
all: z
.preprocess(
(value) => (typeof value === 'string' ? value.toLowerCase() === 'true' : value),
z.boolean()
)
.default(false),
limit: z
.preprocess((value) => {
const parsed = typeof value === 'string' ? Number.parseInt(value, 10) : value
return typeof parsed === 'number' && Number.isFinite(parsed) && parsed > 0 ? parsed : 0
}, z.number())
.default(0),
})
export const jiraIssuesBodySchema = z.object({
domain: z.string().min(1, 'Domain is required'),
accessToken: z.string().min(1, 'Access token is required'),
cloudId: optionalString,
issueKeys: z.array(z.string().min(1)).default([]),
})
export const jiraParentReferenceSchema = z.union([
z.string().min(1),
z.object({ key: z.string().min(1) }).passthrough(),
z.object({ id: z.string().min(1) }).passthrough(),
])
export type JiraParentReference = z.input<typeof jiraParentReferenceSchema>
export const jiraWriteBodySchema = z.object({
domain: z.string({ error: 'Domain is required' }).min(1, 'Domain is required'),
accessToken: z.string({ error: 'Access token is required' }).min(1, 'Access token is required'),
projectId: z.string({ error: 'Project ID is required' }).min(1, 'Project ID is required'),
summary: z.string({ error: 'Summary is required' }).min(1, 'Summary is required'),
description: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(),
priority: z.string().optional(),
assignee: z.string().optional(),
cloudId: z.string().optional(),
issueType: z.string().optional(),
parent: jiraParentReferenceSchema.optional(),
labels: z.array(z.string()).optional(),
duedate: z.string().optional(),
reporter: z.string().optional(),
environment: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(),
customFieldId: z.string().optional(),
customFieldValue: z.string().optional(),
components: z.array(z.string()).optional(),
fixVersions: z.array(z.string()).optional(),
})
export const jiraUpdateBodySchema = z.object({
domain: z.string().min(1, 'Domain is required'),
accessToken: z.string().min(1, 'Access token is required'),
issueKey: z.string().min(1, 'Issue key is required'),
summary: z.string().optional(),
title: z.string().optional(),
description: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(),
priority: z.string().optional(),
assignee: z.string().optional(),
labels: z.array(z.string()).optional(),
components: z.array(z.string()).optional(),
duedate: z.string().optional(),
fixVersions: z.array(z.string()).optional(),
environment: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(),
customFieldId: z.string().optional(),
customFieldValue: z.string().optional(),
notifyUsers: z.boolean().optional(),
cloudId: z.string().optional(),
})
export const jiraAddAttachmentBodySchema = z.object({
accessToken: z.string().min(1, 'Access token is required'),
domain: z.string().min(1, 'Domain is required'),
issueKey: z.string().min(1, 'Issue key is required'),
files: RawFileInputArraySchema,
cloudId: z.string().optional().nullable(),
})
export const jiraProjectsSelectorContract = defineRouteContract({
method: 'GET',
path: '/api/tools/jira/projects',
query: jiraProjectsQuerySchema,
response: {
mode: 'json',
schema: z
.object({ projects: z.array(idNameSchema), cloudId: z.string().optional() })
.passthrough(),
},
})
export const jiraProjectSelectorContract = defineRouteContract({
method: 'POST',
path: '/api/tools/jira/projects',
body: jiraProjectBodySchema,
response: {
mode: 'json',
schema: z
.object({ project: idNameSchema.optional(), cloudId: z.string().optional() })
.passthrough(),
},
})
export const jiraIssuesSelectorContract = defineRouteContract({
method: 'GET',
path: '/api/tools/jira/issues',
query: jiraIssuesQuerySchema,
response: {
mode: 'json',
schema: z
.object({
sections: z.array(jiraIssueSectionSchema).optional(),
cloudId: z.string().optional(),
})
.passthrough(),
},
})
export const jiraIssueSelectorContract = defineRouteContract({
method: 'POST',
path: '/api/tools/jira/issues',
body: jiraIssuesBodySchema,
response: {
mode: 'json',
schema: z
.object({ issues: z.array(idNameSchema).optional(), cloudId: z.string().optional() })
.passthrough(),
},
})
const jiraWriteResponseSchema = z.object({
success: z.literal(true),
output: z.object({
ts: z.string(),
id: z.string(),
issueKey: z.string(),
self: z.string(),
summary: z.string(),
success: z.literal(true),
url: z.string(),
assigneeId: z.string().optional(),
}),
})
const jiraUpdateResponseSchema = z.object({
success: z.literal(true),
output: z.object({
ts: z.string(),
issueKey: z.string(),
summary: z.string(),
success: z.literal(true),
}),
})
const jiraAttachmentSchema = z.object({
id: z.string(),
filename: z.string(),
mimeType: z.string(),
size: z.number(),
content: z.string(),
})
const jiraAddAttachmentUserFileSchema = z
.object({
id: z.string().optional(),
name: z.string(),
url: z.string().optional(),
size: z.number(),
type: z.string().optional(),
key: z.string(),
})
.passthrough()
const jiraAddAttachmentResponseSchema = z.object({
success: z.literal(true),
output: z.object({
ts: z.string(),
issueKey: z.string(),
attachments: z.array(jiraAttachmentSchema),
attachmentIds: z.array(z.string()),
files: z.array(jiraAddAttachmentUserFileSchema),
}),
})
export const jiraWriteContract = defineRouteContract({
method: 'POST',
path: '/api/tools/jira/write',
body: jiraWriteBodySchema,
response: { mode: 'json', schema: jiraWriteResponseSchema },
})
export const jiraUpdateContract = defineRouteContract({
method: 'PUT',
path: '/api/tools/jira/update',
body: jiraUpdateBodySchema,
response: { mode: 'json', schema: jiraUpdateResponseSchema },
})
export const jiraAddAttachmentContract = defineRouteContract({
method: 'POST',
path: '/api/tools/jira/add-attachment',
body: jiraAddAttachmentBodySchema,
response: { mode: 'json', schema: jiraAddAttachmentResponseSchema },
})
export type JiraProjectsQuery = ContractQuery<typeof jiraProjectsSelectorContract>
export type JiraProjectBody = ContractBody<typeof jiraProjectSelectorContract>
export type JiraIssuesQuery = ContractQuery<typeof jiraIssuesSelectorContract>
export type JiraIssuesBody = ContractBody<typeof jiraIssueSelectorContract>
export type JiraWriteBody = ContractBody<typeof jiraWriteContract>
export type JiraUpdateBody = ContractBody<typeof jiraUpdateContract>
export type JiraAddAttachmentBody = ContractBody<typeof jiraAddAttachmentContract>
export type JiraProjectsSelectorResponse = ContractJsonResponse<typeof jiraProjectsSelectorContract>
export type JiraProjectSelectorResponse = ContractJsonResponse<typeof jiraProjectSelectorContract>
export type JiraIssuesSelectorResponse = ContractJsonResponse<typeof jiraIssuesSelectorContract>
export type JiraIssueSelectorResponse = ContractJsonResponse<typeof jiraIssueSelectorContract>
+462
View File
@@ -0,0 +1,462 @@
import { isRecordLike } from '@sim/utils/object'
import { z } from 'zod'
import {
credentialWorkflowDomainBodySchema,
definePostSelector,
idNameSchema,
} from '@/lib/api/contracts/selectors/shared'
import type { ContractBody, ContractJsonResponse } from '@/lib/api/contracts/types'
const jsmBaseBodySchema = z.object({
domain: z.string({ error: 'Domain is required' }).min(1, 'Domain is required'),
accessToken: z.string({ error: 'Access token is required' }).min(1, 'Access token is required'),
cloudId: z.string().optional(),
})
const jsmIssueIdOrKeyField = z
.string({ error: 'Issue ID or key is required' })
.min(1, 'Issue ID or key is required')
const jsmServiceDeskIdField = z
.string({ error: 'Service Desk ID is required' })
.min(1, 'Service Desk ID is required')
const jsmFormIdField = z.string({ error: 'Form ID is required' }).min(1, 'Form ID is required')
const jsmIdListSchema = z.union([z.string(), z.array(z.string())]).optional()
export const jsmRequestTypesBodySchema = credentialWorkflowDomainBodySchema.extend({
serviceDeskId: z.string().min(1),
})
export const jsmServiceDesksBodySchema = jsmBaseBodySchema.extend({
expand: z.string().optional(),
start: z.string().optional(),
limit: z.string().optional(),
})
export const jsmServiceDeskScopedBodySchema = jsmBaseBodySchema.extend({
serviceDeskId: jsmServiceDeskIdField,
start: z.string().optional(),
limit: z.string().optional(),
})
export const jsmQueuesBodySchema = jsmServiceDeskScopedBodySchema.extend({
includeCount: z.string().optional(),
})
export const jsmRequestTypesToolBodySchema = jsmServiceDeskScopedBodySchema.extend({
searchQuery: z.string().optional(),
groupId: z.string().optional(),
expand: z.string().optional(),
})
export const jsmRequestTypeFieldsBodySchema = jsmBaseBodySchema.extend({
serviceDeskId: jsmServiceDeskIdField,
requestTypeId: z
.string({ error: 'Request Type ID is required' })
.min(1, 'Request Type ID is required'),
})
export const jsmRequestsBodySchema = jsmBaseBodySchema.extend({
serviceDeskId: z.string().optional(),
requestOwnership: z.string().optional(),
requestStatus: z.string().optional(),
requestTypeId: z.string().optional(),
searchTerm: z.string().optional(),
expand: z.string().optional(),
start: z.string().optional(),
limit: z.string().optional(),
})
export const jsmRequestBodySchema = jsmBaseBodySchema.extend({
issueIdOrKey: z.string().optional(),
serviceDeskId: z.string().optional(),
requestTypeId: z.string().optional(),
summary: z.string().optional(),
description: z.string().optional(),
raiseOnBehalfOf: z.string().optional(),
requestFieldValues: z.record(z.string(), z.unknown()).optional(),
formAnswers: z.record(z.string(), z.unknown()).optional(),
requestParticipants: z.union([z.string(), z.array(z.string())]).optional(),
channel: z.string().optional(),
expand: z.string().optional(),
})
export const jsmCommentBodySchema = jsmBaseBodySchema.extend({
issueIdOrKey: jsmIssueIdOrKeyField,
body: z.string({ error: 'Comment body is required' }).min(1, 'Comment body is required'),
isPublic: z.boolean().optional(),
})
export const jsmCommentsBodySchema = jsmBaseBodySchema.extend({
issueIdOrKey: jsmIssueIdOrKeyField,
isPublic: z.boolean().optional(),
internal: z.boolean().optional(),
expand: z.string().optional(),
start: z.string().optional(),
limit: z.string().optional(),
})
export const jsmTransitionBodySchema = jsmBaseBodySchema.extend({
issueIdOrKey: jsmIssueIdOrKeyField,
transitionId: z
.string({ error: 'Transition ID is required' })
.min(1, 'Transition ID is required'),
comment: z.string().optional(),
})
export const jsmIssuePaginationBodySchema = jsmBaseBodySchema.extend({
issueIdOrKey: jsmIssueIdOrKeyField,
start: z.string().optional(),
limit: z.string().optional(),
})
export const jsmApprovalsBodySchema = jsmBaseBodySchema.extend({
action: z.string({ error: 'Action is required' }).min(1, 'Action is required'),
issueIdOrKey: jsmIssueIdOrKeyField,
approvalId: z.string().optional(),
decision: z.string().optional(),
start: z.string().optional(),
limit: z.string().optional(),
})
export const jsmParticipantsBodySchema = jsmBaseBodySchema.extend({
action: z.string({ error: 'Action is required' }).min(1, 'Action is required'),
issueIdOrKey: jsmIssueIdOrKeyField,
accountIds: z.union([z.string(), z.array(z.string())]).optional(),
start: z.string().optional(),
limit: z.string().optional(),
})
export const jsmCustomersBodySchema = jsmBaseBodySchema.extend({
serviceDeskId: jsmServiceDeskIdField,
query: z.string().optional(),
start: z.string().optional(),
limit: z.string().optional(),
accountIds: jsmIdListSchema,
emails: jsmIdListSchema,
})
export const jsmOrganizationBodySchema = jsmBaseBodySchema.extend({
action: z.string({ error: 'Action is required' }).min(1, 'Action is required'),
name: z.string().optional(),
serviceDeskId: z.string().optional(),
organizationId: z.string().optional(),
})
export const jsmIssueFormsBodySchema = jsmBaseBodySchema.extend({
issueIdOrKey: jsmIssueIdOrKeyField,
})
export const jsmIssueFormBodySchema = jsmIssueFormsBodySchema.extend({
formId: jsmFormIdField,
})
export const jsmAttachFormBodySchema = jsmIssueFormsBodySchema.extend({
formTemplateId: z
.string({ error: 'Form template ID is required' })
.min(1, 'Form template ID is required'),
})
export const jsmSaveFormAnswersBodySchema = jsmIssueFormBodySchema.extend({
answers: z.custom<Record<string, unknown>>(isRecordLike, {
message: 'Answers object is required',
}),
})
export const jsmProjectFormTemplatesBodySchema = jsmBaseBodySchema.extend({
projectIdOrKey: z
.string({ error: 'Project ID or key is required' })
.min(1, 'Project ID or key is required'),
})
export const jsmProjectFormStructureBodySchema = jsmProjectFormTemplatesBodySchema.extend({
formId: jsmFormIdField,
})
export const jsmCopyFormsBodySchema = jsmBaseBodySchema.extend({
sourceIssueIdOrKey: z
.string({ error: 'Source issue ID or key is required' })
.min(1, 'Source issue ID or key is required'),
targetIssueIdOrKey: z
.string({ error: 'Target issue ID or key is required' })
.min(1, 'Target issue ID or key is required'),
formIds: z.array(z.string(), { error: 'formIds must be an array of form UUIDs' }).optional(),
})
const jsmAssetsBaseBodySchema = jsmBaseBodySchema.extend({
workspaceId: z.string().optional(),
})
const jsmAssetsPaginationField = z.union([z.string(), z.number()]).optional()
export const jsmListObjectSchemasBodySchema = jsmAssetsBaseBodySchema.extend({
startAt: jsmAssetsPaginationField,
maxResults: jsmAssetsPaginationField,
includeCounts: z.union([z.string(), z.boolean()]).optional(),
})
export const jsmObjectSchemaBodySchema = jsmAssetsBaseBodySchema.extend({
schemaId: z.string({ error: 'Schema ID is required' }).min(1, 'Schema ID is required'),
})
export const jsmObjectTypesBodySchema = jsmAssetsBaseBodySchema.extend({
schemaId: z.string({ error: 'Schema ID is required' }).min(1, 'Schema ID is required'),
excludeAbstract: z.union([z.string(), z.boolean()]).optional(),
})
export const jsmObjectTypeAttributesBodySchema = jsmAssetsBaseBodySchema.extend({
objectTypeId: z
.string({ error: 'Object type ID is required' })
.min(1, 'Object type ID is required'),
onlyValueEditable: z.union([z.string(), z.boolean()]).optional(),
query: z.string().optional(),
})
export const jsmSearchObjectsAqlBodySchema = jsmAssetsBaseBodySchema.extend({
qlQuery: z.string({ error: 'AQL query is required' }).min(1, 'AQL query is required'),
page: jsmAssetsPaginationField,
resultsPerPage: jsmAssetsPaginationField,
includeAttributes: z.union([z.string(), z.boolean()]).optional(),
objectTypeId: z.string().optional(),
objectSchemaId: z.string().optional(),
})
export const jsmGetObjectBodySchema = jsmAssetsBaseBodySchema.extend({
objectId: z.string({ error: 'Object ID is required' }).min(1, 'Object ID is required'),
})
const jsmAssetAttributeInputSchema = z.object({
objectTypeAttributeId: z
.string({ error: 'objectTypeAttributeId is required' })
.min(1, 'objectTypeAttributeId is required'),
objectAttributeValues: z
.array(z.object({ value: z.unknown() }), {
error: 'objectAttributeValues must be an array of { value } entries',
})
.min(1, 'Each attribute needs at least one value'),
})
export const jsmCreateObjectBodySchema = jsmAssetsBaseBodySchema.extend({
objectTypeId: z
.string({ error: 'Object type ID is required' })
.min(1, 'Object type ID is required'),
attributes: z
.array(jsmAssetAttributeInputSchema, { error: 'attributes is required' })
.min(1, 'At least one attribute is required'),
})
export const jsmUpdateObjectBodySchema = jsmAssetsBaseBodySchema.extend({
objectId: z.string({ error: 'Object ID is required' }).min(1, 'Object ID is required'),
objectTypeId: z.string().optional(),
attributes: z
.array(jsmAssetAttributeInputSchema, { error: 'attributes is required' })
.min(1, 'At least one attribute is required'),
})
export const jsmDeleteObjectBodySchema = jsmAssetsBaseBodySchema.extend({
objectId: z.string({ error: 'Object ID is required' }).min(1, 'Object ID is required'),
})
export const defineJsmToolContract = <TBody extends z.ZodType>(path: string, body: TBody) =>
definePostSelector(path, body, z.unknown())
export const jsmServiceDesksSelectorContract = definePostSelector(
'/api/tools/jsm/selector-servicedesks',
credentialWorkflowDomainBodySchema,
z.object({ serviceDesks: z.array(idNameSchema) })
)
export const jsmRequestTypesSelectorContract = definePostSelector(
'/api/tools/jsm/selector-requesttypes',
jsmRequestTypesBodySchema,
z.object({ requestTypes: z.array(idNameSchema) })
)
export const jsmServiceDesksContract = defineJsmToolContract(
'/api/tools/jsm/servicedesks',
jsmServiceDesksBodySchema
)
export const jsmQueuesContract = defineJsmToolContract('/api/tools/jsm/queues', jsmQueuesBodySchema)
export const jsmRequestTypesContract = defineJsmToolContract(
'/api/tools/jsm/requesttypes',
jsmRequestTypesToolBodySchema
)
export const jsmRequestTypeFieldsContract = defineJsmToolContract(
'/api/tools/jsm/requesttypefields',
jsmRequestTypeFieldsBodySchema
)
export const jsmRequestsContract = defineJsmToolContract(
'/api/tools/jsm/requests',
jsmRequestsBodySchema
)
export const jsmRequestContract = defineJsmToolContract(
'/api/tools/jsm/request',
jsmRequestBodySchema
)
export const jsmCommentContract = defineJsmToolContract(
'/api/tools/jsm/comment',
jsmCommentBodySchema
)
export const jsmCommentsContract = defineJsmToolContract(
'/api/tools/jsm/comments',
jsmCommentsBodySchema
)
export const jsmTransitionContract = defineJsmToolContract(
'/api/tools/jsm/transition',
jsmTransitionBodySchema
)
export const jsmSlaContract = defineJsmToolContract(
'/api/tools/jsm/sla',
jsmIssuePaginationBodySchema
)
export const jsmTransitionsContract = defineJsmToolContract(
'/api/tools/jsm/transitions',
jsmIssuePaginationBodySchema
)
export const jsmApprovalsContract = defineJsmToolContract(
'/api/tools/jsm/approvals',
jsmApprovalsBodySchema
)
export const jsmParticipantsContract = defineJsmToolContract(
'/api/tools/jsm/participants',
jsmParticipantsBodySchema
)
export const jsmCustomersContract = defineJsmToolContract(
'/api/tools/jsm/customers',
jsmCustomersBodySchema
)
export const jsmOrganizationsContract = defineJsmToolContract(
'/api/tools/jsm/organizations',
jsmServiceDeskScopedBodySchema
)
export const jsmOrganizationContract = defineJsmToolContract(
'/api/tools/jsm/organization',
jsmOrganizationBodySchema
)
export const jsmIssueFormsContract = defineJsmToolContract(
'/api/tools/jsm/forms/issue',
jsmIssueFormsBodySchema
)
export const jsmAttachFormContract = defineJsmToolContract(
'/api/tools/jsm/forms/attach',
jsmAttachFormBodySchema
)
export const jsmGetFormContract = defineJsmToolContract(
'/api/tools/jsm/forms/get',
jsmIssueFormBodySchema
)
export const jsmSubmitFormContract = defineJsmToolContract(
'/api/tools/jsm/forms/submit',
jsmIssueFormBodySchema
)
export const jsmDeleteFormContract = defineJsmToolContract(
'/api/tools/jsm/forms/delete',
jsmIssueFormBodySchema
)
export const jsmExternaliseFormContract = defineJsmToolContract(
'/api/tools/jsm/forms/externalise',
jsmIssueFormBodySchema
)
export const jsmInternaliseFormContract = defineJsmToolContract(
'/api/tools/jsm/forms/internalise',
jsmIssueFormBodySchema
)
export const jsmReopenFormContract = defineJsmToolContract(
'/api/tools/jsm/forms/reopen',
jsmIssueFormBodySchema
)
export const jsmSaveFormAnswersContract = defineJsmToolContract(
'/api/tools/jsm/forms/save',
jsmSaveFormAnswersBodySchema
)
export const jsmFormAnswersContract = defineJsmToolContract(
'/api/tools/jsm/forms/answers',
jsmIssueFormBodySchema
)
export const jsmProjectFormTemplatesContract = defineJsmToolContract(
'/api/tools/jsm/forms/templates',
jsmProjectFormTemplatesBodySchema
)
export const jsmProjectFormStructureContract = defineJsmToolContract(
'/api/tools/jsm/forms/structure',
jsmProjectFormStructureBodySchema
)
export const jsmCopyFormsContract = defineJsmToolContract(
'/api/tools/jsm/forms/copy',
jsmCopyFormsBodySchema
)
export const jsmListObjectSchemasContract = defineJsmToolContract(
'/api/tools/jsm/assets/schemas',
jsmListObjectSchemasBodySchema
)
export const jsmGetObjectSchemaContract = defineJsmToolContract(
'/api/tools/jsm/assets/schema',
jsmObjectSchemaBodySchema
)
export const jsmListObjectTypesContract = defineJsmToolContract(
'/api/tools/jsm/assets/object-types',
jsmObjectTypesBodySchema
)
export const jsmObjectTypeAttributesContract = defineJsmToolContract(
'/api/tools/jsm/assets/attributes',
jsmObjectTypeAttributesBodySchema
)
export const jsmSearchObjectsAqlContract = defineJsmToolContract(
'/api/tools/jsm/assets/search',
jsmSearchObjectsAqlBodySchema
)
export const jsmGetObjectContract = defineJsmToolContract(
'/api/tools/jsm/assets/object/get',
jsmGetObjectBodySchema
)
export const jsmCreateObjectContract = defineJsmToolContract(
'/api/tools/jsm/assets/object/create',
jsmCreateObjectBodySchema
)
export const jsmUpdateObjectContract = defineJsmToolContract(
'/api/tools/jsm/assets/object/update',
jsmUpdateObjectBodySchema
)
export const jsmDeleteObjectContract = defineJsmToolContract(
'/api/tools/jsm/assets/object/delete',
jsmDeleteObjectBodySchema
)
export type JsmServiceDesksBody = ContractBody<typeof jsmServiceDesksContract>
export type JsmQueuesBody = ContractBody<typeof jsmQueuesContract>
export type JsmRequestTypesBody = ContractBody<typeof jsmRequestTypesContract>
export type JsmRequestTypeFieldsBody = ContractBody<typeof jsmRequestTypeFieldsContract>
export type JsmRequestsBody = ContractBody<typeof jsmRequestsContract>
export type JsmRequestBody = ContractBody<typeof jsmRequestContract>
export type JsmCommentBody = ContractBody<typeof jsmCommentContract>
export type JsmCommentsBody = ContractBody<typeof jsmCommentsContract>
export type JsmTransitionBody = ContractBody<typeof jsmTransitionContract>
export type JsmSlaBody = ContractBody<typeof jsmSlaContract>
export type JsmTransitionsBody = ContractBody<typeof jsmTransitionsContract>
export type JsmApprovalsBody = ContractBody<typeof jsmApprovalsContract>
export type JsmParticipantsBody = ContractBody<typeof jsmParticipantsContract>
export type JsmCustomersBody = ContractBody<typeof jsmCustomersContract>
export type JsmOrganizationsBody = ContractBody<typeof jsmOrganizationsContract>
export type JsmOrganizationBody = ContractBody<typeof jsmOrganizationContract>
export type JsmIssueFormsBody = ContractBody<typeof jsmIssueFormsContract>
export type JsmAttachFormBody = ContractBody<typeof jsmAttachFormContract>
export type JsmGetFormBody = ContractBody<typeof jsmGetFormContract>
export type JsmSubmitFormBody = ContractBody<typeof jsmSubmitFormContract>
export type JsmDeleteFormBody = ContractBody<typeof jsmDeleteFormContract>
export type JsmExternaliseFormBody = ContractBody<typeof jsmExternaliseFormContract>
export type JsmInternaliseFormBody = ContractBody<typeof jsmInternaliseFormContract>
export type JsmReopenFormBody = ContractBody<typeof jsmReopenFormContract>
export type JsmSaveFormAnswersBody = ContractBody<typeof jsmSaveFormAnswersContract>
export type JsmFormAnswersBody = ContractBody<typeof jsmFormAnswersContract>
export type JsmProjectFormTemplatesBody = ContractBody<typeof jsmProjectFormTemplatesContract>
export type JsmProjectFormStructureBody = ContractBody<typeof jsmProjectFormStructureContract>
export type JsmCopyFormsBody = ContractBody<typeof jsmCopyFormsContract>
export type JsmServiceDesksSelectorResponse = ContractJsonResponse<
typeof jsmServiceDesksSelectorContract
>
export type JsmRequestTypesSelectorResponse = ContractJsonResponse<
typeof jsmRequestTypesSelectorContract
>
@@ -0,0 +1,69 @@
import { z } from 'zod'
import { optionalString } from '@/lib/api/contracts/selectors/shared'
import type { ContractJsonResponse } from '@/lib/api/contracts/types'
import { defineRouteContract } from '@/lib/api/contracts/types'
const knowledgeDocumentsParamsSchema = z.object({ id: z.string().min(1) })
const knowledgeDocumentParamsSchema = knowledgeDocumentsParamsSchema.extend({
documentId: z.string().min(1),
})
const knowledgeDocumentsQuerySchema = z.object({
limit: z.coerce.number().int().min(1).max(100).optional(),
offset: z.coerce.number().int().min(0).optional(),
search: optionalString,
})
const knowledgeDocumentQuerySchema = z.object({
includeDisabled: optionalString,
})
const knowledgeDocumentSchema = z.object({ id: z.string(), filename: z.string() }).passthrough()
export const listKnowledgeSelectorDocumentsContract = defineRouteContract({
method: 'GET',
path: '/api/knowledge/[id]/documents',
params: knowledgeDocumentsParamsSchema,
query: knowledgeDocumentsQuerySchema,
response: {
mode: 'json',
schema: z.object({
success: z.literal(true),
data: z
.object({
documents: z.array(knowledgeDocumentSchema),
pagination: z
.object({
total: z.number(),
limit: z.number(),
offset: z.number(),
hasMore: z.boolean(),
})
.passthrough(),
})
.passthrough(),
}),
},
})
export const getKnowledgeSelectorDocumentContract = defineRouteContract({
method: 'GET',
path: '/api/knowledge/[id]/documents/[documentId]',
params: knowledgeDocumentParamsSchema,
query: knowledgeDocumentQuerySchema,
response: {
mode: 'json',
schema: z.object({
success: z.literal(true),
data: knowledgeDocumentSchema,
}),
},
})
export type ListKnowledgeSelectorDocumentsResponse = ContractJsonResponse<
typeof listKnowledgeSelectorDocumentsContract
>
export type GetKnowledgeSelectorDocumentResponse = ContractJsonResponse<
typeof getKnowledgeSelectorDocumentContract
>
@@ -0,0 +1,28 @@
import { z } from 'zod'
import {
credentialWorkflowBodySchema,
definePostSelector,
idNameSchema,
} from '@/lib/api/contracts/selectors/shared'
import type { ContractJsonResponse } from '@/lib/api/contracts/types'
export const linearProjectsBodySchema = credentialWorkflowBodySchema.extend({
teamId: z.string().min(1),
})
export const linearTeamsSelectorContract = definePostSelector(
'/api/tools/linear/teams',
credentialWorkflowBodySchema,
z.object({ teams: z.array(idNameSchema) })
)
export const linearProjectsSelectorContract = definePostSelector(
'/api/tools/linear/projects',
linearProjectsBodySchema,
z.object({ projects: z.array(idNameSchema) })
)
export type LinearTeamsSelectorResponse = ContractJsonResponse<typeof linearTeamsSelectorContract>
export type LinearProjectsSelectorResponse = ContractJsonResponse<
typeof linearProjectsSelectorContract
>
@@ -0,0 +1,238 @@
import { z } from 'zod'
import {
credentialIdQuerySchema,
credentialIdQueryWithSearchSchema,
credentialWorkflowBodySchema,
defineGetSelector,
definePostSelector,
fileOptionSchema,
idDisplayNameSchema,
idNameSchema,
idTitleSchema,
optionalString,
} from '@/lib/api/contracts/selectors/shared'
import type { ContractBody, ContractJsonResponse, ContractQuery } from '@/lib/api/contracts/types'
export const teamsChannelsBodySchema = credentialWorkflowBodySchema.extend({
teamId: z.string().min(1),
})
export const plannerTasksBodySchema = credentialWorkflowBodySchema.extend({
planId: z.string().min(1),
})
export const microsoftExcelSheetsQuerySchema = credentialIdQuerySchema.extend({
spreadsheetId: z.string().min(1, 'Spreadsheet ID is required'),
driveId: optionalString,
workflowId: optionalString,
})
/**
* Body for `POST /api/tools/microsoft_excel/drives`. The route serves both
* list-drives (no `driveId`) and single-drive lookup (`driveId` provided),
* dispatching at runtime. The contract permits the optional `driveId` so a
* single body schema covers both flows.
*/
export const microsoftExcelDrivesBodySchema = credentialWorkflowBodySchema.extend({
siteId: z.string().min(1, 'Site ID is required'),
driveId: optionalString,
})
/**
* The `/api/auth/oauth/microsoft/files` route is shared by the
* `microsoft.excel` and `microsoft.word` selectors. `fileType` lets the route
* search for and filter to the correct Office document type; it is optional and
* defaults to `excel` on the server for backward compatibility.
*/
export const microsoftFilesQuerySchema = credentialIdQuerySchema.extend({
query: optionalString,
driveId: optionalString,
workflowId: optionalString,
fileType: z.enum(['excel', 'word']).optional(),
})
export const microsoftFileQuerySchema = credentialIdQuerySchema.extend({
fileId: z.string({ error: 'File ID is required' }).min(1, 'File ID is required'),
workflowId: optionalString,
})
export const onedriveFolderQuerySchema = z.object({
credentialId: z.preprocess(
(value) => value ?? '',
z.string().min(1, 'Credential ID and File ID are required')
),
fileId: z.preprocess(
(value) => value ?? '',
z.string().min(1, 'Credential ID and File ID are required')
),
})
export const onedriveFilesQuerySchema = credentialIdQueryWithSearchSchema
export const onedriveFoldersQuerySchema = credentialIdQueryWithSearchSchema
export const outlookFoldersQuerySchema = credentialIdQuerySchema
export const outlookFoldersSelectorContract = defineGetSelector(
'/api/tools/outlook/folders',
outlookFoldersQuerySchema,
z.object({ folders: z.array(z.object({ id: z.string(), name: z.string() }).passthrough()) })
)
export const microsoftTeamsSelectorContract = definePostSelector(
'/api/tools/microsoft-teams/teams',
credentialWorkflowBodySchema,
z.object({ teams: z.array(idDisplayNameSchema) })
)
export const microsoftChatsSelectorContract = definePostSelector(
'/api/tools/microsoft-teams/chats',
credentialWorkflowBodySchema,
z.object({ chats: z.array(idDisplayNameSchema) })
)
export const microsoftChannelsSelectorContract = definePostSelector(
'/api/tools/microsoft-teams/channels',
teamsChannelsBodySchema,
z.object({ channels: z.array(idDisplayNameSchema) })
)
export const microsoftPlannerPlansSelectorContract = definePostSelector(
'/api/tools/microsoft_planner/plans',
credentialWorkflowBodySchema,
z.object({ plans: z.array(idTitleSchema) })
)
export const microsoftPlannerTasksSelectorContract = definePostSelector(
'/api/tools/microsoft_planner/tasks',
plannerTasksBodySchema,
z
.object({
tasks: z.array(idTitleSchema),
metadata: z
.object({
planId: z.string(),
planUrl: z.string(),
})
.passthrough()
.optional(),
})
.passthrough()
)
export const onedriveFilesSelectorContract = defineGetSelector(
'/api/tools/onedrive/files',
onedriveFilesQuerySchema,
z.object({ files: z.array(fileOptionSchema) })
)
export const onedriveFoldersSelectorContract = defineGetSelector(
'/api/tools/onedrive/folders',
onedriveFoldersQuerySchema,
z.object({ files: z.array(fileOptionSchema) })
)
export const onedriveFolderSelectorContract = defineGetSelector(
'/api/tools/onedrive/folder',
onedriveFolderQuerySchema,
z.object({ file: fileOptionSchema.optional() }).passthrough()
)
export const microsoftExcelSheetsSelectorContract = defineGetSelector(
'/api/tools/microsoft_excel/sheets',
microsoftExcelSheetsQuerySchema,
z.object({ sheets: z.array(idNameSchema) })
)
export const microsoftExcelDrivesSelectorContract = definePostSelector(
'/api/tools/microsoft_excel/drives',
microsoftExcelDrivesBodySchema,
z.object({ drives: z.array(idNameSchema) })
)
/**
* Single-drive variant. Same body schema as the list contract; the `driveId`
* is what discriminates the response shape at the route layer.
*/
export const microsoftExcelDriveSelectorContract = definePostSelector(
'/api/tools/microsoft_excel/drives',
microsoftExcelDrivesBodySchema,
z.object({ drive: idNameSchema.optional() })
)
export const microsoftFilesSelectorContract = defineGetSelector(
'/api/auth/oauth/microsoft/files',
microsoftFilesQuerySchema,
z.object({ files: z.array(fileOptionSchema) })
)
export const microsoftFileSelectorContract = defineGetSelector(
'/api/auth/oauth/microsoft/file',
microsoftFileQuerySchema,
z.object({ file: fileOptionSchema.optional() }).passthrough()
)
export type OutlookFoldersSelectorResponse = ContractJsonResponse<
typeof outlookFoldersSelectorContract
>
export type OutlookFoldersSelectorQuery = ContractQuery<typeof outlookFoldersSelectorContract>
export type MicrosoftTeamsSelectorResponse = ContractJsonResponse<
typeof microsoftTeamsSelectorContract
>
export type MicrosoftTeamsSelectorBody = ContractBody<typeof microsoftTeamsSelectorContract>
export type MicrosoftChatsSelectorResponse = ContractJsonResponse<
typeof microsoftChatsSelectorContract
>
export type MicrosoftChatsSelectorBody = ContractBody<typeof microsoftChatsSelectorContract>
export type MicrosoftChannelsSelectorResponse = ContractJsonResponse<
typeof microsoftChannelsSelectorContract
>
export type MicrosoftChannelsSelectorBody = ContractBody<typeof microsoftChannelsSelectorContract>
export type MicrosoftPlannerPlansSelectorResponse = ContractJsonResponse<
typeof microsoftPlannerPlansSelectorContract
>
export type MicrosoftPlannerPlansSelectorBody = ContractBody<
typeof microsoftPlannerPlansSelectorContract
>
export type MicrosoftPlannerTasksSelectorResponse = ContractJsonResponse<
typeof microsoftPlannerTasksSelectorContract
>
export type MicrosoftPlannerTasksSelectorBody = ContractBody<
typeof microsoftPlannerTasksSelectorContract
>
export type OnedriveFilesSelectorResponse = ContractJsonResponse<
typeof onedriveFilesSelectorContract
>
export type OnedriveFilesSelectorQuery = ContractQuery<typeof onedriveFilesSelectorContract>
export type OnedriveFoldersSelectorResponse = ContractJsonResponse<
typeof onedriveFoldersSelectorContract
>
export type OnedriveFoldersSelectorQuery = ContractQuery<typeof onedriveFoldersSelectorContract>
export type OnedriveFolderSelectorResponse = ContractJsonResponse<
typeof onedriveFolderSelectorContract
>
export type OnedriveFolderSelectorQuery = ContractQuery<typeof onedriveFolderSelectorContract>
export type MicrosoftExcelSheetsSelectorResponse = ContractJsonResponse<
typeof microsoftExcelSheetsSelectorContract
>
export type MicrosoftExcelSheetsSelectorQuery = ContractQuery<
typeof microsoftExcelSheetsSelectorContract
>
export type MicrosoftExcelDrivesSelectorResponse = ContractJsonResponse<
typeof microsoftExcelDrivesSelectorContract
>
export type MicrosoftExcelDrivesSelectorBody = ContractBody<
typeof microsoftExcelDrivesSelectorContract
>
export type MicrosoftExcelDriveSelectorResponse = ContractJsonResponse<
typeof microsoftExcelDriveSelectorContract
>
export type MicrosoftExcelDriveSelectorBody = ContractBody<
typeof microsoftExcelDriveSelectorContract
>
export type MicrosoftFilesSelectorResponse = ContractJsonResponse<
typeof microsoftFilesSelectorContract
>
export type MicrosoftFilesSelectorQuery = ContractQuery<typeof microsoftFilesSelectorContract>
export type MicrosoftFileSelectorResponse = ContractJsonResponse<
typeof microsoftFileSelectorContract
>
export type MicrosoftFileSelectorQuery = ContractQuery<typeof microsoftFileSelectorContract>
@@ -0,0 +1,32 @@
import { z } from 'zod'
import {
credentialWorkflowBodySchema,
definePostSelector,
idNameSchema,
} from '@/lib/api/contracts/selectors/shared'
import type { ContractJsonResponse } from '@/lib/api/contracts/types'
/**
* Monday board IDs are numeric in the API (e.g. `123456789`). Clients
* sometimes pass them as numbers and sometimes as strings, so we accept
* `string | number` and let the route's `validateMondayNumericId` enforce
* the actual numeric format.
*/
export const mondayGroupsBodySchema = credentialWorkflowBodySchema.extend({
boardId: z.union([z.string().min(1), z.number()]),
})
export const mondayBoardsSelectorContract = definePostSelector(
'/api/tools/monday/boards',
credentialWorkflowBodySchema,
z.object({ boards: z.array(idNameSchema) })
)
export const mondayGroupsSelectorContract = definePostSelector(
'/api/tools/monday/groups',
mondayGroupsBodySchema,
z.object({ groups: z.array(idNameSchema) })
)
export type MondayBoardsSelectorResponse = ContractJsonResponse<typeof mondayBoardsSelectorContract>
export type MondayGroupsSelectorResponse = ContractJsonResponse<typeof mondayGroupsSelectorContract>
@@ -0,0 +1,24 @@
import { z } from 'zod'
import {
credentialWorkflowBodySchema,
definePostSelector,
idNameSchema,
} from '@/lib/api/contracts/selectors/shared'
import type { ContractJsonResponse } from '@/lib/api/contracts/types'
export const notionDatabasesSelectorContract = definePostSelector(
'/api/tools/notion/databases',
credentialWorkflowBodySchema,
z.object({ databases: z.array(idNameSchema) })
)
export const notionPagesSelectorContract = definePostSelector(
'/api/tools/notion/pages',
credentialWorkflowBodySchema,
z.object({ pages: z.array(idNameSchema) })
)
export type NotionDatabasesSelectorResponse = ContractJsonResponse<
typeof notionDatabasesSelectorContract
>
export type NotionPagesSelectorResponse = ContractJsonResponse<typeof notionPagesSelectorContract>
@@ -0,0 +1,22 @@
import { z } from 'zod'
import { oauthTokenRequestBodySchema } from '@/lib/api/contracts/oauth-connections'
import { definePostSelector } from '@/lib/api/contracts/selectors/shared'
import type { ContractJsonResponse } from '@/lib/api/contracts/types'
const oauthTokenResponseSchema = z
.object({
accessToken: z.string().optional(),
idToken: z.string().optional(),
instanceUrl: z.string().optional(),
cloudId: z.string().optional(),
domain: z.string().optional(),
})
.passthrough()
export const oauthTokenContract = definePostSelector(
'/api/auth/oauth/token',
oauthTokenRequestBodySchema,
oauthTokenResponseSchema
)
export type OauthTokenResponse = ContractJsonResponse<typeof oauthTokenContract>
@@ -0,0 +1,17 @@
import { z } from 'zod'
import {
credentialWorkflowBodySchema,
definePostSelector,
idNameSchema,
} from '@/lib/api/contracts/selectors/shared'
import type { ContractJsonResponse } from '@/lib/api/contracts/types'
export const pipedrivePipelinesSelectorContract = definePostSelector(
'/api/tools/pipedrive/pipelines',
credentialWorkflowBodySchema,
z.object({ pipelines: z.array(idNameSchema) })
)
export type PipedrivePipelinesSelectorResponse = ContractJsonResponse<
typeof pipedrivePipelinesSelectorContract
>
@@ -0,0 +1,70 @@
import { z } from 'zod'
import { defineRouteContract } from '@/lib/api/contracts/types'
export const optionalString = z.string().optional()
/**
* Accepts `string | null | undefined` on the wire and outputs `string |
* undefined` (null collapsed to undefined). Used for fields like `workflowId`
* where the wire is permissive but server-side handlers expect an optional
* string.
*/
export const nullableOptionalString = z
.string()
.nullish()
.transform((value) => value ?? undefined)
export const credentialWorkflowBodySchema = z.object({
credential: z.string().min(1),
workflowId: nullableOptionalString,
})
export const credentialWorkflowDomainBodySchema = credentialWorkflowBodySchema.extend({
domain: z.string().min(1),
})
export const credentialWorkflowImpersonateBodySchema = credentialWorkflowBodySchema.extend({
impersonateEmail: optionalString,
})
export const credentialIdQuerySchema = z.object({
credentialId: z
.string({ error: 'Credential ID is required' })
.min(1, 'Credential ID is required'),
})
export const credentialIdQueryWithSearchSchema = credentialIdQuerySchema.extend({
query: optionalString,
})
export const idNameSchema = z.object({ id: z.string(), name: z.string() }).passthrough()
export const idTitleSchema = z.object({ id: z.string(), title: z.string() }).passthrough()
export const idDisplayNameSchema = z
.object({ id: z.string(), displayName: z.string() })
.passthrough()
export const fileOptionSchema = z.object({ id: z.string(), name: z.string() }).passthrough()
export const folderOptionSchema = z.object({ id: z.string(), name: z.string() }).passthrough()
export const definePostSelector = <TBody extends z.ZodType, TResponse extends z.ZodType>(
path: string,
body: TBody,
response: TResponse
) =>
defineRouteContract({
method: 'POST',
path,
body,
response: { mode: 'json', schema: response },
})
export const defineGetSelector = <TQuery extends z.ZodType, TResponse extends z.ZodType>(
path: string,
query: TQuery,
response: TResponse
) =>
defineRouteContract({
method: 'GET',
path,
query,
response: { mode: 'json', schema: response },
})
@@ -0,0 +1,60 @@
import { z } from 'zod'
import {
credentialWorkflowBodySchema,
defineGetSelector,
definePostSelector,
fileOptionSchema,
idDisplayNameSchema,
optionalString,
} from '@/lib/api/contracts/selectors/shared'
import type { ContractBody, ContractJsonResponse, ContractQuery } from '@/lib/api/contracts/types'
export const sharepointListsBodySchema = credentialWorkflowBodySchema.extend({
siteId: z.string().min(1),
})
export const sharepointSitesBodySchema = credentialWorkflowBodySchema.extend({
query: optionalString,
})
export const sharepointSiteQuerySchema = z.object({
credentialId: z.preprocess(
(value) => value ?? '',
z.string().min(1, 'Credential ID and Site ID are required')
),
siteId: z.preprocess(
(value) => value ?? '',
z.string().min(1, 'Credential ID and Site ID are required')
),
})
export const sharepointListsSelectorContract = definePostSelector(
'/api/tools/sharepoint/lists',
sharepointListsBodySchema,
z.object({ lists: z.array(idDisplayNameSchema) })
)
export const sharepointSitesSelectorContract = definePostSelector(
'/api/tools/sharepoint/sites',
sharepointSitesBodySchema,
z.object({ files: z.array(fileOptionSchema) })
)
export const sharepointSiteSelectorContract = defineGetSelector(
'/api/tools/sharepoint/site',
sharepointSiteQuerySchema,
z.object({ site: fileOptionSchema.optional() }).passthrough()
)
export type SharepointListsSelectorResponse = ContractJsonResponse<
typeof sharepointListsSelectorContract
>
export type SharepointListsSelectorBody = ContractBody<typeof sharepointListsSelectorContract>
export type SharepointSitesSelectorResponse = ContractJsonResponse<
typeof sharepointSitesSelectorContract
>
export type SharepointSitesSelectorBody = ContractBody<typeof sharepointSitesSelectorContract>
export type SharepointSiteSelectorResponse = ContractJsonResponse<
typeof sharepointSiteSelectorContract
>
export type SharepointSiteSelectorQuery = ContractQuery<typeof sharepointSiteSelectorContract>
@@ -0,0 +1,49 @@
import { z } from 'zod'
import {
credentialWorkflowBodySchema,
definePostSelector,
} from '@/lib/api/contracts/selectors/shared'
import type { ContractJsonResponse } from '@/lib/api/contracts/types'
const slackChannelSchema = z.object({
id: z.string(),
name: z.string(),
isPrivate: z.boolean(),
})
const slackUserSchema = z
.object({ id: z.string(), name: z.string(), real_name: z.string() })
.passthrough()
export const slackUsersBodySchema = credentialWorkflowBodySchema.extend({
userId: z.string().optional(),
})
export const slackChannelsSelectorContract = definePostSelector(
'/api/tools/slack/channels',
credentialWorkflowBodySchema,
z.object({ channels: z.array(slackChannelSchema) })
)
export const slackUsersSelectorContract = definePostSelector(
'/api/tools/slack/users',
credentialWorkflowBodySchema,
z.object({ users: z.array(slackUserSchema) })
)
export const slackUserSelectorContract = definePostSelector(
'/api/tools/slack/users',
credentialWorkflowBodySchema.extend({ userId: z.string().min(1) }),
z.object({ user: slackUserSchema })
)
export const slackUsersListOrDetailContract = definePostSelector(
'/api/tools/slack/users',
slackUsersBodySchema,
z.union([z.object({ user: slackUserSchema }), z.object({ users: z.array(slackUserSchema) })])
)
export type SlackChannelsSelectorResponse = ContractJsonResponse<
typeof slackChannelsSelectorContract
>
export type SlackUsersSelectorResponse = ContractJsonResponse<typeof slackUsersSelectorContract>
export type SlackUserSelectorResponse = ContractJsonResponse<typeof slackUserSelectorContract>
@@ -0,0 +1,18 @@
import { z } from 'zod'
import {
credentialWorkflowBodySchema,
definePostSelector,
} from '@/lib/api/contracts/selectors/shared'
import type { ContractJsonResponse } from '@/lib/api/contracts/types'
const trelloBoardSchema = z
.object({ id: z.string(), name: z.string(), closed: z.boolean().optional() })
.passthrough()
export const trelloBoardsSelectorContract = definePostSelector(
'/api/tools/trello/boards',
credentialWorkflowBodySchema,
z.object({ boards: z.array(trelloBoardSchema) })
)
export type TrelloBoardsSelectorResponse = ContractJsonResponse<typeof trelloBoardsSelectorContract>
@@ -0,0 +1,77 @@
import { z } from 'zod'
import { defineGetSelector, optionalString } from '@/lib/api/contracts/selectors/shared'
import type { ContractJsonResponse } from '@/lib/api/contracts/types'
export const WEALTHBOX_ITEM_TYPES = ['note', 'contact', 'task'] as const
const wealthboxItemSchema = z.object({
id: z.string(),
name: z.string(),
type: z.string(),
content: z.string(),
createdAt: z.string(),
updatedAt: z.string(),
})
const wealthboxItemsResponseSchema = z.object({
items: z.array(wealthboxItemSchema),
})
const wealthboxItemResponseSchema = z.object({
item: wealthboxItemSchema.optional(),
})
export const wealthboxItemsQuerySchema = z.object({
credentialId: z.string().min(1),
type: z.preprocess(
(value) => (value === '' || value === undefined ? 'contact' : value),
z.literal('contact').default('contact')
),
query: z.preprocess(
(value) => (value === undefined || value === null ? '' : value),
optionalString.default('')
),
})
export const wealthboxItemQuerySchema = z.object({
credentialId: z.preprocess(
(value) => value ?? '',
z.string().min(1, 'Credential ID is required')
),
itemId: z.preprocess((value) => value ?? '', z.string().min(1, 'Item ID is required')),
type: z.preprocess(
(value) => value || 'note',
z.enum(WEALTHBOX_ITEM_TYPES, { error: 'type must be one of: note, contact, task' })
),
})
export const wealthboxItemsSelectorContract = defineGetSelector(
'/api/tools/wealthbox/items',
wealthboxItemsQuerySchema,
wealthboxItemsResponseSchema
)
export const wealthboxItemContract = defineGetSelector(
'/api/tools/wealthbox/item',
wealthboxItemQuerySchema,
wealthboxItemResponseSchema
)
export const wealthboxOAuthItemsContract = defineGetSelector(
'/api/auth/oauth/wealthbox/items',
wealthboxItemsQuerySchema,
wealthboxItemsResponseSchema
)
export const wealthboxOAuthItemContract = defineGetSelector(
'/api/auth/oauth/wealthbox/item',
wealthboxItemQuerySchema,
wealthboxItemResponseSchema
)
export type WealthboxItemsSelectorResponse = ContractJsonResponse<
typeof wealthboxItemsSelectorContract
>
export type WealthboxItemResponse = ContractJsonResponse<typeof wealthboxItemContract>
export type WealthboxOAuthItemsResponse = ContractJsonResponse<typeof wealthboxOAuthItemsContract>
export type WealthboxOAuthItemResponse = ContractJsonResponse<typeof wealthboxOAuthItemContract>
@@ -0,0 +1,49 @@
import { z } from 'zod'
import {
credentialWorkflowBodySchema,
definePostSelector,
idNameSchema,
optionalString,
} from '@/lib/api/contracts/selectors/shared'
import type { ContractJsonResponse } from '@/lib/api/contracts/types'
/**
* Webflow `/sites` accepts an optional `siteId`. When provided, the route
* dispatches to the single-site detail endpoint instead of the list endpoint.
*/
export const webflowSitesBodySchema = credentialWorkflowBodySchema.extend({
siteId: optionalString,
})
export const webflowCollectionsBodySchema = credentialWorkflowBodySchema.extend({
siteId: z.string().min(1, 'Site ID is required'),
})
export const webflowItemsBodySchema = credentialWorkflowBodySchema.extend({
collectionId: z.string().min(1, 'Collection ID is required'),
search: optionalString,
})
export const webflowSitesSelectorContract = definePostSelector(
'/api/tools/webflow/sites',
webflowSitesBodySchema,
z.object({ sites: z.array(idNameSchema) })
)
export const webflowCollectionsSelectorContract = definePostSelector(
'/api/tools/webflow/collections',
webflowCollectionsBodySchema,
z.object({ collections: z.array(idNameSchema) })
)
export const webflowItemsSelectorContract = definePostSelector(
'/api/tools/webflow/items',
webflowItemsBodySchema,
z.object({ items: z.array(idNameSchema) })
)
export type WebflowSitesSelectorResponse = ContractJsonResponse<typeof webflowSitesSelectorContract>
export type WebflowCollectionsSelectorResponse = ContractJsonResponse<
typeof webflowCollectionsSelectorContract
>
export type WebflowItemsSelectorResponse = ContractJsonResponse<typeof webflowItemsSelectorContract>
@@ -0,0 +1,15 @@
import { z } from 'zod'
import {
credentialWorkflowBodySchema,
definePostSelector,
idNameSchema,
} from '@/lib/api/contracts/selectors/shared'
import type { ContractJsonResponse } from '@/lib/api/contracts/types'
export const zoomMeetingsSelectorContract = definePostSelector(
'/api/tools/zoom/meetings',
credentialWorkflowBodySchema,
z.object({ meetings: z.array(idNameSchema) })
)
export type ZoomMeetingsSelectorResponse = ContractJsonResponse<typeof zoomMeetingsSelectorContract>