refactor: use schema constants

This commit is contained in:
Nicolas Meienberger
2025-10-23 19:25:12 +02:00
parent 8b1438ea62
commit 4ae738ce41
14 changed files with 661 additions and 35 deletions

View File

@@ -18,6 +18,10 @@ import {
unmountVolume,
healthCheckVolume,
listFiles,
listRepositories,
createRepository,
deleteRepository,
getRepository,
} from "../sdk.gen";
import { queryOptions, type UseMutationOptions, type DefaultError } from "@tanstack/react-query";
import type {
@@ -47,6 +51,12 @@ import type {
HealthCheckVolumeData,
HealthCheckVolumeResponse,
ListFilesData,
ListRepositoriesData,
CreateRepositoryData,
CreateRepositoryResponse,
DeleteRepositoryData,
DeleteRepositoryResponse,
GetRepositoryData,
} from "../types.gen";
import { client as _heyApiClient } from "../client.gen";
@@ -561,3 +571,103 @@ export const listFilesOptions = (options: Options<ListFilesData>) => {
queryKey: listFilesQueryKey(options),
});
};
export const listRepositoriesQueryKey = (options?: Options<ListRepositoriesData>) =>
createQueryKey("listRepositories", options);
/**
* List all repositories
*/
export const listRepositoriesOptions = (options?: Options<ListRepositoriesData>) => {
return queryOptions({
queryFn: async ({ queryKey, signal }) => {
const { data } = await listRepositories({
...options,
...queryKey[0],
signal,
throwOnError: true,
});
return data;
},
queryKey: listRepositoriesQueryKey(options),
});
};
export const createRepositoryQueryKey = (options?: Options<CreateRepositoryData>) =>
createQueryKey("createRepository", options);
/**
* Create a new restic repository
*/
export const createRepositoryOptions = (options?: Options<CreateRepositoryData>) => {
return queryOptions({
queryFn: async ({ queryKey, signal }) => {
const { data } = await createRepository({
...options,
...queryKey[0],
signal,
throwOnError: true,
});
return data;
},
queryKey: createRepositoryQueryKey(options),
});
};
/**
* Create a new restic repository
*/
export const createRepositoryMutation = (
options?: Partial<Options<CreateRepositoryData>>,
): UseMutationOptions<CreateRepositoryResponse, DefaultError, Options<CreateRepositoryData>> => {
const mutationOptions: UseMutationOptions<CreateRepositoryResponse, DefaultError, Options<CreateRepositoryData>> = {
mutationFn: async (localOptions) => {
const { data } = await createRepository({
...options,
...localOptions,
throwOnError: true,
});
return data;
},
};
return mutationOptions;
};
/**
* Delete a repository
*/
export const deleteRepositoryMutation = (
options?: Partial<Options<DeleteRepositoryData>>,
): UseMutationOptions<DeleteRepositoryResponse, DefaultError, Options<DeleteRepositoryData>> => {
const mutationOptions: UseMutationOptions<DeleteRepositoryResponse, DefaultError, Options<DeleteRepositoryData>> = {
mutationFn: async (localOptions) => {
const { data } = await deleteRepository({
...options,
...localOptions,
throwOnError: true,
});
return data;
},
};
return mutationOptions;
};
export const getRepositoryQueryKey = (options: Options<GetRepositoryData>) => createQueryKey("getRepository", options);
/**
* Get a single repository by name
*/
export const getRepositoryOptions = (options: Options<GetRepositoryData>) => {
return queryOptions({
queryFn: async ({ queryKey, signal }) => {
const { data } = await getRepository({
...options,
...queryKey[0],
signal,
throwOnError: true,
});
return data;
},
queryKey: getRepositoryQueryKey(options),
});
};

View File

@@ -17,6 +17,6 @@ export type CreateClientConfig<T extends DefaultClientOptions = ClientOptions> =
export const client = createClient(
createConfig<ClientOptions>({
baseUrl: "http://localhost:4096",
baseUrl: "http://192.168.2.42:4096",
}),
);

View File

