Compare commits

..

2 Commits

Author SHA1 Message Date
DarkSky
2ac9158f87 Merge branch 'canary' into darksky/native-sync-state 2026-01-04 00:27:17 +08:00
DarkSky
7ef550a736 feat: native record encoding 2025-12-31 12:47:34 +08:00
781 changed files with 16563 additions and 35268 deletions

View File

@@ -337,42 +337,8 @@
},
"config": {
"type": "object",
"description": "The config for the S3 compatible storage provider.",
"description": "The config for the s3 compatible storage provider. directly passed to aws-sdk client.\n@link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html",
"properties": {
"endpoint": {
"type": "string",
"description": "The S3 compatible endpoint. Example: \"https://s3.us-east-1.amazonaws.com\" or \"https://<account>.r2.cloudflarestorage.com\"."
},
"region": {
"type": "string",
"description": "The region for the storage provider. Example: \"us-east-1\" or \"auto\" for R2."
},
"forcePathStyle": {
"type": "boolean",
"description": "Whether to use path-style bucket addressing."
},
"requestTimeoutMs": {
"type": "number",
"description": "Request timeout in milliseconds."
},
"minPartSize": {
"type": "number",
"description": "Minimum multipart part size in bytes."
},
"presign": {
"type": "object",
"description": "Presigned URL behavior configuration.",
"properties": {
"expiresInSeconds": {
"type": "number",
"description": "Expiration time in seconds for presigned URLs."
},
"signContentTypeForPut": {
"type": "boolean",
"description": "Whether to sign Content-Type for presigned PUT."
}
}
},
"credentials": {
"type": "object",
"description": "The credentials for the s3 compatible storage provider.",
@@ -382,9 +348,6 @@
},
"secretAccessKey": {
"type": "string"
},
"sessionToken": {
"type": "string"
}
}
}
@@ -406,42 +369,8 @@
},
"config": {
"type": "object",
"description": "The config for the S3 compatible storage provider.",
"description": "The config for the s3 compatible storage provider. directly passed to aws-sdk client.\n@link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html",
"properties": {
"endpoint": {
"type": "string",
"description": "The S3 compatible endpoint. Example: \"https://s3.us-east-1.amazonaws.com\" or \"https://<account>.r2.cloudflarestorage.com\"."
},
"region": {
"type": "string",
"description": "The region for the storage provider. Example: \"us-east-1\" or \"auto\" for R2."
},
"forcePathStyle": {
"type": "boolean",
"description": "Whether to use path-style bucket addressing."
},
"requestTimeoutMs": {
"type": "number",
"description": "Request timeout in milliseconds."
},
"minPartSize": {
"type": "number",
"description": "Minimum multipart part size in bytes."
},
"presign": {
"type": "object",
"description": "Presigned URL behavior configuration.",
"properties": {
"expiresInSeconds": {
"type": "number",
"description": "Expiration time in seconds for presigned URLs."
},
"signContentTypeForPut": {
"type": "boolean",
"description": "Whether to sign Content-Type for presigned PUT."
}
}
},
"credentials": {
"type": "object",
"description": "The credentials for the s3 compatible storage provider.",
@@ -451,9 +380,6 @@
},
"secretAccessKey": {
"type": "string"
},
"sessionToken": {
"type": "string"
}
}
},
@@ -532,42 +458,8 @@
},
"config": {
"type": "object",
"description": "The config for the S3 compatible storage provider.",
"description": "The config for the s3 compatible storage provider. directly passed to aws-sdk client.\n@link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html",
"properties": {
"endpoint": {
"type": "string",
"description": "The S3 compatible endpoint. Example: \"https://s3.us-east-1.amazonaws.com\" or \"https://<account>.r2.cloudflarestorage.com\"."
},
"region": {
"type": "string",
"description": "The region for the storage provider. Example: \"us-east-1\" or \"auto\" for R2."
},
"forcePathStyle": {
"type": "boolean",
"description": "Whether to use path-style bucket addressing."
},
"requestTimeoutMs": {
"type": "number",
"description": "Request timeout in milliseconds."
},
"minPartSize": {
"type": "number",
"description": "Minimum multipart part size in bytes."
},
"presign": {
"type": "object",
"description": "Presigned URL behavior configuration.",
"properties": {
"expiresInSeconds": {
"type": "number",
"description": "Expiration time in seconds for presigned URLs."
},
"signContentTypeForPut": {
"type": "boolean",
"description": "Whether to sign Content-Type for presigned PUT."
}
}
},
"credentials": {
"type": "object",
"description": "The credentials for the s3 compatible storage provider.",
@@ -577,9 +469,6 @@
},
"secretAccessKey": {
"type": "string"
},
"sessionToken": {
"type": "string"
}
}
}
@@ -601,42 +490,8 @@
},
"config": {
"type": "object",
"description": "The config for the S3 compatible storage provider.",
"description": "The config for the s3 compatible storage provider. directly passed to aws-sdk client.\n@link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html",
"properties": {
"endpoint": {
"type": "string",
"description": "The S3 compatible endpoint. Example: \"https://s3.us-east-1.amazonaws.com\" or \"https://<account>.r2.cloudflarestorage.com\"."
},
"region": {
"type": "string",
"description": "The region for the storage provider. Example: \"us-east-1\" or \"auto\" for R2."
},
"forcePathStyle": {
"type": "boolean",
"description": "Whether to use path-style bucket addressing."
},
"requestTimeoutMs": {
"type": "number",
"description": "Request timeout in milliseconds."
},
"minPartSize": {
"type": "number",
"description": "Minimum multipart part size in bytes."
},
"presign": {
"type": "object",
"description": "Presigned URL behavior configuration.",
"properties": {
"expiresInSeconds": {
"type": "number",
"description": "Expiration time in seconds for presigned URLs."
},
"signContentTypeForPut": {
"type": "boolean",
"description": "Whether to sign Content-Type for presigned PUT."
}
}
},
"credentials": {
"type": "object",
"description": "The credentials for the s3 compatible storage provider.",
@@ -646,9 +501,6 @@
},
"secretAccessKey": {
"type": "string"
},
"sessionToken": {
"type": "string"
}
}
},
@@ -743,11 +595,6 @@
"description": "Multiple hosts the server will accept requests from.\n@default []",
"default": []
},
"listenAddr": {
"type": "string",
"description": "The address to listen on (e.g., 0.0.0.0 for IPv4, :: for IPv6).\n@default \"0.0.0.0\"\n@environment `LISTEN_ADDR`",
"default": "0.0.0.0"
},
"port": {
"type": "number",
"description": "Which port the server will listen on.\n@default 3010\n@environment `AFFINE_SERVER_PORT`",
@@ -782,45 +629,6 @@
}
}
},
"telemetry": {
"type": "object",
"description": "Configuration for telemetry module",
"properties": {
"allowedOrigin": {
"type": "array",
"description": "Allowed origins for telemetry collection.\n@default [\"localhost\",\"127.0.0.1\"]",
"default": [
"localhost",
"127.0.0.1"
]
},
"ga4.measurementId": {
"type": "string",
"description": "GA4 Measurement ID for Measurement Protocol.\n@default \"\"\n@environment `GA4_MEASUREMENT_ID`",
"default": ""
},
"ga4.apiSecret": {
"type": "string",
"description": "GA4 API secret for Measurement Protocol.\n@default \"\"\n@environment `GA4_API_SECRET`",
"default": ""
},
"dedupe.ttlHours": {
"type": "number",
"description": "Telemetry dedupe TTL in hours.\n@default 24",
"default": 24
},
"dedupe.maxEntries": {
"type": "number",
"description": "Telemetry dedupe max entries.\n@default 100000",
"default": 100000
},
"batch.maxEvents": {
"type": "number",
"description": "Max events per telemetry batch.\n@default 25",
"default": 25
}
}
},
"client": {
"type": "object",
"description": "Configuration for client module",
@@ -832,108 +640,8 @@
},
"versionControl.requiredVersion": {
"type": "string",
"description": "Allowed version range of the app that allowed to access the server. Requires 'client/versionControl.enabled' to be true to take effect.\n@default \">=0.25.0\"",
"default": ">=0.25.0"
}
}
},
"calendar": {
"type": "object",
"description": "Configuration for calendar module",
"properties": {
"google": {
"type": "object",
"description": "Google Calendar integration config\n@default {\"enabled\":false,\"clientId\":\"\",\"clientSecret\":\"\",\"externalWebhookUrl\":\"\",\"webhookVerificationToken\":\"\"}\n@link https://developers.google.com/calendar/api/guides/push",
"properties": {
"enabled": {
"type": "boolean"
},
"clientId": {
"type": "string"
},
"clientSecret": {
"type": "string"
},
"externalWebhookUrl": {
"type": "string"
},
"webhookVerificationToken": {
"type": "string"
}
},
"default": {
"enabled": false,
"clientId": "",
"clientSecret": "",
"externalWebhookUrl": "",
"webhookVerificationToken": ""
}
},
"caldav": {
"type": "object",
"description": "CalDAV integration config\n@default {\"enabled\":false,\"allowCustomProvider\":false,\"providers\":[],\"allowInsecureHttp\":false,\"allowedHosts\":[],\"blockPrivateNetwork\":true,\"requestTimeoutMs\":10000,\"maxRedirects\":5}",
"properties": {
"enabled": {
"type": "boolean"
},
"allowCustomProvider": {
"type": "boolean"
},
"providers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"label": {
"type": "string"
},
"serverUrl": {
"type": "string"
},
"authType": {
"type": "string"
},
"requiresAppPassword": {
"type": "boolean"
},
"docsUrl": {
"type": "string"
}
}
}
},
"allowInsecureHttp": {
"type": "boolean"
},
"allowedHosts": {
"type": "array",
"items": {
"type": "string"
}
},
"blockPrivateNetwork": {
"type": "boolean"
},
"requestTimeoutMs": {
"type": "number"
},
"maxRedirects": {
"type": "number"
}
},
"default": {
"enabled": false,
"allowCustomProvider": false,
"providers": [],
"allowInsecureHttp": false,
"allowedHosts": [],
"blockPrivateNetwork": true,
"requestTimeoutMs": 10000,
"maxRedirects": 5
}
"description": "Allowed version range of the app that allowed to access the server. Requires 'client/versionControl.enabled' to be true to take effect.\n@default \">=0.20.0\"",
"default": ">=0.20.0"
}
}
},
@@ -1155,42 +863,8 @@
},
"config": {
"type": "object",
"description": "The config for the S3 compatible storage provider.",
"description": "The config for the s3 compatible storage provider. directly passed to aws-sdk client.\n@link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html",
"properties": {
"endpoint": {
"type": "string",
"description": "The S3 compatible endpoint. Example: \"https://s3.us-east-1.amazonaws.com\" or \"https://<account>.r2.cloudflarestorage.com\"."
},
"region": {
"type": "string",
"description": "The region for the storage provider. Example: \"us-east-1\" or \"auto\" for R2."
},
"forcePathStyle": {
"type": "boolean",
"description": "Whether to use path-style bucket addressing."
},
"requestTimeoutMs": {
"type": "number",
"description": "Request timeout in milliseconds."
},
"minPartSize": {
"type": "number",
"description": "Minimum multipart part size in bytes."
},
"presign": {
"type": "object",
"description": "Presigned URL behavior configuration.",
"properties": {
"expiresInSeconds": {
"type": "number",
"description": "Expiration time in seconds for presigned URLs."
},
"signContentTypeForPut": {
"type": "boolean",
"description": "Whether to sign Content-Type for presigned PUT."
}
}
},
"credentials": {
"type": "object",
"description": "The credentials for the s3 compatible storage provider.",
@@ -1200,9 +874,6 @@
},
"secretAccessKey": {
"type": "string"
},
"sessionToken": {
"type": "string"
}
}
}
@@ -1224,42 +895,8 @@
},
"config": {
"type": "object",
"description": "The config for the S3 compatible storage provider.",
"description": "The config for the s3 compatible storage provider. directly passed to aws-sdk client.\n@link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html",
"properties": {
"endpoint": {
"type": "string",
"description": "The S3 compatible endpoint. Example: \"https://s3.us-east-1.amazonaws.com\" or \"https://<account>.r2.cloudflarestorage.com\"."
},
"region": {
"type": "string",
"description": "The region for the storage provider. Example: \"us-east-1\" or \"auto\" for R2."
},
"forcePathStyle": {
"type": "boolean",
"description": "Whether to use path-style bucket addressing."
},
"requestTimeoutMs": {
"type": "number",
"description": "Request timeout in milliseconds."
},
"minPartSize": {
"type": "number",
"description": "Minimum multipart part size in bytes."
},
"presign": {
"type": "object",
"description": "Presigned URL behavior configuration.",
"properties": {
"expiresInSeconds": {
"type": "number",
"description": "Expiration time in seconds for presigned URLs."
},
"signContentTypeForPut": {
"type": "boolean",
"description": "Whether to sign Content-Type for presigned PUT."
}
}
},
"credentials": {
"type": "object",
"description": "The credentials for the s3 compatible storage provider.",
@@ -1269,9 +906,6 @@
},
"secretAccessKey": {
"type": "string"
},
"sessionToken": {
"type": "string"
}
}
},

