Setup and write documentation website, logo update.

This commit is contained in:
Madeorsk 2024-11-27 17:34:00 +01:00
parent b897ce70ab
commit bc654b35ec
Signed by: Madeorsk
GPG key ID: 677E51CA765BB79F
21 changed files with 3782 additions and 35 deletions

8
docs/.editorconfig Normal file
View file

@ -0,0 +1,8 @@
root = true
[*]
end_of_line = lf
indent_style = tab
indent_size = 2
charset = utf-8
insert_final_newline = true

4
docs/.gitattributes vendored Normal file
View file

@ -0,0 +1,4 @@
/.yarn/** linguist-vendored
/.yarn/releases/* binary
/.yarn/plugins/**/* binary
/.pnp.* binary linguist-generated

6
docs/.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
# Yarn
.yarn/*
.pnp.*
# Vitepress
.vitepress/cache

View file

@ -0,0 +1,49 @@
import {defineConfig} from 'vitepress';
// https://vitepress.dev/reference/site-config
export default defineConfig({
title: "ZRM",
description: "Website of the ZRM library.",
themeConfig: {
logo: {
light: "/logo.svg",
dark: "/logo-dark.svg",
},
// https://vitepress.dev/reference/default-theme-config
nav: [
{text: "Home", link: "/"},
{text: "Documentation", link: "/docs"},
],
search: {
provider: "local",
},
sidebar: [
{
text: "Documentation",
link: "/docs",
items: [
{text: "Installation", link: "/docs/install"},
{text: "Database", link: "/docs/database"},
{text: "Repositories", link: "/docs/repositories"},
{text: "Queries", link: "/docs/queries"},
{text: "Insert & update", link: "/docs/insert-update"},
{text: "Relationships", link: "/docs/relationships"},
]
}
],
socialLinks: [
{icon: "git", link: "https://code.zeptotech.net/zedd/zrm"},
{icon: {
svg: '<?xml version="1.0" encoding="UTF-8"?><svg version="1.1" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m375.47 87.498c-77.422 4e-3 -154.85-4e-3 -232.27 0.0248-7.7478 0.26053-14.842 6.6567-15.698 14.398-0.42683 3.6963-0.0855 7.4215-0.20025 11.131 0.0438 5.8956-0.15111 11.802 0.19405 17.689 1.0025 7.6834 8.0618 14.125 15.846 14.215 4.2666 0.16804 8.5375-4e-3 12.806 0.0652 47.273 4e-3 94.547 8e-3 141.82 0.0124-59.064 78.8-118.13 157.6-177.17 236.42-4.1696 5.8777-5.9165 13.246-5.5202 20.391 7e-4 4.3283-0.39389 8.8988 1.5211 12.928 2.6294 5.7046 8.6562 9.7403 14.978 9.7021 3.7094 0.0962 7.4213-0.0206 11.132 0.0372 79.299-8e-5 158.6-4.9e-4 237.9-0.0248 7.7662-0.24442 14.896-6.6555 15.736-14.42 0.4355-3.6719 0.0838-7.374 0.20189-11.059-0.0475-5.9531 0.16144-11.918-0.19405-17.862-1.0754-7.9984-8.7536-14.488-16.828-14.158-56.76-6.6e-4 -113.52-9.9e-4 -170.28-2e-3 59.168-78.755 118.35-157.5 177.49-236.27 4.1886-5.8581 6.0254-13.209 5.6328-20.359-0.033-4.3117 0.43852-8.8577-1.403-12.897-2.671-6.0034-9.0893-10.229-15.705-9.9512z" stroke-width="1.3882"/><path d="m412.39 47.672c-4.8102 0.08112-9.8691 1.9242-12.803 5.8954-3.1585 4.1727-3.3865 10.069-1.2002 14.73 1.5724 3.5822 3.9639 6.716 5.7997 10.158 9.2795 15.896 17.67 32.32 24.949 49.228 3.9724 9.2879 7.6063 18.957 10.803 28.551 3.92 11.803 7.3391 23.791 10.043 35.968 2.7053 12.009 4.5884 24.371 5.742 36.692 0.55268 5.8068 0.90486 11.631 1.0857 17.461 0.0329 1.6505 0.0973 3.5193 0.10888 5.2554 0.082 5.6578 3e-3 11.344-0.19389 16.944-0.10313 2.9514-0.28719 6.1037-0.51345 9.1456-0.59634 8.0708-1.5579 16.346-2.8077 24.349-1.5636 10.089-3.8089 20.181-6.4161 30.111-2.6012 9.8786-5.6052 19.65-8.9883 29.289-1.3489 3.8284-2.8061 7.7936-4.2981 11.637-4.4468 11.564-9.6421 22.97-15.217 34.094-5.476 11.002-11.561 21.865-17.891 32.467-1.4743 2.3235-2.817 4.7811-3.3944 7.4976-1.0035 4.1928-0.25168 8.936 2.6191 12.254 2.9126 3.4016 7.4828 5.0572 11.898 4.927 5.9702-0.0534 11.822-1.8401 17.148-4.4584 4.2796-2.1304 8.2468-4.9643 11.372-8.6053 2.8245-3.2611 5.4571-6.6826 8.1435-10.057 19.783-25.454 35.502-54.065 46.284-84.449 9.7389-27.315 15.5-56.057 16.96-85.021 1.0691-21.939-0.089-44.005-3.7557-65.669-6.031-35.934-18.69-70.751-37.215-102.13-9.2406-15.755-19.847-30.719-31.751-44.574-6.0592-6.1653-14.206-10.202-22.74-11.48-1.2512-0.14827-2.5111-0.23355-3.7715-0.20898z" stroke-width="1.3461"/><path d="m99.614 464.33c4.8102-0.0811 9.8691-1.9242 12.803-5.8954 3.1585-4.1727 3.3865-10.069 1.2002-14.73-1.5724-3.5822-3.9639-6.716-5.7997-10.158-9.2795-15.896-17.67-32.32-24.949-49.228-3.9724-9.2879-7.6063-18.957-10.803-28.551-3.92-11.803-7.3391-23.791-10.043-35.968-2.7053-12.009-4.5884-24.371-5.742-36.692-0.55268-5.8068-0.90486-11.631-1.0857-17.461-0.0329-1.6505-0.0973-3.5193-0.10888-5.2554-0.082-5.6578-3e-3 -11.344 0.19389-16.944 0.10313-2.9514 0.28719-6.1037 0.51345-9.1456 0.59634-8.0708 1.5579-16.346 2.8077-24.349 1.5636-10.089 3.8089-20.181 6.4161-30.111 2.6012-9.8786 5.6052-19.65 8.9883-29.289 1.3489-3.8284 2.8061-7.7936 4.2981-11.637 4.4468-11.564 9.6421-22.97 15.217-34.094 5.476-11.002 11.561-21.865 17.891-32.467 1.4743-2.3235 2.817-4.7811 3.3944-7.4976 1.0035-4.1928 0.25168-8.936-2.6191-12.254-2.9126-3.4016-7.4828-5.0572-11.898-4.927-5.9702 0.0534-11.822 1.8401-17.148 4.4584-4.2796 2.1304-8.2468 4.9643-11.372 8.6053-2.8245 3.2611-5.4571 6.6826-8.1435 10.057-19.783 25.454-35.502 54.065-46.284 84.449-9.7389 27.315-15.5 56.057-16.96 85.021-1.0691 21.939 0.089 44.005 3.7557 65.669 6.031 35.934 18.69 70.751 37.215 102.13 9.2406 15.755 19.847 30.719 31.751 44.574 6.0592 6.1653 14.206 10.202 22.74 11.48 1.2512 0.14827 2.5111 0.23355 3.7715 0.20898z" stroke-width="1.3461"/></svg>',
}, link: "https://code.zeptotech.net/zedd"},
],
footer: {
message: "<a class='zeptotech' href='https://zeptotech.fr' target='_blank' ><img src='/zeptotech.svg' alt='Zeptotech' /></a> Powered by Zeptotech"
}
},
});

View file

@ -0,0 +1,17 @@
// https://vitepress.dev/guide/custom-theme
import {h} from 'vue';
import type {Theme} from 'vitepress';
import DefaultTheme from 'vitepress/theme';
import './style.css';
export default {
extends: DefaultTheme,
Layout: () => {
return h(DefaultTheme.Layout, null, {
// https://vitepress.dev/guide/extending-default-theme#layout-slots
})
},
enhanceApp({app, router, siteData})
{
}
} satisfies Theme;

View file

@ -0,0 +1,170 @@
/**
* Customize default theme styling by overriding CSS variables:
* https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css
*/
/**
* Colors
*
* Each colors have exact same color scale system with 3 levels of solid
* colors with different brightness, and 1 soft color.
*
* - `XXX-1`: The most solid color used mainly for colored text. It must
* satisfy the contrast ratio against when used on top of `XXX-soft`.
*
* - `XXX-2`: The color used mainly for hover state of the button.
*
* - `XXX-3`: The color for solid background, such as bg color of the button.
* It must satisfy the contrast ratio with pure white (#ffffff) text on
* top of it.
*
* - `XXX-soft`: The color used for subtle background such as custom container
* or badges. It must satisfy the contrast ratio when putting `XXX-1` colors
* on top of it.
*
* The soft color must be semi transparent alpha channel. This is crucial
* because it allows adding multiple "soft" colors on top of each other
* to create a accent, such as when having inline code block inside
* custom containers.
*
* - `default`: The color used purely for subtle indication without any
* special meanings attached to it such as bg color for menu hover state.
*
* - `brand`: Used for primary brand colors, such as link text, button with
* brand theme, etc.
*
* - `tip`: Used to indicate useful information. The default theme uses the
* brand color for this by default.
*
* - `warning`: Used to indicate warning to the users. Used in custom
* container, badges, etc.
*
* - `danger`: Used to show error, or dangerous message to the users. Used
* in custom container, badges, etc.
* -------------------------------------------------------------------------- */
:root
{
--vp-c-default-1: var(--vp-c-gray-1);
--vp-c-default-2: var(--vp-c-gray-2);
--vp-c-default-3: var(--vp-c-gray-3);
--vp-c-default-soft: var(--vp-c-gray-soft);
--vp-c-brand-1: var(--vp-c-indigo-1);
--vp-c-brand-2: var(--vp-c-indigo-2);
--vp-c-brand-3: var(--vp-c-indigo-3);
--vp-c-brand-soft: var(--vp-c-indigo-soft);
--vp-c-tip-1: var(--vp-c-brand-1);
--vp-c-tip-2: var(--vp-c-brand-2);
--vp-c-tip-3: var(--vp-c-brand-3);
--vp-c-tip-soft: var(--vp-c-brand-soft);
--vp-c-warning-1: var(--vp-c-yellow-1);
--vp-c-warning-2: var(--vp-c-yellow-2);
--vp-c-warning-3: var(--vp-c-yellow-3);
--vp-c-warning-soft: var(--vp-c-yellow-soft);
--vp-c-danger-1: var(--vp-c-red-1);
--vp-c-danger-2: var(--vp-c-red-2);
--vp-c-danger-3: var(--vp-c-red-3);
--vp-c-danger-soft: var(--vp-c-red-soft);
}
/**
* Component: Button
* -------------------------------------------------------------------------- */
:root
{
--vp-button-brand-border: transparent;
--vp-button-brand-text: var(--vp-c-white);
--vp-button-brand-bg: var(--vp-c-brand-3);
--vp-button-brand-hover-border: transparent;
--vp-button-brand-hover-text: var(--vp-c-white);
--vp-button-brand-hover-bg: var(--vp-c-brand-2);
--vp-button-brand-active-border: transparent;
--vp-button-brand-active-text: var(--vp-c-white);
--vp-button-brand-active-bg: var(--vp-c-brand-1);
}
/**
* Component: Home
* -------------------------------------------------------------------------- */
:root
{
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: -webkit-linear-gradient(
120deg,
#22aeff 30%,
#00f1cb
);
--vp-home-hero-image-background-image: linear-gradient(
-45deg,
#22aeff 50%,
#00f1cb 50%
);
--vp-home-hero-image-filter: blur(44px);
}
@media (min-width: 640px)
{
:root
{
--vp-home-hero-image-filter: blur(56px);
}
}
@media (min-width: 960px)
{
:root
{
--vp-home-hero-image-filter: blur(68px);
}
}
.VPFeatures .icon svg
{
font-size: 32px;
}
/**
* Component: Custom Block
* -------------------------------------------------------------------------- */
:root
{
--vp-custom-block-tip-border: transparent;
--vp-custom-block-tip-text: var(--vp-c-text-1);
--vp-custom-block-tip-bg: var(--vp-c-brand-soft);
--vp-custom-block-tip-code-bg: var(--vp-c-brand-soft);
}
/**
* Component: Algolia
* -------------------------------------------------------------------------- */
.DocSearch
{
--docsearch-primary-color: var(--vp-c-brand-1) !important;
}
/**
* Component: Footer
* -------------------------------------------------------------------------- */
footer a.zeptotech
{
display: block;
margin: auto;
}
footer img
{
display: block;
margin: auto;
width: 3em;
max-width: 95%;
}