@@ -44,6 +44,14 @@ import type {
ListFilesData,
ListFilesResponses,
ListFilesErrors,
ListRepositoriesData,
ListRepositoriesResponses,
CreateRepositoryData,
CreateRepositoryResponses,
DeleteRepositoryData,
DeleteRepositoryResponses,
GetRepositoryData,
GetRepositoryResponses,
} from "./types.gen";
import { client as _heyApiClient } from "./client.gen";
@@ -261,3 +269,55 @@ export const listFiles = <ThrowOnError extends boolean = false>(options: Options
...options,
});
};
/**
* List all repositories
*/
export const listRepositories = <ThrowOnError extends boolean = false>(
options?: Options<ListRepositoriesData, ThrowOnError>,
) => {
return (options?.client ?? _heyApiClient).get<ListRepositoriesResponses, unknown, ThrowOnError>({
url: "/api/v1/repositories",
...options,
});
};
/**
* Create a new restic repository
*/
export const createRepository = <ThrowOnError extends boolean = false>(
options?: Options<CreateRepositoryData, ThrowOnError>,
) => {
return (options?.client ?? _heyApiClient).post<CreateRepositoryResponses, unknown, ThrowOnError>({
url: "/api/v1/repositories",
...options,
headers: {
"Content-Type": "application/json",
...options?.headers,
},
});
};
/**
* Delete a repository
*/
export const deleteRepository = <ThrowOnError extends boolean = false>(
options: Options<DeleteRepositoryData, ThrowOnError>,
) => {
return (options.client ?? _heyApiClient).delete<DeleteRepositoryResponses, unknown, ThrowOnError>({
url: "/api/v1/repositories/{name}",
...options,
});
};
/**
* Get a single repository by name
*/
export const getRepository = <ThrowOnError extends boolean = false>(
options: Options<GetRepositoryData, ThrowOnError>,
) => {
return (options.client ?? _heyApiClient).get<GetRepositoryResponses, unknown, ThrowOnError>({
url: "/api/v1/repositories/{name}",
...options,
});
};

View File

