diff --git a/.github/workflows/nuke.yml b/.github/workflows/nuke.yml index 9a26d96d84..014088b891 100644 --- a/.github/workflows/nuke.yml +++ b/.github/workflows/nuke.yml @@ -70,6 +70,10 @@ on: description: "Eas" type: boolean default: false + clerk: + description: "Clerk" + type: boolean + default: false env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 30c3bc584b..6ec30ce465 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,6 +42,7 @@ jobs: typesense: ${{ steps.force.outputs.all || steps.changes.outputs.typesense }} workos: ${{ steps.force.outputs.all || steps.changes.outputs.workos }} expo-eas: ${{ steps.force.outputs.all || steps.changes.outputs.expo-eas }} + clerk: ${{ steps.force.outputs.all || steps.changes.outputs.clerk }} steps: - id: force if: contains(github.event.pull_request.labels.*.name, 'force-ci') @@ -110,6 +111,9 @@ jobs: expo-eas: - 'packages/expo-eas/**' - 'packages/core/**' + clerk: + - 'packages/clerk/**' + - 'packages/core/**' ci-core: needs: detect-changes @@ -494,3 +498,18 @@ jobs: working-directory: packages/expo-eas env: EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} + + ci-clerk: + needs: detect-changes + if: needs.detect-changes.outputs.clerk == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + - run: bun install + - run: bun run build + working-directory: packages/core + - run: bun run check + working-directory: packages/clerk diff --git a/.gitmodules b/.gitmodules index e326dc56a9..6429c05a80 100644 --- a/.gitmodules +++ b/.gitmodules @@ -107,3 +107,6 @@ shallow = true ignore = dirty fetchRecurseSubmodules = false +[submodule "packages/clerk/specs/distilled-spec-clerk"] + path = packages/clerk/specs/distilled-spec-clerk + url = https://github.com/alchemy-run/distilled-spec-clerk.git diff --git a/bun.lock b/bun.lock index b4c18219a9..cb2a928b8e 100644 --- a/bun.lock +++ b/bun.lock @@ -23,7 +23,7 @@ }, "packages/aws": { "name": "@distilled.cloud/aws", - "version": "0.21.3", + "version": "0.22.4", "dependencies": { "@aws-crypto/crc32": "catalog:", "@aws-crypto/util": "catalog:", @@ -54,7 +54,7 @@ }, "packages/axiom": { "name": "@distilled.cloud/axiom", - "version": "0.21.3", + "version": "0.22.4", "dependencies": { "@distilled.cloud/core": "workspace:*", }, @@ -70,7 +70,7 @@ }, "packages/azure": { "name": "@distilled.cloud/azure", - "version": "0.21.3", + "version": "0.22.4", "dependencies": { "@distilled.cloud/core": "workspace:*", }, @@ -84,9 +84,26 @@ "effect": "catalog:", }, }, + "packages/clerk": { + "name": "@distilled.cloud/clerk", + "version": "0.2.0-alpha", + "dependencies": { + "@distilled.cloud/core": "workspace:*", + "effect": "catalog:", + }, + "devDependencies": { + "@types/bun": "catalog:", + "@types/node": "catalog:", + "dotenv": "catalog:", + "vitest": "catalog:", + }, + "peerDependencies": { + "effect": "catalog:", + }, + }, "packages/cloudflare": { "name": "@distilled.cloud/cloudflare", - "version": "0.21.3", + "version": "0.22.4", "dependencies": { "@distilled.cloud/core": "workspace:*", }, @@ -105,7 +122,7 @@ }, "packages/coinbase": { "name": "@distilled.cloud/coinbase", - "version": "0.21.3", + "version": "0.22.4", "dependencies": { "@distilled.cloud/core": "workspace:*", }, @@ -122,7 +139,7 @@ }, "packages/core": { "name": "@distilled.cloud/core", - "version": "0.21.3", + "version": "0.22.4", "devDependencies": { "@types/bun": "catalog:", "@types/node": "catalog:", @@ -135,7 +152,7 @@ }, "packages/expo-eas": { "name": "@distilled.cloud/expo-eas", - "version": "0.21.3", + "version": "0.22.4", "dependencies": { "@distilled.cloud/core": "workspace:*", }, @@ -151,7 +168,7 @@ }, "packages/fly-io": { "name": "@distilled.cloud/fly-io", - "version": "0.21.3", + "version": "0.22.4", "dependencies": { "@distilled.cloud/core": "workspace:*", }, @@ -167,7 +184,7 @@ }, "packages/gcp": { "name": "@distilled.cloud/gcp", - "version": "0.21.3", + "version": "0.22.4", "dependencies": { "@distilled.cloud/core": "workspace:*", }, @@ -183,7 +200,7 @@ }, "packages/kubernetes": { "name": "@distilled.cloud/kubernetes", - "version": "0.21.3", + "version": "0.22.4", "dependencies": { "@distilled.cloud/core": "workspace:*", }, @@ -199,7 +216,7 @@ }, "packages/mongodb-atlas": { "name": "@distilled.cloud/mongodb-atlas", - "version": "0.21.3", + "version": "0.22.4", "dependencies": { "@distilled.cloud/core": "workspace:*", }, @@ -215,7 +232,7 @@ }, "packages/neon": { "name": "@distilled.cloud/neon", - "version": "0.21.3", + "version": "0.22.4", "dependencies": { "@distilled.cloud/core": "workspace:*", }, @@ -231,7 +248,7 @@ }, "packages/planetscale": { "name": "@distilled.cloud/planetscale", - "version": "0.21.3", + "version": "0.22.4", "dependencies": { "@distilled.cloud/core": "workspace:*", }, @@ -247,7 +264,7 @@ }, "packages/posthog": { "name": "@distilled.cloud/posthog", - "version": "0.21.3", + "version": "0.22.4", "dependencies": { "@distilled.cloud/core": "workspace:*", }, @@ -265,7 +282,7 @@ }, "packages/prisma-postgres": { "name": "@distilled.cloud/prisma-postgres", - "version": "0.21.3", + "version": "0.22.4", "dependencies": { "@distilled.cloud/core": "workspace:*", }, @@ -281,7 +298,7 @@ }, "packages/stripe": { "name": "@distilled.cloud/stripe", - "version": "0.21.3", + "version": "0.22.4", "dependencies": { "@distilled.cloud/core": "workspace:*", }, @@ -297,7 +314,7 @@ }, "packages/supabase": { "name": "@distilled.cloud/supabase", - "version": "0.21.3", + "version": "0.22.4", "dependencies": { "@distilled.cloud/core": "workspace:*", }, @@ -313,7 +330,7 @@ }, "packages/turso": { "name": "@distilled.cloud/turso", - "version": "0.21.3", + "version": "0.22.4", "dependencies": { "@distilled.cloud/core": "workspace:*", }, @@ -329,7 +346,7 @@ }, "packages/typesense": { "name": "@distilled.cloud/typesense", - "version": "0.21.3", + "version": "0.22.4", "dependencies": { "@distilled.cloud/core": "workspace:*", }, @@ -346,7 +363,7 @@ }, "packages/workos": { "name": "@distilled.cloud/workos", - "version": "0.21.3", + "version": "0.22.4", "dependencies": { "@distilled.cloud/core": "workspace:*", }, @@ -474,6 +491,8 @@ "@distilled.cloud/azure": ["@distilled.cloud/azure@workspace:packages/azure"], + "@distilled.cloud/clerk": ["@distilled.cloud/clerk@workspace:packages/clerk"], + "@distilled.cloud/cloudflare": ["@distilled.cloud/cloudflare@workspace:packages/cloudflare"], "@distilled.cloud/coinbase": ["@distilled.cloud/coinbase@workspace:packages/coinbase"], diff --git a/packages/clerk/README.md b/packages/clerk/README.md new file mode 100644 index 0000000000..d53d715eaa --- /dev/null +++ b/packages/clerk/README.md @@ -0,0 +1,99 @@ +# @distilled.cloud/clerk + +Effect-native Clerk SDK generated from the official Clerk OpenAPI specifications. Covers both APIs Clerk publishes: + +- **Platform API** — workspace / application management (private beta). +- **Backend API** — per-instance resources: users, sessions, organizations, JWTs, OAuth, etc. + +Spec source: . + +## Installation + +```bash +npm install @distilled.cloud/clerk effect +``` + +## Quick Start + +The Backend API covers the operations most server-side integrations need. Authenticate with your instance secret key (`sk_test_...` / `sk_live_...`): + +```typescript +import { Effect, Layer } from "effect"; +import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient"; +import { Backend } from "@distilled.cloud/clerk/Operations"; +import { BackendCredentialsFromEnv } from "@distilled.cloud/clerk"; + +const program = Effect.gen(function* () { + const instance = yield* Backend.GetInstance({}); + return instance; +}); + +const ClerkLive = Layer.mergeAll( + FetchHttpClient.layer, + BackendCredentialsFromEnv, +); + +program.pipe(Effect.provide(ClerkLive), Effect.runPromise); +``` + +Platform API operations are namespaced under `Platform` and require the platform access token credential: + +```typescript +import { Platform } from "@distilled.cloud/clerk/Operations"; +import { PlatformCredentialsFromEnv } from "@distilled.cloud/clerk"; + +const apps = Platform.PlatformListApplications({}); +// provide PlatformCredentialsFromEnv (and FetchHttpClient.layer) +``` + +## Configuration + +Set whichever environment variable matches the API(s) you call: + +```bash +# Backend API (per-instance secret key) +CLERK_SECRET_KEY=sk_test_... + +# Platform API (workspace access token, private beta) +CLERK_PLATFORM_API_TOKEN=... +``` + +`CredentialsFromEnv` provides both layers at once if you use both APIs in the same program. + +## Error Handling + +All Clerk error responses share the same envelope (`{ errors: [{ message, long_message, code, meta }], clerk_trace_id }`). The SDK maps HTTP status codes to the standard typed errors from `@distilled.cloud/core` and falls back to `UnknownClerkError` for anything unrecognised: + +```typescript +import { Effect } from "effect"; +import { Backend } from "@distilled.cloud/clerk/Operations"; +import { NotFound, UnknownClerkError } from "@distilled.cloud/clerk"; + +Backend.GetUser({ user_id: "user_missing" }).pipe( + Effect.catchTags({ + NotFound: () => Effect.succeed(null), + UnknownClerkError: (e: UnknownClerkError) => + Effect.fail(new Error(`Clerk error: ${e.message ?? "unknown"}`)), + }), +); +``` + +## Services + +The Backend API surface is large; here are the main groupings: + +- **Users** — list, create, get, update, delete; ban/unban; lock/unlock; email/phone management +- **Sessions / Clients / Sign-in / Sign-up** — session lifecycle, JWT templates, sign-in tokens, OAuth access tokens +- **Organizations** — organizations, memberships, invitations, domains, roles, permissions +- **Allow-list / Block-list / Waitlist** — identifier-based access controls +- **OAuth applications & Enterprise connections** — SSO and OAuth app management +- **Billing & Commerce** — plans, prices, subscription items, statements, credit balances +- **API keys / M2M tokens** — service account credentials + +Platform API operations are prefixed with `Platform*` and cover applications, application instances, domains, deployments, and application transfers. + +Browse the full list of operations in `src/operations/{platform,backend}/`. + +## License + +MIT diff --git a/packages/clerk/package.json b/packages/clerk/package.json new file mode 100644 index 0000000000..e986e5b5d0 --- /dev/null +++ b/packages/clerk/package.json @@ -0,0 +1,88 @@ +{ + "name": "@distilled.cloud/clerk", + "version": "0.2.0-alpha", + "repository": { + "type": "git", + "url": "https://github.com/alchemy-run/distilled", + "directory": "packages/clerk" + }, + "type": "module", + "sideEffects": false, + "module": "src/index.ts", + "files": [ + "lib", + "src" + ], + "exports": { + ".": { + "types": "./lib/index.d.ts", + "bun": "./src/index.ts", + "default": "./lib/index.js" + }, + "./Category": { + "types": "./lib/category.d.ts", + "bun": "./src/category.ts", + "default": "./lib/category.js" + }, + "./Client": { + "types": "./lib/client.d.ts", + "bun": "./src/client.ts", + "default": "./lib/client.js" + }, + "./Credentials": { + "types": "./lib/credentials.d.ts", + "bun": "./src/credentials.ts", + "default": "./lib/credentials.js" + }, + "./Errors": { + "types": "./lib/errors.d.ts", + "bun": "./src/errors.ts", + "default": "./lib/errors.js" + }, + "./Operations": { + "types": "./lib/operations/index.d.ts", + "bun": "./src/operations/index.ts", + "default": "./lib/operations/index.js" + }, + "./Retry": { + "types": "./lib/retry.d.ts", + "bun": "./src/retry.ts", + "default": "./lib/retry.js" + }, + "./Sensitive": { + "types": "./lib/sensitive.d.ts", + "bun": "./src/sensitive.ts", + "default": "./lib/sensitive.js" + }, + "./Traits": { + "types": "./lib/traits.d.ts", + "bun": "./src/traits.ts", + "default": "./lib/traits.js" + } + }, + "scripts": { + "typecheck": "tsgo", + "build": "tsgo -b", + "fmt": "oxfmt --write src", + "lint": "oxlint --fix src", + "check": "tsgo && oxlint src && oxfmt --check src", + "test": "bunx vitest run test --passWithNoTests", + "publish:npm": "bun run build && bun publish --access public", + "generate": "bun run scripts/generate.ts && oxlint --fix src && oxfmt --write src && oxfmt --write src", + "specs:fetch": "git submodule update --force --init --recursive --depth=1 specs/distilled-spec-clerk && git -C specs/distilled-spec-clerk checkout -- .", + "specs:update": "git -C specs/distilled-spec-clerk fetch && git -C specs/distilled-spec-clerk checkout main && git -C specs/distilled-spec-clerk pull" + }, + "dependencies": { + "@distilled.cloud/core": "workspace:*", + "effect": "catalog:" + }, + "devDependencies": { + "@types/bun": "catalog:", + "@types/node": "catalog:", + "dotenv": "catalog:", + "vitest": "catalog:" + }, + "peerDependencies": { + "effect": "catalog:" + } +} \ No newline at end of file diff --git a/packages/clerk/scripts/generate.ts b/packages/clerk/scripts/generate.ts new file mode 100644 index 0000000000..e89dcd9500 --- /dev/null +++ b/packages/clerk/scripts/generate.ts @@ -0,0 +1,271 @@ +/** + * Clerk SDK Code Generator + * + * Clerk publishes two distinct OpenAPI specs: + * - Platform API (workspace/application management, "platform_api_access_token" bearer) + * - Backend API (per-instance resources, "bearerAuth" with secret key) + * + * Both live at https://api.clerk.com/v1 but require different credentials and + * have largely disjoint operation sets, so we generate them into separate + * subdirectories and wire each subdirectory to its own API client instance: + * + * src/operations/platform/* -> imports { API } from ../../../platform-client + * src/operations/backend/* -> imports { API } from ../../../backend-client + * + * Within each spec, operations are further grouped using Clerk's Speakeasy + * vendor extensions: + * - `x-speakeasy-group` -> the subdirectory + namespace the operation + * lives under (e.g. "sessions", "users") + * - `x-speakeasy-name-override` -> the operation's function/file name within + * that group (e.g. "createToken", "revoke") + * + * So `CreateSessionToken` (group "sessions", name "createToken") becomes + * `src/operations/backend/sessions/createToken.ts` and is reachable as + * `Backend.sessions.createToken`. + * + * The shared OpenAPI generator names operations purely off `operationId` and + * emits a flat directory, so we implement grouping the simplest way possible: + * split each spec into one sub-spec per group (rewriting each operation's + * `operationId` to its name override) and run the generator once per group. + */ +import { generateFromOpenAPI } from "@distilled.cloud/core/openapi/generate"; +import { applyAllPatches } from "@distilled.cloud/core/json-patch"; +import * as fs from "fs"; +import * as os from "os"; +import * as path from "path"; + +const HTTP_METHODS = ["get", "post", "put", "patch", "delete"] as const; +type HttpMethod = (typeof HTTP_METHODS)[number]; + +const rootDir = path.join(import.meta.dir, ".."); +const specDir = path.join(rootDir, "specs/distilled-spec-clerk/specs"); +const patchDir = path.join(rootDir, "patches"); +const operationsRoot = path.join(rootDir, "src/operations"); + +interface SpecConfig { + name: "platform" | "backend"; + specPath: string; + /** Bare client module name under `src/` (e.g. "backend-client"). */ + clientModule: string; + /** + * How to bucket operations into groups/namespaces: + * - "group" -> by `x-speakeasy-group` (falls back to tag, then "misc") + * - "tag" -> by the first OpenAPI tag (falls back to group, then "misc") + * + * The Backend spec authors a distinct `x-speakeasy-group` per resource, but + * the Platform spec lumps everything under a single `platform` group, so we + * fan that one out by tag instead. + */ + groupBy: "group" | "tag"; +} + +const SPECS: SpecConfig[] = [ + { + name: "platform", + specPath: path.join(specDir, "openapi.json"), + clientModule: "platform-client", + groupBy: "tag", + }, + { + name: "backend", + specPath: path.join(specDir, "spec-1.json"), + clientModule: "backend-client", + groupBy: "group", + }, +]; + +const STATUS_TO_ERROR_CLASS: Record = { + "400": "BadRequest", + "402": "PaymentRequired", + "403": "Forbidden", + "404": "NotFound", + "409": "Conflict", + "410": "Gone", + "413": "PayloadTooLarge", + "422": "UnprocessableEntity", +}; + +// Wipe a dir so stale files from removed operations/groups don't accumulate. +function resetDir(dir: string) { + if (fs.existsSync(dir)) { + fs.rmSync(dir, { recursive: true, force: true }); + } + fs.mkdirSync(dir, { recursive: true }); +} + +/** Strip characters that aren't valid in a JS identifier; prefix if leading digit. */ +function sanitizeIdentifier(s: string): string { + const cleaned = s.replace(/[^a-zA-Z0-9$_]/g, ""); + return /^[0-9]/.test(cleaned) ? `_${cleaned}` : cleaned || "misc"; +} + +/** + * camelCase a human label like "API Keys" / "Instance Settings" into a valid + * identifier ("apiKeys" / "instanceSettings"). Used only for the tag fallback; + * Speakeasy groups are already valid camelCase and are passed through as-is so + * we don't mangle e.g. "oauthAccessTokens" into "oauthaccesstokens". + */ +function camelCaseLabel(s: string): string { + // Tag labels are human Title Case ("JWT Templates", "Redirect URLs"), so we + // fully normalize each word — lowercasing acronyms — to match the camelCase + // conventions the Backend spec's own groups use (jwtTemplates, redirectUrls). + const words = s.split(/[-_\s]+/).filter(Boolean); + const camel = words + .map((w, i) => { + const lower = w.toLowerCase(); + return i === 0 ? lower : lower.charAt(0).toUpperCase() + lower.slice(1); + }) + .join(""); + return sanitizeIdentifier(camel); +} + +function speakeasyGroup(operation: Record): string | undefined { + const group = operation["x-speakeasy-group"]; + return typeof group === "string" && group.length > 0 + ? sanitizeIdentifier(group) + : undefined; +} + +function firstTagGroup(operation: Record): string | undefined { + const tags = operation["tags"]; + return Array.isArray(tags) && typeof tags[0] === "string" && tags[0].length + ? camelCaseLabel(tags[0]) + : undefined; +} + +/** + * Determine the group a given operation belongs to, per the spec's `groupBy` + * strategy. Each strategy has a fallback to the other signal, then to "misc". + */ +function groupOf( + operation: Record, + groupBy: SpecConfig["groupBy"], +): string { + const byGroup = speakeasyGroup(operation); + const byTag = firstTagGroup(operation); + const primary = groupBy === "tag" ? byTag : byGroup; + const secondary = groupBy === "tag" ? byGroup : byTag; + return primary ?? secondary ?? "misc"; +} + +type PathItem = Record & { + parameters?: unknown; +}; + +for (const spec of SPECS) { + const raw = JSON.parse(fs.readFileSync(spec.specPath, "utf-8")) as { + paths: Record; + [key: string]: unknown; + }; + + // Apply patches once against the full spec (refs resolve against the whole + // document), then split — so per-group generation never re-patches and patch + // targets are never "missing" just because they live in another group. + const { errors: patchErrors } = applyAllPatches(raw, patchDir); + if (patchErrors.length > 0) { + console.error("Patch errors:"); + for (const msg of patchErrors) console.error(` ✗ ${msg}`); + process.exit(1); + } + + // Bucket each operation under its group, rewriting the operationId to the + // name override so the generator derives function/file/schema names from it. + const groups = new Map>(); + for (const [pathTemplate, pathItem] of Object.entries(raw.paths)) { + const pathLevelParams = pathItem.parameters; + for (const method of HTTP_METHODS) { + const operation = pathItem[method] as + | (Record & { operationId?: string }) + | undefined; + if (!operation) continue; + + const nameOverride = operation["x-speakeasy-name-override"]; + if (typeof nameOverride === "string" && nameOverride.length > 0) { + operation.operationId = nameOverride; + } + + const group = groupOf(operation, spec.groupBy); + let groupPaths = groups.get(group); + if (!groupPaths) { + groupPaths = {}; + groups.set(group, groupPaths); + } + if (!groupPaths[pathTemplate]) { + groupPaths[pathTemplate] = pathLevelParams + ? ({ parameters: pathLevelParams } as PathItem) + : ({} as PathItem); + } + (groupPaths[pathTemplate] as Record)[method] = operation; + } + } + + const specOutputDir = path.join(operationsRoot, spec.name); + resetDir(specOutputDir); + + // Write each group's sub-spec to a temp dir and generate from it. Passing a + // non-existent patch dir makes the generator skip re-patching (already done). + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), `clerk-${spec.name}-`)); + const noPatchDir = path.join(tmpDir, "__no_patches__"); + + const allGroupNames = [...groups.keys()].sort(); + // Track only groups that actually emitted operations — a group whose ops are + // all deprecated produces an empty dir we shouldn't barrel-export. + const emittedGroups: string[] = []; + for (const group of allGroupNames) { + const subSpec = { ...raw, paths: groups.get(group)! }; + const subSpecPath = path.join(tmpDir, `${group}.json`); + fs.writeFileSync(subSpecPath, JSON.stringify(subSpec)); + + const groupOutputDir = path.join(specOutputDir, group); + + generateFromOpenAPI({ + specPath: subSpecPath, + patchDir: noPatchDir, + outputDir: groupOutputDir, + // operations///*.ts -> ../../../ reaches src/ + importPrefix: "../../..", + clientImport: `../../../${spec.clientModule}`, + traitsImport: "../../../traits", + sensitiveImport: "../../../sensitive", + errorsImport: "../../../errors", + includeOperationErrors: true, + // Extend the generator's default status->error-class map with the + // Clerk-specific HTTP codes the spec actually documents but that + // sdk-core's generic map doesn't cover. Without these, 402/410/413 + // responses would fall through to UnknownClerkError instead of + // surfacing as PaymentRequired / Gone / PayloadTooLarge. + statusToErrorClass: STATUS_TO_ERROR_CLASS, + skipDeprecated: true, + }); + + const emittedOps = fs + .readdirSync(groupOutputDir) + .some((f) => f.endsWith(".ts") && f !== "index.ts"); + if (emittedOps) { + emittedGroups.push(group); + } else { + fs.rmSync(groupOutputDir, { recursive: true, force: true }); + } + } + + fs.rmSync(tmpDir, { recursive: true, force: true }); + + // Per-spec barrel: re-export each group as its own namespace, e.g. + // `export * as sessions from "./sessions/index.ts";` + const specBarrel = + emittedGroups.map((g) => `export * as ${g} from "./${g}/index.ts";`).join("\n") + "\n"; + fs.writeFileSync(path.join(specOutputDir, "index.ts"), specBarrel); +} + +// Top-level barrel re-exports both specs as namespaces so callers can write +// `import { Platform, Backend } from "@distilled.cloud/clerk/operations"` and +// then reach a grouped op via `Backend.sessions.createToken`. +const barrelPath = path.join(operationsRoot, "index.ts"); +fs.writeFileSync( + barrelPath, + [ + `export * as Platform from "./platform/index.ts";`, + `export * as Backend from "./backend/index.ts";`, + "", + ].join("\n"), +); diff --git a/packages/clerk/specs/distilled-spec-clerk b/packages/clerk/specs/distilled-spec-clerk new file mode 160000 index 0000000000..f79cdc0c72 --- /dev/null +++ b/packages/clerk/specs/distilled-spec-clerk @@ -0,0 +1 @@ +Subproject commit f79cdc0c72ab185e171742e99a2478dac4b9ba8c diff --git a/packages/clerk/src/backend-client.ts b/packages/clerk/src/backend-client.ts new file mode 100644 index 0000000000..17207c8860 --- /dev/null +++ b/packages/clerk/src/backend-client.ts @@ -0,0 +1,30 @@ +/** + * Clerk Backend API client. + * + * Uses {@link BackendCredentials} (an instance secret key, e.g. + * `sk_test_...` / `sk_live_...`). All `src/operations/backend/*` files + * import `{ API }` from this module. + */ +import * as Effect from "effect/Effect"; +import * as Redacted from "effect/Redacted"; +import { makeAPI } from "@distilled.cloud/core/client"; +import { matchClerkError } from "./match-error.ts"; +import { ClerkParseError } from "./errors.ts"; +import { Retry } from "./retry.ts"; +import { BackendCredentials } from "./credentials.ts"; + +export const API = makeAPI({ + credentials: BackendCredentials as any, + getBaseUrl: (creds: any) => creds.apiBaseUrl, + getAuthHeaders: (creds: any) => ({ + Authorization: `Bearer ${Redacted.value(creds.secretKey)}`, + }), + matchError: matchClerkError as ( + status: number, + body: unknown, + errors?: readonly unknown[], + headers?: Record, + ) => Effect.Effect, + ParseError: ClerkParseError as any, + retry: Retry as any, +}); diff --git a/packages/clerk/src/category.ts b/packages/clerk/src/category.ts new file mode 100644 index 0000000000..a09ac2ecca --- /dev/null +++ b/packages/clerk/src/category.ts @@ -0,0 +1,4 @@ +/** + * Re-export the shared category system from sdk-core. + */ +export * from "@distilled.cloud/core/category"; diff --git a/packages/clerk/src/client.ts b/packages/clerk/src/client.ts new file mode 100644 index 0000000000..db87a85282 --- /dev/null +++ b/packages/clerk/src/client.ts @@ -0,0 +1,16 @@ +/** + * Clerk API clients. + * + * Clerk exposes two separate APIs with different authentication schemes: + * - {@link PlatformAPI} — workspace/application management + * - {@link BackendAPI} — per-instance resources (users, sessions, etc.) + * + * Generated operations under `src/operations/{platform,backend}/` import + * `{ API }` directly from their respective client module, so consumers + * usually don't need to touch these — they just provide the matching + * credentials layer. + */ +export { API as PlatformAPI } from "./platform-client.ts"; +export { API as BackendAPI } from "./backend-client.ts"; +export { matchClerkError } from "./match-error.ts"; +export { UnknownClerkError, ClerkParseError } from "./errors.ts"; diff --git a/packages/clerk/src/credentials.ts b/packages/clerk/src/credentials.ts new file mode 100644 index 0000000000..c222e9947d --- /dev/null +++ b/packages/clerk/src/credentials.ts @@ -0,0 +1,91 @@ +/** + * Clerk has two distinct APIs that share a base URL but use different + * credentials: + * - Platform API: workspace-scoped operations, authenticated with a + * "Platform API access token" (env var: CLERK_PLATFORM_API_TOKEN). + * - Backend API: per-instance operations, authenticated with an instance + * secret key like `sk_test_...` / `sk_live_...` + * (env var: CLERK_SECRET_KEY). + * + * Each API has its own Credentials service so consumers only need to provide + * the token relevant to the operations they actually call. + */ +import { ConfigError } from "@distilled.cloud/core/errors"; +import * as EffectConfig from "effect/Config"; +import * as Context from "effect/Context"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import * as Redacted from "effect/Redacted"; + +export const DEFAULT_API_BASE_URL = "https://api.clerk.com/v1"; + +// --- Platform API ---------------------------------------------------------- + +export interface PlatformConfig { + readonly accessToken: Redacted.Redacted; + readonly apiBaseUrl: string; +} + +export class PlatformCredentials extends Context.Service< + PlatformCredentials, + PlatformConfig +>()("ClerkPlatformCredentials") {} + +const platformEnvConfig = EffectConfig.all({ + accessToken: EffectConfig.string("CLERK_PLATFORM_API_TOKEN"), +}); + +export const PlatformCredentialsFromEnv = Layer.effect( + PlatformCredentials, + platformEnvConfig.pipe( + Effect.mapError( + () => + new ConfigError({ + message: "CLERK_PLATFORM_API_TOKEN environment variable is required", + }), + ), + Effect.map(({ accessToken }) => ({ + accessToken: Redacted.make(accessToken), + apiBaseUrl: DEFAULT_API_BASE_URL, + })), + ), +); + +// --- Backend API ----------------------------------------------------------- + +export interface BackendConfig { + readonly secretKey: Redacted.Redacted; + readonly apiBaseUrl: string; +} + +export class BackendCredentials extends Context.Service< + BackendCredentials, + BackendConfig +>()("ClerkBackendCredentials") {} + +const backendEnvConfig = EffectConfig.all({ + secretKey: EffectConfig.string("CLERK_SECRET_KEY"), +}); + +export const BackendCredentialsFromEnv = Layer.effect( + BackendCredentials, + backendEnvConfig.pipe( + Effect.mapError( + () => + new ConfigError({ + message: "CLERK_SECRET_KEY environment variable is required", + }), + ), + Effect.map(({ secretKey }) => ({ + secretKey: Redacted.make(secretKey), + apiBaseUrl: DEFAULT_API_BASE_URL, + })), + ), +); + +// --- Convenience layer that provides both ----------------------------------- + +export const CredentialsFromEnv = Layer.merge( + PlatformCredentialsFromEnv, + BackendCredentialsFromEnv, +); diff --git a/packages/clerk/src/errors.ts b/packages/clerk/src/errors.ts new file mode 100644 index 0000000000..926879d755 --- /dev/null +++ b/packages/clerk/src/errors.ts @@ -0,0 +1,96 @@ +/** + * Clerk-specific error types. + * + * Re-exports common HTTP errors from sdk-core and adds Clerk-specific + * error matching and API error types. + * + * Clerk error responses share a single envelope (used by both the Platform + * and Backend APIs): + * + * { + * "errors": [{ "message": "...", "long_message": "...", "code": "...", "meta": {} }], + * "meta": { ... }, + * "clerk_trace_id": "..." + * } + */ +export { + BadGateway, + BadRequest, + Conflict, + ConfigError, + Forbidden, + GatewayTimeout, + InternalServerError, + Locked, + NotFound, + ServiceUnavailable, + TooManyRequests, + Unauthorized, + UnprocessableEntity, + HTTP_STATUS_MAP, + DEFAULT_ERRORS, + API_ERRORS, +} from "@distilled.cloud/core/errors"; +export type { DefaultErrors } from "@distilled.cloud/core/errors"; + +import * as Schema from "effect/Schema"; +import * as Category from "@distilled.cloud/core/category"; + +/** A single entry in Clerk's `errors[]` array. */ +export const ClerkErrorItem = Schema.Struct({ + message: Schema.String, + long_message: Schema.optional(Schema.String), + code: Schema.optional(Schema.String), + meta: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)), +}); +export type ClerkErrorItem = Schema.Schema.Type; + +/** + * PaymentRequired (402) — operation requires a paid Clerk plan or a feature + * the current plan does not include (e.g. JWT templates on a free instance, + * paid auth features, organization features). Documented across ~40 backend + * operations and several Platform-API config endpoints. + */ +export class PaymentRequired extends Schema.TaggedErrorClass()( + "PaymentRequired", + { message: Schema.String }, +).pipe(Category.withQuotaError) {} + +/** + * Gone (410) — endpoint or resource has been permanently removed. Currently + * documented on Clerk's deprecated `GET /clients` listing endpoint. + */ +export class Gone extends Schema.TaggedErrorClass()("Gone", { + message: Schema.String, +}).pipe(Category.withNotFoundError) {} + +/** + * PayloadTooLarge (413) — request body exceeds the per-endpoint size limit. + * Documented on Clerk's logo/favicon upload endpoints (organization logo, + * OAuth application logo, application logo / favicon). + */ +export class PayloadTooLarge extends Schema.TaggedErrorClass()( + "PayloadTooLarge", + { message: Schema.String }, +).pipe(Category.withBadRequestError) {} + +/** Unknown / unmatched Clerk error response. */ +export class UnknownClerkError extends Schema.TaggedErrorClass()( + "UnknownClerkError", + { + code: Schema.optional(Schema.String), + message: Schema.optional(Schema.String), + errors: Schema.optional(Schema.Array(ClerkErrorItem)), + clerk_trace_id: Schema.optional(Schema.String), + body: Schema.Unknown, + }, +).pipe(Category.withServerError) {} + +/** Schema parse error wrapper. */ +export class ClerkParseError extends Schema.TaggedErrorClass()( + "ClerkParseError", + { + body: Schema.Unknown, + cause: Schema.Unknown, + }, +).pipe(Category.withParseError) {} diff --git a/packages/clerk/src/index.ts b/packages/clerk/src/index.ts new file mode 100644 index 0000000000..d848431650 --- /dev/null +++ b/packages/clerk/src/index.ts @@ -0,0 +1,21 @@ +/** + * Clerk SDK for Effect + * + * Clerk exposes two distinct APIs sharing one base URL: + * - Platform API — workspace/application management (PlatformAPI / PlatformCredentials) + * - Backend API — per-instance resources (BackendAPI / BackendCredentials) + * + * @example + * ```ts + * import * as Clerk from "@distilled.cloud/clerk"; + * import { Backend } from "@distilled.cloud/clerk/Operations"; + * ``` + */ +export * from "./credentials.ts"; +export * as Category from "./category.ts"; +export * as T from "./traits.ts"; +export * as Retry from "./retry.ts"; +export { PlatformAPI, BackendAPI } from "./client.ts"; +export * from "./errors.ts"; +export * from "./operations/index.ts"; +export { SensitiveString, SensitiveNullableString } from "./sensitive.ts"; diff --git a/packages/clerk/src/match-error.ts b/packages/clerk/src/match-error.ts new file mode 100644 index 0000000000..7d6635dcbf --- /dev/null +++ b/packages/clerk/src/match-error.ts @@ -0,0 +1,78 @@ +/** + * Shared error matcher used by both the Platform and Backend Clerk API + * clients. Clerk responses use a single envelope for failures: + * + * { "errors": [{ "message", "long_message", "code", "meta" }], + * "meta": {...}, "clerk_trace_id": "..." } + */ +import * as Effect from "effect/Effect"; +import * as Schema from "effect/Schema"; +import { parseRetryAfterForStatus } from "@distilled.cloud/core/retry-after"; +import { + ClerkErrorItem, + Gone, + HTTP_STATUS_MAP, + PayloadTooLarge, + PaymentRequired, + UnknownClerkError, +} from "./errors.ts"; + +/** + * Clerk extends the standard HTTP status -> error class map with three codes + * that the spec documents but which sdk-core does not map by default: + * - 402 PaymentRequired — billing-gated features + * - 410 Gone — deprecated/removed endpoints (e.g. GET /clients) + * - 413 PayloadTooLarge — logo / favicon uploads + */ +const CLERK_STATUS_MAP: Record = { + ...HTTP_STATUS_MAP, + 402: PaymentRequired, + 410: Gone, + 413: PayloadTooLarge, +}; + +const ClerkErrorEnvelope = Schema.Struct({ + errors: Schema.Array(ClerkErrorItem), + meta: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)), + clerk_trace_id: Schema.optional(Schema.String), +}); + +const decodeEnvelope = Schema.decodeUnknownSync(ClerkErrorEnvelope); + +export const matchClerkError = ( + status: number, + errorBody: unknown, + _errors?: readonly unknown[], + headers?: Record, +): Effect.Effect => { + let envelope: Schema.Schema.Type | undefined; + try { + envelope = decodeEnvelope(errorBody); + } catch { + envelope = undefined; + } + + const first = envelope?.errors[0]; + const message = first?.message ?? ""; + const code = first?.code; + + const ErrorClass = CLERK_STATUS_MAP[status]; + if (ErrorClass) { + return Effect.fail( + new ErrorClass({ + message, + retryAfter: parseRetryAfterForStatus(status, headers), + }), + ); + } + + return Effect.fail( + new UnknownClerkError({ + code, + message: message || undefined, + errors: envelope?.errors, + clerk_trace_id: envelope?.clerk_trace_id, + body: errorBody, + }), + ); +}; diff --git a/packages/clerk/src/operations/backend/actorTokens/create.ts b/packages/clerk/src/operations/backend/actorTokens/create.ts new file mode 100644 index 0000000000..7c8149cc3d --- /dev/null +++ b/packages/clerk/src/operations/backend/actorTokens/create.ts @@ -0,0 +1,46 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String, + actor: Schema.Struct({ + sub: Schema.String, + }), + expires_in_seconds: Schema.optional(Schema.Number), + session_max_duration_in_seconds: Schema.optional(Schema.Number), +}).pipe(T.Http({ method: "POST", path: "/actor_tokens" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["actor_token"]), + id: Schema.String, + status: Schema.Literals(["pending", "accepted", "revoked"]), + user_id: Schema.String, + actor: Schema.Unknown, + token: Schema.optional(Schema.String), + url: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create actor token + * + * Create an actor token that can be used to impersonate the given user. + * The `actor` parameter needs to include at least a "sub" key whose value is the ID of the actor (impersonating) user. + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [BadRequest, PaymentRequired, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/actorTokens/index.ts b/packages/clerk/src/operations/backend/actorTokens/index.ts new file mode 100644 index 0000000000..35fd19e8dd --- /dev/null +++ b/packages/clerk/src/operations/backend/actorTokens/index.ts @@ -0,0 +1,2 @@ +export * from "./create.ts"; +export * from "./revoke.ts"; diff --git a/packages/clerk/src/operations/backend/actorTokens/revoke.ts b/packages/clerk/src/operations/backend/actorTokens/revoke.ts new file mode 100644 index 0000000000..81f8f281a9 --- /dev/null +++ b/packages/clerk/src/operations/backend/actorTokens/revoke.ts @@ -0,0 +1,40 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; + +// Input Schema +export const RevokeInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + actor_token_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ method: "POST", path: "/actor_tokens/{actor_token_id}/revoke" }), +); +export type RevokeInput = typeof RevokeInput.Type; + +// Output Schema +export const RevokeOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["actor_token"]), + id: Schema.String, + status: Schema.Literals(["pending", "accepted", "revoked"]), + user_id: Schema.String, + actor: Schema.Unknown, + token: Schema.optional(Schema.String), + url: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type RevokeOutput = typeof RevokeOutput.Type; + +// The operation +/** + * Revoke actor token + * + * Revokes a pending actor token. + * + * @param actor_token_id - The ID of the actor token to be revoked. + */ +export const revoke = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: RevokeInput, + outputSchema: RevokeOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/adminPortalLinkTokens/createAdminPortalLinkToken.ts b/packages/clerk/src/operations/backend/adminPortalLinkTokens/createAdminPortalLinkToken.ts new file mode 100644 index 0000000000..ea376d34e1 --- /dev/null +++ b/packages/clerk/src/operations/backend/adminPortalLinkTokens/createAdminPortalLinkToken.ts @@ -0,0 +1,48 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, Conflict } from "../../../errors.ts"; + +// Input Schema +export const CreateAdminPortalLinkTokenInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.optional(Schema.String), + it_contact_id: Schema.optional(Schema.String), + scopes: Schema.optional(Schema.Array(Schema.String)), + seconds_until_expiration: Schema.optional(Schema.Number), + }).pipe(T.Http({ method: "POST", path: "/admin_portal_link_tokens" })); +export type CreateAdminPortalLinkTokenInput = + typeof CreateAdminPortalLinkTokenInput.Type; + +// Output Schema +export const CreateAdminPortalLinkTokenOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["admin_portal_link_token"]), + id: Schema.String, + admin_portal_link_id: Schema.String, + instance_id: Schema.String, + organization_id: Schema.NullOr(Schema.String), + it_contact_id: Schema.NullOr(Schema.String), + scopes: Schema.NullOr(Schema.Array(Schema.String)), + token: Schema.String, + revoked: Schema.Boolean, + revocation_reason: Schema.NullOr(Schema.String), + expired: Schema.Boolean, + expiration: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, + }); +export type CreateAdminPortalLinkTokenOutput = + typeof CreateAdminPortalLinkTokenOutput.Type; + +// The operation +/** + * Create an Admin Portal Link Token + */ +export const createAdminPortalLinkToken = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: CreateAdminPortalLinkTokenInput, + outputSchema: CreateAdminPortalLinkTokenOutput, + errors: [BadRequest, Forbidden, Conflict] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/adminPortalLinkTokens/index.ts b/packages/clerk/src/operations/backend/adminPortalLinkTokens/index.ts new file mode 100644 index 0000000000..ea6d5a1adc --- /dev/null +++ b/packages/clerk/src/operations/backend/adminPortalLinkTokens/index.ts @@ -0,0 +1,2 @@ +export * from "./createAdminPortalLinkToken.ts"; +export * from "./revokeAdminPortalLinkToken.ts"; diff --git a/packages/clerk/src/operations/backend/adminPortalLinkTokens/revokeAdminPortalLinkToken.ts b/packages/clerk/src/operations/backend/adminPortalLinkTokens/revokeAdminPortalLinkToken.ts new file mode 100644 index 0000000000..db17d784d8 --- /dev/null +++ b/packages/clerk/src/operations/backend/adminPortalLinkTokens/revokeAdminPortalLinkToken.ts @@ -0,0 +1,50 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const RevokeAdminPortalLinkTokenInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + adminPortalLinkTokenID: Schema.String.pipe(T.PathParam()), + revocation_reason: Schema.optional(Schema.NullOr(Schema.String)), + }).pipe( + T.Http({ + method: "POST", + path: "/admin_portal_link_tokens/{adminPortalLinkTokenID}/revoke", + }), + ); +export type RevokeAdminPortalLinkTokenInput = + typeof RevokeAdminPortalLinkTokenInput.Type; + +// Output Schema +export const RevokeAdminPortalLinkTokenOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["admin_portal_link_token"]), + id: Schema.String, + admin_portal_link_id: Schema.String, + instance_id: Schema.String, + organization_id: Schema.NullOr(Schema.String), + it_contact_id: Schema.NullOr(Schema.String), + scopes: Schema.NullOr(Schema.Array(Schema.String)), + revoked: Schema.Boolean, + revocation_reason: Schema.NullOr(Schema.String), + expired: Schema.Boolean, + expiration: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, + }); +export type RevokeAdminPortalLinkTokenOutput = + typeof RevokeAdminPortalLinkTokenOutput.Type; + +// The operation +/** + * Revoke an Admin Portal Link Token + */ +export const revokeAdminPortalLinkToken = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: RevokeAdminPortalLinkTokenInput, + outputSchema: RevokeAdminPortalLinkTokenOutput, + errors: [BadRequest, Forbidden, NotFound] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/agentTasks/create.ts b/packages/clerk/src/operations/backend/agentTasks/create.ts new file mode 100644 index 0000000000..5ccca3dc6f --- /dev/null +++ b/packages/clerk/src/operations/backend/agentTasks/create.ts @@ -0,0 +1,42 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + on_behalf_of: Schema.Struct({ + user_id: Schema.optional(Schema.String), + identifier: Schema.optional(Schema.String), + }), + permissions: Schema.Literals(["*"]), + agent_name: Schema.String, + task_description: Schema.String, + redirect_url: Schema.String, + session_max_duration_in_seconds: Schema.optional(Schema.Number), +}).pipe(T.Http({ method: "POST", path: "/agents/tasks" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["agent_task"]), + agent_id: Schema.String, + agent_task_id: Schema.String, + task_id: Schema.String, + url: Schema.optional(Schema.String), +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create agent task + * + * Create an agent task on behalf of a user. + * The response contains a URL that, when visited, creates a session for the user. + * The agent_id is stable per agent_name within an instance. The agent_task_id is unique per call. + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [BadRequest, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/agentTasks/index.ts b/packages/clerk/src/operations/backend/agentTasks/index.ts new file mode 100644 index 0000000000..35fd19e8dd --- /dev/null +++ b/packages/clerk/src/operations/backend/agentTasks/index.ts @@ -0,0 +1,2 @@ +export * from "./create.ts"; +export * from "./revoke.ts"; diff --git a/packages/clerk/src/operations/backend/agentTasks/revoke.ts b/packages/clerk/src/operations/backend/agentTasks/revoke.ts new file mode 100644 index 0000000000..22e728dc5e --- /dev/null +++ b/packages/clerk/src/operations/backend/agentTasks/revoke.ts @@ -0,0 +1,36 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; + +// Input Schema +export const RevokeInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + agent_task_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ method: "POST", path: "/agents/tasks/{agent_task_id}/revoke" }), +); +export type RevokeInput = typeof RevokeInput.Type; + +// Output Schema +export const RevokeOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["agent_task"]), + agent_id: Schema.String, + agent_task_id: Schema.String, + task_id: Schema.String, + url: Schema.optional(Schema.String), +}); +export type RevokeOutput = typeof RevokeOutput.Type; + +// The operation +/** + * Revoke agent task + * + * Revokes a pending agent task. + * + * @param agent_task_id - The ID of the agent task to be revoked. + */ +export const revoke = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: RevokeInput, + outputSchema: RevokeOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/allowlistIdentifiers/create.ts b/packages/clerk/src/operations/backend/allowlistIdentifiers/create.ts new file mode 100644 index 0000000000..f1a1554c96 --- /dev/null +++ b/packages/clerk/src/operations/backend/allowlistIdentifiers/create.ts @@ -0,0 +1,42 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + identifier: Schema.String, + notify: Schema.optional(Schema.Boolean), +}).pipe(T.Http({ method: "POST", path: "/allowlist_identifiers" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.optional(Schema.Literals(["allowlist_identifier"])), + id: Schema.optional(Schema.String), + invitation_id: Schema.optional(Schema.String), + identifier: Schema.optional(Schema.String), + identifier_type: Schema.optional( + Schema.Literals(["email_address", "phone_number", "web3_wallet"]), + ), + instance_id: Schema.optional(Schema.String), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Add identifier to the allow-list + * + * Create an identifier allowed to sign up to an instance + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [BadRequest, PaymentRequired, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/allowlistIdentifiers/delete.ts b/packages/clerk/src/operations/backend/allowlistIdentifiers/delete.ts new file mode 100644 index 0000000000..c8aa70546d --- /dev/null +++ b/packages/clerk/src/operations/backend/allowlistIdentifiers/delete.ts @@ -0,0 +1,37 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { PaymentRequired, NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + identifier_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ method: "DELETE", path: "/allowlist_identifiers/{identifier_id}" }), +); +export type DeleteInput = typeof DeleteInput.Type; + +// Output Schema +export const DeleteOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), +}); +export type DeleteOutput = typeof DeleteOutput.Type; + +// The operation +/** + * Delete identifier from allow-list + * + * Delete an identifier from the instance allow-list + * + * @param identifier_id - The ID of the identifier to delete from the allow-list + */ +const delete_ = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteInput, + outputSchema: DeleteOutput, + errors: [PaymentRequired, NotFound] as const, +})); +export { delete_ as delete }; diff --git a/packages/clerk/src/operations/backend/allowlistIdentifiers/index.ts b/packages/clerk/src/operations/backend/allowlistIdentifiers/index.ts new file mode 100644 index 0000000000..4daf9f0205 --- /dev/null +++ b/packages/clerk/src/operations/backend/allowlistIdentifiers/index.ts @@ -0,0 +1,3 @@ +export * from "./list.ts"; +export * from "./create.ts"; +export * from "./delete.ts"; diff --git a/packages/clerk/src/operations/backend/allowlistIdentifiers/list.ts b/packages/clerk/src/operations/backend/allowlistIdentifiers/list.ts new file mode 100644 index 0000000000..845f70a620 --- /dev/null +++ b/packages/clerk/src/operations/backend/allowlistIdentifiers/list.ts @@ -0,0 +1,50 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { PaymentRequired } from "../../../errors.ts"; + +// Input Schema +export const ListInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + paginated: Schema.optional(Schema.Boolean), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), +}).pipe(T.Http({ method: "GET", path: "/allowlist_identifiers" })); +export type ListInput = typeof ListInput.Type; + +// Output Schema +export const ListOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Array( + Schema.Struct({ + object: Schema.optional(Schema.Literals(["allowlist_identifier"])), + id: Schema.optional(Schema.String), + invitation_id: Schema.optional(Schema.String), + identifier: Schema.optional(Schema.String), + identifier_type: Schema.optional( + Schema.Literals(["email_address", "phone_number", "web3_wallet"]), + ), + instance_id: Schema.optional(Schema.String), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + }), +); +export type ListOutput = typeof ListOutput.Type; + +// The operation +/** + * List all identifiers on the allow-list + * + * Get a list of all identifiers allowed to sign up to an instance + * + * @param paginated - Whether to paginate the results. +If true, the results will be paginated. +If false, the results will not be paginated. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + */ +export const list = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInput, + outputSchema: ListOutput, + errors: [PaymentRequired] as const, +})); diff --git a/packages/clerk/src/operations/backend/apiKeys/createApiKey.ts b/packages/clerk/src/operations/backend/apiKeys/createApiKey.ts new file mode 100644 index 0000000000..64f89cde58 --- /dev/null +++ b/packages/clerk/src/operations/backend/apiKeys/createApiKey.ts @@ -0,0 +1,50 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Conflict } from "../../../errors.ts"; +import { SensitiveOutputString } from "../../../sensitive.ts"; + +// Input Schema +export const CreateApiKeyInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + type: Schema.optional(Schema.String), + name: Schema.String, + description: Schema.optional(Schema.NullOr(Schema.String)), + subject: Schema.String, + claims: Schema.optional(Schema.NullOr(Schema.Unknown)), + scopes: Schema.optional(Schema.Array(Schema.String)), + created_by: Schema.optional(Schema.NullOr(Schema.String)), + seconds_until_expiration: Schema.optional(Schema.NullOr(Schema.Number)), +}).pipe(T.Http({ method: "POST", path: "/api_keys" })); +export type CreateApiKeyInput = typeof CreateApiKeyInput.Type; + +// Output Schema +export const CreateApiKeyOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["api_key"]), + id: Schema.String, + type: Schema.String, + subject: Schema.String, + name: Schema.String, + description: Schema.optional(Schema.NullOr(Schema.String)), + claims: Schema.NullOr(Schema.Unknown), + scopes: Schema.Array(Schema.String), + secret: SensitiveOutputString, + revoked: Schema.Boolean, + revocation_reason: Schema.NullOr(Schema.String), + expired: Schema.Boolean, + expiration: Schema.NullOr(Schema.Number), + created_by: Schema.NullOr(Schema.String), + last_used_at: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type CreateApiKeyOutput = typeof CreateApiKeyOutput.Type; + +// The operation +/** + * Create an API Key + */ +export const createApiKey = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateApiKeyInput, + outputSchema: CreateApiKeyOutput, + errors: [BadRequest, Conflict] as const, +})); diff --git a/packages/clerk/src/operations/backend/apiKeys/deleteApiKey.ts b/packages/clerk/src/operations/backend/apiKeys/deleteApiKey.ts new file mode 100644 index 0000000000..47bd830210 --- /dev/null +++ b/packages/clerk/src/operations/backend/apiKeys/deleteApiKey.ts @@ -0,0 +1,28 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteApiKeyInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + apiKeyID: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "DELETE", path: "/api_keys/{apiKeyID}" })); +export type DeleteApiKeyInput = typeof DeleteApiKeyInput.Type; + +// Output Schema +export const DeleteApiKeyOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["api_key"]), + deleted: Schema.Literals(["true"]), +}); +export type DeleteApiKeyOutput = typeof DeleteApiKeyOutput.Type; + +// The operation +/** + * Delete an API Key + */ +export const deleteApiKey = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteApiKeyInput, + outputSchema: DeleteApiKeyOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/apiKeys/getApiKey.ts b/packages/clerk/src/operations/backend/apiKeys/getApiKey.ts new file mode 100644 index 0000000000..02de3169b4 --- /dev/null +++ b/packages/clerk/src/operations/backend/apiKeys/getApiKey.ts @@ -0,0 +1,41 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetApiKeyInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + apiKeyID: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "GET", path: "/api_keys/{apiKeyID}" })); +export type GetApiKeyInput = typeof GetApiKeyInput.Type; + +// Output Schema +export const GetApiKeyOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["api_key"]), + id: Schema.String, + type: Schema.String, + subject: Schema.String, + name: Schema.String, + description: Schema.optional(Schema.NullOr(Schema.String)), + claims: Schema.NullOr(Schema.Unknown), + scopes: Schema.Array(Schema.String), + revoked: Schema.Boolean, + revocation_reason: Schema.NullOr(Schema.String), + expired: Schema.Boolean, + expiration: Schema.NullOr(Schema.Number), + created_by: Schema.NullOr(Schema.String), + last_used_at: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type GetApiKeyOutput = typeof GetApiKeyOutput.Type; + +// The operation +/** + * Get an API Key by ID + */ +export const getApiKey = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetApiKeyInput, + outputSchema: GetApiKeyOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/apiKeys/getApiKeySecret.ts b/packages/clerk/src/operations/backend/apiKeys/getApiKeySecret.ts new file mode 100644 index 0000000000..874c622355 --- /dev/null +++ b/packages/clerk/src/operations/backend/apiKeys/getApiKeySecret.ts @@ -0,0 +1,27 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; +import { SensitiveOutputString } from "../../../sensitive.ts"; + +// Input Schema +export const GetApiKeySecretInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + apiKeyID: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "GET", path: "/api_keys/{apiKeyID}/secret" })); +export type GetApiKeySecretInput = typeof GetApiKeySecretInput.Type; + +// Output Schema +export const GetApiKeySecretOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + secret: SensitiveOutputString, +}); +export type GetApiKeySecretOutput = typeof GetApiKeySecretOutput.Type; + +// The operation +/** + * Get an API Key Secret + */ +export const getApiKeySecret = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetApiKeySecretInput, + outputSchema: GetApiKeySecretOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/apiKeys/getApiKeys.ts b/packages/clerk/src/operations/backend/apiKeys/getApiKeys.ts new file mode 100644 index 0000000000..51b8e12a1b --- /dev/null +++ b/packages/clerk/src/operations/backend/apiKeys/getApiKeys.ts @@ -0,0 +1,51 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetApiKeysInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + type: Schema.optional(Schema.String), + subject: Schema.String, + include_invalid: Schema.optional(Schema.Literals(["true", "false"])), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), + query: Schema.optional(Schema.String), +}).pipe(T.Http({ method: "GET", path: "/api_keys" })); +export type GetApiKeysInput = typeof GetApiKeysInput.Type; + +// Output Schema +export const GetApiKeysOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["api_key"]), + id: Schema.String, + type: Schema.String, + subject: Schema.String, + name: Schema.String, + description: Schema.optional(Schema.NullOr(Schema.String)), + claims: Schema.NullOr(Schema.Unknown), + scopes: Schema.Array(Schema.String), + revoked: Schema.Boolean, + revocation_reason: Schema.NullOr(Schema.String), + expired: Schema.Boolean, + expiration: Schema.NullOr(Schema.Number), + created_by: Schema.NullOr(Schema.String), + last_used_at: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + total_count: Schema.Number, +}); +export type GetApiKeysOutput = typeof GetApiKeysOutput.Type; + +// The operation +/** + * Get API Keys + */ +export const getApiKeys = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetApiKeysInput, + outputSchema: GetApiKeysOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/apiKeys/index.ts b/packages/clerk/src/operations/backend/apiKeys/index.ts new file mode 100644 index 0000000000..29972f63aa --- /dev/null +++ b/packages/clerk/src/operations/backend/apiKeys/index.ts @@ -0,0 +1,8 @@ +export * from "./getApiKeys.ts"; +export * from "./createApiKey.ts"; +export * from "./getApiKey.ts"; +export * from "./updateApiKey.ts"; +export * from "./deleteApiKey.ts"; +export * from "./getApiKeySecret.ts"; +export * from "./revokeApiKey.ts"; +export * from "./verifyApiKey.ts"; diff --git a/packages/clerk/src/operations/backend/apiKeys/revokeApiKey.ts b/packages/clerk/src/operations/backend/apiKeys/revokeApiKey.ts new file mode 100644 index 0000000000..09084cb63c --- /dev/null +++ b/packages/clerk/src/operations/backend/apiKeys/revokeApiKey.ts @@ -0,0 +1,42 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; + +// Input Schema +export const RevokeApiKeyInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + apiKeyID: Schema.String.pipe(T.PathParam()), + revocation_reason: Schema.optional(Schema.NullOr(Schema.String)), +}).pipe(T.Http({ method: "POST", path: "/api_keys/{apiKeyID}/revoke" })); +export type RevokeApiKeyInput = typeof RevokeApiKeyInput.Type; + +// Output Schema +export const RevokeApiKeyOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["api_key"]), + id: Schema.String, + type: Schema.String, + subject: Schema.String, + name: Schema.String, + description: Schema.optional(Schema.NullOr(Schema.String)), + claims: Schema.NullOr(Schema.Unknown), + scopes: Schema.Array(Schema.String), + revoked: Schema.Boolean, + revocation_reason: Schema.NullOr(Schema.String), + expired: Schema.Boolean, + expiration: Schema.NullOr(Schema.Number), + created_by: Schema.NullOr(Schema.String), + last_used_at: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type RevokeApiKeyOutput = typeof RevokeApiKeyOutput.Type; + +// The operation +/** + * Revoke an API Key + */ +export const revokeApiKey = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: RevokeApiKeyInput, + outputSchema: RevokeApiKeyOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/apiKeys/updateApiKey.ts b/packages/clerk/src/operations/backend/apiKeys/updateApiKey.ts new file mode 100644 index 0000000000..97a6afe9a0 --- /dev/null +++ b/packages/clerk/src/operations/backend/apiKeys/updateApiKey.ts @@ -0,0 +1,46 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; + +// Input Schema +export const UpdateApiKeyInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + apiKeyID: Schema.String.pipe(T.PathParam()), + claims: Schema.optional(Schema.NullOr(Schema.Unknown)), + scopes: Schema.optional(Schema.Array(Schema.String)), + description: Schema.optional(Schema.NullOr(Schema.String)), + subject: Schema.optional(Schema.String), + seconds_until_expiration: Schema.optional(Schema.NullOr(Schema.Number)), +}).pipe(T.Http({ method: "PATCH", path: "/api_keys/{apiKeyID}" })); +export type UpdateApiKeyInput = typeof UpdateApiKeyInput.Type; + +// Output Schema +export const UpdateApiKeyOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["api_key"]), + id: Schema.String, + type: Schema.String, + subject: Schema.String, + name: Schema.String, + description: Schema.optional(Schema.NullOr(Schema.String)), + claims: Schema.NullOr(Schema.Unknown), + scopes: Schema.Array(Schema.String), + revoked: Schema.Boolean, + revocation_reason: Schema.NullOr(Schema.String), + expired: Schema.Boolean, + expiration: Schema.NullOr(Schema.Number), + created_by: Schema.NullOr(Schema.String), + last_used_at: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type UpdateApiKeyOutput = typeof UpdateApiKeyOutput.Type; + +// The operation +/** + * Update an API Key + */ +export const updateApiKey = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateApiKeyInput, + outputSchema: UpdateApiKeyOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/apiKeys/verifyApiKey.ts b/packages/clerk/src/operations/backend/apiKeys/verifyApiKey.ts new file mode 100644 index 0000000000..a3b3e2df36 --- /dev/null +++ b/packages/clerk/src/operations/backend/apiKeys/verifyApiKey.ts @@ -0,0 +1,42 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; +import { SensitiveString } from "../../../sensitive.ts"; + +// Input Schema +export const VerifyApiKeyInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + secret: SensitiveString, +}).pipe(T.Http({ method: "POST", path: "/api_keys/verify" })); +export type VerifyApiKeyInput = typeof VerifyApiKeyInput.Type; + +// Output Schema +export const VerifyApiKeyOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["api_key"]), + id: Schema.String, + type: Schema.String, + subject: Schema.String, + name: Schema.String, + description: Schema.optional(Schema.NullOr(Schema.String)), + claims: Schema.NullOr(Schema.Unknown), + scopes: Schema.Array(Schema.String), + revoked: Schema.Boolean, + revocation_reason: Schema.NullOr(Schema.String), + expired: Schema.Boolean, + expiration: Schema.NullOr(Schema.Number), + created_by: Schema.NullOr(Schema.String), + last_used_at: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type VerifyApiKeyOutput = typeof VerifyApiKeyOutput.Type; + +// The operation +/** + * Verify an API Key + */ +export const verifyApiKey = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: VerifyApiKeyInput, + outputSchema: VerifyApiKeyOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/betaFeatures/index.ts b/packages/clerk/src/operations/backend/betaFeatures/index.ts new file mode 100644 index 0000000000..a1e5fa26ba --- /dev/null +++ b/packages/clerk/src/operations/backend/betaFeatures/index.ts @@ -0,0 +1 @@ +export * from "./updateInstanceSettings.ts"; diff --git a/packages/clerk/src/operations/backend/betaFeatures/updateInstanceSettings.ts b/packages/clerk/src/operations/backend/betaFeatures/updateInstanceSettings.ts new file mode 100644 index 0000000000..d1f793350f --- /dev/null +++ b/packages/clerk/src/operations/backend/betaFeatures/updateInstanceSettings.ts @@ -0,0 +1,44 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { PaymentRequired, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const UpdateInstanceSettingsInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + restricted_to_allowlist: Schema.optional(Schema.NullOr(Schema.Boolean)), + from_email_address: Schema.optional(Schema.NullOr(Schema.String)), + progressive_sign_up: Schema.optional(Schema.NullOr(Schema.Boolean)), + test_mode: Schema.optional(Schema.NullOr(Schema.Boolean)), + }).pipe( + T.Http({ method: "PATCH", path: "/beta_features/instance_settings" }), + ); +export type UpdateInstanceSettingsInput = + typeof UpdateInstanceSettingsInput.Type; + +// Output Schema +export const UpdateInstanceSettingsOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.optional(Schema.Literals(["instance_settings"])), + id: Schema.optional(Schema.String), + restricted_to_allowlist: Schema.optional(Schema.Boolean), + from_email_address: Schema.optional(Schema.String), + progressive_sign_up: Schema.optional(Schema.Boolean), + enhanced_email_deliverability: Schema.optional(Schema.Boolean), + }); +export type UpdateInstanceSettingsOutput = + typeof UpdateInstanceSettingsOutput.Type; + +// The operation +/** + * Update instance settings + * + * Updates the settings of an instance + */ +export const updateInstanceSettings = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: UpdateInstanceSettingsInput, + outputSchema: UpdateInstanceSettingsOutput, + errors: [PaymentRequired, UnprocessableEntity] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/billing/cancelSubscriptionItem.ts b/packages/clerk/src/operations/backend/billing/cancelSubscriptionItem.ts new file mode 100644 index 0000000000..e51ea6ba81 --- /dev/null +++ b/packages/clerk/src/operations/backend/billing/cancelSubscriptionItem.ts @@ -0,0 +1,399 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const CancelSubscriptionItemInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + subscription_item_id: Schema.String.pipe(T.PathParam()), + end_now: Schema.optional(Schema.Boolean), + }).pipe( + T.Http({ + method: "DELETE", + path: "/billing/subscription_items/{subscription_item_id}", + }), + ); +export type CancelSubscriptionItemInput = + typeof CancelSubscriptionItemInput.Type; + +// Output Schema +export const CancelSubscriptionItemOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["commerce_subscription_item"]), + id: Schema.String, + instance_id: Schema.String, + status: Schema.Literals([ + "active", + "canceled", + "expired", + "ended", + "past_due", + "upcoming", + "incomplete", + "abandoned", + ]), + credit: Schema.optional( + Schema.Struct({ + amount: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + ), + cycle_remaining_percent: Schema.optional(Schema.Number), + }), + ), + credits: Schema.optional( + Schema.NullOr( + Schema.Struct({ + proration: Schema.NullOr( + Schema.Struct({ + amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + cycle_days_remaining: Schema.Number, + cycle_days_total: Schema.Number, + cycle_remaining_percent: Schema.Number, + }), + ), + payer: Schema.NullOr( + Schema.Struct({ + remaining_balance: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + applied_amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + ), + plan_id: Schema.NullOr(Schema.String), + price_id: Schema.optional(Schema.String), + plan: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["commerce_plan"]), + id: Schema.String, + name: Schema.String, + fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + annual_monthly_fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + annual_fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + description: Schema.NullOr(Schema.String), + product_id: Schema.String, + is_default: Schema.Boolean, + is_recurring: Schema.Boolean, + publicly_visible: Schema.Boolean, + has_base_fee: Schema.Boolean, + for_payer_type: Schema.String, + slug: Schema.String, + avatar_url: Schema.NullOr(Schema.String), + features: Schema.optional( + Schema.Array( + Schema.Struct({ + object: Schema.Literals(["feature"]), + id: Schema.String, + name: Schema.String, + description: Schema.NullOr(Schema.String), + slug: Schema.String, + avatar_url: Schema.NullOr(Schema.String), + }), + ), + ), + free_trial_enabled: Schema.Boolean, + free_trial_days: Schema.NullOr(Schema.Number), + unit_prices: Schema.optional( + Schema.Array( + Schema.Struct({ + name: Schema.String, + block_size: Schema.Number, + tiers: Schema.Array( + Schema.Struct({ + starts_at_block: Schema.Number, + ends_after_block: Schema.optional( + Schema.NullOr(Schema.Number), + ), + fee_per_block: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + }), + ), + ), + }), + ), + ), + plan_period: Schema.Literals(["month", "annual"]), + payment_method: Schema.optional( + Schema.Struct({ + object: Schema.Literals(["commerce_payment_method"]), + id: Schema.String, + payer_id: Schema.String, + payment_type: Schema.Literals(["card", "link"]), + is_default: Schema.optional(Schema.Boolean), + gateway: Schema.String, + gateway_external_id: Schema.String, + gateway_external_account_id: Schema.NullOr(Schema.String), + last4: Schema.NullOr(Schema.String), + status: Schema.Literals(["active", "disconnected"]), + wallet_type: Schema.optional(Schema.NullOr(Schema.String)), + card_type: Schema.NullOr(Schema.String), + expiry_year: Schema.optional(Schema.NullOr(Schema.Number)), + expiry_month: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + is_removable: Schema.optional(Schema.Boolean), + }), + ), + lifetime_paid: Schema.optional( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + next_payment: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + ), + date: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + ), + payer_id: Schema.String, + payer: Schema.optional( + Schema.Struct({ + object: Schema.Literals(["commerce_payer"]), + id: Schema.String, + instance_id: Schema.String, + user_id: Schema.optional(Schema.NullOr(Schema.String)), + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + email: Schema.optional(Schema.NullOr(Schema.String)), + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + organization_name: Schema.optional(Schema.NullOr(Schema.String)), + image_url: Schema.optional(Schema.String), + credits_balance: Schema.optional( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + }), + ), + is_free_trial: Schema.Boolean, + period_start: Schema.Number, + period_end: Schema.NullOr(Schema.Number), + proration_date: Schema.optional(Schema.String), + canceled_at: Schema.NullOr(Schema.Number), + past_due_at: Schema.NullOr(Schema.Number), + ended_at: Schema.NullOr(Schema.Number), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + seats: Schema.optional( + Schema.NullOr( + Schema.Struct({ + quantity: Schema.NullOr(Schema.Number), + tiers: Schema.optional( + Schema.Array( + Schema.Struct({ + quantity: Schema.optional(Schema.NullOr(Schema.Number)), + fee_per_block: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + ), + }), + ), + ), + totals: Schema.optional( + Schema.NullOr( + Schema.Struct({ + subtotal: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + base_fee: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + tax_total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + grand_total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + per_unit_totals: Schema.optional( + Schema.Array( + Schema.Struct({ + name: Schema.String, + block_size: Schema.Number, + tiers: Schema.Array( + Schema.Struct({ + quantity: Schema.optional(Schema.NullOr(Schema.Number)), + fee_per_block: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + }), + ), + ), + credits: Schema.optional( + Schema.NullOr( + Schema.Struct({ + proration: Schema.NullOr( + Schema.Struct({ + amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + cycle_days_remaining: Schema.Number, + cycle_days_total: Schema.Number, + cycle_remaining_percent: Schema.Number, + }), + ), + payer: Schema.NullOr( + Schema.Struct({ + remaining_balance: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + applied_amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + ), + }), + ), + ), + }); +export type CancelSubscriptionItemOutput = + typeof CancelSubscriptionItemOutput.Type; + +// The operation +/** + * Cancel a subscription item + * + * Cancel a specific subscription item. The subscription item can be canceled immediately or at the end of the current billing period. + * + * @param subscription_item_id - The ID of the subscription item to cancel + * @param end_now - Whether to cancel the subscription immediately (true) or at the end of the current billing period (false, default) + */ +export const cancelSubscriptionItem = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: CancelSubscriptionItemInput, + outputSchema: CancelSubscriptionItemOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/billing/createPrice.ts b/packages/clerk/src/operations/backend/billing/createPrice.ts new file mode 100644 index 0000000000..3b6d76d1b3 --- /dev/null +++ b/packages/clerk/src/operations/backend/billing/createPrice.ts @@ -0,0 +1,59 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const CreatePriceInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + plan_id: Schema.String, + currency: Schema.optional(Schema.String), + amount: Schema.NullOr(Schema.Number), + annual_monthly_amount: Schema.optional(Schema.NullOr(Schema.Number)), + description: Schema.optional(Schema.String), +}).pipe(T.Http({ method: "POST", path: "/billing/prices" })); +export type CreatePriceInput = typeof CreatePriceInput.Type; + +// Output Schema +export const CreatePriceOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["commerce_price"]), + id: Schema.String, + plan_id: Schema.String, + instance_id: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + amount: Schema.Number, + annual_monthly_amount: Schema.Number, + fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + annual_monthly_fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + description: Schema.optional(Schema.NullOr(Schema.String)), + is_default: Schema.Boolean, + created_at: Schema.Number, +}); +export type CreatePriceOutput = typeof CreatePriceOutput.Type; + +// The operation +/** + * Create a custom billing price + * + * Creates a custom price for a billing plan. Custom prices allow you to offer different pricing + * to specific customers while maintaining the same plan structure. + */ +export const createPrice = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreatePriceInput, + outputSchema: CreatePriceOutput, + errors: [BadRequest, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/billing/createPriceTransition.ts b/packages/clerk/src/operations/backend/billing/createPriceTransition.ts new file mode 100644 index 0000000000..1921c610b8 --- /dev/null +++ b/packages/clerk/src/operations/backend/billing/createPriceTransition.ts @@ -0,0 +1,383 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + Conflict, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const CreatePriceTransitionInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + subscription_item_id: Schema.String.pipe(T.PathParam()), + from_price_id: Schema.String, + to_price_id: Schema.String, + }).pipe( + T.Http({ + method: "POST", + path: "/billing/subscription_items/{subscription_item_id}/price_transition", + }), + ); +export type CreatePriceTransitionInput = typeof CreatePriceTransitionInput.Type; + +// Output Schema +export const CreatePriceTransitionOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["commerce_price_transition"]), + subscription_item: Schema.Struct({ + object: Schema.Literals(["commerce_subscription_item"]), + id: Schema.String, + instance_id: Schema.String, + status: Schema.Literals([ + "active", + "ended", + "past_due", + "upcoming", + "incomplete", + "abandoned", + ]), + credit: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + ), + cycle_remaining_percent: Schema.optional(Schema.Number), + }), + ), + ), + plan_id: Schema.String, + price_id: Schema.optional(Schema.String), + plan: Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["commerce_plan"]), + id: Schema.String, + name: Schema.String, + fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + annual_monthly_fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + annual_fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + amount: Schema.Number, + amount_formatted: Schema.String, + annual_monthly_amount: Schema.Number, + annual_monthly_amount_formatted: Schema.String, + annual_amount: Schema.Number, + annual_amount_formatted: Schema.String, + currency_symbol: Schema.String, + currency: Schema.String, + description: Schema.String, + product_id: Schema.String, + is_default: Schema.Boolean, + is_recurring: Schema.Boolean, + publicly_visible: Schema.Boolean, + has_base_fee: Schema.Boolean, + payer_type: Schema.Array(Schema.String), + for_payer_type: Schema.String, + slug: Schema.String, + avatar_url: Schema.String, + period: Schema.optional(Schema.String), + interval: Schema.optional(Schema.Number), + features: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["feature"]), + id: Schema.String, + name: Schema.String, + description: Schema.String, + slug: Schema.String, + avatar_url: Schema.String, + }), + ), + free_trial_enabled: Schema.optional(Schema.Boolean), + free_trial_days: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + plan_period: Schema.Literals(["month", "annual"]), + payment_source_id: Schema.String, + payment_source: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["commerce_source"]), + id: Schema.String, + payer_id: Schema.String, + payment_method: Schema.Literals([ + "card", + "apple_pay", + "google_pay", + ]), + is_default: Schema.optional(Schema.NullOr(Schema.Boolean)), + gateway: Schema.String, + gateway_external_id: Schema.String, + gateway_external_account_id: Schema.optional( + Schema.NullOr(Schema.String), + ), + last4: Schema.String, + status: Schema.Literals(["active", "disconnected"]), + wallet_type: Schema.String, + card_type: Schema.String, + expiry_year: Schema.optional(Schema.Number), + expiry_month: Schema.optional(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, + is_removable: Schema.optional(Schema.NullOr(Schema.Boolean)), + }), + ), + ), + lifetime_paid: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + ), + amount: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + ), + next_invoice: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + ), + date: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + ), + next_payment: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + ), + date: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + ), + payer_id: Schema.String, + payer: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["commerce_payer"]), + id: Schema.String, + instance_id: Schema.String, + user_id: Schema.optional(Schema.String), + first_name: Schema.String, + last_name: Schema.String, + email: Schema.String, + organization_id: Schema.optional(Schema.String), + organization_name: Schema.optional(Schema.String), + image_url: Schema.String, + credits_balance: Schema.optional( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + is_free_trial: Schema.Boolean, + period_start: Schema.optional(Schema.NullOr(Schema.Number)), + period_end: Schema.optional(Schema.NullOr(Schema.Number)), + proration_date: Schema.String, + canceled_at: Schema.optional(Schema.NullOr(Schema.Number)), + past_due_at: Schema.optional(Schema.NullOr(Schema.Number)), + ended_at: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + transition: Schema.Struct({ + previous_plan: Schema.Struct({ + object: Schema.Literals(["commerce_plan"]), + id: Schema.String, + name: Schema.String, + fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + annual_monthly_fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + annual_fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + amount: Schema.Number, + amount_formatted: Schema.String, + annual_monthly_amount: Schema.Number, + annual_monthly_amount_formatted: Schema.String, + annual_amount: Schema.Number, + annual_amount_formatted: Schema.String, + currency_symbol: Schema.String, + currency: Schema.String, + description: Schema.String, + product_id: Schema.String, + is_default: Schema.Boolean, + is_recurring: Schema.Boolean, + publicly_visible: Schema.Boolean, + has_base_fee: Schema.Boolean, + payer_type: Schema.Array(Schema.String), + for_payer_type: Schema.String, + slug: Schema.String, + avatar_url: Schema.String, + period: Schema.optional(Schema.String), + interval: Schema.optional(Schema.Number), + features: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["feature"]), + id: Schema.String, + name: Schema.String, + description: Schema.String, + slug: Schema.String, + avatar_url: Schema.String, + }), + ), + free_trial_enabled: Schema.optional(Schema.Boolean), + free_trial_days: Schema.optional(Schema.NullOr(Schema.Number)), + }), + previous_price: Schema.Struct({ + object: Schema.Literals(["commerce_price"]), + id: Schema.String, + plan_id: Schema.String, + instance_id: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + amount: Schema.Number, + annual_monthly_amount: Schema.Number, + fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + annual_monthly_fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + description: Schema.optional(Schema.NullOr(Schema.String)), + is_default: Schema.Boolean, + created_at: Schema.Number, + }), + effective_at: Schema.Number, + effective_mode: Schema.Literals(["immediate", "end_of_period"]), + next_billing_date: Schema.optional(Schema.NullOr(Schema.Number)), + charged_immediately: Schema.Boolean, + immediate_charge: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + ), + previous_subscription_item_status: Schema.Literals([ + "canceled", + "ended", + "abandoned", + ]), + previous_subscription_item_id: Schema.String, + }), + }); +export type CreatePriceTransitionOutput = + typeof CreatePriceTransitionOutput.Type; + +// The operation +/** + * Create a price transition for a subscription item + * + * Creates a price transition for the specified subscription item. + * This may create an upcoming subscription item or activate immediately depending on plan and payer rules. + * + * @param subscription_item_id - The ID of the subscription item to transition + */ +export const createPriceTransition = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: CreatePriceTransitionInput, + outputSchema: CreatePriceTransitionOutput, + errors: [ + BadRequest, + Forbidden, + NotFound, + Conflict, + UnprocessableEntity, + ] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/billing/extendSubscriptionItemFreeTrial.ts b/packages/clerk/src/operations/backend/billing/extendSubscriptionItemFreeTrial.ts new file mode 100644 index 0000000000..274330a4c8 --- /dev/null +++ b/packages/clerk/src/operations/backend/billing/extendSubscriptionItemFreeTrial.ts @@ -0,0 +1,258 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const ExtendSubscriptionItemFreeTrialInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + subscription_item_id: Schema.String.pipe(T.PathParam()), + extend_to: Schema.String, + }).pipe( + T.Http({ + method: "POST", + path: "/billing/subscription_items/{subscription_item_id}/extend_free_trial", + }), + ); +export type ExtendSubscriptionItemFreeTrialInput = + typeof ExtendSubscriptionItemFreeTrialInput.Type; + +// Output Schema +export const ExtendSubscriptionItemFreeTrialOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["commerce_subscription_item"]), + id: Schema.String, + instance_id: Schema.String, + status: Schema.Literals([ + "active", + "ended", + "past_due", + "upcoming", + "incomplete", + "abandoned", + ]), + credit: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + ), + cycle_remaining_percent: Schema.optional(Schema.Number), + }), + ), + ), + plan_id: Schema.String, + price_id: Schema.optional(Schema.String), + plan: Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["commerce_plan"]), + id: Schema.String, + name: Schema.String, + fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + annual_monthly_fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + annual_fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + amount: Schema.Number, + amount_formatted: Schema.String, + annual_monthly_amount: Schema.Number, + annual_monthly_amount_formatted: Schema.String, + annual_amount: Schema.Number, + annual_amount_formatted: Schema.String, + currency_symbol: Schema.String, + currency: Schema.String, + description: Schema.String, + product_id: Schema.String, + is_default: Schema.Boolean, + is_recurring: Schema.Boolean, + publicly_visible: Schema.Boolean, + has_base_fee: Schema.Boolean, + payer_type: Schema.Array(Schema.String), + for_payer_type: Schema.String, + slug: Schema.String, + avatar_url: Schema.String, + period: Schema.optional(Schema.String), + interval: Schema.optional(Schema.Number), + features: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["feature"]), + id: Schema.String, + name: Schema.String, + description: Schema.String, + slug: Schema.String, + avatar_url: Schema.String, + }), + ), + free_trial_enabled: Schema.optional(Schema.Boolean), + free_trial_days: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + plan_period: Schema.Literals(["month", "annual"]), + payment_source_id: Schema.String, + payment_source: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["commerce_source"]), + id: Schema.String, + payer_id: Schema.String, + payment_method: Schema.Literals(["card", "apple_pay", "google_pay"]), + is_default: Schema.optional(Schema.NullOr(Schema.Boolean)), + gateway: Schema.String, + gateway_external_id: Schema.String, + gateway_external_account_id: Schema.optional( + Schema.NullOr(Schema.String), + ), + last4: Schema.String, + status: Schema.Literals(["active", "disconnected"]), + wallet_type: Schema.String, + card_type: Schema.String, + expiry_year: Schema.optional(Schema.Number), + expiry_month: Schema.optional(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, + is_removable: Schema.optional(Schema.NullOr(Schema.Boolean)), + }), + ), + ), + lifetime_paid: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + ), + amount: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + ), + next_invoice: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + ), + date: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + ), + next_payment: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + ), + date: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + ), + payer_id: Schema.String, + payer: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["commerce_payer"]), + id: Schema.String, + instance_id: Schema.String, + user_id: Schema.optional(Schema.String), + first_name: Schema.String, + last_name: Schema.String, + email: Schema.String, + organization_id: Schema.optional(Schema.String), + organization_name: Schema.optional(Schema.String), + image_url: Schema.String, + credits_balance: Schema.optional( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + is_free_trial: Schema.Boolean, + period_start: Schema.optional(Schema.NullOr(Schema.Number)), + period_end: Schema.optional(Schema.NullOr(Schema.Number)), + proration_date: Schema.String, + canceled_at: Schema.optional(Schema.NullOr(Schema.Number)), + past_due_at: Schema.optional(Schema.NullOr(Schema.Number)), + ended_at: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.Number, + updated_at: Schema.Number, + }); +export type ExtendSubscriptionItemFreeTrialOutput = + typeof ExtendSubscriptionItemFreeTrialOutput.Type; + +// The operation +/** + * Extend free trial for a subscription item + * + * Extends the free trial period for a specific subscription item to the specified timestamp. + * The subscription item must be currently in a free trial period, and the plan must support free trials. + * The timestamp must be in the future and not more than 365 days from the end of the current trial period + * This operation is idempotent - repeated requests with the same timestamp will not change the trial period. + * + * @param subscription_item_id - The ID of the subscription item to extend the free trial for + */ +export const extendSubscriptionItemFreeTrial = + /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ExtendSubscriptionItemFreeTrialInput, + outputSchema: ExtendSubscriptionItemFreeTrialOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, + })); diff --git a/packages/clerk/src/operations/backend/billing/getStatement.ts b/packages/clerk/src/operations/backend/billing/getStatement.ts new file mode 100644 index 0000000000..501ef9d33b --- /dev/null +++ b/packages/clerk/src/operations/backend/billing/getStatement.ts @@ -0,0 +1,256 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const GetStatementInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + statementID: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "GET", path: "/billing/statements/{statementID}" })); +export type GetStatementInput = typeof GetStatementInput.Type; + +// Output Schema +export const GetStatementOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["commerce_statement"]), + id: Schema.String, + instance_id: Schema.String, + timestamp: Schema.Number, + payer: Schema.Struct({ + object: Schema.Literals(["commerce_payer"]), + id: Schema.String, + instance_id: Schema.String, + user_id: Schema.optional(Schema.NullOr(Schema.String)), + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + email: Schema.optional(Schema.NullOr(Schema.String)), + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + organization_name: Schema.optional(Schema.NullOr(Schema.String)), + image_url: Schema.optional(Schema.String), + credits_balance: Schema.optional( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + }), + status: Schema.Literals(["open", "closed"]), + totals: Schema.Struct({ + grand_total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + subtotal: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + base_fee: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + tax_total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + groups: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["commerce_statement_group"]), + timestamp: Schema.Number, + items: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["commerce_payment"]), + id: Schema.String, + payment_id: Schema.String, + instance_id: Schema.String, + charge_type: Schema.String, + payee_id: Schema.String, + payee: Schema.Unknown, + payer_id: Schema.String, + payer: Schema.Struct({ + object: Schema.Literals(["commerce_payer"]), + id: Schema.String, + instance_id: Schema.String, + user_id: Schema.optional(Schema.NullOr(Schema.String)), + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + email: Schema.optional(Schema.NullOr(Schema.String)), + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + organization_name: Schema.optional(Schema.NullOr(Schema.String)), + image_url: Schema.optional(Schema.String), + credits_balance: Schema.optional( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + }), + subscription_item_id: Schema.optional(Schema.String), + subscription_item: Schema.optional(Schema.Unknown), + amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + totals: Schema.optional( + Schema.NullOr( + Schema.Struct({ + subtotal: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + base_fee: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + tax_total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + grand_total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + per_unit_totals: Schema.optional( + Schema.Array( + Schema.Struct({ + name: Schema.String, + block_size: Schema.Number, + tiers: Schema.Array( + Schema.Struct({ + quantity: Schema.optional( + Schema.NullOr(Schema.Number), + ), + fee_per_block: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + }), + ), + ), + credits: Schema.optional( + Schema.NullOr( + Schema.Struct({ + proration: Schema.NullOr( + Schema.Struct({ + amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + cycle_days_remaining: Schema.Number, + cycle_days_total: Schema.Number, + cycle_remaining_percent: Schema.Number, + }), + ), + payer: Schema.NullOr( + Schema.Struct({ + remaining_balance: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + applied_amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + ), + }), + ), + ), + payment_method_id: Schema.String, + payment_method: Schema.Struct({ + object: Schema.Literals(["commerce_payment_method"]), + id: Schema.String, + payer_id: Schema.String, + payment_type: Schema.Literals(["card", "link"]), + is_default: Schema.optional(Schema.Boolean), + gateway: Schema.String, + gateway_external_id: Schema.String, + gateway_external_account_id: Schema.NullOr(Schema.String), + last4: Schema.NullOr(Schema.String), + status: Schema.Literals(["active", "disconnected"]), + wallet_type: Schema.optional(Schema.NullOr(Schema.String)), + card_type: Schema.NullOr(Schema.String), + expiry_year: Schema.optional(Schema.NullOr(Schema.Number)), + expiry_month: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + is_removable: Schema.optional(Schema.Boolean), + }), + statement_id: Schema.String, + gateway_external_id: Schema.NullOr(Schema.String), + gateway_external_url: Schema.NullOr(Schema.String), + status: Schema.Literals(["pending", "paid", "failed"]), + paid_at: Schema.NullOr(Schema.Number), + failed_at: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + }), + ), +}); +export type GetStatementOutput = typeof GetStatementOutput.Type; + +// The operation +/** + * Retrieve a billing statement + * + * Retrieves the details of a billing statement. + * + * @param statementID - The ID of the statement to retrieve. + */ +export const getStatement = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetStatementInput, + outputSchema: GetStatementOutput, + errors: [BadRequest, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/billing/getStatementPaymentAttempts.ts b/packages/clerk/src/operations/backend/billing/getStatementPaymentAttempts.ts new file mode 100644 index 0000000000..af9cf7c2fa --- /dev/null +++ b/packages/clerk/src/operations/backend/billing/getStatementPaymentAttempts.ts @@ -0,0 +1,219 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const GetStatementPaymentAttemptsInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + statementID: Schema.String.pipe(T.PathParam()), + paginated: Schema.optional(Schema.Boolean), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), + }).pipe( + T.Http({ + method: "GET", + path: "/billing/statements/{statementID}/payment_attempts", + }), + ); +export type GetStatementPaymentAttemptsInput = + typeof GetStatementPaymentAttemptsInput.Type; + +// Output Schema +export const GetStatementPaymentAttemptsOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["commerce_payment"]), + id: Schema.String, + payment_id: Schema.String, + instance_id: Schema.String, + charge_type: Schema.String, + payee_id: Schema.String, + payee: Schema.Unknown, + payer_id: Schema.String, + payer: Schema.Struct({ + object: Schema.Literals(["commerce_payer"]), + id: Schema.String, + instance_id: Schema.String, + user_id: Schema.optional(Schema.NullOr(Schema.String)), + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + email: Schema.optional(Schema.NullOr(Schema.String)), + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + organization_name: Schema.optional(Schema.NullOr(Schema.String)), + image_url: Schema.optional(Schema.String), + credits_balance: Schema.optional( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + }), + subscription_item_id: Schema.optional(Schema.String), + subscription_item: Schema.optional(Schema.Unknown), + amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + totals: Schema.optional( + Schema.NullOr( + Schema.Struct({ + subtotal: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + base_fee: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + tax_total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + grand_total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + per_unit_totals: Schema.optional( + Schema.Array( + Schema.Struct({ + name: Schema.String, + block_size: Schema.Number, + tiers: Schema.Array( + Schema.Struct({ + quantity: Schema.optional(Schema.NullOr(Schema.Number)), + fee_per_block: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + }), + ), + ), + credits: Schema.optional( + Schema.NullOr( + Schema.Struct({ + proration: Schema.NullOr( + Schema.Struct({ + amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + cycle_days_remaining: Schema.Number, + cycle_days_total: Schema.Number, + cycle_remaining_percent: Schema.Number, + }), + ), + payer: Schema.NullOr( + Schema.Struct({ + remaining_balance: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + applied_amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + ), + }), + ), + ), + payment_method_id: Schema.String, + payment_method: Schema.Struct({ + object: Schema.Literals(["commerce_payment_method"]), + id: Schema.String, + payer_id: Schema.String, + payment_type: Schema.Literals(["card", "link"]), + is_default: Schema.optional(Schema.Boolean), + gateway: Schema.String, + gateway_external_id: Schema.String, + gateway_external_account_id: Schema.NullOr(Schema.String), + last4: Schema.NullOr(Schema.String), + status: Schema.Literals(["active", "disconnected"]), + wallet_type: Schema.optional(Schema.NullOr(Schema.String)), + card_type: Schema.NullOr(Schema.String), + expiry_year: Schema.optional(Schema.NullOr(Schema.Number)), + expiry_month: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + is_removable: Schema.optional(Schema.Boolean), + }), + statement_id: Schema.String, + gateway_external_id: Schema.NullOr(Schema.String), + gateway_external_url: Schema.NullOr(Schema.String), + status: Schema.Literals(["pending", "paid", "failed"]), + paid_at: Schema.NullOr(Schema.Number), + failed_at: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + total_count: Schema.Number, + }); +export type GetStatementPaymentAttemptsOutput = + typeof GetStatementPaymentAttemptsOutput.Type; + +// The operation +/** + * List payment attempts for a billing statement + * + * Returns a list of all payment attempts for a specific billing statement. The payment attempts are returned sorted by creation date, + * with the newest payment attempts appearing first. Pagination is supported. + * + * @param statementID - The ID of the statement to retrieve payment attempts for. + * @param paginated - Whether to paginate the results. +If true, the results will be paginated. +If false, the results will not be paginated. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + */ +export const getStatementPaymentAttempts = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: GetStatementPaymentAttemptsInput, + outputSchema: GetStatementPaymentAttemptsOutput, + errors: [BadRequest, NotFound, UnprocessableEntity] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/billing/index.ts b/packages/clerk/src/operations/backend/billing/index.ts new file mode 100644 index 0000000000..9da6c3b751 --- /dev/null +++ b/packages/clerk/src/operations/backend/billing/index.ts @@ -0,0 +1,10 @@ +export * from "./listPlans.ts"; +export * from "./listPrices.ts"; +export * from "./createPrice.ts"; +export * from "./listSubscriptionItems.ts"; +export * from "./cancelSubscriptionItem.ts"; +export * from "./extendSubscriptionItemFreeTrial.ts"; +export * from "./createPriceTransition.ts"; +export * from "./listStatements.ts"; +export * from "./getStatement.ts"; +export * from "./getStatementPaymentAttempts.ts"; diff --git a/packages/clerk/src/operations/backend/billing/listPlans.ts b/packages/clerk/src/operations/backend/billing/listPlans.ts new file mode 100644 index 0000000000..e3073128d8 --- /dev/null +++ b/packages/clerk/src/operations/backend/billing/listPlans.ts @@ -0,0 +1,116 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ListPlansInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + paginated: Schema.optional(Schema.Boolean), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), + payer_type: Schema.optional(Schema.Literals(["user", "org"])), +}).pipe(T.Http({ method: "GET", path: "/billing/plans" })); +export type ListPlansInput = typeof ListPlansInput.Type; + +// Output Schema +export const ListPlansOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["commerce_plan"]), + id: Schema.String, + name: Schema.String, + fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + annual_monthly_fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + annual_fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + description: Schema.NullOr(Schema.String), + product_id: Schema.String, + is_default: Schema.Boolean, + is_recurring: Schema.Boolean, + publicly_visible: Schema.Boolean, + has_base_fee: Schema.Boolean, + for_payer_type: Schema.String, + slug: Schema.String, + avatar_url: Schema.NullOr(Schema.String), + features: Schema.optional( + Schema.Array( + Schema.Struct({ + object: Schema.Literals(["feature"]), + id: Schema.String, + name: Schema.String, + description: Schema.NullOr(Schema.String), + slug: Schema.String, + avatar_url: Schema.NullOr(Schema.String), + }), + ), + ), + free_trial_enabled: Schema.Boolean, + free_trial_days: Schema.NullOr(Schema.Number), + unit_prices: Schema.optional( + Schema.Array( + Schema.Struct({ + name: Schema.String, + block_size: Schema.Number, + tiers: Schema.Array( + Schema.Struct({ + starts_at_block: Schema.Number, + ends_after_block: Schema.optional(Schema.NullOr(Schema.Number)), + fee_per_block: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + }), + ), + ), + }), + ), + total_count: Schema.Number, +}); +export type ListPlansOutput = typeof ListPlansOutput.Type; + +// The operation +/** + * List all billing plans + * + * Returns a list of all billing plans for the instance. The plans are returned sorted by creation date, + * with the newest plans appearing first. This includes both free and paid plans. Pagination is supported. + * + * @param paginated - Whether to paginate the results. +If true, the results will be paginated. +If false, the results will not be paginated. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + * @param payer_type - Filter plans by payer type + */ +export const listPlans = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListPlansInput, + outputSchema: ListPlansOutput, + errors: [BadRequest, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/billing/listPrices.ts b/packages/clerk/src/operations/backend/billing/listPrices.ts new file mode 100644 index 0000000000..c9394e4f1e --- /dev/null +++ b/packages/clerk/src/operations/backend/billing/listPrices.ts @@ -0,0 +1,73 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ListPricesInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + paginated: Schema.optional(Schema.Boolean), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), + plan_id: Schema.optional(Schema.String), +}).pipe(T.Http({ method: "GET", path: "/billing/prices" })); +export type ListPricesInput = typeof ListPricesInput.Type; + +// Output Schema +export const ListPricesOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["commerce_price"]), + id: Schema.String, + plan_id: Schema.String, + instance_id: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + amount: Schema.Number, + annual_monthly_amount: Schema.Number, + fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + annual_monthly_fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + description: Schema.optional(Schema.NullOr(Schema.String)), + is_default: Schema.Boolean, + created_at: Schema.Number, + }), + ), + total_count: Schema.Number, +}); +export type ListPricesOutput = typeof ListPricesOutput.Type; + +// The operation +/** + * List all billing prices + * + * Returns a list of all prices for the instance. The prices are returned sorted by amount ascending, + * then by creation date descending. This includes both default and custom prices. Pagination is supported. + * + * @param paginated - Whether to paginate the results. +If true, the results will be paginated. +If false, the results will not be paginated. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + * @param plan_id - Filter prices by plan ID + */ +export const listPrices = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListPricesInput, + outputSchema: ListPricesOutput, + errors: [BadRequest, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/billing/listStatements.ts b/packages/clerk/src/operations/backend/billing/listStatements.ts new file mode 100644 index 0000000000..ade79ad4bc --- /dev/null +++ b/packages/clerk/src/operations/backend/billing/listStatements.ts @@ -0,0 +1,273 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ListStatementsInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + paginated: Schema.optional(Schema.Boolean), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), +}).pipe(T.Http({ method: "GET", path: "/billing/statements" })); +export type ListStatementsInput = typeof ListStatementsInput.Type; + +// Output Schema +export const ListStatementsOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["commerce_statement"]), + id: Schema.String, + instance_id: Schema.String, + timestamp: Schema.Number, + payer: Schema.Struct({ + object: Schema.Literals(["commerce_payer"]), + id: Schema.String, + instance_id: Schema.String, + user_id: Schema.optional(Schema.NullOr(Schema.String)), + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + email: Schema.optional(Schema.NullOr(Schema.String)), + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + organization_name: Schema.optional(Schema.NullOr(Schema.String)), + image_url: Schema.optional(Schema.String), + credits_balance: Schema.optional( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + }), + status: Schema.Literals(["open", "closed"]), + totals: Schema.Struct({ + grand_total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + subtotal: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + base_fee: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + tax_total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + groups: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["commerce_statement_group"]), + timestamp: Schema.Number, + items: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["commerce_payment"]), + id: Schema.String, + payment_id: Schema.String, + instance_id: Schema.String, + charge_type: Schema.String, + payee_id: Schema.String, + payee: Schema.Unknown, + payer_id: Schema.String, + payer: Schema.Struct({ + object: Schema.Literals(["commerce_payer"]), + id: Schema.String, + instance_id: Schema.String, + user_id: Schema.optional(Schema.NullOr(Schema.String)), + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + email: Schema.optional(Schema.NullOr(Schema.String)), + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + organization_name: Schema.optional( + Schema.NullOr(Schema.String), + ), + image_url: Schema.optional(Schema.String), + credits_balance: Schema.optional( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + }), + subscription_item_id: Schema.optional(Schema.String), + subscription_item: Schema.optional(Schema.Unknown), + amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + totals: Schema.optional( + Schema.NullOr( + Schema.Struct({ + subtotal: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + base_fee: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + tax_total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + grand_total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + per_unit_totals: Schema.optional( + Schema.Array( + Schema.Struct({ + name: Schema.String, + block_size: Schema.Number, + tiers: Schema.Array( + Schema.Struct({ + quantity: Schema.optional( + Schema.NullOr(Schema.Number), + ), + fee_per_block: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + }), + ), + ), + credits: Schema.optional( + Schema.NullOr( + Schema.Struct({ + proration: Schema.NullOr( + Schema.Struct({ + amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + cycle_days_remaining: Schema.Number, + cycle_days_total: Schema.Number, + cycle_remaining_percent: Schema.Number, + }), + ), + payer: Schema.NullOr( + Schema.Struct({ + remaining_balance: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + applied_amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + ), + }), + ), + ), + payment_method_id: Schema.String, + payment_method: Schema.Struct({ + object: Schema.Literals(["commerce_payment_method"]), + id: Schema.String, + payer_id: Schema.String, + payment_type: Schema.Literals(["card", "link"]), + is_default: Schema.optional(Schema.Boolean), + gateway: Schema.String, + gateway_external_id: Schema.String, + gateway_external_account_id: Schema.NullOr(Schema.String), + last4: Schema.NullOr(Schema.String), + status: Schema.Literals(["active", "disconnected"]), + wallet_type: Schema.optional(Schema.NullOr(Schema.String)), + card_type: Schema.NullOr(Schema.String), + expiry_year: Schema.optional(Schema.NullOr(Schema.Number)), + expiry_month: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + is_removable: Schema.optional(Schema.Boolean), + }), + statement_id: Schema.String, + gateway_external_id: Schema.NullOr(Schema.String), + gateway_external_url: Schema.NullOr(Schema.String), + status: Schema.Literals(["pending", "paid", "failed"]), + paid_at: Schema.NullOr(Schema.Number), + failed_at: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + }), + ), + }), + ), + total_count: Schema.Number, +}); +export type ListStatementsOutput = typeof ListStatementsOutput.Type; + +// The operation +/** + * List all billing statements + * + * Returns a list of all billing statements for the instance. The statements are returned sorted by creation date, + * with the newest statements appearing first. Pagination is supported. + * + * @param paginated - Whether to paginate the results. +If true, the results will be paginated. +If false, the results will not be paginated. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + */ +export const listStatements = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListStatementsInput, + outputSchema: ListStatementsOutput, + errors: [BadRequest, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/billing/listSubscriptionItems.ts b/packages/clerk/src/operations/backend/billing/listSubscriptionItems.ts new file mode 100644 index 0000000000..073fa76097 --- /dev/null +++ b/packages/clerk/src/operations/backend/billing/listSubscriptionItems.ts @@ -0,0 +1,419 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ListSubscriptionItemsInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + paginated: Schema.optional(Schema.Boolean), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), + status: Schema.optional( + Schema.Literals([ + "active", + "ended", + "past_due", + "upcoming", + "free_trial", + ]), + ), + payer_type: Schema.optional(Schema.Literals(["user", "org"])), + plan_id: Schema.optional(Schema.String), + include_free: Schema.optional(Schema.Boolean), + query: Schema.optional(Schema.String), + }).pipe(T.Http({ method: "GET", path: "/billing/subscription_items" })); +export type ListSubscriptionItemsInput = typeof ListSubscriptionItemsInput.Type; + +// Output Schema +export const ListSubscriptionItemsOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["commerce_subscription_item"]), + id: Schema.String, + instance_id: Schema.String, + status: Schema.Literals([ + "active", + "canceled", + "expired", + "ended", + "past_due", + "upcoming", + "incomplete", + "abandoned", + ]), + credit: Schema.optional( + Schema.Struct({ + amount: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + ), + cycle_remaining_percent: Schema.optional(Schema.Number), + }), + ), + credits: Schema.optional( + Schema.NullOr( + Schema.Struct({ + proration: Schema.NullOr( + Schema.Struct({ + amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + cycle_days_remaining: Schema.Number, + cycle_days_total: Schema.Number, + cycle_remaining_percent: Schema.Number, + }), + ), + payer: Schema.NullOr( + Schema.Struct({ + remaining_balance: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + applied_amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + ), + plan_id: Schema.NullOr(Schema.String), + price_id: Schema.optional(Schema.String), + plan: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["commerce_plan"]), + id: Schema.String, + name: Schema.String, + fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + annual_monthly_fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + annual_fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + description: Schema.NullOr(Schema.String), + product_id: Schema.String, + is_default: Schema.Boolean, + is_recurring: Schema.Boolean, + publicly_visible: Schema.Boolean, + has_base_fee: Schema.Boolean, + for_payer_type: Schema.String, + slug: Schema.String, + avatar_url: Schema.NullOr(Schema.String), + features: Schema.optional( + Schema.Array( + Schema.Struct({ + object: Schema.Literals(["feature"]), + id: Schema.String, + name: Schema.String, + description: Schema.NullOr(Schema.String), + slug: Schema.String, + avatar_url: Schema.NullOr(Schema.String), + }), + ), + ), + free_trial_enabled: Schema.Boolean, + free_trial_days: Schema.NullOr(Schema.Number), + unit_prices: Schema.optional( + Schema.Array( + Schema.Struct({ + name: Schema.String, + block_size: Schema.Number, + tiers: Schema.Array( + Schema.Struct({ + starts_at_block: Schema.Number, + ends_after_block: Schema.optional( + Schema.NullOr(Schema.Number), + ), + fee_per_block: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + }), + ), + ), + }), + ), + ), + plan_period: Schema.Literals(["month", "annual"]), + payment_method: Schema.optional( + Schema.Struct({ + object: Schema.Literals(["commerce_payment_method"]), + id: Schema.String, + payer_id: Schema.String, + payment_type: Schema.Literals(["card", "link"]), + is_default: Schema.optional(Schema.Boolean), + gateway: Schema.String, + gateway_external_id: Schema.String, + gateway_external_account_id: Schema.NullOr(Schema.String), + last4: Schema.NullOr(Schema.String), + status: Schema.Literals(["active", "disconnected"]), + wallet_type: Schema.optional(Schema.NullOr(Schema.String)), + card_type: Schema.NullOr(Schema.String), + expiry_year: Schema.optional(Schema.NullOr(Schema.Number)), + expiry_month: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + is_removable: Schema.optional(Schema.Boolean), + }), + ), + lifetime_paid: Schema.optional( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + next_payment: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + ), + date: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + ), + payer_id: Schema.String, + payer: Schema.optional( + Schema.Struct({ + object: Schema.Literals(["commerce_payer"]), + id: Schema.String, + instance_id: Schema.String, + user_id: Schema.optional(Schema.NullOr(Schema.String)), + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + email: Schema.optional(Schema.NullOr(Schema.String)), + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + organization_name: Schema.optional(Schema.NullOr(Schema.String)), + image_url: Schema.optional(Schema.String), + credits_balance: Schema.optional( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + }), + ), + is_free_trial: Schema.Boolean, + period_start: Schema.Number, + period_end: Schema.NullOr(Schema.Number), + proration_date: Schema.optional(Schema.String), + canceled_at: Schema.NullOr(Schema.Number), + past_due_at: Schema.NullOr(Schema.Number), + ended_at: Schema.NullOr(Schema.Number), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + seats: Schema.optional( + Schema.NullOr( + Schema.Struct({ + quantity: Schema.NullOr(Schema.Number), + tiers: Schema.optional( + Schema.Array( + Schema.Struct({ + quantity: Schema.optional(Schema.NullOr(Schema.Number)), + fee_per_block: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + ), + }), + ), + ), + totals: Schema.optional( + Schema.NullOr( + Schema.Struct({ + subtotal: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + base_fee: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + tax_total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + grand_total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + per_unit_totals: Schema.optional( + Schema.Array( + Schema.Struct({ + name: Schema.String, + block_size: Schema.Number, + tiers: Schema.Array( + Schema.Struct({ + quantity: Schema.optional(Schema.NullOr(Schema.Number)), + fee_per_block: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + }), + ), + ), + credits: Schema.optional( + Schema.NullOr( + Schema.Struct({ + proration: Schema.NullOr( + Schema.Struct({ + amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + cycle_days_remaining: Schema.Number, + cycle_days_total: Schema.Number, + cycle_remaining_percent: Schema.Number, + }), + ), + payer: Schema.NullOr( + Schema.Struct({ + remaining_balance: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + applied_amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + ), + }), + ), + ), + }), + ), + total_count: Schema.Number, + }); +export type ListSubscriptionItemsOutput = + typeof ListSubscriptionItemsOutput.Type; + +// The operation +/** + * List all subscription items + * + * Returns a list of all subscription items for the instance. The subscription items are returned sorted by creation date, + * with the newest appearing first. This includes subscriptions for both users and organizations. Pagination is supported. + * + * @param paginated - Whether to paginate the results. +If true, the results will be paginated. +If false, the results will not be paginated. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + * @param status - Filter subscription items by status + * @param payer_type - Filter subscription items by payer type + * @param plan_id - Filter subscription items by plan ID + * @param include_free - Whether to include free plan subscription items + * @param query - Search query to filter subscription items + */ +export const listSubscriptionItems = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: ListSubscriptionItemsInput, + outputSchema: ListSubscriptionItemsOutput, + errors: [BadRequest, UnprocessableEntity] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/blocklistIdentifiers/create.ts b/packages/clerk/src/operations/backend/blocklistIdentifiers/create.ts new file mode 100644 index 0000000000..87487e5238 --- /dev/null +++ b/packages/clerk/src/operations/backend/blocklistIdentifiers/create.ts @@ -0,0 +1,40 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + identifier: Schema.String, +}).pipe(T.Http({ method: "POST", path: "/blocklist_identifiers" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.optional(Schema.Literals(["blocklist_identifier"])), + id: Schema.optional(Schema.String), + identifier: Schema.optional(Schema.String), + identifier_type: Schema.optional( + Schema.Literals(["email_address", "phone_number", "web3_wallet"]), + ), + instance_id: Schema.optional(Schema.String), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Add identifier to the block-list + * + * Create an identifier that is blocked from accessing an instance + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [BadRequest, PaymentRequired, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/blocklistIdentifiers/delete.ts b/packages/clerk/src/operations/backend/blocklistIdentifiers/delete.ts new file mode 100644 index 0000000000..ffb3560ced --- /dev/null +++ b/packages/clerk/src/operations/backend/blocklistIdentifiers/delete.ts @@ -0,0 +1,37 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { PaymentRequired, NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + identifier_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ method: "DELETE", path: "/blocklist_identifiers/{identifier_id}" }), +); +export type DeleteInput = typeof DeleteInput.Type; + +// Output Schema +export const DeleteOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), +}); +export type DeleteOutput = typeof DeleteOutput.Type; + +// The operation +/** + * Delete identifier from block-list + * + * Delete an identifier from the instance block-list + * + * @param identifier_id - The ID of the identifier to delete from the block-list + */ +const delete_ = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteInput, + outputSchema: DeleteOutput, + errors: [PaymentRequired, NotFound] as const, +})); +export { delete_ as delete }; diff --git a/packages/clerk/src/operations/backend/blocklistIdentifiers/index.ts b/packages/clerk/src/operations/backend/blocklistIdentifiers/index.ts new file mode 100644 index 0000000000..4daf9f0205 --- /dev/null +++ b/packages/clerk/src/operations/backend/blocklistIdentifiers/index.ts @@ -0,0 +1,3 @@ +export * from "./list.ts"; +export * from "./create.ts"; +export * from "./delete.ts"; diff --git a/packages/clerk/src/operations/backend/blocklistIdentifiers/list.ts b/packages/clerk/src/operations/backend/blocklistIdentifiers/list.ts new file mode 100644 index 0000000000..435987f2f0 --- /dev/null +++ b/packages/clerk/src/operations/backend/blocklistIdentifiers/list.ts @@ -0,0 +1,41 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { PaymentRequired } from "../../../errors.ts"; + +// Input Schema +export const ListInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({}).pipe( + T.Http({ method: "GET", path: "/blocklist_identifiers" }), +); +export type ListInput = typeof ListInput.Type; + +// Output Schema +export const ListOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.optional(Schema.Literals(["blocklist_identifier"])), + id: Schema.optional(Schema.String), + identifier: Schema.optional(Schema.String), + identifier_type: Schema.optional( + Schema.Literals(["email_address", "phone_number", "web3_wallet"]), + ), + instance_id: Schema.optional(Schema.String), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + }), + ), + total_count: Schema.Number, +}); +export type ListOutput = typeof ListOutput.Type; + +// The operation +/** + * List all identifiers on the block-list + * + * Get a list of all identifiers which are not allowed to access an instance + */ +export const list = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInput, + outputSchema: ListOutput, + errors: [PaymentRequired] as const, +})); diff --git a/packages/clerk/src/operations/backend/clients/get.ts b/packages/clerk/src/operations/backend/clients/get.ts new file mode 100644 index 0000000000..42dea79702 --- /dev/null +++ b/packages/clerk/src/operations/backend/clients/get.ts @@ -0,0 +1,89 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + client_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "GET", path: "/clients/{client_id}" })); +export type GetInput = typeof GetInput.Type; + +// Output Schema +export const GetOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["client"]), + id: Schema.String, + session_ids: Schema.Array(Schema.String), + sessions: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["session"]), + id: Schema.String, + user_id: Schema.String, + client_id: Schema.String, + actor: Schema.optional(Schema.NullOr(Schema.Unknown)), + status: Schema.Literals([ + "active", + "revoked", + "ended", + "expired", + "removed", + "abandoned", + "replaced", + "pending", + ]), + last_active_organization_id: Schema.optional( + Schema.NullOr(Schema.String), + ), + last_active_at: Schema.Number, + latest_activity: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.String, + id: Schema.String, + device_type: Schema.optional(Schema.String), + is_mobile: Schema.Boolean, + browser_name: Schema.optional(Schema.String), + browser_version: Schema.optional(Schema.String), + ip_address: Schema.optional(Schema.String), + city: Schema.optional(Schema.String), + country: Schema.optional(Schema.String), + }), + ), + ), + expire_at: Schema.Number, + abandon_at: Schema.Number, + updated_at: Schema.Number, + created_at: Schema.Number, + tasks: Schema.optional( + Schema.NullOr( + Schema.Array( + Schema.Struct({ + key: Schema.String, + }), + ), + ), + ), + }), + ), + sign_in_id: Schema.NullOr(Schema.String), + sign_up_id: Schema.NullOr(Schema.String), + last_active_session_id: Schema.NullOr(Schema.String), + last_authentication_strategy: Schema.NullOr(Schema.String), + updated_at: Schema.Number, + created_at: Schema.Number, +}); +export type GetOutput = typeof GetOutput.Type; + +// The operation +/** + * Get a client + * + * Returns the details of a client. + * + * @param client_id - Client ID. + */ +export const get = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInput, + outputSchema: GetOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/clients/index.ts b/packages/clerk/src/operations/backend/clients/index.ts new file mode 100644 index 0000000000..3e8fedf97f --- /dev/null +++ b/packages/clerk/src/operations/backend/clients/index.ts @@ -0,0 +1,2 @@ +export * from "./verify.ts"; +export * from "./get.ts"; diff --git a/packages/clerk/src/operations/backend/clients/verify.ts b/packages/clerk/src/operations/backend/clients/verify.ts new file mode 100644 index 0000000000..2692f9851c --- /dev/null +++ b/packages/clerk/src/operations/backend/clients/verify.ts @@ -0,0 +1,87 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; + +// Input Schema +export const VerifyInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + token: Schema.String, +}).pipe(T.Http({ method: "POST", path: "/clients/verify" })); +export type VerifyInput = typeof VerifyInput.Type; + +// Output Schema +export const VerifyOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["client"]), + id: Schema.String, + session_ids: Schema.Array(Schema.String), + sessions: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["session"]), + id: Schema.String, + user_id: Schema.String, + client_id: Schema.String, + actor: Schema.optional(Schema.NullOr(Schema.Unknown)), + status: Schema.Literals([ + "active", + "revoked", + "ended", + "expired", + "removed", + "abandoned", + "replaced", + "pending", + ]), + last_active_organization_id: Schema.optional( + Schema.NullOr(Schema.String), + ), + last_active_at: Schema.Number, + latest_activity: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.String, + id: Schema.String, + device_type: Schema.optional(Schema.String), + is_mobile: Schema.Boolean, + browser_name: Schema.optional(Schema.String), + browser_version: Schema.optional(Schema.String), + ip_address: Schema.optional(Schema.String), + city: Schema.optional(Schema.String), + country: Schema.optional(Schema.String), + }), + ), + ), + expire_at: Schema.Number, + abandon_at: Schema.Number, + updated_at: Schema.Number, + created_at: Schema.Number, + tasks: Schema.optional( + Schema.NullOr( + Schema.Array( + Schema.Struct({ + key: Schema.String, + }), + ), + ), + ), + }), + ), + sign_in_id: Schema.NullOr(Schema.String), + sign_up_id: Schema.NullOr(Schema.String), + last_active_session_id: Schema.NullOr(Schema.String), + last_authentication_strategy: Schema.NullOr(Schema.String), + updated_at: Schema.Number, + created_at: Schema.Number, +}); +export type VerifyOutput = typeof VerifyOutput.Type; + +// The operation +/** + * Verify a client + * + * Verifies the client in the provided token + */ +export const verify = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: VerifyInput, + outputSchema: VerifyOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/domains/add.ts b/packages/clerk/src/operations/backend/domains/add.ts new file mode 100644 index 0000000000..8dacfa4f77 --- /dev/null +++ b/packages/clerk/src/operations/backend/domains/add.ts @@ -0,0 +1,56 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const AddInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + name: Schema.String, + is_satellite: Schema.Literals(["true"]), + proxy_url: Schema.optional(Schema.NullOr(Schema.String)), +}).pipe(T.Http({ method: "POST", path: "/domains" })); +export type AddInput = typeof AddInput.Type; + +// Output Schema +export const AddOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["domain"]), + id: Schema.String, + name: Schema.String, + is_satellite: Schema.Boolean, + frontend_api_url: Schema.String, + accounts_portal_url: Schema.optional(Schema.NullOr(Schema.String)), + proxy_url: Schema.optional(Schema.NullOr(Schema.String)), + development_origin: Schema.String, + cname_targets: Schema.optional( + Schema.NullOr( + Schema.Array( + Schema.Struct({ + host: Schema.String, + value: Schema.String, + required: Schema.Boolean, + }), + ), + ), + ), +}); +export type AddOutput = typeof AddOutput.Type; + +// The operation +/** + * Add a domain + * + * Add a new domain for your instance. + * Useful in the case of multi-domain instances, allows adding satellite domains to an instance. + * The new domain must have a `name`. The domain name can contain the port for development instances, like `localhost:3000`. + * At the moment, instances can have only one primary domain, so the `is_satellite` parameter must be set to `true`. + * If you're planning to configure the new satellite domain to run behind a proxy, pass the `proxy_url` parameter accordingly. + */ +export const add = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: AddInput, + outputSchema: AddOutput, + errors: [BadRequest, PaymentRequired, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/domains/delete.ts b/packages/clerk/src/operations/backend/domains/delete.ts new file mode 100644 index 0000000000..98436fc13e --- /dev/null +++ b/packages/clerk/src/operations/backend/domains/delete.ts @@ -0,0 +1,36 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + domain_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "DELETE", path: "/domains/{domain_id}" })); +export type DeleteInput = typeof DeleteInput.Type; + +// Output Schema +export const DeleteOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), +}); +export type DeleteOutput = typeof DeleteOutput.Type; + +// The operation +/** + * Delete a satellite domain + * + * Deletes a satellite domain for the instance. + * It is currently not possible to delete the instance's primary domain. + * + * @param domain_id - The ID of the domain that will be deleted. Must be a satellite domain. + */ +const delete_ = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteInput, + outputSchema: DeleteOutput, + errors: [Forbidden, NotFound] as const, +})); +export { delete_ as delete }; diff --git a/packages/clerk/src/operations/backend/domains/index.ts b/packages/clerk/src/operations/backend/domains/index.ts new file mode 100644 index 0000000000..4e95419a6b --- /dev/null +++ b/packages/clerk/src/operations/backend/domains/index.ts @@ -0,0 +1,4 @@ +export * from "./list.ts"; +export * from "./add.ts"; +export * from "./update.ts"; +export * from "./delete.ts"; diff --git a/packages/clerk/src/operations/backend/domains/list.ts b/packages/clerk/src/operations/backend/domains/list.ts new file mode 100644 index 0000000000..5106b70141 --- /dev/null +++ b/packages/clerk/src/operations/backend/domains/list.ts @@ -0,0 +1,50 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; + +// Input Schema +export const ListInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({}).pipe( + T.Http({ method: "GET", path: "/domains" }), +); +export type ListInput = typeof ListInput.Type; + +// Output Schema +export const ListOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["domain"]), + id: Schema.String, + name: Schema.String, + is_satellite: Schema.Boolean, + frontend_api_url: Schema.String, + accounts_portal_url: Schema.optional(Schema.NullOr(Schema.String)), + proxy_url: Schema.optional(Schema.NullOr(Schema.String)), + development_origin: Schema.String, + cname_targets: Schema.optional( + Schema.NullOr( + Schema.Array( + Schema.Struct({ + host: Schema.String, + value: Schema.String, + required: Schema.Boolean, + }), + ), + ), + ), + }), + ), + total_count: Schema.Number, +}); +export type ListOutput = typeof ListOutput.Type; + +// The operation +/** + * List all instance domains + * + * Use this endpoint to get a list of all domains for an instance. + * The response will contain the primary domain for the instance and any satellite domains. Each domain in the response contains information about the URLs where Clerk operates and the required CNAME targets. + */ +export const list = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInput, + outputSchema: ListOutput, +})); diff --git a/packages/clerk/src/operations/backend/domains/update.ts b/packages/clerk/src/operations/backend/domains/update.ts new file mode 100644 index 0000000000..a081121c2b --- /dev/null +++ b/packages/clerk/src/operations/backend/domains/update.ts @@ -0,0 +1,58 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const UpdateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + domain_id: Schema.String.pipe(T.PathParam()), + name: Schema.optional(Schema.NullOr(Schema.String)), + proxy_url: Schema.optional(Schema.NullOr(Schema.String)), + is_secondary: Schema.optional(Schema.NullOr(Schema.Boolean)), +}).pipe(T.Http({ method: "PATCH", path: "/domains/{domain_id}" })); +export type UpdateInput = typeof UpdateInput.Type; + +// Output Schema +export const UpdateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["domain"]), + id: Schema.String, + name: Schema.String, + is_satellite: Schema.Boolean, + frontend_api_url: Schema.String, + accounts_portal_url: Schema.optional(Schema.NullOr(Schema.String)), + proxy_url: Schema.optional(Schema.NullOr(Schema.String)), + development_origin: Schema.String, + cname_targets: Schema.optional( + Schema.NullOr( + Schema.Array( + Schema.Struct({ + host: Schema.String, + value: Schema.String, + required: Schema.Boolean, + }), + ), + ), + ), +}); +export type UpdateOutput = typeof UpdateOutput.Type; + +// The operation +/** + * Update a domain + * + * The `proxy_url` can be updated only for production instances. + * Update one of the instance's domains. Both primary and satellite domains can be updated. + * If you choose to use Clerk via proxy, use this endpoint to specify the `proxy_url`. + * Whenever you decide you'd rather switch to DNS setup for Clerk, simply set `proxy_url` + * to `null` for the domain. When you update a production instance's primary domain name, + * you have to make sure that you've completed all the necessary setup steps for DNS and + * emails to work. Expect downtime otherwise. Updating a primary domain's name will also + * update the instance's home origin, affecting the default application paths. + * + * @param domain_id - The ID of the domain that will be updated. + */ +export const update = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateInput, + outputSchema: UpdateOutput, + errors: [BadRequest, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/emailAddresses/create.ts b/packages/clerk/src/operations/backend/emailAddresses/create.ts new file mode 100644 index 0000000000..3e464f0423 --- /dev/null +++ b/packages/clerk/src/operations/backend/emailAddresses/create.ts @@ -0,0 +1,56 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + Conflict, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String, + email_address: Schema.String, + verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + primary: Schema.optional(Schema.NullOr(Schema.Boolean)), +}).pipe(T.Http({ method: "POST", path: "/email_addresses" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create an email address + * + * Create a new email address + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [ + BadRequest, + Forbidden, + NotFound, + Conflict, + UnprocessableEntity, + ] as const, +})); diff --git a/packages/clerk/src/operations/backend/emailAddresses/delete.ts b/packages/clerk/src/operations/backend/emailAddresses/delete.ts new file mode 100644 index 0000000000..8913750a9a --- /dev/null +++ b/packages/clerk/src/operations/backend/emailAddresses/delete.ts @@ -0,0 +1,37 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound, Conflict } from "../../../errors.ts"; + +// Input Schema +export const DeleteInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + email_address_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ method: "DELETE", path: "/email_addresses/{email_address_id}" }), +); +export type DeleteInput = typeof DeleteInput.Type; + +// Output Schema +export const DeleteOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), +}); +export type DeleteOutput = typeof DeleteOutput.Type; + +// The operation +/** + * Delete an email address + * + * Delete the email address with the given ID + * + * @param email_address_id - The ID of the email address to delete + */ +const delete_ = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteInput, + outputSchema: DeleteOutput, + errors: [BadRequest, Forbidden, NotFound, Conflict] as const, +})); +export { delete_ as delete }; diff --git a/packages/clerk/src/operations/backend/emailAddresses/get.ts b/packages/clerk/src/operations/backend/emailAddresses/get.ts new file mode 100644 index 0000000000..79e0b7285b --- /dev/null +++ b/packages/clerk/src/operations/backend/emailAddresses/get.ts @@ -0,0 +1,43 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + email_address_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "GET", path: "/email_addresses/{email_address_id}" })); +export type GetInput = typeof GetInput.Type; + +// Output Schema +export const GetOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type GetOutput = typeof GetOutput.Type; + +// The operation +/** + * Retrieve an email address + * + * Returns the details of an email address. + * + * @param email_address_id - The ID of the email address to retrieve + */ +export const get = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInput, + outputSchema: GetOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/emailAddresses/index.ts b/packages/clerk/src/operations/backend/emailAddresses/index.ts new file mode 100644 index 0000000000..38eadc4ef7 --- /dev/null +++ b/packages/clerk/src/operations/backend/emailAddresses/index.ts @@ -0,0 +1,5 @@ +export * from "./create.ts"; +export * from "./get.ts"; +export * from "./update.ts"; +export * from "./delete.ts"; +export * from "./replaceForUser.ts"; diff --git a/packages/clerk/src/operations/backend/emailAddresses/replaceForUser.ts b/packages/clerk/src/operations/backend/emailAddresses/replaceForUser.ts new file mode 100644 index 0000000000..311c807e99 --- /dev/null +++ b/packages/clerk/src/operations/backend/emailAddresses/replaceForUser.ts @@ -0,0 +1,52 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const ReplaceForUserInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + email_address: Schema.String, +}).pipe(T.Http({ method: "PUT", path: "/users/{user_id}/email_address" })); +export type ReplaceForUserInput = typeof ReplaceForUserInput.Type; + +// Output Schema +export const ReplaceForUserOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type ReplaceForUserOutput = typeof ReplaceForUserOutput.Type; + +// The operation +/** + * Replace a user's email address + * + * Replaces all of the user's email addresses with a single verified, primary email address. + * The new email address is created with the admin verification strategy. Any existing email + * addresses are deleted. If an existing email address is linked to a connected account, the + * request is rejected; remove the connected account first. + * + * @param user_id - The ID of the user whose email address to replace + */ +export const replaceForUser = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ReplaceForUserInput, + outputSchema: ReplaceForUserOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/emailAddresses/update.ts b/packages/clerk/src/operations/backend/emailAddresses/update.ts new file mode 100644 index 0000000000..67a692b12a --- /dev/null +++ b/packages/clerk/src/operations/backend/emailAddresses/update.ts @@ -0,0 +1,47 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound, Conflict } from "../../../errors.ts"; + +// Input Schema +export const UpdateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + email_address_id: Schema.String.pipe(T.PathParam()), + verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + primary: Schema.optional(Schema.NullOr(Schema.Boolean)), +}).pipe( + T.Http({ method: "PATCH", path: "/email_addresses/{email_address_id}" }), +); +export type UpdateInput = typeof UpdateInput.Type; + +// Output Schema +export const UpdateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type UpdateOutput = typeof UpdateOutput.Type; + +// The operation +/** + * Update an email address + * + * Updates an email address. + * + * @param email_address_id - The ID of the email address to update + */ +export const update = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateInput, + outputSchema: UpdateOutput, + errors: [BadRequest, Forbidden, NotFound, Conflict] as const, +})); diff --git a/packages/clerk/src/operations/backend/enterpriseConnections/create.ts b/packages/clerk/src/operations/backend/enterpriseConnections/create.ts new file mode 100644 index 0000000000..de0b2864e2 --- /dev/null +++ b/packages/clerk/src/operations/backend/enterpriseConnections/create.ts @@ -0,0 +1,160 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + PaymentRequired, + Forbidden, + NotFound, + Conflict, + UnprocessableEntity, +} from "../../../errors.ts"; +import { SensitiveNullableString } from "../../../sensitive.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + name: Schema.String, + provider: Schema.Literals([ + "saml_custom", + "saml_okta", + "saml_google", + "saml_microsoft", + "oidc_custom", + "oidc_github_enterprise", + "oidc_gitlab", + ]), + domains: Schema.Array(Schema.String), + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + allow_organization_account_linking: Schema.optional( + Schema.NullOr(Schema.Boolean), + ), + active: Schema.optional(Schema.NullOr(Schema.Boolean)), + saml: Schema.optional( + Schema.NullOr( + Schema.Struct({ + idp_entity_id: Schema.optional(Schema.NullOr(Schema.String)), + idp_sso_url: Schema.optional(Schema.NullOr(Schema.String)), + idp_certificate: Schema.optional(Schema.NullOr(Schema.String)), + idp_metadata_url: Schema.optional(Schema.NullOr(Schema.String)), + idp_metadata: Schema.optional(Schema.NullOr(Schema.String)), + attribute_mapping: Schema.optional( + Schema.NullOr( + Schema.Struct({ + user_id: Schema.optional(Schema.NullOr(Schema.String)), + email_address: Schema.optional(Schema.NullOr(Schema.String)), + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + }), + ), + ), + allow_subdomains: Schema.optional(Schema.NullOr(Schema.Boolean)), + allow_idp_initiated: Schema.optional(Schema.NullOr(Schema.Boolean)), + force_authn: Schema.optional(Schema.NullOr(Schema.Boolean)), + }), + ), + ), + oidc: Schema.optional( + Schema.NullOr( + Schema.Struct({ + client_id: Schema.optional(Schema.NullOr(Schema.String)), + client_secret: Schema.optional(SensitiveNullableString), + discovery_url: Schema.optional(Schema.NullOr(Schema.String)), + auth_url: Schema.optional(Schema.NullOr(Schema.String)), + token_url: Schema.optional(Schema.NullOr(Schema.String)), + user_info_url: Schema.optional(Schema.NullOr(Schema.String)), + requires_pkce: Schema.optional(Schema.NullOr(Schema.Boolean)), + }), + ), + ), + custom_attributes: Schema.optional( + Schema.Array( + Schema.Struct({ + name: Schema.String, + key: Schema.String, + sso_path: Schema.optional(Schema.String), + scim_path: Schema.optional(Schema.String), + }), + ), + ), +}).pipe(T.Http({ method: "POST", path: "/enterprise_connections" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + name: Schema.String, + provider: Schema.String, + logo_public_url: Schema.optional(Schema.NullOr(Schema.String)), + active: Schema.Boolean, + domains: Schema.Array(Schema.String), + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + sync_user_attributes: Schema.optional(Schema.Boolean), + disable_additional_identifications: Schema.optional(Schema.Boolean), + allow_organization_account_linking: Schema.optional(Schema.Boolean), + custom_attributes: Schema.optional( + Schema.Array( + Schema.Struct({ + name: Schema.String, + key: Schema.String, + sso_path: Schema.optional(Schema.String), + scim_path: Schema.optional(Schema.String), + }), + ), + ), + saml_connection: Schema.optional( + Schema.NullOr( + Schema.Struct({ + id: Schema.optional(Schema.String), + name: Schema.optional(Schema.String), + idp_entity_id: Schema.optional(Schema.NullOr(Schema.String)), + idp_sso_url: Schema.optional(Schema.NullOr(Schema.String)), + idp_metadata_url: Schema.optional(Schema.NullOr(Schema.String)), + acs_url: Schema.optional(Schema.NullOr(Schema.String)), + sp_entity_id: Schema.optional(Schema.NullOr(Schema.String)), + sp_metadata_url: Schema.optional(Schema.NullOr(Schema.String)), + active: Schema.optional(Schema.Boolean), + allow_idp_initiated: Schema.optional(Schema.Boolean), + allow_subdomains: Schema.optional(Schema.Boolean), + force_authn: Schema.optional(Schema.Boolean), + }), + ), + ), + oauth_config: Schema.optional( + Schema.NullOr( + Schema.Struct({ + id: Schema.optional(Schema.String), + name: Schema.optional(Schema.String), + provider_key: Schema.optional(Schema.String), + client_id: Schema.optional(Schema.NullOr(Schema.String)), + discovery_url: Schema.optional(Schema.NullOr(Schema.String)), + auth_url: Schema.optional(Schema.NullOr(Schema.String)), + token_url: Schema.optional(Schema.NullOr(Schema.String)), + user_info_url: Schema.optional(Schema.NullOr(Schema.String)), + requires_pkce: Schema.optional(Schema.Boolean), + logo_public_url: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + }), + ), + ), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create an enterprise connection + * + * Create a new enterprise connection. + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [ + PaymentRequired, + Forbidden, + NotFound, + Conflict, + UnprocessableEntity, + ] as const, +})); diff --git a/packages/clerk/src/operations/backend/enterpriseConnections/delete.ts b/packages/clerk/src/operations/backend/enterpriseConnections/delete.ts new file mode 100644 index 0000000000..5abd4d9f97 --- /dev/null +++ b/packages/clerk/src/operations/backend/enterpriseConnections/delete.ts @@ -0,0 +1,40 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { PaymentRequired, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + enterprise_connection_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "DELETE", + path: "/enterprise_connections/{enterprise_connection_id}", + }), +); +export type DeleteInput = typeof DeleteInput.Type; + +// Output Schema +export const DeleteOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), +}); +export type DeleteOutput = typeof DeleteOutput.Type; + +// The operation +/** + * Delete an enterprise connection + * + * Deletes the enterprise connection whose ID matches the provided `enterprise_connection_id` in the path. + * + * @param enterprise_connection_id - The ID of the enterprise connection to delete + */ +const delete_ = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteInput, + outputSchema: DeleteOutput, + errors: [PaymentRequired, Forbidden, NotFound] as const, +})); +export { delete_ as delete }; diff --git a/packages/clerk/src/operations/backend/enterpriseConnections/get.ts b/packages/clerk/src/operations/backend/enterpriseConnections/get.ts new file mode 100644 index 0000000000..dbb916ed16 --- /dev/null +++ b/packages/clerk/src/operations/backend/enterpriseConnections/get.ts @@ -0,0 +1,92 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { PaymentRequired, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + enterprise_connection_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "GET", + path: "/enterprise_connections/{enterprise_connection_id}", + }), +); +export type GetInput = typeof GetInput.Type; + +// Output Schema +export const GetOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + name: Schema.String, + provider: Schema.String, + logo_public_url: Schema.optional(Schema.NullOr(Schema.String)), + active: Schema.Boolean, + domains: Schema.Array(Schema.String), + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + sync_user_attributes: Schema.optional(Schema.Boolean), + disable_additional_identifications: Schema.optional(Schema.Boolean), + allow_organization_account_linking: Schema.optional(Schema.Boolean), + custom_attributes: Schema.optional( + Schema.Array( + Schema.Struct({ + name: Schema.String, + key: Schema.String, + sso_path: Schema.optional(Schema.String), + scim_path: Schema.optional(Schema.String), + }), + ), + ), + saml_connection: Schema.optional( + Schema.NullOr( + Schema.Struct({ + id: Schema.optional(Schema.String), + name: Schema.optional(Schema.String), + idp_entity_id: Schema.optional(Schema.NullOr(Schema.String)), + idp_sso_url: Schema.optional(Schema.NullOr(Schema.String)), + idp_metadata_url: Schema.optional(Schema.NullOr(Schema.String)), + acs_url: Schema.optional(Schema.NullOr(Schema.String)), + sp_entity_id: Schema.optional(Schema.NullOr(Schema.String)), + sp_metadata_url: Schema.optional(Schema.NullOr(Schema.String)), + active: Schema.optional(Schema.Boolean), + allow_idp_initiated: Schema.optional(Schema.Boolean), + allow_subdomains: Schema.optional(Schema.Boolean), + force_authn: Schema.optional(Schema.Boolean), + }), + ), + ), + oauth_config: Schema.optional( + Schema.NullOr( + Schema.Struct({ + id: Schema.optional(Schema.String), + name: Schema.optional(Schema.String), + provider_key: Schema.optional(Schema.String), + client_id: Schema.optional(Schema.NullOr(Schema.String)), + discovery_url: Schema.optional(Schema.NullOr(Schema.String)), + auth_url: Schema.optional(Schema.NullOr(Schema.String)), + token_url: Schema.optional(Schema.NullOr(Schema.String)), + user_info_url: Schema.optional(Schema.NullOr(Schema.String)), + requires_pkce: Schema.optional(Schema.Boolean), + logo_public_url: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + }), + ), + ), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type GetOutput = typeof GetOutput.Type; + +// The operation +/** + * Retrieve an enterprise connection + * + * Fetches the enterprise connection whose ID matches the provided `enterprise_connection_id` in the path. + * + * @param enterprise_connection_id - The ID of the enterprise connection + */ +export const get = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInput, + outputSchema: GetOutput, + errors: [PaymentRequired, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/enterpriseConnections/index.ts b/packages/clerk/src/operations/backend/enterpriseConnections/index.ts new file mode 100644 index 0000000000..9bc43612a5 --- /dev/null +++ b/packages/clerk/src/operations/backend/enterpriseConnections/index.ts @@ -0,0 +1,5 @@ +export * from "./list.ts"; +export * from "./create.ts"; +export * from "./get.ts"; +export * from "./update.ts"; +export * from "./delete.ts"; diff --git a/packages/clerk/src/operations/backend/enterpriseConnections/list.ts b/packages/clerk/src/operations/backend/enterpriseConnections/list.ts new file mode 100644 index 0000000000..f94d23dd9e --- /dev/null +++ b/packages/clerk/src/operations/backend/enterpriseConnections/list.ts @@ -0,0 +1,106 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + PaymentRequired, + Forbidden, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const ListInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), + organization_id: Schema.optional(Schema.String), + active: Schema.optional(Schema.Boolean), +}).pipe(T.Http({ method: "GET", path: "/enterprise_connections" })); +export type ListInput = typeof ListInput.Type; + +// Output Schema +export const ListOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + id: Schema.String, + name: Schema.String, + provider: Schema.String, + logo_public_url: Schema.optional(Schema.NullOr(Schema.String)), + active: Schema.Boolean, + domains: Schema.Array(Schema.String), + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + sync_user_attributes: Schema.optional(Schema.Boolean), + disable_additional_identifications: Schema.optional(Schema.Boolean), + allow_organization_account_linking: Schema.optional(Schema.Boolean), + custom_attributes: Schema.optional( + Schema.Array( + Schema.Struct({ + name: Schema.String, + key: Schema.String, + sso_path: Schema.optional(Schema.String), + scim_path: Schema.optional(Schema.String), + }), + ), + ), + saml_connection: Schema.optional( + Schema.NullOr( + Schema.Struct({ + id: Schema.optional(Schema.String), + name: Schema.optional(Schema.String), + idp_entity_id: Schema.optional(Schema.NullOr(Schema.String)), + idp_sso_url: Schema.optional(Schema.NullOr(Schema.String)), + idp_metadata_url: Schema.optional(Schema.NullOr(Schema.String)), + acs_url: Schema.optional(Schema.NullOr(Schema.String)), + sp_entity_id: Schema.optional(Schema.NullOr(Schema.String)), + sp_metadata_url: Schema.optional(Schema.NullOr(Schema.String)), + active: Schema.optional(Schema.Boolean), + allow_idp_initiated: Schema.optional(Schema.Boolean), + allow_subdomains: Schema.optional(Schema.Boolean), + force_authn: Schema.optional(Schema.Boolean), + }), + ), + ), + oauth_config: Schema.optional( + Schema.NullOr( + Schema.Struct({ + id: Schema.optional(Schema.String), + name: Schema.optional(Schema.String), + provider_key: Schema.optional(Schema.String), + client_id: Schema.optional(Schema.NullOr(Schema.String)), + discovery_url: Schema.optional(Schema.NullOr(Schema.String)), + auth_url: Schema.optional(Schema.NullOr(Schema.String)), + token_url: Schema.optional(Schema.NullOr(Schema.String)), + user_info_url: Schema.optional(Schema.NullOr(Schema.String)), + requires_pkce: Schema.optional(Schema.Boolean), + logo_public_url: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + }), + ), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + total_count: Schema.Number, +}); +export type ListOutput = typeof ListOutput.Type; + +// The operation +/** + * List enterprise connections + * + * Returns the list of enterprise connections for the instance. + * Results can be paginated using the optional `limit` and `offset` query parameters. + * + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + * @param organization_id - Filter enterprise connections by organization ID + * @param active - Filter by active status. If true, only active connections are returned. If false, only inactive connections are returned. If omitted, all connections are returned. + */ +export const list = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInput, + outputSchema: ListOutput, + errors: [PaymentRequired, Forbidden, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/enterpriseConnections/update.ts b/packages/clerk/src/operations/backend/enterpriseConnections/update.ts new file mode 100644 index 0000000000..67a4dadce8 --- /dev/null +++ b/packages/clerk/src/operations/backend/enterpriseConnections/update.ts @@ -0,0 +1,167 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; +import { SensitiveNullableString } from "../../../sensitive.ts"; + +// Input Schema +export const UpdateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + enterprise_connection_id: Schema.String.pipe(T.PathParam()), + name: Schema.optional(Schema.NullOr(Schema.String)), + domains: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + active: Schema.optional(Schema.NullOr(Schema.Boolean)), + sync_user_attributes: Schema.optional(Schema.NullOr(Schema.Boolean)), + disable_additional_identifications: Schema.optional( + Schema.NullOr(Schema.Boolean), + ), + allow_organization_account_linking: Schema.optional( + Schema.NullOr(Schema.Boolean), + ), + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + saml: Schema.optional( + Schema.NullOr( + Schema.Struct({ + name: Schema.optional(Schema.NullOr(Schema.String)), + idp_entity_id: Schema.optional(Schema.NullOr(Schema.String)), + idp_sso_url: Schema.optional(Schema.NullOr(Schema.String)), + idp_certificate: Schema.optional(Schema.NullOr(Schema.String)), + idp_metadata_url: Schema.optional(Schema.NullOr(Schema.String)), + idp_metadata: Schema.optional(Schema.NullOr(Schema.String)), + attribute_mapping: Schema.optional( + Schema.NullOr( + Schema.Struct({ + user_id: Schema.optional(Schema.NullOr(Schema.String)), + email_address: Schema.optional(Schema.NullOr(Schema.String)), + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + }), + ), + ), + allow_subdomains: Schema.optional(Schema.NullOr(Schema.Boolean)), + allow_idp_initiated: Schema.optional(Schema.NullOr(Schema.Boolean)), + force_authn: Schema.optional(Schema.NullOr(Schema.Boolean)), + }), + ), + ), + oidc: Schema.optional( + Schema.NullOr( + Schema.Struct({ + client_id: Schema.optional(Schema.NullOr(Schema.String)), + client_secret: Schema.optional(SensitiveNullableString), + discovery_url: Schema.optional(Schema.NullOr(Schema.String)), + auth_url: Schema.optional(Schema.NullOr(Schema.String)), + token_url: Schema.optional(Schema.NullOr(Schema.String)), + user_info_url: Schema.optional(Schema.NullOr(Schema.String)), + requires_pkce: Schema.optional(Schema.NullOr(Schema.Boolean)), + }), + ), + ), + custom_attributes: Schema.optional( + Schema.NullOr( + Schema.Array( + Schema.Struct({ + name: Schema.String, + key: Schema.String, + sso_path: Schema.optional(Schema.String), + scim_path: Schema.optional(Schema.String), + }), + ), + ), + ), +}).pipe( + T.Http({ + method: "PATCH", + path: "/enterprise_connections/{enterprise_connection_id}", + }), +); +export type UpdateInput = typeof UpdateInput.Type; + +// Output Schema +export const UpdateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + name: Schema.String, + provider: Schema.String, + logo_public_url: Schema.optional(Schema.NullOr(Schema.String)), + active: Schema.Boolean, + domains: Schema.Array(Schema.String), + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + sync_user_attributes: Schema.optional(Schema.Boolean), + disable_additional_identifications: Schema.optional(Schema.Boolean), + allow_organization_account_linking: Schema.optional(Schema.Boolean), + custom_attributes: Schema.optional( + Schema.Array( + Schema.Struct({ + name: Schema.String, + key: Schema.String, + sso_path: Schema.optional(Schema.String), + scim_path: Schema.optional(Schema.String), + }), + ), + ), + saml_connection: Schema.optional( + Schema.NullOr( + Schema.Struct({ + id: Schema.optional(Schema.String), + name: Schema.optional(Schema.String), + idp_entity_id: Schema.optional(Schema.NullOr(Schema.String)), + idp_sso_url: Schema.optional(Schema.NullOr(Schema.String)), + idp_metadata_url: Schema.optional(Schema.NullOr(Schema.String)), + acs_url: Schema.optional(Schema.NullOr(Schema.String)), + sp_entity_id: Schema.optional(Schema.NullOr(Schema.String)), + sp_metadata_url: Schema.optional(Schema.NullOr(Schema.String)), + active: Schema.optional(Schema.Boolean), + allow_idp_initiated: Schema.optional(Schema.Boolean), + allow_subdomains: Schema.optional(Schema.Boolean), + force_authn: Schema.optional(Schema.Boolean), + }), + ), + ), + oauth_config: Schema.optional( + Schema.NullOr( + Schema.Struct({ + id: Schema.optional(Schema.String), + name: Schema.optional(Schema.String), + provider_key: Schema.optional(Schema.String), + client_id: Schema.optional(Schema.NullOr(Schema.String)), + discovery_url: Schema.optional(Schema.NullOr(Schema.String)), + auth_url: Schema.optional(Schema.NullOr(Schema.String)), + token_url: Schema.optional(Schema.NullOr(Schema.String)), + user_info_url: Schema.optional(Schema.NullOr(Schema.String)), + requires_pkce: Schema.optional(Schema.Boolean), + logo_public_url: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + }), + ), + ), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type UpdateOutput = typeof UpdateOutput.Type; + +// The operation +/** + * Update an enterprise connection + * + * Updates the enterprise connection whose ID matches the provided `enterprise_connection_id` in the path. + * When enabling the connection (setting `active` to true), any existing verified organization domains that match the connection's domains (e.g. used for enrollment modes like automatic invitation) may be deleted so the connection can be enabled. + * + * @param enterprise_connection_id - The ID of the enterprise connection to update + */ +export const update = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateInput, + outputSchema: UpdateOutput, + errors: [ + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + UnprocessableEntity, + ] as const, +})); diff --git a/packages/clerk/src/operations/backend/index.ts b/packages/clerk/src/operations/backend/index.ts new file mode 100644 index 0000000000..888fe4f337 --- /dev/null +++ b/packages/clerk/src/operations/backend/index.ts @@ -0,0 +1,38 @@ +export * as actorTokens from "./actorTokens/index.ts"; +export * as adminPortalLinkTokens from "./adminPortalLinkTokens/index.ts"; +export * as agentTasks from "./agentTasks/index.ts"; +export * as allowlistIdentifiers from "./allowlistIdentifiers/index.ts"; +export * as apiKeys from "./apiKeys/index.ts"; +export * as betaFeatures from "./betaFeatures/index.ts"; +export * as billing from "./billing/index.ts"; +export * as blocklistIdentifiers from "./blocklistIdentifiers/index.ts"; +export * as clients from "./clients/index.ts"; +export * as domains from "./domains/index.ts"; +export * as emailAddresses from "./emailAddresses/index.ts"; +export * as enterpriseConnections from "./enterpriseConnections/index.ts"; +export * as instanceSettings from "./instanceSettings/index.ts"; +export * as invitations from "./invitations/index.ts"; +export * as jwks from "./jwks/index.ts"; +export * as jwtTemplates from "./jwtTemplates/index.ts"; +export * as m2m from "./m2m/index.ts"; +export * as machines from "./machines/index.ts"; +export * as miscellaneous from "./miscellaneous/index.ts"; +export * as oauthAccessTokens from "./oauthAccessTokens/index.ts"; +export * as oauthApplications from "./oauthApplications/index.ts"; +export * as organizationDomains from "./organizationDomains/index.ts"; +export * as organizationInvitations from "./organizationInvitations/index.ts"; +export * as organizationMemberships from "./organizationMemberships/index.ts"; +export * as organizationPermissions from "./organizationPermissions/index.ts"; +export * as organizationRoles from "./organizationRoles/index.ts"; +export * as organizations from "./organizations/index.ts"; +export * as phoneNumbers from "./phoneNumbers/index.ts"; +export * as proxyChecks from "./proxyChecks/index.ts"; +export * as redirectUrls from "./redirectUrls/index.ts"; +export * as roleSets from "./roleSets/index.ts"; +export * as sessions from "./sessions/index.ts"; +export * as signInTokens from "./signInTokens/index.ts"; +export * as signUps from "./signUps/index.ts"; +export * as testingTokens from "./testingTokens/index.ts"; +export * as users from "./users/index.ts"; +export * as waitlistEntries from "./waitlistEntries/index.ts"; +export * as webhooks from "./webhooks/index.ts"; diff --git a/packages/clerk/src/operations/backend/instanceSettings/GetInstanceProtect.ts b/packages/clerk/src/operations/backend/instanceSettings/GetInstanceProtect.ts new file mode 100644 index 0000000000..14cd8ff9cc --- /dev/null +++ b/packages/clerk/src/operations/backend/instanceSettings/GetInstanceProtect.ts @@ -0,0 +1,28 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; + +// Input Schema +export const GetInstanceProtectInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({}).pipe( + T.Http({ method: "GET", path: "/instance/protect" }), + ); +export type GetInstanceProtectInput = typeof GetInstanceProtectInput.Type; + +// Output Schema +export const GetInstanceProtectOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["instance_protect"]), + rules_enabled: Schema.Boolean, + specter_enabled: Schema.Boolean, + }); +export type GetInstanceProtectOutput = typeof GetInstanceProtectOutput.Type; + +// The operation +/** + * Get instance protect settings + */ +export const GetInstanceProtect = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInstanceProtectInput, + outputSchema: GetInstanceProtectOutput, +})); diff --git a/packages/clerk/src/operations/backend/instanceSettings/UpdateInstanceProtect.ts b/packages/clerk/src/operations/backend/instanceSettings/UpdateInstanceProtect.ts new file mode 100644 index 0000000000..ee87b9d921 --- /dev/null +++ b/packages/clerk/src/operations/backend/instanceSettings/UpdateInstanceProtect.ts @@ -0,0 +1,34 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const UpdateInstanceProtectInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + rules_enabled: Schema.optional(Schema.NullOr(Schema.Boolean)), + specter_enabled: Schema.optional(Schema.NullOr(Schema.Boolean)), + }).pipe(T.Http({ method: "PATCH", path: "/instance/protect" })); +export type UpdateInstanceProtectInput = typeof UpdateInstanceProtectInput.Type; + +// Output Schema +export const UpdateInstanceProtectOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["instance_protect"]), + rules_enabled: Schema.Boolean, + specter_enabled: Schema.Boolean, + }); +export type UpdateInstanceProtectOutput = + typeof UpdateInstanceProtectOutput.Type; + +// The operation +/** + * Update instance protect settings + */ +export const UpdateInstanceProtect = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: UpdateInstanceProtectInput, + outputSchema: UpdateInstanceProtectOutput, + errors: [UnprocessableEntity] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/instanceSettings/changeDomain.ts b/packages/clerk/src/operations/backend/instanceSettings/changeDomain.ts new file mode 100644 index 0000000000..079f4b75cf --- /dev/null +++ b/packages/clerk/src/operations/backend/instanceSettings/changeDomain.ts @@ -0,0 +1,29 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ChangeDomainInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + home_url: Schema.optional(Schema.String), + is_secondary: Schema.optional(Schema.Boolean), +}).pipe(T.Http({ method: "POST", path: "/instance/change_domain" })); +export type ChangeDomainInput = typeof ChangeDomainInput.Type; + +// Output Schema +export const ChangeDomainOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Void; +export type ChangeDomainOutput = typeof ChangeDomainOutput.Type; + +// The operation +/** + * Update production instance domain + * + * Change the domain of a production instance. + * Changing the domain requires updating the [DNS records](https://clerk.com/docs/deployments/overview#dns-records) accordingly, deploying new [SSL certificates](https://clerk.com/docs/deployments/overview#deploy-certificates), updating your Social Connection's redirect URLs and setting the new keys in your code. + * WARNING: Changing your domain will invalidate all current user sessions (i.e. users will be logged out). Also, while your application is being deployed, a small downtime is expected to occur. + */ +export const changeDomain = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ChangeDomainInput, + outputSchema: ChangeDomainOutput, + errors: [BadRequest, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/instanceSettings/get.ts b/packages/clerk/src/operations/backend/instanceSettings/get.ts new file mode 100644 index 0000000000..de9816015b --- /dev/null +++ b/packages/clerk/src/operations/backend/instanceSettings/get.ts @@ -0,0 +1,29 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; + +// Input Schema +export const GetInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({}).pipe( + T.Http({ method: "GET", path: "/instance" }), +); +export type GetInput = typeof GetInput.Type; + +// Output Schema +export const GetOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["instance"]), + id: Schema.String, + environment_type: Schema.String, + allowed_origins: Schema.NullOr(Schema.Array(Schema.String)), +}); +export type GetOutput = typeof GetOutput.Type; + +// The operation +/** + * Fetch the current instance + * + * Fetches the current instance + */ +export const get = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInput, + outputSchema: GetOutput, +})); diff --git a/packages/clerk/src/operations/backend/instanceSettings/getCommunication.ts b/packages/clerk/src/operations/backend/instanceSettings/getCommunication.ts new file mode 100644 index 0000000000..e06766d639 --- /dev/null +++ b/packages/clerk/src/operations/backend/instanceSettings/getCommunication.ts @@ -0,0 +1,29 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; + +// Input Schema +export const GetCommunicationInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + {}, +).pipe(T.Http({ method: "GET", path: "/instance/communication" })); +export type GetCommunicationInput = typeof GetCommunicationInput.Type; + +// Output Schema +export const GetCommunicationOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + { + object: Schema.Literals(["instance_communication"]), + blocked_country_codes: Schema.Array(Schema.String), + }, +); +export type GetCommunicationOutput = typeof GetCommunicationOutput.Type; + +// The operation +/** + * Get instance communication settings + * + * Retrieves the per-instance SMS communication settings, including the SMS country blocklist. + */ +export const getCommunication = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetCommunicationInput, + outputSchema: GetCommunicationOutput, +})); diff --git a/packages/clerk/src/operations/backend/instanceSettings/getOAuthApplicationSettings.ts b/packages/clerk/src/operations/backend/instanceSettings/getOAuthApplicationSettings.ts new file mode 100644 index 0000000000..c61d4ee8b9 --- /dev/null +++ b/packages/clerk/src/operations/backend/instanceSettings/getOAuthApplicationSettings.ts @@ -0,0 +1,34 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; + +// Input Schema +export const GetOAuthApplicationSettingsInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({}).pipe( + T.Http({ method: "GET", path: "/instance/oauth_application_settings" }), + ); +export type GetOAuthApplicationSettingsInput = + typeof GetOAuthApplicationSettingsInput.Type; + +// Output Schema +export const GetOAuthApplicationSettingsOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["oauth_application_settings"]), + dynamic_oauth_client_registration: Schema.Boolean, + oauth_jwt_access_tokens: Schema.Boolean, + }); +export type GetOAuthApplicationSettingsOutput = + typeof GetOAuthApplicationSettingsOutput.Type; + +// The operation +/** + * Get OAuth application settings + * + * Retrieves the settings for OAuth applications for the instance (dynamic client registration, JWT access tokens, etc.). + */ +export const getOAuthApplicationSettings = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: GetOAuthApplicationSettingsInput, + outputSchema: GetOAuthApplicationSettingsOutput, + }), +); diff --git a/packages/clerk/src/operations/backend/instanceSettings/getOrganizationSettings.ts b/packages/clerk/src/operations/backend/instanceSettings/getOrganizationSettings.ts new file mode 100644 index 0000000000..3a38f8afcc --- /dev/null +++ b/packages/clerk/src/operations/backend/instanceSettings/getOrganizationSettings.ts @@ -0,0 +1,57 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + PaymentRequired, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const GetOrganizationSettingsInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({}).pipe( + T.Http({ method: "GET", path: "/instance/organization_settings" }), + ); +export type GetOrganizationSettingsInput = + typeof GetOrganizationSettingsInput.Type; + +// Output Schema +export const GetOrganizationSettingsOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["organization_settings"]), + enabled: Schema.Boolean, + max_allowed_memberships: Schema.Number, + max_allowed_roles: Schema.Number, + max_role_sets_allowed: Schema.optional(Schema.Number), + max_allowed_domains: Schema.Number, + max_allowed_permissions: Schema.optional(Schema.Number), + creator_role: Schema.String, + admin_delete_enabled: Schema.Boolean, + domains_enabled: Schema.Boolean, + slug_disabled: Schema.optional(Schema.Boolean), + domains_enrollment_modes: Schema.Array( + Schema.Literals([ + "manual_invitation", + "automatic_invitation", + "automatic_suggestion", + ]), + ), + domains_default_role: Schema.String, + initial_role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }); +export type GetOrganizationSettingsOutput = + typeof GetOrganizationSettingsOutput.Type; + +// The operation +/** + * Get instance organization settings + * + * Retrieves the organization settings of the instance + */ +export const getOrganizationSettings = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: GetOrganizationSettingsInput, + outputSchema: GetOrganizationSettingsOutput, + errors: [PaymentRequired, NotFound, UnprocessableEntity] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/instanceSettings/index.ts b/packages/clerk/src/operations/backend/instanceSettings/index.ts new file mode 100644 index 0000000000..0df6f28a08 --- /dev/null +++ b/packages/clerk/src/operations/backend/instanceSettings/index.ts @@ -0,0 +1,12 @@ +export * from "./get.ts"; +export * from "./update.ts"; +export * from "./updateRestrictions.ts"; +export * from "./GetInstanceProtect.ts"; +export * from "./UpdateInstanceProtect.ts"; +export * from "./getCommunication.ts"; +export * from "./updateCommunication.ts"; +export * from "./getOAuthApplicationSettings.ts"; +export * from "./updateOAuthApplicationSettings.ts"; +export * from "./changeDomain.ts"; +export * from "./getOrganizationSettings.ts"; +export * from "./updateOrganizationSettings.ts"; diff --git a/packages/clerk/src/operations/backend/instanceSettings/update.ts b/packages/clerk/src/operations/backend/instanceSettings/update.ts new file mode 100644 index 0000000000..1d824cdcaa --- /dev/null +++ b/packages/clerk/src/operations/backend/instanceSettings/update.ts @@ -0,0 +1,36 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const UpdateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + test_mode: Schema.optional(Schema.NullOr(Schema.Boolean)), + hibp: Schema.optional(Schema.NullOr(Schema.Boolean)), + support_email: Schema.optional(Schema.NullOr(Schema.String)), + clerk_js_version: Schema.optional(Schema.NullOr(Schema.String)), + development_origin: Schema.optional(Schema.NullOr(Schema.String)), + allowed_origins: Schema.optional(Schema.Array(Schema.String)), + cookieless_dev: Schema.optional(Schema.NullOr(Schema.Boolean)), + url_based_session_syncing: Schema.optional(Schema.NullOr(Schema.Boolean)), + preferred_sign_in_strategy_when_password_required: Schema.optional( + Schema.NullOr(Schema.Literals(["password", "otp", ""])), + ), +}).pipe(T.Http({ method: "PATCH", path: "/instance" })); +export type UpdateInput = typeof UpdateInput.Type; + +// Output Schema +export const UpdateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Void; +export type UpdateOutput = typeof UpdateOutput.Type; + +// The operation +/** + * Update instance settings + * + * Updates the settings of an instance + */ +export const update = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateInput, + outputSchema: UpdateOutput, + errors: [UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/instanceSettings/updateCommunication.ts b/packages/clerk/src/operations/backend/instanceSettings/updateCommunication.ts new file mode 100644 index 0000000000..bfa0aa47e6 --- /dev/null +++ b/packages/clerk/src/operations/backend/instanceSettings/updateCommunication.ts @@ -0,0 +1,36 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const UpdateCommunicationInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + blocked_country_codes: Schema.optional( + Schema.NullOr(Schema.Array(Schema.String)), + ), + }).pipe(T.Http({ method: "PATCH", path: "/instance/communication" })); +export type UpdateCommunicationInput = typeof UpdateCommunicationInput.Type; + +// Output Schema +export const UpdateCommunicationOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["instance_communication"]), + blocked_country_codes: Schema.Array(Schema.String), + }); +export type UpdateCommunicationOutput = typeof UpdateCommunicationOutput.Type; + +// The operation +/** + * Update instance communication settings + * + * Replaces the SMS country blocklist for this instance. Pass the full set of ISO 3166-1 + * alpha-2 country codes that should be blocked; codes that aren't recognized as SMS-tier + * countries are silently dropped from the persisted list. Omitting `blocked_country_codes` + * is a no-op. + */ +export const updateCommunication = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateCommunicationInput, + outputSchema: UpdateCommunicationOutput, + errors: [UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/instanceSettings/updateOAuthApplicationSettings.ts b/packages/clerk/src/operations/backend/instanceSettings/updateOAuthApplicationSettings.ts new file mode 100644 index 0000000000..8dec4211fd --- /dev/null +++ b/packages/clerk/src/operations/backend/instanceSettings/updateOAuthApplicationSettings.ts @@ -0,0 +1,40 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const UpdateOAuthApplicationSettingsInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + dynamic_oauth_client_registration: Schema.optional( + Schema.NullOr(Schema.Boolean), + ), + oauth_jwt_access_tokens: Schema.optional(Schema.NullOr(Schema.Boolean)), + }).pipe( + T.Http({ method: "PATCH", path: "/instance/oauth_application_settings" }), + ); +export type UpdateOAuthApplicationSettingsInput = + typeof UpdateOAuthApplicationSettingsInput.Type; + +// Output Schema +export const UpdateOAuthApplicationSettingsOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["oauth_application_settings"]), + dynamic_oauth_client_registration: Schema.Boolean, + oauth_jwt_access_tokens: Schema.Boolean, + }); +export type UpdateOAuthApplicationSettingsOutput = + typeof UpdateOAuthApplicationSettingsOutput.Type; + +// The operation +/** + * Update OAuth application settings + * + * Updates the OAuth application settings for the instance. + */ +export const updateOAuthApplicationSettings = + /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateOAuthApplicationSettingsInput, + outputSchema: UpdateOAuthApplicationSettingsOutput, + errors: [UnprocessableEntity] as const, + })); diff --git a/packages/clerk/src/operations/backend/instanceSettings/updateOrganizationSettings.ts b/packages/clerk/src/operations/backend/instanceSettings/updateOrganizationSettings.ts new file mode 100644 index 0000000000..8d19f7acb5 --- /dev/null +++ b/packages/clerk/src/operations/backend/instanceSettings/updateOrganizationSettings.ts @@ -0,0 +1,70 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const UpdateOrganizationSettingsInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + enabled: Schema.optional(Schema.NullOr(Schema.Boolean)), + max_allowed_memberships: Schema.optional(Schema.NullOr(Schema.Number)), + admin_delete_enabled: Schema.optional(Schema.NullOr(Schema.Boolean)), + domains_enabled: Schema.optional(Schema.NullOr(Schema.Boolean)), + slug_disabled: Schema.optional(Schema.NullOr(Schema.Boolean)), + domains_enrollment_modes: Schema.optional(Schema.Array(Schema.String)), + creator_role_id: Schema.optional(Schema.NullOr(Schema.String)), + domains_default_role_id: Schema.optional(Schema.NullOr(Schema.String)), + }).pipe(T.Http({ method: "PATCH", path: "/instance/organization_settings" })); +export type UpdateOrganizationSettingsInput = + typeof UpdateOrganizationSettingsInput.Type; + +// Output Schema +export const UpdateOrganizationSettingsOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["organization_settings"]), + enabled: Schema.Boolean, + max_allowed_memberships: Schema.Number, + max_allowed_roles: Schema.Number, + max_role_sets_allowed: Schema.optional(Schema.Number), + max_allowed_domains: Schema.Number, + max_allowed_permissions: Schema.optional(Schema.Number), + creator_role: Schema.String, + admin_delete_enabled: Schema.Boolean, + domains_enabled: Schema.Boolean, + slug_disabled: Schema.optional(Schema.Boolean), + domains_enrollment_modes: Schema.Array( + Schema.Literals([ + "manual_invitation", + "automatic_invitation", + "automatic_suggestion", + ]), + ), + domains_default_role: Schema.String, + initial_role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }); +export type UpdateOrganizationSettingsOutput = + typeof UpdateOrganizationSettingsOutput.Type; + +// The operation +/** + * Update instance organization settings + * + * Updates the organization settings of the instance + */ +export const updateOrganizationSettings = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: UpdateOrganizationSettingsInput, + outputSchema: UpdateOrganizationSettingsOutput, + errors: [ + BadRequest, + PaymentRequired, + NotFound, + UnprocessableEntity, + ] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/instanceSettings/updateRestrictions.ts b/packages/clerk/src/operations/backend/instanceSettings/updateRestrictions.ts new file mode 100644 index 0000000000..0a2df8b729 --- /dev/null +++ b/packages/clerk/src/operations/backend/instanceSettings/updateRestrictions.ts @@ -0,0 +1,43 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { PaymentRequired, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const UpdateRestrictionsInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + allowlist: Schema.optional(Schema.NullOr(Schema.Boolean)), + blocklist: Schema.optional(Schema.NullOr(Schema.Boolean)), + allowlist_blocklist_disabled_on_sign_in: Schema.optional( + Schema.NullOr(Schema.Boolean), + ), + block_email_subaddresses: Schema.optional(Schema.NullOr(Schema.Boolean)), + block_disposable_email_domains: Schema.optional( + Schema.NullOr(Schema.Boolean), + ), + }).pipe(T.Http({ method: "PATCH", path: "/instance/restrictions" })); +export type UpdateRestrictionsInput = typeof UpdateRestrictionsInput.Type; + +// Output Schema +export const UpdateRestrictionsOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["instance_restrictions"]), + allowlist: Schema.Boolean, + blocklist: Schema.Boolean, + allowlist_blocklist_disabled_on_sign_in: Schema.Boolean, + block_email_subaddresses: Schema.Boolean, + block_disposable_email_domains: Schema.Boolean, + }); +export type UpdateRestrictionsOutput = typeof UpdateRestrictionsOutput.Type; + +// The operation +/** + * Update instance restrictions + * + * Updates the restriction settings of an instance + */ +export const updateRestrictions = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateRestrictionsInput, + outputSchema: UpdateRestrictionsOutput, + errors: [PaymentRequired, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/invitations/bulkCreate.ts b/packages/clerk/src/operations/backend/invitations/bulkCreate.ts new file mode 100644 index 0000000000..7ee6eba7a1 --- /dev/null +++ b/packages/clerk/src/operations/backend/invitations/bulkCreate.ts @@ -0,0 +1,43 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const BulkCreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + {}, +).pipe(T.Http({ method: "POST", path: "/invitations/bulk" })); +export type BulkCreateInput = typeof BulkCreateInput.Type; + +// Output Schema +export const BulkCreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Array( + Schema.Struct({ + object: Schema.Literals(["invitation"]), + id: Schema.String, + email_address: Schema.String, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + revoked: Schema.optional(Schema.Boolean), + status: Schema.Literals(["pending", "accepted", "revoked", "expired"]), + url: Schema.optional(Schema.String), + expires_at: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), +); +export type BulkCreateOutput = typeof BulkCreateOutput.Type; + +// The operation +/** + * Create multiple invitations + * + * Use this API operation to create multiple invitations for the provided email addresses. You can choose to send the + * invitations as emails by setting the `notify` parameter to `true`. There cannot be an existing invitation for any + * of the email addresses you provide unless you set `ignore_existing` to `true` for specific email addresses. Please + * note that there must be no existing user for any of the email addresses you provide, and this rule cannot be bypassed. + * This endpoint is limited to a maximum of 10 invitations per API call. If you need to send more invitations, please make multiple requests. + */ +export const bulkCreate = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: BulkCreateInput, + outputSchema: BulkCreateOutput, + errors: [BadRequest, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/invitations/create.ts b/packages/clerk/src/operations/backend/invitations/create.ts new file mode 100644 index 0000000000..9e9eb6aef3 --- /dev/null +++ b/packages/clerk/src/operations/backend/invitations/create.ts @@ -0,0 +1,49 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + email_address: Schema.String, + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + redirect_url: Schema.optional(Schema.String), + notify: Schema.optional(Schema.NullOr(Schema.Boolean)), + ignore_existing: Schema.optional(Schema.NullOr(Schema.Boolean)), + expires_in_days: Schema.optional(Schema.NullOr(Schema.Number)), + template_slug: Schema.optional( + Schema.Literals(["invitation", "waitlist_invitation"]), + ), +}).pipe(T.Http({ method: "POST", path: "/invitations" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["invitation"]), + id: Schema.String, + email_address: Schema.String, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + revoked: Schema.optional(Schema.Boolean), + status: Schema.Literals(["pending", "accepted", "revoked", "expired"]), + url: Schema.optional(Schema.String), + expires_at: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create an invitation + * + * Creates a new invitation for the given email address and sends the invitation email. + * Keep in mind that you cannot create an invitation if there is already one for the given email address. + * Also, trying to create an invitation for an email address that already exists in your application will result to an error. + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [BadRequest, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/invitations/index.ts b/packages/clerk/src/operations/backend/invitations/index.ts new file mode 100644 index 0000000000..5d0eb7cb8c --- /dev/null +++ b/packages/clerk/src/operations/backend/invitations/index.ts @@ -0,0 +1,4 @@ +export * from "./list.ts"; +export * from "./create.ts"; +export * from "./bulkCreate.ts"; +export * from "./revoke.ts"; diff --git a/packages/clerk/src/operations/backend/invitations/list.ts b/packages/clerk/src/operations/backend/invitations/list.ts new file mode 100644 index 0000000000..ac6a5f712b --- /dev/null +++ b/packages/clerk/src/operations/backend/invitations/list.ts @@ -0,0 +1,61 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; + +// Input Schema +export const ListInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + status: Schema.optional( + Schema.Literals(["pending", "accepted", "revoked", "expired"]), + ), + query: Schema.optional(Schema.String), + order_by: Schema.optional(Schema.String), + paginated: Schema.optional(Schema.Boolean), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), +}).pipe(T.Http({ method: "GET", path: "/invitations" })); +export type ListInput = typeof ListInput.Type; + +// Output Schema +export const ListOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Array( + Schema.Struct({ + object: Schema.Literals(["invitation"]), + id: Schema.String, + email_address: Schema.String, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + revoked: Schema.optional(Schema.Boolean), + status: Schema.Literals(["pending", "accepted", "revoked", "expired"]), + url: Schema.optional(Schema.String), + expires_at: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), +); +export type ListOutput = typeof ListOutput.Type; + +// The operation +/** + * List all invitations + * + * Returns all non-revoked invitations for your application, sorted by creation date + * + * @param status - Filter invitations based on their status + * @param query - Filter invitations based on their `email_address` or `id` + * @param order_by - Allows to return invitations in a particular order. +At the moment, you can order the returned invitations either by their `created_at`, `email_address` or `expires_at`. +In order to specify the direction, you can use the `+/-` symbols prepended in the property to order by. +For example, if you want invitations to be returned in descending order according to their `created_at` property, you can use `-created_at`. +If you don't use `+` or `-`, then `+` is implied. +Defaults to `-created_at`. + * @param paginated - Whether to paginate the results. +If true, the results will be paginated. +If false, the results will not be paginated. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + */ +export const list = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInput, + outputSchema: ListOutput, +})); diff --git a/packages/clerk/src/operations/backend/invitations/revoke.ts b/packages/clerk/src/operations/backend/invitations/revoke.ts new file mode 100644 index 0000000000..febdb6c007 --- /dev/null +++ b/packages/clerk/src/operations/backend/invitations/revoke.ts @@ -0,0 +1,44 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; + +// Input Schema +export const RevokeInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + invitation_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ method: "POST", path: "/invitations/{invitation_id}/revoke" }), +); +export type RevokeInput = typeof RevokeInput.Type; + +// Output Schema +export const RevokeOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["invitation"]), + id: Schema.String, + email_address: Schema.String, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + revoked: Schema.optional(Schema.Literals(["true"])), + status: Schema.Literals(["revoked"]), + url: Schema.optional(Schema.String), + expires_at: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type RevokeOutput = typeof RevokeOutput.Type; + +// The operation +/** + * Revokes an invitation + * + * Revokes the given invitation. + * Revoking an invitation will prevent the user from using the invitation link that was sent to them. + * However, it doesn't prevent the user from signing up if they follow the sign up flow. + * Only active (i.e. non-revoked) invitations can be revoked. + * + * @param invitation_id - The ID of the invitation to be revoked + */ +export const revoke = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: RevokeInput, + outputSchema: RevokeOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/jwks/getJWKS.ts b/packages/clerk/src/operations/backend/jwks/getJWKS.ts new file mode 100644 index 0000000000..c80608da24 --- /dev/null +++ b/packages/clerk/src/operations/backend/jwks/getJWKS.ts @@ -0,0 +1,24 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; + +// Input Schema +export const GetJWKSInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({}).pipe( + T.Http({ method: "GET", path: "/jwks" }), +); +export type GetJWKSInput = typeof GetJWKSInput.Type; + +// Output Schema +export const GetJWKSOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + keys: Schema.optional(Schema.Array(Schema.Unknown)), +}); +export type GetJWKSOutput = typeof GetJWKSOutput.Type; + +// The operation +/** + * Retrieve the JSON Web Key Set of the instance + */ +export const getJWKS = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetJWKSInput, + outputSchema: GetJWKSOutput, +})); diff --git a/packages/clerk/src/operations/backend/jwks/index.ts b/packages/clerk/src/operations/backend/jwks/index.ts new file mode 100644 index 0000000000..b99de3ef3f --- /dev/null +++ b/packages/clerk/src/operations/backend/jwks/index.ts @@ -0,0 +1 @@ +export * from "./getJWKS.ts"; diff --git a/packages/clerk/src/operations/backend/jwtTemplates/create.ts b/packages/clerk/src/operations/backend/jwtTemplates/create.ts new file mode 100644 index 0000000000..d41d0d1753 --- /dev/null +++ b/packages/clerk/src/operations/backend/jwtTemplates/create.ts @@ -0,0 +1,47 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + name: Schema.String, + claims: Schema.Unknown, + lifetime: Schema.optional(Schema.NullOr(Schema.Number)), + allowed_clock_skew: Schema.optional(Schema.NullOr(Schema.Number)), + custom_signing_key: Schema.optional(Schema.Boolean), + signing_algorithm: Schema.optional(Schema.NullOr(Schema.String)), + signing_key: Schema.optional(Schema.NullOr(Schema.String)), +}).pipe(T.Http({ method: "POST", path: "/jwt_templates" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["jwt_template"]), + id: Schema.String, + name: Schema.String, + claims: Schema.Unknown, + lifetime: Schema.Number, + allowed_clock_skew: Schema.Number, + custom_signing_key: Schema.Boolean, + signing_algorithm: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create a JWT template + * + * Create a new JWT template + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [BadRequest, PaymentRequired, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/jwtTemplates/delete.ts b/packages/clerk/src/operations/backend/jwtTemplates/delete.ts new file mode 100644 index 0000000000..a8befe2fd8 --- /dev/null +++ b/packages/clerk/src/operations/backend/jwtTemplates/delete.ts @@ -0,0 +1,33 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + template_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "DELETE", path: "/jwt_templates/{template_id}" })); +export type DeleteInput = typeof DeleteInput.Type; + +// Output Schema +export const DeleteOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), +}); +export type DeleteOutput = typeof DeleteOutput.Type; + +// The operation +/** + * Delete a Template + * + * @param template_id - JWT Template ID + */ +const delete_ = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteInput, + outputSchema: DeleteOutput, + errors: [Forbidden, NotFound] as const, +})); +export { delete_ as delete }; diff --git a/packages/clerk/src/operations/backend/jwtTemplates/get.ts b/packages/clerk/src/operations/backend/jwtTemplates/get.ts new file mode 100644 index 0000000000..f7a6babc17 --- /dev/null +++ b/packages/clerk/src/operations/backend/jwtTemplates/get.ts @@ -0,0 +1,39 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + template_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "GET", path: "/jwt_templates/{template_id}" })); +export type GetInput = typeof GetInput.Type; + +// Output Schema +export const GetOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["jwt_template"]), + id: Schema.String, + name: Schema.String, + claims: Schema.Unknown, + lifetime: Schema.Number, + allowed_clock_skew: Schema.Number, + custom_signing_key: Schema.Boolean, + signing_algorithm: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type GetOutput = typeof GetOutput.Type; + +// The operation +/** + * Retrieve a template + * + * Retrieve the details of a given JWT template + * + * @param template_id - JWT Template ID + */ +export const get = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInput, + outputSchema: GetOutput, + errors: [NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/jwtTemplates/index.ts b/packages/clerk/src/operations/backend/jwtTemplates/index.ts new file mode 100644 index 0000000000..9bc43612a5 --- /dev/null +++ b/packages/clerk/src/operations/backend/jwtTemplates/index.ts @@ -0,0 +1,5 @@ +export * from "./list.ts"; +export * from "./create.ts"; +export * from "./get.ts"; +export * from "./update.ts"; +export * from "./delete.ts"; diff --git a/packages/clerk/src/operations/backend/jwtTemplates/list.ts b/packages/clerk/src/operations/backend/jwtTemplates/list.ts new file mode 100644 index 0000000000..880fb710ab --- /dev/null +++ b/packages/clerk/src/operations/backend/jwtTemplates/list.ts @@ -0,0 +1,46 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; + +// Input Schema +export const ListInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + paginated: Schema.optional(Schema.Boolean), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), +}).pipe(T.Http({ method: "GET", path: "/jwt_templates" })); +export type ListInput = typeof ListInput.Type; + +// Output Schema +export const ListOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Array( + Schema.Struct({ + object: Schema.Literals(["jwt_template"]), + id: Schema.String, + name: Schema.String, + claims: Schema.Unknown, + lifetime: Schema.Number, + allowed_clock_skew: Schema.Number, + custom_signing_key: Schema.Boolean, + signing_algorithm: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + }), +); +export type ListOutput = typeof ListOutput.Type; + +// The operation +/** + * List all templates + * + * @param paginated - Whether to paginate the results. +If true, the results will be paginated. +If false, the results will not be paginated. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + */ +export const list = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInput, + outputSchema: ListOutput, +})); diff --git a/packages/clerk/src/operations/backend/jwtTemplates/update.ts b/packages/clerk/src/operations/backend/jwtTemplates/update.ts new file mode 100644 index 0000000000..637cef0dec --- /dev/null +++ b/packages/clerk/src/operations/backend/jwtTemplates/update.ts @@ -0,0 +1,50 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const UpdateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + template_id: Schema.String.pipe(T.PathParam()), + name: Schema.String, + claims: Schema.Unknown, + lifetime: Schema.optional(Schema.NullOr(Schema.Number)), + allowed_clock_skew: Schema.optional(Schema.NullOr(Schema.Number)), + custom_signing_key: Schema.optional(Schema.Boolean), + signing_algorithm: Schema.optional(Schema.NullOr(Schema.String)), + signing_key: Schema.optional(Schema.NullOr(Schema.String)), +}).pipe(T.Http({ method: "PATCH", path: "/jwt_templates/{template_id}" })); +export type UpdateInput = typeof UpdateInput.Type; + +// Output Schema +export const UpdateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["jwt_template"]), + id: Schema.String, + name: Schema.String, + claims: Schema.Unknown, + lifetime: Schema.Number, + allowed_clock_skew: Schema.Number, + custom_signing_key: Schema.Boolean, + signing_algorithm: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type UpdateOutput = typeof UpdateOutput.Type; + +// The operation +/** + * Update a JWT template + * + * Updates an existing JWT template + * + * @param template_id - The ID of the JWT template to update + */ +export const update = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateInput, + outputSchema: UpdateOutput, + errors: [BadRequest, PaymentRequired, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/m2m/createToken.ts b/packages/clerk/src/operations/backend/m2m/createToken.ts new file mode 100644 index 0000000000..87d945ad5b --- /dev/null +++ b/packages/clerk/src/operations/backend/m2m/createToken.ts @@ -0,0 +1,43 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Conflict } from "../../../errors.ts"; + +// Input Schema +export const CreateTokenInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + token_format: Schema.optional(Schema.Literals(["opaque", "jwt"])), + seconds_until_expiration: Schema.optional(Schema.NullOr(Schema.Number)), + claims: Schema.optional(Schema.NullOr(Schema.Unknown)), + min_remaining_ttl_seconds: Schema.optional(Schema.Number), +}).pipe(T.Http({ method: "POST", path: "/m2m_tokens" })); +export type CreateTokenInput = typeof CreateTokenInput.Type; + +// Output Schema +export const CreateTokenOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["machine_to_machine_token"]), + id: Schema.String, + subject: Schema.String, + claims: Schema.optional(Schema.NullOr(Schema.Unknown)), + scopes: Schema.optional(Schema.Array(Schema.String)), + token: Schema.String, + revoked: Schema.Boolean, + revocation_reason: Schema.NullOr(Schema.String), + expired: Schema.Boolean, + expiration: Schema.NullOr(Schema.Number), + last_used_at: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type CreateTokenOutput = typeof CreateTokenOutput.Type; + +// The operation +/** + * Create a M2M Token + * + * Creates a new M2M Token. Must be authenticated via a Machine Secret Key. + */ +export const createToken = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateTokenInput, + outputSchema: CreateTokenOutput, + errors: [BadRequest, Conflict] as const, +})); diff --git a/packages/clerk/src/operations/backend/m2m/index.ts b/packages/clerk/src/operations/backend/m2m/index.ts new file mode 100644 index 0000000000..760970a9f0 --- /dev/null +++ b/packages/clerk/src/operations/backend/m2m/index.ts @@ -0,0 +1,4 @@ +export * from "./listTokens.ts"; +export * from "./createToken.ts"; +export * from "./revokeToken.ts"; +export * from "./verifyToken.ts"; diff --git a/packages/clerk/src/operations/backend/m2m/listTokens.ts b/packages/clerk/src/operations/backend/m2m/listTokens.ts new file mode 100644 index 0000000000..895d07c6de --- /dev/null +++ b/packages/clerk/src/operations/backend/m2m/listTokens.ts @@ -0,0 +1,52 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const ListTokensInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + subject: Schema.String, + revoked: Schema.optional(Schema.Boolean), + expired: Schema.optional(Schema.Boolean), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), +}).pipe(T.Http({ method: "GET", path: "/m2m_tokens" })); +export type ListTokensInput = typeof ListTokensInput.Type; + +// Output Schema +export const ListTokensOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + m2m_tokens: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["machine_to_machine_token"]), + id: Schema.String, + subject: Schema.String, + claims: Schema.optional(Schema.NullOr(Schema.Unknown)), + scopes: Schema.optional(Schema.Array(Schema.String)), + revoked: Schema.Boolean, + revocation_reason: Schema.NullOr(Schema.String), + expired: Schema.Boolean, + expiration: Schema.NullOr(Schema.Number), + last_used_at: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + total_count: Schema.Number, +}); +export type ListTokensOutput = typeof ListTokensOutput.Type; + +// The operation +/** + * Get M2M Tokens + * + * Fetches M2M tokens for a specific machine. + * Only tokens created with the opaque token format are returned by this endpoint. JWT-format M2M tokens are stateless and are not stored. + * This endpoint can be authenticated by either a Machine Secret Key or by a Clerk Secret Key. + * - When fetching M2M tokens with a Machine Secret Key, only tokens associated with the authenticated machine can be retrieved. + * - When fetching M2M tokens with a Clerk Secret Key, tokens for any machine in the instance can be retrieved. + */ +export const listTokens = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListTokensInput, + outputSchema: ListTokensOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/m2m/revokeToken.ts b/packages/clerk/src/operations/backend/m2m/revokeToken.ts new file mode 100644 index 0000000000..3ddb64e9f5 --- /dev/null +++ b/packages/clerk/src/operations/backend/m2m/revokeToken.ts @@ -0,0 +1,44 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; + +// Input Schema +export const RevokeTokenInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + m2m_token_id: Schema.String.pipe(T.PathParam()), + revocation_reason: Schema.optional(Schema.NullOr(Schema.String)), +}).pipe(T.Http({ method: "POST", path: "/m2m_tokens/{m2m_token_id}/revoke" })); +export type RevokeTokenInput = typeof RevokeTokenInput.Type; + +// Output Schema +export const RevokeTokenOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["machine_to_machine_token"]), + id: Schema.String, + subject: Schema.String, + claims: Schema.optional(Schema.NullOr(Schema.Unknown)), + scopes: Schema.optional(Schema.Array(Schema.String)), + revoked: Schema.Boolean, + revocation_reason: Schema.NullOr(Schema.String), + expired: Schema.Boolean, + expiration: Schema.NullOr(Schema.Number), + last_used_at: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type RevokeTokenOutput = typeof RevokeTokenOutput.Type; + +// The operation +/** + * Revoke a M2M Token + * + * Revokes a M2M Token. + * This endpoint only revokes stored opaque-format M2M tokens. JWT-format M2M tokens are stateless and cannot be revoked. + * This endpoint can be authenticated by either a Machine Secret Key or by a Clerk Secret Key. + * - When revoking a M2M Token with a Machine Secret Key, the token must managed by the Machine associated with the Machine Secret Key. + * - When revoking a M2M Token with a Clerk Secret Key, any token on the Instance can be revoked. + */ +export const revokeToken = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: RevokeTokenInput, + outputSchema: RevokeTokenOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/m2m/verifyToken.ts b/packages/clerk/src/operations/backend/m2m/verifyToken.ts new file mode 100644 index 0000000000..87a4324082 --- /dev/null +++ b/packages/clerk/src/operations/backend/m2m/verifyToken.ts @@ -0,0 +1,42 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; + +// Input Schema +export const VerifyTokenInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + token: Schema.String, +}).pipe(T.Http({ method: "POST", path: "/m2m_tokens/verify" })); +export type VerifyTokenInput = typeof VerifyTokenInput.Type; + +// Output Schema +export const VerifyTokenOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["machine_to_machine_token"]), + id: Schema.String, + subject: Schema.String, + claims: Schema.optional(Schema.NullOr(Schema.Unknown)), + scopes: Schema.optional(Schema.Array(Schema.String)), + revoked: Schema.Boolean, + revocation_reason: Schema.NullOr(Schema.String), + expired: Schema.Boolean, + expiration: Schema.NullOr(Schema.Number), + last_used_at: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type VerifyTokenOutput = typeof VerifyTokenOutput.Type; + +// The operation +/** + * Verify a M2M Token + * + * Verifies a M2M Token. + * This endpoint can be authenticated by either a Machine Secret Key or by a Clerk Secret Key. + * - When verifying a M2M Token with a Machine Secret Key, the token must be granted access to the Machine associated with the Machine Secret Key. + * - When verifying a M2M Token with a Clerk Secret Key, any token on the Instance can be verified. + */ +export const verifyToken = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: VerifyTokenInput, + outputSchema: VerifyTokenOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/machines/create.ts b/packages/clerk/src/operations/backend/machines/create.ts new file mode 100644 index 0000000000..005f2cc397 --- /dev/null +++ b/packages/clerk/src/operations/backend/machines/create.ts @@ -0,0 +1,31 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, UnprocessableEntity } from "../../../errors.ts"; +import { SensitiveOutputString } from "../../../sensitive.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + name: Schema.String, + scoped_machines: Schema.optional(Schema.Array(Schema.String)), + default_token_ttl: Schema.optional(Schema.Number), +}).pipe(T.Http({ method: "POST", path: "/machines" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + secret_key: SensitiveOutputString, +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create a machine + * + * Creates a new machine. + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [BadRequest, Forbidden, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/machines/createScope.ts b/packages/clerk/src/operations/backend/machines/createScope.ts new file mode 100644 index 0000000000..1e54ad1be2 --- /dev/null +++ b/packages/clerk/src/operations/backend/machines/createScope.ts @@ -0,0 +1,47 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + Conflict, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const CreateScopeInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + machine_id: Schema.String.pipe(T.PathParam()), + to_machine_id: Schema.String, +}).pipe(T.Http({ method: "POST", path: "/machines/{machine_id}/scopes" })); +export type CreateScopeInput = typeof CreateScopeInput.Type; + +// Output Schema +export const CreateScopeOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["machine_scope"]), + from_machine_id: Schema.String, + to_machine_id: Schema.String, + created_at: Schema.Number, +}); +export type CreateScopeOutput = typeof CreateScopeOutput.Type; + +// The operation +/** + * Create a machine scope + * + * Creates a new machine scope, allowing the specified machine to access another machine. + * Maximum of 150 scopes per machine. + * + * @param machine_id - The ID of the machine that will have access to another machine + */ +export const createScope = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateScopeInput, + outputSchema: CreateScopeOutput, + errors: [ + BadRequest, + Forbidden, + NotFound, + Conflict, + UnprocessableEntity, + ] as const, +})); diff --git a/packages/clerk/src/operations/backend/machines/delete.ts b/packages/clerk/src/operations/backend/machines/delete.ts new file mode 100644 index 0000000000..c4b51afb84 --- /dev/null +++ b/packages/clerk/src/operations/backend/machines/delete.ts @@ -0,0 +1,38 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const DeleteInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + machine_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "DELETE", path: "/machines/{machine_id}" })); +export type DeleteInput = typeof DeleteInput.Type; + +// Output Schema +export const DeleteOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["machine"]), + id: Schema.String, + deleted: Schema.Literals(["true"]), +}); +export type DeleteOutput = typeof DeleteOutput.Type; + +// The operation +/** + * Delete a machine + * + * Deletes a machine. + * + * @param machine_id - The ID of the machine to delete + */ +const delete_ = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteInput, + outputSchema: DeleteOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); +export { delete_ as delete }; diff --git a/packages/clerk/src/operations/backend/machines/deleteScope.ts b/packages/clerk/src/operations/backend/machines/deleteScope.ts new file mode 100644 index 0000000000..72eca312e7 --- /dev/null +++ b/packages/clerk/src/operations/backend/machines/deleteScope.ts @@ -0,0 +1,45 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const DeleteScopeInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + machine_id: Schema.String.pipe(T.PathParam()), + other_machine_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "DELETE", + path: "/machines/{machine_id}/scopes/{other_machine_id}", + }), +); +export type DeleteScopeInput = typeof DeleteScopeInput.Type; + +// Output Schema +export const DeleteScopeOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["machine_scope"]), + from_machine_id: Schema.String, + to_machine_id: Schema.String, + deleted: Schema.Literals(["true"]), +}); +export type DeleteScopeOutput = typeof DeleteScopeOutput.Type; + +// The operation +/** + * Delete a machine scope + * + * Deletes a machine scope, removing access from one machine to another. + * + * @param machine_id - The ID of the machine that has access to another machine + * @param other_machine_id - The ID of the machine that is being accessed + */ +export const deleteScope = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteScopeInput, + outputSchema: DeleteScopeOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/machines/get.ts b/packages/clerk/src/operations/backend/machines/get.ts new file mode 100644 index 0000000000..1b990ce4a2 --- /dev/null +++ b/packages/clerk/src/operations/backend/machines/get.ts @@ -0,0 +1,52 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const GetInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + machine_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "GET", path: "/machines/{machine_id}" })); +export type GetInput = typeof GetInput.Type; + +// Output Schema +export const GetOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["machine"]), + id: Schema.String, + name: Schema.String, + instance_id: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + default_token_ttl: Schema.optional(Schema.Number), + scoped_machines: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["machine"]), + id: Schema.String, + name: Schema.String, + instance_id: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + default_token_ttl: Schema.optional(Schema.Number), + }), + ), +}); +export type GetOutput = typeof GetOutput.Type; + +// The operation +/** + * Retrieve a machine + * + * Returns the details of a machine. + * + * @param machine_id - The ID of the machine to retrieve + */ +export const get = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInput, + outputSchema: GetOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/machines/getSecretKey.ts b/packages/clerk/src/operations/backend/machines/getSecretKey.ts new file mode 100644 index 0000000000..cf18746189 --- /dev/null +++ b/packages/clerk/src/operations/backend/machines/getSecretKey.ts @@ -0,0 +1,32 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; +import { SensitiveOutputString } from "../../../sensitive.ts"; + +// Input Schema +export const GetSecretKeyInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + machine_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "GET", path: "/machines/{machine_id}/secret_key" })); +export type GetSecretKeyInput = typeof GetSecretKeyInput.Type; + +// Output Schema +export const GetSecretKeyOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["machine_secret_key"]), + secret: SensitiveOutputString, +}); +export type GetSecretKeyOutput = typeof GetSecretKeyOutput.Type; + +// The operation +/** + * Retrieve a machine secret key + * + * Returns the secret key for a machine. + * + * @param machine_id - The ID of the machine to retrieve the secret key for + */ +export const getSecretKey = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetSecretKeyInput, + outputSchema: GetSecretKeyOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/machines/index.ts b/packages/clerk/src/operations/backend/machines/index.ts new file mode 100644 index 0000000000..a42e7b7344 --- /dev/null +++ b/packages/clerk/src/operations/backend/machines/index.ts @@ -0,0 +1,9 @@ +export * from "./list.ts"; +export * from "./create.ts"; +export * from "./get.ts"; +export * from "./update.ts"; +export * from "./delete.ts"; +export * from "./getSecretKey.ts"; +export * from "./rotateSecretKey.ts"; +export * from "./createScope.ts"; +export * from "./deleteScope.ts"; diff --git a/packages/clerk/src/operations/backend/machines/list.ts b/packages/clerk/src/operations/backend/machines/list.ts new file mode 100644 index 0000000000..ded8b50bd9 --- /dev/null +++ b/packages/clerk/src/operations/backend/machines/list.ts @@ -0,0 +1,68 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ListInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), + query: Schema.optional(Schema.String), + order_by: Schema.optional(Schema.String), +}).pipe(T.Http({ method: "GET", path: "/machines" })); +export type ListInput = typeof ListInput.Type; + +// Output Schema +export const ListOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["machine"]), + id: Schema.String, + name: Schema.String, + instance_id: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + default_token_ttl: Schema.optional(Schema.Number), + scoped_machines: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["machine"]), + id: Schema.String, + name: Schema.String, + instance_id: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + default_token_ttl: Schema.optional(Schema.Number), + }), + ), + }), + ), + total_count: Schema.Number, +}); +export type ListOutput = typeof ListOutput.Type; + +// The operation +/** + * Get a list of machines for an instance + * + * This request returns the list of machines for an instance. The machines are + * ordered by descending creation date (i.e. most recent machines will be + * returned first) + * + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + * @param query - Returns machines with ID or name that match the given query. Uses exact match for machine ID and partial match for name. + * @param order_by - Allows to return machines in a particular order. +You can order the returned machines by their `name` or `created_at`. +To specify the direction, use the `+` or `-` symbols prepended to the property to order by. +For example, to return machines in descending order by `created_at`, use `-created_at`. +If you don't use `+` or `-`, then `+` is implied. +Defaults to `-created_at`. + */ +export const list = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInput, + outputSchema: ListOutput, + errors: [BadRequest, Forbidden, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/machines/rotateSecretKey.ts b/packages/clerk/src/operations/backend/machines/rotateSecretKey.ts new file mode 100644 index 0000000000..cf9b113d16 --- /dev/null +++ b/packages/clerk/src/operations/backend/machines/rotateSecretKey.ts @@ -0,0 +1,42 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; +import { SensitiveOutputString } from "../../../sensitive.ts"; + +// Input Schema +export const RotateSecretKeyInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + machine_id: Schema.String.pipe(T.PathParam()), + previous_token_ttl: Schema.Number, +}).pipe( + T.Http({ method: "POST", path: "/machines/{machine_id}/secret_key/rotate" }), +); +export type RotateSecretKeyInput = typeof RotateSecretKeyInput.Type; + +// Output Schema +export const RotateSecretKeyOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["machine_secret_key"]), + secret: SensitiveOutputString, +}); +export type RotateSecretKeyOutput = typeof RotateSecretKeyOutput.Type; + +// The operation +/** + * Rotate a machine's secret key + * + * Rotates the machine's secret key. + * When the secret key is rotated, make sure to update it in your machine/application. + * The previous secret key will remain valid for the duration specified by the previous_token_ttl parameter. + * + * @param machine_id - The ID of the machine to rotate the secret key for + */ +export const rotateSecretKey = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: RotateSecretKeyInput, + outputSchema: RotateSecretKeyOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/machines/update.ts b/packages/clerk/src/operations/backend/machines/update.ts new file mode 100644 index 0000000000..aab0b1a3e9 --- /dev/null +++ b/packages/clerk/src/operations/backend/machines/update.ts @@ -0,0 +1,55 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const UpdateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + machine_id: Schema.String.pipe(T.PathParam()), + name: Schema.optional(Schema.String), + default_token_ttl: Schema.optional(Schema.Number), +}).pipe(T.Http({ method: "PATCH", path: "/machines/{machine_id}" })); +export type UpdateInput = typeof UpdateInput.Type; + +// Output Schema +export const UpdateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["machine"]), + id: Schema.String, + name: Schema.String, + instance_id: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + default_token_ttl: Schema.optional(Schema.Number), + scoped_machines: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["machine"]), + id: Schema.String, + name: Schema.String, + instance_id: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + default_token_ttl: Schema.optional(Schema.Number), + }), + ), +}); +export type UpdateOutput = typeof UpdateOutput.Type; + +// The operation +/** + * Update a machine + * + * Updates an existing machine. + * Only the provided fields will be updated. + * + * @param machine_id - The ID of the machine to update + */ +export const update = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateInput, + outputSchema: UpdateOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/miscellaneous/getPublicInterstitial.ts b/packages/clerk/src/operations/backend/miscellaneous/getPublicInterstitial.ts new file mode 100644 index 0000000000..4d39a50063 --- /dev/null +++ b/packages/clerk/src/operations/backend/miscellaneous/getPublicInterstitial.ts @@ -0,0 +1,52 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest } from "../../../errors.ts"; + +// Input Schema +export const GetPublicInterstitialInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + frontendApi: Schema.optional(Schema.String), + frontend_api: Schema.optional(Schema.String), + publishable_key: Schema.optional(Schema.String), + proxy_url: Schema.optional(Schema.String), + domain: Schema.optional(Schema.String), + sign_in_url: Schema.optional(Schema.String), + application_name: Schema.optional(Schema.String), + logo_url: Schema.optional(Schema.String), + flow: Schema.optional(Schema.String), + use_domain_for_script: Schema.optional(Schema.Boolean), + }).pipe(T.Http({ method: "GET", path: "/public/interstitial" })); +export type GetPublicInterstitialInput = typeof GetPublicInterstitialInput.Type; + +// Output Schema +export const GetPublicInterstitialOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Void; +export type GetPublicInterstitialOutput = + typeof GetPublicInterstitialOutput.Type; + +// The operation +/** + * Returns the markup for the interstitial page + * + * The Clerk interstitial endpoint serves an html page that loads clerk.js in order to check the user's authentication state. + * It is used by Clerk SDKs when the user's authentication state cannot be immediately determined. + * + * @param frontendApi - Please use `frontend_api` instead + * @param frontend_api - The Frontend API key of your instance + * @param publishable_key - The publishable key of your instance + * @param proxy_url - The proxy URL of your instance + * @param domain - The domain of your instance + * @param sign_in_url - The sign in URL of your instance + * @param application_name - The application name shown in the interstitial UI + * @param logo_url - The absolute HTTP(S) application logo URL shown in the interstitial UI + * @param flow - The authentication flow shown in the interstitial UI + * @param use_domain_for_script - Whether to use the domain for the script URL + */ +export const getPublicInterstitial = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: GetPublicInterstitialInput, + outputSchema: GetPublicInterstitialOutput, + errors: [BadRequest] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/miscellaneous/index.ts b/packages/clerk/src/operations/backend/miscellaneous/index.ts new file mode 100644 index 0000000000..b2999ac9f8 --- /dev/null +++ b/packages/clerk/src/operations/backend/miscellaneous/index.ts @@ -0,0 +1 @@ +export * from "./getPublicInterstitial.ts"; diff --git a/packages/clerk/src/operations/backend/oauthAccessTokens/index.ts b/packages/clerk/src/operations/backend/oauthAccessTokens/index.ts new file mode 100644 index 0000000000..f90f678bbf --- /dev/null +++ b/packages/clerk/src/operations/backend/oauthAccessTokens/index.ts @@ -0,0 +1 @@ +export * from "./verify.ts"; diff --git a/packages/clerk/src/operations/backend/oauthAccessTokens/verify.ts b/packages/clerk/src/operations/backend/oauthAccessTokens/verify.ts new file mode 100644 index 0000000000..9b1775e1c5 --- /dev/null +++ b/packages/clerk/src/operations/backend/oauthAccessTokens/verify.ts @@ -0,0 +1,28 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; +import { SensitiveString } from "../../../sensitive.ts"; + +// Input Schema +export const VerifyInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + access_token: Schema.optional(SensitiveString), + secret: Schema.optional(SensitiveString), +}).pipe( + T.Http({ method: "POST", path: "/oauth_applications/access_tokens/verify" }), +); +export type VerifyInput = typeof VerifyInput.Type; + +// Output Schema +export const VerifyOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Unknown; +export type VerifyOutput = typeof VerifyOutput.Type; + +// The operation +/** + * Verify an OAuth Access Token + */ +export const verify = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: VerifyInput, + outputSchema: VerifyOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/oauthApplications/create.ts b/packages/clerk/src/operations/backend/oauthApplications/create.ts new file mode 100644 index 0000000000..4e9a260cff --- /dev/null +++ b/packages/clerk/src/operations/backend/oauthApplications/create.ts @@ -0,0 +1,58 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, UnprocessableEntity } from "../../../errors.ts"; +import { SensitiveOutputString } from "../../../sensitive.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + name: Schema.String, + redirect_uris: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + callback_url: Schema.optional(Schema.NullOr(Schema.String)), + scopes: Schema.optional(Schema.NullOr(Schema.String)), + consent_screen_enabled: Schema.optional(Schema.NullOr(Schema.Boolean)), + pkce_required: Schema.optional(Schema.NullOr(Schema.Boolean)), + public: Schema.optional(Schema.NullOr(Schema.Boolean)), +}).pipe(T.Http({ method: "POST", path: "/oauth_applications" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["oauth_application"]), + id: Schema.String, + instance_id: Schema.String, + name: Schema.String, + client_id: Schema.String, + client_uri: Schema.NullOr(Schema.String), + client_image_url: Schema.NullOr(Schema.String), + dynamically_registered: Schema.Boolean, + consent_screen_enabled: Schema.Boolean, + pkce_required: Schema.Boolean, + public: Schema.Boolean, + scopes: Schema.String, + redirect_uris: Schema.Array(Schema.String), + callback_url: Schema.String, + authorize_url: Schema.String, + token_fetch_url: Schema.String, + user_info_url: Schema.String, + discovery_url: Schema.String, + token_introspection_url: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + client_secret: Schema.optional(SensitiveOutputString), +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create an OAuth application + * + * Creates a new OAuth application with the given name and callback URL for an instance. + * The callback URL must be a valid URL. + * All URL schemes are allowed such as `http://`, `https://`, `myapp://`, etc... + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [BadRequest, Forbidden, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/oauthApplications/delete.ts b/packages/clerk/src/operations/backend/oauthApplications/delete.ts new file mode 100644 index 0000000000..d2a0fcbf50 --- /dev/null +++ b/packages/clerk/src/operations/backend/oauthApplications/delete.ts @@ -0,0 +1,41 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + oauth_application_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "DELETE", + path: "/oauth_applications/{oauth_application_id}", + }), +); +export type DeleteInput = typeof DeleteInput.Type; + +// Output Schema +export const DeleteOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), +}); +export type DeleteOutput = typeof DeleteOutput.Type; + +// The operation +/** + * Delete an OAuth application + * + * Deletes the given OAuth application. + * This is not reversible. + * + * @param oauth_application_id - The ID of the OAuth application to delete + */ +const delete_ = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteInput, + outputSchema: DeleteOutput, + errors: [Forbidden, NotFound] as const, +})); +export { delete_ as delete }; diff --git a/packages/clerk/src/operations/backend/oauthApplications/get.ts b/packages/clerk/src/operations/backend/oauthApplications/get.ts new file mode 100644 index 0000000000..076671e133 --- /dev/null +++ b/packages/clerk/src/operations/backend/oauthApplications/get.ts @@ -0,0 +1,52 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + oauth_application_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ method: "GET", path: "/oauth_applications/{oauth_application_id}" }), +); +export type GetInput = typeof GetInput.Type; + +// Output Schema +export const GetOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["oauth_application"]), + id: Schema.String, + instance_id: Schema.String, + name: Schema.String, + client_id: Schema.String, + client_uri: Schema.NullOr(Schema.String), + client_image_url: Schema.NullOr(Schema.String), + dynamically_registered: Schema.Boolean, + consent_screen_enabled: Schema.Boolean, + pkce_required: Schema.Boolean, + public: Schema.Boolean, + scopes: Schema.String, + redirect_uris: Schema.Array(Schema.String), + callback_url: Schema.String, + authorize_url: Schema.String, + token_fetch_url: Schema.String, + user_info_url: Schema.String, + discovery_url: Schema.String, + token_introspection_url: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type GetOutput = typeof GetOutput.Type; + +// The operation +/** + * Retrieve an OAuth application by ID + * + * Fetches the OAuth application whose ID matches the provided `id` in the path. + * + * @param oauth_application_id - The ID of the OAuth application + */ +export const get = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInput, + outputSchema: GetOutput, + errors: [Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/oauthApplications/index.ts b/packages/clerk/src/operations/backend/oauthApplications/index.ts new file mode 100644 index 0000000000..4a85758b27 --- /dev/null +++ b/packages/clerk/src/operations/backend/oauthApplications/index.ts @@ -0,0 +1,7 @@ +export * from "./list.ts"; +export * from "./create.ts"; +export * from "./get.ts"; +export * from "./update.ts"; +export * from "./delete.ts"; +export * from "./uploadLogo.ts"; +export * from "./rotateSecret.ts"; diff --git a/packages/clerk/src/operations/backend/oauthApplications/list.ts b/packages/clerk/src/operations/backend/oauthApplications/list.ts new file mode 100644 index 0000000000..4441c03645 --- /dev/null +++ b/packages/clerk/src/operations/backend/oauthApplications/list.ts @@ -0,0 +1,72 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ListInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), + order_by: Schema.optional(Schema.String), + name_query: Schema.optional(Schema.String), +}).pipe(T.Http({ method: "GET", path: "/oauth_applications" })); +export type ListInput = typeof ListInput.Type; + +// Output Schema +export const ListOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["oauth_application"]), + id: Schema.String, + instance_id: Schema.String, + name: Schema.String, + client_id: Schema.String, + client_uri: Schema.NullOr(Schema.String), + client_image_url: Schema.NullOr(Schema.String), + dynamically_registered: Schema.Boolean, + consent_screen_enabled: Schema.Boolean, + pkce_required: Schema.Boolean, + public: Schema.Boolean, + scopes: Schema.String, + redirect_uris: Schema.Array(Schema.String), + callback_url: Schema.String, + authorize_url: Schema.String, + token_fetch_url: Schema.String, + user_info_url: Schema.String, + discovery_url: Schema.String, + token_introspection_url: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + total_count: Schema.Number, +}); +export type ListOutput = typeof ListOutput.Type; + +// The operation +/** + * Get a list of OAuth applications for an instance + * + * This request returns the list of OAuth applications for an instance. + * Results can be paginated using the optional `limit` and `offset` query parameters. + * The OAuth applications are ordered by descending creation date. + * Most recent OAuth applications will be returned first. + * + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + * @param order_by - Allows to return OAuth applications in a particular order. +At the moment, you can order the returned OAuth applications by their `created_at` and `name`. +In order to specify the direction, you can use the `+/-` symbols prepended in the property to order by. +For example, if you want OAuth applications to be returned in descending order according to their `created_at` property, you can use `-created_at`. +If you don't use `+` or `-`, then `+` is implied. We only support one `order_by` parameter, and if multiple `order_by` parameters are provided, we will only keep the first one. For example, +if you pass `order_by=name&order_by=created_at`, we will consider only the first `order_by` parameter, which is `name`. The `created_at` parameter will be ignored in this case. + * @param name_query - Returns OAuth applications with names that match the given query, via case-insensitive partial match. + */ +export const list = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInput, + outputSchema: ListOutput, + errors: [BadRequest, Forbidden, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/oauthApplications/rotateSecret.ts b/packages/clerk/src/operations/backend/oauthApplications/rotateSecret.ts new file mode 100644 index 0000000000..aba8128193 --- /dev/null +++ b/packages/clerk/src/operations/backend/oauthApplications/rotateSecret.ts @@ -0,0 +1,58 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, NotFound } from "../../../errors.ts"; +import { SensitiveOutputString } from "../../../sensitive.ts"; + +// Input Schema +export const RotateSecretInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + oauth_application_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "POST", + path: "/oauth_applications/{oauth_application_id}/rotate_secret", + }), +); +export type RotateSecretInput = typeof RotateSecretInput.Type; + +// Output Schema +export const RotateSecretOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["oauth_application"]), + id: Schema.String, + instance_id: Schema.String, + name: Schema.String, + client_id: Schema.String, + client_uri: Schema.NullOr(Schema.String), + client_image_url: Schema.NullOr(Schema.String), + dynamically_registered: Schema.Boolean, + consent_screen_enabled: Schema.Boolean, + pkce_required: Schema.Boolean, + public: Schema.Boolean, + scopes: Schema.String, + redirect_uris: Schema.Array(Schema.String), + callback_url: Schema.String, + authorize_url: Schema.String, + token_fetch_url: Schema.String, + user_info_url: Schema.String, + discovery_url: Schema.String, + token_introspection_url: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + client_secret: Schema.optional(SensitiveOutputString), +}); +export type RotateSecretOutput = typeof RotateSecretOutput.Type; + +// The operation +/** + * Rotate the client secret of the given OAuth application + * + * Rotates the OAuth application's client secret. + * When the client secret is rotated, make sure to update it in authorized OAuth clients. + * + * @param oauth_application_id - The ID of the OAuth application for which to rotate the client secret + */ +export const rotateSecret = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: RotateSecretInput, + outputSchema: RotateSecretOutput, + errors: [Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/oauthApplications/update.ts b/packages/clerk/src/operations/backend/oauthApplications/update.ts new file mode 100644 index 0000000000..61e3215644 --- /dev/null +++ b/packages/clerk/src/operations/backend/oauthApplications/update.ts @@ -0,0 +1,67 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const UpdateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + oauth_application_id: Schema.String.pipe(T.PathParam()), + name: Schema.optional(Schema.NullOr(Schema.String)), + redirect_uris: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + callback_url: Schema.optional(Schema.NullOr(Schema.String)), + scopes: Schema.optional(Schema.NullOr(Schema.String)), + consent_screen_enabled: Schema.optional(Schema.NullOr(Schema.Boolean)), + pkce_required: Schema.optional(Schema.NullOr(Schema.Boolean)), + public: Schema.optional(Schema.NullOr(Schema.Boolean)), +}).pipe( + T.Http({ + method: "PATCH", + path: "/oauth_applications/{oauth_application_id}", + }), +); +export type UpdateInput = typeof UpdateInput.Type; + +// Output Schema +export const UpdateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["oauth_application"]), + id: Schema.String, + instance_id: Schema.String, + name: Schema.String, + client_id: Schema.String, + client_uri: Schema.NullOr(Schema.String), + client_image_url: Schema.NullOr(Schema.String), + dynamically_registered: Schema.Boolean, + consent_screen_enabled: Schema.Boolean, + pkce_required: Schema.Boolean, + public: Schema.Boolean, + scopes: Schema.String, + redirect_uris: Schema.Array(Schema.String), + callback_url: Schema.String, + authorize_url: Schema.String, + token_fetch_url: Schema.String, + user_info_url: Schema.String, + discovery_url: Schema.String, + token_introspection_url: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type UpdateOutput = typeof UpdateOutput.Type; + +// The operation +/** + * Update an OAuth application + * + * Updates an existing OAuth application + * + * @param oauth_application_id - The ID of the OAuth application to update + */ +export const update = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateInput, + outputSchema: UpdateOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/oauthApplications/uploadLogo.ts b/packages/clerk/src/operations/backend/oauthApplications/uploadLogo.ts new file mode 100644 index 0000000000..fb29652bf0 --- /dev/null +++ b/packages/clerk/src/operations/backend/oauthApplications/uploadLogo.ts @@ -0,0 +1,66 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + PayloadTooLarge, +} from "../../../errors.ts"; + +// Input Schema +export const UploadLogoInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + oauth_application_id: Schema.String.pipe(T.PathParam()), + uploader_user_id: Schema.optional(Schema.String), + file: Schema.String, +}).pipe( + T.Http({ + method: "PUT", + path: "/oauth_applications/{oauth_application_id}/logo", + contentType: "multipart", + }), +); +export type UploadLogoInput = typeof UploadLogoInput.Type; + +// Output Schema +export const UploadLogoOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["oauth_application"]), + id: Schema.String, + instance_id: Schema.String, + name: Schema.String, + client_id: Schema.String, + client_uri: Schema.NullOr(Schema.String), + client_image_url: Schema.NullOr(Schema.String), + dynamically_registered: Schema.Boolean, + consent_screen_enabled: Schema.Boolean, + pkce_required: Schema.Boolean, + public: Schema.Boolean, + scopes: Schema.String, + redirect_uris: Schema.Array(Schema.String), + callback_url: Schema.String, + authorize_url: Schema.String, + token_fetch_url: Schema.String, + user_info_url: Schema.String, + discovery_url: Schema.String, + token_introspection_url: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type UploadLogoOutput = typeof UploadLogoOutput.Type; + +// The operation +/** + * Upload a logo for the OAuth application + * + * Set or replace an OAuth application's logo by uploading an image file. + * This endpoint uses the `multipart/form-data` request content type and accepts a file of image type. + * The file size cannot exceed 10MB. + * Only the following file content types are supported: `image/jpeg`, `image/png`, `image/gif`, `image/webp`. + * + * @param oauth_application_id - The ID of the OAuth application for which to upload a logo + */ +export const uploadLogo = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UploadLogoInput, + outputSchema: UploadLogoOutput, + errors: [BadRequest, Forbidden, NotFound, PayloadTooLarge] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationDomains/create.ts b/packages/clerk/src/operations/backend/organizationDomains/create.ts new file mode 100644 index 0000000000..0fae0c0bc2 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationDomains/create.ts @@ -0,0 +1,91 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + name: Schema.optional(Schema.String), + enrollment_mode: Schema.optional(Schema.String), + verified: Schema.optional(Schema.NullOr(Schema.Boolean)), +}).pipe( + T.Http({ method: "POST", path: "/organizations/{organization_id}/domains" }), +); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["organization_domain"]), + id: Schema.String, + organization_id: Schema.String, + name: Schema.String, + enrollment_mode: Schema.Literals([ + "manual_invitation", + "automatic_invitation", + "automatic_suggestion", + ]), + affiliation_email_address: Schema.NullOr(Schema.String), + affiliation_verification: Schema.NullOr( + Schema.Struct({ + status: Schema.String, + strategy: Schema.String, + attempts: Schema.NullOr(Schema.Number), + expire_at: Schema.NullOr(Schema.Number), + verified_at: Schema.NullOr(Schema.Number), + }), + ), + ownership_verification: Schema.NullOr( + Schema.Struct({ + status: Schema.String, + strategy: Schema.String, + attempts: Schema.NullOr(Schema.Number), + expire_at: Schema.NullOr(Schema.Number), + verified_at: Schema.NullOr(Schema.Number), + }), + ), + verification: Schema.NullOr( + Schema.Struct({ + status: Schema.String, + strategy: Schema.String, + attempts: Schema.NullOr(Schema.Number), + expire_at: Schema.NullOr(Schema.Number), + verified_at: Schema.NullOr(Schema.Number), + }), + ), + total_pending_invitations: Schema.Number, + total_pending_suggestions: Schema.Number, + public_organization_data: Schema.optional( + Schema.NullOr( + Schema.Struct({ + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + }), + ), + ), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create a new organization domain. + * + * Creates a new organization domain. By default the domain is verified, but can be optionally set to unverified. + * + * @param organization_id - The ID of the organization where the new domain will be created. + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationDomains/delete.ts b/packages/clerk/src/operations/backend/organizationDomains/delete.ts new file mode 100644 index 0000000000..b845383a48 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationDomains/delete.ts @@ -0,0 +1,42 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + domain_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "DELETE", + path: "/organizations/{organization_id}/domains/{domain_id}", + }), +); +export type DeleteInput = typeof DeleteInput.Type; + +// Output Schema +export const DeleteOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), +}); +export type DeleteOutput = typeof DeleteOutput.Type; + +// The operation +/** + * Remove a domain from an organization. + * + * Removes the given domain from the organization. + * + * @param organization_id - The ID of the organization to which the domain belongs + * @param domain_id - The ID of the domain + */ +const delete_ = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteInput, + outputSchema: DeleteOutput, + errors: [BadRequest, NotFound] as const, +})); +export { delete_ as delete }; diff --git a/packages/clerk/src/operations/backend/organizationDomains/index.ts b/packages/clerk/src/operations/backend/organizationDomains/index.ts new file mode 100644 index 0000000000..3692f96ec9 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationDomains/index.ts @@ -0,0 +1,6 @@ +export * from "./list.ts"; +export * from "./create.ts"; +export * from "./update.ts"; +export * from "./delete.ts"; +export * from "./verifyOwnership.ts"; +export * from "./listAll.ts"; diff --git a/packages/clerk/src/operations/backend/organizationDomains/list.ts b/packages/clerk/src/operations/backend/organizationDomains/list.ts new file mode 100644 index 0000000000..3a95427a6f --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationDomains/list.ts @@ -0,0 +1,97 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ListInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + verified: Schema.optional(Schema.String), + enrollment_mode: Schema.optional(Schema.String), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), +}).pipe( + T.Http({ method: "GET", path: "/organizations/{organization_id}/domains" }), +); +export type ListInput = typeof ListInput.Type; + +// Output Schema +export const ListOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["organization_domain"]), + id: Schema.String, + organization_id: Schema.String, + name: Schema.String, + enrollment_mode: Schema.Literals([ + "manual_invitation", + "automatic_invitation", + "automatic_suggestion", + ]), + affiliation_email_address: Schema.NullOr(Schema.String), + affiliation_verification: Schema.NullOr( + Schema.Struct({ + status: Schema.String, + strategy: Schema.String, + attempts: Schema.NullOr(Schema.Number), + expire_at: Schema.NullOr(Schema.Number), + verified_at: Schema.NullOr(Schema.Number), + }), + ), + ownership_verification: Schema.NullOr( + Schema.Struct({ + status: Schema.String, + strategy: Schema.String, + attempts: Schema.NullOr(Schema.Number), + expire_at: Schema.NullOr(Schema.Number), + verified_at: Schema.NullOr(Schema.Number), + }), + ), + verification: Schema.NullOr( + Schema.Struct({ + status: Schema.String, + strategy: Schema.String, + attempts: Schema.NullOr(Schema.Number), + expire_at: Schema.NullOr(Schema.Number), + verified_at: Schema.NullOr(Schema.Number), + }), + ), + total_pending_invitations: Schema.Number, + total_pending_suggestions: Schema.Number, + public_organization_data: Schema.optional( + Schema.NullOr( + Schema.Struct({ + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + }), + ), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + total_count: Schema.Number, +}); +export type ListOutput = typeof ListOutput.Type; + +// The operation +/** + * Get a list of all domains of an organization. + * + * @param organization_id - The organization ID. + * @param verified - Filter domains by their verification status. `true` or `false` + * @param enrollment_mode - Filter domains by their enrollment mode + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + */ +export const list = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInput, + outputSchema: ListOutput, + errors: [UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationDomains/listAll.ts b/packages/clerk/src/operations/backend/organizationDomains/listAll.ts new file mode 100644 index 0000000000..066abb3d02 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationDomains/listAll.ts @@ -0,0 +1,118 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ListAllInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.optional(Schema.String), + verified: Schema.optional(Schema.Literals(["true", "false"])), + enrollment_mode: Schema.optional(Schema.String), + query: Schema.optional(Schema.String), + domains: Schema.optional(Schema.String), + order_by: Schema.optional(Schema.String), + offset: Schema.optional(Schema.Number), + limit: Schema.optional(Schema.Number), +}).pipe(T.Http({ method: "GET", path: "/organization_domains" })); +export type ListAllInput = typeof ListAllInput.Type; + +// Output Schema +export const ListAllOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["organization_domain"]), + id: Schema.String, + organization_id: Schema.String, + name: Schema.String, + enrollment_mode: Schema.Literals([ + "manual_invitation", + "automatic_invitation", + "automatic_suggestion", + ]), + affiliation_email_address: Schema.NullOr(Schema.String), + affiliation_verification: Schema.NullOr( + Schema.Struct({ + status: Schema.String, + strategy: Schema.String, + attempts: Schema.NullOr(Schema.Number), + expire_at: Schema.NullOr(Schema.Number), + verified_at: Schema.NullOr(Schema.Number), + }), + ), + ownership_verification: Schema.NullOr( + Schema.Struct({ + status: Schema.String, + strategy: Schema.String, + attempts: Schema.NullOr(Schema.Number), + expire_at: Schema.NullOr(Schema.Number), + verified_at: Schema.NullOr(Schema.Number), + }), + ), + verification: Schema.NullOr( + Schema.Struct({ + status: Schema.String, + strategy: Schema.String, + attempts: Schema.NullOr(Schema.Number), + expire_at: Schema.NullOr(Schema.Number), + verified_at: Schema.NullOr(Schema.Number), + }), + ), + total_pending_invitations: Schema.Number, + total_pending_suggestions: Schema.Number, + public_organization_data: Schema.optional( + Schema.NullOr( + Schema.Struct({ + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + }), + ), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + total_count: Schema.Number, +}); +export type ListAllOutput = typeof ListAllOutput.Type; + +// The operation +/** + * List all organization domains + * + * Retrieves a list of all organization domains within the current instance. + * This endpoint can be used to list all domains across all organizations + * or filter domains by organization, verification status, enrollment mode, or search query. + * The response includes pagination information and details about each domain + * including its verification status, enrollment mode, and associated counts. + * + * @param organization_id - The ID of the organization to filter domains by + * @param verified - Filter by verification status + * @param enrollment_mode - Filter by enrollment mode + * @param query - Search domains by name or organization ID. +If the query starts with "org_", it will search by exact organization ID match. +Otherwise, it performs a case-insensitive partial match on the domain name. + +Note: An empty string or whitespace-only value is not allowed and will result in a validation error. + + * @param domains - Filter by exact domain names. Accepts multiple values (e.g. domains=example.com&domains=test.org). + * @param order_by - Allows to return organization domains in a particular order. +At the moment, you can order the returned domains by their `name` or `created_at`. +In order to specify the direction, you can use the `+/-` symbols prepended to the property to order by. +For example, if you want domains to be returned in descending order according to their `created_at` property, you can use `-created_at`. +If you don't use `+` or `-`, then `+` is implied. +Defaults to `-created_at`. + + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + */ +export const listAll = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListAllInput, + outputSchema: ListAllOutput, + errors: [Forbidden, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationDomains/update.ts b/packages/clerk/src/operations/backend/organizationDomains/update.ts new file mode 100644 index 0000000000..3e70e98262 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationDomains/update.ts @@ -0,0 +1,90 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const UpdateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + domain_id: Schema.String.pipe(T.PathParam()), + enrollment_mode: Schema.optional(Schema.NullOr(Schema.String)), + verified: Schema.optional(Schema.NullOr(Schema.Boolean)), +}).pipe( + T.Http({ + method: "PATCH", + path: "/organizations/{organization_id}/domains/{domain_id}", + }), +); +export type UpdateInput = typeof UpdateInput.Type; + +// Output Schema +export const UpdateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["organization_domain"]), + id: Schema.String, + organization_id: Schema.String, + name: Schema.String, + enrollment_mode: Schema.Literals([ + "manual_invitation", + "automatic_invitation", + "automatic_suggestion", + ]), + affiliation_email_address: Schema.NullOr(Schema.String), + affiliation_verification: Schema.NullOr( + Schema.Struct({ + status: Schema.String, + strategy: Schema.String, + attempts: Schema.NullOr(Schema.Number), + expire_at: Schema.NullOr(Schema.Number), + verified_at: Schema.NullOr(Schema.Number), + }), + ), + ownership_verification: Schema.NullOr( + Schema.Struct({ + status: Schema.String, + strategy: Schema.String, + attempts: Schema.NullOr(Schema.Number), + expire_at: Schema.NullOr(Schema.Number), + verified_at: Schema.NullOr(Schema.Number), + }), + ), + verification: Schema.NullOr( + Schema.Struct({ + status: Schema.String, + strategy: Schema.String, + attempts: Schema.NullOr(Schema.Number), + expire_at: Schema.NullOr(Schema.Number), + verified_at: Schema.NullOr(Schema.Number), + }), + ), + total_pending_invitations: Schema.Number, + total_pending_suggestions: Schema.Number, + public_organization_data: Schema.optional( + Schema.NullOr( + Schema.Struct({ + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + }), + ), + ), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type UpdateOutput = typeof UpdateOutput.Type; + +// The operation +/** + * Update an organization domain. + * + * Updates the properties of an existing organization domain. + * + * @param organization_id - The ID of the organization to which the domain belongs + * @param domain_id - The ID of the domain + */ +export const update = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateInput, + outputSchema: UpdateOutput, + errors: [BadRequest, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationDomains/verifyOwnership.ts b/packages/clerk/src/operations/backend/organizationDomains/verifyOwnership.ts new file mode 100644 index 0000000000..7d8c102915 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationDomains/verifyOwnership.ts @@ -0,0 +1,94 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const VerifyOwnershipInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + domain_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "POST", + path: "/organizations/{organization_id}/domains/{domain_id}/verify_ownership", + }), +); +export type VerifyOwnershipInput = typeof VerifyOwnershipInput.Type; + +// Output Schema +export const VerifyOwnershipOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["organization_domain"]), + id: Schema.String, + organization_id: Schema.String, + name: Schema.String, + enrollment_mode: Schema.Literals([ + "manual_invitation", + "automatic_invitation", + "automatic_suggestion", + ]), + affiliation_email_address: Schema.NullOr(Schema.String), + affiliation_verification: Schema.NullOr( + Schema.Struct({ + status: Schema.String, + strategy: Schema.String, + attempts: Schema.NullOr(Schema.Number), + expire_at: Schema.NullOr(Schema.Number), + verified_at: Schema.NullOr(Schema.Number), + }), + ), + ownership_verification: Schema.NullOr( + Schema.Struct({ + status: Schema.String, + strategy: Schema.String, + attempts: Schema.NullOr(Schema.Number), + expire_at: Schema.NullOr(Schema.Number), + verified_at: Schema.NullOr(Schema.Number), + }), + ), + verification: Schema.NullOr( + Schema.Struct({ + status: Schema.String, + strategy: Schema.String, + attempts: Schema.NullOr(Schema.Number), + expire_at: Schema.NullOr(Schema.Number), + verified_at: Schema.NullOr(Schema.Number), + }), + ), + total_pending_invitations: Schema.Number, + total_pending_suggestions: Schema.Number, + public_organization_data: Schema.optional( + Schema.NullOr( + Schema.Struct({ + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + }), + ), + ), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type VerifyOwnershipOutput = typeof VerifyOwnershipOutput.Type; + +// The operation +/** + * Mark an organization domain's ownership as verified + * + * Flips the organization domain's ownership state to verified via the + * manual override path, bypassing the self-serve TXT DNS challenge. The + * domain row records strategy=`manual_override` and an + * `organization_domain.ownership_verified` audit event is emitted with the + * same strategy. + * Idempotent: re-calling on an already-verified domain returns the current + * ownership state without re-emitting the audit event. + * + * @param organization_id - The ID of the organization to which the domain belongs + * @param domain_id - The ID of the domain + */ +export const verifyOwnership = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: VerifyOwnershipInput, + outputSchema: VerifyOwnershipOutput, + errors: [Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationInvitations/bulkCreate.ts b/packages/clerk/src/operations/backend/organizationInvitations/bulkCreate.ts new file mode 100644 index 0000000000..c5697c18b1 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationInvitations/bulkCreate.ts @@ -0,0 +1,83 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const BulkCreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "POST", + path: "/organizations/{organization_id}/invitations/bulk", + }), +); +export type BulkCreateInput = typeof BulkCreateInput.Type; + +// Output Schema +export const BulkCreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["organization_invitation"]), + id: Schema.String, + email_address: Schema.String, + role: Schema.String, + role_name: Schema.String, + organization_id: Schema.optional(Schema.String), + inviter_id: Schema.NullOr(Schema.String), + public_inviter_data: Schema.NullOr( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.String, + }), + ), + status: Schema.optional(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + url: Schema.NullOr(Schema.String), + expires_at: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + total_count: Schema.Number, +}); +export type BulkCreateOutput = typeof BulkCreateOutput.Type; + +// The operation +/** + * Bulk create and send organization invitations + * + * Creates new organization invitations in bulk and sends out emails to the provided email addresses with a link to accept the invitation and join the organization. + * This endpoint is limited to a maximum of 10 invitations per API call. If you need to send more invitations, please make multiple requests. + * You can specify a different `role` for each invited organization member. + * New organization invitations get a "pending" status until they are revoked by an organization administrator or accepted by the invitee. + * The request body supports passing an optional `redirect_url` parameter for each invitation. + * When the invited user clicks the link to accept the invitation, they will be redirected to the provided URL. + * Use this parameter to implement a custom invitation acceptance flow. + * You can specify the ID of the user that will send the invitation with the `inviter_user_id` parameter. Each invitation + * can have a different inviter user. + * Inviter users must be members with administrator privileges in the organization. + * Only "admin" members can create organization invitations. + * You can optionally provide public and private metadata for each organization invitation. The public metadata are visible + * by both the Frontend and the Backend, whereas the private metadata are only visible by the Backend. + * When the organization invitation is accepted, the metadata will be transferred to the newly created organization membership. + * + * @param organization_id - The organization ID. + */ +export const bulkCreate = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: BulkCreateInput, + outputSchema: BulkCreateOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationInvitations/create.ts b/packages/clerk/src/operations/backend/organizationInvitations/create.ts new file mode 100644 index 0000000000..a250c8dba8 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationInvitations/create.ts @@ -0,0 +1,95 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + email_address: Schema.String, + inviter_user_id: Schema.optional(Schema.NullOr(Schema.String)), + role: Schema.String, + public_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + redirect_url: Schema.optional(Schema.NullOr(Schema.String)), + expires_in_days: Schema.optional(Schema.NullOr(Schema.Number)), + notify: Schema.optional(Schema.NullOr(Schema.Boolean)), +}).pipe( + T.Http({ + method: "POST", + path: "/organizations/{organization_id}/invitations", + }), +); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["organization_invitation"]), + id: Schema.String, + email_address: Schema.String, + role: Schema.String, + role_name: Schema.String, + organization_id: Schema.optional(Schema.String), + inviter_id: Schema.NullOr(Schema.String), + public_inviter_data: Schema.NullOr( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.String, + }), + ), + status: Schema.optional(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + url: Schema.NullOr(Schema.String), + expires_at: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create and send an organization invitation + * + * Creates a new organization invitation and sends an email to the provided `email_address` with a link to accept the invitation and join the organization. + * You can specify the `role` for the invited organization member. + * New organization invitations get a "pending" status until they are revoked by an organization administrator or accepted by the invitee. + * The request body supports passing an optional `redirect_url` parameter. + * When the invited user clicks the link to accept the invitation, they will be redirected to the URL provided. + * Use this parameter to implement a custom invitation acceptance flow. + * You can specify the ID of the user that will send the invitation with the `inviter_user_id` parameter. + * That user must be a member with administrator privileges in the organization. + * Only "admin" members can create organization invitations. + * You can optionally provide public and private metadata for the organization invitation. + * The public metadata are visible by both the Frontend and the Backend whereas the private ones only by the Backend. + * When the organization invitation is accepted, the metadata will be transferred to the newly created organization membership. + * + * @param organization_id - The ID of the organization for which to send the invitation + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [ + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + UnprocessableEntity, + ] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationInvitations/get.ts b/packages/clerk/src/operations/backend/organizationInvitations/get.ts new file mode 100644 index 0000000000..dfafb5c327 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationInvitations/get.ts @@ -0,0 +1,62 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + invitation_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "GET", + path: "/organizations/{organization_id}/invitations/{invitation_id}", + }), +); +export type GetInput = typeof GetInput.Type; + +// Output Schema +export const GetOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["organization_invitation"]), + id: Schema.String, + email_address: Schema.String, + role: Schema.String, + role_name: Schema.String, + organization_id: Schema.optional(Schema.String), + inviter_id: Schema.NullOr(Schema.String), + public_inviter_data: Schema.NullOr( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.String, + }), + ), + status: Schema.optional(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + url: Schema.NullOr(Schema.String), + expires_at: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type GetOutput = typeof GetOutput.Type; + +// The operation +/** + * Retrieve an organization invitation by ID + * + * Use this request to get an existing organization invitation by ID. + * + * @param organization_id - The organization ID. + * @param invitation_id - The organization invitation ID. + */ +export const get = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInput, + outputSchema: GetOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationInvitations/getAll.ts b/packages/clerk/src/operations/backend/organizationInvitations/getAll.ts new file mode 100644 index 0000000000..d75f1d97ce --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationInvitations/getAll.ts @@ -0,0 +1,96 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const GetAllInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + order_by: Schema.optional(Schema.String), + status: Schema.optional( + Schema.Literals(["pending", "accepted", "revoked", "expired"]), + ), + query: Schema.optional(Schema.String), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), +}).pipe(T.Http({ method: "GET", path: "/organization_invitations" })); +export type GetAllInput = typeof GetAllInput.Type; + +// Output Schema +export const GetAllOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.optional(Schema.Literals(["organization_invitation"])), + id: Schema.optional(Schema.String), + email_address: Schema.optional(Schema.String), + role: Schema.optional(Schema.String), + role_name: Schema.optional(Schema.String), + organization_id: Schema.optional(Schema.String), + inviter_id: Schema.optional(Schema.NullOr(Schema.String)), + public_inviter_data: Schema.optional( + Schema.NullOr( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.String, + }), + ), + ), + status: Schema.optional(Schema.String), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + url: Schema.optional(Schema.NullOr(Schema.String)), + expires_at: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + public_organization_data: Schema.optional( + Schema.Struct({ + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + }), + ), + }), + ), + total_count: Schema.Number, +}); +export type GetAllOutput = typeof GetAllOutput.Type; + +// The operation +/** + * Get a list of organization invitations for the current instance + * + * This request returns the list of organization invitations for the instance. + * Results can be paginated using the optional `limit` and `offset` query parameters. + * You can filter them by providing the 'status' query parameter, that accepts multiple values. + * You can change the order by providing the 'order' query parameter, that accepts multiple values. + * You can filter by the invited user email address providing the `query` query parameter. + * The organization invitations are ordered by descending creation date by default. + * + * @param order_by - Allows to return organization invitations in a particular order. +At the moment, you can order the returned organization invitations either by their `created_at` or `email_address`. +In order to specify the direction, you can use the `+/-` symbols prepended in the property to order by. +For example, if you want organization invitations to be returned in descending order according to their `created_at` property, you can use `-created_at`. +If you don't use `+` or `-`, then `+` is implied. +Defaults to `-created_at`. + * @param status - Filter organization invitations based on their status + * @param query - Filter organization invitations based on their `email_address` + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + */ +export const getAll = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetAllInput, + outputSchema: GetAllOutput, + errors: [BadRequest, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationInvitations/index.ts b/packages/clerk/src/operations/backend/organizationInvitations/index.ts new file mode 100644 index 0000000000..87f28c7547 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationInvitations/index.ts @@ -0,0 +1,6 @@ +export * from "./getAll.ts"; +export * from "./list.ts"; +export * from "./create.ts"; +export * from "./bulkCreate.ts"; +export * from "./get.ts"; +export * from "./revoke.ts"; diff --git a/packages/clerk/src/operations/backend/organizationInvitations/list.ts b/packages/clerk/src/operations/backend/organizationInvitations/list.ts new file mode 100644 index 0000000000..dab12a6fe5 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationInvitations/list.ts @@ -0,0 +1,90 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ListInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + status: Schema.optional( + Schema.Literals(["pending", "accepted", "revoked", "expired"]), + ), + email_address: Schema.optional(Schema.String), + order_by: Schema.optional(Schema.String), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), +}).pipe( + T.Http({ + method: "GET", + path: "/organizations/{organization_id}/invitations", + }), +); +export type ListInput = typeof ListInput.Type; + +// Output Schema +export const ListOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["organization_invitation"]), + id: Schema.String, + email_address: Schema.String, + role: Schema.String, + role_name: Schema.String, + organization_id: Schema.optional(Schema.String), + inviter_id: Schema.NullOr(Schema.String), + public_inviter_data: Schema.NullOr( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.String, + }), + ), + status: Schema.optional(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + url: Schema.NullOr(Schema.String), + expires_at: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + total_count: Schema.Number, +}); +export type ListOutput = typeof ListOutput.Type; + +// The operation +/** + * Get a list of organization invitations + * + * This request returns the list of organization invitations. + * Results can be paginated using the optional `limit` and `offset` query parameters. + * You can filter them by providing the 'status' query parameter, that accepts multiple values. + * The organization invitations are ordered by descending creation date. + * Most recent invitations will be returned first. + * Any invitations created as a result of an Organization Domain are not included in the results. + * + * @param organization_id - The organization ID. + * @param status - Filter organization invitations based on their status + * @param email_address - Returns organization invitations inviting the specified email address. + * @param order_by - Allows to return organization invitations in a particular order. +You can order the returned organization invitations either by their `created_at` or `email_address`. +In order to specify the direction, you can use the `+/-` symbols prepended in the property to order by. +For example, if you want organization invitations to be returned in descending order according to their `created_at` property, you can use `-created_at`. +If you don't use `+` or `-`, then `+` is implied. +Defaults to `-created_at`. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + */ +export const list = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInput, + outputSchema: ListOutput, + errors: [BadRequest, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationInvitations/revoke.ts b/packages/clerk/src/operations/backend/organizationInvitations/revoke.ts new file mode 100644 index 0000000000..ac88660abb --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationInvitations/revoke.ts @@ -0,0 +1,67 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const RevokeInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + invitation_id: Schema.String.pipe(T.PathParam()), + requesting_user_id: Schema.optional(Schema.NullOr(Schema.String)), +}).pipe( + T.Http({ + method: "POST", + path: "/organizations/{organization_id}/invitations/{invitation_id}/revoke", + }), +); +export type RevokeInput = typeof RevokeInput.Type; + +// Output Schema +export const RevokeOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["organization_invitation"]), + id: Schema.String, + email_address: Schema.String, + role: Schema.String, + role_name: Schema.String, + organization_id: Schema.optional(Schema.String), + inviter_id: Schema.NullOr(Schema.String), + public_inviter_data: Schema.NullOr( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.String, + }), + ), + status: Schema.optional(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + url: Schema.NullOr(Schema.String), + expires_at: Schema.NullOr(Schema.Number), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type RevokeOutput = typeof RevokeOutput.Type; + +// The operation +/** + * Revoke a pending organization invitation + * + * Use this request to revoke a previously issued organization invitation. + * Revoking an organization invitation makes it invalid; the invited user will no longer be able to join the organization with the revoked invitation. + * Only organization invitations with "pending" status can be revoked. + * The request accepts the `requesting_user_id` parameter to specify the user which revokes the invitation. + * Only users with "admin" role can revoke invitations. + * + * @param organization_id - The organization ID. + * @param invitation_id - The organization invitation ID. + */ +export const revoke = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: RevokeInput, + outputSchema: RevokeOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationMemberships/create.ts b/packages/clerk/src/operations/backend/organizationMemberships/create.ts new file mode 100644 index 0000000000..5e4df4ebed --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationMemberships/create.ts @@ -0,0 +1,97 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + user_id: Schema.String, + role: Schema.String, + public_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), +}).pipe( + T.Http({ + method: "POST", + path: "/organizations/{organization_id}/memberships", + }), +); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional(Schema.Boolean), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create a new organization membership + * + * Adds a user as a member to the given organization. + * Only users in the same instance as the organization can be added as members. + * This organization will be the user's [active organization] (https://clerk.com/docs/organizations/overview#active-organization) + * the next time they create a session, presuming they don't explicitly set a + * different organization as active before then. + * + * @param organization_id - The ID of the organization where the new membership will be created + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationMemberships/delete.ts b/packages/clerk/src/operations/backend/organizationMemberships/delete.ts new file mode 100644 index 0000000000..0030918cca --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationMemberships/delete.ts @@ -0,0 +1,83 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const DeleteInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + user_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "DELETE", + path: "/organizations/{organization_id}/memberships/{user_id}", + }), +); +export type DeleteInput = typeof DeleteInput.Type; + +// Output Schema +export const DeleteOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional(Schema.Boolean), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type DeleteOutput = typeof DeleteOutput.Type; + +// The operation +/** + * Remove a member from an organization + * + * Removes the given membership from the organization + * + * @param organization_id - The ID of the organization to which this membership belongs + * @param user_id - The ID of the user to which this membership belongs + */ +const delete_ = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteInput, + outputSchema: DeleteOutput, + errors: [NotFound, UnprocessableEntity] as const, +})); +export { delete_ as delete }; diff --git a/packages/clerk/src/operations/backend/organizationMemberships/index.ts b/packages/clerk/src/operations/backend/organizationMemberships/index.ts new file mode 100644 index 0000000000..ddaa9d8161 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationMemberships/index.ts @@ -0,0 +1,5 @@ +export * from "./list.ts"; +export * from "./create.ts"; +export * from "./update.ts"; +export * from "./delete.ts"; +export * from "./updateMetadata.ts"; diff --git a/packages/clerk/src/operations/backend/organizationMemberships/list.ts b/packages/clerk/src/operations/backend/organizationMemberships/list.ts new file mode 100644 index 0000000000..dd65007e69 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationMemberships/list.ts @@ -0,0 +1,142 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ListInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + order_by: Schema.optional(Schema.String), + user_id: Schema.optional(Schema.String), + email_address: Schema.optional(Schema.String), + phone_number: Schema.optional(Schema.String), + username: Schema.optional(Schema.String), + web3_wallet: Schema.optional(Schema.String), + role: Schema.optional(Schema.String), + query: Schema.optional(Schema.String), + email_address_query: Schema.optional(Schema.String), + phone_number_query: Schema.optional(Schema.String), + username_query: Schema.optional(Schema.String), + name_query: Schema.optional(Schema.String), + last_active_at_before: Schema.optional(Schema.Number), + last_active_at_after: Schema.optional(Schema.Number), + created_at_before: Schema.optional(Schema.Number), + created_at_after: Schema.optional(Schema.Number), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), +}).pipe( + T.Http({ + method: "GET", + path: "/organizations/{organization_id}/memberships", + }), +); +export type ListInput = typeof ListInput.Type; + +// Output Schema +export const ListOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + total_count: Schema.Number, +}); +export type ListOutput = typeof ListOutput.Type; + +// The operation +/** + * Get a list of all members of an organization + * + * Retrieves all user memberships for the given organization + * + * @param organization_id - The organization ID. + * @param order_by - Sorts organizations memberships by phone_number, email_address, created_at, first_name, last_name or username. +By prepending one of those values with + or -, we can choose to sort in ascending (ASC) or descending (DESC) order." + * @param user_id - Returns users with the user IDs specified. For each user ID, the `+` and `-` can be +prepended to the ID, which denote whether the respective user ID should be included or +excluded from the result set. Accepts up to 100 user IDs. Any user IDs not found are ignored. + * @param email_address - Returns users with the specified email addresses. Accepts up to 100 email addresses. Any email addresses not found are ignored. + * @param phone_number - Returns users with the specified phone numbers. Accepts up to 100 phone numbers. Any phone numbers not found are ignored. + * @param username - Returns users with the specified usernames. +Accepts up to 100 usernames. +Any usernames not found are ignored. + * @param web3_wallet - Returns users with the specified web3 wallet addresses. +Accepts up to 100 web3 wallet addresses. +Any web3 wallet addresses not found are ignored. + * @param role - Returns users with the specified roles. Accepts up to 100 roles. Any roles not found are ignored. + * @param query - Returns users that match the given query. +For possible matches, we check the email addresses, phone numbers, usernames, web3 wallets, user IDs, first and last names. +The query value doesn't need to match the exact value you are looking for, it is capable of partial matches as well. + * @param email_address_query - Returns users with emails that match the given query, via case-insensitive partial match. +For example, `email_address_query=ello` will match a user with the email `HELLO@example.com`. + * @param phone_number_query - Returns users with phone numbers that match the given query, via case-insensitive partial match. +For example, `phone_number_query=555` will match a user with the phone number `+1555xxxxxxx`. + * @param username_query - Returns users with usernames that match the given query, via case-insensitive partial match. +For example, `username_query=CoolUser` will match a user with the username `SomeCoolUser`. + * @param name_query - Returns users with names that match the given query, via case-insensitive partial match. + * @param last_active_at_before - Returns users whose last session activity was before the given date (with millisecond precision). +Example: use 1700690400000 to retrieve users whose last session activity was before 2023-11-23. + * @param last_active_at_after - Returns users whose last session activity was after the given date (with millisecond precision). +Example: use 1700690400000 to retrieve users whose last session activity was after 2023-11-23. + * @param created_at_before - Returns users who have been created before the given date (with millisecond precision). +Example: use 1730160000000 to retrieve users who have been created before 2024-10-29. + * @param created_at_after - Returns users who have been created after the given date (with millisecond precision). +Example: use 1730160000000 to retrieve users who have been created after 2024-10-29. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + */ +export const list = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInput, + outputSchema: ListOutput, + errors: [UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationMemberships/update.ts b/packages/clerk/src/operations/backend/organizationMemberships/update.ts new file mode 100644 index 0000000000..412d6dcb57 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationMemberships/update.ts @@ -0,0 +1,83 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const UpdateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + user_id: Schema.String.pipe(T.PathParam()), + role: Schema.String, +}).pipe( + T.Http({ + method: "PATCH", + path: "/organizations/{organization_id}/memberships/{user_id}", + }), +); +export type UpdateInput = typeof UpdateInput.Type; + +// Output Schema +export const UpdateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional(Schema.Boolean), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type UpdateOutput = typeof UpdateOutput.Type; + +// The operation +/** + * Update an organization membership + * + * Updates the properties of an existing organization membership + * + * @param organization_id - The ID of the organization to which this membership belongs + * @param user_id - The ID of the user to which this membership belongs + */ +export const update = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateInput, + outputSchema: UpdateOutput, + errors: [NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationMemberships/updateMetadata.ts b/packages/clerk/src/operations/backend/organizationMemberships/updateMetadata.ts new file mode 100644 index 0000000000..d687a16a1e --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationMemberships/updateMetadata.ts @@ -0,0 +1,90 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const UpdateMetadataInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + user_id: Schema.String.pipe(T.PathParam()), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), +}).pipe( + T.Http({ + method: "PATCH", + path: "/organizations/{organization_id}/memberships/{user_id}/metadata", + }), +); +export type UpdateMetadataInput = typeof UpdateMetadataInput.Type; + +// Output Schema +export const UpdateMetadataOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional(Schema.Boolean), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type UpdateMetadataOutput = typeof UpdateMetadataOutput.Type; + +// The operation +/** + * Merge and update organization membership metadata + * + * Update an organization membership's metadata attributes by merging existing values with the provided parameters. + * Metadata values will be updated via a deep merge. Deep means that any nested JSON objects will be merged as well. + * You can remove metadata keys at any level by setting their value to `null`. + * + * @param organization_id - The ID of the organization to which this membership belongs + * @param user_id - The ID of the user to which this membership belongs + */ +export const updateMetadata = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateMetadataInput, + outputSchema: UpdateMetadataOutput, + errors: [BadRequest, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationPermissions/create.ts b/packages/clerk/src/operations/backend/organizationPermissions/create.ts new file mode 100644 index 0000000000..8c24da2c97 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationPermissions/create.ts @@ -0,0 +1,42 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + name: Schema.String, + key: Schema.String, + description: Schema.optional(Schema.String), +}).pipe(T.Http({ method: "POST", path: "/organization_permissions" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["permission"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.String, + type: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create a new organization permission + * + * Creates a new organization permission for the given instance. + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [BadRequest, PaymentRequired, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationPermissions/delete.ts b/packages/clerk/src/operations/backend/organizationPermissions/delete.ts new file mode 100644 index 0000000000..7a4cf9d601 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationPermissions/delete.ts @@ -0,0 +1,41 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + permission_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "DELETE", + path: "/organization_permissions/{permission_id}", + }), +); +export type DeleteInput = typeof DeleteInput.Type; + +// Output Schema +export const DeleteOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), +}); +export type DeleteOutput = typeof DeleteOutput.Type; + +// The operation +/** + * Delete an organization permission + * + * Deletes an organization permission. + * System permissions cannot be deleted. + * + * @param permission_id - The ID of the permission to delete + */ +const delete_ = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteInput, + outputSchema: DeleteOutput, + errors: [Forbidden, NotFound] as const, +})); +export { delete_ as delete }; diff --git a/packages/clerk/src/operations/backend/organizationPermissions/get.ts b/packages/clerk/src/operations/backend/organizationPermissions/get.ts new file mode 100644 index 0000000000..0edcf9b941 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationPermissions/get.ts @@ -0,0 +1,39 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + permission_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ method: "GET", path: "/organization_permissions/{permission_id}" }), +); +export type GetInput = typeof GetInput.Type; + +// Output Schema +export const GetOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["permission"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.String, + type: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type GetOutput = typeof GetOutput.Type; + +// The operation +/** + * Get an organization permission + * + * Retrieves the details of an organization permission. + * + * @param permission_id - The ID of the permission to retrieve + */ +export const get = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInput, + outputSchema: GetOutput, + errors: [NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationPermissions/index.ts b/packages/clerk/src/operations/backend/organizationPermissions/index.ts new file mode 100644 index 0000000000..9bc43612a5 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationPermissions/index.ts @@ -0,0 +1,5 @@ +export * from "./list.ts"; +export * from "./create.ts"; +export * from "./get.ts"; +export * from "./update.ts"; +export * from "./delete.ts"; diff --git a/packages/clerk/src/operations/backend/organizationPermissions/list.ts b/packages/clerk/src/operations/backend/organizationPermissions/list.ts new file mode 100644 index 0000000000..ad69fa287a --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationPermissions/list.ts @@ -0,0 +1,55 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ListInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + query: Schema.optional(Schema.String), + order_by: Schema.optional(Schema.String), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), +}).pipe(T.Http({ method: "GET", path: "/organization_permissions" })); +export type ListInput = typeof ListInput.Type; + +// Output Schema +export const ListOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["permission"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.String, + type: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + total_count: Schema.Number, +}); +export type ListOutput = typeof ListOutput.Type; + +// The operation +/** + * Get a list of all organization permissions + * + * Retrieves all organization permissions for the given instance. + * + * @param query - Returns organization permissions with ID, name, or key that match the given query. +Uses exact match for permission ID and partial match for name and key. + * @param order_by - Allows to return organization permissions in a particular order. +At the moment, you can order the returned permissions by their `created_at`, `name`, or `key`. +In order to specify the direction, you can use the `+/-` symbols prepended in the property to order by. +For example, if you want permissions to be returned in descending order according to their `created_at` property, you can use `-created_at`. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + */ +export const list = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInput, + outputSchema: ListOutput, + errors: [UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationPermissions/update.ts b/packages/clerk/src/operations/backend/organizationPermissions/update.ts new file mode 100644 index 0000000000..a1adc0ab72 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationPermissions/update.ts @@ -0,0 +1,51 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const UpdateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + permission_id: Schema.String.pipe(T.PathParam()), + name: Schema.optional(Schema.String), + key: Schema.optional(Schema.String), + description: Schema.optional(Schema.String), +}).pipe( + T.Http({ + method: "PATCH", + path: "/organization_permissions/{permission_id}", + }), +); +export type UpdateInput = typeof UpdateInput.Type; + +// Output Schema +export const UpdateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["permission"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.String, + type: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type UpdateOutput = typeof UpdateOutput.Type; + +// The operation +/** + * Update an organization permission + * + * Updates the properties of an existing organization permission. + * System permissions cannot be updated. + * + * @param permission_id - The ID of the permission to update + */ +export const update = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateInput, + outputSchema: UpdateOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationRoles/assignPermission.ts b/packages/clerk/src/operations/backend/organizationRoles/assignPermission.ts new file mode 100644 index 0000000000..ee62ce596b --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationRoles/assignPermission.ts @@ -0,0 +1,58 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, NotFound, Conflict } from "../../../errors.ts"; + +// Input Schema +export const AssignPermissionInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_role_id: Schema.String.pipe(T.PathParam()), + permission_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "POST", + path: "/organization_roles/{organization_role_id}/permissions/{permission_id}", + }), +); +export type AssignPermissionInput = typeof AssignPermissionInput.Type; + +// Output Schema +export const AssignPermissionOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + { + object: Schema.Literals(["role"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + is_creator_eligible: Schema.Boolean, + permissions: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["permission"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.String, + type: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }, +); +export type AssignPermissionOutput = typeof AssignPermissionOutput.Type; + +// The operation +/** + * Assign a permission to an organization role + * + * Assigns a permission to an organization role + * + * @param organization_role_id - The ID of the organization role + * @param permission_id - The ID of the permission to assign + */ +export const assignPermission = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: AssignPermissionInput, + outputSchema: AssignPermissionOutput, + errors: [Forbidden, NotFound, Conflict] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationRoles/create.ts b/packages/clerk/src/operations/backend/organizationRoles/create.ts new file mode 100644 index 0000000000..0b3827eabc --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationRoles/create.ts @@ -0,0 +1,66 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + name: Schema.String, + key: Schema.String, + description: Schema.optional(Schema.NullOr(Schema.String)), + permissions: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + include_in_initial_role_set: Schema.optional(Schema.NullOr(Schema.Boolean)), +}).pipe(T.Http({ method: "POST", path: "/organization_roles" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["role"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + is_creator_eligible: Schema.Boolean, + permissions: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["permission"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.String, + type: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create an organization role + * + * Creates a new organization role with the given name and permissions for an instance. + * The key must be unique for the instance and start with the 'org:' prefix, followed by lowercase alphanumeric characters and underscores only. + * You can optionally provide a description for the role and specify whether it should be included in the initial role set. + * Organization roles support permissions that can be assigned to control access within the organization. + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [ + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + UnprocessableEntity, + ] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationRoles/delete.ts b/packages/clerk/src/operations/backend/organizationRoles/delete.ts new file mode 100644 index 0000000000..e0245d18d4 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationRoles/delete.ts @@ -0,0 +1,41 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const DeleteInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_role_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "DELETE", + path: "/organization_roles/{organization_role_id}", + }), +); +export type DeleteInput = typeof DeleteInput.Type; + +// Output Schema +export const DeleteOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), +}); +export type DeleteOutput = typeof DeleteOutput.Type; + +// The operation +/** + * Delete an organization role + * + * Deletes the organization role. + * The role cannot be deleted if it is currently used as the default creator role, domain default role, assigned to any members, or exists in any invitations. + * + * @param organization_role_id - The ID of the organization role to delete + */ +const delete_ = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteInput, + outputSchema: DeleteOutput, + errors: [Forbidden, NotFound, UnprocessableEntity] as const, +})); +export { delete_ as delete }; diff --git a/packages/clerk/src/operations/backend/organizationRoles/get.ts b/packages/clerk/src/operations/backend/organizationRoles/get.ts new file mode 100644 index 0000000000..a116cb9ddf --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationRoles/get.ts @@ -0,0 +1,51 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_role_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ method: "GET", path: "/organization_roles/{organization_role_id}" }), +); +export type GetInput = typeof GetInput.Type; + +// Output Schema +export const GetOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["role"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + is_creator_eligible: Schema.Boolean, + permissions: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["permission"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.String, + type: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type GetOutput = typeof GetOutput.Type; + +// The operation +/** + * Retrieve an organization role + * + * Use this request to retrieve an existing organization role by its ID. + * + * @param organization_role_id - The ID of the organization role + */ +export const get = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInput, + outputSchema: GetOutput, + errors: [Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationRoles/index.ts b/packages/clerk/src/operations/backend/organizationRoles/index.ts new file mode 100644 index 0000000000..7472771801 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationRoles/index.ts @@ -0,0 +1,7 @@ +export * from "./list.ts"; +export * from "./create.ts"; +export * from "./get.ts"; +export * from "./update.ts"; +export * from "./delete.ts"; +export * from "./assignPermission.ts"; +export * from "./removePermission.ts"; diff --git a/packages/clerk/src/operations/backend/organizationRoles/list.ts b/packages/clerk/src/operations/backend/organizationRoles/list.ts new file mode 100644 index 0000000000..f7d30938b3 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationRoles/list.ts @@ -0,0 +1,72 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ListInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + query: Schema.optional(Schema.String), + order_by: Schema.optional(Schema.String), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), +}).pipe(T.Http({ method: "GET", path: "/organization_roles" })); +export type ListInput = typeof ListInput.Type; + +// Output Schema +export const ListOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["role"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + is_creator_eligible: Schema.Boolean, + permissions: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["permission"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.String, + type: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + total_count: Schema.Number, +}); +export type ListOutput = typeof ListOutput.Type; + +// The operation +/** + * Get a list of organization roles + * + * This request returns the list of organization roles for the instance. + * Results can be paginated using the optional `limit` and `offset` query parameters. + * The organization roles are ordered by descending creation date. + * Most recent roles will be returned first. + * + * @param query - Returns organization roles with ID, name, or key that match the given query. +Uses exact match for organization role ID and partial match for name and key. + * @param order_by - Allows to return organization roles in a particular order. +At the moment, you can order the returned organization roles by their `created_at`, `name`, or `key`. +In order to specify the direction, you can use the `+/-` symbols prepended in the property to order by. +For example, if you want organization roles to be returned in descending order according to their `created_at` property, you can use `-created_at`. +If you don't use `+` or `-`, then `+` is implied. +Defaults to `-created_at`. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + */ +export const list = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInput, + outputSchema: ListOutput, + errors: [BadRequest, Forbidden, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationRoles/removePermission.ts b/packages/clerk/src/operations/backend/organizationRoles/removePermission.ts new file mode 100644 index 0000000000..6e57601514 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationRoles/removePermission.ts @@ -0,0 +1,58 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const RemovePermissionInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_role_id: Schema.String.pipe(T.PathParam()), + permission_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "DELETE", + path: "/organization_roles/{organization_role_id}/permissions/{permission_id}", + }), +); +export type RemovePermissionInput = typeof RemovePermissionInput.Type; + +// Output Schema +export const RemovePermissionOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + { + object: Schema.Literals(["role"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + is_creator_eligible: Schema.Boolean, + permissions: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["permission"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.String, + type: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }, +); +export type RemovePermissionOutput = typeof RemovePermissionOutput.Type; + +// The operation +/** + * Remove a permission from an organization role + * + * Removes a permission from an organization role + * + * @param organization_role_id - The ID of the organization role + * @param permission_id - The ID of the permission to remove + */ +export const removePermission = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: RemovePermissionInput, + outputSchema: RemovePermissionOutput, + errors: [Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizationRoles/update.ts b/packages/clerk/src/operations/backend/organizationRoles/update.ts new file mode 100644 index 0000000000..15e932b043 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizationRoles/update.ts @@ -0,0 +1,66 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const UpdateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_role_id: Schema.String.pipe(T.PathParam()), + name: Schema.optional(Schema.NullOr(Schema.String)), + key: Schema.optional(Schema.NullOr(Schema.String)), + description: Schema.optional(Schema.NullOr(Schema.String)), + permissions: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), +}).pipe( + T.Http({ + method: "PATCH", + path: "/organization_roles/{organization_role_id}", + }), +); +export type UpdateInput = typeof UpdateInput.Type; + +// Output Schema +export const UpdateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["role"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + is_creator_eligible: Schema.Boolean, + permissions: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["permission"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.String, + type: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type UpdateOutput = typeof UpdateOutput.Type; + +// The operation +/** + * Update an organization role + * + * Updates an existing organization role. + * You can update the name, key, description, and permissions of the role. + * All parameters are optional - you can update only the fields you want to change. + * If the role is used as a creator role or domain default role, updating the key will cascade the update to the organization settings. + * + * @param organization_role_id - The ID of the organization role to update + */ +export const update = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateInput, + outputSchema: UpdateOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizations/adjustBillingCreditBalance.ts b/packages/clerk/src/operations/backend/organizations/adjustBillingCreditBalance.ts new file mode 100644 index 0000000000..d6f1826189 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizations/adjustBillingCreditBalance.ts @@ -0,0 +1,68 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + Conflict, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const AdjustBillingCreditBalanceInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + amount: Schema.Number, + action: Schema.Literals(["increase", "decrease"]), + currency: Schema.optional(Schema.String), + idempotency_key: Schema.String, + note: Schema.optional(Schema.String), + }).pipe( + T.Http({ + method: "POST", + path: "/organizations/{organization_id}/billing/credits", + }), + ); +export type AdjustBillingCreditBalanceInput = + typeof AdjustBillingCreditBalanceInput.Type; + +// Output Schema +export const AdjustBillingCreditBalanceOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.String, + payer_id: Schema.String, + amount: Schema.Number, + currency: Schema.String, + source_type: Schema.String, + source_id: Schema.String, + note: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.String, + }); +export type AdjustBillingCreditBalanceOutput = + typeof AdjustBillingCreditBalanceOutput.Type; + +// The operation +/** + * Adjust an organization's credit balance + * + * Increases or decreases the credit balance for the specified organization. + * Each adjustment is recorded as a ledger entry. The idempotency_key parameter + * ensures that duplicate requests are safely handled. + * + * @param organization_id - The ID of the organization whose credit balance to adjust + */ +export const adjustBillingCreditBalance = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: AdjustBillingCreditBalanceInput, + outputSchema: AdjustBillingCreditBalanceOutput, + errors: [ + BadRequest, + Forbidden, + NotFound, + Conflict, + UnprocessableEntity, + ] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/organizations/create.ts b/packages/clerk/src/operations/backend/organizations/create.ts new file mode 100644 index 0000000000..e11b9158e0 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizations/create.ts @@ -0,0 +1,77 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + Forbidden, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + name: Schema.String, + created_by: Schema.optional(Schema.NullOr(Schema.String)), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + public_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + slug: Schema.optional(Schema.NullOr(Schema.String)), + max_allowed_memberships: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.optional(Schema.NullOr(Schema.String)), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), +}).pipe(T.Http({ method: "POST", path: "/organizations" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional(Schema.Boolean), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create an organization + * + * Creates a new organization with the given name for an instance. + * You can specify an optional slug for the new organization. + * If provided, the organization slug can contain only lowercase alphanumeric characters (letters and digits) and the dash "-". + * Organization slugs must be unique for the instance. + * You can provide additional metadata for the organization and set any custom attribute you want. + * Organizations support private and public metadata. + * Private metadata can only be accessed from the Backend API. + * Public metadata can be accessed from the Backend API, and are read-only from the Frontend API. + * The `created_by` user will see this as their [active organization](https://clerk.com/docs/organizations/overview#active-organization) + * the next time they create a session, presuming they don't explicitly set a different organization as active before then. + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [ + BadRequest, + PaymentRequired, + Forbidden, + UnprocessableEntity, + ] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizations/delete.ts b/packages/clerk/src/operations/backend/organizations/delete.ts new file mode 100644 index 0000000000..313660aa3b --- /dev/null +++ b/packages/clerk/src/operations/backend/organizations/delete.ts @@ -0,0 +1,39 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "DELETE", path: "/organizations/{organization_id}" })); +export type DeleteInput = typeof DeleteInput.Type; + +// Output Schema +export const DeleteOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), +}); +export type DeleteOutput = typeof DeleteOutput.Type; + +// The operation +/** + * Delete an organization + * + * Deletes the given organization. + * Please note that deleting an organization will also delete all memberships and invitations. + * This is not reversible. + * After the organization is deleted, any user's active sessions that contain the deleted + * organization will be cleared. + * + * @param organization_id - The ID of the organization to delete + */ +const delete_ = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteInput, + outputSchema: DeleteOutput, + errors: [NotFound] as const, +})); +export { delete_ as delete }; diff --git a/packages/clerk/src/operations/backend/organizations/deleteLogo.ts b/packages/clerk/src/operations/backend/organizations/deleteLogo.ts new file mode 100644 index 0000000000..3aed715af0 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizations/deleteLogo.ts @@ -0,0 +1,49 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteLogoInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ method: "DELETE", path: "/organizations/{organization_id}/logo" }), +); +export type DeleteLogoInput = typeof DeleteLogoInput.Type; + +// Output Schema +export const DeleteLogoOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional(Schema.Boolean), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), +}); +export type DeleteLogoOutput = typeof DeleteLogoOutput.Type; + +// The operation +/** + * Delete the organization's logo. + * + * @param organization_id - The ID of the organization for which the logo will be deleted. + */ +export const deleteLogo = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteLogoInput, + outputSchema: DeleteLogoOutput, + errors: [NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizations/get.ts b/packages/clerk/src/operations/backend/organizations/get.ts new file mode 100644 index 0000000000..6ae49e26af --- /dev/null +++ b/packages/clerk/src/operations/backend/organizations/get.ts @@ -0,0 +1,55 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + include_members_count: Schema.optional(Schema.Boolean), + include_missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), +}).pipe(T.Http({ method: "GET", path: "/organizations/{organization_id}" })); +export type GetInput = typeof GetInput.Type; + +// Output Schema +export const GetOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional(Schema.Boolean), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), +}); +export type GetOutput = typeof GetOutput.Type; + +// The operation +/** + * Retrieve an organization by ID or slug + * + * Fetches the organization whose ID or slug matches the provided `id_or_slug` URL query parameter. + * + * @param organization_id - The ID or slug of the organization + * @param include_members_count - Flag to denote whether or not the organization's members count should be included in the response. + * @param include_missing_member_with_elevated_permissions - Flag to denote whether or not to include a member with elevated permissions who is not currently a member of the organization. + */ +export const get = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInput, + outputSchema: GetOutput, + errors: [Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizations/getBillingCreditBalance.ts b/packages/clerk/src/operations/backend/organizations/getBillingCreditBalance.ts new file mode 100644 index 0000000000..3a77791ef9 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizations/getBillingCreditBalance.ts @@ -0,0 +1,55 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const GetBillingCreditBalanceInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + }).pipe( + T.Http({ + method: "GET", + path: "/organizations/{organization_id}/billing/credits", + }), + ); +export type GetBillingCreditBalanceInput = + typeof GetBillingCreditBalanceInput.Type; + +// Output Schema +export const GetBillingCreditBalanceOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + balance: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + }); +export type GetBillingCreditBalanceOutput = + typeof GetBillingCreditBalanceOutput.Type; + +// The operation +/** + * Retrieve an organization's credit balance + * + * Retrieves the current credit balance for the specified organization. + * Credits can be applied during checkout to reduce the charge or automatically applied to upcoming recurring charges. + * + * @param organization_id - The ID of the organization whose credit balance to retrieve + */ +export const getBillingCreditBalance = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: GetBillingCreditBalanceInput, + outputSchema: GetBillingCreditBalanceOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/organizations/getBillingSubscription.ts b/packages/clerk/src/operations/backend/organizations/getBillingSubscription.ts new file mode 100644 index 0000000000..df9739d055 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizations/getBillingSubscription.ts @@ -0,0 +1,431 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const GetBillingSubscriptionInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + }).pipe( + T.Http({ + method: "GET", + path: "/organizations/{organization_id}/billing/subscription", + }), + ); +export type GetBillingSubscriptionInput = + typeof GetBillingSubscriptionInput.Type; + +// Output Schema +export const GetBillingSubscriptionOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["commerce_subscription"]), + id: Schema.String, + instance_id: Schema.String, + status: Schema.Literals([ + "active", + "past_due", + "canceled", + "ended", + "abandoned", + "incomplete", + ]), + payer_id: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + active_at: Schema.NullOr(Schema.Number), + past_due_at: Schema.NullOr(Schema.Number), + subscription_items: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["commerce_subscription_item"]), + id: Schema.String, + instance_id: Schema.String, + status: Schema.Literals([ + "active", + "canceled", + "expired", + "ended", + "past_due", + "upcoming", + "incomplete", + "abandoned", + ]), + credit: Schema.optional( + Schema.Struct({ + amount: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + ), + cycle_remaining_percent: Schema.optional(Schema.Number), + }), + ), + credits: Schema.optional( + Schema.NullOr( + Schema.Struct({ + proration: Schema.NullOr( + Schema.Struct({ + amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + cycle_days_remaining: Schema.Number, + cycle_days_total: Schema.Number, + cycle_remaining_percent: Schema.Number, + }), + ), + payer: Schema.NullOr( + Schema.Struct({ + remaining_balance: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + applied_amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + ), + plan_id: Schema.NullOr(Schema.String), + price_id: Schema.optional(Schema.String), + plan: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["commerce_plan"]), + id: Schema.String, + name: Schema.String, + fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + annual_monthly_fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + annual_fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + description: Schema.NullOr(Schema.String), + product_id: Schema.String, + is_default: Schema.Boolean, + is_recurring: Schema.Boolean, + publicly_visible: Schema.Boolean, + has_base_fee: Schema.Boolean, + for_payer_type: Schema.String, + slug: Schema.String, + avatar_url: Schema.NullOr(Schema.String), + features: Schema.optional( + Schema.Array( + Schema.Struct({ + object: Schema.Literals(["feature"]), + id: Schema.String, + name: Schema.String, + description: Schema.NullOr(Schema.String), + slug: Schema.String, + avatar_url: Schema.NullOr(Schema.String), + }), + ), + ), + free_trial_enabled: Schema.Boolean, + free_trial_days: Schema.NullOr(Schema.Number), + unit_prices: Schema.optional( + Schema.Array( + Schema.Struct({ + name: Schema.String, + block_size: Schema.Number, + tiers: Schema.Array( + Schema.Struct({ + starts_at_block: Schema.Number, + ends_after_block: Schema.optional( + Schema.NullOr(Schema.Number), + ), + fee_per_block: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + }), + ), + ), + }), + ), + ), + plan_period: Schema.Literals(["month", "annual"]), + payment_method: Schema.optional( + Schema.Struct({ + object: Schema.Literals(["commerce_payment_method"]), + id: Schema.String, + payer_id: Schema.String, + payment_type: Schema.Literals(["card", "link"]), + is_default: Schema.optional(Schema.Boolean), + gateway: Schema.String, + gateway_external_id: Schema.String, + gateway_external_account_id: Schema.NullOr(Schema.String), + last4: Schema.NullOr(Schema.String), + status: Schema.Literals(["active", "disconnected"]), + wallet_type: Schema.optional(Schema.NullOr(Schema.String)), + card_type: Schema.NullOr(Schema.String), + expiry_year: Schema.optional(Schema.NullOr(Schema.Number)), + expiry_month: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + is_removable: Schema.optional(Schema.Boolean), + }), + ), + lifetime_paid: Schema.optional( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + next_payment: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + ), + date: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + ), + payer_id: Schema.String, + payer: Schema.optional( + Schema.Struct({ + object: Schema.Literals(["commerce_payer"]), + id: Schema.String, + instance_id: Schema.String, + user_id: Schema.optional(Schema.NullOr(Schema.String)), + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + email: Schema.optional(Schema.NullOr(Schema.String)), + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + organization_name: Schema.optional(Schema.NullOr(Schema.String)), + image_url: Schema.optional(Schema.String), + credits_balance: Schema.optional( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + }), + ), + is_free_trial: Schema.Boolean, + period_start: Schema.Number, + period_end: Schema.NullOr(Schema.Number), + proration_date: Schema.optional(Schema.String), + canceled_at: Schema.NullOr(Schema.Number), + past_due_at: Schema.NullOr(Schema.Number), + ended_at: Schema.NullOr(Schema.Number), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + seats: Schema.optional( + Schema.NullOr( + Schema.Struct({ + quantity: Schema.NullOr(Schema.Number), + tiers: Schema.optional( + Schema.Array( + Schema.Struct({ + quantity: Schema.optional(Schema.NullOr(Schema.Number)), + fee_per_block: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + ), + }), + ), + ), + totals: Schema.optional( + Schema.NullOr( + Schema.Struct({ + subtotal: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + base_fee: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + tax_total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + grand_total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + per_unit_totals: Schema.optional( + Schema.Array( + Schema.Struct({ + name: Schema.String, + block_size: Schema.Number, + tiers: Schema.Array( + Schema.Struct({ + quantity: Schema.optional(Schema.NullOr(Schema.Number)), + fee_per_block: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + }), + ), + ), + credits: Schema.optional( + Schema.NullOr( + Schema.Struct({ + proration: Schema.NullOr( + Schema.Struct({ + amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + cycle_days_remaining: Schema.Number, + cycle_days_total: Schema.Number, + cycle_remaining_percent: Schema.Number, + }), + ), + payer: Schema.NullOr( + Schema.Struct({ + remaining_balance: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + applied_amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + ), + }), + ), + ), + }), + ), + next_payment: Schema.optional( + Schema.Struct({ + date: Schema.Number, + amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + eligible_for_free_trial: Schema.optional(Schema.Boolean), + }); +export type GetBillingSubscriptionOutput = + typeof GetBillingSubscriptionOutput.Type; + +// The operation +/** + * Retrieve an organization's billing subscription + * + * Retrieves the billing subscription for the specified organization. + * This includes subscription details, active plans, billing information, and payment status. + * The subscription contains subscription items which represent the individual plans the organization is subscribed to. + * + * @param organization_id - The ID of the organization whose subscription to retrieve + */ +export const getBillingSubscription = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: GetBillingSubscriptionInput, + outputSchema: GetBillingSubscriptionOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/organizations/index.ts b/packages/clerk/src/operations/backend/organizations/index.ts new file mode 100644 index 0000000000..f78c1b31a1 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizations/index.ts @@ -0,0 +1,11 @@ +export * from "./list.ts"; +export * from "./create.ts"; +export * from "./get.ts"; +export * from "./update.ts"; +export * from "./delete.ts"; +export * from "./mergeMetadata.ts"; +export * from "./uploadLogo.ts"; +export * from "./deleteLogo.ts"; +export * from "./getBillingSubscription.ts"; +export * from "./getBillingCreditBalance.ts"; +export * from "./adjustBillingCreditBalance.ts"; diff --git a/packages/clerk/src/operations/backend/organizations/list.ts b/packages/clerk/src/operations/backend/organizations/list.ts new file mode 100644 index 0000000000..621f9cdde2 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizations/list.ts @@ -0,0 +1,87 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ListInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + include_members_count: Schema.optional(Schema.Boolean), + include_missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + query: Schema.optional(Schema.String), + user_id: Schema.optional(Schema.String), + organization_id: Schema.optional(Schema.String), + order_by: Schema.optional(Schema.String), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), +}).pipe(T.Http({ method: "GET", path: "/organizations" })); +export type ListInput = typeof ListInput.Type; + +// Output Schema +export const ListOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional(Schema.Boolean), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + ), + total_count: Schema.Number, +}); +export type ListOutput = typeof ListOutput.Type; + +// The operation +/** + * Get a list of organizations for an instance + * + * This request returns the list of organizations for an instance. + * Results can be paginated using the optional `limit` and `offset` query parameters. + * The organizations are ordered by descending creation date. + * Most recent organizations will be returned first. + * + * @param include_members_count - Flag to denote whether the member counts of each organization should be included in the response or not. + * @param include_missing_member_with_elevated_permissions - Flag to denote whether or not to include a member with elevated permissions who is not currently a member of the organization. + * @param query - Returns organizations with ID, name, or slug that match the given query. +Uses exact match for organization ID and partial match for name and slug. + * @param user_id - Returns organizations that include any of the specified user IDs as members. Any user IDs not found are ignored. +For each user ID, the `+` and `-` can be prepended to the ID, which denote whether the +respective organization should be included or excluded from the result set. + * @param organization_id - Returns organizations with the organization IDs specified. Any organization IDs not found are ignored. +For each organization ID, the `+` and `-` can be prepended to the ID, which denote whether the +respective organization should be included or excluded from the result set. Accepts up to 100 organization IDs. +Example: ?organization_id=+org_1&organization_id=-org_2 + * @param order_by - Allows to return organizations in a particular order. +At the moment, you can order the returned organizations either by their `name`, `created_at` or `members_count`. +In order to specify the direction, you can use the `+/-` symbols prepended in the property to order by. +For example, if you want organizations to be returned in descending order according to their `created_at` property, you can use `-created_at`. +If you don't use `+` or `-`, then `+` is implied. +Defaults to `-created_at`. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + */ +export const list = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInput, + outputSchema: ListOutput, + errors: [BadRequest, Forbidden, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizations/mergeMetadata.ts b/packages/clerk/src/operations/backend/organizations/mergeMetadata.ts new file mode 100644 index 0000000000..9f77bb8eba --- /dev/null +++ b/packages/clerk/src/operations/backend/organizations/mergeMetadata.ts @@ -0,0 +1,63 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const MergeMetadataInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), +}).pipe( + T.Http({ + method: "PATCH", + path: "/organizations/{organization_id}/metadata", + }), +); +export type MergeMetadataInput = typeof MergeMetadataInput.Type; + +// Output Schema +export const MergeMetadataOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional(Schema.Boolean), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), +}); +export type MergeMetadataOutput = typeof MergeMetadataOutput.Type; + +// The operation +/** + * Merge and update metadata for an organization + * + * Update organization metadata attributes by merging existing values with the provided parameters. + * Metadata values will be updated via a deep merge. + * Deep meaning that any nested JSON objects will be merged as well. + * You can remove metadata keys at any level by setting their value to `null`. + * + * @param organization_id - The ID of the organization for which metadata will be merged or updated + */ +export const mergeMetadata = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: MergeMetadataInput, + outputSchema: MergeMetadataOutput, + errors: [BadRequest, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizations/update.ts b/packages/clerk/src/operations/backend/organizations/update.ts new file mode 100644 index 0000000000..8c3cad0c0f --- /dev/null +++ b/packages/clerk/src/operations/backend/organizations/update.ts @@ -0,0 +1,73 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const UpdateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + public_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + name: Schema.optional(Schema.NullOr(Schema.String)), + slug: Schema.optional(Schema.NullOr(Schema.String)), + max_allowed_memberships: Schema.optional(Schema.NullOr(Schema.Number)), + admin_delete_enabled: Schema.optional(Schema.NullOr(Schema.Boolean)), + created_at: Schema.optional(Schema.NullOr(Schema.String)), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), +}).pipe(T.Http({ method: "PATCH", path: "/organizations/{organization_id}" })); +export type UpdateInput = typeof UpdateInput.Type; + +// Output Schema +export const UpdateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional(Schema.Boolean), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), +}); +export type UpdateOutput = typeof UpdateOutput.Type; + +// The operation +/** + * Update an organization + * + * Updates an existing organization + * + * @param organization_id - The ID of the organization to update + */ +export const update = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateInput, + outputSchema: UpdateOutput, + errors: [ + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + UnprocessableEntity, + ] as const, +})); diff --git a/packages/clerk/src/operations/backend/organizations/uploadLogo.ts b/packages/clerk/src/operations/backend/organizations/uploadLogo.ts new file mode 100644 index 0000000000..f49f545ac8 --- /dev/null +++ b/packages/clerk/src/operations/backend/organizations/uploadLogo.ts @@ -0,0 +1,66 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + PayloadTooLarge, +} from "../../../errors.ts"; + +// Input Schema +export const UploadLogoInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + organization_id: Schema.String.pipe(T.PathParam()), + uploader_user_id: Schema.optional(Schema.String), + file: Schema.String, +}).pipe( + T.Http({ + method: "PUT", + path: "/organizations/{organization_id}/logo", + contentType: "multipart", + }), +); +export type UploadLogoInput = typeof UploadLogoInput.Type; + +// Output Schema +export const UploadLogoOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.String, + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional(Schema.Boolean), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + logo_url: Schema.optional(Schema.String), +}); +export type UploadLogoOutput = typeof UploadLogoOutput.Type; + +// The operation +/** + * Upload a logo for the organization + * + * Set or replace an organization's logo, by uploading an image file. + * This endpoint uses the `multipart/form-data` request content type and accepts a file of image type. + * The file size cannot exceed 10MB. + * Only the following file content types are supported: `image/jpeg`, `image/png`, `image/gif`, `image/webp`. + * + * @param organization_id - The ID of the organization for which to upload a logo + */ +export const uploadLogo = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UploadLogoInput, + outputSchema: UploadLogoOutput, + errors: [BadRequest, Forbidden, NotFound, PayloadTooLarge] as const, +})); diff --git a/packages/clerk/src/operations/backend/phoneNumbers/create.ts b/packages/clerk/src/operations/backend/phoneNumbers/create.ts new file mode 100644 index 0000000000..b45c20ca05 --- /dev/null +++ b/packages/clerk/src/operations/backend/phoneNumbers/create.ts @@ -0,0 +1,52 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String, + phone_number: Schema.String, + verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + primary: Schema.optional(Schema.NullOr(Schema.Boolean)), + reserved_for_second_factor: Schema.optional(Schema.NullOr(Schema.Boolean)), +}).pipe(T.Http({ method: "POST", path: "/phone_numbers" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create a phone number + * + * Create a new phone number + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/phoneNumbers/delete.ts b/packages/clerk/src/operations/backend/phoneNumbers/delete.ts new file mode 100644 index 0000000000..f12892de2f --- /dev/null +++ b/packages/clerk/src/operations/backend/phoneNumbers/delete.ts @@ -0,0 +1,35 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + phone_number_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "DELETE", path: "/phone_numbers/{phone_number_id}" })); +export type DeleteInput = typeof DeleteInput.Type; + +// Output Schema +export const DeleteOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), +}); +export type DeleteOutput = typeof DeleteOutput.Type; + +// The operation +/** + * Delete a phone number + * + * Delete the phone number with the given ID + * + * @param phone_number_id - The ID of the phone number to delete + */ +const delete_ = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteInput, + outputSchema: DeleteOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); +export { delete_ as delete }; diff --git a/packages/clerk/src/operations/backend/phoneNumbers/get.ts b/packages/clerk/src/operations/backend/phoneNumbers/get.ts new file mode 100644 index 0000000000..1063141fee --- /dev/null +++ b/packages/clerk/src/operations/backend/phoneNumbers/get.ts @@ -0,0 +1,45 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + phone_number_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "GET", path: "/phone_numbers/{phone_number_id}" })); +export type GetInput = typeof GetInput.Type; + +// Output Schema +export const GetOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type GetOutput = typeof GetOutput.Type; + +// The operation +/** + * Retrieve a phone number + * + * Returns the details of a phone number + * + * @param phone_number_id - The ID of the phone number to retrieve + */ +export const get = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInput, + outputSchema: GetOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/phoneNumbers/index.ts b/packages/clerk/src/operations/backend/phoneNumbers/index.ts new file mode 100644 index 0000000000..38eadc4ef7 --- /dev/null +++ b/packages/clerk/src/operations/backend/phoneNumbers/index.ts @@ -0,0 +1,5 @@ +export * from "./create.ts"; +export * from "./get.ts"; +export * from "./update.ts"; +export * from "./delete.ts"; +export * from "./replaceForUser.ts"; diff --git a/packages/clerk/src/operations/backend/phoneNumbers/replaceForUser.ts b/packages/clerk/src/operations/backend/phoneNumbers/replaceForUser.ts new file mode 100644 index 0000000000..6098d72048 --- /dev/null +++ b/packages/clerk/src/operations/backend/phoneNumbers/replaceForUser.ts @@ -0,0 +1,54 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const ReplaceForUserInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + phone_number: Schema.String, +}).pipe(T.Http({ method: "PUT", path: "/users/{user_id}/phone_number" })); +export type ReplaceForUserInput = typeof ReplaceForUserInput.Type; + +// Output Schema +export const ReplaceForUserOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type ReplaceForUserOutput = typeof ReplaceForUserOutput.Type; + +// The operation +/** + * Replace a user's phone number + * + * Replaces all of the user's phone numbers with a single verified, primary phone number. + * The new phone number is created with the admin verification strategy and is not reserved + * for second factor. Any existing phone numbers are deleted; replacing a phone number that is + * reserved for second factor disables the user's MFA. + * + * @param user_id - The ID of the user whose phone number to replace + */ +export const replaceForUser = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ReplaceForUserInput, + outputSchema: ReplaceForUserOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/phoneNumbers/update.ts b/packages/clerk/src/operations/backend/phoneNumbers/update.ts new file mode 100644 index 0000000000..7738d714bc --- /dev/null +++ b/packages/clerk/src/operations/backend/phoneNumbers/update.ts @@ -0,0 +1,48 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const UpdateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + phone_number_id: Schema.String.pipe(T.PathParam()), + verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + primary: Schema.optional(Schema.NullOr(Schema.Boolean)), + reserved_for_second_factor: Schema.optional(Schema.NullOr(Schema.Boolean)), +}).pipe(T.Http({ method: "PATCH", path: "/phone_numbers/{phone_number_id}" })); +export type UpdateInput = typeof UpdateInput.Type; + +// Output Schema +export const UpdateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type UpdateOutput = typeof UpdateOutput.Type; + +// The operation +/** + * Update a phone number + * + * Updates a phone number + * + * @param phone_number_id - The ID of the phone number to update + */ +export const update = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateInput, + outputSchema: UpdateOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/proxyChecks/index.ts b/packages/clerk/src/operations/backend/proxyChecks/index.ts new file mode 100644 index 0000000000..f90f678bbf --- /dev/null +++ b/packages/clerk/src/operations/backend/proxyChecks/index.ts @@ -0,0 +1 @@ +export * from "./verify.ts"; diff --git a/packages/clerk/src/operations/backend/proxyChecks/verify.ts b/packages/clerk/src/operations/backend/proxyChecks/verify.ts new file mode 100644 index 0000000000..e0470c8b90 --- /dev/null +++ b/packages/clerk/src/operations/backend/proxyChecks/verify.ts @@ -0,0 +1,41 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const VerifyInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + domain_id: Schema.optional(Schema.String), + proxy_url: Schema.optional(Schema.String), +}).pipe(T.Http({ method: "POST", path: "/proxy_checks" })); +export type VerifyInput = typeof VerifyInput.Type; + +// Output Schema +export const VerifyOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["proxy_check"]), + id: Schema.String, + domain_id: Schema.String, + last_run_at: Schema.NullOr(Schema.Number), + proxy_url: Schema.String, + successful: Schema.Boolean, + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type VerifyOutput = typeof VerifyOutput.Type; + +// The operation +/** + * Verify the proxy configuration for your domain + * + * This endpoint can be used to validate that a proxy-enabled domain is operational. + * It tries to verify that the proxy URL provided in the parameters maps to a functional proxy that can reach the Clerk Frontend API. + * You can use this endpoint before you set a proxy URL for a domain. This way you can ensure that switching to proxy-based + * configuration will not lead to downtime for your instance. + * The `proxy_url` parameter allows for testing proxy configurations for domains that don't have a proxy URL yet, or operate on + * a different proxy URL than the one provided. It can also be used to re-validate a domain that is already configured to work with a proxy. + */ +export const verify = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: VerifyInput, + outputSchema: VerifyOutput, + errors: [BadRequest, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/redirectUrls/create.ts b/packages/clerk/src/operations/backend/redirectUrls/create.ts new file mode 100644 index 0000000000..e6d6994d1d --- /dev/null +++ b/packages/clerk/src/operations/backend/redirectUrls/create.ts @@ -0,0 +1,30 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + url: Schema.String, +}).pipe(T.Http({ method: "POST", path: "/redirect_urls" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["redirect_url"]), + id: Schema.String, + url: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create a redirect URL + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [BadRequest, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/redirectUrls/delete.ts b/packages/clerk/src/operations/backend/redirectUrls/delete.ts new file mode 100644 index 0000000000..66a7fa940d --- /dev/null +++ b/packages/clerk/src/operations/backend/redirectUrls/delete.ts @@ -0,0 +1,35 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "DELETE", path: "/redirect_urls/{id}" })); +export type DeleteInput = typeof DeleteInput.Type; + +// Output Schema +export const DeleteOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), +}); +export type DeleteOutput = typeof DeleteOutput.Type; + +// The operation +/** + * Delete a redirect URL + * + * Remove the selected redirect URL from the whitelist of the instance + * + * @param id - The ID of the redirect URL + */ +const delete_ = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteInput, + outputSchema: DeleteOutput, + errors: [NotFound] as const, +})); +export { delete_ as delete }; diff --git a/packages/clerk/src/operations/backend/redirectUrls/get.ts b/packages/clerk/src/operations/backend/redirectUrls/get.ts new file mode 100644 index 0000000000..3ffd63cd08 --- /dev/null +++ b/packages/clerk/src/operations/backend/redirectUrls/get.ts @@ -0,0 +1,34 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "GET", path: "/redirect_urls/{id}" })); +export type GetInput = typeof GetInput.Type; + +// Output Schema +export const GetOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["redirect_url"]), + id: Schema.String, + url: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type GetOutput = typeof GetOutput.Type; + +// The operation +/** + * Retrieve a redirect URL + * + * Retrieve the details of the redirect URL with the given ID + * + * @param id - The ID of the redirect URL + */ +export const get = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInput, + outputSchema: GetOutput, + errors: [NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/redirectUrls/index.ts b/packages/clerk/src/operations/backend/redirectUrls/index.ts new file mode 100644 index 0000000000..084517f21b --- /dev/null +++ b/packages/clerk/src/operations/backend/redirectUrls/index.ts @@ -0,0 +1,4 @@ +export * from "./list.ts"; +export * from "./create.ts"; +export * from "./get.ts"; +export * from "./delete.ts"; diff --git a/packages/clerk/src/operations/backend/redirectUrls/list.ts b/packages/clerk/src/operations/backend/redirectUrls/list.ts new file mode 100644 index 0000000000..e669a15cb4 --- /dev/null +++ b/packages/clerk/src/operations/backend/redirectUrls/list.ts @@ -0,0 +1,43 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; + +// Input Schema +export const ListInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + paginated: Schema.optional(Schema.Boolean), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), +}).pipe(T.Http({ method: "GET", path: "/redirect_urls" })); +export type ListInput = typeof ListInput.Type; + +// Output Schema +export const ListOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Array( + Schema.Struct({ + object: Schema.Literals(["redirect_url"]), + id: Schema.String, + url: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + }), +); +export type ListOutput = typeof ListOutput.Type; + +// The operation +/** + * List all redirect URLs + * + * Lists all whitelisted redirect_urls for the instance + * + * @param paginated - Whether to paginate the results. +If true, the results will be paginated. +If false, the results will not be paginated. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + */ +export const list = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInput, + outputSchema: ListOutput, +})); diff --git a/packages/clerk/src/operations/backend/roleSets/addRoles.ts b/packages/clerk/src/operations/backend/roleSets/addRoles.ts new file mode 100644 index 0000000000..ad3d792297 --- /dev/null +++ b/packages/clerk/src/operations/backend/roleSets/addRoles.ts @@ -0,0 +1,113 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const AddRolesInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + role_set_key_or_id: Schema.String.pipe(T.PathParam()), + role_keys: Schema.Array(Schema.String), + default_role_key: Schema.optional(Schema.String), + creator_role_key: Schema.optional(Schema.String), +}).pipe( + T.Http({ method: "POST", path: "/role_sets/{role_set_key_or_id}/roles" }), +); +export type AddRolesInput = typeof AddRolesInput.Type; + +// Output Schema +export const AddRolesOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["role_set"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + roles: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["role_set_item"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + members_count: Schema.optional(Schema.NullOr(Schema.Number)), + has_members: Schema.optional(Schema.NullOr(Schema.Boolean)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + default_role: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["role_set_item"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + members_count: Schema.optional(Schema.NullOr(Schema.Number)), + has_members: Schema.optional(Schema.NullOr(Schema.Boolean)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + creator_role: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["role_set_item"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + members_count: Schema.optional(Schema.NullOr(Schema.Number)), + has_members: Schema.optional(Schema.NullOr(Schema.Boolean)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + type: Schema.Literals(["initial", "custom"]), + role_set_migration: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["role_set_migration"]), + id: Schema.String, + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + instance_id: Schema.String, + source_role_set_id: Schema.String, + dest_role_set_id: Schema.optional(Schema.NullOr(Schema.String)), + trigger_type: Schema.String, + status: Schema.String, + migrated_members: Schema.Number, + mappings: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.String)), + ), + started_at: Schema.optional(Schema.NullOr(Schema.Number)), + completed_at: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type AddRolesOutput = typeof AddRolesOutput.Type; + +// The operation +/** + * Add roles to a role set + * + * Adds one or more roles to an existing role set. + * You can optionally update the default role or creator role when adding new roles. + * + * @param role_set_key_or_id - The key or ID of the role set + */ +export const addRoles = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: AddRolesInput, + outputSchema: AddRolesOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/roleSets/create.ts b/packages/clerk/src/operations/backend/roleSets/create.ts new file mode 100644 index 0000000000..9e9da0e550 --- /dev/null +++ b/packages/clerk/src/operations/backend/roleSets/create.ts @@ -0,0 +1,120 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + name: Schema.String, + key: Schema.optional(Schema.String), + description: Schema.optional(Schema.NullOr(Schema.String)), + default_role_key: Schema.String, + creator_role_key: Schema.String, + type: Schema.optional(Schema.Literals(["initial", "custom"])), + roles: Schema.Array(Schema.String), +}).pipe(T.Http({ method: "POST", path: "/role_sets" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["role_set"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + roles: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["role_set_item"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + members_count: Schema.optional(Schema.NullOr(Schema.Number)), + has_members: Schema.optional(Schema.NullOr(Schema.Boolean)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + default_role: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["role_set_item"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + members_count: Schema.optional(Schema.NullOr(Schema.Number)), + has_members: Schema.optional(Schema.NullOr(Schema.Boolean)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + creator_role: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["role_set_item"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + members_count: Schema.optional(Schema.NullOr(Schema.Number)), + has_members: Schema.optional(Schema.NullOr(Schema.Boolean)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + type: Schema.Literals(["initial", "custom"]), + role_set_migration: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["role_set_migration"]), + id: Schema.String, + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + instance_id: Schema.String, + source_role_set_id: Schema.String, + dest_role_set_id: Schema.optional(Schema.NullOr(Schema.String)), + trigger_type: Schema.String, + status: Schema.String, + migrated_members: Schema.Number, + mappings: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.String)), + ), + started_at: Schema.optional(Schema.NullOr(Schema.Number)), + completed_at: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create a role set + * + * Creates a new role set with the given name and roles. + * The key must be unique for the instance and start with the 'role_set:' prefix, followed by lowercase alphanumeric characters and underscores only. + * You must provide at least one role and specify a default role key and creator role key. + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [ + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + UnprocessableEntity, + ] as const, +})); diff --git a/packages/clerk/src/operations/backend/roleSets/get.ts b/packages/clerk/src/operations/backend/roleSets/get.ts new file mode 100644 index 0000000000..67f0786f9d --- /dev/null +++ b/packages/clerk/src/operations/backend/roleSets/get.ts @@ -0,0 +1,102 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + role_set_key_or_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "GET", path: "/role_sets/{role_set_key_or_id}" })); +export type GetInput = typeof GetInput.Type; + +// Output Schema +export const GetOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["role_set"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + roles: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["role_set_item"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + members_count: Schema.optional(Schema.NullOr(Schema.Number)), + has_members: Schema.optional(Schema.NullOr(Schema.Boolean)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + default_role: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["role_set_item"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + members_count: Schema.optional(Schema.NullOr(Schema.Number)), + has_members: Schema.optional(Schema.NullOr(Schema.Boolean)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + creator_role: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["role_set_item"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + members_count: Schema.optional(Schema.NullOr(Schema.Number)), + has_members: Schema.optional(Schema.NullOr(Schema.Boolean)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + type: Schema.Literals(["initial", "custom"]), + role_set_migration: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["role_set_migration"]), + id: Schema.String, + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + instance_id: Schema.String, + source_role_set_id: Schema.String, + dest_role_set_id: Schema.optional(Schema.NullOr(Schema.String)), + trigger_type: Schema.String, + status: Schema.String, + migrated_members: Schema.Number, + mappings: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.String)), + ), + started_at: Schema.optional(Schema.NullOr(Schema.Number)), + completed_at: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type GetOutput = typeof GetOutput.Type; + +// The operation +/** + * Retrieve a role set + * + * Retrieves an existing role set by its key or ID. + * + * @param role_set_key_or_id - The key or ID of the role set + */ +export const get = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInput, + outputSchema: GetOutput, + errors: [Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/roleSets/index.ts b/packages/clerk/src/operations/backend/roleSets/index.ts new file mode 100644 index 0000000000..088d6b9e8d --- /dev/null +++ b/packages/clerk/src/operations/backend/roleSets/index.ts @@ -0,0 +1,7 @@ +export * from "./list.ts"; +export * from "./create.ts"; +export * from "./get.ts"; +export * from "./update.ts"; +export * from "./replace.ts"; +export * from "./addRoles.ts"; +export * from "./replaceRole.ts"; diff --git a/packages/clerk/src/operations/backend/roleSets/list.ts b/packages/clerk/src/operations/backend/roleSets/list.ts new file mode 100644 index 0000000000..dfa3f33752 --- /dev/null +++ b/packages/clerk/src/operations/backend/roleSets/list.ts @@ -0,0 +1,124 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ListInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + query: Schema.optional(Schema.String), + order_by: Schema.optional(Schema.String), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), +}).pipe(T.Http({ method: "GET", path: "/role_sets" })); +export type ListInput = typeof ListInput.Type; + +// Output Schema +export const ListOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["role_set"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + roles: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["role_set_item"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + members_count: Schema.optional(Schema.NullOr(Schema.Number)), + has_members: Schema.optional(Schema.NullOr(Schema.Boolean)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + default_role: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["role_set_item"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + members_count: Schema.optional(Schema.NullOr(Schema.Number)), + has_members: Schema.optional(Schema.NullOr(Schema.Boolean)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + creator_role: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["role_set_item"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + members_count: Schema.optional(Schema.NullOr(Schema.Number)), + has_members: Schema.optional(Schema.NullOr(Schema.Boolean)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + type: Schema.Literals(["initial", "custom"]), + role_set_migration: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["role_set_migration"]), + id: Schema.String, + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + instance_id: Schema.String, + source_role_set_id: Schema.String, + dest_role_set_id: Schema.optional(Schema.NullOr(Schema.String)), + trigger_type: Schema.String, + status: Schema.String, + migrated_members: Schema.Number, + mappings: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.String)), + ), + started_at: Schema.optional(Schema.NullOr(Schema.Number)), + completed_at: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + total_count: Schema.Number, +}); +export type ListOutput = typeof ListOutput.Type; + +// The operation +/** + * Get a list of role sets + * + * Returns a list of role sets for the instance. + * Results can be paginated using the optional `limit` and `offset` query parameters. + * The role sets are ordered by descending creation date by default. + * + * @param query - Returns role sets with ID, name, or key that match the given query. +Uses exact match for role set ID and partial match for name and key. + * @param order_by - Allows to return role sets in a particular order. +At the moment, you can order the returned role sets by their `created_at`, `name`, or `key`. +In order to specify the direction, you can use the `+/-` symbols prepended in the property to order by. +For example, if you want role sets to be returned in descending order according to their `created_at` property, you can use `-created_at`. +If you don't use `+` or `-`, then `+` is implied. +Defaults to `-created_at`. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + */ +export const list = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInput, + outputSchema: ListOutput, + errors: [BadRequest, Forbidden, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/roleSets/replace.ts b/packages/clerk/src/operations/backend/roleSets/replace.ts new file mode 100644 index 0000000000..258c6ce4cc --- /dev/null +++ b/packages/clerk/src/operations/backend/roleSets/replace.ts @@ -0,0 +1,47 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const ReplaceInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + role_set_key_or_id: Schema.String.pipe(T.PathParam()), + dest_role_set_key: Schema.String, + reassignment_mappings: Schema.optional( + Schema.Record(Schema.String, Schema.String), + ), +}).pipe( + T.Http({ method: "POST", path: "/role_sets/{role_set_key_or_id}/replace" }), +); +export type ReplaceInput = typeof ReplaceInput.Type; + +// Output Schema +export const ReplaceOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), +}); +export type ReplaceOutput = typeof ReplaceOutput.Type; + +// The operation +/** + * Replace a role set + * + * Replaces a role set with another role set. This is functionally equivalent to deleting + * the role set but allows for atomic replacement with migration support. + * Organizations using this role set will be migrated to the destination role set. + * + * @param role_set_key_or_id - The key or ID of the role set to replace + */ +export const replace = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ReplaceInput, + outputSchema: ReplaceOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/roleSets/replaceRole.ts b/packages/clerk/src/operations/backend/roleSets/replaceRole.ts new file mode 100644 index 0000000000..94707ec374 --- /dev/null +++ b/packages/clerk/src/operations/backend/roleSets/replaceRole.ts @@ -0,0 +1,115 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const ReplaceRoleInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + role_set_key_or_id: Schema.String.pipe(T.PathParam()), + role_key: Schema.String, + to_role_key: Schema.String, +}).pipe( + T.Http({ + method: "POST", + path: "/role_sets/{role_set_key_or_id}/roles/replace", + }), +); +export type ReplaceRoleInput = typeof ReplaceRoleInput.Type; + +// Output Schema +export const ReplaceRoleOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["role_set"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + roles: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["role_set_item"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + members_count: Schema.optional(Schema.NullOr(Schema.Number)), + has_members: Schema.optional(Schema.NullOr(Schema.Boolean)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + default_role: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["role_set_item"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + members_count: Schema.optional(Schema.NullOr(Schema.Number)), + has_members: Schema.optional(Schema.NullOr(Schema.Boolean)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + creator_role: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["role_set_item"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + members_count: Schema.optional(Schema.NullOr(Schema.Number)), + has_members: Schema.optional(Schema.NullOr(Schema.Boolean)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + type: Schema.Literals(["initial", "custom"]), + role_set_migration: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["role_set_migration"]), + id: Schema.String, + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + instance_id: Schema.String, + source_role_set_id: Schema.String, + dest_role_set_id: Schema.optional(Schema.NullOr(Schema.String)), + trigger_type: Schema.String, + status: Schema.String, + migrated_members: Schema.Number, + mappings: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.String)), + ), + started_at: Schema.optional(Schema.NullOr(Schema.Number)), + completed_at: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type ReplaceRoleOutput = typeof ReplaceRoleOutput.Type; + +// The operation +/** + * Replace a role in a role set + * + * Replaces a role in a role set with another role. This atomically removes + * the source role and reassigns any members to the destination role. + * + * @param role_set_key_or_id - The key or ID of the role set + */ +export const replaceRole = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ReplaceRoleInput, + outputSchema: ReplaceRoleOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/roleSets/update.ts b/packages/clerk/src/operations/backend/roleSets/update.ts new file mode 100644 index 0000000000..3057472cc7 --- /dev/null +++ b/packages/clerk/src/operations/backend/roleSets/update.ts @@ -0,0 +1,115 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const UpdateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + role_set_key_or_id: Schema.String.pipe(T.PathParam()), + name: Schema.optional(Schema.NullOr(Schema.String)), + key: Schema.optional(Schema.NullOr(Schema.String)), + description: Schema.optional(Schema.NullOr(Schema.String)), + type: Schema.optional(Schema.NullOr(Schema.Literals(["initial"]))), + default_role_key: Schema.optional(Schema.NullOr(Schema.String)), + creator_role_key: Schema.optional(Schema.NullOr(Schema.String)), +}).pipe(T.Http({ method: "PATCH", path: "/role_sets/{role_set_key_or_id}" })); +export type UpdateInput = typeof UpdateInput.Type; + +// Output Schema +export const UpdateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["role_set"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + roles: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["role_set_item"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + members_count: Schema.optional(Schema.NullOr(Schema.Number)), + has_members: Schema.optional(Schema.NullOr(Schema.Boolean)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + default_role: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["role_set_item"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + members_count: Schema.optional(Schema.NullOr(Schema.Number)), + has_members: Schema.optional(Schema.NullOr(Schema.Boolean)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + creator_role: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["role_set_item"]), + id: Schema.String, + name: Schema.String, + key: Schema.String, + description: Schema.NullOr(Schema.String), + members_count: Schema.optional(Schema.NullOr(Schema.Number)), + has_members: Schema.optional(Schema.NullOr(Schema.Boolean)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + type: Schema.Literals(["initial", "custom"]), + role_set_migration: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["role_set_migration"]), + id: Schema.String, + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + instance_id: Schema.String, + source_role_set_id: Schema.String, + dest_role_set_id: Schema.optional(Schema.NullOr(Schema.String)), + trigger_type: Schema.String, + status: Schema.String, + migrated_members: Schema.Number, + mappings: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.String)), + ), + started_at: Schema.optional(Schema.NullOr(Schema.Number)), + completed_at: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type UpdateOutput = typeof UpdateOutput.Type; + +// The operation +/** + * Update a role set + * + * Updates an existing role set. + * You can update the name, key, description, type, default role, or creator role. + * All parameters are optional - you can update only the fields you want to change. + * + * @param role_set_key_or_id - The key or ID of the role set to update + */ +export const update = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateInput, + outputSchema: UpdateOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/sessions/create.ts b/packages/clerk/src/operations/backend/sessions/create.ts new file mode 100644 index 0000000000..365a866f72 --- /dev/null +++ b/packages/clerk/src/operations/backend/sessions/create.ts @@ -0,0 +1,75 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String, + active_organization_id: Schema.optional(Schema.String), +}).pipe(T.Http({ method: "POST", path: "/sessions" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["session"]), + id: Schema.String, + user_id: Schema.String, + client_id: Schema.String, + actor: Schema.optional(Schema.NullOr(Schema.Unknown)), + status: Schema.Literals([ + "active", + "revoked", + "ended", + "expired", + "removed", + "abandoned", + "replaced", + "pending", + ]), + last_active_organization_id: Schema.optional(Schema.NullOr(Schema.String)), + last_active_at: Schema.Number, + latest_activity: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.String, + id: Schema.String, + device_type: Schema.optional(Schema.String), + is_mobile: Schema.Boolean, + browser_name: Schema.optional(Schema.String), + browser_version: Schema.optional(Schema.String), + ip_address: Schema.optional(Schema.String), + city: Schema.optional(Schema.String), + country: Schema.optional(Schema.String), + }), + ), + ), + expire_at: Schema.Number, + abandon_at: Schema.Number, + updated_at: Schema.Number, + created_at: Schema.Number, + tasks: Schema.optional( + Schema.NullOr( + Schema.Array( + Schema.Struct({ + key: Schema.String, + }), + ), + ), + ), +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create a new active session + * + * Create a new active session for the provided user ID. + * **This operation is intended only for use in testing, and is not available for production instances.** If you are looking to generate a user session from the backend, + * we recommend using the [Sign-in Tokens](https://clerk.com/docs/reference/backend-api/tag/Sign-in-Tokens#operation/CreateSignInToken) resource instead. + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [BadRequest, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/sessions/createToken.ts b/packages/clerk/src/operations/backend/sessions/createToken.ts new file mode 100644 index 0000000000..a2dba510ec --- /dev/null +++ b/packages/clerk/src/operations/backend/sessions/createToken.ts @@ -0,0 +1,32 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { NotFound } from "../../../errors.ts"; + +// Input Schema +export const CreateTokenInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + session_id: Schema.String.pipe(T.PathParam()), + expires_in_seconds: Schema.optional(Schema.NullOr(Schema.Number)), +}).pipe(T.Http({ method: "POST", path: "/sessions/{session_id}/tokens" })); +export type CreateTokenInput = typeof CreateTokenInput.Type; + +// Output Schema +export const CreateTokenOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.optional(Schema.Literals(["token"])), + jwt: Schema.optional(Schema.String), +}); +export type CreateTokenOutput = typeof CreateTokenOutput.Type; + +// The operation +/** + * Create a session token + * + * Creates a session JSON Web Token (JWT) based on a session. + * + * @param session_id - The ID of the session + */ +export const createToken = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateTokenInput, + outputSchema: CreateTokenOutput, + errors: [NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/sessions/createTokenFromTemplate.ts b/packages/clerk/src/operations/backend/sessions/createTokenFromTemplate.ts new file mode 100644 index 0000000000..f06fd2d809 --- /dev/null +++ b/packages/clerk/src/operations/backend/sessions/createTokenFromTemplate.ts @@ -0,0 +1,45 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { NotFound } from "../../../errors.ts"; + +// Input Schema +export const CreateTokenFromTemplateInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + session_id: Schema.String.pipe(T.PathParam()), + template_name: Schema.String.pipe(T.PathParam()), + expires_in_seconds: Schema.optional(Schema.NullOr(Schema.Number)), + }).pipe( + T.Http({ + method: "POST", + path: "/sessions/{session_id}/tokens/{template_name}", + }), + ); +export type CreateTokenFromTemplateInput = + typeof CreateTokenFromTemplateInput.Type; + +// Output Schema +export const CreateTokenFromTemplateOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.optional(Schema.Literals(["token"])), + jwt: Schema.optional(Schema.String), + }); +export type CreateTokenFromTemplateOutput = + typeof CreateTokenFromTemplateOutput.Type; + +// The operation +/** + * Create a session token from a JWT template + * + * Creates a JSON Web Token (JWT) based on a session and a JWT Template name defined for your instance + * + * @param session_id - The ID of the session + * @param template_name - The name of the JWT template defined in your instance (e.g. `custom_hasura`). + */ +export const createTokenFromTemplate = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: CreateTokenFromTemplateInput, + outputSchema: CreateTokenFromTemplateOutput, + errors: [NotFound] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/sessions/get.ts b/packages/clerk/src/operations/backend/sessions/get.ts new file mode 100644 index 0000000000..f2d8ba90da --- /dev/null +++ b/packages/clerk/src/operations/backend/sessions/get.ts @@ -0,0 +1,74 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + session_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "GET", path: "/sessions/{session_id}" })); +export type GetInput = typeof GetInput.Type; + +// Output Schema +export const GetOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["session"]), + id: Schema.String, + user_id: Schema.String, + client_id: Schema.String, + actor: Schema.optional(Schema.NullOr(Schema.Unknown)), + status: Schema.Literals([ + "active", + "revoked", + "ended", + "expired", + "removed", + "abandoned", + "replaced", + "pending", + ]), + last_active_organization_id: Schema.optional(Schema.NullOr(Schema.String)), + last_active_at: Schema.Number, + latest_activity: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.String, + id: Schema.String, + device_type: Schema.optional(Schema.String), + is_mobile: Schema.Boolean, + browser_name: Schema.optional(Schema.String), + browser_version: Schema.optional(Schema.String), + ip_address: Schema.optional(Schema.String), + city: Schema.optional(Schema.String), + country: Schema.optional(Schema.String), + }), + ), + ), + expire_at: Schema.Number, + abandon_at: Schema.Number, + updated_at: Schema.Number, + created_at: Schema.Number, + tasks: Schema.optional( + Schema.NullOr( + Schema.Array( + Schema.Struct({ + key: Schema.String, + }), + ), + ), + ), +}); +export type GetOutput = typeof GetOutput.Type; + +// The operation +/** + * Retrieve a session + * + * Retrieve the details of a session + * + * @param session_id - The ID of the session + */ +export const get = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInput, + outputSchema: GetOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/sessions/index.ts b/packages/clerk/src/operations/backend/sessions/index.ts new file mode 100644 index 0000000000..900833b045 --- /dev/null +++ b/packages/clerk/src/operations/backend/sessions/index.ts @@ -0,0 +1,7 @@ +export * from "./list.ts"; +export * from "./create.ts"; +export * from "./get.ts"; +export * from "./refresh.ts"; +export * from "./revoke.ts"; +export * from "./createToken.ts"; +export * from "./createTokenFromTemplate.ts"; diff --git a/packages/clerk/src/operations/backend/sessions/list.ts b/packages/clerk/src/operations/backend/sessions/list.ts new file mode 100644 index 0000000000..30a613382e --- /dev/null +++ b/packages/clerk/src/operations/backend/sessions/list.ts @@ -0,0 +1,105 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ListInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + client_id: Schema.optional(Schema.String), + user_id: Schema.optional(Schema.String), + status: Schema.optional( + Schema.Literals([ + "abandoned", + "active", + "ended", + "expired", + "removed", + "replaced", + "revoked", + ]), + ), + paginated: Schema.optional(Schema.Boolean), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), +}).pipe(T.Http({ method: "GET", path: "/sessions" })); +export type ListInput = typeof ListInput.Type; + +// Output Schema +export const ListOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Array( + Schema.Struct({ + object: Schema.Literals(["session"]), + id: Schema.String, + user_id: Schema.String, + client_id: Schema.String, + actor: Schema.optional(Schema.NullOr(Schema.Unknown)), + status: Schema.Literals([ + "active", + "revoked", + "ended", + "expired", + "removed", + "abandoned", + "replaced", + "pending", + ]), + last_active_organization_id: Schema.optional(Schema.NullOr(Schema.String)), + last_active_at: Schema.Number, + latest_activity: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.String, + id: Schema.String, + device_type: Schema.optional(Schema.String), + is_mobile: Schema.Boolean, + browser_name: Schema.optional(Schema.String), + browser_version: Schema.optional(Schema.String), + ip_address: Schema.optional(Schema.String), + city: Schema.optional(Schema.String), + country: Schema.optional(Schema.String), + }), + ), + ), + expire_at: Schema.Number, + abandon_at: Schema.Number, + updated_at: Schema.Number, + created_at: Schema.Number, + tasks: Schema.optional( + Schema.NullOr( + Schema.Array( + Schema.Struct({ + key: Schema.String, + }), + ), + ), + ), + }), +); +export type ListOutput = typeof ListOutput.Type; + +// The operation +/** + * List all sessions + * + * Returns a list of sessions matching the provided criteria. + * The sessions are returned sorted by creation date, with the newest sessions appearing first. + * Note: This endpoint does not return all sessions that have ever existed. Old and inactive sessions are periodically cleaned up and will not be included in the results. + * **Deprecation Notice (2024-01-01):** All parameters were initially considered optional, however + * moving forward at least one of `client_id` or `user_id` parameters should be provided. + * + * @param client_id - List sessions for the given client + * @param user_id - List sessions for the given user + * @param status - Filter sessions by the provided status + * @param paginated - Whether to paginate the results. +If true, the results will be paginated. +If false, the results will not be paginated. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + */ +export const list = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInput, + outputSchema: ListOutput, + errors: [BadRequest, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/sessions/refresh.ts b/packages/clerk/src/operations/backend/sessions/refresh.ts new file mode 100644 index 0000000000..cd89a03be0 --- /dev/null +++ b/packages/clerk/src/operations/backend/sessions/refresh.ts @@ -0,0 +1,38 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest } from "../../../errors.ts"; +import { SensitiveString } from "../../../sensitive.ts"; + +// Input Schema +export const RefreshInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + session_id: Schema.String.pipe(T.PathParam()), + expired_token: Schema.String, + refresh_token: SensitiveString, + request_origin: Schema.String, + request_headers: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + format: Schema.optional(Schema.NullOr(Schema.Literals(["token", "cookie"]))), + request_originating_ip: Schema.optional(Schema.NullOr(Schema.String)), +}).pipe(T.Http({ method: "POST", path: "/sessions/{session_id}/refresh" })); +export type RefreshInput = typeof RefreshInput.Type; + +// Output Schema +export const RefreshOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Unknown; +export type RefreshOutput = typeof RefreshOutput.Type; + +// The operation +/** + * Refresh a session + * + * Refreshes a session by creating a new session token. A 401 is returned when there + * are validation errors, which signals the SDKs to fall back to the handshake flow. + * + * @param session_id - The ID of the session + */ +export const refresh = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: RefreshInput, + outputSchema: RefreshOutput, + errors: [BadRequest] as const, +})); diff --git a/packages/clerk/src/operations/backend/sessions/revoke.ts b/packages/clerk/src/operations/backend/sessions/revoke.ts new file mode 100644 index 0000000000..85a83a471f --- /dev/null +++ b/packages/clerk/src/operations/backend/sessions/revoke.ts @@ -0,0 +1,75 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; + +// Input Schema +export const RevokeInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + session_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "POST", path: "/sessions/{session_id}/revoke" })); +export type RevokeInput = typeof RevokeInput.Type; + +// Output Schema +export const RevokeOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["session"]), + id: Schema.String, + user_id: Schema.String, + client_id: Schema.String, + actor: Schema.optional(Schema.NullOr(Schema.Unknown)), + status: Schema.Literals([ + "active", + "revoked", + "ended", + "expired", + "removed", + "abandoned", + "replaced", + "pending", + ]), + last_active_organization_id: Schema.optional(Schema.NullOr(Schema.String)), + last_active_at: Schema.Number, + latest_activity: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.String, + id: Schema.String, + device_type: Schema.optional(Schema.String), + is_mobile: Schema.Boolean, + browser_name: Schema.optional(Schema.String), + browser_version: Schema.optional(Schema.String), + ip_address: Schema.optional(Schema.String), + city: Schema.optional(Schema.String), + country: Schema.optional(Schema.String), + }), + ), + ), + expire_at: Schema.Number, + abandon_at: Schema.Number, + updated_at: Schema.Number, + created_at: Schema.Number, + tasks: Schema.optional( + Schema.NullOr( + Schema.Array( + Schema.Struct({ + key: Schema.String, + }), + ), + ), + ), +}); +export type RevokeOutput = typeof RevokeOutput.Type; + +// The operation +/** + * Revoke a session + * + * Sets the status of a session as "revoked", which is an unauthenticated state. + * In multi-session mode, a revoked session will still be returned along with its client object, however the user will need to sign in again. + * + * @param session_id - The ID of the session + */ +export const revoke = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: RevokeInput, + outputSchema: RevokeOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/signInTokens/create.ts b/packages/clerk/src/operations/backend/signInTokens/create.ts new file mode 100644 index 0000000000..43948c7e96 --- /dev/null +++ b/packages/clerk/src/operations/backend/signInTokens/create.ts @@ -0,0 +1,38 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String, + expires_in_seconds: Schema.optional(Schema.NullOr(Schema.Number)), +}).pipe(T.Http({ method: "POST", path: "/sign_in_tokens" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["sign_in_token"]), + id: Schema.String, + status: Schema.Literals(["pending", "accepted", "revoked"]), + user_id: Schema.String, + token: Schema.optional(Schema.String), + url: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create sign-in token + * + * Creates a new sign-in token and associates it with the given user. + * By default, sign-in tokens expire in 30 days. + * You can optionally supply a different duration in seconds using the `expires_in_seconds` property. + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/signInTokens/index.ts b/packages/clerk/src/operations/backend/signInTokens/index.ts new file mode 100644 index 0000000000..35fd19e8dd --- /dev/null +++ b/packages/clerk/src/operations/backend/signInTokens/index.ts @@ -0,0 +1,2 @@ +export * from "./create.ts"; +export * from "./revoke.ts"; diff --git a/packages/clerk/src/operations/backend/signInTokens/revoke.ts b/packages/clerk/src/operations/backend/signInTokens/revoke.ts new file mode 100644 index 0000000000..dce50f680f --- /dev/null +++ b/packages/clerk/src/operations/backend/signInTokens/revoke.ts @@ -0,0 +1,39 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; + +// Input Schema +export const RevokeInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + sign_in_token_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ method: "POST", path: "/sign_in_tokens/{sign_in_token_id}/revoke" }), +); +export type RevokeInput = typeof RevokeInput.Type; + +// Output Schema +export const RevokeOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["sign_in_token"]), + id: Schema.String, + status: Schema.Literals(["pending", "accepted", "revoked"]), + user_id: Schema.String, + token: Schema.optional(Schema.String), + url: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type RevokeOutput = typeof RevokeOutput.Type; + +// The operation +/** + * Revoke the given sign-in token + * + * Revokes a pending sign-in token + * + * @param sign_in_token_id - The ID of the sign-in token to be revoked + */ +export const revoke = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: RevokeInput, + outputSchema: RevokeOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/signUps/get.ts b/packages/clerk/src/operations/backend/signUps/get.ts new file mode 100644 index 0000000000..b2c2784fa5 --- /dev/null +++ b/packages/clerk/src/operations/backend/signUps/get.ts @@ -0,0 +1,84 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden } from "../../../errors.ts"; + +// Input Schema +export const GetInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "GET", path: "/sign_ups/{id}" })); +export type GetInput = typeof GetInput.Type; + +// Output Schema +export const GetOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["sign_up_attempt"]), + id: Schema.String, + status: Schema.Literals(["missing_requirements", "complete", "abandoned"]), + required_fields: Schema.Array(Schema.String), + optional_fields: Schema.Array(Schema.String), + missing_fields: Schema.Array(Schema.String), + unverified_fields: Schema.Array(Schema.String), + verifications: Schema.Struct({ + email_address: Schema.NullOr( + Schema.Struct({ + next_action: Schema.optional( + Schema.Literals(["needs_prepare", "needs_attempt", ""]), + ), + supported_strategies: Schema.optional(Schema.Array(Schema.String)), + }), + ), + phone_number: Schema.NullOr( + Schema.Struct({ + next_action: Schema.optional( + Schema.Literals(["needs_prepare", "needs_attempt", ""]), + ), + supported_strategies: Schema.optional(Schema.Array(Schema.String)), + }), + ), + web3_wallet: Schema.NullOr( + Schema.Struct({ + next_action: Schema.optional( + Schema.Literals(["needs_prepare", "needs_attempt", ""]), + ), + supported_strategies: Schema.optional(Schema.Array(Schema.String)), + }), + ), + external_account: Schema.NullOr(Schema.Unknown), + }), + username: Schema.NullOr(Schema.String), + email_address: Schema.NullOr(Schema.String), + phone_number: Schema.NullOr(Schema.String), + web3_wallet: Schema.NullOr(Schema.String), + password_enabled: Schema.Boolean, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + custom_action: Schema.Boolean, + external_id: Schema.NullOr(Schema.String), + created_session_id: Schema.NullOr(Schema.String), + created_user_id: Schema.NullOr(Schema.String), + abandon_at: Schema.Number, + legal_accepted_at: Schema.NullOr(Schema.Number), + locale: Schema.optional(Schema.NullOr(Schema.String)), + external_account: Schema.optional(Schema.Unknown), +}); +export type GetOutput = typeof GetOutput.Type; + +// The operation +/** + * Retrieve a sign-up by ID + * + * Retrieve the details of the sign-up with the given ID + * + * @param id - The ID of the sign-up to retrieve + */ +export const get = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInput, + outputSchema: GetOutput, + errors: [Forbidden] as const, +})); diff --git a/packages/clerk/src/operations/backend/signUps/index.ts b/packages/clerk/src/operations/backend/signUps/index.ts new file mode 100644 index 0000000000..c8fb1f0cd2 --- /dev/null +++ b/packages/clerk/src/operations/backend/signUps/index.ts @@ -0,0 +1,2 @@ +export * from "./get.ts"; +export * from "./update.ts"; diff --git a/packages/clerk/src/operations/backend/signUps/update.ts b/packages/clerk/src/operations/backend/signUps/update.ts new file mode 100644 index 0000000000..b089c8a91d --- /dev/null +++ b/packages/clerk/src/operations/backend/signUps/update.ts @@ -0,0 +1,86 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden } from "../../../errors.ts"; + +// Input Schema +export const UpdateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String.pipe(T.PathParam()), + external_id: Schema.optional(Schema.NullOr(Schema.String)), + custom_action: Schema.optional(Schema.NullOr(Schema.Boolean)), +}).pipe(T.Http({ method: "PATCH", path: "/sign_ups/{id}" })); +export type UpdateInput = typeof UpdateInput.Type; + +// Output Schema +export const UpdateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["sign_up_attempt"]), + id: Schema.String, + status: Schema.Literals(["missing_requirements", "complete", "abandoned"]), + required_fields: Schema.Array(Schema.String), + optional_fields: Schema.Array(Schema.String), + missing_fields: Schema.Array(Schema.String), + unverified_fields: Schema.Array(Schema.String), + verifications: Schema.Struct({ + email_address: Schema.NullOr( + Schema.Struct({ + next_action: Schema.optional( + Schema.Literals(["needs_prepare", "needs_attempt", ""]), + ), + supported_strategies: Schema.optional(Schema.Array(Schema.String)), + }), + ), + phone_number: Schema.NullOr( + Schema.Struct({ + next_action: Schema.optional( + Schema.Literals(["needs_prepare", "needs_attempt", ""]), + ), + supported_strategies: Schema.optional(Schema.Array(Schema.String)), + }), + ), + web3_wallet: Schema.NullOr( + Schema.Struct({ + next_action: Schema.optional( + Schema.Literals(["needs_prepare", "needs_attempt", ""]), + ), + supported_strategies: Schema.optional(Schema.Array(Schema.String)), + }), + ), + external_account: Schema.NullOr(Schema.Unknown), + }), + username: Schema.NullOr(Schema.String), + email_address: Schema.NullOr(Schema.String), + phone_number: Schema.NullOr(Schema.String), + web3_wallet: Schema.NullOr(Schema.String), + password_enabled: Schema.Boolean, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + custom_action: Schema.Boolean, + external_id: Schema.NullOr(Schema.String), + created_session_id: Schema.NullOr(Schema.String), + created_user_id: Schema.NullOr(Schema.String), + abandon_at: Schema.Number, + legal_accepted_at: Schema.NullOr(Schema.Number), + locale: Schema.optional(Schema.NullOr(Schema.String)), + external_account: Schema.optional(Schema.Unknown), +}); +export type UpdateOutput = typeof UpdateOutput.Type; + +// The operation +/** + * Update a sign-up + * + * Update the sign-up with the given ID + * + * @param id - The ID of the sign-up to update + */ +export const update = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateInput, + outputSchema: UpdateOutput, + errors: [Forbidden] as const, +})); diff --git a/packages/clerk/src/operations/backend/testingTokens/create.ts b/packages/clerk/src/operations/backend/testingTokens/create.ts new file mode 100644 index 0000000000..302cea6673 --- /dev/null +++ b/packages/clerk/src/operations/backend/testingTokens/create.ts @@ -0,0 +1,28 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({}).pipe( + T.Http({ method: "POST", path: "/testing_tokens" }), +); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["testing_token"]), + token: Schema.String, + expires_at: Schema.Number, +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Retrieve a new testing token + * + * Retrieve a new testing token. + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, +})); diff --git a/packages/clerk/src/operations/backend/testingTokens/index.ts b/packages/clerk/src/operations/backend/testingTokens/index.ts new file mode 100644 index 0000000000..4b1c048b7d --- /dev/null +++ b/packages/clerk/src/operations/backend/testingTokens/index.ts @@ -0,0 +1 @@ +export * from "./create.ts"; diff --git a/packages/clerk/src/operations/backend/users/adjustBillingCreditBalance.ts b/packages/clerk/src/operations/backend/users/adjustBillingCreditBalance.ts new file mode 100644 index 0000000000..a935cc2dde --- /dev/null +++ b/packages/clerk/src/operations/backend/users/adjustBillingCreditBalance.ts @@ -0,0 +1,63 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + Conflict, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const AdjustBillingCreditBalanceInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + amount: Schema.Number, + action: Schema.Literals(["increase", "decrease"]), + currency: Schema.optional(Schema.String), + idempotency_key: Schema.String, + note: Schema.optional(Schema.String), + }).pipe(T.Http({ method: "POST", path: "/users/{user_id}/billing/credits" })); +export type AdjustBillingCreditBalanceInput = + typeof AdjustBillingCreditBalanceInput.Type; + +// Output Schema +export const AdjustBillingCreditBalanceOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.String, + payer_id: Schema.String, + amount: Schema.Number, + currency: Schema.String, + source_type: Schema.String, + source_id: Schema.String, + note: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.String, + }); +export type AdjustBillingCreditBalanceOutput = + typeof AdjustBillingCreditBalanceOutput.Type; + +// The operation +/** + * Adjust a user's credit balance + * + * Increases or decreases the credit balance for the specified user. + * Each adjustment is recorded as a ledger entry. The idempotency_key parameter + * ensures that duplicate requests are safely handled. + * + * @param user_id - The ID of the user whose credit balance to adjust + */ +export const adjustBillingCreditBalance = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: AdjustBillingCreditBalanceInput, + outputSchema: AdjustBillingCreditBalanceOutput, + errors: [ + BadRequest, + Forbidden, + NotFound, + Conflict, + UnprocessableEntity, + ] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/users/ban.ts b/packages/clerk/src/operations/backend/users/ban.ts new file mode 100644 index 0000000000..ec2e357729 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/ban.ts @@ -0,0 +1,255 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { PaymentRequired } from "../../../errors.ts"; + +// Input Schema +export const BanInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "POST", path: "/users/{user_id}/ban" })); +export type BanInput = typeof BanInput.Type; + +// Output Schema +export const BanOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["user"]), + external_id: Schema.NullOr(Schema.String), + primary_email_address_id: Schema.NullOr(Schema.String), + primary_phone_number_id: Schema.NullOr(Schema.String), + primary_web3_wallet_id: Schema.NullOr(Schema.String), + username: Schema.NullOr(Schema.String), + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + locale: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + email_addresses: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + phone_numbers: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + web3_wallets: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["web3_wallet"]), + web3_wallet: Schema.String, + verification: Schema.Unknown, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + passkeys: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["passkey"]), + name: Schema.String, + last_used_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + password_enabled: Schema.Boolean, + two_factor_enabled: Schema.Boolean, + totp_enabled: Schema.Boolean, + backup_code_enabled: Schema.Boolean, + mfa_enabled_at: Schema.NullOr(Schema.Number), + mfa_disabled_at: Schema.NullOr(Schema.Number), + password_last_updated_at: Schema.optional(Schema.NullOr(Schema.Number)), + external_accounts: Schema.Array( + Schema.Struct({ + object: Schema.Literals([ + "external_account", + "facebook_account", + "google_account", + ]), + id: Schema.String, + provider: Schema.String, + identification_id: Schema.String, + provider_user_id: Schema.String, + approved_scopes: Schema.String, + email_address: Schema.String, + email_address_verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + first_name: Schema.String, + last_name: Schema.String, + avatar_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + phone_number: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + saml_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["saml_account"]), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + saml_connection: Schema.optional(Schema.Unknown), + }), + ), + enterprise_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["enterprise_account"]), + protocol: Schema.optional(Schema.Literals(["oauth", "saml"])), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + enterprise_connection_id: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + enterprise_connection: Schema.optional(Schema.Unknown), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + organization_memberships: Schema.optional( + Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + last_sign_in_at: Schema.NullOr(Schema.Number), + banned: Schema.Boolean, + locked: Schema.Boolean, + deprovisioned: Schema.optional(Schema.Boolean), + lockout_expires_in_seconds: Schema.NullOr(Schema.Number), + verification_attempts_remaining: Schema.NullOr(Schema.Number), + updated_at: Schema.Number, + created_at: Schema.Number, + delete_self_enabled: Schema.Boolean, + create_organization_enabled: Schema.Boolean, + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + last_active_at: Schema.NullOr(Schema.Number), + legal_accepted_at: Schema.NullOr(Schema.Number), + bypass_client_trust: Schema.optional(Schema.Boolean), + scim: Schema.optional( + Schema.NullOr( + Schema.Struct({ + directory_id: Schema.String, + directory_enabled: Schema.optional(Schema.Boolean), + external_id: Schema.NullOr(Schema.String), + }), + ), + ), +}); +export type BanOutput = typeof BanOutput.Type; + +// The operation +/** + * Ban a user + * + * Marks the given user as banned, which means that all their sessions are revoked and they are not allowed to sign in again. + * + * @param user_id - The ID of the user to ban + */ +export const ban = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: BanInput, + outputSchema: BanOutput, + errors: [PaymentRequired] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/bulkBan.ts b/packages/clerk/src/operations/backend/users/bulkBan.ts new file mode 100644 index 0000000000..f187e4c599 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/bulkBan.ts @@ -0,0 +1,257 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, PaymentRequired } from "../../../errors.ts"; + +// Input Schema +export const BulkBanInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_ids: Schema.Array(Schema.String), +}).pipe(T.Http({ method: "POST", path: "/users/ban" })); +export type BulkBanInput = typeof BulkBanInput.Type; + +// Output Schema +export const BulkBanOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["user"]), + external_id: Schema.NullOr(Schema.String), + primary_email_address_id: Schema.NullOr(Schema.String), + primary_phone_number_id: Schema.NullOr(Schema.String), + primary_web3_wallet_id: Schema.NullOr(Schema.String), + username: Schema.NullOr(Schema.String), + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + locale: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + email_addresses: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + phone_numbers: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional( + Schema.NullOr(Schema.Array(Schema.String)), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + web3_wallets: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["web3_wallet"]), + web3_wallet: Schema.String, + verification: Schema.Unknown, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + passkeys: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["passkey"]), + name: Schema.String, + last_used_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + password_enabled: Schema.Boolean, + two_factor_enabled: Schema.Boolean, + totp_enabled: Schema.Boolean, + backup_code_enabled: Schema.Boolean, + mfa_enabled_at: Schema.NullOr(Schema.Number), + mfa_disabled_at: Schema.NullOr(Schema.Number), + password_last_updated_at: Schema.optional(Schema.NullOr(Schema.Number)), + external_accounts: Schema.Array( + Schema.Struct({ + object: Schema.Literals([ + "external_account", + "facebook_account", + "google_account", + ]), + id: Schema.String, + provider: Schema.String, + identification_id: Schema.String, + provider_user_id: Schema.String, + approved_scopes: Schema.String, + email_address: Schema.String, + email_address_verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + first_name: Schema.String, + last_name: Schema.String, + avatar_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + phone_number: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + saml_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["saml_account"]), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + saml_connection: Schema.optional(Schema.Unknown), + }), + ), + enterprise_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["enterprise_account"]), + protocol: Schema.optional(Schema.Literals(["oauth", "saml"])), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + enterprise_connection_id: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + enterprise_connection: Schema.optional(Schema.Unknown), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + organization_memberships: Schema.optional( + Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + last_sign_in_at: Schema.NullOr(Schema.Number), + banned: Schema.Boolean, + locked: Schema.Boolean, + deprovisioned: Schema.optional(Schema.Boolean), + lockout_expires_in_seconds: Schema.NullOr(Schema.Number), + verification_attempts_remaining: Schema.NullOr(Schema.Number), + updated_at: Schema.Number, + created_at: Schema.Number, + delete_self_enabled: Schema.Boolean, + create_organization_enabled: Schema.Boolean, + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + last_active_at: Schema.NullOr(Schema.Number), + legal_accepted_at: Schema.NullOr(Schema.Number), + bypass_client_trust: Schema.optional(Schema.Boolean), + scim: Schema.optional( + Schema.NullOr( + Schema.Struct({ + directory_id: Schema.String, + directory_enabled: Schema.optional(Schema.Boolean), + external_id: Schema.NullOr(Schema.String), + }), + ), + ), + }), +); +export type BulkBanOutput = typeof BulkBanOutput.Type; + +// The operation +/** + * Ban multiple users + * + * Marks multiple users as banned, which means that all their sessions are revoked and they are not allowed to sign in again. + */ +export const bulkBan = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: BulkBanInput, + outputSchema: BulkBanOutput, + errors: [BadRequest, PaymentRequired] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/bulkUnban.ts b/packages/clerk/src/operations/backend/users/bulkUnban.ts new file mode 100644 index 0000000000..7022938ff2 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/bulkUnban.ts @@ -0,0 +1,257 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, PaymentRequired } from "../../../errors.ts"; + +// Input Schema +export const BulkUnbanInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_ids: Schema.Array(Schema.String), +}).pipe(T.Http({ method: "POST", path: "/users/unban" })); +export type BulkUnbanInput = typeof BulkUnbanInput.Type; + +// Output Schema +export const BulkUnbanOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["user"]), + external_id: Schema.NullOr(Schema.String), + primary_email_address_id: Schema.NullOr(Schema.String), + primary_phone_number_id: Schema.NullOr(Schema.String), + primary_web3_wallet_id: Schema.NullOr(Schema.String), + username: Schema.NullOr(Schema.String), + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + locale: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + email_addresses: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + phone_numbers: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional( + Schema.NullOr(Schema.Array(Schema.String)), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + web3_wallets: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["web3_wallet"]), + web3_wallet: Schema.String, + verification: Schema.Unknown, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + passkeys: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["passkey"]), + name: Schema.String, + last_used_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + password_enabled: Schema.Boolean, + two_factor_enabled: Schema.Boolean, + totp_enabled: Schema.Boolean, + backup_code_enabled: Schema.Boolean, + mfa_enabled_at: Schema.NullOr(Schema.Number), + mfa_disabled_at: Schema.NullOr(Schema.Number), + password_last_updated_at: Schema.optional(Schema.NullOr(Schema.Number)), + external_accounts: Schema.Array( + Schema.Struct({ + object: Schema.Literals([ + "external_account", + "facebook_account", + "google_account", + ]), + id: Schema.String, + provider: Schema.String, + identification_id: Schema.String, + provider_user_id: Schema.String, + approved_scopes: Schema.String, + email_address: Schema.String, + email_address_verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + first_name: Schema.String, + last_name: Schema.String, + avatar_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + phone_number: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + saml_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["saml_account"]), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + saml_connection: Schema.optional(Schema.Unknown), + }), + ), + enterprise_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["enterprise_account"]), + protocol: Schema.optional(Schema.Literals(["oauth", "saml"])), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + enterprise_connection_id: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + enterprise_connection: Schema.optional(Schema.Unknown), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + organization_memberships: Schema.optional( + Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + last_sign_in_at: Schema.NullOr(Schema.Number), + banned: Schema.Boolean, + locked: Schema.Boolean, + deprovisioned: Schema.optional(Schema.Boolean), + lockout_expires_in_seconds: Schema.NullOr(Schema.Number), + verification_attempts_remaining: Schema.NullOr(Schema.Number), + updated_at: Schema.Number, + created_at: Schema.Number, + delete_self_enabled: Schema.Boolean, + create_organization_enabled: Schema.Boolean, + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + last_active_at: Schema.NullOr(Schema.Number), + legal_accepted_at: Schema.NullOr(Schema.Number), + bypass_client_trust: Schema.optional(Schema.Boolean), + scim: Schema.optional( + Schema.NullOr( + Schema.Struct({ + directory_id: Schema.String, + directory_enabled: Schema.optional(Schema.Boolean), + external_id: Schema.NullOr(Schema.String), + }), + ), + ), + }), +); +export type BulkUnbanOutput = typeof BulkUnbanOutput.Type; + +// The operation +/** + * Unban multiple users + * + * Removes the ban mark from multiple users. + */ +export const bulkUnban = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: BulkUnbanInput, + outputSchema: BulkUnbanOutput, + errors: [BadRequest, PaymentRequired] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/count.ts b/packages/clerk/src/operations/backend/users/count.ts new file mode 100644 index 0000000000..4b86130d99 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/count.ts @@ -0,0 +1,110 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const CountInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + email_address: Schema.optional(Schema.String), + phone_number: Schema.optional(Schema.String), + external_id: Schema.optional(Schema.String), + username: Schema.optional(Schema.String), + web3_wallet: Schema.optional(Schema.String), + user_id: Schema.optional(Schema.String), + organization_id: Schema.optional(Schema.String), + query: Schema.optional(Schema.String), + email_address_query: Schema.optional(Schema.String), + phone_number_query: Schema.optional(Schema.String), + username_query: Schema.optional(Schema.String), + name_query: Schema.optional(Schema.String), + banned: Schema.optional(Schema.Boolean), + last_active_at_before: Schema.optional(Schema.Number), + last_active_at_after: Schema.optional(Schema.Number), + last_active_at_since: Schema.optional(Schema.Number), + created_at_before: Schema.optional(Schema.Number), + created_at_after: Schema.optional(Schema.Number), + last_sign_in_at_before: Schema.optional(Schema.Number), + last_sign_in_at_after: Schema.optional(Schema.Number), + provider: Schema.optional(Schema.String), + provider_user_id: Schema.optional(Schema.String), +}).pipe(T.Http({ method: "GET", path: "/users/count" })); +export type CountInput = typeof CountInput.Type; + +// Output Schema +export const CountOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["total_count"]), + total_count: Schema.Number, +}); +export type CountOutput = typeof CountOutput.Type; + +// The operation +/** + * Count users + * + * Returns a total count of all users that match the given filtering criteria. + * + * @param email_address - Counts users with the specified email addresses. +Accepts up to 100 email addresses. +Any email addresses not found are ignored. + * @param phone_number - Counts users with the specified phone numbers. +Accepts up to 100 phone numbers. +Any phone numbers not found are ignored. + * @param external_id - Counts users with the specified external IDs. +Accepts up to 100 external IDs. +Any external IDs not found are ignored. + * @param username - Counts users with the specified usernames. +Accepts up to 100 usernames. +Any usernames not found are ignored. + * @param web3_wallet - Counts users with the specified web3 wallet addresses. +Accepts up to 100 web3 wallet addresses. +Any web3 wallet addresses not found are ignored. + * @param user_id - Counts users with the user IDs specified. +Accepts up to 100 user IDs. +Any user IDs not found are ignored. + * @param organization_id - Returns users that have memberships to the given organizations. For each organization ID, the `+` and `-` +can be prepended to the ID, which denote whether the respective organization should be included or +excluded from the result set. Accepts up to 100 organization IDs. + * @param query - Counts users that match the given query. +For possible matches, we check the email addresses, phone numbers, usernames, web3 wallets, user IDs, first and last names. +The query value doesn't need to match the exact value you are looking for, it is capable of partial matches as well. + * @param email_address_query - Counts users with emails that match the given query, via case-insensitive partial match. +For example, `email_address_query=ello` will match a user with the email `HELLO@example.com`, +and will be included in the resulting count. + * @param phone_number_query - Counts users with phone numbers that match the given query, via case-insensitive partial match. +For example, `phone_number_query=555` will match a user with the phone number `+1555xxxxxxx`, +and will be included in the resulting count. + * @param username_query - Counts users with usernames that match the given query, via case-insensitive partial match. +For example, `username_query=CoolUser` will match a user with the username `SomeCoolUser`, +and will be included in the resulting count. + * @param name_query - Returns users with names that match the given query, via case-insensitive partial match. + * @param banned - Counts users which are either banned (`banned=true`) or not banned (`banned=false`). + * @param last_active_at_before - Returns users whose last session activity was before the given date (with millisecond precision). +Example: use 1700690400000 to retrieve users whose last session activity was before 2023-11-23. + * @param last_active_at_after - Returns users whose last session activity was after the given date (with millisecond precision). +Example: use 1700690400000 to retrieve users whose last session activity was after 2023-11-23. + * @param last_active_at_since - Returns users that had session activity since the given date. +Example: use 1700690400000 to retrieve users that had session activity from 2023-11-23 until the current day. +Deprecated in favor of `last_active_at_after`. + * @param created_at_before - Returns users who have been created before the given date (with millisecond precision). +Example: use 1730160000000 to retrieve users who have been created before 2024-10-29. + * @param created_at_after - Returns users who have been created after the given date (with millisecond precision). +Example: use 1730160000000 to retrieve users who have been created after 2024-10-29. + * @param last_sign_in_at_before - Counts users whose last sign-in was before the given date (with millisecond precision). +Example: use 1700690400000 to count users whose last sign-in was before 2023-11-23. + * @param last_sign_in_at_after - Counts users whose last sign-in was after the given date (with millisecond precision). +Example: use 1700690400000 to count users whose last sign-in was after 2023-11-23. + * @param provider - Counts users with external accounts for the specified OAuth provider. +Must be used in combination with the `provider_user_id` parameter. +For example, use `provider=oauth_google&provider_user_id=12345` to count users with Google provider user ID 12345. +Accepts up to 100 providers. + * @param provider_user_id - Counts users with the specified provider user IDs for a specific provider. +Must be used in combination with the `provider` parameter. +For example, use `provider=oauth_google&provider_user_id=12345` to count users with Google provider user ID 12345. +Accepts up to 100 provider user IDs. +Any provider user IDs not found are ignored. + */ +export const count = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CountInput, + outputSchema: CountOutput, + errors: [UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/create.ts b/packages/clerk/src/operations/backend/users/create.ts new file mode 100644 index 0000000000..ee430e491d --- /dev/null +++ b/packages/clerk/src/operations/backend/users/create.ts @@ -0,0 +1,300 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + Forbidden, + UnprocessableEntity, +} from "../../../errors.ts"; +import { SensitiveNullableString } from "../../../sensitive.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + external_id: Schema.optional(Schema.NullOr(Schema.String)), + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + locale: Schema.optional(Schema.NullOr(Schema.String)), + email_address: Schema.optional(Schema.Array(Schema.String)), + phone_number: Schema.optional(Schema.Array(Schema.String)), + web3_wallet: Schema.optional(Schema.Array(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + password: Schema.optional(SensitiveNullableString), + password_digest: Schema.optional(SensitiveNullableString), + password_hasher: Schema.optional(Schema.String), + skip_password_checks: Schema.optional(Schema.NullOr(Schema.Boolean)), + skip_password_requirement: Schema.optional(Schema.NullOr(Schema.Boolean)), + totp_secret: Schema.optional(SensitiveNullableString), + backup_codes: Schema.optional(Schema.Array(Schema.String)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + delete_self_enabled: Schema.optional(Schema.NullOr(Schema.Boolean)), + legal_accepted_at: Schema.optional(Schema.NullOr(Schema.String)), + skip_legal_checks: Schema.optional(Schema.NullOr(Schema.Boolean)), + skip_user_requirement: Schema.optional(Schema.NullOr(Schema.Boolean)), + create_organization_enabled: Schema.optional(Schema.NullOr(Schema.Boolean)), + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.optional(Schema.NullOr(Schema.String)), + bypass_client_trust: Schema.optional(Schema.NullOr(Schema.Boolean)), + banned: Schema.optional(Schema.NullOr(Schema.Boolean)), + locked: Schema.optional(Schema.NullOr(Schema.Boolean)), +}).pipe(T.Http({ method: "POST", path: "/users" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["user"]), + external_id: Schema.NullOr(Schema.String), + primary_email_address_id: Schema.NullOr(Schema.String), + primary_phone_number_id: Schema.NullOr(Schema.String), + primary_web3_wallet_id: Schema.NullOr(Schema.String), + username: Schema.NullOr(Schema.String), + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + locale: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + email_addresses: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + phone_numbers: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + web3_wallets: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["web3_wallet"]), + web3_wallet: Schema.String, + verification: Schema.Unknown, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + passkeys: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["passkey"]), + name: Schema.String, + last_used_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + password_enabled: Schema.Boolean, + two_factor_enabled: Schema.Boolean, + totp_enabled: Schema.Boolean, + backup_code_enabled: Schema.Boolean, + mfa_enabled_at: Schema.NullOr(Schema.Number), + mfa_disabled_at: Schema.NullOr(Schema.Number), + password_last_updated_at: Schema.optional(Schema.NullOr(Schema.Number)), + external_accounts: Schema.Array( + Schema.Struct({ + object: Schema.Literals([ + "external_account", + "facebook_account", + "google_account", + ]), + id: Schema.String, + provider: Schema.String, + identification_id: Schema.String, + provider_user_id: Schema.String, + approved_scopes: Schema.String, + email_address: Schema.String, + email_address_verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + first_name: Schema.String, + last_name: Schema.String, + avatar_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + phone_number: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + saml_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["saml_account"]), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + saml_connection: Schema.optional(Schema.Unknown), + }), + ), + enterprise_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["enterprise_account"]), + protocol: Schema.optional(Schema.Literals(["oauth", "saml"])), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + enterprise_connection_id: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + enterprise_connection: Schema.optional(Schema.Unknown), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + organization_memberships: Schema.optional( + Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + last_sign_in_at: Schema.NullOr(Schema.Number), + banned: Schema.Boolean, + locked: Schema.Boolean, + deprovisioned: Schema.optional(Schema.Boolean), + lockout_expires_in_seconds: Schema.NullOr(Schema.Number), + verification_attempts_remaining: Schema.NullOr(Schema.Number), + updated_at: Schema.Number, + created_at: Schema.Number, + delete_self_enabled: Schema.Boolean, + create_organization_enabled: Schema.Boolean, + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + last_active_at: Schema.NullOr(Schema.Number), + legal_accepted_at: Schema.NullOr(Schema.Number), + bypass_client_trust: Schema.optional(Schema.Boolean), + scim: Schema.optional( + Schema.NullOr( + Schema.Struct({ + directory_id: Schema.String, + directory_enabled: Schema.optional(Schema.Boolean), + external_id: Schema.NullOr(Schema.String), + }), + ), + ), +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create a new user + * + * Creates a new user. Your user management settings determine how you should setup your user model. + * Any email address and phone number created using this method will be marked as verified. + * Note: If you are performing a migration, check out our guide on [zero downtime migrations](https://clerk.com/docs/deployments/migrate-overview). + * The following rate limit rules apply to this endpoint: 1000 requests per 10 seconds for production instances and 100 requests per 10 seconds for development instances + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [ + BadRequest, + PaymentRequired, + Forbidden, + UnprocessableEntity, + ] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/delete.ts b/packages/clerk/src/operations/backend/users/delete.ts new file mode 100644 index 0000000000..63bf84fdcd --- /dev/null +++ b/packages/clerk/src/operations/backend/users/delete.ts @@ -0,0 +1,35 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "DELETE", path: "/users/{user_id}" })); +export type DeleteInput = typeof DeleteInput.Type; + +// Output Schema +export const DeleteOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), +}); +export type DeleteOutput = typeof DeleteOutput.Type; + +// The operation +/** + * Delete a user + * + * Delete the specified user + * + * @param user_id - The ID of the user to delete + */ +const delete_ = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteInput, + outputSchema: DeleteOutput, + errors: [BadRequest, NotFound] as const, +})); +export { delete_ as delete }; diff --git a/packages/clerk/src/operations/backend/users/deleteBackupCodes.ts b/packages/clerk/src/operations/backend/users/deleteBackupCodes.ts new file mode 100644 index 0000000000..874675ebd4 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/deleteBackupCodes.ts @@ -0,0 +1,33 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteBackupCodesInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + { + user_id: Schema.String.pipe(T.PathParam()), + }, +).pipe(T.Http({ method: "DELETE", path: "/users/{user_id}/backup_code" })); +export type DeleteBackupCodesInput = typeof DeleteBackupCodesInput.Type; + +// Output Schema +export const DeleteBackupCodesOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.optional(Schema.String), + }); +export type DeleteBackupCodesOutput = typeof DeleteBackupCodesOutput.Type; + +// The operation +/** + * Disable all user's Backup codes + * + * Disable all of a user's backup codes. + * + * @param user_id - The ID of the user whose backup codes are to be deleted. + */ +export const deleteBackupCodes = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteBackupCodesInput, + outputSchema: DeleteBackupCodesOutput, + errors: [NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/deleteExternalAccount.ts b/packages/clerk/src/operations/backend/users/deleteExternalAccount.ts new file mode 100644 index 0000000000..7013fa982d --- /dev/null +++ b/packages/clerk/src/operations/backend/users/deleteExternalAccount.ts @@ -0,0 +1,46 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteExternalAccountInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + external_account_id: Schema.String.pipe(T.PathParam()), + }).pipe( + T.Http({ + method: "DELETE", + path: "/users/{user_id}/external_accounts/{external_account_id}", + }), + ); +export type DeleteExternalAccountInput = typeof DeleteExternalAccountInput.Type; + +// Output Schema +export const DeleteExternalAccountOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), + }); +export type DeleteExternalAccountOutput = + typeof DeleteExternalAccountOutput.Type; + +// The operation +/** + * Delete External Account + * + * Delete an external account by ID. + * + * @param user_id - The ID of the user's external account + * @param external_account_id - The ID of the external account to delete + */ +export const deleteExternalAccount = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: DeleteExternalAccountInput, + outputSchema: DeleteExternalAccountOutput, + errors: [BadRequest, Forbidden, NotFound] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/users/deletePasskey.ts b/packages/clerk/src/operations/backend/users/deletePasskey.ts new file mode 100644 index 0000000000..e4e1ed1d39 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/deletePasskey.ts @@ -0,0 +1,41 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeletePasskeyInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + passkey_identification_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "DELETE", + path: "/users/{user_id}/passkeys/{passkey_identification_id}", + }), +); +export type DeletePasskeyInput = typeof DeletePasskeyInput.Type; + +// Output Schema +export const DeletePasskeyOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), +}); +export type DeletePasskeyOutput = typeof DeletePasskeyOutput.Type; + +// The operation +/** + * Delete a user passkey + * + * Delete the passkey identification for a given user and notify them through email. + * + * @param user_id - The ID of the user that owns the passkey identity + * @param passkey_identification_id - The ID of the passkey identity to be deleted + */ +export const deletePasskey = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeletePasskeyInput, + outputSchema: DeletePasskeyOutput, + errors: [Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/deleteProfileImage.ts b/packages/clerk/src/operations/backend/users/deleteProfileImage.ts new file mode 100644 index 0000000000..aafcdfa632 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/deleteProfileImage.ts @@ -0,0 +1,259 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteProfileImageInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + }).pipe(T.Http({ method: "DELETE", path: "/users/{user_id}/profile_image" })); +export type DeleteProfileImageInput = typeof DeleteProfileImageInput.Type; + +// Output Schema +export const DeleteProfileImageOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["user"]), + external_id: Schema.NullOr(Schema.String), + primary_email_address_id: Schema.NullOr(Schema.String), + primary_phone_number_id: Schema.NullOr(Schema.String), + primary_web3_wallet_id: Schema.NullOr(Schema.String), + username: Schema.NullOr(Schema.String), + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + locale: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + email_addresses: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + phone_numbers: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional( + Schema.NullOr(Schema.Array(Schema.String)), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + web3_wallets: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["web3_wallet"]), + web3_wallet: Schema.String, + verification: Schema.Unknown, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + passkeys: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["passkey"]), + name: Schema.String, + last_used_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + password_enabled: Schema.Boolean, + two_factor_enabled: Schema.Boolean, + totp_enabled: Schema.Boolean, + backup_code_enabled: Schema.Boolean, + mfa_enabled_at: Schema.NullOr(Schema.Number), + mfa_disabled_at: Schema.NullOr(Schema.Number), + password_last_updated_at: Schema.optional(Schema.NullOr(Schema.Number)), + external_accounts: Schema.Array( + Schema.Struct({ + object: Schema.Literals([ + "external_account", + "facebook_account", + "google_account", + ]), + id: Schema.String, + provider: Schema.String, + identification_id: Schema.String, + provider_user_id: Schema.String, + approved_scopes: Schema.String, + email_address: Schema.String, + email_address_verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + first_name: Schema.String, + last_name: Schema.String, + avatar_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + phone_number: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + saml_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["saml_account"]), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + saml_connection: Schema.optional(Schema.Unknown), + }), + ), + enterprise_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["enterprise_account"]), + protocol: Schema.optional(Schema.Literals(["oauth", "saml"])), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + enterprise_connection_id: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + enterprise_connection: Schema.optional(Schema.Unknown), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + organization_memberships: Schema.optional( + Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + last_sign_in_at: Schema.NullOr(Schema.Number), + banned: Schema.Boolean, + locked: Schema.Boolean, + deprovisioned: Schema.optional(Schema.Boolean), + lockout_expires_in_seconds: Schema.NullOr(Schema.Number), + verification_attempts_remaining: Schema.NullOr(Schema.Number), + updated_at: Schema.Number, + created_at: Schema.Number, + delete_self_enabled: Schema.Boolean, + create_organization_enabled: Schema.Boolean, + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + last_active_at: Schema.NullOr(Schema.Number), + legal_accepted_at: Schema.NullOr(Schema.Number), + bypass_client_trust: Schema.optional(Schema.Boolean), + scim: Schema.optional( + Schema.NullOr( + Schema.Struct({ + directory_id: Schema.String, + directory_enabled: Schema.optional(Schema.Boolean), + external_id: Schema.NullOr(Schema.String), + }), + ), + ), + }); +export type DeleteProfileImageOutput = typeof DeleteProfileImageOutput.Type; + +// The operation +/** + * Delete user profile image + * + * Delete a user's profile image + * + * @param user_id - The ID of the user to delete the profile image for + */ +export const deleteProfileImage = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteProfileImageInput, + outputSchema: DeleteProfileImageOutput, + errors: [NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/deleteTOTP.ts b/packages/clerk/src/operations/backend/users/deleteTOTP.ts new file mode 100644 index 0000000000..4dfefa4b97 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/deleteTOTP.ts @@ -0,0 +1,30 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteTOTPInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "DELETE", path: "/users/{user_id}/totp" })); +export type DeleteTOTPInput = typeof DeleteTOTPInput.Type; + +// Output Schema +export const DeleteTOTPOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.optional(Schema.String), +}); +export type DeleteTOTPOutput = typeof DeleteTOTPOutput.Type; + +// The operation +/** + * Delete all the user's TOTPs + * + * Deletes all of the user's TOTPs. + * + * @param user_id - The ID of the user whose TOTPs are to be deleted + */ +export const deleteTOTP = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteTOTPInput, + outputSchema: DeleteTOTPOutput, + errors: [NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/deleteWeb3Wallet.ts b/packages/clerk/src/operations/backend/users/deleteWeb3Wallet.ts new file mode 100644 index 0000000000..40688ff127 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/deleteWeb3Wallet.ts @@ -0,0 +1,43 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteWeb3WalletInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + web3_wallet_identification_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "DELETE", + path: "/users/{user_id}/web3_wallets/{web3_wallet_identification_id}", + }), +); +export type DeleteWeb3WalletInput = typeof DeleteWeb3WalletInput.Type; + +// Output Schema +export const DeleteWeb3WalletOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + { + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), + }, +); +export type DeleteWeb3WalletOutput = typeof DeleteWeb3WalletOutput.Type; + +// The operation +/** + * Delete a user web3 wallet + * + * Delete the web3 wallet identification for a given user. + * + * @param user_id - The ID of the user that owns the web3 wallet + * @param web3_wallet_identification_id - The ID of the web3 wallet identity to be deleted + */ +export const deleteWeb3Wallet = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteWeb3WalletInput, + outputSchema: DeleteWeb3WalletOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/disableMfa.ts b/packages/clerk/src/operations/backend/users/disableMfa.ts new file mode 100644 index 0000000000..a79ac9e078 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/disableMfa.ts @@ -0,0 +1,30 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { NotFound } from "../../../errors.ts"; + +// Input Schema +export const DisableMfaInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "DELETE", path: "/users/{user_id}/mfa" })); +export type DisableMfaInput = typeof DisableMfaInput.Type; + +// Output Schema +export const DisableMfaOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.optional(Schema.String), +}); +export type DisableMfaOutput = typeof DisableMfaOutput.Type; + +// The operation +/** + * Disable a user's MFA methods + * + * Disable all of a user's MFA methods (e.g. OTP sent via SMS, TOTP on their authenticator app) at once. + * + * @param user_id - The ID of the user whose MFA methods are to be disabled + */ +export const disableMfa = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DisableMfaInput, + outputSchema: DisableMfaOutput, + errors: [NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/get.ts b/packages/clerk/src/operations/backend/users/get.ts new file mode 100644 index 0000000000..0732fd8079 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/get.ts @@ -0,0 +1,255 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "GET", path: "/users/{user_id}" })); +export type GetInput = typeof GetInput.Type; + +// Output Schema +export const GetOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["user"]), + external_id: Schema.NullOr(Schema.String), + primary_email_address_id: Schema.NullOr(Schema.String), + primary_phone_number_id: Schema.NullOr(Schema.String), + primary_web3_wallet_id: Schema.NullOr(Schema.String), + username: Schema.NullOr(Schema.String), + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + locale: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + email_addresses: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + phone_numbers: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + web3_wallets: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["web3_wallet"]), + web3_wallet: Schema.String, + verification: Schema.Unknown, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + passkeys: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["passkey"]), + name: Schema.String, + last_used_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + password_enabled: Schema.Boolean, + two_factor_enabled: Schema.Boolean, + totp_enabled: Schema.Boolean, + backup_code_enabled: Schema.Boolean, + mfa_enabled_at: Schema.NullOr(Schema.Number), + mfa_disabled_at: Schema.NullOr(Schema.Number), + password_last_updated_at: Schema.optional(Schema.NullOr(Schema.Number)), + external_accounts: Schema.Array( + Schema.Struct({ + object: Schema.Literals([ + "external_account", + "facebook_account", + "google_account", + ]), + id: Schema.String, + provider: Schema.String, + identification_id: Schema.String, + provider_user_id: Schema.String, + approved_scopes: Schema.String, + email_address: Schema.String, + email_address_verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + first_name: Schema.String, + last_name: Schema.String, + avatar_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + phone_number: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + saml_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["saml_account"]), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + saml_connection: Schema.optional(Schema.Unknown), + }), + ), + enterprise_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["enterprise_account"]), + protocol: Schema.optional(Schema.Literals(["oauth", "saml"])), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + enterprise_connection_id: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + enterprise_connection: Schema.optional(Schema.Unknown), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + organization_memberships: Schema.optional( + Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + last_sign_in_at: Schema.NullOr(Schema.Number), + banned: Schema.Boolean, + locked: Schema.Boolean, + deprovisioned: Schema.optional(Schema.Boolean), + lockout_expires_in_seconds: Schema.NullOr(Schema.Number), + verification_attempts_remaining: Schema.NullOr(Schema.Number), + updated_at: Schema.Number, + created_at: Schema.Number, + delete_self_enabled: Schema.Boolean, + create_organization_enabled: Schema.Boolean, + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + last_active_at: Schema.NullOr(Schema.Number), + legal_accepted_at: Schema.NullOr(Schema.Number), + bypass_client_trust: Schema.optional(Schema.Boolean), + scim: Schema.optional( + Schema.NullOr( + Schema.Struct({ + directory_id: Schema.String, + directory_enabled: Schema.optional(Schema.Boolean), + external_id: Schema.NullOr(Schema.String), + }), + ), + ), +}); +export type GetOutput = typeof GetOutput.Type; + +// The operation +/** + * Retrieve a user + * + * Retrieve the details of a user + * + * @param user_id - The ID of the user to retrieve + */ +export const get = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInput, + outputSchema: GetOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/getBillingCreditBalance.ts b/packages/clerk/src/operations/backend/users/getBillingCreditBalance.ts new file mode 100644 index 0000000000..3554d53a1e --- /dev/null +++ b/packages/clerk/src/operations/backend/users/getBillingCreditBalance.ts @@ -0,0 +1,50 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const GetBillingCreditBalanceInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + }).pipe(T.Http({ method: "GET", path: "/users/{user_id}/billing/credits" })); +export type GetBillingCreditBalanceInput = + typeof GetBillingCreditBalanceInput.Type; + +// Output Schema +export const GetBillingCreditBalanceOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + balance: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + }); +export type GetBillingCreditBalanceOutput = + typeof GetBillingCreditBalanceOutput.Type; + +// The operation +/** + * Retrieve a user's credit balance + * + * Retrieves the current credit balance for the specified user. + * Credits can be applied during checkout to reduce the charge or automatically applied to upcoming recurring charges + * + * @param user_id - The ID of the user whose credit balance to retrieve + */ +export const getBillingCreditBalance = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: GetBillingCreditBalanceInput, + outputSchema: GetBillingCreditBalanceOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/users/getBillingSubscription.ts b/packages/clerk/src/operations/backend/users/getBillingSubscription.ts new file mode 100644 index 0000000000..025ba6afa3 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/getBillingSubscription.ts @@ -0,0 +1,428 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const GetBillingSubscriptionInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + }).pipe( + T.Http({ method: "GET", path: "/users/{user_id}/billing/subscription" }), + ); +export type GetBillingSubscriptionInput = + typeof GetBillingSubscriptionInput.Type; + +// Output Schema +export const GetBillingSubscriptionOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["commerce_subscription"]), + id: Schema.String, + instance_id: Schema.String, + status: Schema.Literals([ + "active", + "past_due", + "canceled", + "ended", + "abandoned", + "incomplete", + ]), + payer_id: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + active_at: Schema.NullOr(Schema.Number), + past_due_at: Schema.NullOr(Schema.Number), + subscription_items: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["commerce_subscription_item"]), + id: Schema.String, + instance_id: Schema.String, + status: Schema.Literals([ + "active", + "canceled", + "expired", + "ended", + "past_due", + "upcoming", + "incomplete", + "abandoned", + ]), + credit: Schema.optional( + Schema.Struct({ + amount: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + ), + cycle_remaining_percent: Schema.optional(Schema.Number), + }), + ), + credits: Schema.optional( + Schema.NullOr( + Schema.Struct({ + proration: Schema.NullOr( + Schema.Struct({ + amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + cycle_days_remaining: Schema.Number, + cycle_days_total: Schema.Number, + cycle_remaining_percent: Schema.Number, + }), + ), + payer: Schema.NullOr( + Schema.Struct({ + remaining_balance: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + applied_amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + ), + plan_id: Schema.NullOr(Schema.String), + price_id: Schema.optional(Schema.String), + plan: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["commerce_plan"]), + id: Schema.String, + name: Schema.String, + fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + annual_monthly_fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + annual_fee: Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + description: Schema.NullOr(Schema.String), + product_id: Schema.String, + is_default: Schema.Boolean, + is_recurring: Schema.Boolean, + publicly_visible: Schema.Boolean, + has_base_fee: Schema.Boolean, + for_payer_type: Schema.String, + slug: Schema.String, + avatar_url: Schema.NullOr(Schema.String), + features: Schema.optional( + Schema.Array( + Schema.Struct({ + object: Schema.Literals(["feature"]), + id: Schema.String, + name: Schema.String, + description: Schema.NullOr(Schema.String), + slug: Schema.String, + avatar_url: Schema.NullOr(Schema.String), + }), + ), + ), + free_trial_enabled: Schema.Boolean, + free_trial_days: Schema.NullOr(Schema.Number), + unit_prices: Schema.optional( + Schema.Array( + Schema.Struct({ + name: Schema.String, + block_size: Schema.Number, + tiers: Schema.Array( + Schema.Struct({ + starts_at_block: Schema.Number, + ends_after_block: Schema.optional( + Schema.NullOr(Schema.Number), + ), + fee_per_block: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + }), + ), + ), + }), + ), + ), + plan_period: Schema.Literals(["month", "annual"]), + payment_method: Schema.optional( + Schema.Struct({ + object: Schema.Literals(["commerce_payment_method"]), + id: Schema.String, + payer_id: Schema.String, + payment_type: Schema.Literals(["card", "link"]), + is_default: Schema.optional(Schema.Boolean), + gateway: Schema.String, + gateway_external_id: Schema.String, + gateway_external_account_id: Schema.NullOr(Schema.String), + last4: Schema.NullOr(Schema.String), + status: Schema.Literals(["active", "disconnected"]), + wallet_type: Schema.optional(Schema.NullOr(Schema.String)), + card_type: Schema.NullOr(Schema.String), + expiry_year: Schema.optional(Schema.NullOr(Schema.Number)), + expiry_month: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + is_removable: Schema.optional(Schema.Boolean), + }), + ), + lifetime_paid: Schema.optional( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + next_payment: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.optional( + Schema.NullOr( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + ), + date: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + ), + payer_id: Schema.String, + payer: Schema.optional( + Schema.Struct({ + object: Schema.Literals(["commerce_payer"]), + id: Schema.String, + instance_id: Schema.String, + user_id: Schema.optional(Schema.NullOr(Schema.String)), + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + email: Schema.optional(Schema.NullOr(Schema.String)), + organization_id: Schema.optional(Schema.NullOr(Schema.String)), + organization_name: Schema.optional(Schema.NullOr(Schema.String)), + image_url: Schema.optional(Schema.String), + credits_balance: Schema.optional( + Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + ), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + }), + ), + is_free_trial: Schema.Boolean, + period_start: Schema.Number, + period_end: Schema.NullOr(Schema.Number), + proration_date: Schema.optional(Schema.String), + canceled_at: Schema.NullOr(Schema.Number), + past_due_at: Schema.NullOr(Schema.Number), + ended_at: Schema.NullOr(Schema.Number), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + seats: Schema.optional( + Schema.NullOr( + Schema.Struct({ + quantity: Schema.NullOr(Schema.Number), + tiers: Schema.optional( + Schema.Array( + Schema.Struct({ + quantity: Schema.optional(Schema.NullOr(Schema.Number)), + fee_per_block: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + ), + }), + ), + ), + totals: Schema.optional( + Schema.NullOr( + Schema.Struct({ + subtotal: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + base_fee: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + tax_total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + grand_total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + per_unit_totals: Schema.optional( + Schema.Array( + Schema.Struct({ + name: Schema.String, + block_size: Schema.Number, + tiers: Schema.Array( + Schema.Struct({ + quantity: Schema.optional(Schema.NullOr(Schema.Number)), + fee_per_block: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + }), + ), + ), + credits: Schema.optional( + Schema.NullOr( + Schema.Struct({ + proration: Schema.NullOr( + Schema.Struct({ + amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + cycle_days_remaining: Schema.Number, + cycle_days_total: Schema.Number, + cycle_remaining_percent: Schema.Number, + }), + ), + payer: Schema.NullOr( + Schema.Struct({ + remaining_balance: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + applied_amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + total: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + ), + }), + ), + ), + }), + ), + next_payment: Schema.optional( + Schema.Struct({ + date: Schema.Number, + amount: Schema.Struct({ + amount: Schema.Number, + amount_formatted: Schema.String, + currency: Schema.String, + currency_symbol: Schema.String, + }), + }), + ), + eligible_for_free_trial: Schema.optional(Schema.Boolean), + }); +export type GetBillingSubscriptionOutput = + typeof GetBillingSubscriptionOutput.Type; + +// The operation +/** + * Retrieve a user's billing subscription + * + * Retrieves the billing subscription for the specified user. + * This includes subscription details, active plans, billing information, and payment status. + * The subscription contains subscription items which represent the individual plans the user is subscribed to. + * + * @param user_id - The ID of the user whose subscription to retrieve + */ +export const getBillingSubscription = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: GetBillingSubscriptionInput, + outputSchema: GetBillingSubscriptionOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/users/getInstanceOrganizationMemberships.ts b/packages/clerk/src/operations/backend/users/getInstanceOrganizationMemberships.ts new file mode 100644 index 0000000000..06900e1c1f --- /dev/null +++ b/packages/clerk/src/operations/backend/users/getInstanceOrganizationMemberships.ts @@ -0,0 +1,96 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const GetInstanceOrganizationMembershipsInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + order_by: Schema.optional(Schema.String), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), + }).pipe(T.Http({ method: "GET", path: "/organization_memberships" })); +export type GetInstanceOrganizationMembershipsInput = + typeof GetInstanceOrganizationMembershipsInput.Type; + +// Output Schema +export const GetInstanceOrganizationMembershipsOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + total_count: Schema.Number, + }); +export type GetInstanceOrganizationMembershipsOutput = + typeof GetInstanceOrganizationMembershipsOutput.Type; + +// The operation +/** + * Get a list of all organization memberships within an instance. + * + * Retrieves all organization user memberships for the given instance. + * + * @param order_by - Sorts organizations memberships by phone_number, email_address, created_at, first_name, last_name or username. +By prepending one of those values with + or -, +we can choose to sort in ascending (ASC) or descending (DESC) order. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + */ +export const getInstanceOrganizationMemberships = + /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInstanceOrganizationMembershipsInput, + outputSchema: GetInstanceOrganizationMembershipsOutput, + errors: [BadRequest, UnprocessableEntity] as const, + })); diff --git a/packages/clerk/src/operations/backend/users/getOAuthAccessToken.ts b/packages/clerk/src/operations/backend/users/getOAuthAccessToken.ts new file mode 100644 index 0000000000..24fc5e6ad9 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/getOAuthAccessToken.ts @@ -0,0 +1,64 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound, UnprocessableEntity } from "../../../errors.ts"; +import { SensitiveOutputString } from "../../../sensitive.ts"; + +// Input Schema +export const GetOAuthAccessTokenInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + provider: Schema.String.pipe(T.PathParam()), + paginated: Schema.optional(Schema.Boolean), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), + }).pipe( + T.Http({ + method: "GET", + path: "/users/{user_id}/oauth_access_tokens/{provider}", + }), + ); +export type GetOAuthAccessTokenInput = typeof GetOAuthAccessTokenInput.Type; + +// Output Schema +export const GetOAuthAccessTokenOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Array( + Schema.Struct({ + object: Schema.Literals(["oauth_access_token"]), + external_account_id: Schema.String, + provider_user_id: Schema.String, + token: Schema.String, + expires_at: Schema.NullOr(Schema.Number), + provider: Schema.String, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.NullOr(Schema.String), + scopes: Schema.optional(Schema.Array(Schema.String)), + id_token: Schema.optional(Schema.String), + token_secret: Schema.optional(SensitiveOutputString), + }), + ); +export type GetOAuthAccessTokenOutput = typeof GetOAuthAccessTokenOutput.Type; + +// The operation +/** + * Retrieve the OAuth access token of a user + * + * Fetch the corresponding OAuth access token for a user that has previously authenticated with a particular OAuth provider. + * For OAuth 2.0, if the access token has expired and we have a corresponding refresh token, the access token will be refreshed transparently the new one will be returned. + * + * @param user_id - The ID of the user for which to retrieve the OAuth access token + * @param provider - The ID of the OAuth provider (e.g. `oauth_google`) + * @param paginated - Whether to paginate the results. +If true, the results will be paginated. +If false, the results will not be paginated. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + */ +export const getOAuthAccessToken = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetOAuthAccessTokenInput, + outputSchema: GetOAuthAccessTokenOutput, + errors: [BadRequest, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/getOrganizationInvitations.ts b/packages/clerk/src/operations/backend/users/getOrganizationInvitations.ts new file mode 100644 index 0000000000..60a0b0554b --- /dev/null +++ b/packages/clerk/src/operations/backend/users/getOrganizationInvitations.ts @@ -0,0 +1,95 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetOrganizationInvitationsInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), + status: Schema.optional( + Schema.Literals(["pending", "accepted", "revoked", "expired"]), + ), + }).pipe( + T.Http({ + method: "GET", + path: "/users/{user_id}/organization_invitations", + }), + ); +export type GetOrganizationInvitationsInput = + typeof GetOrganizationInvitationsInput.Type; + +// Output Schema +export const GetOrganizationInvitationsOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.optional(Schema.Literals(["organization_invitation"])), + id: Schema.optional(Schema.String), + email_address: Schema.optional(Schema.String), + role: Schema.optional(Schema.String), + role_name: Schema.optional(Schema.String), + organization_id: Schema.optional(Schema.String), + inviter_id: Schema.optional(Schema.NullOr(Schema.String)), + public_inviter_data: Schema.optional( + Schema.NullOr( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.String, + }), + ), + ), + status: Schema.optional(Schema.String), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + url: Schema.optional(Schema.NullOr(Schema.String)), + expires_at: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.optional(Schema.Number), + updated_at: Schema.optional(Schema.Number), + public_organization_data: Schema.optional( + Schema.Struct({ + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + }), + ), + }), + ), + total_count: Schema.Number, + }); +export type GetOrganizationInvitationsOutput = + typeof GetOrganizationInvitationsOutput.Type; + +// The operation +/** + * Retrieve all invitations for a user + * + * Retrieve a paginated list of the user's organization invitations + * + * @param user_id - The ID of the user whose organization invitations we want to retrieve + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + * @param status - Filter organization invitations based on their status + */ +export const getOrganizationInvitations = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: GetOrganizationInvitationsInput, + outputSchema: GetOrganizationInvitationsOutput, + errors: [BadRequest, Forbidden, NotFound] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/users/getOrganizationMemberships.ts b/packages/clerk/src/operations/backend/users/getOrganizationMemberships.ts new file mode 100644 index 0000000000..2a376e50d2 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/getOrganizationMemberships.ts @@ -0,0 +1,100 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden } from "../../../errors.ts"; + +// Input Schema +export const GetOrganizationMembershipsInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), + }).pipe( + T.Http({ + method: "GET", + path: "/users/{user_id}/organization_memberships", + }), + ); +export type GetOrganizationMembershipsInput = + typeof GetOrganizationMembershipsInput.Type; + +// Output Schema +export const GetOrganizationMembershipsOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + total_count: Schema.Number, + }); +export type GetOrganizationMembershipsOutput = + typeof GetOrganizationMembershipsOutput.Type; + +// The operation +/** + * Retrieve all memberships for a user + * + * Retrieve a paginated list of the user's organization memberships + * + * @param user_id - The ID of the user whose organization memberships we want to retrieve + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + */ +export const getOrganizationMemberships = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: GetOrganizationMembershipsInput, + outputSchema: GetOrganizationMembershipsOutput, + errors: [Forbidden] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/users/index.ts b/packages/clerk/src/operations/backend/users/index.ts new file mode 100644 index 0000000000..76e3ebab53 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/index.ts @@ -0,0 +1,33 @@ +export * from "./list.ts"; +export * from "./create.ts"; +export * from "./count.ts"; +export * from "./get.ts"; +export * from "./update.ts"; +export * from "./delete.ts"; +export * from "./ban.ts"; +export * from "./unban.ts"; +export * from "./bulkBan.ts"; +export * from "./bulkUnban.ts"; +export * from "./lock.ts"; +export * from "./unlock.ts"; +export * from "./setProfileImage.ts"; +export * from "./deleteProfileImage.ts"; +export * from "./replaceMetadata.ts"; +export * from "./updateMetadata.ts"; +export * from "./getBillingSubscription.ts"; +export * from "./getBillingCreditBalance.ts"; +export * from "./adjustBillingCreditBalance.ts"; +export * from "./getOAuthAccessToken.ts"; +export * from "./getOrganizationMemberships.ts"; +export * from "./getOrganizationInvitations.ts"; +export * from "./verifyPassword.ts"; +export * from "./verifyTotp.ts"; +export * from "./disableMfa.ts"; +export * from "./deleteBackupCodes.ts"; +export * from "./deletePasskey.ts"; +export * from "./deleteWeb3Wallet.ts"; +export * from "./deleteTOTP.ts"; +export * from "./deleteExternalAccount.ts"; +export * from "./setPasswordCompromised.ts"; +export * from "./unsetPasswordCompromised.ts"; +export * from "./getInstanceOrganizationMemberships.ts"; diff --git a/packages/clerk/src/operations/backend/users/list.ts b/packages/clerk/src/operations/backend/users/list.ts new file mode 100644 index 0000000000..98b06c8baf --- /dev/null +++ b/packages/clerk/src/operations/backend/users/list.ts @@ -0,0 +1,361 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ListInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + email_address: Schema.optional(Schema.String), + phone_number: Schema.optional(Schema.String), + external_id: Schema.optional(Schema.String), + username: Schema.optional(Schema.String), + web3_wallet: Schema.optional(Schema.String), + user_id: Schema.optional(Schema.String), + organization_id: Schema.optional(Schema.String), + query: Schema.optional(Schema.String), + email_address_query: Schema.optional(Schema.String), + phone_number_query: Schema.optional(Schema.String), + username_query: Schema.optional(Schema.String), + name_query: Schema.optional(Schema.String), + banned: Schema.optional(Schema.Boolean), + last_active_at_before: Schema.optional(Schema.Number), + last_active_at_after: Schema.optional(Schema.Number), + last_active_at_since: Schema.optional(Schema.Number), + created_at_before: Schema.optional(Schema.Number), + created_at_after: Schema.optional(Schema.Number), + last_sign_in_at_before: Schema.optional(Schema.Number), + last_sign_in_at_after: Schema.optional(Schema.Number), + provider: Schema.optional(Schema.String), + provider_user_id: Schema.optional(Schema.String), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), + order_by: Schema.optional(Schema.String), +}).pipe(T.Http({ method: "GET", path: "/users" })); +export type ListInput = typeof ListInput.Type; + +// Output Schema +export const ListOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["user"]), + external_id: Schema.NullOr(Schema.String), + primary_email_address_id: Schema.NullOr(Schema.String), + primary_phone_number_id: Schema.NullOr(Schema.String), + primary_web3_wallet_id: Schema.NullOr(Schema.String), + username: Schema.NullOr(Schema.String), + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + locale: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + email_addresses: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + phone_numbers: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional( + Schema.NullOr(Schema.Array(Schema.String)), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + web3_wallets: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["web3_wallet"]), + web3_wallet: Schema.String, + verification: Schema.Unknown, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + passkeys: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["passkey"]), + name: Schema.String, + last_used_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + password_enabled: Schema.Boolean, + two_factor_enabled: Schema.Boolean, + totp_enabled: Schema.Boolean, + backup_code_enabled: Schema.Boolean, + mfa_enabled_at: Schema.NullOr(Schema.Number), + mfa_disabled_at: Schema.NullOr(Schema.Number), + password_last_updated_at: Schema.optional(Schema.NullOr(Schema.Number)), + external_accounts: Schema.Array( + Schema.Struct({ + object: Schema.Literals([ + "external_account", + "facebook_account", + "google_account", + ]), + id: Schema.String, + provider: Schema.String, + identification_id: Schema.String, + provider_user_id: Schema.String, + approved_scopes: Schema.String, + email_address: Schema.String, + email_address_verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + first_name: Schema.String, + last_name: Schema.String, + avatar_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + phone_number: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + saml_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["saml_account"]), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + saml_connection: Schema.optional(Schema.Unknown), + }), + ), + enterprise_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["enterprise_account"]), + protocol: Schema.optional(Schema.Literals(["oauth", "saml"])), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + enterprise_connection_id: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + enterprise_connection: Schema.optional(Schema.Unknown), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + organization_memberships: Schema.optional( + Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + last_sign_in_at: Schema.NullOr(Schema.Number), + banned: Schema.Boolean, + locked: Schema.Boolean, + deprovisioned: Schema.optional(Schema.Boolean), + lockout_expires_in_seconds: Schema.NullOr(Schema.Number), + verification_attempts_remaining: Schema.NullOr(Schema.Number), + updated_at: Schema.Number, + created_at: Schema.Number, + delete_self_enabled: Schema.Boolean, + create_organization_enabled: Schema.Boolean, + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + last_active_at: Schema.NullOr(Schema.Number), + legal_accepted_at: Schema.NullOr(Schema.Number), + bypass_client_trust: Schema.optional(Schema.Boolean), + scim: Schema.optional( + Schema.NullOr( + Schema.Struct({ + directory_id: Schema.String, + directory_enabled: Schema.optional(Schema.Boolean), + external_id: Schema.NullOr(Schema.String), + }), + ), + ), + }), +); +export type ListOutput = typeof ListOutput.Type; + +// The operation +/** + * List all users + * + * Returns a list of all users. + * The users are returned sorted by creation date, with the newest users appearing first. + * + * @param email_address - Returns users with the specified email addresses. +Accepts up to 100 email addresses. +Any email addresses not found are ignored. + * @param phone_number - Returns users with the specified phone numbers. +Accepts up to 100 phone numbers. +Any phone numbers not found are ignored. + * @param external_id - Returns users with the specified external IDs. +For each external ID, the `+` and `-` can be +prepended to the ID, which denote whether the +respective external ID should be included or +excluded from the result set. +Accepts up to 100 external IDs. +Any external IDs not found are ignored. + * @param username - Returns users with the specified usernames. +Accepts up to 100 usernames. +Any usernames not found are ignored. + * @param web3_wallet - Returns users with the specified web3 wallet addresses. +Accepts up to 100 web3 wallet addresses. +Any web3 wallet addresses not found are ignored. + * @param user_id - Returns users with the user IDs specified. +For each user ID, the `+` and `-` can be +prepended to the ID, which denote whether the +respective user ID should be included or +excluded from the result set. +Accepts up to 100 user IDs. +Any user IDs not found are ignored. + * @param organization_id - Returns users that have memberships to the +given organizations. +For each organization ID, the `+` and `-` can be +prepended to the ID, which denote whether the +respective organization should be included or +excluded from the result set. +Accepts up to 100 organization IDs. + * @param query - Returns users that match the given query. +For possible matches, we check the email addresses, phone numbers, usernames, web3 wallets, user IDs, first and last names. +The query value doesn't need to match the exact value you are looking for, it is capable of partial matches as well. + * @param email_address_query - Returns users with emails that match the given query, via case-insensitive partial match. +For example, `email_address_query=ello` will match a user with the email `HELLO@example.com`. + * @param phone_number_query - Returns users with phone numbers that match the given query, via case-insensitive partial match. +For example, `phone_number_query=555` will match a user with the phone number `+1555xxxxxxx`. + * @param username_query - Returns users with usernames that match the given query, via case-insensitive partial match. +For example, `username_query=CoolUser` will match a user with the username `SomeCoolUser`. + * @param name_query - Returns users with names that match the given query, via case-insensitive partial match. + * @param banned - Returns users which are either banned (`banned=true`) or not banned (`banned=false`). + * @param last_active_at_before - Returns users whose last session activity was before the given date (with millisecond precision). +Example: use 1700690400000 to retrieve users whose last session activity was before 2023-11-23. + * @param last_active_at_after - Returns users whose last session activity was after the given date (with millisecond precision). +Example: use 1700690400000 to retrieve users whose last session activity was after 2023-11-23. + * @param last_active_at_since - Returns users that had session activity since the given date. +Example: use 1700690400000 to retrieve users that had session activity from 2023-11-23 until the current day. +Deprecated in favor of `last_active_at_after`. + * @param created_at_before - Returns users who have been created before the given date (with millisecond precision). +Example: use 1730160000000 to retrieve users who have been created before 2024-10-29. + * @param created_at_after - Returns users who have been created after the given date (with millisecond precision). +Example: use 1730160000000 to retrieve users who have been created after 2024-10-29. + * @param last_sign_in_at_before - Returns users whose last sign-in was before the given date (with millisecond precision). +Example: use 1700690400000 to retrieve users whose last sign-in was before 2023-11-23. + * @param last_sign_in_at_after - Returns users whose last sign-in was after the given date (with millisecond precision). +Example: use 1700690400000 to retrieve users whose last sign-in was after 2023-11-23. + * @param provider - Returns users with external accounts for the specified OAuth provider. +Must be used in combination with the `provider_user_id` parameter. +For example, use `provider=oauth_google&provider_user_id=12345` to retrieve a user with Google provider user ID 12345. + * @param provider_user_id - Returns users with the specified provider user IDs for a specific provider. +Must be used in combination with the `provider` parameter. +For example, use `provider=oauth_google&provider_user_id=12345` to retrieve a user with Google provider user ID 12345. +Accepts up to 100 provider user IDs. +Any provider user IDs not found are ignored. + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + * @param order_by - Allows to return users in a particular order. +At the moment, you can order the returned users by their `created_at`,`updated_at`,`email_address`,`web3wallet`,`first_name`,`last_name`,`phone_number`,`username`,`last_active_at`,`last_sign_in_at`. +In order to specify the direction, you can use the `+/-` symbols prepended in the property to order by. +For example, if you want users to be returned in descending order according to their `created_at` property, you can use `-created_at`. +If you don't use `+` or `-`, then `+` is implied. We only support one `order_by` parameter, and if multiple `order_by` parameters are provided, we will only keep the first one. For example, +if you pass `order_by=username&order_by=created_at`, we will consider only the first `order_by` parameter, which is `username`. The `created_at` parameter will be ignored in this case. + */ +export const list = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInput, + outputSchema: ListOutput, + errors: [BadRequest, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/lock.ts b/packages/clerk/src/operations/backend/users/lock.ts new file mode 100644 index 0000000000..c33b2ac980 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/lock.ts @@ -0,0 +1,256 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden } from "../../../errors.ts"; + +// Input Schema +export const LockInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "POST", path: "/users/{user_id}/lock" })); +export type LockInput = typeof LockInput.Type; + +// Output Schema +export const LockOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["user"]), + external_id: Schema.NullOr(Schema.String), + primary_email_address_id: Schema.NullOr(Schema.String), + primary_phone_number_id: Schema.NullOr(Schema.String), + primary_web3_wallet_id: Schema.NullOr(Schema.String), + username: Schema.NullOr(Schema.String), + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + locale: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + email_addresses: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + phone_numbers: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + web3_wallets: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["web3_wallet"]), + web3_wallet: Schema.String, + verification: Schema.Unknown, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + passkeys: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["passkey"]), + name: Schema.String, + last_used_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + password_enabled: Schema.Boolean, + two_factor_enabled: Schema.Boolean, + totp_enabled: Schema.Boolean, + backup_code_enabled: Schema.Boolean, + mfa_enabled_at: Schema.NullOr(Schema.Number), + mfa_disabled_at: Schema.NullOr(Schema.Number), + password_last_updated_at: Schema.optional(Schema.NullOr(Schema.Number)), + external_accounts: Schema.Array( + Schema.Struct({ + object: Schema.Literals([ + "external_account", + "facebook_account", + "google_account", + ]), + id: Schema.String, + provider: Schema.String, + identification_id: Schema.String, + provider_user_id: Schema.String, + approved_scopes: Schema.String, + email_address: Schema.String, + email_address_verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + first_name: Schema.String, + last_name: Schema.String, + avatar_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + phone_number: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + saml_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["saml_account"]), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + saml_connection: Schema.optional(Schema.Unknown), + }), + ), + enterprise_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["enterprise_account"]), + protocol: Schema.optional(Schema.Literals(["oauth", "saml"])), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + enterprise_connection_id: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + enterprise_connection: Schema.optional(Schema.Unknown), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + organization_memberships: Schema.optional( + Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + last_sign_in_at: Schema.NullOr(Schema.Number), + banned: Schema.Boolean, + locked: Schema.Boolean, + deprovisioned: Schema.optional(Schema.Boolean), + lockout_expires_in_seconds: Schema.NullOr(Schema.Number), + verification_attempts_remaining: Schema.NullOr(Schema.Number), + updated_at: Schema.Number, + created_at: Schema.Number, + delete_self_enabled: Schema.Boolean, + create_organization_enabled: Schema.Boolean, + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + last_active_at: Schema.NullOr(Schema.Number), + legal_accepted_at: Schema.NullOr(Schema.Number), + bypass_client_trust: Schema.optional(Schema.Boolean), + scim: Schema.optional( + Schema.NullOr( + Schema.Struct({ + directory_id: Schema.String, + directory_enabled: Schema.optional(Schema.Boolean), + external_id: Schema.NullOr(Schema.String), + }), + ), + ), +}); +export type LockOutput = typeof LockOutput.Type; + +// The operation +/** + * Lock a user + * + * Marks the given user as locked, which means they are not allowed to sign in again until the lock expires. + * Lock duration can be configured in the instance's restrictions settings. + * + * @param user_id - The ID of the user to lock + */ +export const lock = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: LockInput, + outputSchema: LockOutput, + errors: [Forbidden] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/replaceMetadata.ts b/packages/clerk/src/operations/backend/users/replaceMetadata.ts new file mode 100644 index 0000000000..38e3abb544 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/replaceMetadata.ts @@ -0,0 +1,270 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ReplaceMetadataInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), +}).pipe(T.Http({ method: "PUT", path: "/users/{user_id}/metadata" })); +export type ReplaceMetadataInput = typeof ReplaceMetadataInput.Type; + +// Output Schema +export const ReplaceMetadataOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["user"]), + external_id: Schema.NullOr(Schema.String), + primary_email_address_id: Schema.NullOr(Schema.String), + primary_phone_number_id: Schema.NullOr(Schema.String), + primary_web3_wallet_id: Schema.NullOr(Schema.String), + username: Schema.NullOr(Schema.String), + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + locale: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + email_addresses: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + phone_numbers: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + web3_wallets: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["web3_wallet"]), + web3_wallet: Schema.String, + verification: Schema.Unknown, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + passkeys: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["passkey"]), + name: Schema.String, + last_used_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + password_enabled: Schema.Boolean, + two_factor_enabled: Schema.Boolean, + totp_enabled: Schema.Boolean, + backup_code_enabled: Schema.Boolean, + mfa_enabled_at: Schema.NullOr(Schema.Number), + mfa_disabled_at: Schema.NullOr(Schema.Number), + password_last_updated_at: Schema.optional(Schema.NullOr(Schema.Number)), + external_accounts: Schema.Array( + Schema.Struct({ + object: Schema.Literals([ + "external_account", + "facebook_account", + "google_account", + ]), + id: Schema.String, + provider: Schema.String, + identification_id: Schema.String, + provider_user_id: Schema.String, + approved_scopes: Schema.String, + email_address: Schema.String, + email_address_verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + first_name: Schema.String, + last_name: Schema.String, + avatar_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + phone_number: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + saml_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["saml_account"]), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + saml_connection: Schema.optional(Schema.Unknown), + }), + ), + enterprise_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["enterprise_account"]), + protocol: Schema.optional(Schema.Literals(["oauth", "saml"])), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + enterprise_connection_id: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + enterprise_connection: Schema.optional(Schema.Unknown), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + organization_memberships: Schema.optional( + Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + last_sign_in_at: Schema.NullOr(Schema.Number), + banned: Schema.Boolean, + locked: Schema.Boolean, + deprovisioned: Schema.optional(Schema.Boolean), + lockout_expires_in_seconds: Schema.NullOr(Schema.Number), + verification_attempts_remaining: Schema.NullOr(Schema.Number), + updated_at: Schema.Number, + created_at: Schema.Number, + delete_self_enabled: Schema.Boolean, + create_organization_enabled: Schema.Boolean, + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + last_active_at: Schema.NullOr(Schema.Number), + legal_accepted_at: Schema.NullOr(Schema.Number), + bypass_client_trust: Schema.optional(Schema.Boolean), + scim: Schema.optional( + Schema.NullOr( + Schema.Struct({ + directory_id: Schema.String, + directory_enabled: Schema.optional(Schema.Boolean), + external_id: Schema.NullOr(Schema.String), + }), + ), + ), +}); +export type ReplaceMetadataOutput = typeof ReplaceMetadataOutput.Type; + +// The operation +/** + * Replace a user's metadata + * + * Replace a user's metadata attributes with the provided values. + * Unlike `PATCH /v1/users/{user_id}/metadata` (merge semantics), this endpoint + * replaces the supplied metadata columns entirely — the prior contents of each + * supplied column are discarded. Columns omitted from the request body are + * left unchanged. + * Prefer the `PATCH` endpoint for partial updates. Use `PUT` only when you + * explicitly intend to overwrite a metadata column wholesale. + * + * @param user_id - The ID of the user whose metadata will be replaced + */ +export const replaceMetadata = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ReplaceMetadataInput, + outputSchema: ReplaceMetadataOutput, + errors: [BadRequest, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/setPasswordCompromised.ts b/packages/clerk/src/operations/backend/users/setPasswordCompromised.ts new file mode 100644 index 0000000000..64534981d3 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/setPasswordCompromised.ts @@ -0,0 +1,274 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const SetPasswordCompromisedInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + revoke_all_sessions: Schema.optional(Schema.NullOr(Schema.Boolean)), + }).pipe( + T.Http({ + method: "POST", + path: "/users/{user_id}/password/set_compromised", + }), + ); +export type SetPasswordCompromisedInput = + typeof SetPasswordCompromisedInput.Type; + +// Output Schema +export const SetPasswordCompromisedOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["user"]), + external_id: Schema.NullOr(Schema.String), + primary_email_address_id: Schema.NullOr(Schema.String), + primary_phone_number_id: Schema.NullOr(Schema.String), + primary_web3_wallet_id: Schema.NullOr(Schema.String), + username: Schema.NullOr(Schema.String), + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + locale: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + email_addresses: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + phone_numbers: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional( + Schema.NullOr(Schema.Array(Schema.String)), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + web3_wallets: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["web3_wallet"]), + web3_wallet: Schema.String, + verification: Schema.Unknown, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + passkeys: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["passkey"]), + name: Schema.String, + last_used_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + password_enabled: Schema.Boolean, + two_factor_enabled: Schema.Boolean, + totp_enabled: Schema.Boolean, + backup_code_enabled: Schema.Boolean, + mfa_enabled_at: Schema.NullOr(Schema.Number), + mfa_disabled_at: Schema.NullOr(Schema.Number), + password_last_updated_at: Schema.optional(Schema.NullOr(Schema.Number)), + external_accounts: Schema.Array( + Schema.Struct({ + object: Schema.Literals([ + "external_account", + "facebook_account", + "google_account", + ]), + id: Schema.String, + provider: Schema.String, + identification_id: Schema.String, + provider_user_id: Schema.String, + approved_scopes: Schema.String, + email_address: Schema.String, + email_address_verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + first_name: Schema.String, + last_name: Schema.String, + avatar_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + phone_number: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + saml_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["saml_account"]), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + saml_connection: Schema.optional(Schema.Unknown), + }), + ), + enterprise_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["enterprise_account"]), + protocol: Schema.optional(Schema.Literals(["oauth", "saml"])), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + enterprise_connection_id: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + enterprise_connection: Schema.optional(Schema.Unknown), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + organization_memberships: Schema.optional( + Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + last_sign_in_at: Schema.NullOr(Schema.Number), + banned: Schema.Boolean, + locked: Schema.Boolean, + deprovisioned: Schema.optional(Schema.Boolean), + lockout_expires_in_seconds: Schema.NullOr(Schema.Number), + verification_attempts_remaining: Schema.NullOr(Schema.Number), + updated_at: Schema.Number, + created_at: Schema.Number, + delete_self_enabled: Schema.Boolean, + create_organization_enabled: Schema.Boolean, + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + last_active_at: Schema.NullOr(Schema.Number), + legal_accepted_at: Schema.NullOr(Schema.Number), + bypass_client_trust: Schema.optional(Schema.Boolean), + scim: Schema.optional( + Schema.NullOr( + Schema.Struct({ + directory_id: Schema.String, + directory_enabled: Schema.optional(Schema.Boolean), + external_id: Schema.NullOr(Schema.String), + }), + ), + ), + }); +export type SetPasswordCompromisedOutput = + typeof SetPasswordCompromisedOutput.Type; + +// The operation +/** + * Set a user's password as compromised + * + * Sets the given user's password as compromised. The user will be prompted to reset their password on their next sign-in. + * + * @param user_id - The ID of the user to set the password as compromised + */ +export const setPasswordCompromised = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: SetPasswordCompromisedInput, + outputSchema: SetPasswordCompromisedOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/users/setProfileImage.ts b/packages/clerk/src/operations/backend/users/setProfileImage.ts new file mode 100644 index 0000000000..22c3e0b7e5 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/setProfileImage.ts @@ -0,0 +1,262 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound } from "../../../errors.ts"; + +// Input Schema +export const SetProfileImageInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + file: Schema.optional(Schema.String), +}).pipe( + T.Http({ + method: "POST", + path: "/users/{user_id}/profile_image", + contentType: "multipart", + }), +); +export type SetProfileImageInput = typeof SetProfileImageInput.Type; + +// Output Schema +export const SetProfileImageOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["user"]), + external_id: Schema.NullOr(Schema.String), + primary_email_address_id: Schema.NullOr(Schema.String), + primary_phone_number_id: Schema.NullOr(Schema.String), + primary_web3_wallet_id: Schema.NullOr(Schema.String), + username: Schema.NullOr(Schema.String), + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + locale: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + email_addresses: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + phone_numbers: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + web3_wallets: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["web3_wallet"]), + web3_wallet: Schema.String, + verification: Schema.Unknown, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + passkeys: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["passkey"]), + name: Schema.String, + last_used_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + password_enabled: Schema.Boolean, + two_factor_enabled: Schema.Boolean, + totp_enabled: Schema.Boolean, + backup_code_enabled: Schema.Boolean, + mfa_enabled_at: Schema.NullOr(Schema.Number), + mfa_disabled_at: Schema.NullOr(Schema.Number), + password_last_updated_at: Schema.optional(Schema.NullOr(Schema.Number)), + external_accounts: Schema.Array( + Schema.Struct({ + object: Schema.Literals([ + "external_account", + "facebook_account", + "google_account", + ]), + id: Schema.String, + provider: Schema.String, + identification_id: Schema.String, + provider_user_id: Schema.String, + approved_scopes: Schema.String, + email_address: Schema.String, + email_address_verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + first_name: Schema.String, + last_name: Schema.String, + avatar_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + phone_number: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + saml_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["saml_account"]), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + saml_connection: Schema.optional(Schema.Unknown), + }), + ), + enterprise_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["enterprise_account"]), + protocol: Schema.optional(Schema.Literals(["oauth", "saml"])), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + enterprise_connection_id: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + enterprise_connection: Schema.optional(Schema.Unknown), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + organization_memberships: Schema.optional( + Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + last_sign_in_at: Schema.NullOr(Schema.Number), + banned: Schema.Boolean, + locked: Schema.Boolean, + deprovisioned: Schema.optional(Schema.Boolean), + lockout_expires_in_seconds: Schema.NullOr(Schema.Number), + verification_attempts_remaining: Schema.NullOr(Schema.Number), + updated_at: Schema.Number, + created_at: Schema.Number, + delete_self_enabled: Schema.Boolean, + create_organization_enabled: Schema.Boolean, + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + last_active_at: Schema.NullOr(Schema.Number), + legal_accepted_at: Schema.NullOr(Schema.Number), + bypass_client_trust: Schema.optional(Schema.Boolean), + scim: Schema.optional( + Schema.NullOr( + Schema.Struct({ + directory_id: Schema.String, + directory_enabled: Schema.optional(Schema.Boolean), + external_id: Schema.NullOr(Schema.String), + }), + ), + ), +}); +export type SetProfileImageOutput = typeof SetProfileImageOutput.Type; + +// The operation +/** + * Set user profile image + * + * Update a user's profile image + * + * @param user_id - The ID of the user to update the profile image for + */ +export const setProfileImage = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: SetProfileImageInput, + outputSchema: SetProfileImageOutput, + errors: [BadRequest, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/unban.ts b/packages/clerk/src/operations/backend/users/unban.ts new file mode 100644 index 0000000000..13a54074e4 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/unban.ts @@ -0,0 +1,255 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { PaymentRequired } from "../../../errors.ts"; + +// Input Schema +export const UnbanInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "POST", path: "/users/{user_id}/unban" })); +export type UnbanInput = typeof UnbanInput.Type; + +// Output Schema +export const UnbanOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["user"]), + external_id: Schema.NullOr(Schema.String), + primary_email_address_id: Schema.NullOr(Schema.String), + primary_phone_number_id: Schema.NullOr(Schema.String), + primary_web3_wallet_id: Schema.NullOr(Schema.String), + username: Schema.NullOr(Schema.String), + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + locale: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + email_addresses: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + phone_numbers: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + web3_wallets: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["web3_wallet"]), + web3_wallet: Schema.String, + verification: Schema.Unknown, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + passkeys: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["passkey"]), + name: Schema.String, + last_used_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + password_enabled: Schema.Boolean, + two_factor_enabled: Schema.Boolean, + totp_enabled: Schema.Boolean, + backup_code_enabled: Schema.Boolean, + mfa_enabled_at: Schema.NullOr(Schema.Number), + mfa_disabled_at: Schema.NullOr(Schema.Number), + password_last_updated_at: Schema.optional(Schema.NullOr(Schema.Number)), + external_accounts: Schema.Array( + Schema.Struct({ + object: Schema.Literals([ + "external_account", + "facebook_account", + "google_account", + ]), + id: Schema.String, + provider: Schema.String, + identification_id: Schema.String, + provider_user_id: Schema.String, + approved_scopes: Schema.String, + email_address: Schema.String, + email_address_verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + first_name: Schema.String, + last_name: Schema.String, + avatar_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + phone_number: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + saml_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["saml_account"]), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + saml_connection: Schema.optional(Schema.Unknown), + }), + ), + enterprise_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["enterprise_account"]), + protocol: Schema.optional(Schema.Literals(["oauth", "saml"])), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + enterprise_connection_id: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + enterprise_connection: Schema.optional(Schema.Unknown), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + organization_memberships: Schema.optional( + Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + last_sign_in_at: Schema.NullOr(Schema.Number), + banned: Schema.Boolean, + locked: Schema.Boolean, + deprovisioned: Schema.optional(Schema.Boolean), + lockout_expires_in_seconds: Schema.NullOr(Schema.Number), + verification_attempts_remaining: Schema.NullOr(Schema.Number), + updated_at: Schema.Number, + created_at: Schema.Number, + delete_self_enabled: Schema.Boolean, + create_organization_enabled: Schema.Boolean, + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + last_active_at: Schema.NullOr(Schema.Number), + legal_accepted_at: Schema.NullOr(Schema.Number), + bypass_client_trust: Schema.optional(Schema.Boolean), + scim: Schema.optional( + Schema.NullOr( + Schema.Struct({ + directory_id: Schema.String, + directory_enabled: Schema.optional(Schema.Boolean), + external_id: Schema.NullOr(Schema.String), + }), + ), + ), +}); +export type UnbanOutput = typeof UnbanOutput.Type; + +// The operation +/** + * Unban a user + * + * Removes the ban mark from the given user. + * + * @param user_id - The ID of the user to unban + */ +export const unban = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UnbanInput, + outputSchema: UnbanOutput, + errors: [PaymentRequired] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/unlock.ts b/packages/clerk/src/operations/backend/users/unlock.ts new file mode 100644 index 0000000000..34ac8f83f9 --- /dev/null +++ b/packages/clerk/src/operations/backend/users/unlock.ts @@ -0,0 +1,255 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden } from "../../../errors.ts"; + +// Input Schema +export const UnlockInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "POST", path: "/users/{user_id}/unlock" })); +export type UnlockInput = typeof UnlockInput.Type; + +// Output Schema +export const UnlockOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["user"]), + external_id: Schema.NullOr(Schema.String), + primary_email_address_id: Schema.NullOr(Schema.String), + primary_phone_number_id: Schema.NullOr(Schema.String), + primary_web3_wallet_id: Schema.NullOr(Schema.String), + username: Schema.NullOr(Schema.String), + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + locale: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + email_addresses: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + phone_numbers: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + web3_wallets: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["web3_wallet"]), + web3_wallet: Schema.String, + verification: Schema.Unknown, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + passkeys: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["passkey"]), + name: Schema.String, + last_used_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + password_enabled: Schema.Boolean, + two_factor_enabled: Schema.Boolean, + totp_enabled: Schema.Boolean, + backup_code_enabled: Schema.Boolean, + mfa_enabled_at: Schema.NullOr(Schema.Number), + mfa_disabled_at: Schema.NullOr(Schema.Number), + password_last_updated_at: Schema.optional(Schema.NullOr(Schema.Number)), + external_accounts: Schema.Array( + Schema.Struct({ + object: Schema.Literals([ + "external_account", + "facebook_account", + "google_account", + ]), + id: Schema.String, + provider: Schema.String, + identification_id: Schema.String, + provider_user_id: Schema.String, + approved_scopes: Schema.String, + email_address: Schema.String, + email_address_verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + first_name: Schema.String, + last_name: Schema.String, + avatar_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + phone_number: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + saml_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["saml_account"]), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + saml_connection: Schema.optional(Schema.Unknown), + }), + ), + enterprise_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["enterprise_account"]), + protocol: Schema.optional(Schema.Literals(["oauth", "saml"])), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + enterprise_connection_id: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + enterprise_connection: Schema.optional(Schema.Unknown), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + organization_memberships: Schema.optional( + Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + last_sign_in_at: Schema.NullOr(Schema.Number), + banned: Schema.Boolean, + locked: Schema.Boolean, + deprovisioned: Schema.optional(Schema.Boolean), + lockout_expires_in_seconds: Schema.NullOr(Schema.Number), + verification_attempts_remaining: Schema.NullOr(Schema.Number), + updated_at: Schema.Number, + created_at: Schema.Number, + delete_self_enabled: Schema.Boolean, + create_organization_enabled: Schema.Boolean, + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + last_active_at: Schema.NullOr(Schema.Number), + legal_accepted_at: Schema.NullOr(Schema.Number), + bypass_client_trust: Schema.optional(Schema.Boolean), + scim: Schema.optional( + Schema.NullOr( + Schema.Struct({ + directory_id: Schema.String, + directory_enabled: Schema.optional(Schema.Boolean), + external_id: Schema.NullOr(Schema.String), + }), + ), + ), +}); +export type UnlockOutput = typeof UnlockOutput.Type; + +// The operation +/** + * Unlock a user + * + * Removes the lock from the given user. + * + * @param user_id - The ID of the user to unlock + */ +export const unlock = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UnlockInput, + outputSchema: UnlockOutput, + errors: [Forbidden] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/unsetPasswordCompromised.ts b/packages/clerk/src/operations/backend/users/unsetPasswordCompromised.ts new file mode 100644 index 0000000000..baf3318c9a --- /dev/null +++ b/packages/clerk/src/operations/backend/users/unsetPasswordCompromised.ts @@ -0,0 +1,273 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const UnsetPasswordCompromisedInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + }).pipe( + T.Http({ + method: "POST", + path: "/users/{user_id}/password/unset_compromised", + }), + ); +export type UnsetPasswordCompromisedInput = + typeof UnsetPasswordCompromisedInput.Type; + +// Output Schema +export const UnsetPasswordCompromisedOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["user"]), + external_id: Schema.NullOr(Schema.String), + primary_email_address_id: Schema.NullOr(Schema.String), + primary_phone_number_id: Schema.NullOr(Schema.String), + primary_web3_wallet_id: Schema.NullOr(Schema.String), + username: Schema.NullOr(Schema.String), + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + locale: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + email_addresses: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + phone_numbers: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional( + Schema.NullOr(Schema.Array(Schema.String)), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + web3_wallets: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["web3_wallet"]), + web3_wallet: Schema.String, + verification: Schema.Unknown, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + passkeys: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["passkey"]), + name: Schema.String, + last_used_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + password_enabled: Schema.Boolean, + two_factor_enabled: Schema.Boolean, + totp_enabled: Schema.Boolean, + backup_code_enabled: Schema.Boolean, + mfa_enabled_at: Schema.NullOr(Schema.Number), + mfa_disabled_at: Schema.NullOr(Schema.Number), + password_last_updated_at: Schema.optional(Schema.NullOr(Schema.Number)), + external_accounts: Schema.Array( + Schema.Struct({ + object: Schema.Literals([ + "external_account", + "facebook_account", + "google_account", + ]), + id: Schema.String, + provider: Schema.String, + identification_id: Schema.String, + provider_user_id: Schema.String, + approved_scopes: Schema.String, + email_address: Schema.String, + email_address_verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + first_name: Schema.String, + last_name: Schema.String, + avatar_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + phone_number: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + saml_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["saml_account"]), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + saml_connection: Schema.optional(Schema.Unknown), + }), + ), + enterprise_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["enterprise_account"]), + protocol: Schema.optional(Schema.Literals(["oauth", "saml"])), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + enterprise_connection_id: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + enterprise_connection: Schema.optional(Schema.Unknown), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + organization_memberships: Schema.optional( + Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + last_sign_in_at: Schema.NullOr(Schema.Number), + banned: Schema.Boolean, + locked: Schema.Boolean, + deprovisioned: Schema.optional(Schema.Boolean), + lockout_expires_in_seconds: Schema.NullOr(Schema.Number), + verification_attempts_remaining: Schema.NullOr(Schema.Number), + updated_at: Schema.Number, + created_at: Schema.Number, + delete_self_enabled: Schema.Boolean, + create_organization_enabled: Schema.Boolean, + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + last_active_at: Schema.NullOr(Schema.Number), + legal_accepted_at: Schema.NullOr(Schema.Number), + bypass_client_trust: Schema.optional(Schema.Boolean), + scim: Schema.optional( + Schema.NullOr( + Schema.Struct({ + directory_id: Schema.String, + directory_enabled: Schema.optional(Schema.Boolean), + external_id: Schema.NullOr(Schema.String), + }), + ), + ), + }); +export type UnsetPasswordCompromisedOutput = + typeof UnsetPasswordCompromisedOutput.Type; + +// The operation +/** + * Unset a user's password as compromised + * + * Sets the given user's password as no longer compromised. The user will no longer be prompted to reset their password on their next sign-in. + * + * @param user_id - The ID of the user to unset the compromised status for + */ +export const unsetPasswordCompromised = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: UnsetPasswordCompromisedInput, + outputSchema: UnsetPasswordCompromisedOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, + }), +); diff --git a/packages/clerk/src/operations/backend/users/update.ts b/packages/clerk/src/operations/backend/users/update.ts new file mode 100644 index 0000000000..9db4ee223b --- /dev/null +++ b/packages/clerk/src/operations/backend/users/update.ts @@ -0,0 +1,307 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + NotFound, + Conflict, + UnprocessableEntity, +} from "../../../errors.ts"; +import { + SensitiveString, + SensitiveNullableString, +} from "../../../sensitive.ts"; + +// Input Schema +export const UpdateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + external_id: Schema.optional(Schema.NullOr(Schema.String)), + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + locale: Schema.optional(Schema.NullOr(Schema.String)), + primary_email_address_id: Schema.optional(Schema.NullOr(Schema.String)), + notify_primary_email_address_changed: Schema.optional( + Schema.NullOr(Schema.Boolean), + ), + primary_phone_number_id: Schema.optional(Schema.NullOr(Schema.String)), + primary_web3_wallet_id: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_id: Schema.optional(Schema.NullOr(Schema.String)), + password: Schema.optional(SensitiveNullableString), + password_digest: Schema.optional(SensitiveString), + password_hasher: Schema.optional(Schema.String), + skip_password_checks: Schema.optional(Schema.NullOr(Schema.Boolean)), + sign_out_of_other_sessions: Schema.optional(Schema.NullOr(Schema.Boolean)), + totp_secret: Schema.optional(SensitiveNullableString), + backup_codes: Schema.optional(Schema.Array(Schema.String)), + public_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + delete_self_enabled: Schema.optional(Schema.NullOr(Schema.Boolean)), + create_organization_enabled: Schema.optional(Schema.NullOr(Schema.Boolean)), + legal_accepted_at: Schema.optional(Schema.NullOr(Schema.String)), + skip_legal_checks: Schema.optional(Schema.NullOr(Schema.Boolean)), + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.optional(Schema.NullOr(Schema.String)), + bypass_client_trust: Schema.optional(Schema.NullOr(Schema.Boolean)), +}).pipe(T.Http({ method: "PATCH", path: "/users/{user_id}" })); +export type UpdateInput = typeof UpdateInput.Type; + +// Output Schema +export const UpdateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["user"]), + external_id: Schema.NullOr(Schema.String), + primary_email_address_id: Schema.NullOr(Schema.String), + primary_phone_number_id: Schema.NullOr(Schema.String), + primary_web3_wallet_id: Schema.NullOr(Schema.String), + username: Schema.NullOr(Schema.String), + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + locale: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + email_addresses: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + phone_numbers: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + web3_wallets: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["web3_wallet"]), + web3_wallet: Schema.String, + verification: Schema.Unknown, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + passkeys: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["passkey"]), + name: Schema.String, + last_used_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + password_enabled: Schema.Boolean, + two_factor_enabled: Schema.Boolean, + totp_enabled: Schema.Boolean, + backup_code_enabled: Schema.Boolean, + mfa_enabled_at: Schema.NullOr(Schema.Number), + mfa_disabled_at: Schema.NullOr(Schema.Number), + password_last_updated_at: Schema.optional(Schema.NullOr(Schema.Number)), + external_accounts: Schema.Array( + Schema.Struct({ + object: Schema.Literals([ + "external_account", + "facebook_account", + "google_account", + ]), + id: Schema.String, + provider: Schema.String, + identification_id: Schema.String, + provider_user_id: Schema.String, + approved_scopes: Schema.String, + email_address: Schema.String, + email_address_verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + first_name: Schema.String, + last_name: Schema.String, + avatar_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + phone_number: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + saml_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["saml_account"]), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + saml_connection: Schema.optional(Schema.Unknown), + }), + ), + enterprise_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["enterprise_account"]), + protocol: Schema.optional(Schema.Literals(["oauth", "saml"])), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + enterprise_connection_id: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + enterprise_connection: Schema.optional(Schema.Unknown), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + organization_memberships: Schema.optional( + Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + last_sign_in_at: Schema.NullOr(Schema.Number), + banned: Schema.Boolean, + locked: Schema.Boolean, + deprovisioned: Schema.optional(Schema.Boolean), + lockout_expires_in_seconds: Schema.NullOr(Schema.Number), + verification_attempts_remaining: Schema.NullOr(Schema.Number), + updated_at: Schema.Number, + created_at: Schema.Number, + delete_self_enabled: Schema.Boolean, + create_organization_enabled: Schema.Boolean, + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + last_active_at: Schema.NullOr(Schema.Number), + legal_accepted_at: Schema.NullOr(Schema.Number), + bypass_client_trust: Schema.optional(Schema.Boolean), + scim: Schema.optional( + Schema.NullOr( + Schema.Struct({ + directory_id: Schema.String, + directory_enabled: Schema.optional(Schema.Boolean), + external_id: Schema.NullOr(Schema.String), + }), + ), + ), +}); +export type UpdateOutput = typeof UpdateOutput.Type; + +// The operation +/** + * Update a user + * + * Update a user's attributes. + * You can set the user's primary contact identifiers (email address and phone numbers) by updating the `primary_email_address_id` and `primary_phone_number_id` attributes respectively. + * Both IDs should correspond to verified identifications that belong to the user. + * You can remove a user's username by setting the username attribute to null or the blank string "". + * This is a destructive action; the identification will be deleted forever. + * Usernames can be removed only if they are optional in your instance settings and there's at least one other identifier which can be used for authentication. + * This endpoint allows changing a user's password. When passing the `password` parameter directly you have two further options. + * You can ignore the password policy checks for your instance by setting the `skip_password_checks` parameter to `true`. + * You can also choose to sign the user out of all their active sessions on any device once the password is updated. Just set `sign_out_of_other_sessions` to `true`. + * + * @param user_id - The ID of the user to update + */ +export const update = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateInput, + outputSchema: UpdateOutput, + errors: [BadRequest, NotFound, Conflict, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/updateMetadata.ts b/packages/clerk/src/operations/backend/users/updateMetadata.ts new file mode 100644 index 0000000000..d6064b591c --- /dev/null +++ b/packages/clerk/src/operations/backend/users/updateMetadata.ts @@ -0,0 +1,269 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const UpdateMetadataInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), +}).pipe(T.Http({ method: "PATCH", path: "/users/{user_id}/metadata" })); +export type UpdateMetadataInput = typeof UpdateMetadataInput.Type; + +// Output Schema +export const UpdateMetadataOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["user"]), + external_id: Schema.NullOr(Schema.String), + primary_email_address_id: Schema.NullOr(Schema.String), + primary_phone_number_id: Schema.NullOr(Schema.String), + primary_web3_wallet_id: Schema.NullOr(Schema.String), + username: Schema.NullOr(Schema.String), + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + locale: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + email_addresses: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + phone_numbers: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + web3_wallets: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["web3_wallet"]), + web3_wallet: Schema.String, + verification: Schema.Unknown, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + passkeys: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["passkey"]), + name: Schema.String, + last_used_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + password_enabled: Schema.Boolean, + two_factor_enabled: Schema.Boolean, + totp_enabled: Schema.Boolean, + backup_code_enabled: Schema.Boolean, + mfa_enabled_at: Schema.NullOr(Schema.Number), + mfa_disabled_at: Schema.NullOr(Schema.Number), + password_last_updated_at: Schema.optional(Schema.NullOr(Schema.Number)), + external_accounts: Schema.Array( + Schema.Struct({ + object: Schema.Literals([ + "external_account", + "facebook_account", + "google_account", + ]), + id: Schema.String, + provider: Schema.String, + identification_id: Schema.String, + provider_user_id: Schema.String, + approved_scopes: Schema.String, + email_address: Schema.String, + email_address_verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + first_name: Schema.String, + last_name: Schema.String, + avatar_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + phone_number: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + saml_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["saml_account"]), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + saml_connection: Schema.optional(Schema.Unknown), + }), + ), + enterprise_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["enterprise_account"]), + protocol: Schema.optional(Schema.Literals(["oauth", "saml"])), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + enterprise_connection_id: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + enterprise_connection: Schema.optional(Schema.Unknown), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + organization_memberships: Schema.optional( + Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + last_sign_in_at: Schema.NullOr(Schema.Number), + banned: Schema.Boolean, + locked: Schema.Boolean, + deprovisioned: Schema.optional(Schema.Boolean), + lockout_expires_in_seconds: Schema.NullOr(Schema.Number), + verification_attempts_remaining: Schema.NullOr(Schema.Number), + updated_at: Schema.Number, + created_at: Schema.Number, + delete_self_enabled: Schema.Boolean, + create_organization_enabled: Schema.Boolean, + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + last_active_at: Schema.NullOr(Schema.Number), + legal_accepted_at: Schema.NullOr(Schema.Number), + bypass_client_trust: Schema.optional(Schema.Boolean), + scim: Schema.optional( + Schema.NullOr( + Schema.Struct({ + directory_id: Schema.String, + directory_enabled: Schema.optional(Schema.Boolean), + external_id: Schema.NullOr(Schema.String), + }), + ), + ), +}); +export type UpdateMetadataOutput = typeof UpdateMetadataOutput.Type; + +// The operation +/** + * Merge and update a user's metadata + * + * Update a user's metadata attributes by merging existing values with the provided parameters. + * This endpoint behaves differently than the *Update a user* endpoint. + * Metadata values will not be replaced entirely. + * Instead, a deep merge will be performed. + * Deep means that any nested JSON objects will be merged as well. + * You can remove metadata keys at any level by setting their value to `null`. + * + * @param user_id - The ID of the user whose metadata will be updated and merged + */ +export const updateMetadata = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateMetadataInput, + outputSchema: UpdateMetadataOutput, + errors: [BadRequest, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/verifyPassword.ts b/packages/clerk/src/operations/backend/users/verifyPassword.ts new file mode 100644 index 0000000000..2f89f9f07b --- /dev/null +++ b/packages/clerk/src/operations/backend/users/verifyPassword.ts @@ -0,0 +1,33 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound, UnprocessableEntity } from "../../../errors.ts"; +import { SensitiveString } from "../../../sensitive.ts"; + +// Input Schema +export const VerifyPasswordInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + password: SensitiveString, +}).pipe(T.Http({ method: "POST", path: "/users/{user_id}/verify_password" })); +export type VerifyPasswordInput = typeof VerifyPasswordInput.Type; + +// Output Schema +export const VerifyPasswordOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + verified: Schema.optional(Schema.Boolean), +}); +export type VerifyPasswordOutput = typeof VerifyPasswordOutput.Type; + +// The operation +/** + * Verify the password of a user + * + * Check that the user's password matches the supplied input. + * Useful for custom auth flows and re-verification. + * + * @param user_id - The ID of the user for whom to verify the password + */ +export const verifyPassword = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: VerifyPasswordInput, + outputSchema: VerifyPasswordOutput, + errors: [BadRequest, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/users/verifyTotp.ts b/packages/clerk/src/operations/backend/users/verifyTotp.ts new file mode 100644 index 0000000000..6bfab0687b --- /dev/null +++ b/packages/clerk/src/operations/backend/users/verifyTotp.ts @@ -0,0 +1,35 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const VerifyTotpInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + user_id: Schema.String.pipe(T.PathParam()), + code: Schema.String, +}).pipe(T.Http({ method: "POST", path: "/users/{user_id}/verify_totp" })); +export type VerifyTotpInput = typeof VerifyTotpInput.Type; + +// Output Schema +export const VerifyTotpOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + verified: Schema.optional(Schema.Boolean), + code_type: Schema.optional(Schema.Literals(["totp", "backup_code"])), +}); +export type VerifyTotpOutput = typeof VerifyTotpOutput.Type; + +// The operation +/** + * Verify a TOTP or backup code for a user + * + * Verify that the provided TOTP or backup code is valid for the user. + * Verifying a backup code will result it in being consumed (i.e. it will + * become invalid). + * Useful for custom auth flows and re-verification. + * + * @param user_id - The ID of the user for whom to verify the TOTP + */ +export const verifyTotp = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: VerifyTotpInput, + outputSchema: VerifyTotpOutput, + errors: [BadRequest, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/waitlistEntries/bulkCreate.ts b/packages/clerk/src/operations/backend/waitlistEntries/bulkCreate.ts new file mode 100644 index 0000000000..04b025e9c6 --- /dev/null +++ b/packages/clerk/src/operations/backend/waitlistEntries/bulkCreate.ts @@ -0,0 +1,62 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const BulkCreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + {}, +).pipe(T.Http({ method: "POST", path: "/waitlist_entries/bulk" })); +export type BulkCreateInput = typeof BulkCreateInput.Type; + +// Output Schema +export const BulkCreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Array( + Schema.Struct({ + object: Schema.Literals(["waitlist_entry"]), + id: Schema.String, + email_address: Schema.String, + status: Schema.Literals(["pending", "invited", "rejected", "completed"]), + is_locked: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + invitation: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["invitation"]), + id: Schema.String, + email_address: Schema.String, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + revoked: Schema.optional(Schema.Boolean), + status: Schema.Literals([ + "pending", + "accepted", + "revoked", + "expired", + ]), + url: Schema.optional(Schema.String), + expires_at: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + }), +); +export type BulkCreateOutput = typeof BulkCreateOutput.Type; + +// The operation +/** + * Create multiple waitlist entries + * + * Creates multiple waitlist entries for the provided email addresses. + * You can choose whether to send confirmation emails by setting the `notify` parameter to `true` or `false` for each entry. + * If the `notify` parameter is omitted, it defaults to `true`. + * If an email address is already on the waitlist, no new entry will be created and the existing waitlist entry will be returned. + * Duplicate email addresses within the same request are not allowed. + * This endpoint is limited to a maximum of 50 entries per API call. If you need to add more entries, please make multiple requests. + */ +export const bulkCreate = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: BulkCreateInput, + outputSchema: BulkCreateOutput, + errors: [BadRequest, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/waitlistEntries/create.ts b/packages/clerk/src/operations/backend/waitlistEntries/create.ts new file mode 100644 index 0000000000..06f7afa362 --- /dev/null +++ b/packages/clerk/src/operations/backend/waitlistEntries/create.ts @@ -0,0 +1,52 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const CreateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + email_address: Schema.String, + notify: Schema.optional(Schema.NullOr(Schema.Boolean)), +}).pipe(T.Http({ method: "POST", path: "/waitlist_entries" })); +export type CreateInput = typeof CreateInput.Type; + +// Output Schema +export const CreateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["waitlist_entry"]), + id: Schema.String, + email_address: Schema.String, + status: Schema.Literals(["pending", "invited", "rejected", "completed"]), + is_locked: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + invitation: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["invitation"]), + id: Schema.String, + email_address: Schema.String, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + revoked: Schema.optional(Schema.Boolean), + status: Schema.Literals(["pending", "accepted", "revoked", "expired"]), + url: Schema.optional(Schema.String), + expires_at: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), +}); +export type CreateOutput = typeof CreateOutput.Type; + +// The operation +/** + * Create a waitlist entry + * + * Creates a new waitlist entry for the given email address. + * If the email address is already on the waitlist, no new entry will be created and the existing waitlist entry will be returned. + */ +export const create = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInput, + outputSchema: CreateOutput, + errors: [BadRequest, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/waitlistEntries/delete.ts b/packages/clerk/src/operations/backend/waitlistEntries/delete.ts new file mode 100644 index 0000000000..6e3456f602 --- /dev/null +++ b/packages/clerk/src/operations/backend/waitlistEntries/delete.ts @@ -0,0 +1,42 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + NotFound, + Conflict, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const DeleteInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + waitlist_entry_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ method: "DELETE", path: "/waitlist_entries/{waitlist_entry_id}" }), +); +export type DeleteInput = typeof DeleteInput.Type; + +// Output Schema +export const DeleteOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), +}); +export type DeleteOutput = typeof DeleteOutput.Type; + +// The operation +/** + * Delete a pending waitlist entry + * + * Delete a pending waitlist entry. + * + * @param waitlist_entry_id - The ID of the waitlist entry to delete + */ +const delete_ = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteInput, + outputSchema: DeleteOutput, + errors: [BadRequest, NotFound, Conflict, UnprocessableEntity] as const, +})); +export { delete_ as delete }; diff --git a/packages/clerk/src/operations/backend/waitlistEntries/index.ts b/packages/clerk/src/operations/backend/waitlistEntries/index.ts new file mode 100644 index 0000000000..4581579db9 --- /dev/null +++ b/packages/clerk/src/operations/backend/waitlistEntries/index.ts @@ -0,0 +1,6 @@ +export * from "./list.ts"; +export * from "./create.ts"; +export * from "./bulkCreate.ts"; +export * from "./delete.ts"; +export * from "./invite.ts"; +export * from "./reject.ts"; diff --git a/packages/clerk/src/operations/backend/waitlistEntries/invite.ts b/packages/clerk/src/operations/backend/waitlistEntries/invite.ts new file mode 100644 index 0000000000..7194f50ac1 --- /dev/null +++ b/packages/clerk/src/operations/backend/waitlistEntries/invite.ts @@ -0,0 +1,63 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + NotFound, + Conflict, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const InviteInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + waitlist_entry_id: Schema.String.pipe(T.PathParam()), + ignore_existing: Schema.optional(Schema.NullOr(Schema.Boolean)), +}).pipe( + T.Http({ + method: "POST", + path: "/waitlist_entries/{waitlist_entry_id}/invite", + }), +); +export type InviteInput = typeof InviteInput.Type; + +// Output Schema +export const InviteOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["waitlist_entry"]), + id: Schema.String, + email_address: Schema.String, + status: Schema.Literals(["pending", "invited", "rejected", "completed"]), + is_locked: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + invitation: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["invitation"]), + id: Schema.String, + email_address: Schema.String, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + revoked: Schema.optional(Schema.Boolean), + status: Schema.Literals(["pending", "accepted", "revoked", "expired"]), + url: Schema.optional(Schema.String), + expires_at: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), +}); +export type InviteOutput = typeof InviteOutput.Type; + +// The operation +/** + * Invite a waitlist entry + * + * Send an invite to the email address in a waitlist entry. + * + * @param waitlist_entry_id - The ID of the waitlist entry to invite + */ +export const invite = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: InviteInput, + outputSchema: InviteOutput, + errors: [BadRequest, NotFound, Conflict, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/waitlistEntries/list.ts b/packages/clerk/src/operations/backend/waitlistEntries/list.ts new file mode 100644 index 0000000000..956eb407dd --- /dev/null +++ b/packages/clerk/src/operations/backend/waitlistEntries/list.ts @@ -0,0 +1,80 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; + +// Input Schema +export const ListInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), + query: Schema.optional(Schema.String), + status: Schema.optional( + Schema.Literals(["pending", "invited", "completed", "rejected"]), + ), + order_by: Schema.optional(Schema.String), +}).pipe(T.Http({ method: "GET", path: "/waitlist_entries" })); +export type ListInput = typeof ListInput.Type; + +// Output Schema +export const ListOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["waitlist_entry"]), + id: Schema.String, + email_address: Schema.String, + status: Schema.Literals(["pending", "invited", "rejected", "completed"]), + is_locked: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + invitation: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["invitation"]), + id: Schema.String, + email_address: Schema.String, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + revoked: Schema.optional(Schema.Boolean), + status: Schema.Literals([ + "pending", + "accepted", + "revoked", + "expired", + ]), + url: Schema.optional(Schema.String), + expires_at: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + }), + ), + total_count: Schema.Number, +}); +export type ListOutput = typeof ListOutput.Type; + +// The operation +/** + * List all waitlist entries + * + * Retrieve a list of waitlist entries for the instance. + * Entries are ordered by creation date in descending order by default. + * Supports filtering by email address or status and pagination with limit and offset parameters. + * + * @param limit - Applies a limit to the number of results returned. +Can be used for paginating the results together with `offset`. + * @param offset - Skip the first `offset` results when paginating. +Needs to be an integer greater or equal to zero. +To be used in conjunction with `limit`. + * @param query - Filter waitlist entries by `email_address` or `id` + * @param status - Filter waitlist entries by their status + * @param order_by - Specify the order of results. Supported values are: +- `created_at` +- `email_address` +- `invited_at` + +Use `+` for ascending or `-` for descending order. Defaults to `-created_at`. + */ +export const list = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInput, + outputSchema: ListOutput, +})); diff --git a/packages/clerk/src/operations/backend/waitlistEntries/reject.ts b/packages/clerk/src/operations/backend/waitlistEntries/reject.ts new file mode 100644 index 0000000000..1a14a5ec08 --- /dev/null +++ b/packages/clerk/src/operations/backend/waitlistEntries/reject.ts @@ -0,0 +1,62 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + NotFound, + Conflict, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const RejectInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + waitlist_entry_id: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "POST", + path: "/waitlist_entries/{waitlist_entry_id}/reject", + }), +); +export type RejectInput = typeof RejectInput.Type; + +// Output Schema +export const RejectOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["waitlist_entry"]), + id: Schema.String, + email_address: Schema.String, + status: Schema.Literals(["pending", "invited", "rejected", "completed"]), + is_locked: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + invitation: Schema.optional( + Schema.NullOr( + Schema.Struct({ + object: Schema.Literals(["invitation"]), + id: Schema.String, + email_address: Schema.String, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + revoked: Schema.optional(Schema.Boolean), + status: Schema.Literals(["pending", "accepted", "revoked", "expired"]), + url: Schema.optional(Schema.String), + expires_at: Schema.optional(Schema.NullOr(Schema.Number)), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), +}); +export type RejectOutput = typeof RejectOutput.Type; + +// The operation +/** + * Reject a waitlist entry + * + * Reject a waitlist entry. + * + * @param waitlist_entry_id - The ID of the waitlist entry to reject + */ +export const reject = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: RejectInput, + outputSchema: RejectOutput, + errors: [BadRequest, NotFound, Conflict, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/backend/webhooks/createSvixApp.ts b/packages/clerk/src/operations/backend/webhooks/createSvixApp.ts new file mode 100644 index 0000000000..1f84dd24e3 --- /dev/null +++ b/packages/clerk/src/operations/backend/webhooks/createSvixApp.ts @@ -0,0 +1,28 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest } from "../../../errors.ts"; + +// Input Schema +export const CreateSvixAppInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + {}, +).pipe(T.Http({ method: "POST", path: "/webhooks/svix" })); +export type CreateSvixAppInput = typeof CreateSvixAppInput.Type; + +// Output Schema +export const CreateSvixAppOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + svix_url: Schema.String, +}); +export type CreateSvixAppOutput = typeof CreateSvixAppOutput.Type; + +// The operation +/** + * Create a Svix app + * + * Create a Svix app and associate it with the current instance + */ +export const createSvixApp = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateSvixAppInput, + outputSchema: CreateSvixAppOutput, + errors: [BadRequest] as const, +})); diff --git a/packages/clerk/src/operations/backend/webhooks/deleteSvixApp.ts b/packages/clerk/src/operations/backend/webhooks/deleteSvixApp.ts new file mode 100644 index 0000000000..39da4f9bf4 --- /dev/null +++ b/packages/clerk/src/operations/backend/webhooks/deleteSvixApp.ts @@ -0,0 +1,26 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest } from "../../../errors.ts"; + +// Input Schema +export const DeleteSvixAppInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + {}, +).pipe(T.Http({ method: "DELETE", path: "/webhooks/svix" })); +export type DeleteSvixAppInput = typeof DeleteSvixAppInput.Type; + +// Output Schema +export const DeleteSvixAppOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Void; +export type DeleteSvixAppOutput = typeof DeleteSvixAppOutput.Type; + +// The operation +/** + * Delete a Svix app + * + * Delete a Svix app and disassociate it from the current instance + */ +export const deleteSvixApp = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteSvixAppInput, + outputSchema: DeleteSvixAppOutput, + errors: [BadRequest] as const, +})); diff --git a/packages/clerk/src/operations/backend/webhooks/generateSvixAuthURL.ts b/packages/clerk/src/operations/backend/webhooks/generateSvixAuthURL.ts new file mode 100644 index 0000000000..f1b1a17fbe --- /dev/null +++ b/packages/clerk/src/operations/backend/webhooks/generateSvixAuthURL.ts @@ -0,0 +1,30 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../backend-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest } from "../../../errors.ts"; + +// Input Schema +export const GenerateSvixAuthURLInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({}).pipe( + T.Http({ method: "POST", path: "/webhooks/svix_url" }), + ); +export type GenerateSvixAuthURLInput = typeof GenerateSvixAuthURLInput.Type; + +// Output Schema +export const GenerateSvixAuthURLOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + svix_url: Schema.String, + }); +export type GenerateSvixAuthURLOutput = typeof GenerateSvixAuthURLOutput.Type; + +// The operation +/** + * Create a Svix Dashboard URL + * + * Generate a new URL for accessing the Svix's management dashboard for that particular instance + */ +export const generateSvixAuthURL = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GenerateSvixAuthURLInput, + outputSchema: GenerateSvixAuthURLOutput, + errors: [BadRequest] as const, +})); diff --git a/packages/clerk/src/operations/backend/webhooks/index.ts b/packages/clerk/src/operations/backend/webhooks/index.ts new file mode 100644 index 0000000000..de7741a655 --- /dev/null +++ b/packages/clerk/src/operations/backend/webhooks/index.ts @@ -0,0 +1,3 @@ +export * from "./createSvixApp.ts"; +export * from "./deleteSvixApp.ts"; +export * from "./generateSvixAuthURL.ts"; diff --git a/packages/clerk/src/operations/index.ts b/packages/clerk/src/operations/index.ts new file mode 100644 index 0000000000..765027f84d --- /dev/null +++ b/packages/clerk/src/operations/index.ts @@ -0,0 +1,2 @@ +export * as Backend from "./backend/index.ts"; +export * as Platform from "./platform/index.ts"; diff --git a/packages/clerk/src/operations/platform/applicationTransfers/cancelApplicationTransfer.ts b/packages/clerk/src/operations/platform/applicationTransfers/cancelApplicationTransfer.ts new file mode 100644 index 0000000000..a376d40099 --- /dev/null +++ b/packages/clerk/src/operations/platform/applicationTransfers/cancelApplicationTransfer.ts @@ -0,0 +1,51 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, NotFound, Conflict } from "../../../errors.ts"; + +// Input Schema +export const CancelApplicationTransferInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + transferID: Schema.String.pipe(T.PathParam()), + }).pipe( + T.Http({ + method: "DELETE", + path: "/platform/applications/{applicationID}/transfers/{transferID}", + }), + ); +export type CancelApplicationTransferInput = + typeof CancelApplicationTransferInput.Type; + +// Output Schema +export const CancelApplicationTransferOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["application_transfer"]), + id: Schema.String, + code: Schema.String, + application_id: Schema.String, + status: Schema.Literals(["pending", "completed", "canceled", "expired"]), + expires_at: Schema.String, + created_at: Schema.String, + canceled_at: Schema.NullOr(Schema.String), + completed_at: Schema.NullOr(Schema.String), + }); +export type CancelApplicationTransferOutput = + typeof CancelApplicationTransferOutput.Type; + +// The operation +/** + * Cancel an application transfer + * + * Cancel an existing application transfer. Only transfers in 'pending' status can be canceled. + * + * @param applicationID - Application ID. + * @param transferID - Application Transfer ID. + */ +export const cancelApplicationTransfer = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: CancelApplicationTransferInput, + outputSchema: CancelApplicationTransferOutput, + errors: [Forbidden, NotFound, Conflict] as const, + }), +); diff --git a/packages/clerk/src/operations/platform/applicationTransfers/createApplicationTransfer.ts b/packages/clerk/src/operations/platform/applicationTransfers/createApplicationTransfer.ts new file mode 100644 index 0000000000..4058eeb626 --- /dev/null +++ b/packages/clerk/src/operations/platform/applicationTransfers/createApplicationTransfer.ts @@ -0,0 +1,52 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, Conflict } from "../../../errors.ts"; + +// Input Schema +export const CreateApplicationTransferInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + }).pipe( + T.Http({ + method: "POST", + path: "/platform/applications/{applicationID}/transfers", + }), + ); +export type CreateApplicationTransferInput = + typeof CreateApplicationTransferInput.Type; + +// Output Schema +export const CreateApplicationTransferOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["application_transfer"]), + id: Schema.String, + code: Schema.String, + application_id: Schema.String, + status: Schema.Literals(["pending", "completed", "canceled", "expired"]), + expires_at: Schema.String, + created_at: Schema.String, + canceled_at: Schema.NullOr(Schema.String), + completed_at: Schema.NullOr(Schema.String), + }); +export type CreateApplicationTransferOutput = + typeof CreateApplicationTransferOutput.Type; + +// The operation +/** + * Create an application transfer + * + * Create a new transfer request for an application. This initiates the process of + * transferring ownership of the application to another workspace. + * Only one pending transfer can exist for an application at a time. The transfer + * will expire after 24 hours if not completed. + * + * @param applicationID - Application ID. + */ +export const createApplicationTransfer = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: CreateApplicationTransferInput, + outputSchema: CreateApplicationTransferOutput, + errors: [Forbidden, Conflict] as const, + }), +); diff --git a/packages/clerk/src/operations/platform/applicationTransfers/getApplicationTransfer.ts b/packages/clerk/src/operations/platform/applicationTransfers/getApplicationTransfer.ts new file mode 100644 index 0000000000..def789c45e --- /dev/null +++ b/packages/clerk/src/operations/platform/applicationTransfers/getApplicationTransfer.ts @@ -0,0 +1,51 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetApplicationTransferInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + transferID: Schema.String.pipe(T.PathParam()), + }).pipe( + T.Http({ + method: "GET", + path: "/platform/applications/{applicationID}/transfers/{transferID}", + }), + ); +export type GetApplicationTransferInput = + typeof GetApplicationTransferInput.Type; + +// Output Schema +export const GetApplicationTransferOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["application_transfer"]), + id: Schema.String, + code: Schema.String, + application_id: Schema.String, + status: Schema.Literals(["pending", "completed", "canceled", "expired"]), + expires_at: Schema.String, + created_at: Schema.String, + canceled_at: Schema.NullOr(Schema.String), + completed_at: Schema.NullOr(Schema.String), + }); +export type GetApplicationTransferOutput = + typeof GetApplicationTransferOutput.Type; + +// The operation +/** + * Get an application transfer + * + * Retrieve details of an application transfer by its ID. + * + * @param applicationID - Application ID. + * @param transferID - Application Transfer ID. + */ +export const getApplicationTransfer = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: GetApplicationTransferInput, + outputSchema: GetApplicationTransferOutput, + errors: [Forbidden, NotFound] as const, + }), +); diff --git a/packages/clerk/src/operations/platform/applicationTransfers/index.ts b/packages/clerk/src/operations/platform/applicationTransfers/index.ts new file mode 100644 index 0000000000..c62f0d180f --- /dev/null +++ b/packages/clerk/src/operations/platform/applicationTransfers/index.ts @@ -0,0 +1,4 @@ +export * from "./listApplicationTransfers.ts"; +export * from "./createApplicationTransfer.ts"; +export * from "./getApplicationTransfer.ts"; +export * from "./cancelApplicationTransfer.ts"; diff --git a/packages/clerk/src/operations/platform/applicationTransfers/listApplicationTransfers.ts b/packages/clerk/src/operations/platform/applicationTransfers/listApplicationTransfers.ts new file mode 100644 index 0000000000..decdc160e2 --- /dev/null +++ b/packages/clerk/src/operations/platform/applicationTransfers/listApplicationTransfers.ts @@ -0,0 +1,68 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ListApplicationTransfersInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + status: Schema.optional(Schema.String), + limit: Schema.optional(Schema.Number), + starting_after: Schema.optional(Schema.String), + ending_before: Schema.optional(Schema.String), + }).pipe(T.Http({ method: "GET", path: "/platform/application_transfers" })); +export type ListApplicationTransfersInput = + typeof ListApplicationTransfersInput.Type; + +// Output Schema +export const ListApplicationTransfersOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["application_transfer"]), + id: Schema.String, + code: Schema.String, + application_id: Schema.String, + status: Schema.Literals([ + "pending", + "completed", + "canceled", + "expired", + ]), + expires_at: Schema.String, + created_at: Schema.String, + canceled_at: Schema.NullOr(Schema.String), + completed_at: Schema.NullOr(Schema.String), + }), + ), + total_count: Schema.Number, + }); +export type ListApplicationTransfersOutput = + typeof ListApplicationTransfersOutput.Type; + +// The operation +/** + * List application transfers + * + * List all transfer requests created by the authenticated workspace. Returns + * transfers sorted by creation date in descending order (most recent first). + * Use the `status` parameter to filter by transfer status. + * + * @param status - Filter by transfer status. Multiple values can be provided by repeating +the parameter (e.g., `?status=pending&status=canceled`). + + * @param limit - Number of results to return per page (1-500, default 10). + * @param starting_after - Cursor for pagination. Provide the ID of the last transfer from the +previous page to get the next page of results. + + * @param ending_before - Cursor for pagination. Provide the ID of the first transfer from the +previous page to get the previous page of results. + + */ +export const listApplicationTransfers = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: ListApplicationTransfersInput, + outputSchema: ListApplicationTransfersOutput, + errors: [Forbidden, UnprocessableEntity] as const, + }), +); diff --git a/packages/clerk/src/operations/platform/applications/claimAccountlessApplication.ts b/packages/clerk/src/operations/platform/applications/claimAccountlessApplication.ts new file mode 100644 index 0000000000..d9cb0c68b2 --- /dev/null +++ b/packages/clerk/src/operations/platform/applications/claimAccountlessApplication.ts @@ -0,0 +1,57 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; +import { SensitiveOutputString } from "../../../sensitive.ts"; + +// Input Schema +export const ClaimAccountlessApplicationInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + token: Schema.String, + name: Schema.String, + }).pipe( + T.Http({ + method: "POST", + path: "/platform/accountless_applications/claim", + }), + ); +export type ClaimAccountlessApplicationInput = + typeof ClaimAccountlessApplicationInput.Type; + +// Output Schema +export const ClaimAccountlessApplicationOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + application_id: Schema.String, + name: Schema.String, + instances: Schema.Array( + Schema.Struct({ + instance_id: Schema.String, + environment_type: Schema.Literals(["development", "production"]), + secret_key: Schema.optional(SensitiveOutputString), + publishable_key: Schema.optional(Schema.String), + }), + ), + }); +export type ClaimAccountlessApplicationOutput = + typeof ClaimAccountlessApplicationOutput.Type; + +// The operation +/** + * Claim an accountless application + * + * Claim an accountless application by assigning it to the authenticated principal's workspace. + * The application is identified by a single-use claim token that was generated when the + * accountless application was created. Once claimed, the token is invalidated and cannot be reused. + */ +export const claimAccountlessApplication = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: ClaimAccountlessApplicationInput, + outputSchema: ClaimAccountlessApplicationOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, + }), +); diff --git a/packages/clerk/src/operations/platform/applications/createApplication.ts b/packages/clerk/src/operations/platform/applications/createApplication.ts new file mode 100644 index 0000000000..1a9b55058c --- /dev/null +++ b/packages/clerk/src/operations/platform/applications/createApplication.ts @@ -0,0 +1,45 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, UnprocessableEntity } from "../../../errors.ts"; +import { SensitiveOutputString } from "../../../sensitive.ts"; + +// Input Schema +export const CreateApplicationInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + { + name: Schema.String, + domain: Schema.optional(Schema.String), + proxy_path: Schema.optional(Schema.String), + environment_types: Schema.optional(Schema.Array(Schema.String)), + template: Schema.optional(Schema.String), + }, +).pipe(T.Http({ method: "POST", path: "/platform/applications" })); +export type CreateApplicationInput = typeof CreateApplicationInput.Type; + +// Output Schema +export const CreateApplicationOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + application_id: Schema.String, + name: Schema.String, + instances: Schema.Array( + Schema.Struct({ + instance_id: Schema.String, + environment_type: Schema.Literals(["development", "production"]), + secret_key: Schema.optional(SensitiveOutputString), + publishable_key: Schema.optional(Schema.String), + }), + ), + }); +export type CreateApplicationOutput = typeof CreateApplicationOutput.Type; + +// The operation +/** + * Create an application + * + * Create a new application. + */ +export const createApplication = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateApplicationInput, + outputSchema: CreateApplicationOutput, + errors: [BadRequest, Forbidden, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/platform/applications/createInstance.ts b/packages/clerk/src/operations/platform/applications/createInstance.ts new file mode 100644 index 0000000000..d3c56cfa9f --- /dev/null +++ b/packages/clerk/src/operations/platform/applications/createInstance.ts @@ -0,0 +1,97 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; +import { SensitiveOutputString } from "../../../sensitive.ts"; + +// Input Schema +export const CreateInstanceInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + domain: Schema.String, + environment_type: Schema.optional(Schema.Literals(["production"])), + clone_instance_id: Schema.optional(Schema.NullOr(Schema.String)), +}).pipe( + T.Http({ + method: "POST", + path: "/platform/applications/{applicationID}/instances", + }), +); +export type CreateInstanceInput = typeof CreateInstanceInput.Type; + +// Output Schema +export const CreateInstanceOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["instance"]), + id: Schema.String, + environment_type: Schema.Literals(["production"]), + active_domain: Schema.Struct({ + object: Schema.Literals(["domain"]), + id: Schema.String, + name: Schema.String, + is_satellite: Schema.optional(Schema.Boolean), + is_provider_domain: Schema.optional(Schema.Boolean), + frontend_api_url: Schema.String, + development_origin: Schema.String, + accounts_portal_url: Schema.optional(Schema.String), + proxy_url: Schema.optional(Schema.String), + cname_targets: Schema.optional( + Schema.Array( + Schema.Struct({ + host: Schema.optional(Schema.String), + value: Schema.optional(Schema.String), + required: Schema.optional(Schema.Boolean), + }), + ), + ), + created_at: Schema.String, + updated_at: Schema.String, + application: Schema.optional( + Schema.Struct({ + object: Schema.Literals(["application"]), + id: Schema.String, + created_at: Schema.String, + updated_at: Schema.String, + }), + ), + instance: Schema.optional( + Schema.Struct({ + object: Schema.Literals(["instance"]), + id: Schema.String, + environment_type: Schema.Literals(["production", "development"]), + created_at: Schema.String, + updated_at: Schema.String, + }), + ), + }), + secret_key: SensitiveOutputString, + publishable_key: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type CreateInstanceOutput = typeof CreateInstanceOutput.Type; + +// The operation +/** + * Create instance + * + * Creates a production instance for the given application. Optionally clones + * configuration from an existing development instance. + * + * @param applicationID - Application ID. + */ +export const createInstance = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateInstanceInput, + outputSchema: CreateInstanceOutput, + errors: [ + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + UnprocessableEntity, + ] as const, +})); diff --git a/packages/clerk/src/operations/platform/applications/deleteApplication.ts b/packages/clerk/src/operations/platform/applications/deleteApplication.ts new file mode 100644 index 0000000000..23fcfbf8e9 --- /dev/null +++ b/packages/clerk/src/operations/platform/applications/deleteApplication.ts @@ -0,0 +1,37 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteApplicationInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + { + applicationID: Schema.String.pipe(T.PathParam()), + }, +).pipe( + T.Http({ method: "DELETE", path: "/platform/applications/{applicationID}" }), +); +export type DeleteApplicationInput = typeof DeleteApplicationInput.Type; + +// Output Schema +export const DeleteApplicationOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + deleted: Schema.Boolean, + object: Schema.String, + id: Schema.String, + }); +export type DeleteApplicationOutput = typeof DeleteApplicationOutput.Type; + +// The operation +/** + * Delete an application + * + * Delete an application. + * + * @param applicationID - Application ID. + */ +export const deleteApplication = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteApplicationInput, + outputSchema: DeleteApplicationOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/platform/applications/deleteApplicationFavicon.ts b/packages/clerk/src/operations/platform/applications/deleteApplicationFavicon.ts new file mode 100644 index 0000000000..26e37dbfa0 --- /dev/null +++ b/packages/clerk/src/operations/platform/applications/deleteApplicationFavicon.ts @@ -0,0 +1,51 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; +import { SensitiveOutputString } from "../../../sensitive.ts"; + +// Input Schema +export const DeleteApplicationFaviconInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + }).pipe( + T.Http({ + method: "DELETE", + path: "/platform/applications/{applicationID}/favicon", + }), + ); +export type DeleteApplicationFaviconInput = + typeof DeleteApplicationFaviconInput.Type; + +// Output Schema +export const DeleteApplicationFaviconOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + application_id: Schema.String, + name: Schema.String, + instances: Schema.Array( + Schema.Struct({ + instance_id: Schema.String, + environment_type: Schema.Literals(["development", "production"]), + secret_key: Schema.optional(SensitiveOutputString), + publishable_key: Schema.optional(Schema.String), + }), + ), + }); +export type DeleteApplicationFaviconOutput = + typeof DeleteApplicationFaviconOutput.Type; + +// The operation +/** + * Delete application favicon + * + * Delete an application's favicon. + * + * @param applicationID - Application ID. + */ +export const deleteApplicationFavicon = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: DeleteApplicationFaviconInput, + outputSchema: DeleteApplicationFaviconOutput, + errors: [BadRequest, Forbidden, NotFound] as const, + }), +); diff --git a/packages/clerk/src/operations/platform/applications/deleteApplicationLogo.ts b/packages/clerk/src/operations/platform/applications/deleteApplicationLogo.ts new file mode 100644 index 0000000000..c5d8fde122 --- /dev/null +++ b/packages/clerk/src/operations/platform/applications/deleteApplicationLogo.ts @@ -0,0 +1,50 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; +import { SensitiveOutputString } from "../../../sensitive.ts"; + +// Input Schema +export const DeleteApplicationLogoInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + }).pipe( + T.Http({ + method: "DELETE", + path: "/platform/applications/{applicationID}/logo", + }), + ); +export type DeleteApplicationLogoInput = typeof DeleteApplicationLogoInput.Type; + +// Output Schema +export const DeleteApplicationLogoOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + application_id: Schema.String, + name: Schema.String, + instances: Schema.Array( + Schema.Struct({ + instance_id: Schema.String, + environment_type: Schema.Literals(["development", "production"]), + secret_key: Schema.optional(SensitiveOutputString), + publishable_key: Schema.optional(Schema.String), + }), + ), + }); +export type DeleteApplicationLogoOutput = + typeof DeleteApplicationLogoOutput.Type; + +// The operation +/** + * Delete application logo + * + * Delete an application's logo. + * + * @param applicationID - Application ID. + */ +export const deleteApplicationLogo = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: DeleteApplicationLogoInput, + outputSchema: DeleteApplicationLogoOutput, + errors: [BadRequest, Forbidden, NotFound] as const, + }), +); diff --git a/packages/clerk/src/operations/platform/applications/getApplication.ts b/packages/clerk/src/operations/platform/applications/getApplication.ts new file mode 100644 index 0000000000..68f1181609 --- /dev/null +++ b/packages/clerk/src/operations/platform/applications/getApplication.ts @@ -0,0 +1,44 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; +import { SensitiveOutputString } from "../../../sensitive.ts"; + +// Input Schema +export const GetApplicationInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + include_secret_keys: Schema.optional(Schema.Boolean), +}).pipe( + T.Http({ method: "GET", path: "/platform/applications/{applicationID}" }), +); +export type GetApplicationInput = typeof GetApplicationInput.Type; + +// Output Schema +export const GetApplicationOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + application_id: Schema.String, + name: Schema.String, + instances: Schema.Array( + Schema.Struct({ + instance_id: Schema.String, + environment_type: Schema.Literals(["development", "production"]), + secret_key: Schema.optional(SensitiveOutputString), + publishable_key: Schema.optional(Schema.String), + }), + ), +}); +export type GetApplicationOutput = typeof GetApplicationOutput.Type; + +// The operation +/** + * Get an application + * + * Get application details. + * + * @param applicationID - Application ID. + * @param include_secret_keys - Whether to include secret keys in the response. If 'true', the response will include the secret keys for each instance. + */ +export const getApplication = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetApplicationInput, + outputSchema: GetApplicationOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/platform/applications/getInstanceUsage.ts b/packages/clerk/src/operations/platform/applications/getInstanceUsage.ts new file mode 100644 index 0000000000..7ccec94586 --- /dev/null +++ b/packages/clerk/src/operations/platform/applications/getInstanceUsage.ts @@ -0,0 +1,92 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, NotFound, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const GetInstanceUsageInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + envOrInsID: Schema.String.pipe(T.PathParam()), + start: Schema.optional(Schema.String), + end: Schema.optional(Schema.String), +}).pipe( + T.Http({ + method: "GET", + path: "/platform/applications/{applicationID}/instances/{envOrInsID}/usage", + }), +); +export type GetInstanceUsageInput = typeof GetInstanceUsageInput.Type; + +// Output Schema +export const GetInstanceUsageOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + { + application_id: Schema.String, + instance_id: Schema.String, + period_start: Schema.String, + period_end: Schema.String, + mau: Schema.Struct({ + total_usage: Schema.Number, + total_billable_usage: Schema.Number, + }), + mao: Schema.Struct({ + total_usage: Schema.Number, + total_billable_usage: Schema.Number, + }), + custom_domains: Schema.Struct({ + total_usage: Schema.Number, + }), + sms_messages: Schema.Struct({ + total_usage: Schema.Number, + tier_a: Schema.Struct({ + total_usage: Schema.Number, + }), + tier_b: Schema.Struct({ + total_usage: Schema.Number, + }), + tier_c: Schema.Struct({ + total_usage: Schema.Number, + }), + tier_d: Schema.Struct({ + total_usage: Schema.Number, + }), + tier_e: Schema.Struct({ + total_usage: Schema.Number, + }), + tier_f: Schema.Struct({ + total_usage: Schema.Number, + }), + }), + enterprise_connections: Schema.Struct({ + total_usage: Schema.Number, + }), + }, +); +export type GetInstanceUsageOutput = typeof GetInstanceUsageOutput.Type; + +// The operation +/** + * Get instance usage + * + * Get usage totals for an application instance across meters such as MAU, MAO, + * SMS, custom domains, and enterprise connections. + * The `envOrInsID` parameter can be either an environment type (e.g., "development", "production") + * or an instance ID. + * Requires the `application_usage:read` scope. + * + * @param applicationID - Application ID. + * @param envOrInsID - Environment type (e.g., "development", "production") or instance ID. + + * @param start - Start of the date range for usage data in YYYY-MM-DD format. +Both `start` and `end` must be provided together. +When omitted, the current billing cycle is used. + + * @param end - Inclusive end of the date range for usage data in YYYY-MM-DD format. +Both `start` and `end` must be provided together. +When omitted, the current billing cycle is used. + + */ +export const getInstanceUsage = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetInstanceUsageInput, + outputSchema: GetInstanceUsageOutput, + errors: [Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/platform/applications/index.ts b/packages/clerk/src/operations/platform/applications/index.ts new file mode 100644 index 0000000000..6837c12c6b --- /dev/null +++ b/packages/clerk/src/operations/platform/applications/index.ts @@ -0,0 +1,12 @@ +export * from "./listApplications.ts"; +export * from "./createApplication.ts"; +export * from "./getApplication.ts"; +export * from "./updateApplication.ts"; +export * from "./deleteApplication.ts"; +export * from "./uploadApplicationLogo.ts"; +export * from "./deleteApplicationLogo.ts"; +export * from "./uploadApplicationFavicon.ts"; +export * from "./deleteApplicationFavicon.ts"; +export * from "./getInstanceUsage.ts"; +export * from "./createInstance.ts"; +export * from "./claimAccountlessApplication.ts"; diff --git a/packages/clerk/src/operations/platform/applications/listApplications.ts b/packages/clerk/src/operations/platform/applications/listApplications.ts new file mode 100644 index 0000000000..fdf866e8f0 --- /dev/null +++ b/packages/clerk/src/operations/platform/applications/listApplications.ts @@ -0,0 +1,42 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden } from "../../../errors.ts"; +import { SensitiveOutputString } from "../../../sensitive.ts"; + +// Input Schema +export const ListApplicationsInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + include_secret_keys: Schema.optional(Schema.Boolean), +}).pipe(T.Http({ method: "GET", path: "/platform/applications" })); +export type ListApplicationsInput = typeof ListApplicationsInput.Type; + +// Output Schema +export const ListApplicationsOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Array( + Schema.Struct({ + application_id: Schema.String, + name: Schema.String, + instances: Schema.Array( + Schema.Struct({ + instance_id: Schema.String, + environment_type: Schema.Literals(["development", "production"]), + secret_key: Schema.optional(SensitiveOutputString), + publishable_key: Schema.optional(Schema.String), + }), + ), + }), +); +export type ListApplicationsOutput = typeof ListApplicationsOutput.Type; + +// The operation +/** + * List applications + * + * List applications. + * + * @param include_secret_keys - Whether to include secret keys in the response. + */ +export const listApplications = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListApplicationsInput, + outputSchema: ListApplicationsOutput, + errors: [BadRequest, Forbidden] as const, +})); diff --git a/packages/clerk/src/operations/platform/applications/updateApplication.ts b/packages/clerk/src/operations/platform/applications/updateApplication.ts new file mode 100644 index 0000000000..5e8caa9fc2 --- /dev/null +++ b/packages/clerk/src/operations/platform/applications/updateApplication.ts @@ -0,0 +1,51 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; +import { SensitiveOutputString } from "../../../sensitive.ts"; + +// Input Schema +export const UpdateApplicationInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + { + applicationID: Schema.String.pipe(T.PathParam()), + name: Schema.optional(Schema.String), + }, +).pipe( + T.Http({ method: "PATCH", path: "/platform/applications/{applicationID}" }), +); +export type UpdateApplicationInput = typeof UpdateApplicationInput.Type; + +// Output Schema +export const UpdateApplicationOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + application_id: Schema.String, + name: Schema.String, + instances: Schema.Array( + Schema.Struct({ + instance_id: Schema.String, + environment_type: Schema.Literals(["development", "production"]), + secret_key: Schema.optional(SensitiveOutputString), + publishable_key: Schema.optional(Schema.String), + }), + ), + }); +export type UpdateApplicationOutput = typeof UpdateApplicationOutput.Type; + +// The operation +/** + * Update an application + * + * Update an application. + * + * @param applicationID - Application ID. + */ +export const updateApplication = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateApplicationInput, + outputSchema: UpdateApplicationOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/platform/applications/uploadApplicationFavicon.ts b/packages/clerk/src/operations/platform/applications/uploadApplicationFavicon.ts new file mode 100644 index 0000000000..7f66f91e7b --- /dev/null +++ b/packages/clerk/src/operations/platform/applications/uploadApplicationFavicon.ts @@ -0,0 +1,65 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + PayloadTooLarge, + UnprocessableEntity, +} from "../../../errors.ts"; +import { SensitiveOutputString } from "../../../sensitive.ts"; + +// Input Schema +export const UploadApplicationFaviconInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + file: Schema.String, + }).pipe( + T.Http({ + method: "POST", + path: "/platform/applications/{applicationID}/favicon", + contentType: "multipart", + }), + ); +export type UploadApplicationFaviconInput = + typeof UploadApplicationFaviconInput.Type; + +// Output Schema +export const UploadApplicationFaviconOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + application_id: Schema.String, + name: Schema.String, + instances: Schema.Array( + Schema.Struct({ + instance_id: Schema.String, + environment_type: Schema.Literals(["development", "production"]), + secret_key: Schema.optional(SensitiveOutputString), + publishable_key: Schema.optional(Schema.String), + }), + ), + }); +export type UploadApplicationFaviconOutput = + typeof UploadApplicationFaviconOutput.Type; + +// The operation +/** + * Upload application favicon + * + * Set or replace an application's favicon by uploading an image file. + * + * @param applicationID - Application ID. + */ +export const uploadApplicationFavicon = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: UploadApplicationFaviconInput, + outputSchema: UploadApplicationFaviconOutput, + errors: [ + BadRequest, + Forbidden, + NotFound, + PayloadTooLarge, + UnprocessableEntity, + ] as const, + }), +); diff --git a/packages/clerk/src/operations/platform/applications/uploadApplicationLogo.ts b/packages/clerk/src/operations/platform/applications/uploadApplicationLogo.ts new file mode 100644 index 0000000000..afd98cf866 --- /dev/null +++ b/packages/clerk/src/operations/platform/applications/uploadApplicationLogo.ts @@ -0,0 +1,64 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + PayloadTooLarge, + UnprocessableEntity, +} from "../../../errors.ts"; +import { SensitiveOutputString } from "../../../sensitive.ts"; + +// Input Schema +export const UploadApplicationLogoInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + file: Schema.String, + }).pipe( + T.Http({ + method: "POST", + path: "/platform/applications/{applicationID}/logo", + contentType: "multipart", + }), + ); +export type UploadApplicationLogoInput = typeof UploadApplicationLogoInput.Type; + +// Output Schema +export const UploadApplicationLogoOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + application_id: Schema.String, + name: Schema.String, + instances: Schema.Array( + Schema.Struct({ + instance_id: Schema.String, + environment_type: Schema.Literals(["development", "production"]), + secret_key: Schema.optional(SensitiveOutputString), + publishable_key: Schema.optional(Schema.String), + }), + ), + }); +export type UploadApplicationLogoOutput = + typeof UploadApplicationLogoOutput.Type; + +// The operation +/** + * Upload application logo + * + * Set or replace an application's logo by uploading an image file. + * + * @param applicationID - Application ID. + */ +export const uploadApplicationLogo = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: UploadApplicationLogoInput, + outputSchema: UploadApplicationLogoOutput, + errors: [ + BadRequest, + Forbidden, + NotFound, + PayloadTooLarge, + UnprocessableEntity, + ] as const, + }), +); diff --git a/packages/clerk/src/operations/platform/config/getConfig.ts b/packages/clerk/src/operations/platform/config/getConfig.ts new file mode 100644 index 0000000000..d4b2c18b8f --- /dev/null +++ b/packages/clerk/src/operations/platform/config/getConfig.ts @@ -0,0 +1,44 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetConfigInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + envOrInsID: Schema.String.pipe(T.PathParam()), + keys: Schema.optional(Schema.String), +}).pipe( + T.Http({ + method: "GET", + path: "/platform/applications/{applicationID}/instances/{envOrInsID}/config", + }), +); +export type GetConfigInput = typeof GetConfigInput.Type; + +// Output Schema +export const GetConfigOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + config_version: Schema.optional(Schema.String), +}); +export type GetConfigOutput = typeof GetConfigOutput.Type; + +// The operation +/** + * Get instance config + * + * Get the configuration for an application instance. + * The `envOrInsID` parameter can be either an environment type (e.g., "development", "production") + * or an instance ID. + * Optionally filter to specific config keys using the `keys` query parameter. + * Requires the `applications:manage` scope. + * + * @param applicationID - Application ID. + * @param envOrInsID - Environment type (e.g., "development", "production") or instance ID. + + * @param keys - Config keys to retrieve. If not specified, all keys are returned. + */ +export const getConfig = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetConfigInput, + outputSchema: GetConfigOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/platform/config/getConfigSchema.ts b/packages/clerk/src/operations/platform/config/getConfigSchema.ts new file mode 100644 index 0000000000..0f1ee0a0e0 --- /dev/null +++ b/packages/clerk/src/operations/platform/config/getConfigSchema.ts @@ -0,0 +1,47 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetConfigSchemaInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + envOrInsID: Schema.String.pipe(T.PathParam()), + keys: Schema.optional(Schema.String), +}).pipe( + T.Http({ + method: "GET", + path: "/platform/applications/{applicationID}/instances/{envOrInsID}/config/schema", + }), +); +export type GetConfigSchemaInput = typeof GetConfigSchemaInput.Type; + +// Output Schema +export const GetConfigSchemaOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + $schema: Schema.optional(Schema.String), + $id: Schema.optional(Schema.String), + type: Schema.optional(Schema.String), + properties: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)), +}); +export type GetConfigSchemaOutput = typeof GetConfigSchemaOutput.Type; + +// The operation +/** + * Get instance config schema + * + * Get the JSON Schema for the instance configuration. + * The `envOrInsID` parameter can be either an environment type (e.g., "development", "production") + * or an instance ID. + * Optionally filter to specific config keys using the `keys` query parameter. + * Requires the `applications:manage` scope. + * + * @param applicationID - Application ID. + * @param envOrInsID - Environment type (e.g., "development", "production") or instance ID. + + * @param keys - Config keys to retrieve schema for. If not specified, all keys are returned. + */ +export const getConfigSchema = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetConfigSchemaInput, + outputSchema: GetConfigSchemaOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/platform/config/index.ts b/packages/clerk/src/operations/platform/config/index.ts new file mode 100644 index 0000000000..22e79791db --- /dev/null +++ b/packages/clerk/src/operations/platform/config/index.ts @@ -0,0 +1,4 @@ +export * from "./getConfig.ts"; +export * from "./putConfig.ts"; +export * from "./patchConfig.ts"; +export * from "./getConfigSchema.ts"; diff --git a/packages/clerk/src/operations/platform/config/patchConfig.ts b/packages/clerk/src/operations/platform/config/patchConfig.ts new file mode 100644 index 0000000000..8ec2b3d596 --- /dev/null +++ b/packages/clerk/src/operations/platform/config/patchConfig.ts @@ -0,0 +1,68 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + Conflict, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const PatchConfigInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + envOrInsID: Schema.String.pipe(T.PathParam()), + dry_run: Schema.optional(Schema.Boolean), + destructive: Schema.optional(Schema.Boolean), + keys: Schema.optional(Schema.String), +}).pipe( + T.Http({ + method: "PATCH", + path: "/platform/applications/{applicationID}/instances/{envOrInsID}/config", + }), +); +export type PatchConfigInput = typeof PatchConfigInput.Type; + +// Output Schema +export const PatchConfigOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + config_version: Schema.optional(Schema.String), + dry_run: Schema.optional(Schema.Boolean), + before: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)), + after: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)), +}); +export type PatchConfigOutput = typeof PatchConfigOutput.Type; + +// The operation +/** + * Update instance config + * + * Update the configuration for an application instance. + * The `envOrInsID` parameter can be either an environment type (e.g., "development", "production") + * or an instance ID. + * Use the `dry_run` query parameter to preview changes without applying them. + * Use the `destructive` query parameter to allow clearing config keys by setting them to null. + * Use the `If-Match` header to provide optimistic concurrency control via config version. + * Requires the `applications:manage` scope. + * + * @param applicationID - Application ID. + * @param envOrInsID - Environment type (e.g., "development", "production") or instance ID. + + * @param dry_run - If true, preview the changes without applying them. + * @param destructive - If true, allow clearing config keys by setting them to null. + * @param keys - Config keys to return in the response. If not specified, only updated keys are returned. + * @param If-Match - Config version for optimistic concurrency control. + */ +export const patchConfig = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: PatchConfigInput, + outputSchema: PatchConfigOutput, + errors: [ + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + Conflict, + UnprocessableEntity, + ] as const, +})); diff --git a/packages/clerk/src/operations/platform/config/putConfig.ts b/packages/clerk/src/operations/platform/config/putConfig.ts new file mode 100644 index 0000000000..a7177988f4 --- /dev/null +++ b/packages/clerk/src/operations/platform/config/putConfig.ts @@ -0,0 +1,68 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + Conflict, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const PutConfigInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + envOrInsID: Schema.String.pipe(T.PathParam()), + dry_run: Schema.optional(Schema.Boolean), + destructive: Schema.optional(Schema.Boolean), +}).pipe( + T.Http({ + method: "PUT", + path: "/platform/applications/{applicationID}/instances/{envOrInsID}/config", + }), +); +export type PutConfigInput = typeof PutConfigInput.Type; + +// Output Schema +export const PutConfigOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + config_version: Schema.optional(Schema.String), + dry_run: Schema.optional(Schema.Boolean), + before: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)), + after: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)), +}); +export type PutConfigOutput = typeof PutConfigOutput.Type; + +// The operation +/** + * Replace instance config + * + * Replace the full configuration for an application instance. + * Unlike PATCH, PUT requires all config keys to be included in the request body. + * If any keys are missing, a 400 error is returned listing the missing keys. + * The `envOrInsID` parameter can be either an environment type (e.g., "development", "production") + * or an instance ID. + * Use the `dry_run` query parameter to preview changes without applying them. + * Use the `destructive` query parameter to allow clearing config keys by setting them to null. + * Use the `If-Match` header to provide optimistic concurrency control via config version. + * Requires the `applications:manage` scope. + * + * @param applicationID - Application ID. + * @param envOrInsID - Environment type (e.g., "development", "production") or instance ID. + + * @param dry_run - If true, preview the changes without applying them. + * @param destructive - If true, allow clearing config keys by setting them to null. + * @param If-Match - Config version for optimistic concurrency control. + */ +export const putConfig = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: PutConfigInput, + outputSchema: PutConfigOutput, + errors: [ + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + Conflict, + UnprocessableEntity, + ] as const, +})); diff --git a/packages/clerk/src/operations/platform/domains/createApplicationDomain.ts b/packages/clerk/src/operations/platform/domains/createApplicationDomain.ts new file mode 100644 index 0000000000..18a782f0e9 --- /dev/null +++ b/packages/clerk/src/operations/platform/domains/createApplicationDomain.ts @@ -0,0 +1,91 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + Conflict, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const CreateApplicationDomainInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + name: Schema.String, + proxy_path: Schema.optional(Schema.String), + }).pipe( + T.Http({ + method: "POST", + path: "/platform/applications/{applicationID}/domains", + }), + ); +export type CreateApplicationDomainInput = + typeof CreateApplicationDomainInput.Type; + +// Output Schema +export const CreateApplicationDomainOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["domain"]), + id: Schema.String, + name: Schema.String, + is_satellite: Schema.optional(Schema.Boolean), + is_provider_domain: Schema.optional(Schema.Boolean), + frontend_api_url: Schema.String, + development_origin: Schema.String, + accounts_portal_url: Schema.optional(Schema.String), + proxy_url: Schema.optional(Schema.String), + cname_targets: Schema.optional( + Schema.Array( + Schema.Struct({ + host: Schema.optional(Schema.String), + value: Schema.optional(Schema.String), + required: Schema.optional(Schema.Boolean), + }), + ), + ), + created_at: Schema.String, + updated_at: Schema.String, + application: Schema.optional( + Schema.Struct({ + object: Schema.Literals(["application"]), + id: Schema.String, + created_at: Schema.String, + updated_at: Schema.String, + }), + ), + instance: Schema.optional( + Schema.Struct({ + object: Schema.Literals(["instance"]), + id: Schema.String, + environment_type: Schema.Literals(["production", "development"]), + created_at: Schema.String, + updated_at: Schema.String, + }), + ), + }); +export type CreateApplicationDomainOutput = + typeof CreateApplicationDomainOutput.Type; + +// The operation +/** + * Create application domain + * + * Create a provider domain for an application's production instance. + * + * @param applicationID - Application ID. + */ +export const createApplicationDomain = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: CreateApplicationDomainInput, + outputSchema: CreateApplicationDomainOutput, + errors: [ + BadRequest, + Forbidden, + NotFound, + Conflict, + UnprocessableEntity, + ] as const, + }), +); diff --git a/packages/clerk/src/operations/platform/domains/deleteApplicationDomain.ts b/packages/clerk/src/operations/platform/domains/deleteApplicationDomain.ts new file mode 100644 index 0000000000..5176874fde --- /dev/null +++ b/packages/clerk/src/operations/platform/domains/deleteApplicationDomain.ts @@ -0,0 +1,51 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const DeleteApplicationDomainInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + domainIDOrName: Schema.String.pipe(T.PathParam()), + }).pipe( + T.Http({ + method: "DELETE", + path: "/platform/applications/{applicationID}/domains/{domainIDOrName}", + }), + ); +export type DeleteApplicationDomainInput = + typeof DeleteApplicationDomainInput.Type; + +// Output Schema +export const DeleteApplicationDomainOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + deleted: Schema.Boolean, + object: Schema.String, + id: Schema.String, + }); +export type DeleteApplicationDomainOutput = + typeof DeleteApplicationDomainOutput.Type; + +// The operation +/** + * Delete application domain + * + * Delete a provider domain from an application's production instance. + * Only provider domains can be deleted. The primary domain cannot be deleted. + * + * @param applicationID - Application ID. + * @param domainIDOrName - Domain ID or domain name. + */ +export const deleteApplicationDomain = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: DeleteApplicationDomainInput, + outputSchema: DeleteApplicationDomainOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, + }), +); diff --git a/packages/clerk/src/operations/platform/domains/getApplicationDomain.ts b/packages/clerk/src/operations/platform/domains/getApplicationDomain.ts new file mode 100644 index 0000000000..1feea5cea4 --- /dev/null +++ b/packages/clerk/src/operations/platform/domains/getApplicationDomain.ts @@ -0,0 +1,77 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetApplicationDomainInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + domainIDOrName: Schema.String.pipe(T.PathParam()), + }).pipe( + T.Http({ + method: "GET", + path: "/platform/applications/{applicationID}/domains/{domainIDOrName}", + }), + ); +export type GetApplicationDomainInput = typeof GetApplicationDomainInput.Type; + +// Output Schema +export const GetApplicationDomainOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["domain"]), + id: Schema.String, + name: Schema.String, + is_satellite: Schema.optional(Schema.Boolean), + is_provider_domain: Schema.optional(Schema.Boolean), + frontend_api_url: Schema.String, + development_origin: Schema.String, + accounts_portal_url: Schema.optional(Schema.String), + proxy_url: Schema.optional(Schema.String), + cname_targets: Schema.optional( + Schema.Array( + Schema.Struct({ + host: Schema.optional(Schema.String), + value: Schema.optional(Schema.String), + required: Schema.optional(Schema.Boolean), + }), + ), + ), + created_at: Schema.String, + updated_at: Schema.String, + application: Schema.optional( + Schema.Struct({ + object: Schema.Literals(["application"]), + id: Schema.String, + created_at: Schema.String, + updated_at: Schema.String, + }), + ), + instance: Schema.optional( + Schema.Struct({ + object: Schema.Literals(["instance"]), + id: Schema.String, + environment_type: Schema.Literals(["production", "development"]), + created_at: Schema.String, + updated_at: Schema.String, + }), + ), + }); +export type GetApplicationDomainOutput = typeof GetApplicationDomainOutput.Type; + +// The operation +/** + * Get application domain + * + * Get domain information for an application. + * + * @param applicationID - Application ID. + * @param domainIDOrName - Domain ID or domain name. + */ +export const getApplicationDomain = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: GetApplicationDomainInput, + outputSchema: GetApplicationDomainOutput, + errors: [BadRequest, Forbidden, NotFound] as const, + }), +); diff --git a/packages/clerk/src/operations/platform/domains/getApplicationDomainStatus.ts b/packages/clerk/src/operations/platform/domains/getApplicationDomainStatus.ts new file mode 100644 index 0000000000..e29a8c3fe0 --- /dev/null +++ b/packages/clerk/src/operations/platform/domains/getApplicationDomainStatus.ts @@ -0,0 +1,189 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetApplicationDomainStatusInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + domainIDOrName: Schema.String.pipe(T.PathParam()), + }).pipe( + T.Http({ + method: "GET", + path: "/platform/applications/{applicationID}/domains/{domainIDOrName}/status", + }), + ); +export type GetApplicationDomainStatusInput = + typeof GetApplicationDomainStatusInput.Type; + +// Output Schema +export const GetApplicationDomainStatusOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + dns: Schema.Struct({ + status: Schema.Literals(["not_started", "in_progress", "complete"]), + cnames: Schema.Struct({ + accounts: Schema.optional( + Schema.Struct({ + actual: Schema.optional(Schema.Array(Schema.String)), + clerk_subdomain: Schema.String, + from: Schema.String, + to: Schema.String, + verified: Schema.Boolean, + required: Schema.Boolean, + failure_hints: Schema.NullOr( + Schema.Array( + Schema.Struct({ + code: Schema.String, + message: Schema.String, + }), + ), + ), + }), + ), + clerk: Schema.optional( + Schema.Struct({ + actual: Schema.optional(Schema.Array(Schema.String)), + clerk_subdomain: Schema.String, + from: Schema.String, + to: Schema.String, + verified: Schema.Boolean, + required: Schema.Boolean, + failure_hints: Schema.NullOr( + Schema.Array( + Schema.Struct({ + code: Schema.String, + message: Schema.String, + }), + ), + ), + }), + ), + "clk._domainkey": Schema.optional( + Schema.Struct({ + actual: Schema.optional(Schema.Array(Schema.String)), + clerk_subdomain: Schema.String, + from: Schema.String, + to: Schema.String, + verified: Schema.Boolean, + required: Schema.Boolean, + failure_hints: Schema.NullOr( + Schema.Array( + Schema.Struct({ + code: Schema.String, + message: Schema.String, + }), + ), + ), + }), + ), + "clk2._domainkey": Schema.optional( + Schema.Struct({ + actual: Schema.optional(Schema.Array(Schema.String)), + clerk_subdomain: Schema.String, + from: Schema.String, + to: Schema.String, + verified: Schema.Boolean, + required: Schema.Boolean, + failure_hints: Schema.NullOr( + Schema.Array( + Schema.Struct({ + code: Schema.String, + message: Schema.String, + }), + ), + ), + }), + ), + clkmail: Schema.optional( + Schema.Struct({ + actual: Schema.optional(Schema.Array(Schema.String)), + clerk_subdomain: Schema.String, + from: Schema.String, + to: Schema.String, + verified: Schema.Boolean, + required: Schema.Boolean, + failure_hints: Schema.NullOr( + Schema.Array( + Schema.Struct({ + code: Schema.String, + message: Schema.String, + }), + ), + ), + }), + ), + }), + }), + ssl: Schema.Struct({ + status: Schema.optional( + Schema.Literals([ + "complete", + "in_process", + "not_started", + "failed", + "incomplete", + ]), + ), + required: Schema.optional(Schema.Boolean), + failure_hints: Schema.optional( + Schema.NullOr( + Schema.Array( + Schema.Struct({ + code: Schema.String, + message: Schema.String, + }), + ), + ), + ), + }), + ssl_hosts: Schema.optional( + Schema.Record( + Schema.String, + Schema.Struct({ + status: Schema.String, + required: Schema.Boolean, + failure_hints: Schema.NullOr( + Schema.Array( + Schema.Struct({ + code: Schema.String, + message: Schema.String, + }), + ), + ), + }), + ), + ), + mail: Schema.optional( + Schema.Struct({ + status: Schema.String, + required: Schema.Boolean, + }), + ), + proxy: Schema.optional( + Schema.Struct({ + status: Schema.String, + required: Schema.Boolean, + }), + ), + status: Schema.Literals(["complete", "incomplete"]), + }); +export type GetApplicationDomainStatusOutput = + typeof GetApplicationDomainStatusOutput.Type; + +// The operation +/** + * Get application domain status + * + * Get the status of a domain for an application. + * + * @param applicationID - Application ID. + * @param domainIDOrName - Domain ID or domain name. + */ +export const getApplicationDomainStatus = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: GetApplicationDomainStatusInput, + outputSchema: GetApplicationDomainStatusOutput, + errors: [BadRequest, Forbidden, NotFound] as const, + }), +); diff --git a/packages/clerk/src/operations/platform/domains/getDomain.ts b/packages/clerk/src/operations/platform/domains/getDomain.ts new file mode 100644 index 0000000000..a517bf701d --- /dev/null +++ b/packages/clerk/src/operations/platform/domains/getDomain.ts @@ -0,0 +1,62 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetDomainInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + domainIDOrName: Schema.String.pipe(T.PathParam()), +}).pipe(T.Http({ method: "GET", path: "/platform/domains/{domainIDOrName}" })); +export type GetDomainInput = typeof GetDomainInput.Type; + +// Output Schema +export const GetDomainOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["domain"]), + id: Schema.String, + name: Schema.String, + is_satellite: Schema.optional(Schema.Boolean), + is_provider_domain: Schema.optional(Schema.Boolean), + frontend_api_url: Schema.String, + development_origin: Schema.String, + accounts_portal_url: Schema.optional(Schema.String), + proxy_url: Schema.optional(Schema.String), + cname_targets: Schema.optional( + Schema.Array( + Schema.Struct({ + host: Schema.optional(Schema.String), + value: Schema.optional(Schema.String), + required: Schema.optional(Schema.Boolean), + }), + ), + ), + created_at: Schema.String, + updated_at: Schema.String, + application: Schema.Struct({ + object: Schema.Literals(["application"]), + id: Schema.String, + created_at: Schema.String, + updated_at: Schema.String, + }), + instance: Schema.Struct({ + object: Schema.Literals(["instance"]), + id: Schema.String, + environment_type: Schema.Literals(["production", "development"]), + created_at: Schema.String, + updated_at: Schema.String, + }), +}); +export type GetDomainOutput = typeof GetDomainOutput.Type; + +// The operation +/** + * Get domain + * + * Retrieve a single domain by ID or name for the authenticated workspace. + * + * @param domainIDOrName - Domain ID or domain name. + */ +export const getDomain = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetDomainInput, + outputSchema: GetDomainOutput, + errors: [Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/platform/domains/index.ts b/packages/clerk/src/operations/platform/domains/index.ts new file mode 100644 index 0000000000..b155da4320 --- /dev/null +++ b/packages/clerk/src/operations/platform/domains/index.ts @@ -0,0 +1,9 @@ +export * from "./updateApplicationDomain.ts"; +export * from "./listApplicationDomains.ts"; +export * from "./createApplicationDomain.ts"; +export * from "./getApplicationDomain.ts"; +export * from "./deleteApplicationDomain.ts"; +export * from "./getApplicationDomainStatus.ts"; +export * from "./triggerDNSCheck.ts"; +export * from "./listDomains.ts"; +export * from "./getDomain.ts"; diff --git a/packages/clerk/src/operations/platform/domains/listApplicationDomains.ts b/packages/clerk/src/operations/platform/domains/listApplicationDomains.ts new file mode 100644 index 0000000000..7efaf5650e --- /dev/null +++ b/packages/clerk/src/operations/platform/domains/listApplicationDomains.ts @@ -0,0 +1,82 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const ListApplicationDomainsInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + }).pipe( + T.Http({ + method: "GET", + path: "/platform/applications/{applicationID}/domains", + }), + ); +export type ListApplicationDomainsInput = + typeof ListApplicationDomainsInput.Type; + +// Output Schema +export const ListApplicationDomainsOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["domain"]), + id: Schema.String, + name: Schema.String, + is_satellite: Schema.optional(Schema.Boolean), + is_provider_domain: Schema.optional(Schema.Boolean), + frontend_api_url: Schema.String, + development_origin: Schema.String, + accounts_portal_url: Schema.optional(Schema.String), + proxy_url: Schema.optional(Schema.String), + cname_targets: Schema.optional( + Schema.Array( + Schema.Struct({ + host: Schema.optional(Schema.String), + value: Schema.optional(Schema.String), + required: Schema.optional(Schema.Boolean), + }), + ), + ), + created_at: Schema.String, + updated_at: Schema.String, + application: Schema.optional( + Schema.Struct({ + object: Schema.Literals(["application"]), + id: Schema.String, + created_at: Schema.String, + updated_at: Schema.String, + }), + ), + instance: Schema.optional( + Schema.Struct({ + object: Schema.Literals(["instance"]), + id: Schema.String, + environment_type: Schema.Literals(["production", "development"]), + created_at: Schema.String, + updated_at: Schema.String, + }), + ), + }), + ), + total_count: Schema.Number, + }); +export type ListApplicationDomainsOutput = + typeof ListApplicationDomainsOutput.Type; + +// The operation +/** + * List application domains + * + * List all domains for an application's production instance. + * + * @param applicationID - Application ID. + */ +export const listApplicationDomains = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: ListApplicationDomainsInput, + outputSchema: ListApplicationDomainsOutput, + errors: [Forbidden, NotFound] as const, + }), +); diff --git a/packages/clerk/src/operations/platform/domains/listDomains.ts b/packages/clerk/src/operations/platform/domains/listDomains.ts new file mode 100644 index 0000000000..facc3837f9 --- /dev/null +++ b/packages/clerk/src/operations/platform/domains/listDomains.ts @@ -0,0 +1,81 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { Forbidden, UnprocessableEntity } from "../../../errors.ts"; + +// Input Schema +export const ListDomainsInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + name: Schema.optional(Schema.String), + include_development: Schema.optional(Schema.Literals(["true"])), + limit: Schema.optional(Schema.Number), + starting_after: Schema.optional(Schema.String), + ending_before: Schema.optional(Schema.String), +}).pipe(T.Http({ method: "GET", path: "/platform/domains" })); +export type ListDomainsInput = typeof ListDomainsInput.Type; + +// Output Schema +export const ListDomainsOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["domain"]), + id: Schema.String, + name: Schema.String, + is_satellite: Schema.optional(Schema.Boolean), + is_provider_domain: Schema.optional(Schema.Boolean), + frontend_api_url: Schema.String, + development_origin: Schema.String, + accounts_portal_url: Schema.optional(Schema.String), + proxy_url: Schema.optional(Schema.String), + cname_targets: Schema.optional( + Schema.Array( + Schema.Struct({ + host: Schema.optional(Schema.String), + value: Schema.optional(Schema.String), + required: Schema.optional(Schema.Boolean), + }), + ), + ), + created_at: Schema.String, + updated_at: Schema.String, + application: Schema.Struct({ + object: Schema.Literals(["application"]), + id: Schema.String, + created_at: Schema.String, + updated_at: Schema.String, + }), + instance: Schema.Struct({ + object: Schema.Literals(["instance"]), + id: Schema.String, + environment_type: Schema.Literals(["production", "development"]), + created_at: Schema.String, + updated_at: Schema.String, + }), + }), + ), + total_count: Schema.Number, +}); +export type ListDomainsOutput = typeof ListDomainsOutput.Type; + +// The operation +/** + * List domains + * + * List production instance domains for applications in the authenticated workspace. + * Results are sorted by domain creation time in descending order (most recent first). + * Set `include_development` to include development instance domains. + * + * @param name - Filter domains whose name contains this substring (case-insensitive). + * @param include_development - When `true`, include development instance domains. Defaults to production only. + * @param limit - Number of results to return per page (1-500, default 10). + * @param starting_after - Cursor for pagination. Provide the ID of the last domain from the +previous page to get the next page of results. + + * @param ending_before - Cursor for pagination. Provide the ID of the first domain from the +previous page to get the previous page of results. + + */ +export const listDomains = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListDomainsInput, + outputSchema: ListDomainsOutput, + errors: [Forbidden, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/platform/domains/triggerDNSCheck.ts b/packages/clerk/src/operations/platform/domains/triggerDNSCheck.ts new file mode 100644 index 0000000000..93d888257b --- /dev/null +++ b/packages/clerk/src/operations/platform/domains/triggerDNSCheck.ts @@ -0,0 +1,188 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound, Conflict } from "../../../errors.ts"; + +// Input Schema +export const TriggerDNSCheckInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + domainIDOrName: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "POST", + path: "/platform/applications/{applicationID}/domains/{domainIDOrName}/dns_check", + }), +); +export type TriggerDNSCheckInput = typeof TriggerDNSCheckInput.Type; + +// Output Schema +export const TriggerDNSCheckOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + dns: Schema.Struct({ + status: Schema.Literals(["not_started", "in_progress", "complete"]), + cnames: Schema.Struct({ + accounts: Schema.optional( + Schema.Struct({ + actual: Schema.optional(Schema.Array(Schema.String)), + clerk_subdomain: Schema.String, + from: Schema.String, + to: Schema.String, + verified: Schema.Boolean, + required: Schema.Boolean, + failure_hints: Schema.NullOr( + Schema.Array( + Schema.Struct({ + code: Schema.String, + message: Schema.String, + }), + ), + ), + }), + ), + clerk: Schema.optional( + Schema.Struct({ + actual: Schema.optional(Schema.Array(Schema.String)), + clerk_subdomain: Schema.String, + from: Schema.String, + to: Schema.String, + verified: Schema.Boolean, + required: Schema.Boolean, + failure_hints: Schema.NullOr( + Schema.Array( + Schema.Struct({ + code: Schema.String, + message: Schema.String, + }), + ), + ), + }), + ), + "clk._domainkey": Schema.optional( + Schema.Struct({ + actual: Schema.optional(Schema.Array(Schema.String)), + clerk_subdomain: Schema.String, + from: Schema.String, + to: Schema.String, + verified: Schema.Boolean, + required: Schema.Boolean, + failure_hints: Schema.NullOr( + Schema.Array( + Schema.Struct({ + code: Schema.String, + message: Schema.String, + }), + ), + ), + }), + ), + "clk2._domainkey": Schema.optional( + Schema.Struct({ + actual: Schema.optional(Schema.Array(Schema.String)), + clerk_subdomain: Schema.String, + from: Schema.String, + to: Schema.String, + verified: Schema.Boolean, + required: Schema.Boolean, + failure_hints: Schema.NullOr( + Schema.Array( + Schema.Struct({ + code: Schema.String, + message: Schema.String, + }), + ), + ), + }), + ), + clkmail: Schema.optional( + Schema.Struct({ + actual: Schema.optional(Schema.Array(Schema.String)), + clerk_subdomain: Schema.String, + from: Schema.String, + to: Schema.String, + verified: Schema.Boolean, + required: Schema.Boolean, + failure_hints: Schema.NullOr( + Schema.Array( + Schema.Struct({ + code: Schema.String, + message: Schema.String, + }), + ), + ), + }), + ), + }), + }), + ssl: Schema.Struct({ + status: Schema.optional( + Schema.Literals([ + "complete", + "in_process", + "not_started", + "failed", + "incomplete", + ]), + ), + required: Schema.optional(Schema.Boolean), + failure_hints: Schema.optional( + Schema.NullOr( + Schema.Array( + Schema.Struct({ + code: Schema.String, + message: Schema.String, + }), + ), + ), + ), + }), + ssl_hosts: Schema.optional( + Schema.Record( + Schema.String, + Schema.Struct({ + status: Schema.String, + required: Schema.Boolean, + failure_hints: Schema.NullOr( + Schema.Array( + Schema.Struct({ + code: Schema.String, + message: Schema.String, + }), + ), + ), + }), + ), + ), + mail: Schema.optional( + Schema.Struct({ + status: Schema.String, + required: Schema.Boolean, + }), + ), + proxy: Schema.optional( + Schema.Struct({ + status: Schema.String, + required: Schema.Boolean, + }), + ), + status: Schema.Literals(["complete", "incomplete"]), + domain_id: Schema.String, + last_run_at: Schema.NullOr(Schema.Number), +}); +export type TriggerDNSCheckOutput = typeof TriggerDNSCheckOutput.Type; + +// The operation +/** + * Trigger DNS check + * + * Triggers a DNS check for a domain and returns the current domain status. + * This endpoint ensures that at most one DNS check job is in-flight at any time. + * If a check is already running or was recently performed, a 409 Conflict is returned. + * Use this endpoint to trigger a new DNS verification check after configuring DNS records. + * + * @param applicationID - Application ID. + * @param domainIDOrName - Domain ID or domain name. + */ +export const triggerDNSCheck = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: TriggerDNSCheckInput, + outputSchema: TriggerDNSCheckOutput, + errors: [BadRequest, Forbidden, NotFound, Conflict] as const, +})); diff --git a/packages/clerk/src/operations/platform/domains/updateApplicationDomain.ts b/packages/clerk/src/operations/platform/domains/updateApplicationDomain.ts new file mode 100644 index 0000000000..fa1a9ff832 --- /dev/null +++ b/packages/clerk/src/operations/platform/domains/updateApplicationDomain.ts @@ -0,0 +1,84 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const UpdateApplicationDomainInput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + name: Schema.String, + proxy_path: Schema.optional(Schema.String), + }).pipe( + T.Http({ + method: "PATCH", + path: "/platform/applications/{applicationID}/domain", + }), + ); +export type UpdateApplicationDomainInput = + typeof UpdateApplicationDomainInput.Type; + +// Output Schema +export const UpdateApplicationDomainOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["domain"]), + id: Schema.String, + name: Schema.String, + is_satellite: Schema.optional(Schema.Boolean), + is_provider_domain: Schema.optional(Schema.Boolean), + frontend_api_url: Schema.String, + development_origin: Schema.String, + accounts_portal_url: Schema.optional(Schema.String), + proxy_url: Schema.optional(Schema.String), + cname_targets: Schema.optional( + Schema.Array( + Schema.Struct({ + host: Schema.optional(Schema.String), + value: Schema.optional(Schema.String), + required: Schema.optional(Schema.Boolean), + }), + ), + ), + created_at: Schema.String, + updated_at: Schema.String, + application: Schema.optional( + Schema.Struct({ + object: Schema.Literals(["application"]), + id: Schema.String, + created_at: Schema.String, + updated_at: Schema.String, + }), + ), + instance: Schema.optional( + Schema.Struct({ + object: Schema.Literals(["instance"]), + id: Schema.String, + environment_type: Schema.Literals(["production", "development"]), + created_at: Schema.String, + updated_at: Schema.String, + }), + ), + }); +export type UpdateApplicationDomainOutput = + typeof UpdateApplicationDomainOutput.Type; + +// The operation +/** + * Update application domain + * + * Update the production domain for an application. + * + * @param applicationID - Application ID. + */ +export const updateApplicationDomain = /*@__PURE__*/ /*#__PURE__*/ API.make( + () => ({ + inputSchema: UpdateApplicationDomainInput, + outputSchema: UpdateApplicationDomainOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, + }), +); diff --git a/packages/clerk/src/operations/platform/index.ts b/packages/clerk/src/operations/platform/index.ts new file mode 100644 index 0000000000..644ad12cd6 --- /dev/null +++ b/packages/clerk/src/operations/platform/index.ts @@ -0,0 +1,7 @@ +export * as applicationTransfers from "./applicationTransfers/index.ts"; +export * as applications from "./applications/index.ts"; +export * as config from "./config/index.ts"; +export * as domains from "./domains/index.ts"; +export * as jwtTemplates from "./jwtTemplates/index.ts"; +export * as redirectUrls from "./redirectUrls/index.ts"; +export * as users from "./users/index.ts"; diff --git a/packages/clerk/src/operations/platform/jwtTemplates/createJWTTemplate.ts b/packages/clerk/src/operations/platform/jwtTemplates/createJWTTemplate.ts new file mode 100644 index 0000000000..dfc047f509 --- /dev/null +++ b/packages/clerk/src/operations/platform/jwtTemplates/createJWTTemplate.ts @@ -0,0 +1,70 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const CreateJWTTemplateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + { + applicationID: Schema.String.pipe(T.PathParam()), + envOrInsID: Schema.String.pipe(T.PathParam()), + name: Schema.String, + claims: Schema.Unknown, + lifetime: Schema.optional(Schema.NullOr(Schema.Number)), + allowed_clock_skew: Schema.optional(Schema.NullOr(Schema.Number)), + custom_signing_key: Schema.optional(Schema.Boolean), + signing_algorithm: Schema.optional(Schema.NullOr(Schema.String)), + signing_key: Schema.optional(Schema.NullOr(Schema.String)), + }, +).pipe( + T.Http({ + method: "POST", + path: "/platform/applications/{applicationID}/instances/{envOrInsID}/jwt_templates", + }), +); +export type CreateJWTTemplateInput = typeof CreateJWTTemplateInput.Type; + +// Output Schema +export const CreateJWTTemplateOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["jwt_template"]), + id: Schema.String, + name: Schema.String, + claims: Schema.Unknown, + lifetime: Schema.Number, + allowed_clock_skew: Schema.Number, + custom_signing_key: Schema.Boolean, + signing_algorithm: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + }); +export type CreateJWTTemplateOutput = typeof CreateJWTTemplateOutput.Type; + +// The operation +/** + * Create a JWT template + * + * Create a new JWT template for an application instance. + * Requires the `jwt_templates:manage` scope. + * + * @param applicationID - Application ID. + * @param envOrInsID - Environment type (e.g., "development", "production") or instance ID. + + */ +export const createJWTTemplate = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateJWTTemplateInput, + outputSchema: CreateJWTTemplateOutput, + errors: [ + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + UnprocessableEntity, + ] as const, +})); diff --git a/packages/clerk/src/operations/platform/jwtTemplates/deleteJWTTemplate.ts b/packages/clerk/src/operations/platform/jwtTemplates/deleteJWTTemplate.ts new file mode 100644 index 0000000000..046a37b3c0 --- /dev/null +++ b/packages/clerk/src/operations/platform/jwtTemplates/deleteJWTTemplate.ts @@ -0,0 +1,48 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteJWTTemplateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + { + applicationID: Schema.String.pipe(T.PathParam()), + envOrInsID: Schema.String.pipe(T.PathParam()), + templateID: Schema.String.pipe(T.PathParam()), + }, +).pipe( + T.Http({ + method: "DELETE", + path: "/platform/applications/{applicationID}/instances/{envOrInsID}/jwt_templates/{templateID}", + }), +); +export type DeleteJWTTemplateInput = typeof DeleteJWTTemplateInput.Type; + +// Output Schema +export const DeleteJWTTemplateOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), + }); +export type DeleteJWTTemplateOutput = typeof DeleteJWTTemplateOutput.Type; + +// The operation +/** + * Delete a JWT template + * + * Delete a JWT template from an application instance. + * Requires the `jwt_templates:manage` scope. + * + * @param applicationID - Application ID. + * @param envOrInsID - Environment type (e.g., "development", "production") or instance ID. + + * @param templateID - JWT Template ID. + */ +export const deleteJWTTemplate = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteJWTTemplateInput, + outputSchema: DeleteJWTTemplateOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/platform/jwtTemplates/getJWTTemplate.ts b/packages/clerk/src/operations/platform/jwtTemplates/getJWTTemplate.ts new file mode 100644 index 0000000000..c69e04b3d3 --- /dev/null +++ b/packages/clerk/src/operations/platform/jwtTemplates/getJWTTemplate.ts @@ -0,0 +1,50 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetJWTTemplateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + envOrInsID: Schema.String.pipe(T.PathParam()), + templateID: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "GET", + path: "/platform/applications/{applicationID}/instances/{envOrInsID}/jwt_templates/{templateID}", + }), +); +export type GetJWTTemplateInput = typeof GetJWTTemplateInput.Type; + +// Output Schema +export const GetJWTTemplateOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["jwt_template"]), + id: Schema.String, + name: Schema.String, + claims: Schema.Unknown, + lifetime: Schema.Number, + allowed_clock_skew: Schema.Number, + custom_signing_key: Schema.Boolean, + signing_algorithm: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type GetJWTTemplateOutput = typeof GetJWTTemplateOutput.Type; + +// The operation +/** + * Get a JWT template + * + * Retrieve a specific JWT template for an application instance. + * Requires the `jwt_templates:read` scope. + * + * @param applicationID - Application ID. + * @param envOrInsID - Environment type (e.g., "development", "production") or instance ID. + + * @param templateID - JWT Template ID. + */ +export const getJWTTemplate = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetJWTTemplateInput, + outputSchema: GetJWTTemplateOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/platform/jwtTemplates/index.ts b/packages/clerk/src/operations/platform/jwtTemplates/index.ts new file mode 100644 index 0000000000..86bcf768df --- /dev/null +++ b/packages/clerk/src/operations/platform/jwtTemplates/index.ts @@ -0,0 +1,5 @@ +export * from "./listJWTTemplates.ts"; +export * from "./createJWTTemplate.ts"; +export * from "./getJWTTemplate.ts"; +export * from "./updateJWTTemplate.ts"; +export * from "./deleteJWTTemplate.ts"; diff --git a/packages/clerk/src/operations/platform/jwtTemplates/listJWTTemplates.ts b/packages/clerk/src/operations/platform/jwtTemplates/listJWTTemplates.ts new file mode 100644 index 0000000000..bc7ac8f4a7 --- /dev/null +++ b/packages/clerk/src/operations/platform/jwtTemplates/listJWTTemplates.ts @@ -0,0 +1,50 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const ListJWTTemplatesInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + envOrInsID: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "GET", + path: "/platform/applications/{applicationID}/instances/{envOrInsID}/jwt_templates", + }), +); +export type ListJWTTemplatesInput = typeof ListJWTTemplatesInput.Type; + +// Output Schema +export const ListJWTTemplatesOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Array( + Schema.Struct({ + object: Schema.Literals(["jwt_template"]), + id: Schema.String, + name: Schema.String, + claims: Schema.Unknown, + lifetime: Schema.Number, + allowed_clock_skew: Schema.Number, + custom_signing_key: Schema.Boolean, + signing_algorithm: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + }), +); +export type ListJWTTemplatesOutput = typeof ListJWTTemplatesOutput.Type; + +// The operation +/** + * List JWT templates + * + * List all JWT templates for an application instance. + * Requires the `jwt_templates:read` scope. + * + * @param applicationID - Application ID. + * @param envOrInsID - Environment type (e.g., "development", "production") or instance ID. + + */ +export const listJWTTemplates = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListJWTTemplatesInput, + outputSchema: ListJWTTemplatesOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/platform/jwtTemplates/updateJWTTemplate.ts b/packages/clerk/src/operations/platform/jwtTemplates/updateJWTTemplate.ts new file mode 100644 index 0000000000..863b58167d --- /dev/null +++ b/packages/clerk/src/operations/platform/jwtTemplates/updateJWTTemplate.ts @@ -0,0 +1,72 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const UpdateJWTTemplateInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + { + applicationID: Schema.String.pipe(T.PathParam()), + envOrInsID: Schema.String.pipe(T.PathParam()), + templateID: Schema.String.pipe(T.PathParam()), + name: Schema.String, + claims: Schema.Unknown, + lifetime: Schema.optional(Schema.NullOr(Schema.Number)), + allowed_clock_skew: Schema.optional(Schema.NullOr(Schema.Number)), + custom_signing_key: Schema.optional(Schema.Boolean), + signing_algorithm: Schema.optional(Schema.NullOr(Schema.String)), + signing_key: Schema.optional(Schema.NullOr(Schema.String)), + }, +).pipe( + T.Http({ + method: "PATCH", + path: "/platform/applications/{applicationID}/instances/{envOrInsID}/jwt_templates/{templateID}", + }), +); +export type UpdateJWTTemplateInput = typeof UpdateJWTTemplateInput.Type; + +// Output Schema +export const UpdateJWTTemplateOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["jwt_template"]), + id: Schema.String, + name: Schema.String, + claims: Schema.Unknown, + lifetime: Schema.Number, + allowed_clock_skew: Schema.Number, + custom_signing_key: Schema.Boolean, + signing_algorithm: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + }); +export type UpdateJWTTemplateOutput = typeof UpdateJWTTemplateOutput.Type; + +// The operation +/** + * Update a JWT template + * + * Update an existing JWT template for an application instance. + * Requires the `jwt_templates:manage` scope. + * + * @param applicationID - Application ID. + * @param envOrInsID - Environment type (e.g., "development", "production") or instance ID. + + * @param templateID - JWT Template ID. + */ +export const updateJWTTemplate = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UpdateJWTTemplateInput, + outputSchema: UpdateJWTTemplateOutput, + errors: [ + BadRequest, + PaymentRequired, + Forbidden, + NotFound, + UnprocessableEntity, + ] as const, +})); diff --git a/packages/clerk/src/operations/platform/redirectUrls/createRedirectURL.ts b/packages/clerk/src/operations/platform/redirectUrls/createRedirectURL.ts new file mode 100644 index 0000000000..4f32980612 --- /dev/null +++ b/packages/clerk/src/operations/platform/redirectUrls/createRedirectURL.ts @@ -0,0 +1,54 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const CreateRedirectURLInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + { + applicationID: Schema.String.pipe(T.PathParam()), + envOrInsID: Schema.String.pipe(T.PathParam()), + url: Schema.String, + }, +).pipe( + T.Http({ + method: "POST", + path: "/platform/applications/{applicationID}/instances/{envOrInsID}/redirect_urls", + }), +); +export type CreateRedirectURLInput = typeof CreateRedirectURLInput.Type; + +// Output Schema +export const CreateRedirectURLOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["redirect_url"]), + id: Schema.String, + url: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + }); +export type CreateRedirectURLOutput = typeof CreateRedirectURLOutput.Type; + +// The operation +/** + * Create a redirect URL + * + * Create a redirect URL for an application instance. + * The `envOrInsID` parameter can be either an environment type (e.g., "development", "production") + * or an instance ID. + * Requires the `applications:manage` scope. + * + * @param applicationID - Application ID. + * @param envOrInsID - Environment type (e.g., "development", "production") or instance ID. + + */ +export const createRedirectURL = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: CreateRedirectURLInput, + outputSchema: CreateRedirectURLOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/platform/redirectUrls/deleteRedirectURL.ts b/packages/clerk/src/operations/platform/redirectUrls/deleteRedirectURL.ts new file mode 100644 index 0000000000..79a72eae75 --- /dev/null +++ b/packages/clerk/src/operations/platform/redirectUrls/deleteRedirectURL.ts @@ -0,0 +1,50 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteRedirectURLInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + { + applicationID: Schema.String.pipe(T.PathParam()), + envOrInsID: Schema.String.pipe(T.PathParam()), + redirectURLID: Schema.String.pipe(T.PathParam()), + }, +).pipe( + T.Http({ + method: "DELETE", + path: "/platform/applications/{applicationID}/instances/{envOrInsID}/redirect_urls/{redirectURLID}", + }), +); +export type DeleteRedirectURLInput = typeof DeleteRedirectURLInput.Type; + +// Output Schema +export const DeleteRedirectURLOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), + }); +export type DeleteRedirectURLOutput = typeof DeleteRedirectURLOutput.Type; + +// The operation +/** + * Delete a redirect URL + * + * Delete a redirect URL from an application instance. + * The `envOrInsID` parameter can be either an environment type (e.g., "development", "production") + * or an instance ID. + * Requires the `applications:manage` scope. + * + * @param applicationID - Application ID. + * @param envOrInsID - Environment type (e.g., "development", "production") or instance ID. + + * @param redirectURLID - Redirect URL ID. + */ +export const deleteRedirectURL = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteRedirectURLInput, + outputSchema: DeleteRedirectURLOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/platform/redirectUrls/getRedirectURL.ts b/packages/clerk/src/operations/platform/redirectUrls/getRedirectURL.ts new file mode 100644 index 0000000000..0939710ea1 --- /dev/null +++ b/packages/clerk/src/operations/platform/redirectUrls/getRedirectURL.ts @@ -0,0 +1,47 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const GetRedirectURLInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + envOrInsID: Schema.String.pipe(T.PathParam()), + redirectURLID: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "GET", + path: "/platform/applications/{applicationID}/instances/{envOrInsID}/redirect_urls/{redirectURLID}", + }), +); +export type GetRedirectURLInput = typeof GetRedirectURLInput.Type; + +// Output Schema +export const GetRedirectURLOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.Literals(["redirect_url"]), + id: Schema.String, + url: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, +}); +export type GetRedirectURLOutput = typeof GetRedirectURLOutput.Type; + +// The operation +/** + * Get a redirect URL + * + * Retrieve a redirect URL for an application instance. + * The `envOrInsID` parameter can be either an environment type (e.g., "development", "production") + * or an instance ID. + * Requires the `applications:read` scope. + * + * @param applicationID - Application ID. + * @param envOrInsID - Environment type (e.g., "development", "production") or instance ID. + + * @param redirectURLID - Redirect URL ID. + */ +export const getRedirectURL = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: GetRedirectURLInput, + outputSchema: GetRedirectURLOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/platform/redirectUrls/index.ts b/packages/clerk/src/operations/platform/redirectUrls/index.ts new file mode 100644 index 0000000000..fc8cda2324 --- /dev/null +++ b/packages/clerk/src/operations/platform/redirectUrls/index.ts @@ -0,0 +1,4 @@ +export * from "./listRedirectURLs.ts"; +export * from "./createRedirectURL.ts"; +export * from "./getRedirectURL.ts"; +export * from "./deleteRedirectURL.ts"; diff --git a/packages/clerk/src/operations/platform/redirectUrls/listRedirectURLs.ts b/packages/clerk/src/operations/platform/redirectUrls/listRedirectURLs.ts new file mode 100644 index 0000000000..5e5175c70e --- /dev/null +++ b/packages/clerk/src/operations/platform/redirectUrls/listRedirectURLs.ts @@ -0,0 +1,61 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; + +// Input Schema +export const ListRedirectURLsInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + envOrInsID: Schema.String.pipe(T.PathParam()), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), +}).pipe( + T.Http({ + method: "GET", + path: "/platform/applications/{applicationID}/instances/{envOrInsID}/redirect_urls", + }), +); +export type ListRedirectURLsInput = typeof ListRedirectURLsInput.Type; + +// Output Schema +export const ListRedirectURLsOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + { + data: Schema.Array( + Schema.Struct({ + object: Schema.Literals(["redirect_url"]), + id: Schema.String, + url: Schema.String, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + total_count: Schema.Number, + }, +); +export type ListRedirectURLsOutput = typeof ListRedirectURLsOutput.Type; + +// The operation +/** + * List redirect URLs + * + * List all redirect URLs for an application instance. + * The `envOrInsID` parameter can be either an environment type (e.g., "development", "production") + * or an instance ID. + * Requires the `applications:read` scope. + * + * @param applicationID - Application ID. + * @param envOrInsID - Environment type (e.g., "development", "production") or instance ID. + + * @param limit - Number of results to return per page (1-500, default 10). + * @param offset - Number of results to skip for pagination. + */ +export const listRedirectURLs = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListRedirectURLsInput, + outputSchema: ListRedirectURLsOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/platform/users/banUser.ts b/packages/clerk/src/operations/platform/users/banUser.ts new file mode 100644 index 0000000000..ad03a42030 --- /dev/null +++ b/packages/clerk/src/operations/platform/users/banUser.ts @@ -0,0 +1,267 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const BanUserInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + envOrInsID: Schema.String.pipe(T.PathParam()), + userID: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "POST", + path: "/platform/applications/{applicationID}/instances/{envOrInsID}/users/{userID}/ban", + }), +); +export type BanUserInput = typeof BanUserInput.Type; + +// Output Schema +export const BanUserOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["user"]), + external_id: Schema.NullOr(Schema.String), + primary_email_address_id: Schema.NullOr(Schema.String), + primary_phone_number_id: Schema.NullOr(Schema.String), + primary_web3_wallet_id: Schema.NullOr(Schema.String), + username: Schema.NullOr(Schema.String), + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + locale: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + email_addresses: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + phone_numbers: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + web3_wallets: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["web3_wallet"]), + web3_wallet: Schema.String, + verification: Schema.Unknown, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + passkeys: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["passkey"]), + name: Schema.String, + last_used_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + password_enabled: Schema.Boolean, + two_factor_enabled: Schema.Boolean, + totp_enabled: Schema.Boolean, + backup_code_enabled: Schema.Boolean, + mfa_enabled_at: Schema.NullOr(Schema.Number), + mfa_disabled_at: Schema.NullOr(Schema.Number), + password_last_updated_at: Schema.optional(Schema.NullOr(Schema.Number)), + external_accounts: Schema.Array( + Schema.Struct({ + object: Schema.Literals([ + "external_account", + "facebook_account", + "google_account", + ]), + id: Schema.String, + provider: Schema.String, + identification_id: Schema.String, + provider_user_id: Schema.String, + approved_scopes: Schema.String, + email_address: Schema.String, + email_address_verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + first_name: Schema.String, + last_name: Schema.String, + avatar_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + phone_number: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + saml_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["saml_account"]), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + saml_connection: Schema.optional(Schema.Unknown), + }), + ), + enterprise_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["enterprise_account"]), + protocol: Schema.optional(Schema.Literals(["oauth", "saml"])), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + enterprise_connection_id: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + enterprise_connection: Schema.optional(Schema.Unknown), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + organization_memberships: Schema.optional( + Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + last_sign_in_at: Schema.NullOr(Schema.Number), + banned: Schema.Boolean, + locked: Schema.Boolean, + deprovisioned: Schema.optional(Schema.Boolean), + lockout_expires_in_seconds: Schema.NullOr(Schema.Number), + verification_attempts_remaining: Schema.NullOr(Schema.Number), + updated_at: Schema.Number, + created_at: Schema.Number, + delete_self_enabled: Schema.Boolean, + create_organization_enabled: Schema.Boolean, + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + last_active_at: Schema.NullOr(Schema.Number), + legal_accepted_at: Schema.NullOr(Schema.Number), + bypass_client_trust: Schema.optional(Schema.Boolean), + scim: Schema.optional( + Schema.NullOr( + Schema.Struct({ + directory_id: Schema.String, + directory_enabled: Schema.optional(Schema.Boolean), + external_id: Schema.NullOr(Schema.String), + }), + ), + ), +}); +export type BanUserOutput = typeof BanUserOutput.Type; + +// The operation +/** + * Ban a user + * + * Marks a user as banned within an application instance. This terminates their active sessions + * (marks them as revoked) and prevents them from signing in again. + * Requires the `users:manage` scope. + * + * @param applicationID - Application ID. + * @param envOrInsID - Environment type (e.g., "development", "production") or instance ID. + + * @param userID - User ID. + */ +export const banUser = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: BanUserInput, + outputSchema: BanUserOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/platform/users/deleteUser.ts b/packages/clerk/src/operations/platform/users/deleteUser.ts new file mode 100644 index 0000000000..322bf54c37 --- /dev/null +++ b/packages/clerk/src/operations/platform/users/deleteUser.ts @@ -0,0 +1,48 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const DeleteUserInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + envOrInsID: Schema.String.pipe(T.PathParam()), + userID: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "DELETE", + path: "/platform/applications/{applicationID}/instances/{envOrInsID}/users/{userID}", + }), +); +export type DeleteUserInput = typeof DeleteUserInput.Type; + +// Output Schema +export const DeleteUserOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + object: Schema.String, + id: Schema.optional(Schema.String), + slug: Schema.optional(Schema.String), + deleted: Schema.Boolean, + external_id: Schema.optional(Schema.String), +}); +export type DeleteUserOutput = typeof DeleteUserOutput.Type; + +// The operation +/** + * Delete a user + * + * Delete a user within an application instance. Returns the deleted user object. + * Deleting a user that does not exist (or has already been deleted) returns 404. + * The `envOrInsID` parameter can be either an environment type (e.g., "development", "production") + * or an instance ID. + * Requires the `users:delete` scope. + * + * @param applicationID - Application ID. + * @param envOrInsID - Environment type (e.g., "development", "production") or instance ID. + + * @param userID - User ID. + */ +export const deleteUser = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: DeleteUserInput, + outputSchema: DeleteUserOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/operations/platform/users/index.ts b/packages/clerk/src/operations/platform/users/index.ts new file mode 100644 index 0000000000..457faba96a --- /dev/null +++ b/packages/clerk/src/operations/platform/users/index.ts @@ -0,0 +1,4 @@ +export * from "./listInstanceUsers.ts"; +export * from "./deleteUser.ts"; +export * from "./banUser.ts"; +export * from "./unbanUser.ts"; diff --git a/packages/clerk/src/operations/platform/users/listInstanceUsers.ts b/packages/clerk/src/operations/platform/users/listInstanceUsers.ts new file mode 100644 index 0000000000..5090861c4e --- /dev/null +++ b/packages/clerk/src/operations/platform/users/listInstanceUsers.ts @@ -0,0 +1,309 @@ +import * as Schema from "effect/Schema"; +import { + BadRequest, + Forbidden, + NotFound, + UnprocessableEntity, +} from "../../../errors.ts"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; + +// Input Schema +export const ListInstanceUsersInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct( + { + applicationID: Schema.String.pipe(T.PathParam()), + envOrInsID: Schema.String.pipe(T.PathParam()), + query: Schema.optional(Schema.String), + order_by: Schema.optional(Schema.String), + limit: Schema.optional(Schema.Number), + offset: Schema.optional(Schema.Number), + }, +).pipe( + T.Http({ + method: "GET", + path: "/platform/applications/{applicationID}/instances/{envOrInsID}/users", + }), +); +export type ListInstanceUsersInput = typeof ListInstanceUsersInput.Type; + +// Output Schema +export const ListInstanceUsersOutput = + /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + data: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["user"]), + external_id: Schema.NullOr(Schema.String), + primary_email_address_id: Schema.NullOr(Schema.String), + primary_phone_number_id: Schema.NullOr(Schema.String), + primary_web3_wallet_id: Schema.NullOr(Schema.String), + username: Schema.NullOr(Schema.String), + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + locale: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + email_addresses: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + phone_numbers: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional( + Schema.NullOr(Schema.Array(Schema.String)), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + web3_wallets: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["web3_wallet"]), + web3_wallet: Schema.String, + verification: Schema.Unknown, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + passkeys: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["passkey"]), + name: Schema.String, + last_used_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + password_enabled: Schema.Boolean, + two_factor_enabled: Schema.Boolean, + totp_enabled: Schema.Boolean, + backup_code_enabled: Schema.Boolean, + mfa_enabled_at: Schema.NullOr(Schema.Number), + mfa_disabled_at: Schema.NullOr(Schema.Number), + password_last_updated_at: Schema.optional(Schema.NullOr(Schema.Number)), + external_accounts: Schema.Array( + Schema.Struct({ + object: Schema.Literals([ + "external_account", + "facebook_account", + "google_account", + ]), + id: Schema.String, + provider: Schema.String, + identification_id: Schema.String, + provider_user_id: Schema.String, + approved_scopes: Schema.String, + email_address: Schema.String, + email_address_verified: Schema.optional( + Schema.NullOr(Schema.Boolean), + ), + first_name: Schema.String, + last_name: Schema.String, + avatar_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + phone_number: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + saml_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["saml_account"]), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + last_authenticated_at: Schema.optional( + Schema.NullOr(Schema.Number), + ), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + saml_connection: Schema.optional(Schema.Unknown), + }), + ), + enterprise_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["enterprise_account"]), + protocol: Schema.optional(Schema.Literals(["oauth", "saml"])), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + enterprise_connection_id: Schema.optional( + Schema.NullOr(Schema.String), + ), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + enterprise_connection: Schema.optional(Schema.Unknown), + last_authenticated_at: Schema.optional( + Schema.NullOr(Schema.Number), + ), + }), + ), + organization_memberships: Schema.optional( + Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + last_sign_in_at: Schema.NullOr(Schema.Number), + banned: Schema.Boolean, + locked: Schema.Boolean, + deprovisioned: Schema.optional(Schema.Boolean), + lockout_expires_in_seconds: Schema.NullOr(Schema.Number), + verification_attempts_remaining: Schema.NullOr(Schema.Number), + updated_at: Schema.Number, + created_at: Schema.Number, + delete_self_enabled: Schema.Boolean, + create_organization_enabled: Schema.Boolean, + create_organizations_limit: Schema.optional( + Schema.NullOr(Schema.Number), + ), + last_active_at: Schema.NullOr(Schema.Number), + legal_accepted_at: Schema.NullOr(Schema.Number), + bypass_client_trust: Schema.optional(Schema.Boolean), + scim: Schema.optional( + Schema.NullOr( + Schema.Struct({ + directory_id: Schema.String, + directory_enabled: Schema.optional(Schema.Boolean), + external_id: Schema.NullOr(Schema.String), + }), + ), + ), + }), + ), + total_count: Schema.Number, + }); +export type ListInstanceUsersOutput = typeof ListInstanceUsersOutput.Type; + +// The operation +/** + * List instance users + * + * List all users for an application instance. + * The `envOrInsID` parameter can be either an environment type (e.g., "development", "production") + * or an instance ID. + * Use the `query` parameter to filter users by searching across email addresses, phone numbers, + * usernames, web3 wallets, user IDs, first names, and last names. + * Use the `order_by` parameter to sort the results. Prefix with `+` for ascending or `-` for descending order. + * Valid fields: `created_at`, `updated_at`, `last_sign_in_at`, `last_active_at`, `email_address`, `first_name`, + * `last_name`, `username`, `phone_number`, `web3_wallet`. Default is `-created_at` (most recent first). + * Requires the `users:read` scope. + * + * @param applicationID - Application ID. + * @param envOrInsID - Environment type (e.g., "development", "production") or instance ID. + + * @param query - A search query to filter users. Searches across email addresses, phone numbers, +usernames, web3 wallets, user IDs, first names, and last names. + + * @param order_by - Field to order results by. Prefix with `+` for ascending or `-` for descending order. +Valid fields: `created_at`, `updated_at`, `last_sign_in_at`, `last_active_at`, `email_address`, +`first_name`, `last_name`, `username`, `phone_number`, `web3_wallet`. + + * @param limit - Number of results to return per page (1-500, default 10). + * @param offset - Number of results to skip for pagination. + */ +export const listInstanceUsers = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: ListInstanceUsersInput, + outputSchema: ListInstanceUsersOutput, + errors: [BadRequest, Forbidden, NotFound, UnprocessableEntity] as const, +})); diff --git a/packages/clerk/src/operations/platform/users/unbanUser.ts b/packages/clerk/src/operations/platform/users/unbanUser.ts new file mode 100644 index 0000000000..861bf867df --- /dev/null +++ b/packages/clerk/src/operations/platform/users/unbanUser.ts @@ -0,0 +1,266 @@ +import * as Schema from "effect/Schema"; +import { API } from "../../../platform-client.ts"; +import * as T from "../../../traits.ts"; +import { BadRequest, Forbidden, NotFound } from "../../../errors.ts"; + +// Input Schema +export const UnbanUserInput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + applicationID: Schema.String.pipe(T.PathParam()), + envOrInsID: Schema.String.pipe(T.PathParam()), + userID: Schema.String.pipe(T.PathParam()), +}).pipe( + T.Http({ + method: "POST", + path: "/platform/applications/{applicationID}/instances/{envOrInsID}/users/{userID}/unban", + }), +); +export type UnbanUserInput = typeof UnbanUserInput.Type; + +// Output Schema +export const UnbanUserOutput = /*@__PURE__*/ /*#__PURE__*/ Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["user"]), + external_id: Schema.NullOr(Schema.String), + primary_email_address_id: Schema.NullOr(Schema.String), + primary_phone_number_id: Schema.NullOr(Schema.String), + primary_web3_wallet_id: Schema.NullOr(Schema.String), + username: Schema.NullOr(Schema.String), + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + locale: Schema.optional(Schema.NullOr(Schema.String)), + profile_image_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.NullOr(Schema.Record(Schema.String, Schema.Unknown)), + ), + unsafe_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + email_addresses: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["email_address"]), + email_address: Schema.String, + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + matches_sso_connection: Schema.optional(Schema.Boolean), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + phone_numbers: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["phone_number"]), + phone_number: Schema.String, + reserved_for_second_factor: Schema.optional(Schema.Boolean), + default_second_factor: Schema.optional(Schema.Boolean), + reserved: Schema.Boolean, + verification: Schema.Unknown, + linked_to: Schema.Array( + Schema.Struct({ + type: Schema.String, + id: Schema.String, + }), + ), + backup_codes: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + web3_wallets: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["web3_wallet"]), + web3_wallet: Schema.String, + verification: Schema.Unknown, + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + passkeys: Schema.Array( + Schema.Struct({ + id: Schema.optional(Schema.String), + object: Schema.Literals(["passkey"]), + name: Schema.String, + last_used_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + password_enabled: Schema.Boolean, + two_factor_enabled: Schema.Boolean, + totp_enabled: Schema.Boolean, + backup_code_enabled: Schema.Boolean, + mfa_enabled_at: Schema.NullOr(Schema.Number), + mfa_disabled_at: Schema.NullOr(Schema.Number), + password_last_updated_at: Schema.optional(Schema.NullOr(Schema.Number)), + external_accounts: Schema.Array( + Schema.Struct({ + object: Schema.Literals([ + "external_account", + "facebook_account", + "google_account", + ]), + id: Schema.String, + provider: Schema.String, + identification_id: Schema.String, + provider_user_id: Schema.String, + approved_scopes: Schema.String, + email_address: Schema.String, + email_address_verified: Schema.optional(Schema.NullOr(Schema.Boolean)), + first_name: Schema.String, + last_name: Schema.String, + avatar_url: Schema.optional(Schema.String), + image_url: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + phone_number: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + label: Schema.optional(Schema.NullOr(Schema.String)), + created_at: Schema.Number, + updated_at: Schema.Number, + verification: Schema.Unknown, + }), + ), + saml_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["saml_account"]), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + saml_connection: Schema.optional(Schema.Unknown), + }), + ), + enterprise_accounts: Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["enterprise_account"]), + protocol: Schema.optional(Schema.Literals(["oauth", "saml"])), + provider: Schema.String, + active: Schema.Boolean, + email_address: Schema.String, + first_name: Schema.optional(Schema.NullOr(Schema.String)), + last_name: Schema.optional(Schema.NullOr(Schema.String)), + provider_user_id: Schema.optional(Schema.NullOr(Schema.String)), + enterprise_connection_id: Schema.optional(Schema.NullOr(Schema.String)), + public_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + verification: Schema.Unknown, + enterprise_connection: Schema.optional(Schema.Unknown), + last_authenticated_at: Schema.optional(Schema.NullOr(Schema.Number)), + }), + ), + organization_memberships: Schema.optional( + Schema.Array( + Schema.Struct({ + id: Schema.String, + object: Schema.Literals(["organization_membership"]), + role: Schema.String, + role_name: Schema.optional(Schema.String), + permissions: Schema.Array(Schema.String), + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + organization: Schema.Struct({ + object: Schema.Literals(["organization"]), + id: Schema.String, + name: Schema.String, + slug: Schema.String, + image_url: Schema.optional(Schema.String), + has_image: Schema.Boolean, + members_count: Schema.optional(Schema.Number), + missing_member_with_elevated_permissions: Schema.optional( + Schema.Boolean, + ), + pending_invitations_count: Schema.optional(Schema.Number), + max_allowed_memberships: Schema.Number, + admin_delete_enabled: Schema.Boolean, + public_metadata: Schema.Record(Schema.String, Schema.Unknown), + private_metadata: Schema.optional( + Schema.Record(Schema.String, Schema.Unknown), + ), + created_by: Schema.optional(Schema.String), + created_at: Schema.Number, + updated_at: Schema.Number, + last_active_at: Schema.optional(Schema.Number), + role_set_key: Schema.optional(Schema.NullOr(Schema.String)), + }), + public_user_data: Schema.optional( + Schema.Struct({ + user_id: Schema.String, + first_name: Schema.NullOr(Schema.String), + last_name: Schema.NullOr(Schema.String), + profile_image_url: Schema.NullOr(Schema.String), + image_url: Schema.String, + has_image: Schema.Boolean, + identifier: Schema.optional(Schema.NullOr(Schema.String)), + username: Schema.optional(Schema.NullOr(Schema.String)), + banned: Schema.optional(Schema.Boolean), + }), + ), + created_at: Schema.Number, + updated_at: Schema.Number, + }), + ), + ), + last_sign_in_at: Schema.NullOr(Schema.Number), + banned: Schema.Boolean, + locked: Schema.Boolean, + deprovisioned: Schema.optional(Schema.Boolean), + lockout_expires_in_seconds: Schema.NullOr(Schema.Number), + verification_attempts_remaining: Schema.NullOr(Schema.Number), + updated_at: Schema.Number, + created_at: Schema.Number, + delete_self_enabled: Schema.Boolean, + create_organization_enabled: Schema.Boolean, + create_organizations_limit: Schema.optional(Schema.NullOr(Schema.Number)), + last_active_at: Schema.NullOr(Schema.Number), + legal_accepted_at: Schema.NullOr(Schema.Number), + bypass_client_trust: Schema.optional(Schema.Boolean), + scim: Schema.optional( + Schema.NullOr( + Schema.Struct({ + directory_id: Schema.String, + directory_enabled: Schema.optional(Schema.Boolean), + external_id: Schema.NullOr(Schema.String), + }), + ), + ), +}); +export type UnbanUserOutput = typeof UnbanUserOutput.Type; + +// The operation +/** + * Unban a user + * + * Removes the ban from a user within an application instance. This allows them to sign in again. + * Requires the `users:manage` scope. + * + * @param applicationID - Application ID. + * @param envOrInsID - Environment type (e.g., "development", "production") or instance ID. + + * @param userID - User ID. + */ +export const unbanUser = /*@__PURE__*/ /*#__PURE__*/ API.make(() => ({ + inputSchema: UnbanUserInput, + outputSchema: UnbanUserOutput, + errors: [BadRequest, Forbidden, NotFound] as const, +})); diff --git a/packages/clerk/src/platform-client.ts b/packages/clerk/src/platform-client.ts new file mode 100644 index 0000000000..09cc7b2596 --- /dev/null +++ b/packages/clerk/src/platform-client.ts @@ -0,0 +1,29 @@ +/** + * Clerk Platform API client. + * + * Uses {@link PlatformCredentials} (a Platform API access token). All + * `src/operations/platform/*` files import `{ API }` from this module. + */ +import * as Effect from "effect/Effect"; +import * as Redacted from "effect/Redacted"; +import { makeAPI } from "@distilled.cloud/core/client"; +import { matchClerkError } from "./match-error.ts"; +import { ClerkParseError } from "./errors.ts"; +import { Retry } from "./retry.ts"; +import { PlatformCredentials } from "./credentials.ts"; + +export const API = makeAPI({ + credentials: PlatformCredentials as any, + getBaseUrl: (creds: any) => creds.apiBaseUrl, + getAuthHeaders: (creds: any) => ({ + Authorization: `Bearer ${Redacted.value(creds.accessToken)}`, + }), + matchError: matchClerkError as ( + status: number, + body: unknown, + errors?: readonly unknown[], + headers?: Record, + ) => Effect.Effect, + ParseError: ClerkParseError as any, + retry: Retry as any, +}); diff --git a/packages/clerk/src/retry.ts b/packages/clerk/src/retry.ts new file mode 100644 index 0000000000..d7226b6736 --- /dev/null +++ b/packages/clerk/src/retry.ts @@ -0,0 +1,42 @@ +/** + * Clerk retry configuration. + */ +import * as Context from "effect/Context"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import { + type Policy, + throttlingFactory, + transientFactory, +} from "@distilled.cloud/core/retry"; + +export { + type Options, + type Factory, + type Policy, + makeDefault, + jittered, + capped, + throttlingOptions, + transientOptions, + throttlingFactory, + transientFactory, +} from "@distilled.cloud/core/retry"; + +/** Context tag for configuring retry behavior of Clerk API calls. */ +export class Retry extends Context.Service()("ClerkRetry") {} + +/** Provides a custom retry policy to every Clerk API call below it. */ +export const policy = (optionsOrFactory: Policy) => + Effect.provide(Layer.succeed(Retry, optionsOrFactory)); + +/** Disables all automatic retries. */ +export const none = Effect.provide( + Layer.succeed(Retry, { while: () => false }), +); + +/** Apply the throttling retry policy (retries throttling errors indefinitely). */ +export const throttling = policy(throttlingFactory); + +/** Apply the transient retry policy (retries all transient errors indefinitely). */ +export const transient = policy(transientFactory); diff --git a/packages/clerk/src/sensitive.ts b/packages/clerk/src/sensitive.ts new file mode 100644 index 0000000000..2167a39b2a --- /dev/null +++ b/packages/clerk/src/sensitive.ts @@ -0,0 +1,4 @@ +/** + * Re-export sensitive data schemas from sdk-core. + */ +export * from "@distilled.cloud/core/sensitive"; diff --git a/packages/clerk/src/traits.ts b/packages/clerk/src/traits.ts new file mode 100644 index 0000000000..cf13e396a9 --- /dev/null +++ b/packages/clerk/src/traits.ts @@ -0,0 +1,4 @@ +/** + * Re-export the shared traits system from sdk-core. + */ +export * from "@distilled.cloud/core/traits"; diff --git a/packages/clerk/test/PlatformBanUser.test.ts b/packages/clerk/test/PlatformBanUser.test.ts new file mode 100644 index 0000000000..9f28844cc3 --- /dev/null +++ b/packages/clerk/test/PlatformBanUser.test.ts @@ -0,0 +1,133 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { banUser as PlatformBanUser } from "../src/operations/platform/users/banUser"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { listInstanceUsers as PlatformListInstanceUsers } from "../src/operations/platform/users/listInstanceUsers"; +import { unbanUser as PlatformUnbanUser } from "../src/operations/platform/users/unbanUser"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_USER = `user_does_not_exist_${testRunId}`; + +/** + * Pick the first application/instance/user reachable with the configured + * Platform credentials. We need a real user to ban for the happy path. + */ +const pickAppInstanceAndUser = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformBanUser", + ), + ); + } + const instance = app.instances[0]; + if (!instance) { + return yield* Effect.die( + new Error( + `Application ${app.application_id} has no instances - cannot test PlatformBanUser`, + ), + ); + } + const users = yield* PlatformListInstanceUsers({ + applicationID: app.application_id, + envOrInsID: instance.instance_id, + limit: 1, + }); + const user = users.data[0]; + if (!user) { + return yield* Effect.die( + new Error( + `Instance ${instance.instance_id} has no users - cannot test PlatformBanUser`, + ), + ); + } + return { + applicationID: app.application_id, + envOrInsID: instance.instance_id, + userID: user.id, + }; +}); + +describe("PlatformBanUser", () => { + it("bans an existing user and returns the user envelope with banned=true", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID, userID } = + yield* pickAppInstanceAndUser; + + yield* Effect.gen(function* () { + const result = yield* PlatformBanUser({ + applicationID, + envOrInsID, + userID, + }); + + expect(result.id).toBe(userID); + expect(result.object).toBe("user"); + expect(result.banned).toBe(true); + expect(typeof result.created_at).toBe("number"); + expect(typeof result.updated_at).toBe("number"); + }).pipe( + // Always unban the test user so subsequent runs (and this + // workspace) aren't left with a permanently banned account. + Effect.ensuring( + PlatformUnbanUser({ applicationID, envOrInsID, userID }).pipe( + Effect.ignore, + ), + ), + ); + }), + ); + }); + + it("returns BadRequest for a malformed user id", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppInstanceAndUser; + + const error = yield* PlatformBanUser({ + applicationID, + envOrInsID, + // A whitespace-only path segment trips Clerk's input + // validation (400) before the resource lookup. + userID: " ", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformBanUser({ + applicationID: NON_EXISTENT_FOREIGN_APP, + envOrInsID: "production", + userID: NON_EXISTENT_USER, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for a user id that does not exist on a real instance", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppInstanceAndUser; + + const error = yield* PlatformBanUser({ + applicationID, + envOrInsID, + userID: NON_EXISTENT_USER, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformCancelApplicationTransfer.test.ts b/packages/clerk/test/PlatformCancelApplicationTransfer.test.ts new file mode 100644 index 0000000000..a6ccf74e00 --- /dev/null +++ b/packages/clerk/test/PlatformCancelApplicationTransfer.test.ts @@ -0,0 +1,103 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { cancelApplicationTransfer as PlatformCancelApplicationTransfer } from "../src/operations/platform/applicationTransfers/cancelApplicationTransfer"; +import { createApplicationTransfer as PlatformCreateApplicationTransfer } from "../src/operations/platform/applicationTransfers/createApplicationTransfer"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_APP = `app_does_not_exist_${testRunId}`; +const NON_EXISTENT_TRANSFER = `atrn_does_not_exist_${testRunId}`; + +/** + * Pick the first application available on the Platform account. Application + * transfers are scoped to a specific application, so we need a real one. + */ +const pickApplication = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformCancelApplicationTransfer", + ), + ); + } + return app.application_id; +}); + +describe("PlatformCancelApplicationTransfer", () => { + it("cancels a pending transfer", async () => { + await runEffect( + Effect.gen(function* () { + const applicationID = yield* pickApplication; + const created = yield* PlatformCreateApplicationTransfer({ + applicationID, + }); + + const canceled = yield* PlatformCancelApplicationTransfer({ + applicationID, + transferID: created.id, + }); + + expect(canceled.object).toBe("application_transfer"); + expect(canceled.id).toBe(created.id); + expect(canceled.application_id).toBe(applicationID); + expect(canceled.status).toBe("canceled"); + expect(canceled.canceled_at).not.toBeNull(); + expect(canceled.completed_at).toBeNull(); + }), + ); + }); + + it("returns NotFound for a non-existent transfer id on a real application", async () => { + await runEffect( + Effect.gen(function* () { + const applicationID = yield* pickApplication; + const error = yield* PlatformCancelApplicationTransfer({ + applicationID, + transferID: NON_EXISTENT_TRANSFER, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformCancelApplicationTransfer({ + applicationID: NON_EXISTENT_APP, + transferID: NON_EXISTENT_TRANSFER, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns Conflict when canceling a transfer that is no longer pending", async () => { + await runEffect( + Effect.gen(function* () { + const applicationID = yield* pickApplication; + const created = yield* PlatformCreateApplicationTransfer({ + applicationID, + }); + + // First cancel succeeds and moves the transfer out of `pending`. + yield* PlatformCancelApplicationTransfer({ + applicationID, + transferID: created.id, + }); + + // Second cancel must fail because only pending transfers can be canceled. + const error = yield* PlatformCancelApplicationTransfer({ + applicationID, + transferID: created.id, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Conflict"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformClaimAccountlessApplication.test.ts b/packages/clerk/test/PlatformClaimAccountlessApplication.test.ts new file mode 100644 index 0000000000..23c8b8a155 --- /dev/null +++ b/packages/clerk/test/PlatformClaimAccountlessApplication.test.ts @@ -0,0 +1,118 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { claimAccountlessApplication as PlatformClaimAccountlessApplication } from "../src/operations/platform/applications/claimAccountlessApplication"; +import { deleteApplication as PlatformDeleteApplication } from "../src/operations/platform/applications/deleteApplication"; +import { runEffect, testRunId } from "./setup"; + +const applicationName = (suffix: string): string => + `distilled-clerk-accountless-${suffix}-${testRunId}`; + +// Accountless applications are created by Clerk framework integrations +// (e.g. the Next.js/React middleware in keyless mode). There is no +// platform API to create one, so a valid single-use claim token must be +// supplied via the CLERK_ACCOUNTLESS_CLAIM_TOKEN environment variable +// for the happy path to run. Without it, the happy path test fails loudly. +const CLAIM_TOKEN: string | undefined = + process.env.CLERK_ACCOUNTLESS_CLAIM_TOKEN; + +const NON_EXISTENT_TOKEN = `accto_does_not_exist_${testRunId}`; + +describe("PlatformClaimAccountlessApplication", () => { + it("claims an accountless application using a valid single-use token", async () => { + await runEffect( + Effect.gen(function* () { + if (!CLAIM_TOKEN) { + return yield* Effect.die( + new Error( + "CLERK_ACCOUNTLESS_CLAIM_TOKEN env var is required for the PlatformClaimAccountlessApplication happy path", + ), + ); + } + + const claimed = yield* PlatformClaimAccountlessApplication({ + token: CLAIM_TOKEN, + name: applicationName("happy"), + }); + + yield* Effect.gen(function* () { + expect(typeof claimed.application_id).toBe("string"); + expect(claimed.application_id.length).toBeGreaterThan(0); + expect(claimed.name).toBe(applicationName("happy")); + expect(Array.isArray(claimed.instances)).toBe(true); + expect(claimed.instances.length).toBeGreaterThan(0); + for (const instance of claimed.instances) { + expect(typeof instance.instance_id).toBe("string"); + expect(["development", "production"]).toContain( + instance.environment_type, + ); + } + }).pipe( + // Once the token is consumed the application belongs to the + // workspace and must be cleaned up so subsequent test runs do + // not accumulate dangling applications. + Effect.ensuring( + PlatformDeleteApplication({ + applicationID: claimed.application_id, + }).pipe(Effect.ignore), + ), + ); + }), + ); + }); + + it("returns BadRequest when the token is empty", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformClaimAccountlessApplication({ + token: "", + name: applicationName("bad-request"), + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); + + it("returns NotFound for a token that does not match any accountless application", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformClaimAccountlessApplication({ + token: NON_EXISTENT_TOKEN, + name: applicationName("not-found"), + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); + + it("returns Forbidden when the caller is not allowed to claim the accountless application", async () => { + await runEffect( + Effect.gen(function* () { + // A token from another workspace cannot be claimed by this caller. + // We deliberately use a value that is well-formed enough to pass + // initial validation but does not belong to the authenticated + // principal, so the API responds with 403 Forbidden. + const error = yield* PlatformClaimAccountlessApplication({ + token: `accto_foreign_workspace_${testRunId}`, + name: applicationName("forbidden"), + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns UnprocessableEntity when the name is empty", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformClaimAccountlessApplication({ + token: NON_EXISTENT_TOKEN, + name: "", + }).pipe(Effect.flip); + + expect(error._tag).toBe("UnprocessableEntity"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformCreateApplication.test.ts b/packages/clerk/test/PlatformCreateApplication.test.ts new file mode 100644 index 0000000000..cd42a34b2f --- /dev/null +++ b/packages/clerk/test/PlatformCreateApplication.test.ts @@ -0,0 +1,83 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { createApplication as PlatformCreateApplication } from "../src/operations/platform/applications/createApplication"; +import { deleteApplication as PlatformDeleteApplication } from "../src/operations/platform/applications/deleteApplication"; +import { runEffect, testRunId } from "./setup"; + +const appName = (suffix: string): string => + `distilled-clerk-createapp-${suffix}-${testRunId}`; + +describe("PlatformCreateApplication", () => { + it("creates a new application and returns its details", async () => { + await runEffect( + Effect.gen(function* () { + const created = yield* PlatformCreateApplication({ + name: appName("happy"), + }); + + yield* Effect.gen(function* () { + expect(typeof created.application_id).toBe("string"); + expect(created.application_id.length).toBeGreaterThan(0); + expect(created.name).toBe(appName("happy")); + expect(Array.isArray(created.instances)).toBe(true); + expect(created.instances.length).toBeGreaterThan(0); + for (const instance of created.instances) { + expect(typeof instance.instance_id).toBe("string"); + expect(["development", "production"]).toContain( + instance.environment_type, + ); + } + }).pipe( + Effect.ensuring( + PlatformDeleteApplication({ + applicationID: created.application_id, + }).pipe(Effect.ignore), + ), + ); + }), + ); + }); + + it("returns BadRequest when the name is empty", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformCreateApplication({ + name: "", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); + + it("returns UnprocessableEntity for an invalid environment_types value", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformCreateApplication({ + name: appName("unproc"), + // The API only accepts "development" and "production" here; + // any other token is a semantically invalid enum value. + environment_types: ["not_a_real_environment"], + }).pipe(Effect.flip); + + expect(error._tag).toBe("UnprocessableEntity"); + }), + ); + }); + + it("returns Forbidden when the template references a resource the caller cannot access", async () => { + await runEffect( + Effect.gen(function* () { + // Templates are workspace-scoped. Passing a template id that + // belongs to a different workspace must fail authorization + // rather than fall through to a 404 or 422. + const error = yield* PlatformCreateApplication({ + name: appName("forbidden"), + template: `tmpl_2ForeignWorkspaceTemplate${testRunId}`, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformCreateApplicationDomain.test.ts b/packages/clerk/test/PlatformCreateApplicationDomain.test.ts new file mode 100644 index 0000000000..ee6620c1a6 --- /dev/null +++ b/packages/clerk/test/PlatformCreateApplicationDomain.test.ts @@ -0,0 +1,138 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { createApplication as PlatformCreateApplication } from "../src/operations/platform/applications/createApplication"; +import { createApplicationDomain as PlatformCreateApplicationDomain } from "../src/operations/platform/domains/createApplicationDomain"; +import { deleteApplication as PlatformDeleteApplication } from "../src/operations/platform/applications/deleteApplication"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_APP = `app_does_not_exist_${testRunId}`; + +const appName = (suffix: string): string => + `distilled-clerk-createdomain-${suffix}-${testRunId}`; + +const providerDomainName = (suffix: string): string => + `satellite-${suffix}-${testRunId}.example.com`; + +/** + * Provision a fresh application with both development and production + * instances. PlatformCreateApplicationDomain attaches a provider domain + * to the production instance, so the app must have one. + */ +const withFreshApplicationWithProduction = ( + suffix: string, + testFn: (applicationID: string) => Effect.Effect, +) => + Effect.gen(function* () { + const created = yield* PlatformCreateApplication({ + name: appName(suffix), + environment_types: ["development", "production"], + }); + return yield* testFn(created.application_id).pipe( + Effect.ensuring( + PlatformDeleteApplication({ + applicationID: created.application_id, + }).pipe(Effect.ignore), + ), + ); + }); + +describe("PlatformCreateApplicationDomain", () => { + it("creates a provider domain on an application", async () => { + await runEffect( + withFreshApplicationWithProduction("happy", (applicationID) => + Effect.gen(function* () { + const domain = yield* PlatformCreateApplicationDomain({ + applicationID, + name: providerDomainName("happy"), + }); + + expect(domain.object).toBe("domain"); + expect(domain.id).toMatch(/.+/); + expect(domain.name).toMatch(/.+/); + expect(domain.frontend_api_url).toMatch(/.+/); + expect(typeof domain.created_at).toBe("string"); + expect(typeof domain.updated_at).toBe("string"); + }), + ), + ); + }); + + it("returns BadRequest when the name is empty", async () => { + await runEffect( + withFreshApplicationWithProduction("bad-request", (applicationID) => + Effect.gen(function* () { + const error = yield* PlatformCreateApplicationDomain({ + applicationID, + name: "", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ), + ); + }); + + it("returns UnprocessableEntity for a malformed domain", async () => { + await runEffect( + withFreshApplicationWithProduction("unproc", (applicationID) => + Effect.gen(function* () { + const error = yield* PlatformCreateApplicationDomain({ + applicationID, + name: "not a real domain", + }).pipe(Effect.flip); + + expect(error._tag).toBe("UnprocessableEntity"); + }), + ), + ); + }); + + it("returns Conflict when the same domain is registered twice", async () => { + await runEffect( + withFreshApplicationWithProduction("conflict", (applicationID) => + Effect.gen(function* () { + const name = providerDomainName("conflict"); + + // First registration succeeds. + yield* PlatformCreateApplicationDomain({ applicationID, name }); + + // Re-registering the same name on the same application must + // collide with the existing entry and return 409 Conflict. + const error = yield* PlatformCreateApplicationDomain({ + applicationID, + name, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Conflict"); + }), + ), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformCreateApplicationDomain({ + applicationID: NON_EXISTENT_FOREIGN_APP, + name: providerDomainName("forbidden"), + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for an application id that does not exist", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformCreateApplicationDomain({ + applicationID: NON_EXISTENT_APP, + name: providerDomainName("not-found"), + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformCreateApplicationTransfer.test.ts b/packages/clerk/test/PlatformCreateApplicationTransfer.test.ts new file mode 100644 index 0000000000..53f3826703 --- /dev/null +++ b/packages/clerk/test/PlatformCreateApplicationTransfer.test.ts @@ -0,0 +1,98 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { cancelApplicationTransfer as PlatformCancelApplicationTransfer } from "../src/operations/platform/applicationTransfers/cancelApplicationTransfer"; +import { createApplicationTransfer as PlatformCreateApplicationTransfer } from "../src/operations/platform/applicationTransfers/createApplicationTransfer"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_APP = `app_does_not_exist_${testRunId}`; + +/** + * Pick the first application available on the Platform account. Used for both + * happy-path and Conflict-path tests (Clerk allows only one pending transfer + * per application). + */ +const pickApplication = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformCreateApplicationTransfer", + ), + ); + } + return app.application_id; +}); + +describe("PlatformCreateApplicationTransfer", () => { + it("creates a pending transfer for an owned application", async () => { + await runEffect( + Effect.gen(function* () { + const applicationID = yield* pickApplication; + + const transfer = yield* PlatformCreateApplicationTransfer({ + applicationID, + }); + + yield* Effect.gen(function* () { + expect(transfer.object).toBe("application_transfer"); + expect(transfer.id).toMatch(/.+/); + expect(transfer.code).toMatch(/.+/); + expect(transfer.application_id).toBe(applicationID); + expect(transfer.status).toBe("pending"); + expect(transfer.expires_at).toMatch(/.+/); + expect(transfer.created_at).toMatch(/.+/); + expect(transfer.canceled_at).toBeNull(); + expect(transfer.completed_at).toBeNull(); + }).pipe( + Effect.ensuring( + PlatformCancelApplicationTransfer({ + applicationID, + transferID: transfer.id, + }).pipe(Effect.ignore), + ), + ); + }), + ); + }); + + it("returns Conflict when a pending transfer already exists for the application", async () => { + await runEffect( + Effect.gen(function* () { + const applicationID = yield* pickApplication; + + const first = yield* PlatformCreateApplicationTransfer({ + applicationID, + }); + + yield* Effect.gen(function* () { + const error = yield* PlatformCreateApplicationTransfer({ + applicationID, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Conflict"); + }).pipe( + Effect.ensuring( + PlatformCancelApplicationTransfer({ + applicationID, + transferID: first.id, + }).pipe(Effect.ignore), + ), + ); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformCreateApplicationTransfer({ + applicationID: NON_EXISTENT_APP, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformCreateInstance.test.ts b/packages/clerk/test/PlatformCreateInstance.test.ts new file mode 100644 index 0000000000..7ccb7a8540 --- /dev/null +++ b/packages/clerk/test/PlatformCreateInstance.test.ts @@ -0,0 +1,142 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { createApplication as PlatformCreateApplication } from "../src/operations/platform/applications/createApplication"; +import { createInstance as PlatformCreateInstance } from "../src/operations/platform/applications/createInstance"; +import { deleteApplication as PlatformDeleteApplication } from "../src/operations/platform/applications/deleteApplication"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_APP = `app_does_not_exist_${testRunId}`; + +const appName = (suffix: string): string => + `distilled-clerk-instance-${suffix}-${testRunId}`; + +const happyDomain = (suffix: string): string => + `prod-${suffix}-${testRunId}.example.com`; + +/** + * Provision a fresh application for the duration of `testFn`. The application + * (and any production instance created under it) is removed via + * `PlatformDeleteApplication` afterwards so test runs don't accumulate + * orphaned resources on the Platform account. + */ +const withFreshApplication = ( + suffix: string, + testFn: (applicationID: string) => Effect.Effect, +) => + Effect.gen(function* () { + const created = yield* PlatformCreateApplication({ + name: appName(suffix), + }); + return yield* testFn(created.application_id).pipe( + Effect.ensuring( + PlatformDeleteApplication({ + applicationID: created.application_id, + }).pipe(Effect.ignore), + ), + ); + }); + +describe("PlatformCreateInstance", () => { + it("creates a production instance on a fresh application", async () => { + await runEffect( + withFreshApplication("happy", (applicationID) => + Effect.gen(function* () { + const instance = yield* PlatformCreateInstance({ + applicationID, + domain: happyDomain("happy"), + environment_type: "production", + }); + + expect(instance.object).toBe("instance"); + expect(instance.id).toMatch(/.+/); + expect(instance.environment_type).toBe("production"); + expect(instance.active_domain.object).toBe("domain"); + expect(instance.active_domain.name).toMatch(/.+/); + expect(instance.active_domain.frontend_api_url).toMatch(/.+/); + expect(instance.publishable_key).toMatch(/.+/); + expect(typeof instance.created_at).toBe("number"); + expect(typeof instance.updated_at).toBe("number"); + }), + ), + ); + }); + + it("returns BadRequest when the domain is empty", async () => { + await runEffect( + withFreshApplication("bad-request", (applicationID) => + Effect.gen(function* () { + const error = yield* PlatformCreateInstance({ + applicationID, + domain: "", + environment_type: "production", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ), + ); + }); + + it("returns UnprocessableEntity for a malformed domain", async () => { + await runEffect( + withFreshApplication("unproc", (applicationID) => + Effect.gen(function* () { + const error = yield* PlatformCreateInstance({ + applicationID, + domain: "not a real domain", + environment_type: "production", + }).pipe(Effect.flip); + + expect(error._tag).toBe("UnprocessableEntity"); + }), + ), + ); + }); + + it("returns PaymentRequired when the workspace plan does not allow production instances", async () => { + await runEffect( + withFreshApplication("payment", (applicationID) => + Effect.gen(function* () { + // Production instance creation is a paid plan feature on Clerk. + // Workspaces without a qualifying plan receive 402 Payment Required. + const error = yield* PlatformCreateInstance({ + applicationID, + domain: happyDomain("payment"), + environment_type: "production", + }).pipe(Effect.flip); + + expect(error._tag).toBe("PaymentRequired"); + }), + ), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformCreateInstance({ + applicationID: NON_EXISTENT_FOREIGN_APP, + domain: happyDomain("forbidden"), + environment_type: "production", + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for an application id that does not exist", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformCreateInstance({ + applicationID: NON_EXISTENT_APP, + domain: happyDomain("not-found"), + environment_type: "production", + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformCreateJWTTemplate.test.ts b/packages/clerk/test/PlatformCreateJWTTemplate.test.ts new file mode 100644 index 0000000000..90e4a6a9ff --- /dev/null +++ b/packages/clerk/test/PlatformCreateJWTTemplate.test.ts @@ -0,0 +1,185 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { createJWTTemplate as PlatformCreateJWTTemplate } from "../src/operations/platform/jwtTemplates/createJWTTemplate"; +import { deleteJWTTemplate as PlatformDeleteJWTTemplate } from "../src/operations/platform/jwtTemplates/deleteJWTTemplate"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_INSTANCE = `ins_does_not_exist_${testRunId}`; + +const pickAppAndInstance = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformCreateJWTTemplate", + ), + ); + } + const instance = app.instances[0]; + if (!instance) { + return yield* Effect.die( + new Error( + `Application ${app.application_id} has no instances - cannot test PlatformCreateJWTTemplate`, + ), + ); + } + return { + applicationID: app.application_id, + envOrInsID: instance.instance_id, + }; +}); + +describe("PlatformCreateJWTTemplate", () => { + it("creates a new JWT template on an existing instance", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + const name = `create-jwt-${testRunId}`; + + let createdID: string | undefined; + yield* Effect.gen(function* () { + const result = yield* PlatformCreateJWTTemplate({ + applicationID, + envOrInsID, + name, + claims: { sub: "{{user.id}}" }, + }); + createdID = result.id; + + expect(result.object).toBe("jwt_template"); + expect(typeof result.id).toBe("string"); + expect(result.name).toBe(name); + expect(typeof result.lifetime).toBe("number"); + expect(typeof result.allowed_clock_skew).toBe("number"); + expect(typeof result.custom_signing_key).toBe("boolean"); + expect(typeof result.signing_algorithm).toBe("string"); + expect(typeof result.created_at).toBe("number"); + expect(typeof result.updated_at).toBe("number"); + }).pipe( + Effect.ensuring( + Effect.suspend(() => + createdID + ? PlatformDeleteJWTTemplate({ + applicationID, + envOrInsID, + templateID: createdID, + }).pipe(Effect.ignore) + : Effect.void, + ), + ), + ); + }), + ); + }); + + it("returns BadRequest for a malformed environment/instance identifier", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID } = yield* pickAppAndInstance; + + const error = yield* PlatformCreateJWTTemplate({ + applicationID, + // Whitespace-only path segment trips Clerk's input validation + // (400) before any resource lookup. + envOrInsID: " ", + name: `bad-jwt-${testRunId}`, + claims: { sub: "{{user.id}}" }, + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); + + it("returns PaymentRequired when supplying a custom signing key on a non-eligible plan", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const error = yield* PlatformCreateJWTTemplate({ + applicationID, + envOrInsID, + name: `pay-jwt-${testRunId}`, + claims: { sub: "{{user.id}}" }, + // custom_signing_key + a user-supplied signing key is gated + // behind paid plans on Clerk's default tiers. + custom_signing_key: true, + signing_algorithm: "HS256", + signing_key: "a".repeat(64), + }).pipe(Effect.flip); + + expect(error._tag).toBe("PaymentRequired"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformCreateJWTTemplate({ + applicationID: NON_EXISTENT_FOREIGN_APP, + envOrInsID: "production", + name: `forbidden-jwt-${testRunId}`, + claims: { sub: "{{user.id}}" }, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for a non-existent instance on a real application", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID } = yield* pickAppAndInstance; + + const error = yield* PlatformCreateJWTTemplate({ + applicationID, + envOrInsID: NON_EXISTENT_INSTANCE, + name: `notfound-jwt-${testRunId}`, + claims: { sub: "{{user.id}}" }, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); + + it("returns UnprocessableEntity when creating a template with a duplicate name", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + const name = `dup-jwt-${testRunId}`; + + const first = yield* PlatformCreateJWTTemplate({ + applicationID, + envOrInsID, + name, + claims: { sub: "{{user.id}}" }, + }); + + yield* Effect.gen(function* () { + const error = yield* PlatformCreateJWTTemplate({ + applicationID, + envOrInsID, + name, + claims: { sub: "{{user.id}}" }, + }).pipe(Effect.flip); + + expect(error._tag).toBe("UnprocessableEntity"); + }).pipe( + Effect.ensuring( + PlatformDeleteJWTTemplate({ + applicationID, + envOrInsID, + templateID: first.id, + }).pipe(Effect.ignore), + ), + ); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformDeleteApplicationDomain.test.ts b/packages/clerk/test/PlatformDeleteApplicationDomain.test.ts new file mode 100644 index 0000000000..7cc870251d --- /dev/null +++ b/packages/clerk/test/PlatformDeleteApplicationDomain.test.ts @@ -0,0 +1,143 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { createApplication as PlatformCreateApplication } from "../src/operations/platform/applications/createApplication"; +import { createApplicationDomain as PlatformCreateApplicationDomain } from "../src/operations/platform/domains/createApplicationDomain"; +import { deleteApplication as PlatformDeleteApplication } from "../src/operations/platform/applications/deleteApplication"; +import { deleteApplicationDomain as PlatformDeleteApplicationDomain } from "../src/operations/platform/domains/deleteApplicationDomain"; +import { listApplicationDomains as PlatformListApplicationDomains } from "../src/operations/platform/domains/listApplicationDomains"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_DOMAIN = `dmn_does_not_exist_${testRunId}`; + +const appName = (suffix: string): string => + `distilled-clerk-deldomain-${suffix}-${testRunId}`; + +const providerDomainName = (suffix: string): string => + `satellite-${suffix}-${testRunId}.example.com`; + +/** + * Provision a fresh application with both development and production + * instances. PlatformDeleteApplicationDomain operates on the production + * instance's domains, so a production instance must exist. The whole + * application is deleted afterwards. + */ +const withFreshApplicationWithProduction = ( + suffix: string, + testFn: (applicationID: string) => Effect.Effect, +) => + Effect.gen(function* () { + const created = yield* PlatformCreateApplication({ + name: appName(suffix), + environment_types: ["development", "production"], + }); + return yield* testFn(created.application_id).pipe( + Effect.ensuring( + PlatformDeleteApplication({ + applicationID: created.application_id, + }).pipe(Effect.ignore), + ), + ); + }); + +describe("PlatformDeleteApplicationDomain", () => { + it("deletes a provider domain that was added to the application", async () => { + await runEffect( + withFreshApplicationWithProduction("happy", (applicationID) => + Effect.gen(function* () { + const provider = yield* PlatformCreateApplicationDomain({ + applicationID, + name: providerDomainName("happy"), + }); + + const result = yield* PlatformDeleteApplicationDomain({ + applicationID, + domainIDOrName: provider.id, + }); + + expect(result.deleted).toBe(true); + expect(result.id).toBe(provider.id); + expect(typeof result.object).toBe("string"); + }), + ), + ); + }); + + it("returns BadRequest for a malformed domain identifier", async () => { + await runEffect( + withFreshApplicationWithProduction("bad-request", (applicationID) => + Effect.gen(function* () { + const error = yield* PlatformDeleteApplicationDomain({ + applicationID, + // Whitespace-only path segment trips input validation (400) + // before the resource lookup. + domainIDOrName: " ", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ), + ); + }); + + it("returns NotFound for a non-existent domain on a real application", async () => { + await runEffect( + withFreshApplicationWithProduction("not-found", (applicationID) => + Effect.gen(function* () { + const error = yield* PlatformDeleteApplicationDomain({ + applicationID, + domainIDOrName: NON_EXISTENT_DOMAIN, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformDeleteApplicationDomain({ + applicationID: NON_EXISTENT_FOREIGN_APP, + domainIDOrName: NON_EXISTENT_DOMAIN, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns UnprocessableEntity when attempting to delete the primary domain", async () => { + await runEffect( + withFreshApplicationWithProduction("unproc", (applicationID) => + Effect.gen(function* () { + // Per the operation docs: "Only provider domains can be deleted. + // The primary domain cannot be deleted." Identify the primary + // domain (the auto-provisioned production instance domain) and + // attempt to delete it — the API must reject with 422. + const domains = yield* PlatformListApplicationDomains({ + applicationID, + }); + const primary = + domains.data.find((d) => d.is_provider_domain === false) ?? + domains.data[0]; + if (!primary) { + return yield* Effect.die( + new Error( + "Fresh application has no domains - cannot test primary-domain deletion guard", + ), + ); + } + + const error = yield* PlatformDeleteApplicationDomain({ + applicationID, + domainIDOrName: primary.id, + }).pipe(Effect.flip); + + expect(error._tag).toBe("UnprocessableEntity"); + }), + ), + ); + }); +}); diff --git a/packages/clerk/test/PlatformDeleteApplicationFavicon.test.ts b/packages/clerk/test/PlatformDeleteApplicationFavicon.test.ts new file mode 100644 index 0000000000..f2055a7026 --- /dev/null +++ b/packages/clerk/test/PlatformDeleteApplicationFavicon.test.ts @@ -0,0 +1,93 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { deleteApplicationFavicon as PlatformDeleteApplicationFavicon } from "../src/operations/platform/applications/deleteApplicationFavicon"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { runEffect, testRunId } from "./setup"; + +// Reasonably-formed-but-fake IDs used to provoke specific 4xx responses. +const NON_EXISTENT_APP = `app_does_not_exist_${testRunId}`; +const MALFORMED_APP = `bad id ${testRunId}`; + +/** + * Pick the first application available on the Platform account. We need a real + * application to exercise the happy path; deleting the favicon on a test app + * is fine because the operation is idempotent and only clears the favicon + * field. + */ +const pickApplication = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformDeleteApplicationFavicon", + ), + ); + } + return app; +}); + +describe("PlatformDeleteApplicationFavicon", () => { + it("clears the favicon on an owned application", async () => { + await runEffect( + Effect.gen(function* () { + const app = yield* pickApplication; + + const result = yield* PlatformDeleteApplicationFavicon({ + applicationID: app.application_id, + }); + + expect(result.application_id).toBe(app.application_id); + expect(typeof result.name).toBe("string"); + expect(Array.isArray(result.instances)).toBe(true); + // Every application has at least one instance (development or production). + expect(result.instances.length).toBeGreaterThan(0); + for (const instance of result.instances) { + expect(typeof instance.instance_id).toBe("string"); + expect(["development", "production"]).toContain( + instance.environment_type, + ); + } + }), + ); + }); + + it("returns NotFound for an application id that does not exist", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformDeleteApplicationFavicon({ + applicationID: NON_EXISTENT_APP, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); + + it("returns Forbidden when the caller has no access to the application", async () => { + await runEffect( + Effect.gen(function* () { + // A well-formed but foreign application id triggers an authorization + // failure rather than a 404 - Clerk distinguishes scope-rejected ids + // from id-shape rejections. + const error = yield* PlatformDeleteApplicationFavicon({ + applicationID: `app_2ForeignApplicationOwnedBySomeoneElse${testRunId}`, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns BadRequest for a malformed application id", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformDeleteApplicationFavicon({ + applicationID: MALFORMED_APP, + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformDeleteApplicationLogo.test.ts b/packages/clerk/test/PlatformDeleteApplicationLogo.test.ts new file mode 100644 index 0000000000..338c43211a --- /dev/null +++ b/packages/clerk/test/PlatformDeleteApplicationLogo.test.ts @@ -0,0 +1,92 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { deleteApplicationLogo as PlatformDeleteApplicationLogo } from "../src/operations/platform/applications/deleteApplicationLogo"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { runEffect, testRunId } from "./setup"; + +// Reasonably-formed-but-fake IDs used to provoke specific 4xx responses. +const NON_EXISTENT_APP = `app_does_not_exist_${testRunId}`; +const MALFORMED_APP = `bad id ${testRunId}`; + +/** + * Pick the first application available on the Platform account. We need a real + * application to exercise the happy path; deleting the logo on a test app is + * fine because the operation is idempotent and only clears the logo field. + */ +const pickApplication = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformDeleteApplicationLogo", + ), + ); + } + return app; +}); + +describe("PlatformDeleteApplicationLogo", () => { + it("clears the logo on an owned application", async () => { + await runEffect( + Effect.gen(function* () { + const app = yield* pickApplication; + + const result = yield* PlatformDeleteApplicationLogo({ + applicationID: app.application_id, + }); + + expect(result.application_id).toBe(app.application_id); + expect(typeof result.name).toBe("string"); + expect(Array.isArray(result.instances)).toBe(true); + // Every application has at least one instance (development or production). + expect(result.instances.length).toBeGreaterThan(0); + for (const instance of result.instances) { + expect(typeof instance.instance_id).toBe("string"); + expect(["development", "production"]).toContain( + instance.environment_type, + ); + } + }), + ); + }); + + it("returns NotFound for an application id that does not exist", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformDeleteApplicationLogo({ + applicationID: NON_EXISTENT_APP, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); + + it("returns Forbidden when the caller has no access to the application", async () => { + await runEffect( + Effect.gen(function* () { + // A well-formed but foreign application id triggers an authorization + // failure rather than a 404 - Clerk distinguishes scope-rejected ids + // from id-shape rejections. + const error = yield* PlatformDeleteApplicationLogo({ + applicationID: `app_2ForeignApplicationOwnedBySomeoneElse${testRunId}`, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns BadRequest for a malformed application id", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformDeleteApplicationLogo({ + applicationID: MALFORMED_APP, + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformDeleteJWTTemplate.test.ts b/packages/clerk/test/PlatformDeleteJWTTemplate.test.ts new file mode 100644 index 0000000000..f591083e73 --- /dev/null +++ b/packages/clerk/test/PlatformDeleteJWTTemplate.test.ts @@ -0,0 +1,111 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { createJWTTemplate as PlatformCreateJWTTemplate } from "../src/operations/platform/jwtTemplates/createJWTTemplate"; +import { deleteJWTTemplate as PlatformDeleteJWTTemplate } from "../src/operations/platform/jwtTemplates/deleteJWTTemplate"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_TEMPLATE = `jtmpl_does_not_exist_${testRunId}`; + +const templateName = (suffix: string): string => + `distilled-clerk-deljwt-${suffix}-${testRunId}`; + +const pickAppAndInstance = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformDeleteJWTTemplate", + ), + ); + } + const instance = app.instances[0]; + if (!instance) { + return yield* Effect.die( + new Error( + `Application ${app.application_id} has no instances - cannot test PlatformDeleteJWTTemplate`, + ), + ); + } + return { + applicationID: app.application_id, + envOrInsID: instance.instance_id, + }; +}); + +describe("PlatformDeleteJWTTemplate", () => { + it("deletes a JWT template that was just created", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const created = yield* PlatformCreateJWTTemplate({ + applicationID, + envOrInsID, + name: templateName("happy"), + claims: { foo: "bar" }, + }); + + const result = yield* PlatformDeleteJWTTemplate({ + applicationID, + envOrInsID, + templateID: created.id, + }); + + expect(result.deleted).toBe(true); + expect(result.id).toBe(created.id); + expect(typeof result.object).toBe("string"); + }), + ); + }); + + it("returns BadRequest for a malformed template id", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const error = yield* PlatformDeleteJWTTemplate({ + applicationID, + envOrInsID, + // Whitespace-only path segment trips input validation (400) + // before the resource lookup. + templateID: " ", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformDeleteJWTTemplate({ + applicationID: NON_EXISTENT_FOREIGN_APP, + envOrInsID: "production", + templateID: NON_EXISTENT_TEMPLATE, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for a template id that does not exist on a real instance", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const error = yield* PlatformDeleteJWTTemplate({ + applicationID, + envOrInsID, + templateID: NON_EXISTENT_TEMPLATE, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformDeleteRedirectURL.test.ts b/packages/clerk/test/PlatformDeleteRedirectURL.test.ts new file mode 100644 index 0000000000..aba419ff17 --- /dev/null +++ b/packages/clerk/test/PlatformDeleteRedirectURL.test.ts @@ -0,0 +1,113 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { createRedirectURL as PlatformCreateRedirectURL } from "../src/operations/platform/redirectUrls/createRedirectURL"; +import { deleteRedirectURL as PlatformDeleteRedirectURL } from "../src/operations/platform/redirectUrls/deleteRedirectURL"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_REDIRECT = `ru_does_not_exist_${testRunId}`; + +const redirectURL = (suffix: string): string => + `https://distilled-clerk-${suffix}-${testRunId}.example.com/oauth/callback`; + +/** + * Pick the first application/instance available on the Platform account. + */ +const pickAppAndInstance = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformDeleteRedirectURL", + ), + ); + } + const instance = app.instances[0]; + if (!instance) { + return yield* Effect.die( + new Error( + `Application ${app.application_id} has no instances - cannot test PlatformDeleteRedirectURL`, + ), + ); + } + return { + applicationID: app.application_id, + envOrInsID: instance.instance_id, + }; +}); + +describe("PlatformDeleteRedirectURL", () => { + it("deletes a redirect URL that was just created", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const created = yield* PlatformCreateRedirectURL({ + applicationID, + envOrInsID, + url: redirectURL("happy"), + }); + + const result = yield* PlatformDeleteRedirectURL({ + applicationID, + envOrInsID, + redirectURLID: created.id, + }); + + expect(result.deleted).toBe(true); + expect(result.id).toBe(created.id); + expect(typeof result.object).toBe("string"); + }), + ); + }); + + it("returns BadRequest for a malformed redirect URL identifier", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const error = yield* PlatformDeleteRedirectURL({ + applicationID, + envOrInsID, + // Whitespace-only path segment trips input validation (400) + // before the resource lookup. + redirectURLID: " ", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformDeleteRedirectURL({ + applicationID: NON_EXISTENT_FOREIGN_APP, + envOrInsID: "production", + redirectURLID: NON_EXISTENT_REDIRECT, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for a redirect URL id that does not exist on a real instance", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const error = yield* PlatformDeleteRedirectURL({ + applicationID, + envOrInsID, + redirectURLID: NON_EXISTENT_REDIRECT, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformDeleteUser.test.ts b/packages/clerk/test/PlatformDeleteUser.test.ts new file mode 100644 index 0000000000..efe433e05a --- /dev/null +++ b/packages/clerk/test/PlatformDeleteUser.test.ts @@ -0,0 +1,124 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { deleteUser as PlatformDeleteUser } from "../src/operations/platform/users/deleteUser"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { listInstanceUsers as PlatformListInstanceUsers } from "../src/operations/platform/users/listInstanceUsers"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_USER = `user_does_not_exist_${testRunId}`; + +const pickAppAndInstance = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformDeleteUser", + ), + ); + } + const instance = app.instances[0]; + if (!instance) { + return yield* Effect.die( + new Error( + `Application ${app.application_id} has no instances - cannot test PlatformDeleteUser`, + ), + ); + } + return { + applicationID: app.application_id, + envOrInsID: instance.instance_id, + }; +}); + +/** + * Pick the most recently created user in the first available instance. + * PlatformDeleteUser is destructive - per the test rules, destructive + * operations on test data are explicitly permitted and must be tested. + */ +const pickUser = Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + const users = yield* PlatformListInstanceUsers({ + applicationID, + envOrInsID, + limit: 1, + }); + const user = users.data[0]; + if (!user) { + return yield* Effect.die( + new Error( + `Instance ${envOrInsID} has no users - cannot test PlatformDeleteUser`, + ), + ); + } + return { applicationID, envOrInsID, userID: user.id }; +}); + +describe("PlatformDeleteUser", () => { + it("deletes an existing user and returns the deletion record", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID, userID } = yield* pickUser; + + const result = yield* PlatformDeleteUser({ + applicationID, + envOrInsID, + userID, + }); + + expect(result.deleted).toBe(true); + expect(result.id).toBe(userID); + expect(typeof result.object).toBe("string"); + }), + ); + }); + + it("returns BadRequest for a malformed user id", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const error = yield* PlatformDeleteUser({ + applicationID, + envOrInsID, + // Whitespace-only path segment trips input validation (400) + // before the resource lookup. + userID: " ", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformDeleteUser({ + applicationID: NON_EXISTENT_FOREIGN_APP, + envOrInsID: "production", + userID: NON_EXISTENT_USER, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for a user id that does not exist on a real instance", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const error = yield* PlatformDeleteUser({ + applicationID, + envOrInsID, + userID: NON_EXISTENT_USER, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformGetApplication.test.ts b/packages/clerk/test/PlatformGetApplication.test.ts new file mode 100644 index 0000000000..4dfc145ca9 --- /dev/null +++ b/packages/clerk/test/PlatformGetApplication.test.ts @@ -0,0 +1,90 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { getApplication as PlatformGetApplication } from "../src/operations/platform/applications/getApplication"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_APP = `app_does_not_exist_${testRunId}`; +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignApplicationOwnedBySomeoneElse${testRunId}`; +const MALFORMED_APP = `bad id ${testRunId}`; + +/** + * Pick the first application available on the Platform account. Every + * Platform API token has at least one app to fetch. + */ +const pickApplication = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformGetApplication", + ), + ); + } + return app; +}); + +describe("PlatformGetApplication", () => { + it("returns details for an owned application", async () => { + await runEffect( + Effect.gen(function* () { + const app = yield* pickApplication; + + const result = yield* PlatformGetApplication({ + applicationID: app.application_id, + }); + + expect(result.application_id).toBe(app.application_id); + expect(typeof result.name).toBe("string"); + expect(Array.isArray(result.instances)).toBe(true); + expect(result.instances.length).toBeGreaterThan(0); + for (const instance of result.instances) { + expect(typeof instance.instance_id).toBe("string"); + expect(["development", "production"]).toContain( + instance.environment_type, + ); + } + }), + ); + }); + + it("returns NotFound for an application id that does not exist", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformGetApplication({ + applicationID: NON_EXISTENT_APP, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); + + it("returns Forbidden when the caller has no access to the application", async () => { + await runEffect( + Effect.gen(function* () { + // A well-formed but foreign application id triggers an authorization + // failure rather than a 404 - Clerk distinguishes scope-rejected ids + // from id-shape rejections. + const error = yield* PlatformGetApplication({ + applicationID: NON_EXISTENT_FOREIGN_APP, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns BadRequest for a malformed application id", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformGetApplication({ + applicationID: MALFORMED_APP, + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformGetApplicationDomain.test.ts b/packages/clerk/test/PlatformGetApplicationDomain.test.ts new file mode 100644 index 0000000000..4c0f1ef58e --- /dev/null +++ b/packages/clerk/test/PlatformGetApplicationDomain.test.ts @@ -0,0 +1,115 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { getApplicationDomain as PlatformGetApplicationDomain } from "../src/operations/platform/domains/getApplicationDomain"; +import { listApplicationDomains as PlatformListApplicationDomains } from "../src/operations/platform/domains/listApplicationDomains"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_APP = `app_does_not_exist_${testRunId}`; +const NON_EXISTENT_DOMAIN = `dmn_does_not_exist_${testRunId}`; + +const pickAppAndDomain = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformGetApplicationDomain", + ), + ); + } + const domains = yield* PlatformListApplicationDomains({ + applicationID: app.application_id, + }); + const domain = domains.data[0]; + if (!domain) { + return yield* Effect.die( + new Error( + `Application ${app.application_id} has no domains - cannot test PlatformGetApplicationDomain`, + ), + ); + } + return { + applicationID: app.application_id, + domainID: domain.id, + }; +}); + +describe("PlatformGetApplicationDomain", () => { + it("fetches an application domain by id and returns the full envelope", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, domainID } = yield* pickAppAndDomain; + + const result = yield* PlatformGetApplicationDomain({ + applicationID, + domainIDOrName: domainID, + }); + + expect(result.object).toBe("domain"); + expect(result.id).toBe(domainID); + expect(typeof result.name).toBe("string"); + expect(typeof result.frontend_api_url).toBe("string"); + expect(typeof result.development_origin).toBe("string"); + expect(typeof result.created_at).toBe("string"); + expect(typeof result.updated_at).toBe("string"); + + if (result.application) { + expect(result.application.object).toBe("application"); + expect(typeof result.application.id).toBe("string"); + } + if (result.instance) { + expect(result.instance.object).toBe("instance"); + expect(["production", "development"]).toContain( + result.instance.environment_type, + ); + } + }), + ); + }); + + it("returns BadRequest for a malformed domain identifier", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID } = yield* pickAppAndDomain; + + const error = yield* PlatformGetApplicationDomain({ + applicationID, + // Whitespace-only path segment trips Clerk's input validation + // before the resource lookup. + domainIDOrName: " ", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformGetApplicationDomain({ + applicationID: NON_EXISTENT_APP, + domainIDOrName: NON_EXISTENT_DOMAIN, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for a non-existent domain on a real application", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID } = yield* pickAppAndDomain; + + const error = yield* PlatformGetApplicationDomain({ + applicationID, + domainIDOrName: NON_EXISTENT_DOMAIN, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformGetApplicationDomainStatus.test.ts b/packages/clerk/test/PlatformGetApplicationDomainStatus.test.ts new file mode 100644 index 0000000000..b9b4083a72 --- /dev/null +++ b/packages/clerk/test/PlatformGetApplicationDomainStatus.test.ts @@ -0,0 +1,108 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { getApplicationDomainStatus as PlatformGetApplicationDomainStatus } from "../src/operations/platform/domains/getApplicationDomainStatus"; +import { listApplicationDomains as PlatformListApplicationDomains } from "../src/operations/platform/domains/listApplicationDomains"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_APP = `app_does_not_exist_${testRunId}`; +const NON_EXISTENT_DOMAIN = `dmn_does_not_exist_${testRunId}`; + +/** + * Pick the first application + one of its existing domains. Every Clerk + * application has at least the default development domain, so this should + * always yield a real (applicationID, domainID) pair. + */ +const pickAppAndDomain = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformGetApplicationDomainStatus", + ), + ); + } + const domains = yield* PlatformListApplicationDomains({ + applicationID: app.application_id, + }); + const domain = domains.data[0]; + if (!domain) { + return yield* Effect.die( + new Error( + `Application ${app.application_id} has no domains - cannot test PlatformGetApplicationDomainStatus`, + ), + ); + } + return { + applicationID: app.application_id, + domainID: domain.id, + }; +}); + +describe("PlatformGetApplicationDomainStatus", () => { + it("returns the status of an existing application domain", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, domainID } = yield* pickAppAndDomain; + + const status = yield* PlatformGetApplicationDomainStatus({ + applicationID, + domainIDOrName: domainID, + }); + + expect(["complete", "incomplete"]).toContain(status.status); + expect(["not_started", "in_progress", "complete"]).toContain( + status.dns.status, + ); + expect(typeof status.dns.cnames).toBe("object"); + expect(typeof status.ssl).toBe("object"); + }), + ); + }); + + it("returns BadRequest for a malformed domain identifier", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID } = yield* pickAppAndDomain; + + const error = yield* PlatformGetApplicationDomainStatus({ + applicationID, + // A whitespace-only path segment triggers Clerk's input + // validation (400) rather than a 404 lookup miss. + domainIDOrName: " ", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformGetApplicationDomainStatus({ + applicationID: NON_EXISTENT_APP, + domainIDOrName: NON_EXISTENT_DOMAIN, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for a non-existent domain on a real application", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID } = yield* pickAppAndDomain; + + const error = yield* PlatformGetApplicationDomainStatus({ + applicationID, + domainIDOrName: NON_EXISTENT_DOMAIN, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformGetApplicationTransfer.test.ts b/packages/clerk/test/PlatformGetApplicationTransfer.test.ts new file mode 100644 index 0000000000..6ac8725693 --- /dev/null +++ b/packages/clerk/test/PlatformGetApplicationTransfer.test.ts @@ -0,0 +1,91 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { cancelApplicationTransfer as PlatformCancelApplicationTransfer } from "../src/operations/platform/applicationTransfers/cancelApplicationTransfer"; +import { createApplicationTransfer as PlatformCreateApplicationTransfer } from "../src/operations/platform/applicationTransfers/createApplicationTransfer"; +import { getApplicationTransfer as PlatformGetApplicationTransfer } from "../src/operations/platform/applicationTransfers/getApplicationTransfer"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_APP = `app_does_not_exist_${testRunId}`; +const NON_EXISTENT_TRANSFER = `atrn_does_not_exist_${testRunId}`; + +/** + * Pick the first application available on the Platform account. Application + * transfers are scoped to a specific application, so we need a real one. + */ +const pickApplication = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformGetApplicationTransfer", + ), + ); + } + return app.application_id; +}); + +describe("PlatformGetApplicationTransfer", () => { + it("retrieves a transfer that was just created", async () => { + await runEffect( + Effect.gen(function* () { + const applicationID = yield* pickApplication; + const created = yield* PlatformCreateApplicationTransfer({ + applicationID, + }); + + yield* Effect.gen(function* () { + const fetched = yield* PlatformGetApplicationTransfer({ + applicationID, + transferID: created.id, + }); + + expect(fetched.object).toBe("application_transfer"); + expect(fetched.id).toBe(created.id); + expect(fetched.code).toBe(created.code); + expect(fetched.application_id).toBe(applicationID); + expect(fetched.status).toBe("pending"); + expect(fetched.expires_at).toBe(created.expires_at); + expect(fetched.created_at).toBe(created.created_at); + expect(fetched.canceled_at).toBeNull(); + expect(fetched.completed_at).toBeNull(); + }).pipe( + Effect.ensuring( + PlatformCancelApplicationTransfer({ + applicationID, + transferID: created.id, + }).pipe(Effect.ignore), + ), + ); + }), + ); + }); + + it("returns NotFound for a non-existent transfer id on a real application", async () => { + await runEffect( + Effect.gen(function* () { + const applicationID = yield* pickApplication; + const error = yield* PlatformGetApplicationTransfer({ + applicationID, + transferID: NON_EXISTENT_TRANSFER, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformGetApplicationTransfer({ + applicationID: NON_EXISTENT_APP, + transferID: NON_EXISTENT_TRANSFER, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformGetConfig.test.ts b/packages/clerk/test/PlatformGetConfig.test.ts new file mode 100644 index 0000000000..d0400c9558 --- /dev/null +++ b/packages/clerk/test/PlatformGetConfig.test.ts @@ -0,0 +1,88 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { getConfig as PlatformGetConfig } from "../src/operations/platform/config/getConfig"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_APP = `app_does_not_exist_${testRunId}`; +const NON_EXISTENT_INSTANCE = `ins_does_not_exist_${testRunId}`; + +const pickApplication = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformGetConfig", + ), + ); + } + return app; +}); + +describe("PlatformGetConfig", () => { + it("returns the development instance config for an existing application", async () => { + await runEffect( + Effect.gen(function* () { + const app = yield* pickApplication; + + const result = yield* PlatformGetConfig({ + applicationID: app.application_id, + envOrInsID: "development", + }); + + // The output schema only guarantees an optional config_version. + // Assert the envelope shape without requiring it. + expect(typeof result).toBe("object"); + if (result.config_version !== undefined) { + expect(typeof result.config_version).toBe("string"); + } + }), + ); + }); + + it("returns BadRequest for a malformed environment/instance identifier", async () => { + await runEffect( + Effect.gen(function* () { + const app = yield* pickApplication; + + const error = yield* PlatformGetConfig({ + applicationID: app.application_id, + // Whitespace-only path segment trips Clerk's input validation + // before any resource lookup. + envOrInsID: " ", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformGetConfig({ + applicationID: NON_EXISTENT_APP, + envOrInsID: "development", + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for a non-existent instance on a real application", async () => { + await runEffect( + Effect.gen(function* () { + const app = yield* pickApplication; + + const error = yield* PlatformGetConfig({ + applicationID: app.application_id, + envOrInsID: NON_EXISTENT_INSTANCE, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformGetConfigSchema.test.ts b/packages/clerk/test/PlatformGetConfigSchema.test.ts new file mode 100644 index 0000000000..8367bada7a --- /dev/null +++ b/packages/clerk/test/PlatformGetConfigSchema.test.ts @@ -0,0 +1,106 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { getConfigSchema as PlatformGetConfigSchema } from "../src/operations/platform/config/getConfigSchema"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_INSTANCE = `ins_does_not_exist_${testRunId}`; + +const pickAppAndInstance = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformGetConfigSchema", + ), + ); + } + const instance = app.instances[0]; + if (!instance) { + return yield* Effect.die( + new Error( + `Application ${app.application_id} has no instances - cannot test PlatformGetConfigSchema`, + ), + ); + } + return { + applicationID: app.application_id, + envOrInsID: instance.instance_id, + }; +}); + +describe("PlatformGetConfigSchema", () => { + it("returns the JSON Schema for an existing application instance config", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const result = yield* PlatformGetConfigSchema({ + applicationID, + envOrInsID, + }); + + expect(typeof result).toBe("object"); + if (result.$schema !== undefined) { + expect(typeof result.$schema).toBe("string"); + } + if (result.$id !== undefined) { + expect(typeof result.$id).toBe("string"); + } + if (result.type !== undefined) { + expect(typeof result.type).toBe("string"); + } + if (result.properties !== undefined) { + expect(typeof result.properties).toBe("object"); + } + }), + ); + }); + + it("returns BadRequest for a malformed environment/instance identifier", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID } = yield* pickAppAndInstance; + + const error = yield* PlatformGetConfigSchema({ + applicationID, + // Whitespace-only path segment trips Clerk's input validation + // (400) before the resource lookup. + envOrInsID: " ", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformGetConfigSchema({ + applicationID: NON_EXISTENT_FOREIGN_APP, + envOrInsID: "production", + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for a non-existent instance on a real application", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID } = yield* pickAppAndInstance; + + const error = yield* PlatformGetConfigSchema({ + applicationID, + envOrInsID: NON_EXISTENT_INSTANCE, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformGetDomain.test.ts b/packages/clerk/test/PlatformGetDomain.test.ts new file mode 100644 index 0000000000..b8c65bf13b --- /dev/null +++ b/packages/clerk/test/PlatformGetDomain.test.ts @@ -0,0 +1,69 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { getDomain as PlatformGetDomain } from "../src/operations/platform/domains/getDomain"; +import { listDomains as PlatformListDomains } from "../src/operations/platform/domains/listDomains"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_DOMAIN = `dmn_does_not_exist_${testRunId}`; +const FOREIGN_DOMAIN = `dmn_foreign_workspace_${testRunId}`; + +const pickDomain = Effect.gen(function* () { + const domains = yield* PlatformListDomains({}); + const domain = domains.data[0]; + if (!domain) { + return yield* Effect.die( + new Error( + "PlatformListDomains returned no domains - cannot test PlatformGetDomain", + ), + ); + } + return domain; +}); + +describe("PlatformGetDomain", () => { + it("fetches a domain by id and returns the full envelope", async () => { + await runEffect( + Effect.gen(function* () { + const picked = yield* pickDomain; + + const result = yield* PlatformGetDomain({ + domainIDOrName: picked.id, + }); + + expect(result.object).toBe("domain"); + expect(result.id).toBe(picked.id); + expect(typeof result.name).toBe("string"); + expect(typeof result.frontend_api_url).toBe("string"); + expect(result.application.object).toBe("application"); + expect(typeof result.application.id).toBe("string"); + expect(["production", "development"]).toContain( + result.instance.environment_type, + ); + }), + ); + }); + + it("returns NotFound for a domain id that does not exist", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformGetDomain({ + domainIDOrName: NON_EXISTENT_DOMAIN, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); + + it("returns Forbidden when fetching a domain that belongs to another workspace", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformGetDomain({ + domainIDOrName: FOREIGN_DOMAIN, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformGetInstanceUsage.test.ts b/packages/clerk/test/PlatformGetInstanceUsage.test.ts new file mode 100644 index 0000000000..a1a69cbf56 --- /dev/null +++ b/packages/clerk/test/PlatformGetInstanceUsage.test.ts @@ -0,0 +1,108 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { getInstanceUsage as PlatformGetInstanceUsage } from "../src/operations/platform/applications/getInstanceUsage"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_INSTANCE = `ins_does_not_exist_${testRunId}`; + +const pickAppAndInstance = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformGetInstanceUsage", + ), + ); + } + const instance = app.instances[0]; + if (!instance) { + return yield* Effect.die( + new Error( + `Application ${app.application_id} has no instances - cannot test PlatformGetInstanceUsage`, + ), + ); + } + return { + applicationID: app.application_id, + envOrInsID: instance.instance_id, + }; +}); + +describe("PlatformGetInstanceUsage", () => { + it("returns usage totals for the current billing cycle", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const result = yield* PlatformGetInstanceUsage({ + applicationID, + envOrInsID, + }); + + expect(result.application_id).toBe(applicationID); + expect(typeof result.instance_id).toBe("string"); + expect(typeof result.period_start).toBe("string"); + expect(typeof result.period_end).toBe("string"); + + expect(typeof result.mau.total_usage).toBe("number"); + expect(typeof result.mau.total_billable_usage).toBe("number"); + expect(typeof result.mao.total_usage).toBe("number"); + expect(typeof result.mao.total_billable_usage).toBe("number"); + expect(typeof result.custom_domains.total_usage).toBe("number"); + expect(typeof result.sms_messages.total_usage).toBe("number"); + expect(typeof result.sms_messages.tier_a.total_usage).toBe("number"); + expect(typeof result.sms_messages.tier_f.total_usage).toBe("number"); + expect(typeof result.enterprise_connections.total_usage).toBe("number"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformGetInstanceUsage({ + applicationID: NON_EXISTENT_FOREIGN_APP, + envOrInsID: "production", + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for a non-existent instance on a real application", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID } = yield* pickAppAndInstance; + + const error = yield* PlatformGetInstanceUsage({ + applicationID, + envOrInsID: NON_EXISTENT_INSTANCE, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); + + it("returns UnprocessableEntity when only one of start/end is provided", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const error = yield* PlatformGetInstanceUsage({ + applicationID, + envOrInsID, + // Per the docs, `start` and `end` must be provided together; + // supplying only one violates the semantic constraint (422). + start: "2024-01-01", + }).pipe(Effect.flip); + + expect(error._tag).toBe("UnprocessableEntity"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformGetJWTTemplate.test.ts b/packages/clerk/test/PlatformGetJWTTemplate.test.ts new file mode 100644 index 0000000000..6506df6557 --- /dev/null +++ b/packages/clerk/test/PlatformGetJWTTemplate.test.ts @@ -0,0 +1,125 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { createJWTTemplate as PlatformCreateJWTTemplate } from "../src/operations/platform/jwtTemplates/createJWTTemplate"; +import { deleteJWTTemplate as PlatformDeleteJWTTemplate } from "../src/operations/platform/jwtTemplates/deleteJWTTemplate"; +import { getJWTTemplate as PlatformGetJWTTemplate } from "../src/operations/platform/jwtTemplates/getJWTTemplate"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_TEMPLATE = `jtmpl_does_not_exist_${testRunId}`; + +const pickAppAndInstance = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformGetJWTTemplate", + ), + ); + } + const instance = app.instances[0]; + if (!instance) { + return yield* Effect.die( + new Error( + `Application ${app.application_id} has no instances - cannot test PlatformGetJWTTemplate`, + ), + ); + } + return { + applicationID: app.application_id, + envOrInsID: instance.instance_id, + }; +}); + +describe("PlatformGetJWTTemplate", () => { + it("fetches an existing JWT template by id", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const created = yield* PlatformCreateJWTTemplate({ + applicationID, + envOrInsID, + name: `get-jwt-${testRunId}`, + claims: { sub: "{{user.id}}" }, + }); + + yield* Effect.gen(function* () { + const result = yield* PlatformGetJWTTemplate({ + applicationID, + envOrInsID, + templateID: created.id, + }); + + expect(result.object).toBe("jwt_template"); + expect(result.id).toBe(created.id); + expect(result.name).toBe(`get-jwt-${testRunId}`); + expect(typeof result.lifetime).toBe("number"); + expect(typeof result.allowed_clock_skew).toBe("number"); + expect(typeof result.custom_signing_key).toBe("boolean"); + expect(typeof result.signing_algorithm).toBe("string"); + expect(typeof result.created_at).toBe("number"); + expect(typeof result.updated_at).toBe("number"); + }).pipe( + Effect.ensuring( + PlatformDeleteJWTTemplate({ + applicationID, + envOrInsID, + templateID: created.id, + }).pipe(Effect.ignore), + ), + ); + }), + ); + }); + + it("returns BadRequest for a malformed template id", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const error = yield* PlatformGetJWTTemplate({ + applicationID, + envOrInsID, + // Whitespace-only path segment trips Clerk's input validation + // (400) before the resource lookup. + templateID: " ", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformGetJWTTemplate({ + applicationID: NON_EXISTENT_FOREIGN_APP, + envOrInsID: "production", + templateID: NON_EXISTENT_TEMPLATE, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for a non-existent template id on a real instance", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const error = yield* PlatformGetJWTTemplate({ + applicationID, + envOrInsID, + templateID: NON_EXISTENT_TEMPLATE, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformListApplicationDomains.test.ts b/packages/clerk/test/PlatformListApplicationDomains.test.ts new file mode 100644 index 0000000000..5a28a9c7d4 --- /dev/null +++ b/packages/clerk/test/PlatformListApplicationDomains.test.ts @@ -0,0 +1,83 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { listApplicationDomains as PlatformListApplicationDomains } from "../src/operations/platform/domains/listApplicationDomains"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_APP = `app_does_not_exist_${testRunId}`; + +const pickApplication = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformListApplicationDomains", + ), + ); + } + return app; +}); + +describe("PlatformListApplicationDomains", () => { + it("lists domains for an existing application", async () => { + await runEffect( + Effect.gen(function* () { + const app = yield* pickApplication; + + const result = yield* PlatformListApplicationDomains({ + applicationID: app.application_id, + }); + + expect(Array.isArray(result.data)).toBe(true); + expect(typeof result.total_count).toBe("number"); + expect(result.total_count).toBeGreaterThanOrEqual(0); + + for (const domain of result.data) { + expect(domain.object).toBe("domain"); + expect(typeof domain.id).toBe("string"); + expect(typeof domain.name).toBe("string"); + expect(typeof domain.frontend_api_url).toBe("string"); + expect(typeof domain.development_origin).toBe("string"); + expect(typeof domain.created_at).toBe("string"); + expect(typeof domain.updated_at).toBe("string"); + + if (domain.application) { + expect(domain.application.object).toBe("application"); + } + if (domain.instance) { + expect(domain.instance.object).toBe("instance"); + expect(["production", "development"]).toContain( + domain.instance.environment_type, + ); + } + } + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformListApplicationDomains({ + applicationID: NON_EXISTENT_FOREIGN_APP, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for an application id that does not exist", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformListApplicationDomains({ + applicationID: NON_EXISTENT_APP, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformListApplicationTransfers.test.ts b/packages/clerk/test/PlatformListApplicationTransfers.test.ts new file mode 100644 index 0000000000..17d8041bc4 --- /dev/null +++ b/packages/clerk/test/PlatformListApplicationTransfers.test.ts @@ -0,0 +1,73 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { listApplicationTransfers as PlatformListApplicationTransfers } from "../src/operations/platform/applicationTransfers/listApplicationTransfers"; +import { runEffect, testRunId } from "./setup"; + +const FOREIGN_TRANSFER_CURSOR = `apptr_foreign_workspace_${testRunId}`; + +describe("PlatformListApplicationTransfers", () => { + it("lists application transfers for the authenticated workspace", async () => { + await runEffect( + Effect.gen(function* () { + const result = yield* PlatformListApplicationTransfers({}); + + expect(Array.isArray(result.data)).toBe(true); + expect(typeof result.total_count).toBe("number"); + expect(result.total_count).toBeGreaterThanOrEqual(0); + + for (const transfer of result.data) { + expect(transfer.object).toBe("application_transfer"); + expect(typeof transfer.id).toBe("string"); + expect(typeof transfer.code).toBe("string"); + expect(typeof transfer.application_id).toBe("string"); + expect(["pending", "completed", "canceled", "expired"]).toContain( + transfer.status, + ); + expect(typeof transfer.expires_at).toBe("string"); + expect(typeof transfer.created_at).toBe("string"); + } + }), + ); + }); + + it("honors the status filter and returns only matching transfers", async () => { + await runEffect( + Effect.gen(function* () { + const result = yield* PlatformListApplicationTransfers({ + status: "pending", + limit: 5, + }); + + expect(Array.isArray(result.data)).toBe(true); + for (const transfer of result.data) { + expect(transfer.status).toBe("pending"); + } + }), + ); + }); + + it("returns Forbidden when paginating with a cursor that belongs to another workspace", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformListApplicationTransfers({ + starting_after: FOREIGN_TRANSFER_CURSOR, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns UnprocessableEntity when limit is outside the allowed range", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformListApplicationTransfers({ + // The documented range is 1-500; 100k violates the upper bound. + limit: 100_000, + }).pipe(Effect.flip); + + expect(error._tag).toBe("UnprocessableEntity"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformListApplications.test.ts b/packages/clerk/test/PlatformListApplications.test.ts new file mode 100644 index 0000000000..fea527970e --- /dev/null +++ b/packages/clerk/test/PlatformListApplications.test.ts @@ -0,0 +1,59 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { runEffect } from "./setup"; + +describe("PlatformListApplications", () => { + it("lists applications in the authenticated workspace", async () => { + await runEffect( + Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + + expect(Array.isArray(apps)).toBe(true); + for (const app of apps) { + expect(typeof app.application_id).toBe("string"); + expect(app.application_id.length).toBeGreaterThan(0); + expect(typeof app.name).toBe("string"); + expect(Array.isArray(app.instances)).toBe(true); + for (const instance of app.instances) { + expect(typeof instance.instance_id).toBe("string"); + expect(["development", "production"]).toContain( + instance.environment_type, + ); + } + } + }), + ); + }); + + it("returns BadRequest for a malformed include_secret_keys value", async () => { + await runEffect( + Effect.gen(function* () { + // The schema types `include_secret_keys` as a boolean, but the + // wire format is a query string. Sending a non-boolean token + // bypasses Effect's schema (via cast) and lets the server reject + // the raw query value with 400. + const error = yield* PlatformListApplications({ + include_secret_keys: "not-a-boolean" as unknown as boolean, + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); + + it("returns Forbidden when requesting secret keys without the required scope", async () => { + await runEffect( + Effect.gen(function* () { + // `include_secret_keys: true` requires a scope above the default + // applications:read. Calling it with a token that lacks the + // elevated scope yields 403 Forbidden. + const error = yield* PlatformListApplications({ + include_secret_keys: true, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformListDomains.test.ts b/packages/clerk/test/PlatformListDomains.test.ts new file mode 100644 index 0000000000..54217c5c9d --- /dev/null +++ b/packages/clerk/test/PlatformListDomains.test.ts @@ -0,0 +1,80 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { listDomains as PlatformListDomains } from "../src/operations/platform/domains/listDomains"; +import { runEffect, testRunId } from "./setup"; + +const FOREIGN_DOMAIN_CURSOR = `dmn_foreign_workspace_${testRunId}`; + +describe("PlatformListDomains", () => { + it("lists production domains in the authenticated workspace", async () => { + await runEffect( + Effect.gen(function* () { + const result = yield* PlatformListDomains({}); + + expect(typeof result.total_count).toBe("number"); + expect(result.total_count).toBeGreaterThanOrEqual(0); + expect(Array.isArray(result.data)).toBe(true); + for (const domain of result.data) { + expect(domain.object).toBe("domain"); + expect(typeof domain.id).toBe("string"); + expect(typeof domain.name).toBe("string"); + expect(typeof domain.frontend_api_url).toBe("string"); + expect(["production", "development"]).toContain( + domain.instance.environment_type, + ); + // Default scope is production-only. + expect(domain.instance.environment_type).toBe("production"); + } + }), + ); + }); + + it("includes development domains when include_development is set", async () => { + await runEffect( + Effect.gen(function* () { + const result = yield* PlatformListDomains({ + include_development: "true", + limit: 50, + }); + + expect(typeof result.total_count).toBe("number"); + expect(Array.isArray(result.data)).toBe(true); + for (const domain of result.data) { + expect(["production", "development"]).toContain( + domain.instance.environment_type, + ); + } + }), + ); + }); + + it("returns Forbidden when the pagination cursor references a domain outside the caller's workspace", async () => { + await runEffect( + Effect.gen(function* () { + // `starting_after` cursors are domain IDs. Pointing it at an id + // that does not belong to the authenticated workspace must not + // leak data; Clerk responds with 403 Forbidden rather than + // silently treating it as an empty page. + const error = yield* PlatformListDomains({ + starting_after: FOREIGN_DOMAIN_CURSOR, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns UnprocessableEntity for a limit value outside the allowed range", async () => { + await runEffect( + Effect.gen(function* () { + // Per the operation docs, `limit` must be in [1, 500]. A value + // far above that bound is rejected as semantically invalid. + const error = yield* PlatformListDomains({ + limit: 100_000, + }).pipe(Effect.flip); + + expect(error._tag).toBe("UnprocessableEntity"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformListInstanceUsers.test.ts b/packages/clerk/test/PlatformListInstanceUsers.test.ts new file mode 100644 index 0000000000..5202cfbd7f --- /dev/null +++ b/packages/clerk/test/PlatformListInstanceUsers.test.ts @@ -0,0 +1,124 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { listInstanceUsers as PlatformListInstanceUsers } from "../src/operations/platform/users/listInstanceUsers"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_INSTANCE = `ins_does_not_exist_${testRunId}`; + +const pickAppAndInstance = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformListInstanceUsers", + ), + ); + } + const instance = app.instances[0]; + if (!instance) { + return yield* Effect.die( + new Error( + `Application ${app.application_id} has no instances - cannot test PlatformListInstanceUsers`, + ), + ); + } + return { + applicationID: app.application_id, + envOrInsID: instance.instance_id, + }; +}); + +describe("PlatformListInstanceUsers", () => { + it("lists users in an instance and respects the limit", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const result = yield* PlatformListInstanceUsers({ + applicationID, + envOrInsID, + limit: 5, + }); + + expect(typeof result.total_count).toBe("number"); + expect(result.total_count).toBeGreaterThanOrEqual(0); + expect(Array.isArray(result.data)).toBe(true); + expect(result.data.length).toBeLessThanOrEqual(5); + for (const user of result.data) { + expect(user.object).toBe("user"); + expect(typeof user.id).toBe("string"); + expect(typeof user.created_at).toBe("number"); + expect(typeof user.updated_at).toBe("number"); + expect(Array.isArray(user.email_addresses)).toBe(true); + } + }), + ); + }); + + it("returns BadRequest for a malformed instance identifier", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID } = yield* pickAppAndInstance; + + const error = yield* PlatformListInstanceUsers({ + applicationID, + // Whitespace-only path segment trips input validation (400) + // before the resource lookup. + envOrInsID: " ", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformListInstanceUsers({ + applicationID: NON_EXISTENT_FOREIGN_APP, + envOrInsID: "production", + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for an instance id that does not exist on a real application", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID } = yield* pickAppAndInstance; + + const error = yield* PlatformListInstanceUsers({ + applicationID, + envOrInsID: NON_EXISTENT_INSTANCE, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); + + it("returns UnprocessableEntity for an invalid order_by field", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + // `order_by` must reference one of the documented sort fields + // prefixed with `+` or `-`. A bogus field name is a semantically + // invalid sort specifier. + const error = yield* PlatformListInstanceUsers({ + applicationID, + envOrInsID, + order_by: "-not_a_real_field", + }).pipe(Effect.flip); + + expect(error._tag).toBe("UnprocessableEntity"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformListJWTTemplates.test.ts b/packages/clerk/test/PlatformListJWTTemplates.test.ts new file mode 100644 index 0000000000..7fc7c60ee0 --- /dev/null +++ b/packages/clerk/test/PlatformListJWTTemplates.test.ts @@ -0,0 +1,106 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { listJWTTemplates as PlatformListJWTTemplates } from "../src/operations/platform/jwtTemplates/listJWTTemplates"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_INSTANCE = `ins_does_not_exist_${testRunId}`; + +const pickAppAndInstance = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformListJWTTemplates", + ), + ); + } + const instance = app.instances[0]; + if (!instance) { + return yield* Effect.die( + new Error( + `Application ${app.application_id} has no instances - cannot test PlatformListJWTTemplates`, + ), + ); + } + return { + applicationID: app.application_id, + envOrInsID: instance.instance_id, + }; +}); + +describe("PlatformListJWTTemplates", () => { + it("lists JWT templates for an existing application instance", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const result = yield* PlatformListJWTTemplates({ + applicationID, + envOrInsID, + }); + + expect(Array.isArray(result)).toBe(true); + + for (const template of result) { + expect(template.object).toBe("jwt_template"); + expect(typeof template.id).toBe("string"); + expect(typeof template.name).toBe("string"); + expect(typeof template.lifetime).toBe("number"); + expect(typeof template.allowed_clock_skew).toBe("number"); + expect(typeof template.custom_signing_key).toBe("boolean"); + expect(typeof template.signing_algorithm).toBe("string"); + expect(typeof template.created_at).toBe("number"); + expect(typeof template.updated_at).toBe("number"); + } + }), + ); + }); + + it("returns BadRequest for a malformed environment/instance identifier", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID } = yield* pickAppAndInstance; + + const error = yield* PlatformListJWTTemplates({ + applicationID, + // Whitespace-only path segment trips Clerk's input validation + // (400) before the resource lookup. + envOrInsID: " ", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformListJWTTemplates({ + applicationID: NON_EXISTENT_FOREIGN_APP, + envOrInsID: "production", + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for a non-existent instance on a real application", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID } = yield* pickAppAndInstance; + + const error = yield* PlatformListJWTTemplates({ + applicationID, + envOrInsID: NON_EXISTENT_INSTANCE, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformPatchConfig.test.ts b/packages/clerk/test/PlatformPatchConfig.test.ts new file mode 100644 index 0000000000..a22a382d31 --- /dev/null +++ b/packages/clerk/test/PlatformPatchConfig.test.ts @@ -0,0 +1,172 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { patchConfig as PlatformPatchConfig } from "../src/operations/platform/config/patchConfig"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_INSTANCE = `ins_does_not_exist_${testRunId}`; + +const pickAppAndInstance = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformPatchConfig", + ), + ); + } + const instance = app.instances[0]; + if (!instance) { + return yield* Effect.die( + new Error( + `Application ${app.application_id} has no instances - cannot test PlatformPatchConfig`, + ), + ); + } + return { + applicationID: app.application_id, + envOrInsID: instance.instance_id, + }; +}); + +describe("PlatformPatchConfig", () => { + it("previews a config patch via dry_run on an existing instance", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const result = yield* PlatformPatchConfig({ + applicationID, + envOrInsID, + dry_run: true, + }); + + // Output envelope is fully optional; assert shape where present. + expect(typeof result).toBe("object"); + if (result.config_version !== undefined) { + expect(typeof result.config_version).toBe("string"); + } + if (result.dry_run !== undefined) { + expect(typeof result.dry_run).toBe("boolean"); + } + if (result.before !== undefined) { + expect(typeof result.before).toBe("object"); + } + if (result.after !== undefined) { + expect(typeof result.after).toBe("object"); + } + }), + ); + }); + + it("returns BadRequest for a malformed environment/instance identifier", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID } = yield* pickAppAndInstance; + + const error = yield* PlatformPatchConfig({ + applicationID, + // Whitespace-only path segment trips Clerk's input validation + // (400) before the resource lookup. + envOrInsID: " ", + dry_run: true, + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); + + it("returns PaymentRequired when attempting a destructive patch that requires a paid plan", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const error = yield* PlatformPatchConfig({ + applicationID, + envOrInsID, + // Destructive patches (clearing config keys via null) are + // gated behind paid plans on Clerk's default tiers. + destructive: true, + }).pipe(Effect.flip); + + expect(error._tag).toBe("PaymentRequired"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformPatchConfig({ + applicationID: NON_EXISTENT_FOREIGN_APP, + envOrInsID: "production", + dry_run: true, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for a non-existent instance on a real application", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID } = yield* pickAppAndInstance; + + const error = yield* PlatformPatchConfig({ + applicationID, + envOrInsID: NON_EXISTENT_INSTANCE, + dry_run: true, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); + + it("returns Conflict when the submitted config version is stale", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + // Clerk's PATCH enforces optimistic concurrency via the config + // version. Submitting back-to-back patches without a fresh + // version produces a 409 on the second call. + yield* PlatformPatchConfig({ + applicationID, + envOrInsID, + dry_run: true, + }).pipe(Effect.either); + + const error = yield* PlatformPatchConfig({ + applicationID, + envOrInsID, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Conflict"); + }), + ); + }); + + it("returns UnprocessableEntity when dry_run and destructive flags conflict semantically", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + // Combining dry_run preview with destructive clearing is a + // semantic contradiction Clerk surfaces as 422. + const error = yield* PlatformPatchConfig({ + applicationID, + envOrInsID, + dry_run: true, + destructive: true, + }).pipe(Effect.flip); + + expect(error._tag).toBe("UnprocessableEntity"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformPutConfig.test.ts b/packages/clerk/test/PlatformPutConfig.test.ts new file mode 100644 index 0000000000..3cceb201fd --- /dev/null +++ b/packages/clerk/test/PlatformPutConfig.test.ts @@ -0,0 +1,172 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { putConfig as PlatformPutConfig } from "../src/operations/platform/config/putConfig"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_INSTANCE = `ins_does_not_exist_${testRunId}`; + +const pickAppAndInstance = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformPutConfig", + ), + ); + } + const instance = app.instances[0]; + if (!instance) { + return yield* Effect.die( + new Error( + `Application ${app.application_id} has no instances - cannot test PlatformPutConfig`, + ), + ); + } + return { + applicationID: app.application_id, + envOrInsID: instance.instance_id, + }; +}); + +describe("PlatformPutConfig", () => { + it("previews a config replacement via dry_run on an existing instance", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const result = yield* PlatformPutConfig({ + applicationID, + envOrInsID, + dry_run: true, + }); + + // Output envelope is fully optional; assert shape where present. + expect(typeof result).toBe("object"); + if (result.config_version !== undefined) { + expect(typeof result.config_version).toBe("string"); + } + if (result.dry_run !== undefined) { + expect(typeof result.dry_run).toBe("boolean"); + } + if (result.before !== undefined) { + expect(typeof result.before).toBe("object"); + } + if (result.after !== undefined) { + expect(typeof result.after).toBe("object"); + } + }), + ); + }); + + it("returns BadRequest for a malformed environment/instance identifier", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID } = yield* pickAppAndInstance; + + const error = yield* PlatformPutConfig({ + applicationID, + // Whitespace-only path segment trips Clerk's input validation + // (400) before the resource lookup. + envOrInsID: " ", + dry_run: true, + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); + + it("returns PaymentRequired when attempting a destructive replacement that requires a paid plan", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const error = yield* PlatformPutConfig({ + applicationID, + envOrInsID, + // Destructive replacements (clearing config keys via null) are + // gated behind paid plans on Clerk's default tiers. + destructive: true, + }).pipe(Effect.flip); + + expect(error._tag).toBe("PaymentRequired"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformPutConfig({ + applicationID: NON_EXISTENT_FOREIGN_APP, + envOrInsID: "production", + dry_run: true, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for a non-existent instance on a real application", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID } = yield* pickAppAndInstance; + + const error = yield* PlatformPutConfig({ + applicationID, + envOrInsID: NON_EXISTENT_INSTANCE, + dry_run: true, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); + + it("returns Conflict when the submitted config version is stale", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + // Clerk's PUT enforces optimistic concurrency via the config + // version. Submitting back-to-back replacements without a fresh + // version produces a 409 on the second call. + yield* PlatformPutConfig({ + applicationID, + envOrInsID, + dry_run: true, + }).pipe(Effect.either); + + const error = yield* PlatformPutConfig({ + applicationID, + envOrInsID, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Conflict"); + }), + ); + }); + + it("returns UnprocessableEntity when both dry_run and destructive flags conflict semantically", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + // Combining dry_run preview with destructive clearing is a + // semantic contradiction Clerk surfaces as 422. + const error = yield* PlatformPutConfig({ + applicationID, + envOrInsID, + dry_run: true, + destructive: true, + }).pipe(Effect.flip); + + expect(error._tag).toBe("UnprocessableEntity"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformTriggerDNSCheck.test.ts b/packages/clerk/test/PlatformTriggerDNSCheck.test.ts new file mode 100644 index 0000000000..33e5bb760c --- /dev/null +++ b/packages/clerk/test/PlatformTriggerDNSCheck.test.ts @@ -0,0 +1,134 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { listApplicationDomains as PlatformListApplicationDomains } from "../src/operations/platform/domains/listApplicationDomains"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { triggerDNSCheck as PlatformTriggerDNSCheck } from "../src/operations/platform/domains/triggerDNSCheck"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_APP = `app_does_not_exist_${testRunId}`; +const NON_EXISTENT_DOMAIN = `dmn_does_not_exist_${testRunId}`; + +/** + * Pick the first application + one of its existing domains. Every Clerk + * application has at least the default development domain. + */ +const pickAppAndDomain = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformTriggerDNSCheck", + ), + ); + } + const domains = yield* PlatformListApplicationDomains({ + applicationID: app.application_id, + }); + const domain = domains.data[0]; + if (!domain) { + return yield* Effect.die( + new Error( + `Application ${app.application_id} has no domains - cannot test PlatformTriggerDNSCheck`, + ), + ); + } + return { + applicationID: app.application_id, + domainID: domain.id, + }; +}); + +describe("PlatformTriggerDNSCheck", () => { + it("triggers a DNS check and returns the current domain status", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, domainID } = yield* pickAppAndDomain; + + const result = yield* PlatformTriggerDNSCheck({ + applicationID, + domainIDOrName: domainID, + }); + + expect(result.domain_id).toBe(domainID); + expect(["complete", "incomplete"]).toContain(result.status); + expect(["not_started", "in_progress", "complete"]).toContain( + result.dns.status, + ); + expect(typeof result.dns.cnames).toBe("object"); + expect(typeof result.ssl).toBe("object"); + }), + ); + }); + + it("returns BadRequest for a malformed domain identifier", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID } = yield* pickAppAndDomain; + + const error = yield* PlatformTriggerDNSCheck({ + applicationID, + // Whitespace-only path segment trips input validation (400) + // before the resource lookup. + domainIDOrName: " ", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformTriggerDNSCheck({ + applicationID: NON_EXISTENT_APP, + domainIDOrName: NON_EXISTENT_DOMAIN, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for a non-existent domain on a real application", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID } = yield* pickAppAndDomain; + + const error = yield* PlatformTriggerDNSCheck({ + applicationID, + domainIDOrName: NON_EXISTENT_DOMAIN, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); + + it("returns Conflict when a DNS check is already in flight or was recently performed", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, domainID } = yield* pickAppAndDomain; + + // Prime the cooldown / in-flight job. The outcome of this call + // isn't asserted - it may succeed or already conflict depending + // on prior trigger activity. Effect.either reifies the result so + // we don't swallow it silently, we just don't gate on it. + yield* PlatformTriggerDNSCheck({ + applicationID, + domainIDOrName: domainID, + }).pipe(Effect.either); + + // A subsequent trigger immediately afterwards must hit the + // documented "at most one in-flight check" guard. + const error = yield* PlatformTriggerDNSCheck({ + applicationID, + domainIDOrName: domainID, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Conflict"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformUnbanUser.test.ts b/packages/clerk/test/PlatformUnbanUser.test.ts new file mode 100644 index 0000000000..9e3737d89a --- /dev/null +++ b/packages/clerk/test/PlatformUnbanUser.test.ts @@ -0,0 +1,137 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { banUser as PlatformBanUser } from "../src/operations/platform/users/banUser"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { listInstanceUsers as PlatformListInstanceUsers } from "../src/operations/platform/users/listInstanceUsers"; +import { unbanUser as PlatformUnbanUser } from "../src/operations/platform/users/unbanUser"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_USER = `user_does_not_exist_${testRunId}`; + +/** + * Pick the first application/instance/user reachable with the configured + * Platform credentials. We need a real user to ban+unban for the happy path + * since `PlatformUnbanUser` operates on existing users. + */ +const pickAppInstanceAndUser = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformUnbanUser", + ), + ); + } + const instance = app.instances[0]; + if (!instance) { + return yield* Effect.die( + new Error( + `Application ${app.application_id} has no instances - cannot test PlatformUnbanUser`, + ), + ); + } + const users = yield* PlatformListInstanceUsers({ + applicationID: app.application_id, + envOrInsID: instance.instance_id, + limit: 1, + }); + const user = users.data[0]; + if (!user) { + return yield* Effect.die( + new Error( + `Instance ${instance.instance_id} has no users - cannot test PlatformUnbanUser`, + ), + ); + } + return { + applicationID: app.application_id, + envOrInsID: instance.instance_id, + userID: user.id, + }; +}); + +describe("PlatformUnbanUser", () => { + it("removes the ban from a previously banned user", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID, userID } = + yield* pickAppInstanceAndUser; + + // Banning the user is a prerequisite for a meaningful unban call. + yield* PlatformBanUser({ applicationID, envOrInsID, userID }); + + yield* Effect.gen(function* () { + const result = yield* PlatformUnbanUser({ + applicationID, + envOrInsID, + userID, + }); + + expect(result.id).toBe(userID); + expect(result.object).toBe("user"); + expect(result.banned).toBe(false); + expect(typeof result.created_at).toBe("number"); + expect(typeof result.updated_at).toBe("number"); + }).pipe( + // Idempotent safety net: if the assertion-side unban failed, + // make sure we don't leave the test user banned for subsequent runs. + Effect.ensuring( + PlatformUnbanUser({ applicationID, envOrInsID, userID }).pipe( + Effect.ignore, + ), + ), + ); + }), + ); + }); + + it("returns BadRequest for a malformed user id", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppInstanceAndUser; + + const error = yield* PlatformUnbanUser({ + applicationID, + envOrInsID, + // An empty-segment-ish path component triggers Clerk's + // input validation (400) rather than a 404 lookup miss. + userID: " ", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformUnbanUser({ + applicationID: NON_EXISTENT_FOREIGN_APP, + envOrInsID: "production", + userID: NON_EXISTENT_USER, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for a user id that does not exist on a real instance", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppInstanceAndUser; + + const error = yield* PlatformUnbanUser({ + applicationID, + envOrInsID, + userID: NON_EXISTENT_USER, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformUpdateApplication.test.ts b/packages/clerk/test/PlatformUpdateApplication.test.ts new file mode 100644 index 0000000000..f8685447f8 --- /dev/null +++ b/packages/clerk/test/PlatformUpdateApplication.test.ts @@ -0,0 +1,115 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { createApplication as PlatformCreateApplication } from "../src/operations/platform/applications/createApplication"; +import { deleteApplication as PlatformDeleteApplication } from "../src/operations/platform/applications/deleteApplication"; +import { updateApplication as PlatformUpdateApplication } from "../src/operations/platform/applications/updateApplication"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_APP = `app_does_not_exist_${testRunId}`; + +const appName = (suffix: string): string => + `distilled-clerk-updateapp-${suffix}-${testRunId}`; + +const withFreshApplication = ( + suffix: string, + testFn: (applicationID: string) => Effect.Effect, +) => + Effect.gen(function* () { + const created = yield* PlatformCreateApplication({ + name: appName(`${suffix}-orig`), + }); + return yield* testFn(created.application_id).pipe( + Effect.ensuring( + PlatformDeleteApplication({ + applicationID: created.application_id, + }).pipe(Effect.ignore), + ), + ); + }); + +describe("PlatformUpdateApplication", () => { + it("renames an existing application", async () => { + await runEffect( + withFreshApplication("happy", (applicationID) => + Effect.gen(function* () { + const renamed = appName("happy-renamed"); + const updated = yield* PlatformUpdateApplication({ + applicationID, + name: renamed, + }); + + expect(updated.application_id).toBe(applicationID); + expect(updated.name).toBe(renamed); + expect(Array.isArray(updated.instances)).toBe(true); + for (const instance of updated.instances) { + expect(typeof instance.instance_id).toBe("string"); + expect(["development", "production"]).toContain( + instance.environment_type, + ); + } + }), + ), + ); + }); + + it("returns BadRequest when the name is empty", async () => { + await runEffect( + withFreshApplication("bad-request", (applicationID) => + Effect.gen(function* () { + const error = yield* PlatformUpdateApplication({ + applicationID, + name: "", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ), + ); + }); + + it("returns UnprocessableEntity for a name that exceeds the allowed length", async () => { + await runEffect( + withFreshApplication("unproc", (applicationID) => + Effect.gen(function* () { + // Clerk caps application names well below 10k characters; this + // value is far beyond any reasonable limit so the server must + // reject it as semantically invalid. + const oversized = "x".repeat(10_000); + const error = yield* PlatformUpdateApplication({ + applicationID, + name: oversized, + }).pipe(Effect.flip); + + expect(error._tag).toBe("UnprocessableEntity"); + }), + ), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformUpdateApplication({ + applicationID: NON_EXISTENT_FOREIGN_APP, + name: appName("forbidden"), + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for an application id that does not exist", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformUpdateApplication({ + applicationID: NON_EXISTENT_APP, + name: appName("not-found"), + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformUpdateApplicationDomain.test.ts b/packages/clerk/test/PlatformUpdateApplicationDomain.test.ts new file mode 100644 index 0000000000..1a900a2ed0 --- /dev/null +++ b/packages/clerk/test/PlatformUpdateApplicationDomain.test.ts @@ -0,0 +1,153 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { createApplication as PlatformCreateApplication } from "../src/operations/platform/applications/createApplication"; +import { deleteApplication as PlatformDeleteApplication } from "../src/operations/platform/applications/deleteApplication"; +import { updateApplicationDomain as PlatformUpdateApplicationDomain } from "../src/operations/platform/domains/updateApplicationDomain"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_APP = `app_does_not_exist_${testRunId}`; + +const appName = (suffix: string): string => + `distilled-clerk-domain-${suffix}-${testRunId}`; + +const happyDomain = (suffix: string): string => + `prod-${suffix}-${testRunId}.example.com`; + +/** + * Provision a fresh application (with both development and production + * instances) for the duration of `testFn`. PlatformUpdateApplicationDomain + * targets the production domain, so the app must have a production + * instance for the happy path to be meaningful. The whole application is + * deleted afterwards to avoid leaking resources between runs. + */ +const withFreshApplicationWithProduction = ( + suffix: string, + testFn: (applicationID: string) => Effect.Effect, +) => + Effect.gen(function* () { + const created = yield* PlatformCreateApplication({ + name: appName(suffix), + environment_types: ["development", "production"], + }); + return yield* testFn(created.application_id).pipe( + Effect.ensuring( + PlatformDeleteApplication({ + applicationID: created.application_id, + }).pipe(Effect.ignore), + ), + ); + }); + +const withFreshApplication = ( + suffix: string, + testFn: (applicationID: string) => Effect.Effect, +) => + Effect.gen(function* () { + const created = yield* PlatformCreateApplication({ + name: appName(suffix), + }); + return yield* testFn(created.application_id).pipe( + Effect.ensuring( + PlatformDeleteApplication({ + applicationID: created.application_id, + }).pipe(Effect.ignore), + ), + ); + }); + +describe("PlatformUpdateApplicationDomain", () => { + it("updates the production domain on an application", async () => { + await runEffect( + withFreshApplicationWithProduction("happy", (applicationID) => + Effect.gen(function* () { + const updated = yield* PlatformUpdateApplicationDomain({ + applicationID, + name: happyDomain("happy"), + }); + + expect(updated.object).toBe("domain"); + expect(updated.id).toMatch(/.+/); + expect(updated.name).toMatch(/.+/); + expect(updated.frontend_api_url).toMatch(/.+/); + expect(typeof updated.created_at).toBe("string"); + expect(typeof updated.updated_at).toBe("string"); + }), + ), + ); + }); + + it("returns BadRequest when the domain name is empty", async () => { + await runEffect( + withFreshApplicationWithProduction("bad-request", (applicationID) => + Effect.gen(function* () { + const error = yield* PlatformUpdateApplicationDomain({ + applicationID, + name: "", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ), + ); + }); + + it("returns UnprocessableEntity for a malformed domain", async () => { + await runEffect( + withFreshApplicationWithProduction("unproc", (applicationID) => + Effect.gen(function* () { + const error = yield* PlatformUpdateApplicationDomain({ + applicationID, + name: "not a real domain", + }).pipe(Effect.flip); + + expect(error._tag).toBe("UnprocessableEntity"); + }), + ), + ); + }); + + it("returns NotFound when the application has no production domain to update", async () => { + await runEffect( + withFreshApplication("not-found", (applicationID) => + Effect.gen(function* () { + // A fresh application provisioned with the default + // (development-only) environment has no production domain, so + // the PATCH target resource does not exist. + const error = yield* PlatformUpdateApplicationDomain({ + applicationID, + name: happyDomain("not-found"), + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformUpdateApplicationDomain({ + applicationID: NON_EXISTENT_FOREIGN_APP, + name: happyDomain("forbidden"), + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for an application id that does not exist", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformUpdateApplicationDomain({ + applicationID: NON_EXISTENT_APP, + name: happyDomain("missing-app"), + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformUpdateJWTTemplate.test.ts b/packages/clerk/test/PlatformUpdateJWTTemplate.test.ts new file mode 100644 index 0000000000..fb80de1b01 --- /dev/null +++ b/packages/clerk/test/PlatformUpdateJWTTemplate.test.ts @@ -0,0 +1,228 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { createJWTTemplate as PlatformCreateJWTTemplate } from "../src/operations/platform/jwtTemplates/createJWTTemplate"; +import { deleteJWTTemplate as PlatformDeleteJWTTemplate } from "../src/operations/platform/jwtTemplates/deleteJWTTemplate"; +import { listApplications as PlatformListApplications } from "../src/operations/platform/applications/listApplications"; +import { updateJWTTemplate as PlatformUpdateJWTTemplate } from "../src/operations/platform/jwtTemplates/updateJWTTemplate"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_APP = `app_does_not_exist_${testRunId}`; +const NON_EXISTENT_TEMPLATE = `jtmpl_does_not_exist_${testRunId}`; + +const templateName = (suffix: string): string => + `distilled-clerk-jwt-${suffix}-${testRunId}`; + +/** + * Pick the first application/instance available on the Platform account. + * Every Platform API token has at least one app+instance to act on. + */ +const pickAppAndInstance = Effect.gen(function* () { + const apps = yield* PlatformListApplications({}); + const app = apps[0]; + if (!app) { + return yield* Effect.die( + new Error( + "PlatformListApplications returned no applications - cannot test PlatformUpdateJWTTemplate", + ), + ); + } + const instance = app.instances[0]; + if (!instance) { + return yield* Effect.die( + new Error( + `Application ${app.application_id} has no instances - cannot test PlatformUpdateJWTTemplate`, + ), + ); + } + return { + applicationID: app.application_id, + envOrInsID: instance.instance_id, + }; +}); + +describe("PlatformUpdateJWTTemplate", () => { + it("updates an existing JWT template", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const created = yield* PlatformCreateJWTTemplate({ + applicationID, + envOrInsID, + name: templateName("happy-orig"), + claims: { foo: "bar" }, + }); + + yield* Effect.gen(function* () { + const newName = templateName("happy-renamed"); + const updated = yield* PlatformUpdateJWTTemplate({ + applicationID, + envOrInsID, + templateID: created.id, + name: newName, + claims: { baz: "qux" }, + lifetime: 600, + }); + + expect(updated.object).toBe("jwt_template"); + expect(updated.id).toBe(created.id); + expect(updated.name).toBe(newName); + expect(updated.lifetime).toBe(600); + expect(updated.updated_at).toBeGreaterThanOrEqual(created.updated_at); + }).pipe( + Effect.ensuring( + PlatformDeleteJWTTemplate({ + applicationID, + envOrInsID, + templateID: created.id, + }).pipe(Effect.ignore), + ), + ); + }), + ); + }); + + it("returns NotFound for a non-existent template id", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + const error = yield* PlatformUpdateJWTTemplate({ + applicationID, + envOrInsID, + templateID: NON_EXISTENT_TEMPLATE, + name: templateName("not-found"), + claims: { foo: "bar" }, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformUpdateJWTTemplate({ + applicationID: NON_EXISTENT_APP, + envOrInsID: "ins_does_not_exist", + templateID: NON_EXISTENT_TEMPLATE, + name: templateName("forbidden"), + claims: { foo: "bar" }, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns BadRequest when the name is empty", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const created = yield* PlatformCreateJWTTemplate({ + applicationID, + envOrInsID, + name: templateName("bad-request"), + claims: { foo: "bar" }, + }); + + yield* Effect.gen(function* () { + const error = yield* PlatformUpdateJWTTemplate({ + applicationID, + envOrInsID, + templateID: created.id, + name: "", + claims: { foo: "bar" }, + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }).pipe( + Effect.ensuring( + PlatformDeleteJWTTemplate({ + applicationID, + envOrInsID, + templateID: created.id, + }).pipe(Effect.ignore), + ), + ); + }), + ); + }); + + it("returns UnprocessableEntity for an invalid signing algorithm", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const created = yield* PlatformCreateJWTTemplate({ + applicationID, + envOrInsID, + name: templateName("unproc"), + claims: { foo: "bar" }, + }); + + yield* Effect.gen(function* () { + const error = yield* PlatformUpdateJWTTemplate({ + applicationID, + envOrInsID, + templateID: created.id, + name: templateName("unproc"), + claims: { foo: "bar" }, + signing_algorithm: "NOT_A_REAL_ALGORITHM", + }).pipe(Effect.flip); + + expect(error._tag).toBe("UnprocessableEntity"); + }).pipe( + Effect.ensuring( + PlatformDeleteJWTTemplate({ + applicationID, + envOrInsID, + templateID: created.id, + }).pipe(Effect.ignore), + ), + ); + }), + ); + }); + + it("returns PaymentRequired when a paid-only feature is requested without a qualifying plan", async () => { + await runEffect( + Effect.gen(function* () { + const { applicationID, envOrInsID } = yield* pickAppAndInstance; + + const created = yield* PlatformCreateJWTTemplate({ + applicationID, + envOrInsID, + name: templateName("payment"), + claims: { foo: "bar" }, + }); + + yield* Effect.gen(function* () { + // Toggling `custom_signing_key` on is a paid plan feature on Clerk; + // on a non-paid instance the API responds with 402 Payment Required. + const error = yield* PlatformUpdateJWTTemplate({ + applicationID, + envOrInsID, + templateID: created.id, + name: templateName("payment"), + claims: { foo: "bar" }, + custom_signing_key: true, + signing_algorithm: "HS256", + signing_key: "test-signing-key-only-for-422-or-402-discrimination", + }).pipe(Effect.flip); + + expect(error._tag).toBe("PaymentRequired"); + }).pipe( + Effect.ensuring( + PlatformDeleteJWTTemplate({ + applicationID, + envOrInsID, + templateID: created.id, + }).pipe(Effect.ignore), + ), + ); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformUploadApplicationFavicon.test.ts b/packages/clerk/test/PlatformUploadApplicationFavicon.test.ts new file mode 100644 index 0000000000..948608834a --- /dev/null +++ b/packages/clerk/test/PlatformUploadApplicationFavicon.test.ts @@ -0,0 +1,151 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { createApplication as PlatformCreateApplication } from "../src/operations/platform/applications/createApplication"; +import { deleteApplication as PlatformDeleteApplication } from "../src/operations/platform/applications/deleteApplication"; +import { uploadApplicationFavicon as PlatformUploadApplicationFavicon } from "../src/operations/platform/applications/uploadApplicationFavicon"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_APP = `app_does_not_exist_${testRunId}`; + +const appName = (suffix: string): string => + `distilled-clerk-upfav-${suffix}-${testRunId}`; + +/** + * Minimal valid 1x1 transparent PNG (67 bytes). Decoded from the canonical + * smallest-PNG byte sequence and represented as a binary string so it can be + * passed through `file: Schema.String`. + */ +const TINY_PNG_BYTES = new Uint8Array([ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, + 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x1f, 0x15, 0xc4, 0x89, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x44, + 0x41, 0x54, 0x78, 0x9c, 0x62, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x01, 0x0d, + 0x0a, 0x2d, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, + 0x60, 0x82, +]); +const TINY_PNG = String.fromCharCode(...TINY_PNG_BYTES); + +/** + * Roughly 11 MB string used to provoke PayloadTooLarge. Clerk's image upload + * endpoints cap body size at 10 MB. + */ +const OVERSIZED_FILE = "x".repeat(11 * 1024 * 1024); + +/** + * Provision a fresh application for the duration of `testFn`, then delete it + * afterwards so uploaded favicons and the app itself don't accumulate. + */ +const withFreshApplication = ( + suffix: string, + testFn: (applicationID: string) => Effect.Effect, +) => + Effect.gen(function* () { + const created = yield* PlatformCreateApplication({ + name: appName(suffix), + }); + return yield* testFn(created.application_id).pipe( + Effect.ensuring( + PlatformDeleteApplication({ + applicationID: created.application_id, + }).pipe(Effect.ignore), + ), + ); + }); + +describe("PlatformUploadApplicationFavicon", () => { + it("uploads a favicon to a fresh application", async () => { + await runEffect( + withFreshApplication("happy", (applicationID) => + Effect.gen(function* () { + const result = yield* PlatformUploadApplicationFavicon({ + applicationID, + file: TINY_PNG, + }); + + expect(result.application_id).toBe(applicationID); + expect(typeof result.name).toBe("string"); + expect(Array.isArray(result.instances)).toBe(true); + expect(result.instances.length).toBeGreaterThan(0); + for (const instance of result.instances) { + expect(typeof instance.instance_id).toBe("string"); + expect(["development", "production"]).toContain( + instance.environment_type, + ); + } + }), + ), + ); + }); + + it("returns BadRequest when the file field is empty", async () => { + await runEffect( + withFreshApplication("bad-request", (applicationID) => + Effect.gen(function* () { + const error = yield* PlatformUploadApplicationFavicon({ + applicationID, + file: "", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ), + ); + }); + + it("returns UnprocessableEntity when the file is not a valid image", async () => { + await runEffect( + withFreshApplication("unproc", (applicationID) => + Effect.gen(function* () { + const error = yield* PlatformUploadApplicationFavicon({ + applicationID, + file: "this is plain text, definitely not an image", + }).pipe(Effect.flip); + + expect(error._tag).toBe("UnprocessableEntity"); + }), + ), + ); + }); + + it("returns PayloadTooLarge when the file exceeds the size limit", async () => { + await runEffect( + withFreshApplication("too-large", (applicationID) => + Effect.gen(function* () { + const error = yield* PlatformUploadApplicationFavicon({ + applicationID, + file: OVERSIZED_FILE, + }).pipe(Effect.flip); + + expect(error._tag).toBe("PayloadTooLarge"); + }), + ), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformUploadApplicationFavicon({ + applicationID: NON_EXISTENT_FOREIGN_APP, + file: TINY_PNG, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for an application id that does not exist", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformUploadApplicationFavicon({ + applicationID: NON_EXISTENT_APP, + file: TINY_PNG, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); +}); diff --git a/packages/clerk/test/PlatformUploadApplicationLogo.test.ts b/packages/clerk/test/PlatformUploadApplicationLogo.test.ts new file mode 100644 index 0000000000..c7ed4ed2ff --- /dev/null +++ b/packages/clerk/test/PlatformUploadApplicationLogo.test.ts @@ -0,0 +1,151 @@ +import { Effect } from "effect"; +import { describe, expect, it } from "vitest"; +import { createApplication as PlatformCreateApplication } from "../src/operations/platform/applications/createApplication"; +import { deleteApplication as PlatformDeleteApplication } from "../src/operations/platform/applications/deleteApplication"; +import { uploadApplicationLogo as PlatformUploadApplicationLogo } from "../src/operations/platform/applications/uploadApplicationLogo"; +import { runEffect, testRunId } from "./setup"; + +const NON_EXISTENT_FOREIGN_APP = `app_2ForeignNotOwned${testRunId}`; +const NON_EXISTENT_APP = `app_does_not_exist_${testRunId}`; + +const appName = (suffix: string): string => + `distilled-clerk-uplogo-${suffix}-${testRunId}`; + +/** + * Minimal valid 1x1 transparent PNG (67 bytes). Decoded from the canonical + * smallest-PNG byte sequence and represented as a binary string so it can be + * passed through `file: Schema.String`. + */ +const TINY_PNG_BYTES = new Uint8Array([ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, + 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x1f, 0x15, 0xc4, 0x89, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x44, + 0x41, 0x54, 0x78, 0x9c, 0x62, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x01, 0x0d, + 0x0a, 0x2d, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, + 0x60, 0x82, +]); +const TINY_PNG = String.fromCharCode(...TINY_PNG_BYTES); + +/** + * Roughly 11 MB string used to provoke PayloadTooLarge. Clerk's logo upload + * endpoint caps body size at 10 MB. + */ +const OVERSIZED_FILE = "x".repeat(11 * 1024 * 1024); + +/** + * Provision a fresh application for the duration of `testFn`, then delete it + * afterwards so uploaded logos and the app itself don't accumulate. + */ +const withFreshApplication = ( + suffix: string, + testFn: (applicationID: string) => Effect.Effect, +) => + Effect.gen(function* () { + const created = yield* PlatformCreateApplication({ + name: appName(suffix), + }); + return yield* testFn(created.application_id).pipe( + Effect.ensuring( + PlatformDeleteApplication({ + applicationID: created.application_id, + }).pipe(Effect.ignore), + ), + ); + }); + +describe("PlatformUploadApplicationLogo", () => { + it("uploads a logo to a fresh application", async () => { + await runEffect( + withFreshApplication("happy", (applicationID) => + Effect.gen(function* () { + const result = yield* PlatformUploadApplicationLogo({ + applicationID, + file: TINY_PNG, + }); + + expect(result.application_id).toBe(applicationID); + expect(typeof result.name).toBe("string"); + expect(Array.isArray(result.instances)).toBe(true); + expect(result.instances.length).toBeGreaterThan(0); + for (const instance of result.instances) { + expect(typeof instance.instance_id).toBe("string"); + expect(["development", "production"]).toContain( + instance.environment_type, + ); + } + }), + ), + ); + }); + + it("returns BadRequest when the file field is empty", async () => { + await runEffect( + withFreshApplication("bad-request", (applicationID) => + Effect.gen(function* () { + const error = yield* PlatformUploadApplicationLogo({ + applicationID, + file: "", + }).pipe(Effect.flip); + + expect(error._tag).toBe("BadRequest"); + }), + ), + ); + }); + + it("returns UnprocessableEntity when the file is not a valid image", async () => { + await runEffect( + withFreshApplication("unproc", (applicationID) => + Effect.gen(function* () { + const error = yield* PlatformUploadApplicationLogo({ + applicationID, + file: "this is plain text, definitely not an image", + }).pipe(Effect.flip); + + expect(error._tag).toBe("UnprocessableEntity"); + }), + ), + ); + }); + + it("returns PayloadTooLarge when the file exceeds the size limit", async () => { + await runEffect( + withFreshApplication("too-large", (applicationID) => + Effect.gen(function* () { + const error = yield* PlatformUploadApplicationLogo({ + applicationID, + file: OVERSIZED_FILE, + }).pipe(Effect.flip); + + expect(error._tag).toBe("PayloadTooLarge"); + }), + ), + ); + }); + + it("returns Forbidden when targeting an application the caller does not own", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformUploadApplicationLogo({ + applicationID: NON_EXISTENT_FOREIGN_APP, + file: TINY_PNG, + }).pipe(Effect.flip); + + expect(error._tag).toBe("Forbidden"); + }), + ); + }); + + it("returns NotFound for an application id that does not exist", async () => { + await runEffect( + Effect.gen(function* () { + const error = yield* PlatformUploadApplicationLogo({ + applicationID: NON_EXISTENT_APP, + file: TINY_PNG, + }).pipe(Effect.flip); + + expect(error._tag).toBe("NotFound"); + }), + ); + }); +}); diff --git a/packages/clerk/test/setup.ts b/packages/clerk/test/setup.ts new file mode 100644 index 0000000000..b556c33c5f --- /dev/null +++ b/packages/clerk/test/setup.ts @@ -0,0 +1,31 @@ +import { config } from "dotenv"; +import { Effect, Layer } from "effect"; +import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient"; +import { CredentialsFromEnv } from "../src/credentials"; + +// Load environment variables from .env file +config(); + +/** + * Main layer providing Platform + Backend credentials and an HTTP client for + * all Clerk tests. + */ +export const MainLayer = Layer.merge(CredentialsFromEnv, FetchHttpClient.layer); + +/** + * Short random hex string generated once per test run. Append this to resource + * names so parallel test runs don't collide. + */ +export const testRunId: string = crypto + .randomUUID() + .replace(/-/g, "") + .slice(0, 8); + +/** + * Run an Effect with the MainLayer provided. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const runEffect = (effect: Effect.Effect): Promise => + Effect.runPromise( + effect.pipe(Effect.provide(MainLayer)) as Effect.Effect, + ); diff --git a/packages/clerk/tsconfig.json b/packages/clerk/tsconfig.json new file mode 100644 index 0000000000..3760701b6a --- /dev/null +++ b/packages/clerk/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.base.json", + "include": [ + "src/**/*.ts" + ], + "compilerOptions": { + "outDir": "./lib", + "rootDir": "./src", + "paths": { + "~/*": [ + "./src/*" + ] + } + }, + "references": [ + { + "path": "../core" + } + ] +} \ No newline at end of file diff --git a/packages/clerk/tsconfig.test.json b/packages/clerk/tsconfig.test.json new file mode 100644 index 0000000000..b2af39b257 --- /dev/null +++ b/packages/clerk/tsconfig.test.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base.json", + "include": [ + "src/**/*.ts", + "test/**/*.ts" + ], + "compilerOptions": { + "rootDir": ".", + "noEmit": true, + "paths": { + "~/*": [ + "./src/*" + ] + } + } +} \ No newline at end of file diff --git a/packages/clerk/vitest.config.ts b/packages/clerk/vitest.config.ts new file mode 100644 index 0000000000..fdad534022 --- /dev/null +++ b/packages/clerk/vitest.config.ts @@ -0,0 +1,17 @@ +import { config } from "dotenv"; +import { resolve } from "path"; + +config({ path: resolve(__dirname, "../../.env") }); +config({ path: resolve(__dirname, ".env") }); + +export default { + test: { + include: ["test/**/*.test.ts"], + testTimeout: 120000, + }, + resolve: { + alias: { + "~": new URL("./src", import.meta.url).pathname, + }, + }, +}; diff --git a/packages/core/scripts/generate-openapi.ts b/packages/core/scripts/generate-openapi.ts index ec89ed2898..12fb822b20 100644 --- a/packages/core/scripts/generate-openapi.ts +++ b/packages/core/scripts/generate-openapi.ts @@ -220,6 +220,25 @@ function operationIdToFunctionName(operationId: string): string { return toCamelCase(operationId); } +/** + * JS reserved words that can't be used as a top-level `const` binding name but + * are perfectly valid as an exported name / namespace member (e.g. an operation + * named `delete` -> `ns.delete`). For these we bind to a safe internal name and + * re-export it under the desired name via `export { binding as name }`. + */ +const RESERVED_WORDS = new Set([ + "break", "case", "catch", "class", "const", "continue", "debugger", + "default", "delete", "do", "else", "enum", "export", "extends", "false", + "finally", "for", "function", "if", "import", "in", "instanceof", "new", + "null", "return", "super", "switch", "this", "throw", "true", "try", + "typeof", "var", "void", "while", "with", "yield", "let", "static", + "implements", "interface", "package", "private", "protected", "public", +]); + +function isReservedWord(name: string): boolean { + return RESERVED_WORDS.has(name); +} + function escapeStringLiteral(s: string): string { return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/`/g, "\\`"); } @@ -1619,16 +1638,21 @@ function buildOperationFile( ? `\n pagination: { mode: "${pagination.mode}", inputToken: "${pagination.inputToken}", outputToken: "${pagination.outputToken}", items: "${pagination.items}" },` : ""; - const operationCode = jsDoc - ? `${jsDoc} -export const ${functionName} = /*@__PURE__*/ /*#__PURE__*/ API.${factory}(() => ({ - inputSchema: ${inputSchemaName}, - outputSchema: ${outputSchemaName},${errorsLine}${paginationLine} -}));` - : `export const ${functionName} = /*@__PURE__*/ /*#__PURE__*/ API.${factory}(() => ({ + // A reserved word like `delete` is a legal exported/namespace name but not a + // legal `const` binding. Bind to a safe alias and re-export under the name. + const reserved = isReservedWord(functionName); + const bindingName = reserved ? `${functionName}_` : functionName; + const exportPrefix = reserved ? "const" : "export const"; + const aliasExport = reserved + ? `\nexport { ${bindingName} as ${functionName} };` + : ""; + + const operationBody = `${exportPrefix} ${bindingName} = /*@__PURE__*/ /*#__PURE__*/ API.${factory}(() => ({ inputSchema: ${inputSchemaName}, outputSchema: ${outputSchemaName},${errorsLine}${paginationLine} -}));`; +}));${aliasExport}`; + + const operationCode = jsDoc ? `${jsDoc}\n${operationBody}` : operationBody; let imports = `import * as Schema from "effect/Schema"; import { API } from "${clientImport}.ts"; diff --git a/packages/planetscale/specs/distilled-spec-planetscale b/packages/planetscale/specs/distilled-spec-planetscale index 536cf00714..42fb43bf24 160000 --- a/packages/planetscale/specs/distilled-spec-planetscale +++ b/packages/planetscale/specs/distilled-spec-planetscale @@ -1 +1 @@ -Subproject commit 536cf00714dcb21b288cebcfd658a45a9c5ee9a8 +Subproject commit 42fb43bf242e318cb9d25e47ffd36c39a499adb5