diff --git a/.dockerignore b/.dockerignore index 6a154f15..0e4767f1 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,9 @@ .git .user +cli/ +docs/ +dpkit/ +site/ **/node_modules **/build **/compile diff --git a/app/helpers/rpc.ts b/app/helpers/rpc.ts deleted file mode 100644 index e04426ce..00000000 --- a/app/helpers/rpc.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { newHttpBatchRpcSession } from "capnweb" -import * as settings from "#settings.ts" -import type * as types from "#types/index.ts" - -export function createRpcSession() { - const url = import.meta.env.PROD - ? new URL("/rpc", settings.URL).toString() - : "/rpc" - - return newHttpBatchRpcSession(url) -} diff --git a/app/logger.ts b/app/logger.ts deleted file mode 100644 index fc75860e..00000000 --- a/app/logger.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ConsoleTransport, LogLayer } from "loglayer" - -export const logger = new LogLayer({ - transport: new ConsoleTransport({ - logger: console, - }), -}) diff --git a/app/routes/package/validate/services.ts b/app/routes/package/validate/services.ts deleted file mode 100644 index 1c822b7f..00000000 --- a/app/routes/package/validate/services.ts +++ /dev/null @@ -1,8 +0,0 @@ -import * as dpkit from "@dpkit/lib" - -export async function validatePackage( - source: Parameters[0], -) { - const result = await dpkit.validatePackage(source) - return result -} diff --git a/app/rpc.ts b/app/rpc.ts deleted file mode 100644 index 63d4141f..00000000 --- a/app/rpc.ts +++ /dev/null @@ -1,34 +0,0 @@ -import http from "node:http" -import { RpcTarget, nodeHttpBatchRpcResponse } from "capnweb" -import { logger } from "#logger.ts" -import { validatePackage } from "#routes/package/validate/services.ts" - -export class Rpc extends RpcTarget { - validatePackage(...args: Parameters) { - return validatePackage(...args) - } -} - -const server = http.createServer(async (request, response) => { - try { - // For health-checks we just return 200 - if (request.url === "/") { - response.statusCode = 200 - response.end() - return - } - - await nodeHttpBatchRpcResponse(request, response, new Rpc()) - } catch (error: any) { - response.statusCode = 500 - response.end("Internal Error") - - logger.error(error) - } finally { - logger.info(`[${request.method}] ${request.url}`, response.statusCode) - } -}) - -server.listen(8080, () => { - logger.info("Listening on port 8080") -}) diff --git a/browser/README.md b/browser/README.md new file mode 100644 index 00000000..d99e6749 --- /dev/null +++ b/browser/README.md @@ -0,0 +1,3 @@ +# @dpkit/browser + +dpkit is a fast data management framework built on top of the Data Package standard and Polars DataFrames. It supports various formats like CSV, JSON, and Parquet and integrates with data platforms such as CKAN, Zenodo, and GitHub. For more information, please visit the [documentation portal](https://dpkit.dev). diff --git a/ui/.keep b/browser/index.ts similarity index 100% rename from ui/.keep rename to browser/index.ts diff --git a/browser/package.json b/browser/package.json new file mode 100644 index 00000000..442607c5 --- /dev/null +++ b/browser/package.json @@ -0,0 +1,27 @@ +{ + "name": "@dpkit/browser", + "type": "module", + "version": "0.0.0-dev", + "private": true, + "exports": "./build/index.js", + "sideEffects": false, + "license": "MIT", + "author": "Evgeny Karev", + "repository": "https://github.com/datisthq/dpkit", + "description": "Fast TypeScript data management framework built on top of the Data Package standard and Polars DataFrames", + "keywords": [ + "data", + "polars", + "dataframe", + "datapackage", + "tableschema", + "typescript", + "validation", + "quality", + "fair", + "browser" + ], + "scripts": { + "build": "tsc" + } +} diff --git a/browser/tsconfig.json b/browser/tsconfig.json new file mode 100644 index 00000000..3c43903c --- /dev/null +++ b/browser/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../tsconfig.json" +} diff --git a/app/Dockerfile b/cloud/Dockerfile similarity index 84% rename from app/Dockerfile rename to cloud/Dockerfile index 4c70e45c..cf7ce5bd 100644 --- a/app/Dockerfile +++ b/cloud/Dockerfile @@ -11,4 +11,4 @@ RUN pnpm install:ci RUN pnpm build EXPOSE 8080 -CMD ["node", "app/rpc.ts"] +CMD ["node", "cloud/api/server.ts"] diff --git a/app/README.md b/cloud/README.md similarity index 95% rename from app/README.md rename to cloud/README.md index 325fe3e9..fba11c2a 100644 --- a/app/README.md +++ b/cloud/README.md @@ -1,3 +1,3 @@ -# @dpkit/app +# @dpkit/cloud dpkit is a fast data management framework built on top of the Data Package standard and Polars DataFrames. It supports various formats like CSV, JSON, and Parquet and integrates with data platforms such as CKAN, Zenodo, and GitHub. For more information, please visit the [documentation portal](https://dpkit.dev). diff --git a/cloud/api/client.ts b/cloud/api/client.ts new file mode 100644 index 00000000..52d4626f --- /dev/null +++ b/cloud/api/client.ts @@ -0,0 +1,9 @@ +import "client-only" +import { createClient } from "@dpkit/service/client" +import * as settings from "#settings.ts" + +export const api = createClient({ + protocol: import.meta.env.PROD ? "https" : "http", + host: import.meta.env.PROD ? settings.URL : "localhost", + port: import.meta.env.PROD ? 80 : 4000, +}) diff --git a/cloud/api/server.ts b/cloud/api/server.ts new file mode 100644 index 00000000..c4b30a65 --- /dev/null +++ b/cloud/api/server.ts @@ -0,0 +1,3 @@ +import { createServer } from "@dpkit/service/server" + +createServer({ start: true }) diff --git a/app/assets/about.png b/cloud/assets/about.png similarity index 100% rename from app/assets/about.png rename to cloud/assets/about.png diff --git a/app/assets/logo.svg b/cloud/assets/logo.svg similarity index 100% rename from app/assets/logo.svg rename to cloud/assets/logo.svg diff --git a/app/components/Alert/Alert.tsx b/cloud/components/Alert/Alert.tsx similarity index 100% rename from app/components/Alert/Alert.tsx rename to cloud/components/Alert/Alert.tsx diff --git a/app/components/Dialog/Dialog.module.css b/cloud/components/Dialog/Dialog.module.css similarity index 100% rename from app/components/Dialog/Dialog.module.css rename to cloud/components/Dialog/Dialog.module.css diff --git a/app/components/Dialog/Dialog.tsx b/cloud/components/Dialog/Dialog.tsx similarity index 100% rename from app/components/Dialog/Dialog.tsx rename to cloud/components/Dialog/Dialog.tsx diff --git a/app/components/Dialog/index.ts b/cloud/components/Dialog/index.ts similarity index 100% rename from app/components/Dialog/index.ts rename to cloud/components/Dialog/index.ts diff --git a/app/components/Form/Form.tsx b/cloud/components/Form/Form.tsx similarity index 100% rename from app/components/Form/Form.tsx rename to cloud/components/Form/Form.tsx diff --git a/app/components/Form/index.ts b/cloud/components/Form/index.ts similarity index 100% rename from app/components/Form/index.ts rename to cloud/components/Form/index.ts diff --git a/app/components/Layout/About.module.css b/cloud/components/Layout/About.module.css similarity index 100% rename from app/components/Layout/About.module.css rename to cloud/components/Layout/About.module.css diff --git a/app/components/Layout/About.tsx b/cloud/components/Layout/About.tsx similarity index 100% rename from app/components/Layout/About.tsx rename to cloud/components/Layout/About.tsx diff --git a/app/components/Layout/Banner.module.css b/cloud/components/Layout/Banner.module.css similarity index 100% rename from app/components/Layout/Banner.module.css rename to cloud/components/Layout/Banner.module.css diff --git a/app/components/Layout/Banner.tsx b/cloud/components/Layout/Banner.tsx similarity index 100% rename from app/components/Layout/Banner.tsx rename to cloud/components/Layout/Banner.tsx diff --git a/app/components/Layout/Breadcrumbs.tsx b/cloud/components/Layout/Breadcrumbs.tsx similarity index 100% rename from app/components/Layout/Breadcrumbs.tsx rename to cloud/components/Layout/Breadcrumbs.tsx diff --git a/app/components/Layout/Content.tsx b/cloud/components/Layout/Content.tsx similarity index 100% rename from app/components/Layout/Content.tsx rename to cloud/components/Layout/Content.tsx diff --git a/app/components/Layout/Footer.module.css b/cloud/components/Layout/Footer.module.css similarity index 100% rename from app/components/Layout/Footer.module.css rename to cloud/components/Layout/Footer.module.css diff --git a/app/components/Layout/Footer.tsx b/cloud/components/Layout/Footer.tsx similarity index 100% rename from app/components/Layout/Footer.tsx rename to cloud/components/Layout/Footer.tsx diff --git a/app/components/Layout/Header.module.css b/cloud/components/Layout/Header.module.css similarity index 100% rename from app/components/Layout/Header.module.css rename to cloud/components/Layout/Header.module.css diff --git a/app/components/Layout/Header.tsx b/cloud/components/Layout/Header.tsx similarity index 100% rename from app/components/Layout/Header.tsx rename to cloud/components/Layout/Header.tsx diff --git a/app/components/Layout/Language.module.css b/cloud/components/Layout/Language.module.css similarity index 100% rename from app/components/Layout/Language.module.css rename to cloud/components/Layout/Language.module.css diff --git a/app/components/Layout/Language.tsx b/cloud/components/Layout/Language.tsx similarity index 100% rename from app/components/Layout/Language.tsx rename to cloud/components/Layout/Language.tsx diff --git a/app/components/Layout/Layout.tsx b/cloud/components/Layout/Layout.tsx similarity index 100% rename from app/components/Layout/Layout.tsx rename to cloud/components/Layout/Layout.tsx diff --git a/app/components/Layout/Logo.module.css b/cloud/components/Layout/Logo.module.css similarity index 100% rename from app/components/Layout/Logo.module.css rename to cloud/components/Layout/Logo.module.css diff --git a/app/components/Layout/Logo.tsx b/cloud/components/Layout/Logo.tsx similarity index 100% rename from app/components/Layout/Logo.tsx rename to cloud/components/Layout/Logo.tsx diff --git a/app/components/Layout/Meta.tsx b/cloud/components/Layout/Meta.tsx similarity index 100% rename from app/components/Layout/Meta.tsx rename to cloud/components/Layout/Meta.tsx diff --git a/app/components/Layout/Navigation.tsx b/cloud/components/Layout/Navigation.tsx similarity index 100% rename from app/components/Layout/Navigation.tsx rename to cloud/components/Layout/Navigation.tsx diff --git a/app/components/Layout/Repository.module.css b/cloud/components/Layout/Repository.module.css similarity index 100% rename from app/components/Layout/Repository.module.css rename to cloud/components/Layout/Repository.module.css diff --git a/app/components/Layout/Repository.tsx b/cloud/components/Layout/Repository.tsx similarity index 100% rename from app/components/Layout/Repository.tsx rename to cloud/components/Layout/Repository.tsx diff --git a/app/components/Layout/Share.module.css b/cloud/components/Layout/Share.module.css similarity index 100% rename from app/components/Layout/Share.module.css rename to cloud/components/Layout/Share.module.css diff --git a/app/components/Layout/Share.tsx b/cloud/components/Layout/Share.tsx similarity index 100% rename from app/components/Layout/Share.tsx rename to cloud/components/Layout/Share.tsx diff --git a/app/components/Layout/Sitemap.tsx b/cloud/components/Layout/Sitemap.tsx similarity index 100% rename from app/components/Layout/Sitemap.tsx rename to cloud/components/Layout/Sitemap.tsx diff --git a/app/components/Layout/Theme.module.css b/cloud/components/Layout/Theme.module.css similarity index 100% rename from app/components/Layout/Theme.module.css rename to cloud/components/Layout/Theme.module.css diff --git a/app/components/Layout/Theme.tsx b/cloud/components/Layout/Theme.tsx similarity index 100% rename from app/components/Layout/Theme.tsx rename to cloud/components/Layout/Theme.tsx diff --git a/app/components/Layout/index.ts b/cloud/components/Layout/index.ts similarity index 100% rename from app/components/Layout/index.ts rename to cloud/components/Layout/index.ts diff --git a/app/components/Link/Link.module.css b/cloud/components/Link/Link.module.css similarity index 100% rename from app/components/Link/Link.module.css rename to cloud/components/Link/Link.module.css diff --git a/app/components/Link/Link.tsx b/cloud/components/Link/Link.tsx similarity index 100% rename from app/components/Link/Link.tsx rename to cloud/components/Link/Link.tsx diff --git a/app/components/Link/index.ts b/cloud/components/Link/index.ts similarity index 100% rename from app/components/Link/index.ts rename to cloud/components/Link/index.ts diff --git a/app/components/Report/Error/Cell.tsx b/cloud/components/Report/Error/Cell.tsx similarity index 100% rename from app/components/Report/Error/Cell.tsx rename to cloud/components/Report/Error/Cell.tsx diff --git a/app/components/Report/Error/Error.tsx b/cloud/components/Report/Error/Error.tsx similarity index 100% rename from app/components/Report/Error/Error.tsx rename to cloud/components/Report/Error/Error.tsx diff --git a/app/components/Report/Error/Field.tsx b/cloud/components/Report/Error/Field.tsx similarity index 100% rename from app/components/Report/Error/Field.tsx rename to cloud/components/Report/Error/Field.tsx diff --git a/app/components/Report/Error/Fields.tsx b/cloud/components/Report/Error/Fields.tsx similarity index 100% rename from app/components/Report/Error/Fields.tsx rename to cloud/components/Report/Error/Fields.tsx diff --git a/app/components/Report/Error/File.tsx b/cloud/components/Report/Error/File.tsx similarity index 100% rename from app/components/Report/Error/File.tsx rename to cloud/components/Report/Error/File.tsx diff --git a/app/components/Report/Error/Metadata.tsx b/cloud/components/Report/Error/Metadata.tsx similarity index 100% rename from app/components/Report/Error/Metadata.tsx rename to cloud/components/Report/Error/Metadata.tsx diff --git a/app/components/Report/Error/Row.tsx b/cloud/components/Report/Error/Row.tsx similarity index 100% rename from app/components/Report/Error/Row.tsx rename to cloud/components/Report/Error/Row.tsx diff --git a/app/components/Report/Report.tsx b/cloud/components/Report/Report.tsx similarity index 100% rename from app/components/Report/Report.tsx rename to cloud/components/Report/Report.tsx diff --git a/app/components/Report/index.ts b/cloud/components/Report/index.ts similarity index 100% rename from app/components/Report/index.ts rename to cloud/components/Report/index.ts diff --git a/app/components/Status/Status.module.css b/cloud/components/Status/Status.module.css similarity index 100% rename from app/components/Status/Status.module.css rename to cloud/components/Status/Status.module.css diff --git a/app/components/Status/Status.tsx b/cloud/components/Status/Status.tsx similarity index 100% rename from app/components/Status/Status.tsx rename to cloud/components/Status/Status.tsx diff --git a/app/components/Status/index.ts b/cloud/components/Status/index.ts similarity index 100% rename from app/components/Status/index.ts rename to cloud/components/Status/index.ts diff --git a/app/components/System/Error.module.css b/cloud/components/System/Error.module.css similarity index 100% rename from app/components/System/Error.module.css rename to cloud/components/System/Error.module.css diff --git a/app/components/System/Error.tsx b/cloud/components/System/Error.tsx similarity index 100% rename from app/components/System/Error.tsx rename to cloud/components/System/Error.tsx diff --git a/app/components/System/System.tsx b/cloud/components/System/System.tsx similarity index 100% rename from app/components/System/System.tsx rename to cloud/components/System/System.tsx diff --git a/app/components/System/context.ts b/cloud/components/System/context.ts similarity index 100% rename from app/components/System/context.ts rename to cloud/components/System/context.ts diff --git a/app/components/System/index.ts b/cloud/components/System/index.ts similarity index 100% rename from app/components/System/index.ts rename to cloud/components/System/index.ts diff --git a/app/components/System/selectors.ts b/cloud/components/System/selectors.ts similarity index 100% rename from app/components/System/selectors.ts rename to cloud/components/System/selectors.ts diff --git a/app/constants/language.ts b/cloud/constants/language.ts similarity index 100% rename from app/constants/language.ts rename to cloud/constants/language.ts diff --git a/app/constants/page.ts b/cloud/constants/page.ts similarity index 100% rename from app/constants/page.ts rename to cloud/constants/page.ts diff --git a/app/helpers/context.ts b/cloud/helpers/context.ts similarity index 100% rename from app/helpers/context.ts rename to cloud/helpers/context.ts diff --git a/app/helpers/link.ts b/cloud/helpers/link.ts similarity index 100% rename from app/helpers/link.ts rename to cloud/helpers/link.ts diff --git a/app/helpers/media.ts b/cloud/helpers/media.ts similarity index 100% rename from app/helpers/media.ts rename to cloud/helpers/media.ts diff --git a/app/helpers/platform.ts b/cloud/helpers/platform.ts similarity index 100% rename from app/helpers/platform.ts rename to cloud/helpers/platform.ts diff --git a/app/helpers/revision.ts b/cloud/helpers/revision.ts similarity index 100% rename from app/helpers/revision.ts rename to cloud/helpers/revision.ts diff --git a/app/helpers/store.ts b/cloud/helpers/store.ts similarity index 100% rename from app/helpers/store.ts rename to cloud/helpers/store.ts diff --git a/app/i18n.ts b/cloud/i18n.ts similarity index 100% rename from app/i18n.ts rename to cloud/i18n.ts diff --git a/app/icons.ts b/cloud/icons.ts similarity index 100% rename from app/icons.ts rename to cloud/icons.ts diff --git a/app/locales/de.json b/cloud/locales/de.json similarity index 100% rename from app/locales/de.json rename to cloud/locales/de.json diff --git a/app/locales/en.json b/cloud/locales/en.json similarity index 98% rename from app/locales/en.json rename to cloud/locales/en.json index 7d96d66e..a9f16fcb 100644 --- a/app/locales/en.json +++ b/cloud/locales/en.json @@ -97,9 +97,9 @@ "about-privacy-first-description": "dpkit Cloud is a competely open source project being under constant peer-review. The code is transparent and auditable, ensuring your data is never collected on our servers and remains accessible only to you during processing. dpkit Cloud does not collect your data or store your data after processing.", "about-self-hosting-title": "Self-hosting", "about-self-hosting-description": "dpkit Cloud is completely open source and can be self-hosted. Visit our GitHub repository to get started. For assistance with self-hosting or custom deployments, please get in touch with us.", - "about-github-repository": "GitHub repository", "about-get-in-touch": "get in touch with us", "about-description-intro": "dpkit Cloud provides free online privacy-first tools for converting and validating data. Unlike others, dpkit Cloud is free and completely", - "about-self-hosting-intro": "dpkit Cloud is completely open source and can be self-hosted. Visit our", + "about-self-hosting-docs": "the self-hosting documentation", + "about-self-hosting-intro": "dpkit Cloud is completely open source and can be self-hosted. Visit ", "about-self-hosting-middle": "to get started. For assistance with self-hosting or custom deployments, please" } diff --git a/app/locales/es.json b/cloud/locales/es.json similarity index 100% rename from app/locales/es.json rename to cloud/locales/es.json diff --git a/app/locales/fr.json b/cloud/locales/fr.json similarity index 100% rename from app/locales/fr.json rename to cloud/locales/fr.json diff --git a/app/locales/it.json b/cloud/locales/it.json similarity index 100% rename from app/locales/it.json rename to cloud/locales/it.json diff --git a/app/locales/pt.json b/cloud/locales/pt.json similarity index 100% rename from app/locales/pt.json rename to cloud/locales/pt.json diff --git a/app/locales/ru.json b/cloud/locales/ru.json similarity index 100% rename from app/locales/ru.json rename to cloud/locales/ru.json diff --git a/app/locales/uk.json b/cloud/locales/uk.json similarity index 100% rename from app/locales/uk.json rename to cloud/locales/uk.json diff --git a/cloud/logger.ts b/cloud/logger.ts new file mode 100644 index 00000000..77cac9c0 --- /dev/null +++ b/cloud/logger.ts @@ -0,0 +1,9 @@ +import { TsLogTransport } from "@loglayer/transport-tslog" +import { LogLayer } from "loglayer" +import { Logger } from "tslog" + +export const logger = new LogLayer({ + transport: new TsLogTransport({ + logger: new Logger({ type: "pretty" }), + }), +}) diff --git a/app/main.ts b/cloud/main.ts similarity index 71% rename from app/main.ts rename to cloud/main.ts index 9269dd59..f278e674 100644 --- a/app/main.ts +++ b/cloud/main.ts @@ -2,10 +2,10 @@ import { Container, getContainer } from "@cloudflare/containers" import { createRequestHandler } from "react-router" export interface Env { - RPC: DurableObjectNamespace + API: DurableObjectNamespace } -export class Rpc extends Container { +export class API extends Container { defaultPort = 8080 sleepAfter = "1h" } @@ -29,12 +29,12 @@ export default { async fetch(request, env, ctx) { const path = new URL(request.url).pathname - if (path === "/rpc") { - const containerInstance = getContainer(env.RPC, path) - return containerInstance.fetch(request) + if (path.startsWith("/api")) { + const containerInstance = getContainer(env.API, path) + return await containerInstance.fetch(request) } - return requestHandler(request, { + return await requestHandler(request, { cloudflare: { env, ctx }, }) }, diff --git a/app/package.json b/cloud/package.json similarity index 80% rename from app/package.json rename to cloud/package.json index c124331b..666a5ce5 100644 --- a/app/package.json +++ b/cloud/package.json @@ -1,5 +1,5 @@ { - "name": "@dpkit/app", + "name": "@dpkit/cloud", "type": "module", "version": "0.0.0-dev", "private": true, @@ -14,20 +14,29 @@ "start": "react-router dev --port 4000 --host", "type": "tsc --noEmit" }, - "dependencies": { + "devDependencies": { "@cloudflare/containers": "0.0.28", - "@dpkit/lib": "workspace:*", + "@cloudflare/vite-plugin": "1.13.13", + "@dpkit/core": "workspace:*", + "@dpkit/service": "workspace:*", "@mantine/core": "8.3.5", "@mantine/form": "8.3.5", "@mantine/hooks": "8.3.5", + "@orpc/openapi-client": "1.10.0", + "@react-router/dev": "7.9.4", "@tanstack/react-query": "5.90.3", - "capnweb": "0.1.0", + "@types/react": "19.2.2", + "@types/react-dom": "^19.2.0", + "client-only": "0.0.1", "es-toolkit": "1.39.10", "i18next": "25.6.0", "immer": "10.1.3", "isbot": "5.1.31", "loglayer": "6.9.1", "lucide-react": "0.545.0", + "postcss": "8.5.6", + "postcss-preset-mantine": "1.18.0", + "postcss-simple-vars": "7.0.1", "react": "19.2.0", "react-dom": "19.2.0", "react-flags-select": "2.5.0", @@ -37,21 +46,15 @@ "react-share": "5.2.2", "react-type-animation": "3.2.0", "remix-utils": "9.0.0", - "ts-extras": "^0.14.0", + "server-only": "0.0.1", + "ts-extras": "0.14.0", "vaul": "1.1.2", - "xml-js": "1.6.11", - "zustand": "5.0.8" - }, - "devDependencies": { - "@cloudflare/vite-plugin": "1.13.13", - "@react-router/dev": "7.9.4", - "@types/react": "19.2.2", - "@types/react-dom": "^19.2.0", - "postcss": "8.5.6", - "postcss-preset-mantine": "1.18.0", - "postcss-simple-vars": "7.0.1", "vite-plugin-devtools-json": "1.0.0", "vite-plugin-svgr": "4.5.0", - "wrangler": "4.43.0" + "wrangler": "4.43.0", + "xml-js": "1.6.11", + "zustand": "5.0.8", + "@loglayer/transport-tslog": "3.0.4", + "tslog": "4.10.2" } } diff --git a/app/payload.ts b/cloud/payload.ts similarity index 100% rename from app/payload.ts rename to cloud/payload.ts diff --git a/app/postcss.config.ts b/cloud/postcss.config.ts similarity index 100% rename from app/postcss.config.ts rename to cloud/postcss.config.ts diff --git a/app/public/favicon.png b/cloud/public/favicon.png similarity index 100% rename from app/public/favicon.png rename to cloud/public/favicon.png diff --git a/app/public/robots.txt b/cloud/public/robots.txt similarity index 100% rename from app/public/robots.txt rename to cloud/public/robots.txt diff --git a/app/react-router.config.ts b/cloud/react-router.config.ts similarity index 100% rename from app/react-router.config.ts rename to cloud/react-router.config.ts diff --git a/app/routes/about/route.tsx b/cloud/routes/about/route.tsx similarity index 93% rename from app/routes/about/route.tsx rename to cloud/routes/about/route.tsx index 58318247..d638c1c2 100644 --- a/app/routes/about/route.tsx +++ b/cloud/routes/about/route.tsx @@ -34,8 +34,8 @@ export default function Page(_props: Route.ComponentProps) { {t("about-self-hosting-title")} {t("about-self-hosting-intro")}{" "} - - {t("about-github-repository")} + + {t("about-self-hosting-docs")} {" "} {t("about-self-hosting-middle")}{" "} diff --git a/app/routes/entry.client.tsx b/cloud/routes/entry.client.tsx similarity index 100% rename from app/routes/entry.client.tsx rename to cloud/routes/entry.client.tsx diff --git a/app/routes/entry.server.tsx b/cloud/routes/entry.server.tsx similarity index 100% rename from app/routes/entry.server.tsx rename to cloud/routes/entry.server.tsx diff --git a/app/routes/home/route.module.css b/cloud/routes/home/route.module.css similarity index 100% rename from app/routes/home/route.module.css rename to cloud/routes/home/route.module.css diff --git a/app/routes/home/route.tsx b/cloud/routes/home/route.tsx similarity index 100% rename from app/routes/home/route.tsx rename to cloud/routes/home/route.tsx diff --git a/app/routes/package/validate/Dialog.tsx b/cloud/routes/package/validate/Dialog.tsx similarity index 100% rename from app/routes/package/validate/Dialog.tsx rename to cloud/routes/package/validate/Dialog.tsx diff --git a/app/routes/package/validate/Form.tsx b/cloud/routes/package/validate/Form.tsx similarity index 51% rename from app/routes/package/validate/Form.tsx rename to cloud/routes/package/validate/Form.tsx index a3121182..02306fcd 100644 --- a/app/routes/package/validate/Form.tsx +++ b/cloud/routes/package/validate/Form.tsx @@ -1,16 +1,16 @@ -import type { Descriptor } from "@dpkit/lib" +import type { Descriptor } from "@dpkit/core" import { Form } from "#components/Form/index.ts" import { useValidatePackage } from "./queries.ts" export function ValidatePackageForm() { const validatePackage = useValidatePackage() - const handleSubmit = (value: string | File | Descriptor) => { - if (value instanceof File) { - throw new Error("File upload not implemented") + const handleSubmit = (source: string | File | Descriptor) => { + if (source instanceof File) { + throw new Error("Not implemented") } - validatePackage.mutate(value) + validatePackage.mutate({ source }) } return
diff --git a/app/routes/package/validate/queries.ts b/cloud/routes/package/validate/queries.ts similarity index 60% rename from app/routes/package/validate/queries.ts rename to cloud/routes/package/validate/queries.ts index 5a69832a..d4fa7a3e 100644 --- a/app/routes/package/validate/queries.ts +++ b/cloud/routes/package/validate/queries.ts @@ -1,19 +1,16 @@ import { useMutation } from "@tanstack/react-query" -import { createRpcSession } from "#helpers/rpc.ts" -import type { validatePackage } from "./services.ts" +import { api } from "#api/client.ts" import { store } from "./store.ts" export function useValidatePackage() { return useMutation({ mutationKey: ["validatePackage"], - mutationFn: async (source: Parameters[0]) => { + mutationFn: async (input: Parameters[0]) => { + return await api.package.validate(input) + }, + onMutate: () => { store.setState({ isDialogOpen: true }) store.setState({ isPending: true }) - - const rpc = createRpcSession() - const report = await rpc.validatePackage(source) - - return report }, onSettled: () => { store.setState({ isPending: false }) @@ -21,7 +18,8 @@ export function useValidatePackage() { onSuccess: report => { store.setState({ report }) }, - onError: () => { + onError: error => { + console.log(error) store.setState({ isFault: true }) }, }) diff --git a/app/routes/package/validate/route.tsx b/cloud/routes/package/validate/route.tsx similarity index 100% rename from app/routes/package/validate/route.tsx rename to cloud/routes/package/validate/route.tsx diff --git a/app/routes/package/validate/store.ts b/cloud/routes/package/validate/store.ts similarity index 100% rename from app/routes/package/validate/store.ts rename to cloud/routes/package/validate/store.ts diff --git a/app/routes/root.tsx b/cloud/routes/root.tsx similarity index 100% rename from app/routes/root.tsx rename to cloud/routes/root.tsx diff --git a/app/routes/routes.ts b/cloud/routes/routes.ts similarity index 100% rename from app/routes/routes.ts rename to cloud/routes/routes.ts diff --git a/app/routes/schema/infer/route.tsx b/cloud/routes/schema/infer/route.tsx similarity index 100% rename from app/routes/schema/infer/route.tsx rename to cloud/routes/schema/infer/route.tsx diff --git a/app/routes/sitemap/page.ts b/cloud/routes/sitemap/page.ts similarity index 100% rename from app/routes/sitemap/page.ts rename to cloud/routes/sitemap/page.ts diff --git a/app/routes/sitemap/root.ts b/cloud/routes/sitemap/root.ts similarity index 100% rename from app/routes/sitemap/root.ts rename to cloud/routes/sitemap/root.ts diff --git a/app/routes/sitemap/services.ts b/cloud/routes/sitemap/services.ts similarity index 100% rename from app/routes/sitemap/services.ts rename to cloud/routes/sitemap/services.ts diff --git a/app/routes/system/redirects/home.ts b/cloud/routes/system/redirects/home.ts similarity index 100% rename from app/routes/system/redirects/home.ts rename to cloud/routes/system/redirects/home.ts diff --git a/app/routes/table/convert/route.tsx b/cloud/routes/table/convert/route.tsx similarity index 100% rename from app/routes/table/convert/route.tsx rename to cloud/routes/table/convert/route.tsx diff --git a/app/routes/table/validate/route.tsx b/cloud/routes/table/validate/route.tsx similarity index 100% rename from app/routes/table/validate/route.tsx rename to cloud/routes/table/validate/route.tsx diff --git a/app/settings.ts b/cloud/settings.ts similarity index 100% rename from app/settings.ts rename to cloud/settings.ts index 689282bc..511b5f47 100644 --- a/app/settings.ts +++ b/cloud/settings.ts @@ -3,5 +3,5 @@ export const TITLE = "dpkit Cloud" export const DESCRIPTION = "dpkit Cloud" export const ICON_SIZE = 20 -export const ICON_STROKE_WIDTH = 1.5 export const ICON_SIZE_SMALL = 12 +export const ICON_STROKE_WIDTH = 1.5 diff --git a/app/styles/index.ts b/cloud/styles/index.ts similarity index 100% rename from app/styles/index.ts rename to cloud/styles/index.ts diff --git a/app/styles/main.css b/cloud/styles/main.css similarity index 100% rename from app/styles/main.css rename to cloud/styles/main.css diff --git a/app/theme.ts b/cloud/theme.ts similarity index 100% rename from app/theme.ts rename to cloud/theme.ts diff --git a/app/tsconfig.json b/cloud/tsconfig.json similarity index 100% rename from app/tsconfig.json rename to cloud/tsconfig.json diff --git a/app/types/index.ts b/cloud/types/index.ts similarity index 100% rename from app/types/index.ts rename to cloud/types/index.ts diff --git a/app/types/language.ts b/cloud/types/language.ts similarity index 100% rename from app/types/language.ts rename to cloud/types/language.ts diff --git a/app/types/page.ts b/cloud/types/page.ts similarity index 100% rename from app/types/page.ts rename to cloud/types/page.ts diff --git a/app/types/payload.ts b/cloud/types/payload.ts similarity index 100% rename from app/types/payload.ts rename to cloud/types/payload.ts diff --git a/app/types/rpc.ts b/cloud/types/rpc.ts similarity index 100% rename from app/types/rpc.ts rename to cloud/types/rpc.ts diff --git a/app/vite.config.ts b/cloud/vite.config.ts similarity index 85% rename from app/vite.config.ts rename to cloud/vite.config.ts index 0dfc2ef6..b7316219 100644 --- a/app/vite.config.ts +++ b/cloud/vite.config.ts @@ -13,6 +13,6 @@ export default defineConfig({ svgr(), ], resolve: { - alias: [{ find: "@dpkit/app", replacement: import.meta.dirname }], + alias: [{ find: "@dpkit/cloud", replacement: import.meta.dirname }], }, }) diff --git a/app/worker-configuration.d.ts b/cloud/worker-configuration.d.ts similarity index 100% rename from app/worker-configuration.d.ts rename to cloud/worker-configuration.d.ts diff --git a/app/wrangler.jsonc b/cloud/wrangler.jsonc similarity index 82% rename from app/wrangler.jsonc rename to cloud/wrangler.jsonc index e32a4a0f..c4986da1 100644 --- a/app/wrangler.jsonc +++ b/cloud/wrangler.jsonc @@ -13,7 +13,7 @@ }, "containers": [ { - "class_name": "Rpc", + "class_name": "API", "image": "./Dockerfile", "image_build_context": "../", "instance_type": "standard-1", @@ -23,14 +23,14 @@ "durable_objects": { "bindings": [ { - "class_name": "Rpc", - "name": "RPC" + "class_name": "API", + "name": "API" } ] }, "migrations": [ { - "new_sqlite_classes": ["Rpc"], + "new_sqlite_classes": ["API"], "tag": "v1" } ] diff --git a/docs/astro.config.ts b/docs/astro.config.ts index d3742118..0bd065f4 100644 --- a/docs/astro.config.ts +++ b/docs/astro.config.ts @@ -41,7 +41,6 @@ export default defineConfig({ "dpkit is a fast data management framework built on top of the Data Package standard and Polars DataFrames. It supports various formats like CSV, JSON, and Parquet and integrates with data platforms such as CKAN, Zenodo, and GitHub", customCss: ["/assets/styles.css"], components: { - SiteTitle: "./components/Header/SiteTitle.astro", SocialIcons: "./components/Header/SocialIcons.astro", }, logo: { diff --git a/docs/components/Header/SiteTitle.astro b/docs/components/Header/SiteTitle.astro deleted file mode 100644 index b98d3c36..00000000 --- a/docs/components/Header/SiteTitle.astro +++ /dev/null @@ -1,59 +0,0 @@ ---- -import config from "virtual:starlight/user-config" -import { logos } from "virtual:starlight/user-images" -const { siteTitle, siteTitleHref } = Astro.locals.starlightRoute ---- - - - { - config.logo && logos.dark && ( - <> - {config.logo.alt} - {/* Show light alternate if a user configure both light and dark logos. */} - {!('src' in config.logo) && ( - {config.logo.alt} - )} - - ) - } - - {siteTitle} - - - - diff --git a/docs/content/docs/guides/cloud.md b/docs/content/docs/guides/cloud.md new file mode 100644 index 00000000..fa022afb --- /dev/null +++ b/docs/content/docs/guides/cloud.md @@ -0,0 +1,33 @@ +--- +title: Self-Hosting dpkit Cloud +sidebar: + label: dpkit Cloud + order: 11 +--- + +This document provides a brief overview of self-hosting [dpkit Cloud](https://cloud.dpkit.dev) on your own infrastructure. + +> [!TIP] +> dpkit Cloud is built on top of [Cloudflare Workers](https://workers.cloudflare.com/) but it is possible to use any other runtime that fits your needs e.g. Digital Ocean Apps or Docker. + +## Deployment + +1. Fork the [dpkit repository](https://github.com/datisthq/dpkit) to your GitHub account. +2. Clone the forked repository to your local machine. +3. Update the `wrangler.jsonc` with you application name e.g. `dpkit-cloud-custom`. +4. Setup a new CloudFlare worker with the following configuration: + - Name: e.g. `dpkit-cloud-custom` (should be the same as in `wrangler.jsonc`) + - Github repository: point to your forked repository + - Build command: `pnpm -F cloud build` + - Deploy command: `cd cloud && pnpm wrangler deploy` + - Branch deploy command: `cd cloud && pnpm wrangler versions upload` + - Variables ans secrets: `NODE_VERSION=24` +5. Push a new commit to your forked repository to trigger the build and deployment. + +## Usage + +Find a `workder.dev` subdomain in your CloudFlare dashboard and visit it in your browser or add a custom domain if it is desired. Now, you can use your custom dpkit Cloud the same way you use [dpkit Cloud](https://cloud.dpkit.dev). + +## Support + +We happy to help with a deployment of any complexity. Please [reach out to us to get a quote](https://www.linkedin.com/in/evgeny-karev/). diff --git a/docs/content/docs/guides/service.md b/docs/content/docs/guides/service.md new file mode 100644 index 00000000..5cd5a249 --- /dev/null +++ b/docs/content/docs/guides/service.md @@ -0,0 +1,30 @@ +--- +title: Self-Hosting dpkit Service +sidebar: + label: dpkit Service + order: 10 +--- + +This document provides a brief overview of self-hosting **dpkit Service** on your own infrastructure, a OpenAPI-compatible web-server powering [dpkit Cloud](https://cloud.dpkit.dev). + +## Instructions + +1. Install `@dpkit/service` package. +2. Within your project, create a `main.ts` file: + +```ts +import { createServer } from "@dpkit/service" + +createServer({ start: true }) +``` +``` +Listening on http://localhost:8080/api +``` + +## Usage + +Visit `http://localhost:8080` to see the OpenAPI documentation for using your service. You can use your new API as any other OpenAPI-compatible service. + +## Support + +We happy to help with a deployment of any complexity. Please [reach out to us to get a quote](https://www.linkedin.com/in/evgeny-karev/). diff --git a/package.json b/package.json index 614a168c..ceb83402 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "pnpm": "^10.0.0" }, "scripts": { - "build": "pnpm -F !app -F !docs -F !site build", + "build": "pnpm -F !cloud -F !docs -F !site build", "bump": "ncu -ws -u", "clean": "rm -rf **/node_modules", "compile": "pnpm -F cli compile", @@ -37,7 +37,7 @@ "npm-check-updates": "18.0.1", "semantic-release": "24.2.9", "tempy": "3.1.0", - "type-fest": "^4.41.0", + "type-fest": "4.41.0", "typescript": "5.9.2", "vite": "7.1.10", "vitest": "3.2.4" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 365963e3..ec114cde 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,7 +42,7 @@ importers: specifier: 3.1.0 version: 3.1.0 type-fest: - specifier: ^4.41.0 + specifier: 4.41.0 version: 4.41.0 typescript: specifier: 5.9.2 @@ -54,118 +54,6 @@ importers: specifier: 3.2.4 version: 3.2.4(@types/debug@4.1.12)(@types/node@24.2.0)(@vitest/ui@3.2.4)(sass@1.93.2)(sugarss@5.0.1(postcss@8.5.6))(tsx@4.20.3)(yaml@2.8.1) - app: - dependencies: - '@cloudflare/containers': - specifier: 0.0.28 - version: 0.0.28 - '@dpkit/lib': - specifier: workspace:* - version: link:../lib - '@mantine/core': - specifier: 8.3.5 - version: 8.3.5(@mantine/hooks@8.3.5(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@mantine/form': - specifier: 8.3.5 - version: 8.3.5(react@19.2.0) - '@mantine/hooks': - specifier: 8.3.5 - version: 8.3.5(react@19.2.0) - '@tanstack/react-query': - specifier: 5.90.3 - version: 5.90.3(react@19.2.0) - capnweb: - specifier: 0.1.0 - version: 0.1.0 - es-toolkit: - specifier: 1.39.10 - version: 1.39.10 - i18next: - specifier: 25.6.0 - version: 25.6.0(typescript@5.9.2) - immer: - specifier: 10.1.3 - version: 10.1.3 - isbot: - specifier: 5.1.31 - version: 5.1.31 - loglayer: - specifier: 6.9.1 - version: 6.9.1 - lucide-react: - specifier: 0.545.0 - version: 0.545.0(react@19.2.0) - react: - specifier: 19.2.0 - version: 19.2.0 - react-dom: - specifier: 19.2.0 - version: 19.2.0(react@19.2.0) - react-flags-select: - specifier: 2.5.0 - version: 2.5.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react-i18next: - specifier: 16.0.1 - version: 16.0.1(i18next@25.6.0(typescript@5.9.2))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.2) - react-router: - specifier: 7.9.4 - version: 7.9.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react-schemaorg: - specifier: 2.0.0 - version: 2.0.0(react@19.2.0)(schema-dts@1.1.5)(typescript@5.9.2) - react-share: - specifier: 5.2.2 - version: 5.2.2(react@19.2.0) - react-type-animation: - specifier: 3.2.0 - version: 3.2.0(prop-types@15.8.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - remix-utils: - specifier: 9.0.0 - version: 9.0.0(@oslojs/encoding@1.1.0)(react-router@7.9.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0) - ts-extras: - specifier: ^0.14.0 - version: 0.14.0 - vaul: - specifier: 1.1.2 - version: 1.1.2(@types/react-dom@19.2.0(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - xml-js: - specifier: 1.6.11 - version: 1.6.11 - zustand: - specifier: 5.0.8 - version: 5.0.8(@types/react@19.2.2)(immer@10.1.3)(react@19.2.0) - devDependencies: - '@cloudflare/vite-plugin': - specifier: 1.13.13 - version: 1.13.13(vite@7.1.10(@types/node@24.2.0)(sass@1.93.2)(sugarss@5.0.1(postcss@8.5.6))(tsx@4.20.3)(yaml@2.8.1))(workerd@1.20251008.0)(wrangler@4.43.0) - '@react-router/dev': - specifier: 7.9.4 - version: 7.9.4(@types/node@24.2.0)(react-router@7.9.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(sass@1.93.2)(sugarss@5.0.1(postcss@8.5.6))(tsx@4.20.3)(typescript@5.9.2)(vite@7.1.10(@types/node@24.2.0)(sass@1.93.2)(sugarss@5.0.1(postcss@8.5.6))(tsx@4.20.3)(yaml@2.8.1))(wrangler@4.43.0)(yaml@2.8.1) - '@types/react': - specifier: 19.2.2 - version: 19.2.2 - '@types/react-dom': - specifier: ^19.2.0 - version: 19.2.0(@types/react@19.2.2) - postcss: - specifier: 8.5.6 - version: 8.5.6 - postcss-preset-mantine: - specifier: 1.18.0 - version: 1.18.0(postcss@8.5.6) - postcss-simple-vars: - specifier: 7.0.1 - version: 7.0.1(postcss@8.5.6) - vite-plugin-devtools-json: - specifier: 1.0.0 - version: 1.0.0(vite@7.1.10(@types/node@24.2.0)(sass@1.93.2)(sugarss@5.0.1(postcss@8.5.6))(tsx@4.20.3)(yaml@2.8.1)) - vite-plugin-svgr: - specifier: 4.5.0 - version: 4.5.0(rollup@4.52.4)(typescript@5.9.2)(vite@7.1.10(@types/node@24.2.0)(sass@1.93.2)(sugarss@5.0.1(postcss@8.5.6))(tsx@4.20.3)(yaml@2.8.1)) - wrangler: - specifier: 4.43.0 - version: 4.43.0 - arrow: dependencies: '@dpkit/core': @@ -188,6 +76,8 @@ importers: specifier: workspace:* version: link:../test + browser: {} + ckan: dependencies: '@dpkit/core': @@ -259,6 +149,132 @@ importers: specifier: 4.0.0 version: 4.0.0(@types/react@19.1.9) + cloud: + devDependencies: + '@cloudflare/containers': + specifier: 0.0.28 + version: 0.0.28 + '@cloudflare/vite-plugin': + specifier: 1.13.13 + version: 1.13.13(vite@7.1.10(@types/node@24.2.0)(sass@1.93.2)(sugarss@5.0.1(postcss@8.5.6))(tsx@4.20.3)(yaml@2.8.1))(workerd@1.20251008.0)(wrangler@4.43.0) + '@dpkit/core': + specifier: workspace:* + version: link:../core + '@dpkit/service': + specifier: workspace:* + version: link:../service + '@loglayer/transport-tslog': + specifier: 3.0.4 + version: 3.0.4(tslog@4.10.2) + '@mantine/core': + specifier: 8.3.5 + version: 8.3.5(@mantine/hooks@8.3.5(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@mantine/form': + specifier: 8.3.5 + version: 8.3.5(react@19.2.0) + '@mantine/hooks': + specifier: 8.3.5 + version: 8.3.5(react@19.2.0) + '@orpc/openapi-client': + specifier: 1.10.0 + version: 1.10.0 + '@react-router/dev': + specifier: 7.9.4 + version: 7.9.4(@types/node@24.2.0)(react-router@7.9.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(sass@1.93.2)(sugarss@5.0.1(postcss@8.5.6))(tsx@4.20.3)(typescript@5.9.2)(vite@7.1.10(@types/node@24.2.0)(sass@1.93.2)(sugarss@5.0.1(postcss@8.5.6))(tsx@4.20.3)(yaml@2.8.1))(wrangler@4.43.0)(yaml@2.8.1) + '@tanstack/react-query': + specifier: 5.90.3 + version: 5.90.3(react@19.2.0) + '@types/react': + specifier: 19.2.2 + version: 19.2.2 + '@types/react-dom': + specifier: ^19.2.0 + version: 19.2.0(@types/react@19.2.2) + client-only: + specifier: 0.0.1 + version: 0.0.1 + es-toolkit: + specifier: 1.39.10 + version: 1.39.10 + i18next: + specifier: 25.6.0 + version: 25.6.0(typescript@5.9.2) + immer: + specifier: 10.1.3 + version: 10.1.3 + isbot: + specifier: 5.1.31 + version: 5.1.31 + loglayer: + specifier: 6.9.1 + version: 6.9.1 + lucide-react: + specifier: 0.545.0 + version: 0.545.0(react@19.2.0) + postcss: + specifier: 8.5.6 + version: 8.5.6 + postcss-preset-mantine: + specifier: 1.18.0 + version: 1.18.0(postcss@8.5.6) + postcss-simple-vars: + specifier: 7.0.1 + version: 7.0.1(postcss@8.5.6) + react: + specifier: 19.2.0 + version: 19.2.0 + react-dom: + specifier: 19.2.0 + version: 19.2.0(react@19.2.0) + react-flags-select: + specifier: 2.5.0 + version: 2.5.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react-i18next: + specifier: 16.0.1 + version: 16.0.1(i18next@25.6.0(typescript@5.9.2))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.2) + react-router: + specifier: 7.9.4 + version: 7.9.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react-schemaorg: + specifier: 2.0.0 + version: 2.0.0(react@19.2.0)(schema-dts@1.1.5)(typescript@5.9.2) + react-share: + specifier: 5.2.2 + version: 5.2.2(react@19.2.0) + react-type-animation: + specifier: 3.2.0 + version: 3.2.0(prop-types@15.8.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + remix-utils: + specifier: 9.0.0 + version: 9.0.0(@oslojs/encoding@1.1.0)(@standard-schema/spec@1.0.0)(react-router@7.9.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0) + server-only: + specifier: 0.0.1 + version: 0.0.1 + ts-extras: + specifier: 0.14.0 + version: 0.14.0 + tslog: + specifier: 4.10.2 + version: 4.10.2 + vaul: + specifier: 1.1.2 + version: 1.1.2(@types/react-dom@19.2.0(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + vite-plugin-devtools-json: + specifier: 1.0.0 + version: 1.0.0(vite@7.1.10(@types/node@24.2.0)(sass@1.93.2)(sugarss@5.0.1(postcss@8.5.6))(tsx@4.20.3)(yaml@2.8.1)) + vite-plugin-svgr: + specifier: 4.5.0 + version: 4.5.0(rollup@4.52.4)(typescript@5.9.2)(vite@7.1.10(@types/node@24.2.0)(sass@1.93.2)(sugarss@5.0.1(postcss@8.5.6))(tsx@4.20.3)(yaml@2.8.1)) + wrangler: + specifier: 4.43.0 + version: 4.43.0 + xml-js: + specifier: 1.6.11 + version: 1.6.11 + zustand: + specifier: 5.0.8 + version: 5.0.8(@types/react@19.2.2)(immer@10.1.3)(react@19.2.0) + core: dependencies: '@sindresorhus/slugify': @@ -632,6 +648,42 @@ importers: specifier: workspace:* version: link:../test + service: + dependencies: + '@dpkit/lib': + specifier: workspace:* + version: link:../lib + '@loglayer/transport-tslog': + specifier: 3.0.4 + version: 3.0.4(tslog@4.10.2) + '@orpc/client': + specifier: ^1.10.0 + version: 1.10.0 + '@orpc/contract': + specifier: ^1.10.0 + version: 1.10.0 + '@orpc/openapi': + specifier: 1.10.0 + version: 1.10.0(crossws@0.3.5)(ws@8.18.3) + '@orpc/openapi-client': + specifier: 1.10.0 + version: 1.10.0 + '@orpc/server': + specifier: 1.10.0 + version: 1.10.0(crossws@0.3.5)(ws@8.18.3) + '@orpc/zod': + specifier: 1.10.0 + version: 1.10.0(@orpc/contract@1.10.0)(@orpc/server@1.10.0(crossws@0.3.5)(ws@8.18.3))(crossws@0.3.5)(ws@8.18.3)(zod@4.1.12) + loglayer: + specifier: 6.9.1 + version: 6.9.1 + tslog: + specifier: 4.10.2 + version: 4.10.2 + zod: + specifier: 4.1.12 + version: 4.1.12 + site: devDependencies: '@astrojs/starlight': @@ -1671,6 +1723,12 @@ packages: resolution: {integrity: sha512-KT8ic1wWstCKOjZNR6Vqpk9rK7PPx/QPjGhfqePGYjo6l9R94mSHry+zdtYjIrIPvQHbnzsp/wd6lj6IvD3MvQ==} engines: {node: '>=18'} + '@loglayer/transport-tslog@3.0.4': + resolution: {integrity: sha512-ifF4vFVOb7nc9uGdI103B1MyFeSMw1YzEV4KbxcbkO8Bxt2ppxMpNC4LQj+jnoYmYNAC5k7NDn2BQB2TfwCWHw==} + engines: {node: '>=18'} + peerDependencies: + tslog: '*' + '@loglayer/transport@2.3.4': resolution: {integrity: sha512-SHvaex987nfqV9hNj/KEMEnE7SwtDa5YwxqXbZNEtWDSWimn6agHyce73/qJC86GVrtOJmLj2HMii9JdKeZozw==} engines: {node: '>=18'} @@ -1848,6 +1906,65 @@ packages: '@octokit/types@15.0.0': resolution: {integrity: sha512-8o6yDfmoGJUIeR9OfYU0/TUJTnMPG2r68+1yEdUeG2Fdqpj8Qetg0ziKIgcBm0RW/j29H41WP37CYCEhp6GoHQ==} + '@orpc/client@1.10.0': + resolution: {integrity: sha512-5KDvC5eoZ3hCtn4lrWthXAXe+cV4UtWuAO96nRoMfJLBvGwARKbhK6mKZYq7xJuaRqpByzCAhbYAY5BNRxOK8Q==} + + '@orpc/contract@1.10.0': + resolution: {integrity: sha512-hbpYOTZBaA6dMY+Uo+xvfKCCX/2PCx4VWS4s5XT3vzrY5QocrNSFzqa7MwNXkAmJJVCYYJhd0vlur6K2n58gfw==} + + '@orpc/interop@1.10.0': + resolution: {integrity: sha512-8PHHb3IRzSWNhfeOjtcwTZD4BUsJMPsuRQpMTt1n2fwvcvupodtMPASg4LNK19dY+IZp8nuCxFhUutnBUuIDjA==} + + '@orpc/json-schema@1.10.0': + resolution: {integrity: sha512-zEytOUTa52pYlIW768MsGQYRmUvD3HRcPSSKf/2FVBL0dg2fZqnDpel909YW/d2vjJcDae0+jLvdesCmxaZKjg==} + + '@orpc/openapi-client@1.10.0': + resolution: {integrity: sha512-6QgE9yfO/g3dmGRh2Rfe1f+oRqJgmcrrZR3dEfD9LU8Hvv2/VKZfNgBGUVGbSsvEV9pDK4y1mwuQaun/Nd8ldA==} + + '@orpc/openapi@1.10.0': + resolution: {integrity: sha512-YXHCRjN+pA+VSH4v3FdqavBEJVJycpjBJq1j6x5lyRAG22aP9faGbRshRC8mNi2Br3B93CCoWGLL6TYnkjHcvQ==} + + '@orpc/server@1.10.0': + resolution: {integrity: sha512-6aNHQaGG3nhByGezXcrns74f0kyTi0Cz0aDcH8DR7tsKtcxhADrNKVy5o3hz/gDvFU0eX8FGV41Fl750a8URyw==} + peerDependencies: + crossws: '>=0.3.4' + ws: '>=8.18.1' + peerDependenciesMeta: + crossws: + optional: true + ws: + optional: true + + '@orpc/shared@1.10.0': + resolution: {integrity: sha512-YzTcoM3fImeIqx+uelNrZGhQX88t+YHvaZSe3p05t4ZRkOOjxvfrx9gv9ivHPpZ3RI12Qc/GXWzcst99fD0/AQ==} + peerDependencies: + '@opentelemetry/api': '>=1.9.0' + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + + '@orpc/standard-server-aws-lambda@1.10.0': + resolution: {integrity: sha512-ScVS4wciV++8AfLXlBBt85IfSKk8RpCsmUQVAXBTXW63YE/8YDQOIaf0NZLtIfKsN8ZI+UCwahg/Yf/a6L7pNQ==} + + '@orpc/standard-server-fetch@1.10.0': + resolution: {integrity: sha512-cERB8pPOKtqqsykmIw/TxIEoJsqIROlpBXj2LFO5iPJ5gwso0QhDhBmiaOUj9Cz+xigjIHBJvzWWokyO+mVbZw==} + + '@orpc/standard-server-node@1.10.0': + resolution: {integrity: sha512-/JO4+gPL8ULycRuQI2mzmOT8WbWHvOThXv8RsarJD7/trrG0y/3k4YWY5mZFg7XMkUu+E8apOeUrlwi2SL1mqg==} + + '@orpc/standard-server-peer@1.10.0': + resolution: {integrity: sha512-km8lTZeykmvSmM8tcnH+eiZUfHCWSG061bXDyau7FJUoyFvYWVoHKbPzYDsoje9iU0BLDyDo37HHInqQ5bHiUA==} + + '@orpc/standard-server@1.10.0': + resolution: {integrity: sha512-VX+0PIivl+fdxhHqsKMAjBVOUg65bXD2QBnOZE73KVxFPfWs++h+E8GO9KvhBqQbmHO0tQl1gnHqIqd9ICGuPA==} + + '@orpc/zod@1.10.0': + resolution: {integrity: sha512-2uYIkClwjQGrYc1S7m8QDX6mf+BlCgmeHboCfhIY6DZcKTvnqmksZ5blq6bM0Y24FxpQSw3Cc6JqU7zKYXDTrw==} + peerDependencies: + '@orpc/contract': 1.10.0 + '@orpc/server': 1.10.0 + zod: '>=3.25.0' + '@oslojs/encoding@1.1.0': resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} @@ -2423,6 +2540,9 @@ packages: '@speed-highlight/core@1.2.7': resolution: {integrity: sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g==} + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@svgr/babel-plugin-add-jsx-attribute@8.0.0': resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} engines: {node: '>=14'} @@ -2874,9 +2994,6 @@ packages: caniuse-lite@1.0.30001750: resolution: {integrity: sha512-cuom0g5sdX6rw00qOoLNSFCJ9/mYIsuSOA+yzpDw8eopiFqcVwQvZHqov0vmEighRxX++cfC0Vg1G+1Iy/mSpQ==} - capnweb@0.1.0: - resolution: {integrity: sha512-+pygKx1JFTZTRdd1hHgaBRg5BwULEDZq8ZoHXkYP2GXNV3lrjXLj5qzlGz+SgBCJjWUmNBtlh7JPWdr0wIbY8w==} - ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -2959,6 +3076,9 @@ packages: resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} engines: {node: '>=18'} + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + cliui@7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} @@ -4741,6 +4861,9 @@ packages: oniguruma-to-es@4.3.3: resolution: {integrity: sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==} + openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + p-each-series@3.0.0: resolution: {integrity: sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==} engines: {node: '>=12'} @@ -5067,6 +5190,10 @@ packages: resolution: {integrity: sha512-fG4L8TlD1CacJiGMGPxM1/K8l/GaKL2eFQZ6DWAjxZYxSf07DkumbC/Mhh+u/NHvxkfQVL25By0pxBS8QE9ZrQ==} engines: {node: '>=18'} + radash@12.1.1: + resolution: {integrity: sha512-h36JMxKRqrAxVD8201FrCpyeNuUY9Y5zZwujr20fFO77tpUtGa6EZzfKw/3WaiBX95fq7+MpsuMLNdSnORAwSA==} + engines: {node: '>=14.18.0'} + radix3@1.1.2: resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} @@ -5375,6 +5502,9 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rou3@0.7.8: + resolution: {integrity: sha512-21X/el5fdOaEsqwl3an/d9kpZ8hshVIyrwFCpsoleJ4ccAGRbN+PVoxyXzWXkHDxfMkVnLe4yzx+imz2qoem2Q==} + route-recognizer@0.3.4: resolution: {integrity: sha512-2+MhsfPhvauN1O8KaXpXAOfR/fwe8dnUXVM+xw7yt40lJRfPVQxV6yryZm0cgRvAj5fMF/mdRZbL2ptwbs5i2g==} @@ -5438,6 +5568,9 @@ packages: resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} engines: {node: '>= 0.8.0'} + server-only@0.0.1: + resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} + set-cookie-parser@2.7.1: resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} @@ -5713,6 +5846,10 @@ packages: tabbable@6.2.0: resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + temp-dir@3.0.0: resolution: {integrity: sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==} engines: {node: '>=14.16'} @@ -5812,6 +5949,10 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tslog@4.10.2: + resolution: {integrity: sha512-XuELoRpMR+sq8fuWwX7P0bcj+PRNiicOKDEb3fGNURhxWVyykCi9BNq7c4uVz7h7P0sj8qgBsr5SWS6yBClq3g==} + engines: {node: '>=16'} + tsx@4.20.3: resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} engines: {node: '>=18.0.0'} @@ -5829,6 +5970,10 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} + type-fest@5.1.0: + resolution: {integrity: sha512-wQ531tuWvB6oK+pchHIu5lHe5f5wpSCqB8Kf4dWQRbOYc9HTge7JL0G4Qd44bh6QuJCccIzL3bugb8GI0MwHrg==} + engines: {node: '>=20'} + type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} @@ -6284,6 +6429,9 @@ packages: resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} engines: {node: '>=18'} + wildcard-match@5.1.4: + resolution: {integrity: sha512-wldeCaczs8XXq7hj+5d/F38JE2r7EXgb6WQDM84RVwxy81T/sxB5e9+uZLK9Q9oNz1mlvjut+QtvgaOQFPVq/g==} + wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} @@ -6440,6 +6588,9 @@ packages: zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@4.1.12: + resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==} + zustand@5.0.8: resolution: {integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==} engines: {node: '>=12.20.0'} @@ -7310,6 +7461,11 @@ snapshots: '@loglayer/shared@2.5.3': {} + '@loglayer/transport-tslog@3.0.4(tslog@4.10.2)': + dependencies: + '@loglayer/transport': 2.3.4 + tslog: 4.10.2 + '@loglayer/transport@2.3.4': dependencies: '@loglayer/shared': 2.5.3 @@ -7526,6 +7682,137 @@ snapshots: dependencies: '@octokit/openapi-types': 26.0.0 + '@orpc/client@1.10.0': + dependencies: + '@orpc/shared': 1.10.0 + '@orpc/standard-server': 1.10.0 + '@orpc/standard-server-fetch': 1.10.0 + '@orpc/standard-server-peer': 1.10.0 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/contract@1.10.0': + dependencies: + '@orpc/client': 1.10.0 + '@orpc/shared': 1.10.0 + '@standard-schema/spec': 1.0.0 + openapi-types: 12.1.3 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/interop@1.10.0': {} + + '@orpc/json-schema@1.10.0(crossws@0.3.5)(ws@8.18.3)': + dependencies: + '@orpc/contract': 1.10.0 + '@orpc/interop': 1.10.0 + '@orpc/openapi': 1.10.0(crossws@0.3.5)(ws@8.18.3) + '@orpc/server': 1.10.0(crossws@0.3.5)(ws@8.18.3) + '@orpc/shared': 1.10.0 + transitivePeerDependencies: + - '@opentelemetry/api' + - crossws + - ws + + '@orpc/openapi-client@1.10.0': + dependencies: + '@orpc/client': 1.10.0 + '@orpc/contract': 1.10.0 + '@orpc/shared': 1.10.0 + '@orpc/standard-server': 1.10.0 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/openapi@1.10.0(crossws@0.3.5)(ws@8.18.3)': + dependencies: + '@orpc/client': 1.10.0 + '@orpc/contract': 1.10.0 + '@orpc/interop': 1.10.0 + '@orpc/openapi-client': 1.10.0 + '@orpc/server': 1.10.0(crossws@0.3.5)(ws@8.18.3) + '@orpc/shared': 1.10.0 + '@orpc/standard-server': 1.10.0 + rou3: 0.7.8 + transitivePeerDependencies: + - '@opentelemetry/api' + - crossws + - ws + + '@orpc/server@1.10.0(crossws@0.3.5)(ws@8.18.3)': + dependencies: + '@orpc/client': 1.10.0 + '@orpc/contract': 1.10.0 + '@orpc/interop': 1.10.0 + '@orpc/shared': 1.10.0 + '@orpc/standard-server': 1.10.0 + '@orpc/standard-server-aws-lambda': 1.10.0 + '@orpc/standard-server-fetch': 1.10.0 + '@orpc/standard-server-node': 1.10.0 + '@orpc/standard-server-peer': 1.10.0 + cookie: 1.0.2 + optionalDependencies: + crossws: 0.3.5 + ws: 8.18.3 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/shared@1.10.0': + dependencies: + radash: 12.1.1 + type-fest: 5.1.0 + + '@orpc/standard-server-aws-lambda@1.10.0': + dependencies: + '@orpc/shared': 1.10.0 + '@orpc/standard-server': 1.10.0 + '@orpc/standard-server-fetch': 1.10.0 + '@orpc/standard-server-node': 1.10.0 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/standard-server-fetch@1.10.0': + dependencies: + '@orpc/shared': 1.10.0 + '@orpc/standard-server': 1.10.0 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/standard-server-node@1.10.0': + dependencies: + '@orpc/shared': 1.10.0 + '@orpc/standard-server': 1.10.0 + '@orpc/standard-server-fetch': 1.10.0 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/standard-server-peer@1.10.0': + dependencies: + '@orpc/shared': 1.10.0 + '@orpc/standard-server': 1.10.0 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/standard-server@1.10.0': + dependencies: + '@orpc/shared': 1.10.0 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/zod@1.10.0(@orpc/contract@1.10.0)(@orpc/server@1.10.0(crossws@0.3.5)(ws@8.18.3))(crossws@0.3.5)(ws@8.18.3)(zod@4.1.12)': + dependencies: + '@orpc/contract': 1.10.0 + '@orpc/json-schema': 1.10.0(crossws@0.3.5)(ws@8.18.3) + '@orpc/openapi': 1.10.0(crossws@0.3.5)(ws@8.18.3) + '@orpc/server': 1.10.0(crossws@0.3.5)(ws@8.18.3) + '@orpc/shared': 1.10.0 + escape-string-regexp: 5.0.0 + wildcard-match: 5.1.4 + zod: 4.1.12 + transitivePeerDependencies: + - '@opentelemetry/api' + - crossws + - ws + '@oslojs/encoding@1.1.0': {} '@pagefind/darwin-arm64@1.4.0': @@ -8106,6 +8393,8 @@ snapshots: '@speed-highlight/core@1.2.7': {} + '@standard-schema/spec@1.0.0': {} + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 @@ -8675,8 +8964,6 @@ snapshots: caniuse-lite@1.0.30001750: {} - capnweb@0.1.0: {} - ccount@2.0.1: {} chai@5.3.3: @@ -8754,6 +9041,8 @@ snapshots: slice-ansi: 5.0.0 string-width: 7.2.0 + client-only@0.0.1: {} + cliui@7.0.4: dependencies: string-width: 4.2.3 @@ -10847,6 +11136,8 @@ snapshots: regex: 6.0.1 regex-recursion: 6.0.2 + openapi-types@12.1.3: {} + p-each-series@3.0.0: {} p-filter@4.1.0: @@ -11139,6 +11430,8 @@ snapshots: quick-lru@7.2.0: {} + radash@12.1.1: {} + radix3@1.1.2: {} range-parser@1.2.1: {} @@ -11444,11 +11737,12 @@ snapshots: transitivePeerDependencies: - supports-color - remix-utils@9.0.0(@oslojs/encoding@1.1.0)(react-router@7.9.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0): + remix-utils@9.0.0(@oslojs/encoding@1.1.0)(@standard-schema/spec@1.0.0)(react-router@7.9.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0): dependencies: type-fest: 4.41.0 optionalDependencies: '@oslojs/encoding': 1.1.0 + '@standard-schema/spec': 1.0.0 react: 19.2.0 react-router: 7.9.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -11527,6 +11821,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.52.4 fsevents: 2.3.3 + rou3@0.7.8: {} + route-recognizer@0.3.4: {} safe-buffer@5.1.2: {} @@ -11625,6 +11921,8 @@ snapshots: transitivePeerDependencies: - supports-color + server-only@0.0.1: {} + set-cookie-parser@2.7.1: {} setprototypeof@1.2.0: {} @@ -11949,6 +12247,8 @@ snapshots: tabbable@6.2.0: {} + tagged-tag@1.0.0: {} + temp-dir@3.0.0: {} tempy@3.1.0: @@ -12028,6 +12328,8 @@ snapshots: tslib@2.8.1: {} + tslog@4.10.2: {} + tsx@4.20.3: dependencies: esbuild: 0.25.10 @@ -12042,6 +12344,10 @@ snapshots: type-fest@4.41.0: {} + type-fest@5.1.0: + dependencies: + tagged-tag: 1.0.0 + type-is@1.6.18: dependencies: media-typer: 0.3.0 @@ -12422,6 +12728,8 @@ snapshots: dependencies: string-width: 7.2.0 + wildcard-match@5.1.4: {} + wordwrap@1.0.0: {} workerd@1.20251008.0: @@ -12558,6 +12866,8 @@ snapshots: zod@3.25.76: {} + zod@4.1.12: {} + zustand@5.0.8(@types/react@19.2.2)(immer@10.1.3)(react@19.2.0): optionalDependencies: '@types/react': 19.2.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0d5269ec..6473bd05 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,7 +1,8 @@ dangerouslyAllowAllBuilds: true packages: - - app - arrow + - browser + - cloud - ckan - cli - core @@ -20,6 +21,7 @@ packages: - markdown - ods - parquet + - service - site - table - test diff --git a/service/@generate.ts b/service/@generate.ts new file mode 100644 index 00000000..99921e6f --- /dev/null +++ b/service/@generate.ts @@ -0,0 +1,11 @@ +import fs from "node:fs" +import { join } from "node:path" +import { minifyContractRouter } from "@orpc/contract" +import { router } from "./router.ts" + +const contractJson = minifyContractRouter(router) + +fs.writeFileSync( + join(import.meta.dirname, "contract.json"), + `${JSON.stringify(contractJson, null, 2)}\n`, +) diff --git a/service/README.md b/service/README.md new file mode 100644 index 00000000..aa77dbad --- /dev/null +++ b/service/README.md @@ -0,0 +1,3 @@ +# @dpkit/service + +dpkit is a fast data management framework built on top of the Data Package standard and Polars DataFrames. It supports various formats like CSV, JSON, and Parquet and integrates with data platforms such as CKAN, Zenodo, and GitHub. For more information, please visit the [documentation portal](https://dpkit.dev). diff --git a/service/client.ts b/service/client.ts new file mode 100644 index 00000000..fc6f0870 --- /dev/null +++ b/service/client.ts @@ -0,0 +1,36 @@ +import { createORPCClient } from "@orpc/client" +import type { ContractRouterClient } from "@orpc/contract" +import type { ContractRouter } from "@orpc/contract" +import type { JsonifiedClient } from "@orpc/openapi-client" +import { OpenAPILink } from "@orpc/openapi-client/fetch" +import type { Logger } from "tslog" +import { contract } from "./contract.ts" +import { logger } from "./logger.ts" +import * as settings from "./settings.ts" + +export function createClient(options?: { + contract?: ContractRouter + logger?: Logger + protocol?: "http" | "https" + host?: string + port?: number + prefix?: `/${string}` +}) { + const config = { + contract: options?.contract ?? contract, + logger: options?.logger ?? logger, + protocol: options?.protocol ?? settings.PROTOCOL, + host: options?.host ?? settings.HOST, + port: options?.port ?? settings.PORT, + prefix: options?.prefix ?? settings.PREFIX, + } + + const port = ![80, 443].includes(config.port) ? config.port : undefined + const host = [config.host, port].filter(Boolean).join(":") + const url = new URL(config.prefix, `${config.protocol}://${host}`) + + const client: JsonifiedClient> = + createORPCClient(new OpenAPILink(config.contract, { url })) + + return client +} diff --git a/service/contract.json b/service/contract.json new file mode 100644 index 00000000..85263eb0 --- /dev/null +++ b/service/contract.json @@ -0,0 +1,14 @@ +{ + "package": { + "validate": { + "~orpc": { + "errorMap": {}, + "meta": {}, + "route": { + "method": "POST", + "path": "/package/validate" + } + } + } + } +} diff --git a/service/contract.ts b/service/contract.ts new file mode 100644 index 00000000..685e7f8e --- /dev/null +++ b/service/contract.ts @@ -0,0 +1,4 @@ +import contractJson from "./contract.json" with { type: "json" } +import type { router } from "./router.ts" + +export const contract = contractJson as typeof router diff --git a/service/endpoints/package/validate.ts b/service/endpoints/package/validate.ts new file mode 100644 index 00000000..6cb5335d --- /dev/null +++ b/service/endpoints/package/validate.ts @@ -0,0 +1,25 @@ +import * as dpkit from "@dpkit/lib" +import { os } from "@orpc/server" +import * as z from "zod" + +export const validatePackage = os + .route({ + method: "POST", + path: "/package/validate", + }) + .input( + z.object({ + source: z.union([z.string(), z.record(z.string(), z.unknown())]), + }), + ) + .output( + z.object({ + valid: z.boolean(), + errors: z.array(z.record(z.string(), z.any())), + }), + ) + .handler(async ({ input }) => { + const { source } = input + const result = await dpkit.validatePackage(source) + return result + }) diff --git a/service/logger.ts b/service/logger.ts new file mode 100644 index 00000000..77cac9c0 --- /dev/null +++ b/service/logger.ts @@ -0,0 +1,9 @@ +import { TsLogTransport } from "@loglayer/transport-tslog" +import { LogLayer } from "loglayer" +import { Logger } from "tslog" + +export const logger = new LogLayer({ + transport: new TsLogTransport({ + logger: new Logger({ type: "pretty" }), + }), +}) diff --git a/service/main.ts b/service/main.ts new file mode 100644 index 00000000..368f6439 --- /dev/null +++ b/service/main.ts @@ -0,0 +1,3 @@ +import { createServer } from "./server.ts" + +createServer({ start: true }) diff --git a/service/package.json b/service/package.json new file mode 100644 index 00000000..1a386d04 --- /dev/null +++ b/service/package.json @@ -0,0 +1,43 @@ +{ + "name": "@dpkit/service", + "type": "module", + "version": "0.0.0-dev", + "exports": { + "./client": "./build/runtimes/client.js", + "./server": "./build/runtimes/server.js" + }, + "sideEffects": false, + "license": "MIT", + "author": "Evgeny Karev", + "repository": "https://github.com/datisthq/dpkit", + "description": "Fast TypeScript data management framework built on top of the Data Package standard and Polars DataFrames", + "keywords": [ + "data", + "polars", + "dataframe", + "datapackage", + "tableschema", + "typescript", + "validation", + "quality", + "fair", + "service" + ], + "scripts": { + "build": "tsc", + "generate": "node @generate.ts" + }, + "dependencies": { + "@dpkit/lib": "workspace:*", + "@loglayer/transport-tslog": "3.0.4", + "@orpc/client": "^1.10.0", + "@orpc/contract": "^1.10.0", + "@orpc/openapi": "1.10.0", + "@orpc/openapi-client": "1.10.0", + "@orpc/server": "1.10.0", + "@orpc/zod": "1.10.0", + "loglayer": "6.9.1", + "tslog": "4.10.2", + "zod": "4.1.12" + } +} diff --git a/service/router.ts b/service/router.ts new file mode 100644 index 00000000..cb90464d --- /dev/null +++ b/service/router.ts @@ -0,0 +1,7 @@ +import { validatePackage } from "./endpoints/package/validate.ts" + +export const router = { + package: { + validate: validatePackage, + }, +} diff --git a/service/runtimes/client.ts b/service/runtimes/client.ts new file mode 100644 index 00000000..b8448c16 --- /dev/null +++ b/service/runtimes/client.ts @@ -0,0 +1,3 @@ +export { logger } from "../logger.ts" +export { contract } from "../contract.ts" +export { createClient } from "../client.ts" diff --git a/service/runtimes/server.ts b/service/runtimes/server.ts new file mode 100644 index 00000000..f76f62d9 --- /dev/null +++ b/service/runtimes/server.ts @@ -0,0 +1,3 @@ +export { router } from "../router.ts" +export { logger } from "../logger.ts" +export { createServer } from "../server.ts" diff --git a/service/server.ts b/service/server.ts new file mode 100644 index 00000000..34de7217 --- /dev/null +++ b/service/server.ts @@ -0,0 +1,123 @@ +import * as http from "node:http" +import { OpenAPIGenerator } from "@orpc/openapi" +import { OpenAPIHandler } from "@orpc/openapi/node" +import type { Router } from "@orpc/server" +import { CORSPlugin } from "@orpc/server/plugins" +import { ZodToJsonSchemaConverter } from "@orpc/zod" +import type { Logger } from "tslog" +import { logger } from "./logger.ts" +import metadata from "./package.json" with { type: "json" } +import { router } from "./router.ts" +import * as settings from "./settings.ts" + +export function createServer(options?: { + start?: boolean + router?: Router + logger?: Logger + protocol?: "http" | "https" + host?: string + port?: number + prefix?: `/${string}` + corsMethods?: string[] + withDocumentation?: boolean +}) { + const config = { + start: options?.start ?? settings.START, + router: options?.router ?? router, + logger: options?.logger ?? logger, + protocol: options?.protocol ?? settings.PROTOCOL, + host: options?.host ?? settings.HOST, + port: options?.port ?? settings.PORT, + prefix: options?.prefix ?? settings.PREFIX, + corsMethods: options?.corsMethods ?? settings.CORS_METHODS, + withDocumentation: + options?.withDocumentation ?? settings.WITH_DOCUMENTATION, + } + + const port = ![80, 443].includes(config.port) ? config.port : undefined + const host = [config.host, port].filter(Boolean).join(":") + const url = new URL(config.prefix, `${config.protocol}://${host}`) + + const openAPIHandler = new OpenAPIHandler(config.router, { + plugins: [ + new CORSPlugin({ + allowMethods: config.corsMethods, + exposeHeaders: ["Content-Disposition"], + }), + ], + }) + + const openAPIGenerator = new OpenAPIGenerator({ + schemaConverters: [new ZodToJsonSchemaConverter()], + }) + + const server = http.createServer(async (req, res) => { + try { + if (req.url === "/") { + // Handle health checks + res.writeHead(200) + res.end() + } else if (req.url === config.prefix) { + const html = ` + + + + dpkit Service + + + + + +
+ + + + + ` + + res.writeHead(200, { "Content-Type": "text/html" }) + res.end(html) + } else if (req.url === `${config.prefix}/spec.json`) { + const spec = await openAPIGenerator.generate(config.router, { + info: { + title: "dpkit Service", + version: metadata.version, + }, + servers: [{ url: url.toString() }], + }) + + res.writeHead(200, { "Content-Type": "application/json" }) + res.end(JSON.stringify(spec)) + return + } else { + const { matched } = await openAPIHandler.handle(req, res, { + prefix: config.prefix, + }) + + if (!matched) { + res.writeHead(404) + res.end() + } + } + } catch (error: any) { + logger.error(error) + + res.writeHead(500) + res.end() + } finally { + logger.info(`[${req.method}] ${req.url} → ${res.statusCode}`) + } + }) + + if (config.start) { + server.listen(config.port, () => + logger.info(`Listening on ${url.toString()}`), + ) + } + + return server +} diff --git a/service/settings.ts b/service/settings.ts new file mode 100644 index 00000000..07e376cf --- /dev/null +++ b/service/settings.ts @@ -0,0 +1,7 @@ +export const START = false +export const PORT = 8080 +export const HOST = "localhost" +export const PREFIX = "/api" +export const PROTOCOL = "http" +export const CORS_METHODS = ["GET", "HEAD", "PUT", "POST", "DELETE", "PATCH"] +export const WITH_DOCUMENTATION = true diff --git a/service/tsconfig.json b/service/tsconfig.json new file mode 100644 index 00000000..3c43903c --- /dev/null +++ b/service/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../tsconfig.json" +} diff --git a/tsconfig.json b/tsconfig.json index b68af920..fffde1ae 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,10 +3,10 @@ "include": ["${configDir}/**/*.ts", "${configDir}/**/*.tsx"], "exclude": [ - "${configDir}/**/app/", "${configDir}/**/docs/", "${configDir}/**/site/", "${configDir}/**/build/", + "${configDir}/**/cloud/", "${configDir}/**/compile/", "${configDir}/**/*.spec.*/" ],