3
docs/README.md Normal file
View file

@ -0,0 +1,3 @@
# ZRM :: Documentation
This is the documentation website of the ZRM library.

70
docs/docs/database.md Normal file
View file

@ -0,0 +1,70 @@
# Database
As a database-to-zig mapper, ZRM obviously needs to connect to a database to operate. All interactions with ZRM features will first need to define a database connection.
::: info
ZRM currently only supports PostgreSQL through [pg.zig](https://github.com/karlseguin/pg.zig). More DBMS's support is [planned](https://code.zeptotech.net/zedd/zrm/issues/8).
:::
## Connection
As ZRM is currently using [pg.zig](https://github.com/karlseguin/pg.zig) to connect to PostgreSQL databases, you can find a full documentation and example on the [pg.zig documentation](https://github.com/karlseguin/pg.zig#example).
```zig
const database = try pg.Pool.init(allocator, .{
.connect = .{
.host = "localhost",
.port = 5432,
},
.auth = .{
.username = "zrm",
.password = "zrm",
.database = "zrm",
},
.size = 5,
});
```
## Connector
ZRM does not use opened connections directly. All features use a generic interface called a `Connector`. A connector manages how connections are opened and released for a group of operations. There are currently two types of connectors in ZRM.
### Pool connector
The pool connector simply use a `pg.Pool` to get connections when needed. The only requirement is an opened database pool from pg.zig.
```zig
var poolConnector = zrm.database.PoolConnector{
.pool = database,
};
```
### Session connector
A session connector use a single connection while it is initialized, which is very useful when you want to perform a group of operations in a transaction. The deinitialization releases the connection.
```zig
// Start a new session.
var session = try zrm.Session.init(database);
defer session.deinit();
```
Using sessions, you can start transactions and use savepoints.
```zig
try session.beginTransaction();
// Do something.
try session.savepoint("my_savepoint");
// Do something else.
try session.rollbackTo("my_savepoint");
// Do a third thing.
try session.commitTransaction();
// or
try session.rollbackTransaction();
```

110
docs/docs/index.md Normal file
View file

@ -0,0 +1,110 @@
# Documentation
Welcome to the ZRM documentation!
ZRM is a try to make a fast and efficient zig-native [ORM](https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping) (Object Relational Mapper). With ZRM, you can define your zig models structures and easily link them to your database tables.
ZRM is using [compile-time features](https://ziglang.org/documentation/0.13.0/#toc-comptime) of the zig language to do a lot of generic work you clearly don't want to do.
## How does it look like?
```zig
/// User model.
pub const User = struct {
pub const Table = struct {
id: i32,
name: []const u8,
pub const Insert = struct {
name: []const u8,
};
};
id: i32,
name: []const u8,
info: ?UserInfo = null,
};
/// Repository of User model.
pub const UserRepository = zrm.Repository(User, User.Table, .{
.table = "example_users",
.insertShape = User.Table.Insert,
.key = &[_][]const u8{"id"},
.fromSql = zrm.helpers.TableModel(User, User.Table).copyTableToModel,
.toSql = zrm.helpers.TableModel(User, User.Table).copyModelToTable,
});
/// Relationships of User model.
pub const UserRelationships = UserRepository.relationships.define(.{
.info = UserRepository.relationships.one(UserInfoRepository, .{
.reverse = .{},
}),
});
/// User info model.
pub const UserInfo = struct {
pub const Table = struct {
user_id: i32,
birthdate: i64,
pub const Insert = struct {
user_id: i32,
birthdate: i64,
};
};
user_id: i32,
birthdate: i64,
user: ?*User = null,
};
/// Repository of UserInfo model.
pub const UserInfoRepository = zrm.Repository(UserInfo, UserInfo.Table, .{
.table = "example_users_info",
.insertShape = UserInfo.Table.Insert,
.key = &[_][]const u8{"user_id"},
.fromSql = zrm.helpers.TableModel(UserInfo, UserInfo.Table).copyTableToModel,
.toSql = zrm.helpers.TableModel(UserInfo, UserInfo.Table).copyModelToTable,
});
/// Relationships of UserInfo model.
pub const UserInfoRelationships = UserInfoRepository.relationships.define(.{
.user = UserInfoRepository.relationships.one(UserRepository, .{
.direct = .{
.foreignKey = "user_id",
},
}),
});
// Initialize a query to get users.
var firstQuery = UserRepository.QueryWith(
// Retrieve info of users.
&[_]zrm.relationships.Relationship{UserRelationships.info}
).init(std.testing.allocator, poolConnector.connector(), .{});
// We want to get the user with ID 2.
try firstQuery.whereKey(2);
defer firstQuery.deinit();
// Executing the query and getting its result.
var firstResult = try firstQuery.get(std.testing.allocator);
defer firstResult.deinit();
if (firstResult.first()) |myUser| {
// A user has been found.
std.debug.print("birthdate timestamp: {d}\n", .{user.info.birthdate});
// Changing user name.
myUser.name = "zrm lover";
// Saving the altered user.
const saveResult = UserRepository.save(allocator, poolConnector.connector(), myUser);
defer saveResult.deinit();
} else {
std.debug.print("no user with id 2 :-(\n", .{});
}
```
## Discover
This documentation will help you to discover all the features provided by ZRM and how you can use them in your project. We'll cover [installation](/docs/install), [database connection](/docs/database), [repository declaration](/docs/repositories), [queries](/docs/queries), [insertions and updates](/docs/insert-update), and [relationships](/docs/relationships). Most examples are based on a test file, [`tests/example.zig`](https://code.zeptotech.net/zedd/zrm/src/branch/main/tests/example.zig), which demonstrates and tests models, repositories, relationships and queries.

147
docs/docs/insert-update.md Normal file
View file

@ -0,0 +1,147 @@
# Insert & update
To define and use inserts and updates, you must have a fully defined [repository](/docs/repositories). In this tutorial, we'll be assuming that we have a defined repository for a user model, as it's defined in [this section](/docs/repositories.html#define-a-repository).
Executing inserts and updates also require to [set up a connection to your database](/docs/databases). We'll also be assuming that we have a working database connector set up, as it's defined in [this section](/docs/database#pool-connector).
## Insert
Just like queries, we can insert models using the type `Insert` published on repositories. With this type, the inserted shape is the default one of the repository. You can customize this structure by using `InsertCustom` function.
```zig
const insertQuery = UserRepository.Insert.init(allocator, poolConnector.connector());
// or
const insertQuery = UserRepository.InsertCustom(struct {
id: i32,
name: []const u8,
}).init(allocator, poolConnector.connector());
```
If you just need to insert a model without any other parameters, repositories provide a `create` function which do just that. The given model **will be altered** with inserted row data.
```zig
const results = UserRepository.create(allocator, poolConnector.connector(), model);
defer results.deinit();
```
### Values
With an insert query, we can pass our values to insert with the `values` function. This looks like [`set` function of update queries](#values-1).
```zig
// Insert a single model.
try insertQuery.values(model);
// Insert an array of models.
try insertQuery.values(&[_]Model{firstModel, secondModel});
// Insert a table-shaped structure.
try insertQuery.values(table);
// Insert an array of table-shaped structures.
try insertQuery.values(&[_]Model.Table{firstTable, secondTable});
// Insert a structure matching InsertShape.
try insertQuery.values(insertShapeStructure);
// Insert an array of structures matching InsertShape.
try insertQuery.values(&[_]Model.Table.Insert{firstInsertShape, secondInsertShape});
```
### Returning
It's often useful to retrieve inserted data after the query. One use case would for example to get the inserted auto-increment IDs of the models. We can do this using the `returningX` functions of the insert query builder.
::: danger
Never put user-sent values as selected columns. This could lead to severe security issues (like [SQL injections](https://en.wikipedia.org/wiki/SQL_injection)).
:::
#### Returning all
This will return all the columns of the inserted rows.
```zig
try insertQuery.values(...);
insertQuery.returningAll();
```
#### Returning columns
This will return all the provided columns of the inserted rows.
```zig
try insertQuery.values(...);
insertQuery.returningColumns(&[_][]const u8{"id", "name"});
```
#### Raw returning
We can also directly provide raw `RETURNING` clause content.
```zig
try insertQuery.values(...);
insertQuery.returning("id, label AS name");
```
### Results
We can perform the insertion by running `insert` on the insert query.
```zig
const results = try insertQuery.insert(allocator);
defer results.deinit();
```
The results of an insert query are the same as normal queries. You can find the documentation about it in [its dedicated section](/docs/queries#results).
## Update
To make an update query, we must provide the structure of the updated columns (called update shape).
```zig
const updateQuery = UserRepository.Update(struct { name: []const u8 }).init(allocator, poolConnector.connector());
```
If you just need to update a model without any other parameters, repositories provide a `save` function which do just that. The given model **will be altered** with updated row data.
```zig
const results = UserRepository.save(allocator, poolConnector.connector(), model);
defer results.deinit();
```
### Values
With an update query, we can set our updated values with the `set` function. This looks like [`values` function of insert queries](#values).
```zig
// Set data of a single model.
try updateQuery.set(model);
// Set data of an array of models.
try updateQuery.values(&[_]Model{firstModel, secondModel});
// Set data of a table-shaped structure.
try updateQuery.values(table);
// Set data of an array of table-shaped structures.
try updateQuery.values(&[_]Model.Table{firstTable, secondTable});
// Set data of a structure matching UpdateShape.
try updateQuery.values(myUpdate);
// Set data of an array of structures matching UpdateShape.
try updateQuery.values(&[_]UpdateStruct{myFirstUpdate, mySecondUpdate});
```
### Conditions
The conditions building API is the same as normal queries. You can find the documentation about it in [its dedicated section](/docs/queries#conditions).
### Returning
The returning columns API is the same as insert queries. You can find the documentation about it in [its dedicated section](#returning).
### Results
We can perform the update by running `update` on the update query.
```zig
const results = try updateQuery.update(allocator);
defer results.deinit();
```
The results of an update query are the same as normal queries. You can find the documentation about it in [its dedicated section](/docs/queries#results).

41
docs/docs/install.md Normal file
View file

@ -0,0 +1,41 @@
# Installation
You can easily install ZRM using the `zig fetch` command:
```shell
$ zig fetch --save https://code.zeptotech.net/zedd/zrm/archive/v0.3.0.tar.gz
```
::: info
You can tweak the version if you want to use something else than the latest stable one, you can find all available versions in [the tags page](https://code.zeptotech.net/zedd/zrm/tags) on the repository.
:::
This should add something like the following in `build.zig.zon` dependencies:
```zon
.{
// ...
.dependencies = .{
// ...
.zrm = .{
.url = "https://code.zeptotech.net/zedd/zrm/archive/v0.3.0.tar.gz",
.hash = "12200fe147879d72381633e6f44d76db2c8a603cda1969b4e474c15c31052dbb24b7",
},
// ...
},
// ...
}
```
Then, you can add the `zrm` module to your project build in `build.zig`.
```zig
// Add zrm dependency.
const zrm = b.dependency("zrm", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("zrm", zrm.module("zrm"));
```
You can now start to use ZRM! Why not trying to define [your first repository](/docs/repositories)?

209
docs/docs/queries.md Normal file
View file

@ -0,0 +1,209 @@
# Queries
To define and use queries, you must have a fully defined [repository](/docs/repositories). In this tutorial, we'll be assuming that we have a defined repository for a user model, as it's defined in [this section](/docs/repositories.html#define-a-repository).
Executing queries also require to [set up a connection to your database](/docs/databases). We'll also be assuming that we have a working database connector set up, as it's defined in [this section](/docs/database#pool-connector).
## Query building
ZRM repositories provide a model query builder by default. We can access it from the published type `Query`.
### Basics
Let's start with the most simple query we can make.
```zig
var myFirstQuery = UserRepository.Query.init(
allocator, poolConnector.connector(), .{}
);
defer myFirstQuery.deinit();
```
As you can see, we're creating a new query instance with our [previously defined connector](/docs/database#pool-connector). The last argument has no mandatory fields, but here are all the available options:
- `select`: a raw query part where we can set all columns that we want to select. By default, all columns of the table are retrieved (in the form of `"table_name".*`).
- `join`: a raw query part where we can set all joined tables. By default, nothing is set (~ empty string) and there will be no `JOIN` clause.
- `where`: a raw query part where we can set all the conditions to apply to the query. By default, nothing is set and there will be no `WHERE` clause.
::: warning
It is currently **NOT recommended** to use these variables to build your queries. This configuration object is experimental and might be used later to define comptime-known parts of the query, and may also be entirely removed.
:::
Based on the current configuration, we can execute this query as is, and ZRM will try to get **all** models in the defined table.
### Conditions
::: warning
Calling any of the `whereX` functions on a query overrides anything that has been previously set. If you call `whereValue` two times, only the secondly defined condition will be kept. If you need to have multiple conditions at once, you should use the [conditions builder](/docs/queries#conditions-builder).
:::
#### Simple value
Add a condition between a column and a runtime value. The type of the value must be provided as a first argument. Any valid SQL operator is accepted.
```zig
try query.whereValue(usize, "id", "!=", 1);
try query.whereValue(f32, "\"products\".\"amount\"", "<", 35.25);
```
#### Primary keys
Add a condition on the primary keys. If the primary key is composite, a structure with all the keys values is expected.
```zig
// Find the model with ID 1.
try query.whereKey(1);
// Find the model with primary key ('foo', 'bar').
try compositeQuery.whereKey(.{ .identifier = "foo", .name = "bar" });
```
The provided argument can also be an array of keys.
```zig
// Find models with ID 1 or 3.
try query.whereKey(&[_]usize{1, 3});
// Find models with primary key ('foo', 'bar') or ('baz', 'test').
try compositeQuery.whereKey(&[_]struct{identifier: []const u8, name: []const u8}{
.{ .identifier = "foo", .name = "bar" },
.{ .identifier = "baz", .name = "test" }
});
```
If you just need to get models from their ID without any other condition, repositories provide a `find` function which do just that.
```zig
const models = UserRepository.find(allocator, poolConnector.connector(), 1);
```
#### Array
Add a `WHERE column IN` condition.
```zig
try query.whereIn(usize, "id", &[_]usize{1, 2});
```
#### Column
Add a condition between two columns in the query. The columns name all must be comptime-known.
```zig
try query.whereColumn("products.amount", "<", "clients.available_amount");
```
#### Conditions builder
Sometimes, we need to build complex conditions. For this purpose, we can use the conditions builder.
The recommended way to initialize a conditions builder is to use the query, as the built conditions will be freed when the query is deinitialized.
```zig
try query.newCondition()
```
We can also directly use the conditions builder with our own allocator.
```zig
try zrm.conditions.Builder.init(allocator);
```
With the conditions builder, we can build complex conditions with AND / OR and different types of tests.
```zig
query.where(
try query.newCondition().@"or"(&[_]zrm.RawQuery{
try query.newCondition().value(usize, "id", "=", 1),
try query.newCondition().@"and"(&[_]zrm.RawQuery{
try query.newCondition().in(usize, "id", &[_]usize{100000, 200000, 300000}),
try query.newCondition().@"or"(&[_]zrm.RawQuery{
try query.newCondition().value(f64, "amount", ">", 12.13),
try query.newCondition().value([]const u8, "name", "=", "test"),
})
}),
})
);
// will produce the following WHERE clause:
// WHERE (id = ? OR (id IN (?,?,?) AND (amount > ? OR name = ?)))
```
#### Raw where
To set a raw `WHERE` clause content, we can use the `where` function.
```zig
query.where(zrm.RawQuery{
.sql = "id = ?",
.params = &[_]zrm.RawQueryParameter{.{.integer = 1}}
});
```
### Joins
::: warning
ZRM currently only supports **raw joins** definitions. Real join definition functions are expected to come in next releases.
:::
To set a raw `JOIN` clause, we can use the `join` function.
```zig
query.join(zrm.RawQuery{
.sql = "INNER JOIN foo ON user.id = foo.user_id",
.params = &[0]zrm.RawQueryParameter{}
});
// or
query.join(zrm.RawQuery{
.sql = "LEFT JOIN foo ON foo.id = ?",
.params = &[_]zrm.RawQueryParameter{.{.integer = 1}}
});
```
### Selects
::: danger
**Never** put user-sent values as selected columns. This could lead to severe security issues (like [SQL injections](https://en.wikipedia.org/wiki/SQL_injection)).
:::
#### Columns
We can select specific columns in a query with `selectColumns`. At least one selected column is required.
```zig
try query.selectColumns(&[_][]const u8{"id", "label AS name", "amount"});
```
#### Raw select
To set a raw `SELECT` clause content, we can use the `select` function.
```zig
query.where(zrm.RawQuery{
.sql = "id, label AS name, amount",
.params = &[0]zrm.RawQueryParameter{}
});
```
## Results
When our query is fully configured, we can finally call `get` to retrieve the results. We must provide an allocator to hold all the allocated models and their values. The results don't require the query to be kept, so we **can** run `query.deinit()` after getting the results without losing what has been retrieved.
```zig
var result = try query.get(allocator);
defer result.deinit();
```
The result structure allows to access the models list or the first model directly, if we just want a single one (or made sure that only one has been retrieved).
```zig{4,8}
var result = try query.get(allocator);
defer result.deinit();
if (result.first()) |model| {
// Do something with the first model.
}
for (result.models) |model| {
// Do something with all models.
}
```
The query builder allows you to get zig models from the database, but you may also need to [store them in database](/docs/insert-update) after creating or altering them.

174
docs/docs/relationships.md Normal file
View file

@ -0,0 +1,174 @@
# Relationships
To define and use relationships, you must have a fully defined [repository](/docs/repositories). In this tutorial, we'll be assuming that we have a defined repository for a user model, as it's defined in [this section](/docs/repositories.html#define-a-repository).
Executing queries also require to [set up a connection to your database](/docs/databases). We'll also be assuming that we have a working database connector set up, as it's defined in [this section](/docs/database#pool-connector).
## What is a relationship?
Before starting to define our relationships, let's try to define what they are. A relationship is a logical connection between models. In real-world applications, models are often connected between each other, and that's why we even made _relational_ databases.
If we are trying to create an easy model of a chat room, we could have two main entities:
- the chatters, that we will then call "users".
- the messages.
There is a relationship between a message and a user, because a message is always written by _someone_. In relational databases, we use foreign keys to represent this relationship (we would store a `user_id` in the `messages` table). But in programming languages, we manipulate structures and objects directly, so it can be a pain to perform operations with indexed maps or arrays.
That's why ORM are now so common in object-oriented languages. They greatly simplify the use of database-stored models, even sometimes completely hiding this fact. Zig structures are sometimes quite similar to objects of object-oriented languages, so simplifying interactions between zig structures and database tables is important.
## Define relationships
In ZRM, we define relationships on a repository. The defined relationships are stored in a comptime-known structure, reusable when building a model query.
```zig
const UserRelationships = UserRepository.relationships.define(.{
// Here, we can define the relationships of the User model.
});
```
The field where the related models will be stored after retrieval is the one with the same name in the relationships structure.
```zig
const UserRelationships = UserRepository.relationships.define(.{
// Will put the related model in `relatedModel` field of User structure:
.relatedModel = UserRepository.relationships.one(.{...}),
// Will put the related models in `relatedModels` field of User structure:
.relatedModels = UserRepository.relationships.many(.{...}),
});
```
## `one` relationships
This type of relationship is used when only a single model is related. In our chat example, the relationship type between a message and a user is "one", as there's only one message author.
### Direct
The direct one relationship uses a local foreign key to get the related model. In other libraries, this type of relationship can be referred as "belongs to". It has two parameters:
- **mandatory** `foreignKey`: name of the field / column where the related model key is stored.
- _optional_ `modelKey`: name of the key of the related model. When none is provided, the default related model key name is used (it's usually the right choice).
```zig
const MessageRelationships = MessageRepository.relationships.define(.{
.user = MessageRepository.relationships.one(UserRepository, .{
.direct => .{
.foreignKey = "user_id",
},
}),
});
```
### Reverse
The reverse one relationship uses a distant foreign key to get the related model. It can be used to get related models when they hold a foreign key to the origin model. In other libraries, this type of relationship can be referred as "has one". It has two parameters:
- _optional_ `foreignKey`: name of the field / column where the related model key is stored. When none is provided, the default related model key name is used.
- _optional_ `modelKey`: name of the key of the origin model. When none is provided, the default origin model key name is used (it's usually the right choice).
```zig
const UserRelationships = UserRepository.relationships.define(.{
.info = UserRepository.relationships.one(UserInfoRepository, .{
.reverse = .{
.foreignKey = "user_id", // this is optional if "user_id" is the defined primary key of UserInfoRepository.
},
}),
});
```
### Through
The through one relationship uses a pivot table to get the related model. It can be used to get related models when the foreign key is hold by an intermediate table. In other libraries, this type of relationship can be referred as "has one through". It has five parameters:
- **mandatory** `table`: name of the pivot / intermediate / join table.
- _optional_ `foreignKey`: name of the foreign key in the origin table. When none is provided, the default origin model key name is used (it's usually the right choice).
- **mandatory** `joinForeignKey`: name of the foreign key in the intermediate table. Its value will match the one in `foreignKey`.
- **mandatory** `joinModelKey`: name of the related model key name in the intermediate table. Its value will match the one in `modelKey`.
- _optional_ `modelKey`: name of the model key in the related table. When none is provided, the default related model key name is used (it's usually the right choice).
```zig
const MessageRelationships = MessageRepository.relationships.define(.{
.user = MessageRepository.relationships.one(UserRepository, .{
.direct = .{
.foreignKey = "user_id",
}
}),
.user_picture = MessageRepository.relationships.one(MediaRepository, .{
.through = .{
.table = "example_users",
.foreignKey = "user_id",
.joinForeignKey = "id",
.joinModelKey = "picture_id",
},
}),
});
```
## `many` relationships
This type of relationship is used when only a many models are related. In our chat example, the relationship type between a user and messages is "many", as an author can write multiple messages.
### Direct
The direct many relationship uses a distant foreign key to get related models. It's often used at the opposite side of a direct one relationship. In other libraries, this type of relationship can be referred as "has many". It has two parameters:
- **mandatory** `foreignKey`: name of the field / column where the origin model key is stored.
- _optional_ `modelKey`: name of the key of the origin model. When none is provided, the default origin model key name is used (it's usually the right choice).
```zig
const UserRelationships = UserRepository.relationships.define(.{
.messages = UserRepository.relationships.many(MessageRepository, .{
.direct = .{
.foreignKey = "user_id",
},
}),
});
```
### Through
The through many relationship uses a pivot table to get the related models. It can be used to get related models when the foreign key is hold by an intermediate table. In other libraries, this type of relationship can be referred as "belongs to many". It has five parameters:
- **mandatory** `table`: name of the pivot / intermediate / join table.
- _optional_ `foreignKey`: name of the foreign key in the origin table. When none is provided, the default origin model key name is used (it's usually the right choice).
- **mandatory** `joinForeignKey`: name of the foreign key in the intermediate table. Its value will match the one in `foreignKey`.
- **mandatory** `joinModelKey`: name of the related model key name in the intermediate table. Its value will match the one in `modelKey`.
- _optional_ `modelKey`: name of the model key in the related table. When none is provided, the default related model key name is used (it's usually the right choice).
```zig
const MessageRelationships = MessageRepository.relationships.define(.{
.medias = MessageRepository.relationships.many(MediaRepository, .{
.through = .{
.table = "example_messages_medias",
.joinModelKey = "message_id",
.joinForeignKey = "media_id",
},
}),
});
```
## Query related models
Now that our relationships are defined, we can query our models with their relationships directly with the `QueryWith` function of the repository. `QueryWith` takes an array of `Relationship` structures, which are created by `Repository.relationships.define`. To get relationships along with the models, you just need to fill this array with the requested relationships.
```zig
// Initialize a user query, with their messages.
var userQuery = UserRepository.QueryWith(
// Get messages of retrieved users.
&[_]zrm.relationships.Relationship{UserRelationships.messages}
).init(std.testing.allocator, poolConnector.connector(), .{});
try userQuery.whereKey(1);
defer userQuery.deinit();
// Get the queried user with their messages.
var userResult = try userQuery.get(std.testing.allocator);
defer userResult.deinit();
if (userResult.first()) |user| {
// The user has been found, showing their messages.
for (user.messages.?) |message| {
std.debug.print("{s}: {s}", .{user.name, message.text});
}
}
```

114
docs/docs/repositories.md Normal file
View file

@ -0,0 +1,114 @@
# Repositories
The first concept that ZRM introduces is a pretty common one: **repositories**. Repositories are the main interface for you to access what is stored in database, or to store anything in it. It is the _bridge_ between your model (a normal zig structure) and the table in database.
## Define a model
There's nothing special to do to define a model to use with ZRM. Let's start with a simple user model.
```zig
const User = struct {
id: i32,
name: []const u8,
};
```
It's a quite simple structure, but you'll quickly add more things when working with it, so let's define another structure that will hold the structure of the user in database.
```zig
const User = struct {
pub const Table = struct {
id: i32,
name: []const u8,
};
id: i32,
name: []const u8,
};
```
For now, `User` and `User.Table` are the same, but this will change as we add more features to our user.
## Define a repository
Now, let's define a repository for our `User` model.
```zig
const UserRepository = zrm.Repository(User, User.Table, .{
// ...
});
```
A repository is mainly based on 2 structures: the model and the table. These are the first two arguments. Next, it's a configuration object, with the following mandatory values:
- `table`: the table in which the models are stored.
- `insertShape`: the inserted columns by default. See [Insert & update](/docs/insert-update#insert) for more info.
- `key`: array of fields / columns to use as primary keys.
- `fromSql` / `toSql`: functions to convert tables to models and models to tables, which are used when getting and storing data.
Let's define all these fields:
```zig
const User = struct {
pub const Table = struct {
id: i32,
name: []const u8,
pub const Insert = struct {
name: []const u8,
};
};
id: i32,
name: []const u8,
};
const UserRepository = zrm.Repository(User, User.Table, .{
.table = "example_users",
.insertShape = User.Table.Insert,
.key = &[_][]const u8{"id"},
.fromSql = userFromSql,
.toSql = userToSql,
});
fn userFromSql(table: User.Table) User {
return .{
.id = table.id,
.name = table.name,
};
}
fn userToSql(user: User) User.Table {
return .{
.id = user.id,
.name = user.name,
};
}
```
We created a new structure for `insertShape`: it's the same as `User.Table`, but without the ID, as it will be automatically filled by the database when inserting (assuming that its column is defined as _auto-incrementing on insert_).
You may see that current implementation of `userFromSql` and `userToSql` is a bit useless. Luckily, ZRM provides a helper function to automatically generate them.
```zig{7,8}
const UserRepository = zrm.Repository(User, User.Table, .{
.table = "example_users",
.insertShape = User.Table.Insert,
.key = &[_][]const u8{"id"},
.fromSql = zrm.helpers.TableModel(User, User.Table).copyTableToModel,
.toSql = zrm.helpers.TableModel(User, User.Table).copyModelToTable,
});
```
It's finally done! Our repository is fully defined. As you can see we defined the following:
- where to store the models in database (which table).
- what will be inserted.
- what are the primary keys of the model.
- how to format stored data and how to get them from their stored form.
These are all the info required by ZRM to know how to deal with your models. We can now have a look to [how to retrieve models from database](/docs/queries).

32
docs/index.md Normal file
View file

@ -0,0 +1,32 @@
---
# https://vitepress.dev/reference/default-theme-home-page
layout: home
hero:
name: "ZRM"
text: "Zig Relational Mapper"
tagline: Easy interactions with your database
image:
light: "/logo.svg"
dark: "/logo-dark.svg"
alt: "ZRM logo"
actions:
- theme: brand
text: Documentation
link: /docs
- theme: alt
text: API
link: https://zedd.zeptotech.net/zrm/api
features:
- icon: '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><g fill="currentColor"><path d="M216 80c0 26.51-39.4 48-88 48s-88-21.49-88-48s39.4-48 88-48s88 21.49 88 48" opacity="0.2"/><path d="M128 24c-53.83 0-96 24.6-96 56v96c0 31.4 42.17 56 96 56s96-24.6 96-56V80c0-31.4-42.17-56-96-56m80 104c0 9.62-7.88 19.43-21.61 26.92C170.93 163.35 150.19 168 128 168s-42.93-4.65-58.39-13.08C55.88 147.43 48 137.62 48 128v-16.64c17.06 15 46.23 24.64 80 24.64s62.94-9.68 80-24.64ZM69.61 53.08C85.07 44.65 105.81 40 128 40s42.93 4.65 58.39 13.08C200.12 60.57 208 70.38 208 80s-7.88 19.43-21.61 26.92C170.93 115.35 150.19 120 128 120s-42.93-4.65-58.39-13.08C55.88 99.43 48 89.62 48 80s7.88-19.43 21.61-26.92m116.78 149.84C170.93 211.35 150.19 216 128 216s-42.93-4.65-58.39-13.08C55.88 195.43 48 185.62 48 176v-16.64c17.06 15 46.23 24.64 80 24.64s62.94-9.68 80-24.64V176c0 9.62-7.88 19.43-21.61 26.92"/></g></svg>'
title: Database models mapper
details: Connect zig models to your database.
- icon: '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><g fill="currentColor"><path d="M64 112v32a8 8 0 0 1-8 8H24a8 8 0 0 1-8-8v-32a8 8 0 0 1 8-8h32a8 8 0 0 1 8 8m144-72h-48a8 8 0 0 0-8 8v48a8 8 0 0 0 8 8h48a8 8 0 0 0 8-8V48a8 8 0 0 0-8-8m0 112h-48a8 8 0 0 0-8 8v48a8 8 0 0 0 8 8h48a8 8 0 0 0 8-8v-48a8 8 0 0 0-8-8" opacity="0.2"/><path d="M160 112h48a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16h-48a16 16 0 0 0-16 16v16h-16a24 24 0 0 0-24 24v32H72v-8a16 16 0 0 0-16-16H24a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h32a16 16 0 0 0 16-16v-8h32v32a24 24 0 0 0 24 24h16v16a16 16 0 0 0 16 16h48a16 16 0 0 0 16-16v-48a16 16 0 0 0-16-16h-48a16 16 0 0 0-16 16v16h-16a8 8 0 0 1-8-8V88a8 8 0 0 1 8-8h16v16a16 16 0 0 0 16 16M56 144H24v-32h32zm104 16h48v48h-48Zm0-112h48v48h-48Z"/></g></svg>'
title: Models relationships definition
details: Get related models in a single well-defined structure.
- icon: '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><g fill="currentColor"><path d="m96 240l16-80l-64-24L160 16l-16 80l64 24Z" opacity="0.2"/><path d="M215.79 118.17a8 8 0 0 0-5-5.66L153.18 90.9l14.66-73.33a8 8 0 0 0-13.69-7l-112 120a8 8 0 0 0 3 13l57.63 21.61l-14.62 73.25a8 8 0 0 0 13.69 7l112-120a8 8 0 0 0 1.94-7.26M109.37 214l10.47-52.38a8 8 0 0 0-5-9.06L62 132.71l84.62-90.66l-10.46 52.38a8 8 0 0 0 5 9.06l52.8 19.8Z"/></g></svg>'
title: Fast and easy queries
details: Easily get and store model data to your database from zig code.
---

1
docs/logo-dark.svg Normal file
View file

@ -0,0 +1 @@
<svg width="512" height="512" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" fill="white"><g style="display:inline;fill-opacity:1"><path d="M20 18c0 2.2091-3.5817 4-8 4-4.41828 0-8-1.7909-8-4v-4.026c.50221.6166 1.21495 1.1289 2.00774 1.5252C7.58004 16.2854 9.69967 16.75 12 16.75c2.3003 0 4.42-.4646 5.9923-1.2508.7928-.3963 1.5055-.9086 2.0077-1.5252Z" style="fill-opacity:1" transform="translate(-22.35483335 -22.47245115) scale(23.20604)"/><path d="M12 10.75c2.3003 0 4.42-.4646 5.9923-1.25075.7928-.3964 1.5055-.90866 2.0077-1.52528V12c0 .5-1.7857 1.5911-2.6786 2.1576C15.9983 14.8192 14.118 15.25 12 15.25c-2.11795 0-3.99832-.4308-5.32144-1.0924C5.5 13.5683 4 12.5 4 12V7.97397c.50221.61662 1.21495 1.12888 2.00774 1.52528C7.58004 10.2854 9.69967 10.75 12 10.75Z" style="fill-opacity:1" transform="translate(-22.35483335 -22.47245115) scale(23.20604)"/><path d="M17.3214 8.15761C15.9983 8.81917 14.118 9.25 12 9.25c-2.11795 0-3.99832-.43083-5.32144-1.09239-.51472-.20165-1.67219-.84269-2.47706-1.87826-.13696-.17622-.19574-.40082-.16162-.62137.02295-.14829.05492-.30103.0959-.39572C4.82815 3.40554 8.0858 2 12 2c3.9142 0 7.1718 1.40554 7.8642 3.26226.041.09469.073.24743.0959.39572.0341.22055-.0246.44515-.1616.62137-.8049 1.03557-1.9623 1.67661-2.4771 1.87826z" style="fill-opacity:1" transform="translate(-22.35483335 -22.47245115) scale(23.20604)"/></g><path style="display:inline;fill-opacity:1;stroke-width:23.5293" d="M276.04706 168.09412-.000001 474.65883l150.164711-43.03528 30.1891-25.96241L512 37.341172 362.30588 80.258809Z"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

1
docs/logo.svg Normal file
View file

@ -0,0 +1 @@
<svg width="512" height="512" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g style="display:inline;fill-opacity:1"><path d="M20 18c0 2.2091-3.5817 4-8 4-4.41828 0-8-1.7909-8-4v-4.026c.50221.6166 1.21495 1.1289 2.00774 1.5252C7.58004 16.2854 9.69967 16.75 12 16.75c2.3003 0 4.42-.4646 5.9923-1.2508.7928-.3963 1.5055-.9086 2.0077-1.5252Z" style="fill-opacity:1" transform="translate(-22.35483335 -22.47245115) scale(23.20604)"/><path d="M12 10.75c2.3003 0 4.42-.4646 5.9923-1.25075.7928-.3964 1.5055-.90866 2.0077-1.52528V12c0 .5-1.7857 1.5911-2.6786 2.1576C15.9983 14.8192 14.118 15.25 12 15.25c-2.11795 0-3.99832-.4308-5.32144-1.0924C5.5 13.5683 4 12.5 4 12V7.97397c.50221.61662 1.21495 1.12888 2.00774 1.52528C7.58004 10.2854 9.69967 10.75 12 10.75Z" style="fill-opacity:1" transform="translate(-22.35483335 -22.47245115) scale(23.20604)"/><path d="M17.3214 8.15761C15.9983 8.81917 14.118 9.25 12 9.25c-2.11795 0-3.99832-.43083-5.32144-1.09239-.51472-.20165-1.67219-.84269-2.47706-1.87826-.13696-.17622-.19574-.40082-.16162-.62137.02295-.14829.05492-.30103.0959-.39572C4.82815 3.40554 8.0858 2 12 2c3.9142 0 7.1718 1.40554 7.8642 3.26226.041.09469.073.24743.0959.39572.0341.22055-.0246.44515-.1616.62137-.8049 1.03557-1.9623 1.67661-2.4771 1.87826z" style="fill-opacity:1" transform="translate(-22.35483335 -22.47245115) scale(23.20604)"/></g><path style="display:inline;fill-opacity:1;stroke-width:23.5293" d="M276.04706 168.09412-.000001 474.65883l150.164711-43.03528 30.1891-25.96241L512 37.341172 362.30588 80.258809Z"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

12
docs/package.json Normal file
View file

@ -0,0 +1,12 @@
{
"name": "zrm-docs",
"packageManager": "yarn@4.5.2",
"scripts": {
"docs:dev": "vitepress dev",
"docs:build": "vitepress build",
"docs:preview": "vitepress preview"
},
"devDependencies": {
"vitepress": "^1.5.0"
}
}

2544
docs/yarn.lock Normal file

File diff suppressed because it is too large Load diff

69
docs/zeptotech.svg Normal file
View file

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="720.14001"
height="720.14001"
viewBox="0 0 190.53705 190.53705"
version="1.1"
id="svg1"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="linearGradient3">
<stop
style="stop-color:#22aeff;stop-opacity:1;"
offset="0"
id="stop1" />
<stop
style="stop-color:#00f1cb;stop-opacity:1;"
offset="1"
id="stop2" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient3"
id="linearGradient2"
x1="25.052734"
y1="160.9082"
x2="167.30664"
y2="12.158203"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#linearGradient3"
id="linearGradient6"
x1="215.58984"
y1="163.26172"
x2="42.798828"
y2="63.660156"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#linearGradient3"
id="linearGradient12"
x1="91.082031"
y1="73.657227"
x2="215.58984"
y2="73.657227"
gradientUnits="userSpaceOnUse" />
</defs>
<g
id="layer1"
transform="translate(-25.052762,8.7353212)">
<g
id="g1">
<path
id="path3-3"
style="display:inline;fill:url(#linearGradient12);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 176.85742,117.05273 H 103.875 L 91.082031,137.50977 H 215.58984 L 135.76758,9.8046875 h -25.93946 z" />
<path
id="path4"
style="display:inline;fill:url(#linearGradient6);stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 42.798828,163.26172 H 202.79688 l 12.79296,-20.45899 H 81.53125 L 118.02148,84.412109 105.05273,63.660156 Z" />
<path
id="path3"
style="display:inline;fill:url(#linearGradient2);stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 25.052734,140.15625 38.023438,160.9082 105.05273,53.669922 141.36719,111.75977 h 25.93945 L 105.05273,12.158203 Z" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -1,35 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="512" height="512" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g style="display:inline;fill-opacity:1"><path d="M20 18c0 2.2091-3.5817 4-8 4-4.41828 0-8-1.7909-8-4v-4.026c.50221.6166 1.21495 1.1289 2.00774 1.5252C7.58004 16.2854 9.69967 16.75 12 16.75c2.3003 0 4.42-.4646 5.9923-1.2508.7928-.3963 1.5055-.9086 2.0077-1.5252Z" style="fill-opacity:1" transform="translate(-22.35483335 -22.47245115) scale(23.20604)"/><path d="M12 10.75c2.3003 0 4.42-.4646 5.9923-1.25075.7928-.3964 1.5055-.90866 2.0077-1.52528V12c0 .5-1.7857 1.5911-2.6786 2.1576C15.9983 14.8192 14.118 15.25 12 15.25c-2.11795 0-3.99832-.4308-5.32144-1.0924C5.5 13.5683 4 12.5 4 12V7.97397c.50221.61662 1.21495 1.12888 2.00774 1.52528C7.58004 10.2854 9.69967 10.75 12 10.75Z" style="fill-opacity:1" transform="translate(-22.35483335 -22.47245115) scale(23.20604)"/><path d="M17.3214 8.15761C15.9983 8.81917 14.118 9.25 12 9.25c-2.11795 0-3.99832-.43083-5.32144-1.09239-.51472-.20165-1.67219-.84269-2.47706-1.87826-.13696-.17622-.19574-.40082-.16162-.62137.02295-.14829.05492-.30103.0959-.39572C4.82815 3.40554 8.0858 2 12 2c3.9142 0 7.1718 1.40554 7.8642 3.26226.041.09469.073.24743.0959.39572.0341.22055-.0246.44515-.1616.62137-.8049 1.03557-1.9623 1.67661-2.4771 1.87826z" style="fill-opacity:1" transform="translate(-22.35483335 -22.47245115) scale(23.20604)"/></g><path style="display:inline;fill-opacity:1;stroke-width:23.5293" d="M276.04706 168.09412-.000001 474.65883l150.164711-43.03528 30.1891-25.96241L512 37.341172 362.30588 80.258809Z"/></svg>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 512 512"
version="1.1"
id="svg1"
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs1" /><g
id="layer1"><g
style="display:inline;fill:#000000;fill-opacity:1"
id="g19"
transform="matrix(2.0189327,0,0,2.0189327,249.8064,248.00901)"><path
d="m 20,18 c 0,2.2091 -3.5817,4 -8,4 -4.41828,0 -8,-1.7909 -8,-4 v -4.026 c 0.50221,0.6166 1.21495,1.1289 2.00774,1.5252 C 7.58004,16.2854 9.69967,16.75 12,16.75 c 2.3003,0 4.42,-0.4646 5.9923,-1.2508 C 18.7851,15.1029 19.4978,14.5906 20,13.974 Z"
fill="#1c274c"
id="path17"
style="fill:#000000;fill-opacity:1"
transform="matrix(11.494213,0,0,11.494213,-134.80451,-133.9725)" /><path
d="m 12,10.75 c 2.3003,0 4.42,-0.4646 5.9923,-1.25075 C 18.7851,9.10285 19.4978,8.59059 20,7.97397 V 12 c 0,0.5 -1.7857,1.5911 -2.6786,2.1576 C 15.9983,14.8192 14.118,15.25 12,15.25 9.88205,15.25 8.00168,14.8192 6.67856,14.1576 5.5,13.5683 4,12.5 4,12 V 7.97397 C 4.50221,8.59059 5.21495,9.10285 6.00774,9.49925 7.58004,10.2854 9.69967,10.75 12,10.75 Z"
fill="#1c274c"
id="path18"
style="fill:#000000;fill-opacity:1"
transform="matrix(11.494213,0,0,11.494213,-134.80451,-133.9725)" /><path
d="M 17.3214,8.15761 C 15.9983,8.81917 14.118,9.25 12,9.25 9.88205,9.25 8.00168,8.81917 6.67856,8.15761 6.16384,7.95596 5.00637,7.31492 4.2015,6.27935 4.06454,6.10313 4.00576,5.87853 4.03988,5.65798 4.06283,5.50969 4.0948,5.35695 4.13578,5.26226 4.82815,3.40554 8.0858,2 12,2 c 3.9142,0 7.1718,1.40554 7.8642,3.26226 0.041,0.09469 0.073,0.24743 0.0959,0.39572 0.0341,0.22055 -0.0246,0.44515 -0.1616,0.62137 -0.8049,1.03557 -1.9623,1.67661 -2.4771,1.87826 z"
fill="#1c274c"
id="path19"
style="fill:#000000;fill-opacity:1"
transform="matrix(11.494213,0,0,11.494213,-134.80451,-133.9725)" /></g><path
id="polygon3"
style="display:inline;fill:#000000;fill-opacity:1;stroke-width:23.5293"
d="M 276.04706,168.09412 -10e-7,474.65883 150.16471,431.62355 180.35381,405.66114 512,37.341172 362.30588,80.258809 Z" /></g></svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB