feat: add support for version bounded parsing and migration (#5127)

- CLI Collection runner enhancements:

  - Support mixed versions - child collections based on different versions compared to the parent collection.
  - Better support for incoming data via IDs conforming to older formats.

- Bump `verzod` across packages.
- Relevant updates to test suite.
- Bump `zod` under `selfhost-desktop`.
This commit is contained in:
James George 2025-06-05 14:51:19 +05:30 committed by GitHub
parent 07c79170f6
commit 5ac2ac40de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 446 additions and 102 deletions

View file

@ -1,6 +1,6 @@
{
"name": "@hoppscotch/cli",
"version": "0.23.0",
"version": "0.23.1",
"description": "A CLI to run Hoppscotch test scripts in CI environments.",
"homepage": "https://hoppscotch.io",
"type": "module",
@ -50,7 +50,7 @@
"lodash-es": "4.17.21",
"papaparse": "5.4.1",
"qs": "6.13.0",
"verzod": "0.2.4",
"verzod": "0.3.0",
"xmlbuilder2": "3.1.1",
"zod": "3.25.32"
},

View file

@ -89,6 +89,24 @@ describe("hopp test [options] <file_path_or_id>", { timeout: 100000 }, () => {
expect(error).toBeNull();
});
});
describe("Mixed versions", () => {
test("Successfully processes children based on valid version ranges", async () => {
const args = `test ${getTestJsonFilePath("valid-mixed-versions-coll.json", "collection")}`;
const { error } = await runCLI(args);
expect(error).toBeNull();
});
test("Errors with the code `MALFORMED_COLLECTION` if the children fall out of valid version ranges", async () => {
const args = `test ${getTestJsonFilePath("invalid-mixed-versions-coll.json", "collection")}`;
const { stderr } = await runCLI(args);
const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("MALFORMED_COLLECTION");
});
});
});
describe("Environments", () => {

View file

@ -0,0 +1,48 @@
{
"v": 5,
"id": "cmbgcmyly10yhdjet63hiorps",
"name": "Invalid mixed versions collection",
"folders": [
{
"v": 8,
"id": "cmbgnb4h100v613phgrz6vku9",
"name": "fold1",
"folders": [],
"requests": [
{
"v": "13",
"name": "req",
"method": "GET",
"endpoint": "https://echo.hoppscotch.io",
"params": [],
"headers": [],
"preRequestScript": "",
"testScript": "",
"auth": {
"authType": "inherit",
"authActive": true
},
"body": {
"contentType": null,
"body": null
},
"requestVariables": [],
"responses": {}
}
],
"auth": {
"authType": "inherit",
"authActive": true
},
"headers": [],
"_ref_id": "coll_mbgnb8zy_eab190e2-6770-428c-8645-f49e88a3fe90"
}
],
"requests": [],
"auth": {
"authType": "inherit",
"authActive": true
},
"headers": [],
"_ref_id": "coll_mbgnb8zy_7c3f6eca-bd94-44a5-90e3-3f7c2126d63e"
}

View file

@ -0,0 +1,90 @@
{
"v": 8,
"id": "cmbgcmyly10yhdjet63hiorps",
"name": "Valid mixed versions collection",
"folders": [
{
"v": 7,
"id": "cmbgnb4h100v613phgrz6vku9",
"name": "fold1",
"folders": [
{
"v": 6,
"id": "cmbhux44609zs2triccmql79r",
"name": "fold2",
"folders": [
{
"v": 5,
"id": "cmbhux6es09zv2triton3kzv4",
"name": "fold3",
"folders": [
{
"v": 4,
"id": "cmbhux9vo0b2t13ph6dwapwh3",
"name": "fold4",
"folders": [],
"requests": [
{
"v": "13",
"name": "req",
"method": "GET",
"endpoint": "https://echo.hoppscotch.io",
"params": [],
"headers": [],
"preRequestScript": "",
"testScript": "",
"auth": {
"authType": "inherit",
"authActive": true
},
"body": {
"contentType": null,
"body": null
},
"requestVariables": [],
"responses": {}
}
],
"auth": {
"authType": "inherit",
"authActive": true
},
"headers": [],
"_ref_id": "coll_mbhuxoci_023b77eb-ed0c-4ec7-97ca-f46cf327cc54"
}
],
"requests": [],
"auth": {
"authType": "inherit",
"authActive": true
},
"headers": [],
"_ref_id": "coll_mbhuxoci_d22767f4-fd0a-4264-a86a-ba4a87c009cb"
}
],
"requests": [],
"auth": {
"authType": "inherit",
"authActive": true
},
"headers": [],
"_ref_id": "coll_mbhuxoci_0effd54f-8e08-441f-9b44-95d02ff1861e"
}
],
"requests": [],
"auth": {
"authType": "inherit",
"authActive": true
},
"headers": [],
"_ref_id": "coll_mbhuxoci_d7eee2ab-a921-447f-931d-bdce8c715770"
}
],
"requests": [],
"auth": {
"authType": "inherit",
"authActive": true
},
"headers": [],
"_ref_id": "coll_mbhuxoci_a8fc710e-04c1-489c-a183-7f16946a7225"
}

View file

@ -1365,7 +1365,81 @@ export const TRANSFORMED_COLLECTIONS_WITHOUT_AUTH_HEADERS_AT_CERTAIN_LEVELS_MOCK
},
];
export const WORKSPACE_ENVIRONMENT_MOCK: WorkspaceEnvironment = {
export const WORKSPACE_ENVIRONMENT_V0_FORMAT_MOCK = {
id: "clwudd68q00079rufju8uo3om",
teamID: "clws3hg58000011o8h07glsb1",
name: "Workspace environment v0 format",
variables: [
{
key: "firstName",
value: "John",
},
{
key: "lastName",
value: "Doe",
},
],
};
export const TRANSFORMED_ENVIRONMENT_V0_FORMAT_MOCK: Environment = {
v: EnvironmentSchemaVersion,
id: "clwudd68q00079rufju8uo3om",
name: "Workspace environment v0 format",
variables: [
{
key: "firstName",
initialValue: "John",
currentValue: "John",
secret: false,
},
{
key: "lastName",
initialValue: "Doe",
currentValue: "Doe",
secret: false,
},
],
};
export const WORKSPACE_ENVIRONMENT_V1_FORMAT_MOCK = {
id: "clwudd68q00079rufju8uo3om",
teamID: "clws3hg58000011o8h07glsb1",
name: "Workspace environment v1 format",
variables: [
{
key: "firstName",
value: "John",
secret: false,
},
{
key: "lastName",
value: "Doe",
secret: false,
},
],
};
export const TRANSFORMED_ENVIRONMENT_V1_FORMAT_MOCK: Environment = {
v: EnvironmentSchemaVersion,
id: "clwudd68q00079rufju8uo3om",
name: "Workspace environment v1 format",
variables: [
{
key: "firstName",
initialValue: "John",
currentValue: "John",
secret: false,
},
{
key: "lastName",
initialValue: "Doe",
currentValue: "Doe",
secret: false,
},
],
};
export const WORKSPACE_ENVIRONMENT_V2_FORMAT_MOCK: WorkspaceEnvironment = {
id: "clwudd68q00079rufju8uo3on",
teamID: "clws3hg58000011o8h07glsb1",
name: "Response body sample",
@ -1421,7 +1495,7 @@ export const WORKSPACE_ENVIRONMENT_MOCK: WorkspaceEnvironment = {
],
};
export const TRANSFORMED_ENVIRONMENT_MOCK: Environment = {
export const TRANSFORMED_ENVIRONMENT_V2_FORMAT_MOCK: Environment = {
v: EnvironmentSchemaVersion,
id: "clwudd68q00079rufju8uo3on",
name: "Response body sample",

View file

@ -7,11 +7,15 @@ import {
import {
TRANSFORMED_COLLECTIONS_WITHOUT_AUTH_HEADERS_AT_CERTAIN_LEVELS_MOCK,
TRANSFORMED_DEEPLY_NESTED_COLLECTIONS_WITH_AUTH_HEADERS_MOCK,
TRANSFORMED_ENVIRONMENT_MOCK,
TRANSFORMED_ENVIRONMENT_V0_FORMAT_MOCK,
TRANSFORMED_ENVIRONMENT_V1_FORMAT_MOCK,
TRANSFORMED_ENVIRONMENT_V2_FORMAT_MOCK,
TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK,
WORKSPACE_COLLECTIONS_WITHOUT_AUTH_HEADERS_AT_CERTAIN_LEVELS_MOCK,
WORKSPACE_DEEPLY_NESTED_COLLECTIONS_WITH_AUTH_HEADERS_MOCK,
WORKSPACE_ENVIRONMENT_MOCK,
WORKSPACE_ENVIRONMENT_V0_FORMAT_MOCK,
WORKSPACE_ENVIRONMENT_V1_FORMAT_MOCK,
WORKSPACE_ENVIRONMENT_V2_FORMAT_MOCK,
WORKSPACE_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK,
} from "./fixtures/workspace-access.mock";
@ -45,10 +49,24 @@ describe("workspace-access", () => {
});
describe("transformWorkspaceEnvironment", () => {
test("Successfully transforms environment data conforming to the format received from the network call to the `HoppEnvironment` format", () => {
expect(transformWorkspaceEnvironment(WORKSPACE_ENVIRONMENT_MOCK)).toEqual(
TRANSFORMED_ENVIRONMENT_MOCK
);
test("Successfully transforms environment data conforming to the `v0` format received from the network call to the `HoppEnvironment` format", () => {
expect(
// @ts-expect-error: Testing legacy format transformation
transformWorkspaceEnvironment(WORKSPACE_ENVIRONMENT_V0_FORMAT_MOCK)
).toEqual(TRANSFORMED_ENVIRONMENT_V0_FORMAT_MOCK);
});
test("Successfully transforms environment data conforming to the `v1` format received from the network call to the `HoppEnvironment` format", () => {
expect(
// @ts-expect-error: Testing legacy format transformation
transformWorkspaceEnvironment(WORKSPACE_ENVIRONMENT_V1_FORMAT_MOCK)
).toEqual(TRANSFORMED_ENVIRONMENT_V1_FORMAT_MOCK);
});
test("Successfully transforms environment data conforming to the `v2` format received from the network call to the `HoppEnvironment` format", () => {
expect(
transformWorkspaceEnvironment(WORKSPACE_ENVIRONMENT_V2_FORMAT_MOCK)
).toEqual(TRANSFORMED_ENVIRONMENT_V2_FORMAT_MOCK);
});
});
});

View file

@ -45,6 +45,37 @@ const transformWorkspaceRequests = (
requests: WorkspaceRequest[]
): HoppRESTRequest[] => requests.map(({ request }) => JSON.parse(request));
/**
* Apply relevant migrations for data conforming to older formats
*
* @param {HoppEnvPair} variable - The environment variable to normalize.
* @returns {HoppEnvPair} The normalized environment variable conforming to the `HoppEnvPair` type.
*/
const normalizeEnvironmentVariable = (variable: HoppEnvPair): HoppEnvPair => {
if (
"secret" in variable &&
"initialValue" in variable &&
"currentValue" in variable
) {
return variable;
}
const envPair = variable as Partial<HoppEnvPair> & {
key: string;
value?: string;
};
const isSecret = !!envPair.secret;
const value = envPair.value ?? "";
return {
key: envPair.key,
secret: isSecret,
initialValue: isSecret ? "" : (envPair.initialValue ?? value),
currentValue: isSecret ? "" : (envPair.currentValue ?? value),
};
};
/**
* Transforms workspace environment data to the `HoppEnvironment` format.
*
@ -56,23 +87,11 @@ export const transformWorkspaceEnvironment = (
): Environment => {
const { teamID, variables, ...rest } = workspaceEnvironment;
// Add `secret` field if the data conforms to an older schema
const transformedEnvVars = variables.map((variable) => {
if (!("secret" in variable)) {
return {
...(variable as HoppEnvPair),
secret: false,
} as HoppEnvPair;
}
return variable;
});
// The response doesn't include a way to infer the schema version, so it's set to the latest version
// Any relevant migrations have to be accounted here
return {
v: EnvironmentSchemaVersion,
variables: transformedEnvVars,
variables: variables.map(normalizeEnvironmentVariable),
...rest,
};
};
@ -96,19 +115,34 @@ export const transformWorkspaceCollections = (
const { auth = { authType: "inherit", authActive: true }, headers = [] } =
parsedData;
const migratedAuth: HoppRESTAuth =
auth.authType === "oauth-2"
? {
...auth,
grantTypeInfo:
auth.grantTypeInfo.grantType === "CLIENT_CREDENTIALS"
? {
...auth.grantTypeInfo,
clientAuthentication: "IN_BODY",
}
: auth.grantTypeInfo,
}
: auth;
const migratedHeaders = headers.map((header) =>
header.description ? header : { ...header, description: "" }
);
// The response doesn't include a way to infer the schema version, so it's set to the latest version
// Any relevant migrations have to be accounted here
// `ref_id` field isn't necessary being applicable only to personal workspace and asociates with syncing
return {
v: CollectionSchemaVersion,
id,
name: title,
folders: transformWorkspaceCollections(folders),
requests: transformWorkspaceRequests(requests),
auth,
auth: migratedAuth,
headers: migratedHeaders,
};
});

View file

@ -102,7 +102,7 @@
"url": "0.11.4",
"util": "0.12.5",
"uuid": "10.0.0",
"verzod": "0.2.4",
"verzod": "0.3.0",
"vue": "3.5.12",
"vue-i18n": "10.0.4",
"vue-json-pretty": "2.4.0",

View file

@ -43,11 +43,11 @@
"dependencies": {
"fp-ts": "2.16.9",
"io-ts": "2.2.21",
"jose": "6.0.11",
"lodash": "4.17.21",
"parser-ts": "0.7.0",
"jose": "6.0.11",
"uuid": "10.0.0",
"verzod": "0.2.4",
"verzod": "0.3.0",
"zod": "3.25.32"
}
}

View file

@ -1,7 +1,8 @@
import { defineVersion, entityReference } from "verzod"
import { defineVersion, entityReference, entityRefUptoVersion } from "verzod"
import { z } from "zod"
import { HoppRESTRequest } from "../../rest"
import { HoppGQLRequest } from "../../graphql"
import { HoppCollection } from ".."
const baseCollectionSchema = z.object({
v: z.literal(1),
@ -26,10 +27,9 @@ type Output = z.output<typeof baseCollectionSchema> & {
folders: Output[]
}
export const V1_SCHEMA: z.ZodType<Output, z.ZodTypeDef, Input> =
baseCollectionSchema.extend({
folders: z.lazy(() => z.array(V1_SCHEMA)),
})
export const V1_SCHEMA = baseCollectionSchema.extend({
folders: z.lazy(() => z.array(entityRefUptoVersion(HoppCollection, 1))),
}) as z.ZodType<Output, z.ZodTypeDef, Input>
export default defineVersion({
initial: true,

View file

@ -1,4 +1,4 @@
import { defineVersion, entityReference } from "verzod"
import { defineVersion, entityReference, entityRefUptoVersion } from "verzod"
import { z } from "zod"
import { HoppGQLRequest } from "../../graphql"
@ -8,6 +8,7 @@ import { HoppRESTRequest } from "../../rest"
import { HoppRESTHeaders } from "../../rest/v/1"
import { HoppRESTAuth } from "../../rest/v/5"
import { V1_SCHEMA } from "./1"
import { HoppCollection } from ".."
export const v2_baseCollectionSchema = z.object({
v: z.literal(2),
@ -35,16 +36,14 @@ type Output = z.output<typeof v2_baseCollectionSchema> & {
folders: Output[]
}
export const V2_SCHEMA: z.ZodType<Output, z.ZodTypeDef, Input> =
v2_baseCollectionSchema.extend({
folders: z.lazy(() => z.array(V2_SCHEMA)),
})
export const V2_SCHEMA = v2_baseCollectionSchema.extend({
folders: z.lazy(() => z.array(entityRefUptoVersion(HoppCollection, 2))),
}) as z.ZodType<Output, z.ZodTypeDef, Input>
export default defineVersion({
initial: false,
schema: V2_SCHEMA,
up(old: z.infer<typeof V1_SCHEMA>) {
// @ts-expect-error
const result: z.infer<typeof V2_SCHEMA> = {
...old,
v: 2,
@ -53,6 +52,15 @@ export default defineVersion({
authType: "inherit",
},
headers: [],
folders: old.folders.map((folder) => {
const result = HoppCollection.safeParseUpToVersion(folder, 2)
if (result.type !== "ok") {
throw new Error("Failed to migrate child collections")
}
return result.value
}),
}
if (old.id) result.id = old.id

View file

@ -1,4 +1,4 @@
import { defineVersion } from "verzod"
import { defineVersion, entityRefUptoVersion } from "verzod"
import { z } from "zod"
import { GQLHeader as V1_GQLHeader } from "../../graphql/v/1"
@ -11,6 +11,7 @@ import {
} from "../../rest/v/7"
import { v2_baseCollectionSchema, V2_SCHEMA } from "./2"
import { HoppCollection } from ".."
export const v3_baseCollectionSchema = v2_baseCollectionSchema.extend({
v: z.literal(3),
@ -30,10 +31,9 @@ type Output = z.output<typeof v3_baseCollectionSchema> & {
folders: Output[]
}
export const V3_SCHEMA: z.ZodType<Output, z.ZodTypeDef, Input> =
v3_baseCollectionSchema.extend({
folders: z.lazy(() => z.array(V3_SCHEMA)),
})
export const V3_SCHEMA = v3_baseCollectionSchema.extend({
folders: z.lazy(() => z.array(entityRefUptoVersion(HoppCollection, 3))),
}) as z.ZodType<Output, z.ZodTypeDef, Input>
export default defineVersion({
initial: false,
@ -46,11 +46,19 @@ export default defineVersion({
})
)
// @ts-expect-error
const result: z.infer<typeof V3_SCHEMA> = {
...old,
v: 3,
headers,
folders: old.folders.map((folder) => {
const result = HoppCollection.safeParseUpToVersion(folder, 3)
if (result.type !== "ok") {
throw new Error("Failed to migrate child collections")
}
return result.value
}),
}
return result

View file

@ -1,10 +1,11 @@
import { defineVersion } from "verzod"
import { defineVersion, entityRefUptoVersion } from "verzod"
import { z } from "zod"
import { HoppGQLAuth } from "../../graphql/v/7"
import { HoppRESTAuth } from "../../rest/v/8"
import { V3_SCHEMA, v3_baseCollectionSchema } from "./3"
import { HoppCollection } from ".."
export const v4_baseCollectionSchema = v3_baseCollectionSchema.extend({
v: z.literal(4),
@ -19,19 +20,28 @@ type Output = z.output<typeof v4_baseCollectionSchema> & {
folders: Output[]
}
export const V4_SCHEMA: z.ZodType<Output, z.ZodTypeDef, Input> =
v4_baseCollectionSchema.extend({
folders: z.lazy(() => z.array(V4_SCHEMA)),
})
export const V4_SCHEMA = v4_baseCollectionSchema.extend({
folders: z.lazy(() => z.array(entityRefUptoVersion(HoppCollection, 4))),
}) as z.ZodType<Output, z.ZodTypeDef, Input>
export default defineVersion({
initial: false,
schema: V4_SCHEMA,
// @ts-expect-error
up(old: z.infer<typeof V3_SCHEMA>) {
return {
const result: z.infer<typeof V4_SCHEMA> = {
...old,
v: 4 as const,
folders: old.folders.map((folder) => {
const result = HoppCollection.safeParseUpToVersion(folder, 4)
if (result.type !== "ok") {
throw new Error("Failed to migrate child collections")
}
return result.value
}),
}
return result
},
})

View file

@ -1,8 +1,9 @@
import { defineVersion } from "verzod"
import { defineVersion, entityRefUptoVersion } from "verzod"
import { z } from "zod"
import { V4_SCHEMA, v4_baseCollectionSchema } from "./4"
import { generateUniqueRefId } from "../../utils/collection"
import { HoppCollection } from ".."
export const v5_baseCollectionSchema = v4_baseCollectionSchema.extend({
v: z.literal(5),
@ -17,20 +18,29 @@ type Output = z.output<typeof v5_baseCollectionSchema> & {
folders: Output[]
}
export const V5_SCHEMA: z.ZodType<Output, z.ZodTypeDef, Input> =
v5_baseCollectionSchema.extend({
folders: z.lazy(() => z.array(V5_SCHEMA)),
})
export const V5_SCHEMA = v5_baseCollectionSchema.extend({
folders: z.lazy(() => z.array(entityRefUptoVersion(HoppCollection, 5))),
}) as z.ZodType<Output, z.ZodTypeDef, Input>
export default defineVersion({
initial: false,
schema: V5_SCHEMA,
// @ts-expect-error
up(old: z.infer<typeof V4_SCHEMA>) {
return {
const result: z.infer<typeof V5_SCHEMA> = {
...old,
v: 5 as const,
_ref_id: generateUniqueRefId("coll"),
folders: old.folders.map((folder) => {
const result = HoppCollection.safeParseUpToVersion(folder, 5)
if (result.type !== "ok") {
throw new Error("Failed to migrate child collections")
}
return result.value
}),
}
return result
},
})

View file

@ -1,10 +1,11 @@
import { defineVersion } from "verzod"
import { defineVersion, entityRefUptoVersion } from "verzod"
import { z } from "zod"
import { HoppGQLAuth } from "../../graphql/v/8"
import { HoppRESTAuth } from "../../rest/v/11"
import { V5_SCHEMA, v5_baseCollectionSchema } from "./5"
import { HoppCollection } from ".."
export const v6_baseCollectionSchema = v5_baseCollectionSchema.extend({
v: z.literal(6),
@ -19,15 +20,13 @@ type Output = z.output<typeof v6_baseCollectionSchema> & {
folders: Output[]
}
export const V6_SCHEMA: z.ZodType<Output, z.ZodTypeDef, Input> =
v6_baseCollectionSchema.extend({
folders: z.lazy(() => z.array(V6_SCHEMA)),
})
export const V6_SCHEMA = v6_baseCollectionSchema.extend({
folders: z.lazy(() => z.array(entityRefUptoVersion(HoppCollection, 6))),
}) as z.ZodType<Output, z.ZodTypeDef, Input>
export default defineVersion({
initial: false,
schema: V6_SCHEMA,
// @ts-expect-error
up(old: z.infer<typeof V5_SCHEMA>) {
const auth = old.auth
@ -45,10 +44,21 @@ export default defineVersion({
}
: auth
return {
const result: z.infer<typeof V6_SCHEMA> = {
...old,
auth: migratedAuth,
v: 6 as const,
folders: old.folders.map((folder) => {
const result = HoppCollection.safeParseUpToVersion(folder, 6)
if (result.type !== "ok") {
throw new Error("Failed to migrate child collections")
}
return result.value
}),
}
return result
},
})

View file

@ -1,10 +1,11 @@
import { defineVersion } from "verzod"
import { defineVersion, entityRefUptoVersion } from "verzod"
import { z } from "zod"
import { HoppGQLAuth } from "../../graphql/v/8"
import { HoppRESTAuth } from "../../rest/v/12"
import { V6_SCHEMA, v6_baseCollectionSchema } from "./6"
import { HoppCollection } from ".."
export const v7_baseCollectionSchema = v6_baseCollectionSchema.extend({
v: z.literal(7),
@ -19,19 +20,28 @@ type Output = z.output<typeof v7_baseCollectionSchema> & {
folders: Output[]
}
export const V7_SCHEMA: z.ZodType<Output, z.ZodTypeDef, Input> =
v7_baseCollectionSchema.extend({
folders: z.lazy(() => z.array(V7_SCHEMA)),
})
export const V7_SCHEMA = v7_baseCollectionSchema.extend({
folders: z.lazy(() => z.array(entityRefUptoVersion(HoppCollection, 7))),
}) as z.ZodType<Output, z.ZodTypeDef, Input>
export default defineVersion({
initial: false,
schema: V7_SCHEMA,
// @ts-expect-error
up(old: z.infer<typeof V6_SCHEMA>) {
return {
const result: z.infer<typeof V7_SCHEMA> = {
...old,
v: 7 as const,
folders: old.folders.map((folder) => {
const result = HoppCollection.safeParseUpToVersion(folder, 7)
if (result.type !== "ok") {
throw new Error("Failed to migrate child collections")
}
return result.value
}),
}
return result
},
})

View file

@ -1,10 +1,11 @@
import { defineVersion } from "verzod"
import { defineVersion, entityRefUptoVersion } from "verzod"
import { z } from "zod"
import { HoppGQLAuth } from "../../graphql/v/8"
import { HoppRESTAuth } from "../../rest/v/13"
import { V7_SCHEMA, v7_baseCollectionSchema } from "./7"
import { HoppCollection } from ".."
import { v7_baseCollectionSchema, V7_SCHEMA } from "./7"
export const v8_baseCollectionSchema = v7_baseCollectionSchema.extend({
v: z.literal(8),
@ -19,19 +20,28 @@ type Output = z.output<typeof v8_baseCollectionSchema> & {
folders: Output[]
}
export const V8_SCHEMA: z.ZodType<Output, z.ZodTypeDef, Input> =
v8_baseCollectionSchema.extend({
folders: z.lazy(() => z.array(V8_SCHEMA)),
})
export const V8_SCHEMA = v8_baseCollectionSchema.extend({
folders: z.lazy(() => z.array(entityRefUptoVersion(HoppCollection, 8))),
}) as z.ZodType<Output, z.ZodTypeDef, Input>
export default defineVersion({
initial: false,
schema: V8_SCHEMA,
// @ts-expect-error
up(old: z.infer<typeof V7_SCHEMA>) {
return {
const result: z.infer<typeof V8_SCHEMA> = {
...old,
v: 8 as const,
folders: old.folders.map((folder) => {
const result = HoppCollection.safeParseUpToVersion(folder, 8)
if (result.type !== "ok") {
throw new Error("Failed to migrate child collections")
}
return result.value
}),
}
return result
},
})

View file

@ -36,10 +36,10 @@
"tauri": "link:@tauri-apps/api/tauri",
"tauri-plugin-store-api": "0.0.0",
"util": "0.12.5",
"verzod": "0.2.4",
"verzod": "0.3.0",
"vue": "3.3.9",
"workbox-window": "6.6.0",
"zod": "3.22.4"
"zod": "3.25.32"
},
"devDependencies": {
"@graphql-codegen/add": "5.0.0",

View file

@ -44,7 +44,7 @@
"rxjs": "7.8.1",
"stream-browserify": "3.0.0",
"util": "0.12.5",
"verzod": "0.2.4",
"verzod": "0.3.0",
"vue": "3.5.12",
"workbox-window": "7.1.0",
"zod": "3.25.32"

View file

@ -422,8 +422,8 @@ importers:
specifier: 6.13.0
version: 6.13.0
verzod:
specifier: 0.2.4
version: 0.2.4(zod@3.25.32)
specifier: 0.3.0
version: 0.3.0(zod@3.25.32)
xmlbuilder2:
specifier: 3.1.1
version: 3.1.1
@ -714,8 +714,8 @@ importers:
specifier: 10.0.0
version: 10.0.0
verzod:
specifier: 0.2.4
version: 0.2.4(zod@3.25.32)
specifier: 0.3.0
version: 0.3.0(zod@3.25.32)
vue:
specifier: 3.5.12
version: 3.5.12(typescript@5.8.3)
@ -961,8 +961,8 @@ importers:
specifier: 10.0.0
version: 10.0.0
verzod:
specifier: 0.2.4
version: 0.2.4(zod@3.25.32)
specifier: 0.3.0
version: 0.3.0(zod@3.25.32)
zod:
specifier: 3.25.32
version: 3.25.32
@ -1316,8 +1316,8 @@ importers:
specifier: 0.12.5
version: 0.12.5
verzod:
specifier: 0.2.4
version: 0.2.4(zod@3.22.4)
specifier: 0.3.0
version: 0.3.0(zod@3.25.32)
vue:
specifier: 3.5.12
version: 3.5.12(typescript@4.9.5)
@ -1325,8 +1325,8 @@ importers:
specifier: 6.6.0
version: 6.6.0
zod:
specifier: 3.22.4
version: 3.22.4
specifier: 3.25.32
version: 3.25.32
devDependencies:
'@graphql-codegen/add':
specifier: 5.0.0
@ -1512,8 +1512,8 @@ importers:
specifier: 0.12.5
version: 0.12.5
verzod:
specifier: 0.2.4
version: 0.2.4(zod@3.25.32)
specifier: 0.3.0
version: 0.3.0(zod@3.25.32)
vue:
specifier: 3.5.12
version: 3.5.12(typescript@5.8.3)
@ -13609,8 +13609,8 @@ packages:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'}
verzod@0.2.4:
resolution: {integrity: sha512-xli0iwB1nxtWiJTjekTuPm9DuHXU8zdu6vKG2QXT0ZYKn9HanS/azJDSvbvT5IQ5HHltoV9h2Jf4Y+dwk8Fa5Q==}
verzod@0.3.0:
resolution: {integrity: sha512-h3L33u54/U/KWQh5z2sEaB0esQt3K3BhCCiriACW9t+83fLD7ebQnzcdckhIwZeZWfgQHNI7PTEDITlCqHTtZw==}
engines: {node: '>=16'}
peerDependencies:
zod: ^3.22.0
@ -30416,11 +30416,7 @@ snapshots:
vary@1.1.2: {}
verzod@0.2.4(zod@3.22.4):
dependencies:
zod: 3.22.4
verzod@0.2.4(zod@3.25.32):
verzod@0.3.0(zod@3.25.32):
dependencies:
zod: 3.25.32