diff --git a/apps/backend/package.json b/apps/backend/package.json index 1b12d73fb..21fded26b 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/backend", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "private": true, "type": "module", diff --git a/apps/backend/prisma/migrations/20260624000000_add_is_available_as_preview_project/migration.sql b/apps/backend/prisma/migrations/20260624000000_add_is_available_as_preview_project/migration.sql new file mode 100644 index 000000000..0fb8df170 --- /dev/null +++ b/apps/backend/prisma/migrations/20260624000000_add_is_available_as_preview_project/migration.sql @@ -0,0 +1,14 @@ +-- SPLIT_STATEMENT_SENTINEL +-- SINGLE_STATEMENT_SENTINEL +-- RUN_OUTSIDE_TRANSACTION_SENTINEL +ALTER TABLE /* SCHEMA_NAME_SENTINEL */."Project" +ADD COLUMN IF NOT EXISTS "isAvailableAsPreviewProject" BOOLEAN NOT NULL DEFAULT false; + +-- Partial index for fast pool claiming: only indexes the (tiny) subset of rows +-- that are currently available, ordered by creation time so the oldest is claimed first. +-- SPLIT_STATEMENT_SENTINEL +-- SINGLE_STATEMENT_SENTINEL +-- RUN_OUTSIDE_TRANSACTION_SENTINEL +CREATE INDEX CONCURRENTLY IF NOT EXISTS "Project_isAvailableAsPreviewProject_createdAt_idx" +ON /* SCHEMA_NAME_SENTINEL */."Project" ("createdAt" ASC) +WHERE "isAvailableAsPreviewProject" = true; diff --git a/apps/backend/prisma/migrations/20260624000000_add_is_available_as_preview_project/tests/default-value.ts b/apps/backend/prisma/migrations/20260624000000_add_is_available_as_preview_project/tests/default-value.ts new file mode 100644 index 000000000..ee958ae04 --- /dev/null +++ b/apps/backend/prisma/migrations/20260624000000_add_is_available_as_preview_project/tests/default-value.ts @@ -0,0 +1,22 @@ +import { randomUUID } from "crypto"; +import type { Sql } from "postgres"; +import { expect } from "vitest"; + +export const preMigration = async (sql: Sql) => { + const projectId = `test-${randomUUID()}`; + await sql` + INSERT INTO "Project" ("id", "createdAt", "updatedAt", "displayName", "description", "isProductionMode") + VALUES (${projectId}, NOW(), NOW(), 'Preview Pool Test Project', '', false) + `; + return { projectId }; +}; + +export const postMigration = async (sql: Sql, ctx: Awaited>) => { + const rows = await sql` + SELECT "isAvailableAsPreviewProject" + FROM "Project" + WHERE "id" = ${ctx.projectId} + `; + expect(rows).toHaveLength(1); + expect(rows[0].isAvailableAsPreviewProject).toBe(false); +}; diff --git a/apps/backend/prisma/schema.prisma b/apps/backend/prisma/schema.prisma index 685c77d26..fcc8f8ca4 100644 --- a/apps/backend/prisma/schema.prisma +++ b/apps/backend/prisma/schema.prisma @@ -30,6 +30,8 @@ model Project { onboardingStatus String @default("completed") onboardingState Json? + isAvailableAsPreviewProject Boolean @default(false) + logoUrl String? logoFullUrl String? logoDarkModeUrl String? diff --git a/apps/backend/src/app/api/latest/internal/preview/create-project/route.tsx b/apps/backend/src/app/api/latest/internal/preview/create-project/route.tsx index 2eebcb859..e57abe1e9 100644 --- a/apps/backend/src/app/api/latest/internal/preview/create-project/route.tsx +++ b/apps/backend/src/app/api/latest/internal/preview/create-project/route.tsx @@ -1,12 +1,69 @@ import { getClickhouseAdminClient } from "@/lib/clickhouse"; import { isPreviewModeEnabled } from "@/lib/preview-mode"; import { seedDummyProject } from "@/lib/seed-dummy-data"; -import { getPrismaClientForTenancy } from "@/prisma-client"; +import { Prisma } from "@/generated/prisma/client"; +import { getPrismaClientForTenancy, globalPrismaClient } from "@/prisma-client"; import { createSmartRouteHandler } from "@/route-handlers/smart-route-handler"; +import { runAsynchronouslyAndWaitUntil } from "@/utils/background-tasks"; import { adaptSchema, clientOrHigherAuthTypeSchema, yupNumber, yupObject, yupString } from "@hexclave/shared/dist/schema-fields"; import { StatusError } from "@hexclave/shared/dist/utils/errors"; import { ignoreUnhandledRejection } from "@hexclave/shared/dist/utils/promises"; +/** + * Atomically claims one pre-seeded preview project from the pool by flipping + * its `isAvailableAsPreviewProject` flag to false and assigning the given owner + * team. Returns the project ID if one was available, or null otherwise. + */ +async function claimPoolProject(ownerTeamId: string): Promise { + const rows = await globalPrismaClient.$queryRaw>(Prisma.sql` + UPDATE "Project" + SET "isAvailableAsPreviewProject" = false, + "ownerTeamId" = ${ownerTeamId}::uuid, + "updatedAt" = NOW() + WHERE "id" = ( + SELECT "id" FROM "Project" + WHERE "isAvailableAsPreviewProject" = true + ORDER BY "createdAt" ASC + LIMIT 1 + FOR UPDATE SKIP LOCKED + ) + RETURNING "id" + `); + return rows[0]?.id ?? null; +} + +/** + * Asynchronously seeds a new preview project into the pool (with + * isAvailableAsPreviewProject = true) so a future request can claim it + * instantly. + * + * Pool projects have ownerTeamId = null so they don't appear in any user's + * dashboard. The claim query assigns the real ownerTeamId when a project is + * claimed. + */ +function replenishPreviewProjectPool(ownerTeamId: string): void { + runAsynchronouslyAndWaitUntil(async () => { + const clickhouseClient = getClickhouseAdminClient(); + const projectId = await seedDummyProject({ + ownerTeamId, + oauthProviderIds: ['github', 'google', 'microsoft', 'spotify'], + excludeAlphaApps: true, + skipGithubConfigSource: true, + clickhouseClient, + }); + // Mark as available and null out ownerTeamId so the pool project doesn't + // appear in the seeding user's dashboard. The claim query sets the real + // ownerTeamId when the project is claimed. + await globalPrismaClient.project.update({ + where: { id: projectId }, + data: { + isAvailableAsPreviewProject: true, + ownerTeamId: null, + }, + }); + }); +} + export const POST = createSmartRouteHandler({ metadata: { summary: "Create a preview project", @@ -37,17 +94,6 @@ export const POST = createSmartRouteHandler({ throw new StatusError(StatusError.Forbidden, "This endpoint is only available in preview mode"); } - // Pre-warm the ClickHouse Cloud connection, then hand the same client to - // seedDummyProject so every analytics insert reuses it. The first insert - // otherwise pays a one-time ~0.7s cold cost (idle-service wake-up + TLS). - // Firing a trivial query now — unawaited — overlaps that wake-up and the - // TLS handshake with the Postgres-heavy seeding below; threading the warmed - // client through means the handshake is established exactly once. - const clickhouseClient = getClickhouseAdminClient(); - const clickhouseWarmup = clickhouseClient - .command({ query: "SELECT 1" }); - ignoreUnhandledRejection(clickhouseWarmup); - const userId = auth.user.id; const prisma = await getPrismaClientForTenancy(auth.tenancy); @@ -66,17 +112,33 @@ export const POST = createSmartRouteHandler({ throw new StatusError(StatusError.BadRequest, "User must belong to a team to create a preview project"); } - const projectId = await seedDummyProject({ - ownerTeamId: membership.teamId, - oauthProviderIds: ['github', 'google', 'microsoft', 'spotify'], - excludeAlphaApps: true, - skipGithubConfigSource: true, - clickhouseClient, - }); + // Try to claim a pre-seeded project from the pool (near-instant). + const claimedProjectId = await claimPoolProject(membership.teamId); - // Settle the warm-up promise (long since resolved by now) so it does not - // float past the handler return. - await clickhouseWarmup; + let projectId: string; + if (claimedProjectId) { + projectId = claimedProjectId; + } else { + // Pool empty — fall back to creating a fresh project synchronously. + const clickhouseClient = getClickhouseAdminClient(); + const clickhouseWarmup = clickhouseClient.command({ query: "SELECT 1" }); + ignoreUnhandledRejection(clickhouseWarmup); + + projectId = await seedDummyProject({ + ownerTeamId: membership.teamId, + oauthProviderIds: ['github', 'google', 'microsoft', 'spotify'], + excludeAlphaApps: true, + skipGithubConfigSource: true, + clickhouseClient, + }); + + await clickhouseWarmup; + } + + // Replenish the pool asynchronously so the next request can be served + // instantly. ownerTeamId is needed for seedDummyProject but gets nulled out + // afterward — the claim query assigns the real owner. + replenishPreviewProjectPool(membership.teamId); return { statusCode: 200, diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 41384e01e..d284f19b3 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/dashboard", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "private": true, "scripts": { diff --git a/apps/dev-launchpad/package.json b/apps/dev-launchpad/package.json index cf43918af..f31dce60f 100644 --- a/apps/dev-launchpad/package.json +++ b/apps/dev-launchpad/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/dev-launchpad", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "private": true, "scripts": { diff --git a/apps/e2e/package.json b/apps/e2e/package.json index 3df97d854..e08782e2d 100644 --- a/apps/e2e/package.json +++ b/apps/e2e/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/e2e-tests", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "private": true, "type": "module", diff --git a/apps/hosted-components/package.json b/apps/hosted-components/package.json index 9544adcbb..319712d2c 100644 --- a/apps/hosted-components/package.json +++ b/apps/hosted-components/package.json @@ -1,7 +1,7 @@ { "name": "@hexclave/hosted-components", "private": true, - "version": "1.0.27", + "version": "1.0.28", "type": "module", "scripts": { "dev": "vite dev --port ${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}09", diff --git a/apps/internal-tool/package.json b/apps/internal-tool/package.json index 53a2ba60e..68488913c 100644 --- a/apps/internal-tool/package.json +++ b/apps/internal-tool/package.json @@ -1,7 +1,7 @@ { "name": "@hexclave/internal-tool", "private": true, - "version": "1.0.27", + "version": "1.0.28", "type": "module", "scripts": { "dev": "node scripts/pre-dev.mjs && next dev --turbopack --port ${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}41", diff --git a/apps/mcp/package.json b/apps/mcp/package.json index 73b1780ac..f91334db8 100644 --- a/apps/mcp/package.json +++ b/apps/mcp/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/mcp", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "private": true, "type": "module", diff --git a/apps/mock-oauth-server/package.json b/apps/mock-oauth-server/package.json index 0d9c3e6dd..d5afa89a2 100644 --- a/apps/mock-oauth-server/package.json +++ b/apps/mock-oauth-server/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/mock-oauth-server", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "private": true, "main": "index.js", diff --git a/apps/skills/package.json b/apps/skills/package.json index 9e211966a..b65ee4f9b 100644 --- a/apps/skills/package.json +++ b/apps/skills/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/skills", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "private": true, "type": "module", diff --git a/docs-mintlify/package.json b/docs-mintlify/package.json index 1aaf3b18f..133520ce0 100644 --- a/docs-mintlify/package.json +++ b/docs-mintlify/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/docs-mintlify", - "version": "1.0.27", + "version": "1.0.28", "private": true, "scripts": { "dev": "mint dev --port ${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}04 --no-open", diff --git a/docs/package.json b/docs/package.json index 7e36c4cd3..2a09aef2d 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/docs", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "description": "", "main": "index.js", diff --git a/examples/cjs-test/package.json b/examples/cjs-test/package.json index a49ae4e48..3828a2865 100644 --- a/examples/cjs-test/package.json +++ b/examples/cjs-test/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/example-cjs-test", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "private": true, "scripts": { diff --git a/examples/convex/package.json b/examples/convex/package.json index 03f5a33b2..115581711 100644 --- a/examples/convex/package.json +++ b/examples/convex/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/convex-example", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "private": true, "scripts": { diff --git a/examples/demo/package.json b/examples/demo/package.json index 5f3d0ef14..ca92d552c 100644 --- a/examples/demo/package.json +++ b/examples/demo/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/example-demo-app", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "description": "", "private": true, diff --git a/examples/docs-examples/package.json b/examples/docs-examples/package.json index 29bded1d3..3c276cf9a 100644 --- a/examples/docs-examples/package.json +++ b/examples/docs-examples/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/docs-examples", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "description": "", "private": true, diff --git a/examples/e-commerce/package.json b/examples/e-commerce/package.json index 8c408303f..bd5e1737c 100644 --- a/examples/e-commerce/package.json +++ b/examples/e-commerce/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/e-commerce-demo", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "private": true, "scripts": { diff --git a/examples/js-example/package.json b/examples/js-example/package.json index 0c77c6f67..efd34679b 100644 --- a/examples/js-example/package.json +++ b/examples/js-example/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/js-example", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "private": true, "description": "", diff --git a/examples/lovable-react-18-example/package.json b/examples/lovable-react-18-example/package.json index ae913f534..e4cd5d20c 100644 --- a/examples/lovable-react-18-example/package.json +++ b/examples/lovable-react-18-example/package.json @@ -1,7 +1,7 @@ { "name": "@hexclave/lovable-react-18-example", "private": true, - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "type": "module", "scripts": { diff --git a/examples/middleware/package.json b/examples/middleware/package.json index 1cf8ad848..6b01872bd 100644 --- a/examples/middleware/package.json +++ b/examples/middleware/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/example-middleware-demo", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "private": true, "scripts": { diff --git a/examples/react-example/package.json b/examples/react-example/package.json index 59c760eb5..8f43d3407 100644 --- a/examples/react-example/package.json +++ b/examples/react-example/package.json @@ -1,7 +1,7 @@ { "name": "react-example", "private": true, - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "type": "module", "scripts": { diff --git a/examples/supabase/package.json b/examples/supabase/package.json index 2d024327e..3c693cd58 100644 --- a/examples/supabase/package.json +++ b/examples/supabase/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/example-supabase", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "private": true, "scripts": { diff --git a/examples/tanstack-start-demo/package.json b/examples/tanstack-start-demo/package.json index e891ab4c0..58c245662 100644 --- a/examples/tanstack-start-demo/package.json +++ b/examples/tanstack-start-demo/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/example-tanstack-start-demo", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "description": "TanStack Start demo app for Hexclave", "private": true, diff --git a/packages/cli/package.json b/packages/cli/package.json index 81e8cea96..48f723746 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/cli", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "description": "The CLI for Hexclave. https://hexclave.com", "main": "dist/index.js", diff --git a/packages/dashboard-ui-components/package.json b/packages/dashboard-ui-components/package.json index 08ee0e393..4bbe34364 100644 --- a/packages/dashboard-ui-components/package.json +++ b/packages/dashboard-ui-components/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/dashboard-ui-components", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/js/package.json b/packages/js/package.json index 9bd695d72..e3e6510b8 100644 --- a/packages/js/package.json +++ b/packages/js/package.json @@ -1,7 +1,7 @@ { "//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)", "name": "@hexclave/js", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "sideEffects": false, "main": "./dist/index.js", diff --git a/packages/next/package.json b/packages/next/package.json index 76a2faaa9..3c1a82174 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,7 +1,7 @@ { "//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)", "name": "@hexclave/next", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "sideEffects": false, "main": "./dist/index.js", diff --git a/packages/react/package.json b/packages/react/package.json index 22d9b683b..ecf8d634f 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,7 +1,7 @@ { "//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)", "name": "@hexclave/react", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "sideEffects": false, "main": "./dist/index.js", diff --git a/packages/sc/package.json b/packages/sc/package.json index deff5d241..f7444e06d 100644 --- a/packages/sc/package.json +++ b/packages/sc/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/sc", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "exports": { "./force-react-server": { diff --git a/packages/shared-backend/package.json b/packages/shared-backend/package.json index 59a4c6497..eca4ddb3a 100644 --- a/packages/shared-backend/package.json +++ b/packages/shared-backend/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/shared-backend", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/shared/package.json b/packages/shared/package.json index 977a8fbfc..a7a35d68d 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/shared", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "scripts": { "build": "rimraf dist && tsdown", diff --git a/packages/tanstack-start/package.json b/packages/tanstack-start/package.json index 5a9d8605c..c021d9845 100644 --- a/packages/tanstack-start/package.json +++ b/packages/tanstack-start/package.json @@ -1,7 +1,7 @@ { "//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)", "name": "@hexclave/tanstack-start", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "sideEffects": false, "main": "./dist/index.js", diff --git a/packages/template/package-template.json b/packages/template/package-template.json index f78cd06c2..e18be6d11 100644 --- a/packages/template/package-template.json +++ b/packages/template/package-template.json @@ -13,7 +13,7 @@ "//": "NEXT_LINE_PLATFORM template", "private": true, - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "sideEffects": false, "main": "./dist/index.js", diff --git a/packages/template/package.json b/packages/template/package.json index df4eb3ff9..82e74b530 100644 --- a/packages/template/package.json +++ b/packages/template/package.json @@ -2,7 +2,7 @@ "//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)", "name": "@hexclave/template", "private": true, - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "sideEffects": false, "main": "./dist/index.js", diff --git a/packages/ui/package.json b/packages/ui/package.json index 2601d2f32..25d1f54c8 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/ui", - "version": "1.0.27", + "version": "1.0.28", "repository": "https://github.com/hexclave/hexclave", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/sdks/implementations/swift/package.json b/sdks/implementations/swift/package.json index 196a36d5f..d8ebb4c5e 100644 --- a/sdks/implementations/swift/package.json +++ b/sdks/implementations/swift/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/swift-sdk", - "version": "1.0.27", + "version": "1.0.28", "private": true, "description": "Hexclave Swift SDK", "scripts": { diff --git a/sdks/spec/package.json b/sdks/spec/package.json index 6936a2422..8a44e48cb 100644 --- a/sdks/spec/package.json +++ b/sdks/spec/package.json @@ -1,6 +1,6 @@ { "name": "@hexclave/sdk-spec", - "version": "1.0.27", + "version": "1.0.28", "private": true, "description": "Hexclave SDK specification files", "scripts": {}