@@ -178,7 +178,7 @@ export type ListVolumesResponses = {
lastHealthCheck: number;
name: string;
path: string;
status: "error" | "mounted" | "unknown" | "unmounted";
status: "error" | "mounted" | "unmounted";
type: "directory" | "nfs" | "smb" | "webdav";
updatedAt: number;
}>;
@@ -374,7 +374,7 @@ export type GetVolumeResponses = {
lastHealthCheck: number;
name: string;
path: string;
status: "error" | "mounted" | "unknown" | "unmounted";
status: "error" | "mounted" | "unmounted";
type: "directory" | "nfs" | "smb" | "webdav";
updatedAt: number;
};
@@ -474,7 +474,7 @@ export type UpdateVolumeResponses = {
lastHealthCheck: number;
name: string;
path: string;
status: "error" | "mounted" | "unknown" | "unmounted";
status: "error" | "mounted" | "unmounted";
type: "directory" | "nfs" | "smb" | "webdav";
updatedAt: number;
};
@@ -639,6 +639,145 @@ export type ListFilesResponses = {
export type ListFilesResponse = ListFilesResponses[keyof ListFilesResponses];
export type ClientOptions = {
baseUrl: "http://localhost:4096" | (string & {});
export type ListRepositoriesData = {
body?: never;
path?: never;
query?: never;
url: "/api/v1/repositories";
};
export type ListRepositoriesResponses = {
/**
* List of repositories
*/
200: {
repositories: Array<{
compressionMode: "auto" | "better" | "fastest" | "max" | "off" | null;
config:
| {
accessKeyId: string;
backend: "s3";
bucket: string;
endpoint: string;
secretAccessKey: string;
}
| {
backend: "local";
path: string;
};
createdAt: number;
id: string;
lastChecked: number | null;
lastError: string | null;
name: string;
status: "error" | "healthy" | "unknown" | null;
type: "local" | "s3";
updatedAt: number;
}>;
};
};
export type ListRepositoriesResponse = ListRepositoriesResponses[keyof ListRepositoriesResponses];
export type CreateRepositoryData = {
body?: {
config:
| {
accessKeyId: string;
backend: "s3";
bucket: string;
endpoint: string;
secretAccessKey: string;
}
| {
backend: "local";
path: string;
};
name: string;
compressionMode?: "auto" | "better" | "fastest" | "max" | "off";
};
path?: never;
query?: never;
url: "/api/v1/repositories";
};
export type CreateRepositoryResponses = {
/**
* Repository created successfully
*/
201: {
message: string;
repository: {
id: string;
name: string;
};
};
};
export type CreateRepositoryResponse = CreateRepositoryResponses[keyof CreateRepositoryResponses];
export type DeleteRepositoryData = {
body?: never;
path: {
name: string;
};
query?: never;
url: "/api/v1/repositories/{name}";
};
export type DeleteRepositoryResponses = {
/**
* Repository deleted successfully
*/
200: {
message: string;
};
};
export type DeleteRepositoryResponse = DeleteRepositoryResponses[keyof DeleteRepositoryResponses];
export type GetRepositoryData = {
body?: never;
path: {
name: string;
};
query?: never;
url: "/api/v1/repositories/{name}";
};
export type GetRepositoryResponses = {
/**
* Repository details
*/
200: {
repository: {
compressionMode: "auto" | "better" | "fastest" | "max" | "off" | null;
config:
| {
accessKeyId: string;
backend: "s3";
bucket: string;
endpoint: string;
secretAccessKey: string;
}
| {
backend: "local";
path: string;
};
createdAt: number;
id: string;
lastChecked: number | null;
lastError: string | null;
name: string;
status: "error" | "healthy" | "unknown" | null;
type: "local" | "s3";
updatedAt: number;
};
};
};
export type GetRepositoryResponse = GetRepositoryResponses[keyof GetRepositoryResponses];
export type ClientOptions = {
baseUrl: "http://192.168.2.42:4096" | (string & {});
};

View File

@@ -0,0 +1 @@
ALTER TABLE `repositories_table` RENAME COLUMN "backend" TO "type";

View File

@@ -0,0 +1,313 @@
{
"version": "6",
"dialect": "sqlite",
"id": "866b1d3b-454b-4cf7-9835-a0f60d048b6e",
"prevId": "16f360b6-fb61-44f3-a7f7-2bae78ebf7ca",
"tables": {
"repositories_table": {
"name": "repositories_table",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"config": {
"name": "config",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"compression_mode": {
"name": "compression_mode",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'auto'"
},
"status": {
"name": "status",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'unknown'"
},
"last_checked": {
"name": "last_checked",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"last_error": {
"name": "last_error",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(unixepoch())"
},
"updated_at": {
"name": "updated_at",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(unixepoch())"
}
},
"indexes": {
"repositories_table_name_unique": {
"name": "repositories_table_name_unique",
"columns": [
"name"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"sessions_table": {
"name": "sessions_table",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"user_id": {
"name": "user_id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"expires_at": {
"name": "expires_at",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(unixepoch())"
}
},
"indexes": {},
"foreignKeys": {
"sessions_table_user_id_users_table_id_fk": {
"name": "sessions_table_user_id_users_table_id_fk",
"tableFrom": "sessions_table",
"tableTo": "users_table",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"users_table": {
"name": "users_table",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"username": {
"name": "username",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"password_hash": {
"name": "password_hash",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(unixepoch())"
},
"updated_at": {
"name": "updated_at",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(unixepoch())"
}
},
"indexes": {
"users_table_username_unique": {
"name": "users_table_username_unique",
"columns": [
"username"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"volumes_table": {
"name": "volumes_table",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"status": {
"name": "status",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'unmounted'"
},
"last_error": {
"name": "last_error",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"last_health_check": {
"name": "last_health_check",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(unixepoch())"
},
"created_at": {
"name": "created_at",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(unixepoch())"
},
"updated_at": {
"name": "updated_at",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(unixepoch())"
},
"config": {
"name": "config",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"auto_remount": {
"name": "auto_remount",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": true
}
},
"indexes": {
"volumes_table_name_unique": {
"name": "volumes_table_name_unique",
"columns": [
"name"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {
"\"repositories_table\".\"backend\"": "\"repositories_table\".\"type\""
}
},
"internal": {
"indexes": {}
}
}

View File

@@ -50,6 +50,13 @@
"when": 1760734377440,
"tag": "0006_secret_micromacro",
"breakpoints": true
},
{
"idx": 7,
"version": "6",
"when": 1761224911352,
"tag": "0007_watery_sersi",
"breakpoints": true
}
]
}

View File

@@ -9,11 +9,10 @@
"studio": "drizzle-kit studio"
},
"dependencies": {
"@hono/arktype-validator": "^2.0.1",
"@hono/standard-validator": "^0.1.5",
"@ironmount/schemas": "workspace:*",
"@scalar/hono-api-reference": "^0.9.13",
"arktype": "^2.1.20",
"arktype": "^2.1.23",
"dockerode": "^4.0.8",
"dotenv": "^17.2.1",
"drizzle-orm": "^0.44.6",

View File

@@ -47,7 +47,7 @@ export type Session = typeof sessionsTable.$inferSelect;
export const repositoriesTable = sqliteTable("repositories_table", {
id: text().primaryKey(),
name: text().notNull().unique(),
backend: text().$type<RepositoryBackend>().notNull(),
type: text().$type<RepositoryBackend>().notNull(),
config: text("config", { mode: "json" }).$type<typeof repositoryConfigSchema.inferOut>().notNull(),
compressionMode: text("compression_mode").$type<CompressionMode>().default("auto"),
status: text().$type<RepositoryStatus>().default("unknown"),

View File

@@ -1,14 +1,19 @@
import { repositoryConfigSchema } from "@ironmount/schemas/restic";
import {
COMPRESSION_MODES,
REPOSITORY_BACKENDS,
REPOSITORY_STATUS,
repositoryConfigSchema,
} from "@ironmount/schemas/restic";
import { type } from "arktype";
import { describeRoute, resolver } from "hono-openapi";
const repositorySchema = type({
id: "string",
name: "string",
backend: type.enumerated("local", "sftp", "s3"),
type: type.valueOf(REPOSITORY_BACKENDS),
config: repositoryConfigSchema,
compressionMode: type.enumerated("off", "auto", "fastest", "better", "max").or("null"),
status: type.enumerated("healthy", "error", "unknown").or("null"),
compressionMode: type.valueOf(COMPRESSION_MODES).or("null"),
status: type.valueOf(REPOSITORY_STATUS).or("null"),
lastChecked: "number | null",
lastError: "string | null",
createdAt: "number",
@@ -46,8 +51,8 @@ export const listRepositoriesDto = describeRoute({
*/
export const createRepositoryBody = type({
name: "string",
compressionMode: type.valueOf(COMPRESSION_MODES).optional(),
config: repositoryConfigSchema,
"compressionMode?": type.enumerated("off", "auto", "fastest", "better", "max"),
});
export type CreateRepositoryBody = typeof createRepositoryBody.infer;

View File

@@ -1,5 +1,5 @@
import crypto from "node:crypto";
import type { CompressionMode, RepositoryConfig } from "@ironmount/schemas";
import type { CompressionMode, RepositoryConfig } from "@ironmount/schemas/restic";
import { eq } from "drizzle-orm";
import { ConflictError, InternalServerError, NotFoundError } from "http-errors-enhanced";
import slugify from "slugify";
@@ -15,14 +15,16 @@ const listRepositories = async () => {
};
const encryptConfig = async (config: RepositoryConfig): Promise<RepositoryConfig> => {
const encryptedConfig = { ...config };
const encryptedConfig: Record<string, string> = { ...config };
switch (config.backend) {
case "s3":
encryptedConfig.accessKeyId = await cryptoUtils.encrypt(config.accessKeyId);
encryptedConfig.secretAccessKey = await cryptoUtils.encrypt(config.secretAccessKey);
break;
}
return encryptedConfig;
return encryptedConfig as RepositoryConfig;
};
const createRepository = async (name: string, config: RepositoryConfig, compressionMode?: CompressionMode) => {
@@ -45,7 +47,7 @@ const createRepository = async (name: string, config: RepositoryConfig, compress
.values({
id,
name: slug,
backend: config.backend,
type: config.backend,
config: encryptedConfig,
compressionMode: compressionMode ?? "auto",
status: "unknown",

View File

@@ -1,12 +1,12 @@
import { volumeConfigSchema } from "@ironmount/schemas";
import { BACKEND_STATUS, BACKEND_TYPES, volumeConfigSchema } from "@ironmount/schemas";
import { type } from "arktype";
import { describeRoute, resolver } from "hono-openapi";
const volumeSchema = type({
name: "string",
path: "string",
type: type.enumerated("nfs", "smb", "directory", "webdav"),
status: type.enumerated("mounted", "unmounted", "error", "unknown"),
type: type.valueOf(BACKEND_TYPES),
status: type.valueOf(BACKEND_STATUS),
lastError: "string | null",
createdAt: "number",
updatedAt: "number",
@@ -199,7 +199,7 @@ export const testConnectionDto = describeRoute({
*/
export const mountVolumeResponse = type({
error: "string?",
status: type.enumerated("mounted", "unmounted", "error"),
status: type.valueOf(BACKEND_STATUS),
});
export const mountVolumeDto = describeRoute({
@@ -226,7 +226,7 @@ export const mountVolumeDto = describeRoute({
*/
export const unmountVolumeResponse = type({
error: "string?",
status: type.enumerated("mounted", "unmounted", "error"),
status: type.valueOf(BACKEND_STATUS),
});
export const unmountVolumeDto = describeRoute({
@@ -250,7 +250,7 @@ export const unmountVolumeDto = describeRoute({
export const healthCheckResponse = type({
error: "string?",
status: type.enumerated("mounted", "unmounted", "error"),
status: type.valueOf(BACKEND_STATUS),
});
export const healthCheckDto = describeRoute({

View File

@@ -63,11 +63,10 @@
"apps/server": {
"name": "@ironmount/server",
"dependencies": {
"@hono/arktype-validator": "^2.0.1",
"@hono/standard-validator": "^0.1.5",
"@ironmount/schemas": "workspace:*",
"@scalar/hono-api-reference": "^0.9.13",
"arktype": "^2.1.20",
"arktype": "^2.1.23",
"dockerode": "^4.0.8",
"dotenv": "^17.2.1",
"drizzle-orm": "^0.44.6",
@@ -239,8 +238,6 @@
"@hey-api/openapi-ts": ["@hey-api/openapi-ts@0.80.18", "", { "dependencies": { "@hey-api/json-schema-ref-parser": "1.0.6", "ansi-colors": "4.1.3", "c12": "2.0.1", "color-support": "1.1.3", "commander": "13.0.0", "handlebars": "4.7.8", "js-yaml": "4.1.0", "open": "10.1.2", "semver": "7.7.2" }, "peerDependencies": { "typescript": "^5.5.3" }, "bin": { "openapi-ts": "bin/index.cjs" } }, "sha512-YCcRdw+OmD/WgBk5ZNaSblEb94PfqBKp4gDvfEYtEdRVS76etdgVOD0CPz72Tus+6zeSzGDVlWE8GOfcib0C6g=="],
"@hono/arktype-validator": ["@hono/arktype-validator@2.0.1", "", { "peerDependencies": { "arktype": "^2.0.0-dev.14", "hono": "*" } }, "sha512-Z4PQFtzgbGneBap+TTViRIBAoUWbwEwg8PaKNqALAP6z9N2ksJI81PfcsSQNUzwtrn8LipkMvBb8/D9Pei2GJw=="],
"@hono/standard-validator": ["@hono/standard-validator@0.1.5", "", { "peerDependencies": { "@standard-schema/spec": "1.0.0", "hono": ">=3.9.0" } }, "sha512-EIyZPPwkyLn6XKwFj5NBEWHXhXbgmnVh2ceIFo5GO7gKI9WmzTjPDKnppQB0KrqKeAkq3kpoW4SIbu5X1dgx3w=="],
"@hookform/resolvers": ["@hookform/resolvers@5.2.2", "", { "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "react-hook-form": "^7.55.0" } }, "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA=="],
@@ -1299,8 +1296,6 @@
"@grpc/grpc-js/@grpc/proto-loader": ["@grpc/proto-loader@0.8.0", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.5.3", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ=="],
"@ironmount/server/arktype": ["arktype@2.1.22", "", { "dependencies": { "@ark/schema": "0.49.0", "@ark/util": "0.49.0" } }, "sha512-xdzl6WcAhrdahvRRnXaNwsipCgHuNoLobRqhiP8RjnfL9Gp947abGlo68GAIyLtxbD+MLzNyH2YR4kEqioMmYQ=="],
"@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
"@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
@@ -1433,10 +1428,6 @@
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="],
"@ironmount/server/arktype/@ark/schema": ["@ark/schema@0.49.0", "", { "dependencies": { "@ark/util": "0.49.0" } }, "sha512-GphZBLpW72iS0v4YkeUtV3YIno35Gimd7+ezbPO9GwEi9kzdUrPVjvf6aXSBAfHikaFc/9pqZOpv3pOXnC71tw=="],
"@ironmount/server/arktype/@ark/util": ["@ark/util@0.49.0", "", {}, "sha512-/BtnX7oCjNkxi2vi6y1399b+9xd1jnCrDYhZ61f0a+3X8x8DxlK52VgEEzyuC2UQMPACIfYrmHkhD3lGt2GaMA=="],
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
"@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],

View File

@@ -2,7 +2,6 @@ import { type } from "arktype";
export const REPOSITORY_BACKENDS = {
local: "local",
sftp: "sftp",
s3: "s3",
} as const;