View File

@@ -1,26 +0,0 @@
.git
.github/**/*.md
.gitignore
# Local dependency/build artifacts
/node_modules
/target
# Yarn v4 artifacts (not needed for image packaging)
/.yarn/cache
/.yarn/unplugged
/.yarn/install-state.gz
/.pnp.*
# Test artifacts
/test-results
/playwright-report
/coverage
/.coverage
# OS noise
.DS_Store
# Sourcemaps (keep server sourcemap for backend stacktraces)
**/*.map
!packages/backend/server/dist/main.js.map

View File

@@ -1,8 +1,6 @@
# Editor configuration, see http://editorconfig.org
root = true
[*.rs]
max_line_length = 120
[*]
charset = utf-8
indent_style = space

View File

@@ -2,6 +2,7 @@ name: Feature Request
description: Suggest a feature or improvement
title: '[Feature Request]: '
labels: ['feat', 'story']
assignees: ['hwangdev97']
body:
- type: markdown
attributes:

View File

@@ -29,26 +29,43 @@ const isInternal = buildType === 'internal';
const replicaConfig = {
stable: {
front: Number(process.env.PRODUCTION_FRONT_REPLICA) || 2,
web: 2,
graphql: Number(process.env.PRODUCTION_GRAPHQL_REPLICA) || 2,
sync: Number(process.env.PRODUCTION_SYNC_REPLICA) || 2,
renderer: Number(process.env.PRODUCTION_RENDERER_REPLICA) || 2,
doc: Number(process.env.PRODUCTION_DOC_REPLICA) || 2,
},
beta: {
front: Number(process.env.BETA_FRONT_REPLICA) || 1,
web: 1,
graphql: Number(process.env.BETA_GRAPHQL_REPLICA) || 1,
sync: Number(process.env.BETA_SYNC_REPLICA) || 1,
renderer: Number(process.env.BETA_RENDERER_REPLICA) || 1,
doc: Number(process.env.BETA_DOC_REPLICA) || 1,
},
canary: { front: 1, graphql: 1, doc: 1 },
canary: {
web: 1,
graphql: 1,
sync: 1,
renderer: 1,
doc: 1,
},
};
const cpuConfig = {
beta: { front: '1', graphql: '1', doc: '1' },
canary: { front: '500m', graphql: '1', doc: '500m' },
};
const memoryConfig = {
beta: { front: '1Gi', graphql: '1Gi', doc: '1Gi' },
canary: { front: '512Mi', graphql: '512Mi', doc: '512Mi' },
beta: {
web: '300m',
graphql: '1',
sync: '1',
doc: '1',
renderer: '300m',
},
canary: {
web: '300m',
graphql: '1',
sync: '1',
doc: '1',
renderer: '300m',
},
};
const createHelmCommand = ({ isDryRun }) => {
@@ -73,16 +90,16 @@ const createHelmCommand = ({ isDryRun }) => {
`--set-string global.indexer.apiKey="${AFFINE_INDEXER_SEARCH_API_KEY}"`,
];
const serviceAnnotations = [
`--set-json front.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
`--set-json web.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
`--set-json graphql.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
`--set-json sync.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
`--set-json doc.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
].concat(
isProduction || isBeta || isInternal
? [
`--set-json front.services.web.annotations="{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }"`,
`--set-json front.services.sync.annotations="{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }"`,
`--set-json front.services.renderer.annotations="{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }"`,
`--set-json web.service.annotations="{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }"`,
`--set-json graphql.service.annotations="{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }"`,
`--set-json sync.service.annotations="{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }"`,
`--set-json cloud-sql-proxy.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${CLOUD_SQL_IAM_ACCOUNT}\\" }"`,
`--set-json cloud-sql-proxy.nodeSelector="{ \\"iam.gke.io/gke-metadata-server-enabled\\": \\"true\\" }"`,
]
@@ -90,22 +107,14 @@ const createHelmCommand = ({ isDryRun }) => {
);
const cpu = cpuConfig[buildType];
const memory = memoryConfig[buildType];
let resources = [];
if (cpu) {
resources = resources.concat([
`--set front.resources.requests.cpu="${cpu.front}"`,
`--set graphql.resources.requests.cpu="${cpu.graphql}"`,
`--set doc.resources.requests.cpu="${cpu.doc}"`,
]);
}
if (memory) {
resources = resources.concat([
`--set front.resources.requests.memory="${memory.front}"`,
`--set graphql.resources.requests.memory="${memory.graphql}"`,
`--set doc.resources.requests.memory="${memory.doc}"`,
]);
}
const resources = cpu
? [
`--set web.resources.requests.cpu="${cpu.web}"`,
`--set graphql.resources.requests.cpu="${cpu.graphql}"`,
`--set sync.resources.requests.cpu="${cpu.sync}"`,
`--set doc.resources.requests.cpu="${cpu.doc}"`,
]
: [];
const replica = replicaConfig[buildType] || replicaConfig.canary;
@@ -121,7 +130,6 @@ const createHelmCommand = ({ isDryRun }) => {
.split(',')
.map(host => host.trim())
.filter(host => host);
const primaryHost = hosts[0] || '0.0.0.0';
const deployCommand = [
`helm upgrade --install affine .github/helm/affine`,
`--namespace ${namespace}`,
@@ -136,14 +144,18 @@ const createHelmCommand = ({ isDryRun }) => {
`--set-string global.version="${APP_VERSION}"`,
...redisAndPostgres,
...indexerOptions,
`--set front.replicaCount=${replica.front}`,
`--set-string front.image.tag="${imageTag}"`,
`--set-string front.app.host="${primaryHost}"`,
`--set web.replicaCount=${replica.web}`,
`--set-string web.image.tag="${imageTag}"`,
`--set graphql.replicaCount=${replica.graphql}`,
`--set-string graphql.image.tag="${imageTag}"`,
`--set-string graphql.app.host="${primaryHost}"`,
`--set graphql.app.host=${hosts[0]}`,
`--set sync.replicaCount=${replica.sync}`,
`--set-string sync.image.tag="${imageTag}"`,
`--set-string renderer.image.tag="${imageTag}"`,
`--set renderer.app.host=${hosts[0]}`,
`--set renderer.replicaCount=${replica.renderer}`,
`--set-string doc.image.tag="${imageTag}"`,
`--set-string doc.app.host="${primaryHost}"`,
`--set doc.app.host=${hosts[0]}`,
`--set doc.replicaCount=${replica.doc}`,
...serviceAnnotations,
...resources,

13
.github/deployment/front/Dockerfile vendored Normal file
View File

@@ -0,0 +1,13 @@
FROM openresty/openresty:1.27.1.1-0-buster
WORKDIR /app
COPY ./packages/frontend/apps/web/dist ./dist
COPY ./packages/frontend/admin/dist ./admin
COPY ./packages/frontend/apps/mobile/dist ./mobile
COPY ./.github/deployment/front/nginx.conf /usr/local/openresty/nginx/conf/nginx.conf
COPY ./.github/deployment/front/affine.nginx.conf /etc/nginx/conf.d/affine.nginx.conf
RUN mkdir -p /var/log/nginx && \
rm /etc/nginx/conf.d/default.conf
EXPOSE 8080
CMD ["/usr/local/openresty/bin/openresty", "-g", "daemon off;"]

View File

@@ -0,0 +1,42 @@
server {
listen 8080;
location /admin {
root /app/;
index index.html;
try_files $uri/index.html $uri/ $uri /admin/index.html;
}
set $app_root_path /app/dist/;
set $mobile_root /app/dist/;
set_by_lua $affine_env 'return os.getenv("AFFINE_ENV")';
if ($affine_env = "dev") {
set $mobile_root /app/mobile/;
}
# https://gist.github.com/mariusom/6683dc52b1cad1a1f372e908bdb209d0
if ($http_user_agent ~* "(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino") {
set $app_root_path $mobile_root;
}
if ($http_user_agent ~* "^(1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-)") {
set $app_root_path $mobile_root;
}
location ~ ^/(_plugin|assets|imgs|js|plugins|static)/ {
root $app_root_path;
try_files $uri $uri/ =404;
}
location / {
root $app_root_path;
index index.html;
try_files $uri $uri/ /index.html;
add_header Cache-Control "private, no-cache, no-store, max-age=0, must-revalidate";
}
error_page 404 /404.html;
location = /404.html {
internal;
}
}

15
.github/deployment/front/nginx.conf vendored Normal file
View File

@@ -0,0 +1,15 @@
worker_processes 4;
error_log /var/log/nginx/error.log warn;
pcre_jit on;
env AFFINE_ENV;
events {
worker_connections 1024;
}
http {
include mime.types;
log_format main '$remote_addr [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
include /etc/nginx/conf.d/*.conf;
}

View File

@@ -1,28 +1,11 @@
# syntax=docker/dockerfile:1.7
FROM node:22-bookworm-slim AS assets
WORKDIR /app
FROM node:22-bookworm-slim
COPY ./packages/backend/server /app
COPY ./packages/frontend/apps/web/dist /app/static
COPY ./packages/frontend/admin/dist /app/static/admin
COPY ./packages/frontend/apps/mobile/dist /app/static/mobile
# Keep server sourcemap for stacktraces, but don't ship frontend/node_modules sourcemaps.
ARG TARGETARCH
ARG TARGETVARIANT
# Needed for Prisma engine resolution (and potential engine download during cleanup).
RUN apt-get update && \
apt-get install -y --no-install-recommends openssl ca-certificates && \
rm -rf /var/lib/apt/lists/*
RUN AFFINE_DOCKER_CLEAN=1 TARGETARCH="${TARGETARCH}" TARGETVARIANT="${TARGETVARIANT}" node ./scripts/docker-clean.mjs
FROM node:22-bookworm-slim
WORKDIR /app
COPY --from=assets /app /app
RUN apt-get update && \
apt-get install -y --no-install-recommends openssl libjemalloc2 && \
rm -rf /var/lib/apt/lists/*
@@ -30,6 +13,4 @@ RUN apt-get update && \
# Enable jemalloc by preloading the library
ENV LD_PRELOAD=libjemalloc.so.2
EXPOSE 3010
CMD ["node", "./dist/main.js"]

View File

@@ -3,4 +3,4 @@ name: affine
description: AFFiNE cloud chart
type: application
version: 0.0.0
appVersion: "0.26.1"
appVersion: "0.25.7"

View File

@@ -3,7 +3,7 @@ name: doc
description: AFFiNE doc server
type: application
version: 0.0.0
appVersion: "0.26.1"
appVersion: "0.25.7"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -1,120 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "front.fullname" . }}
labels:
{{- include "front.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "front.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "front.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "front.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: AFFINE_PRIVATE_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.global.secret.secretName }}"
key: key
- name: NODE_ENV
value: "{{ .Values.env }}"
- name: NODE_OPTIONS
value: "{{ .Values.nodeOptions }}"
- name: NO_COLOR
value: "1"
- name: DEPLOYMENT_TYPE
value: "{{ .Values.global.deployment.type }}"
- name: DEPLOYMENT_PLATFORM
value: "{{ .Values.global.deployment.platform }}"
- name: SERVER_FLAVOR
value: "front"
- name: AFFINE_ENV
value: "{{ .Release.Namespace }}"
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: pg-postgresql
key: postgres-password
- name: DATABASE_URL
value: postgres://{{ .Values.global.database.user }}:$(DATABASE_PASSWORD)@{{ .Values.global.database.host }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }}
- name: REDIS_SERVER_ENABLED
value: "true"
- name: REDIS_SERVER_HOST
value: "{{ .Values.global.redis.host }}"
- name: REDIS_SERVER_PORT
value: "{{ .Values.global.redis.port }}"
- name: REDIS_SERVER_USER
value: "{{ .Values.global.redis.username }}"
- name: REDIS_SERVER_PASSWORD
valueFrom:
secretKeyRef:
name: redis
key: redis-password
- name: REDIS_SERVER_DATABASE
value: "{{ .Values.global.redis.database }}"
- name: AFFINE_INDEXER_SEARCH_PROVIDER
value: "{{ .Values.global.indexer.provider }}"
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
value: "{{ .Values.global.indexer.endpoint }}"
- name: AFFINE_INDEXER_SEARCH_API_KEY
valueFrom:
secretKeyRef:
name: indexer
key: indexer-apiKey
- name: AFFINE_SERVER_PORT
value: "{{ .Values.app.port }}"
- name: AFFINE_SERVER_SUB_PATH
value: "{{ .Values.app.path }}"
- name: AFFINE_SERVER_HOST
value: "{{ .Values.app.host }}"
- name: AFFINE_SERVER_HTTPS
value: "{{ .Values.app.https }}"
- name: DOC_SERVICE_ENDPOINT
value: "http://{{ .Values.global.docService.name }}:{{ .Values.global.docService.port }}"
ports:
- name: http
containerPort: {{ .Values.app.port }}
protocol: TCP
livenessProbe:
httpGet:
path: /info
port: http
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
readinessProbe:
httpGet:
path: /info
port: http
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -1,19 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.services.renderer.name }}
labels:
{{- include "front.labels" . | nindent 4 }}
{{- with .Values.services.renderer.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.services.renderer.type }}
ports:
- port: {{ .Values.services.renderer.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "front.selectorLabels" . | nindent 4 }}

View File

@@ -1,19 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.services.sync.name }}
labels:
{{- include "front.labels" . | nindent 4 }}
{{- with .Values.services.sync.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.services.sync.type }}
ports:
- port: {{ .Values.services.sync.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "front.selectorLabels" . | nindent 4 }}

View File

@@ -1,19 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.services.web.name }}
labels:
{{- include "front.labels" . | nindent 4 }}
{{- with .Values.services.web.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.services.web.type }}
ports:
- port: {{ .Values.services.web.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "front.selectorLabels" . | nindent 4 }}

View File

@@ -1,60 +0,0 @@
replicaCount: 1
image:
repository: ghcr.io/toeverything/affine
pullPolicy: IfNotPresent
tag: ''
imagePullSecrets: []
nameOverride: ''
fullnameOverride: ''
# map to NODE_ENV environment variable
env: 'production'
nodeOptions: '--max-old-space-size=3072'
app:
# AFFINE_SERVER_PORT
port: 3010
# AFFINE_SERVER_SUB_PATH
path: ''
# AFFINE_SERVER_HOST
host: '0.0.0.0'
https: true
serviceAccount:
create: true
annotations: {}
name: 'affine-front'
podAnnotations: {}
podSecurityContext:
fsGroup: 2000
resources:
requests:
cpu: '2'
memory: 4Gi
probe:
initialDelaySeconds: 20
services:
sync:
name: affine-sync
type: ClusterIP
port: 3010
annotations:
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
renderer:
name: affine-renderer
type: ClusterIP
port: 3000
annotations:
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
web:
name: affine-web
type: ClusterIP
port: 8080
annotations: {}
nodeSelector: {}
tolerations: []
affinity: {}

View File

@@ -3,7 +3,7 @@ name: graphql
description: AFFiNE GraphQL server
type: application
version: 0.0.0
appVersion: "0.26.1"
appVersion: "0.25.7"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -0,0 +1,11 @@
apiVersion: v2
name: renderer
description: AFFiNE renderer server
type: application
version: 0.0.0
appVersion: "0.25.7"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0
repository: "file://../gcloud-sql-proxy"
condition: .global.database.gcloud.enabled

View File

@@ -1,15 +1,15 @@
1. Get the application URL by running these commands:
{{- if contains "NodePort" .Values.services.sync.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ .Values.services.sync.name }})
{{- if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "renderer.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.services.sync.type }}
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ .Values.services.sync.name }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ .Values.services.sync.name }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.services.sync.port }}
{{- else if contains "ClusterIP" .Values.services.sync.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "front.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "renderer.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "renderer.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "renderer.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT

View File

@@ -0,0 +1,63 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "renderer.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "renderer.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "renderer.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "renderer.labels" -}}
helm.sh/chart: {{ include "renderer.chart" . }}
{{ include "renderer.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
monitoring: enabled
{{- end }}
{{/*
Selector labels
*/}}
{{- define "renderer.selectorLabels" -}}
app.kubernetes.io/name: {{ include "renderer.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "renderer.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "renderer.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,118 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "renderer.fullname" . }}
labels:
{{- include "renderer.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "renderer.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "renderer.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "renderer.serviceAccountName" . }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: AFFINE_PRIVATE_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.global.secret.secretName }}"
key: key
- name: NODE_ENV
value: "{{ .Values.env }}"
- name: NODE_OPTIONS
value: "--max-old-space-size=2048"
- name: NO_COLOR
value: "1"
- name: DEPLOYMENT_TYPE
value: "{{ .Values.global.deployment.type }}"
- name: DEPLOYMENT_PLATFORM
value: "{{ .Values.global.deployment.platform }}"
- name: SERVER_FLAVOR
value: "renderer"
- name: AFFINE_ENV
value: "{{ .Release.Namespace }}"
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: pg-postgresql
key: postgres-password
- name: DATABASE_URL
value: postgres://{{ .Values.global.database.user }}:$(DATABASE_PASSWORD)@{{ .Values.global.database.host }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }}
- name: REDIS_SERVER_ENABLED
value: "true"
- name: REDIS_SERVER_HOST
value: "{{ .Values.global.redis.host }}"
- name: REDIS_SERVER_PORT
value: "{{ .Values.global.redis.port }}"
- name: REDIS_SERVER_USER
value: "{{ .Values.global.redis.username }}"
- name: REDIS_SERVER_PASSWORD
valueFrom:
secretKeyRef:
name: redis
key: redis-password
- name: REDIS_SERVER_DATABASE
value: "{{ .Values.global.redis.database }}"
- name: AFFINE_INDEXER_SEARCH_PROVIDER
value: "{{ .Values.global.indexer.provider }}"
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
value: "{{ .Values.global.indexer.endpoint }}"
- name: AFFINE_INDEXER_SEARCH_API_KEY
valueFrom:
secretKeyRef:
name: indexer
key: indexer-apiKey
- name: AFFINE_SERVER_PORT
value: "{{ .Values.service.port }}"
- name: AFFINE_SERVER_SUB_PATH
value: "{{ .Values.app.path }}"
- name: AFFINE_SERVER_HOST
value: "{{ .Values.app.host }}"
- name: AFFINE_SERVER_HTTPS
value: "{{ .Values.app.https }}"
- name: DOC_SERVICE_ENDPOINT
value: "http://{{ .Values.global.docService.name }}:{{ .Values.global.docService.port }}"
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
livenessProbe:
httpGet:
path: /info
port: http
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
readinessProbe:
httpGet:
path: /info
port: http
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -0,0 +1,19 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "graphql.fullname" . }}
labels:
{{- include "graphql.labels" . | nindent 4 }}
{{- with .Values.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "graphql.selectorLabels" . | nindent 4 }}

View File

@@ -0,0 +1,12 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "graphql.serviceAccountName" . }}
labels:
{{- include "graphql.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "renderer.fullname" . }}-test-connection"
labels:
{{- include "renderer.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "renderer.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never

View File

@@ -0,0 +1,38 @@
replicaCount: 1
image:
repository: ghcr.io/toeverything/affine
pullPolicy: IfNotPresent
tag: ''
imagePullSecrets: []
nameOverride: ''
fullnameOverride: ''
# map to NODE_ENV environment variable
env: 'production'
app:
# AFFINE_SERVER_SUB_PATH
path: ''
# AFFINE_SERVER_HOST
host: '0.0.0.0'
https: true
serviceAccount:
create: true
annotations: {}
name: 'affine-renderer'
podAnnotations: {}
podSecurityContext:
fsGroup: 2000
resources:
requests:
cpu: '1'
memory: 2Gi
probe:
initialDelaySeconds: 20
nodeSelector: {}
tolerations: []
affinity: {}

View File

@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

View File

@@ -1,9 +1,9 @@
apiVersion: v2
name: front
description: AFFiNE front server
name: sync
description: AFFiNE Sync Server
type: application
version: 0.0.0
appVersion: "0.26.1"
appVersion: "0.25.7"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -0,0 +1,16 @@
1. Get the application URL by running these commands:
{{- if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "sync.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "sync.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "sync.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "sync.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}

View File

@@ -1,7 +1,7 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "front.name" -}}
{{- define "sync.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
@@ -10,7 +10,7 @@ Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "front.fullname" -}}
{{- define "sync.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
@@ -26,16 +26,16 @@ If release name contains chart name it will be used as a full name.
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "front.chart" -}}
{{- define "sync.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "front.labels" -}}
helm.sh/chart: {{ include "front.chart" . }}
{{ include "front.selectorLabels" . }}
{{- define "sync.labels" -}}
helm.sh/chart: {{ include "sync.chart" . }}
{{ include "sync.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
@@ -46,17 +46,17 @@ monitoring: enabled
{{/*
Selector labels
*/}}
{{- define "front.selectorLabels" -}}
app.kubernetes.io/name: {{ include "front.name" . }}
{{- define "sync.selectorLabels" -}}
app.kubernetes.io/name: {{ include "sync.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "front.serviceAccountName" -}}
{{- define "sync.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "front.fullname" .) .Values.serviceAccount.name }}
{{- default (include "sync.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}

View File

@@ -0,0 +1,112 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "sync.fullname" . }}
labels:
{{- include "sync.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "sync.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "sync.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "sync.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: AFFINE_PRIVATE_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.global.secret.secretName }}"
key: key
- name: NODE_ENV
value: "{{ .Values.env }}"
- name: NO_COLOR
value: "1"
- name: DEPLOYMENT_TYPE
value: "{{ .Values.global.deployment.type }}"
- name: DEPLOYMENT_PLATFORM
value: "{{ .Values.global.deployment.platform }}"
- name: SERVER_FLAVOR
value: "sync"
- name: AFFINE_ENV
value: "{{ .Release.Namespace }}"
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: pg-postgresql
key: postgres-password
- name: DATABASE_URL
value: postgres://{{ .Values.global.database.user }}:$(DATABASE_PASSWORD)@{{ .Values.global.database.host }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }}
- name: REDIS_SERVER_HOST
value: "{{ .Values.global.redis.host }}"
- name: REDIS_SERVER_PORT
value: "{{ .Values.global.redis.port }}"
- name: REDIS_SERVER_USER
value: "{{ .Values.global.redis.username }}"
- name: REDIS_SERVER_PASSWORD
valueFrom:
secretKeyRef:
name: redis
key: redis-password
- name: REDIS_SERVER_DATABASE
value: "{{ .Values.global.redis.database }}"
- name: AFFINE_INDEXER_SEARCH_PROVIDER
value: "{{ .Values.global.indexer.provider }}"
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
value: "{{ .Values.global.indexer.endpoint }}"
- name: AFFINE_INDEXER_SEARCH_API_KEY
valueFrom:
secretKeyRef:
name: indexer
key: indexer-apiKey
- name: AFFINE_SERVER_PORT
value: "{{ .Values.service.port }}"
- name: AFFINE_SERVER_HOST
value: "{{ .Values.app.host }}"
- name: DOC_SERVICE_ENDPOINT
value: "http://{{ .Values.global.docService.name }}:{{ .Values.global.docService.port }}"
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
livenessProbe:
tcpSocket:
port: http
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
readinessProbe:
tcpSocket:
port: http
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -0,0 +1,19 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "sync.fullname" . }}
labels:
{{- include "sync.labels" . | nindent 4 }}
{{- with .Values.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "sync.selectorLabels" . | nindent 4 }}

View File

@@ -2,9 +2,9 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "front.serviceAccountName" . }}
name: {{ include "sync.serviceAccountName" . }}
labels:
{{- include "front.labels" . | nindent 4 }}
{{- include "sync.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}

View File

@@ -1,9 +1,9 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "front.fullname" . }}-test-connection"
name: "{{ include "sync.fullname" . }}-test-connection"
labels:
{{- include "front.labels" . | nindent 4 }}
{{- include "sync.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
@@ -11,5 +11,5 @@ spec:
- name: wget
image: busybox
command: ['wget']
args: ['{{ .Values.services.sync.name }}:{{ .Values.services.sync.port }}']
args: ['{{ include "sync.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never

View File

@@ -0,0 +1,38 @@
replicaCount: 1
image:
repository: ghcr.io/toeverything/affine
pullPolicy: IfNotPresent
tag: ''
imagePullSecrets: []
nameOverride: ''
fullnameOverride: ''
# map to NODE_ENV environment variable
env: 'production'
app:
# AFFINE_SERVER_HOST
host: '0.0.0.0'
serviceAccount:
create: true
annotations: {}
name: 'affine-sync'
podAnnotations: {}
podSecurityContext:
fsGroup: 2000
resources:
limits:
cpu: '2'
memory: 4Gi
requests:
cpu: '1'
memory: 2Gi
probe:
initialDelaySeconds: 20
nodeSelector: {}
tolerations: []
affinity: {}

View File

@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

View File

@@ -0,0 +1,6 @@
apiVersion: v2
name: web
description: A Helm chart for Kubernetes
type: application
version: 0.0.0
appVersion: "0.7.0-canary.18"

View File

@@ -0,0 +1,16 @@
1. Get the application URL by running these commands:
{{- if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "web.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "web.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "web.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "web.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}

View File

@@ -0,0 +1,63 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "web.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "web.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "web.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "web.labels" -}}
helm.sh/chart: {{ include "web.chart" . }}
{{ include "web.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
monitoring: enabled
{{- end }}
{{/*
Selector labels
*/}}
{{- define "web.selectorLabels" -}}
app.kubernetes.io/name: {{ include "web.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "web.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "web.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,60 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "web.fullname" . }}
labels:
{{- include "web.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "web.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "web.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "web.serviceAccountName" . }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: AFFINE_ENV
value: "{{ .Release.Namespace }}"
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "web.fullname" . }}
labels:
{{- include "web.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "web.selectorLabels" . | nindent 4 }}

View File

@@ -0,0 +1,12 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "web.serviceAccountName" . }}
labels:
{{- include "web.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "web.fullname" . }}-test-connection"
labels:
{{- include "web.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "web.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never

View File

@@ -0,0 +1,37 @@
replicaCount: 1
image:
repository: ghcr.io/toeverything/affine-front
pullPolicy: IfNotPresent
tag: ""
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
create: true
annotations: {}
name: "affine-web"
podAnnotations: {}
podSecurityContext:
fsGroup: 2000
resources:
limits:
cpu: '500m'
memory: 2Gi
requests:
cpu: '500m'
memory: 2Gi
nodeSelector: {}
tolerations: []
affinity: {}
probe:
initialDelaySeconds: 1

View File

@@ -44,9 +44,9 @@ spec:
pathType: Prefix
backend:
service:
name: {{ $.Values.front.services.sync.name }}
name: affine-sync
port:
number: {{ $.Values.front.services.sync.port }}
number: {{ $.Values.sync.service.port }}
- path: /graphql
pathType: Prefix
backend:
@@ -65,15 +65,15 @@ spec:
pathType: Prefix
backend:
service:
name: {{ $.Values.front.services.renderer.name }}
name: affine-renderer
port:
number: {{ $.Values.front.services.renderer.port }}
number: {{ $.Values.renderer.service.port }}
- path: /
pathType: Prefix
backend:
service:
name: {{ $.Values.front.services.web.name }}
name: affine-web
port:
number: {{ $.Values.front.services.web.port }}
number: {{ $.Values.web.service.port }}
{{- end }}
{{- end }}

View File

@@ -47,27 +47,27 @@ graphql:
annotations:
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
sync:
service:
type: ClusterIP
port: 3010
annotations:
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
renderer:
service:
type: ClusterIP
port: 3000
annotations:
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
doc:
service:
type: ClusterIP
annotations:
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
front:
services:
sync:
name: affine-sync
type: ClusterIP
port: 3010
annotations:
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
renderer:
name: affine-renderer
type: ClusterIP
port: 3000
annotations:
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
web:
name: affine-web
type: ClusterIP
port: 8080
web:
service:
type: ClusterIP
port: 8080

View File

@@ -45,6 +45,7 @@ jobs:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
PERFSEE_TOKEN: ${{ secrets.PERFSEE_TOKEN }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
- name: Upload web artifact
uses: actions/upload-artifact@v4
with:
@@ -77,6 +78,7 @@ jobs:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
PERFSEE_TOKEN: ${{ secrets.PERFSEE_TOKEN }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
- name: Upload admin artifact
uses: actions/upload-artifact@v4
with:
@@ -109,6 +111,7 @@ jobs:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
PERFSEE_TOKEN: ${{ secrets.PERFSEE_TOKEN }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
- name: Upload mobile artifact
uses: actions/upload-artifact@v4
with:
@@ -263,7 +266,18 @@ jobs:
with:
app-version: ${{ inputs.app-version }}
- name: Build backend Dockerfile
- name: Build front Dockerfile
uses: docker/build-push-action@v6
with:
context: .
push: true
pull: true
platforms: linux/amd64,linux/arm64
provenance: true
file: .github/deployment/front/Dockerfile
tags: ghcr.io/toeverything/affine-front:${{inputs.build-type}}-${{ inputs.git-short-hash }}
- name: Build graphql Dockerfile
uses: docker/build-push-action@v6
with:
context: .

View File

@@ -182,7 +182,7 @@ jobs:
strategy:
fail-fast: false
matrix:
shard: [1, 2]
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
@@ -213,7 +213,7 @@ jobs:
strategy:
fail-fast: false
matrix:
shard: [1]
shard: [1, 2]
browser: ['chromium', 'firefox', 'webkit']
steps:
- uses: actions/checkout@v4
@@ -251,7 +251,7 @@ jobs:
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4, 5]
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
@@ -282,7 +282,7 @@ jobs:
strategy:
fail-fast: false
matrix:
shard: [1, 2]
shard: [1, 2, 3, 4, 5]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
@@ -313,7 +313,7 @@ jobs:
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3]
shard: [1, 2, 3, 4, 5]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
@@ -507,8 +507,8 @@ jobs:
strategy:
fail-fast: false
matrix:
node_index: [0, 1, 2, 3]
total_nodes: [4]
node_index: [0, 1, 2, 3, 4, 5, 6, 7]
total_nodes: [8]
env:
NODE_ENV: test
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
@@ -798,6 +798,49 @@ jobs:
name: fuzz-artifact
path: packages/common/y-octo/utils/fuzz/artifacts/**/*
y-octo-binding-test:
name: y-octo binding test on ${{ matrix.settings.target }}
runs-on: ${{ matrix.settings.os }}
strategy:
fail-fast: false
matrix:
settings:
- { target: 'x86_64-unknown-linux-gnu', os: 'ubuntu-latest' }
- { target: 'aarch64-unknown-linux-gnu', os: 'ubuntu-24.04-arm' }
- { target: 'x86_64-apple-darwin', os: 'macos-15-intel' }
- { target: 'aarch64-apple-darwin', os: 'macos-latest' }
- { target: 'x86_64-pc-windows-msvc', os: 'windows-latest' }
- { target: 'aarch64-pc-windows-msvc', os: 'windows-11-arm' }
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
extra-flags: workspaces focus @affine-tools/cli @affine/monorepo @y-octo/node
electron-install: false
- name: Install rustup (Windows 11 ARM)
if: matrix.settings.os == 'windows-11-arm'
shell: pwsh
run: |
Invoke-WebRequest -Uri "https://static.rust-lang.org/rustup/dist/aarch64-pc-windows-msvc/rustup-init.exe" -OutFile rustup-init.exe
.\rustup-init.exe --default-toolchain none -y
"$env:USERPROFILE\.cargo\bin" | Out-File -Append -Encoding ascii $env:GITHUB_PATH
"CARGO_HOME=$env:USERPROFILE\.cargo" | Out-File -Append -Encoding ascii $env:GITHUB_ENV
- name: Install Rust (Windows 11 ARM)
if: matrix.settings.os == 'windows-11-arm'
shell: pwsh
run: |
rustup install stable
rustup target add ${{ matrix.settings.target }}
cargo --version
- name: Build Rust
uses: ./.github/actions/build-rust
with:
target: ${{ matrix.settings.target }}
package: '@y-octo/node'
- name: Run tests
run: yarn affine @y-octo/node test
rust-test:
name: Run native tests
runs-on: ubuntu-latest
@@ -924,8 +967,8 @@ jobs:
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4, 5]
shardTotal: [5]
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
shardTotal: [10]
needs:
- build-server-native
services:
@@ -1016,12 +1059,36 @@ jobs:
fail-fast: false
matrix:
tests:
- name: 'Cloud E2E Test 1/2'
- name: 'Cloud E2E Test 1/10'
shard: 1
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=1/2
- name: 'Cloud E2E Test 2/2'
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=1/10
- name: 'Cloud E2E Test 2/10'
shard: 2
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=2/2
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=2/10
- name: 'Cloud E2E Test 3/10'
shard: 3
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=3/10
- name: 'Cloud E2E Test 4/10'
shard: 4
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=4/10
- name: 'Cloud E2E Test 5/10'
shard: 5
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=5/10
- name: 'Cloud E2E Test 6/10'
shard: 6
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=6/10
- name: 'Cloud E2E Test 7/10'
shard: 7
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=7/10
- name: 'Cloud E2E Test 8/10'
shard: 8
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=8/10
- name: 'Cloud E2E Test 9/10'
shard: 9
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=9/10
- name: 'Cloud E2E Test 10/10'
shard: 10
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=10/10
- name: 'Cloud Desktop E2E Test'
shard: desktop
script: |
@@ -1320,6 +1387,7 @@ jobs:
- miri
- loom
- fuzzing
- y-octo-binding-test
- server-test
- server-e2e-test
- rust-test

View File

@@ -68,6 +68,7 @@ jobs:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_RELEASE: ${{ inputs.app_version }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
steps:
- uses: actions/checkout@v4

View File

@@ -66,6 +66,7 @@ jobs:
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_RELEASE: ${{ inputs.app-version }}
RELEASE_VERSION: ${{ inputs.app-version }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
- name: Upload web artifact
uses: actions/upload-artifact@v4

View File

@@ -39,6 +39,7 @@ jobs:
run: yarn affine @affine/ios build
env:
PUBLIC_PATH: '/'
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: 'affine'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
@@ -67,6 +68,7 @@ jobs:
run: yarn affine @affine/android build
env:
PUBLIC_PATH: '/'
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: 'affine'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
@@ -108,7 +110,7 @@ jobs:
enableScripts: false
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: 26.2
xcode-version: 16.4
- name: Install Swiftformat
run: brew install swiftformat
- name: Cap sync

3
.gitignore vendored
View File

@@ -47,7 +47,8 @@ testem.log
.pnpm-debug.log
/typings
tsconfig.tsbuildinfo
.context
rfc*.md
todo.md
# System Files
.DS_Store

2
.nvmrc
View File

@@ -1 +1 @@
22.22.0
22.21.1

View File

@@ -1,8 +1,4 @@
exclude = [
"node_modules/**/*.toml",
"target/**/*.toml",
"packages/frontend/apps/ios/App/Packages/AffineGraphQL/**/*.toml",
]
exclude = ["node_modules/**/*.toml", "target/**/*.toml"]
# https://taplo.tamasfe.dev/configuration/formatter-options.html
[formatting]

1597
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@ members = [
"./packages/backend/native",
"./packages/common/native",
"./packages/common/y-octo/core",
"./packages/common/y-octo/node",
"./packages/common/y-octo/utils",
"./packages/frontend/mobile-native",
"./packages/frontend/native",
@@ -46,7 +47,7 @@ resolver = "3"
libc = "0.2"
log = "0.4"
loom = { version = "0.7", features = ["checkpoint"] }
memory-indexer = "0.3.0"
memory-indexer = "0.2.1"
mimalloc = "0.1"
mp4parse = "0.17"
nanoid = "0.4"
@@ -63,6 +64,7 @@ resolver = "3"
notify = { version = "8", features = ["serde"] }
objc2 = "0.6"
objc2-foundation = "0.3"
ogg = "0.9"
once_cell = "1"
ordered-float = "5"
parking_lot = "0.12"
@@ -71,7 +73,6 @@ resolver = "3"
phf = { version = "0.11", features = ["macros"] }
proptest = "1.3"
proptest-derive = "0.5"
pulldown-cmark = "0.13"
rand = "0.9"
rand_chacha = "0.9"
rand_distr = "0.5"

View File

@@ -6,8 +6,8 @@ We recommend users to always use the latest major version. Security updates will
| Version | Supported |
| --------------- | ------------------ |
| 0.26.x (stable) | :white_check_mark: |
| < 0.26.x | :x: |
| 0.25.x (stable) | :white_check_mark: |
| < 0.25.x | :x: |
## Reporting a Vulnerability

View File

@@ -296,7 +296,7 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1",
"version": "0.25.7",
"devDependencies": {
"@vanilla-extract/vite-plugin": "^5.0.0",
"msw": "^2.12.4",

View File

@@ -23,7 +23,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"file-type": "^21.0.0",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
@@ -41,5 +41,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -24,7 +24,7 @@
"@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -26,7 +26,7 @@
"@floating-ui/dom": "^1.6.10",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/mdast": "^4.0.4",
"emoji-mart": "^5.6.0",
"lit": "^3.2.0",
@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -28,7 +28,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
@@ -48,5 +48,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -24,7 +24,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -28,7 +28,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/mdast": "^4.0.4",
"date-fns": "^4.0.0",
"lit": "^3.2.0",
@@ -48,5 +48,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -21,7 +21,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
@@ -39,5 +39,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -26,7 +26,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
@@ -43,5 +43,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -26,10 +26,10 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
@@ -49,5 +49,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -26,10 +26,10 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
@@ -49,5 +49,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -10,6 +10,7 @@ const GENERIC_DEFAULT_HEIGHT_IN_NOTE = 400;
* These are based on the centralized cloud constants and known AFFiNE domains
*/
const AFFINE_DOMAINS = [
'affine.pro', // Main AFFiNE domain
'app.affine.pro', // Stable cloud domain
'insider.affine.pro', // Beta/internal cloud domain
'affine.fail', // Canary cloud domain

View File

@@ -25,7 +25,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -25,7 +25,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"file-type": "^21.0.0",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -25,7 +25,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/katex": "^0.16.7",
"@types/mdast": "^4.0.4",
"katex": "^0.16.27",
@@ -46,5 +46,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -24,7 +24,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
@@ -46,5 +46,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -27,12 +27,12 @@
"@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"@types/mdast": "^4.0.4",
"@vanilla-extract/css": "^1.17.0",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
@@ -49,5 +49,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -23,7 +23,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -44,12 +44,12 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"dompurify": "^3.3.0",
"html2canvas": "^1.4.1",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
@@ -67,5 +67,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -25,11 +25,11 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"fractional-indexing": "^3.2.0",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"nanoid": "^5.1.6",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -20,12 +20,12 @@
"@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"fractional-indexing": "^3.2.0",
"html2canvas": "^1.4.1",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"nanoid": "^5.1.6",
"pdf-lib": "^1.17.1",
"rxjs": "^7.8.2",
@@ -46,5 +46,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -21,7 +21,7 @@
"@lit/context": "^1.1.2",
"@lottiefiles/dotlottie-wc": "^0.5.0",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/hast": "^3.0.4",
"@types/katex": "^0.16.7",
"@types/lodash-es": "^4.17.12",
@@ -31,7 +31,7 @@
"katex": "^0.16.27",
"lit": "^3.2.0",
"lit-html": "^3.2.1",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"remark-math": "^6.0.0",
"rxjs": "^7.8.2",
"shiki": "^3.19.0",
@@ -82,5 +82,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -21,12 +21,12 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"clsx": "^2.1.1",
"date-fns": "^4.0.0",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
@@ -48,5 +48,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -48,41 +48,32 @@ const compareList = <T>(
return 0;
};
const compareString = (a: unknown, b: unknown): CompareType => {
const strA = String(a ?? '');
const strB = String(b ?? '');
if (strA === '' && strB !== '') {
return Compare.GT; // Empty strings come last
if (typeof a != 'string' || a === '') {
return Compare.GT;
}
if (strA !== '' && strB === '') {
return Compare.LT; // Empty strings come last
if (typeof b != 'string' || b === '') {
return Compare.LT;
}
if (strA === '' && strB === '') {
return 0; // Both empty, equal
}
const listA = strA.split('.');
const listB = strB.split('.');
const listA = a.split('.');
const listB = b.split('.');
return compareList(listA, listB, (a, b) => {
const lowA = String(a).toLowerCase(); // Ensure 'a' and 'b' from split are strings too
const lowB = String(b).toLowerCase();
const lowA = a.toLowerCase();
const lowB = b.toLowerCase();
const numberA = Number.parseInt(lowA);
const numberB = Number.parseInt(lowB);
const aIsNaN = Number.isNaN(numberA);
const bIsNaN = Number.isNaN(numberB);
if (aIsNaN && !bIsNaN) {
return 1; // Non-numeric part comes after numeric part
return 1;
}
if (!aIsNaN && bIsNaN) {
return -1; // Numeric part comes before non-numeric part
return -1;
}
if (!aIsNaN && !bIsNaN && numberA !== numberB) {
return numberA - numberB; // Numeric comparison for numeric parts
return numberA - numberB;
}
return lowA.localeCompare(lowB); // Lexicographical comparison for string parts
return lowA.localeCompare(lowB);
});
};
const compareNumber = (a: unknown, b: unknown) => {

View File

@@ -26,5 +26,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -22,10 +22,10 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -21,7 +21,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"lit": "^3.2.0",
"rxjs": "^7.8.2"
},
@@ -35,5 +35,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -24,7 +24,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
@@ -40,5 +40,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -24,10 +24,10 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -24,7 +24,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@vanilla-extract/css": "^1.17.0",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
@@ -41,5 +41,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -23,10 +23,10 @@
"@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
@@ -43,5 +43,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -24,10 +24,10 @@
"@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -24,10 +24,10 @@
"@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -26,10 +26,10 @@
"@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -30,10 +30,10 @@
"@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"simple-xml-to-json": "^1.2.2",
@@ -51,5 +51,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -26,10 +26,10 @@
"@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -23,10 +23,10 @@
"@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -24,10 +24,10 @@
"@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -25,10 +25,10 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -23,10 +23,10 @@
"@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
@@ -43,5 +43,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -25,5 +25,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -19,11 +19,11 @@
"@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.15",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lit-html": "^3.2.1",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

View File

@@ -22,13 +22,13 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"collapse-white-space": "^2.1.0",
"date-fns": "^4.0.0",
"lit": "^3.2.0",
"lit-html": "^3.2.1",
"lodash-es": "^4.17.23",
"lodash-es": "^4.17.21",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
@@ -47,5 +47,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.1"
"version": "0.25.7"
}

Some files were not shown because too many files have changed in this diff Show More